Compare commits
	
		
			5 Commits
		
	
	
		
			v3.0.0-rc0
			...
			rm-protoco
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 6321d78dc5 | ||
|  | a0974f99aa | ||
|  | 42883c01bf | ||
|  | 7966c2b312 | ||
|  | 5888011244 | 
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -36,7 +36,6 @@ | ||||
| /qapi/qapi-commands-common.[ch] | ||||
| /qapi/qapi-commands-crypto.[ch] | ||||
| /qapi/qapi-commands-introspect.[ch] | ||||
| /qapi/qapi-commands-job.[ch] | ||||
| /qapi/qapi-commands-migration.[ch] | ||||
| /qapi/qapi-commands-misc.[ch] | ||||
| /qapi/qapi-commands-net.[ch] | ||||
| @@ -54,7 +53,6 @@ | ||||
| /qapi/qapi-events-common.[ch] | ||||
| /qapi/qapi-events-crypto.[ch] | ||||
| /qapi/qapi-events-introspect.[ch] | ||||
| /qapi/qapi-events-job.[ch] | ||||
| /qapi/qapi-events-migration.[ch] | ||||
| /qapi/qapi-events-misc.[ch] | ||||
| /qapi/qapi-events-net.[ch] | ||||
| @@ -73,7 +71,6 @@ | ||||
| /qapi/qapi-types-common.[ch] | ||||
| /qapi/qapi-types-crypto.[ch] | ||||
| /qapi/qapi-types-introspect.[ch] | ||||
| /qapi/qapi-types-job.[ch] | ||||
| /qapi/qapi-types-migration.[ch] | ||||
| /qapi/qapi-types-misc.[ch] | ||||
| /qapi/qapi-types-net.[ch] | ||||
| @@ -91,7 +88,6 @@ | ||||
| /qapi/qapi-visit-common.[ch] | ||||
| /qapi/qapi-visit-crypto.[ch] | ||||
| /qapi/qapi-visit-introspect.[ch] | ||||
| /qapi/qapi-visit-job.[ch] | ||||
| /qapi/qapi-visit-migration.[ch] | ||||
| /qapi/qapi-visit-misc.[ch] | ||||
| /qapi/qapi-visit-net.[ch] | ||||
| @@ -155,7 +151,6 @@ | ||||
| .sdk | ||||
| *.gcda | ||||
| *.gcno | ||||
| *.gcov | ||||
| /pc-bios/bios-pq/status | ||||
| /pc-bios/vgabios-pq/status | ||||
| /pc-bios/optionrom/linuxboot.asm | ||||
| @@ -211,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 | ||||
|   | ||||
							
								
								
									
										7
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,6 @@ | ||||
| [submodule "roms/vgabios"] | ||||
| 	path = roms/vgabios | ||||
| 	url = git://git.qemu-project.org/vgabios.git/ | ||||
| [submodule "roms/seabios"] | ||||
| 	path = roms/seabios | ||||
| 	url = git://git.qemu-project.org/seabios.git/ | ||||
| @@ -15,7 +18,7 @@ | ||||
| 	url = git://git.qemu-project.org/openhackware.git | ||||
| [submodule "roms/qemu-palcode"] | ||||
| 	path = roms/qemu-palcode | ||||
| 	url = git://git.qemu.org/qemu-palcode.git | ||||
| 	url = git://github.com/rth7680/qemu-palcode.git | ||||
| [submodule "roms/sgabios"] | ||||
| 	path = roms/sgabios | ||||
| 	url = git://git.qemu-project.org/sgabios.git | ||||
| @@ -42,4 +45,4 @@ | ||||
| 	url = git://github.com/hdeller/seabios-hppa.git | ||||
| [submodule "roms/u-boot-sam460ex"] | ||||
| 	path = roms/u-boot-sam460ex | ||||
| 	url = git://git.qemu.org/u-boot-sam460ex.git | ||||
| 	url = git://github.com/zbalaton/u-boot-sam460ex | ||||
|   | ||||
							
								
								
									
										18
									
								
								.mailmap
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								.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> | ||||
| @@ -16,19 +15,10 @@ 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 | ||||
| Daniel P. Berrangé <berrange@redhat.com> | ||||
|   | ||||
| @@ -35,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)) | ||||
|   | ||||
							
								
								
									
										122
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										122
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,8 +1,4 @@ | ||||
| # The current Travis default is a container based 14.04 Trust on EC2 | ||||
| # Additional builds with specific requirements for a full VM need to | ||||
| # be added as additional matrix: entries later on | ||||
| sudo: false | ||||
| dist: trusty | ||||
| language: c | ||||
| python: | ||||
|   - "2.6" | ||||
| @@ -38,7 +34,6 @@ addons: | ||||
|       - libvte-2.90-dev | ||||
|       - sparse | ||||
|       - uuid-dev | ||||
|       - gcovr | ||||
|  | ||||
| # The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu | ||||
| # to prevent IRC notifications from forks. This was created using: | ||||
| @@ -51,15 +46,12 @@ notifications: | ||||
|     on_failure: always | ||||
| env: | ||||
|   global: | ||||
|     - SRC_DIR="." | ||||
|     - BUILD_DIR="." | ||||
|     - TEST_CMD="make check" | ||||
|     - MAKEFLAGS="-j3" | ||||
|   matrix: | ||||
|     - CONFIG="--disable-system" | ||||
|     - CONFIG="--disable-user" | ||||
|     - CONFIG="--enable-debug --enable-debug-tcg" | ||||
|     - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb --disable-user" | ||||
|     - 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" | ||||
| @@ -69,39 +61,31 @@ git: | ||||
| before_install: | ||||
|   - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update ; fi | ||||
|   - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install libffi gettext glib pixman ; fi | ||||
|   - git submodule update --init --recursive capstone dtc ui/keycodemapdb | ||||
|   - 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 ${MAKEFLAGS} && ${TEST_CMD} | ||||
| matrix: | ||||
|   include: | ||||
|     # 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" | ||||
|       compiler: clang | ||||
|     - env: CONFIG="--disable-user" | ||||
|     # Test with CLang for compile portability | ||||
|     - env: CONFIG="" | ||||
|       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="" | ||||
|       compiler: gcc | ||||
|     # We don't need to exercise every backend with every front-end | ||||
|     - env: CONFIG="--enable-trace-backends=log,simple,syslog --disable-system" | ||||
|     - env: CONFIG="--enable-trace-backends=simple" | ||||
|            TEST_CMD="" | ||||
|       compiler: gcc | ||||
|     - env: CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu" | ||||
|     - env: CONFIG="--enable-trace-backends=ftrace" | ||||
|            TEST_CMD="" | ||||
|       compiler: gcc | ||||
|     - env: CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu" | ||||
|     - env: CONFIG="--enable-trace-backends=ust" | ||||
|            TEST_CMD="" | ||||
|       compiler: gcc | ||||
|     - env: CONFIG="--disable-tcg" | ||||
| @@ -110,24 +94,80 @@ matrix: | ||||
|     - env: CONFIG="" | ||||
|       os: osx | ||||
|       compiler: clang | ||||
|     # Python builds | ||||
|     - env: CONFIG="--target-list=x86_64-softmmu" | ||||
|     # 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.0" | ||||
|     - env: CONFIG="--target-list=x86_64-softmmu" | ||||
|       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" | ||||
|       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 | ||||
| @@ -156,16 +196,8 @@ matrix: | ||||
|       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 check-tcg" | ||||
|       script: | ||||
|         - make ${MAKEFLAGS} && ${TEST_CMD} ${MAKEFLAGS} | ||||
|       sudo: required | ||||
|       dist: trusty | ||||
|       compiler: gcc | ||||
|         - ./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 | ||||
|   | ||||
							
								
								
									
										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. | ||||
|  | ||||
|   | ||||
							
								
								
									
										137
									
								
								MAINTAINERS
									
									
									
									
									
								
							
							
						
						
									
										137
									
								
								MAINTAINERS
									
									
									
									
									
								
							| @@ -127,6 +127,7 @@ Alpha | ||||
| M: Richard Henderson <rth@twiddle.net> | ||||
| S: Maintained | ||||
| F: target/alpha/ | ||||
| F: hw/alpha/ | ||||
| F: tests/tcg/alpha/ | ||||
| F: disas/alpha.c | ||||
|  | ||||
| @@ -135,8 +136,6 @@ M: Peter Maydell <peter.maydell@linaro.org> | ||||
| L: qemu-arm@nongnu.org | ||||
| S: Maintained | ||||
| F: target/arm/ | ||||
| F: tests/tcg/arm/ | ||||
| F: tests/tcg/aarch64/ | ||||
| F: hw/arm/ | ||||
| F: hw/cpu/a*mpcore.c | ||||
| F: include/hw/cpu/a*mpcore.h | ||||
| @@ -187,7 +186,7 @@ F: disas/microblaze.c | ||||
|  | ||||
| MIPS | ||||
| M: Aurelien Jarno <aurelien@aurel32.net> | ||||
| M: Aleksandar Markovic <aleksandar.markovic@mips.com> | ||||
| M: Yongbok Kim <yongbok.kim@mips.com> | ||||
| S: Maintained | ||||
| F: target/mips/ | ||||
| F: hw/mips/ | ||||
| @@ -285,15 +284,13 @@ M: Richard Henderson <rth@twiddle.net> | ||||
| M: Eduardo Habkost <ehabkost@redhat.com> | ||||
| S: Maintained | ||||
| F: target/i386/ | ||||
| F: tests/tcg/i386/ | ||||
| F: tests/tcg/x86_64/ | ||||
| F: hw/i386/ | ||||
| F: disas/i386.c | ||||
| T: git git://github.com/ehabkost/qemu.git x86-next | ||||
|  | ||||
| Xtensa | ||||
| M: Max Filippov <jcmvbkbc@gmail.com> | ||||
| W: http://wiki.osll.ru/doku.php?id=etc:users:jcmvbkbc:qemu-target-xtensa | ||||
| W: http://wiki.osll.spb.ru/doku.php?id=etc:users:jcmvbkbc:qemu-target-xtensa | ||||
| S: Maintained | ||||
| F: target/xtensa/ | ||||
| F: hw/xtensa/ | ||||
| @@ -307,10 +304,6 @@ F: target/tricore/ | ||||
| F: hw/tricore/ | ||||
| F: include/hw/tricore/ | ||||
|  | ||||
| Multiarch Linux User Tests | ||||
| M: Alex Bennée <alex.bennee@linaro.org> | ||||
| F: tests/tcg/multiarch/ | ||||
|  | ||||
| Guest CPU Cores (KVM): | ||||
| ---------------------- | ||||
|  | ||||
| @@ -420,12 +413,6 @@ F: include/*/*win32* | ||||
| X: qga/*win32* | ||||
| F: qemu.nsi | ||||
|  | ||||
| Alpha Machines | ||||
| M: Richard Henderson <rth@twiddle.net> | ||||
| S: Maintained | ||||
| F: hw/alpha/ | ||||
| F: hw/isa/smc37c669-superio.c | ||||
|  | ||||
| ARM Machines | ||||
| ------------ | ||||
| Allwinner-a10 | ||||
| @@ -455,10 +442,6 @@ F: hw/timer/cmsdk-apb-timer.c | ||||
| F: include/hw/timer/cmsdk-apb-timer.h | ||||
| F: hw/char/cmsdk-apb-uart.c | ||||
| F: include/hw/char/cmsdk-apb-uart.h | ||||
| F: hw/misc/tz-ppc.c | ||||
| F: include/hw/misc/tz-ppc.h | ||||
| F: hw/misc/tz-mpc.c | ||||
| F: include/hw/misc/tz-mpc.h | ||||
|  | ||||
| ARM cores | ||||
| M: Peter Maydell <peter.maydell@linaro.org> | ||||
| @@ -501,10 +484,9 @@ F: include/hw/arm/digic.h | ||||
| F: hw/*/digic* | ||||
|  | ||||
| Gumstix | ||||
| M: Philippe Mathieu-Daudé <f4bug@amsat.org> | ||||
| L: qemu-devel@nongnu.org | ||||
| L: qemu-arm@nongnu.org | ||||
| S: Odd Fixes | ||||
| S: Orphan | ||||
| F: hw/arm/gumstix.c | ||||
|  | ||||
| i.MX31 | ||||
| @@ -528,11 +510,8 @@ M: Peter Maydell <peter.maydell@linaro.org> | ||||
| L: qemu-arm@nongnu.org | ||||
| S: Maintained | ||||
| F: hw/arm/mps2.c | ||||
| F: hw/arm/mps2-tz.c | ||||
| F: hw/misc/mps2-*.c | ||||
| F: include/hw/misc/mps2-*.h | ||||
| F: hw/arm/iotkit.c | ||||
| F: include/hw/arm/iotkit.h | ||||
| F: hw/misc/mps2-scc.c | ||||
| F: include/hw/misc/mps2-scc.h | ||||
|  | ||||
| Musicpal | ||||
| M: Jan Kiszka <jan.kiszka@web.de> | ||||
| @@ -645,17 +624,6 @@ M: Subbaraya Sundeep <sundeep.lkml@gmail.com> | ||||
| S: Maintained | ||||
| F: hw/arm/msf2-som.c | ||||
|  | ||||
| ASPEED BMCs | ||||
| M: Cédric Le Goater <clg@kaod.org> | ||||
| R: Andrew Jeffery <andrew@aj.id.au> | ||||
| R: Joel Stanley <joel@jms.id.au> | ||||
| L: qemu-arm@nongnu.org | ||||
| S: Maintained | ||||
| F: hw/*/*aspeed* | ||||
| F: include/hw/*/*aspeed* | ||||
| F: hw/net/ftgmac100.c | ||||
| F: include/hw/net/ftgmac100.h | ||||
|  | ||||
| CRIS Machines | ||||
| ------------- | ||||
| Axis Dev88 | ||||
| @@ -718,7 +686,7 @@ S: Maintained | ||||
| F: hw/mips/mips_malta.c | ||||
|  | ||||
| Mipssim | ||||
| M: Aleksandar Markovic <aleksandar.markovic@mips.com> | ||||
| M: Yongbok Kim <yongbok.kim@mips.com> | ||||
| S: Odd Fixes | ||||
| F: hw/mips/mips_mipssim.c | ||||
| F: hw/net/mipsnet.c | ||||
| @@ -729,11 +697,9 @@ S: Maintained | ||||
| F: hw/mips/mips_r4k.c | ||||
|  | ||||
| Fulong 2E | ||||
| M: Aleksandar Markovic <aleksandar.markovic@mips.com> | ||||
| M: Yongbok Kim <yongbok.kim@mips.com> | ||||
| S: Odd Fixes | ||||
| F: hw/mips/mips_fulong2e.c | ||||
| F: hw/isa/vt82c686.c | ||||
| F: include/hw/isa/vt82c686.h | ||||
|  | ||||
| Boston | ||||
| M: Paul Burton <paul.burton@mips.com> | ||||
| @@ -788,11 +754,8 @@ F: hw/ppc/mac_newworld.c | ||||
| F: hw/pci-host/uninorth.c | ||||
| F: hw/pci-bridge/dec.[hc] | ||||
| F: hw/misc/macio/ | ||||
| F: hw/misc/mos6522.c | ||||
| F: hw/nvram/mac_nvram.c | ||||
| F: include/hw/misc/macio/ | ||||
| F: include/hw/misc/mos6522.h | ||||
| F: include/hw/ppc/mac_dbdma.h | ||||
| F: hw/nvram/mac_nvram.c | ||||
|  | ||||
| Old World | ||||
| M: Alexander Graf <agraf@suse.de> | ||||
| @@ -813,10 +776,9 @@ F: hw/ppc/prep_systemio.c | ||||
| F: hw/ppc/rs6000_mc.c | ||||
| F: hw/pci-host/prep.[hc] | ||||
| F: hw/isa/i82378.c | ||||
| F: hw/isa/pc87312.c | ||||
| F: hw/isa/pc87312.[hc] | ||||
| F: hw/dma/i82374.c | ||||
| F: hw/timer/m48t59-isa.c | ||||
| F: include/hw/isa/pc87312.h | ||||
| F: include/hw/timer/m48t59.h | ||||
| F: pc-bios/ppc_rom.bin | ||||
|  | ||||
| @@ -851,7 +813,6 @@ M: BALATON Zoltan <balaton@eik.bme.hu> | ||||
| L: qemu-ppc@nongnu.org | ||||
| S: Maintained | ||||
| F: hw/ide/sii3112.c | ||||
| F: hw/timer/m41t80.c | ||||
|  | ||||
| SH4 Machines | ||||
| ------------ | ||||
| @@ -940,7 +901,7 @@ X86 Machines | ||||
| ------------ | ||||
| PC | ||||
| M: Michael S. Tsirkin <mst@redhat.com> | ||||
| M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com> | ||||
| M: Marcel Apfelbaum <marcel@redhat.com> | ||||
| S: Supported | ||||
| F: include/hw/i386/ | ||||
| F: hw/i386/ | ||||
| @@ -963,7 +924,7 @@ M: Michael S. Tsirkin <mst@redhat.com> | ||||
| M: Paolo Bonzini <pbonzini@redhat.com> | ||||
| S: Supported | ||||
| F: hw/char/debugcon.c | ||||
| F: hw/char/parallel* | ||||
| F: hw/char/parallel.c | ||||
| F: hw/char/serial* | ||||
| F: hw/dma/i8257* | ||||
| F: hw/i2c/pm_smbus.c | ||||
| @@ -971,7 +932,6 @@ F: hw/input/pckbd.c | ||||
| F: hw/intc/apic* | ||||
| F: hw/intc/ioapic* | ||||
| F: hw/intc/i8259* | ||||
| F: hw/isa/isa-superio.c | ||||
| F: hw/misc/debugexit.c | ||||
| F: hw/misc/pc-testdev.c | ||||
| F: hw/timer/hpet* | ||||
| @@ -979,18 +939,15 @@ F: hw/timer/i8254* | ||||
| F: hw/timer/mc146818rtc* | ||||
| F: hw/watchdog/wdt_ib700.c | ||||
| F: include/hw/display/vga.h | ||||
| F: include/hw/char/parallel.h | ||||
| F: include/hw/dma/i8257.h | ||||
| F: include/hw/i2c/pm_smbus.h | ||||
| F: include/hw/input/i8042.h | ||||
| F: include/hw/isa/superio.h | ||||
| F: include/hw/isa/i8257.h | ||||
| F: include/hw/timer/hpet.h | ||||
| F: include/hw/timer/i8254* | ||||
| F: include/hw/timer/mc146818rtc* | ||||
|  | ||||
| Machine core | ||||
| M: Eduardo Habkost <ehabkost@redhat.com> | ||||
| M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com> | ||||
| M: Marcel Apfelbaum <marcel@redhat.com> | ||||
| S: Supported | ||||
| F: hw/core/machine.c | ||||
| F: hw/core/null-machine.c | ||||
| @@ -1029,7 +986,6 @@ F: hw/block/cdrom.c | ||||
| F: hw/block/hd-geometry.c | ||||
| F: tests/ide-test.c | ||||
| F: tests/ahci-test.c | ||||
| F: tests/cdrom-test.c | ||||
| F: tests/libqos/ahci* | ||||
| T: git git://github.com/jnsnow/qemu.git ide | ||||
|  | ||||
| @@ -1065,7 +1021,7 @@ F: hw/ipack/ | ||||
|  | ||||
| PCI | ||||
| M: Michael S. Tsirkin <mst@redhat.com> | ||||
| M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com> | ||||
| M: Marcel Apfelbaum <marcel@redhat.com> | ||||
| S: Supported | ||||
| F: include/hw/pci/* | ||||
| F: hw/misc/pci-testdev.c | ||||
| @@ -1346,33 +1302,6 @@ S: Maintained | ||||
| F: include/hw/misc/unimp.h | ||||
| F: hw/misc/unimp.c | ||||
|  | ||||
| Standard VGA | ||||
| M: Gerd Hoffmann <kraxel@redhat.com> | ||||
| S: Maintained | ||||
| F: hw/display/vga* | ||||
| F: hw/display/bochs-display.c | ||||
| F: include/hw/display/vga.h | ||||
| F: include/hw/display/bochs-vbe.h | ||||
|  | ||||
| ramfb | ||||
| M: Gerd Hoffmann <kraxel@redhat.com> | ||||
| S: Maintained | ||||
| F: hw/display/ramfb*.c | ||||
| F: include/hw/display/ramfb.h | ||||
|  | ||||
| virtio-gpu | ||||
| M: Gerd Hoffmann <kraxel@redhat.com> | ||||
| S: Maintained | ||||
| F: hw/display/virtio-gpu* | ||||
| F: hw/display/virtio-vga.c | ||||
| F: include/hw/virtio/virtio-gpu.h | ||||
|  | ||||
| Cirrus VGA | ||||
| M: Gerd Hoffmann <kraxel@redhat.com> | ||||
| S: Odd Fixes | ||||
| W: https://www.kraxel.org/blog/2014/10/qemu-using-cirrus-considered-harmful/ | ||||
| F: hw/display/cirrus* | ||||
|  | ||||
| Subsystems | ||||
| ---------- | ||||
| Audio | ||||
| @@ -1398,8 +1327,6 @@ F: qemu-img* | ||||
| F: qemu-io* | ||||
| F: tests/qemu-iotests/ | ||||
| F: util/qemu-progress.c | ||||
| F: qobject/block-qdict.c | ||||
| F: test/check-block-qdict.c | ||||
| T: git git://repo.or.cz/qemu/kevin.git block | ||||
|  | ||||
| Block I/O path | ||||
| @@ -1412,7 +1339,6 @@ F: util/aio-*.c | ||||
| F: block/io.c | ||||
| F: migration/block* | ||||
| F: include/block/aio.h | ||||
| F: include/block/aio-wait.h | ||||
| F: scripts/qemugdb/aio.py | ||||
| T: git git://github.com/stefanha/qemu.git block | ||||
|  | ||||
| @@ -1430,14 +1356,10 @@ L: qemu-block@nongnu.org | ||||
| S: Supported | ||||
| F: blockjob.c | ||||
| F: include/block/blockjob.h | ||||
| F: job.c | ||||
| F: job-qmp.c | ||||
| F: include/block/job.h | ||||
| F: block/backup.c | ||||
| F: block/commit.c | ||||
| F: block/stream.c | ||||
| F: block/mirror.c | ||||
| F: qapi/job.json | ||||
| T: git git://github.com/codyprime/qemu-kvm-jtc.git block | ||||
|  | ||||
| Block QAPI, monitor, command line | ||||
| @@ -1638,8 +1560,7 @@ F: tests/test-*-visitor.c | ||||
| F: tests/test-qapi-*.c | ||||
| F: tests/test-qmp-*.c | ||||
| F: tests/test-visitor-serialization.c | ||||
| F: scripts/qapi-gen.py | ||||
| F: scripts/qapi/* | ||||
| F: scripts/qapi* | ||||
| F: docs/devel/qapi* | ||||
| T: git git://repo.or.cz/qemu/armbru.git qapi-next | ||||
|  | ||||
| @@ -1711,7 +1632,6 @@ S: Maintained | ||||
| F: slirp/ | ||||
| F: net/slirp.c | ||||
| F: include/net/slirp.h | ||||
| T: git https://people.debian.org/~sthibault/qemu.git slirp | ||||
| T: git git://git.kiszka.org/qemu.git queues/slirp | ||||
|  | ||||
| Stubs | ||||
| @@ -1723,8 +1643,6 @@ Tracing | ||||
| M: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| S: Maintained | ||||
| F: trace/ | ||||
| F: trace-events | ||||
| F: qemu-option-trace.texi | ||||
| F: scripts/tracetool.py | ||||
| F: scripts/tracetool/ | ||||
| F: docs/devel/tracing.txt | ||||
| @@ -1850,12 +1768,6 @@ F: include/sysemu/replay.h | ||||
| F: docs/replay.txt | ||||
| F: stubs/replay.c | ||||
|  | ||||
| IOVA Tree | ||||
| M: Peter Xu <peterx@redhat.com> | ||||
| S: Maintained | ||||
| F: include/qemu/iova-tree.h | ||||
| F: util/iova-tree.c | ||||
|  | ||||
| Usermode Emulation | ||||
| ------------------ | ||||
| Overall | ||||
| @@ -1996,7 +1908,6 @@ F: nbd/ | ||||
| F: include/block/nbd* | ||||
| F: qemu-nbd.* | ||||
| F: blockdev-nbd.c | ||||
| F: docs/interop/nbd.txt | ||||
| T: git git://repo.or.cz/qemu/ericb.git nbd | ||||
|  | ||||
| NFS | ||||
| @@ -2052,12 +1963,6 @@ S: Supported | ||||
| F: block/quorum.c | ||||
| L: qemu-block@nongnu.org | ||||
|  | ||||
| blklogwrites | ||||
| M: Ari Sundholm <ari@tuxera.com> | ||||
| L: qemu-block@nongnu.org | ||||
| S: Supported | ||||
| F: block/blklogwrites.c | ||||
|  | ||||
| blkverify | ||||
| M: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| L: qemu-block@nongnu.org | ||||
| @@ -2157,7 +2062,7 @@ F: docs/block-replication.txt | ||||
|  | ||||
| PVRDMA | ||||
| M: Yuval Shaia <yuval.shaia@oracle.com> | ||||
| M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com> | ||||
| M: Marcel Apfelbaum <marcel@redhat.com> | ||||
| S: Maintained | ||||
| F: hw/rdma/* | ||||
| F: hw/rdma/vmw/* | ||||
| @@ -2172,7 +2077,6 @@ R: Philippe Mathieu-Daudé <f4bug@amsat.org> | ||||
| L: qemu-devel@nongnu.org | ||||
| S: Maintained | ||||
| F: .travis.yml | ||||
| F: scripts/travis/ | ||||
| F: .shippable.yml | ||||
| F: tests/docker/ | ||||
| F: tests/vm/ | ||||
| @@ -2180,13 +2084,6 @@ W: https://travis-ci.org/qemu/qemu | ||||
| W: https://app.shippable.com/github/qemu/qemu | ||||
| W: http://patchew.org/QEMU/ | ||||
|  | ||||
| Guest Test Compilation Support | ||||
| M: Alex Bennée <alex.bennee@linaro.org> | ||||
| R: Philippe Mathieu-Daudé <f4bug@amsat.org> | ||||
| F: tests/tcg/Makefile | ||||
| F: tests/tcg/Makefile.include | ||||
| L: qemu-devel@nongnu.org | ||||
|  | ||||
| Documentation | ||||
| ------------- | ||||
| Build system architecture | ||||
|   | ||||
							
								
								
									
										69
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										69
									
								
								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 | ||||
| @@ -96,7 +98,6 @@ 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-job.h qapi/qapi-types-job.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 | ||||
| @@ -115,7 +116,6 @@ 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-job.h qapi/qapi-visit-job.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 | ||||
| @@ -133,7 +133,6 @@ 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-job.h qapi/qapi-commands-job.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 | ||||
| @@ -151,7 +150,6 @@ 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-job.h qapi/qapi-events-job.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 | ||||
| @@ -320,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) | ||||
| @@ -350,7 +347,7 @@ $(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 | ||||
| @@ -441,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; \ | ||||
| 			fi; \ | ||||
| 		fi; \ | ||||
| 		printf "#define QEMU_PKGVERSION \"$${pkgvers}\"\n"; \ | ||||
| 		if test -n "$${pkgvers}"; then \ | ||||
| 			printf '#define QEMU_FULL_VERSION QEMU_VERSION " (" QEMU_PKGVERSION ")"\n'; \ | ||||
| 				printf ')"\n'; \ | ||||
| 			else \ | ||||
| 			printf '#define QEMU_FULL_VERSION QEMU_VERSION\n'; \ | ||||
| 				printf '""\n'; \ | ||||
| 			fi; \ | ||||
| 		) > $@.tmp) | ||||
| 		fi) > $@.tmp) | ||||
| 	$(call quiet-command, if ! cmp -s $@ $@.tmp; then \ | ||||
| 	  mv $@.tmp $@; \ | ||||
| 	 else \ | ||||
| @@ -488,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. | ||||
| @@ -566,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 \ | ||||
| @@ -574,7 +570,7 @@ 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=%)") | ||||
| 	@>$@ | ||||
| @@ -584,7 +580,6 @@ qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json $(SRC_PATH)/qapi/common.json \ | ||||
|                $(SRC_PATH)/qapi/char.json \ | ||||
|                $(SRC_PATH)/qapi/crypto.json \ | ||||
|                $(SRC_PATH)/qapi/introspect.json \ | ||||
|                $(SRC_PATH)/qapi/job.json \ | ||||
|                $(SRC_PATH)/qapi/migration.json \ | ||||
|                $(SRC_PATH)/qapi/misc.json \ | ||||
|                $(SRC_PATH)/qapi/net.json \ | ||||
| @@ -604,7 +599,6 @@ 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-job.c qapi/qapi-types-job.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 \ | ||||
| @@ -623,7 +617,6 @@ 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-job.c qapi/qapi-visit-job.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 \ | ||||
| @@ -641,7 +634,6 @@ 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-job.c qapi/qapi-commands-job.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 \ | ||||
| @@ -659,7 +651,6 @@ 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-job.c qapi/qapi-events-job.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 \ | ||||
| @@ -674,7 +665,7 @@ 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=%)") | ||||
| 	@>$@ | ||||
| @@ -723,14 +714,6 @@ module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak | ||||
| 	$(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 | ||||
| @@ -792,6 +775,7 @@ bepo    cz | ||||
| ifdef INSTALL_BLOBS | ||||
| BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \ | ||||
| vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \ | ||||
| acpi-dsdt.aml \ | ||||
| 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 \ | ||||
| @@ -871,7 +855,7 @@ ifneq ($(BLOBS),) | ||||
| 		$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \ | ||||
| 	done | ||||
| endif | ||||
| ifdef CONFIG_GTK | ||||
| ifeq ($(CONFIG_GTK),m) | ||||
| 	$(MAKE) -C po $@ | ||||
| endif | ||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps" | ||||
| @@ -986,16 +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 | ||||
|  | ||||
| # 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 | ||||
|  | ||||
| @@ -1076,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' | ||||
| @@ -1091,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  '' | ||||
| @@ -1105,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:' | ||||
|   | ||||
| @@ -10,7 +10,6 @@ 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-job.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 | ||||
| @@ -29,7 +28,6 @@ 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-job.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 | ||||
| @@ -47,7 +45,6 @@ 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-job.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 | ||||
| @@ -66,7 +63,7 @@ chardev-obj-y = chardev/ | ||||
| # 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 | ||||
| @@ -97,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 | ||||
| @@ -144,7 +140,6 @@ 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-job.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 | ||||
| @@ -196,67 +191,66 @@ vhost-user-blk-obj-y = contrib/vhost-user-blk/ | ||||
|  | ||||
| ###################################################################### | ||||
| 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/acpi | ||||
| trace-events-subdirs += hw/arm | ||||
| trace-events-subdirs += hw/alpha | ||||
| trace-events-subdirs += hw/hppa | ||||
| trace-events-subdirs += hw/xen | ||||
| 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 += 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,11 +36,6 @@ 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 | ||||
|  | ||||
| @@ -102,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 | ||||
| @@ -143,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) | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| obj-$(CONFIG_SOFTMMU) += accel.o | ||||
| obj-$(CONFIG_KVM) += kvm/ | ||||
| obj-y += kvm/ | ||||
| obj-$(CONFIG_TCG) += tcg/ | ||||
| obj-y += stubs/ | ||||
|   | ||||
| @@ -70,8 +70,8 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms) | ||||
|  | ||||
| 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; | ||||
| @@ -83,10 +83,13 @@ void configure_accelerator(MachineState *ms) | ||||
|         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; | ||||
|         } | ||||
| @@ -104,7 +107,6 @@ void configure_accelerator(MachineState *ms) | ||||
|             accel_initialised = true; | ||||
|         } | ||||
|     } | ||||
|     g_strfreev(accel_list); | ||||
|  | ||||
|     if (!accel_initialised) { | ||||
|         if (!init_failed) { | ||||
| @@ -124,15 +126,6 @@ void accel_register_compat_props(AccelState *accel) | ||||
|     register_compat_props_array(class->global_props); | ||||
| } | ||||
|  | ||||
| void accel_setup_post(MachineState *ms) | ||||
| { | ||||
|     AccelState *accel = ms->accelerator; | ||||
|     AccelClass *acc = ACCEL_GET_CLASS(accel); | ||||
|     if (acc->setup_post) { | ||||
|         acc->setup_post(ms, accel); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void register_accel_types(void) | ||||
| { | ||||
|     type_register_static(&accel_type); | ||||
|   | ||||
| @@ -1,2 +1 @@ | ||||
| obj-y += kvm-all.o | ||||
| obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o | ||||
| obj-$(CONFIG_KVM) += kvm-all.o | ||||
|   | ||||
| @@ -38,7 +38,6 @@ | ||||
| #include "qemu/event_notifier.h" | ||||
| #include "trace.h" | ||||
| #include "hw/irq.h" | ||||
| #include "sysemu/sev.h" | ||||
|  | ||||
| #include "hw/boards.h" | ||||
|  | ||||
| @@ -104,10 +103,6 @@ struct KVMState | ||||
| #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; | ||||
| @@ -143,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; | ||||
| @@ -256,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; | ||||
| @@ -267,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; | ||||
| @@ -275,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; | ||||
| @@ -392,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, | ||||
| @@ -753,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)); | ||||
| @@ -770,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)); | ||||
| @@ -1660,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; | ||||
|   | ||||
| @@ -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) | ||||
| { | ||||
| } | ||||
|   | ||||
| @@ -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,10 +63,7 @@ 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; | ||||
|     ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); | ||||
|     DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
|     return ret; | ||||
| } | ||||
| @@ -110,8 +73,6 @@ 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; | ||||
|     __atomic_load(haddr, &val, __ATOMIC_RELAXED); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
|     return val; | ||||
| @@ -122,8 +83,6 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||
|  | ||||
|     ATOMIC_TRACE_ST; | ||||
|     __atomic_store(haddr, &val, __ATOMIC_RELAXED); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
| } | ||||
| @@ -133,10 +92,7 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, | ||||
| { | ||||
|     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; | ||||
| } | ||||
| @@ -147,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;                                                     \ | ||||
| } | ||||
| @@ -165,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 | ||||
|  | ||||
| @@ -214,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, | ||||
| @@ -225,10 +137,7 @@ 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; | ||||
|     ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); | ||||
|     DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
|     return BSWAP(ret); | ||||
| } | ||||
| @@ -238,8 +147,6 @@ 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; | ||||
|     __atomic_load(haddr, &val, __ATOMIC_RELAXED); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
|     return BSWAP(val); | ||||
| @@ -250,8 +157,6 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||
|  | ||||
|     ATOMIC_TRACE_ST; | ||||
|     val = BSWAP(val); | ||||
|     __atomic_store(haddr, &val, __ATOMIC_RELAXED); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
| @@ -262,10 +167,7 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, | ||||
| { | ||||
|     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); | ||||
| } | ||||
| @@ -276,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);                                              \ | ||||
| } | ||||
| @@ -293,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 | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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_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; | ||||
| @@ -334,7 +338,7 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, | ||||
|     phys_pc = get_page_addr_code(desc.env, pc); | ||||
|     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) | ||||
| @@ -348,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, | ||||
| @@ -394,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_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); | ||||
| @@ -414,8 +419,17 @@ static inline TranslationBlock *tb_find(CPUState *cpu, | ||||
| #endif | ||||
|     /* See if we can patch the calling 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; | ||||
| } | ||||
|  | ||||
| @@ -571,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'; | ||||
| @@ -593,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; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
| @@ -690,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(); | ||||
|         } | ||||
|   | ||||
| @@ -125,6 +125,8 @@ static void tlb_flush_nocheck(CPUState *cpu) | ||||
|     atomic_set(&env->tlb_flush_count, env->tlb_flush_count + 1); | ||||
|     tlb_debug("(count: %zu)\n", tlb_flush_count()); | ||||
|  | ||||
|     tb_lock(); | ||||
|  | ||||
|     memset(env->tlb_table, -1, sizeof(env->tlb_table)); | ||||
|     memset(env->tlb_v_table, -1, sizeof(env->tlb_v_table)); | ||||
|     cpu_tb_jmp_cache_clear(cpu); | ||||
| @@ -133,6 +135,8 @@ static void tlb_flush_nocheck(CPUState *cpu) | ||||
|     env->tlb_flush_addr = -1; | ||||
|     env->tlb_flush_mask = 0; | ||||
|  | ||||
|     tb_unlock(); | ||||
|  | ||||
|     atomic_mb_set(&cpu->pending_tlb_flush, 0); | ||||
| } | ||||
|  | ||||
| @@ -176,6 +180,8 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data) | ||||
|  | ||||
|     assert_cpu_is_self(cpu); | ||||
|  | ||||
|     tb_lock(); | ||||
|  | ||||
|     tlb_debug("start: mmu_idx:0x%04lx\n", mmu_idx_bitmask); | ||||
|  | ||||
|     for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { | ||||
| @@ -191,6 +197,8 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data) | ||||
|     cpu_tb_jmp_cache_clear(cpu); | ||||
|  | ||||
|     tlb_debug("done\n"); | ||||
|  | ||||
|     tb_unlock(); | ||||
| } | ||||
|  | ||||
| void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap) | ||||
| @@ -235,30 +243,20 @@ void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, | ||||
|     async_safe_run_on_cpu(src_cpu, fn, RUN_ON_CPU_HOST_INT(idxmap)); | ||||
| } | ||||
|  | ||||
| static inline bool tlb_hit_page_anyprot(CPUTLBEntry *tlb_entry, | ||||
|                                         target_ulong page) | ||||
| { | ||||
|     return tlb_hit_page(tlb_entry->addr_read, page) || | ||||
|            tlb_hit_page(tlb_entry->addr_write, page) || | ||||
|            tlb_hit_page(tlb_entry->addr_code, page); | ||||
| } | ||||
|  | ||||
| static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong page) | ||||
|  | ||||
| static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr) | ||||
| { | ||||
|     if (tlb_hit_page_anyprot(tlb_entry, page)) { | ||||
|     if (addr == (tlb_entry->addr_read & | ||||
|                  (TARGET_PAGE_MASK | TLB_INVALID_MASK)) || | ||||
|         addr == (tlb_entry->addr_write & | ||||
|                  (TARGET_PAGE_MASK | TLB_INVALID_MASK)) || | ||||
|         addr == (tlb_entry->addr_code & | ||||
|                  (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { | ||||
|         memset(tlb_entry, -1, sizeof(*tlb_entry)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static inline void tlb_flush_vtlb_page(CPUArchState *env, int mmu_idx, | ||||
|                                        target_ulong page) | ||||
| { | ||||
|     int k; | ||||
|     for (k = 0; k < CPU_VTLB_SIZE; k++) { | ||||
|         tlb_flush_entry(&env->tlb_v_table[mmu_idx][k], page); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void tlb_flush_page_async_work(CPUState *cpu, run_on_cpu_data data) | ||||
| { | ||||
|     CPUArchState *env = cpu->env_ptr; | ||||
| @@ -284,7 +282,14 @@ static void tlb_flush_page_async_work(CPUState *cpu, run_on_cpu_data data) | ||||
|     i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|     for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { | ||||
|         tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr); | ||||
|         tlb_flush_vtlb_page(env, mmu_idx, addr); | ||||
|     } | ||||
|  | ||||
|     /* check whether there are entries that need to be flushed in the vtlb */ | ||||
|     for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { | ||||
|         int k; | ||||
|         for (k = 0; k < CPU_VTLB_SIZE; k++) { | ||||
|             tlb_flush_entry(&env->tlb_v_table[mmu_idx][k], addr); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     tb_flush_jmp_cache(cpu, addr); | ||||
| @@ -316,6 +321,7 @@ static void tlb_flush_page_by_mmuidx_async_work(CPUState *cpu, | ||||
|     unsigned long mmu_idx_bitmap = addr_and_mmuidx & ALL_MMUIDX_BITS; | ||||
|     int page = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|     int mmu_idx; | ||||
|     int i; | ||||
|  | ||||
|     assert_cpu_is_self(cpu); | ||||
|  | ||||
| @@ -325,7 +331,11 @@ static void tlb_flush_page_by_mmuidx_async_work(CPUState *cpu, | ||||
|     for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { | ||||
|         if (test_bit(mmu_idx, &mmu_idx_bitmap)) { | ||||
|             tlb_flush_entry(&env->tlb_table[mmu_idx][page], addr); | ||||
|             tlb_flush_vtlb_page(env, mmu_idx, addr); | ||||
|  | ||||
|             /* check whether there are vltb entries that need to be flushed */ | ||||
|             for (i = 0; i < CPU_VTLB_SIZE; i++) { | ||||
|                 tlb_flush_entry(&env->tlb_v_table[mmu_idx][i], addr); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -610,42 +620,27 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, | ||||
|     target_ulong address; | ||||
|     target_ulong code_address; | ||||
|     uintptr_t addend; | ||||
|     CPUTLBEntry *te, tn; | ||||
|     hwaddr iotlb, xlat, sz, paddr_page; | ||||
|     target_ulong vaddr_page; | ||||
|     CPUTLBEntry *te, *tv, tn; | ||||
|     hwaddr iotlb, xlat, sz; | ||||
|     unsigned vidx = env->vtlb_index++ % CPU_VTLB_SIZE; | ||||
|     int asidx = cpu_asidx_from_attrs(cpu, attrs); | ||||
|  | ||||
|     assert_cpu_is_self(cpu); | ||||
|  | ||||
|     if (size < TARGET_PAGE_SIZE) { | ||||
|         sz = TARGET_PAGE_SIZE; | ||||
|     } else { | ||||
|         if (size > TARGET_PAGE_SIZE) { | ||||
|     assert(size >= TARGET_PAGE_SIZE); | ||||
|     if (size != TARGET_PAGE_SIZE) { | ||||
|         tlb_add_large_page(env, vaddr, size); | ||||
|     } | ||||
|         sz = size; | ||||
|     } | ||||
|     vaddr_page = vaddr & TARGET_PAGE_MASK; | ||||
|     paddr_page = paddr & TARGET_PAGE_MASK; | ||||
|  | ||||
|     section = address_space_translate_for_iotlb(cpu, asidx, paddr_page, | ||||
|                                                 &xlat, &sz, attrs, &prot); | ||||
|     sz = size; | ||||
|     section = address_space_translate_for_iotlb(cpu, asidx, paddr, &xlat, &sz); | ||||
|     assert(sz >= TARGET_PAGE_SIZE); | ||||
|  | ||||
|     tlb_debug("vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx | ||||
|               " prot=%x idx=%d\n", | ||||
|               vaddr, paddr, prot, mmu_idx); | ||||
|  | ||||
|     address = vaddr_page; | ||||
|     if (size < TARGET_PAGE_SIZE) { | ||||
|         /* | ||||
|          * Slow-path the TLB entries; we will repeat the MMU check and TLB | ||||
|          * fill on every access. | ||||
|          */ | ||||
|         address |= TLB_RECHECK; | ||||
|     } | ||||
|     if (!memory_region_is_ram(section->mr) && | ||||
|         !memory_region_is_romd(section->mr)) { | ||||
|     address = vaddr; | ||||
|     if (!memory_region_is_ram(section->mr) && !memory_region_is_romd(section->mr)) { | ||||
|         /* IO memory case */ | ||||
|         address |= TLB_MMIO; | ||||
|         addend = 0; | ||||
| @@ -654,47 +649,26 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, | ||||
|         addend = (uintptr_t)memory_region_get_ram_ptr(section->mr) + xlat; | ||||
|     } | ||||
|  | ||||
|     /* Make sure there's no cached translation for the new page.  */ | ||||
|     tlb_flush_vtlb_page(env, mmu_idx, vaddr_page); | ||||
|  | ||||
|     code_address = address; | ||||
|     iotlb = memory_region_section_get_iotlb(cpu, section, vaddr_page, | ||||
|                                             paddr_page, xlat, prot, &address); | ||||
|     iotlb = memory_region_section_get_iotlb(cpu, section, vaddr, paddr, xlat, | ||||
|                                             prot, &address); | ||||
|  | ||||
|     index = (vaddr_page >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|     index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|     te = &env->tlb_table[mmu_idx][index]; | ||||
|     /* do not discard the translation in te, evict it into a victim tlb */ | ||||
|     tv = &env->tlb_v_table[mmu_idx][vidx]; | ||||
|  | ||||
|     /* | ||||
|      * Only evict the old entry to the victim tlb if it's for a | ||||
|      * different page; otherwise just overwrite the stale data. | ||||
|      */ | ||||
|     if (!tlb_hit_page_anyprot(te, vaddr_page)) { | ||||
|         unsigned vidx = env->vtlb_index++ % CPU_VTLB_SIZE; | ||||
|         CPUTLBEntry *tv = &env->tlb_v_table[mmu_idx][vidx]; | ||||
|  | ||||
|         /* Evict the old entry into the victim tlb.  */ | ||||
|     /* addr_write can race with tlb_reset_dirty_range */ | ||||
|     copy_tlb_helper(tv, te, true); | ||||
|  | ||||
|     env->iotlb_v[mmu_idx][vidx] = env->iotlb[mmu_idx][index]; | ||||
|     } | ||||
|  | ||||
|     /* refill the tlb */ | ||||
|     /* | ||||
|      * At this point iotlb contains a physical section number in the lower | ||||
|      * TARGET_PAGE_BITS, and either | ||||
|      *  + the ram_addr_t of the page base of the target RAM (if NOTDIRTY or ROM) | ||||
|      *  + the offset within section->mr of the page base (otherwise) | ||||
|      * We subtract the vaddr_page (which is page aligned and thus won't | ||||
|      * disturb the low bits) to give an offset which can be added to the | ||||
|      * (non-page-aligned) vaddr of the eventual memory access to get | ||||
|      * the MemoryRegion offset for the access. Note that the vaddr we | ||||
|      * subtract here is that of the page base, and not the same as the | ||||
|      * vaddr we add back in io_readx()/io_writex()/get_page_addr_code(). | ||||
|      */ | ||||
|     env->iotlb[mmu_idx][index].addr = iotlb - vaddr_page; | ||||
|     env->iotlb[mmu_idx][index].addr = iotlb - vaddr; | ||||
|     env->iotlb[mmu_idx][index].attrs = attrs; | ||||
|  | ||||
|     /* Now calculate the new entry */ | ||||
|     tn.addend = addend - vaddr_page; | ||||
|     tn.addend = addend - vaddr; | ||||
|     if (prot & PAGE_READ) { | ||||
|         tn.addr_read = address; | ||||
|     } else { | ||||
| @@ -788,43 +762,16 @@ static inline ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr) | ||||
|  | ||||
| static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, | ||||
|                          int mmu_idx, | ||||
|                          target_ulong addr, uintptr_t retaddr, | ||||
|                          bool recheck, int size) | ||||
|                          target_ulong addr, uintptr_t retaddr, int size) | ||||
| { | ||||
|     CPUState *cpu = ENV_GET_CPU(env); | ||||
|     hwaddr mr_offset; | ||||
|     MemoryRegionSection *section; | ||||
|     MemoryRegion *mr; | ||||
|     hwaddr physaddr = iotlbentry->addr; | ||||
|     MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs); | ||||
|     uint64_t val; | ||||
|     bool locked = false; | ||||
|     MemTxResult r; | ||||
|  | ||||
|     if (recheck) { | ||||
|         /* | ||||
|          * This is a TLB_RECHECK access, where the MMU protection | ||||
|          * covers a smaller range than a target page, and we must | ||||
|          * repeat the MMU check here. This tlb_fill() call might | ||||
|          * longjump out if this access should cause a guest exception. | ||||
|          */ | ||||
|         int index; | ||||
|         target_ulong tlb_addr; | ||||
|  | ||||
|         tlb_fill(cpu, addr, size, MMU_DATA_LOAD, mmu_idx, retaddr); | ||||
|  | ||||
|         index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|         tlb_addr = env->tlb_table[mmu_idx][index].addr_read; | ||||
|         if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) { | ||||
|             /* RAM access */ | ||||
|             uintptr_t haddr = addr + env->tlb_table[mmu_idx][index].addend; | ||||
|  | ||||
|             return ldn_p((void *)haddr, size); | ||||
|         } | ||||
|         /* Fall through for handling IO accesses */ | ||||
|     } | ||||
|  | ||||
|     section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs); | ||||
|     mr = section->mr; | ||||
|     mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr; | ||||
|     physaddr = (physaddr & TARGET_PAGE_MASK) + addr; | ||||
|     cpu->mem_io_pc = retaddr; | ||||
|     if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) { | ||||
|         cpu_io_recompile(cpu, retaddr); | ||||
| @@ -836,13 +783,9 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, | ||||
|         qemu_mutex_lock_iothread(); | ||||
|         locked = true; | ||||
|     } | ||||
|     r = memory_region_dispatch_read(mr, mr_offset, | ||||
|     r = memory_region_dispatch_read(mr, physaddr, | ||||
|                                     &val, size, iotlbentry->attrs); | ||||
|     if (r != MEMTX_OK) { | ||||
|         hwaddr physaddr = mr_offset + | ||||
|             section->offset_within_address_space - | ||||
|             section->offset_within_region; | ||||
|  | ||||
|         cpu_transaction_failed(cpu, physaddr, addr, size, MMU_DATA_LOAD, | ||||
|                                mmu_idx, iotlbentry->attrs, r, retaddr); | ||||
|     } | ||||
| @@ -856,42 +799,15 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, | ||||
| static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry, | ||||
|                       int mmu_idx, | ||||
|                       uint64_t val, target_ulong addr, | ||||
|                       uintptr_t retaddr, bool recheck, int size) | ||||
|                       uintptr_t retaddr, int size) | ||||
| { | ||||
|     CPUState *cpu = ENV_GET_CPU(env); | ||||
|     hwaddr mr_offset; | ||||
|     MemoryRegionSection *section; | ||||
|     MemoryRegion *mr; | ||||
|     hwaddr physaddr = iotlbentry->addr; | ||||
|     MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs); | ||||
|     bool locked = false; | ||||
|     MemTxResult r; | ||||
|  | ||||
|     if (recheck) { | ||||
|         /* | ||||
|          * This is a TLB_RECHECK access, where the MMU protection | ||||
|          * covers a smaller range than a target page, and we must | ||||
|          * repeat the MMU check here. This tlb_fill() call might | ||||
|          * longjump out if this access should cause a guest exception. | ||||
|          */ | ||||
|         int index; | ||||
|         target_ulong tlb_addr; | ||||
|  | ||||
|         tlb_fill(cpu, addr, size, MMU_DATA_STORE, mmu_idx, retaddr); | ||||
|  | ||||
|         index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|         tlb_addr = env->tlb_table[mmu_idx][index].addr_write; | ||||
|         if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) { | ||||
|             /* RAM access */ | ||||
|             uintptr_t haddr = addr + env->tlb_table[mmu_idx][index].addend; | ||||
|  | ||||
|             stn_p((void *)haddr, size, val); | ||||
|             return; | ||||
|         } | ||||
|         /* Fall through for handling IO accesses */ | ||||
|     } | ||||
|  | ||||
|     section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs); | ||||
|     mr = section->mr; | ||||
|     mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr; | ||||
|     physaddr = (physaddr & TARGET_PAGE_MASK) + addr; | ||||
|     if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) { | ||||
|         cpu_io_recompile(cpu, retaddr); | ||||
|     } | ||||
| @@ -902,13 +818,9 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry, | ||||
|         qemu_mutex_lock_iothread(); | ||||
|         locked = true; | ||||
|     } | ||||
|     r = memory_region_dispatch_write(mr, mr_offset, | ||||
|     r = memory_region_dispatch_write(mr, physaddr, | ||||
|                                      val, size, iotlbentry->attrs); | ||||
|     if (r != MEMTX_OK) { | ||||
|         hwaddr physaddr = mr_offset + | ||||
|             section->offset_within_address_space - | ||||
|             section->offset_within_region; | ||||
|  | ||||
|         cpu_transaction_failed(cpu, physaddr, addr, size, MMU_DATA_STORE, | ||||
|                                mmu_idx, iotlbentry->attrs, r, retaddr); | ||||
|     } | ||||
| @@ -956,51 +868,24 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index, | ||||
|  */ | ||||
| tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr) | ||||
| { | ||||
|     int mmu_idx, index; | ||||
|     int mmu_idx, index, pd; | ||||
|     void *p; | ||||
|     MemoryRegion *mr; | ||||
|     MemoryRegionSection *section; | ||||
|     CPUState *cpu = ENV_GET_CPU(env); | ||||
|     CPUIOTLBEntry *iotlbentry; | ||||
|     hwaddr physaddr, mr_offset; | ||||
|     hwaddr physaddr; | ||||
|  | ||||
|     index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|     mmu_idx = cpu_mmu_index(env, true); | ||||
|     if (unlikely(!tlb_hit(env->tlb_table[mmu_idx][index].addr_code, addr))) { | ||||
|     if (unlikely(env->tlb_table[mmu_idx][index].addr_code != | ||||
|                  (addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK)))) { | ||||
|         if (!VICTIM_TLB_HIT(addr_read, addr)) { | ||||
|             tlb_fill(ENV_GET_CPU(env), addr, 0, MMU_INST_FETCH, mmu_idx, 0); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (unlikely((env->tlb_table[mmu_idx][index].addr_code & | ||||
|                   (TLB_RECHECK | TLB_INVALID_MASK)) == TLB_RECHECK)) { | ||||
|         /* | ||||
|          * This is a TLB_RECHECK access, where the MMU protection | ||||
|          * covers a smaller range than a target page, and we must | ||||
|          * repeat the MMU check here. This tlb_fill() call might | ||||
|          * longjump out if this access should cause a guest exception. | ||||
|          */ | ||||
|         int index; | ||||
|         target_ulong tlb_addr; | ||||
|  | ||||
|         tlb_fill(cpu, addr, 0, MMU_INST_FETCH, mmu_idx, 0); | ||||
|  | ||||
|         index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|         tlb_addr = env->tlb_table[mmu_idx][index].addr_code; | ||||
|         if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) { | ||||
|             /* RAM access. We can't handle this, so for now just stop */ | ||||
|             cpu_abort(cpu, "Unable to handle guest executing from RAM within " | ||||
|                       "a small MPU region at 0x" TARGET_FMT_lx, addr); | ||||
|         } | ||||
|         /* | ||||
|          * Fall through to handle IO accesses (which will almost certainly | ||||
|          * also result in failure) | ||||
|          */ | ||||
|     } | ||||
|  | ||||
|     iotlbentry = &env->iotlb[mmu_idx][index]; | ||||
|     section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs); | ||||
|     mr = section->mr; | ||||
|     pd = iotlbentry->addr & ~TARGET_PAGE_MASK; | ||||
|     mr = iotlb_to_region(cpu, pd, iotlbentry->attrs); | ||||
|     if (memory_region_is_unassigned(mr)) { | ||||
|         qemu_mutex_lock_iothread(); | ||||
|         if (memory_region_request_mmio_ptr(mr, addr)) { | ||||
| @@ -1021,10 +906,7 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr) | ||||
|          * and use the MemTXResult it produced). However it is the | ||||
|          * simplest place we have currently available for the check. | ||||
|          */ | ||||
|         mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr; | ||||
|         physaddr = mr_offset + | ||||
|             section->offset_within_address_space - | ||||
|             section->offset_within_region; | ||||
|         physaddr = (iotlbentry->addr & TARGET_PAGE_MASK) + addr; | ||||
|         cpu_transaction_failed(cpu, physaddr, addr, 0, MMU_INST_FETCH, mmu_idx, | ||||
|                                iotlbentry->attrs, MEMTX_DECODE_ERROR, 0); | ||||
|  | ||||
| @@ -1052,7 +934,8 @@ void probe_write(CPUArchState *env, target_ulong addr, int size, int mmu_idx, | ||||
|     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; | ||||
|  | ||||
|     if (!tlb_hit(tlb_addr, addr)) { | ||||
|     if ((addr & TARGET_PAGE_MASK) | ||||
|         != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { | ||||
|         /* TLB entry is for a different page */ | ||||
|         if (!VICTIM_TLB_HIT(addr_write, addr)) { | ||||
|             tlb_fill(ENV_GET_CPU(env), addr, size, MMU_DATA_STORE, | ||||
| @@ -1096,7 +979,8 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, | ||||
|     } | ||||
|  | ||||
|     /* Check TLB entry and enforce page permissions.  */ | ||||
|     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, 1 << s_bits, MMU_DATA_STORE, | ||||
|                      mmu_idx, retaddr); | ||||
| @@ -1104,8 +988,8 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, | ||||
|         tlb_addr = tlbe->addr_write & ~TLB_INVALID_MASK; | ||||
|     } | ||||
|  | ||||
|     /* Notice an IO access or a needs-MMU-lookup access */ | ||||
|     if (unlikely(tlb_addr & (TLB_MMIO | TLB_RECHECK))) { | ||||
|     /* Notice an IO access  */ | ||||
|     if (unlikely(tlb_addr & TLB_MMIO)) { | ||||
|         /* There's really nothing that can be done to | ||||
|            support this apart from stop-the-world.  */ | ||||
|         goto stop_the_world; | ||||
|   | ||||
| @@ -98,12 +98,10 @@ | ||||
| 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) | ||||
|                                               uintptr_t retaddr) | ||||
| { | ||||
|     CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; | ||||
|     return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, recheck, | ||||
|                     DATA_SIZE); | ||||
|     return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, DATA_SIZE); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @@ -123,7 +121,8 @@ 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); | ||||
| @@ -139,8 +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); | ||||
|         res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr); | ||||
|         res = TGT_LE(res); | ||||
|         return res; | ||||
|     } | ||||
| @@ -190,7 +188,8 @@ 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); | ||||
| @@ -206,8 +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); | ||||
|         res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr); | ||||
|         res = TGT_BE(res); | ||||
|         return res; | ||||
|     } | ||||
| @@ -261,12 +259,10 @@ 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, | ||||
| @@ -284,7 +280,8 @@ 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); | ||||
| @@ -301,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; | ||||
|     } | ||||
|  | ||||
| @@ -319,7 +315,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||
|         page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; | ||||
|         index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|         tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; | ||||
|         if (!tlb_hit_page(tlb_addr2, page2) | ||||
|         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); | ||||
| @@ -361,7 +357,8 @@ 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); | ||||
| @@ -378,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; | ||||
|     } | ||||
|  | ||||
| @@ -396,7 +392,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||
|         page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; | ||||
|         index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|         tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; | ||||
|         if (!tlb_hit_page(tlb_addr2, page2) | ||||
|         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); | ||||
|   | ||||
| @@ -705,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);                                            \ | ||||
|   | ||||
| @@ -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) | ||||
|  | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -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,6 +34,8 @@ void translator_loop_temp_check(DisasContextBase *db) | ||||
| void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | ||||
|                      CPUState *cpu, TranslationBlock *tb) | ||||
| { | ||||
|     int max_insns; | ||||
|  | ||||
|     /* Initialize DisasContext */ | ||||
|     db->tb = tb; | ||||
|     db->pc_first = tb->pc; | ||||
| @@ -43,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 */ | ||||
| @@ -93,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); | ||||
| @@ -110,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; | ||||
|         } | ||||
|   | ||||
| @@ -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) | ||||
| { | ||||
|   | ||||
| @@ -168,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); | ||||
|   | ||||
							
								
								
									
										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; | ||||
| } | ||||
|   | ||||
| @@ -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 { | ||||
|     else { | ||||
|         timer_del (s->ts); | ||||
|         if (audio_timer_running) { | ||||
|             audio_timer_running = false; | ||||
|             trace_audio_timer_stop(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| 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); | ||||
| } | ||||
|   | ||||
| @@ -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" | ||||
|   | ||||
| @@ -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); | ||||
| @@ -187,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); | ||||
| @@ -218,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); | ||||
|  | ||||
| @@ -311,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, | ||||
|   | ||||
| @@ -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> | ||||
| @@ -247,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; | ||||
| } | ||||
| @@ -262,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) | ||||
| { | ||||
| @@ -368,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); | ||||
| @@ -415,11 +416,18 @@ host_memory_backend_class_init(ObjectClass *oc, void *data) | ||||
|         &HostMemPolicy_lookup, | ||||
|         host_memory_backend_get_policy, | ||||
|         host_memory_backend_set_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); | ||||
| } | ||||
|  | ||||
| static void host_memory_backend_finalize(Object *o) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); | ||||
|     g_free(backend->id); | ||||
| } | ||||
|  | ||||
| static const TypeInfo host_memory_backend_info = { | ||||
|     .name = TYPE_MEMORY_BACKEND, | ||||
|     .parent = TYPE_OBJECT, | ||||
| @@ -428,6 +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_finalize = host_memory_backend_finalize, | ||||
|     .interfaces = (InterfaceInfo[]) { | ||||
|         { TYPE_USER_CREATABLE }, | ||||
|         { } | ||||
|   | ||||
							
								
								
									
										301
									
								
								block.c
									
									
									
									
									
								
							
							
						
						
									
										301
									
								
								block.c
									
									
									
									
									
								
							| @@ -27,14 +27,12 @@ | ||||
| #include "block/block_int.h" | ||||
| #include "block/blockjob.h" | ||||
| #include "block/nbd.h" | ||||
| #include "block/qdict.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "module_block.h" | ||||
| #include "qemu/module.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qapi/qmp/qdict.h" | ||||
| #include "qapi/qmp/qjson.h" | ||||
| #include "qapi/qmp/qnull.h" | ||||
| #include "qapi/qmp/qstring.h" | ||||
| #include "qapi/qobject-output-visitor.h" | ||||
| #include "qapi/qapi-visit-block-core.h" | ||||
| @@ -333,10 +331,6 @@ BlockDriverState *bdrv_new(void) | ||||
|  | ||||
|     qemu_co_queue_init(&bs->flush_queue); | ||||
|  | ||||
|     for (i = 0; i < bdrv_drain_all_count; i++) { | ||||
|         bdrv_drained_begin(bs); | ||||
|     } | ||||
|  | ||||
|     QTAILQ_INSERT_TAIL(&all_bdrv_states, bs, bs_list); | ||||
|  | ||||
|     return bs; | ||||
| @@ -725,7 +719,7 @@ static int find_image_format(BlockBackend *file, const char *filename, | ||||
|  * Set the current 'total_sectors' value | ||||
|  * Return 0 on success, -errno on error. | ||||
|  */ | ||||
| int refresh_total_sectors(BlockDriverState *bs, int64_t hint) | ||||
| static int refresh_total_sectors(BlockDriverState *bs, int64_t hint) | ||||
| { | ||||
|     BlockDriver *drv = bs->drv; | ||||
|  | ||||
| @@ -822,13 +816,7 @@ static char *bdrv_child_get_parent_desc(BdrvChild *c) | ||||
| static void bdrv_child_cb_drained_begin(BdrvChild *child) | ||||
| { | ||||
|     BlockDriverState *bs = child->opaque; | ||||
|     bdrv_do_drained_begin_quiesce(bs, NULL, false); | ||||
| } | ||||
|  | ||||
| static bool bdrv_child_cb_drained_poll(BdrvChild *child) | ||||
| { | ||||
|     BlockDriverState *bs = child->opaque; | ||||
|     return bdrv_drain_poll(bs, false, NULL, false); | ||||
|     bdrv_drained_begin(bs); | ||||
| } | ||||
|  | ||||
| static void bdrv_child_cb_drained_end(BdrvChild *child) | ||||
| @@ -912,11 +900,9 @@ static void bdrv_inherited_options(int *child_flags, QDict *child_options, | ||||
| } | ||||
|  | ||||
| const BdrvChildRole child_file = { | ||||
|     .parent_is_bds   = true, | ||||
|     .get_parent_desc = bdrv_child_get_parent_desc, | ||||
|     .inherit_options = bdrv_inherited_options, | ||||
|     .drained_begin   = bdrv_child_cb_drained_begin, | ||||
|     .drained_poll    = bdrv_child_cb_drained_poll, | ||||
|     .drained_end     = bdrv_child_cb_drained_end, | ||||
|     .attach          = bdrv_child_cb_attach, | ||||
|     .detach          = bdrv_child_cb_detach, | ||||
| @@ -938,11 +924,9 @@ static void bdrv_inherited_fmt_options(int *child_flags, QDict *child_options, | ||||
| } | ||||
|  | ||||
| const BdrvChildRole child_format = { | ||||
|     .parent_is_bds   = true, | ||||
|     .get_parent_desc = bdrv_child_get_parent_desc, | ||||
|     .inherit_options = bdrv_inherited_fmt_options, | ||||
|     .drained_begin   = bdrv_child_cb_drained_begin, | ||||
|     .drained_poll    = bdrv_child_cb_drained_poll, | ||||
|     .drained_end     = bdrv_child_cb_drained_end, | ||||
|     .attach          = bdrv_child_cb_attach, | ||||
|     .detach          = bdrv_child_cb_detach, | ||||
| @@ -1057,13 +1041,11 @@ static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base, | ||||
| } | ||||
|  | ||||
| const BdrvChildRole child_backing = { | ||||
|     .parent_is_bds   = true, | ||||
|     .get_parent_desc = bdrv_child_get_parent_desc, | ||||
|     .attach          = bdrv_backing_attach, | ||||
|     .detach          = bdrv_backing_detach, | ||||
|     .inherit_options = bdrv_backing_options, | ||||
|     .drained_begin   = bdrv_child_cb_drained_begin, | ||||
|     .drained_poll    = bdrv_child_cb_drained_poll, | ||||
|     .drained_end     = bdrv_child_cb_drained_end, | ||||
|     .inactivate      = bdrv_child_cb_inactivate, | ||||
|     .update_filename = bdrv_backing_update_filename, | ||||
| @@ -1156,12 +1138,6 @@ static void bdrv_assign_node_name(BlockDriverState *bs, | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     /* Make sure that the node name isn't truncated */ | ||||
|     if (strlen(node_name) >= sizeof(bs->node_name)) { | ||||
|         error_setg(errp, "Node name too long"); | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     /* copy node name into the bs and insert it into the graph list */ | ||||
|     pstrcpy(bs->node_name, sizeof(bs->node_name), node_name); | ||||
|     QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list); | ||||
| @@ -1174,7 +1150,7 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, | ||||
|                             int open_flags, Error **errp) | ||||
| { | ||||
|     Error *local_err = NULL; | ||||
|     int i, ret; | ||||
|     int ret; | ||||
|  | ||||
|     bdrv_assign_node_name(bs, node_name, &local_err); | ||||
|     if (local_err) { | ||||
| @@ -1222,12 +1198,6 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, | ||||
|     assert(bdrv_min_mem_align(bs) != 0); | ||||
|     assert(is_power_of_2(bs->bl.request_alignment)); | ||||
|  | ||||
|     for (i = 0; i < bs->quiesce_counter; i++) { | ||||
|         if (drv->bdrv_co_drain_begin) { | ||||
|             drv->bdrv_co_drain_begin(bs); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| open_failed: | ||||
|     bs->drv = NULL; | ||||
| @@ -1256,9 +1226,9 @@ BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, | ||||
|  | ||||
|     ret = bdrv_open_driver(bs, drv, node_name, bs->options, flags, errp); | ||||
|     if (ret < 0) { | ||||
|         qobject_unref(bs->explicit_options); | ||||
|         QDECREF(bs->explicit_options); | ||||
|         bs->explicit_options = NULL; | ||||
|         qobject_unref(bs->options); | ||||
|         QDECREF(bs->options); | ||||
|         bs->options = NULL; | ||||
|         bdrv_unref(bs); | ||||
|         return NULL; | ||||
| @@ -1487,9 +1457,9 @@ static QDict *parse_json_filename(const char *filename, Error **errp) | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     options = qobject_to(QDict, options_obj); | ||||
|     options = qobject_to_qdict(options_obj); | ||||
|     if (!options) { | ||||
|         qobject_unref(options_obj); | ||||
|         qobject_decref(options_obj); | ||||
|         error_setg(errp, "Invalid JSON object given"); | ||||
|         return NULL; | ||||
|     } | ||||
| @@ -1519,7 +1489,7 @@ static void parse_json_protocol(QDict *options, const char **pfilename, | ||||
|     /* Options given in the filename have lower priority than options | ||||
|      * specified directly */ | ||||
|     qdict_join(options, json_options, false); | ||||
|     qobject_unref(json_options); | ||||
|     QDECREF(json_options); | ||||
|     *pfilename = NULL; | ||||
| } | ||||
|  | ||||
| @@ -1649,24 +1619,13 @@ static int bdrv_reopen_get_flags(BlockReopenQueue *q, BlockDriverState *bs) | ||||
|  | ||||
| /* Returns whether the image file can be written to after the reopen queue @q | ||||
|  * has been successfully applied, or right now if @q is NULL. */ | ||||
| static bool bdrv_is_writable_after_reopen(BlockDriverState *bs, | ||||
|                                           BlockReopenQueue *q) | ||||
| static bool bdrv_is_writable(BlockDriverState *bs, BlockReopenQueue *q) | ||||
| { | ||||
|     int flags = bdrv_reopen_get_flags(q, bs); | ||||
|  | ||||
|     return (flags & (BDRV_O_RDWR | BDRV_O_INACTIVE)) == BDRV_O_RDWR; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Return whether the BDS can be written to.  This is not necessarily | ||||
|  * the same as !bdrv_is_read_only(bs), as inactivated images may not | ||||
|  * be written to but do not count as read-only images. | ||||
|  */ | ||||
| bool bdrv_is_writable(BlockDriverState *bs) | ||||
| { | ||||
|     return bdrv_is_writable_after_reopen(bs, NULL); | ||||
| } | ||||
|  | ||||
| static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, | ||||
|                             BdrvChild *c, const BdrvChildRole *role, | ||||
|                             BlockReopenQueue *reopen_queue, | ||||
| @@ -1704,7 +1663,7 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||||
|  | ||||
|     /* Write permissions never work with read-only images */ | ||||
|     if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) && | ||||
|         !bdrv_is_writable_after_reopen(bs, q)) | ||||
|         !bdrv_is_writable(bs, q)) | ||||
|     { | ||||
|         error_setg(errp, "Block node is read-only"); | ||||
|         return -EPERM; | ||||
| @@ -1954,6 +1913,12 @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| #define DEFAULT_PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \ | ||||
|                                  | BLK_PERM_WRITE \ | ||||
|                                  | BLK_PERM_WRITE_UNCHANGED \ | ||||
|                                  | BLK_PERM_RESIZE) | ||||
| #define DEFAULT_PERM_UNCHANGED (BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH) | ||||
|  | ||||
| void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c, | ||||
|                                const BdrvChildRole *role, | ||||
|                                BlockReopenQueue *reopen_queue, | ||||
| @@ -1990,7 +1955,7 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, | ||||
|                                   &perm, &shared); | ||||
|  | ||||
|         /* Format drivers may touch metadata even if the guest doesn't write */ | ||||
|         if (bdrv_is_writable_after_reopen(bs, reopen_queue)) { | ||||
|         if (bdrv_is_writable(bs, reopen_queue)) { | ||||
|             perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE; | ||||
|         } | ||||
|  | ||||
| @@ -2043,12 +2008,7 @@ static void bdrv_replace_child_noperm(BdrvChild *child, | ||||
|             child->role->detach(child); | ||||
|         } | ||||
|         if (old_bs->quiesce_counter && child->role->drained_end) { | ||||
|             int num = old_bs->quiesce_counter; | ||||
|             if (child->role->parent_is_bds) { | ||||
|                 num -= bdrv_drain_all_count; | ||||
|             } | ||||
|             assert(num >= 0); | ||||
|             for (i = 0; i < num; i++) { | ||||
|             for (i = 0; i < old_bs->quiesce_counter; i++) { | ||||
|                 child->role->drained_end(child); | ||||
|             } | ||||
|         } | ||||
| @@ -2060,13 +2020,8 @@ static void bdrv_replace_child_noperm(BdrvChild *child, | ||||
|     if (new_bs) { | ||||
|         QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent); | ||||
|         if (new_bs->quiesce_counter && child->role->drained_begin) { | ||||
|             int num = new_bs->quiesce_counter; | ||||
|             if (child->role->parent_is_bds) { | ||||
|                 num -= bdrv_drain_all_count; | ||||
|             } | ||||
|             assert(num >= 0); | ||||
|             for (i = 0; i < num; i++) { | ||||
|                 bdrv_parent_drained_begin_single(child, true); | ||||
|             for (i = 0; i < new_bs->quiesce_counter; i++) { | ||||
|                 child->role->drained_begin(child); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -2226,6 +2181,16 @@ static void bdrv_parent_cb_change_media(BlockDriverState *bs, bool load) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void bdrv_parent_cb_resize(BlockDriverState *bs) | ||||
| { | ||||
|     BdrvChild *c; | ||||
|     QLIST_FOREACH(c, &bs->parents, next_parent) { | ||||
|         if (c->role->resize) { | ||||
|             c->role->resize(c); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Sets the backing file link of a BDS. A new reference is created; callers | ||||
|  * which don't need their own reference any more must call bdrv_unref(). | ||||
| @@ -2307,7 +2272,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, | ||||
|     if (reference || qdict_haskey(options, "file.filename")) { | ||||
|         backing_filename[0] = '\0'; | ||||
|     } else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) { | ||||
|         qobject_unref(options); | ||||
|         QDECREF(options); | ||||
|         goto free_exit; | ||||
|     } else { | ||||
|         bdrv_get_full_backing_filename(bs, backing_filename, PATH_MAX, | ||||
| @@ -2315,7 +2280,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, | ||||
|         if (local_err) { | ||||
|             ret = -EINVAL; | ||||
|             error_propagate(errp, local_err); | ||||
|             qobject_unref(options); | ||||
|             QDECREF(options); | ||||
|             goto free_exit; | ||||
|         } | ||||
|     } | ||||
| @@ -2323,7 +2288,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, | ||||
|     if (!bs->drv || !bs->drv->supports_backing) { | ||||
|         ret = -EINVAL; | ||||
|         error_setg(errp, "Driver doesn't support backing files"); | ||||
|         qobject_unref(options); | ||||
|         QDECREF(options); | ||||
|         goto free_exit; | ||||
|     } | ||||
|  | ||||
| @@ -2357,7 +2322,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, | ||||
|  | ||||
| free_exit: | ||||
|     g_free(backing_filename); | ||||
|     qobject_unref(tmp_parent_options); | ||||
|     QDECREF(tmp_parent_options); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| @@ -2390,7 +2355,7 @@ bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key, | ||||
|             error_setg(errp, "A block device must be specified for \"%s\"", | ||||
|                        bdref_key); | ||||
|         } | ||||
|         qobject_unref(image_options); | ||||
|         QDECREF(image_options); | ||||
|         goto done; | ||||
|     } | ||||
|  | ||||
| @@ -2468,7 +2433,7 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp) | ||||
|         } | ||||
|         visit_complete(v, &obj); | ||||
|  | ||||
|         qdict = qobject_to(QDict, obj); | ||||
|         qdict = qobject_to_qdict(obj); | ||||
|         qdict_flatten(qdict); | ||||
|  | ||||
|         /* bdrv_open_inherit() defaults to the values in bdrv_flags (for | ||||
| @@ -2483,7 +2448,7 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp) | ||||
|     obj = NULL; | ||||
|  | ||||
| fail: | ||||
|     qobject_unref(obj); | ||||
|     qobject_decref(obj); | ||||
|     visit_free(v); | ||||
|     return bs; | ||||
| } | ||||
| @@ -2553,7 +2518,7 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, | ||||
|     } | ||||
|  | ||||
| out: | ||||
|     qobject_unref(snapshot_options); | ||||
|     QDECREF(snapshot_options); | ||||
|     g_free(tmp_filename); | ||||
|     return bs_snapshot; | ||||
| } | ||||
| @@ -2564,7 +2529,7 @@ out: | ||||
|  * options is a QDict of options to pass to the block drivers, or NULL for an | ||||
|  * empty set of options. The reference to the QDict belongs to the block layer | ||||
|  * after the call (even on failure), so if the caller intends to reuse the | ||||
|  * dictionary, it needs to use qobject_ref() before calling bdrv_open. | ||||
|  * dictionary, it needs to use QINCREF() before calling bdrv_open. | ||||
|  * | ||||
|  * If *pbs is NULL, a new BDS will be created with a pointer to it stored there. | ||||
|  * If it is not NULL, the referenced BDS will be reused. | ||||
| @@ -2595,7 +2560,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, | ||||
|  | ||||
|     if (reference) { | ||||
|         bool options_non_empty = options ? qdict_size(options) : false; | ||||
|         qobject_unref(options); | ||||
|         QDECREF(options); | ||||
|  | ||||
|         if (filename || options_non_empty) { | ||||
|             error_setg(errp, "Cannot reference an existing block device with " | ||||
| @@ -2680,13 +2645,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, | ||||
|  | ||||
|     /* See cautionary note on accessing @options above */ | ||||
|     backing = qdict_get_try_str(options, "backing"); | ||||
|     if (qobject_to(QNull, qdict_get(options, "backing")) != NULL || | ||||
|         (backing && *backing == '\0')) | ||||
|     { | ||||
|         if (backing) { | ||||
|             warn_report("Use of \"backing\": \"\" is deprecated; " | ||||
|                         "use \"backing\": null instead"); | ||||
|         } | ||||
|     if (backing && *backing == '\0') { | ||||
|         flags |= BDRV_O_NO_BACKING; | ||||
|         qdict_del(options, "backing"); | ||||
|     } | ||||
| @@ -2786,7 +2745,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, | ||||
|  | ||||
|     bdrv_parent_cb_change_media(bs, true); | ||||
|  | ||||
|     qobject_unref(options); | ||||
|     QDECREF(options); | ||||
|  | ||||
|     /* For snapshot=on, create a temporary qcow2 overlay. bs points to the | ||||
|      * temporary snapshot afterwards. */ | ||||
| @@ -2810,10 +2769,10 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, | ||||
|  | ||||
| fail: | ||||
|     blk_unref(file); | ||||
|     qobject_unref(snapshot_options); | ||||
|     qobject_unref(bs->explicit_options); | ||||
|     qobject_unref(bs->options); | ||||
|     qobject_unref(options); | ||||
|     QDECREF(snapshot_options); | ||||
|     QDECREF(bs->explicit_options); | ||||
|     QDECREF(bs->options); | ||||
|     QDECREF(options); | ||||
|     bs->options = NULL; | ||||
|     bs->explicit_options = NULL; | ||||
|     bdrv_unref(bs); | ||||
| @@ -2822,8 +2781,8 @@ fail: | ||||
|  | ||||
| close_and_fail: | ||||
|     bdrv_unref(bs); | ||||
|     qobject_unref(snapshot_options); | ||||
|     qobject_unref(options); | ||||
|     QDECREF(snapshot_options); | ||||
|     QDECREF(options); | ||||
|     error_propagate(errp, local_err); | ||||
|     return NULL; | ||||
| } | ||||
| @@ -2918,28 +2877,20 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, | ||||
|         old_options = qdict_clone_shallow(bs->explicit_options); | ||||
|     } | ||||
|     bdrv_join_options(bs, options, old_options); | ||||
|     qobject_unref(old_options); | ||||
|     QDECREF(old_options); | ||||
|  | ||||
|     explicit_options = qdict_clone_shallow(options); | ||||
|  | ||||
|     /* Inherit from parent node */ | ||||
|     if (parent_options) { | ||||
|         QemuOpts *opts; | ||||
|         QDict *options_copy; | ||||
|         assert(!flags); | ||||
|         role->inherit_options(&flags, options, parent_flags, parent_options); | ||||
|         options_copy = qdict_clone_shallow(options); | ||||
|         opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort); | ||||
|         qemu_opts_absorb_qdict(opts, options_copy, NULL); | ||||
|         update_flags_from_options(&flags, opts); | ||||
|         qemu_opts_del(opts); | ||||
|         qobject_unref(options_copy); | ||||
|     } | ||||
|  | ||||
|     /* Old values are used for options that aren't set yet */ | ||||
|     old_options = qdict_clone_shallow(bs->options); | ||||
|     bdrv_join_options(bs, options, old_options); | ||||
|     qobject_unref(old_options); | ||||
|     QDECREF(old_options); | ||||
|  | ||||
|     /* bdrv_open_inherit() sets and clears some additional flags internally */ | ||||
|     flags &= ~BDRV_O_PROTOCOL; | ||||
| @@ -2951,8 +2902,8 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, | ||||
|         bs_entry = g_new0(BlockReopenQueueEntry, 1); | ||||
|         QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry); | ||||
|     } else { | ||||
|         qobject_unref(bs_entry->state.options); | ||||
|         qobject_unref(bs_entry->state.explicit_options); | ||||
|         QDECREF(bs_entry->state.options); | ||||
|         QDECREF(bs_entry->state.explicit_options); | ||||
|     } | ||||
|  | ||||
|     bs_entry->state.bs = bs; | ||||
| @@ -3042,9 +2993,9 @@ cleanup: | ||||
|         if (ret && bs_entry->prepared) { | ||||
|             bdrv_reopen_abort(&bs_entry->state); | ||||
|         } else if (ret) { | ||||
|             qobject_unref(bs_entry->state.explicit_options); | ||||
|             QDECREF(bs_entry->state.explicit_options); | ||||
|         } | ||||
|         qobject_unref(bs_entry->state.options); | ||||
|         QDECREF(bs_entry->state.options); | ||||
|         g_free(bs_entry); | ||||
|     } | ||||
|     g_free(bs_queue); | ||||
| @@ -3287,7 +3238,7 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state) | ||||
|     } | ||||
|  | ||||
|     /* set BDS specific flags now */ | ||||
|     qobject_unref(bs->explicit_options); | ||||
|     QDECREF(bs->explicit_options); | ||||
|  | ||||
|     bs->explicit_options   = reopen_state->explicit_options; | ||||
|     bs->open_flags         = reopen_state->flags; | ||||
| @@ -3330,7 +3281,7 @@ void bdrv_reopen_abort(BDRVReopenState *reopen_state) | ||||
|         drv->bdrv_reopen_abort(reopen_state); | ||||
|     } | ||||
|  | ||||
|     qobject_unref(reopen_state->explicit_options); | ||||
|     QDECREF(reopen_state->explicit_options); | ||||
|  | ||||
|     bdrv_abort_perm_update(reopen_state->bs); | ||||
| } | ||||
| @@ -3377,11 +3328,11 @@ static void bdrv_close(BlockDriverState *bs) | ||||
|     bs->total_sectors = 0; | ||||
|     bs->encrypted = false; | ||||
|     bs->sg = false; | ||||
|     qobject_unref(bs->options); | ||||
|     qobject_unref(bs->explicit_options); | ||||
|     QDECREF(bs->options); | ||||
|     QDECREF(bs->explicit_options); | ||||
|     bs->options = NULL; | ||||
|     bs->explicit_options = NULL; | ||||
|     qobject_unref(bs->full_open_options); | ||||
|     QDECREF(bs->full_open_options); | ||||
|     bs->full_open_options = NULL; | ||||
|  | ||||
|     bdrv_release_named_dirty_bitmaps(bs); | ||||
| @@ -3396,7 +3347,7 @@ static void bdrv_close(BlockDriverState *bs) | ||||
|  | ||||
| void bdrv_close_all(void) | ||||
| { | ||||
|     assert(job_next(NULL) == NULL); | ||||
|     block_job_cancel_sync_all(); | ||||
|     nbd_export_close_all(); | ||||
|  | ||||
|     /* Drop references from requests still in flight, such as canceled block | ||||
| @@ -3417,39 +3368,16 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to) | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /* If the child @c belongs to the BDS @to, replacing the current | ||||
|      * c->bs by @to would mean to create a loop. | ||||
|      * | ||||
|      * Such a case occurs when appending a BDS to a backing chain. | ||||
|      * For instance, imagine the following chain: | ||||
|      * | ||||
|      *   guest device -> node A -> further backing chain... | ||||
|      * | ||||
|      * Now we create a new BDS B which we want to put on top of this | ||||
|      * chain, so we first attach A as its backing node: | ||||
|      * | ||||
|      *                   node B | ||||
|      *                     | | ||||
|      *                     v | ||||
|      *   guest device -> node A -> further backing chain... | ||||
|      * | ||||
|      * Finally we want to replace A by B.  When doing that, we want to | ||||
|      * replace all pointers to A by pointers to B -- except for the | ||||
|      * pointer from B because (1) that would create a loop, and (2) | ||||
|      * that pointer should simply stay intact: | ||||
|      * | ||||
|      *   guest device -> node B | ||||
|      *                     | | ||||
|      *                     v | ||||
|      *                   node A -> further backing chain... | ||||
|      * | ||||
|      * In general, when replacing a node A (c->bs) by a node B (@to), | ||||
|      * if A is a child of B, that means we cannot replace A by B there | ||||
|      * because that would create a loop.  Silently detaching A from B | ||||
|      * is also not really an option.  So overall just leaving A in | ||||
|      * place there is the most sensible choice. */ | ||||
|     if (c->role == &child_backing) { | ||||
|         /* If @from is a backing file of @to, ignore the child to avoid | ||||
|          * creating a loop. We only want to change the pointer of other | ||||
|          * parents. */ | ||||
|         QLIST_FOREACH(to_c, &to->children, next) { | ||||
|             if (to_c == c) { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         if (to_c) { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| @@ -3475,7 +3403,6 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, | ||||
|  | ||||
|     /* Put all parents into @list and calculate their cumulative permissions */ | ||||
|     QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { | ||||
|         assert(c->bs == from); | ||||
|         if (!should_update_child(c, to)) { | ||||
|             continue; | ||||
|         } | ||||
| @@ -3744,12 +3671,12 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, | ||||
|         GSList *ignore_children = g_slist_prepend(NULL, c); | ||||
|         bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm, | ||||
|                                ignore_children, &local_err); | ||||
|         g_slist_free(ignore_children); | ||||
|         if (local_err) { | ||||
|             ret = -EPERM; | ||||
|             error_report_err(local_err); | ||||
|             goto exit; | ||||
|         } | ||||
|         g_slist_free(ignore_children); | ||||
|  | ||||
|         /* If so, update the backing file path in the image file */ | ||||
|         if (c->role->update_filename) { | ||||
| @@ -3775,6 +3702,58 @@ exit: | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Truncate file to 'offset' bytes (needed only for file protocols) | ||||
|  */ | ||||
| int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, | ||||
|                   Error **errp) | ||||
| { | ||||
|     BlockDriverState *bs = child->bs; | ||||
|     BlockDriver *drv = bs->drv; | ||||
|     int ret; | ||||
|  | ||||
|     assert(child->perm & BLK_PERM_RESIZE); | ||||
|  | ||||
|     /* if bs->drv == NULL, bs is closed, so there's nothing to do here */ | ||||
|     if (!drv) { | ||||
|         error_setg(errp, "No medium inserted"); | ||||
|         return -ENOMEDIUM; | ||||
|     } | ||||
|     if (offset < 0) { | ||||
|         error_setg(errp, "Image size cannot be negative"); | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     if (!drv->bdrv_truncate) { | ||||
|         if (bs->file && drv->is_filter) { | ||||
|             return bdrv_truncate(bs->file, offset, prealloc, errp); | ||||
|         } | ||||
|         error_setg(errp, "Image format driver does not support resize"); | ||||
|         return -ENOTSUP; | ||||
|     } | ||||
|     if (bs->read_only) { | ||||
|         error_setg(errp, "Image is read-only"); | ||||
|         return -EACCES; | ||||
|     } | ||||
|  | ||||
|     assert(!(bs->open_flags & BDRV_O_INACTIVE)); | ||||
|  | ||||
|     ret = drv->bdrv_truncate(bs, offset, prealloc, errp); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|     ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); | ||||
|     if (ret < 0) { | ||||
|         error_setg_errno(errp, -ret, "Could not refresh total sector count"); | ||||
|     } else { | ||||
|         offset = bs->total_sectors * BDRV_SECTOR_SIZE; | ||||
|     } | ||||
|     bdrv_dirty_bitmap_truncate(bs, offset); | ||||
|     bdrv_parent_cb_resize(bs); | ||||
|     atomic_inc(&bs->write_gen); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Length of a allocated file in bytes. Sparse files are counted by actual | ||||
|  * allocated space. Return < 0 if error or unknown. | ||||
| @@ -4031,14 +4010,6 @@ BlockDriverState *bdrv_next_node(BlockDriverState *bs) | ||||
|     return QTAILQ_NEXT(bs, node_list); | ||||
| } | ||||
|  | ||||
| BlockDriverState *bdrv_next_all_states(BlockDriverState *bs) | ||||
| { | ||||
|     if (!bs) { | ||||
|         return QTAILQ_FIRST(&all_bdrv_states); | ||||
|     } | ||||
|     return QTAILQ_NEXT(bs, bs_list); | ||||
| } | ||||
|  | ||||
| const char *bdrv_get_node_name(const BlockDriverState *bs) | ||||
| { | ||||
|     return bs->node_name; | ||||
| @@ -4950,7 +4921,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context) | ||||
|     AioContext *ctx = bdrv_get_aio_context(bs); | ||||
|  | ||||
|     aio_disable_external(ctx); | ||||
|     bdrv_parent_drained_begin(bs, NULL, false); | ||||
|     bdrv_parent_drained_begin(bs, NULL); | ||||
|     bdrv_drain(bs); /* ensure there are no in-flight requests */ | ||||
|  | ||||
|     while (aio_poll(ctx, false)) { | ||||
| @@ -4964,7 +4935,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context) | ||||
|      */ | ||||
|     aio_context_acquire(new_context); | ||||
|     bdrv_attach_aio_context(bs, new_context); | ||||
|     bdrv_parent_drained_end(bs, NULL, false); | ||||
|     bdrv_parent_drained_end(bs, NULL); | ||||
|     aio_enable_external(ctx); | ||||
|     aio_context_release(new_context); | ||||
| } | ||||
| @@ -5010,19 +4981,15 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs, | ||||
| } | ||||
|  | ||||
| int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts, | ||||
|                        BlockDriverAmendStatusCB *status_cb, void *cb_opaque, | ||||
|                        Error **errp) | ||||
|                        BlockDriverAmendStatusCB *status_cb, void *cb_opaque) | ||||
| { | ||||
|     if (!bs->drv) { | ||||
|         error_setg(errp, "Node is ejected"); | ||||
|         return -ENOMEDIUM; | ||||
|     } | ||||
|     if (!bs->drv->bdrv_amend_options) { | ||||
|         error_setg(errp, "Block driver '%s' does not support option amendment", | ||||
|                    bs->drv->format_name); | ||||
|         return -ENOTSUP; | ||||
|     } | ||||
|     return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque, errp); | ||||
|     return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque); | ||||
| } | ||||
|  | ||||
| /* This function will be called by the bdrv_recurse_is_first_non_filter method | ||||
| @@ -5152,8 +5119,8 @@ static bool append_open_options(QDict *d, BlockDriverState *bs) | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         qdict_put_obj(d, qdict_entry_key(entry), | ||||
|                       qobject_ref(qdict_entry_value(entry))); | ||||
|         qobject_incref(qdict_entry_value(entry)); | ||||
|         qdict_put_obj(d, qdict_entry_key(entry), qdict_entry_value(entry)); | ||||
|         found_any = true; | ||||
|     } | ||||
|  | ||||
| @@ -5192,21 +5159,21 @@ void bdrv_refresh_filename(BlockDriverState *bs) | ||||
|          * information before refreshing it */ | ||||
|         bs->exact_filename[0] = '\0'; | ||||
|         if (bs->full_open_options) { | ||||
|             qobject_unref(bs->full_open_options); | ||||
|             QDECREF(bs->full_open_options); | ||||
|             bs->full_open_options = NULL; | ||||
|         } | ||||
|  | ||||
|         opts = qdict_new(); | ||||
|         append_open_options(opts, bs); | ||||
|         drv->bdrv_refresh_filename(bs, opts); | ||||
|         qobject_unref(opts); | ||||
|         QDECREF(opts); | ||||
|     } else if (bs->file) { | ||||
|         /* Try to reconstruct valid information from the underlying file */ | ||||
|         bool has_open_options; | ||||
|  | ||||
|         bs->exact_filename[0] = '\0'; | ||||
|         if (bs->full_open_options) { | ||||
|             qobject_unref(bs->full_open_options); | ||||
|             QDECREF(bs->full_open_options); | ||||
|             bs->full_open_options = NULL; | ||||
|         } | ||||
|  | ||||
| @@ -5225,12 +5192,12 @@ void bdrv_refresh_filename(BlockDriverState *bs) | ||||
|          * suffices without querying the (exact_)filename of this BDS. */ | ||||
|         if (bs->file->bs->full_open_options) { | ||||
|             qdict_put_str(opts, "driver", drv->format_name); | ||||
|             qdict_put(opts, "file", | ||||
|                       qobject_ref(bs->file->bs->full_open_options)); | ||||
|             QINCREF(bs->file->bs->full_open_options); | ||||
|             qdict_put(opts, "file", bs->file->bs->full_open_options); | ||||
|  | ||||
|             bs->full_open_options = opts; | ||||
|         } else { | ||||
|             qobject_unref(opts); | ||||
|             QDECREF(opts); | ||||
|         } | ||||
|     } else if (!bs->full_open_options && qdict_size(bs->options)) { | ||||
|         /* There is no underlying file BDS (at least referenced by BDS.file), | ||||
| @@ -5264,7 +5231,7 @@ void bdrv_refresh_filename(BlockDriverState *bs) | ||||
|         QString *json = qobject_to_json(QOBJECT(bs->full_open_options)); | ||||
|         snprintf(bs->filename, sizeof(bs->filename), "json:%s", | ||||
|                  qstring_get_str(json)); | ||||
|         qobject_unref(json); | ||||
|         QDECREF(json); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -5,7 +5,6 @@ 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 += parallels.o blkdebug.o blkverify.o blkreplay.o | ||||
| block-obj-y += blklogwrites.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 | ||||
| @@ -27,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 | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
							
								
								
									
										258
									
								
								block/backup.c
									
									
									
									
									
								
							
							
						
						
									
										258
									
								
								block/backup.c
									
									
									
									
									
								
							| @@ -27,6 +27,7 @@ | ||||
| #include "qemu/error-report.h" | ||||
|  | ||||
| #define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16) | ||||
| #define SLICE_TIME 100000000ULL /* ns */ | ||||
|  | ||||
| typedef struct BackupBlockJob { | ||||
|     BlockJob common; | ||||
| @@ -34,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; | ||||
| @@ -45,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, | ||||
| @@ -90,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 occured, 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 occured, 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); | ||||
|  | ||||
| @@ -199,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); | ||||
|         n = MIN(job->cluster_size, job->common.len - start); | ||||
|  | ||||
|         if (!bounce_buffer) { | ||||
|             bounce_buffer = blk_blockalign(blk, job->cluster_size); | ||||
|         } | ||||
|         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) { | ||||
|                 job->use_copy_range = false; | ||||
|             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 (!job->use_copy_range) { | ||||
|             ret = backup_cow_with_bounce_buffer(job, start, end, is_write_notifier, | ||||
|                                                 error_is_read, &bounce_buffer); | ||||
|  | ||||
|         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); | ||||
|     } | ||||
| @@ -258,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); | ||||
| @@ -274,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; | ||||
| @@ -310,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" | ||||
| @@ -318,7 +261,7 @@ 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); | ||||
| } | ||||
|  | ||||
| @@ -328,7 +271,7 @@ void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset, | ||||
|     BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); | ||||
|     int64_t start, end; | ||||
|  | ||||
|     assert(block_job_driver(job) == &backup_job_driver); | ||||
|     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); | ||||
| @@ -341,7 +284,7 @@ void backup_cow_request_begin(CowRequest *req, BlockJob *job, | ||||
|     BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); | ||||
|     int64_t start, end; | ||||
|  | ||||
|     assert(block_job_driver(job) == &backup_job_driver); | ||||
|     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); | ||||
| @@ -384,29 +327,33 @@ typedef struct { | ||||
|     int ret; | ||||
| } BackupCompleteData; | ||||
|  | ||||
| static void backup_complete(Job *job, void *opaque) | ||||
| static void backup_complete(BlockJob *job, void *opaque) | ||||
| { | ||||
|     BackupCompleteData *data = opaque; | ||||
|  | ||||
|     job_completed(job, data->ret, NULL); | ||||
|     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); | ||||
|     /* 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; | ||||
|     job_sleep_ns(&job->common.job, delay_ns); | ||||
|         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; | ||||
|     } | ||||
|  | ||||
| @@ -421,7 +368,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) | ||||
|     HBitmapIter hbi; | ||||
|  | ||||
|     hbitmap_iter_init(&hbi, job->copy_bitmap, 0); | ||||
|     while ((cluster = hbitmap_iter_next(&hbi, true)) != -1) { | ||||
|     while ((cluster = hbitmap_iter_next(&hbi)) != -1) { | ||||
|         do { | ||||
|             if (yield_and_check(job)) { | ||||
|                 return 0; | ||||
| @@ -473,9 +420,8 @@ 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); | ||||
| } | ||||
| @@ -491,9 +437,7 @@ static void coroutine_fn backup_run(void *opaque) | ||||
|     QLIST_INIT(&job->inflight_reqs); | ||||
|     qemu_co_rwlock_init(&job->flush_rwlock); | ||||
|  | ||||
|     nb_clusters = DIV_ROUND_UP(job->len, job->cluster_size); | ||||
|     job_progress_set_remaining(&job->common.job, job->len); | ||||
|  | ||||
|     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); | ||||
| @@ -508,16 +452,16 @@ static void coroutine_fn backup_run(void *opaque) | ||||
|     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->common.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->common.job); | ||||
|             block_job_yield(&job->common); | ||||
|         } | ||||
|     } 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 < job->len; | ||||
|         for (offset = 0; offset < job->common.len; | ||||
|              offset += job->cluster_size) { | ||||
|             bool error_is_read; | ||||
|             int alloced = 0; | ||||
| @@ -586,21 +530,17 @@ static void coroutine_fn backup_run(void *opaque) | ||||
|  | ||||
|     data = g_malloc(sizeof(*data)); | ||||
|     data->ret = ret; | ||||
|     job_defer_to_main_loop(&job->common.job, backup_complete, data); | ||||
|     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, | ||||
|     .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, | ||||
| }; | ||||
| @@ -613,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; | ||||
| @@ -680,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, | ||||
| @@ -706,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. */ | ||||
| @@ -735,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; | ||||
|  | ||||
| @@ -754,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; | ||||
|   | ||||
| @@ -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,550 +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_obj(opts, "file", QOBJECT(bs->file->bs->full_open_options)); | ||||
|         qobject_ref(s->log_file->bs->full_open_options); | ||||
|         qdict_put_obj(opts, "log", | ||||
|                       QOBJECT(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); | ||||
| @@ -35,9 +35,6 @@ 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; | ||||
| @@ -113,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(); | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
| @@ -76,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; | ||||
| @@ -255,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, | ||||
|  | ||||
| @@ -298,9 +260,6 @@ static const BdrvChildRole child_root = { | ||||
|  | ||||
|     .activate           = blk_root_activate, | ||||
|     .inactivate         = blk_root_inactivate, | ||||
|  | ||||
|     .attach             = blk_root_attach, | ||||
|     .detach             = blk_root_detach, | ||||
| }; | ||||
|  | ||||
| /* | ||||
| @@ -328,7 +287,6 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm) | ||||
|  | ||||
|     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; | ||||
| @@ -406,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); | ||||
| @@ -768,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; | ||||
| } | ||||
| @@ -1560,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) | ||||
| @@ -1870,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) | ||||
| @@ -1899,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); | ||||
| @@ -1920,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) | ||||
| @@ -2216,22 +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); | ||||
| } | ||||
|   | ||||
| @@ -31,8 +31,11 @@ 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; | ||||
| @@ -72,10 +75,9 @@ typedef struct { | ||||
|     int ret; | ||||
| } CommitCompleteData; | ||||
|  | ||||
| static void commit_complete(Job *job, void *opaque) | ||||
| static void commit_complete(BlockJob *job, void *opaque) | ||||
| { | ||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); | ||||
|     BlockJob *bjob = &s->common; | ||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common); | ||||
|     CommitCompleteData *data = opaque; | ||||
|     BlockDriverState *top = blk_bs(s->top); | ||||
|     BlockDriverState *base = blk_bs(s->base); | ||||
| @@ -91,7 +93,7 @@ static void commit_complete(Job *job, void *opaque) | ||||
|      * the normal backing chain can be restored. */ | ||||
|     blk_unref(s->base); | ||||
|  | ||||
|     if (!job_is_cancelled(job) && ret == 0) { | ||||
|     if (!block_job_is_cancelled(&s->common) && ret == 0) { | ||||
|         /* success */ | ||||
|         ret = bdrv_drop_intermediate(s->commit_top_bs, base, | ||||
|                                      s->backing_file_str); | ||||
| @@ -112,12 +114,12 @@ static void commit_complete(Job *job, void *opaque) | ||||
|     blk_unref(s->top); | ||||
|  | ||||
|     /* If there is more than one reference to the job (e.g. if called from | ||||
|      * job_finish_sync()), 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(bjob); | ||||
|      * 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); | ||||
|  | ||||
|     job_completed(job, ret, NULL); | ||||
|     block_job_completed(&s->common, ret); | ||||
|     g_free(data); | ||||
|  | ||||
|     /* If bdrv_drop_intermediate() didn't already do that, remove the commit | ||||
| @@ -144,21 +146,21 @@ static void coroutine_fn commit_run(void *opaque) | ||||
|     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; | ||||
|         } | ||||
| @@ -166,14 +168,14 @@ static void coroutine_fn commit_run(void *opaque) | ||||
|  | ||||
|     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 */ | ||||
| @@ -196,12 +198,10 @@ static void coroutine_fn commit_run(void *opaque) | ||||
|             } | ||||
|         } | ||||
|         /* 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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -212,18 +212,25 @@ out: | ||||
|  | ||||
|     data = g_malloc(sizeof(*data)); | ||||
|     data->ret = ret; | ||||
|     job_defer_to_main_loop(&s->common.job, commit_complete, data); | ||||
|     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, | ||||
|     .job_type      = BLOCK_JOB_TYPE_COMMIT, | ||||
|     .set_speed     = commit_set_speed, | ||||
|     .start         = commit_run, | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs, | ||||
| @@ -282,8 +289,8 @@ 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, JOB_DEFAULT, 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; | ||||
|     } | ||||
| @@ -373,7 +380,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, | ||||
|     s->on_error = on_error; | ||||
|  | ||||
|     trace_commit_start(bs, base, top, s); | ||||
|     job_start(&s->common.job); | ||||
|     block_job_start(&s->common); | ||||
|     return; | ||||
|  | ||||
| fail: | ||||
| @@ -386,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); | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,173 +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; | ||||
| } | ||||
|  | ||||
|  | ||||
| static void cor_close(BlockDriverState *bs) | ||||
| { | ||||
| } | ||||
|  | ||||
|  | ||||
| #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_close                         = cor_close, | ||||
|     .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,51 +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; | ||||
|     int ret; | ||||
|     Error *err; | ||||
| } BlockdevCreateJob; | ||||
|     Error **errp; | ||||
| } BlockdevCreateCo; | ||||
|  | ||||
| static void blockdev_create_complete(Job *job, void *opaque) | ||||
| static void coroutine_fn bdrv_co_create_co_entry(void *opaque) | ||||
| { | ||||
|     BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); | ||||
|  | ||||
|     job_completed(job, s->ret, s->err); | ||||
|     BlockdevCreateCo *cco = opaque; | ||||
|     cco->ret = cco->drv->bdrv_co_create(cco->opts, cco->errp); | ||||
| } | ||||
|  | ||||
| static void coroutine_fn blockdev_create_run(void *opaque) | ||||
| void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp) | ||||
| { | ||||
|     BlockdevCreateJob *s = opaque; | ||||
|  | ||||
|     job_progress_set_remaining(&s->common, 1); | ||||
|     s->ret = s->drv->bdrv_co_create(s->opts, &s->err); | ||||
|     job_progress_update(&s->common, 1); | ||||
|  | ||||
|     qapi_free_BlockdevCreateOptions(s->opts); | ||||
|     job_defer_to_main_loop(&s->common, blockdev_create_complete, NULL); | ||||
| } | ||||
|  | ||||
| static const JobDriver blockdev_create_job_driver = { | ||||
|     .instance_size = sizeof(BlockdevCreateJob), | ||||
|     .job_type      = JOB_TYPE_CREATE, | ||||
|     .start         = blockdev_create_run, | ||||
| }; | ||||
|  | ||||
| void qmp_blockdev_create(const char *job_id, 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. */ | ||||
| @@ -78,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, | ||||
|     }; | ||||
|  | ||||
|     s->drv = drv, | ||||
|     s->opts = QAPI_CLONE(BlockdevCreateOptions, options), | ||||
|  | ||||
|     job_start(&s->common); | ||||
|     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); | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										260
									
								
								block/crypto.c
									
									
									
									
									
								
							
							
						
						
									
										260
									
								
								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; | ||||
|     } | ||||
| @@ -240,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, | ||||
| 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, | ||||
| @@ -281,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, | ||||
| 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) | ||||
| @@ -463,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; | ||||
|  | ||||
| @@ -491,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,9 +623,8 @@ BlockDriver bdrv_crypto_luks = { | ||||
|     .bdrv_open          = block_crypto_open_luks, | ||||
|     .bdrv_close         = block_crypto_close, | ||||
|     .bdrv_child_perm    = bdrv_format_default_perms, | ||||
|     .bdrv_co_create     = block_crypto_co_create_luks, | ||||
|     .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__ */ | ||||
|   | ||||
| @@ -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 */ | ||||
| @@ -97,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, | ||||
| @@ -176,18 +183,6 @@ bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap) | ||||
|     return bitmap->successor; | ||||
| } | ||||
|  | ||||
| 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) | ||||
| { | ||||
| @@ -199,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 { | ||||
| @@ -241,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. | ||||
| @@ -301,9 +267,9 @@ 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 *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, | ||||
|                                            BdrvDirtyBitmap *parent, | ||||
|                                            Error **errp) | ||||
| { | ||||
| @@ -318,26 +284,12 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, | ||||
|         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. | ||||
| @@ -356,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); | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -372,15 +357,7 @@ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) | ||||
|  */ | ||||
| void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) | ||||
| { | ||||
|     BdrvDirtyBitmap *bm, *next; | ||||
|  | ||||
|     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); | ||||
|     bdrv_do_release_matching_dirty_bitmap(bs, NULL, bdrv_dirty_bitmap_has_name); | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -388,19 +365,11 @@ void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) | ||||
|  * bdrv_inactivate_recurse()). | ||||
|  * There must not be any frozen bitmaps attached. | ||||
|  * This function does not remove persistent bitmaps from the storage. | ||||
|  * Called with BQL taken. | ||||
|  */ | ||||
| void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs) | ||||
| { | ||||
|     BdrvDirtyBitmap *bm, *next; | ||||
|  | ||||
|     bdrv_dirty_bitmaps_lock(bs); | ||||
|     QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { | ||||
|         if (bdrv_dirty_bitmap_get_persistance(bm)) { | ||||
|             bdrv_release_dirty_bitmap_locked(bm); | ||||
|         } | ||||
|     } | ||||
|     bdrv_dirty_bitmaps_unlock(bs); | ||||
|     bdrv_do_release_matching_dirty_bitmap(bs, NULL, | ||||
|                                           bdrv_dirty_bitmap_get_persistance); | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -420,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) | ||||
| @@ -525,62 +493,7 @@ void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter) | ||||
|  | ||||
| int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) | ||||
| { | ||||
|     return hbitmap_iter_next(&iter->hbi, true); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Return the next consecutively dirty area in the dirty bitmap | ||||
|  * belonging to the given iterator @iter. | ||||
|  * | ||||
|  * @max_offset: Maximum value that may be returned for | ||||
|  *              *offset + *bytes | ||||
|  * @offset:     Will contain the start offset of the next dirty area | ||||
|  * @bytes:      Will contain the length of the next dirty area | ||||
|  * | ||||
|  * Returns: True if a dirty area could be found before max_offset | ||||
|  *          (which means that *offset and *bytes then contain valid | ||||
|  *          values), false otherwise. | ||||
|  * | ||||
|  * Note that @iter is never advanced if false is returned.  If an area | ||||
|  * is found (which means that true is returned), it will be advanced | ||||
|  * past that area. | ||||
|  */ | ||||
| bool bdrv_dirty_iter_next_area(BdrvDirtyBitmapIter *iter, uint64_t max_offset, | ||||
|                                uint64_t *offset, int *bytes) | ||||
| { | ||||
|     uint32_t granularity = bdrv_dirty_bitmap_granularity(iter->bitmap); | ||||
|     uint64_t gran_max_offset; | ||||
|     int64_t ret; | ||||
|     int size; | ||||
|  | ||||
|     if (max_offset == iter->bitmap->size) { | ||||
|         /* If max_offset points to the image end, round it up by the | ||||
|          * bitmap granularity */ | ||||
|         gran_max_offset = ROUND_UP(max_offset, granularity); | ||||
|     } else { | ||||
|         gran_max_offset = max_offset; | ||||
|     } | ||||
|  | ||||
|     ret = hbitmap_iter_next(&iter->hbi, false); | ||||
|     if (ret < 0 || ret + granularity > gran_max_offset) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     *offset = ret; | ||||
|     size = 0; | ||||
|  | ||||
|     assert(granularity <= INT_MAX); | ||||
|  | ||||
|     do { | ||||
|         /* Advance iterator */ | ||||
|         ret = hbitmap_iter_next(&iter->hbi, true); | ||||
|         size += granularity; | ||||
|     } while (ret + granularity <= gran_max_offset && | ||||
|              hbitmap_iter_next(&iter->hbi, false) == ret + granularity && | ||||
|              size <= INT_MAX - granularity); | ||||
|  | ||||
|     *bytes = MIN(size, max_offset - *offset); | ||||
|     return true; | ||||
|     return hbitmap_iter_next(&iter->hbi); | ||||
| } | ||||
|  | ||||
| /* Called within bdrv_dirty_bitmap_lock..unlock */ | ||||
| @@ -789,21 +702,3 @@ int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset) | ||||
| { | ||||
|     return hbitmap_next_zero(bitmap->bitmap, offset); | ||||
| } | ||||
|  | ||||
| void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, | ||||
|                              Error **errp) | ||||
| { | ||||
|     /* only bitmaps from one bds are supported */ | ||||
|     assert(dest->mutex == src->mutex); | ||||
|  | ||||
|     qemu_mutex_lock(dest->mutex); | ||||
|  | ||||
|     assert(bdrv_dirty_bitmap_enabled(dest)); | ||||
|     assert(!bdrv_dirty_bitmap_readonly(dest)); | ||||
|  | ||||
|     if (!hbitmap_merge(dest->bitmap, src->bitmap)) { | ||||
|         error_setg(errp, "Bitmaps are incompatible and can't be merged"); | ||||
|     } | ||||
|  | ||||
|     qemu_mutex_unlock(dest->mutex); | ||||
| } | ||||
|   | ||||
										
											
												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); | ||||
| } | ||||
| @@ -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, | ||||
| 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, | ||||
| 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,7 +463,7 @@ static void raw_close(BlockDriverState *bs) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, | ||||
| static int raw_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                         PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
| @@ -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" | ||||
| @@ -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; | ||||
|             } | ||||
|             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; | ||||
|         } | ||||
| @@ -1177,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); | ||||
| @@ -1197,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); | ||||
| } | ||||
|  | ||||
| @@ -1501,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, | ||||
| @@ -1530,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, | ||||
| @@ -1559,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, | ||||
| @@ -1594,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, | ||||
|   | ||||
							
								
								
									
										885
									
								
								block/io.c
									
									
									
									
									
								
							
							
						
						
									
										885
									
								
								block/io.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										362
									
								
								block/iscsi.c
									
									
									
									
									
								
							
							
						
						
									
										362
									
								
								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,7 +43,6 @@ | ||||
| #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 | ||||
| @@ -70,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 | ||||
| @@ -558,19 +555,8 @@ 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, | ||||
| iscsi_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors, | ||||
|                       QEMUIOVector *iov, int flags) | ||||
| { | ||||
|     IscsiLun *iscsilun = bs->opaque; | ||||
| @@ -630,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); | ||||
| @@ -701,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) { | ||||
| @@ -735,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) { | ||||
| @@ -865,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; | ||||
| @@ -903,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); | ||||
| @@ -1135,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); | ||||
| @@ -1231,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 && | ||||
| @@ -1715,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) | ||||
| { | ||||
| @@ -1754,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) { | ||||
| @@ -1910,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, | ||||
| @@ -1936,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; | ||||
|         } | ||||
| @@ -1989,7 +1989,7 @@ out: | ||||
|         } | ||||
|         memset(iscsilun, 0, sizeof(IscsiLun)); | ||||
|     } | ||||
|  | ||||
| exit: | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| @@ -2003,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); | ||||
| @@ -2086,7 +2082,7 @@ static void iscsi_reopen_commit(BDRVReopenState *reopen_state) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset, | ||||
| static int iscsi_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                           PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     IscsiLun *iscsilun = bs->opaque; | ||||
| @@ -2147,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; | ||||
| @@ -2188,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), | ||||
| @@ -2437,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__ | ||||
| @@ -2470,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> | ||||
|  | ||||
| @@ -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; | ||||
|     } | ||||
|  | ||||
|   | ||||
							
								
								
									
										752
									
								
								block/mirror.c
									
									
									
									
									
								
							
							
						
						
									
										752
									
								
								block/mirror.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -228,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.meta_base_allocation_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.meta_base_allocation_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 | ||||
|  */ | ||||
| @@ -527,7 +481,6 @@ static coroutine_fn int nbd_co_receive_one_chunk( | ||||
|  | ||||
| typedef struct NBDReplyChunkIter { | ||||
|     int ret; | ||||
|     bool fatal; | ||||
|     Error *err; | ||||
|     bool done, only_structured; | ||||
| } NBDReplyChunkIter; | ||||
| @@ -537,12 +490,11 @@ static void nbd_iter_error(NBDReplyChunkIter *iter, bool fatal, | ||||
| { | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     if ((fatal && !iter->fatal) || iter->ret == 0) { | ||||
|     if (fatal || iter->ret == 0) { | ||||
|         if (iter->ret != 0) { | ||||
|             error_free(iter->err); | ||||
|             iter->err = NULL; | ||||
|         } | ||||
|         iter->fatal = fatal; | ||||
|         iter->ret = ret; | ||||
|         error_propagate(&iter->err, *local_err); | ||||
|     } else { | ||||
| @@ -688,68 +640,6 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle, | ||||
|     return iter.ret; | ||||
| } | ||||
|  | ||||
| static int nbd_co_receive_blockstatus_reply(NBDClientSession *s, | ||||
|                                             uint64_t handle, uint64_t length, | ||||
|                                             NBDExtent *extent, 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_error(&iter, true, -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_error(&iter, true, 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_error(&iter, true, -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); | ||||
|     return iter.ret; | ||||
| } | ||||
|  | ||||
| static int nbd_co_request(BlockDriverState *bs, NBDRequest *request, | ||||
|                           QEMUIOVector *write_qiov) | ||||
| { | ||||
| @@ -892,51 +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) | ||||
| { | ||||
|     int64_t 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, &local_err); | ||||
|     if (local_err) { | ||||
|         error_report_err(local_err); | ||||
|     } | ||||
|     if (ret < 0) { | ||||
|         return 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 +815,6 @@ int nbd_client_init(BlockDriverState *bs, | ||||
|                     const char *export, | ||||
|                     QCryptoTLSCreds *tlscreds, | ||||
|                     const char *hostname, | ||||
|                     const char *x_dirty_bitmap, | ||||
|                     Error **errp) | ||||
| { | ||||
|     NBDClientSession *client = nbd_get_client_session(bs); | ||||
| @@ -982,12 +826,9 @@ int nbd_client_init(BlockDriverState *bs, | ||||
|  | ||||
|     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); | ||||
|     ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export, | ||||
|                                 tlscreds, hostname, | ||||
|                                 &client->ioc, &client->info, errp); | ||||
|     g_free(client->info.x_dirty_bitmap); | ||||
|     if (ret < 0) { | ||||
|         logout("Failed to negotiate with the NBD server\n"); | ||||
|         return ret; | ||||
|   | ||||
| @@ -45,7 +45,6 @@ int nbd_client_init(BlockDriverState *bs, | ||||
|                     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 */ | ||||
|   | ||||
							
								
								
									
										33
									
								
								block/nbd.c
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								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" | ||||
| @@ -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; | ||||
| } | ||||
| @@ -378,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 */ } | ||||
|     }, | ||||
| }; | ||||
| @@ -444,8 +448,8 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     } | ||||
|  | ||||
|     /* NBD handshake */ | ||||
|     ret = nbd_client_init(bs, sioc, s->export, tlscreds, hostname, | ||||
|                           qemu_opt_get(opts, "x-dirty-bitmap"), errp); | ||||
|     ret = nbd_client_init(bs, sioc, s->export, | ||||
|                           tlscreds, hostname, errp); | ||||
|  error: | ||||
|     if (sioc) { | ||||
|         object_unref(OBJECT(sioc)); | ||||
| @@ -581,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 = { | ||||
| @@ -601,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 = { | ||||
| @@ -621,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) | ||||
|   | ||||
							
								
								
									
										23
									
								
								block/nfs.c
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								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,8 +737,7 @@ 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, | ||||
| static int nfs_file_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                              PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     NFSClient *client = bs->opaque; | ||||
| @@ -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, | ||||
|   | ||||
							
								
								
									
										40
									
								
								block/null.c
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								block/null.c
									
									
									
									
									
								
							| @@ -93,7 +93,6 @@ 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; | ||||
| } | ||||
|  | ||||
| @@ -117,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); | ||||
| } | ||||
| @@ -187,24 +186,24 @@ 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, | ||||
| 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, | ||||
| static BlockAIOCB *null_aio_writev(BlockDriverState *bs, | ||||
|                                    int64_t sector_num, QEMUIOVector *qiov, | ||||
|                                    int nb_sectors, | ||||
|                                    BlockCompletionFunc *cb, | ||||
|                                    void *opaque) | ||||
| { | ||||
| @@ -245,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)) { | ||||
| @@ -253,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 = { | ||||
| @@ -266,8 +266,8 @@ static BlockDriver bdrv_null_co = { | ||||
|     .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, | ||||
|  | ||||
| @@ -286,8 +286,8 @@ static BlockDriver bdrv_null_aio = { | ||||
|     .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, | ||||
|  | ||||
|   | ||||
							
								
								
									
										14
									
								
								block/nvme.c
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								block/nvme.c
									
									
									
									
									
								
							| @@ -695,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)) { | ||||
| @@ -707,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")); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -1073,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)) { | ||||
| @@ -1080,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) | ||||
|   | ||||
| @@ -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, | ||||
| 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,105 +542,26 @@ 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; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int parallels_probe(const uint8_t *buf, int buf_size, | ||||
|                            const char *filename) | ||||
| @@ -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, | ||||
|   | ||||
							
								
								
									
										55
									
								
								block/qapi.c
									
									
									
									
									
								
							
							
						
						
									
										55
									
								
								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, | ||||
| @@ -688,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; | ||||
|         } | ||||
| @@ -771,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); | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										334
									
								
								block/qcow.c
									
									
									
									
									
								
							
							
						
						
									
										334
									
								
								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) | ||||
| @@ -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; | ||||
| @@ -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; | ||||
| @@ -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, | ||||
| 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,96 +959,12 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, | ||||
|     ret = 0; | ||||
| exit: | ||||
|     blk_unref(qcow_blk); | ||||
| 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; | ||||
| } | ||||
|  | ||||
| @@ -1109,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; | ||||
|         } | ||||
| @@ -1190,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 | ||||
| @@ -254,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; | ||||
|     } | ||||
|  | ||||
| @@ -775,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; | ||||
|     } | ||||
| @@ -1008,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; | ||||
| @@ -1017,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; | ||||
| @@ -1064,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); | ||||
|     } | ||||
|  | ||||
| @@ -1077,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. | ||||
|  */ | ||||
|   | ||||
| @@ -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" | ||||
|  | ||||
| @@ -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, | ||||
|   | ||||
| @@ -26,7 +26,7 @@ | ||||
| #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" | ||||
| @@ -734,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); | ||||
| @@ -839,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; | ||||
|             } | ||||
| @@ -1577,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++; | ||||
|             } | ||||
| @@ -1799,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]; | ||||
| @@ -1831,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; | ||||
| @@ -1873,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); | ||||
| @@ -2705,16 +2689,6 @@ 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; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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" | ||||
|   | ||||
							
								
								
									
										641
									
								
								block/qcow2.c
									
									
									
									
									
								
							
							
						
						
									
										641
									
								
								block/qcow2.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -77,6 +77,10 @@ | ||||
| #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 | ||||
|  | ||||
|  | ||||
| @@ -94,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" | ||||
| @@ -295,7 +298,6 @@ typedef struct BDRVQcow2State { | ||||
|     uint32_t nb_bitmaps; | ||||
|     uint64_t bitmap_directory_size; | ||||
|     uint64_t bitmap_directory_offset; | ||||
|     bool dirty_bitmaps_loaded; | ||||
|  | ||||
|     int flags; | ||||
|     int qcow_version; | ||||
| @@ -327,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 { | ||||
| @@ -409,9 +408,8 @@ typedef enum QCow2MetadataOverlap { | ||||
|     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_MAX_BITNR              = 9, | ||||
|     QCOW2_OL_MAX_BITNR            = 8, | ||||
|  | ||||
|     QCOW2_OL_NONE           = 0, | ||||
|     QCOW2_OL_MAIN_HEADER    = (1 << QCOW2_OL_MAIN_HEADER_BITNR), | ||||
| @@ -424,13 +422,12 @@ typedef enum QCow2MetadataOverlap { | ||||
|     /* 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), | ||||
| } 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 \ | ||||
| @@ -620,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); | ||||
| @@ -675,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); | ||||
|   | ||||
							
								
								
									
										214
									
								
								block/qed.c
									
									
									
									
									
								
							
							
						
						
									
										214
									
								
								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) | ||||
| @@ -600,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); | ||||
| @@ -679,25 +638,22 @@ 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; | ||||
|         } | ||||
|     } | ||||
|     } | ||||
|  | ||||
|     qed_header_cpu_to_le(&header, &le_header); | ||||
|     ret = blk_pwrite(blk, 0, &le_header, sizeof(le_header), 0); | ||||
|     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; | ||||
| @@ -713,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; | ||||
| } | ||||
|  | ||||
| @@ -721,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; | ||||
| } | ||||
|  | ||||
| @@ -1434,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); | ||||
| } | ||||
|  | ||||
| @@ -1467,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; | ||||
| @@ -1673,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), | ||||
| @@ -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--; | ||||
| @@ -614,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++) { | ||||
| @@ -649,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) | ||||
| @@ -677,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; | ||||
| @@ -703,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 { | ||||
| @@ -723,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++) { | ||||
| @@ -965,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; | ||||
|  | ||||
| @@ -1088,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,7 +352,7 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, | ||||
| 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, | ||||
| @@ -497,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), | ||||
| @@ -551,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, | ||||
|   | ||||
							
								
								
									
										152
									
								
								block/rbd.c
									
									
									
									
									
								
							
							
						
						
									
										152
									
								
								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; | ||||
|     } | ||||
|  | ||||
| @@ -661,11 +622,27 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     BDRVRBDState *s = bs->opaque; | ||||
|     BlockdevOptionsRbd *opts = NULL; | ||||
|     Visitor *v; | ||||
|     const QDictEntry *e; | ||||
|     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"); | ||||
| @@ -677,14 +654,16 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     } | ||||
|  | ||||
|     /* Convert the remaining options into a QAPI object */ | ||||
|     v = qobject_input_visitor_new_flat_confused(options, errp); | ||||
|     if (!v) { | ||||
|     crumpled = qdict_crumple(options, errp); | ||||
|     if (crumpled == NULL) { | ||||
|         r = -EINVAL; | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     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); | ||||
| @@ -692,12 +671,6 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         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); | ||||
|     } | ||||
|  | ||||
|     r = qemu_rbd_connect(&s->cluster, &s->io_ctx, opts, | ||||
|                          !(flags & BDRV_O_NOCACHE), keypairs, secretid, errp); | ||||
|     if (r < 0) { | ||||
| @@ -919,23 +892,27 @@ failed: | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| static BlockAIOCB *qemu_rbd_aio_preadv(BlockDriverState *bs, | ||||
|                                        uint64_t offset, uint64_t bytes, | ||||
|                                        QEMUIOVector *qiov, int flags, | ||||
| 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, | ||||
| 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); | ||||
| } | ||||
|  | ||||
| @@ -990,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; | ||||
| @@ -1176,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, | ||||
| @@ -1186,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, | ||||
|   | ||||
| @@ -145,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) { | ||||
| @@ -246,14 +246,13 @@ static coroutine_fn int replication_co_readv(BlockDriverState *bs, | ||||
|         backup_cow_request_begin(&req, child->bs->job, | ||||
|                                  sector_num * BDRV_SECTOR_SIZE, | ||||
|                                  remaining_bytes); | ||||
|         ret = bdrv_co_preadv(bs->file, sector_num * BDRV_SECTOR_SIZE, | ||||
|                              remaining_bytes, qiov, 0); | ||||
|         ret = bdrv_co_readv(bs->file, sector_num, remaining_sectors, | ||||
|                             qiov); | ||||
|         backup_cow_request_end(&req); | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_co_preadv(bs->file, sector_num * BDRV_SECTOR_SIZE, | ||||
|                          remaining_sectors * BDRV_SECTOR_SIZE, qiov, 0); | ||||
|     ret = bdrv_co_readv(bs->file, sector_num, remaining_sectors, qiov); | ||||
| out: | ||||
|     return replication_return_value(s, ret); | ||||
| } | ||||
| @@ -261,8 +260,7 @@ out: | ||||
| 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; | ||||
| @@ -273,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); | ||||
|     } | ||||
|  | ||||
| @@ -307,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; | ||||
|         } | ||||
| @@ -570,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); | ||||
| @@ -578,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); | ||||
| @@ -683,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) { | ||||
| @@ -695,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: | ||||
|   | ||||
| @@ -24,7 +24,6 @@ | ||||
| #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" | ||||
| @@ -539,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); | ||||
| @@ -558,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; | ||||
| } | ||||
|  | ||||
| @@ -1026,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; | ||||
|     } | ||||
|  | ||||
| @@ -1849,7 +1859,9 @@ out: | ||||
|         error_setg_errno(errp, -ret, "Can't pre-allocate"); | ||||
|     } | ||||
| out_with_err_set: | ||||
|     if (blk) { | ||||
|         blk_unref(blk); | ||||
|     } | ||||
|     g_free(buf); | ||||
|  | ||||
|     return ret; | ||||
| @@ -1871,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"); | ||||
| @@ -1889,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; | ||||
| } | ||||
|  | ||||
| @@ -1975,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; | ||||
|         } | ||||
|  | ||||
| @@ -2170,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; | ||||
|  | ||||
| @@ -2205,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); | ||||
| @@ -2238,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; | ||||
| } | ||||
|  | ||||
| @@ -2292,7 +2305,7 @@ static int64_t sd_getlength(BlockDriverState *bs) | ||||
|     return s->inode.vdi_size; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn sd_co_truncate(BlockDriverState *bs, int64_t offset, | ||||
| static int sd_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                        PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     BDRVSheepdogState *s = bs->opaque; | ||||
| @@ -2322,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, | ||||
| @@ -2599,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; | ||||
|         } | ||||
| @@ -2690,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. */ | ||||
| @@ -2925,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) { | ||||
| @@ -2975,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++; | ||||
|         } | ||||
|     } | ||||
| @@ -3004,7 +3014,6 @@ out: | ||||
|     *psn_tab = sn_tab; | ||||
|  | ||||
|     g_free(vdi_inuse); | ||||
|     g_free(inode); | ||||
|  | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
| @@ -3231,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, | ||||
| @@ -3268,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, | ||||
| @@ -3305,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,7 +214,7 @@ 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)); | ||||
|  | ||||
|         drv->bdrv_close(bs); | ||||
| @@ -224,7 +223,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs, | ||||
|  | ||||
|         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; | ||||
|   | ||||
							
								
								
									
										26
									
								
								block/ssh.c
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								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" | ||||
| @@ -606,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; | ||||
|  | ||||
| @@ -622,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); | ||||
| @@ -907,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; | ||||
| } | ||||
| @@ -1154,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); | ||||
| @@ -1243,7 +1251,7 @@ static int64_t ssh_getlength(BlockDriverState *bs) | ||||
|     return length; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset, | ||||
| static int ssh_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                         PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     BDRVSSHState *s = bs->opaque; | ||||
| @@ -1279,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, | ||||
| }; | ||||
|   | ||||
| @@ -29,8 +29,11 @@ enum { | ||||
|     STREAM_BUFFER_SIZE = 512 * 1024, /* in bytes */ | ||||
| }; | ||||
|  | ||||
| #define SLICE_TIME 100000000ULL /* ns */ | ||||
|  | ||||
| typedef struct StreamBlockJob { | ||||
|     BlockJob common; | ||||
|     RateLimit limit; | ||||
|     BlockDriverState *base; | ||||
|     BlockdevOnError on_error; | ||||
|     char *backing_file_str; | ||||
| @@ -58,16 +61,16 @@ typedef struct { | ||||
|     int ret; | ||||
| } StreamCompleteData; | ||||
|  | ||||
| static void stream_complete(Job *job, void *opaque) | ||||
| static void stream_complete(BlockJob *job, void *opaque) | ||||
| { | ||||
|     StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); | ||||
|     BlockJob *bjob = &s->common; | ||||
|     StreamBlockJob *s = container_of(job, StreamBlockJob, common); | ||||
|     StreamCompleteData *data = opaque; | ||||
|     BlockDriverState *bs = blk_bs(bjob->blk); | ||||
|     BlockDriverState *bs = blk_bs(job->blk); | ||||
|     BlockDriverState *base = s->base; | ||||
|     Error *local_err = NULL; | ||||
|  | ||||
|     if (!job_is_cancelled(job) && bs->backing && data->ret == 0) { | ||||
|     if (!block_job_is_cancelled(&s->common) && bs->backing && | ||||
|         data->ret == 0) { | ||||
|         const char *base_id = NULL, *base_fmt = NULL; | ||||
|         if (base) { | ||||
|             base_id = s->backing_file_str; | ||||
| @@ -88,12 +91,12 @@ out: | ||||
|     /* Reopen the image back in read-only mode if necessary */ | ||||
|     if (s->bs_flags != bdrv_get_flags(bs)) { | ||||
|         /* Give up write permissions before making it read-only */ | ||||
|         blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort); | ||||
|         blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort); | ||||
|         bdrv_reopen(bs, s->bs_flags, NULL); | ||||
|     } | ||||
|  | ||||
|     g_free(s->backing_file_str); | ||||
|     job_completed(job, data->ret, NULL); | ||||
|     block_job_completed(&s->common, data->ret); | ||||
|     g_free(data); | ||||
| } | ||||
|  | ||||
| @@ -104,7 +107,6 @@ static void coroutine_fn stream_run(void *opaque) | ||||
|     BlockBackend *blk = s->common.blk; | ||||
|     BlockDriverState *bs = blk_bs(blk); | ||||
|     BlockDriverState *base = s->base; | ||||
|     int64_t len; | ||||
|     int64_t offset = 0; | ||||
|     uint64_t delay_ns = 0; | ||||
|     int error = 0; | ||||
| @@ -116,12 +118,11 @@ static void coroutine_fn stream_run(void *opaque) | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     len = bdrv_getlength(bs); | ||||
|     if (len < 0) { | ||||
|         ret = len; | ||||
|     s->common.len = bdrv_getlength(bs); | ||||
|     if (s->common.len < 0) { | ||||
|         ret = s->common.len; | ||||
|         goto out; | ||||
|     } | ||||
|     job_progress_set_remaining(&s->common.job, len); | ||||
|  | ||||
|     buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE); | ||||
|  | ||||
| @@ -134,14 +135,14 @@ static void coroutine_fn stream_run(void *opaque) | ||||
|         bdrv_enable_copy_on_read(bs); | ||||
|     } | ||||
|  | ||||
|     for ( ; offset < len; offset += n) { | ||||
|     for ( ; 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; | ||||
|         } | ||||
|  | ||||
| @@ -158,7 +159,7 @@ static void coroutine_fn stream_run(void *opaque) | ||||
|  | ||||
|             /* Finish early if end of backing file has been reached */ | ||||
|             if (ret == 0 && n == 0) { | ||||
|                 n = len - offset; | ||||
|                 n = s->common.len - offset; | ||||
|             } | ||||
|  | ||||
|             copy = (ret == 1); | ||||
| @@ -184,11 +185,9 @@ static void coroutine_fn stream_run(void *opaque) | ||||
|         ret = 0; | ||||
|  | ||||
|         /* Publish progress */ | ||||
|         job_progress_update(&s->common.job, n); | ||||
|         if (copy) { | ||||
|             delay_ns = block_job_ratelimit_get_delay(&s->common, n); | ||||
|         } else { | ||||
|             delay_ns = 0; | ||||
|         s->common.offset += n; | ||||
|         if (copy && s->common.speed) { | ||||
|             delay_ns = ratelimit_calculate_delay(&s->limit, n); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -205,18 +204,25 @@ out: | ||||
|     /* Modify backing chain and close BDSes in main loop */ | ||||
|     data = g_malloc(sizeof(*data)); | ||||
|     data->ret = ret; | ||||
|     job_defer_to_main_loop(&s->common.job, stream_complete, data); | ||||
|     block_job_defer_to_main_loop(&s->common, stream_complete, data); | ||||
| } | ||||
|  | ||||
| static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp) | ||||
| { | ||||
|     StreamBlockJob *s = container_of(job, StreamBlockJob, common); | ||||
|  | ||||
|     if (speed < 0) { | ||||
|         error_setg(errp, QERR_INVALID_PARAMETER, "speed"); | ||||
|         return; | ||||
|     } | ||||
|     ratelimit_set_speed(&s->limit, speed, SLICE_TIME); | ||||
| } | ||||
|  | ||||
| static const BlockJobDriver stream_job_driver = { | ||||
|     .job_driver = { | ||||
|     .instance_size = sizeof(StreamBlockJob), | ||||
|         .job_type      = JOB_TYPE_STREAM, | ||||
|         .free          = block_job_free, | ||||
|     .job_type      = BLOCK_JOB_TYPE_STREAM, | ||||
|     .set_speed     = stream_set_speed, | ||||
|     .start         = stream_run, | ||||
|         .user_resume   = block_job_user_resume, | ||||
|         .drain         = block_job_drain, | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| void stream_start(const char *job_id, BlockDriverState *bs, | ||||
| @@ -238,12 +244,12 @@ void stream_start(const char *job_id, BlockDriverState *bs, | ||||
|     /* Prevent concurrent jobs trying to modify the graph structure here, we | ||||
|      * already have our own plans. Also don't allow resize as the image size is | ||||
|      * queried only at the job start and then cached. */ | ||||
|     s = block_job_create(job_id, &stream_job_driver, NULL, bs, | ||||
|     s = block_job_create(job_id, &stream_job_driver, bs, | ||||
|                          BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED | | ||||
|                          BLK_PERM_GRAPH_MOD, | ||||
|                          BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED | | ||||
|                          BLK_PERM_WRITE, | ||||
|                          speed, JOB_DEFAULT, NULL, NULL, errp); | ||||
|                          speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp); | ||||
|     if (!s) { | ||||
|         goto fail; | ||||
|     } | ||||
| @@ -264,7 +270,7 @@ void stream_start(const char *job_id, BlockDriverState *bs, | ||||
|  | ||||
|     s->on_error = on_error; | ||||
|     trace_stream_start(bs, base, s); | ||||
|     job_start(&s->common.job); | ||||
|     block_job_start(&s->common); | ||||
|     return; | ||||
|  | ||||
| fail: | ||||
|   | ||||
| @@ -36,12 +36,9 @@ static QemuOptsList throttle_opts = { | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * If this function succeeds then the throttle group name is stored in | ||||
|  * @group and must be freed by the caller. | ||||
|  * If there's an error then @group remains unmodified. | ||||
|  */ | ||||
| static int throttle_parse_options(QDict *options, char **group, Error **errp) | ||||
| static int throttle_configure_tgm(BlockDriverState *bs, | ||||
|                                   ThrottleGroupMember *tgm, | ||||
|                                   QDict *options, Error **errp) | ||||
| { | ||||
|     int ret; | ||||
|     const char *group_name; | ||||
| @@ -66,7 +63,8 @@ static int throttle_parse_options(QDict *options, char **group, Error **errp) | ||||
|         goto fin; | ||||
|     } | ||||
|  | ||||
|     *group = g_strdup(group_name); | ||||
|     /* Register membership to group with name group_name */ | ||||
|     throttle_group_register_tgm(tgm, group_name, bdrv_get_aio_context(bs)); | ||||
|     ret = 0; | ||||
| fin: | ||||
|     qemu_opts_del(opts); | ||||
| @@ -77,27 +75,16 @@ static int throttle_open(BlockDriverState *bs, QDict *options, | ||||
|                          int flags, Error **errp) | ||||
| { | ||||
|     ThrottleGroupMember *tgm = bs->opaque; | ||||
|     char *group; | ||||
|     int ret; | ||||
|  | ||||
|     bs->file = bdrv_open_child(NULL, options, "file", bs, | ||||
|                                &child_file, false, errp); | ||||
|     if (!bs->file) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|     bs->supported_write_flags = bs->file->bs->supported_write_flags | | ||||
|                                 BDRV_REQ_WRITE_UNCHANGED; | ||||
|     bs->supported_zero_flags = bs->file->bs->supported_zero_flags | | ||||
|                                BDRV_REQ_WRITE_UNCHANGED; | ||||
|     bs->supported_write_flags = bs->file->bs->supported_write_flags; | ||||
|     bs->supported_zero_flags = bs->file->bs->supported_zero_flags; | ||||
|  | ||||
|     ret = throttle_parse_options(options, &group, errp); | ||||
|     if (ret == 0) { | ||||
|         /* Register membership to group with name group_name */ | ||||
|         throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs)); | ||||
|         g_free(group); | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
|     return throttle_configure_tgm(bs, tgm, options, errp); | ||||
| } | ||||
|  | ||||
| static void throttle_close(BlockDriverState *bs) | ||||
| @@ -149,7 +136,7 @@ static int coroutine_fn throttle_co_pdiscard(BlockDriverState *bs, | ||||
|     ThrottleGroupMember *tgm = bs->opaque; | ||||
|     throttle_group_co_io_limits_intercept(tgm, bytes, true); | ||||
|  | ||||
|     return bdrv_co_pdiscard(bs->file, offset, bytes); | ||||
|     return bdrv_co_pdiscard(bs->file->bs, offset, bytes); | ||||
| } | ||||
|  | ||||
| static int throttle_co_flush(BlockDriverState *bs) | ||||
| @@ -173,36 +160,35 @@ static void throttle_attach_aio_context(BlockDriverState *bs, | ||||
| static int throttle_reopen_prepare(BDRVReopenState *reopen_state, | ||||
|                                    BlockReopenQueue *queue, Error **errp) | ||||
| { | ||||
|     int ret; | ||||
|     char *group = NULL; | ||||
|     ThrottleGroupMember *tgm; | ||||
|  | ||||
|     assert(reopen_state != NULL); | ||||
|     assert(reopen_state->bs != NULL); | ||||
|  | ||||
|     ret = throttle_parse_options(reopen_state->options, &group, errp); | ||||
|     reopen_state->opaque = group; | ||||
|     return ret; | ||||
|     reopen_state->opaque = g_new0(ThrottleGroupMember, 1); | ||||
|     tgm = reopen_state->opaque; | ||||
|  | ||||
|     return throttle_configure_tgm(reopen_state->bs, tgm, reopen_state->options, | ||||
|             errp); | ||||
| } | ||||
|  | ||||
| static void throttle_reopen_commit(BDRVReopenState *reopen_state) | ||||
| { | ||||
|     BlockDriverState *bs = reopen_state->bs; | ||||
|     ThrottleGroupMember *tgm = bs->opaque; | ||||
|     char *group = reopen_state->opaque; | ||||
|     ThrottleGroupMember *old_tgm = reopen_state->bs->opaque; | ||||
|     ThrottleGroupMember *new_tgm = reopen_state->opaque; | ||||
|  | ||||
|     assert(group); | ||||
|  | ||||
|     if (strcmp(group, throttle_group_get_name(tgm))) { | ||||
|         throttle_group_unregister_tgm(tgm); | ||||
|         throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs)); | ||||
|     } | ||||
|     g_free(reopen_state->opaque); | ||||
|     throttle_group_unregister_tgm(old_tgm); | ||||
|     g_free(old_tgm); | ||||
|     reopen_state->bs->opaque = new_tgm; | ||||
|     reopen_state->opaque = NULL; | ||||
| } | ||||
|  | ||||
| static void throttle_reopen_abort(BDRVReopenState *reopen_state) | ||||
| { | ||||
|     g_free(reopen_state->opaque); | ||||
|     ThrottleGroupMember *tgm = reopen_state->opaque; | ||||
|  | ||||
|     throttle_group_unregister_tgm(tgm); | ||||
|     g_free(tgm); | ||||
|     reopen_state->opaque = NULL; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -7,16 +7,12 @@ bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d" | ||||
| # block/block-backend.c | ||||
| blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" | ||||
| blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" | ||||
| blk_root_attach(void *child, void *blk, void *bs) "child %p blk %p bs %p" | ||||
| blk_root_detach(void *child, void *blk, void *bs) "child %p blk %p bs %p" | ||||
|  | ||||
| # block/io.c | ||||
| bdrv_co_preadv(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x" | ||||
| bdrv_co_pwritev(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x" | ||||
| bdrv_co_pwrite_zeroes(void *bs, int64_t offset, int count, int flags) "bs %p offset %"PRId64" count %d flags 0x%x" | ||||
| bdrv_co_do_copy_on_readv(void *bs, int64_t offset, unsigned int bytes, int64_t cluster_offset, int64_t cluster_bytes) "bs %p offset %"PRId64" bytes %u cluster_offset %"PRId64" cluster_bytes %"PRId64 | ||||
| bdrv_co_copy_range_from(void *src, uint64_t src_offset, void *dst, uint64_t dst_offset, uint64_t bytes, int read_flags, int write_flags) "src %p offset %"PRIu64" dst %p offset %"PRIu64" bytes %"PRIu64" rw flags 0x%x 0x%x" | ||||
| bdrv_co_copy_range_to(void *src, uint64_t src_offset, void *dst, uint64_t dst_offset, uint64_t bytes, int read_flags, int write_flags) "src %p offset %"PRIu64" dst %p offset %"PRIu64" bytes %"PRIu64" rw flags 0x%x 0x%x" | ||||
|  | ||||
| # block/stream.c | ||||
| stream_one_iteration(void *s, int64_t offset, uint64_t bytes, int is_allocated) "s %p offset %" PRId64 " bytes %" PRIu64 " is_allocated %d" | ||||
| @@ -44,22 +40,18 @@ backup_do_cow_skip(void *job, int64_t start) "job %p start %"PRId64 | ||||
| backup_do_cow_process(void *job, int64_t start) "job %p start %"PRId64 | ||||
| backup_do_cow_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d" | ||||
| backup_do_cow_write_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d" | ||||
| backup_do_cow_copy_range_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d" | ||||
|  | ||||
| # blockdev.c | ||||
| qmp_block_job_cancel(void *job) "job %p" | ||||
| qmp_block_job_pause(void *job) "job %p" | ||||
| qmp_block_job_resume(void *job) "job %p" | ||||
| qmp_block_job_complete(void *job) "job %p" | ||||
| qmp_block_job_finalize(void *job) "job %p" | ||||
| qmp_block_job_dismiss(void *job) "job %p" | ||||
| qmp_block_stream(void *bs, void *job) "bs %p job %p" | ||||
|  | ||||
| # block/file-win32.c | ||||
| # block/file-posix.c | ||||
| file_paio_submit_co(int64_t offset, int count, int type) "offset %"PRId64" count %d type %d" | ||||
| file_paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d" | ||||
| file_copy_file_range(void *bs, int src, int64_t src_off, int dst, int64_t dst_off, int64_t bytes, int flags, int64_t ret) "bs %p src_fd %d offset %"PRIu64" dst_fd %d offset %"PRIu64" bytes %"PRIu64" flags %d ret %"PRId64 | ||||
| paio_submit_co(int64_t offset, int count, int type) "offset %"PRId64" count %d type %d" | ||||
| paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d" | ||||
|  | ||||
| # block/qcow2.c | ||||
| qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d" | ||||
| @@ -153,6 +145,3 @@ nvme_free_req_queue_wait(void *q) "q %p" | ||||
| nvme_cmd_map_qiov(void *s, void *cmd, void *req, void *qiov, int entries) "s %p cmd %p req %p qiov %p entries %d" | ||||
| nvme_cmd_map_qiov_pages(void *s, int i, uint64_t page) "s %p page[%d] 0x%"PRIx64 | ||||
| nvme_cmd_map_qiov_iov(void *s, int i, void *page, int pages) "s %p iov[%d] %p pages %d" | ||||
|  | ||||
| # block/iscsi.c | ||||
| iscsi_xcopy(void *src_lun, uint64_t src_off, void *dst_lun, uint64_t dst_off, uint64_t bytes, int ret) "src_lun %p offset %"PRIu64" dst_lun %p offset %"PRIu64" bytes %"PRIu64" ret %d" | ||||
|   | ||||
							
								
								
									
										199
									
								
								block/vdi.c
									
									
									
									
									
								
							
							
						
						
									
										199
									
								
								block/vdi.c
									
									
									
									
									
								
							| @@ -50,12 +50,8 @@ | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu/units.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qapi/qobject-input-visitor.h" | ||||
| #include "qapi/qapi-visit-block-core.h" | ||||
| #include "block/block_int.h" | ||||
| #include "block/qdict.h" | ||||
| #include "sysemu/block-backend.h" | ||||
| #include "qemu/module.h" | ||||
| #include "qemu/option.h" | ||||
| @@ -84,6 +80,9 @@ | ||||
| /* Command line option for static images. */ | ||||
| #define BLOCK_OPT_STATIC "static" | ||||
|  | ||||
| #define KiB     1024 | ||||
| #define MiB     (KiB * KiB) | ||||
|  | ||||
| #define SECTOR_SIZE 512 | ||||
| #define DEFAULT_CLUSTER_SIZE (1 * MiB) | ||||
|  | ||||
| @@ -141,8 +140,6 @@ | ||||
| #define VDI_DISK_SIZE_MAX        ((uint64_t)VDI_BLOCKS_IN_IMAGE_MAX * \ | ||||
|                                   (uint64_t)DEFAULT_CLUSTER_SIZE) | ||||
|  | ||||
| static QemuOptsList vdi_create_opts; | ||||
|  | ||||
| typedef struct { | ||||
|     char text[0x40]; | ||||
|     uint32_t signature; | ||||
| @@ -233,6 +230,7 @@ static void vdi_header_to_le(VdiHeader *header) | ||||
|     qemu_uuid_bswap(&header->uuid_parent); | ||||
| } | ||||
|  | ||||
| #if defined(CONFIG_VDI_DEBUG) | ||||
| static void vdi_header_print(VdiHeader *header) | ||||
| { | ||||
|     char uuid[37]; | ||||
| @@ -254,15 +252,16 @@ static void vdi_header_print(VdiHeader *header) | ||||
|     logout("block extra 0x%04x\n", header->block_extra); | ||||
|     logout("blocks tot. 0x%04x\n", header->blocks_in_image); | ||||
|     logout("blocks all. 0x%04x\n", header->blocks_allocated); | ||||
|     qemu_uuid_unparse(&header->uuid_image, uuid); | ||||
|     uuid_unparse(header->uuid_image, uuid); | ||||
|     logout("uuid image  %s\n", uuid); | ||||
|     qemu_uuid_unparse(&header->uuid_last_snap, uuid); | ||||
|     uuid_unparse(header->uuid_last_snap, uuid); | ||||
|     logout("uuid snap   %s\n", uuid); | ||||
|     qemu_uuid_unparse(&header->uuid_link, uuid); | ||||
|     uuid_unparse(header->uuid_link, uuid); | ||||
|     logout("uuid link   %s\n", uuid); | ||||
|     qemu_uuid_unparse(&header->uuid_parent, uuid); | ||||
|     uuid_unparse(header->uuid_parent, uuid); | ||||
|     logout("uuid parent %s\n", uuid); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static int coroutine_fn vdi_co_check(BlockDriverState *bs, BdrvCheckResult *res, | ||||
|                                      BdrvCheckMode fix) | ||||
| @@ -383,9 +382,9 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     } | ||||
|  | ||||
|     vdi_header_to_cpu(&header); | ||||
|     if (VDI_DEBUG) { | ||||
| #if defined(CONFIG_VDI_DEBUG) | ||||
|     vdi_header_print(&header); | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     if (header.disk_size > VDI_DISK_SIZE_MAX) { | ||||
|         error_setg(errp, "Unsupported VDI image size (size is 0x%" PRIx64 | ||||
| @@ -432,8 +431,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         goto fail; | ||||
|     } else if (header.block_size != DEFAULT_CLUSTER_SIZE) { | ||||
|         error_setg(errp, "unsupported VDI image (block size %" PRIu32 | ||||
|                          " is not %" PRIu64 ")", | ||||
|                    header.block_size, DEFAULT_CLUSTER_SIZE); | ||||
|                    " is not %u)", header.block_size, DEFAULT_CLUSTER_SIZE); | ||||
|         ret = -ENOTSUP; | ||||
|         goto fail; | ||||
|     } else if (header.disk_size > | ||||
| @@ -718,59 +716,36 @@ nonallocating_write: | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, | ||||
|                                          size_t block_size, Error **errp) | ||||
| static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|                                            Error **errp) | ||||
| { | ||||
|     BlockdevCreateOptionsVdi *vdi_opts; | ||||
|     int ret = 0; | ||||
|     uint64_t bytes = 0; | ||||
|     uint32_t blocks; | ||||
|     uint32_t image_type; | ||||
|     size_t block_size = DEFAULT_CLUSTER_SIZE; | ||||
|     uint32_t image_type = VDI_TYPE_DYNAMIC; | ||||
|     VdiHeader header; | ||||
|     size_t i; | ||||
|     size_t bmap_size; | ||||
|     int64_t offset = 0; | ||||
|     BlockDriverState *bs_file = NULL; | ||||
|     Error *local_err = NULL; | ||||
|     BlockBackend *blk = NULL; | ||||
|     uint32_t *bmap = NULL; | ||||
|  | ||||
|     assert(create_options->driver == BLOCKDEV_DRIVER_VDI); | ||||
|     vdi_opts = &create_options->u.vdi; | ||||
|  | ||||
|     logout("\n"); | ||||
|  | ||||
|     /* Validate options and set default values */ | ||||
|     bytes = vdi_opts->size; | ||||
|  | ||||
|     if (!vdi_opts->has_preallocation) { | ||||
|         vdi_opts->preallocation = PREALLOC_MODE_OFF; | ||||
|     } | ||||
|     switch (vdi_opts->preallocation) { | ||||
|     case PREALLOC_MODE_OFF: | ||||
|         image_type = VDI_TYPE_DYNAMIC; | ||||
|         break; | ||||
|     case PREALLOC_MODE_METADATA: | ||||
|         image_type = VDI_TYPE_STATIC; | ||||
|         break; | ||||
|     default: | ||||
|         error_setg(errp, "Preallocation mode not supported for vdi"); | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
| #ifndef CONFIG_VDI_STATIC_IMAGE | ||||
|     if (image_type == VDI_TYPE_STATIC) { | ||||
|         ret = -ENOTSUP; | ||||
|         error_setg(errp, "Statically allocated images cannot be created in " | ||||
|                    "this build"); | ||||
|         goto exit; | ||||
|     } | ||||
|     /* Read out options. */ | ||||
|     bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), | ||||
|                      BDRV_SECTOR_SIZE); | ||||
| #if defined(CONFIG_VDI_BLOCK_SIZE) | ||||
|     /* TODO: Additional checks (SECTOR_SIZE * 2^n, ...). */ | ||||
|     block_size = qemu_opt_get_size_del(opts, | ||||
|                                        BLOCK_OPT_CLUSTER_SIZE, | ||||
|                                        DEFAULT_CLUSTER_SIZE); | ||||
| #endif | ||||
| #ifndef CONFIG_VDI_BLOCK_SIZE | ||||
|     if (block_size != DEFAULT_CLUSTER_SIZE) { | ||||
|         ret = -ENOTSUP; | ||||
|         error_setg(errp, | ||||
|                    "A non-default cluster size is not supported in this build"); | ||||
|         goto exit; | ||||
| #if defined(CONFIG_VDI_STATIC_IMAGE) | ||||
|     if (qemu_opt_get_bool_del(opts, BLOCK_OPT_STATIC, false)) { | ||||
|         image_type = VDI_TYPE_STATIC; | ||||
|     } | ||||
| #endif | ||||
|  | ||||
| @@ -782,16 +757,18 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, | ||||
|         goto exit; | ||||
|     } | ||||
|  | ||||
|     /* Create BlockBackend to write to the image */ | ||||
|     bs_file = bdrv_open_blockdev_ref(vdi_opts->file, errp); | ||||
|     if (!bs_file) { | ||||
|         ret = -EIO; | ||||
|     ret = bdrv_create_file(filename, opts, &local_err); | ||||
|     if (ret < 0) { | ||||
|         error_propagate(errp, local_err); | ||||
|         goto exit; | ||||
|     } | ||||
|  | ||||
|     blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); | ||||
|     ret = blk_insert_bs(blk, bs_file, errp); | ||||
|     if (ret < 0) { | ||||
|     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); | ||||
|         ret = -EIO; | ||||
|         goto exit; | ||||
|     } | ||||
|  | ||||
| @@ -822,13 +799,13 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, | ||||
|     qemu_uuid_generate(&header.uuid_image); | ||||
|     qemu_uuid_generate(&header.uuid_last_snap); | ||||
|     /* There is no need to set header.uuid_link or header.uuid_parent here. */ | ||||
|     if (VDI_DEBUG) { | ||||
| #if defined(CONFIG_VDI_DEBUG) | ||||
|     vdi_header_print(&header); | ||||
|     } | ||||
| #endif | ||||
|     vdi_header_to_le(&header); | ||||
|     ret = blk_pwrite(blk, offset, &header, sizeof(header), 0); | ||||
|     if (ret < 0) { | ||||
|         error_setg(errp, "Error writing header"); | ||||
|         error_setg(errp, "Error writing header to %s", filename); | ||||
|         goto exit; | ||||
|     } | ||||
|     offset += sizeof(header); | ||||
| @@ -849,7 +826,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, | ||||
|         } | ||||
|         ret = blk_pwrite(blk, offset, bmap, bmap_size, 0); | ||||
|         if (ret < 0) { | ||||
|             error_setg(errp, "Error writing bmap"); | ||||
|             error_setg(errp, "Error writing bmap to %s", filename); | ||||
|             goto exit; | ||||
|         } | ||||
|         offset += bmap_size; | ||||
| @@ -859,108 +836,17 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, | ||||
|         ret = blk_truncate(blk, offset + blocks * block_size, | ||||
|                            PREALLOC_MODE_OFF, errp); | ||||
|         if (ret < 0) { | ||||
|             error_prepend(errp, "Failed to statically allocate file"); | ||||
|             error_prepend(errp, "Failed to statically allocate %s", filename); | ||||
|             goto exit; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     ret = 0; | ||||
| exit: | ||||
|     blk_unref(blk); | ||||
|     bdrv_unref(bs_file); | ||||
|     g_free(bmap); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn vdi_co_create(BlockdevCreateOptions *create_options, | ||||
|                                       Error **errp) | ||||
| { | ||||
|     return vdi_co_do_create(create_options, DEFAULT_CLUSTER_SIZE, errp); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|                                            Error **errp) | ||||
| { | ||||
|     QDict *qdict = NULL; | ||||
|     BlockdevCreateOptions *create_options = NULL; | ||||
|     BlockDriverState *bs_file = NULL; | ||||
|     uint64_t block_size = DEFAULT_CLUSTER_SIZE; | ||||
|     bool is_static = false; | ||||
|     Visitor *v; | ||||
|     Error *local_err = NULL; | ||||
|     int ret; | ||||
|  | ||||
|     /* Parse options and convert legacy syntax. | ||||
|      * | ||||
|      * Since CONFIG_VDI_BLOCK_SIZE is disabled by default, | ||||
|      * cluster-size is not part of the QAPI schema; therefore we have | ||||
|      * to parse it before creating the QAPI object. */ | ||||
| #if defined(CONFIG_VDI_BLOCK_SIZE) | ||||
|     block_size = qemu_opt_get_size_del(opts, | ||||
|                                        BLOCK_OPT_CLUSTER_SIZE, | ||||
|                                        DEFAULT_CLUSTER_SIZE); | ||||
|     if (block_size < BDRV_SECTOR_SIZE || block_size > UINT32_MAX || | ||||
|         !is_power_of_2(block_size)) | ||||
|     { | ||||
|         error_setg(errp, "Invalid cluster size"); | ||||
|         ret = -EINVAL; | ||||
|         goto done; | ||||
|     } | ||||
| #endif | ||||
|     if (qemu_opt_get_bool_del(opts, BLOCK_OPT_STATIC, false)) { | ||||
|         is_static = true; | ||||
|     } | ||||
|  | ||||
|     qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vdi_create_opts, true); | ||||
|  | ||||
|     /* Create and open the file (protocol layer) */ | ||||
|     ret = bdrv_create_file(filename, opts, errp); | ||||
|     if (ret < 0) { | ||||
|         goto done; | ||||
|     } | ||||
|  | ||||
|     bs_file = bdrv_open(filename, NULL, NULL, | ||||
|                         BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); | ||||
|     if (!bs_file) { | ||||
|         ret = -EIO; | ||||
|         goto done; | ||||
|     } | ||||
|  | ||||
|     qdict_put_str(qdict, "driver", "vdi"); | ||||
|     qdict_put_str(qdict, "file", bs_file->node_name); | ||||
|     if (is_static) { | ||||
|         qdict_put_str(qdict, "preallocation", "metadata"); | ||||
|     } | ||||
|  | ||||
|     /* Get the QAPI object */ | ||||
|     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 size */ | ||||
|     assert(create_options->driver == BLOCKDEV_DRIVER_VDI); | ||||
|     create_options->u.vdi.size = ROUND_UP(create_options->u.vdi.size, | ||||
|                                           BDRV_SECTOR_SIZE); | ||||
|  | ||||
|     /* Create the vdi image (format layer) */ | ||||
|     ret = vdi_co_do_create(create_options, block_size, errp); | ||||
| done: | ||||
|     qobject_unref(qdict); | ||||
|     qapi_free_BlockdevCreateOptions(create_options); | ||||
|     bdrv_unref(bs_file); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static void vdi_close(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVVdiState *s = bs->opaque; | ||||
| @@ -1009,7 +895,6 @@ static BlockDriver bdrv_vdi = { | ||||
|     .bdrv_close = vdi_close, | ||||
|     .bdrv_reopen_prepare = vdi_reopen_prepare, | ||||
|     .bdrv_child_perm          = bdrv_format_default_perms, | ||||
|     .bdrv_co_create      = vdi_co_create, | ||||
|     .bdrv_co_create_opts = vdi_co_create_opts, | ||||
|     .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||||
|     .bdrv_co_block_status = vdi_co_block_status, | ||||
|   | ||||
| @@ -19,7 +19,7 @@ | ||||
| #include "qemu-common.h" | ||||
| #include "block/block_int.h" | ||||
| #include "qemu/bswap.h" | ||||
| #include "vhdx.h" | ||||
| #include "block/vhdx.h" | ||||
|  | ||||
| /* | ||||
|  * All the VHDX formats on disk are little endian - the following | ||||
|   | ||||
| @@ -24,7 +24,7 @@ | ||||
| #include "qemu/error-report.h" | ||||
| #include "qemu/module.h" | ||||
| #include "qemu/bswap.h" | ||||
| #include "vhdx.h" | ||||
| #include "block/vhdx.h" | ||||
|  | ||||
|  | ||||
| typedef struct VHDXLogSequence { | ||||
|   | ||||
							
								
								
									
										240
									
								
								block/vhdx.c
									
									
									
									
									
								
							
							
						
						
									
										240
									
								
								block/vhdx.c
									
									
									
									
									
								
							| @@ -18,18 +18,14 @@ | ||||
| #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 "qemu/crc32c.h" | ||||
| #include "qemu/bswap.h" | ||||
| #include "vhdx.h" | ||||
| #include "block/vhdx.h" | ||||
| #include "migration/blocker.h" | ||||
| #include "qemu/uuid.h" | ||||
| #include "qapi/qmp/qdict.h" | ||||
| #include "qapi/qobject-input-visitor.h" | ||||
| #include "qapi/qapi-visit-block-core.h" | ||||
|  | ||||
| /* Options for VHDX creation */ | ||||
|  | ||||
| @@ -43,8 +39,6 @@ typedef enum VHDXImageType { | ||||
|     VHDX_TYPE_DIFFERENCING,   /* Currently unsupported */ | ||||
| } VHDXImageType; | ||||
|  | ||||
| static QemuOptsList vhdx_create_opts; | ||||
|  | ||||
| /* Several metadata and region table data entries are identified by | ||||
|  * guids in  a MS-specific GUID format. */ | ||||
|  | ||||
| @@ -1127,9 +1121,9 @@ static coroutine_fn int vhdx_co_readv(BlockDriverState *bs, int64_t sector_num, | ||||
|                 break; | ||||
|             case PAYLOAD_BLOCK_FULLY_PRESENT: | ||||
|                 qemu_co_mutex_unlock(&s->lock); | ||||
|                 ret = bdrv_co_preadv(bs->file, sinfo.file_offset, | ||||
|                                      sinfo.sectors_avail * BDRV_SECTOR_SIZE, | ||||
|                                      &hd_qiov, 0); | ||||
|                 ret = bdrv_co_readv(bs->file, | ||||
|                                     sinfo.file_offset >> BDRV_SECTOR_BITS, | ||||
|                                     sinfo.sectors_avail, &hd_qiov); | ||||
|                 qemu_co_mutex_lock(&s->lock); | ||||
|                 if (ret < 0) { | ||||
|                     goto exit; | ||||
| @@ -1227,8 +1221,7 @@ int vhdx_user_visible_write(BlockDriverState *bs, BDRVVHDXState *s) | ||||
| } | ||||
|  | ||||
| static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, | ||||
|                                        int nb_sectors, QEMUIOVector *qiov, | ||||
|                                        int flags) | ||||
|                                       int nb_sectors, QEMUIOVector *qiov) | ||||
| { | ||||
|     int ret = -ENOTSUP; | ||||
|     BDRVVHDXState *s = bs->opaque; | ||||
| @@ -1244,7 +1237,6 @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, | ||||
|     uint64_t bat_prior_offset = 0; | ||||
|     bool bat_update = false; | ||||
|  | ||||
|     assert(!flags); | ||||
|     qemu_iovec_init(&hd_qiov, qiov->niov); | ||||
|  | ||||
|     qemu_co_mutex_lock(&s->lock); | ||||
| @@ -1349,9 +1341,9 @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, | ||||
|                 } | ||||
|                 /* block exists, so we can just overwrite it */ | ||||
|                 qemu_co_mutex_unlock(&s->lock); | ||||
|                 ret = bdrv_co_pwritev(bs->file, sinfo.file_offset, | ||||
|                                       sectors_to_write * BDRV_SECTOR_SIZE, | ||||
|                                       &hd_qiov, 0); | ||||
|                 ret = bdrv_co_writev(bs->file, | ||||
|                                     sinfo.file_offset >> BDRV_SECTOR_BITS, | ||||
|                                     sectors_to_write, &hd_qiov); | ||||
|                 qemu_co_mutex_lock(&s->lock); | ||||
|                 if (ret < 0) { | ||||
|                     goto error_bat_restore; | ||||
| @@ -1800,75 +1792,59 @@ exit: | ||||
|  *    .---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------. | ||||
|  *   1MB | ||||
|  */ | ||||
| static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, | ||||
| static int coroutine_fn vhdx_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|                                             Error **errp) | ||||
| { | ||||
|     BlockdevCreateOptionsVhdx *vhdx_opts; | ||||
|     BlockBackend *blk = NULL; | ||||
|     BlockDriverState *bs = NULL; | ||||
|  | ||||
|     int ret = 0; | ||||
|     uint64_t image_size; | ||||
|     uint32_t log_size; | ||||
|     uint32_t block_size; | ||||
|     uint64_t image_size = (uint64_t) 2 * GiB; | ||||
|     uint32_t log_size   = 1 * MiB; | ||||
|     uint32_t block_size = 0; | ||||
|     uint64_t signature; | ||||
|     uint64_t metadata_offset; | ||||
|     bool use_zero_blocks = false; | ||||
|  | ||||
|     gunichar2 *creator = NULL; | ||||
|     glong creator_items; | ||||
|     BlockBackend *blk; | ||||
|     char *type = NULL; | ||||
|     VHDXImageType image_type; | ||||
|     Error *local_err = NULL; | ||||
|  | ||||
|     assert(opts->driver == BLOCKDEV_DRIVER_VHDX); | ||||
|     vhdx_opts = &opts->u.vhdx; | ||||
|     image_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), | ||||
|                           BDRV_SECTOR_SIZE); | ||||
|     log_size = qemu_opt_get_size_del(opts, VHDX_BLOCK_OPT_LOG_SIZE, 0); | ||||
|     block_size = qemu_opt_get_size_del(opts, VHDX_BLOCK_OPT_BLOCK_SIZE, 0); | ||||
|     type = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT); | ||||
|     use_zero_blocks = qemu_opt_get_bool_del(opts, VHDX_BLOCK_OPT_ZERO, true); | ||||
|  | ||||
|     /* Validate options and set default values */ | ||||
|     image_size = vhdx_opts->size; | ||||
|     if (image_size > VHDX_MAX_IMAGE_SIZE) { | ||||
|         error_setg(errp, "Image size too large; max of 64TB"); | ||||
|         return -EINVAL; | ||||
|         error_setg_errno(errp, EINVAL, "Image size too large; max of 64TB"); | ||||
|         ret = -EINVAL; | ||||
|         goto exit; | ||||
|     } | ||||
|  | ||||
|     if (!vhdx_opts->has_log_size) { | ||||
|         log_size = DEFAULT_LOG_SIZE; | ||||
|     } else { | ||||
|         if (vhdx_opts->log_size > UINT32_MAX) { | ||||
|             error_setg(errp, "Log size must be smaller than 4 GB"); | ||||
|             return -EINVAL; | ||||
|         } | ||||
|         log_size = vhdx_opts->log_size; | ||||
|     } | ||||
|     if (log_size < MiB || (log_size % MiB) != 0) { | ||||
|         error_setg(errp, "Log size must be a multiple of 1 MB"); | ||||
|         return -EINVAL; | ||||
|     if (type == NULL) { | ||||
|         type = g_strdup("dynamic"); | ||||
|     } | ||||
|  | ||||
|     if (!vhdx_opts->has_block_state_zero) { | ||||
|         use_zero_blocks = true; | ||||
|     } else { | ||||
|         use_zero_blocks = vhdx_opts->block_state_zero; | ||||
|     } | ||||
|  | ||||
|     if (!vhdx_opts->has_subformat) { | ||||
|         vhdx_opts->subformat = BLOCKDEV_VHDX_SUBFORMAT_DYNAMIC; | ||||
|     } | ||||
|  | ||||
|     switch (vhdx_opts->subformat) { | ||||
|     case BLOCKDEV_VHDX_SUBFORMAT_DYNAMIC: | ||||
|     if (!strcmp(type, "dynamic")) { | ||||
|         image_type = VHDX_TYPE_DYNAMIC; | ||||
|         break; | ||||
|     case BLOCKDEV_VHDX_SUBFORMAT_FIXED: | ||||
|     } else if (!strcmp(type, "fixed")) { | ||||
|         image_type = VHDX_TYPE_FIXED; | ||||
|         break; | ||||
|     default: | ||||
|         g_assert_not_reached(); | ||||
|     } else if (!strcmp(type, "differencing")) { | ||||
|         error_setg_errno(errp, ENOTSUP, | ||||
|                          "Differencing files not yet supported"); | ||||
|         ret = -ENOTSUP; | ||||
|         goto exit; | ||||
|     } else { | ||||
|         error_setg(errp, "Invalid subformat '%s'", type); | ||||
|         ret = -EINVAL; | ||||
|         goto exit; | ||||
|     } | ||||
|  | ||||
|     /* These are pretty arbitrary, and mainly designed to keep the BAT | ||||
|      * size reasonable to load into RAM */ | ||||
|     if (vhdx_opts->has_block_size) { | ||||
|         block_size = vhdx_opts->block_size; | ||||
|     } else { | ||||
|     if (block_size == 0) { | ||||
|         if (image_size > 32 * TiB) { | ||||
|             block_size = 64 * MiB; | ||||
|         } else if (image_size > (uint64_t) 100 * GiB) { | ||||
| @@ -1880,30 +1856,30 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (block_size < MiB || (block_size % MiB) != 0) { | ||||
|         error_setg(errp, "Block size must be a multiple of 1 MB"); | ||||
|         return -EINVAL; | ||||
|     } | ||||
|     if (!is_power_of_2(block_size)) { | ||||
|         error_setg(errp, "Block size must be a power of two"); | ||||
|         return -EINVAL; | ||||
|     } | ||||
|     if (block_size > VHDX_BLOCK_SIZE_MAX) { | ||||
|         error_setg(errp, "Block size must not exceed %d", VHDX_BLOCK_SIZE_MAX); | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     /* Create BlockBackend to write to the image */ | ||||
|     bs = bdrv_open_blockdev_ref(vhdx_opts->file, errp); | ||||
|     if (bs == NULL) { | ||||
|         return -EIO; | ||||
|     } | ||||
|     /* make the log size close to what was specified, but must be | ||||
|      * min 1MB, and multiple of 1MB */ | ||||
|     log_size = ROUND_UP(log_size, MiB); | ||||
|  | ||||
|     blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); | ||||
|     ret = blk_insert_bs(blk, bs, errp); | ||||
|     block_size = ROUND_UP(block_size, MiB); | ||||
|     block_size = block_size > VHDX_BLOCK_SIZE_MAX ? VHDX_BLOCK_SIZE_MAX : | ||||
|                                                     block_size; | ||||
|  | ||||
|     ret = bdrv_create_file(filename, opts, &local_err); | ||||
|     if (ret < 0) { | ||||
|         goto delete_and_exit; | ||||
|         error_propagate(errp, local_err); | ||||
|         goto exit; | ||||
|     } | ||||
|  | ||||
|     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); | ||||
|         ret = -EIO; | ||||
|         goto exit; | ||||
|     } | ||||
|  | ||||
|     blk_set_allow_write_beyond_eof(blk, true); | ||||
|  | ||||
|     /* Create (A) */ | ||||
| @@ -1952,108 +1928,15 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, | ||||
|         goto delete_and_exit; | ||||
|     } | ||||
|  | ||||
|     ret = 0; | ||||
|  | ||||
| delete_and_exit: | ||||
|     blk_unref(blk); | ||||
|     bdrv_unref(bs); | ||||
| exit: | ||||
|     g_free(type); | ||||
|     g_free(creator); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn vhdx_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; | ||||
|     int ret; | ||||
|  | ||||
|     static const QDictRenames opt_renames[] = { | ||||
|         { VHDX_BLOCK_OPT_LOG_SIZE,      "log-size" }, | ||||
|         { VHDX_BLOCK_OPT_BLOCK_SIZE,    "block-size" }, | ||||
|         { VHDX_BLOCK_OPT_ZERO,          "block-state-zero" }, | ||||
|         { NULL, NULL }, | ||||
|     }; | ||||
|  | ||||
|     /* Parse options and convert legacy syntax */ | ||||
|     qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vhdx_create_opts, true); | ||||
|  | ||||
|     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", "vhdx"); | ||||
|     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 sizes: | ||||
|      * The image size is rounded to 512 bytes. Make the block and log size | ||||
|      * close to what was specified, but must be at least 1MB, and a multiple of | ||||
|      * 1 MB. Also respect VHDX_BLOCK_SIZE_MAX for block sizes. block_size = 0 | ||||
|      * means auto, which is represented by a missing key in QAPI. */ | ||||
|     assert(create_options->driver == BLOCKDEV_DRIVER_VHDX); | ||||
|     create_options->u.vhdx.size = | ||||
|         ROUND_UP(create_options->u.vhdx.size, BDRV_SECTOR_SIZE); | ||||
|  | ||||
|     if (create_options->u.vhdx.has_log_size) { | ||||
|         create_options->u.vhdx.log_size = | ||||
|             ROUND_UP(create_options->u.vhdx.log_size, MiB); | ||||
|     } | ||||
|     if (create_options->u.vhdx.has_block_size) { | ||||
|         create_options->u.vhdx.block_size = | ||||
|             ROUND_UP(create_options->u.vhdx.block_size, MiB); | ||||
|  | ||||
|         if (create_options->u.vhdx.block_size == 0) { | ||||
|             create_options->u.vhdx.has_block_size = false; | ||||
|         } | ||||
|         if (create_options->u.vhdx.block_size > VHDX_BLOCK_SIZE_MAX) { | ||||
|             create_options->u.vhdx.block_size = VHDX_BLOCK_SIZE_MAX; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* Create the vhdx image (format layer) */ | ||||
|     ret = vhdx_co_create(create_options, errp); | ||||
|  | ||||
| fail: | ||||
|     qobject_unref(qdict); | ||||
|     bdrv_unref(bs); | ||||
|     qapi_free_BlockdevCreateOptions(create_options); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /* If opened r/w, the VHDX driver will automatically replay the log, | ||||
|  * if one is present, inside the vhdx_open() call. | ||||
|  * | ||||
| @@ -2122,7 +2005,6 @@ static BlockDriver bdrv_vhdx = { | ||||
|     .bdrv_child_perm        = bdrv_format_default_perms, | ||||
|     .bdrv_co_readv          = vhdx_co_readv, | ||||
|     .bdrv_co_writev         = vhdx_co_writev, | ||||
|     .bdrv_co_create         = vhdx_co_create, | ||||
|     .bdrv_co_create_opts    = vhdx_co_create_opts, | ||||
|     .bdrv_get_info          = vhdx_get_info, | ||||
|     .bdrv_co_check          = vhdx_co_check, | ||||
|   | ||||
							
								
								
									
										12
									
								
								block/vmdk.c
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								block/vmdk.c
									
									
									
									
									
								
							| @@ -47,8 +47,6 @@ | ||||
| #define VMDK4_FLAG_MARKER (1 << 17) | ||||
| #define VMDK4_GD_AT_END 0xffffffffffffffffULL | ||||
|  | ||||
| #define VMDK_EXTENT_MAX_SECTORS (1ULL << 32) | ||||
|  | ||||
| #define VMDK_GTE_ZEROED 0x1 | ||||
|  | ||||
| /* VMDK internal error codes */ | ||||
| @@ -333,12 +331,6 @@ static int vmdk_is_cid_valid(BlockDriverState *bs) | ||||
|     if (!s->cid_checked && bs->backing) { | ||||
|         BlockDriverState *p_bs = bs->backing->bs; | ||||
|  | ||||
|         if (strcmp(p_bs->drv->format_name, "vmdk")) { | ||||
|             /* Backing file is not in vmdk format, so it does not have | ||||
|              * a CID, which makes the overlay's parent CID invalid */ | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
|         if (vmdk_read_cid(p_bs, 0, &cur_pcid) != 0) { | ||||
|             /* read failure: report as not valid */ | ||||
|             return 0; | ||||
| @@ -1258,10 +1250,6 @@ static int get_cluster_offset(BlockDriverState *bs, | ||||
|             return zeroed ? VMDK_ZEROED : VMDK_UNALLOC; | ||||
|         } | ||||
|  | ||||
|         if (extent->next_cluster_sector >= VMDK_EXTENT_MAX_SECTORS) { | ||||
|             return VMDK_ERROR; | ||||
|         } | ||||
|  | ||||
|         cluster_sector = extent->next_cluster_sector; | ||||
|         extent->next_cluster_sector += extent->cluster_sectors; | ||||
|  | ||||
|   | ||||
							
								
								
									
										234
									
								
								block/vpc.c
									
									
									
									
									
								
							
							
						
						
									
										234
									
								
								block/vpc.c
									
									
									
									
									
								
							| @@ -26,16 +26,12 @@ | ||||
| #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 "migration/blocker.h" | ||||
| #include "qemu/bswap.h" | ||||
| #include "qemu/uuid.h" | ||||
| #include "qapi/qmp/qdict.h" | ||||
| #include "qapi/qobject-input-visitor.h" | ||||
| #include "qapi/qapi-visit-block-core.h" | ||||
|  | ||||
| /**************************************************************/ | ||||
|  | ||||
| @@ -170,8 +166,6 @@ static QemuOptsList vpc_runtime_opts = { | ||||
|     } | ||||
| }; | ||||
|  | ||||
| static QemuOptsList vpc_create_opts; | ||||
|  | ||||
| static uint32_t vpc_checksum(uint8_t* buf, size_t size) | ||||
| { | ||||
|     uint32_t res = 0; | ||||
| @@ -903,19 +897,60 @@ static int create_fixed_disk(BlockBackend *blk, uint8_t *buf, | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static int calculate_rounded_image_size(BlockdevCreateOptionsVpc *vpc_opts, | ||||
|                                         uint16_t *out_cyls, | ||||
|                                         uint8_t *out_heads, | ||||
|                                         uint8_t *out_secs_per_cyl, | ||||
|                                         int64_t *out_total_sectors, | ||||
| static int coroutine_fn vpc_co_create_opts(const char *filename, QemuOpts *opts, | ||||
|                                            Error **errp) | ||||
| { | ||||
|     int64_t total_size = vpc_opts->size; | ||||
|     uint8_t buf[1024]; | ||||
|     VHDFooter *footer = (VHDFooter *) buf; | ||||
|     char *disk_type_param; | ||||
|     int i; | ||||
|     uint16_t cyls = 0; | ||||
|     uint8_t heads = 0; | ||||
|     uint8_t secs_per_cyl = 0; | ||||
|     int64_t total_sectors; | ||||
|     int i; | ||||
|     int64_t total_size; | ||||
|     int disk_type; | ||||
|     int ret = -EIO; | ||||
|     bool force_size; | ||||
|     Error *local_err = NULL; | ||||
|     BlockBackend *blk = NULL; | ||||
|  | ||||
|     /* Read out options */ | ||||
|     total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), | ||||
|                           BDRV_SECTOR_SIZE); | ||||
|     disk_type_param = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT); | ||||
|     if (disk_type_param) { | ||||
|         if (!strcmp(disk_type_param, "dynamic")) { | ||||
|             disk_type = VHD_DYNAMIC; | ||||
|         } else if (!strcmp(disk_type_param, "fixed")) { | ||||
|             disk_type = VHD_FIXED; | ||||
|         } else { | ||||
|             error_setg(errp, "Invalid disk type, %s", disk_type_param); | ||||
|             ret = -EINVAL; | ||||
|             goto out; | ||||
|         } | ||||
|     } else { | ||||
|         disk_type = VHD_DYNAMIC; | ||||
|     } | ||||
|  | ||||
|     force_size = qemu_opt_get_bool_del(opts, VPC_OPT_FORCE_SIZE, false); | ||||
|  | ||||
|     ret = bdrv_create_file(filename, opts, &local_err); | ||||
|     if (ret < 0) { | ||||
|         error_propagate(errp, local_err); | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     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); | ||||
|         ret = -EIO; | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     blk_set_allow_write_beyond_eof(blk, true); | ||||
|  | ||||
|     /* | ||||
|      * Calculate matching total_size and geometry. Increase the number of | ||||
| @@ -926,7 +961,7 @@ static int calculate_rounded_image_size(BlockdevCreateOptionsVpc *vpc_opts, | ||||
|      * we set the geometry to 65535 x 16 x 255 (CxHxS) sectors and use | ||||
|      * the image size from the VHD footer to calculate total_sectors. | ||||
|      */ | ||||
|     if (vpc_opts->force_size) { | ||||
|     if (force_size) { | ||||
|         /* This will force the use of total_size for sector count, below */ | ||||
|         cyls         = VHD_CHS_MAX_C; | ||||
|         heads        = VHD_CHS_MAX_H; | ||||
| @@ -943,95 +978,19 @@ static int calculate_rounded_image_size(BlockdevCreateOptionsVpc *vpc_opts, | ||||
|         /* Allow a maximum disk size of 2040 GiB */ | ||||
|         if (total_sectors > VHD_MAX_SECTORS) { | ||||
|             error_setg(errp, "Disk size is too large, max size is 2040 GiB"); | ||||
|             return -EFBIG; | ||||
|             ret = -EFBIG; | ||||
|             goto out; | ||||
|         } | ||||
|     } else { | ||||
|         total_sectors = (int64_t)cyls * heads * secs_per_cyl; | ||||
|     } | ||||
|  | ||||
|     *out_total_sectors = total_sectors; | ||||
|     if (out_cyls) { | ||||
|         *out_cyls = cyls; | ||||
|         *out_heads = heads; | ||||
|         *out_secs_per_cyl = secs_per_cyl; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts, | ||||
|                                       Error **errp) | ||||
| { | ||||
|     BlockdevCreateOptionsVpc *vpc_opts; | ||||
|     BlockBackend *blk = NULL; | ||||
|     BlockDriverState *bs = NULL; | ||||
|  | ||||
|     uint8_t buf[1024]; | ||||
|     VHDFooter *footer = (VHDFooter *) buf; | ||||
|     uint16_t cyls = 0; | ||||
|     uint8_t heads = 0; | ||||
|     uint8_t secs_per_cyl = 0; | ||||
|     int64_t total_sectors; | ||||
|     int64_t total_size; | ||||
|     int disk_type; | ||||
|     int ret = -EIO; | ||||
|  | ||||
|     assert(opts->driver == BLOCKDEV_DRIVER_VPC); | ||||
|     vpc_opts = &opts->u.vpc; | ||||
|  | ||||
|     /* Validate options and set default values */ | ||||
|     total_size = vpc_opts->size; | ||||
|  | ||||
|     if (!vpc_opts->has_subformat) { | ||||
|         vpc_opts->subformat = BLOCKDEV_VPC_SUBFORMAT_DYNAMIC; | ||||
|     } | ||||
|     switch (vpc_opts->subformat) { | ||||
|     case BLOCKDEV_VPC_SUBFORMAT_DYNAMIC: | ||||
|         disk_type = VHD_DYNAMIC; | ||||
|         break; | ||||
|     case BLOCKDEV_VPC_SUBFORMAT_FIXED: | ||||
|         disk_type = VHD_FIXED; | ||||
|         break; | ||||
|     default: | ||||
|         g_assert_not_reached(); | ||||
|     } | ||||
|  | ||||
|     /* Create BlockBackend to write to the image */ | ||||
|     bs = bdrv_open_blockdev_ref(vpc_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); | ||||
|  | ||||
|     /* Get geometry and check that it matches the image size*/ | ||||
|     ret = calculate_rounded_image_size(vpc_opts, &cyls, &heads, &secs_per_cyl, | ||||
|                                        &total_sectors, errp); | ||||
|     if (ret < 0) { | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     if (total_size != total_sectors * BDRV_SECTOR_SIZE) { | ||||
|         error_setg(errp, "The requested image size cannot be represented in " | ||||
|                          "CHS geometry"); | ||||
|         error_append_hint(errp, "Try size=%llu or force-size=on (the " | ||||
|                                 "latter makes the image incompatible with " | ||||
|                                 "Virtual PC)", | ||||
|                           total_sectors * BDRV_SECTOR_SIZE); | ||||
|         ret = -EINVAL; | ||||
|         goto out; | ||||
|         total_size = total_sectors * BDRV_SECTOR_SIZE; | ||||
|     } | ||||
|  | ||||
|     /* Prepare the Hard Disk Footer */ | ||||
|     memset(buf, 0, 1024); | ||||
|  | ||||
|     memcpy(footer->creator, "conectix", 8); | ||||
|     if (vpc_opts->force_size) { | ||||
|     if (force_size) { | ||||
|         memcpy(footer->creator_app, "qem2", 4); | ||||
|     } else { | ||||
|         memcpy(footer->creator_app, "qemu", 4); | ||||
| @@ -1073,94 +1032,10 @@ static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts, | ||||
|  | ||||
| out: | ||||
|     blk_unref(blk); | ||||
|     bdrv_unref(bs); | ||||
|     g_free(disk_type_param); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn vpc_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; | ||||
|     int ret; | ||||
|  | ||||
|     static const QDictRenames opt_renames[] = { | ||||
|         { VPC_OPT_FORCE_SIZE,           "force-size" }, | ||||
|         { NULL, NULL }, | ||||
|     }; | ||||
|  | ||||
|     /* Parse options and convert legacy syntax */ | ||||
|     qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vpc_create_opts, true); | ||||
|  | ||||
|     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", "vpc"); | ||||
|     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_VPC); | ||||
|     create_options->u.vpc.size = | ||||
|         ROUND_UP(create_options->u.vpc.size, BDRV_SECTOR_SIZE); | ||||
|  | ||||
|     if (!create_options->u.vpc.force_size) { | ||||
|         int64_t total_sectors; | ||||
|         ret = calculate_rounded_image_size(&create_options->u.vpc, NULL, NULL, | ||||
|                                            NULL, &total_sectors, errp); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
|         } | ||||
|  | ||||
|         create_options->u.vpc.size = total_sectors * BDRV_SECTOR_SIZE; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /* Create the vpc image (format layer) */ | ||||
|     ret = vpc_co_create(create_options, errp); | ||||
|  | ||||
| fail: | ||||
|     qobject_unref(qdict); | ||||
|     bdrv_unref(bs); | ||||
|     qapi_free_BlockdevCreateOptions(create_options); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int vpc_has_zero_init(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVVPCState *s = bs->opaque; | ||||
| @@ -1221,7 +1096,6 @@ static BlockDriver bdrv_vpc = { | ||||
|     .bdrv_close             = vpc_close, | ||||
|     .bdrv_reopen_prepare    = vpc_reopen_prepare, | ||||
|     .bdrv_child_perm        = bdrv_format_default_perms, | ||||
|     .bdrv_co_create         = vpc_co_create, | ||||
|     .bdrv_co_create_opts    = vpc_co_create_opts, | ||||
|  | ||||
|     .bdrv_co_preadv             = vpc_co_preadv, | ||||
|   | ||||
| @@ -27,7 +27,6 @@ | ||||
| #include <dirent.h> | ||||
| #include "qapi/error.h" | ||||
| #include "block/block_int.h" | ||||
| #include "block/qdict.h" | ||||
| #include "qemu/module.h" | ||||
| #include "qemu/option.h" | ||||
| #include "qemu/bswap.h" | ||||
| @@ -3130,11 +3129,10 @@ static void vvfat_qcow_options(int *child_flags, QDict *child_options, | ||||
|                                int parent_flags, QDict *parent_options) | ||||
| { | ||||
|     qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "off"); | ||||
|     qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on"); | ||||
|     *child_flags = BDRV_O_NO_FLUSH; | ||||
| } | ||||
|  | ||||
| static const BdrvChildRole child_vvfat_qcow = { | ||||
|     .parent_is_bds      = true, | ||||
|     .inherit_options    = vvfat_qcow_options, | ||||
| }; | ||||
|  | ||||
| @@ -3181,7 +3179,7 @@ static int enable_write_target(BlockDriverState *bs, Error **errp) | ||||
|     qdict_put_str(options, "write-target.driver", "qcow"); | ||||
|     s->qcow = bdrv_open_child(s->qcow_filename, options, "write-target", bs, | ||||
|                               &child_vvfat_qcow, false, errp); | ||||
|     qobject_unref(options); | ||||
|     QDECREF(options); | ||||
|     if (!s->qcow) { | ||||
|         ret = -EINVAL; | ||||
|         goto err; | ||||
|   | ||||
							
								
								
									
										44
									
								
								block/vxhs.c
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								block/vxhs.c
									
									
									
									
									
								
							| @@ -12,7 +12,6 @@ | ||||
| #include <qnio/qnio_api.h> | ||||
| #include <sys/param.h> | ||||
| #include "block/block_int.h" | ||||
| #include "block/qdict.h" | ||||
| #include "qapi/qmp/qerror.h" | ||||
| #include "qapi/qmp/qdict.h" | ||||
| #include "qapi/qmp/qstring.h" | ||||
| @@ -217,12 +216,6 @@ static void vxhs_parse_filename(const char *filename, QDict *options, | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void vxhs_refresh_limits(BlockDriverState *bs, Error **errp) | ||||
| { | ||||
|     /* XXX Does VXHS support AIO on less than 512-byte alignment? */ | ||||
|     bs->bl.request_alignment = 512; | ||||
| } | ||||
|  | ||||
| static int vxhs_init_and_ref(void) | ||||
| { | ||||
|     if (vxhs_ref++ == 0) { | ||||
| @@ -403,7 +396,7 @@ static int vxhs_open(BlockDriverState *bs, QDict *options, | ||||
|  | ||||
| out: | ||||
|     g_free(of_vsa_addr); | ||||
|     qobject_unref(backing_options); | ||||
|     QDECREF(backing_options); | ||||
|     qemu_opts_del(tcp_opts); | ||||
|     qemu_opts_del(opts); | ||||
|     g_free(cacert); | ||||
| @@ -431,17 +424,21 @@ static const AIOCBInfo vxhs_aiocb_info = { | ||||
|  * and is passed to QNIO. When QNIO completes the work, | ||||
|  * it will be passed back through the callback. | ||||
|  */ | ||||
| static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, uint64_t offset, | ||||
|                                QEMUIOVector *qiov, uint64_t size, | ||||
| static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, int64_t sector_num, | ||||
|                                QEMUIOVector *qiov, int nb_sectors, | ||||
|                                BlockCompletionFunc *cb, void *opaque, | ||||
|                                VDISKAIOCmd iodir) | ||||
| { | ||||
|     VXHSAIOCB *acb = NULL; | ||||
|     BDRVVXHSState *s = bs->opaque; | ||||
|     size_t size; | ||||
|     uint64_t offset; | ||||
|     int iio_flags = 0; | ||||
|     int ret = 0; | ||||
|     void *dev_handle = s->vdisk_hostinfo.dev_handle; | ||||
|  | ||||
|     offset = sector_num * BDRV_SECTOR_SIZE; | ||||
|     size = nb_sectors * BDRV_SECTOR_SIZE; | ||||
|     acb = qemu_aio_get(&vxhs_aiocb_info, bs, cb, opaque); | ||||
|  | ||||
|     /* | ||||
| @@ -454,11 +451,11 @@ static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, uint64_t offset, | ||||
|     switch (iodir) { | ||||
|     case VDISK_AIO_WRITE: | ||||
|             ret = iio_writev(dev_handle, acb, qiov->iov, qiov->niov, | ||||
|                              offset, size, iio_flags); | ||||
|                              offset, (uint64_t)size, iio_flags); | ||||
|             break; | ||||
|     case VDISK_AIO_READ: | ||||
|             ret = iio_readv(dev_handle, acb, qiov->iov, qiov->niov, | ||||
|                             offset, size, iio_flags); | ||||
|                             offset, (uint64_t)size, iio_flags); | ||||
|             break; | ||||
|     default: | ||||
|             trace_vxhs_aio_rw_invalid(iodir); | ||||
| @@ -477,20 +474,22 @@ errout: | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| static BlockAIOCB *vxhs_aio_preadv(BlockDriverState *bs, | ||||
|                                    uint64_t offset, uint64_t bytes, | ||||
|                                    QEMUIOVector *qiov, int flags, | ||||
| static BlockAIOCB *vxhs_aio_readv(BlockDriverState *bs, | ||||
|                                    int64_t sector_num, QEMUIOVector *qiov, | ||||
|                                    int nb_sectors, | ||||
|                                    BlockCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     return vxhs_aio_rw(bs, offset, qiov, bytes, cb, opaque, VDISK_AIO_READ); | ||||
|     return vxhs_aio_rw(bs, sector_num, qiov, nb_sectors, cb, | ||||
|                        opaque, VDISK_AIO_READ); | ||||
| } | ||||
|  | ||||
| static BlockAIOCB *vxhs_aio_pwritev(BlockDriverState *bs, | ||||
|                                     uint64_t offset, uint64_t bytes, | ||||
|                                     QEMUIOVector *qiov, int flags, | ||||
| static BlockAIOCB *vxhs_aio_writev(BlockDriverState *bs, | ||||
|                                    int64_t sector_num, QEMUIOVector *qiov, | ||||
|                                    int nb_sectors, | ||||
|                                    BlockCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     return vxhs_aio_rw(bs, offset, qiov, bytes, cb, opaque, VDISK_AIO_WRITE); | ||||
|     return vxhs_aio_rw(bs, sector_num, qiov, nb_sectors, | ||||
|                        cb, opaque, VDISK_AIO_WRITE); | ||||
| } | ||||
|  | ||||
| static void vxhs_close(BlockDriverState *bs) | ||||
| @@ -562,11 +561,10 @@ static BlockDriver bdrv_vxhs = { | ||||
|     .instance_size                = sizeof(BDRVVXHSState), | ||||
|     .bdrv_file_open               = vxhs_open, | ||||
|     .bdrv_parse_filename          = vxhs_parse_filename, | ||||
|     .bdrv_refresh_limits          = vxhs_refresh_limits, | ||||
|     .bdrv_close                   = vxhs_close, | ||||
|     .bdrv_getlength               = vxhs_getlength, | ||||
|     .bdrv_aio_preadv              = vxhs_aio_preadv, | ||||
|     .bdrv_aio_pwritev             = vxhs_aio_pwritev, | ||||
|     .bdrv_aio_readv               = vxhs_aio_readv, | ||||
|     .bdrv_aio_writev              = vxhs_aio_writev, | ||||
| }; | ||||
|  | ||||
| static void bdrv_vxhs_init(void) | ||||
|   | ||||
| @@ -112,14 +112,15 @@ static const AIOCBInfo win32_aiocb_info = { | ||||
|  | ||||
| BlockAIOCB *win32_aio_submit(BlockDriverState *bs, | ||||
|         QEMUWin32AIOState *aio, HANDLE hfile, | ||||
|         uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, | ||||
|         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | ||||
|         BlockCompletionFunc *cb, void *opaque, int type) | ||||
| { | ||||
|     struct QEMUWin32AIOCB *waiocb; | ||||
|     uint64_t offset = sector_num * 512; | ||||
|     DWORD rc; | ||||
|  | ||||
|     waiocb = qemu_aio_get(&win32_aiocb_info, bs, cb, opaque); | ||||
|     waiocb->nbytes = bytes; | ||||
|     waiocb->nbytes = nb_sectors * 512; | ||||
|     waiocb->qiov = qiov; | ||||
|     waiocb->is_read = (type == QEMU_AIO_READ); | ||||
|  | ||||
|   | ||||
| @@ -220,26 +220,3 @@ void qmp_nbd_server_stop(Error **errp) | ||||
|     nbd_server_free(nbd_server); | ||||
|     nbd_server = NULL; | ||||
| } | ||||
|  | ||||
| void qmp_x_nbd_server_add_bitmap(const char *name, const char *bitmap, | ||||
|                                  bool has_bitmap_export_name, | ||||
|                                  const char *bitmap_export_name, | ||||
|                                  Error **errp) | ||||
| { | ||||
|     NBDExport *exp; | ||||
|  | ||||
|     if (!nbd_server) { | ||||
|         error_setg(errp, "NBD server not running"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     exp = nbd_export_find(name); | ||||
|     if (exp == NULL) { | ||||
|         error_setg(errp, "Export '%s' is not found", name); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     nbd_export_bitmap(exp, bitmap, | ||||
|                       has_bitmap_export_name ? bitmap_export_name : bitmap, | ||||
|                       errp); | ||||
| } | ||||
|   | ||||
							
								
								
									
										384
									
								
								blockdev.c
									
									
									
									
									
								
							
							
						
						
									
										384
									
								
								blockdev.c
									
									
									
									
									
								
							| @@ -35,7 +35,6 @@ | ||||
| #include "sysemu/blockdev.h" | ||||
| #include "hw/block/block.h" | ||||
| #include "block/blockjob.h" | ||||
| #include "block/qdict.h" | ||||
| #include "block/throttle-groups.h" | ||||
| #include "monitor/monitor.h" | ||||
| #include "qemu/error-report.h" | ||||
| @@ -151,7 +150,7 @@ void blockdev_mark_auto_del(BlockBackend *blk) | ||||
|         aio_context_acquire(aio_context); | ||||
|  | ||||
|         if (bs->job) { | ||||
|             job_cancel(&bs->job->job, false); | ||||
|             block_job_cancel(bs->job); | ||||
|         } | ||||
|  | ||||
|         aio_context_release(aio_context); | ||||
| @@ -335,8 +334,7 @@ static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals, | ||||
|  | ||||
|         case QTYPE_QSTRING: { | ||||
|             unsigned long long length; | ||||
|             const char *str = qstring_get_str(qobject_to(QString, | ||||
|                                                          entry->value)); | ||||
|             const char *str = qstring_get_str(qobject_to_qstring(entry->value)); | ||||
|             if (parse_uint_full(str, &length, 10) == 0 && | ||||
|                 length > 0 && length <= UINT_MAX) { | ||||
|                 block_acct_add_interval(stats, (unsigned) length); | ||||
| @@ -348,7 +346,7 @@ static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals, | ||||
|         } | ||||
|  | ||||
|         case QTYPE_QNUM: { | ||||
|             int64_t length = qnum_get_int(qobject_to(QNum, entry->value)); | ||||
|             int64_t length = qnum_get_int(qobject_to_qnum(entry->value)); | ||||
|  | ||||
|             if (length > 0 && length <= UINT_MAX) { | ||||
|                 block_acct_add_interval(stats, (unsigned) length); | ||||
| @@ -577,7 +575,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, | ||||
|         blk_rs->read_only     = read_only; | ||||
|         blk_rs->detect_zeroes = detect_zeroes; | ||||
|  | ||||
|         qobject_unref(bs_opts); | ||||
|         QDECREF(bs_opts); | ||||
|     } else { | ||||
|         if (file && !*file) { | ||||
|             file = NULL; | ||||
| @@ -633,16 +631,16 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, | ||||
|  | ||||
| err_no_bs_opts: | ||||
|     qemu_opts_del(opts); | ||||
|     qobject_unref(interval_dict); | ||||
|     qobject_unref(interval_list); | ||||
|     QDECREF(interval_dict); | ||||
|     QDECREF(interval_list); | ||||
|     return blk; | ||||
|  | ||||
| early_err: | ||||
|     qemu_opts_del(opts); | ||||
|     qobject_unref(interval_dict); | ||||
|     qobject_unref(interval_list); | ||||
|     QDECREF(interval_dict); | ||||
|     QDECREF(interval_list); | ||||
| err_no_opts: | ||||
|     qobject_unref(bs_opts); | ||||
|     QDECREF(bs_opts); | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| @@ -1131,7 +1129,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||||
|  | ||||
| fail: | ||||
|     qemu_opts_del(legacy_opts); | ||||
|     qobject_unref(bs_opts); | ||||
|     QDECREF(bs_opts); | ||||
|     return dinfo; | ||||
| } | ||||
|  | ||||
| @@ -1447,7 +1445,7 @@ typedef struct BlkActionOps { | ||||
| struct BlkActionState { | ||||
|     TransactionAction *action; | ||||
|     const BlkActionOps *ops; | ||||
|     JobTxn *block_job_txn; | ||||
|     BlockJobTxn *block_job_txn; | ||||
|     TransactionProperties *txn_props; | ||||
|     QSIMPLEQ_ENTRY(BlkActionState) entry; | ||||
| }; | ||||
| @@ -1865,7 +1863,7 @@ typedef struct DriveBackupState { | ||||
|     BlockJob *job; | ||||
| } DriveBackupState; | ||||
|  | ||||
| static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, | ||||
| static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, | ||||
|                             Error **errp); | ||||
|  | ||||
| static void drive_backup_prepare(BlkActionState *common, Error **errp) | ||||
| @@ -1911,7 +1909,7 @@ static void drive_backup_commit(BlkActionState *common) | ||||
|     aio_context_acquire(aio_context); | ||||
|  | ||||
|     assert(state->job); | ||||
|     job_start(&state->job->job); | ||||
|     block_job_start(state->job); | ||||
|  | ||||
|     aio_context_release(aio_context); | ||||
| } | ||||
| @@ -1926,7 +1924,7 @@ static void drive_backup_abort(BlkActionState *common) | ||||
|         aio_context = bdrv_get_aio_context(state->bs); | ||||
|         aio_context_acquire(aio_context); | ||||
|  | ||||
|         job_cancel_sync(&state->job->job); | ||||
|         block_job_cancel_sync(state->job); | ||||
|  | ||||
|         aio_context_release(aio_context); | ||||
|     } | ||||
| @@ -1955,7 +1953,7 @@ typedef struct BlockdevBackupState { | ||||
|     BlockJob *job; | ||||
| } BlockdevBackupState; | ||||
|  | ||||
| static BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, | ||||
| static BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, | ||||
|                                     Error **errp); | ||||
|  | ||||
| static void blockdev_backup_prepare(BlkActionState *common, Error **errp) | ||||
| @@ -1969,7 +1967,7 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp) | ||||
|     assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); | ||||
|     backup = common->action->u.blockdev_backup.data; | ||||
|  | ||||
|     bs = bdrv_lookup_bs(backup->device, backup->device, errp); | ||||
|     bs = qmp_get_root_bs(backup->device, errp); | ||||
|     if (!bs) { | ||||
|         return; | ||||
|     } | ||||
| @@ -2009,7 +2007,7 @@ static void blockdev_backup_commit(BlkActionState *common) | ||||
|     aio_context_acquire(aio_context); | ||||
|  | ||||
|     assert(state->job); | ||||
|     job_start(&state->job->job); | ||||
|     block_job_start(state->job); | ||||
|  | ||||
|     aio_context_release(aio_context); | ||||
| } | ||||
| @@ -2024,7 +2022,7 @@ static void blockdev_backup_abort(BlkActionState *common) | ||||
|         aio_context = bdrv_get_aio_context(state->bs); | ||||
|         aio_context_acquire(aio_context); | ||||
|  | ||||
|         job_cancel_sync(&state->job->job); | ||||
|         block_job_cancel_sync(state->job); | ||||
|  | ||||
|         aio_context_release(aio_context); | ||||
|     } | ||||
| @@ -2053,7 +2051,6 @@ typedef struct BlockDirtyBitmapState { | ||||
|     BlockDriverState *bs; | ||||
|     HBitmap *backup; | ||||
|     bool prepared; | ||||
|     bool was_enabled; | ||||
| } BlockDirtyBitmapState; | ||||
|  | ||||
| static void block_dirty_bitmap_add_prepare(BlkActionState *common, | ||||
| @@ -2074,7 +2071,6 @@ static void block_dirty_bitmap_add_prepare(BlkActionState *common, | ||||
|                                action->has_granularity, action->granularity, | ||||
|                                action->has_persistent, action->persistent, | ||||
|                                action->has_autoload, action->autoload, | ||||
|                                action->has_x_disabled, action->x_disabled, | ||||
|                                &local_err); | ||||
|  | ||||
|     if (!local_err) { | ||||
| @@ -2122,9 +2118,6 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common, | ||||
|     if (bdrv_dirty_bitmap_frozen(state->bitmap)) { | ||||
|         error_setg(errp, "Cannot modify a frozen bitmap"); | ||||
|         return; | ||||
|     } else if (bdrv_dirty_bitmap_qmp_locked(state->bitmap)) { | ||||
|         error_setg(errp, "Cannot modify a locked bitmap"); | ||||
|         return; | ||||
|     } else if (!bdrv_dirty_bitmap_enabled(state->bitmap)) { | ||||
|         error_setg(errp, "Cannot clear a disabled bitmap"); | ||||
|         return; | ||||
| @@ -2154,74 +2147,6 @@ static void block_dirty_bitmap_clear_commit(BlkActionState *common) | ||||
|     hbitmap_free(state->backup); | ||||
| } | ||||
|  | ||||
| static void block_dirty_bitmap_enable_prepare(BlkActionState *common, | ||||
|                                               Error **errp) | ||||
| { | ||||
|     BlockDirtyBitmap *action; | ||||
|     BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, | ||||
|                                              common, common); | ||||
|  | ||||
|     if (action_check_completion_mode(common, errp) < 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     action = common->action->u.x_block_dirty_bitmap_enable.data; | ||||
|     state->bitmap = block_dirty_bitmap_lookup(action->node, | ||||
|                                               action->name, | ||||
|                                               NULL, | ||||
|                                               errp); | ||||
|     if (!state->bitmap) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap); | ||||
|     bdrv_enable_dirty_bitmap(state->bitmap); | ||||
| } | ||||
|  | ||||
| static void block_dirty_bitmap_enable_abort(BlkActionState *common) | ||||
| { | ||||
|     BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, | ||||
|                                              common, common); | ||||
|  | ||||
|     if (!state->was_enabled) { | ||||
|         bdrv_disable_dirty_bitmap(state->bitmap); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void block_dirty_bitmap_disable_prepare(BlkActionState *common, | ||||
|                                                Error **errp) | ||||
| { | ||||
|     BlockDirtyBitmap *action; | ||||
|     BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, | ||||
|                                              common, common); | ||||
|  | ||||
|     if (action_check_completion_mode(common, errp) < 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     action = common->action->u.x_block_dirty_bitmap_disable.data; | ||||
|     state->bitmap = block_dirty_bitmap_lookup(action->node, | ||||
|                                               action->name, | ||||
|                                               NULL, | ||||
|                                               errp); | ||||
|     if (!state->bitmap) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap); | ||||
|     bdrv_disable_dirty_bitmap(state->bitmap); | ||||
| } | ||||
|  | ||||
| static void block_dirty_bitmap_disable_abort(BlkActionState *common) | ||||
| { | ||||
|     BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, | ||||
|                                              common, common); | ||||
|  | ||||
|     if (state->was_enabled) { | ||||
|         bdrv_enable_dirty_bitmap(state->bitmap); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void abort_prepare(BlkActionState *common, Error **errp) | ||||
| { | ||||
|     error_setg(errp, "Transaction aborted using Abort action"); | ||||
| @@ -2282,16 +2207,6 @@ static const BlkActionOps actions[] = { | ||||
|         .prepare = block_dirty_bitmap_clear_prepare, | ||||
|         .commit = block_dirty_bitmap_clear_commit, | ||||
|         .abort = block_dirty_bitmap_clear_abort, | ||||
|     }, | ||||
|     [TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_ENABLE] = { | ||||
|         .instance_size = sizeof(BlockDirtyBitmapState), | ||||
|         .prepare = block_dirty_bitmap_enable_prepare, | ||||
|         .abort = block_dirty_bitmap_enable_abort, | ||||
|     }, | ||||
|     [TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_DISABLE] = { | ||||
|         .instance_size = sizeof(BlockDirtyBitmapState), | ||||
|         .prepare = block_dirty_bitmap_disable_prepare, | ||||
|         .abort = block_dirty_bitmap_disable_abort, | ||||
|     } | ||||
| }; | ||||
|  | ||||
| @@ -2324,7 +2239,7 @@ void qmp_transaction(TransactionActionList *dev_list, | ||||
|                      Error **errp) | ||||
| { | ||||
|     TransactionActionList *dev_entry = dev_list; | ||||
|     JobTxn *block_job_txn = NULL; | ||||
|     BlockJobTxn *block_job_txn = NULL; | ||||
|     BlkActionState *state, *next; | ||||
|     Error *local_err = NULL; | ||||
|  | ||||
| @@ -2332,11 +2247,11 @@ void qmp_transaction(TransactionActionList *dev_list, | ||||
|     QSIMPLEQ_INIT(&snap_bdrv_states); | ||||
|  | ||||
|     /* Does this transaction get canceled as a group on failure? | ||||
|      * If not, we don't really need to make a JobTxn. | ||||
|      * If not, we don't really need to make a BlockJobTxn. | ||||
|      */ | ||||
|     props = get_transaction_properties(props); | ||||
|     if (props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) { | ||||
|         block_job_txn = job_txn_new(); | ||||
|         block_job_txn = block_job_txn_new(); | ||||
|     } | ||||
|  | ||||
|     /* drain all i/o before any operations */ | ||||
| @@ -2395,7 +2310,7 @@ exit: | ||||
|     if (!has_props) { | ||||
|         qapi_free_TransactionProperties(props); | ||||
|     } | ||||
|     job_txn_unref(block_job_txn); | ||||
|     block_job_txn_unref(block_job_txn); | ||||
| } | ||||
|  | ||||
| void qmp_eject(bool has_device, const char *device, | ||||
| @@ -2882,7 +2797,6 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, | ||||
|                                 bool has_granularity, uint32_t granularity, | ||||
|                                 bool has_persistent, bool persistent, | ||||
|                                 bool has_autoload, bool autoload, | ||||
|                                 bool has_disabled, bool disabled, | ||||
|                                 Error **errp) | ||||
| { | ||||
|     BlockDriverState *bs; | ||||
| @@ -2917,10 +2831,6 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, | ||||
|         warn_report("Autoload option is deprecated and its value is ignored"); | ||||
|     } | ||||
|  | ||||
|     if (!has_disabled) { | ||||
|         disabled = false; | ||||
|     } | ||||
|  | ||||
|     if (persistent && | ||||
|         !bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) | ||||
|     { | ||||
| @@ -2932,10 +2842,6 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (disabled) { | ||||
|         bdrv_disable_dirty_bitmap(bitmap); | ||||
|     } | ||||
|  | ||||
|     bdrv_dirty_bitmap_set_persistance(bitmap, persistent); | ||||
| } | ||||
|  | ||||
| @@ -2956,11 +2862,6 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name, | ||||
|                    "Bitmap '%s' is currently frozen and cannot be removed", | ||||
|                    name); | ||||
|         return; | ||||
|     } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) { | ||||
|         error_setg(errp, | ||||
|                    "Bitmap '%s' is currently locked and cannot be removed", | ||||
|                    name); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (bdrv_dirty_bitmap_get_persistance(bitmap)) { | ||||
| @@ -2971,6 +2872,7 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     bdrv_dirty_bitmap_make_anon(bitmap); | ||||
|     bdrv_release_dirty_bitmap(bs, bitmap); | ||||
| } | ||||
|  | ||||
| @@ -2994,11 +2896,6 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name, | ||||
|                    "Bitmap '%s' is currently frozen and cannot be modified", | ||||
|                    name); | ||||
|         return; | ||||
|     } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) { | ||||
|         error_setg(errp, | ||||
|                    "Bitmap '%s' is currently locked and cannot be modified", | ||||
|                    name); | ||||
|         return; | ||||
|     } else if (!bdrv_dirty_bitmap_enabled(bitmap)) { | ||||
|         error_setg(errp, | ||||
|                    "Bitmap '%s' is currently disabled and cannot be cleared", | ||||
| @@ -3012,78 +2909,6 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name, | ||||
|     bdrv_clear_dirty_bitmap(bitmap, NULL); | ||||
| } | ||||
|  | ||||
| void qmp_x_block_dirty_bitmap_enable(const char *node, const char *name, | ||||
|                                    Error **errp) | ||||
| { | ||||
|     BlockDriverState *bs; | ||||
|     BdrvDirtyBitmap *bitmap; | ||||
|  | ||||
|     bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); | ||||
|     if (!bitmap) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (bdrv_dirty_bitmap_frozen(bitmap)) { | ||||
|         error_setg(errp, | ||||
|                    "Bitmap '%s' is currently frozen and cannot be enabled", | ||||
|                    name); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     bdrv_enable_dirty_bitmap(bitmap); | ||||
| } | ||||
|  | ||||
| void qmp_x_block_dirty_bitmap_disable(const char *node, const char *name, | ||||
|                                     Error **errp) | ||||
| { | ||||
|     BlockDriverState *bs; | ||||
|     BdrvDirtyBitmap *bitmap; | ||||
|  | ||||
|     bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); | ||||
|     if (!bitmap) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (bdrv_dirty_bitmap_frozen(bitmap)) { | ||||
|         error_setg(errp, | ||||
|                    "Bitmap '%s' is currently frozen and cannot be disabled", | ||||
|                    name); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     bdrv_disable_dirty_bitmap(bitmap); | ||||
| } | ||||
|  | ||||
| void qmp_x_block_dirty_bitmap_merge(const char *node, const char *dst_name, | ||||
|                                     const char *src_name, Error **errp) | ||||
| { | ||||
|     BlockDriverState *bs; | ||||
|     BdrvDirtyBitmap *dst, *src; | ||||
|  | ||||
|     dst = block_dirty_bitmap_lookup(node, dst_name, &bs, errp); | ||||
|     if (!dst) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (bdrv_dirty_bitmap_frozen(dst)) { | ||||
|         error_setg(errp, "Bitmap '%s' is frozen and cannot be modified", | ||||
|                    dst_name); | ||||
|         return; | ||||
|     } else if (bdrv_dirty_bitmap_readonly(dst)) { | ||||
|         error_setg(errp, "Bitmap '%s' is readonly and cannot be modified", | ||||
|                    dst_name); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     src = bdrv_find_dirty_bitmap(bs, src_name); | ||||
|     if (!src) { | ||||
|         error_setg(errp, "Dirty bitmap '%s' not found", src_name); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     bdrv_merge_dirty_bitmap(dst, src, errp); | ||||
| } | ||||
|  | ||||
| BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, | ||||
|                                                               const char *name, | ||||
|                                                               Error **errp) | ||||
| @@ -3405,7 +3230,7 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, | ||||
|             goto out; | ||||
|         } | ||||
|         commit_active_start(has_job_id ? job_id : NULL, bs, base_bs, | ||||
|                             JOB_DEFAULT, speed, on_error, | ||||
|                             BLOCK_JOB_DEFAULT, speed, on_error, | ||||
|                             filter_node_name, NULL, NULL, false, &local_err); | ||||
|     } else { | ||||
|         BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs); | ||||
| @@ -3425,7 +3250,7 @@ out: | ||||
|     aio_context_release(aio_context); | ||||
| } | ||||
|  | ||||
| static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, | ||||
| static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, | ||||
|                                  Error **errp) | ||||
| { | ||||
|     BlockDriverState *bs; | ||||
| @@ -3436,7 +3261,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, | ||||
|     AioContext *aio_context; | ||||
|     QDict *options = NULL; | ||||
|     Error *local_err = NULL; | ||||
|     int flags, job_flags = JOB_DEFAULT; | ||||
|     int flags; | ||||
|     int64_t size; | ||||
|     bool set_backing_hd = false; | ||||
|  | ||||
| @@ -3455,12 +3280,6 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, | ||||
|     if (!backup->has_job_id) { | ||||
|         backup->job_id = NULL; | ||||
|     } | ||||
|     if (!backup->has_auto_finalize) { | ||||
|         backup->auto_finalize = true; | ||||
|     } | ||||
|     if (!backup->has_auto_dismiss) { | ||||
|         backup->auto_dismiss = true; | ||||
|     } | ||||
|     if (!backup->has_compress) { | ||||
|         backup->compress = false; | ||||
|     } | ||||
| @@ -3551,24 +3370,12 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, | ||||
|             bdrv_unref(target_bs); | ||||
|             goto out; | ||||
|         } | ||||
|         if (bdrv_dirty_bitmap_qmp_locked(bmap)) { | ||||
|             error_setg(errp, | ||||
|                        "Bitmap '%s' is currently locked and cannot be used for " | ||||
|                        "backup", backup->bitmap); | ||||
|             goto out; | ||||
|         } | ||||
|     } | ||||
|     if (!backup->auto_finalize) { | ||||
|         job_flags |= JOB_MANUAL_FINALIZE; | ||||
|     } | ||||
|     if (!backup->auto_dismiss) { | ||||
|         job_flags |= JOB_MANUAL_DISMISS; | ||||
|     } | ||||
|  | ||||
|     job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, | ||||
|                             backup->sync, bmap, backup->compress, | ||||
|                             backup->on_source_error, backup->on_target_error, | ||||
|                             job_flags, NULL, NULL, txn, &local_err); | ||||
|                             BLOCK_JOB_DEFAULT, NULL, NULL, txn, &local_err); | ||||
|     bdrv_unref(target_bs); | ||||
|     if (local_err != NULL) { | ||||
|         error_propagate(errp, local_err); | ||||
| @@ -3586,7 +3393,7 @@ void qmp_drive_backup(DriveBackup *arg, Error **errp) | ||||
|     BlockJob *job; | ||||
|     job = do_drive_backup(arg, NULL, errp); | ||||
|     if (job) { | ||||
|         job_start(&job->job); | ||||
|         block_job_start(job); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -3595,7 +3402,7 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) | ||||
|     return bdrv_named_nodes_list(errp); | ||||
| } | ||||
|  | ||||
| BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, | ||||
| BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, | ||||
|                              Error **errp) | ||||
| { | ||||
|     BlockDriverState *bs; | ||||
| @@ -3603,7 +3410,6 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, | ||||
|     Error *local_err = NULL; | ||||
|     AioContext *aio_context; | ||||
|     BlockJob *job = NULL; | ||||
|     int job_flags = JOB_DEFAULT; | ||||
|  | ||||
|     if (!backup->has_speed) { | ||||
|         backup->speed = 0; | ||||
| @@ -3617,17 +3423,11 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, | ||||
|     if (!backup->has_job_id) { | ||||
|         backup->job_id = NULL; | ||||
|     } | ||||
|     if (!backup->has_auto_finalize) { | ||||
|         backup->auto_finalize = true; | ||||
|     } | ||||
|     if (!backup->has_auto_dismiss) { | ||||
|         backup->auto_dismiss = true; | ||||
|     } | ||||
|     if (!backup->has_compress) { | ||||
|         backup->compress = false; | ||||
|     } | ||||
|  | ||||
|     bs = bdrv_lookup_bs(backup->device, backup->device, errp); | ||||
|     bs = qmp_get_root_bs(backup->device, errp); | ||||
|     if (!bs) { | ||||
|         return NULL; | ||||
|     } | ||||
| @@ -3651,16 +3451,10 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, | ||||
|             goto out; | ||||
|         } | ||||
|     } | ||||
|     if (!backup->auto_finalize) { | ||||
|         job_flags |= JOB_MANUAL_FINALIZE; | ||||
|     } | ||||
|     if (!backup->auto_dismiss) { | ||||
|         job_flags |= JOB_MANUAL_DISMISS; | ||||
|     } | ||||
|     job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, | ||||
|                             backup->sync, NULL, backup->compress, | ||||
|                             backup->on_source_error, backup->on_target_error, | ||||
|                             job_flags, NULL, NULL, txn, &local_err); | ||||
|                             BLOCK_JOB_DEFAULT, NULL, NULL, txn, &local_err); | ||||
|     if (local_err != NULL) { | ||||
|         error_propagate(errp, local_err); | ||||
|     } | ||||
| @@ -3674,7 +3468,7 @@ void qmp_blockdev_backup(BlockdevBackup *arg, Error **errp) | ||||
|     BlockJob *job; | ||||
|     job = do_blockdev_backup(arg, NULL, errp); | ||||
|     if (job) { | ||||
|         job_start(&job->job); | ||||
|         block_job_start(job); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -3696,7 +3490,6 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, | ||||
|                                    bool has_unmap, bool unmap, | ||||
|                                    bool has_filter_node_name, | ||||
|                                    const char *filter_node_name, | ||||
|                                    bool has_copy_mode, MirrorCopyMode copy_mode, | ||||
|                                    Error **errp) | ||||
| { | ||||
|  | ||||
| @@ -3721,9 +3514,6 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, | ||||
|     if (!has_filter_node_name) { | ||||
|         filter_node_name = NULL; | ||||
|     } | ||||
|     if (!has_copy_mode) { | ||||
|         copy_mode = MIRROR_COPY_MODE_BACKGROUND; | ||||
|     } | ||||
|  | ||||
|     if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) { | ||||
|         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity", | ||||
| @@ -3754,7 +3544,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, | ||||
|                  has_replaces ? replaces : NULL, | ||||
|                  speed, granularity, buf_size, sync, backing_mode, | ||||
|                  on_source_error, on_target_error, unmap, filter_node_name, | ||||
|                  copy_mode, errp); | ||||
|                  errp); | ||||
| } | ||||
|  | ||||
| void qmp_drive_mirror(DriveMirror *arg, Error **errp) | ||||
| @@ -3900,7 +3690,6 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) | ||||
|                            arg->has_on_target_error, arg->on_target_error, | ||||
|                            arg->has_unmap, arg->unmap, | ||||
|                            false, NULL, | ||||
|                            arg->has_copy_mode, arg->copy_mode, | ||||
|                            &local_err); | ||||
|     bdrv_unref(target_bs); | ||||
|     error_propagate(errp, local_err); | ||||
| @@ -3921,7 +3710,6 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id, | ||||
|                          BlockdevOnError on_target_error, | ||||
|                          bool has_filter_node_name, | ||||
|                          const char *filter_node_name, | ||||
|                          bool has_copy_mode, MirrorCopyMode copy_mode, | ||||
|                          Error **errp) | ||||
| { | ||||
|     BlockDriverState *bs; | ||||
| @@ -3954,7 +3742,6 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id, | ||||
|                            has_on_target_error, on_target_error, | ||||
|                            true, true, | ||||
|                            has_filter_node_name, filter_node_name, | ||||
|                            has_copy_mode, copy_mode, | ||||
|                            &local_err); | ||||
|     error_propagate(errp, local_err); | ||||
|  | ||||
| @@ -4012,14 +3799,14 @@ void qmp_block_job_cancel(const char *device, | ||||
|         force = false; | ||||
|     } | ||||
|  | ||||
|     if (job_user_paused(&job->job) && !force) { | ||||
|     if (block_job_user_paused(job) && !force) { | ||||
|         error_setg(errp, "The block job for device '%s' is currently paused", | ||||
|                    device); | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     trace_qmp_block_job_cancel(job); | ||||
|     job_user_cancel(&job->job, force, errp); | ||||
|     block_job_cancel(job); | ||||
| out: | ||||
|     aio_context_release(aio_context); | ||||
| } | ||||
| @@ -4029,12 +3816,12 @@ void qmp_block_job_pause(const char *device, Error **errp) | ||||
|     AioContext *aio_context; | ||||
|     BlockJob *job = find_block_job(device, &aio_context, errp); | ||||
|  | ||||
|     if (!job) { | ||||
|     if (!job || block_job_user_paused(job)) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     trace_qmp_block_job_pause(job); | ||||
|     job_user_pause(&job->job, errp); | ||||
|     block_job_user_pause(job); | ||||
|     aio_context_release(aio_context); | ||||
| } | ||||
|  | ||||
| @@ -4043,12 +3830,12 @@ void qmp_block_job_resume(const char *device, Error **errp) | ||||
|     AioContext *aio_context; | ||||
|     BlockJob *job = find_block_job(device, &aio_context, errp); | ||||
|  | ||||
|     if (!job) { | ||||
|     if (!job || !block_job_user_paused(job)) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     trace_qmp_block_job_resume(job); | ||||
|     job_user_resume(&job->job, errp); | ||||
|     block_job_user_resume(job); | ||||
|     aio_context_release(aio_context); | ||||
| } | ||||
|  | ||||
| @@ -4062,37 +3849,7 @@ void qmp_block_job_complete(const char *device, Error **errp) | ||||
|     } | ||||
|  | ||||
|     trace_qmp_block_job_complete(job); | ||||
|     job_complete(&job->job, errp); | ||||
|     aio_context_release(aio_context); | ||||
| } | ||||
|  | ||||
| void qmp_block_job_finalize(const char *id, Error **errp) | ||||
| { | ||||
|     AioContext *aio_context; | ||||
|     BlockJob *job = find_block_job(id, &aio_context, errp); | ||||
|  | ||||
|     if (!job) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     trace_qmp_block_job_finalize(job); | ||||
|     job_finalize(&job->job, errp); | ||||
|     aio_context_release(aio_context); | ||||
| } | ||||
|  | ||||
| void qmp_block_job_dismiss(const char *id, Error **errp) | ||||
| { | ||||
|     AioContext *aio_context; | ||||
|     BlockJob *bjob = find_block_job(id, &aio_context, errp); | ||||
|     Job *job; | ||||
|  | ||||
|     if (!bjob) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     trace_qmp_block_job_dismiss(bjob); | ||||
|     job = &bjob->job; | ||||
|     job_dismiss(&job, errp); | ||||
|     block_job_complete(job, errp); | ||||
|     aio_context_release(aio_context); | ||||
| } | ||||
|  | ||||
| @@ -4192,7 +3949,7 @@ void hmp_drive_add_node(Monitor *mon, const char *optstr) | ||||
|     qdict = qemu_opts_to_qdict(opts, NULL); | ||||
|  | ||||
|     if (!qdict_get_try_str(qdict, "node-name")) { | ||||
|         qobject_unref(qdict); | ||||
|         QDECREF(qdict); | ||||
|         error_report("'node-name' needs to be specified"); | ||||
|         goto out; | ||||
|     } | ||||
| @@ -4215,6 +3972,7 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp) | ||||
|     QObject *obj; | ||||
|     Visitor *v = qobject_output_visitor_new(&obj); | ||||
|     QDict *qdict; | ||||
|     const QDictEntry *ent; | ||||
|     Error *local_err = NULL; | ||||
|  | ||||
|     visit_type_BlockdevOptions(v, NULL, &options, &local_err); | ||||
| @@ -4224,10 +3982,23 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp) | ||||
|     } | ||||
|  | ||||
|     visit_complete(v, &obj); | ||||
|     qdict = qobject_to(QDict, obj); | ||||
|     qdict = qobject_to_qdict(obj); | ||||
|  | ||||
|     qdict_flatten(qdict); | ||||
|  | ||||
|     /* | ||||
|      * Rewrite "backing": null to "backing": "" | ||||
|      * TODO Rewrite "" to null instead, and perhaps not even here | ||||
|      */ | ||||
|     for (ent = qdict_first(qdict); ent; ent = qdict_next(qdict, ent)) { | ||||
|         char *dot = strrchr(ent->key, '.'); | ||||
|  | ||||
|         if (!strcmp(dot ? dot + 1 : ent->key, "backing") | ||||
|             && qobject_type(ent->value) == QTYPE_QNULL) { | ||||
|             qdict_put(qdict, ent->key, qstring_new()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (!qdict_get_try_str(qdict, "node-name")) { | ||||
|         error_setg(errp, "'node-name' must be specified for the root node"); | ||||
|         goto fail; | ||||
| @@ -4409,49 +4180,6 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread, | ||||
|     aio_context_release(old_context); | ||||
| } | ||||
|  | ||||
| void qmp_x_block_latency_histogram_set( | ||||
|     const char *device, | ||||
|     bool has_boundaries, uint64List *boundaries, | ||||
|     bool has_boundaries_read, uint64List *boundaries_read, | ||||
|     bool has_boundaries_write, uint64List *boundaries_write, | ||||
|     bool has_boundaries_flush, uint64List *boundaries_flush, | ||||
|     Error **errp) | ||||
| { | ||||
|     BlockBackend *blk = blk_by_name(device); | ||||
|     BlockAcctStats *stats; | ||||
|  | ||||
|     if (!blk) { | ||||
|         error_setg(errp, "Device '%s' not found", device); | ||||
|         return; | ||||
|     } | ||||
|     stats = blk_get_stats(blk); | ||||
|  | ||||
|     if (!has_boundaries && !has_boundaries_read && !has_boundaries_write && | ||||
|         !has_boundaries_flush) | ||||
|     { | ||||
|         block_latency_histograms_clear(stats); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (has_boundaries || has_boundaries_read) { | ||||
|         block_latency_histogram_set( | ||||
|             stats, BLOCK_ACCT_READ, | ||||
|             has_boundaries_read ? boundaries_read : boundaries); | ||||
|     } | ||||
|  | ||||
|     if (has_boundaries || has_boundaries_write) { | ||||
|         block_latency_histogram_set( | ||||
|             stats, BLOCK_ACCT_WRITE, | ||||
|             has_boundaries_write ? boundaries_write : boundaries); | ||||
|     } | ||||
|  | ||||
|     if (has_boundaries || has_boundaries_flush) { | ||||
|         block_latency_histogram_set( | ||||
|             stats, BLOCK_ACCT_FLUSH, | ||||
|             has_boundaries_flush ? boundaries_flush : boundaries); | ||||
|     } | ||||
| } | ||||
|  | ||||
| QemuOptsList qemu_common_drive_opts = { | ||||
|     .name = "drive", | ||||
|     .head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head), | ||||
|   | ||||
							
								
								
									
										878
									
								
								blockjob.c
									
									
									
									
									
								
							
							
						
						
									
										878
									
								
								blockjob.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -17,7 +17,6 @@ | ||||
|  *  along with this program; if not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu/units.h" | ||||
| #include "qemu-version.h" | ||||
| #include <machine/trap.h> | ||||
|  | ||||
| @@ -650,7 +649,7 @@ void cpu_loop(CPUSPARCState *env) | ||||
|  | ||||
| static void usage(void) | ||||
| { | ||||
|     printf("qemu-" TARGET_NAME " version " QEMU_FULL_VERSION | ||||
|     printf("qemu-" TARGET_NAME " version " QEMU_VERSION QEMU_PKGVERSION | ||||
|            "\n" QEMU_COPYRIGHT "\n" | ||||
|            "usage: qemu-" TARGET_NAME " [options] program [arguments...]\n" | ||||
|            "BSD CPU emulator (compiled for %s emulation)\n" | ||||
| @@ -724,7 +723,6 @@ int main(int argc, char **argv) | ||||
| { | ||||
|     const char *filename; | ||||
|     const char *cpu_model; | ||||
|     const char *cpu_type; | ||||
|     const char *log_file = NULL; | ||||
|     const char *log_mask = NULL; | ||||
|     struct target_pt_regs regs1, *regs = ®s1; | ||||
| @@ -796,9 +794,9 @@ int main(int argc, char **argv) | ||||
|             if (x86_stack_size <= 0) | ||||
|                 usage(); | ||||
|             if (*r == 'M') | ||||
|                 x86_stack_size *= MiB; | ||||
|                 x86_stack_size *= 1024 * 1024; | ||||
|             else if (*r == 'k' || *r == 'K') | ||||
|                 x86_stack_size *= KiB; | ||||
|                 x86_stack_size *= 1024; | ||||
|         } else if (!strcmp(r, "L")) { | ||||
|             interp_prefix = argv[optind++]; | ||||
|         } else if (!strcmp(r, "p")) { | ||||
| @@ -899,12 +897,10 @@ int main(int argc, char **argv) | ||||
|         cpu_model = "any"; | ||||
| #endif | ||||
|     } | ||||
|  | ||||
|     /* init tcg before creating CPUs and to get qemu_host_page_size */ | ||||
|     tcg_exec_init(0); | ||||
|  | ||||
|     cpu_type = parse_cpu_model(cpu_model); | ||||
|     cpu = cpu_create(cpu_type); | ||||
|     /* NOTE: we need to init the CPU at this stage to get | ||||
|        qemu_host_page_size */ | ||||
|     cpu = cpu_init(cpu_model); | ||||
|     env = cpu->env_ptr; | ||||
| #if defined(TARGET_SPARC) || defined(TARGET_PPC) | ||||
|     cpu_reset(cpu); | ||||
| @@ -919,7 +915,7 @@ int main(int argc, char **argv) | ||||
|     envlist_free(envlist); | ||||
|  | ||||
|     /* | ||||
|      * Now that page sizes are configured in tcg_exec_init() we can do | ||||
|      * Now that page sizes are configured in cpu_init() we can do | ||||
|      * proper page alignment for guest_base. | ||||
|      */ | ||||
|     guest_base = HOST_PAGE_ALIGN(guest_base); | ||||
|   | ||||
| @@ -21,7 +21,6 @@ | ||||
| #include "qemu.h" | ||||
| #include "qemu-common.h" | ||||
| #include "bsd-mman.h" | ||||
| #include "exec/exec-all.h" | ||||
|  | ||||
| //#define DEBUG_MMAP | ||||
|  | ||||
|   | ||||
| @@ -19,6 +19,7 @@ | ||||
|  | ||||
|  | ||||
| #include "cpu.h" | ||||
| #include "exec/exec-all.h" | ||||
| #include "exec/cpu_ldst.h" | ||||
|  | ||||
| #undef DEBUG_REMAP | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user