Compare commits
	
		
			126 Commits
		
	
	
		
			pull-input
			...
			SLE12-SP1-
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 3456706297 | ||
|  | 6bfc70462b | ||
|  | 77bd1c30f4 | ||
|  | 0d9bbf181e | ||
|  | c7f5d47e08 | ||
|  | aa7fb34246 | ||
|  | 4c3849714f | ||
|  | 8970c6a04e | ||
|  | 0bcc356da2 | ||
|  | 5cc51586d1 | ||
|  | 7637d9fbfe | ||
|  | 589b223ca5 | ||
|  | 225bfdb2ed | ||
|  | fc1b871631 | ||
|  | a78f57a82b | ||
|  | 457b3ba055 | ||
|  | bff3afb57d | ||
|  | eb70eb33cb | ||
|  | 8526e2d444 | ||
|  | 99bc193099 | ||
|  | 5e454fe423 | ||
|  | 41eb27692f | ||
|  | c251fa606e | ||
|  | c923a7775c | ||
|  | 09d66eb5f5 | ||
|  | b313f6d23d | ||
|  | 319e0e347f | ||
|  | daede1f0f2 | ||
|  | 789adc278e | ||
|  | c4c86364b3 | ||
|  | b31a018f58 | ||
|  | 588bf41e1f | ||
|  | babe800bcc | ||
|  | 17e0a09e25 | ||
|  | ed87ee47e0 | ||
|  | 621ddf509e | ||
|  | ac10d55b82 | ||
|  | 8e9b8b2204 | ||
|  | 2ac80007da | ||
|  | 4168180338 | ||
|  | 683051c844 | ||
|  | 4a7c93f23e | ||
|  | 66e090df95 | ||
|  | acc6309904 | ||
|  | 9bb28247d7 | ||
|  | 1a8b5a066a | ||
|  | 5673fb20cd | ||
|  | f8898ec278 | ||
|  | efa9daceb1 | ||
|  | ed19da1195 | ||
|  | b8ddc9be4c | ||
|  | 0228bc7ae0 | ||
|  | a5a901fc4c | ||
|  | ca05fad76e | ||
|  | 6855fbd119 | ||
|  | 02792b8b1d | ||
|  | f72dd33b9a | ||
|  | cc2fb7f00d | ||
|  | bd2ce7536f | ||
|  | f2b44e9116 | ||
|  | b5af05aeb7 | ||
|  | dfa83a6bae | ||
|  | 35a616edef | ||
|  | 35c30d3efd | ||
|  | f4c861fd68 | ||
|  | b7a197c39e | ||
|  | 85611098ff | ||
|  | ce4f451bbb | ||
|  | 6722c126f3 | ||
|  | 8dd45dcd83 | ||
|  | e750591c8a | ||
|  | f9c0ae2723 | ||
|  | c8bd74d1d5 | ||
|  | d1557697fd | ||
|  | 86d6fe4cb0 | ||
|  | 9634e45e0b | ||
|  | 0dc545e977 | ||
|  | 358f0ee234 | ||
|  | 961c74a841 | ||
|  | 98fe91ed66 | ||
|  | 46addaa0b5 | ||
|  | 5a4568717c | ||
|  | 87740cecc3 | ||
|  | 8df2a9acd2 | ||
|  | c5c71e87aa | ||
|  | 2060efae47 | ||
|  | 8d64975c98 | ||
|  | 9b4420ad62 | ||
|  | 99c3468d8f | ||
|  | 1c17e8c7d3 | ||
|  | ffd060d51f | ||
|  | e4fb4bea37 | ||
|  | edc0a65326 | ||
|  | c62f6c8f67 | ||
|  | 3d8b7aed60 | ||
|  | 27ed14c4d7 | ||
|  | 6a45a1b8e4 | ||
|  | 6cacd2651a | ||
|  | e8248a5af1 | ||
|  | 81cb0a5657 | ||
|  | 6130c46232 | ||
|  | 49ef542e41 | ||
|  | c270245a53 | ||
|  | 9272707a1f | ||
|  | c759f1a078 | ||
|  | 714b54401c | ||
|  | e7e08380c3 | ||
|  | c631ee6520 | ||
|  | b153c8d3f3 | ||
|  | f45048225a | ||
|  | ae0fa48f51 | ||
|  | bb3a1da4d4 | ||
|  | b48a391cff | ||
|  | cc883fe42d | ||
|  | 4072585ecf | ||
|  | 959fad0ff1 | ||
|  | a4bb522ee5 | ||
|  | cf6c213981 | ||
|  | cf3297868c | ||
|  | ad9c167fd2 | ||
|  | d8e231fce2 | ||
|  | 53cd79c117 | ||
|  | 3dd15f3e58 | ||
|  | 4c59860506 | ||
|  | b575af0730 | ||
|  | d3b59789e8 | 
| @@ -1,2 +0,0 @@ | ||||
| ((c-mode . ((c-file-style . "stroustrup") | ||||
| 	    (indent-tabs-mode . nil)))) | ||||
							
								
								
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -17,15 +17,12 @@ | ||||
| /trace/generated-tcg-tracers.h | ||||
| /trace/generated-ust-provider.h | ||||
| /trace/generated-ust.c | ||||
| /ui/shader/texture-blit-frag.h | ||||
| /ui/shader/texture-blit-vert.h | ||||
| /libcacard/trace/generated-tracers.c | ||||
| *-timestamp | ||||
| /*-softmmu | ||||
| /*-darwin-user | ||||
| /*-linux-user | ||||
| /*-bsd-user | ||||
| /ivshmem-client | ||||
| /ivshmem-server | ||||
| /libdis* | ||||
| /libuser | ||||
| /linux-headers/asm | ||||
| @@ -35,7 +32,6 @@ | ||||
| /qapi-visit.[ch] | ||||
| /qapi-event.[ch] | ||||
| /qmp-commands.h | ||||
| /qmp-introspect.[ch] | ||||
| /qmp-marshal.c | ||||
| /qemu-doc.html | ||||
| /qemu-tech.html | ||||
| @@ -51,7 +47,6 @@ | ||||
| /qemu-ga | ||||
| /qemu-bridge-helper | ||||
| /qemu-monitor.texi | ||||
| /qemu-monitor-info.texi | ||||
| /qmp-commands.txt | ||||
| /vscclient | ||||
| /fsdev/virtfs-proxy-helper | ||||
| @@ -61,7 +56,6 @@ | ||||
| *.cp | ||||
| *.dvi | ||||
| *.exe | ||||
| *.msi | ||||
| *.dll | ||||
| *.so | ||||
| *.mo | ||||
|   | ||||
							
								
								
									
										133
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										133
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,38 +1,9 @@ | ||||
| sudo: false | ||||
| language: c | ||||
| python: | ||||
|   - "2.4" | ||||
| compiler: | ||||
|   - gcc | ||||
|   - clang | ||||
| cache: ccache | ||||
| addons: | ||||
|   apt: | ||||
|     packages: | ||||
|       - libaio-dev | ||||
|       - libattr1-dev | ||||
|       - libbrlapi-dev | ||||
|       - libcap-ng-dev | ||||
|       - libgnutls-dev | ||||
|       - libgtk-3-dev | ||||
|       - libiscsi-dev | ||||
|       - liblttng-ust-dev | ||||
|       - libncurses5-dev | ||||
|       - libnss3-dev | ||||
|       - libpixman-1-dev | ||||
|       - libpng12-dev | ||||
|       - librados-dev | ||||
|       - libsdl1.2-dev | ||||
|       - libseccomp-dev | ||||
|       - libspice-protocol-dev | ||||
|       - libspice-server-dev | ||||
|       - libssh2-1-dev | ||||
|       - liburcu-dev | ||||
|       - libusb-1.0-0-dev | ||||
|       - libvte-2.90-dev | ||||
|       - sparse | ||||
|       - uuid-dev | ||||
|  | ||||
| notifications: | ||||
|   irc: | ||||
|     channels: | ||||
| @@ -41,16 +12,29 @@ notifications: | ||||
|     on_failure: always | ||||
| env: | ||||
|   global: | ||||
|     - TEST_CMD="make check" | ||||
|     - TEST_CMD="" | ||||
|     - EXTRA_CONFIG="" | ||||
|     # Development packages, EXTRA_PKGS saved for additional builds | ||||
|     - CORE_PKGS="libusb-1.0-0-dev libiscsi-dev librados-dev libncurses5-dev" | ||||
|     - NET_PKGS="libseccomp-dev libgnutls-dev libssh2-1-dev  libspice-server-dev libspice-protocol-dev libnss3-dev" | ||||
|     - GUI_PKGS="libgtk-3-dev libvte-2.90-dev libsdl1.2-dev libpng12-dev libpixman-1-dev" | ||||
|     - EXTRA_PKGS="" | ||||
|   matrix: | ||||
|     # Group major targets together with their linux-user counterparts | ||||
|     - TARGETS=alpha-softmmu,alpha-linux-user,cris-softmmu,cris-linux-user,m68k-softmmu,m68k-linux-user,microblaze-softmmu,microblazeel-softmmu,microblaze-linux-user,microblazeel-linux-user | ||||
|     - TARGETS=alpha-softmmu,alpha-linux-user | ||||
|     - TARGETS=arm-softmmu,arm-linux-user,armeb-linux-user,aarch64-softmmu,aarch64-linux-user | ||||
|     - TARGETS=cris-softmmu,cris-linux-user | ||||
|     - TARGETS=i386-softmmu,i386-linux-user,x86_64-softmmu,x86_64-linux-user | ||||
|     - TARGETS=mips-softmmu,mips64-softmmu,mips64el-softmmu,mipsel-softmmu,mips-linux-user,mips64-linux-user,mips64el-linux-user,mipsel-linux-user,mipsn32-linux-user,mipsn32el-linux-user | ||||
|     - TARGETS=or32-softmmu,or32-linux-user,ppc-softmmu,ppc64-softmmu,ppcemb-softmmu,ppc-linux-user,ppc64-linux-user,ppc64abi32-linux-user,ppc64le-linux-user | ||||
|     - TARGETS=s390x-softmmu,s390x-linux-user,sh4-softmmu,sh4eb-softmmu,sh4-linux-user,sh4eb-linux-user,sparc-softmmu,sparc64-softmmu,sparc-linux-user,sparc32plus-linux-user,sparc64-linux-user,unicore32-softmmu,unicore32-linux-user | ||||
|     - TARGETS=m68k-softmmu,m68k-linux-user | ||||
|     - TARGETS=microblaze-softmmu,microblazeel-softmmu,microblaze-linux-user,microblazeel-linux-user | ||||
|     - TARGETS=mips-softmmu,mips64-softmmu,mips64el-softmmu,mipsel-softmmu | ||||
|     - TARGETS=mips-linux-user,mips64-linux-user,mips64el-linux-user,mipsel-linux-user,mipsn32-linux-user,mipsn32el-linux-user | ||||
|     - TARGETS=or32-softmmu,or32-linux-user | ||||
|     - TARGETS=ppc-softmmu,ppc64-softmmu,ppcemb-softmmu,ppc-linux-user,ppc64-linux-user,ppc64abi32-linux-user,ppc64le-linux-user | ||||
|     - TARGETS=s390x-softmmu,s390x-linux-user | ||||
|     - TARGETS=sh4-softmmu,sh4eb-softmmu,sh4-linux-user sh4eb-linux-user | ||||
|     - TARGETS=sparc-softmmu,sparc64-softmmu,sparc-linux-user,sparc32plus-linux-user,sparc64-linux-user | ||||
|     - TARGETS=unicore32-softmmu,unicore32-linux-user | ||||
|     # Group remaining softmmu only targets into one build | ||||
|     - TARGETS=lm32-softmmu,moxie-softmmu,tricore-softmmu,xtensa-softmmu,xtensaeb-softmmu | ||||
| git: | ||||
| @@ -59,6 +43,8 @@ git: | ||||
| before_install: | ||||
|   - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||
|   - git submodule update --init --recursive | ||||
|   - sudo apt-get update -qq | ||||
|   - sudo apt-get install -qq ${CORE_PKGS} ${NET_PKGS} ${GUI_PKGS} ${EXTRA_PKGS} | ||||
| before_script: | ||||
|   - ./configure --target-list=${TARGETS} --enable-debug-tcg ${EXTRA_CONFIG} | ||||
| script: | ||||
| @@ -66,59 +52,52 @@ script: | ||||
| matrix: | ||||
|   # We manually include a number of additional build for non-standard bits | ||||
|   include: | ||||
|     # Make check target (we only do this once) | ||||
|     - env: | ||||
|         - TARGETS=alpha-softmmu,arm-softmmu,aarch64-softmmu,cris-softmmu, | ||||
|                   i386-softmmu,x86_64-softmmu,m68k-softmmu,microblaze-softmmu, | ||||
|                   microblazeel-softmmu,mips-softmmu,mips64-softmmu, | ||||
|                   mips64el-softmmu,mipsel-softmmu,or32-softmmu,ppc-softmmu, | ||||
|                   ppc64-softmmu,ppcemb-softmmu,s390x-softmmu,sh4-softmmu, | ||||
|                   sh4eb-softmmu,sparc-softmmu,sparc64-softmmu, | ||||
|                   unicore32-softmmu,unicore32-linux-user, | ||||
|                   lm32-softmmu,moxie-softmmu,tricore-softmmu,xtensa-softmmu, | ||||
|                   xtensaeb-softmmu | ||||
|           TEST_CMD="make check" | ||||
|       compiler: gcc | ||||
|     # Debug related options | ||||
|     - env: TARGETS=x86_64-softmmu | ||||
|     - env: TARGETS=i386-softmmu,x86_64-softmmu | ||||
|            EXTRA_CONFIG="--enable-debug" | ||||
|       compiler: gcc | ||||
|     # We currently disable "make check" | ||||
|     - env: TARGETS=alpha-softmmu | ||||
|     - env: TARGETS=i386-softmmu,x86_64-softmmu | ||||
|            EXTRA_CONFIG="--enable-debug --enable-tcg-interpreter" | ||||
|            TEST_CMD="" | ||||
|       compiler: gcc | ||||
|     # Disable a few of the optional features | ||||
|     - env: TARGETS=x86_64-softmmu | ||||
|            EXTRA_CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb" | ||||
|     # All the extra -dev packages | ||||
|     - env: TARGETS=i386-softmmu,x86_64-softmmu | ||||
|            EXTRA_PKGS="libaio-dev libcap-ng-dev libattr1-dev libbrlapi-dev uuid-dev libusb-1.0.0-dev" | ||||
|       compiler: gcc | ||||
|     # Currently configure doesn't force --disable-pie | ||||
|     - env: TARGETS=x86_64-softmmu | ||||
|     - env: TARGETS=i386-softmmu,x86_64-softmmu | ||||
|            EXTRA_CONFIG="--enable-gprof --enable-gcov --disable-pie" | ||||
|       compiler: gcc | ||||
|     # Sparse | ||||
|     - env: TARGETS=x86_64-softmmu | ||||
|     - env: TARGETS=i386-softmmu,x86_64-softmmu | ||||
|            EXTRA_PKGS="sparse" | ||||
|            EXTRA_CONFIG="--enable-sparse" | ||||
|       compiler: gcc | ||||
|     # Modules | ||||
|     - env: TARGETS=arm-softmmu,x86_64-softmmu | ||||
|     # All the trace backends (apart from dtrace) | ||||
|     - env: TARGETS=i386-softmmu,x86_64-softmmu | ||||
|            EXTRA_CONFIG="--enable-trace-backends=stderr" | ||||
|       compiler: gcc | ||||
|     - env: TARGETS=i386-softmmu,x86_64-softmmu | ||||
|            EXTRA_CONFIG="--enable-trace-backends=simple" | ||||
|       compiler: gcc | ||||
|     - env: TARGETS=i386-softmmu,x86_64-softmmu | ||||
|            EXTRA_CONFIG="--enable-trace-backends=ftrace" | ||||
|       compiler: gcc | ||||
|     - env: TARGETS=i386-softmmu,x86_64-softmmu | ||||
|           EXTRA_PKGS="liblttng-ust-dev liburcu-dev" | ||||
|           EXTRA_CONFIG="--enable-trace-backends=ust" | ||||
|       compiler: gcc | ||||
|     - env: TARGETS=i386-softmmu,x86_64-softmmu | ||||
|            EXTRA_CONFIG="--enable-modules" | ||||
|       compiler: gcc | ||||
|     # All the trace backends (apart from dtrace) | ||||
|     - env: TARGETS=i386-softmmu | ||||
|            EXTRA_CONFIG="--enable-trace-backends=log" | ||||
|       compiler: gcc | ||||
|     # We currently disable "make check" (until 41fc57e44ed regression fixed) | ||||
|     - env: TARGETS=x86_64-softmmu | ||||
|            EXTRA_CONFIG="--enable-trace-backends=simple" | ||||
|            TEST_CMD="" | ||||
|       compiler: gcc | ||||
|     # We currently disable "make check" | ||||
|     - env: TARGETS=x86_64-softmmu | ||||
|            EXTRA_CONFIG="--enable-trace-backends=ftrace" | ||||
|            TEST_CMD="" | ||||
|       compiler: gcc | ||||
|     # We currently disable "make check" | ||||
|     - env: TARGETS=x86_64-softmmu | ||||
|            EXTRA_CONFIG="--enable-trace-backends=ust" | ||||
|            TEST_CMD="" | ||||
|       compiler: gcc | ||||
|     # All the co-routine backends (apart from windows) | ||||
|     # We currently disable "make check" | ||||
|     - env: TARGETS=x86_64-softmmu | ||||
|            EXTRA_CONFIG="--with-coroutine=gthread" | ||||
|            TEST_CMD="" | ||||
|       compiler: gcc | ||||
|     - env: TARGETS=x86_64-softmmu | ||||
|            EXTRA_CONFIG="--with-coroutine=ucontext" | ||||
|       compiler: gcc | ||||
|     - env: TARGETS=x86_64-softmmu | ||||
|            EXTRA_CONFIG="--with-coroutine=sigaltstack" | ||||
|       compiler: gcc | ||||
|   | ||||
							
								
								
									
										13
									
								
								CODING_STYLE
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								CODING_STYLE
									
									
									
									
									
								
							| @@ -87,15 +87,10 @@ Furthermore, it is the QEMU coding style. | ||||
|  | ||||
| 5. Declarations | ||||
|  | ||||
| Mixed declarations (interleaving statements and declarations within | ||||
| blocks) are generally not allowed; declarations should be at the beginning | ||||
| of blocks. | ||||
|  | ||||
| Every now and then, an exception is made for declarations inside a | ||||
| #ifdef or #ifndef block: if the code looks nicer, such declarations can | ||||
| be placed at the top of the block even if there are statements above. | ||||
| On the other hand, however, it's often best to move that #ifdef/#ifndef | ||||
| block to a separate function altogether. | ||||
| Mixed declarations (interleaving statements and declarations within blocks) | ||||
| are not allowed; declarations should be at the beginning of blocks.  In other | ||||
| words, the code should not generate warnings if using GCC's | ||||
| -Wdeclaration-after-statement option. | ||||
|  | ||||
| 6. Conditional statements | ||||
|  | ||||
|   | ||||
							
								
								
									
										55
									
								
								HACKING
									
									
									
									
									
								
							
							
						
						
									
										55
									
								
								HACKING
									
									
									
									
									
								
							| @@ -157,58 +157,3 @@ painful. These are: | ||||
|  * you may assume that integers are 2s complement representation | ||||
|  * you may assume that right shift of a signed integer duplicates | ||||
|    the sign bit (ie it is an arithmetic shift, not a logical shift) | ||||
|  | ||||
| 7. Error handling and reporting | ||||
|  | ||||
| 7.1 Reporting errors to the human user | ||||
|  | ||||
| Do not use printf(), fprintf() or monitor_printf().  Instead, use | ||||
| error_report() or error_vreport() from error-report.h.  This ensures the | ||||
| error is reported in the right place (current monitor or stderr), and in | ||||
| a uniform format. | ||||
|  | ||||
| Use error_printf() & friends to print additional information. | ||||
|  | ||||
| error_report() prints the current location.  In certain common cases | ||||
| like command line parsing, the current location is tracked | ||||
| automatically.  To manipulate it manually, use the loc_*() from | ||||
| error-report.h. | ||||
|  | ||||
| 7.2 Propagating errors | ||||
|  | ||||
| An error can't always be reported to the user right where it's detected, | ||||
| but often needs to be propagated up the call chain to a place that can | ||||
| handle it.  This can be done in various ways. | ||||
|  | ||||
| The most flexible one is Error objects.  See error.h for usage | ||||
| information. | ||||
|  | ||||
| Use the simplest suitable method to communicate success / failure to | ||||
| callers.  Stick to common methods: non-negative on success / -1 on | ||||
| error, non-negative / -errno, non-null / null, or Error objects. | ||||
|  | ||||
| Example: when a function returns a non-null pointer on success, and it | ||||
| can fail only in one way (as far as the caller is concerned), returning | ||||
| null on failure is just fine, and certainly simpler and a lot easier on | ||||
| the eyes than propagating an Error object through an Error ** parameter. | ||||
|  | ||||
| Example: when a function's callers need to report details on failure | ||||
| only the function really knows, use Error **, and set suitable errors. | ||||
|  | ||||
| Do not report an error to the user when you're also returning an error | ||||
| for somebody else to handle.  Leave the reporting to the place that | ||||
| consumes the error returned. | ||||
|  | ||||
| 7.3 Handling errors | ||||
|  | ||||
| Calling exit() is fine when handling configuration errors during | ||||
| startup.  It's problematic during normal operation.  In particular, | ||||
| monitor commands should never exit(). | ||||
|  | ||||
| Do not call exit() or abort() to handle an error that can be triggered | ||||
| by the guest (e.g., some unimplemented corner case in guest code | ||||
| translation or device emulation).  Guests should not be able to | ||||
| terminate QEMU. | ||||
|  | ||||
| Note that &error_fatal is just another way to exit(1), and &error_abort | ||||
| is just another way to abort(). | ||||
|   | ||||
							
								
								
									
										557
									
								
								MAINTAINERS
									
									
									
									
									
								
							
							
						
						
									
										557
									
								
								MAINTAINERS
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										150
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										150
									
								
								Makefile
									
									
									
									
									
								
							| @@ -3,11 +3,6 @@ | ||||
| # Always point to the root of the build tree (needs GNU make). | ||||
| BUILD_DIR=$(CURDIR) | ||||
|  | ||||
| # Before including a proper config-host.mak, assume we are in the source tree | ||||
| SRC_PATH=. | ||||
|  | ||||
| UNCHECKED_GOALS := %clean TAGS cscope ctags | ||||
|  | ||||
| # All following code might depend on configuration variables | ||||
| ifneq ($(wildcard config-host.mak),) | ||||
| # Put the all: rule here so that config-host.mak can contain dependencies. | ||||
| @@ -43,7 +38,7 @@ config-host.mak: $(SRC_PATH)/configure | ||||
| 	fi | ||||
| else | ||||
| config-host.mak: | ||||
| ifneq ($(filter-out $(UNCHECKED_GOALS),$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail)) | ||||
| ifneq ($(filter-out %clean,$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail)) | ||||
| 	@echo "Please call configure before running make!" | ||||
| 	@exit 1 | ||||
| endif | ||||
| @@ -52,8 +47,6 @@ endif | ||||
| GENERATED_HEADERS = config-host.h qemu-options.def | ||||
| GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h qapi-event.h | ||||
| GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c qapi-event.c | ||||
| GENERATED_HEADERS += qmp-introspect.h | ||||
| GENERATED_SOURCES += qmp-introspect.c | ||||
|  | ||||
| GENERATED_HEADERS += trace/generated-events.h | ||||
| GENERATED_SOURCES += trace/generated-events.c | ||||
| @@ -81,7 +74,7 @@ Makefile: ; | ||||
| configure: ; | ||||
|  | ||||
| .PHONY: all clean cscope distclean dvi html info install install-doc \ | ||||
| 	pdf recurse-all speed test dist msi | ||||
| 	pdf recurse-all speed test dist | ||||
|  | ||||
| $(call set-vpath, $(SRC_PATH)) | ||||
|  | ||||
| @@ -90,8 +83,7 @@ LIBS+=-lz $(LIBS_TOOLS) | ||||
| HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF) | ||||
|  | ||||
| ifdef BUILD_DOCS | ||||
| DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8 | ||||
| DOCS+=qmp-commands.txt | ||||
| DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 qmp-commands.txt | ||||
| ifdef CONFIG_LINUX | ||||
| DOCS+=kvm_stat.1 | ||||
| endif | ||||
| @@ -151,21 +143,18 @@ dummy := $(call unnest-vars,, \ | ||||
|                 stub-obj-y \ | ||||
|                 util-obj-y \ | ||||
|                 qga-obj-y \ | ||||
|                 ivshmem-client-obj-y \ | ||||
|                 ivshmem-server-obj-y \ | ||||
|                 qga-vss-dll-obj-y \ | ||||
|                 block-obj-y \ | ||||
|                 block-obj-m \ | ||||
|                 crypto-obj-y \ | ||||
|                 crypto-aes-obj-y \ | ||||
|                 qom-obj-y \ | ||||
|                 io-obj-y \ | ||||
|                 common-obj-y \ | ||||
|                 common-obj-m) | ||||
|  | ||||
| ifneq ($(wildcard config-host.mak),) | ||||
| include $(SRC_PATH)/tests/Makefile | ||||
| endif | ||||
| ifeq ($(CONFIG_SMARTCARD_NSS),y) | ||||
| include $(SRC_PATH)/libcacard/Makefile | ||||
| endif | ||||
|  | ||||
| all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules | ||||
|  | ||||
| @@ -178,8 +167,6 @@ SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS)) | ||||
| SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES)) | ||||
|  | ||||
| $(SOFTMMU_SUBDIR_RULES): $(block-obj-y) | ||||
| $(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y) | ||||
| $(SOFTMMU_SUBDIR_RULES): $(io-obj-y) | ||||
| $(SOFTMMU_SUBDIR_RULES): config-all-devices.mak | ||||
|  | ||||
| subdir-%: | ||||
| @@ -204,7 +191,7 @@ subdir-dtc:dtc/libfdt dtc/tests | ||||
| dtc/%: | ||||
| 	mkdir -p $@ | ||||
|  | ||||
| $(SUBDIR_RULES): libqemuutil.a libqemustub.a $(common-obj-y) $(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY)) | ||||
| $(SUBDIR_RULES): libqemuutil.a libqemustub.a $(common-obj-y) | ||||
|  | ||||
| ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS)) | ||||
| romsubdir-%: | ||||
| @@ -234,13 +221,13 @@ util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)' | ||||
|  | ||||
| qemu-img.o: qemu-img-cmds.h | ||||
|  | ||||
| qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a | ||||
| qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a | ||||
| qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a | ||||
| qemu-img$(EXESUF): qemu-img.o $(block-obj-y) libqemuutil.a libqemustub.a | ||||
| qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) libqemuutil.a libqemustub.a | ||||
| qemu-io$(EXESUF): qemu-io.o $(block-obj-y) libqemuutil.a libqemustub.a | ||||
|  | ||||
| qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o | ||||
|  | ||||
| fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o libqemuutil.a libqemustub.a | ||||
| fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/virtio-9p-marshal.o libqemuutil.a libqemustub.a | ||||
| fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap | ||||
|  | ||||
| qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx | ||||
| @@ -256,49 +243,42 @@ qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py | ||||
| qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h :\ | ||||
| $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py) | ||||
| 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py \ | ||||
| 		$(gen-out-type) -o qga/qapi-generated -p "qga-" $<, \ | ||||
| 		$(gen-out-type) -o qga/qapi-generated -p "qga-" -i $<, \ | ||||
| 		"  GEN   $@") | ||||
| qga/qapi-generated/qga-qapi-visit.c qga/qapi-generated/qga-qapi-visit.h :\ | ||||
| $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py) | ||||
| 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py \ | ||||
| 		$(gen-out-type) -o qga/qapi-generated -p "qga-" $<, \ | ||||
| 		$(gen-out-type) -o qga/qapi-generated -p "qga-" -i $<, \ | ||||
| 		"  GEN   $@") | ||||
| qga/qapi-generated/qga-qmp-commands.h qga/qapi-generated/qga-qmp-marshal.c :\ | ||||
| $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py) | ||||
| 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \ | ||||
| 		$(gen-out-type) -o qga/qapi-generated -p "qga-" $<, \ | ||||
| 		$(gen-out-type) -o qga/qapi-generated -p "qga-" -i $<, \ | ||||
| 		"  GEN   $@") | ||||
|  | ||||
| qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \ | ||||
|                $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \ | ||||
|                $(SRC_PATH)/qapi/event.json $(SRC_PATH)/qapi/introspect.json \ | ||||
|                $(SRC_PATH)/qapi/crypto.json $(SRC_PATH)/qapi/rocker.json \ | ||||
|                $(SRC_PATH)/qapi/trace.json | ||||
|                $(SRC_PATH)/qapi/event.json | ||||
|  | ||||
| qapi-types.c qapi-types.h :\ | ||||
| $(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py) | ||||
| 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py \ | ||||
| 		$(gen-out-type) -o "." -b $<, \ | ||||
| 		$(gen-out-type) -o "." -b -i $<, \ | ||||
| 		"  GEN   $@") | ||||
| qapi-visit.c qapi-visit.h :\ | ||||
| $(qapi-modules) $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py) | ||||
| 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py \ | ||||
| 		$(gen-out-type) -o "." -b $<, \ | ||||
| 		$(gen-out-type) -o "." -b -i $<, \ | ||||
| 		"  GEN   $@") | ||||
| qapi-event.c qapi-event.h :\ | ||||
| $(qapi-modules) $(SRC_PATH)/scripts/qapi-event.py $(qapi-py) | ||||
| 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py \ | ||||
| 		$(gen-out-type) -o "." $<, \ | ||||
| 		$(gen-out-type) -o "." -b -i $<, \ | ||||
| 		"  GEN   $@") | ||||
| qmp-commands.h qmp-marshal.c :\ | ||||
| $(qapi-modules) $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py) | ||||
| 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \ | ||||
| 		$(gen-out-type) -o "." -m $<, \ | ||||
| 		"  GEN   $@") | ||||
| qmp-introspect.h qmp-introspect.c :\ | ||||
| $(qapi-modules) $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py) | ||||
| 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-introspect.py \ | ||||
| 		$(gen-out-type) -o "." $<, \ | ||||
| 		$(gen-out-type) -o "." -m -i $<, \ | ||||
| 		"  GEN   $@") | ||||
|  | ||||
| QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h) | ||||
| @@ -307,44 +287,15 @@ $(qga-obj-y) qemu-ga.o: $(QGALIB_GEN) | ||||
| qemu-ga$(EXESUF): $(qga-obj-y) libqemuutil.a libqemustub.a | ||||
| 	$(call LINK, $^) | ||||
|  | ||||
| ifdef QEMU_GA_MSI_ENABLED | ||||
| QEMU_GA_MSI=qemu-ga-$(ARCH).msi | ||||
|  | ||||
| msi: $(QEMU_GA_MSI) | ||||
|  | ||||
| $(QEMU_GA_MSI): qemu-ga.exe $(QGA_VSS_PROVIDER) | ||||
|  | ||||
| $(QEMU_GA_MSI): config-host.mak | ||||
|  | ||||
| $(QEMU_GA_MSI):  $(SRC_PATH)/qga/installer/qemu-ga.wxs | ||||
| 	$(call quiet-command,QEMU_GA_VERSION="$(QEMU_GA_VERSION)" QEMU_GA_MANUFACTURER="$(QEMU_GA_MANUFACTURER)" QEMU_GA_DISTRO="$(QEMU_GA_DISTRO)" BUILD_DIR="$(BUILD_DIR)" \ | ||||
| 	wixl -o $@ $(QEMU_GA_MSI_ARCH) $(QEMU_GA_MSI_WITH_VSS) $(QEMU_GA_MSI_MINGW_DLL_PATH) $<, "  WIXL  $@") | ||||
| else | ||||
| msi: | ||||
| 	@echo "MSI build not configured or dependency resolution failed (reconfigure with --enable-guest-agent-msi option)" | ||||
| endif | ||||
|  | ||||
| ifneq ($(EXESUF),) | ||||
| .PHONY: qemu-ga | ||||
| qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI) | ||||
| endif | ||||
|  | ||||
| ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) | ||||
| 	$(call LINK, $^) | ||||
| ivshmem-server$(EXESUF): $(ivshmem-server-obj-y) libqemuutil.a libqemustub.a | ||||
| 	$(call LINK, $^) | ||||
|  | ||||
| clean: | ||||
| # avoid old build problems by removing potentially incorrect old files | ||||
| 	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h | ||||
| 	rm -f qemu-options.def | ||||
| 	rm -f *.msi | ||||
| 	find . \( -name '*.l[oa]' -o -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f -exec rm {} + | ||||
| 	rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~ | ||||
| 	rm -f fsdev/*.pod | ||||
| 	rm -rf .libs */.libs | ||||
| 	rm -f qemu-img-cmds.h | ||||
| 	rm -f ui/shader/*-vert.h ui/shader/*-frag.h | ||||
| 	@# May not be present in GENERATED_HEADERS | ||||
| 	rm -f trace/generated-tracers-dtrace.dtrace* | ||||
| 	rm -f trace/generated-tracers-dtrace.h* | ||||
| @@ -365,7 +316,7 @@ qemu-%.tar.bz2: | ||||
| 	$(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)" | ||||
|  | ||||
| distclean: clean | ||||
| 	rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi | ||||
| 	rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi | ||||
| 	rm -f config-all-devices.mak config-all-disas.mak config.status | ||||
| 	rm -f po/*.mo tests/qemu-iotests/common.env | ||||
| 	rm -f roms/seabios/config.mak roms/vgabios/config.mak | ||||
| @@ -390,8 +341,8 @@ bepo    cz | ||||
|  | ||||
| ifdef INSTALL_BLOBS | ||||
| BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \ | ||||
| vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \ | ||||
| acpi-dsdt.aml \ | ||||
| vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin \ | ||||
| acpi-dsdt.aml q35-acpi-dsdt.aml \ | ||||
| ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \ | ||||
| pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \ | ||||
| pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \ | ||||
| @@ -400,6 +351,7 @@ efi-pcnet.rom efi-rtl8139.rom efi-virtio.rom \ | ||||
| qemu-icon.bmp qemu_logo_no_text.svg \ | ||||
| bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ | ||||
| multiboot.bin linuxboot.bin kvmvapic.bin \ | ||||
| s390-zipl.rom \ | ||||
| s390-ccw.img \ | ||||
| spapr-rtas.bin slof.bin \ | ||||
| palcode-clipper \ | ||||
| @@ -420,9 +372,6 @@ ifneq ($(TOOLS),) | ||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8" | ||||
| 	$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8" | ||||
| endif | ||||
| ifneq (,$(findstring qemu-ga,$(TOOLS))) | ||||
| 	$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8" | ||||
| endif | ||||
| endif | ||||
| ifdef CONFIG_VIRTFS | ||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1" | ||||
| @@ -439,11 +388,16 @@ ifneq (,$(findstring qemu-ga,$(TOOLS))) | ||||
| endif | ||||
| endif | ||||
|  | ||||
| install-confdir: | ||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_confdir)" | ||||
|  | ||||
| install: all $(if $(BUILD_DOCS),install-doc) \ | ||||
| install-sysconfig: install-datadir install-confdir | ||||
| 	$(INSTALL_DATA) $(SRC_PATH)/sysconfigs/target/target-x86_64.conf "$(DESTDIR)$(qemu_confdir)" | ||||
|  | ||||
| install: all $(if $(BUILD_DOCS),install-doc) install-sysconfig \ | ||||
| install-datadir install-localstatedir | ||||
| ifneq ($(TOOLS),) | ||||
| 	$(call install-prog,$(subst qemu-ga,qemu-ga$(EXESUF),$(TOOLS)),$(DESTDIR)$(bindir)) | ||||
| 	$(call install-prog,$(TOOLS),$(DESTDIR)$(bindir)) | ||||
| endif | ||||
| ifneq ($(CONFIG_MODULES),) | ||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_moddir)" | ||||
| @@ -477,36 +431,15 @@ endif | ||||
| test speed: all | ||||
| 	$(MAKE) -C tests/tcg $@ | ||||
|  | ||||
| .PHONY: ctags | ||||
| ctags: | ||||
| 	rm -f $@ | ||||
| 	find "$(SRC_PATH)" -name '*.[hc]' -exec ctags --append {} + | ||||
|  | ||||
| .PHONY: TAGS | ||||
| TAGS: | ||||
| 	rm -f $@ | ||||
| 	find "$(SRC_PATH)" -name '*.[hc]' -exec etags --append {} + | ||||
|  | ||||
| cscope: | ||||
| 	rm -f "$(SRC_PATH)"/cscope.* | ||||
| 	find "$(SRC_PATH)/" -name "*.[chsS]" -print | sed 's,^\./,,' > "$(SRC_PATH)/cscope.files" | ||||
| 	cscope -b -i"$(SRC_PATH)/cscope.files" | ||||
|  | ||||
| # opengl shader programs | ||||
| ui/shader/%-vert.h: $(SRC_PATH)/ui/shader/%.vert $(SRC_PATH)/scripts/shaderinclude.pl | ||||
| 	@mkdir -p $(dir $@) | ||||
| 	$(call quiet-command,\ | ||||
| 		perl $(SRC_PATH)/scripts/shaderinclude.pl $< > $@,\ | ||||
| 		"  VERT  $@") | ||||
|  | ||||
| ui/shader/%-frag.h: $(SRC_PATH)/ui/shader/%.frag $(SRC_PATH)/scripts/shaderinclude.pl | ||||
| 	@mkdir -p $(dir $@) | ||||
| 	$(call quiet-command,\ | ||||
| 		perl $(SRC_PATH)/scripts/shaderinclude.pl $< > $@,\ | ||||
| 		"  FRAG  $@") | ||||
|  | ||||
| ui/console-gl.o: $(SRC_PATH)/ui/console-gl.c \ | ||||
| 	ui/shader/texture-blit-vert.h ui/shader/texture-blit-frag.h | ||||
| 	rm -f ./cscope.* | ||||
| 	find "$(SRC_PATH)" -name "*.[chsS]" -print | sed 's,^\./,,' > ./cscope.files | ||||
| 	cscope -b | ||||
|  | ||||
| # documentation | ||||
| MAKEINFO=makeinfo | ||||
| @@ -531,16 +464,13 @@ qemu-options.texi: $(SRC_PATH)/qemu-options.hx | ||||
| qemu-monitor.texi: $(SRC_PATH)/hmp-commands.hx | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"  GEN   $@") | ||||
|  | ||||
| qemu-monitor-info.texi: $(SRC_PATH)/hmp-commands-info.hx | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"  GEN   $@") | ||||
|  | ||||
| qmp-commands.txt: $(SRC_PATH)/qmp-commands.hx | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -q < $< > $@,"  GEN   $@") | ||||
|  | ||||
| qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"  GEN   $@") | ||||
|  | ||||
| qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi qemu-monitor-info.texi | ||||
| qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi | ||||
| 	$(call quiet-command, \ | ||||
| 	  perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu.pod && \ | ||||
| 	  $(POD2MAN) --section=1 --center=" " --release=" " qemu.pod > $@, \ | ||||
| @@ -564,12 +494,6 @@ qemu-nbd.8: qemu-nbd.texi | ||||
| 	  $(POD2MAN) --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \ | ||||
| 	  "  GEN   $@") | ||||
|  | ||||
| qemu-ga.8: qemu-ga.texi | ||||
| 	$(call quiet-command, \ | ||||
| 	  perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-ga.pod && \ | ||||
| 	  $(POD2MAN) --section=8 --center=" " --release=" " qemu-ga.pod > $@, \ | ||||
| 	  "  GEN   $@") | ||||
|  | ||||
| kvm_stat.1: scripts/kvm/kvm_stat.texi | ||||
| 	$(call quiet-command, \ | ||||
| 	  perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< kvm_stat.pod && \ | ||||
| @@ -583,8 +507,7 @@ pdf: qemu-doc.pdf qemu-tech.pdf | ||||
|  | ||||
| qemu-doc.dvi qemu-doc.html qemu-doc.info qemu-doc.pdf: \ | ||||
| 	qemu-img.texi qemu-nbd.texi qemu-options.texi \ | ||||
| 	qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \ | ||||
| 	qemu-monitor-info.texi | ||||
| 	qemu-monitor.texi qemu-img-cmds.texi | ||||
|  | ||||
| ifdef CONFIG_WIN32 | ||||
|  | ||||
| @@ -634,7 +557,6 @@ endif # SIGNCODE | ||||
|                 $(if $(DLL_PATH),-DDLLDIR="$(DLL_PATH)") \ | ||||
|                 -DSRCDIR="$(SRC_PATH)" \ | ||||
|                 -DOUTFILE="$(INSTALLER)" \ | ||||
|                 -DDISPLAYVERSION="$(VERSION)" \ | ||||
|                 $(SRC_PATH)/qemu.nsi | ||||
| 	rm -r ${INSTDIR} | ||||
| ifdef SIGNCODE | ||||
| @@ -644,7 +566,7 @@ endif # CONFIG_WIN | ||||
|  | ||||
| # Add a dependency on the generated files, so that they are always | ||||
| # rebuilt before other object files | ||||
| ifneq ($(filter-out $(UNCHECKED_GOALS),$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail)) | ||||
| ifneq ($(filter-out %clean,$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail)) | ||||
| Makefile: $(GENERATED_HEADERS) | ||||
| endif | ||||
|  | ||||
|   | ||||
| @@ -1,38 +1,37 @@ | ||||
| ####################################################################### | ||||
| # Common libraries for tools and emulators | ||||
| stub-obj-y = stubs/ | ||||
| util-obj-y = util/ qobject/ qapi/ | ||||
| util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o | ||||
| util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o | ||||
|  | ||||
| ####################################################################### | ||||
| # 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 += block.o blockjob.o | ||||
| block-obj-y += nbd.o 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 += qemu-io-cmds.o | ||||
|  | ||||
| block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o | ||||
| block-obj-y += qemu-coroutine-sleep.o | ||||
| block-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o | ||||
|  | ||||
| block-obj-m = block/ | ||||
|  | ||||
| ####################################################################### | ||||
| # crypto-obj-y is code used by both qemu system emulation and qemu-img | ||||
|  | ||||
| crypto-obj-y = crypto/ | ||||
| crypto-aes-obj-y = crypto/ | ||||
| ###################################################################### | ||||
| # smartcard | ||||
|  | ||||
| ####################################################################### | ||||
| # qom-obj-y is code used by both qemu system emulation and qemu-img | ||||
|  | ||||
| qom-obj-y = qom/ | ||||
|  | ||||
| ####################################################################### | ||||
| # io-obj-y is code used by both qemu system emulation and qemu-img | ||||
|  | ||||
| io-obj-y = io/ | ||||
| libcacard-y += libcacard/cac.o libcacard/event.o | ||||
| libcacard-y += libcacard/vcard.o libcacard/vreader.o | ||||
| libcacard-y += libcacard/vcard_emul_nss.o | ||||
| libcacard-y += libcacard/vcard_emul_type.o | ||||
| libcacard-y += libcacard/card_7816.o | ||||
| libcacard-y += libcacard/vcardt.o | ||||
| libcacard/vcard_emul_nss.o-cflags := $(NSS_CFLAGS) | ||||
| libcacard/vcard_emul_nss.o-libs := $(NSS_LIBS) | ||||
|  | ||||
| ###################################################################### | ||||
| # Target independent part of system emulation. The long term path is to | ||||
| @@ -60,8 +59,6 @@ common-obj-y += audio/ | ||||
| common-obj-y += hw/ | ||||
| common-obj-y += accel.o | ||||
|  | ||||
| common-obj-y += replay/ | ||||
|  | ||||
| common-obj-y += ui/ | ||||
| common-obj-y += bt-host.o bt-vhci.o | ||||
| bt-host.o-cflags := $(BLUEZ_CFLAGS) | ||||
| @@ -77,18 +74,18 @@ common-obj-y += backends/ | ||||
|  | ||||
| common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o | ||||
|  | ||||
| common-obj-$(CONFIG_FDT) += device_tree.o | ||||
| common-obj-$(CONFIG_SMARTCARD_NSS) += $(libcacard-y) | ||||
|  | ||||
| ###################################################################### | ||||
| # qapi | ||||
|  | ||||
| common-obj-y += qmp-marshal.o | ||||
| common-obj-y += qmp-introspect.o | ||||
| common-obj-y += qmp.o hmp.o | ||||
| endif | ||||
|  | ||||
| ####################################################################### | ||||
| # Target-independent parts used in system and user emulation | ||||
| common-obj-y += qemu-log.o | ||||
| common-obj-y += tcg-runtime.o | ||||
| common-obj-y += hw/ | ||||
| common-obj-y += qom/ | ||||
| @@ -111,8 +108,3 @@ target-obj-y += trace/ | ||||
| # by libqemuutil.a.  These should be moved to a separate .json schema. | ||||
| qga-obj-y = qga/ | ||||
| qga-vss-dll-obj-y = qga/ | ||||
|  | ||||
| ###################################################################### | ||||
| # contrib | ||||
| ivshmem-client-obj-y = contrib/ivshmem-client/ | ||||
| ivshmem-server-obj-y = contrib/ivshmem-server/ | ||||
|   | ||||
| @@ -1,13 +1,11 @@ | ||||
| # -*- Mode: makefile -*- | ||||
|  | ||||
| BUILD_DIR?=$(CURDIR)/.. | ||||
|  | ||||
| include ../config-host.mak | ||||
| include config-target.mak | ||||
| include config-devices.mak | ||||
| include $(SRC_PATH)/rules.mak | ||||
|  | ||||
| $(call set-vpath, $(SRC_PATH):$(BUILD_DIR)) | ||||
| $(call set-vpath, $(SRC_PATH)) | ||||
| ifdef CONFIG_LINUX | ||||
| QEMU_CFLAGS += -I../linux-headers | ||||
| endif | ||||
| @@ -36,6 +34,10 @@ endif | ||||
| PROGS=$(QEMU_PROG) $(QEMU_PROGW) | ||||
| STPFILES= | ||||
|  | ||||
| ifdef CONFIG_LINUX_USER | ||||
| PROGS+=$(QEMU_PROG)-binfmt | ||||
| endif | ||||
|  | ||||
| config-target.h: config-target.h-timestamp | ||||
| config-target.h-timestamp: config-target.mak | ||||
|  | ||||
| @@ -85,11 +87,8 @@ all: $(PROGS) stap | ||||
| ######################################################### | ||||
| # cpu emulator library | ||||
| obj-y = exec.o translate-all.o cpu-exec.o | ||||
| obj-y += translate-common.o | ||||
| obj-y += cpu-exec-common.o | ||||
| obj-y += tcg/tcg.o tcg/tcg-op.o tcg/optimize.o | ||||
| obj-$(CONFIG_TCG_INTERPRETER) += tci.o | ||||
| obj-y += tcg/tcg-common.o | ||||
| obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o | ||||
| obj-y += fpu/softfloat.o | ||||
| obj-y += target-$(TARGET_BASE_ARCH)/ | ||||
| @@ -113,6 +112,8 @@ QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) -I$(SRC_PATH)/linux-user | ||||
| obj-y += linux-user/ | ||||
| obj-y += gdbstub.o thunk.o user-exec.o | ||||
|  | ||||
| obj-binfmt-y += linux-user/ | ||||
|  | ||||
| endif #CONFIG_LINUX_USER | ||||
|  | ||||
| ######################################################### | ||||
| @@ -134,12 +135,12 @@ ifdef CONFIG_SOFTMMU | ||||
| obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o | ||||
| obj-y += qtest.o bootdevice.o | ||||
| obj-y += hw/ | ||||
| obj-$(CONFIG_FDT) += device_tree.o | ||||
| obj-$(CONFIG_KVM) += kvm-all.o | ||||
| obj-y += memory.o cputlb.o | ||||
| obj-y += memory.o savevm.o cputlb.o | ||||
| obj-y += memory_mapping.o | ||||
| obj-y += dump.o | ||||
| obj-y += migration/ram.o migration/savevm.o | ||||
| LIBS := $(libs_softmmu) $(LIBS) | ||||
| LIBS+=$(libs_softmmu) | ||||
|  | ||||
| # xen support | ||||
| obj-$(CONFIG_XEN) += xen-common.o | ||||
| @@ -154,14 +155,18 @@ else | ||||
| obj-y += hw/$(TARGET_BASE_ARCH)/ | ||||
| endif | ||||
|  | ||||
| GENERATED_HEADERS += hmp-commands.h hmp-commands-info.h qmp-commands-old.h | ||||
| GENERATED_HEADERS += hmp-commands.h qmp-commands-old.h | ||||
|  | ||||
| endif # CONFIG_SOFTMMU | ||||
|  | ||||
| # Workaround for http://gcc.gnu.org/PR55489, see configure. | ||||
| %/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS) | ||||
|  | ||||
| ifdef CONFIG_LINUX_USER | ||||
| dummy := $(call unnest-vars,,obj-y obj-binfmt-y) | ||||
| else | ||||
| dummy := $(call unnest-vars,,obj-y) | ||||
| endif | ||||
| all-obj-y := $(obj-y) | ||||
|  | ||||
| target-obj-y := | ||||
| @@ -173,30 +178,21 @@ target-obj-y-save := $(target-obj-y) | ||||
| dummy := $(call unnest-vars,.., \ | ||||
|                block-obj-y \ | ||||
|                block-obj-m \ | ||||
|                crypto-obj-y \ | ||||
|                crypto-aes-obj-y \ | ||||
|                qom-obj-y \ | ||||
|                io-obj-y \ | ||||
|                common-obj-y \ | ||||
|                common-obj-m) | ||||
| target-obj-y := $(target-obj-y-save) | ||||
| all-obj-y += $(common-obj-y) | ||||
| all-obj-y += $(target-obj-y) | ||||
| all-obj-y += $(qom-obj-y) | ||||
| all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) | ||||
| all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y) | ||||
| all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y) | ||||
| all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y) | ||||
|  | ||||
| $(QEMU_PROG_BUILD): config-devices.mak | ||||
|  | ||||
| # build either PROG or PROGW | ||||
| $(QEMU_PROG_BUILD): $(all-obj-y) ../libqemuutil.a ../libqemustub.a | ||||
| 	$(call LINK, $(filter-out %.mak, $^)) | ||||
| ifdef CONFIG_DARWIN | ||||
| 	$(call quiet-command,Rez -append $(SRC_PATH)/pc-bios/qemu.rsrc -o $@,"  REZ   $(TARGET_DIR)$@") | ||||
| 	$(call quiet-command,SetFile -a C $@,"  SETFILE $(TARGET_DIR)$@") | ||||
| endif | ||||
|  | ||||
| $(QEMU_PROG)-binfmt: $(obj-binfmt-y) | ||||
| 	$(call LINK,$^) | ||||
|  | ||||
| gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/scripts/feature_to_c.sh | ||||
| 	$(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/scripts/feature_to_c.sh $@ $(TARGET_XML_FILES),"  GEN   $(TARGET_DIR)$@") | ||||
| @@ -204,9 +200,6 @@ gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/scripts/feature_to_c.sh | ||||
| hmp-commands.h: $(SRC_PATH)/hmp-commands.hx | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"  GEN   $(TARGET_DIR)$@") | ||||
|  | ||||
| hmp-commands-info.h: $(SRC_PATH)/hmp-commands-info.hx | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"  GEN   $(TARGET_DIR)$@") | ||||
|  | ||||
| qmp-commands-old.h: $(SRC_PATH)/qmp-commands.hx | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"  GEN   $(TARGET_DIR)$@") | ||||
|  | ||||
|   | ||||
							
								
								
									
										108
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										108
									
								
								README
									
									
									
									
									
								
							| @@ -1,107 +1,3 @@ | ||||
|          QEMU README | ||||
|          =========== | ||||
| Read the documentation in qemu-doc.html or on http://wiki.qemu-project.org | ||||
|  | ||||
| QEMU is a generic and open source machine & userspace emulator and | ||||
| virtualizer. | ||||
|  | ||||
| QEMU is capable of emulating a complete machine in software without any | ||||
| need for hardware virtualization support. By using dynamic translation, | ||||
| it achieves very good performance. QEMU can also integrate with the Xen | ||||
| and KVM hypervisors to provide emulated hardware while allowing the | ||||
| hypervisor to manage the CPU. With hypervisor support, QEMU can achieve | ||||
| near native performance for CPUs. When QEMU emulates CPUs directly it is | ||||
| capable of running operating systems made for one machine (e.g. an ARMv7 | ||||
| board) on a different machine (e.g. an x86_64 PC board). | ||||
|  | ||||
| QEMU is also capable of providing userspace API virtualization for Linux | ||||
| and BSD kernel interfaces. This allows binaries compiled against one | ||||
| architecture ABI (e.g. the Linux PPC64 ABI) to be run on a host using a | ||||
| different architecture ABI (e.g. the Linux x86_64 ABI). This does not | ||||
| involve any hardware emulation, simply CPU and syscall emulation. | ||||
|  | ||||
| QEMU aims to fit into a variety of use cases. It can be invoked directly | ||||
| by users wishing to have full control over its behaviour and settings. | ||||
| It also aims to facilitate integration into higher level management | ||||
| layers, by providing a stable command line interface and monitor API. | ||||
| It is commonly invoked indirectly via the libvirt library when using | ||||
| open source applications such as oVirt, OpenStack and virt-manager. | ||||
|  | ||||
| QEMU as a whole is released under the GNU General Public License, | ||||
| version 2. For full licensing details, consult the LICENSE file. | ||||
|  | ||||
|  | ||||
| Building | ||||
| ======== | ||||
|  | ||||
| QEMU is multi-platform software intended to be buildable on all modern | ||||
| Linux platforms, OS-X, Win32 (via the Mingw64 toolchain) and a variety | ||||
| of other UNIX targets. The simple steps to build QEMU are: | ||||
|  | ||||
|   mkdir build | ||||
|   cd build | ||||
|   ../configure | ||||
|   make | ||||
|  | ||||
| Complete details of the process for building and configuring QEMU for | ||||
| all supported host platforms can be found in the qemu-tech.html file. | ||||
| Additional information can also be found online via the QEMU website: | ||||
|  | ||||
|   http://qemu-project.org/Hosts/Linux | ||||
|   http://qemu-project.org/Hosts/W32 | ||||
|  | ||||
|  | ||||
| Submitting patches | ||||
| ================== | ||||
|  | ||||
| The QEMU source code is maintained under the GIT version control system. | ||||
|  | ||||
|    git clone git://git.qemu-project.org/qemu.git | ||||
|  | ||||
| When submitting patches, the preferred approach is to use 'git | ||||
| format-patch' and/or 'git send-email' to format & send the mail to the | ||||
| qemu-devel@nongnu.org mailing list. All patches submitted must contain | ||||
| a 'Signed-off-by' line from the author. Patches should follow the | ||||
| guidelines set out in the HACKING and CODING_STYLE files. | ||||
|  | ||||
| Additional information on submitting patches can be found online via | ||||
| the QEMU website | ||||
|  | ||||
|   http://qemu-project.org/Contribute/SubmitAPatch | ||||
|   http://qemu-project.org/Contribute/TrivialPatches | ||||
|  | ||||
|  | ||||
| Bug reporting | ||||
| ============= | ||||
|  | ||||
| The QEMU project uses Launchpad as its primary upstream bug tracker. Bugs | ||||
| found when running code built from QEMU git or upstream released sources | ||||
| should be reported via: | ||||
|  | ||||
|   https://bugs.launchpad.net/qemu/ | ||||
|  | ||||
| If using QEMU via an operating system vendor pre-built binary package, it | ||||
| is preferable to report bugs to the vendor's own bug tracker first. If | ||||
| the bug is also known to affect latest upstream code, it can also be | ||||
| reported via launchpad. | ||||
|  | ||||
| For additional information on bug reporting consult: | ||||
|  | ||||
|   http://qemu-project.org/Contribute/ReportABug | ||||
|  | ||||
|  | ||||
| Contact | ||||
| ======= | ||||
|  | ||||
| The QEMU community can be contacted in a number of ways, with the two | ||||
| main methods being email and IRC | ||||
|  | ||||
|  - qemu-devel@nongnu.org | ||||
|    http://lists.nongnu.org/mailman/listinfo/qemu-devel | ||||
|  - #qemu on irc.oftc.net | ||||
|  | ||||
| Information on additional methods of contacting the community can be | ||||
| found online via the QEMU website: | ||||
|  | ||||
|   http://qemu-project.org/Contribute/StartHere | ||||
|  | ||||
| -- End | ||||
| - QEMU team | ||||
|   | ||||
							
								
								
									
										1
									
								
								accel.c
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								accel.c
									
									
									
									
									
								
							| @@ -23,7 +23,6 @@ | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "sysemu/accel.h" | ||||
| #include "hw/boards.h" | ||||
| #include "qemu-common.h" | ||||
|   | ||||
							
								
								
									
										309
									
								
								aio-posix.c
									
									
									
									
									
								
							
							
						
						
									
										309
									
								
								aio-posix.c
									
									
									
									
									
								
							| @@ -13,14 +13,10 @@ | ||||
|  * GNU GPL, version 2 or (at your option) any later version. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "block/block.h" | ||||
| #include "qemu/queue.h" | ||||
| #include "qemu/sockets.h" | ||||
| #ifdef CONFIG_EPOLL | ||||
| #include <sys/epoll.h> | ||||
| #endif | ||||
|  | ||||
| struct AioHandler | ||||
| { | ||||
| @@ -28,167 +24,11 @@ struct AioHandler | ||||
|     IOHandler *io_read; | ||||
|     IOHandler *io_write; | ||||
|     int deleted; | ||||
|     int pollfds_idx; | ||||
|     void *opaque; | ||||
|     bool is_external; | ||||
|     QLIST_ENTRY(AioHandler) node; | ||||
| }; | ||||
|  | ||||
| #ifdef CONFIG_EPOLL | ||||
|  | ||||
| /* The fd number threashold to switch to epoll */ | ||||
| #define EPOLL_ENABLE_THRESHOLD 64 | ||||
|  | ||||
| static void aio_epoll_disable(AioContext *ctx) | ||||
| { | ||||
|     ctx->epoll_available = false; | ||||
|     if (!ctx->epoll_enabled) { | ||||
|         return; | ||||
|     } | ||||
|     ctx->epoll_enabled = false; | ||||
|     close(ctx->epollfd); | ||||
| } | ||||
|  | ||||
| static inline int epoll_events_from_pfd(int pfd_events) | ||||
| { | ||||
|     return (pfd_events & G_IO_IN ? EPOLLIN : 0) | | ||||
|            (pfd_events & G_IO_OUT ? EPOLLOUT : 0) | | ||||
|            (pfd_events & G_IO_HUP ? EPOLLHUP : 0) | | ||||
|            (pfd_events & G_IO_ERR ? EPOLLERR : 0); | ||||
| } | ||||
|  | ||||
| static bool aio_epoll_try_enable(AioContext *ctx) | ||||
| { | ||||
|     AioHandler *node; | ||||
|     struct epoll_event event; | ||||
|  | ||||
|     QLIST_FOREACH(node, &ctx->aio_handlers, node) { | ||||
|         int r; | ||||
|         if (node->deleted || !node->pfd.events) { | ||||
|             continue; | ||||
|         } | ||||
|         event.events = epoll_events_from_pfd(node->pfd.events); | ||||
|         event.data.ptr = node; | ||||
|         r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, node->pfd.fd, &event); | ||||
|         if (r) { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     ctx->epoll_enabled = true; | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| static void aio_epoll_update(AioContext *ctx, AioHandler *node, bool is_new) | ||||
| { | ||||
|     struct epoll_event event; | ||||
|     int r; | ||||
|  | ||||
|     if (!ctx->epoll_enabled) { | ||||
|         return; | ||||
|     } | ||||
|     if (!node->pfd.events) { | ||||
|         r = epoll_ctl(ctx->epollfd, EPOLL_CTL_DEL, node->pfd.fd, &event); | ||||
|         if (r) { | ||||
|             aio_epoll_disable(ctx); | ||||
|         } | ||||
|     } else { | ||||
|         event.data.ptr = node; | ||||
|         event.events = epoll_events_from_pfd(node->pfd.events); | ||||
|         if (is_new) { | ||||
|             r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, node->pfd.fd, &event); | ||||
|             if (r) { | ||||
|                 aio_epoll_disable(ctx); | ||||
|             } | ||||
|         } else { | ||||
|             r = epoll_ctl(ctx->epollfd, EPOLL_CTL_MOD, node->pfd.fd, &event); | ||||
|             if (r) { | ||||
|                 aio_epoll_disable(ctx); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int aio_epoll(AioContext *ctx, GPollFD *pfds, | ||||
|                      unsigned npfd, int64_t timeout) | ||||
| { | ||||
|     AioHandler *node; | ||||
|     int i, ret = 0; | ||||
|     struct epoll_event events[128]; | ||||
|  | ||||
|     assert(npfd == 1); | ||||
|     assert(pfds[0].fd == ctx->epollfd); | ||||
|     if (timeout > 0) { | ||||
|         ret = qemu_poll_ns(pfds, npfd, timeout); | ||||
|     } | ||||
|     if (timeout <= 0 || ret > 0) { | ||||
|         ret = epoll_wait(ctx->epollfd, events, | ||||
|                          sizeof(events) / sizeof(events[0]), | ||||
|                          timeout); | ||||
|         if (ret <= 0) { | ||||
|             goto out; | ||||
|         } | ||||
|         for (i = 0; i < ret; i++) { | ||||
|             int ev = events[i].events; | ||||
|             node = events[i].data.ptr; | ||||
|             node->pfd.revents = (ev & EPOLLIN ? G_IO_IN : 0) | | ||||
|                 (ev & EPOLLOUT ? G_IO_OUT : 0) | | ||||
|                 (ev & EPOLLHUP ? G_IO_HUP : 0) | | ||||
|                 (ev & EPOLLERR ? G_IO_ERR : 0); | ||||
|         } | ||||
|     } | ||||
| out: | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static bool aio_epoll_enabled(AioContext *ctx) | ||||
| { | ||||
|     /* Fall back to ppoll when external clients are disabled. */ | ||||
|     return !aio_external_disabled(ctx) && ctx->epoll_enabled; | ||||
| } | ||||
|  | ||||
| static bool aio_epoll_check_poll(AioContext *ctx, GPollFD *pfds, | ||||
|                                  unsigned npfd, int64_t timeout) | ||||
| { | ||||
|     if (!ctx->epoll_available) { | ||||
|         return false; | ||||
|     } | ||||
|     if (aio_epoll_enabled(ctx)) { | ||||
|         return true; | ||||
|     } | ||||
|     if (npfd >= EPOLL_ENABLE_THRESHOLD) { | ||||
|         if (aio_epoll_try_enable(ctx)) { | ||||
|             return true; | ||||
|         } else { | ||||
|             aio_epoll_disable(ctx); | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| #else | ||||
|  | ||||
| static void aio_epoll_update(AioContext *ctx, AioHandler *node, bool is_new) | ||||
| { | ||||
| } | ||||
|  | ||||
| static int aio_epoll(AioContext *ctx, GPollFD *pfds, | ||||
|                      unsigned npfd, int64_t timeout) | ||||
| { | ||||
|     assert(false); | ||||
| } | ||||
|  | ||||
| static bool aio_epoll_enabled(AioContext *ctx) | ||||
| { | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| static bool aio_epoll_check_poll(AioContext *ctx, GPollFD *pfds, | ||||
|                           unsigned npfd, int64_t timeout) | ||||
| { | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| #endif | ||||
|  | ||||
| static AioHandler *find_aio_handler(AioContext *ctx, int fd) | ||||
| { | ||||
|     AioHandler *node; | ||||
| @@ -204,14 +44,11 @@ static AioHandler *find_aio_handler(AioContext *ctx, int fd) | ||||
|  | ||||
| void aio_set_fd_handler(AioContext *ctx, | ||||
|                         int fd, | ||||
|                         bool is_external, | ||||
|                         IOHandler *io_read, | ||||
|                         IOHandler *io_write, | ||||
|                         void *opaque) | ||||
| { | ||||
|     AioHandler *node; | ||||
|     bool is_new = false; | ||||
|     bool deleted = false; | ||||
|  | ||||
|     node = find_aio_handler(ctx, fd); | ||||
|  | ||||
| @@ -230,7 +67,7 @@ void aio_set_fd_handler(AioContext *ctx, | ||||
|                  * releasing the walking_handlers lock. | ||||
|                  */ | ||||
|                 QLIST_REMOVE(node, node); | ||||
|                 deleted = true; | ||||
|                 g_free(node); | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
| @@ -241,32 +78,26 @@ void aio_set_fd_handler(AioContext *ctx, | ||||
|             QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node); | ||||
|  | ||||
|             g_source_add_poll(&ctx->source, &node->pfd); | ||||
|             is_new = true; | ||||
|         } | ||||
|         /* Update handler with latest information */ | ||||
|         node->io_read = io_read; | ||||
|         node->io_write = io_write; | ||||
|         node->opaque = opaque; | ||||
|         node->is_external = is_external; | ||||
|         node->pollfds_idx = -1; | ||||
|  | ||||
|         node->pfd.events = (io_read ? G_IO_IN | G_IO_HUP | G_IO_ERR : 0); | ||||
|         node->pfd.events |= (io_write ? G_IO_OUT | G_IO_ERR : 0); | ||||
|     } | ||||
|  | ||||
|     aio_epoll_update(ctx, node, is_new); | ||||
|     aio_notify(ctx); | ||||
|     if (deleted) { | ||||
|         g_free(node); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void aio_set_event_notifier(AioContext *ctx, | ||||
|                             EventNotifier *notifier, | ||||
|                             bool is_external, | ||||
|                             EventNotifierHandler *io_read) | ||||
| { | ||||
|     aio_set_fd_handler(ctx, event_notifier_get_fd(notifier), | ||||
|                        is_external, (IOHandler *)io_read, NULL, notifier); | ||||
|                        (IOHandler *)io_read, NULL, notifier); | ||||
| } | ||||
|  | ||||
| bool aio_prepare(AioContext *ctx) | ||||
| @@ -355,141 +186,69 @@ bool aio_dispatch(AioContext *ctx) | ||||
|     return progress; | ||||
| } | ||||
|  | ||||
| /* These thread-local variables are used only in a small part of aio_poll | ||||
|  * around the call to the poll() system call.  In particular they are not | ||||
|  * used while aio_poll is performing callbacks, which makes it much easier | ||||
|  * to think about reentrancy! | ||||
|  * | ||||
|  * Stack-allocated arrays would be perfect but they have size limitations; | ||||
|  * heap allocation is expensive enough that we want to reuse arrays across | ||||
|  * calls to aio_poll().  And because poll() has to be called without holding | ||||
|  * any lock, the arrays cannot be stored in AioContext.  Thread-local data | ||||
|  * has none of the disadvantages of these three options. | ||||
|  */ | ||||
| static __thread GPollFD *pollfds; | ||||
| static __thread AioHandler **nodes; | ||||
| static __thread unsigned npfd, nalloc; | ||||
| static __thread Notifier pollfds_cleanup_notifier; | ||||
|  | ||||
| static void pollfds_cleanup(Notifier *n, void *unused) | ||||
| { | ||||
|     g_assert(npfd == 0); | ||||
|     g_free(pollfds); | ||||
|     g_free(nodes); | ||||
|     nalloc = 0; | ||||
| } | ||||
|  | ||||
| static void add_pollfd(AioHandler *node) | ||||
| { | ||||
|     if (npfd == nalloc) { | ||||
|         if (nalloc == 0) { | ||||
|             pollfds_cleanup_notifier.notify = pollfds_cleanup; | ||||
|             qemu_thread_atexit_add(&pollfds_cleanup_notifier); | ||||
|             nalloc = 8; | ||||
|         } else { | ||||
|             g_assert(nalloc <= INT_MAX); | ||||
|             nalloc *= 2; | ||||
|         } | ||||
|         pollfds = g_renew(GPollFD, pollfds, nalloc); | ||||
|         nodes = g_renew(AioHandler *, nodes, nalloc); | ||||
|     } | ||||
|     nodes[npfd] = node; | ||||
|     pollfds[npfd] = (GPollFD) { | ||||
|         .fd = node->pfd.fd, | ||||
|         .events = node->pfd.events, | ||||
|     }; | ||||
|     npfd++; | ||||
| } | ||||
|  | ||||
| bool aio_poll(AioContext *ctx, bool blocking) | ||||
| { | ||||
|     AioHandler *node; | ||||
|     int i, ret; | ||||
|     bool was_dispatching; | ||||
|     int ret; | ||||
|     bool progress; | ||||
|     int64_t timeout; | ||||
|  | ||||
|     aio_context_acquire(ctx); | ||||
|     was_dispatching = ctx->dispatching; | ||||
|     progress = false; | ||||
|  | ||||
|     /* aio_notify can avoid the expensive event_notifier_set if | ||||
|      * everything (file descriptors, bottom halves, timers) will | ||||
|      * be re-evaluated before the next blocking poll().  This is | ||||
|      * already true when aio_poll is called with blocking == false; | ||||
|      * if blocking == true, it is only true after poll() returns, | ||||
|      * so disable the optimization now. | ||||
|      * if blocking == true, it is only true after poll() returns. | ||||
|      * | ||||
|      * If we're in a nested event loop, ctx->dispatching might be true. | ||||
|      * In that case we can restore it just before returning, but we | ||||
|      * have to clear it now. | ||||
|      */ | ||||
|     if (blocking) { | ||||
|         atomic_add(&ctx->notify_me, 2); | ||||
|     } | ||||
|     aio_set_dispatching(ctx, !blocking); | ||||
|  | ||||
|     ctx->walking_handlers++; | ||||
|  | ||||
|     assert(npfd == 0); | ||||
|     g_array_set_size(ctx->pollfds, 0); | ||||
|  | ||||
|     /* fill pollfds */ | ||||
|     QLIST_FOREACH(node, &ctx->aio_handlers, node) { | ||||
|         if (!node->deleted && node->pfd.events | ||||
|             && !aio_epoll_enabled(ctx) | ||||
|             && aio_node_check(ctx, node->is_external)) { | ||||
|             add_pollfd(node); | ||||
|         node->pollfds_idx = -1; | ||||
|         if (!node->deleted && node->pfd.events) { | ||||
|             GPollFD pfd = { | ||||
|                 .fd = node->pfd.fd, | ||||
|                 .events = node->pfd.events, | ||||
|             }; | ||||
|             node->pollfds_idx = ctx->pollfds->len; | ||||
|             g_array_append_val(ctx->pollfds, pfd); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     timeout = blocking ? aio_compute_timeout(ctx) : 0; | ||||
|     ctx->walking_handlers--; | ||||
|  | ||||
|     /* wait until next event */ | ||||
|     if (timeout) { | ||||
|         aio_context_release(ctx); | ||||
|     } | ||||
|     if (aio_epoll_check_poll(ctx, pollfds, npfd, timeout)) { | ||||
|         AioHandler epoll_handler; | ||||
|  | ||||
|         epoll_handler.pfd.fd = ctx->epollfd; | ||||
|         epoll_handler.pfd.events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR; | ||||
|         npfd = 0; | ||||
|         add_pollfd(&epoll_handler); | ||||
|         ret = aio_epoll(ctx, pollfds, npfd, timeout); | ||||
|     } else  { | ||||
|         ret = qemu_poll_ns(pollfds, npfd, timeout); | ||||
|     } | ||||
|     if (blocking) { | ||||
|         atomic_sub(&ctx->notify_me, 2); | ||||
|     } | ||||
|     if (timeout) { | ||||
|         aio_context_acquire(ctx); | ||||
|     } | ||||
|  | ||||
|     aio_notify_accept(ctx); | ||||
|     ret = qemu_poll_ns((GPollFD *)ctx->pollfds->data, | ||||
|                          ctx->pollfds->len, | ||||
|                          blocking ? aio_compute_timeout(ctx) : 0); | ||||
|  | ||||
|     /* if we have any readable fds, dispatch event */ | ||||
|     if (ret > 0) { | ||||
|         for (i = 0; i < npfd; i++) { | ||||
|             nodes[i]->pfd.revents = pollfds[i].revents; | ||||
|         QLIST_FOREACH(node, &ctx->aio_handlers, node) { | ||||
|             if (node->pollfds_idx != -1) { | ||||
|                 GPollFD *pfd = &g_array_index(ctx->pollfds, GPollFD, | ||||
|                                               node->pollfds_idx); | ||||
|                 node->pfd.revents = pfd->revents; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     npfd = 0; | ||||
|     ctx->walking_handlers--; | ||||
|  | ||||
|     /* Run dispatch even if there were no readable fds to run timers */ | ||||
|     aio_set_dispatching(ctx, true); | ||||
|     if (aio_dispatch(ctx)) { | ||||
|         progress = true; | ||||
|     } | ||||
|  | ||||
|     aio_context_release(ctx); | ||||
|  | ||||
|     aio_set_dispatching(ctx, was_dispatching); | ||||
|     return progress; | ||||
| } | ||||
|  | ||||
| void aio_context_setup(AioContext *ctx, Error **errp) | ||||
| { | ||||
| #ifdef CONFIG_EPOLL | ||||
|     assert(!ctx->epollfd); | ||||
|     ctx->epollfd = epoll_create1(EPOLL_CLOEXEC); | ||||
|     if (ctx->epollfd == -1) { | ||||
|         ctx->epoll_available = false; | ||||
|     } else { | ||||
|         ctx->epoll_available = true; | ||||
|     } | ||||
| #endif | ||||
| } | ||||
|   | ||||
							
								
								
									
										69
									
								
								aio-win32.c
									
									
									
									
									
								
							
							
						
						
									
										69
									
								
								aio-win32.c
									
									
									
									
									
								
							| @@ -15,7 +15,6 @@ | ||||
|  * GNU GPL, version 2 or (at your option) any later version. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "block/block.h" | ||||
| #include "qemu/queue.h" | ||||
| @@ -29,13 +28,11 @@ struct AioHandler { | ||||
|     GPollFD pfd; | ||||
|     int deleted; | ||||
|     void *opaque; | ||||
|     bool is_external; | ||||
|     QLIST_ENTRY(AioHandler) node; | ||||
| }; | ||||
|  | ||||
| void aio_set_fd_handler(AioContext *ctx, | ||||
|                         int fd, | ||||
|                         bool is_external, | ||||
|                         IOHandler *io_read, | ||||
|                         IOHandler *io_write, | ||||
|                         void *opaque) | ||||
| @@ -89,7 +86,6 @@ void aio_set_fd_handler(AioContext *ctx, | ||||
|         node->opaque = opaque; | ||||
|         node->io_read = io_read; | ||||
|         node->io_write = io_write; | ||||
|         node->is_external = is_external; | ||||
|  | ||||
|         event = event_notifier_get_handle(&ctx->notifier); | ||||
|         WSAEventSelect(node->pfd.fd, event, | ||||
| @@ -102,7 +98,6 @@ void aio_set_fd_handler(AioContext *ctx, | ||||
|  | ||||
| void aio_set_event_notifier(AioContext *ctx, | ||||
|                             EventNotifier *e, | ||||
|                             bool is_external, | ||||
|                             EventNotifierHandler *io_notify) | ||||
| { | ||||
|     AioHandler *node; | ||||
| @@ -138,7 +133,6 @@ void aio_set_event_notifier(AioContext *ctx, | ||||
|             node->e = e; | ||||
|             node->pfd.fd = (uintptr_t)event_notifier_get_handle(e); | ||||
|             node->pfd.events = G_IO_IN; | ||||
|             node->is_external = is_external; | ||||
|             QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node); | ||||
|  | ||||
|             g_source_add_poll(&ctx->source, &node->pfd); | ||||
| @@ -285,33 +279,36 @@ bool aio_poll(AioContext *ctx, bool blocking) | ||||
| { | ||||
|     AioHandler *node; | ||||
|     HANDLE events[MAXIMUM_WAIT_OBJECTS + 1]; | ||||
|     bool progress, have_select_revents, first; | ||||
|     bool was_dispatching, progress, have_select_revents, first; | ||||
|     int count; | ||||
|     int timeout; | ||||
|  | ||||
|     aio_context_acquire(ctx); | ||||
|     have_select_revents = aio_prepare(ctx); | ||||
|     if (have_select_revents) { | ||||
|         blocking = false; | ||||
|     } | ||||
|  | ||||
|     was_dispatching = ctx->dispatching; | ||||
|     progress = false; | ||||
|  | ||||
|     /* aio_notify can avoid the expensive event_notifier_set if | ||||
|      * everything (file descriptors, bottom halves, timers) will | ||||
|      * be re-evaluated before the next blocking poll().  This is | ||||
|      * already true when aio_poll is called with blocking == false; | ||||
|      * if blocking == true, it is only true after poll() returns, | ||||
|      * so disable the optimization now. | ||||
|      * if blocking == true, it is only true after poll() returns. | ||||
|      * | ||||
|      * If we're in a nested event loop, ctx->dispatching might be true. | ||||
|      * In that case we can restore it just before returning, but we | ||||
|      * have to clear it now. | ||||
|      */ | ||||
|     if (blocking) { | ||||
|         atomic_add(&ctx->notify_me, 2); | ||||
|     } | ||||
|  | ||||
|     have_select_revents = aio_prepare(ctx); | ||||
|     aio_set_dispatching(ctx, !blocking); | ||||
|  | ||||
|     ctx->walking_handlers++; | ||||
|  | ||||
|     /* fill fd sets */ | ||||
|     count = 0; | ||||
|     QLIST_FOREACH(node, &ctx->aio_handlers, node) { | ||||
|         if (!node->deleted && node->io_notify | ||||
|             && aio_node_check(ctx, node->is_external)) { | ||||
|         if (!node->deleted && node->io_notify) { | ||||
|             events[count++] = event_notifier_get_handle(node->e); | ||||
|         } | ||||
|     } | ||||
| @@ -319,36 +316,20 @@ bool aio_poll(AioContext *ctx, bool blocking) | ||||
|     ctx->walking_handlers--; | ||||
|     first = true; | ||||
|  | ||||
|     /* ctx->notifier is always registered.  */ | ||||
|     assert(count > 0); | ||||
|  | ||||
|     /* Multiple iterations, all of them non-blocking except the first, | ||||
|      * may be necessary to process all pending events.  After the first | ||||
|      * WaitForMultipleObjects call ctx->notify_me will be decremented. | ||||
|      */ | ||||
|     do { | ||||
|     /* wait until next event */ | ||||
|     while (count > 0) { | ||||
|         HANDLE event; | ||||
|         int ret; | ||||
|  | ||||
|         timeout = blocking && !have_select_revents | ||||
|         timeout = blocking | ||||
|             ? qemu_timeout_ns_to_ms(aio_compute_timeout(ctx)) : 0; | ||||
|         if (timeout) { | ||||
|             aio_context_release(ctx); | ||||
|         } | ||||
|         ret = WaitForMultipleObjects(count, events, FALSE, timeout); | ||||
|         if (blocking) { | ||||
|             assert(first); | ||||
|             atomic_sub(&ctx->notify_me, 2); | ||||
|         } | ||||
|         if (timeout) { | ||||
|             aio_context_acquire(ctx); | ||||
|         } | ||||
|         aio_set_dispatching(ctx, true); | ||||
|  | ||||
|         if (first) { | ||||
|             aio_notify_accept(ctx); | ||||
|             progress |= aio_bh_poll(ctx); | ||||
|             first = false; | ||||
|         if (first && aio_bh_poll(ctx)) { | ||||
|             progress = true; | ||||
|         } | ||||
|         first = false; | ||||
|  | ||||
|         /* if we have any signaled events, dispatch event */ | ||||
|         event = NULL; | ||||
| @@ -363,14 +344,10 @@ bool aio_poll(AioContext *ctx, bool blocking) | ||||
|         blocking = false; | ||||
|  | ||||
|         progress |= aio_dispatch_handlers(ctx, event); | ||||
|     } while (count > 0); | ||||
|     } | ||||
|  | ||||
|     progress |= timerlistgroup_run_timers(&ctx->tlg); | ||||
|  | ||||
|     aio_context_release(ctx); | ||||
|     aio_set_dispatching(ctx, was_dispatching); | ||||
|     return progress; | ||||
| } | ||||
|  | ||||
| void aio_context_setup(AioContext *ctx, Error **errp) | ||||
| { | ||||
| } | ||||
|   | ||||
							
								
								
									
										1197
									
								
								arch_init.c
									
									
									
									
									
								
							
							
						
						
									
										1197
									
								
								arch_init.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										86
									
								
								async.c
									
									
									
									
									
								
							
							
						
						
									
										86
									
								
								async.c
									
									
									
									
									
								
							| @@ -22,7 +22,6 @@ | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "block/aio.h" | ||||
| #include "block/thread-pool.h" | ||||
| @@ -60,11 +59,6 @@ QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque) | ||||
|     return bh; | ||||
| } | ||||
|  | ||||
| void aio_bh_call(QEMUBH *bh) | ||||
| { | ||||
|     bh->cb(bh->opaque); | ||||
| } | ||||
|  | ||||
| /* Multiple occurrences of aio_bh_poll cannot be called concurrently */ | ||||
| int aio_bh_poll(AioContext *ctx) | ||||
| { | ||||
| @@ -85,12 +79,10 @@ int aio_bh_poll(AioContext *ctx) | ||||
|          * aio_notify again if necessary. | ||||
|          */ | ||||
|         if (!bh->deleted && atomic_xchg(&bh->scheduled, 0)) { | ||||
|             /* Idle BHs and the notify BH don't count as progress */ | ||||
|             if (!bh->idle && bh != ctx->notify_dummy_bh) { | ||||
|             if (!bh->idle) | ||||
|                 ret = 1; | ||||
|             } | ||||
|             bh->idle = 0; | ||||
|             aio_bh_call(bh); | ||||
|             bh->cb(bh->opaque); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -192,8 +184,6 @@ aio_ctx_prepare(GSource *source, gint    *timeout) | ||||
| { | ||||
|     AioContext *ctx = (AioContext *) source; | ||||
|  | ||||
|     atomic_or(&ctx->notify_me, 1); | ||||
|  | ||||
|     /* We assume there is no timeout already supplied */ | ||||
|     *timeout = qemu_timeout_ns_to_ms(aio_compute_timeout(ctx)); | ||||
|  | ||||
| @@ -210,9 +200,6 @@ aio_ctx_check(GSource *source) | ||||
|     AioContext *ctx = (AioContext *) source; | ||||
|     QEMUBH *bh; | ||||
|  | ||||
|     atomic_and(&ctx->notify_me, ~1); | ||||
|     aio_notify_accept(ctx); | ||||
|  | ||||
|     for (bh = ctx->first_bh; bh; bh = bh->next) { | ||||
|         if (!bh->deleted && bh->scheduled) { | ||||
|             return true; | ||||
| @@ -238,25 +225,12 @@ aio_ctx_finalize(GSource     *source) | ||||
| { | ||||
|     AioContext *ctx = (AioContext *) source; | ||||
|  | ||||
|     qemu_bh_delete(ctx->notify_dummy_bh); | ||||
|     thread_pool_free(ctx->thread_pool); | ||||
|  | ||||
|     qemu_mutex_lock(&ctx->bh_lock); | ||||
|     while (ctx->first_bh) { | ||||
|         QEMUBH *next = ctx->first_bh->next; | ||||
|  | ||||
|         /* qemu_bh_delete() must have been called on BHs in this AioContext */ | ||||
|         assert(ctx->first_bh->deleted); | ||||
|  | ||||
|         g_free(ctx->first_bh); | ||||
|         ctx->first_bh = next; | ||||
|     } | ||||
|     qemu_mutex_unlock(&ctx->bh_lock); | ||||
|  | ||||
|     aio_set_event_notifier(ctx, &ctx->notifier, false, NULL); | ||||
|     aio_set_event_notifier(ctx, &ctx->notifier, NULL); | ||||
|     event_notifier_cleanup(&ctx->notifier); | ||||
|     rfifolock_destroy(&ctx->lock); | ||||
|     qemu_mutex_destroy(&ctx->bh_lock); | ||||
|     g_array_free(ctx->pollfds, TRUE); | ||||
|     timerlistgroup_deinit(&ctx->tlg); | ||||
| } | ||||
|  | ||||
| @@ -281,22 +255,24 @@ ThreadPool *aio_get_thread_pool(AioContext *ctx) | ||||
|     return ctx->thread_pool; | ||||
| } | ||||
|  | ||||
| void aio_notify(AioContext *ctx) | ||||
| void aio_set_dispatching(AioContext *ctx, bool dispatching) | ||||
| { | ||||
|     /* Write e.g. bh->scheduled before reading ctx->notify_me.  Pairs | ||||
|      * with atomic_or in aio_ctx_prepare or atomic_add in aio_poll. | ||||
|     ctx->dispatching = dispatching; | ||||
|     if (!dispatching) { | ||||
|         /* Write ctx->dispatching before reading e.g. bh->scheduled. | ||||
|          * Optimization: this is only needed when we're entering the "unsafe" | ||||
|          * phase where other threads must call event_notifier_set. | ||||
|          */ | ||||
|         smp_mb(); | ||||
|     if (ctx->notify_me) { | ||||
|         event_notifier_set(&ctx->notifier); | ||||
|         atomic_mb_set(&ctx->notified, true); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void aio_notify_accept(AioContext *ctx) | ||||
| void aio_notify(AioContext *ctx) | ||||
| { | ||||
|     if (atomic_xchg(&ctx->notified, false)) { | ||||
|         event_notifier_test_and_clear(&ctx->notifier); | ||||
|     /* Write e.g. bh->scheduled before reading ctx->dispatching.  */ | ||||
|     smp_mb(); | ||||
|     if (!ctx->dispatching) { | ||||
|         event_notifier_set(&ctx->notifier); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -307,54 +283,32 @@ static void aio_timerlist_notify(void *opaque) | ||||
|  | ||||
| static void aio_rfifolock_cb(void *opaque) | ||||
| { | ||||
|     AioContext *ctx = opaque; | ||||
|  | ||||
|     /* Kick owner thread in case they are blocked in aio_poll() */ | ||||
|     qemu_bh_schedule(ctx->notify_dummy_bh); | ||||
| } | ||||
|  | ||||
| static void notify_dummy_bh(void *opaque) | ||||
| { | ||||
|     /* Do nothing, we were invoked just to force the event loop to iterate */ | ||||
| } | ||||
|  | ||||
| static void event_notifier_dummy_cb(EventNotifier *e) | ||||
| { | ||||
|     aio_notify(opaque); | ||||
| } | ||||
|  | ||||
| AioContext *aio_context_new(Error **errp) | ||||
| { | ||||
|     int ret; | ||||
|     AioContext *ctx; | ||||
|     Error *local_err = NULL; | ||||
|  | ||||
|     ctx = (AioContext *) g_source_new(&aio_source_funcs, sizeof(AioContext)); | ||||
|     aio_context_setup(ctx, &local_err); | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         goto fail; | ||||
|     } | ||||
|     ret = event_notifier_init(&ctx->notifier, false); | ||||
|     if (ret < 0) { | ||||
|         g_source_destroy(&ctx->source); | ||||
|         error_setg_errno(errp, -ret, "Failed to initialize event notifier"); | ||||
|         goto fail; | ||||
|         return NULL; | ||||
|     } | ||||
|     g_source_set_can_recurse(&ctx->source, true); | ||||
|     aio_set_event_notifier(ctx, &ctx->notifier, | ||||
|                            false, | ||||
|                            (EventNotifierHandler *) | ||||
|                            event_notifier_dummy_cb); | ||||
|                            event_notifier_test_and_clear); | ||||
|     ctx->pollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD)); | ||||
|     ctx->thread_pool = NULL; | ||||
|     qemu_mutex_init(&ctx->bh_lock); | ||||
|     rfifolock_init(&ctx->lock, aio_rfifolock_cb, ctx); | ||||
|     timerlistgroup_init(&ctx->tlg, aio_timerlist_notify, ctx); | ||||
|  | ||||
|     ctx->notify_dummy_bh = aio_bh_new(ctx, notify_dummy_bh, NULL); | ||||
|  | ||||
|     return ctx; | ||||
| fail: | ||||
|     g_source_destroy(&ctx->source); | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| void aio_context_ref(AioContext *ctx) | ||||
|   | ||||
| @@ -5,9 +5,13 @@ common-obj-$(CONFIG_SPICE) += spiceaudio.o | ||||
| common-obj-$(CONFIG_COREAUDIO) += coreaudio.o | ||||
| common-obj-$(CONFIG_ALSA) += alsaaudio.o | ||||
| common-obj-$(CONFIG_DSOUND) += dsoundaudio.o | ||||
| common-obj-$(CONFIG_FMOD) += fmodaudio.o | ||||
| common-obj-$(CONFIG_ESD) += esdaudio.o | ||||
| common-obj-$(CONFIG_PA) += paaudio.o | ||||
| common-obj-$(CONFIG_WINWAVE) += winwaveaudio.o | ||||
| common-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o | ||||
| common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o | ||||
| common-obj-y += wavcapture.o | ||||
|  | ||||
| $(obj)/audio.o $(obj)/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS) | ||||
| sdlaudio.o-cflags := $(SDL_CFLAGS) | ||||
|   | ||||
| @@ -21,12 +21,10 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include <alsa/asoundlib.h> | ||||
| #include "qemu-common.h" | ||||
| #include "qemu/main-loop.h" | ||||
| #include "audio.h" | ||||
| #include "trace.h" | ||||
|  | ||||
| #if QEMU_GNUC_PREREQ(4, 3) | ||||
| #pragma GCC diagnostic ignored "-Waddress" | ||||
| @@ -35,28 +33,9 @@ | ||||
| #define AUDIO_CAP "alsa" | ||||
| #include "audio_int.h" | ||||
|  | ||||
| typedef struct ALSAConf { | ||||
|     int size_in_usec_in; | ||||
|     int size_in_usec_out; | ||||
|     const char *pcm_name_in; | ||||
|     const char *pcm_name_out; | ||||
|     unsigned int buffer_size_in; | ||||
|     unsigned int period_size_in; | ||||
|     unsigned int buffer_size_out; | ||||
|     unsigned int period_size_out; | ||||
|     unsigned int threshold; | ||||
|  | ||||
|     int buffer_size_in_overridden; | ||||
|     int period_size_in_overridden; | ||||
|  | ||||
|     int buffer_size_out_overridden; | ||||
|     int period_size_out_overridden; | ||||
| } ALSAConf; | ||||
|  | ||||
| struct pollhlp { | ||||
|     snd_pcm_t *handle; | ||||
|     struct pollfd *pfds; | ||||
|     ALSAConf *conf; | ||||
|     int count; | ||||
|     int mask; | ||||
| }; | ||||
| @@ -77,6 +56,30 @@ typedef struct ALSAVoiceIn { | ||||
|     struct pollhlp pollhlp; | ||||
| } ALSAVoiceIn; | ||||
|  | ||||
| static struct { | ||||
|     int size_in_usec_in; | ||||
|     int size_in_usec_out; | ||||
|     const char *pcm_name_in; | ||||
|     const char *pcm_name_out; | ||||
|     unsigned int buffer_size_in; | ||||
|     unsigned int period_size_in; | ||||
|     unsigned int buffer_size_out; | ||||
|     unsigned int period_size_out; | ||||
|     unsigned int threshold; | ||||
|  | ||||
|     int buffer_size_in_overridden; | ||||
|     int period_size_in_overridden; | ||||
|  | ||||
|     int buffer_size_out_overridden; | ||||
|     int period_size_out_overridden; | ||||
|     int verbose; | ||||
| } conf = { | ||||
|     .buffer_size_out = 4096, | ||||
|     .period_size_out = 1024, | ||||
|     .pcm_name_out = "default", | ||||
|     .pcm_name_in = "default", | ||||
| }; | ||||
|  | ||||
| struct alsa_params_req { | ||||
|     int freq; | ||||
|     snd_pcm_format_t fmt; | ||||
| @@ -202,7 +205,9 @@ static void alsa_poll_handler (void *opaque) | ||||
|     } | ||||
|  | ||||
|     if (!(revents & hlp->mask)) { | ||||
|         trace_alsa_revents(revents); | ||||
|         if (conf.verbose) { | ||||
|             dolog ("revents = %d\n", revents); | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -261,14 +266,31 @@ static int alsa_poll_helper (snd_pcm_t *handle, struct pollhlp *hlp, int mask) | ||||
|  | ||||
|     for (i = 0; i < count; ++i) { | ||||
|         if (pfds[i].events & POLLIN) { | ||||
|             qemu_set_fd_handler (pfds[i].fd, alsa_poll_handler, NULL, hlp); | ||||
|             err = qemu_set_fd_handler (pfds[i].fd, alsa_poll_handler, | ||||
|                                        NULL, hlp); | ||||
|         } | ||||
|         if (pfds[i].events & POLLOUT) { | ||||
|             trace_alsa_pollout(i, pfds[i].fd); | ||||
|             qemu_set_fd_handler (pfds[i].fd, NULL, alsa_poll_handler, hlp); | ||||
|             if (conf.verbose) { | ||||
|                 dolog ("POLLOUT %d %d\n", i, pfds[i].fd); | ||||
|             } | ||||
|             err = qemu_set_fd_handler (pfds[i].fd, NULL, | ||||
|                                        alsa_poll_handler, hlp); | ||||
|         } | ||||
|         if (conf.verbose) { | ||||
|             dolog ("Set handler events=%#x index=%d fd=%d err=%d\n", | ||||
|                    pfds[i].events, i, pfds[i].fd, err); | ||||
|         } | ||||
|         trace_alsa_set_handler(pfds[i].events, i, pfds[i].fd, err); | ||||
|  | ||||
|         if (err) { | ||||
|             dolog ("Failed to set handler events=%#x index=%d fd=%d err=%d\n", | ||||
|                    pfds[i].events, i, pfds[i].fd, err); | ||||
|  | ||||
|             while (i--) { | ||||
|                 qemu_set_fd_handler (pfds[i].fd, NULL, NULL, NULL); | ||||
|             } | ||||
|             g_free (pfds); | ||||
|             return -1; | ||||
|         } | ||||
|     } | ||||
|     hlp->pfds = pfds; | ||||
|     hlp->count = count; | ||||
| @@ -454,15 +476,14 @@ static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold) | ||||
| } | ||||
|  | ||||
| static int alsa_open (int in, struct alsa_params_req *req, | ||||
|                       struct alsa_params_obt *obt, snd_pcm_t **handlep, | ||||
|                       ALSAConf *conf) | ||||
|                       struct alsa_params_obt *obt, snd_pcm_t **handlep) | ||||
| { | ||||
|     snd_pcm_t *handle; | ||||
|     snd_pcm_hw_params_t *hw_params; | ||||
|     int err; | ||||
|     int size_in_usec; | ||||
|     unsigned int freq, nchannels; | ||||
|     const char *pcm_name = in ? conf->pcm_name_in : conf->pcm_name_out; | ||||
|     const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out; | ||||
|     snd_pcm_uframes_t obt_buffer_size; | ||||
|     const char *typ = in ? "ADC" : "DAC"; | ||||
|     snd_pcm_format_t obtfmt; | ||||
| @@ -501,7 +522,7 @@ static int alsa_open (int in, struct alsa_params_req *req, | ||||
|     } | ||||
|  | ||||
|     err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt); | ||||
|     if (err < 0) { | ||||
|     if (err < 0 && conf.verbose) { | ||||
|         alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt); | ||||
|     } | ||||
|  | ||||
| @@ -633,7 +654,7 @@ static int alsa_open (int in, struct alsa_params_req *req, | ||||
|         goto err; | ||||
|     } | ||||
|  | ||||
|     if (!in && conf->threshold) { | ||||
|     if (!in && conf.threshold) { | ||||
|         snd_pcm_uframes_t threshold; | ||||
|         int bytes_per_sec; | ||||
|  | ||||
| @@ -655,7 +676,7 @@ static int alsa_open (int in, struct alsa_params_req *req, | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         threshold = (conf->threshold * bytes_per_sec) / 1000; | ||||
|         threshold = (conf.threshold * bytes_per_sec) / 1000; | ||||
|         alsa_set_threshold (handle, threshold); | ||||
|     } | ||||
|  | ||||
| @@ -665,9 +686,10 @@ static int alsa_open (int in, struct alsa_params_req *req, | ||||
|  | ||||
|     *handlep = handle; | ||||
|  | ||||
|     if (obtfmt != req->fmt || | ||||
|     if (conf.verbose && | ||||
|         (obtfmt != req->fmt || | ||||
|          obt->nchannels != req->nchannels || | ||||
|          obt->freq != req->freq) { | ||||
|          obt->freq != req->freq)) { | ||||
|         dolog ("Audio parameters for %s\n", typ); | ||||
|         alsa_dump_info (req, obt, obtfmt); | ||||
|     } | ||||
| @@ -721,7 +743,9 @@ static void alsa_write_pending (ALSAVoiceOut *alsa) | ||||
|             if (written <= 0) { | ||||
|                 switch (written) { | ||||
|                 case 0: | ||||
|                     trace_alsa_wrote_zero(len); | ||||
|                     if (conf.verbose) { | ||||
|                         dolog ("Failed to write %d frames (wrote zero)\n", len); | ||||
|                     } | ||||
|                     return; | ||||
|  | ||||
|                 case -EPIPE: | ||||
| @@ -730,7 +754,9 @@ static void alsa_write_pending (ALSAVoiceOut *alsa) | ||||
|                                      len); | ||||
|                         return; | ||||
|                     } | ||||
|                     trace_alsa_xrun_out(); | ||||
|                     if (conf.verbose) { | ||||
|                         dolog ("Recovering from playback xrun\n"); | ||||
|                     } | ||||
|                     continue; | ||||
|  | ||||
|                 case -ESTRPIPE: | ||||
| @@ -741,7 +767,9 @@ static void alsa_write_pending (ALSAVoiceOut *alsa) | ||||
|                                      len); | ||||
|                         return; | ||||
|                     } | ||||
|                     trace_alsa_resume_out(); | ||||
|                     if (conf.verbose) { | ||||
|                         dolog ("Resuming suspended output stream\n"); | ||||
|                     } | ||||
|                     continue; | ||||
|  | ||||
|                 case -EAGAIN: | ||||
| @@ -791,27 +819,25 @@ static void alsa_fini_out (HWVoiceOut *hw) | ||||
|     alsa->pcm_buf = NULL; | ||||
| } | ||||
|  | ||||
| static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|                          void *drv_opaque) | ||||
| static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
| { | ||||
|     ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; | ||||
|     struct alsa_params_req req; | ||||
|     struct alsa_params_obt obt; | ||||
|     snd_pcm_t *handle; | ||||
|     struct audsettings obt_as; | ||||
|     ALSAConf *conf = drv_opaque; | ||||
|  | ||||
|     req.fmt = aud_to_alsafmt (as->fmt, as->endianness); | ||||
|     req.freq = as->freq; | ||||
|     req.nchannels = as->nchannels; | ||||
|     req.period_size = conf->period_size_out; | ||||
|     req.buffer_size = conf->buffer_size_out; | ||||
|     req.size_in_usec = conf->size_in_usec_out; | ||||
|     req.period_size = conf.period_size_out; | ||||
|     req.buffer_size = conf.buffer_size_out; | ||||
|     req.size_in_usec = conf.size_in_usec_out; | ||||
|     req.override_mask = | ||||
|         (conf->period_size_out_overridden ? 1 : 0) | | ||||
|         (conf->buffer_size_out_overridden ? 2 : 0); | ||||
|         (conf.period_size_out_overridden ? 1 : 0) | | ||||
|         (conf.buffer_size_out_overridden ? 2 : 0); | ||||
|  | ||||
|     if (alsa_open (0, &req, &obt, &handle, conf)) { | ||||
|     if (alsa_open (0, &req, &obt, &handle)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| @@ -832,7 +858,6 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     } | ||||
|  | ||||
|     alsa->handle = handle; | ||||
|     alsa->pollhlp.conf = conf; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @@ -903,26 +928,25 @@ static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | ||||
| static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as) | ||||
| { | ||||
|     ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; | ||||
|     struct alsa_params_req req; | ||||
|     struct alsa_params_obt obt; | ||||
|     snd_pcm_t *handle; | ||||
|     struct audsettings obt_as; | ||||
|     ALSAConf *conf = drv_opaque; | ||||
|  | ||||
|     req.fmt = aud_to_alsafmt (as->fmt, as->endianness); | ||||
|     req.freq = as->freq; | ||||
|     req.nchannels = as->nchannels; | ||||
|     req.period_size = conf->period_size_in; | ||||
|     req.buffer_size = conf->buffer_size_in; | ||||
|     req.size_in_usec = conf->size_in_usec_in; | ||||
|     req.period_size = conf.period_size_in; | ||||
|     req.buffer_size = conf.buffer_size_in; | ||||
|     req.size_in_usec = conf.size_in_usec_in; | ||||
|     req.override_mask = | ||||
|         (conf->period_size_in_overridden ? 1 : 0) | | ||||
|         (conf->buffer_size_in_overridden ? 2 : 0); | ||||
|         (conf.period_size_in_overridden ? 1 : 0) | | ||||
|         (conf.buffer_size_in_overridden ? 2 : 0); | ||||
|  | ||||
|     if (alsa_open (1, &req, &obt, &handle, conf)) { | ||||
|     if (alsa_open (1, &req, &obt, &handle)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| @@ -943,7 +967,6 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | ||||
|     } | ||||
|  | ||||
|     alsa->handle = handle; | ||||
|     alsa->pollhlp.conf = conf; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @@ -999,10 +1022,14 @@ static int alsa_run_in (HWVoiceIn *hw) | ||||
|                 dolog ("Failed to resume suspended input stream\n"); | ||||
|                 return 0; | ||||
|             } | ||||
|             trace_alsa_resume_in(); | ||||
|             if (conf.verbose) { | ||||
|                 dolog ("Resuming suspended input stream\n"); | ||||
|             } | ||||
|             break; | ||||
|         default: | ||||
|             trace_alsa_no_frames(state); | ||||
|             if (conf.verbose) { | ||||
|                 dolog ("No frames available and ALSA state is %d\n", state); | ||||
|             } | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
| @@ -1037,7 +1064,9 @@ static int alsa_run_in (HWVoiceIn *hw) | ||||
|             if (nread <= 0) { | ||||
|                 switch (nread) { | ||||
|                 case 0: | ||||
|                     trace_alsa_read_zero(len); | ||||
|                     if (conf.verbose) { | ||||
|                         dolog ("Failed to read %ld frames (read zero)\n", len); | ||||
|                     } | ||||
|                     goto exit; | ||||
|  | ||||
|                 case -EPIPE: | ||||
| @@ -1045,7 +1074,9 @@ static int alsa_run_in (HWVoiceIn *hw) | ||||
|                         alsa_logerr (nread, "Failed to read %ld frames\n", len); | ||||
|                         goto exit; | ||||
|                     } | ||||
|                     trace_alsa_xrun_in(); | ||||
|                     if (conf.verbose) { | ||||
|                         dolog ("Recovering from capture xrun\n"); | ||||
|                     } | ||||
|                     continue; | ||||
|  | ||||
|                 case -EAGAIN: | ||||
| @@ -1117,85 +1148,82 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static ALSAConf glob_conf = { | ||||
|     .buffer_size_out = 4096, | ||||
|     .period_size_out = 1024, | ||||
|     .pcm_name_out = "default", | ||||
|     .pcm_name_in = "default", | ||||
| }; | ||||
|  | ||||
| static void *alsa_audio_init (void) | ||||
| { | ||||
|     ALSAConf *conf = g_malloc(sizeof(ALSAConf)); | ||||
|     *conf = glob_conf; | ||||
|     return conf; | ||||
|     return &conf; | ||||
| } | ||||
|  | ||||
| static void alsa_audio_fini (void *opaque) | ||||
| { | ||||
|     g_free(opaque); | ||||
|     (void) opaque; | ||||
| } | ||||
|  | ||||
| static struct audio_option alsa_options[] = { | ||||
|     { | ||||
|         .name        = "DAC_SIZE_IN_USEC", | ||||
|         .tag         = AUD_OPT_BOOL, | ||||
|         .valp        = &glob_conf.size_in_usec_out, | ||||
|         .valp        = &conf.size_in_usec_out, | ||||
|         .descr       = "DAC period/buffer size in microseconds (otherwise in frames)" | ||||
|     }, | ||||
|     { | ||||
|         .name        = "DAC_PERIOD_SIZE", | ||||
|         .tag         = AUD_OPT_INT, | ||||
|         .valp        = &glob_conf.period_size_out, | ||||
|         .valp        = &conf.period_size_out, | ||||
|         .descr       = "DAC period size (0 to go with system default)", | ||||
|         .overriddenp = &glob_conf.period_size_out_overridden | ||||
|         .overriddenp = &conf.period_size_out_overridden | ||||
|     }, | ||||
|     { | ||||
|         .name        = "DAC_BUFFER_SIZE", | ||||
|         .tag         = AUD_OPT_INT, | ||||
|         .valp        = &glob_conf.buffer_size_out, | ||||
|         .valp        = &conf.buffer_size_out, | ||||
|         .descr       = "DAC buffer size (0 to go with system default)", | ||||
|         .overriddenp = &glob_conf.buffer_size_out_overridden | ||||
|         .overriddenp = &conf.buffer_size_out_overridden | ||||
|     }, | ||||
|     { | ||||
|         .name        = "ADC_SIZE_IN_USEC", | ||||
|         .tag         = AUD_OPT_BOOL, | ||||
|         .valp        = &glob_conf.size_in_usec_in, | ||||
|         .valp        = &conf.size_in_usec_in, | ||||
|         .descr       = | ||||
|         "ADC period/buffer size in microseconds (otherwise in frames)" | ||||
|     }, | ||||
|     { | ||||
|         .name        = "ADC_PERIOD_SIZE", | ||||
|         .tag         = AUD_OPT_INT, | ||||
|         .valp        = &glob_conf.period_size_in, | ||||
|         .valp        = &conf.period_size_in, | ||||
|         .descr       = "ADC period size (0 to go with system default)", | ||||
|         .overriddenp = &glob_conf.period_size_in_overridden | ||||
|         .overriddenp = &conf.period_size_in_overridden | ||||
|     }, | ||||
|     { | ||||
|         .name        = "ADC_BUFFER_SIZE", | ||||
|         .tag         = AUD_OPT_INT, | ||||
|         .valp        = &glob_conf.buffer_size_in, | ||||
|         .valp        = &conf.buffer_size_in, | ||||
|         .descr       = "ADC buffer size (0 to go with system default)", | ||||
|         .overriddenp = &glob_conf.buffer_size_in_overridden | ||||
|         .overriddenp = &conf.buffer_size_in_overridden | ||||
|     }, | ||||
|     { | ||||
|         .name        = "THRESHOLD", | ||||
|         .tag         = AUD_OPT_INT, | ||||
|         .valp        = &glob_conf.threshold, | ||||
|         .valp        = &conf.threshold, | ||||
|         .descr       = "(undocumented)" | ||||
|     }, | ||||
|     { | ||||
|         .name        = "DAC_DEV", | ||||
|         .tag         = AUD_OPT_STR, | ||||
|         .valp        = &glob_conf.pcm_name_out, | ||||
|         .valp        = &conf.pcm_name_out, | ||||
|         .descr       = "DAC device name (for instance dmix)" | ||||
|     }, | ||||
|     { | ||||
|         .name        = "ADC_DEV", | ||||
|         .tag         = AUD_OPT_STR, | ||||
|         .valp        = &glob_conf.pcm_name_in, | ||||
|         .valp        = &conf.pcm_name_in, | ||||
|         .descr       = "ADC device name" | ||||
|     }, | ||||
|     { | ||||
|         .name        = "VERBOSE", | ||||
|         .tag         = AUD_OPT_BOOL, | ||||
|         .valp        = &conf.verbose, | ||||
|         .descr       = "Behave in a more verbose way" | ||||
|     }, | ||||
|     { /* End of list */ } | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -21,7 +21,6 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "hw/hw.h" | ||||
| #include "audio.h" | ||||
| #include "monitor/monitor.h" | ||||
| @@ -31,6 +30,7 @@ | ||||
| #define AUDIO_CAP "audio" | ||||
| #include "audio_int.h" | ||||
|  | ||||
| /* #define DEBUG_PLIVE */ | ||||
| /* #define DEBUG_LIVE */ | ||||
| /* #define DEBUG_OUT */ | ||||
| /* #define DEBUG_CAPTURE */ | ||||
| @@ -66,6 +66,8 @@ static struct { | ||||
|         int hertz; | ||||
|         int64_t ticks; | ||||
|     } period; | ||||
|     int plive; | ||||
|     int log_to_monitor; | ||||
|     int try_poll_in; | ||||
|     int try_poll_out; | ||||
| } conf = { | ||||
| @@ -94,6 +96,8 @@ static struct { | ||||
|     }, | ||||
|  | ||||
|     .period = { .hertz = 100 }, | ||||
|     .plive = 0, | ||||
|     .log_to_monitor = 0, | ||||
|     .try_poll_in = 1, | ||||
|     .try_poll_out = 1, | ||||
| }; | ||||
| @@ -327,12 +331,21 @@ static const char *audio_get_conf_str (const char *key, | ||||
|  | ||||
| void AUD_vlog (const char *cap, const char *fmt, va_list ap) | ||||
| { | ||||
|     if (conf.log_to_monitor) { | ||||
|         if (cap) { | ||||
|             monitor_printf(default_mon, "%s: ", cap); | ||||
|         } | ||||
|  | ||||
|         monitor_vprintf(default_mon, fmt, ap); | ||||
|     } | ||||
|     else { | ||||
|         if (cap) { | ||||
|             fprintf (stderr, "%s: ", cap); | ||||
|         } | ||||
|  | ||||
|         vfprintf (stderr, fmt, ap); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AUD_log (const char *cap, const char *fmt, ...) | ||||
| { | ||||
| @@ -1441,6 +1454,9 @@ static void audio_run_out (AudioState *s) | ||||
|             while (sw) { | ||||
|                 sw1 = sw->entries.le_next; | ||||
|                 if (!sw->active && !sw->callback.fn) { | ||||
| #ifdef DEBUG_PLIVE | ||||
|                     dolog ("Finishing with old voice\n"); | ||||
| #endif | ||||
|                     audio_close_out (sw); | ||||
|                 } | ||||
|                 sw = sw1; | ||||
| @@ -1632,6 +1648,18 @@ static struct audio_option audio_options[] = { | ||||
|         .valp  = &conf.period.hertz, | ||||
|         .descr = "Timer period in HZ (0 - use lowest possible)" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "PLIVE", | ||||
|         .tag   = AUD_OPT_BOOL, | ||||
|         .valp  = &conf.plive, | ||||
|         .descr = "(undocumented)" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "LOG_TO_MONITOR", | ||||
|         .tag   = AUD_OPT_BOOL, | ||||
|         .valp  = &conf.log_to_monitor, | ||||
|         .descr = "Print logging messages to monitor instead of stderr" | ||||
|     }, | ||||
|     { /* End of list */ } | ||||
| }; | ||||
|  | ||||
| @@ -1807,6 +1835,9 @@ static void audio_init (void) | ||||
|     atexit (audio_atexit); | ||||
|  | ||||
|     s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s); | ||||
|     if (!s->ts) { | ||||
|         hw_error("Could not create audio timer\n"); | ||||
|     } | ||||
|  | ||||
|     audio_process_options ("AUDIO", audio_options); | ||||
|  | ||||
| @@ -1857,9 +1888,13 @@ static void audio_init (void) | ||||
|  | ||||
|     if (!done) { | ||||
|         done = !audio_driver_init (s, &no_audio_driver); | ||||
|         assert(done); | ||||
|         if (!done) { | ||||
|             hw_error("Could not initialize audio subsystem\n"); | ||||
|         } | ||||
|         else { | ||||
|             dolog ("warning: Using timer based audio emulation\n"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (conf.period.hertz <= 0) { | ||||
|         if (conf.period.hertz < 0) { | ||||
|   | ||||
| @@ -24,6 +24,7 @@ | ||||
| #ifndef QEMU_AUDIO_H | ||||
| #define QEMU_AUDIO_H | ||||
|  | ||||
| #include "config-host.h" | ||||
| #include "qemu/queue.h" | ||||
|  | ||||
| typedef void (*audio_callback_fn) (void *opaque, int avail); | ||||
|   | ||||
| @@ -156,13 +156,13 @@ struct audio_driver { | ||||
| }; | ||||
|  | ||||
| struct audio_pcm_ops { | ||||
|     int  (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque); | ||||
|     int  (*init_out)(HWVoiceOut *hw, struct audsettings *as); | ||||
|     void (*fini_out)(HWVoiceOut *hw); | ||||
|     int  (*run_out) (HWVoiceOut *hw, int live); | ||||
|     int  (*write)   (SWVoiceOut *sw, void *buf, int size); | ||||
|     int  (*ctl_out) (HWVoiceOut *hw, int cmd, ...); | ||||
|  | ||||
|     int  (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque); | ||||
|     int  (*init_in) (HWVoiceIn *hw, struct audsettings *as); | ||||
|     void (*fini_in) (HWVoiceIn *hw); | ||||
|     int  (*run_in)  (HWVoiceIn *hw); | ||||
|     int  (*read)    (SWVoiceIn *sw, void *buf, int size); | ||||
| @@ -206,11 +206,14 @@ extern struct audio_driver no_audio_driver; | ||||
| extern struct audio_driver oss_audio_driver; | ||||
| extern struct audio_driver sdl_audio_driver; | ||||
| extern struct audio_driver wav_audio_driver; | ||||
| extern struct audio_driver fmod_audio_driver; | ||||
| extern struct audio_driver alsa_audio_driver; | ||||
| extern struct audio_driver coreaudio_audio_driver; | ||||
| extern struct audio_driver dsound_audio_driver; | ||||
| extern struct audio_driver esd_audio_driver; | ||||
| extern struct audio_driver pa_audio_driver; | ||||
| extern struct audio_driver spice_audio_driver; | ||||
| extern struct audio_driver winwave_audio_driver; | ||||
| extern const struct mixeng_volume nominal_volume; | ||||
|  | ||||
| void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as); | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "audio.h" | ||||
|  | ||||
|   | ||||
| @@ -262,7 +262,7 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as) | ||||
| #ifdef DAC | ||||
|     QLIST_INIT (&hw->cap_head); | ||||
| #endif | ||||
|     if (glue (hw->pcm_ops->init_, TYPE) (hw, as, s->drv_opaque)) { | ||||
|     if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) { | ||||
|         goto err0; | ||||
|     } | ||||
|  | ||||
| @@ -398,6 +398,10 @@ SW *glue (AUD_open_, TYPE) ( | ||||
|     ) | ||||
| { | ||||
|     AudioState *s = &glob_audio_state; | ||||
| #ifdef DAC | ||||
|     int live = 0; | ||||
|     SW *old_sw = NULL; | ||||
| #endif | ||||
|  | ||||
|     if (audio_bug (AUDIO_FUNC, !card || !name || !callback_fn || !as)) { | ||||
|         dolog ("card=%p name=%p callback_fn=%p as=%p\n", | ||||
| @@ -422,6 +426,29 @@ SW *glue (AUD_open_, TYPE) ( | ||||
|         return sw; | ||||
|     } | ||||
|  | ||||
| #ifdef DAC | ||||
|     if (conf.plive && sw && (!sw->active && !sw->empty)) { | ||||
|         live = sw->total_hw_samples_mixed; | ||||
|  | ||||
| #ifdef DEBUG_PLIVE | ||||
|         dolog ("Replacing voice %s with %d live samples\n", SW_NAME (sw), live); | ||||
|         dolog ("Old %s freq %d, bits %d, channels %d\n", | ||||
|                SW_NAME (sw), sw->info.freq, sw->info.bits, sw->info.nchannels); | ||||
|         dolog ("New %s freq %d, bits %d, channels %d\n", | ||||
|                name, | ||||
|                as->freq, | ||||
|                (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) ? 16 : 8, | ||||
|                as->nchannels); | ||||
| #endif | ||||
|  | ||||
|         if (live) { | ||||
|             old_sw = sw; | ||||
|             old_sw->callback.fn = NULL; | ||||
|             sw = NULL; | ||||
|         } | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     if (!glue (conf.fixed_, TYPE).enabled && sw) { | ||||
|         glue (AUD_close_, TYPE) (card, sw); | ||||
|         sw = NULL; | ||||
| @@ -454,6 +481,20 @@ SW *glue (AUD_open_, TYPE) ( | ||||
|     sw->callback.fn = callback_fn; | ||||
|     sw->callback.opaque = callback_opaque; | ||||
|  | ||||
| #ifdef DAC | ||||
|     if (live) { | ||||
|         int mixed = | ||||
|             (live << old_sw->info.shift) | ||||
|             * old_sw->info.bytes_per_second | ||||
|             / sw->info.bytes_per_second; | ||||
|  | ||||
| #ifdef DEBUG_PLIVE | ||||
|         dolog ("Silence will be mixed %d\n", mixed); | ||||
| #endif | ||||
|         sw->total_hw_samples_mixed += mixed; | ||||
|     } | ||||
| #endif | ||||
|  | ||||
| #ifdef DEBUG_AUDIO | ||||
|     dolog ("%s\n", name); | ||||
|     audio_pcm_print_info ("hw", &sw->hw->info); | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| /* public domain */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
|  | ||||
| #define AUDIO_CAP "win-int" | ||||
|   | ||||
| @@ -22,8 +22,8 @@ | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include <CoreAudio/CoreAudio.h> | ||||
| #include <string.h>             /* strerror */ | ||||
| #include <pthread.h>            /* pthread_X */ | ||||
|  | ||||
| #include "qemu-common.h" | ||||
| @@ -32,250 +32,28 @@ | ||||
| #define AUDIO_CAP "coreaudio" | ||||
| #include "audio_int.h" | ||||
|  | ||||
| #ifndef MAC_OS_X_VERSION_10_6 | ||||
| #define MAC_OS_X_VERSION_10_6 1060 | ||||
| #endif | ||||
|  | ||||
| static int isAtexit; | ||||
|  | ||||
| typedef struct { | ||||
| struct { | ||||
|     int buffer_frames; | ||||
|     int nbuffers; | ||||
| } CoreaudioConf; | ||||
|     int isAtexit; | ||||
| } conf = { | ||||
|     .buffer_frames = 512, | ||||
|     .nbuffers = 4, | ||||
|     .isAtexit = 0 | ||||
| }; | ||||
|  | ||||
| typedef struct coreaudioVoiceOut { | ||||
|     HWVoiceOut hw; | ||||
|     pthread_mutex_t mutex; | ||||
|     int isAtexit; | ||||
|     AudioDeviceID outputDeviceID; | ||||
|     UInt32 audioDevicePropertyBufferFrameSize; | ||||
|     AudioStreamBasicDescription outputStreamBasicDescription; | ||||
|     AudioDeviceIOProcID ioprocid; | ||||
|     int live; | ||||
|     int decr; | ||||
|     int rpos; | ||||
| } coreaudioVoiceOut; | ||||
|  | ||||
| #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 | ||||
| /* The APIs used here only become available from 10.6 */ | ||||
|  | ||||
| static OSStatus coreaudio_get_voice(AudioDeviceID *id) | ||||
| { | ||||
|     UInt32 size = sizeof(*id); | ||||
|     AudioObjectPropertyAddress addr = { | ||||
|         kAudioHardwarePropertyDefaultOutputDevice, | ||||
|         kAudioObjectPropertyScopeGlobal, | ||||
|         kAudioObjectPropertyElementMaster | ||||
|     }; | ||||
|  | ||||
|     return AudioObjectGetPropertyData(kAudioObjectSystemObject, | ||||
|                                       &addr, | ||||
|                                       0, | ||||
|                                       NULL, | ||||
|                                       &size, | ||||
|                                       id); | ||||
| } | ||||
|  | ||||
| static OSStatus coreaudio_get_framesizerange(AudioDeviceID id, | ||||
|                                              AudioValueRange *framerange) | ||||
| { | ||||
|     UInt32 size = sizeof(*framerange); | ||||
|     AudioObjectPropertyAddress addr = { | ||||
|         kAudioDevicePropertyBufferFrameSizeRange, | ||||
|         kAudioDevicePropertyScopeOutput, | ||||
|         kAudioObjectPropertyElementMaster | ||||
|     }; | ||||
|  | ||||
|     return AudioObjectGetPropertyData(id, | ||||
|                                       &addr, | ||||
|                                       0, | ||||
|                                       NULL, | ||||
|                                       &size, | ||||
|                                       framerange); | ||||
| } | ||||
|  | ||||
| static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize) | ||||
| { | ||||
|     UInt32 size = sizeof(*framesize); | ||||
|     AudioObjectPropertyAddress addr = { | ||||
|         kAudioDevicePropertyBufferFrameSize, | ||||
|         kAudioDevicePropertyScopeOutput, | ||||
|         kAudioObjectPropertyElementMaster | ||||
|     }; | ||||
|  | ||||
|     return AudioObjectGetPropertyData(id, | ||||
|                                       &addr, | ||||
|                                       0, | ||||
|                                       NULL, | ||||
|                                       &size, | ||||
|                                       framesize); | ||||
| } | ||||
|  | ||||
| static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize) | ||||
| { | ||||
|     UInt32 size = sizeof(*framesize); | ||||
|     AudioObjectPropertyAddress addr = { | ||||
|         kAudioDevicePropertyBufferFrameSize, | ||||
|         kAudioDevicePropertyScopeOutput, | ||||
|         kAudioObjectPropertyElementMaster | ||||
|     }; | ||||
|  | ||||
|     return AudioObjectSetPropertyData(id, | ||||
|                                       &addr, | ||||
|                                       0, | ||||
|                                       NULL, | ||||
|                                       size, | ||||
|                                       framesize); | ||||
| } | ||||
|  | ||||
| static OSStatus coreaudio_get_streamformat(AudioDeviceID id, | ||||
|                                            AudioStreamBasicDescription *d) | ||||
| { | ||||
|     UInt32 size = sizeof(*d); | ||||
|     AudioObjectPropertyAddress addr = { | ||||
|         kAudioDevicePropertyStreamFormat, | ||||
|         kAudioDevicePropertyScopeOutput, | ||||
|         kAudioObjectPropertyElementMaster | ||||
|     }; | ||||
|  | ||||
|     return AudioObjectGetPropertyData(id, | ||||
|                                       &addr, | ||||
|                                       0, | ||||
|                                       NULL, | ||||
|                                       &size, | ||||
|                                       d); | ||||
| } | ||||
|  | ||||
| static OSStatus coreaudio_set_streamformat(AudioDeviceID id, | ||||
|                                            AudioStreamBasicDescription *d) | ||||
| { | ||||
|     UInt32 size = sizeof(*d); | ||||
|     AudioObjectPropertyAddress addr = { | ||||
|         kAudioDevicePropertyStreamFormat, | ||||
|         kAudioDevicePropertyScopeOutput, | ||||
|         kAudioObjectPropertyElementMaster | ||||
|     }; | ||||
|  | ||||
|     return AudioObjectSetPropertyData(id, | ||||
|                                       &addr, | ||||
|                                       0, | ||||
|                                       NULL, | ||||
|                                       size, | ||||
|                                       d); | ||||
| } | ||||
|  | ||||
| static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result) | ||||
| { | ||||
|     UInt32 size = sizeof(*result); | ||||
|     AudioObjectPropertyAddress addr = { | ||||
|         kAudioDevicePropertyDeviceIsRunning, | ||||
|         kAudioDevicePropertyScopeOutput, | ||||
|         kAudioObjectPropertyElementMaster | ||||
|     }; | ||||
|  | ||||
|     return AudioObjectGetPropertyData(id, | ||||
|                                       &addr, | ||||
|                                       0, | ||||
|                                       NULL, | ||||
|                                       &size, | ||||
|                                       result); | ||||
| } | ||||
| #else | ||||
| /* Legacy versions of functions using deprecated APIs */ | ||||
|  | ||||
| static OSStatus coreaudio_get_voice(AudioDeviceID *id) | ||||
| { | ||||
|     UInt32 size = sizeof(*id); | ||||
|  | ||||
|     return AudioHardwareGetProperty( | ||||
|         kAudioHardwarePropertyDefaultOutputDevice, | ||||
|         &size, | ||||
|         id); | ||||
| } | ||||
|  | ||||
| static OSStatus coreaudio_get_framesizerange(AudioDeviceID id, | ||||
|                                              AudioValueRange *framerange) | ||||
| { | ||||
|     UInt32 size = sizeof(*framerange); | ||||
|  | ||||
|     return AudioDeviceGetProperty( | ||||
|         id, | ||||
|         0, | ||||
|         0, | ||||
|         kAudioDevicePropertyBufferFrameSizeRange, | ||||
|         &size, | ||||
|         framerange); | ||||
| } | ||||
|  | ||||
| static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize) | ||||
| { | ||||
|     UInt32 size = sizeof(*framesize); | ||||
|  | ||||
|     return AudioDeviceGetProperty( | ||||
|         id, | ||||
|         0, | ||||
|         false, | ||||
|         kAudioDevicePropertyBufferFrameSize, | ||||
|         &size, | ||||
|         framesize); | ||||
| } | ||||
|  | ||||
| static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize) | ||||
| { | ||||
|     UInt32 size = sizeof(*framesize); | ||||
|  | ||||
|     return AudioDeviceSetProperty( | ||||
|         id, | ||||
|         NULL, | ||||
|         0, | ||||
|         false, | ||||
|         kAudioDevicePropertyBufferFrameSize, | ||||
|         size, | ||||
|         framesize); | ||||
| } | ||||
|  | ||||
| static OSStatus coreaudio_get_streamformat(AudioDeviceID id, | ||||
|                                            AudioStreamBasicDescription *d) | ||||
| { | ||||
|     UInt32 size = sizeof(*d); | ||||
|  | ||||
|     return AudioDeviceGetProperty( | ||||
|         id, | ||||
|         0, | ||||
|         false, | ||||
|         kAudioDevicePropertyStreamFormat, | ||||
|         &size, | ||||
|         d); | ||||
| } | ||||
|  | ||||
| static OSStatus coreaudio_set_streamformat(AudioDeviceID id, | ||||
|                                            AudioStreamBasicDescription *d) | ||||
| { | ||||
|     UInt32 size = sizeof(*d); | ||||
|  | ||||
|     return AudioDeviceSetProperty( | ||||
|         id, | ||||
|         0, | ||||
|         0, | ||||
|         0, | ||||
|         kAudioDevicePropertyStreamFormat, | ||||
|         size, | ||||
|         d); | ||||
| } | ||||
|  | ||||
| static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result) | ||||
| { | ||||
|     UInt32 size = sizeof(*result); | ||||
|  | ||||
|     return AudioDeviceGetProperty( | ||||
|         id, | ||||
|         0, | ||||
|         0, | ||||
|         kAudioDevicePropertyDeviceIsRunning, | ||||
|         &size, | ||||
|         result); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static void coreaudio_logstatus (OSStatus status) | ||||
| { | ||||
|     const char *str = "BUG"; | ||||
| @@ -370,7 +148,10 @@ static inline UInt32 isPlaying (AudioDeviceID outputDeviceID) | ||||
| { | ||||
|     OSStatus status; | ||||
|     UInt32 result = 0; | ||||
|     status = coreaudio_get_isrunning(outputDeviceID, &result); | ||||
|     UInt32 propertySize = sizeof(outputDeviceID); | ||||
|     status = AudioDeviceGetProperty( | ||||
|         outputDeviceID, 0, 0, | ||||
|         kAudioDevicePropertyDeviceIsRunning, &propertySize, &result); | ||||
|     if (status != kAudioHardwareNoError) { | ||||
|         coreaudio_logerr(status, | ||||
|                          "Could not determine whether Device is playing\n"); | ||||
| @@ -380,7 +161,7 @@ static inline UInt32 isPlaying (AudioDeviceID outputDeviceID) | ||||
|  | ||||
| static void coreaudio_atexit (void) | ||||
| { | ||||
|     isAtexit = 1; | ||||
|     conf.isAtexit = 1; | ||||
| } | ||||
|  | ||||
| static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name) | ||||
| @@ -506,15 +287,14 @@ static int coreaudio_write (SWVoiceOut *sw, void *buf, int len) | ||||
|     return audio_pcm_sw_write (sw, buf, len); | ||||
| } | ||||
|  | ||||
| static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|                               void *drv_opaque) | ||||
| static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
| { | ||||
|     OSStatus status; | ||||
|     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; | ||||
|     UInt32 propertySize; | ||||
|     int err; | ||||
|     const char *typ = "playback"; | ||||
|     AudioValueRange frameRange; | ||||
|     CoreaudioConf *conf = drv_opaque; | ||||
|  | ||||
|     /* create mutex */ | ||||
|     err = pthread_mutex_init(&core->mutex, NULL); | ||||
| @@ -525,7 +305,12 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|  | ||||
|     audio_pcm_init_info (&hw->info, as); | ||||
|  | ||||
|     status = coreaudio_get_voice(&core->outputDeviceID); | ||||
|     /* open default output device */ | ||||
|     propertySize = sizeof(core->outputDeviceID); | ||||
|     status = AudioHardwareGetProperty( | ||||
|         kAudioHardwarePropertyDefaultOutputDevice, | ||||
|         &propertySize, | ||||
|         &core->outputDeviceID); | ||||
|     if (status != kAudioHardwareNoError) { | ||||
|         coreaudio_logerr2 (status, typ, | ||||
|                            "Could not get default output Device\n"); | ||||
| @@ -537,7 +322,13 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     } | ||||
|  | ||||
|     /* get minimum and maximum buffer frame sizes */ | ||||
|     status = coreaudio_get_framesizerange(core->outputDeviceID, | ||||
|     propertySize = sizeof(frameRange); | ||||
|     status = AudioDeviceGetProperty( | ||||
|         core->outputDeviceID, | ||||
|         0, | ||||
|         0, | ||||
|         kAudioDevicePropertyBufferFrameSizeRange, | ||||
|         &propertySize, | ||||
|         &frameRange); | ||||
|     if (status != kAudioHardwareNoError) { | ||||
|         coreaudio_logerr2 (status, typ, | ||||
| @@ -545,20 +336,27 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (frameRange.mMinimum > conf->buffer_frames) { | ||||
|     if (frameRange.mMinimum > conf.buffer_frames) { | ||||
|         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; | ||||
|         dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum); | ||||
|     } | ||||
|     else if (frameRange.mMaximum < conf->buffer_frames) { | ||||
|     else if (frameRange.mMaximum < conf.buffer_frames) { | ||||
|         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; | ||||
|         dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); | ||||
|     } | ||||
|     else { | ||||
|         core->audioDevicePropertyBufferFrameSize = conf->buffer_frames; | ||||
|         core->audioDevicePropertyBufferFrameSize = conf.buffer_frames; | ||||
|     } | ||||
|  | ||||
|     /* set Buffer Frame Size */ | ||||
|     status = coreaudio_set_framesize(core->outputDeviceID, | ||||
|     propertySize = sizeof(core->audioDevicePropertyBufferFrameSize); | ||||
|     status = AudioDeviceSetProperty( | ||||
|         core->outputDeviceID, | ||||
|         NULL, | ||||
|         0, | ||||
|         false, | ||||
|         kAudioDevicePropertyBufferFrameSize, | ||||
|         propertySize, | ||||
|         &core->audioDevicePropertyBufferFrameSize); | ||||
|     if (status != kAudioHardwareNoError) { | ||||
|         coreaudio_logerr2 (status, typ, | ||||
| @@ -568,17 +366,29 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     } | ||||
|  | ||||
|     /* get Buffer Frame Size */ | ||||
|     status = coreaudio_get_framesize(core->outputDeviceID, | ||||
|     propertySize = sizeof(core->audioDevicePropertyBufferFrameSize); | ||||
|     status = AudioDeviceGetProperty( | ||||
|         core->outputDeviceID, | ||||
|         0, | ||||
|         false, | ||||
|         kAudioDevicePropertyBufferFrameSize, | ||||
|         &propertySize, | ||||
|         &core->audioDevicePropertyBufferFrameSize); | ||||
|     if (status != kAudioHardwareNoError) { | ||||
|         coreaudio_logerr2 (status, typ, | ||||
|                            "Could not get device buffer frame size\n"); | ||||
|         return -1; | ||||
|     } | ||||
|     hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize; | ||||
|     hw->samples = conf.nbuffers * core->audioDevicePropertyBufferFrameSize; | ||||
|  | ||||
|     /* get StreamFormat */ | ||||
|     status = coreaudio_get_streamformat(core->outputDeviceID, | ||||
|     propertySize = sizeof(core->outputStreamBasicDescription); | ||||
|     status = AudioDeviceGetProperty( | ||||
|         core->outputDeviceID, | ||||
|         0, | ||||
|         false, | ||||
|         kAudioDevicePropertyStreamFormat, | ||||
|         &propertySize, | ||||
|         &core->outputStreamBasicDescription); | ||||
|     if (status != kAudioHardwareNoError) { | ||||
|         coreaudio_logerr2 (status, typ, | ||||
| @@ -589,7 +399,14 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|  | ||||
|     /* set Samplerate */ | ||||
|     core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq; | ||||
|     status = coreaudio_set_streamformat(core->outputDeviceID, | ||||
|     propertySize = sizeof(core->outputStreamBasicDescription); | ||||
|     status = AudioDeviceSetProperty( | ||||
|         core->outputDeviceID, | ||||
|         0, | ||||
|         0, | ||||
|         0, | ||||
|         kAudioDevicePropertyStreamFormat, | ||||
|         propertySize, | ||||
|         &core->outputStreamBasicDescription); | ||||
|     if (status != kAudioHardwareNoError) { | ||||
|         coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n", | ||||
| @@ -599,12 +416,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     } | ||||
|  | ||||
|     /* set Callback */ | ||||
|     core->ioprocid = NULL; | ||||
|     status = AudioDeviceCreateIOProcID(core->outputDeviceID, | ||||
|                                        audioDeviceIOProc, | ||||
|                                        hw, | ||||
|                                        &core->ioprocid); | ||||
|     if (status != kAudioHardwareNoError || core->ioprocid == NULL) { | ||||
|     status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw); | ||||
|     if (status != kAudioHardwareNoError) { | ||||
|         coreaudio_logerr2 (status, typ, "Could not set IOProc\n"); | ||||
|         core->outputDeviceID = kAudioDeviceUnknown; | ||||
|         return -1; | ||||
| @@ -612,10 +425,10 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|  | ||||
|     /* start Playback */ | ||||
|     if (!isPlaying(core->outputDeviceID)) { | ||||
|         status = AudioDeviceStart(core->outputDeviceID, core->ioprocid); | ||||
|         status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc); | ||||
|         if (status != kAudioHardwareNoError) { | ||||
|             coreaudio_logerr2 (status, typ, "Could not start playback\n"); | ||||
|             AudioDeviceDestroyIOProcID(core->outputDeviceID, core->ioprocid); | ||||
|             AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc); | ||||
|             core->outputDeviceID = kAudioDeviceUnknown; | ||||
|             return -1; | ||||
|         } | ||||
| @@ -630,18 +443,18 @@ static void coreaudio_fini_out (HWVoiceOut *hw) | ||||
|     int err; | ||||
|     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; | ||||
|  | ||||
|     if (!isAtexit) { | ||||
|     if (!conf.isAtexit) { | ||||
|         /* stop playback */ | ||||
|         if (isPlaying(core->outputDeviceID)) { | ||||
|             status = AudioDeviceStop(core->outputDeviceID, core->ioprocid); | ||||
|             status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); | ||||
|             if (status != kAudioHardwareNoError) { | ||||
|                 coreaudio_logerr (status, "Could not stop playback\n"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /* remove callback */ | ||||
|         status = AudioDeviceDestroyIOProcID(core->outputDeviceID, | ||||
|                                             core->ioprocid); | ||||
|         status = AudioDeviceRemoveIOProc(core->outputDeviceID, | ||||
|                                          audioDeviceIOProc); | ||||
|         if (status != kAudioHardwareNoError) { | ||||
|             coreaudio_logerr (status, "Could not remove IOProc\n"); | ||||
|         } | ||||
| @@ -664,7 +477,7 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
|     case VOICE_ENABLE: | ||||
|         /* start playback */ | ||||
|         if (!isPlaying(core->outputDeviceID)) { | ||||
|             status = AudioDeviceStart(core->outputDeviceID, core->ioprocid); | ||||
|             status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc); | ||||
|             if (status != kAudioHardwareNoError) { | ||||
|                 coreaudio_logerr (status, "Could not resume playback\n"); | ||||
|             } | ||||
| @@ -673,10 +486,9 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
|  | ||||
|     case VOICE_DISABLE: | ||||
|         /* stop playback */ | ||||
|         if (!isAtexit) { | ||||
|         if (!conf.isAtexit) { | ||||
|             if (isPlaying(core->outputDeviceID)) { | ||||
|                 status = AudioDeviceStop(core->outputDeviceID, | ||||
|                                          core->ioprocid); | ||||
|                 status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); | ||||
|                 if (status != kAudioHardwareNoError) { | ||||
|                     coreaudio_logerr (status, "Could not pause playback\n"); | ||||
|                 } | ||||
| @@ -687,36 +499,28 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static CoreaudioConf glob_conf = { | ||||
|     .buffer_frames = 512, | ||||
|     .nbuffers = 4, | ||||
| }; | ||||
|  | ||||
| static void *coreaudio_audio_init (void) | ||||
| { | ||||
|     CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf)); | ||||
|     *conf = glob_conf; | ||||
|  | ||||
|     atexit(coreaudio_atexit); | ||||
|     return conf; | ||||
|     return &coreaudio_audio_init; | ||||
| } | ||||
|  | ||||
| static void coreaudio_audio_fini (void *opaque) | ||||
| { | ||||
|     g_free(opaque); | ||||
|     (void) opaque; | ||||
| } | ||||
|  | ||||
| static struct audio_option coreaudio_options[] = { | ||||
|     { | ||||
|         .name  = "BUFFER_SIZE", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.buffer_frames, | ||||
|         .valp  = &conf.buffer_frames, | ||||
|         .descr = "Size of the buffer in frames" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "BUFFER_COUNT", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.nbuffers, | ||||
|         .valp  = &conf.nbuffers, | ||||
|         .descr = "Number of buffers" | ||||
|     }, | ||||
|     { /* End of list */ } | ||||
|   | ||||
| @@ -67,11 +67,11 @@ static int glue (dsound_lock_, TYPE) ( | ||||
|     LPVOID *p2p, | ||||
|     DWORD *blen1p, | ||||
|     DWORD *blen2p, | ||||
|     int entire, | ||||
|     dsound *s | ||||
|     int entire | ||||
|     ) | ||||
| { | ||||
|     HRESULT hr; | ||||
|     int i; | ||||
|     LPVOID p1 = NULL, p2 = NULL; | ||||
|     DWORD blen1 = 0, blen2 = 0; | ||||
|     DWORD flag; | ||||
| @@ -81,21 +81,40 @@ static int glue (dsound_lock_, TYPE) ( | ||||
| #else | ||||
|     flag = entire ? DSBLOCK_ENTIREBUFFER : 0; | ||||
| #endif | ||||
|     hr = glue(IFACE, _Lock)(buf, pos, len, &p1, &blen1, &p2, &blen2, flag); | ||||
|     for (i = 0; i < conf.lock_retries; ++i) { | ||||
|         hr = glue (IFACE, _Lock) ( | ||||
|             buf, | ||||
|             pos, | ||||
|             len, | ||||
|             &p1, | ||||
|             &blen1, | ||||
|             &p2, | ||||
|             &blen2, | ||||
|             flag | ||||
|             ); | ||||
|  | ||||
|         if (FAILED (hr)) { | ||||
| #ifndef DSBTYPE_IN | ||||
|             if (hr == DSERR_BUFFERLOST) { | ||||
|             if (glue (dsound_restore_, TYPE) (buf, s)) { | ||||
|                 if (glue (dsound_restore_, TYPE) (buf)) { | ||||
|                     dsound_logerr (hr, "Could not lock " NAME "\n"); | ||||
|             } | ||||
|                     goto fail; | ||||
|                 } | ||||
|                 continue; | ||||
|             } | ||||
| #endif | ||||
|             dsound_logerr (hr, "Could not lock " NAME "\n"); | ||||
|             goto fail; | ||||
|         } | ||||
|  | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     if (i == conf.lock_retries) { | ||||
|         dolog ("%d attempts to lock " NAME " failed\n", i); | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) { | ||||
|         dolog ("DirectSound returned misaligned buffer %ld %ld\n", | ||||
|                blen1, blen2); | ||||
| @@ -155,19 +174,16 @@ static void dsound_fini_out (HWVoiceOut *hw) | ||||
| } | ||||
|  | ||||
| #ifdef DSBTYPE_IN | ||||
| static int dsound_init_in(HWVoiceIn *hw, struct audsettings *as, | ||||
|                           void *drv_opaque) | ||||
| static int dsound_init_in (HWVoiceIn *hw, struct audsettings *as) | ||||
| #else | ||||
| static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|                            void *drv_opaque) | ||||
| static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
| #endif | ||||
| { | ||||
|     int err; | ||||
|     HRESULT hr; | ||||
|     dsound *s = drv_opaque; | ||||
|     dsound *s = &glob_dsound; | ||||
|     WAVEFORMATEX wfx; | ||||
|     struct audsettings obt_as; | ||||
|     DSoundConf *conf = &s->conf; | ||||
| #ifdef DSBTYPE_IN | ||||
|     const char *typ = "ADC"; | ||||
|     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; | ||||
| @@ -194,7 +210,7 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     bd.dwSize = sizeof (bd); | ||||
|     bd.lpwfxFormat = &wfx; | ||||
| #ifdef DSBTYPE_IN | ||||
|     bd.dwBufferBytes = conf->bufsize_in; | ||||
|     bd.dwBufferBytes = conf.bufsize_in; | ||||
|     hr = IDirectSoundCapture_CreateCaptureBuffer ( | ||||
|         s->dsound_capture, | ||||
|         &bd, | ||||
| @@ -203,7 +219,7 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|         ); | ||||
| #else | ||||
|     bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; | ||||
|     bd.dwBufferBytes = conf->bufsize_out; | ||||
|     bd.dwBufferBytes = conf.bufsize_out; | ||||
|     hr = IDirectSound_CreateSoundBuffer ( | ||||
|         s->dsound, | ||||
|         &bd, | ||||
| @@ -253,7 +269,6 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|             ); | ||||
|     } | ||||
|     hw->samples = bc.dwBufferBytes >> hw->info.shift; | ||||
|     ds->s = s; | ||||
|  | ||||
| #ifdef DEBUG_DSOUND | ||||
|     dolog ("caps %ld, desc %ld\n", | ||||
|   | ||||
| @@ -26,7 +26,6 @@ | ||||
|  * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "audio.h" | ||||
|  | ||||
| @@ -42,25 +41,42 @@ | ||||
|  | ||||
| /* #define DEBUG_DSOUND */ | ||||
|  | ||||
| typedef struct { | ||||
| static struct { | ||||
|     int lock_retries; | ||||
|     int restore_retries; | ||||
|     int getstatus_retries; | ||||
|     int set_primary; | ||||
|     int bufsize_in; | ||||
|     int bufsize_out; | ||||
|     struct audsettings settings; | ||||
|     int latency_millis; | ||||
| } DSoundConf; | ||||
| } conf = { | ||||
|     .lock_retries       = 1, | ||||
|     .restore_retries    = 1, | ||||
|     .getstatus_retries  = 1, | ||||
|     .set_primary        = 0, | ||||
|     .bufsize_in         = 16384, | ||||
|     .bufsize_out        = 16384, | ||||
|     .settings.freq      = 44100, | ||||
|     .settings.nchannels = 2, | ||||
|     .settings.fmt       = AUD_FMT_S16, | ||||
|     .latency_millis     = 10 | ||||
| }; | ||||
|  | ||||
| typedef struct { | ||||
|     LPDIRECTSOUND dsound; | ||||
|     LPDIRECTSOUNDCAPTURE dsound_capture; | ||||
|     LPDIRECTSOUNDBUFFER dsound_primary_buffer; | ||||
|     struct audsettings settings; | ||||
|     DSoundConf conf; | ||||
| } dsound; | ||||
|  | ||||
| static dsound glob_dsound; | ||||
|  | ||||
| typedef struct { | ||||
|     HWVoiceOut hw; | ||||
|     LPDIRECTSOUNDBUFFER dsound_buffer; | ||||
|     DWORD old_pos; | ||||
|     int first_time; | ||||
|     dsound *s; | ||||
| #ifdef DEBUG_DSOUND | ||||
|     DWORD old_ppos; | ||||
|     DWORD played; | ||||
| @@ -72,7 +88,6 @@ typedef struct { | ||||
|     HWVoiceIn hw; | ||||
|     int first_time; | ||||
|     LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer; | ||||
|     dsound *s; | ||||
| } DSoundVoiceIn; | ||||
|  | ||||
| static void dsound_log_hresult (HRESULT hr) | ||||
| @@ -266,17 +281,29 @@ static void print_wave_format (WAVEFORMATEX *wfx) | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb, dsound *s) | ||||
| static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb) | ||||
| { | ||||
|     HRESULT hr; | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < conf.restore_retries; ++i) { | ||||
|         hr = IDirectSoundBuffer_Restore (dsb); | ||||
|  | ||||
|     if (hr != DS_OK) { | ||||
|         switch (hr) { | ||||
|         case DS_OK: | ||||
|             return 0; | ||||
|  | ||||
|         case DSERR_BUFFERLOST: | ||||
|             continue; | ||||
|  | ||||
|         default: | ||||
|             dsound_logerr (hr, "Could not restore playback buffer\n"); | ||||
|             return -1; | ||||
|         } | ||||
|     return 0; | ||||
|     } | ||||
|  | ||||
|     dolog ("%d attempts to restore playback buffer failed\n", i); | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| #include "dsound_template.h" | ||||
| @@ -284,11 +311,12 @@ static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb, dsound *s) | ||||
| #include "dsound_template.h" | ||||
| #undef DSBTYPE_IN | ||||
|  | ||||
| static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp, | ||||
|                                   dsound *s) | ||||
| static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp) | ||||
| { | ||||
|     HRESULT hr; | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < conf.getstatus_retries; ++i) { | ||||
|         hr = IDirectSoundBuffer_GetStatus (dsb, statusp); | ||||
|         if (FAILED (hr)) { | ||||
|             dsound_logerr (hr, "Could not get playback buffer status\n"); | ||||
| @@ -296,9 +324,13 @@ static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp, | ||||
|         } | ||||
|  | ||||
|         if (*statusp & DSERR_BUFFERLOST) { | ||||
|         dsound_restore_out(dsb, s); | ||||
|             if (dsound_restore_out (dsb)) { | ||||
|                 return -1; | ||||
|             } | ||||
|             continue; | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| @@ -344,8 +376,7 @@ static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) | ||||
|     hw->rpos = pos % hw->samples; | ||||
| } | ||||
|  | ||||
| static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb, | ||||
|                                  dsound *s) | ||||
| static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb) | ||||
| { | ||||
|     int err; | ||||
|     LPVOID p1, p2; | ||||
| @@ -358,8 +389,7 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb, | ||||
|         hw->samples << hw->info.shift, | ||||
|         &p1, &p2, | ||||
|         &blen1, &blen2, | ||||
|         1, | ||||
|         s | ||||
|         1 | ||||
|         ); | ||||
|     if (err) { | ||||
|         return; | ||||
| @@ -385,9 +415,25 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb, | ||||
|     dsound_unlock_out (dsb, p1, p2, blen1, blen2); | ||||
| } | ||||
|  | ||||
| static int dsound_open (dsound *s) | ||||
| static void dsound_close (dsound *s) | ||||
| { | ||||
|     HRESULT hr; | ||||
|  | ||||
|     if (s->dsound_primary_buffer) { | ||||
|         hr = IDirectSoundBuffer_Release (s->dsound_primary_buffer); | ||||
|         if (FAILED (hr)) { | ||||
|             dsound_logerr (hr, "Could not release primary buffer\n"); | ||||
|         } | ||||
|         s->dsound_primary_buffer = NULL; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int dsound_open (dsound *s) | ||||
| { | ||||
|     int err; | ||||
|     HRESULT hr; | ||||
|     WAVEFORMATEX wfx; | ||||
|     DSBUFFERDESC dsbd; | ||||
|     HWND hwnd; | ||||
|  | ||||
|     hwnd = GetForegroundWindow (); | ||||
| @@ -403,16 +449,71 @@ static int dsound_open (dsound *s) | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (!conf.set_primary) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     err = waveformat_from_audio_settings (&wfx, &conf.settings); | ||||
|     if (err) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     memset (&dsbd, 0, sizeof (dsbd)); | ||||
|     dsbd.dwSize = sizeof (dsbd); | ||||
|     dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; | ||||
|     dsbd.dwBufferBytes = 0; | ||||
|     dsbd.lpwfxFormat = NULL; | ||||
|  | ||||
|     hr = IDirectSound_CreateSoundBuffer ( | ||||
|         s->dsound, | ||||
|         &dsbd, | ||||
|         &s->dsound_primary_buffer, | ||||
|         NULL | ||||
|         ); | ||||
|     if (FAILED (hr)) { | ||||
|         dsound_logerr (hr, "Could not create primary playback buffer\n"); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     hr = IDirectSoundBuffer_SetFormat (s->dsound_primary_buffer, &wfx); | ||||
|     if (FAILED (hr)) { | ||||
|         dsound_logerr (hr, "Could not set primary playback buffer format\n"); | ||||
|     } | ||||
|  | ||||
|     hr = IDirectSoundBuffer_GetFormat ( | ||||
|         s->dsound_primary_buffer, | ||||
|         &wfx, | ||||
|         sizeof (wfx), | ||||
|         NULL | ||||
|         ); | ||||
|     if (FAILED (hr)) { | ||||
|         dsound_logerr (hr, "Could not get primary playback buffer format\n"); | ||||
|         goto fail0; | ||||
|     } | ||||
|  | ||||
| #ifdef DEBUG_DSOUND | ||||
|     dolog ("Primary\n"); | ||||
|     print_wave_format (&wfx); | ||||
| #endif | ||||
|  | ||||
|     err = waveformat_to_audio_settings (&wfx, &s->settings); | ||||
|     if (err) { | ||||
|         goto fail0; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
|  | ||||
|  fail0: | ||||
|     dsound_close (s); | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
| { | ||||
|     HRESULT hr; | ||||
|     DWORD status; | ||||
|     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; | ||||
|     LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; | ||||
|     dsound *s = ds->s; | ||||
|  | ||||
|     if (!dsb) { | ||||
|         dolog ("Attempt to control voice without a buffer\n"); | ||||
| @@ -421,7 +522,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
|  | ||||
|     switch (cmd) { | ||||
|     case VOICE_ENABLE: | ||||
|         if (dsound_get_status_out (dsb, &status, s)) { | ||||
|         if (dsound_get_status_out (dsb, &status)) { | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
| @@ -430,7 +531,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
|         dsound_clear_sample (hw, dsb, s); | ||||
|         dsound_clear_sample (hw, dsb); | ||||
|  | ||||
|         hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING); | ||||
|         if (FAILED (hr)) { | ||||
| @@ -440,7 +541,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
|         break; | ||||
|  | ||||
|     case VOICE_DISABLE: | ||||
|         if (dsound_get_status_out (dsb, &status, s)) { | ||||
|         if (dsound_get_status_out (dsb, &status)) { | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
| @@ -477,8 +578,6 @@ static int dsound_run_out (HWVoiceOut *hw, int live) | ||||
|     DWORD wpos, ppos, old_pos; | ||||
|     LPVOID p1, p2; | ||||
|     int bufsize; | ||||
|     dsound *s = ds->s; | ||||
|     DSoundConf *conf = &s->conf; | ||||
|  | ||||
|     if (!dsb) { | ||||
|         dolog ("Attempt to run empty with playback buffer\n"); | ||||
| @@ -501,14 +600,14 @@ static int dsound_run_out (HWVoiceOut *hw, int live) | ||||
|     len = live << hwshift; | ||||
|  | ||||
|     if (ds->first_time) { | ||||
|         if (conf->latency_millis) { | ||||
|         if (conf.latency_millis) { | ||||
|             DWORD cur_blat; | ||||
|  | ||||
|             cur_blat = audio_ring_dist (wpos, ppos, bufsize); | ||||
|             ds->first_time = 0; | ||||
|             old_pos = wpos; | ||||
|             old_pos += | ||||
|                 millis_to_bytes (&hw->info, conf->latency_millis) - cur_blat; | ||||
|                 millis_to_bytes (&hw->info, conf.latency_millis) - cur_blat; | ||||
|             old_pos %= bufsize; | ||||
|             old_pos &= ~hw->info.align; | ||||
|         } | ||||
| @@ -564,8 +663,7 @@ static int dsound_run_out (HWVoiceOut *hw, int live) | ||||
|         len, | ||||
|         &p1, &p2, | ||||
|         &blen1, &blen2, | ||||
|         0, | ||||
|         s | ||||
|         0 | ||||
|         ); | ||||
|     if (err) { | ||||
|         return 0; | ||||
| @@ -668,7 +766,6 @@ static int dsound_run_in (HWVoiceIn *hw) | ||||
|     DWORD cpos, rpos; | ||||
|     LPVOID p1, p2; | ||||
|     int hwshift; | ||||
|     dsound *s = ds->s; | ||||
|  | ||||
|     if (!dscb) { | ||||
|         dolog ("Attempt to run without capture buffer\n"); | ||||
| @@ -723,8 +820,7 @@ static int dsound_run_in (HWVoiceIn *hw) | ||||
|         &p2, | ||||
|         &blen1, | ||||
|         &blen2, | ||||
|         0, | ||||
|         s | ||||
|         0 | ||||
|         ); | ||||
|     if (err) { | ||||
|         return 0; | ||||
| @@ -747,19 +843,12 @@ static int dsound_run_in (HWVoiceIn *hw) | ||||
|     return decr; | ||||
| } | ||||
|  | ||||
| static DSoundConf glob_conf = { | ||||
|     .bufsize_in         = 16384, | ||||
|     .bufsize_out        = 16384, | ||||
|     .latency_millis     = 10 | ||||
| }; | ||||
|  | ||||
| static void dsound_audio_fini (void *opaque) | ||||
| { | ||||
|     HRESULT hr; | ||||
|     dsound *s = opaque; | ||||
|  | ||||
|     if (!s->dsound) { | ||||
|         g_free(s); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -770,7 +859,6 @@ static void dsound_audio_fini (void *opaque) | ||||
|     s->dsound = NULL; | ||||
|  | ||||
|     if (!s->dsound_capture) { | ||||
|         g_free(s); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -779,21 +867,17 @@ static void dsound_audio_fini (void *opaque) | ||||
|         dsound_logerr (hr, "Could not release DirectSoundCapture\n"); | ||||
|     } | ||||
|     s->dsound_capture = NULL; | ||||
|  | ||||
|     g_free(s); | ||||
| } | ||||
|  | ||||
| static void *dsound_audio_init (void) | ||||
| { | ||||
|     int err; | ||||
|     HRESULT hr; | ||||
|     dsound *s = g_malloc0(sizeof(dsound)); | ||||
|     dsound *s = &glob_dsound; | ||||
|  | ||||
|     s->conf = glob_conf; | ||||
|     hr = CoInitialize (NULL); | ||||
|     if (FAILED (hr)) { | ||||
|         dsound_logerr (hr, "Could not initialize COM\n"); | ||||
|         g_free(s); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
| @@ -806,7 +890,6 @@ static void *dsound_audio_init (void) | ||||
|         ); | ||||
|     if (FAILED (hr)) { | ||||
|         dsound_logerr (hr, "Could not create DirectSound instance\n"); | ||||
|         g_free(s); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
| @@ -818,7 +901,7 @@ static void *dsound_audio_init (void) | ||||
|         if (FAILED (hr)) { | ||||
|             dsound_logerr (hr, "Could not release DirectSound\n"); | ||||
|         } | ||||
|         g_free(s); | ||||
|         s->dsound = NULL; | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
| @@ -855,22 +938,64 @@ static void *dsound_audio_init (void) | ||||
| } | ||||
|  | ||||
| static struct audio_option dsound_options[] = { | ||||
|     { | ||||
|         .name  = "LOCK_RETRIES", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &conf.lock_retries, | ||||
|         .descr = "Number of times to attempt locking the buffer" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "RESTOURE_RETRIES", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &conf.restore_retries, | ||||
|         .descr = "Number of times to attempt restoring the buffer" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "GETSTATUS_RETRIES", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &conf.getstatus_retries, | ||||
|         .descr = "Number of times to attempt getting status of the buffer" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "SET_PRIMARY", | ||||
|         .tag   = AUD_OPT_BOOL, | ||||
|         .valp  = &conf.set_primary, | ||||
|         .descr = "Set the parameters of primary buffer" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "LATENCY_MILLIS", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.latency_millis, | ||||
|         .valp  = &conf.latency_millis, | ||||
|         .descr = "(undocumented)" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "PRIMARY_FREQ", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &conf.settings.freq, | ||||
|         .descr = "Primary buffer frequency" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "PRIMARY_CHANNELS", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &conf.settings.nchannels, | ||||
|         .descr = "Primary buffer number of channels (1 - mono, 2 - stereo)" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "PRIMARY_FMT", | ||||
|         .tag   = AUD_OPT_FMT, | ||||
|         .valp  = &conf.settings.fmt, | ||||
|         .descr = "Primary buffer format" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "BUFSIZE_OUT", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.bufsize_out, | ||||
|         .valp  = &conf.bufsize_out, | ||||
|         .descr = "(undocumented)" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "BUFSIZE_IN", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.bufsize_in, | ||||
|         .valp  = &conf.bufsize_in, | ||||
|         .descr = "(undocumented)" | ||||
|     }, | ||||
|     { /* End of list */ } | ||||
|   | ||||
							
								
								
									
										557
									
								
								audio/esdaudio.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										557
									
								
								audio/esdaudio.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,557 @@ | ||||
| /* | ||||
|  * QEMU ESD audio driver | ||||
|  * | ||||
|  * Copyright (c) 2006 Frederick Reeve (brushed up by malc) | ||||
|  * | ||||
|  * 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 <esd.h> | ||||
| #include "qemu-common.h" | ||||
| #include "audio.h" | ||||
|  | ||||
| #define AUDIO_CAP "esd" | ||||
| #include "audio_int.h" | ||||
| #include "audio_pt_int.h" | ||||
|  | ||||
| typedef struct { | ||||
|     HWVoiceOut hw; | ||||
|     int done; | ||||
|     int live; | ||||
|     int decr; | ||||
|     int rpos; | ||||
|     void *pcm_buf; | ||||
|     int fd; | ||||
|     struct audio_pt pt; | ||||
| } ESDVoiceOut; | ||||
|  | ||||
| typedef struct { | ||||
|     HWVoiceIn hw; | ||||
|     int done; | ||||
|     int dead; | ||||
|     int incr; | ||||
|     int wpos; | ||||
|     void *pcm_buf; | ||||
|     int fd; | ||||
|     struct audio_pt pt; | ||||
| } ESDVoiceIn; | ||||
|  | ||||
| static struct { | ||||
|     int samples; | ||||
|     int divisor; | ||||
|     char *dac_host; | ||||
|     char *adc_host; | ||||
| } conf = { | ||||
|     .samples = 1024, | ||||
|     .divisor = 2, | ||||
| }; | ||||
|  | ||||
| static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...) | ||||
| { | ||||
|     va_list ap; | ||||
|  | ||||
|     va_start (ap, fmt); | ||||
|     AUD_vlog (AUDIO_CAP, fmt, ap); | ||||
|     va_end (ap); | ||||
|  | ||||
|     AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err)); | ||||
| } | ||||
|  | ||||
| /* playback */ | ||||
| static void *qesd_thread_out (void *arg) | ||||
| { | ||||
|     ESDVoiceOut *esd = arg; | ||||
|     HWVoiceOut *hw = &esd->hw; | ||||
|     int threshold; | ||||
|  | ||||
|     threshold = conf.divisor ? hw->samples / conf.divisor : 0; | ||||
|  | ||||
|     if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     for (;;) { | ||||
|         int decr, to_mix, rpos; | ||||
|  | ||||
|         for (;;) { | ||||
|             if (esd->done) { | ||||
|                 goto exit; | ||||
|             } | ||||
|  | ||||
|             if (esd->live > threshold) { | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) { | ||||
|                 goto exit; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         decr = to_mix = esd->live; | ||||
|         rpos = hw->rpos; | ||||
|  | ||||
|         if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) { | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         while (to_mix) { | ||||
|             ssize_t written; | ||||
|             int chunk = audio_MIN (to_mix, hw->samples - rpos); | ||||
|             struct st_sample *src = hw->mix_buf + rpos; | ||||
|  | ||||
|             hw->clip (esd->pcm_buf, src, chunk); | ||||
|  | ||||
|         again: | ||||
|             written = write (esd->fd, esd->pcm_buf, chunk << hw->info.shift); | ||||
|             if (written == -1) { | ||||
|                 if (errno == EINTR || errno == EAGAIN) { | ||||
|                     goto again; | ||||
|                 } | ||||
|                 qesd_logerr (errno, "write failed\n"); | ||||
|                 return NULL; | ||||
|             } | ||||
|  | ||||
|             if (written != chunk << hw->info.shift) { | ||||
|                 int wsamples = written >> hw->info.shift; | ||||
|                 int wbytes = wsamples << hw->info.shift; | ||||
|                 if (wbytes != written) { | ||||
|                     dolog ("warning: Misaligned write %d (requested %zd), " | ||||
|                            "alignment %d\n", | ||||
|                            wbytes, written, hw->info.align + 1); | ||||
|                 } | ||||
|                 to_mix -= wsamples; | ||||
|                 rpos = (rpos + wsamples) % hw->samples; | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             rpos = (rpos + chunk) % hw->samples; | ||||
|             to_mix -= chunk; | ||||
|         } | ||||
|  | ||||
|         if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         esd->rpos = rpos; | ||||
|         esd->live -= decr; | ||||
|         esd->decr += decr; | ||||
|     } | ||||
|  | ||||
|  exit: | ||||
|     audio_pt_unlock (&esd->pt, AUDIO_FUNC); | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| static int qesd_run_out (HWVoiceOut *hw, int live) | ||||
| { | ||||
|     int decr; | ||||
|     ESDVoiceOut *esd = (ESDVoiceOut *) hw; | ||||
|  | ||||
|     if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     decr = audio_MIN (live, esd->decr); | ||||
|     esd->decr -= decr; | ||||
|     esd->live = live - decr; | ||||
|     hw->rpos = esd->rpos; | ||||
|     if (esd->live > 0) { | ||||
|         audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC); | ||||
|     } | ||||
|     else { | ||||
|         audio_pt_unlock (&esd->pt, AUDIO_FUNC); | ||||
|     } | ||||
|     return decr; | ||||
| } | ||||
|  | ||||
| static int qesd_write (SWVoiceOut *sw, void *buf, int len) | ||||
| { | ||||
|     return audio_pcm_sw_write (sw, buf, len); | ||||
| } | ||||
|  | ||||
| static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
| { | ||||
|     ESDVoiceOut *esd = (ESDVoiceOut *) hw; | ||||
|     struct audsettings obt_as = *as; | ||||
|     int esdfmt = ESD_STREAM | ESD_PLAY; | ||||
|  | ||||
|     esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO; | ||||
|     switch (as->fmt) { | ||||
|     case AUD_FMT_S8: | ||||
|     case AUD_FMT_U8: | ||||
|         esdfmt |= ESD_BITS8; | ||||
|         obt_as.fmt = AUD_FMT_U8; | ||||
|         break; | ||||
|  | ||||
|     case AUD_FMT_S32: | ||||
|     case AUD_FMT_U32: | ||||
|         dolog ("Will use 16 instead of 32 bit samples\n"); | ||||
|         /* fall through */ | ||||
|     case AUD_FMT_S16: | ||||
|     case AUD_FMT_U16: | ||||
|     deffmt: | ||||
|         esdfmt |= ESD_BITS16; | ||||
|         obt_as.fmt = AUD_FMT_S16; | ||||
|         break; | ||||
|  | ||||
|     default: | ||||
|         dolog ("Internal logic error: Bad audio format %d\n", as->fmt); | ||||
|         goto deffmt; | ||||
|  | ||||
|     } | ||||
|     obt_as.endianness = AUDIO_HOST_ENDIANNESS; | ||||
|  | ||||
|     audio_pcm_init_info (&hw->info, &obt_as); | ||||
|  | ||||
|     hw->samples = conf.samples; | ||||
|     esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); | ||||
|     if (!esd->pcm_buf) { | ||||
|         dolog ("Could not allocate buffer (%d bytes)\n", | ||||
|                hw->samples << hw->info.shift); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL); | ||||
|     if (esd->fd < 0) { | ||||
|         qesd_logerr (errno, "esd_play_stream failed\n"); | ||||
|         goto fail1; | ||||
|     } | ||||
|  | ||||
|     if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) { | ||||
|         goto fail2; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
|  | ||||
|  fail2: | ||||
|     if (close (esd->fd)) { | ||||
|         qesd_logerr (errno, "%s: close on esd socket(%d) failed\n", | ||||
|                      AUDIO_FUNC, esd->fd); | ||||
|     } | ||||
|     esd->fd = -1; | ||||
|  | ||||
|  fail1: | ||||
|     g_free (esd->pcm_buf); | ||||
|     esd->pcm_buf = NULL; | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static void qesd_fini_out (HWVoiceOut *hw) | ||||
| { | ||||
|     void *ret; | ||||
|     ESDVoiceOut *esd = (ESDVoiceOut *) hw; | ||||
|  | ||||
|     audio_pt_lock (&esd->pt, AUDIO_FUNC); | ||||
|     esd->done = 1; | ||||
|     audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC); | ||||
|     audio_pt_join (&esd->pt, &ret, AUDIO_FUNC); | ||||
|  | ||||
|     if (esd->fd >= 0) { | ||||
|         if (close (esd->fd)) { | ||||
|             qesd_logerr (errno, "failed to close esd socket\n"); | ||||
|         } | ||||
|         esd->fd = -1; | ||||
|     } | ||||
|  | ||||
|     audio_pt_fini (&esd->pt, AUDIO_FUNC); | ||||
|  | ||||
|     g_free (esd->pcm_buf); | ||||
|     esd->pcm_buf = NULL; | ||||
| } | ||||
|  | ||||
| static int qesd_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
| { | ||||
|     (void) hw; | ||||
|     (void) cmd; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* capture */ | ||||
| static void *qesd_thread_in (void *arg) | ||||
| { | ||||
|     ESDVoiceIn *esd = arg; | ||||
|     HWVoiceIn *hw = &esd->hw; | ||||
|     int threshold; | ||||
|  | ||||
|     threshold = conf.divisor ? hw->samples / conf.divisor : 0; | ||||
|  | ||||
|     if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     for (;;) { | ||||
|         int incr, to_grab, wpos; | ||||
|  | ||||
|         for (;;) { | ||||
|             if (esd->done) { | ||||
|                 goto exit; | ||||
|             } | ||||
|  | ||||
|             if (esd->dead > threshold) { | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) { | ||||
|                 goto exit; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         incr = to_grab = esd->dead; | ||||
|         wpos = hw->wpos; | ||||
|  | ||||
|         if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) { | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         while (to_grab) { | ||||
|             ssize_t nread; | ||||
|             int chunk = audio_MIN (to_grab, hw->samples - wpos); | ||||
|             void *buf = advance (esd->pcm_buf, wpos); | ||||
|  | ||||
|         again: | ||||
|             nread = read (esd->fd, buf, chunk << hw->info.shift); | ||||
|             if (nread == -1) { | ||||
|                 if (errno == EINTR || errno == EAGAIN) { | ||||
|                     goto again; | ||||
|                 } | ||||
|                 qesd_logerr (errno, "read failed\n"); | ||||
|                 return NULL; | ||||
|             } | ||||
|  | ||||
|             if (nread != chunk << hw->info.shift) { | ||||
|                 int rsamples = nread >> hw->info.shift; | ||||
|                 int rbytes = rsamples << hw->info.shift; | ||||
|                 if (rbytes != nread) { | ||||
|                     dolog ("warning: Misaligned write %d (requested %zd), " | ||||
|                            "alignment %d\n", | ||||
|                            rbytes, nread, hw->info.align + 1); | ||||
|                 } | ||||
|                 to_grab -= rsamples; | ||||
|                 wpos = (wpos + rsamples) % hw->samples; | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift); | ||||
|             wpos = (wpos + chunk) % hw->samples; | ||||
|             to_grab -= chunk; | ||||
|         } | ||||
|  | ||||
|         if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         esd->wpos = wpos; | ||||
|         esd->dead -= incr; | ||||
|         esd->incr += incr; | ||||
|     } | ||||
|  | ||||
|  exit: | ||||
|     audio_pt_unlock (&esd->pt, AUDIO_FUNC); | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| static int qesd_run_in (HWVoiceIn *hw) | ||||
| { | ||||
|     int live, incr, dead; | ||||
|     ESDVoiceIn *esd = (ESDVoiceIn *) hw; | ||||
|  | ||||
|     if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     live = audio_pcm_hw_get_live_in (hw); | ||||
|     dead = hw->samples - live; | ||||
|     incr = audio_MIN (dead, esd->incr); | ||||
|     esd->incr -= incr; | ||||
|     esd->dead = dead - incr; | ||||
|     hw->wpos = esd->wpos; | ||||
|     if (esd->dead > 0) { | ||||
|         audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC); | ||||
|     } | ||||
|     else { | ||||
|         audio_pt_unlock (&esd->pt, AUDIO_FUNC); | ||||
|     } | ||||
|     return incr; | ||||
| } | ||||
|  | ||||
| static int qesd_read (SWVoiceIn *sw, void *buf, int len) | ||||
| { | ||||
|     return audio_pcm_sw_read (sw, buf, len); | ||||
| } | ||||
|  | ||||
| static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as) | ||||
| { | ||||
|     ESDVoiceIn *esd = (ESDVoiceIn *) hw; | ||||
|     struct audsettings obt_as = *as; | ||||
|     int esdfmt = ESD_STREAM | ESD_RECORD; | ||||
|  | ||||
|     esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO; | ||||
|     switch (as->fmt) { | ||||
|     case AUD_FMT_S8: | ||||
|     case AUD_FMT_U8: | ||||
|         esdfmt |= ESD_BITS8; | ||||
|         obt_as.fmt = AUD_FMT_U8; | ||||
|         break; | ||||
|  | ||||
|     case AUD_FMT_S16: | ||||
|     case AUD_FMT_U16: | ||||
|         esdfmt |= ESD_BITS16; | ||||
|         obt_as.fmt = AUD_FMT_S16; | ||||
|         break; | ||||
|  | ||||
|     case AUD_FMT_S32: | ||||
|     case AUD_FMT_U32: | ||||
|         dolog ("Will use 16 instead of 32 bit samples\n"); | ||||
|         esdfmt |= ESD_BITS16; | ||||
|         obt_as.fmt = AUD_FMT_S16; | ||||
|         break; | ||||
|     } | ||||
|     obt_as.endianness = AUDIO_HOST_ENDIANNESS; | ||||
|  | ||||
|     audio_pcm_init_info (&hw->info, &obt_as); | ||||
|  | ||||
|     hw->samples = conf.samples; | ||||
|     esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); | ||||
|     if (!esd->pcm_buf) { | ||||
|         dolog ("Could not allocate buffer (%d bytes)\n", | ||||
|                hw->samples << hw->info.shift); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL); | ||||
|     if (esd->fd < 0) { | ||||
|         qesd_logerr (errno, "esd_record_stream failed\n"); | ||||
|         goto fail1; | ||||
|     } | ||||
|  | ||||
|     if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) { | ||||
|         goto fail2; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
|  | ||||
|  fail2: | ||||
|     if (close (esd->fd)) { | ||||
|         qesd_logerr (errno, "%s: close on esd socket(%d) failed\n", | ||||
|                      AUDIO_FUNC, esd->fd); | ||||
|     } | ||||
|     esd->fd = -1; | ||||
|  | ||||
|  fail1: | ||||
|     g_free (esd->pcm_buf); | ||||
|     esd->pcm_buf = NULL; | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static void qesd_fini_in (HWVoiceIn *hw) | ||||
| { | ||||
|     void *ret; | ||||
|     ESDVoiceIn *esd = (ESDVoiceIn *) hw; | ||||
|  | ||||
|     audio_pt_lock (&esd->pt, AUDIO_FUNC); | ||||
|     esd->done = 1; | ||||
|     audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC); | ||||
|     audio_pt_join (&esd->pt, &ret, AUDIO_FUNC); | ||||
|  | ||||
|     if (esd->fd >= 0) { | ||||
|         if (close (esd->fd)) { | ||||
|             qesd_logerr (errno, "failed to close esd socket\n"); | ||||
|         } | ||||
|         esd->fd = -1; | ||||
|     } | ||||
|  | ||||
|     audio_pt_fini (&esd->pt, AUDIO_FUNC); | ||||
|  | ||||
|     g_free (esd->pcm_buf); | ||||
|     esd->pcm_buf = NULL; | ||||
| } | ||||
|  | ||||
| static int qesd_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||
| { | ||||
|     (void) hw; | ||||
|     (void) cmd; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* common */ | ||||
| static void *qesd_audio_init (void) | ||||
| { | ||||
|     return &conf; | ||||
| } | ||||
|  | ||||
| static void qesd_audio_fini (void *opaque) | ||||
| { | ||||
|     (void) opaque; | ||||
|     ldebug ("esd_fini"); | ||||
| } | ||||
|  | ||||
| struct audio_option qesd_options[] = { | ||||
|     { | ||||
|         .name  = "SAMPLES", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &conf.samples, | ||||
|         .descr = "buffer size in samples" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "DIVISOR", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &conf.divisor, | ||||
|         .descr = "threshold divisor" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "DAC_HOST", | ||||
|         .tag   = AUD_OPT_STR, | ||||
|         .valp  = &conf.dac_host, | ||||
|         .descr = "playback host" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "ADC_HOST", | ||||
|         .tag   = AUD_OPT_STR, | ||||
|         .valp  = &conf.adc_host, | ||||
|         .descr = "capture host" | ||||
|     }, | ||||
|     { /* End of list */ } | ||||
| }; | ||||
|  | ||||
| static struct audio_pcm_ops qesd_pcm_ops = { | ||||
|     .init_out = qesd_init_out, | ||||
|     .fini_out = qesd_fini_out, | ||||
|     .run_out  = qesd_run_out, | ||||
|     .write    = qesd_write, | ||||
|     .ctl_out  = qesd_ctl_out, | ||||
|  | ||||
|     .init_in  = qesd_init_in, | ||||
|     .fini_in  = qesd_fini_in, | ||||
|     .run_in   = qesd_run_in, | ||||
|     .read     = qesd_read, | ||||
|     .ctl_in   = qesd_ctl_in, | ||||
| }; | ||||
|  | ||||
| struct audio_driver esd_audio_driver = { | ||||
|     .name           = "esd", | ||||
|     .descr          = "http://en.wikipedia.org/wiki/Esound", | ||||
|     .options        = qesd_options, | ||||
|     .init           = qesd_audio_init, | ||||
|     .fini           = qesd_audio_fini, | ||||
|     .pcm_ops        = &qesd_pcm_ops, | ||||
|     .can_be_default = 0, | ||||
|     .max_voices_out = INT_MAX, | ||||
|     .max_voices_in  = INT_MAX, | ||||
|     .voice_size_out = sizeof (ESDVoiceOut), | ||||
|     .voice_size_in  = sizeof (ESDVoiceIn) | ||||
| }; | ||||
							
								
								
									
										685
									
								
								audio/fmodaudio.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										685
									
								
								audio/fmodaudio.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,685 @@ | ||||
| /* | ||||
|  * QEMU FMOD audio driver | ||||
|  * | ||||
|  * Copyright (c) 2004-2005 Vassili Karpov (malc) | ||||
|  * | ||||
|  * 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 <fmod.h> | ||||
| #include <fmod_errors.h> | ||||
| #include "qemu-common.h" | ||||
| #include "audio.h" | ||||
|  | ||||
| #define AUDIO_CAP "fmod" | ||||
| #include "audio_int.h" | ||||
|  | ||||
| typedef struct FMODVoiceOut { | ||||
|     HWVoiceOut hw; | ||||
|     unsigned int old_pos; | ||||
|     FSOUND_SAMPLE *fmod_sample; | ||||
|     int channel; | ||||
| } FMODVoiceOut; | ||||
|  | ||||
| typedef struct FMODVoiceIn { | ||||
|     HWVoiceIn hw; | ||||
|     FSOUND_SAMPLE *fmod_sample; | ||||
| } FMODVoiceIn; | ||||
|  | ||||
| static struct { | ||||
|     const char *drvname; | ||||
|     int nb_samples; | ||||
|     int freq; | ||||
|     int nb_channels; | ||||
|     int bufsize; | ||||
|     int broken_adc; | ||||
| } conf = { | ||||
|     .nb_samples  = 2048 * 2, | ||||
|     .freq        = 44100, | ||||
|     .nb_channels = 2, | ||||
| }; | ||||
|  | ||||
| static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...) | ||||
| { | ||||
|     va_list ap; | ||||
|  | ||||
|     va_start (ap, fmt); | ||||
|     AUD_vlog (AUDIO_CAP, fmt, ap); | ||||
|     va_end (ap); | ||||
|  | ||||
|     AUD_log (AUDIO_CAP, "Reason: %s\n", | ||||
|              FMOD_ErrorString (FSOUND_GetError ())); | ||||
| } | ||||
|  | ||||
| static void GCC_FMT_ATTR (2, 3) fmod_logerr2 ( | ||||
|     const char *typ, | ||||
|     const char *fmt, | ||||
|     ... | ||||
|     ) | ||||
| { | ||||
|     va_list ap; | ||||
|  | ||||
|     AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); | ||||
|  | ||||
|     va_start (ap, fmt); | ||||
|     AUD_vlog (AUDIO_CAP, fmt, ap); | ||||
|     va_end (ap); | ||||
|  | ||||
|     AUD_log (AUDIO_CAP, "Reason: %s\n", | ||||
|              FMOD_ErrorString (FSOUND_GetError ())); | ||||
| } | ||||
|  | ||||
| static int fmod_write (SWVoiceOut *sw, void *buf, int len) | ||||
| { | ||||
|     return audio_pcm_sw_write (sw, buf, len); | ||||
| } | ||||
|  | ||||
| static void fmod_clear_sample (FMODVoiceOut *fmd) | ||||
| { | ||||
|     HWVoiceOut *hw = &fmd->hw; | ||||
|     int status; | ||||
|     void *p1 = 0, *p2 = 0; | ||||
|     unsigned int len1 = 0, len2 = 0; | ||||
|  | ||||
|     status = FSOUND_Sample_Lock ( | ||||
|         fmd->fmod_sample, | ||||
|         0, | ||||
|         hw->samples << hw->info.shift, | ||||
|         &p1, | ||||
|         &p2, | ||||
|         &len1, | ||||
|         &len2 | ||||
|         ); | ||||
|  | ||||
|     if (!status) { | ||||
|         fmod_logerr ("Failed to lock sample\n"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if ((len1 & hw->info.align) || (len2 & hw->info.align)) { | ||||
|         dolog ("Lock returned misaligned length %d, %d, alignment %d\n", | ||||
|                len1, len2, hw->info.align + 1); | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     if ((len1 + len2) - (hw->samples << hw->info.shift)) { | ||||
|         dolog ("Lock returned incomplete length %d, %d\n", | ||||
|                len1 + len2, hw->samples << hw->info.shift); | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     audio_pcm_info_clear_buf (&hw->info, p1, hw->samples); | ||||
|  | ||||
|  fail: | ||||
|     status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2); | ||||
|     if (!status) { | ||||
|         fmod_logerr ("Failed to unlock sample\n"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) | ||||
| { | ||||
|     int src_len1 = dst_len; | ||||
|     int src_len2 = 0; | ||||
|     int pos = hw->rpos + dst_len; | ||||
|     struct st_sample *src1 = hw->mix_buf + hw->rpos; | ||||
|     struct st_sample *src2 = NULL; | ||||
|  | ||||
|     if (pos > hw->samples) { | ||||
|         src_len1 = hw->samples - hw->rpos; | ||||
|         src2 = hw->mix_buf; | ||||
|         src_len2 = dst_len - src_len1; | ||||
|         pos = src_len2; | ||||
|     } | ||||
|  | ||||
|     if (src_len1) { | ||||
|         hw->clip (dst, src1, src_len1); | ||||
|     } | ||||
|  | ||||
|     if (src_len2) { | ||||
|         dst = advance (dst, src_len1 << hw->info.shift); | ||||
|         hw->clip (dst, src2, src_len2); | ||||
|     } | ||||
|  | ||||
|     hw->rpos = pos % hw->samples; | ||||
| } | ||||
|  | ||||
| static int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2, | ||||
|                                unsigned int blen1, unsigned int blen2) | ||||
| { | ||||
|     int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2); | ||||
|     if (!status) { | ||||
|         fmod_logerr ("Failed to unlock sample\n"); | ||||
|         return -1; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int fmod_lock_sample ( | ||||
|     FSOUND_SAMPLE *sample, | ||||
|     struct audio_pcm_info *info, | ||||
|     int pos, | ||||
|     int len, | ||||
|     void **p1, | ||||
|     void **p2, | ||||
|     unsigned int *blen1, | ||||
|     unsigned int *blen2 | ||||
|     ) | ||||
| { | ||||
|     int status; | ||||
|  | ||||
|     status = FSOUND_Sample_Lock ( | ||||
|         sample, | ||||
|         pos << info->shift, | ||||
|         len << info->shift, | ||||
|         p1, | ||||
|         p2, | ||||
|         blen1, | ||||
|         blen2 | ||||
|         ); | ||||
|  | ||||
|     if (!status) { | ||||
|         fmod_logerr ("Failed to lock sample\n"); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if ((*blen1 & info->align) || (*blen2 & info->align)) { | ||||
|         dolog ("Lock returned misaligned length %d, %d, alignment %d\n", | ||||
|                *blen1, *blen2, info->align + 1); | ||||
|  | ||||
|         fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2); | ||||
|  | ||||
|         *p1 = NULL - 1; | ||||
|         *p2 = NULL - 1; | ||||
|         *blen1 = ~0U; | ||||
|         *blen2 = ~0U; | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (!*p1 && *blen1) { | ||||
|         dolog ("warning: !p1 && blen1=%d\n", *blen1); | ||||
|         *blen1 = 0; | ||||
|     } | ||||
|  | ||||
|     if (!p2 && *blen2) { | ||||
|         dolog ("warning: !p2 && blen2=%d\n", *blen2); | ||||
|         *blen2 = 0; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int fmod_run_out (HWVoiceOut *hw, int live) | ||||
| { | ||||
|     FMODVoiceOut *fmd = (FMODVoiceOut *) hw; | ||||
|     int decr; | ||||
|     void *p1 = 0, *p2 = 0; | ||||
|     unsigned int blen1 = 0, blen2 = 0; | ||||
|     unsigned int len1 = 0, len2 = 0; | ||||
|  | ||||
|     if (!hw->pending_disable) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     decr = live; | ||||
|  | ||||
|     if (fmd->channel >= 0) { | ||||
|         int len = decr; | ||||
|         int old_pos = fmd->old_pos; | ||||
|         int ppos = FSOUND_GetCurrentPosition (fmd->channel); | ||||
|  | ||||
|         if (ppos == old_pos || !ppos) { | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
|         if ((old_pos < ppos) && ((old_pos + len) > ppos)) { | ||||
|             len = ppos - old_pos; | ||||
|         } | ||||
|         else { | ||||
|             if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) { | ||||
|                 len = hw->samples - old_pos + ppos; | ||||
|             } | ||||
|         } | ||||
|         decr = len; | ||||
|  | ||||
|         if (audio_bug (AUDIO_FUNC, decr < 0)) { | ||||
|             dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n", | ||||
|                    decr, live, ppos, old_pos, len); | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     if (!decr) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info, | ||||
|                           fmd->old_pos, decr, | ||||
|                           &p1, &p2, | ||||
|                           &blen1, &blen2)) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     len1 = blen1 >> hw->info.shift; | ||||
|     len2 = blen2 >> hw->info.shift; | ||||
|     ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2); | ||||
|     decr = len1 + len2; | ||||
|  | ||||
|     if (p1 && len1) { | ||||
|         fmod_write_sample (hw, p1, len1); | ||||
|     } | ||||
|  | ||||
|     if (p2 && len2) { | ||||
|         fmod_write_sample (hw, p2, len2); | ||||
|     } | ||||
|  | ||||
|     fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); | ||||
|  | ||||
|     fmd->old_pos = (fmd->old_pos + decr) % hw->samples; | ||||
|     return decr; | ||||
| } | ||||
|  | ||||
| static int aud_to_fmodfmt (audfmt_e fmt, int stereo) | ||||
| { | ||||
|     int mode = FSOUND_LOOP_NORMAL; | ||||
|  | ||||
|     switch (fmt) { | ||||
|     case AUD_FMT_S8: | ||||
|         mode |= FSOUND_SIGNED | FSOUND_8BITS; | ||||
|         break; | ||||
|  | ||||
|     case AUD_FMT_U8: | ||||
|         mode |= FSOUND_UNSIGNED | FSOUND_8BITS; | ||||
|         break; | ||||
|  | ||||
|     case AUD_FMT_S16: | ||||
|         mode |= FSOUND_SIGNED | FSOUND_16BITS; | ||||
|         break; | ||||
|  | ||||
|     case AUD_FMT_U16: | ||||
|         mode |= FSOUND_UNSIGNED | FSOUND_16BITS; | ||||
|         break; | ||||
|  | ||||
|     default: | ||||
|         dolog ("Internal logic error: Bad audio format %d\n", fmt); | ||||
| #ifdef DEBUG_FMOD | ||||
|         abort (); | ||||
| #endif | ||||
|         mode |= FSOUND_8BITS; | ||||
|     } | ||||
|     mode |= stereo ? FSOUND_STEREO : FSOUND_MONO; | ||||
|     return mode; | ||||
| } | ||||
|  | ||||
| static void fmod_fini_out (HWVoiceOut *hw) | ||||
| { | ||||
|     FMODVoiceOut *fmd = (FMODVoiceOut *) hw; | ||||
|  | ||||
|     if (fmd->fmod_sample) { | ||||
|         FSOUND_Sample_Free (fmd->fmod_sample); | ||||
|         fmd->fmod_sample = 0; | ||||
|  | ||||
|         if (fmd->channel >= 0) { | ||||
|             FSOUND_StopSound (fmd->channel); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int fmod_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
| { | ||||
|     int mode, channel; | ||||
|     FMODVoiceOut *fmd = (FMODVoiceOut *) hw; | ||||
|     struct audsettings obt_as = *as; | ||||
|  | ||||
|     mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0); | ||||
|     fmd->fmod_sample = FSOUND_Sample_Alloc ( | ||||
|         FSOUND_FREE,            /* index */ | ||||
|         conf.nb_samples,        /* length */ | ||||
|         mode,                   /* mode */ | ||||
|         as->freq,               /* freq */ | ||||
|         255,                    /* volume */ | ||||
|         128,                    /* pan */ | ||||
|         255                     /* priority */ | ||||
|         ); | ||||
|  | ||||
|     if (!fmd->fmod_sample) { | ||||
|         fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n"); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1); | ||||
|     if (channel < 0) { | ||||
|         fmod_logerr2 ("DAC", "Failed to start playing sound\n"); | ||||
|         FSOUND_Sample_Free (fmd->fmod_sample); | ||||
|         return -1; | ||||
|     } | ||||
|     fmd->channel = channel; | ||||
|  | ||||
|     /* FMOD always operates on little endian frames? */ | ||||
|     obt_as.endianness = 0; | ||||
|     audio_pcm_init_info (&hw->info, &obt_as); | ||||
|     hw->samples = conf.nb_samples; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
| { | ||||
|     int status; | ||||
|     FMODVoiceOut *fmd = (FMODVoiceOut *) hw; | ||||
|  | ||||
|     switch (cmd) { | ||||
|     case VOICE_ENABLE: | ||||
|         fmod_clear_sample (fmd); | ||||
|         status = FSOUND_SetPaused (fmd->channel, 0); | ||||
|         if (!status) { | ||||
|             fmod_logerr ("Failed to resume channel %d\n", fmd->channel); | ||||
|         } | ||||
|         break; | ||||
|  | ||||
|     case VOICE_DISABLE: | ||||
|         status = FSOUND_SetPaused (fmd->channel, 1); | ||||
|         if (!status) { | ||||
|             fmod_logerr ("Failed to pause channel %d\n", fmd->channel); | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int fmod_init_in (HWVoiceIn *hw, struct audsettings *as) | ||||
| { | ||||
|     int mode; | ||||
|     FMODVoiceIn *fmd = (FMODVoiceIn *) hw; | ||||
|     struct audsettings obt_as = *as; | ||||
|  | ||||
|     if (conf.broken_adc) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0); | ||||
|     fmd->fmod_sample = FSOUND_Sample_Alloc ( | ||||
|         FSOUND_FREE,            /* index */ | ||||
|         conf.nb_samples,        /* length */ | ||||
|         mode,                   /* mode */ | ||||
|         as->freq,               /* freq */ | ||||
|         255,                    /* volume */ | ||||
|         128,                    /* pan */ | ||||
|         255                     /* priority */ | ||||
|         ); | ||||
|  | ||||
|     if (!fmd->fmod_sample) { | ||||
|         fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n"); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     /* FMOD always operates on little endian frames? */ | ||||
|     obt_as.endianness = 0; | ||||
|     audio_pcm_init_info (&hw->info, &obt_as); | ||||
|     hw->samples = conf.nb_samples; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void fmod_fini_in (HWVoiceIn *hw) | ||||
| { | ||||
|     FMODVoiceIn *fmd = (FMODVoiceIn *) hw; | ||||
|  | ||||
|     if (fmd->fmod_sample) { | ||||
|         FSOUND_Record_Stop (); | ||||
|         FSOUND_Sample_Free (fmd->fmod_sample); | ||||
|         fmd->fmod_sample = 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int fmod_run_in (HWVoiceIn *hw) | ||||
| { | ||||
|     FMODVoiceIn *fmd = (FMODVoiceIn *) hw; | ||||
|     int hwshift = hw->info.shift; | ||||
|     int live, dead, new_pos, len; | ||||
|     unsigned int blen1 = 0, blen2 = 0; | ||||
|     unsigned int len1, len2; | ||||
|     unsigned int decr; | ||||
|     void *p1, *p2; | ||||
|  | ||||
|     live = audio_pcm_hw_get_live_in (hw); | ||||
|     dead = hw->samples - live; | ||||
|     if (!dead) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     new_pos = FSOUND_Record_GetPosition (); | ||||
|     if (new_pos < 0) { | ||||
|         fmod_logerr ("Could not get recording position\n"); | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     len = audio_ring_dist (new_pos,  hw->wpos, hw->samples); | ||||
|     if (!len) { | ||||
|         return 0; | ||||
|     } | ||||
|     len = audio_MIN (len, dead); | ||||
|  | ||||
|     if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info, | ||||
|                           hw->wpos, len, | ||||
|                           &p1, &p2, | ||||
|                           &blen1, &blen2)) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     len1 = blen1 >> hwshift; | ||||
|     len2 = blen2 >> hwshift; | ||||
|     decr = len1 + len2; | ||||
|  | ||||
|     if (p1 && blen1) { | ||||
|         hw->conv (hw->conv_buf + hw->wpos, p1, len1); | ||||
|     } | ||||
|     if (p2 && len2) { | ||||
|         hw->conv (hw->conv_buf, p2, len2); | ||||
|     } | ||||
|  | ||||
|     fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); | ||||
|     hw->wpos = (hw->wpos + decr) % hw->samples; | ||||
|     return decr; | ||||
| } | ||||
|  | ||||
| static struct { | ||||
|     const char *name; | ||||
|     int type; | ||||
| } drvtab[] = { | ||||
|     { .name = "none",   .type = FSOUND_OUTPUT_NOSOUND }, | ||||
| #ifdef _WIN32 | ||||
|     { .name = "winmm",  .type = FSOUND_OUTPUT_WINMM   }, | ||||
|     { .name = "dsound", .type = FSOUND_OUTPUT_DSOUND  }, | ||||
|     { .name = "a3d",    .type = FSOUND_OUTPUT_A3D     }, | ||||
|     { .name = "asio",   .type = FSOUND_OUTPUT_ASIO    }, | ||||
| #endif | ||||
| #ifdef __linux__ | ||||
|     { .name = "oss",    .type = FSOUND_OUTPUT_OSS     }, | ||||
|     { .name = "alsa",   .type = FSOUND_OUTPUT_ALSA    }, | ||||
|     { .name = "esd",    .type = FSOUND_OUTPUT_ESD     }, | ||||
| #endif | ||||
| #ifdef __APPLE__ | ||||
|     { .name = "mac",    .type = FSOUND_OUTPUT_MAC     }, | ||||
| #endif | ||||
| #if 0 | ||||
|     { .name = "xbox",   .type = FSOUND_OUTPUT_XBOX    }, | ||||
|     { .name = "ps2",    .type = FSOUND_OUTPUT_PS2     }, | ||||
|     { .name = "gcube",  .type = FSOUND_OUTPUT_GC      }, | ||||
| #endif | ||||
|     { .name = "none-realtime", .type = FSOUND_OUTPUT_NOSOUND_NONREALTIME } | ||||
| }; | ||||
|  | ||||
| static void *fmod_audio_init (void) | ||||
| { | ||||
|     size_t i; | ||||
|     double ver; | ||||
|     int status; | ||||
|     int output_type = -1; | ||||
|     const char *drv = conf.drvname; | ||||
|  | ||||
|     ver = FSOUND_GetVersion (); | ||||
|     if (ver < FMOD_VERSION) { | ||||
|         dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
| #ifdef __linux__ | ||||
|     if (ver < 3.75) { | ||||
|         dolog ("FMOD before 3.75 has bug preventing ADC from working\n" | ||||
|                "ADC will be disabled.\n"); | ||||
|         conf.broken_adc = 1; | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     if (drv) { | ||||
|         int found = 0; | ||||
|         for (i = 0; i < ARRAY_SIZE (drvtab); i++) { | ||||
|             if (!strcmp (drv, drvtab[i].name)) { | ||||
|                 output_type = drvtab[i].type; | ||||
|                 found = 1; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         if (!found) { | ||||
|             dolog ("Unknown FMOD driver `%s'\n", drv); | ||||
|             dolog ("Valid drivers:\n"); | ||||
|             for (i = 0; i < ARRAY_SIZE (drvtab); i++) { | ||||
|                 dolog ("  %s\n", drvtab[i].name); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (output_type != -1) { | ||||
|         status = FSOUND_SetOutput (output_type); | ||||
|         if (!status) { | ||||
|             fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type); | ||||
|             return NULL; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (conf.bufsize) { | ||||
|         status = FSOUND_SetBufferSize (conf.bufsize); | ||||
|         if (!status) { | ||||
|             fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     status = FSOUND_Init (conf.freq, conf.nb_channels, 0); | ||||
|     if (!status) { | ||||
|         fmod_logerr ("FSOUND_Init failed\n"); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     return &conf; | ||||
| } | ||||
|  | ||||
| static int fmod_read (SWVoiceIn *sw, void *buf, int size) | ||||
| { | ||||
|     return audio_pcm_sw_read (sw, buf, size); | ||||
| } | ||||
|  | ||||
| static int fmod_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||
| { | ||||
|     int status; | ||||
|     FMODVoiceIn *fmd = (FMODVoiceIn *) hw; | ||||
|  | ||||
|     switch (cmd) { | ||||
|     case VOICE_ENABLE: | ||||
|         status = FSOUND_Record_StartSample (fmd->fmod_sample, 1); | ||||
|         if (!status) { | ||||
|             fmod_logerr ("Failed to start recording\n"); | ||||
|         } | ||||
|         break; | ||||
|  | ||||
|     case VOICE_DISABLE: | ||||
|         status = FSOUND_Record_Stop (); | ||||
|         if (!status) { | ||||
|             fmod_logerr ("Failed to stop recording\n"); | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void fmod_audio_fini (void *opaque) | ||||
| { | ||||
|     (void) opaque; | ||||
|     FSOUND_Close (); | ||||
| } | ||||
|  | ||||
| static struct audio_option fmod_options[] = { | ||||
|     { | ||||
|         .name  = "DRV", | ||||
|         .tag   = AUD_OPT_STR, | ||||
|         .valp  = &conf.drvname, | ||||
|         .descr = "FMOD driver" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "FREQ", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &conf.freq, | ||||
|         .descr = "Default frequency" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "SAMPLES", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &conf.nb_samples, | ||||
|         .descr = "Buffer size in samples" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "CHANNELS", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &conf.nb_channels, | ||||
|         .descr = "Number of default channels (1 - mono, 2 - stereo)" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "BUFSIZE", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &conf.bufsize, | ||||
|         .descr = "(undocumented)" | ||||
|     }, | ||||
|     { /* End of list */ } | ||||
| }; | ||||
|  | ||||
| static struct audio_pcm_ops fmod_pcm_ops = { | ||||
|     .init_out = fmod_init_out, | ||||
|     .fini_out = fmod_fini_out, | ||||
|     .run_out  = fmod_run_out, | ||||
|     .write    = fmod_write, | ||||
|     .ctl_out  = fmod_ctl_out, | ||||
|  | ||||
|     .init_in  = fmod_init_in, | ||||
|     .fini_in  = fmod_fini_in, | ||||
|     .run_in   = fmod_run_in, | ||||
|     .read     = fmod_read, | ||||
|     .ctl_in   = fmod_ctl_in | ||||
| }; | ||||
|  | ||||
| struct audio_driver fmod_audio_driver = { | ||||
|     .name           = "fmod", | ||||
|     .descr          = "FMOD 3.xx http://www.fmod.org", | ||||
|     .options        = fmod_options, | ||||
|     .init           = fmod_audio_init, | ||||
|     .fini           = fmod_audio_fini, | ||||
|     .pcm_ops        = &fmod_pcm_ops, | ||||
|     .can_be_default = 1, | ||||
|     .max_voices_out = INT_MAX, | ||||
|     .max_voices_in  = INT_MAX, | ||||
|     .voice_size_out = sizeof (FMODVoiceOut), | ||||
|     .voice_size_in  = sizeof (FMODVoiceIn) | ||||
| }; | ||||
| @@ -22,7 +22,6 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "audio.h" | ||||
|  | ||||
|   | ||||
| @@ -21,7 +21,6 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "audio.h" | ||||
| #include "qemu/timer.h" | ||||
| @@ -64,7 +63,7 @@ static int no_write (SWVoiceOut *sw, void *buf, int len) | ||||
|     return audio_pcm_sw_write (sw, buf, len); | ||||
| } | ||||
|  | ||||
| static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) | ||||
| static int no_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
| { | ||||
|     audio_pcm_init_info (&hw->info, as); | ||||
|     hw->samples = 1024; | ||||
| @@ -83,7 +82,7 @@ static int no_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int no_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | ||||
| static int no_init_in (HWVoiceIn *hw, struct audsettings *as) | ||||
| { | ||||
|     audio_pcm_init_info (&hw->info, as); | ||||
|     hw->samples = 1024; | ||||
|   | ||||
							
								
								
									
										144
									
								
								audio/ossaudio.c
									
									
									
									
									
								
							
							
						
						
									
										144
									
								
								audio/ossaudio.c
									
									
									
									
									
								
							| @@ -21,15 +21,15 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include <stdlib.h> | ||||
| #include <sys/mman.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/ioctl.h> | ||||
| #include <sys/soundcard.h> | ||||
| #include "qemu-common.h" | ||||
| #include "qemu/main-loop.h" | ||||
| #include "qemu/host-utils.h" | ||||
| #include "audio.h" | ||||
| #include "trace.h" | ||||
|  | ||||
| #define AUDIO_CAP "oss" | ||||
| #include "audio_int.h" | ||||
| @@ -38,16 +38,6 @@ | ||||
| #define USE_DSP_POLICY | ||||
| #endif | ||||
|  | ||||
| typedef struct OSSConf { | ||||
|     int try_mmap; | ||||
|     int nfrags; | ||||
|     int fragsize; | ||||
|     const char *devpath_out; | ||||
|     const char *devpath_in; | ||||
|     int exclusive; | ||||
|     int policy; | ||||
| } OSSConf; | ||||
|  | ||||
| typedef struct OSSVoiceOut { | ||||
|     HWVoiceOut hw; | ||||
|     void *pcm_buf; | ||||
| @@ -57,7 +47,6 @@ typedef struct OSSVoiceOut { | ||||
|     int fragsize; | ||||
|     int mmapped; | ||||
|     int pending; | ||||
|     OSSConf *conf; | ||||
| } OSSVoiceOut; | ||||
|  | ||||
| typedef struct OSSVoiceIn { | ||||
| @@ -66,9 +55,28 @@ typedef struct OSSVoiceIn { | ||||
|     int fd; | ||||
|     int nfrags; | ||||
|     int fragsize; | ||||
|     OSSConf *conf; | ||||
| } OSSVoiceIn; | ||||
|  | ||||
| static struct { | ||||
|     int try_mmap; | ||||
|     int nfrags; | ||||
|     int fragsize; | ||||
|     const char *devpath_out; | ||||
|     const char *devpath_in; | ||||
|     int debug; | ||||
|     int exclusive; | ||||
|     int policy; | ||||
| } conf = { | ||||
|     .try_mmap = 0, | ||||
|     .nfrags = 4, | ||||
|     .fragsize = 4096, | ||||
|     .devpath_out = "/dev/dsp", | ||||
|     .devpath_in = "/dev/dsp", | ||||
|     .debug = 0, | ||||
|     .exclusive = 0, | ||||
|     .policy = 5 | ||||
| }; | ||||
|  | ||||
| struct oss_params { | ||||
|     int freq; | ||||
|     audfmt_e fmt; | ||||
| @@ -130,18 +138,18 @@ static void oss_helper_poll_in (void *opaque) | ||||
|     audio_run ("oss_poll_in"); | ||||
| } | ||||
|  | ||||
| static void oss_poll_out (HWVoiceOut *hw) | ||||
| static int oss_poll_out (HWVoiceOut *hw) | ||||
| { | ||||
|     OSSVoiceOut *oss = (OSSVoiceOut *) hw; | ||||
|  | ||||
|     qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL); | ||||
|     return qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL); | ||||
| } | ||||
|  | ||||
| static void oss_poll_in (HWVoiceIn *hw) | ||||
| static int oss_poll_in (HWVoiceIn *hw) | ||||
| { | ||||
|     OSSVoiceIn *oss = (OSSVoiceIn *) hw; | ||||
|  | ||||
|     qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL); | ||||
|     return qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL); | ||||
| } | ||||
|  | ||||
| static int oss_write (SWVoiceOut *sw, void *buf, int len) | ||||
| @@ -264,18 +272,18 @@ static int oss_get_version (int fd, int *version, const char *typ) | ||||
| #endif | ||||
|  | ||||
| static int oss_open (int in, struct oss_params *req, | ||||
|                      struct oss_params *obt, int *pfd, OSSConf* conf) | ||||
|                      struct oss_params *obt, int *pfd) | ||||
| { | ||||
|     int fd; | ||||
|     int oflags = conf->exclusive ? O_EXCL : 0; | ||||
|     int oflags = conf.exclusive ? O_EXCL : 0; | ||||
|     audio_buf_info abinfo; | ||||
|     int fmt, freq, nchannels; | ||||
|     int setfragment = 1; | ||||
|     const char *dspname = in ? conf->devpath_in : conf->devpath_out; | ||||
|     const char *dspname = in ? conf.devpath_in : conf.devpath_out; | ||||
|     const char *typ = in ? "ADC" : "DAC"; | ||||
|  | ||||
|     /* Kludge needed to have working mmap on Linux */ | ||||
|     oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY); | ||||
|     oflags |= conf.try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY); | ||||
|  | ||||
|     fd = open (dspname, oflags | O_NONBLOCK); | ||||
|     if (-1 == fd) { | ||||
| @@ -309,18 +317,20 @@ static int oss_open (int in, struct oss_params *req, | ||||
|     } | ||||
|  | ||||
| #ifdef USE_DSP_POLICY | ||||
|     if (conf->policy >= 0) { | ||||
|     if (conf.policy >= 0) { | ||||
|         int version; | ||||
|  | ||||
|         if (!oss_get_version (fd, &version, typ)) { | ||||
|             trace_oss_version(version); | ||||
|             if (conf.debug) { | ||||
|                 dolog ("OSS version = %#x\n", version); | ||||
|             } | ||||
|  | ||||
|             if (version >= 0x040000) { | ||||
|                 int policy = conf->policy; | ||||
|                 int policy = conf.policy; | ||||
|                 if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) { | ||||
|                     oss_logerr2 (errno, typ, | ||||
|                                  "Failed to set timing policy to %d\n", | ||||
|                                  conf->policy); | ||||
|                                  conf.policy); | ||||
|                     goto err; | ||||
|                 } | ||||
|                 setfragment = 0; | ||||
| @@ -448,12 +458,19 @@ static int oss_run_out (HWVoiceOut *hw, int live) | ||||
|         } | ||||
|  | ||||
|         if (abinfo.bytes > bufsize) { | ||||
|             trace_oss_invalid_available_size(abinfo.bytes, bufsize); | ||||
|             if (conf.debug) { | ||||
|                 dolog ("warning: Invalid available size, size=%d bufsize=%d\n" | ||||
|                        "please report your OS/audio hw to av1474@comtv.ru\n", | ||||
|                        abinfo.bytes, bufsize); | ||||
|             } | ||||
|             abinfo.bytes = bufsize; | ||||
|         } | ||||
|  | ||||
|         if (abinfo.bytes < 0) { | ||||
|             trace_oss_invalid_available_size(abinfo.bytes, bufsize); | ||||
|             if (conf.debug) { | ||||
|                 dolog ("warning: Invalid available size, size=%d bufsize=%d\n", | ||||
|                        abinfo.bytes, bufsize); | ||||
|             } | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
| @@ -493,8 +510,7 @@ static void oss_fini_out (HWVoiceOut *hw) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|                         void *drv_opaque) | ||||
| static int oss_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
| { | ||||
|     OSSVoiceOut *oss = (OSSVoiceOut *) hw; | ||||
|     struct oss_params req, obt; | ||||
| @@ -503,17 +519,16 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     int fd; | ||||
|     audfmt_e effective_fmt; | ||||
|     struct audsettings obt_as; | ||||
|     OSSConf *conf = drv_opaque; | ||||
|  | ||||
|     oss->fd = -1; | ||||
|  | ||||
|     req.fmt = aud_to_ossfmt (as->fmt, as->endianness); | ||||
|     req.freq = as->freq; | ||||
|     req.nchannels = as->nchannels; | ||||
|     req.fragsize = conf->fragsize; | ||||
|     req.nfrags = conf->nfrags; | ||||
|     req.fragsize = conf.fragsize; | ||||
|     req.nfrags = conf.nfrags; | ||||
|  | ||||
|     if (oss_open (0, &req, &obt, &fd, conf)) { | ||||
|     if (oss_open (0, &req, &obt, &fd)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| @@ -540,7 +555,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; | ||||
|  | ||||
|     oss->mmapped = 0; | ||||
|     if (conf->try_mmap) { | ||||
|     if (conf.try_mmap) { | ||||
|         oss->pcm_buf = mmap ( | ||||
|             NULL, | ||||
|             hw->samples << hw->info.shift, | ||||
| @@ -600,7 +615,6 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     } | ||||
|  | ||||
|     oss->fd = fd; | ||||
|     oss->conf = conf; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @@ -620,8 +634,7 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
|             va_end (ap); | ||||
|  | ||||
|             ldebug ("enabling voice\n"); | ||||
|             if (poll_mode) { | ||||
|                 oss_poll_out (hw); | ||||
|             if (poll_mode && oss_poll_out (hw)) { | ||||
|                 poll_mode = 0; | ||||
|             } | ||||
|             hw->poll_mode = poll_mode; | ||||
| @@ -663,7 +676,7 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | ||||
| static int oss_init_in (HWVoiceIn *hw, struct audsettings *as) | ||||
| { | ||||
|     OSSVoiceIn *oss = (OSSVoiceIn *) hw; | ||||
|     struct oss_params req, obt; | ||||
| @@ -672,16 +685,15 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | ||||
|     int fd; | ||||
|     audfmt_e effective_fmt; | ||||
|     struct audsettings obt_as; | ||||
|     OSSConf *conf = drv_opaque; | ||||
|  | ||||
|     oss->fd = -1; | ||||
|  | ||||
|     req.fmt = aud_to_ossfmt (as->fmt, as->endianness); | ||||
|     req.freq = as->freq; | ||||
|     req.nchannels = as->nchannels; | ||||
|     req.fragsize = conf->fragsize; | ||||
|     req.nfrags = conf->nfrags; | ||||
|     if (oss_open (1, &req, &obt, &fd, conf)) { | ||||
|     req.fragsize = conf.fragsize; | ||||
|     req.nfrags = conf.nfrags; | ||||
|     if (oss_open (1, &req, &obt, &fd)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| @@ -715,7 +727,6 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | ||||
|     } | ||||
|  | ||||
|     oss->fd = fd; | ||||
|     oss->conf = conf; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @@ -817,8 +828,7 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||
|             poll_mode = va_arg (ap, int); | ||||
|             va_end (ap); | ||||
|  | ||||
|             if (poll_mode) { | ||||
|                 oss_poll_in (hw); | ||||
|             if (poll_mode && oss_poll_in (hw)) { | ||||
|                 poll_mode = 0; | ||||
|             } | ||||
|             hw->poll_mode = poll_mode; | ||||
| @@ -835,79 +845,71 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static OSSConf glob_conf = { | ||||
|     .try_mmap = 0, | ||||
|     .nfrags = 4, | ||||
|     .fragsize = 4096, | ||||
|     .devpath_out = "/dev/dsp", | ||||
|     .devpath_in = "/dev/dsp", | ||||
|     .exclusive = 0, | ||||
|     .policy = 5 | ||||
| }; | ||||
|  | ||||
| static void *oss_audio_init (void) | ||||
| { | ||||
|     OSSConf *conf = g_malloc(sizeof(OSSConf)); | ||||
|     *conf = glob_conf; | ||||
|  | ||||
|     if (access(conf->devpath_in, R_OK | W_OK) < 0 || | ||||
|         access(conf->devpath_out, R_OK | W_OK) < 0) { | ||||
|         g_free(conf); | ||||
|     if (access(conf.devpath_in, R_OK | W_OK) < 0 || | ||||
|         access(conf.devpath_out, R_OK | W_OK) < 0) { | ||||
|         return NULL; | ||||
|     } | ||||
|     return conf; | ||||
|     return &conf; | ||||
| } | ||||
|  | ||||
| static void oss_audio_fini (void *opaque) | ||||
| { | ||||
|     g_free(opaque); | ||||
|     (void) opaque; | ||||
| } | ||||
|  | ||||
| static struct audio_option oss_options[] = { | ||||
|     { | ||||
|         .name  = "FRAGSIZE", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.fragsize, | ||||
|         .valp  = &conf.fragsize, | ||||
|         .descr = "Fragment size in bytes" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "NFRAGS", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.nfrags, | ||||
|         .valp  = &conf.nfrags, | ||||
|         .descr = "Number of fragments" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "MMAP", | ||||
|         .tag   = AUD_OPT_BOOL, | ||||
|         .valp  = &glob_conf.try_mmap, | ||||
|         .valp  = &conf.try_mmap, | ||||
|         .descr = "Try using memory mapped access" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "DAC_DEV", | ||||
|         .tag   = AUD_OPT_STR, | ||||
|         .valp  = &glob_conf.devpath_out, | ||||
|         .valp  = &conf.devpath_out, | ||||
|         .descr = "Path to DAC device" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "ADC_DEV", | ||||
|         .tag   = AUD_OPT_STR, | ||||
|         .valp  = &glob_conf.devpath_in, | ||||
|         .valp  = &conf.devpath_in, | ||||
|         .descr = "Path to ADC device" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "EXCLUSIVE", | ||||
|         .tag   = AUD_OPT_BOOL, | ||||
|         .valp  = &glob_conf.exclusive, | ||||
|         .valp  = &conf.exclusive, | ||||
|         .descr = "Open device in exclusive mode (vmix wont work)" | ||||
|     }, | ||||
| #ifdef USE_DSP_POLICY | ||||
|     { | ||||
|         .name  = "POLICY", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.policy, | ||||
|         .valp  = &conf.policy, | ||||
|         .descr = "Set the timing policy of the device, -1 to use fragment mode", | ||||
|     }, | ||||
| #endif | ||||
|     { | ||||
|         .name  = "DEBUG", | ||||
|         .tag   = AUD_OPT_BOOL, | ||||
|         .valp  = &conf.debug, | ||||
|         .descr = "Turn on some debugging messages" | ||||
|     }, | ||||
|     { /* End of list */ } | ||||
| }; | ||||
|  | ||||
|   | ||||
							
								
								
									
										106
									
								
								audio/paaudio.c
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								audio/paaudio.c
									
									
									
									
									
								
							| @@ -1,5 +1,4 @@ | ||||
| /* public domain */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "audio.h" | ||||
|  | ||||
| @@ -9,19 +8,6 @@ | ||||
| #include "audio_int.h" | ||||
| #include "audio_pt_int.h" | ||||
|  | ||||
| typedef struct { | ||||
|     int samples; | ||||
|     char *server; | ||||
|     char *sink; | ||||
|     char *source; | ||||
| } PAConf; | ||||
|  | ||||
| typedef struct { | ||||
|     PAConf conf; | ||||
|     pa_threaded_mainloop *mainloop; | ||||
|     pa_context *context; | ||||
| } paaudio; | ||||
|  | ||||
| typedef struct { | ||||
|     HWVoiceOut hw; | ||||
|     int done; | ||||
| @@ -31,7 +17,6 @@ typedef struct { | ||||
|     pa_stream *stream; | ||||
|     void *pcm_buf; | ||||
|     struct audio_pt pt; | ||||
|     paaudio *g; | ||||
| } PAVoiceOut; | ||||
|  | ||||
| typedef struct { | ||||
| @@ -45,10 +30,20 @@ typedef struct { | ||||
|     struct audio_pt pt; | ||||
|     const void *read_data; | ||||
|     size_t read_index, read_length; | ||||
|     paaudio *g; | ||||
| } PAVoiceIn; | ||||
|  | ||||
| static void qpa_audio_fini(void *opaque); | ||||
| typedef struct { | ||||
|     int samples; | ||||
|     char *server; | ||||
|     char *sink; | ||||
|     char *source; | ||||
|     pa_threaded_mainloop *mainloop; | ||||
|     pa_context *context; | ||||
| } paaudio; | ||||
|  | ||||
| static paaudio glob_paaudio = { | ||||
|     .samples = 4096, | ||||
| }; | ||||
|  | ||||
| static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...) | ||||
| { | ||||
| @@ -111,7 +106,7 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) | ||||
|  | ||||
| static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror) | ||||
| { | ||||
|     paaudio *g = p->g; | ||||
|     paaudio *g = &glob_paaudio; | ||||
|  | ||||
|     pa_threaded_mainloop_lock (g->mainloop); | ||||
|  | ||||
| @@ -165,7 +160,7 @@ unlock_and_fail: | ||||
|  | ||||
| static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror) | ||||
| { | ||||
|     paaudio *g = p->g; | ||||
|     paaudio *g = &glob_paaudio; | ||||
|  | ||||
|     pa_threaded_mainloop_lock (g->mainloop); | ||||
|  | ||||
| @@ -227,7 +222,7 @@ static void *qpa_thread_out (void *arg) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2); | ||||
|         decr = to_mix = audio_MIN (pa->live, glob_paaudio.samples >> 2); | ||||
|         rpos = pa->rpos; | ||||
|  | ||||
|         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { | ||||
| @@ -319,7 +314,7 @@ static void *qpa_thread_in (void *arg) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2); | ||||
|         incr = to_grab = audio_MIN (pa->dead, glob_paaudio.samples >> 2); | ||||
|         wpos = pa->wpos; | ||||
|  | ||||
|         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { | ||||
| @@ -435,7 +430,7 @@ static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness) | ||||
|  | ||||
| static void context_state_cb (pa_context *c, void *userdata) | ||||
| { | ||||
|     paaudio *g = userdata; | ||||
|     paaudio *g = &glob_paaudio; | ||||
|  | ||||
|     switch (pa_context_get_state(c)) { | ||||
|     case PA_CONTEXT_READY: | ||||
| @@ -454,7 +449,7 @@ static void context_state_cb (pa_context *c, void *userdata) | ||||
|  | ||||
| static void stream_state_cb (pa_stream *s, void * userdata) | ||||
| { | ||||
|     paaudio *g = userdata; | ||||
|     paaudio *g = &glob_paaudio; | ||||
|  | ||||
|     switch (pa_stream_get_state (s)) { | ||||
|  | ||||
| @@ -472,21 +467,23 @@ static void stream_state_cb (pa_stream *s, void * userdata) | ||||
|  | ||||
| static void stream_request_cb (pa_stream *s, size_t length, void *userdata) | ||||
| { | ||||
|     paaudio *g = userdata; | ||||
|     paaudio *g = &glob_paaudio; | ||||
|  | ||||
|     pa_threaded_mainloop_signal (g->mainloop, 0); | ||||
| } | ||||
|  | ||||
| static pa_stream *qpa_simple_new ( | ||||
|         paaudio *g, | ||||
|         const char *server, | ||||
|         const char *name, | ||||
|         pa_stream_direction_t dir, | ||||
|         const char *dev, | ||||
|         const char *stream_name, | ||||
|         const pa_sample_spec *ss, | ||||
|         const pa_channel_map *map, | ||||
|         const pa_buffer_attr *attr, | ||||
|         int *rerror) | ||||
| { | ||||
|     paaudio *g = &glob_paaudio; | ||||
|     int r; | ||||
|     pa_stream *stream; | ||||
|  | ||||
| @@ -537,15 +534,13 @@ fail: | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|                         void *drv_opaque) | ||||
| static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
| { | ||||
|     int error; | ||||
|     pa_sample_spec ss; | ||||
|     pa_buffer_attr ba; | ||||
|     static pa_sample_spec ss; | ||||
|     static pa_buffer_attr ba; | ||||
|     struct audsettings obt_as = *as; | ||||
|     PAVoiceOut *pa = (PAVoiceOut *) hw; | ||||
|     paaudio *g = pa->g = drv_opaque; | ||||
|  | ||||
|     ss.format = audfmt_to_pa (as->fmt, as->endianness); | ||||
|     ss.channels = as->nchannels; | ||||
| @@ -563,10 +558,11 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); | ||||
|  | ||||
|     pa->stream = qpa_simple_new ( | ||||
|         g, | ||||
|         glob_paaudio.server, | ||||
|         "qemu", | ||||
|         PA_STREAM_PLAYBACK, | ||||
|         g->conf.sink, | ||||
|         glob_paaudio.sink, | ||||
|         "pcm.playback", | ||||
|         &ss, | ||||
|         NULL,                   /* channel map */ | ||||
|         &ba,                    /* buffering attributes */ | ||||
| @@ -578,7 +574,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     } | ||||
|  | ||||
|     audio_pcm_init_info (&hw->info, &obt_as); | ||||
|     hw->samples = g->conf.samples; | ||||
|     hw->samples = glob_paaudio.samples; | ||||
|     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); | ||||
|     pa->rpos = hw->rpos; | ||||
|     if (!pa->pcm_buf) { | ||||
| @@ -605,13 +601,12 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | ||||
| static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as) | ||||
| { | ||||
|     int error; | ||||
|     pa_sample_spec ss; | ||||
|     static pa_sample_spec ss; | ||||
|     struct audsettings obt_as = *as; | ||||
|     PAVoiceIn *pa = (PAVoiceIn *) hw; | ||||
|     paaudio *g = pa->g = drv_opaque; | ||||
|  | ||||
|     ss.format = audfmt_to_pa (as->fmt, as->endianness); | ||||
|     ss.channels = as->nchannels; | ||||
| @@ -620,10 +615,11 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | ||||
|     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); | ||||
|  | ||||
|     pa->stream = qpa_simple_new ( | ||||
|         g, | ||||
|         glob_paaudio.server, | ||||
|         "qemu", | ||||
|         PA_STREAM_RECORD, | ||||
|         g->conf.source, | ||||
|         glob_paaudio.source, | ||||
|         "pcm.capture", | ||||
|         &ss, | ||||
|         NULL,                   /* channel map */ | ||||
|         NULL,                   /* buffering attributes */ | ||||
| @@ -635,7 +631,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | ||||
|     } | ||||
|  | ||||
|     audio_pcm_init_info (&hw->info, &obt_as); | ||||
|     hw->samples = g->conf.samples; | ||||
|     hw->samples = glob_paaudio.samples; | ||||
|     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); | ||||
|     pa->wpos = hw->wpos; | ||||
|     if (!pa->pcm_buf) { | ||||
| @@ -707,7 +703,7 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
|     PAVoiceOut *pa = (PAVoiceOut *) hw; | ||||
|     pa_operation *op; | ||||
|     pa_cvolume v; | ||||
|     paaudio *g = pa->g; | ||||
|     paaudio *g = &glob_paaudio; | ||||
|  | ||||
| #ifdef PA_CHECK_VERSION    /* macro is present in 0.9.16+ */ | ||||
|     pa_cvolume_init (&v);  /* function is present in 0.9.13+ */ | ||||
| @@ -759,7 +755,7 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||
|     PAVoiceIn *pa = (PAVoiceIn *) hw; | ||||
|     pa_operation *op; | ||||
|     pa_cvolume v; | ||||
|     paaudio *g = pa->g; | ||||
|     paaudio *g = &glob_paaudio; | ||||
|  | ||||
| #ifdef PA_CHECK_VERSION | ||||
|     pa_cvolume_init (&v); | ||||
| @@ -809,31 +805,23 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||
| } | ||||
|  | ||||
| /* common */ | ||||
| static PAConf glob_conf = { | ||||
|     .samples = 4096, | ||||
| }; | ||||
|  | ||||
| static void *qpa_audio_init (void) | ||||
| { | ||||
|     paaudio *g = g_malloc(sizeof(paaudio)); | ||||
|     g->conf = glob_conf; | ||||
|     g->mainloop = NULL; | ||||
|     g->context = NULL; | ||||
|     paaudio *g = &glob_paaudio; | ||||
|  | ||||
|     g->mainloop = pa_threaded_mainloop_new (); | ||||
|     if (!g->mainloop) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), | ||||
|                                  g->conf.server); | ||||
|     g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), glob_paaudio.server); | ||||
|     if (!g->context) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     pa_context_set_state_callback (g->context, context_state_cb, g); | ||||
|  | ||||
|     if (pa_context_connect (g->context, g->conf.server, 0, NULL) < 0) { | ||||
|     if (pa_context_connect (g->context, glob_paaudio.server, 0, NULL) < 0) { | ||||
|         qpa_logerr (pa_context_errno (g->context), | ||||
|                     "pa_context_connect() failed\n"); | ||||
|         goto fail; | ||||
| @@ -866,13 +854,12 @@ static void *qpa_audio_init (void) | ||||
|  | ||||
|     pa_threaded_mainloop_unlock (g->mainloop); | ||||
|  | ||||
|     return g; | ||||
|     return &glob_paaudio; | ||||
|  | ||||
| unlock_and_fail: | ||||
|     pa_threaded_mainloop_unlock (g->mainloop); | ||||
| fail: | ||||
|     AUD_log (AUDIO_CAP, "Failed to initialize PA context"); | ||||
|     qpa_audio_fini(g); | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| @@ -887,38 +874,39 @@ static void qpa_audio_fini (void *opaque) | ||||
|     if (g->context) { | ||||
|         pa_context_disconnect (g->context); | ||||
|         pa_context_unref (g->context); | ||||
|         g->context = NULL; | ||||
|     } | ||||
|  | ||||
|     if (g->mainloop) { | ||||
|         pa_threaded_mainloop_free (g->mainloop); | ||||
|     } | ||||
|  | ||||
|     g_free(g); | ||||
|     g->mainloop = NULL; | ||||
| } | ||||
|  | ||||
| struct audio_option qpa_options[] = { | ||||
|     { | ||||
|         .name  = "SAMPLES", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.samples, | ||||
|         .valp  = &glob_paaudio.samples, | ||||
|         .descr = "buffer size in samples" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "SERVER", | ||||
|         .tag   = AUD_OPT_STR, | ||||
|         .valp  = &glob_conf.server, | ||||
|         .valp  = &glob_paaudio.server, | ||||
|         .descr = "server address" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "SINK", | ||||
|         .tag   = AUD_OPT_STR, | ||||
|         .valp  = &glob_conf.sink, | ||||
|         .valp  = &glob_paaudio.sink, | ||||
|         .descr = "sink device name" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "SOURCE", | ||||
|         .tag   = AUD_OPT_STR, | ||||
|         .valp  = &glob_conf.source, | ||||
|         .valp  = &glob_paaudio.source, | ||||
|         .descr = "source device name" | ||||
|     }, | ||||
|     { /* End of list */ } | ||||
|   | ||||
| @@ -21,7 +21,6 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include <SDL.h> | ||||
| #include <SDL_thread.h> | ||||
| #include "qemu-common.h" | ||||
| @@ -56,7 +55,6 @@ static struct SDLAudioState { | ||||
|     SDL_mutex *mutex; | ||||
|     SDL_sem *sem; | ||||
|     int initialized; | ||||
|     bool driver_created; | ||||
| } glob_sdl; | ||||
| typedef struct SDLAudioState SDLAudioState; | ||||
|  | ||||
| @@ -334,8 +332,7 @@ static void sdl_fini_out (HWVoiceOut *hw) | ||||
|     sdl_close (&glob_sdl); | ||||
| } | ||||
|  | ||||
| static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|                         void *drv_opaque) | ||||
| static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
| { | ||||
|     SDLVoiceOut *sdl = (SDLVoiceOut *) hw; | ||||
|     SDLAudioState *s = &glob_sdl; | ||||
| @@ -395,10 +392,6 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
| static void *sdl_audio_init (void) | ||||
| { | ||||
|     SDLAudioState *s = &glob_sdl; | ||||
|     if (s->driver_created) { | ||||
|         sdl_logerr("Can't create multiple sdl backends\n"); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { | ||||
|         sdl_logerr ("SDL failed to initialize audio subsystem\n"); | ||||
| @@ -420,7 +413,6 @@ static void *sdl_audio_init (void) | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     s->driver_created = true; | ||||
|     return s; | ||||
| } | ||||
|  | ||||
| @@ -431,7 +423,6 @@ static void sdl_audio_fini (void *opaque) | ||||
|     SDL_DestroySemaphore (s->sem); | ||||
|     SDL_DestroyMutex (s->mutex); | ||||
|     SDL_QuitSubSystem (SDL_INIT_AUDIO); | ||||
|     s->driver_created = false; | ||||
| } | ||||
|  | ||||
| static struct audio_option sdl_options[] = { | ||||
|   | ||||
| @@ -17,9 +17,7 @@ | ||||
|  * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "hw/hw.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "qemu/timer.h" | ||||
| #include "ui/qemu-spice.h" | ||||
|  | ||||
| @@ -117,8 +115,7 @@ static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate) | ||||
|  | ||||
| /* playback */ | ||||
|  | ||||
| static int line_out_init(HWVoiceOut *hw, struct audsettings *as, | ||||
|                          void *drv_opaque) | ||||
| static int line_out_init (HWVoiceOut *hw, struct audsettings *as) | ||||
| { | ||||
|     SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); | ||||
|     struct audsettings settings; | ||||
| @@ -246,7 +243,7 @@ static int line_out_ctl (HWVoiceOut *hw, int cmd, ...) | ||||
|  | ||||
| /* record */ | ||||
|  | ||||
| static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | ||||
| static int line_in_init (HWVoiceIn *hw, struct audsettings *as) | ||||
| { | ||||
|     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); | ||||
|     struct audsettings settings; | ||||
|   | ||||
| @@ -21,7 +21,6 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "hw/hw.h" | ||||
| #include "qemu/timer.h" | ||||
| #include "audio.h" | ||||
| @@ -37,10 +36,15 @@ typedef struct WAVVoiceOut { | ||||
|     int total_samples; | ||||
| } WAVVoiceOut; | ||||
|  | ||||
| typedef struct { | ||||
| static struct { | ||||
|     struct audsettings settings; | ||||
|     const char *wav_path; | ||||
| } WAVConf; | ||||
| } conf = { | ||||
|     .settings.freq      = 44100, | ||||
|     .settings.nchannels = 2, | ||||
|     .settings.fmt       = AUD_FMT_S16, | ||||
|     .wav_path           = "qemu.wav" | ||||
| }; | ||||
|  | ||||
| static int wav_run_out (HWVoiceOut *hw, int live) | ||||
| { | ||||
| @@ -101,8 +105,7 @@ static void le_store (uint8_t *buf, uint32_t val, int len) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int wav_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|                         void *drv_opaque) | ||||
| static int wav_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
| { | ||||
|     WAVVoiceOut *wav = (WAVVoiceOut *) hw; | ||||
|     int bits16 = 0, stereo = 0; | ||||
| @@ -112,8 +115,9 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|         0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, | ||||
|         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 | ||||
|     }; | ||||
|     WAVConf *conf = drv_opaque; | ||||
|     struct audsettings wav_as = conf->settings; | ||||
|     struct audsettings wav_as = conf.settings; | ||||
|  | ||||
|     (void) as; | ||||
|  | ||||
|     stereo = wav_as.nchannels == 2; | ||||
|     switch (wav_as.fmt) { | ||||
| @@ -151,10 +155,10 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4); | ||||
|     le_store (hdr + 32, 1 << (bits16 + stereo), 2); | ||||
|  | ||||
|     wav->f = fopen (conf->wav_path, "wb"); | ||||
|     wav->f = fopen (conf.wav_path, "wb"); | ||||
|     if (!wav->f) { | ||||
|         dolog ("Failed to open wave file `%s'\nReason: %s\n", | ||||
|                conf->wav_path, strerror (errno)); | ||||
|                conf.wav_path, strerror (errno)); | ||||
|         g_free (wav->pcm_buf); | ||||
|         wav->pcm_buf = NULL; | ||||
|         return -1; | ||||
| @@ -222,49 +226,40 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static WAVConf glob_conf = { | ||||
|     .settings.freq      = 44100, | ||||
|     .settings.nchannels = 2, | ||||
|     .settings.fmt       = AUD_FMT_S16, | ||||
|     .wav_path           = "qemu.wav" | ||||
| }; | ||||
|  | ||||
| static void *wav_audio_init (void) | ||||
| { | ||||
|     WAVConf *conf = g_malloc(sizeof(WAVConf)); | ||||
|     *conf = glob_conf; | ||||
|     return conf; | ||||
|     return &conf; | ||||
| } | ||||
|  | ||||
| static void wav_audio_fini (void *opaque) | ||||
| { | ||||
|     (void) opaque; | ||||
|     ldebug ("wav_fini"); | ||||
|     g_free(opaque); | ||||
| } | ||||
|  | ||||
| static struct audio_option wav_options[] = { | ||||
|     { | ||||
|         .name  = "FREQUENCY", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.settings.freq, | ||||
|         .valp  = &conf.settings.freq, | ||||
|         .descr = "Frequency" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "FORMAT", | ||||
|         .tag   = AUD_OPT_FMT, | ||||
|         .valp  = &glob_conf.settings.fmt, | ||||
|         .valp  = &conf.settings.fmt, | ||||
|         .descr = "Format" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "DAC_FIXED_CHANNELS", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.settings.nchannels, | ||||
|         .valp  = &conf.settings.nchannels, | ||||
|         .descr = "Number of channels (1 - mono, 2 - stereo)" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "PATH", | ||||
|         .tag   = AUD_OPT_STR, | ||||
|         .valp  = &glob_conf.wav_path, | ||||
|         .valp  = &conf.wav_path, | ||||
|         .descr = "Path to wave file" | ||||
|     }, | ||||
|     { /* End of list */ } | ||||
|   | ||||
| @@ -1,7 +1,5 @@ | ||||
| #include "qemu/osdep.h" | ||||
| #include "hw/hw.h" | ||||
| #include "monitor/monitor.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "audio.h" | ||||
|  | ||||
| typedef struct { | ||||
|   | ||||
							
								
								
									
										717
									
								
								audio/winwaveaudio.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										717
									
								
								audio/winwaveaudio.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,717 @@ | ||||
| /* public domain */ | ||||
|  | ||||
| #include "qemu-common.h" | ||||
| #include "sysemu/sysemu.h" | ||||
| #include "audio.h" | ||||
|  | ||||
| #define AUDIO_CAP "winwave" | ||||
| #include "audio_int.h" | ||||
|  | ||||
| #include <windows.h> | ||||
| #include <mmsystem.h> | ||||
|  | ||||
| #include "audio_win_int.h" | ||||
|  | ||||
| static struct { | ||||
|     int dac_headers; | ||||
|     int dac_samples; | ||||
|     int adc_headers; | ||||
|     int adc_samples; | ||||
| } conf = { | ||||
|     .dac_headers = 4, | ||||
|     .dac_samples = 1024, | ||||
|     .adc_headers = 4, | ||||
|     .adc_samples = 1024 | ||||
| }; | ||||
|  | ||||
| typedef struct { | ||||
|     HWVoiceOut hw; | ||||
|     HWAVEOUT hwo; | ||||
|     WAVEHDR *hdrs; | ||||
|     HANDLE event; | ||||
|     void *pcm_buf; | ||||
|     int avail; | ||||
|     int pending; | ||||
|     int curhdr; | ||||
|     int paused; | ||||
|     CRITICAL_SECTION crit_sect; | ||||
| } WaveVoiceOut; | ||||
|  | ||||
| typedef struct { | ||||
|     HWVoiceIn hw; | ||||
|     HWAVEIN hwi; | ||||
|     WAVEHDR *hdrs; | ||||
|     HANDLE event; | ||||
|     void *pcm_buf; | ||||
|     int curhdr; | ||||
|     int paused; | ||||
|     int rpos; | ||||
|     int avail; | ||||
|     CRITICAL_SECTION crit_sect; | ||||
| } WaveVoiceIn; | ||||
|  | ||||
| static void winwave_log_mmresult (MMRESULT mr) | ||||
| { | ||||
|     const char *str = "BUG"; | ||||
|  | ||||
|     switch (mr) { | ||||
|     case MMSYSERR_NOERROR: | ||||
|         str = "Success"; | ||||
|         break; | ||||
|  | ||||
|     case MMSYSERR_INVALHANDLE: | ||||
|         str = "Specified device handle is invalid"; | ||||
|         break; | ||||
|  | ||||
|     case MMSYSERR_BADDEVICEID: | ||||
|         str = "Specified device id is out of range"; | ||||
|         break; | ||||
|  | ||||
|     case MMSYSERR_NODRIVER: | ||||
|         str = "No device driver is present"; | ||||
|         break; | ||||
|  | ||||
|     case MMSYSERR_NOMEM: | ||||
|         str = "Unable to allocate or lock memory"; | ||||
|         break; | ||||
|  | ||||
|     case WAVERR_SYNC: | ||||
|         str = "Device is synchronous but waveOutOpen was called " | ||||
|             "without using the WINWAVE_ALLOWSYNC flag"; | ||||
|         break; | ||||
|  | ||||
|     case WAVERR_UNPREPARED: | ||||
|         str = "The data block pointed to by the pwh parameter " | ||||
|             "hasn't been prepared"; | ||||
|         break; | ||||
|  | ||||
|     case WAVERR_STILLPLAYING: | ||||
|         str = "There are still buffers in the queue"; | ||||
|         break; | ||||
|  | ||||
|     default: | ||||
|         dolog ("Reason: Unknown (MMRESULT %#x)\n", mr); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     dolog ("Reason: %s\n", str); | ||||
| } | ||||
|  | ||||
| static void GCC_FMT_ATTR (2, 3) winwave_logerr ( | ||||
|     MMRESULT mr, | ||||
|     const char *fmt, | ||||
|     ... | ||||
|     ) | ||||
| { | ||||
|     va_list ap; | ||||
|  | ||||
|     va_start (ap, fmt); | ||||
|     AUD_vlog (AUDIO_CAP, fmt, ap); | ||||
|     va_end (ap); | ||||
|  | ||||
|     AUD_log (NULL, " failed\n"); | ||||
|     winwave_log_mmresult (mr); | ||||
| } | ||||
|  | ||||
| static void winwave_anal_close_out (WaveVoiceOut *wave) | ||||
| { | ||||
|     MMRESULT mr; | ||||
|  | ||||
|     mr = waveOutClose (wave->hwo); | ||||
|     if (mr != MMSYSERR_NOERROR) { | ||||
|         winwave_logerr (mr, "waveOutClose"); | ||||
|     } | ||||
|     wave->hwo = NULL; | ||||
| } | ||||
|  | ||||
| static void CALLBACK winwave_callback_out ( | ||||
|     HWAVEOUT hwo, | ||||
|     UINT msg, | ||||
|     DWORD_PTR dwInstance, | ||||
|     DWORD_PTR dwParam1, | ||||
|     DWORD_PTR dwParam2 | ||||
|     ) | ||||
| { | ||||
|     WaveVoiceOut *wave = (WaveVoiceOut *) dwInstance; | ||||
|  | ||||
|     switch (msg) { | ||||
|     case WOM_DONE: | ||||
|         { | ||||
|             WAVEHDR *h = (WAVEHDR *) dwParam1; | ||||
|             if (!h->dwUser) { | ||||
|                 h->dwUser = 1; | ||||
|                 EnterCriticalSection (&wave->crit_sect); | ||||
|                 { | ||||
|                     wave->avail += conf.dac_samples; | ||||
|                 } | ||||
|                 LeaveCriticalSection (&wave->crit_sect); | ||||
|                 if (wave->hw.poll_mode) { | ||||
|                     if (!SetEvent (wave->event)) { | ||||
|                         dolog ("DAC SetEvent failed %lx\n", GetLastError ()); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         break; | ||||
|  | ||||
|     case WOM_CLOSE: | ||||
|     case WOM_OPEN: | ||||
|         break; | ||||
|  | ||||
|     default: | ||||
|         dolog ("unknown wave out callback msg %x\n", msg); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
| { | ||||
|     int i; | ||||
|     int err; | ||||
|     MMRESULT mr; | ||||
|     WAVEFORMATEX wfx; | ||||
|     WaveVoiceOut *wave; | ||||
|  | ||||
|     wave = (WaveVoiceOut *) hw; | ||||
|  | ||||
|     InitializeCriticalSection (&wave->crit_sect); | ||||
|  | ||||
|     err = waveformat_from_audio_settings (&wfx, as); | ||||
|     if (err) { | ||||
|         goto err0; | ||||
|     } | ||||
|  | ||||
|     mr = waveOutOpen (&wave->hwo, WAVE_MAPPER, &wfx, | ||||
|                       (DWORD_PTR) winwave_callback_out, | ||||
|                       (DWORD_PTR) wave, CALLBACK_FUNCTION); | ||||
|     if (mr != MMSYSERR_NOERROR) { | ||||
|         winwave_logerr (mr, "waveOutOpen"); | ||||
|         goto err1; | ||||
|     } | ||||
|  | ||||
|     wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers, | ||||
|                                sizeof (*wave->hdrs)); | ||||
|     if (!wave->hdrs) { | ||||
|         goto err2; | ||||
|     } | ||||
|  | ||||
|     audio_pcm_init_info (&hw->info, as); | ||||
|     hw->samples = conf.dac_samples * conf.dac_headers; | ||||
|     wave->avail = hw->samples; | ||||
|  | ||||
|     wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.dac_samples, | ||||
|                                   conf.dac_headers << hw->info.shift); | ||||
|     if (!wave->pcm_buf) { | ||||
|         goto err3; | ||||
|     } | ||||
|  | ||||
|     for (i = 0; i < conf.dac_headers; ++i) { | ||||
|         WAVEHDR *h = &wave->hdrs[i]; | ||||
|  | ||||
|         h->dwUser = 0; | ||||
|         h->dwBufferLength = conf.dac_samples << hw->info.shift; | ||||
|         h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength); | ||||
|         h->dwFlags = 0; | ||||
|  | ||||
|         mr = waveOutPrepareHeader (wave->hwo, h, sizeof (*h)); | ||||
|         if (mr != MMSYSERR_NOERROR) { | ||||
|             winwave_logerr (mr, "waveOutPrepareHeader(%d)", i); | ||||
|             goto err4; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
|  | ||||
|  err4: | ||||
|     g_free (wave->pcm_buf); | ||||
|  err3: | ||||
|     g_free (wave->hdrs); | ||||
|  err2: | ||||
|     winwave_anal_close_out (wave); | ||||
|  err1: | ||||
|  err0: | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static int winwave_write (SWVoiceOut *sw, void *buf, int len) | ||||
| { | ||||
|     return audio_pcm_sw_write (sw, buf, len); | ||||
| } | ||||
|  | ||||
| static int winwave_run_out (HWVoiceOut *hw, int live) | ||||
| { | ||||
|     WaveVoiceOut *wave = (WaveVoiceOut *) hw; | ||||
|     int decr; | ||||
|     int doreset; | ||||
|  | ||||
|     EnterCriticalSection (&wave->crit_sect); | ||||
|     { | ||||
|         decr = audio_MIN (live, wave->avail); | ||||
|         decr = audio_pcm_hw_clip_out (hw, wave->pcm_buf, decr, wave->pending); | ||||
|         wave->pending += decr; | ||||
|         wave->avail -= decr; | ||||
|     } | ||||
|     LeaveCriticalSection (&wave->crit_sect); | ||||
|  | ||||
|     doreset = hw->poll_mode && (wave->pending >= conf.dac_samples); | ||||
|     if (doreset && !ResetEvent (wave->event)) { | ||||
|         dolog ("DAC ResetEvent failed %lx\n", GetLastError ()); | ||||
|     } | ||||
|  | ||||
|     while (wave->pending >= conf.dac_samples) { | ||||
|         MMRESULT mr; | ||||
|         WAVEHDR *h = &wave->hdrs[wave->curhdr]; | ||||
|  | ||||
|         h->dwUser = 0; | ||||
|         mr = waveOutWrite (wave->hwo, h, sizeof (*h)); | ||||
|         if (mr != MMSYSERR_NOERROR) { | ||||
|             winwave_logerr (mr, "waveOutWrite(%d)", wave->curhdr); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         wave->pending -= conf.dac_samples; | ||||
|         wave->curhdr = (wave->curhdr + 1) % conf.dac_headers; | ||||
|     } | ||||
|  | ||||
|     return decr; | ||||
| } | ||||
|  | ||||
| static void winwave_poll (void *opaque) | ||||
| { | ||||
|     (void) opaque; | ||||
|     audio_run ("winwave_poll"); | ||||
| } | ||||
|  | ||||
| static void winwave_fini_out (HWVoiceOut *hw) | ||||
| { | ||||
|     int i; | ||||
|     MMRESULT mr; | ||||
|     WaveVoiceOut *wave = (WaveVoiceOut *) hw; | ||||
|  | ||||
|     mr = waveOutReset (wave->hwo); | ||||
|     if (mr != MMSYSERR_NOERROR) { | ||||
|         winwave_logerr (mr, "waveOutReset"); | ||||
|     } | ||||
|  | ||||
|     for (i = 0; i < conf.dac_headers; ++i) { | ||||
|         mr = waveOutUnprepareHeader (wave->hwo, &wave->hdrs[i], | ||||
|                                      sizeof (wave->hdrs[i])); | ||||
|         if (mr != MMSYSERR_NOERROR) { | ||||
|             winwave_logerr (mr, "waveOutUnprepareHeader(%d)", i); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     winwave_anal_close_out (wave); | ||||
|  | ||||
|     if (wave->event) { | ||||
|         qemu_del_wait_object (wave->event, winwave_poll, wave); | ||||
|         if (!CloseHandle (wave->event)) { | ||||
|             dolog ("DAC CloseHandle failed %lx\n", GetLastError ()); | ||||
|         } | ||||
|         wave->event = NULL; | ||||
|     } | ||||
|  | ||||
|     g_free (wave->pcm_buf); | ||||
|     wave->pcm_buf = NULL; | ||||
|  | ||||
|     g_free (wave->hdrs); | ||||
|     wave->hdrs = NULL; | ||||
| } | ||||
|  | ||||
| static int winwave_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
| { | ||||
|     MMRESULT mr; | ||||
|     WaveVoiceOut *wave = (WaveVoiceOut *) hw; | ||||
|  | ||||
|     switch (cmd) { | ||||
|     case VOICE_ENABLE: | ||||
|         { | ||||
|             va_list ap; | ||||
|             int poll_mode; | ||||
|  | ||||
|             va_start (ap, cmd); | ||||
|             poll_mode = va_arg (ap, int); | ||||
|             va_end (ap); | ||||
|  | ||||
|             if (poll_mode && !wave->event) { | ||||
|                 wave->event = CreateEvent (NULL, TRUE, TRUE, NULL); | ||||
|                 if (!wave->event) { | ||||
|                     dolog ("DAC CreateEvent: %lx, poll mode will be disabled\n", | ||||
|                            GetLastError ()); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (wave->event) { | ||||
|                 int ret; | ||||
|  | ||||
|                 ret = qemu_add_wait_object (wave->event, winwave_poll, wave); | ||||
|                 hw->poll_mode = (ret == 0); | ||||
|             } | ||||
|             else { | ||||
|                 hw->poll_mode = 0; | ||||
|             } | ||||
|             wave->paused = 0; | ||||
|         } | ||||
|         return 0; | ||||
|  | ||||
|     case VOICE_DISABLE: | ||||
|         if (!wave->paused) { | ||||
|             mr = waveOutReset (wave->hwo); | ||||
|             if (mr != MMSYSERR_NOERROR) { | ||||
|                 winwave_logerr (mr, "waveOutReset"); | ||||
|             } | ||||
|             else { | ||||
|                 wave->paused = 1; | ||||
|             } | ||||
|         } | ||||
|         if (wave->event) { | ||||
|             qemu_del_wait_object (wave->event, winwave_poll, wave); | ||||
|         } | ||||
|         return 0; | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static void winwave_anal_close_in (WaveVoiceIn *wave) | ||||
| { | ||||
|     MMRESULT mr; | ||||
|  | ||||
|     mr = waveInClose (wave->hwi); | ||||
|     if (mr != MMSYSERR_NOERROR) { | ||||
|         winwave_logerr (mr, "waveInClose"); | ||||
|     } | ||||
|     wave->hwi = NULL; | ||||
| } | ||||
|  | ||||
| static void CALLBACK winwave_callback_in ( | ||||
|     HWAVEIN *hwi, | ||||
|     UINT msg, | ||||
|     DWORD_PTR dwInstance, | ||||
|     DWORD_PTR dwParam1, | ||||
|     DWORD_PTR dwParam2 | ||||
|     ) | ||||
| { | ||||
|     WaveVoiceIn *wave = (WaveVoiceIn *) dwInstance; | ||||
|  | ||||
|     switch (msg) { | ||||
|     case WIM_DATA: | ||||
|         { | ||||
|             WAVEHDR *h = (WAVEHDR *) dwParam1; | ||||
|             if (!h->dwUser) { | ||||
|                 h->dwUser = 1; | ||||
|                 EnterCriticalSection (&wave->crit_sect); | ||||
|                 { | ||||
|                     wave->avail += conf.adc_samples; | ||||
|                 } | ||||
|                 LeaveCriticalSection (&wave->crit_sect); | ||||
|                 if (wave->hw.poll_mode) { | ||||
|                     if (!SetEvent (wave->event)) { | ||||
|                         dolog ("ADC SetEvent failed %lx\n", GetLastError ()); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         break; | ||||
|  | ||||
|     case WIM_CLOSE: | ||||
|     case WIM_OPEN: | ||||
|         break; | ||||
|  | ||||
|     default: | ||||
|         dolog ("unknown wave in callback msg %x\n", msg); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void winwave_add_buffers (WaveVoiceIn *wave, int samples) | ||||
| { | ||||
|     int doreset; | ||||
|  | ||||
|     doreset = wave->hw.poll_mode && (samples >= conf.adc_samples); | ||||
|     if (doreset && !ResetEvent (wave->event)) { | ||||
|         dolog ("ADC ResetEvent failed %lx\n", GetLastError ()); | ||||
|     } | ||||
|  | ||||
|     while (samples >= conf.adc_samples) { | ||||
|         MMRESULT mr; | ||||
|         WAVEHDR *h = &wave->hdrs[wave->curhdr]; | ||||
|  | ||||
|         h->dwUser = 0; | ||||
|         mr = waveInAddBuffer (wave->hwi, h, sizeof (*h)); | ||||
|         if (mr != MMSYSERR_NOERROR) { | ||||
|             winwave_logerr (mr, "waveInAddBuffer(%d)", wave->curhdr); | ||||
|         } | ||||
|         wave->curhdr = (wave->curhdr + 1) % conf.adc_headers; | ||||
|         samples -= conf.adc_samples; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int winwave_init_in (HWVoiceIn *hw, struct audsettings *as) | ||||
| { | ||||
|     int i; | ||||
|     int err; | ||||
|     MMRESULT mr; | ||||
|     WAVEFORMATEX wfx; | ||||
|     WaveVoiceIn *wave; | ||||
|  | ||||
|     wave = (WaveVoiceIn *) hw; | ||||
|  | ||||
|     InitializeCriticalSection (&wave->crit_sect); | ||||
|  | ||||
|     err = waveformat_from_audio_settings (&wfx, as); | ||||
|     if (err) { | ||||
|         goto err0; | ||||
|     } | ||||
|  | ||||
|     mr = waveInOpen (&wave->hwi, WAVE_MAPPER, &wfx, | ||||
|                      (DWORD_PTR) winwave_callback_in, | ||||
|                      (DWORD_PTR) wave, CALLBACK_FUNCTION); | ||||
|     if (mr != MMSYSERR_NOERROR) { | ||||
|         winwave_logerr (mr, "waveInOpen"); | ||||
|         goto err1; | ||||
|     } | ||||
|  | ||||
|     wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers, | ||||
|                                sizeof (*wave->hdrs)); | ||||
|     if (!wave->hdrs) { | ||||
|         goto err2; | ||||
|     } | ||||
|  | ||||
|     audio_pcm_init_info (&hw->info, as); | ||||
|     hw->samples = conf.adc_samples * conf.adc_headers; | ||||
|     wave->avail = 0; | ||||
|  | ||||
|     wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.adc_samples, | ||||
|                                   conf.adc_headers << hw->info.shift); | ||||
|     if (!wave->pcm_buf) { | ||||
|         goto err3; | ||||
|     } | ||||
|  | ||||
|     for (i = 0; i < conf.adc_headers; ++i) { | ||||
|         WAVEHDR *h = &wave->hdrs[i]; | ||||
|  | ||||
|         h->dwUser = 0; | ||||
|         h->dwBufferLength = conf.adc_samples << hw->info.shift; | ||||
|         h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength); | ||||
|         h->dwFlags = 0; | ||||
|  | ||||
|         mr = waveInPrepareHeader (wave->hwi, h, sizeof (*h)); | ||||
|         if (mr != MMSYSERR_NOERROR) { | ||||
|             winwave_logerr (mr, "waveInPrepareHeader(%d)", i); | ||||
|             goto err4; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     wave->paused = 1; | ||||
|     winwave_add_buffers (wave, hw->samples); | ||||
|     return 0; | ||||
|  | ||||
|  err4: | ||||
|     g_free (wave->pcm_buf); | ||||
|  err3: | ||||
|     g_free (wave->hdrs); | ||||
|  err2: | ||||
|     winwave_anal_close_in (wave); | ||||
|  err1: | ||||
|  err0: | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static void winwave_fini_in (HWVoiceIn *hw) | ||||
| { | ||||
|     int i; | ||||
|     MMRESULT mr; | ||||
|     WaveVoiceIn *wave = (WaveVoiceIn *) hw; | ||||
|  | ||||
|     mr = waveInReset (wave->hwi); | ||||
|     if (mr != MMSYSERR_NOERROR) { | ||||
|         winwave_logerr (mr, "waveInReset"); | ||||
|     } | ||||
|  | ||||
|     for (i = 0; i < conf.adc_headers; ++i) { | ||||
|         mr = waveInUnprepareHeader (wave->hwi, &wave->hdrs[i], | ||||
|                                      sizeof (wave->hdrs[i])); | ||||
|         if (mr != MMSYSERR_NOERROR) { | ||||
|             winwave_logerr (mr, "waveInUnprepareHeader(%d)", i); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     winwave_anal_close_in (wave); | ||||
|  | ||||
|     if (wave->event) { | ||||
|         qemu_del_wait_object (wave->event, winwave_poll, wave); | ||||
|         if (!CloseHandle (wave->event)) { | ||||
|             dolog ("ADC CloseHandle failed %lx\n", GetLastError ()); | ||||
|         } | ||||
|         wave->event = NULL; | ||||
|     } | ||||
|  | ||||
|     g_free (wave->pcm_buf); | ||||
|     wave->pcm_buf = NULL; | ||||
|  | ||||
|     g_free (wave->hdrs); | ||||
|     wave->hdrs = NULL; | ||||
| } | ||||
|  | ||||
| static int winwave_run_in (HWVoiceIn *hw) | ||||
| { | ||||
|     WaveVoiceIn *wave = (WaveVoiceIn *) hw; | ||||
|     int live = audio_pcm_hw_get_live_in (hw); | ||||
|     int dead = hw->samples - live; | ||||
|     int decr, ret; | ||||
|  | ||||
|     if (!dead) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     EnterCriticalSection (&wave->crit_sect); | ||||
|     { | ||||
|         decr = audio_MIN (dead, wave->avail); | ||||
|         wave->avail -= decr; | ||||
|     } | ||||
|     LeaveCriticalSection (&wave->crit_sect); | ||||
|  | ||||
|     ret = decr; | ||||
|     while (decr) { | ||||
|         int left = hw->samples - hw->wpos; | ||||
|         int conv = audio_MIN (left, decr); | ||||
|         hw->conv (hw->conv_buf + hw->wpos, | ||||
|                   advance (wave->pcm_buf, wave->rpos << hw->info.shift), | ||||
|                   conv); | ||||
|  | ||||
|         wave->rpos = (wave->rpos + conv) % hw->samples; | ||||
|         hw->wpos = (hw->wpos + conv) % hw->samples; | ||||
|         decr -= conv; | ||||
|     } | ||||
|  | ||||
|     winwave_add_buffers (wave, ret); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static int winwave_read (SWVoiceIn *sw, void *buf, int size) | ||||
| { | ||||
|     return audio_pcm_sw_read (sw, buf, size); | ||||
| } | ||||
|  | ||||
| static int winwave_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||
| { | ||||
|     MMRESULT mr; | ||||
|     WaveVoiceIn *wave = (WaveVoiceIn *) hw; | ||||
|  | ||||
|     switch (cmd) { | ||||
|     case VOICE_ENABLE: | ||||
|         { | ||||
|             va_list ap; | ||||
|             int poll_mode; | ||||
|  | ||||
|             va_start (ap, cmd); | ||||
|             poll_mode = va_arg (ap, int); | ||||
|             va_end (ap); | ||||
|  | ||||
|             if (poll_mode && !wave->event) { | ||||
|                 wave->event = CreateEvent (NULL, TRUE, TRUE, NULL); | ||||
|                 if (!wave->event) { | ||||
|                     dolog ("ADC CreateEvent: %lx, poll mode will be disabled\n", | ||||
|                            GetLastError ()); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (wave->event) { | ||||
|                 int ret; | ||||
|  | ||||
|                 ret = qemu_add_wait_object (wave->event, winwave_poll, wave); | ||||
|                 hw->poll_mode = (ret == 0); | ||||
|             } | ||||
|             else { | ||||
|                 hw->poll_mode = 0; | ||||
|             } | ||||
|             if (wave->paused) { | ||||
|                 mr = waveInStart (wave->hwi); | ||||
|                 if (mr != MMSYSERR_NOERROR) { | ||||
|                     winwave_logerr (mr, "waveInStart"); | ||||
|                 } | ||||
|                 wave->paused = 0; | ||||
|             } | ||||
|         } | ||||
|         return 0; | ||||
|  | ||||
|     case VOICE_DISABLE: | ||||
|         if (!wave->paused) { | ||||
|             mr = waveInStop (wave->hwi); | ||||
|             if (mr != MMSYSERR_NOERROR) { | ||||
|                 winwave_logerr (mr, "waveInStop"); | ||||
|             } | ||||
|             else { | ||||
|                 wave->paused = 1; | ||||
|             } | ||||
|         } | ||||
|         if (wave->event) { | ||||
|             qemu_del_wait_object (wave->event, winwave_poll, wave); | ||||
|         } | ||||
|         return 0; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void *winwave_audio_init (void) | ||||
| { | ||||
|     return &conf; | ||||
| } | ||||
|  | ||||
| static void winwave_audio_fini (void *opaque) | ||||
| { | ||||
|     (void) opaque; | ||||
| } | ||||
|  | ||||
| static struct audio_option winwave_options[] = { | ||||
|     { | ||||
|         .name        = "DAC_HEADERS", | ||||
|         .tag         = AUD_OPT_INT, | ||||
|         .valp        = &conf.dac_headers, | ||||
|         .descr       = "DAC number of headers", | ||||
|     }, | ||||
|     { | ||||
|         .name        = "DAC_SAMPLES", | ||||
|         .tag         = AUD_OPT_INT, | ||||
|         .valp        = &conf.dac_samples, | ||||
|         .descr       = "DAC number of samples per header", | ||||
|     }, | ||||
|     { | ||||
|         .name        = "ADC_HEADERS", | ||||
|         .tag         = AUD_OPT_INT, | ||||
|         .valp        = &conf.adc_headers, | ||||
|         .descr       = "ADC number of headers", | ||||
|     }, | ||||
|     { | ||||
|         .name        = "ADC_SAMPLES", | ||||
|         .tag         = AUD_OPT_INT, | ||||
|         .valp        = &conf.adc_samples, | ||||
|         .descr       = "ADC number of samples per header", | ||||
|     }, | ||||
|     { /* End of list */ } | ||||
| }; | ||||
|  | ||||
| static struct audio_pcm_ops winwave_pcm_ops = { | ||||
|     .init_out = winwave_init_out, | ||||
|     .fini_out = winwave_fini_out, | ||||
|     .run_out  = winwave_run_out, | ||||
|     .write    = winwave_write, | ||||
|     .ctl_out  = winwave_ctl_out, | ||||
|     .init_in  = winwave_init_in, | ||||
|     .fini_in  = winwave_fini_in, | ||||
|     .run_in   = winwave_run_in, | ||||
|     .read     = winwave_read, | ||||
|     .ctl_in   = winwave_ctl_in | ||||
| }; | ||||
|  | ||||
| struct audio_driver winwave_audio_driver = { | ||||
|     .name           = "winwave", | ||||
|     .descr          = "Windows Waveform Audio http://msdn.microsoft.com", | ||||
|     .options        = winwave_options, | ||||
|     .init           = winwave_audio_init, | ||||
|     .fini           = winwave_audio_fini, | ||||
|     .pcm_ops        = &winwave_pcm_ops, | ||||
|     .can_be_default = 1, | ||||
|     .max_voices_out = INT_MAX, | ||||
|     .max_voices_in  = INT_MAX, | ||||
|     .voice_size_out = sizeof (WaveVoiceOut), | ||||
|     .voice_size_in  = sizeof (WaveVoiceIn) | ||||
| }; | ||||
| @@ -21,7 +21,6 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "sysemu/char.h" | ||||
| #include "qemu/timer.h" | ||||
| @@ -304,7 +303,7 @@ static int baum_eat_packet(BaumDriverState *baum, const uint8_t *buf, int len) | ||||
|                 return 0; | ||||
|             cur++; | ||||
|         } | ||||
|         DPRINTF("Dropped %td bytes!\n", cur - buf); | ||||
|         DPRINTF("Dropped %d bytes!\n", cur - buf); | ||||
|     } | ||||
|  | ||||
| #define EAT(c) do {\ | ||||
| @@ -562,12 +561,8 @@ static void baum_close(struct CharDriverState *chr) | ||||
|     g_free(baum); | ||||
| } | ||||
|  | ||||
| static CharDriverState *chr_baum_init(const char *id, | ||||
|                                       ChardevBackend *backend, | ||||
|                                       ChardevReturn *ret, | ||||
|                                       Error **errp) | ||||
| CharDriverState *chr_baum_init(void) | ||||
| { | ||||
|     ChardevCommon *common = qapi_ChardevDummy_base(backend->u.braille); | ||||
|     BaumDriverState *baum; | ||||
|     CharDriverState *chr; | ||||
|     brlapi_handle_t *handle; | ||||
| @@ -578,12 +573,8 @@ static CharDriverState *chr_baum_init(const char *id, | ||||
| #endif | ||||
|     int tty; | ||||
|  | ||||
|     chr = qemu_chr_alloc(common, errp); | ||||
|     if (!chr) { | ||||
|         return NULL; | ||||
|     } | ||||
|     baum = g_malloc0(sizeof(BaumDriverState)); | ||||
|     baum->chr = chr; | ||||
|     baum->chr = chr = qemu_chr_alloc(); | ||||
|  | ||||
|     chr->opaque = baum; | ||||
|     chr->chr_write = baum_write; | ||||
| @@ -595,16 +586,14 @@ static CharDriverState *chr_baum_init(const char *id, | ||||
|  | ||||
|     baum->brlapi_fd = brlapi__openConnection(handle, NULL, NULL); | ||||
|     if (baum->brlapi_fd == -1) { | ||||
|         error_setg(errp, "brlapi__openConnection: %s", | ||||
|                    brlapi_strerror(brlapi_error_location())); | ||||
|         brlapi_perror("baum_init: brlapi_openConnection"); | ||||
|         goto fail_handle; | ||||
|     } | ||||
|  | ||||
|     baum->cellCount_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, baum_cellCount_timer_cb, baum); | ||||
|  | ||||
|     if (brlapi__getDisplaySize(handle, &baum->x, &baum->y) == -1) { | ||||
|         error_setg(errp, "brlapi__getDisplaySize: %s", | ||||
|                    brlapi_strerror(brlapi_error_location())); | ||||
|         brlapi_perror("baum_init: brlapi_getDisplaySize"); | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
| @@ -620,8 +609,7 @@ static CharDriverState *chr_baum_init(const char *id, | ||||
|         tty = BRLAPI_TTY_DEFAULT; | ||||
|  | ||||
|     if (brlapi__enterTtyMode(handle, tty, NULL) == -1) { | ||||
|         error_setg(errp, "brlapi__enterTtyMode: %s", | ||||
|                    brlapi_strerror(brlapi_error_location())); | ||||
|         brlapi_perror("baum_init: brlapi_enterTtyMode"); | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
| @@ -641,8 +629,7 @@ fail_handle: | ||||
|  | ||||
| static void register_types(void) | ||||
| { | ||||
|     register_char_driver("braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL, | ||||
|                          chr_baum_init); | ||||
|     register_char_driver("braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL); | ||||
| } | ||||
|  | ||||
| type_init(register_types); | ||||
|   | ||||
| @@ -9,7 +9,6 @@ | ||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||||
|  * See the COPYING file in the top-level directory. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "sysemu/hostmem.h" | ||||
| #include "sysemu/sysemu.h" | ||||
| @@ -44,7 +43,7 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | ||||
|         return; | ||||
|     } | ||||
|     if (!fb->mem_path) { | ||||
|         error_setg(errp, "mem-path property not set"); | ||||
|         error_setg(errp, "mem_path property not set"); | ||||
|         return; | ||||
|     } | ||||
| #ifndef CONFIG_LINUX | ||||
| @@ -84,7 +83,9 @@ static void set_mem_path(Object *o, const char *str, Error **errp) | ||||
|         error_setg(errp, "cannot change property value"); | ||||
|         return; | ||||
|     } | ||||
|     if (fb->mem_path) { | ||||
|         g_free(fb->mem_path); | ||||
|     } | ||||
|     fb->mem_path = g_strdup(str); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,6 @@ | ||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||||
|  * See the COPYING file in the top-level directory. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "sysemu/hostmem.h" | ||||
| #include "qom/object_interfaces.h" | ||||
|  | ||||
|   | ||||
| @@ -9,12 +9,11 @@ | ||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||||
|  * See the COPYING file in the top-level directory. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "sysemu/hostmem.h" | ||||
| #include "hw/boards.h" | ||||
| #include "qapi/visitor.h" | ||||
| #include "qapi-types.h" | ||||
| #include "qapi-visit.h" | ||||
| #include "qapi/qmp/qerror.h" | ||||
| #include "qemu/config-file.h" | ||||
| #include "qom/object_interfaces.h" | ||||
|  | ||||
| @@ -27,18 +26,18 @@ QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE); | ||||
| #endif | ||||
|  | ||||
| static void | ||||
| host_memory_backend_get_size(Object *obj, Visitor *v, const char *name, | ||||
|                              void *opaque, Error **errp) | ||||
| host_memory_backend_get_size(Object *obj, Visitor *v, void *opaque, | ||||
|                              const char *name, Error **errp) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||
|     uint64_t value = backend->size; | ||||
|  | ||||
|     visit_type_size(v, name, &value, errp); | ||||
|     visit_type_size(v, &value, name, errp); | ||||
| } | ||||
|  | ||||
| static void | ||||
| host_memory_backend_set_size(Object *obj, Visitor *v, const char *name, | ||||
|                              void *opaque, Error **errp) | ||||
| host_memory_backend_set_size(Object *obj, Visitor *v, void *opaque, | ||||
|                              const char *name, Error **errp) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||
|     Error *local_err = NULL; | ||||
| @@ -49,7 +48,7 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name, | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     visit_type_size(v, name, &value, &local_err); | ||||
|     visit_type_size(v, &value, name, &local_err); | ||||
|     if (local_err) { | ||||
|         goto out; | ||||
|     } | ||||
| @@ -64,8 +63,8 @@ out: | ||||
| } | ||||
|  | ||||
| static void | ||||
| host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name, | ||||
|                                    void *opaque, Error **errp) | ||||
| host_memory_backend_get_host_nodes(Object *obj, Visitor *v, void *opaque, | ||||
|                                    const char *name, Error **errp) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||
|     uint16List *host_nodes = NULL; | ||||
| @@ -92,18 +91,18 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name, | ||||
|         node = &(*node)->next; | ||||
|     } while (true); | ||||
|  | ||||
|     visit_type_uint16List(v, name, &host_nodes, errp); | ||||
|     visit_type_uint16List(v, &host_nodes, name, errp); | ||||
| } | ||||
|  | ||||
| static void | ||||
| host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name, | ||||
|                                    void *opaque, Error **errp) | ||||
| host_memory_backend_set_host_nodes(Object *obj, Visitor *v, void *opaque, | ||||
|                                    const char *name, Error **errp) | ||||
| { | ||||
| #ifdef CONFIG_NUMA | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||
|     uint16List *l = NULL; | ||||
|  | ||||
|     visit_type_uint16List(v, name, &l, errp); | ||||
|     visit_type_uint16List(v, &l, name, errp); | ||||
|  | ||||
|     while (l) { | ||||
|         bitmap_set(backend->host_nodes, l->value, 1); | ||||
| @@ -114,17 +113,24 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name, | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static int | ||||
| host_memory_backend_get_policy(Object *obj, Error **errp G_GNUC_UNUSED) | ||||
| static void | ||||
| host_memory_backend_get_policy(Object *obj, Visitor *v, void *opaque, | ||||
|                                const char *name, Error **errp) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||
|     return backend->policy; | ||||
|     int policy = backend->policy; | ||||
|  | ||||
|     visit_type_enum(v, &policy, HostMemPolicy_lookup, NULL, name, errp); | ||||
| } | ||||
|  | ||||
| static void | ||||
| host_memory_backend_set_policy(Object *obj, int policy, Error **errp) | ||||
| host_memory_backend_set_policy(Object *obj, Visitor *v, void *opaque, | ||||
|                                const char *name, Error **errp) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||
|     int policy; | ||||
|  | ||||
|     visit_type_enum(v, &policy, HostMemPolicy_lookup, NULL, name, errp); | ||||
|     backend->policy = policy; | ||||
|  | ||||
| #ifndef CONFIG_NUMA | ||||
| @@ -224,10 +230,11 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value, | ||||
| static void host_memory_backend_init(Object *obj) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||
|     MachineState *machine = MACHINE(qdev_get_machine()); | ||||
|  | ||||
|     backend->merge = machine_mem_merge(machine); | ||||
|     backend->dump = machine_dump_guest_core(machine); | ||||
|     backend->merge = qemu_opt_get_bool(qemu_get_machine_opts(), | ||||
|                                        "mem-merge", true); | ||||
|     backend->dump = qemu_opt_get_bool(qemu_get_machine_opts(), | ||||
|                                       "dump-guest-core", true); | ||||
|     backend->prealloc = mem_prealloc; | ||||
|  | ||||
|     object_property_add_bool(obj, "merge", | ||||
| @@ -245,10 +252,9 @@ static void host_memory_backend_init(Object *obj) | ||||
|     object_property_add(obj, "host-nodes", "int", | ||||
|                         host_memory_backend_get_host_nodes, | ||||
|                         host_memory_backend_set_host_nodes, NULL, NULL, NULL); | ||||
|     object_property_add_enum(obj, "policy", "HostMemPolicy", | ||||
|                              HostMemPolicy_lookup, | ||||
|     object_property_add(obj, "policy", "str", | ||||
|                         host_memory_backend_get_policy, | ||||
|                              host_memory_backend_set_policy, NULL); | ||||
|                         host_memory_backend_set_policy, NULL, NULL, NULL); | ||||
| } | ||||
|  | ||||
| MemoryRegion * | ||||
| @@ -314,12 +320,10 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) | ||||
|         assert(maxnode <= MAX_NODES); | ||||
|         if (mbind(ptr, sz, backend->policy, | ||||
|                   maxnode ? backend->host_nodes : NULL, maxnode + 1, flags)) { | ||||
|             if (backend->policy != MPOL_DEFAULT || errno != ENOSYS) { | ||||
|             error_setg_errno(errp, errno, | ||||
|                              "cannot bind memory to host NUMA nodes"); | ||||
|             return; | ||||
|         } | ||||
|         } | ||||
| #endif | ||||
|         /* Preallocate memory after the NUMA policy has been instantiated. | ||||
|          * This is necessary to guarantee memory is allocated with | ||||
|   | ||||
| @@ -21,7 +21,7 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include <stdlib.h> | ||||
| #include "qemu-common.h" | ||||
| #include "sysemu/char.h" | ||||
| #include "ui/console.h" | ||||
| @@ -63,18 +63,11 @@ static void msmouse_chr_close (struct CharDriverState *chr) | ||||
|     g_free (chr); | ||||
| } | ||||
|  | ||||
| static CharDriverState *qemu_chr_open_msmouse(const char *id, | ||||
|                                               ChardevBackend *backend, | ||||
|                                               ChardevReturn *ret, | ||||
|                                               Error **errp) | ||||
| CharDriverState *qemu_chr_open_msmouse(void) | ||||
| { | ||||
|     ChardevCommon *common = qapi_ChardevDummy_base(backend->u.msmouse); | ||||
|     CharDriverState *chr; | ||||
|  | ||||
|     chr = qemu_chr_alloc(common, errp); | ||||
|     if (!chr) { | ||||
|         return NULL; | ||||
|     } | ||||
|     chr = qemu_chr_alloc(); | ||||
|     chr->chr_write = msmouse_chr_write; | ||||
|     chr->chr_close = msmouse_chr_close; | ||||
|     chr->explicit_be_open = true; | ||||
| @@ -86,8 +79,7 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id, | ||||
|  | ||||
| static void register_types(void) | ||||
| { | ||||
|     register_char_driver("msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL, | ||||
|                          qemu_chr_open_msmouse); | ||||
|     register_char_driver("msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL); | ||||
| } | ||||
|  | ||||
| type_init(register_types); | ||||
|   | ||||
| @@ -10,7 +10,6 @@ | ||||
|  * See the COPYING file in the top-level directory. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "sysemu/rng.h" | ||||
| #include "sysemu/char.h" | ||||
| #include "qapi/qmp/qerror.h" | ||||
| @@ -141,20 +140,19 @@ static void rng_egd_opened(RngBackend *b, Error **errp) | ||||
|     RngEgd *s = RNG_EGD(b); | ||||
|  | ||||
|     if (s->chr_name == NULL) { | ||||
|         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, | ||||
|         error_set(errp, QERR_INVALID_PARAMETER_VALUE, | ||||
|                   "chardev", "a valid character device"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     s->chr = qemu_chr_find(s->chr_name); | ||||
|     if (s->chr == NULL) { | ||||
|         error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, | ||||
|                   "Device '%s' not found", s->chr_name); | ||||
|         error_set(errp, QERR_DEVICE_NOT_FOUND, s->chr_name); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (qemu_chr_fe_claim(s->chr) != 0) { | ||||
|         error_setg(errp, QERR_DEVICE_IN_USE, s->chr_name); | ||||
|         error_set(errp, QERR_DEVICE_IN_USE, s->chr_name); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -169,7 +167,7 @@ static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp) | ||||
|     RngEgd *s = RNG_EGD(b); | ||||
|  | ||||
|     if (b->opened) { | ||||
|         error_setg(errp, QERR_PERMISSION_DENIED); | ||||
|         error_set(errp, QERR_PERMISSION_DENIED); | ||||
|     } else { | ||||
|         g_free(s->chr_name); | ||||
|         s->chr_name = g_strdup(value); | ||||
|   | ||||
| @@ -10,7 +10,6 @@ | ||||
|  * See the COPYING file in the top-level directory. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "sysemu/rng-random.h" | ||||
| #include "sysemu/rng.h" | ||||
| #include "qapi/qmp/qerror.h" | ||||
| @@ -75,7 +74,7 @@ static void rng_random_opened(RngBackend *b, Error **errp) | ||||
|     RndRandom *s = RNG_RANDOM(b); | ||||
|  | ||||
|     if (s->filename == NULL) { | ||||
|         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, | ||||
|         error_set(errp, QERR_INVALID_PARAMETER_VALUE, | ||||
|                   "filename", "a valid filename"); | ||||
|     } else { | ||||
|         s->fd = qemu_open(s->filename, O_RDONLY | O_NONBLOCK); | ||||
| @@ -99,7 +98,7 @@ static void rng_random_set_filename(Object *obj, const char *filename, | ||||
|     RndRandom *s = RNG_RANDOM(obj); | ||||
|  | ||||
|     if (b->opened) { | ||||
|         error_setg(errp, QERR_PERMISSION_DENIED); | ||||
|         error_set(errp, QERR_PERMISSION_DENIED); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -10,7 +10,6 @@ | ||||
|  * See the COPYING file in the top-level directory. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "sysemu/rng.h" | ||||
| #include "qapi/qmp/qerror.h" | ||||
| #include "qom/object_interfaces.h" | ||||
| @@ -58,7 +57,7 @@ static void rng_backend_prop_set_opened(Object *obj, bool value, Error **errp) | ||||
|     } | ||||
|  | ||||
|     if (!value && s->opened) { | ||||
|         error_setg(errp, QERR_PERMISSION_DENIED); | ||||
|         error_set(errp, QERR_PERMISSION_DENIED); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -23,7 +23,6 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "sysemu/char.h" | ||||
|  | ||||
| @@ -109,16 +108,13 @@ static void testdev_close(struct CharDriverState *chr) | ||||
|     g_free(testdev); | ||||
| } | ||||
|  | ||||
| static CharDriverState *chr_testdev_init(const char *id, | ||||
|                                          ChardevBackend *backend, | ||||
|                                          ChardevReturn *ret, | ||||
|                                          Error **errp) | ||||
| CharDriverState *chr_testdev_init(void) | ||||
| { | ||||
|     TestdevCharState *testdev; | ||||
|     CharDriverState *chr; | ||||
|  | ||||
|     testdev = g_new0(TestdevCharState, 1); | ||||
|     testdev->chr = chr = g_new0(CharDriverState, 1); | ||||
|     testdev = g_malloc0(sizeof(TestdevCharState)); | ||||
|     testdev->chr = chr = g_malloc0(sizeof(CharDriverState)); | ||||
|  | ||||
|     chr->opaque = testdev; | ||||
|     chr->chr_write = testdev_write; | ||||
| @@ -129,8 +125,7 @@ static CharDriverState *chr_testdev_init(const char *id, | ||||
|  | ||||
| static void register_types(void) | ||||
| { | ||||
|     register_char_driver("testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL, | ||||
|                          chr_testdev_init); | ||||
|     register_char_driver("testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL); | ||||
| } | ||||
|  | ||||
| type_init(register_types); | ||||
|   | ||||
| @@ -12,7 +12,6 @@ | ||||
|  * Based on backends/rng.c by Anthony Liguori | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "sysemu/tpm_backend.h" | ||||
| #include "qapi/qmp/qerror.h" | ||||
| #include "sysemu/tpm.h" | ||||
| @@ -97,20 +96,6 @@ bool tpm_backend_get_tpm_established_flag(TPMBackend *s) | ||||
|     return k->ops->get_tpm_established_flag(s); | ||||
| } | ||||
|  | ||||
| int tpm_backend_reset_tpm_established_flag(TPMBackend *s, uint8_t locty) | ||||
| { | ||||
|     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); | ||||
|  | ||||
|     return k->ops->reset_tpm_established_flag(s, locty); | ||||
| } | ||||
|  | ||||
| TPMVersion tpm_backend_get_tpm_version(TPMBackend *s) | ||||
| { | ||||
|     TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); | ||||
|  | ||||
|     return k->ops->get_tpm_version(s); | ||||
| } | ||||
|  | ||||
| static bool tpm_backend_prop_get_opened(Object *obj, Error **errp) | ||||
| { | ||||
|     TPMBackend *s = TPM_BACKEND(obj); | ||||
| @@ -134,7 +119,7 @@ static void tpm_backend_prop_set_opened(Object *obj, bool value, Error **errp) | ||||
|     } | ||||
|  | ||||
|     if (!value && s->opened) { | ||||
|         error_setg(errp, QERR_PERMISSION_DENIED); | ||||
|         error_set(errp, QERR_PERMISSION_DENIED); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -180,6 +165,17 @@ void tpm_backend_thread_end(TPMBackendThread *tbt) | ||||
|     } | ||||
| } | ||||
|  | ||||
| void tpm_backend_thread_tpm_reset(TPMBackendThread *tbt, | ||||
|                                   GFunc func, gpointer user_data) | ||||
| { | ||||
|     if (!tbt->pool) { | ||||
|         tpm_backend_thread_create(tbt, func, user_data); | ||||
|     } else { | ||||
|         g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_TPM_RESET, | ||||
|                            NULL); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static const TypeInfo tpm_backend_info = { | ||||
|     .name = TYPE_TPM_BACKEND, | ||||
|     .parent = TYPE_OBJECT, | ||||
|   | ||||
							
								
								
									
										18
									
								
								balloon.c
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								balloon.c
									
									
									
									
									
								
							| @@ -24,30 +24,17 @@ | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "monitor/monitor.h" | ||||
| #include "exec/cpu-common.h" | ||||
| #include "sysemu/kvm.h" | ||||
| #include "sysemu/balloon.h" | ||||
| #include "trace.h" | ||||
| #include "qmp-commands.h" | ||||
| #include "qapi/qmp/qerror.h" | ||||
| #include "qapi/qmp/qjson.h" | ||||
|  | ||||
| static QEMUBalloonEvent *balloon_event_fn; | ||||
| static QEMUBalloonStatus *balloon_stat_fn; | ||||
| static void *balloon_opaque; | ||||
| static bool balloon_inhibited; | ||||
|  | ||||
| bool qemu_balloon_is_inhibited(void) | ||||
| { | ||||
|     return balloon_inhibited; | ||||
| } | ||||
|  | ||||
| void qemu_balloon_inhibit(bool state) | ||||
| { | ||||
|     balloon_inhibited = state; | ||||
| } | ||||
|  | ||||
| static bool have_balloon(Error **errp) | ||||
| { | ||||
| @@ -71,6 +58,7 @@ int qemu_add_balloon_handler(QEMUBalloonEvent *event_func, | ||||
|         /* We're already registered one balloon handler.  How many can | ||||
|          * a guest really have? | ||||
|          */ | ||||
|         error_report("Another balloon device already registered"); | ||||
|         return -1; | ||||
|     } | ||||
|     balloon_event_fn = event_func; | ||||
| @@ -109,7 +97,7 @@ void qmp_balloon(int64_t target, Error **errp) | ||||
|     } | ||||
|  | ||||
|     if (target <= 0) { | ||||
|         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "target", "a size"); | ||||
|         error_set(errp, QERR_INVALID_PARAMETER_VALUE, "target", "a size"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,16 +1,15 @@ | ||||
| block-obj-y += raw_bsd.o qcow.o vdi.o vmdk.o cloop.o bochs.o vpc.o vvfat.o | ||||
| block-obj-y += raw_bsd.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o | ||||
| block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o | ||||
| block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o | ||||
| block-obj-y += qed-check.o | ||||
| block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o | ||||
| block-obj-y += quorum.o | ||||
| block-obj-$(CONFIG_QUORUM) += quorum.o | ||||
| block-obj-y += parallels.o blkdebug.o blkverify.o | ||||
| block-obj-y += block-backend.o snapshot.o qapi.o | ||||
| block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o | ||||
| block-obj-$(CONFIG_POSIX) += raw-posix.o | ||||
| block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o | ||||
| block-obj-y += null.o mirror.o io.o | ||||
| block-obj-y += throttle-groups.o | ||||
| block-obj-y += null.o mirror.o | ||||
|  | ||||
| block-obj-y += nbd.o nbd-client.o sheepdog.o | ||||
| block-obj-$(CONFIG_LIBISCSI) += iscsi.o | ||||
| @@ -20,6 +19,8 @@ block-obj-$(CONFIG_RBD) += rbd.o | ||||
| block-obj-$(CONFIG_GLUSTERFS) += gluster.o | ||||
| block-obj-$(CONFIG_ARCHIPELAGO) += archipelago.o | ||||
| block-obj-$(CONFIG_LIBSSH2) += ssh.o | ||||
| block-obj-y += dictzip.o | ||||
| block-obj-y += tar.o | ||||
| block-obj-y += accounting.o | ||||
| block-obj-y += write-threshold.o | ||||
|  | ||||
| @@ -38,7 +39,6 @@ gluster.o-libs     := $(GLUSTERFS_LIBS) | ||||
| ssh.o-cflags       := $(LIBSSH2_CFLAGS) | ||||
| ssh.o-libs         := $(LIBSSH2_LIBS) | ||||
| archipelago.o-libs := $(ARCHIPELAGO_LIBS) | ||||
| block-obj-m        += dmg.o | ||||
| dmg.o-libs         := $(BZIP2_LIBS) | ||||
| qcow.o-libs        := -lz | ||||
| linux-aio.o-libs   := -laio | ||||
|   | ||||
| @@ -2,7 +2,6 @@ | ||||
|  * QEMU System Emulator block accounting | ||||
|  * | ||||
|  * Copyright (c) 2011 Christoph Hellwig | ||||
|  * Copyright (c) 2015 Igalia, S.L. | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
| @@ -23,58 +22,9 @@ | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "block/accounting.h" | ||||
| #include "block/block_int.h" | ||||
| #include "qemu/timer.h" | ||||
| #include "sysemu/qtest.h" | ||||
|  | ||||
| static QEMUClockType clock_type = QEMU_CLOCK_REALTIME; | ||||
| static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000; | ||||
|  | ||||
| void block_acct_init(BlockAcctStats *stats, bool account_invalid, | ||||
|                      bool account_failed) | ||||
| { | ||||
|     stats->account_invalid = account_invalid; | ||||
|     stats->account_failed = account_failed; | ||||
|  | ||||
|     if (qtest_enabled()) { | ||||
|         clock_type = QEMU_CLOCK_VIRTUAL; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void block_acct_cleanup(BlockAcctStats *stats) | ||||
| { | ||||
|     BlockAcctTimedStats *s, *next; | ||||
|     QSLIST_FOREACH_SAFE(s, &stats->intervals, entries, next) { | ||||
|         g_free(s); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length) | ||||
| { | ||||
|     BlockAcctTimedStats *s; | ||||
|     unsigned i; | ||||
|  | ||||
|     s = g_new0(BlockAcctTimedStats, 1); | ||||
|     s->interval_length = interval_length; | ||||
|     QSLIST_INSERT_HEAD(&stats->intervals, s, entries); | ||||
|  | ||||
|     for (i = 0; i < BLOCK_MAX_IOTYPE; i++) { | ||||
|         timed_average_init(&s->latency[i], clock_type, | ||||
|                            (uint64_t) interval_length * NANOSECONDS_PER_SECOND); | ||||
|     } | ||||
| } | ||||
|  | ||||
| BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats, | ||||
|                                               BlockAcctTimedStats *s) | ||||
| { | ||||
|     if (s == NULL) { | ||||
|         return QSLIST_FIRST(&stats->intervals); | ||||
|     } else { | ||||
|         return QSLIST_NEXT(s, entries); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie, | ||||
|                       int64_t bytes, enum BlockAcctType type) | ||||
| @@ -82,69 +32,26 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie, | ||||
|     assert(type < BLOCK_MAX_IOTYPE); | ||||
|  | ||||
|     cookie->bytes = bytes; | ||||
|     cookie->start_time_ns = qemu_clock_get_ns(clock_type); | ||||
|     cookie->start_time_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); | ||||
|     cookie->type = type; | ||||
| } | ||||
|  | ||||
| void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie) | ||||
| { | ||||
|     BlockAcctTimedStats *s; | ||||
|     int64_t time_ns = qemu_clock_get_ns(clock_type); | ||||
|     int64_t latency_ns = time_ns - cookie->start_time_ns; | ||||
|  | ||||
|     if (qtest_enabled()) { | ||||
|         latency_ns = qtest_latency_ns; | ||||
|     } | ||||
|  | ||||
|     assert(cookie->type < BLOCK_MAX_IOTYPE); | ||||
|  | ||||
|     stats->nr_bytes[cookie->type] += cookie->bytes; | ||||
|     stats->nr_ops[cookie->type]++; | ||||
|     stats->total_time_ns[cookie->type] += latency_ns; | ||||
|     stats->last_access_time_ns = time_ns; | ||||
|  | ||||
|     QSLIST_FOREACH(s, &stats->intervals, entries) { | ||||
|         timed_average_account(&s->latency[cookie->type], latency_ns); | ||||
|     } | ||||
|     stats->total_time_ns[cookie->type] += | ||||
|         qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - cookie->start_time_ns; | ||||
| } | ||||
|  | ||||
| void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie) | ||||
|  | ||||
| void block_acct_highest_sector(BlockAcctStats *stats, int64_t sector_num, | ||||
|                                unsigned int nb_sectors) | ||||
| { | ||||
|     assert(cookie->type < BLOCK_MAX_IOTYPE); | ||||
|  | ||||
|     stats->failed_ops[cookie->type]++; | ||||
|  | ||||
|     if (stats->account_failed) { | ||||
|         BlockAcctTimedStats *s; | ||||
|         int64_t time_ns = qemu_clock_get_ns(clock_type); | ||||
|         int64_t latency_ns = time_ns - cookie->start_time_ns; | ||||
|  | ||||
|         if (qtest_enabled()) { | ||||
|             latency_ns = qtest_latency_ns; | ||||
|         } | ||||
|  | ||||
|         stats->total_time_ns[cookie->type] += latency_ns; | ||||
|         stats->last_access_time_ns = time_ns; | ||||
|  | ||||
|         QSLIST_FOREACH(s, &stats->intervals, entries) { | ||||
|             timed_average_account(&s->latency[cookie->type], latency_ns); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type) | ||||
| { | ||||
|     assert(type < BLOCK_MAX_IOTYPE); | ||||
|  | ||||
|     /* block_acct_done() and block_acct_failed() update | ||||
|      * total_time_ns[], but this one does not. The reason is that | ||||
|      * invalid requests are accounted during their submission, | ||||
|      * therefore there's no actual I/O involved. */ | ||||
|  | ||||
|     stats->invalid_ops[type]++; | ||||
|  | ||||
|     if (stats->account_invalid) { | ||||
|         stats->last_access_time_ns = qemu_clock_get_ns(clock_type); | ||||
|     if (stats->wr_highest_sector < sector_num + nb_sectors - 1) { | ||||
|         stats->wr_highest_sector = sector_num + nb_sectors - 1; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -154,20 +61,3 @@ void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type, | ||||
|     assert(type < BLOCK_MAX_IOTYPE); | ||||
|     stats->merged[type] += num_requests; | ||||
| } | ||||
|  | ||||
| int64_t block_acct_idle_time_ns(BlockAcctStats *stats) | ||||
| { | ||||
|     return qemu_clock_get_ns(clock_type) - stats->last_access_time_ns; | ||||
| } | ||||
|  | ||||
| double block_acct_queue_depth(BlockAcctTimedStats *stats, | ||||
|                               enum BlockAcctType type) | ||||
| { | ||||
|     uint64_t sum, elapsed; | ||||
|  | ||||
|     assert(type < BLOCK_MAX_IOTYPE); | ||||
|  | ||||
|     sum = timed_average_sum(&stats->latency[type], &elapsed); | ||||
|  | ||||
|     return (double) sum / elapsed; | ||||
| } | ||||
|   | ||||
| @@ -50,7 +50,6 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "block/block_int.h" | ||||
| #include "qemu/error-report.h" | ||||
| @@ -60,6 +59,7 @@ | ||||
| #include "qapi/qmp/qjson.h" | ||||
| #include "qemu/atomic.h" | ||||
|  | ||||
| #include <inttypes.h> | ||||
| #include <xseg/xseg.h> | ||||
| #include <xseg/protocol.h> | ||||
|  | ||||
|   | ||||
							
								
								
									
										221
									
								
								block/backup.c
									
									
									
									
									
								
							
							
						
						
									
										221
									
								
								block/backup.c
									
									
									
									
									
								
							| @@ -11,15 +11,15 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include <stdio.h> | ||||
| #include <errno.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include "trace.h" | ||||
| #include "block/block.h" | ||||
| #include "block/block_int.h" | ||||
| #include "block/blockjob.h" | ||||
| #include "qapi/qmp/qerror.h" | ||||
| #include "qemu/ratelimit.h" | ||||
| #include "sysemu/block-backend.h" | ||||
|  | ||||
| #define BACKUP_CLUSTER_BITS 16 | ||||
| #define BACKUP_CLUSTER_SIZE (1 << BACKUP_CLUSTER_BITS) | ||||
| @@ -37,8 +37,6 @@ typedef struct CowRequest { | ||||
| typedef struct BackupBlockJob { | ||||
|     BlockJob common; | ||||
|     BlockDriverState *target; | ||||
|     /* bitmap for sync=incremental */ | ||||
|     BdrvDirtyBitmap *sync_bitmap; | ||||
|     MirrorSyncMode sync_mode; | ||||
|     RateLimit limit; | ||||
|     BlockdevOnError on_source_error; | ||||
| @@ -88,8 +86,7 @@ static void cow_request_end(CowRequest *req) | ||||
|  | ||||
| static int coroutine_fn backup_do_cow(BlockDriverState *bs, | ||||
|                                       int64_t sector_num, int nb_sectors, | ||||
|                                       bool *error_is_read, | ||||
|                                       bool is_write_notifier) | ||||
|                                       bool *error_is_read) | ||||
| { | ||||
|     BackupBlockJob *job = (BackupBlockJob *)bs->job; | ||||
|     CowRequest cow_request; | ||||
| @@ -129,14 +126,8 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs, | ||||
|         iov.iov_len = n * BDRV_SECTOR_SIZE; | ||||
|         qemu_iovec_init_external(&bounce_qiov, &iov, 1); | ||||
|  | ||||
|         if (is_write_notifier) { | ||||
|             ret = bdrv_co_readv_no_serialising(bs, | ||||
|                                            start * BACKUP_SECTORS_PER_CLUSTER, | ||||
|                                            n, &bounce_qiov); | ||||
|         } else { | ||||
|         ret = bdrv_co_readv(bs, start * BACKUP_SECTORS_PER_CLUSTER, n, | ||||
|                             &bounce_qiov); | ||||
|         } | ||||
|         if (ret < 0) { | ||||
|             trace_backup_do_cow_read_fail(job, start, ret); | ||||
|             if (error_is_read) { | ||||
| @@ -196,7 +187,7 @@ static int coroutine_fn backup_before_write_notify( | ||||
|     assert((req->offset & (BDRV_SECTOR_SIZE - 1)) == 0); | ||||
|     assert((req->bytes & (BDRV_SECTOR_SIZE - 1)) == 0); | ||||
|  | ||||
|     return backup_do_cow(req->bs, sector_num, nb_sectors, NULL, true); | ||||
|     return backup_do_cow(req->bs, sector_num, nb_sectors, NULL); | ||||
| } | ||||
|  | ||||
| static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp) | ||||
| @@ -204,7 +195,7 @@ static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp) | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common); | ||||
|  | ||||
|     if (speed < 0) { | ||||
|         error_setg(errp, QERR_INVALID_PARAMETER, "speed"); | ||||
|         error_set(errp, QERR_INVALID_PARAMETER, "speed"); | ||||
|         return; | ||||
|     } | ||||
|     ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME); | ||||
| @@ -214,41 +205,7 @@ static void backup_iostatus_reset(BlockJob *job) | ||||
| { | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common); | ||||
|  | ||||
|     if (s->target->blk) { | ||||
|         blk_iostatus_reset(s->target->blk); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) | ||||
| { | ||||
|     BdrvDirtyBitmap *bm; | ||||
|     BlockDriverState *bs = job->common.bs; | ||||
|  | ||||
|     if (ret < 0 || block_job_is_cancelled(&job->common)) { | ||||
|         /* Merge the successor back into the parent, delete nothing. */ | ||||
|         bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL); | ||||
|         assert(bm); | ||||
|     } else { | ||||
|         /* Everything is fine, delete this bitmap and install the backup. */ | ||||
|         bm = bdrv_dirty_bitmap_abdicate(bs, job->sync_bitmap, NULL); | ||||
|         assert(bm); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void backup_commit(BlockJob *job) | ||||
| { | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common); | ||||
|     if (s->sync_bitmap) { | ||||
|         backup_cleanup_sync_bitmap(s, 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void backup_abort(BlockJob *job) | ||||
| { | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common); | ||||
|     if (s->sync_bitmap) { | ||||
|         backup_cleanup_sync_bitmap(s, -1); | ||||
|     } | ||||
|     bdrv_iostatus_reset(s->target); | ||||
| } | ||||
|  | ||||
| static const BlockJobDriver backup_job_driver = { | ||||
| @@ -256,8 +213,6 @@ static const BlockJobDriver backup_job_driver = { | ||||
|     .job_type       = BLOCK_JOB_TYPE_BACKUP, | ||||
|     .set_speed      = backup_set_speed, | ||||
|     .iostatus_reset = backup_iostatus_reset, | ||||
|     .commit         = backup_commit, | ||||
|     .abort          = backup_abort, | ||||
| }; | ||||
|  | ||||
| static BlockErrorAction backup_error_action(BackupBlockJob *job, | ||||
| @@ -287,92 +242,6 @@ static void backup_complete(BlockJob *job, void *opaque) | ||||
|     g_free(data); | ||||
| } | ||||
|  | ||||
| static bool coroutine_fn yield_and_check(BackupBlockJob *job) | ||||
| { | ||||
|     if (block_job_is_cancelled(&job->common)) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /* we need to yield so that bdrv_drain_all() returns. | ||||
|      * (without, VM does not reboot) | ||||
|      */ | ||||
|     if (job->common.speed) { | ||||
|         uint64_t delay_ns = ratelimit_calculate_delay(&job->limit, | ||||
|                                                       job->sectors_read); | ||||
|         job->sectors_read = 0; | ||||
|         block_job_sleep_ns(&job->common, QEMU_CLOCK_REALTIME, delay_ns); | ||||
|     } else { | ||||
|         block_job_sleep_ns(&job->common, QEMU_CLOCK_REALTIME, 0); | ||||
|     } | ||||
|  | ||||
|     if (block_job_is_cancelled(&job->common)) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn backup_run_incremental(BackupBlockJob *job) | ||||
| { | ||||
|     bool error_is_read; | ||||
|     int ret = 0; | ||||
|     int clusters_per_iter; | ||||
|     uint32_t granularity; | ||||
|     int64_t sector; | ||||
|     int64_t cluster; | ||||
|     int64_t end; | ||||
|     int64_t last_cluster = -1; | ||||
|     BlockDriverState *bs = job->common.bs; | ||||
|     HBitmapIter hbi; | ||||
|  | ||||
|     granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap); | ||||
|     clusters_per_iter = MAX((granularity / BACKUP_CLUSTER_SIZE), 1); | ||||
|     bdrv_dirty_iter_init(job->sync_bitmap, &hbi); | ||||
|  | ||||
|     /* Find the next dirty sector(s) */ | ||||
|     while ((sector = hbitmap_iter_next(&hbi)) != -1) { | ||||
|         cluster = sector / BACKUP_SECTORS_PER_CLUSTER; | ||||
|  | ||||
|         /* Fake progress updates for any clusters we skipped */ | ||||
|         if (cluster != last_cluster + 1) { | ||||
|             job->common.offset += ((cluster - last_cluster - 1) * | ||||
|                                    BACKUP_CLUSTER_SIZE); | ||||
|         } | ||||
|  | ||||
|         for (end = cluster + clusters_per_iter; cluster < end; cluster++) { | ||||
|             do { | ||||
|                 if (yield_and_check(job)) { | ||||
|                     return ret; | ||||
|                 } | ||||
|                 ret = backup_do_cow(bs, cluster * BACKUP_SECTORS_PER_CLUSTER, | ||||
|                                     BACKUP_SECTORS_PER_CLUSTER, &error_is_read, | ||||
|                                     false); | ||||
|                 if ((ret < 0) && | ||||
|                     backup_error_action(job, error_is_read, -ret) == | ||||
|                     BLOCK_ERROR_ACTION_REPORT) { | ||||
|                     return ret; | ||||
|                 } | ||||
|             } while (ret < 0); | ||||
|         } | ||||
|  | ||||
|         /* If the bitmap granularity is smaller than the backup granularity, | ||||
|          * we need to advance the iterator pointer to the next cluster. */ | ||||
|         if (granularity < BACKUP_CLUSTER_SIZE) { | ||||
|             bdrv_set_dirty_iter(&hbi, cluster * BACKUP_SECTORS_PER_CLUSTER); | ||||
|         } | ||||
|  | ||||
|         last_cluster = cluster - 1; | ||||
|     } | ||||
|  | ||||
|     /* Play some final catchup with the progress meter */ | ||||
|     end = DIV_ROUND_UP(job->common.len, BACKUP_CLUSTER_SIZE); | ||||
|     if (last_cluster + 1 < end) { | ||||
|         job->common.offset += ((end - last_cluster - 1) * BACKUP_CLUSTER_SIZE); | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static void coroutine_fn backup_run(void *opaque) | ||||
| { | ||||
|     BackupBlockJob *job = opaque; | ||||
| @@ -390,15 +259,14 @@ static void coroutine_fn backup_run(void *opaque) | ||||
|     qemu_co_rwlock_init(&job->flush_rwlock); | ||||
|  | ||||
|     start = 0; | ||||
|     end = DIV_ROUND_UP(job->common.len, BACKUP_CLUSTER_SIZE); | ||||
|     end = DIV_ROUND_UP(job->common.len / BDRV_SECTOR_SIZE, | ||||
|                        BACKUP_SECTORS_PER_CLUSTER); | ||||
|  | ||||
|     job->bitmap = hbitmap_alloc(end, 0); | ||||
|  | ||||
|     bdrv_set_enable_write_cache(target, true); | ||||
|     if (target->blk) { | ||||
|         blk_set_on_error(target->blk, on_target_error, on_target_error); | ||||
|         blk_iostatus_enable(target->blk); | ||||
|     } | ||||
|     bdrv_set_on_error(target, on_target_error, on_target_error); | ||||
|     bdrv_iostatus_enable(target); | ||||
|  | ||||
|     bdrv_add_before_write_notifier(bs, &before_write); | ||||
|  | ||||
| @@ -410,13 +278,28 @@ static void coroutine_fn backup_run(void *opaque) | ||||
|             qemu_coroutine_yield(); | ||||
|             job->common.busy = true; | ||||
|         } | ||||
|     } else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { | ||||
|         ret = backup_run_incremental(job); | ||||
|     } else { | ||||
|         /* Both FULL and TOP SYNC_MODE's require copying.. */ | ||||
|         for (; start < end; start++) { | ||||
|             bool error_is_read; | ||||
|             if (yield_and_check(job)) { | ||||
|  | ||||
|             if (block_job_is_cancelled(&job->common)) { | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             /* we need to yield so that qemu_aio_flush() returns. | ||||
|              * (without, VM does not reboot) | ||||
|              */ | ||||
|             if (job->common.speed) { | ||||
|                 uint64_t delay_ns = ratelimit_calculate_delay( | ||||
|                         &job->limit, job->sectors_read); | ||||
|                 job->sectors_read = 0; | ||||
|                 block_job_sleep_ns(&job->common, QEMU_CLOCK_REALTIME, delay_ns); | ||||
|             } else { | ||||
|                 block_job_sleep_ns(&job->common, QEMU_CLOCK_REALTIME, 0); | ||||
|             } | ||||
|  | ||||
|             if (block_job_is_cancelled(&job->common)) { | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
| @@ -453,7 +336,7 @@ static void coroutine_fn backup_run(void *opaque) | ||||
|             } | ||||
|             /* FULL sync mode we copy the whole drive. */ | ||||
|             ret = backup_do_cow(bs, start * BACKUP_SECTORS_PER_CLUSTER, | ||||
|                     BACKUP_SECTORS_PER_CLUSTER, &error_is_read, false); | ||||
|                     BACKUP_SECTORS_PER_CLUSTER, &error_is_read); | ||||
|             if (ret < 0) { | ||||
|                 /* Depending on error action, fail now or retry cluster */ | ||||
|                 BlockErrorAction action = | ||||
| @@ -473,11 +356,10 @@ static void coroutine_fn backup_run(void *opaque) | ||||
|     /* wait until pending backup_do_cow() calls have completed */ | ||||
|     qemu_co_rwlock_wrlock(&job->flush_rwlock); | ||||
|     qemu_co_rwlock_unlock(&job->flush_rwlock); | ||||
|  | ||||
|     hbitmap_free(job->bitmap); | ||||
|  | ||||
|     if (target->blk) { | ||||
|         blk_iostatus_disable(target->blk); | ||||
|     } | ||||
|     bdrv_iostatus_disable(target); | ||||
|     bdrv_op_unblock_all(target, job->common.blocker); | ||||
|  | ||||
|     data = g_malloc(sizeof(*data)); | ||||
| @@ -487,11 +369,10 @@ static void coroutine_fn backup_run(void *opaque) | ||||
|  | ||||
| void backup_start(BlockDriverState *bs, BlockDriverState *target, | ||||
|                   int64_t speed, MirrorSyncMode sync_mode, | ||||
|                   BdrvDirtyBitmap *sync_bitmap, | ||||
|                   BlockdevOnError on_source_error, | ||||
|                   BlockdevOnError on_target_error, | ||||
|                   BlockCompletionFunc *cb, void *opaque, | ||||
|                   BlockJobTxn *txn, Error **errp) | ||||
|                   Error **errp) | ||||
| { | ||||
|     int64_t len; | ||||
|  | ||||
| @@ -506,8 +387,8 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, | ||||
|  | ||||
|     if ((on_source_error == BLOCKDEV_ON_ERROR_STOP || | ||||
|          on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) && | ||||
|         (!bs->blk || !blk_iostatus_is_enabled(bs->blk))) { | ||||
|         error_setg(errp, QERR_INVALID_PARAMETER, "on-source-error"); | ||||
|         !bdrv_iostatus_is_enabled(bs)) { | ||||
|         error_set(errp, QERR_INVALID_PARAMETER, "on-source-error"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -531,36 +412,17 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { | ||||
|         if (!sync_bitmap) { | ||||
|             error_setg(errp, "must provide a valid bitmap name for " | ||||
|                              "\"incremental\" sync mode"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /* Create a new bitmap, and freeze/disable this one. */ | ||||
|         if (bdrv_dirty_bitmap_create_successor(bs, sync_bitmap, errp) < 0) { | ||||
|             return; | ||||
|         } | ||||
|     } else if (sync_bitmap) { | ||||
|         error_setg(errp, | ||||
|                    "a sync_bitmap was provided to backup_run, " | ||||
|                    "but received an incompatible sync_mode (%s)", | ||||
|                    MirrorSyncMode_lookup[sync_mode]); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     len = bdrv_getlength(bs); | ||||
|     if (len < 0) { | ||||
|         error_setg_errno(errp, -len, "unable to get length for '%s'", | ||||
|                          bdrv_get_device_name(bs)); | ||||
|         goto error; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     BackupBlockJob *job = block_job_create(&backup_job_driver, bs, speed, | ||||
|                                            cb, opaque, errp); | ||||
|     if (!job) { | ||||
|         goto error; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     bdrv_op_block_all(target, job->common.blocker); | ||||
| @@ -569,16 +431,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, | ||||
|     job->on_target_error = on_target_error; | ||||
|     job->target = target; | ||||
|     job->sync_mode = sync_mode; | ||||
|     job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_INCREMENTAL ? | ||||
|                        sync_bitmap : NULL; | ||||
|     job->common.len = len; | ||||
|     job->common.co = qemu_coroutine_create(backup_run); | ||||
|     block_job_txn_add_job(txn, &job->common); | ||||
|     qemu_coroutine_enter(job->common.co, job); | ||||
|     return; | ||||
|  | ||||
|  error: | ||||
|     if (sync_bitmap) { | ||||
|         bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL); | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										159
									
								
								block/blkdebug.c
									
									
									
									
									
								
							
							
						
						
									
										159
									
								
								block/blkdebug.c
									
									
									
									
									
								
							| @@ -22,7 +22,6 @@ | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "qemu/config-file.h" | ||||
| #include "block/block_int.h" | ||||
| @@ -31,13 +30,12 @@ | ||||
| #include "qapi/qmp/qdict.h" | ||||
| #include "qapi/qmp/qint.h" | ||||
| #include "qapi/qmp/qstring.h" | ||||
| #include "sysemu/qtest.h" | ||||
|  | ||||
| typedef struct BDRVBlkdebugState { | ||||
|     int state; | ||||
|     int new_state; | ||||
|  | ||||
|     QLIST_HEAD(, BlkdebugRule) rules[BLKDBG__MAX]; | ||||
|     QLIST_HEAD(, BlkdebugRule) rules[BLKDBG_EVENT_MAX]; | ||||
|     QSIMPLEQ_HEAD(, BlkdebugRule) active_rules; | ||||
|     QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs; | ||||
| } BDRVBlkdebugState; | ||||
| @@ -65,7 +63,7 @@ enum { | ||||
| }; | ||||
|  | ||||
| typedef struct BlkdebugRule { | ||||
|     BlkdebugEvent event; | ||||
|     BlkDebugEvent event; | ||||
|     int action; | ||||
|     int state; | ||||
|     union { | ||||
| @@ -144,12 +142,69 @@ static QemuOptsList *config_groups[] = { | ||||
|     NULL | ||||
| }; | ||||
|  | ||||
| static int get_event_by_name(const char *name, BlkdebugEvent *event) | ||||
| static const char *event_names[BLKDBG_EVENT_MAX] = { | ||||
|     [BLKDBG_L1_UPDATE]                      = "l1_update", | ||||
|     [BLKDBG_L1_GROW_ALLOC_TABLE]            = "l1_grow.alloc_table", | ||||
|     [BLKDBG_L1_GROW_WRITE_TABLE]            = "l1_grow.write_table", | ||||
|     [BLKDBG_L1_GROW_ACTIVATE_TABLE]         = "l1_grow.activate_table", | ||||
|  | ||||
|     [BLKDBG_L2_LOAD]                        = "l2_load", | ||||
|     [BLKDBG_L2_UPDATE]                      = "l2_update", | ||||
|     [BLKDBG_L2_UPDATE_COMPRESSED]           = "l2_update_compressed", | ||||
|     [BLKDBG_L2_ALLOC_COW_READ]              = "l2_alloc.cow_read", | ||||
|     [BLKDBG_L2_ALLOC_WRITE]                 = "l2_alloc.write", | ||||
|  | ||||
|     [BLKDBG_READ_AIO]                       = "read_aio", | ||||
|     [BLKDBG_READ_BACKING_AIO]               = "read_backing_aio", | ||||
|     [BLKDBG_READ_COMPRESSED]                = "read_compressed", | ||||
|  | ||||
|     [BLKDBG_WRITE_AIO]                      = "write_aio", | ||||
|     [BLKDBG_WRITE_COMPRESSED]               = "write_compressed", | ||||
|  | ||||
|     [BLKDBG_VMSTATE_LOAD]                   = "vmstate_load", | ||||
|     [BLKDBG_VMSTATE_SAVE]                   = "vmstate_save", | ||||
|  | ||||
|     [BLKDBG_COW_READ]                       = "cow_read", | ||||
|     [BLKDBG_COW_WRITE]                      = "cow_write", | ||||
|  | ||||
|     [BLKDBG_REFTABLE_LOAD]                  = "reftable_load", | ||||
|     [BLKDBG_REFTABLE_GROW]                  = "reftable_grow", | ||||
|     [BLKDBG_REFTABLE_UPDATE]                = "reftable_update", | ||||
|  | ||||
|     [BLKDBG_REFBLOCK_LOAD]                  = "refblock_load", | ||||
|     [BLKDBG_REFBLOCK_UPDATE]                = "refblock_update", | ||||
|     [BLKDBG_REFBLOCK_UPDATE_PART]           = "refblock_update_part", | ||||
|     [BLKDBG_REFBLOCK_ALLOC]                 = "refblock_alloc", | ||||
|     [BLKDBG_REFBLOCK_ALLOC_HOOKUP]          = "refblock_alloc.hookup", | ||||
|     [BLKDBG_REFBLOCK_ALLOC_WRITE]           = "refblock_alloc.write", | ||||
|     [BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS]    = "refblock_alloc.write_blocks", | ||||
|     [BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE]     = "refblock_alloc.write_table", | ||||
|     [BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE]    = "refblock_alloc.switch_table", | ||||
|  | ||||
|     [BLKDBG_CLUSTER_ALLOC]                  = "cluster_alloc", | ||||
|     [BLKDBG_CLUSTER_ALLOC_BYTES]            = "cluster_alloc_bytes", | ||||
|     [BLKDBG_CLUSTER_FREE]                   = "cluster_free", | ||||
|  | ||||
|     [BLKDBG_FLUSH_TO_OS]                    = "flush_to_os", | ||||
|     [BLKDBG_FLUSH_TO_DISK]                  = "flush_to_disk", | ||||
|  | ||||
|     [BLKDBG_PWRITEV_RMW_HEAD]               = "pwritev_rmw.head", | ||||
|     [BLKDBG_PWRITEV_RMW_AFTER_HEAD]         = "pwritev_rmw.after_head", | ||||
|     [BLKDBG_PWRITEV_RMW_TAIL]               = "pwritev_rmw.tail", | ||||
|     [BLKDBG_PWRITEV_RMW_AFTER_TAIL]         = "pwritev_rmw.after_tail", | ||||
|     [BLKDBG_PWRITEV]                        = "pwritev", | ||||
|     [BLKDBG_PWRITEV_ZERO]                   = "pwritev_zero", | ||||
|     [BLKDBG_PWRITEV_DONE]                   = "pwritev_done", | ||||
|  | ||||
|     [BLKDBG_EMPTY_IMAGE_PREPARE]            = "empty_image_prepare", | ||||
| }; | ||||
|  | ||||
| static int get_event_by_name(const char *name, BlkDebugEvent *event) | ||||
| { | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < BLKDBG__MAX; i++) { | ||||
|         if (!strcmp(BlkdebugEvent_lookup[i], name)) { | ||||
|     for (i = 0; i < BLKDBG_EVENT_MAX; i++) { | ||||
|         if (!strcmp(event_names[i], name)) { | ||||
|             *event = i; | ||||
|             return 0; | ||||
|         } | ||||
| @@ -161,23 +216,24 @@ static int get_event_by_name(const char *name, BlkdebugEvent *event) | ||||
| struct add_rule_data { | ||||
|     BDRVBlkdebugState *s; | ||||
|     int action; | ||||
|     Error **errp; | ||||
| }; | ||||
|  | ||||
| static int add_rule(void *opaque, QemuOpts *opts, Error **errp) | ||||
| static int add_rule(QemuOpts *opts, void *opaque) | ||||
| { | ||||
|     struct add_rule_data *d = opaque; | ||||
|     BDRVBlkdebugState *s = d->s; | ||||
|     const char* event_name; | ||||
|     BlkdebugEvent event; | ||||
|     BlkDebugEvent event; | ||||
|     struct BlkdebugRule *rule; | ||||
|  | ||||
|     /* Find the right event for the rule */ | ||||
|     event_name = qemu_opt_get(opts, "event"); | ||||
|     if (!event_name) { | ||||
|         error_setg(errp, "Missing event name for rule"); | ||||
|         error_setg(d->errp, "Missing event name for rule"); | ||||
|         return -1; | ||||
|     } else if (get_event_by_name(event_name, &event) < 0) { | ||||
|         error_setg(errp, "Invalid event name \"%s\"", event_name); | ||||
|         error_setg(d->errp, "Invalid event name \"%s\"", event_name); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| @@ -263,7 +319,8 @@ static int read_config(BDRVBlkdebugState *s, const char *filename, | ||||
|  | ||||
|     d.s = s; | ||||
|     d.action = ACTION_INJECT_ERROR; | ||||
|     qemu_opts_foreach(&inject_error_opts, add_rule, &d, &local_err); | ||||
|     d.errp = &local_err; | ||||
|     qemu_opts_foreach(&inject_error_opts, add_rule, &d, 1); | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         ret = -EINVAL; | ||||
| @@ -271,7 +328,7 @@ static int read_config(BDRVBlkdebugState *s, const char *filename, | ||||
|     } | ||||
|  | ||||
|     d.action = ACTION_SET_STATE; | ||||
|     qemu_opts_foreach(&set_state_opts, add_rule, &d, &local_err); | ||||
|     qemu_opts_foreach(&set_state_opts, add_rule, &d, 1); | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         ret = -EINVAL; | ||||
| @@ -371,11 +428,11 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     /* Set initial state */ | ||||
|     s->state = 1; | ||||
|  | ||||
|     /* Open the image file */ | ||||
|     bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image", | ||||
|                                bs, &child_file, false, &local_err); | ||||
|     if (local_err) { | ||||
|         ret = -EINVAL; | ||||
|     /* Open the backing file */ | ||||
|     assert(bs->file == NULL); | ||||
|     ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-image"), options, "image", | ||||
|                           flags | BDRV_O_PROTOCOL, false, &local_err); | ||||
|     if (ret < 0) { | ||||
|         error_propagate(errp, local_err); | ||||
|         goto out; | ||||
|     } | ||||
| @@ -394,7 +451,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     goto out; | ||||
|  | ||||
| fail_unref: | ||||
|     bdrv_unref_child(bs, bs->file); | ||||
|     bdrv_unref(bs->file); | ||||
| out: | ||||
|     qemu_opts_del(opts); | ||||
|     return ret; | ||||
| @@ -455,8 +512,7 @@ static BlockAIOCB *blkdebug_aio_readv(BlockDriverState *bs, | ||||
|         return inject_error(bs, cb, opaque, rule); | ||||
|     } | ||||
|  | ||||
|     return bdrv_aio_readv(bs->file->bs, sector_num, qiov, nb_sectors, | ||||
|                           cb, opaque); | ||||
|     return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque); | ||||
| } | ||||
|  | ||||
| static BlockAIOCB *blkdebug_aio_writev(BlockDriverState *bs, | ||||
| @@ -478,8 +534,7 @@ static BlockAIOCB *blkdebug_aio_writev(BlockDriverState *bs, | ||||
|         return inject_error(bs, cb, opaque, rule); | ||||
|     } | ||||
|  | ||||
|     return bdrv_aio_writev(bs->file->bs, sector_num, qiov, nb_sectors, | ||||
|                            cb, opaque); | ||||
|     return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque); | ||||
| } | ||||
|  | ||||
| static BlockAIOCB *blkdebug_aio_flush(BlockDriverState *bs, | ||||
| @@ -498,7 +553,7 @@ static BlockAIOCB *blkdebug_aio_flush(BlockDriverState *bs, | ||||
|         return inject_error(bs, cb, opaque, rule); | ||||
|     } | ||||
|  | ||||
|     return bdrv_aio_flush(bs->file->bs, cb, opaque); | ||||
|     return bdrv_aio_flush(bs->file, cb, opaque); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -508,7 +563,7 @@ static void blkdebug_close(BlockDriverState *bs) | ||||
|     BlkdebugRule *rule, *next; | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < BLKDBG__MAX; i++) { | ||||
|     for (i = 0; i < BLKDBG_EVENT_MAX; i++) { | ||||
|         QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) { | ||||
|             remove_rule(rule); | ||||
|         } | ||||
| @@ -528,13 +583,9 @@ static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule) | ||||
|     remove_rule(rule); | ||||
|     QLIST_INSERT_HEAD(&s->suspended_reqs, &r, next); | ||||
|  | ||||
|     if (!qtest_enabled()) { | ||||
|     printf("blkdebug: Suspended request '%s'\n", r.tag); | ||||
|     } | ||||
|     qemu_coroutine_yield(); | ||||
|     if (!qtest_enabled()) { | ||||
|     printf("blkdebug: Resuming request '%s'\n", r.tag); | ||||
|     } | ||||
|  | ||||
|     QLIST_REMOVE(&r, next); | ||||
|     g_free(r.tag); | ||||
| @@ -571,13 +622,13 @@ static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule, | ||||
|     return injected; | ||||
| } | ||||
|  | ||||
| static void blkdebug_debug_event(BlockDriverState *bs, BlkdebugEvent event) | ||||
| static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event) | ||||
| { | ||||
|     BDRVBlkdebugState *s = bs->opaque; | ||||
|     struct BlkdebugRule *rule, *next; | ||||
|     bool injected; | ||||
|  | ||||
|     assert((int)event >= 0 && event < BLKDBG__MAX); | ||||
|     assert((int)event >= 0 && event < BLKDBG_EVENT_MAX); | ||||
|  | ||||
|     injected = false; | ||||
|     s->new_state = s->state; | ||||
| @@ -592,7 +643,7 @@ static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event, | ||||
| { | ||||
|     BDRVBlkdebugState *s = bs->opaque; | ||||
|     struct BlkdebugRule *rule; | ||||
|     BlkdebugEvent blkdebug_event; | ||||
|     BlkDebugEvent blkdebug_event; | ||||
|  | ||||
|     if (get_event_by_name(event, &blkdebug_event) < 0) { | ||||
|         return -ENOENT; | ||||
| @@ -634,7 +685,7 @@ static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs, | ||||
|     BlkdebugRule *rule, *next; | ||||
|     int i, ret = -ENOENT; | ||||
|  | ||||
|     for (i = 0; i < BLKDBG__MAX; i++) { | ||||
|     for (i = 0; i < BLKDBG_EVENT_MAX; i++) { | ||||
|         QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) { | ||||
|             if (rule->action == ACTION_SUSPEND && | ||||
|                 !strcmp(rule->options.suspend.tag, tag)) { | ||||
| @@ -667,50 +718,50 @@ static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag) | ||||
|  | ||||
| static int64_t blkdebug_getlength(BlockDriverState *bs) | ||||
| { | ||||
|     return bdrv_getlength(bs->file->bs); | ||||
|     return bdrv_getlength(bs->file); | ||||
| } | ||||
|  | ||||
| static int blkdebug_truncate(BlockDriverState *bs, int64_t offset) | ||||
| { | ||||
|     return bdrv_truncate(bs->file->bs, offset); | ||||
| } | ||||
|  | ||||
| static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options) | ||||
| static void blkdebug_refresh_filename(BlockDriverState *bs) | ||||
| { | ||||
|     QDict *opts; | ||||
|     const QDictEntry *e; | ||||
|     bool force_json = false; | ||||
|  | ||||
|     for (e = qdict_first(options); e; e = qdict_next(options, e)) { | ||||
|     for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) { | ||||
|         if (strcmp(qdict_entry_key(e), "config") && | ||||
|             strcmp(qdict_entry_key(e), "x-image")) | ||||
|             strcmp(qdict_entry_key(e), "x-image") && | ||||
|             strcmp(qdict_entry_key(e), "image") && | ||||
|             strncmp(qdict_entry_key(e), "image.", strlen("image."))) | ||||
|         { | ||||
|             force_json = true; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (force_json && !bs->file->bs->full_open_options) { | ||||
|     if (force_json && !bs->file->full_open_options) { | ||||
|         /* The config file cannot be recreated, so creating a plain filename | ||||
|          * is impossible */ | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!force_json && bs->file->bs->exact_filename[0]) { | ||||
|     if (!force_json && bs->file->exact_filename[0]) { | ||||
|         snprintf(bs->exact_filename, sizeof(bs->exact_filename), | ||||
|                  "blkdebug:%s:%s", | ||||
|                  qdict_get_try_str(options, "config") ?: "", | ||||
|                  bs->file->bs->exact_filename); | ||||
|                  qdict_get_try_str(bs->options, "config") ?: "", | ||||
|                  bs->file->exact_filename); | ||||
|     } | ||||
|  | ||||
|     opts = qdict_new(); | ||||
|     qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("blkdebug"))); | ||||
|  | ||||
|     QINCREF(bs->file->bs->full_open_options); | ||||
|     qdict_put_obj(opts, "image", QOBJECT(bs->file->bs->full_open_options)); | ||||
|     QINCREF(bs->file->full_open_options); | ||||
|     qdict_put_obj(opts, "image", QOBJECT(bs->file->full_open_options)); | ||||
|  | ||||
|     for (e = qdict_first(options); e; e = qdict_next(options, e)) { | ||||
|         if (strcmp(qdict_entry_key(e), "x-image")) { | ||||
|     for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) { | ||||
|         if (strcmp(qdict_entry_key(e), "x-image") && | ||||
|             strcmp(qdict_entry_key(e), "image") && | ||||
|             strncmp(qdict_entry_key(e), "image.", strlen("image."))) | ||||
|         { | ||||
|             qobject_incref(qdict_entry_value(e)); | ||||
|             qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e)); | ||||
|         } | ||||
| @@ -719,12 +770,6 @@ static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options) | ||||
|     bs->full_open_options = opts; | ||||
| } | ||||
|  | ||||
| static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state, | ||||
|                                    BlockReopenQueue *queue, Error **errp) | ||||
| { | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static BlockDriver bdrv_blkdebug = { | ||||
|     .format_name            = "blkdebug", | ||||
|     .protocol_name          = "blkdebug", | ||||
| @@ -733,9 +778,7 @@ static BlockDriver bdrv_blkdebug = { | ||||
|     .bdrv_parse_filename    = blkdebug_parse_filename, | ||||
|     .bdrv_file_open         = blkdebug_open, | ||||
|     .bdrv_close             = blkdebug_close, | ||||
|     .bdrv_reopen_prepare    = blkdebug_reopen_prepare, | ||||
|     .bdrv_getlength         = blkdebug_getlength, | ||||
|     .bdrv_truncate          = blkdebug_truncate, | ||||
|     .bdrv_refresh_filename  = blkdebug_refresh_filename, | ||||
|  | ||||
|     .bdrv_aio_readv         = blkdebug_aio_readv, | ||||
|   | ||||
| @@ -7,14 +7,14 @@ | ||||
|  * See the COPYING file in the top-level directory. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include <stdarg.h> | ||||
| #include "qemu/sockets.h" /* for EINPROGRESS on Windows */ | ||||
| #include "block/block_int.h" | ||||
| #include "qapi/qmp/qdict.h" | ||||
| #include "qapi/qmp/qstring.h" | ||||
|  | ||||
| typedef struct { | ||||
|     BdrvChild *test_file; | ||||
|     BlockDriverState *test_file; | ||||
| } BDRVBlkverifyState; | ||||
|  | ||||
| typedef struct BlkverifyAIOCB BlkverifyAIOCB; | ||||
| @@ -123,29 +123,26 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     } | ||||
|  | ||||
|     /* Open the raw file */ | ||||
|     bs->file = bdrv_open_child(qemu_opt_get(opts, "x-raw"), options, "raw", | ||||
|                                bs, &child_file, false, &local_err); | ||||
|     if (local_err) { | ||||
|         ret = -EINVAL; | ||||
|     assert(bs->file == NULL); | ||||
|     ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-raw"), options, | ||||
|                           "raw", flags | BDRV_O_PROTOCOL, false, &local_err); | ||||
|     if (ret < 0) { | ||||
|         error_propagate(errp, local_err); | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     /* Open the test file */ | ||||
|     s->test_file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, | ||||
|                                    "test", bs, &child_format, false, | ||||
|                                    &local_err); | ||||
|     if (local_err) { | ||||
|         ret = -EINVAL; | ||||
|     assert(s->test_file == NULL); | ||||
|     ret = bdrv_open_image(&s->test_file, qemu_opt_get(opts, "x-image"), options, | ||||
|                           "test", flags, false, &local_err); | ||||
|     if (ret < 0) { | ||||
|         error_propagate(errp, local_err); | ||||
|         s->test_file = NULL; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     ret = 0; | ||||
| fail: | ||||
|     if (ret < 0) { | ||||
|         bdrv_unref_child(bs, bs->file); | ||||
|     } | ||||
|     qemu_opts_del(opts); | ||||
|     return ret; | ||||
| } | ||||
| @@ -154,7 +151,7 @@ static void blkverify_close(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVBlkverifyState *s = bs->opaque; | ||||
|  | ||||
|     bdrv_unref_child(bs, s->test_file); | ||||
|     bdrv_unref(s->test_file); | ||||
|     s->test_file = NULL; | ||||
| } | ||||
|  | ||||
| @@ -162,7 +159,7 @@ static int64_t blkverify_getlength(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVBlkverifyState *s = bs->opaque; | ||||
|  | ||||
|     return bdrv_getlength(s->test_file->bs); | ||||
|     return bdrv_getlength(s->test_file); | ||||
| } | ||||
|  | ||||
| static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write, | ||||
| @@ -241,13 +238,13 @@ static BlockAIOCB *blkverify_aio_readv(BlockDriverState *bs, | ||||
|                                             nb_sectors, cb, opaque); | ||||
|  | ||||
|     acb->verify = blkverify_verify_readv; | ||||
|     acb->buf = qemu_blockalign(bs->file->bs, qiov->size); | ||||
|     acb->buf = qemu_blockalign(bs->file, qiov->size); | ||||
|     qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov); | ||||
|     qemu_iovec_clone(&acb->raw_qiov, qiov, acb->buf); | ||||
|  | ||||
|     bdrv_aio_readv(s->test_file->bs, sector_num, qiov, nb_sectors, | ||||
|     bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors, | ||||
|                    blkverify_aio_cb, acb); | ||||
|     bdrv_aio_readv(bs->file->bs, sector_num, &acb->raw_qiov, nb_sectors, | ||||
|     bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors, | ||||
|                    blkverify_aio_cb, acb); | ||||
|     return &acb->common; | ||||
| } | ||||
| @@ -260,9 +257,9 @@ static BlockAIOCB *blkverify_aio_writev(BlockDriverState *bs, | ||||
|     BlkverifyAIOCB *acb = blkverify_aio_get(bs, true, sector_num, qiov, | ||||
|                                             nb_sectors, cb, opaque); | ||||
|  | ||||
|     bdrv_aio_writev(s->test_file->bs, sector_num, qiov, nb_sectors, | ||||
|     bdrv_aio_writev(s->test_file, sector_num, qiov, nb_sectors, | ||||
|                     blkverify_aio_cb, acb); | ||||
|     bdrv_aio_writev(bs->file->bs, sector_num, qiov, nb_sectors, | ||||
|     bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, | ||||
|                     blkverify_aio_cb, acb); | ||||
|     return &acb->common; | ||||
| } | ||||
| @@ -274,7 +271,7 @@ static BlockAIOCB *blkverify_aio_flush(BlockDriverState *bs, | ||||
|     BDRVBlkverifyState *s = bs->opaque; | ||||
|  | ||||
|     /* Only flush test file, the raw file is not important */ | ||||
|     return bdrv_aio_flush(s->test_file->bs, cb, opaque); | ||||
|     return bdrv_aio_flush(s->test_file, cb, opaque); | ||||
| } | ||||
|  | ||||
| static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs, | ||||
| @@ -282,13 +279,13 @@ static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs, | ||||
| { | ||||
|     BDRVBlkverifyState *s = bs->opaque; | ||||
|  | ||||
|     bool perm = bdrv_recurse_is_first_non_filter(bs->file->bs, candidate); | ||||
|     bool perm = bdrv_recurse_is_first_non_filter(bs->file, candidate); | ||||
|  | ||||
|     if (perm) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate); | ||||
|     return bdrv_recurse_is_first_non_filter(s->test_file, candidate); | ||||
| } | ||||
|  | ||||
| /* Propagate AioContext changes to ->test_file */ | ||||
| @@ -296,7 +293,7 @@ static void blkverify_detach_aio_context(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVBlkverifyState *s = bs->opaque; | ||||
|  | ||||
|     bdrv_detach_aio_context(s->test_file->bs); | ||||
|     bdrv_detach_aio_context(s->test_file); | ||||
| } | ||||
|  | ||||
| static void blkverify_attach_aio_context(BlockDriverState *bs, | ||||
| @@ -304,38 +301,32 @@ static void blkverify_attach_aio_context(BlockDriverState *bs, | ||||
| { | ||||
|     BDRVBlkverifyState *s = bs->opaque; | ||||
|  | ||||
|     bdrv_attach_aio_context(s->test_file->bs, new_context); | ||||
|     bdrv_attach_aio_context(s->test_file, new_context); | ||||
| } | ||||
|  | ||||
| static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options) | ||||
| static void blkverify_refresh_filename(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVBlkverifyState *s = bs->opaque; | ||||
|  | ||||
|     /* bs->file->bs has already been refreshed */ | ||||
|     bdrv_refresh_filename(s->test_file->bs); | ||||
|     /* bs->file has already been refreshed */ | ||||
|     bdrv_refresh_filename(s->test_file); | ||||
|  | ||||
|     if (bs->file->bs->full_open_options | ||||
|         && s->test_file->bs->full_open_options) | ||||
|     { | ||||
|     if (bs->file->full_open_options && s->test_file->full_open_options) { | ||||
|         QDict *opts = qdict_new(); | ||||
|         qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("blkverify"))); | ||||
|  | ||||
|         QINCREF(bs->file->bs->full_open_options); | ||||
|         qdict_put_obj(opts, "raw", QOBJECT(bs->file->bs->full_open_options)); | ||||
|         QINCREF(s->test_file->bs->full_open_options); | ||||
|         qdict_put_obj(opts, "test", | ||||
|                       QOBJECT(s->test_file->bs->full_open_options)); | ||||
|         QINCREF(bs->file->full_open_options); | ||||
|         qdict_put_obj(opts, "raw", QOBJECT(bs->file->full_open_options)); | ||||
|         QINCREF(s->test_file->full_open_options); | ||||
|         qdict_put_obj(opts, "test", QOBJECT(s->test_file->full_open_options)); | ||||
|  | ||||
|         bs->full_open_options = opts; | ||||
|     } | ||||
|  | ||||
|     if (bs->file->bs->exact_filename[0] | ||||
|         && s->test_file->bs->exact_filename[0]) | ||||
|     { | ||||
|     if (bs->file->exact_filename[0] && s->test_file->exact_filename[0]) { | ||||
|         snprintf(bs->exact_filename, sizeof(bs->exact_filename), | ||||
|                  "blkverify:%s:%s", | ||||
|                  bs->file->bs->exact_filename, | ||||
|                  s->test_file->bs->exact_filename); | ||||
|                  bs->file->exact_filename, s->test_file->exact_filename); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -10,20 +10,14 @@ | ||||
|  * or later.  See the COPYING.LIB file in the top-level directory. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "sysemu/block-backend.h" | ||||
| #include "block/block_int.h" | ||||
| #include "block/blockjob.h" | ||||
| #include "block/throttle-groups.h" | ||||
| #include "sysemu/blockdev.h" | ||||
| #include "sysemu/sysemu.h" | ||||
| #include "qapi-event.h" | ||||
|  | ||||
| /* Number of coroutines to reserve per attached device model */ | ||||
| #define COROUTINE_POOL_RESERVATION 64 | ||||
|  | ||||
| static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb); | ||||
|  | ||||
| struct BlockBackend { | ||||
|     char *name; | ||||
|     int refcnt; | ||||
| @@ -35,33 +29,15 @@ struct BlockBackend { | ||||
|     /* TODO change to DeviceState when all users are qdevified */ | ||||
|     const BlockDevOps *dev_ops; | ||||
|     void *dev_opaque; | ||||
|  | ||||
|     /* the block size for which the guest device expects atomicity */ | ||||
|     int guest_block_size; | ||||
|  | ||||
|     /* If the BDS tree is removed, some of its options are stored here (which | ||||
|      * can be used to restore those options in the new BDS on insert) */ | ||||
|     BlockBackendRootState root_state; | ||||
|  | ||||
|     /* I/O stats (display with "info blockstats"). */ | ||||
|     BlockAcctStats stats; | ||||
|  | ||||
|     BlockdevOnError on_read_error, on_write_error; | ||||
|     bool iostatus_enabled; | ||||
|     BlockDeviceIoStatus iostatus; | ||||
|  | ||||
|     NotifierList remove_bs_notifiers, insert_bs_notifiers; | ||||
| }; | ||||
|  | ||||
| typedef struct BlockBackendAIOCB { | ||||
|     BlockAIOCB common; | ||||
|     QEMUBH *bh; | ||||
|     BlockBackend *blk; | ||||
|     int ret; | ||||
| } BlockBackendAIOCB; | ||||
|  | ||||
| static const AIOCBInfo block_backend_aiocb_info = { | ||||
|     .get_aio_context = blk_aiocb_get_aio_context, | ||||
|     .aiocb_size = sizeof(BlockBackendAIOCB), | ||||
| }; | ||||
|  | ||||
| @@ -101,8 +77,6 @@ BlockBackend *blk_new(const char *name, Error **errp) | ||||
|     blk = g_new0(BlockBackend, 1); | ||||
|     blk->name = g_strdup(name); | ||||
|     blk->refcnt = 1; | ||||
|     notifier_list_init(&blk->remove_bs_notifiers); | ||||
|     notifier_list_init(&blk->insert_bs_notifiers); | ||||
|     QTAILQ_INSERT_TAIL(&blk_backends, blk, link); | ||||
|     return blk; | ||||
| } | ||||
| @@ -152,7 +126,7 @@ BlockBackend *blk_new_open(const char *name, const char *filename, | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_open(&blk->bs, filename, reference, options, flags, errp); | ||||
|     ret = bdrv_open(&blk->bs, filename, reference, options, flags, NULL, errp); | ||||
|     if (ret < 0) { | ||||
|         blk_unref(blk); | ||||
|         return NULL; | ||||
| @@ -166,13 +140,10 @@ static void blk_delete(BlockBackend *blk) | ||||
|     assert(!blk->refcnt); | ||||
|     assert(!blk->dev); | ||||
|     if (blk->bs) { | ||||
|         blk_remove_bs(blk); | ||||
|     } | ||||
|     assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers)); | ||||
|     assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers)); | ||||
|     if (blk->root_state.throttle_state) { | ||||
|         g_free(blk->root_state.throttle_group); | ||||
|         throttle_group_unref(blk->root_state.throttle_state); | ||||
|         assert(blk->bs->blk == blk); | ||||
|         blk->bs->blk = NULL; | ||||
|         bdrv_unref(blk->bs); | ||||
|         blk->bs = NULL; | ||||
|     } | ||||
|     /* Avoid double-remove after blk_hide_on_behalf_of_hmp_drive_del() */ | ||||
|     if (blk->name[0]) { | ||||
| @@ -180,7 +151,6 @@ static void blk_delete(BlockBackend *blk) | ||||
|     } | ||||
|     g_free(blk->name); | ||||
|     drive_info_del(blk->legacy_dinfo); | ||||
|     block_acct_cleanup(&blk->stats); | ||||
|     g_free(blk); | ||||
| } | ||||
|  | ||||
| @@ -194,11 +164,6 @@ static void drive_info_del(DriveInfo *dinfo) | ||||
|     g_free(dinfo); | ||||
| } | ||||
|  | ||||
| int blk_get_refcnt(BlockBackend *blk) | ||||
| { | ||||
|     return blk ? blk->refcnt : 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Increment @blk's reference count. | ||||
|  * @blk must not be null. | ||||
| @@ -223,21 +188,6 @@ void blk_unref(BlockBackend *blk) | ||||
|     } | ||||
| } | ||||
|  | ||||
| void blk_remove_all_bs(void) | ||||
| { | ||||
|     BlockBackend *blk; | ||||
|  | ||||
|     QTAILQ_FOREACH(blk, &blk_backends, link) { | ||||
|         AioContext *ctx = blk_get_aio_context(blk); | ||||
|  | ||||
|         aio_context_acquire(ctx); | ||||
|         if (blk->bs) { | ||||
|             blk_remove_bs(blk); | ||||
|         } | ||||
|         aio_context_release(ctx); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Return the BlockBackend after @blk. | ||||
|  * If @blk is null, return the first one. | ||||
| @@ -288,23 +238,6 @@ BlockDriverState *blk_bs(BlockBackend *blk) | ||||
|     return blk->bs; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Changes the BlockDriverState attached to @blk | ||||
|  */ | ||||
| void blk_set_bs(BlockBackend *blk, BlockDriverState *bs) | ||||
| { | ||||
|     bdrv_ref(bs); | ||||
|  | ||||
|     if (blk->bs) { | ||||
|         blk->bs->blk = NULL; | ||||
|         bdrv_unref(blk->bs); | ||||
|     } | ||||
|     assert(bs->blk == NULL); | ||||
|  | ||||
|     blk->bs = bs; | ||||
|     bs->blk = blk; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Return @blk's DriveInfo if any, else null. | ||||
|  */ | ||||
| @@ -358,35 +291,6 @@ void blk_hide_on_behalf_of_hmp_drive_del(BlockBackend *blk) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Disassociates the currently associated BlockDriverState from @blk. | ||||
|  */ | ||||
| void blk_remove_bs(BlockBackend *blk) | ||||
| { | ||||
|     assert(blk->bs->blk == blk); | ||||
|  | ||||
|     notifier_list_notify(&blk->remove_bs_notifiers, blk); | ||||
|  | ||||
|     blk_update_root_state(blk); | ||||
|  | ||||
|     blk->bs->blk = NULL; | ||||
|     bdrv_unref(blk->bs); | ||||
|     blk->bs = NULL; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Associates a new BlockDriverState with @blk. | ||||
|  */ | ||||
| void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs) | ||||
| { | ||||
|     assert(!blk->bs && !bs->blk); | ||||
|     bdrv_ref(bs); | ||||
|     blk->bs = bs; | ||||
|     bs->blk = blk; | ||||
|  | ||||
|     notifier_list_notify(&blk->insert_bs_notifiers, blk); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Attach device model @dev to @blk. | ||||
|  * Return 0 on success, -EBUSY when a device model is attached already. | ||||
| @@ -399,7 +303,7 @@ int blk_attach_dev(BlockBackend *blk, void *dev) | ||||
|     } | ||||
|     blk_ref(blk); | ||||
|     blk->dev = dev; | ||||
|     blk_iostatus_reset(blk); | ||||
|     bdrv_iostatus_reset(blk->bs); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @@ -426,7 +330,7 @@ void blk_detach_dev(BlockBackend *blk, void *dev) | ||||
|     blk->dev = NULL; | ||||
|     blk->dev_ops = NULL; | ||||
|     blk->dev_opaque = NULL; | ||||
|     blk->guest_block_size = 512; | ||||
|     bdrv_set_guest_block_size(blk->bs, 512); | ||||
|     blk_unref(blk); | ||||
| } | ||||
|  | ||||
| @@ -460,15 +364,18 @@ void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, | ||||
| void blk_dev_change_media_cb(BlockBackend *blk, bool load) | ||||
| { | ||||
|     if (blk->dev_ops && blk->dev_ops->change_media_cb) { | ||||
|         bool tray_was_open, tray_is_open; | ||||
|         bool tray_was_closed = !blk_dev_is_tray_open(blk); | ||||
|  | ||||
|         tray_was_open = blk_dev_is_tray_open(blk); | ||||
|         blk->dev_ops->change_media_cb(blk->dev_opaque, load); | ||||
|         tray_is_open = blk_dev_is_tray_open(blk); | ||||
|  | ||||
|         if (tray_was_open != tray_is_open) { | ||||
|             qapi_event_send_device_tray_moved(blk_name(blk), tray_is_open, | ||||
|                                               &error_abort); | ||||
|         if (tray_was_closed) { | ||||
|             /* tray open */ | ||||
|             qapi_event_send_device_tray_moved(blk_name(blk), | ||||
|                                               true, &error_abort); | ||||
|         } | ||||
|         if (load) { | ||||
|             /* tray close */ | ||||
|             qapi_event_send_device_tray_moved(blk_name(blk), | ||||
|                                               false, &error_abort); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -482,14 +389,6 @@ bool blk_dev_has_removable_media(BlockBackend *blk) | ||||
|     return !blk->dev || (blk->dev_ops && blk->dev_ops->change_media_cb); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Does @blk's attached device model have a tray? | ||||
|  */ | ||||
| bool blk_dev_has_tray(BlockBackend *blk) | ||||
| { | ||||
|     return blk->dev_ops && blk->dev_ops->is_tray_open; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Notify @blk's attached device model of a media eject request. | ||||
|  * If @force is true, the medium is about to be yanked out forcefully. | ||||
| @@ -506,7 +405,7 @@ void blk_dev_eject_request(BlockBackend *blk, bool force) | ||||
|  */ | ||||
| bool blk_dev_is_tray_open(BlockBackend *blk) | ||||
| { | ||||
|     if (blk_dev_has_tray(blk)) { | ||||
|     if (blk->dev_ops && blk->dev_ops->is_tray_open) { | ||||
|         return blk->dev_ops->is_tray_open(blk->dev_opaque); | ||||
|     } | ||||
|     return false; | ||||
| @@ -536,47 +435,7 @@ void blk_dev_resize_cb(BlockBackend *blk) | ||||
|  | ||||
| void blk_iostatus_enable(BlockBackend *blk) | ||||
| { | ||||
|     blk->iostatus_enabled = true; | ||||
|     blk->iostatus = BLOCK_DEVICE_IO_STATUS_OK; | ||||
| } | ||||
|  | ||||
| /* The I/O status is only enabled if the drive explicitly | ||||
|  * enables it _and_ the VM is configured to stop on errors */ | ||||
| bool blk_iostatus_is_enabled(const BlockBackend *blk) | ||||
| { | ||||
|     return (blk->iostatus_enabled && | ||||
|            (blk->on_write_error == BLOCKDEV_ON_ERROR_ENOSPC || | ||||
|             blk->on_write_error == BLOCKDEV_ON_ERROR_STOP   || | ||||
|             blk->on_read_error == BLOCKDEV_ON_ERROR_STOP)); | ||||
| } | ||||
|  | ||||
| BlockDeviceIoStatus blk_iostatus(const BlockBackend *blk) | ||||
| { | ||||
|     return blk->iostatus; | ||||
| } | ||||
|  | ||||
| void blk_iostatus_disable(BlockBackend *blk) | ||||
| { | ||||
|     blk->iostatus_enabled = false; | ||||
| } | ||||
|  | ||||
| void blk_iostatus_reset(BlockBackend *blk) | ||||
| { | ||||
|     if (blk_iostatus_is_enabled(blk)) { | ||||
|         blk->iostatus = BLOCK_DEVICE_IO_STATUS_OK; | ||||
|         if (blk->bs && blk->bs->job) { | ||||
|             block_job_iostatus_reset(blk->bs->job); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void blk_iostatus_set_err(BlockBackend *blk, int error) | ||||
| { | ||||
|     assert(blk_iostatus_is_enabled(blk)); | ||||
|     if (blk->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { | ||||
|         blk->iostatus = error == ENOSPC ? BLOCK_DEVICE_IO_STATUS_NOSPACE : | ||||
|                                           BLOCK_DEVICE_IO_STATUS_FAILED; | ||||
|     } | ||||
|     bdrv_iostatus_enable(blk->bs); | ||||
| } | ||||
|  | ||||
| static int blk_check_byte_request(BlockBackend *blk, int64_t offset, | ||||
| @@ -588,7 +447,7 @@ static int blk_check_byte_request(BlockBackend *blk, int64_t offset, | ||||
|         return -EIO; | ||||
|     } | ||||
|  | ||||
|     if (!blk_is_available(blk)) { | ||||
|     if (!blk_is_inserted(blk)) { | ||||
|         return -ENOMEDIUM; | ||||
|     } | ||||
|  | ||||
| @@ -656,17 +515,6 @@ int blk_write(BlockBackend *blk, int64_t sector_num, const uint8_t *buf, | ||||
|     return bdrv_write(blk->bs, sector_num, buf, nb_sectors); | ||||
| } | ||||
|  | ||||
| int blk_write_zeroes(BlockBackend *blk, int64_t sector_num, | ||||
|                      int nb_sectors, BdrvRequestFlags flags) | ||||
| { | ||||
|     int ret = blk_check_request(blk, sector_num, nb_sectors); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     return bdrv_write_zeroes(blk->bs, sector_num, nb_sectors, flags); | ||||
| } | ||||
|  | ||||
| static void error_callback_bh(void *opaque) | ||||
| { | ||||
|     struct BlockBackendAIOCB *acb = opaque; | ||||
| @@ -675,15 +523,13 @@ static void error_callback_bh(void *opaque) | ||||
|     qemu_aio_unref(acb); | ||||
| } | ||||
|  | ||||
| BlockAIOCB *blk_abort_aio_request(BlockBackend *blk, | ||||
|                                   BlockCompletionFunc *cb, | ||||
| static BlockAIOCB *abort_aio_request(BlockBackend *blk, BlockCompletionFunc *cb, | ||||
|                                      void *opaque, int ret) | ||||
| { | ||||
|     struct BlockBackendAIOCB *acb; | ||||
|     QEMUBH *bh; | ||||
|  | ||||
|     acb = blk_aio_get(&block_backend_aiocb_info, blk, cb, opaque); | ||||
|     acb->blk = blk; | ||||
|     acb->ret = ret; | ||||
|  | ||||
|     bh = aio_bh_new(blk_get_aio_context(blk), error_callback_bh, acb); | ||||
| @@ -699,7 +545,7 @@ BlockAIOCB *blk_aio_write_zeroes(BlockBackend *blk, int64_t sector_num, | ||||
| { | ||||
|     int ret = blk_check_request(blk, sector_num, nb_sectors); | ||||
|     if (ret < 0) { | ||||
|         return blk_abort_aio_request(blk, cb, opaque, ret); | ||||
|         return abort_aio_request(blk, cb, opaque, ret); | ||||
|     } | ||||
|  | ||||
|     return bdrv_aio_write_zeroes(blk->bs, sector_num, nb_sectors, flags, | ||||
| @@ -728,28 +574,16 @@ int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int count) | ||||
|  | ||||
| int64_t blk_getlength(BlockBackend *blk) | ||||
| { | ||||
|     if (!blk_is_available(blk)) { | ||||
|         return -ENOMEDIUM; | ||||
|     } | ||||
|  | ||||
|     return bdrv_getlength(blk->bs); | ||||
| } | ||||
|  | ||||
| void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr) | ||||
| { | ||||
|     if (!blk->bs) { | ||||
|         *nb_sectors_ptr = 0; | ||||
|     } else { | ||||
|     bdrv_get_geometry(blk->bs, nb_sectors_ptr); | ||||
| } | ||||
| } | ||||
|  | ||||
| int64_t blk_nb_sectors(BlockBackend *blk) | ||||
| { | ||||
|     if (!blk_is_available(blk)) { | ||||
|         return -ENOMEDIUM; | ||||
|     } | ||||
|  | ||||
|     return bdrv_nb_sectors(blk->bs); | ||||
| } | ||||
|  | ||||
| @@ -759,7 +593,7 @@ BlockAIOCB *blk_aio_readv(BlockBackend *blk, int64_t sector_num, | ||||
| { | ||||
|     int ret = blk_check_request(blk, sector_num, nb_sectors); | ||||
|     if (ret < 0) { | ||||
|         return blk_abort_aio_request(blk, cb, opaque, ret); | ||||
|         return abort_aio_request(blk, cb, opaque, ret); | ||||
|     } | ||||
|  | ||||
|     return bdrv_aio_readv(blk->bs, sector_num, iov, nb_sectors, cb, opaque); | ||||
| @@ -771,7 +605,7 @@ BlockAIOCB *blk_aio_writev(BlockBackend *blk, int64_t sector_num, | ||||
| { | ||||
|     int ret = blk_check_request(blk, sector_num, nb_sectors); | ||||
|     if (ret < 0) { | ||||
|         return blk_abort_aio_request(blk, cb, opaque, ret); | ||||
|         return abort_aio_request(blk, cb, opaque, ret); | ||||
|     } | ||||
|  | ||||
|     return bdrv_aio_writev(blk->bs, sector_num, iov, nb_sectors, cb, opaque); | ||||
| @@ -780,10 +614,6 @@ BlockAIOCB *blk_aio_writev(BlockBackend *blk, int64_t sector_num, | ||||
| BlockAIOCB *blk_aio_flush(BlockBackend *blk, | ||||
|                           BlockCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     if (!blk_is_available(blk)) { | ||||
|         return blk_abort_aio_request(blk, cb, opaque, -ENOMEDIUM); | ||||
|     } | ||||
|  | ||||
|     return bdrv_aio_flush(blk->bs, cb, opaque); | ||||
| } | ||||
|  | ||||
| @@ -793,7 +623,7 @@ BlockAIOCB *blk_aio_discard(BlockBackend *blk, | ||||
| { | ||||
|     int ret = blk_check_request(blk, sector_num, nb_sectors); | ||||
|     if (ret < 0) { | ||||
|         return blk_abort_aio_request(blk, cb, opaque, ret); | ||||
|         return abort_aio_request(blk, cb, opaque, ret); | ||||
|     } | ||||
|  | ||||
|     return bdrv_aio_discard(blk->bs, sector_num, nb_sectors, cb, opaque); | ||||
| @@ -825,20 +655,12 @@ int blk_aio_multiwrite(BlockBackend *blk, BlockRequest *reqs, int num_reqs) | ||||
|  | ||||
| int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) | ||||
| { | ||||
|     if (!blk_is_available(blk)) { | ||||
|         return -ENOMEDIUM; | ||||
|     } | ||||
|  | ||||
|     return bdrv_ioctl(blk->bs, req, buf); | ||||
| } | ||||
|  | ||||
| BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, | ||||
|                           BlockCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     if (!blk_is_available(blk)) { | ||||
|         return blk_abort_aio_request(blk, cb, opaque, -ENOMEDIUM); | ||||
|     } | ||||
|  | ||||
|     return bdrv_aio_ioctl(blk->bs, req, buf, cb, opaque); | ||||
| } | ||||
|  | ||||
| @@ -854,19 +676,11 @@ int blk_co_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors) | ||||
|  | ||||
| int blk_co_flush(BlockBackend *blk) | ||||
| { | ||||
|     if (!blk_is_available(blk)) { | ||||
|         return -ENOMEDIUM; | ||||
|     } | ||||
|  | ||||
|     return bdrv_co_flush(blk->bs); | ||||
| } | ||||
|  | ||||
| int blk_flush(BlockBackend *blk) | ||||
| { | ||||
|     if (!blk_is_available(blk)) { | ||||
|         return -ENOMEDIUM; | ||||
|     } | ||||
|  | ||||
|     return bdrv_flush(blk->bs); | ||||
| } | ||||
|  | ||||
| @@ -875,200 +689,81 @@ int blk_flush_all(void) | ||||
|     return bdrv_flush_all(); | ||||
| } | ||||
|  | ||||
| void blk_drain(BlockBackend *blk) | ||||
| { | ||||
|     if (blk->bs) { | ||||
|         bdrv_drain(blk->bs); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void blk_drain_all(void) | ||||
| { | ||||
|     bdrv_drain_all(); | ||||
| } | ||||
|  | ||||
| void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, | ||||
|                       BlockdevOnError on_write_error) | ||||
| { | ||||
|     blk->on_read_error = on_read_error; | ||||
|     blk->on_write_error = on_write_error; | ||||
| } | ||||
|  | ||||
| BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read) | ||||
| { | ||||
|     return is_read ? blk->on_read_error : blk->on_write_error; | ||||
|     return bdrv_get_on_error(blk->bs, is_read); | ||||
| } | ||||
|  | ||||
| BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, | ||||
|                                       int error) | ||||
| { | ||||
|     BlockdevOnError on_err = blk_get_on_error(blk, is_read); | ||||
|  | ||||
|     switch (on_err) { | ||||
|     case BLOCKDEV_ON_ERROR_ENOSPC: | ||||
|         return (error == ENOSPC) ? | ||||
|                BLOCK_ERROR_ACTION_STOP : BLOCK_ERROR_ACTION_REPORT; | ||||
|     case BLOCKDEV_ON_ERROR_STOP: | ||||
|         return BLOCK_ERROR_ACTION_STOP; | ||||
|     case BLOCKDEV_ON_ERROR_REPORT: | ||||
|         return BLOCK_ERROR_ACTION_REPORT; | ||||
|     case BLOCKDEV_ON_ERROR_IGNORE: | ||||
|         return BLOCK_ERROR_ACTION_IGNORE; | ||||
|     default: | ||||
|         abort(); | ||||
|     } | ||||
|     return bdrv_get_error_action(blk->bs, is_read, error); | ||||
| } | ||||
|  | ||||
| static void send_qmp_error_event(BlockBackend *blk, | ||||
|                                  BlockErrorAction action, | ||||
|                                  bool is_read, int error) | ||||
| { | ||||
|     IoOperationType optype; | ||||
|  | ||||
|     optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE; | ||||
|     qapi_event_send_block_io_error(blk_name(blk), optype, action, | ||||
|                                    blk_iostatus_is_enabled(blk), | ||||
|                                    error == ENOSPC, strerror(error), | ||||
|                                    &error_abort); | ||||
| } | ||||
|  | ||||
| /* This is done by device models because, while the block layer knows | ||||
|  * about the error, it does not know whether an operation comes from | ||||
|  * the device or the block layer (from a job, for example). | ||||
|  */ | ||||
| void blk_error_action(BlockBackend *blk, BlockErrorAction action, | ||||
|                       bool is_read, int error) | ||||
| { | ||||
|     assert(error >= 0); | ||||
|  | ||||
|     if (action == BLOCK_ERROR_ACTION_STOP) { | ||||
|         /* First set the iostatus, so that "info block" returns an iostatus | ||||
|          * that matches the events raised so far (an additional error iostatus | ||||
|          * is fine, but not a lost one). | ||||
|          */ | ||||
|         blk_iostatus_set_err(blk, error); | ||||
|  | ||||
|         /* Then raise the request to stop the VM and the event. | ||||
|          * qemu_system_vmstop_request_prepare has two effects.  First, | ||||
|          * it ensures that the STOP event always comes after the | ||||
|          * BLOCK_IO_ERROR event.  Second, it ensures that even if management | ||||
|          * can observe the STOP event and do a "cont" before the STOP | ||||
|          * event is issued, the VM will not stop.  In this case, vm_start() | ||||
|          * also ensures that the STOP/RESUME pair of events is emitted. | ||||
|          */ | ||||
|         qemu_system_vmstop_request_prepare(); | ||||
|         send_qmp_error_event(blk, action, is_read, error); | ||||
|         qemu_system_vmstop_request(RUN_STATE_IO_ERROR); | ||||
|     } else { | ||||
|         send_qmp_error_event(blk, action, is_read, error); | ||||
|     } | ||||
|     bdrv_error_action(blk->bs, action, is_read, error); | ||||
| } | ||||
|  | ||||
| int blk_is_read_only(BlockBackend *blk) | ||||
| { | ||||
|     if (blk->bs) { | ||||
|     return bdrv_is_read_only(blk->bs); | ||||
|     } else { | ||||
|         return blk->root_state.read_only; | ||||
|     } | ||||
| } | ||||
|  | ||||
| int blk_is_sg(BlockBackend *blk) | ||||
| { | ||||
|     if (!blk->bs) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     return bdrv_is_sg(blk->bs); | ||||
| } | ||||
|  | ||||
| int blk_enable_write_cache(BlockBackend *blk) | ||||
| { | ||||
|     if (blk->bs) { | ||||
|     return bdrv_enable_write_cache(blk->bs); | ||||
|     } else { | ||||
|         return !!(blk->root_state.open_flags & BDRV_O_CACHE_WB); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void blk_set_enable_write_cache(BlockBackend *blk, bool wce) | ||||
| { | ||||
|     if (blk->bs) { | ||||
|     bdrv_set_enable_write_cache(blk->bs, wce); | ||||
|     } else { | ||||
|         if (wce) { | ||||
|             blk->root_state.open_flags |= BDRV_O_CACHE_WB; | ||||
|         } else { | ||||
|             blk->root_state.open_flags &= ~BDRV_O_CACHE_WB; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void blk_invalidate_cache(BlockBackend *blk, Error **errp) | ||||
| { | ||||
|     if (!blk->bs) { | ||||
|         error_setg(errp, "Device '%s' has no medium", blk->name); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     bdrv_invalidate_cache(blk->bs, errp); | ||||
| } | ||||
|  | ||||
| bool blk_is_inserted(BlockBackend *blk) | ||||
| int blk_is_inserted(BlockBackend *blk) | ||||
| { | ||||
|     return blk->bs && bdrv_is_inserted(blk->bs); | ||||
| } | ||||
|  | ||||
| bool blk_is_available(BlockBackend *blk) | ||||
| { | ||||
|     return blk_is_inserted(blk) && !blk_dev_is_tray_open(blk); | ||||
|     return bdrv_is_inserted(blk->bs); | ||||
| } | ||||
|  | ||||
| void blk_lock_medium(BlockBackend *blk, bool locked) | ||||
| { | ||||
|     if (blk->bs) { | ||||
|     bdrv_lock_medium(blk->bs, locked); | ||||
| } | ||||
| } | ||||
|  | ||||
| void blk_eject(BlockBackend *blk, bool eject_flag) | ||||
| { | ||||
|     if (blk->bs) { | ||||
|     bdrv_eject(blk->bs, eject_flag); | ||||
| } | ||||
| } | ||||
|  | ||||
| int blk_get_flags(BlockBackend *blk) | ||||
| { | ||||
|     if (blk->bs) { | ||||
|     return bdrv_get_flags(blk->bs); | ||||
|     } else { | ||||
|         return blk->root_state.open_flags; | ||||
|     } | ||||
| } | ||||
|  | ||||
| int blk_get_max_transfer_length(BlockBackend *blk) | ||||
| { | ||||
|     if (blk->bs) { | ||||
|     return blk->bs->bl.max_transfer_length; | ||||
|     } else { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| int blk_get_max_iov(BlockBackend *blk) | ||||
| { | ||||
|     return blk->bs->bl.max_iov; | ||||
| } | ||||
|  | ||||
| void blk_set_guest_block_size(BlockBackend *blk, int align) | ||||
| { | ||||
|     blk->guest_block_size = align; | ||||
| } | ||||
|  | ||||
| void *blk_try_blockalign(BlockBackend *blk, size_t size) | ||||
| { | ||||
|     return qemu_try_blockalign(blk ? blk->bs : NULL, size); | ||||
|     bdrv_set_guest_block_size(blk->bs, align); | ||||
| } | ||||
|  | ||||
| void *blk_blockalign(BlockBackend *blk, size_t size) | ||||
| @@ -1078,65 +773,41 @@ void *blk_blockalign(BlockBackend *blk, size_t size) | ||||
|  | ||||
| bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp) | ||||
| { | ||||
|     if (!blk->bs) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     return bdrv_op_is_blocked(blk->bs, op, errp); | ||||
| } | ||||
|  | ||||
| void blk_op_unblock(BlockBackend *blk, BlockOpType op, Error *reason) | ||||
| { | ||||
|     if (blk->bs) { | ||||
|     bdrv_op_unblock(blk->bs, op, reason); | ||||
| } | ||||
| } | ||||
|  | ||||
| void blk_op_block_all(BlockBackend *blk, Error *reason) | ||||
| { | ||||
|     if (blk->bs) { | ||||
|     bdrv_op_block_all(blk->bs, reason); | ||||
| } | ||||
| } | ||||
|  | ||||
| void blk_op_unblock_all(BlockBackend *blk, Error *reason) | ||||
| { | ||||
|     if (blk->bs) { | ||||
|     bdrv_op_unblock_all(blk->bs, reason); | ||||
| } | ||||
| } | ||||
|  | ||||
| AioContext *blk_get_aio_context(BlockBackend *blk) | ||||
| { | ||||
|     if (blk->bs) { | ||||
|     return bdrv_get_aio_context(blk->bs); | ||||
|     } else { | ||||
|         return qemu_get_aio_context(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb) | ||||
| { | ||||
|     BlockBackendAIOCB *blk_acb = DO_UPCAST(BlockBackendAIOCB, common, acb); | ||||
|     return blk_get_aio_context(blk_acb->blk); | ||||
| } | ||||
|  | ||||
| void blk_set_aio_context(BlockBackend *blk, AioContext *new_context) | ||||
| { | ||||
|     if (blk->bs) { | ||||
|     bdrv_set_aio_context(blk->bs, new_context); | ||||
| } | ||||
| } | ||||
|  | ||||
| void blk_add_aio_context_notifier(BlockBackend *blk, | ||||
|         void (*attached_aio_context)(AioContext *new_context, void *opaque), | ||||
|         void (*detach_aio_context)(void *opaque), void *opaque) | ||||
| { | ||||
|     if (blk->bs) { | ||||
|     bdrv_add_aio_context_notifier(blk->bs, attached_aio_context, | ||||
|                                   detach_aio_context, opaque); | ||||
| } | ||||
| } | ||||
|  | ||||
| void blk_remove_aio_context_notifier(BlockBackend *blk, | ||||
|                                      void (*attached_aio_context)(AioContext *, | ||||
| @@ -1144,39 +815,28 @@ void blk_remove_aio_context_notifier(BlockBackend *blk, | ||||
|                                      void (*detach_aio_context)(void *), | ||||
|                                      void *opaque) | ||||
| { | ||||
|     if (blk->bs) { | ||||
|     bdrv_remove_aio_context_notifier(blk->bs, attached_aio_context, | ||||
|                                      detach_aio_context, opaque); | ||||
| } | ||||
| } | ||||
|  | ||||
| void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify) | ||||
| void blk_add_close_notifier(BlockBackend *blk, Notifier *notify) | ||||
| { | ||||
|     notifier_list_add(&blk->remove_bs_notifiers, notify); | ||||
| } | ||||
|  | ||||
| void blk_add_insert_bs_notifier(BlockBackend *blk, Notifier *notify) | ||||
| { | ||||
|     notifier_list_add(&blk->insert_bs_notifiers, notify); | ||||
|     bdrv_add_close_notifier(blk->bs, notify); | ||||
| } | ||||
|  | ||||
| void blk_io_plug(BlockBackend *blk) | ||||
| { | ||||
|     if (blk->bs) { | ||||
|     bdrv_io_plug(blk->bs); | ||||
| } | ||||
| } | ||||
|  | ||||
| void blk_io_unplug(BlockBackend *blk) | ||||
| { | ||||
|     if (blk->bs) { | ||||
|     bdrv_io_unplug(blk->bs); | ||||
| } | ||||
| } | ||||
|  | ||||
| BlockAcctStats *blk_get_stats(BlockBackend *blk) | ||||
| { | ||||
|     return &blk->stats; | ||||
|     return bdrv_get_stats(blk->bs); | ||||
| } | ||||
|  | ||||
| void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, | ||||
| @@ -1209,10 +869,6 @@ int blk_write_compressed(BlockBackend *blk, int64_t sector_num, | ||||
|  | ||||
| int blk_truncate(BlockBackend *blk, int64_t offset) | ||||
| { | ||||
|     if (!blk_is_available(blk)) { | ||||
|         return -ENOMEDIUM; | ||||
|     } | ||||
|  | ||||
|     return bdrv_truncate(blk->bs, offset); | ||||
| } | ||||
|  | ||||
| @@ -1229,94 +885,20 @@ int blk_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors) | ||||
| int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, | ||||
|                      int64_t pos, int size) | ||||
| { | ||||
|     if (!blk_is_available(blk)) { | ||||
|         return -ENOMEDIUM; | ||||
|     } | ||||
|  | ||||
|     return bdrv_save_vmstate(blk->bs, buf, pos, size); | ||||
| } | ||||
|  | ||||
| int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size) | ||||
| { | ||||
|     if (!blk_is_available(blk)) { | ||||
|         return -ENOMEDIUM; | ||||
|     } | ||||
|  | ||||
|     return bdrv_load_vmstate(blk->bs, buf, pos, size); | ||||
| } | ||||
|  | ||||
| int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz) | ||||
| { | ||||
|     if (!blk_is_available(blk)) { | ||||
|         return -ENOMEDIUM; | ||||
|     } | ||||
|  | ||||
|     return bdrv_probe_blocksizes(blk->bs, bsz); | ||||
| } | ||||
|  | ||||
| int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo) | ||||
| { | ||||
|     if (!blk_is_available(blk)) { | ||||
|         return -ENOMEDIUM; | ||||
|     } | ||||
|  | ||||
|     return bdrv_probe_geometry(blk->bs, geo); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Updates the BlockBackendRootState object with data from the currently | ||||
|  * attached BlockDriverState. | ||||
|  */ | ||||
| void blk_update_root_state(BlockBackend *blk) | ||||
| { | ||||
|     assert(blk->bs); | ||||
|  | ||||
|     blk->root_state.open_flags    = blk->bs->open_flags; | ||||
|     blk->root_state.read_only     = blk->bs->read_only; | ||||
|     blk->root_state.detect_zeroes = blk->bs->detect_zeroes; | ||||
|  | ||||
|     if (blk->root_state.throttle_group) { | ||||
|         g_free(blk->root_state.throttle_group); | ||||
|         throttle_group_unref(blk->root_state.throttle_state); | ||||
|     } | ||||
|     if (blk->bs->throttle_state) { | ||||
|         const char *name = throttle_group_get_name(blk->bs); | ||||
|         blk->root_state.throttle_group = g_strdup(name); | ||||
|         blk->root_state.throttle_state = throttle_group_incref(name); | ||||
|     } else { | ||||
|         blk->root_state.throttle_group = NULL; | ||||
|         blk->root_state.throttle_state = NULL; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Applies the information in the root state to the given BlockDriverState. This | ||||
|  * does not include the flags which have to be specified for bdrv_open(), use | ||||
|  * blk_get_open_flags_from_root_state() to inquire them. | ||||
|  */ | ||||
| void blk_apply_root_state(BlockBackend *blk, BlockDriverState *bs) | ||||
| { | ||||
|     bs->detect_zeroes = blk->root_state.detect_zeroes; | ||||
|     if (blk->root_state.throttle_group) { | ||||
|         bdrv_io_limits_enable(bs, blk->root_state.throttle_group); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Returns the flags to be used for bdrv_open() of a BlockDriverState which is | ||||
|  * supposed to inherit the root state. | ||||
|  */ | ||||
| int blk_get_open_flags_from_root_state(BlockBackend *blk) | ||||
| { | ||||
|     int bs_flags; | ||||
|  | ||||
|     bs_flags = blk->root_state.read_only ? 0 : BDRV_O_RDWR; | ||||
|     bs_flags |= blk->root_state.open_flags & ~BDRV_O_RDWR; | ||||
|  | ||||
|     return bs_flags; | ||||
| } | ||||
|  | ||||
| BlockBackendRootState *blk_get_root_state(BlockBackend *blk) | ||||
| { | ||||
|     return &blk->root_state; | ||||
| } | ||||
|   | ||||
| @@ -22,7 +22,6 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "block/block_int.h" | ||||
| #include "qemu/module.h" | ||||
| @@ -104,7 +103,7 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|  | ||||
|     bs->read_only = 1; // no write support yet | ||||
|  | ||||
|     ret = bdrv_pread(bs->file->bs, 0, &bochs, sizeof(bochs)); | ||||
|     ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
| @@ -138,7 +137,7 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         return -ENOMEM; | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_pread(bs->file->bs, le32_to_cpu(bochs.header), s->catalog_bitmap, | ||||
|     ret = bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap, | ||||
|                      s->catalog_size * 4); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
| @@ -207,7 +206,7 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) | ||||
|         (s->extent_blocks + s->bitmap_blocks)); | ||||
|  | ||||
|     /* read in bitmap for current extent */ | ||||
|     ret = bdrv_pread(bs->file->bs, bitmap_offset + (extent_offset / 8), | ||||
|     ret = bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8), | ||||
|                      &bitmap_entry, 1); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
| @@ -230,7 +229,7 @@ static int bochs_read(BlockDriverState *bs, int64_t sector_num, | ||||
|         if (block_offset < 0) { | ||||
|             return block_offset; | ||||
|         } else if (block_offset > 0) { | ||||
|             ret = bdrv_pread(bs->file->bs, block_offset, buf, 512); | ||||
|             ret = bdrv_pread(bs->file, block_offset, buf, 512); | ||||
|             if (ret < 0) { | ||||
|                 return ret; | ||||
|             } | ||||
|   | ||||
| @@ -21,7 +21,6 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "block/block_int.h" | ||||
| #include "qemu/module.h" | ||||
| @@ -67,7 +66,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     bs->read_only = 1; | ||||
|  | ||||
|     /* read header */ | ||||
|     ret = bdrv_pread(bs->file->bs, 128, &s->block_size, 4); | ||||
|     ret = bdrv_pread(bs->file, 128, &s->block_size, 4); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
| @@ -93,7 +92,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_pread(bs->file->bs, 128 + 4, &s->n_blocks, 4); | ||||
|     ret = bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
| @@ -124,7 +123,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         return -ENOMEM; | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_pread(bs->file->bs, 128 + 4 + 4, s->offsets, offsets_size); | ||||
|     ret = bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
| @@ -204,8 +203,8 @@ static inline int cloop_read_block(BlockDriverState *bs, int block_num) | ||||
|         int ret; | ||||
|         uint32_t bytes = s->offsets[block_num + 1] - s->offsets[block_num]; | ||||
|  | ||||
|         ret = bdrv_pread(bs->file->bs, s->offsets[block_num], | ||||
|                          s->compressed_block, bytes); | ||||
|         ret = bdrv_pread(bs->file, s->offsets[block_num], s->compressed_block, | ||||
|                          bytes); | ||||
|         if (ret != bytes) { | ||||
|             return -1; | ||||
|         } | ||||
|   | ||||
| @@ -12,13 +12,10 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "trace.h" | ||||
| #include "block/block_int.h" | ||||
| #include "block/blockjob.h" | ||||
| #include "qapi/qmp/qerror.h" | ||||
| #include "qemu/ratelimit.h" | ||||
| #include "sysemu/block-backend.h" | ||||
|  | ||||
| enum { | ||||
|     /* | ||||
| @@ -189,7 +186,7 @@ static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp) | ||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common); | ||||
|  | ||||
|     if (speed < 0) { | ||||
|         error_setg(errp, QERR_INVALID_PARAMETER, "speed"); | ||||
|         error_set(errp, QERR_INVALID_PARAMETER, "speed"); | ||||
|         return; | ||||
|     } | ||||
|     ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME); | ||||
| @@ -215,7 +212,7 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base, | ||||
|  | ||||
|     if ((on_error == BLOCKDEV_ON_ERROR_STOP || | ||||
|          on_error == BLOCKDEV_ON_ERROR_ENOSPC) && | ||||
|         (!bs->blk || !blk_iostatus_is_enabled(bs->blk))) { | ||||
|         !bdrv_iostatus_is_enabled(bs)) { | ||||
|         error_setg(errp, "Invalid parameter combination"); | ||||
|         return; | ||||
|     } | ||||
| @@ -237,14 +234,14 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base, | ||||
|     orig_overlay_flags = bdrv_get_flags(overlay_bs); | ||||
|  | ||||
|     /* convert base & overlay_bs to r/w, if necessary */ | ||||
|     if (!(orig_overlay_flags & BDRV_O_RDWR)) { | ||||
|         reopen_queue = bdrv_reopen_queue(reopen_queue, overlay_bs, NULL, | ||||
|                                          orig_overlay_flags | BDRV_O_RDWR); | ||||
|     } | ||||
|     if (!(orig_base_flags & BDRV_O_RDWR)) { | ||||
|         reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL, | ||||
|         reopen_queue = bdrv_reopen_queue(reopen_queue, base, | ||||
|                                          orig_base_flags | BDRV_O_RDWR); | ||||
|     } | ||||
|     if (!(orig_overlay_flags & BDRV_O_RDWR)) { | ||||
|         reopen_queue = bdrv_reopen_queue(reopen_queue, overlay_bs, | ||||
|                                          orig_overlay_flags | BDRV_O_RDWR); | ||||
|     } | ||||
|     if (reopen_queue) { | ||||
|         bdrv_reopen_multiple(reopen_queue, &local_err); | ||||
|         if (local_err != NULL) { | ||||
|   | ||||
							
								
								
									
										31
									
								
								block/curl.c
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								block/curl.c
									
									
									
									
									
								
							| @@ -21,12 +21,9 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "block/block_int.h" | ||||
| #include "qapi/qmp/qbool.h" | ||||
| #include "qapi/qmp/qstring.h" | ||||
| #include <curl/curl.h> | ||||
|  | ||||
| // #define DEBUG_CURL | ||||
| @@ -155,20 +152,18 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, | ||||
|     DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, fd); | ||||
|     switch (action) { | ||||
|         case CURL_POLL_IN: | ||||
|             aio_set_fd_handler(s->aio_context, fd, false, | ||||
|                                curl_multi_read, NULL, state); | ||||
|             aio_set_fd_handler(s->aio_context, fd, curl_multi_read, | ||||
|                                NULL, state); | ||||
|             break; | ||||
|         case CURL_POLL_OUT: | ||||
|             aio_set_fd_handler(s->aio_context, fd, false, | ||||
|                                NULL, curl_multi_do, state); | ||||
|             aio_set_fd_handler(s->aio_context, fd, NULL, curl_multi_do, state); | ||||
|             break; | ||||
|         case CURL_POLL_INOUT: | ||||
|             aio_set_fd_handler(s->aio_context, fd, false, | ||||
|                                curl_multi_read, curl_multi_do, state); | ||||
|             aio_set_fd_handler(s->aio_context, fd, curl_multi_read, | ||||
|                                curl_multi_do, state); | ||||
|             break; | ||||
|         case CURL_POLL_REMOVE: | ||||
|             aio_set_fd_handler(s->aio_context, fd, false, | ||||
|                                NULL, NULL, NULL); | ||||
|             aio_set_fd_handler(s->aio_context, fd, NULL, NULL, NULL); | ||||
|             break; | ||||
|     } | ||||
|  | ||||
| @@ -302,18 +297,6 @@ static void curl_multi_check_completion(BDRVCURLState *s) | ||||
|             /* ACBs for successful messages get completed in curl_read_cb */ | ||||
|             if (msg->data.result != CURLE_OK) { | ||||
|                 int i; | ||||
|                 static int errcount = 100; | ||||
|  | ||||
|                 /* Don't lose the original error message from curl, since | ||||
|                  * it contains extra data. | ||||
|                  */ | ||||
|                 if (errcount > 0) { | ||||
|                     error_report("curl: %s", state->errmsg); | ||||
|                     if (--errcount == 0) { | ||||
|                         error_report("curl: further errors suppressed"); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 for (i = 0; i < CURL_NUM_ACB; i++) { | ||||
|                     CURLAIOCB *acb = state->acb[i]; | ||||
|  | ||||
| @@ -321,7 +304,7 @@ static void curl_multi_check_completion(BDRVCURLState *s) | ||||
|                         continue; | ||||
|                     } | ||||
|  | ||||
|                     acb->common.cb(acb->common.opaque, -EPROTO); | ||||
|                     acb->common.cb(acb->common.opaque, -EIO); | ||||
|                     qemu_aio_unref(acb); | ||||
|                     state->acb[i] = NULL; | ||||
|                 } | ||||
|   | ||||
							
								
								
									
										584
									
								
								block/dictzip.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										584
									
								
								block/dictzip.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,584 @@ | ||||
| /* | ||||
|  * DictZip Block driver for dictzip enabled gzip files | ||||
|  * | ||||
|  * Use the "dictzip" tool from the "dictd" package to create gzip files that | ||||
|  * contain the extra DictZip headers. | ||||
|  * | ||||
|  * dictzip(1) is a compression program which creates compressed files in the | ||||
|  * gzip format (see RFC 1952). However, unlike gzip(1), dictzip(1) compresses | ||||
|  * the file in pieces and stores an index to the pieces in the gzip header. | ||||
|  * This allows random access to the file at the granularity of the compressed | ||||
|  * pieces (currently about 64kB) while maintaining good compression ratios | ||||
|  * (within 5% of the expected ratio for dictionary data). | ||||
|  * dictd(8) uses files stored in this format. | ||||
|  * | ||||
|  * For details on DictZip see http://dict.org/. | ||||
|  * | ||||
|  * Copyright (c) 2009 Alexander Graf <agraf@suse.de> | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "qemu-common.h" | ||||
| #include "block/block_int.h" | ||||
| #include <zlib.h> | ||||
|  | ||||
| // #define DEBUG | ||||
|  | ||||
| #ifdef DEBUG | ||||
| #define dprintf(fmt, ...) do { printf("dzip: " fmt, ## __VA_ARGS__); } while (0) | ||||
| #else | ||||
| #define dprintf(fmt, ...) do { } while (0) | ||||
| #endif | ||||
|  | ||||
| #define SECTOR_SIZE 512 | ||||
| #define Z_STREAM_COUNT 4 | ||||
| #define CACHE_COUNT 20 | ||||
|  | ||||
| /* magic values */ | ||||
|  | ||||
| #define GZ_MAGIC1     0x1f | ||||
| #define GZ_MAGIC2     0x8b | ||||
| #define DZ_MAGIC1      'R' | ||||
| #define DZ_MAGIC2      'A' | ||||
|  | ||||
| #define GZ_FEXTRA     0x04      /* Optional field (random access index)    */ | ||||
| #define GZ_FNAME      0x08      /* Original name                           */ | ||||
| #define GZ_COMMENT    0x10      /* Zero-terminated, human-readable comment */ | ||||
| #define GZ_FHCRC      0x02      /* Header CRC16                            */ | ||||
|  | ||||
| /* offsets */ | ||||
|  | ||||
| #define GZ_ID            0      /* GZ_MAGIC (16bit)                        */ | ||||
| #define GZ_FLG           3      /* FLaGs (see above)                       */ | ||||
| #define GZ_XLEN         10      /* eXtra LENgth (16bit)                    */ | ||||
| #define GZ_SI           12      /* Subfield ID (16bit)                     */ | ||||
| #define GZ_VERSION      16      /* Version for subfield format             */ | ||||
| #define GZ_CHUNKSIZE    18      /* Chunk size (16bit)                      */ | ||||
| #define GZ_CHUNKCNT     20      /* Number of chunks (16bit)                */ | ||||
| #define GZ_RNDDATA      22      /* Random access data (16bit)              */ | ||||
|  | ||||
| #define GZ_99_CHUNKSIZE 18      /* Chunk size (32bit)                      */ | ||||
| #define GZ_99_CHUNKCNT  22      /* Number of chunks (32bit)                */ | ||||
| #define GZ_99_FILESIZE  26      /* Size of unpacked file (64bit)           */ | ||||
| #define GZ_99_RNDDATA   34      /* Random access data (32bit)              */ | ||||
|  | ||||
| struct BDRVDictZipState; | ||||
|  | ||||
| typedef struct DictZipAIOCB { | ||||
|     BlockAIOCB common; | ||||
|     struct BDRVDictZipState *s; | ||||
|     QEMUIOVector *qiov;          /* QIOV of the original request */ | ||||
|     QEMUIOVector *qiov_gz;       /* QIOV of the gz subrequest */ | ||||
|     QEMUBH *bh;                  /* BH for cache */ | ||||
|     z_stream *zStream;           /* stream to use for decoding */ | ||||
|     int zStream_id;              /* stream id of the above pointer */ | ||||
|     size_t start;                /* offset into the uncompressed file */ | ||||
|     size_t len;                  /* uncompressed bytes to read */ | ||||
|     uint8_t *gzipped;            /* the gzipped data */ | ||||
|     uint8_t *buf;                /* cached result */ | ||||
|     size_t gz_len;               /* amount of gzip data */ | ||||
|     size_t gz_start;             /* uncompressed starting point of gzip data */ | ||||
|     uint64_t offset;             /* offset for "start" into the uncompressed chunk */ | ||||
|     int chunks_len;              /* amount of uncompressed data in all gzip data */ | ||||
| } DictZipAIOCB; | ||||
|  | ||||
| typedef struct dict_cache { | ||||
|     size_t start; | ||||
|     size_t len; | ||||
|     uint8_t *buf; | ||||
| } DictCache; | ||||
|  | ||||
| typedef struct BDRVDictZipState { | ||||
|     BlockDriverState *hd; | ||||
|     z_stream zStream[Z_STREAM_COUNT]; | ||||
|     DictCache cache[CACHE_COUNT]; | ||||
|     int cache_index; | ||||
|     uint8_t  stream_in_use; | ||||
|     uint64_t chunk_len; | ||||
|     uint32_t chunk_cnt; | ||||
|     uint16_t *chunks; | ||||
|     uint32_t *chunks32; | ||||
|     uint64_t *offsets; | ||||
|     int64_t file_len; | ||||
| } BDRVDictZipState; | ||||
|  | ||||
| static int start_zStream(z_stream *zStream) | ||||
| { | ||||
|     zStream->zalloc    = NULL; | ||||
|     zStream->zfree     = NULL; | ||||
|     zStream->opaque    = NULL; | ||||
|     zStream->next_in   = 0; | ||||
|     zStream->avail_in  = 0; | ||||
|     zStream->next_out  = NULL; | ||||
|     zStream->avail_out = 0; | ||||
|  | ||||
|     return inflateInit2( zStream, -15 ); | ||||
| } | ||||
|  | ||||
| static QemuOptsList runtime_opts = { | ||||
|     .name = "dzip", | ||||
|     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), | ||||
|     .desc = { | ||||
|         { | ||||
|             .name = "filename", | ||||
|             .type = QEMU_OPT_STRING, | ||||
|             .help = "URL to the dictzip file", | ||||
|         }, | ||||
|         { /* end of list */ } | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| static int dictzip_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) | ||||
| { | ||||
|     BDRVDictZipState *s = bs->opaque; | ||||
|     const char *err = "Unknown (read error?)"; | ||||
|     uint8_t magic[2]; | ||||
|     char buf[100]; | ||||
|     uint8_t header_flags; | ||||
|     uint16_t chunk_len16; | ||||
|     uint16_t chunk_cnt16; | ||||
|     uint32_t chunk_len32; | ||||
|     uint16_t header_ver; | ||||
|     uint16_t tmp_short; | ||||
|     uint64_t offset; | ||||
|     int chunks_len; | ||||
|     int headerLength = GZ_XLEN - 1; | ||||
|     int rnd_offs; | ||||
|     int ret; | ||||
|     int i; | ||||
|     QemuOpts *opts; | ||||
|     Error *local_err = NULL; | ||||
|     const char *filename; | ||||
|  | ||||
|     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); | ||||
|     qemu_opts_absorb_qdict(opts, options, &local_err); | ||||
|     if (local_err != NULL) { | ||||
|         error_propagate(errp, local_err); | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     filename = qemu_opt_get(opts, "filename"); | ||||
|  | ||||
|     if (!strncmp(filename, "dzip://", 7)) | ||||
|         filename += 7; | ||||
|     else if (!strncmp(filename, "dzip:", 5)) | ||||
|         filename += 5; | ||||
|  | ||||
|     ret = bdrv_open(&s->hd, filename, NULL, NULL, flags | BDRV_O_PROTOCOL, NULL, &local_err); | ||||
|     if (ret < 0) { | ||||
|         error_propagate(errp, local_err); | ||||
|         qemu_opts_del(opts); | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     /* initialize zlib streams */ | ||||
|     for (i = 0; i < Z_STREAM_COUNT; i++) { | ||||
|         if (start_zStream( &s->zStream[i] ) != Z_OK) { | ||||
|             err = s->zStream[i].msg; | ||||
|             goto fail; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* gzip header */ | ||||
|     if (bdrv_pread(s->hd, GZ_ID, &magic, sizeof(magic)) != sizeof(magic)) | ||||
|         goto fail; | ||||
|  | ||||
|     if (!((magic[0] == GZ_MAGIC1) && (magic[1] == GZ_MAGIC2))) { | ||||
|         err = "No gzip file"; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     /* dzip header */ | ||||
|     if (bdrv_pread(s->hd, GZ_FLG, &header_flags, 1) != 1) | ||||
|         goto fail; | ||||
|  | ||||
|     if (!(header_flags & GZ_FEXTRA)) { | ||||
|         err = "Not a dictzip file (wrong flags)"; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     /* extra length */ | ||||
|     if (bdrv_pread(s->hd, GZ_XLEN, &tmp_short, 2) != 2) | ||||
|         goto fail; | ||||
|  | ||||
|     headerLength += le16_to_cpu(tmp_short) + 2; | ||||
|  | ||||
|     /* DictZip magic */ | ||||
|     if (bdrv_pread(s->hd, GZ_SI, &magic, 2) != 2) | ||||
|         goto fail; | ||||
|  | ||||
|     if (magic[0] != DZ_MAGIC1 || magic[1] != DZ_MAGIC2) { | ||||
|         err = "Not a dictzip file (missing extra magic)"; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     /* DictZip version */ | ||||
|     if (bdrv_pread(s->hd, GZ_VERSION, &header_ver, 2) != 2) | ||||
|         goto fail; | ||||
|  | ||||
|     header_ver = le16_to_cpu(header_ver); | ||||
|  | ||||
|     switch (header_ver) { | ||||
|         case 1: /* Normal DictZip */ | ||||
|             /* number of chunks */ | ||||
|             if (bdrv_pread(s->hd, GZ_CHUNKSIZE, &chunk_len16, 2) != 2) | ||||
|                 goto fail; | ||||
|  | ||||
|             s->chunk_len = le16_to_cpu(chunk_len16); | ||||
|  | ||||
|             /* chunk count */ | ||||
|             if (bdrv_pread(s->hd, GZ_CHUNKCNT, &chunk_cnt16, 2) != 2) | ||||
|                 goto fail; | ||||
|  | ||||
|             s->chunk_cnt = le16_to_cpu(chunk_cnt16); | ||||
|             chunks_len = sizeof(short) * s->chunk_cnt; | ||||
|             rnd_offs = GZ_RNDDATA; | ||||
|             break; | ||||
|         case 99: /* Special Alex pigz version */ | ||||
|             /* number of chunks */ | ||||
|             if (bdrv_pread(s->hd, GZ_99_CHUNKSIZE, &chunk_len32, 4) != 4) | ||||
|                 goto fail; | ||||
|  | ||||
|             dprintf("chunk len [%#x] = %d\n", GZ_99_CHUNKSIZE, chunk_len32); | ||||
|             s->chunk_len = le32_to_cpu(chunk_len32); | ||||
|  | ||||
|             /* chunk count */ | ||||
|             if (bdrv_pread(s->hd, GZ_99_CHUNKCNT, &s->chunk_cnt, 4) != 4) | ||||
|                 goto fail; | ||||
|  | ||||
|             s->chunk_cnt = le32_to_cpu(s->chunk_cnt); | ||||
|  | ||||
|             dprintf("chunk len | count = %"PRId64" | %d\n", s->chunk_len, s->chunk_cnt); | ||||
|  | ||||
|             /* file size */ | ||||
|             if (bdrv_pread(s->hd, GZ_99_FILESIZE, &s->file_len, 8) != 8) | ||||
|                 goto fail; | ||||
|  | ||||
|             s->file_len = le64_to_cpu(s->file_len); | ||||
|             chunks_len = sizeof(int) * s->chunk_cnt; | ||||
|             rnd_offs = GZ_99_RNDDATA; | ||||
|             break; | ||||
|         default: | ||||
|             err = "Invalid DictZip version"; | ||||
|             goto fail; | ||||
|     } | ||||
|  | ||||
|     /* random access data */ | ||||
|     s->chunks = g_malloc(chunks_len); | ||||
|     if (header_ver == 99) | ||||
|         s->chunks32 = (uint32_t *)s->chunks; | ||||
|  | ||||
|     if (bdrv_pread(s->hd, rnd_offs, s->chunks, chunks_len) != chunks_len) | ||||
|         goto fail; | ||||
|  | ||||
|     /* orig filename */ | ||||
|     if (header_flags & GZ_FNAME) { | ||||
|         if (bdrv_pread(s->hd, headerLength + 1, buf, sizeof(buf)) != sizeof(buf)) | ||||
|             goto fail; | ||||
|  | ||||
|         buf[sizeof(buf) - 1] = '\0'; | ||||
|         headerLength += strlen(buf) + 1; | ||||
|  | ||||
|         if (strlen(buf) == sizeof(buf)) | ||||
|             goto fail; | ||||
|  | ||||
|         dprintf("filename: %s\n", buf); | ||||
|     } | ||||
|  | ||||
|     /* comment field */ | ||||
|     if (header_flags & GZ_COMMENT) { | ||||
|         if (bdrv_pread(s->hd, headerLength, buf, sizeof(buf)) != sizeof(buf)) | ||||
|             goto fail; | ||||
|  | ||||
|         buf[sizeof(buf) - 1] = '\0'; | ||||
|         headerLength += strlen(buf) + 1; | ||||
|  | ||||
|         if (strlen(buf) == sizeof(buf)) | ||||
|             goto fail; | ||||
|  | ||||
|         dprintf("comment: %s\n", buf); | ||||
|     } | ||||
|  | ||||
|     if (header_flags & GZ_FHCRC) | ||||
|         headerLength += 2; | ||||
|  | ||||
|     /* uncompressed file length*/ | ||||
|     if (!s->file_len) { | ||||
|         uint32_t file_len; | ||||
|  | ||||
|         if (bdrv_pread(s->hd, bdrv_getlength(s->hd) - 4, &file_len, 4) != 4) | ||||
|             goto fail; | ||||
|  | ||||
|         s->file_len = le32_to_cpu(file_len); | ||||
|     } | ||||
|  | ||||
|     /* compute offsets */ | ||||
|     s->offsets = g_malloc(sizeof( *s->offsets ) * s->chunk_cnt); | ||||
|  | ||||
|     for (offset = headerLength + 1, i = 0; i < s->chunk_cnt; i++) { | ||||
|         s->offsets[i] = offset; | ||||
|         switch (header_ver) { | ||||
|         case 1: | ||||
|             offset += le16_to_cpu(s->chunks[i]); | ||||
|             break; | ||||
|         case 99: | ||||
|             offset += le32_to_cpu(s->chunks32[i]); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         dprintf("chunk %#"PRIx64" - %#"PRIx64" = offset %#"PRIx64" -> %#"PRIx64"\n", i * s->chunk_len, (i+1) * s->chunk_len, s->offsets[i], offset); | ||||
|     } | ||||
|     qemu_opts_del(opts); | ||||
|  | ||||
|     return 0; | ||||
|  | ||||
| fail: | ||||
|     fprintf(stderr, "DictZip: Error opening file: %s\n", err); | ||||
|     bdrv_unref(s->hd); | ||||
|     if (s->chunks) | ||||
|         g_free(s->chunks); | ||||
|     qemu_opts_del(opts); | ||||
|     return -EINVAL; | ||||
| } | ||||
|  | ||||
| /* This callback gets invoked when we have the result in cache already */ | ||||
| static void dictzip_cache_cb(void *opaque) | ||||
| { | ||||
|     DictZipAIOCB *acb = (DictZipAIOCB *)opaque; | ||||
|  | ||||
|     qemu_iovec_from_buf(acb->qiov, 0, acb->buf, acb->len); | ||||
|     acb->common.cb(acb->common.opaque, 0); | ||||
|     qemu_bh_delete(acb->bh); | ||||
|     qemu_aio_unref(acb); | ||||
| } | ||||
|  | ||||
| /* This callback gets invoked by the underlying block reader when we have | ||||
|  * all compressed data. We uncompress in here. */ | ||||
| static void dictzip_read_cb(void *opaque, int ret) | ||||
| { | ||||
|     DictZipAIOCB *acb = (DictZipAIOCB *)opaque; | ||||
|     struct BDRVDictZipState *s = acb->s; | ||||
|     uint8_t *buf; | ||||
|     DictCache *cache; | ||||
|     int r, i; | ||||
|  | ||||
|     buf = g_malloc(acb->chunks_len); | ||||
|  | ||||
|     /* try to find zlib stream for decoding */ | ||||
|     do { | ||||
|         for (i = 0; i < Z_STREAM_COUNT; i++) { | ||||
|             if (!(s->stream_in_use & (1 << i))) { | ||||
|                 s->stream_in_use |= (1 << i); | ||||
|                 acb->zStream_id = i; | ||||
|                 acb->zStream = &s->zStream[i]; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } while(!acb->zStream); | ||||
|  | ||||
|     /* sure, we could handle more streams, but this callback should be single | ||||
|        threaded and when it's not, we really want to know! */ | ||||
|     assert(i == 0); | ||||
|  | ||||
|     /* uncompress the chunk */ | ||||
|     acb->zStream->next_in   = acb->gzipped; | ||||
|     acb->zStream->avail_in  = acb->gz_len; | ||||
|     acb->zStream->next_out  = buf; | ||||
|     acb->zStream->avail_out = acb->chunks_len; | ||||
|  | ||||
|     r = inflate( acb->zStream,  Z_PARTIAL_FLUSH ); | ||||
|     if ( (r != Z_OK) && (r != Z_STREAM_END) ) | ||||
|         fprintf(stderr, "Error inflating: [%d] %s\n", r, acb->zStream->msg); | ||||
|  | ||||
|     if ( r == Z_STREAM_END ) | ||||
|         inflateReset(acb->zStream); | ||||
|  | ||||
|     dprintf("inflating [%d] left: %d | %d bytes\n", r, acb->zStream->avail_in, acb->zStream->avail_out); | ||||
|     s->stream_in_use &= ~(1 << acb->zStream_id); | ||||
|  | ||||
|     /* nofity the caller */ | ||||
|     qemu_iovec_from_buf(acb->qiov, 0, buf + acb->offset, acb->len); | ||||
|     acb->common.cb(acb->common.opaque, 0); | ||||
|  | ||||
|     /* fill the cache */ | ||||
|     cache = &s->cache[s->cache_index]; | ||||
|     s->cache_index++; | ||||
|     if (s->cache_index == CACHE_COUNT) | ||||
|         s->cache_index = 0; | ||||
|  | ||||
|     cache->len = 0; | ||||
|     if (cache->buf) | ||||
|         g_free(cache->buf); | ||||
|     cache->start = acb->gz_start; | ||||
|     cache->buf = buf; | ||||
|     cache->len = acb->chunks_len; | ||||
|  | ||||
|     /* free occupied ressources */ | ||||
|     g_free(acb->qiov_gz); | ||||
|     qemu_aio_unref(acb); | ||||
| } | ||||
|  | ||||
| static const AIOCBInfo dictzip_aiocb_info = { | ||||
|     .aiocb_size         = sizeof(DictZipAIOCB), | ||||
| }; | ||||
|  | ||||
| /* This is where we get a request from a caller to read something */ | ||||
| static BlockAIOCB *dictzip_aio_readv(BlockDriverState *bs, | ||||
|         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | ||||
|         BlockCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     BDRVDictZipState *s = bs->opaque; | ||||
|     DictZipAIOCB *acb; | ||||
|     QEMUIOVector *qiov_gz; | ||||
|     struct iovec *iov; | ||||
|     uint8_t *buf; | ||||
|     size_t  start = sector_num * SECTOR_SIZE; | ||||
|     size_t  len = nb_sectors * SECTOR_SIZE; | ||||
|     size_t  end = start + len; | ||||
|     size_t  gz_start; | ||||
|     size_t  gz_len; | ||||
|     int64_t gz_sector_num; | ||||
|     int     gz_nb_sectors; | ||||
|     int     first_chunk, last_chunk; | ||||
|     int     first_offset; | ||||
|     int     i; | ||||
|  | ||||
|     acb = qemu_aio_get(&dictzip_aiocb_info, bs, cb, opaque); | ||||
|     if (!acb) | ||||
|         return NULL; | ||||
|  | ||||
|     /* Search Cache */ | ||||
|     for (i = 0; i < CACHE_COUNT; i++) { | ||||
|         if (!s->cache[i].len) | ||||
|             continue; | ||||
|  | ||||
|         if ((start >= s->cache[i].start) && | ||||
|             (end <= (s->cache[i].start + s->cache[i].len))) { | ||||
|             acb->buf = s->cache[i].buf + (start - s->cache[i].start); | ||||
|             acb->len = len; | ||||
|             acb->qiov = qiov; | ||||
|             acb->bh = qemu_bh_new(dictzip_cache_cb, acb); | ||||
|             qemu_bh_schedule(acb->bh); | ||||
|  | ||||
|             return &acb->common; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* No cache, so let's decode */ | ||||
|     /* We need to read these chunks */ | ||||
|     first_chunk  = start / s->chunk_len; | ||||
|     first_offset = start - first_chunk * s->chunk_len; | ||||
|     last_chunk   = end / s->chunk_len; | ||||
|  | ||||
|     gz_start = s->offsets[first_chunk]; | ||||
|     gz_len = 0; | ||||
|     for (i = first_chunk; i <= last_chunk; i++) { | ||||
|         if (s->chunks32) | ||||
|             gz_len += le32_to_cpu(s->chunks32[i]); | ||||
|         else | ||||
|             gz_len += le16_to_cpu(s->chunks[i]); | ||||
|     } | ||||
|  | ||||
|     gz_sector_num = gz_start / SECTOR_SIZE; | ||||
|     gz_nb_sectors = (gz_len / SECTOR_SIZE); | ||||
|  | ||||
|     /* account for tail and heads */ | ||||
|     while ((gz_start + gz_len) > ((gz_sector_num + gz_nb_sectors) * SECTOR_SIZE)) | ||||
|         gz_nb_sectors++; | ||||
|  | ||||
|     /* Allocate qiov, iov and buf in one chunk so we only need to free qiov */ | ||||
|     qiov_gz = g_malloc0(sizeof(QEMUIOVector) + sizeof(struct iovec) + | ||||
|                            (gz_nb_sectors * SECTOR_SIZE)); | ||||
|     iov = (struct iovec *)(((char *)qiov_gz) + sizeof(QEMUIOVector)); | ||||
|     buf = ((uint8_t *)iov) + sizeof(struct iovec *); | ||||
|  | ||||
|     /* Kick off the read by the backing file, so we can start decompressing */ | ||||
|     iov->iov_base = (void *)buf; | ||||
|     iov->iov_len = gz_nb_sectors * 512; | ||||
|     qemu_iovec_init_external(qiov_gz, iov, 1); | ||||
|  | ||||
|     dprintf("read %zd - %zd => %zd - %zd\n", start, end, gz_start, gz_start + gz_len); | ||||
|  | ||||
|     acb->s = s; | ||||
|     acb->qiov = qiov; | ||||
|     acb->qiov_gz = qiov_gz; | ||||
|     acb->start = start; | ||||
|     acb->len = len; | ||||
|     acb->gzipped = buf + (gz_start % SECTOR_SIZE); | ||||
|     acb->gz_len = gz_len; | ||||
|     acb->gz_start = first_chunk * s->chunk_len; | ||||
|     acb->offset = first_offset; | ||||
|     acb->chunks_len = (last_chunk - first_chunk + 1) * s->chunk_len; | ||||
|  | ||||
|     return bdrv_aio_readv(s->hd, gz_sector_num, qiov_gz, gz_nb_sectors, | ||||
|                           dictzip_read_cb, acb); | ||||
| } | ||||
|  | ||||
| static void dictzip_close(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVDictZipState *s = bs->opaque; | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < CACHE_COUNT; i++) { | ||||
|         if (!s->cache[i].len) | ||||
|             continue; | ||||
|  | ||||
|         g_free(s->cache[i].buf); | ||||
|     } | ||||
|  | ||||
|     for (i = 0; i < Z_STREAM_COUNT; i++) { | ||||
|         inflateEnd(&s->zStream[i]); | ||||
|     } | ||||
|  | ||||
|     if (s->chunks) | ||||
|         g_free(s->chunks); | ||||
|  | ||||
|     if (s->offsets) | ||||
|         g_free(s->offsets); | ||||
|  | ||||
|     dprintf("Close\n"); | ||||
| } | ||||
|  | ||||
| static int64_t dictzip_getlength(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVDictZipState *s = bs->opaque; | ||||
|     dprintf("getlength -> %ld\n", s->file_len); | ||||
|     return s->file_len; | ||||
| } | ||||
|  | ||||
| static BlockDriver bdrv_dictzip = { | ||||
|     .format_name     = "dzip", | ||||
|     .protocol_name   = "dzip", | ||||
|  | ||||
|     .instance_size   = sizeof(BDRVDictZipState), | ||||
|     .bdrv_file_open  = dictzip_open, | ||||
|     .bdrv_close      = dictzip_close, | ||||
|     .bdrv_getlength  = dictzip_getlength, | ||||
|  | ||||
|     .bdrv_aio_readv  = dictzip_aio_readv, | ||||
| }; | ||||
|  | ||||
| static void dictzip_block_init(void) | ||||
| { | ||||
|     bdrv_register(&bdrv_dictzip); | ||||
| } | ||||
|  | ||||
| block_init(dictzip_block_init); | ||||
							
								
								
									
										22
									
								
								block/dmg.c
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								block/dmg.c
									
									
									
									
									
								
							| @@ -21,11 +21,9 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "block/block_int.h" | ||||
| #include "qemu/bswap.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "qemu/module.h" | ||||
| #include <zlib.h> | ||||
| #ifdef CONFIG_BZIP2 | ||||
| @@ -86,7 +84,7 @@ static int read_uint64(BlockDriverState *bs, int64_t offset, uint64_t *result) | ||||
|     uint64_t buffer; | ||||
|     int ret; | ||||
|  | ||||
|     ret = bdrv_pread(bs->file->bs, offset, &buffer, 8); | ||||
|     ret = bdrv_pread(bs->file, offset, &buffer, 8); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
| @@ -100,7 +98,7 @@ static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result) | ||||
|     uint32_t buffer; | ||||
|     int ret; | ||||
|  | ||||
|     ret = bdrv_pread(bs->file->bs, offset, &buffer, 4); | ||||
|     ret = bdrv_pread(bs->file, offset, &buffer, 4); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
| @@ -355,7 +353,7 @@ static int dmg_read_resource_fork(BlockDriverState *bs, DmgHeaderState *ds, | ||||
|         offset += 4; | ||||
|  | ||||
|         buffer = g_realloc(buffer, count); | ||||
|         ret = bdrv_pread(bs->file->bs, offset, buffer, count); | ||||
|         ret = bdrv_pread(bs->file, offset, buffer, count); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
|         } | ||||
| @@ -392,7 +390,7 @@ static int dmg_read_plist_xml(BlockDriverState *bs, DmgHeaderState *ds, | ||||
|  | ||||
|     buffer = g_malloc(info_length + 1); | ||||
|     buffer[info_length] = '\0'; | ||||
|     ret = bdrv_pread(bs->file->bs, info_begin, buffer, info_length); | ||||
|     ret = bdrv_pread(bs->file, info_begin, buffer, info_length); | ||||
|     if (ret != info_length) { | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
| @@ -447,7 +445,7 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     ds.max_sectors_per_chunk = 1; | ||||
|  | ||||
|     /* locate the UDIF trailer */ | ||||
|     offset = dmg_find_koly_offset(bs->file->bs, errp); | ||||
|     offset = dmg_find_koly_offset(bs->file, errp); | ||||
|     if (offset < 0) { | ||||
|         ret = offset; | ||||
|         goto fail; | ||||
| @@ -515,9 +513,9 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     } | ||||
|  | ||||
|     /* initialize zlib engine */ | ||||
|     s->compressed_chunk = qemu_try_blockalign(bs->file->bs, | ||||
|     s->compressed_chunk = qemu_try_blockalign(bs->file, | ||||
|                                               ds.max_compressed_size + 1); | ||||
|     s->uncompressed_chunk = qemu_try_blockalign(bs->file->bs, | ||||
|     s->uncompressed_chunk = qemu_try_blockalign(bs->file, | ||||
|                                                 512 * ds.max_sectors_per_chunk); | ||||
|     if (s->compressed_chunk == NULL || s->uncompressed_chunk == NULL) { | ||||
|         ret = -ENOMEM; | ||||
| @@ -593,7 +591,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) | ||||
|         case 0x80000005: { /* zlib compressed */ | ||||
|             /* we need to buffer, because only the chunk as whole can be | ||||
|              * inflated. */ | ||||
|             ret = bdrv_pread(bs->file->bs, s->offsets[chunk], | ||||
|             ret = bdrv_pread(bs->file, s->offsets[chunk], | ||||
|                              s->compressed_chunk, s->lengths[chunk]); | ||||
|             if (ret != s->lengths[chunk]) { | ||||
|                 return -1; | ||||
| @@ -617,7 +615,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) | ||||
|         case 0x80000006: /* bzip2 compressed */ | ||||
|             /* we need to buffer, because only the chunk as whole can be | ||||
|              * inflated. */ | ||||
|             ret = bdrv_pread(bs->file->bs, s->offsets[chunk], | ||||
|             ret = bdrv_pread(bs->file, s->offsets[chunk], | ||||
|                              s->compressed_chunk, s->lengths[chunk]); | ||||
|             if (ret != s->lengths[chunk]) { | ||||
|                 return -1; | ||||
| @@ -642,7 +640,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) | ||||
|             break; | ||||
| #endif /* CONFIG_BZIP2 */ | ||||
|         case 1: /* copy */ | ||||
|             ret = bdrv_pread(bs->file->bs, s->offsets[chunk], | ||||
|             ret = bdrv_pread(bs->file, s->offsets[chunk], | ||||
|                              s->uncompressed_chunk, s->lengths[chunk]); | ||||
|             if (ret != s->lengths[chunk]) { | ||||
|                 return -1; | ||||
|   | ||||
| @@ -7,7 +7,6 @@ | ||||
|  * See the COPYING file in the top-level directory. | ||||
|  * | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include <glusterfs/api/glfs.h> | ||||
| #include "block/block_int.h" | ||||
| #include "qemu/uri.h" | ||||
| @@ -430,23 +429,28 @@ static coroutine_fn int qemu_gluster_co_write_zeroes(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors, BdrvRequestFlags flags) | ||||
| { | ||||
|     int ret; | ||||
|     GlusterAIOCB acb; | ||||
|     GlusterAIOCB *acb = g_slice_new(GlusterAIOCB); | ||||
|     BDRVGlusterState *s = bs->opaque; | ||||
|     off_t size = nb_sectors * BDRV_SECTOR_SIZE; | ||||
|     off_t offset = sector_num * BDRV_SECTOR_SIZE; | ||||
|  | ||||
|     acb.size = size; | ||||
|     acb.ret = 0; | ||||
|     acb.coroutine = qemu_coroutine_self(); | ||||
|     acb.aio_context = bdrv_get_aio_context(bs); | ||||
|     acb->size = size; | ||||
|     acb->ret = 0; | ||||
|     acb->coroutine = qemu_coroutine_self(); | ||||
|     acb->aio_context = bdrv_get_aio_context(bs); | ||||
|  | ||||
|     ret = glfs_zerofill_async(s->fd, offset, size, gluster_finish_aiocb, &acb); | ||||
|     ret = glfs_zerofill_async(s->fd, offset, size, &gluster_finish_aiocb, acb); | ||||
|     if (ret < 0) { | ||||
|         return -errno; | ||||
|         ret = -errno; | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     qemu_coroutine_yield(); | ||||
|     return acb.ret; | ||||
|     ret = acb->ret; | ||||
|  | ||||
| out: | ||||
|     g_slice_free(GlusterAIOCB, acb); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static inline bool gluster_supports_zerofill(void) | ||||
| @@ -537,30 +541,35 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int write) | ||||
| { | ||||
|     int ret; | ||||
|     GlusterAIOCB acb; | ||||
|     GlusterAIOCB *acb = g_slice_new(GlusterAIOCB); | ||||
|     BDRVGlusterState *s = bs->opaque; | ||||
|     size_t size = nb_sectors * BDRV_SECTOR_SIZE; | ||||
|     off_t offset = sector_num * BDRV_SECTOR_SIZE; | ||||
|  | ||||
|     acb.size = size; | ||||
|     acb.ret = 0; | ||||
|     acb.coroutine = qemu_coroutine_self(); | ||||
|     acb.aio_context = bdrv_get_aio_context(bs); | ||||
|     acb->size = size; | ||||
|     acb->ret = 0; | ||||
|     acb->coroutine = qemu_coroutine_self(); | ||||
|     acb->aio_context = bdrv_get_aio_context(bs); | ||||
|  | ||||
|     if (write) { | ||||
|         ret = glfs_pwritev_async(s->fd, qiov->iov, qiov->niov, offset, 0, | ||||
|             gluster_finish_aiocb, &acb); | ||||
|             &gluster_finish_aiocb, acb); | ||||
|     } else { | ||||
|         ret = glfs_preadv_async(s->fd, qiov->iov, qiov->niov, offset, 0, | ||||
|             gluster_finish_aiocb, &acb); | ||||
|             &gluster_finish_aiocb, acb); | ||||
|     } | ||||
|  | ||||
|     if (ret < 0) { | ||||
|         return -errno; | ||||
|         ret = -errno; | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     qemu_coroutine_yield(); | ||||
|     return acb.ret; | ||||
|     ret = acb->ret; | ||||
|  | ||||
| out: | ||||
|     g_slice_free(GlusterAIOCB, acb); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset) | ||||
| @@ -591,21 +600,26 @@ static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs, | ||||
| static coroutine_fn int qemu_gluster_co_flush_to_disk(BlockDriverState *bs) | ||||
| { | ||||
|     int ret; | ||||
|     GlusterAIOCB acb; | ||||
|     GlusterAIOCB *acb = g_slice_new(GlusterAIOCB); | ||||
|     BDRVGlusterState *s = bs->opaque; | ||||
|  | ||||
|     acb.size = 0; | ||||
|     acb.ret = 0; | ||||
|     acb.coroutine = qemu_coroutine_self(); | ||||
|     acb.aio_context = bdrv_get_aio_context(bs); | ||||
|     acb->size = 0; | ||||
|     acb->ret = 0; | ||||
|     acb->coroutine = qemu_coroutine_self(); | ||||
|     acb->aio_context = bdrv_get_aio_context(bs); | ||||
|  | ||||
|     ret = glfs_fsync_async(s->fd, gluster_finish_aiocb, &acb); | ||||
|     ret = glfs_fsync_async(s->fd, &gluster_finish_aiocb, acb); | ||||
|     if (ret < 0) { | ||||
|         return -errno; | ||||
|         ret = -errno; | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     qemu_coroutine_yield(); | ||||
|     return acb.ret; | ||||
|     ret = acb->ret; | ||||
|  | ||||
| out: | ||||
|     g_slice_free(GlusterAIOCB, acb); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| #ifdef CONFIG_GLUSTERFS_DISCARD | ||||
| @@ -613,23 +627,28 @@ static coroutine_fn int qemu_gluster_co_discard(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors) | ||||
| { | ||||
|     int ret; | ||||
|     GlusterAIOCB acb; | ||||
|     GlusterAIOCB *acb = g_slice_new(GlusterAIOCB); | ||||
|     BDRVGlusterState *s = bs->opaque; | ||||
|     size_t size = nb_sectors * BDRV_SECTOR_SIZE; | ||||
|     off_t offset = sector_num * BDRV_SECTOR_SIZE; | ||||
|  | ||||
|     acb.size = 0; | ||||
|     acb.ret = 0; | ||||
|     acb.coroutine = qemu_coroutine_self(); | ||||
|     acb.aio_context = bdrv_get_aio_context(bs); | ||||
|     acb->size = 0; | ||||
|     acb->ret = 0; | ||||
|     acb->coroutine = qemu_coroutine_self(); | ||||
|     acb->aio_context = bdrv_get_aio_context(bs); | ||||
|  | ||||
|     ret = glfs_discard_async(s->fd, offset, size, gluster_finish_aiocb, &acb); | ||||
|     ret = glfs_discard_async(s->fd, offset, size, &gluster_finish_aiocb, acb); | ||||
|     if (ret < 0) { | ||||
|         return -errno; | ||||
|         ret = -errno; | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     qemu_coroutine_yield(); | ||||
|     return acb.ret; | ||||
|     ret = acb->ret; | ||||
|  | ||||
| out: | ||||
|     g_slice_free(GlusterAIOCB, acb); | ||||
|     return ret; | ||||
| } | ||||
| #endif | ||||
|  | ||||
|   | ||||
							
								
								
									
										2787
									
								
								block/io.c
									
									
									
									
									
								
							
							
						
						
									
										2787
									
								
								block/io.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										338
									
								
								block/iscsi.c
									
									
									
									
									
								
							
							
						
						
									
										338
									
								
								block/iscsi.c
									
									
									
									
									
								
							| @@ -2,7 +2,7 @@ | ||||
|  * QEMU Block driver for iSCSI images | ||||
|  * | ||||
|  * Copyright (c) 2010-2011 Ronnie Sahlberg <ronniesahlberg@gmail.com> | ||||
|  * Copyright (c) 2012-2015 Peter Lieven <pl@kamp.de> | ||||
|  * Copyright (c) 2012-2014 Peter Lieven <pl@kamp.de> | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
| @@ -23,7 +23,7 @@ | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "config-host.h" | ||||
|  | ||||
| #include <poll.h> | ||||
| #include <math.h> | ||||
| @@ -38,7 +38,6 @@ | ||||
| #include "qemu/iov.h" | ||||
| #include "sysemu/sysemu.h" | ||||
| #include "qmp-commands.h" | ||||
| #include "qapi/qmp/qstring.h" | ||||
|  | ||||
| #include <iscsi/iscsi.h> | ||||
| #include <iscsi/scsi-lowlevel.h> | ||||
| @@ -58,6 +57,9 @@ typedef struct IscsiLun { | ||||
|     int events; | ||||
|     QEMUTimer *nop_timer; | ||||
|     QEMUTimer *event_timer; | ||||
|     uint8_t lbpme; | ||||
|     uint8_t lbprz; | ||||
|     uint8_t has_write_same; | ||||
|     struct scsi_inquiry_logical_block_provisioning lbp; | ||||
|     struct scsi_inquiry_block_limits bl; | ||||
|     unsigned char *zeroblock; | ||||
| @@ -65,12 +67,6 @@ typedef struct IscsiLun { | ||||
|     int cluster_sectors; | ||||
|     bool use_16_for_rw; | ||||
|     bool write_protected; | ||||
|     bool lbpme; | ||||
|     bool lbprz; | ||||
|     bool dpofua; | ||||
|     bool has_write_same; | ||||
|     bool force_next_flush; | ||||
|     bool request_timed_out; | ||||
| } IscsiLun; | ||||
|  | ||||
| typedef struct IscsiTask { | ||||
| @@ -83,8 +79,6 @@ typedef struct IscsiTask { | ||||
|     QEMUBH *bh; | ||||
|     IscsiLun *iscsilun; | ||||
|     QEMUTimer retry_timer; | ||||
|     bool force_next_flush; | ||||
|     int err_code; | ||||
| } IscsiTask; | ||||
|  | ||||
| typedef struct IscsiAIOCB { | ||||
| @@ -97,18 +91,16 @@ typedef struct IscsiAIOCB { | ||||
|     int status; | ||||
|     int64_t sector_num; | ||||
|     int nb_sectors; | ||||
|     int ret; | ||||
| #ifdef __linux__ | ||||
|     sg_io_hdr_t *ioh; | ||||
| #endif | ||||
| } IscsiAIOCB; | ||||
|  | ||||
| /* libiscsi uses time_t so its enough to process events every second */ | ||||
| #define EVENT_INTERVAL 1000 | ||||
| #define EVENT_INTERVAL 250 | ||||
| #define NOP_INTERVAL 5000 | ||||
| #define MAX_NOP_FAILURES 3 | ||||
| #define ISCSI_CMD_RETRIES ARRAY_SIZE(iscsi_retry_times) | ||||
| static const unsigned iscsi_retry_times[] = {8, 32, 128, 512, 2048, 8192, 32768}; | ||||
| static const unsigned iscsi_retry_times[] = {8, 32, 128, 512, 2048}; | ||||
|  | ||||
| /* this threshold is a trade-off knob to choose between | ||||
|  * the potential additional overhead of an extra GET_LBA_STATUS request | ||||
| @@ -171,70 +163,6 @@ static inline unsigned exp_random(double mean) | ||||
|     return -mean * log((double)rand() / RAND_MAX); | ||||
| } | ||||
|  | ||||
| /* SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST was introduced in | ||||
|  * libiscsi 1.10.0, together with other constants we need.  Use it as | ||||
|  * a hint that we have to define them ourselves if needed, to keep the | ||||
|  * minimum required libiscsi version at 1.9.0.  We use an ASCQ macro for | ||||
|  * the test because SCSI_STATUS_* is an enum. | ||||
|  * | ||||
|  * To guard against future changes where SCSI_SENSE_ASCQ_* also becomes | ||||
|  * an enum, check against the LIBISCSI_API_VERSION macro, which was | ||||
|  * introduced in 1.11.0.  If it is present, there is no need to define | ||||
|  * anything. | ||||
|  */ | ||||
| #if !defined(SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST) && \ | ||||
|     !defined(LIBISCSI_API_VERSION) | ||||
| #define SCSI_STATUS_TASK_SET_FULL                          0x28 | ||||
| #define SCSI_STATUS_TIMEOUT                                0x0f000002 | ||||
| #define SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST    0x2600 | ||||
| #define SCSI_SENSE_ASCQ_PARAMETER_LIST_LENGTH_ERROR        0x1a00 | ||||
| #endif | ||||
|  | ||||
| static int iscsi_translate_sense(struct scsi_sense *sense) | ||||
| { | ||||
|     int ret; | ||||
|  | ||||
|     switch (sense->key) { | ||||
|     case SCSI_SENSE_NOT_READY: | ||||
|         return -EBUSY; | ||||
|     case SCSI_SENSE_DATA_PROTECTION: | ||||
|         return -EACCES; | ||||
|     case SCSI_SENSE_COMMAND_ABORTED: | ||||
|         return -ECANCELED; | ||||
|     case SCSI_SENSE_ILLEGAL_REQUEST: | ||||
|         /* Parse ASCQ */ | ||||
|         break; | ||||
|     default: | ||||
|         return -EIO; | ||||
|     } | ||||
|     switch (sense->ascq) { | ||||
|     case SCSI_SENSE_ASCQ_PARAMETER_LIST_LENGTH_ERROR: | ||||
|     case SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE: | ||||
|     case SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB: | ||||
|     case SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST: | ||||
|         ret = -EINVAL; | ||||
|         break; | ||||
|     case SCSI_SENSE_ASCQ_LBA_OUT_OF_RANGE: | ||||
|         ret = -ENOSPC; | ||||
|         break; | ||||
|     case SCSI_SENSE_ASCQ_LOGICAL_UNIT_NOT_SUPPORTED: | ||||
|         ret = -ENOTSUP; | ||||
|         break; | ||||
|     case SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT: | ||||
|     case SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_CLOSED: | ||||
|     case SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_OPEN: | ||||
|         ret = -ENOMEDIUM; | ||||
|         break; | ||||
|     case SCSI_SENSE_ASCQ_WRITE_PROTECTED: | ||||
|         ret = -EACCES; | ||||
|         break; | ||||
|     default: | ||||
|         ret = -EIO; | ||||
|         break; | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static void | ||||
| iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, | ||||
|                         void *command_data, void *opaque) | ||||
| @@ -255,19 +183,10 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, | ||||
|                 iTask->do_retry = 1; | ||||
|                 goto out; | ||||
|             } | ||||
|             if (status == SCSI_STATUS_BUSY || | ||||
|                 status == SCSI_STATUS_TIMEOUT || | ||||
|                 status == SCSI_STATUS_TASK_SET_FULL) { | ||||
|             if (status == SCSI_STATUS_BUSY) { | ||||
|                 unsigned retry_time = | ||||
|                     exp_random(iscsi_retry_times[iTask->retries - 1]); | ||||
|                 if (status == SCSI_STATUS_TIMEOUT) { | ||||
|                     /* make sure the request is rescheduled AFTER the | ||||
|                      * reconnect is initiated */ | ||||
|                     retry_time = EVENT_INTERVAL * 2; | ||||
|                     iTask->iscsilun->request_timed_out = true; | ||||
|                 } | ||||
|                 error_report("iSCSI Busy/TaskSetFull/TimeOut" | ||||
|                              " (retry #%u in %u ms): %s", | ||||
|                 error_report("iSCSI Busy (retry #%u in %u ms): %s", | ||||
|                              iTask->retries, retry_time, | ||||
|                              iscsi_get_error(iscsi)); | ||||
|                 aio_timer_init(iTask->iscsilun->aio_context, | ||||
| @@ -279,10 +198,7 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|         iTask->err_code = iscsi_translate_sense(&task->sense); | ||||
|         error_report("iSCSI Failure: %s", iscsi_get_error(iscsi)); | ||||
|     } else { | ||||
|         iTask->iscsilun->force_next_flush |= iTask->force_next_flush; | ||||
|     } | ||||
|  | ||||
| out: | ||||
| @@ -345,34 +261,28 @@ iscsi_set_events(IscsiLun *iscsilun) | ||||
|     int ev = iscsi_which_events(iscsi); | ||||
|  | ||||
|     if (ev != iscsilun->events) { | ||||
|         aio_set_fd_handler(iscsilun->aio_context, iscsi_get_fd(iscsi), | ||||
|                            false, | ||||
|         aio_set_fd_handler(iscsilun->aio_context, | ||||
|                            iscsi_get_fd(iscsi), | ||||
|                            (ev & POLLIN) ? iscsi_process_read : NULL, | ||||
|                            (ev & POLLOUT) ? iscsi_process_write : NULL, | ||||
|                            iscsilun); | ||||
|         iscsilun->events = ev; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void iscsi_timed_check_events(void *opaque) | ||||
| { | ||||
|     IscsiLun *iscsilun = opaque; | ||||
|  | ||||
|     /* check for timed out requests */ | ||||
|     iscsi_service(iscsilun->iscsi, 0); | ||||
|  | ||||
|     if (iscsilun->request_timed_out) { | ||||
|         iscsilun->request_timed_out = false; | ||||
|         iscsi_reconnect(iscsilun->iscsi); | ||||
|     } | ||||
|  | ||||
|     /* newer versions of libiscsi may return zero events. Ensure we are able | ||||
|      * to return to service once this situation changes. */ | ||||
|     iscsi_set_events(iscsilun); | ||||
|  | ||||
|     /* newer versions of libiscsi may return zero events. In this | ||||
|      * case start a timer to ensure we are able to return to service | ||||
|      * once this situation changes. */ | ||||
|     if (!ev) { | ||||
|         timer_mod(iscsilun->event_timer, | ||||
|                   qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void iscsi_timed_set_events(void *opaque) | ||||
| { | ||||
|     IscsiLun *iscsilun = opaque; | ||||
|     iscsi_set_events(iscsilun); | ||||
| } | ||||
|  | ||||
| static void | ||||
| iscsi_process_read(void *arg) | ||||
| @@ -459,7 +369,6 @@ static int coroutine_fn iscsi_co_writev(BlockDriverState *bs, | ||||
|     struct IscsiTask iTask; | ||||
|     uint64_t lba; | ||||
|     uint32_t num_sectors; | ||||
|     int fua; | ||||
|  | ||||
|     if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { | ||||
|         return -EINVAL; | ||||
| @@ -475,17 +384,15 @@ static int coroutine_fn iscsi_co_writev(BlockDriverState *bs, | ||||
|     num_sectors = sector_qemu2lun(nb_sectors, iscsilun); | ||||
|     iscsi_co_init_iscsitask(iscsilun, &iTask); | ||||
| retry: | ||||
|     fua = iscsilun->dpofua && !bs->enable_write_cache; | ||||
|     iTask.force_next_flush = !fua; | ||||
|     if (iscsilun->use_16_for_rw) { | ||||
|         iTask.task = iscsi_write16_task(iscsilun->iscsi, iscsilun->lun, lba, | ||||
|                                         NULL, num_sectors * iscsilun->block_size, | ||||
|                                         iscsilun->block_size, 0, 0, fua, 0, 0, | ||||
|                                         iscsilun->block_size, 0, 0, 0, 0, 0, | ||||
|                                         iscsi_co_generic_cb, &iTask); | ||||
|     } else { | ||||
|         iTask.task = iscsi_write10_task(iscsilun->iscsi, iscsilun->lun, lba, | ||||
|                                         NULL, num_sectors * iscsilun->block_size, | ||||
|                                         iscsilun->block_size, 0, 0, fua, 0, 0, | ||||
|                                         iscsilun->block_size, 0, 0, 0, 0, 0, | ||||
|                                         iscsi_co_generic_cb, &iTask); | ||||
|     } | ||||
|     if (iTask.task == NULL) { | ||||
| @@ -509,7 +416,7 @@ retry: | ||||
|     } | ||||
|  | ||||
|     if (iTask.status != SCSI_STATUS_GOOD) { | ||||
|         return iTask.err_code; | ||||
|         return -EIO; | ||||
|     } | ||||
|  | ||||
|     iscsi_allocationmap_set(iscsilun, sector_num, nb_sectors); | ||||
| @@ -532,8 +439,7 @@ static bool iscsi_allocationmap_is_allocated(IscsiLun *iscsilun, | ||||
|  | ||||
| static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs, | ||||
|                                                   int64_t sector_num, | ||||
|                                                   int nb_sectors, int *pnum, | ||||
|                                                   BlockDriverState **file) | ||||
|                                                   int nb_sectors, int *pnum) | ||||
| { | ||||
|     IscsiLun *iscsilun = bs->opaque; | ||||
|     struct scsi_get_lba_status *lbas = NULL; | ||||
| @@ -554,7 +460,7 @@ static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs, | ||||
|     *pnum = nb_sectors; | ||||
|  | ||||
|     /* LUN does not support logical block provisioning */ | ||||
|     if (!iscsilun->lbpme) { | ||||
|     if (iscsilun->lbpme == 0) { | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
| @@ -625,9 +531,6 @@ out: | ||||
|     if (iTask.task != NULL) { | ||||
|         scsi_free_scsi_task(iTask.task); | ||||
|     } | ||||
|     if (ret > 0 && ret & BDRV_BLOCK_OFFSET_VALID) { | ||||
|         *file = bs; | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| @@ -654,8 +557,7 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs, | ||||
|         !iscsi_allocationmap_is_allocated(iscsilun, sector_num, nb_sectors)) { | ||||
|         int64_t ret; | ||||
|         int pnum; | ||||
|         BlockDriverState *file; | ||||
|         ret = iscsi_co_get_block_status(bs, sector_num, INT_MAX, &pnum, &file); | ||||
|         ret = iscsi_co_get_block_status(bs, sector_num, INT_MAX, &pnum); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
| @@ -703,7 +605,7 @@ retry: | ||||
|     } | ||||
|  | ||||
|     if (iTask.status != SCSI_STATUS_GOOD) { | ||||
|         return iTask.err_code; | ||||
|         return -EIO; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| @@ -714,12 +616,12 @@ static int coroutine_fn iscsi_co_flush(BlockDriverState *bs) | ||||
|     IscsiLun *iscsilun = bs->opaque; | ||||
|     struct IscsiTask iTask; | ||||
|  | ||||
|     if (!iscsilun->force_next_flush) { | ||||
|     if (bs->sg) { | ||||
|         return 0; | ||||
|     } | ||||
|     iscsilun->force_next_flush = false; | ||||
|  | ||||
|     iscsi_co_init_iscsitask(iscsilun, &iTask); | ||||
|  | ||||
| retry: | ||||
|     if (iscsi_synchronizecache10_task(iscsilun->iscsi, iscsilun->lun, 0, 0, 0, | ||||
|                                       0, iscsi_co_generic_cb, &iTask) == NULL) { | ||||
| @@ -742,7 +644,7 @@ retry: | ||||
|     } | ||||
|  | ||||
|     if (iTask.status != SCSI_STATUS_GOOD) { | ||||
|         return iTask.err_code; | ||||
|         return -EIO; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| @@ -762,7 +664,7 @@ iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status, | ||||
|     if (status < 0) { | ||||
|         error_report("Failed to ioctl(SG_IO) to iSCSI lun. %s", | ||||
|                      iscsi_get_error(iscsi)); | ||||
|         acb->status = iscsi_translate_sense(&acb->task->sense); | ||||
|         acb->status = -EIO; | ||||
|     } | ||||
|  | ||||
|     acb->ioh->driver_status = 0; | ||||
| @@ -785,38 +687,6 @@ iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status, | ||||
|     iscsi_schedule_bh(acb); | ||||
| } | ||||
|  | ||||
| static void iscsi_ioctl_bh_completion(void *opaque) | ||||
| { | ||||
|     IscsiAIOCB *acb = opaque; | ||||
|  | ||||
|     qemu_bh_delete(acb->bh); | ||||
|     acb->common.cb(acb->common.opaque, acb->ret); | ||||
|     qemu_aio_unref(acb); | ||||
| } | ||||
|  | ||||
| static void iscsi_ioctl_handle_emulated(IscsiAIOCB *acb, int req, void *buf) | ||||
| { | ||||
|     BlockDriverState *bs = acb->common.bs; | ||||
|     IscsiLun *iscsilun = bs->opaque; | ||||
|     int ret = 0; | ||||
|  | ||||
|     switch (req) { | ||||
|     case SG_GET_VERSION_NUM: | ||||
|         *(int *)buf = 30000; | ||||
|         break; | ||||
|     case SG_GET_SCSI_ID: | ||||
|         ((struct sg_scsi_id *)buf)->scsi_type = iscsilun->type; | ||||
|         break; | ||||
|     default: | ||||
|         ret = -EINVAL; | ||||
|     } | ||||
|     assert(!acb->bh); | ||||
|     acb->bh = aio_bh_new(bdrv_get_aio_context(bs), | ||||
|                          iscsi_ioctl_bh_completion, acb); | ||||
|     acb->ret = ret; | ||||
|     qemu_bh_schedule(acb->bh); | ||||
| } | ||||
|  | ||||
| static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, | ||||
|         unsigned long int req, void *buf, | ||||
|         BlockCompletionFunc *cb, void *opaque) | ||||
| @@ -826,6 +696,8 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, | ||||
|     struct iscsi_data data; | ||||
|     IscsiAIOCB *acb; | ||||
|  | ||||
|     assert(req == SG_IO); | ||||
|  | ||||
|     acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); | ||||
|  | ||||
|     acb->iscsilun = iscsilun; | ||||
| @@ -834,11 +706,6 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, | ||||
|     acb->buf         = NULL; | ||||
|     acb->ioh         = buf; | ||||
|  | ||||
|     if (req != SG_IO) { | ||||
|         iscsi_ioctl_handle_emulated(acb, req, buf); | ||||
|         return &acb->common; | ||||
|     } | ||||
|  | ||||
|     acb->task = malloc(sizeof(struct scsi_task)); | ||||
|     if (acb->task == NULL) { | ||||
|         error_report("iSCSI: Failed to allocate task for scsi command. %s", | ||||
| @@ -903,6 +770,38 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, | ||||
|     return &acb->common; | ||||
| } | ||||
|  | ||||
| static void ioctl_cb(void *opaque, int status) | ||||
| { | ||||
|     int *p_status = opaque; | ||||
|     *p_status = status; | ||||
| } | ||||
|  | ||||
| static int iscsi_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) | ||||
| { | ||||
|     IscsiLun *iscsilun = bs->opaque; | ||||
|     int status; | ||||
|  | ||||
|     switch (req) { | ||||
|     case SG_GET_VERSION_NUM: | ||||
|         *(int *)buf = 30000; | ||||
|         break; | ||||
|     case SG_GET_SCSI_ID: | ||||
|         ((struct sg_scsi_id *)buf)->scsi_type = iscsilun->type; | ||||
|         break; | ||||
|     case SG_IO: | ||||
|         status = -EINPROGRESS; | ||||
|         iscsi_aio_ioctl(bs, req, buf, ioctl_cb, &status); | ||||
|  | ||||
|         while (status == -EINPROGRESS) { | ||||
|             aio_poll(iscsilun->aio_context, true); | ||||
|         } | ||||
|  | ||||
|         return 0; | ||||
|     default: | ||||
|         return -1; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static int64_t | ||||
| @@ -967,7 +866,7 @@ retry: | ||||
|     } | ||||
|  | ||||
|     if (iTask.status != SCSI_STATUS_GOOD) { | ||||
|         return iTask.err_code; | ||||
|         return -EIO; | ||||
|     } | ||||
|  | ||||
|     iscsi_allocationmap_clear(iscsilun, sector_num, nb_sectors); | ||||
| @@ -1018,7 +917,6 @@ coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num, | ||||
|     } | ||||
|  | ||||
|     iscsi_co_init_iscsitask(iscsilun, &iTask); | ||||
|     iTask.force_next_flush = true; | ||||
| retry: | ||||
|     if (use_16_for_ws) { | ||||
|         iTask.task = iscsi_writesame16_task(iscsilun->iscsi, iscsilun->lun, lba, | ||||
| @@ -1061,7 +959,7 @@ retry: | ||||
|     } | ||||
|  | ||||
|     if (iTask.status != SCSI_STATUS_GOOD) { | ||||
|         return iTask.err_code; | ||||
|         return -EIO; | ||||
|     } | ||||
|  | ||||
|     if (flags & BDRV_REQ_MAY_UNMAP) { | ||||
| @@ -1182,37 +1080,16 @@ static char *parse_initiator_name(const char *target) | ||||
|     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) | ||||
| { | ||||
|     IscsiLun *iscsilun = opaque; | ||||
|  | ||||
|     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..."); | ||||
|         iscsilun->request_timed_out = true; | ||||
|     } else if (iscsi_nop_out_async(iscsilun->iscsi, NULL, NULL, 0, NULL) != 0) { | ||||
|         iscsi_reconnect(iscsilun->iscsi); | ||||
|     } | ||||
|  | ||||
|     if (iscsi_nop_out_async(iscsilun->iscsi, NULL, NULL, 0, NULL) != 0) { | ||||
|         error_report("iSCSI: failed to sent NOP-Out. Disabling NOP messages."); | ||||
|         return; | ||||
|     } | ||||
| @@ -1244,17 +1121,12 @@ static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp) | ||||
|                 } else { | ||||
|                     iscsilun->block_size = rc16->block_length; | ||||
|                     iscsilun->num_blocks = rc16->returned_lba + 1; | ||||
|                     iscsilun->lbpme = !!rc16->lbpme; | ||||
|                     iscsilun->lbprz = !!rc16->lbprz; | ||||
|                     iscsilun->lbpme = rc16->lbpme; | ||||
|                     iscsilun->lbprz = rc16->lbprz; | ||||
|                     iscsilun->use_16_for_rw = (rc16->returned_lba > 0xffffffff); | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|             if (task != NULL && task->status == SCSI_STATUS_CHECK_CONDITION | ||||
|                 && task->sense.key == SCSI_SENSE_UNIT_ATTENTION) { | ||||
|             break; | ||||
|             } | ||||
|             /* Fall through and try READ CAPACITY(10) instead.  */ | ||||
|         case TYPE_ROM: | ||||
|             task = iscsi_readcapacity10_sync(iscsilun->iscsi, iscsilun->lun, 0, 0); | ||||
|             if (task != NULL && task->status == SCSI_STATUS_GOOD) { | ||||
| @@ -1280,11 +1152,7 @@ static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp) | ||||
|              && retries-- > 0); | ||||
|  | ||||
|     if (task == NULL || task->status != SCSI_STATUS_GOOD) { | ||||
|         error_setg(errp, "iSCSI: failed to send readcapacity10/16 command"); | ||||
|     } else if (!iscsilun->block_size || | ||||
|                iscsilun->block_size % BDRV_SECTOR_SIZE) { | ||||
|         error_setg(errp, "iSCSI: the target returned an invalid " | ||||
|                    "block size of %d.", iscsilun->block_size); | ||||
|         error_setg(errp, "iSCSI: failed to send readcapacity10 command."); | ||||
|     } | ||||
|     if (task) { | ||||
|         scsi_free_scsi_task(task); | ||||
| @@ -1347,8 +1215,9 @@ static void iscsi_detach_aio_context(BlockDriverState *bs) | ||||
| { | ||||
|     IscsiLun *iscsilun = bs->opaque; | ||||
|  | ||||
|     aio_set_fd_handler(iscsilun->aio_context, iscsi_get_fd(iscsilun->iscsi), | ||||
|                        false, NULL, NULL, NULL); | ||||
|     aio_set_fd_handler(iscsilun->aio_context, | ||||
|                        iscsi_get_fd(iscsilun->iscsi), | ||||
|                        NULL, NULL, NULL); | ||||
|     iscsilun->events = 0; | ||||
|  | ||||
|     if (iscsilun->nop_timer) { | ||||
| @@ -1378,21 +1247,17 @@ static void iscsi_attach_aio_context(BlockDriverState *bs, | ||||
|     timer_mod(iscsilun->nop_timer, | ||||
|               qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL); | ||||
|  | ||||
|     /* Set up a timer for periodic calls to iscsi_set_events and to | ||||
|      * scan for command timeout */ | ||||
|     /* Prepare a timer for a delayed call to iscsi_set_events */ | ||||
|     iscsilun->event_timer = aio_timer_new(iscsilun->aio_context, | ||||
|                                           QEMU_CLOCK_REALTIME, SCALE_MS, | ||||
|                                           iscsi_timed_check_events, iscsilun); | ||||
|     timer_mod(iscsilun->event_timer, | ||||
|               qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL); | ||||
|                                           iscsi_timed_set_events, iscsilun); | ||||
| } | ||||
|  | ||||
| static void iscsi_modesense_sync(IscsiLun *iscsilun) | ||||
| static bool iscsi_is_write_protected(IscsiLun *iscsilun) | ||||
| { | ||||
|     struct scsi_task *task; | ||||
|     struct scsi_mode_sense *ms = NULL; | ||||
|     iscsilun->write_protected = false; | ||||
|     iscsilun->dpofua = false; | ||||
|     bool wrprotected = false; | ||||
|  | ||||
|     task = iscsi_modesense6_sync(iscsilun->iscsi, iscsilun->lun, | ||||
|                                  1, SCSI_MODESENSE_PC_CURRENT, | ||||
| @@ -1413,13 +1278,13 @@ static void iscsi_modesense_sync(IscsiLun *iscsilun) | ||||
|                      iscsi_get_error(iscsilun->iscsi)); | ||||
|         goto out; | ||||
|     } | ||||
|     iscsilun->write_protected = ms->device_specific_parameter & 0x80; | ||||
|     iscsilun->dpofua          = ms->device_specific_parameter & 0x10; | ||||
|     wrprotected = ms->device_specific_parameter & 0x80; | ||||
|  | ||||
| out: | ||||
|     if (task) { | ||||
|         scsi_free_scsi_task(task); | ||||
|     } | ||||
|     return wrprotected; | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -1439,7 +1304,14 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     QemuOpts *opts; | ||||
|     Error *local_err = NULL; | ||||
|     const char *filename; | ||||
|     int i, ret = 0, timeout = 0; | ||||
|     int i, ret = 0; | ||||
|  | ||||
|     if ((BDRV_SECTOR_SIZE % 512) != 0) { | ||||
|         error_setg(errp, "iSCSI: Invalid BDRV_SECTOR_SIZE. " | ||||
|                    "BDRV_SECTOR_SIZE(%lld) is not a multiple " | ||||
|                    "of 512", BDRV_SECTOR_SIZE); | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); | ||||
|     qemu_opts_absorb_qdict(opts, options, &local_err); | ||||
| @@ -1509,16 +1381,6 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     /* timeout handling is broken in libiscsi before 1.15.0 */ | ||||
|     timeout = parse_timeout(iscsi_url->target); | ||||
| #if defined(LIBISCSI_API_VERSION) && LIBISCSI_API_VERSION >= 20150621 | ||||
|     iscsi_set_timeout(iscsi, timeout); | ||||
| #else | ||||
|     if (timeout) { | ||||
|         error_report("iSCSI: ignoring timeout value for libiscsi <1.15.0"); | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { | ||||
|         error_setg(errp, "iSCSI: Failed to connect to LUN : %s", | ||||
|             iscsi_get_error(iscsi)); | ||||
| @@ -1541,8 +1403,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     scsi_free_scsi_task(task); | ||||
|     task = NULL; | ||||
|  | ||||
|     iscsi_modesense_sync(iscsilun); | ||||
|  | ||||
|     iscsilun->write_protected = iscsi_is_write_protected(iscsilun); | ||||
|     /* Check the write protect flag of the LUN if we want to write */ | ||||
|     if (iscsilun->type == TYPE_DISK && (flags & BDRV_O_RDWR) && | ||||
|         iscsilun->write_protected) { | ||||
| @@ -1620,7 +1481,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         iscsilun->bl.opt_unmap_gran * iscsilun->block_size <= 16 * 1024 * 1024) { | ||||
|         iscsilun->cluster_sectors = (iscsilun->bl.opt_unmap_gran * | ||||
|                                      iscsilun->block_size) >> BDRV_SECTOR_BITS; | ||||
|         if (iscsilun->lbprz) { | ||||
|         if (iscsilun->lbprz && !(bs->open_flags & BDRV_O_NOCACHE)) { | ||||
|             iscsilun->allocationmap = iscsi_allocationmap_init(iscsilun); | ||||
|             if (iscsilun->allocationmap == NULL) { | ||||
|                 ret = -ENOMEM; | ||||
| @@ -1794,7 +1655,7 @@ out: | ||||
| static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) | ||||
| { | ||||
|     IscsiLun *iscsilun = bs->opaque; | ||||
|     bdi->unallocated_blocks_are_zero = iscsilun->lbprz; | ||||
|     bdi->unallocated_blocks_are_zero = !!iscsilun->lbprz; | ||||
|     bdi->can_write_zeroes_with_unmap = iscsilun->lbprz && iscsilun->lbp.lbpws; | ||||
|     bdi->cluster_size = iscsilun->cluster_sectors * BDRV_SECTOR_SIZE; | ||||
|     return 0; | ||||
| @@ -1838,6 +1699,7 @@ static BlockDriver bdrv_iscsi = { | ||||
|     .bdrv_co_flush_to_disk = iscsi_co_flush, | ||||
|  | ||||
| #ifdef __linux__ | ||||
|     .bdrv_ioctl       = iscsi_ioctl, | ||||
|     .bdrv_aio_ioctl   = iscsi_aio_ioctl, | ||||
| #endif | ||||
|  | ||||
| @@ -1866,10 +1728,6 @@ static QemuOptsList qemu_iscsi_opts = { | ||||
|             .name = "initiator-name", | ||||
|             .type = QEMU_OPT_STRING, | ||||
|             .help = "Initiator iqn name to use when connecting", | ||||
|         },{ | ||||
|             .name = "timeout", | ||||
|             .type = QEMU_OPT_NUMBER, | ||||
|             .help = "Request timeout in seconds (default 0 = no timeout)", | ||||
|         }, | ||||
|         { /* end of list */ } | ||||
|     }, | ||||
|   | ||||
| @@ -7,7 +7,6 @@ | ||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||||
|  * See the COPYING file in the top-level directory. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "block/aio.h" | ||||
| #include "qemu/queue.h" | ||||
| @@ -288,7 +287,7 @@ void laio_detach_aio_context(void *s_, AioContext *old_context) | ||||
| { | ||||
|     struct qemu_laio_state *s = s_; | ||||
|  | ||||
|     aio_set_event_notifier(old_context, &s->e, false, NULL); | ||||
|     aio_set_event_notifier(old_context, &s->e, NULL); | ||||
|     qemu_bh_delete(s->completion_bh); | ||||
| } | ||||
|  | ||||
| @@ -297,8 +296,7 @@ void laio_attach_aio_context(void *s_, AioContext *new_context) | ||||
|     struct qemu_laio_state *s = s_; | ||||
|  | ||||
|     s->completion_bh = aio_bh_new(new_context, qemu_laio_completion_bh, s); | ||||
|     aio_set_event_notifier(new_context, &s->e, false, | ||||
|                            qemu_laio_completion_cb); | ||||
|     aio_set_event_notifier(new_context, &s->e, qemu_laio_completion_cb); | ||||
| } | ||||
|  | ||||
| void *laio_init(void) | ||||
|   | ||||
							
								
								
									
										214
									
								
								block/mirror.c
									
									
									
									
									
								
							
							
						
						
									
										214
									
								
								block/mirror.c
									
									
									
									
									
								
							| @@ -11,19 +11,14 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "trace.h" | ||||
| #include "block/blockjob.h" | ||||
| #include "block/block_int.h" | ||||
| #include "sysemu/block-backend.h" | ||||
| #include "qapi/qmp/qerror.h" | ||||
| #include "qemu/ratelimit.h" | ||||
| #include "qemu/bitmap.h" | ||||
| #include "qemu/error-report.h" | ||||
|  | ||||
| #define SLICE_TIME    100000000ULL /* ns */ | ||||
| #define MAX_IN_FLIGHT 16 | ||||
| #define DEFAULT_MIRROR_BUF_SIZE   (10 << 20) | ||||
|  | ||||
| /* The mirroring buffer is a list of granularity-sized chunks. | ||||
|  * Free chunks are organized in a list. | ||||
| @@ -63,7 +58,6 @@ typedef struct MirrorBlockJob { | ||||
|     int sectors_in_flight; | ||||
|     int ret; | ||||
|     bool unmap; | ||||
|     bool waiting_for_io; | ||||
| } MirrorBlockJob; | ||||
|  | ||||
| typedef struct MirrorOp { | ||||
| @@ -116,9 +110,13 @@ static void mirror_iteration_done(MirrorOp *op, int ret) | ||||
|     } | ||||
|  | ||||
|     qemu_iovec_destroy(&op->qiov); | ||||
|     g_free(op); | ||||
|     g_slice_free(MirrorOp, op); | ||||
|  | ||||
|     if (s->waiting_for_io) { | ||||
|     /* Enter coroutine when it is not sleeping.  The coroutine sleeps to | ||||
|      * rate-limit itself.  The coroutine will eventually resume since there is | ||||
|      * a sleep timeout so don't wake it early. | ||||
|      */ | ||||
|     if (s->common.busy) { | ||||
|         qemu_coroutine_enter(s->common.co, NULL); | ||||
|     } | ||||
| } | ||||
| @@ -128,9 +126,11 @@ static void mirror_write_complete(void *opaque, int ret) | ||||
|     MirrorOp *op = opaque; | ||||
|     MirrorBlockJob *s = op->s; | ||||
|     if (ret < 0) { | ||||
|         BlockDriverState *source = s->common.bs; | ||||
|         BlockErrorAction action; | ||||
|  | ||||
|         bdrv_set_dirty_bitmap(s->dirty_bitmap, op->sector_num, op->nb_sectors); | ||||
|         bdrv_set_dirty_bitmap(source, s->dirty_bitmap, op->sector_num, | ||||
|                               op->nb_sectors); | ||||
|         action = mirror_error_action(s, false, -ret); | ||||
|         if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) { | ||||
|             s->ret = ret; | ||||
| @@ -144,9 +144,11 @@ static void mirror_read_complete(void *opaque, int ret) | ||||
|     MirrorOp *op = opaque; | ||||
|     MirrorBlockJob *s = op->s; | ||||
|     if (ret < 0) { | ||||
|         BlockDriverState *source = s->common.bs; | ||||
|         BlockErrorAction action; | ||||
|  | ||||
|         bdrv_set_dirty_bitmap(s->dirty_bitmap, op->sector_num, op->nb_sectors); | ||||
|         bdrv_set_dirty_bitmap(source, s->dirty_bitmap, op->sector_num, | ||||
|                               op->nb_sectors); | ||||
|         action = mirror_error_action(s, true, -ret); | ||||
|         if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) { | ||||
|             s->ret = ret; | ||||
| @@ -162,21 +164,19 @@ static void mirror_read_complete(void *opaque, int ret) | ||||
| static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) | ||||
| { | ||||
|     BlockDriverState *source = s->common.bs; | ||||
|     int nb_sectors, sectors_per_chunk, nb_chunks, max_iov; | ||||
|     int nb_sectors, sectors_per_chunk, nb_chunks; | ||||
|     int64_t end, sector_num, next_chunk, next_sector, hbitmap_next_sector; | ||||
|     uint64_t delay_ns = 0; | ||||
|     MirrorOp *op; | ||||
|     int pnum; | ||||
|     int64_t ret; | ||||
|     BlockDriverState *file; | ||||
|  | ||||
|     max_iov = MIN(source->bl.max_iov, s->target->bl.max_iov); | ||||
|  | ||||
|     s->sector_num = hbitmap_iter_next(&s->hbi); | ||||
|     if (s->sector_num < 0) { | ||||
|         bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi); | ||||
|         bdrv_dirty_iter_init(source, s->dirty_bitmap, &s->hbi); | ||||
|         s->sector_num = hbitmap_iter_next(&s->hbi); | ||||
|         trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap)); | ||||
|         trace_mirror_restart_iter(s, | ||||
|                                   bdrv_get_dirty_count(source, s->dirty_bitmap)); | ||||
|         assert(s->sector_num >= 0); | ||||
|     } | ||||
|  | ||||
| @@ -206,9 +206,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) | ||||
|     /* Wait for I/O to this cluster (from a previous iteration) to be done.  */ | ||||
|     while (test_bit(next_chunk, s->in_flight_bitmap)) { | ||||
|         trace_mirror_yield_in_flight(s, sector_num, s->in_flight); | ||||
|         s->waiting_for_io = true; | ||||
|         qemu_coroutine_yield(); | ||||
|         s->waiting_for_io = false; | ||||
|     } | ||||
|  | ||||
|     do { | ||||
| @@ -244,18 +242,12 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) | ||||
|          */ | ||||
|         while (nb_chunks == 0 && s->buf_free_count < added_chunks) { | ||||
|             trace_mirror_yield_buf_busy(s, nb_chunks, s->in_flight); | ||||
|             s->waiting_for_io = true; | ||||
|             qemu_coroutine_yield(); | ||||
|             s->waiting_for_io = false; | ||||
|         } | ||||
|         if (s->buf_free_count < nb_chunks + added_chunks) { | ||||
|             trace_mirror_break_buf_busy(s, nb_chunks, s->in_flight); | ||||
|             break; | ||||
|         } | ||||
|         if (max_iov < nb_chunks + added_chunks) { | ||||
|             trace_mirror_break_iov_max(s, nb_chunks, added_chunks); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         /* We have enough free space to copy these sectors.  */ | ||||
|         bitmap_set(s->in_flight_bitmap, next_chunk, added_chunks); | ||||
| @@ -270,7 +262,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) | ||||
|     } while (delay_ns == 0 && next_sector < end); | ||||
|  | ||||
|     /* Allocate a MirrorOp that is used as an AIO callback.  */ | ||||
|     op = g_new(MirrorOp, 1); | ||||
|     op = g_slice_new(MirrorOp); | ||||
|     op->s = s; | ||||
|     op->sector_num = sector_num; | ||||
|     op->nb_sectors = nb_sectors; | ||||
| @@ -299,7 +291,8 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) | ||||
|         next_sector += sectors_per_chunk; | ||||
|     } | ||||
|  | ||||
|     bdrv_reset_dirty_bitmap(s->dirty_bitmap, sector_num, nb_sectors); | ||||
|     bdrv_reset_dirty_bitmap(source, s->dirty_bitmap, sector_num, | ||||
|                             nb_sectors); | ||||
|  | ||||
|     /* Copy the dirty cluster.  */ | ||||
|     s->in_flight++; | ||||
| @@ -307,7 +300,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) | ||||
|     trace_mirror_one_iteration(s, sector_num, nb_sectors); | ||||
|  | ||||
|     ret = bdrv_get_block_status_above(source, NULL, sector_num, | ||||
|                                       nb_sectors, &pnum, &file); | ||||
|                                       nb_sectors, &pnum); | ||||
|     if (ret < 0 || pnum < nb_sectors || | ||||
|             (ret & BDRV_BLOCK_DATA && !(ret & BDRV_BLOCK_ZERO))) { | ||||
|         bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors, | ||||
| @@ -344,9 +337,7 @@ static void mirror_free_init(MirrorBlockJob *s) | ||||
| static void mirror_drain(MirrorBlockJob *s) | ||||
| { | ||||
|     while (s->in_flight > 0) { | ||||
|         s->waiting_for_io = true; | ||||
|         qemu_coroutine_yield(); | ||||
|         s->waiting_for_io = false; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -359,11 +350,6 @@ static void mirror_exit(BlockJob *job, void *opaque) | ||||
|     MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); | ||||
|     MirrorExitData *data = opaque; | ||||
|     AioContext *replace_aio_context = NULL; | ||||
|     BlockDriverState *src = s->common.bs; | ||||
|  | ||||
|     /* Make sure that the source BDS doesn't go away before we called | ||||
|      * block_job_completed(). */ | ||||
|     bdrv_ref(src); | ||||
|  | ||||
|     if (s->to_replace) { | ||||
|         replace_aio_context = bdrv_get_aio_context(s->to_replace); | ||||
| @@ -375,22 +361,18 @@ static void mirror_exit(BlockJob *job, void *opaque) | ||||
|         if (s->to_replace) { | ||||
|             to_replace = s->to_replace; | ||||
|         } | ||||
|  | ||||
|         /* This was checked in mirror_start_job(), but meanwhile one of the | ||||
|          * nodes could have been newly attached to a BlockBackend. */ | ||||
|         if (to_replace->blk && s->target->blk) { | ||||
|             error_report("block job: Can't create node with two BlockBackends"); | ||||
|             data->ret = -EINVAL; | ||||
|             goto out; | ||||
|         } | ||||
|  | ||||
|         if (bdrv_get_flags(s->target) != bdrv_get_flags(to_replace)) { | ||||
|             bdrv_reopen(s->target, bdrv_get_flags(to_replace), NULL); | ||||
|         } | ||||
|         bdrv_replace_in_backing_chain(to_replace, s->target); | ||||
|         bdrv_swap(s->target, to_replace); | ||||
|         if (s->common.driver->job_type == BLOCK_JOB_TYPE_COMMIT) { | ||||
|             /* drop the bs loop chain formed by the swap: break the loop then | ||||
|              * trigger the unref from the top one */ | ||||
|             BlockDriverState *p = s->base->backing_hd; | ||||
|             bdrv_set_backing_hd(s->base, NULL); | ||||
|             bdrv_unref(p); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| out: | ||||
|     if (s->to_replace) { | ||||
|         bdrv_op_unblock_all(s->to_replace, s->replace_blocker); | ||||
|         error_free(s->replace_blocker); | ||||
| @@ -400,12 +382,9 @@ out: | ||||
|         aio_context_release(replace_aio_context); | ||||
|     } | ||||
|     g_free(s->replaces); | ||||
|     bdrv_op_unblock_all(s->target, s->common.blocker); | ||||
|     bdrv_unref(s->target); | ||||
|     block_job_completed(&s->common, data->ret); | ||||
|     g_free(data); | ||||
|     bdrv_drained_end(src); | ||||
|     bdrv_unref(src); | ||||
| } | ||||
|  | ||||
| static void coroutine_fn mirror_run(void *opaque) | ||||
| @@ -413,7 +392,7 @@ static void coroutine_fn mirror_run(void *opaque) | ||||
|     MirrorBlockJob *s = opaque; | ||||
|     MirrorExitData *data; | ||||
|     BlockDriverState *bs = s->common.bs; | ||||
|     int64_t sector_num, end, length; | ||||
|     int64_t sector_num, end, sectors_per_chunk, length; | ||||
|     uint64_t last_pause_ns; | ||||
|     BlockDriverInfo bdi; | ||||
|     char backing_filename[2]; /* we only need 2 characters because we are only | ||||
| @@ -449,7 +428,7 @@ static void coroutine_fn mirror_run(void *opaque) | ||||
|      */ | ||||
|     bdrv_get_backing_filename(s->target, backing_filename, | ||||
|                               sizeof(backing_filename)); | ||||
|     if (backing_filename[0] && !s->target->backing) { | ||||
|     if (backing_filename[0] && !s->target->backing_hd) { | ||||
|         ret = bdrv_get_info(s->target, &bdi); | ||||
|         if (ret < 0) { | ||||
|             goto immediate_exit; | ||||
| @@ -467,44 +446,33 @@ static void coroutine_fn mirror_run(void *opaque) | ||||
|         goto immediate_exit; | ||||
|     } | ||||
|  | ||||
|     sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS; | ||||
|     mirror_free_init(s); | ||||
|  | ||||
|     last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); | ||||
|     if (!s->is_none_mode) { | ||||
|         /* First part, loop on the sectors and initialize the dirty bitmap.  */ | ||||
|         BlockDriverState *base = s->base; | ||||
|         bool mark_all_dirty = s->base == NULL && !bdrv_has_zero_init(s->target); | ||||
|  | ||||
|         for (sector_num = 0; sector_num < end; ) { | ||||
|             /* Just to make sure we are not exceeding int limit. */ | ||||
|             int nb_sectors = MIN(INT_MAX >> BDRV_SECTOR_BITS, | ||||
|                                  end - sector_num); | ||||
|             int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); | ||||
|  | ||||
|             if (now - last_pause_ns > SLICE_TIME) { | ||||
|                 last_pause_ns = now; | ||||
|                 block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, 0); | ||||
|             } | ||||
|  | ||||
|             if (block_job_is_cancelled(&s->common)) { | ||||
|                 goto immediate_exit; | ||||
|             } | ||||
|  | ||||
|             ret = bdrv_is_allocated_above(bs, base, sector_num, nb_sectors, &n); | ||||
|             int64_t next = (sector_num | (sectors_per_chunk - 1)) + 1; | ||||
|             ret = bdrv_is_allocated_above(bs, base, | ||||
|                                           sector_num, next - sector_num, &n); | ||||
|  | ||||
|             if (ret < 0) { | ||||
|                 goto immediate_exit; | ||||
|             } | ||||
|  | ||||
|             assert(n > 0); | ||||
|             if (ret == 1 || mark_all_dirty) { | ||||
|                 bdrv_set_dirty_bitmap(s->dirty_bitmap, sector_num, n); | ||||
|             } | ||||
|             if (ret == 1) { | ||||
|                 bdrv_set_dirty_bitmap(bs, s->dirty_bitmap, sector_num, n); | ||||
|                 sector_num = next; | ||||
|             } else { | ||||
|                 sector_num += n; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi); | ||||
|     bdrv_dirty_iter_init(bs, s->dirty_bitmap, &s->hbi); | ||||
|     last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); | ||||
|     for (;;) { | ||||
|         uint64_t delay_ns = 0; | ||||
|         int64_t cnt; | ||||
| @@ -515,7 +483,7 @@ static void coroutine_fn mirror_run(void *opaque) | ||||
|             goto immediate_exit; | ||||
|         } | ||||
|  | ||||
|         cnt = bdrv_get_dirty_count(s->dirty_bitmap); | ||||
|         cnt = bdrv_get_dirty_count(bs, s->dirty_bitmap); | ||||
|         /* s->common.offset contains the number of bytes already processed so | ||||
|          * far, cnt is the number of dirty sectors remaining and | ||||
|          * s->sectors_in_flight is the number of sectors currently being | ||||
| @@ -524,7 +492,7 @@ static void coroutine_fn mirror_run(void *opaque) | ||||
|                         (cnt + s->sectors_in_flight) * BDRV_SECTOR_SIZE; | ||||
|  | ||||
|         /* Note that even when no rate limit is applied we need to yield | ||||
|          * periodically with no pending I/O so that bdrv_drain_all() returns. | ||||
|          * periodically with no pending I/O so that qemu_aio_flush() returns. | ||||
|          * We do so every SLICE_TIME nanoseconds, or when there is an error, | ||||
|          * or when the source is clean, whichever comes first. | ||||
|          */ | ||||
| @@ -533,12 +501,13 @@ static void coroutine_fn mirror_run(void *opaque) | ||||
|             if (s->in_flight == MAX_IN_FLIGHT || s->buf_free_count == 0 || | ||||
|                 (cnt == 0 && s->in_flight > 0)) { | ||||
|                 trace_mirror_yield(s, s->in_flight, s->buf_free_count, cnt); | ||||
|                 s->waiting_for_io = true; | ||||
|                 qemu_coroutine_yield(); | ||||
|                 s->waiting_for_io = false; | ||||
|                 continue; | ||||
|             } else if (cnt != 0) { | ||||
|                 delay_ns = mirror_iteration(s); | ||||
|                 if (delay_ns == 0) { | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -564,7 +533,7 @@ static void coroutine_fn mirror_run(void *opaque) | ||||
|  | ||||
|                 should_complete = s->should_complete || | ||||
|                     block_job_is_cancelled(&s->common); | ||||
|                 cnt = bdrv_get_dirty_count(s->dirty_bitmap); | ||||
|                 cnt = bdrv_get_dirty_count(bs, s->dirty_bitmap); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -579,7 +548,7 @@ static void coroutine_fn mirror_run(void *opaque) | ||||
|              */ | ||||
|             trace_mirror_before_drain(s, cnt); | ||||
|             bdrv_drain(bs); | ||||
|             cnt = bdrv_get_dirty_count(s->dirty_bitmap); | ||||
|             cnt = bdrv_get_dirty_count(bs, s->dirty_bitmap); | ||||
|         } | ||||
|  | ||||
|         ret = 0; | ||||
| @@ -618,15 +587,10 @@ immediate_exit: | ||||
|     g_free(s->cow_bitmap); | ||||
|     g_free(s->in_flight_bitmap); | ||||
|     bdrv_release_dirty_bitmap(bs, s->dirty_bitmap); | ||||
|     if (s->target->blk) { | ||||
|         blk_iostatus_disable(s->target->blk); | ||||
|     } | ||||
|     bdrv_iostatus_disable(s->target); | ||||
|  | ||||
|     data = g_malloc(sizeof(*data)); | ||||
|     data->ret = ret; | ||||
|     /* Before we switch to target in mirror_exit, make sure data doesn't | ||||
|      * change. */ | ||||
|     bdrv_drained_begin(s->common.bs); | ||||
|     block_job_defer_to_main_loop(&s->common, mirror_exit, data); | ||||
| } | ||||
|  | ||||
| @@ -635,7 +599,7 @@ static void mirror_set_speed(BlockJob *job, int64_t speed, Error **errp) | ||||
|     MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); | ||||
|  | ||||
|     if (speed < 0) { | ||||
|         error_setg(errp, QERR_INVALID_PARAMETER, "speed"); | ||||
|         error_set(errp, QERR_INVALID_PARAMETER, "speed"); | ||||
|         return; | ||||
|     } | ||||
|     ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME); | ||||
| @@ -645,9 +609,7 @@ static void mirror_iostatus_reset(BlockJob *job) | ||||
| { | ||||
|     MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); | ||||
|  | ||||
|     if (s->target->blk) { | ||||
|         blk_iostatus_reset(s->target->blk); | ||||
|     } | ||||
|     bdrv_iostatus_reset(s->target); | ||||
| } | ||||
|  | ||||
| static void mirror_complete(BlockJob *job, Error **errp) | ||||
| @@ -656,13 +618,14 @@ static void mirror_complete(BlockJob *job, Error **errp) | ||||
|     Error *local_err = NULL; | ||||
|     int ret; | ||||
|  | ||||
|     ret = bdrv_open_backing_file(s->target, NULL, "backing", &local_err); | ||||
|     ret = bdrv_open_backing_file(s->target, NULL, &local_err); | ||||
|     if (ret < 0) { | ||||
|         error_propagate(errp, local_err); | ||||
|         return; | ||||
|     } | ||||
|     if (!s->synced) { | ||||
|         error_setg(errp, QERR_BLOCK_JOB_NOT_READY, job->id); | ||||
|         error_set(errp, QERR_BLOCK_JOB_NOT_READY, | ||||
|                   bdrv_get_device_name(job->bs)); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -670,9 +633,9 @@ static void mirror_complete(BlockJob *job, Error **errp) | ||||
|     if (s->replaces) { | ||||
|         AioContext *replace_aio_context; | ||||
|  | ||||
|         s->to_replace = bdrv_find_node(s->replaces); | ||||
|         s->to_replace = check_to_replace_node(s->replaces, &local_err); | ||||
|         if (!s->to_replace) { | ||||
|             error_setg(errp, "Node name '%s' not found", s->replaces); | ||||
|             error_propagate(errp, local_err); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -688,7 +651,7 @@ static void mirror_complete(BlockJob *job, Error **errp) | ||||
|     } | ||||
|  | ||||
|     s->should_complete = true; | ||||
|     block_job_enter(&s->common); | ||||
|     block_job_resume(job); | ||||
| } | ||||
|  | ||||
| static const BlockJobDriver mirror_job_driver = { | ||||
| @@ -710,7 +673,7 @@ static const BlockJobDriver commit_active_job_driver = { | ||||
|  | ||||
| static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, | ||||
|                              const char *replaces, | ||||
|                              int64_t speed, uint32_t granularity, | ||||
|                              int64_t speed, int64_t granularity, | ||||
|                              int64_t buf_size, | ||||
|                              BlockdevOnError on_source_error, | ||||
|                              BlockdevOnError on_target_error, | ||||
| @@ -721,44 +684,28 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, | ||||
|                              bool is_none_mode, BlockDriverState *base) | ||||
| { | ||||
|     MirrorBlockJob *s; | ||||
|     BlockDriverState *replaced_bs; | ||||
|  | ||||
|     if (granularity == 0) { | ||||
|         granularity = bdrv_get_default_bitmap_granularity(target); | ||||
|         /* Choose the default granularity based on the target file's cluster | ||||
|          * size, clamped between 4k and 64k.  */ | ||||
|         BlockDriverInfo bdi; | ||||
|         if (bdrv_get_info(target, &bdi) >= 0 && bdi.cluster_size != 0) { | ||||
|             granularity = MAX(4096, bdi.cluster_size); | ||||
|             granularity = MIN(65536, granularity); | ||||
|         } else { | ||||
|             granularity = 65536; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     assert ((granularity & (granularity - 1)) == 0); | ||||
|  | ||||
|     if ((on_source_error == BLOCKDEV_ON_ERROR_STOP || | ||||
|          on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) && | ||||
|         (!bs->blk || !blk_iostatus_is_enabled(bs->blk))) { | ||||
|         error_setg(errp, QERR_INVALID_PARAMETER, "on-source-error"); | ||||
|         !bdrv_iostatus_is_enabled(bs)) { | ||||
|         error_set(errp, QERR_INVALID_PARAMETER, "on-source-error"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (buf_size < 0) { | ||||
|         error_setg(errp, "Invalid parameter 'buf-size'"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (buf_size == 0) { | ||||
|         buf_size = DEFAULT_MIRROR_BUF_SIZE; | ||||
|     } | ||||
|  | ||||
|     /* We can't support this case as long as the block layer can't handle | ||||
|      * multiple BlockBackends per BlockDriverState. */ | ||||
|     if (replaces) { | ||||
|         replaced_bs = bdrv_lookup_bs(replaces, replaces, errp); | ||||
|         if (replaced_bs == NULL) { | ||||
|             return; | ||||
|         } | ||||
|     } else { | ||||
|         replaced_bs = bs; | ||||
|     } | ||||
|     if (replaced_bs->blk && target->blk) { | ||||
|         error_setg(errp, "Can't create node with two BlockBackends"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     s = block_job_create(driver, bs, speed, cb, opaque, errp); | ||||
|     if (!s) { | ||||
| @@ -772,23 +719,16 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, | ||||
|     s->is_none_mode = is_none_mode; | ||||
|     s->base = base; | ||||
|     s->granularity = granularity; | ||||
|     s->buf_size = ROUND_UP(buf_size, granularity); | ||||
|     s->buf_size = MAX(buf_size, granularity); | ||||
|     s->unmap = unmap; | ||||
|  | ||||
|     s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp); | ||||
|     s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, errp); | ||||
|     if (!s->dirty_bitmap) { | ||||
|         g_free(s->replaces); | ||||
|         block_job_unref(&s->common); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     bdrv_op_block_all(s->target, s->common.blocker); | ||||
|  | ||||
|     bdrv_set_enable_write_cache(s->target, true); | ||||
|     if (s->target->blk) { | ||||
|         blk_set_on_error(s->target->blk, on_target_error, on_target_error); | ||||
|         blk_iostatus_enable(s->target->blk); | ||||
|     } | ||||
|     bdrv_set_on_error(s->target, on_target_error, on_target_error); | ||||
|     bdrv_iostatus_enable(s->target); | ||||
|     s->common.co = qemu_coroutine_create(mirror_run); | ||||
|     trace_mirror_start(bs, s, s->common.co, opaque); | ||||
|     qemu_coroutine_enter(s->common.co, s); | ||||
| @@ -796,7 +736,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, | ||||
|  | ||||
| void mirror_start(BlockDriverState *bs, BlockDriverState *target, | ||||
|                   const char *replaces, | ||||
|                   int64_t speed, uint32_t granularity, int64_t buf_size, | ||||
|                   int64_t speed, int64_t granularity, int64_t buf_size, | ||||
|                   MirrorSyncMode mode, BlockdevOnError on_source_error, | ||||
|                   BlockdevOnError on_target_error, | ||||
|                   bool unmap, | ||||
| @@ -806,12 +746,8 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target, | ||||
|     bool is_none_mode; | ||||
|     BlockDriverState *base; | ||||
|  | ||||
|     if (mode == MIRROR_SYNC_MODE_INCREMENTAL) { | ||||
|         error_setg(errp, "Sync mode 'incremental' not supported"); | ||||
|         return; | ||||
|     } | ||||
|     is_none_mode = mode == MIRROR_SYNC_MODE_NONE; | ||||
|     base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL; | ||||
|     base = mode == MIRROR_SYNC_MODE_TOP ? bs->backing_hd : NULL; | ||||
|     mirror_start_job(bs, target, replaces, | ||||
|                      speed, granularity, buf_size, | ||||
|                      on_source_error, on_target_error, unmap, cb, opaque, errp, | ||||
|   | ||||
| @@ -26,8 +26,8 @@ | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "nbd-client.h" | ||||
| #include "qemu/sockets.h" | ||||
|  | ||||
| #define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs)) | ||||
| #define INDEX_TO_HANDLE(bs, index)  ((index)  ^ ((uint64_t)(intptr_t)bs)) | ||||
| @@ -47,21 +47,13 @@ static void nbd_teardown_connection(BlockDriverState *bs) | ||||
| { | ||||
|     NbdClientSession *client = nbd_get_client_session(bs); | ||||
|  | ||||
|     if (!client->ioc) { /* Already closed */ | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* finish any pending coroutines */ | ||||
|     qio_channel_shutdown(client->ioc, | ||||
|                          QIO_CHANNEL_SHUTDOWN_BOTH, | ||||
|                          NULL); | ||||
|     shutdown(client->sock, 2); | ||||
|     nbd_recv_coroutines_enter_all(client); | ||||
|  | ||||
|     nbd_client_detach_aio_context(bs); | ||||
|     object_unref(OBJECT(client->sioc)); | ||||
|     client->sioc = NULL; | ||||
|     object_unref(OBJECT(client->ioc)); | ||||
|     client->ioc = NULL; | ||||
|     closesocket(client->sock); | ||||
|     client->sock = -1; | ||||
| } | ||||
|  | ||||
| static void nbd_reply_ready(void *opaque) | ||||
| @@ -71,16 +63,12 @@ static void nbd_reply_ready(void *opaque) | ||||
|     uint64_t i; | ||||
|     int ret; | ||||
|  | ||||
|     if (!s->ioc) { /* Already closed */ | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     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->sock, &s->reply); | ||||
|         if (ret == -EAGAIN) { | ||||
|             return; | ||||
|         } | ||||
| @@ -131,36 +119,32 @@ static int nbd_co_send_request(BlockDriverState *bs, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     g_assert(qemu_in_coroutine()); | ||||
|     assert(i < MAX_NBD_REQUESTS); | ||||
|     request->handle = INDEX_TO_HANDLE(s, i); | ||||
|  | ||||
|     if (!s->ioc) { | ||||
|         qemu_co_mutex_unlock(&s->send_mutex); | ||||
|         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, | ||||
|     aio_set_fd_handler(aio_context, s->sock, | ||||
|                        nbd_reply_ready, nbd_restart_write, bs); | ||||
|     if (qiov) { | ||||
|         qio_channel_set_cork(s->ioc, true); | ||||
|         rc = nbd_send_request(s->ioc, request); | ||||
|         if (!s->is_unix) { | ||||
|             socket_set_cork(s->sock, 1); | ||||
|         } | ||||
|         rc = nbd_send_request(s->sock, request); | ||||
|         if (rc >= 0) { | ||||
|             ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov, | ||||
|                                offset, request->len, 0); | ||||
|             ret = qemu_co_sendv(s->sock, qiov->iov, qiov->niov, | ||||
|                                 offset, request->len); | ||||
|             if (ret != request->len) { | ||||
|                 rc = -EIO; | ||||
|             } | ||||
|         } | ||||
|         qio_channel_set_cork(s->ioc, false); | ||||
|     } else { | ||||
|         rc = nbd_send_request(s->ioc, request); | ||||
|         if (!s->is_unix) { | ||||
|             socket_set_cork(s->sock, 0); | ||||
|         } | ||||
|     aio_set_fd_handler(aio_context, s->sioc->fd, false, | ||||
|                        nbd_reply_ready, NULL, bs); | ||||
|     } else { | ||||
|         rc = nbd_send_request(s->sock, request); | ||||
|     } | ||||
|     aio_set_fd_handler(aio_context, s->sock, nbd_reply_ready, NULL, bs); | ||||
|     s->send_coroutine = NULL; | ||||
|     qemu_co_mutex_unlock(&s->send_mutex); | ||||
|     return rc; | ||||
| @@ -176,13 +160,12 @@ static void nbd_co_receive_reply(NbdClientSession *s, | ||||
|      * peek at the next reply and avoid yielding if it's ours?  */ | ||||
|     qemu_coroutine_yield(); | ||||
|     *reply = s->reply; | ||||
|     if (reply->handle != request->handle || | ||||
|         !s->ioc) { | ||||
|     if (reply->handle != request->handle) { | ||||
|         reply->error = EIO; | ||||
|     } else { | ||||
|         if (qiov && reply->error == 0) { | ||||
|             ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov, | ||||
|                                offset, request->len, 1); | ||||
|             ret = qemu_co_recvv(s->sock, qiov->iov, qiov->niov, | ||||
|                                 offset, request->len); | ||||
|             if (ret != request->len) { | ||||
|                 reply->error = EIO; | ||||
|             } | ||||
| @@ -365,15 +348,14 @@ int nbd_client_co_discard(BlockDriverState *bs, int64_t sector_num, | ||||
| void nbd_client_detach_aio_context(BlockDriverState *bs) | ||||
| { | ||||
|     aio_set_fd_handler(bdrv_get_aio_context(bs), | ||||
|                        nbd_get_client_session(bs)->sioc->fd, | ||||
|                        false, NULL, NULL, NULL); | ||||
|                        nbd_get_client_session(bs)->sock, NULL, NULL, NULL); | ||||
| } | ||||
|  | ||||
| void nbd_client_attach_aio_context(BlockDriverState *bs, | ||||
|                                    AioContext *new_context) | ||||
| { | ||||
|     aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sioc->fd, | ||||
|                        false, nbd_reply_ready, NULL, bs); | ||||
|     aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sock, | ||||
|                        nbd_reply_ready, NULL, bs); | ||||
| } | ||||
|  | ||||
| void nbd_client_close(BlockDriverState *bs) | ||||
| @@ -385,20 +367,16 @@ void nbd_client_close(BlockDriverState *bs) | ||||
|         .len = 0 | ||||
|     }; | ||||
|  | ||||
|     if (client->ioc == NULL) { | ||||
|     if (client->sock == -1) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     nbd_send_request(client->ioc, &request); | ||||
|     nbd_send_request(client->sock, &request); | ||||
|  | ||||
|     nbd_teardown_connection(bs); | ||||
| } | ||||
|  | ||||
| int nbd_client_init(BlockDriverState *bs, | ||||
|                     QIOChannelSocket *sioc, | ||||
|                     const char *export, | ||||
|                     QCryptoTLSCreds *tlscreds, | ||||
|                     const char *hostname, | ||||
| int nbd_client_init(BlockDriverState *bs, int sock, const char *export, | ||||
|                     Error **errp) | ||||
| { | ||||
|     NbdClientSession *client = nbd_get_client_session(bs); | ||||
| @@ -406,32 +384,22 @@ int nbd_client_init(BlockDriverState *bs, | ||||
|  | ||||
|     /* NBD handshake */ | ||||
|     logout("session init %s\n", export); | ||||
|     qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL); | ||||
|  | ||||
|     ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export, | ||||
|                                 &client->nbdflags, | ||||
|                                 tlscreds, hostname, | ||||
|                                 &client->ioc, | ||||
|                                 &client->size, errp); | ||||
|     qemu_set_block(sock); | ||||
|     ret = nbd_receive_negotiate(sock, export, | ||||
|                                 &client->nbdflags, &client->size, errp); | ||||
|     if (ret < 0) { | ||||
|         logout("Failed to negotiate with the NBD server\n"); | ||||
|         closesocket(sock); | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     qemu_co_mutex_init(&client->send_mutex); | ||||
|     qemu_co_mutex_init(&client->free_sema); | ||||
|     client->sioc = sioc; | ||||
|     object_ref(OBJECT(client->sioc)); | ||||
|  | ||||
|     if (!client->ioc) { | ||||
|         client->ioc = QIO_CHANNEL(sioc); | ||||
|         object_ref(OBJECT(client->ioc)); | ||||
|     } | ||||
|     client->sock = sock; | ||||
|  | ||||
|     /* Now that we're connected, set the socket to be non-blocking and | ||||
|      * kick the reply mechanism.  */ | ||||
|     qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL); | ||||
|  | ||||
|     qemu_set_nonblock(sock); | ||||
|     nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs)); | ||||
|  | ||||
|     logout("Established connection with NBD server\n"); | ||||
|   | ||||
| @@ -4,7 +4,6 @@ | ||||
| #include "qemu-common.h" | ||||
| #include "block/nbd.h" | ||||
| #include "block/block_int.h" | ||||
| #include "io/channel-socket.h" | ||||
|  | ||||
| /* #define DEBUG_NBD */ | ||||
|  | ||||
| @@ -18,8 +17,7 @@ | ||||
| #define MAX_NBD_REQUESTS    16 | ||||
|  | ||||
| typedef struct NbdClientSession { | ||||
|     QIOChannelSocket *sioc; /* The master data channel */ | ||||
|     QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */ | ||||
|     int sock; | ||||
|     uint32_t nbdflags; | ||||
|     off_t size; | ||||
|  | ||||
| @@ -36,11 +34,7 @@ typedef struct NbdClientSession { | ||||
|  | ||||
| NbdClientSession *nbd_get_client_session(BlockDriverState *bs); | ||||
|  | ||||
| int nbd_client_init(BlockDriverState *bs, | ||||
|                     QIOChannelSocket *sock, | ||||
|                     const char *export_name, | ||||
|                     QCryptoTLSCreds *tlscreds, | ||||
|                     const char *hostname, | ||||
| int nbd_client_init(BlockDriverState *bs, int sock, const char *export_name, | ||||
|                     Error **errp); | ||||
| void nbd_client_close(BlockDriverState *bs); | ||||
|  | ||||
|   | ||||
							
								
								
									
										169
									
								
								block/nbd.c
									
									
									
									
									
								
							
							
						
						
									
										169
									
								
								block/nbd.c
									
									
									
									
									
								
							| @@ -26,21 +26,24 @@ | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "block/nbd-client.h" | ||||
| #include "qemu/uri.h" | ||||
| #include "block/block_int.h" | ||||
| #include "qemu/module.h" | ||||
| #include "qemu/sockets.h" | ||||
| #include "qapi/qmp/qdict.h" | ||||
| #include "qapi/qmp/qjson.h" | ||||
| #include "qapi/qmp/qint.h" | ||||
| #include "qapi/qmp/qstring.h" | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #define EN_OPTSTR ":exportname=" | ||||
|  | ||||
| typedef struct BDRVNBDState { | ||||
|     NbdClientSession client; | ||||
|     QemuOpts *socket_opts; | ||||
| } BDRVNBDState; | ||||
|  | ||||
| static int nbd_parse_uri(const char *filename, QDict *options) | ||||
| @@ -187,10 +190,10 @@ out: | ||||
|     g_free(file); | ||||
| } | ||||
|  | ||||
| static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, char **export, | ||||
| static void nbd_config(BDRVNBDState *s, QDict *options, char **export, | ||||
|                        Error **errp) | ||||
| { | ||||
|     SocketAddress *saddr; | ||||
|     Error *local_err = NULL; | ||||
|  | ||||
|     if (qdict_haskey(options, "path") == qdict_haskey(options, "host")) { | ||||
|         if (qdict_haskey(options, "path")) { | ||||
| @@ -198,37 +201,28 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, char **export, | ||||
|         } else { | ||||
|             error_setg(errp, "one of path and host must be specified."); | ||||
|         } | ||||
|         return NULL; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     saddr = g_new0(SocketAddress, 1); | ||||
|     s->client.is_unix = qdict_haskey(options, "path"); | ||||
|     s->socket_opts = qemu_opts_create(&socket_optslist, NULL, 0, | ||||
|                                       &error_abort); | ||||
|  | ||||
|     if (qdict_haskey(options, "path")) { | ||||
|         saddr->type = SOCKET_ADDRESS_KIND_UNIX; | ||||
|         saddr->u.q_unix = g_new0(UnixSocketAddress, 1); | ||||
|         saddr->u.q_unix->path = g_strdup(qdict_get_str(options, "path")); | ||||
|         qdict_del(options, "path"); | ||||
|     } else { | ||||
|         saddr->type = SOCKET_ADDRESS_KIND_INET; | ||||
|         saddr->u.inet = g_new0(InetSocketAddress, 1); | ||||
|         saddr->u.inet->host = g_strdup(qdict_get_str(options, "host")); | ||||
|         if (!qdict_get_try_str(options, "port")) { | ||||
|             saddr->u.inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT); | ||||
|         } else { | ||||
|             saddr->u.inet->port = g_strdup(qdict_get_str(options, "port")); | ||||
|         } | ||||
|         qdict_del(options, "host"); | ||||
|         qdict_del(options, "port"); | ||||
|     qemu_opts_absorb_qdict(s->socket_opts, options, &local_err); | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     s->client.is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX; | ||||
|     if (!qemu_opt_get(s->socket_opts, "port")) { | ||||
|         qemu_opt_set_number(s->socket_opts, "port", NBD_DEFAULT_PORT, | ||||
|                             &error_abort); | ||||
|     } | ||||
|  | ||||
|     *export = g_strdup(qdict_get_try_str(options, "export")); | ||||
|     if (*export) { | ||||
|         qdict_del(options, "export"); | ||||
|     } | ||||
|  | ||||
|     return saddr; | ||||
| } | ||||
|  | ||||
| NbdClientSession *nbd_get_client_session(BlockDriverState *bs) | ||||
| @@ -237,113 +231,57 @@ NbdClientSession *nbd_get_client_session(BlockDriverState *bs) | ||||
|     return &s->client; | ||||
| } | ||||
|  | ||||
| static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr, | ||||
|                                                   Error **errp) | ||||
| static int nbd_establish_connection(BlockDriverState *bs, Error **errp) | ||||
| { | ||||
|     QIOChannelSocket *sioc; | ||||
|     Error *local_err = NULL; | ||||
|     BDRVNBDState *s = bs->opaque; | ||||
|     int sock; | ||||
|  | ||||
|     sioc = qio_channel_socket_new(); | ||||
|  | ||||
|     qio_channel_socket_connect_sync(sioc, | ||||
|                                     saddr, | ||||
|                                     &local_err); | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         return NULL; | ||||
|     if (s->client.is_unix) { | ||||
|         sock = unix_connect_opts(s->socket_opts, errp, NULL, NULL); | ||||
|     } else { | ||||
|         sock = inet_connect_opts(s->socket_opts, errp, NULL, NULL); | ||||
|         if (sock >= 0) { | ||||
|             socket_set_nodelay(sock); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     qio_channel_set_delay(QIO_CHANNEL(sioc), false); | ||||
|  | ||||
|     return sioc; | ||||
|     /* Failed to establish connection */ | ||||
|     if (sock < 0) { | ||||
|         logout("Failed to establish connection to NBD server\n"); | ||||
|         return -EIO; | ||||
|     } | ||||
|  | ||||
|  | ||||
| static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) | ||||
| { | ||||
|     Object *obj; | ||||
|     QCryptoTLSCreds *creds; | ||||
|  | ||||
|     obj = object_resolve_path_component( | ||||
|         object_get_objects_root(), id); | ||||
|     if (!obj) { | ||||
|         error_setg(errp, "No TLS credentials with id '%s'", | ||||
|                    id); | ||||
|         return NULL; | ||||
|     return sock; | ||||
| } | ||||
|     creds = (QCryptoTLSCreds *) | ||||
|         object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS); | ||||
|     if (!creds) { | ||||
|         error_setg(errp, "Object with id '%s' is not TLS credentials", | ||||
|                    id); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { | ||||
|         error_setg(errp, | ||||
|                    "Expecting TLS credentials with a client endpoint"); | ||||
|         return NULL; | ||||
|     } | ||||
|     object_ref(obj); | ||||
|     return creds; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int nbd_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                     Error **errp) | ||||
| { | ||||
|     BDRVNBDState *s = bs->opaque; | ||||
|     char *export = NULL; | ||||
|     QIOChannelSocket *sioc = NULL; | ||||
|     SocketAddress *saddr; | ||||
|     const char *tlscredsid; | ||||
|     QCryptoTLSCreds *tlscreds = NULL; | ||||
|     const char *hostname = NULL; | ||||
|     int ret = -EINVAL; | ||||
|     int result, sock; | ||||
|     Error *local_err = NULL; | ||||
|  | ||||
|     /* Pop the config into our state object. Exit if invalid. */ | ||||
|     saddr = nbd_config(s, options, &export, errp); | ||||
|     if (!saddr) { | ||||
|         goto error; | ||||
|     } | ||||
|  | ||||
|     tlscredsid = g_strdup(qdict_get_try_str(options, "tls-creds")); | ||||
|     if (tlscredsid) { | ||||
|         qdict_del(options, "tls-creds"); | ||||
|         tlscreds = nbd_get_tls_creds(tlscredsid, errp); | ||||
|         if (!tlscreds) { | ||||
|             goto error; | ||||
|         } | ||||
|  | ||||
|         if (saddr->type != SOCKET_ADDRESS_KIND_INET) { | ||||
|             error_setg(errp, "TLS only supported over IP sockets"); | ||||
|             goto error; | ||||
|         } | ||||
|         hostname = saddr->u.inet->host; | ||||
|     nbd_config(s, options, &export, &local_err); | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     /* establish TCP connection, return error if it fails | ||||
|      * TODO: Configurable retry-until-timeout behaviour. | ||||
|      */ | ||||
|     sioc = nbd_establish_connection(saddr, errp); | ||||
|     if (!sioc) { | ||||
|         ret = -ECONNREFUSED; | ||||
|         goto error; | ||||
|     sock = nbd_establish_connection(bs, errp); | ||||
|     if (sock < 0) { | ||||
|         g_free(export); | ||||
|         return sock; | ||||
|     } | ||||
|  | ||||
|     /* NBD handshake */ | ||||
|     ret = nbd_client_init(bs, sioc, export, | ||||
|                           tlscreds, hostname, errp); | ||||
|  error: | ||||
|     if (sioc) { | ||||
|         object_unref(OBJECT(sioc)); | ||||
|     } | ||||
|     if (tlscreds) { | ||||
|         object_unref(OBJECT(tlscreds)); | ||||
|     } | ||||
|     qapi_free_SocketAddress(saddr); | ||||
|     result = nbd_client_init(bs, sock, export, errp); | ||||
|     g_free(export); | ||||
|     return ret; | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| static int nbd_co_readv(BlockDriverState *bs, int64_t sector_num, | ||||
| @@ -377,6 +315,9 @@ static int nbd_co_discard(BlockDriverState *bs, int64_t sector_num, | ||||
|  | ||||
| static void nbd_close(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVNBDState *s = bs->opaque; | ||||
|  | ||||
|     qemu_opts_del(s->socket_opts); | ||||
|     nbd_client_close(bs); | ||||
| } | ||||
|  | ||||
| @@ -398,14 +339,13 @@ static void nbd_attach_aio_context(BlockDriverState *bs, | ||||
|     nbd_client_attach_aio_context(bs, new_context); | ||||
| } | ||||
|  | ||||
| static void nbd_refresh_filename(BlockDriverState *bs, QDict *options) | ||||
| static void nbd_refresh_filename(BlockDriverState *bs) | ||||
| { | ||||
|     QDict *opts = qdict_new(); | ||||
|     const char *path   = qdict_get_try_str(options, "path"); | ||||
|     const char *host   = qdict_get_try_str(options, "host"); | ||||
|     const char *port   = qdict_get_try_str(options, "port"); | ||||
|     const char *export = qdict_get_try_str(options, "export"); | ||||
|     const char *tlscreds = qdict_get_try_str(options, "tls-creds"); | ||||
|     const char *path   = qdict_get_try_str(bs->options, "path"); | ||||
|     const char *host   = qdict_get_try_str(bs->options, "host"); | ||||
|     const char *port   = qdict_get_try_str(bs->options, "port"); | ||||
|     const char *export = qdict_get_try_str(bs->options, "export"); | ||||
|  | ||||
|     qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("nbd"))); | ||||
|  | ||||
| @@ -440,9 +380,6 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options) | ||||
|     if (export) { | ||||
|         qdict_put_obj(opts, "export", QOBJECT(qstring_from_str(export))); | ||||
|     } | ||||
|     if (tlscreds) { | ||||
|         qdict_put_obj(opts, "tls-creds", QOBJECT(qstring_from_str(tlscreds))); | ||||
|     } | ||||
|  | ||||
|     bs->full_open_options = opts; | ||||
| } | ||||
|   | ||||
							
								
								
									
										57
									
								
								block/nfs.c
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								block/nfs.c
									
									
									
									
									
								
							| @@ -22,7 +22,7 @@ | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "config-host.h" | ||||
|  | ||||
| #include <poll.h> | ||||
| #include "qemu-common.h" | ||||
| @@ -43,7 +43,6 @@ typedef struct NFSClient { | ||||
|     int events; | ||||
|     bool has_zero_init; | ||||
|     AioContext *aio_context; | ||||
|     blkcnt_t st_blocks; | ||||
| } NFSClient; | ||||
|  | ||||
| typedef struct NFSRPC { | ||||
| @@ -63,10 +62,11 @@ static void nfs_set_events(NFSClient *client) | ||||
| { | ||||
|     int ev = nfs_which_events(client->context); | ||||
|     if (ev != client->events) { | ||||
|         aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context), | ||||
|                            false, | ||||
|         aio_set_fd_handler(client->aio_context, | ||||
|                            nfs_get_fd(client->context), | ||||
|                            (ev & POLLIN) ? nfs_process_read : NULL, | ||||
|                            (ev & POLLOUT) ? nfs_process_write : NULL, client); | ||||
|                            (ev & POLLOUT) ? nfs_process_write : NULL, | ||||
|                            client); | ||||
|  | ||||
|     } | ||||
|     client->events = ev; | ||||
| @@ -241,8 +241,9 @@ static void nfs_detach_aio_context(BlockDriverState *bs) | ||||
| { | ||||
|     NFSClient *client = bs->opaque; | ||||
|  | ||||
|     aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context), | ||||
|                        false, NULL, NULL, NULL); | ||||
|     aio_set_fd_handler(client->aio_context, | ||||
|                        nfs_get_fd(client->context), | ||||
|                        NULL, NULL, NULL); | ||||
|     client->events = 0; | ||||
| } | ||||
|  | ||||
| @@ -261,8 +262,9 @@ static void nfs_client_close(NFSClient *client) | ||||
|         if (client->fh) { | ||||
|             nfs_close(client->context, client->fh); | ||||
|         } | ||||
|         aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context), | ||||
|                            false, NULL, NULL, NULL); | ||||
|         aio_set_fd_handler(client->aio_context, | ||||
|                            nfs_get_fd(client->context), | ||||
|                            NULL, NULL, NULL); | ||||
|         nfs_destroy_context(client->context); | ||||
|     } | ||||
|     memset(client, 0, sizeof(NFSClient)); | ||||
| @@ -372,7 +374,6 @@ static int64_t nfs_client_open(NFSClient *client, const char *filename, | ||||
|     } | ||||
|  | ||||
|     ret = DIV_ROUND_UP(st.st_size, BDRV_SECTOR_SIZE); | ||||
|     client->st_blocks = st.st_blocks; | ||||
|     client->has_zero_init = S_ISREG(st.st_mode); | ||||
|     goto out; | ||||
| fail: | ||||
| @@ -463,11 +464,6 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) | ||||
|     NFSRPC task = {0}; | ||||
|     struct stat st; | ||||
|  | ||||
|     if (bdrv_is_read_only(bs) && | ||||
|         !(bs->open_flags & BDRV_O_NOCACHE)) { | ||||
|         return client->st_blocks * 512; | ||||
|     } | ||||
|  | ||||
|     task.st = &st; | ||||
|     if (nfs_fstat_async(client->context, client->fh, nfs_co_generic_cb, | ||||
|                         &task) != 0) { | ||||
| @@ -479,7 +475,7 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) | ||||
|         aio_poll(client->aio_context, true); | ||||
|     } | ||||
|  | ||||
|     return (task.ret < 0 ? task.ret : st.st_blocks * 512); | ||||
|     return (task.ret < 0 ? task.ret : st.st_blocks * st.st_blksize); | ||||
| } | ||||
|  | ||||
| static int nfs_file_truncate(BlockDriverState *bs, int64_t offset) | ||||
| @@ -488,34 +484,6 @@ static int nfs_file_truncate(BlockDriverState *bs, int64_t offset) | ||||
|     return nfs_ftruncate(client->context, client->fh, offset); | ||||
| } | ||||
|  | ||||
| /* Note that this will not re-establish a connection with the NFS server | ||||
|  * - it is effectively a NOP.  */ | ||||
| static int nfs_reopen_prepare(BDRVReopenState *state, | ||||
|                               BlockReopenQueue *queue, Error **errp) | ||||
| { | ||||
|     NFSClient *client = state->bs->opaque; | ||||
|     struct stat st; | ||||
|     int ret = 0; | ||||
|  | ||||
|     if (state->flags & BDRV_O_RDWR && bdrv_is_read_only(state->bs)) { | ||||
|         error_setg(errp, "Cannot open a read-only mount as read-write"); | ||||
|         return -EACCES; | ||||
|     } | ||||
|  | ||||
|     /* Update cache for read-only reopens */ | ||||
|     if (!(state->flags & BDRV_O_RDWR)) { | ||||
|         ret = nfs_fstat(client->context, client->fh, &st); | ||||
|         if (ret < 0) { | ||||
|             error_setg(errp, "Failed to fstat file: %s", | ||||
|                        nfs_get_error(client->context)); | ||||
|             return ret; | ||||
|         } | ||||
|         client->st_blocks = st.st_blocks; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static BlockDriver bdrv_nfs = { | ||||
|     .format_name                    = "nfs", | ||||
|     .protocol_name                  = "nfs", | ||||
| @@ -531,7 +499,6 @@ static BlockDriver bdrv_nfs = { | ||||
|     .bdrv_file_open                 = nfs_file_open, | ||||
|     .bdrv_close                     = nfs_file_close, | ||||
|     .bdrv_create                    = nfs_file_create, | ||||
|     .bdrv_reopen_prepare            = nfs_reopen_prepare, | ||||
|  | ||||
|     .bdrv_co_readv                  = nfs_co_readv, | ||||
|     .bdrv_co_writev                 = nfs_co_writev, | ||||
|   | ||||
							
								
								
									
										63
									
								
								block/null.c
									
									
									
									
									
								
							
							
						
						
									
										63
									
								
								block/null.c
									
									
									
									
									
								
							| @@ -10,14 +10,10 @@ | ||||
|  * See the COPYING file in the top-level directory. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "block/block_int.h" | ||||
|  | ||||
| #define NULL_OPT_LATENCY "latency-ns" | ||||
|  | ||||
| typedef struct { | ||||
|     int64_t length; | ||||
|     int64_t latency_ns; | ||||
| } BDRVNullState; | ||||
|  | ||||
| static QemuOptsList runtime_opts = { | ||||
| @@ -34,12 +30,6 @@ static QemuOptsList runtime_opts = { | ||||
|             .type = QEMU_OPT_SIZE, | ||||
|             .help = "size of the null block", | ||||
|         }, | ||||
|         { | ||||
|             .name = NULL_OPT_LATENCY, | ||||
|             .type = QEMU_OPT_NUMBER, | ||||
|             .help = "nanoseconds (approximated) to wait " | ||||
|                     "before completing request", | ||||
|         }, | ||||
|         { /* end of list */ } | ||||
|     }, | ||||
| }; | ||||
| @@ -49,20 +39,13 @@ static int null_file_open(BlockDriverState *bs, QDict *options, int flags, | ||||
| { | ||||
|     QemuOpts *opts; | ||||
|     BDRVNullState *s = bs->opaque; | ||||
|     int ret = 0; | ||||
|  | ||||
|     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); | ||||
|     qemu_opts_absorb_qdict(opts, options, &error_abort); | ||||
|     s->length = | ||||
|         qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 1 << 30); | ||||
|     s->latency_ns = | ||||
|         qemu_opt_get_number(opts, NULL_OPT_LATENCY, 0); | ||||
|     if (s->latency_ns < 0) { | ||||
|         error_setg(errp, "latency-ns is invalid"); | ||||
|         ret = -EINVAL; | ||||
|     } | ||||
|     qemu_opts_del(opts); | ||||
|     return ret; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void null_close(BlockDriverState *bs) | ||||
| @@ -75,40 +58,28 @@ static int64_t null_getlength(BlockDriverState *bs) | ||||
|     return s->length; | ||||
| } | ||||
|  | ||||
| static coroutine_fn int null_co_common(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVNullState *s = bs->opaque; | ||||
|  | ||||
|     if (s->latency_ns) { | ||||
|         co_aio_sleep_ns(bdrv_get_aio_context(bs), QEMU_CLOCK_REALTIME, | ||||
|                         s->latency_ns); | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static coroutine_fn int null_co_readv(BlockDriverState *bs, | ||||
|                                       int64_t sector_num, int nb_sectors, | ||||
|                                       QEMUIOVector *qiov) | ||||
| { | ||||
|     return null_co_common(bs); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static coroutine_fn int null_co_writev(BlockDriverState *bs, | ||||
|                                        int64_t sector_num, int nb_sectors, | ||||
|                                        QEMUIOVector *qiov) | ||||
| { | ||||
|     return null_co_common(bs); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static coroutine_fn int null_co_flush(BlockDriverState *bs) | ||||
| { | ||||
|     return null_co_common(bs); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| typedef struct { | ||||
|     BlockAIOCB common; | ||||
|     QEMUBH *bh; | ||||
|     QEMUTimer timer; | ||||
| } NullAIOCB; | ||||
|  | ||||
| static const AIOCBInfo null_aiocb_info = { | ||||
| @@ -123,33 +94,15 @@ static void null_bh_cb(void *opaque) | ||||
|     qemu_aio_unref(acb); | ||||
| } | ||||
|  | ||||
| static void null_timer_cb(void *opaque) | ||||
| { | ||||
|     NullAIOCB *acb = opaque; | ||||
|     acb->common.cb(acb->common.opaque, 0); | ||||
|     timer_deinit(&acb->timer); | ||||
|     qemu_aio_unref(acb); | ||||
| } | ||||
|  | ||||
| static inline BlockAIOCB *null_aio_common(BlockDriverState *bs, | ||||
|                                           BlockCompletionFunc *cb, | ||||
|                                           void *opaque) | ||||
| { | ||||
|     NullAIOCB *acb; | ||||
|     BDRVNullState *s = bs->opaque; | ||||
|  | ||||
|     acb = qemu_aio_get(&null_aiocb_info, bs, cb, opaque); | ||||
|     /* Only emulate latency after vcpu is running. */ | ||||
|     if (s->latency_ns) { | ||||
|         aio_timer_init(bdrv_get_aio_context(bs), &acb->timer, | ||||
|                        QEMU_CLOCK_REALTIME, SCALE_NS, | ||||
|                        null_timer_cb, acb); | ||||
|         timer_mod_ns(&acb->timer, | ||||
|                      qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + s->latency_ns); | ||||
|     } else { | ||||
|     acb->bh = aio_bh_new(bdrv_get_aio_context(bs), null_bh_cb, acb); | ||||
|     qemu_bh_schedule(acb->bh); | ||||
|     } | ||||
|     return &acb->common; | ||||
| } | ||||
|  | ||||
| @@ -178,12 +131,6 @@ static BlockAIOCB *null_aio_flush(BlockDriverState *bs, | ||||
|     return null_aio_common(bs, cb, opaque); | ||||
| } | ||||
|  | ||||
| static int null_reopen_prepare(BDRVReopenState *reopen_state, | ||||
|                                BlockReopenQueue *queue, Error **errp) | ||||
| { | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static BlockDriver bdrv_null_co = { | ||||
|     .format_name            = "null-co", | ||||
|     .protocol_name          = "null-co", | ||||
| @@ -196,7 +143,6 @@ static BlockDriver bdrv_null_co = { | ||||
|     .bdrv_co_readv          = null_co_readv, | ||||
|     .bdrv_co_writev         = null_co_writev, | ||||
|     .bdrv_co_flush_to_disk  = null_co_flush, | ||||
|     .bdrv_reopen_prepare    = null_reopen_prepare, | ||||
| }; | ||||
|  | ||||
| static BlockDriver bdrv_null_aio = { | ||||
| @@ -211,7 +157,6 @@ static BlockDriver bdrv_null_aio = { | ||||
|     .bdrv_aio_readv         = null_aio_readv, | ||||
|     .bdrv_aio_writev        = null_aio_writev, | ||||
|     .bdrv_aio_flush         = null_aio_flush, | ||||
|     .bdrv_reopen_prepare    = null_reopen_prepare, | ||||
| }; | ||||
|  | ||||
| static void bdrv_null_init(void) | ||||
|   | ||||
| @@ -2,12 +2,8 @@ | ||||
|  * Block driver for Parallels disk image format | ||||
|  * | ||||
|  * Copyright (c) 2007 Alex Beregszaszi | ||||
|  * Copyright (c) 2015 Denis V. Lunev <den@openvz.org> | ||||
|  * | ||||
|  * This code was originally based on comparing different disk images created | ||||
|  * by Parallels. Currently it is based on opened OpenVZ sources | ||||
|  * available at | ||||
|  *     http://git.openvz.org/?p=ploop;a=summary | ||||
|  * This code is based on comparing different disk images created by Parallels. | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
| @@ -27,548 +23,68 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "block/block_int.h" | ||||
| #include "qemu/module.h" | ||||
| #include "qemu/bitmap.h" | ||||
| #include "qapi/util.h" | ||||
|  | ||||
| /**************************************************************/ | ||||
|  | ||||
| #define HEADER_MAGIC "WithoutFreeSpace" | ||||
| #define HEADER_MAGIC2 "WithouFreSpacExt" | ||||
| #define HEADER_VERSION 2 | ||||
| #define HEADER_INUSE_MAGIC  (0x746F6E59) | ||||
|  | ||||
| #define DEFAULT_CLUSTER_SIZE 1048576        /* 1 MiB */ | ||||
|  | ||||
| #define HEADER_SIZE 64 | ||||
|  | ||||
| // always little-endian | ||||
| typedef struct ParallelsHeader { | ||||
| struct parallels_header { | ||||
|     char magic[16]; // "WithoutFreeSpace" | ||||
|     uint32_t version; | ||||
|     uint32_t heads; | ||||
|     uint32_t cylinders; | ||||
|     uint32_t tracks; | ||||
|     uint32_t bat_entries; | ||||
|     uint32_t catalog_entries; | ||||
|     uint64_t nb_sectors; | ||||
|     uint32_t inuse; | ||||
|     uint32_t data_off; | ||||
|     char padding[12]; | ||||
| } QEMU_PACKED ParallelsHeader; | ||||
|  | ||||
|  | ||||
| typedef enum ParallelsPreallocMode { | ||||
|     PRL_PREALLOC_MODE_FALLOCATE = 0, | ||||
|     PRL_PREALLOC_MODE_TRUNCATE = 1, | ||||
|     PRL_PREALLOC_MODE__MAX = 2, | ||||
| } ParallelsPreallocMode; | ||||
|  | ||||
| static const char *prealloc_mode_lookup[] = { | ||||
|     "falloc", | ||||
|     "truncate", | ||||
|     NULL, | ||||
| }; | ||||
|  | ||||
| } QEMU_PACKED; | ||||
|  | ||||
| typedef struct BDRVParallelsState { | ||||
|     /** Locking is conservative, the lock protects | ||||
|      *   - image file extending (truncate, fallocate) | ||||
|      *   - any access to block allocation table | ||||
|      */ | ||||
|     CoMutex lock; | ||||
|  | ||||
|     ParallelsHeader *header; | ||||
|     uint32_t header_size; | ||||
|     bool header_unclean; | ||||
|  | ||||
|     unsigned long *bat_dirty_bmap; | ||||
|     unsigned int  bat_dirty_block; | ||||
|  | ||||
|     uint32_t *bat_bitmap; | ||||
|     unsigned int bat_size; | ||||
|  | ||||
|     int64_t  data_end; | ||||
|     uint64_t prealloc_size; | ||||
|     ParallelsPreallocMode prealloc_mode; | ||||
|     uint32_t *catalog_bitmap; | ||||
|     unsigned int catalog_size; | ||||
|  | ||||
|     unsigned int tracks; | ||||
|  | ||||
|     unsigned int off_multiplier; | ||||
| } BDRVParallelsState; | ||||
|  | ||||
|  | ||||
| #define PARALLELS_OPT_PREALLOC_MODE     "prealloc-mode" | ||||
| #define PARALLELS_OPT_PREALLOC_SIZE     "prealloc-size" | ||||
|  | ||||
| static QemuOptsList parallels_runtime_opts = { | ||||
|     .name = "parallels", | ||||
|     .head = QTAILQ_HEAD_INITIALIZER(parallels_runtime_opts.head), | ||||
|     .desc = { | ||||
| static int parallels_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||
| { | ||||
|             .name = PARALLELS_OPT_PREALLOC_SIZE, | ||||
|             .type = QEMU_OPT_SIZE, | ||||
|             .help = "Preallocation size on image expansion", | ||||
|             .def_value_str = "128MiB", | ||||
|         }, | ||||
|         { | ||||
|             .name = PARALLELS_OPT_PREALLOC_MODE, | ||||
|             .type = QEMU_OPT_STRING, | ||||
|             .help = "Preallocation mode on image expansion " | ||||
|                     "(allowed values: falloc, truncate)", | ||||
|             .def_value_str = "falloc", | ||||
|         }, | ||||
|         { /* end of list */ }, | ||||
|     }, | ||||
| }; | ||||
|     const struct parallels_header *ph = (const void *)buf; | ||||
|  | ||||
|  | ||||
| static int64_t bat2sect(BDRVParallelsState *s, uint32_t idx) | ||||
| { | ||||
|     return (uint64_t)le32_to_cpu(s->bat_bitmap[idx]) * s->off_multiplier; | ||||
| } | ||||
|  | ||||
| static uint32_t bat_entry_off(uint32_t idx) | ||||
| { | ||||
|     return sizeof(ParallelsHeader) + sizeof(uint32_t) * idx; | ||||
| } | ||||
|  | ||||
| static int64_t seek_to_sector(BDRVParallelsState *s, int64_t sector_num) | ||||
| { | ||||
|     uint32_t index, offset; | ||||
|  | ||||
|     index = sector_num / s->tracks; | ||||
|     offset = sector_num % s->tracks; | ||||
|  | ||||
|     /* not allocated */ | ||||
|     if ((index >= s->bat_size) || (s->bat_bitmap[index] == 0)) { | ||||
|         return -1; | ||||
|     } | ||||
|     return bat2sect(s, index) + offset; | ||||
| } | ||||
|  | ||||
| static int cluster_remainder(BDRVParallelsState *s, int64_t sector_num, | ||||
|         int nb_sectors) | ||||
| { | ||||
|     int ret = s->tracks - sector_num % s->tracks; | ||||
|     return MIN(nb_sectors, ret); | ||||
| } | ||||
|  | ||||
| static int64_t block_status(BDRVParallelsState *s, int64_t sector_num, | ||||
|                             int nb_sectors, int *pnum) | ||||
| { | ||||
|     int64_t start_off = -2, prev_end_off = -2; | ||||
|  | ||||
|     *pnum = 0; | ||||
|     while (nb_sectors > 0 || start_off == -2) { | ||||
|         int64_t offset = seek_to_sector(s, sector_num); | ||||
|         int to_end; | ||||
|  | ||||
|         if (start_off == -2) { | ||||
|             start_off = offset; | ||||
|             prev_end_off = offset; | ||||
|         } else if (offset != prev_end_off) { | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         to_end = cluster_remainder(s, sector_num, nb_sectors); | ||||
|         nb_sectors -= to_end; | ||||
|         sector_num += to_end; | ||||
|         *pnum += to_end; | ||||
|  | ||||
|         if (offset > 0) { | ||||
|             prev_end_off += to_end; | ||||
|         } | ||||
|     } | ||||
|     return start_off; | ||||
| } | ||||
|  | ||||
| static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, | ||||
|                                  int nb_sectors, int *pnum) | ||||
| { | ||||
|     BDRVParallelsState *s = bs->opaque; | ||||
|     uint32_t idx, to_allocate, i; | ||||
|     int64_t pos, space; | ||||
|  | ||||
|     pos = block_status(s, sector_num, nb_sectors, pnum); | ||||
|     if (pos > 0) { | ||||
|         return pos; | ||||
|     } | ||||
|  | ||||
|     idx = sector_num / s->tracks; | ||||
|     if (idx >= s->bat_size) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     to_allocate = (sector_num + *pnum + s->tracks - 1) / s->tracks - idx; | ||||
|     space = to_allocate * s->tracks; | ||||
|     if (s->data_end + space > bdrv_getlength(bs->file->bs) >> BDRV_SECTOR_BITS) { | ||||
|         int ret; | ||||
|         space += s->prealloc_size; | ||||
|         if (s->prealloc_mode == PRL_PREALLOC_MODE_FALLOCATE) { | ||||
|             ret = bdrv_write_zeroes(bs->file->bs, s->data_end, space, 0); | ||||
|         } else { | ||||
|             ret = bdrv_truncate(bs->file->bs, | ||||
|                                 (s->data_end + space) << BDRV_SECTOR_BITS); | ||||
|         } | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     for (i = 0; i < to_allocate; i++) { | ||||
|         s->bat_bitmap[idx + i] = cpu_to_le32(s->data_end / s->off_multiplier); | ||||
|         s->data_end += s->tracks; | ||||
|         bitmap_set(s->bat_dirty_bmap, | ||||
|                    bat_entry_off(idx + i) / s->bat_dirty_block, 1); | ||||
|     } | ||||
|  | ||||
|     return bat2sect(s, idx) + sector_num % s->tracks; | ||||
| } | ||||
|  | ||||
|  | ||||
| static coroutine_fn int parallels_co_flush_to_os(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVParallelsState *s = bs->opaque; | ||||
|     unsigned long size = DIV_ROUND_UP(s->header_size, s->bat_dirty_block); | ||||
|     unsigned long bit; | ||||
|  | ||||
|     qemu_co_mutex_lock(&s->lock); | ||||
|  | ||||
|     bit = find_first_bit(s->bat_dirty_bmap, size); | ||||
|     while (bit < size) { | ||||
|         uint32_t off = bit * s->bat_dirty_block; | ||||
|         uint32_t to_write = s->bat_dirty_block; | ||||
|         int ret; | ||||
|  | ||||
|         if (off + to_write > s->header_size) { | ||||
|             to_write = s->header_size - off; | ||||
|         } | ||||
|         ret = bdrv_pwrite(bs->file->bs, off, (uint8_t *)s->header + off, | ||||
|                           to_write); | ||||
|         if (ret < 0) { | ||||
|             qemu_co_mutex_unlock(&s->lock); | ||||
|             return ret; | ||||
|         } | ||||
|         bit = find_next_bit(s->bat_dirty_bmap, size, bit + 1); | ||||
|     } | ||||
|     bitmap_zero(s->bat_dirty_bmap, size); | ||||
|  | ||||
|     qemu_co_mutex_unlock(&s->lock); | ||||
|     if (buf_size < HEADER_SIZE) | ||||
|         return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int64_t coroutine_fn parallels_co_get_block_status(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) | ||||
| { | ||||
|     BDRVParallelsState *s = bs->opaque; | ||||
|     int64_t offset; | ||||
|  | ||||
|     qemu_co_mutex_lock(&s->lock); | ||||
|     offset = block_status(s, sector_num, nb_sectors, pnum); | ||||
|     qemu_co_mutex_unlock(&s->lock); | ||||
|  | ||||
|     if (offset < 0) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     *file = bs->file->bs; | ||||
|     return (offset << BDRV_SECTOR_BITS) | | ||||
|         BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; | ||||
| } | ||||
|  | ||||
| static coroutine_fn int parallels_co_writev(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) | ||||
| { | ||||
|     BDRVParallelsState *s = bs->opaque; | ||||
|     uint64_t bytes_done = 0; | ||||
|     QEMUIOVector hd_qiov; | ||||
|     int ret = 0; | ||||
|  | ||||
|     qemu_iovec_init(&hd_qiov, qiov->niov); | ||||
|  | ||||
|     while (nb_sectors > 0) { | ||||
|         int64_t position; | ||||
|         int n, nbytes; | ||||
|  | ||||
|         qemu_co_mutex_lock(&s->lock); | ||||
|         position = allocate_clusters(bs, sector_num, nb_sectors, &n); | ||||
|         qemu_co_mutex_unlock(&s->lock); | ||||
|         if (position < 0) { | ||||
|             ret = (int)position; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         nbytes = n << BDRV_SECTOR_BITS; | ||||
|  | ||||
|         qemu_iovec_reset(&hd_qiov); | ||||
|         qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes); | ||||
|  | ||||
|         ret = bdrv_co_writev(bs->file->bs, position, n, &hd_qiov); | ||||
|         if (ret < 0) { | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         nb_sectors -= n; | ||||
|         sector_num += n; | ||||
|         bytes_done += nbytes; | ||||
|     } | ||||
|  | ||||
|     qemu_iovec_destroy(&hd_qiov); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static coroutine_fn int parallels_co_readv(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) | ||||
| { | ||||
|     BDRVParallelsState *s = bs->opaque; | ||||
|     uint64_t bytes_done = 0; | ||||
|     QEMUIOVector hd_qiov; | ||||
|     int ret = 0; | ||||
|  | ||||
|     qemu_iovec_init(&hd_qiov, qiov->niov); | ||||
|  | ||||
|     while (nb_sectors > 0) { | ||||
|         int64_t position; | ||||
|         int n, nbytes; | ||||
|  | ||||
|         qemu_co_mutex_lock(&s->lock); | ||||
|         position = block_status(s, sector_num, nb_sectors, &n); | ||||
|         qemu_co_mutex_unlock(&s->lock); | ||||
|  | ||||
|         nbytes = n << BDRV_SECTOR_BITS; | ||||
|  | ||||
|         if (position < 0) { | ||||
|             qemu_iovec_memset(qiov, bytes_done, 0, nbytes); | ||||
|         } else { | ||||
|             qemu_iovec_reset(&hd_qiov); | ||||
|             qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes); | ||||
|  | ||||
|             ret = bdrv_co_readv(bs->file->bs, position, n, &hd_qiov); | ||||
|             if (ret < 0) { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         nb_sectors -= n; | ||||
|         sector_num += n; | ||||
|         bytes_done += nbytes; | ||||
|     } | ||||
|  | ||||
|     qemu_iovec_destroy(&hd_qiov); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res, | ||||
|                            BdrvCheckMode fix) | ||||
| { | ||||
|     BDRVParallelsState *s = bs->opaque; | ||||
|     int64_t size, prev_off, high_off; | ||||
|     int ret; | ||||
|     uint32_t i; | ||||
|     bool flush_bat = false; | ||||
|     int cluster_size = s->tracks << BDRV_SECTOR_BITS; | ||||
|  | ||||
|     size = bdrv_getlength(bs->file->bs); | ||||
|     if (size < 0) { | ||||
|         res->check_errors++; | ||||
|         return size; | ||||
|     } | ||||
|  | ||||
|     if (s->header_unclean) { | ||||
|         fprintf(stderr, "%s image was not closed correctly\n", | ||||
|                 fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR"); | ||||
|         res->corruptions++; | ||||
|         if (fix & BDRV_FIX_ERRORS) { | ||||
|             /* parallels_close will do the job right */ | ||||
|             res->corruptions_fixed++; | ||||
|             s->header_unclean = false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     res->bfi.total_clusters = s->bat_size; | ||||
|     res->bfi.compressed_clusters = 0; /* compression is not supported */ | ||||
|  | ||||
|     high_off = 0; | ||||
|     prev_off = 0; | ||||
|     for (i = 0; i < s->bat_size; i++) { | ||||
|         int64_t off = bat2sect(s, i) << BDRV_SECTOR_BITS; | ||||
|         if (off == 0) { | ||||
|             prev_off = 0; | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         /* cluster outside the image */ | ||||
|         if (off > size) { | ||||
|             fprintf(stderr, "%s cluster %u is outside image\n", | ||||
|                     fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i); | ||||
|             res->corruptions++; | ||||
|             if (fix & BDRV_FIX_ERRORS) { | ||||
|                 prev_off = 0; | ||||
|                 s->bat_bitmap[i] = 0; | ||||
|                 res->corruptions_fixed++; | ||||
|                 flush_bat = true; | ||||
|                 continue; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         res->bfi.allocated_clusters++; | ||||
|         if (off > high_off) { | ||||
|             high_off = off; | ||||
|         } | ||||
|  | ||||
|         if (prev_off != 0 && (prev_off + cluster_size) != off) { | ||||
|             res->bfi.fragmented_clusters++; | ||||
|         } | ||||
|         prev_off = off; | ||||
|     } | ||||
|  | ||||
|     if (flush_bat) { | ||||
|         ret = bdrv_pwrite_sync(bs->file->bs, 0, s->header, s->header_size); | ||||
|         if (ret < 0) { | ||||
|             res->check_errors++; | ||||
|             return ret; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     res->image_end_offset = high_off + cluster_size; | ||||
|     if (size > res->image_end_offset) { | ||||
|         int64_t count; | ||||
|         count = DIV_ROUND_UP(size - res->image_end_offset, cluster_size); | ||||
|         fprintf(stderr, "%s space leaked at the end of the image %" PRId64 "\n", | ||||
|                 fix & BDRV_FIX_LEAKS ? "Repairing" : "ERROR", | ||||
|                 size - res->image_end_offset); | ||||
|         res->leaks += count; | ||||
|         if (fix & BDRV_FIX_LEAKS) { | ||||
|             ret = bdrv_truncate(bs->file->bs, res->image_end_offset); | ||||
|             if (ret < 0) { | ||||
|                 res->check_errors++; | ||||
|                 return ret; | ||||
|             } | ||||
|             res->leaks_fixed += count; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int parallels_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
| { | ||||
|     int64_t total_size, cl_size; | ||||
|     uint8_t tmp[BDRV_SECTOR_SIZE]; | ||||
|     Error *local_err = NULL; | ||||
|     BlockDriverState *file; | ||||
|     uint32_t bat_entries, bat_sectors; | ||||
|     ParallelsHeader header; | ||||
|     int ret; | ||||
|  | ||||
|     total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), | ||||
|                           BDRV_SECTOR_SIZE); | ||||
|     cl_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE, | ||||
|                           DEFAULT_CLUSTER_SIZE), BDRV_SECTOR_SIZE); | ||||
|  | ||||
|     ret = bdrv_create_file(filename, opts, &local_err); | ||||
|     if (ret < 0) { | ||||
|         error_propagate(errp, local_err); | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     file = NULL; | ||||
|     ret = bdrv_open(&file, filename, NULL, NULL, | ||||
|                     BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); | ||||
|     if (ret < 0) { | ||||
|         error_propagate(errp, local_err); | ||||
|         return ret; | ||||
|     } | ||||
|     ret = bdrv_truncate(file, 0); | ||||
|     if (ret < 0) { | ||||
|         goto exit; | ||||
|     } | ||||
|  | ||||
|     bat_entries = DIV_ROUND_UP(total_size, cl_size); | ||||
|     bat_sectors = DIV_ROUND_UP(bat_entry_off(bat_entries), cl_size); | ||||
|     bat_sectors = (bat_sectors *  cl_size) >> BDRV_SECTOR_BITS; | ||||
|  | ||||
|     memset(&header, 0, sizeof(header)); | ||||
|     memcpy(header.magic, HEADER_MAGIC2, sizeof(header.magic)); | ||||
|     header.version = cpu_to_le32(HEADER_VERSION); | ||||
|     /* don't care much about geometry, it is not used on image level */ | ||||
|     header.heads = cpu_to_le32(16); | ||||
|     header.cylinders = cpu_to_le32(total_size / BDRV_SECTOR_SIZE / 16 / 32); | ||||
|     header.tracks = cpu_to_le32(cl_size >> BDRV_SECTOR_BITS); | ||||
|     header.bat_entries = cpu_to_le32(bat_entries); | ||||
|     header.nb_sectors = cpu_to_le64(DIV_ROUND_UP(total_size, BDRV_SECTOR_SIZE)); | ||||
|     header.data_off = cpu_to_le32(bat_sectors); | ||||
|  | ||||
|     /* write all the data */ | ||||
|     memset(tmp, 0, sizeof(tmp)); | ||||
|     memcpy(tmp, &header, sizeof(header)); | ||||
|  | ||||
|     ret = bdrv_pwrite(file, 0, tmp, BDRV_SECTOR_SIZE); | ||||
|     if (ret < 0) { | ||||
|         goto exit; | ||||
|     } | ||||
|     ret = bdrv_write_zeroes(file, 1, bat_sectors - 1, 0); | ||||
|     if (ret < 0) { | ||||
|         goto exit; | ||||
|     } | ||||
|     ret = 0; | ||||
|  | ||||
| done: | ||||
|     bdrv_unref(file); | ||||
|     return ret; | ||||
|  | ||||
| exit: | ||||
|     error_setg_errno(errp, -ret, "Failed to create Parallels image"); | ||||
|     goto done; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int parallels_probe(const uint8_t *buf, int buf_size, | ||||
|                            const char *filename) | ||||
| { | ||||
|     const ParallelsHeader *ph = (const void *)buf; | ||||
|  | ||||
|     if (buf_size < sizeof(ParallelsHeader)) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if ((!memcmp(ph->magic, HEADER_MAGIC, 16) || | ||||
|         !memcmp(ph->magic, HEADER_MAGIC2, 16)) && | ||||
|            (le32_to_cpu(ph->version) == HEADER_VERSION)) { | ||||
|         (le32_to_cpu(ph->version) == HEADER_VERSION)) | ||||
|         return 100; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int parallels_update_header(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVParallelsState *s = bs->opaque; | ||||
|     unsigned size = MAX(bdrv_opt_mem_align(bs->file->bs), | ||||
|                         sizeof(ParallelsHeader)); | ||||
|  | ||||
|     if (size > s->header_size) { | ||||
|         size = s->header_size; | ||||
|     } | ||||
|     return bdrv_pwrite_sync(bs->file->bs, 0, s->header, size); | ||||
| } | ||||
|  | ||||
| static int parallels_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                           Error **errp) | ||||
| { | ||||
|     BDRVParallelsState *s = bs->opaque; | ||||
|     ParallelsHeader ph; | ||||
|     int ret, size, i; | ||||
|     QemuOpts *opts = NULL; | ||||
|     Error *local_err = NULL; | ||||
|     char *buf; | ||||
|     int i; | ||||
|     struct parallels_header ph; | ||||
|     int ret; | ||||
|  | ||||
|     ret = bdrv_pread(bs->file->bs, 0, &ph, sizeof(ph)); | ||||
|     bs->read_only = 1; // no write support yet | ||||
|  | ||||
|     ret = bdrv_pread(bs->file, 0, &ph, sizeof(ph)); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
| @@ -599,90 +115,25 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     s->bat_size = le32_to_cpu(ph.bat_entries); | ||||
|     if (s->bat_size > INT_MAX / sizeof(uint32_t)) { | ||||
|     s->catalog_size = le32_to_cpu(ph.catalog_entries); | ||||
|     if (s->catalog_size > INT_MAX / 4) { | ||||
|         error_setg(errp, "Catalog too large"); | ||||
|         ret = -EFBIG; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     size = bat_entry_off(s->bat_size); | ||||
|     s->header_size = ROUND_UP(size, bdrv_opt_mem_align(bs->file->bs)); | ||||
|     s->header = qemu_try_blockalign(bs->file->bs, s->header_size); | ||||
|     if (s->header == NULL) { | ||||
|     s->catalog_bitmap = g_try_new(uint32_t, s->catalog_size); | ||||
|     if (s->catalog_size && s->catalog_bitmap == NULL) { | ||||
|         ret = -ENOMEM; | ||||
|         goto fail; | ||||
|     } | ||||
|     s->data_end = le32_to_cpu(ph.data_off); | ||||
|     if (s->data_end == 0) { | ||||
|         s->data_end = ROUND_UP(bat_entry_off(s->bat_size), BDRV_SECTOR_SIZE); | ||||
|     } | ||||
|     if (s->data_end < s->header_size) { | ||||
|         /* there is not enough unused space to fit to block align between BAT | ||||
|            and actual data. We can't avoid read-modify-write... */ | ||||
|         s->header_size = size; | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_pread(bs->file->bs, 0, s->header, s->header_size); | ||||
|     ret = bdrv_pread(bs->file, 64, s->catalog_bitmap, s->catalog_size * 4); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|     s->bat_bitmap = (uint32_t *)(s->header + 1); | ||||
|  | ||||
|     for (i = 0; i < s->bat_size; i++) { | ||||
|         int64_t off = bat2sect(s, i); | ||||
|         if (off >= s->data_end) { | ||||
|             s->data_end = off + s->tracks; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (le32_to_cpu(ph.inuse) == HEADER_INUSE_MAGIC) { | ||||
|         /* Image was not closed correctly. The check is mandatory */ | ||||
|         s->header_unclean = true; | ||||
|         if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_CHECK)) { | ||||
|             error_setg(errp, "parallels: Image was not closed correctly; " | ||||
|                        "cannot be opened read/write"); | ||||
|             ret = -EACCES; | ||||
|             goto fail; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     opts = qemu_opts_create(¶llels_runtime_opts, NULL, 0, &local_err); | ||||
|     if (local_err != NULL) { | ||||
|         goto fail_options; | ||||
|     } | ||||
|  | ||||
|     qemu_opts_absorb_qdict(opts, options, &local_err); | ||||
|     if (local_err != NULL) { | ||||
|         goto fail_options; | ||||
|     } | ||||
|  | ||||
|     s->prealloc_size = | ||||
|         qemu_opt_get_size_del(opts, PARALLELS_OPT_PREALLOC_SIZE, 0); | ||||
|     s->prealloc_size = MAX(s->tracks, s->prealloc_size >> BDRV_SECTOR_BITS); | ||||
|     buf = qemu_opt_get_del(opts, PARALLELS_OPT_PREALLOC_MODE); | ||||
|     s->prealloc_mode = qapi_enum_parse(prealloc_mode_lookup, buf, | ||||
|             PRL_PREALLOC_MODE__MAX, PRL_PREALLOC_MODE_FALLOCATE, &local_err); | ||||
|     g_free(buf); | ||||
|     if (local_err != NULL) { | ||||
|         goto fail_options; | ||||
|     } | ||||
|     if (!bdrv_has_zero_init(bs->file->bs) || | ||||
|             bdrv_truncate(bs->file->bs, bdrv_getlength(bs->file->bs)) != 0) { | ||||
|         s->prealloc_mode = PRL_PREALLOC_MODE_FALLOCATE; | ||||
|     } | ||||
|  | ||||
|     if (flags & BDRV_O_RDWR) { | ||||
|         s->header->inuse = cpu_to_le32(HEADER_INUSE_MAGIC); | ||||
|         ret = parallels_update_header(bs); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     s->bat_dirty_block = 4 * getpagesize(); | ||||
|     s->bat_dirty_bmap = | ||||
|         bitmap_new(DIV_ROUND_UP(s->header_size, s->bat_dirty_block)); | ||||
|     for (i = 0; i < s->catalog_size; i++) | ||||
|         le32_to_cpus(&s->catalog_bitmap[i]); | ||||
|  | ||||
|     qemu_co_mutex_init(&s->lock); | ||||
|     return 0; | ||||
| @@ -691,67 +142,67 @@ fail_format: | ||||
|     error_setg(errp, "Image not in Parallels format"); | ||||
|     ret = -EINVAL; | ||||
| fail: | ||||
|     qemu_vfree(s->header); | ||||
|     g_free(s->catalog_bitmap); | ||||
|     return ret; | ||||
|  | ||||
| fail_options: | ||||
|     error_propagate(errp, local_err); | ||||
|     ret = -EINVAL; | ||||
|     goto fail; | ||||
| } | ||||
|  | ||||
| static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) | ||||
| { | ||||
|     BDRVParallelsState *s = bs->opaque; | ||||
|     uint32_t index, offset; | ||||
|  | ||||
|     index = sector_num / s->tracks; | ||||
|     offset = sector_num % s->tracks; | ||||
|  | ||||
|     /* not allocated */ | ||||
|     if ((index >= s->catalog_size) || (s->catalog_bitmap[index] == 0)) | ||||
|         return -1; | ||||
|     return | ||||
|         ((uint64_t)s->catalog_bitmap[index] * s->off_multiplier + offset) * 512; | ||||
| } | ||||
|  | ||||
| static int parallels_read(BlockDriverState *bs, int64_t sector_num, | ||||
|                     uint8_t *buf, int nb_sectors) | ||||
| { | ||||
|     while (nb_sectors > 0) { | ||||
|         int64_t position = seek_to_sector(bs, sector_num); | ||||
|         if (position >= 0) { | ||||
|             if (bdrv_pread(bs->file, position, buf, 512) != 512) | ||||
|                 return -1; | ||||
|         } else { | ||||
|             memset(buf, 0, 512); | ||||
|         } | ||||
|         nb_sectors--; | ||||
|         sector_num++; | ||||
|         buf += 512; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static coroutine_fn int parallels_co_read(BlockDriverState *bs, int64_t sector_num, | ||||
|                                           uint8_t *buf, int nb_sectors) | ||||
| { | ||||
|     int ret; | ||||
|     BDRVParallelsState *s = bs->opaque; | ||||
|     qemu_co_mutex_lock(&s->lock); | ||||
|     ret = parallels_read(bs, sector_num, buf, nb_sectors); | ||||
|     qemu_co_mutex_unlock(&s->lock); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static void parallels_close(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVParallelsState *s = bs->opaque; | ||||
|  | ||||
|     if (bs->open_flags & BDRV_O_RDWR) { | ||||
|         s->header->inuse = 0; | ||||
|         parallels_update_header(bs); | ||||
|     g_free(s->catalog_bitmap); | ||||
| } | ||||
|  | ||||
|     if (bs->open_flags & BDRV_O_RDWR) { | ||||
|         bdrv_truncate(bs->file->bs, s->data_end << BDRV_SECTOR_BITS); | ||||
|     } | ||||
|  | ||||
|     g_free(s->bat_dirty_bmap); | ||||
|     qemu_vfree(s->header); | ||||
| } | ||||
|  | ||||
| static QemuOptsList parallels_create_opts = { | ||||
|     .name = "parallels-create-opts", | ||||
|     .head = QTAILQ_HEAD_INITIALIZER(parallels_create_opts.head), | ||||
|     .desc = { | ||||
|         { | ||||
|             .name = BLOCK_OPT_SIZE, | ||||
|             .type = QEMU_OPT_SIZE, | ||||
|             .help = "Virtual disk size", | ||||
|         }, | ||||
|         { | ||||
|             .name = BLOCK_OPT_CLUSTER_SIZE, | ||||
|             .type = QEMU_OPT_SIZE, | ||||
|             .help = "Parallels image cluster size", | ||||
|             .def_value_str = stringify(DEFAULT_CLUSTER_SIZE), | ||||
|         }, | ||||
|         { /* end of list */ } | ||||
|     } | ||||
| }; | ||||
|  | ||||
| static BlockDriver bdrv_parallels = { | ||||
|     .format_name	= "parallels", | ||||
|     .instance_size	= sizeof(BDRVParallelsState), | ||||
|     .bdrv_probe		= parallels_probe, | ||||
|     .bdrv_open		= parallels_open, | ||||
|     .bdrv_read          = parallels_co_read, | ||||
|     .bdrv_close		= parallels_close, | ||||
|     .bdrv_co_get_block_status = parallels_co_get_block_status, | ||||
|     .bdrv_has_zero_init       = bdrv_has_zero_init_1, | ||||
|     .bdrv_co_flush_to_os      = parallels_co_flush_to_os, | ||||
|     .bdrv_co_readv  = parallels_co_readv, | ||||
|     .bdrv_co_writev = parallels_co_writev, | ||||
|  | ||||
|     .bdrv_create    = parallels_create, | ||||
|     .bdrv_check     = parallels_check, | ||||
|     .create_opts    = ¶llels_create_opts, | ||||
| }; | ||||
|  | ||||
| static void bdrv_parallels_init(void) | ||||
|   | ||||
							
								
								
									
										224
									
								
								block/qapi.c
									
									
									
									
									
								
							
							
						
						
									
										224
									
								
								block/qapi.c
									
									
									
									
									
								
							| @@ -22,10 +22,8 @@ | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "block/qapi.h" | ||||
| #include "block/block_int.h" | ||||
| #include "block/throttle-groups.h" | ||||
| #include "block/write-threshold.h" | ||||
| #include "qmp-commands.h" | ||||
| #include "qapi-visit.h" | ||||
| @@ -33,10 +31,8 @@ | ||||
| #include "qapi/qmp/types.h" | ||||
| #include "sysemu/block-backend.h" | ||||
|  | ||||
| BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp) | ||||
| BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs) | ||||
| { | ||||
|     ImageInfo **p_image_info; | ||||
|     BlockDriverState *bs0; | ||||
|     BlockDeviceInfo *info = g_malloc0(sizeof(*info)); | ||||
|  | ||||
|     info->file                   = g_strdup(bs->filename); | ||||
| @@ -65,11 +61,9 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp) | ||||
|     info->backing_file_depth = bdrv_get_backing_file_depth(bs); | ||||
|     info->detect_zeroes = bs->detect_zeroes; | ||||
|  | ||||
|     if (bs->throttle_state) { | ||||
|     if (bs->io_limits_enabled) { | ||||
|         ThrottleConfig cfg; | ||||
|  | ||||
|         throttle_group_get_config(bs, &cfg); | ||||
|  | ||||
|         throttle_get_config(&bs->throttle_state, &cfg); | ||||
|         info->bps     = cfg.buckets[THROTTLE_BPS_TOTAL].avg; | ||||
|         info->bps_rd  = cfg.buckets[THROTTLE_BPS_READ].avg; | ||||
|         info->bps_wr  = cfg.buckets[THROTTLE_BPS_WRITE].avg; | ||||
| @@ -92,54 +86,12 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp) | ||||
|         info->has_iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max; | ||||
|         info->iops_wr_max     = cfg.buckets[THROTTLE_OPS_WRITE].max; | ||||
|  | ||||
|         info->has_bps_max_length     = info->has_bps_max; | ||||
|         info->bps_max_length         = | ||||
|             cfg.buckets[THROTTLE_BPS_TOTAL].burst_length; | ||||
|         info->has_bps_rd_max_length  = info->has_bps_rd_max; | ||||
|         info->bps_rd_max_length      = | ||||
|             cfg.buckets[THROTTLE_BPS_READ].burst_length; | ||||
|         info->has_bps_wr_max_length  = info->has_bps_wr_max; | ||||
|         info->bps_wr_max_length      = | ||||
|             cfg.buckets[THROTTLE_BPS_WRITE].burst_length; | ||||
|  | ||||
|         info->has_iops_max_length    = info->has_iops_max; | ||||
|         info->iops_max_length        = | ||||
|             cfg.buckets[THROTTLE_OPS_TOTAL].burst_length; | ||||
|         info->has_iops_rd_max_length = info->has_iops_rd_max; | ||||
|         info->iops_rd_max_length     = | ||||
|             cfg.buckets[THROTTLE_OPS_READ].burst_length; | ||||
|         info->has_iops_wr_max_length = info->has_iops_wr_max; | ||||
|         info->iops_wr_max_length     = | ||||
|             cfg.buckets[THROTTLE_OPS_WRITE].burst_length; | ||||
|  | ||||
|         info->has_iops_size = cfg.op_size; | ||||
|         info->iops_size = cfg.op_size; | ||||
|  | ||||
|         info->has_group = true; | ||||
|         info->group = g_strdup(throttle_group_get_name(bs)); | ||||
|     } | ||||
|  | ||||
|     info->write_threshold = bdrv_write_threshold_get(bs); | ||||
|  | ||||
|     bs0 = bs; | ||||
|     p_image_info = &info->image; | ||||
|     while (1) { | ||||
|         Error *local_err = NULL; | ||||
|         bdrv_query_image_info(bs0, p_image_info, &local_err); | ||||
|         if (local_err) { | ||||
|             error_propagate(errp, local_err); | ||||
|             qapi_free_BlockDeviceInfo(info); | ||||
|             return NULL; | ||||
|         } | ||||
|         if (bs0->drv && bs0->backing) { | ||||
|             bs0 = bs0->backing->bs; | ||||
|             (*p_image_info)->has_backing_image = true; | ||||
|             p_image_info = &((*p_image_info)->backing_image); | ||||
|         } else { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return info; | ||||
| } | ||||
|  | ||||
| @@ -231,13 +183,11 @@ void bdrv_query_image_info(BlockDriverState *bs, | ||||
|     Error *err = NULL; | ||||
|     ImageInfo *info; | ||||
|  | ||||
|     aio_context_acquire(bdrv_get_aio_context(bs)); | ||||
|  | ||||
|     size = bdrv_getlength(bs); | ||||
|     if (size < 0) { | ||||
|         error_setg_errno(errp, -size, "Can't get size of device '%s'", | ||||
|                          bdrv_get_device_name(bs)); | ||||
|         goto out; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     info = g_new0(ImageInfo, 1); | ||||
| @@ -268,18 +218,15 @@ void bdrv_query_image_info(BlockDriverState *bs, | ||||
|         info->has_backing_filename = true; | ||||
|         bdrv_get_full_backing_filename(bs, backing_filename2, PATH_MAX, &err); | ||||
|         if (err) { | ||||
|             /* Can't reconstruct the full backing filename, so we must omit | ||||
|              * this field and apply a Best Effort to this query. */ | ||||
|             error_propagate(errp, err); | ||||
|             qapi_free_ImageInfo(info); | ||||
|             g_free(backing_filename2); | ||||
|             backing_filename2 = NULL; | ||||
|             error_free(err); | ||||
|             err = NULL; | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /* Always report the full_backing_filename if present, even if it's the | ||||
|          * same as backing_filename. That they are same is useful info. */ | ||||
|         if (backing_filename2) { | ||||
|             info->full_backing_filename = g_strdup(backing_filename2); | ||||
|         if (strcmp(backing_filename, backing_filename2) != 0) { | ||||
|             info->full_backing_filename = | ||||
|                         g_strdup(backing_filename2); | ||||
|             info->has_full_backing_filename = true; | ||||
|         } | ||||
|  | ||||
| @@ -305,13 +252,10 @@ void bdrv_query_image_info(BlockDriverState *bs, | ||||
|     default: | ||||
|         error_propagate(errp, err); | ||||
|         qapi_free_ImageInfo(info); | ||||
|         goto out; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     *p_info = info; | ||||
|  | ||||
| out: | ||||
|     aio_context_release(bdrv_get_aio_context(bs)); | ||||
| } | ||||
|  | ||||
| /* @p_info will be set only on success. */ | ||||
| @@ -320,32 +264,49 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, | ||||
| { | ||||
|     BlockInfo *info = g_malloc0(sizeof(*info)); | ||||
|     BlockDriverState *bs = blk_bs(blk); | ||||
|     BlockDriverState *bs0; | ||||
|     ImageInfo **p_image_info; | ||||
|     Error *local_err = NULL; | ||||
|     info->device = g_strdup(blk_name(blk)); | ||||
|     info->type = g_strdup("unknown"); | ||||
|     info->locked = blk_dev_is_medium_locked(blk); | ||||
|     info->removable = blk_dev_has_removable_media(blk); | ||||
|  | ||||
|     if (blk_dev_has_tray(blk)) { | ||||
|     if (blk_dev_has_removable_media(blk)) { | ||||
|         info->has_tray_open = true; | ||||
|         info->tray_open = blk_dev_is_tray_open(blk); | ||||
|     } | ||||
|  | ||||
|     if (blk_iostatus_is_enabled(blk)) { | ||||
|     if (bdrv_iostatus_is_enabled(bs)) { | ||||
|         info->has_io_status = true; | ||||
|         info->io_status = blk_iostatus(blk); | ||||
|         info->io_status = bs->iostatus; | ||||
|     } | ||||
|  | ||||
|     if (bs && !QLIST_EMPTY(&bs->dirty_bitmaps)) { | ||||
|     if (!QLIST_EMPTY(&bs->dirty_bitmaps)) { | ||||
|         info->has_dirty_bitmaps = true; | ||||
|         info->dirty_bitmaps = bdrv_query_dirty_bitmaps(bs); | ||||
|     } | ||||
|  | ||||
|     if (bs && bs->drv) { | ||||
|     if (bs->drv) { | ||||
|         info->has_inserted = true; | ||||
|         info->inserted = bdrv_block_device_info(bs, errp); | ||||
|         if (info->inserted == NULL) { | ||||
|         info->inserted = bdrv_block_device_info(bs); | ||||
|  | ||||
|         bs0 = bs; | ||||
|         p_image_info = &info->inserted->image; | ||||
|         while (1) { | ||||
|             bdrv_query_image_info(bs0, p_image_info, &local_err); | ||||
|             if (local_err) { | ||||
|                 error_propagate(errp, local_err); | ||||
|                 goto err; | ||||
|             } | ||||
|             if (bs0->drv && bs0->backing_hd) { | ||||
|                 bs0 = bs0->backing_hd; | ||||
|                 (*p_image_info)->has_backing_image = true; | ||||
|                 p_image_info = &((*p_image_info)->backing_image); | ||||
|             } else { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     *p_info = info; | ||||
| @@ -373,82 +334,27 @@ static BlockStats *bdrv_query_stats(const BlockDriverState *bs, | ||||
|     } | ||||
|  | ||||
|     s->stats = g_malloc0(sizeof(*s->stats)); | ||||
|     if (bs->blk) { | ||||
|         BlockAcctStats *stats = blk_get_stats(bs->blk); | ||||
|         BlockAcctTimedStats *ts = NULL; | ||||
|  | ||||
|         s->stats->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ]; | ||||
|         s->stats->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE]; | ||||
|         s->stats->rd_operations = stats->nr_ops[BLOCK_ACCT_READ]; | ||||
|         s->stats->wr_operations = stats->nr_ops[BLOCK_ACCT_WRITE]; | ||||
|  | ||||
|         s->stats->failed_rd_operations = stats->failed_ops[BLOCK_ACCT_READ]; | ||||
|         s->stats->failed_wr_operations = stats->failed_ops[BLOCK_ACCT_WRITE]; | ||||
|         s->stats->failed_flush_operations = stats->failed_ops[BLOCK_ACCT_FLUSH]; | ||||
|  | ||||
|         s->stats->invalid_rd_operations = stats->invalid_ops[BLOCK_ACCT_READ]; | ||||
|         s->stats->invalid_wr_operations = stats->invalid_ops[BLOCK_ACCT_WRITE]; | ||||
|         s->stats->invalid_flush_operations = | ||||
|             stats->invalid_ops[BLOCK_ACCT_FLUSH]; | ||||
|  | ||||
|         s->stats->rd_merged = stats->merged[BLOCK_ACCT_READ]; | ||||
|         s->stats->wr_merged = stats->merged[BLOCK_ACCT_WRITE]; | ||||
|         s->stats->flush_operations = stats->nr_ops[BLOCK_ACCT_FLUSH]; | ||||
|         s->stats->wr_total_time_ns = stats->total_time_ns[BLOCK_ACCT_WRITE]; | ||||
|         s->stats->rd_total_time_ns = stats->total_time_ns[BLOCK_ACCT_READ]; | ||||
|         s->stats->flush_total_time_ns = stats->total_time_ns[BLOCK_ACCT_FLUSH]; | ||||
|  | ||||
|         s->stats->has_idle_time_ns = stats->last_access_time_ns > 0; | ||||
|         if (s->stats->has_idle_time_ns) { | ||||
|             s->stats->idle_time_ns = block_acct_idle_time_ns(stats); | ||||
|         } | ||||
|  | ||||
|         s->stats->account_invalid = stats->account_invalid; | ||||
|         s->stats->account_failed = stats->account_failed; | ||||
|  | ||||
|         while ((ts = block_acct_interval_next(stats, ts))) { | ||||
|             BlockDeviceTimedStatsList *timed_stats = | ||||
|                 g_malloc0(sizeof(*timed_stats)); | ||||
|             BlockDeviceTimedStats *dev_stats = g_malloc0(sizeof(*dev_stats)); | ||||
|             timed_stats->next = s->stats->timed_stats; | ||||
|             timed_stats->value = dev_stats; | ||||
|             s->stats->timed_stats = timed_stats; | ||||
|  | ||||
|             TimedAverage *rd = &ts->latency[BLOCK_ACCT_READ]; | ||||
|             TimedAverage *wr = &ts->latency[BLOCK_ACCT_WRITE]; | ||||
|             TimedAverage *fl = &ts->latency[BLOCK_ACCT_FLUSH]; | ||||
|  | ||||
|             dev_stats->interval_length = ts->interval_length; | ||||
|  | ||||
|             dev_stats->min_rd_latency_ns = timed_average_min(rd); | ||||
|             dev_stats->max_rd_latency_ns = timed_average_max(rd); | ||||
|             dev_stats->avg_rd_latency_ns = timed_average_avg(rd); | ||||
|  | ||||
|             dev_stats->min_wr_latency_ns = timed_average_min(wr); | ||||
|             dev_stats->max_wr_latency_ns = timed_average_max(wr); | ||||
|             dev_stats->avg_wr_latency_ns = timed_average_avg(wr); | ||||
|  | ||||
|             dev_stats->min_flush_latency_ns = timed_average_min(fl); | ||||
|             dev_stats->max_flush_latency_ns = timed_average_max(fl); | ||||
|             dev_stats->avg_flush_latency_ns = timed_average_avg(fl); | ||||
|  | ||||
|             dev_stats->avg_rd_queue_depth = | ||||
|                 block_acct_queue_depth(ts, BLOCK_ACCT_READ); | ||||
|             dev_stats->avg_wr_queue_depth = | ||||
|                 block_acct_queue_depth(ts, BLOCK_ACCT_WRITE); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     s->stats->wr_highest_offset = bs->wr_highest_offset; | ||||
|     s->stats->rd_bytes = bs->stats.nr_bytes[BLOCK_ACCT_READ]; | ||||
|     s->stats->wr_bytes = bs->stats.nr_bytes[BLOCK_ACCT_WRITE]; | ||||
|     s->stats->rd_operations = bs->stats.nr_ops[BLOCK_ACCT_READ]; | ||||
|     s->stats->wr_operations = bs->stats.nr_ops[BLOCK_ACCT_WRITE]; | ||||
|     s->stats->rd_merged = bs->stats.merged[BLOCK_ACCT_READ]; | ||||
|     s->stats->wr_merged = bs->stats.merged[BLOCK_ACCT_WRITE]; | ||||
|     s->stats->wr_highest_offset = | ||||
|         bs->stats.wr_highest_sector * BDRV_SECTOR_SIZE; | ||||
|     s->stats->flush_operations = bs->stats.nr_ops[BLOCK_ACCT_FLUSH]; | ||||
|     s->stats->wr_total_time_ns = bs->stats.total_time_ns[BLOCK_ACCT_WRITE]; | ||||
|     s->stats->rd_total_time_ns = bs->stats.total_time_ns[BLOCK_ACCT_READ]; | ||||
|     s->stats->flush_total_time_ns = bs->stats.total_time_ns[BLOCK_ACCT_FLUSH]; | ||||
|  | ||||
|     if (bs->file) { | ||||
|         s->has_parent = true; | ||||
|         s->parent = bdrv_query_stats(bs->file->bs, query_backing); | ||||
|         s->parent = bdrv_query_stats(bs->file, query_backing); | ||||
|     } | ||||
|  | ||||
|     if (query_backing && bs->backing) { | ||||
|     if (query_backing && bs->backing_hd) { | ||||
|         s->has_backing = true; | ||||
|         s->backing = bdrv_query_stats(bs->backing->bs, query_backing); | ||||
|         s->backing = bdrv_query_stats(bs->backing_hd, query_backing); | ||||
|     } | ||||
|  | ||||
|     return s; | ||||
| @@ -465,9 +371,7 @@ BlockInfoList *qmp_query_block(Error **errp) | ||||
|         bdrv_query_info(blk, &info->value, &local_err); | ||||
|         if (local_err) { | ||||
|             error_propagate(errp, local_err); | ||||
|             g_free(info); | ||||
|             qapi_free_BlockInfoList(head); | ||||
|             return NULL; | ||||
|             goto err; | ||||
|         } | ||||
|  | ||||
|         *p_next = info; | ||||
| @@ -475,6 +379,10 @@ BlockInfoList *qmp_query_block(Error **errp) | ||||
|     } | ||||
|  | ||||
|     return head; | ||||
|  | ||||
|  err: | ||||
|     qapi_free_BlockInfoList(head); | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| BlockStatsList *qmp_query_blockstats(bool has_query_nodes, | ||||
| @@ -602,9 +510,18 @@ static void dump_qobject(fprintf_function func_fprintf, void *f, | ||||
|         } | ||||
|         case QTYPE_QBOOL: { | ||||
|             QBool *value = qobject_to_qbool(obj); | ||||
|             func_fprintf(f, "%s", qbool_get_bool(value) ? "true" : "false"); | ||||
|             func_fprintf(f, "%s", qbool_get_int(value) ? "true" : "false"); | ||||
|             break; | ||||
|         } | ||||
|         case QTYPE_QERROR: { | ||||
|             QString *value = qerror_human((QError *)obj); | ||||
|             func_fprintf(f, "%s", qstring_get_str(value)); | ||||
|             QDECREF(value); | ||||
|             break; | ||||
|         } | ||||
|         case QTYPE_NONE: | ||||
|             break; | ||||
|         case QTYPE_MAX: | ||||
|         default: | ||||
|             abort(); | ||||
|     } | ||||
| @@ -617,7 +534,7 @@ static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation, | ||||
|     int i = 0; | ||||
|  | ||||
|     for (entry = qlist_first(list); entry; entry = qlist_next(entry), i++) { | ||||
|         QType type = qobject_type(entry->value); | ||||
|         qtype_code type = qobject_type(entry->value); | ||||
|         bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST); | ||||
|         const char *format = composite ? "%*s[%i]:\n" : "%*s[%i]: "; | ||||
|  | ||||
| @@ -635,7 +552,7 @@ static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation, | ||||
|     const QDictEntry *entry; | ||||
|  | ||||
|     for (entry = qdict_first(dict); entry; entry = qdict_next(dict, entry)) { | ||||
|         QType type = qobject_type(entry->value); | ||||
|         qtype_code type = qobject_type(entry->value); | ||||
|         bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST); | ||||
|         const char *format = composite ? "%*s%s:\n" : "%*s%s: "; | ||||
|         char key[strlen(entry->key) + 1]; | ||||
| @@ -661,7 +578,7 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f, | ||||
|     QmpOutputVisitor *ov = qmp_output_visitor_new(); | ||||
|     QObject *obj, *data; | ||||
|  | ||||
|     visit_type_ImageInfoSpecific(qmp_output_get_visitor(ov), NULL, &info_spec, | ||||
|     visit_type_ImageInfoSpecific(qmp_output_get_visitor(ov), &info_spec, NULL, | ||||
|                                  &error_abort); | ||||
|     obj = qmp_output_get_qobject(ov); | ||||
|     assert(qobject_type(obj) == QTYPE_QDICT); | ||||
| @@ -705,10 +622,7 @@ void bdrv_image_info_dump(fprintf_function func_fprintf, void *f, | ||||
|  | ||||
|     if (info->has_backing_filename) { | ||||
|         func_fprintf(f, "backing file: %s", info->backing_filename); | ||||
|         if (!info->has_full_backing_filename) { | ||||
|             func_fprintf(f, " (cannot determine actual path)"); | ||||
|         } else if (strcmp(info->backing_filename, | ||||
|                           info->full_backing_filename) != 0) { | ||||
|         if (info->has_full_backing_filename) { | ||||
|             func_fprintf(f, " (actual path: %s)", info->full_backing_filename); | ||||
|         } | ||||
|         func_fprintf(f, "\n"); | ||||
|   | ||||
							
								
								
									
										166
									
								
								block/qcow.c
									
									
									
									
									
								
							
							
						
						
									
										166
									
								
								block/qcow.c
									
									
									
									
									
								
							| @@ -21,13 +21,11 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "block/block_int.h" | ||||
| #include "qemu/module.h" | ||||
| #include <zlib.h> | ||||
| #include "qapi/qmp/qerror.h" | ||||
| #include "crypto/cipher.h" | ||||
| #include "qemu/aes.h" | ||||
| #include "migration/migration.h" | ||||
|  | ||||
| /**************************************************************/ | ||||
| @@ -73,8 +71,10 @@ typedef struct BDRVQcowState { | ||||
|     uint8_t *cluster_cache; | ||||
|     uint8_t *cluster_data; | ||||
|     uint64_t cluster_cache_offset; | ||||
|     QCryptoCipher *cipher; /* NULL if no key yet */ | ||||
|     uint32_t crypt_method; /* current crypt method, 0 if no key yet */ | ||||
|     uint32_t crypt_method_header; | ||||
|     AES_KEY aes_encrypt_key; | ||||
|     AES_KEY aes_decrypt_key; | ||||
|     CoMutex lock; | ||||
|     Error *migration_blocker; | ||||
| } BDRVQcowState; | ||||
| @@ -101,7 +101,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     int ret; | ||||
|     QCowHeader header; | ||||
|  | ||||
|     ret = bdrv_pread(bs->file->bs, 0, &header, sizeof(header)); | ||||
|     ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
| @@ -123,8 +123,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         char version[64]; | ||||
|         snprintf(version, sizeof(version), "QCOW version %" PRIu32, | ||||
|                  header.version); | ||||
|         error_setg(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, | ||||
|                    bdrv_get_device_or_node_name(bs), "qcow", version); | ||||
|         error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, | ||||
|                   bdrv_get_device_name(bs), "qcow", version); | ||||
|         ret = -ENOTSUP; | ||||
|         goto fail; | ||||
|     } | ||||
| @@ -153,11 +153,6 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|     } | ||||
|     if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) { | ||||
|         error_setg(errp, "AES cipher not available"); | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|     } | ||||
|     s->crypt_method_header = header.crypt_method; | ||||
|     if (s->crypt_method_header) { | ||||
|         bs->encrypted = 1; | ||||
| @@ -194,7 +189,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_pread(bs->file->bs, s->l1_table_offset, s->l1_table, | ||||
|     ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, | ||||
|                s->l1_size * sizeof(uint64_t)); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
| @@ -206,7 +201,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|  | ||||
|     /* alloc L2 cache (max. 64k * 16 * 8 = 8 MB) */ | ||||
|     s->l2_cache = | ||||
|         qemu_try_blockalign(bs->file->bs, | ||||
|         qemu_try_blockalign(bs->file, | ||||
|                             s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t)); | ||||
|     if (s->l2_cache == NULL) { | ||||
|         error_setg(errp, "Could not allocate L2 table cache"); | ||||
| @@ -225,7 +220,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|             ret = -EINVAL; | ||||
|             goto fail; | ||||
|         } | ||||
|         ret = bdrv_pread(bs->file->bs, header.backing_file_offset, | ||||
|         ret = bdrv_pread(bs->file, header.backing_file_offset, | ||||
|                    bs->backing_file, len); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
| @@ -234,9 +229,9 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     } | ||||
|  | ||||
|     /* Disable migration when qcow images are used */ | ||||
|     error_setg(&s->migration_blocker, "The qcow format used by node '%s' " | ||||
|                "does not support live migration", | ||||
|                bdrv_get_device_or_node_name(bs)); | ||||
|     error_set(&s->migration_blocker, | ||||
|               QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, | ||||
|               "qcow", bdrv_get_device_name(bs), "live migration"); | ||||
|     migrate_add_blocker(s->migration_blocker); | ||||
|  | ||||
|     qemu_co_mutex_init(&s->lock); | ||||
| @@ -264,7 +259,6 @@ static int qcow_set_key(BlockDriverState *bs, const char *key) | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint8_t keybuf[16]; | ||||
|     int len, i; | ||||
|     Error *err; | ||||
|  | ||||
|     memset(keybuf, 0, 16); | ||||
|     len = strlen(key); | ||||
| @@ -275,68 +269,38 @@ static int qcow_set_key(BlockDriverState *bs, const char *key) | ||||
|     for(i = 0;i < len;i++) { | ||||
|         keybuf[i] = key[i]; | ||||
|     } | ||||
|     assert(bs->encrypted); | ||||
|     s->crypt_method = s->crypt_method_header; | ||||
|  | ||||
|     qcrypto_cipher_free(s->cipher); | ||||
|     s->cipher = qcrypto_cipher_new( | ||||
|         QCRYPTO_CIPHER_ALG_AES_128, | ||||
|         QCRYPTO_CIPHER_MODE_CBC, | ||||
|         keybuf, G_N_ELEMENTS(keybuf), | ||||
|         &err); | ||||
|  | ||||
|     if (!s->cipher) { | ||||
|         /* XXX would be nice if errors in this method could | ||||
|          * be properly propagate to the caller. Would need | ||||
|          * the bdrv_set_key() API signature to be fixed. */ | ||||
|         error_free(err); | ||||
|     if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0) | ||||
|         return -1; | ||||
|     if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0) | ||||
|         return -1; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* The crypt function is compatible with the linux cryptoloop | ||||
|    algorithm for < 4 GB images. NOTE: out_buf == in_buf is | ||||
|    supported */ | ||||
| static int encrypt_sectors(BDRVQcowState *s, int64_t sector_num, | ||||
| static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num, | ||||
|                             uint8_t *out_buf, const uint8_t *in_buf, | ||||
|                            int nb_sectors, bool enc, Error **errp) | ||||
|                             int nb_sectors, int enc, | ||||
|                             const AES_KEY *key) | ||||
| { | ||||
|     union { | ||||
|         uint64_t ll[2]; | ||||
|         uint8_t b[16]; | ||||
|     } ivec; | ||||
|     int i; | ||||
|     int ret; | ||||
|  | ||||
|     for(i = 0; i < nb_sectors; i++) { | ||||
|         ivec.ll[0] = cpu_to_le64(sector_num); | ||||
|         ivec.ll[1] = 0; | ||||
|         if (qcrypto_cipher_setiv(s->cipher, | ||||
|                                  ivec.b, G_N_ELEMENTS(ivec.b), | ||||
|                                  errp) < 0) { | ||||
|             return -1; | ||||
|         } | ||||
|         if (enc) { | ||||
|             ret = qcrypto_cipher_encrypt(s->cipher, | ||||
|                                          in_buf, | ||||
|                                          out_buf, | ||||
|                                          512, | ||||
|                                          errp); | ||||
|         } else { | ||||
|             ret = qcrypto_cipher_decrypt(s->cipher, | ||||
|                                          in_buf, | ||||
|                                          out_buf, | ||||
|                                          512, | ||||
|                                          errp); | ||||
|         } | ||||
|         if (ret < 0) { | ||||
|             return -1; | ||||
|         } | ||||
|         AES_cbc_encrypt(in_buf, out_buf, 512, key, | ||||
|                         ivec.b, enc); | ||||
|         sector_num++; | ||||
|         in_buf += 512; | ||||
|         out_buf += 512; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* 'allocate' is: | ||||
| @@ -370,13 +334,13 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | ||||
|         if (!allocate) | ||||
|             return 0; | ||||
|         /* allocate a new l2 entry */ | ||||
|         l2_offset = bdrv_getlength(bs->file->bs); | ||||
|         l2_offset = bdrv_getlength(bs->file); | ||||
|         /* round to cluster size */ | ||||
|         l2_offset = (l2_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); | ||||
|         /* update the L1 entry */ | ||||
|         s->l1_table[l1_index] = l2_offset; | ||||
|         tmp = cpu_to_be64(l2_offset); | ||||
|         if (bdrv_pwrite_sync(bs->file->bs, | ||||
|         if (bdrv_pwrite_sync(bs->file, | ||||
|                 s->l1_table_offset + l1_index * sizeof(tmp), | ||||
|                 &tmp, sizeof(tmp)) < 0) | ||||
|             return 0; | ||||
| @@ -406,12 +370,11 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | ||||
|     l2_table = s->l2_cache + (min_index << s->l2_bits); | ||||
|     if (new_l2_table) { | ||||
|         memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); | ||||
|         if (bdrv_pwrite_sync(bs->file->bs, l2_offset, l2_table, | ||||
|         if (bdrv_pwrite_sync(bs->file, l2_offset, l2_table, | ||||
|                 s->l2_size * sizeof(uint64_t)) < 0) | ||||
|             return 0; | ||||
|     } else { | ||||
|         if (bdrv_pread(bs->file->bs, l2_offset, l2_table, | ||||
|                        s->l2_size * sizeof(uint64_t)) != | ||||
|         if (bdrv_pread(bs->file, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) != | ||||
|             s->l2_size * sizeof(uint64_t)) | ||||
|             return 0; | ||||
|     } | ||||
| @@ -432,42 +395,34 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | ||||
|                overwritten */ | ||||
|             if (decompress_cluster(bs, cluster_offset) < 0) | ||||
|                 return 0; | ||||
|             cluster_offset = bdrv_getlength(bs->file->bs); | ||||
|             cluster_offset = bdrv_getlength(bs->file); | ||||
|             cluster_offset = (cluster_offset + s->cluster_size - 1) & | ||||
|                 ~(s->cluster_size - 1); | ||||
|             /* write the cluster content */ | ||||
|             if (bdrv_pwrite(bs->file->bs, cluster_offset, s->cluster_cache, | ||||
|                             s->cluster_size) != | ||||
|             if (bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache, s->cluster_size) != | ||||
|                 s->cluster_size) | ||||
|                 return -1; | ||||
|         } else { | ||||
|             cluster_offset = bdrv_getlength(bs->file->bs); | ||||
|             cluster_offset = bdrv_getlength(bs->file); | ||||
|             if (allocate == 1) { | ||||
|                 /* round to cluster size */ | ||||
|                 cluster_offset = (cluster_offset + 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 | ||||
|                    content which won't be written */ | ||||
|                 if (bs->encrypted && | ||||
|                 if (s->crypt_method && | ||||
|                     (n_end - n_start) < s->cluster_sectors) { | ||||
|                     uint64_t start_sect; | ||||
|                     assert(s->cipher); | ||||
|                     start_sect = (offset & ~(s->cluster_size - 1)) >> 9; | ||||
|                     memset(s->cluster_data + 512, 0x00, 512); | ||||
|                     for(i = 0; i < s->cluster_sectors; i++) { | ||||
|                         if (i < n_start || i >= n_end) { | ||||
|                             Error *err = NULL; | ||||
|                             if (encrypt_sectors(s, start_sect + i, | ||||
|                             encrypt_sectors(s, start_sect + i, | ||||
|                                             s->cluster_data, | ||||
|                                                 s->cluster_data + 512, 1, | ||||
|                                                 true, &err) < 0) { | ||||
|                                 error_free(err); | ||||
|                                 errno = EIO; | ||||
|                                 return -1; | ||||
|                             } | ||||
|                             if (bdrv_pwrite(bs->file->bs, | ||||
|                                             cluster_offset + i * 512, | ||||
|                                             s->cluster_data + 512, 1, 1, | ||||
|                                             &s->aes_encrypt_key); | ||||
|                             if (bdrv_pwrite(bs->file, cluster_offset + i * 512, | ||||
|                                             s->cluster_data, 512) != 512) | ||||
|                                 return -1; | ||||
|                         } | ||||
| @@ -481,7 +436,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | ||||
|         /* update L2 table */ | ||||
|         tmp = cpu_to_be64(cluster_offset); | ||||
|         l2_table[l2_index] = tmp; | ||||
|         if (bdrv_pwrite_sync(bs->file->bs, l2_offset + l2_index * sizeof(tmp), | ||||
|         if (bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp), | ||||
|                 &tmp, sizeof(tmp)) < 0) | ||||
|             return 0; | ||||
|     } | ||||
| @@ -489,7 +444,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | ||||
| } | ||||
|  | ||||
| static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) | ||||
|         int64_t sector_num, int nb_sectors, int *pnum) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int index_in_cluster, n; | ||||
| @@ -506,11 +461,10 @@ static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs, | ||||
|     if (!cluster_offset) { | ||||
|         return 0; | ||||
|     } | ||||
|     if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->cipher) { | ||||
|     if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->crypt_method) { | ||||
|         return BDRV_BLOCK_DATA; | ||||
|     } | ||||
|     cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS); | ||||
|     *file = bs->file->bs; | ||||
|     return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | cluster_offset; | ||||
| } | ||||
|  | ||||
| @@ -551,7 +505,7 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) | ||||
|     if (s->cluster_cache_offset != coffset) { | ||||
|         csize = cluster_offset >> (63 - s->cluster_bits); | ||||
|         csize &= (s->cluster_size - 1); | ||||
|         ret = bdrv_pread(bs->file->bs, coffset, s->cluster_data, csize); | ||||
|         ret = bdrv_pread(bs->file, coffset, s->cluster_data, csize); | ||||
|         if (ret != csize) | ||||
|             return -1; | ||||
|         if (decompress_buffer(s->cluster_cache, s->cluster_size, | ||||
| @@ -574,7 +528,6 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, | ||||
|     QEMUIOVector hd_qiov; | ||||
|     uint8_t *buf; | ||||
|     void *orig_buf; | ||||
|     Error *err = NULL; | ||||
|  | ||||
|     if (qiov->niov > 1) { | ||||
|         buf = orig_buf = qemu_try_blockalign(bs, qiov->size); | ||||
| @@ -599,13 +552,13 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, | ||||
|         } | ||||
|  | ||||
|         if (!cluster_offset) { | ||||
|             if (bs->backing) { | ||||
|             if (bs->backing_hd) { | ||||
|                 /* read from the base image */ | ||||
|                 hd_iov.iov_base = (void *)buf; | ||||
|                 hd_iov.iov_len = n * 512; | ||||
|                 qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); | ||||
|                 qemu_co_mutex_unlock(&s->lock); | ||||
|                 ret = bdrv_co_readv(bs->backing->bs, sector_num, | ||||
|                 ret = bdrv_co_readv(bs->backing_hd, sector_num, | ||||
|                                     n, &hd_qiov); | ||||
|                 qemu_co_mutex_lock(&s->lock); | ||||
|                 if (ret < 0) { | ||||
| @@ -630,19 +583,17 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, | ||||
|             hd_iov.iov_len = n * 512; | ||||
|             qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); | ||||
|             qemu_co_mutex_unlock(&s->lock); | ||||
|             ret = bdrv_co_readv(bs->file->bs, | ||||
|             ret = bdrv_co_readv(bs->file, | ||||
|                                 (cluster_offset >> 9) + index_in_cluster, | ||||
|                                 n, &hd_qiov); | ||||
|             qemu_co_mutex_lock(&s->lock); | ||||
|             if (ret < 0) { | ||||
|                 break; | ||||
|             } | ||||
|             if (bs->encrypted) { | ||||
|                 assert(s->cipher); | ||||
|                 if (encrypt_sectors(s, sector_num, buf, buf, | ||||
|                                     n, false, &err) < 0) { | ||||
|                     goto fail; | ||||
|                 } | ||||
|             if (s->crypt_method) { | ||||
|                 encrypt_sectors(s, sector_num, buf, buf, | ||||
|                                 n, 0, | ||||
|                                 &s->aes_decrypt_key); | ||||
|             } | ||||
|         } | ||||
|         ret = 0; | ||||
| @@ -663,7 +614,6 @@ done: | ||||
|     return ret; | ||||
|  | ||||
| fail: | ||||
|     error_free(err); | ||||
|     ret = -EIO; | ||||
|     goto done; | ||||
| } | ||||
| @@ -711,18 +661,12 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, | ||||
|             ret = -EIO; | ||||
|             break; | ||||
|         } | ||||
|         if (bs->encrypted) { | ||||
|             Error *err = NULL; | ||||
|             assert(s->cipher); | ||||
|         if (s->crypt_method) { | ||||
|             if (!cluster_data) { | ||||
|                 cluster_data = g_malloc0(s->cluster_size); | ||||
|             } | ||||
|             if (encrypt_sectors(s, sector_num, cluster_data, buf, | ||||
|                                 n, true, &err) < 0) { | ||||
|                 error_free(err); | ||||
|                 ret = -EIO; | ||||
|                 break; | ||||
|             } | ||||
|             encrypt_sectors(s, sector_num, cluster_data, buf, | ||||
|                             n, 1, &s->aes_encrypt_key); | ||||
|             src_buf = cluster_data; | ||||
|         } else { | ||||
|             src_buf = buf; | ||||
| @@ -732,7 +676,7 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, | ||||
|         hd_iov.iov_len = n * 512; | ||||
|         qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); | ||||
|         qemu_co_mutex_unlock(&s->lock); | ||||
|         ret = bdrv_co_writev(bs->file->bs, | ||||
|         ret = bdrv_co_writev(bs->file, | ||||
|                              (cluster_offset >> 9) + index_in_cluster, | ||||
|                              n, &hd_qiov); | ||||
|         qemu_co_mutex_lock(&s->lock); | ||||
| @@ -759,8 +703,6 @@ static void qcow_close(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|  | ||||
|     qcrypto_cipher_free(s->cipher); | ||||
|     s->cipher = NULL; | ||||
|     g_free(s->l1_table); | ||||
|     qemu_vfree(s->l2_cache); | ||||
|     g_free(s->cluster_cache); | ||||
| @@ -798,7 +740,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
|  | ||||
|     qcow_bs = NULL; | ||||
|     ret = bdrv_open(&qcow_bs, filename, NULL, NULL, | ||||
|                     BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); | ||||
|                     BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err); | ||||
|     if (ret < 0) { | ||||
|         error_propagate(errp, local_err); | ||||
|         goto cleanup; | ||||
| @@ -884,10 +826,10 @@ static int qcow_make_empty(BlockDriverState *bs) | ||||
|     int ret; | ||||
|  | ||||
|     memset(s->l1_table, 0, l1_length); | ||||
|     if (bdrv_pwrite_sync(bs->file->bs, s->l1_table_offset, s->l1_table, | ||||
|     if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, s->l1_table, | ||||
|             l1_length) < 0) | ||||
|         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) | ||||
|         return ret; | ||||
|  | ||||
| @@ -967,7 +909,7 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num, | ||||
|         } | ||||
|  | ||||
|         cluster_offset &= s->cluster_offset_mask; | ||||
|         ret = bdrv_pwrite(bs->file->bs, cluster_offset, out_buf, out_len); | ||||
|         ret = bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
|         } | ||||
|   | ||||
| @@ -22,23 +22,17 @@ | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| /* Needed for CONFIG_MADVISE */ | ||||
| #include "qemu/osdep.h" | ||||
|  | ||||
| #if defined(CONFIG_MADVISE) || defined(CONFIG_POSIX_MADVISE) | ||||
| #include <sys/mman.h> | ||||
| #endif | ||||
|  | ||||
| #include "block/block_int.h" | ||||
| #include "qemu-common.h" | ||||
| #include "qcow2.h" | ||||
| #include "trace.h" | ||||
|  | ||||
| typedef struct Qcow2CachedTable { | ||||
|     void*   table; | ||||
|     int64_t offset; | ||||
|     uint64_t lru_counter; | ||||
|     int      ref; | ||||
|     bool    dirty; | ||||
|     int     cache_hits; | ||||
|     int     ref; | ||||
| } Qcow2CachedTable; | ||||
|  | ||||
| struct Qcow2Cache { | ||||
| @@ -46,97 +40,39 @@ struct Qcow2Cache { | ||||
|     struct Qcow2Cache*      depends; | ||||
|     int                     size; | ||||
|     bool                    depends_on_flush; | ||||
|     void                   *table_array; | ||||
|     uint64_t                lru_counter; | ||||
|     uint64_t                cache_clean_lru_counter; | ||||
| }; | ||||
|  | ||||
| static inline void *qcow2_cache_get_table_addr(BlockDriverState *bs, | ||||
|                     Qcow2Cache *c, int table) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     return (uint8_t *) c->table_array + (size_t) table * s->cluster_size; | ||||
| } | ||||
|  | ||||
| static inline int qcow2_cache_get_table_idx(BlockDriverState *bs, | ||||
|                   Qcow2Cache *c, void *table) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     ptrdiff_t table_offset = (uint8_t *) table - (uint8_t *) c->table_array; | ||||
|     int idx = table_offset / s->cluster_size; | ||||
|     assert(idx >= 0 && idx < c->size && table_offset % s->cluster_size == 0); | ||||
|     return idx; | ||||
| } | ||||
|  | ||||
| static void qcow2_cache_table_release(BlockDriverState *bs, Qcow2Cache *c, | ||||
|                                       int i, int num_tables) | ||||
| { | ||||
| #if QEMU_MADV_DONTNEED != QEMU_MADV_INVALID | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     void *t = qcow2_cache_get_table_addr(bs, c, i); | ||||
|     int align = getpagesize(); | ||||
|     size_t mem_size = (size_t) s->cluster_size * num_tables; | ||||
|     size_t offset = QEMU_ALIGN_UP((uintptr_t) t, align) - (uintptr_t) t; | ||||
|     size_t length = QEMU_ALIGN_DOWN(mem_size - offset, align); | ||||
|     if (length > 0) { | ||||
|         qemu_madvise((uint8_t *) t + offset, length, QEMU_MADV_DONTNEED); | ||||
|     } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static inline bool can_clean_entry(Qcow2Cache *c, int i) | ||||
| { | ||||
|     Qcow2CachedTable *t = &c->entries[i]; | ||||
|     return t->ref == 0 && !t->dirty && t->offset != 0 && | ||||
|         t->lru_counter <= c->cache_clean_lru_counter; | ||||
| } | ||||
|  | ||||
| void qcow2_cache_clean_unused(BlockDriverState *bs, Qcow2Cache *c) | ||||
| { | ||||
|     int i = 0; | ||||
|     while (i < c->size) { | ||||
|         int to_clean = 0; | ||||
|  | ||||
|         /* Skip the entries that we don't need to clean */ | ||||
|         while (i < c->size && !can_clean_entry(c, i)) { | ||||
|             i++; | ||||
|         } | ||||
|  | ||||
|         /* And count how many we can clean in a row */ | ||||
|         while (i < c->size && can_clean_entry(c, i)) { | ||||
|             c->entries[i].offset = 0; | ||||
|             c->entries[i].lru_counter = 0; | ||||
|             i++; | ||||
|             to_clean++; | ||||
|         } | ||||
|  | ||||
|         if (to_clean > 0) { | ||||
|             qcow2_cache_table_release(bs, c, i - to_clean, to_clean); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     c->cache_clean_lru_counter = c->lru_counter; | ||||
| } | ||||
|  | ||||
| Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     Qcow2Cache *c; | ||||
|     int i; | ||||
|  | ||||
|     c = g_new0(Qcow2Cache, 1); | ||||
|     c->size = num_tables; | ||||
|     c->entries = g_try_new0(Qcow2CachedTable, num_tables); | ||||
|     c->table_array = qemu_try_blockalign(bs->file->bs, | ||||
|                                          (size_t) num_tables * s->cluster_size); | ||||
|     if (!c->entries) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     if (!c->entries || !c->table_array) { | ||||
|         qemu_vfree(c->table_array); | ||||
|         g_free(c->entries); | ||||
|         g_free(c); | ||||
|         c = NULL; | ||||
|     for (i = 0; i < c->size; i++) { | ||||
|         c->entries[i].table = qemu_try_blockalign(bs->file, s->cluster_size); | ||||
|         if (c->entries[i].table == NULL) { | ||||
|             goto fail; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return c; | ||||
|  | ||||
| fail: | ||||
|     if (c->entries) { | ||||
|         for (i = 0; i < c->size; i++) { | ||||
|             qemu_vfree(c->entries[i].table); | ||||
|         } | ||||
|     } | ||||
|     g_free(c->entries); | ||||
|     g_free(c); | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c) | ||||
| @@ -145,9 +81,9 @@ int qcow2_cache_destroy(BlockDriverState *bs, Qcow2Cache *c) | ||||
|  | ||||
|     for (i = 0; i < c->size; i++) { | ||||
|         assert(c->entries[i].ref == 0); | ||||
|         qemu_vfree(c->entries[i].table); | ||||
|     } | ||||
|  | ||||
|     qemu_vfree(c->table_array); | ||||
|     g_free(c->entries); | ||||
|     g_free(c); | ||||
|  | ||||
| @@ -171,7 +107,7 @@ static int qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c) | ||||
|  | ||||
| static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int ret = 0; | ||||
|  | ||||
|     if (!c->entries[i].dirty || !c->entries[i].offset) { | ||||
| @@ -184,7 +120,7 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) | ||||
|     if (c->depends) { | ||||
|         ret = qcow2_cache_flush_dependency(bs, c); | ||||
|     } else if (c->depends_on_flush) { | ||||
|         ret = bdrv_flush(bs->file->bs); | ||||
|         ret = bdrv_flush(bs->file); | ||||
|         if (ret >= 0) { | ||||
|             c->depends_on_flush = false; | ||||
|         } | ||||
| @@ -215,8 +151,8 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) | ||||
|         BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_pwrite(bs->file->bs, c->entries[i].offset, | ||||
|                       qcow2_cache_get_table_addr(bs, c, i), s->cluster_size); | ||||
|     ret = bdrv_pwrite(bs->file, c->entries[i].offset, c->entries[i].table, | ||||
|         s->cluster_size); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
| @@ -228,7 +164,7 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) | ||||
|  | ||||
| int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int result = 0; | ||||
|     int ret; | ||||
|     int i; | ||||
| @@ -243,7 +179,7 @@ int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c) | ||||
|     } | ||||
|  | ||||
|     if (result == 0) { | ||||
|         ret = bdrv_flush(bs->file->bs); | ||||
|         ret = bdrv_flush(bs->file); | ||||
|         if (ret < 0) { | ||||
|             result = ret; | ||||
|         } | ||||
| @@ -292,55 +228,68 @@ int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c) | ||||
|     for (i = 0; i < c->size; i++) { | ||||
|         assert(c->entries[i].ref == 0); | ||||
|         c->entries[i].offset = 0; | ||||
|         c->entries[i].lru_counter = 0; | ||||
|         c->entries[i].cache_hits = 0; | ||||
|     } | ||||
|  | ||||
|     qcow2_cache_table_release(bs, c, 0, c->size); | ||||
|  | ||||
|     c->lru_counter = 0; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int qcow2_cache_find_entry_to_replace(Qcow2Cache *c) | ||||
| { | ||||
|     int i; | ||||
|     int min_count = INT_MAX; | ||||
|     int min_index = -1; | ||||
|  | ||||
|  | ||||
|     for (i = 0; i < c->size; i++) { | ||||
|         if (c->entries[i].ref) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         if (c->entries[i].cache_hits < min_count) { | ||||
|             min_index = i; | ||||
|             min_count = c->entries[i].cache_hits; | ||||
|         } | ||||
|  | ||||
|         /* Give newer hits priority */ | ||||
|         /* TODO Check how to optimize the replacement strategy */ | ||||
|         if (c->entries[i].cache_hits > 1) { | ||||
|             c->entries[i].cache_hits /= 2; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (min_index == -1) { | ||||
|         /* This can't happen in current synchronous code, but leave the check | ||||
|          * here as a reminder for whoever starts using AIO with the cache */ | ||||
|         abort(); | ||||
|     } | ||||
|     return min_index; | ||||
| } | ||||
|  | ||||
| static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, | ||||
|     uint64_t offset, void **table, bool read_from_disk) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int i; | ||||
|     int ret; | ||||
|     int lookup_index; | ||||
|     uint64_t min_lru_counter = UINT64_MAX; | ||||
|     int min_lru_index = -1; | ||||
|  | ||||
|     trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache, | ||||
|                           offset, read_from_disk); | ||||
|  | ||||
|     /* Check if the table is already cached */ | ||||
|     i = lookup_index = (offset / s->cluster_size * 4) % c->size; | ||||
|     do { | ||||
|         const Qcow2CachedTable *t = &c->entries[i]; | ||||
|         if (t->offset == offset) { | ||||
|     for (i = 0; i < c->size; i++) { | ||||
|         if (c->entries[i].offset == offset) { | ||||
|             goto found; | ||||
|         } | ||||
|         if (t->ref == 0 && t->lru_counter < min_lru_counter) { | ||||
|             min_lru_counter = t->lru_counter; | ||||
|             min_lru_index = i; | ||||
|         } | ||||
|         if (++i == c->size) { | ||||
|             i = 0; | ||||
|         } | ||||
|     } while (i != lookup_index); | ||||
|  | ||||
|     if (min_lru_index == -1) { | ||||
|         /* This can't happen in current synchronous code, but leave the check | ||||
|          * here as a reminder for whoever starts using AIO with the cache */ | ||||
|         abort(); | ||||
|     } | ||||
|  | ||||
|     /* Cache miss: write a table back and replace it */ | ||||
|     i = min_lru_index; | ||||
|     /* If not, write a table back and replace it */ | ||||
|     i = qcow2_cache_find_entry_to_replace(c); | ||||
|     trace_qcow2_cache_get_replace_entry(qemu_coroutine_self(), | ||||
|                                         c == s->l2_table_cache, i); | ||||
|     if (i < 0) { | ||||
|         return i; | ||||
|     } | ||||
|  | ||||
|     ret = qcow2_cache_entry_flush(bs, c, i); | ||||
|     if (ret < 0) { | ||||
| @@ -355,20 +304,22 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, | ||||
|             BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD); | ||||
|         } | ||||
|  | ||||
|         ret = bdrv_pread(bs->file->bs, offset, | ||||
|                          qcow2_cache_get_table_addr(bs, c, i), | ||||
|                          s->cluster_size); | ||||
|         ret = bdrv_pread(bs->file, offset, c->entries[i].table, s->cluster_size); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* Give the table some hits for the start so that it won't be replaced | ||||
|      * immediately. The number 32 is completely arbitrary. */ | ||||
|     c->entries[i].cache_hits = 32; | ||||
|     c->entries[i].offset = offset; | ||||
|  | ||||
|     /* And return the right table */ | ||||
| found: | ||||
|     c->entries[i].cache_hits++; | ||||
|     c->entries[i].ref++; | ||||
|     *table = qcow2_cache_get_table_addr(bs, c, i); | ||||
|     *table = c->entries[i].table; | ||||
|  | ||||
|     trace_qcow2_cache_get_done(qemu_coroutine_self(), | ||||
|                                c == s->l2_table_cache, i); | ||||
| @@ -388,24 +339,36 @@ int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, | ||||
|     return qcow2_cache_do_get(bs, c, offset, table, false); | ||||
| } | ||||
|  | ||||
| void qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table) | ||||
| int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table) | ||||
| { | ||||
|     int i = qcow2_cache_get_table_idx(bs, c, *table); | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < c->size; i++) { | ||||
|         if (c->entries[i].table == *table) { | ||||
|             goto found; | ||||
|         } | ||||
|     } | ||||
|     return -ENOENT; | ||||
|  | ||||
| found: | ||||
|     c->entries[i].ref--; | ||||
|     *table = NULL; | ||||
|  | ||||
|     if (c->entries[i].ref == 0) { | ||||
|         c->entries[i].lru_counter = ++c->lru_counter; | ||||
|     } | ||||
|  | ||||
|     assert(c->entries[i].ref >= 0); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void qcow2_cache_entry_mark_dirty(BlockDriverState *bs, Qcow2Cache *c, | ||||
|      void *table) | ||||
| void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table) | ||||
| { | ||||
|     int i = qcow2_cache_get_table_idx(bs, c, table); | ||||
|     assert(c->entries[i].offset != 0); | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < c->size; i++) { | ||||
|         if (c->entries[i].table == table) { | ||||
|             goto found; | ||||
|         } | ||||
|     } | ||||
|     abort(); | ||||
|  | ||||
| found: | ||||
|     c->entries[i].dirty = true; | ||||
| } | ||||
|   | ||||
| @@ -22,7 +22,6 @@ | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include <zlib.h> | ||||
|  | ||||
| #include "qemu-common.h" | ||||
| @@ -33,7 +32,7 @@ | ||||
| int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, | ||||
|                         bool exact_size) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int new_l1_size2, ret, i; | ||||
|     uint64_t *new_l1_table; | ||||
|     int64_t old_l1_table_offset, old_l1_size; | ||||
| @@ -73,7 +72,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, | ||||
| #endif | ||||
|  | ||||
|     new_l1_size2 = sizeof(uint64_t) * new_l1_size; | ||||
|     new_l1_table = qemu_try_blockalign(bs->file->bs, | ||||
|     new_l1_table = qemu_try_blockalign(bs->file, | ||||
|                                        align_offset(new_l1_size2, 512)); | ||||
|     if (new_l1_table == NULL) { | ||||
|         return -ENOMEM; | ||||
| @@ -106,8 +105,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE); | ||||
|     for(i = 0; i < s->l1_size; i++) | ||||
|         new_l1_table[i] = cpu_to_be64(new_l1_table[i]); | ||||
|     ret = bdrv_pwrite_sync(bs->file->bs, new_l1_table_offset, | ||||
|                            new_l1_table, new_l1_size2); | ||||
|     ret = bdrv_pwrite_sync(bs->file, new_l1_table_offset, new_l1_table, new_l1_size2); | ||||
|     if (ret < 0) | ||||
|         goto fail; | ||||
|     for(i = 0; i < s->l1_size; i++) | ||||
| @@ -117,8 +115,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ACTIVATE_TABLE); | ||||
|     cpu_to_be32w((uint32_t*)data, new_l1_size); | ||||
|     stq_be_p(data + 4, new_l1_table_offset); | ||||
|     ret = bdrv_pwrite_sync(bs->file->bs, offsetof(QCowHeader, l1_size), | ||||
|                            data, sizeof(data)); | ||||
|     ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_size), data,sizeof(data)); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
| @@ -151,7 +148,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, | ||||
| static int l2_load(BlockDriverState *bs, uint64_t l2_offset, | ||||
|     uint64_t **l2_table) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int ret; | ||||
|  | ||||
|     ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, (void**) l2_table); | ||||
| @@ -166,7 +163,7 @@ static int l2_load(BlockDriverState *bs, uint64_t l2_offset, | ||||
| #define L1_ENTRIES_PER_SECTOR (512 / 8) | ||||
| int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint64_t buf[L1_ENTRIES_PER_SECTOR] = { 0 }; | ||||
|     int l1_start_index; | ||||
|     int i, ret; | ||||
| @@ -185,8 +182,7 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) | ||||
|     } | ||||
|  | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); | ||||
|     ret = bdrv_pwrite_sync(bs->file->bs, | ||||
|                            s->l1_table_offset + 8 * l1_start_index, | ||||
|     ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset + 8 * l1_start_index, | ||||
|         buf, sizeof(buf)); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
| @@ -207,7 +203,7 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) | ||||
|  | ||||
| static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint64_t old_l2_offset; | ||||
|     uint64_t *l2_table = NULL; | ||||
|     int64_t l2_offset; | ||||
| @@ -257,14 +253,17 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) | ||||
|  | ||||
|         memcpy(l2_table, old_table, s->cluster_size); | ||||
|  | ||||
|         qcow2_cache_put(bs, s->l2_table_cache, (void **) &old_table); | ||||
|         ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &old_table); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* write the l2 table to the file */ | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE); | ||||
|  | ||||
|     trace_qcow2_l2_allocate_write_l2(bs, l1_index); | ||||
|     qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); | ||||
|     qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||||
|     ret = qcow2_cache_flush(bs, s->l2_table_cache); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
| @@ -302,7 +301,7 @@ fail: | ||||
|  * as contiguous. (This allows it, for example, to stop at the first compressed | ||||
|  * cluster which may require a different handling) | ||||
|  */ | ||||
| static int count_contiguous_clusters(int nb_clusters, int cluster_size, | ||||
| static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size, | ||||
|         uint64_t *l2_table, uint64_t stop_flags) | ||||
| { | ||||
|     int i; | ||||
| @@ -313,7 +312,7 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size, | ||||
|     if (!offset) | ||||
|         return 0; | ||||
|  | ||||
|     assert(qcow2_get_cluster_type(first_entry) == QCOW2_CLUSTER_NORMAL); | ||||
|     assert(qcow2_get_cluster_type(first_entry) != QCOW2_CLUSTER_COMPRESSED); | ||||
|  | ||||
|     for (i = 0; i < nb_clusters; i++) { | ||||
|         uint64_t l2_entry = be64_to_cpu(l2_table[i]) & mask; | ||||
| @@ -325,16 +324,14 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size, | ||||
| 	return i; | ||||
| } | ||||
|  | ||||
| static int count_contiguous_clusters_by_type(int nb_clusters, | ||||
|                                              uint64_t *l2_table, | ||||
|                                              int wanted_type) | ||||
| static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_table) | ||||
| { | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < nb_clusters; i++) { | ||||
|         int type = qcow2_get_cluster_type(be64_to_cpu(l2_table[i])); | ||||
|  | ||||
|         if (type != wanted_type) { | ||||
|         if (type != QCOW2_CLUSTER_UNALLOCATED) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| @@ -345,47 +342,26 @@ static int count_contiguous_clusters_by_type(int nb_clusters, | ||||
| /* The crypt function is compatible with the linux cryptoloop | ||||
|    algorithm for < 4 GB images. NOTE: out_buf == in_buf is | ||||
|    supported */ | ||||
| int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num, | ||||
| void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, | ||||
|                            uint8_t *out_buf, const uint8_t *in_buf, | ||||
|                           int nb_sectors, bool enc, | ||||
|                           Error **errp) | ||||
|                            int nb_sectors, int enc, | ||||
|                            const AES_KEY *key) | ||||
| { | ||||
|     union { | ||||
|         uint64_t ll[2]; | ||||
|         uint8_t b[16]; | ||||
|     } ivec; | ||||
|     int i; | ||||
|     int ret; | ||||
|  | ||||
|     for(i = 0; i < nb_sectors; i++) { | ||||
|         ivec.ll[0] = cpu_to_le64(sector_num); | ||||
|         ivec.ll[1] = 0; | ||||
|         if (qcrypto_cipher_setiv(s->cipher, | ||||
|                                  ivec.b, G_N_ELEMENTS(ivec.b), | ||||
|                                  errp) < 0) { | ||||
|             return -1; | ||||
|         } | ||||
|         if (enc) { | ||||
|             ret = qcrypto_cipher_encrypt(s->cipher, | ||||
|                                          in_buf, | ||||
|                                          out_buf, | ||||
|                                          512, | ||||
|                                          errp); | ||||
|         } else { | ||||
|             ret = qcrypto_cipher_decrypt(s->cipher, | ||||
|                                          in_buf, | ||||
|                                          out_buf, | ||||
|                                          512, | ||||
|                                          errp); | ||||
|         } | ||||
|         if (ret < 0) { | ||||
|             return -1; | ||||
|         } | ||||
|         AES_cbc_encrypt(in_buf, out_buf, 512, key, | ||||
|                         ivec.b, enc); | ||||
|         sector_num++; | ||||
|         in_buf += 512; | ||||
|         out_buf += 512; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn copy_sectors(BlockDriverState *bs, | ||||
| @@ -393,7 +369,7 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs, | ||||
|                                      uint64_t cluster_offset, | ||||
|                                      int n_start, int n_end) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     QEMUIOVector qiov; | ||||
|     struct iovec iov; | ||||
|     int n, ret; | ||||
| @@ -427,16 +403,10 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs, | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     if (bs->encrypted) { | ||||
|         Error *err = NULL; | ||||
|         assert(s->cipher); | ||||
|         if (qcow2_encrypt_sectors(s, start_sect + n_start, | ||||
|                                   iov.iov_base, iov.iov_base, n, | ||||
|                                   true, &err) < 0) { | ||||
|             ret = -EIO; | ||||
|             error_free(err); | ||||
|             goto out; | ||||
|         } | ||||
|     if (s->crypt_method) { | ||||
|         qcow2_encrypt_sectors(s, start_sect + n_start, | ||||
|                         iov.iov_base, iov.iov_base, n, 1, | ||||
|                         &s->aes_encrypt_key); | ||||
|     } | ||||
|  | ||||
|     ret = qcow2_pre_write_overlap_check(bs, 0, | ||||
| @@ -446,8 +416,7 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs, | ||||
|     } | ||||
|  | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE); | ||||
|     ret = bdrv_co_writev(bs->file->bs, (cluster_offset >> 9) + n_start, n, | ||||
|                          &qiov); | ||||
|     ret = bdrv_co_writev(bs->file, (cluster_offset >> 9) + n_start, n, &qiov); | ||||
|     if (ret < 0) { | ||||
|         goto out; | ||||
|     } | ||||
| @@ -476,7 +445,7 @@ out: | ||||
| int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||
|     int *num, uint64_t *cluster_offset) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     unsigned int l2_index; | ||||
|     uint64_t l1_index, l2_offset, *l2_table; | ||||
|     int l1_bits, c; | ||||
| @@ -502,11 +471,10 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||
|     if (nb_needed > nb_available) { | ||||
|         nb_needed = nb_available; | ||||
|     } | ||||
|     assert(nb_needed <= INT_MAX); | ||||
|  | ||||
|     *cluster_offset = 0; | ||||
|  | ||||
|     /* seek to the l2 offset in the l1 table */ | ||||
|     /* seek the the l2 offset in the l1 table */ | ||||
|  | ||||
|     l1_index = offset >> l1_bits; | ||||
|     if (l1_index >= s->l1_size) { | ||||
| @@ -538,8 +506,6 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||
|  | ||||
|     l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1); | ||||
|     *cluster_offset = be64_to_cpu(l2_table[l2_index]); | ||||
|  | ||||
|     /* nb_needed <= INT_MAX, thus nb_clusters <= INT_MAX, too */ | ||||
|     nb_clusters = size_to_clusters(s, nb_needed << 9); | ||||
|  | ||||
|     ret = qcow2_get_cluster_type(*cluster_offset); | ||||
| @@ -557,14 +523,13 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||
|             ret = -EIO; | ||||
|             goto fail; | ||||
|         } | ||||
|         c = count_contiguous_clusters_by_type(nb_clusters, &l2_table[l2_index], | ||||
|                                               QCOW2_CLUSTER_ZERO); | ||||
|         c = count_contiguous_clusters(nb_clusters, s->cluster_size, | ||||
|                 &l2_table[l2_index], QCOW_OFLAG_ZERO); | ||||
|         *cluster_offset = 0; | ||||
|         break; | ||||
|     case QCOW2_CLUSTER_UNALLOCATED: | ||||
|         /* how many empty clusters ? */ | ||||
|         c = count_contiguous_clusters_by_type(nb_clusters, &l2_table[l2_index], | ||||
|                                               QCOW2_CLUSTER_UNALLOCATED); | ||||
|         c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]); | ||||
|         *cluster_offset = 0; | ||||
|         break; | ||||
|     case QCOW2_CLUSTER_NORMAL: | ||||
| @@ -617,13 +582,13 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, | ||||
|                              uint64_t **new_l2_table, | ||||
|                              int *new_l2_index) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     unsigned int l2_index; | ||||
|     uint64_t l1_index, l2_offset; | ||||
|     uint64_t *l2_table = NULL; | ||||
|     int ret; | ||||
|  | ||||
|     /* seek to the l2 offset in the l1 table */ | ||||
|     /* seek the the l2 offset in the l1 table */ | ||||
|  | ||||
|     l1_index = offset >> (s->l2_bits + s->cluster_bits); | ||||
|     if (l1_index >= s->l1_size) { | ||||
| @@ -691,7 +656,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, | ||||
|                                                uint64_t offset, | ||||
|                                                int compressed_size) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int l2_index, ret; | ||||
|     uint64_t *l2_table; | ||||
|     int64_t cluster_offset; | ||||
| @@ -727,16 +692,19 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, | ||||
|     /* compressed clusters never have the copied flag */ | ||||
|  | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED); | ||||
|     qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); | ||||
|     qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||||
|     l2_table[l2_index] = cpu_to_be64(cluster_offset); | ||||
|     qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); | ||||
|     ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); | ||||
|     if (ret < 0) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     return cluster_offset; | ||||
| } | ||||
|  | ||||
| static int perform_cow(BlockDriverState *bs, QCowL2Meta *m, Qcow2COWRegion *r) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int ret; | ||||
|  | ||||
|     if (r->nb_sectors == 0) { | ||||
| @@ -765,7 +733,7 @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m, Qcow2COWRegion *r) | ||||
|  | ||||
| int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int i, j = 0, l2_index, ret; | ||||
|     uint64_t *old_cluster, *l2_table; | ||||
|     uint64_t cluster_offset = m->alloc_offset; | ||||
| @@ -803,7 +771,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) | ||||
|     if (ret < 0) { | ||||
|         goto err; | ||||
|     } | ||||
|     qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); | ||||
|     qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||||
|  | ||||
|     assert(l2_index + m->nb_clusters <= s->l2_size); | ||||
|     for (i = 0; i < m->nb_clusters; i++) { | ||||
| @@ -821,10 +789,14 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) | ||||
|      } | ||||
|  | ||||
|  | ||||
|     qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); | ||||
|     ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); | ||||
|     if (ret < 0) { | ||||
|         goto err; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * If this was a COW, we need to decrease the refcount of the old cluster. | ||||
|      * Also flush bs->file to get the right order for L2 and refcount update. | ||||
|      * | ||||
|      * Don't discard clusters that reach a refcount of 0 (e.g. compressed | ||||
|      * clusters), the next write will reuse them anyway. | ||||
| @@ -847,7 +819,7 @@ err: | ||||
|  * write, but require COW to be performed (this includes yet unallocated space, | ||||
|  * which must copy from the backing file) | ||||
|  */ | ||||
| static int count_cow_clusters(BDRVQcow2State *s, int nb_clusters, | ||||
| static int count_cow_clusters(BDRVQcowState *s, int nb_clusters, | ||||
|     uint64_t *l2_table, int l2_index) | ||||
| { | ||||
|     int i; | ||||
| @@ -893,7 +865,7 @@ out: | ||||
| static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset, | ||||
|     uint64_t *cur_bytes, QCowL2Meta **m) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     QCowL2Meta *old_alloc; | ||||
|     uint64_t bytes = *cur_bytes; | ||||
|  | ||||
| @@ -966,13 +938,13 @@ static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset, | ||||
| static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, | ||||
|     uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int l2_index; | ||||
|     uint64_t cluster_offset; | ||||
|     uint64_t *l2_table; | ||||
|     uint64_t nb_clusters; | ||||
|     unsigned int nb_clusters; | ||||
|     unsigned int keep_clusters; | ||||
|     int ret; | ||||
|     int ret, pret; | ||||
|  | ||||
|     trace_qcow2_handle_copied(qemu_coroutine_self(), guest_offset, *host_offset, | ||||
|                               *bytes); | ||||
| @@ -989,7 +961,6 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, | ||||
|  | ||||
|     l2_index = offset_to_l2_index(s, guest_offset); | ||||
|     nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); | ||||
|     assert(nb_clusters <= INT_MAX); | ||||
|  | ||||
|     /* Find L2 entry for the first involved cluster */ | ||||
|     ret = get_cluster_table(bs, guest_offset, &l2_table, &l2_index); | ||||
| @@ -1040,7 +1011,10 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, | ||||
|  | ||||
|     /* Cleanup */ | ||||
| out: | ||||
|     qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); | ||||
|     pret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); | ||||
|     if (pret < 0) { | ||||
|         return pret; | ||||
|     } | ||||
|  | ||||
|     /* Only return a host offset if we actually made progress. Otherwise we | ||||
|      * would make requirements for handle_alloc() that it can't fulfill */ | ||||
| @@ -1072,9 +1046,9 @@ out: | ||||
|  * restarted, but the whole request should not be failed. | ||||
|  */ | ||||
| static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, | ||||
|                                    uint64_t *host_offset, uint64_t *nb_clusters) | ||||
|     uint64_t *host_offset, unsigned int *nb_clusters) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|  | ||||
|     trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset, | ||||
|                                          *host_offset, *nb_clusters); | ||||
| @@ -1090,7 +1064,7 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, | ||||
|         *host_offset = cluster_offset; | ||||
|         return 0; | ||||
|     } else { | ||||
|         int64_t ret = qcow2_alloc_clusters_at(bs, *host_offset, *nb_clusters); | ||||
|         int ret = qcow2_alloc_clusters_at(bs, *host_offset, *nb_clusters); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
| @@ -1122,11 +1096,11 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, | ||||
| static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, | ||||
|     uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int l2_index; | ||||
|     uint64_t *l2_table; | ||||
|     uint64_t entry; | ||||
|     uint64_t nb_clusters; | ||||
|     unsigned int nb_clusters; | ||||
|     int ret; | ||||
|  | ||||
|     uint64_t alloc_cluster_offset; | ||||
| @@ -1144,7 +1118,6 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, | ||||
|  | ||||
|     l2_index = offset_to_l2_index(s, guest_offset); | ||||
|     nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); | ||||
|     assert(nb_clusters <= INT_MAX); | ||||
|  | ||||
|     /* Find L2 entry for the first involved cluster */ | ||||
|     ret = get_cluster_table(bs, guest_offset, &l2_table, &l2_index); | ||||
| @@ -1166,7 +1139,10 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, | ||||
|      * wrong with our code. */ | ||||
|     assert(nb_clusters > 0); | ||||
|  | ||||
|     qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); | ||||
|     ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     /* Allocate, if necessary at a given offset in the image file */ | ||||
|     alloc_cluster_offset = start_of_cluster(s, *host_offset); | ||||
| @@ -1275,7 +1251,7 @@ fail: | ||||
| int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||
|     int *num, uint64_t *host_offset, QCowL2Meta **m) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint64_t start, remaining; | ||||
|     uint64_t cluster_offset; | ||||
|     uint64_t cur_bytes; | ||||
| @@ -1409,7 +1385,7 @@ static int decompress_buffer(uint8_t *out_buf, int out_buf_size, | ||||
|  | ||||
| int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int ret, csize, nb_csectors, sector_offset; | ||||
|     uint64_t coffset; | ||||
|  | ||||
| @@ -1419,8 +1395,7 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) | ||||
|         sector_offset = coffset & 511; | ||||
|         csize = nb_csectors * 512 - sector_offset; | ||||
|         BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED); | ||||
|         ret = bdrv_read(bs->file->bs, coffset >> 9, s->cluster_data, | ||||
|                         nb_csectors); | ||||
|         ret = bdrv_read(bs->file, coffset >> 9, s->cluster_data, nb_csectors); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
| @@ -1439,10 +1414,9 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) | ||||
|  * clusters. | ||||
|  */ | ||||
| static int discard_single_l2(BlockDriverState *bs, uint64_t offset, | ||||
|                              uint64_t nb_clusters, enum qcow2_discard_type type, | ||||
|                              bool full_discard) | ||||
|     unsigned int nb_clusters, enum qcow2_discard_type type, bool full_discard) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint64_t *l2_table; | ||||
|     int l2_index; | ||||
|     int ret; | ||||
| @@ -1455,7 +1429,6 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, | ||||
|  | ||||
|     /* Limit nb_clusters to one L2 table */ | ||||
|     nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); | ||||
|     assert(nb_clusters <= INT_MAX); | ||||
|  | ||||
|     for (i = 0; i < nb_clusters; i++) { | ||||
|         uint64_t old_l2_entry; | ||||
| @@ -1477,7 +1450,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, | ||||
|          */ | ||||
|         switch (qcow2_get_cluster_type(old_l2_entry)) { | ||||
|             case QCOW2_CLUSTER_UNALLOCATED: | ||||
|                 if (full_discard || !bs->backing) { | ||||
|                 if (full_discard || !bs->backing_hd) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 break; | ||||
| @@ -1497,7 +1470,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, | ||||
|         } | ||||
|  | ||||
|         /* First remove L2 entries */ | ||||
|         qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); | ||||
|         qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||||
|         if (!full_discard && s->qcow_version >= 3) { | ||||
|             l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); | ||||
|         } else { | ||||
| @@ -1508,7 +1481,10 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, | ||||
|         qcow2_free_any_clusters(bs, old_l2_entry, 1, type); | ||||
|     } | ||||
|  | ||||
|     qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); | ||||
|     ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     return nb_clusters; | ||||
| } | ||||
| @@ -1516,9 +1492,9 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, | ||||
| int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, | ||||
|     int nb_sectors, enum qcow2_discard_type type, bool full_discard) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint64_t end_offset; | ||||
|     uint64_t nb_clusters; | ||||
|     unsigned int nb_clusters; | ||||
|     int ret; | ||||
|  | ||||
|     end_offset = offset + (nb_sectors << BDRV_SECTOR_BITS); | ||||
| @@ -1560,9 +1536,9 @@ fail: | ||||
|  * clusters. | ||||
|  */ | ||||
| static int zero_single_l2(BlockDriverState *bs, uint64_t offset, | ||||
|                           uint64_t nb_clusters) | ||||
|     unsigned int nb_clusters) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint64_t *l2_table; | ||||
|     int l2_index; | ||||
|     int ret; | ||||
| @@ -1575,7 +1551,6 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, | ||||
|  | ||||
|     /* Limit nb_clusters to one L2 table */ | ||||
|     nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); | ||||
|     assert(nb_clusters <= INT_MAX); | ||||
|  | ||||
|     for (i = 0; i < nb_clusters; i++) { | ||||
|         uint64_t old_offset; | ||||
| @@ -1583,7 +1558,7 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, | ||||
|         old_offset = be64_to_cpu(l2_table[l2_index + i]); | ||||
|  | ||||
|         /* Update L2 entries */ | ||||
|         qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); | ||||
|         qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||||
|         if (old_offset & QCOW_OFLAG_COMPRESSED) { | ||||
|             l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); | ||||
|             qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST); | ||||
| @@ -1592,15 +1567,18 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); | ||||
|     ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     return nb_clusters; | ||||
| } | ||||
|  | ||||
| int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     uint64_t nb_clusters; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     unsigned int nb_clusters; | ||||
|     int ret; | ||||
|  | ||||
|     /* The zero flag is only supported by version 3 and newer */ | ||||
| @@ -1642,10 +1620,9 @@ fail: | ||||
| static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, | ||||
|                                       int l1_size, int64_t *visited_l1_entries, | ||||
|                                       int64_t l1_entries, | ||||
|                                       BlockDriverAmendStatusCB *status_cb, | ||||
|                                       void *cb_opaque) | ||||
|                                       BlockDriverAmendStatusCB *status_cb) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     bool is_active_l1 = (l1_table == s->l1_table); | ||||
|     uint64_t *l2_table = NULL; | ||||
|     int ret; | ||||
| @@ -1654,7 +1631,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, | ||||
|     if (!is_active_l1) { | ||||
|         /* inactive L2 tables require a buffer to be stored in when loading | ||||
|          * them from disk */ | ||||
|         l2_table = qemu_try_blockalign(bs->file->bs, s->cluster_size); | ||||
|         l2_table = qemu_try_blockalign(bs->file, s->cluster_size); | ||||
|         if (l2_table == NULL) { | ||||
|             return -ENOMEM; | ||||
|         } | ||||
| @@ -1669,7 +1646,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, | ||||
|             /* unallocated */ | ||||
|             (*visited_l1_entries)++; | ||||
|             if (status_cb) { | ||||
|                 status_cb(bs, *visited_l1_entries, l1_entries, cb_opaque); | ||||
|                 status_cb(bs, *visited_l1_entries, l1_entries); | ||||
|             } | ||||
|             continue; | ||||
|         } | ||||
| @@ -1688,7 +1665,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, | ||||
|                     (void **)&l2_table); | ||||
|         } else { | ||||
|             /* load inactive L2 tables from disk */ | ||||
|             ret = bdrv_read(bs->file->bs, l2_offset / BDRV_SECTOR_SIZE, | ||||
|             ret = bdrv_read(bs->file, l2_offset / BDRV_SECTOR_SIZE, | ||||
|                     (void *)l2_table, s->cluster_sectors); | ||||
|         } | ||||
|         if (ret < 0) { | ||||
| @@ -1712,7 +1689,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, | ||||
|             } | ||||
|  | ||||
|             if (!preallocated) { | ||||
|                 if (!bs->backing) { | ||||
|                 if (!bs->backing_hd) { | ||||
|                     /* not backed; therefore we can simply deallocate the | ||||
|                      * cluster */ | ||||
|                     l2_table[j] = 0; | ||||
| @@ -1763,7 +1740,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, | ||||
|                 goto fail; | ||||
|             } | ||||
|  | ||||
|             ret = bdrv_write_zeroes(bs->file->bs, offset / BDRV_SECTOR_SIZE, | ||||
|             ret = bdrv_write_zeroes(bs->file, offset / BDRV_SECTOR_SIZE, | ||||
|                                     s->cluster_sectors, 0); | ||||
|             if (ret < 0) { | ||||
|                 if (!preallocated) { | ||||
| @@ -1783,10 +1760,14 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, | ||||
|  | ||||
|         if (is_active_l1) { | ||||
|             if (l2_dirty) { | ||||
|                 qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); | ||||
|                 qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||||
|                 qcow2_cache_depends_on_flush(s->l2_table_cache); | ||||
|             } | ||||
|             qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); | ||||
|             ret = qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table); | ||||
|             if (ret < 0) { | ||||
|                 l2_table = NULL; | ||||
|                 goto fail; | ||||
|             } | ||||
|         } else { | ||||
|             if (l2_dirty) { | ||||
|                 ret = qcow2_pre_write_overlap_check(bs, | ||||
| @@ -1796,7 +1777,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, | ||||
|                     goto fail; | ||||
|                 } | ||||
|  | ||||
|                 ret = bdrv_write(bs->file->bs, l2_offset / BDRV_SECTOR_SIZE, | ||||
|                 ret = bdrv_write(bs->file, l2_offset / BDRV_SECTOR_SIZE, | ||||
|                         (void *)l2_table, s->cluster_sectors); | ||||
|                 if (ret < 0) { | ||||
|                     goto fail; | ||||
| @@ -1806,7 +1787,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, | ||||
|  | ||||
|         (*visited_l1_entries)++; | ||||
|         if (status_cb) { | ||||
|             status_cb(bs, *visited_l1_entries, l1_entries, cb_opaque); | ||||
|             status_cb(bs, *visited_l1_entries, l1_entries); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -1817,7 +1798,12 @@ fail: | ||||
|         if (!is_active_l1) { | ||||
|             qemu_vfree(l2_table); | ||||
|         } else { | ||||
|             if (ret < 0) { | ||||
|                 qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table); | ||||
|             } else { | ||||
|                 ret = qcow2_cache_put(bs, s->l2_table_cache, | ||||
|                         (void **)&l2_table); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return ret; | ||||
| @@ -1830,10 +1816,9 @@ fail: | ||||
|  * qcow2 version which doesn't yet support metadata zero clusters. | ||||
|  */ | ||||
| int qcow2_expand_zero_clusters(BlockDriverState *bs, | ||||
|                                BlockDriverAmendStatusCB *status_cb, | ||||
|                                void *cb_opaque) | ||||
|                                BlockDriverAmendStatusCB *status_cb) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint64_t *l1_table = NULL; | ||||
|     int64_t l1_entries = 0, visited_l1_entries = 0; | ||||
|     int ret; | ||||
| @@ -1848,7 +1833,7 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs, | ||||
|  | ||||
|     ret = expand_zero_clusters_in_l1(bs, s->l1_table, s->l1_size, | ||||
|                                      &visited_l1_entries, l1_entries, | ||||
|                                      status_cb, cb_opaque); | ||||
|                                      status_cb); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
| @@ -1871,9 +1856,8 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs, | ||||
|  | ||||
|         l1_table = g_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE); | ||||
|  | ||||
|         ret = bdrv_read(bs->file->bs, | ||||
|                         s->snapshots[i].l1_table_offset / BDRV_SECTOR_SIZE, | ||||
|                         (void *)l1_table, l1_sectors); | ||||
|         ret = bdrv_read(bs->file, s->snapshots[i].l1_table_offset / | ||||
|                 BDRV_SECTOR_SIZE, (void *)l1_table, l1_sectors); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
|         } | ||||
| @@ -1884,7 +1868,7 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs, | ||||
|  | ||||
|         ret = expand_zero_clusters_in_l1(bs, l1_table, s->snapshots[i].l1_size, | ||||
|                                          &visited_l1_entries, l1_entries, | ||||
|                                          status_cb, cb_opaque); | ||||
|                                          status_cb); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
|         } | ||||
|   | ||||
| @@ -22,7 +22,6 @@ | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "block/block_int.h" | ||||
| #include "block/qcow2.h" | ||||
| @@ -83,7 +82,7 @@ static Qcow2SetRefcountFunc *const set_refcount_funcs[] = { | ||||
|  | ||||
| int qcow2_refcount_init(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     unsigned int refcount_table_size2, i; | ||||
|     int ret; | ||||
|  | ||||
| @@ -102,7 +101,7 @@ int qcow2_refcount_init(BlockDriverState *bs) | ||||
|             goto fail; | ||||
|         } | ||||
|         BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_LOAD); | ||||
|         ret = bdrv_pread(bs->file->bs, s->refcount_table_offset, | ||||
|         ret = bdrv_pread(bs->file, s->refcount_table_offset, | ||||
|                          s->refcount_table, refcount_table_size2); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
| @@ -117,7 +116,7 @@ int qcow2_refcount_init(BlockDriverState *bs) | ||||
|  | ||||
| void qcow2_refcount_close(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     g_free(s->refcount_table); | ||||
| } | ||||
|  | ||||
| @@ -215,7 +214,7 @@ static int load_refcount_block(BlockDriverState *bs, | ||||
|                                int64_t refcount_block_offset, | ||||
|                                void **refcount_block) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int ret; | ||||
|  | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_LOAD); | ||||
| @@ -232,7 +231,7 @@ static int load_refcount_block(BlockDriverState *bs, | ||||
| int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, | ||||
|                        uint64_t *refcount) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint64_t refcount_table_index, block_index; | ||||
|     int64_t refcount_block_offset; | ||||
|     int ret; | ||||
| @@ -266,7 +265,10 @@ int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, | ||||
|     block_index = cluster_index & (s->refcount_block_size - 1); | ||||
|     *refcount = s->get_refcount(refcount_block, block_index); | ||||
|  | ||||
|     qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); | ||||
|     ret = qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| @@ -275,7 +277,7 @@ int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, | ||||
|  * Rounds the refcount table size up to avoid growing the table for each single | ||||
|  * refcount block that is allocated. | ||||
|  */ | ||||
| static unsigned int next_refcount_table_size(BDRVQcow2State *s, | ||||
| static unsigned int next_refcount_table_size(BDRVQcowState *s, | ||||
|     unsigned int min_size) | ||||
| { | ||||
|     unsigned int min_clusters = (min_size >> (s->cluster_bits - 3)) + 1; | ||||
| @@ -291,7 +293,7 @@ static unsigned int next_refcount_table_size(BDRVQcow2State *s, | ||||
|  | ||||
|  | ||||
| /* Checks if two offsets are described by the same refcount block */ | ||||
| static int in_same_refcount_block(BDRVQcow2State *s, uint64_t offset_a, | ||||
| static int in_same_refcount_block(BDRVQcowState *s, uint64_t offset_a, | ||||
|     uint64_t offset_b) | ||||
| { | ||||
|     uint64_t block_a = offset_a >> (s->cluster_bits + s->refcount_block_bits); | ||||
| @@ -309,7 +311,7 @@ static int in_same_refcount_block(BDRVQcow2State *s, uint64_t offset_a, | ||||
| static int alloc_refcount_block(BlockDriverState *bs, | ||||
|                                 int64_t cluster_index, void **refcount_block) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     unsigned int refcount_table_index; | ||||
|     int ret; | ||||
|  | ||||
| @@ -422,7 +424,7 @@ static int alloc_refcount_block(BlockDriverState *bs, | ||||
|  | ||||
|     /* Now the new refcount block needs to be written to disk */ | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE); | ||||
|     qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, *refcount_block); | ||||
|     qcow2_cache_entry_mark_dirty(s->refcount_block_cache, *refcount_block); | ||||
|     ret = qcow2_cache_flush(bs, s->refcount_block_cache); | ||||
|     if (ret < 0) { | ||||
|         goto fail_block; | ||||
| @@ -432,7 +434,7 @@ static int alloc_refcount_block(BlockDriverState *bs, | ||||
|     if (refcount_table_index < s->refcount_table_size) { | ||||
|         uint64_t data64 = cpu_to_be64(new_block); | ||||
|         BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_HOOKUP); | ||||
|         ret = bdrv_pwrite_sync(bs->file->bs, | ||||
|         ret = bdrv_pwrite_sync(bs->file, | ||||
|             s->refcount_table_offset + refcount_table_index * sizeof(uint64_t), | ||||
|             &data64, sizeof(data64)); | ||||
|         if (ret < 0) { | ||||
| @@ -446,7 +448,10 @@ static int alloc_refcount_block(BlockDriverState *bs, | ||||
|         return -EAGAIN; | ||||
|     } | ||||
|  | ||||
|     qcow2_cache_put(bs, s->refcount_block_cache, refcount_block); | ||||
|     ret = qcow2_cache_put(bs, s->refcount_block_cache, refcount_block); | ||||
|     if (ret < 0) { | ||||
|         goto fail_block; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * If we come here, we need to grow the refcount table. Again, a new | ||||
| @@ -536,7 +541,7 @@ static int alloc_refcount_block(BlockDriverState *bs, | ||||
|  | ||||
|     /* Write refcount blocks to disk */ | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS); | ||||
|     ret = bdrv_pwrite_sync(bs->file->bs, meta_offset, new_blocks, | ||||
|     ret = bdrv_pwrite_sync(bs->file, meta_offset, new_blocks, | ||||
|         blocks_clusters * s->cluster_size); | ||||
|     g_free(new_blocks); | ||||
|     new_blocks = NULL; | ||||
| @@ -550,7 +555,7 @@ static int alloc_refcount_block(BlockDriverState *bs, | ||||
|     } | ||||
|  | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE); | ||||
|     ret = bdrv_pwrite_sync(bs->file->bs, table_offset, new_table, | ||||
|     ret = bdrv_pwrite_sync(bs->file, table_offset, new_table, | ||||
|         table_size * sizeof(uint64_t)); | ||||
|     if (ret < 0) { | ||||
|         goto fail_table; | ||||
| @@ -561,16 +566,12 @@ static int alloc_refcount_block(BlockDriverState *bs, | ||||
|     } | ||||
|  | ||||
|     /* Hook up the new refcount table in the qcow2 header */ | ||||
|     struct QEMU_PACKED { | ||||
|         uint64_t d64; | ||||
|         uint32_t d32; | ||||
|     } data; | ||||
|     cpu_to_be64w(&data.d64, table_offset); | ||||
|     cpu_to_be32w(&data.d32, table_clusters); | ||||
|     uint8_t data[12]; | ||||
|     cpu_to_be64w((uint64_t*)data, table_offset); | ||||
|     cpu_to_be32w((uint32_t*)(data + 8), table_clusters); | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE); | ||||
|     ret = bdrv_pwrite_sync(bs->file->bs, | ||||
|                            offsetof(QCowHeader, refcount_table_offset), | ||||
|                            &data, sizeof(data)); | ||||
|     ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, refcount_table_offset), | ||||
|         data, sizeof(data)); | ||||
|     if (ret < 0) { | ||||
|         goto fail_table; | ||||
|     } | ||||
| @@ -610,7 +611,7 @@ fail_block: | ||||
|  | ||||
| void qcow2_process_discards(BlockDriverState *bs, int ret) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     Qcow2DiscardRegion *d, *next; | ||||
|  | ||||
|     QTAILQ_FOREACH_SAFE(d, &s->discards, next, next) { | ||||
| @@ -618,7 +619,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret) | ||||
|  | ||||
|         /* Discard is optional, ignore the return value */ | ||||
|         if (ret >= 0) { | ||||
|             bdrv_discard(bs->file->bs, | ||||
|             bdrv_discard(bs->file, | ||||
|                          d->offset >> BDRV_SECTOR_BITS, | ||||
|                          d->bytes >> BDRV_SECTOR_BITS); | ||||
|         } | ||||
| @@ -630,7 +631,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret) | ||||
| static void update_refcount_discard(BlockDriverState *bs, | ||||
|                                     uint64_t offset, uint64_t length) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     Qcow2DiscardRegion *d, *p, *next; | ||||
|  | ||||
|     QTAILQ_FOREACH(d, &s->discards, next) { | ||||
| @@ -687,7 +688,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, | ||||
|                                                    bool decrease, | ||||
|                                                    enum qcow2_discard_type type) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int64_t start, last, cluster_offset; | ||||
|     void *refcount_block = NULL; | ||||
|     int64_t old_table_index = -1; | ||||
| @@ -722,8 +723,13 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, | ||||
|         /* Load the refcount block and allocate it if needed */ | ||||
|         if (table_index != old_table_index) { | ||||
|             if (refcount_block) { | ||||
|                 qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); | ||||
|                 ret = qcow2_cache_put(bs, s->refcount_block_cache, | ||||
|                                       &refcount_block); | ||||
|                 if (ret < 0) { | ||||
|                     goto fail; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             ret = alloc_refcount_block(bs, cluster_index, &refcount_block); | ||||
|             if (ret < 0) { | ||||
|                 goto fail; | ||||
| @@ -731,8 +737,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, | ||||
|         } | ||||
|         old_table_index = table_index; | ||||
|  | ||||
|         qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, | ||||
|                                      refcount_block); | ||||
|         qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refcount_block); | ||||
|  | ||||
|         /* we can update the count and save it */ | ||||
|         block_index = cluster_index & (s->refcount_block_size - 1); | ||||
| @@ -768,7 +773,11 @@ fail: | ||||
|  | ||||
|     /* Write last changed block to disk */ | ||||
|     if (refcount_block) { | ||||
|         qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); | ||||
|         int wret; | ||||
|         wret = qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); | ||||
|         if (wret < 0) { | ||||
|             return ret < 0 ? ret : wret; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* | ||||
| @@ -798,7 +807,7 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, | ||||
|                                   uint64_t addend, bool decrease, | ||||
|                                   enum qcow2_discard_type type) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int ret; | ||||
|  | ||||
|     ret = update_refcount(bs, cluster_index << s->cluster_bits, 1, addend, | ||||
| @@ -820,7 +829,7 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, | ||||
| /* return < 0 if error */ | ||||
| static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint64_t i, nb_clusters, refcount; | ||||
|     int ret; | ||||
|  | ||||
| @@ -880,10 +889,10 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size) | ||||
|     return offset; | ||||
| } | ||||
|  | ||||
| int64_t qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, | ||||
|                                 int64_t nb_clusters) | ||||
| int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, | ||||
|     int nb_clusters) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint64_t cluster_index, refcount; | ||||
|     uint64_t i; | ||||
|     int ret; | ||||
| @@ -921,7 +930,7 @@ int64_t qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, | ||||
|    contiguous sectors. size must be <= cluster_size */ | ||||
| int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int64_t offset; | ||||
|     size_t free_in_cluster; | ||||
|     int ret; | ||||
| @@ -1003,7 +1012,7 @@ void qcow2_free_clusters(BlockDriverState *bs, | ||||
| void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry, | ||||
|                              int nb_clusters, enum qcow2_discard_type type) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|  | ||||
|     switch (qcow2_get_cluster_type(l2_entry)) { | ||||
|     case QCOW2_CLUSTER_COMPRESSED: | ||||
| @@ -1047,7 +1056,7 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry, | ||||
| int qcow2_update_snapshot_refcount(BlockDriverState *bs, | ||||
|     int64_t l1_table_offset, int l1_size, int addend) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, refcount; | ||||
|     bool l1_allocated = false; | ||||
|     int64_t old_offset, old_l2_offset; | ||||
| @@ -1073,7 +1082,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, | ||||
|         } | ||||
|         l1_allocated = true; | ||||
|  | ||||
|         ret = bdrv_pread(bs->file->bs, l1_table_offset, l1_table, l1_size2); | ||||
|         ret = bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
|         } | ||||
| @@ -1181,12 +1190,15 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, | ||||
|                             s->refcount_block_cache); | ||||
|                     } | ||||
|                     l2_table[j] = cpu_to_be64(offset); | ||||
|                     qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, | ||||
|                                                  l2_table); | ||||
|                     qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); | ||||
|             ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); | ||||
|             if (ret < 0) { | ||||
|                 goto fail; | ||||
|             } | ||||
|  | ||||
|  | ||||
|             if (addend != 0) { | ||||
|                 ret = qcow2_update_cluster_refcount(bs, l2_offset >> | ||||
| @@ -1226,8 +1238,7 @@ fail: | ||||
|             cpu_to_be64s(&l1_table[i]); | ||||
|         } | ||||
|  | ||||
|         ret = bdrv_pwrite_sync(bs->file->bs, l1_table_offset, | ||||
|                                l1_table, l1_size2); | ||||
|         ret = bdrv_pwrite_sync(bs->file, l1_table_offset, l1_table, l1_size2); | ||||
|  | ||||
|         for (i = 0; i < l1_size; i++) { | ||||
|             be64_to_cpus(&l1_table[i]); | ||||
| @@ -1245,7 +1256,7 @@ fail: | ||||
| /* refcount checking functions */ | ||||
|  | ||||
|  | ||||
| static uint64_t refcount_array_byte_size(BDRVQcow2State *s, uint64_t entries) | ||||
| static size_t refcount_array_byte_size(BDRVQcowState *s, uint64_t entries) | ||||
| { | ||||
|     /* This assertion holds because there is no way we can address more than | ||||
|      * 2^(64 - 9) clusters at once (with cluster size 512 = 2^9, and because | ||||
| @@ -1268,10 +1279,10 @@ static uint64_t refcount_array_byte_size(BDRVQcow2State *s, uint64_t entries) | ||||
|  * refcount array buffer will be aligned to a cluster boundary, and the newly | ||||
|  * allocated area will be zeroed. | ||||
|  */ | ||||
| static int realloc_refcount_array(BDRVQcow2State *s, void **array, | ||||
| static int realloc_refcount_array(BDRVQcowState *s, void **array, | ||||
|                                   int64_t *size, int64_t new_size) | ||||
| { | ||||
|     int64_t old_byte_size, new_byte_size; | ||||
|     size_t old_byte_size, new_byte_size; | ||||
|     void *new_ptr; | ||||
|  | ||||
|     /* Round to clusters so the array can be directly written to disk */ | ||||
| @@ -1287,17 +1298,13 @@ static int realloc_refcount_array(BDRVQcow2State *s, void **array, | ||||
|  | ||||
|     assert(new_byte_size > 0); | ||||
|  | ||||
|     if (new_byte_size > SIZE_MAX) { | ||||
|         return -ENOMEM; | ||||
|     } | ||||
|  | ||||
|     new_ptr = g_try_realloc(*array, new_byte_size); | ||||
|     if (!new_ptr) { | ||||
|         return -ENOMEM; | ||||
|     } | ||||
|  | ||||
|     if (new_byte_size > old_byte_size) { | ||||
|         memset((char *)new_ptr + old_byte_size, 0, | ||||
|         memset((void *)((uintptr_t)new_ptr + old_byte_size), 0, | ||||
|                new_byte_size - old_byte_size); | ||||
|     } | ||||
|  | ||||
| @@ -1310,7 +1317,7 @@ static int realloc_refcount_array(BDRVQcow2State *s, void **array, | ||||
| /* | ||||
|  * Increases the refcount for a range of clusters in a given refcount table. | ||||
|  * This is used to construct a temporary refcount table out of L1 and L2 tables | ||||
|  * which can be compared to the refcount table saved in the image. | ||||
|  * which can be compared the the refcount table saved in the image. | ||||
|  * | ||||
|  * Modifies the number of errors in res. | ||||
|  */ | ||||
| @@ -1320,7 +1327,7 @@ static int inc_refcounts(BlockDriverState *bs, | ||||
|                          int64_t *refcount_table_size, | ||||
|                          int64_t offset, int64_t size) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint64_t start, last, cluster_offset, k, refcount; | ||||
|     int ret; | ||||
|  | ||||
| @@ -1346,9 +1353,6 @@ static int inc_refcounts(BlockDriverState *bs, | ||||
|         if (refcount == s->refcount_max) { | ||||
|             fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64 | ||||
|                     "\n", cluster_offset); | ||||
|             fprintf(stderr, "Use qemu-img amend to increase the refcount entry " | ||||
|                     "width or qemu-img convert to create a clean copy if the " | ||||
|                     "image cannot be opened for writing\n"); | ||||
|             res->corruptions++; | ||||
|             continue; | ||||
|         } | ||||
| @@ -1376,7 +1380,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, | ||||
|                               int64_t *refcount_table_size, int64_t l2_offset, | ||||
|                               int flags) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint64_t *l2_table, l2_entry; | ||||
|     uint64_t next_contiguous_offset = 0; | ||||
|     int i, l2_size, nb_csectors, ret; | ||||
| @@ -1385,7 +1389,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, | ||||
|     l2_size = s->l2_size * sizeof(uint64_t); | ||||
|     l2_table = g_malloc(l2_size); | ||||
|  | ||||
|     ret = bdrv_pread(bs->file->bs, l2_offset, l2_table, l2_size); | ||||
|     ret = bdrv_pread(bs->file, l2_offset, l2_table, l2_size); | ||||
|     if (ret < 0) { | ||||
|         fprintf(stderr, "ERROR: I/O error in check_refcounts_l2\n"); | ||||
|         res->check_errors++; | ||||
| @@ -1496,7 +1500,7 @@ static int check_refcounts_l1(BlockDriverState *bs, | ||||
|                               int64_t l1_table_offset, int l1_size, | ||||
|                               int flags) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint64_t *l1_table = NULL, l2_offset, l1_size2; | ||||
|     int i, ret; | ||||
|  | ||||
| @@ -1517,7 +1521,7 @@ static int check_refcounts_l1(BlockDriverState *bs, | ||||
|             res->check_errors++; | ||||
|             goto fail; | ||||
|         } | ||||
|         ret = bdrv_pread(bs->file->bs, l1_table_offset, l1_table, l1_size2); | ||||
|         ret = bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2); | ||||
|         if (ret < 0) { | ||||
|             fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n"); | ||||
|             res->check_errors++; | ||||
| @@ -1573,7 +1577,7 @@ fail: | ||||
| static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, | ||||
|                               BdrvCheckMode fix) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint64_t *l2_table = qemu_blockalign(bs, s->cluster_size); | ||||
|     int ret; | ||||
|     uint64_t refcount; | ||||
| @@ -1615,7 +1619,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         ret = bdrv_pread(bs->file->bs, l2_offset, l2_table, | ||||
|         ret = bdrv_pread(bs->file, l2_offset, l2_table, | ||||
|                          s->l2_size * sizeof(uint64_t)); | ||||
|         if (ret < 0) { | ||||
|             fprintf(stderr, "ERROR: Could not read L2 table: %s\n", | ||||
| @@ -1667,8 +1671,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, | ||||
|                 goto fail; | ||||
|             } | ||||
|  | ||||
|             ret = bdrv_pwrite(bs->file->bs, l2_offset, l2_table, | ||||
|                               s->cluster_size); | ||||
|             ret = bdrv_pwrite(bs->file, l2_offset, l2_table, s->cluster_size); | ||||
|             if (ret < 0) { | ||||
|                 fprintf(stderr, "ERROR: Could not write L2 table: %s\n", | ||||
|                         strerror(-ret)); | ||||
| @@ -1693,7 +1696,7 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, | ||||
|                            BdrvCheckMode fix, bool *rebuild, | ||||
|                            void **refcount_table, int64_t *nb_clusters) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int64_t i, size; | ||||
|     int ret; | ||||
|  | ||||
| @@ -1723,11 +1726,11 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, | ||||
|                     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) { | ||||
|                     goto resize_fail; | ||||
|                 } | ||||
|                 size = bdrv_getlength(bs->file->bs); | ||||
|                 size = bdrv_getlength(bs->file); | ||||
|                 if (size < 0) { | ||||
|                     ret = size; | ||||
|                     goto resize_fail; | ||||
| @@ -1796,7 +1799,7 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, | ||||
|                                BdrvCheckMode fix, bool *rebuild, | ||||
|                                void **refcount_table, int64_t *nb_clusters) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int64_t i; | ||||
|     QCowSnapshot *sn; | ||||
|     int ret; | ||||
| @@ -1860,7 +1863,7 @@ static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, | ||||
|                               int64_t *highest_cluster, | ||||
|                               void *refcount_table, int64_t nb_clusters) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int64_t i; | ||||
|     uint64_t refcount1, refcount2; | ||||
|     int ret; | ||||
| @@ -1937,7 +1940,7 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs, | ||||
|                                    int64_t *imrt_nb_clusters, | ||||
|                                    int64_t *first_free_cluster) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int64_t cluster = *first_free_cluster, i; | ||||
|     bool first_gap = true; | ||||
|     int contiguous_free_clusters; | ||||
| @@ -2007,7 +2010,7 @@ static int rebuild_refcount_structure(BlockDriverState *bs, | ||||
|                                       void **refcount_table, | ||||
|                                       int64_t *nb_clusters) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int64_t first_free_cluster = 0, reftable_offset = -1, cluster = 0; | ||||
|     int64_t refblock_offset, refblock_start, refblock_index; | ||||
|     uint32_t reftable_size = 0; | ||||
| @@ -2101,7 +2104,7 @@ write_refblocks: | ||||
|         on_disk_refblock = (void *)((char *) *refcount_table + | ||||
|                                     refblock_index * s->cluster_size); | ||||
|  | ||||
|         ret = bdrv_write(bs->file->bs, refblock_offset / BDRV_SECTOR_SIZE, | ||||
|         ret = bdrv_write(bs->file, refblock_offset / BDRV_SECTOR_SIZE, | ||||
|                          on_disk_refblock, s->cluster_sectors); | ||||
|         if (ret < 0) { | ||||
|             fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret)); | ||||
| @@ -2150,7 +2153,7 @@ write_refblocks: | ||||
|     } | ||||
|  | ||||
|     assert(reftable_size < INT_MAX / sizeof(uint64_t)); | ||||
|     ret = bdrv_pwrite(bs->file->bs, reftable_offset, on_disk_reftable, | ||||
|     ret = bdrv_pwrite(bs->file, reftable_offset, on_disk_reftable, | ||||
|                       reftable_size * sizeof(uint64_t)); | ||||
|     if (ret < 0) { | ||||
|         fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret)); | ||||
| @@ -2162,7 +2165,7 @@ write_refblocks: | ||||
|                  reftable_offset); | ||||
|     cpu_to_be32w(&reftable_offset_and_clusters.reftable_clusters, | ||||
|                  size_to_clusters(s, reftable_size * sizeof(uint64_t))); | ||||
|     ret = bdrv_pwrite_sync(bs->file->bs, offsetof(QCowHeader, | ||||
|     ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, | ||||
|                                               refcount_table_offset), | ||||
|                            &reftable_offset_and_clusters, | ||||
|                            sizeof(reftable_offset_and_clusters)); | ||||
| @@ -2194,14 +2197,14 @@ fail: | ||||
| int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, | ||||
|                           BdrvCheckMode fix) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     BdrvCheckResult pre_compare_res; | ||||
|     int64_t size, highest_cluster, nb_clusters; | ||||
|     void *refcount_table = NULL; | ||||
|     bool rebuild = false; | ||||
|     int ret; | ||||
|  | ||||
|     size = bdrv_getlength(bs->file->bs); | ||||
|     size = bdrv_getlength(bs->file); | ||||
|     if (size < 0) { | ||||
|         res->check_errors++; | ||||
|         return size; | ||||
| @@ -2331,7 +2334,7 @@ fail: | ||||
| int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, | ||||
|                                  int64_t size) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int chk = s->overlap_check & ~ign; | ||||
|     int i, j; | ||||
|  | ||||
| @@ -2410,7 +2413,7 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, | ||||
|                 return -ENOMEM; | ||||
|             } | ||||
|  | ||||
|             ret = bdrv_pread(bs->file->bs, l1_ofs, l1, l1_sz2); | ||||
|             ret = bdrv_pread(bs->file, l1_ofs, l1, l1_sz2); | ||||
|             if (ret < 0) { | ||||
|                 g_free(l1); | ||||
|                 return ret; | ||||
| @@ -2460,7 +2463,7 @@ int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } else if (ret > 0) { | ||||
|         int metadata_ol_bitnr = ctz32(ret); | ||||
|         int metadata_ol_bitnr = ffs(ret) - 1; | ||||
|         assert(metadata_ol_bitnr < QCOW2_OL_MAX_BITNR); | ||||
|  | ||||
|         qcow2_signal_corruption(bs, true, offset, size, "Preventing invalid " | ||||
| @@ -2471,450 +2474,3 @@ int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* A pointer to a function of this type is given to walk_over_reftable(). That | ||||
|  * function will create refblocks and pass them to a RefblockFinishOp once they | ||||
|  * are completed (@refblock). @refblock_empty is set if the refblock is | ||||
|  * completely empty. | ||||
|  * | ||||
|  * Along with the refblock, a corresponding reftable entry is passed, in the | ||||
|  * reftable @reftable (which may be reallocated) at @reftable_index. | ||||
|  * | ||||
|  * @allocated should be set to true if a new cluster has been allocated. | ||||
|  */ | ||||
| typedef int (RefblockFinishOp)(BlockDriverState *bs, uint64_t **reftable, | ||||
|                                uint64_t reftable_index, uint64_t *reftable_size, | ||||
|                                void *refblock, bool refblock_empty, | ||||
|                                bool *allocated, Error **errp); | ||||
|  | ||||
| /** | ||||
|  * This "operation" for walk_over_reftable() allocates the refblock on disk (if | ||||
|  * it is not empty) and inserts its offset into the new reftable. The size of | ||||
|  * this new reftable is increased as required. | ||||
|  */ | ||||
| static int alloc_refblock(BlockDriverState *bs, uint64_t **reftable, | ||||
|                           uint64_t reftable_index, uint64_t *reftable_size, | ||||
|                           void *refblock, bool refblock_empty, bool *allocated, | ||||
|                           Error **errp) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     int64_t offset; | ||||
|  | ||||
|     if (!refblock_empty && reftable_index >= *reftable_size) { | ||||
|         uint64_t *new_reftable; | ||||
|         uint64_t new_reftable_size; | ||||
|  | ||||
|         new_reftable_size = ROUND_UP(reftable_index + 1, | ||||
|                                      s->cluster_size / sizeof(uint64_t)); | ||||
|         if (new_reftable_size > QCOW_MAX_REFTABLE_SIZE / sizeof(uint64_t)) { | ||||
|             error_setg(errp, | ||||
|                        "This operation would make the refcount table grow " | ||||
|                        "beyond the maximum size supported by QEMU, aborting"); | ||||
|             return -ENOTSUP; | ||||
|         } | ||||
|  | ||||
|         new_reftable = g_try_realloc(*reftable, new_reftable_size * | ||||
|                                                 sizeof(uint64_t)); | ||||
|         if (!new_reftable) { | ||||
|             error_setg(errp, "Failed to increase reftable buffer size"); | ||||
|             return -ENOMEM; | ||||
|         } | ||||
|  | ||||
|         memset(new_reftable + *reftable_size, 0, | ||||
|                (new_reftable_size - *reftable_size) * sizeof(uint64_t)); | ||||
|  | ||||
|         *reftable      = new_reftable; | ||||
|         *reftable_size = new_reftable_size; | ||||
|     } | ||||
|  | ||||
|     if (!refblock_empty && !(*reftable)[reftable_index]) { | ||||
|         offset = qcow2_alloc_clusters(bs, s->cluster_size); | ||||
|         if (offset < 0) { | ||||
|             error_setg_errno(errp, -offset, "Failed to allocate refblock"); | ||||
|             return offset; | ||||
|         } | ||||
|         (*reftable)[reftable_index] = offset; | ||||
|         *allocated = true; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * This "operation" for walk_over_reftable() writes the refblock to disk at the | ||||
|  * offset specified by the new reftable's entry. It does not modify the new | ||||
|  * reftable or change any refcounts. | ||||
|  */ | ||||
| static int flush_refblock(BlockDriverState *bs, uint64_t **reftable, | ||||
|                           uint64_t reftable_index, uint64_t *reftable_size, | ||||
|                           void *refblock, bool refblock_empty, bool *allocated, | ||||
|                           Error **errp) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     int64_t offset; | ||||
|     int ret; | ||||
|  | ||||
|     if (reftable_index < *reftable_size && (*reftable)[reftable_index]) { | ||||
|         offset = (*reftable)[reftable_index]; | ||||
|  | ||||
|         ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size); | ||||
|         if (ret < 0) { | ||||
|             error_setg_errno(errp, -ret, "Overlap check failed"); | ||||
|             return ret; | ||||
|         } | ||||
|  | ||||
|         ret = bdrv_pwrite(bs->file->bs, offset, refblock, s->cluster_size); | ||||
|         if (ret < 0) { | ||||
|             error_setg_errno(errp, -ret, "Failed to write refblock"); | ||||
|             return ret; | ||||
|         } | ||||
|     } else { | ||||
|         assert(refblock_empty); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * This function walks over the existing reftable and every referenced refblock; | ||||
|  * if @new_set_refcount is non-NULL, it is called for every refcount entry to | ||||
|  * create an equal new entry in the passed @new_refblock. Once that | ||||
|  * @new_refblock is completely filled, @operation will be called. | ||||
|  * | ||||
|  * @status_cb and @cb_opaque are used for the amend operation's status callback. | ||||
|  * @index is the index of the walk_over_reftable() calls and @total is the total | ||||
|  * number of walk_over_reftable() calls per amend operation. Both are used for | ||||
|  * calculating the parameters for the status callback. | ||||
|  * | ||||
|  * @allocated is set to true if a new cluster has been allocated. | ||||
|  */ | ||||
| static int walk_over_reftable(BlockDriverState *bs, uint64_t **new_reftable, | ||||
|                               uint64_t *new_reftable_index, | ||||
|                               uint64_t *new_reftable_size, | ||||
|                               void *new_refblock, int new_refblock_size, | ||||
|                               int new_refcount_bits, | ||||
|                               RefblockFinishOp *operation, bool *allocated, | ||||
|                               Qcow2SetRefcountFunc *new_set_refcount, | ||||
|                               BlockDriverAmendStatusCB *status_cb, | ||||
|                               void *cb_opaque, int index, int total, | ||||
|                               Error **errp) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     uint64_t reftable_index; | ||||
|     bool new_refblock_empty = true; | ||||
|     int refblock_index; | ||||
|     int new_refblock_index = 0; | ||||
|     int ret; | ||||
|  | ||||
|     for (reftable_index = 0; reftable_index < s->refcount_table_size; | ||||
|          reftable_index++) | ||||
|     { | ||||
|         uint64_t refblock_offset = s->refcount_table[reftable_index] | ||||
|                                  & REFT_OFFSET_MASK; | ||||
|  | ||||
|         status_cb(bs, (uint64_t)index * s->refcount_table_size + reftable_index, | ||||
|                   (uint64_t)total * s->refcount_table_size, cb_opaque); | ||||
|  | ||||
|         if (refblock_offset) { | ||||
|             void *refblock; | ||||
|  | ||||
|             if (offset_into_cluster(s, refblock_offset)) { | ||||
|                 qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#" | ||||
|                                         PRIx64 " unaligned (reftable index: %#" | ||||
|                                         PRIx64 ")", refblock_offset, | ||||
|                                         reftable_index); | ||||
|                 error_setg(errp, | ||||
|                            "Image is corrupt (unaligned refblock offset)"); | ||||
|                 return -EIO; | ||||
|             } | ||||
|  | ||||
|             ret = qcow2_cache_get(bs, s->refcount_block_cache, refblock_offset, | ||||
|                                   &refblock); | ||||
|             if (ret < 0) { | ||||
|                 error_setg_errno(errp, -ret, "Failed to retrieve refblock"); | ||||
|                 return ret; | ||||
|             } | ||||
|  | ||||
|             for (refblock_index = 0; refblock_index < s->refcount_block_size; | ||||
|                  refblock_index++) | ||||
|             { | ||||
|                 uint64_t refcount; | ||||
|  | ||||
|                 if (new_refblock_index >= new_refblock_size) { | ||||
|                     /* new_refblock is now complete */ | ||||
|                     ret = operation(bs, new_reftable, *new_reftable_index, | ||||
|                                     new_reftable_size, new_refblock, | ||||
|                                     new_refblock_empty, allocated, errp); | ||||
|                     if (ret < 0) { | ||||
|                         qcow2_cache_put(bs, s->refcount_block_cache, &refblock); | ||||
|                         return ret; | ||||
|                     } | ||||
|  | ||||
|                     (*new_reftable_index)++; | ||||
|                     new_refblock_index = 0; | ||||
|                     new_refblock_empty = true; | ||||
|                 } | ||||
|  | ||||
|                 refcount = s->get_refcount(refblock, refblock_index); | ||||
|                 if (new_refcount_bits < 64 && refcount >> new_refcount_bits) { | ||||
|                     uint64_t offset; | ||||
|  | ||||
|                     qcow2_cache_put(bs, s->refcount_block_cache, &refblock); | ||||
|  | ||||
|                     offset = ((reftable_index << s->refcount_block_bits) | ||||
|                               + refblock_index) << s->cluster_bits; | ||||
|  | ||||
|                     error_setg(errp, "Cannot decrease refcount entry width to " | ||||
|                                "%i bits: Cluster at offset %#" PRIx64 " has a " | ||||
|                                "refcount of %" PRIu64, new_refcount_bits, | ||||
|                                offset, refcount); | ||||
|                     return -EINVAL; | ||||
|                 } | ||||
|  | ||||
|                 if (new_set_refcount) { | ||||
|                     new_set_refcount(new_refblock, new_refblock_index++, | ||||
|                                      refcount); | ||||
|                 } else { | ||||
|                     new_refblock_index++; | ||||
|                 } | ||||
|                 new_refblock_empty = new_refblock_empty && refcount == 0; | ||||
|             } | ||||
|  | ||||
|             qcow2_cache_put(bs, s->refcount_block_cache, &refblock); | ||||
|         } else { | ||||
|             /* No refblock means every refcount is 0 */ | ||||
|             for (refblock_index = 0; refblock_index < s->refcount_block_size; | ||||
|                  refblock_index++) | ||||
|             { | ||||
|                 if (new_refblock_index >= new_refblock_size) { | ||||
|                     /* new_refblock is now complete */ | ||||
|                     ret = operation(bs, new_reftable, *new_reftable_index, | ||||
|                                     new_reftable_size, new_refblock, | ||||
|                                     new_refblock_empty, allocated, errp); | ||||
|                     if (ret < 0) { | ||||
|                         return ret; | ||||
|                     } | ||||
|  | ||||
|                     (*new_reftable_index)++; | ||||
|                     new_refblock_index = 0; | ||||
|                     new_refblock_empty = true; | ||||
|                 } | ||||
|  | ||||
|                 if (new_set_refcount) { | ||||
|                     new_set_refcount(new_refblock, new_refblock_index++, 0); | ||||
|                 } else { | ||||
|                     new_refblock_index++; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (new_refblock_index > 0) { | ||||
|         /* Complete the potentially existing partially filled final refblock */ | ||||
|         if (new_set_refcount) { | ||||
|             for (; new_refblock_index < new_refblock_size; | ||||
|                  new_refblock_index++) | ||||
|             { | ||||
|                 new_set_refcount(new_refblock, new_refblock_index, 0); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         ret = operation(bs, new_reftable, *new_reftable_index, | ||||
|                         new_reftable_size, new_refblock, new_refblock_empty, | ||||
|                         allocated, errp); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
|  | ||||
|         (*new_reftable_index)++; | ||||
|     } | ||||
|  | ||||
|     status_cb(bs, (uint64_t)(index + 1) * s->refcount_table_size, | ||||
|               (uint64_t)total * s->refcount_table_size, cb_opaque); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, | ||||
|                                 BlockDriverAmendStatusCB *status_cb, | ||||
|                                 void *cb_opaque, Error **errp) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     Qcow2GetRefcountFunc *new_get_refcount; | ||||
|     Qcow2SetRefcountFunc *new_set_refcount; | ||||
|     void *new_refblock = qemu_blockalign(bs->file->bs, s->cluster_size); | ||||
|     uint64_t *new_reftable = NULL, new_reftable_size = 0; | ||||
|     uint64_t *old_reftable, old_reftable_size, old_reftable_offset; | ||||
|     uint64_t new_reftable_index = 0; | ||||
|     uint64_t i; | ||||
|     int64_t new_reftable_offset = 0, allocated_reftable_size = 0; | ||||
|     int new_refblock_size, new_refcount_bits = 1 << refcount_order; | ||||
|     int old_refcount_order; | ||||
|     int walk_index = 0; | ||||
|     int ret; | ||||
|     bool new_allocation; | ||||
|  | ||||
|     assert(s->qcow_version >= 3); | ||||
|     assert(refcount_order >= 0 && refcount_order <= 6); | ||||
|  | ||||
|     /* see qcow2_open() */ | ||||
|     new_refblock_size = 1 << (s->cluster_bits - (refcount_order - 3)); | ||||
|  | ||||
|     new_get_refcount = get_refcount_funcs[refcount_order]; | ||||
|     new_set_refcount = set_refcount_funcs[refcount_order]; | ||||
|  | ||||
|  | ||||
|     do { | ||||
|         int total_walks; | ||||
|  | ||||
|         new_allocation = false; | ||||
|  | ||||
|         /* At least we have to do this walk and the one which writes the | ||||
|          * refblocks; also, at least we have to do this loop here at least | ||||
|          * twice (normally), first to do the allocations, and second to | ||||
|          * determine that everything is correctly allocated, this then makes | ||||
|          * three walks in total */ | ||||
|         total_walks = MAX(walk_index + 2, 3); | ||||
|  | ||||
|         /* First, allocate the structures so they are present in the refcount | ||||
|          * structures */ | ||||
|         ret = walk_over_reftable(bs, &new_reftable, &new_reftable_index, | ||||
|                                  &new_reftable_size, NULL, new_refblock_size, | ||||
|                                  new_refcount_bits, &alloc_refblock, | ||||
|                                  &new_allocation, NULL, status_cb, cb_opaque, | ||||
|                                  walk_index++, total_walks, errp); | ||||
|         if (ret < 0) { | ||||
|             goto done; | ||||
|         } | ||||
|  | ||||
|         new_reftable_index = 0; | ||||
|  | ||||
|         if (new_allocation) { | ||||
|             if (new_reftable_offset) { | ||||
|                 qcow2_free_clusters(bs, new_reftable_offset, | ||||
|                                     allocated_reftable_size * sizeof(uint64_t), | ||||
|                                     QCOW2_DISCARD_NEVER); | ||||
|             } | ||||
|  | ||||
|             new_reftable_offset = qcow2_alloc_clusters(bs, new_reftable_size * | ||||
|                                                            sizeof(uint64_t)); | ||||
|             if (new_reftable_offset < 0) { | ||||
|                 error_setg_errno(errp, -new_reftable_offset, | ||||
|                                  "Failed to allocate the new reftable"); | ||||
|                 ret = new_reftable_offset; | ||||
|                 goto done; | ||||
|             } | ||||
|             allocated_reftable_size = new_reftable_size; | ||||
|         } | ||||
|     } while (new_allocation); | ||||
|  | ||||
|     /* Second, write the new refblocks */ | ||||
|     ret = walk_over_reftable(bs, &new_reftable, &new_reftable_index, | ||||
|                              &new_reftable_size, new_refblock, | ||||
|                              new_refblock_size, new_refcount_bits, | ||||
|                              &flush_refblock, &new_allocation, new_set_refcount, | ||||
|                              status_cb, cb_opaque, walk_index, walk_index + 1, | ||||
|                              errp); | ||||
|     if (ret < 0) { | ||||
|         goto done; | ||||
|     } | ||||
|     assert(!new_allocation); | ||||
|  | ||||
|  | ||||
|     /* Write the new reftable */ | ||||
|     ret = qcow2_pre_write_overlap_check(bs, 0, new_reftable_offset, | ||||
|                                         new_reftable_size * sizeof(uint64_t)); | ||||
|     if (ret < 0) { | ||||
|         error_setg_errno(errp, -ret, "Overlap check failed"); | ||||
|         goto done; | ||||
|     } | ||||
|  | ||||
|     for (i = 0; i < new_reftable_size; i++) { | ||||
|         cpu_to_be64s(&new_reftable[i]); | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_pwrite(bs->file->bs, new_reftable_offset, new_reftable, | ||||
|                       new_reftable_size * sizeof(uint64_t)); | ||||
|  | ||||
|     for (i = 0; i < new_reftable_size; i++) { | ||||
|         be64_to_cpus(&new_reftable[i]); | ||||
|     } | ||||
|  | ||||
|     if (ret < 0) { | ||||
|         error_setg_errno(errp, -ret, "Failed to write the new reftable"); | ||||
|         goto done; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /* Empty the refcount cache */ | ||||
|     ret = qcow2_cache_flush(bs, s->refcount_block_cache); | ||||
|     if (ret < 0) { | ||||
|         error_setg_errno(errp, -ret, "Failed to flush the refblock cache"); | ||||
|         goto done; | ||||
|     } | ||||
|  | ||||
|     /* Update the image header to point to the new reftable; this only updates | ||||
|      * the fields which are relevant to qcow2_update_header(); other fields | ||||
|      * such as s->refcount_table or s->refcount_bits stay stale for now | ||||
|      * (because we have to restore everything if qcow2_update_header() fails) */ | ||||
|     old_refcount_order  = s->refcount_order; | ||||
|     old_reftable_size   = s->refcount_table_size; | ||||
|     old_reftable_offset = s->refcount_table_offset; | ||||
|  | ||||
|     s->refcount_order        = refcount_order; | ||||
|     s->refcount_table_size   = new_reftable_size; | ||||
|     s->refcount_table_offset = new_reftable_offset; | ||||
|  | ||||
|     ret = qcow2_update_header(bs); | ||||
|     if (ret < 0) { | ||||
|         s->refcount_order        = old_refcount_order; | ||||
|         s->refcount_table_size   = old_reftable_size; | ||||
|         s->refcount_table_offset = old_reftable_offset; | ||||
|         error_setg_errno(errp, -ret, "Failed to update the qcow2 header"); | ||||
|         goto done; | ||||
|     } | ||||
|  | ||||
|     /* Now update the rest of the in-memory information */ | ||||
|     old_reftable = s->refcount_table; | ||||
|     s->refcount_table = new_reftable; | ||||
|  | ||||
|     s->refcount_bits = 1 << refcount_order; | ||||
|     s->refcount_max = UINT64_C(1) << (s->refcount_bits - 1); | ||||
|     s->refcount_max += s->refcount_max - 1; | ||||
|  | ||||
|     s->refcount_block_bits = s->cluster_bits - (refcount_order - 3); | ||||
|     s->refcount_block_size = 1 << s->refcount_block_bits; | ||||
|  | ||||
|     s->get_refcount = new_get_refcount; | ||||
|     s->set_refcount = new_set_refcount; | ||||
|  | ||||
|     /* For cleaning up all old refblocks and the old reftable below the "done" | ||||
|      * label */ | ||||
|     new_reftable        = old_reftable; | ||||
|     new_reftable_size   = old_reftable_size; | ||||
|     new_reftable_offset = old_reftable_offset; | ||||
|  | ||||
| done: | ||||
|     if (new_reftable) { | ||||
|         /* On success, new_reftable actually points to the old reftable (and | ||||
|          * new_reftable_size is the old reftable's size); but that is just | ||||
|          * fine */ | ||||
|         for (i = 0; i < new_reftable_size; i++) { | ||||
|             uint64_t offset = new_reftable[i] & REFT_OFFSET_MASK; | ||||
|             if (offset) { | ||||
|                 qcow2_free_clusters(bs, offset, s->cluster_size, | ||||
|                                     QCOW2_DISCARD_OTHER); | ||||
|             } | ||||
|         } | ||||
|         g_free(new_reftable); | ||||
|  | ||||
|         if (new_reftable_offset > 0) { | ||||
|             qcow2_free_clusters(bs, new_reftable_offset, | ||||
|                                 new_reftable_size * sizeof(uint64_t), | ||||
|                                 QCOW2_DISCARD_OTHER); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     qemu_vfree(new_refblock); | ||||
|     return ret; | ||||
| } | ||||
|   | ||||
| @@ -22,15 +22,13 @@ | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "block/block_int.h" | ||||
| #include "block/qcow2.h" | ||||
| #include "qemu/error-report.h" | ||||
|  | ||||
| void qcow2_free_snapshots(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int i; | ||||
|  | ||||
|     for(i = 0; i < s->nb_snapshots; i++) { | ||||
| @@ -44,7 +42,7 @@ void qcow2_free_snapshots(BlockDriverState *bs) | ||||
|  | ||||
| int qcow2_read_snapshots(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     QCowSnapshotHeader h; | ||||
|     QCowSnapshotExtraData extra; | ||||
|     QCowSnapshot *sn; | ||||
| @@ -65,7 +63,7 @@ int qcow2_read_snapshots(BlockDriverState *bs) | ||||
|     for(i = 0; i < s->nb_snapshots; i++) { | ||||
|         /* Read statically sized part of the snapshot header */ | ||||
|         offset = align_offset(offset, 8); | ||||
|         ret = bdrv_pread(bs->file->bs, offset, &h, sizeof(h)); | ||||
|         ret = bdrv_pread(bs->file, offset, &h, sizeof(h)); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
|         } | ||||
| @@ -84,7 +82,7 @@ int qcow2_read_snapshots(BlockDriverState *bs) | ||||
|         name_size = be16_to_cpu(h.name_size); | ||||
|  | ||||
|         /* Read extra data */ | ||||
|         ret = bdrv_pread(bs->file->bs, offset, &extra, | ||||
|         ret = bdrv_pread(bs->file, offset, &extra, | ||||
|                          MIN(sizeof(extra), extra_data_size)); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
| @@ -103,7 +101,7 @@ int qcow2_read_snapshots(BlockDriverState *bs) | ||||
|  | ||||
|         /* Read snapshot ID */ | ||||
|         sn->id_str = g_malloc(id_str_size + 1); | ||||
|         ret = bdrv_pread(bs->file->bs, offset, sn->id_str, id_str_size); | ||||
|         ret = bdrv_pread(bs->file, offset, sn->id_str, id_str_size); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
|         } | ||||
| @@ -112,7 +110,7 @@ int qcow2_read_snapshots(BlockDriverState *bs) | ||||
|  | ||||
|         /* Read snapshot name */ | ||||
|         sn->name = g_malloc(name_size + 1); | ||||
|         ret = bdrv_pread(bs->file->bs, offset, sn->name, name_size); | ||||
|         ret = bdrv_pread(bs->file, offset, sn->name, name_size); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
|         } | ||||
| @@ -137,7 +135,7 @@ fail: | ||||
| /* add at the end of the file a new list of snapshots */ | ||||
| static int qcow2_write_snapshots(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     QCowSnapshot *sn; | ||||
|     QCowSnapshotHeader h; | ||||
|     QCowSnapshotExtraData extra; | ||||
| @@ -215,25 +213,25 @@ static int qcow2_write_snapshots(BlockDriverState *bs) | ||||
|         h.name_size = cpu_to_be16(name_size); | ||||
|         offset = align_offset(offset, 8); | ||||
|  | ||||
|         ret = bdrv_pwrite(bs->file->bs, offset, &h, sizeof(h)); | ||||
|         ret = bdrv_pwrite(bs->file, offset, &h, sizeof(h)); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
|         } | ||||
|         offset += sizeof(h); | ||||
|  | ||||
|         ret = bdrv_pwrite(bs->file->bs, offset, &extra, sizeof(extra)); | ||||
|         ret = bdrv_pwrite(bs->file, offset, &extra, sizeof(extra)); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
|         } | ||||
|         offset += sizeof(extra); | ||||
|  | ||||
|         ret = bdrv_pwrite(bs->file->bs, offset, sn->id_str, id_str_size); | ||||
|         ret = bdrv_pwrite(bs->file, offset, sn->id_str, id_str_size); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
|         } | ||||
|         offset += id_str_size; | ||||
|  | ||||
|         ret = bdrv_pwrite(bs->file->bs, offset, sn->name, name_size); | ||||
|         ret = bdrv_pwrite(bs->file, offset, sn->name, name_size); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
|         } | ||||
| @@ -255,7 +253,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs) | ||||
|     header_data.nb_snapshots        = cpu_to_be32(s->nb_snapshots); | ||||
|     header_data.snapshots_offset    = cpu_to_be64(snapshots_offset); | ||||
|  | ||||
|     ret = bdrv_pwrite_sync(bs->file->bs, offsetof(QCowHeader, nb_snapshots), | ||||
|     ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots), | ||||
|                            &header_data, sizeof(header_data)); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
| @@ -279,7 +277,7 @@ fail: | ||||
| static void find_new_snapshot_id(BlockDriverState *bs, | ||||
|                                  char *id_str, int id_str_size) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     QCowSnapshot *sn; | ||||
|     int i; | ||||
|     unsigned long id, id_max = 0; | ||||
| @@ -297,7 +295,7 @@ static int find_snapshot_by_id_and_name(BlockDriverState *bs, | ||||
|                                         const char *id, | ||||
|                                         const char *name) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int i; | ||||
|  | ||||
|     if (id && name) { | ||||
| @@ -339,7 +337,7 @@ static int find_snapshot_by_id_or_name(BlockDriverState *bs, | ||||
| /* if no id is provided, a new one is constructed */ | ||||
| int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     QCowSnapshot *new_snapshot_list = NULL; | ||||
|     QCowSnapshot *old_snapshot_list = NULL; | ||||
|     QCowSnapshot sn1, *sn = &sn1; | ||||
| @@ -353,8 +351,10 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) | ||||
|  | ||||
|     memset(sn, 0, sizeof(*sn)); | ||||
|  | ||||
|     /* Generate an ID */ | ||||
|     /* Generate an ID if it wasn't passed */ | ||||
|     if (sn_info->id_str[0] == '\0') { | ||||
|         find_new_snapshot_id(bs, sn_info->id_str, sizeof(sn_info->id_str)); | ||||
|     } | ||||
|  | ||||
|     /* Check that the ID is unique */ | ||||
|     if (find_snapshot_by_id_and_name(bs, sn_info->id_str, NULL) >= 0) { | ||||
| @@ -397,7 +397,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_pwrite(bs->file->bs, sn->l1_table_offset, l1_table, | ||||
|     ret = bdrv_pwrite(bs->file, sn->l1_table_offset, l1_table, | ||||
|                       s->l1_size * sizeof(uint64_t)); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
| @@ -462,7 +462,7 @@ fail: | ||||
| /* copy the snapshot 'snapshot_name' into the current disk image */ | ||||
| int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     QCowSnapshot *sn; | ||||
|     int i, snapshot_index; | ||||
|     int cur_l1_bytes, sn_l1_bytes; | ||||
| @@ -510,8 +510,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_pread(bs->file->bs, sn->l1_table_offset, | ||||
|                      sn_l1_table, sn_l1_bytes); | ||||
|     ret = bdrv_pread(bs->file, sn->l1_table_offset, sn_l1_table, sn_l1_bytes); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
| @@ -528,7 +527,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_pwrite_sync(bs->file->bs, s->l1_table_offset, sn_l1_table, | ||||
|     ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset, sn_l1_table, | ||||
|                            cur_l1_bytes); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
| @@ -589,7 +588,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, | ||||
|                           const char *name, | ||||
|                           Error **errp) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     QCowSnapshot sn; | ||||
|     int snapshot_index, ret; | ||||
|  | ||||
| @@ -652,7 +651,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, | ||||
|  | ||||
| int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     QEMUSnapshotInfo *sn_tab, *sn_info; | ||||
|     QCowSnapshot *sn; | ||||
|     int i; | ||||
| @@ -685,7 +684,7 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, | ||||
|                             Error **errp) | ||||
| { | ||||
|     int i, snapshot_index; | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     QCowSnapshot *sn; | ||||
|     uint64_t *new_l1_table; | ||||
|     int new_l1_bytes; | ||||
| @@ -708,14 +707,13 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, | ||||
|         return -EFBIG; | ||||
|     } | ||||
|     new_l1_bytes = sn->l1_size * sizeof(uint64_t); | ||||
|     new_l1_table = qemu_try_blockalign(bs->file->bs, | ||||
|     new_l1_table = qemu_try_blockalign(bs->file, | ||||
|                                        align_offset(new_l1_bytes, 512)); | ||||
|     if (new_l1_table == NULL) { | ||||
|         return -ENOMEM; | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_pread(bs->file->bs, sn->l1_table_offset, | ||||
|                      new_l1_table, new_l1_bytes); | ||||
|     ret = bdrv_pread(bs->file, sn->l1_table_offset, new_l1_table, new_l1_bytes); | ||||
|     if (ret < 0) { | ||||
|         error_setg(errp, "Failed to read l1 table for snapshot"); | ||||
|         qemu_vfree(new_l1_table); | ||||
|   | ||||
							
								
								
									
										1007
									
								
								block/qcow2.c
									
									
									
									
									
								
							
							
						
						
									
										1007
									
								
								block/qcow2.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -25,8 +25,8 @@ | ||||
| #ifndef BLOCK_QCOW2_H | ||||
| #define BLOCK_QCOW2_H | ||||
|  | ||||
| #include "crypto/cipher.h" | ||||
| #include "qemu/coroutine.h" | ||||
| #include "qemu/aes.h" | ||||
| #include "block/coroutine.h" | ||||
|  | ||||
| //#define DEBUG_ALLOC | ||||
| //#define DEBUG_ALLOC2 | ||||
| @@ -68,8 +68,6 @@ | ||||
| /* Must be at least 4 to cover all cases of refcount table growth */ | ||||
| #define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */ | ||||
|  | ||||
| /* Whichever is more */ | ||||
| #define DEFAULT_L2_CACHE_CLUSTERS 8 /* clusters */ | ||||
| #define DEFAULT_L2_CACHE_BYTE_SIZE 1048576 /* bytes */ | ||||
|  | ||||
| /* The refblock cache needs only a fourth of the L2 cache size to cover as many | ||||
| @@ -96,7 +94,6 @@ | ||||
| #define QCOW2_OPT_CACHE_SIZE "cache-size" | ||||
| #define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size" | ||||
| #define QCOW2_OPT_REFCOUNT_CACHE_SIZE "refcount-cache-size" | ||||
| #define QCOW2_OPT_CACHE_CLEAN_INTERVAL "cache-clean-interval" | ||||
|  | ||||
| typedef struct QCowHeader { | ||||
|     uint32_t magic; | ||||
| @@ -222,7 +219,7 @@ typedef uint64_t Qcow2GetRefcountFunc(const void *refcount_array, | ||||
| typedef void Qcow2SetRefcountFunc(void *refcount_array, | ||||
|                                   uint64_t index, uint64_t value); | ||||
|  | ||||
| typedef struct BDRVQcow2State { | ||||
| typedef struct BDRVQcowState { | ||||
|     int cluster_bits; | ||||
|     int cluster_size; | ||||
|     int cluster_sectors; | ||||
| @@ -240,8 +237,6 @@ typedef struct BDRVQcow2State { | ||||
|  | ||||
|     Qcow2Cache* l2_table_cache; | ||||
|     Qcow2Cache* refcount_block_cache; | ||||
|     QEMUTimer *cache_clean_timer; | ||||
|     unsigned cache_clean_interval; | ||||
|  | ||||
|     uint8_t *cluster_cache; | ||||
|     uint8_t *cluster_data; | ||||
| @@ -256,8 +251,10 @@ typedef struct BDRVQcow2State { | ||||
|  | ||||
|     CoMutex lock; | ||||
|  | ||||
|     QCryptoCipher *cipher; /* current cipher, NULL if no key yet */ | ||||
|     uint32_t crypt_method; /* current crypt method, 0 if no key yet */ | ||||
|     uint32_t crypt_method_header; | ||||
|     AES_KEY aes_encrypt_key; | ||||
|     AES_KEY aes_decrypt_key; | ||||
|     uint64_t snapshots_offset; | ||||
|     int snapshots_size; | ||||
|     unsigned int nb_snapshots; | ||||
| @@ -293,7 +290,9 @@ typedef struct BDRVQcow2State { | ||||
|      * override) */ | ||||
|     char *image_backing_file; | ||||
|     char *image_backing_format; | ||||
| } BDRVQcow2State; | ||||
| } BDRVQcowState; | ||||
|  | ||||
| struct QCowAIOCB; | ||||
|  | ||||
| typedef struct Qcow2COWRegion { | ||||
|     /** | ||||
| @@ -403,28 +402,28 @@ typedef enum QCow2MetadataOverlap { | ||||
|  | ||||
| #define REFT_OFFSET_MASK 0xfffffffffffffe00ULL | ||||
|  | ||||
| static inline int64_t start_of_cluster(BDRVQcow2State *s, int64_t offset) | ||||
| static inline int64_t start_of_cluster(BDRVQcowState *s, int64_t offset) | ||||
| { | ||||
|     return offset & ~(s->cluster_size - 1); | ||||
| } | ||||
|  | ||||
| static inline int64_t offset_into_cluster(BDRVQcow2State *s, int64_t offset) | ||||
| static inline int64_t offset_into_cluster(BDRVQcowState *s, int64_t offset) | ||||
| { | ||||
|     return offset & (s->cluster_size - 1); | ||||
| } | ||||
|  | ||||
| static inline uint64_t size_to_clusters(BDRVQcow2State *s, uint64_t size) | ||||
| static inline int size_to_clusters(BDRVQcowState *s, int64_t size) | ||||
| { | ||||
|     return (size + (s->cluster_size - 1)) >> s->cluster_bits; | ||||
| } | ||||
|  | ||||
| static inline int64_t size_to_l1(BDRVQcow2State *s, int64_t size) | ||||
| static inline int64_t size_to_l1(BDRVQcowState *s, int64_t size) | ||||
| { | ||||
|     int shift = s->cluster_bits + s->l2_bits; | ||||
|     return (size + (1ULL << shift) - 1) >> shift; | ||||
| } | ||||
|  | ||||
| static inline int offset_to_l2_index(BDRVQcow2State *s, int64_t offset) | ||||
| static inline int offset_to_l2_index(BDRVQcowState *s, int64_t offset) | ||||
| { | ||||
|     return (offset >> s->cluster_bits) & (s->l2_size - 1); | ||||
| } | ||||
| @@ -435,12 +434,12 @@ static inline int64_t align_offset(int64_t offset, int n) | ||||
|     return offset; | ||||
| } | ||||
|  | ||||
| static inline int64_t qcow2_vm_state_offset(BDRVQcow2State *s) | ||||
| static inline int64_t qcow2_vm_state_offset(BDRVQcowState *s) | ||||
| { | ||||
|     return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits); | ||||
| } | ||||
|  | ||||
| static inline uint64_t qcow2_max_refcount_clusters(BDRVQcow2State *s) | ||||
| static inline uint64_t qcow2_max_refcount_clusters(BDRVQcowState *s) | ||||
| { | ||||
|     return QCOW_MAX_REFTABLE_SIZE >> s->cluster_bits; | ||||
| } | ||||
| @@ -459,7 +458,7 @@ static inline int qcow2_get_cluster_type(uint64_t l2_entry) | ||||
| } | ||||
|  | ||||
| /* Check whether refcounts are eager or lazy */ | ||||
| static inline bool qcow2_need_accurate_refcounts(BDRVQcow2State *s) | ||||
| static inline bool qcow2_need_accurate_refcounts(BDRVQcowState *s) | ||||
| { | ||||
|     return !(s->incompatible_features & QCOW2_INCOMPAT_DIRTY); | ||||
| } | ||||
| @@ -507,8 +506,8 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, | ||||
|                                   enum qcow2_discard_type type); | ||||
|  | ||||
| int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size); | ||||
| int64_t qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, | ||||
|                                 int64_t nb_clusters); | ||||
| int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, | ||||
|     int nb_clusters); | ||||
| int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size); | ||||
| void qcow2_free_clusters(BlockDriverState *bs, | ||||
|                           int64_t offset, int64_t size, | ||||
| @@ -529,19 +528,16 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, | ||||
| int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, | ||||
|                                   int64_t size); | ||||
|  | ||||
| int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, | ||||
|                                 BlockDriverAmendStatusCB *status_cb, | ||||
|                                 void *cb_opaque, Error **errp); | ||||
|  | ||||
| /* qcow2-cluster.c functions */ | ||||
| int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, | ||||
|                         bool exact_size); | ||||
| int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index); | ||||
| void qcow2_l2_cache_reset(BlockDriverState *bs); | ||||
| int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); | ||||
| int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num, | ||||
| void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, | ||||
|                      uint8_t *out_buf, const uint8_t *in_buf, | ||||
|                           int nb_sectors, bool enc, Error **errp); | ||||
|                      int nb_sectors, int enc, | ||||
|                      const AES_KEY *key); | ||||
|  | ||||
| int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||
|     int *num, uint64_t *cluster_offset); | ||||
| @@ -557,8 +553,7 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, | ||||
| int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors); | ||||
|  | ||||
| int qcow2_expand_zero_clusters(BlockDriverState *bs, | ||||
|                                BlockDriverAmendStatusCB *status_cb, | ||||
|                                void *cb_opaque); | ||||
|                                BlockDriverAmendStatusCB *status_cb); | ||||
|  | ||||
| /* qcow2-snapshot.c functions */ | ||||
| int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); | ||||
| @@ -580,20 +575,18 @@ int qcow2_read_snapshots(BlockDriverState *bs); | ||||
| Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables); | ||||
| int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c); | ||||
|  | ||||
| void qcow2_cache_entry_mark_dirty(BlockDriverState *bs, Qcow2Cache *c, | ||||
|      void *table); | ||||
| void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table); | ||||
| int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c); | ||||
| int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, | ||||
|     Qcow2Cache *dependency); | ||||
| void qcow2_cache_depends_on_flush(Qcow2Cache *c); | ||||
|  | ||||
| void qcow2_cache_clean_unused(BlockDriverState *bs, Qcow2Cache *c); | ||||
| int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c); | ||||
|  | ||||
| int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, | ||||
|     void **table); | ||||
| int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, | ||||
|     void **table); | ||||
| void qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table); | ||||
| int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -11,7 +11,6 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "qed.h" | ||||
|  | ||||
| typedef struct { | ||||
|   | ||||
| @@ -12,7 +12,6 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "qed.h" | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -11,7 +11,6 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "qed.h" | ||||
|  | ||||
| void *gencb_alloc(size_t len, BlockCompletionFunc *cb, void *opaque) | ||||
|   | ||||
| @@ -50,7 +50,6 @@ | ||||
|  * table will be deleted in favor of the existing cache entry. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "trace.h" | ||||
| #include "qed.h" | ||||
|  | ||||
|   | ||||
| @@ -12,7 +12,6 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "trace.h" | ||||
| #include "qemu/sockets.h" /* for EINPROGRESS on Windows */ | ||||
| #include "qed.h" | ||||
| @@ -64,7 +63,7 @@ static void qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, | ||||
|     read_table_cb->iov.iov_len = s->header.cluster_size * s->header.table_size, | ||||
|  | ||||
|     qemu_iovec_init_external(qiov, &read_table_cb->iov, 1); | ||||
|     bdrv_aio_readv(s->bs->file->bs, offset / BDRV_SECTOR_SIZE, qiov, | ||||
|     bdrv_aio_readv(s->bs->file, offset / BDRV_SECTOR_SIZE, qiov, | ||||
|                    qiov->size / BDRV_SECTOR_SIZE, | ||||
|                    qed_read_table_cb, read_table_cb); | ||||
| } | ||||
| @@ -153,7 +152,7 @@ static void qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, | ||||
|     /* Adjust for offset into table */ | ||||
|     offset += start * sizeof(uint64_t); | ||||
|  | ||||
|     bdrv_aio_writev(s->bs->file->bs, offset / BDRV_SECTOR_SIZE, | ||||
|     bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE, | ||||
|                     &write_table_cb->qiov, | ||||
|                     write_table_cb->qiov.size / BDRV_SECTOR_SIZE, | ||||
|                     qed_write_table_cb, write_table_cb); | ||||
|   | ||||
							
								
								
									
										88
									
								
								block/qed.c
									
									
									
									
									
								
							
							
						
						
									
										88
									
								
								block/qed.c
									
									
									
									
									
								
							| @@ -12,7 +12,6 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu/timer.h" | ||||
| #include "trace.h" | ||||
| #include "qed.h" | ||||
| @@ -83,7 +82,7 @@ int qed_write_header_sync(BDRVQEDState *s) | ||||
|     int ret; | ||||
|  | ||||
|     qed_header_cpu_to_le(&s->header, &le); | ||||
|     ret = bdrv_pwrite(s->bs->file->bs, 0, &le, sizeof(le)); | ||||
|     ret = bdrv_pwrite(s->bs->file, 0, &le, sizeof(le)); | ||||
|     if (ret != sizeof(le)) { | ||||
|         return ret; | ||||
|     } | ||||
| @@ -120,7 +119,7 @@ static void qed_write_header_read_cb(void *opaque, int ret) | ||||
|     /* Update header */ | ||||
|     qed_header_cpu_to_le(&s->header, (QEDHeader *)write_header_cb->buf); | ||||
|  | ||||
|     bdrv_aio_writev(s->bs->file->bs, 0, &write_header_cb->qiov, | ||||
|     bdrv_aio_writev(s->bs->file, 0, &write_header_cb->qiov, | ||||
|                     write_header_cb->nsectors, qed_write_header_cb, | ||||
|                     write_header_cb); | ||||
| } | ||||
| @@ -153,7 +152,7 @@ static void qed_write_header(BDRVQEDState *s, BlockCompletionFunc cb, | ||||
|     write_header_cb->iov.iov_len = len; | ||||
|     qemu_iovec_init_external(&write_header_cb->qiov, &write_header_cb->iov, 1); | ||||
|  | ||||
|     bdrv_aio_readv(s->bs->file->bs, 0, &write_header_cb->qiov, nsectors, | ||||
|     bdrv_aio_readv(s->bs->file, 0, &write_header_cb->qiov, nsectors, | ||||
|                    qed_write_header_read_cb, write_header_cb); | ||||
| } | ||||
|  | ||||
| @@ -355,6 +354,12 @@ static void qed_cancel_need_check_timer(BDRVQEDState *s) | ||||
|     timer_del(s->need_check_timer); | ||||
| } | ||||
|  | ||||
| static void bdrv_qed_rebind(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVQEDState *s = bs->opaque; | ||||
|     s->bs = bs; | ||||
| } | ||||
|  | ||||
| static void bdrv_qed_detach_aio_context(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVQEDState *s = bs->opaque; | ||||
| @@ -376,18 +381,6 @@ static void bdrv_qed_attach_aio_context(BlockDriverState *bs, | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void bdrv_qed_drain(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVQEDState *s = bs->opaque; | ||||
|  | ||||
|     /* Cancel timer and start doing I/O that were meant to happen as if it | ||||
|      * fired, that way we get bdrv_drain() taking care of the ongoing requests | ||||
|      * correctly. */ | ||||
|     qed_cancel_need_check_timer(s); | ||||
|     qed_plug_allocating_write_reqs(s); | ||||
|     bdrv_aio_flush(s->bs, qed_clear_need_check, s); | ||||
| } | ||||
|  | ||||
| static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                          Error **errp) | ||||
| { | ||||
| @@ -399,7 +392,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     s->bs = bs; | ||||
|     QSIMPLEQ_INIT(&s->allocating_write_reqs); | ||||
|  | ||||
|     ret = bdrv_pread(bs->file->bs, 0, &le_header, sizeof(le_header)); | ||||
|     ret = bdrv_pread(bs->file, 0, &le_header, sizeof(le_header)); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
| @@ -414,8 +407,8 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         char buf[64]; | ||||
|         snprintf(buf, sizeof(buf), "%" PRIx64, | ||||
|             s->header.features & ~QED_FEATURE_MASK); | ||||
|         error_setg(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, | ||||
|                    bdrv_get_device_or_node_name(bs), "QED", buf); | ||||
|         error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, | ||||
|             bdrv_get_device_name(bs), "QED", buf); | ||||
|         return -ENOTSUP; | ||||
|     } | ||||
|     if (!qed_is_cluster_size_valid(s->header.cluster_size)) { | ||||
| @@ -423,7 +416,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     } | ||||
|  | ||||
|     /* Round down file size to the last cluster */ | ||||
|     file_size = bdrv_getlength(bs->file->bs); | ||||
|     file_size = bdrv_getlength(bs->file); | ||||
|     if (file_size < 0) { | ||||
|         return file_size; | ||||
|     } | ||||
| @@ -443,9 +436,9 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|  | ||||
|     s->table_nelems = (s->header.cluster_size * s->header.table_size) / | ||||
|                       sizeof(uint64_t); | ||||
|     s->l2_shift = ctz32(s->header.cluster_size); | ||||
|     s->l2_shift = ffs(s->header.cluster_size) - 1; | ||||
|     s->l2_mask = s->table_nelems - 1; | ||||
|     s->l1_shift = s->l2_shift + ctz32(s->table_nelems); | ||||
|     s->l1_shift = s->l2_shift + ffs(s->table_nelems) - 1; | ||||
|  | ||||
|     /* Header size calculation must not overflow uint32_t */ | ||||
|     if (s->header.header_size > UINT32_MAX / s->header.cluster_size) { | ||||
| @@ -459,7 +452,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|             return -EINVAL; | ||||
|         } | ||||
|  | ||||
|         ret = qed_read_string(bs->file->bs, s->header.backing_filename_offset, | ||||
|         ret = qed_read_string(bs->file, s->header.backing_filename_offset, | ||||
|                               s->header.backing_filename_size, bs->backing_file, | ||||
|                               sizeof(bs->backing_file)); | ||||
|         if (ret < 0) { | ||||
| @@ -478,7 +471,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|      * feature is no longer valid. | ||||
|      */ | ||||
|     if ((s->header.autoclear_features & ~QED_AUTOCLEAR_FEATURE_MASK) != 0 && | ||||
|         !bdrv_is_read_only(bs->file->bs) && !(flags & BDRV_O_INACTIVE)) { | ||||
|         !bdrv_is_read_only(bs->file) && !(flags & BDRV_O_INCOMING)) { | ||||
|         s->header.autoclear_features &= QED_AUTOCLEAR_FEATURE_MASK; | ||||
|  | ||||
|         ret = qed_write_header_sync(s); | ||||
| @@ -487,7 +480,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         } | ||||
|  | ||||
|         /* From here on only known autoclear feature bits are valid */ | ||||
|         bdrv_flush(bs->file->bs); | ||||
|         bdrv_flush(bs->file); | ||||
|     } | ||||
|  | ||||
|     s->l1_table = qed_alloc_table(s); | ||||
| @@ -505,8 +498,8 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|          * potentially inconsistent images to be opened read-only.  This can | ||||
|          * aid data recovery from an otherwise inconsistent image. | ||||
|          */ | ||||
|         if (!bdrv_is_read_only(bs->file->bs) && | ||||
|             !(flags & BDRV_O_INACTIVE)) { | ||||
|         if (!bdrv_is_read_only(bs->file) && | ||||
|             !(flags & BDRV_O_INCOMING)) { | ||||
|             BdrvCheckResult result = {0}; | ||||
|  | ||||
|             ret = qed_check(s, &result, true); | ||||
| @@ -548,7 +541,7 @@ static void bdrv_qed_close(BlockDriverState *bs) | ||||
|     bdrv_qed_detach_aio_context(bs); | ||||
|  | ||||
|     /* Ensure writes reach stable storage */ | ||||
|     bdrv_flush(bs->file->bs); | ||||
|     bdrv_flush(bs->file); | ||||
|  | ||||
|     /* Clean shutdown, no check required on next open */ | ||||
|     if (s->header.features & QED_F_NEED_CHECK) { | ||||
| @@ -590,7 +583,7 @@ static int qed_create(const char *filename, uint32_t cluster_size, | ||||
|  | ||||
|     bs = NULL; | ||||
|     ret = bdrv_open(&bs, filename, NULL, NULL, | ||||
|                     BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, | ||||
|                     BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, NULL, | ||||
|                     &local_err); | ||||
|     if (ret < 0) { | ||||
|         error_propagate(errp, local_err); | ||||
| @@ -693,7 +686,6 @@ typedef struct { | ||||
|     uint64_t pos; | ||||
|     int64_t status; | ||||
|     int *pnum; | ||||
|     BlockDriverState **file; | ||||
| } QEDIsAllocatedCB; | ||||
|  | ||||
| static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t len) | ||||
| @@ -705,7 +697,6 @@ static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t l | ||||
|     case QED_CLUSTER_FOUND: | ||||
|         offset |= qed_offset_into_cluster(s, cb->pos); | ||||
|         cb->status = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset; | ||||
|         *cb->file = cb->bs->file->bs; | ||||
|         break; | ||||
|     case QED_CLUSTER_ZERO: | ||||
|         cb->status = BDRV_BLOCK_ZERO; | ||||
| @@ -727,8 +718,7 @@ static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t l | ||||
|  | ||||
| static int64_t coroutine_fn bdrv_qed_co_get_block_status(BlockDriverState *bs, | ||||
|                                                  int64_t sector_num, | ||||
|                                                  int nb_sectors, int *pnum, | ||||
|                                                  BlockDriverState **file) | ||||
|                                                  int nb_sectors, int *pnum) | ||||
| { | ||||
|     BDRVQEDState *s = bs->opaque; | ||||
|     size_t len = (size_t)nb_sectors * BDRV_SECTOR_SIZE; | ||||
| @@ -737,7 +727,6 @@ static int64_t coroutine_fn bdrv_qed_co_get_block_status(BlockDriverState *bs, | ||||
|         .pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE, | ||||
|         .status = BDRV_BLOCK_OFFSET_MASK, | ||||
|         .pnum = pnum, | ||||
|         .file = file, | ||||
|     }; | ||||
|     QEDRequest request = { .l2_table = NULL }; | ||||
|  | ||||
| @@ -783,8 +772,8 @@ static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos, | ||||
|     /* If there is a backing file, get its length.  Treat the absence of a | ||||
|      * backing file like a zero length backing file. | ||||
|      */ | ||||
|     if (s->bs->backing) { | ||||
|         int64_t l = bdrv_getlength(s->bs->backing->bs); | ||||
|     if (s->bs->backing_hd) { | ||||
|         int64_t l = bdrv_getlength(s->bs->backing_hd); | ||||
|         if (l < 0) { | ||||
|             cb(opaque, l); | ||||
|             return; | ||||
| @@ -813,7 +802,7 @@ static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos, | ||||
|     qemu_iovec_concat(*backing_qiov, qiov, 0, size); | ||||
|  | ||||
|     BLKDBG_EVENT(s->bs->file, BLKDBG_READ_BACKING_AIO); | ||||
|     bdrv_aio_readv(s->bs->backing->bs, pos / BDRV_SECTOR_SIZE, | ||||
|     bdrv_aio_readv(s->bs->backing_hd, pos / BDRV_SECTOR_SIZE, | ||||
|                    *backing_qiov, size / BDRV_SECTOR_SIZE, cb, opaque); | ||||
| } | ||||
|  | ||||
| @@ -850,7 +839,7 @@ static void qed_copy_from_backing_file_write(void *opaque, int ret) | ||||
|     } | ||||
|  | ||||
|     BLKDBG_EVENT(s->bs->file, BLKDBG_COW_WRITE); | ||||
|     bdrv_aio_writev(s->bs->file->bs, copy_cb->offset / BDRV_SECTOR_SIZE, | ||||
|     bdrv_aio_writev(s->bs->file, copy_cb->offset / BDRV_SECTOR_SIZE, | ||||
|                     ©_cb->qiov, copy_cb->qiov.size / BDRV_SECTOR_SIZE, | ||||
|                     qed_copy_from_backing_file_cb, copy_cb); | ||||
| } | ||||
| @@ -1066,7 +1055,7 @@ static void qed_aio_write_flush_before_l2_update(void *opaque, int ret) | ||||
|     QEDAIOCB *acb = opaque; | ||||
|     BDRVQEDState *s = acb_to_s(acb); | ||||
|  | ||||
|     if (!bdrv_aio_flush(s->bs->file->bs, qed_aio_write_l2_update_cb, opaque)) { | ||||
|     if (!bdrv_aio_flush(s->bs->file, qed_aio_write_l2_update_cb, opaque)) { | ||||
|         qed_aio_complete(acb, -EIO); | ||||
|     } | ||||
| } | ||||
| @@ -1092,7 +1081,7 @@ static void qed_aio_write_main(void *opaque, int ret) | ||||
|     if (acb->find_cluster_ret == QED_CLUSTER_FOUND) { | ||||
|         next_fn = qed_aio_next_io; | ||||
|     } else { | ||||
|         if (s->bs->backing) { | ||||
|         if (s->bs->backing_hd) { | ||||
|             next_fn = qed_aio_write_flush_before_l2_update; | ||||
|         } else { | ||||
|             next_fn = qed_aio_write_l2_update_cb; | ||||
| @@ -1100,7 +1089,7 @@ static void qed_aio_write_main(void *opaque, int ret) | ||||
|     } | ||||
|  | ||||
|     BLKDBG_EVENT(s->bs->file, BLKDBG_WRITE_AIO); | ||||
|     bdrv_aio_writev(s->bs->file->bs, offset / BDRV_SECTOR_SIZE, | ||||
|     bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE, | ||||
|                     &acb->cur_qiov, acb->cur_qiov.size / BDRV_SECTOR_SIZE, | ||||
|                     next_fn, acb); | ||||
| } | ||||
| @@ -1150,7 +1139,7 @@ static void qed_aio_write_prefill(void *opaque, int ret) | ||||
| static bool qed_should_set_need_check(BDRVQEDState *s) | ||||
| { | ||||
|     /* The flush before L2 update path ensures consistency */ | ||||
|     if (s->bs->backing) { | ||||
|     if (s->bs->backing_hd) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
| @@ -1332,7 +1321,7 @@ static void qed_aio_read_data(void *opaque, int ret, | ||||
|     } | ||||
|  | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); | ||||
|     bdrv_aio_readv(bs->file->bs, offset / BDRV_SECTOR_SIZE, | ||||
|     bdrv_aio_readv(bs->file, offset / BDRV_SECTOR_SIZE, | ||||
|                    &acb->cur_qiov, acb->cur_qiov.size / BDRV_SECTOR_SIZE, | ||||
|                    qed_aio_next_io, acb); | ||||
|     return; | ||||
| @@ -1454,7 +1443,7 @@ static int coroutine_fn bdrv_qed_co_write_zeroes(BlockDriverState *bs, | ||||
|     struct iovec iov; | ||||
|  | ||||
|     /* Refuse if there are untouched backing file sectors */ | ||||
|     if (bs->backing) { | ||||
|     if (bs->backing_hd) { | ||||
|         if (qed_offset_into_cluster(s, sector_num * BDRV_SECTOR_SIZE) != 0) { | ||||
|             return -ENOTSUP; | ||||
|         } | ||||
| @@ -1591,7 +1580,7 @@ static int bdrv_qed_change_backing_file(BlockDriverState *bs, | ||||
|     } | ||||
|  | ||||
|     /* Write new header */ | ||||
|     ret = bdrv_pwrite_sync(bs->file->bs, 0, buffer, buffer_len); | ||||
|     ret = bdrv_pwrite_sync(bs->file, 0, buffer, buffer_len); | ||||
|     g_free(buffer); | ||||
|     if (ret == 0) { | ||||
|         memcpy(&s->header, &new_header, sizeof(new_header)); | ||||
| @@ -1607,7 +1596,7 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp) | ||||
|  | ||||
|     bdrv_qed_close(bs); | ||||
|  | ||||
|     bdrv_invalidate_cache(bs->file->bs, &local_err); | ||||
|     bdrv_invalidate_cache(bs->file, &local_err); | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         return; | ||||
| @@ -1616,8 +1605,9 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp) | ||||
|     memset(s, 0, sizeof(BDRVQEDState)); | ||||
|     ret = bdrv_qed_open(bs, NULL, bs->open_flags, &local_err); | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         error_prepend(errp, "Could not reopen qed layer: "); | ||||
|         error_setg(errp, "Could not reopen qed layer: %s", | ||||
|                    error_get_pretty(local_err)); | ||||
|         error_free(local_err); | ||||
|         return; | ||||
|     } else if (ret < 0) { | ||||
|         error_setg_errno(errp, -ret, "Could not reopen qed layer"); | ||||
| @@ -1674,6 +1664,7 @@ static BlockDriver bdrv_qed = { | ||||
|     .supports_backing         = true, | ||||
|  | ||||
|     .bdrv_probe               = bdrv_qed_probe, | ||||
|     .bdrv_rebind              = bdrv_qed_rebind, | ||||
|     .bdrv_open                = bdrv_qed_open, | ||||
|     .bdrv_close               = bdrv_qed_close, | ||||
|     .bdrv_reopen_prepare      = bdrv_qed_reopen_prepare, | ||||
| @@ -1692,7 +1683,6 @@ static BlockDriver bdrv_qed = { | ||||
|     .bdrv_check               = bdrv_qed_check, | ||||
|     .bdrv_detach_aio_context  = bdrv_qed_detach_aio_context, | ||||
|     .bdrv_attach_aio_context  = bdrv_qed_attach_aio_context, | ||||
|     .bdrv_drain               = bdrv_qed_drain, | ||||
| }; | ||||
|  | ||||
| static void bdrv_qed_init(void) | ||||
|   | ||||
							
								
								
									
										177
									
								
								block/quorum.c
									
									
									
									
									
								
							
							
						
						
									
										177
									
								
								block/quorum.c
									
									
									
									
									
								
							| @@ -13,17 +13,16 @@ | ||||
|  * See the COPYING file in the top-level directory. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include <gnutls/gnutls.h> | ||||
| #include <gnutls/crypto.h> | ||||
| #include "block/block_int.h" | ||||
| #include "qapi/qmp/qbool.h" | ||||
| #include "qapi/qmp/qdict.h" | ||||
| #include "qapi/qmp/qerror.h" | ||||
| #include "qapi/qmp/qint.h" | ||||
| #include "qapi/qmp/qjson.h" | ||||
| #include "qapi/qmp/qlist.h" | ||||
| #include "qapi/qmp/qstring.h" | ||||
| #include "qapi-event.h" | ||||
| #include "crypto/hash.h" | ||||
|  | ||||
| #define HASH_LENGTH 32 | ||||
|  | ||||
| @@ -34,7 +33,7 @@ | ||||
|  | ||||
| /* This union holds a vote hash value */ | ||||
| typedef union QuorumVoteValue { | ||||
|     uint8_t h[HASH_LENGTH];    /* SHA-256 hash */ | ||||
|     char h[HASH_LENGTH];       /* SHA-256 hash */ | ||||
|     int64_t l;                 /* simpler 64 bits hash */ | ||||
| } QuorumVoteValue; | ||||
|  | ||||
| @@ -65,7 +64,7 @@ typedef struct QuorumVotes { | ||||
|  | ||||
| /* the following structure holds the state of one quorum instance */ | ||||
| typedef struct BDRVQuorumState { | ||||
|     BdrvChild **children;  /* children BlockDriverStates */ | ||||
|     BlockDriverState **bs; /* children BlockDriverStates */ | ||||
|     int num_children;      /* children count */ | ||||
|     int threshold;         /* if less than threshold children reads gave the | ||||
|                             * same result a quorum error occurs. | ||||
| @@ -227,7 +226,10 @@ static void quorum_report_bad(QuorumAIOCB *acb, char *node_name, int ret) | ||||
|  | ||||
| static void quorum_report_failure(QuorumAIOCB *acb) | ||||
| { | ||||
|     const char *reference = bdrv_get_device_or_node_name(acb->common.bs); | ||||
|     const char *reference = bdrv_get_device_name(acb->common.bs)[0] ? | ||||
|                             bdrv_get_device_name(acb->common.bs) : | ||||
|                             acb->common.bs->node_name; | ||||
|  | ||||
|     qapi_event_send_quorum_failure(reference, acb->sector_num, | ||||
|                                    acb->nb_sectors, &error_abort); | ||||
| } | ||||
| @@ -286,8 +288,7 @@ static void quorum_aio_cb(void *opaque, int ret) | ||||
|  | ||||
|     if (acb->is_read && s->read_pattern == QUORUM_READ_PATTERN_FIFO) { | ||||
|         /* We try to read next child in FIFO order if we fail to read */ | ||||
|         if (ret < 0 && (acb->child_iter + 1) < s->num_children) { | ||||
|             acb->child_iter++; | ||||
|         if (ret < 0 && ++acb->child_iter < s->num_children) { | ||||
|             read_fifo_child(acb); | ||||
|             return; | ||||
|         } | ||||
| @@ -338,7 +339,7 @@ static void quorum_report_bad_versions(BDRVQuorumState *s, | ||||
|             continue; | ||||
|         } | ||||
|         QLIST_FOREACH(item, &version->items, next) { | ||||
|             quorum_report_bad(acb, s->children[item->index]->bs->node_name, 0); | ||||
|             quorum_report_bad(acb, s->bs[item->index]->node_name, 0); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -371,9 +372,8 @@ static bool quorum_rewrite_bad_versions(BDRVQuorumState *s, QuorumAIOCB *acb, | ||||
|             continue; | ||||
|         } | ||||
|         QLIST_FOREACH(item, &version->items, next) { | ||||
|             bdrv_aio_writev(s->children[item->index]->bs, acb->sector_num, | ||||
|                             acb->qiov, acb->nb_sectors, quorum_rewrite_aio_cb, | ||||
|                             acb); | ||||
|             bdrv_aio_writev(s->bs[item->index], acb->sector_num, acb->qiov, | ||||
|                             acb->nb_sectors, quorum_rewrite_aio_cb, acb); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -430,21 +430,25 @@ static void quorum_free_vote_list(QuorumVotes *votes) | ||||
|  | ||||
| static int quorum_compute_hash(QuorumAIOCB *acb, int i, QuorumVoteValue *hash) | ||||
| { | ||||
|     int j, ret; | ||||
|     gnutls_hash_hd_t dig; | ||||
|     QEMUIOVector *qiov = &acb->qcrs[i].qiov; | ||||
|     size_t len = sizeof(hash->h); | ||||
|     uint8_t *data = hash->h; | ||||
|  | ||||
|     /* XXX - would be nice if we could pass in the Error ** | ||||
|      * and propagate that back, but this quorum code is | ||||
|      * restricted to just errno values currently */ | ||||
|     if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALG_SHA256, | ||||
|                             qiov->iov, qiov->niov, | ||||
|                             &data, &len, | ||||
|                             NULL) < 0) { | ||||
|         return -EINVAL; | ||||
|     ret = gnutls_hash_init(&dig, GNUTLS_DIG_SHA256); | ||||
|  | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
|     for (j = 0; j < qiov->niov; j++) { | ||||
|         ret = gnutls_hash(dig, qiov->iov[j].iov_base, qiov->iov[j].iov_len); | ||||
|         if (ret < 0) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     gnutls_hash_deinit(dig, (void *) hash); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static QuorumVoteVersion *quorum_get_vote_winner(QuorumVotes *votes) | ||||
| @@ -642,13 +646,13 @@ static BlockAIOCB *read_quorum_children(QuorumAIOCB *acb) | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < s->num_children; i++) { | ||||
|         acb->qcrs[i].buf = qemu_blockalign(s->children[i]->bs, acb->qiov->size); | ||||
|         acb->qcrs[i].buf = qemu_blockalign(s->bs[i], acb->qiov->size); | ||||
|         qemu_iovec_init(&acb->qcrs[i].qiov, acb->qiov->niov); | ||||
|         qemu_iovec_clone(&acb->qcrs[i].qiov, acb->qiov, acb->qcrs[i].buf); | ||||
|     } | ||||
|  | ||||
|     for (i = 0; i < s->num_children; i++) { | ||||
|         bdrv_aio_readv(s->children[i]->bs, acb->sector_num, &acb->qcrs[i].qiov, | ||||
|         bdrv_aio_readv(s->bs[i], acb->sector_num, &acb->qcrs[i].qiov, | ||||
|                        acb->nb_sectors, quorum_aio_cb, &acb->qcrs[i]); | ||||
|     } | ||||
|  | ||||
| @@ -659,12 +663,12 @@ static BlockAIOCB *read_fifo_child(QuorumAIOCB *acb) | ||||
| { | ||||
|     BDRVQuorumState *s = acb->common.bs->opaque; | ||||
|  | ||||
|     acb->qcrs[acb->child_iter].buf = | ||||
|         qemu_blockalign(s->children[acb->child_iter]->bs, acb->qiov->size); | ||||
|     acb->qcrs[acb->child_iter].buf = qemu_blockalign(s->bs[acb->child_iter], | ||||
|                                                      acb->qiov->size); | ||||
|     qemu_iovec_init(&acb->qcrs[acb->child_iter].qiov, acb->qiov->niov); | ||||
|     qemu_iovec_clone(&acb->qcrs[acb->child_iter].qiov, acb->qiov, | ||||
|                      acb->qcrs[acb->child_iter].buf); | ||||
|     bdrv_aio_readv(s->children[acb->child_iter]->bs, acb->sector_num, | ||||
|     bdrv_aio_readv(s->bs[acb->child_iter], acb->sector_num, | ||||
|                    &acb->qcrs[acb->child_iter].qiov, acb->nb_sectors, | ||||
|                    quorum_aio_cb, &acb->qcrs[acb->child_iter]); | ||||
|  | ||||
| @@ -705,8 +709,8 @@ static BlockAIOCB *quorum_aio_writev(BlockDriverState *bs, | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < s->num_children; i++) { | ||||
|         acb->qcrs[i].aiocb = bdrv_aio_writev(s->children[i]->bs, sector_num, | ||||
|                                              qiov, nb_sectors, &quorum_aio_cb, | ||||
|         acb->qcrs[i].aiocb = bdrv_aio_writev(s->bs[i], sector_num, qiov, | ||||
|                                              nb_sectors, &quorum_aio_cb, | ||||
|                                              &acb->qcrs[i]); | ||||
|     } | ||||
|  | ||||
| @@ -720,12 +724,12 @@ static int64_t quorum_getlength(BlockDriverState *bs) | ||||
|     int i; | ||||
|  | ||||
|     /* check that all file have the same length */ | ||||
|     result = bdrv_getlength(s->children[0]->bs); | ||||
|     result = bdrv_getlength(s->bs[0]); | ||||
|     if (result < 0) { | ||||
|         return result; | ||||
|     } | ||||
|     for (i = 1; i < s->num_children; i++) { | ||||
|         int64_t value = bdrv_getlength(s->children[i]->bs); | ||||
|         int64_t value = bdrv_getlength(s->bs[i]); | ||||
|         if (value < 0) { | ||||
|             return value; | ||||
|         } | ||||
| @@ -744,7 +748,7 @@ static void quorum_invalidate_cache(BlockDriverState *bs, Error **errp) | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < s->num_children; i++) { | ||||
|         bdrv_invalidate_cache(s->children[i]->bs, &local_err); | ||||
|         bdrv_invalidate_cache(s->bs[i], &local_err); | ||||
|         if (local_err) { | ||||
|             error_propagate(errp, local_err); | ||||
|             return; | ||||
| @@ -765,7 +769,7 @@ static coroutine_fn int quorum_co_flush(BlockDriverState *bs) | ||||
|     error_votes.compare = quorum_64bits_compare; | ||||
|  | ||||
|     for (i = 0; i < s->num_children; i++) { | ||||
|         result = bdrv_co_flush(s->children[i]->bs); | ||||
|         result = bdrv_co_flush(s->bs[i]); | ||||
|         result_value.l = result; | ||||
|         quorum_count_vote(&error_votes, &result_value, i); | ||||
|     } | ||||
| @@ -785,7 +789,7 @@ static bool quorum_recurse_is_first_non_filter(BlockDriverState *bs, | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < s->num_children; i++) { | ||||
|         bool perm = bdrv_recurse_is_first_non_filter(s->children[i]->bs, | ||||
|         bool perm = bdrv_recurse_is_first_non_filter(s->bs[i], | ||||
|                                                      candidate); | ||||
|         if (perm) { | ||||
|             return true; | ||||
| @@ -799,7 +803,7 @@ static int quorum_valid_threshold(int threshold, int num_children, Error **errp) | ||||
| { | ||||
|  | ||||
|     if (threshold < 1) { | ||||
|         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, | ||||
|         error_set(errp, QERR_INVALID_PARAMETER_VALUE, | ||||
|                   "vote-threshold", "value >= 1"); | ||||
|         return -ERANGE; | ||||
|     } | ||||
| @@ -849,7 +853,7 @@ static int parse_read_pattern(const char *opt) | ||||
|         return QUORUM_READ_PATTERN_QUORUM; | ||||
|     } | ||||
|  | ||||
|     for (i = 0; i < QUORUM_READ_PATTERN__MAX; i++) { | ||||
|     for (i = 0; i < QUORUM_READ_PATTERN_MAX; i++) { | ||||
|         if (!strcmp(opt, QuorumReadPattern_lookup[i])) { | ||||
|             return i; | ||||
|         } | ||||
| @@ -865,18 +869,25 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     Error *local_err = NULL; | ||||
|     QemuOpts *opts = NULL; | ||||
|     bool *opened; | ||||
|     QDict *sub = NULL; | ||||
|     QList *list = NULL; | ||||
|     const QListEntry *lentry; | ||||
|     int i; | ||||
|     int ret = 0; | ||||
|  | ||||
|     qdict_flatten(options); | ||||
|     qdict_extract_subqdict(options, &sub, "children."); | ||||
|     qdict_array_split(sub, &list); | ||||
|  | ||||
|     /* count how many different children are present */ | ||||
|     s->num_children = qdict_array_entries(options, "children."); | ||||
|     if (s->num_children < 0) { | ||||
|         error_setg(&local_err, "Option children is not a valid array"); | ||||
|     if (qdict_size(sub)) { | ||||
|         error_setg(&local_err, "Invalid option children.%s", | ||||
|                    qdict_first(sub)->key); | ||||
|         ret = -EINVAL; | ||||
|         goto exit; | ||||
|     } | ||||
|  | ||||
|     /* count how many different children are present */ | ||||
|     s->num_children = qlist_size(list); | ||||
|     if (s->num_children < 2) { | ||||
|         error_setg(&local_err, | ||||
|                    "Number of provided children must be greater than 1"); | ||||
| @@ -892,12 +903,6 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     } | ||||
|  | ||||
|     s->threshold = qemu_opt_get_number(opts, QUORUM_OPT_VOTE_THRESHOLD, 0); | ||||
|     /* and validate it against s->num_children */ | ||||
|     ret = quorum_valid_threshold(s->threshold, s->num_children, &local_err); | ||||
|     if (ret < 0) { | ||||
|         goto exit; | ||||
|     } | ||||
|  | ||||
|     ret = parse_read_pattern(qemu_opt_get(opts, QUORUM_OPT_READ_PATTERN)); | ||||
|     if (ret < 0) { | ||||
|         error_setg(&local_err, "Please set read-pattern as fifo or quorum"); | ||||
| @@ -906,6 +911,12 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     s->read_pattern = ret; | ||||
|  | ||||
|     if (s->read_pattern == QUORUM_READ_PATTERN_QUORUM) { | ||||
|         /* and validate it against s->num_children */ | ||||
|         ret = quorum_valid_threshold(s->threshold, s->num_children, &local_err); | ||||
|         if (ret < 0) { | ||||
|             goto exit; | ||||
|         } | ||||
|  | ||||
|         /* is the driver in blkverify mode */ | ||||
|         if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false) && | ||||
|             s->num_children == 2 && s->threshold == 2) { | ||||
| @@ -925,22 +936,41 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* allocate the children array */ | ||||
|     s->children = g_new0(BdrvChild *, s->num_children); | ||||
|     /* allocate the children BlockDriverState array */ | ||||
|     s->bs = g_new0(BlockDriverState *, s->num_children); | ||||
|     opened = g_new0(bool, s->num_children); | ||||
|  | ||||
|     for (i = 0; i < s->num_children; i++) { | ||||
|         char indexstr[32]; | ||||
|         ret = snprintf(indexstr, 32, "children.%d", i); | ||||
|         assert(ret < 32); | ||||
|     for (i = 0, lentry = qlist_first(list); lentry; | ||||
|          lentry = qlist_next(lentry), i++) { | ||||
|         QDict *d; | ||||
|         QString *string; | ||||
|  | ||||
|         s->children[i] = bdrv_open_child(NULL, options, indexstr, bs, | ||||
|                                          &child_format, false, &local_err); | ||||
|         if (local_err) { | ||||
|         switch (qobject_type(lentry->value)) | ||||
|         { | ||||
|             /* List of options */ | ||||
|             case QTYPE_QDICT: | ||||
|                 d = qobject_to_qdict(lentry->value); | ||||
|                 QINCREF(d); | ||||
|                 ret = bdrv_open(&s->bs[i], NULL, NULL, d, flags, NULL, | ||||
|                                 &local_err); | ||||
|                 break; | ||||
|  | ||||
|             /* QMP reference */ | ||||
|             case QTYPE_QSTRING: | ||||
|                 string = qobject_to_qstring(lentry->value); | ||||
|                 ret = bdrv_open(&s->bs[i], NULL, qstring_get_str(string), NULL, | ||||
|                                 flags, NULL, &local_err); | ||||
|                 break; | ||||
|  | ||||
|             default: | ||||
|                 error_setg(&local_err, "Specification of child block device %i " | ||||
|                            "is invalid", i); | ||||
|                 ret = -EINVAL; | ||||
|             goto close_exit; | ||||
|         } | ||||
|  | ||||
|         if (ret < 0) { | ||||
|             goto close_exit; | ||||
|         } | ||||
|         opened[i] = true; | ||||
|     } | ||||
|  | ||||
| @@ -953,9 +983,9 @@ close_exit: | ||||
|         if (!opened[i]) { | ||||
|             continue; | ||||
|         } | ||||
|         bdrv_unref_child(bs, s->children[i]); | ||||
|         bdrv_unref(s->bs[i]); | ||||
|     } | ||||
|     g_free(s->children); | ||||
|     g_free(s->bs); | ||||
|     g_free(opened); | ||||
| exit: | ||||
|     qemu_opts_del(opts); | ||||
| @@ -963,6 +993,8 @@ exit: | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|     } | ||||
|     QDECREF(list); | ||||
|     QDECREF(sub); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| @@ -972,10 +1004,10 @@ static void quorum_close(BlockDriverState *bs) | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < s->num_children; i++) { | ||||
|         bdrv_unref_child(bs, s->children[i]); | ||||
|         bdrv_unref(s->bs[i]); | ||||
|     } | ||||
|  | ||||
|     g_free(s->children); | ||||
|     g_free(s->bs); | ||||
| } | ||||
|  | ||||
| static void quorum_detach_aio_context(BlockDriverState *bs) | ||||
| @@ -984,7 +1016,7 @@ static void quorum_detach_aio_context(BlockDriverState *bs) | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < s->num_children; i++) { | ||||
|         bdrv_detach_aio_context(s->children[i]->bs); | ||||
|         bdrv_detach_aio_context(s->bs[i]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -995,11 +1027,11 @@ static void quorum_attach_aio_context(BlockDriverState *bs, | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < s->num_children; i++) { | ||||
|         bdrv_attach_aio_context(s->children[i]->bs, new_context); | ||||
|         bdrv_attach_aio_context(s->bs[i], new_context); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void quorum_refresh_filename(BlockDriverState *bs, QDict *options) | ||||
| static void quorum_refresh_filename(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVQuorumState *s = bs->opaque; | ||||
|     QDict *opts; | ||||
| @@ -1007,17 +1039,16 @@ static void quorum_refresh_filename(BlockDriverState *bs, QDict *options) | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < s->num_children; i++) { | ||||
|         bdrv_refresh_filename(s->children[i]->bs); | ||||
|         if (!s->children[i]->bs->full_open_options) { | ||||
|         bdrv_refresh_filename(s->bs[i]); | ||||
|         if (!s->bs[i]->full_open_options) { | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     children = qlist_new(); | ||||
|     for (i = 0; i < s->num_children; i++) { | ||||
|         QINCREF(s->children[i]->bs->full_open_options); | ||||
|         qlist_append_obj(children, | ||||
|                          QOBJECT(s->children[i]->bs->full_open_options)); | ||||
|         QINCREF(s->bs[i]->full_open_options); | ||||
|         qlist_append_obj(children, QOBJECT(s->bs[i]->full_open_options)); | ||||
|     } | ||||
|  | ||||
|     opts = qdict_new(); | ||||
| @@ -1025,9 +1056,9 @@ static void quorum_refresh_filename(BlockDriverState *bs, QDict *options) | ||||
|     qdict_put_obj(opts, QUORUM_OPT_VOTE_THRESHOLD, | ||||
|                   QOBJECT(qint_from_int(s->threshold))); | ||||
|     qdict_put_obj(opts, QUORUM_OPT_BLKVERIFY, | ||||
|                   QOBJECT(qbool_from_bool(s->is_blkverify))); | ||||
|                   QOBJECT(qbool_from_int(s->is_blkverify))); | ||||
|     qdict_put_obj(opts, QUORUM_OPT_REWRITE, | ||||
|                   QOBJECT(qbool_from_bool(s->rewrite_corrupted))); | ||||
|                   QOBJECT(qbool_from_int(s->rewrite_corrupted))); | ||||
|     qdict_put_obj(opts, "children", QOBJECT(children)); | ||||
|  | ||||
|     bs->full_open_options = opts; | ||||
| @@ -1060,10 +1091,6 @@ static BlockDriver bdrv_quorum = { | ||||
|  | ||||
| static void bdrv_quorum_init(void) | ||||
| { | ||||
|     if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA256)) { | ||||
|         /* SHA256 hash support is required for quorum device */ | ||||
|         return; | ||||
|     } | ||||
|     bdrv_register(&bdrv_quorum); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -21,9 +21,7 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "qemu/timer.h" | ||||
| #include "qemu/log.h" | ||||
| #include "block/block_int.h" | ||||
| @@ -33,7 +31,6 @@ | ||||
| #include "qemu/iov.h" | ||||
| #include "raw-aio.h" | ||||
| #include "qapi/util.h" | ||||
| #include "qapi/qmp/qstring.h" | ||||
|  | ||||
| #if defined(__APPLE__) && (__MACH__) | ||||
| #include <paths.h> | ||||
| @@ -52,13 +49,14 @@ | ||||
| #include <sys/dkio.h> | ||||
| #endif | ||||
| #ifdef __linux__ | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/ioctl.h> | ||||
| #include <sys/param.h> | ||||
| #include <linux/cdrom.h> | ||||
| #include <linux/fd.h> | ||||
| #include <linux/fs.h> | ||||
| #include <linux/hdreg.h> | ||||
| #include <scsi/sg.h> | ||||
| #ifdef __s390__ | ||||
| #include <asm/dasd.h> | ||||
| #endif | ||||
| @@ -96,19 +94,15 @@ | ||||
| #include <xfs/xfs.h> | ||||
| #endif | ||||
|  | ||||
| //#define DEBUG_BLOCK | ||||
| //#define DEBUG_FLOPPY | ||||
|  | ||||
| #ifdef DEBUG_BLOCK | ||||
| # define DEBUG_BLOCK_PRINT 1 | ||||
| //#define DEBUG_BLOCK | ||||
| #if defined(DEBUG_BLOCK) | ||||
| #define DEBUG_BLOCK_PRINT(formatCstr, ...) do { if (qemu_log_enabled()) \ | ||||
|     { qemu_log(formatCstr, ## __VA_ARGS__); qemu_log_flush(); } } while (0) | ||||
| #else | ||||
| # define DEBUG_BLOCK_PRINT 0 | ||||
| #define DEBUG_BLOCK_PRINT(formatCstr, ...) | ||||
| #endif | ||||
| #define DPRINTF(fmt, ...) \ | ||||
| do { \ | ||||
|     if (DEBUG_BLOCK_PRINT) { \ | ||||
|         printf(fmt, ## __VA_ARGS__); \ | ||||
|     } \ | ||||
| } while (0) | ||||
|  | ||||
| /* OS X does not have O_DSYNC */ | ||||
| #ifndef O_DSYNC | ||||
| @@ -126,6 +120,11 @@ do { \ | ||||
|  | ||||
| #define FTYPE_FILE   0 | ||||
| #define FTYPE_CD     1 | ||||
| #define FTYPE_FD     2 | ||||
|  | ||||
| /* if the FD is not accessed during that time (in ns), we try to | ||||
|    reopen it to see if the disk has been changed */ | ||||
| #define FD_OPEN_TIMEOUT (1000000000) | ||||
|  | ||||
| #define MAX_BLOCKSIZE	4096 | ||||
|  | ||||
| @@ -135,6 +134,13 @@ typedef struct BDRVRawState { | ||||
|     int open_flags; | ||||
|     size_t buf_align; | ||||
|  | ||||
| #if defined(__linux__) | ||||
|     /* linux floppy specific */ | ||||
|     int64_t fd_open_time; | ||||
|     int64_t fd_error_time; | ||||
|     int fd_got_error; | ||||
|     int fd_media_changed; | ||||
| #endif | ||||
| #ifdef CONFIG_LINUX_AIO | ||||
|     int use_aio; | ||||
|     void *aio_ctx; | ||||
| @@ -295,11 +301,10 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     char *buf; | ||||
|     size_t max_align = MAX(MAX_BLOCKSIZE, getpagesize()); | ||||
|  | ||||
|     /* For SCSI generic devices the alignment is not really used. | ||||
|     /* For /dev/sg devices the alignment is not really used. | ||||
|        With buffered I/O, we don't have any restrictions. */ | ||||
|     if (bdrv_is_sg(bs) || !s->needs_alignment) { | ||||
|     if (bs->sg || !s->needs_alignment) { | ||||
|         bs->request_alignment = 1; | ||||
|         s->buf_align = 1; | ||||
|         return; | ||||
| @@ -325,9 +330,9 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) | ||||
|     /* If we could not get the sizes so far, we can only guess them */ | ||||
|     if (!s->buf_align) { | ||||
|         size_t align; | ||||
|         buf = qemu_memalign(max_align, 2 * max_align); | ||||
|         for (align = 512; align <= max_align; align <<= 1) { | ||||
|             if (raw_is_io_aligned(fd, buf + align, max_align)) { | ||||
|         buf = qemu_memalign(MAX_BLOCKSIZE, 2 * MAX_BLOCKSIZE); | ||||
|         for (align = 512; align <= MAX_BLOCKSIZE; align <<= 1) { | ||||
|             if (raw_is_io_aligned(fd, buf + align, MAX_BLOCKSIZE)) { | ||||
|                 s->buf_align = align; | ||||
|                 break; | ||||
|             } | ||||
| @@ -337,8 +342,8 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) | ||||
|  | ||||
|     if (!bs->request_alignment) { | ||||
|         size_t align; | ||||
|         buf = qemu_memalign(s->buf_align, max_align); | ||||
|         for (align = 512; align <= max_align; align <<= 1) { | ||||
|         buf = qemu_memalign(s->buf_align, MAX_BLOCKSIZE); | ||||
|         for (align = 512; align <= MAX_BLOCKSIZE; align <<= 1) { | ||||
|             if (raw_is_io_aligned(fd, buf, align)) { | ||||
|                 bs->request_alignment = align; | ||||
|                 break; | ||||
| @@ -499,19 +504,14 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, | ||||
|         goto fail; | ||||
|     } | ||||
|     if (!s->use_aio && (bdrv_flags & BDRV_O_NATIVE_AIO)) { | ||||
|         error_setg(errp, "aio=native was specified, but it requires " | ||||
|                          "cache.direct=on, which was not specified."); | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|         error_printf("WARNING: aio=native was specified for '%s', but " | ||||
|                      "it requires cache.direct=on, which was not " | ||||
|                      "specified. Falling back to aio=threads.\n" | ||||
|                      "         This will become an error condition in " | ||||
|                      "future QEMU versions.\n", | ||||
|                      bs->filename); | ||||
|     } | ||||
| #else | ||||
|     if (bdrv_flags & BDRV_O_NATIVE_AIO) { | ||||
|         error_setg(errp, "aio=native was specified, but is not supported " | ||||
|                          "in this build."); | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|     } | ||||
| #endif /* !defined(CONFIG_LINUX_AIO) */ | ||||
| #endif | ||||
|  | ||||
|     s->has_discard = true; | ||||
|     s->has_write_zeroes = true; | ||||
| @@ -618,7 +618,7 @@ static int raw_reopen_prepare(BDRVReopenState *state, | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     if (s->type == FTYPE_CD) { | ||||
|     if (s->type == FTYPE_FD || s->type == FTYPE_CD) { | ||||
|         raw_s->open_flags |= O_NONBLOCK; | ||||
|     } | ||||
|  | ||||
| @@ -662,19 +662,13 @@ static int raw_reopen_prepare(BDRVReopenState *state, | ||||
|  | ||||
|     /* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */ | ||||
|     if (raw_s->fd == -1) { | ||||
|         const char *normalized_filename = state->bs->filename; | ||||
|         ret = raw_normalize_devicepath(&normalized_filename); | ||||
|         if (ret < 0) { | ||||
|             error_setg_errno(errp, -ret, "Could not normalize device path"); | ||||
|         } else { | ||||
|         assert(!(raw_s->open_flags & O_CREAT)); | ||||
|             raw_s->fd = qemu_open(normalized_filename, raw_s->open_flags); | ||||
|         raw_s->fd = qemu_open(state->bs->filename, raw_s->open_flags); | ||||
|         if (raw_s->fd == -1) { | ||||
|             error_setg_errno(errp, errno, "Could not reopen file"); | ||||
|             ret = -1; | ||||
|         } | ||||
|     } | ||||
|     } | ||||
|  | ||||
|     /* Fail already reopen_prepare() if we can't get a working O_DIRECT | ||||
|      * alignment with the new fd. */ | ||||
| @@ -731,8 +725,7 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|  | ||||
|     raw_probe_alignment(bs, s->fd, errp); | ||||
|     bs->bl.min_mem_alignment = s->buf_align; | ||||
|     bs->bl.opt_mem_alignment = MAX(s->buf_align, getpagesize()); | ||||
|     bs->bl.opt_mem_alignment = s->buf_align; | ||||
| } | ||||
|  | ||||
| static int check_for_dasd(int fd) | ||||
| @@ -778,6 +771,7 @@ static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     struct hd_geometry ioctl_geo = {0}; | ||||
|     uint32_t blksize; | ||||
|  | ||||
|     /* If DASD, get its geometry */ | ||||
|     if (check_for_dasd(s->fd) < 0) { | ||||
| @@ -797,6 +791,12 @@ static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo) | ||||
|     } | ||||
|     geo->heads = ioctl_geo.heads; | ||||
|     geo->sectors = ioctl_geo.sectors; | ||||
|     if (!probe_physical_blocksize(s->fd, &blksize)) { | ||||
|         /* overwrite cyls: HDIO_GETGEO result is incorrect for big drives */ | ||||
|         geo->cylinders = bdrv_nb_sectors(bs) / (blksize / BDRV_SECTOR_SIZE) | ||||
|                                              / (geo->heads * geo->sectors); | ||||
|         return 0; | ||||
|     } | ||||
|     geo->cylinders = ioctl_geo.cylinders; | ||||
|  | ||||
|     return 0; | ||||
| @@ -1016,7 +1016,6 @@ static ssize_t handle_aiocb_rw(RawPosixAIOData *aiocb) | ||||
| static int xfs_write_zeroes(BDRVRawState *s, int64_t offset, uint64_t bytes) | ||||
| { | ||||
|     struct xfs_flock64 fl; | ||||
|     int err; | ||||
|  | ||||
|     memset(&fl, 0, sizeof(fl)); | ||||
|     fl.l_whence = SEEK_SET; | ||||
| @@ -1024,9 +1023,8 @@ static int xfs_write_zeroes(BDRVRawState *s, int64_t offset, uint64_t bytes) | ||||
|     fl.l_len = bytes; | ||||
|  | ||||
|     if (xfsctl(NULL, s->fd, XFS_IOC_ZERO_RANGE, &fl) < 0) { | ||||
|         err = errno; | ||||
|         DPRINTF("cannot write zero range (%s)\n", strerror(errno)); | ||||
|         return -err; | ||||
|         DEBUG_BLOCK_PRINT("cannot write zero range (%s)\n", strerror(errno)); | ||||
|         return -errno; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| @@ -1035,7 +1033,6 @@ static int xfs_write_zeroes(BDRVRawState *s, int64_t offset, uint64_t bytes) | ||||
| static int xfs_discard(BDRVRawState *s, int64_t offset, uint64_t bytes) | ||||
| { | ||||
|     struct xfs_flock64 fl; | ||||
|     int err; | ||||
|  | ||||
|     memset(&fl, 0, sizeof(fl)); | ||||
|     fl.l_whence = SEEK_SET; | ||||
| @@ -1043,9 +1040,8 @@ static int xfs_discard(BDRVRawState *s, int64_t offset, uint64_t bytes) | ||||
|     fl.l_len = bytes; | ||||
|  | ||||
|     if (xfsctl(NULL, s->fd, XFS_IOC_UNRESVSP64, &fl) < 0) { | ||||
|         err = errno; | ||||
|         DPRINTF("cannot punch hole (%s)\n", strerror(errno)); | ||||
|         return -err; | ||||
|         DEBUG_BLOCK_PRINT("cannot punch hole (%s)\n", strerror(errno)); | ||||
|         return -errno; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| @@ -1244,7 +1240,7 @@ static int aio_worker(void *arg) | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     g_free(aiocb); | ||||
|     g_slice_free(RawPosixAIOData, aiocb); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| @@ -1252,7 +1248,7 @@ static int paio_submit_co(BlockDriverState *bs, int fd, | ||||
|         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | ||||
|         int type) | ||||
| { | ||||
|     RawPosixAIOData *acb = g_new(RawPosixAIOData, 1); | ||||
|     RawPosixAIOData *acb = g_slice_new(RawPosixAIOData); | ||||
|     ThreadPool *pool; | ||||
|  | ||||
|     acb->bs = bs; | ||||
| @@ -1277,7 +1273,7 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, int fd, | ||||
|         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | ||||
|         BlockCompletionFunc *cb, void *opaque, int type) | ||||
| { | ||||
|     RawPosixAIOData *acb = g_new(RawPosixAIOData, 1); | ||||
|     RawPosixAIOData *acb = g_slice_new(RawPosixAIOData); | ||||
|     ThreadPool *pool; | ||||
|  | ||||
|     acb->bs = bs; | ||||
| @@ -1624,7 +1620,7 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
|     nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false); | ||||
|     buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); | ||||
|     prealloc = qapi_enum_parse(PreallocMode_lookup, buf, | ||||
|                                PREALLOC_MODE__MAX, PREALLOC_MODE_OFF, | ||||
|                                PREALLOC_MODE_MAX, PREALLOC_MODE_OFF, | ||||
|                                &local_err); | ||||
|     g_free(buf); | ||||
|     if (local_err) { | ||||
| @@ -1633,7 +1629,7 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     fd = qemu_open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, | ||||
|     fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, | ||||
|                    0644); | ||||
|     if (fd < 0) { | ||||
|         result = -errno; | ||||
| @@ -1818,8 +1814,7 @@ static int find_allocation(BlockDriverState *bs, off_t start, | ||||
|  */ | ||||
| static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, | ||||
|                                                     int64_t sector_num, | ||||
|                                                     int nb_sectors, int *pnum, | ||||
|                                                     BlockDriverState **file) | ||||
|                                                     int nb_sectors, int *pnum) | ||||
| { | ||||
|     off_t start, data = 0, hole = 0; | ||||
|     int64_t total_size; | ||||
| @@ -1851,9 +1846,8 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, | ||||
|         *pnum = nb_sectors; | ||||
|         ret = BDRV_BLOCK_DATA; | ||||
|     } else if (data == start) { | ||||
|         /* On a data extent, compute sectors to the end of the extent, | ||||
|          * possibly including a partial sector at EOF. */ | ||||
|         *pnum = MIN(nb_sectors, DIV_ROUND_UP(hole - start, BDRV_SECTOR_SIZE)); | ||||
|         /* On a data extent, compute sectors to the end of the extent.  */ | ||||
|         *pnum = MIN(nb_sectors, (hole - start) / BDRV_SECTOR_SIZE); | ||||
|         ret = BDRV_BLOCK_DATA; | ||||
|     } else { | ||||
|         /* On a hole, compute sectors to the beginning of the next extent.  */ | ||||
| @@ -1861,7 +1855,6 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, | ||||
|         *pnum = MIN(nb_sectors, (data - start) / BDRV_SECTOR_SIZE); | ||||
|         ret = BDRV_BLOCK_ZERO; | ||||
|     } | ||||
|     *file = bs; | ||||
|     return ret | BDRV_BLOCK_OFFSET_VALID | start; | ||||
| } | ||||
|  | ||||
| @@ -1966,8 +1959,8 @@ BlockDriver bdrv_file = { | ||||
|  | ||||
| #if defined(__APPLE__) && defined(__MACH__) | ||||
| static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator ); | ||||
| static kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath, | ||||
|                                 CFIndex maxPathSize, int flags); | ||||
| static kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize ); | ||||
|  | ||||
| kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator ) | ||||
| { | ||||
|     kern_return_t       kernResult; | ||||
| @@ -1994,8 +1987,7 @@ kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator ) | ||||
|     return kernResult; | ||||
| } | ||||
|  | ||||
| kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath, | ||||
|                          CFIndex maxPathSize, int flags) | ||||
| kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize ) | ||||
| { | ||||
|     io_object_t     nextMedia; | ||||
|     kern_return_t   kernResult = KERN_FAILURE; | ||||
| @@ -2008,9 +2000,7 @@ kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath, | ||||
|         if ( bsdPathAsCFString ) { | ||||
|             size_t devPathLength; | ||||
|             strcpy( bsdPath, _PATH_DEV ); | ||||
|             if (flags & BDRV_O_NOCACHE) { | ||||
|             strcat( bsdPath, "r" ); | ||||
|             } | ||||
|             devPathLength = strlen( bsdPath ); | ||||
|             if ( CFStringGetCString( bsdPathAsCFString, bsdPath + devPathLength, maxPathSize - devPathLength, kCFStringEncodingASCII ) ) { | ||||
|                 kernResult = KERN_SUCCESS; | ||||
| @@ -2083,38 +2073,15 @@ static void hdev_parse_filename(const char *filename, QDict *options, | ||||
|     qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename))); | ||||
| } | ||||
|  | ||||
| static bool hdev_is_sg(BlockDriverState *bs) | ||||
| { | ||||
|  | ||||
| #if defined(__linux__) | ||||
|  | ||||
|     struct stat st; | ||||
|     struct sg_scsi_id scsiid; | ||||
|     int sg_version; | ||||
|  | ||||
|     if (stat(bs->filename, &st) >= 0 && S_ISCHR(st.st_mode) && | ||||
|         !bdrv_ioctl(bs, SG_GET_VERSION_NUM, &sg_version) && | ||||
|         !bdrv_ioctl(bs, SG_GET_SCSI_ID, &scsiid)) { | ||||
|         DPRINTF("SG device found: type=%d, version=%d\n", | ||||
|             scsiid.scsi_type, sg_version); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
| #endif | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| static int hdev_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                      Error **errp) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     Error *local_err = NULL; | ||||
|     int ret; | ||||
|  | ||||
| #if defined(__APPLE__) && defined(__MACH__) | ||||
|     const char *filename = qdict_get_str(options, "filename"); | ||||
|  | ||||
| #if defined(__APPLE__) && defined(__MACH__) | ||||
|     if (strstart(filename, "/dev/cdrom", NULL)) { | ||||
|         kern_return_t kernResult; | ||||
|         io_iterator_t mediaIterator; | ||||
| @@ -2122,8 +2089,8 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         int fd; | ||||
|  | ||||
|         kernResult = FindEjectableCDMedia( &mediaIterator ); | ||||
|         kernResult = GetBSDPath(mediaIterator, bsdPath, sizeof(bsdPath), | ||||
|                                 flags); | ||||
|         kernResult = GetBSDPath( mediaIterator, bsdPath, sizeof( bsdPath ) ); | ||||
|  | ||||
|         if ( bsdPath[ 0 ] != '\0' ) { | ||||
|             strcat(bsdPath,"s0"); | ||||
|             /* some CDs don't have a partition 0 */ | ||||
| @@ -2143,6 +2110,16 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags, | ||||
| #endif | ||||
|  | ||||
|     s->type = FTYPE_FILE; | ||||
| #if defined(__linux__) | ||||
|     { | ||||
|         char resolved_path[ MAXPATHLEN ], *temp; | ||||
|  | ||||
|         temp = realpath(filename, resolved_path); | ||||
|         if (temp && strstart(temp, "/dev/sg", NULL)) { | ||||
|             bs->sg = 1; | ||||
|         } | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     ret = raw_open_common(bs, options, flags, 0, &local_err); | ||||
|     if (ret < 0) { | ||||
| @@ -2152,9 +2129,6 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     /* Since this does ioctl the device must be already opened */ | ||||
|     bs->sg = hdev_is_sg(bs); | ||||
|  | ||||
|     if (flags & BDRV_O_RDWR) { | ||||
|         ret = check_hdev_writable(s); | ||||
|         if (ret < 0) { | ||||
| @@ -2168,6 +2142,61 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags, | ||||
| } | ||||
|  | ||||
| #if defined(__linux__) | ||||
| /* Note: we do not have a reliable method to detect if the floppy is | ||||
|    present. The current method is to try to open the floppy at every | ||||
|    I/O and to keep it opened during a few hundreds of ms. */ | ||||
| static int fd_open(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     int last_media_present; | ||||
|  | ||||
|     if (s->type != FTYPE_FD) | ||||
|         return 0; | ||||
|     last_media_present = (s->fd >= 0); | ||||
|     if (s->fd >= 0 && | ||||
|         (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - s->fd_open_time) >= FD_OPEN_TIMEOUT) { | ||||
|         qemu_close(s->fd); | ||||
|         s->fd = -1; | ||||
| #ifdef DEBUG_FLOPPY | ||||
|         printf("Floppy closed\n"); | ||||
| #endif | ||||
|     } | ||||
|     if (s->fd < 0) { | ||||
|         if (s->fd_got_error && | ||||
|             (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - s->fd_error_time) < FD_OPEN_TIMEOUT) { | ||||
| #ifdef DEBUG_FLOPPY | ||||
|             printf("No floppy (open delayed)\n"); | ||||
| #endif | ||||
|             return -EIO; | ||||
|         } | ||||
|         s->fd = qemu_open(bs->filename, s->open_flags & ~O_NONBLOCK); | ||||
|         if (s->fd < 0) { | ||||
|             s->fd_error_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); | ||||
|             s->fd_got_error = 1; | ||||
|             if (last_media_present) | ||||
|                 s->fd_media_changed = 1; | ||||
| #ifdef DEBUG_FLOPPY | ||||
|             printf("No floppy\n"); | ||||
| #endif | ||||
|             return -EIO; | ||||
|         } | ||||
| #ifdef DEBUG_FLOPPY | ||||
|         printf("Floppy opened\n"); | ||||
| #endif | ||||
|     } | ||||
|     if (!last_media_present) | ||||
|         s->fd_media_changed = 1; | ||||
|     s->fd_open_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); | ||||
|     s->fd_got_error = 0; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int hdev_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|  | ||||
|     return ioctl(s->fd, req, buf); | ||||
| } | ||||
|  | ||||
| static BlockAIOCB *hdev_aio_ioctl(BlockDriverState *bs, | ||||
|         unsigned long int req, void *buf, | ||||
| @@ -2180,7 +2209,7 @@ static BlockAIOCB *hdev_aio_ioctl(BlockDriverState *bs, | ||||
|     if (fd_open(bs) < 0) | ||||
|         return NULL; | ||||
|  | ||||
|     acb = g_new(RawPosixAIOData, 1); | ||||
|     acb = g_slice_new(RawPosixAIOData); | ||||
|     acb->bs = bs; | ||||
|     acb->aio_type = QEMU_AIO_IOCTL; | ||||
|     acb->aio_fildes = s->fd; | ||||
| @@ -2190,8 +2219,8 @@ static BlockAIOCB *hdev_aio_ioctl(BlockDriverState *bs, | ||||
|     pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); | ||||
|     return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); | ||||
| } | ||||
| #endif /* linux */ | ||||
|  | ||||
| #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) | ||||
| static int fd_open(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
| @@ -2201,6 +2230,14 @@ static int fd_open(BlockDriverState *bs) | ||||
|         return 0; | ||||
|     return -EIO; | ||||
| } | ||||
| #else /* !linux && !FreeBSD */ | ||||
|  | ||||
| static int fd_open(BlockDriverState *bs) | ||||
| { | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| #endif /* !linux && !FreeBSD */ | ||||
|  | ||||
| static coroutine_fn BlockAIOCB *hdev_aio_discard(BlockDriverState *bs, | ||||
|     int64_t sector_num, int nb_sectors, | ||||
| @@ -2244,22 +2281,17 @@ static int hdev_create(const char *filename, QemuOpts *opts, | ||||
|     int64_t total_size = 0; | ||||
|     bool has_prefix; | ||||
|  | ||||
|     /* This function is used by both protocol block drivers and therefore either | ||||
|      * of these prefixes may be given. | ||||
|     /* This function is used by all three protocol block drivers and therefore | ||||
|      * any of these three prefixes may be given. | ||||
|      * The return value has to be stored somewhere, otherwise this is an error | ||||
|      * due to -Werror=unused-value. */ | ||||
|     has_prefix = | ||||
|         strstart(filename, "host_device:", &filename) || | ||||
|         strstart(filename, "host_cdrom:" , &filename); | ||||
|         strstart(filename, "host_cdrom:" , &filename) || | ||||
|         strstart(filename, "host_floppy:", &filename); | ||||
|  | ||||
|     (void)has_prefix; | ||||
|  | ||||
|     ret = raw_normalize_devicepath(&filename); | ||||
|     if (ret < 0) { | ||||
|         error_setg_errno(errp, -ret, "Could not normalize device path"); | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     /* Read out options */ | ||||
|     total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), | ||||
|                           BDRV_SECTOR_SIZE); | ||||
| @@ -2325,10 +2357,161 @@ static BlockDriver bdrv_host_device = { | ||||
|  | ||||
|     /* generic scsi device */ | ||||
| #ifdef __linux__ | ||||
|     .bdrv_ioctl         = hdev_ioctl, | ||||
|     .bdrv_aio_ioctl     = hdev_aio_ioctl, | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| #ifdef __linux__ | ||||
| static void floppy_parse_filename(const char *filename, QDict *options, | ||||
|                                   Error **errp) | ||||
| { | ||||
|     /* The prefix is optional, just as for "file". */ | ||||
|     strstart(filename, "host_floppy:", &filename); | ||||
|  | ||||
|     qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename))); | ||||
| } | ||||
|  | ||||
| static int floppy_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                        Error **errp) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     Error *local_err = NULL; | ||||
|     int ret; | ||||
|  | ||||
|     s->type = FTYPE_FD; | ||||
|  | ||||
|     /* open will not fail even if no floppy is inserted, so add O_NONBLOCK */ | ||||
|     ret = raw_open_common(bs, options, flags, O_NONBLOCK, &local_err); | ||||
|     if (ret) { | ||||
|         if (local_err) { | ||||
|             error_propagate(errp, local_err); | ||||
|         } | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     /* close fd so that we can reopen it as needed */ | ||||
|     qemu_close(s->fd); | ||||
|     s->fd = -1; | ||||
|     s->fd_media_changed = 1; | ||||
|  | ||||
|     error_report("Host floppy pass-through is deprecated"); | ||||
|     error_printf("Support for it will be removed in a future release.\n"); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int floppy_probe_device(const char *filename) | ||||
| { | ||||
|     int fd, ret; | ||||
|     int prio = 0; | ||||
|     struct floppy_struct fdparam; | ||||
|     struct stat st; | ||||
|  | ||||
|     if (strstart(filename, "/dev/fd", NULL) && | ||||
|         !strstart(filename, "/dev/fdset/", NULL)) { | ||||
|         prio = 50; | ||||
|     } | ||||
|  | ||||
|     fd = qemu_open(filename, O_RDONLY | O_NONBLOCK); | ||||
|     if (fd < 0) { | ||||
|         goto out; | ||||
|     } | ||||
|     ret = fstat(fd, &st); | ||||
|     if (ret == -1 || !S_ISBLK(st.st_mode)) { | ||||
|         goto outc; | ||||
|     } | ||||
|  | ||||
|     /* Attempt to detect via a floppy specific ioctl */ | ||||
|     ret = ioctl(fd, FDGETPRM, &fdparam); | ||||
|     if (ret >= 0) | ||||
|         prio = 100; | ||||
|  | ||||
| outc: | ||||
|     qemu_close(fd); | ||||
| out: | ||||
|     return prio; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int floppy_is_inserted(BlockDriverState *bs) | ||||
| { | ||||
|     return fd_open(bs) >= 0; | ||||
| } | ||||
|  | ||||
| static int floppy_media_changed(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     int ret; | ||||
|  | ||||
|     /* | ||||
|      * XXX: we do not have a true media changed indication. | ||||
|      * It does not work if the floppy is changed without trying to read it. | ||||
|      */ | ||||
|     fd_open(bs); | ||||
|     ret = s->fd_media_changed; | ||||
|     s->fd_media_changed = 0; | ||||
| #ifdef DEBUG_FLOPPY | ||||
|     printf("Floppy changed=%d\n", ret); | ||||
| #endif | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static void floppy_eject(BlockDriverState *bs, bool eject_flag) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     int fd; | ||||
|  | ||||
|     if (s->fd >= 0) { | ||||
|         qemu_close(s->fd); | ||||
|         s->fd = -1; | ||||
|     } | ||||
|     fd = qemu_open(bs->filename, s->open_flags | O_NONBLOCK); | ||||
|     if (fd >= 0) { | ||||
|         if (ioctl(fd, FDEJECT, 0) < 0) | ||||
|             perror("FDEJECT"); | ||||
|         qemu_close(fd); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static BlockDriver bdrv_host_floppy = { | ||||
|     .format_name        = "host_floppy", | ||||
|     .protocol_name      = "host_floppy", | ||||
|     .instance_size      = sizeof(BDRVRawState), | ||||
|     .bdrv_needs_filename = true, | ||||
|     .bdrv_probe_device	= floppy_probe_device, | ||||
|     .bdrv_parse_filename = floppy_parse_filename, | ||||
|     .bdrv_file_open     = floppy_open, | ||||
|     .bdrv_close         = raw_close, | ||||
|     .bdrv_reopen_prepare = raw_reopen_prepare, | ||||
|     .bdrv_reopen_commit  = raw_reopen_commit, | ||||
|     .bdrv_reopen_abort   = raw_reopen_abort, | ||||
|     .bdrv_create         = hdev_create, | ||||
|     .create_opts         = &raw_create_opts, | ||||
|  | ||||
|     .bdrv_aio_readv     = raw_aio_readv, | ||||
|     .bdrv_aio_writev    = raw_aio_writev, | ||||
|     .bdrv_aio_flush	= raw_aio_flush, | ||||
|     .bdrv_refresh_limits = raw_refresh_limits, | ||||
|     .bdrv_io_plug = raw_aio_plug, | ||||
|     .bdrv_io_unplug = raw_aio_unplug, | ||||
|     .bdrv_flush_io_queue = raw_aio_flush_io_queue, | ||||
|  | ||||
|     .bdrv_truncate      = raw_truncate, | ||||
|     .bdrv_getlength      = raw_getlength, | ||||
|     .has_variable_length = true, | ||||
|     .bdrv_get_allocated_file_size | ||||
|                         = raw_get_allocated_file_size, | ||||
|  | ||||
|     .bdrv_detach_aio_context = raw_detach_aio_context, | ||||
|     .bdrv_attach_aio_context = raw_attach_aio_context, | ||||
|  | ||||
|     /* removable device support */ | ||||
|     .bdrv_is_inserted   = floppy_is_inserted, | ||||
|     .bdrv_media_changed = floppy_media_changed, | ||||
|     .bdrv_eject         = floppy_eject, | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| #if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) | ||||
| static void cdrom_parse_filename(const char *filename, QDict *options, | ||||
|                                  Error **errp) | ||||
| @@ -2384,13 +2567,15 @@ out: | ||||
|     return prio; | ||||
| } | ||||
|  | ||||
| static bool cdrom_is_inserted(BlockDriverState *bs) | ||||
| static int cdrom_is_inserted(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     int ret; | ||||
|  | ||||
|     ret = ioctl(s->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT); | ||||
|     return ret == CDS_DISC_OK; | ||||
|     if (ret == CDS_DISC_OK) | ||||
|         return 1; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void cdrom_eject(BlockDriverState *bs, bool eject_flag) | ||||
| @@ -2457,6 +2642,7 @@ static BlockDriver bdrv_host_cdrom = { | ||||
|     .bdrv_lock_medium   = cdrom_lock_medium, | ||||
|  | ||||
|     /* generic scsi device */ | ||||
|     .bdrv_ioctl         = hdev_ioctl, | ||||
|     .bdrv_aio_ioctl     = hdev_aio_ioctl, | ||||
| }; | ||||
| #endif /* __linux__ */ | ||||
| @@ -2515,7 +2701,7 @@ static int cdrom_reopen(BlockDriverState *bs) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static bool cdrom_is_inserted(BlockDriverState *bs) | ||||
| static int cdrom_is_inserted(BlockDriverState *bs) | ||||
| { | ||||
|     return raw_getlength(bs) > 0; | ||||
| } | ||||
| @@ -2603,6 +2789,7 @@ static void bdrv_file_init(void) | ||||
|     bdrv_register(&bdrv_file); | ||||
|     bdrv_register(&bdrv_host_device); | ||||
| #ifdef __linux__ | ||||
|     bdrv_register(&bdrv_host_floppy); | ||||
|     bdrv_register(&bdrv_host_cdrom); | ||||
| #endif | ||||
| #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) | ||||
|   | ||||
| @@ -21,7 +21,6 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "qemu/timer.h" | ||||
| #include "block/block_int.h" | ||||
| @@ -30,7 +29,6 @@ | ||||
| #include "trace.h" | ||||
| #include "block/thread-pool.h" | ||||
| #include "qemu/iov.h" | ||||
| #include "qapi/qmp/qstring.h" | ||||
| #include <windows.h> | ||||
| #include <winioctl.h> | ||||
|  | ||||
| @@ -120,9 +118,9 @@ static int aio_worker(void *arg) | ||||
|     case QEMU_AIO_WRITE: | ||||
|         count = handle_aiocb_rw(aiocb); | ||||
|         if (count == aiocb->aio_nbytes) { | ||||
|             ret = 0; | ||||
|             count = 0; | ||||
|         } else { | ||||
|             ret = -EINVAL; | ||||
|             count = -EINVAL; | ||||
|         } | ||||
|         break; | ||||
|     case QEMU_AIO_FLUSH: | ||||
| @@ -136,7 +134,7 @@ static int aio_worker(void *arg) | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     g_free(aiocb); | ||||
|     g_slice_free(RawWin32AIOData, aiocb); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| @@ -144,7 +142,7 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile, | ||||
|         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | ||||
|         BlockCompletionFunc *cb, void *opaque, int type) | ||||
| { | ||||
|     RawWin32AIOData *acb = g_new(RawWin32AIOData, 1); | ||||
|     RawWin32AIOData *acb = g_slice_new(RawWin32AIOData); | ||||
|     ThreadPool *pool; | ||||
|  | ||||
|     acb->bs = bs; | ||||
|   | ||||
| @@ -26,7 +26,6 @@ | ||||
|  * IN THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "block/block_int.h" | ||||
| #include "qemu/option.h" | ||||
|  | ||||
| @@ -53,7 +52,7 @@ static int coroutine_fn raw_co_readv(BlockDriverState *bs, int64_t sector_num, | ||||
|                                      int nb_sectors, QEMUIOVector *qiov) | ||||
| { | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); | ||||
|     return bdrv_co_readv(bs->file->bs, sector_num, nb_sectors, qiov); | ||||
|     return bdrv_co_readv(bs->file, sector_num, nb_sectors, qiov); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn raw_co_writev(BlockDriverState *bs, int64_t sector_num, | ||||
| @@ -76,7 +75,7 @@ static int coroutine_fn raw_co_writev(BlockDriverState *bs, int64_t sector_num, | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
|         buf = qemu_try_blockalign(bs->file->bs, 512); | ||||
|         buf = qemu_try_blockalign(bs->file, 512); | ||||
|         if (!buf) { | ||||
|             ret = -ENOMEM; | ||||
|             goto fail; | ||||
| @@ -103,7 +102,7 @@ static int coroutine_fn raw_co_writev(BlockDriverState *bs, int64_t sector_num, | ||||
|     } | ||||
|  | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); | ||||
|     ret = bdrv_co_writev(bs->file->bs, sector_num, nb_sectors, qiov); | ||||
|     ret = bdrv_co_writev(bs->file, sector_num, nb_sectors, qiov); | ||||
|  | ||||
| fail: | ||||
|     if (qiov == &local_qiov) { | ||||
| @@ -115,11 +114,9 @@ fail: | ||||
|  | ||||
| static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, | ||||
|                                             int64_t sector_num, | ||||
|                                             int nb_sectors, int *pnum, | ||||
|                                             BlockDriverState **file) | ||||
|                                             int nb_sectors, int *pnum) | ||||
| { | ||||
|     *pnum = nb_sectors; | ||||
|     *file = bs->file->bs; | ||||
|     return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA | | ||||
|            (sector_num << BDRV_SECTOR_BITS); | ||||
| } | ||||
| @@ -128,48 +125,58 @@ static int coroutine_fn raw_co_write_zeroes(BlockDriverState *bs, | ||||
|                                             int64_t sector_num, int nb_sectors, | ||||
|                                             BdrvRequestFlags flags) | ||||
| { | ||||
|     return bdrv_co_write_zeroes(bs->file->bs, sector_num, nb_sectors, flags); | ||||
|     return bdrv_co_write_zeroes(bs->file, sector_num, nb_sectors, flags); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn raw_co_discard(BlockDriverState *bs, | ||||
|                                        int64_t sector_num, int nb_sectors) | ||||
| { | ||||
|     return bdrv_co_discard(bs->file->bs, sector_num, nb_sectors); | ||||
|     return bdrv_co_discard(bs->file, sector_num, nb_sectors); | ||||
| } | ||||
|  | ||||
| static int64_t raw_getlength(BlockDriverState *bs) | ||||
| { | ||||
|     return bdrv_getlength(bs->file->bs); | ||||
|     return bdrv_getlength(bs->file); | ||||
| } | ||||
|  | ||||
| static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) | ||||
| { | ||||
|     return bdrv_get_info(bs->file->bs, bdi); | ||||
|     return bdrv_get_info(bs->file, bdi); | ||||
| } | ||||
|  | ||||
| static void raw_refresh_limits(BlockDriverState *bs, Error **errp) | ||||
| { | ||||
|     bs->bl = bs->file->bs->bl; | ||||
|     bs->bl = bs->file->bl; | ||||
| } | ||||
|  | ||||
| static int raw_truncate(BlockDriverState *bs, int64_t offset) | ||||
| { | ||||
|     return bdrv_truncate(bs->file->bs, offset); | ||||
|     return bdrv_truncate(bs->file, offset); | ||||
| } | ||||
|  | ||||
| static int raw_is_inserted(BlockDriverState *bs) | ||||
| { | ||||
|     return bdrv_is_inserted(bs->file); | ||||
| } | ||||
|  | ||||
| static int raw_media_changed(BlockDriverState *bs) | ||||
| { | ||||
|     return bdrv_media_changed(bs->file->bs); | ||||
|     return bdrv_media_changed(bs->file); | ||||
| } | ||||
|  | ||||
| static void raw_eject(BlockDriverState *bs, bool eject_flag) | ||||
| { | ||||
|     bdrv_eject(bs->file->bs, eject_flag); | ||||
|     bdrv_eject(bs->file, eject_flag); | ||||
| } | ||||
|  | ||||
| static void raw_lock_medium(BlockDriverState *bs, bool locked) | ||||
| { | ||||
|     bdrv_lock_medium(bs->file->bs, locked); | ||||
|     bdrv_lock_medium(bs->file, locked); | ||||
| } | ||||
|  | ||||
| static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) | ||||
| { | ||||
|     return bdrv_ioctl(bs->file, req, buf); | ||||
| } | ||||
|  | ||||
| static BlockAIOCB *raw_aio_ioctl(BlockDriverState *bs, | ||||
| @@ -177,12 +184,12 @@ static BlockAIOCB *raw_aio_ioctl(BlockDriverState *bs, | ||||
|                                  BlockCompletionFunc *cb, | ||||
|                                  void *opaque) | ||||
| { | ||||
|     return bdrv_aio_ioctl(bs->file->bs, req, buf, cb, opaque); | ||||
|     return bdrv_aio_ioctl(bs->file, req, buf, cb, opaque); | ||||
| } | ||||
|  | ||||
| static int raw_has_zero_init(BlockDriverState *bs) | ||||
| { | ||||
|     return bdrv_has_zero_init(bs->file->bs); | ||||
|     return bdrv_has_zero_init(bs->file); | ||||
| } | ||||
|  | ||||
| static int raw_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
| @@ -200,7 +207,7 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
| static int raw_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                     Error **errp) | ||||
| { | ||||
|     bs->sg = bs->file->bs->sg; | ||||
|     bs->sg = bs->file->sg; | ||||
|  | ||||
|     if (bs->probed && !bdrv_is_read_only(bs)) { | ||||
|         fprintf(stderr, | ||||
| @@ -210,7 +217,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                 "raw images, write operations on block 0 will be restricted.\n" | ||||
|                 "         Specify the 'raw' format explicitly to remove the " | ||||
|                 "restrictions.\n", | ||||
|                 bs->file->bs->filename); | ||||
|                 bs->file->filename); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| @@ -230,12 +237,12 @@ static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||
|  | ||||
| static int raw_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) | ||||
| { | ||||
|     return bdrv_probe_blocksizes(bs->file->bs, bsz); | ||||
|     return bdrv_probe_blocksizes(bs->file, bsz); | ||||
| } | ||||
|  | ||||
| static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) | ||||
| { | ||||
|     return bdrv_probe_geometry(bs->file->bs, geo); | ||||
|     return bdrv_probe_geometry(bs->file, geo); | ||||
| } | ||||
|  | ||||
| BlockDriver bdrv_raw = { | ||||
| @@ -257,9 +264,11 @@ BlockDriver bdrv_raw = { | ||||
|     .bdrv_refresh_limits  = &raw_refresh_limits, | ||||
|     .bdrv_probe_blocksizes = &raw_probe_blocksizes, | ||||
|     .bdrv_probe_geometry  = &raw_probe_geometry, | ||||
|     .bdrv_is_inserted     = &raw_is_inserted, | ||||
|     .bdrv_media_changed   = &raw_media_changed, | ||||
|     .bdrv_eject           = &raw_eject, | ||||
|     .bdrv_lock_medium     = &raw_lock_medium, | ||||
|     .bdrv_ioctl           = &raw_ioctl, | ||||
|     .bdrv_aio_ioctl       = &raw_aio_ioctl, | ||||
|     .create_opts          = &raw_create_opts, | ||||
|     .bdrv_has_zero_init   = &raw_has_zero_init | ||||
|   | ||||
							
								
								
									
										60
									
								
								block/rbd.c
									
									
									
									
									
								
							
							
						
						
									
										60
									
								
								block/rbd.c
									
									
									
									
									
								
							| @@ -11,7 +11,7 @@ | ||||
|  * GNU GPL, version 2 or (at your option) any later version. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include <inttypes.h> | ||||
|  | ||||
| #include "qemu-common.h" | ||||
| #include "qemu/error-report.h" | ||||
| @@ -74,18 +74,25 @@ typedef struct RBDAIOCB { | ||||
|     QEMUIOVector *qiov; | ||||
|     char *bounce; | ||||
|     RBDAIOCmd cmd; | ||||
|     int64_t sector_num; | ||||
|     int error; | ||||
|     struct BDRVRBDState *s; | ||||
|     int status; | ||||
| } RBDAIOCB; | ||||
|  | ||||
| typedef struct RADOSCB { | ||||
|     int rcbid; | ||||
|     RBDAIOCB *acb; | ||||
|     struct BDRVRBDState *s; | ||||
|     int done; | ||||
|     int64_t size; | ||||
|     char *buf; | ||||
|     int64_t ret; | ||||
| } RADOSCB; | ||||
|  | ||||
| #define RBD_FD_READ 0 | ||||
| #define RBD_FD_WRITE 1 | ||||
|  | ||||
| typedef struct BDRVRBDState { | ||||
|     rados_t cluster; | ||||
|     rados_ioctx_t io_ctx; | ||||
| @@ -228,9 +235,7 @@ static char *qemu_rbd_parse_clientname(const char *conf, char *clientname) | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| static int qemu_rbd_set_conf(rados_t cluster, const char *conf, | ||||
|                              bool only_read_conf_file, | ||||
|                              Error **errp) | ||||
| static int qemu_rbd_set_conf(rados_t cluster, const char *conf, Error **errp) | ||||
| { | ||||
|     char *p, *buf; | ||||
|     char name[RBD_MAX_CONF_NAME_SIZE]; | ||||
| @@ -262,18 +267,14 @@ static int qemu_rbd_set_conf(rados_t cluster, const char *conf, | ||||
|         qemu_rbd_unescape(value); | ||||
|  | ||||
|         if (strcmp(name, "conf") == 0) { | ||||
|             /* read the conf file alone, so it doesn't override more | ||||
|                specific settings for a particular device */ | ||||
|             if (only_read_conf_file) { | ||||
|             ret = rados_conf_read_file(cluster, value); | ||||
|             if (ret < 0) { | ||||
|                 error_setg(errp, "error reading conf file %s", value); | ||||
|                 break; | ||||
|             } | ||||
|             } | ||||
|         } else if (strcmp(name, "id") == 0) { | ||||
|             /* ignore, this is parsed by qemu_rbd_parse_clientname() */ | ||||
|         } else if (!only_read_conf_file) { | ||||
|         } else { | ||||
|             ret = rados_conf_set(cluster, name, value); | ||||
|             if (ret < 0) { | ||||
|                 error_setg(errp, "invalid conf option %s", name); | ||||
| @@ -324,7 +325,7 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
|             error_setg(errp, "obj size too small"); | ||||
|             return -EINVAL; | ||||
|         } | ||||
|         obj_order = ctz32(objsize); | ||||
|         obj_order = ffs(objsize) - 1; | ||||
|     } | ||||
|  | ||||
|     clientname = qemu_rbd_parse_clientname(conf, clientname_buf); | ||||
| @@ -336,15 +337,10 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
|     if (strstr(conf, "conf=") == NULL) { | ||||
|         /* try default location, but ignore failure */ | ||||
|         rados_conf_read_file(cluster, NULL); | ||||
|     } else if (conf[0] != '\0' && | ||||
|                qemu_rbd_set_conf(cluster, conf, true, &local_err) < 0) { | ||||
|         rados_shutdown(cluster); | ||||
|         error_propagate(errp, local_err); | ||||
|         return -EIO; | ||||
|     } | ||||
|  | ||||
|     if (conf[0] != '\0' && | ||||
|         qemu_rbd_set_conf(cluster, conf, false, &local_err) < 0) { | ||||
|         qemu_rbd_set_conf(cluster, conf, &local_err) < 0) { | ||||
|         rados_shutdown(cluster); | ||||
|         error_propagate(errp, local_err); | ||||
|         return -EIO; | ||||
| @@ -409,6 +405,7 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb) | ||||
|     } | ||||
|     qemu_vfree(acb->bounce); | ||||
|     acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret)); | ||||
|     acb->status = 0; | ||||
|  | ||||
|     qemu_aio_unref(acb); | ||||
| } | ||||
| @@ -471,23 +468,6 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         s->snap = g_strdup(snap_buf); | ||||
|     } | ||||
|  | ||||
|     if (strstr(conf, "conf=") == NULL) { | ||||
|         /* try default location, but ignore failure */ | ||||
|         rados_conf_read_file(s->cluster, NULL); | ||||
|     } else if (conf[0] != '\0') { | ||||
|         r = qemu_rbd_set_conf(s->cluster, conf, true, errp); | ||||
|         if (r < 0) { | ||||
|             goto failed_shutdown; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (conf[0] != '\0') { | ||||
|         r = qemu_rbd_set_conf(s->cluster, conf, false, errp); | ||||
|         if (r < 0) { | ||||
|             goto failed_shutdown; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * Fallback to more conservative semantics if setting cache | ||||
|      * options fails. Ignore errors from setting rbd_cache because the | ||||
| @@ -501,6 +481,18 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         rados_conf_set(s->cluster, "rbd_cache", "true"); | ||||
|     } | ||||
|  | ||||
|     if (strstr(conf, "conf=") == NULL) { | ||||
|         /* try default location, but ignore failure */ | ||||
|         rados_conf_read_file(s->cluster, NULL); | ||||
|     } | ||||
|  | ||||
|     if (conf[0] != '\0') { | ||||
|         r = qemu_rbd_set_conf(s->cluster, conf, errp); | ||||
|         if (r < 0) { | ||||
|             goto failed_shutdown; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     r = rados_connect(s->cluster); | ||||
|     if (r < 0) { | ||||
|         error_setg(errp, "error connecting"); | ||||
| @@ -629,6 +621,7 @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs, | ||||
|     acb->error = 0; | ||||
|     acb->s = s; | ||||
|     acb->bh = NULL; | ||||
|     acb->status = -EINPROGRESS; | ||||
|  | ||||
|     if (cmd == RBD_AIO_WRITE) { | ||||
|         qemu_iovec_to_buf(acb->qiov, 0, acb->bounce, qiov->size); | ||||
| @@ -640,6 +633,7 @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs, | ||||
|     size = nb_sectors * BDRV_SECTOR_SIZE; | ||||
|  | ||||
|     rcb = g_new(RADOSCB, 1); | ||||
|     rcb->done = 0; | ||||
|     rcb->acb = acb; | ||||
|     rcb->buf = buf; | ||||
|     rcb->s = acb->s; | ||||
|   | ||||
							
								
								
									
										340
									
								
								block/sheepdog.c
									
									
									
									
									
								
							
							
						
						
									
										340
									
								
								block/sheepdog.c
									
									
									
									
									
								
							| @@ -12,7 +12,6 @@ | ||||
|  * GNU GPL, version 2 or (at your option) any later version. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "qemu/uri.h" | ||||
| #include "qemu/error-report.h" | ||||
| @@ -29,6 +28,7 @@ | ||||
| #define SD_OP_READ_OBJ       0x02 | ||||
| #define SD_OP_WRITE_OBJ      0x03 | ||||
| /* 0x04 is used internally by Sheepdog */ | ||||
| #define SD_OP_DISCARD_OBJ    0x05 | ||||
|  | ||||
| #define SD_OP_NEW_VDI        0x11 | ||||
| #define SD_OP_LOCK_VDI       0x12 | ||||
| @@ -318,10 +318,6 @@ enum AIOCBState { | ||||
|     AIOCB_DISCARD_OBJ, | ||||
| }; | ||||
|  | ||||
| #define AIOCBOverlapping(x, y)                                 \ | ||||
|     (!(x->max_affect_data_idx < y->min_affect_data_idx          \ | ||||
|        || y->max_affect_data_idx < x->min_affect_data_idx)) | ||||
|  | ||||
| struct SheepdogAIOCB { | ||||
|     BlockAIOCB common; | ||||
|  | ||||
| @@ -338,20 +334,6 @@ struct SheepdogAIOCB { | ||||
|  | ||||
|     bool cancelable; | ||||
|     int nr_pending; | ||||
|  | ||||
|     uint32_t min_affect_data_idx; | ||||
|     uint32_t max_affect_data_idx; | ||||
|  | ||||
|     /* | ||||
|      * The difference between affect_data_idx and dirty_data_idx: | ||||
|      * affect_data_idx represents range of index of all request types. | ||||
|      * dirty_data_idx represents range of index updated by COW requests. | ||||
|      * dirty_data_idx is used for updating an inode object. | ||||
|      */ | ||||
|     uint32_t min_dirty_data_idx; | ||||
|     uint32_t max_dirty_data_idx; | ||||
|  | ||||
|     QLIST_ENTRY(SheepdogAIOCB) aiocb_siblings; | ||||
| }; | ||||
|  | ||||
| typedef struct BDRVSheepdogState { | ||||
| @@ -360,6 +342,9 @@ typedef struct BDRVSheepdogState { | ||||
|  | ||||
|     SheepdogInode inode; | ||||
|  | ||||
|     uint32_t min_dirty_data_idx; | ||||
|     uint32_t max_dirty_data_idx; | ||||
|  | ||||
|     char name[SD_MAX_VDI_LEN]; | ||||
|     bool is_snapshot; | ||||
|     uint32_t cache_flags; | ||||
| @@ -377,17 +362,10 @@ typedef struct BDRVSheepdogState { | ||||
|  | ||||
|     /* Every aio request must be linked to either of these queues. */ | ||||
|     QLIST_HEAD(inflight_aio_head, AIOReq) inflight_aio_head; | ||||
|     QLIST_HEAD(pending_aio_head, AIOReq) pending_aio_head; | ||||
|     QLIST_HEAD(failed_aio_head, AIOReq) failed_aio_head; | ||||
|  | ||||
|     CoQueue overlapping_queue; | ||||
|     QLIST_HEAD(inflight_aiocb_head, SheepdogAIOCB) inflight_aiocb_head; | ||||
| } BDRVSheepdogState; | ||||
|  | ||||
| typedef struct BDRVSheepdogReopenState { | ||||
|     int fd; | ||||
|     int cache_flags; | ||||
| } BDRVSheepdogReopenState; | ||||
|  | ||||
| static const char * sd_strerror(int err) | ||||
| { | ||||
|     int i; | ||||
| @@ -520,7 +498,13 @@ static void sd_aio_cancel(BlockAIOCB *blockacb) | ||||
|     AIOReq *aioreq, *next; | ||||
|  | ||||
|     if (sd_acb_cancelable(acb)) { | ||||
|         /* Remove outstanding requests from failed queue.  */ | ||||
|         /* Remove outstanding requests from pending and failed queues.  */ | ||||
|         QLIST_FOREACH_SAFE(aioreq, &s->pending_aio_head, aio_siblings, | ||||
|                            next) { | ||||
|             if (aioreq->aiocb == acb) { | ||||
|                 free_aio_req(s, aioreq); | ||||
|             } | ||||
|         } | ||||
|         QLIST_FOREACH_SAFE(aioreq, &s->failed_aio_head, aio_siblings, | ||||
|                            next) { | ||||
|             if (aioreq->aiocb == acb) { | ||||
| @@ -545,10 +529,6 @@ static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov, | ||||
|                                    int64_t sector_num, int nb_sectors) | ||||
| { | ||||
|     SheepdogAIOCB *acb; | ||||
|     uint32_t object_size; | ||||
|     BDRVSheepdogState *s = bs->opaque; | ||||
|  | ||||
|     object_size = (UINT32_C(1) << s->inode.block_size_shift); | ||||
|  | ||||
|     acb = qemu_aio_get(&sd_aiocb_info, bs, NULL, NULL); | ||||
|  | ||||
| @@ -562,14 +542,6 @@ static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov, | ||||
|     acb->coroutine = qemu_coroutine_self(); | ||||
|     acb->ret = 0; | ||||
|     acb->nr_pending = 0; | ||||
|  | ||||
|     acb->min_affect_data_idx = acb->sector_num * BDRV_SECTOR_SIZE / object_size; | ||||
|     acb->max_affect_data_idx = (acb->sector_num * BDRV_SECTOR_SIZE + | ||||
|                               acb->nb_sectors * BDRV_SECTOR_SIZE) / object_size; | ||||
|  | ||||
|     acb->min_dirty_data_idx = UINT32_MAX; | ||||
|     acb->max_dirty_data_idx = 0; | ||||
|  | ||||
|     return acb; | ||||
| } | ||||
|  | ||||
| @@ -652,16 +624,14 @@ static coroutine_fn void do_co_req(void *opaque) | ||||
|     unsigned int *rlen = srco->rlen; | ||||
|  | ||||
|     co = qemu_coroutine_self(); | ||||
|     aio_set_fd_handler(srco->aio_context, sockfd, false, | ||||
|                        NULL, restart_co_req, co); | ||||
|     aio_set_fd_handler(srco->aio_context, sockfd, NULL, restart_co_req, co); | ||||
|  | ||||
|     ret = send_co_req(sockfd, hdr, data, wlen); | ||||
|     if (ret < 0) { | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     aio_set_fd_handler(srco->aio_context, sockfd, false, | ||||
|                        restart_co_req, NULL, co); | ||||
|     aio_set_fd_handler(srco->aio_context, sockfd, restart_co_req, NULL, co); | ||||
|  | ||||
|     ret = qemu_co_recv(sockfd, hdr, sizeof(*hdr)); | ||||
|     if (ret != sizeof(*hdr)) { | ||||
| @@ -686,8 +656,7 @@ static coroutine_fn void do_co_req(void *opaque) | ||||
| out: | ||||
|     /* there is at most one request for this sockfd, so it is safe to | ||||
|      * set each handler to NULL. */ | ||||
|     aio_set_fd_handler(srco->aio_context, sockfd, false, | ||||
|                        NULL, NULL, NULL); | ||||
|     aio_set_fd_handler(srco->aio_context, sockfd, NULL, NULL, NULL); | ||||
|  | ||||
|     srco->ret = ret; | ||||
|     srco->finished = true; | ||||
| @@ -734,13 +703,44 @@ static int reload_inode(BDRVSheepdogState *s, uint32_t snapid, const char *tag); | ||||
| static int get_sheep_fd(BDRVSheepdogState *s, Error **errp); | ||||
| static void co_write_request(void *opaque); | ||||
|  | ||||
| static AIOReq *find_pending_req(BDRVSheepdogState *s, uint64_t oid) | ||||
| { | ||||
|     AIOReq *aio_req; | ||||
|  | ||||
|     QLIST_FOREACH(aio_req, &s->pending_aio_head, aio_siblings) { | ||||
|         if (aio_req->oid == oid) { | ||||
|             return aio_req; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * This function searchs pending requests to the object `oid', and | ||||
|  * sends them. | ||||
|  */ | ||||
| static void coroutine_fn send_pending_req(BDRVSheepdogState *s, uint64_t oid) | ||||
| { | ||||
|     AIOReq *aio_req; | ||||
|     SheepdogAIOCB *acb; | ||||
|  | ||||
|     while ((aio_req = find_pending_req(s, oid)) != NULL) { | ||||
|         acb = aio_req->aiocb; | ||||
|         /* move aio_req from pending list to inflight one */ | ||||
|         QLIST_REMOVE(aio_req, aio_siblings); | ||||
|         QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings); | ||||
|         add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov, | ||||
|                         acb->aiocb_type); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static coroutine_fn void reconnect_to_sdog(void *opaque) | ||||
| { | ||||
|     BDRVSheepdogState *s = opaque; | ||||
|     AIOReq *aio_req, *next; | ||||
|  | ||||
|     aio_set_fd_handler(s->aio_context, s->fd, false, NULL, | ||||
|                        NULL, NULL); | ||||
|     aio_set_fd_handler(s->aio_context, s->fd, NULL, NULL, NULL); | ||||
|     close(s->fd); | ||||
|     s->fd = -1; | ||||
|  | ||||
| @@ -837,9 +837,15 @@ static void coroutine_fn aio_read_response(void *opaque) | ||||
|              */ | ||||
|             if (rsp.result == SD_RES_SUCCESS) { | ||||
|                 s->inode.data_vdi_id[idx] = s->inode.vdi_id; | ||||
|                 acb->max_dirty_data_idx = MAX(idx, acb->max_dirty_data_idx); | ||||
|                 acb->min_dirty_data_idx = MIN(idx, acb->min_dirty_data_idx); | ||||
|                 s->max_dirty_data_idx = MAX(idx, s->max_dirty_data_idx); | ||||
|                 s->min_dirty_data_idx = MIN(idx, s->min_dirty_data_idx); | ||||
|             } | ||||
|             /* | ||||
|              * Some requests may be blocked because simultaneous | ||||
|              * create requests are not allowed, so we search the | ||||
|              * pending requests here. | ||||
|              */ | ||||
|             send_pending_req(s, aio_req->oid); | ||||
|         } | ||||
|         break; | ||||
|     case AIOCB_READ_UDATA: | ||||
| @@ -865,6 +871,10 @@ static void coroutine_fn aio_read_response(void *opaque) | ||||
|             rsp.result = SD_RES_SUCCESS; | ||||
|             s->discard_supported = false; | ||||
|             break; | ||||
|         case SD_RES_SUCCESS: | ||||
|             idx = data_oid_to_idx(aio_req->oid); | ||||
|             s->inode.data_vdi_id[idx] = 0; | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
| @@ -943,8 +953,7 @@ static int get_sheep_fd(BDRVSheepdogState *s, Error **errp) | ||||
|         return fd; | ||||
|     } | ||||
|  | ||||
|     aio_set_fd_handler(s->aio_context, fd, false, | ||||
|                        co_read_response, NULL, s); | ||||
|     aio_set_fd_handler(s->aio_context, fd, co_read_response, NULL, s); | ||||
|     return fd; | ||||
| } | ||||
|  | ||||
| @@ -1180,13 +1189,7 @@ static void coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, | ||||
|         hdr.flags = SD_FLAG_CMD_WRITE | flags; | ||||
|         break; | ||||
|     case AIOCB_DISCARD_OBJ: | ||||
|         hdr.opcode = SD_OP_WRITE_OBJ; | ||||
|         hdr.flags = SD_FLAG_CMD_WRITE | flags; | ||||
|         s->inode.data_vdi_id[data_oid_to_idx(oid)] = 0; | ||||
|         offset = offsetof(SheepdogInode, | ||||
|                           data_vdi_id[data_oid_to_idx(oid)]); | ||||
|         oid = vid_to_vdi_oid(s->inode.vdi_id); | ||||
|         wlen = datalen = sizeof(uint32_t); | ||||
|         hdr.opcode = SD_OP_DISCARD_OBJ; | ||||
|         break; | ||||
|     } | ||||
|  | ||||
| @@ -1205,7 +1208,7 @@ static void coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, | ||||
|  | ||||
|     qemu_co_mutex_lock(&s->lock); | ||||
|     s->co_send = qemu_coroutine_self(); | ||||
|     aio_set_fd_handler(s->aio_context, s->fd, false, | ||||
|     aio_set_fd_handler(s->aio_context, s->fd, | ||||
|                        co_read_response, co_write_request, s); | ||||
|     socket_set_cork(s->fd, 1); | ||||
|  | ||||
| @@ -1224,8 +1227,7 @@ static void coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, | ||||
|     } | ||||
| out: | ||||
|     socket_set_cork(s->fd, 0); | ||||
|     aio_set_fd_handler(s->aio_context, s->fd, false, | ||||
|                        co_read_response, NULL, s); | ||||
|     aio_set_fd_handler(s->aio_context, s->fd, co_read_response, NULL, s); | ||||
|     s->co_send = NULL; | ||||
|     qemu_co_mutex_unlock(&s->lock); | ||||
| } | ||||
| @@ -1339,6 +1341,30 @@ out: | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /* Return true if the specified request is linked to the pending list. */ | ||||
| static bool check_simultaneous_create(BDRVSheepdogState *s, AIOReq *aio_req) | ||||
| { | ||||
|     AIOReq *areq; | ||||
|     QLIST_FOREACH(areq, &s->inflight_aio_head, aio_siblings) { | ||||
|         if (areq != aio_req && areq->oid == aio_req->oid) { | ||||
|             /* | ||||
|              * Sheepdog cannot handle simultaneous create requests to the same | ||||
|              * object, so we cannot send the request until the previous request | ||||
|              * finishes. | ||||
|              */ | ||||
|             DPRINTF("simultaneous create to %" PRIx64 "\n", aio_req->oid); | ||||
|             aio_req->flags = 0; | ||||
|             aio_req->base_oid = 0; | ||||
|             aio_req->create = false; | ||||
|             QLIST_REMOVE(aio_req, aio_siblings); | ||||
|             QLIST_INSERT_HEAD(&s->pending_aio_head, aio_req, aio_siblings); | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| static void coroutine_fn resend_aioreq(BDRVSheepdogState *s, AIOReq *aio_req) | ||||
| { | ||||
|     SheepdogAIOCB *acb = aio_req->aiocb; | ||||
| @@ -1353,6 +1379,10 @@ static void coroutine_fn resend_aioreq(BDRVSheepdogState *s, AIOReq *aio_req) | ||||
|             goto out; | ||||
|         } | ||||
|  | ||||
|         if (check_simultaneous_create(s, aio_req)) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (s->inode.data_vdi_id[idx]) { | ||||
|             aio_req->base_oid = vid_to_data_oid(s->inode.data_vdi_id[idx], idx); | ||||
|             aio_req->flags |= SD_FLAG_CMD_COW; | ||||
| @@ -1375,8 +1405,7 @@ static void sd_detach_aio_context(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVSheepdogState *s = bs->opaque; | ||||
|  | ||||
|     aio_set_fd_handler(s->aio_context, s->fd, false, NULL, | ||||
|                        NULL, NULL); | ||||
|     aio_set_fd_handler(s->aio_context, s->fd, NULL, NULL, NULL); | ||||
| } | ||||
|  | ||||
| static void sd_attach_aio_context(BlockDriverState *bs, | ||||
| @@ -1385,8 +1414,7 @@ static void sd_attach_aio_context(BlockDriverState *bs, | ||||
|     BDRVSheepdogState *s = bs->opaque; | ||||
|  | ||||
|     s->aio_context = new_context; | ||||
|     aio_set_fd_handler(new_context, s->fd, false, | ||||
|                        co_read_response, NULL, s); | ||||
|     aio_set_fd_handler(new_context, s->fd, co_read_response, NULL, s); | ||||
| } | ||||
|  | ||||
| /* TODO Convert to fine grained options */ | ||||
| @@ -1430,8 +1458,8 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     filename = qemu_opt_get(opts, "filename"); | ||||
|  | ||||
|     QLIST_INIT(&s->inflight_aio_head); | ||||
|     QLIST_INIT(&s->pending_aio_head); | ||||
|     QLIST_INIT(&s->failed_aio_head); | ||||
|     QLIST_INIT(&s->inflight_aiocb_head); | ||||
|     s->fd = -1; | ||||
|  | ||||
|     memset(vdi, 0, sizeof(vdi)); | ||||
| @@ -1490,17 +1518,17 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     } | ||||
|  | ||||
|     memcpy(&s->inode, buf, sizeof(s->inode)); | ||||
|     s->min_dirty_data_idx = UINT32_MAX; | ||||
|     s->max_dirty_data_idx = 0; | ||||
|  | ||||
|     bs->total_sectors = s->inode.vdi_size / BDRV_SECTOR_SIZE; | ||||
|     pstrcpy(s->name, sizeof(s->name), vdi); | ||||
|     qemu_co_mutex_init(&s->lock); | ||||
|     qemu_co_queue_init(&s->overlapping_queue); | ||||
|     qemu_opts_del(opts); | ||||
|     g_free(buf); | ||||
|     return 0; | ||||
| out: | ||||
|     aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd, | ||||
|                        false, NULL, NULL, NULL); | ||||
|     aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd, NULL, NULL, NULL); | ||||
|     if (s->fd >= 0) { | ||||
|         closesocket(s->fd); | ||||
|     } | ||||
| @@ -1509,70 +1537,6 @@ out: | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static int sd_reopen_prepare(BDRVReopenState *state, BlockReopenQueue *queue, | ||||
|                              Error **errp) | ||||
| { | ||||
|     BDRVSheepdogState *s = state->bs->opaque; | ||||
|     BDRVSheepdogReopenState *re_s; | ||||
|     int ret = 0; | ||||
|  | ||||
|     re_s = state->opaque = g_new0(BDRVSheepdogReopenState, 1); | ||||
|  | ||||
|     re_s->cache_flags = SD_FLAG_CMD_CACHE; | ||||
|     if (state->flags & BDRV_O_NOCACHE) { | ||||
|         re_s->cache_flags = SD_FLAG_CMD_DIRECT; | ||||
|     } | ||||
|  | ||||
|     re_s->fd = get_sheep_fd(s, errp); | ||||
|     if (re_s->fd < 0) { | ||||
|         ret = re_s->fd; | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static void sd_reopen_commit(BDRVReopenState *state) | ||||
| { | ||||
|     BDRVSheepdogReopenState *re_s = state->opaque; | ||||
|     BDRVSheepdogState *s = state->bs->opaque; | ||||
|  | ||||
|     if (s->fd) { | ||||
|         aio_set_fd_handler(s->aio_context, s->fd, false, | ||||
|                            NULL, NULL, NULL); | ||||
|         closesocket(s->fd); | ||||
|     } | ||||
|  | ||||
|     s->fd = re_s->fd; | ||||
|     s->cache_flags = re_s->cache_flags; | ||||
|  | ||||
|     g_free(state->opaque); | ||||
|     state->opaque = NULL; | ||||
|  | ||||
|     return; | ||||
| } | ||||
|  | ||||
| static void sd_reopen_abort(BDRVReopenState *state) | ||||
| { | ||||
|     BDRVSheepdogReopenState *re_s = state->opaque; | ||||
|     BDRVSheepdogState *s = state->bs->opaque; | ||||
|  | ||||
|     if (re_s == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (re_s->fd) { | ||||
|         aio_set_fd_handler(s->aio_context, re_s->fd, false, | ||||
|                            NULL, NULL, NULL); | ||||
|         closesocket(re_s->fd); | ||||
|     } | ||||
|  | ||||
|     g_free(state->opaque); | ||||
|     state->opaque = NULL; | ||||
|  | ||||
|     return; | ||||
| } | ||||
|  | ||||
| static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot, | ||||
|                         Error **errp) | ||||
| { | ||||
| @@ -1641,7 +1605,7 @@ static int sd_prealloc(const char *filename, Error **errp) | ||||
|     int ret; | ||||
|  | ||||
|     ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, | ||||
|                     errp); | ||||
|                     NULL, errp); | ||||
|     if (ret < 0) { | ||||
|         goto out_with_err_set; | ||||
|     } | ||||
| @@ -1752,7 +1716,7 @@ static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt) | ||||
|         if ((object_size - 1) & object_size) {    /* not a power of 2? */ | ||||
|             return -EINVAL; | ||||
|         } | ||||
|         obj_order = ctz32(object_size); | ||||
|         obj_order = ffs(object_size) - 1; | ||||
|         if (obj_order < 20 || obj_order > 31) { | ||||
|             return -EINVAL; | ||||
|         } | ||||
| @@ -1833,7 +1797,8 @@ static int sd_create(const char *filename, QemuOpts *opts, | ||||
|         } | ||||
|  | ||||
|         bs = NULL; | ||||
|         ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_PROTOCOL, errp); | ||||
|         ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_PROTOCOL, NULL, | ||||
|                         errp); | ||||
|         if (ret < 0) { | ||||
|             goto out; | ||||
|         } | ||||
| @@ -1862,7 +1827,8 @@ static int sd_create(const char *filename, QemuOpts *opts, | ||||
|  | ||||
|         fd = connect_to_sdog(s, &local_err); | ||||
|         if (fd < 0) { | ||||
|             error_report_err(local_err); | ||||
|             error_report("%s", error_get_pretty(local_err)); | ||||
|             error_free(local_err); | ||||
|             ret = -EIO; | ||||
|             goto out; | ||||
|         } | ||||
| @@ -1946,8 +1912,7 @@ static void sd_close(BlockDriverState *bs) | ||||
|         error_report("%s, %s", sd_strerror(rsp->result), s->name); | ||||
|     } | ||||
|  | ||||
|     aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd, | ||||
|                        false, NULL, NULL, NULL); | ||||
|     aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd, NULL, NULL, NULL); | ||||
|     closesocket(s->fd); | ||||
|     g_free(s->host_spec); | ||||
| } | ||||
| @@ -2009,16 +1974,16 @@ static void coroutine_fn sd_write_done(SheepdogAIOCB *acb) | ||||
|     AIOReq *aio_req; | ||||
|     uint32_t offset, data_len, mn, mx; | ||||
|  | ||||
|     mn = acb->min_dirty_data_idx; | ||||
|     mx = acb->max_dirty_data_idx; | ||||
|     mn = s->min_dirty_data_idx; | ||||
|     mx = s->max_dirty_data_idx; | ||||
|     if (mn <= mx) { | ||||
|         /* we need to update the vdi object. */ | ||||
|         offset = sizeof(s->inode) - sizeof(s->inode.data_vdi_id) + | ||||
|             mn * sizeof(s->inode.data_vdi_id[0]); | ||||
|         data_len = (mx - mn + 1) * sizeof(s->inode.data_vdi_id[0]); | ||||
|  | ||||
|         acb->min_dirty_data_idx = UINT32_MAX; | ||||
|         acb->max_dirty_data_idx = 0; | ||||
|         s->min_dirty_data_idx = UINT32_MAX; | ||||
|         s->max_dirty_data_idx = 0; | ||||
|  | ||||
|         iov.iov_base = &s->inode; | ||||
|         iov.iov_len = sizeof(s->inode); | ||||
| @@ -2227,11 +2192,15 @@ static int coroutine_fn sd_co_rw_vector(void *p) | ||||
|         } | ||||
|  | ||||
|         aio_req = alloc_aio_req(s, acb, oid, len, offset, flags, create, | ||||
|                                 old_oid, | ||||
|                                 acb->aiocb_type == AIOCB_DISCARD_OBJ ? | ||||
|                                 0 : done); | ||||
|                                 old_oid, done); | ||||
|         QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings); | ||||
|  | ||||
|         if (create) { | ||||
|             if (check_simultaneous_create(s, aio_req)) { | ||||
|                 goto done; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov, | ||||
|                         acb->aiocb_type); | ||||
|     done: | ||||
| @@ -2246,20 +2215,6 @@ out: | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| static bool check_overlapping_aiocb(BDRVSheepdogState *s, SheepdogAIOCB *aiocb) | ||||
| { | ||||
|     SheepdogAIOCB *cb; | ||||
|  | ||||
|     QLIST_FOREACH(cb, &s->inflight_aiocb_head, aiocb_siblings) { | ||||
|         if (AIOCBOverlapping(aiocb, cb)) { | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     QLIST_INSERT_HEAD(&s->inflight_aiocb_head, aiocb, aiocb_siblings); | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num, | ||||
|                         int nb_sectors, QEMUIOVector *qiov) | ||||
| { | ||||
| @@ -2279,25 +2234,14 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num, | ||||
|     acb->aio_done_func = sd_write_done; | ||||
|     acb->aiocb_type = AIOCB_WRITE_UDATA; | ||||
|  | ||||
| retry: | ||||
|     if (check_overlapping_aiocb(s, acb)) { | ||||
|         qemu_co_queue_wait(&s->overlapping_queue); | ||||
|         goto retry; | ||||
|     } | ||||
|  | ||||
|     ret = sd_co_rw_vector(acb); | ||||
|     if (ret <= 0) { | ||||
|         QLIST_REMOVE(acb, aiocb_siblings); | ||||
|         qemu_co_queue_restart_all(&s->overlapping_queue); | ||||
|         qemu_aio_unref(acb); | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     qemu_coroutine_yield(); | ||||
|  | ||||
|     QLIST_REMOVE(acb, aiocb_siblings); | ||||
|     qemu_co_queue_restart_all(&s->overlapping_queue); | ||||
|  | ||||
|     return acb->ret; | ||||
| } | ||||
|  | ||||
| @@ -2306,30 +2250,19 @@ static coroutine_fn int sd_co_readv(BlockDriverState *bs, int64_t sector_num, | ||||
| { | ||||
|     SheepdogAIOCB *acb; | ||||
|     int ret; | ||||
|     BDRVSheepdogState *s = bs->opaque; | ||||
|  | ||||
|     acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors); | ||||
|     acb->aiocb_type = AIOCB_READ_UDATA; | ||||
|     acb->aio_done_func = sd_finish_aiocb; | ||||
|  | ||||
| retry: | ||||
|     if (check_overlapping_aiocb(s, acb)) { | ||||
|         qemu_co_queue_wait(&s->overlapping_queue); | ||||
|         goto retry; | ||||
|     } | ||||
|  | ||||
|     ret = sd_co_rw_vector(acb); | ||||
|     if (ret <= 0) { | ||||
|         QLIST_REMOVE(acb, aiocb_siblings); | ||||
|         qemu_co_queue_restart_all(&s->overlapping_queue); | ||||
|         qemu_aio_unref(acb); | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     qemu_coroutine_yield(); | ||||
|  | ||||
|     QLIST_REMOVE(acb, aiocb_siblings); | ||||
|     qemu_co_queue_restart_all(&s->overlapping_queue); | ||||
|     return acb->ret; | ||||
| } | ||||
|  | ||||
| @@ -2406,8 +2339,8 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) | ||||
|  | ||||
|     ret = do_sd_create(s, &new_vid, 1, &local_err); | ||||
|     if (ret < 0) { | ||||
|         error_reportf_err(local_err, | ||||
|                           "failed to create inode for snapshot: "); | ||||
|         error_report("failed to create inode for snapshot: %s", | ||||
|                      error_get_pretty(local_err)); | ||||
|         goto cleanup; | ||||
|     } | ||||
|  | ||||
| @@ -2664,51 +2597,32 @@ static coroutine_fn int sd_co_discard(BlockDriverState *bs, int64_t sector_num, | ||||
|                                       int nb_sectors) | ||||
| { | ||||
|     SheepdogAIOCB *acb; | ||||
|     QEMUIOVector dummy; | ||||
|     BDRVSheepdogState *s = bs->opaque; | ||||
|     int ret; | ||||
|     QEMUIOVector discard_iov; | ||||
|     struct iovec iov; | ||||
|     uint32_t zero = 0; | ||||
|  | ||||
|     if (!s->discard_supported) { | ||||
|             return 0; | ||||
|     } | ||||
|  | ||||
|     memset(&discard_iov, 0, sizeof(discard_iov)); | ||||
|     memset(&iov, 0, sizeof(iov)); | ||||
|     iov.iov_base = &zero; | ||||
|     iov.iov_len = sizeof(zero); | ||||
|     discard_iov.iov = &iov; | ||||
|     discard_iov.niov = 1; | ||||
|     acb = sd_aio_setup(bs, &discard_iov, sector_num, nb_sectors); | ||||
|     acb = sd_aio_setup(bs, &dummy, sector_num, nb_sectors); | ||||
|     acb->aiocb_type = AIOCB_DISCARD_OBJ; | ||||
|     acb->aio_done_func = sd_finish_aiocb; | ||||
|  | ||||
| retry: | ||||
|     if (check_overlapping_aiocb(s, acb)) { | ||||
|         qemu_co_queue_wait(&s->overlapping_queue); | ||||
|         goto retry; | ||||
|     } | ||||
|  | ||||
|     ret = sd_co_rw_vector(acb); | ||||
|     if (ret <= 0) { | ||||
|         QLIST_REMOVE(acb, aiocb_siblings); | ||||
|         qemu_co_queue_restart_all(&s->overlapping_queue); | ||||
|         qemu_aio_unref(acb); | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     qemu_coroutine_yield(); | ||||
|  | ||||
|     QLIST_REMOVE(acb, aiocb_siblings); | ||||
|     qemu_co_queue_restart_all(&s->overlapping_queue); | ||||
|  | ||||
|     return acb->ret; | ||||
| } | ||||
|  | ||||
| static coroutine_fn int64_t | ||||
| sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, | ||||
|                        int *pnum, BlockDriverState **file) | ||||
|                        int *pnum) | ||||
| { | ||||
|     BDRVSheepdogState *s = bs->opaque; | ||||
|     SheepdogInode *inode = &s->inode; | ||||
| @@ -2739,9 +2653,6 @@ sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, | ||||
|     if (*pnum > nb_sectors) { | ||||
|         *pnum = nb_sectors; | ||||
|     } | ||||
|     if (ret > 0 && ret & BDRV_BLOCK_OFFSET_VALID) { | ||||
|         *file = bs; | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| @@ -2801,9 +2712,6 @@ static BlockDriver bdrv_sheepdog = { | ||||
|     .instance_size  = sizeof(BDRVSheepdogState), | ||||
|     .bdrv_needs_filename = true, | ||||
|     .bdrv_file_open = sd_open, | ||||
|     .bdrv_reopen_prepare    = sd_reopen_prepare, | ||||
|     .bdrv_reopen_commit     = sd_reopen_commit, | ||||
|     .bdrv_reopen_abort      = sd_reopen_abort, | ||||
|     .bdrv_close     = sd_close, | ||||
|     .bdrv_create    = sd_create, | ||||
|     .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||||
| @@ -2837,9 +2745,6 @@ static BlockDriver bdrv_sheepdog_tcp = { | ||||
|     .instance_size  = sizeof(BDRVSheepdogState), | ||||
|     .bdrv_needs_filename = true, | ||||
|     .bdrv_file_open = sd_open, | ||||
|     .bdrv_reopen_prepare    = sd_reopen_prepare, | ||||
|     .bdrv_reopen_commit     = sd_reopen_commit, | ||||
|     .bdrv_reopen_abort      = sd_reopen_abort, | ||||
|     .bdrv_close     = sd_close, | ||||
|     .bdrv_create    = sd_create, | ||||
|     .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||||
| @@ -2873,9 +2778,6 @@ static BlockDriver bdrv_sheepdog_unix = { | ||||
|     .instance_size  = sizeof(BDRVSheepdogState), | ||||
|     .bdrv_needs_filename = true, | ||||
|     .bdrv_file_open = sd_open, | ||||
|     .bdrv_reopen_prepare    = sd_reopen_prepare, | ||||
|     .bdrv_reopen_commit     = sd_reopen_commit, | ||||
|     .bdrv_reopen_abort      = sd_reopen_abort, | ||||
|     .bdrv_close     = sd_close, | ||||
|     .bdrv_create    = sd_create, | ||||
|     .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||||
|   | ||||
							
								
								
									
										177
									
								
								block/snapshot.c
									
									
									
									
									
								
							
							
						
						
									
										177
									
								
								block/snapshot.c
									
									
									
									
									
								
							| @@ -22,10 +22,8 @@ | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "block/snapshot.h" | ||||
| #include "block/block_int.h" | ||||
| #include "qapi/qmp/qerror.h" | ||||
|  | ||||
| QemuOptsList internal_snapshot_opts = { | ||||
|     .name = "snapshot", | ||||
| @@ -150,7 +148,7 @@ int bdrv_can_snapshot(BlockDriverState *bs) | ||||
|  | ||||
|     if (!drv->bdrv_snapshot_create) { | ||||
|         if (bs->file != NULL) { | ||||
|             return bdrv_can_snapshot(bs->file->bs); | ||||
|             return bdrv_can_snapshot(bs->file); | ||||
|         } | ||||
|         return 0; | ||||
|     } | ||||
| @@ -169,7 +167,7 @@ int bdrv_snapshot_create(BlockDriverState *bs, | ||||
|         return drv->bdrv_snapshot_create(bs, sn_info); | ||||
|     } | ||||
|     if (bs->file) { | ||||
|         return bdrv_snapshot_create(bs->file->bs, sn_info); | ||||
|         return bdrv_snapshot_create(bs->file, sn_info); | ||||
|     } | ||||
|     return -ENOTSUP; | ||||
| } | ||||
| @@ -189,10 +187,10 @@ int bdrv_snapshot_goto(BlockDriverState *bs, | ||||
|  | ||||
|     if (bs->file) { | ||||
|         drv->bdrv_close(bs); | ||||
|         ret = bdrv_snapshot_goto(bs->file->bs, snapshot_id); | ||||
|         ret = bdrv_snapshot_goto(bs->file, snapshot_id); | ||||
|         open_ret = drv->bdrv_open(bs, NULL, bs->open_flags, NULL); | ||||
|         if (open_ret < 0) { | ||||
|             bdrv_unref(bs->file->bs); | ||||
|             bdrv_unref(bs->file); | ||||
|             bs->drv = NULL; | ||||
|             return open_ret; | ||||
|         } | ||||
| @@ -230,10 +228,8 @@ int bdrv_snapshot_delete(BlockDriverState *bs, | ||||
|                          Error **errp) | ||||
| { | ||||
|     BlockDriver *drv = bs->drv; | ||||
|     int ret; | ||||
|  | ||||
|     if (!drv) { | ||||
|         error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); | ||||
|         error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); | ||||
|         return -ENOMEDIUM; | ||||
|     } | ||||
|     if (!snapshot_id && !name) { | ||||
| @@ -242,24 +238,21 @@ int bdrv_snapshot_delete(BlockDriverState *bs, | ||||
|     } | ||||
|  | ||||
|     /* drain all pending i/o before deleting snapshot */ | ||||
|     bdrv_drained_begin(bs); | ||||
|     bdrv_drain_all(); | ||||
|  | ||||
|     if (drv->bdrv_snapshot_delete) { | ||||
|         ret = drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp); | ||||
|     } else if (bs->file) { | ||||
|         ret = bdrv_snapshot_delete(bs->file->bs, snapshot_id, name, errp); | ||||
|     } else { | ||||
|         error_setg(errp, "Block format '%s' used by device '%s' " | ||||
|                    "does not support internal snapshot deletion", | ||||
|                    drv->format_name, bdrv_get_device_name(bs)); | ||||
|         ret = -ENOTSUP; | ||||
|         return drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp); | ||||
|     } | ||||
|     if (bs->file) { | ||||
|         return bdrv_snapshot_delete(bs->file, snapshot_id, name, errp); | ||||
|     } | ||||
|     error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, | ||||
|               drv->format_name, bdrv_get_device_name(bs), | ||||
|               "internal snapshot deletion"); | ||||
|     return -ENOTSUP; | ||||
| } | ||||
|  | ||||
|     bdrv_drained_end(bs); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| int bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs, | ||||
| void bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs, | ||||
|                                         const char *id_or_name, | ||||
|                                         Error **errp) | ||||
| { | ||||
| @@ -276,7 +269,6 @@ int bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs, | ||||
|     if (ret < 0) { | ||||
|         error_propagate(errp, local_err); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| int bdrv_snapshot_list(BlockDriverState *bs, | ||||
| @@ -290,7 +282,7 @@ int bdrv_snapshot_list(BlockDriverState *bs, | ||||
|         return drv->bdrv_snapshot_list(bs, psn_info); | ||||
|     } | ||||
|     if (bs->file) { | ||||
|         return bdrv_snapshot_list(bs->file->bs, psn_info); | ||||
|         return bdrv_snapshot_list(bs->file, psn_info); | ||||
|     } | ||||
|     return -ENOTSUP; | ||||
| } | ||||
| @@ -323,7 +315,7 @@ int bdrv_snapshot_load_tmp(BlockDriverState *bs, | ||||
|     BlockDriver *drv = bs->drv; | ||||
|  | ||||
|     if (!drv) { | ||||
|         error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); | ||||
|         error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); | ||||
|         return -ENOMEDIUM; | ||||
|     } | ||||
|     if (!snapshot_id && !name) { | ||||
| @@ -337,9 +329,9 @@ int bdrv_snapshot_load_tmp(BlockDriverState *bs, | ||||
|     if (drv->bdrv_snapshot_load_tmp) { | ||||
|         return drv->bdrv_snapshot_load_tmp(bs, snapshot_id, name, errp); | ||||
|     } | ||||
|     error_setg(errp, "Block format '%s' used by device '%s' " | ||||
|                "does not support temporarily loading internal snapshots", | ||||
|                drv->format_name, bdrv_get_device_name(bs)); | ||||
|     error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, | ||||
|               drv->format_name, bdrv_get_device_name(bs), | ||||
|               "temporarily load internal snapshot"); | ||||
|     return -ENOTSUP; | ||||
| } | ||||
|  | ||||
| @@ -363,130 +355,3 @@ int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs, | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Group operations. All block drivers are involved. | ||||
|  * These functions will properly handle dataplane (take aio_context_acquire | ||||
|  * when appropriate for appropriate block drivers) */ | ||||
|  | ||||
| bool bdrv_all_can_snapshot(BlockDriverState **first_bad_bs) | ||||
| { | ||||
|     bool ok = true; | ||||
|     BlockDriverState *bs = NULL; | ||||
|  | ||||
|     while (ok && (bs = bdrv_next(bs))) { | ||||
|         AioContext *ctx = bdrv_get_aio_context(bs); | ||||
|  | ||||
|         aio_context_acquire(ctx); | ||||
|         if (bdrv_is_inserted(bs) && !bdrv_is_read_only(bs)) { | ||||
|             ok = bdrv_can_snapshot(bs); | ||||
|         } | ||||
|         aio_context_release(ctx); | ||||
|     } | ||||
|  | ||||
|     *first_bad_bs = bs; | ||||
|     return ok; | ||||
| } | ||||
|  | ||||
| int bdrv_all_delete_snapshot(const char *name, BlockDriverState **first_bad_bs, | ||||
|                              Error **err) | ||||
| { | ||||
|     int ret = 0; | ||||
|     BlockDriverState *bs = NULL; | ||||
|     QEMUSnapshotInfo sn1, *snapshot = &sn1; | ||||
|  | ||||
|     while (ret == 0 && (bs = bdrv_next(bs))) { | ||||
|         AioContext *ctx = bdrv_get_aio_context(bs); | ||||
|  | ||||
|         aio_context_acquire(ctx); | ||||
|         if (bdrv_can_snapshot(bs) && | ||||
|                 bdrv_snapshot_find(bs, snapshot, name) >= 0) { | ||||
|             ret = bdrv_snapshot_delete_by_id_or_name(bs, name, err); | ||||
|         } | ||||
|         aio_context_release(ctx); | ||||
|     } | ||||
|  | ||||
|     *first_bad_bs = bs; | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
|  | ||||
| int bdrv_all_goto_snapshot(const char *name, BlockDriverState **first_bad_bs) | ||||
| { | ||||
|     int err = 0; | ||||
|     BlockDriverState *bs = NULL; | ||||
|  | ||||
|     while (err == 0 && (bs = bdrv_next(bs))) { | ||||
|         AioContext *ctx = bdrv_get_aio_context(bs); | ||||
|  | ||||
|         aio_context_acquire(ctx); | ||||
|         if (bdrv_can_snapshot(bs)) { | ||||
|             err = bdrv_snapshot_goto(bs, name); | ||||
|         } | ||||
|         aio_context_release(ctx); | ||||
|     } | ||||
|  | ||||
|     *first_bad_bs = bs; | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| int bdrv_all_find_snapshot(const char *name, BlockDriverState **first_bad_bs) | ||||
| { | ||||
|     QEMUSnapshotInfo sn; | ||||
|     int err = 0; | ||||
|     BlockDriverState *bs = NULL; | ||||
|  | ||||
|     while (err == 0 && (bs = bdrv_next(bs))) { | ||||
|         AioContext *ctx = bdrv_get_aio_context(bs); | ||||
|  | ||||
|         aio_context_acquire(ctx); | ||||
|         if (bdrv_can_snapshot(bs)) { | ||||
|             err = bdrv_snapshot_find(bs, &sn, name); | ||||
|         } | ||||
|         aio_context_release(ctx); | ||||
|     } | ||||
|  | ||||
|     *first_bad_bs = bs; | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn, | ||||
|                              BlockDriverState *vm_state_bs, | ||||
|                              uint64_t vm_state_size, | ||||
|                              BlockDriverState **first_bad_bs) | ||||
| { | ||||
|     int err = 0; | ||||
|     BlockDriverState *bs = NULL; | ||||
|  | ||||
|     while (err == 0 && (bs = bdrv_next(bs))) { | ||||
|         AioContext *ctx = bdrv_get_aio_context(bs); | ||||
|  | ||||
|         aio_context_acquire(ctx); | ||||
|         if (bs == vm_state_bs) { | ||||
|             sn->vm_state_size = vm_state_size; | ||||
|             err = bdrv_snapshot_create(bs, sn); | ||||
|         } else if (bdrv_can_snapshot(bs)) { | ||||
|             sn->vm_state_size = 0; | ||||
|             err = bdrv_snapshot_create(bs, sn); | ||||
|         } | ||||
|         aio_context_release(ctx); | ||||
|     } | ||||
|  | ||||
|     *first_bad_bs = bs; | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| BlockDriverState *bdrv_all_find_vmstate_bs(void) | ||||
| { | ||||
|     bool not_found = true; | ||||
|     BlockDriverState *bs = NULL; | ||||
|  | ||||
|     while (not_found && (bs = bdrv_next(bs))) { | ||||
|         AioContext *ctx = bdrv_get_aio_context(bs); | ||||
|  | ||||
|         aio_context_acquire(ctx); | ||||
|         not_found = !bdrv_can_snapshot(bs); | ||||
|         aio_context_release(ctx); | ||||
|     } | ||||
|     return bs; | ||||
| } | ||||
|   | ||||
							
								
								
									
										18
									
								
								block/ssh.c
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								block/ssh.c
									
									
									
									
									
								
							| @@ -22,17 +22,17 @@ | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdarg.h> | ||||
|  | ||||
| #include <libssh2.h> | ||||
| #include <libssh2_sftp.h> | ||||
|  | ||||
| #include "block/block_int.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "qemu/sockets.h" | ||||
| #include "qemu/uri.h" | ||||
| #include "qapi/qmp/qint.h" | ||||
| #include "qapi/qmp/qstring.h" | ||||
|  | ||||
| /* DEBUG_SSH=1 enables the DPRINTF (debugging printf) statements in | ||||
|  * this block driver code. | ||||
| @@ -191,7 +191,7 @@ sftp_error_report(BDRVSSHState *s, const char *fs, ...) | ||||
| static int parse_uri(const char *filename, QDict *options, Error **errp) | ||||
| { | ||||
|     URI *uri = NULL; | ||||
|     QueryParams *qp; | ||||
|     QueryParams *qp = NULL; | ||||
|     int i; | ||||
|  | ||||
|     uri = uri_parse(filename); | ||||
| @@ -247,6 +247,9 @@ static int parse_uri(const char *filename, QDict *options, Error **errp) | ||||
|     return 0; | ||||
|  | ||||
|  err: | ||||
|     if (qp) { | ||||
|       query_params_free(qp); | ||||
|     } | ||||
|     if (uri) { | ||||
|       uri_free(uri); | ||||
|     } | ||||
| @@ -558,7 +561,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, | ||||
|     /* Open the socket and connect. */ | ||||
|     s->sock = inet_connect(s->hostport, errp); | ||||
|     if (s->sock < 0) { | ||||
|         ret = -EIO; | ||||
|         ret = -errno; | ||||
|         goto err; | ||||
|     } | ||||
|  | ||||
| @@ -798,15 +801,14 @@ static coroutine_fn void set_fd_handler(BDRVSSHState *s, BlockDriverState *bs) | ||||
|             rd_handler, wr_handler); | ||||
|  | ||||
|     aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock, | ||||
|                        false, rd_handler, wr_handler, co); | ||||
|                        rd_handler, wr_handler, 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); | ||||
|     aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock, NULL, NULL, NULL); | ||||
| } | ||||
|  | ||||
| /* A non-blocking call returned EAGAIN, so yield, ensuring the | ||||
|   | ||||
| @@ -11,13 +11,10 @@ | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "trace.h" | ||||
| #include "block/block_int.h" | ||||
| #include "block/blockjob.h" | ||||
| #include "qapi/qmp/qerror.h" | ||||
| #include "qemu/ratelimit.h" | ||||
| #include "sysemu/block-backend.h" | ||||
|  | ||||
| enum { | ||||
|     /* | ||||
| @@ -54,6 +51,34 @@ static int coroutine_fn stream_populate(BlockDriverState *bs, | ||||
|     return bdrv_co_copy_on_readv(bs, sector_num, nb_sectors, &qiov); | ||||
| } | ||||
|  | ||||
| static void close_unused_images(BlockDriverState *top, BlockDriverState *base, | ||||
|                                 const char *base_id) | ||||
| { | ||||
|     BlockDriverState *intermediate; | ||||
|     intermediate = top->backing_hd; | ||||
|  | ||||
|     /* Must assign before bdrv_delete() to prevent traversing dangling pointer | ||||
|      * while we delete backing image instances. | ||||
|      */ | ||||
|     bdrv_set_backing_hd(top, base); | ||||
|  | ||||
|     while (intermediate) { | ||||
|         BlockDriverState *unused; | ||||
|  | ||||
|         /* reached base */ | ||||
|         if (intermediate == base) { | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         unused = intermediate; | ||||
|         intermediate = intermediate->backing_hd; | ||||
|         bdrv_set_backing_hd(unused, NULL); | ||||
|         bdrv_unref(unused); | ||||
|     } | ||||
|  | ||||
|     bdrv_refresh_limits(top, NULL); | ||||
| } | ||||
|  | ||||
| typedef struct { | ||||
|     int ret; | ||||
|     bool reached_end; | ||||
| @@ -75,7 +100,7 @@ static void stream_complete(BlockJob *job, void *opaque) | ||||
|             } | ||||
|         } | ||||
|         data->ret = bdrv_change_backing_file(job->bs, base_id, base_fmt); | ||||
|         bdrv_set_backing_hd(job->bs, base); | ||||
|         close_unused_images(job->bs, base, base_id); | ||||
|     } | ||||
|  | ||||
|     g_free(s->backing_file_str); | ||||
| @@ -95,7 +120,7 @@ static void coroutine_fn stream_run(void *opaque) | ||||
|     int n = 0; | ||||
|     void *buf; | ||||
|  | ||||
|     if (!bs->backing) { | ||||
|     if (!bs->backing_hd) { | ||||
|         block_job_completed(&s->common, 0); | ||||
|         return; | ||||
|     } | ||||
| @@ -140,7 +165,7 @@ wait: | ||||
|         } else if (ret >= 0) { | ||||
|             /* Copy if allocated in the intermediate images.  Limit to the | ||||
|              * known-unallocated area [sector_num, sector_num+n).  */ | ||||
|             ret = bdrv_is_allocated_above(backing_bs(bs), base, | ||||
|             ret = bdrv_is_allocated_above(bs->backing_hd, base, | ||||
|                                           sector_num, n, &n); | ||||
|  | ||||
|             /* Finish early if end of backing file has been reached */ | ||||
| @@ -202,7 +227,7 @@ static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp) | ||||
|     StreamBlockJob *s = container_of(job, StreamBlockJob, common); | ||||
|  | ||||
|     if (speed < 0) { | ||||
|         error_setg(errp, QERR_INVALID_PARAMETER, "speed"); | ||||
|         error_set(errp, QERR_INVALID_PARAMETER, "speed"); | ||||
|         return; | ||||
|     } | ||||
|     ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME); | ||||
| @@ -224,8 +249,8 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base, | ||||
|  | ||||
|     if ((on_error == BLOCKDEV_ON_ERROR_STOP || | ||||
|          on_error == BLOCKDEV_ON_ERROR_ENOSPC) && | ||||
|         (!bs->blk || !blk_iostatus_is_enabled(bs->blk))) { | ||||
|         error_setg(errp, QERR_INVALID_PARAMETER, "on-error"); | ||||
|         !bdrv_iostatus_is_enabled(bs)) { | ||||
|         error_set(errp, QERR_INVALID_PARAMETER, "on-error"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|   | ||||
							
								
								
									
										377
									
								
								block/tar.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										377
									
								
								block/tar.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,377 @@ | ||||
| /* | ||||
|  * Tar block driver | ||||
|  * | ||||
|  * Copyright (c) 2009 Alexander Graf <agraf@suse.de> | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "qemu-common.h" | ||||
| #include "block/block_int.h" | ||||
|  | ||||
| // #define DEBUG | ||||
|  | ||||
| #ifdef DEBUG | ||||
| #define dprintf(fmt, ...) do { printf("tar: " fmt, ## __VA_ARGS__); } while (0) | ||||
| #else | ||||
| #define dprintf(fmt, ...) do { } while (0) | ||||
| #endif | ||||
|  | ||||
| #define SECTOR_SIZE      512 | ||||
|  | ||||
| #define POSIX_TAR_MAGIC  "ustar" | ||||
| #define OFFS_LENGTH      0x7c | ||||
| #define OFFS_TYPE        0x9c | ||||
| #define OFFS_MAGIC       0x101 | ||||
|  | ||||
| #define OFFS_S_SP        0x182 | ||||
| #define OFFS_S_EXT       0x1e2 | ||||
| #define OFFS_S_LENGTH    0x1e3 | ||||
| #define OFFS_SX_EXT      0x1f8 | ||||
|  | ||||
| typedef struct SparseCache { | ||||
|     uint64_t start; | ||||
|     uint64_t end; | ||||
| } SparseCache; | ||||
|  | ||||
| typedef struct BDRVTarState { | ||||
|     BlockDriverState *hd; | ||||
|     size_t file_sec; | ||||
|     uint64_t file_len; | ||||
|     SparseCache *sparse; | ||||
|     int sparse_num; | ||||
|     uint64_t last_end; | ||||
|     char longfile[2048]; | ||||
| } BDRVTarState; | ||||
|  | ||||
| static int str_ends(char *str, const char *end) | ||||
| { | ||||
|     int end_len = strlen(end); | ||||
|     int str_len = strlen(str); | ||||
|  | ||||
|     if (str_len < end_len) | ||||
|         return 0; | ||||
|  | ||||
|     return !strncmp(str + str_len - end_len, end, end_len); | ||||
| } | ||||
|  | ||||
| static int is_target_file(BlockDriverState *bs, char *filename, | ||||
|                           char *header) | ||||
| { | ||||
|     int retval = 0; | ||||
|  | ||||
|     if (str_ends(filename, ".raw")) | ||||
|         retval = 1; | ||||
|  | ||||
|     if (str_ends(filename, ".qcow")) | ||||
|         retval = 1; | ||||
|  | ||||
|     if (str_ends(filename, ".qcow2")) | ||||
|         retval = 1; | ||||
|  | ||||
|     if (str_ends(filename, ".vmdk")) | ||||
|         retval = 1; | ||||
|  | ||||
|     if (retval && | ||||
|         (header[OFFS_TYPE] != '0') && | ||||
|         (header[OFFS_TYPE] != 'S')) { | ||||
|         retval = 0; | ||||
|     } | ||||
|  | ||||
|     dprintf("does filename %s match? %s\n", filename, retval ? "yes" : "no"); | ||||
|  | ||||
|     /* make sure we're not using this name again */ | ||||
|     filename[0] = '\0'; | ||||
|  | ||||
|     return retval; | ||||
| } | ||||
|  | ||||
| static uint64_t tar2u64(char *ptr) | ||||
| { | ||||
|     uint64_t retval; | ||||
|     char oldend = ptr[12]; | ||||
|  | ||||
|     ptr[12] = '\0'; | ||||
|     if (*ptr & 0x80) { | ||||
|         /* XXX we only support files up to 64 bit length */ | ||||
|         retval = be64_to_cpu(*(uint64_t *)(ptr+4)); | ||||
|         dprintf("Convert %lx -> %#lx\n", *(uint64_t*)(ptr+4), retval); | ||||
|     } else { | ||||
|         retval = strtol(ptr, NULL, 8); | ||||
|         dprintf("Convert %s -> %#lx\n", ptr, retval); | ||||
|     } | ||||
|  | ||||
|     ptr[12] = oldend; | ||||
|  | ||||
|     return retval; | ||||
| } | ||||
|  | ||||
| static void tar_sparse(BDRVTarState *s, uint64_t offs, uint64_t len) | ||||
| { | ||||
|     SparseCache *sparse; | ||||
|  | ||||
|     if (!len) | ||||
|         return; | ||||
|     if (!(offs - s->last_end)) { | ||||
|         s->last_end += len; | ||||
|         return; | ||||
|     } | ||||
|     if (s->last_end > offs) | ||||
|         return; | ||||
|  | ||||
|     dprintf("Last chunk until %lx new chunk at %lx\n", s->last_end, offs); | ||||
|  | ||||
|     s->sparse = g_realloc(s->sparse, (s->sparse_num + 1) * sizeof(SparseCache)); | ||||
|     sparse = &s->sparse[s->sparse_num]; | ||||
|     sparse->start = s->last_end; | ||||
|     sparse->end = offs; | ||||
|     s->last_end = offs + len; | ||||
|     s->sparse_num++; | ||||
|     dprintf("Sparse at %lx end=%lx\n", sparse->start, | ||||
|                                        sparse->end); | ||||
| } | ||||
|  | ||||
| static QemuOptsList runtime_opts = { | ||||
|     .name = "tar", | ||||
|     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), | ||||
|     .desc = { | ||||
|         { | ||||
|             .name = "filename", | ||||
|             .type = QEMU_OPT_STRING, | ||||
|             .help = "URL to the tar file", | ||||
|         }, | ||||
|         { /* end of list */ } | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| static int tar_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) | ||||
| { | ||||
|     BDRVTarState *s = bs->opaque; | ||||
|     char header[SECTOR_SIZE]; | ||||
|     char *real_file = header; | ||||
|     char *magic; | ||||
|     size_t header_offs = 0; | ||||
|     int ret; | ||||
|     QemuOpts *opts; | ||||
|     Error *local_err = NULL; | ||||
|     const char *filename; | ||||
|  | ||||
|     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); | ||||
|     qemu_opts_absorb_qdict(opts, options, &local_err); | ||||
|     if (local_err != NULL) { | ||||
|         error_propagate(errp, local_err); | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     filename = qemu_opt_get(opts, "filename"); | ||||
|  | ||||
|     if (!strncmp(filename, "tar://", 6)) | ||||
|         filename += 6; | ||||
|     else if (!strncmp(filename, "tar:", 4)) | ||||
|         filename += 4; | ||||
|  | ||||
|     ret = bdrv_open(&s->hd, filename, NULL, NULL, flags | BDRV_O_PROTOCOL, NULL, &local_err); | ||||
|     if (ret < 0) { | ||||
|         error_propagate(errp, local_err); | ||||
|         qemu_opts_del(opts); | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     /* Search the file for an image */ | ||||
|  | ||||
|     do { | ||||
|         /* tar header */ | ||||
|         if (bdrv_pread(s->hd, header_offs, header, SECTOR_SIZE) != SECTOR_SIZE) | ||||
|             goto fail; | ||||
|  | ||||
|         if ((header_offs > 1) && !header[0]) { | ||||
|             fprintf(stderr, "Tar: No image file found in archive\n"); | ||||
|             goto fail; | ||||
|         } | ||||
|  | ||||
|         magic = &header[OFFS_MAGIC]; | ||||
|         if (strncmp(magic, POSIX_TAR_MAGIC, 5)) { | ||||
|             fprintf(stderr, "Tar: Invalid magic: %s\n", magic); | ||||
|             goto fail; | ||||
|         } | ||||
|  | ||||
|         dprintf("file type: %c\n", header[OFFS_TYPE]); | ||||
|  | ||||
|         /* file length*/ | ||||
|         s->file_len = (tar2u64(&header[OFFS_LENGTH]) + (SECTOR_SIZE - 1)) & | ||||
|                       ~(SECTOR_SIZE - 1); | ||||
|         s->file_sec = (header_offs / SECTOR_SIZE) + 1; | ||||
|  | ||||
|         header_offs += s->file_len + SECTOR_SIZE; | ||||
|  | ||||
|         if (header[OFFS_TYPE] == 'L') { | ||||
|             bdrv_pread(s->hd, header_offs - s->file_len, s->longfile, | ||||
|                        sizeof(s->longfile)); | ||||
|             s->longfile[sizeof(s->longfile)-1] = '\0'; | ||||
|             real_file = header; | ||||
|         } else if (s->longfile[0]) { | ||||
|             real_file = s->longfile; | ||||
|         } else { | ||||
|             real_file = header; | ||||
|         } | ||||
|     } while(!is_target_file(bs, real_file, header)); | ||||
|  | ||||
|     /* We found an image! */ | ||||
|  | ||||
|     if (header[OFFS_TYPE] == 'S') { | ||||
|         uint8_t isextended; | ||||
|         int i; | ||||
|  | ||||
|         for (i = OFFS_S_SP; i < (OFFS_S_SP + (4 * 24)); i += 24) | ||||
|             tar_sparse(s, tar2u64(&header[i]), tar2u64(&header[i+12])); | ||||
|  | ||||
|         s->file_len = tar2u64(&header[OFFS_S_LENGTH]); | ||||
|         isextended = header[OFFS_S_EXT]; | ||||
|  | ||||
|         while (isextended) { | ||||
|             if (bdrv_pread(s->hd, s->file_sec * SECTOR_SIZE, header, | ||||
|                            SECTOR_SIZE) != SECTOR_SIZE) | ||||
|                 goto fail; | ||||
|  | ||||
|             for (i = 0; i < (21 * 24); i += 24) | ||||
|                 tar_sparse(s, tar2u64(&header[i]), tar2u64(&header[i+12])); | ||||
|             isextended = header[OFFS_SX_EXT]; | ||||
|             s->file_sec++; | ||||
|         } | ||||
|         tar_sparse(s, s->file_len, 1); | ||||
|     } | ||||
|     qemu_opts_del(opts); | ||||
|  | ||||
|     return 0; | ||||
|  | ||||
| fail: | ||||
|     fprintf(stderr, "Tar: Error opening file\n"); | ||||
|     bdrv_unref(s->hd); | ||||
|     qemu_opts_del(opts); | ||||
|     return -EINVAL; | ||||
| } | ||||
|  | ||||
| typedef struct TarAIOCB { | ||||
|     BlockAIOCB common; | ||||
|     QEMUBH *bh; | ||||
| } TarAIOCB; | ||||
|  | ||||
| /* This callback gets invoked when we have pure sparseness */ | ||||
| static void tar_sparse_cb(void *opaque) | ||||
| { | ||||
|     TarAIOCB *acb = (TarAIOCB *)opaque; | ||||
|  | ||||
|     acb->common.cb(acb->common.opaque, 0); | ||||
|     qemu_bh_delete(acb->bh); | ||||
|     qemu_aio_unref(acb); | ||||
| } | ||||
|  | ||||
| static AIOCBInfo tar_aiocb_info = { | ||||
|     .aiocb_size         = sizeof(TarAIOCB), | ||||
| }; | ||||
|  | ||||
| /* This is where we get a request from a caller to read something */ | ||||
| static BlockAIOCB *tar_aio_readv(BlockDriverState *bs, | ||||
|         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | ||||
|         BlockCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     BDRVTarState *s = bs->opaque; | ||||
|     SparseCache *sparse; | ||||
|     int64_t sec_file = sector_num + s->file_sec; | ||||
|     int64_t start = sector_num * SECTOR_SIZE; | ||||
|     int64_t end = start + (nb_sectors * SECTOR_SIZE); | ||||
|     int i; | ||||
|     TarAIOCB *acb; | ||||
|  | ||||
|     for (i = 0; i < s->sparse_num; i++) { | ||||
|         sparse = &s->sparse[i]; | ||||
|         if (sparse->start > end) { | ||||
|             /* We expect the cache to be start increasing */ | ||||
|             break; | ||||
|         } else if ((sparse->start < start) && (sparse->end <= start)) { | ||||
|             /* sparse before our offset */ | ||||
|             sec_file -= (sparse->end - sparse->start) / SECTOR_SIZE; | ||||
|         } else if ((sparse->start <= start) && (sparse->end >= end)) { | ||||
|             /* all our sectors are sparse */ | ||||
|             char *buf = g_malloc0(nb_sectors * SECTOR_SIZE); | ||||
|  | ||||
|             acb = qemu_aio_get(&tar_aiocb_info, bs, cb, opaque); | ||||
|             qemu_iovec_from_buf(qiov, 0, buf, nb_sectors * SECTOR_SIZE); | ||||
|             g_free(buf); | ||||
|             acb->bh = qemu_bh_new(tar_sparse_cb, acb); | ||||
|             qemu_bh_schedule(acb->bh); | ||||
|  | ||||
|             return &acb->common; | ||||
|         } else if (((sparse->start >= start) && (sparse->start < end)) || | ||||
|                    ((sparse->end >= start) && (sparse->end < end))) { | ||||
|             /* we're semi-sparse (worst case) */ | ||||
|             /* let's go synchronous and read all sectors individually */ | ||||
|             char *buf = g_malloc(nb_sectors * SECTOR_SIZE); | ||||
|             uint64_t offs; | ||||
|  | ||||
|             for (offs = 0; offs < (nb_sectors * SECTOR_SIZE); | ||||
|                  offs += SECTOR_SIZE) { | ||||
|                 bdrv_pread(bs, (sector_num * SECTOR_SIZE) + offs, | ||||
|                            buf + offs, SECTOR_SIZE); | ||||
|             } | ||||
|  | ||||
|             qemu_iovec_from_buf(qiov, 0, buf, nb_sectors * SECTOR_SIZE); | ||||
|             acb = qemu_aio_get(&tar_aiocb_info, bs, cb, opaque); | ||||
|             acb->bh = qemu_bh_new(tar_sparse_cb, acb); | ||||
|             qemu_bh_schedule(acb->bh); | ||||
|  | ||||
|             return &acb->common; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return bdrv_aio_readv(s->hd, sec_file, qiov, nb_sectors, | ||||
|                           cb, opaque); | ||||
| } | ||||
|  | ||||
| static void tar_close(BlockDriverState *bs) | ||||
| { | ||||
|     dprintf("Close\n"); | ||||
| } | ||||
|  | ||||
| static int64_t tar_getlength(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVTarState *s = bs->opaque; | ||||
|     dprintf("getlength -> %ld\n", s->file_len); | ||||
|     return s->file_len; | ||||
| } | ||||
|  | ||||
| static BlockDriver bdrv_tar = { | ||||
|     .format_name     = "tar", | ||||
|     .protocol_name   = "tar", | ||||
|  | ||||
|     .instance_size   = sizeof(BDRVTarState), | ||||
|     .bdrv_file_open  = tar_open, | ||||
|     .bdrv_close      = tar_close, | ||||
|     .bdrv_getlength  = tar_getlength, | ||||
|  | ||||
|     .bdrv_aio_readv  = tar_aio_readv, | ||||
| }; | ||||
|  | ||||
| static void tar_block_init(void) | ||||
| { | ||||
|     bdrv_register(&bdrv_tar); | ||||
| } | ||||
|  | ||||
| block_init(tar_block_init); | ||||
| @@ -1,483 +0,0 @@ | ||||
| /* | ||||
|  * QEMU block throttling group infrastructure | ||||
|  * | ||||
|  * Copyright (C) Nodalink, EURL. 2014 | ||||
|  * Copyright (C) Igalia, S.L. 2015 | ||||
|  * | ||||
|  * Authors: | ||||
|  *   Benoît Canet <benoit.canet@nodalink.com> | ||||
|  *   Alberto Garcia <berto@igalia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License as | ||||
|  * published by the Free Software Foundation; either version 2 or | ||||
|  * (at your option) version 3 of the License. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "block/throttle-groups.h" | ||||
| #include "qemu/queue.h" | ||||
| #include "qemu/thread.h" | ||||
| #include "sysemu/qtest.h" | ||||
|  | ||||
| /* The ThrottleGroup structure (with its ThrottleState) is shared | ||||
|  * among different BlockDriverState and it's independent from | ||||
|  * AioContext, so in order to use it from different threads it needs | ||||
|  * its own locking. | ||||
|  * | ||||
|  * This locking is however handled internally in this file, so it's | ||||
|  * transparent to outside users. | ||||
|  * | ||||
|  * The whole ThrottleGroup structure is private and invisible to | ||||
|  * outside users, that only use it through its ThrottleState. | ||||
|  * | ||||
|  * In addition to the ThrottleGroup structure, BlockDriverState has | ||||
|  * fields that need to be accessed by other members of the group and | ||||
|  * therefore also need to be protected by this lock. Once a BDS is | ||||
|  * registered in a group those fields can be accessed by other threads | ||||
|  * any time. | ||||
|  * | ||||
|  * Again, all this is handled internally and is mostly transparent to | ||||
|  * the outside. The 'throttle_timers' field however has an additional | ||||
|  * constraint because it may be temporarily invalid (see for example | ||||
|  * bdrv_set_aio_context()). Therefore in this file a thread will | ||||
|  * access some other BDS's timers only after verifying that that BDS | ||||
|  * has throttled requests in the queue. | ||||
|  */ | ||||
| typedef struct ThrottleGroup { | ||||
|     char *name; /* This is constant during the lifetime of the group */ | ||||
|  | ||||
|     QemuMutex lock; /* This lock protects the following four fields */ | ||||
|     ThrottleState ts; | ||||
|     QLIST_HEAD(, BlockDriverState) head; | ||||
|     BlockDriverState *tokens[2]; | ||||
|     bool any_timer_armed[2]; | ||||
|  | ||||
|     /* These two are protected by the global throttle_groups_lock */ | ||||
|     unsigned refcount; | ||||
|     QTAILQ_ENTRY(ThrottleGroup) list; | ||||
| } ThrottleGroup; | ||||
|  | ||||
| static QemuMutex throttle_groups_lock; | ||||
| static QTAILQ_HEAD(, ThrottleGroup) throttle_groups = | ||||
|     QTAILQ_HEAD_INITIALIZER(throttle_groups); | ||||
|  | ||||
| /* Increments the reference count of a ThrottleGroup given its name. | ||||
|  * | ||||
|  * If no ThrottleGroup is found with the given name a new one is | ||||
|  * created. | ||||
|  * | ||||
|  * @name: the name of the ThrottleGroup | ||||
|  * @ret:  the ThrottleState member of the ThrottleGroup | ||||
|  */ | ||||
| ThrottleState *throttle_group_incref(const char *name) | ||||
| { | ||||
|     ThrottleGroup *tg = NULL; | ||||
|     ThrottleGroup *iter; | ||||
|  | ||||
|     qemu_mutex_lock(&throttle_groups_lock); | ||||
|  | ||||
|     /* Look for an existing group with that name */ | ||||
|     QTAILQ_FOREACH(iter, &throttle_groups, list) { | ||||
|         if (!strcmp(name, iter->name)) { | ||||
|             tg = iter; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* Create a new one if not found */ | ||||
|     if (!tg) { | ||||
|         tg = g_new0(ThrottleGroup, 1); | ||||
|         tg->name = g_strdup(name); | ||||
|         qemu_mutex_init(&tg->lock); | ||||
|         throttle_init(&tg->ts); | ||||
|         QLIST_INIT(&tg->head); | ||||
|  | ||||
|         QTAILQ_INSERT_TAIL(&throttle_groups, tg, list); | ||||
|     } | ||||
|  | ||||
|     tg->refcount++; | ||||
|  | ||||
|     qemu_mutex_unlock(&throttle_groups_lock); | ||||
|  | ||||
|     return &tg->ts; | ||||
| } | ||||
|  | ||||
| /* Decrease the reference count of a ThrottleGroup. | ||||
|  * | ||||
|  * When the reference count reaches zero the ThrottleGroup is | ||||
|  * destroyed. | ||||
|  * | ||||
|  * @ts:  The ThrottleGroup to unref, given by its ThrottleState member | ||||
|  */ | ||||
| void throttle_group_unref(ThrottleState *ts) | ||||
| { | ||||
|     ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); | ||||
|  | ||||
|     qemu_mutex_lock(&throttle_groups_lock); | ||||
|     if (--tg->refcount == 0) { | ||||
|         QTAILQ_REMOVE(&throttle_groups, tg, list); | ||||
|         qemu_mutex_destroy(&tg->lock); | ||||
|         g_free(tg->name); | ||||
|         g_free(tg); | ||||
|     } | ||||
|     qemu_mutex_unlock(&throttle_groups_lock); | ||||
| } | ||||
|  | ||||
| /* Get the name from a BlockDriverState's ThrottleGroup. The name (and | ||||
|  * the pointer) is guaranteed to remain constant during the lifetime | ||||
|  * of the group. | ||||
|  * | ||||
|  * @bs:   a BlockDriverState that is member of a throttling group | ||||
|  * @ret:  the name of the group. | ||||
|  */ | ||||
| const char *throttle_group_get_name(BlockDriverState *bs) | ||||
| { | ||||
|     ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); | ||||
|     return tg->name; | ||||
| } | ||||
|  | ||||
| /* Return the next BlockDriverState in the round-robin sequence, | ||||
|  * simulating a circular list. | ||||
|  * | ||||
|  * This assumes that tg->lock is held. | ||||
|  * | ||||
|  * @bs:  the current BlockDriverState | ||||
|  * @ret: the next BlockDriverState in the sequence | ||||
|  */ | ||||
| static BlockDriverState *throttle_group_next_bs(BlockDriverState *bs) | ||||
| { | ||||
|     ThrottleState *ts = bs->throttle_state; | ||||
|     ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); | ||||
|     BlockDriverState *next = QLIST_NEXT(bs, round_robin); | ||||
|  | ||||
|     if (!next) { | ||||
|         return QLIST_FIRST(&tg->head); | ||||
|     } | ||||
|  | ||||
|     return next; | ||||
| } | ||||
|  | ||||
| /* Return the next BlockDriverState in the round-robin sequence with | ||||
|  * pending I/O requests. | ||||
|  * | ||||
|  * This assumes that tg->lock is held. | ||||
|  * | ||||
|  * @bs:        the current BlockDriverState | ||||
|  * @is_write:  the type of operation (read/write) | ||||
|  * @ret:       the next BlockDriverState with pending requests, or bs | ||||
|  *             if there is none. | ||||
|  */ | ||||
| static BlockDriverState *next_throttle_token(BlockDriverState *bs, | ||||
|                                              bool is_write) | ||||
| { | ||||
|     ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); | ||||
|     BlockDriverState *token, *start; | ||||
|  | ||||
|     start = token = tg->tokens[is_write]; | ||||
|  | ||||
|     /* get next bs round in round robin style */ | ||||
|     token = throttle_group_next_bs(token); | ||||
|     while (token != start && !token->pending_reqs[is_write]) { | ||||
|         token = throttle_group_next_bs(token); | ||||
|     } | ||||
|  | ||||
|     /* If no IO are queued for scheduling on the next round robin token | ||||
|      * then decide the token is the current bs because chances are | ||||
|      * the current bs get the current request queued. | ||||
|      */ | ||||
|     if (token == start && !token->pending_reqs[is_write]) { | ||||
|         token = bs; | ||||
|     } | ||||
|  | ||||
|     return token; | ||||
| } | ||||
|  | ||||
| /* Check if the next I/O request for a BlockDriverState needs to be | ||||
|  * throttled or not. If there's no timer set in this group, set one | ||||
|  * and update the token accordingly. | ||||
|  * | ||||
|  * This assumes that tg->lock is held. | ||||
|  * | ||||
|  * @bs:         the current BlockDriverState | ||||
|  * @is_write:   the type of operation (read/write) | ||||
|  * @ret:        whether the I/O request needs to be throttled or not | ||||
|  */ | ||||
| static bool throttle_group_schedule_timer(BlockDriverState *bs, | ||||
|                                           bool is_write) | ||||
| { | ||||
|     ThrottleState *ts = bs->throttle_state; | ||||
|     ThrottleTimers *tt = &bs->throttle_timers; | ||||
|     ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); | ||||
|     bool must_wait; | ||||
|  | ||||
|     /* Check if any of the timers in this group is already armed */ | ||||
|     if (tg->any_timer_armed[is_write]) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     must_wait = throttle_schedule_timer(ts, tt, is_write); | ||||
|  | ||||
|     /* If a timer just got armed, set bs as the current token */ | ||||
|     if (must_wait) { | ||||
|         tg->tokens[is_write] = bs; | ||||
|         tg->any_timer_armed[is_write] = true; | ||||
|     } | ||||
|  | ||||
|     return must_wait; | ||||
| } | ||||
|  | ||||
| /* Look for the next pending I/O request and schedule it. | ||||
|  * | ||||
|  * This assumes that tg->lock is held. | ||||
|  * | ||||
|  * @bs:        the current BlockDriverState | ||||
|  * @is_write:  the type of operation (read/write) | ||||
|  */ | ||||
| static void schedule_next_request(BlockDriverState *bs, bool is_write) | ||||
| { | ||||
|     ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); | ||||
|     bool must_wait; | ||||
|     BlockDriverState *token; | ||||
|  | ||||
|     /* Check if there's any pending request to schedule next */ | ||||
|     token = next_throttle_token(bs, is_write); | ||||
|     if (!token->pending_reqs[is_write]) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* Set a timer for the request if it needs to be throttled */ | ||||
|     must_wait = throttle_group_schedule_timer(token, is_write); | ||||
|  | ||||
|     /* If it doesn't have to wait, queue it for immediate execution */ | ||||
|     if (!must_wait) { | ||||
|         /* Give preference to requests from the current bs */ | ||||
|         if (qemu_in_coroutine() && | ||||
|             qemu_co_queue_next(&bs->throttled_reqs[is_write])) { | ||||
|             token = bs; | ||||
|         } else { | ||||
|             ThrottleTimers *tt = &token->throttle_timers; | ||||
|             int64_t now = qemu_clock_get_ns(tt->clock_type); | ||||
|             timer_mod(tt->timers[is_write], now + 1); | ||||
|             tg->any_timer_armed[is_write] = true; | ||||
|         } | ||||
|         tg->tokens[is_write] = token; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Check if an I/O request needs to be throttled, wait and set a timer | ||||
|  * if necessary, and schedule the next request using a round robin | ||||
|  * algorithm. | ||||
|  * | ||||
|  * @bs:        the current BlockDriverState | ||||
|  * @bytes:     the number of bytes for this I/O | ||||
|  * @is_write:  the type of operation (read/write) | ||||
|  */ | ||||
| void coroutine_fn throttle_group_co_io_limits_intercept(BlockDriverState *bs, | ||||
|                                                         unsigned int bytes, | ||||
|                                                         bool is_write) | ||||
| { | ||||
|     bool must_wait; | ||||
|     BlockDriverState *token; | ||||
|  | ||||
|     ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); | ||||
|     qemu_mutex_lock(&tg->lock); | ||||
|  | ||||
|     /* First we check if this I/O has to be throttled. */ | ||||
|     token = next_throttle_token(bs, is_write); | ||||
|     must_wait = throttle_group_schedule_timer(token, is_write); | ||||
|  | ||||
|     /* Wait if there's a timer set or queued requests of this type */ | ||||
|     if (must_wait || bs->pending_reqs[is_write]) { | ||||
|         bs->pending_reqs[is_write]++; | ||||
|         qemu_mutex_unlock(&tg->lock); | ||||
|         qemu_co_queue_wait(&bs->throttled_reqs[is_write]); | ||||
|         qemu_mutex_lock(&tg->lock); | ||||
|         bs->pending_reqs[is_write]--; | ||||
|     } | ||||
|  | ||||
|     /* The I/O will be executed, so do the accounting */ | ||||
|     throttle_account(bs->throttle_state, is_write, bytes); | ||||
|  | ||||
|     /* Schedule the next request */ | ||||
|     schedule_next_request(bs, is_write); | ||||
|  | ||||
|     qemu_mutex_unlock(&tg->lock); | ||||
| } | ||||
|  | ||||
| /* Update the throttle configuration for a particular group. Similar | ||||
|  * to throttle_config(), but guarantees atomicity within the | ||||
|  * throttling group. | ||||
|  * | ||||
|  * @bs:  a BlockDriverState that is member of the group | ||||
|  * @cfg: the configuration to set | ||||
|  */ | ||||
| void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg) | ||||
| { | ||||
|     ThrottleTimers *tt = &bs->throttle_timers; | ||||
|     ThrottleState *ts = bs->throttle_state; | ||||
|     ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); | ||||
|     qemu_mutex_lock(&tg->lock); | ||||
|     /* throttle_config() cancels the timers */ | ||||
|     if (timer_pending(tt->timers[0])) { | ||||
|         tg->any_timer_armed[0] = false; | ||||
|     } | ||||
|     if (timer_pending(tt->timers[1])) { | ||||
|         tg->any_timer_armed[1] = false; | ||||
|     } | ||||
|     throttle_config(ts, tt, cfg); | ||||
|     qemu_mutex_unlock(&tg->lock); | ||||
| } | ||||
|  | ||||
| /* Get the throttle configuration from a particular group. Similar to | ||||
|  * throttle_get_config(), but guarantees atomicity within the | ||||
|  * throttling group. | ||||
|  * | ||||
|  * @bs:  a BlockDriverState that is member of the group | ||||
|  * @cfg: the configuration will be written here | ||||
|  */ | ||||
| void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg) | ||||
| { | ||||
|     ThrottleState *ts = bs->throttle_state; | ||||
|     ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); | ||||
|     qemu_mutex_lock(&tg->lock); | ||||
|     throttle_get_config(ts, cfg); | ||||
|     qemu_mutex_unlock(&tg->lock); | ||||
| } | ||||
|  | ||||
| /* ThrottleTimers callback. This wakes up a request that was waiting | ||||
|  * because it had been throttled. | ||||
|  * | ||||
|  * @bs:        the BlockDriverState whose request had been throttled | ||||
|  * @is_write:  the type of operation (read/write) | ||||
|  */ | ||||
| static void timer_cb(BlockDriverState *bs, bool is_write) | ||||
| { | ||||
|     ThrottleState *ts = bs->throttle_state; | ||||
|     ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); | ||||
|     bool empty_queue; | ||||
|  | ||||
|     /* The timer has just been fired, so we can update the flag */ | ||||
|     qemu_mutex_lock(&tg->lock); | ||||
|     tg->any_timer_armed[is_write] = false; | ||||
|     qemu_mutex_unlock(&tg->lock); | ||||
|  | ||||
|     /* Run the request that was waiting for this timer */ | ||||
|     empty_queue = !qemu_co_enter_next(&bs->throttled_reqs[is_write]); | ||||
|  | ||||
|     /* If the request queue was empty then we have to take care of | ||||
|      * scheduling the next one */ | ||||
|     if (empty_queue) { | ||||
|         qemu_mutex_lock(&tg->lock); | ||||
|         schedule_next_request(bs, is_write); | ||||
|         qemu_mutex_unlock(&tg->lock); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void read_timer_cb(void *opaque) | ||||
| { | ||||
|     timer_cb(opaque, false); | ||||
| } | ||||
|  | ||||
| static void write_timer_cb(void *opaque) | ||||
| { | ||||
|     timer_cb(opaque, true); | ||||
| } | ||||
|  | ||||
| /* Register a BlockDriverState in the throttling group, also | ||||
|  * initializing its timers and updating its throttle_state pointer to | ||||
|  * point to it. If a throttling group with that name does not exist | ||||
|  * yet, it will be created. | ||||
|  * | ||||
|  * @bs:        the BlockDriverState to insert | ||||
|  * @groupname: the name of the group | ||||
|  */ | ||||
| void throttle_group_register_bs(BlockDriverState *bs, const char *groupname) | ||||
| { | ||||
|     int i; | ||||
|     ThrottleState *ts = throttle_group_incref(groupname); | ||||
|     ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); | ||||
|     int clock_type = QEMU_CLOCK_REALTIME; | ||||
|  | ||||
|     if (qtest_enabled()) { | ||||
|         /* For testing block IO throttling only */ | ||||
|         clock_type = QEMU_CLOCK_VIRTUAL; | ||||
|     } | ||||
|  | ||||
|     bs->throttle_state = ts; | ||||
|  | ||||
|     qemu_mutex_lock(&tg->lock); | ||||
|     /* If the ThrottleGroup is new set this BlockDriverState as the token */ | ||||
|     for (i = 0; i < 2; i++) { | ||||
|         if (!tg->tokens[i]) { | ||||
|             tg->tokens[i] = bs; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     QLIST_INSERT_HEAD(&tg->head, bs, round_robin); | ||||
|  | ||||
|     throttle_timers_init(&bs->throttle_timers, | ||||
|                          bdrv_get_aio_context(bs), | ||||
|                          clock_type, | ||||
|                          read_timer_cb, | ||||
|                          write_timer_cb, | ||||
|                          bs); | ||||
|  | ||||
|     qemu_mutex_unlock(&tg->lock); | ||||
| } | ||||
|  | ||||
| /* Unregister a BlockDriverState from its group, removing it from the | ||||
|  * list, destroying the timers and setting the throttle_state pointer | ||||
|  * to NULL. | ||||
|  * | ||||
|  * The BlockDriverState must not have pending throttled requests, so | ||||
|  * the caller has to drain them first. | ||||
|  * | ||||
|  * The group will be destroyed if it's empty after this operation. | ||||
|  * | ||||
|  * @bs: the BlockDriverState to remove | ||||
|  */ | ||||
| void throttle_group_unregister_bs(BlockDriverState *bs) | ||||
| { | ||||
|     ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); | ||||
|     int i; | ||||
|  | ||||
|     assert(bs->pending_reqs[0] == 0 && bs->pending_reqs[1] == 0); | ||||
|     assert(qemu_co_queue_empty(&bs->throttled_reqs[0])); | ||||
|     assert(qemu_co_queue_empty(&bs->throttled_reqs[1])); | ||||
|  | ||||
|     qemu_mutex_lock(&tg->lock); | ||||
|     for (i = 0; i < 2; i++) { | ||||
|         if (tg->tokens[i] == bs) { | ||||
|             BlockDriverState *token = throttle_group_next_bs(bs); | ||||
|             /* Take care of the case where this is the last bs in the group */ | ||||
|             if (token == bs) { | ||||
|                 token = NULL; | ||||
|             } | ||||
|             tg->tokens[i] = token; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* remove the current bs from the list */ | ||||
|     QLIST_REMOVE(bs, round_robin); | ||||
|     throttle_timers_destroy(&bs->throttle_timers); | ||||
|     qemu_mutex_unlock(&tg->lock); | ||||
|  | ||||
|     throttle_group_unref(&tg->ts); | ||||
|     bs->throttle_state = NULL; | ||||
| } | ||||
|  | ||||
| static void throttle_groups_init(void) | ||||
| { | ||||
|     qemu_mutex_init(&throttle_groups_lock); | ||||
| } | ||||
|  | ||||
| block_init(throttle_groups_init); | ||||
							
								
								
									
										31
									
								
								block/vdi.c
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								block/vdi.c
									
									
									
									
									
								
							| @@ -49,12 +49,11 @@ | ||||
|  * so this seems to be reasonable. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "block/block_int.h" | ||||
| #include "qemu/module.h" | ||||
| #include "migration/migration.h" | ||||
| #include "qemu/coroutine.h" | ||||
| #include "block/coroutine.h" | ||||
|  | ||||
| #if defined(CONFIG_UUID) | ||||
| #include <uuid/uuid.h> | ||||
| @@ -400,7 +399,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|  | ||||
|     logout("\n"); | ||||
|  | ||||
|     ret = bdrv_read(bs->file->bs, 0, (uint8_t *)&header, 1); | ||||
|     ret = bdrv_read(bs->file, 0, (uint8_t *)&header, 1); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
| @@ -491,22 +490,21 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|  | ||||
|     bmap_size = header.blocks_in_image * sizeof(uint32_t); | ||||
|     bmap_size = DIV_ROUND_UP(bmap_size, SECTOR_SIZE); | ||||
|     s->bmap = qemu_try_blockalign(bs->file->bs, bmap_size * SECTOR_SIZE); | ||||
|     s->bmap = qemu_try_blockalign(bs->file, bmap_size * SECTOR_SIZE); | ||||
|     if (s->bmap == NULL) { | ||||
|         ret = -ENOMEM; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_read(bs->file->bs, s->bmap_sector, (uint8_t *)s->bmap, | ||||
|                     bmap_size); | ||||
|     ret = bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap, bmap_size); | ||||
|     if (ret < 0) { | ||||
|         goto fail_free_bmap; | ||||
|     } | ||||
|  | ||||
|     /* Disable migration when vdi images are used */ | ||||
|     error_setg(&s->migration_blocker, "The vdi format used by node '%s' " | ||||
|                "does not support live migration", | ||||
|                bdrv_get_device_or_node_name(bs)); | ||||
|     error_set(&s->migration_blocker, | ||||
|               QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, | ||||
|               "vdi", bdrv_get_device_name(bs), "live migration"); | ||||
|     migrate_add_blocker(s->migration_blocker); | ||||
|  | ||||
|     qemu_co_mutex_init(&s->write_lock); | ||||
| @@ -527,7 +525,7 @@ static int vdi_reopen_prepare(BDRVReopenState *state, | ||||
| } | ||||
|  | ||||
| static int64_t coroutine_fn vdi_co_get_block_status(BlockDriverState *bs, | ||||
|         int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file) | ||||
|         int64_t sector_num, int nb_sectors, int *pnum) | ||||
| { | ||||
|     /* TODO: Check for too large sector_num (in bdrv_is_allocated or here). */ | ||||
|     BDRVVdiState *s = (BDRVVdiState *)bs->opaque; | ||||
| @@ -551,7 +549,6 @@ static int64_t coroutine_fn vdi_co_get_block_status(BlockDriverState *bs, | ||||
|     offset = s->header.offset_data + | ||||
|                               (uint64_t)bmap_entry * s->block_size + | ||||
|                               sector_in_block * SECTOR_SIZE; | ||||
|     *file = bs->file->bs; | ||||
|     return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset; | ||||
| } | ||||
|  | ||||
| @@ -588,7 +585,7 @@ static int vdi_co_read(BlockDriverState *bs, | ||||
|             uint64_t offset = s->header.offset_data / SECTOR_SIZE + | ||||
|                               (uint64_t)bmap_entry * s->block_sectors + | ||||
|                               sector_in_block; | ||||
|             ret = bdrv_read(bs->file->bs, offset, buf, n_sectors); | ||||
|             ret = bdrv_read(bs->file, offset, buf, n_sectors); | ||||
|         } | ||||
|         logout("%u sectors read\n", n_sectors); | ||||
|  | ||||
| @@ -656,7 +653,7 @@ static int vdi_co_write(BlockDriverState *bs, | ||||
|              * acquire the lock and thus the padded cluster is written before | ||||
|              * the other coroutines can write to the affected area. */ | ||||
|             qemu_co_mutex_lock(&s->write_lock); | ||||
|             ret = bdrv_write(bs->file->bs, offset, block, s->block_sectors); | ||||
|             ret = bdrv_write(bs->file, offset, block, s->block_sectors); | ||||
|             qemu_co_mutex_unlock(&s->write_lock); | ||||
|         } else { | ||||
|             uint64_t offset = s->header.offset_data / SECTOR_SIZE + | ||||
| @@ -672,7 +669,7 @@ static int vdi_co_write(BlockDriverState *bs, | ||||
|              * that that write operation has returned (there may be other writes | ||||
|              * in flight, but they do not concern this very operation). */ | ||||
|             qemu_co_mutex_unlock(&s->write_lock); | ||||
|             ret = bdrv_write(bs->file->bs, offset, buf, n_sectors); | ||||
|             ret = bdrv_write(bs->file, offset, buf, n_sectors); | ||||
|         } | ||||
|  | ||||
|         nb_sectors -= n_sectors; | ||||
| @@ -697,7 +694,7 @@ static int vdi_co_write(BlockDriverState *bs, | ||||
|         assert(VDI_IS_ALLOCATED(bmap_first)); | ||||
|         *header = s->header; | ||||
|         vdi_header_to_le(header); | ||||
|         ret = bdrv_write(bs->file->bs, 0, block, 1); | ||||
|         ret = bdrv_write(bs->file, 0, block, 1); | ||||
|         g_free(block); | ||||
|         block = NULL; | ||||
|  | ||||
| @@ -715,7 +712,7 @@ static int vdi_co_write(BlockDriverState *bs, | ||||
|         base = ((uint8_t *)&s->bmap[0]) + bmap_first * SECTOR_SIZE; | ||||
|         logout("will write %u block map sectors starting from entry %u\n", | ||||
|                n_sectors, bmap_first); | ||||
|         ret = bdrv_write(bs->file->bs, offset, base, n_sectors); | ||||
|         ret = bdrv_write(bs->file, offset, base, n_sectors); | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
| @@ -767,7 +764,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp) | ||||
|         goto exit; | ||||
|     } | ||||
|     ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, | ||||
|                     &local_err); | ||||
|                     NULL, &local_err); | ||||
|     if (ret < 0) { | ||||
|         error_propagate(errp, local_err); | ||||
|         goto exit; | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user