Compare commits
	
		
			5 Commits
		
	
	
		
			qemu-sparc
			...
			rm-protoco
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 6321d78dc5 | ||
|  | a0974f99aa | ||
|  | 42883c01bf | ||
|  | 7966c2b312 | ||
|  | 5888011244 | 
| @@ -1,10 +1,4 @@ | ||||
| # EditorConfig is a file format and collection of text editor plugins | ||||
| # for maintaining consistent coding styles between different editors | ||||
| # and IDEs. Most popular editors support this either natively or via | ||||
| # plugin. | ||||
| # | ||||
| # Check https://editorconfig.org for details. | ||||
|  | ||||
| # http://editorconfig.org | ||||
| root = true | ||||
|  | ||||
| [*] | ||||
| @@ -12,23 +6,10 @@ end_of_line = lf | ||||
| insert_final_newline = true | ||||
| charset = utf-8 | ||||
|  | ||||
| [*.mak] | ||||
| indent_style = tab | ||||
| indent_size = 8 | ||||
| file_type_emacs = makefile | ||||
|  | ||||
| [Makefile*] | ||||
| indent_style = tab | ||||
| indent_size = 8 | ||||
| file_type_emacs = makefile | ||||
|  | ||||
| [*.{c,h}] | ||||
| indent_style = space | ||||
| indent_size = 4 | ||||
|  | ||||
| [*.{vert,frag}] | ||||
| file_type_emacs = glsl | ||||
|  | ||||
| [*.json] | ||||
| indent_style = space | ||||
| file_type_emacs = python | ||||
|   | ||||
							
								
								
									
										71
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										71
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -30,20 +30,79 @@ | ||||
| /qapi-gen-timestamp | ||||
| /qapi/qapi-builtin-types.[ch] | ||||
| /qapi/qapi-builtin-visit.[ch] | ||||
| /qapi/qapi-commands-*.[ch] | ||||
| /qapi/qapi-commands-block-core.[ch] | ||||
| /qapi/qapi-commands-block.[ch] | ||||
| /qapi/qapi-commands-char.[ch] | ||||
| /qapi/qapi-commands-common.[ch] | ||||
| /qapi/qapi-commands-crypto.[ch] | ||||
| /qapi/qapi-commands-introspect.[ch] | ||||
| /qapi/qapi-commands-migration.[ch] | ||||
| /qapi/qapi-commands-misc.[ch] | ||||
| /qapi/qapi-commands-net.[ch] | ||||
| /qapi/qapi-commands-rocker.[ch] | ||||
| /qapi/qapi-commands-run-state.[ch] | ||||
| /qapi/qapi-commands-sockets.[ch] | ||||
| /qapi/qapi-commands-tpm.[ch] | ||||
| /qapi/qapi-commands-trace.[ch] | ||||
| /qapi/qapi-commands-transaction.[ch] | ||||
| /qapi/qapi-commands-ui.[ch] | ||||
| /qapi/qapi-commands.[ch] | ||||
| /qapi/qapi-events-*.[ch] | ||||
| /qapi/qapi-events-block-core.[ch] | ||||
| /qapi/qapi-events-block.[ch] | ||||
| /qapi/qapi-events-char.[ch] | ||||
| /qapi/qapi-events-common.[ch] | ||||
| /qapi/qapi-events-crypto.[ch] | ||||
| /qapi/qapi-events-introspect.[ch] | ||||
| /qapi/qapi-events-migration.[ch] | ||||
| /qapi/qapi-events-misc.[ch] | ||||
| /qapi/qapi-events-net.[ch] | ||||
| /qapi/qapi-events-rocker.[ch] | ||||
| /qapi/qapi-events-run-state.[ch] | ||||
| /qapi/qapi-events-sockets.[ch] | ||||
| /qapi/qapi-events-tpm.[ch] | ||||
| /qapi/qapi-events-trace.[ch] | ||||
| /qapi/qapi-events-transaction.[ch] | ||||
| /qapi/qapi-events-ui.[ch] | ||||
| /qapi/qapi-events.[ch] | ||||
| /qapi/qapi-introspect.[ch] | ||||
| /qapi/qapi-types-*.[ch] | ||||
| /qapi/qapi-types-block-core.[ch] | ||||
| /qapi/qapi-types-block.[ch] | ||||
| /qapi/qapi-types-char.[ch] | ||||
| /qapi/qapi-types-common.[ch] | ||||
| /qapi/qapi-types-crypto.[ch] | ||||
| /qapi/qapi-types-introspect.[ch] | ||||
| /qapi/qapi-types-migration.[ch] | ||||
| /qapi/qapi-types-misc.[ch] | ||||
| /qapi/qapi-types-net.[ch] | ||||
| /qapi/qapi-types-rocker.[ch] | ||||
| /qapi/qapi-types-run-state.[ch] | ||||
| /qapi/qapi-types-sockets.[ch] | ||||
| /qapi/qapi-types-tpm.[ch] | ||||
| /qapi/qapi-types-trace.[ch] | ||||
| /qapi/qapi-types-transaction.[ch] | ||||
| /qapi/qapi-types-ui.[ch] | ||||
| /qapi/qapi-types.[ch] | ||||
| /qapi/qapi-visit-*.[ch] | ||||
| /qapi/qapi-visit-block-core.[ch] | ||||
| /qapi/qapi-visit-block.[ch] | ||||
| /qapi/qapi-visit-char.[ch] | ||||
| /qapi/qapi-visit-common.[ch] | ||||
| /qapi/qapi-visit-crypto.[ch] | ||||
| /qapi/qapi-visit-introspect.[ch] | ||||
| /qapi/qapi-visit-migration.[ch] | ||||
| /qapi/qapi-visit-misc.[ch] | ||||
| /qapi/qapi-visit-net.[ch] | ||||
| /qapi/qapi-visit-rocker.[ch] | ||||
| /qapi/qapi-visit-run-state.[ch] | ||||
| /qapi/qapi-visit-sockets.[ch] | ||||
| /qapi/qapi-visit-tpm.[ch] | ||||
| /qapi/qapi-visit-trace.[ch] | ||||
| /qapi/qapi-visit-transaction.[ch] | ||||
| /qapi/qapi-visit-ui.[ch] | ||||
| /qapi/qapi-visit.[ch] | ||||
| /qapi/qapi-doc.texi | ||||
| /qemu-doc.html | ||||
| /qemu-doc.info | ||||
| /qemu-doc.txt | ||||
| /qemu-edid | ||||
| /qemu-img | ||||
| /qemu-nbd | ||||
| /qemu-options.def | ||||
| @@ -92,7 +151,6 @@ | ||||
| .sdk | ||||
| *.gcda | ||||
| *.gcno | ||||
| *.gcov | ||||
| /pc-bios/bios-pq/status | ||||
| /pc-bios/vgabios-pq/status | ||||
| /pc-bios/optionrom/linuxboot.asm | ||||
| @@ -148,4 +206,3 @@ trace-dtrace-root.h | ||||
| trace-dtrace-root.dtrace | ||||
| trace-ust-all.h | ||||
| trace-ust-all.c | ||||
| /target/arm/decode-sve.inc.c | ||||
|   | ||||
							
								
								
									
										39
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										39
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @@ -1,51 +1,48 @@ | ||||
| [submodule "roms/vgabios"] | ||||
| 	path = roms/vgabios | ||||
| 	url = git://git.qemu-project.org/vgabios.git/ | ||||
| [submodule "roms/seabios"] | ||||
| 	path = roms/seabios | ||||
| 	url = https://git.qemu.org/git/seabios.git/ | ||||
| 	url = git://git.qemu-project.org/seabios.git/ | ||||
| [submodule "roms/SLOF"] | ||||
| 	path = roms/SLOF | ||||
| 	url = https://git.qemu.org/git/SLOF.git | ||||
| 	url = git://git.qemu-project.org/SLOF.git | ||||
| [submodule "roms/ipxe"] | ||||
| 	path = roms/ipxe | ||||
| 	url = https://git.qemu.org/git/ipxe.git | ||||
| 	url = git://git.qemu-project.org/ipxe.git | ||||
| [submodule "roms/openbios"] | ||||
| 	path = roms/openbios | ||||
| 	url = https://git.qemu.org/git/openbios.git | ||||
| 	url = git://git.qemu-project.org/openbios.git | ||||
| [submodule "roms/openhackware"] | ||||
| 	path = roms/openhackware | ||||
| 	url = https://git.qemu.org/git/openhackware.git | ||||
| 	url = git://git.qemu-project.org/openhackware.git | ||||
| [submodule "roms/qemu-palcode"] | ||||
| 	path = roms/qemu-palcode | ||||
| 	url = https://git.qemu.org/git/qemu-palcode.git | ||||
| 	url = git://github.com/rth7680/qemu-palcode.git | ||||
| [submodule "roms/sgabios"] | ||||
| 	path = roms/sgabios | ||||
| 	url = https://git.qemu.org/git/sgabios.git | ||||
| 	url = git://git.qemu-project.org/sgabios.git | ||||
| [submodule "dtc"] | ||||
| 	path = dtc | ||||
| 	url = https://git.qemu.org/git/dtc.git | ||||
| 	url = git://git.qemu-project.org/dtc.git | ||||
| [submodule "roms/u-boot"] | ||||
| 	path = roms/u-boot | ||||
| 	url = https://git.qemu.org/git/u-boot.git | ||||
| 	url = git://git.qemu-project.org/u-boot.git | ||||
| [submodule "roms/skiboot"] | ||||
| 	path = roms/skiboot | ||||
| 	url = https://git.qemu.org/git/skiboot.git | ||||
| 	url = git://git.qemu.org/skiboot.git | ||||
| [submodule "roms/QemuMacDrivers"] | ||||
| 	path = roms/QemuMacDrivers | ||||
| 	url = https://git.qemu.org/git/QemuMacDrivers.git | ||||
| 	url = git://git.qemu.org/QemuMacDrivers.git | ||||
| [submodule "ui/keycodemapdb"] | ||||
| 	path = ui/keycodemapdb | ||||
| 	url = https://git.qemu.org/git/keycodemapdb.git | ||||
| 	url = git://git.qemu.org/keycodemapdb.git | ||||
| [submodule "capstone"] | ||||
| 	path = capstone | ||||
| 	url = https://git.qemu.org/git/capstone.git | ||||
| 	url = git://git.qemu.org/capstone.git | ||||
| [submodule "roms/seabios-hppa"] | ||||
| 	path = roms/seabios-hppa | ||||
| 	url = https://github.com/hdeller/seabios-hppa.git | ||||
| 	url = git://github.com/hdeller/seabios-hppa.git | ||||
| [submodule "roms/u-boot-sam460ex"] | ||||
| 	path = roms/u-boot-sam460ex | ||||
| 	url = https://git.qemu.org/git/u-boot-sam460ex.git | ||||
| [submodule "tests/fp/berkeley-testfloat-3"] | ||||
| 	path = tests/fp/berkeley-testfloat-3 | ||||
| 	url = https://github.com/cota/berkeley-testfloat-3 | ||||
| [submodule "tests/fp/berkeley-softfloat-3"] | ||||
| 	path = tests/fp/berkeley-softfloat-3 | ||||
| 	url = https://github.com/cota/berkeley-softfloat-3 | ||||
| 	url = git://github.com/zbalaton/u-boot-sam460ex | ||||
|   | ||||
							
								
								
									
										29
									
								
								.mailmap
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								.mailmap
									
									
									
									
									
								
							| @@ -1,7 +1,6 @@ | ||||
| # This mailmap fixes up author names/addresses. | ||||
|  | ||||
| # The first section translates weird addresses from the original git import | ||||
| # into proper addresses so that they are counted properly by git shortlog. | ||||
| # This mailmap just translates the weird addresses from the original import into git | ||||
| # into proper addresses so that they are counted properly in git shortlog output. | ||||
| # | ||||
| Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||
| Anthony Liguori <anthony@codemonkey.ws> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||
| Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com> | ||||
| @@ -12,28 +11,14 @@ Fabrice Bellard <fabrice@bellard.org> bellard <bellard@c046a42c-6fe2-441c-8c8c-7 | ||||
| James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com> | ||||
| Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||
| Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||
| Yongbok Kim <yongbok.kim@mips.com> <yongbok.kim@imgtec.com> | ||||
| Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@mips.com> | ||||
| Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@imgtec.com> | ||||
| Paul Burton <pburton@wavecomp.com> <paul.burton@mips.com> | ||||
| Paul Burton <pburton@wavecomp.com> <paul.burton@imgtec.com> | ||||
| Paul Burton <pburton@wavecomp.com> <paul@archlinuxmips.org> | ||||
| Paul Burton <paul.burton@mips.com> <paul.burton@imgtec.com> | ||||
| Paul Burton <paul.burton@mips.com> <paul@archlinuxmips.org> | ||||
| Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||
| malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||
|  | ||||
| # There is also a: | ||||
| #    (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||
| # for the cvs2svn initialization commit e63c3dc74bf. | ||||
|  | ||||
| # Next, translate a few commits where mailman rewrote the From: line due | ||||
| # to strict SPF, although we prefer to avoid adding more entries like that. | ||||
| Ed Swierk <eswierk@skyportsystems.com> Ed Swierk via Qemu-devel <qemu-devel@nongnu.org> | ||||
| Ian McKellar <ianloic@google.com> Ian McKellar via Qemu-devel <qemu-devel@nongnu.org> | ||||
| Julia Suvorova <jusual@mail.ru> Julia Suvorova via Qemu-devel <qemu-devel@nongnu.org> | ||||
| Justin Terry (VM) <juterry@microsoft.com> Justin Terry (VM) via Qemu-devel <qemu-devel@nongnu.org> | ||||
|  | ||||
|  | ||||
| # | ||||
| # Also list preferred name forms where people have changed their | ||||
| # git author config, or had utf8/latin1 encoding issues. | ||||
| # git author config | ||||
| Daniel P. Berrangé <berrange@redhat.com> | ||||
| Reimar Döffinger <Reimar.Doeffinger@gmx.de> | ||||
|   | ||||
| @@ -7,11 +7,10 @@ env: | ||||
|   matrix: | ||||
|     - IMAGE=debian-amd64 | ||||
|       TARGET_LIST=x86_64-softmmu,x86_64-linux-user | ||||
|     # currently disabled as the mxe.cc repos are down | ||||
|     # - IMAGE=debian-win32-cross | ||||
|     #   TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu | ||||
|     # - IMAGE=debian-win64-cross | ||||
|     #   TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu | ||||
|     - IMAGE=debian-win32-cross | ||||
|       TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu | ||||
|     - IMAGE=debian-win64-cross | ||||
|       TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu | ||||
|     - IMAGE=debian-armel-cross | ||||
|       TARGET_LIST=arm-softmmu,arm-linux-user,armeb-linux-user | ||||
|     - IMAGE=debian-armhf-cross | ||||
| @@ -36,5 +35,13 @@ build: | ||||
|     options: "-e HOME=/root" | ||||
|   ci: | ||||
|     - unset CC | ||||
|     # some targets require newer up to date packages, for example TARGET_LIST matching | ||||
|     # aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu) | ||||
|     # see the configure script: | ||||
|     #    error_exit "DTC (libfdt) version >= 1.4.2 not present. Your options:" | ||||
|     #    "  (1) Preferred: Install the DTC (libfdt) devel package" | ||||
|     #    "  (2) Fetch the DTC submodule, using:" | ||||
|     #    "      git submodule update --init dtc" | ||||
|     - dpkg --compare-versions `dpkg-query --showformat='${Version}' --show libfdt-dev` ge 1.4.2 || git submodule update --init dtc | ||||
|     - ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST} | ||||
|     - make -j$(($(getconf _NPROCESSORS_ONLN) + 1)) | ||||
|   | ||||
							
								
								
									
										258
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										258
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,13 +1,10 @@ | ||||
| # The current Travis default is a VM based 16.04 Xenial on GCE | ||||
| # Additional builds with specific requirements for a full VM need to | ||||
| # be added as additional matrix: entries later on | ||||
| dist: xenial | ||||
| sudo: false | ||||
| language: c | ||||
| python: | ||||
|   - "2.6" | ||||
| compiler: | ||||
|   - gcc | ||||
| cache: ccache | ||||
|  | ||||
|  | ||||
| addons: | ||||
|   apt: | ||||
|     packages: | ||||
| @@ -34,17 +31,9 @@ addons: | ||||
|       - libssh2-1-dev | ||||
|       - liburcu-dev | ||||
|       - libusb-1.0-0-dev | ||||
|       - libvte-2.91-dev | ||||
|       - libvte-2.90-dev | ||||
|       - sparse | ||||
|       - uuid-dev | ||||
|       - gcovr | ||||
|   homebrew: | ||||
|     packages: | ||||
|       - libffi | ||||
|       - gettext | ||||
|       - glib | ||||
|       - pixman | ||||
|  | ||||
|  | ||||
| # The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu | ||||
| # to prevent IRC notifications from forks. This was created using: | ||||
| @@ -55,158 +44,130 @@ notifications: | ||||
|       - secure: "F7GDRgjuOo5IUyRLqSkmDL7kvdU4UcH3Lm/W2db2JnDHTGCqgEdaYEYKciyCLZ57vOTsTsOgesN8iUT7hNHBd1KWKjZe9KDTZWppWRYVwAwQMzVeSOsbbU4tRoJ6Pp+3qhH1Z0eGYR9ZgKYAoTumDFgSAYRp4IscKS8jkoedOqM=" | ||||
|     on_success: change | ||||
|     on_failure: always | ||||
|  | ||||
|  | ||||
| env: | ||||
|   global: | ||||
|     - SRC_DIR="." | ||||
|     - BUILD_DIR="." | ||||
|     - TEST_CMD="make check -j3 V=1" | ||||
|  | ||||
|  | ||||
|     - TEST_CMD="make check" | ||||
|     - MAKEFLAGS="-j3" | ||||
|   matrix: | ||||
|     - CONFIG="" | ||||
|     - CONFIG="--enable-debug --enable-debug-tcg --enable-trace-backends=log" | ||||
|     - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb" | ||||
|     - CONFIG="--enable-modules --disable-linux-user" | ||||
|     - CONFIG="--with-coroutine=ucontext --disable-linux-user" | ||||
|     - CONFIG="--with-coroutine=sigaltstack --disable-linux-user" | ||||
| git: | ||||
|   # we want to do this ourselves | ||||
|   submodules: false | ||||
|  | ||||
|  | ||||
| before_install: | ||||
|   - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update ; fi | ||||
|   - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install libffi gettext glib pixman ; fi | ||||
|   - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||
|   - git submodule update --init --recursive | ||||
| before_script: | ||||
|   - mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR} | ||||
|   - ${SRC_DIR}/configure ${CONFIG} || { cat config.log && exit 1; } | ||||
|   - ./configure ${CONFIG} | ||||
| script: | ||||
|   - make -j3 && ${TEST_CMD} | ||||
|  | ||||
|  | ||||
|   - make ${MAKEFLAGS} && ${TEST_CMD} | ||||
| matrix: | ||||
|   include: | ||||
|     - env: | ||||
|         - CONFIG="--disable-system" | ||||
|  | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--disable-user" | ||||
|  | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--enable-debug --enable-debug-tcg" | ||||
|  | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb --disable-user" | ||||
|  | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--enable-modules --disable-linux-user" | ||||
|  | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--with-coroutine=ucontext --disable-linux-user" | ||||
|  | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--with-coroutine=sigaltstack --disable-linux-user" | ||||
|  | ||||
|  | ||||
|     # Test out-of-tree builds | ||||
|     - env: | ||||
|         - CONFIG="--enable-debug --enable-debug-tcg" | ||||
|         - BUILD_DIR="out-of-tree/build/dir" SRC_DIR="../../.." | ||||
|  | ||||
|  | ||||
|     # Test with Clang for compile portability (Travis uses clang-5.0) | ||||
|     - env: | ||||
|         - CONFIG="--disable-system" | ||||
|     # Test with CLang for compile portability | ||||
|     - env: CONFIG="" | ||||
|       compiler: clang | ||||
|  | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--disable-user" | ||||
|       compiler: clang | ||||
|  | ||||
|  | ||||
|     # gprof/gcov are GCC features | ||||
|     - env: | ||||
|         - CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu" | ||||
|       after_success: | ||||
|         - ${SRC_DIR}/scripts/travis/coverage-summary.sh | ||||
|  | ||||
|  | ||||
|     - env: CONFIG="--enable-gprof --enable-gcov --disable-pie" | ||||
|       compiler: gcc | ||||
|     # We manually include builds which we disable "make check" for | ||||
|     - env: | ||||
|         - CONFIG="--enable-debug --enable-tcg-interpreter" | ||||
|         - TEST_CMD="" | ||||
|  | ||||
|  | ||||
|     # We don't need to exercise every backend with every front-end | ||||
|     - env: | ||||
|         - CONFIG="--enable-trace-backends=log,simple,syslog --disable-system" | ||||
|         - TEST_CMD="" | ||||
|  | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu" | ||||
|         - TEST_CMD="" | ||||
|  | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu" | ||||
|         - TEST_CMD="" | ||||
|  | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--disable-tcg" | ||||
|         - TEST_CMD="" | ||||
|  | ||||
|  | ||||
|     # MacOSX builds | ||||
|     - env: | ||||
|         - CONFIG="--target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu" | ||||
|     - env: CONFIG="--enable-debug --enable-tcg-interpreter" | ||||
|            TEST_CMD="" | ||||
|       compiler: gcc | ||||
|     - env: CONFIG="--enable-trace-backends=simple" | ||||
|            TEST_CMD="" | ||||
|       compiler: gcc | ||||
|     - env: CONFIG="--enable-trace-backends=ftrace" | ||||
|            TEST_CMD="" | ||||
|       compiler: gcc | ||||
|     - env: CONFIG="--enable-trace-backends=ust" | ||||
|            TEST_CMD="" | ||||
|       compiler: gcc | ||||
|     - env: CONFIG="--disable-tcg" | ||||
|            TEST_CMD="" | ||||
|       compiler: gcc | ||||
|     - env: CONFIG="" | ||||
|       os: osx | ||||
|       osx_image: xcode9.4 | ||||
|       compiler: clang | ||||
|  | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu" | ||||
|       os: osx | ||||
|       osx_image: xcode10 | ||||
|       compiler: clang | ||||
|  | ||||
|  | ||||
|     # Python builds | ||||
|     - env: | ||||
|         - CONFIG="--target-list=x86_64-softmmu" | ||||
|       language: python | ||||
|     # Plain Trusty System Build | ||||
|     - env: CONFIG="--disable-linux-user" | ||||
|       sudo: required | ||||
|       addons: | ||||
|       dist: trusty | ||||
|       compiler: gcc | ||||
|       before_install: | ||||
|         - sudo apt-get update -qq | ||||
|         - sudo apt-get build-dep -qq qemu | ||||
|         - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||
|         - git submodule update --init --recursive | ||||
|     # Plain Trusty Linux User Build | ||||
|     - env: CONFIG="--disable-system" | ||||
|       sudo: required | ||||
|       addons: | ||||
|       dist: trusty | ||||
|       compiler: gcc | ||||
|       before_install: | ||||
|         - sudo apt-get update -qq | ||||
|         - sudo apt-get build-dep -qq qemu | ||||
|         - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||
|         - git submodule update --init --recursive | ||||
|     # Trusty System build with latest stable clang & python 3.0 | ||||
|     - sudo: required | ||||
|       addons: | ||||
|       dist: trusty | ||||
|       language: generic | ||||
|       compiler: none | ||||
|       python: | ||||
|         - "3.4" | ||||
|  | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--target-list=x86_64-softmmu" | ||||
|       language: python | ||||
|         - "3.0" | ||||
|       env: | ||||
|         - COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9 | ||||
|         - CONFIG="--disable-linux-user --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3" | ||||
|       before_install: | ||||
|         - wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - | ||||
|         - sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main' | ||||
|         - sudo apt-get update -qq | ||||
|         - sudo apt-get install -qq -y clang-3.9 | ||||
|         - sudo apt-get build-dep -qq qemu | ||||
|         - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||
|         - git submodule update --init --recursive | ||||
|       before_script: | ||||
|         - ./configure ${CONFIG} || cat config.log | ||||
|     # Trusty Linux User build with latest stable clang & python 3.6 | ||||
|     - sudo: required | ||||
|       addons: | ||||
|       dist: trusty | ||||
|       language: generic | ||||
|       compiler: none | ||||
|       python: | ||||
|         - "3.6" | ||||
|  | ||||
|  | ||||
|     # Acceptance (Functional) tests | ||||
|     - env: | ||||
|         - CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu" | ||||
|         - TEST_CMD="make AVOCADO_SHOW=app check-acceptance" | ||||
|       addons: | ||||
|         apt: | ||||
|           packages: | ||||
|             - python3-pip | ||||
|             - python3.5-venv | ||||
|       env: | ||||
|         - COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9 | ||||
|         - CONFIG="--disable-system --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3" | ||||
|       before_install: | ||||
|         - wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - | ||||
|         - sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main' | ||||
|         - sudo apt-get update -qq | ||||
|         - sudo apt-get install -qq -y clang-3.9 | ||||
|         - sudo apt-get build-dep -qq qemu | ||||
|         - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||
|         - git submodule update --init --recursive | ||||
|       before_script: | ||||
|         - ./configure ${CONFIG} || cat config.log | ||||
|     # Using newer GCC with sanitizers | ||||
|     - addons: | ||||
|         apt: | ||||
|           update: true | ||||
|           sources: | ||||
|             # PPAs for newer toolchains | ||||
|             - ubuntu-toolchain-r-test | ||||
|           packages: | ||||
|             # Extra toolchains | ||||
|             - gcc-7 | ||||
|             - g++-7 | ||||
|             - gcc-5 | ||||
|             - g++-5 | ||||
|             # Build dependencies | ||||
|             - libaio-dev | ||||
|             - libattr1-dev | ||||
| @@ -229,19 +190,14 @@ matrix: | ||||
|             - libssh2-1-dev | ||||
|             - liburcu-dev | ||||
|             - libusb-1.0-0-dev | ||||
|             - libvte-2.91-dev | ||||
|             - libvte-2.90-dev | ||||
|             - sparse | ||||
|             - uuid-dev | ||||
|       language: generic | ||||
|       compiler: none | ||||
|       env: | ||||
|         - COMPILER_NAME=gcc CXX=g++-7 CC=gcc-7 | ||||
|         - CONFIG="--cc=gcc-7 --cxx=g++-7 --disable-pie --disable-linux-user" | ||||
|         - COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5 | ||||
|         - CONFIG="--cc=gcc-5 --cxx=g++-5 --disable-pie --disable-linux-user" | ||||
|         - TEST_CMD="" | ||||
|       before_script: | ||||
|         - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || { cat config.log && exit 1; } | ||||
|  | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--disable-system --disable-docs" | ||||
|         - TEST_CMD="make -j3 check-tcg V=1" | ||||
|         - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || cat config.log | ||||
|   | ||||
							
								
								
									
										17
									
								
								CODING_STYLE
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								CODING_STYLE
									
									
									
									
									
								
							| @@ -124,23 +124,6 @@ We use traditional C-style /* */ comments and avoid // comments. | ||||
| Rationale: The // form is valid in C99, so this is purely a matter of | ||||
| consistency of style. The checkpatch script will warn you about this. | ||||
|  | ||||
| Multiline comment blocks should have a row of stars on the left, | ||||
| and the initial /* and terminating */ both on their own lines: | ||||
|     /* | ||||
|      * like | ||||
|      * this | ||||
|      */ | ||||
| This is the same format required by the Linux kernel coding style. | ||||
|  | ||||
| (Some of the existing comments in the codebase use the GNU Coding | ||||
| Standards form which does not have stars on the left, or other | ||||
| variations; avoid these when writing new comments, but don't worry | ||||
| about converting to the preferred form unless you're editing that | ||||
| comment anyway.) | ||||
|  | ||||
| Rationale: Consistency, and ease of visually picking out a multiline | ||||
| comment from the surrounding code. | ||||
|  | ||||
| 8. trace-events style | ||||
|  | ||||
| 8.1 0x prefix | ||||
|   | ||||
							
								
								
									
										22
									
								
								COPYING.LIB
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								COPYING.LIB
									
									
									
									
									
								
							| @@ -1,8 +1,8 @@ | ||||
|                   GNU LESSER GENERAL PUBLIC LICENSE | ||||
|                        Version 2.1, February 1999 | ||||
| 		  GNU LESSER GENERAL PUBLIC LICENSE | ||||
| 		       Version 2.1, February 1999 | ||||
|  | ||||
|  Copyright (C) 1991, 1999 Free Software Foundation, Inc. | ||||
|  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA | ||||
| 	51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||
|  Everyone is permitted to copy and distribute verbatim copies | ||||
|  of this license document, but changing it is not allowed. | ||||
|  | ||||
| @@ -10,7 +10,7 @@ | ||||
|  as the successor of the GNU Library Public License, version 2, hence | ||||
|  the version number 2.1.] | ||||
|  | ||||
|                             Preamble | ||||
| 			    Preamble | ||||
|  | ||||
|   The licenses for most software are designed to take away your | ||||
| freedom to share and change it.  By contrast, the GNU General Public | ||||
| @@ -112,7 +112,7 @@ modification follow.  Pay close attention to the difference between a | ||||
| former contains code derived from the library, whereas the latter must | ||||
| be combined with the library in order to run. | ||||
|  | ||||
|                   GNU LESSER GENERAL PUBLIC LICENSE | ||||
| 		  GNU LESSER GENERAL PUBLIC LICENSE | ||||
|    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||||
|  | ||||
|   0. This License Agreement applies to any software library or other | ||||
| @@ -146,7 +146,7 @@ such a program is covered only if its contents constitute a work based | ||||
| on the Library (independent of the use of the Library in a tool for | ||||
| writing it).  Whether that is true depends on what the Library does | ||||
| and what the program that uses the Library does. | ||||
|  | ||||
|    | ||||
|   1. You may copy and distribute verbatim copies of the Library's | ||||
| complete source code as you receive it, in any medium, provided that | ||||
| you conspicuously and appropriately publish on each copy an | ||||
| @@ -432,7 +432,7 @@ decision will be guided by the two goals of preserving the free status | ||||
| of all derivatives of our free software and of promoting the sharing | ||||
| and reuse of software generally. | ||||
|  | ||||
|                             NO WARRANTY | ||||
| 			    NO WARRANTY | ||||
|  | ||||
|   15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO | ||||
| WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. | ||||
| @@ -455,7 +455,7 @@ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF | ||||
| SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | ||||
| DAMAGES. | ||||
|  | ||||
|                      END OF TERMS AND CONDITIONS | ||||
| 		     END OF TERMS AND CONDITIONS | ||||
|  | ||||
|            How to Apply These Terms to Your New Libraries | ||||
|  | ||||
| @@ -476,7 +476,7 @@ convey the exclusion of warranty; and each file should have at least the | ||||
|     This library is free software; you can redistribute it and/or | ||||
|     modify it under the terms of the GNU Lesser General Public | ||||
|     License as published by the Free Software Foundation; either | ||||
|     version 2.1 of the License, or (at your option) any later version. | ||||
|     version 2 of the License, or (at your option) any later version. | ||||
|  | ||||
|     This library is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| @@ -485,7 +485,7 @@ convey the exclusion of warranty; and each file should have at least the | ||||
|  | ||||
|     You should have received a copy of the GNU Lesser General Public | ||||
|     License along with this library; if not, write to the Free Software | ||||
|     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA | ||||
|     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||
|  | ||||
| Also add information on how to contact you by electronic and paper mail. | ||||
|  | ||||
| @@ -500,3 +500,5 @@ necessary.  Here is a sample; alter the names: | ||||
|   Ty Coon, President of Vice | ||||
|  | ||||
| That's all there is to it! | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										270
									
								
								COPYING.PYTHON
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										270
									
								
								COPYING.PYTHON
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,270 @@ | ||||
| A. HISTORY OF THE SOFTWARE | ||||
| ========================== | ||||
|  | ||||
| Python was created in the early 1990s by Guido van Rossum at Stichting | ||||
| Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands | ||||
| as a successor of a language called ABC.  Guido remains Python's | ||||
| principal author, although it includes many contributions from others. | ||||
|  | ||||
| In 1995, Guido continued his work on Python at the Corporation for | ||||
| National Research Initiatives (CNRI, see http://www.cnri.reston.va.us) | ||||
| in Reston, Virginia where he released several versions of the | ||||
| software. | ||||
|  | ||||
| In May 2000, Guido and the Python core development team moved to | ||||
| BeOpen.com to form the BeOpen PythonLabs team.  In October of the same | ||||
| year, the PythonLabs team moved to Digital Creations (now Zope | ||||
| Corporation, see http://www.zope.com).  In 2001, the Python Software | ||||
| Foundation (PSF, see http://www.python.org/psf/) was formed, a | ||||
| non-profit organization created specifically to own Python-related | ||||
| Intellectual Property.  Zope Corporation is a sponsoring member of | ||||
| the PSF. | ||||
|  | ||||
| All Python releases are Open Source (see http://www.opensource.org for | ||||
| the Open Source Definition).  Historically, most, but not all, Python | ||||
| releases have also been GPL-compatible; the table below summarizes | ||||
| the various releases. | ||||
|  | ||||
|     Release         Derived     Year        Owner       GPL- | ||||
|                     from                                compatible? (1) | ||||
|  | ||||
|     0.9.0 thru 1.2              1991-1995   CWI         yes | ||||
|     1.3 thru 1.5.2  1.2         1995-1999   CNRI        yes | ||||
|     1.6             1.5.2       2000        CNRI        no | ||||
|     2.0             1.6         2000        BeOpen.com  no | ||||
|     1.6.1           1.6         2001        CNRI        yes (2) | ||||
|     2.1             2.0+1.6.1   2001        PSF         no | ||||
|     2.0.1           2.0+1.6.1   2001        PSF         yes | ||||
|     2.1.1           2.1+2.0.1   2001        PSF         yes | ||||
|     2.2             2.1.1       2001        PSF         yes | ||||
|     2.1.2           2.1.1       2002        PSF         yes | ||||
|     2.1.3           2.1.2       2002        PSF         yes | ||||
|     2.2.1           2.2         2002        PSF         yes | ||||
|     2.2.2           2.2.1       2002        PSF         yes | ||||
|     2.2.3           2.2.2       2003        PSF         yes | ||||
|     2.3             2.2.2       2002-2003   PSF         yes | ||||
|     2.3.1           2.3         2002-2003   PSF         yes | ||||
|     2.3.2           2.3.1       2002-2003   PSF         yes | ||||
|     2.3.3           2.3.2       2002-2003   PSF         yes | ||||
|     2.3.4           2.3.3       2004        PSF         yes | ||||
|     2.3.5           2.3.4       2005        PSF         yes | ||||
|     2.4             2.3         2004        PSF         yes | ||||
|     2.4.1           2.4         2005        PSF         yes | ||||
|     2.4.2           2.4.1       2005        PSF         yes | ||||
|     2.4.3           2.4.2       2006        PSF         yes | ||||
|     2.5             2.4         2006        PSF         yes | ||||
|     2.7             2.6         2010        PSF         yes | ||||
|  | ||||
| Footnotes: | ||||
|  | ||||
| (1) GPL-compatible doesn't mean that we're distributing Python under | ||||
|     the GPL.  All Python licenses, unlike the GPL, let you distribute | ||||
|     a modified version without making your changes open source.  The | ||||
|     GPL-compatible licenses make it possible to combine Python with | ||||
|     other software that is released under the GPL; the others don't. | ||||
|  | ||||
| (2) According to Richard Stallman, 1.6.1 is not GPL-compatible, | ||||
|     because its license has a choice of law clause.  According to | ||||
|     CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 | ||||
|     is "not incompatible" with the GPL. | ||||
|  | ||||
| Thanks to the many outside volunteers who have worked under Guido's | ||||
| direction to make these releases possible. | ||||
|  | ||||
|  | ||||
| B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON | ||||
| =============================================================== | ||||
|  | ||||
| PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 | ||||
| -------------------------------------------- | ||||
|  | ||||
| 1. This LICENSE AGREEMENT is between the Python Software Foundation | ||||
| ("PSF"), and the Individual or Organization ("Licensee") accessing and | ||||
| otherwise using this software ("Python") in source or binary form and | ||||
| its associated documentation. | ||||
|  | ||||
| 2. Subject to the terms and conditions of this License Agreement, PSF | ||||
| hereby grants Licensee a nonexclusive, royalty-free, world-wide | ||||
| license to reproduce, analyze, test, perform and/or display publicly, | ||||
| prepare derivative works, distribute, and otherwise use Python | ||||
| alone or in any derivative version, provided, however, that PSF's | ||||
| License Agreement and PSF's notice of copyright, i.e., "Copyright (c) | ||||
| 2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation; All Rights | ||||
| Reserved" are retained in Python alone or in any derivative version  | ||||
| prepared by Licensee. | ||||
|  | ||||
| 3. In the event Licensee prepares a derivative work that is based on | ||||
| or incorporates Python or any part thereof, and wants to make | ||||
| the derivative work available to others as provided herein, then | ||||
| Licensee hereby agrees to include in any such work a brief summary of | ||||
| the changes made to Python. | ||||
|  | ||||
| 4. PSF is making Python available to Licensee on an "AS IS" | ||||
| basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR | ||||
| IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND | ||||
| DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS | ||||
| FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT | ||||
| INFRINGE ANY THIRD PARTY RIGHTS. | ||||
|  | ||||
| 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON | ||||
| FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS | ||||
| A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, | ||||
| OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. | ||||
|  | ||||
| 6. This License Agreement will automatically terminate upon a material | ||||
| breach of its terms and conditions. | ||||
|  | ||||
| 7. Nothing in this License Agreement shall be deemed to create any | ||||
| relationship of agency, partnership, or joint venture between PSF and | ||||
| Licensee.  This License Agreement does not grant permission to use PSF | ||||
| trademarks or trade name in a trademark sense to endorse or promote | ||||
| products or services of Licensee, or any third party. | ||||
|  | ||||
| 8. By copying, installing or otherwise using Python, Licensee | ||||
| agrees to be bound by the terms and conditions of this License | ||||
| Agreement. | ||||
|  | ||||
|  | ||||
| BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 | ||||
| ------------------------------------------- | ||||
|  | ||||
| BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 | ||||
|  | ||||
| 1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an | ||||
| office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the | ||||
| Individual or Organization ("Licensee") accessing and otherwise using | ||||
| this software in source or binary form and its associated | ||||
| documentation ("the Software"). | ||||
|  | ||||
| 2. Subject to the terms and conditions of this BeOpen Python License | ||||
| Agreement, BeOpen hereby grants Licensee a non-exclusive, | ||||
| royalty-free, world-wide license to reproduce, analyze, test, perform | ||||
| and/or display publicly, prepare derivative works, distribute, and | ||||
| otherwise use the Software alone or in any derivative version, | ||||
| provided, however, that the BeOpen Python License is retained in the | ||||
| Software, alone or in any derivative version prepared by Licensee. | ||||
|  | ||||
| 3. BeOpen is making the Software available to Licensee on an "AS IS" | ||||
| basis.  BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR | ||||
| IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND | ||||
| DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS | ||||
| FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT | ||||
| INFRINGE ANY THIRD PARTY RIGHTS. | ||||
|  | ||||
| 4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE | ||||
| SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS | ||||
| AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY | ||||
| DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. | ||||
|  | ||||
| 5. This License Agreement will automatically terminate upon a material | ||||
| breach of its terms and conditions. | ||||
|  | ||||
| 6. This License Agreement shall be governed by and interpreted in all | ||||
| respects by the law of the State of California, excluding conflict of | ||||
| law provisions.  Nothing in this License Agreement shall be deemed to | ||||
| create any relationship of agency, partnership, or joint venture | ||||
| between BeOpen and Licensee.  This License Agreement does not grant | ||||
| permission to use BeOpen trademarks or trade names in a trademark | ||||
| sense to endorse or promote products or services of Licensee, or any | ||||
| third party.  As an exception, the "BeOpen Python" logos available at | ||||
| http://www.pythonlabs.com/logos.html may be used according to the | ||||
| permissions granted on that web page. | ||||
|  | ||||
| 7. By copying, installing or otherwise using the software, Licensee | ||||
| agrees to be bound by the terms and conditions of this License | ||||
| Agreement. | ||||
|  | ||||
|  | ||||
| CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 | ||||
| --------------------------------------- | ||||
|  | ||||
| 1. This LICENSE AGREEMENT is between the Corporation for National | ||||
| Research Initiatives, having an office at 1895 Preston White Drive, | ||||
| Reston, VA 20191 ("CNRI"), and the Individual or Organization | ||||
| ("Licensee") accessing and otherwise using Python 1.6.1 software in | ||||
| source or binary form and its associated documentation. | ||||
|  | ||||
| 2. Subject to the terms and conditions of this License Agreement, CNRI | ||||
| hereby grants Licensee a nonexclusive, royalty-free, world-wide | ||||
| license to reproduce, analyze, test, perform and/or display publicly, | ||||
| prepare derivative works, distribute, and otherwise use Python 1.6.1 | ||||
| alone or in any derivative version, provided, however, that CNRI's | ||||
| License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) | ||||
| 1995-2001 Corporation for National Research Initiatives; All Rights | ||||
| Reserved" are retained in Python 1.6.1 alone or in any derivative | ||||
| version prepared by Licensee.  Alternately, in lieu of CNRI's License | ||||
| Agreement, Licensee may substitute the following text (omitting the | ||||
| quotes): "Python 1.6.1 is made available subject to the terms and | ||||
| conditions in CNRI's License Agreement.  This Agreement together with | ||||
| Python 1.6.1 may be located on the Internet using the following | ||||
| unique, persistent identifier (known as a handle): 1895.22/1013.  This | ||||
| Agreement may also be obtained from a proxy server on the Internet | ||||
| using the following URL: http://hdl.handle.net/1895.22/1013". | ||||
|  | ||||
| 3. In the event Licensee prepares a derivative work that is based on | ||||
| or incorporates Python 1.6.1 or any part thereof, and wants to make | ||||
| the derivative work available to others as provided herein, then | ||||
| Licensee hereby agrees to include in any such work a brief summary of | ||||
| the changes made to Python 1.6.1. | ||||
|  | ||||
| 4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" | ||||
| basis.  CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR | ||||
| IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND | ||||
| DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS | ||||
| FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT | ||||
| INFRINGE ANY THIRD PARTY RIGHTS. | ||||
|  | ||||
| 5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON | ||||
| 1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS | ||||
| A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, | ||||
| OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. | ||||
|  | ||||
| 6. This License Agreement will automatically terminate upon a material | ||||
| breach of its terms and conditions. | ||||
|  | ||||
| 7. This License Agreement shall be governed by the federal | ||||
| intellectual property law of the United States, including without | ||||
| limitation the federal copyright law, and, to the extent such | ||||
| U.S. federal law does not apply, by the law of the Commonwealth of | ||||
| Virginia, excluding Virginia's conflict of law provisions. | ||||
| Notwithstanding the foregoing, with regard to derivative works based | ||||
| on Python 1.6.1 that incorporate non-separable material that was | ||||
| previously distributed under the GNU General Public License (GPL), the | ||||
| law of the Commonwealth of Virginia shall govern this License | ||||
| Agreement only as to issues arising under or with respect to | ||||
| Paragraphs 4, 5, and 7 of this License Agreement.  Nothing in this | ||||
| License Agreement shall be deemed to create any relationship of | ||||
| agency, partnership, or joint venture between CNRI and Licensee.  This | ||||
| License Agreement does not grant permission to use CNRI trademarks or | ||||
| trade name in a trademark sense to endorse or promote products or | ||||
| services of Licensee, or any third party. | ||||
|  | ||||
| 8. By clicking on the "ACCEPT" button where indicated, or by copying, | ||||
| installing or otherwise using Python 1.6.1, Licensee agrees to be | ||||
| bound by the terms and conditions of this License Agreement. | ||||
|  | ||||
|         ACCEPT | ||||
|  | ||||
|  | ||||
| CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 | ||||
| -------------------------------------------------- | ||||
|  | ||||
| Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, | ||||
| The Netherlands.  All rights reserved. | ||||
|  | ||||
| Permission to use, copy, modify, and distribute this software and its | ||||
| documentation for any purpose and without fee is hereby granted, | ||||
| provided that the above copyright notice appear in all copies and that | ||||
| both that copyright notice and this permission notice appear in | ||||
| supporting documentation, and that the name of Stichting Mathematisch | ||||
| Centrum or CWI not be used in advertising or publicity pertaining to | ||||
| distribution of the software without specific, written prior | ||||
| permission. | ||||
|  | ||||
| STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO | ||||
| THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | ||||
| FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE | ||||
| FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | ||||
| OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
							
								
								
									
										9
									
								
								HACKING
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								HACKING
									
									
									
									
									
								
							| @@ -118,15 +118,6 @@ Please note that g_malloc will exit on allocation failure, so there | ||||
| is no need to test for failure (as you would have to with malloc). | ||||
| Calling g_malloc with a zero size is valid and will return NULL. | ||||
|  | ||||
| Prefer g_new(T, n) instead of g_malloc(sizeof(T) * n) for the following | ||||
| reasons: | ||||
|  | ||||
|   a. It catches multiplication overflowing size_t; | ||||
|   b. It returns T * instead of void *, letting compiler catch more type | ||||
|      errors. | ||||
|  | ||||
| Declarations like T *v = g_malloc(sizeof(*v)) are acceptable, though. | ||||
|  | ||||
| Memory allocated by qemu_memalign or qemu_blockalign must be freed with | ||||
| qemu_vfree, since breaking this will cause problems on Win32. | ||||
|  | ||||
|   | ||||
							
								
								
									
										692
									
								
								MAINTAINERS
									
									
									
									
									
								
							
							
						
						
									
										692
									
								
								MAINTAINERS
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										325
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										325
									
								
								Makefile
									
									
									
									
									
								
							| @@ -20,6 +20,8 @@ ifneq ($(wildcard config-host.mak),) | ||||
| all: | ||||
| include config-host.mak | ||||
|  | ||||
| PYTHON_UTF8 = LC_ALL= LANG=C LC_CTYPE=en_US.UTF-8 $(PYTHON) | ||||
|  | ||||
| git-submodule-update: | ||||
|  | ||||
| .PHONY: git-submodule-update | ||||
| @@ -67,7 +69,7 @@ CONFIG_ALL=y | ||||
| -include config-all-devices.mak | ||||
| -include config-all-disas.mak | ||||
|  | ||||
| config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios $(SRC_PATH)/VERSION | ||||
| config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios | ||||
| 	@echo $@ is out-of-date, running configure | ||||
| 	@# TODO: The next lines include code which supports a smooth | ||||
| 	@# transition from old configurations without config.status. | ||||
| @@ -88,26 +90,78 @@ endif | ||||
| include $(SRC_PATH)/rules.mak | ||||
|  | ||||
| GENERATED_FILES = qemu-version.h config-host.h qemu-options.def | ||||
|  | ||||
| #see Makefile.objs for the definition of QAPI_MODULES | ||||
| GENERATED_QAPI_FILES = qapi/qapi-builtin-types.h qapi/qapi-builtin-types.c | ||||
| GENERATED_QAPI_FILES += qapi/qapi-types.h qapi/qapi-types.c | ||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-types-%.h) | ||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-types-%.c) | ||||
| GENERATED_QAPI_FILES += qapi/qapi-builtin-visit.h qapi/qapi-builtin-visit.c | ||||
| GENERATED_QAPI_FILES += qapi/qapi-visit.h qapi/qapi-visit.c | ||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.h) | ||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.c) | ||||
| GENERATED_QAPI_FILES += qapi/qapi-commands.h qapi/qapi-commands.c | ||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.h) | ||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.c) | ||||
| GENERATED_QAPI_FILES += qapi/qapi-events.h qapi/qapi-events.c | ||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.h) | ||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.c) | ||||
| GENERATED_QAPI_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h | ||||
| GENERATED_QAPI_FILES += qapi/qapi-doc.texi | ||||
|  | ||||
| GENERATED_FILES += $(GENERATED_QAPI_FILES) | ||||
| GENERATED_FILES += qapi/qapi-builtin-types.h qapi/qapi-builtin-types.c | ||||
| GENERATED_FILES += qapi/qapi-types.h qapi/qapi-types.c | ||||
| GENERATED_FILES += qapi/qapi-types-block-core.h qapi/qapi-types-block-core.c | ||||
| GENERATED_FILES += qapi/qapi-types-block.h qapi/qapi-types-block.c | ||||
| GENERATED_FILES += qapi/qapi-types-char.h qapi/qapi-types-char.c | ||||
| GENERATED_FILES += qapi/qapi-types-common.h qapi/qapi-types-common.c | ||||
| GENERATED_FILES += qapi/qapi-types-crypto.h qapi/qapi-types-crypto.c | ||||
| GENERATED_FILES += qapi/qapi-types-introspect.h qapi/qapi-types-introspect.c | ||||
| GENERATED_FILES += qapi/qapi-types-migration.h qapi/qapi-types-migration.c | ||||
| GENERATED_FILES += qapi/qapi-types-misc.h qapi/qapi-types-misc.c | ||||
| GENERATED_FILES += qapi/qapi-types-net.h qapi/qapi-types-net.c | ||||
| GENERATED_FILES += qapi/qapi-types-rocker.h qapi/qapi-types-rocker.c | ||||
| GENERATED_FILES += qapi/qapi-types-run-state.h qapi/qapi-types-run-state.c | ||||
| GENERATED_FILES += qapi/qapi-types-sockets.h qapi/qapi-types-sockets.c | ||||
| GENERATED_FILES += qapi/qapi-types-tpm.h qapi/qapi-types-tpm.c | ||||
| GENERATED_FILES += qapi/qapi-types-trace.h qapi/qapi-types-trace.c | ||||
| GENERATED_FILES += qapi/qapi-types-transaction.h qapi/qapi-types-transaction.c | ||||
| GENERATED_FILES += qapi/qapi-types-ui.h qapi/qapi-types-ui.c | ||||
| GENERATED_FILES += qapi/qapi-builtin-visit.h qapi/qapi-builtin-visit.c | ||||
| GENERATED_FILES += qapi/qapi-visit.h qapi/qapi-visit.c | ||||
| GENERATED_FILES += qapi/qapi-visit-block-core.h qapi/qapi-visit-block-core.c | ||||
| GENERATED_FILES += qapi/qapi-visit-block.h qapi/qapi-visit-block.c | ||||
| GENERATED_FILES += qapi/qapi-visit-char.h qapi/qapi-visit-char.c | ||||
| GENERATED_FILES += qapi/qapi-visit-common.h qapi/qapi-visit-common.c | ||||
| GENERATED_FILES += qapi/qapi-visit-crypto.h qapi/qapi-visit-crypto.c | ||||
| GENERATED_FILES += qapi/qapi-visit-introspect.h qapi/qapi-visit-introspect.c | ||||
| GENERATED_FILES += qapi/qapi-visit-migration.h qapi/qapi-visit-migration.c | ||||
| GENERATED_FILES += qapi/qapi-visit-misc.h qapi/qapi-visit-misc.c | ||||
| GENERATED_FILES += qapi/qapi-visit-net.h qapi/qapi-visit-net.c | ||||
| GENERATED_FILES += qapi/qapi-visit-rocker.h qapi/qapi-visit-rocker.c | ||||
| GENERATED_FILES += qapi/qapi-visit-run-state.h qapi/qapi-visit-run-state.c | ||||
| GENERATED_FILES += qapi/qapi-visit-sockets.h qapi/qapi-visit-sockets.c | ||||
| GENERATED_FILES += qapi/qapi-visit-tpm.h qapi/qapi-visit-tpm.c | ||||
| GENERATED_FILES += qapi/qapi-visit-trace.h qapi/qapi-visit-trace.c | ||||
| GENERATED_FILES += qapi/qapi-visit-transaction.h qapi/qapi-visit-transaction.c | ||||
| GENERATED_FILES += qapi/qapi-visit-ui.h qapi/qapi-visit-ui.c | ||||
| GENERATED_FILES += qapi/qapi-commands.h qapi/qapi-commands.c | ||||
| GENERATED_FILES += qapi/qapi-commands-block-core.h qapi/qapi-commands-block-core.c | ||||
| GENERATED_FILES += qapi/qapi-commands-block.h qapi/qapi-commands-block.c | ||||
| GENERATED_FILES += qapi/qapi-commands-char.h qapi/qapi-commands-char.c | ||||
| GENERATED_FILES += qapi/qapi-commands-common.h qapi/qapi-commands-common.c | ||||
| GENERATED_FILES += qapi/qapi-commands-crypto.h qapi/qapi-commands-crypto.c | ||||
| GENERATED_FILES += qapi/qapi-commands-introspect.h qapi/qapi-commands-introspect.c | ||||
| GENERATED_FILES += qapi/qapi-commands-migration.h qapi/qapi-commands-migration.c | ||||
| GENERATED_FILES += qapi/qapi-commands-misc.h qapi/qapi-commands-misc.c | ||||
| GENERATED_FILES += qapi/qapi-commands-net.h qapi/qapi-commands-net.c | ||||
| GENERATED_FILES += qapi/qapi-commands-rocker.h qapi/qapi-commands-rocker.c | ||||
| GENERATED_FILES += qapi/qapi-commands-run-state.h qapi/qapi-commands-run-state.c | ||||
| GENERATED_FILES += qapi/qapi-commands-sockets.h qapi/qapi-commands-sockets.c | ||||
| GENERATED_FILES += qapi/qapi-commands-tpm.h qapi/qapi-commands-tpm.c | ||||
| GENERATED_FILES += qapi/qapi-commands-trace.h qapi/qapi-commands-trace.c | ||||
| GENERATED_FILES += qapi/qapi-commands-transaction.h qapi/qapi-commands-transaction.c | ||||
| GENERATED_FILES += qapi/qapi-commands-ui.h qapi/qapi-commands-ui.c | ||||
| GENERATED_FILES += qapi/qapi-events.h qapi/qapi-events.c | ||||
| GENERATED_FILES += qapi/qapi-events-block-core.h qapi/qapi-events-block-core.c | ||||
| GENERATED_FILES += qapi/qapi-events-block.h qapi/qapi-events-block.c | ||||
| GENERATED_FILES += qapi/qapi-events-char.h qapi/qapi-events-char.c | ||||
| GENERATED_FILES += qapi/qapi-events-common.h qapi/qapi-events-common.c | ||||
| GENERATED_FILES += qapi/qapi-events-crypto.h qapi/qapi-events-crypto.c | ||||
| GENERATED_FILES += qapi/qapi-events-introspect.h qapi/qapi-events-introspect.c | ||||
| GENERATED_FILES += qapi/qapi-events-migration.h qapi/qapi-events-migration.c | ||||
| GENERATED_FILES += qapi/qapi-events-misc.h qapi/qapi-events-misc.c | ||||
| GENERATED_FILES += qapi/qapi-events-net.h qapi/qapi-events-net.c | ||||
| GENERATED_FILES += qapi/qapi-events-rocker.h qapi/qapi-events-rocker.c | ||||
| GENERATED_FILES += qapi/qapi-events-run-state.h qapi/qapi-events-run-state.c | ||||
| GENERATED_FILES += qapi/qapi-events-sockets.h qapi/qapi-events-sockets.c | ||||
| GENERATED_FILES += qapi/qapi-events-tpm.h qapi/qapi-events-tpm.c | ||||
| GENERATED_FILES += qapi/qapi-events-trace.h qapi/qapi-events-trace.c | ||||
| GENERATED_FILES += qapi/qapi-events-transaction.h qapi/qapi-events-transaction.c | ||||
| GENERATED_FILES += qapi/qapi-events-ui.h qapi/qapi-events-ui.c | ||||
| GENERATED_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h | ||||
| GENERATED_FILES += qapi/qapi-doc.texi | ||||
|  | ||||
| GENERATED_FILES += trace/generated-tcg-tracers.h | ||||
|  | ||||
| @@ -145,7 +199,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") | ||||
|  | ||||
| %/trace.h: %/trace.h-timestamp | ||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||
| %/trace.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | ||||
| %/trace.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) | ||||
| 	$(call quiet-command,$(TRACETOOL) \ | ||||
| 		--group=$(call trace-group-name,$@) \ | ||||
| 		--format=h \ | ||||
| @@ -154,7 +208,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") | ||||
|  | ||||
| %/trace.c: %/trace.c-timestamp | ||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||
| %/trace.c-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | ||||
| %/trace.c-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) | ||||
| 	$(call quiet-command,$(TRACETOOL) \ | ||||
| 		--group=$(call trace-group-name,$@) \ | ||||
| 		--format=c \ | ||||
| @@ -163,7 +217,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") | ||||
|  | ||||
| %/trace-ust.h: %/trace-ust.h-timestamp | ||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||
| %/trace-ust.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | ||||
| %/trace-ust.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) | ||||
| 	$(call quiet-command,$(TRACETOOL) \ | ||||
| 		--group=$(call trace-group-name,$@) \ | ||||
| 		--format=ust-events-h \ | ||||
| @@ -187,7 +241,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") | ||||
|  | ||||
| trace-root.h: trace-root.h-timestamp | ||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||
| trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | ||||
| trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) | ||||
| 	$(call quiet-command,$(TRACETOOL) \ | ||||
| 		--group=root \ | ||||
| 		--format=h \ | ||||
| @@ -196,7 +250,7 @@ trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/con | ||||
|  | ||||
| trace-root.c: trace-root.c-timestamp | ||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||
| trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | ||||
| trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) | ||||
| 	$(call quiet-command,$(TRACETOOL) \ | ||||
| 		--group=root \ | ||||
| 		--format=c \ | ||||
| @@ -205,7 +259,7 @@ trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/con | ||||
|  | ||||
| trace-ust-root.h: trace-ust-root.h-timestamp | ||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||
| trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | ||||
| trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) | ||||
| 	$(call quiet-command,$(TRACETOOL) \ | ||||
| 		--group=root \ | ||||
| 		--format=ust-events-h \ | ||||
| @@ -214,7 +268,7 @@ trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR) | ||||
|  | ||||
| trace-ust-all.h: trace-ust-all.h-timestamp | ||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||
| trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/config-host.mak | ||||
| trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y) | ||||
| 	$(call quiet-command,$(TRACETOOL) \ | ||||
| 		--group=all \ | ||||
| 		--format=ust-events-h \ | ||||
| @@ -223,7 +277,7 @@ trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/con | ||||
|  | ||||
| trace-ust-all.c: trace-ust-all.c-timestamp | ||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||
| trace-ust-all.c-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/config-host.mak | ||||
| trace-ust-all.c-timestamp: $(trace-events-files) $(tracetool-y) | ||||
| 	$(call quiet-command,$(TRACETOOL) \ | ||||
| 		--group=all \ | ||||
| 		--format=ust-events-c \ | ||||
| @@ -264,7 +318,6 @@ KEYCODEMAP_FILES = \ | ||||
| 		 ui/input-keymap-xorgkbd-to-qcode.c \ | ||||
| 		 ui/input-keymap-xorgxquartz-to-qcode.c \ | ||||
| 		 ui/input-keymap-xorgxwin-to-qcode.c \ | ||||
| 		 ui/input-keymap-osx-to-qcode.c \ | ||||
| 		 $(NULL) | ||||
|  | ||||
| GENERATED_FILES += $(KEYCODEMAP_FILES) | ||||
| @@ -294,20 +347,16 @@ $(call set-vpath, $(SRC_PATH)) | ||||
|  | ||||
| LIBS+=-lz $(LIBS_TOOLS) | ||||
|  | ||||
| HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) = qemu-bridge-helper$(EXESUF) | ||||
| HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF) | ||||
|  | ||||
| ifdef BUILD_DOCS | ||||
| DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8 | ||||
| DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7 | ||||
| DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7 | ||||
| DOCS+=docs/qemu-block-drivers.7 | ||||
| DOCS+=docs/qemu-cpu-models.7 | ||||
| ifdef CONFIG_VIRTFS | ||||
| DOCS+=fsdev/virtfs-proxy-helper.1 | ||||
| endif | ||||
| ifdef CONFIG_TRACE_SYSTEMTAP | ||||
| DOCS+=scripts/qemu-trace-stap.1 | ||||
| endif | ||||
| else | ||||
| DOCS= | ||||
| endif | ||||
| @@ -362,10 +411,8 @@ dummy := $(call unnest-vars,, \ | ||||
|                 chardev-obj-y \ | ||||
|                 util-obj-y \ | ||||
|                 qga-obj-y \ | ||||
|                 elf2dmp-obj-y \ | ||||
|                 ivshmem-client-obj-y \ | ||||
|                 ivshmem-server-obj-y \ | ||||
|                 rdmacm-mux-obj-y \ | ||||
|                 libvhost-user-obj-y \ | ||||
|                 vhost-user-scsi-obj-y \ | ||||
|                 vhost-user-blk-obj-y \ | ||||
| @@ -382,8 +429,7 @@ dummy := $(call unnest-vars,, \ | ||||
|                 ui-obj-m \ | ||||
|                 audio-obj-y \ | ||||
|                 audio-obj-m \ | ||||
|                 trace-obj-y \ | ||||
|                 slirp-obj-y) | ||||
|                 trace-obj-y) | ||||
|  | ||||
| include $(SRC_PATH)/tests/Makefile.include | ||||
|  | ||||
| @@ -392,23 +438,21 @@ all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules | ||||
| qemu-version.h: FORCE | ||||
| 	$(call quiet-command, \ | ||||
| 		(cd $(SRC_PATH); \ | ||||
| 		printf '#define QEMU_PKGVERSION '; \ | ||||
| 		if test -n "$(PKGVERSION)"; then \ | ||||
| 			pkgvers="$(PKGVERSION)"; \ | ||||
| 			printf '"$(PKGVERSION)"\n'; \ | ||||
| 		else \ | ||||
| 			if test -d .git; then \ | ||||
| 				pkgvers=$$(git describe --match 'v*' 2>/dev/null | tr -d '\n');\ | ||||
| 				printf '" ('; \ | ||||
| 				git describe --match 'v*' 2>/dev/null | tr -d '\n'; \ | ||||
| 				if ! git diff-index --quiet HEAD &>/dev/null; then \ | ||||
| 					pkgvers="$${pkgvers}-dirty"; \ | ||||
| 					printf -- '-dirty'; \ | ||||
| 				fi; \ | ||||
| 				printf ')"\n'; \ | ||||
| 			else \ | ||||
| 				printf '""\n'; \ | ||||
| 			fi; \ | ||||
| 		fi; \ | ||||
| 		printf "#define QEMU_PKGVERSION \"$${pkgvers}\"\n"; \ | ||||
| 		if test -n "$${pkgvers}"; then \ | ||||
| 			printf '#define QEMU_FULL_VERSION QEMU_VERSION " (" QEMU_PKGVERSION ")"\n'; \ | ||||
| 		else \ | ||||
| 			printf '#define QEMU_FULL_VERSION QEMU_VERSION\n'; \ | ||||
| 		fi; \ | ||||
| 		) > $@.tmp) | ||||
| 		fi) > $@.tmp) | ||||
| 	$(call quiet-command, if ! cmp -s $@ $@.tmp; then \ | ||||
| 	  mv $@.tmp $@; \ | ||||
| 	 else \ | ||||
| @@ -439,7 +483,7 @@ subdir-dtc: .git-submodule-status dtc/libfdt dtc/tests | ||||
| 	$(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt/libfdt.a,) | ||||
|  | ||||
| dtc/%: .git-submodule-status | ||||
| 	@mkdir -p $@ | ||||
| 	mkdir -p $@ | ||||
|  | ||||
| # Overriding CFLAGS causes us to lose defines added in the sub-makefile. | ||||
| # Not overriding CFLAGS leads to mis-matches between compilation modes. | ||||
| @@ -456,7 +500,7 @@ CAP_CFLAGS += -DCAPSTONE_HAS_X86 | ||||
| subdir-capstone: .git-submodule-status | ||||
| 	$(call quiet-command,$(MAKE) -C $(SRC_PATH)/capstone CAPSTONE_SHARED=no BUILDDIR="$(BUILD_DIR)/capstone" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(CAP_CFLAGS)" $(SUBDIR_MAKEFLAGS) $(BUILD_DIR)/capstone/$(LIBCAPSTONE)) | ||||
|  | ||||
| $(SUBDIR_RULES): libqemuutil.a $(common-obj-y) $(chardev-obj-y) $(slirp-obj-y) \ | ||||
| $(SUBDIR_RULES): libqemuutil.a $(common-obj-y) $(chardev-obj-y) \ | ||||
| 	$(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY)) | ||||
|  | ||||
| ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS)) | ||||
| @@ -493,8 +537,6 @@ qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS) | ||||
|  | ||||
| qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $(COMMON_LDADDS) | ||||
|  | ||||
| qemu-edid$(EXESUF): qemu-edid.o hw/display/edid-generate.o $(COMMON_LDADDS) | ||||
|  | ||||
| fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS) | ||||
| fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap | ||||
|  | ||||
| @@ -519,6 +561,7 @@ $(SRC_PATH)/scripts/qapi/types.py \ | ||||
| $(SRC_PATH)/scripts/qapi/visit.py \ | ||||
| $(SRC_PATH)/scripts/qapi/common.py \ | ||||
| $(SRC_PATH)/scripts/qapi/doc.py \ | ||||
| $(SRC_PATH)/scripts/ordereddict.py \ | ||||
| $(SRC_PATH)/scripts/qapi-gen.py | ||||
|  | ||||
| qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \ | ||||
| @@ -527,17 +570,102 @@ qga/qapi-generated/qga-qapi-commands.h qga/qapi-generated/qga-qapi-commands.c \ | ||||
| qga/qapi-generated/qga-qapi-doc.texi: \ | ||||
| qga/qapi-generated/qapi-gen-timestamp ; | ||||
| qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py) | ||||
| 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ | ||||
| 	$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \ | ||||
| 		-o qga/qapi-generated -p "qga-" $<, \ | ||||
| 		"GEN","$(@:%-timestamp=%)") | ||||
| 	@>$@ | ||||
|  | ||||
| qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json \ | ||||
|                $(QAPI_MODULES:%=$(SRC_PATH)/qapi/%.json) | ||||
| qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json $(SRC_PATH)/qapi/common.json \ | ||||
|                $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \ | ||||
|                $(SRC_PATH)/qapi/char.json \ | ||||
|                $(SRC_PATH)/qapi/crypto.json \ | ||||
|                $(SRC_PATH)/qapi/introspect.json \ | ||||
|                $(SRC_PATH)/qapi/migration.json \ | ||||
|                $(SRC_PATH)/qapi/misc.json \ | ||||
|                $(SRC_PATH)/qapi/net.json \ | ||||
|                $(SRC_PATH)/qapi/rocker.json \ | ||||
|                $(SRC_PATH)/qapi/run-state.json \ | ||||
|                $(SRC_PATH)/qapi/sockets.json \ | ||||
|                $(SRC_PATH)/qapi/tpm.json \ | ||||
|                $(SRC_PATH)/qapi/trace.json \ | ||||
|                $(SRC_PATH)/qapi/transaction.json \ | ||||
|                $(SRC_PATH)/qapi/ui.json | ||||
|  | ||||
| $(GENERATED_QAPI_FILES): qapi-gen-timestamp ; | ||||
| qapi/qapi-builtin-types.c qapi/qapi-builtin-types.h \ | ||||
| qapi/qapi-types.c qapi/qapi-types.h \ | ||||
| qapi/qapi-types-block-core.c qapi/qapi-types-block-core.h \ | ||||
| qapi/qapi-types-block.c qapi/qapi-types-block.h \ | ||||
| qapi/qapi-types-char.c qapi/qapi-types-char.h \ | ||||
| qapi/qapi-types-common.c qapi/qapi-types-common.h \ | ||||
| qapi/qapi-types-crypto.c qapi/qapi-types-crypto.h \ | ||||
| qapi/qapi-types-introspect.c qapi/qapi-types-introspect.h \ | ||||
| qapi/qapi-types-migration.c qapi/qapi-types-migration.h \ | ||||
| qapi/qapi-types-misc.c qapi/qapi-types-misc.h \ | ||||
| qapi/qapi-types-net.c qapi/qapi-types-net.h \ | ||||
| qapi/qapi-types-rocker.c qapi/qapi-types-rocker.h \ | ||||
| qapi/qapi-types-run-state.c qapi/qapi-types-run-state.h \ | ||||
| qapi/qapi-types-sockets.c qapi/qapi-types-sockets.h \ | ||||
| qapi/qapi-types-tpm.c qapi/qapi-types-tpm.h \ | ||||
| qapi/qapi-types-trace.c qapi/qapi-types-trace.h \ | ||||
| qapi/qapi-types-transaction.c qapi/qapi-types-transaction.h \ | ||||
| qapi/qapi-types-ui.c qapi/qapi-types-ui.h \ | ||||
| qapi/qapi-builtin-visit.c qapi/qapi-builtin-visit.h \ | ||||
| qapi/qapi-visit.c qapi/qapi-visit.h \ | ||||
| qapi/qapi-visit-block-core.c qapi/qapi-visit-block-core.h \ | ||||
| qapi/qapi-visit-block.c qapi/qapi-visit-block.h \ | ||||
| qapi/qapi-visit-char.c qapi/qapi-visit-char.h \ | ||||
| qapi/qapi-visit-common.c qapi/qapi-visit-common.h \ | ||||
| qapi/qapi-visit-crypto.c qapi/qapi-visit-crypto.h \ | ||||
| qapi/qapi-visit-introspect.c qapi/qapi-visit-introspect.h \ | ||||
| qapi/qapi-visit-migration.c qapi/qapi-visit-migration.h \ | ||||
| qapi/qapi-visit-misc.c qapi/qapi-visit-misc.h \ | ||||
| qapi/qapi-visit-net.c qapi/qapi-visit-net.h \ | ||||
| qapi/qapi-visit-rocker.c qapi/qapi-visit-rocker.h \ | ||||
| qapi/qapi-visit-run-state.c qapi/qapi-visit-run-state.h \ | ||||
| qapi/qapi-visit-sockets.c qapi/qapi-visit-sockets.h \ | ||||
| qapi/qapi-visit-tpm.c qapi/qapi-visit-tpm.h \ | ||||
| qapi/qapi-visit-trace.c qapi/qapi-visit-trace.h \ | ||||
| qapi/qapi-visit-transaction.c qapi/qapi-visit-transaction.h \ | ||||
| qapi/qapi-visit-ui.c qapi/qapi-visit-ui.h \ | ||||
| qapi/qapi-commands.h qapi/qapi-commands.c \ | ||||
| qapi/qapi-commands-block-core.c qapi/qapi-commands-block-core.h \ | ||||
| qapi/qapi-commands-block.c qapi/qapi-commands-block.h \ | ||||
| qapi/qapi-commands-char.c qapi/qapi-commands-char.h \ | ||||
| qapi/qapi-commands-common.c qapi/qapi-commands-common.h \ | ||||
| qapi/qapi-commands-crypto.c qapi/qapi-commands-crypto.h \ | ||||
| qapi/qapi-commands-introspect.c qapi/qapi-commands-introspect.h \ | ||||
| qapi/qapi-commands-migration.c qapi/qapi-commands-migration.h \ | ||||
| qapi/qapi-commands-misc.c qapi/qapi-commands-misc.h \ | ||||
| qapi/qapi-commands-net.c qapi/qapi-commands-net.h \ | ||||
| qapi/qapi-commands-rocker.c qapi/qapi-commands-rocker.h \ | ||||
| qapi/qapi-commands-run-state.c qapi/qapi-commands-run-state.h \ | ||||
| qapi/qapi-commands-sockets.c qapi/qapi-commands-sockets.h \ | ||||
| qapi/qapi-commands-tpm.c qapi/qapi-commands-tpm.h \ | ||||
| qapi/qapi-commands-trace.c qapi/qapi-commands-trace.h \ | ||||
| qapi/qapi-commands-transaction.c qapi/qapi-commands-transaction.h \ | ||||
| qapi/qapi-commands-ui.c qapi/qapi-commands-ui.h \ | ||||
| qapi/qapi-events.c qapi/qapi-events.h \ | ||||
| qapi/qapi-events-block-core.c qapi/qapi-events-block-core.h \ | ||||
| qapi/qapi-events-block.c qapi/qapi-events-block.h \ | ||||
| qapi/qapi-events-char.c qapi/qapi-events-char.h \ | ||||
| qapi/qapi-events-common.c qapi/qapi-events-common.h \ | ||||
| qapi/qapi-events-crypto.c qapi/qapi-events-crypto.h \ | ||||
| qapi/qapi-events-introspect.c qapi/qapi-events-introspect.h \ | ||||
| qapi/qapi-events-migration.c qapi/qapi-events-migration.h \ | ||||
| qapi/qapi-events-misc.c qapi/qapi-events-misc.h \ | ||||
| qapi/qapi-events-net.c qapi/qapi-events-net.h \ | ||||
| qapi/qapi-events-rocker.c qapi/qapi-events-rocker.h \ | ||||
| qapi/qapi-events-run-state.c qapi/qapi-events-run-state.h \ | ||||
| qapi/qapi-events-sockets.c qapi/qapi-events-sockets.h \ | ||||
| qapi/qapi-events-tpm.c qapi/qapi-events-tpm.h \ | ||||
| qapi/qapi-events-trace.c qapi/qapi-events-trace.h \ | ||||
| qapi/qapi-events-transaction.c qapi/qapi-events-transaction.h \ | ||||
| qapi/qapi-events-ui.c qapi/qapi-events-ui.h \ | ||||
| qapi/qapi-introspect.h qapi/qapi-introspect.c \ | ||||
| qapi/qapi-doc.texi: \ | ||||
| qapi-gen-timestamp ; | ||||
| qapi-gen-timestamp: $(qapi-modules) $(qapi-py) | ||||
| 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ | ||||
| 	$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \ | ||||
| 		-o "qapi" -b $<, \ | ||||
| 		"GEN","$(@:%-timestamp=%)") | ||||
| 	@>$@ | ||||
| @@ -570,10 +698,6 @@ ifneq ($(EXESUF),) | ||||
| qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI) | ||||
| endif | ||||
|  | ||||
| elf2dmp: LIBS = $(CURL_LIBS) | ||||
| elf2dmp: $(elf2dmp-obj-y) | ||||
| 	$(call LINK, $^) | ||||
|  | ||||
| ifdef CONFIG_IVSHMEM | ||||
| ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS) | ||||
| 	$(call LINK, $^) | ||||
| @@ -585,23 +709,11 @@ vhost-user-scsi$(EXESUF): $(vhost-user-scsi-obj-y) libvhost-user.a | ||||
| vhost-user-blk$(EXESUF): $(vhost-user-blk-obj-y) libvhost-user.a | ||||
| 	$(call LINK, $^) | ||||
|  | ||||
| rdmacm-mux$(EXESUF): LIBS += "-libumad" | ||||
| rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS) | ||||
| 	$(call LINK, $^) | ||||
|  | ||||
| module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak | ||||
| 	$(call quiet-command,$(PYTHON) $< $@ \ | ||||
| 	$(addprefix $(SRC_PATH)/,$(patsubst %.mo,%.c,$(block-obj-m))), \ | ||||
| 	"GEN","$@") | ||||
|  | ||||
| ifdef CONFIG_GCOV | ||||
| .PHONY: clean-coverage | ||||
| clean-coverage: | ||||
| 	$(call quiet-command, \ | ||||
| 		find . \( -name '*.gcda' -o -name '*.gcov' \) -type f -exec rm {} +, \ | ||||
| 		"CLEAN", "coverage files") | ||||
| endif | ||||
|  | ||||
| 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 | ||||
| @@ -622,7 +734,7 @@ clean: | ||||
| 	if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \ | ||||
| 	rm -f $$d/qemu-options.def; \ | ||||
|         done | ||||
| 	rm -f config-all-devices.mak | ||||
| 	rm -f $(SUBDIR_DEVICES_MAK) config-all-devices.mak | ||||
|  | ||||
| VERSION ?= $(shell cat VERSION) | ||||
|  | ||||
| @@ -634,7 +746,6 @@ 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-all-devices.mak config-all-disas.mak config.status | ||||
| 	rm -f $(SUBDIR_DEVICES_MAK) | ||||
| 	rm -f po/*.mo tests/qemu-iotests/common.env | ||||
| 	rm -f roms/seabios/config.mak roms/vgabios/config.mak | ||||
| 	rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps | ||||
| @@ -650,28 +761,28 @@ distclean: clean | ||||
| 	rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf | ||||
| 	rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html | ||||
| 	rm -f docs/qemu-block-drivers.7 | ||||
| 	rm -f docs/qemu-cpu-models.7 | ||||
| 	for d in $(TARGET_DIRS); do \ | ||||
| 	rm -rf $$d || exit 1 ; \ | ||||
|         done | ||||
| 	rm -Rf .sdk | ||||
| 	if test -f dtc/version_gen.h; then $(MAKE) $(DTC_MAKE_ARGS) clean; fi | ||||
|  | ||||
| KEYMAPS=da     en-gb  et  fr     fr-ch  is  lt  no  pt-br  sv \ | ||||
| KEYMAPS=da     en-gb  et  fr     fr-ch  is  lt  modifiers  no  pt-br  sv \ | ||||
| ar      de     en-us  fi  fr-be  hr     it  lv  nl         pl  ru     th \ | ||||
| de-ch  es     fo  fr-ca  hu     ja  mk  pt  sl     tr \ | ||||
| common  de-ch  es     fo  fr-ca  hu     ja  mk  nl-be      pt  sl     tr \ | ||||
| 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 \ | ||||
| vgabios-ramfb.bin vgabios-bochs-display.bin \ | ||||
| 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 \ | ||||
| efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \ | ||||
| efi-pcnet.rom efi-rtl8139.rom efi-virtio.rom \ | ||||
| efi-e1000e.rom efi-vmxnet3.rom \ | ||||
| qemu-icon.bmp qemu_logo_no_text.svg \ | ||||
| bamboo.dtb canyonlands.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ | ||||
| multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin \ | ||||
| s390-ccw.img s390-netboot.img \ | ||||
| @@ -696,15 +807,11 @@ ifdef CONFIG_POSIX | ||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7" | ||||
| 	$(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7" | ||||
| 	$(INSTALL_DATA) docs/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7" | ||||
| 	$(INSTALL_DATA) docs/qemu-cpu-models.7 "$(DESTDIR)$(mandir)/man7" | ||||
| ifneq ($(TOOLS),) | ||||
| 	$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1" | ||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8" | ||||
| 	$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8" | ||||
| endif | ||||
| ifdef CONFIG_TRACE_SYSTEMTAP | ||||
| 	$(INSTALL_DATA) scripts/qemu-trace-stap.1 "$(DESTDIR)$(mandir)/man1" | ||||
| endif | ||||
| ifneq (,$(findstring qemu-ga,$(TOOLS))) | ||||
| 	$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8" | ||||
| 	$(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)" | ||||
| @@ -727,7 +834,6 @@ ifneq (,$(findstring qemu-ga,$(TOOLS))) | ||||
| endif | ||||
| endif | ||||
|  | ||||
| ICON_SIZES=16x16 24x24 32x32 48x48 64x64 128x128 256x256 512x512 | ||||
|  | ||||
| install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir | ||||
| ifneq ($(TOOLS),) | ||||
| @@ -744,29 +850,12 @@ endif | ||||
| ifneq ($(HELPERS-y),) | ||||
| 	$(call install-prog,$(HELPERS-y),$(DESTDIR)$(libexecdir)) | ||||
| endif | ||||
| ifdef CONFIG_TRACE_SYSTEMTAP | ||||
| 	$(INSTALL_PROG) "scripts/qemu-trace-stap" $(DESTDIR)$(bindir) | ||||
| endif | ||||
| ifneq ($(BLOBS),) | ||||
| 	set -e; for x in $(BLOBS); do \ | ||||
| 		$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \ | ||||
| 	done | ||||
| endif | ||||
| 	for s in $(ICON_SIZES); do \ | ||||
| 		mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/$${s}/apps"; \ | ||||
| 		$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu_$${s}.png \ | ||||
| 			"$(DESTDIR)/$(qemu_icondir)/hicolor/$${s}/apps/qemu.png"; \ | ||||
| 	done; \ | ||||
| 	mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/32x32/apps"; \ | ||||
| 	$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu_32x32.bmp \ | ||||
| 		"$(DESTDIR)/$(qemu_icondir)/hicolor/32x32/apps/qemu.bmp"; \ | ||||
| 	mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/scalable/apps"; \ | ||||
| 	$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu.svg \ | ||||
| 		"$(DESTDIR)/$(qemu_icondir)/hicolor/scalable/apps/qemu.svg" | ||||
| 	mkdir -p "$(DESTDIR)/$(qemu_desktopdir)" | ||||
| 	$(INSTALL_DATA) $(SRC_PATH)/ui/qemu.desktop \ | ||||
| 		"$(DESTDIR)/$(qemu_desktopdir)/qemu.desktop" | ||||
| ifdef CONFIG_GTK | ||||
| ifeq ($(CONFIG_GTK),m) | ||||
| 	$(MAKE) -C po $@ | ||||
| endif | ||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps" | ||||
| @@ -860,8 +949,6 @@ fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi | ||||
| qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi | ||||
| qemu-ga.8: qemu-ga.texi | ||||
| docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi | ||||
| docs/qemu-cpu-models.7: docs/qemu-cpu-models.texi | ||||
| scripts/qemu-trace-stap.1: scripts/qemu-trace-stap.texi | ||||
|  | ||||
| html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html | ||||
| info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info | ||||
| @@ -870,9 +957,8 @@ txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt | ||||
|  | ||||
| qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \ | ||||
| 	qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \ | ||||
| 	qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \ | ||||
| 	qemu-monitor-info.texi docs/qemu-block-drivers.texi \ | ||||
| 	docs/qemu-cpu-models.texi | ||||
| 	qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \ | ||||
| 	qemu-monitor-info.texi docs/qemu-block-drivers.texi | ||||
|  | ||||
| docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \ | ||||
|     docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \ | ||||
| @@ -884,18 +970,6 @@ docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \ | ||||
|     docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \ | ||||
| 	docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi | ||||
|  | ||||
| $(filter %.1 %.7 %.8,$(DOCS)): scripts/texi2pod.pl | ||||
|  | ||||
| # Reports/Analysis | ||||
|  | ||||
| %/coverage-report.html: | ||||
| 	@mkdir -p $* | ||||
| 	$(call quiet-command,\ | ||||
| 		gcovr -p --html --html-details -o $@, \ | ||||
| 		"GEN", "coverage-report.html") | ||||
|  | ||||
| .PHONY: coverage-report | ||||
| coverage-report: $(CURDIR)/reports/coverage/coverage-report.html | ||||
|  | ||||
| ifdef CONFIG_WIN32 | ||||
|  | ||||
| @@ -976,9 +1050,6 @@ include $(SRC_PATH)/tests/vm/Makefile.include | ||||
| help: | ||||
| 	@echo  'Generic targets:' | ||||
| 	@echo  '  all             - Build all' | ||||
| ifdef CONFIG_MODULES | ||||
| 	@echo  '  modules         - Build all modules' | ||||
| endif | ||||
| 	@echo  '  dir/file.o      - Build specified target only' | ||||
| 	@echo  '  install         - Install QEMU, documentation and tools' | ||||
| 	@echo  '  ctags/TAGS      - Generate tags file for editors' | ||||
| @@ -991,9 +1062,6 @@ endif | ||||
| 		echo '') | ||||
| 	@echo  'Cleaning targets:' | ||||
| 	@echo  '  clean           - Remove most generated files but keep the config' | ||||
| ifdef CONFIG_GCOV | ||||
| 	@echo  '  clean-coverage  - Remove coverage files' | ||||
| endif | ||||
| 	@echo  '  distclean       - Remove all generated files' | ||||
| 	@echo  '  dist            - Build a distributable tarball' | ||||
| 	@echo  '' | ||||
| @@ -1005,9 +1073,6 @@ endif | ||||
| 	@echo  'Documentation targets:' | ||||
| 	@echo  '  html info pdf txt' | ||||
| 	@echo  '                  - Build documentation in specified format' | ||||
| ifdef CONFIG_GCOV | ||||
| 	@echo  '  coverage-report - Create code coverage report' | ||||
| endif | ||||
| 	@echo  '' | ||||
| ifdef CONFIG_WIN32 | ||||
| 	@echo  'Windows targets:' | ||||
|   | ||||
							
								
								
									
										166
									
								
								Makefile.objs
									
									
									
									
									
								
							
							
						
						
									
										166
									
								
								Makefile.objs
									
									
									
									
									
								
							| @@ -1,29 +1,69 @@ | ||||
| QAPI_MODULES = block-core block char common crypto introspect job migration | ||||
| QAPI_MODULES += misc net rdma rocker run-state sockets tpm trace transaction | ||||
| QAPI_MODULES += ui | ||||
|  | ||||
| ####################################################################### | ||||
| # Common libraries for tools and emulators | ||||
| stub-obj-y = stubs/ crypto/ | ||||
| util-obj-y = util/ qobject/ qapi/ | ||||
| util-obj-y += qapi/qapi-builtin-types.o | ||||
| util-obj-y += qapi/qapi-types.o | ||||
| util-obj-y += $(QAPI_MODULES:%=qapi/qapi-types-%.o) | ||||
| util-obj-y += qapi/qapi-types-block-core.o | ||||
| util-obj-y += qapi/qapi-types-block.o | ||||
| util-obj-y += qapi/qapi-types-char.o | ||||
| util-obj-y += qapi/qapi-types-common.o | ||||
| util-obj-y += qapi/qapi-types-crypto.o | ||||
| util-obj-y += qapi/qapi-types-introspect.o | ||||
| util-obj-y += qapi/qapi-types-migration.o | ||||
| util-obj-y += qapi/qapi-types-misc.o | ||||
| util-obj-y += qapi/qapi-types-net.o | ||||
| util-obj-y += qapi/qapi-types-rocker.o | ||||
| util-obj-y += qapi/qapi-types-run-state.o | ||||
| util-obj-y += qapi/qapi-types-sockets.o | ||||
| util-obj-y += qapi/qapi-types-tpm.o | ||||
| util-obj-y += qapi/qapi-types-trace.o | ||||
| util-obj-y += qapi/qapi-types-transaction.o | ||||
| util-obj-y += qapi/qapi-types-ui.o | ||||
| util-obj-y += qapi/qapi-builtin-visit.o | ||||
| util-obj-y += qapi/qapi-visit.o | ||||
| util-obj-y += $(QAPI_MODULES:%=qapi/qapi-visit-%.o) | ||||
| util-obj-y += qapi/qapi-visit-block-core.o | ||||
| util-obj-y += qapi/qapi-visit-block.o | ||||
| util-obj-y += qapi/qapi-visit-char.o | ||||
| util-obj-y += qapi/qapi-visit-common.o | ||||
| util-obj-y += qapi/qapi-visit-crypto.o | ||||
| util-obj-y += qapi/qapi-visit-introspect.o | ||||
| util-obj-y += qapi/qapi-visit-migration.o | ||||
| util-obj-y += qapi/qapi-visit-misc.o | ||||
| util-obj-y += qapi/qapi-visit-net.o | ||||
| util-obj-y += qapi/qapi-visit-rocker.o | ||||
| util-obj-y += qapi/qapi-visit-run-state.o | ||||
| util-obj-y += qapi/qapi-visit-sockets.o | ||||
| util-obj-y += qapi/qapi-visit-tpm.o | ||||
| util-obj-y += qapi/qapi-visit-trace.o | ||||
| util-obj-y += qapi/qapi-visit-transaction.o | ||||
| util-obj-y += qapi/qapi-visit-ui.o | ||||
| util-obj-y += qapi/qapi-events.o | ||||
| util-obj-y += $(QAPI_MODULES:%=qapi/qapi-events-%.o) | ||||
| util-obj-y += qapi/qapi-events-block-core.o | ||||
| util-obj-y += qapi/qapi-events-block.o | ||||
| util-obj-y += qapi/qapi-events-char.o | ||||
| util-obj-y += qapi/qapi-events-common.o | ||||
| util-obj-y += qapi/qapi-events-crypto.o | ||||
| util-obj-y += qapi/qapi-events-introspect.o | ||||
| util-obj-y += qapi/qapi-events-migration.o | ||||
| util-obj-y += qapi/qapi-events-misc.o | ||||
| util-obj-y += qapi/qapi-events-net.o | ||||
| util-obj-y += qapi/qapi-events-rocker.o | ||||
| util-obj-y += qapi/qapi-events-run-state.o | ||||
| util-obj-y += qapi/qapi-events-sockets.o | ||||
| util-obj-y += qapi/qapi-events-tpm.o | ||||
| util-obj-y += qapi/qapi-events-trace.o | ||||
| util-obj-y += qapi/qapi-events-transaction.o | ||||
| util-obj-y += qapi/qapi-events-ui.o | ||||
| util-obj-y += qapi/qapi-introspect.o | ||||
|  | ||||
| chardev-obj-y = chardev/ | ||||
| slirp-obj-$(CONFIG_SLIRP) = slirp/ | ||||
|  | ||||
| ####################################################################### | ||||
| # block-obj-y is code used by both qemu system emulation and qemu-img | ||||
|  | ||||
| block-obj-y += nbd/ | ||||
| block-obj-y += block.o blockjob.o job.o | ||||
| block-obj-y += block.o blockjob.o | ||||
| block-obj-y += block/ scsi/ | ||||
| block-obj-y += qemu-io-cmds.o | ||||
| block-obj-$(CONFIG_REPLICATION) += replication.o | ||||
| @@ -54,7 +94,6 @@ io-obj-y = io/ | ||||
| ifeq ($(CONFIG_SOFTMMU),y) | ||||
| common-obj-y = blockdev.o blockdev-nbd.o block/ | ||||
| common-obj-y += bootdevice.o iothread.o | ||||
| common-obj-y += job-qmp.o | ||||
| common-obj-y += net/ | ||||
| common-obj-y += qdev-monitor.o device-hotplug.o | ||||
| common-obj-$(CONFIG_WIN32) += os-win32.o | ||||
| @@ -80,6 +119,8 @@ common-obj-y += vl.o | ||||
| vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS) | ||||
| common-obj-$(CONFIG_TPM) += tpm.o | ||||
|  | ||||
| common-obj-$(CONFIG_SLIRP) += slirp/ | ||||
|  | ||||
| common-obj-y += backends/ | ||||
| common-obj-y += chardev/ | ||||
|  | ||||
| @@ -93,7 +134,22 @@ common-obj-$(CONFIG_FDT) += device_tree.o | ||||
| # qapi | ||||
|  | ||||
| common-obj-y += qapi/qapi-commands.o | ||||
| common-obj-y += $(QAPI_MODULES:%=qapi/qapi-commands-%.o) | ||||
| common-obj-y += qapi/qapi-commands-block-core.o | ||||
| common-obj-y += qapi/qapi-commands-block.o | ||||
| common-obj-y += qapi/qapi-commands-char.o | ||||
| common-obj-y += qapi/qapi-commands-common.o | ||||
| common-obj-y += qapi/qapi-commands-crypto.o | ||||
| common-obj-y += qapi/qapi-commands-introspect.o | ||||
| common-obj-y += qapi/qapi-commands-migration.o | ||||
| common-obj-y += qapi/qapi-commands-misc.o | ||||
| common-obj-y += qapi/qapi-commands-net.o | ||||
| common-obj-y += qapi/qapi-commands-rocker.o | ||||
| common-obj-y += qapi/qapi-commands-run-state.o | ||||
| common-obj-y += qapi/qapi-commands-sockets.o | ||||
| common-obj-y += qapi/qapi-commands-tpm.o | ||||
| common-obj-y += qapi/qapi-commands-trace.o | ||||
| common-obj-y += qapi/qapi-commands-transaction.o | ||||
| common-obj-y += qapi/qapi-commands-ui.o | ||||
| common-obj-y += qapi/qapi-introspect.o | ||||
| common-obj-y += qmp.o hmp.o | ||||
| endif | ||||
| @@ -125,7 +181,6 @@ qga-vss-dll-obj-y = qga/ | ||||
|  | ||||
| ###################################################################### | ||||
| # contrib | ||||
| elf2dmp-obj-y = contrib/elf2dmp/ | ||||
| ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/ | ||||
| ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/ | ||||
| libvhost-user-obj-y = contrib/libvhost-user/ | ||||
| @@ -133,74 +188,69 @@ vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS) | ||||
| vhost-user-scsi.o-libs := $(LIBISCSI_LIBS) | ||||
| vhost-user-scsi-obj-y = contrib/vhost-user-scsi/ | ||||
| vhost-user-blk-obj-y = contrib/vhost-user-blk/ | ||||
| rdmacm-mux-obj-y = contrib/rdmacm-mux/ | ||||
|  | ||||
| ###################################################################### | ||||
| trace-events-subdirs = | ||||
| trace-events-subdirs += accel/kvm | ||||
| trace-events-subdirs += accel/tcg | ||||
| trace-events-subdirs += audio | ||||
| trace-events-subdirs += util | ||||
| trace-events-subdirs += crypto | ||||
| trace-events-subdirs += io | ||||
| trace-events-subdirs += migration | ||||
| trace-events-subdirs += block | ||||
| trace-events-subdirs += chardev | ||||
| trace-events-subdirs += crypto | ||||
| trace-events-subdirs += hw/9pfs | ||||
| trace-events-subdirs += hw/acpi | ||||
| trace-events-subdirs += hw/alpha | ||||
| trace-events-subdirs += hw/arm | ||||
| trace-events-subdirs += hw/audio | ||||
| trace-events-subdirs += hw/block | ||||
| trace-events-subdirs += hw/block/dataplane | ||||
| trace-events-subdirs += hw/char | ||||
| trace-events-subdirs += hw/display | ||||
| trace-events-subdirs += hw/dma | ||||
| trace-events-subdirs += hw/hppa | ||||
| trace-events-subdirs += hw/i2c | ||||
| trace-events-subdirs += hw/i386 | ||||
| trace-events-subdirs += hw/i386/xen | ||||
| trace-events-subdirs += hw/ide | ||||
| trace-events-subdirs += hw/input | ||||
| trace-events-subdirs += hw/intc | ||||
| trace-events-subdirs += hw/isa | ||||
| trace-events-subdirs += hw/mem | ||||
| trace-events-subdirs += hw/misc | ||||
| trace-events-subdirs += hw/misc/macio | ||||
| trace-events-subdirs += hw/net | ||||
| trace-events-subdirs += hw/nvram | ||||
| trace-events-subdirs += hw/pci | ||||
| trace-events-subdirs += hw/pci-host | ||||
| trace-events-subdirs += hw/ppc | ||||
| trace-events-subdirs += hw/rdma | ||||
| trace-events-subdirs += hw/rdma/vmw | ||||
| trace-events-subdirs += hw/s390x | ||||
| trace-events-subdirs += hw/virtio | ||||
| trace-events-subdirs += hw/audio | ||||
| trace-events-subdirs += hw/misc | ||||
| trace-events-subdirs += hw/misc/macio | ||||
| trace-events-subdirs += hw/usb | ||||
| trace-events-subdirs += hw/scsi | ||||
| trace-events-subdirs += hw/sd | ||||
| trace-events-subdirs += hw/nvram | ||||
| trace-events-subdirs += hw/display | ||||
| trace-events-subdirs += hw/input | ||||
| trace-events-subdirs += hw/timer | ||||
| trace-events-subdirs += hw/dma | ||||
| trace-events-subdirs += hw/sparc | ||||
| trace-events-subdirs += hw/sparc64 | ||||
| trace-events-subdirs += hw/timer | ||||
| trace-events-subdirs += hw/tpm | ||||
| trace-events-subdirs += hw/usb | ||||
| trace-events-subdirs += hw/sd | ||||
| trace-events-subdirs += hw/isa | ||||
| trace-events-subdirs += hw/mem | ||||
| trace-events-subdirs += hw/i386 | ||||
| trace-events-subdirs += hw/i386/xen | ||||
| trace-events-subdirs += hw/9pfs | ||||
| trace-events-subdirs += hw/ppc | ||||
| trace-events-subdirs += hw/pci | ||||
| trace-events-subdirs += hw/pci-host | ||||
| trace-events-subdirs += hw/s390x | ||||
| trace-events-subdirs += hw/vfio | ||||
| trace-events-subdirs += hw/virtio | ||||
| trace-events-subdirs += hw/watchdog | ||||
| trace-events-subdirs += hw/acpi | ||||
| trace-events-subdirs += hw/arm | ||||
| trace-events-subdirs += hw/alpha | ||||
| trace-events-subdirs += hw/hppa | ||||
| trace-events-subdirs += hw/xen | ||||
| trace-events-subdirs += hw/gpio | ||||
| trace-events-subdirs += io | ||||
| trace-events-subdirs += linux-user | ||||
| trace-events-subdirs += migration | ||||
| trace-events-subdirs += nbd | ||||
| trace-events-subdirs += hw/ide | ||||
| trace-events-subdirs += hw/tpm | ||||
| trace-events-subdirs += ui | ||||
| trace-events-subdirs += audio | ||||
| trace-events-subdirs += net | ||||
| trace-events-subdirs += qapi | ||||
| trace-events-subdirs += qom | ||||
| trace-events-subdirs += scsi | ||||
| trace-events-subdirs += slirp | ||||
| trace-events-subdirs += target/arm | ||||
| trace-events-subdirs += target/i386 | ||||
| trace-events-subdirs += target/mips | ||||
| trace-events-subdirs += target/ppc | ||||
| trace-events-subdirs += target/s390x | ||||
| trace-events-subdirs += target/sparc | ||||
| trace-events-subdirs += ui | ||||
| trace-events-subdirs += util | ||||
| trace-events-subdirs += target/s390x | ||||
| trace-events-subdirs += target/ppc | ||||
| trace-events-subdirs += qom | ||||
| trace-events-subdirs += linux-user | ||||
| trace-events-subdirs += qapi | ||||
| trace-events-subdirs += accel/tcg | ||||
| trace-events-subdirs += accel/kvm | ||||
| trace-events-subdirs += nbd | ||||
| trace-events-subdirs += scsi | ||||
|  | ||||
| trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events) | ||||
|  | ||||
|   | ||||
| @@ -11,9 +11,9 @@ $(call set-vpath, $(SRC_PATH):$(BUILD_DIR)) | ||||
| ifdef CONFIG_LINUX | ||||
| QEMU_CFLAGS += -I../linux-headers | ||||
| endif | ||||
| QEMU_CFLAGS += -iquote .. -iquote $(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H | ||||
| QEMU_CFLAGS += -I.. -I$(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H | ||||
|  | ||||
| QEMU_CFLAGS+=-iquote $(SRC_PATH)/include | ||||
| QEMU_CFLAGS+=-I$(SRC_PATH)/include | ||||
|  | ||||
| ifdef CONFIG_USER_ONLY | ||||
| # user emulator name | ||||
| @@ -36,16 +36,11 @@ endif | ||||
| PROGS=$(QEMU_PROG) $(QEMU_PROGW) | ||||
| STPFILES= | ||||
|  | ||||
| # Makefile Tests | ||||
| ifdef CONFIG_USER_ONLY | ||||
| include $(SRC_PATH)/tests/tcg/Makefile.include | ||||
| endif | ||||
|  | ||||
| config-target.h: config-target.h-timestamp | ||||
| config-target.h-timestamp: config-target.mak | ||||
|  | ||||
| ifdef CONFIG_TRACE_SYSTEMTAP | ||||
| stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp $(QEMU_PROG)-log.stp | ||||
| stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp | ||||
|  | ||||
| ifdef CONFIG_USER_ONLY | ||||
| TARGET_TYPE=user | ||||
| @@ -84,14 +79,6 @@ $(QEMU_PROG)-simpletrace.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y) | ||||
| 		--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \ | ||||
| 		$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp") | ||||
|  | ||||
| $(QEMU_PROG)-log.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y) | ||||
| 	$(call quiet-command,$(TRACETOOL) \ | ||||
| 		--group=all \ | ||||
| 		--format=log-stap \ | ||||
| 		--backends=$(TRACE_BACKENDS) \ | ||||
| 		--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \ | ||||
| 		$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-log.stp") | ||||
|  | ||||
| else | ||||
| stap: | ||||
| endif | ||||
| @@ -110,7 +97,7 @@ obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o | ||||
| obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o | ||||
| obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o | ||||
| obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o | ||||
| obj-$(CONFIG_TCG) += fpu/softfloat.o | ||||
| obj-y += fpu/softfloat.o | ||||
| obj-y += target/$(TARGET_BASE_ARCH)/ | ||||
| obj-y += disas.o | ||||
| obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o | ||||
| @@ -151,7 +138,6 @@ obj-y += hw/ | ||||
| obj-y += memory.o | ||||
| obj-y += memory_mapping.o | ||||
| obj-y += dump.o | ||||
| obj-$(TARGET_X86_64) += win_dump.o | ||||
| obj-y += migration/ram.o | ||||
| LIBS := $(libs_softmmu) $(LIBS) | ||||
|  | ||||
| @@ -166,6 +152,9 @@ GENERATED_FILES += hmp-commands.h hmp-commands-info.h | ||||
|  | ||||
| endif # CONFIG_SOFTMMU | ||||
|  | ||||
| # Workaround for http://gcc.gnu.org/PR55489, see configure. | ||||
| %/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS) | ||||
|  | ||||
| dummy := $(call unnest-vars,,obj-y) | ||||
| all-obj-y := $(obj-y) | ||||
|  | ||||
| @@ -173,7 +162,6 @@ target-obj-y := | ||||
| block-obj-y := | ||||
| common-obj-y := | ||||
| chardev-obj-y := | ||||
| slirp-obj-y := | ||||
| include $(SRC_PATH)/Makefile.objs | ||||
| dummy := $(call unnest-vars,,target-obj-y) | ||||
| target-obj-y-save := $(target-obj-y) | ||||
| @@ -186,8 +174,7 @@ dummy := $(call unnest-vars,.., \ | ||||
|                qom-obj-y \ | ||||
|                io-obj-y \ | ||||
|                common-obj-y \ | ||||
|                common-obj-m \ | ||||
|                slirp-obj-y) | ||||
|                common-obj-m) | ||||
| target-obj-y := $(target-obj-y-save) | ||||
| all-obj-y += $(common-obj-y) | ||||
| all-obj-y += $(target-obj-y) | ||||
| @@ -196,7 +183,6 @@ all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-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) | ||||
| all-obj-$(CONFIG_SOFTMMU) += $(slirp-obj-y) | ||||
|  | ||||
| $(QEMU_PROG_BUILD): config-devices.mak | ||||
|  | ||||
| @@ -235,7 +221,6 @@ ifdef CONFIG_TRACE_SYSTEMTAP | ||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset" | ||||
| 	$(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp" | ||||
| 	$(INSTALL_DATA) $(QEMU_PROG)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp" | ||||
| 	$(INSTALL_DATA) $(QEMU_PROG)-log.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-log.stp" | ||||
| endif | ||||
|  | ||||
| GENERATED_FILES += config-target.h | ||||
|   | ||||
							
								
								
									
										4
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								README
									
									
									
									
									
								
							| @@ -54,7 +54,7 @@ Submitting patches | ||||
|  | ||||
| The QEMU source code is maintained under the GIT version control system. | ||||
|  | ||||
|    git clone https://git.qemu.org/git/qemu.git | ||||
|    git clone git://git.qemu.org/qemu.git | ||||
|  | ||||
| When submitting patches, one common approach is to use 'git | ||||
| format-patch' and/or 'git send-email' to format & send the mail to the | ||||
| @@ -70,7 +70,7 @@ the QEMU website | ||||
|  | ||||
| The QEMU website is also maintained under source control. | ||||
|  | ||||
|   git clone https://git.qemu.org/git/qemu-web.git | ||||
|   git clone git://git.qemu.org/qemu-web.git | ||||
|   https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/ | ||||
|  | ||||
| A 'git-publish' utility was created to make above process less | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| obj-$(CONFIG_SOFTMMU) += accel.o | ||||
| obj-$(CONFIG_KVM) += kvm/ | ||||
| obj-y += kvm/ | ||||
| obj-$(CONFIG_TCG) += tcg/ | ||||
| obj-y += stubs/ | ||||
|   | ||||
| @@ -34,7 +34,6 @@ | ||||
| #include "qom/object.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "qemu/option.h" | ||||
| #include "qapi/error.h" | ||||
|  | ||||
| static const TypeInfo accel_type = { | ||||
|     .name = TYPE_ACCEL, | ||||
| @@ -69,10 +68,10 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms) | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| void configure_accelerator(MachineState *ms, const char *progname) | ||||
| void configure_accelerator(MachineState *ms) | ||||
| { | ||||
|     const char *accel; | ||||
|     char **accel_list, **tmp; | ||||
|     const char *accel, *p; | ||||
|     char buf[10]; | ||||
|     int ret; | ||||
|     bool accel_initialised = false; | ||||
|     bool init_failed = false; | ||||
| @@ -80,26 +79,17 @@ void configure_accelerator(MachineState *ms, const char *progname) | ||||
|  | ||||
|     accel = qemu_opt_get(qemu_get_machine_opts(), "accel"); | ||||
|     if (accel == NULL) { | ||||
|         /* Select the default accelerator */ | ||||
|         int pnlen = strlen(progname); | ||||
|         if (pnlen >= 3 && g_str_equal(&progname[pnlen - 3], "kvm")) { | ||||
|             /* If the program name ends with "kvm", we prefer KVM */ | ||||
|             accel = "kvm:tcg"; | ||||
|         } else { | ||||
| #if defined(CONFIG_TCG) | ||||
|             accel = "tcg"; | ||||
| #elif defined(CONFIG_KVM) | ||||
|             accel = "kvm"; | ||||
| #else | ||||
| #error "No default accelerator available" | ||||
| #endif | ||||
|         } | ||||
|         /* Use the default "accelerator", tcg */ | ||||
|         accel = "tcg"; | ||||
|     } | ||||
|  | ||||
|     accel_list = g_strsplit(accel, ":", 0); | ||||
|  | ||||
|     for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) { | ||||
|         acc = accel_find(*tmp); | ||||
|     p = accel; | ||||
|     while (!accel_initialised && *p != '\0') { | ||||
|         if (*p == ':') { | ||||
|             p++; | ||||
|         } | ||||
|         p = get_opt_name(buf, sizeof(buf), p, ':'); | ||||
|         acc = accel_find(buf); | ||||
|         if (!acc) { | ||||
|             continue; | ||||
|         } | ||||
| @@ -117,7 +107,6 @@ void configure_accelerator(MachineState *ms, const char *progname) | ||||
|             accel_initialised = true; | ||||
|         } | ||||
|     } | ||||
|     g_strfreev(accel_list); | ||||
|  | ||||
|     if (!accel_initialised) { | ||||
|         if (!init_failed) { | ||||
| @@ -131,13 +120,10 @@ void configure_accelerator(MachineState *ms, const char *progname) | ||||
|     } | ||||
| } | ||||
|  | ||||
| void accel_setup_post(MachineState *ms) | ||||
| void accel_register_compat_props(AccelState *accel) | ||||
| { | ||||
|     AccelState *accel = ms->accelerator; | ||||
|     AccelClass *acc = ACCEL_GET_CLASS(accel); | ||||
|     if (acc->setup_post) { | ||||
|         acc->setup_post(ms, accel); | ||||
|     } | ||||
|     AccelClass *class = ACCEL_GET_CLASS(accel); | ||||
|     register_compat_props_array(class->global_props); | ||||
| } | ||||
|  | ||||
| static void register_accel_types(void) | ||||
|   | ||||
| @@ -1,2 +1 @@ | ||||
| obj-y += kvm-all.o | ||||
| obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o | ||||
| obj-$(CONFIG_KVM) += kvm-all.o | ||||
|   | ||||
| @@ -38,8 +38,6 @@ | ||||
| #include "qemu/event_notifier.h" | ||||
| #include "trace.h" | ||||
| #include "hw/irq.h" | ||||
| #include "sysemu/sev.h" | ||||
| #include "sysemu/balloon.h" | ||||
|  | ||||
| #include "hw/boards.h" | ||||
|  | ||||
| @@ -79,14 +77,13 @@ struct KVMState | ||||
|     int fd; | ||||
|     int vmfd; | ||||
|     int coalesced_mmio; | ||||
|     int coalesced_pio; | ||||
|     struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; | ||||
|     bool coalesced_flush_in_progress; | ||||
|     int vcpu_events; | ||||
|     int robust_singlestep; | ||||
|     int debugregs; | ||||
| #ifdef KVM_CAP_SET_GUEST_DEBUG | ||||
|     QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints; | ||||
|     struct kvm_sw_breakpoint_head kvm_sw_breakpoints; | ||||
| #endif | ||||
|     int many_ioeventfds; | ||||
|     int intx_set_mask; | ||||
| @@ -102,14 +99,10 @@ struct KVMState | ||||
|     int nr_allocated_irq_routes; | ||||
|     unsigned long *used_gsi_bitmap; | ||||
|     unsigned int gsi_count; | ||||
|     QTAILQ_HEAD(, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE]; | ||||
|     QTAILQ_HEAD(msi_hashtab, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE]; | ||||
| #endif | ||||
|     KVMMemoryListener memory_listener; | ||||
|     QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus; | ||||
|  | ||||
|     /* memory encryption */ | ||||
|     void *memcrypt_handle; | ||||
|     int (*memcrypt_encrypt_data)(void *handle, uint8_t *ptr, uint64_t len); | ||||
| }; | ||||
|  | ||||
| KVMState *kvm_state; | ||||
| @@ -145,26 +138,6 @@ int kvm_get_max_memslots(void) | ||||
|     return s->nr_slots; | ||||
| } | ||||
|  | ||||
| bool kvm_memcrypt_enabled(void) | ||||
| { | ||||
|     if (kvm_state && kvm_state->memcrypt_handle) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len) | ||||
| { | ||||
|     if (kvm_state->memcrypt_handle && | ||||
|         kvm_state->memcrypt_encrypt_data) { | ||||
|         return kvm_state->memcrypt_encrypt_data(kvm_state->memcrypt_handle, | ||||
|                                               ptr, len); | ||||
|     } | ||||
|  | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml) | ||||
| { | ||||
|     KVMState *s = kvm_state; | ||||
| @@ -258,7 +231,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new) | ||||
| static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot) | ||||
| { | ||||
|     KVMState *s = kvm_state; | ||||
|     struct kvm_userspace_memory_region mem; | ||||
| @@ -269,7 +242,7 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo | ||||
|     mem.userspace_addr = (unsigned long)slot->ram; | ||||
|     mem.flags = slot->flags; | ||||
|  | ||||
|     if (slot->memory_size && !new && (mem.flags ^ slot->old_flags) & KVM_MEM_READONLY) { | ||||
|     if (slot->memory_size && mem.flags & KVM_MEM_READONLY) { | ||||
|         /* Set the slot size to 0 before setting the slot to the desired | ||||
|          * value. This is needed based on KVM commit 75d61fbc. */ | ||||
|         mem.memory_size = 0; | ||||
| @@ -277,7 +250,6 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo | ||||
|     } | ||||
|     mem.memory_size = slot->memory_size; | ||||
|     ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); | ||||
|     slot->old_flags = mem.flags; | ||||
|     trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr, | ||||
|                               mem.memory_size, mem.userspace_addr, ret); | ||||
|     return ret; | ||||
| @@ -394,14 +366,17 @@ static int kvm_mem_flags(MemoryRegion *mr) | ||||
| static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem, | ||||
|                                  MemoryRegion *mr) | ||||
| { | ||||
|     int old_flags; | ||||
|  | ||||
|     old_flags = mem->flags; | ||||
|     mem->flags = kvm_mem_flags(mr); | ||||
|  | ||||
|     /* If nothing changed effectively, no need to issue ioctl */ | ||||
|     if (mem->flags == mem->old_flags) { | ||||
|     if (mem->flags == old_flags) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     return kvm_set_user_memory_region(kml, mem, false); | ||||
|     return kvm_set_user_memory_region(kml, mem); | ||||
| } | ||||
|  | ||||
| static int kvm_section_update_flags(KVMMemoryListener *kml, | ||||
| @@ -561,45 +536,6 @@ static void kvm_uncoalesce_mmio_region(MemoryListener *listener, | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void kvm_coalesce_pio_add(MemoryListener *listener, | ||||
|                                 MemoryRegionSection *section, | ||||
|                                 hwaddr start, hwaddr size) | ||||
| { | ||||
|     KVMState *s = kvm_state; | ||||
|  | ||||
|     if (s->coalesced_pio) { | ||||
|         struct kvm_coalesced_mmio_zone zone; | ||||
|  | ||||
|         zone.addr = start; | ||||
|         zone.size = size; | ||||
|         zone.pio = 1; | ||||
|  | ||||
|         (void)kvm_vm_ioctl(s, KVM_REGISTER_COALESCED_MMIO, &zone); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void kvm_coalesce_pio_del(MemoryListener *listener, | ||||
|                                 MemoryRegionSection *section, | ||||
|                                 hwaddr start, hwaddr size) | ||||
| { | ||||
|     KVMState *s = kvm_state; | ||||
|  | ||||
|     if (s->coalesced_pio) { | ||||
|         struct kvm_coalesced_mmio_zone zone; | ||||
|  | ||||
|         zone.addr = start; | ||||
|         zone.size = size; | ||||
|         zone.pio = 1; | ||||
|  | ||||
|         (void)kvm_vm_ioctl(s, KVM_UNREGISTER_COALESCED_MMIO, &zone); | ||||
|      } | ||||
| } | ||||
|  | ||||
| static MemoryListener kvm_coalesced_pio_listener = { | ||||
|     .coalesced_io_add = kvm_coalesce_pio_add, | ||||
|     .coalesced_io_del = kvm_coalesce_pio_del, | ||||
| }; | ||||
|  | ||||
| int kvm_check_extension(KVMState *s, unsigned int extension) | ||||
| { | ||||
|     int ret; | ||||
| @@ -794,8 +730,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, | ||||
|  | ||||
|         /* unregister the slot */ | ||||
|         mem->memory_size = 0; | ||||
|         mem->flags = 0; | ||||
|         err = kvm_set_user_memory_region(kml, mem, false); | ||||
|         err = kvm_set_user_memory_region(kml, mem); | ||||
|         if (err) { | ||||
|             fprintf(stderr, "%s: error unregistering slot: %s\n", | ||||
|                     __func__, strerror(-err)); | ||||
| @@ -811,7 +746,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, | ||||
|     mem->ram = ram; | ||||
|     mem->flags = kvm_mem_flags(mr); | ||||
|  | ||||
|     err = kvm_set_user_memory_region(kml, mem, true); | ||||
|     err = kvm_set_user_memory_region(kml, mem); | ||||
|     if (err) { | ||||
|         fprintf(stderr, "%s: error registering slot: %s\n", __func__, | ||||
|                 strerror(-err)); | ||||
| @@ -1656,8 +1591,6 @@ static int kvm_init(MachineState *ms) | ||||
|     } | ||||
|  | ||||
|     s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO); | ||||
|     s->coalesced_pio = s->coalesced_mmio && | ||||
|                        kvm_check_extension(s, KVM_CAP_COALESCED_PIO); | ||||
|  | ||||
| #ifdef KVM_CAP_VCPU_EVENTS | ||||
|     s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS); | ||||
| @@ -1681,8 +1614,10 @@ static int kvm_init(MachineState *ms) | ||||
|         s->irq_set_ioctl = KVM_IRQ_LINE_STATUS; | ||||
|     } | ||||
|  | ||||
| #ifdef KVM_CAP_READONLY_MEM | ||||
|     kvm_readonly_mem_allowed = | ||||
|         (kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0); | ||||
| #endif | ||||
|  | ||||
|     kvm_eventfds_allowed = | ||||
|         (kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0); | ||||
| @@ -1701,20 +1636,6 @@ static int kvm_init(MachineState *ms) | ||||
|  | ||||
|     kvm_state = s; | ||||
|  | ||||
|     /* | ||||
|      * if memory encryption object is specified then initialize the memory | ||||
|      * encryption context. | ||||
|      */ | ||||
|     if (ms->memory_encryption) { | ||||
|         kvm_state->memcrypt_handle = sev_guest_init(ms->memory_encryption); | ||||
|         if (!kvm_state->memcrypt_handle) { | ||||
|             ret = -1; | ||||
|             goto err; | ||||
|         } | ||||
|  | ||||
|         kvm_state->memcrypt_encrypt_data = sev_encrypt_data; | ||||
|     } | ||||
|  | ||||
|     ret = kvm_arch_init(ms, s); | ||||
|     if (ret < 0) { | ||||
|         goto err; | ||||
| @@ -1728,22 +1649,17 @@ static int kvm_init(MachineState *ms) | ||||
|         s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add; | ||||
|         s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del; | ||||
|     } | ||||
|     s->memory_listener.listener.coalesced_io_add = kvm_coalesce_mmio_region; | ||||
|     s->memory_listener.listener.coalesced_io_del = kvm_uncoalesce_mmio_region; | ||||
|     s->memory_listener.listener.coalesced_mmio_add = kvm_coalesce_mmio_region; | ||||
|     s->memory_listener.listener.coalesced_mmio_del = kvm_uncoalesce_mmio_region; | ||||
|  | ||||
|     kvm_memory_listener_register(s, &s->memory_listener, | ||||
|                                  &address_space_memory, 0); | ||||
|     memory_listener_register(&kvm_io_listener, | ||||
|                              &address_space_io); | ||||
|     memory_listener_register(&kvm_coalesced_pio_listener, | ||||
|                              &address_space_io); | ||||
|  | ||||
|     s->many_ioeventfds = kvm_check_many_ioeventfds(); | ||||
|  | ||||
|     s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU); | ||||
|     if (!s->sync_mmu) { | ||||
|         qemu_balloon_inhibit(true); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
|  | ||||
| @@ -1822,13 +1738,7 @@ void kvm_flush_coalesced_mmio_buffer(void) | ||||
|  | ||||
|             ent = &ring->coalesced_mmio[ring->first]; | ||||
|  | ||||
|             if (ent->pio == 1) { | ||||
|                 address_space_rw(&address_space_io, ent->phys_addr, | ||||
|                                  MEMTXATTRS_UNSPECIFIED, ent->data, | ||||
|                                  ent->len, true); | ||||
|             } else { | ||||
|                 cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len); | ||||
|             } | ||||
|             cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len); | ||||
|             smp_wmb(); | ||||
|             ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX; | ||||
|         } | ||||
|   | ||||
| @@ -1,26 +0,0 @@ | ||||
| /* | ||||
|  * QEMU SEV stub | ||||
|  * | ||||
|  * Copyright Advanced Micro Devices 2018 | ||||
|  * | ||||
|  * Authors: | ||||
|  *      Brijesh Singh <brijesh.singh@amd.com> | ||||
|  * | ||||
|  * 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/sev.h" | ||||
|  | ||||
| int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len) | ||||
| { | ||||
|     abort(); | ||||
| } | ||||
|  | ||||
| void *sev_guest_init(const char *id) | ||||
| { | ||||
|     return NULL; | ||||
| } | ||||
| @@ -105,16 +105,6 @@ int kvm_on_sigbus(int code, void *addr) | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| bool kvm_memcrypt_enabled(void) | ||||
| { | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len) | ||||
| { | ||||
|   return 1; | ||||
| } | ||||
|  | ||||
| #ifndef CONFIG_USER_ONLY | ||||
| int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev) | ||||
| { | ||||
|   | ||||
| @@ -21,6 +21,10 @@ void tb_flush(CPUState *cpu) | ||||
| { | ||||
| } | ||||
|  | ||||
| void tb_unlock(void) | ||||
| { | ||||
| } | ||||
|  | ||||
| void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) | ||||
| { | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| @@ -18,37 +18,26 @@ | ||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #include "trace/mem.h" | ||||
|  | ||||
| #if DATA_SIZE == 16 | ||||
| # define SUFFIX     o | ||||
| # define DATA_TYPE  Int128 | ||||
| # define BSWAP      bswap128 | ||||
| # define SHIFT      4 | ||||
| #elif DATA_SIZE == 8 | ||||
| # define SUFFIX     q | ||||
| # define DATA_TYPE  uint64_t | ||||
| # define SDATA_TYPE int64_t | ||||
| # define BSWAP      bswap64 | ||||
| # define SHIFT      3 | ||||
| #elif DATA_SIZE == 4 | ||||
| # define SUFFIX     l | ||||
| # define DATA_TYPE  uint32_t | ||||
| # define SDATA_TYPE int32_t | ||||
| # define BSWAP      bswap32 | ||||
| # define SHIFT      2 | ||||
| #elif DATA_SIZE == 2 | ||||
| # define SUFFIX     w | ||||
| # define DATA_TYPE  uint16_t | ||||
| # define SDATA_TYPE int16_t | ||||
| # define BSWAP      bswap16 | ||||
| # define SHIFT      1 | ||||
| #elif DATA_SIZE == 1 | ||||
| # define SUFFIX     b | ||||
| # define DATA_TYPE  uint8_t | ||||
| # define SDATA_TYPE int8_t | ||||
| # define BSWAP | ||||
| # define SHIFT      0 | ||||
| #else | ||||
| # error unsupported data size | ||||
| #endif | ||||
| @@ -59,37 +48,14 @@ | ||||
| # define ABI_TYPE  uint32_t | ||||
| #endif | ||||
|  | ||||
| #define ATOMIC_TRACE_RMW do {                                           \ | ||||
|         uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \ | ||||
|                                                                         \ | ||||
|         trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \ | ||||
|         trace_guest_mem_before_exec(ENV_GET_CPU(env), addr,             \ | ||||
|                                     info | TRACE_MEM_ST);               \ | ||||
|     } while (0) | ||||
|  | ||||
| #define ATOMIC_TRACE_LD do {                                            \ | ||||
|         uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \ | ||||
|                                                                         \ | ||||
|         trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \ | ||||
|     } while (0) | ||||
|  | ||||
| # define ATOMIC_TRACE_ST do {                                           \ | ||||
|         uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true); \ | ||||
|                                                                         \ | ||||
|         trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \ | ||||
|     } while (0) | ||||
|  | ||||
| /* Define host-endian atomic operations.  Note that END is used within | ||||
|    the ATOMIC_NAME macro, and redefined below.  */ | ||||
| #if DATA_SIZE == 1 | ||||
| # define END | ||||
| # define MEND _be /* either le or be would be fine */ | ||||
| #elif defined(HOST_WORDS_BIGENDIAN) | ||||
| # define END  _be | ||||
| # define MEND _be | ||||
| #else | ||||
| # define END  _le | ||||
| # define MEND _le | ||||
| #endif | ||||
|  | ||||
| ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | ||||
| @@ -97,27 +63,17 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||
|     DATA_TYPE ret; | ||||
|  | ||||
|     ATOMIC_TRACE_RMW; | ||||
| #if DATA_SIZE == 16 | ||||
|     ret = atomic16_cmpxchg(haddr, cmpv, newv); | ||||
| #else | ||||
|     ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); | ||||
| #endif | ||||
|     DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| #if DATA_SIZE >= 16 | ||||
| #if HAVE_ATOMIC128 | ||||
| ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; | ||||
|  | ||||
|     ATOMIC_TRACE_LD; | ||||
|     val = atomic16_read(haddr); | ||||
|     __atomic_load(haddr, &val, __ATOMIC_RELAXED); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
|     return val; | ||||
| } | ||||
| @@ -127,22 +83,16 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||
|  | ||||
|     ATOMIC_TRACE_ST; | ||||
|     atomic16_set(haddr, val); | ||||
|     __atomic_store(haddr, &val, __ATOMIC_RELAXED); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
| } | ||||
| #endif | ||||
| #else | ||||
| ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, | ||||
|                            ABI_TYPE val EXTRA_ARGS) | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||
|     DATA_TYPE ret; | ||||
|  | ||||
|     ATOMIC_TRACE_RMW; | ||||
|     ret = atomic_xchg__nocheck(haddr, val); | ||||
|     DATA_TYPE ret = atomic_xchg__nocheck(haddr, val); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
|     return ret; | ||||
| } | ||||
| @@ -153,10 +103,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ | ||||
| {                                                                   \ | ||||
|     ATOMIC_MMU_DECLS;                                               \ | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \ | ||||
|     DATA_TYPE ret;                                                  \ | ||||
|                                                                     \ | ||||
|     ATOMIC_TRACE_RMW;                                               \ | ||||
|     ret = atomic_##X(haddr, val);                                   \ | ||||
|     DATA_TYPE ret = atomic_##X(haddr, val);                         \ | ||||
|     ATOMIC_MMU_CLEANUP;                                             \ | ||||
|     return ret;                                                     \ | ||||
| } | ||||
| @@ -171,48 +118,9 @@ GEN_ATOMIC_HELPER(or_fetch) | ||||
| GEN_ATOMIC_HELPER(xor_fetch) | ||||
|  | ||||
| #undef GEN_ATOMIC_HELPER | ||||
|  | ||||
| /* These helpers are, as a whole, full barriers.  Within the helper, | ||||
|  * the leading barrier is explicit and the trailing barrier is within | ||||
|  * cmpxchg primitive. | ||||
|  * | ||||
|  * Trace this load + RMW loop as a single RMW op. This way, regardless | ||||
|  * of CF_PARALLEL's value, we'll trace just a read and a write. | ||||
|  */ | ||||
| #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET)                \ | ||||
| ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ | ||||
|                         ABI_TYPE xval EXTRA_ARGS)                   \ | ||||
| {                                                                   \ | ||||
|     ATOMIC_MMU_DECLS;                                               \ | ||||
|     XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                          \ | ||||
|     XDATA_TYPE cmp, old, new, val = xval;                           \ | ||||
|                                                                     \ | ||||
|     ATOMIC_TRACE_RMW;                                               \ | ||||
|     smp_mb();                                                       \ | ||||
|     cmp = atomic_read__nocheck(haddr);                              \ | ||||
|     do {                                                            \ | ||||
|         old = cmp; new = FN(old, val);                              \ | ||||
|         cmp = atomic_cmpxchg__nocheck(haddr, old, new);             \ | ||||
|     } while (cmp != old);                                           \ | ||||
|     ATOMIC_MMU_CLEANUP;                                             \ | ||||
|     return RET;                                                     \ | ||||
| } | ||||
|  | ||||
| GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old) | ||||
| GEN_ATOMIC_HELPER_FN(fetch_umin, MIN,  DATA_TYPE, old) | ||||
| GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old) | ||||
| GEN_ATOMIC_HELPER_FN(fetch_umax, MAX,  DATA_TYPE, old) | ||||
|  | ||||
| GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new) | ||||
| GEN_ATOMIC_HELPER_FN(umin_fetch, MIN,  DATA_TYPE, new) | ||||
| GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new) | ||||
| GEN_ATOMIC_HELPER_FN(umax_fetch, MAX,  DATA_TYPE, new) | ||||
|  | ||||
| #undef GEN_ATOMIC_HELPER_FN | ||||
| #endif /* DATA SIZE >= 16 */ | ||||
|  | ||||
| #undef END | ||||
| #undef MEND | ||||
|  | ||||
| #if DATA_SIZE > 1 | ||||
|  | ||||
| @@ -220,10 +128,8 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX,  DATA_TYPE, new) | ||||
|    within the ATOMIC_NAME macro.  */ | ||||
| #ifdef HOST_WORDS_BIGENDIAN | ||||
| # define END  _le | ||||
| # define MEND _le | ||||
| #else | ||||
| # define END  _be | ||||
| # define MEND _be | ||||
| #endif | ||||
|  | ||||
| ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | ||||
| @@ -231,27 +137,17 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||
|     DATA_TYPE ret; | ||||
|  | ||||
|     ATOMIC_TRACE_RMW; | ||||
| #if DATA_SIZE == 16 | ||||
|     ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv)); | ||||
| #else | ||||
|     ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); | ||||
| #endif | ||||
|     DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
|     return BSWAP(ret); | ||||
| } | ||||
|  | ||||
| #if DATA_SIZE >= 16 | ||||
| #if HAVE_ATOMIC128 | ||||
| ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; | ||||
|  | ||||
|     ATOMIC_TRACE_LD; | ||||
|     val = atomic16_read(haddr); | ||||
|     __atomic_load(haddr, &val, __ATOMIC_RELAXED); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
|     return BSWAP(val); | ||||
| } | ||||
| @@ -261,23 +157,17 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||
|  | ||||
|     ATOMIC_TRACE_ST; | ||||
|     val = BSWAP(val); | ||||
|     atomic16_set(haddr, val); | ||||
|     __atomic_store(haddr, &val, __ATOMIC_RELAXED); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
| } | ||||
| #endif | ||||
| #else | ||||
| ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, | ||||
|                            ABI_TYPE val EXTRA_ARGS) | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||
|     ABI_TYPE ret; | ||||
|  | ||||
|     ATOMIC_TRACE_RMW; | ||||
|     ret = atomic_xchg__nocheck(haddr, BSWAP(val)); | ||||
|     ABI_TYPE ret = atomic_xchg__nocheck(haddr, BSWAP(val)); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
|     return BSWAP(ret); | ||||
| } | ||||
| @@ -288,10 +178,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ | ||||
| {                                                                   \ | ||||
|     ATOMIC_MMU_DECLS;                                               \ | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \ | ||||
|     DATA_TYPE ret;                                                  \ | ||||
|                                                                     \ | ||||
|     ATOMIC_TRACE_RMW;                                               \ | ||||
|     ret = atomic_##X(haddr, BSWAP(val));                            \ | ||||
|     DATA_TYPE ret = atomic_##X(haddr, BSWAP(val));                  \ | ||||
|     ATOMIC_MMU_CLEANUP;                                             \ | ||||
|     return BSWAP(ret);                                              \ | ||||
| } | ||||
| @@ -305,64 +192,54 @@ GEN_ATOMIC_HELPER(xor_fetch) | ||||
|  | ||||
| #undef GEN_ATOMIC_HELPER | ||||
|  | ||||
| /* These helpers are, as a whole, full barriers.  Within the helper, | ||||
|  * the leading barrier is explicit and the trailing barrier is within | ||||
|  * cmpxchg primitive. | ||||
|  * | ||||
|  * Trace this load + RMW loop as a single RMW op. This way, regardless | ||||
|  * of CF_PARALLEL's value, we'll trace just a read and a write. | ||||
|  */ | ||||
| #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET)                \ | ||||
| ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ | ||||
|                         ABI_TYPE xval EXTRA_ARGS)                   \ | ||||
| {                                                                   \ | ||||
|     ATOMIC_MMU_DECLS;                                               \ | ||||
|     XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                          \ | ||||
|     XDATA_TYPE ldo, ldn, old, new, val = xval;                      \ | ||||
|                                                                     \ | ||||
|     ATOMIC_TRACE_RMW;                                               \ | ||||
|     smp_mb();                                                       \ | ||||
|     ldn = atomic_read__nocheck(haddr);                              \ | ||||
|     do {                                                            \ | ||||
|         ldo = ldn; old = BSWAP(ldo); new = FN(old, val);            \ | ||||
|         ldn = atomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new));      \ | ||||
|     } while (ldo != ldn);                                           \ | ||||
|     ATOMIC_MMU_CLEANUP;                                             \ | ||||
|     return RET;                                                     \ | ||||
| } | ||||
|  | ||||
| GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old) | ||||
| GEN_ATOMIC_HELPER_FN(fetch_umin, MIN,  DATA_TYPE, old) | ||||
| GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old) | ||||
| GEN_ATOMIC_HELPER_FN(fetch_umax, MAX,  DATA_TYPE, old) | ||||
|  | ||||
| GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new) | ||||
| GEN_ATOMIC_HELPER_FN(umin_fetch, MIN,  DATA_TYPE, new) | ||||
| GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new) | ||||
| GEN_ATOMIC_HELPER_FN(umax_fetch, MAX,  DATA_TYPE, new) | ||||
|  | ||||
| /* Note that for addition, we need to use a separate cmpxchg loop instead | ||||
|    of bswaps for the reverse-host-endian helpers.  */ | ||||
| #define ADD(X, Y)   (X + Y) | ||||
| GEN_ATOMIC_HELPER_FN(fetch_add, ADD, DATA_TYPE, old) | ||||
| GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new) | ||||
| #undef ADD | ||||
| ABI_TYPE ATOMIC_NAME(fetch_add)(CPUArchState *env, target_ulong addr, | ||||
|                          ABI_TYPE val EXTRA_ARGS) | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||
|     DATA_TYPE ldo, ldn, ret, sto; | ||||
|  | ||||
| #undef GEN_ATOMIC_HELPER_FN | ||||
|     ldo = atomic_read__nocheck(haddr); | ||||
|     while (1) { | ||||
|         ret = BSWAP(ldo); | ||||
|         sto = BSWAP(ret + val); | ||||
|         ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto); | ||||
|         if (ldn == ldo) { | ||||
|             ATOMIC_MMU_CLEANUP; | ||||
|             return ret; | ||||
|         } | ||||
|         ldo = ldn; | ||||
|     } | ||||
| } | ||||
|  | ||||
| ABI_TYPE ATOMIC_NAME(add_fetch)(CPUArchState *env, target_ulong addr, | ||||
|                          ABI_TYPE val EXTRA_ARGS) | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||
|     DATA_TYPE ldo, ldn, ret, sto; | ||||
|  | ||||
|     ldo = atomic_read__nocheck(haddr); | ||||
|     while (1) { | ||||
|         ret = BSWAP(ldo) + val; | ||||
|         sto = BSWAP(ret); | ||||
|         ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto); | ||||
|         if (ldn == ldo) { | ||||
|             ATOMIC_MMU_CLEANUP; | ||||
|             return ret; | ||||
|         } | ||||
|         ldo = ldn; | ||||
|     } | ||||
| } | ||||
| #endif /* DATA_SIZE >= 16 */ | ||||
|  | ||||
| #undef END | ||||
| #undef MEND | ||||
| #endif /* DATA_SIZE > 1 */ | ||||
|  | ||||
| #undef ATOMIC_TRACE_ST | ||||
| #undef ATOMIC_TRACE_LD | ||||
| #undef ATOMIC_TRACE_RMW | ||||
|  | ||||
| #undef BSWAP | ||||
| #undef ABI_TYPE | ||||
| #undef DATA_TYPE | ||||
| #undef SDATA_TYPE | ||||
| #undef SUFFIX | ||||
| #undef DATA_SIZE | ||||
| #undef SHIFT | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| @@ -27,8 +27,10 @@ bool tcg_allowed; | ||||
| /* exit the current TB, but without causing any exception to be raised */ | ||||
| void cpu_loop_exit_noexc(CPUState *cpu) | ||||
| { | ||||
|     /* XXX: restore cpu registers saved in host registers */ | ||||
|  | ||||
|     cpu->exception_index = -1; | ||||
|     cpu_loop_exit(cpu); | ||||
|     siglongjmp(cpu->jmp_env, 1); | ||||
| } | ||||
|  | ||||
| #if defined(CONFIG_SOFTMMU) | ||||
| @@ -63,17 +65,15 @@ void cpu_reloading_memory_map(void) | ||||
|  | ||||
| void cpu_loop_exit(CPUState *cpu) | ||||
| { | ||||
|     /* Undo the setting in cpu_tb_exec.  */ | ||||
|     cpu->can_do_io = 1; | ||||
|     siglongjmp(cpu->jmp_env, 1); | ||||
| } | ||||
|  | ||||
| void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) | ||||
| { | ||||
|     if (pc) { | ||||
|         cpu_restore_state(cpu, pc, true); | ||||
|         cpu_restore_state(cpu, pc); | ||||
|     } | ||||
|     cpu_loop_exit(cpu); | ||||
|     siglongjmp(cpu->jmp_env, 1); | ||||
| } | ||||
|  | ||||
| void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc) | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| @@ -25,6 +25,7 @@ | ||||
| #include "qemu/atomic.h" | ||||
| #include "sysemu/qtest.h" | ||||
| #include "qemu/timer.h" | ||||
| #include "exec/address-spaces.h" | ||||
| #include "qemu/rcu.h" | ||||
| #include "exec/tb-hash.h" | ||||
| #include "exec/tb-lookup.h" | ||||
| @@ -155,14 +156,11 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb) | ||||
|     if (qemu_loglevel_mask(CPU_LOG_TB_CPU) | ||||
|         && qemu_log_in_addr_range(itb->pc)) { | ||||
|         qemu_log_lock(); | ||||
|         int flags = 0; | ||||
|         if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) { | ||||
|             flags |= CPU_DUMP_FPU; | ||||
|         } | ||||
| #if defined(TARGET_I386) | ||||
|         flags |= CPU_DUMP_CCOP; | ||||
|         log_cpu_state(cpu, CPU_DUMP_CCOP); | ||||
| #else | ||||
|         log_cpu_state(cpu, 0); | ||||
| #endif | ||||
|         log_cpu_state(cpu, flags); | ||||
|         qemu_log_unlock(); | ||||
|     } | ||||
| #endif /* DEBUG_DISAS */ | ||||
| @@ -212,20 +210,20 @@ static void cpu_exec_nocache(CPUState *cpu, int max_cycles, | ||||
|        We only end up here when an existing TB is too long.  */ | ||||
|     cflags |= MIN(max_cycles, CF_COUNT_MASK); | ||||
|  | ||||
|     mmap_lock(); | ||||
|     tb_lock(); | ||||
|     tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, | ||||
|                      orig_tb->flags, cflags); | ||||
|     tb->orig_tb = orig_tb; | ||||
|     mmap_unlock(); | ||||
|     tb_unlock(); | ||||
|  | ||||
|     /* execute the generated code */ | ||||
|     trace_exec_tb_nocache(tb, tb->pc); | ||||
|     cpu_tb_exec(cpu, tb); | ||||
|  | ||||
|     mmap_lock(); | ||||
|     tb_lock(); | ||||
|     tb_phys_invalidate(tb, -1); | ||||
|     mmap_unlock(); | ||||
|     tcg_tb_remove(tb); | ||||
|     tb_remove(tb); | ||||
|     tb_unlock(); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @@ -244,7 +242,12 @@ void cpu_exec_step_atomic(CPUState *cpu) | ||||
|         tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); | ||||
|         if (tb == NULL) { | ||||
|             mmap_lock(); | ||||
|             tb = tb_gen_code(cpu, pc, cs_base, flags, cflags); | ||||
|             tb_lock(); | ||||
|             tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask); | ||||
|             if (likely(tb == NULL)) { | ||||
|                 tb = tb_gen_code(cpu, pc, cs_base, flags, cflags); | ||||
|             } | ||||
|             tb_unlock(); | ||||
|             mmap_unlock(); | ||||
|         } | ||||
|  | ||||
| @@ -259,14 +262,15 @@ void cpu_exec_step_atomic(CPUState *cpu) | ||||
|         cpu_tb_exec(cpu, tb); | ||||
|         cc->cpu_exec_exit(cpu); | ||||
|     } else { | ||||
|         /* | ||||
|         /* We may have exited due to another problem here, so we need | ||||
|          * to reset any tb_locks we may have taken but didn't release. | ||||
|          * The mmap_lock is dropped by tb_gen_code if it runs out of | ||||
|          * memory. | ||||
|          */ | ||||
| #ifndef CONFIG_SOFTMMU | ||||
|         tcg_debug_assert(!have_mmap_lock()); | ||||
| #endif | ||||
|         assert_no_pages_locked(); | ||||
|         tb_lock_reset(); | ||||
|     } | ||||
|  | ||||
|     if (in_exclusive_region) { | ||||
| @@ -289,7 +293,7 @@ struct tb_desc { | ||||
|     uint32_t trace_vcpu_dstate; | ||||
| }; | ||||
|  | ||||
| static bool tb_lookup_cmp(const void *p, const void *d) | ||||
| static bool tb_cmp(const void *p, const void *d) | ||||
| { | ||||
|     const TranslationBlock *tb = p; | ||||
|     const struct tb_desc *desc = d; | ||||
| @@ -325,9 +329,6 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, | ||||
|     struct tb_desc desc; | ||||
|     uint32_t h; | ||||
|  | ||||
|     cf_mask &= ~CF_CLUSTER_MASK; | ||||
|     cf_mask |= cpu->cluster_index << CF_CLUSTER_SHIFT; | ||||
|  | ||||
|     desc.env = (CPUArchState *)cpu->env_ptr; | ||||
|     desc.cs_base = cs_base; | ||||
|     desc.flags = flags; | ||||
| @@ -335,12 +336,9 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, | ||||
|     desc.trace_vcpu_dstate = *cpu->trace_dstate; | ||||
|     desc.pc = pc; | ||||
|     phys_pc = get_page_addr_code(desc.env, pc); | ||||
|     if (phys_pc == -1) { | ||||
|         return NULL; | ||||
|     } | ||||
|     desc.phys_page1 = phys_pc & TARGET_PAGE_MASK; | ||||
|     h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate); | ||||
|     return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp); | ||||
|     return qht_lookup(&tb_ctx.htable, tb_cmp, &desc, h); | ||||
| } | ||||
|  | ||||
| void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) | ||||
| @@ -354,43 +352,28 @@ void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Called with tb_lock held.  */ | ||||
| static inline void tb_add_jump(TranslationBlock *tb, int n, | ||||
|                                TranslationBlock *tb_next) | ||||
| { | ||||
|     uintptr_t old; | ||||
|  | ||||
|     assert(n < ARRAY_SIZE(tb->jmp_list_next)); | ||||
|     qemu_spin_lock(&tb_next->jmp_lock); | ||||
|  | ||||
|     /* make sure the destination TB is valid */ | ||||
|     if (tb_next->cflags & CF_INVALID) { | ||||
|         goto out_unlock_next; | ||||
|     if (tb->jmp_list_next[n]) { | ||||
|         /* Another thread has already done this while we were | ||||
|          * outside of the lock; nothing to do in this case */ | ||||
|         return; | ||||
|     } | ||||
|     /* Atomically claim the jump destination slot only if it was NULL */ | ||||
|     old = atomic_cmpxchg(&tb->jmp_dest[n], (uintptr_t)NULL, (uintptr_t)tb_next); | ||||
|     if (old) { | ||||
|         goto out_unlock_next; | ||||
|     } | ||||
|  | ||||
|     /* patch the native jump address */ | ||||
|     tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr); | ||||
|  | ||||
|     /* add in TB jmp list */ | ||||
|     tb->jmp_list_next[n] = tb_next->jmp_list_head; | ||||
|     tb_next->jmp_list_head = (uintptr_t)tb | n; | ||||
|  | ||||
|     qemu_spin_unlock(&tb_next->jmp_lock); | ||||
|  | ||||
|     qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc, | ||||
|                            "Linking TBs %p [" TARGET_FMT_lx | ||||
|                            "] index %d -> %p [" TARGET_FMT_lx "]\n", | ||||
|                            tb->tc.ptr, tb->pc, n, | ||||
|                            tb_next->tc.ptr, tb_next->pc); | ||||
|     return; | ||||
|  | ||||
|  out_unlock_next: | ||||
|     qemu_spin_unlock(&tb_next->jmp_lock); | ||||
|     return; | ||||
|     /* patch the native jump address */ | ||||
|     tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr); | ||||
|  | ||||
|     /* add in TB jmp circular list */ | ||||
|     tb->jmp_list_next[n] = tb_next->jmp_list_first; | ||||
|     tb_next->jmp_list_first = (uintptr_t)tb | n; | ||||
| } | ||||
|  | ||||
| static inline TranslationBlock *tb_find(CPUState *cpu, | ||||
| @@ -400,11 +383,27 @@ static inline TranslationBlock *tb_find(CPUState *cpu, | ||||
|     TranslationBlock *tb; | ||||
|     target_ulong cs_base, pc; | ||||
|     uint32_t flags; | ||||
|     bool acquired_tb_lock = false; | ||||
|  | ||||
|     tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); | ||||
|     if (tb == NULL) { | ||||
|         /* mmap_lock is needed by tb_gen_code, and mmap_lock must be | ||||
|          * taken outside tb_lock. As system emulation is currently | ||||
|          * single threaded the locks are NOPs. | ||||
|          */ | ||||
|         mmap_lock(); | ||||
|         tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask); | ||||
|         tb_lock(); | ||||
|         acquired_tb_lock = true; | ||||
|  | ||||
|         /* There's a chance that our desired tb has been translated while | ||||
|          * taking the locks so we check again inside the lock. | ||||
|          */ | ||||
|         tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask); | ||||
|         if (likely(tb == NULL)) { | ||||
|             /* if no translated code available, then translate it now */ | ||||
|             tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask); | ||||
|         } | ||||
|  | ||||
|         mmap_unlock(); | ||||
|         /* We add the TB in the virtual pc hash table for the fast lookup */ | ||||
|         atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb); | ||||
| @@ -419,8 +418,17 @@ static inline TranslationBlock *tb_find(CPUState *cpu, | ||||
|     } | ||||
| #endif | ||||
|     /* See if we can patch the calling TB. */ | ||||
|     if (last_tb) { | ||||
|         tb_add_jump(last_tb, tb_exit, tb); | ||||
|     if (last_tb && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) { | ||||
|         if (!acquired_tb_lock) { | ||||
|             tb_lock(); | ||||
|             acquired_tb_lock = true; | ||||
|         } | ||||
|         if (!(tb->cflags & CF_INVALID)) { | ||||
|             tb_add_jump(last_tb, tb_exit, tb); | ||||
|         } | ||||
|     } | ||||
|     if (acquired_tb_lock) { | ||||
|         tb_unlock(); | ||||
|     } | ||||
|     return tb; | ||||
| } | ||||
| @@ -577,7 +585,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, | ||||
|         else { | ||||
|             if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { | ||||
|                 replay_interrupt(); | ||||
|                 cpu->exception_index = -1; | ||||
|                 *last_tb = NULL; | ||||
|             } | ||||
|             /* The target hook may have updated the 'cpu->interrupt_request'; | ||||
| @@ -599,9 +606,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, | ||||
|     if (unlikely(atomic_read(&cpu->exit_request) | ||||
|         || (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) { | ||||
|         atomic_set(&cpu->exit_request, 0); | ||||
|         if (cpu->exception_index == -1) { | ||||
|             cpu->exception_index = EXCP_INTERRUPT; | ||||
|         } | ||||
|         cpu->exception_index = EXCP_INTERRUPT; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
| @@ -696,9 +701,8 @@ int cpu_exec(CPUState *cpu) | ||||
|         g_assert(cpu == current_cpu); | ||||
|         g_assert(cc == CPU_GET_CLASS(cpu)); | ||||
| #endif /* buggy compiler */ | ||||
| #ifndef CONFIG_SOFTMMU | ||||
|         tcg_debug_assert(!have_mmap_lock()); | ||||
| #endif | ||||
|         cpu->can_do_io = 1; | ||||
|         tb_lock_reset(); | ||||
|         if (qemu_mutex_iothread_locked()) { | ||||
|             qemu_mutex_unlock_iothread(); | ||||
|         } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -11,7 +11,7 @@ | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| @@ -98,23 +98,19 @@ | ||||
| static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env, | ||||
|                                               size_t mmu_idx, size_t index, | ||||
|                                               target_ulong addr, | ||||
|                                               uintptr_t retaddr, | ||||
|                                               bool recheck, | ||||
|                                               MMUAccessType access_type) | ||||
|                                               uintptr_t retaddr) | ||||
| { | ||||
|     CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; | ||||
|     return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, recheck, | ||||
|                     access_type, DATA_SIZE); | ||||
|     return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, DATA_SIZE); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, | ||||
|                             TCGMemOpIdx oi, uintptr_t retaddr) | ||||
| { | ||||
|     uintptr_t mmu_idx = get_mmuidx(oi); | ||||
|     uintptr_t index = tlb_index(env, mmu_idx, addr); | ||||
|     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); | ||||
|     target_ulong tlb_addr = entry->ADDR_READ; | ||||
|     unsigned mmu_idx = get_mmuidx(oi); | ||||
|     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; | ||||
|     unsigned a_bits = get_alignment_bits(get_memop(oi)); | ||||
|     uintptr_t haddr; | ||||
|     DATA_TYPE res; | ||||
| @@ -125,12 +121,13 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, | ||||
|     } | ||||
|  | ||||
|     /* If the TLB entry is for a different page, reload and try again.  */ | ||||
|     if (!tlb_hit(tlb_addr, addr)) { | ||||
|     if ((addr & TARGET_PAGE_MASK) | ||||
|          != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { | ||||
|         if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { | ||||
|             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, | ||||
|                      mmu_idx, retaddr); | ||||
|         } | ||||
|         tlb_addr = entry->ADDR_READ; | ||||
|         tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; | ||||
|     } | ||||
|  | ||||
|     /* Handle an IO access.  */ | ||||
| @@ -141,9 +138,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, | ||||
|  | ||||
|         /* ??? Note that the io helpers always read data in the target | ||||
|            byte ordering.  We should push the LE/BE request down into io.  */ | ||||
|         res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr, | ||||
|                                     tlb_addr & TLB_RECHECK, | ||||
|                                     READ_ACCESS_TYPE); | ||||
|         res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr); | ||||
|         res = TGT_LE(res); | ||||
|         return res; | ||||
|     } | ||||
| @@ -167,7 +162,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, | ||||
|         return res; | ||||
|     } | ||||
|  | ||||
|     haddr = addr + entry->addend; | ||||
|     haddr = addr + env->tlb_table[mmu_idx][index].addend; | ||||
| #if DATA_SIZE == 1 | ||||
|     res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr); | ||||
| #else | ||||
| @@ -180,10 +175,9 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, | ||||
| WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, | ||||
|                             TCGMemOpIdx oi, uintptr_t retaddr) | ||||
| { | ||||
|     uintptr_t mmu_idx = get_mmuidx(oi); | ||||
|     uintptr_t index = tlb_index(env, mmu_idx, addr); | ||||
|     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); | ||||
|     target_ulong tlb_addr = entry->ADDR_READ; | ||||
|     unsigned mmu_idx = get_mmuidx(oi); | ||||
|     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; | ||||
|     unsigned a_bits = get_alignment_bits(get_memop(oi)); | ||||
|     uintptr_t haddr; | ||||
|     DATA_TYPE res; | ||||
| @@ -194,12 +188,13 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, | ||||
|     } | ||||
|  | ||||
|     /* If the TLB entry is for a different page, reload and try again.  */ | ||||
|     if (!tlb_hit(tlb_addr, addr)) { | ||||
|     if ((addr & TARGET_PAGE_MASK) | ||||
|          != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { | ||||
|         if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { | ||||
|             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, | ||||
|                      mmu_idx, retaddr); | ||||
|         } | ||||
|         tlb_addr = entry->ADDR_READ; | ||||
|         tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; | ||||
|     } | ||||
|  | ||||
|     /* Handle an IO access.  */ | ||||
| @@ -210,9 +205,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, | ||||
|  | ||||
|         /* ??? Note that the io helpers always read data in the target | ||||
|            byte ordering.  We should push the LE/BE request down into io.  */ | ||||
|         res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr, | ||||
|                                     tlb_addr & TLB_RECHECK, | ||||
|                                     READ_ACCESS_TYPE); | ||||
|         res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr); | ||||
|         res = TGT_BE(res); | ||||
|         return res; | ||||
|     } | ||||
| @@ -236,7 +229,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, | ||||
|         return res; | ||||
|     } | ||||
|  | ||||
|     haddr = addr + entry->addend; | ||||
|     haddr = addr + env->tlb_table[mmu_idx][index].addend; | ||||
|     res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr); | ||||
|     return res; | ||||
| } | ||||
| @@ -266,21 +259,18 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env, | ||||
|                                           size_t mmu_idx, size_t index, | ||||
|                                           DATA_TYPE val, | ||||
|                                           target_ulong addr, | ||||
|                                           uintptr_t retaddr, | ||||
|                                           bool recheck) | ||||
|                                           uintptr_t retaddr) | ||||
| { | ||||
|     CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; | ||||
|     return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr, | ||||
|                      recheck, DATA_SIZE); | ||||
|     return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr, DATA_SIZE); | ||||
| } | ||||
|  | ||||
| void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||
|                        TCGMemOpIdx oi, uintptr_t retaddr) | ||||
| { | ||||
|     uintptr_t mmu_idx = get_mmuidx(oi); | ||||
|     uintptr_t index = tlb_index(env, mmu_idx, addr); | ||||
|     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); | ||||
|     target_ulong tlb_addr = tlb_addr_write(entry); | ||||
|     unsigned mmu_idx = get_mmuidx(oi); | ||||
|     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; | ||||
|     unsigned a_bits = get_alignment_bits(get_memop(oi)); | ||||
|     uintptr_t haddr; | ||||
|  | ||||
| @@ -290,12 +280,13 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||
|     } | ||||
|  | ||||
|     /* If the TLB entry is for a different page, reload and try again.  */ | ||||
|     if (!tlb_hit(tlb_addr, addr)) { | ||||
|     if ((addr & TARGET_PAGE_MASK) | ||||
|         != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { | ||||
|         if (!VICTIM_TLB_HIT(addr_write, addr)) { | ||||
|             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, | ||||
|                      mmu_idx, retaddr); | ||||
|         } | ||||
|         tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK; | ||||
|         tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK; | ||||
|     } | ||||
|  | ||||
|     /* Handle an IO access.  */ | ||||
| @@ -307,8 +298,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||
|         /* ??? Note that the io helpers always read data in the target | ||||
|            byte ordering.  We should push the LE/BE request down into io.  */ | ||||
|         val = TGT_LE(val); | ||||
|         glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, | ||||
|                                retaddr, tlb_addr & TLB_RECHECK); | ||||
|         glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -316,16 +306,16 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||
|     if (DATA_SIZE > 1 | ||||
|         && unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1 | ||||
|                      >= TARGET_PAGE_SIZE)) { | ||||
|         int i; | ||||
|         target_ulong page2; | ||||
|         CPUTLBEntry *entry2; | ||||
|         int i, index2; | ||||
|         target_ulong page2, tlb_addr2; | ||||
|     do_unaligned_access: | ||||
|         /* Ensure the second page is in the TLB.  Note that the first page | ||||
|            is already guaranteed to be filled, and that the second page | ||||
|            cannot evict the first.  */ | ||||
|         page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; | ||||
|         entry2 = tlb_entry(env, mmu_idx, page2); | ||||
|         if (!tlb_hit_page(tlb_addr_write(entry2), page2) | ||||
|         index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|         tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; | ||||
|         if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK)) | ||||
|             && !VICTIM_TLB_HIT(addr_write, page2)) { | ||||
|             tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE, | ||||
|                      mmu_idx, retaddr); | ||||
| @@ -343,7 +333,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     haddr = addr + entry->addend; | ||||
|     haddr = addr + env->tlb_table[mmu_idx][index].addend; | ||||
| #if DATA_SIZE == 1 | ||||
|     glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val); | ||||
| #else | ||||
| @@ -355,10 +345,9 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||
| void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||
|                        TCGMemOpIdx oi, uintptr_t retaddr) | ||||
| { | ||||
|     uintptr_t mmu_idx = get_mmuidx(oi); | ||||
|     uintptr_t index = tlb_index(env, mmu_idx, addr); | ||||
|     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); | ||||
|     target_ulong tlb_addr = tlb_addr_write(entry); | ||||
|     unsigned mmu_idx = get_mmuidx(oi); | ||||
|     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; | ||||
|     unsigned a_bits = get_alignment_bits(get_memop(oi)); | ||||
|     uintptr_t haddr; | ||||
|  | ||||
| @@ -368,12 +357,13 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||
|     } | ||||
|  | ||||
|     /* If the TLB entry is for a different page, reload and try again.  */ | ||||
|     if (!tlb_hit(tlb_addr, addr)) { | ||||
|     if ((addr & TARGET_PAGE_MASK) | ||||
|         != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { | ||||
|         if (!VICTIM_TLB_HIT(addr_write, addr)) { | ||||
|             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, | ||||
|                      mmu_idx, retaddr); | ||||
|         } | ||||
|         tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK; | ||||
|         tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK; | ||||
|     } | ||||
|  | ||||
|     /* Handle an IO access.  */ | ||||
| @@ -385,8 +375,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||
|         /* ??? Note that the io helpers always read data in the target | ||||
|            byte ordering.  We should push the LE/BE request down into io.  */ | ||||
|         val = TGT_BE(val); | ||||
|         glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr, | ||||
|                                tlb_addr & TLB_RECHECK); | ||||
|         glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -394,16 +383,16 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||
|     if (DATA_SIZE > 1 | ||||
|         && unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1 | ||||
|                      >= TARGET_PAGE_SIZE)) { | ||||
|         int i; | ||||
|         target_ulong page2; | ||||
|         CPUTLBEntry *entry2; | ||||
|         int i, index2; | ||||
|         target_ulong page2, tlb_addr2; | ||||
|     do_unaligned_access: | ||||
|         /* Ensure the second page is in the TLB.  Note that the first page | ||||
|            is already guaranteed to be filled, and that the second page | ||||
|            cannot evict the first.  */ | ||||
|         page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; | ||||
|         entry2 = tlb_entry(env, mmu_idx, page2); | ||||
|         if (!tlb_hit_page(tlb_addr_write(entry2), page2) | ||||
|         index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|         tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; | ||||
|         if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK)) | ||||
|             && !VICTIM_TLB_HIT(addr_write, page2)) { | ||||
|             tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE, | ||||
|                      mmu_idx, retaddr); | ||||
| @@ -421,7 +410,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     haddr = addr + entry->addend; | ||||
|     haddr = addr + env->tlb_table[mmu_idx][index].addend; | ||||
|     glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val); | ||||
| } | ||||
| #endif /* DATA_SIZE > 1 */ | ||||
|   | ||||
| @@ -51,7 +51,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask) | ||||
|     if (!qemu_cpu_is_self(cpu)) { | ||||
|         qemu_cpu_kick(cpu); | ||||
|     } else { | ||||
|         atomic_set(&cpu->icount_decr.u16.high, -1); | ||||
|         cpu->icount_decr.u16.high = -1; | ||||
|         if (use_icount && | ||||
|             !cpu->can_do_io | ||||
|             && (mask & ~old_mask) != 0) { | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| @@ -512,39 +512,6 @@ void HELPER(gvec_orc)(void *d, void *a, void *b, uint32_t desc) | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_nand)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { | ||||
|         *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) & *(vec64 *)(b + i)); | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_nor)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { | ||||
|         *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) | *(vec64 *)(b + i)); | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_eqv)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { | ||||
|         *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) ^ *(vec64 *)(b + i)); | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
| @@ -738,7 +705,7 @@ void HELPER(NAME)(void *d, void *a, void *b, uint32_t desc)                \ | ||||
| {                                                                          \ | ||||
|     intptr_t oprsz = simd_oprsz(desc);                                     \ | ||||
|     intptr_t i;                                                            \ | ||||
|     for (i = 0; i < oprsz; i += sizeof(TYPE)) {                            \ | ||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) {                           \ | ||||
|         *(TYPE *)(d + i) = DO_CMP0(*(TYPE *)(a + i) OP *(TYPE *)(b + i));  \ | ||||
|     }                                                                      \ | ||||
|     clear_high(d, oprsz, desc);                                            \ | ||||
| @@ -1028,227 +995,3 @@ void HELPER(gvec_ussub64)(void *d, void *a, void *b, uint32_t desc) | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_smin8)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(int8_t)) { | ||||
|         int8_t aa = *(int8_t *)(a + i); | ||||
|         int8_t bb = *(int8_t *)(b + i); | ||||
|         int8_t dd = aa < bb ? aa : bb; | ||||
|         *(int8_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_smin16)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(int16_t)) { | ||||
|         int16_t aa = *(int16_t *)(a + i); | ||||
|         int16_t bb = *(int16_t *)(b + i); | ||||
|         int16_t dd = aa < bb ? aa : bb; | ||||
|         *(int16_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_smin32)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(int32_t)) { | ||||
|         int32_t aa = *(int32_t *)(a + i); | ||||
|         int32_t bb = *(int32_t *)(b + i); | ||||
|         int32_t dd = aa < bb ? aa : bb; | ||||
|         *(int32_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_smin64)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(int64_t)) { | ||||
|         int64_t aa = *(int64_t *)(a + i); | ||||
|         int64_t bb = *(int64_t *)(b + i); | ||||
|         int64_t dd = aa < bb ? aa : bb; | ||||
|         *(int64_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_smax8)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(int8_t)) { | ||||
|         int8_t aa = *(int8_t *)(a + i); | ||||
|         int8_t bb = *(int8_t *)(b + i); | ||||
|         int8_t dd = aa > bb ? aa : bb; | ||||
|         *(int8_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_smax16)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(int16_t)) { | ||||
|         int16_t aa = *(int16_t *)(a + i); | ||||
|         int16_t bb = *(int16_t *)(b + i); | ||||
|         int16_t dd = aa > bb ? aa : bb; | ||||
|         *(int16_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_smax32)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(int32_t)) { | ||||
|         int32_t aa = *(int32_t *)(a + i); | ||||
|         int32_t bb = *(int32_t *)(b + i); | ||||
|         int32_t dd = aa > bb ? aa : bb; | ||||
|         *(int32_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_smax64)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(int64_t)) { | ||||
|         int64_t aa = *(int64_t *)(a + i); | ||||
|         int64_t bb = *(int64_t *)(b + i); | ||||
|         int64_t dd = aa > bb ? aa : bb; | ||||
|         *(int64_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_umin8)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(uint8_t)) { | ||||
|         uint8_t aa = *(uint8_t *)(a + i); | ||||
|         uint8_t bb = *(uint8_t *)(b + i); | ||||
|         uint8_t dd = aa < bb ? aa : bb; | ||||
|         *(uint8_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_umin16)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(uint16_t)) { | ||||
|         uint16_t aa = *(uint16_t *)(a + i); | ||||
|         uint16_t bb = *(uint16_t *)(b + i); | ||||
|         uint16_t dd = aa < bb ? aa : bb; | ||||
|         *(uint16_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_umin32)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(uint32_t)) { | ||||
|         uint32_t aa = *(uint32_t *)(a + i); | ||||
|         uint32_t bb = *(uint32_t *)(b + i); | ||||
|         uint32_t dd = aa < bb ? aa : bb; | ||||
|         *(uint32_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_umin64)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(uint64_t)) { | ||||
|         uint64_t aa = *(uint64_t *)(a + i); | ||||
|         uint64_t bb = *(uint64_t *)(b + i); | ||||
|         uint64_t dd = aa < bb ? aa : bb; | ||||
|         *(uint64_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_umax8)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(uint8_t)) { | ||||
|         uint8_t aa = *(uint8_t *)(a + i); | ||||
|         uint8_t bb = *(uint8_t *)(b + i); | ||||
|         uint8_t dd = aa > bb ? aa : bb; | ||||
|         *(uint8_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_umax16)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(uint16_t)) { | ||||
|         uint16_t aa = *(uint16_t *)(a + i); | ||||
|         uint16_t bb = *(uint16_t *)(b + i); | ||||
|         uint16_t dd = aa > bb ? aa : bb; | ||||
|         *(uint16_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_umax32)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(uint32_t)) { | ||||
|         uint32_t aa = *(uint32_t *)(a + i); | ||||
|         uint32_t bb = *(uint32_t *)(b + i); | ||||
|         uint32_t dd = aa > bb ? aa : bb; | ||||
|         *(uint32_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_umax64)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(uint64_t)) { | ||||
|         uint64_t aa = *(uint64_t *)(a + i); | ||||
|         uint64_t bb = *(uint64_t *)(b + i); | ||||
|         uint64_t dd = aa > bb ? aa : bb; | ||||
|         *(uint64_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|   | ||||
| @@ -125,19 +125,11 @@ GEN_ATOMIC_HELPERS(fetch_add) | ||||
| GEN_ATOMIC_HELPERS(fetch_and) | ||||
| GEN_ATOMIC_HELPERS(fetch_or) | ||||
| GEN_ATOMIC_HELPERS(fetch_xor) | ||||
| GEN_ATOMIC_HELPERS(fetch_smin) | ||||
| GEN_ATOMIC_HELPERS(fetch_umin) | ||||
| GEN_ATOMIC_HELPERS(fetch_smax) | ||||
| GEN_ATOMIC_HELPERS(fetch_umax) | ||||
|  | ||||
| GEN_ATOMIC_HELPERS(add_fetch) | ||||
| GEN_ATOMIC_HELPERS(and_fetch) | ||||
| GEN_ATOMIC_HELPERS(or_fetch) | ||||
| GEN_ATOMIC_HELPERS(xor_fetch) | ||||
| GEN_ATOMIC_HELPERS(smin_fetch) | ||||
| GEN_ATOMIC_HELPERS(umin_fetch) | ||||
| GEN_ATOMIC_HELPERS(smax_fetch) | ||||
| GEN_ATOMIC_HELPERS(umax_fetch) | ||||
|  | ||||
| GEN_ATOMIC_HELPERS(xchg) | ||||
|  | ||||
| @@ -200,26 +192,6 @@ DEF_HELPER_FLAGS_4(gvec_ussub16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_ussub32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_ussub64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
|  | ||||
| DEF_HELPER_FLAGS_4(gvec_smin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_smin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_smin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_smin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
|  | ||||
| DEF_HELPER_FLAGS_4(gvec_smax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_smax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_smax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_smax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
|  | ||||
| DEF_HELPER_FLAGS_4(gvec_umin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_umin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_umin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_umin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
|  | ||||
| DEF_HELPER_FLAGS_4(gvec_umax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_umax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_umax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_umax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
|  | ||||
| DEF_HELPER_FLAGS_3(gvec_neg8, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_3(gvec_neg16, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_3(gvec_neg32, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | ||||
| @@ -231,9 +203,6 @@ DEF_HELPER_FLAGS_4(gvec_or, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_xor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_andc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_orc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_nand, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_nor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_eqv, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
|  | ||||
| DEF_HELPER_FLAGS_4(gvec_ands, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_xors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -6,7 +6,7 @@ | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| @@ -23,13 +23,10 @@ | ||||
|  | ||||
|  | ||||
| /* translate-all.c */ | ||||
| struct page_collection *page_collection_lock(tb_page_addr_t start, | ||||
|                                              tb_page_addr_t end); | ||||
| void page_collection_unlock(struct page_collection *set); | ||||
| void tb_invalidate_phys_page_fast(struct page_collection *pages, | ||||
|                                   tb_page_addr_t start, int len); | ||||
| void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len); | ||||
| void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, | ||||
|                                    int is_cpu_write_access); | ||||
| void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end); | ||||
| void tb_check_watchpoint(CPUState *cpu); | ||||
|  | ||||
| #ifdef CONFIG_USER_ONLY | ||||
|   | ||||
| @@ -34,7 +34,7 @@ void translator_loop_temp_check(DisasContextBase *db) | ||||
| void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | ||||
|                      CPUState *cpu, TranslationBlock *tb) | ||||
| { | ||||
|     int bp_insn = 0; | ||||
|     int max_insns; | ||||
|  | ||||
|     /* Initialize DisasContext */ | ||||
|     db->tb = tb; | ||||
| @@ -45,18 +45,18 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | ||||
|     db->singlestep_enabled = cpu->singlestep_enabled; | ||||
|  | ||||
|     /* Instruction counting */ | ||||
|     db->max_insns = tb_cflags(db->tb) & CF_COUNT_MASK; | ||||
|     if (db->max_insns == 0) { | ||||
|         db->max_insns = CF_COUNT_MASK; | ||||
|     max_insns = tb_cflags(db->tb) & CF_COUNT_MASK; | ||||
|     if (max_insns == 0) { | ||||
|         max_insns = CF_COUNT_MASK; | ||||
|     } | ||||
|     if (db->max_insns > TCG_MAX_INSNS) { | ||||
|         db->max_insns = TCG_MAX_INSNS; | ||||
|     if (max_insns > TCG_MAX_INSNS) { | ||||
|         max_insns = TCG_MAX_INSNS; | ||||
|     } | ||||
|     if (db->singlestep_enabled || singlestep) { | ||||
|         db->max_insns = 1; | ||||
|         max_insns = 1; | ||||
|     } | ||||
|  | ||||
|     ops->init_disas_context(db, cpu); | ||||
|     max_insns = ops->init_disas_context(db, cpu, max_insns); | ||||
|     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */ | ||||
|  | ||||
|     /* Reset the temp count so that we can identify leaks */ | ||||
| @@ -73,13 +73,11 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | ||||
|         tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */ | ||||
|  | ||||
|         /* Pass breakpoint hits to target for further processing */ | ||||
|         if (!db->singlestep_enabled | ||||
|             && unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) { | ||||
|         if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) { | ||||
|             CPUBreakpoint *bp; | ||||
|             QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { | ||||
|                 if (bp->pc == db->pc_next) { | ||||
|                     if (ops->breakpoint_check(db, cpu, bp)) { | ||||
|                         bp_insn = 1; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
| @@ -97,8 +95,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | ||||
|            update db->pc_next and db->is_jmp to indicate what should be | ||||
|            done next -- either exiting this loop or locate the start of | ||||
|            the next instruction.  */ | ||||
|         if (db->num_insns == db->max_insns | ||||
|             && (tb_cflags(db->tb) & CF_LAST_IO)) { | ||||
|         if (db->num_insns == max_insns && (tb_cflags(db->tb) & CF_LAST_IO)) { | ||||
|             /* Accept I/O on the last instruction.  */ | ||||
|             gen_io_start(); | ||||
|             ops->translate_insn(db, cpu); | ||||
| @@ -114,7 +111,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | ||||
|  | ||||
|         /* Stop translation if the output buffer is full, | ||||
|            or we have executed all of the allowed instructions.  */ | ||||
|         if (tcg_op_buf_full() || db->num_insns >= db->max_insns) { | ||||
|         if (tcg_op_buf_full() || db->num_insns >= max_insns) { | ||||
|             db->is_jmp = DISAS_TOO_MANY; | ||||
|             break; | ||||
|         } | ||||
| @@ -122,7 +119,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | ||||
|  | ||||
|     /* Emit code to exit the TB, as indicated by db->is_jmp.  */ | ||||
|     ops->tb_stop(db, cpu); | ||||
|     gen_tb_end(db->tb, db->num_insns - bp_insn); | ||||
|     gen_tb_end(db->tb, db->num_insns); | ||||
|  | ||||
|     /* The disas_log hook may use these values rather than recompute.  */ | ||||
|     db->tb->size = db->pc_next - db->pc_first; | ||||
|   | ||||
| @@ -2,9 +2,6 @@ | ||||
| #include "qemu-common.h" | ||||
| #include "qom/cpu.h" | ||||
| #include "sysemu/replay.h" | ||||
| #include "sysemu/sysemu.h" | ||||
|  | ||||
| bool enable_cpu_pm = false; | ||||
|  | ||||
| void cpu_resume(CPUState *cpu) | ||||
| { | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| @@ -25,7 +25,6 @@ | ||||
| #include "exec/cpu_ldst.h" | ||||
| #include "translate-all.h" | ||||
| #include "exec/helper-proto.h" | ||||
| #include "qemu/atomic128.h" | ||||
|  | ||||
| #undef EAX | ||||
| #undef ECX | ||||
| @@ -169,7 +168,7 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, | ||||
|     } | ||||
|  | ||||
|     /* Now we have a real cpu fault.  */ | ||||
|     cpu_restore_state(cpu, pc, true); | ||||
|     cpu_restore_state(cpu, pc); | ||||
|  | ||||
|     sigprocmask(SIG_SETMASK, old_set, NULL); | ||||
|     cpu_loop_exit(cpu); | ||||
| @@ -479,66 +478,28 @@ int cpu_signal_handler(int host_signum, void *pinfo, | ||||
|  | ||||
| #elif defined(__aarch64__) | ||||
|  | ||||
| #ifndef ESR_MAGIC | ||||
| /* Pre-3.16 kernel headers don't have these, so provide fallback definitions */ | ||||
| #define ESR_MAGIC 0x45535201 | ||||
| struct esr_context { | ||||
|     struct _aarch64_ctx head; | ||||
|     uint64_t esr; | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc) | ||||
| { | ||||
|     return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved; | ||||
| } | ||||
|  | ||||
| static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr) | ||||
| { | ||||
|     return (struct _aarch64_ctx *)((char *)hdr + hdr->size); | ||||
| } | ||||
|  | ||||
| int cpu_signal_handler(int host_signum, void *pinfo, void *puc) | ||||
| { | ||||
|     siginfo_t *info = pinfo; | ||||
|     ucontext_t *uc = puc; | ||||
|     uintptr_t pc = uc->uc_mcontext.pc; | ||||
|     uint32_t insn = *(uint32_t *)pc; | ||||
|     bool is_write; | ||||
|     struct _aarch64_ctx *hdr; | ||||
|     struct esr_context const *esrctx = NULL; | ||||
|  | ||||
|     /* Find the esr_context, which has the WnR bit in it */ | ||||
|     for (hdr = first_ctx(uc); hdr->magic; hdr = next_ctx(hdr)) { | ||||
|         if (hdr->magic == ESR_MAGIC) { | ||||
|             esrctx = (struct esr_context const *)hdr; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     /* XXX: need kernel patch to get write flag faster.  */ | ||||
|     is_write = (   (insn & 0xbfff0000) == 0x0c000000   /* C3.3.1 */ | ||||
|                 || (insn & 0xbfe00000) == 0x0c800000   /* C3.3.2 */ | ||||
|                 || (insn & 0xbfdf0000) == 0x0d000000   /* C3.3.3 */ | ||||
|                 || (insn & 0xbfc00000) == 0x0d800000   /* C3.3.4 */ | ||||
|                 || (insn & 0x3f400000) == 0x08000000   /* C3.3.6 */ | ||||
|                 || (insn & 0x3bc00000) == 0x39000000   /* C3.3.13 */ | ||||
|                 || (insn & 0x3fc00000) == 0x3d800000   /* ... 128bit */ | ||||
|                 /* Ingore bits 10, 11 & 21, controlling indexing.  */ | ||||
|                 || (insn & 0x3bc00000) == 0x38000000   /* C3.3.8-12 */ | ||||
|                 || (insn & 0x3fe00000) == 0x3c800000   /* ... 128bit */ | ||||
|                 /* Ignore bits 23 & 24, controlling indexing.  */ | ||||
|                 || (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */ | ||||
|  | ||||
|     if (esrctx) { | ||||
|         /* For data aborts ESR.EC is 0b10010x: then bit 6 is the WnR bit */ | ||||
|         uint64_t esr = esrctx->esr; | ||||
|         is_write = extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1; | ||||
|     } else { | ||||
|         /* | ||||
|          * Fall back to parsing instructions; will only be needed | ||||
|          * for really ancient (pre-3.16) kernels. | ||||
|          */ | ||||
|         uint32_t insn = *(uint32_t *)pc; | ||||
|  | ||||
|         is_write = ((insn & 0xbfff0000) == 0x0c000000   /* C3.3.1 */ | ||||
|                     || (insn & 0xbfe00000) == 0x0c800000   /* C3.3.2 */ | ||||
|                     || (insn & 0xbfdf0000) == 0x0d000000   /* C3.3.3 */ | ||||
|                     || (insn & 0xbfc00000) == 0x0d800000   /* C3.3.4 */ | ||||
|                     || (insn & 0x3f400000) == 0x08000000   /* C3.3.6 */ | ||||
|                     || (insn & 0x3bc00000) == 0x39000000   /* C3.3.13 */ | ||||
|                     || (insn & 0x3fc00000) == 0x3d800000   /* ... 128bit */ | ||||
|                     /* Ignore bits 10, 11 & 21, controlling indexing.  */ | ||||
|                     || (insn & 0x3bc00000) == 0x38000000   /* C3.3.8-12 */ | ||||
|                     || (insn & 0x3fe00000) == 0x3c800000   /* ... 128bit */ | ||||
|                     /* Ignore bits 23 & 24, controlling indexing.  */ | ||||
|                     || (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */ | ||||
|     } | ||||
|     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); | ||||
| } | ||||
|  | ||||
| @@ -609,81 +570,6 @@ int cpu_signal_handler(int host_signum, void *pinfo, | ||||
|     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); | ||||
| } | ||||
|  | ||||
| #elif defined(__riscv) | ||||
|  | ||||
| int cpu_signal_handler(int host_signum, void *pinfo, | ||||
|                        void *puc) | ||||
| { | ||||
|     siginfo_t *info = pinfo; | ||||
|     ucontext_t *uc = puc; | ||||
|     greg_t pc = uc->uc_mcontext.__gregs[REG_PC]; | ||||
|     uint32_t insn = *(uint32_t *)pc; | ||||
|     int is_write = 0; | ||||
|  | ||||
|     /* Detect store by reading the instruction at the program | ||||
|        counter. Note: we currently only generate 32-bit | ||||
|        instructions so we thus only detect 32-bit stores */ | ||||
|     switch (((insn >> 0) & 0b11)) { | ||||
|     case 3: | ||||
|         switch (((insn >> 2) & 0b11111)) { | ||||
|         case 8: | ||||
|             switch (((insn >> 12) & 0b111)) { | ||||
|             case 0: /* sb */ | ||||
|             case 1: /* sh */ | ||||
|             case 2: /* sw */ | ||||
|             case 3: /* sd */ | ||||
|             case 4: /* sq */ | ||||
|                 is_write = 1; | ||||
|                 break; | ||||
|             default: | ||||
|                 break; | ||||
|             } | ||||
|             break; | ||||
|         case 9: | ||||
|             switch (((insn >> 12) & 0b111)) { | ||||
|             case 2: /* fsw */ | ||||
|             case 3: /* fsd */ | ||||
|             case 4: /* fsq */ | ||||
|                 is_write = 1; | ||||
|                 break; | ||||
|             default: | ||||
|                 break; | ||||
|             } | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* Check for compressed instructions */ | ||||
|     switch (((insn >> 13) & 0b111)) { | ||||
|     case 7: | ||||
|         switch (insn & 0b11) { | ||||
|         case 0: /*c.sd */ | ||||
|         case 2: /* c.sdsp */ | ||||
|             is_write = 1; | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|         break; | ||||
|     case 6: | ||||
|         switch (insn & 0b11) { | ||||
|         case 0: /* c.sw */ | ||||
|         case 3: /* c.swsp */ | ||||
|             is_write = 1; | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|         break; | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); | ||||
| } | ||||
|  | ||||
| #else | ||||
|  | ||||
| #error host CPU specific signal handler needed | ||||
| @@ -729,7 +615,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, | ||||
| /* The following is only callable from other helpers, and matches up | ||||
|    with the softmmu version.  */ | ||||
|  | ||||
| #if HAVE_ATOMIC128 || HAVE_CMPXCHG128 | ||||
| #ifdef CONFIG_ATOMIC128 | ||||
|  | ||||
| #undef EXTRA_ARGS | ||||
| #undef ATOMIC_NAME | ||||
| @@ -742,4 +628,4 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, | ||||
|  | ||||
| #define DATA_SIZE 16 | ||||
| #include "atomic_template.h" | ||||
| #endif | ||||
| #endif /* CONFIG_ATOMIC128 */ | ||||
|   | ||||
							
								
								
									
										20
									
								
								arch_init.c
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								arch_init.c
									
									
									
									
									
								
							| @@ -29,7 +29,6 @@ | ||||
| #include "hw/pci/pci.h" | ||||
| #include "hw/audio/soundhw.h" | ||||
| #include "qapi/qapi-commands-misc.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qemu/config-file.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "hw/acpi/acpi.h" | ||||
| @@ -52,14 +51,14 @@ int graphic_depth = 32; | ||||
| #define QEMU_ARCH QEMU_ARCH_ARM | ||||
| #elif defined(TARGET_CRIS) | ||||
| #define QEMU_ARCH QEMU_ARCH_CRIS | ||||
| #elif defined(TARGET_HPPA) | ||||
| #define QEMU_ARCH QEMU_ARCH_HPPA | ||||
| #elif defined(TARGET_I386) | ||||
| #define QEMU_ARCH QEMU_ARCH_I386 | ||||
| #elif defined(TARGET_LM32) | ||||
| #define QEMU_ARCH QEMU_ARCH_LM32 | ||||
| #elif defined(TARGET_HPPA) | ||||
| #define QEMU_ARCH QEMU_ARCH_HPPA | ||||
| #elif defined(TARGET_M68K) | ||||
| #define QEMU_ARCH QEMU_ARCH_M68K | ||||
| #elif defined(TARGET_LM32) | ||||
| #define QEMU_ARCH QEMU_ARCH_LM32 | ||||
| #elif defined(TARGET_MICROBLAZE) | ||||
| #define QEMU_ARCH QEMU_ARCH_MICROBLAZE | ||||
| #elif defined(TARGET_MIPS) | ||||
| @@ -80,12 +79,12 @@ int graphic_depth = 32; | ||||
| #define QEMU_ARCH QEMU_ARCH_SH4 | ||||
| #elif defined(TARGET_SPARC) | ||||
| #define QEMU_ARCH QEMU_ARCH_SPARC | ||||
| #elif defined(TARGET_TRICORE) | ||||
| #define QEMU_ARCH QEMU_ARCH_TRICORE | ||||
| #elif defined(TARGET_UNICORE32) | ||||
| #define QEMU_ARCH QEMU_ARCH_UNICORE32 | ||||
| #elif defined(TARGET_XTENSA) | ||||
| #define QEMU_ARCH QEMU_ARCH_XTENSA | ||||
| #elif defined(TARGET_UNICORE32) | ||||
| #define QEMU_ARCH QEMU_ARCH_UNICORE32 | ||||
| #elif defined(TARGET_TRICORE) | ||||
| #define QEMU_ARCH QEMU_ARCH_TRICORE | ||||
| #endif | ||||
|  | ||||
| const uint32_t arch_type = QEMU_ARCH; | ||||
| @@ -113,8 +112,7 @@ TargetInfo *qmp_query_target(Error **errp) | ||||
| { | ||||
|     TargetInfo *info = g_malloc0(sizeof(*info)); | ||||
|  | ||||
|     info->arch = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, -1, | ||||
|                                  &error_abort); | ||||
|     info->arch = g_strdup(TARGET_NAME); | ||||
|  | ||||
|     return info; | ||||
| } | ||||
|   | ||||
| @@ -28,7 +28,9 @@ | ||||
| #include "audio.h" | ||||
| #include "trace.h" | ||||
|  | ||||
| #if QEMU_GNUC_PREREQ(4, 3) | ||||
| #pragma GCC diagnostic ignored "-Waddress" | ||||
| #endif | ||||
|  | ||||
| #define AUDIO_CAP "alsa" | ||||
| #include "audio_int.h" | ||||
|   | ||||
| @@ -29,7 +29,6 @@ | ||||
| #include "sysemu/sysemu.h" | ||||
| #include "qemu/cutils.h" | ||||
| #include "sysemu/replay.h" | ||||
| #include "trace.h" | ||||
|  | ||||
| #define AUDIO_CAP "audio" | ||||
| #include "audio_int.h" | ||||
| @@ -336,8 +335,9 @@ static int audio_get_conf_int (const char *key, int defval, int *defaultp) | ||||
|     char *strval; | ||||
|  | ||||
|     strval = getenv (key); | ||||
|     if (strval && !qemu_strtoi(strval, NULL, 10, &val)) { | ||||
|     if (strval) { | ||||
|         *defaultp = 0; | ||||
|         val = atoi (strval); | ||||
|         return val; | ||||
|     } | ||||
|     else { | ||||
| @@ -1130,10 +1130,6 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info) | ||||
| /* | ||||
|  * Timer | ||||
|  */ | ||||
|  | ||||
| static bool audio_timer_running; | ||||
| static uint64_t audio_timer_last; | ||||
|  | ||||
| static int audio_is_timer_needed (void) | ||||
| { | ||||
|     HWVoiceIn *hwi = NULL; | ||||
| @@ -1153,31 +1149,14 @@ static void audio_reset_timer (AudioState *s) | ||||
|     if (audio_is_timer_needed ()) { | ||||
|         timer_mod_anticipate_ns(s->ts, | ||||
|             qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks); | ||||
|         if (!audio_timer_running) { | ||||
|             audio_timer_running = true; | ||||
|             audio_timer_last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); | ||||
|             trace_audio_timer_start(conf.period.ticks / SCALE_MS); | ||||
|         } | ||||
|     } else { | ||||
|         timer_del(s->ts); | ||||
|         if (audio_timer_running) { | ||||
|             audio_timer_running = false; | ||||
|             trace_audio_timer_stop(); | ||||
|         } | ||||
|     } | ||||
|     else { | ||||
|         timer_del (s->ts); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void audio_timer (void *opaque) | ||||
| { | ||||
|     int64_t now, diff; | ||||
|  | ||||
|     now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); | ||||
|     diff = now - audio_timer_last; | ||||
|     if (diff > conf.period.ticks * 3 / 2) { | ||||
|         trace_audio_timer_delayed(diff / SCALE_MS); | ||||
|     } | ||||
|     audio_timer_last = now; | ||||
|  | ||||
|     audio_run ("timer"); | ||||
|     audio_reset_timer (opaque); | ||||
| } | ||||
| @@ -1762,7 +1741,7 @@ void AUD_help (void) | ||||
|         ); | ||||
| } | ||||
|  | ||||
| static int audio_driver_init(AudioState *s, struct audio_driver *drv, bool msg) | ||||
| static int audio_driver_init (AudioState *s, struct audio_driver *drv) | ||||
| { | ||||
|     if (drv->options) { | ||||
|         audio_process_options (drv->name, drv->options); | ||||
| @@ -1776,9 +1755,7 @@ static int audio_driver_init(AudioState *s, struct audio_driver *drv, bool msg) | ||||
|         return 0; | ||||
|     } | ||||
|     else { | ||||
|         if (msg) { | ||||
|             dolog("Could not init `%s' audio driver\n", drv->name); | ||||
|         } | ||||
|         dolog ("Could not init `%s' audio driver\n", drv->name); | ||||
|         return -1; | ||||
|     } | ||||
| } | ||||
| @@ -1903,7 +1880,7 @@ static void audio_init (void) | ||||
|     if (drvname) { | ||||
|         driver = audio_driver_lookup(drvname); | ||||
|         if (driver) { | ||||
|             done = !audio_driver_init(s, driver, true); | ||||
|             done = !audio_driver_init(s, driver); | ||||
|         } else { | ||||
|             dolog ("Unknown audio driver `%s'\n", drvname); | ||||
|             dolog ("Run with -audio-help to list available drivers\n"); | ||||
| @@ -1914,14 +1891,14 @@ static void audio_init (void) | ||||
|         for (i = 0; !done && i < ARRAY_SIZE(audio_prio_list); i++) { | ||||
|             driver = audio_driver_lookup(audio_prio_list[i]); | ||||
|             if (driver && driver->can_be_default) { | ||||
|                 done = !audio_driver_init(s, driver, false); | ||||
|                 done = !audio_driver_init(s, driver); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (!done) { | ||||
|         driver = audio_driver_lookup("none"); | ||||
|         done = !audio_driver_init(s, driver, false); | ||||
|         done = !audio_driver_init(s, driver); | ||||
|         assert(done); | ||||
|         dolog("warning: Using timer based audio emulation\n"); | ||||
|     } | ||||
|   | ||||
| @@ -191,7 +191,7 @@ struct SWVoiceCap { | ||||
|     QLIST_ENTRY (SWVoiceCap) entries; | ||||
| }; | ||||
|  | ||||
| typedef struct AudioState { | ||||
| struct AudioState { | ||||
|     struct audio_driver *drv; | ||||
|     void *drv_opaque; | ||||
|  | ||||
| @@ -203,7 +203,7 @@ typedef struct AudioState { | ||||
|     int nb_hw_voices_out; | ||||
|     int nb_hw_voices_in; | ||||
|     int vm_running; | ||||
| } AudioState; | ||||
| }; | ||||
|  | ||||
| extern const struct mixeng_volume nominal_volume; | ||||
|  | ||||
|   | ||||
| @@ -227,7 +227,7 @@ static void *qpa_thread_out (void *arg) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         decr = to_mix = audio_MIN(pa->live, pa->g->conf.samples >> 5); | ||||
|         decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2); | ||||
|         rpos = pa->rpos; | ||||
|  | ||||
|         if (audio_pt_unlock(&pa->pt, __func__)) { | ||||
| @@ -319,7 +319,7 @@ static void *qpa_thread_in (void *arg) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         incr = to_grab = audio_MIN(pa->dead, pa->g->conf.samples >> 5); | ||||
|         incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2); | ||||
|         wpos = pa->wpos; | ||||
|  | ||||
|         if (audio_pt_unlock(&pa->pt, __func__)) { | ||||
| @@ -814,21 +814,6 @@ static PAConf glob_conf = { | ||||
|  | ||||
| static void *qpa_audio_init (void) | ||||
| { | ||||
|     if (glob_conf.server == NULL) { | ||||
|         char pidfile[64]; | ||||
|         char *runtime; | ||||
|         struct stat st; | ||||
|  | ||||
|         runtime = getenv("XDG_RUNTIME_DIR"); | ||||
|         if (!runtime) { | ||||
|             return NULL; | ||||
|         } | ||||
|         snprintf(pidfile, sizeof(pidfile), "%s/pulse/pid", runtime); | ||||
|         if (stat(pidfile, &st) != 0) { | ||||
|             return NULL; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     paaudio *g = g_malloc(sizeof(paaudio)); | ||||
|     g->conf = glob_conf; | ||||
|     g->mainloop = NULL; | ||||
|   | ||||
| @@ -15,8 +15,3 @@ alsa_no_frames(int state) "No frames available and ALSA state is %d" | ||||
| # audio/ossaudio.c | ||||
| oss_version(int version) "OSS version = 0x%x" | ||||
| oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d" | ||||
|  | ||||
| # audio/audio.c | ||||
| audio_timer_start(int interval) "interval %d ms" | ||||
| audio_timer_stop(void) "" | ||||
| audio_timer_delayed(int interval) "interval %d ms" | ||||
|   | ||||
| @@ -4,7 +4,7 @@ common-obj-$(CONFIG_POSIX) += rng-random.o | ||||
| common-obj-$(CONFIG_TPM) += tpm.o | ||||
|  | ||||
| common-obj-y += hostmem.o hostmem-ram.o | ||||
| common-obj-$(CONFIG_POSIX) += hostmem-file.o | ||||
| common-obj-$(CONFIG_LINUX) += hostmem-file.o | ||||
|  | ||||
| common-obj-y += cryptodev.o | ||||
| common-obj-y += cryptodev-builtin.o | ||||
|   | ||||
| @@ -26,7 +26,6 @@ | ||||
| #include "qapi/error.h" | ||||
| #include "qapi/qmp/qerror.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "hw/virtio/vhost-user.h" | ||||
| #include "standard-headers/linux/virtio_crypto.h" | ||||
| #include "sysemu/cryptodev-vhost.h" | ||||
| #include "chardev/char-fe.h" | ||||
| @@ -47,7 +46,6 @@ | ||||
| typedef struct CryptoDevBackendVhostUser { | ||||
|     CryptoDevBackend parent_obj; | ||||
|  | ||||
|     VhostUserState *vhost_user; | ||||
|     CharBackend chr; | ||||
|     char *chr_name; | ||||
|     bool opened; | ||||
| @@ -104,7 +102,7 @@ cryptodev_vhost_user_start(int queues, | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         options.opaque = s->vhost_user; | ||||
|         options.opaque = &s->chr; | ||||
|         options.backend_type = VHOST_BACKEND_TYPE_USER; | ||||
|         options.cc = b->conf.peers.ccs[i]; | ||||
|         s->vhost_crypto[i] = cryptodev_vhost_init(&options); | ||||
| @@ -157,6 +155,7 @@ static void cryptodev_vhost_user_event(void *opaque, int event) | ||||
| { | ||||
|     CryptoDevBackendVhostUser *s = opaque; | ||||
|     CryptoDevBackend *b = CRYPTODEV_BACKEND(s); | ||||
|     Error *err = NULL; | ||||
|     int queues = b->conf.peers.queues; | ||||
|  | ||||
|     assert(queues < MAX_CRYPTO_QUEUE_NUM); | ||||
| @@ -173,6 +172,10 @@ static void cryptodev_vhost_user_event(void *opaque, int event) | ||||
|         cryptodev_vhost_user_stop(queues, s); | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     if (err) { | ||||
|         error_report_err(err); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void cryptodev_vhost_user_init( | ||||
| @@ -182,7 +185,6 @@ static void cryptodev_vhost_user_init( | ||||
|     size_t i; | ||||
|     Error *local_err = NULL; | ||||
|     Chardev *chr; | ||||
|     VhostUserState *user; | ||||
|     CryptoDevBackendClient *cc; | ||||
|     CryptoDevBackendVhostUser *s = | ||||
|                       CRYPTODEV_BACKEND_VHOST_USER(backend); | ||||
| @@ -213,15 +215,6 @@ static void cryptodev_vhost_user_init( | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     user = vhost_user_init(); | ||||
|     if (!user) { | ||||
|         error_setg(errp, "Failed to init vhost_user"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     user->chr = &s->chr; | ||||
|     s->vhost_user = user; | ||||
|  | ||||
|     qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, | ||||
|                      cryptodev_vhost_user_event, NULL, s, NULL, true); | ||||
|  | ||||
| @@ -306,12 +299,6 @@ static void cryptodev_vhost_user_cleanup( | ||||
|             backend->conf.peers.ccs[i] = NULL; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (s->vhost_user) { | ||||
|         vhost_user_cleanup(s->vhost_user); | ||||
|         g_free(s->vhost_user); | ||||
|         s->vhost_user = NULL; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void cryptodev_vhost_user_set_chardev(Object *obj, | ||||
|   | ||||
| @@ -12,7 +12,6 @@ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qemu-common.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "sysemu/hostmem.h" | ||||
| #include "sysemu/sysemu.h" | ||||
| #include "qom/object_interfaces.h" | ||||
| @@ -32,19 +31,15 @@ typedef struct HostMemoryBackendFile HostMemoryBackendFile; | ||||
| struct HostMemoryBackendFile { | ||||
|     HostMemoryBackend parent_obj; | ||||
|  | ||||
|     bool discard_data; | ||||
|     char *mem_path; | ||||
|     uint64_t align; | ||||
|     bool discard_data; | ||||
|     bool is_pmem; | ||||
| }; | ||||
|  | ||||
| static void | ||||
| file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | ||||
| { | ||||
|     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend); | ||||
| #ifdef CONFIG_POSIX | ||||
|     gchar *name; | ||||
| #endif | ||||
|  | ||||
|     if (!backend->size) { | ||||
|         error_setg(errp, "can't create backend with size 0"); | ||||
| @@ -54,18 +49,19 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | ||||
|         error_setg(errp, "mem-path property not set"); | ||||
|         return; | ||||
|     } | ||||
| #ifndef CONFIG_POSIX | ||||
| #ifndef CONFIG_LINUX | ||||
|     error_setg(errp, "-mem-path not supported on this host"); | ||||
| #else | ||||
|     backend->force_prealloc = mem_prealloc; | ||||
|     name = host_memory_backend_get_name(backend); | ||||
|     memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), | ||||
|                                      name, | ||||
|                                      backend->size, fb->align, | ||||
|                                      (backend->share ? RAM_SHARED : 0) | | ||||
|                                      (fb->is_pmem ? RAM_PMEM : 0), | ||||
|                                      fb->mem_path, errp); | ||||
|     g_free(name); | ||||
|     if (!host_memory_backend_mr_inited(backend)) { | ||||
|         gchar *path; | ||||
|         backend->force_prealloc = mem_prealloc; | ||||
|         path = object_get_canonical_path(OBJECT(backend)); | ||||
|         memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), | ||||
|                                  path, | ||||
|                                  backend->size, fb->align, backend->share, | ||||
|                                  fb->mem_path, errp); | ||||
|         g_free(path); | ||||
|     } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| @@ -82,8 +78,7 @@ static void set_mem_path(Object *o, const char *str, Error **errp) | ||||
|     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); | ||||
|  | ||||
|     if (host_memory_backend_mr_inited(backend)) { | ||||
|         error_setg(errp, "cannot change property 'mem-path' of %s", | ||||
|                    object_get_typename(o)); | ||||
|         error_setg(errp, "cannot change property value"); | ||||
|         return; | ||||
|     } | ||||
|     g_free(fb->mem_path); | ||||
| @@ -121,8 +116,7 @@ static void file_memory_backend_set_align(Object *o, Visitor *v, | ||||
|     uint64_t val; | ||||
|  | ||||
|     if (host_memory_backend_mr_inited(backend)) { | ||||
|         error_setg(&local_err, "cannot change property '%s' of %s", | ||||
|                    name, object_get_typename(o)); | ||||
|         error_setg(&local_err, "cannot change property value"); | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
| @@ -136,39 +130,6 @@ static void file_memory_backend_set_align(Object *o, Visitor *v, | ||||
|     error_propagate(errp, local_err); | ||||
| } | ||||
|  | ||||
| static bool file_memory_backend_get_pmem(Object *o, Error **errp) | ||||
| { | ||||
|     return MEMORY_BACKEND_FILE(o)->is_pmem; | ||||
| } | ||||
|  | ||||
| static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); | ||||
|     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); | ||||
|  | ||||
|     if (host_memory_backend_mr_inited(backend)) { | ||||
|  | ||||
|         error_setg(errp, "cannot change property 'pmem' of %s.", | ||||
|                    object_get_typename(o)); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| #ifndef CONFIG_LIBPMEM | ||||
|     if (value) { | ||||
|         Error *local_err = NULL; | ||||
|  | ||||
|         error_setg(&local_err, | ||||
|                    "Lack of libpmem support while setting the 'pmem=on'" | ||||
|                    " of %s. We can't ensure data persistence.", | ||||
|                    object_get_typename(o)); | ||||
|         error_propagate(errp, local_err); | ||||
|         return; | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     fb->is_pmem = value; | ||||
| } | ||||
|  | ||||
| static void file_backend_unparent(Object *obj) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||
| @@ -200,9 +161,6 @@ file_backend_class_init(ObjectClass *oc, void *data) | ||||
|         file_memory_backend_get_align, | ||||
|         file_memory_backend_set_align, | ||||
|         NULL, NULL, &error_abort); | ||||
|     object_class_property_add_bool(oc, "pmem", | ||||
|         file_memory_backend_get_pmem, file_memory_backend_set_pmem, | ||||
|         &error_abort); | ||||
| } | ||||
|  | ||||
| static void file_backend_instance_finalize(Object *o) | ||||
|   | ||||
| @@ -44,6 +44,10 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (host_memory_backend_mr_inited(backend)) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     backend->force_prealloc = mem_prealloc; | ||||
|     fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size, | ||||
|                            m->hugetlb, m->hugetlbsize, m->seal ? | ||||
| @@ -53,10 +57,9 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     name = host_memory_backend_get_name(backend); | ||||
|     name = object_get_canonical_path(OBJECT(backend)); | ||||
|     memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), | ||||
|                                    name, backend->size, | ||||
|                                    backend->share, fd, errp); | ||||
|                                    name, backend->size, true, fd, errp); | ||||
|     g_free(name); | ||||
| } | ||||
|  | ||||
| @@ -128,7 +131,6 @@ memfd_backend_instance_init(Object *obj) | ||||
|  | ||||
|     /* default to sealed file */ | ||||
|     m->seal = true; | ||||
|     MEMORY_BACKEND(m)->share = true; | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -138,31 +140,18 @@ memfd_backend_class_init(ObjectClass *oc, void *data) | ||||
|  | ||||
|     bc->alloc = memfd_backend_memory_alloc; | ||||
|  | ||||
|     if (qemu_memfd_check(MFD_HUGETLB)) { | ||||
|         object_class_property_add_bool(oc, "hugetlb", | ||||
|                                        memfd_backend_get_hugetlb, | ||||
|                                        memfd_backend_set_hugetlb, | ||||
|                                        &error_abort); | ||||
|         object_class_property_set_description(oc, "hugetlb", | ||||
|                                               "Use huge pages", | ||||
|                                               &error_abort); | ||||
|         object_class_property_add(oc, "hugetlbsize", "int", | ||||
|                                   memfd_backend_get_hugetlbsize, | ||||
|                                   memfd_backend_set_hugetlbsize, | ||||
|                                   NULL, NULL, &error_abort); | ||||
|         object_class_property_set_description(oc, "hugetlbsize", | ||||
|                                               "Huge pages size (ex: 2M, 1G)", | ||||
|                                               &error_abort); | ||||
|     } | ||||
|     if (qemu_memfd_check(MFD_ALLOW_SEALING)) { | ||||
|         object_class_property_add_bool(oc, "seal", | ||||
|                                        memfd_backend_get_seal, | ||||
|                                        memfd_backend_set_seal, | ||||
|                                        &error_abort); | ||||
|         object_class_property_set_description(oc, "seal", | ||||
|                                               "Seal growing & shrinking", | ||||
|                                               &error_abort); | ||||
|     } | ||||
|     object_class_property_add_bool(oc, "hugetlb", | ||||
|                                    memfd_backend_get_hugetlb, | ||||
|                                    memfd_backend_set_hugetlb, | ||||
|                                    &error_abort); | ||||
|     object_class_property_add(oc, "hugetlbsize", "int", | ||||
|                               memfd_backend_get_hugetlbsize, | ||||
|                               memfd_backend_set_hugetlbsize, | ||||
|                               NULL, NULL, &error_abort); | ||||
|     object_class_property_add_bool(oc, "seal", | ||||
|                                    memfd_backend_get_seal, | ||||
|                                    memfd_backend_set_seal, | ||||
|                                    &error_abort); | ||||
| } | ||||
|  | ||||
| static const TypeInfo memfd_backend_info = { | ||||
| @@ -175,9 +164,7 @@ static const TypeInfo memfd_backend_info = { | ||||
|  | ||||
| static void register_types(void) | ||||
| { | ||||
|     if (qemu_memfd_check(0)) { | ||||
|         type_register_static(&memfd_backend_info); | ||||
|     } | ||||
|     type_register_static(&memfd_backend_info); | ||||
| } | ||||
|  | ||||
| type_init(register_types); | ||||
|   | ||||
| @@ -16,20 +16,21 @@ | ||||
|  | ||||
| #define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram" | ||||
|  | ||||
|  | ||||
| static void | ||||
| ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | ||||
| { | ||||
|     char *name; | ||||
|     char *path; | ||||
|  | ||||
|     if (!backend->size) { | ||||
|         error_setg(errp, "can't create backend with size 0"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     name = host_memory_backend_get_name(backend); | ||||
|     memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), name, | ||||
|     path = object_get_canonical_path_component(OBJECT(backend)); | ||||
|     memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), path, | ||||
|                            backend->size, backend->share, errp); | ||||
|     g_free(name); | ||||
|     g_free(path); | ||||
| } | ||||
|  | ||||
| static void | ||||
|   | ||||
| @@ -18,7 +18,6 @@ | ||||
| #include "qapi/visitor.h" | ||||
| #include "qemu/config-file.h" | ||||
| #include "qom/object_interfaces.h" | ||||
| #include "qemu/mmap-alloc.h" | ||||
|  | ||||
| #ifdef CONFIG_NUMA | ||||
| #include <numaif.h> | ||||
| @@ -28,16 +27,6 @@ QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_BIND != MPOL_BIND); | ||||
| QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE); | ||||
| #endif | ||||
|  | ||||
| char * | ||||
| host_memory_backend_get_name(HostMemoryBackend *backend) | ||||
| { | ||||
|     if (!backend->use_canonical_path) { | ||||
|         return object_get_canonical_path_component(OBJECT(backend)); | ||||
|     } | ||||
|  | ||||
|     return object_get_canonical_path(OBJECT(backend)); | ||||
| } | ||||
|  | ||||
| static void | ||||
| host_memory_backend_get_size(Object *obj, Visitor *v, const char *name, | ||||
|                              void *opaque, Error **errp) | ||||
| @@ -57,8 +46,7 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name, | ||||
|     uint64_t value; | ||||
|  | ||||
|     if (host_memory_backend_mr_inited(backend)) { | ||||
|         error_setg(&local_err, "cannot change property %s of %s ", | ||||
|                    name, object_get_typename(obj)); | ||||
|         error_setg(&local_err, "cannot change property value"); | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
| @@ -67,9 +55,8 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name, | ||||
|         goto out; | ||||
|     } | ||||
|     if (!value) { | ||||
|         error_setg(&local_err, | ||||
|                    "property '%s' of %s doesn't take value '%" PRIu64 "'", | ||||
|                    name, object_get_typename(obj), value); | ||||
|         error_setg(&local_err, "Property '%s.%s' doesn't take value '%" | ||||
|                    PRIu64 "'", object_get_typename(obj), name, value); | ||||
|         goto out; | ||||
|     } | ||||
|     backend->size = value; | ||||
| @@ -115,23 +102,14 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name, | ||||
| { | ||||
| #ifdef CONFIG_NUMA | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||
|     uint16List *l, *host_nodes = NULL; | ||||
|     uint16List *l = NULL; | ||||
|  | ||||
|     visit_type_uint16List(v, name, &host_nodes, errp); | ||||
|     visit_type_uint16List(v, name, &l, errp); | ||||
|  | ||||
|     for (l = host_nodes; l; l = l->next) { | ||||
|         if (l->value >= MAX_NODES) { | ||||
|             error_setg(errp, "Invalid host-nodes value: %d", l->value); | ||||
|             goto out; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     for (l = host_nodes; l; l = l->next) { | ||||
|     while (l) { | ||||
|         bitmap_set(backend->host_nodes, l->value, 1); | ||||
|         l = l->next; | ||||
|     } | ||||
|  | ||||
| out: | ||||
|     qapi_free_uint16List(host_nodes); | ||||
| #else | ||||
|     error_setg(errp, "NUMA node binding are not supported by this QEMU"); | ||||
| #endif | ||||
| @@ -259,11 +237,6 @@ static void host_memory_backend_init(Object *obj) | ||||
|     backend->prealloc = mem_prealloc; | ||||
| } | ||||
|  | ||||
| static void host_memory_backend_post_init(Object *obj) | ||||
| { | ||||
|     object_apply_compat_props(obj); | ||||
| } | ||||
|  | ||||
| bool host_memory_backend_mr_inited(HostMemoryBackend *backend) | ||||
| { | ||||
|     /* | ||||
| @@ -273,7 +246,8 @@ bool host_memory_backend_mr_inited(HostMemoryBackend *backend) | ||||
|     return memory_region_size(&backend->mr) != 0; | ||||
| } | ||||
|  | ||||
| MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend) | ||||
| MemoryRegion * | ||||
| host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp) | ||||
| { | ||||
|     return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL; | ||||
| } | ||||
| @@ -288,23 +262,6 @@ bool host_memory_backend_is_mapped(HostMemoryBackend *backend) | ||||
|     return backend->is_mapped; | ||||
| } | ||||
|  | ||||
| #ifdef __linux__ | ||||
| size_t host_memory_backend_pagesize(HostMemoryBackend *memdev) | ||||
| { | ||||
|     Object *obj = OBJECT(memdev); | ||||
|     char *path = object_property_get_str(obj, "mem-path", NULL); | ||||
|     size_t pagesize = qemu_mempath_getpagesize(path); | ||||
|  | ||||
|     g_free(path); | ||||
|     return pagesize; | ||||
| } | ||||
| #else | ||||
| size_t host_memory_backend_pagesize(HostMemoryBackend *memdev) | ||||
| { | ||||
|     return getpagesize(); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static void | ||||
| host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) | ||||
| { | ||||
| @@ -394,6 +351,24 @@ host_memory_backend_can_be_deleted(UserCreatable *uc) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static char *get_id(Object *o, Error **errp) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); | ||||
|  | ||||
|     return g_strdup(backend->id); | ||||
| } | ||||
|  | ||||
| static void set_id(Object *o, const char *str, Error **errp) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); | ||||
|  | ||||
|     if (backend->id) { | ||||
|         error_setg(errp, "cannot change property value"); | ||||
|         return; | ||||
|     } | ||||
|     backend->id = g_strdup(str); | ||||
| } | ||||
|  | ||||
| static bool host_memory_backend_get_share(Object *o, Error **errp) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); | ||||
| @@ -412,23 +387,6 @@ static void host_memory_backend_set_share(Object *o, bool value, Error **errp) | ||||
|     backend->share = value; | ||||
| } | ||||
|  | ||||
| static bool | ||||
| host_memory_backend_get_use_canonical_path(Object *obj, Error **errp) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||
|  | ||||
|     return backend->use_canonical_path; | ||||
| } | ||||
|  | ||||
| static void | ||||
| host_memory_backend_set_use_canonical_path(Object *obj, bool value, | ||||
|                                            Error **errp) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||
|  | ||||
|     backend->use_canonical_path = value; | ||||
| } | ||||
|  | ||||
| static void | ||||
| host_memory_backend_class_init(ObjectClass *oc, void *data) | ||||
| { | ||||
| @@ -440,44 +398,34 @@ host_memory_backend_class_init(ObjectClass *oc, void *data) | ||||
|     object_class_property_add_bool(oc, "merge", | ||||
|         host_memory_backend_get_merge, | ||||
|         host_memory_backend_set_merge, &error_abort); | ||||
|     object_class_property_set_description(oc, "merge", | ||||
|         "Mark memory as mergeable", &error_abort); | ||||
|     object_class_property_add_bool(oc, "dump", | ||||
|         host_memory_backend_get_dump, | ||||
|         host_memory_backend_set_dump, &error_abort); | ||||
|     object_class_property_set_description(oc, "dump", | ||||
|         "Set to 'off' to exclude from core dump", &error_abort); | ||||
|     object_class_property_add_bool(oc, "prealloc", | ||||
|         host_memory_backend_get_prealloc, | ||||
|         host_memory_backend_set_prealloc, &error_abort); | ||||
|     object_class_property_set_description(oc, "prealloc", | ||||
|         "Preallocate memory", &error_abort); | ||||
|     object_class_property_add(oc, "size", "int", | ||||
|         host_memory_backend_get_size, | ||||
|         host_memory_backend_set_size, | ||||
|         NULL, NULL, &error_abort); | ||||
|     object_class_property_set_description(oc, "size", | ||||
|         "Size of the memory region (ex: 500M)", &error_abort); | ||||
|     object_class_property_add(oc, "host-nodes", "int", | ||||
|         host_memory_backend_get_host_nodes, | ||||
|         host_memory_backend_set_host_nodes, | ||||
|         NULL, NULL, &error_abort); | ||||
|     object_class_property_set_description(oc, "host-nodes", | ||||
|         "Binds memory to the list of NUMA host nodes", &error_abort); | ||||
|     object_class_property_add_enum(oc, "policy", "HostMemPolicy", | ||||
|         &HostMemPolicy_lookup, | ||||
|         host_memory_backend_get_policy, | ||||
|         host_memory_backend_set_policy, &error_abort); | ||||
|     object_class_property_set_description(oc, "policy", | ||||
|         "Set the NUMA policy", &error_abort); | ||||
|     object_class_property_add_str(oc, "id", get_id, set_id, &error_abort); | ||||
|     object_class_property_add_bool(oc, "share", | ||||
|         host_memory_backend_get_share, host_memory_backend_set_share, | ||||
|         &error_abort); | ||||
|     object_class_property_set_description(oc, "share", | ||||
|         "Mark the memory as private to QEMU or shared", &error_abort); | ||||
|     object_class_property_add_bool(oc, "x-use-canonical-path-for-ramblock-id", | ||||
|         host_memory_backend_get_use_canonical_path, | ||||
|         host_memory_backend_set_use_canonical_path, &error_abort); | ||||
| } | ||||
|  | ||||
| static void host_memory_backend_finalize(Object *o) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); | ||||
|     g_free(backend->id); | ||||
| } | ||||
|  | ||||
| static const TypeInfo host_memory_backend_info = { | ||||
| @@ -488,7 +436,7 @@ static const TypeInfo host_memory_backend_info = { | ||||
|     .class_init = host_memory_backend_class_init, | ||||
|     .instance_size = sizeof(HostMemoryBackend), | ||||
|     .instance_init = host_memory_backend_init, | ||||
|     .instance_post_init = host_memory_backend_post_init, | ||||
|     .instance_finalize = host_memory_backend_finalize, | ||||
|     .interfaces = (InterfaceInfo[]) { | ||||
|         { TYPE_USER_CREATABLE }, | ||||
|         { } | ||||
|   | ||||
							
								
								
									
										13
									
								
								balloon.c
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								balloon.c
									
									
									
									
									
								
							| @@ -26,7 +26,6 @@ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "qemu/atomic.h" | ||||
| #include "exec/cpu-common.h" | ||||
| #include "sysemu/kvm.h" | ||||
| #include "sysemu/balloon.h" | ||||
| @@ -38,22 +37,16 @@ | ||||
| static QEMUBalloonEvent *balloon_event_fn; | ||||
| static QEMUBalloonStatus *balloon_stat_fn; | ||||
| static void *balloon_opaque; | ||||
| static int balloon_inhibit_count; | ||||
| static bool balloon_inhibited; | ||||
|  | ||||
| bool qemu_balloon_is_inhibited(void) | ||||
| { | ||||
|     return atomic_read(&balloon_inhibit_count) > 0; | ||||
|     return balloon_inhibited; | ||||
| } | ||||
|  | ||||
| void qemu_balloon_inhibit(bool state) | ||||
| { | ||||
|     if (state) { | ||||
|         atomic_inc(&balloon_inhibit_count); | ||||
|     } else { | ||||
|         atomic_dec(&balloon_inhibit_count); | ||||
|     } | ||||
|  | ||||
|     assert(atomic_read(&balloon_inhibit_count) >= 0); | ||||
|     balloon_inhibited = state; | ||||
| } | ||||
|  | ||||
| static bool have_balloon(Error **errp) | ||||
|   | ||||
| @@ -1,19 +1,10 @@ | ||||
| block-obj-y += raw-format.o vmdk.o vpc.o | ||||
| block-obj-$(CONFIG_QCOW1) += qcow.o | ||||
| block-obj-$(CONFIG_VDI) += vdi.o | ||||
| block-obj-$(CONFIG_CLOOP) += cloop.o | ||||
| block-obj-$(CONFIG_BOCHS) += bochs.o | ||||
| block-obj-$(CONFIG_VVFAT) += vvfat.o | ||||
| block-obj-$(CONFIG_DMG) += dmg.o | ||||
|  | ||||
| block-obj-y += raw-format.o qcow.o vdi.o vmdk.o cloop.o bochs.o vpc.o vvfat.o dmg.o | ||||
| block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o | ||||
| block-obj-$(CONFIG_QED) += qed.o qed-l2-cache.o qed-table.o qed-cluster.o | ||||
| block-obj-$(CONFIG_QED) += qed-check.o | ||||
| block-obj-y += qed.o qed-l2-cache.o qed-table.o qed-cluster.o | ||||
| block-obj-y += qed-check.o | ||||
| block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o | ||||
| block-obj-y += quorum.o | ||||
| block-obj-y += blkdebug.o blkverify.o blkreplay.o | ||||
| block-obj-$(CONFIG_PARALLELS) += parallels.o | ||||
| block-obj-y += blklogwrites.o | ||||
| block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o | ||||
| block-obj-y += block-backend.o snapshot.o qapi.o | ||||
| block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o | ||||
| block-obj-$(CONFIG_POSIX) += file-posix.o | ||||
| @@ -22,8 +13,7 @@ block-obj-y += null.o mirror.o commit.o io.o create.o | ||||
| block-obj-y += throttle-groups.o | ||||
| block-obj-$(CONFIG_LINUX) += nvme.o | ||||
|  | ||||
| block-obj-y += nbd.o nbd-client.o | ||||
| block-obj-$(CONFIG_SHEEPDOG) += sheepdog.o | ||||
| block-obj-y += nbd.o nbd-client.o sheepdog.o | ||||
| block-obj-$(CONFIG_LIBISCSI) += iscsi.o | ||||
| block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o | ||||
| block-obj-$(CONFIG_LIBNFS) += nfs.o | ||||
| @@ -36,7 +26,7 @@ block-obj-y += accounting.o dirty-bitmap.o | ||||
| block-obj-y += write-threshold.o | ||||
| block-obj-y += backup.o | ||||
| block-obj-$(CONFIG_REPLICATION) += replication.o | ||||
| block-obj-y += throttle.o copy-on-read.o | ||||
| block-obj-y += throttle.o | ||||
|  | ||||
| block-obj-y += crypto.o | ||||
|  | ||||
| @@ -54,11 +44,8 @@ gluster.o-libs     := $(GLUSTERFS_LIBS) | ||||
| vxhs.o-libs        := $(VXHS_LIBS) | ||||
| ssh.o-cflags       := $(LIBSSH2_CFLAGS) | ||||
| ssh.o-libs         := $(LIBSSH2_LIBS) | ||||
| block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o | ||||
| block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y) | ||||
| block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o | ||||
| dmg-bz2.o-libs     := $(BZIP2_LIBS) | ||||
| block-obj-$(if $(CONFIG_LZFSE),m,n) += dmg-lzfse.o | ||||
| dmg-lzfse.o-libs   := $(LZFSE_LIBS) | ||||
| qcow.o-libs        := -lz | ||||
| linux-aio.o-libs   := -laio | ||||
| parallels.o-cflags := $(LIBXML2_CFLAGS) | ||||
|   | ||||
| @@ -94,94 +94,6 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie, | ||||
|     cookie->type = type; | ||||
| } | ||||
|  | ||||
| /* block_latency_histogram_compare_func: | ||||
|  * Compare @key with interval [@it[0], @it[1]). | ||||
|  * Return: -1 if @key < @it[0] | ||||
|  *          0 if @key in [@it[0], @it[1]) | ||||
|  *         +1 if @key >= @it[1] | ||||
|  */ | ||||
| static int block_latency_histogram_compare_func(const void *key, const void *it) | ||||
| { | ||||
|     uint64_t k = *(uint64_t *)key; | ||||
|     uint64_t a = ((uint64_t *)it)[0]; | ||||
|     uint64_t b = ((uint64_t *)it)[1]; | ||||
|  | ||||
|     return k < a ? -1 : (k < b ? 0 : 1); | ||||
| } | ||||
|  | ||||
| static void block_latency_histogram_account(BlockLatencyHistogram *hist, | ||||
|                                             int64_t latency_ns) | ||||
| { | ||||
|     uint64_t *pos; | ||||
|  | ||||
|     if (hist->bins == NULL) { | ||||
|         /* histogram disabled */ | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     if (latency_ns < hist->boundaries[0]) { | ||||
|         hist->bins[0]++; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (latency_ns >= hist->boundaries[hist->nbins - 2]) { | ||||
|         hist->bins[hist->nbins - 1]++; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     pos = bsearch(&latency_ns, hist->boundaries, hist->nbins - 2, | ||||
|                   sizeof(hist->boundaries[0]), | ||||
|                   block_latency_histogram_compare_func); | ||||
|     assert(pos != NULL); | ||||
|  | ||||
|     hist->bins[pos - hist->boundaries + 1]++; | ||||
| } | ||||
|  | ||||
| int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type, | ||||
|                                 uint64List *boundaries) | ||||
| { | ||||
|     BlockLatencyHistogram *hist = &stats->latency_histogram[type]; | ||||
|     uint64List *entry; | ||||
|     uint64_t *ptr; | ||||
|     uint64_t prev = 0; | ||||
|     int new_nbins = 1; | ||||
|  | ||||
|     for (entry = boundaries; entry; entry = entry->next) { | ||||
|         if (entry->value <= prev) { | ||||
|             return -EINVAL; | ||||
|         } | ||||
|         new_nbins++; | ||||
|         prev = entry->value; | ||||
|     } | ||||
|  | ||||
|     hist->nbins = new_nbins; | ||||
|     g_free(hist->boundaries); | ||||
|     hist->boundaries = g_new(uint64_t, hist->nbins - 1); | ||||
|     for (entry = boundaries, ptr = hist->boundaries; entry; | ||||
|          entry = entry->next, ptr++) | ||||
|     { | ||||
|         *ptr = entry->value; | ||||
|     } | ||||
|  | ||||
|     g_free(hist->bins); | ||||
|     hist->bins = g_new0(uint64_t, hist->nbins); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void block_latency_histograms_clear(BlockAcctStats *stats) | ||||
| { | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < BLOCK_MAX_IOTYPE; i++) { | ||||
|         BlockLatencyHistogram *hist = &stats->latency_histogram[i]; | ||||
|         g_free(hist->bins); | ||||
|         g_free(hist->boundaries); | ||||
|         memset(hist, 0, sizeof(*hist)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, | ||||
|                                  bool failed) | ||||
| { | ||||
| @@ -204,9 +116,6 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, | ||||
|         stats->nr_ops[cookie->type]++; | ||||
|     } | ||||
|  | ||||
|     block_latency_histogram_account(&stats->latency_histogram[cookie->type], | ||||
|                                     latency_ns); | ||||
|  | ||||
|     if (!failed || stats->account_failed) { | ||||
|         stats->total_time_ns[cookie->type] += latency_ns; | ||||
|         stats->last_access_time_ns = time_ns; | ||||
|   | ||||
							
								
								
									
										372
									
								
								block/backup.c
									
									
									
									
									
								
							
							
						
						
									
										372
									
								
								block/backup.c
									
									
									
									
									
								
							| @@ -27,13 +27,7 @@ | ||||
| #include "qemu/error-report.h" | ||||
|  | ||||
| #define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16) | ||||
|  | ||||
| typedef struct CowRequest { | ||||
|     int64_t start_byte; | ||||
|     int64_t end_byte; | ||||
|     QLIST_ENTRY(CowRequest) list; | ||||
|     CoQueue wait_queue; /* coroutines blocked on this request */ | ||||
| } CowRequest; | ||||
| #define SLICE_TIME 100000000ULL /* ns */ | ||||
|  | ||||
| typedef struct BackupBlockJob { | ||||
|     BlockJob common; | ||||
| @@ -41,10 +35,10 @@ typedef struct BackupBlockJob { | ||||
|     /* bitmap for sync=incremental */ | ||||
|     BdrvDirtyBitmap *sync_bitmap; | ||||
|     MirrorSyncMode sync_mode; | ||||
|     RateLimit limit; | ||||
|     BlockdevOnError on_source_error; | ||||
|     BlockdevOnError on_target_error; | ||||
|     CoRwlock flush_rwlock; | ||||
|     uint64_t len; | ||||
|     uint64_t bytes_read; | ||||
|     int64_t cluster_size; | ||||
|     bool compress; | ||||
| @@ -52,14 +46,8 @@ typedef struct BackupBlockJob { | ||||
|     QLIST_HEAD(, CowRequest) inflight_reqs; | ||||
|  | ||||
|     HBitmap *copy_bitmap; | ||||
|     bool use_copy_range; | ||||
|     int64_t copy_range_size; | ||||
|  | ||||
|     bool serialize_target_writes; | ||||
| } BackupBlockJob; | ||||
|  | ||||
| static const BlockJobDriver backup_job_driver; | ||||
|  | ||||
| /* See if in-flight requests overlap and wait for them to complete */ | ||||
| static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job, | ||||
|                                                        int64_t start, | ||||
| @@ -97,104 +85,19 @@ static void cow_request_end(CowRequest *req) | ||||
|     qemu_co_queue_restart_all(&req->wait_queue); | ||||
| } | ||||
|  | ||||
| /* Copy range to target with a bounce buffer and return the bytes copied. If | ||||
|  * error occurred, return a negative error number */ | ||||
| static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, | ||||
|                                                       int64_t start, | ||||
|                                                       int64_t end, | ||||
|                                                       bool is_write_notifier, | ||||
|                                                       bool *error_is_read, | ||||
|                                                       void **bounce_buffer) | ||||
| { | ||||
|     int ret; | ||||
|     struct iovec iov; | ||||
|     QEMUIOVector qiov; | ||||
|     BlockBackend *blk = job->common.blk; | ||||
|     int nbytes; | ||||
|     int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0; | ||||
|     int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0; | ||||
|  | ||||
|     hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1); | ||||
|     nbytes = MIN(job->cluster_size, job->len - start); | ||||
|     if (!*bounce_buffer) { | ||||
|         *bounce_buffer = blk_blockalign(blk, job->cluster_size); | ||||
|     } | ||||
|     iov.iov_base = *bounce_buffer; | ||||
|     iov.iov_len = nbytes; | ||||
|     qemu_iovec_init_external(&qiov, &iov, 1); | ||||
|  | ||||
|     ret = blk_co_preadv(blk, start, qiov.size, &qiov, read_flags); | ||||
|     if (ret < 0) { | ||||
|         trace_backup_do_cow_read_fail(job, start, ret); | ||||
|         if (error_is_read) { | ||||
|             *error_is_read = true; | ||||
|         } | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     if (qemu_iovec_is_zero(&qiov)) { | ||||
|         ret = blk_co_pwrite_zeroes(job->target, start, | ||||
|                                    qiov.size, write_flags | BDRV_REQ_MAY_UNMAP); | ||||
|     } else { | ||||
|         ret = blk_co_pwritev(job->target, start, | ||||
|                              qiov.size, &qiov, write_flags | | ||||
|                              (job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0)); | ||||
|     } | ||||
|     if (ret < 0) { | ||||
|         trace_backup_do_cow_write_fail(job, start, ret); | ||||
|         if (error_is_read) { | ||||
|             *error_is_read = false; | ||||
|         } | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     return nbytes; | ||||
| fail: | ||||
|     hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1); | ||||
|     return ret; | ||||
|  | ||||
| } | ||||
|  | ||||
| /* Copy range to target and return the bytes copied. If error occurred, return a | ||||
|  * negative error number. */ | ||||
| static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job, | ||||
|                                                 int64_t start, | ||||
|                                                 int64_t end, | ||||
|                                                 bool is_write_notifier) | ||||
| { | ||||
|     int ret; | ||||
|     int nr_clusters; | ||||
|     BlockBackend *blk = job->common.blk; | ||||
|     int nbytes; | ||||
|     int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0; | ||||
|     int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0; | ||||
|  | ||||
|     assert(QEMU_IS_ALIGNED(job->copy_range_size, job->cluster_size)); | ||||
|     nbytes = MIN(job->copy_range_size, end - start); | ||||
|     nr_clusters = DIV_ROUND_UP(nbytes, job->cluster_size); | ||||
|     hbitmap_reset(job->copy_bitmap, start / job->cluster_size, | ||||
|                   nr_clusters); | ||||
|     ret = blk_co_copy_range(blk, start, job->target, start, nbytes, | ||||
|                             read_flags, write_flags); | ||||
|     if (ret < 0) { | ||||
|         trace_backup_do_cow_copy_range_fail(job, start, ret); | ||||
|         hbitmap_set(job->copy_bitmap, start / job->cluster_size, | ||||
|                     nr_clusters); | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     return nbytes; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn backup_do_cow(BackupBlockJob *job, | ||||
|                                       int64_t offset, uint64_t bytes, | ||||
|                                       bool *error_is_read, | ||||
|                                       bool is_write_notifier) | ||||
| { | ||||
|     BlockBackend *blk = job->common.blk; | ||||
|     CowRequest cow_request; | ||||
|     struct iovec iov; | ||||
|     QEMUIOVector bounce_qiov; | ||||
|     void *bounce_buffer = NULL; | ||||
|     int ret = 0; | ||||
|     int64_t start, end; /* bytes */ | ||||
|     void *bounce_buffer = NULL; | ||||
|     int n; /* bytes */ | ||||
|  | ||||
|     qemu_co_rwlock_rdlock(&job->flush_rwlock); | ||||
|  | ||||
| @@ -206,38 +109,60 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job, | ||||
|     wait_for_overlapping_requests(job, start, end); | ||||
|     cow_request_begin(&cow_request, job, start, end); | ||||
|  | ||||
|     while (start < end) { | ||||
|     for (; start < end; start += job->cluster_size) { | ||||
|         if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) { | ||||
|             trace_backup_do_cow_skip(job, start); | ||||
|             start += job->cluster_size; | ||||
|             continue; /* already copied */ | ||||
|         } | ||||
|         hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1); | ||||
|  | ||||
|         trace_backup_do_cow_process(job, start); | ||||
|  | ||||
|         if (job->use_copy_range) { | ||||
|             ret = backup_cow_with_offload(job, start, end, is_write_notifier); | ||||
|             if (ret < 0) { | ||||
|                 job->use_copy_range = false; | ||||
|             } | ||||
|         n = MIN(job->cluster_size, job->common.len - start); | ||||
|  | ||||
|         if (!bounce_buffer) { | ||||
|             bounce_buffer = blk_blockalign(blk, job->cluster_size); | ||||
|         } | ||||
|         if (!job->use_copy_range) { | ||||
|             ret = backup_cow_with_bounce_buffer(job, start, end, is_write_notifier, | ||||
|                                                 error_is_read, &bounce_buffer); | ||||
|         iov.iov_base = bounce_buffer; | ||||
|         iov.iov_len = n; | ||||
|         qemu_iovec_init_external(&bounce_qiov, &iov, 1); | ||||
|  | ||||
|         ret = blk_co_preadv(blk, start, bounce_qiov.size, &bounce_qiov, | ||||
|                             is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0); | ||||
|         if (ret < 0) { | ||||
|             trace_backup_do_cow_read_fail(job, start, ret); | ||||
|             if (error_is_read) { | ||||
|                 *error_is_read = true; | ||||
|             } | ||||
|             hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1); | ||||
|             goto out; | ||||
|         } | ||||
|  | ||||
|         if (buffer_is_zero(iov.iov_base, iov.iov_len)) { | ||||
|             ret = blk_co_pwrite_zeroes(job->target, start, | ||||
|                                        bounce_qiov.size, BDRV_REQ_MAY_UNMAP); | ||||
|         } else { | ||||
|             ret = blk_co_pwritev(job->target, start, | ||||
|                                  bounce_qiov.size, &bounce_qiov, | ||||
|                                  job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0); | ||||
|         } | ||||
|         if (ret < 0) { | ||||
|             break; | ||||
|             trace_backup_do_cow_write_fail(job, start, ret); | ||||
|             if (error_is_read) { | ||||
|                 *error_is_read = false; | ||||
|             } | ||||
|             hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1); | ||||
|             goto out; | ||||
|         } | ||||
|  | ||||
|         /* Publish progress, guest I/O counts as progress too.  Note that the | ||||
|          * offset field is an opaque progress value, it is not a disk offset. | ||||
|          */ | ||||
|         start += ret; | ||||
|         job->bytes_read += ret; | ||||
|         job_progress_update(&job->common.job, ret); | ||||
|         ret = 0; | ||||
|         job->bytes_read += n; | ||||
|         job->common.offset += n; | ||||
|     } | ||||
|  | ||||
| out: | ||||
|     if (bounce_buffer) { | ||||
|         qemu_vfree(bounce_buffer); | ||||
|     } | ||||
| @@ -265,12 +190,23 @@ static int coroutine_fn backup_before_write_notify( | ||||
|     return backup_do_cow(job, req->offset, req->bytes, NULL, true); | ||||
| } | ||||
|  | ||||
| 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"); | ||||
|         return; | ||||
|     } | ||||
|     ratelimit_set_speed(&s->limit, speed, SLICE_TIME); | ||||
| } | ||||
|  | ||||
| static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) | ||||
| { | ||||
|     BdrvDirtyBitmap *bm; | ||||
|     BlockDriverState *bs = blk_bs(job->common.blk); | ||||
|  | ||||
|     if (ret < 0) { | ||||
|     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); | ||||
| @@ -281,25 +217,25 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void backup_commit(Job *job) | ||||
| static void backup_commit(BlockJob *job) | ||||
| { | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common); | ||||
|     if (s->sync_bitmap) { | ||||
|         backup_cleanup_sync_bitmap(s, 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void backup_abort(Job *job) | ||||
| static void backup_abort(BlockJob *job) | ||||
| { | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common); | ||||
|     if (s->sync_bitmap) { | ||||
|         backup_cleanup_sync_bitmap(s, -1); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void backup_clean(Job *job) | ||||
| static void backup_clean(BlockJob *job) | ||||
| { | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common); | ||||
|     assert(s->target); | ||||
|     blk_unref(s->target); | ||||
|     s->target = NULL; | ||||
| @@ -317,7 +253,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp) | ||||
|     BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); | ||||
|     int64_t len; | ||||
|  | ||||
|     assert(block_job_driver(job) == &backup_job_driver); | ||||
|     assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); | ||||
|  | ||||
|     if (backup_job->sync_mode != MIRROR_SYNC_MODE_NONE) { | ||||
|         error_setg(errp, "The backup job only supports block checkpoint in" | ||||
| @@ -325,10 +261,41 @@ void backup_do_checkpoint(BlockJob *job, Error **errp) | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     len = DIV_ROUND_UP(backup_job->len, backup_job->cluster_size); | ||||
|     len = DIV_ROUND_UP(backup_job->common.len, backup_job->cluster_size); | ||||
|     hbitmap_set(backup_job->copy_bitmap, 0, len); | ||||
| } | ||||
|  | ||||
| void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset, | ||||
|                                           uint64_t bytes) | ||||
| { | ||||
|     BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); | ||||
|     int64_t start, end; | ||||
|  | ||||
|     assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); | ||||
|  | ||||
|     start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size); | ||||
|     end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size); | ||||
|     wait_for_overlapping_requests(backup_job, start, end); | ||||
| } | ||||
|  | ||||
| void backup_cow_request_begin(CowRequest *req, BlockJob *job, | ||||
|                               int64_t offset, uint64_t bytes) | ||||
| { | ||||
|     BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); | ||||
|     int64_t start, end; | ||||
|  | ||||
|     assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); | ||||
|  | ||||
|     start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size); | ||||
|     end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size); | ||||
|     cow_request_begin(req, backup_job, start, end); | ||||
| } | ||||
|  | ||||
| void backup_cow_request_end(CowRequest *req) | ||||
| { | ||||
|     cow_request_end(req); | ||||
| } | ||||
|  | ||||
| static void backup_drain(BlockJob *job) | ||||
| { | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common); | ||||
| @@ -356,21 +323,37 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job, | ||||
|     } | ||||
| } | ||||
|  | ||||
| typedef struct { | ||||
|     int ret; | ||||
| } BackupCompleteData; | ||||
|  | ||||
| static void backup_complete(BlockJob *job, void *opaque) | ||||
| { | ||||
|     BackupCompleteData *data = opaque; | ||||
|  | ||||
|     block_job_completed(job, data->ret); | ||||
|     g_free(data); | ||||
| } | ||||
|  | ||||
| static bool coroutine_fn yield_and_check(BackupBlockJob *job) | ||||
| { | ||||
|     uint64_t delay_ns; | ||||
|  | ||||
|     if (job_is_cancelled(&job->common.job)) { | ||||
|     if (block_job_is_cancelled(&job->common)) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /* We need to yield even for delay_ns = 0 so that bdrv_drain_all() can | ||||
|      * return. Without a yield, the VM would not reboot. */ | ||||
|     delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read); | ||||
|     job->bytes_read = 0; | ||||
|     job_sleep_ns(&job->common.job, delay_ns); | ||||
|     /* 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->bytes_read); | ||||
|         job->bytes_read = 0; | ||||
|         block_job_sleep_ns(&job->common, delay_ns); | ||||
|     } else { | ||||
|         block_job_sleep_ns(&job->common, 0); | ||||
|     } | ||||
|  | ||||
|     if (job_is_cancelled(&job->common.job)) { | ||||
|     if (block_job_is_cancelled(&job->common)) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
| @@ -422,8 +405,7 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset, | ||||
|                                              UINT64_MAX); | ||||
|         offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset); | ||||
|         if (offset == -1) { | ||||
|             hbitmap_set(job->copy_bitmap, cluster, end - cluster); | ||||
|             break; | ||||
| @@ -438,66 +420,64 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) | ||||
|         bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size); | ||||
|     } | ||||
|  | ||||
|     /* TODO job_progress_set_remaining() would make more sense */ | ||||
|     job_progress_update(&job->common.job, | ||||
|         job->len - hbitmap_count(job->copy_bitmap) * job->cluster_size); | ||||
|     job->common.offset = job->common.len - | ||||
|                          hbitmap_count(job->copy_bitmap) * job->cluster_size; | ||||
|  | ||||
|     bdrv_dirty_iter_free(dbi); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn backup_run(Job *job, Error **errp) | ||||
| static void coroutine_fn backup_run(void *opaque) | ||||
| { | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); | ||||
|     BlockDriverState *bs = blk_bs(s->common.blk); | ||||
|     BackupBlockJob *job = opaque; | ||||
|     BackupCompleteData *data; | ||||
|     BlockDriverState *bs = blk_bs(job->common.blk); | ||||
|     int64_t offset, nb_clusters; | ||||
|     int ret = 0; | ||||
|  | ||||
|     QLIST_INIT(&s->inflight_reqs); | ||||
|     qemu_co_rwlock_init(&s->flush_rwlock); | ||||
|     QLIST_INIT(&job->inflight_reqs); | ||||
|     qemu_co_rwlock_init(&job->flush_rwlock); | ||||
|  | ||||
|     nb_clusters = DIV_ROUND_UP(s->len, s->cluster_size); | ||||
|     job_progress_set_remaining(job, s->len); | ||||
|  | ||||
|     s->copy_bitmap = hbitmap_alloc(nb_clusters, 0); | ||||
|     if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { | ||||
|         backup_incremental_init_copy_bitmap(s); | ||||
|     nb_clusters = DIV_ROUND_UP(job->common.len, job->cluster_size); | ||||
|     job->copy_bitmap = hbitmap_alloc(nb_clusters, 0); | ||||
|     if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { | ||||
|         backup_incremental_init_copy_bitmap(job); | ||||
|     } else { | ||||
|         hbitmap_set(s->copy_bitmap, 0, nb_clusters); | ||||
|         hbitmap_set(job->copy_bitmap, 0, nb_clusters); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     s->before_write.notify = backup_before_write_notify; | ||||
|     bdrv_add_before_write_notifier(bs, &s->before_write); | ||||
|     job->before_write.notify = backup_before_write_notify; | ||||
|     bdrv_add_before_write_notifier(bs, &job->before_write); | ||||
|  | ||||
|     if (s->sync_mode == MIRROR_SYNC_MODE_NONE) { | ||||
|     if (job->sync_mode == MIRROR_SYNC_MODE_NONE) { | ||||
|         /* All bits are set in copy_bitmap to allow any cluster to be copied. | ||||
|          * This does not actually require them to be copied. */ | ||||
|         while (!job_is_cancelled(job)) { | ||||
|         while (!block_job_is_cancelled(&job->common)) { | ||||
|             /* Yield until the job is cancelled.  We just let our before_write | ||||
|              * notify callback service CoW requests. */ | ||||
|             job_yield(job); | ||||
|             block_job_yield(&job->common); | ||||
|         } | ||||
|     } else if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { | ||||
|         ret = backup_run_incremental(s); | ||||
|     } 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 (offset = 0; offset < s->len; | ||||
|              offset += s->cluster_size) { | ||||
|         for (offset = 0; offset < job->common.len; | ||||
|              offset += job->cluster_size) { | ||||
|             bool error_is_read; | ||||
|             int alloced = 0; | ||||
|  | ||||
|             if (yield_and_check(s)) { | ||||
|             if (yield_and_check(job)) { | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             if (s->sync_mode == MIRROR_SYNC_MODE_TOP) { | ||||
|             if (job->sync_mode == MIRROR_SYNC_MODE_TOP) { | ||||
|                 int i; | ||||
|                 int64_t n; | ||||
|  | ||||
|                 /* Check to see if these blocks are already in the | ||||
|                  * backing file. */ | ||||
|  | ||||
|                 for (i = 0; i < s->cluster_size;) { | ||||
|                 for (i = 0; i < job->cluster_size;) { | ||||
|                     /* bdrv_is_allocated() only returns true/false based | ||||
|                      * on the first set of sectors it comes across that | ||||
|                      * are are all in the same state. | ||||
| @@ -506,7 +486,7 @@ static int coroutine_fn backup_run(Job *job, Error **errp) | ||||
|                      * needed but at some point that is always the case. */ | ||||
|                     alloced = | ||||
|                         bdrv_is_allocated(bs, offset + i, | ||||
|                                           s->cluster_size - i, &n); | ||||
|                                           job->cluster_size - i, &n); | ||||
|                     i += n; | ||||
|  | ||||
|                     if (alloced || n == 0) { | ||||
| @@ -524,45 +504,43 @@ static int coroutine_fn backup_run(Job *job, Error **errp) | ||||
|             if (alloced < 0) { | ||||
|                 ret = alloced; | ||||
|             } else { | ||||
|                 ret = backup_do_cow(s, offset, s->cluster_size, | ||||
|                 ret = backup_do_cow(job, offset, job->cluster_size, | ||||
|                                     &error_is_read, false); | ||||
|             } | ||||
|             if (ret < 0) { | ||||
|                 /* Depending on error action, fail now or retry cluster */ | ||||
|                 BlockErrorAction action = | ||||
|                     backup_error_action(s, error_is_read, -ret); | ||||
|                     backup_error_action(job, error_is_read, -ret); | ||||
|                 if (action == BLOCK_ERROR_ACTION_REPORT) { | ||||
|                     break; | ||||
|                 } else { | ||||
|                     offset -= s->cluster_size; | ||||
|                     offset -= job->cluster_size; | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     notifier_with_return_remove(&s->before_write); | ||||
|     notifier_with_return_remove(&job->before_write); | ||||
|  | ||||
|     /* wait until pending backup_do_cow() calls have completed */ | ||||
|     qemu_co_rwlock_wrlock(&s->flush_rwlock); | ||||
|     qemu_co_rwlock_unlock(&s->flush_rwlock); | ||||
|     hbitmap_free(s->copy_bitmap); | ||||
|     qemu_co_rwlock_wrlock(&job->flush_rwlock); | ||||
|     qemu_co_rwlock_unlock(&job->flush_rwlock); | ||||
|     hbitmap_free(job->copy_bitmap); | ||||
|  | ||||
|     return ret; | ||||
|     data = g_malloc(sizeof(*data)); | ||||
|     data->ret = ret; | ||||
|     block_job_defer_to_main_loop(&job->common, backup_complete, data); | ||||
| } | ||||
|  | ||||
| static const BlockJobDriver backup_job_driver = { | ||||
|     .job_driver = { | ||||
|         .instance_size          = sizeof(BackupBlockJob), | ||||
|         .job_type               = JOB_TYPE_BACKUP, | ||||
|         .free                   = block_job_free, | ||||
|         .user_resume            = block_job_user_resume, | ||||
|         .drain                  = block_job_drain, | ||||
|         .run                    = backup_run, | ||||
|         .commit                 = backup_commit, | ||||
|         .abort                  = backup_abort, | ||||
|         .clean                  = backup_clean, | ||||
|     }, | ||||
|     .instance_size          = sizeof(BackupBlockJob), | ||||
|     .job_type               = BLOCK_JOB_TYPE_BACKUP, | ||||
|     .start                  = backup_run, | ||||
|     .set_speed              = backup_set_speed, | ||||
|     .commit                 = backup_commit, | ||||
|     .abort                  = backup_abort, | ||||
|     .clean                  = backup_clean, | ||||
|     .attached_aio_context   = backup_attached_aio_context, | ||||
|     .drain                  = backup_drain, | ||||
| }; | ||||
| @@ -575,7 +553,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||||
|                   BlockdevOnError on_target_error, | ||||
|                   int creation_flags, | ||||
|                   BlockCompletionFunc *cb, void *opaque, | ||||
|                   JobTxn *txn, Error **errp) | ||||
|                   BlockJobTxn *txn, Error **errp) | ||||
| { | ||||
|     int64_t len; | ||||
|     BlockDriverInfo bdi; | ||||
| @@ -642,8 +620,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||||
|         goto error; | ||||
|     } | ||||
|  | ||||
|     /* job->len is fixed, so we can't allow resize */ | ||||
|     job = block_job_create(job_id, &backup_job_driver, txn, bs, | ||||
|     /* job->common.len is fixed, so we can't allow resize */ | ||||
|     job = block_job_create(job_id, &backup_job_driver, bs, | ||||
|                            BLK_PERM_CONSISTENT_READ, | ||||
|                            BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | | ||||
|                            BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD, | ||||
| @@ -668,9 +646,6 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||||
|                        sync_bitmap : NULL; | ||||
|     job->compress = compress; | ||||
|  | ||||
|     /* Detect image-fleecing (and similar) schemes */ | ||||
|     job->serialize_target_writes = bdrv_chain_contains(target, bs); | ||||
|  | ||||
|     /* If there is no backing file on the target, we cannot rely on COW if our | ||||
|      * backup cluster size is smaller than the target cluster size. Even for | ||||
|      * targets with a backing file, try to avoid COW if possible. */ | ||||
| @@ -697,17 +672,12 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||||
|     } else { | ||||
|         job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size); | ||||
|     } | ||||
|     job->use_copy_range = true; | ||||
|     job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk), | ||||
|                                         blk_get_max_transfer(job->target)); | ||||
|     job->copy_range_size = MAX(job->cluster_size, | ||||
|                                QEMU_ALIGN_UP(job->copy_range_size, | ||||
|                                              job->cluster_size)); | ||||
|  | ||||
|     /* Required permissions are already taken with target's blk_new() */ | ||||
|     block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, | ||||
|                        &error_abort); | ||||
|     job->len = len; | ||||
|     job->common.len = len; | ||||
|     block_job_txn_add_job(txn, &job->common); | ||||
|  | ||||
|     return &job->common; | ||||
|  | ||||
| @@ -716,8 +686,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||||
|         bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL); | ||||
|     } | ||||
|     if (job) { | ||||
|         backup_clean(&job->common.job); | ||||
|         job_early_fail(&job->common.job); | ||||
|         backup_clean(&job->common); | ||||
|         block_job_early_fail(&job->common); | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
|   | ||||
| @@ -305,7 +305,7 @@ static void blkdebug_parse_filename(const char *filename, QDict *options, | ||||
|  | ||||
|     if (c != filename) { | ||||
|         QString *config_path; | ||||
|         config_path = qstring_from_substr(filename, 0, c - filename); | ||||
|         config_path = qstring_from_substr(filename, 0, c - filename - 1); | ||||
|         qdict_put(options, "config", config_path); | ||||
|     } | ||||
|  | ||||
| @@ -398,11 +398,10 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | | ||||
|         (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); | ||||
|     bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | | ||||
|         ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & | ||||
|             bs->file->bs->supported_zero_flags); | ||||
|     bs->supported_write_flags = BDRV_REQ_FUA & | ||||
|         bs->file->bs->supported_write_flags; | ||||
|     bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & | ||||
|         bs->file->bs->supported_zero_flags; | ||||
|     ret = -EINVAL; | ||||
|  | ||||
|     /* Set alignment overrides */ | ||||
| @@ -625,7 +624,7 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs, | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     return bdrv_co_pdiscard(bs->file, offset, bytes); | ||||
|     return bdrv_co_pdiscard(bs->file->bs, offset, bytes); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs, | ||||
| @@ -846,12 +845,13 @@ static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options) | ||||
|     opts = qdict_new(); | ||||
|     qdict_put_str(opts, "driver", "blkdebug"); | ||||
|  | ||||
|     qdict_put(opts, "image", qobject_ref(bs->file->bs->full_open_options)); | ||||
|     QINCREF(bs->file->bs->full_open_options); | ||||
|     qdict_put(opts, "image", bs->file->bs->full_open_options); | ||||
|  | ||||
|     for (e = qdict_first(options); e; e = qdict_next(options, e)) { | ||||
|         if (strcmp(qdict_entry_key(e), "x-image")) { | ||||
|             qdict_put_obj(opts, qdict_entry_key(e), | ||||
|                           qobject_ref(qdict_entry_value(e))); | ||||
|             qobject_incref(qdict_entry_value(e)); | ||||
|             qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,549 +0,0 @@ | ||||
| /* | ||||
|  * Write logging blk driver based on blkverify and blkdebug. | ||||
|  * | ||||
|  * Copyright (c) 2017 Tuomas Tynkkynen <tuomas@tuxera.com> | ||||
|  * Copyright (c) 2018 Aapo Vienamo <aapo@tuxera.com> | ||||
|  * Copyright (c) 2018 Ari Sundholm <ari@tuxera.com> | ||||
|  * | ||||
|  * 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 "qapi/error.h" | ||||
| #include "qemu/sockets.h" /* for EINPROGRESS on Windows */ | ||||
| #include "block/block_int.h" | ||||
| #include "qapi/qmp/qdict.h" | ||||
| #include "qapi/qmp/qstring.h" | ||||
| #include "qemu/cutils.h" | ||||
| #include "qemu/option.h" | ||||
|  | ||||
| /* Disk format stuff - taken from Linux drivers/md/dm-log-writes.c */ | ||||
|  | ||||
| #define LOG_FLUSH_FLAG   (1 << 0) | ||||
| #define LOG_FUA_FLAG     (1 << 1) | ||||
| #define LOG_DISCARD_FLAG (1 << 2) | ||||
| #define LOG_MARK_FLAG    (1 << 3) | ||||
| #define LOG_FLAG_MASK    (LOG_FLUSH_FLAG \ | ||||
|                          | LOG_FUA_FLAG \ | ||||
|                          | LOG_DISCARD_FLAG \ | ||||
|                          | LOG_MARK_FLAG) | ||||
|  | ||||
| #define WRITE_LOG_VERSION 1ULL | ||||
| #define WRITE_LOG_MAGIC 0x6a736677736872ULL | ||||
|  | ||||
| /* All fields are little-endian. */ | ||||
| struct log_write_super { | ||||
|     uint64_t magic; | ||||
|     uint64_t version; | ||||
|     uint64_t nr_entries; | ||||
|     uint32_t sectorsize; | ||||
| } QEMU_PACKED; | ||||
|  | ||||
| struct log_write_entry { | ||||
|     uint64_t sector; | ||||
|     uint64_t nr_sectors; | ||||
|     uint64_t flags; | ||||
|     uint64_t data_len; | ||||
| } QEMU_PACKED; | ||||
|  | ||||
| /* End of disk format structures. */ | ||||
|  | ||||
| typedef struct { | ||||
|     BdrvChild *log_file; | ||||
|     uint32_t sectorsize; | ||||
|     uint32_t sectorbits; | ||||
|     uint64_t cur_log_sector; | ||||
|     uint64_t nr_entries; | ||||
|     uint64_t update_interval; | ||||
| } BDRVBlkLogWritesState; | ||||
|  | ||||
| static QemuOptsList runtime_opts = { | ||||
|     .name = "blklogwrites", | ||||
|     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), | ||||
|     .desc = { | ||||
|         { | ||||
|             .name = "log-append", | ||||
|             .type = QEMU_OPT_BOOL, | ||||
|             .help = "Append to an existing log", | ||||
|         }, | ||||
|         { | ||||
|             .name = "log-sector-size", | ||||
|             .type = QEMU_OPT_SIZE, | ||||
|             .help = "Log sector size", | ||||
|         }, | ||||
|         { | ||||
|             .name = "log-super-update-interval", | ||||
|             .type = QEMU_OPT_NUMBER, | ||||
|             .help = "Log superblock update interval (# of write requests)", | ||||
|         }, | ||||
|         { /* end of list */ } | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| static inline uint32_t blk_log_writes_log2(uint32_t value) | ||||
| { | ||||
|     assert(value > 0); | ||||
|     return 31 - clz32(value); | ||||
| } | ||||
|  | ||||
| static inline bool blk_log_writes_sector_size_valid(uint32_t sector_size) | ||||
| { | ||||
|     return is_power_of_2(sector_size) && | ||||
|         sector_size >= sizeof(struct log_write_super) && | ||||
|         sector_size >= sizeof(struct log_write_entry) && | ||||
|         sector_size < (1ull << 24); | ||||
| } | ||||
|  | ||||
| static uint64_t blk_log_writes_find_cur_log_sector(BdrvChild *log, | ||||
|                                                    uint32_t sector_size, | ||||
|                                                    uint64_t nr_entries, | ||||
|                                                    Error **errp) | ||||
| { | ||||
|     uint64_t cur_sector = 1; | ||||
|     uint64_t cur_idx = 0; | ||||
|     uint32_t sector_bits = blk_log_writes_log2(sector_size); | ||||
|     struct log_write_entry cur_entry; | ||||
|  | ||||
|     while (cur_idx < nr_entries) { | ||||
|         int read_ret = bdrv_pread(log, cur_sector << sector_bits, &cur_entry, | ||||
|                                   sizeof(cur_entry)); | ||||
|         if (read_ret < 0) { | ||||
|             error_setg_errno(errp, -read_ret, | ||||
|                              "Failed to read log entry %"PRIu64, cur_idx); | ||||
|             return (uint64_t)-1ull; | ||||
|         } | ||||
|  | ||||
|         if (cur_entry.flags & ~cpu_to_le64(LOG_FLAG_MASK)) { | ||||
|             error_setg(errp, "Invalid flags 0x%"PRIx64" in log entry %"PRIu64, | ||||
|                        le64_to_cpu(cur_entry.flags), cur_idx); | ||||
|             return (uint64_t)-1ull; | ||||
|         } | ||||
|  | ||||
|         /* Account for the sector of the entry itself */ | ||||
|         ++cur_sector; | ||||
|  | ||||
|         /* | ||||
|          * Account for the data of the write. | ||||
|          * For discards, this data is not present. | ||||
|          */ | ||||
|         if (!(cur_entry.flags & cpu_to_le64(LOG_DISCARD_FLAG))) { | ||||
|             cur_sector += le64_to_cpu(cur_entry.nr_sectors); | ||||
|         } | ||||
|  | ||||
|         ++cur_idx; | ||||
|     } | ||||
|  | ||||
|     return cur_sector; | ||||
| } | ||||
|  | ||||
| static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                                Error **errp) | ||||
| { | ||||
|     BDRVBlkLogWritesState *s = bs->opaque; | ||||
|     QemuOpts *opts; | ||||
|     Error *local_err = NULL; | ||||
|     int ret; | ||||
|     uint64_t log_sector_size; | ||||
|     bool log_append; | ||||
|  | ||||
|     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); | ||||
|     qemu_opts_absorb_qdict(opts, options, &local_err); | ||||
|     if (local_err) { | ||||
|         ret = -EINVAL; | ||||
|         error_propagate(errp, local_err); | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     /* Open the file */ | ||||
|     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, | ||||
|                                &local_err); | ||||
|     if (local_err) { | ||||
|         ret = -EINVAL; | ||||
|         error_propagate(errp, local_err); | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     /* Open the log file */ | ||||
|     s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_file, false, | ||||
|                                   &local_err); | ||||
|     if (local_err) { | ||||
|         ret = -EINVAL; | ||||
|         error_propagate(errp, local_err); | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     log_append = qemu_opt_get_bool(opts, "log-append", false); | ||||
|  | ||||
|     if (log_append) { | ||||
|         struct log_write_super log_sb = { 0, 0, 0, 0 }; | ||||
|  | ||||
|         if (qemu_opt_find(opts, "log-sector-size")) { | ||||
|             ret = -EINVAL; | ||||
|             error_setg(errp, "log-append and log-sector-size are mutually " | ||||
|                        "exclusive"); | ||||
|             goto fail_log; | ||||
|         } | ||||
|  | ||||
|         /* Read log superblock or fake one for an empty log */ | ||||
|         if (!bdrv_getlength(s->log_file->bs)) { | ||||
|             log_sb.magic      = cpu_to_le64(WRITE_LOG_MAGIC); | ||||
|             log_sb.version    = cpu_to_le64(WRITE_LOG_VERSION); | ||||
|             log_sb.nr_entries = cpu_to_le64(0); | ||||
|             log_sb.sectorsize = cpu_to_le32(BDRV_SECTOR_SIZE); | ||||
|         } else { | ||||
|             ret = bdrv_pread(s->log_file, 0, &log_sb, sizeof(log_sb)); | ||||
|             if (ret < 0) { | ||||
|                 error_setg_errno(errp, -ret, "Could not read log superblock"); | ||||
|                 goto fail_log; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (log_sb.magic != cpu_to_le64(WRITE_LOG_MAGIC)) { | ||||
|             ret = -EINVAL; | ||||
|             error_setg(errp, "Invalid log superblock magic"); | ||||
|             goto fail_log; | ||||
|         } | ||||
|  | ||||
|         if (log_sb.version != cpu_to_le64(WRITE_LOG_VERSION)) { | ||||
|             ret = -EINVAL; | ||||
|             error_setg(errp, "Unsupported log version %"PRIu64, | ||||
|                        le64_to_cpu(log_sb.version)); | ||||
|             goto fail_log; | ||||
|         } | ||||
|  | ||||
|         log_sector_size = le32_to_cpu(log_sb.sectorsize); | ||||
|         s->cur_log_sector = 1; | ||||
|         s->nr_entries = 0; | ||||
|  | ||||
|         if (blk_log_writes_sector_size_valid(log_sector_size)) { | ||||
|             s->cur_log_sector = | ||||
|                 blk_log_writes_find_cur_log_sector(s->log_file, log_sector_size, | ||||
|                                     le64_to_cpu(log_sb.nr_entries), &local_err); | ||||
|             if (local_err) { | ||||
|                 ret = -EINVAL; | ||||
|                 error_propagate(errp, local_err); | ||||
|                 goto fail_log; | ||||
|             } | ||||
|  | ||||
|             s->nr_entries = le64_to_cpu(log_sb.nr_entries); | ||||
|         } | ||||
|     } else { | ||||
|         log_sector_size = qemu_opt_get_size(opts, "log-sector-size", | ||||
|                                             BDRV_SECTOR_SIZE); | ||||
|         s->cur_log_sector = 1; | ||||
|         s->nr_entries = 0; | ||||
|     } | ||||
|  | ||||
|     if (!blk_log_writes_sector_size_valid(log_sector_size)) { | ||||
|         ret = -EINVAL; | ||||
|         error_setg(errp, "Invalid log sector size %"PRIu64, log_sector_size); | ||||
|         goto fail_log; | ||||
|     } | ||||
|  | ||||
|     s->sectorsize = log_sector_size; | ||||
|     s->sectorbits = blk_log_writes_log2(log_sector_size); | ||||
|     s->update_interval = qemu_opt_get_number(opts, "log-super-update-interval", | ||||
|                                              4096); | ||||
|     if (!s->update_interval) { | ||||
|         ret = -EINVAL; | ||||
|         error_setg(errp, "Invalid log superblock update interval %"PRIu64, | ||||
|                    s->update_interval); | ||||
|         goto fail_log; | ||||
|     } | ||||
|  | ||||
|     ret = 0; | ||||
| fail_log: | ||||
|     if (ret < 0) { | ||||
|         bdrv_unref_child(bs, s->log_file); | ||||
|         s->log_file = NULL; | ||||
|     } | ||||
| fail: | ||||
|     if (ret < 0) { | ||||
|         bdrv_unref_child(bs, bs->file); | ||||
|         bs->file = NULL; | ||||
|     } | ||||
|     qemu_opts_del(opts); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static void blk_log_writes_close(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVBlkLogWritesState *s = bs->opaque; | ||||
|  | ||||
|     bdrv_unref_child(bs, s->log_file); | ||||
|     s->log_file = NULL; | ||||
| } | ||||
|  | ||||
| static int64_t blk_log_writes_getlength(BlockDriverState *bs) | ||||
| { | ||||
|     return bdrv_getlength(bs->file->bs); | ||||
| } | ||||
|  | ||||
| static void blk_log_writes_refresh_filename(BlockDriverState *bs, | ||||
|                                             QDict *options) | ||||
| { | ||||
|     BDRVBlkLogWritesState *s = bs->opaque; | ||||
|  | ||||
|     /* bs->file->bs has already been refreshed */ | ||||
|     bdrv_refresh_filename(s->log_file->bs); | ||||
|  | ||||
|     if (bs->file->bs->full_open_options | ||||
|         && s->log_file->bs->full_open_options) | ||||
|     { | ||||
|         QDict *opts = qdict_new(); | ||||
|         qdict_put_str(opts, "driver", "blklogwrites"); | ||||
|  | ||||
|         qobject_ref(bs->file->bs->full_open_options); | ||||
|         qdict_put(opts, "file", bs->file->bs->full_open_options); | ||||
|         qobject_ref(s->log_file->bs->full_open_options); | ||||
|         qdict_put(opts, "log", s->log_file->bs->full_open_options); | ||||
|         qdict_put_int(opts, "log-sector-size", s->sectorsize); | ||||
|  | ||||
|         bs->full_open_options = opts; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c, | ||||
|                                       const BdrvChildRole *role, | ||||
|                                       BlockReopenQueue *ro_q, | ||||
|                                       uint64_t perm, uint64_t shrd, | ||||
|                                       uint64_t *nperm, uint64_t *nshrd) | ||||
| { | ||||
|     if (!c) { | ||||
|         *nperm = perm & DEFAULT_PERM_PASSTHROUGH; | ||||
|         *nshrd = (shrd & DEFAULT_PERM_PASSTHROUGH) | DEFAULT_PERM_UNCHANGED; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!strcmp(c->name, "log")) { | ||||
|         bdrv_format_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd); | ||||
|     } else { | ||||
|         bdrv_filter_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void blk_log_writes_refresh_limits(BlockDriverState *bs, Error **errp) | ||||
| { | ||||
|     BDRVBlkLogWritesState *s = bs->opaque; | ||||
|     bs->bl.request_alignment = s->sectorsize; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn | ||||
| blk_log_writes_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, | ||||
|                          QEMUIOVector *qiov, int flags) | ||||
| { | ||||
|     return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); | ||||
| } | ||||
|  | ||||
| typedef struct BlkLogWritesFileReq { | ||||
|     BlockDriverState *bs; | ||||
|     uint64_t offset; | ||||
|     uint64_t bytes; | ||||
|     int file_flags; | ||||
|     QEMUIOVector *qiov; | ||||
|     int (*func)(struct BlkLogWritesFileReq *r); | ||||
|     int file_ret; | ||||
| } BlkLogWritesFileReq; | ||||
|  | ||||
| typedef struct { | ||||
|     BlockDriverState *bs; | ||||
|     QEMUIOVector *qiov; | ||||
|     struct log_write_entry entry; | ||||
|     uint64_t zero_size; | ||||
|     int log_ret; | ||||
| } BlkLogWritesLogReq; | ||||
|  | ||||
| static void coroutine_fn blk_log_writes_co_do_log(BlkLogWritesLogReq *lr) | ||||
| { | ||||
|     BDRVBlkLogWritesState *s = lr->bs->opaque; | ||||
|     uint64_t cur_log_offset = s->cur_log_sector << s->sectorbits; | ||||
|  | ||||
|     s->nr_entries++; | ||||
|     s->cur_log_sector += | ||||
|             ROUND_UP(lr->qiov->size, s->sectorsize) >> s->sectorbits; | ||||
|  | ||||
|     lr->log_ret = bdrv_co_pwritev(s->log_file, cur_log_offset, lr->qiov->size, | ||||
|                                   lr->qiov, 0); | ||||
|  | ||||
|     /* Logging for the "write zeroes" operation */ | ||||
|     if (lr->log_ret == 0 && lr->zero_size) { | ||||
|         cur_log_offset = s->cur_log_sector << s->sectorbits; | ||||
|         s->cur_log_sector += | ||||
|                 ROUND_UP(lr->zero_size, s->sectorsize) >> s->sectorbits; | ||||
|  | ||||
|         lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, cur_log_offset, | ||||
|                                             lr->zero_size, 0); | ||||
|     } | ||||
|  | ||||
|     /* Update super block on flush or every update interval */ | ||||
|     if (lr->log_ret == 0 && ((lr->entry.flags & LOG_FLUSH_FLAG) | ||||
|         || (s->nr_entries % s->update_interval == 0))) | ||||
|     { | ||||
|         struct log_write_super super = { | ||||
|             .magic      = cpu_to_le64(WRITE_LOG_MAGIC), | ||||
|             .version    = cpu_to_le64(WRITE_LOG_VERSION), | ||||
|             .nr_entries = cpu_to_le64(s->nr_entries), | ||||
|             .sectorsize = cpu_to_le32(s->sectorsize), | ||||
|         }; | ||||
|         void *zeroes = g_malloc0(s->sectorsize - sizeof(super)); | ||||
|         QEMUIOVector qiov; | ||||
|  | ||||
|         qemu_iovec_init(&qiov, 2); | ||||
|         qemu_iovec_add(&qiov, &super, sizeof(super)); | ||||
|         qemu_iovec_add(&qiov, zeroes, s->sectorsize - sizeof(super)); | ||||
|  | ||||
|         lr->log_ret = | ||||
|             bdrv_co_pwritev(s->log_file, 0, s->sectorsize, &qiov, 0); | ||||
|         if (lr->log_ret == 0) { | ||||
|             lr->log_ret = bdrv_co_flush(s->log_file->bs); | ||||
|         } | ||||
|         qemu_iovec_destroy(&qiov); | ||||
|         g_free(zeroes); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void coroutine_fn blk_log_writes_co_do_file(BlkLogWritesFileReq *fr) | ||||
| { | ||||
|     fr->file_ret = fr->func(fr); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn | ||||
| blk_log_writes_co_log(BlockDriverState *bs, uint64_t offset, uint64_t bytes, | ||||
|                       QEMUIOVector *qiov, int flags, | ||||
|                       int (*file_func)(BlkLogWritesFileReq *r), | ||||
|                       uint64_t entry_flags, bool is_zero_write) | ||||
| { | ||||
|     QEMUIOVector log_qiov; | ||||
|     size_t niov = qiov ? qiov->niov : 0; | ||||
|     BDRVBlkLogWritesState *s = bs->opaque; | ||||
|     BlkLogWritesFileReq fr = { | ||||
|         .bs         = bs, | ||||
|         .offset     = offset, | ||||
|         .bytes      = bytes, | ||||
|         .file_flags = flags, | ||||
|         .qiov       = qiov, | ||||
|         .func       = file_func, | ||||
|     }; | ||||
|     BlkLogWritesLogReq lr = { | ||||
|         .bs             = bs, | ||||
|         .qiov           = &log_qiov, | ||||
|         .entry = { | ||||
|             .sector     = cpu_to_le64(offset >> s->sectorbits), | ||||
|             .nr_sectors = cpu_to_le64(bytes >> s->sectorbits), | ||||
|             .flags      = cpu_to_le64(entry_flags), | ||||
|             .data_len   = 0, | ||||
|         }, | ||||
|         .zero_size = is_zero_write ? bytes : 0, | ||||
|     }; | ||||
|     void *zeroes = g_malloc0(s->sectorsize - sizeof(lr.entry)); | ||||
|  | ||||
|     assert((1 << s->sectorbits) == s->sectorsize); | ||||
|     assert(bs->bl.request_alignment == s->sectorsize); | ||||
|     assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment)); | ||||
|     assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment)); | ||||
|  | ||||
|     qemu_iovec_init(&log_qiov, niov + 2); | ||||
|     qemu_iovec_add(&log_qiov, &lr.entry, sizeof(lr.entry)); | ||||
|     qemu_iovec_add(&log_qiov, zeroes, s->sectorsize - sizeof(lr.entry)); | ||||
|     if (qiov) { | ||||
|         qemu_iovec_concat(&log_qiov, qiov, 0, qiov->size); | ||||
|     } | ||||
|  | ||||
|     blk_log_writes_co_do_file(&fr); | ||||
|     blk_log_writes_co_do_log(&lr); | ||||
|  | ||||
|     qemu_iovec_destroy(&log_qiov); | ||||
|     g_free(zeroes); | ||||
|  | ||||
|     if (lr.log_ret < 0) { | ||||
|         return lr.log_ret; | ||||
|     } | ||||
|  | ||||
|     return fr.file_ret; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn | ||||
| blk_log_writes_co_do_file_pwritev(BlkLogWritesFileReq *fr) | ||||
| { | ||||
|     return bdrv_co_pwritev(fr->bs->file, fr->offset, fr->bytes, | ||||
|                            fr->qiov, fr->file_flags); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn | ||||
| blk_log_writes_co_do_file_pwrite_zeroes(BlkLogWritesFileReq *fr) | ||||
| { | ||||
|     return bdrv_co_pwrite_zeroes(fr->bs->file, fr->offset, fr->bytes, | ||||
|                                  fr->file_flags); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn blk_log_writes_co_do_file_flush(BlkLogWritesFileReq *fr) | ||||
| { | ||||
|     return bdrv_co_flush(fr->bs->file->bs); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn | ||||
| blk_log_writes_co_do_file_pdiscard(BlkLogWritesFileReq *fr) | ||||
| { | ||||
|     return bdrv_co_pdiscard(fr->bs->file, fr->offset, fr->bytes); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn | ||||
| blk_log_writes_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, | ||||
|                           QEMUIOVector *qiov, int flags) | ||||
| { | ||||
|     return blk_log_writes_co_log(bs, offset, bytes, qiov, flags, | ||||
|                                  blk_log_writes_co_do_file_pwritev, 0, false); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn | ||||
| blk_log_writes_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, | ||||
|                                 BdrvRequestFlags flags) | ||||
| { | ||||
|     return blk_log_writes_co_log(bs, offset, bytes, NULL, flags, | ||||
|                                  blk_log_writes_co_do_file_pwrite_zeroes, 0, | ||||
|                                  true); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn blk_log_writes_co_flush_to_disk(BlockDriverState *bs) | ||||
| { | ||||
|     return blk_log_writes_co_log(bs, 0, 0, NULL, 0, | ||||
|                                  blk_log_writes_co_do_file_flush, | ||||
|                                  LOG_FLUSH_FLAG, false); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn | ||||
| blk_log_writes_co_pdiscard(BlockDriverState *bs, int64_t offset, int count) | ||||
| { | ||||
|     return blk_log_writes_co_log(bs, offset, count, NULL, 0, | ||||
|                                  blk_log_writes_co_do_file_pdiscard, | ||||
|                                  LOG_DISCARD_FLAG, false); | ||||
| } | ||||
|  | ||||
| static BlockDriver bdrv_blk_log_writes = { | ||||
|     .format_name            = "blklogwrites", | ||||
|     .instance_size          = sizeof(BDRVBlkLogWritesState), | ||||
|  | ||||
|     .bdrv_open              = blk_log_writes_open, | ||||
|     .bdrv_close             = blk_log_writes_close, | ||||
|     .bdrv_getlength         = blk_log_writes_getlength, | ||||
|     .bdrv_refresh_filename  = blk_log_writes_refresh_filename, | ||||
|     .bdrv_child_perm        = blk_log_writes_child_perm, | ||||
|     .bdrv_refresh_limits    = blk_log_writes_refresh_limits, | ||||
|  | ||||
|     .bdrv_co_preadv         = blk_log_writes_co_preadv, | ||||
|     .bdrv_co_pwritev        = blk_log_writes_co_pwritev, | ||||
|     .bdrv_co_pwrite_zeroes  = blk_log_writes_co_pwrite_zeroes, | ||||
|     .bdrv_co_flush_to_disk  = blk_log_writes_co_flush_to_disk, | ||||
|     .bdrv_co_pdiscard       = blk_log_writes_co_pdiscard, | ||||
|     .bdrv_co_block_status   = bdrv_co_block_status_from_file, | ||||
|  | ||||
|     .is_filter              = true, | ||||
| }; | ||||
|  | ||||
| static void bdrv_blk_log_writes_init(void) | ||||
| { | ||||
|     bdrv_register(&bdrv_blk_log_writes); | ||||
| } | ||||
|  | ||||
| block_init(bdrv_blk_log_writes_init); | ||||
							
								
								
									
										10
									
								
								block/blkreplay.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										10
									
								
								block/blkreplay.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -35,14 +35,15 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; | ||||
|     bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; | ||||
|  | ||||
|     ret = 0; | ||||
| fail: | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static void blkreplay_close(BlockDriverState *bs) | ||||
| { | ||||
| } | ||||
|  | ||||
| static int64_t blkreplay_getlength(BlockDriverState *bs) | ||||
| { | ||||
|     return bdrv_getlength(bs->file->bs); | ||||
| @@ -109,7 +110,7 @@ static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs, | ||||
|                                               int64_t offset, int bytes) | ||||
| { | ||||
|     uint64_t reqid = blkreplay_next_id(); | ||||
|     int ret = bdrv_co_pdiscard(bs->file, offset, bytes); | ||||
|     int ret = bdrv_co_pdiscard(bs->file->bs, offset, bytes); | ||||
|     block_request_create(reqid, bs, qemu_coroutine_self()); | ||||
|     qemu_coroutine_yield(); | ||||
|  | ||||
| @@ -131,6 +132,7 @@ static BlockDriver bdrv_blkreplay = { | ||||
|     .instance_size          = 0, | ||||
|  | ||||
|     .bdrv_open              = blkreplay_open, | ||||
|     .bdrv_close             = blkreplay_close, | ||||
|     .bdrv_child_perm        = bdrv_filter_default_perms, | ||||
|     .bdrv_getlength         = blkreplay_getlength, | ||||
|  | ||||
|   | ||||
| @@ -80,7 +80,7 @@ static void blkverify_parse_filename(const char *filename, QDict *options, | ||||
|     } | ||||
|  | ||||
|     /* TODO Implement option pass-through and set raw.filename here */ | ||||
|     raw_path = qstring_from_substr(filename, 0, c - filename); | ||||
|     raw_path = qstring_from_substr(filename, 0, c - filename - 1); | ||||
|     qdict_put(options, "x-raw", raw_path); | ||||
|  | ||||
|     /* TODO Allow multi-level nesting and set file.filename here */ | ||||
| @@ -141,9 +141,6 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; | ||||
|     bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; | ||||
|  | ||||
|     ret = 0; | ||||
| fail: | ||||
|     qemu_opts_del(opts); | ||||
| @@ -294,10 +291,10 @@ static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options) | ||||
|         QDict *opts = qdict_new(); | ||||
|         qdict_put_str(opts, "driver", "blkverify"); | ||||
|  | ||||
|         qdict_put(opts, "raw", | ||||
|                   qobject_ref(bs->file->bs->full_open_options)); | ||||
|         qdict_put(opts, "test", | ||||
|                   qobject_ref(s->test_file->bs->full_open_options)); | ||||
|         QINCREF(bs->file->bs->full_open_options); | ||||
|         qdict_put(opts, "raw", bs->file->bs->full_open_options); | ||||
|         QINCREF(s->test_file->bs->full_open_options); | ||||
|         qdict_put(opts, "test", s->test_file->bs->full_open_options); | ||||
|  | ||||
|         bs->full_open_options = opts; | ||||
|     } | ||||
|   | ||||
| @@ -31,13 +31,6 @@ | ||||
|  | ||||
| static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb); | ||||
|  | ||||
| typedef struct BlockBackendAioNotifier { | ||||
|     void (*attached_aio_context)(AioContext *new_context, void *opaque); | ||||
|     void (*detach_aio_context)(void *opaque); | ||||
|     void *opaque; | ||||
|     QLIST_ENTRY(BlockBackendAioNotifier) list; | ||||
| } BlockBackendAioNotifier; | ||||
|  | ||||
| struct BlockBackend { | ||||
|     char *name; | ||||
|     int refcnt; | ||||
| @@ -47,7 +40,9 @@ struct BlockBackend { | ||||
|     QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */ | ||||
|     BlockBackendPublic public; | ||||
|  | ||||
|     DeviceState *dev;           /* attached device model, if any */ | ||||
|     void *dev;                  /* attached device model, if any */ | ||||
|     bool legacy_dev;            /* true if dev is not a DeviceState */ | ||||
|     /* TODO change to DeviceState when all users are qdevified */ | ||||
|     const BlockDevOps *dev_ops; | ||||
|     void *dev_opaque; | ||||
|  | ||||
| @@ -74,7 +69,6 @@ struct BlockBackend { | ||||
|     bool allow_write_beyond_eof; | ||||
|  | ||||
|     NotifierList remove_bs_notifiers, insert_bs_notifiers; | ||||
|     QLIST_HEAD(, BlockBackendAioNotifier) aio_notifiers; | ||||
|  | ||||
|     int quiesce_counter; | ||||
|     VMChangeStateEntry *vmsh; | ||||
| @@ -86,6 +80,7 @@ struct BlockBackend { | ||||
|      * Accessed with atomic ops. | ||||
|      */ | ||||
|     unsigned int in_flight; | ||||
|     AioWait wait; | ||||
| }; | ||||
|  | ||||
| typedef struct BlockBackendAIOCB { | ||||
| @@ -118,7 +113,6 @@ static void blk_root_inherit_options(int *child_flags, QDict *child_options, | ||||
|     abort(); | ||||
| } | ||||
| static void blk_root_drained_begin(BdrvChild *child); | ||||
| static bool blk_root_drained_poll(BdrvChild *child); | ||||
| static void blk_root_drained_end(BdrvChild *child); | ||||
|  | ||||
| static void blk_root_change_media(BdrvChild *child, bool load); | ||||
| @@ -253,36 +247,6 @@ static int blk_root_inactivate(BdrvChild *child) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void blk_root_attach(BdrvChild *child) | ||||
| { | ||||
|     BlockBackend *blk = child->opaque; | ||||
|     BlockBackendAioNotifier *notifier; | ||||
|  | ||||
|     trace_blk_root_attach(child, blk, child->bs); | ||||
|  | ||||
|     QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { | ||||
|         bdrv_add_aio_context_notifier(child->bs, | ||||
|                 notifier->attached_aio_context, | ||||
|                 notifier->detach_aio_context, | ||||
|                 notifier->opaque); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void blk_root_detach(BdrvChild *child) | ||||
| { | ||||
|     BlockBackend *blk = child->opaque; | ||||
|     BlockBackendAioNotifier *notifier; | ||||
|  | ||||
|     trace_blk_root_detach(child, blk, child->bs); | ||||
|  | ||||
|     QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { | ||||
|         bdrv_remove_aio_context_notifier(child->bs, | ||||
|                 notifier->attached_aio_context, | ||||
|                 notifier->detach_aio_context, | ||||
|                 notifier->opaque); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static const BdrvChildRole child_root = { | ||||
|     .inherit_options    = blk_root_inherit_options, | ||||
|  | ||||
| @@ -292,14 +256,10 @@ static const BdrvChildRole child_root = { | ||||
|     .get_parent_desc    = blk_root_get_parent_desc, | ||||
|  | ||||
|     .drained_begin      = blk_root_drained_begin, | ||||
|     .drained_poll       = blk_root_drained_poll, | ||||
|     .drained_end        = blk_root_drained_end, | ||||
|  | ||||
|     .activate           = blk_root_activate, | ||||
|     .inactivate         = blk_root_inactivate, | ||||
|  | ||||
|     .attach             = blk_root_attach, | ||||
|     .detach             = blk_root_detach, | ||||
| }; | ||||
|  | ||||
| /* | ||||
| @@ -323,14 +283,10 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm) | ||||
|     blk->shared_perm = shared_perm; | ||||
|     blk_set_enable_write_cache(blk, true); | ||||
|  | ||||
|     blk->on_read_error = BLOCKDEV_ON_ERROR_REPORT; | ||||
|     blk->on_write_error = BLOCKDEV_ON_ERROR_ENOSPC; | ||||
|  | ||||
|     block_acct_init(&blk->stats); | ||||
|  | ||||
|     notifier_list_init(&blk->remove_bs_notifiers); | ||||
|     notifier_list_init(&blk->insert_bs_notifiers); | ||||
|     QLIST_INIT(&blk->aio_notifiers); | ||||
|  | ||||
|     QTAILQ_INSERT_TAIL(&block_backends, blk, link); | ||||
|     return blk; | ||||
| @@ -408,7 +364,6 @@ static void blk_delete(BlockBackend *blk) | ||||
|     } | ||||
|     assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers)); | ||||
|     assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers)); | ||||
|     assert(QLIST_EMPTY(&blk->aio_notifiers)); | ||||
|     QTAILQ_REMOVE(&block_backends, blk, link); | ||||
|     drive_info_del(blk->legacy_dinfo); | ||||
|     block_acct_cleanup(&blk->stats); | ||||
| @@ -421,6 +376,7 @@ static void drive_info_del(DriveInfo *dinfo) | ||||
|         return; | ||||
|     } | ||||
|     qemu_opts_del(dinfo->opts); | ||||
|     g_free(dinfo->serial); | ||||
|     g_free(dinfo); | ||||
| } | ||||
|  | ||||
| @@ -435,7 +391,6 @@ int blk_get_refcnt(BlockBackend *blk) | ||||
|  */ | ||||
| void blk_ref(BlockBackend *blk) | ||||
| { | ||||
|     assert(blk->refcnt > 0); | ||||
|     blk->refcnt++; | ||||
| } | ||||
|  | ||||
| @@ -448,13 +403,7 @@ void blk_unref(BlockBackend *blk) | ||||
| { | ||||
|     if (blk) { | ||||
|         assert(blk->refcnt > 0); | ||||
|         if (blk->refcnt > 1) { | ||||
|             blk->refcnt--; | ||||
|         } else { | ||||
|             blk_drain(blk); | ||||
|             /* blk_drain() cannot resurrect blk, nobody held a reference */ | ||||
|             assert(blk->refcnt == 1); | ||||
|             blk->refcnt = 0; | ||||
|         if (!--blk->refcnt) { | ||||
|             blk_delete(blk); | ||||
|         } | ||||
|     } | ||||
| @@ -776,11 +725,6 @@ void blk_remove_bs(BlockBackend *blk) | ||||
|  | ||||
|     blk_update_root_state(blk); | ||||
|  | ||||
|     /* bdrv_root_unref_child() will cause blk->root to become stale and may | ||||
|      * switch to a completion coroutine later on. Let's drain all I/O here | ||||
|      * to avoid that and a potential QEMU crash. | ||||
|      */ | ||||
|     blk_drain(blk); | ||||
|     bdrv_root_unref_child(blk->root); | ||||
|     blk->root = NULL; | ||||
| } | ||||
| @@ -834,11 +778,7 @@ void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm) | ||||
|     *shared_perm = blk->shared_perm; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Attach device model @dev to @blk. | ||||
|  * Return 0 on success, -EBUSY when a device model is attached already. | ||||
|  */ | ||||
| int blk_attach_dev(BlockBackend *blk, DeviceState *dev) | ||||
| static int blk_do_attach_dev(BlockBackend *blk, void *dev) | ||||
| { | ||||
|     if (blk->dev) { | ||||
|         return -EBUSY; | ||||
| @@ -853,16 +793,40 @@ int blk_attach_dev(BlockBackend *blk, DeviceState *dev) | ||||
|  | ||||
|     blk_ref(blk); | ||||
|     blk->dev = dev; | ||||
|     blk->legacy_dev = false; | ||||
|     blk_iostatus_reset(blk); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Attach device model @dev to @blk. | ||||
|  * Return 0 on success, -EBUSY when a device model is attached already. | ||||
|  */ | ||||
| int blk_attach_dev(BlockBackend *blk, DeviceState *dev) | ||||
| { | ||||
|     return blk_do_attach_dev(blk, dev); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Attach device model @dev to @blk. | ||||
|  * @blk must not have a device model attached already. | ||||
|  * TODO qdevified devices don't use this, remove when devices are qdevified | ||||
|  */ | ||||
| void blk_attach_dev_legacy(BlockBackend *blk, void *dev) | ||||
| { | ||||
|     if (blk_do_attach_dev(blk, dev) < 0) { | ||||
|         abort(); | ||||
|     } | ||||
|     blk->legacy_dev = true; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Detach device model @dev from @blk. | ||||
|  * @dev must be currently attached to @blk. | ||||
|  */ | ||||
| void blk_detach_dev(BlockBackend *blk, DeviceState *dev) | ||||
| void blk_detach_dev(BlockBackend *blk, void *dev) | ||||
| /* TODO change to DeviceState *dev when all users are qdevified */ | ||||
| { | ||||
|     assert(blk->dev == dev); | ||||
|     blk->dev = NULL; | ||||
| @@ -876,7 +840,8 @@ void blk_detach_dev(BlockBackend *blk, DeviceState *dev) | ||||
| /* | ||||
|  * Return the device model attached to @blk if any, else null. | ||||
|  */ | ||||
| DeviceState *blk_get_attached_dev(BlockBackend *blk) | ||||
| void *blk_get_attached_dev(BlockBackend *blk) | ||||
| /* TODO change to return DeviceState * when all users are qdevified */ | ||||
| { | ||||
|     return blk->dev; | ||||
| } | ||||
| @@ -885,15 +850,17 @@ DeviceState *blk_get_attached_dev(BlockBackend *blk) | ||||
|  * device attached to the BlockBackend. */ | ||||
| char *blk_get_attached_dev_id(BlockBackend *blk) | ||||
| { | ||||
|     DeviceState *dev = blk->dev; | ||||
|     DeviceState *dev; | ||||
|  | ||||
|     assert(!blk->legacy_dev); | ||||
|     dev = blk->dev; | ||||
|  | ||||
|     if (!dev) { | ||||
|         return g_strdup(""); | ||||
|     } else if (dev->id) { | ||||
|         return g_strdup(dev->id); | ||||
|     } | ||||
|  | ||||
|     return object_get_canonical_path(OBJECT(dev)) ?: g_strdup(""); | ||||
|     return object_get_canonical_path(OBJECT(dev)); | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -923,6 +890,11 @@ BlockBackend *blk_by_dev(void *dev) | ||||
| void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, | ||||
|                      void *opaque) | ||||
| { | ||||
|     /* All drivers that use blk_set_dev_ops() are qdevified and we want to keep | ||||
|      * it that way, so we can assume blk->dev, if present, is a DeviceState if | ||||
|      * blk->dev_ops is set. Non-device users may use dev_ops without device. */ | ||||
|     assert(!blk->legacy_dev); | ||||
|  | ||||
|     blk->dev_ops = ops; | ||||
|     blk->dev_opaque = opaque; | ||||
|  | ||||
| @@ -948,6 +920,8 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp) | ||||
|         bool tray_was_open, tray_is_open; | ||||
|         Error *local_err = NULL; | ||||
|  | ||||
|         assert(!blk->legacy_dev); | ||||
|  | ||||
|         tray_was_open = blk_dev_is_tray_open(blk); | ||||
|         blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err); | ||||
|         if (local_err) { | ||||
| @@ -959,7 +933,8 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp) | ||||
|  | ||||
|         if (tray_was_open != tray_is_open) { | ||||
|             char *id = blk_get_attached_dev_id(blk); | ||||
|             qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open); | ||||
|             qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open, | ||||
|                                               &error_abort); | ||||
|             g_free(id); | ||||
|         } | ||||
|     } | ||||
| @@ -1187,7 +1162,6 @@ static void blk_read_entry(void *opaque) | ||||
|  | ||||
|     rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size, | ||||
|                               qiov, rwco->flags); | ||||
|     aio_wait_kick(); | ||||
| } | ||||
|  | ||||
| static void blk_write_entry(void *opaque) | ||||
| @@ -1197,7 +1171,6 @@ static void blk_write_entry(void *opaque) | ||||
|  | ||||
|     rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size, | ||||
|                                qiov, rwco->flags); | ||||
|     aio_wait_kick(); | ||||
| } | ||||
|  | ||||
| static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, | ||||
| @@ -1270,7 +1243,7 @@ static void blk_inc_in_flight(BlockBackend *blk) | ||||
| static void blk_dec_in_flight(BlockBackend *blk) | ||||
| { | ||||
|     atomic_dec(&blk->in_flight); | ||||
|     aio_wait_kick(); | ||||
|     aio_wait_kick(&blk->wait); | ||||
| } | ||||
|  | ||||
| static void error_callback_bh(void *opaque) | ||||
| @@ -1311,8 +1284,8 @@ static const AIOCBInfo blk_aio_em_aiocb_info = { | ||||
| static void blk_aio_complete(BlkAioEmAIOCB *acb) | ||||
| { | ||||
|     if (acb->has_returned) { | ||||
|         acb->common.cb(acb->common.opaque, acb->rwco.ret); | ||||
|         blk_dec_in_flight(acb->rwco.blk); | ||||
|         acb->common.cb(acb->common.opaque, acb->rwco.ret); | ||||
|         qemu_aio_unref(acb); | ||||
|     } | ||||
| } | ||||
| @@ -1509,7 +1482,6 @@ static void blk_ioctl_entry(void *opaque) | ||||
|  | ||||
|     rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, | ||||
|                              qiov->iov[0].iov_base); | ||||
|     aio_wait_kick(); | ||||
| } | ||||
|  | ||||
| int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) | ||||
| @@ -1540,7 +1512,7 @@ int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes) | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     return bdrv_co_pdiscard(blk->root, offset, bytes); | ||||
|     return bdrv_co_pdiscard(blk_bs(blk), offset, bytes); | ||||
| } | ||||
|  | ||||
| int blk_co_flush(BlockBackend *blk) | ||||
| @@ -1556,7 +1528,6 @@ static void blk_flush_entry(void *opaque) | ||||
| { | ||||
|     BlkRwCo *rwco = opaque; | ||||
|     rwco->ret = blk_co_flush(rwco->blk); | ||||
|     aio_wait_kick(); | ||||
| } | ||||
|  | ||||
| int blk_flush(BlockBackend *blk) | ||||
| @@ -1573,8 +1544,9 @@ void blk_drain(BlockBackend *blk) | ||||
|     } | ||||
|  | ||||
|     /* We may have -ENOMEDIUM completions in flight */ | ||||
|     AIO_WAIT_WHILE(blk_get_aio_context(blk), | ||||
|                    atomic_mb_read(&blk->in_flight) > 0); | ||||
|     AIO_WAIT_WHILE(&blk->wait, | ||||
|             blk_get_aio_context(blk), | ||||
|             atomic_mb_read(&blk->in_flight) > 0); | ||||
|  | ||||
|     if (bs) { | ||||
|         bdrv_drained_end(bs); | ||||
| @@ -1593,7 +1565,8 @@ void blk_drain_all(void) | ||||
|         aio_context_acquire(ctx); | ||||
|  | ||||
|         /* We may have -ENOMEDIUM completions in flight */ | ||||
|         AIO_WAIT_WHILE(ctx, atomic_mb_read(&blk->in_flight) > 0); | ||||
|         AIO_WAIT_WHILE(&blk->wait, ctx, | ||||
|                 atomic_mb_read(&blk->in_flight) > 0); | ||||
|  | ||||
|         aio_context_release(ctx); | ||||
|     } | ||||
| @@ -1645,7 +1618,8 @@ static void send_qmp_error_event(BlockBackend *blk, | ||||
|     qapi_event_send_block_io_error(blk_name(blk), !!bs, | ||||
|                                    bs ? bdrv_get_node_name(bs) : NULL, optype, | ||||
|                                    action, blk_iostatus_is_enabled(blk), | ||||
|                                    error == ENOSPC, strerror(error)); | ||||
|                                    error == ENOSPC, strerror(error), | ||||
|                                    &error_abort); | ||||
| } | ||||
|  | ||||
| /* This is done by device models because, while the block layer knows | ||||
| @@ -1680,7 +1654,7 @@ void blk_error_action(BlockBackend *blk, BlockErrorAction action, | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool blk_is_read_only(BlockBackend *blk) | ||||
| int blk_is_read_only(BlockBackend *blk) | ||||
| { | ||||
|     BlockDriverState *bs = blk_bs(blk); | ||||
|  | ||||
| @@ -1691,18 +1665,18 @@ bool blk_is_read_only(BlockBackend *blk) | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool blk_is_sg(BlockBackend *blk) | ||||
| int blk_is_sg(BlockBackend *blk) | ||||
| { | ||||
|     BlockDriverState *bs = blk_bs(blk); | ||||
|  | ||||
|     if (!bs) { | ||||
|         return false; | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     return bdrv_is_sg(bs); | ||||
| } | ||||
|  | ||||
| bool blk_enable_write_cache(BlockBackend *blk) | ||||
| int blk_enable_write_cache(BlockBackend *blk) | ||||
| { | ||||
|     return blk->enable_write_cache; | ||||
| } | ||||
| @@ -1750,6 +1724,9 @@ void blk_eject(BlockBackend *blk, bool eject_flag) | ||||
|     BlockDriverState *bs = blk_bs(blk); | ||||
|     char *id; | ||||
|  | ||||
|     /* blk_eject is only called by qdevified devices */ | ||||
|     assert(!blk->legacy_dev); | ||||
|  | ||||
|     if (bs) { | ||||
|         bdrv_eject(bs, eject_flag); | ||||
|     } | ||||
| @@ -1758,7 +1735,7 @@ void blk_eject(BlockBackend *blk, bool eject_flag) | ||||
|      * the frontend experienced a tray event. */ | ||||
|     id = blk_get_attached_dev_id(blk); | ||||
|     qapi_event_send_device_tray_moved(blk_name(blk), id, | ||||
|                                       eject_flag); | ||||
|                                       eject_flag, &error_abort); | ||||
|     g_free(id); | ||||
| } | ||||
|  | ||||
| @@ -1845,7 +1822,13 @@ void blk_op_unblock_all(BlockBackend *blk, Error *reason) | ||||
|  | ||||
| AioContext *blk_get_aio_context(BlockBackend *blk) | ||||
| { | ||||
|     return bdrv_get_aio_context(blk_bs(blk)); | ||||
|     BlockDriverState *bs = blk_bs(blk); | ||||
|  | ||||
|     if (bs) { | ||||
|         return bdrv_get_aio_context(bs); | ||||
|     } else { | ||||
|         return qemu_get_aio_context(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb) | ||||
| @@ -1874,15 +1857,8 @@ 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) | ||||
| { | ||||
|     BlockBackendAioNotifier *notifier; | ||||
|     BlockDriverState *bs = blk_bs(blk); | ||||
|  | ||||
|     notifier = g_new(BlockBackendAioNotifier, 1); | ||||
|     notifier->attached_aio_context = attached_aio_context; | ||||
|     notifier->detach_aio_context = detach_aio_context; | ||||
|     notifier->opaque = opaque; | ||||
|     QLIST_INSERT_HEAD(&blk->aio_notifiers, notifier, list); | ||||
|  | ||||
|     if (bs) { | ||||
|         bdrv_add_aio_context_notifier(bs, attached_aio_context, | ||||
|                                       detach_aio_context, opaque); | ||||
| @@ -1895,25 +1871,12 @@ void blk_remove_aio_context_notifier(BlockBackend *blk, | ||||
|                                      void (*detach_aio_context)(void *), | ||||
|                                      void *opaque) | ||||
| { | ||||
|     BlockBackendAioNotifier *notifier; | ||||
|     BlockDriverState *bs = blk_bs(blk); | ||||
|  | ||||
|     if (bs) { | ||||
|         bdrv_remove_aio_context_notifier(bs, attached_aio_context, | ||||
|                                          detach_aio_context, opaque); | ||||
|     } | ||||
|  | ||||
|     QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { | ||||
|         if (notifier->attached_aio_context == attached_aio_context && | ||||
|             notifier->detach_aio_context == detach_aio_context && | ||||
|             notifier->opaque == opaque) { | ||||
|             QLIST_REMOVE(notifier, list); | ||||
|             g_free(notifier); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     abort(); | ||||
| } | ||||
|  | ||||
| void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify) | ||||
| @@ -1986,7 +1949,6 @@ static void blk_pdiscard_entry(void *opaque) | ||||
|     QEMUIOVector *qiov = rwco->iobuf; | ||||
|  | ||||
|     rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size); | ||||
|     aio_wait_kick(); | ||||
| } | ||||
|  | ||||
| int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes) | ||||
| @@ -2168,13 +2130,6 @@ static void blk_root_drained_begin(BdrvChild *child) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static bool blk_root_drained_poll(BdrvChild *child) | ||||
| { | ||||
|     BlockBackend *blk = child->opaque; | ||||
|     assert(blk->quiesce_counter); | ||||
|     return !!blk->in_flight; | ||||
| } | ||||
|  | ||||
| static void blk_root_drained_end(BdrvChild *child) | ||||
| { | ||||
|     BlockBackend *blk = child->opaque; | ||||
| @@ -2199,27 +2154,3 @@ void blk_unregister_buf(BlockBackend *blk, void *host) | ||||
| { | ||||
|     bdrv_unregister_buf(blk_bs(blk), host); | ||||
| } | ||||
|  | ||||
| int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, | ||||
|                                    BlockBackend *blk_out, int64_t off_out, | ||||
|                                    int bytes, BdrvRequestFlags read_flags, | ||||
|                                    BdrvRequestFlags write_flags) | ||||
| { | ||||
|     int r; | ||||
|     r = blk_check_byte_request(blk_in, off_in, bytes); | ||||
|     if (r) { | ||||
|         return r; | ||||
|     } | ||||
|     r = blk_check_byte_request(blk_out, off_out, bytes); | ||||
|     if (r) { | ||||
|         return r; | ||||
|     } | ||||
|     return bdrv_co_copy_range(blk_in->root, off_in, | ||||
|                               blk_out->root, off_out, | ||||
|                               bytes, read_flags, write_flags); | ||||
| } | ||||
|  | ||||
| const BdrvChild *blk_root(BlockBackend *blk) | ||||
| { | ||||
|     return blk->root; | ||||
| } | ||||
|   | ||||
| @@ -85,14 +85,14 @@ static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||
|     const struct bochs_header *bochs = (const void *)buf; | ||||
|  | ||||
|     if (buf_size < HEADER_SIZE) | ||||
|         return 0; | ||||
| 	return 0; | ||||
|  | ||||
|     if (!strcmp(bochs->magic, HEADER_MAGIC) && | ||||
|         !strcmp(bochs->type, REDOLOG_TYPE) && | ||||
|         !strcmp(bochs->subtype, GROWING_TYPE) && | ||||
|         ((le32_to_cpu(bochs->version) == HEADER_VERSION) || | ||||
|         (le32_to_cpu(bochs->version) == HEADER_V1))) | ||||
|         return 100; | ||||
| 	!strcmp(bochs->type, REDOLOG_TYPE) && | ||||
| 	!strcmp(bochs->subtype, GROWING_TYPE) && | ||||
| 	((le32_to_cpu(bochs->version) == HEADER_VERSION) || | ||||
| 	(le32_to_cpu(bochs->version) == HEADER_V1))) | ||||
| 	return 100; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| @@ -105,18 +105,23 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     struct bochs_header bochs; | ||||
|     int ret; | ||||
|  | ||||
|     /* No write support yet */ | ||||
|     ret = bdrv_apply_auto_read_only(bs, NULL, errp); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||
|                                false, errp); | ||||
|     if (!bs->file) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     if (!bdrv_is_read_only(bs)) { | ||||
|         error_report("Opening bochs images without an explicit read-only=on " | ||||
|                      "option is deprecated. Future versions will refuse to " | ||||
|                      "open the image instead of automatically marking the " | ||||
|                      "image read-only."); | ||||
|         ret = bdrv_set_read_only(bs, true, errp); /* no write support yet */ | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
| @@ -125,8 +130,8 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     if (strcmp(bochs.magic, HEADER_MAGIC) || | ||||
|         strcmp(bochs.type, REDOLOG_TYPE) || | ||||
|         strcmp(bochs.subtype, GROWING_TYPE) || | ||||
|         ((le32_to_cpu(bochs.version) != HEADER_VERSION) && | ||||
|         (le32_to_cpu(bochs.version) != HEADER_V1))) { | ||||
| 	((le32_to_cpu(bochs.version) != HEADER_VERSION) && | ||||
| 	(le32_to_cpu(bochs.version) != HEADER_V1))) { | ||||
|         error_setg(errp, "Image not in Bochs format"); | ||||
|         return -EINVAL; | ||||
|     } | ||||
| @@ -158,7 +163,7 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     } | ||||
|  | ||||
|     for (i = 0; i < s->catalog_size; i++) | ||||
|         le32_to_cpus(&s->catalog_bitmap[i]); | ||||
| 	le32_to_cpus(&s->catalog_bitmap[i]); | ||||
|  | ||||
|     s->data_offset = le32_to_cpu(bochs.header) + (s->catalog_size * 4); | ||||
|  | ||||
| @@ -217,7 +222,7 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) | ||||
|     extent_offset = (offset % s->extent_size) / 512; | ||||
|  | ||||
|     if (s->catalog_bitmap[extent_index] == 0xffffffff) { | ||||
|         return 0; /* not allocated */ | ||||
| 	return 0; /* not allocated */ | ||||
|     } | ||||
|  | ||||
|     bitmap_offset = s->data_offset + | ||||
| @@ -232,7 +237,7 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) | ||||
|     } | ||||
|  | ||||
|     if (!((bitmap_entry >> (extent_offset % 8)) & 1)) { | ||||
|         return 0; /* not allocated */ | ||||
| 	return 0; /* not allocated */ | ||||
|     } | ||||
|  | ||||
|     return bitmap_offset + (512 * (s->bitmap_blocks + extent_offset)); | ||||
|   | ||||
| @@ -67,17 +67,23 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     uint32_t offsets_size, max_compressed_block_size = 1, i; | ||||
|     int ret; | ||||
|  | ||||
|     ret = bdrv_apply_auto_read_only(bs, NULL, errp); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||
|                                false, errp); | ||||
|     if (!bs->file) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     if (!bdrv_is_read_only(bs)) { | ||||
|         error_report("Opening cloop images without an explicit read-only=on " | ||||
|                      "option is deprecated. Future versions will refuse to " | ||||
|                      "open the image instead of automatically marking the " | ||||
|                      "image read-only."); | ||||
|         ret = bdrv_set_read_only(bs, true, errp); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* read header */ | ||||
|     ret = bdrv_pread(bs->file, 128, &s->block_size, 4); | ||||
|     if (ret < 0) { | ||||
|   | ||||
							
								
								
									
										197
									
								
								block/commit.c
									
									
									
									
									
								
							
							
						
						
									
										197
									
								
								block/commit.c
									
									
									
									
									
								
							| @@ -31,14 +31,16 @@ enum { | ||||
|     COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */ | ||||
| }; | ||||
|  | ||||
| #define SLICE_TIME 100000000ULL /* ns */ | ||||
|  | ||||
| typedef struct CommitBlockJob { | ||||
|     BlockJob common; | ||||
|     RateLimit limit; | ||||
|     BlockDriverState *commit_top_bs; | ||||
|     BlockBackend *top; | ||||
|     BlockBackend *base; | ||||
|     BlockDriverState *base_bs; | ||||
|     BlockdevOnError on_error; | ||||
|     bool base_read_only; | ||||
|     int base_flags; | ||||
|     char *backing_file_str; | ||||
| } CommitBlockJob; | ||||
|  | ||||
| @@ -69,93 +71,96 @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int commit_prepare(Job *job) | ||||
| typedef struct { | ||||
|     int ret; | ||||
| } CommitCompleteData; | ||||
|  | ||||
| static void commit_complete(BlockJob *job, void *opaque) | ||||
| { | ||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); | ||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common); | ||||
|     CommitCompleteData *data = opaque; | ||||
|     BlockDriverState *top = blk_bs(s->top); | ||||
|     BlockDriverState *base = blk_bs(s->base); | ||||
|     BlockDriverState *commit_top_bs = s->commit_top_bs; | ||||
|     int ret = data->ret; | ||||
|     bool remove_commit_top_bs = false; | ||||
|  | ||||
|     /* Make sure commit_top_bs and top stay around until bdrv_replace_node() */ | ||||
|     bdrv_ref(top); | ||||
|     bdrv_ref(commit_top_bs); | ||||
|  | ||||
|     /* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before | ||||
|      * the normal backing chain can be restored. */ | ||||
|     blk_unref(s->base); | ||||
|     s->base = NULL; | ||||
|  | ||||
|     /* FIXME: bdrv_drop_intermediate treats total failures and partial failures | ||||
|      * identically. Further work is needed to disambiguate these cases. */ | ||||
|     return bdrv_drop_intermediate(s->commit_top_bs, s->base_bs, | ||||
|                                   s->backing_file_str); | ||||
| } | ||||
|  | ||||
| static void commit_abort(Job *job) | ||||
| { | ||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); | ||||
|     BlockDriverState *top_bs = blk_bs(s->top); | ||||
|  | ||||
|     /* Make sure commit_top_bs and top stay around until bdrv_replace_node() */ | ||||
|     bdrv_ref(top_bs); | ||||
|     bdrv_ref(s->commit_top_bs); | ||||
|  | ||||
|     if (s->base) { | ||||
|         blk_unref(s->base); | ||||
|     if (!block_job_is_cancelled(&s->common) && ret == 0) { | ||||
|         /* success */ | ||||
|         ret = bdrv_drop_intermediate(s->commit_top_bs, base, | ||||
|                                      s->backing_file_str); | ||||
|     } else { | ||||
|         /* XXX Can (or should) we somehow keep 'consistent read' blocked even | ||||
|          * after the failed/cancelled commit job is gone? If we already wrote | ||||
|          * something to base, the intermediate images aren't valid any more. */ | ||||
|         remove_commit_top_bs = true; | ||||
|     } | ||||
|  | ||||
|     /* free the blockers on the intermediate nodes so that bdrv_replace_nodes | ||||
|      * can succeed */ | ||||
|     block_job_remove_all_bdrv(&s->common); | ||||
|  | ||||
|     /* If bdrv_drop_intermediate() failed (or was not invoked), remove the | ||||
|      * commit filter driver from the backing chain now. Do this as the final | ||||
|      * step so that the 'consistent read' permission can be granted. | ||||
|      * | ||||
|      * XXX Can (or should) we somehow keep 'consistent read' blocked even | ||||
|      * after the failed/cancelled commit job is gone? If we already wrote | ||||
|      * something to base, the intermediate images aren't valid any more. */ | ||||
|     bdrv_child_try_set_perm(s->commit_top_bs->backing, 0, BLK_PERM_ALL, | ||||
|                             &error_abort); | ||||
|     bdrv_replace_node(s->commit_top_bs, backing_bs(s->commit_top_bs), | ||||
|                       &error_abort); | ||||
|  | ||||
|     bdrv_unref(s->commit_top_bs); | ||||
|     bdrv_unref(top_bs); | ||||
| } | ||||
|  | ||||
| static void commit_clean(Job *job) | ||||
| { | ||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); | ||||
|  | ||||
|     /* restore base open flags here if appropriate (e.g., change the base back | ||||
|      * to r/o). These reopens do not need to be atomic, since we won't abort | ||||
|      * even on failure here */ | ||||
|     if (s->base_read_only) { | ||||
|         bdrv_reopen_set_read_only(s->base_bs, true, NULL); | ||||
|     if (s->base_flags != bdrv_get_flags(base)) { | ||||
|         bdrv_reopen(base, s->base_flags, NULL); | ||||
|     } | ||||
|  | ||||
|     g_free(s->backing_file_str); | ||||
|     blk_unref(s->top); | ||||
|  | ||||
|     /* If there is more than one reference to the job (e.g. if called from | ||||
|      * block_job_finish_sync()), block_job_completed() won't free it and | ||||
|      * therefore the blockers on the intermediate nodes remain. This would | ||||
|      * cause bdrv_set_backing_hd() to fail. */ | ||||
|     block_job_remove_all_bdrv(job); | ||||
|  | ||||
|     block_job_completed(&s->common, ret); | ||||
|     g_free(data); | ||||
|  | ||||
|     /* If bdrv_drop_intermediate() didn't already do that, remove the commit | ||||
|      * filter driver from the backing chain. Do this as the final step so that | ||||
|      * the 'consistent read' permission can be granted.  */ | ||||
|     if (remove_commit_top_bs) { | ||||
|         bdrv_child_try_set_perm(commit_top_bs->backing, 0, BLK_PERM_ALL, | ||||
|                                 &error_abort); | ||||
|         bdrv_replace_node(commit_top_bs, backing_bs(commit_top_bs), | ||||
|                           &error_abort); | ||||
|     } | ||||
|  | ||||
|     bdrv_unref(commit_top_bs); | ||||
|     bdrv_unref(top); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn commit_run(Job *job, Error **errp) | ||||
| static void coroutine_fn commit_run(void *opaque) | ||||
| { | ||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); | ||||
|     CommitBlockJob *s = opaque; | ||||
|     CommitCompleteData *data; | ||||
|     int64_t offset; | ||||
|     uint64_t delay_ns = 0; | ||||
|     int ret = 0; | ||||
|     int64_t n = 0; /* bytes */ | ||||
|     void *buf = NULL; | ||||
|     int bytes_written = 0; | ||||
|     int64_t len, base_len; | ||||
|     int64_t base_len; | ||||
|  | ||||
|     ret = len = blk_getlength(s->top); | ||||
|     if (len < 0) { | ||||
|     ret = s->common.len = blk_getlength(s->top); | ||||
|  | ||||
|     if (s->common.len < 0) { | ||||
|         goto out; | ||||
|     } | ||||
|     job_progress_set_remaining(&s->common.job, len); | ||||
|  | ||||
|     ret = base_len = blk_getlength(s->base); | ||||
|     if (base_len < 0) { | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     if (base_len < len) { | ||||
|         ret = blk_truncate(s->base, len, PREALLOC_MODE_OFF, NULL); | ||||
|     if (base_len < s->common.len) { | ||||
|         ret = blk_truncate(s->base, s->common.len, PREALLOC_MODE_OFF, NULL); | ||||
|         if (ret) { | ||||
|             goto out; | ||||
|         } | ||||
| @@ -163,14 +168,14 @@ static int coroutine_fn commit_run(Job *job, Error **errp) | ||||
|  | ||||
|     buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE); | ||||
|  | ||||
|     for (offset = 0; offset < len; offset += n) { | ||||
|     for (offset = 0; offset < s->common.len; offset += n) { | ||||
|         bool copy; | ||||
|  | ||||
|         /* Note that even when no rate limit is applied we need to yield | ||||
|          * with no pending I/O here so that bdrv_drain_all() returns. | ||||
|          */ | ||||
|         job_sleep_ns(&s->common.job, delay_ns); | ||||
|         if (job_is_cancelled(&s->common.job)) { | ||||
|         block_job_sleep_ns(&s->common, delay_ns); | ||||
|         if (block_job_is_cancelled(&s->common)) { | ||||
|             break; | ||||
|         } | ||||
|         /* Copy if allocated above the base */ | ||||
| @@ -193,12 +198,10 @@ static int coroutine_fn commit_run(Job *job, Error **errp) | ||||
|             } | ||||
|         } | ||||
|         /* Publish progress */ | ||||
|         job_progress_update(&s->common.job, n); | ||||
|         s->common.offset += n; | ||||
|  | ||||
|         if (copy) { | ||||
|             delay_ns = block_job_ratelimit_get_delay(&s->common, n); | ||||
|         } else { | ||||
|             delay_ns = 0; | ||||
|         if (copy && s->common.speed) { | ||||
|             delay_ns = ratelimit_calculate_delay(&s->limit, n); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -207,21 +210,27 @@ static int coroutine_fn commit_run(Job *job, Error **errp) | ||||
| out: | ||||
|     qemu_vfree(buf); | ||||
|  | ||||
|     return ret; | ||||
|     data = g_malloc(sizeof(*data)); | ||||
|     data->ret = ret; | ||||
|     block_job_defer_to_main_loop(&s->common, commit_complete, data); | ||||
| } | ||||
|  | ||||
| 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"); | ||||
|         return; | ||||
|     } | ||||
|     ratelimit_set_speed(&s->limit, speed, SLICE_TIME); | ||||
| } | ||||
|  | ||||
| static const BlockJobDriver commit_job_driver = { | ||||
|     .job_driver = { | ||||
|         .instance_size = sizeof(CommitBlockJob), | ||||
|         .job_type      = JOB_TYPE_COMMIT, | ||||
|         .free          = block_job_free, | ||||
|         .user_resume   = block_job_user_resume, | ||||
|         .drain         = block_job_drain, | ||||
|         .run           = commit_run, | ||||
|         .prepare       = commit_prepare, | ||||
|         .abort         = commit_abort, | ||||
|         .clean         = commit_clean | ||||
|     }, | ||||
|     .instance_size = sizeof(CommitBlockJob), | ||||
|     .job_type      = BLOCK_JOB_TYPE_COMMIT, | ||||
|     .set_speed     = commit_set_speed, | ||||
|     .start         = commit_run, | ||||
| }; | ||||
|  | ||||
| static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs, | ||||
| @@ -237,6 +246,10 @@ static void bdrv_commit_top_refresh_filename(BlockDriverState *bs, QDict *opts) | ||||
|             bs->backing->bs->filename); | ||||
| } | ||||
|  | ||||
| static void bdrv_commit_top_close(BlockDriverState *bs) | ||||
| { | ||||
| } | ||||
|  | ||||
| static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c, | ||||
|                                        const BdrvChildRole *role, | ||||
|                                        BlockReopenQueue *reopen_queue, | ||||
| @@ -254,16 +267,17 @@ static BlockDriver bdrv_commit_top = { | ||||
|     .bdrv_co_preadv             = bdrv_commit_top_preadv, | ||||
|     .bdrv_co_block_status       = bdrv_co_block_status_from_backing, | ||||
|     .bdrv_refresh_filename      = bdrv_commit_top_refresh_filename, | ||||
|     .bdrv_close                 = bdrv_commit_top_close, | ||||
|     .bdrv_child_perm            = bdrv_commit_top_child_perm, | ||||
| }; | ||||
|  | ||||
| void commit_start(const char *job_id, BlockDriverState *bs, | ||||
|                   BlockDriverState *base, BlockDriverState *top, | ||||
|                   int creation_flags, int64_t speed, | ||||
|                   BlockDriverState *base, BlockDriverState *top, int64_t speed, | ||||
|                   BlockdevOnError on_error, const char *backing_file_str, | ||||
|                   const char *filter_node_name, Error **errp) | ||||
| { | ||||
|     CommitBlockJob *s; | ||||
|     int orig_base_flags; | ||||
|     BlockDriverState *iter; | ||||
|     BlockDriverState *commit_top_bs = NULL; | ||||
|     Error *local_err = NULL; | ||||
| @@ -275,16 +289,18 @@ void commit_start(const char *job_id, BlockDriverState *bs, | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL, | ||||
|                          speed, creation_flags, NULL, NULL, errp); | ||||
|     s = block_job_create(job_id, &commit_job_driver, bs, 0, BLK_PERM_ALL, | ||||
|                          speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp); | ||||
|     if (!s) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* convert base to r/w, if necessary */ | ||||
|     s->base_read_only = bdrv_is_read_only(base); | ||||
|     if (s->base_read_only) { | ||||
|         if (bdrv_reopen_set_read_only(base, false, errp) != 0) { | ||||
|     orig_base_flags = bdrv_get_flags(base); | ||||
|     if (!(orig_base_flags & BDRV_O_RDWR)) { | ||||
|         bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err); | ||||
|         if (local_err != NULL) { | ||||
|             error_propagate(errp, local_err); | ||||
|             goto fail; | ||||
|         } | ||||
|     } | ||||
| @@ -351,7 +367,6 @@ void commit_start(const char *job_id, BlockDriverState *bs, | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|     s->base_bs = base; | ||||
|  | ||||
|     /* Required permissions are already taken with block_job_add_bdrv() */ | ||||
|     s->top = blk_new(0, BLK_PERM_ALL); | ||||
| @@ -360,11 +375,12 @@ void commit_start(const char *job_id, BlockDriverState *bs, | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     s->base_flags = orig_base_flags; | ||||
|     s->backing_file_str = g_strdup(backing_file_str); | ||||
|     s->on_error = on_error; | ||||
|  | ||||
|     trace_commit_start(bs, base, top, s); | ||||
|     job_start(&s->common.job); | ||||
|     block_job_start(&s->common); | ||||
|     return; | ||||
|  | ||||
| fail: | ||||
| @@ -377,7 +393,7 @@ fail: | ||||
|     if (commit_top_bs) { | ||||
|         bdrv_replace_node(commit_top_bs, top, &error_abort); | ||||
|     } | ||||
|     job_early_fail(&s->common.job); | ||||
|     block_job_early_fail(&s->common); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -391,7 +407,7 @@ int bdrv_commit(BlockDriverState *bs) | ||||
|     BlockDriverState *commit_top_bs = NULL; | ||||
|     BlockDriver *drv = bs->drv; | ||||
|     int64_t offset, length, backing_length; | ||||
|     int ro; | ||||
|     int ro, open_flags; | ||||
|     int64_t n; | ||||
|     int ret = 0; | ||||
|     uint8_t *buf = NULL; | ||||
| @@ -410,9 +426,10 @@ int bdrv_commit(BlockDriverState *bs) | ||||
|     } | ||||
|  | ||||
|     ro = bs->backing->bs->read_only; | ||||
|     open_flags =  bs->backing->bs->open_flags; | ||||
|  | ||||
|     if (ro) { | ||||
|         if (bdrv_reopen_set_read_only(bs->backing->bs, false, NULL)) { | ||||
|         if (bdrv_reopen(bs->backing->bs, open_flags | BDRV_O_RDWR, NULL)) { | ||||
|             return -EACCES; | ||||
|         } | ||||
|     } | ||||
| @@ -522,7 +539,7 @@ ro_cleanup: | ||||
|  | ||||
|     if (ro) { | ||||
|         /* ignoring error return here */ | ||||
|         bdrv_reopen_set_read_only(bs->backing->bs, true, NULL); | ||||
|         bdrv_reopen(bs->backing->bs, open_flags & ~BDRV_O_RDWR, NULL); | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
|   | ||||
| @@ -1,167 +0,0 @@ | ||||
| /* | ||||
|  * Copy-on-read filter block driver | ||||
|  * | ||||
|  * Copyright (c) 2018 Red Hat, Inc. | ||||
|  * | ||||
|  * Author: | ||||
|  *   Max Reitz <mreitz@redhat.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/block_int.h" | ||||
|  | ||||
|  | ||||
| static int cor_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                     Error **errp) | ||||
| { | ||||
|     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, | ||||
|                                errp); | ||||
|     if (!bs->file) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | | ||||
|                                 (BDRV_REQ_FUA & | ||||
|                                     bs->file->bs->supported_write_flags); | ||||
|  | ||||
|     bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | | ||||
|                                ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & | ||||
|                                     bs->file->bs->supported_zero_flags); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| #define PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \ | ||||
|                           | BLK_PERM_WRITE \ | ||||
|                           | BLK_PERM_RESIZE) | ||||
| #define PERM_UNCHANGED (BLK_PERM_ALL & ~PERM_PASSTHROUGH) | ||||
|  | ||||
| static void cor_child_perm(BlockDriverState *bs, BdrvChild *c, | ||||
|                            const BdrvChildRole *role, | ||||
|                            BlockReopenQueue *reopen_queue, | ||||
|                            uint64_t perm, uint64_t shared, | ||||
|                            uint64_t *nperm, uint64_t *nshared) | ||||
| { | ||||
|     if (c == NULL) { | ||||
|         *nperm = (perm & PERM_PASSTHROUGH) | BLK_PERM_WRITE_UNCHANGED; | ||||
|         *nshared = (shared & PERM_PASSTHROUGH) | PERM_UNCHANGED; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     *nperm = (perm & PERM_PASSTHROUGH) | | ||||
|              (c->perm & PERM_UNCHANGED); | ||||
|     *nshared = (shared & PERM_PASSTHROUGH) | | ||||
|                (c->shared_perm & PERM_UNCHANGED); | ||||
| } | ||||
|  | ||||
|  | ||||
| static int64_t cor_getlength(BlockDriverState *bs) | ||||
| { | ||||
|     return bdrv_getlength(bs->file->bs); | ||||
| } | ||||
|  | ||||
|  | ||||
| static int coroutine_fn cor_co_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                                         PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     return bdrv_co_truncate(bs->file, offset, prealloc, errp); | ||||
| } | ||||
|  | ||||
|  | ||||
| static int coroutine_fn cor_co_preadv(BlockDriverState *bs, | ||||
|                                       uint64_t offset, uint64_t bytes, | ||||
|                                       QEMUIOVector *qiov, int flags) | ||||
| { | ||||
|     return bdrv_co_preadv(bs->file, offset, bytes, qiov, | ||||
|                           flags | BDRV_REQ_COPY_ON_READ); | ||||
| } | ||||
|  | ||||
|  | ||||
| static int coroutine_fn cor_co_pwritev(BlockDriverState *bs, | ||||
|                                        uint64_t offset, uint64_t bytes, | ||||
|                                        QEMUIOVector *qiov, int flags) | ||||
| { | ||||
|  | ||||
|     return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); | ||||
| } | ||||
|  | ||||
|  | ||||
| static int coroutine_fn cor_co_pwrite_zeroes(BlockDriverState *bs, | ||||
|                                              int64_t offset, int bytes, | ||||
|                                              BdrvRequestFlags flags) | ||||
| { | ||||
|     return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); | ||||
| } | ||||
|  | ||||
|  | ||||
| static int coroutine_fn cor_co_pdiscard(BlockDriverState *bs, | ||||
|                                         int64_t offset, int bytes) | ||||
| { | ||||
|     return bdrv_co_pdiscard(bs->file, offset, bytes); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void cor_eject(BlockDriverState *bs, bool eject_flag) | ||||
| { | ||||
|     bdrv_eject(bs->file->bs, eject_flag); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void cor_lock_medium(BlockDriverState *bs, bool locked) | ||||
| { | ||||
|     bdrv_lock_medium(bs->file->bs, locked); | ||||
| } | ||||
|  | ||||
|  | ||||
| static bool cor_recurse_is_first_non_filter(BlockDriverState *bs, | ||||
|                                             BlockDriverState *candidate) | ||||
| { | ||||
|     return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate); | ||||
| } | ||||
|  | ||||
|  | ||||
| BlockDriver bdrv_copy_on_read = { | ||||
|     .format_name                        = "copy-on-read", | ||||
|  | ||||
|     .bdrv_open                          = cor_open, | ||||
|     .bdrv_child_perm                    = cor_child_perm, | ||||
|  | ||||
|     .bdrv_getlength                     = cor_getlength, | ||||
|     .bdrv_co_truncate                   = cor_co_truncate, | ||||
|  | ||||
|     .bdrv_co_preadv                     = cor_co_preadv, | ||||
|     .bdrv_co_pwritev                    = cor_co_pwritev, | ||||
|     .bdrv_co_pwrite_zeroes              = cor_co_pwrite_zeroes, | ||||
|     .bdrv_co_pdiscard                   = cor_co_pdiscard, | ||||
|  | ||||
|     .bdrv_eject                         = cor_eject, | ||||
|     .bdrv_lock_medium                   = cor_lock_medium, | ||||
|  | ||||
|     .bdrv_co_block_status               = bdrv_co_block_status_from_file, | ||||
|  | ||||
|     .bdrv_recurse_is_first_non_filter   = cor_recurse_is_first_non_filter, | ||||
|  | ||||
|     .has_variable_length                = true, | ||||
|     .is_filter                          = true, | ||||
| }; | ||||
|  | ||||
| static void bdrv_copy_on_read_init(void) | ||||
| { | ||||
|     bdrv_register(&bdrv_copy_on_read); | ||||
| } | ||||
|  | ||||
| block_init(bdrv_copy_on_read_init); | ||||
| @@ -24,44 +24,28 @@ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "block/block_int.h" | ||||
| #include "qemu/job.h" | ||||
| #include "qapi/qapi-commands-block-core.h" | ||||
| #include "qapi/qapi-visit-block-core.h" | ||||
| #include "qapi/clone-visitor.h" | ||||
| #include "qapi/error.h" | ||||
|  | ||||
| typedef struct BlockdevCreateJob { | ||||
|     Job common; | ||||
| typedef struct BlockdevCreateCo { | ||||
|     BlockDriver *drv; | ||||
|     BlockdevCreateOptions *opts; | ||||
| } BlockdevCreateJob; | ||||
|  | ||||
| static int coroutine_fn blockdev_create_run(Job *job, Error **errp) | ||||
| { | ||||
|     BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); | ||||
|     int ret; | ||||
|     Error **errp; | ||||
| } BlockdevCreateCo; | ||||
|  | ||||
|     job_progress_set_remaining(&s->common, 1); | ||||
|     ret = s->drv->bdrv_co_create(s->opts, errp); | ||||
|     job_progress_update(&s->common, 1); | ||||
|  | ||||
|     qapi_free_BlockdevCreateOptions(s->opts); | ||||
|  | ||||
|     return ret; | ||||
| static void coroutine_fn bdrv_co_create_co_entry(void *opaque) | ||||
| { | ||||
|     BlockdevCreateCo *cco = opaque; | ||||
|     cco->ret = cco->drv->bdrv_co_create(cco->opts, cco->errp); | ||||
| } | ||||
|  | ||||
| static const JobDriver blockdev_create_job_driver = { | ||||
|     .instance_size = sizeof(BlockdevCreateJob), | ||||
|     .job_type      = JOB_TYPE_CREATE, | ||||
|     .run           = blockdev_create_run, | ||||
| }; | ||||
|  | ||||
| void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options, | ||||
|                          Error **errp) | ||||
| void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp) | ||||
| { | ||||
|     BlockdevCreateJob *s; | ||||
|     const char *fmt = BlockdevDriver_str(options->driver); | ||||
|     BlockDriver *drv = bdrv_find_format(fmt); | ||||
|     Coroutine *co; | ||||
|     BlockdevCreateCo cco; | ||||
|  | ||||
|     /* If the driver is in the schema, we know that it exists. But it may not | ||||
|      * be whitelisted. */ | ||||
| @@ -71,24 +55,22 @@ void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options, | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* Error out if the driver doesn't support .bdrv_co_create */ | ||||
|     /* Call callback if it exists */ | ||||
|     if (!drv->bdrv_co_create) { | ||||
|         error_setg(errp, "Driver does not support blockdev-create"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* Create the block job */ | ||||
|     /* TODO Running in the main context. Block drivers need to error out or add | ||||
|      * locking when they use a BDS in a different AioContext. */ | ||||
|     s = job_create(job_id, &blockdev_create_job_driver, NULL, | ||||
|                    qemu_get_aio_context(), JOB_DEFAULT | JOB_MANUAL_DISMISS, | ||||
|                    NULL, NULL, errp); | ||||
|     if (!s) { | ||||
|         return; | ||||
|     cco = (BlockdevCreateCo) { | ||||
|         .drv = drv, | ||||
|         .opts = options, | ||||
|         .ret = -EINPROGRESS, | ||||
|         .errp = errp, | ||||
|     }; | ||||
|  | ||||
|     co = qemu_coroutine_create(bdrv_co_create_co_entry, &cco); | ||||
|     qemu_coroutine_enter(co); | ||||
|     while (cco.ret == -EINPROGRESS) { | ||||
|         aio_poll(qemu_get_aio_context(), true); | ||||
|     } | ||||
|  | ||||
|     s->drv = drv, | ||||
|     s->opts = QAPI_CLONE(BlockdevCreateOptions, options), | ||||
|  | ||||
|     job_start(&s->common); | ||||
| } | ||||
|   | ||||
							
								
								
									
										269
									
								
								block/crypto.c
									
									
									
									
									
								
							
							
						
						
									
										269
									
								
								block/crypto.c
									
									
									
									
									
								
							| @@ -21,15 +21,15 @@ | ||||
| #include "qemu/osdep.h" | ||||
|  | ||||
| #include "block/block_int.h" | ||||
| #include "block/qdict.h" | ||||
| #include "sysemu/block-backend.h" | ||||
| #include "crypto/block.h" | ||||
| #include "qapi/opts-visitor.h" | ||||
| #include "qapi/qapi-visit-crypto.h" | ||||
| #include "qapi/qmp/qdict.h" | ||||
| #include "qapi/qobject-input-visitor.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qemu/option.h" | ||||
| #include "crypto.h" | ||||
| #include "block/crypto.h" | ||||
|  | ||||
| typedef struct BlockCrypto BlockCrypto; | ||||
|  | ||||
| @@ -71,6 +71,8 @@ static ssize_t block_crypto_read_func(QCryptoBlock *block, | ||||
|  | ||||
|  | ||||
| struct BlockCryptoCreateData { | ||||
|     const char *filename; | ||||
|     QemuOpts *opts; | ||||
|     BlockBackend *blk; | ||||
|     uint64_t size; | ||||
| }; | ||||
| @@ -101,18 +103,27 @@ static ssize_t block_crypto_init_func(QCryptoBlock *block, | ||||
|                                       Error **errp) | ||||
| { | ||||
|     struct BlockCryptoCreateData *data = opaque; | ||||
|  | ||||
|     if (data->size > INT64_MAX || headerlen > INT64_MAX - data->size) { | ||||
|         error_setg(errp, "The requested file size is too large"); | ||||
|         return -EFBIG; | ||||
|     } | ||||
|     int ret; | ||||
|  | ||||
|     /* User provided size should reflect amount of space made | ||||
|      * available to the guest, so we must take account of that | ||||
|      * which will be used by the crypto header | ||||
|      */ | ||||
|     return blk_truncate(data->blk, data->size + headerlen, PREALLOC_MODE_OFF, | ||||
|                         errp); | ||||
|     data->size += headerlen; | ||||
|  | ||||
|     qemu_opt_set_number(data->opts, BLOCK_OPT_SIZE, data->size, &error_abort); | ||||
|     ret = bdrv_create_file(data->filename, data->opts, errp); | ||||
|     if (ret < 0) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     data->blk = blk_new_open(data->filename, NULL, NULL, | ||||
|                              BDRV_O_RDWR | BDRV_O_PROTOCOL, errp); | ||||
|     if (!data->blk) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -148,36 +159,102 @@ static QemuOptsList block_crypto_create_opts_luks = { | ||||
|  | ||||
|  | ||||
| QCryptoBlockOpenOptions * | ||||
| block_crypto_open_opts_init(QDict *opts, Error **errp) | ||||
| block_crypto_open_opts_init(QCryptoBlockFormat format, | ||||
|                             QDict *opts, | ||||
|                             Error **errp) | ||||
| { | ||||
|     Visitor *v; | ||||
|     QCryptoBlockOpenOptions *ret; | ||||
|     QCryptoBlockOpenOptions *ret = NULL; | ||||
|     Error *local_err = NULL; | ||||
|  | ||||
|     v = qobject_input_visitor_new_flat_confused(opts, errp); | ||||
|     if (!v) { | ||||
|         return NULL; | ||||
|     ret = g_new0(QCryptoBlockOpenOptions, 1); | ||||
|     ret->format = format; | ||||
|  | ||||
|     v = qobject_input_visitor_new_keyval(QOBJECT(opts)); | ||||
|  | ||||
|     visit_start_struct(v, NULL, NULL, 0, &local_err); | ||||
|     if (local_err) { | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     visit_type_QCryptoBlockOpenOptions(v, NULL, &ret, errp); | ||||
|     switch (format) { | ||||
|     case Q_CRYPTO_BLOCK_FORMAT_LUKS: | ||||
|         visit_type_QCryptoBlockOptionsLUKS_members( | ||||
|             v, &ret->u.luks, &local_err); | ||||
|         break; | ||||
|  | ||||
|     case Q_CRYPTO_BLOCK_FORMAT_QCOW: | ||||
|         visit_type_QCryptoBlockOptionsQCow_members( | ||||
|             v, &ret->u.qcow, &local_err); | ||||
|         break; | ||||
|  | ||||
|     default: | ||||
|         error_setg(&local_err, "Unsupported block format %d", format); | ||||
|         break; | ||||
|     } | ||||
|     if (!local_err) { | ||||
|         visit_check_struct(v, &local_err); | ||||
|     } | ||||
|  | ||||
|     visit_end_struct(v, NULL); | ||||
|  | ||||
|  out: | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         qapi_free_QCryptoBlockOpenOptions(ret); | ||||
|         ret = NULL; | ||||
|     } | ||||
|     visit_free(v); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
|  | ||||
| QCryptoBlockCreateOptions * | ||||
| block_crypto_create_opts_init(QDict *opts, Error **errp) | ||||
| block_crypto_create_opts_init(QCryptoBlockFormat format, | ||||
|                               QDict *opts, | ||||
|                               Error **errp) | ||||
| { | ||||
|     Visitor *v; | ||||
|     QCryptoBlockCreateOptions *ret; | ||||
|     QCryptoBlockCreateOptions *ret = NULL; | ||||
|     Error *local_err = NULL; | ||||
|  | ||||
|     v = qobject_input_visitor_new_flat_confused(opts, errp); | ||||
|     if (!v) { | ||||
|         return NULL; | ||||
|     ret = g_new0(QCryptoBlockCreateOptions, 1); | ||||
|     ret->format = format; | ||||
|  | ||||
|     v = qobject_input_visitor_new_keyval(QOBJECT(opts)); | ||||
|  | ||||
|     visit_start_struct(v, NULL, NULL, 0, &local_err); | ||||
|     if (local_err) { | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     visit_type_QCryptoBlockCreateOptions(v, NULL, &ret, errp); | ||||
|     switch (format) { | ||||
|     case Q_CRYPTO_BLOCK_FORMAT_LUKS: | ||||
|         visit_type_QCryptoBlockCreateOptionsLUKS_members( | ||||
|             v, &ret->u.luks, &local_err); | ||||
|         break; | ||||
|  | ||||
|     case Q_CRYPTO_BLOCK_FORMAT_QCOW: | ||||
|         visit_type_QCryptoBlockOptionsQCow_members( | ||||
|             v, &ret->u.qcow, &local_err); | ||||
|         break; | ||||
|  | ||||
|     default: | ||||
|         error_setg(&local_err, "Unsupported block format %d", format); | ||||
|         break; | ||||
|     } | ||||
|     if (!local_err) { | ||||
|         visit_check_struct(v, &local_err); | ||||
|     } | ||||
|  | ||||
|     visit_end_struct(v, NULL); | ||||
|  | ||||
|  out: | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         qapi_free_QCryptoBlockCreateOptions(ret); | ||||
|         ret = NULL; | ||||
|     } | ||||
|     visit_free(v); | ||||
|     return ret; | ||||
| } | ||||
| @@ -215,9 +292,8 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, | ||||
|     } | ||||
|  | ||||
|     cryptoopts = qemu_opts_to_qdict(opts, NULL); | ||||
|     qdict_put_str(cryptoopts, "format", QCryptoBlockFormat_str(format)); | ||||
|  | ||||
|     open_opts = block_crypto_open_opts_init(cryptoopts, errp); | ||||
|     open_opts = block_crypto_open_opts_init(format, cryptoopts, errp); | ||||
|     if (!open_opts) { | ||||
|         goto cleanup; | ||||
|     } | ||||
| @@ -229,7 +305,6 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, | ||||
|                                        block_crypto_read_func, | ||||
|                                        bs, | ||||
|                                        cflags, | ||||
|                                        1, | ||||
|                                        errp); | ||||
|  | ||||
|     if (!crypto->block) { | ||||
| @@ -241,35 +316,36 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, | ||||
|  | ||||
|     ret = 0; | ||||
|  cleanup: | ||||
|     qobject_unref(cryptoopts); | ||||
|     QDECREF(cryptoopts); | ||||
|     qapi_free_QCryptoBlockOpenOptions(open_opts); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int block_crypto_co_create_generic(BlockDriverState *bs, | ||||
|                                           int64_t size, | ||||
|                                           QCryptoBlockCreateOptions *opts, | ||||
|                                           Error **errp) | ||||
| static int block_crypto_create_generic(QCryptoBlockFormat format, | ||||
|                                        const char *filename, | ||||
|                                        QemuOpts *opts, | ||||
|                                        Error **errp) | ||||
| { | ||||
|     int ret; | ||||
|     BlockBackend *blk; | ||||
|     int ret = -EINVAL; | ||||
|     QCryptoBlockCreateOptions *create_opts = NULL; | ||||
|     QCryptoBlock *crypto = NULL; | ||||
|     struct BlockCryptoCreateData data; | ||||
|     struct BlockCryptoCreateData data = { | ||||
|         .size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), | ||||
|                          BDRV_SECTOR_SIZE), | ||||
|         .opts = opts, | ||||
|         .filename = filename, | ||||
|     }; | ||||
|     QDict *cryptoopts; | ||||
|  | ||||
|     blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); | ||||
|     cryptoopts = qemu_opts_to_qdict(opts, NULL); | ||||
|  | ||||
|     ret = blk_insert_bs(blk, bs, errp); | ||||
|     if (ret < 0) { | ||||
|         goto cleanup; | ||||
|     create_opts = block_crypto_create_opts_init(format, cryptoopts, errp); | ||||
|     if (!create_opts) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     data = (struct BlockCryptoCreateData) { | ||||
|         .blk = blk, | ||||
|         .size = size, | ||||
|     }; | ||||
|  | ||||
|     crypto = qcrypto_block_create(opts, NULL, | ||||
|     crypto = qcrypto_block_create(create_opts, NULL, | ||||
|                                   block_crypto_init_func, | ||||
|                                   block_crypto_write_func, | ||||
|                                   &data, | ||||
| @@ -282,27 +358,24 @@ static int block_crypto_co_create_generic(BlockDriverState *bs, | ||||
|  | ||||
|     ret = 0; | ||||
|  cleanup: | ||||
|     QDECREF(cryptoopts); | ||||
|     qcrypto_block_free(crypto); | ||||
|     blk_unref(blk); | ||||
|     blk_unref(data.blk); | ||||
|     qapi_free_QCryptoBlockCreateOptions(create_opts); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn | ||||
| block_crypto_co_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                          PreallocMode prealloc, Error **errp) | ||||
| static int block_crypto_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                                  PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     BlockCrypto *crypto = bs->opaque; | ||||
|     uint64_t payload_offset = | ||||
|         qcrypto_block_get_payload_offset(crypto->block); | ||||
|  | ||||
|     if (payload_offset > INT64_MAX - offset) { | ||||
|         error_setg(errp, "The requested file size is too large"); | ||||
|         return -EFBIG; | ||||
|     } | ||||
|     assert(payload_offset < (INT64_MAX - offset)); | ||||
|  | ||||
|     offset += payload_offset; | ||||
|  | ||||
|     return bdrv_co_truncate(bs->file, offset, prealloc, errp); | ||||
|     return bdrv_truncate(bs->file, offset, prealloc, errp); | ||||
| } | ||||
|  | ||||
| static void block_crypto_close(BlockDriverState *bs) | ||||
| @@ -464,10 +537,7 @@ static int64_t block_crypto_getlength(BlockDriverState *bs) | ||||
|  | ||||
|     uint64_t offset = qcrypto_block_get_payload_offset(crypto->block); | ||||
|     assert(offset < INT64_MAX); | ||||
|  | ||||
|     if (offset > len) { | ||||
|         return -EIO; | ||||
|     } | ||||
|     assert(offset < len); | ||||
|  | ||||
|     len -= offset; | ||||
|  | ||||
| @@ -492,88 +562,12 @@ static int block_crypto_open_luks(BlockDriverState *bs, | ||||
|                                      bs, options, flags, errp); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn | ||||
| block_crypto_co_create_luks(BlockdevCreateOptions *create_options, Error **errp) | ||||
| { | ||||
|     BlockdevCreateOptionsLUKS *luks_opts; | ||||
|     BlockDriverState *bs = NULL; | ||||
|     QCryptoBlockCreateOptions create_opts; | ||||
|     int ret; | ||||
|  | ||||
|     assert(create_options->driver == BLOCKDEV_DRIVER_LUKS); | ||||
|     luks_opts = &create_options->u.luks; | ||||
|  | ||||
|     bs = bdrv_open_blockdev_ref(luks_opts->file, errp); | ||||
|     if (bs == NULL) { | ||||
|         return -EIO; | ||||
|     } | ||||
|  | ||||
|     create_opts = (QCryptoBlockCreateOptions) { | ||||
|         .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, | ||||
|         .u.luks = *qapi_BlockdevCreateOptionsLUKS_base(luks_opts), | ||||
|     }; | ||||
|  | ||||
|     ret = block_crypto_co_create_generic(bs, luks_opts->size, &create_opts, | ||||
|                                          errp); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     ret = 0; | ||||
| fail: | ||||
|     bdrv_unref(bs); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename, | ||||
|                                                          QemuOpts *opts, | ||||
|                                                          Error **errp) | ||||
| { | ||||
|     QCryptoBlockCreateOptions *create_opts = NULL; | ||||
|     BlockDriverState *bs = NULL; | ||||
|     QDict *cryptoopts; | ||||
|     int64_t size; | ||||
|     int ret; | ||||
|  | ||||
|     /* Parse options */ | ||||
|     size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); | ||||
|  | ||||
|     cryptoopts = qemu_opts_to_qdict_filtered(opts, NULL, | ||||
|                                              &block_crypto_create_opts_luks, | ||||
|                                              true); | ||||
|  | ||||
|     qdict_put_str(cryptoopts, "format", "luks"); | ||||
|     create_opts = block_crypto_create_opts_init(cryptoopts, errp); | ||||
|     if (!create_opts) { | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     /* Create protocol layer */ | ||||
|     ret = bdrv_create_file(filename, opts, errp); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     bs = bdrv_open(filename, NULL, NULL, | ||||
|                    BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); | ||||
|     if (!bs) { | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     /* Create format layer */ | ||||
|     ret = block_crypto_co_create_generic(bs, size, create_opts, errp); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     ret = 0; | ||||
| fail: | ||||
|     bdrv_unref(bs); | ||||
|     qapi_free_QCryptoBlockCreateOptions(create_opts); | ||||
|     qobject_unref(cryptoopts); | ||||
|     return ret; | ||||
|     return block_crypto_create_generic(Q_CRYPTO_BLOCK_FORMAT_LUKS, | ||||
|                                        filename, opts, errp); | ||||
| } | ||||
|  | ||||
| static int block_crypto_get_info_luks(BlockDriverState *bs, | ||||
| @@ -628,12 +622,9 @@ BlockDriver bdrv_crypto_luks = { | ||||
|     .bdrv_probe         = block_crypto_probe_luks, | ||||
|     .bdrv_open          = block_crypto_open_luks, | ||||
|     .bdrv_close         = block_crypto_close, | ||||
|     /* This driver doesn't modify LUKS metadata except when creating image. | ||||
|      * Allow share-rw=on as a special case. */ | ||||
|     .bdrv_child_perm    = bdrv_filter_default_perms, | ||||
|     .bdrv_co_create     = block_crypto_co_create_luks, | ||||
|     .bdrv_child_perm    = bdrv_format_default_perms, | ||||
|     .bdrv_co_create_opts = block_crypto_co_create_opts_luks, | ||||
|     .bdrv_co_truncate   = block_crypto_co_truncate, | ||||
|     .bdrv_truncate      = block_crypto_truncate, | ||||
|     .create_opts        = &block_crypto_create_opts_luks, | ||||
|  | ||||
|     .bdrv_reopen_prepare = block_crypto_reopen_prepare, | ||||
|   | ||||
| @@ -89,9 +89,13 @@ | ||||
|     } | ||||
|  | ||||
| QCryptoBlockCreateOptions * | ||||
| block_crypto_create_opts_init(QDict *opts, Error **errp); | ||||
| block_crypto_create_opts_init(QCryptoBlockFormat format, | ||||
|                               QDict *opts, | ||||
|                               Error **errp); | ||||
|  | ||||
| QCryptoBlockOpenOptions * | ||||
| block_crypto_open_opts_init(QDict *opts, Error **errp); | ||||
| block_crypto_open_opts_init(QCryptoBlockFormat format, | ||||
|                             QDict *opts, | ||||
|                             Error **errp); | ||||
|  | ||||
| #endif /* BLOCK_CRYPTO_H__ */ | ||||
|   | ||||
							
								
								
									
										41
									
								
								block/curl.c
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								block/curl.c
									
									
									
									
									
								
							| @@ -32,10 +32,22 @@ | ||||
| #include "crypto/secret.h" | ||||
| #include <curl/curl.h> | ||||
| #include "qemu/cutils.h" | ||||
| #include "trace.h" | ||||
|  | ||||
| // #define DEBUG_CURL | ||||
| // #define DEBUG_VERBOSE | ||||
|  | ||||
| #ifdef DEBUG_CURL | ||||
| #define DEBUG_CURL_PRINT 1 | ||||
| #else | ||||
| #define DEBUG_CURL_PRINT 0 | ||||
| #endif | ||||
| #define DPRINTF(fmt, ...)                                            \ | ||||
|     do {                                                             \ | ||||
|         if (DEBUG_CURL_PRINT) {                                      \ | ||||
|             fprintf(stderr, fmt, ## __VA_ARGS__);                    \ | ||||
|         }                                                            \ | ||||
|     } while (0) | ||||
|  | ||||
| #if LIBCURL_VERSION_NUM >= 0x071000 | ||||
| /* The multi interface timer callback was introduced in 7.16.0 */ | ||||
| #define NEED_CURL_TIMER_CALLBACK | ||||
| @@ -142,7 +154,7 @@ static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque) | ||||
| { | ||||
|     BDRVCURLState *s = opaque; | ||||
|  | ||||
|     trace_curl_timer_cb(timeout_ms); | ||||
|     DPRINTF("CURL: timer callback timeout_ms %ld\n", timeout_ms); | ||||
|     if (timeout_ms == -1) { | ||||
|         timer_del(&s->timer); | ||||
|     } else { | ||||
| @@ -181,7 +193,7 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, | ||||
|     } | ||||
|     socket = NULL; | ||||
|  | ||||
|     trace_curl_sock_cb(action, (int)fd); | ||||
|     DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, (int)fd); | ||||
|     switch (action) { | ||||
|         case CURL_POLL_IN: | ||||
|             aio_set_fd_handler(s->aio_context, fd, false, | ||||
| @@ -226,7 +238,7 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque) | ||||
|     size_t realsize = size * nmemb; | ||||
|     int i; | ||||
|  | ||||
|     trace_curl_read_cb(realsize); | ||||
|     DPRINTF("CURL: Just reading %zd bytes\n", realsize); | ||||
|  | ||||
|     if (!s || !s->orig_buf) { | ||||
|         goto read_end; | ||||
| @@ -471,8 +483,6 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state) | ||||
|         curl_easy_setopt(state->curl, CURLOPT_URL, s->url); | ||||
|         curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER, | ||||
|                          (long) s->sslverify); | ||||
|         curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYHOST, | ||||
|                          s->sslverify ? 2L : 0L); | ||||
|         if (s->cookie) { | ||||
|             curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie); | ||||
|         } | ||||
| @@ -672,10 +682,10 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     const char *protocol_delimiter; | ||||
|     int ret; | ||||
|  | ||||
|     ret = bdrv_apply_auto_read_only(bs, "curl driver does not support writes", | ||||
|                                     errp); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|  | ||||
|     if (flags & BDRV_O_RDWR) { | ||||
|         error_setg(errp, "curl block device does not support writes"); | ||||
|         return -EROFS; | ||||
|     } | ||||
|  | ||||
|     if (!libcurl_initialized) { | ||||
| @@ -765,7 +775,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     trace_curl_open(file); | ||||
|     DPRINTF("CURL: Opening %s\n", file); | ||||
|     qemu_co_queue_init(&s->free_state_waitq); | ||||
|     s->aio_context = bdrv_get_aio_context(bs); | ||||
|     s->url = g_strdup(file); | ||||
| @@ -794,7 +804,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     } | ||||
|     /* Prior CURL 7.19.4 return value of 0 could mean that the file size is not | ||||
|      * know or the size is zero. From 7.19.4 CURL returns -1 if size is not | ||||
|      * known and zero if it is really zero-length file. */ | ||||
|      * known and zero if it is realy zero-length file. */ | ||||
| #if LIBCURL_VERSION_NUM >= 0x071304 | ||||
|     if (d < 0) { | ||||
|         pstrcpy(state->errmsg, CURL_ERROR_SIZE, | ||||
| @@ -818,7 +828,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                 "Server does not support 'range' (byte ranges)."); | ||||
|         goto out; | ||||
|     } | ||||
|     trace_curl_open_size(s->len); | ||||
|     DPRINTF("CURL: Size = %" PRIu64 "\n", s->len); | ||||
|  | ||||
|     qemu_mutex_lock(&s->mutex); | ||||
|     curl_clean_state(state); | ||||
| @@ -896,7 +906,8 @@ static void curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb) | ||||
|     state->acb[0] = acb; | ||||
|  | ||||
|     snprintf(state->range, 127, "%" PRIu64 "-%" PRIu64, start, end); | ||||
|     trace_curl_setup_preadv(acb->bytes, start, state->range); | ||||
|     DPRINTF("CURL (AIO): Reading %" PRIu64 " at %" PRIu64 " (%s)\n", | ||||
|             acb->bytes, start, state->range); | ||||
|     curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range); | ||||
|  | ||||
|     curl_multi_add_handle(s->multi, state->curl); | ||||
| @@ -930,7 +941,7 @@ static void curl_close(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVCURLState *s = bs->opaque; | ||||
|  | ||||
|     trace_curl_close(); | ||||
|     DPRINTF("CURL: Close\n"); | ||||
|     curl_detach_aio_context(bs); | ||||
|     qemu_mutex_destroy(&s->mutex); | ||||
|  | ||||
|   | ||||
| @@ -40,8 +40,6 @@ struct BdrvDirtyBitmap { | ||||
|     QemuMutex *mutex; | ||||
|     HBitmap *bitmap;            /* Dirty bitmap implementation */ | ||||
|     HBitmap *meta;              /* Meta dirty bitmap */ | ||||
|     bool qmp_locked;            /* Bitmap is locked, it can't be modified | ||||
|                                    through QMP */ | ||||
|     BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */ | ||||
|     char *name;                 /* Optional non-empty unique ID */ | ||||
|     int64_t size;               /* Size of the bitmap, in bytes */ | ||||
| @@ -55,10 +53,6 @@ struct BdrvDirtyBitmap { | ||||
|                                    and this bitmap must remain unchanged while | ||||
|                                    this flag is set. */ | ||||
|     bool persistent;            /* bitmap must be saved to owner disk image */ | ||||
|     bool migration;             /* Bitmap is selected for migration, it should | ||||
|                                    not be stored on the next inactivation | ||||
|                                    (persistent flag doesn't matter until next | ||||
|                                    invalidation).*/ | ||||
|     QLIST_ENTRY(BdrvDirtyBitmap) list; | ||||
| }; | ||||
|  | ||||
| @@ -101,6 +95,15 @@ BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name) | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken.  */ | ||||
| void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     assert(!bdrv_dirty_bitmap_frozen(bitmap)); | ||||
|     g_free(bitmap->name); | ||||
|     bitmap->name = NULL; | ||||
|     bitmap->persistent = false; | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken.  */ | ||||
| BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, | ||||
|                                           uint32_t granularity, | ||||
| @@ -180,24 +183,6 @@ bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap) | ||||
|     return bitmap->successor; | ||||
| } | ||||
|  | ||||
| /* Both conditions disallow user-modification via QMP. */ | ||||
| bool bdrv_dirty_bitmap_user_locked(BdrvDirtyBitmap *bitmap) { | ||||
|     return bdrv_dirty_bitmap_frozen(bitmap) || | ||||
|            bdrv_dirty_bitmap_qmp_locked(bitmap); | ||||
| } | ||||
|  | ||||
| void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked) | ||||
| { | ||||
|     qemu_mutex_lock(bitmap->mutex); | ||||
|     bitmap->qmp_locked = qmp_locked; | ||||
|     qemu_mutex_unlock(bitmap->mutex); | ||||
| } | ||||
|  | ||||
| bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     return bitmap->qmp_locked; | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken.  */ | ||||
| bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
| @@ -209,8 +194,6 @@ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     if (bdrv_dirty_bitmap_frozen(bitmap)) { | ||||
|         return DIRTY_BITMAP_STATUS_FROZEN; | ||||
|     } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) { | ||||
|         return DIRTY_BITMAP_STATUS_LOCKED; | ||||
|     } else if (!bdrv_dirty_bitmap_enabled(bitmap)) { | ||||
|         return DIRTY_BITMAP_STATUS_DISABLED; | ||||
|     } else { | ||||
| @@ -251,33 +234,6 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     assert(!bdrv_dirty_bitmap_frozen(bitmap)); | ||||
|     bitmap->disabled = false; | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken. */ | ||||
| void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     assert(bitmap->mutex == bitmap->successor->mutex); | ||||
|     qemu_mutex_lock(bitmap->mutex); | ||||
|     bdrv_enable_dirty_bitmap_locked(bitmap->successor); | ||||
|     qemu_mutex_unlock(bitmap->mutex); | ||||
| } | ||||
|  | ||||
| /* Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken.  */ | ||||
| static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     assert(!bitmap->active_iterators); | ||||
|     assert(!bdrv_dirty_bitmap_frozen(bitmap)); | ||||
|     assert(!bitmap->meta); | ||||
|     QLIST_REMOVE(bitmap, list); | ||||
|     hbitmap_free(bitmap->bitmap); | ||||
|     g_free(bitmap->name); | ||||
|     g_free(bitmap); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * For a bitmap with a successor, yield our name to the successor, | ||||
|  * delete the old bitmap, and return a handle to the new bitmap. | ||||
| @@ -311,11 +267,11 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, | ||||
|  * In cases of failure where we can no longer safely delete the parent, | ||||
|  * we may wish to re-join the parent and child/successor. | ||||
|  * The merged parent will be un-frozen, but not explicitly re-enabled. | ||||
|  * Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken. | ||||
|  * Called with BQL taken. | ||||
|  */ | ||||
| BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, | ||||
|                                                   BdrvDirtyBitmap *parent, | ||||
|                                                   Error **errp) | ||||
| BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, | ||||
|                                            BdrvDirtyBitmap *parent, | ||||
|                                            Error **errp) | ||||
| { | ||||
|     BdrvDirtyBitmap *successor = parent->successor; | ||||
|  | ||||
| @@ -324,30 +280,16 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     if (!hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap)) { | ||||
|     if (!hbitmap_merge(parent->bitmap, successor->bitmap)) { | ||||
|         error_setg(errp, "Merging of parent and successor bitmap failed"); | ||||
|         return NULL; | ||||
|     } | ||||
|     bdrv_release_dirty_bitmap_locked(successor); | ||||
|     bdrv_release_dirty_bitmap(bs, successor); | ||||
|     parent->successor = NULL; | ||||
|  | ||||
|     return parent; | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken. */ | ||||
| BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, | ||||
|                                            BdrvDirtyBitmap *parent, | ||||
|                                            Error **errp) | ||||
| { | ||||
|     BdrvDirtyBitmap *ret; | ||||
|  | ||||
|     qemu_mutex_lock(parent->mutex); | ||||
|     ret = bdrv_reclaim_dirty_bitmap_locked(bs, parent, errp); | ||||
|     qemu_mutex_unlock(parent->mutex); | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Truncates _all_ bitmaps attached to a BDS. | ||||
|  * Called with BQL taken. | ||||
| @@ -366,12 +308,45 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes) | ||||
|     bdrv_dirty_bitmaps_unlock(bs); | ||||
| } | ||||
|  | ||||
| static bool bdrv_dirty_bitmap_has_name(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     return !!bdrv_dirty_bitmap_name(bitmap); | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken.  */ | ||||
| static void bdrv_do_release_matching_dirty_bitmap( | ||||
|     BlockDriverState *bs, BdrvDirtyBitmap *bitmap, | ||||
|     bool (*cond)(BdrvDirtyBitmap *bitmap)) | ||||
| { | ||||
|     BdrvDirtyBitmap *bm, *next; | ||||
|     bdrv_dirty_bitmaps_lock(bs); | ||||
|     QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { | ||||
|         if ((!bitmap || bm == bitmap) && (!cond || cond(bm))) { | ||||
|             assert(!bm->active_iterators); | ||||
|             assert(!bdrv_dirty_bitmap_frozen(bm)); | ||||
|             assert(!bm->meta); | ||||
|             QLIST_REMOVE(bm, list); | ||||
|             hbitmap_free(bm->bitmap); | ||||
|             g_free(bm->name); | ||||
|             g_free(bm); | ||||
|  | ||||
|             if (bitmap) { | ||||
|                 goto out; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     if (bitmap) { | ||||
|         abort(); | ||||
|     } | ||||
|  | ||||
| out: | ||||
|     bdrv_dirty_bitmaps_unlock(bs); | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken.  */ | ||||
| void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     bdrv_dirty_bitmaps_lock(bs); | ||||
|     bdrv_release_dirty_bitmap_locked(bitmap); | ||||
|     bdrv_dirty_bitmaps_unlock(bs); | ||||
|     bdrv_do_release_matching_dirty_bitmap(bs, bitmap, NULL); | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -382,15 +357,19 @@ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) | ||||
|  */ | ||||
| void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) | ||||
| { | ||||
|     BdrvDirtyBitmap *bm, *next; | ||||
|     bdrv_do_release_matching_dirty_bitmap(bs, NULL, bdrv_dirty_bitmap_has_name); | ||||
| } | ||||
|  | ||||
|     bdrv_dirty_bitmaps_lock(bs); | ||||
|     QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { | ||||
|         if (bdrv_dirty_bitmap_name(bm)) { | ||||
|             bdrv_release_dirty_bitmap_locked(bm); | ||||
|         } | ||||
|     } | ||||
|     bdrv_dirty_bitmaps_unlock(bs); | ||||
| /** | ||||
|  * Release all persistent dirty bitmaps attached to a BDS (for use in | ||||
|  * bdrv_inactivate_recurse()). | ||||
|  * There must not be any frozen bitmaps attached. | ||||
|  * This function does not remove persistent bitmaps from the storage. | ||||
|  */ | ||||
| void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs) | ||||
| { | ||||
|     bdrv_do_release_matching_dirty_bitmap(bs, NULL, | ||||
|                                           bdrv_dirty_bitmap_get_persistance); | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -410,19 +389,18 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken.  */ | ||||
| void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     bdrv_dirty_bitmap_lock(bitmap); | ||||
|     assert(!bdrv_dirty_bitmap_frozen(bitmap)); | ||||
|     bitmap->disabled = true; | ||||
|     bdrv_dirty_bitmap_unlock(bitmap); | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken.  */ | ||||
| void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     bdrv_dirty_bitmap_lock(bitmap); | ||||
|     bdrv_enable_dirty_bitmap_locked(bitmap); | ||||
|     bdrv_dirty_bitmap_unlock(bitmap); | ||||
|     assert(!bdrv_dirty_bitmap_frozen(bitmap)); | ||||
|     bitmap->disabled = false; | ||||
| } | ||||
|  | ||||
| BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) | ||||
| @@ -554,6 +532,7 @@ void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap, | ||||
|  | ||||
| void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out) | ||||
| { | ||||
|     assert(bdrv_dirty_bitmap_enabled(bitmap)); | ||||
|     assert(!bdrv_dirty_bitmap_readonly(bitmap)); | ||||
|     bdrv_dirty_bitmap_lock(bitmap); | ||||
|     if (!out) { | ||||
| @@ -567,11 +546,12 @@ void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out) | ||||
|     bdrv_dirty_bitmap_unlock(bitmap); | ||||
| } | ||||
|  | ||||
| void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup) | ||||
| void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in) | ||||
| { | ||||
|     HBitmap *tmp = bitmap->bitmap; | ||||
|     assert(bdrv_dirty_bitmap_enabled(bitmap)); | ||||
|     assert(!bdrv_dirty_bitmap_readonly(bitmap)); | ||||
|     bitmap->bitmap = backup; | ||||
|     bitmap->bitmap = in; | ||||
|     hbitmap_free(tmp); | ||||
| } | ||||
|  | ||||
| @@ -689,24 +669,16 @@ void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent) | ||||
|     qemu_mutex_unlock(bitmap->mutex); | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken. */ | ||||
| void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration) | ||||
| { | ||||
|     qemu_mutex_lock(bitmap->mutex); | ||||
|     bitmap->migration = migration; | ||||
|     qemu_mutex_unlock(bitmap->mutex); | ||||
| } | ||||
|  | ||||
| bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     return bitmap->persistent && !bitmap->migration; | ||||
|     return bitmap->persistent; | ||||
| } | ||||
|  | ||||
| bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs) | ||||
| { | ||||
|     BdrvDirtyBitmap *bm; | ||||
|     QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { | ||||
|         if (bm->persistent && !bm->readonly && !bm->migration) { | ||||
|         if (bm->persistent && !bm->readonly) { | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| @@ -726,54 +698,7 @@ char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp) | ||||
|     return hbitmap_sha256(bitmap->bitmap, errp); | ||||
| } | ||||
|  | ||||
| int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset, | ||||
|                                     uint64_t bytes) | ||||
| int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset) | ||||
| { | ||||
|     return hbitmap_next_zero(bitmap->bitmap, offset, bytes); | ||||
| } | ||||
|  | ||||
| bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap, | ||||
|                                        uint64_t *offset, uint64_t *bytes) | ||||
| { | ||||
|     return hbitmap_next_dirty_area(bitmap->bitmap, offset, bytes); | ||||
| } | ||||
|  | ||||
| void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, | ||||
|                              HBitmap **backup, Error **errp) | ||||
| { | ||||
|     bool ret; | ||||
|  | ||||
|     /* only bitmaps from one bds are supported */ | ||||
|     assert(dest->mutex == src->mutex); | ||||
|  | ||||
|     qemu_mutex_lock(dest->mutex); | ||||
|  | ||||
|     if (bdrv_dirty_bitmap_user_locked(dest)) { | ||||
|         error_setg(errp, "Bitmap '%s' is currently in use by another" | ||||
|         " operation and cannot be modified", dest->name); | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     if (bdrv_dirty_bitmap_readonly(dest)) { | ||||
|         error_setg(errp, "Bitmap '%s' is readonly and cannot be modified", | ||||
|                    dest->name); | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     if (!hbitmap_can_merge(dest->bitmap, src->bitmap)) { | ||||
|         error_setg(errp, "Bitmaps are incompatible and can't be merged"); | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     if (backup) { | ||||
|         *backup = dest->bitmap; | ||||
|         dest->bitmap = hbitmap_alloc(dest->size, hbitmap_granularity(*backup)); | ||||
|         ret = hbitmap_merge(*backup, src->bitmap, dest->bitmap); | ||||
|     } else { | ||||
|         ret = hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap); | ||||
|     } | ||||
|     assert(ret); | ||||
|  | ||||
| out: | ||||
|     qemu_mutex_unlock(dest->mutex); | ||||
|     return hbitmap_next_zero(bitmap->bitmap, offset); | ||||
| } | ||||
|   | ||||
| @@ -1,49 +0,0 @@ | ||||
| /* | ||||
|  * DMG lzfse uncompression | ||||
|  * | ||||
|  * Copyright (c) 2018 Julio Cesar Faracco | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "dmg.h" | ||||
| #include <lzfse.h> | ||||
|  | ||||
| static int dmg_uncompress_lzfse_do(char *next_in, unsigned int avail_in, | ||||
|                                    char *next_out, unsigned int avail_out) | ||||
| { | ||||
|     size_t out_size = lzfse_decode_buffer((uint8_t *) next_out, avail_out, | ||||
|                                           (uint8_t *) next_in, avail_in, | ||||
|                                           NULL); | ||||
|  | ||||
|     /* We need to decode the single chunk only. */ | ||||
|     /* So, out_size == avail_out is not an error here. */ | ||||
|     if (out_size > 0) { | ||||
|         return out_size; | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| __attribute__((constructor)) | ||||
| static void dmg_lzfse_init(void) | ||||
| { | ||||
|     assert(!dmg_uncompress_lzfse); | ||||
|     dmg_uncompress_lzfse = dmg_uncompress_lzfse_do; | ||||
| } | ||||
							
								
								
									
										104
									
								
								block/dmg.c
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								block/dmg.c
									
									
									
									
									
								
							| @@ -33,9 +33,6 @@ | ||||
| int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in, | ||||
|                           char *next_out, unsigned int avail_out); | ||||
|  | ||||
| int (*dmg_uncompress_lzfse)(char *next_in, unsigned int avail_in, | ||||
|                             char *next_out, unsigned int avail_out); | ||||
|  | ||||
| enum { | ||||
|     /* Limit chunk sizes to prevent unreasonable amounts of memory being used | ||||
|      * or truncating when converting to 32-bit types | ||||
| @@ -44,19 +41,6 @@ enum { | ||||
|     DMG_SECTORCOUNTS_MAX = DMG_LENGTHS_MAX / 512, | ||||
| }; | ||||
|  | ||||
| enum { | ||||
|     /* DMG Block Type */ | ||||
|     UDZE = 0, /* Zeroes */ | ||||
|     UDRW,     /* RAW type */ | ||||
|     UDIG,     /* Ignore */ | ||||
|     UDCO = 0x80000004, | ||||
|     UDZO, | ||||
|     UDBZ, | ||||
|     ULFO, | ||||
|     UDCM = 0x7ffffffe, /* Comments */ | ||||
|     UDLE = 0xffffffff  /* Last Entry */ | ||||
| }; | ||||
|  | ||||
| static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||
| { | ||||
|     int len; | ||||
| @@ -121,17 +105,15 @@ static void update_max_chunk_size(BDRVDMGState *s, uint32_t chunk, | ||||
|     uint32_t uncompressed_sectors = 0; | ||||
|  | ||||
|     switch (s->types[chunk]) { | ||||
|     case UDZO: /* zlib compressed */ | ||||
|     case UDBZ: /* bzip2 compressed */ | ||||
|     case ULFO: /* lzfse compressed */ | ||||
|     case 0x80000005: /* zlib compressed */ | ||||
|     case 0x80000006: /* bzip2 compressed */ | ||||
|         compressed_size = s->lengths[chunk]; | ||||
|         uncompressed_sectors = s->sectorcounts[chunk]; | ||||
|         break; | ||||
|     case UDRW: /* copy */ | ||||
|     case 1: /* copy */ | ||||
|         uncompressed_sectors = DIV_ROUND_UP(s->lengths[chunk], 512); | ||||
|         break; | ||||
|     case UDZE: /* zero */ | ||||
|     case UDIG: /* ignore */ | ||||
|     case 2: /* zero */ | ||||
|         /* as the all-zeroes block may be large, it is treated specially: the | ||||
|          * sector is not copied from a large buffer, a simple memset is used | ||||
|          * instead. Therefore uncompressed_sectors does not need to be set. */ | ||||
| @@ -200,15 +182,12 @@ typedef struct DmgHeaderState { | ||||
| static bool dmg_is_known_block_type(uint32_t entry_type) | ||||
| { | ||||
|     switch (entry_type) { | ||||
|     case UDZE:    /* zeros */ | ||||
|     case UDRW:    /* uncompressed */ | ||||
|     case UDIG:    /* ignore */ | ||||
|     case UDZO:    /* zlib */ | ||||
|     case 0x00000001:    /* uncompressed */ | ||||
|     case 0x00000002:    /* zeroes */ | ||||
|     case 0x80000005:    /* zlib */ | ||||
|         return true; | ||||
|     case UDBZ:    /* bzip2 */ | ||||
|     case 0x80000006:    /* bzip2 */ | ||||
|         return !!dmg_uncompress_bz2; | ||||
|     case ULFO:    /* lzfse */ | ||||
|         return !!dmg_uncompress_lzfse; | ||||
|     default: | ||||
|         return false; | ||||
|     } | ||||
| @@ -267,10 +246,9 @@ static int dmg_read_mish_block(BDRVDMGState *s, DmgHeaderState *ds, | ||||
|         /* sector count */ | ||||
|         s->sectorcounts[i] = buff_read_uint64(buffer, offset + 0x10); | ||||
|  | ||||
|         /* all-zeroes sector (type UDZE and UDIG) does not need to be | ||||
|          * "uncompressed" and can therefore be unbounded. */ | ||||
|         if (s->types[i] != UDZE && s->types[i] != UDIG | ||||
|             && s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) { | ||||
|         /* all-zeroes sector (type 2) does not need to be "uncompressed" and can | ||||
|          * therefore be unbounded. */ | ||||
|         if (s->types[i] != 2 && s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) { | ||||
|             error_report("sector count %" PRIu64 " for chunk %" PRIu32 | ||||
|                          " is larger than max (%u)", | ||||
|                          s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX); | ||||
| @@ -435,19 +413,24 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     int64_t offset; | ||||
|     int ret; | ||||
|  | ||||
|     ret = bdrv_apply_auto_read_only(bs, NULL, errp); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||
|                                false, errp); | ||||
|     if (!bs->file) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     if (!bdrv_is_read_only(bs)) { | ||||
|         error_report("Opening dmg images without an explicit read-only=on " | ||||
|                      "option is deprecated. Future versions will refuse to " | ||||
|                      "open the image instead of automatically marking the " | ||||
|                      "image read-only."); | ||||
|         ret = bdrv_set_read_only(bs, true, errp); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     block_module_load_one("dmg-bz2"); | ||||
|     block_module_load_one("dmg-lzfse"); | ||||
|  | ||||
|     s->n_chunks = 0; | ||||
|     s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; | ||||
| @@ -575,20 +558,16 @@ static inline uint32_t search_chunk(BDRVDMGState *s, uint64_t sector_num) | ||||
| { | ||||
|     /* binary search */ | ||||
|     uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3; | ||||
|     while (chunk1 <= chunk2) { | ||||
|     while (chunk1 != chunk2) { | ||||
|         chunk3 = (chunk1 + chunk2) / 2; | ||||
|         if (s->sectors[chunk3] > sector_num) { | ||||
|             if (chunk3 == 0) { | ||||
|                 goto err; | ||||
|             } | ||||
|             chunk2 = chunk3 - 1; | ||||
|             chunk2 = chunk3; | ||||
|         } else if (s->sectors[chunk3] + s->sectorcounts[chunk3] > sector_num) { | ||||
|             return chunk3; | ||||
|         } else { | ||||
|             chunk1 = chunk3 + 1; | ||||
|             chunk1 = chunk3; | ||||
|         } | ||||
|     } | ||||
| err: | ||||
|     return s->n_chunks; /* error */ | ||||
| } | ||||
|  | ||||
| @@ -606,7 +585,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) | ||||
|  | ||||
|         s->current_chunk = s->n_chunks; | ||||
|         switch (s->types[chunk]) { /* block entry type */ | ||||
|         case UDZO: { /* zlib compressed */ | ||||
|         case 0x80000005: { /* zlib compressed */ | ||||
|             /* we need to buffer, because only the chunk as whole can be | ||||
|              * inflated. */ | ||||
|             ret = bdrv_pread(bs->file, s->offsets[chunk], | ||||
| @@ -629,7 +608,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) | ||||
|                 return -1; | ||||
|             } | ||||
|             break; } | ||||
|         case UDBZ: /* bzip2 compressed */ | ||||
|         case 0x80000006: /* bzip2 compressed */ | ||||
|             if (!dmg_uncompress_bz2) { | ||||
|                 break; | ||||
|             } | ||||
| @@ -650,36 +629,14 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) | ||||
|                 return ret; | ||||
|             } | ||||
|             break; | ||||
|         case ULFO: | ||||
|             if (!dmg_uncompress_lzfse) { | ||||
|                 break; | ||||
|             } | ||||
|             /* we need to buffer, because only the chunk as whole can be | ||||
|              * inflated. */ | ||||
|             ret = bdrv_pread(bs->file, s->offsets[chunk], | ||||
|                              s->compressed_chunk, s->lengths[chunk]); | ||||
|             if (ret != s->lengths[chunk]) { | ||||
|                 return -1; | ||||
|             } | ||||
|  | ||||
|             ret = dmg_uncompress_lzfse((char *)s->compressed_chunk, | ||||
|                                        (unsigned int) s->lengths[chunk], | ||||
|                                        (char *)s->uncompressed_chunk, | ||||
|                                        (unsigned int) | ||||
|                                            (512 * s->sectorcounts[chunk])); | ||||
|             if (ret < 0) { | ||||
|                 return ret; | ||||
|             } | ||||
|             break; | ||||
|         case UDRW: /* copy */ | ||||
|         case 1: /* copy */ | ||||
|             ret = bdrv_pread(bs->file, s->offsets[chunk], | ||||
|                              s->uncompressed_chunk, s->lengths[chunk]); | ||||
|             if (ret != s->lengths[chunk]) { | ||||
|                 return -1; | ||||
|             } | ||||
|             break; | ||||
|         case UDZE: /* zeros */ | ||||
|         case UDIG: /* ignore */ | ||||
|         case 2: /* zero */ | ||||
|             /* see dmg_read, it is treated specially. No buffer needs to be | ||||
|              * pre-filled, the zeroes can be set directly. */ | ||||
|             break; | ||||
| @@ -714,8 +671,7 @@ dmg_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, | ||||
|         /* Special case: current chunk is all zeroes. Do not perform a memcpy as | ||||
|          * s->uncompressed_chunk may be too small to cover the large all-zeroes | ||||
|          * section. dmg_read_chunk is called to find s->current_chunk */ | ||||
|         if (s->types[s->current_chunk] == UDZE | ||||
|             || s->types[s->current_chunk] == UDIG) { /* all zeroes block entry */ | ||||
|         if (s->types[s->current_chunk] == 2) { /* all zeroes block entry */ | ||||
|             qemu_iovec_memset(qiov, i * 512, 0, 512); | ||||
|             continue; | ||||
|         } | ||||
|   | ||||
| @@ -55,7 +55,4 @@ typedef struct BDRVDMGState { | ||||
| extern int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in, | ||||
|                                  char *next_out, unsigned int avail_out); | ||||
|  | ||||
| extern int (*dmg_uncompress_lzfse)(char *next_in, unsigned int avail_in, | ||||
|                                    char *next_out, unsigned int avail_out); | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										1230
									
								
								block/file-posix.c
									
									
									
									
									
								
							
							
						
						
									
										1230
									
								
								block/file-posix.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -162,7 +162,7 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile, | ||||
|     acb->aio_nbytes = count; | ||||
|     acb->aio_offset = offset; | ||||
|  | ||||
|     trace_file_paio_submit(acb, opaque, offset, count, type); | ||||
|     trace_paio_submit(acb, opaque, offset, count, type); | ||||
|     pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); | ||||
|     return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); | ||||
| } | ||||
| @@ -176,7 +176,7 @@ int qemu_ftruncate64(int fd, int64_t length) | ||||
|     BOOL res; | ||||
|  | ||||
|     if ((GetVersion() & 0x80000000UL) && (length >> 32) != 0) | ||||
|         return -1; | ||||
| 	return -1; | ||||
|  | ||||
|     h = (HANDLE)_get_osfhandle(fd); | ||||
|  | ||||
| @@ -184,13 +184,13 @@ int qemu_ftruncate64(int fd, int64_t length) | ||||
|     li.HighPart = 0; | ||||
|     li.LowPart = SetFilePointer (h, 0, &li.HighPart, FILE_CURRENT); | ||||
|     if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { | ||||
|         return -1; | ||||
| 	return -1; | ||||
|     } | ||||
|  | ||||
|     high = length >> 32; | ||||
|     dw = SetFilePointer(h, (DWORD) length, &high, FILE_BEGIN); | ||||
|     if (dw == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { | ||||
|         return -1; | ||||
| 	return -1; | ||||
|     } | ||||
|     res = SetEndOfFile(h); | ||||
|  | ||||
| @@ -203,7 +203,7 @@ static int set_sparse(int fd) | ||||
| { | ||||
|     DWORD returned; | ||||
|     return (int) DeviceIoControl((HANDLE)_get_osfhandle(fd), FSCTL_SET_SPARSE, | ||||
|                                  NULL, 0, NULL, 0, &returned, NULL); | ||||
| 				 NULL, 0, NULL, 0, &returned, NULL); | ||||
| } | ||||
|  | ||||
| static void raw_detach_aio_context(BlockDriverState *bs) | ||||
| @@ -251,11 +251,7 @@ static void raw_probe_alignment(BlockDriverState *bs, Error **errp) | ||||
|                          &dg.Geometry.BytesPerSector, | ||||
|                          &freeClusters, &totalClusters); | ||||
|         bs->bl.request_alignment = dg.Geometry.BytesPerSector; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* XXX Does Windows support AIO on less than 512-byte alignment? */ | ||||
|     bs->bl.request_alignment = 512; | ||||
| } | ||||
|  | ||||
| static void raw_parse_flags(int flags, bool use_aio, int *access_flags, | ||||
| @@ -414,32 +410,32 @@ fail: | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static BlockAIOCB *raw_aio_preadv(BlockDriverState *bs, | ||||
|                                   uint64_t offset, uint64_t bytes, | ||||
|                                   QEMUIOVector *qiov, int flags, | ||||
|                                   BlockCompletionFunc *cb, void *opaque) | ||||
| static BlockAIOCB *raw_aio_readv(BlockDriverState *bs, | ||||
|                          int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | ||||
|                          BlockCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     if (s->aio) { | ||||
|         return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov, | ||||
|                                 cb, opaque, QEMU_AIO_READ); | ||||
|         return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov, | ||||
|                                 nb_sectors, cb, opaque, QEMU_AIO_READ); | ||||
|     } else { | ||||
|         return paio_submit(bs, s->hfile, offset, qiov, bytes, | ||||
|         return paio_submit(bs, s->hfile, sector_num << BDRV_SECTOR_BITS, qiov, | ||||
|                            nb_sectors << BDRV_SECTOR_BITS, | ||||
|                            cb, opaque, QEMU_AIO_READ); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static BlockAIOCB *raw_aio_pwritev(BlockDriverState *bs, | ||||
|                                    uint64_t offset, uint64_t bytes, | ||||
|                                    QEMUIOVector *qiov, int flags, | ||||
|                                    BlockCompletionFunc *cb, void *opaque) | ||||
| static BlockAIOCB *raw_aio_writev(BlockDriverState *bs, | ||||
|                           int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | ||||
|                           BlockCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     if (s->aio) { | ||||
|         return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov, | ||||
|                                 cb, opaque, QEMU_AIO_WRITE); | ||||
|         return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov, | ||||
|                                 nb_sectors, cb, opaque, QEMU_AIO_WRITE); | ||||
|     } else { | ||||
|         return paio_submit(bs, s->hfile, offset, qiov, bytes, | ||||
|         return paio_submit(bs, s->hfile, sector_num << BDRV_SECTOR_BITS, qiov, | ||||
|                            nb_sectors << BDRV_SECTOR_BITS, | ||||
|                            cb, opaque, QEMU_AIO_WRITE); | ||||
|     } | ||||
| } | ||||
| @@ -467,8 +463,8 @@ static void raw_close(BlockDriverState *bs) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                                         PreallocMode prealloc, Error **errp) | ||||
| static int raw_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                         PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     LONG low, high; | ||||
| @@ -636,11 +632,11 @@ BlockDriver bdrv_file = { | ||||
|     .bdrv_co_create_opts = raw_co_create_opts, | ||||
|     .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||||
|  | ||||
|     .bdrv_aio_preadv    = raw_aio_preadv, | ||||
|     .bdrv_aio_pwritev   = raw_aio_pwritev, | ||||
|     .bdrv_aio_readv     = raw_aio_readv, | ||||
|     .bdrv_aio_writev    = raw_aio_writev, | ||||
|     .bdrv_aio_flush     = raw_aio_flush, | ||||
|  | ||||
|     .bdrv_co_truncate   = raw_co_truncate, | ||||
|     .bdrv_truncate	= raw_truncate, | ||||
|     .bdrv_getlength	= raw_getlength, | ||||
|     .bdrv_get_allocated_file_size | ||||
|                         = raw_get_allocated_file_size, | ||||
| @@ -712,12 +708,6 @@ static void hdev_parse_filename(const char *filename, QDict *options, | ||||
|     bdrv_parse_filename_strip_prefix(filename, "host_device:", options); | ||||
| } | ||||
|  | ||||
| static void hdev_refresh_limits(BlockDriverState *bs, Error **errp) | ||||
| { | ||||
|     /* XXX Does Windows support AIO on less than 512-byte alignment? */ | ||||
|     bs->bl.request_alignment = 512; | ||||
| } | ||||
|  | ||||
| static int hdev_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                      Error **errp) | ||||
| { | ||||
| @@ -803,10 +793,9 @@ static BlockDriver bdrv_host_device = { | ||||
|     .bdrv_probe_device	= hdev_probe_device, | ||||
|     .bdrv_file_open	= hdev_open, | ||||
|     .bdrv_close		= raw_close, | ||||
|     .bdrv_refresh_limits = hdev_refresh_limits, | ||||
|  | ||||
|     .bdrv_aio_preadv    = raw_aio_preadv, | ||||
|     .bdrv_aio_pwritev   = raw_aio_pwritev, | ||||
|     .bdrv_aio_readv     = raw_aio_readv, | ||||
|     .bdrv_aio_writev    = raw_aio_writev, | ||||
|     .bdrv_aio_flush     = raw_aio_flush, | ||||
|  | ||||
|     .bdrv_detach_aio_context = raw_detach_aio_context, | ||||
|   | ||||
| @@ -11,7 +11,6 @@ | ||||
| #include "qemu/osdep.h" | ||||
| #include <glusterfs/api/glfs.h> | ||||
| #include "block/block_int.h" | ||||
| #include "block/qdict.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qapi/qmp/qdict.h" | ||||
| #include "qapi/qmp/qerror.h" | ||||
| @@ -72,7 +71,7 @@ typedef struct ListElement { | ||||
|     GlfsPreopened saved; | ||||
| } ListElement; | ||||
|  | ||||
| static QLIST_HEAD(, ListElement) glfs_list; | ||||
| static QLIST_HEAD(glfs_list, ListElement) glfs_list; | ||||
|  | ||||
| static QemuOptsList qemu_gluster_create_opts = { | ||||
|     .name = "qemu-gluster-create-opts", | ||||
| @@ -168,12 +167,7 @@ static QemuOptsList runtime_unix_opts = { | ||||
|         { | ||||
|             .name = GLUSTER_OPT_SOCKET, | ||||
|             .type = QEMU_OPT_STRING, | ||||
|             .help = "socket file path (legacy)", | ||||
|         }, | ||||
|         { | ||||
|             .name = GLUSTER_OPT_PATH, | ||||
|             .type = QEMU_OPT_STRING, | ||||
|             .help = "socket file path (QAPI)", | ||||
|             .help = "socket file path)", | ||||
|         }, | ||||
|         { /* end of list */ } | ||||
|     }, | ||||
| @@ -621,18 +615,10 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, | ||||
|                 goto out; | ||||
|             } | ||||
|  | ||||
|             ptr = qemu_opt_get(opts, GLUSTER_OPT_PATH); | ||||
|             if (!ptr) { | ||||
|                 ptr = qemu_opt_get(opts, GLUSTER_OPT_SOCKET); | ||||
|             } else if (qemu_opt_get(opts, GLUSTER_OPT_SOCKET)) { | ||||
|                 error_setg(&local_err, | ||||
|                            "Conflicting parameters 'path' and 'socket'"); | ||||
|                 error_append_hint(&local_err, GERR_INDEX_HINT, i); | ||||
|                 goto out; | ||||
|             } | ||||
|             ptr = qemu_opt_get(opts, GLUSTER_OPT_SOCKET); | ||||
|             if (!ptr) { | ||||
|                 error_setg(&local_err, QERR_MISSING_PARAMETER, | ||||
|                            GLUSTER_OPT_PATH); | ||||
|                            GLUSTER_OPT_SOCKET); | ||||
|                 error_append_hint(&local_err, GERR_INDEX_HINT, i); | ||||
|                 goto out; | ||||
|             } | ||||
| @@ -651,7 +637,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, | ||||
|         } | ||||
|         gsconf = NULL; | ||||
|  | ||||
|         qobject_unref(backing_options); | ||||
|         QDECREF(backing_options); | ||||
|         backing_options = NULL; | ||||
|         g_free(str); | ||||
|         str = NULL; | ||||
| @@ -664,7 +650,7 @@ out: | ||||
|     qapi_free_SocketAddress(gsconf); | ||||
|     qemu_opts_del(opts); | ||||
|     g_free(str); | ||||
|     qobject_unref(backing_options); | ||||
|     QDECREF(backing_options); | ||||
|     errno = EINVAL; | ||||
|     return -errno; | ||||
| } | ||||
| @@ -679,7 +665,7 @@ static int qemu_gluster_parse(BlockdevOptionsGluster *gconf, | ||||
|     if (filename) { | ||||
|         ret = qemu_gluster_parse_uri(gconf, filename); | ||||
|         if (ret < 0) { | ||||
|             error_setg(errp, "invalid URI %s", filename); | ||||
|             error_setg(errp, "invalid URI"); | ||||
|             error_append_hint(errp, "Usage: file=gluster[+transport]://" | ||||
|                                     "[host[:port]]volume/path[?socket=...]" | ||||
|                                     "[,file.debug=N]" | ||||
| @@ -698,7 +684,7 @@ static int qemu_gluster_parse(BlockdevOptionsGluster *gconf, | ||||
|                              "file.server.0.host=1.2.3.4," | ||||
|                              "file.server.0.port=24007," | ||||
|                              "file.server.1.transport=unix," | ||||
|                              "file.server.1.path=/var/run/glusterd.socket ..." | ||||
|                              "file.server.1.socket=/var/run/glusterd.socket ..." | ||||
|                              "\n"); | ||||
|             return ret; | ||||
|         } | ||||
| @@ -849,16 +835,8 @@ static int qemu_gluster_open(BlockDriverState *bs,  QDict *options, | ||||
|     qemu_gluster_parse_flags(bdrv_flags, &open_flags); | ||||
|  | ||||
|     s->fd = glfs_open(s->glfs, gconf->path, open_flags); | ||||
|     ret = s->fd ? 0 : -errno; | ||||
|  | ||||
|     if (ret == -EACCES || ret == -EROFS) { | ||||
|         /* Try to degrade to read-only, but if it doesn't work, still use the | ||||
|          * normal error message. */ | ||||
|         if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) { | ||||
|             open_flags = (open_flags & ~O_RDWR) | O_RDONLY; | ||||
|             s->fd = glfs_open(s->glfs, gconf->path, open_flags); | ||||
|             ret = s->fd ? 0 : -errno; | ||||
|         } | ||||
|     if (!s->fd) { | ||||
|         ret = -errno; | ||||
|     } | ||||
|  | ||||
|     s->supports_seek_data = qemu_gluster_test_seek(s->fd); | ||||
| @@ -1185,10 +1163,8 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs, | ||||
|     return acb.ret; | ||||
| } | ||||
|  | ||||
| static coroutine_fn int qemu_gluster_co_truncate(BlockDriverState *bs, | ||||
|                                                  int64_t offset, | ||||
|                                                  PreallocMode prealloc, | ||||
|                                                  Error **errp) | ||||
| static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                                  PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     BDRVGlusterState *s = bs->opaque; | ||||
|     return qemu_gluster_do_truncate(s->fd, offset, prealloc, errp); | ||||
| @@ -1205,10 +1181,8 @@ static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs, | ||||
| static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs, | ||||
|                                                int64_t sector_num, | ||||
|                                                int nb_sectors, | ||||
|                                                QEMUIOVector *qiov, | ||||
|                                                int flags) | ||||
|                                                QEMUIOVector *qiov) | ||||
| { | ||||
|     assert(!flags); | ||||
|     return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1); | ||||
| } | ||||
|  | ||||
| @@ -1334,7 +1308,7 @@ static int qemu_gluster_has_zero_init(BlockDriverState *bs) | ||||
|  * If @start is in a trailing hole or beyond EOF, return -ENXIO. | ||||
|  * If we can't find out, return a negative errno other than -ENXIO. | ||||
|  * | ||||
|  * (Shamefully copied from file-posix.c, only minuscule adaptions.) | ||||
|  * (Shamefully copied from file-posix.c, only miniscule adaptions.) | ||||
|  */ | ||||
| static int find_allocation(BlockDriverState *bs, off_t start, | ||||
|                            off_t *data, off_t *hole) | ||||
| @@ -1509,7 +1483,7 @@ static BlockDriver bdrv_gluster = { | ||||
|     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, | ||||
|     .bdrv_getlength               = qemu_gluster_getlength, | ||||
|     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, | ||||
|     .bdrv_co_truncate             = qemu_gluster_co_truncate, | ||||
|     .bdrv_truncate                = qemu_gluster_truncate, | ||||
|     .bdrv_co_readv                = qemu_gluster_co_readv, | ||||
|     .bdrv_co_writev               = qemu_gluster_co_writev, | ||||
|     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, | ||||
| @@ -1538,7 +1512,7 @@ static BlockDriver bdrv_gluster_tcp = { | ||||
|     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, | ||||
|     .bdrv_getlength               = qemu_gluster_getlength, | ||||
|     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, | ||||
|     .bdrv_co_truncate             = qemu_gluster_co_truncate, | ||||
|     .bdrv_truncate                = qemu_gluster_truncate, | ||||
|     .bdrv_co_readv                = qemu_gluster_co_readv, | ||||
|     .bdrv_co_writev               = qemu_gluster_co_writev, | ||||
|     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, | ||||
| @@ -1567,7 +1541,7 @@ static BlockDriver bdrv_gluster_unix = { | ||||
|     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, | ||||
|     .bdrv_getlength               = qemu_gluster_getlength, | ||||
|     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, | ||||
|     .bdrv_co_truncate             = qemu_gluster_co_truncate, | ||||
|     .bdrv_truncate                = qemu_gluster_truncate, | ||||
|     .bdrv_co_readv                = qemu_gluster_co_readv, | ||||
|     .bdrv_co_writev               = qemu_gluster_co_writev, | ||||
|     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, | ||||
| @@ -1602,7 +1576,7 @@ static BlockDriver bdrv_gluster_rdma = { | ||||
|     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, | ||||
|     .bdrv_getlength               = qemu_gluster_getlength, | ||||
|     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, | ||||
|     .bdrv_co_truncate             = qemu_gluster_co_truncate, | ||||
|     .bdrv_truncate                = qemu_gluster_truncate, | ||||
|     .bdrv_co_readv                = qemu_gluster_co_readv, | ||||
|     .bdrv_co_writev               = qemu_gluster_co_writev, | ||||
|     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, | ||||
|   | ||||
							
								
								
									
										901
									
								
								block/io.c
									
									
									
									
									
								
							
							
						
						
									
										901
									
								
								block/io.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										425
									
								
								block/iscsi.c
									
									
									
									
									
								
							
							
						
						
									
										425
									
								
								block/iscsi.c
									
									
									
									
									
								
							| @@ -33,7 +33,6 @@ | ||||
| #include "qemu/bitops.h" | ||||
| #include "qemu/bitmap.h" | ||||
| #include "block/block_int.h" | ||||
| #include "block/qdict.h" | ||||
| #include "scsi/constants.h" | ||||
| #include "qemu/iov.h" | ||||
| #include "qemu/option.h" | ||||
| @@ -44,14 +43,11 @@ | ||||
| #include "qapi/qmp/qstring.h" | ||||
| #include "crypto/secret.h" | ||||
| #include "scsi/utils.h" | ||||
| #include "trace.h" | ||||
|  | ||||
| /* Conflict between scsi/utils.h and libiscsi! :( */ | ||||
| #define SCSI_XFER_NONE ISCSI_XFER_NONE | ||||
| #include <iscsi/iscsi.h> | ||||
| #define inline __attribute__((gnu_inline))  /* required for libiscsi v1.9.0 */ | ||||
| #include <iscsi/scsi-lowlevel.h> | ||||
| #undef inline | ||||
| #undef SCSI_XFER_NONE | ||||
| QEMU_BUILD_BUG_ON((int)SCSI_XFER_NONE != (int)ISCSI_XFER_NONE); | ||||
|  | ||||
| @@ -72,7 +68,6 @@ typedef struct IscsiLun { | ||||
|     QemuMutex mutex; | ||||
|     struct scsi_inquiry_logical_block_provisioning lbp; | ||||
|     struct scsi_inquiry_block_limits bl; | ||||
|     struct scsi_inquiry_device_designator *dd; | ||||
|     unsigned char *zeroblock; | ||||
|     /* The allocmap tracks which clusters (pages) on the iSCSI target are | ||||
|      * allocated and which are not. In case a target returns zeros for | ||||
| @@ -119,6 +114,7 @@ typedef struct IscsiAIOCB { | ||||
|     QEMUBH *bh; | ||||
|     IscsiLun *iscsilun; | ||||
|     struct scsi_task *task; | ||||
|     uint8_t *buf; | ||||
|     int status; | ||||
|     int64_t sector_num; | ||||
|     int nb_sectors; | ||||
| @@ -126,7 +122,6 @@ typedef struct IscsiAIOCB { | ||||
| #ifdef __linux__ | ||||
|     sg_io_hdr_t *ioh; | ||||
| #endif | ||||
|     bool cancelled; | ||||
| } IscsiAIOCB; | ||||
|  | ||||
| /* libiscsi uses time_t so its enough to process events every second */ | ||||
| @@ -152,6 +147,9 @@ iscsi_bh_cb(void *p) | ||||
|  | ||||
|     qemu_bh_delete(acb->bh); | ||||
|  | ||||
|     g_free(acb->buf); | ||||
|     acb->buf = NULL; | ||||
|  | ||||
|     acb->common.cb(acb->common.opaque, acb->status); | ||||
|  | ||||
|     if (acb->task != NULL) { | ||||
| @@ -290,20 +288,14 @@ static void iscsi_co_init_iscsitask(IscsiLun *iscsilun, struct IscsiTask *iTask) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| /* Called (via iscsi_service) with QemuMutex held. */ | ||||
| static void | ||||
| iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data, | ||||
|                     void *private_data) | ||||
| { | ||||
|     IscsiAIOCB *acb = private_data; | ||||
|  | ||||
|     /* If the command callback hasn't been called yet, drop the task */ | ||||
|     if (!acb->bh) { | ||||
|         /* Call iscsi_aio_ioctl_cb() with SCSI_STATUS_CANCELLED */ | ||||
|         iscsi_scsi_cancel_task(iscsi, acb->task); | ||||
|     } | ||||
|  | ||||
|     qemu_aio_unref(acb); /* acquired in iscsi_aio_cancel() */ | ||||
|     acb->status = -ECANCELED; | ||||
|     iscsi_schedule_bh(acb); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -312,25 +304,14 @@ iscsi_aio_cancel(BlockAIOCB *blockacb) | ||||
|     IscsiAIOCB *acb = (IscsiAIOCB *)blockacb; | ||||
|     IscsiLun *iscsilun = acb->iscsilun; | ||||
|  | ||||
|     qemu_mutex_lock(&iscsilun->mutex); | ||||
|  | ||||
|     /* If it was cancelled or completed already, our work is done here */ | ||||
|     if (acb->cancelled || acb->status != -EINPROGRESS) { | ||||
|         qemu_mutex_unlock(&iscsilun->mutex); | ||||
|     if (acb->status != -EINPROGRESS) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     acb->cancelled = true; | ||||
|  | ||||
|     qemu_aio_ref(acb); /* released in iscsi_abort_task_cb() */ | ||||
|  | ||||
|     /* send a task mgmt call to the target to cancel the task on the target */ | ||||
|     if (iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task, | ||||
|                                          iscsi_abort_task_cb, acb) < 0) { | ||||
|         qemu_aio_unref(acb); /* since iscsi_abort_task_cb() won't be called */ | ||||
|     } | ||||
|     iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task, | ||||
|                                      iscsi_abort_task_cb, acb); | ||||
|  | ||||
|     qemu_mutex_unlock(&iscsilun->mutex); | ||||
| } | ||||
|  | ||||
| static const AIOCBInfo iscsi_aiocb_info = { | ||||
| @@ -364,8 +345,6 @@ static void iscsi_timed_check_events(void *opaque) | ||||
| { | ||||
|     IscsiLun *iscsilun = opaque; | ||||
|  | ||||
|     qemu_mutex_lock(&iscsilun->mutex); | ||||
|  | ||||
|     /* check for timed out requests */ | ||||
|     iscsi_service(iscsilun->iscsi, 0); | ||||
|  | ||||
| @@ -378,8 +357,6 @@ static void iscsi_timed_check_events(void *opaque) | ||||
|      * to return to service once this situation changes. */ | ||||
|     iscsi_set_events(iscsilun); | ||||
|  | ||||
|     qemu_mutex_unlock(&iscsilun->mutex); | ||||
|  | ||||
|     timer_mod(iscsilun->event_timer, | ||||
|               qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL); | ||||
| } | ||||
| @@ -578,20 +555,9 @@ static inline bool iscsi_allocmap_is_valid(IscsiLun *iscsilun, | ||||
|                                offset / iscsilun->cluster_size) == size); | ||||
| } | ||||
|  | ||||
| static void coroutine_fn iscsi_co_wait_for_task(IscsiTask *iTask, | ||||
|                                                 IscsiLun *iscsilun) | ||||
| { | ||||
|     while (!iTask->complete) { | ||||
|         iscsi_set_events(iscsilun); | ||||
|         qemu_mutex_unlock(&iscsilun->mutex); | ||||
|         qemu_coroutine_yield(); | ||||
|         qemu_mutex_lock(&iscsilun->mutex); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int coroutine_fn | ||||
| iscsi_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, | ||||
|                 QEMUIOVector *iov, int flags) | ||||
| iscsi_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors, | ||||
|                       QEMUIOVector *iov, int flags) | ||||
| { | ||||
|     IscsiLun *iscsilun = bs->opaque; | ||||
|     struct IscsiTask iTask; | ||||
| @@ -650,7 +616,12 @@ retry: | ||||
|     scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov, | ||||
|                           iov->niov); | ||||
| #endif | ||||
|     iscsi_co_wait_for_task(&iTask, iscsilun); | ||||
|     while (!iTask.complete) { | ||||
|         iscsi_set_events(iscsilun); | ||||
|         qemu_mutex_unlock(&iscsilun->mutex); | ||||
|         qemu_coroutine_yield(); | ||||
|         qemu_mutex_lock(&iscsilun->mutex); | ||||
|     } | ||||
|  | ||||
|     if (iTask.task != NULL) { | ||||
|         scsi_free_scsi_task(iTask.task); | ||||
| @@ -721,7 +692,13 @@ retry: | ||||
|         ret = -ENOMEM; | ||||
|         goto out_unlock; | ||||
|     } | ||||
|     iscsi_co_wait_for_task(&iTask, iscsilun); | ||||
|  | ||||
|     while (!iTask.complete) { | ||||
|         iscsi_set_events(iscsilun); | ||||
|         qemu_mutex_unlock(&iscsilun->mutex); | ||||
|         qemu_coroutine_yield(); | ||||
|         qemu_mutex_lock(&iscsilun->mutex); | ||||
|     } | ||||
|  | ||||
|     if (iTask.do_retry) { | ||||
|         if (iTask.task != NULL) { | ||||
| @@ -755,7 +732,7 @@ retry: | ||||
|         goto out_unlock; | ||||
|     } | ||||
|  | ||||
|     *pnum = (int64_t) lbasd->num_blocks * iscsilun->block_size; | ||||
|     *pnum = lbasd->num_blocks * iscsilun->block_size; | ||||
|  | ||||
|     if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED || | ||||
|         lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) { | ||||
| @@ -885,8 +862,13 @@ retry: | ||||
| #if LIBISCSI_API_VERSION < (20160603) | ||||
|     scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov); | ||||
| #endif | ||||
|     while (!iTask.complete) { | ||||
|         iscsi_set_events(iscsilun); | ||||
|         qemu_mutex_unlock(&iscsilun->mutex); | ||||
|         qemu_coroutine_yield(); | ||||
|         qemu_mutex_lock(&iscsilun->mutex); | ||||
|     } | ||||
|  | ||||
|     iscsi_co_wait_for_task(&iTask, iscsilun); | ||||
|     if (iTask.task != NULL) { | ||||
|         scsi_free_scsi_task(iTask.task); | ||||
|         iTask.task = NULL; | ||||
| @@ -923,7 +905,12 @@ retry: | ||||
|         return -ENOMEM; | ||||
|     } | ||||
|  | ||||
|     iscsi_co_wait_for_task(&iTask, iscsilun); | ||||
|     while (!iTask.complete) { | ||||
|         iscsi_set_events(iscsilun); | ||||
|         qemu_mutex_unlock(&iscsilun->mutex); | ||||
|         qemu_coroutine_yield(); | ||||
|         qemu_mutex_lock(&iscsilun->mutex); | ||||
|     } | ||||
|  | ||||
|     if (iTask.task != NULL) { | ||||
|         scsi_free_scsi_task(iTask.task); | ||||
| @@ -953,13 +940,8 @@ iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status, | ||||
| { | ||||
|     IscsiAIOCB *acb = opaque; | ||||
|  | ||||
|     if (status == SCSI_STATUS_CANCELLED) { | ||||
|         if (!acb->bh) { | ||||
|             acb->status = -ECANCELED; | ||||
|             iscsi_schedule_bh(acb); | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
|     g_free(acb->buf); | ||||
|     acb->buf = NULL; | ||||
|  | ||||
|     acb->status = 0; | ||||
|     if (status < 0) { | ||||
| @@ -1035,8 +1017,8 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, | ||||
|     acb->iscsilun = iscsilun; | ||||
|     acb->bh          = NULL; | ||||
|     acb->status      = -EINPROGRESS; | ||||
|     acb->buf         = NULL; | ||||
|     acb->ioh         = buf; | ||||
|     acb->cancelled   = false; | ||||
|  | ||||
|     if (req != SG_IO) { | ||||
|         iscsi_ioctl_handle_emulated(acb, req, buf); | ||||
| @@ -1160,7 +1142,12 @@ retry: | ||||
|         goto out_unlock; | ||||
|     } | ||||
|  | ||||
|     iscsi_co_wait_for_task(&iTask, iscsilun); | ||||
|     while (!iTask.complete) { | ||||
|         iscsi_set_events(iscsilun); | ||||
|         qemu_mutex_unlock(&iscsilun->mutex); | ||||
|         qemu_coroutine_yield(); | ||||
|         qemu_mutex_lock(&iscsilun->mutex); | ||||
|     } | ||||
|  | ||||
|     if (iTask.task != NULL) { | ||||
|         scsi_free_scsi_task(iTask.task); | ||||
| @@ -1256,7 +1243,12 @@ retry: | ||||
|         return -ENOMEM; | ||||
|     } | ||||
|  | ||||
|     iscsi_co_wait_for_task(&iTask, iscsilun); | ||||
|     while (!iTask.complete) { | ||||
|         iscsi_set_events(iscsilun); | ||||
|         qemu_mutex_unlock(&iscsilun->mutex); | ||||
|         qemu_coroutine_yield(); | ||||
|         qemu_mutex_lock(&iscsilun->mutex); | ||||
|     } | ||||
|  | ||||
|     if (iTask.status == SCSI_STATUS_CHECK_CONDITION && | ||||
|         iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && | ||||
| @@ -1740,34 +1732,14 @@ static QemuOptsList runtime_opts = { | ||||
|             .name = "timeout", | ||||
|             .type = QEMU_OPT_NUMBER, | ||||
|         }, | ||||
|         { | ||||
|             .name = "filename", | ||||
|             .type = QEMU_OPT_STRING, | ||||
|         }, | ||||
|         { /* end of list */ } | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| static void iscsi_save_designator(IscsiLun *lun, | ||||
|                                   struct scsi_inquiry_device_identification *inq_di) | ||||
| { | ||||
|     struct scsi_inquiry_device_designator *desig, *copy = NULL; | ||||
|  | ||||
|     for (desig = inq_di->designators; desig; desig = desig->next) { | ||||
|         if (desig->association || | ||||
|             desig->designator_type > SCSI_DESIGNATOR_TYPE_NAA) { | ||||
|             continue; | ||||
|         } | ||||
|         /* NAA works better than T10 vendor ID based designator. */ | ||||
|         if (!copy || copy->designator_type < desig->designator_type) { | ||||
|             copy = desig; | ||||
|         } | ||||
|     } | ||||
|     if (copy) { | ||||
|         lun->dd = g_new(struct scsi_inquiry_device_designator, 1); | ||||
|         *lun->dd = *copy; | ||||
|         lun->dd->next = NULL; | ||||
|         lun->dd->designator = g_malloc(copy->designator_length); | ||||
|         memcpy(lun->dd->designator, copy->designator, copy->designator_length); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                       Error **errp) | ||||
| { | ||||
| @@ -1779,12 +1751,27 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     char *initiator_name = NULL; | ||||
|     QemuOpts *opts; | ||||
|     Error *local_err = NULL; | ||||
|     const char *transport_name, *portal, *target; | ||||
|     const char *transport_name, *portal, *target, *filename; | ||||
| #if LIBISCSI_API_VERSION >= (20160603) | ||||
|     enum iscsi_transport_type transport; | ||||
| #endif | ||||
|     int i, ret = 0, timeout = 0, lun; | ||||
|  | ||||
|     /* If we are given a filename, parse the filename, with precedence given to | ||||
|      * filename encoded options */ | ||||
|     filename = qdict_get_try_str(options, "filename"); | ||||
|     if (filename) { | ||||
|         warn_report("'filename' option specified. " | ||||
|                     "This is an unsupported option, and may be deprecated " | ||||
|                     "in the future"); | ||||
|         iscsi_parse_filename(filename, options, &local_err); | ||||
|         if (local_err) { | ||||
|             ret = -EINVAL; | ||||
|             error_propagate(errp, local_err); | ||||
|             goto exit; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); | ||||
|     qemu_opts_absorb_qdict(opts, options, &local_err); | ||||
|     if (local_err) { | ||||
| @@ -1869,7 +1856,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     iscsi_set_timeout(iscsi, timeout); | ||||
| #else | ||||
|     if (timeout) { | ||||
|         warn_report("iSCSI: ignoring timeout value for libiscsi <1.15.0"); | ||||
|         error_report("iSCSI: ignoring timeout value for libiscsi <1.15.0"); | ||||
|     } | ||||
| #endif | ||||
|  | ||||
| @@ -1903,11 +1890,9 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     /* 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) { | ||||
|         ret = bdrv_apply_auto_read_only(bs, "LUN is write protected", errp); | ||||
|         if (ret < 0) { | ||||
|             goto out; | ||||
|         } | ||||
|         flags &= ~BDRV_O_RDWR; | ||||
|         error_setg(errp, "Cannot open a write protected LUN as read-write"); | ||||
|         ret = -EACCES; | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     iscsi_readcapacity_sync(iscsilun, &local_err); | ||||
| @@ -1937,7 +1922,6 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         struct scsi_task *inq_task; | ||||
|         struct scsi_inquiry_logical_block_provisioning *inq_lbp; | ||||
|         struct scsi_inquiry_block_limits *inq_bl; | ||||
|         struct scsi_inquiry_device_identification *inq_di; | ||||
|         switch (inq_vpd->pages[i]) { | ||||
|         case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING: | ||||
|             inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, | ||||
| @@ -1963,17 +1947,6 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                    sizeof(struct scsi_inquiry_block_limits)); | ||||
|             scsi_free_scsi_task(inq_task); | ||||
|             break; | ||||
|         case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION: | ||||
|             inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, | ||||
|                                     SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION, | ||||
|                                     (void **) &inq_di, errp); | ||||
|             if (inq_task == NULL) { | ||||
|                 ret = -EINVAL; | ||||
|                 goto out; | ||||
|             } | ||||
|             iscsi_save_designator(iscsilun, inq_di); | ||||
|             scsi_free_scsi_task(inq_task); | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
| @@ -2016,7 +1989,7 @@ out: | ||||
|         } | ||||
|         memset(iscsilun, 0, sizeof(IscsiLun)); | ||||
|     } | ||||
|  | ||||
| exit: | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| @@ -2030,10 +2003,6 @@ static void iscsi_close(BlockDriverState *bs) | ||||
|         iscsi_logout_sync(iscsi); | ||||
|     } | ||||
|     iscsi_destroy_context(iscsi); | ||||
|     if (iscsilun->dd) { | ||||
|         g_free(iscsilun->dd->designator); | ||||
|         g_free(iscsilun->dd); | ||||
|     } | ||||
|     g_free(iscsilun->zeroblock); | ||||
|     iscsi_allocmap_free(iscsilun); | ||||
|     qemu_mutex_destroy(&iscsilun->mutex); | ||||
| @@ -2113,8 +2082,8 @@ static void iscsi_reopen_commit(BDRVReopenState *reopen_state) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                                           PreallocMode prealloc, Error **errp) | ||||
| static int iscsi_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                           PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     IscsiLun *iscsilun = bs->opaque; | ||||
|     Error *local_err = NULL; | ||||
| @@ -2174,7 +2143,7 @@ static int coroutine_fn iscsi_co_create_opts(const char *filename, QemuOpts *opt | ||||
|     } else { | ||||
|         ret = iscsi_open(bs, bs_options, 0, NULL); | ||||
|     } | ||||
|     qobject_unref(bs_options); | ||||
|     QDECREF(bs_options); | ||||
|  | ||||
|     if (ret != 0) { | ||||
|         goto out; | ||||
| @@ -2215,226 +2184,6 @@ static void coroutine_fn iscsi_co_invalidate_cache(BlockDriverState *bs, | ||||
|     iscsi_allocmap_invalidate(iscsilun); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn iscsi_co_copy_range_from(BlockDriverState *bs, | ||||
|                                                  BdrvChild *src, | ||||
|                                                  uint64_t src_offset, | ||||
|                                                  BdrvChild *dst, | ||||
|                                                  uint64_t dst_offset, | ||||
|                                                  uint64_t bytes, | ||||
|                                                  BdrvRequestFlags read_flags, | ||||
|                                                  BdrvRequestFlags write_flags) | ||||
| { | ||||
|     return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, | ||||
|                                  read_flags, write_flags); | ||||
| } | ||||
|  | ||||
| static struct scsi_task *iscsi_xcopy_task(int param_len) | ||||
| { | ||||
|     struct scsi_task *task; | ||||
|  | ||||
|     task = g_new0(struct scsi_task, 1); | ||||
|  | ||||
|     task->cdb[0]     = EXTENDED_COPY; | ||||
|     task->cdb[10]    = (param_len >> 24) & 0xFF; | ||||
|     task->cdb[11]    = (param_len >> 16) & 0xFF; | ||||
|     task->cdb[12]    = (param_len >> 8) & 0xFF; | ||||
|     task->cdb[13]    = param_len & 0xFF; | ||||
|     task->cdb_size   = 16; | ||||
|     task->xfer_dir   = SCSI_XFER_WRITE; | ||||
|     task->expxferlen = param_len; | ||||
|  | ||||
|     return task; | ||||
| } | ||||
|  | ||||
| static void iscsi_populate_target_desc(unsigned char *desc, IscsiLun *lun) | ||||
| { | ||||
|     struct scsi_inquiry_device_designator *dd = lun->dd; | ||||
|  | ||||
|     memset(desc, 0, 32); | ||||
|     desc[0] = 0xE4; /* IDENT_DESCR_TGT_DESCR */ | ||||
|     desc[4] = dd->code_set; | ||||
|     desc[5] = (dd->designator_type & 0xF) | ||||
|         | ((dd->association & 3) << 4); | ||||
|     desc[7] = dd->designator_length; | ||||
|     memcpy(desc + 8, dd->designator, MIN(dd->designator_length, 20)); | ||||
|  | ||||
|     desc[28] = 0; | ||||
|     desc[29] = (lun->block_size >> 16) & 0xFF; | ||||
|     desc[30] = (lun->block_size >> 8) & 0xFF; | ||||
|     desc[31] = lun->block_size & 0xFF; | ||||
| } | ||||
|  | ||||
| static void iscsi_xcopy_desc_hdr(uint8_t *hdr, int dc, int cat, int src_index, | ||||
|                                  int dst_index) | ||||
| { | ||||
|     hdr[0] = 0x02; /* BLK_TO_BLK_SEG_DESCR */ | ||||
|     hdr[1] = ((dc << 1) | cat) & 0xFF; | ||||
|     hdr[2] = (XCOPY_BLK2BLK_SEG_DESC_SIZE >> 8) & 0xFF; | ||||
|     /* don't account for the first 4 bytes in descriptor header*/ | ||||
|     hdr[3] = (XCOPY_BLK2BLK_SEG_DESC_SIZE - 4 /* SEG_DESC_SRC_INDEX_OFFSET */) & 0xFF; | ||||
|     hdr[4] = (src_index >> 8) & 0xFF; | ||||
|     hdr[5] = src_index & 0xFF; | ||||
|     hdr[6] = (dst_index >> 8) & 0xFF; | ||||
|     hdr[7] = dst_index & 0xFF; | ||||
| } | ||||
|  | ||||
| static void iscsi_xcopy_populate_desc(uint8_t *desc, int dc, int cat, | ||||
|                                       int src_index, int dst_index, int num_blks, | ||||
|                                       uint64_t src_lba, uint64_t dst_lba) | ||||
| { | ||||
|     iscsi_xcopy_desc_hdr(desc, dc, cat, src_index, dst_index); | ||||
|  | ||||
|     /* The caller should verify the request size */ | ||||
|     assert(num_blks < 65536); | ||||
|     desc[10] = (num_blks >> 8) & 0xFF; | ||||
|     desc[11] = num_blks & 0xFF; | ||||
|     desc[12] = (src_lba >> 56) & 0xFF; | ||||
|     desc[13] = (src_lba >> 48) & 0xFF; | ||||
|     desc[14] = (src_lba >> 40) & 0xFF; | ||||
|     desc[15] = (src_lba >> 32) & 0xFF; | ||||
|     desc[16] = (src_lba >> 24) & 0xFF; | ||||
|     desc[17] = (src_lba >> 16) & 0xFF; | ||||
|     desc[18] = (src_lba >> 8) & 0xFF; | ||||
|     desc[19] = src_lba & 0xFF; | ||||
|     desc[20] = (dst_lba >> 56) & 0xFF; | ||||
|     desc[21] = (dst_lba >> 48) & 0xFF; | ||||
|     desc[22] = (dst_lba >> 40) & 0xFF; | ||||
|     desc[23] = (dst_lba >> 32) & 0xFF; | ||||
|     desc[24] = (dst_lba >> 24) & 0xFF; | ||||
|     desc[25] = (dst_lba >> 16) & 0xFF; | ||||
|     desc[26] = (dst_lba >> 8) & 0xFF; | ||||
|     desc[27] = dst_lba & 0xFF; | ||||
| } | ||||
|  | ||||
| static void iscsi_xcopy_populate_header(unsigned char *buf, int list_id, int str, | ||||
|                                         int list_id_usage, int prio, | ||||
|                                         int tgt_desc_len, | ||||
|                                         int seg_desc_len, int inline_data_len) | ||||
| { | ||||
|     buf[0] = list_id; | ||||
|     buf[1] = ((str & 1) << 5) | ((list_id_usage & 3) << 3) | (prio & 7); | ||||
|     buf[2] = (tgt_desc_len >> 8) & 0xFF; | ||||
|     buf[3] = tgt_desc_len & 0xFF; | ||||
|     buf[8] = (seg_desc_len >> 24) & 0xFF; | ||||
|     buf[9] = (seg_desc_len >> 16) & 0xFF; | ||||
|     buf[10] = (seg_desc_len >> 8) & 0xFF; | ||||
|     buf[11] = seg_desc_len & 0xFF; | ||||
|     buf[12] = (inline_data_len >> 24) & 0xFF; | ||||
|     buf[13] = (inline_data_len >> 16) & 0xFF; | ||||
|     buf[14] = (inline_data_len >> 8) & 0xFF; | ||||
|     buf[15] = inline_data_len & 0xFF; | ||||
| } | ||||
|  | ||||
| static void iscsi_xcopy_data(struct iscsi_data *data, | ||||
|                              IscsiLun *src, int64_t src_lba, | ||||
|                              IscsiLun *dst, int64_t dst_lba, | ||||
|                              uint16_t num_blocks) | ||||
| { | ||||
|     uint8_t *buf; | ||||
|     const int src_offset = XCOPY_DESC_OFFSET; | ||||
|     const int dst_offset = XCOPY_DESC_OFFSET + IDENT_DESCR_TGT_DESCR_SIZE; | ||||
|     const int seg_offset = dst_offset + IDENT_DESCR_TGT_DESCR_SIZE; | ||||
|  | ||||
|     data->size = XCOPY_DESC_OFFSET + | ||||
|                  IDENT_DESCR_TGT_DESCR_SIZE * 2 + | ||||
|                  XCOPY_BLK2BLK_SEG_DESC_SIZE; | ||||
|     data->data = g_malloc0(data->size); | ||||
|     buf = data->data; | ||||
|  | ||||
|     /* Initialise the parameter list header */ | ||||
|     iscsi_xcopy_populate_header(buf, 1, 0, 2 /* LIST_ID_USAGE_DISCARD */, | ||||
|                                 0, 2 * IDENT_DESCR_TGT_DESCR_SIZE, | ||||
|                                 XCOPY_BLK2BLK_SEG_DESC_SIZE, | ||||
|                                 0); | ||||
|  | ||||
|     /* Initialise CSCD list with one src + one dst descriptor */ | ||||
|     iscsi_populate_target_desc(&buf[src_offset], src); | ||||
|     iscsi_populate_target_desc(&buf[dst_offset], dst); | ||||
|  | ||||
|     /* Initialise one segment descriptor */ | ||||
|     iscsi_xcopy_populate_desc(&buf[seg_offset], 0, 0, 0, 1, num_blocks, | ||||
|                               src_lba, dst_lba); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn iscsi_co_copy_range_to(BlockDriverState *bs, | ||||
|                                                BdrvChild *src, | ||||
|                                                uint64_t src_offset, | ||||
|                                                BdrvChild *dst, | ||||
|                                                uint64_t dst_offset, | ||||
|                                                uint64_t bytes, | ||||
|                                                BdrvRequestFlags read_flags, | ||||
|                                                BdrvRequestFlags write_flags) | ||||
| { | ||||
|     IscsiLun *dst_lun = dst->bs->opaque; | ||||
|     IscsiLun *src_lun; | ||||
|     struct IscsiTask iscsi_task; | ||||
|     struct iscsi_data data; | ||||
|     int r = 0; | ||||
|     int block_size; | ||||
|  | ||||
|     if (src->bs->drv->bdrv_co_copy_range_to != iscsi_co_copy_range_to) { | ||||
|         return -ENOTSUP; | ||||
|     } | ||||
|     src_lun = src->bs->opaque; | ||||
|  | ||||
|     if (!src_lun->dd || !dst_lun->dd) { | ||||
|         return -ENOTSUP; | ||||
|     } | ||||
|     if (!is_byte_request_lun_aligned(dst_offset, bytes, dst_lun)) { | ||||
|         return -ENOTSUP; | ||||
|     } | ||||
|     if (!is_byte_request_lun_aligned(src_offset, bytes, src_lun)) { | ||||
|         return -ENOTSUP; | ||||
|     } | ||||
|     if (dst_lun->block_size != src_lun->block_size || | ||||
|         !dst_lun->block_size) { | ||||
|         return -ENOTSUP; | ||||
|     } | ||||
|  | ||||
|     block_size = dst_lun->block_size; | ||||
|     if (bytes / block_size > 65535) { | ||||
|         return -ENOTSUP; | ||||
|     } | ||||
|  | ||||
|     iscsi_xcopy_data(&data, | ||||
|                      src_lun, src_offset / block_size, | ||||
|                      dst_lun, dst_offset / block_size, | ||||
|                      bytes / block_size); | ||||
|  | ||||
|     iscsi_co_init_iscsitask(dst_lun, &iscsi_task); | ||||
|  | ||||
|     qemu_mutex_lock(&dst_lun->mutex); | ||||
|     iscsi_task.task = iscsi_xcopy_task(data.size); | ||||
| retry: | ||||
|     if (iscsi_scsi_command_async(dst_lun->iscsi, dst_lun->lun, | ||||
|                                  iscsi_task.task, iscsi_co_generic_cb, | ||||
|                                  &data, | ||||
|                                  &iscsi_task) != 0) { | ||||
|         r = -EIO; | ||||
|         goto out_unlock; | ||||
|     } | ||||
|  | ||||
|     iscsi_co_wait_for_task(&iscsi_task, dst_lun); | ||||
|  | ||||
|     if (iscsi_task.do_retry) { | ||||
|         iscsi_task.complete = 0; | ||||
|         goto retry; | ||||
|     } | ||||
|  | ||||
|     if (iscsi_task.status != SCSI_STATUS_GOOD) { | ||||
|         r = iscsi_task.err_code; | ||||
|         goto out_unlock; | ||||
|     } | ||||
|  | ||||
| out_unlock: | ||||
|  | ||||
|     trace_iscsi_xcopy(src_lun, src_offset, dst_lun, dst_offset, bytes, r); | ||||
|     g_free(iscsi_task.task); | ||||
|     qemu_mutex_unlock(&dst_lun->mutex); | ||||
|     g_free(iscsi_task.err_str); | ||||
|     return r; | ||||
| } | ||||
|  | ||||
| static QemuOptsList iscsi_create_opts = { | ||||
|     .name = "iscsi-create-opts", | ||||
|     .head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head), | ||||
| @@ -2464,16 +2213,14 @@ static BlockDriver bdrv_iscsi = { | ||||
|  | ||||
|     .bdrv_getlength  = iscsi_getlength, | ||||
|     .bdrv_get_info   = iscsi_get_info, | ||||
|     .bdrv_co_truncate    = iscsi_co_truncate, | ||||
|     .bdrv_truncate   = iscsi_truncate, | ||||
|     .bdrv_refresh_limits = iscsi_refresh_limits, | ||||
|  | ||||
|     .bdrv_co_block_status  = iscsi_co_block_status, | ||||
|     .bdrv_co_pdiscard      = iscsi_co_pdiscard, | ||||
|     .bdrv_co_copy_range_from = iscsi_co_copy_range_from, | ||||
|     .bdrv_co_copy_range_to  = iscsi_co_copy_range_to, | ||||
|     .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, | ||||
|     .bdrv_co_readv         = iscsi_co_readv, | ||||
|     .bdrv_co_writev        = iscsi_co_writev, | ||||
|     .bdrv_co_writev_flags  = iscsi_co_writev_flags, | ||||
|     .bdrv_co_flush_to_disk = iscsi_co_flush, | ||||
|  | ||||
| #ifdef __linux__ | ||||
| @@ -2497,20 +2244,18 @@ static BlockDriver bdrv_iser = { | ||||
|     .create_opts            = &iscsi_create_opts, | ||||
|     .bdrv_reopen_prepare    = iscsi_reopen_prepare, | ||||
|     .bdrv_reopen_commit     = iscsi_reopen_commit, | ||||
|     .bdrv_co_invalidate_cache  = iscsi_co_invalidate_cache, | ||||
|     .bdrv_invalidate_cache  = iscsi_invalidate_cache, | ||||
|  | ||||
|     .bdrv_getlength  = iscsi_getlength, | ||||
|     .bdrv_get_info   = iscsi_get_info, | ||||
|     .bdrv_co_truncate    = iscsi_co_truncate, | ||||
|     .bdrv_truncate   = iscsi_truncate, | ||||
|     .bdrv_refresh_limits = iscsi_refresh_limits, | ||||
|  | ||||
|     .bdrv_co_block_status  = iscsi_co_block_status, | ||||
|     .bdrv_co_pdiscard      = iscsi_co_pdiscard, | ||||
|     .bdrv_co_copy_range_from = iscsi_co_copy_range_from, | ||||
|     .bdrv_co_copy_range_to  = iscsi_co_copy_range_to, | ||||
|     .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, | ||||
|     .bdrv_co_readv         = iscsi_co_readv, | ||||
|     .bdrv_co_writev        = iscsi_co_writev, | ||||
|     .bdrv_co_writev_flags  = iscsi_co_writev_flags, | ||||
|     .bdrv_co_flush_to_disk = iscsi_co_flush, | ||||
|  | ||||
| #ifdef __linux__ | ||||
|   | ||||
| @@ -15,7 +15,6 @@ | ||||
| #include "block/raw-aio.h" | ||||
| #include "qemu/event_notifier.h" | ||||
| #include "qemu/coroutine.h" | ||||
| #include "qapi/error.h" | ||||
|  | ||||
| #include <libaio.h> | ||||
|  | ||||
| @@ -234,9 +233,9 @@ static void qemu_laio_process_completions(LinuxAioState *s) | ||||
|  | ||||
| static void qemu_laio_process_completions_and_submit(LinuxAioState *s) | ||||
| { | ||||
|     aio_context_acquire(s->aio_context); | ||||
|     qemu_laio_process_completions(s); | ||||
|  | ||||
|     aio_context_acquire(s->aio_context); | ||||
|     if (!s->io_q.plugged && !QSIMPLEQ_EMPTY(&s->io_q.pending)) { | ||||
|         ioq_submit(s); | ||||
|     } | ||||
| @@ -384,10 +383,10 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, | ||||
|     switch (type) { | ||||
|     case QEMU_AIO_WRITE: | ||||
|         io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset); | ||||
|         break; | ||||
| 	break; | ||||
|     case QEMU_AIO_READ: | ||||
|         io_prep_preadv(iocbs, fd, qiov->iov, qiov->niov, offset); | ||||
|         break; | ||||
| 	break; | ||||
|     /* Currently Linux kernel does not support other operations */ | ||||
|     default: | ||||
|         fprintf(stderr, "%s: invalid AIO request type 0x%x.\n", | ||||
| @@ -471,21 +470,16 @@ void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context) | ||||
|                            qemu_laio_poll_cb); | ||||
| } | ||||
|  | ||||
| LinuxAioState *laio_init(Error **errp) | ||||
| LinuxAioState *laio_init(void) | ||||
| { | ||||
|     int rc; | ||||
|     LinuxAioState *s; | ||||
|  | ||||
|     s = g_malloc0(sizeof(*s)); | ||||
|     rc = event_notifier_init(&s->e, false); | ||||
|     if (rc < 0) { | ||||
|         error_setg_errno(errp, -rc, "failed to to initialize event notifier"); | ||||
|     if (event_notifier_init(&s->e, false) < 0) { | ||||
|         goto out_free_state; | ||||
|     } | ||||
|  | ||||
|     rc = io_setup(MAX_EVENTS, &s->ctx); | ||||
|     if (rc < 0) { | ||||
|         error_setg_errno(errp, -rc, "failed to create linux AIO context"); | ||||
|     if (io_setup(MAX_EVENTS, &s->ctx) != 0) { | ||||
|         goto out_close_efd; | ||||
|     } | ||||
|  | ||||
|   | ||||
							
								
								
									
										889
									
								
								block/mirror.c
									
									
									
									
									
								
							
							
						
						
									
										889
									
								
								block/mirror.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -28,8 +28,6 @@ | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
|  | ||||
| #include "trace.h" | ||||
| #include "qapi/error.h" | ||||
| #include "nbd-client.h" | ||||
|  | ||||
| @@ -53,13 +51,15 @@ static void nbd_teardown_connection(BlockDriverState *bs) | ||||
| { | ||||
|     NBDClientSession *client = nbd_get_client_session(bs); | ||||
|  | ||||
|     assert(client->ioc); | ||||
|     if (!client->ioc) { /* Already closed */ | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* finish any pending coroutines */ | ||||
|     qio_channel_shutdown(client->ioc, | ||||
|                          QIO_CHANNEL_SHUTDOWN_BOTH, | ||||
|                          NULL); | ||||
|     BDRV_POLL_WHILE(bs, client->connection_co); | ||||
|     BDRV_POLL_WHILE(bs, client->read_reply_co); | ||||
|  | ||||
|     nbd_client_detach_aio_context(bs); | ||||
|     object_unref(OBJECT(client->sioc)); | ||||
| @@ -68,7 +68,7 @@ static void nbd_teardown_connection(BlockDriverState *bs) | ||||
|     client->ioc = NULL; | ||||
| } | ||||
|  | ||||
| static coroutine_fn void nbd_connection_entry(void *opaque) | ||||
| static coroutine_fn void nbd_read_reply_entry(void *opaque) | ||||
| { | ||||
|     NBDClientSession *s = opaque; | ||||
|     uint64_t i; | ||||
| @@ -79,8 +79,7 @@ static coroutine_fn void nbd_connection_entry(void *opaque) | ||||
|         assert(s->reply.handle == 0); | ||||
|         ret = nbd_receive_reply(s->ioc, &s->reply, &local_err); | ||||
|         if (local_err) { | ||||
|             trace_nbd_read_reply_entry_fail(ret, error_get_pretty(local_err)); | ||||
|             error_free(local_err); | ||||
|             error_report_err(local_err); | ||||
|         } | ||||
|         if (ret <= 0) { | ||||
|             break; | ||||
| @@ -100,14 +99,14 @@ static coroutine_fn void nbd_connection_entry(void *opaque) | ||||
|         } | ||||
|  | ||||
|         /* We're woken up again by the request itself.  Note that there | ||||
|          * is no race between yielding and reentering connection_co.  This | ||||
|          * is no race between yielding and reentering read_reply_co.  This | ||||
|          * is because: | ||||
|          * | ||||
|          * - if the request runs on the same AioContext, it is only | ||||
|          *   entered after we yield | ||||
|          * | ||||
|          * - if the request runs on a different AioContext, reentering | ||||
|          *   connection_co happens through a bottom half, which can only | ||||
|          *   read_reply_co happens through a bottom half, which can only | ||||
|          *   run after we yield. | ||||
|          */ | ||||
|         aio_co_wake(s->requests[i].coroutine); | ||||
| @@ -116,8 +115,7 @@ static coroutine_fn void nbd_connection_entry(void *opaque) | ||||
|  | ||||
|     s->quit = true; | ||||
|     nbd_recv_coroutines_wake_all(s); | ||||
|     s->connection_co = NULL; | ||||
|     aio_wait_kick(); | ||||
|     s->read_reply_co = NULL; | ||||
| } | ||||
|  | ||||
| static int nbd_co_send_request(BlockDriverState *bs, | ||||
| @@ -152,7 +150,10 @@ static int nbd_co_send_request(BlockDriverState *bs, | ||||
|         rc = -EIO; | ||||
|         goto err; | ||||
|     } | ||||
|     assert(s->ioc); | ||||
|     if (!s->ioc) { | ||||
|         rc = -EPIPE; | ||||
|         goto err; | ||||
|     } | ||||
|  | ||||
|     if (qiov) { | ||||
|         qio_channel_set_cork(s->ioc, true); | ||||
| @@ -227,52 +228,6 @@ static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* nbd_parse_blockstatus_payload | ||||
|  * support only one extent in reply and only for | ||||
|  * base:allocation context | ||||
|  */ | ||||
| static int nbd_parse_blockstatus_payload(NBDClientSession *client, | ||||
|                                          NBDStructuredReplyChunk *chunk, | ||||
|                                          uint8_t *payload, uint64_t orig_length, | ||||
|                                          NBDExtent *extent, Error **errp) | ||||
| { | ||||
|     uint32_t context_id; | ||||
|  | ||||
|     if (chunk->length != sizeof(context_id) + sizeof(*extent)) { | ||||
|         error_setg(errp, "Protocol error: invalid payload for " | ||||
|                          "NBD_REPLY_TYPE_BLOCK_STATUS"); | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     context_id = payload_advance32(&payload); | ||||
|     if (client->info.context_id != context_id) { | ||||
|         error_setg(errp, "Protocol error: unexpected context id %d for " | ||||
|                          "NBD_REPLY_TYPE_BLOCK_STATUS, when negotiated context " | ||||
|                          "id is %d", context_id, | ||||
|                          client->info.context_id); | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     extent->length = payload_advance32(&payload); | ||||
|     extent->flags = payload_advance32(&payload); | ||||
|  | ||||
|     if (extent->length == 0 || | ||||
|         (client->info.min_block && !QEMU_IS_ALIGNED(extent->length, | ||||
|                                                     client->info.min_block))) { | ||||
|         error_setg(errp, "Protocol error: server sent status chunk with " | ||||
|                    "invalid length"); | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     /* The server is allowed to send us extra information on the final | ||||
|      * extent; just clamp it to the length we requested. */ | ||||
|     if (extent->length > orig_length) { | ||||
|         extent->length = orig_length; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* nbd_parse_error_payload | ||||
|  * on success @errp contains message describing nbd error reply | ||||
|  */ | ||||
| @@ -333,9 +288,10 @@ static int nbd_co_receive_offset_data_payload(NBDClientSession *s, | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     if (nbd_read64(s->ioc, &offset, "OFFSET_DATA offset", errp) < 0) { | ||||
|     if (nbd_read(s->ioc, &offset, sizeof(offset), errp) < 0) { | ||||
|         return -EIO; | ||||
|     } | ||||
|     be64_to_cpus(&offset); | ||||
|  | ||||
|     data_size = chunk->length - sizeof(offset); | ||||
|     assert(data_size); | ||||
| @@ -382,7 +338,7 @@ static coroutine_fn int nbd_co_receive_structured_payload( | ||||
|     } | ||||
|  | ||||
|     *payload = g_new(char, len); | ||||
|     ret = nbd_read(s->ioc, *payload, len, "structured payload", errp); | ||||
|     ret = nbd_read(s->ioc, *payload, len, errp); | ||||
|     if (ret < 0) { | ||||
|         g_free(*payload); | ||||
|         *payload = NULL; | ||||
| @@ -420,15 +376,14 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( | ||||
|     } | ||||
|     *request_ret = 0; | ||||
|  | ||||
|     /* Wait until we're woken up by nbd_connection_entry.  */ | ||||
|     /* Wait until we're woken up by nbd_read_reply_entry.  */ | ||||
|     s->requests[i].receiving = true; | ||||
|     qemu_coroutine_yield(); | ||||
|     s->requests[i].receiving = false; | ||||
|     if (s->quit) { | ||||
|     if (!s->ioc || s->quit) { | ||||
|         error_setg(errp, "Connection closed"); | ||||
|         return -EIO; | ||||
|     } | ||||
|     assert(s->ioc); | ||||
|  | ||||
|     assert(s->reply.handle == handle); | ||||
|  | ||||
| @@ -495,29 +450,30 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( | ||||
| } | ||||
|  | ||||
| /* nbd_co_receive_one_chunk | ||||
|  * Read reply, wake up connection_co and set s->quit if needed. | ||||
|  * Read reply, wake up read_reply_co and set s->quit if needed. | ||||
|  * Return value is a fatal error code or normal nbd reply error code | ||||
|  */ | ||||
| static coroutine_fn int nbd_co_receive_one_chunk( | ||||
|         NBDClientSession *s, uint64_t handle, bool only_structured, | ||||
|         int *request_ret, QEMUIOVector *qiov, NBDReply *reply, void **payload, | ||||
|         Error **errp) | ||||
|         QEMUIOVector *qiov, NBDReply *reply, void **payload, Error **errp) | ||||
| { | ||||
|     int request_ret; | ||||
|     int ret = nbd_co_do_receive_one_chunk(s, handle, only_structured, | ||||
|                                           request_ret, qiov, payload, errp); | ||||
|                                           &request_ret, qiov, payload, errp); | ||||
|  | ||||
|     if (ret < 0) { | ||||
|         s->quit = true; | ||||
|     } else { | ||||
|         /* For assert at loop start in nbd_connection_entry */ | ||||
|         /* For assert at loop start in nbd_read_reply_entry */ | ||||
|         if (reply) { | ||||
|             *reply = s->reply; | ||||
|         } | ||||
|         s->reply.handle = 0; | ||||
|         ret = request_ret; | ||||
|     } | ||||
|  | ||||
|     if (s->connection_co) { | ||||
|         aio_co_wake(s->connection_co); | ||||
|     if (s->read_reply_co) { | ||||
|         aio_co_wake(s->read_reply_co); | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
| @@ -525,17 +481,20 @@ static coroutine_fn int nbd_co_receive_one_chunk( | ||||
|  | ||||
| typedef struct NBDReplyChunkIter { | ||||
|     int ret; | ||||
|     int request_ret; | ||||
|     Error *err; | ||||
|     bool done, only_structured; | ||||
| } NBDReplyChunkIter; | ||||
|  | ||||
| static void nbd_iter_channel_error(NBDReplyChunkIter *iter, | ||||
|                                    int ret, Error **local_err) | ||||
| static void nbd_iter_error(NBDReplyChunkIter *iter, bool fatal, | ||||
|                            int ret, Error **local_err) | ||||
| { | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     if (!iter->ret) { | ||||
|     if (fatal || iter->ret == 0) { | ||||
|         if (iter->ret != 0) { | ||||
|             error_free(iter->err); | ||||
|             iter->err = NULL; | ||||
|         } | ||||
|         iter->ret = ret; | ||||
|         error_propagate(&iter->err, *local_err); | ||||
|     } else { | ||||
| @@ -545,15 +504,6 @@ static void nbd_iter_channel_error(NBDReplyChunkIter *iter, | ||||
|     *local_err = NULL; | ||||
| } | ||||
|  | ||||
| static void nbd_iter_request_error(NBDReplyChunkIter *iter, int ret) | ||||
| { | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     if (!iter->request_ret) { | ||||
|         iter->request_ret = ret; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* NBD_FOREACH_REPLY_CHUNK | ||||
|  */ | ||||
| #define NBD_FOREACH_REPLY_CHUNK(s, iter, handle, structured, \ | ||||
| @@ -569,13 +519,13 @@ static bool nbd_reply_chunk_iter_receive(NBDClientSession *s, | ||||
|                                          QEMUIOVector *qiov, NBDReply *reply, | ||||
|                                          void **payload) | ||||
| { | ||||
|     int ret, request_ret; | ||||
|     int ret; | ||||
|     NBDReply local_reply; | ||||
|     NBDStructuredReplyChunk *chunk; | ||||
|     Error *local_err = NULL; | ||||
|     if (s->quit) { | ||||
|         error_setg(&local_err, "Connection closed"); | ||||
|         nbd_iter_channel_error(iter, -EIO, &local_err); | ||||
|         nbd_iter_error(iter, true, -EIO, &local_err); | ||||
|         goto break_loop; | ||||
|     } | ||||
|  | ||||
| @@ -589,16 +539,14 @@ static bool nbd_reply_chunk_iter_receive(NBDClientSession *s, | ||||
|     } | ||||
|  | ||||
|     ret = nbd_co_receive_one_chunk(s, handle, iter->only_structured, | ||||
|                                    &request_ret, qiov, reply, payload, | ||||
|                                    &local_err); | ||||
|                                    qiov, reply, payload, &local_err); | ||||
|     if (ret < 0) { | ||||
|         nbd_iter_channel_error(iter, ret, &local_err); | ||||
|     } else if (request_ret < 0) { | ||||
|         nbd_iter_request_error(iter, request_ret); | ||||
|         /* If it is a fatal error s->quit is set by nbd_co_receive_one_chunk */ | ||||
|         nbd_iter_error(iter, s->quit, ret, &local_err); | ||||
|     } | ||||
|  | ||||
|     /* Do not execute the body of NBD_FOREACH_REPLY_CHUNK for simple reply. */ | ||||
|     if (nbd_reply_is_simple(reply) || s->quit) { | ||||
|     if (nbd_reply_is_simple(&s->reply) || s->quit) { | ||||
|         goto break_loop; | ||||
|     } | ||||
|  | ||||
| @@ -631,7 +579,7 @@ break_loop: | ||||
| } | ||||
|  | ||||
| static int nbd_co_receive_return_code(NBDClientSession *s, uint64_t handle, | ||||
|                                       int *request_ret, Error **errp) | ||||
|                                       Error **errp) | ||||
| { | ||||
|     NBDReplyChunkIter iter; | ||||
|  | ||||
| @@ -640,13 +588,12 @@ static int nbd_co_receive_return_code(NBDClientSession *s, uint64_t handle, | ||||
|     } | ||||
|  | ||||
|     error_propagate(errp, iter.err); | ||||
|     *request_ret = iter.request_ret; | ||||
|     return iter.ret; | ||||
| } | ||||
|  | ||||
| static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle, | ||||
|                                         uint64_t offset, QEMUIOVector *qiov, | ||||
|                                         int *request_ret, Error **errp) | ||||
|                                         Error **errp) | ||||
| { | ||||
|     NBDReplyChunkIter iter; | ||||
|     NBDReply reply; | ||||
| @@ -671,7 +618,7 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle, | ||||
|                                                 offset, qiov, &local_err); | ||||
|             if (ret < 0) { | ||||
|                 s->quit = true; | ||||
|                 nbd_iter_channel_error(&iter, ret, &local_err); | ||||
|                 nbd_iter_error(&iter, true, ret, &local_err); | ||||
|             } | ||||
|             break; | ||||
|         default: | ||||
| @@ -681,7 +628,7 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle, | ||||
|                 error_setg(&local_err, | ||||
|                            "Unexpected reply type: %d (%s) for CMD_READ", | ||||
|                            chunk->type, nbd_reply_type_lookup(chunk->type)); | ||||
|                 nbd_iter_channel_error(&iter, -EINVAL, &local_err); | ||||
|                 nbd_iter_error(&iter, true, -EINVAL, &local_err); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -690,79 +637,13 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle, | ||||
|     } | ||||
|  | ||||
|     error_propagate(errp, iter.err); | ||||
|     *request_ret = iter.request_ret; | ||||
|     return iter.ret; | ||||
| } | ||||
|  | ||||
| static int nbd_co_receive_blockstatus_reply(NBDClientSession *s, | ||||
|                                             uint64_t handle, uint64_t length, | ||||
|                                             NBDExtent *extent, | ||||
|                                             int *request_ret, Error **errp) | ||||
| { | ||||
|     NBDReplyChunkIter iter; | ||||
|     NBDReply reply; | ||||
|     void *payload = NULL; | ||||
|     Error *local_err = NULL; | ||||
|     bool received = false; | ||||
|  | ||||
|     assert(!extent->length); | ||||
|     NBD_FOREACH_REPLY_CHUNK(s, iter, handle, s->info.structured_reply, | ||||
|                             NULL, &reply, &payload) | ||||
|     { | ||||
|         int ret; | ||||
|         NBDStructuredReplyChunk *chunk = &reply.structured; | ||||
|  | ||||
|         assert(nbd_reply_is_structured(&reply)); | ||||
|  | ||||
|         switch (chunk->type) { | ||||
|         case NBD_REPLY_TYPE_BLOCK_STATUS: | ||||
|             if (received) { | ||||
|                 s->quit = true; | ||||
|                 error_setg(&local_err, "Several BLOCK_STATUS chunks in reply"); | ||||
|                 nbd_iter_channel_error(&iter, -EINVAL, &local_err); | ||||
|             } | ||||
|             received = true; | ||||
|  | ||||
|             ret = nbd_parse_blockstatus_payload(s, &reply.structured, | ||||
|                                                 payload, length, extent, | ||||
|                                                 &local_err); | ||||
|             if (ret < 0) { | ||||
|                 s->quit = true; | ||||
|                 nbd_iter_channel_error(&iter, ret, &local_err); | ||||
|             } | ||||
|             break; | ||||
|         default: | ||||
|             if (!nbd_reply_type_is_error(chunk->type)) { | ||||
|                 s->quit = true; | ||||
|                 error_setg(&local_err, | ||||
|                            "Unexpected reply type: %d (%s) " | ||||
|                            "for CMD_BLOCK_STATUS", | ||||
|                            chunk->type, nbd_reply_type_lookup(chunk->type)); | ||||
|                 nbd_iter_channel_error(&iter, -EINVAL, &local_err); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         g_free(payload); | ||||
|         payload = NULL; | ||||
|     } | ||||
|  | ||||
|     if (!extent->length && !iter.err) { | ||||
|         error_setg(&iter.err, | ||||
|                    "Server did not reply with any status extents"); | ||||
|         if (!iter.ret) { | ||||
|             iter.ret = -EIO; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     error_propagate(errp, iter.err); | ||||
|     *request_ret = iter.request_ret; | ||||
|     return iter.ret; | ||||
| } | ||||
|  | ||||
| static int nbd_co_request(BlockDriverState *bs, NBDRequest *request, | ||||
|                           QEMUIOVector *write_qiov) | ||||
| { | ||||
|     int ret, request_ret; | ||||
|     int ret; | ||||
|     Error *local_err = NULL; | ||||
|     NBDClientSession *client = nbd_get_client_session(bs); | ||||
|  | ||||
| @@ -778,22 +659,17 @@ static int nbd_co_request(BlockDriverState *bs, NBDRequest *request, | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     ret = nbd_co_receive_return_code(client, request->handle, | ||||
|                                      &request_ret, &local_err); | ||||
|     ret = nbd_co_receive_return_code(client, request->handle, &local_err); | ||||
|     if (local_err) { | ||||
|         trace_nbd_co_request_fail(request->from, request->len, request->handle, | ||||
|                                   request->flags, request->type, | ||||
|                                   nbd_cmd_lookup(request->type), | ||||
|                                   ret, error_get_pretty(local_err)); | ||||
|         error_free(local_err); | ||||
|         error_report_err(local_err); | ||||
|     } | ||||
|     return ret ? ret : request_ret; | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, | ||||
|                          uint64_t bytes, QEMUIOVector *qiov, int flags) | ||||
| { | ||||
|     int ret, request_ret; | ||||
|     int ret; | ||||
|     Error *local_err = NULL; | ||||
|     NBDClientSession *client = nbd_get_client_session(bs); | ||||
|     NBDRequest request = { | ||||
| @@ -814,15 +690,11 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, | ||||
|     } | ||||
|  | ||||
|     ret = nbd_co_receive_cmdread_reply(client, request.handle, offset, qiov, | ||||
|                                        &request_ret, &local_err); | ||||
|                                        &local_err); | ||||
|     if (local_err) { | ||||
|         trace_nbd_co_request_fail(request.from, request.len, request.handle, | ||||
|                                   request.flags, request.type, | ||||
|                                   nbd_cmd_lookup(request.type), | ||||
|                                   ret, error_get_pretty(local_err)); | ||||
|         error_free(local_err); | ||||
|         error_report_err(local_err); | ||||
|     } | ||||
|     return ret ? ret : request_ret; | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset, | ||||
| @@ -910,55 +782,6 @@ int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) | ||||
|     return nbd_co_request(bs, &request, NULL); | ||||
| } | ||||
|  | ||||
| int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs, | ||||
|                                             bool want_zero, | ||||
|                                             int64_t offset, int64_t bytes, | ||||
|                                             int64_t *pnum, int64_t *map, | ||||
|                                             BlockDriverState **file) | ||||
| { | ||||
|     int ret, request_ret; | ||||
|     NBDExtent extent = { 0 }; | ||||
|     NBDClientSession *client = nbd_get_client_session(bs); | ||||
|     Error *local_err = NULL; | ||||
|  | ||||
|     NBDRequest request = { | ||||
|         .type = NBD_CMD_BLOCK_STATUS, | ||||
|         .from = offset, | ||||
|         .len = MIN(MIN_NON_ZERO(QEMU_ALIGN_DOWN(INT_MAX, | ||||
|                                                 bs->bl.request_alignment), | ||||
|                                 client->info.max_block), bytes), | ||||
|         .flags = NBD_CMD_FLAG_REQ_ONE, | ||||
|     }; | ||||
|  | ||||
|     if (!client->info.base_allocation) { | ||||
|         *pnum = bytes; | ||||
|         return BDRV_BLOCK_DATA; | ||||
|     } | ||||
|  | ||||
|     ret = nbd_co_send_request(bs, &request, NULL); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     ret = nbd_co_receive_blockstatus_reply(client, request.handle, bytes, | ||||
|                                            &extent, &request_ret, &local_err); | ||||
|     if (local_err) { | ||||
|         trace_nbd_co_request_fail(request.from, request.len, request.handle, | ||||
|                                   request.flags, request.type, | ||||
|                                   nbd_cmd_lookup(request.type), | ||||
|                                   ret, error_get_pretty(local_err)); | ||||
|         error_free(local_err); | ||||
|     } | ||||
|     if (ret < 0 || request_ret < 0) { | ||||
|         return ret ? ret : request_ret; | ||||
|     } | ||||
|  | ||||
|     assert(extent.length); | ||||
|     *pnum = extent.length; | ||||
|     return (extent.flags & NBD_STATE_HOLE ? 0 : BDRV_BLOCK_DATA) | | ||||
|            (extent.flags & NBD_STATE_ZERO ? BDRV_BLOCK_ZERO : 0); | ||||
| } | ||||
|  | ||||
| void nbd_client_detach_aio_context(BlockDriverState *bs) | ||||
| { | ||||
|     NBDClientSession *client = nbd_get_client_session(bs); | ||||
| @@ -970,7 +793,7 @@ void nbd_client_attach_aio_context(BlockDriverState *bs, | ||||
| { | ||||
|     NBDClientSession *client = nbd_get_client_session(bs); | ||||
|     qio_channel_attach_aio_context(QIO_CHANNEL(client->ioc), new_context); | ||||
|     aio_co_schedule(new_context, client->connection_co); | ||||
|     aio_co_schedule(new_context, client->read_reply_co); | ||||
| } | ||||
|  | ||||
| void nbd_client_close(BlockDriverState *bs) | ||||
| @@ -978,84 +801,43 @@ void nbd_client_close(BlockDriverState *bs) | ||||
|     NBDClientSession *client = nbd_get_client_session(bs); | ||||
|     NBDRequest request = { .type = NBD_CMD_DISC }; | ||||
|  | ||||
|     assert(client->ioc); | ||||
|     if (client->ioc == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     nbd_send_request(client->ioc, &request); | ||||
|  | ||||
|     nbd_teardown_connection(bs); | ||||
| } | ||||
|  | ||||
| static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr, | ||||
|                                                   Error **errp) | ||||
| { | ||||
|     QIOChannelSocket *sioc; | ||||
|     Error *local_err = NULL; | ||||
|  | ||||
|     sioc = qio_channel_socket_new(); | ||||
|     qio_channel_set_name(QIO_CHANNEL(sioc), "nbd-client"); | ||||
|  | ||||
|     qio_channel_socket_connect_sync(sioc, saddr, &local_err); | ||||
|     if (local_err) { | ||||
|         object_unref(OBJECT(sioc)); | ||||
|         error_propagate(errp, local_err); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     qio_channel_set_delay(QIO_CHANNEL(sioc), false); | ||||
|  | ||||
|     return sioc; | ||||
| } | ||||
|  | ||||
| static int nbd_client_connect(BlockDriverState *bs, | ||||
|                               SocketAddress *saddr, | ||||
|                               const char *export, | ||||
|                               QCryptoTLSCreds *tlscreds, | ||||
|                               const char *hostname, | ||||
|                               const char *x_dirty_bitmap, | ||||
|                               Error **errp) | ||||
| int nbd_client_init(BlockDriverState *bs, | ||||
|                     QIOChannelSocket *sioc, | ||||
|                     const char *export, | ||||
|                     QCryptoTLSCreds *tlscreds, | ||||
|                     const char *hostname, | ||||
|                     Error **errp) | ||||
| { | ||||
|     NBDClientSession *client = nbd_get_client_session(bs); | ||||
|     int ret; | ||||
|  | ||||
|     /* | ||||
|      * establish TCP connection, return error if it fails | ||||
|      * TODO: Configurable retry-until-timeout behaviour. | ||||
|      */ | ||||
|     QIOChannelSocket *sioc = nbd_establish_connection(saddr, errp); | ||||
|  | ||||
|     if (!sioc) { | ||||
|         return -ECONNREFUSED; | ||||
|     } | ||||
|  | ||||
|     /* NBD handshake */ | ||||
|     logout("session init %s\n", export); | ||||
|     qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL); | ||||
|  | ||||
|     client->info.request_sizes = true; | ||||
|     client->info.structured_reply = true; | ||||
|     client->info.base_allocation = true; | ||||
|     client->info.x_dirty_bitmap = g_strdup(x_dirty_bitmap); | ||||
|     client->info.name = g_strdup(export ?: ""); | ||||
|     ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), tlscreds, hostname, | ||||
|     ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export, | ||||
|                                 tlscreds, hostname, | ||||
|                                 &client->ioc, &client->info, errp); | ||||
|     g_free(client->info.x_dirty_bitmap); | ||||
|     g_free(client->info.name); | ||||
|     if (ret < 0) { | ||||
|         logout("Failed to negotiate with the NBD server\n"); | ||||
|         object_unref(OBJECT(sioc)); | ||||
|         return ret; | ||||
|     } | ||||
|     if (x_dirty_bitmap && !client->info.base_allocation) { | ||||
|         error_setg(errp, "requested x-dirty-bitmap %s not found", | ||||
|                    x_dirty_bitmap); | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|     } | ||||
|     if (client->info.flags & NBD_FLAG_READ_ONLY) { | ||||
|         ret = bdrv_apply_auto_read_only(bs, "NBD export is read-only", errp); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
|         } | ||||
|     if (client->info.flags & NBD_FLAG_READ_ONLY && | ||||
|         !bdrv_is_read_only(bs)) { | ||||
|         error_setg(errp, | ||||
|                    "request for write access conflicts with read-only export"); | ||||
|         return -EACCES; | ||||
|     } | ||||
|     if (client->info.flags & NBD_FLAG_SEND_FUA) { | ||||
|         bs->supported_write_flags = BDRV_REQ_FUA; | ||||
| @@ -1065,7 +847,10 @@ static int nbd_client_connect(BlockDriverState *bs, | ||||
|         bs->supported_zero_flags |= BDRV_REQ_MAY_UNMAP; | ||||
|     } | ||||
|  | ||||
|     qemu_co_mutex_init(&client->send_mutex); | ||||
|     qemu_co_queue_init(&client->free_sema); | ||||
|     client->sioc = sioc; | ||||
|     object_ref(OBJECT(client->sioc)); | ||||
|  | ||||
|     if (!client->ioc) { | ||||
|         client->ioc = QIO_CHANNEL(sioc); | ||||
| @@ -1075,42 +860,9 @@ static int nbd_client_connect(BlockDriverState *bs, | ||||
|     /* 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); | ||||
|     client->connection_co = qemu_coroutine_create(nbd_connection_entry, client); | ||||
|     client->read_reply_co = qemu_coroutine_create(nbd_read_reply_entry, client); | ||||
|     nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs)); | ||||
|  | ||||
|     logout("Established connection with NBD server\n"); | ||||
|     return 0; | ||||
|  | ||||
|  fail: | ||||
|     /* | ||||
|      * We have connected, but must fail for other reasons. The | ||||
|      * connection is still blocking; send NBD_CMD_DISC as a courtesy | ||||
|      * to the server. | ||||
|      */ | ||||
|     { | ||||
|         NBDRequest request = { .type = NBD_CMD_DISC }; | ||||
|  | ||||
|         nbd_send_request(client->ioc ?: QIO_CHANNEL(sioc), &request); | ||||
|  | ||||
|         object_unref(OBJECT(sioc)); | ||||
|  | ||||
|         return ret; | ||||
|     } | ||||
| } | ||||
|  | ||||
| int nbd_client_init(BlockDriverState *bs, | ||||
|                     SocketAddress *saddr, | ||||
|                     const char *export, | ||||
|                     QCryptoTLSCreds *tlscreds, | ||||
|                     const char *hostname, | ||||
|                     const char *x_dirty_bitmap, | ||||
|                     Error **errp) | ||||
| { | ||||
|     NBDClientSession *client = nbd_get_client_session(bs); | ||||
|  | ||||
|     qemu_co_mutex_init(&client->send_mutex); | ||||
|     qemu_co_queue_init(&client->free_sema); | ||||
|  | ||||
|     return nbd_client_connect(bs, saddr, export, tlscreds, hostname, | ||||
|                               x_dirty_bitmap, errp); | ||||
| } | ||||
|   | ||||
| @@ -20,7 +20,7 @@ | ||||
| typedef struct { | ||||
|     Coroutine *coroutine; | ||||
|     uint64_t offset;        /* original offset of the request */ | ||||
|     bool receiving;         /* waiting for connection_co? */ | ||||
|     bool receiving;         /* waiting for read_reply_co? */ | ||||
| } NBDClientRequest; | ||||
|  | ||||
| typedef struct NBDClientSession { | ||||
| @@ -30,7 +30,7 @@ typedef struct NBDClientSession { | ||||
|  | ||||
|     CoMutex send_mutex; | ||||
|     CoQueue free_sema; | ||||
|     Coroutine *connection_co; | ||||
|     Coroutine *read_reply_co; | ||||
|     int in_flight; | ||||
|  | ||||
|     NBDClientRequest requests[MAX_NBD_REQUESTS]; | ||||
| @@ -41,11 +41,10 @@ typedef struct NBDClientSession { | ||||
| NBDClientSession *nbd_get_client_session(BlockDriverState *bs); | ||||
|  | ||||
| int nbd_client_init(BlockDriverState *bs, | ||||
|                     SocketAddress *saddr, | ||||
|                     QIOChannelSocket *sock, | ||||
|                     const char *export_name, | ||||
|                     QCryptoTLSCreds *tlscreds, | ||||
|                     const char *hostname, | ||||
|                     const char *x_dirty_bitmap, | ||||
|                     Error **errp); | ||||
| void nbd_client_close(BlockDriverState *bs); | ||||
|  | ||||
| @@ -62,10 +61,4 @@ void nbd_client_detach_aio_context(BlockDriverState *bs); | ||||
| void nbd_client_attach_aio_context(BlockDriverState *bs, | ||||
|                                    AioContext *new_context); | ||||
|  | ||||
| int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs, | ||||
|                                             bool want_zero, | ||||
|                                             int64_t offset, int64_t bytes, | ||||
|                                             int64_t *pnum, int64_t *map, | ||||
|                                             BlockDriverState **file); | ||||
|  | ||||
| #endif /* NBD_CLIENT_H */ | ||||
|   | ||||
							
								
								
									
										73
									
								
								block/nbd.c
									
									
									
									
									
								
							
							
						
						
									
										73
									
								
								block/nbd.c
									
									
									
									
									
								
							| @@ -27,8 +27,7 @@ | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "nbd-client.h" | ||||
| #include "block/qdict.h" | ||||
| #include "block/nbd-client.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qemu/uri.h" | ||||
| #include "block/block_int.h" | ||||
| @@ -109,7 +108,7 @@ static int nbd_parse_uri(const char *filename, QDict *options) | ||||
|         /* strip braces from literal IPv6 address */ | ||||
|         if (uri->server[0] == '[') { | ||||
|             host = qstring_from_substr(uri->server, 1, | ||||
|                                        strlen(uri->server) - 1); | ||||
|                                        strlen(uri->server) - 2); | ||||
|         } else { | ||||
|             host = qstring_from_str(uri->server); | ||||
|         } | ||||
| @@ -263,6 +262,7 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, | ||||
| { | ||||
|     SocketAddress *saddr = NULL; | ||||
|     QDict *addr = NULL; | ||||
|     QObject *crumpled_addr = NULL; | ||||
|     Visitor *iv = NULL; | ||||
|     Error *local_err = NULL; | ||||
|  | ||||
| @@ -272,11 +272,20 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, | ||||
|         goto done; | ||||
|     } | ||||
|  | ||||
|     iv = qobject_input_visitor_new_flat_confused(addr, errp); | ||||
|     if (!iv) { | ||||
|     crumpled_addr = qdict_crumple(addr, errp); | ||||
|     if (!crumpled_addr) { | ||||
|         goto done; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive | ||||
|      * server.type=inet.  .to doesn't matter, it's ignored anyway. | ||||
|      * That's because when @options come from -blockdev or | ||||
|      * blockdev_add, members are typed according to the QAPI schema, | ||||
|      * but when they come from -drive, they're all QString.  The | ||||
|      * visitor expects the former. | ||||
|      */ | ||||
|     iv = qobject_input_visitor_new(crumpled_addr); | ||||
|     visit_type_SocketAddress(iv, NULL, &saddr, &local_err); | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
| @@ -284,7 +293,8 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, | ||||
|     } | ||||
|  | ||||
| done: | ||||
|     qobject_unref(addr); | ||||
|     QDECREF(addr); | ||||
|     qobject_decref(crumpled_addr); | ||||
|     visit_free(iv); | ||||
|     return saddr; | ||||
| } | ||||
| @@ -295,6 +305,30 @@ NBDClientSession *nbd_get_client_session(BlockDriverState *bs) | ||||
|     return &s->client; | ||||
| } | ||||
|  | ||||
| static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr, | ||||
|                                                   Error **errp) | ||||
| { | ||||
|     QIOChannelSocket *sioc; | ||||
|     Error *local_err = NULL; | ||||
|  | ||||
|     sioc = qio_channel_socket_new(); | ||||
|     qio_channel_set_name(QIO_CHANNEL(sioc), "nbd-client"); | ||||
|  | ||||
|     qio_channel_socket_connect_sync(sioc, | ||||
|                                     saddr, | ||||
|                                     &local_err); | ||||
|     if (local_err) { | ||||
|         object_unref(OBJECT(sioc)); | ||||
|         error_propagate(errp, local_err); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     qio_channel_set_delay(QIO_CHANNEL(sioc), false); | ||||
|  | ||||
|     return sioc; | ||||
| } | ||||
|  | ||||
|  | ||||
| static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) | ||||
| { | ||||
|     Object *obj; | ||||
| @@ -354,12 +388,6 @@ static QemuOptsList nbd_runtime_opts = { | ||||
|             .type = QEMU_OPT_STRING, | ||||
|             .help = "ID of the TLS credentials to use", | ||||
|         }, | ||||
|         { | ||||
|             .name = "x-dirty-bitmap", | ||||
|             .type = QEMU_OPT_STRING, | ||||
|             .help = "experimental: expose named dirty bitmap in place of " | ||||
|                     "block status", | ||||
|         }, | ||||
|         { /* end of list */ } | ||||
|     }, | ||||
| }; | ||||
| @@ -370,6 +398,7 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     BDRVNBDState *s = bs->opaque; | ||||
|     QemuOpts *opts = NULL; | ||||
|     Error *local_err = NULL; | ||||
|     QIOChannelSocket *sioc = NULL; | ||||
|     QCryptoTLSCreds *tlscreds = NULL; | ||||
|     const char *hostname = NULL; | ||||
|     int ret = -EINVAL; | ||||
| @@ -409,11 +438,22 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         hostname = s->saddr->u.inet.host; | ||||
|     } | ||||
|  | ||||
|     /* NBD handshake */ | ||||
|     ret = nbd_client_init(bs, s->saddr, s->export, tlscreds, hostname, | ||||
|                           qemu_opt_get(opts, "x-dirty-bitmap"), errp); | ||||
|     /* establish TCP connection, return error if it fails | ||||
|      * TODO: Configurable retry-until-timeout behaviour. | ||||
|      */ | ||||
|     sioc = nbd_establish_connection(s->saddr, errp); | ||||
|     if (!sioc) { | ||||
|         ret = -ECONNREFUSED; | ||||
|         goto error; | ||||
|     } | ||||
|  | ||||
|     /* NBD handshake */ | ||||
|     ret = nbd_client_init(bs, sioc, s->export, | ||||
|                           tlscreds, hostname, errp); | ||||
|  error: | ||||
|     if (sioc) { | ||||
|         object_unref(OBJECT(sioc)); | ||||
|     } | ||||
|     if (tlscreds) { | ||||
|         object_unref(OBJECT(tlscreds)); | ||||
|     } | ||||
| @@ -545,7 +585,6 @@ static BlockDriver bdrv_nbd = { | ||||
|     .bdrv_detach_aio_context    = nbd_detach_aio_context, | ||||
|     .bdrv_attach_aio_context    = nbd_attach_aio_context, | ||||
|     .bdrv_refresh_filename      = nbd_refresh_filename, | ||||
|     .bdrv_co_block_status       = nbd_client_co_block_status, | ||||
| }; | ||||
|  | ||||
| static BlockDriver bdrv_nbd_tcp = { | ||||
| @@ -565,7 +604,6 @@ static BlockDriver bdrv_nbd_tcp = { | ||||
|     .bdrv_detach_aio_context    = nbd_detach_aio_context, | ||||
|     .bdrv_attach_aio_context    = nbd_attach_aio_context, | ||||
|     .bdrv_refresh_filename      = nbd_refresh_filename, | ||||
|     .bdrv_co_block_status       = nbd_client_co_block_status, | ||||
| }; | ||||
|  | ||||
| static BlockDriver bdrv_nbd_unix = { | ||||
| @@ -585,7 +623,6 @@ static BlockDriver bdrv_nbd_unix = { | ||||
|     .bdrv_detach_aio_context    = nbd_detach_aio_context, | ||||
|     .bdrv_attach_aio_context    = nbd_attach_aio_context, | ||||
|     .bdrv_refresh_filename      = nbd_refresh_filename, | ||||
|     .bdrv_co_block_status       = nbd_client_co_block_status, | ||||
| }; | ||||
|  | ||||
| static void bdrv_nbd_init(void) | ||||
|   | ||||
							
								
								
									
										25
									
								
								block/nfs.c
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								block/nfs.c
									
									
									
									
									
								
							| @@ -29,7 +29,6 @@ | ||||
| #include "qemu/error-report.h" | ||||
| #include "qapi/error.h" | ||||
| #include "block/block_int.h" | ||||
| #include "block/qdict.h" | ||||
| #include "trace.h" | ||||
| #include "qemu/iov.h" | ||||
| #include "qemu/option.h" | ||||
| @@ -556,29 +555,24 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options, | ||||
|                                                      Error **errp) | ||||
| { | ||||
|     BlockdevOptionsNfs *opts = NULL; | ||||
|     QObject *crumpled = NULL; | ||||
|     Visitor *v; | ||||
|     const QDictEntry *e; | ||||
|     Error *local_err = NULL; | ||||
|  | ||||
|     v = qobject_input_visitor_new_flat_confused(options, errp); | ||||
|     if (!v) { | ||||
|     crumpled = qdict_crumple(options, errp); | ||||
|     if (crumpled == NULL) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     v = qobject_input_visitor_new_keyval(crumpled); | ||||
|     visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err); | ||||
|     visit_free(v); | ||||
|     qobject_decref(crumpled); | ||||
|  | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     /* Remove the processed options from the QDict (the visitor processes | ||||
|      * _all_ options in the QDict) */ | ||||
|     while ((e = qdict_first(options))) { | ||||
|         qdict_del(options, e->key); | ||||
|     } | ||||
|  | ||||
|     return opts; | ||||
| } | ||||
|  | ||||
| @@ -689,7 +683,7 @@ static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts, | ||||
|  | ||||
|     ret = 0; | ||||
| out: | ||||
|     qobject_unref(options); | ||||
|     QDECREF(options); | ||||
|     qapi_free_BlockdevCreateOptions(create_options); | ||||
|     return ret; | ||||
| } | ||||
| @@ -743,9 +737,8 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) | ||||
|     return (task.ret < 0 ? task.ret : st.st_blocks * 512); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn | ||||
| nfs_file_co_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                      PreallocMode prealloc, Error **errp) | ||||
| static int nfs_file_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                              PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     NFSClient *client = bs->opaque; | ||||
|     int ret; | ||||
| @@ -874,7 +867,7 @@ static BlockDriver bdrv_nfs = { | ||||
|  | ||||
|     .bdrv_has_zero_init             = nfs_has_zero_init, | ||||
|     .bdrv_get_allocated_file_size   = nfs_get_allocated_file_size, | ||||
|     .bdrv_co_truncate               = nfs_file_co_truncate, | ||||
|     .bdrv_truncate                  = nfs_file_truncate, | ||||
|  | ||||
|     .bdrv_file_open                 = nfs_file_open, | ||||
|     .bdrv_close                     = nfs_file_close, | ||||
|   | ||||
							
								
								
									
										54
									
								
								block/null.c
									
									
									
									
									
								
							
							
						
						
									
										54
									
								
								block/null.c
									
									
									
									
									
								
							| @@ -93,10 +93,13 @@ static int null_file_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     } | ||||
|     s->read_zeroes = qemu_opt_get_bool(opts, NULL_OPT_ZEROES, false); | ||||
|     qemu_opts_del(opts); | ||||
|     bs->supported_write_flags = BDRV_REQ_FUA; | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static void null_close(BlockDriverState *bs) | ||||
| { | ||||
| } | ||||
|  | ||||
| static int64_t null_getlength(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVNullState *s = bs->opaque; | ||||
| @@ -113,22 +116,22 @@ static coroutine_fn int null_co_common(BlockDriverState *bs) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static coroutine_fn int null_co_preadv(BlockDriverState *bs, | ||||
|                                        uint64_t offset, uint64_t bytes, | ||||
|                                        QEMUIOVector *qiov, int flags) | ||||
| static coroutine_fn int null_co_readv(BlockDriverState *bs, | ||||
|                                       int64_t sector_num, int nb_sectors, | ||||
|                                       QEMUIOVector *qiov) | ||||
| { | ||||
|     BDRVNullState *s = bs->opaque; | ||||
|  | ||||
|     if (s->read_zeroes) { | ||||
|         qemu_iovec_memset(qiov, 0, 0, bytes); | ||||
|         qemu_iovec_memset(qiov, 0, 0, nb_sectors * BDRV_SECTOR_SIZE); | ||||
|     } | ||||
|  | ||||
|     return null_co_common(bs); | ||||
| } | ||||
|  | ||||
| static coroutine_fn int null_co_pwritev(BlockDriverState *bs, | ||||
|                                         uint64_t offset, uint64_t bytes, | ||||
|                                         QEMUIOVector *qiov, int flags) | ||||
| static coroutine_fn int null_co_writev(BlockDriverState *bs, | ||||
|                                        int64_t sector_num, int nb_sectors, | ||||
|                                        QEMUIOVector *qiov) | ||||
| { | ||||
|     return null_co_common(bs); | ||||
| } | ||||
| @@ -183,26 +186,26 @@ static inline BlockAIOCB *null_aio_common(BlockDriverState *bs, | ||||
|     return &acb->common; | ||||
| } | ||||
|  | ||||
| static BlockAIOCB *null_aio_preadv(BlockDriverState *bs, | ||||
|                                    uint64_t offset, uint64_t bytes, | ||||
|                                    QEMUIOVector *qiov, int flags, | ||||
|                                    BlockCompletionFunc *cb, | ||||
|                                    void *opaque) | ||||
| static BlockAIOCB *null_aio_readv(BlockDriverState *bs, | ||||
|                                   int64_t sector_num, QEMUIOVector *qiov, | ||||
|                                   int nb_sectors, | ||||
|                                   BlockCompletionFunc *cb, | ||||
|                                   void *opaque) | ||||
| { | ||||
|     BDRVNullState *s = bs->opaque; | ||||
|  | ||||
|     if (s->read_zeroes) { | ||||
|         qemu_iovec_memset(qiov, 0, 0, bytes); | ||||
|         qemu_iovec_memset(qiov, 0, 0, nb_sectors * BDRV_SECTOR_SIZE); | ||||
|     } | ||||
|  | ||||
|     return null_aio_common(bs, cb, opaque); | ||||
| } | ||||
|  | ||||
| static BlockAIOCB *null_aio_pwritev(BlockDriverState *bs, | ||||
|                                     uint64_t offset, uint64_t bytes, | ||||
|                                     QEMUIOVector *qiov, int flags, | ||||
|                                     BlockCompletionFunc *cb, | ||||
|                                     void *opaque) | ||||
| static BlockAIOCB *null_aio_writev(BlockDriverState *bs, | ||||
|                                    int64_t sector_num, QEMUIOVector *qiov, | ||||
|                                    int nb_sectors, | ||||
|                                    BlockCompletionFunc *cb, | ||||
|                                    void *opaque) | ||||
| { | ||||
|     return null_aio_common(bs, cb, opaque); | ||||
| } | ||||
| @@ -241,6 +244,7 @@ static int coroutine_fn null_co_block_status(BlockDriverState *bs, | ||||
|  | ||||
| static void null_refresh_filename(BlockDriverState *bs, QDict *opts) | ||||
| { | ||||
|     QINCREF(opts); | ||||
|     qdict_del(opts, "filename"); | ||||
|  | ||||
|     if (!qdict_size(opts)) { | ||||
| @@ -249,7 +253,7 @@ static void null_refresh_filename(BlockDriverState *bs, QDict *opts) | ||||
|     } | ||||
|  | ||||
|     qdict_put_str(opts, "driver", bs->drv->format_name); | ||||
|     bs->full_open_options = qobject_ref(opts); | ||||
|     bs->full_open_options = opts; | ||||
| } | ||||
|  | ||||
| static BlockDriver bdrv_null_co = { | ||||
| @@ -259,10 +263,11 @@ static BlockDriver bdrv_null_co = { | ||||
|  | ||||
|     .bdrv_file_open         = null_file_open, | ||||
|     .bdrv_parse_filename    = null_co_parse_filename, | ||||
|     .bdrv_close             = null_close, | ||||
|     .bdrv_getlength         = null_getlength, | ||||
|  | ||||
|     .bdrv_co_preadv         = null_co_preadv, | ||||
|     .bdrv_co_pwritev        = null_co_pwritev, | ||||
|     .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, | ||||
|  | ||||
| @@ -278,10 +283,11 @@ static BlockDriver bdrv_null_aio = { | ||||
|  | ||||
|     .bdrv_file_open         = null_file_open, | ||||
|     .bdrv_parse_filename    = null_aio_parse_filename, | ||||
|     .bdrv_close             = null_close, | ||||
|     .bdrv_getlength         = null_getlength, | ||||
|  | ||||
|     .bdrv_aio_preadv        = null_aio_preadv, | ||||
|     .bdrv_aio_pwritev       = null_aio_pwritev, | ||||
|     .bdrv_aio_readv         = null_aio_readv, | ||||
|     .bdrv_aio_writev        = null_aio_writev, | ||||
|     .bdrv_aio_flush         = null_aio_flush, | ||||
|     .bdrv_reopen_prepare    = null_reopen_prepare, | ||||
|  | ||||
|   | ||||
							
								
								
									
										90
									
								
								block/nvme.c
									
									
									
									
									
								
							
							
						
						
									
										90
									
								
								block/nvme.c
									
									
									
									
									
								
							| @@ -104,7 +104,7 @@ typedef struct { | ||||
|     uint64_t nsze; /* Namespace size reported by identify command */ | ||||
|     int nsid;      /* The namespace id to read/write data. */ | ||||
|     uint64_t max_transfer; | ||||
|     bool plugged; | ||||
|     int plugged; | ||||
|  | ||||
|     CoMutex dma_map_lock; | ||||
|     CoQueue dma_flush_queue; | ||||
| @@ -390,7 +390,6 @@ static void nvme_cmd_sync_cb(void *opaque, int ret) | ||||
| { | ||||
|     int *pret = opaque; | ||||
|     *pret = ret; | ||||
|     aio_wait_kick(); | ||||
| } | ||||
|  | ||||
| static int nvme_cmd_sync(BlockDriverState *bs, NVMeQueuePair *q, | ||||
| @@ -490,8 +489,10 @@ static void nvme_handle_event(EventNotifier *n) | ||||
|     BDRVNVMeState *s = container_of(n, BDRVNVMeState, irq_notifier); | ||||
|  | ||||
|     trace_nvme_handle_event(s); | ||||
|     aio_context_acquire(s->aio_context); | ||||
|     event_notifier_test_and_clear(n); | ||||
|     nvme_poll_queues(s); | ||||
|     aio_context_release(s->aio_context); | ||||
| } | ||||
|  | ||||
| static bool nvme_add_io_queue(BlockDriverState *bs, Error **errp) | ||||
| @@ -568,13 +569,13 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, | ||||
|     s->vfio = qemu_vfio_open_pci(device, errp); | ||||
|     if (!s->vfio) { | ||||
|         ret = -EINVAL; | ||||
|         goto out; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     s->regs = qemu_vfio_pci_map_bar(s->vfio, 0, 0, NVME_BAR_SIZE, errp); | ||||
|     if (!s->regs) { | ||||
|         ret = -EINVAL; | ||||
|         goto out; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     /* Perform initialize sequence as described in NVMe spec "7.6.1 | ||||
| @@ -584,7 +585,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, | ||||
|     if (!(cap & (1ULL << 37))) { | ||||
|         error_setg(errp, "Device doesn't support NVMe command set"); | ||||
|         ret = -EINVAL; | ||||
|         goto out; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     s->page_size = MAX(4096, 1 << (12 + ((cap >> 48) & 0xF))); | ||||
| @@ -602,7 +603,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, | ||||
|                              PRId64 " ms)", | ||||
|                        timeout_ms); | ||||
|             ret = -ETIMEDOUT; | ||||
|             goto out; | ||||
|             goto fail; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -612,7 +613,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, | ||||
|     s->queues[0] = nvme_create_queue_pair(bs, 0, NVME_QUEUE_SIZE, errp); | ||||
|     if (!s->queues[0]) { | ||||
|         ret = -EINVAL; | ||||
|         goto out; | ||||
|         goto fail; | ||||
|     } | ||||
|     QEMU_BUILD_BUG_ON(NVME_QUEUE_SIZE & 0xF000); | ||||
|     s->regs->aqa = cpu_to_le32((NVME_QUEUE_SIZE << 16) | NVME_QUEUE_SIZE); | ||||
| @@ -632,14 +633,14 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, | ||||
|                              PRId64 " ms)", | ||||
|                        timeout_ms); | ||||
|             ret = -ETIMEDOUT; | ||||
|             goto out; | ||||
|             goto fail_queue; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     ret = qemu_vfio_pci_init_irq(s->vfio, &s->irq_notifier, | ||||
|                                  VFIO_PCI_MSIX_IRQ_INDEX, errp); | ||||
|     if (ret) { | ||||
|         goto out; | ||||
|         goto fail_queue; | ||||
|     } | ||||
|     aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier, | ||||
|                            false, nvme_handle_event, nvme_poll_cb); | ||||
| @@ -648,15 +649,30 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         ret = -EIO; | ||||
|         goto out; | ||||
|         goto fail_handler; | ||||
|     } | ||||
|  | ||||
|     /* Set up command queues. */ | ||||
|     if (!nvme_add_io_queue(bs, errp)) { | ||||
|         ret = -EIO; | ||||
|         goto fail_handler; | ||||
|     } | ||||
| out: | ||||
|     /* Cleaning up is done in nvme_file_open() upon error. */ | ||||
|     return 0; | ||||
|  | ||||
| fail_handler: | ||||
|     aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier, | ||||
|                            false, NULL, NULL); | ||||
| fail_queue: | ||||
|     nvme_free_queue_pair(bs, s->queues[0]); | ||||
| fail: | ||||
|     g_free(s->queues); | ||||
|     if (s->regs) { | ||||
|         qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)s->regs, 0, NVME_BAR_SIZE); | ||||
|     } | ||||
|     if (s->vfio) { | ||||
|         qemu_vfio_close(s->vfio); | ||||
|     } | ||||
|     event_notifier_cleanup(&s->irq_notifier); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| @@ -679,11 +695,12 @@ static void nvme_parse_filename(const char *filename, QDict *options, | ||||
|         unsigned long ns; | ||||
|         const char *slash = strchr(tmp, '/'); | ||||
|         if (!slash) { | ||||
|             qdict_put_str(options, NVME_BLOCK_OPT_DEVICE, tmp); | ||||
|             qdict_put(options, NVME_BLOCK_OPT_DEVICE, | ||||
|                       qstring_from_str(tmp)); | ||||
|             return; | ||||
|         } | ||||
|         device = g_strndup(tmp, slash - tmp); | ||||
|         qdict_put_str(options, NVME_BLOCK_OPT_DEVICE, device); | ||||
|         qdict_put(options, NVME_BLOCK_OPT_DEVICE, qstring_from_str(device)); | ||||
|         g_free(device); | ||||
|         namespace = slash + 1; | ||||
|         if (*namespace && qemu_strtoul(namespace, NULL, 10, &ns)) { | ||||
| @@ -691,8 +708,8 @@ static void nvme_parse_filename(const char *filename, QDict *options, | ||||
|                        namespace); | ||||
|             return; | ||||
|         } | ||||
|         qdict_put_str(options, NVME_BLOCK_OPT_NAMESPACE, | ||||
|                       *namespace ? namespace : "1"); | ||||
|         qdict_put(options, NVME_BLOCK_OPT_NAMESPACE, | ||||
|                   qstring_from_str(*namespace ? namespace : "1")); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -723,10 +740,8 @@ static void nvme_close(BlockDriverState *bs) | ||||
|     for (i = 0; i < s->nr_queues; ++i) { | ||||
|         nvme_free_queue_pair(bs, s->queues[i]); | ||||
|     } | ||||
|     g_free(s->queues); | ||||
|     aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier, | ||||
|                            false, NULL, NULL); | ||||
|     event_notifier_cleanup(&s->irq_notifier); | ||||
|     qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)s->regs, 0, NVME_BAR_SIZE); | ||||
|     qemu_vfio_close(s->vfio); | ||||
| } | ||||
| @@ -838,7 +853,7 @@ try_map: | ||||
|         } | ||||
|  | ||||
|         for (j = 0; j < qiov->iov[i].iov_len / s->page_size; j++) { | ||||
|             pagelist[entries++] = cpu_to_le64(iova + j * s->page_size); | ||||
|             pagelist[entries++] = iova + j * s->page_size; | ||||
|         } | ||||
|         trace_nvme_cmd_map_qiov_iov(s, i, qiov->iov[i].iov_base, | ||||
|                                     qiov->iov[i].iov_len / s->page_size); | ||||
| @@ -851,16 +866,20 @@ try_map: | ||||
|     case 0: | ||||
|         abort(); | ||||
|     case 1: | ||||
|         cmd->prp1 = pagelist[0]; | ||||
|         cmd->prp1 = cpu_to_le64(pagelist[0]); | ||||
|         cmd->prp2 = 0; | ||||
|         break; | ||||
|     case 2: | ||||
|         cmd->prp1 = pagelist[0]; | ||||
|         cmd->prp2 = pagelist[1]; | ||||
|         cmd->prp1 = cpu_to_le64(pagelist[0]); | ||||
|         cmd->prp2 = cpu_to_le64(pagelist[1]);; | ||||
|         break; | ||||
|     default: | ||||
|         cmd->prp1 = pagelist[0]; | ||||
|         cmd->prp2 = cpu_to_le64(req->prp_list_iova + sizeof(uint64_t)); | ||||
|         cmd->prp1 = cpu_to_le64(pagelist[0]); | ||||
|         cmd->prp2 = cpu_to_le64(req->prp_list_iova); | ||||
|         for (i = 0; i < entries - 1; ++i) { | ||||
|             pagelist[i] = cpu_to_le64(pagelist[i + 1]); | ||||
|         } | ||||
|         pagelist[entries - 1] = 0; | ||||
|         break; | ||||
|     } | ||||
|     trace_nvme_cmd_map_qiov(s, cmd, req, qiov, entries); | ||||
| @@ -1055,6 +1074,7 @@ static int nvme_reopen_prepare(BDRVReopenState *reopen_state, | ||||
|  | ||||
| static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts) | ||||
| { | ||||
|     QINCREF(opts); | ||||
|     qdict_del(opts, "filename"); | ||||
|  | ||||
|     if (!qdict_size(opts)) { | ||||
| @@ -1062,8 +1082,8 @@ static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts) | ||||
|                  bs->drv->format_name); | ||||
|     } | ||||
|  | ||||
|     qdict_put_str(opts, "driver", bs->drv->format_name); | ||||
|     bs->full_open_options = qobject_ref(opts); | ||||
|     qdict_put(opts, "driver", qstring_from_str(bs->drv->format_name)); | ||||
|     bs->full_open_options = opts; | ||||
| } | ||||
|  | ||||
| static void nvme_refresh_limits(BlockDriverState *bs, Error **errp) | ||||
| @@ -1096,8 +1116,7 @@ static void nvme_attach_aio_context(BlockDriverState *bs, | ||||
| static void nvme_aio_plug(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVNVMeState *s = bs->opaque; | ||||
|     assert(!s->plugged); | ||||
|     s->plugged = true; | ||||
|     s->plugged++; | ||||
| } | ||||
|  | ||||
| static void nvme_aio_unplug(BlockDriverState *bs) | ||||
| @@ -1105,13 +1124,14 @@ static void nvme_aio_unplug(BlockDriverState *bs) | ||||
|     int i; | ||||
|     BDRVNVMeState *s = bs->opaque; | ||||
|     assert(s->plugged); | ||||
|     s->plugged = false; | ||||
|     for (i = 1; i < s->nr_queues; i++) { | ||||
|         NVMeQueuePair *q = s->queues[i]; | ||||
|         qemu_mutex_lock(&q->lock); | ||||
|         nvme_kick(s, q); | ||||
|         nvme_process_completion(s, q); | ||||
|         qemu_mutex_unlock(&q->lock); | ||||
|     if (!--s->plugged) { | ||||
|         for (i = 1; i < s->nr_queues; i++) { | ||||
|             NVMeQueuePair *q = s->queues[i]; | ||||
|             qemu_mutex_lock(&q->lock); | ||||
|             nvme_kick(s, q); | ||||
|             nvme_process_completion(s, q); | ||||
|             qemu_mutex_unlock(&q->lock); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -31,13 +31,9 @@ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qapi/error.h" | ||||
| #include "block/block_int.h" | ||||
| #include "block/qdict.h" | ||||
| #include "sysemu/block-backend.h" | ||||
| #include "qemu/module.h" | ||||
| #include "qemu/option.h" | ||||
| #include "qapi/qmp/qdict.h" | ||||
| #include "qapi/qobject-input-visitor.h" | ||||
| #include "qapi/qapi-visit-block-core.h" | ||||
| #include "qemu/bswap.h" | ||||
| #include "qemu/bitmap.h" | ||||
| #include "migration/blocker.h" | ||||
| @@ -83,25 +79,6 @@ static QemuOptsList parallels_runtime_opts = { | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| 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 int64_t bat2sect(BDRVParallelsState *s, uint32_t idx) | ||||
| { | ||||
| @@ -227,15 +204,14 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, | ||||
|         }; | ||||
|         qemu_iovec_init_external(&qiov, &iov, 1); | ||||
|  | ||||
|         ret = bdrv_co_preadv(bs->backing, idx * s->tracks * BDRV_SECTOR_SIZE, | ||||
|                              nb_cow_bytes, &qiov, 0); | ||||
|         ret = bdrv_co_readv(bs->backing, idx * s->tracks, nb_cow_sectors, | ||||
|                             &qiov); | ||||
|         if (ret < 0) { | ||||
|             qemu_vfree(iov.iov_base); | ||||
|             return ret; | ||||
|         } | ||||
|  | ||||
|         ret = bdrv_co_pwritev(bs->file, s->data_end * BDRV_SECTOR_SIZE, | ||||
|                               nb_cow_bytes, &qiov, 0); | ||||
|         ret = bdrv_co_writev(bs->file, s->data_end, nb_cow_sectors, &qiov); | ||||
|         qemu_vfree(iov.iov_base); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
| @@ -313,15 +289,13 @@ static int coroutine_fn parallels_co_block_status(BlockDriverState *bs, | ||||
| } | ||||
|  | ||||
| static coroutine_fn int parallels_co_writev(BlockDriverState *bs, | ||||
|                                             int64_t sector_num, int nb_sectors, | ||||
|                                             QEMUIOVector *qiov, int flags) | ||||
|         int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) | ||||
| { | ||||
|     BDRVParallelsState *s = bs->opaque; | ||||
|     uint64_t bytes_done = 0; | ||||
|     QEMUIOVector hd_qiov; | ||||
|     int ret = 0; | ||||
|  | ||||
|     assert(!flags); | ||||
|     qemu_iovec_init(&hd_qiov, qiov->niov); | ||||
|  | ||||
|     while (nb_sectors > 0) { | ||||
| @@ -341,8 +315,7 @@ static coroutine_fn int parallels_co_writev(BlockDriverState *bs, | ||||
|         qemu_iovec_reset(&hd_qiov); | ||||
|         qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes); | ||||
|  | ||||
|         ret = bdrv_co_pwritev(bs->file, position * BDRV_SECTOR_SIZE, nbytes, | ||||
|                               &hd_qiov, 0); | ||||
|         ret = bdrv_co_writev(bs->file, position, n, &hd_qiov); | ||||
|         if (ret < 0) { | ||||
|             break; | ||||
|         } | ||||
| @@ -381,8 +354,7 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs, | ||||
|  | ||||
|         if (position < 0) { | ||||
|             if (bs->backing) { | ||||
|                 ret = bdrv_co_preadv(bs->backing, sector_num * BDRV_SECTOR_SIZE, | ||||
|                                      nbytes, &hd_qiov, 0); | ||||
|                 ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov); | ||||
|                 if (ret < 0) { | ||||
|                     break; | ||||
|                 } | ||||
| @@ -390,8 +362,7 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs, | ||||
|                 qemu_iovec_memset(&hd_qiov, 0, 0, nbytes); | ||||
|             } | ||||
|         } else { | ||||
|             ret = bdrv_co_preadv(bs->file, position * BDRV_SECTOR_SIZE, nbytes, | ||||
|                                  &hd_qiov, 0); | ||||
|             ret = bdrv_co_readv(bs->file, position, n, &hd_qiov); | ||||
|             if (ret < 0) { | ||||
|                 break; | ||||
|             } | ||||
| @@ -509,67 +480,46 @@ out: | ||||
| } | ||||
|  | ||||
|  | ||||
| static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts, | ||||
|                                             Error **errp) | ||||
| static int coroutine_fn parallels_co_create_opts(const char *filename, | ||||
|                                                  QemuOpts *opts, | ||||
|                                                  Error **errp) | ||||
| { | ||||
|     BlockdevCreateOptionsParallels *parallels_opts; | ||||
|     BlockDriverState *bs; | ||||
|     BlockBackend *blk; | ||||
|     int64_t total_size, cl_size; | ||||
|     uint8_t tmp[BDRV_SECTOR_SIZE]; | ||||
|     Error *local_err = NULL; | ||||
|     BlockBackend *file; | ||||
|     uint32_t bat_entries, bat_sectors; | ||||
|     ParallelsHeader header; | ||||
|     uint8_t tmp[BDRV_SECTOR_SIZE]; | ||||
|     int ret; | ||||
|  | ||||
|     assert(opts->driver == BLOCKDEV_DRIVER_PARALLELS); | ||||
|     parallels_opts = &opts->u.parallels; | ||||
|  | ||||
|     /* Sanity checks */ | ||||
|     total_size = parallels_opts->size; | ||||
|  | ||||
|     if (parallels_opts->has_cluster_size) { | ||||
|         cl_size = parallels_opts->cluster_size; | ||||
|     } else { | ||||
|         cl_size = DEFAULT_CLUSTER_SIZE; | ||||
|     } | ||||
|  | ||||
|     /* XXX What is the real limit here? This is an insanely large maximum. */ | ||||
|     if (cl_size >= INT64_MAX / MAX_PARALLELS_IMAGE_FACTOR) { | ||||
|         error_setg(errp, "Cluster size is too large"); | ||||
|         return -EINVAL; | ||||
|     } | ||||
|     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); | ||||
|     if (total_size >= MAX_PARALLELS_IMAGE_FACTOR * cl_size) { | ||||
|         error_setg(errp, "Image size is too large for this cluster size"); | ||||
|         error_propagate(errp, local_err); | ||||
|         return -E2BIG; | ||||
|     } | ||||
|  | ||||
|     if (!QEMU_IS_ALIGNED(total_size, BDRV_SECTOR_SIZE)) { | ||||
|         error_setg(errp, "Image size must be a multiple of 512 bytes"); | ||||
|         return -EINVAL; | ||||
|     ret = bdrv_create_file(filename, opts, &local_err); | ||||
|     if (ret < 0) { | ||||
|         error_propagate(errp, local_err); | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     if (!QEMU_IS_ALIGNED(cl_size, BDRV_SECTOR_SIZE)) { | ||||
|         error_setg(errp, "Cluster size must be a multiple of 512 bytes"); | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     /* Create BlockBackend to write to the image */ | ||||
|     bs = bdrv_open_blockdev_ref(parallels_opts->file, errp); | ||||
|     if (bs == NULL) { | ||||
|     file = blk_new_open(filename, NULL, NULL, | ||||
|                         BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, | ||||
|                         &local_err); | ||||
|     if (file == NULL) { | ||||
|         error_propagate(errp, local_err); | ||||
|         return -EIO; | ||||
|     } | ||||
|  | ||||
|     blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); | ||||
|     ret = blk_insert_bs(blk, bs, errp); | ||||
|     if (ret < 0) { | ||||
|         goto out; | ||||
|     } | ||||
|     blk_set_allow_write_beyond_eof(blk, true); | ||||
|     blk_set_allow_write_beyond_eof(file, true); | ||||
|  | ||||
|     /* Create image format */ | ||||
|     ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp); | ||||
|     ret = blk_truncate(file, 0, PREALLOC_MODE_OFF, errp); | ||||
|     if (ret < 0) { | ||||
|         goto out; | ||||
|         goto exit; | ||||
|     } | ||||
|  | ||||
|     bat_entries = DIV_ROUND_UP(total_size, cl_size); | ||||
| @@ -592,103 +542,24 @@ static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts, | ||||
|     memset(tmp, 0, sizeof(tmp)); | ||||
|     memcpy(tmp, &header, sizeof(header)); | ||||
|  | ||||
|     ret = blk_pwrite(blk, 0, tmp, BDRV_SECTOR_SIZE, 0); | ||||
|     ret = blk_pwrite(file, 0, tmp, BDRV_SECTOR_SIZE, 0); | ||||
|     if (ret < 0) { | ||||
|         goto exit; | ||||
|     } | ||||
|     ret = blk_pwrite_zeroes(blk, BDRV_SECTOR_SIZE, | ||||
|     ret = blk_pwrite_zeroes(file, BDRV_SECTOR_SIZE, | ||||
|                             (bat_sectors - 1) << BDRV_SECTOR_BITS, 0); | ||||
|     if (ret < 0) { | ||||
|         goto exit; | ||||
|     } | ||||
|  | ||||
|     ret = 0; | ||||
| out: | ||||
|     blk_unref(blk); | ||||
|     bdrv_unref(bs); | ||||
|  | ||||
| done: | ||||
|     blk_unref(file); | ||||
|     return ret; | ||||
|  | ||||
| exit: | ||||
|     error_setg_errno(errp, -ret, "Failed to create Parallels image"); | ||||
|     goto out; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn parallels_co_create_opts(const char *filename, | ||||
|                                                  QemuOpts *opts, | ||||
|                                                  Error **errp) | ||||
| { | ||||
|     BlockdevCreateOptions *create_options = NULL; | ||||
|     Error *local_err = NULL; | ||||
|     BlockDriverState *bs = NULL; | ||||
|     QDict *qdict; | ||||
|     Visitor *v; | ||||
|     int ret; | ||||
|  | ||||
|     static const QDictRenames opt_renames[] = { | ||||
|         { BLOCK_OPT_CLUSTER_SIZE,       "cluster-size" }, | ||||
|         { NULL, NULL }, | ||||
|     }; | ||||
|  | ||||
|     /* Parse options and convert legacy syntax */ | ||||
|     qdict = qemu_opts_to_qdict_filtered(opts, NULL, ¶llels_create_opts, | ||||
|                                         true); | ||||
|  | ||||
|     if (!qdict_rename_keys(qdict, opt_renames, errp)) { | ||||
|         ret = -EINVAL; | ||||
|         goto done; | ||||
|     } | ||||
|  | ||||
|     /* Create and open the file (protocol layer) */ | ||||
|     ret = bdrv_create_file(filename, opts, &local_err); | ||||
|     if (ret < 0) { | ||||
|         error_propagate(errp, local_err); | ||||
|         goto done; | ||||
|     } | ||||
|  | ||||
|     bs = bdrv_open(filename, NULL, NULL, | ||||
|                    BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); | ||||
|     if (bs == NULL) { | ||||
|         ret = -EIO; | ||||
|         goto done; | ||||
|     } | ||||
|  | ||||
|     /* Now get the QAPI type BlockdevCreateOptions */ | ||||
|     qdict_put_str(qdict, "driver", "parallels"); | ||||
|     qdict_put_str(qdict, "file", bs->node_name); | ||||
|  | ||||
|     v = qobject_input_visitor_new_flat_confused(qdict, errp); | ||||
|     if (!v) { | ||||
|         ret = -EINVAL; | ||||
|         goto done; | ||||
|     } | ||||
|  | ||||
|     visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); | ||||
|     visit_free(v); | ||||
|  | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         ret = -EINVAL; | ||||
|         goto done; | ||||
|     } | ||||
|  | ||||
|     /* Silently round up sizes */ | ||||
|     create_options->u.parallels.size = | ||||
|         ROUND_UP(create_options->u.parallels.size, BDRV_SECTOR_SIZE); | ||||
|     create_options->u.parallels.cluster_size = | ||||
|         ROUND_UP(create_options->u.parallels.cluster_size, BDRV_SECTOR_SIZE); | ||||
|  | ||||
|     /* Create the Parallels image (format layer) */ | ||||
|     ret = parallels_co_create(create_options, errp); | ||||
|     if (ret < 0) { | ||||
|         goto done; | ||||
|     } | ||||
|     ret = 0; | ||||
|  | ||||
| done: | ||||
|     qobject_unref(qdict); | ||||
|     bdrv_unref(bs); | ||||
|     qapi_free_BlockdevCreateOptions(create_options); | ||||
|     return ret; | ||||
|     goto done; | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -900,6 +771,25 @@ static void parallels_close(BlockDriverState *bs) | ||||
|     error_free(s->migration_blocker); | ||||
| } | ||||
|  | ||||
| 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), | ||||
| @@ -913,7 +803,6 @@ static BlockDriver bdrv_parallels = { | ||||
|     .bdrv_co_readv  = parallels_co_readv, | ||||
|     .bdrv_co_writev = parallels_co_writev, | ||||
|     .supports_backing = true, | ||||
|     .bdrv_co_create      = parallels_co_create, | ||||
|     .bdrv_co_create_opts = parallels_co_create_opts, | ||||
|     .bdrv_co_check  = parallels_co_check, | ||||
|     .create_opts    = ¶llels_create_opts, | ||||
|   | ||||
							
								
								
									
										74
									
								
								block/qapi.c
									
									
									
									
									
								
							
							
						
						
									
										74
									
								
								block/qapi.c
									
									
									
									
									
								
							| @@ -394,37 +394,6 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, | ||||
|     qapi_free_BlockInfo(info); | ||||
| } | ||||
|  | ||||
| static uint64List *uint64_list(uint64_t *list, int size) | ||||
| { | ||||
|     int i; | ||||
|     uint64List *out_list = NULL; | ||||
|     uint64List **pout_list = &out_list; | ||||
|  | ||||
|     for (i = 0; i < size; i++) { | ||||
|         uint64List *entry = g_new(uint64List, 1); | ||||
|         entry->value = list[i]; | ||||
|         *pout_list = entry; | ||||
|         pout_list = &entry->next; | ||||
|     } | ||||
|  | ||||
|     *pout_list = NULL; | ||||
|  | ||||
|     return out_list; | ||||
| } | ||||
|  | ||||
| static void bdrv_latency_histogram_stats(BlockLatencyHistogram *hist, | ||||
|                                          bool *not_null, | ||||
|                                          BlockLatencyHistogramInfo **info) | ||||
| { | ||||
|     *not_null = hist->bins != NULL; | ||||
|     if (*not_null) { | ||||
|         *info = g_new0(BlockLatencyHistogramInfo, 1); | ||||
|  | ||||
|         (*info)->boundaries = uint64_list(hist->boundaries, hist->nbins - 1); | ||||
|         (*info)->bins = uint64_list(hist->bins, hist->nbins); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) | ||||
| { | ||||
|     BlockAcctStats *stats = blk_get_stats(blk); | ||||
| @@ -490,16 +459,6 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) | ||||
|         dev_stats->avg_wr_queue_depth = | ||||
|             block_acct_queue_depth(ts, BLOCK_ACCT_WRITE); | ||||
|     } | ||||
|  | ||||
|     bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_READ], | ||||
|                                  &ds->has_x_rd_latency_histogram, | ||||
|                                  &ds->x_rd_latency_histogram); | ||||
|     bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_WRITE], | ||||
|                                  &ds->has_x_wr_latency_histogram, | ||||
|                                  &ds->x_wr_latency_histogram); | ||||
|     bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_FLUSH], | ||||
|                                  &ds->has_x_flush_latency_histogram, | ||||
|                                  &ds->x_flush_latency_histogram); | ||||
| } | ||||
|  | ||||
| static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs, | ||||
| @@ -593,33 +552,18 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, | ||||
|             p_next = &info->next; | ||||
|         } | ||||
|     } else { | ||||
|         for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) { | ||||
|             BlockStatsList *info; | ||||
|         for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { | ||||
|             BlockStatsList *info = g_malloc0(sizeof(*info)); | ||||
|             AioContext *ctx = blk_get_aio_context(blk); | ||||
|             BlockStats *s; | ||||
|             char *qdev; | ||||
|  | ||||
|             if (!*blk_name(blk) && !blk_get_attached_dev(blk)) { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             aio_context_acquire(ctx); | ||||
|             s = bdrv_query_bds_stats(blk_bs(blk), true); | ||||
|             s->has_device = true; | ||||
|             s->device = g_strdup(blk_name(blk)); | ||||
|  | ||||
|             qdev = blk_get_attached_dev_id(blk); | ||||
|             if (qdev && *qdev) { | ||||
|                 s->has_qdev = true; | ||||
|                 s->qdev = qdev; | ||||
|             } else { | ||||
|                 g_free(qdev); | ||||
|             } | ||||
|  | ||||
|             bdrv_query_blk_stats(s->stats, blk); | ||||
|             aio_context_release(ctx); | ||||
|  | ||||
|             info = g_malloc0(sizeof(*info)); | ||||
|             info->value = s; | ||||
|             *p_next = info; | ||||
|             p_next = &info->next; | ||||
| @@ -703,29 +647,29 @@ static void dump_qobject(fprintf_function func_fprintf, void *f, | ||||
| { | ||||
|     switch (qobject_type(obj)) { | ||||
|         case QTYPE_QNUM: { | ||||
|             QNum *value = qobject_to(QNum, obj); | ||||
|             QNum *value = qobject_to_qnum(obj); | ||||
|             char *tmp = qnum_to_string(value); | ||||
|             func_fprintf(f, "%s", tmp); | ||||
|             g_free(tmp); | ||||
|             break; | ||||
|         } | ||||
|         case QTYPE_QSTRING: { | ||||
|             QString *value = qobject_to(QString, obj); | ||||
|             QString *value = qobject_to_qstring(obj); | ||||
|             func_fprintf(f, "%s", qstring_get_str(value)); | ||||
|             break; | ||||
|         } | ||||
|         case QTYPE_QDICT: { | ||||
|             QDict *value = qobject_to(QDict, obj); | ||||
|             QDict *value = qobject_to_qdict(obj); | ||||
|             dump_qdict(func_fprintf, f, comp_indent, value); | ||||
|             break; | ||||
|         } | ||||
|         case QTYPE_QLIST: { | ||||
|             QList *value = qobject_to(QList, obj); | ||||
|             QList *value = qobject_to_qlist(obj); | ||||
|             dump_qlist(func_fprintf, f, comp_indent, value); | ||||
|             break; | ||||
|         } | ||||
|         case QTYPE_QBOOL: { | ||||
|             QBool *value = qobject_to(QBool, obj); | ||||
|             QBool *value = qobject_to_qbool(obj); | ||||
|             func_fprintf(f, "%s", qbool_get_bool(value) ? "true" : "false"); | ||||
|             break; | ||||
|         } | ||||
| @@ -786,9 +730,9 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f, | ||||
|  | ||||
|     visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort); | ||||
|     visit_complete(v, &obj); | ||||
|     data = qdict_get(qobject_to(QDict, obj), "data"); | ||||
|     data = qdict_get(qobject_to_qdict(obj), "data"); | ||||
|     dump_qobject(func_fprintf, f, 1, data); | ||||
|     qobject_unref(obj); | ||||
|     qobject_decref(obj); | ||||
|     visit_free(v); | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										357
									
								
								block/qcow.c
									
									
									
									
									
								
							
							
						
						
									
										357
									
								
								block/qcow.c
									
									
									
									
									
								
							| @@ -26,7 +26,6 @@ | ||||
| #include "qapi/error.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "block/block_int.h" | ||||
| #include "block/qdict.h" | ||||
| #include "sysemu/block-backend.h" | ||||
| #include "qemu/module.h" | ||||
| #include "qemu/option.h" | ||||
| @@ -34,11 +33,9 @@ | ||||
| #include <zlib.h> | ||||
| #include "qapi/qmp/qdict.h" | ||||
| #include "qapi/qmp/qstring.h" | ||||
| #include "qapi/qobject-input-visitor.h" | ||||
| #include "qapi/qapi-visit-block-core.h" | ||||
| #include "crypto/block.h" | ||||
| #include "migration/blocker.h" | ||||
| #include "crypto.h" | ||||
| #include "block/crypto.h" | ||||
|  | ||||
| /**************************************************************/ | ||||
| /* QEMU COW block driver with compression and encryption support */ | ||||
| @@ -70,6 +67,7 @@ typedef struct QCowHeader { | ||||
| typedef struct BDRVQcowState { | ||||
|     int cluster_bits; | ||||
|     int cluster_size; | ||||
|     int cluster_sectors; | ||||
|     int l2_bits; | ||||
|     int l2_size; | ||||
|     unsigned int l1_size; | ||||
| @@ -88,8 +86,6 @@ typedef struct BDRVQcowState { | ||||
|     Error *migration_blocker; | ||||
| } BDRVQcowState; | ||||
|  | ||||
| static QemuOptsList qcow_create_opts; | ||||
|  | ||||
| static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); | ||||
|  | ||||
| static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||
| @@ -140,14 +136,14 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|     header.magic = be32_to_cpu(header.magic); | ||||
|     header.version = be32_to_cpu(header.version); | ||||
|     header.backing_file_offset = be64_to_cpu(header.backing_file_offset); | ||||
|     header.backing_file_size = be32_to_cpu(header.backing_file_size); | ||||
|     header.mtime = be32_to_cpu(header.mtime); | ||||
|     header.size = be64_to_cpu(header.size); | ||||
|     header.crypt_method = be32_to_cpu(header.crypt_method); | ||||
|     header.l1_table_offset = be64_to_cpu(header.l1_table_offset); | ||||
|     be32_to_cpus(&header.magic); | ||||
|     be32_to_cpus(&header.version); | ||||
|     be64_to_cpus(&header.backing_file_offset); | ||||
|     be32_to_cpus(&header.backing_file_size); | ||||
|     be32_to_cpus(&header.mtime); | ||||
|     be64_to_cpus(&header.size); | ||||
|     be32_to_cpus(&header.crypt_method); | ||||
|     be64_to_cpus(&header.l1_table_offset); | ||||
|  | ||||
|     if (header.magic != QCOW_MAGIC) { | ||||
|         error_setg(errp, "Image not in qcow format"); | ||||
| @@ -202,8 +198,9 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                 ret = -EINVAL; | ||||
|                 goto fail; | ||||
|             } | ||||
|             qdict_put_str(encryptopts, "format", "qcow"); | ||||
|             crypto_opts = block_crypto_open_opts_init(encryptopts, errp); | ||||
|             qdict_del(encryptopts, "format"); | ||||
|             crypto_opts = block_crypto_open_opts_init( | ||||
|                 Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp); | ||||
|             if (!crypto_opts) { | ||||
|                 ret = -EINVAL; | ||||
|                 goto fail; | ||||
| @@ -213,7 +210,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                 cflags |= QCRYPTO_BLOCK_OPEN_NO_IO; | ||||
|             } | ||||
|             s->crypto = qcrypto_block_open(crypto_opts, "encrypt.", | ||||
|                                            NULL, NULL, cflags, 1, errp); | ||||
|                                            NULL, NULL, cflags, errp); | ||||
|             if (!s->crypto) { | ||||
|                 ret = -EINVAL; | ||||
|                 goto fail; | ||||
| @@ -234,6 +231,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     } | ||||
|     s->cluster_bits = header.cluster_bits; | ||||
|     s->cluster_size = 1 << s->cluster_bits; | ||||
|     s->cluster_sectors = 1 << (s->cluster_bits - 9); | ||||
|     s->l2_bits = header.l2_bits; | ||||
|     s->l2_size = 1 << s->l2_bits; | ||||
|     bs->total_sectors = header.size / 512; | ||||
| @@ -270,7 +268,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     } | ||||
|  | ||||
|     for(i = 0;i < s->l1_size; i++) { | ||||
|         s->l1_table[i] = be64_to_cpu(s->l1_table[i]); | ||||
|         be64_to_cpus(&s->l1_table[i]); | ||||
|     } | ||||
|  | ||||
|     /* alloc L2 cache (max. 64k * 16 * 8 = 8 MB) */ | ||||
| @@ -313,7 +311,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     qobject_unref(encryptopts); | ||||
|     QDECREF(encryptopts); | ||||
|     qapi_free_QCryptoBlockOpenOptions(crypto_opts); | ||||
|     qemu_co_mutex_init(&s->lock); | ||||
|     return 0; | ||||
| @@ -324,7 +322,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     g_free(s->cluster_cache); | ||||
|     g_free(s->cluster_data); | ||||
|     qcrypto_block_free(s->crypto); | ||||
|     qobject_unref(encryptopts); | ||||
|     QDECREF(encryptopts); | ||||
|     qapi_free_QCryptoBlockOpenOptions(crypto_opts); | ||||
|     return ret; | ||||
| } | ||||
| @@ -343,8 +341,8 @@ static int qcow_reopen_prepare(BDRVReopenState *state, | ||||
|  * | ||||
|  * 0 to not allocate. | ||||
|  * | ||||
|  * 1 to allocate a normal cluster (for sector-aligned byte offsets 'n_start' | ||||
|  * to 'n_end' within the cluster) | ||||
|  * 1 to allocate a normal cluster (for sector indexes 'n_start' to | ||||
|  * 'n_end') | ||||
|  * | ||||
|  * 2 to allocate a compressed cluster of size | ||||
|  * 'compressed_size'. 'compressed_size' must be > 0 and < | ||||
| @@ -438,10 +436,9 @@ static int get_cluster_offset(BlockDriverState *bs, | ||||
|         if (!allocate) | ||||
|             return 0; | ||||
|         BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC); | ||||
|         assert(QEMU_IS_ALIGNED(n_start | n_end, BDRV_SECTOR_SIZE)); | ||||
|         /* allocate a new cluster */ | ||||
|         if ((cluster_offset & QCOW_OFLAG_COMPRESSED) && | ||||
|             (n_end - n_start) < s->cluster_size) { | ||||
|             (n_end - n_start) < s->cluster_sectors) { | ||||
|             /* if the cluster is already compressed, we must | ||||
|                decompress it in the case it is not completely | ||||
|                overwritten */ | ||||
| @@ -479,15 +476,16 @@ static int get_cluster_offset(BlockDriverState *bs, | ||||
|                 /* if encrypted, we must initialize the cluster | ||||
|                    content which won't be written */ | ||||
|                 if (bs->encrypted && | ||||
|                     (n_end - n_start) < s->cluster_size) { | ||||
|                     uint64_t start_offset; | ||||
|                     (n_end - n_start) < s->cluster_sectors) { | ||||
|                     uint64_t start_sect; | ||||
|                     assert(s->crypto); | ||||
|                     start_offset = offset & ~(s->cluster_size - 1); | ||||
|                     for (i = 0; i < s->cluster_size; i += BDRV_SECTOR_SIZE) { | ||||
|                     start_sect = (offset & ~(s->cluster_size - 1)) >> 9; | ||||
|                     for(i = 0; i < s->cluster_sectors; i++) { | ||||
|                         if (i < n_start || i >= n_end) { | ||||
|                             memset(s->cluster_data, 0x00, BDRV_SECTOR_SIZE); | ||||
|                             memset(s->cluster_data, 0x00, 512); | ||||
|                             if (qcrypto_block_encrypt(s->crypto, | ||||
|                                                       start_offset + i, | ||||
|                                                       (start_sect + i) * | ||||
|                                                       BDRV_SECTOR_SIZE, | ||||
|                                                       s->cluster_data, | ||||
|                                                       BDRV_SECTOR_SIZE, | ||||
|                                                       NULL) < 0) { | ||||
| @@ -495,9 +493,8 @@ static int get_cluster_offset(BlockDriverState *bs, | ||||
|                             } | ||||
|                             BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); | ||||
|                             ret = bdrv_pwrite(bs->file, | ||||
|                                               cluster_offset + i, | ||||
|                                               s->cluster_data, | ||||
|                                               BDRV_SECTOR_SIZE); | ||||
|                                               cluster_offset + i * 512, | ||||
|                                               s->cluster_data, 512); | ||||
|                             if (ret < 0) { | ||||
|                                 return ret; | ||||
|                             } | ||||
| @@ -611,21 +608,11 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void qcow_refresh_limits(BlockDriverState *bs, Error **errp) | ||||
| { | ||||
|     /* At least encrypted images require 512-byte alignment. Apply the | ||||
|      * limit universally, rather than just on encrypted images, as | ||||
|      * it's easier to let the block layer handle rounding than to | ||||
|      * audit this code further. */ | ||||
|     bs->bl.request_alignment = BDRV_SECTOR_SIZE; | ||||
| } | ||||
|  | ||||
| static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset, | ||||
|                                        uint64_t bytes, QEMUIOVector *qiov, | ||||
|                                        int flags) | ||||
| static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, | ||||
|                          int nb_sectors, QEMUIOVector *qiov) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int offset_in_cluster; | ||||
|     int index_in_cluster; | ||||
|     int ret = 0, n; | ||||
|     uint64_t cluster_offset; | ||||
|     struct iovec hd_iov; | ||||
| @@ -633,7 +620,6 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset, | ||||
|     uint8_t *buf; | ||||
|     void *orig_buf; | ||||
|  | ||||
|     assert(!flags); | ||||
|     if (qiov->niov > 1) { | ||||
|         buf = orig_buf = qemu_try_blockalign(bs, qiov->size); | ||||
|         if (buf == NULL) { | ||||
| @@ -646,35 +632,36 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset, | ||||
|  | ||||
|     qemu_co_mutex_lock(&s->lock); | ||||
|  | ||||
|     while (bytes != 0) { | ||||
|     while (nb_sectors != 0) { | ||||
|         /* prepare next request */ | ||||
|         ret = get_cluster_offset(bs, offset, 0, 0, 0, 0, &cluster_offset); | ||||
|         ret = get_cluster_offset(bs, sector_num << 9, | ||||
|                                  0, 0, 0, 0, &cluster_offset); | ||||
|         if (ret < 0) { | ||||
|             break; | ||||
|         } | ||||
|         offset_in_cluster = offset & (s->cluster_size - 1); | ||||
|         n = s->cluster_size - offset_in_cluster; | ||||
|         if (n > bytes) { | ||||
|             n = bytes; | ||||
|         index_in_cluster = sector_num & (s->cluster_sectors - 1); | ||||
|         n = s->cluster_sectors - index_in_cluster; | ||||
|         if (n > nb_sectors) { | ||||
|             n = nb_sectors; | ||||
|         } | ||||
|  | ||||
|         if (!cluster_offset) { | ||||
|             if (bs->backing) { | ||||
|                 /* read from the base image */ | ||||
|                 hd_iov.iov_base = (void *)buf; | ||||
|                 hd_iov.iov_len = n; | ||||
|                 hd_iov.iov_len = n * 512; | ||||
|                 qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); | ||||
|                 qemu_co_mutex_unlock(&s->lock); | ||||
|                 /* qcow2 emits this on bs->file instead of bs->backing */ | ||||
|                 BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); | ||||
|                 ret = bdrv_co_preadv(bs->backing, offset, n, &hd_qiov, 0); | ||||
|                 ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov); | ||||
|                 qemu_co_mutex_lock(&s->lock); | ||||
|                 if (ret < 0) { | ||||
|                     break; | ||||
|                 } | ||||
|             } else { | ||||
|                 /* Note: in this case, no need to wait */ | ||||
|                 memset(buf, 0, n); | ||||
|                 memset(buf, 0, 512 * n); | ||||
|             } | ||||
|         } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { | ||||
|             /* add AIO support for compressed blocks ? */ | ||||
| @@ -682,19 +669,21 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset, | ||||
|                 ret = -EIO; | ||||
|                 break; | ||||
|             } | ||||
|             memcpy(buf, s->cluster_cache + offset_in_cluster, n); | ||||
|             memcpy(buf, | ||||
|                    s->cluster_cache + index_in_cluster * 512, 512 * n); | ||||
|         } else { | ||||
|             if ((cluster_offset & 511) != 0) { | ||||
|                 ret = -EIO; | ||||
|                 break; | ||||
|             } | ||||
|             hd_iov.iov_base = (void *)buf; | ||||
|             hd_iov.iov_len = n; | ||||
|             hd_iov.iov_len = n * 512; | ||||
|             qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); | ||||
|             qemu_co_mutex_unlock(&s->lock); | ||||
|             BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); | ||||
|             ret = bdrv_co_preadv(bs->file, cluster_offset + offset_in_cluster, | ||||
|                                  n, &hd_qiov, 0); | ||||
|             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; | ||||
| @@ -702,7 +691,8 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset, | ||||
|             if (bs->encrypted) { | ||||
|                 assert(s->crypto); | ||||
|                 if (qcrypto_block_decrypt(s->crypto, | ||||
|                                           offset, buf, n, NULL) < 0) { | ||||
|                                           sector_num * BDRV_SECTOR_SIZE, buf, | ||||
|                                           n * BDRV_SECTOR_SIZE, NULL) < 0) { | ||||
|                     ret = -EIO; | ||||
|                     break; | ||||
|                 } | ||||
| @@ -710,9 +700,9 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset, | ||||
|         } | ||||
|         ret = 0; | ||||
|  | ||||
|         bytes -= n; | ||||
|         offset += n; | ||||
|         buf += n; | ||||
|         nb_sectors -= n; | ||||
|         sector_num += n; | ||||
|         buf += n * 512; | ||||
|     } | ||||
|  | ||||
|     qemu_co_mutex_unlock(&s->lock); | ||||
| @@ -725,12 +715,11 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset, | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset, | ||||
|                                         uint64_t bytes, QEMUIOVector *qiov, | ||||
|                                         int flags) | ||||
| static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, | ||||
|                           int nb_sectors, QEMUIOVector *qiov) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int offset_in_cluster; | ||||
|     int index_in_cluster; | ||||
|     uint64_t cluster_offset; | ||||
|     int ret = 0, n; | ||||
|     struct iovec hd_iov; | ||||
| @@ -738,7 +727,6 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset, | ||||
|     uint8_t *buf; | ||||
|     void *orig_buf; | ||||
|  | ||||
|     assert(!flags); | ||||
|     s->cluster_cache_offset = -1; /* disable compressed cache */ | ||||
|  | ||||
|     /* We must always copy the iov when encrypting, so we | ||||
| @@ -756,14 +744,16 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset, | ||||
|  | ||||
|     qemu_co_mutex_lock(&s->lock); | ||||
|  | ||||
|     while (bytes != 0) { | ||||
|         offset_in_cluster = offset & (s->cluster_size - 1); | ||||
|         n = s->cluster_size - offset_in_cluster; | ||||
|         if (n > bytes) { | ||||
|             n = bytes; | ||||
|     while (nb_sectors != 0) { | ||||
|  | ||||
|         index_in_cluster = sector_num & (s->cluster_sectors - 1); | ||||
|         n = s->cluster_sectors - index_in_cluster; | ||||
|         if (n > nb_sectors) { | ||||
|             n = nb_sectors; | ||||
|         } | ||||
|         ret = get_cluster_offset(bs, offset, 1, 0, offset_in_cluster, | ||||
|                                  offset_in_cluster + n, &cluster_offset); | ||||
|         ret = get_cluster_offset(bs, sector_num << 9, 1, 0, | ||||
|                                  index_in_cluster, | ||||
|                                  index_in_cluster + n, &cluster_offset); | ||||
|         if (ret < 0) { | ||||
|             break; | ||||
|         } | ||||
| @@ -773,28 +763,30 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset, | ||||
|         } | ||||
|         if (bs->encrypted) { | ||||
|             assert(s->crypto); | ||||
|             if (qcrypto_block_encrypt(s->crypto, offset, buf, n, NULL) < 0) { | ||||
|             if (qcrypto_block_encrypt(s->crypto, sector_num * BDRV_SECTOR_SIZE, | ||||
|                                       buf, n * BDRV_SECTOR_SIZE, NULL) < 0) { | ||||
|                 ret = -EIO; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         hd_iov.iov_base = (void *)buf; | ||||
|         hd_iov.iov_len = n; | ||||
|         hd_iov.iov_len = n * 512; | ||||
|         qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); | ||||
|         qemu_co_mutex_unlock(&s->lock); | ||||
|         BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); | ||||
|         ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster, | ||||
|                               n, &hd_qiov, 0); | ||||
|         ret = bdrv_co_writev(bs->file, | ||||
|                              (cluster_offset >> 9) + index_in_cluster, | ||||
|                              n, &hd_qiov); | ||||
|         qemu_co_mutex_lock(&s->lock); | ||||
|         if (ret < 0) { | ||||
|             break; | ||||
|         } | ||||
|         ret = 0; | ||||
|  | ||||
|         bytes -= n; | ||||
|         offset += n; | ||||
|         buf += n; | ||||
|         nb_sectors -= n; | ||||
|         sector_num += n; | ||||
|         buf += n * 512; | ||||
|     } | ||||
|     qemu_co_mutex_unlock(&s->lock); | ||||
|  | ||||
| @@ -818,50 +810,62 @@ static void qcow_close(BlockDriverState *bs) | ||||
|     error_free(s->migration_blocker); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, | ||||
|                                        Error **errp) | ||||
| static int coroutine_fn qcow_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|                                             Error **errp) | ||||
| { | ||||
|     BlockdevCreateOptionsQcow *qcow_opts; | ||||
|     int header_size, backing_filename_len, l1_size, shift, i; | ||||
|     QCowHeader header; | ||||
|     uint8_t *tmp; | ||||
|     int64_t total_size = 0; | ||||
|     char *backing_file = NULL; | ||||
|     Error *local_err = NULL; | ||||
|     int ret; | ||||
|     BlockDriverState *bs; | ||||
|     BlockBackend *qcow_blk; | ||||
|     char *encryptfmt = NULL; | ||||
|     QDict *options; | ||||
|     QDict *encryptopts = NULL; | ||||
|     QCryptoBlockCreateOptions *crypto_opts = NULL; | ||||
|     QCryptoBlock *crypto = NULL; | ||||
|  | ||||
|     assert(opts->driver == BLOCKDEV_DRIVER_QCOW); | ||||
|     qcow_opts = &opts->u.qcow; | ||||
|  | ||||
|     /* Sanity checks */ | ||||
|     total_size = qcow_opts->size; | ||||
|     /* Read out options */ | ||||
|     total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), | ||||
|                           BDRV_SECTOR_SIZE); | ||||
|     if (total_size == 0) { | ||||
|         error_setg(errp, "Image size is too small, cannot be zero length"); | ||||
|         return -EINVAL; | ||||
|         ret = -EINVAL; | ||||
|         goto cleanup; | ||||
|     } | ||||
|  | ||||
|     if (qcow_opts->has_encrypt && | ||||
|         qcow_opts->encrypt->format != Q_CRYPTO_BLOCK_FORMAT_QCOW) | ||||
|     { | ||||
|         error_setg(errp, "Unsupported encryption format"); | ||||
|         return -EINVAL; | ||||
|     backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); | ||||
|     encryptfmt = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT); | ||||
|     if (encryptfmt) { | ||||
|         if (qemu_opt_get(opts, BLOCK_OPT_ENCRYPT)) { | ||||
|             error_setg(errp, "Options " BLOCK_OPT_ENCRYPT " and " | ||||
|                        BLOCK_OPT_ENCRYPT_FORMAT " are mutually exclusive"); | ||||
|             ret = -EINVAL; | ||||
|             goto cleanup; | ||||
|         } | ||||
|     } else if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ENCRYPT, false)) { | ||||
|         encryptfmt = g_strdup("aes"); | ||||
|     } | ||||
|  | ||||
|     /* Create BlockBackend to write to the image */ | ||||
|     bs = bdrv_open_blockdev_ref(qcow_opts->file, errp); | ||||
|     if (bs == NULL) { | ||||
|         return -EIO; | ||||
|     } | ||||
|  | ||||
|     qcow_blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); | ||||
|     ret = blk_insert_bs(qcow_blk, bs, errp); | ||||
|     ret = bdrv_create_file(filename, opts, &local_err); | ||||
|     if (ret < 0) { | ||||
|         goto exit; | ||||
|         error_propagate(errp, local_err); | ||||
|         goto cleanup; | ||||
|     } | ||||
|  | ||||
|     qcow_blk = blk_new_open(filename, NULL, NULL, | ||||
|                             BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, | ||||
|                             &local_err); | ||||
|     if (qcow_blk == NULL) { | ||||
|         error_propagate(errp, local_err); | ||||
|         ret = -EIO; | ||||
|         goto cleanup; | ||||
|     } | ||||
|  | ||||
|     blk_set_allow_write_beyond_eof(qcow_blk, true); | ||||
|  | ||||
|     /* Create image format */ | ||||
|     ret = blk_truncate(qcow_blk, 0, PREALLOC_MODE_OFF, errp); | ||||
|     if (ret < 0) { | ||||
|         goto exit; | ||||
| @@ -873,15 +877,16 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, | ||||
|     header.size = cpu_to_be64(total_size); | ||||
|     header_size = sizeof(header); | ||||
|     backing_filename_len = 0; | ||||
|     if (qcow_opts->has_backing_file) { | ||||
|         if (strcmp(qcow_opts->backing_file, "fat:")) { | ||||
|     if (backing_file) { | ||||
|         if (strcmp(backing_file, "fat:")) { | ||||
|             header.backing_file_offset = cpu_to_be64(header_size); | ||||
|             backing_filename_len = strlen(qcow_opts->backing_file); | ||||
|             backing_filename_len = strlen(backing_file); | ||||
|             header.backing_file_size = cpu_to_be32(backing_filename_len); | ||||
|             header_size += backing_filename_len; | ||||
|         } else { | ||||
|             /* special backing file for vvfat */ | ||||
|             qcow_opts->has_backing_file = false; | ||||
|             g_free(backing_file); | ||||
|             backing_file = NULL; | ||||
|         } | ||||
|         header.cluster_bits = 9; /* 512 byte cluster to avoid copying | ||||
|                                     unmodified sectors */ | ||||
| @@ -896,10 +901,26 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, | ||||
|  | ||||
|     header.l1_table_offset = cpu_to_be64(header_size); | ||||
|  | ||||
|     if (qcow_opts->has_encrypt) { | ||||
|     options = qemu_opts_to_qdict(opts, NULL); | ||||
|     qdict_extract_subqdict(options, &encryptopts, "encrypt."); | ||||
|     QDECREF(options); | ||||
|     if (encryptfmt) { | ||||
|         if (!g_str_equal(encryptfmt, "aes")) { | ||||
|             error_setg(errp, "Unknown encryption format '%s', expected 'aes'", | ||||
|                        encryptfmt); | ||||
|             ret = -EINVAL; | ||||
|             goto exit; | ||||
|         } | ||||
|         header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES); | ||||
|  | ||||
|         crypto = qcrypto_block_create(qcow_opts->encrypt, "encrypt.", | ||||
|         crypto_opts = block_crypto_create_opts_init( | ||||
|             Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp); | ||||
|         if (!crypto_opts) { | ||||
|             ret = -EINVAL; | ||||
|             goto exit; | ||||
|         } | ||||
|  | ||||
|         crypto = qcrypto_block_create(crypto_opts, "encrypt.", | ||||
|                                       NULL, NULL, NULL, errp); | ||||
|         if (!crypto) { | ||||
|             ret = -EINVAL; | ||||
| @@ -915,9 +936,9 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, | ||||
|         goto exit; | ||||
|     } | ||||
|  | ||||
|     if (qcow_opts->has_backing_file) { | ||||
|     if (backing_file) { | ||||
|         ret = blk_pwrite(qcow_blk, sizeof(header), | ||||
|                          qcow_opts->backing_file, backing_filename_len, 0); | ||||
|                          backing_file, backing_filename_len, 0); | ||||
|         if (ret != backing_filename_len) { | ||||
|             goto exit; | ||||
|         } | ||||
| @@ -938,97 +959,12 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, | ||||
|     ret = 0; | ||||
| exit: | ||||
|     blk_unref(qcow_blk); | ||||
|     bdrv_unref(bs); | ||||
| cleanup: | ||||
|     QDECREF(encryptopts); | ||||
|     g_free(encryptfmt); | ||||
|     qcrypto_block_free(crypto); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn qcow_co_create_opts(const char *filename, | ||||
|                                             QemuOpts *opts, Error **errp) | ||||
| { | ||||
|     BlockdevCreateOptions *create_options = NULL; | ||||
|     BlockDriverState *bs = NULL; | ||||
|     QDict *qdict; | ||||
|     Visitor *v; | ||||
|     const char *val; | ||||
|     Error *local_err = NULL; | ||||
|     int ret; | ||||
|  | ||||
|     static const QDictRenames opt_renames[] = { | ||||
|         { BLOCK_OPT_BACKING_FILE,       "backing-file" }, | ||||
|         { BLOCK_OPT_ENCRYPT,            BLOCK_OPT_ENCRYPT_FORMAT }, | ||||
|         { NULL, NULL }, | ||||
|     }; | ||||
|  | ||||
|     /* Parse options and convert legacy syntax */ | ||||
|     qdict = qemu_opts_to_qdict_filtered(opts, NULL, &qcow_create_opts, true); | ||||
|  | ||||
|     val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT); | ||||
|     if (val && !strcmp(val, "on")) { | ||||
|         qdict_put_str(qdict, BLOCK_OPT_ENCRYPT, "qcow"); | ||||
|     } else if (val && !strcmp(val, "off")) { | ||||
|         qdict_del(qdict, BLOCK_OPT_ENCRYPT); | ||||
|     } | ||||
|  | ||||
|     val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT); | ||||
|     if (val && !strcmp(val, "aes")) { | ||||
|         qdict_put_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT, "qcow"); | ||||
|     } | ||||
|  | ||||
|     if (!qdict_rename_keys(qdict, opt_renames, errp)) { | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     /* Create and open the file (protocol layer) */ | ||||
|     ret = bdrv_create_file(filename, opts, &local_err); | ||||
|     if (ret < 0) { | ||||
|         error_propagate(errp, local_err); | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     bs = bdrv_open(filename, NULL, NULL, | ||||
|                    BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); | ||||
|     if (bs == NULL) { | ||||
|         ret = -EIO; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     /* Now get the QAPI type BlockdevCreateOptions */ | ||||
|     qdict_put_str(qdict, "driver", "qcow"); | ||||
|     qdict_put_str(qdict, "file", bs->node_name); | ||||
|  | ||||
|     v = qobject_input_visitor_new_flat_confused(qdict, errp); | ||||
|     if (!v) { | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); | ||||
|     visit_free(v); | ||||
|  | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     /* Silently round up size */ | ||||
|     assert(create_options->driver == BLOCKDEV_DRIVER_QCOW); | ||||
|     create_options->u.qcow.size = | ||||
|         ROUND_UP(create_options->u.qcow.size, BDRV_SECTOR_SIZE); | ||||
|  | ||||
|     /* Create the qcow image (format layer) */ | ||||
|     ret = qcow_co_create(create_options, errp); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     ret = 0; | ||||
| fail: | ||||
|     qobject_unref(qdict); | ||||
|     bdrv_unref(bs); | ||||
|     qapi_free_BlockdevCreateOptions(create_options); | ||||
|     qapi_free_QCryptoBlockCreateOptions(crypto_opts); | ||||
|     g_free(backing_file); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| @@ -1110,7 +1046,8 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, | ||||
|  | ||||
|     if (ret != Z_STREAM_END || out_len >= s->cluster_size) { | ||||
|         /* could not compress: write normal cluster */ | ||||
|         ret = qcow_co_pwritev(bs, offset, bytes, qiov, 0); | ||||
|         ret = qcow_co_writev(bs, offset >> BDRV_SECTOR_BITS, | ||||
|                              bytes >> BDRV_SECTOR_BITS, qiov); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
|         } | ||||
| @@ -1191,14 +1128,12 @@ static BlockDriver bdrv_qcow = { | ||||
|     .bdrv_close		= qcow_close, | ||||
|     .bdrv_child_perm        = bdrv_format_default_perms, | ||||
|     .bdrv_reopen_prepare    = qcow_reopen_prepare, | ||||
|     .bdrv_co_create         = qcow_co_create, | ||||
|     .bdrv_co_create_opts    = qcow_co_create_opts, | ||||
|     .bdrv_has_zero_init     = bdrv_has_zero_init_1, | ||||
|     .supports_backing       = true, | ||||
|     .bdrv_refresh_limits    = qcow_refresh_limits, | ||||
|  | ||||
|     .bdrv_co_preadv         = qcow_co_preadv, | ||||
|     .bdrv_co_pwritev        = qcow_co_pwritev, | ||||
|     .bdrv_co_readv          = qcow_co_readv, | ||||
|     .bdrv_co_writev         = qcow_co_writev, | ||||
|     .bdrv_co_block_status   = qcow_co_block_status, | ||||
|  | ||||
|     .bdrv_make_empty        = qcow_make_empty, | ||||
|   | ||||
| @@ -30,7 +30,7 @@ | ||||
| #include "qemu/cutils.h" | ||||
|  | ||||
| #include "block/block_int.h" | ||||
| #include "qcow2.h" | ||||
| #include "block/qcow2.h" | ||||
|  | ||||
| /* NOTICE: BME here means Bitmaps Extension and used as a namespace for | ||||
|  * _internal_ constants. Please do not use this _internal_ abbreviation for | ||||
| @@ -77,6 +77,8 @@ typedef struct Qcow2BitmapTable { | ||||
|     uint32_t size; /* number of 64bit entries */ | ||||
|     QSIMPLEQ_ENTRY(Qcow2BitmapTable) entry; | ||||
| } Qcow2BitmapTable; | ||||
| typedef QSIMPLEQ_HEAD(Qcow2BitmapTableList, Qcow2BitmapTable) | ||||
|     Qcow2BitmapTableList; | ||||
|  | ||||
| typedef struct Qcow2Bitmap { | ||||
|     Qcow2BitmapTable table; | ||||
| @@ -116,7 +118,7 @@ static inline void bitmap_table_to_be(uint64_t *bitmap_table, size_t size) | ||||
|     size_t i; | ||||
|  | ||||
|     for (i = 0; i < size; ++i) { | ||||
|         bitmap_table[i] = cpu_to_be64(bitmap_table[i]); | ||||
|         cpu_to_be64s(&bitmap_table[i]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -229,7 +231,7 @@ static int bitmap_table_load(BlockDriverState *bs, Qcow2BitmapTable *tb, | ||||
|     } | ||||
|  | ||||
|     for (i = 0; i < tb->size; ++i) { | ||||
|         table[i] = be64_to_cpu(table[i]); | ||||
|         be64_to_cpus(&table[i]); | ||||
|         ret = check_table_entry(table[i], s->cluster_size); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
| @@ -252,6 +254,7 @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb) | ||||
|  | ||||
|     ret = bitmap_table_load(bs, tb, &bitmap_table); | ||||
|     if (ret < 0) { | ||||
|         assert(bitmap_table == NULL); | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
| @@ -392,20 +395,20 @@ fail: | ||||
|  | ||||
| static inline void bitmap_dir_entry_to_cpu(Qcow2BitmapDirEntry *entry) | ||||
| { | ||||
|     entry->bitmap_table_offset = be64_to_cpu(entry->bitmap_table_offset); | ||||
|     entry->bitmap_table_size = be32_to_cpu(entry->bitmap_table_size); | ||||
|     entry->flags = be32_to_cpu(entry->flags); | ||||
|     entry->name_size = be16_to_cpu(entry->name_size); | ||||
|     entry->extra_data_size = be32_to_cpu(entry->extra_data_size); | ||||
|     be64_to_cpus(&entry->bitmap_table_offset); | ||||
|     be32_to_cpus(&entry->bitmap_table_size); | ||||
|     be32_to_cpus(&entry->flags); | ||||
|     be16_to_cpus(&entry->name_size); | ||||
|     be32_to_cpus(&entry->extra_data_size); | ||||
| } | ||||
|  | ||||
| static inline void bitmap_dir_entry_to_be(Qcow2BitmapDirEntry *entry) | ||||
| { | ||||
|     entry->bitmap_table_offset = cpu_to_be64(entry->bitmap_table_offset); | ||||
|     entry->bitmap_table_size = cpu_to_be32(entry->bitmap_table_size); | ||||
|     entry->flags = cpu_to_be32(entry->flags); | ||||
|     entry->name_size = cpu_to_be16(entry->name_size); | ||||
|     entry->extra_data_size = cpu_to_be32(entry->extra_data_size); | ||||
|     cpu_to_be64s(&entry->bitmap_table_offset); | ||||
|     cpu_to_be32s(&entry->bitmap_table_size); | ||||
|     cpu_to_be32s(&entry->flags); | ||||
|     cpu_to_be16s(&entry->name_size); | ||||
|     cpu_to_be32s(&entry->extra_data_size); | ||||
| } | ||||
|  | ||||
| static inline int calc_dir_entry_size(size_t name_size, size_t extra_data_size) | ||||
| @@ -773,12 +776,7 @@ static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* Actually, even in in-place case ignoring QCOW2_OL_BITMAP_DIRECTORY is not | ||||
|      * necessary, because we drop QCOW2_AUTOCLEAR_BITMAPS when updating bitmap | ||||
|      * directory in-place (actually, turn-off the extension), which is checked | ||||
|      * in qcow2_check_metadata_overlap() */ | ||||
|     ret = qcow2_pre_write_overlap_check( | ||||
|             bs, in_place ? QCOW2_OL_BITMAP_DIRECTORY : 0, dir_offset, dir_size); | ||||
|     ret = qcow2_pre_write_overlap_check(bs, 0, dir_offset, dir_size); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
| @@ -1006,8 +1004,7 @@ fail: | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated, | ||||
|                                  Error **errp) | ||||
| int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     Qcow2BitmapList *bm_list; | ||||
| @@ -1015,10 +1012,6 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated, | ||||
|     GSList *ro_dirty_bitmaps = NULL; | ||||
|     int ret = 0; | ||||
|  | ||||
|     if (header_updated != NULL) { | ||||
|         *header_updated = false; | ||||
|     } | ||||
|  | ||||
|     if (s->nb_bitmaps == 0) { | ||||
|         /* No bitmaps - nothing to do */ | ||||
|         return 0; | ||||
| @@ -1062,9 +1055,6 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated, | ||||
|             error_setg_errno(errp, -ret, "Can't update bitmap directory"); | ||||
|             goto out; | ||||
|         } | ||||
|         if (header_updated != NULL) { | ||||
|             *header_updated = true; | ||||
|         } | ||||
|         g_slist_foreach(ro_dirty_bitmaps, set_readonly_helper, false); | ||||
|     } | ||||
|  | ||||
| @@ -1075,11 +1065,6 @@ out: | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp) | ||||
| { | ||||
|     return qcow2_reopen_bitmaps_rw_hint(bs, NULL, errp); | ||||
| } | ||||
|  | ||||
| /* store_bitmap_data() | ||||
|  * Store bitmap to image, filling bitmap table accordingly. | ||||
|  */ | ||||
| @@ -1314,7 +1299,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp) | ||||
|     int ret; | ||||
|     Qcow2BitmapList *bm_list; | ||||
|     Qcow2Bitmap *bm; | ||||
|     QSIMPLEQ_HEAD(, Qcow2BitmapTable) drop_tables; | ||||
|     Qcow2BitmapTableList drop_tables; | ||||
|     Qcow2BitmapTable *tb, *tb_next; | ||||
|  | ||||
|     if (!bdrv_has_changed_persistent_bitmaps(bs)) { | ||||
| @@ -1416,22 +1401,6 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp) | ||||
|         g_free(tb); | ||||
|     } | ||||
|  | ||||
|     QSIMPLEQ_FOREACH(bm, bm_list, entry) { | ||||
|         /* For safety, we remove bitmap after storing. | ||||
|          * We may be here in two cases: | ||||
|          * 1. bdrv_close. It's ok to drop bitmap. | ||||
|          * 2. inactivation. It means migration without 'dirty-bitmaps' | ||||
|          *    capability, so bitmaps are not marked with | ||||
|          *    BdrvDirtyBitmap.migration flags. It's not bad to drop them too, | ||||
|          *    and reload on invalidation. | ||||
|          */ | ||||
|         if (bm->dirty_bitmap == NULL) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         bdrv_release_dirty_bitmap(bs, bm->dirty_bitmap); | ||||
|     } | ||||
|  | ||||
|     bitmap_list_free(bm_list); | ||||
|     return; | ||||
|  | ||||
|   | ||||
| @@ -28,7 +28,7 @@ | ||||
| #include "qapi/error.h" | ||||
| #include "qemu-common.h" | ||||
| #include "block/block_int.h" | ||||
| #include "qcow2.h" | ||||
| #include "block/qcow2.h" | ||||
| #include "qemu/bswap.h" | ||||
| #include "trace.h" | ||||
|  | ||||
| @@ -402,7 +402,7 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|         return i; | ||||
| 	return i; | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -994,17 +994,6 @@ err: | ||||
|     return ret; | ||||
|  } | ||||
|  | ||||
| /** | ||||
|  * Frees the allocated clusters because the request failed and they won't | ||||
|  * actually be linked. | ||||
|  */ | ||||
| void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     qcow2_free_clusters(bs, m->alloc_offset, m->nb_clusters << s->cluster_bits, | ||||
|                         QCOW2_DISCARD_NEVER); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Returns the number of contiguous clusters that can be used for an allocating | ||||
|  * write, but require COW to be performed (this includes yet unallocated space, | ||||
| @@ -1571,6 +1560,76 @@ again: | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int decompress_buffer(uint8_t *out_buf, int out_buf_size, | ||||
|                              const uint8_t *buf, int buf_size) | ||||
| { | ||||
|     z_stream strm1, *strm = &strm1; | ||||
|     int ret, out_len; | ||||
|  | ||||
|     memset(strm, 0, sizeof(*strm)); | ||||
|  | ||||
|     strm->next_in = (uint8_t *)buf; | ||||
|     strm->avail_in = buf_size; | ||||
|     strm->next_out = out_buf; | ||||
|     strm->avail_out = out_buf_size; | ||||
|  | ||||
|     ret = inflateInit2(strm, -12); | ||||
|     if (ret != Z_OK) | ||||
|         return -1; | ||||
|     ret = inflate(strm, Z_FINISH); | ||||
|     out_len = strm->next_out - out_buf; | ||||
|     if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) || | ||||
|         out_len != out_buf_size) { | ||||
|         inflateEnd(strm); | ||||
|         return -1; | ||||
|     } | ||||
|     inflateEnd(strm); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     int ret, csize, nb_csectors, sector_offset; | ||||
|     uint64_t coffset; | ||||
|  | ||||
|     coffset = cluster_offset & s->cluster_offset_mask; | ||||
|     if (s->cluster_cache_offset != coffset) { | ||||
|         nb_csectors = ((cluster_offset >> s->csize_shift) & s->csize_mask) + 1; | ||||
|         sector_offset = coffset & 511; | ||||
|         csize = nb_csectors * 512 - sector_offset; | ||||
|  | ||||
|         /* Allocate buffers on first decompress operation, most images are | ||||
|          * uncompressed and the memory overhead can be avoided.  The buffers | ||||
|          * are freed in .bdrv_close(). | ||||
|          */ | ||||
|         if (!s->cluster_data) { | ||||
|             /* one more sector for decompressed data alignment */ | ||||
|             s->cluster_data = qemu_try_blockalign(bs->file->bs, | ||||
|                     QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size + 512); | ||||
|             if (!s->cluster_data) { | ||||
|                 return -ENOMEM; | ||||
|             } | ||||
|         } | ||||
|         if (!s->cluster_cache) { | ||||
|             s->cluster_cache = g_malloc(s->cluster_size); | ||||
|         } | ||||
|  | ||||
|         BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED); | ||||
|         ret = bdrv_read(bs->file, coffset >> 9, s->cluster_data, | ||||
|                         nb_csectors); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
|         if (decompress_buffer(s->cluster_cache, s->cluster_size, | ||||
|                               s->cluster_data + sector_offset, csize) < 0) { | ||||
|             return -EIO; | ||||
|         } | ||||
|         s->cluster_cache_offset = coffset; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * This discards as many clusters of nb_clusters as possible at once (i.e. | ||||
|  * all clusters in the same L2 slice) and returns the number of discarded | ||||
|   | ||||
| @@ -26,13 +26,12 @@ | ||||
| #include "qapi/error.h" | ||||
| #include "qemu-common.h" | ||||
| #include "block/block_int.h" | ||||
| #include "qcow2.h" | ||||
| #include "block/qcow2.h" | ||||
| #include "qemu/range.h" | ||||
| #include "qemu/bswap.h" | ||||
| #include "qemu/cutils.h" | ||||
|  | ||||
| static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size, | ||||
|                                     uint64_t max); | ||||
| static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size); | ||||
| static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, | ||||
|                             int64_t offset, int64_t length, uint64_t addend, | ||||
|                             bool decrease, enum qcow2_discard_type type); | ||||
| @@ -363,14 +362,11 @@ static int alloc_refcount_block(BlockDriverState *bs, | ||||
|     } | ||||
|  | ||||
|     /* Allocate the refcount block itself and mark it as used */ | ||||
|     int64_t new_block = alloc_clusters_noref(bs, s->cluster_size, INT64_MAX); | ||||
|     int64_t new_block = alloc_clusters_noref(bs, s->cluster_size); | ||||
|     if (new_block < 0) { | ||||
|         return new_block; | ||||
|     } | ||||
|  | ||||
|     /* The offset must fit in the offset field of the refcount table entry */ | ||||
|     assert((new_block & REFT_OFFSET_MASK) == new_block); | ||||
|  | ||||
|     /* If we're allocating the block at offset 0 then something is wrong */ | ||||
|     if (new_block == 0) { | ||||
|         qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid " | ||||
| @@ -738,7 +734,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret) | ||||
|  | ||||
|         /* Discard is optional, ignore the return value */ | ||||
|         if (ret >= 0) { | ||||
|             bdrv_pdiscard(bs->file, d->offset, d->bytes); | ||||
|             bdrv_pdiscard(bs->file->bs, d->offset, d->bytes); | ||||
|         } | ||||
|  | ||||
|         g_free(d); | ||||
| @@ -843,13 +839,6 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, | ||||
|                 qcow2_cache_put(s->refcount_block_cache, &refcount_block); | ||||
|             } | ||||
|             ret = alloc_refcount_block(bs, cluster_index, &refcount_block); | ||||
|             /* If the caller needs to restart the search for free clusters, | ||||
|              * try the same ones first to see if they're still free. */ | ||||
|             if (ret == -EAGAIN) { | ||||
|                 if (s->free_cluster_index > (start >> s->cluster_bits)) { | ||||
|                     s->free_cluster_index = (start >> s->cluster_bits); | ||||
|                 } | ||||
|             } | ||||
|             if (ret < 0) { | ||||
|                 goto fail; | ||||
|             } | ||||
| @@ -958,8 +947,7 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, | ||||
|  | ||||
|  | ||||
| /* return < 0 if error */ | ||||
| static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size, | ||||
|                                     uint64_t max) | ||||
| static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size) | ||||
| { | ||||
|     BDRVQcow2State *s = bs->opaque; | ||||
|     uint64_t i, nb_clusters, refcount; | ||||
| @@ -984,9 +972,9 @@ retry: | ||||
|     } | ||||
|  | ||||
|     /* Make sure that all offsets in the "allocated" range are representable | ||||
|      * in the requested max */ | ||||
|      * in an int64_t */ | ||||
|     if (s->free_cluster_index > 0 && | ||||
|         s->free_cluster_index - 1 > (max >> s->cluster_bits)) | ||||
|         s->free_cluster_index - 1 > (INT64_MAX >> s->cluster_bits)) | ||||
|     { | ||||
|         return -EFBIG; | ||||
|     } | ||||
| @@ -1006,7 +994,7 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size) | ||||
|  | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC); | ||||
|     do { | ||||
|         offset = alloc_clusters_noref(bs, size, QCOW_MAX_CLUSTER_OFFSET); | ||||
|         offset = alloc_clusters_noref(bs, size); | ||||
|         if (offset < 0) { | ||||
|             return offset; | ||||
|         } | ||||
| @@ -1088,11 +1076,7 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) | ||||
|     free_in_cluster = s->cluster_size - offset_into_cluster(s, offset); | ||||
|     do { | ||||
|         if (!offset || free_in_cluster < size) { | ||||
|             int64_t new_cluster; | ||||
|  | ||||
|             new_cluster = alloc_clusters_noref(bs, s->cluster_size, | ||||
|                                                MIN(s->cluster_offset_mask, | ||||
|                                                    QCOW_MAX_CLUSTER_OFFSET)); | ||||
|             int64_t new_cluster = alloc_clusters_noref(bs, s->cluster_size); | ||||
|             if (new_cluster < 0) { | ||||
|                 return new_cluster; | ||||
|             } | ||||
| @@ -1586,9 +1570,9 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, | ||||
|         case QCOW2_CLUSTER_COMPRESSED: | ||||
|             /* Compressed clusters don't have QCOW_OFLAG_COPIED */ | ||||
|             if (l2_entry & QCOW_OFLAG_COPIED) { | ||||
|                 fprintf(stderr, "ERROR: coffset=0x%" PRIx64 ": " | ||||
|                 fprintf(stderr, "ERROR: cluster %" PRId64 ": " | ||||
|                     "copied flag must never be set for compressed " | ||||
|                     "clusters\n", l2_entry & s->cluster_offset_mask); | ||||
|                     "clusters\n", l2_entry >> s->cluster_bits); | ||||
|                 l2_entry &= ~QCOW_OFLAG_COPIED; | ||||
|                 res->corruptions++; | ||||
|             } | ||||
| @@ -1808,19 +1792,6 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, | ||||
|     int ret; | ||||
|     uint64_t refcount; | ||||
|     int i, j; | ||||
|     bool repair; | ||||
|  | ||||
|     if (fix & BDRV_FIX_ERRORS) { | ||||
|         /* Always repair */ | ||||
|         repair = true; | ||||
|     } else if (fix & BDRV_FIX_LEAKS) { | ||||
|         /* Repair only if that seems safe: This function is always | ||||
|          * called after the refcounts have been fixed, so the refcount | ||||
|          * is accurate if that repair was successful */ | ||||
|         repair = !res->check_errors && !res->corruptions && !res->leaks; | ||||
|     } else { | ||||
|         repair = false; | ||||
|     } | ||||
|  | ||||
|     for (i = 0; i < s->l1_size; i++) { | ||||
|         uint64_t l1_entry = s->l1_table[i]; | ||||
| @@ -1840,8 +1811,10 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, | ||||
|         if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) { | ||||
|             fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d " | ||||
|                     "l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n", | ||||
|                     repair ? "Repairing" : "ERROR", i, l1_entry, refcount); | ||||
|             if (repair) { | ||||
|                     fix & BDRV_FIX_ERRORS ? "Repairing" : | ||||
|                                             "ERROR", | ||||
|                     i, l1_entry, refcount); | ||||
|             if (fix & BDRV_FIX_ERRORS) { | ||||
|                 s->l1_table[i] = refcount == 1 | ||||
|                                ? l1_entry |  QCOW_OFLAG_COPIED | ||||
|                                : l1_entry & ~QCOW_OFLAG_COPIED; | ||||
| @@ -1882,8 +1855,10 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, | ||||
|                 if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { | ||||
|                     fprintf(stderr, "%s OFLAG_COPIED data cluster: " | ||||
|                             "l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n", | ||||
|                             repair ? "Repairing" : "ERROR", l2_entry, refcount); | ||||
|                     if (repair) { | ||||
|                             fix & BDRV_FIX_ERRORS ? "Repairing" : | ||||
|                                                     "ERROR", | ||||
|                             l2_entry, refcount); | ||||
|                     if (fix & BDRV_FIX_ERRORS) { | ||||
|                         l2_table[j] = cpu_to_be64(refcount == 1 | ||||
|                                     ? l2_entry |  QCOW_OFLAG_COPIED | ||||
|                                     : l2_entry & ~QCOW_OFLAG_COPIED); | ||||
| @@ -2714,31 +2689,19 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if ((chk & QCOW2_OL_BITMAP_DIRECTORY) && | ||||
|         (s->autoclear_features & QCOW2_AUTOCLEAR_BITMAPS)) | ||||
|     { | ||||
|         if (overlaps_with(s->bitmap_directory_offset, | ||||
|                           s->bitmap_directory_size)) | ||||
|         { | ||||
|             return QCOW2_OL_BITMAP_DIRECTORY; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static const char *metadata_ol_names[] = { | ||||
|     [QCOW2_OL_MAIN_HEADER_BITNR]        = "qcow2_header", | ||||
|     [QCOW2_OL_ACTIVE_L1_BITNR]          = "active L1 table", | ||||
|     [QCOW2_OL_ACTIVE_L2_BITNR]          = "active L2 table", | ||||
|     [QCOW2_OL_REFCOUNT_TABLE_BITNR]     = "refcount table", | ||||
|     [QCOW2_OL_REFCOUNT_BLOCK_BITNR]     = "refcount block", | ||||
|     [QCOW2_OL_SNAPSHOT_TABLE_BITNR]     = "snapshot table", | ||||
|     [QCOW2_OL_INACTIVE_L1_BITNR]        = "inactive L1 table", | ||||
|     [QCOW2_OL_INACTIVE_L2_BITNR]        = "inactive L2 table", | ||||
|     [QCOW2_OL_BITMAP_DIRECTORY_BITNR]   = "bitmap directory", | ||||
|     [QCOW2_OL_MAIN_HEADER_BITNR]    = "qcow2_header", | ||||
|     [QCOW2_OL_ACTIVE_L1_BITNR]      = "active L1 table", | ||||
|     [QCOW2_OL_ACTIVE_L2_BITNR]      = "active L2 table", | ||||
|     [QCOW2_OL_REFCOUNT_TABLE_BITNR] = "refcount table", | ||||
|     [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = "refcount block", | ||||
|     [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table", | ||||
|     [QCOW2_OL_INACTIVE_L1_BITNR]    = "inactive L1 table", | ||||
|     [QCOW2_OL_INACTIVE_L2_BITNR]    = "inactive L2 table", | ||||
| }; | ||||
| QEMU_BUILD_BUG_ON(QCOW2_OL_MAX_BITNR != ARRAY_SIZE(metadata_ol_names)); | ||||
|  | ||||
| /* | ||||
|  * First performs a check for metadata overlaps (through | ||||
|   | ||||
| @@ -25,7 +25,7 @@ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qapi/error.h" | ||||
| #include "block/block_int.h" | ||||
| #include "qcow2.h" | ||||
| #include "block/qcow2.h" | ||||
| #include "qemu/bswap.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "qemu/cutils.h" | ||||
|   | ||||
							
								
								
									
										967
									
								
								block/qcow2.c
									
									
									
									
									
								
							
							
						
						
									
										967
									
								
								block/qcow2.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -27,7 +27,6 @@ | ||||
|  | ||||
| #include "crypto/block.h" | ||||
| #include "qemu/coroutine.h" | ||||
| #include "qemu/units.h" | ||||
|  | ||||
| //#define DEBUG_ALLOC | ||||
| //#define DEBUG_ALLOC2 | ||||
| @@ -42,19 +41,13 @@ | ||||
| #define QCOW_MAX_CRYPT_CLUSTERS 32 | ||||
| #define QCOW_MAX_SNAPSHOTS 65536 | ||||
|  | ||||
| /* Field widths in qcow2 mean normal cluster offsets cannot reach | ||||
|  * 64PB; depending on cluster size, compressed clusters can have a | ||||
|  * smaller limit (64PB for up to 16k clusters, then ramps down to | ||||
|  * 512TB for 2M clusters).  */ | ||||
| #define QCOW_MAX_CLUSTER_OFFSET ((1ULL << 56) - 1) | ||||
|  | ||||
| /* 8 MB refcount table is enough for 2 PB images at 64k cluster size | ||||
|  * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */ | ||||
| #define QCOW_MAX_REFTABLE_SIZE (8 * MiB) | ||||
| #define QCOW_MAX_REFTABLE_SIZE 0x800000 | ||||
|  | ||||
| /* 32 MB L1 table is enough for 2 PB images at 64k cluster size | ||||
|  * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */ | ||||
| #define QCOW_MAX_L1_SIZE (32 * MiB) | ||||
| #define QCOW_MAX_L1_SIZE 0x2000000 | ||||
|  | ||||
| /* Allow for an average of 1k per snapshot table entry, should be plenty of | ||||
|  * space for snapshot names and IDs */ | ||||
| @@ -80,17 +73,17 @@ | ||||
| /* Must be at least 4 to cover all cases of refcount table growth */ | ||||
| #define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */ | ||||
|  | ||||
| #ifdef CONFIG_LINUX | ||||
| #define DEFAULT_L2_CACHE_MAX_SIZE (32 * MiB) | ||||
| #define DEFAULT_CACHE_CLEAN_INTERVAL 600  /* seconds */ | ||||
| #else | ||||
| #define DEFAULT_L2_CACHE_MAX_SIZE (8 * MiB) | ||||
| /* Cache clean interval is currently available only on Linux, so must be 0 */ | ||||
| #define DEFAULT_CACHE_CLEAN_INTERVAL 0 | ||||
| #endif | ||||
| /* 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 | ||||
|  * clusters */ | ||||
| #define DEFAULT_L2_REFCOUNT_SIZE_RATIO 4 | ||||
|  | ||||
| #define DEFAULT_CLUSTER_SIZE 65536 | ||||
|  | ||||
|  | ||||
| #define QCOW2_OPT_LAZY_REFCOUNTS "lazy-refcounts" | ||||
| #define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request" | ||||
| #define QCOW2_OPT_DISCARD_SNAPSHOT "pass-discard-snapshot" | ||||
| @@ -105,7 +98,6 @@ | ||||
| #define QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE "overlap-check.snapshot-table" | ||||
| #define QCOW2_OPT_OVERLAP_INACTIVE_L1 "overlap-check.inactive-l1" | ||||
| #define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2" | ||||
| #define QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY "overlap-check.bitmap-directory" | ||||
| #define QCOW2_OPT_CACHE_SIZE "cache-size" | ||||
| #define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size" | ||||
| #define QCOW2_OPT_L2_CACHE_ENTRY_SIZE "l2-cache-entry-size" | ||||
| @@ -281,7 +273,7 @@ typedef struct BDRVQcow2State { | ||||
|     uint8_t *cluster_cache; | ||||
|     uint8_t *cluster_data; | ||||
|     uint64_t cluster_cache_offset; | ||||
|     QLIST_HEAD(, QCowL2Meta) cluster_allocs; | ||||
|     QLIST_HEAD(QCowClusterAlloc, QCowL2Meta) cluster_allocs; | ||||
|  | ||||
|     uint64_t *refcount_table; | ||||
|     uint64_t refcount_table_offset; | ||||
| @@ -337,9 +329,6 @@ typedef struct BDRVQcow2State { | ||||
|      * override) */ | ||||
|     char *image_backing_file; | ||||
|     char *image_backing_format; | ||||
|  | ||||
|     CoQueue compress_wait_queue; | ||||
|     int nb_compress_threads; | ||||
| } BDRVQcow2State; | ||||
|  | ||||
| typedef struct Qcow2COWRegion { | ||||
| @@ -411,36 +400,34 @@ typedef enum QCow2ClusterType { | ||||
| } QCow2ClusterType; | ||||
|  | ||||
| typedef enum QCow2MetadataOverlap { | ||||
|     QCOW2_OL_MAIN_HEADER_BITNR      = 0, | ||||
|     QCOW2_OL_ACTIVE_L1_BITNR        = 1, | ||||
|     QCOW2_OL_ACTIVE_L2_BITNR        = 2, | ||||
|     QCOW2_OL_REFCOUNT_TABLE_BITNR   = 3, | ||||
|     QCOW2_OL_REFCOUNT_BLOCK_BITNR   = 4, | ||||
|     QCOW2_OL_SNAPSHOT_TABLE_BITNR   = 5, | ||||
|     QCOW2_OL_INACTIVE_L1_BITNR      = 6, | ||||
|     QCOW2_OL_INACTIVE_L2_BITNR      = 7, | ||||
|     QCOW2_OL_BITMAP_DIRECTORY_BITNR = 8, | ||||
|     QCOW2_OL_MAIN_HEADER_BITNR    = 0, | ||||
|     QCOW2_OL_ACTIVE_L1_BITNR      = 1, | ||||
|     QCOW2_OL_ACTIVE_L2_BITNR      = 2, | ||||
|     QCOW2_OL_REFCOUNT_TABLE_BITNR = 3, | ||||
|     QCOW2_OL_REFCOUNT_BLOCK_BITNR = 4, | ||||
|     QCOW2_OL_SNAPSHOT_TABLE_BITNR = 5, | ||||
|     QCOW2_OL_INACTIVE_L1_BITNR    = 6, | ||||
|     QCOW2_OL_INACTIVE_L2_BITNR    = 7, | ||||
|  | ||||
|     QCOW2_OL_MAX_BITNR              = 9, | ||||
|     QCOW2_OL_MAX_BITNR            = 8, | ||||
|  | ||||
|     QCOW2_OL_NONE             = 0, | ||||
|     QCOW2_OL_MAIN_HEADER      = (1 << QCOW2_OL_MAIN_HEADER_BITNR), | ||||
|     QCOW2_OL_ACTIVE_L1        = (1 << QCOW2_OL_ACTIVE_L1_BITNR), | ||||
|     QCOW2_OL_ACTIVE_L2        = (1 << QCOW2_OL_ACTIVE_L2_BITNR), | ||||
|     QCOW2_OL_REFCOUNT_TABLE   = (1 << QCOW2_OL_REFCOUNT_TABLE_BITNR), | ||||
|     QCOW2_OL_REFCOUNT_BLOCK   = (1 << QCOW2_OL_REFCOUNT_BLOCK_BITNR), | ||||
|     QCOW2_OL_SNAPSHOT_TABLE   = (1 << QCOW2_OL_SNAPSHOT_TABLE_BITNR), | ||||
|     QCOW2_OL_INACTIVE_L1      = (1 << QCOW2_OL_INACTIVE_L1_BITNR), | ||||
|     QCOW2_OL_NONE           = 0, | ||||
|     QCOW2_OL_MAIN_HEADER    = (1 << QCOW2_OL_MAIN_HEADER_BITNR), | ||||
|     QCOW2_OL_ACTIVE_L1      = (1 << QCOW2_OL_ACTIVE_L1_BITNR), | ||||
|     QCOW2_OL_ACTIVE_L2      = (1 << QCOW2_OL_ACTIVE_L2_BITNR), | ||||
|     QCOW2_OL_REFCOUNT_TABLE = (1 << QCOW2_OL_REFCOUNT_TABLE_BITNR), | ||||
|     QCOW2_OL_REFCOUNT_BLOCK = (1 << QCOW2_OL_REFCOUNT_BLOCK_BITNR), | ||||
|     QCOW2_OL_SNAPSHOT_TABLE = (1 << QCOW2_OL_SNAPSHOT_TABLE_BITNR), | ||||
|     QCOW2_OL_INACTIVE_L1    = (1 << QCOW2_OL_INACTIVE_L1_BITNR), | ||||
|     /* NOTE: Checking overlaps with inactive L2 tables will result in bdrv | ||||
|      * reads. */ | ||||
|     QCOW2_OL_INACTIVE_L2      = (1 << QCOW2_OL_INACTIVE_L2_BITNR), | ||||
|     QCOW2_OL_BITMAP_DIRECTORY = (1 << QCOW2_OL_BITMAP_DIRECTORY_BITNR), | ||||
|     QCOW2_OL_INACTIVE_L2    = (1 << QCOW2_OL_INACTIVE_L2_BITNR), | ||||
| } QCow2MetadataOverlap; | ||||
|  | ||||
| /* Perform all overlap checks which can be done in constant time */ | ||||
| #define QCOW2_OL_CONSTANT \ | ||||
|     (QCOW2_OL_MAIN_HEADER | QCOW2_OL_ACTIVE_L1 | QCOW2_OL_REFCOUNT_TABLE | \ | ||||
|      QCOW2_OL_SNAPSHOT_TABLE | QCOW2_OL_BITMAP_DIRECTORY) | ||||
|      QCOW2_OL_SNAPSHOT_TABLE) | ||||
|  | ||||
| /* Perform all overlap checks which don't require disk access */ | ||||
| #define QCOW2_OL_CACHED \ | ||||
| @@ -616,6 +603,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, | ||||
|                         bool exact_size); | ||||
| int qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t max_size); | ||||
| int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index); | ||||
| int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); | ||||
| int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num, | ||||
|                           uint8_t *buf, int nb_sectors, bool enc, Error **errp); | ||||
|  | ||||
| @@ -629,7 +617,6 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, | ||||
|                                          int compressed_size); | ||||
|  | ||||
| int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); | ||||
| void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m); | ||||
| int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset, | ||||
|                           uint64_t bytes, enum qcow2_discard_type type, | ||||
|                           bool full_discard); | ||||
| @@ -684,8 +671,6 @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, | ||||
|                                   void **refcount_table, | ||||
|                                   int64_t *refcount_table_size); | ||||
| bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp); | ||||
| int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated, | ||||
|                                  Error **errp); | ||||
| int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp); | ||||
| void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp); | ||||
| int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp); | ||||
|   | ||||
							
								
								
									
										223
									
								
								block/qed.c
									
									
									
									
									
								
							
							
						
						
									
										223
									
								
								block/qed.c
									
									
									
									
									
								
							| @@ -13,7 +13,6 @@ | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "block/qdict.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qemu/timer.h" | ||||
| #include "qemu/bswap.h" | ||||
| @@ -21,11 +20,6 @@ | ||||
| #include "trace.h" | ||||
| #include "qed.h" | ||||
| #include "sysemu/block-backend.h" | ||||
| #include "qapi/qmp/qdict.h" | ||||
| #include "qapi/qobject-input-visitor.h" | ||||
| #include "qapi/qapi-visit-block-core.h" | ||||
|  | ||||
| static QemuOptsList qed_create_opts; | ||||
|  | ||||
| static int bdrv_qed_probe(const uint8_t *buf, int buf_size, | ||||
|                           const char *filename) | ||||
| @@ -559,7 +553,6 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     if (qemu_in_coroutine()) { | ||||
|         bdrv_qed_open_entry(&qoc); | ||||
|     } else { | ||||
|         assert(qemu_get_current_aio_context() == qemu_get_aio_context()); | ||||
|         qemu_coroutine_enter(qemu_coroutine_create(bdrv_qed_open_entry, &qoc)); | ||||
|         BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS); | ||||
|     } | ||||
| @@ -601,78 +594,43 @@ static void bdrv_qed_close(BlockDriverState *bs) | ||||
|     qemu_vfree(s->l1_table); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts, | ||||
|                                            Error **errp) | ||||
| static int qed_create(const char *filename, uint32_t cluster_size, | ||||
|                       uint64_t image_size, uint32_t table_size, | ||||
|                       const char *backing_file, const char *backing_fmt, | ||||
|                       QemuOpts *opts, Error **errp) | ||||
| { | ||||
|     BlockdevCreateOptionsQed *qed_opts; | ||||
|     BlockBackend *blk = NULL; | ||||
|     BlockDriverState *bs = NULL; | ||||
|  | ||||
|     QEDHeader header; | ||||
|     QEDHeader le_header; | ||||
|     uint8_t *l1_table = NULL; | ||||
|     size_t l1_size; | ||||
|     int ret = 0; | ||||
|  | ||||
|     assert(opts->driver == BLOCKDEV_DRIVER_QED); | ||||
|     qed_opts = &opts->u.qed; | ||||
|  | ||||
|     /* Validate options and set default values */ | ||||
|     if (!qed_opts->has_cluster_size) { | ||||
|         qed_opts->cluster_size = QED_DEFAULT_CLUSTER_SIZE; | ||||
|     } | ||||
|     if (!qed_opts->has_table_size) { | ||||
|         qed_opts->table_size = QED_DEFAULT_TABLE_SIZE; | ||||
|     } | ||||
|  | ||||
|     if (!qed_is_cluster_size_valid(qed_opts->cluster_size)) { | ||||
|         error_setg(errp, "QED cluster size must be within range [%u, %u] " | ||||
|                          "and power of 2", | ||||
|                    QED_MIN_CLUSTER_SIZE, QED_MAX_CLUSTER_SIZE); | ||||
|         return -EINVAL; | ||||
|     } | ||||
|     if (!qed_is_table_size_valid(qed_opts->table_size)) { | ||||
|         error_setg(errp, "QED table size must be within range [%u, %u] " | ||||
|                          "and power of 2", | ||||
|                    QED_MIN_TABLE_SIZE, QED_MAX_TABLE_SIZE); | ||||
|         return -EINVAL; | ||||
|     } | ||||
|     if (!qed_is_image_size_valid(qed_opts->size, qed_opts->cluster_size, | ||||
|                                  qed_opts->table_size)) | ||||
|     { | ||||
|         error_setg(errp, "QED image size must be a non-zero multiple of " | ||||
|                          "cluster size and less than %" PRIu64 " bytes", | ||||
|                    qed_max_image_size(qed_opts->cluster_size, | ||||
|                                       qed_opts->table_size)); | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     /* Create BlockBackend to write to the image */ | ||||
|     bs = bdrv_open_blockdev_ref(qed_opts->file, errp); | ||||
|     if (bs == NULL) { | ||||
|         return -EIO; | ||||
|     } | ||||
|  | ||||
|     blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); | ||||
|     ret = blk_insert_bs(blk, bs, errp); | ||||
|     if (ret < 0) { | ||||
|         goto out; | ||||
|     } | ||||
|     blk_set_allow_write_beyond_eof(blk, true); | ||||
|  | ||||
|     /* Prepare image format */ | ||||
|     header = (QEDHeader) { | ||||
|     QEDHeader header = { | ||||
|         .magic = QED_MAGIC, | ||||
|         .cluster_size = qed_opts->cluster_size, | ||||
|         .table_size = qed_opts->table_size, | ||||
|         .cluster_size = cluster_size, | ||||
|         .table_size = table_size, | ||||
|         .header_size = 1, | ||||
|         .features = 0, | ||||
|         .compat_features = 0, | ||||
|         .l1_table_offset = qed_opts->cluster_size, | ||||
|         .image_size = qed_opts->size, | ||||
|         .l1_table_offset = cluster_size, | ||||
|         .image_size = image_size, | ||||
|     }; | ||||
|     QEDHeader le_header; | ||||
|     uint8_t *l1_table = NULL; | ||||
|     size_t l1_size = header.cluster_size * header.table_size; | ||||
|     Error *local_err = NULL; | ||||
|     int ret = 0; | ||||
|     BlockBackend *blk; | ||||
|  | ||||
|     l1_size = header.cluster_size * header.table_size; | ||||
|     ret = bdrv_create_file(filename, opts, &local_err); | ||||
|     if (ret < 0) { | ||||
|         error_propagate(errp, local_err); | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     blk = blk_new_open(filename, NULL, NULL, | ||||
|                        BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, | ||||
|                        &local_err); | ||||
|     if (blk == NULL) { | ||||
|         error_propagate(errp, local_err); | ||||
|         return -EIO; | ||||
|     } | ||||
|  | ||||
|     blk_set_allow_write_beyond_eof(blk, true); | ||||
|  | ||||
|     /* File must start empty and grow, check truncate is supported */ | ||||
|     ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp); | ||||
| @@ -680,16 +638,13 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts, | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     if (qed_opts->has_backing_file) { | ||||
|     if (backing_file) { | ||||
|         header.features |= QED_F_BACKING_FILE; | ||||
|         header.backing_filename_offset = sizeof(le_header); | ||||
|         header.backing_filename_size = strlen(qed_opts->backing_file); | ||||
|         header.backing_filename_size = strlen(backing_file); | ||||
|  | ||||
|         if (qed_opts->has_backing_fmt) { | ||||
|             const char *backing_fmt = BlockdevDriver_str(qed_opts->backing_fmt); | ||||
|             if (qed_fmt_is_raw(backing_fmt)) { | ||||
|                 header.features |= QED_F_BACKING_FORMAT_NO_PROBE; | ||||
|             } | ||||
|         if (qed_fmt_is_raw(backing_fmt)) { | ||||
|             header.features |= QED_F_BACKING_FORMAT_NO_PROBE; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -698,7 +653,7 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts, | ||||
|     if (ret < 0) { | ||||
|         goto out; | ||||
|     } | ||||
|     ret = blk_pwrite(blk, sizeof(le_header), qed_opts->backing_file, | ||||
|     ret = blk_pwrite(blk, sizeof(le_header), backing_file, | ||||
|                      header.backing_filename_size, 0); | ||||
|     if (ret < 0) { | ||||
|         goto out; | ||||
| @@ -714,7 +669,6 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts, | ||||
| out: | ||||
|     g_free(l1_table); | ||||
|     blk_unref(blk); | ||||
|     bdrv_unref(bs); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| @@ -722,74 +676,51 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, | ||||
|                                                 QemuOpts *opts, | ||||
|                                                 Error **errp) | ||||
| { | ||||
|     BlockdevCreateOptions *create_options = NULL; | ||||
|     QDict *qdict; | ||||
|     Visitor *v; | ||||
|     BlockDriverState *bs = NULL; | ||||
|     Error *local_err = NULL; | ||||
|     uint64_t image_size = 0; | ||||
|     uint32_t cluster_size = QED_DEFAULT_CLUSTER_SIZE; | ||||
|     uint32_t table_size = QED_DEFAULT_TABLE_SIZE; | ||||
|     char *backing_file = NULL; | ||||
|     char *backing_fmt = NULL; | ||||
|     int ret; | ||||
|  | ||||
|     static const QDictRenames opt_renames[] = { | ||||
|         { BLOCK_OPT_BACKING_FILE,       "backing-file" }, | ||||
|         { BLOCK_OPT_BACKING_FMT,        "backing-fmt" }, | ||||
|         { BLOCK_OPT_CLUSTER_SIZE,       "cluster-size" }, | ||||
|         { BLOCK_OPT_TABLE_SIZE,         "table-size" }, | ||||
|         { NULL, NULL }, | ||||
|     }; | ||||
|     image_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), | ||||
|                           BDRV_SECTOR_SIZE); | ||||
|     backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); | ||||
|     backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT); | ||||
|     cluster_size = qemu_opt_get_size_del(opts, | ||||
|                                          BLOCK_OPT_CLUSTER_SIZE, | ||||
|                                          QED_DEFAULT_CLUSTER_SIZE); | ||||
|     table_size = qemu_opt_get_size_del(opts, BLOCK_OPT_TABLE_SIZE, | ||||
|                                        QED_DEFAULT_TABLE_SIZE); | ||||
|  | ||||
|     /* Parse options and convert legacy syntax */ | ||||
|     qdict = qemu_opts_to_qdict_filtered(opts, NULL, &qed_create_opts, true); | ||||
|  | ||||
|     if (!qdict_rename_keys(qdict, opt_renames, errp)) { | ||||
|     if (!qed_is_cluster_size_valid(cluster_size)) { | ||||
|         error_setg(errp, "QED cluster size must be within range [%u, %u] " | ||||
|                          "and power of 2", | ||||
|                    QED_MIN_CLUSTER_SIZE, QED_MAX_CLUSTER_SIZE); | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|         goto finish; | ||||
|     } | ||||
|  | ||||
|     /* Create and open the file (protocol layer) */ | ||||
|     ret = bdrv_create_file(filename, opts, &local_err); | ||||
|     if (ret < 0) { | ||||
|         error_propagate(errp, local_err); | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     bs = bdrv_open(filename, NULL, NULL, | ||||
|                    BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); | ||||
|     if (bs == NULL) { | ||||
|         ret = -EIO; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     /* Now get the QAPI type BlockdevCreateOptions */ | ||||
|     qdict_put_str(qdict, "driver", "qed"); | ||||
|     qdict_put_str(qdict, "file", bs->node_name); | ||||
|  | ||||
|     v = qobject_input_visitor_new_flat_confused(qdict, errp); | ||||
|     if (!v) { | ||||
|     if (!qed_is_table_size_valid(table_size)) { | ||||
|         error_setg(errp, "QED table size must be within range [%u, %u] " | ||||
|                          "and power of 2", | ||||
|                    QED_MIN_TABLE_SIZE, QED_MAX_TABLE_SIZE); | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|         goto finish; | ||||
|     } | ||||
|  | ||||
|     visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); | ||||
|     visit_free(v); | ||||
|  | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|     if (!qed_is_image_size_valid(image_size, cluster_size, table_size)) { | ||||
|         error_setg(errp, "QED image size must be a non-zero multiple of " | ||||
|                          "cluster size and less than %" PRIu64 " bytes", | ||||
|                    qed_max_image_size(cluster_size, table_size)); | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|         goto finish; | ||||
|     } | ||||
|  | ||||
|     /* Silently round up size */ | ||||
|     assert(create_options->driver == BLOCKDEV_DRIVER_QED); | ||||
|     create_options->u.qed.size = | ||||
|         ROUND_UP(create_options->u.qed.size, BDRV_SECTOR_SIZE); | ||||
|     ret = qed_create(filename, cluster_size, image_size, table_size, | ||||
|                      backing_file, backing_fmt, opts, errp); | ||||
|  | ||||
|     /* Create the qed image (format layer) */ | ||||
|     ret = bdrv_qed_co_create(create_options, errp); | ||||
|  | ||||
| fail: | ||||
|     qobject_unref(qdict); | ||||
|     bdrv_unref(bs); | ||||
|     qapi_free_BlockdevCreateOptions(create_options); | ||||
| finish: | ||||
|     g_free(backing_file); | ||||
|     g_free(backing_fmt); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| @@ -1435,9 +1366,8 @@ static int coroutine_fn bdrv_qed_co_readv(BlockDriverState *bs, | ||||
|  | ||||
| static int coroutine_fn bdrv_qed_co_writev(BlockDriverState *bs, | ||||
|                                            int64_t sector_num, int nb_sectors, | ||||
|                                            QEMUIOVector *qiov, int flags) | ||||
|                                            QEMUIOVector *qiov) | ||||
| { | ||||
|     assert(!flags); | ||||
|     return qed_co_request(bs, sector_num, qiov, nb_sectors, QED_AIOCB_WRITE); | ||||
| } | ||||
|  | ||||
| @@ -1468,10 +1398,8 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs, | ||||
|                           QED_AIOCB_WRITE | QED_AIOCB_ZERO); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn bdrv_qed_co_truncate(BlockDriverState *bs, | ||||
|                                              int64_t offset, | ||||
|                                              PreallocMode prealloc, | ||||
|                                              Error **errp) | ||||
| static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                              PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     BDRVQEDState *s = bs->opaque; | ||||
|     uint64_t old_image_size; | ||||
| @@ -1607,8 +1535,8 @@ static void coroutine_fn bdrv_qed_co_invalidate_cache(BlockDriverState *bs, | ||||
|     ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, &local_err); | ||||
|     qemu_co_mutex_unlock(&s->table_lock); | ||||
|     if (local_err) { | ||||
|         error_propagate_prepend(errp, local_err, | ||||
|                                 "Could not reopen qed layer: "); | ||||
|         error_propagate(errp, local_err); | ||||
|         error_prepend(errp, "Could not reopen qed layer: "); | ||||
|         return; | ||||
|     } else if (ret < 0) { | ||||
|         error_setg_errno(errp, -ret, "Could not reopen qed layer"); | ||||
| @@ -1674,14 +1602,13 @@ static BlockDriver bdrv_qed = { | ||||
|     .bdrv_close               = bdrv_qed_close, | ||||
|     .bdrv_reopen_prepare      = bdrv_qed_reopen_prepare, | ||||
|     .bdrv_child_perm          = bdrv_format_default_perms, | ||||
|     .bdrv_co_create           = bdrv_qed_co_create, | ||||
|     .bdrv_co_create_opts      = bdrv_qed_co_create_opts, | ||||
|     .bdrv_has_zero_init       = bdrv_has_zero_init_1, | ||||
|     .bdrv_co_block_status     = bdrv_qed_co_block_status, | ||||
|     .bdrv_co_readv            = bdrv_qed_co_readv, | ||||
|     .bdrv_co_writev           = bdrv_qed_co_writev, | ||||
|     .bdrv_co_pwrite_zeroes    = bdrv_qed_co_pwrite_zeroes, | ||||
|     .bdrv_co_truncate         = bdrv_qed_co_truncate, | ||||
|     .bdrv_truncate            = bdrv_qed_truncate, | ||||
|     .bdrv_getlength           = bdrv_qed_getlength, | ||||
|     .bdrv_get_info            = bdrv_qed_get_info, | ||||
|     .bdrv_refresh_limits      = bdrv_qed_refresh_limits, | ||||
|   | ||||
| @@ -17,7 +17,6 @@ | ||||
| #include "qemu/cutils.h" | ||||
| #include "qemu/option.h" | ||||
| #include "block/block_int.h" | ||||
| #include "block/qdict.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qapi/qapi-events-block.h" | ||||
| #include "qapi/qmp/qdict.h" | ||||
| @@ -116,7 +115,6 @@ struct QuorumAIOCB { | ||||
|     /* Request metadata */ | ||||
|     uint64_t offset; | ||||
|     uint64_t bytes; | ||||
|     int flags; | ||||
|  | ||||
|     QEMUIOVector *qiov;         /* calling IOV */ | ||||
|  | ||||
| @@ -159,8 +157,7 @@ static bool quorum_64bits_compare(QuorumVoteValue *a, QuorumVoteValue *b) | ||||
| static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs, | ||||
|                                    QEMUIOVector *qiov, | ||||
|                                    uint64_t offset, | ||||
|                                    uint64_t bytes, | ||||
|                                    int flags) | ||||
|                                    uint64_t bytes) | ||||
| { | ||||
|     BDRVQuorumState *s = bs->opaque; | ||||
|     QuorumAIOCB *acb = g_new(QuorumAIOCB, 1); | ||||
| @@ -171,7 +168,6 @@ static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs, | ||||
|         .bs                 = bs, | ||||
|         .offset             = offset, | ||||
|         .bytes              = bytes, | ||||
|         .flags              = flags, | ||||
|         .qiov               = qiov, | ||||
|         .votes.compare      = quorum_sha256_compare, | ||||
|         .votes.vote_list    = QLIST_HEAD_INITIALIZER(acb.votes.vote_list), | ||||
| @@ -199,7 +195,7 @@ static void quorum_report_bad(QuorumOpType type, uint64_t offset, | ||||
|     } | ||||
|  | ||||
|     qapi_event_send_quorum_report_bad(type, !!msg, msg, node_name, start_sector, | ||||
|                                       end_sector - start_sector); | ||||
|                                       end_sector - start_sector, &error_abort); | ||||
| } | ||||
|  | ||||
| static void quorum_report_failure(QuorumAIOCB *acb) | ||||
| @@ -210,7 +206,7 @@ static void quorum_report_failure(QuorumAIOCB *acb) | ||||
|                                       BDRV_SECTOR_SIZE); | ||||
|  | ||||
|     qapi_event_send_quorum_failure(reference, start_sector, | ||||
|                                    end_sector - start_sector); | ||||
|                                    end_sector - start_sector, &error_abort); | ||||
| } | ||||
|  | ||||
| static int quorum_vote_error(QuorumAIOCB *acb); | ||||
| @@ -275,11 +271,9 @@ static void quorum_rewrite_entry(void *opaque) | ||||
|     BDRVQuorumState *s = acb->bs->opaque; | ||||
|  | ||||
|     /* Ignore any errors, it's just a correction attempt for already | ||||
|      * corrupted data. | ||||
|      * Mask out BDRV_REQ_WRITE_UNCHANGED because this overwrites the | ||||
|      * area with different data from the other children. */ | ||||
|      * corrupted data. */ | ||||
|     bdrv_co_pwritev(s->children[co->idx], acb->offset, acb->bytes, | ||||
|                     acb->qiov, acb->flags & ~BDRV_REQ_WRITE_UNCHANGED); | ||||
|                     acb->qiov, 0); | ||||
|  | ||||
|     /* Wake up the caller after the last rewrite */ | ||||
|     acb->rewrite_count--; | ||||
| @@ -437,7 +431,23 @@ static bool quorum_iovec_compare(QEMUIOVector *a, QEMUIOVector *b) | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| static bool quorum_compare(QuorumAIOCB *acb, QEMUIOVector *a, QEMUIOVector *b) | ||||
| static void GCC_FMT_ATTR(2, 3) quorum_err(QuorumAIOCB *acb, | ||||
|                                           const char *fmt, ...) | ||||
| { | ||||
|     va_list ap; | ||||
|  | ||||
|     va_start(ap, fmt); | ||||
|     fprintf(stderr, "quorum: offset=%" PRIu64 " bytes=%" PRIu64 " ", | ||||
|             acb->offset, acb->bytes); | ||||
|     vfprintf(stderr, fmt, ap); | ||||
|     fprintf(stderr, "\n"); | ||||
|     va_end(ap); | ||||
|     exit(1); | ||||
| } | ||||
|  | ||||
| static bool quorum_compare(QuorumAIOCB *acb, | ||||
|                            QEMUIOVector *a, | ||||
|                            QEMUIOVector *b) | ||||
| { | ||||
|     BDRVQuorumState *s = acb->bs->opaque; | ||||
|     ssize_t offset; | ||||
| @@ -446,10 +456,8 @@ static bool quorum_compare(QuorumAIOCB *acb, QEMUIOVector *a, QEMUIOVector *b) | ||||
|     if (s->is_blkverify) { | ||||
|         offset = qemu_iovec_compare(a, b); | ||||
|         if (offset != -1) { | ||||
|             fprintf(stderr, "quorum: offset=%" PRIu64 " bytes=%" PRIu64 | ||||
|                     " contents mismatch at offset %" PRIu64 "\n", | ||||
|                     acb->offset, acb->bytes, acb->offset + offset); | ||||
|             exit(1); | ||||
|             quorum_err(acb, "contents mismatch at offset %" PRIu64, | ||||
|                        acb->offset + offset); | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
| @@ -600,7 +608,7 @@ static void read_quorum_children_entry(void *opaque) | ||||
| static int read_quorum_children(QuorumAIOCB *acb) | ||||
| { | ||||
|     BDRVQuorumState *s = acb->bs->opaque; | ||||
|     int i; | ||||
|     int i, ret; | ||||
|  | ||||
|     acb->children_read = s->num_children; | ||||
|     for (i = 0; i < s->num_children; i++) { | ||||
| @@ -635,7 +643,9 @@ static int read_quorum_children(QuorumAIOCB *acb) | ||||
|         qemu_coroutine_yield(); | ||||
|     } | ||||
|  | ||||
|     return acb->vote_ret; | ||||
|     ret = acb->vote_ret; | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static int read_fifo_child(QuorumAIOCB *acb) | ||||
| @@ -663,7 +673,7 @@ static int quorum_co_preadv(BlockDriverState *bs, uint64_t offset, | ||||
|                             uint64_t bytes, QEMUIOVector *qiov, int flags) | ||||
| { | ||||
|     BDRVQuorumState *s = bs->opaque; | ||||
|     QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes, flags); | ||||
|     QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes); | ||||
|     int ret; | ||||
|  | ||||
|     acb->is_read = true; | ||||
| @@ -689,7 +699,7 @@ static void write_quorum_entry(void *opaque) | ||||
|  | ||||
|     sacb->bs = s->children[i]->bs; | ||||
|     sacb->ret = bdrv_co_pwritev(s->children[i], acb->offset, acb->bytes, | ||||
|                                 acb->qiov, acb->flags); | ||||
|                                 acb->qiov, 0); | ||||
|     if (sacb->ret == 0) { | ||||
|         acb->success_count++; | ||||
|     } else { | ||||
| @@ -709,7 +719,7 @@ static int quorum_co_pwritev(BlockDriverState *bs, uint64_t offset, | ||||
|                              uint64_t bytes, QEMUIOVector *qiov, int flags) | ||||
| { | ||||
|     BDRVQuorumState *s = bs->opaque; | ||||
|     QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes, flags); | ||||
|     QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes); | ||||
|     int i, ret; | ||||
|  | ||||
|     for (i = 0; i < s->num_children; i++) { | ||||
| @@ -912,12 +922,13 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     s->read_pattern = ret; | ||||
|  | ||||
|     if (s->read_pattern == QUORUM_READ_PATTERN_QUORUM) { | ||||
|         s->is_blkverify = qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false); | ||||
|         if (s->is_blkverify && (s->num_children != 2 || s->threshold != 2)) { | ||||
|             error_setg(&local_err, "blkverify=on can only be set if there are " | ||||
|                        "exactly two files and vote-threshold is 2"); | ||||
|             ret = -EINVAL; | ||||
|             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) { | ||||
|             s->is_blkverify = true; | ||||
|         } else if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false)) { | ||||
|             fprintf(stderr, "blkverify mode is set by setting blkverify=on " | ||||
|                     "and using two files with vote_threshold=2\n"); | ||||
|         } | ||||
|  | ||||
|         s->rewrite_corrupted = qemu_opt_get_bool(opts, QUORUM_OPT_REWRITE, | ||||
| @@ -950,8 +961,6 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     } | ||||
|     s->next_child_index = s->num_children; | ||||
|  | ||||
|     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; | ||||
|  | ||||
|     g_free(opened); | ||||
|     goto exit; | ||||
|  | ||||
| @@ -992,11 +1001,6 @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs, | ||||
|     char indexstr[32]; | ||||
|     int ret; | ||||
|  | ||||
|     if (s->is_blkverify) { | ||||
|         error_setg(errp, "Cannot add a child to a quorum in blkverify mode"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     assert(s->num_children <= INT_MAX / sizeof(BdrvChild *)); | ||||
|     if (s->num_children == INT_MAX / sizeof(BdrvChild *) || | ||||
|         s->next_child_index == UINT_MAX) { | ||||
| @@ -1051,9 +1055,6 @@ static void quorum_del_child(BlockDriverState *bs, BdrvChild *child, | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* We know now that num_children > threshold, so blkverify must be false */ | ||||
|     assert(!s->is_blkverify); | ||||
|  | ||||
|     bdrv_drained_begin(bs); | ||||
|  | ||||
|     /* We can safely remove this child now */ | ||||
| @@ -1081,8 +1082,8 @@ static void quorum_refresh_filename(BlockDriverState *bs, QDict *options) | ||||
|  | ||||
|     children = qlist_new(); | ||||
|     for (i = 0; i < s->num_children; i++) { | ||||
|         qlist_append(children, | ||||
|                      qobject_ref(s->children[i]->bs->full_open_options)); | ||||
|         QINCREF(s->children[i]->bs->full_open_options); | ||||
|         qlist_append(children, s->children[i]->bs->full_open_options); | ||||
|     } | ||||
|  | ||||
|     opts = qdict_new(); | ||||
|   | ||||
| @@ -167,37 +167,16 @@ static void raw_reopen_abort(BDRVReopenState *state) | ||||
|     state->opaque = NULL; | ||||
| } | ||||
|  | ||||
| /* Check and adjust the offset, against 'offset' and 'size' options. */ | ||||
| static inline int raw_adjust_offset(BlockDriverState *bs, uint64_t *offset, | ||||
|                                     uint64_t bytes, bool is_write) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|  | ||||
|     if (s->has_size && (*offset > s->size || bytes > (s->size - *offset))) { | ||||
|         /* There's not enough space for the write, or the read request is | ||||
|          * out-of-range. Don't read/write anything to prevent leaking out of | ||||
|          * the size specified in options. */ | ||||
|         return is_write ? -ENOSPC : -EINVAL; | ||||
|     } | ||||
|  | ||||
|     if (*offset > INT64_MAX - s->offset) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|     *offset += s->offset; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn raw_co_preadv(BlockDriverState *bs, uint64_t offset, | ||||
|                                       uint64_t bytes, QEMUIOVector *qiov, | ||||
|                                       int flags) | ||||
| { | ||||
|     int ret; | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|  | ||||
|     ret = raw_adjust_offset(bs, &offset, bytes, false); | ||||
|     if (ret) { | ||||
|         return ret; | ||||
|     if (offset > UINT64_MAX - s->offset) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|     offset += s->offset; | ||||
|  | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); | ||||
|     return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); | ||||
| @@ -207,11 +186,23 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset, | ||||
|                                        uint64_t bytes, QEMUIOVector *qiov, | ||||
|                                        int flags) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     void *buf = NULL; | ||||
|     BlockDriver *drv; | ||||
|     QEMUIOVector local_qiov; | ||||
|     int ret; | ||||
|  | ||||
|     if (s->has_size && (offset > s->size || bytes > (s->size - offset))) { | ||||
|         /* There's not enough space for the data. Don't write anything and just | ||||
|          * fail to prevent leaking out of the size specified in options. */ | ||||
|         return -ENOSPC; | ||||
|     } | ||||
|  | ||||
|     if (offset > UINT64_MAX - s->offset) { | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     if (bs->probed && offset < BLOCK_PROBE_BUF_SIZE && bytes) { | ||||
|         /* Handling partial writes would be a pain - so we just | ||||
|          * require that guests have 512-byte request alignment if | ||||
| @@ -246,10 +237,7 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset, | ||||
|         qiov = &local_qiov; | ||||
|     } | ||||
|  | ||||
|     ret = raw_adjust_offset(bs, &offset, bytes, true); | ||||
|     if (ret) { | ||||
|         goto fail; | ||||
|     } | ||||
|     offset += s->offset; | ||||
|  | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); | ||||
|     ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); | ||||
| @@ -279,25 +267,23 @@ static int coroutine_fn raw_co_pwrite_zeroes(BlockDriverState *bs, | ||||
|                                              int64_t offset, int bytes, | ||||
|                                              BdrvRequestFlags flags) | ||||
| { | ||||
|     int ret; | ||||
|  | ||||
|     ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true); | ||||
|     if (ret) { | ||||
|         return ret; | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     if (offset > UINT64_MAX - s->offset) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|     offset += s->offset; | ||||
|     return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs, | ||||
|                                         int64_t offset, int bytes) | ||||
| { | ||||
|     int ret; | ||||
|  | ||||
|     ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true); | ||||
|     if (ret) { | ||||
|         return ret; | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     if (offset > UINT64_MAX - s->offset) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|     return bdrv_co_pdiscard(bs->file, offset, bytes); | ||||
|     offset += s->offset; | ||||
|     return bdrv_co_pdiscard(bs->file->bs, offset, bytes); | ||||
| } | ||||
|  | ||||
| static int64_t raw_getlength(BlockDriverState *bs) | ||||
| @@ -366,8 +352,8 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                                         PreallocMode prealloc, Error **errp) | ||||
| static int raw_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                         PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|  | ||||
| @@ -383,7 +369,7 @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, | ||||
|  | ||||
|     s->size = offset; | ||||
|     offset += s->offset; | ||||
|     return bdrv_co_truncate(bs->file, offset, prealloc, errp); | ||||
|     return bdrv_truncate(bs->file, offset, prealloc, errp); | ||||
| } | ||||
|  | ||||
| static void raw_eject(BlockDriverState *bs, bool eject_flag) | ||||
| @@ -429,11 +415,10 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     } | ||||
|  | ||||
|     bs->sg = bs->file->bs->sg; | ||||
|     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | | ||||
|         (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); | ||||
|     bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | | ||||
|         ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & | ||||
|             bs->file->bs->supported_zero_flags); | ||||
|     bs->supported_write_flags = BDRV_REQ_FUA & | ||||
|         bs->file->bs->supported_write_flags; | ||||
|     bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & | ||||
|         bs->file->bs->supported_zero_flags; | ||||
|  | ||||
|     if (bs->probed && !bdrv_is_read_only(bs)) { | ||||
|         fprintf(stderr, | ||||
| @@ -459,6 +444,10 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void raw_close(BlockDriverState *bs) | ||||
| { | ||||
| } | ||||
|  | ||||
| static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||
| { | ||||
|     /* smallest possible positive score so that raw is used if and only if no | ||||
| @@ -493,44 +482,6 @@ static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) | ||||
|     return bdrv_probe_geometry(bs->file->bs, geo); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs, | ||||
|                                                BdrvChild *src, | ||||
|                                                uint64_t src_offset, | ||||
|                                                BdrvChild *dst, | ||||
|                                                uint64_t dst_offset, | ||||
|                                                uint64_t bytes, | ||||
|                                                BdrvRequestFlags read_flags, | ||||
|                                                BdrvRequestFlags write_flags) | ||||
| { | ||||
|     int ret; | ||||
|  | ||||
|     ret = raw_adjust_offset(bs, &src_offset, bytes, false); | ||||
|     if (ret) { | ||||
|         return ret; | ||||
|     } | ||||
|     return bdrv_co_copy_range_from(bs->file, src_offset, dst, dst_offset, | ||||
|                                    bytes, read_flags, write_flags); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, | ||||
|                                              BdrvChild *src, | ||||
|                                              uint64_t src_offset, | ||||
|                                              BdrvChild *dst, | ||||
|                                              uint64_t dst_offset, | ||||
|                                              uint64_t bytes, | ||||
|                                              BdrvRequestFlags read_flags, | ||||
|                                              BdrvRequestFlags write_flags) | ||||
| { | ||||
|     int ret; | ||||
|  | ||||
|     ret = raw_adjust_offset(bs, &dst_offset, bytes, true); | ||||
|     if (ret) { | ||||
|         return ret; | ||||
|     } | ||||
|     return bdrv_co_copy_range_to(src, src_offset, bs->file, dst_offset, bytes, | ||||
|                                  read_flags, write_flags); | ||||
| } | ||||
|  | ||||
| BlockDriver bdrv_raw = { | ||||
|     .format_name          = "raw", | ||||
|     .instance_size        = sizeof(BDRVRawState), | ||||
| @@ -539,6 +490,7 @@ BlockDriver bdrv_raw = { | ||||
|     .bdrv_reopen_commit   = &raw_reopen_commit, | ||||
|     .bdrv_reopen_abort    = &raw_reopen_abort, | ||||
|     .bdrv_open            = &raw_open, | ||||
|     .bdrv_close           = &raw_close, | ||||
|     .bdrv_child_perm      = bdrv_filter_default_perms, | ||||
|     .bdrv_co_create_opts  = &raw_co_create_opts, | ||||
|     .bdrv_co_preadv       = &raw_co_preadv, | ||||
| @@ -546,9 +498,7 @@ BlockDriver bdrv_raw = { | ||||
|     .bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes, | ||||
|     .bdrv_co_pdiscard     = &raw_co_pdiscard, | ||||
|     .bdrv_co_block_status = &raw_co_block_status, | ||||
|     .bdrv_co_copy_range_from = &raw_co_copy_range_from, | ||||
|     .bdrv_co_copy_range_to  = &raw_co_copy_range_to, | ||||
|     .bdrv_co_truncate     = &raw_co_truncate, | ||||
|     .bdrv_truncate        = &raw_truncate, | ||||
|     .bdrv_getlength       = &raw_getlength, | ||||
|     .has_variable_length  = true, | ||||
|     .bdrv_measure         = &raw_measure, | ||||
|   | ||||
							
								
								
									
										258
									
								
								block/rbd.c
									
									
									
									
									
								
							
							
						
						
									
										258
									
								
								block/rbd.c
									
									
									
									
									
								
							| @@ -18,7 +18,6 @@ | ||||
| #include "qemu/error-report.h" | ||||
| #include "qemu/option.h" | ||||
| #include "block/block_int.h" | ||||
| #include "block/qdict.h" | ||||
| #include "crypto/secret.h" | ||||
| #include "qemu/cutils.h" | ||||
| #include "qapi/qmp/qstring.h" | ||||
| @@ -227,57 +226,27 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options, | ||||
|  | ||||
| done: | ||||
|     g_free(buf); | ||||
|     qobject_unref(keypairs); | ||||
|     QDECREF(keypairs); | ||||
|     return; | ||||
| } | ||||
|  | ||||
|  | ||||
| static void qemu_rbd_refresh_limits(BlockDriverState *bs, Error **errp) | ||||
| { | ||||
|     /* XXX Does RBD support AIO on less than 512-byte alignment? */ | ||||
|     bs->bl.request_alignment = 512; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int qemu_rbd_set_auth(rados_t cluster, BlockdevOptionsRbd *opts, | ||||
| static int qemu_rbd_set_auth(rados_t cluster, const char *secretid, | ||||
|                              Error **errp) | ||||
| { | ||||
|     char *key, *acr; | ||||
|     int r; | ||||
|     GString *accu; | ||||
|     RbdAuthModeList *auth; | ||||
|  | ||||
|     if (opts->key_secret) { | ||||
|         key = qcrypto_secret_lookup_as_base64(opts->key_secret, errp); | ||||
|         if (!key) { | ||||
|             return -EIO; | ||||
|         } | ||||
|         r = rados_conf_set(cluster, "key", key); | ||||
|         g_free(key); | ||||
|         if (r < 0) { | ||||
|             error_setg_errno(errp, -r, "Could not set 'key'"); | ||||
|             return r; | ||||
|         } | ||||
|     if (secretid == 0) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if (opts->has_auth_client_required) { | ||||
|         accu = g_string_new(""); | ||||
|         for (auth = opts->auth_client_required; auth; auth = auth->next) { | ||||
|             if (accu->str[0]) { | ||||
|                 g_string_append_c(accu, ';'); | ||||
|             } | ||||
|             g_string_append(accu, RbdAuthMode_str(auth->value)); | ||||
|         } | ||||
|         acr = g_string_free(accu, FALSE); | ||||
|         r = rados_conf_set(cluster, "auth_client_required", acr); | ||||
|         g_free(acr); | ||||
|         if (r < 0) { | ||||
|             error_setg_errno(errp, -r, | ||||
|                              "Could not set 'auth_client_required'"); | ||||
|             return r; | ||||
|         } | ||||
|     gchar *secret = qcrypto_secret_lookup_as_base64(secretid, | ||||
|                                                     errp); | ||||
|     if (!secret) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     rados_conf_set(cluster, "key", secret); | ||||
|     g_free(secret); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @@ -294,29 +263,29 @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json, | ||||
|     if (!keypairs_json) { | ||||
|         return ret; | ||||
|     } | ||||
|     keypairs = qobject_to(QList, | ||||
|                           qobject_from_json(keypairs_json, &error_abort)); | ||||
|     keypairs = qobject_to_qlist(qobject_from_json(keypairs_json, | ||||
|                                                   &error_abort)); | ||||
|     remaining = qlist_size(keypairs) / 2; | ||||
|     assert(remaining); | ||||
|  | ||||
|     while (remaining--) { | ||||
|         name = qobject_to(QString, qlist_pop(keypairs)); | ||||
|         value = qobject_to(QString, qlist_pop(keypairs)); | ||||
|         name = qobject_to_qstring(qlist_pop(keypairs)); | ||||
|         value = qobject_to_qstring(qlist_pop(keypairs)); | ||||
|         assert(name && value); | ||||
|         key = qstring_get_str(name); | ||||
|  | ||||
|         ret = rados_conf_set(cluster, key, qstring_get_str(value)); | ||||
|         qobject_unref(value); | ||||
|         QDECREF(value); | ||||
|         if (ret < 0) { | ||||
|             error_setg_errno(errp, -ret, "invalid conf option %s", key); | ||||
|             qobject_unref(name); | ||||
|             QDECREF(name); | ||||
|             ret = -EINVAL; | ||||
|             break; | ||||
|         } | ||||
|         qobject_unref(name); | ||||
|         QDECREF(name); | ||||
|     } | ||||
|  | ||||
|     qobject_unref(keypairs); | ||||
|     QDECREF(keypairs); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| @@ -368,7 +337,9 @@ static QemuOptsList runtime_opts = { | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| /* FIXME Deprecate and remove keypairs or make it available in QMP. */ | ||||
| /* FIXME Deprecate and remove keypairs or make it available in QMP. | ||||
|  * password_secret should eventually be configurable in opts->location. Support | ||||
|  * for it in .bdrv_open will make it work here as well. */ | ||||
| static int qemu_rbd_do_create(BlockdevCreateOptions *options, | ||||
|                               const char *keypairs, const char *password_secret, | ||||
|                               Error **errp) | ||||
| @@ -478,7 +449,7 @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename, | ||||
|     } | ||||
|  | ||||
| exit: | ||||
|     qobject_unref(options); | ||||
|     QDECREF(options); | ||||
|     qapi_free_BlockdevCreateOptions(create_options); | ||||
|     return ret; | ||||
| } | ||||
| @@ -574,16 +545,6 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, | ||||
|     Error *local_err = NULL; | ||||
|     int r; | ||||
|  | ||||
|     if (secretid) { | ||||
|         if (opts->key_secret) { | ||||
|             error_setg(errp, | ||||
|                        "Legacy 'password-secret' clashes with 'key-secret'"); | ||||
|             return -EINVAL; | ||||
|         } | ||||
|         opts->key_secret = g_strdup(secretid); | ||||
|         opts->has_key_secret = true; | ||||
|     } | ||||
|  | ||||
|     mon_host = qemu_rbd_mon_host(opts, &local_err); | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
| @@ -616,8 +577,8 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     r = qemu_rbd_set_auth(*cluster, opts, errp); | ||||
|     if (r < 0) { | ||||
|     if (qemu_rbd_set_auth(*cluster, secretid, errp) < 0) { | ||||
|         r = -EIO; | ||||
|         goto failed_shutdown; | ||||
|     } | ||||
|  | ||||
| @@ -655,66 +616,33 @@ failed_opts: | ||||
|     return r; | ||||
| } | ||||
|  | ||||
| static int qemu_rbd_convert_options(QDict *options, BlockdevOptionsRbd **opts, | ||||
|                                     Error **errp) | ||||
| { | ||||
|     Visitor *v; | ||||
|     Error *local_err = NULL; | ||||
|  | ||||
|     /* Convert the remaining options into a QAPI object */ | ||||
|     v = qobject_input_visitor_new_flat_confused(options, errp); | ||||
|     if (!v) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     visit_type_BlockdevOptionsRbd(v, NULL, opts, &local_err); | ||||
|     visit_free(v); | ||||
|  | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int qemu_rbd_attempt_legacy_options(QDict *options, | ||||
|                                            BlockdevOptionsRbd **opts, | ||||
|                                            char **keypairs) | ||||
| { | ||||
|     char *filename; | ||||
|     int r; | ||||
|  | ||||
|     filename = g_strdup(qdict_get_try_str(options, "filename")); | ||||
|     if (!filename) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|     qdict_del(options, "filename"); | ||||
|  | ||||
|     qemu_rbd_parse_filename(filename, options, NULL); | ||||
|  | ||||
|     /* keypairs freed by caller */ | ||||
|     *keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs")); | ||||
|     if (*keypairs) { | ||||
|         qdict_del(options, "=keyvalue-pairs"); | ||||
|     } | ||||
|  | ||||
|     r = qemu_rbd_convert_options(options, opts, NULL); | ||||
|  | ||||
|     g_free(filename); | ||||
|     return r; | ||||
| } | ||||
|  | ||||
| static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                          Error **errp) | ||||
| { | ||||
|     BDRVRBDState *s = bs->opaque; | ||||
|     BlockdevOptionsRbd *opts = NULL; | ||||
|     const QDictEntry *e; | ||||
|     Visitor *v; | ||||
|     QObject *crumpled = NULL; | ||||
|     Error *local_err = NULL; | ||||
|     const char *filename; | ||||
|     char *keypairs, *secretid; | ||||
|     int r; | ||||
|  | ||||
|     /* If we are given a filename, parse the filename, with precedence given to | ||||
|      * filename encoded options */ | ||||
|     filename = qdict_get_try_str(options, "filename"); | ||||
|     if (filename) { | ||||
|         warn_report("'filename' option specified. " | ||||
|                     "This is an unsupported option, and may be deprecated " | ||||
|                     "in the future"); | ||||
|         qemu_rbd_parse_filename(filename, options, &local_err); | ||||
|         qdict_del(options, "filename"); | ||||
|         if (local_err) { | ||||
|             error_propagate(errp, local_err); | ||||
|             return -EINVAL; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs")); | ||||
|     if (keypairs) { | ||||
|         qdict_del(options, "=keyvalue-pairs"); | ||||
| @@ -725,39 +653,22 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         qdict_del(options, "password-secret"); | ||||
|     } | ||||
|  | ||||
|     r = qemu_rbd_convert_options(options, &opts, &local_err); | ||||
|     if (local_err) { | ||||
|         /* If keypairs are present, that means some options are present in | ||||
|          * the modern option format.  Don't attempt to parse legacy option | ||||
|          * formats, as we won't support mixed usage. */ | ||||
|         if (keypairs) { | ||||
|             error_propagate(errp, local_err); | ||||
|             goto out; | ||||
|         } | ||||
|  | ||||
|         /* If the initial attempt to convert and process the options failed, | ||||
|          * we may be attempting to open an image file that has the rbd options | ||||
|          * specified in the older format consisting of all key/value pairs | ||||
|          * encoded in the filename.  Go ahead and attempt to parse the | ||||
|          * filename, and see if we can pull out the required options. */ | ||||
|         r = qemu_rbd_attempt_legacy_options(options, &opts, &keypairs); | ||||
|         if (r < 0) { | ||||
|             /* Propagate the original error, not the legacy parsing fallback | ||||
|              * error, as the latter was just a best-effort attempt. */ | ||||
|             error_propagate(errp, local_err); | ||||
|             goto out; | ||||
|         } | ||||
|         /* Take care whenever deciding to actually deprecate; once this ability | ||||
|          * is removed, we will not be able to open any images with legacy-styled | ||||
|          * backing image strings. */ | ||||
|         warn_report("RBD options encoded in the filename as keyvalue pairs " | ||||
|                     "is deprecated"); | ||||
|     /* Convert the remaining options into a QAPI object */ | ||||
|     crumpled = qdict_crumple(options, errp); | ||||
|     if (crumpled == NULL) { | ||||
|         r = -EINVAL; | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     /* Remove the processed options from the QDict (the visitor processes | ||||
|      * _all_ options in the QDict) */ | ||||
|     while ((e = qdict_first(options))) { | ||||
|         qdict_del(options, e->key); | ||||
|     v = qobject_input_visitor_new_keyval(crumpled); | ||||
|     visit_type_BlockdevOptionsRbd(v, NULL, &opts, &local_err); | ||||
|     visit_free(v); | ||||
|     qobject_decref(crumpled); | ||||
|  | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         r = -EINVAL; | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     r = qemu_rbd_connect(&s->cluster, &s->io_ctx, opts, | ||||
| @@ -780,10 +691,16 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     /* If we are using an rbd snapshot, we must be r/o, otherwise | ||||
|      * leave as-is */ | ||||
|     if (s->snap != NULL) { | ||||
|         r = bdrv_apply_auto_read_only(bs, "rbd snapshots are read-only", errp); | ||||
|         if (r < 0) { | ||||
|             rbd_close(s->image); | ||||
|             goto failed_open; | ||||
|         if (!bdrv_is_read_only(bs)) { | ||||
|             error_report("Opening rbd snapshots without an explicit " | ||||
|                          "read-only=on option is deprecated. Future versions " | ||||
|                          "will refuse to open the image instead of " | ||||
|                          "automatically marking the image read-only."); | ||||
|             r = bdrv_set_read_only(bs, true, &local_err); | ||||
|             if (r < 0) { | ||||
|                 error_propagate(errp, local_err); | ||||
|                 goto failed_open; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -975,23 +892,27 @@ failed: | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| static BlockAIOCB *qemu_rbd_aio_preadv(BlockDriverState *bs, | ||||
|                                        uint64_t offset, uint64_t bytes, | ||||
|                                        QEMUIOVector *qiov, int flags, | ||||
|                                        BlockCompletionFunc *cb, | ||||
|                                        void *opaque) | ||||
| static BlockAIOCB *qemu_rbd_aio_readv(BlockDriverState *bs, | ||||
|                                       int64_t sector_num, | ||||
|                                       QEMUIOVector *qiov, | ||||
|                                       int nb_sectors, | ||||
|                                       BlockCompletionFunc *cb, | ||||
|                                       void *opaque) | ||||
| { | ||||
|     return rbd_start_aio(bs, offset, qiov, bytes, cb, opaque, | ||||
|     return rbd_start_aio(bs, sector_num << BDRV_SECTOR_BITS, qiov, | ||||
|                          (int64_t) nb_sectors << BDRV_SECTOR_BITS, cb, opaque, | ||||
|                          RBD_AIO_READ); | ||||
| } | ||||
|  | ||||
| static BlockAIOCB *qemu_rbd_aio_pwritev(BlockDriverState *bs, | ||||
|                                         uint64_t offset, uint64_t bytes, | ||||
|                                         QEMUIOVector *qiov, int flags, | ||||
|                                         BlockCompletionFunc *cb, | ||||
|                                         void *opaque) | ||||
| static BlockAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs, | ||||
|                                        int64_t sector_num, | ||||
|                                        QEMUIOVector *qiov, | ||||
|                                        int nb_sectors, | ||||
|                                        BlockCompletionFunc *cb, | ||||
|                                        void *opaque) | ||||
| { | ||||
|     return rbd_start_aio(bs, offset, qiov, bytes, cb, opaque, | ||||
|     return rbd_start_aio(bs, sector_num << BDRV_SECTOR_BITS, qiov, | ||||
|                          (int64_t) nb_sectors << BDRV_SECTOR_BITS, cb, opaque, | ||||
|                          RBD_AIO_WRITE); | ||||
| } | ||||
|  | ||||
| @@ -1046,10 +967,8 @@ static int64_t qemu_rbd_getlength(BlockDriverState *bs) | ||||
|     return info.size; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn qemu_rbd_co_truncate(BlockDriverState *bs, | ||||
|                                              int64_t offset, | ||||
|                                              PreallocMode prealloc, | ||||
|                                              Error **errp) | ||||
| static int qemu_rbd_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                              PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     BDRVRBDState *s = bs->opaque; | ||||
|     int r; | ||||
| @@ -1232,7 +1151,6 @@ static BlockDriver bdrv_rbd = { | ||||
|     .format_name            = "rbd", | ||||
|     .instance_size          = sizeof(BDRVRBDState), | ||||
|     .bdrv_parse_filename    = qemu_rbd_parse_filename, | ||||
|     .bdrv_refresh_limits    = qemu_rbd_refresh_limits, | ||||
|     .bdrv_file_open         = qemu_rbd_open, | ||||
|     .bdrv_close             = qemu_rbd_close, | ||||
|     .bdrv_reopen_prepare    = qemu_rbd_reopen_prepare, | ||||
| @@ -1242,11 +1160,11 @@ static BlockDriver bdrv_rbd = { | ||||
|     .bdrv_get_info          = qemu_rbd_getinfo, | ||||
|     .create_opts            = &qemu_rbd_create_opts, | ||||
|     .bdrv_getlength         = qemu_rbd_getlength, | ||||
|     .bdrv_co_truncate       = qemu_rbd_co_truncate, | ||||
|     .bdrv_truncate          = qemu_rbd_truncate, | ||||
|     .protocol_name          = "rbd", | ||||
|  | ||||
|     .bdrv_aio_preadv        = qemu_rbd_aio_preadv, | ||||
|     .bdrv_aio_pwritev       = qemu_rbd_aio_pwritev, | ||||
|     .bdrv_aio_readv         = qemu_rbd_aio_readv, | ||||
|     .bdrv_aio_writev        = qemu_rbd_aio_writev, | ||||
|  | ||||
| #ifdef LIBRBD_SUPPORTS_AIO_FLUSH | ||||
|     .bdrv_aio_flush         = qemu_rbd_aio_flush, | ||||
|   | ||||
| @@ -20,7 +20,6 @@ | ||||
| #include "block/block_backup.h" | ||||
| #include "sysemu/block-backend.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qapi/qmp/qdict.h" | ||||
| #include "replication.h" | ||||
|  | ||||
| typedef enum { | ||||
| @@ -40,8 +39,8 @@ typedef struct BDRVReplicationState { | ||||
|     char *top_id; | ||||
|     ReplicationState *rs; | ||||
|     Error *blocker; | ||||
|     bool orig_hidden_read_only; | ||||
|     bool orig_secondary_read_only; | ||||
|     int orig_hidden_flags; | ||||
|     int orig_secondary_flags; | ||||
|     int error; | ||||
| } BDRVReplicationState; | ||||
|  | ||||
| @@ -146,7 +145,7 @@ static void replication_close(BlockDriverState *bs) | ||||
|         replication_stop(s->rs, false, NULL); | ||||
|     } | ||||
|     if (s->stage == BLOCK_REPLICATION_FAILOVER) { | ||||
|         job_cancel_sync(&s->active_disk->bs->job->job); | ||||
|         block_job_cancel_sync(s->active_disk->bs->job); | ||||
|     } | ||||
|  | ||||
|     if (s->mode == REPLICATION_MODE_SECONDARY) { | ||||
| @@ -219,6 +218,9 @@ static coroutine_fn int replication_co_readv(BlockDriverState *bs, | ||||
|                                              QEMUIOVector *qiov) | ||||
| { | ||||
|     BDRVReplicationState *s = bs->opaque; | ||||
|     BdrvChild *child = s->secondary_disk; | ||||
|     BlockJob *job = NULL; | ||||
|     CowRequest req; | ||||
|     int ret; | ||||
|  | ||||
|     if (s->mode == REPLICATION_MODE_PRIMARY) { | ||||
| @@ -231,17 +233,34 @@ static coroutine_fn int replication_co_readv(BlockDriverState *bs, | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_co_preadv(bs->file, sector_num * BDRV_SECTOR_SIZE, | ||||
|                          remaining_sectors * BDRV_SECTOR_SIZE, qiov, 0); | ||||
|     if (child && child->bs) { | ||||
|         job = child->bs->job; | ||||
|     } | ||||
|  | ||||
|     if (job) { | ||||
|         uint64_t remaining_bytes = remaining_sectors * BDRV_SECTOR_SIZE; | ||||
|  | ||||
|         backup_wait_for_overlapping_requests(child->bs->job, | ||||
|                                              sector_num * BDRV_SECTOR_SIZE, | ||||
|                                              remaining_bytes); | ||||
|         backup_cow_request_begin(&req, child->bs->job, | ||||
|                                  sector_num * BDRV_SECTOR_SIZE, | ||||
|                                  remaining_bytes); | ||||
|         ret = bdrv_co_readv(bs->file, sector_num, remaining_sectors, | ||||
|                             qiov); | ||||
|         backup_cow_request_end(&req); | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_co_readv(bs->file, sector_num, remaining_sectors, qiov); | ||||
| out: | ||||
|     return replication_return_value(s, ret); | ||||
| } | ||||
|  | ||||
| static coroutine_fn int replication_co_writev(BlockDriverState *bs, | ||||
|                                               int64_t sector_num, | ||||
|                                               int remaining_sectors, | ||||
|                                               QEMUIOVector *qiov, | ||||
|                                               int flags) | ||||
|                                               QEMUIOVector *qiov) | ||||
| { | ||||
|     BDRVReplicationState *s = bs->opaque; | ||||
|     QEMUIOVector hd_qiov; | ||||
| @@ -252,15 +271,14 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs, | ||||
|     int ret; | ||||
|     int64_t n; | ||||
|  | ||||
|     assert(!flags); | ||||
|     ret = replication_get_io_status(s); | ||||
|     if (ret < 0) { | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     if (ret == 0) { | ||||
|         ret = bdrv_co_pwritev(top, sector_num * BDRV_SECTOR_SIZE, | ||||
|                               remaining_sectors * BDRV_SECTOR_SIZE, qiov, 0); | ||||
|         ret = bdrv_co_writev(top, sector_num, | ||||
|                              remaining_sectors, qiov); | ||||
|         return replication_return_value(s, ret); | ||||
|     } | ||||
|  | ||||
| @@ -286,8 +304,7 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs, | ||||
|         qemu_iovec_concat(&hd_qiov, qiov, bytes_done, count); | ||||
|  | ||||
|         target = ret ? top : base; | ||||
|         ret = bdrv_co_pwritev(target, sector_num * BDRV_SECTOR_SIZE, | ||||
|                               n * BDRV_SECTOR_SIZE, &hd_qiov, 0); | ||||
|         ret = bdrv_co_writev(target, sector_num, n, &hd_qiov); | ||||
|         if (ret < 0) { | ||||
|             goto out1; | ||||
|         } | ||||
| @@ -350,38 +367,44 @@ static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* This function is supposed to be called twice: | ||||
|  * first with writable = true, then with writable = false. | ||||
|  * The first call puts s->hidden_disk and s->secondary_disk in | ||||
|  * r/w mode, and the second puts them back in their original state. | ||||
|  */ | ||||
| static void reopen_backing_file(BlockDriverState *bs, bool writable, | ||||
|                                 Error **errp) | ||||
| { | ||||
|     BDRVReplicationState *s = bs->opaque; | ||||
|     BlockReopenQueue *reopen_queue = NULL; | ||||
|     int orig_hidden_flags, orig_secondary_flags; | ||||
|     int new_hidden_flags, new_secondary_flags; | ||||
|     Error *local_err = NULL; | ||||
|  | ||||
|     if (writable) { | ||||
|         s->orig_hidden_read_only = bdrv_is_read_only(s->hidden_disk->bs); | ||||
|         s->orig_secondary_read_only = bdrv_is_read_only(s->secondary_disk->bs); | ||||
|         orig_hidden_flags = s->orig_hidden_flags = | ||||
|                                 bdrv_get_flags(s->hidden_disk->bs); | ||||
|         new_hidden_flags = (orig_hidden_flags | BDRV_O_RDWR) & | ||||
|                                                     ~BDRV_O_INACTIVE; | ||||
|         orig_secondary_flags = s->orig_secondary_flags = | ||||
|                                 bdrv_get_flags(s->secondary_disk->bs); | ||||
|         new_secondary_flags = (orig_secondary_flags | BDRV_O_RDWR) & | ||||
|                                                      ~BDRV_O_INACTIVE; | ||||
|     } else { | ||||
|         orig_hidden_flags = (s->orig_hidden_flags | BDRV_O_RDWR) & | ||||
|                                                     ~BDRV_O_INACTIVE; | ||||
|         new_hidden_flags = s->orig_hidden_flags; | ||||
|         orig_secondary_flags = (s->orig_secondary_flags | BDRV_O_RDWR) & | ||||
|                                                     ~BDRV_O_INACTIVE; | ||||
|         new_secondary_flags = s->orig_secondary_flags; | ||||
|     } | ||||
|  | ||||
|     bdrv_subtree_drained_begin(s->hidden_disk->bs); | ||||
|     bdrv_subtree_drained_begin(s->secondary_disk->bs); | ||||
|  | ||||
|     if (s->orig_hidden_read_only) { | ||||
|         QDict *opts = qdict_new(); | ||||
|         qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable); | ||||
|         reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, | ||||
|                                          opts); | ||||
|     if (orig_hidden_flags != new_hidden_flags) { | ||||
|         reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, NULL, | ||||
|                                          new_hidden_flags); | ||||
|     } | ||||
|  | ||||
|     if (s->orig_secondary_read_only) { | ||||
|         QDict *opts = qdict_new(); | ||||
|         qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable); | ||||
|     if (!(orig_secondary_flags & BDRV_O_RDWR)) { | ||||
|         reopen_queue = bdrv_reopen_queue(reopen_queue, s->secondary_disk->bs, | ||||
|                                          opts); | ||||
|                                          NULL, new_secondary_flags); | ||||
|     } | ||||
|  | ||||
|     if (reopen_queue) { | ||||
| @@ -543,7 +566,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | ||||
|         job = backup_job_create(NULL, s->secondary_disk->bs, s->hidden_disk->bs, | ||||
|                                 0, MIRROR_SYNC_MODE_NONE, NULL, false, | ||||
|                                 BLOCKDEV_ON_ERROR_REPORT, | ||||
|                                 BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL, | ||||
|                                 BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL, | ||||
|                                 backup_job_completed, bs, NULL, &local_err); | ||||
|         if (local_err) { | ||||
|             error_propagate(errp, local_err); | ||||
| @@ -551,7 +574,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | ||||
|             aio_context_release(aio_context); | ||||
|             return; | ||||
|         } | ||||
|         job_start(&job->job); | ||||
|         block_job_start(job); | ||||
|         break; | ||||
|     default: | ||||
|         aio_context_release(aio_context); | ||||
| @@ -656,7 +679,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) | ||||
|          * disk, secondary disk in backup_job_completed(). | ||||
|          */ | ||||
|         if (s->secondary_disk->bs->job) { | ||||
|             job_cancel_sync(&s->secondary_disk->bs->job->job); | ||||
|             block_job_cancel_sync(s->secondary_disk->bs->job); | ||||
|         } | ||||
|  | ||||
|         if (!failover) { | ||||
| @@ -668,7 +691,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) | ||||
|  | ||||
|         s->stage = BLOCK_REPLICATION_FAILOVER; | ||||
|         commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs, | ||||
|                             JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT, | ||||
|                             BLOCK_JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT, | ||||
|                             NULL, replication_done, bs, true, errp); | ||||
|         break; | ||||
|     default: | ||||
|   | ||||
							
								
								
									
										144
									
								
								block/sheepdog.c
									
									
									
									
									
								
							
							
						
						
									
										144
									
								
								block/sheepdog.c
									
									
									
									
									
								
							| @@ -24,11 +24,9 @@ | ||||
| #include "qemu/option.h" | ||||
| #include "qemu/sockets.h" | ||||
| #include "block/block_int.h" | ||||
| #include "block/qdict.h" | ||||
| #include "sysemu/block-backend.h" | ||||
| #include "qemu/bitops.h" | ||||
| #include "qemu/cutils.h" | ||||
| #include "trace.h" | ||||
|  | ||||
| #define SD_PROTO_VER 0x01 | ||||
|  | ||||
| @@ -300,6 +298,19 @@ static inline size_t count_data_objs(const struct SheepdogInode *inode) | ||||
|                         (1UL << inode->block_size_shift)); | ||||
| } | ||||
|  | ||||
| #undef DPRINTF | ||||
| #ifdef DEBUG_SDOG | ||||
| #define DEBUG_SDOG_PRINT 1 | ||||
| #else | ||||
| #define DEBUG_SDOG_PRINT 0 | ||||
| #endif | ||||
| #define DPRINTF(fmt, args...)                                           \ | ||||
|     do {                                                                \ | ||||
|         if (DEBUG_SDOG_PRINT) {                                         \ | ||||
|             fprintf(stderr, "%s %d: " fmt, __func__, __LINE__, ##args); \ | ||||
|         }                                                               \ | ||||
|     } while (0) | ||||
|  | ||||
| typedef struct SheepdogAIOCB SheepdogAIOCB; | ||||
| typedef struct BDRVSheepdogState BDRVSheepdogState; | ||||
|  | ||||
| @@ -379,12 +390,12 @@ struct BDRVSheepdogState { | ||||
|     uint32_t aioreq_seq_num; | ||||
|  | ||||
|     /* Every aio request must be linked to either of these queues. */ | ||||
|     QLIST_HEAD(, AIOReq) inflight_aio_head; | ||||
|     QLIST_HEAD(, AIOReq) failed_aio_head; | ||||
|     QLIST_HEAD(inflight_aio_head, AIOReq) inflight_aio_head; | ||||
|     QLIST_HEAD(failed_aio_head, AIOReq) failed_aio_head; | ||||
|  | ||||
|     CoMutex queue_lock; | ||||
|     CoQueue overlapping_queue; | ||||
|     QLIST_HEAD(, SheepdogAIOCB) inflight_aiocb_head; | ||||
|     QLIST_HEAD(inflight_aiocb_head, SheepdogAIOCB) inflight_aiocb_head; | ||||
| }; | ||||
|  | ||||
| typedef struct BDRVSheepdogReopenState { | ||||
| @@ -527,17 +538,27 @@ static void sd_aio_setup(SheepdogAIOCB *acb, BDRVSheepdogState *s, | ||||
| static SocketAddress *sd_server_config(QDict *options, Error **errp) | ||||
| { | ||||
|     QDict *server = NULL; | ||||
|     QObject *crumpled_server = NULL; | ||||
|     Visitor *iv = NULL; | ||||
|     SocketAddress *saddr = NULL; | ||||
|     Error *local_err = NULL; | ||||
|  | ||||
|     qdict_extract_subqdict(options, &server, "server."); | ||||
|  | ||||
|     iv = qobject_input_visitor_new_flat_confused(server, errp); | ||||
|     if (!iv) { | ||||
|     crumpled_server = qdict_crumple(server, errp); | ||||
|     if (!crumpled_server) { | ||||
|         goto done; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive | ||||
|      * server.type=inet.  .to doesn't matter, it's ignored anyway. | ||||
|      * That's because when @options come from -blockdev or | ||||
|      * blockdev_add, members are typed according to the QAPI schema, | ||||
|      * but when they come from -drive, they're all QString.  The | ||||
|      * visitor expects the former. | ||||
|      */ | ||||
|     iv = qobject_input_visitor_new(crumpled_server); | ||||
|     visit_type_SocketAddress(iv, NULL, &saddr, &local_err); | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
| @@ -546,7 +567,8 @@ static SocketAddress *sd_server_config(QDict *options, Error **errp) | ||||
|  | ||||
| done: | ||||
|     visit_free(iv); | ||||
|     qobject_unref(server); | ||||
|     qobject_decref(crumpled_server); | ||||
|     QDECREF(server); | ||||
|     return saddr; | ||||
| } | ||||
|  | ||||
| @@ -560,7 +582,7 @@ static int connect_to_sdog(BDRVSheepdogState *s, Error **errp) | ||||
|     if (s->addr->type == SOCKET_ADDRESS_TYPE_INET && fd >= 0) { | ||||
|         int ret = socket_set_nodelay(fd); | ||||
|         if (ret < 0) { | ||||
|             warn_report("can't set TCP_NODELAY: %s", strerror(errno)); | ||||
|             error_report("%s", strerror(errno)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -738,7 +760,7 @@ static coroutine_fn void reconnect_to_sdog(void *opaque) | ||||
|         Error *local_err = NULL; | ||||
|         s->fd = get_sheep_fd(s, &local_err); | ||||
|         if (s->fd < 0) { | ||||
|             trace_sheepdog_reconnect_to_sdog(); | ||||
|             DPRINTF("Wait for connection to be established\n"); | ||||
|             error_report_err(local_err); | ||||
|             qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000000ULL); | ||||
|         } | ||||
| @@ -835,7 +857,7 @@ static void coroutine_fn aio_read_response(void *opaque) | ||||
|         break; | ||||
|     case AIOCB_FLUSH_CACHE: | ||||
|         if (rsp.result == SD_RES_INVALID_PARMS) { | ||||
|             trace_sheepdog_aio_read_response(); | ||||
|             DPRINTF("disable cache since the server doesn't support it\n"); | ||||
|             s->cache_flags = SD_FLAG_CMD_DIRECT; | ||||
|             rsp.result = SD_RES_SUCCESS; | ||||
|         } | ||||
| @@ -1014,7 +1036,7 @@ static void sd_parse_uri(SheepdogConfig *cfg, const char *filename, | ||||
|  | ||||
|     cfg->uri = uri = uri_parse(filename); | ||||
|     if (!uri) { | ||||
|         error_setg(&err, "invalid URI '%s'", filename); | ||||
|         error_setg(&err, "invalid URI"); | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
| @@ -1212,7 +1234,7 @@ static int find_vdi_name(BDRVSheepdogState *s, const char *filename, | ||||
|     SheepdogVdiReq hdr; | ||||
|     SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr; | ||||
|     unsigned int wlen, rlen = 0; | ||||
|     char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN] QEMU_NONSTRING; | ||||
|     char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN]; | ||||
|  | ||||
|     fd = connect_to_sdog(s, errp); | ||||
|     if (fd < 0) { | ||||
| @@ -1627,7 +1649,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     s->discard_supported = true; | ||||
|  | ||||
|     if (snap_id || tag[0]) { | ||||
|         trace_sheepdog_open(vid); | ||||
|         DPRINTF("%" PRIx32 " snapshot inode was open.\n", vid); | ||||
|         s->is_snapshot = true; | ||||
|     } | ||||
|  | ||||
| @@ -1837,7 +1859,9 @@ out: | ||||
|         error_setg_errno(errp, -ret, "Can't pre-allocate"); | ||||
|     } | ||||
| out_with_err_set: | ||||
|     blk_unref(blk); | ||||
|     if (blk) { | ||||
|         blk_unref(blk); | ||||
|     } | ||||
|     g_free(buf); | ||||
|  | ||||
|     return ret; | ||||
| @@ -1859,11 +1883,11 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size, | ||||
|  | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         qobject_unref(obj); | ||||
|         qobject_decref(obj); | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     qdict = qobject_to(QDict, obj); | ||||
|     qdict = qobject_to_qdict(obj); | ||||
|     qdict_flatten(qdict); | ||||
|  | ||||
|     qdict_put_str(qdict, "driver", "sheepdog"); | ||||
| @@ -1877,7 +1901,7 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size, | ||||
|     ret = sd_prealloc(bs, 0, size, errp); | ||||
| fail: | ||||
|     bdrv_unref(bs); | ||||
|     qobject_unref(qdict); | ||||
|     QDECREF(qdict); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| @@ -1963,7 +1987,6 @@ static SheepdogRedundancy *parse_redundancy_str(const char *opt) | ||||
|     } else { | ||||
|         ret = qemu_strtol(n2, NULL, 10, &parity); | ||||
|         if (ret < 0) { | ||||
|             g_free(redundancy); | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
| @@ -2158,8 +2181,9 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, | ||||
| { | ||||
|     BlockdevCreateOptions *create_options = NULL; | ||||
|     QDict *qdict, *location_qdict; | ||||
|     QObject *crumpled; | ||||
|     Visitor *v; | ||||
|     char *redundancy; | ||||
|     const char *redundancy; | ||||
|     Error *local_err = NULL; | ||||
|     int ret; | ||||
|  | ||||
| @@ -2193,14 +2217,16 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|     } | ||||
|  | ||||
|     /* Get the QAPI object */ | ||||
|     v = qobject_input_visitor_new_flat_confused(qdict, errp); | ||||
|     if (!v) { | ||||
|     crumpled = qdict_crumple(qdict, errp); | ||||
|     if (crumpled == NULL) { | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     v = qobject_input_visitor_new_keyval(crumpled); | ||||
|     visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); | ||||
|     visit_free(v); | ||||
|     qobject_decref(crumpled); | ||||
|  | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
| @@ -2226,8 +2252,7 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|     ret = sd_co_create(create_options, errp); | ||||
| fail: | ||||
|     qapi_free_BlockdevCreateOptions(create_options); | ||||
|     qobject_unref(qdict); | ||||
|     g_free(redundancy); | ||||
|     QDECREF(qdict); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| @@ -2240,7 +2265,7 @@ static void sd_close(BlockDriverState *bs) | ||||
|     unsigned int wlen, rlen = 0; | ||||
|     int fd, ret; | ||||
|  | ||||
|     trace_sheepdog_close(s->name); | ||||
|     DPRINTF("%s\n", s->name); | ||||
|  | ||||
|     fd = connect_to_sdog(s, &local_err); | ||||
|     if (fd < 0) { | ||||
| @@ -2280,8 +2305,8 @@ static int64_t sd_getlength(BlockDriverState *bs) | ||||
|     return s->inode.vdi_size; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn sd_co_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                                        PreallocMode prealloc, Error **errp) | ||||
| static int sd_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                        PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     BDRVSheepdogState *s = bs->opaque; | ||||
|     int ret, fd; | ||||
| @@ -2310,7 +2335,7 @@ static int coroutine_fn sd_co_truncate(BlockDriverState *bs, int64_t offset, | ||||
|     } | ||||
|  | ||||
|     /* we don't need to update entire object */ | ||||
|     datalen = SD_INODE_HEADER_SIZE; | ||||
|     datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id); | ||||
|     s->inode.vdi_size = offset; | ||||
|     ret = write_object(fd, s->bs, (char *)&s->inode, | ||||
|                        vid_to_vdi_oid(s->inode.vdi_id), s->inode.nr_copies, | ||||
| @@ -2417,7 +2442,7 @@ static int sd_create_branch(BDRVSheepdogState *s) | ||||
|     char *buf; | ||||
|     bool deleted; | ||||
|  | ||||
|     trace_sheepdog_create_branch_snapshot(s->inode.vdi_id); | ||||
|     DPRINTF("%" PRIx32 " is snapshot.\n", s->inode.vdi_id); | ||||
|  | ||||
|     buf = g_malloc(SD_INODE_SIZE); | ||||
|  | ||||
| @@ -2433,7 +2458,7 @@ static int sd_create_branch(BDRVSheepdogState *s) | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     trace_sheepdog_create_branch_created(vid); | ||||
|     DPRINTF("%" PRIx32 " is created.\n", vid); | ||||
|  | ||||
|     fd = connect_to_sdog(s, &local_err); | ||||
|     if (fd < 0) { | ||||
| @@ -2455,7 +2480,7 @@ static int sd_create_branch(BDRVSheepdogState *s) | ||||
|  | ||||
|     s->is_snapshot = false; | ||||
|     ret = 0; | ||||
|     trace_sheepdog_create_branch_new(s->inode.vdi_id); | ||||
|     DPRINTF("%" PRIx32 " was newly created.\n", s->inode.vdi_id); | ||||
|  | ||||
| out: | ||||
|     g_free(buf); | ||||
| @@ -2549,11 +2574,11 @@ static void coroutine_fn sd_co_rw_vector(SheepdogAIOCB *acb) | ||||
|         } | ||||
|  | ||||
|         if (create) { | ||||
|             trace_sheepdog_co_rw_vector_update(inode->vdi_id, oid, | ||||
|                                   vid_to_data_oid(inode->data_vdi_id[idx], idx), | ||||
|                                   idx); | ||||
|             DPRINTF("update ino (%" PRIu32 ") %" PRIu64 " %" PRIu64 " %ld\n", | ||||
|                     inode->vdi_id, oid, | ||||
|                     vid_to_data_oid(inode->data_vdi_id[idx], idx), idx); | ||||
|             oid = vid_to_data_oid(inode->vdi_id, idx); | ||||
|             trace_sheepdog_co_rw_vector_new(oid); | ||||
|             DPRINTF("new oid %" PRIx64 "\n", oid); | ||||
|         } | ||||
|  | ||||
|         aio_req = alloc_aio_req(s, acb, oid, len, offset, flags, create, | ||||
| @@ -2587,17 +2612,15 @@ static void sd_aio_complete(SheepdogAIOCB *acb) | ||||
| } | ||||
|  | ||||
| static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num, | ||||
|                                      int nb_sectors, QEMUIOVector *qiov, | ||||
|                                      int flags) | ||||
|                         int nb_sectors, QEMUIOVector *qiov) | ||||
| { | ||||
|     SheepdogAIOCB acb; | ||||
|     int ret; | ||||
|     int64_t offset = (sector_num + nb_sectors) * BDRV_SECTOR_SIZE; | ||||
|     BDRVSheepdogState *s = bs->opaque; | ||||
|  | ||||
|     assert(!flags); | ||||
|     if (offset > s->inode.vdi_size) { | ||||
|         ret = sd_co_truncate(bs, offset, PREALLOC_MODE_OFF, NULL); | ||||
|         ret = sd_truncate(bs, offset, PREALLOC_MODE_OFF, NULL); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
| @@ -2658,8 +2681,9 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) | ||||
|     SheepdogInode *inode; | ||||
|     unsigned int datalen; | ||||
|  | ||||
|     trace_sheepdog_snapshot_create_info(sn_info->name, sn_info->id_str, s->name, | ||||
|                                         sn_info->vm_state_size, s->is_snapshot); | ||||
|     DPRINTF("sn_info: name %s id_str %s s: name %s vm_state_size %" PRId64 " " | ||||
|             "is_snapshot %d\n", sn_info->name, sn_info->id_str, | ||||
|             s->name, sn_info->vm_state_size, s->is_snapshot); | ||||
|  | ||||
|     if (s->is_snapshot) { | ||||
|         error_report("You can't create a snapshot of a snapshot VDI, " | ||||
| @@ -2668,7 +2692,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     trace_sheepdog_snapshot_create(sn_info->name, sn_info->id_str); | ||||
|     DPRINTF("%s %s\n", sn_info->name, sn_info->id_str); | ||||
|  | ||||
|     s->inode.vm_state_size = sn_info->vm_state_size; | ||||
|     s->inode.vm_clock_nsec = sn_info->vm_clock_nsec; | ||||
| @@ -2677,7 +2701,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) | ||||
|      */ | ||||
|     strncpy(s->inode.tag, sn_info->name, sizeof(s->inode.tag)); | ||||
|     /* we don't need to update entire object */ | ||||
|     datalen = SD_INODE_HEADER_SIZE; | ||||
|     datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id); | ||||
|     inode = g_malloc(datalen); | ||||
|  | ||||
|     /* refresh inode. */ | ||||
| @@ -2713,8 +2737,8 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) | ||||
|     } | ||||
|  | ||||
|     memcpy(&s->inode, inode, datalen); | ||||
|     trace_sheepdog_snapshot_create_inode(s->inode.name, s->inode.snap_id, | ||||
|                                          s->inode.vdi_id); | ||||
|     DPRINTF("s->inode: name %s snap_id %x oid %x\n", | ||||
|             s->inode.name, s->inode.snap_id, s->inode.vdi_id); | ||||
|  | ||||
| cleanup: | ||||
|     g_free(inode); | ||||
| @@ -2912,14 +2936,13 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) | ||||
|     QEMUSnapshotInfo *sn_tab = NULL; | ||||
|     unsigned wlen, rlen; | ||||
|     int found = 0; | ||||
|     SheepdogInode *inode; | ||||
|     static SheepdogInode inode; | ||||
|     unsigned long *vdi_inuse; | ||||
|     unsigned int start_nr; | ||||
|     uint64_t hval; | ||||
|     uint32_t vid; | ||||
|  | ||||
|     vdi_inuse = g_malloc(max); | ||||
|     inode = g_malloc(SD_INODE_HEADER_SIZE); | ||||
|  | ||||
|     fd = connect_to_sdog(s, &local_err); | ||||
|     if (fd < 0) { | ||||
| @@ -2962,26 +2985,26 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) | ||||
|         } | ||||
|  | ||||
|         /* we don't need to read entire object */ | ||||
|         ret = read_object(fd, s->bs, (char *)inode, | ||||
|         ret = read_object(fd, s->bs, (char *)&inode, | ||||
|                           vid_to_vdi_oid(vid), | ||||
|                           0, SD_INODE_HEADER_SIZE, 0, | ||||
|                           0, SD_INODE_SIZE - sizeof(inode.data_vdi_id), 0, | ||||
|                           s->cache_flags); | ||||
|  | ||||
|         if (ret) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         if (!strcmp(inode->name, s->name) && is_snapshot(inode)) { | ||||
|             sn_tab[found].date_sec = inode->snap_ctime >> 32; | ||||
|             sn_tab[found].date_nsec = inode->snap_ctime & 0xffffffff; | ||||
|             sn_tab[found].vm_state_size = inode->vm_state_size; | ||||
|             sn_tab[found].vm_clock_nsec = inode->vm_clock_nsec; | ||||
|         if (!strcmp(inode.name, s->name) && is_snapshot(&inode)) { | ||||
|             sn_tab[found].date_sec = inode.snap_ctime >> 32; | ||||
|             sn_tab[found].date_nsec = inode.snap_ctime & 0xffffffff; | ||||
|             sn_tab[found].vm_state_size = inode.vm_state_size; | ||||
|             sn_tab[found].vm_clock_nsec = inode.vm_clock_nsec; | ||||
|  | ||||
|             snprintf(sn_tab[found].id_str, sizeof(sn_tab[found].id_str), | ||||
|                      "%" PRIu32, inode->snap_id); | ||||
|                      "%" PRIu32, inode.snap_id); | ||||
|             pstrcpy(sn_tab[found].name, | ||||
|                     MIN(sizeof(sn_tab[found].name), sizeof(inode->tag)), | ||||
|                     inode->tag); | ||||
|                     MIN(sizeof(sn_tab[found].name), sizeof(inode.tag)), | ||||
|                     inode.tag); | ||||
|             found++; | ||||
|         } | ||||
|     } | ||||
| @@ -2991,7 +3014,6 @@ out: | ||||
|     *psn_tab = sn_tab; | ||||
|  | ||||
|     g_free(vdi_inuse); | ||||
|     g_free(inode); | ||||
|  | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
| @@ -3218,7 +3240,7 @@ static BlockDriver bdrv_sheepdog = { | ||||
|     .bdrv_has_zero_init           = bdrv_has_zero_init_1, | ||||
|     .bdrv_getlength               = sd_getlength, | ||||
|     .bdrv_get_allocated_file_size = sd_get_allocated_file_size, | ||||
|     .bdrv_co_truncate             = sd_co_truncate, | ||||
|     .bdrv_truncate                = sd_truncate, | ||||
|  | ||||
|     .bdrv_co_readv                = sd_co_readv, | ||||
|     .bdrv_co_writev               = sd_co_writev, | ||||
| @@ -3255,7 +3277,7 @@ static BlockDriver bdrv_sheepdog_tcp = { | ||||
|     .bdrv_has_zero_init           = bdrv_has_zero_init_1, | ||||
|     .bdrv_getlength               = sd_getlength, | ||||
|     .bdrv_get_allocated_file_size = sd_get_allocated_file_size, | ||||
|     .bdrv_co_truncate             = sd_co_truncate, | ||||
|     .bdrv_truncate                = sd_truncate, | ||||
|  | ||||
|     .bdrv_co_readv                = sd_co_readv, | ||||
|     .bdrv_co_writev               = sd_co_writev, | ||||
| @@ -3292,7 +3314,7 @@ static BlockDriver bdrv_sheepdog_unix = { | ||||
|     .bdrv_has_zero_init           = bdrv_has_zero_init_1, | ||||
|     .bdrv_getlength               = sd_getlength, | ||||
|     .bdrv_get_allocated_file_size = sd_get_allocated_file_size, | ||||
|     .bdrv_co_truncate             = sd_co_truncate, | ||||
|     .bdrv_truncate                = sd_truncate, | ||||
|  | ||||
|     .bdrv_co_readv                = sd_co_readv, | ||||
|     .bdrv_co_writev               = sd_co_writev, | ||||
|   | ||||
| @@ -25,7 +25,6 @@ | ||||
| #include "qemu/osdep.h" | ||||
| #include "block/snapshot.h" | ||||
| #include "block/block_int.h" | ||||
| #include "block/qdict.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qapi/qmp/qdict.h" | ||||
| #include "qapi/qmp/qerror.h" | ||||
| @@ -215,18 +214,16 @@ int bdrv_snapshot_goto(BlockDriverState *bs, | ||||
|         bdrv_ref(file); | ||||
|  | ||||
|         qdict_extract_subqdict(options, &file_options, "file."); | ||||
|         qobject_unref(file_options); | ||||
|         QDECREF(file_options); | ||||
|         qdict_put_str(options, "file", bdrv_get_node_name(file)); | ||||
|  | ||||
|         if (drv->bdrv_close) { | ||||
|             drv->bdrv_close(bs); | ||||
|         } | ||||
|         drv->bdrv_close(bs); | ||||
|         bdrv_unref_child(bs, bs->file); | ||||
|         bs->file = NULL; | ||||
|  | ||||
|         ret = bdrv_snapshot_goto(file, snapshot_id, errp); | ||||
|         open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err); | ||||
|         qobject_unref(options); | ||||
|         QDECREF(options); | ||||
|         if (open_ret < 0) { | ||||
|             bdrv_unref(file); | ||||
|             bs->drv = NULL; | ||||
|   | ||||
							
								
								
									
										74
									
								
								block/ssh.c
									
									
									
									
									
								
							
							
						
						
									
										74
									
								
								block/ssh.c
									
									
									
									
									
								
							| @@ -28,7 +28,6 @@ | ||||
| #include <libssh2_sftp.h> | ||||
|  | ||||
| #include "block/block_int.h" | ||||
| #include "block/qdict.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "qemu/option.h" | ||||
| @@ -41,17 +40,27 @@ | ||||
| #include "qapi/qmp/qstring.h" | ||||
| #include "qapi/qobject-input-visitor.h" | ||||
| #include "qapi/qobject-output-visitor.h" | ||||
| #include "trace.h" | ||||
|  | ||||
| /* | ||||
| /* DEBUG_SSH=1 enables the DPRINTF (debugging printf) statements in | ||||
|  * this block driver code. | ||||
|  * | ||||
|  * TRACE_LIBSSH2=<bitmask> enables tracing in libssh2 itself.  Note | ||||
|  * that this requires that libssh2 was specially compiled with the | ||||
|  * `./configure --enable-debug' option, so most likely you will have | ||||
|  * to compile it yourself.  The meaning of <bitmask> is described | ||||
|  * here: http://www.libssh2.org/libssh2_trace.html | ||||
|  */ | ||||
| #define DEBUG_SSH     0 | ||||
| #define TRACE_LIBSSH2 0 /* or try: LIBSSH2_TRACE_SFTP */ | ||||
|  | ||||
| #define DPRINTF(fmt, ...)                           \ | ||||
|     do {                                            \ | ||||
|         if (DEBUG_SSH) {                            \ | ||||
|             fprintf(stderr, "ssh: %-15s " fmt "\n", \ | ||||
|                     __func__, ##__VA_ARGS__);       \ | ||||
|         }                                           \ | ||||
|     } while (0) | ||||
|  | ||||
| typedef struct BDRVSSHState { | ||||
|     /* Coroutine. */ | ||||
|     CoMutex lock; | ||||
| @@ -326,7 +335,7 @@ static int check_host_key_knownhosts(BDRVSSHState *s, | ||||
|     switch (r) { | ||||
|     case LIBSSH2_KNOWNHOST_CHECK_MATCH: | ||||
|         /* OK */ | ||||
|         trace_ssh_check_host_key_knownhosts(found->key); | ||||
|         DPRINTF("host key OK: %s", found->key); | ||||
|         break; | ||||
|     case LIBSSH2_KNOWNHOST_CHECK_MISMATCH: | ||||
|         ret = -EINVAL; | ||||
| @@ -596,6 +605,7 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp) | ||||
|     BlockdevOptionsSsh *result = NULL; | ||||
|     QemuOpts *opts = NULL; | ||||
|     Error *local_err = NULL; | ||||
|     QObject *crumpled; | ||||
|     const QDictEntry *e; | ||||
|     Visitor *v; | ||||
|  | ||||
| @@ -612,13 +622,23 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp) | ||||
|     } | ||||
|  | ||||
|     /* Create the QAPI object */ | ||||
|     v = qobject_input_visitor_new_flat_confused(options, errp); | ||||
|     if (!v) { | ||||
|     crumpled = qdict_crumple(options, errp); | ||||
|     if (crumpled == NULL) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive. | ||||
|      * .to doesn't matter, it's ignored anyway. | ||||
|      * That's because when @options come from -blockdev or | ||||
|      * blockdev_add, members are typed according to the QAPI schema, | ||||
|      * but when they come from -drive, they're all QString.  The | ||||
|      * visitor expects the former. | ||||
|      */ | ||||
|     v = qobject_input_visitor_new(crumpled); | ||||
|     visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err); | ||||
|     visit_free(v); | ||||
|     qobject_decref(crumpled); | ||||
|  | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
| @@ -711,7 +731,8 @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts, | ||||
|     } | ||||
|  | ||||
|     /* Open the remote file. */ | ||||
|     trace_ssh_connect_to_ssh(opts->path, ssh_flags, creat_mode); | ||||
|     DPRINTF("opening file %s flags=0x%x creat_mode=0%o", | ||||
|             opts->path, ssh_flags, creat_mode); | ||||
|     s->sftp_handle = libssh2_sftp_open(s->sftp, opts->path, ssh_flags, | ||||
|                                        creat_mode); | ||||
|     if (!s->sftp_handle) { | ||||
| @@ -879,7 +900,7 @@ static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|     /* Get desired file size. */ | ||||
|     ssh_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), | ||||
|                               BDRV_SECTOR_SIZE); | ||||
|     trace_ssh_co_create_opts(ssh_opts->size); | ||||
|     DPRINTF("total_size=%" PRIi64, ssh_opts->size); | ||||
|  | ||||
|     uri_options = qdict_new(); | ||||
|     ret = parse_uri(filename, uri_options, errp); | ||||
| @@ -896,7 +917,7 @@ static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|     ret = ssh_co_create(create_options, errp); | ||||
|  | ||||
|  out: | ||||
|     qobject_unref(uri_options); | ||||
|     QDECREF(uri_options); | ||||
|     qapi_free_BlockdevCreateOptions(create_options); | ||||
|     return ret; | ||||
| } | ||||
| @@ -935,7 +956,7 @@ static void restart_coroutine(void *opaque) | ||||
|     BDRVSSHState *s = bs->opaque; | ||||
|     AioContext *ctx = bdrv_get_aio_context(bs); | ||||
|  | ||||
|     trace_ssh_restart_coroutine(restart->co); | ||||
|     DPRINTF("co=%p", restart->co); | ||||
|     aio_set_fd_handler(ctx, s->sock, false, NULL, NULL, NULL, NULL); | ||||
|  | ||||
|     aio_co_wake(restart->co); | ||||
| @@ -963,12 +984,13 @@ static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs) | ||||
|         wr_handler = restart_coroutine; | ||||
|     } | ||||
|  | ||||
|     trace_ssh_co_yield(s->sock, rd_handler, wr_handler); | ||||
|     DPRINTF("s->sock=%d rd_handler=%p wr_handler=%p", s->sock, | ||||
|             rd_handler, wr_handler); | ||||
|  | ||||
|     aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock, | ||||
|                        false, rd_handler, wr_handler, NULL, &restart); | ||||
|     qemu_coroutine_yield(); | ||||
|     trace_ssh_co_yield_back(s->sock); | ||||
|     DPRINTF("s->sock=%d - back", s->sock); | ||||
| } | ||||
|  | ||||
| /* SFTP has a function `libssh2_sftp_seek64' which seeks to a position | ||||
| @@ -991,7 +1013,7 @@ static void ssh_seek(BDRVSSHState *s, int64_t offset, int flags) | ||||
|     bool force = (flags & SSH_SEEK_FORCE) != 0; | ||||
|  | ||||
|     if (force || op_read != s->offset_op_read || offset != s->offset) { | ||||
|         trace_ssh_seek(offset); | ||||
|         DPRINTF("seeking to offset=%" PRIi64, offset); | ||||
|         libssh2_sftp_seek64(s->sftp_handle, offset); | ||||
|         s->offset = offset; | ||||
|         s->offset_op_read = op_read; | ||||
| @@ -1007,7 +1029,7 @@ static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs, | ||||
|     char *buf, *end_of_vec; | ||||
|     struct iovec *i; | ||||
|  | ||||
|     trace_ssh_read(offset, size); | ||||
|     DPRINTF("offset=%" PRIi64 " size=%zu", offset, size); | ||||
|  | ||||
|     ssh_seek(s, offset, SSH_SEEK_READ); | ||||
|  | ||||
| @@ -1026,9 +1048,9 @@ static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs, | ||||
|      */ | ||||
|     for (got = 0; got < size; ) { | ||||
|     again: | ||||
|         trace_ssh_read_buf(buf, end_of_vec - buf); | ||||
|         DPRINTF("sftp_read buf=%p size=%zu", buf, end_of_vec - buf); | ||||
|         r = libssh2_sftp_read(s->sftp_handle, buf, end_of_vec - buf); | ||||
|         trace_ssh_read_return(r); | ||||
|         DPRINTF("sftp_read returned %zd", r); | ||||
|  | ||||
|         if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) { | ||||
|             co_yield(s, bs); | ||||
| @@ -1082,7 +1104,7 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs, | ||||
|     char *buf, *end_of_vec; | ||||
|     struct iovec *i; | ||||
|  | ||||
|     trace_ssh_write(offset, size); | ||||
|     DPRINTF("offset=%" PRIi64 " size=%zu", offset, size); | ||||
|  | ||||
|     ssh_seek(s, offset, SSH_SEEK_WRITE); | ||||
|  | ||||
| @@ -1096,9 +1118,9 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs, | ||||
|  | ||||
|     for (written = 0; written < size; ) { | ||||
|     again: | ||||
|         trace_ssh_write_buf(buf, end_of_vec - buf); | ||||
|         DPRINTF("sftp_write buf=%p size=%zu", buf, end_of_vec - buf); | ||||
|         r = libssh2_sftp_write(s->sftp_handle, buf, end_of_vec - buf); | ||||
|         trace_ssh_write_return(r); | ||||
|         DPRINTF("sftp_write returned %zd", r); | ||||
|  | ||||
|         if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) { | ||||
|             co_yield(s, bs); | ||||
| @@ -1142,13 +1164,11 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs, | ||||
|  | ||||
| static coroutine_fn int ssh_co_writev(BlockDriverState *bs, | ||||
|                                       int64_t sector_num, | ||||
|                                       int nb_sectors, QEMUIOVector *qiov, | ||||
|                                       int flags) | ||||
|                                       int nb_sectors, QEMUIOVector *qiov) | ||||
| { | ||||
|     BDRVSSHState *s = bs->opaque; | ||||
|     int ret; | ||||
|  | ||||
|     assert(!flags); | ||||
|     qemu_co_mutex_lock(&s->lock); | ||||
|     ret = ssh_write(s, bs, sector_num * BDRV_SECTOR_SIZE, | ||||
|                     nb_sectors * BDRV_SECTOR_SIZE, qiov); | ||||
| @@ -1175,7 +1195,7 @@ static coroutine_fn int ssh_flush(BDRVSSHState *s, BlockDriverState *bs) | ||||
| { | ||||
|     int r; | ||||
|  | ||||
|     trace_ssh_flush(); | ||||
|     DPRINTF("fsync"); | ||||
|  again: | ||||
|     r = libssh2_sftp_fsync(s->sftp_handle); | ||||
|     if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) { | ||||
| @@ -1226,13 +1246,13 @@ static int64_t ssh_getlength(BlockDriverState *bs) | ||||
|  | ||||
|     /* Note we cannot make a libssh2 call here. */ | ||||
|     length = (int64_t) s->attrs.filesize; | ||||
|     trace_ssh_getlength(length); | ||||
|     DPRINTF("length=%" PRIi64, length); | ||||
|  | ||||
|     return length; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                                         PreallocMode prealloc, Error **errp) | ||||
| static int ssh_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                         PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     BDRVSSHState *s = bs->opaque; | ||||
|  | ||||
| @@ -1267,7 +1287,7 @@ static BlockDriver bdrv_ssh = { | ||||
|     .bdrv_co_readv                = ssh_co_readv, | ||||
|     .bdrv_co_writev               = ssh_co_writev, | ||||
|     .bdrv_getlength               = ssh_getlength, | ||||
|     .bdrv_co_truncate             = ssh_co_truncate, | ||||
|     .bdrv_truncate                = ssh_truncate, | ||||
|     .bdrv_co_flush_to_disk        = ssh_co_flush, | ||||
|     .create_opts                  = &ssh_create_opts, | ||||
| }; | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user