Compare commits
	
		
			5 Commits
		
	
	
		
			qemu-openb
			...
			rm-protoco
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 6321d78dc5 | ||
|  | a0974f99aa | ||
|  | 42883c01bf | ||
|  | 7966c2b312 | ||
|  | 5888011244 | 
							
								
								
									
										16
									
								
								.cirrus.yml
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								.cirrus.yml
									
									
									
									
									
								
							| @@ -1,16 +0,0 @@ | |||||||
| freebsd_12_task: |  | ||||||
|   freebsd_instance: |  | ||||||
|     image: freebsd-12-0-release-amd64 |  | ||||||
|     cpu: 8 |  | ||||||
|     memory: 8G |  | ||||||
|   env: |  | ||||||
|     CIRRUS_CLONE_DEPTH: 1 |  | ||||||
|   install_script: pkg install -y |  | ||||||
|     bison curl cyrus-sasl git glib gmake gnutls |  | ||||||
|     nettle perl5 pixman pkgconf png usbredir |  | ||||||
|   script: |  | ||||||
|     - mkdir build |  | ||||||
|     - cd build |  | ||||||
|     - ../configure || { cat config.log; exit 1; } |  | ||||||
|     - gmake -j8 |  | ||||||
|     - gmake -j8 V=1 check |  | ||||||
| @@ -1,10 +1,4 @@ | |||||||
| # EditorConfig is a file format and collection of text editor plugins | # http://editorconfig.org | ||||||
| # for maintaining consistent coding styles between different editors |  | ||||||
| # and IDEs. Most popular editors support this either natively or via |  | ||||||
| # plugin. |  | ||||||
| # |  | ||||||
| # Check https://editorconfig.org for details. |  | ||||||
|  |  | ||||||
| root = true | root = true | ||||||
|  |  | ||||||
| [*] | [*] | ||||||
| @@ -12,23 +6,10 @@ end_of_line = lf | |||||||
| insert_final_newline = true | insert_final_newline = true | ||||||
| charset = utf-8 | charset = utf-8 | ||||||
|  |  | ||||||
| [*.mak] |  | ||||||
| indent_style = tab |  | ||||||
| indent_size = 8 |  | ||||||
| file_type_emacs = makefile |  | ||||||
|  |  | ||||||
| [Makefile*] | [Makefile*] | ||||||
| indent_style = tab | indent_style = tab | ||||||
| indent_size = 8 | indent_size = 8 | ||||||
| file_type_emacs = makefile |  | ||||||
|  |  | ||||||
| [*.{c,h}] | [*.{c,h}] | ||||||
| indent_style = space | indent_style = space | ||||||
| indent_size = 4 | indent_size = 4 | ||||||
|  |  | ||||||
| [*.{vert,frag}] |  | ||||||
| file_type_emacs = glsl |  | ||||||
|  |  | ||||||
| [*.json] |  | ||||||
| indent_style = space |  | ||||||
| file_type_emacs = python |  | ||||||
|   | |||||||
							
								
								
									
										75
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										75
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -30,20 +30,79 @@ | |||||||
| /qapi-gen-timestamp | /qapi-gen-timestamp | ||||||
| /qapi/qapi-builtin-types.[ch] | /qapi/qapi-builtin-types.[ch] | ||||||
| /qapi/qapi-builtin-visit.[ch] | /qapi/qapi-builtin-visit.[ch] | ||||||
| /qapi/qapi-commands-*.[ch] | /qapi/qapi-commands-block-core.[ch] | ||||||
|  | /qapi/qapi-commands-block.[ch] | ||||||
|  | /qapi/qapi-commands-char.[ch] | ||||||
|  | /qapi/qapi-commands-common.[ch] | ||||||
|  | /qapi/qapi-commands-crypto.[ch] | ||||||
|  | /qapi/qapi-commands-introspect.[ch] | ||||||
|  | /qapi/qapi-commands-migration.[ch] | ||||||
|  | /qapi/qapi-commands-misc.[ch] | ||||||
|  | /qapi/qapi-commands-net.[ch] | ||||||
|  | /qapi/qapi-commands-rocker.[ch] | ||||||
|  | /qapi/qapi-commands-run-state.[ch] | ||||||
|  | /qapi/qapi-commands-sockets.[ch] | ||||||
|  | /qapi/qapi-commands-tpm.[ch] | ||||||
|  | /qapi/qapi-commands-trace.[ch] | ||||||
|  | /qapi/qapi-commands-transaction.[ch] | ||||||
|  | /qapi/qapi-commands-ui.[ch] | ||||||
| /qapi/qapi-commands.[ch] | /qapi/qapi-commands.[ch] | ||||||
| /qapi/qapi-events-*.[ch] | /qapi/qapi-events-block-core.[ch] | ||||||
|  | /qapi/qapi-events-block.[ch] | ||||||
|  | /qapi/qapi-events-char.[ch] | ||||||
|  | /qapi/qapi-events-common.[ch] | ||||||
|  | /qapi/qapi-events-crypto.[ch] | ||||||
|  | /qapi/qapi-events-introspect.[ch] | ||||||
|  | /qapi/qapi-events-migration.[ch] | ||||||
|  | /qapi/qapi-events-misc.[ch] | ||||||
|  | /qapi/qapi-events-net.[ch] | ||||||
|  | /qapi/qapi-events-rocker.[ch] | ||||||
|  | /qapi/qapi-events-run-state.[ch] | ||||||
|  | /qapi/qapi-events-sockets.[ch] | ||||||
|  | /qapi/qapi-events-tpm.[ch] | ||||||
|  | /qapi/qapi-events-trace.[ch] | ||||||
|  | /qapi/qapi-events-transaction.[ch] | ||||||
|  | /qapi/qapi-events-ui.[ch] | ||||||
| /qapi/qapi-events.[ch] | /qapi/qapi-events.[ch] | ||||||
| /qapi/qapi-introspect.[ch] | /qapi/qapi-introspect.[ch] | ||||||
| /qapi/qapi-types-*.[ch] | /qapi/qapi-types-block-core.[ch] | ||||||
|  | /qapi/qapi-types-block.[ch] | ||||||
|  | /qapi/qapi-types-char.[ch] | ||||||
|  | /qapi/qapi-types-common.[ch] | ||||||
|  | /qapi/qapi-types-crypto.[ch] | ||||||
|  | /qapi/qapi-types-introspect.[ch] | ||||||
|  | /qapi/qapi-types-migration.[ch] | ||||||
|  | /qapi/qapi-types-misc.[ch] | ||||||
|  | /qapi/qapi-types-net.[ch] | ||||||
|  | /qapi/qapi-types-rocker.[ch] | ||||||
|  | /qapi/qapi-types-run-state.[ch] | ||||||
|  | /qapi/qapi-types-sockets.[ch] | ||||||
|  | /qapi/qapi-types-tpm.[ch] | ||||||
|  | /qapi/qapi-types-trace.[ch] | ||||||
|  | /qapi/qapi-types-transaction.[ch] | ||||||
|  | /qapi/qapi-types-ui.[ch] | ||||||
| /qapi/qapi-types.[ch] | /qapi/qapi-types.[ch] | ||||||
| /qapi/qapi-visit-*.[ch] | /qapi/qapi-visit-block-core.[ch] | ||||||
|  | /qapi/qapi-visit-block.[ch] | ||||||
|  | /qapi/qapi-visit-char.[ch] | ||||||
|  | /qapi/qapi-visit-common.[ch] | ||||||
|  | /qapi/qapi-visit-crypto.[ch] | ||||||
|  | /qapi/qapi-visit-introspect.[ch] | ||||||
|  | /qapi/qapi-visit-migration.[ch] | ||||||
|  | /qapi/qapi-visit-misc.[ch] | ||||||
|  | /qapi/qapi-visit-net.[ch] | ||||||
|  | /qapi/qapi-visit-rocker.[ch] | ||||||
|  | /qapi/qapi-visit-run-state.[ch] | ||||||
|  | /qapi/qapi-visit-sockets.[ch] | ||||||
|  | /qapi/qapi-visit-tpm.[ch] | ||||||
|  | /qapi/qapi-visit-trace.[ch] | ||||||
|  | /qapi/qapi-visit-transaction.[ch] | ||||||
|  | /qapi/qapi-visit-ui.[ch] | ||||||
| /qapi/qapi-visit.[ch] | /qapi/qapi-visit.[ch] | ||||||
| /qapi/qapi-doc.texi | /qapi/qapi-doc.texi | ||||||
| /qemu-doc.html | /qemu-doc.html | ||||||
| /qemu-doc.info | /qemu-doc.info | ||||||
| /qemu-doc.txt | /qemu-doc.txt | ||||||
| /qemu-edid |  | ||||||
| /qemu-img | /qemu-img | ||||||
| /qemu-nbd | /qemu-nbd | ||||||
| /qemu-options.def | /qemu-options.def | ||||||
| @@ -92,7 +151,6 @@ | |||||||
| .sdk | .sdk | ||||||
| *.gcda | *.gcda | ||||||
| *.gcno | *.gcno | ||||||
| *.gcov |  | ||||||
| /pc-bios/bios-pq/status | /pc-bios/bios-pq/status | ||||||
| /pc-bios/vgabios-pq/status | /pc-bios/vgabios-pq/status | ||||||
| /pc-bios/optionrom/linuxboot.asm | /pc-bios/optionrom/linuxboot.asm | ||||||
| @@ -103,10 +161,6 @@ | |||||||
| /pc-bios/optionrom/linuxboot_dma.bin | /pc-bios/optionrom/linuxboot_dma.bin | ||||||
| /pc-bios/optionrom/linuxboot_dma.raw | /pc-bios/optionrom/linuxboot_dma.raw | ||||||
| /pc-bios/optionrom/linuxboot_dma.img | /pc-bios/optionrom/linuxboot_dma.img | ||||||
| /pc-bios/optionrom/pvh.asm |  | ||||||
| /pc-bios/optionrom/pvh.bin |  | ||||||
| /pc-bios/optionrom/pvh.raw |  | ||||||
| /pc-bios/optionrom/pvh.img |  | ||||||
| /pc-bios/optionrom/multiboot.asm | /pc-bios/optionrom/multiboot.asm | ||||||
| /pc-bios/optionrom/multiboot.bin | /pc-bios/optionrom/multiboot.bin | ||||||
| /pc-bios/optionrom/multiboot.raw | /pc-bios/optionrom/multiboot.raw | ||||||
| @@ -152,4 +206,3 @@ trace-dtrace-root.h | |||||||
| trace-dtrace-root.dtrace | trace-dtrace-root.dtrace | ||||||
| trace-ust-all.h | trace-ust-all.h | ||||||
| trace-ust-all.c | trace-ust-all.c | ||||||
| /target/arm/decode-sve.inc.c |  | ||||||
|   | |||||||
							
								
								
									
										39
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										39
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @@ -1,51 +1,48 @@ | |||||||
|  | [submodule "roms/vgabios"] | ||||||
|  | 	path = roms/vgabios | ||||||
|  | 	url = git://git.qemu-project.org/vgabios.git/ | ||||||
| [submodule "roms/seabios"] | [submodule "roms/seabios"] | ||||||
| 	path = roms/seabios | 	path = roms/seabios | ||||||
| 	url = https://git.qemu.org/git/seabios.git/ | 	url = git://git.qemu-project.org/seabios.git/ | ||||||
| [submodule "roms/SLOF"] | [submodule "roms/SLOF"] | ||||||
| 	path = roms/SLOF | 	path = roms/SLOF | ||||||
| 	url = https://git.qemu.org/git/SLOF.git | 	url = git://git.qemu-project.org/SLOF.git | ||||||
| [submodule "roms/ipxe"] | [submodule "roms/ipxe"] | ||||||
| 	path = roms/ipxe | 	path = roms/ipxe | ||||||
| 	url = https://git.qemu.org/git/ipxe.git | 	url = git://git.qemu-project.org/ipxe.git | ||||||
| [submodule "roms/openbios"] | [submodule "roms/openbios"] | ||||||
| 	path = roms/openbios | 	path = roms/openbios | ||||||
| 	url = https://git.qemu.org/git/openbios.git | 	url = git://git.qemu-project.org/openbios.git | ||||||
| [submodule "roms/openhackware"] | [submodule "roms/openhackware"] | ||||||
| 	path = roms/openhackware | 	path = roms/openhackware | ||||||
| 	url = https://git.qemu.org/git/openhackware.git | 	url = git://git.qemu-project.org/openhackware.git | ||||||
| [submodule "roms/qemu-palcode"] | [submodule "roms/qemu-palcode"] | ||||||
| 	path = roms/qemu-palcode | 	path = roms/qemu-palcode | ||||||
| 	url = https://git.qemu.org/git/qemu-palcode.git | 	url = git://github.com/rth7680/qemu-palcode.git | ||||||
| [submodule "roms/sgabios"] | [submodule "roms/sgabios"] | ||||||
| 	path = roms/sgabios | 	path = roms/sgabios | ||||||
| 	url = https://git.qemu.org/git/sgabios.git | 	url = git://git.qemu-project.org/sgabios.git | ||||||
| [submodule "dtc"] | [submodule "dtc"] | ||||||
| 	path = dtc | 	path = dtc | ||||||
| 	url = https://git.qemu.org/git/dtc.git | 	url = git://git.qemu-project.org/dtc.git | ||||||
| [submodule "roms/u-boot"] | [submodule "roms/u-boot"] | ||||||
| 	path = roms/u-boot | 	path = roms/u-boot | ||||||
| 	url = https://git.qemu.org/git/u-boot.git | 	url = git://git.qemu-project.org/u-boot.git | ||||||
| [submodule "roms/skiboot"] | [submodule "roms/skiboot"] | ||||||
| 	path = roms/skiboot | 	path = roms/skiboot | ||||||
| 	url = https://git.qemu.org/git/skiboot.git | 	url = git://git.qemu.org/skiboot.git | ||||||
| [submodule "roms/QemuMacDrivers"] | [submodule "roms/QemuMacDrivers"] | ||||||
| 	path = roms/QemuMacDrivers | 	path = roms/QemuMacDrivers | ||||||
| 	url = https://git.qemu.org/git/QemuMacDrivers.git | 	url = git://git.qemu.org/QemuMacDrivers.git | ||||||
| [submodule "ui/keycodemapdb"] | [submodule "ui/keycodemapdb"] | ||||||
| 	path = ui/keycodemapdb | 	path = ui/keycodemapdb | ||||||
| 	url = https://git.qemu.org/git/keycodemapdb.git | 	url = git://git.qemu.org/keycodemapdb.git | ||||||
| [submodule "capstone"] | [submodule "capstone"] | ||||||
| 	path = capstone | 	path = capstone | ||||||
| 	url = https://git.qemu.org/git/capstone.git | 	url = git://git.qemu.org/capstone.git | ||||||
| [submodule "roms/seabios-hppa"] | [submodule "roms/seabios-hppa"] | ||||||
| 	path = roms/seabios-hppa | 	path = roms/seabios-hppa | ||||||
| 	url = https://github.com/hdeller/seabios-hppa.git | 	url = git://github.com/hdeller/seabios-hppa.git | ||||||
| [submodule "roms/u-boot-sam460ex"] | [submodule "roms/u-boot-sam460ex"] | ||||||
| 	path = roms/u-boot-sam460ex | 	path = roms/u-boot-sam460ex | ||||||
| 	url = https://git.qemu.org/git/u-boot-sam460ex.git | 	url = git://github.com/zbalaton/u-boot-sam460ex | ||||||
| [submodule "tests/fp/berkeley-testfloat-3"] |  | ||||||
| 	path = tests/fp/berkeley-testfloat-3 |  | ||||||
| 	url = https://github.com/cota/berkeley-testfloat-3 |  | ||||||
| [submodule "tests/fp/berkeley-softfloat-3"] |  | ||||||
| 	path = tests/fp/berkeley-softfloat-3 |  | ||||||
| 	url = https://github.com/cota/berkeley-softfloat-3 |  | ||||||
|   | |||||||
							
								
								
									
										29
									
								
								.mailmap
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								.mailmap
									
									
									
									
									
								
							| @@ -1,7 +1,6 @@ | |||||||
| # This mailmap fixes up author names/addresses. | # 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. | ||||||
| # The first section translates weird addresses from the original git import | # | ||||||
| # into proper addresses so that they are counted properly by git shortlog. |  | ||||||
| Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162> | 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> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
| Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com> | Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com> | ||||||
| @@ -12,28 +11,14 @@ Fabrice Bellard <fabrice@bellard.org> bellard <bellard@c046a42c-6fe2-441c-8c8c-7 | |||||||
| James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com> | James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com> | ||||||
| Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162> | Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
| Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162> | Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
| Yongbok Kim <yongbok.kim@mips.com> <yongbok.kim@imgtec.com> | Paul Burton <paul.burton@mips.com> <paul.burton@imgtec.com> | ||||||
| Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@mips.com> | Paul Burton <paul.burton@mips.com> <paul@archlinuxmips.org> | ||||||
| Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@imgtec.com> |  | ||||||
| Paul Burton <pburton@wavecomp.com> <paul.burton@mips.com> |  | ||||||
| Paul Burton <pburton@wavecomp.com> <paul.burton@imgtec.com> |  | ||||||
| Paul Burton <pburton@wavecomp.com> <paul@archlinuxmips.org> |  | ||||||
| Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162> | Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
| malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162> | malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
|  |  | ||||||
| # There is also a: | # There is also a: | ||||||
| #    (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162> | #    (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
| # for the cvs2svn initialization commit e63c3dc74bf. | # 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 | # Also list preferred name forms where people have changed their | ||||||
| # git author config, or had utf8/latin1 encoding issues. | # git author config | ||||||
| Daniel P. Berrangé <berrange@redhat.com> | Daniel P. Berrangé <berrange@redhat.com> | ||||||
| Reimar Döffinger <Reimar.Doeffinger@gmx.de> |  | ||||||
|   | |||||||
| @@ -7,11 +7,10 @@ env: | |||||||
|   matrix: |   matrix: | ||||||
|     - IMAGE=debian-amd64 |     - IMAGE=debian-amd64 | ||||||
|       TARGET_LIST=x86_64-softmmu,x86_64-linux-user |       TARGET_LIST=x86_64-softmmu,x86_64-linux-user | ||||||
|     # currently disabled as the mxe.cc repos are down |     - IMAGE=debian-win32-cross | ||||||
|     # - IMAGE=debian-win32-cross |       TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu | ||||||
|     #   TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu |     - IMAGE=debian-win64-cross | ||||||
|     # - IMAGE=debian-win64-cross |       TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu | ||||||
|     #   TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu |  | ||||||
|     - IMAGE=debian-armel-cross |     - IMAGE=debian-armel-cross | ||||||
|       TARGET_LIST=arm-softmmu,arm-linux-user,armeb-linux-user |       TARGET_LIST=arm-softmmu,arm-linux-user,armeb-linux-user | ||||||
|     - IMAGE=debian-armhf-cross |     - IMAGE=debian-armhf-cross | ||||||
| @@ -36,5 +35,13 @@ build: | |||||||
|     options: "-e HOME=/root" |     options: "-e HOME=/root" | ||||||
|   ci: |   ci: | ||||||
|     - unset CC |     - 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} |     - ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST} | ||||||
|     - make -j$(($(getconf _NPROCESSORS_ONLN) + 1)) |     - make -j$(($(getconf _NPROCESSORS_ONLN) + 1)) | ||||||
|   | |||||||
							
								
								
									
										267
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										267
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,13 +1,10 @@ | |||||||
| # The current Travis default is a VM based 16.04 Xenial on GCE | sudo: false | ||||||
| # Additional builds with specific requirements for a full VM need to |  | ||||||
| # be added as additional matrix: entries later on |  | ||||||
| dist: xenial |  | ||||||
| language: c | language: c | ||||||
|  | python: | ||||||
|  |   - "2.6" | ||||||
| compiler: | compiler: | ||||||
|   - gcc |   - gcc | ||||||
| cache: ccache | cache: ccache | ||||||
|  |  | ||||||
|  |  | ||||||
| addons: | addons: | ||||||
|   apt: |   apt: | ||||||
|     packages: |     packages: | ||||||
| @@ -34,15 +31,9 @@ addons: | |||||||
|       - libssh2-1-dev |       - libssh2-1-dev | ||||||
|       - liburcu-dev |       - liburcu-dev | ||||||
|       - libusb-1.0-0-dev |       - libusb-1.0-0-dev | ||||||
|       - libvte-2.91-dev |       - libvte-2.90-dev | ||||||
|       - sparse |       - sparse | ||||||
|       - uuid-dev |       - uuid-dev | ||||||
|       - gcovr |  | ||||||
|   homebrew: |  | ||||||
|     packages: |  | ||||||
|       - glib |  | ||||||
|       - pixman |  | ||||||
|  |  | ||||||
|  |  | ||||||
| # The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu | # The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu | ||||||
| # to prevent IRC notifications from forks. This was created using: | # to prevent IRC notifications from forks. This was created using: | ||||||
| @@ -53,169 +44,130 @@ notifications: | |||||||
|       - secure: "F7GDRgjuOo5IUyRLqSkmDL7kvdU4UcH3Lm/W2db2JnDHTGCqgEdaYEYKciyCLZ57vOTsTsOgesN8iUT7hNHBd1KWKjZe9KDTZWppWRYVwAwQMzVeSOsbbU4tRoJ6Pp+3qhH1Z0eGYR9ZgKYAoTumDFgSAYRp4IscKS8jkoedOqM=" |       - secure: "F7GDRgjuOo5IUyRLqSkmDL7kvdU4UcH3Lm/W2db2JnDHTGCqgEdaYEYKciyCLZ57vOTsTsOgesN8iUT7hNHBd1KWKjZe9KDTZWppWRYVwAwQMzVeSOsbbU4tRoJ6Pp+3qhH1Z0eGYR9ZgKYAoTumDFgSAYRp4IscKS8jkoedOqM=" | ||||||
|     on_success: change |     on_success: change | ||||||
|     on_failure: always |     on_failure: always | ||||||
|  |  | ||||||
|  |  | ||||||
| env: | env: | ||||||
|   global: |   global: | ||||||
|     - SRC_DIR="." |     - TEST_CMD="make check" | ||||||
|     - BUILD_DIR="." |     - MAKEFLAGS="-j3" | ||||||
|     - BASE_CONFIG="--disable-docs --disable-tools" |   matrix: | ||||||
|     - TEST_CMD="make check -j3 V=1" |     - CONFIG="" | ||||||
|  |     - CONFIG="--enable-debug --enable-debug-tcg --enable-trace-backends=log" | ||||||
|  |     - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb" | ||||||
|  |     - CONFIG="--enable-modules --disable-linux-user" | ||||||
|  |     - CONFIG="--with-coroutine=ucontext --disable-linux-user" | ||||||
|  |     - CONFIG="--with-coroutine=sigaltstack --disable-linux-user" | ||||||
| git: | git: | ||||||
|   # we want to do this ourselves |   # we want to do this ourselves | ||||||
|   submodules: false |   submodules: false | ||||||
|  | before_install: | ||||||
|  |   - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update ; fi | ||||||
|  |   - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install libffi gettext glib pixman ; fi | ||||||
|  |   - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||||
|  |   - git submodule update --init --recursive | ||||||
| before_script: | before_script: | ||||||
|   - mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR} |   - ./configure ${CONFIG} | ||||||
|   - ${SRC_DIR}/configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; } |  | ||||||
| script: | script: | ||||||
|   - make -j3 && ${TEST_CMD} |   - make ${MAKEFLAGS} && ${TEST_CMD} | ||||||
|  |  | ||||||
|  |  | ||||||
| matrix: | matrix: | ||||||
|   include: |   include: | ||||||
|     - env: |     # Test with CLang for compile portability | ||||||
|         - CONFIG="--disable-system" |     - env: CONFIG="" | ||||||
|  |  | ||||||
|  |  | ||||||
|     - env: |  | ||||||
|         - CONFIG="--disable-user" |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     - env: |  | ||||||
|         - CONFIG="--enable-debug --enable-debug-tcg" |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     - env: |  | ||||||
|         - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb --disable-user" |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     - env: |  | ||||||
|         - CONFIG="--enable-modules --disable-linux-user" |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     # Alternate coroutines implementations are only really of interest to KVM users |  | ||||||
|     # However we can't test against KVM on Travis so we can only run unit tests |  | ||||||
|     - env: |  | ||||||
|         - CONFIG="--with-coroutine=ucontext --disable-tcg" |  | ||||||
|         - TEST_CMD="make check-unit -j3 V=1" |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     - env: |  | ||||||
|         - CONFIG="--with-coroutine=sigaltstack --disable-tcg" |  | ||||||
|         - TEST_CMD="make check-unit -j3 V=1" |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     # Check we can build docs and tools |  | ||||||
|     - env: |  | ||||||
|         - BASE_CONFIG="--enable-tools --enable-docs" |  | ||||||
|         - CONFIG="--target-list=x86_64-softmmu,aarch64-linux-user" |  | ||||||
|       addons: |  | ||||||
|         apt: |  | ||||||
|           packages: |  | ||||||
|             - python-sphinx |  | ||||||
|             - texinfo |  | ||||||
|             - perl |  | ||||||
|  |  | ||||||
|     # 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 |       compiler: clang | ||||||
|  |  | ||||||
|  |  | ||||||
|     - env: |  | ||||||
|         - CONFIG="--disable-user" |  | ||||||
|       compiler: clang |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     # gprof/gcov are GCC features |     # gprof/gcov are GCC features | ||||||
|     - env: |     - env: CONFIG="--enable-gprof --enable-gcov --disable-pie" | ||||||
|         - 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" |       compiler: gcc | ||||||
|       after_success: |  | ||||||
|         - ${SRC_DIR}/scripts/travis/coverage-summary.sh |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     # We manually include builds which we disable "make check" for |     # We manually include builds which we disable "make check" for | ||||||
|     - env: |     - env: CONFIG="--enable-debug --enable-tcg-interpreter" | ||||||
|         - CONFIG="--enable-debug --enable-tcg-interpreter" |            TEST_CMD="" | ||||||
|         - TEST_CMD="" |       compiler: gcc | ||||||
|  |     - env: CONFIG="--enable-trace-backends=simple" | ||||||
|  |            TEST_CMD="" | ||||||
|     # We don't need to exercise every backend with every front-end |       compiler: gcc | ||||||
|     - env: |     - env: CONFIG="--enable-trace-backends=ftrace" | ||||||
|         - CONFIG="--enable-trace-backends=log,simple,syslog --disable-system" |            TEST_CMD="" | ||||||
|         - TEST_CMD="" |       compiler: gcc | ||||||
|  |     - env: CONFIG="--enable-trace-backends=ust" | ||||||
|  |            TEST_CMD="" | ||||||
|     - env: |       compiler: gcc | ||||||
|         - CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu" |     - env: CONFIG="--disable-tcg" | ||||||
|         - TEST_CMD="" |            TEST_CMD="" | ||||||
|  |       compiler: gcc | ||||||
|  |     - env: CONFIG="" | ||||||
|     - env: |  | ||||||
|         - CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu" |  | ||||||
|         - TEST_CMD="" |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     # MacOSX builds |  | ||||||
|     - env: |  | ||||||
|         - CONFIG="--target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu" |  | ||||||
|       os: osx |       os: osx | ||||||
|       osx_image: xcode9.4 |  | ||||||
|       compiler: clang |       compiler: clang | ||||||
|  |     # Plain Trusty System Build | ||||||
|  |     - env: CONFIG="--disable-linux-user" | ||||||
|     - env: |       sudo: required | ||||||
|         - CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu" |       addons: | ||||||
|       os: osx |       dist: trusty | ||||||
|       osx_image: xcode10 |       compiler: gcc | ||||||
|       compiler: clang |       before_install: | ||||||
|  |         - sudo apt-get update -qq | ||||||
|  |         - sudo apt-get build-dep -qq qemu | ||||||
|     # Python builds |         - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||||
|     - env: |         - git submodule update --init --recursive | ||||||
|         - CONFIG="--target-list=x86_64-softmmu" |     # Plain Trusty Linux User Build | ||||||
|       language: python |     - 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: |       python: | ||||||
|         - "3.4" |         - "3.0" | ||||||
|  |       env: | ||||||
|  |         - COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9 | ||||||
|     - env: |         - CONFIG="--disable-linux-user --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3" | ||||||
|         - CONFIG="--target-list=x86_64-softmmu" |       before_install: | ||||||
|       language: python |         - 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: |       python: | ||||||
|         - "3.6" |         - "3.6" | ||||||
|  |       env: | ||||||
|  |         - COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9 | ||||||
|     # Acceptance (Functional) tests |         - CONFIG="--disable-system --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3" | ||||||
|     - env: |       before_install: | ||||||
|         - CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu" |         - wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - | ||||||
|         - TEST_CMD="make AVOCADO_SHOW=app check-acceptance" |         - sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main' | ||||||
|       addons: |         - sudo apt-get update -qq | ||||||
|         apt: |         - sudo apt-get install -qq -y clang-3.9 | ||||||
|           packages: |         - sudo apt-get build-dep -qq qemu | ||||||
|             - python3-pip |         - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||||
|             - python3.5-venv |         - git submodule update --init --recursive | ||||||
|  |       before_script: | ||||||
|  |         - ./configure ${CONFIG} || cat config.log | ||||||
|     # Using newer GCC with sanitizers |     # Using newer GCC with sanitizers | ||||||
|     - addons: |     - addons: | ||||||
|         apt: |         apt: | ||||||
|           update: true |  | ||||||
|           sources: |           sources: | ||||||
|             # PPAs for newer toolchains |             # PPAs for newer toolchains | ||||||
|             - ubuntu-toolchain-r-test |             - ubuntu-toolchain-r-test | ||||||
|           packages: |           packages: | ||||||
|             # Extra toolchains |             # Extra toolchains | ||||||
|             - gcc-7 |             - gcc-5 | ||||||
|             - g++-7 |             - g++-5 | ||||||
|             # Build dependencies |             # Build dependencies | ||||||
|             - libaio-dev |             - libaio-dev | ||||||
|             - libattr1-dev |             - libattr1-dev | ||||||
| @@ -238,19 +190,14 @@ matrix: | |||||||
|             - libssh2-1-dev |             - libssh2-1-dev | ||||||
|             - liburcu-dev |             - liburcu-dev | ||||||
|             - libusb-1.0-0-dev |             - libusb-1.0-0-dev | ||||||
|             - libvte-2.91-dev |             - libvte-2.90-dev | ||||||
|             - sparse |             - sparse | ||||||
|             - uuid-dev |             - uuid-dev | ||||||
|       language: generic |       language: generic | ||||||
|       compiler: none |       compiler: none | ||||||
|       env: |       env: | ||||||
|         - COMPILER_NAME=gcc CXX=g++-7 CC=gcc-7 |         - COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5 | ||||||
|         - CONFIG="--cc=gcc-7 --cxx=g++-7 --disable-pie --disable-linux-user" |         - CONFIG="--cc=gcc-5 --cxx=g++-5 --disable-pie --disable-linux-user" | ||||||
|         - TEST_CMD="" |         - TEST_CMD="" | ||||||
|       before_script: |       before_script: | ||||||
|         - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || { cat config.log && exit 1; } |         - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || cat config.log | ||||||
|  |  | ||||||
|  |  | ||||||
|     - env: |  | ||||||
|         - CONFIG="--disable-system" |  | ||||||
|         - TEST_CMD="make -j3 check-tcg V=1" |  | ||||||
|   | |||||||
							
								
								
									
										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 | 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. | 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. trace-events style | ||||||
|  |  | ||||||
| 8.1 0x prefix | 8.1 0x prefix | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
| 		       Version 2.1, February 1999 | 		       Version 2.1, February 1999 | ||||||
|  |  | ||||||
|  Copyright (C) 1991, 1999 Free Software Foundation, Inc. |  Copyright (C) 1991, 1999 Free Software Foundation, Inc. | ||||||
|  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA | 	51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||||
|  Everyone is permitted to copy and distribute verbatim copies |  Everyone is permitted to copy and distribute verbatim copies | ||||||
|  of this license document, but changing it is not allowed. |  of this license document, but changing it is not allowed. | ||||||
|  |  | ||||||
| @@ -476,7 +476,7 @@ convey the exclusion of warranty; and each file should have at least the | |||||||
|     This library is free software; you can redistribute it and/or |     This library is free software; you can redistribute it and/or | ||||||
|     modify it under the terms of the GNU Lesser General Public |     modify it under the terms of the GNU Lesser General Public | ||||||
|     License as published by the Free Software Foundation; either |     License as published by the Free Software Foundation; either | ||||||
|     version 2.1 of the License, or (at your option) any later version. |     version 2 of the License, or (at your option) any later version. | ||||||
|  |  | ||||||
|     This library is distributed in the hope that it will be useful, |     This library is distributed in the hope that it will be useful, | ||||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of |     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -485,7 +485,7 @@ convey the exclusion of warranty; and each file should have at least the | |||||||
|  |  | ||||||
|     You should have received a copy of the GNU Lesser General Public |     You should have received a copy of the GNU Lesser General Public | ||||||
|     License along with this library; if not, write to the Free Software |     License along with this library; if not, write to the Free Software | ||||||
|     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA |     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||||
|  |  | ||||||
| Also add information on how to contact you by electronic and paper mail. | Also add information on how to contact you by electronic and paper mail. | ||||||
|  |  | ||||||
| @@ -500,3 +500,5 @@ necessary.  Here is a sample; alter the names: | |||||||
|   Ty Coon, President of Vice |   Ty Coon, President of Vice | ||||||
|  |  | ||||||
| That's all there is to it! | That's all there is to it! | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										270
									
								
								COPYING.PYTHON
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										270
									
								
								COPYING.PYTHON
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,270 @@ | |||||||
|  | A. HISTORY OF THE SOFTWARE | ||||||
|  | ========================== | ||||||
|  |  | ||||||
|  | Python was created in the early 1990s by Guido van Rossum at Stichting | ||||||
|  | Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands | ||||||
|  | as a successor of a language called ABC.  Guido remains Python's | ||||||
|  | principal author, although it includes many contributions from others. | ||||||
|  |  | ||||||
|  | In 1995, Guido continued his work on Python at the Corporation for | ||||||
|  | National Research Initiatives (CNRI, see http://www.cnri.reston.va.us) | ||||||
|  | in Reston, Virginia where he released several versions of the | ||||||
|  | software. | ||||||
|  |  | ||||||
|  | In May 2000, Guido and the Python core development team moved to | ||||||
|  | BeOpen.com to form the BeOpen PythonLabs team.  In October of the same | ||||||
|  | year, the PythonLabs team moved to Digital Creations (now Zope | ||||||
|  | Corporation, see http://www.zope.com).  In 2001, the Python Software | ||||||
|  | Foundation (PSF, see http://www.python.org/psf/) was formed, a | ||||||
|  | non-profit organization created specifically to own Python-related | ||||||
|  | Intellectual Property.  Zope Corporation is a sponsoring member of | ||||||
|  | the PSF. | ||||||
|  |  | ||||||
|  | All Python releases are Open Source (see http://www.opensource.org for | ||||||
|  | the Open Source Definition).  Historically, most, but not all, Python | ||||||
|  | releases have also been GPL-compatible; the table below summarizes | ||||||
|  | the various releases. | ||||||
|  |  | ||||||
|  |     Release         Derived     Year        Owner       GPL- | ||||||
|  |                     from                                compatible? (1) | ||||||
|  |  | ||||||
|  |     0.9.0 thru 1.2              1991-1995   CWI         yes | ||||||
|  |     1.3 thru 1.5.2  1.2         1995-1999   CNRI        yes | ||||||
|  |     1.6             1.5.2       2000        CNRI        no | ||||||
|  |     2.0             1.6         2000        BeOpen.com  no | ||||||
|  |     1.6.1           1.6         2001        CNRI        yes (2) | ||||||
|  |     2.1             2.0+1.6.1   2001        PSF         no | ||||||
|  |     2.0.1           2.0+1.6.1   2001        PSF         yes | ||||||
|  |     2.1.1           2.1+2.0.1   2001        PSF         yes | ||||||
|  |     2.2             2.1.1       2001        PSF         yes | ||||||
|  |     2.1.2           2.1.1       2002        PSF         yes | ||||||
|  |     2.1.3           2.1.2       2002        PSF         yes | ||||||
|  |     2.2.1           2.2         2002        PSF         yes | ||||||
|  |     2.2.2           2.2.1       2002        PSF         yes | ||||||
|  |     2.2.3           2.2.2       2003        PSF         yes | ||||||
|  |     2.3             2.2.2       2002-2003   PSF         yes | ||||||
|  |     2.3.1           2.3         2002-2003   PSF         yes | ||||||
|  |     2.3.2           2.3.1       2002-2003   PSF         yes | ||||||
|  |     2.3.3           2.3.2       2002-2003   PSF         yes | ||||||
|  |     2.3.4           2.3.3       2004        PSF         yes | ||||||
|  |     2.3.5           2.3.4       2005        PSF         yes | ||||||
|  |     2.4             2.3         2004        PSF         yes | ||||||
|  |     2.4.1           2.4         2005        PSF         yes | ||||||
|  |     2.4.2           2.4.1       2005        PSF         yes | ||||||
|  |     2.4.3           2.4.2       2006        PSF         yes | ||||||
|  |     2.5             2.4         2006        PSF         yes | ||||||
|  |     2.7             2.6         2010        PSF         yes | ||||||
|  |  | ||||||
|  | Footnotes: | ||||||
|  |  | ||||||
|  | (1) GPL-compatible doesn't mean that we're distributing Python under | ||||||
|  |     the GPL.  All Python licenses, unlike the GPL, let you distribute | ||||||
|  |     a modified version without making your changes open source.  The | ||||||
|  |     GPL-compatible licenses make it possible to combine Python with | ||||||
|  |     other software that is released under the GPL; the others don't. | ||||||
|  |  | ||||||
|  | (2) According to Richard Stallman, 1.6.1 is not GPL-compatible, | ||||||
|  |     because its license has a choice of law clause.  According to | ||||||
|  |     CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 | ||||||
|  |     is "not incompatible" with the GPL. | ||||||
|  |  | ||||||
|  | Thanks to the many outside volunteers who have worked under Guido's | ||||||
|  | direction to make these releases possible. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON | ||||||
|  | =============================================================== | ||||||
|  |  | ||||||
|  | PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 | ||||||
|  | -------------------------------------------- | ||||||
|  |  | ||||||
|  | 1. This LICENSE AGREEMENT is between the Python Software Foundation | ||||||
|  | ("PSF"), and the Individual or Organization ("Licensee") accessing and | ||||||
|  | otherwise using this software ("Python") in source or binary form and | ||||||
|  | its associated documentation. | ||||||
|  |  | ||||||
|  | 2. Subject to the terms and conditions of this License Agreement, PSF | ||||||
|  | hereby grants Licensee a nonexclusive, royalty-free, world-wide | ||||||
|  | license to reproduce, analyze, test, perform and/or display publicly, | ||||||
|  | prepare derivative works, distribute, and otherwise use Python | ||||||
|  | alone or in any derivative version, provided, however, that PSF's | ||||||
|  | License Agreement and PSF's notice of copyright, i.e., "Copyright (c) | ||||||
|  | 2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation; All Rights | ||||||
|  | Reserved" are retained in Python alone or in any derivative version  | ||||||
|  | prepared by Licensee. | ||||||
|  |  | ||||||
|  | 3. In the event Licensee prepares a derivative work that is based on | ||||||
|  | or incorporates Python or any part thereof, and wants to make | ||||||
|  | the derivative work available to others as provided herein, then | ||||||
|  | Licensee hereby agrees to include in any such work a brief summary of | ||||||
|  | the changes made to Python. | ||||||
|  |  | ||||||
|  | 4. PSF is making Python available to Licensee on an "AS IS" | ||||||
|  | basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR | ||||||
|  | IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND | ||||||
|  | DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS | ||||||
|  | FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT | ||||||
|  | INFRINGE ANY THIRD PARTY RIGHTS. | ||||||
|  |  | ||||||
|  | 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON | ||||||
|  | FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS | ||||||
|  | A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, | ||||||
|  | OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. | ||||||
|  |  | ||||||
|  | 6. This License Agreement will automatically terminate upon a material | ||||||
|  | breach of its terms and conditions. | ||||||
|  |  | ||||||
|  | 7. Nothing in this License Agreement shall be deemed to create any | ||||||
|  | relationship of agency, partnership, or joint venture between PSF and | ||||||
|  | Licensee.  This License Agreement does not grant permission to use PSF | ||||||
|  | trademarks or trade name in a trademark sense to endorse or promote | ||||||
|  | products or services of Licensee, or any third party. | ||||||
|  |  | ||||||
|  | 8. By copying, installing or otherwise using Python, Licensee | ||||||
|  | agrees to be bound by the terms and conditions of this License | ||||||
|  | Agreement. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 | ||||||
|  | ------------------------------------------- | ||||||
|  |  | ||||||
|  | BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 | ||||||
|  |  | ||||||
|  | 1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an | ||||||
|  | office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the | ||||||
|  | Individual or Organization ("Licensee") accessing and otherwise using | ||||||
|  | this software in source or binary form and its associated | ||||||
|  | documentation ("the Software"). | ||||||
|  |  | ||||||
|  | 2. Subject to the terms and conditions of this BeOpen Python License | ||||||
|  | Agreement, BeOpen hereby grants Licensee a non-exclusive, | ||||||
|  | royalty-free, world-wide license to reproduce, analyze, test, perform | ||||||
|  | and/or display publicly, prepare derivative works, distribute, and | ||||||
|  | otherwise use the Software alone or in any derivative version, | ||||||
|  | provided, however, that the BeOpen Python License is retained in the | ||||||
|  | Software, alone or in any derivative version prepared by Licensee. | ||||||
|  |  | ||||||
|  | 3. BeOpen is making the Software available to Licensee on an "AS IS" | ||||||
|  | basis.  BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR | ||||||
|  | IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND | ||||||
|  | DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS | ||||||
|  | FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT | ||||||
|  | INFRINGE ANY THIRD PARTY RIGHTS. | ||||||
|  |  | ||||||
|  | 4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE | ||||||
|  | SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS | ||||||
|  | AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY | ||||||
|  | DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. | ||||||
|  |  | ||||||
|  | 5. This License Agreement will automatically terminate upon a material | ||||||
|  | breach of its terms and conditions. | ||||||
|  |  | ||||||
|  | 6. This License Agreement shall be governed by and interpreted in all | ||||||
|  | respects by the law of the State of California, excluding conflict of | ||||||
|  | law provisions.  Nothing in this License Agreement shall be deemed to | ||||||
|  | create any relationship of agency, partnership, or joint venture | ||||||
|  | between BeOpen and Licensee.  This License Agreement does not grant | ||||||
|  | permission to use BeOpen trademarks or trade names in a trademark | ||||||
|  | sense to endorse or promote products or services of Licensee, or any | ||||||
|  | third party.  As an exception, the "BeOpen Python" logos available at | ||||||
|  | http://www.pythonlabs.com/logos.html may be used according to the | ||||||
|  | permissions granted on that web page. | ||||||
|  |  | ||||||
|  | 7. By copying, installing or otherwise using the software, Licensee | ||||||
|  | agrees to be bound by the terms and conditions of this License | ||||||
|  | Agreement. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 | ||||||
|  | --------------------------------------- | ||||||
|  |  | ||||||
|  | 1. This LICENSE AGREEMENT is between the Corporation for National | ||||||
|  | Research Initiatives, having an office at 1895 Preston White Drive, | ||||||
|  | Reston, VA 20191 ("CNRI"), and the Individual or Organization | ||||||
|  | ("Licensee") accessing and otherwise using Python 1.6.1 software in | ||||||
|  | source or binary form and its associated documentation. | ||||||
|  |  | ||||||
|  | 2. Subject to the terms and conditions of this License Agreement, CNRI | ||||||
|  | hereby grants Licensee a nonexclusive, royalty-free, world-wide | ||||||
|  | license to reproduce, analyze, test, perform and/or display publicly, | ||||||
|  | prepare derivative works, distribute, and otherwise use Python 1.6.1 | ||||||
|  | alone or in any derivative version, provided, however, that CNRI's | ||||||
|  | License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) | ||||||
|  | 1995-2001 Corporation for National Research Initiatives; All Rights | ||||||
|  | Reserved" are retained in Python 1.6.1 alone or in any derivative | ||||||
|  | version prepared by Licensee.  Alternately, in lieu of CNRI's License | ||||||
|  | Agreement, Licensee may substitute the following text (omitting the | ||||||
|  | quotes): "Python 1.6.1 is made available subject to the terms and | ||||||
|  | conditions in CNRI's License Agreement.  This Agreement together with | ||||||
|  | Python 1.6.1 may be located on the Internet using the following | ||||||
|  | unique, persistent identifier (known as a handle): 1895.22/1013.  This | ||||||
|  | Agreement may also be obtained from a proxy server on the Internet | ||||||
|  | using the following URL: http://hdl.handle.net/1895.22/1013". | ||||||
|  |  | ||||||
|  | 3. In the event Licensee prepares a derivative work that is based on | ||||||
|  | or incorporates Python 1.6.1 or any part thereof, and wants to make | ||||||
|  | the derivative work available to others as provided herein, then | ||||||
|  | Licensee hereby agrees to include in any such work a brief summary of | ||||||
|  | the changes made to Python 1.6.1. | ||||||
|  |  | ||||||
|  | 4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" | ||||||
|  | basis.  CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR | ||||||
|  | IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND | ||||||
|  | DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS | ||||||
|  | FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT | ||||||
|  | INFRINGE ANY THIRD PARTY RIGHTS. | ||||||
|  |  | ||||||
|  | 5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON | ||||||
|  | 1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS | ||||||
|  | A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, | ||||||
|  | OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. | ||||||
|  |  | ||||||
|  | 6. This License Agreement will automatically terminate upon a material | ||||||
|  | breach of its terms and conditions. | ||||||
|  |  | ||||||
|  | 7. This License Agreement shall be governed by the federal | ||||||
|  | intellectual property law of the United States, including without | ||||||
|  | limitation the federal copyright law, and, to the extent such | ||||||
|  | U.S. federal law does not apply, by the law of the Commonwealth of | ||||||
|  | Virginia, excluding Virginia's conflict of law provisions. | ||||||
|  | Notwithstanding the foregoing, with regard to derivative works based | ||||||
|  | on Python 1.6.1 that incorporate non-separable material that was | ||||||
|  | previously distributed under the GNU General Public License (GPL), the | ||||||
|  | law of the Commonwealth of Virginia shall govern this License | ||||||
|  | Agreement only as to issues arising under or with respect to | ||||||
|  | Paragraphs 4, 5, and 7 of this License Agreement.  Nothing in this | ||||||
|  | License Agreement shall be deemed to create any relationship of | ||||||
|  | agency, partnership, or joint venture between CNRI and Licensee.  This | ||||||
|  | License Agreement does not grant permission to use CNRI trademarks or | ||||||
|  | trade name in a trademark sense to endorse or promote products or | ||||||
|  | services of Licensee, or any third party. | ||||||
|  |  | ||||||
|  | 8. By clicking on the "ACCEPT" button where indicated, or by copying, | ||||||
|  | installing or otherwise using Python 1.6.1, Licensee agrees to be | ||||||
|  | bound by the terms and conditions of this License Agreement. | ||||||
|  |  | ||||||
|  |         ACCEPT | ||||||
|  |  | ||||||
|  |  | ||||||
|  | CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 | ||||||
|  | -------------------------------------------------- | ||||||
|  |  | ||||||
|  | Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, | ||||||
|  | The Netherlands.  All rights reserved. | ||||||
|  |  | ||||||
|  | Permission to use, copy, modify, and distribute this software and its | ||||||
|  | documentation for any purpose and without fee is hereby granted, | ||||||
|  | provided that the above copyright notice appear in all copies and that | ||||||
|  | both that copyright notice and this permission notice appear in | ||||||
|  | supporting documentation, and that the name of Stichting Mathematisch | ||||||
|  | Centrum or CWI not be used in advertising or publicity pertaining to | ||||||
|  | distribution of the software without specific, written prior | ||||||
|  | permission. | ||||||
|  |  | ||||||
|  | STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO | ||||||
|  | THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | ||||||
|  | FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE | ||||||
|  | FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||||
|  | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||||
|  | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | ||||||
|  | OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||||
							
								
								
									
										9
									
								
								HACKING
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								HACKING
									
									
									
									
									
								
							| @@ -118,15 +118,6 @@ Please note that g_malloc will exit on allocation failure, so there | |||||||
| is no need to test for failure (as you would have to with malloc). | 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. | 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 | Memory allocated by qemu_memalign or qemu_blockalign must be freed with | ||||||
| qemu_vfree, since breaking this will cause problems on Win32. | qemu_vfree, since breaking this will cause problems on Win32. | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										702
									
								
								MAINTAINERS
									
									
									
									
									
								
							
							
						
						
									
										702
									
								
								MAINTAINERS
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										325
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										325
									
								
								Makefile
									
									
									
									
									
								
							| @@ -20,6 +20,8 @@ ifneq ($(wildcard config-host.mak),) | |||||||
| all: | all: | ||||||
| include config-host.mak | include config-host.mak | ||||||
|  |  | ||||||
|  | PYTHON_UTF8 = LC_ALL= LANG=C LC_CTYPE=en_US.UTF-8 $(PYTHON) | ||||||
|  |  | ||||||
| git-submodule-update: | git-submodule-update: | ||||||
|  |  | ||||||
| .PHONY: git-submodule-update | .PHONY: git-submodule-update | ||||||
| @@ -67,7 +69,7 @@ CONFIG_ALL=y | |||||||
| -include config-all-devices.mak | -include config-all-devices.mak | ||||||
| -include config-all-disas.mak | -include config-all-disas.mak | ||||||
|  |  | ||||||
| config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios $(SRC_PATH)/VERSION | config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios | ||||||
| 	@echo $@ is out-of-date, running configure | 	@echo $@ is out-of-date, running configure | ||||||
| 	@# TODO: The next lines include code which supports a smooth | 	@# TODO: The next lines include code which supports a smooth | ||||||
| 	@# transition from old configurations without config.status. | 	@# transition from old configurations without config.status. | ||||||
| @@ -88,26 +90,78 @@ endif | |||||||
| include $(SRC_PATH)/rules.mak | include $(SRC_PATH)/rules.mak | ||||||
|  |  | ||||||
| GENERATED_FILES = qemu-version.h config-host.h qemu-options.def | GENERATED_FILES = qemu-version.h config-host.h qemu-options.def | ||||||
|  | GENERATED_FILES += qapi/qapi-builtin-types.h qapi/qapi-builtin-types.c | ||||||
| #see Makefile.objs for the definition of QAPI_MODULES | GENERATED_FILES += qapi/qapi-types.h qapi/qapi-types.c | ||||||
| GENERATED_QAPI_FILES = qapi/qapi-builtin-types.h qapi/qapi-builtin-types.c | GENERATED_FILES += qapi/qapi-types-block-core.h qapi/qapi-types-block-core.c | ||||||
| GENERATED_QAPI_FILES += qapi/qapi-types.h qapi/qapi-types.c | GENERATED_FILES += qapi/qapi-types-block.h qapi/qapi-types-block.c | ||||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-types-%.h) | GENERATED_FILES += qapi/qapi-types-char.h qapi/qapi-types-char.c | ||||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-types-%.c) | GENERATED_FILES += qapi/qapi-types-common.h qapi/qapi-types-common.c | ||||||
| GENERATED_QAPI_FILES += qapi/qapi-builtin-visit.h qapi/qapi-builtin-visit.c | GENERATED_FILES += qapi/qapi-types-crypto.h qapi/qapi-types-crypto.c | ||||||
| GENERATED_QAPI_FILES += qapi/qapi-visit.h qapi/qapi-visit.c | GENERATED_FILES += qapi/qapi-types-introspect.h qapi/qapi-types-introspect.c | ||||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.h) | GENERATED_FILES += qapi/qapi-types-migration.h qapi/qapi-types-migration.c | ||||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.c) | GENERATED_FILES += qapi/qapi-types-misc.h qapi/qapi-types-misc.c | ||||||
| GENERATED_QAPI_FILES += qapi/qapi-commands.h qapi/qapi-commands.c | GENERATED_FILES += qapi/qapi-types-net.h qapi/qapi-types-net.c | ||||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.h) | GENERATED_FILES += qapi/qapi-types-rocker.h qapi/qapi-types-rocker.c | ||||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.c) | GENERATED_FILES += qapi/qapi-types-run-state.h qapi/qapi-types-run-state.c | ||||||
| GENERATED_QAPI_FILES += qapi/qapi-events.h qapi/qapi-events.c | GENERATED_FILES += qapi/qapi-types-sockets.h qapi/qapi-types-sockets.c | ||||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.h) | GENERATED_FILES += qapi/qapi-types-tpm.h qapi/qapi-types-tpm.c | ||||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.c) | GENERATED_FILES += qapi/qapi-types-trace.h qapi/qapi-types-trace.c | ||||||
| GENERATED_QAPI_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h | GENERATED_FILES += qapi/qapi-types-transaction.h qapi/qapi-types-transaction.c | ||||||
| GENERATED_QAPI_FILES += qapi/qapi-doc.texi | GENERATED_FILES += qapi/qapi-types-ui.h qapi/qapi-types-ui.c | ||||||
|  | GENERATED_FILES += qapi/qapi-builtin-visit.h qapi/qapi-builtin-visit.c | ||||||
| GENERATED_FILES += $(GENERATED_QAPI_FILES) | GENERATED_FILES += qapi/qapi-visit.h qapi/qapi-visit.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-block-core.h qapi/qapi-visit-block-core.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-block.h qapi/qapi-visit-block.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-char.h qapi/qapi-visit-char.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-common.h qapi/qapi-visit-common.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-crypto.h qapi/qapi-visit-crypto.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-introspect.h qapi/qapi-visit-introspect.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-migration.h qapi/qapi-visit-migration.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-misc.h qapi/qapi-visit-misc.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-net.h qapi/qapi-visit-net.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-rocker.h qapi/qapi-visit-rocker.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-run-state.h qapi/qapi-visit-run-state.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-sockets.h qapi/qapi-visit-sockets.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-tpm.h qapi/qapi-visit-tpm.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-trace.h qapi/qapi-visit-trace.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-transaction.h qapi/qapi-visit-transaction.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-ui.h qapi/qapi-visit-ui.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands.h qapi/qapi-commands.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-block-core.h qapi/qapi-commands-block-core.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-block.h qapi/qapi-commands-block.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-char.h qapi/qapi-commands-char.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-common.h qapi/qapi-commands-common.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-crypto.h qapi/qapi-commands-crypto.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-introspect.h qapi/qapi-commands-introspect.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-migration.h qapi/qapi-commands-migration.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-misc.h qapi/qapi-commands-misc.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-net.h qapi/qapi-commands-net.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-rocker.h qapi/qapi-commands-rocker.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-run-state.h qapi/qapi-commands-run-state.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-sockets.h qapi/qapi-commands-sockets.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-tpm.h qapi/qapi-commands-tpm.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-trace.h qapi/qapi-commands-trace.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-transaction.h qapi/qapi-commands-transaction.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-ui.h qapi/qapi-commands-ui.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events.h qapi/qapi-events.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-block-core.h qapi/qapi-events-block-core.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-block.h qapi/qapi-events-block.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-char.h qapi/qapi-events-char.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-common.h qapi/qapi-events-common.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-crypto.h qapi/qapi-events-crypto.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-introspect.h qapi/qapi-events-introspect.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-migration.h qapi/qapi-events-migration.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-misc.h qapi/qapi-events-misc.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-net.h qapi/qapi-events-net.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-rocker.h qapi/qapi-events-rocker.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-run-state.h qapi/qapi-events-run-state.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-sockets.h qapi/qapi-events-sockets.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-tpm.h qapi/qapi-events-tpm.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-trace.h qapi/qapi-events-trace.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-transaction.h qapi/qapi-events-transaction.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-ui.h qapi/qapi-events-ui.c | ||||||
|  | GENERATED_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h | ||||||
|  | GENERATED_FILES += qapi/qapi-doc.texi | ||||||
|  |  | ||||||
| GENERATED_FILES += trace/generated-tcg-tracers.h | GENERATED_FILES += trace/generated-tcg-tracers.h | ||||||
|  |  | ||||||
| @@ -145,7 +199,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") | |||||||
|  |  | ||||||
| %/trace.h: %/trace.h-timestamp | %/trace.h: %/trace.h-timestamp | ||||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||||
| %/trace.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | %/trace.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ | 	$(call quiet-command,$(TRACETOOL) \ | ||||||
| 		--group=$(call trace-group-name,$@) \ | 		--group=$(call trace-group-name,$@) \ | ||||||
| 		--format=h \ | 		--format=h \ | ||||||
| @@ -154,7 +208,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") | |||||||
|  |  | ||||||
| %/trace.c: %/trace.c-timestamp | %/trace.c: %/trace.c-timestamp | ||||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||||
| %/trace.c-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | %/trace.c-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ | 	$(call quiet-command,$(TRACETOOL) \ | ||||||
| 		--group=$(call trace-group-name,$@) \ | 		--group=$(call trace-group-name,$@) \ | ||||||
| 		--format=c \ | 		--format=c \ | ||||||
| @@ -163,7 +217,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") | |||||||
|  |  | ||||||
| %/trace-ust.h: %/trace-ust.h-timestamp | %/trace-ust.h: %/trace-ust.h-timestamp | ||||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||||
| %/trace-ust.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | %/trace-ust.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ | 	$(call quiet-command,$(TRACETOOL) \ | ||||||
| 		--group=$(call trace-group-name,$@) \ | 		--group=$(call trace-group-name,$@) \ | ||||||
| 		--format=ust-events-h \ | 		--format=ust-events-h \ | ||||||
| @@ -187,7 +241,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") | |||||||
|  |  | ||||||
| trace-root.h: trace-root.h-timestamp | trace-root.h: trace-root.h-timestamp | ||||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||||
| trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ | 	$(call quiet-command,$(TRACETOOL) \ | ||||||
| 		--group=root \ | 		--group=root \ | ||||||
| 		--format=h \ | 		--format=h \ | ||||||
| @@ -196,7 +250,7 @@ trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/con | |||||||
|  |  | ||||||
| trace-root.c: trace-root.c-timestamp | trace-root.c: trace-root.c-timestamp | ||||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||||
| trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ | 	$(call quiet-command,$(TRACETOOL) \ | ||||||
| 		--group=root \ | 		--group=root \ | ||||||
| 		--format=c \ | 		--format=c \ | ||||||
| @@ -205,7 +259,7 @@ trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/con | |||||||
|  |  | ||||||
| trace-ust-root.h: trace-ust-root.h-timestamp | trace-ust-root.h: trace-ust-root.h-timestamp | ||||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||||
| trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ | 	$(call quiet-command,$(TRACETOOL) \ | ||||||
| 		--group=root \ | 		--group=root \ | ||||||
| 		--format=ust-events-h \ | 		--format=ust-events-h \ | ||||||
| @@ -214,7 +268,7 @@ trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR) | |||||||
|  |  | ||||||
| trace-ust-all.h: trace-ust-all.h-timestamp | trace-ust-all.h: trace-ust-all.h-timestamp | ||||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||||
| trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/config-host.mak | trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y) | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ | 	$(call quiet-command,$(TRACETOOL) \ | ||||||
| 		--group=all \ | 		--group=all \ | ||||||
| 		--format=ust-events-h \ | 		--format=ust-events-h \ | ||||||
| @@ -223,7 +277,7 @@ trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/con | |||||||
|  |  | ||||||
| trace-ust-all.c: trace-ust-all.c-timestamp | trace-ust-all.c: trace-ust-all.c-timestamp | ||||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||||
| trace-ust-all.c-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/config-host.mak | trace-ust-all.c-timestamp: $(trace-events-files) $(tracetool-y) | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ | 	$(call quiet-command,$(TRACETOOL) \ | ||||||
| 		--group=all \ | 		--group=all \ | ||||||
| 		--format=ust-events-c \ | 		--format=ust-events-c \ | ||||||
| @@ -264,7 +318,6 @@ KEYCODEMAP_FILES = \ | |||||||
| 		 ui/input-keymap-xorgkbd-to-qcode.c \ | 		 ui/input-keymap-xorgkbd-to-qcode.c \ | ||||||
| 		 ui/input-keymap-xorgxquartz-to-qcode.c \ | 		 ui/input-keymap-xorgxquartz-to-qcode.c \ | ||||||
| 		 ui/input-keymap-xorgxwin-to-qcode.c \ | 		 ui/input-keymap-xorgxwin-to-qcode.c \ | ||||||
| 		 ui/input-keymap-osx-to-qcode.c \ |  | ||||||
| 		 $(NULL) | 		 $(NULL) | ||||||
|  |  | ||||||
| GENERATED_FILES += $(KEYCODEMAP_FILES) | GENERATED_FILES += $(KEYCODEMAP_FILES) | ||||||
| @@ -294,20 +347,16 @@ $(call set-vpath, $(SRC_PATH)) | |||||||
|  |  | ||||||
| LIBS+=-lz $(LIBS_TOOLS) | 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 | ifdef BUILD_DOCS | ||||||
| DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8 | DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8 | ||||||
| DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7 | DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7 | ||||||
| DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7 | DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7 | ||||||
| DOCS+=docs/qemu-block-drivers.7 | DOCS+=docs/qemu-block-drivers.7 | ||||||
| DOCS+=docs/qemu-cpu-models.7 |  | ||||||
| ifdef CONFIG_VIRTFS | ifdef CONFIG_VIRTFS | ||||||
| DOCS+=fsdev/virtfs-proxy-helper.1 | DOCS+=fsdev/virtfs-proxy-helper.1 | ||||||
| endif | endif | ||||||
| ifdef CONFIG_TRACE_SYSTEMTAP |  | ||||||
| DOCS+=scripts/qemu-trace-stap.1 |  | ||||||
| endif |  | ||||||
| else | else | ||||||
| DOCS= | DOCS= | ||||||
| endif | endif | ||||||
| @@ -362,10 +411,8 @@ dummy := $(call unnest-vars,, \ | |||||||
|                 chardev-obj-y \ |                 chardev-obj-y \ | ||||||
|                 util-obj-y \ |                 util-obj-y \ | ||||||
|                 qga-obj-y \ |                 qga-obj-y \ | ||||||
|                 elf2dmp-obj-y \ |  | ||||||
|                 ivshmem-client-obj-y \ |                 ivshmem-client-obj-y \ | ||||||
|                 ivshmem-server-obj-y \ |                 ivshmem-server-obj-y \ | ||||||
|                 rdmacm-mux-obj-y \ |  | ||||||
|                 libvhost-user-obj-y \ |                 libvhost-user-obj-y \ | ||||||
|                 vhost-user-scsi-obj-y \ |                 vhost-user-scsi-obj-y \ | ||||||
|                 vhost-user-blk-obj-y \ |                 vhost-user-blk-obj-y \ | ||||||
| @@ -382,8 +429,7 @@ dummy := $(call unnest-vars,, \ | |||||||
|                 ui-obj-m \ |                 ui-obj-m \ | ||||||
|                 audio-obj-y \ |                 audio-obj-y \ | ||||||
|                 audio-obj-m \ |                 audio-obj-m \ | ||||||
|                 trace-obj-y \ |                 trace-obj-y) | ||||||
|                 slirp-obj-y) |  | ||||||
|  |  | ||||||
| include $(SRC_PATH)/tests/Makefile.include | include $(SRC_PATH)/tests/Makefile.include | ||||||
|  |  | ||||||
| @@ -392,23 +438,21 @@ all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules | |||||||
| qemu-version.h: FORCE | qemu-version.h: FORCE | ||||||
| 	$(call quiet-command, \ | 	$(call quiet-command, \ | ||||||
| 		(cd $(SRC_PATH); \ | 		(cd $(SRC_PATH); \ | ||||||
|  | 		printf '#define QEMU_PKGVERSION '; \ | ||||||
| 		if test -n "$(PKGVERSION)"; then \ | 		if test -n "$(PKGVERSION)"; then \ | ||||||
| 			pkgvers="$(PKGVERSION)"; \ | 			printf '"$(PKGVERSION)"\n'; \ | ||||||
| 		else \ | 		else \ | ||||||
| 			if test -d .git; then \ | 			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 \ | 				if ! git diff-index --quiet HEAD &>/dev/null; then \ | ||||||
| 					pkgvers="$${pkgvers}-dirty"; \ | 					printf -- '-dirty'; \ | ||||||
| 				fi; \ | 				fi; \ | ||||||
| 			fi; \ | 				printf ')"\n'; \ | ||||||
| 		fi; \ |  | ||||||
| 		printf "#define QEMU_PKGVERSION \"$${pkgvers}\"\n"; \ |  | ||||||
| 		if test -n "$${pkgvers}"; then \ |  | ||||||
| 			printf '#define QEMU_FULL_VERSION QEMU_VERSION " (" QEMU_PKGVERSION ")"\n'; \ |  | ||||||
| 			else \ | 			else \ | ||||||
| 			printf '#define QEMU_FULL_VERSION QEMU_VERSION\n'; \ | 				printf '""\n'; \ | ||||||
| 			fi; \ | 			fi; \ | ||||||
| 		) > $@.tmp) | 		fi) > $@.tmp) | ||||||
| 	$(call quiet-command, if ! cmp -s $@ $@.tmp; then \ | 	$(call quiet-command, if ! cmp -s $@ $@.tmp; then \ | ||||||
| 	  mv $@.tmp $@; \ | 	  mv $@.tmp $@; \ | ||||||
| 	 else \ | 	 else \ | ||||||
| @@ -439,7 +483,7 @@ subdir-dtc: .git-submodule-status dtc/libfdt dtc/tests | |||||||
| 	$(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt/libfdt.a,) | 	$(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 | dtc/%: .git-submodule-status | ||||||
| 	@mkdir -p $@ | 	mkdir -p $@ | ||||||
|  |  | ||||||
| # Overriding CFLAGS causes us to lose defines added in the sub-makefile. | # Overriding CFLAGS causes us to lose defines added in the sub-makefile. | ||||||
| # Not overriding CFLAGS leads to mis-matches between compilation modes. | # Not overriding CFLAGS leads to mis-matches between compilation modes. | ||||||
| @@ -456,7 +500,7 @@ CAP_CFLAGS += -DCAPSTONE_HAS_X86 | |||||||
| subdir-capstone: .git-submodule-status | subdir-capstone: .git-submodule-status | ||||||
| 	$(call quiet-command,$(MAKE) -C $(SRC_PATH)/capstone CAPSTONE_SHARED=no BUILDDIR="$(BUILD_DIR)/capstone" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(CAP_CFLAGS)" $(SUBDIR_MAKEFLAGS) $(BUILD_DIR)/capstone/$(LIBCAPSTONE)) | 	$(call quiet-command,$(MAKE) -C $(SRC_PATH)/capstone CAPSTONE_SHARED=no BUILDDIR="$(BUILD_DIR)/capstone" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(CAP_CFLAGS)" $(SUBDIR_MAKEFLAGS) $(BUILD_DIR)/capstone/$(LIBCAPSTONE)) | ||||||
|  |  | ||||||
| $(SUBDIR_RULES): libqemuutil.a $(common-obj-y) $(chardev-obj-y) $(slirp-obj-y) \ | $(SUBDIR_RULES): libqemuutil.a $(common-obj-y) $(chardev-obj-y) \ | ||||||
| 	$(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY)) | 	$(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY)) | ||||||
|  |  | ||||||
| ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS)) | ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS)) | ||||||
| @@ -493,8 +537,6 @@ qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS) | |||||||
|  |  | ||||||
| qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $(COMMON_LDADDS) | qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $(COMMON_LDADDS) | ||||||
|  |  | ||||||
| qemu-edid$(EXESUF): qemu-edid.o hw/display/edid-generate.o $(COMMON_LDADDS) |  | ||||||
|  |  | ||||||
| fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS) | fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS) | ||||||
| fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap | fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap | ||||||
|  |  | ||||||
| @@ -519,6 +561,7 @@ $(SRC_PATH)/scripts/qapi/types.py \ | |||||||
| $(SRC_PATH)/scripts/qapi/visit.py \ | $(SRC_PATH)/scripts/qapi/visit.py \ | ||||||
| $(SRC_PATH)/scripts/qapi/common.py \ | $(SRC_PATH)/scripts/qapi/common.py \ | ||||||
| $(SRC_PATH)/scripts/qapi/doc.py \ | $(SRC_PATH)/scripts/qapi/doc.py \ | ||||||
|  | $(SRC_PATH)/scripts/ordereddict.py \ | ||||||
| $(SRC_PATH)/scripts/qapi-gen.py | $(SRC_PATH)/scripts/qapi-gen.py | ||||||
|  |  | ||||||
| qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \ | qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \ | ||||||
| @@ -527,17 +570,102 @@ qga/qapi-generated/qga-qapi-commands.h qga/qapi-generated/qga-qapi-commands.c \ | |||||||
| qga/qapi-generated/qga-qapi-doc.texi: \ | qga/qapi-generated/qga-qapi-doc.texi: \ | ||||||
| qga/qapi-generated/qapi-gen-timestamp ; | qga/qapi-generated/qapi-gen-timestamp ; | ||||||
| qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py) | 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-" $<, \ | 		-o qga/qapi-generated -p "qga-" $<, \ | ||||||
| 		"GEN","$(@:%-timestamp=%)") | 		"GEN","$(@:%-timestamp=%)") | ||||||
| 	@>$@ | 	@>$@ | ||||||
|  |  | ||||||
| qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json \ | qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json $(SRC_PATH)/qapi/common.json \ | ||||||
|                $(QAPI_MODULES:%=$(SRC_PATH)/qapi/%.json) |                $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \ | ||||||
|  |                $(SRC_PATH)/qapi/char.json \ | ||||||
|  |                $(SRC_PATH)/qapi/crypto.json \ | ||||||
|  |                $(SRC_PATH)/qapi/introspect.json \ | ||||||
|  |                $(SRC_PATH)/qapi/migration.json \ | ||||||
|  |                $(SRC_PATH)/qapi/misc.json \ | ||||||
|  |                $(SRC_PATH)/qapi/net.json \ | ||||||
|  |                $(SRC_PATH)/qapi/rocker.json \ | ||||||
|  |                $(SRC_PATH)/qapi/run-state.json \ | ||||||
|  |                $(SRC_PATH)/qapi/sockets.json \ | ||||||
|  |                $(SRC_PATH)/qapi/tpm.json \ | ||||||
|  |                $(SRC_PATH)/qapi/trace.json \ | ||||||
|  |                $(SRC_PATH)/qapi/transaction.json \ | ||||||
|  |                $(SRC_PATH)/qapi/ui.json | ||||||
|  |  | ||||||
| $(GENERATED_QAPI_FILES): qapi-gen-timestamp ; | qapi/qapi-builtin-types.c qapi/qapi-builtin-types.h \ | ||||||
|  | qapi/qapi-types.c qapi/qapi-types.h \ | ||||||
|  | qapi/qapi-types-block-core.c qapi/qapi-types-block-core.h \ | ||||||
|  | qapi/qapi-types-block.c qapi/qapi-types-block.h \ | ||||||
|  | qapi/qapi-types-char.c qapi/qapi-types-char.h \ | ||||||
|  | qapi/qapi-types-common.c qapi/qapi-types-common.h \ | ||||||
|  | qapi/qapi-types-crypto.c qapi/qapi-types-crypto.h \ | ||||||
|  | qapi/qapi-types-introspect.c qapi/qapi-types-introspect.h \ | ||||||
|  | qapi/qapi-types-migration.c qapi/qapi-types-migration.h \ | ||||||
|  | qapi/qapi-types-misc.c qapi/qapi-types-misc.h \ | ||||||
|  | qapi/qapi-types-net.c qapi/qapi-types-net.h \ | ||||||
|  | qapi/qapi-types-rocker.c qapi/qapi-types-rocker.h \ | ||||||
|  | qapi/qapi-types-run-state.c qapi/qapi-types-run-state.h \ | ||||||
|  | qapi/qapi-types-sockets.c qapi/qapi-types-sockets.h \ | ||||||
|  | qapi/qapi-types-tpm.c qapi/qapi-types-tpm.h \ | ||||||
|  | qapi/qapi-types-trace.c qapi/qapi-types-trace.h \ | ||||||
|  | qapi/qapi-types-transaction.c qapi/qapi-types-transaction.h \ | ||||||
|  | qapi/qapi-types-ui.c qapi/qapi-types-ui.h \ | ||||||
|  | qapi/qapi-builtin-visit.c qapi/qapi-builtin-visit.h \ | ||||||
|  | qapi/qapi-visit.c qapi/qapi-visit.h \ | ||||||
|  | qapi/qapi-visit-block-core.c qapi/qapi-visit-block-core.h \ | ||||||
|  | qapi/qapi-visit-block.c qapi/qapi-visit-block.h \ | ||||||
|  | qapi/qapi-visit-char.c qapi/qapi-visit-char.h \ | ||||||
|  | qapi/qapi-visit-common.c qapi/qapi-visit-common.h \ | ||||||
|  | qapi/qapi-visit-crypto.c qapi/qapi-visit-crypto.h \ | ||||||
|  | qapi/qapi-visit-introspect.c qapi/qapi-visit-introspect.h \ | ||||||
|  | qapi/qapi-visit-migration.c qapi/qapi-visit-migration.h \ | ||||||
|  | qapi/qapi-visit-misc.c qapi/qapi-visit-misc.h \ | ||||||
|  | qapi/qapi-visit-net.c qapi/qapi-visit-net.h \ | ||||||
|  | qapi/qapi-visit-rocker.c qapi/qapi-visit-rocker.h \ | ||||||
|  | qapi/qapi-visit-run-state.c qapi/qapi-visit-run-state.h \ | ||||||
|  | qapi/qapi-visit-sockets.c qapi/qapi-visit-sockets.h \ | ||||||
|  | qapi/qapi-visit-tpm.c qapi/qapi-visit-tpm.h \ | ||||||
|  | qapi/qapi-visit-trace.c qapi/qapi-visit-trace.h \ | ||||||
|  | qapi/qapi-visit-transaction.c qapi/qapi-visit-transaction.h \ | ||||||
|  | qapi/qapi-visit-ui.c qapi/qapi-visit-ui.h \ | ||||||
|  | qapi/qapi-commands.h qapi/qapi-commands.c \ | ||||||
|  | qapi/qapi-commands-block-core.c qapi/qapi-commands-block-core.h \ | ||||||
|  | qapi/qapi-commands-block.c qapi/qapi-commands-block.h \ | ||||||
|  | qapi/qapi-commands-char.c qapi/qapi-commands-char.h \ | ||||||
|  | qapi/qapi-commands-common.c qapi/qapi-commands-common.h \ | ||||||
|  | qapi/qapi-commands-crypto.c qapi/qapi-commands-crypto.h \ | ||||||
|  | qapi/qapi-commands-introspect.c qapi/qapi-commands-introspect.h \ | ||||||
|  | qapi/qapi-commands-migration.c qapi/qapi-commands-migration.h \ | ||||||
|  | qapi/qapi-commands-misc.c qapi/qapi-commands-misc.h \ | ||||||
|  | qapi/qapi-commands-net.c qapi/qapi-commands-net.h \ | ||||||
|  | qapi/qapi-commands-rocker.c qapi/qapi-commands-rocker.h \ | ||||||
|  | qapi/qapi-commands-run-state.c qapi/qapi-commands-run-state.h \ | ||||||
|  | qapi/qapi-commands-sockets.c qapi/qapi-commands-sockets.h \ | ||||||
|  | qapi/qapi-commands-tpm.c qapi/qapi-commands-tpm.h \ | ||||||
|  | qapi/qapi-commands-trace.c qapi/qapi-commands-trace.h \ | ||||||
|  | qapi/qapi-commands-transaction.c qapi/qapi-commands-transaction.h \ | ||||||
|  | qapi/qapi-commands-ui.c qapi/qapi-commands-ui.h \ | ||||||
|  | qapi/qapi-events.c qapi/qapi-events.h \ | ||||||
|  | qapi/qapi-events-block-core.c qapi/qapi-events-block-core.h \ | ||||||
|  | qapi/qapi-events-block.c qapi/qapi-events-block.h \ | ||||||
|  | qapi/qapi-events-char.c qapi/qapi-events-char.h \ | ||||||
|  | qapi/qapi-events-common.c qapi/qapi-events-common.h \ | ||||||
|  | qapi/qapi-events-crypto.c qapi/qapi-events-crypto.h \ | ||||||
|  | qapi/qapi-events-introspect.c qapi/qapi-events-introspect.h \ | ||||||
|  | qapi/qapi-events-migration.c qapi/qapi-events-migration.h \ | ||||||
|  | qapi/qapi-events-misc.c qapi/qapi-events-misc.h \ | ||||||
|  | qapi/qapi-events-net.c qapi/qapi-events-net.h \ | ||||||
|  | qapi/qapi-events-rocker.c qapi/qapi-events-rocker.h \ | ||||||
|  | qapi/qapi-events-run-state.c qapi/qapi-events-run-state.h \ | ||||||
|  | qapi/qapi-events-sockets.c qapi/qapi-events-sockets.h \ | ||||||
|  | qapi/qapi-events-tpm.c qapi/qapi-events-tpm.h \ | ||||||
|  | qapi/qapi-events-trace.c qapi/qapi-events-trace.h \ | ||||||
|  | qapi/qapi-events-transaction.c qapi/qapi-events-transaction.h \ | ||||||
|  | qapi/qapi-events-ui.c qapi/qapi-events-ui.h \ | ||||||
|  | qapi/qapi-introspect.h qapi/qapi-introspect.c \ | ||||||
|  | qapi/qapi-doc.texi: \ | ||||||
|  | qapi-gen-timestamp ; | ||||||
| qapi-gen-timestamp: $(qapi-modules) $(qapi-py) | 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 $<, \ | 		-o "qapi" -b $<, \ | ||||||
| 		"GEN","$(@:%-timestamp=%)") | 		"GEN","$(@:%-timestamp=%)") | ||||||
| 	@>$@ | 	@>$@ | ||||||
| @@ -570,10 +698,6 @@ ifneq ($(EXESUF),) | |||||||
| qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI) | qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI) | ||||||
| endif | endif | ||||||
|  |  | ||||||
| elf2dmp$(EXESUF): LIBS += $(CURL_LIBS) |  | ||||||
| elf2dmp$(EXESUF): $(elf2dmp-obj-y) |  | ||||||
| 	$(call LINK, $^) |  | ||||||
|  |  | ||||||
| ifdef CONFIG_IVSHMEM | ifdef CONFIG_IVSHMEM | ||||||
| ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS) | ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS) | ||||||
| 	$(call LINK, $^) | 	$(call LINK, $^) | ||||||
| @@ -585,23 +709,11 @@ vhost-user-scsi$(EXESUF): $(vhost-user-scsi-obj-y) libvhost-user.a | |||||||
| vhost-user-blk$(EXESUF): $(vhost-user-blk-obj-y) libvhost-user.a | vhost-user-blk$(EXESUF): $(vhost-user-blk-obj-y) libvhost-user.a | ||||||
| 	$(call LINK, $^) | 	$(call LINK, $^) | ||||||
|  |  | ||||||
| rdmacm-mux$(EXESUF): LIBS += "-libumad" |  | ||||||
| rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS) |  | ||||||
| 	$(call LINK, $^) |  | ||||||
|  |  | ||||||
| module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak | module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak | ||||||
| 	$(call quiet-command,$(PYTHON) $< $@ \ | 	$(call quiet-command,$(PYTHON) $< $@ \ | ||||||
| 	$(addprefix $(SRC_PATH)/,$(patsubst %.mo,%.c,$(block-obj-m))), \ | 	$(addprefix $(SRC_PATH)/,$(patsubst %.mo,%.c,$(block-obj-m))), \ | ||||||
| 	"GEN","$@") | 	"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: | clean: | ||||||
| # avoid old build problems by removing potentially incorrect old files | # avoid old build problems by removing potentially incorrect old files | ||||||
| 	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h | 	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h | ||||||
| @@ -622,7 +734,7 @@ clean: | |||||||
| 	if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \ | 	if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \ | ||||||
| 	rm -f $$d/qemu-options.def; \ | 	rm -f $$d/qemu-options.def; \ | ||||||
|         done |         done | ||||||
| 	rm -f config-all-devices.mak | 	rm -f $(SUBDIR_DEVICES_MAK) config-all-devices.mak | ||||||
|  |  | ||||||
| VERSION ?= $(shell cat VERSION) | VERSION ?= $(shell cat VERSION) | ||||||
|  |  | ||||||
| @@ -634,7 +746,6 @@ qemu-%.tar.bz2: | |||||||
| distclean: clean | distclean: clean | ||||||
| 	rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi | 	rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi | ||||||
| 	rm -f config-all-devices.mak config-all-disas.mak config.status | 	rm -f config-all-devices.mak config-all-disas.mak config.status | ||||||
| 	rm -f $(SUBDIR_DEVICES_MAK) |  | ||||||
| 	rm -f po/*.mo tests/qemu-iotests/common.env | 	rm -f po/*.mo tests/qemu-iotests/common.env | ||||||
| 	rm -f roms/seabios/config.mak roms/vgabios/config.mak | 	rm -f roms/seabios/config.mak roms/vgabios/config.mak | ||||||
| 	rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps | 	rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps | ||||||
| @@ -650,30 +761,30 @@ distclean: clean | |||||||
| 	rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf | 	rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf | ||||||
| 	rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html | 	rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html | ||||||
| 	rm -f docs/qemu-block-drivers.7 | 	rm -f docs/qemu-block-drivers.7 | ||||||
| 	rm -f docs/qemu-cpu-models.7 |  | ||||||
| 	for d in $(TARGET_DIRS); do \ | 	for d in $(TARGET_DIRS); do \ | ||||||
| 	rm -rf $$d || exit 1 ; \ | 	rm -rf $$d || exit 1 ; \ | ||||||
|         done |         done | ||||||
| 	rm -Rf .sdk | 	rm -Rf .sdk | ||||||
| 	if test -f dtc/version_gen.h; then $(MAKE) $(DTC_MAKE_ARGS) clean; fi | 	if test -f dtc/version_gen.h; then $(MAKE) $(DTC_MAKE_ARGS) clean; fi | ||||||
|  |  | ||||||
| KEYMAPS=da     en-gb  et  fr     fr-ch  is  lt  no  pt-br  sv \ | KEYMAPS=da     en-gb  et  fr     fr-ch  is  lt  modifiers  no  pt-br  sv \ | ||||||
| ar      de     en-us  fi  fr-be  hr     it  lv  nl         pl  ru     th \ | ar      de     en-us  fi  fr-be  hr     it  lv  nl         pl  ru     th \ | ||||||
| de-ch  es     fo  fr-ca  hu     ja  mk  pt  sl     tr \ | common  de-ch  es     fo  fr-ca  hu     ja  mk  nl-be      pt  sl     tr \ | ||||||
| bepo    cz | bepo    cz | ||||||
|  |  | ||||||
| ifdef INSTALL_BLOBS | ifdef INSTALL_BLOBS | ||||||
| BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \ | BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \ | ||||||
| vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \ | vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \ | ||||||
| vgabios-ramfb.bin vgabios-bochs-display.bin \ | acpi-dsdt.aml \ | ||||||
| ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \ | 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-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \ | ||||||
| pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \ | pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \ | ||||||
| efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \ | efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \ | ||||||
| efi-pcnet.rom efi-rtl8139.rom efi-virtio.rom \ | efi-pcnet.rom efi-rtl8139.rom efi-virtio.rom \ | ||||||
| efi-e1000e.rom efi-vmxnet3.rom \ | efi-e1000e.rom efi-vmxnet3.rom \ | ||||||
|  | qemu-icon.bmp qemu_logo_no_text.svg \ | ||||||
| bamboo.dtb canyonlands.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ | bamboo.dtb canyonlands.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ | ||||||
| multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin pvh.bin \ | multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin \ | ||||||
| s390-ccw.img s390-netboot.img \ | s390-ccw.img s390-netboot.img \ | ||||||
| spapr-rtas.bin slof.bin skiboot.lid \ | spapr-rtas.bin slof.bin skiboot.lid \ | ||||||
| palcode-clipper \ | palcode-clipper \ | ||||||
| @@ -696,15 +807,11 @@ ifdef CONFIG_POSIX | |||||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7" | 	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7" | ||||||
| 	$(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7" | 	$(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7" | ||||||
| 	$(INSTALL_DATA) docs/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7" | 	$(INSTALL_DATA) docs/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7" | ||||||
| 	$(INSTALL_DATA) docs/qemu-cpu-models.7 "$(DESTDIR)$(mandir)/man7" |  | ||||||
| ifneq ($(TOOLS),) | ifneq ($(TOOLS),) | ||||||
| 	$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1" | 	$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1" | ||||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8" | 	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8" | ||||||
| 	$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8" | 	$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8" | ||||||
| endif | endif | ||||||
| ifdef CONFIG_TRACE_SYSTEMTAP |  | ||||||
| 	$(INSTALL_DATA) scripts/qemu-trace-stap.1 "$(DESTDIR)$(mandir)/man1" |  | ||||||
| endif |  | ||||||
| ifneq (,$(findstring qemu-ga,$(TOOLS))) | ifneq (,$(findstring qemu-ga,$(TOOLS))) | ||||||
| 	$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8" | 	$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8" | ||||||
| 	$(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)" | 	$(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)" | ||||||
| @@ -727,7 +834,6 @@ ifneq (,$(findstring qemu-ga,$(TOOLS))) | |||||||
| endif | endif | ||||||
| endif | endif | ||||||
|  |  | ||||||
| ICON_SIZES=16x16 24x24 32x32 48x48 64x64 128x128 256x256 512x512 |  | ||||||
|  |  | ||||||
| install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir | install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir | ||||||
| ifneq ($(TOOLS),) | ifneq ($(TOOLS),) | ||||||
| @@ -744,29 +850,12 @@ endif | |||||||
| ifneq ($(HELPERS-y),) | ifneq ($(HELPERS-y),) | ||||||
| 	$(call install-prog,$(HELPERS-y),$(DESTDIR)$(libexecdir)) | 	$(call install-prog,$(HELPERS-y),$(DESTDIR)$(libexecdir)) | ||||||
| endif | endif | ||||||
| ifdef CONFIG_TRACE_SYSTEMTAP |  | ||||||
| 	$(INSTALL_PROG) "scripts/qemu-trace-stap" $(DESTDIR)$(bindir) |  | ||||||
| endif |  | ||||||
| ifneq ($(BLOBS),) | ifneq ($(BLOBS),) | ||||||
| 	set -e; for x in $(BLOBS); do \ | 	set -e; for x in $(BLOBS); do \ | ||||||
| 		$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \ | 		$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \ | ||||||
| 	done | 	done | ||||||
| endif | endif | ||||||
| 	for s in $(ICON_SIZES); do \ | ifeq ($(CONFIG_GTK),m) | ||||||
| 		mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/$${s}/apps"; \ |  | ||||||
| 		$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu_$${s}.png \ |  | ||||||
| 			"$(DESTDIR)/$(qemu_icondir)/hicolor/$${s}/apps/qemu.png"; \ |  | ||||||
| 	done; \ |  | ||||||
| 	mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/32x32/apps"; \ |  | ||||||
| 	$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu_32x32.bmp \ |  | ||||||
| 		"$(DESTDIR)/$(qemu_icondir)/hicolor/32x32/apps/qemu.bmp"; \ |  | ||||||
| 	mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/scalable/apps"; \ |  | ||||||
| 	$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu.svg \ |  | ||||||
| 		"$(DESTDIR)/$(qemu_icondir)/hicolor/scalable/apps/qemu.svg" |  | ||||||
| 	mkdir -p "$(DESTDIR)/$(qemu_desktopdir)" |  | ||||||
| 	$(INSTALL_DATA) $(SRC_PATH)/ui/qemu.desktop \ |  | ||||||
| 		"$(DESTDIR)/$(qemu_desktopdir)/qemu.desktop" |  | ||||||
| ifdef CONFIG_GTK |  | ||||||
| 	$(MAKE) -C po $@ | 	$(MAKE) -C po $@ | ||||||
| endif | endif | ||||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps" | 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps" | ||||||
| @@ -860,8 +949,6 @@ fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi | |||||||
| qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi | qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi | ||||||
| qemu-ga.8: qemu-ga.texi | qemu-ga.8: qemu-ga.texi | ||||||
| docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi | docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi | ||||||
| docs/qemu-cpu-models.7: docs/qemu-cpu-models.texi |  | ||||||
| scripts/qemu-trace-stap.1: scripts/qemu-trace-stap.texi |  | ||||||
|  |  | ||||||
| html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html | html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html | ||||||
| info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info | info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info | ||||||
| @@ -870,9 +957,8 @@ txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt | |||||||
|  |  | ||||||
| qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \ | qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \ | ||||||
| 	qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \ | 	qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \ | ||||||
| 	qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \ | 	qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \ | ||||||
| 	qemu-monitor-info.texi docs/qemu-block-drivers.texi \ | 	qemu-monitor-info.texi docs/qemu-block-drivers.texi | ||||||
| 	docs/qemu-cpu-models.texi |  | ||||||
|  |  | ||||||
| docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \ | docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \ | ||||||
|     docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \ |     docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \ | ||||||
| @@ -884,18 +970,6 @@ docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \ | |||||||
|     docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \ |     docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \ | ||||||
| 	docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi | 	docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi | ||||||
|  |  | ||||||
| $(filter %.1 %.7 %.8,$(DOCS)): scripts/texi2pod.pl |  | ||||||
|  |  | ||||||
| # Reports/Analysis |  | ||||||
|  |  | ||||||
| %/coverage-report.html: |  | ||||||
| 	@mkdir -p $* |  | ||||||
| 	$(call quiet-command,\ |  | ||||||
| 		gcovr -p --html --html-details -o $@, \ |  | ||||||
| 		"GEN", "coverage-report.html") |  | ||||||
|  |  | ||||||
| .PHONY: coverage-report |  | ||||||
| coverage-report: $(CURDIR)/reports/coverage/coverage-report.html |  | ||||||
|  |  | ||||||
| ifdef CONFIG_WIN32 | ifdef CONFIG_WIN32 | ||||||
|  |  | ||||||
| @@ -976,9 +1050,6 @@ include $(SRC_PATH)/tests/vm/Makefile.include | |||||||
| help: | help: | ||||||
| 	@echo  'Generic targets:' | 	@echo  'Generic targets:' | ||||||
| 	@echo  '  all             - Build all' | 	@echo  '  all             - Build all' | ||||||
| ifdef CONFIG_MODULES |  | ||||||
| 	@echo  '  modules         - Build all modules' |  | ||||||
| endif |  | ||||||
| 	@echo  '  dir/file.o      - Build specified target only' | 	@echo  '  dir/file.o      - Build specified target only' | ||||||
| 	@echo  '  install         - Install QEMU, documentation and tools' | 	@echo  '  install         - Install QEMU, documentation and tools' | ||||||
| 	@echo  '  ctags/TAGS      - Generate tags file for editors' | 	@echo  '  ctags/TAGS      - Generate tags file for editors' | ||||||
| @@ -991,9 +1062,6 @@ endif | |||||||
| 		echo '') | 		echo '') | ||||||
| 	@echo  'Cleaning targets:' | 	@echo  'Cleaning targets:' | ||||||
| 	@echo  '  clean           - Remove most generated files but keep the config' | 	@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  '  distclean       - Remove all generated files' | ||||||
| 	@echo  '  dist            - Build a distributable tarball' | 	@echo  '  dist            - Build a distributable tarball' | ||||||
| 	@echo  '' | 	@echo  '' | ||||||
| @@ -1005,9 +1073,6 @@ endif | |||||||
| 	@echo  'Documentation targets:' | 	@echo  'Documentation targets:' | ||||||
| 	@echo  '  html info pdf txt' | 	@echo  '  html info pdf txt' | ||||||
| 	@echo  '                  - Build documentation in specified format' | 	@echo  '                  - Build documentation in specified format' | ||||||
| ifdef CONFIG_GCOV |  | ||||||
| 	@echo  '  coverage-report - Create code coverage report' |  | ||||||
| endif |  | ||||||
| 	@echo  '' | 	@echo  '' | ||||||
| ifdef CONFIG_WIN32 | ifdef CONFIG_WIN32 | ||||||
| 	@echo  'Windows targets:' | 	@echo  'Windows targets:' | ||||||
|   | |||||||
							
								
								
									
										165
									
								
								Makefile.objs
									
									
									
									
									
								
							
							
						
						
									
										165
									
								
								Makefile.objs
									
									
									
									
									
								
							| @@ -1,29 +1,69 @@ | |||||||
| QAPI_MODULES = block-core block char common crypto introspect job migration |  | ||||||
| QAPI_MODULES += misc net rdma rocker run-state sockets tpm trace transaction |  | ||||||
| QAPI_MODULES += ui |  | ||||||
|  |  | ||||||
| ####################################################################### | ####################################################################### | ||||||
| # Common libraries for tools and emulators | # Common libraries for tools and emulators | ||||||
| stub-obj-y = stubs/ crypto/ | stub-obj-y = stubs/ crypto/ | ||||||
| util-obj-y = util/ qobject/ qapi/ | util-obj-y = util/ qobject/ qapi/ | ||||||
| util-obj-y += qapi/qapi-builtin-types.o | util-obj-y += qapi/qapi-builtin-types.o | ||||||
| util-obj-y += qapi/qapi-types.o | util-obj-y += qapi/qapi-types.o | ||||||
| util-obj-y += $(QAPI_MODULES:%=qapi/qapi-types-%.o) | util-obj-y += qapi/qapi-types-block-core.o | ||||||
|  | util-obj-y += qapi/qapi-types-block.o | ||||||
|  | util-obj-y += qapi/qapi-types-char.o | ||||||
|  | util-obj-y += qapi/qapi-types-common.o | ||||||
|  | util-obj-y += qapi/qapi-types-crypto.o | ||||||
|  | util-obj-y += qapi/qapi-types-introspect.o | ||||||
|  | util-obj-y += qapi/qapi-types-migration.o | ||||||
|  | util-obj-y += qapi/qapi-types-misc.o | ||||||
|  | util-obj-y += qapi/qapi-types-net.o | ||||||
|  | util-obj-y += qapi/qapi-types-rocker.o | ||||||
|  | util-obj-y += qapi/qapi-types-run-state.o | ||||||
|  | util-obj-y += qapi/qapi-types-sockets.o | ||||||
|  | util-obj-y += qapi/qapi-types-tpm.o | ||||||
|  | util-obj-y += qapi/qapi-types-trace.o | ||||||
|  | util-obj-y += qapi/qapi-types-transaction.o | ||||||
|  | util-obj-y += qapi/qapi-types-ui.o | ||||||
| util-obj-y += qapi/qapi-builtin-visit.o | util-obj-y += qapi/qapi-builtin-visit.o | ||||||
| util-obj-y += qapi/qapi-visit.o | util-obj-y += qapi/qapi-visit.o | ||||||
| util-obj-y += $(QAPI_MODULES:%=qapi/qapi-visit-%.o) | util-obj-y += qapi/qapi-visit-block-core.o | ||||||
|  | util-obj-y += qapi/qapi-visit-block.o | ||||||
|  | util-obj-y += qapi/qapi-visit-char.o | ||||||
|  | util-obj-y += qapi/qapi-visit-common.o | ||||||
|  | util-obj-y += qapi/qapi-visit-crypto.o | ||||||
|  | util-obj-y += qapi/qapi-visit-introspect.o | ||||||
|  | util-obj-y += qapi/qapi-visit-migration.o | ||||||
|  | util-obj-y += qapi/qapi-visit-misc.o | ||||||
|  | util-obj-y += qapi/qapi-visit-net.o | ||||||
|  | util-obj-y += qapi/qapi-visit-rocker.o | ||||||
|  | util-obj-y += qapi/qapi-visit-run-state.o | ||||||
|  | util-obj-y += qapi/qapi-visit-sockets.o | ||||||
|  | util-obj-y += qapi/qapi-visit-tpm.o | ||||||
|  | util-obj-y += qapi/qapi-visit-trace.o | ||||||
|  | util-obj-y += qapi/qapi-visit-transaction.o | ||||||
|  | util-obj-y += qapi/qapi-visit-ui.o | ||||||
| util-obj-y += qapi/qapi-events.o | util-obj-y += qapi/qapi-events.o | ||||||
| util-obj-y += $(QAPI_MODULES:%=qapi/qapi-events-%.o) | util-obj-y += qapi/qapi-events-block-core.o | ||||||
|  | util-obj-y += qapi/qapi-events-block.o | ||||||
|  | util-obj-y += qapi/qapi-events-char.o | ||||||
|  | util-obj-y += qapi/qapi-events-common.o | ||||||
|  | util-obj-y += qapi/qapi-events-crypto.o | ||||||
|  | util-obj-y += qapi/qapi-events-introspect.o | ||||||
|  | util-obj-y += qapi/qapi-events-migration.o | ||||||
|  | util-obj-y += qapi/qapi-events-misc.o | ||||||
|  | util-obj-y += qapi/qapi-events-net.o | ||||||
|  | util-obj-y += qapi/qapi-events-rocker.o | ||||||
|  | util-obj-y += qapi/qapi-events-run-state.o | ||||||
|  | util-obj-y += qapi/qapi-events-sockets.o | ||||||
|  | util-obj-y += qapi/qapi-events-tpm.o | ||||||
|  | util-obj-y += qapi/qapi-events-trace.o | ||||||
|  | util-obj-y += qapi/qapi-events-transaction.o | ||||||
|  | util-obj-y += qapi/qapi-events-ui.o | ||||||
| util-obj-y += qapi/qapi-introspect.o | util-obj-y += qapi/qapi-introspect.o | ||||||
|  |  | ||||||
| chardev-obj-y = chardev/ | chardev-obj-y = chardev/ | ||||||
| slirp-obj-$(CONFIG_SLIRP) = slirp/ |  | ||||||
|  |  | ||||||
| ####################################################################### | ####################################################################### | ||||||
| # block-obj-y is code used by both qemu system emulation and qemu-img | # block-obj-y is code used by both qemu system emulation and qemu-img | ||||||
|  |  | ||||||
| block-obj-y += nbd/ | 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 += block/ scsi/ | ||||||
| block-obj-y += qemu-io-cmds.o | block-obj-y += qemu-io-cmds.o | ||||||
| block-obj-$(CONFIG_REPLICATION) += replication.o | block-obj-$(CONFIG_REPLICATION) += replication.o | ||||||
| @@ -54,7 +94,6 @@ io-obj-y = io/ | |||||||
| ifeq ($(CONFIG_SOFTMMU),y) | ifeq ($(CONFIG_SOFTMMU),y) | ||||||
| common-obj-y = blockdev.o blockdev-nbd.o block/ | common-obj-y = blockdev.o blockdev-nbd.o block/ | ||||||
| common-obj-y += bootdevice.o iothread.o | common-obj-y += bootdevice.o iothread.o | ||||||
| common-obj-y += job-qmp.o |  | ||||||
| common-obj-y += net/ | common-obj-y += net/ | ||||||
| common-obj-y += qdev-monitor.o device-hotplug.o | common-obj-y += qdev-monitor.o device-hotplug.o | ||||||
| common-obj-$(CONFIG_WIN32) += os-win32.o | common-obj-$(CONFIG_WIN32) += os-win32.o | ||||||
| @@ -80,6 +119,8 @@ common-obj-y += vl.o | |||||||
| vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS) | vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS) | ||||||
| common-obj-$(CONFIG_TPM) += tpm.o | common-obj-$(CONFIG_TPM) += tpm.o | ||||||
|  |  | ||||||
|  | common-obj-$(CONFIG_SLIRP) += slirp/ | ||||||
|  |  | ||||||
| common-obj-y += backends/ | common-obj-y += backends/ | ||||||
| common-obj-y += chardev/ | common-obj-y += chardev/ | ||||||
|  |  | ||||||
| @@ -93,7 +134,22 @@ common-obj-$(CONFIG_FDT) += device_tree.o | |||||||
| # qapi | # qapi | ||||||
|  |  | ||||||
| common-obj-y += qapi/qapi-commands.o | common-obj-y += qapi/qapi-commands.o | ||||||
| common-obj-y += $(QAPI_MODULES:%=qapi/qapi-commands-%.o) | common-obj-y += qapi/qapi-commands-block-core.o | ||||||
|  | common-obj-y += qapi/qapi-commands-block.o | ||||||
|  | common-obj-y += qapi/qapi-commands-char.o | ||||||
|  | common-obj-y += qapi/qapi-commands-common.o | ||||||
|  | common-obj-y += qapi/qapi-commands-crypto.o | ||||||
|  | common-obj-y += qapi/qapi-commands-introspect.o | ||||||
|  | common-obj-y += qapi/qapi-commands-migration.o | ||||||
|  | common-obj-y += qapi/qapi-commands-misc.o | ||||||
|  | common-obj-y += qapi/qapi-commands-net.o | ||||||
|  | common-obj-y += qapi/qapi-commands-rocker.o | ||||||
|  | common-obj-y += qapi/qapi-commands-run-state.o | ||||||
|  | common-obj-y += qapi/qapi-commands-sockets.o | ||||||
|  | common-obj-y += qapi/qapi-commands-tpm.o | ||||||
|  | common-obj-y += qapi/qapi-commands-trace.o | ||||||
|  | common-obj-y += qapi/qapi-commands-transaction.o | ||||||
|  | common-obj-y += qapi/qapi-commands-ui.o | ||||||
| common-obj-y += qapi/qapi-introspect.o | common-obj-y += qapi/qapi-introspect.o | ||||||
| common-obj-y += qmp.o hmp.o | common-obj-y += qmp.o hmp.o | ||||||
| endif | endif | ||||||
| @@ -125,7 +181,6 @@ qga-vss-dll-obj-y = qga/ | |||||||
|  |  | ||||||
| ###################################################################### | ###################################################################### | ||||||
| # contrib | # contrib | ||||||
| elf2dmp-obj-y = contrib/elf2dmp/ |  | ||||||
| ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/ | ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/ | ||||||
| ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/ | ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/ | ||||||
| libvhost-user-obj-y = contrib/libvhost-user/ | libvhost-user-obj-y = contrib/libvhost-user/ | ||||||
| @@ -133,73 +188,69 @@ vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS) | |||||||
| vhost-user-scsi.o-libs := $(LIBISCSI_LIBS) | vhost-user-scsi.o-libs := $(LIBISCSI_LIBS) | ||||||
| vhost-user-scsi-obj-y = contrib/vhost-user-scsi/ | vhost-user-scsi-obj-y = contrib/vhost-user-scsi/ | ||||||
| vhost-user-blk-obj-y = contrib/vhost-user-blk/ | vhost-user-blk-obj-y = contrib/vhost-user-blk/ | ||||||
| rdmacm-mux-obj-y = contrib/rdmacm-mux/ |  | ||||||
|  |  | ||||||
| ###################################################################### | ###################################################################### | ||||||
| trace-events-subdirs = | trace-events-subdirs = | ||||||
| trace-events-subdirs += accel/kvm | trace-events-subdirs += util | ||||||
| trace-events-subdirs += accel/tcg | trace-events-subdirs += crypto | ||||||
| trace-events-subdirs += audio | trace-events-subdirs += io | ||||||
|  | trace-events-subdirs += migration | ||||||
| trace-events-subdirs += block | trace-events-subdirs += block | ||||||
| trace-events-subdirs += chardev | 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 | ||||||
| trace-events-subdirs += hw/block/dataplane | trace-events-subdirs += hw/block/dataplane | ||||||
| trace-events-subdirs += hw/char | 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/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/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 | ||||||
| trace-events-subdirs += hw/rdma/vmw | 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/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/sparc | ||||||
| trace-events-subdirs += hw/sparc64 | trace-events-subdirs += hw/sparc64 | ||||||
| trace-events-subdirs += hw/timer | trace-events-subdirs += hw/sd | ||||||
| trace-events-subdirs += hw/tpm | trace-events-subdirs += hw/isa | ||||||
| trace-events-subdirs += hw/usb | 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/vfio | ||||||
| trace-events-subdirs += hw/virtio | trace-events-subdirs += hw/acpi | ||||||
| trace-events-subdirs += hw/watchdog | trace-events-subdirs += hw/arm | ||||||
|  | trace-events-subdirs += hw/alpha | ||||||
|  | trace-events-subdirs += hw/hppa | ||||||
| trace-events-subdirs += hw/xen | trace-events-subdirs += hw/xen | ||||||
| trace-events-subdirs += hw/gpio | trace-events-subdirs += hw/ide | ||||||
| trace-events-subdirs += io | trace-events-subdirs += hw/tpm | ||||||
| trace-events-subdirs += linux-user | trace-events-subdirs += ui | ||||||
| trace-events-subdirs += migration | trace-events-subdirs += audio | ||||||
| trace-events-subdirs += nbd |  | ||||||
| trace-events-subdirs += net | 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/arm | ||||||
| trace-events-subdirs += target/i386 | trace-events-subdirs += target/i386 | ||||||
| trace-events-subdirs += target/mips | trace-events-subdirs += target/mips | ||||||
| trace-events-subdirs += target/ppc |  | ||||||
| trace-events-subdirs += target/s390x |  | ||||||
| trace-events-subdirs += target/sparc | trace-events-subdirs += target/sparc | ||||||
| trace-events-subdirs += ui | trace-events-subdirs += target/s390x | ||||||
| trace-events-subdirs += util | 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) | 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 | ifdef CONFIG_LINUX | ||||||
| QEMU_CFLAGS += -I../linux-headers | QEMU_CFLAGS += -I../linux-headers | ||||||
| endif | 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 | ifdef CONFIG_USER_ONLY | ||||||
| # user emulator name | # user emulator name | ||||||
| @@ -36,16 +36,11 @@ endif | |||||||
| PROGS=$(QEMU_PROG) $(QEMU_PROGW) | PROGS=$(QEMU_PROG) $(QEMU_PROGW) | ||||||
| STPFILES= | 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: config-target.h-timestamp | ||||||
| config-target.h-timestamp: config-target.mak | config-target.h-timestamp: config-target.mak | ||||||
|  |  | ||||||
| ifdef CONFIG_TRACE_SYSTEMTAP | ifdef CONFIG_TRACE_SYSTEMTAP | ||||||
| stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp $(QEMU_PROG)-log.stp | stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp | ||||||
|  |  | ||||||
| ifdef CONFIG_USER_ONLY | ifdef CONFIG_USER_ONLY | ||||||
| TARGET_TYPE=user | TARGET_TYPE=user | ||||||
| @@ -84,14 +79,6 @@ $(QEMU_PROG)-simpletrace.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y) | |||||||
| 		--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \ | 		--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \ | ||||||
| 		$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp") | 		$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp") | ||||||
|  |  | ||||||
| $(QEMU_PROG)-log.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y) |  | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ |  | ||||||
| 		--group=all \ |  | ||||||
| 		--format=log-stap \ |  | ||||||
| 		--backends=$(TRACE_BACKENDS) \ |  | ||||||
| 		--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \ |  | ||||||
| 		$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-log.stp") |  | ||||||
|  |  | ||||||
| else | else | ||||||
| stap: | stap: | ||||||
| endif | endif | ||||||
| @@ -110,7 +97,7 @@ obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o | |||||||
| obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o | obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o | ||||||
| obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o | obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o | ||||||
| obj-$(CONFIG_TCG_INTERPRETER) += disas/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 += target/$(TARGET_BASE_ARCH)/ | ||||||
| obj-y += disas.o | obj-y += disas.o | ||||||
| obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o | obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o | ||||||
| @@ -151,7 +138,6 @@ obj-y += hw/ | |||||||
| obj-y += memory.o | obj-y += memory.o | ||||||
| obj-y += memory_mapping.o | obj-y += memory_mapping.o | ||||||
| obj-y += dump.o | obj-y += dump.o | ||||||
| obj-$(TARGET_X86_64) += win_dump.o |  | ||||||
| obj-y += migration/ram.o | obj-y += migration/ram.o | ||||||
| LIBS := $(libs_softmmu) $(LIBS) | LIBS := $(libs_softmmu) $(LIBS) | ||||||
|  |  | ||||||
| @@ -166,6 +152,9 @@ GENERATED_FILES += hmp-commands.h hmp-commands-info.h | |||||||
|  |  | ||||||
| endif # CONFIG_SOFTMMU | endif # CONFIG_SOFTMMU | ||||||
|  |  | ||||||
|  | # Workaround for http://gcc.gnu.org/PR55489, see configure. | ||||||
|  | %/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS) | ||||||
|  |  | ||||||
| dummy := $(call unnest-vars,,obj-y) | dummy := $(call unnest-vars,,obj-y) | ||||||
| all-obj-y := $(obj-y) | all-obj-y := $(obj-y) | ||||||
|  |  | ||||||
| @@ -173,7 +162,6 @@ target-obj-y := | |||||||
| block-obj-y := | block-obj-y := | ||||||
| common-obj-y := | common-obj-y := | ||||||
| chardev-obj-y := | chardev-obj-y := | ||||||
| slirp-obj-y := |  | ||||||
| include $(SRC_PATH)/Makefile.objs | include $(SRC_PATH)/Makefile.objs | ||||||
| dummy := $(call unnest-vars,,target-obj-y) | dummy := $(call unnest-vars,,target-obj-y) | ||||||
| target-obj-y-save := $(target-obj-y) | target-obj-y-save := $(target-obj-y) | ||||||
| @@ -186,8 +174,7 @@ dummy := $(call unnest-vars,.., \ | |||||||
|                qom-obj-y \ |                qom-obj-y \ | ||||||
|                io-obj-y \ |                io-obj-y \ | ||||||
|                common-obj-y \ |                common-obj-y \ | ||||||
|                common-obj-m \ |                common-obj-m) | ||||||
|                slirp-obj-y) |  | ||||||
| target-obj-y := $(target-obj-y-save) | target-obj-y := $(target-obj-y-save) | ||||||
| all-obj-y += $(common-obj-y) | all-obj-y += $(common-obj-y) | ||||||
| all-obj-y += $(target-obj-y) | all-obj-y += $(target-obj-y) | ||||||
| @@ -196,7 +183,6 @@ all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y) | |||||||
| all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y) | all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y) | ||||||
| all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y) | all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y) | ||||||
| all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y) | all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y) | ||||||
| all-obj-$(CONFIG_SOFTMMU) += $(slirp-obj-y) |  | ||||||
|  |  | ||||||
| $(QEMU_PROG_BUILD): config-devices.mak | $(QEMU_PROG_BUILD): config-devices.mak | ||||||
|  |  | ||||||
| @@ -235,7 +221,6 @@ ifdef CONFIG_TRACE_SYSTEMTAP | |||||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset" | 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset" | ||||||
| 	$(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp" | 	$(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp" | ||||||
| 	$(INSTALL_DATA) $(QEMU_PROG)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp" | 	$(INSTALL_DATA) $(QEMU_PROG)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp" | ||||||
| 	$(INSTALL_DATA) $(QEMU_PROG)-log.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-log.stp" |  | ||||||
| endif | endif | ||||||
|  |  | ||||||
| GENERATED_FILES += config-target.h | GENERATED_FILES += config-target.h | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								README
									
									
									
									
									
								
							| @@ -54,7 +54,7 @@ Submitting patches | |||||||
|  |  | ||||||
| The QEMU source code is maintained under the GIT version control system. | The QEMU source code is maintained under the GIT version control system. | ||||||
|  |  | ||||||
|    git clone https://git.qemu.org/git/qemu.git |    git clone git://git.qemu.org/qemu.git | ||||||
|  |  | ||||||
| When submitting patches, one common approach is to use 'git | When submitting patches, one common approach is to use 'git | ||||||
| format-patch' and/or 'git send-email' to format & send the mail to the | format-patch' and/or 'git send-email' to format & send the mail to the | ||||||
| @@ -70,7 +70,7 @@ the QEMU website | |||||||
|  |  | ||||||
| The QEMU website is also maintained under source control. | The QEMU website is also maintained under source control. | ||||||
|  |  | ||||||
|   git clone https://git.qemu.org/git/qemu-web.git |   git clone git://git.qemu.org/qemu-web.git | ||||||
|   https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/ |   https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/ | ||||||
|  |  | ||||||
| A 'git-publish' utility was created to make above process less | A 'git-publish' utility was created to make above process less | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| obj-$(CONFIG_SOFTMMU) += accel.o | obj-$(CONFIG_SOFTMMU) += accel.o | ||||||
| obj-$(CONFIG_KVM) += kvm/ | obj-y += kvm/ | ||||||
| obj-$(CONFIG_TCG) += tcg/ | obj-$(CONFIG_TCG) += tcg/ | ||||||
| obj-y += stubs/ | obj-y += stubs/ | ||||||
|   | |||||||
| @@ -34,7 +34,6 @@ | |||||||
| #include "qom/object.h" | #include "qom/object.h" | ||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
| #include "qemu/option.h" | #include "qemu/option.h" | ||||||
| #include "qapi/error.h" |  | ||||||
|  |  | ||||||
| static const TypeInfo accel_type = { | static const TypeInfo accel_type = { | ||||||
|     .name = TYPE_ACCEL, |     .name = TYPE_ACCEL, | ||||||
| @@ -69,10 +68,10 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms) | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| void configure_accelerator(MachineState *ms, const char *progname) | void configure_accelerator(MachineState *ms) | ||||||
| { | { | ||||||
|     const char *accel; |     const char *accel, *p; | ||||||
|     char **accel_list, **tmp; |     char buf[10]; | ||||||
|     int ret; |     int ret; | ||||||
|     bool accel_initialised = false; |     bool accel_initialised = false; | ||||||
|     bool init_failed = false; |     bool init_failed = false; | ||||||
| @@ -80,26 +79,17 @@ void configure_accelerator(MachineState *ms, const char *progname) | |||||||
|  |  | ||||||
|     accel = qemu_opt_get(qemu_get_machine_opts(), "accel"); |     accel = qemu_opt_get(qemu_get_machine_opts(), "accel"); | ||||||
|     if (accel == NULL) { |     if (accel == NULL) { | ||||||
|         /* Select the default accelerator */ |         /* Use the default "accelerator", tcg */ | ||||||
|         int pnlen = strlen(progname); |  | ||||||
|         if (pnlen >= 3 && g_str_equal(&progname[pnlen - 3], "kvm")) { |  | ||||||
|             /* If the program name ends with "kvm", we prefer KVM */ |  | ||||||
|             accel = "kvm:tcg"; |  | ||||||
|         } else { |  | ||||||
| #if defined(CONFIG_TCG) |  | ||||||
|         accel = "tcg"; |         accel = "tcg"; | ||||||
| #elif defined(CONFIG_KVM) |  | ||||||
|             accel = "kvm"; |  | ||||||
| #else |  | ||||||
| #error "No default accelerator available" |  | ||||||
| #endif |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     accel_list = g_strsplit(accel, ":", 0); |     p = accel; | ||||||
|  |     while (!accel_initialised && *p != '\0') { | ||||||
|     for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) { |         if (*p == ':') { | ||||||
|         acc = accel_find(*tmp); |             p++; | ||||||
|  |         } | ||||||
|  |         p = get_opt_name(buf, sizeof(buf), p, ':'); | ||||||
|  |         acc = accel_find(buf); | ||||||
|         if (!acc) { |         if (!acc) { | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
| @@ -117,7 +107,6 @@ void configure_accelerator(MachineState *ms, const char *progname) | |||||||
|             accel_initialised = true; |             accel_initialised = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     g_strfreev(accel_list); |  | ||||||
|  |  | ||||||
|     if (!accel_initialised) { |     if (!accel_initialised) { | ||||||
|         if (!init_failed) { |         if (!init_failed) { | ||||||
| @@ -131,13 +120,10 @@ void configure_accelerator(MachineState *ms, const char *progname) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void accel_setup_post(MachineState *ms) | void accel_register_compat_props(AccelState *accel) | ||||||
| { | { | ||||||
|     AccelState *accel = ms->accelerator; |     AccelClass *class = ACCEL_GET_CLASS(accel); | ||||||
|     AccelClass *acc = ACCEL_GET_CLASS(accel); |     register_compat_props_array(class->global_props); | ||||||
|     if (acc->setup_post) { |  | ||||||
|         acc->setup_post(ms, accel); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void register_accel_types(void) | static void register_accel_types(void) | ||||||
|   | |||||||
| @@ -1,2 +1 @@ | |||||||
| obj-y += kvm-all.o | obj-$(CONFIG_KVM) += kvm-all.o | ||||||
| obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o |  | ||||||
|   | |||||||
| @@ -38,8 +38,6 @@ | |||||||
| #include "qemu/event_notifier.h" | #include "qemu/event_notifier.h" | ||||||
| #include "trace.h" | #include "trace.h" | ||||||
| #include "hw/irq.h" | #include "hw/irq.h" | ||||||
| #include "sysemu/sev.h" |  | ||||||
| #include "sysemu/balloon.h" |  | ||||||
|  |  | ||||||
| #include "hw/boards.h" | #include "hw/boards.h" | ||||||
|  |  | ||||||
| @@ -79,14 +77,13 @@ struct KVMState | |||||||
|     int fd; |     int fd; | ||||||
|     int vmfd; |     int vmfd; | ||||||
|     int coalesced_mmio; |     int coalesced_mmio; | ||||||
|     int coalesced_pio; |  | ||||||
|     struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; |     struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; | ||||||
|     bool coalesced_flush_in_progress; |     bool coalesced_flush_in_progress; | ||||||
|     int vcpu_events; |     int vcpu_events; | ||||||
|     int robust_singlestep; |     int robust_singlestep; | ||||||
|     int debugregs; |     int debugregs; | ||||||
| #ifdef KVM_CAP_SET_GUEST_DEBUG | #ifdef KVM_CAP_SET_GUEST_DEBUG | ||||||
|     QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints; |     struct kvm_sw_breakpoint_head kvm_sw_breakpoints; | ||||||
| #endif | #endif | ||||||
|     int many_ioeventfds; |     int many_ioeventfds; | ||||||
|     int intx_set_mask; |     int intx_set_mask; | ||||||
| @@ -102,14 +99,10 @@ struct KVMState | |||||||
|     int nr_allocated_irq_routes; |     int nr_allocated_irq_routes; | ||||||
|     unsigned long *used_gsi_bitmap; |     unsigned long *used_gsi_bitmap; | ||||||
|     unsigned int gsi_count; |     unsigned int gsi_count; | ||||||
|     QTAILQ_HEAD(, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE]; |     QTAILQ_HEAD(msi_hashtab, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE]; | ||||||
| #endif | #endif | ||||||
|     KVMMemoryListener memory_listener; |     KVMMemoryListener memory_listener; | ||||||
|     QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus; |     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; | KVMState *kvm_state; | ||||||
| @@ -145,26 +138,6 @@ int kvm_get_max_memslots(void) | |||||||
|     return s->nr_slots; |     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) | static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml) | ||||||
| { | { | ||||||
|     KVMState *s = kvm_state; |     KVMState *s = kvm_state; | ||||||
| @@ -258,7 +231,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram, | |||||||
|     return 0; |     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; |     KVMState *s = kvm_state; | ||||||
|     struct kvm_userspace_memory_region mem; |     struct kvm_userspace_memory_region mem; | ||||||
| @@ -269,7 +242,7 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo | |||||||
|     mem.userspace_addr = (unsigned long)slot->ram; |     mem.userspace_addr = (unsigned long)slot->ram; | ||||||
|     mem.flags = slot->flags; |     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 |         /* Set the slot size to 0 before setting the slot to the desired | ||||||
|          * value. This is needed based on KVM commit 75d61fbc. */ |          * value. This is needed based on KVM commit 75d61fbc. */ | ||||||
|         mem.memory_size = 0; |         mem.memory_size = 0; | ||||||
| @@ -277,7 +250,6 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo | |||||||
|     } |     } | ||||||
|     mem.memory_size = slot->memory_size; |     mem.memory_size = slot->memory_size; | ||||||
|     ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); |     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, |     trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr, | ||||||
|                               mem.memory_size, mem.userspace_addr, ret); |                               mem.memory_size, mem.userspace_addr, ret); | ||||||
|     return ret; |     return ret; | ||||||
| @@ -394,14 +366,17 @@ static int kvm_mem_flags(MemoryRegion *mr) | |||||||
| static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem, | static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem, | ||||||
|                                  MemoryRegion *mr) |                                  MemoryRegion *mr) | ||||||
| { | { | ||||||
|  |     int old_flags; | ||||||
|  |  | ||||||
|  |     old_flags = mem->flags; | ||||||
|     mem->flags = kvm_mem_flags(mr); |     mem->flags = kvm_mem_flags(mr); | ||||||
|  |  | ||||||
|     /* If nothing changed effectively, no need to issue ioctl */ |     /* If nothing changed effectively, no need to issue ioctl */ | ||||||
|     if (mem->flags == mem->old_flags) { |     if (mem->flags == old_flags) { | ||||||
|         return 0; |         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, | static int kvm_section_update_flags(KVMMemoryListener *kml, | ||||||
| @@ -561,45 +536,6 @@ static void kvm_uncoalesce_mmio_region(MemoryListener *listener, | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void kvm_coalesce_pio_add(MemoryListener *listener, |  | ||||||
|                                 MemoryRegionSection *section, |  | ||||||
|                                 hwaddr start, hwaddr size) |  | ||||||
| { |  | ||||||
|     KVMState *s = kvm_state; |  | ||||||
|  |  | ||||||
|     if (s->coalesced_pio) { |  | ||||||
|         struct kvm_coalesced_mmio_zone zone; |  | ||||||
|  |  | ||||||
|         zone.addr = start; |  | ||||||
|         zone.size = size; |  | ||||||
|         zone.pio = 1; |  | ||||||
|  |  | ||||||
|         (void)kvm_vm_ioctl(s, KVM_REGISTER_COALESCED_MMIO, &zone); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void kvm_coalesce_pio_del(MemoryListener *listener, |  | ||||||
|                                 MemoryRegionSection *section, |  | ||||||
|                                 hwaddr start, hwaddr size) |  | ||||||
| { |  | ||||||
|     KVMState *s = kvm_state; |  | ||||||
|  |  | ||||||
|     if (s->coalesced_pio) { |  | ||||||
|         struct kvm_coalesced_mmio_zone zone; |  | ||||||
|  |  | ||||||
|         zone.addr = start; |  | ||||||
|         zone.size = size; |  | ||||||
|         zone.pio = 1; |  | ||||||
|  |  | ||||||
|         (void)kvm_vm_ioctl(s, KVM_UNREGISTER_COALESCED_MMIO, &zone); |  | ||||||
|      } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static MemoryListener kvm_coalesced_pio_listener = { |  | ||||||
|     .coalesced_io_add = kvm_coalesce_pio_add, |  | ||||||
|     .coalesced_io_del = kvm_coalesce_pio_del, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| int kvm_check_extension(KVMState *s, unsigned int extension) | int kvm_check_extension(KVMState *s, unsigned int extension) | ||||||
| { | { | ||||||
|     int ret; |     int ret; | ||||||
| @@ -657,8 +593,6 @@ static int kvm_set_ioeventfd_mmio(int fd, hwaddr addr, uint32_t val, | |||||||
|         .fd = fd, |         .fd = fd, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     trace_kvm_set_ioeventfd_mmio(fd, (uint64_t)addr, val, assign, size, |  | ||||||
|                                  datamatch); |  | ||||||
|     if (!kvm_enabled()) { |     if (!kvm_enabled()) { | ||||||
|         return -ENOSYS; |         return -ENOSYS; | ||||||
|     } |     } | ||||||
| @@ -690,7 +624,6 @@ static int kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint16_t val, | |||||||
|         .fd = fd, |         .fd = fd, | ||||||
|     }; |     }; | ||||||
|     int r; |     int r; | ||||||
|     trace_kvm_set_ioeventfd_pio(fd, addr, val, assign, size, datamatch); |  | ||||||
|     if (!kvm_enabled()) { |     if (!kvm_enabled()) { | ||||||
|         return -ENOSYS; |         return -ENOSYS; | ||||||
|     } |     } | ||||||
| @@ -797,8 +730,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, | |||||||
|  |  | ||||||
|         /* unregister the slot */ |         /* unregister the slot */ | ||||||
|         mem->memory_size = 0; |         mem->memory_size = 0; | ||||||
|         mem->flags = 0; |         err = kvm_set_user_memory_region(kml, mem); | ||||||
|         err = kvm_set_user_memory_region(kml, mem, false); |  | ||||||
|         if (err) { |         if (err) { | ||||||
|             fprintf(stderr, "%s: error unregistering slot: %s\n", |             fprintf(stderr, "%s: error unregistering slot: %s\n", | ||||||
|                     __func__, strerror(-err)); |                     __func__, strerror(-err)); | ||||||
| @@ -814,7 +746,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, | |||||||
|     mem->ram = ram; |     mem->ram = ram; | ||||||
|     mem->flags = kvm_mem_flags(mr); |     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) { |     if (err) { | ||||||
|         fprintf(stderr, "%s: error registering slot: %s\n", __func__, |         fprintf(stderr, "%s: error registering slot: %s\n", __func__, | ||||||
|                 strerror(-err)); |                 strerror(-err)); | ||||||
| @@ -1659,8 +1591,6 @@ static int kvm_init(MachineState *ms) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO); |     s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO); | ||||||
|     s->coalesced_pio = s->coalesced_mmio && |  | ||||||
|                        kvm_check_extension(s, KVM_CAP_COALESCED_PIO); |  | ||||||
|  |  | ||||||
| #ifdef KVM_CAP_VCPU_EVENTS | #ifdef KVM_CAP_VCPU_EVENTS | ||||||
|     s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS); |     s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS); | ||||||
| @@ -1684,8 +1614,10 @@ static int kvm_init(MachineState *ms) | |||||||
|         s->irq_set_ioctl = KVM_IRQ_LINE_STATUS; |         s->irq_set_ioctl = KVM_IRQ_LINE_STATUS; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | #ifdef KVM_CAP_READONLY_MEM | ||||||
|     kvm_readonly_mem_allowed = |     kvm_readonly_mem_allowed = | ||||||
|         (kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0); |         (kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|     kvm_eventfds_allowed = |     kvm_eventfds_allowed = | ||||||
|         (kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0); |         (kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0); | ||||||
| @@ -1704,20 +1636,6 @@ static int kvm_init(MachineState *ms) | |||||||
|  |  | ||||||
|     kvm_state = s; |     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); |     ret = kvm_arch_init(ms, s); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         goto err; |         goto err; | ||||||
| @@ -1731,22 +1649,17 @@ static int kvm_init(MachineState *ms) | |||||||
|         s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add; |         s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add; | ||||||
|         s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del; |         s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del; | ||||||
|     } |     } | ||||||
|     s->memory_listener.listener.coalesced_io_add = kvm_coalesce_mmio_region; |     s->memory_listener.listener.coalesced_mmio_add = kvm_coalesce_mmio_region; | ||||||
|     s->memory_listener.listener.coalesced_io_del = kvm_uncoalesce_mmio_region; |     s->memory_listener.listener.coalesced_mmio_del = kvm_uncoalesce_mmio_region; | ||||||
|  |  | ||||||
|     kvm_memory_listener_register(s, &s->memory_listener, |     kvm_memory_listener_register(s, &s->memory_listener, | ||||||
|                                  &address_space_memory, 0); |                                  &address_space_memory, 0); | ||||||
|     memory_listener_register(&kvm_io_listener, |     memory_listener_register(&kvm_io_listener, | ||||||
|                              &address_space_io); |                              &address_space_io); | ||||||
|     memory_listener_register(&kvm_coalesced_pio_listener, |  | ||||||
|                              &address_space_io); |  | ||||||
|  |  | ||||||
|     s->many_ioeventfds = kvm_check_many_ioeventfds(); |     s->many_ioeventfds = kvm_check_many_ioeventfds(); | ||||||
|  |  | ||||||
|     s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU); |     s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU); | ||||||
|     if (!s->sync_mmu) { |  | ||||||
|         qemu_balloon_inhibit(true); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
| @@ -1825,13 +1738,7 @@ void kvm_flush_coalesced_mmio_buffer(void) | |||||||
|  |  | ||||||
|             ent = &ring->coalesced_mmio[ring->first]; |             ent = &ring->coalesced_mmio[ring->first]; | ||||||
|  |  | ||||||
|             if (ent->pio == 1) { |  | ||||||
|                 address_space_rw(&address_space_io, ent->phys_addr, |  | ||||||
|                                  MEMTXATTRS_UNSPECIFIED, ent->data, |  | ||||||
|                                  ent->len, true); |  | ||||||
|             } else { |  | ||||||
|             cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len); |             cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len); | ||||||
|             } |  | ||||||
|             smp_wmb(); |             smp_wmb(); | ||||||
|             ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX; |             ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -1,26 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU SEV stub |  | ||||||
|  * |  | ||||||
|  * Copyright Advanced Micro Devices 2018 |  | ||||||
|  * |  | ||||||
|  * Authors: |  | ||||||
|  *      Brijesh Singh <brijesh.singh@amd.com> |  | ||||||
|  * |  | ||||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or later. |  | ||||||
|  * See the COPYING file in the top-level directory. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "qemu-common.h" |  | ||||||
| #include "sysemu/sev.h" |  | ||||||
|  |  | ||||||
| int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len) |  | ||||||
| { |  | ||||||
|     abort(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void *sev_guest_init(const char *id) |  | ||||||
| { |  | ||||||
|     return NULL; |  | ||||||
| } |  | ||||||
| @@ -12,7 +12,5 @@ kvm_irqchip_commit_routes(void) "" | |||||||
| kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d" | kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d" | ||||||
| kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d" | kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d" | ||||||
| kvm_irqchip_release_virq(int virq) "virq %d" | kvm_irqchip_release_virq(int virq) "virq %d" | ||||||
| kvm_set_ioeventfd_mmio(int fd, uint64_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%" PRIx64 " val=0x%x assign: %d size: %d match: %d" |  | ||||||
| kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%x val=0x%x assign: %d size: %d match: %d" |  | ||||||
| kvm_set_user_memory(uint32_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d" | kvm_set_user_memory(uint32_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -105,16 +105,6 @@ int kvm_on_sigbus(int code, void *addr) | |||||||
|     return 1; |     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 | #ifndef CONFIG_USER_ONLY | ||||||
| int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev) | 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) | void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) | ||||||
| { | { | ||||||
| } | } | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ | |||||||
|  * This library is free software; you can redistribute it and/or |  * This library is free software; you can redistribute it and/or | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  * License as published by the Free Software Foundation; either |  * License as published by the Free Software Foundation; either | ||||||
|  * version 2.1 of the License, or (at your option) any later version. |  * version 2 of the License, or (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This library is distributed in the hope that it will be useful, |  * This library is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -18,37 +18,26 @@ | |||||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>. |  * License along with this library; if not, see <http://www.gnu.org/licenses/>. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "trace/mem.h" |  | ||||||
|  |  | ||||||
| #if DATA_SIZE == 16 | #if DATA_SIZE == 16 | ||||||
| # define SUFFIX     o | # define SUFFIX     o | ||||||
| # define DATA_TYPE  Int128 | # define DATA_TYPE  Int128 | ||||||
| # define BSWAP      bswap128 | # define BSWAP      bswap128 | ||||||
| # define SHIFT      4 |  | ||||||
| #elif DATA_SIZE == 8 | #elif DATA_SIZE == 8 | ||||||
| # define SUFFIX     q | # define SUFFIX     q | ||||||
| # define DATA_TYPE  uint64_t | # define DATA_TYPE  uint64_t | ||||||
| # define SDATA_TYPE int64_t |  | ||||||
| # define BSWAP      bswap64 | # define BSWAP      bswap64 | ||||||
| # define SHIFT      3 |  | ||||||
| #elif DATA_SIZE == 4 | #elif DATA_SIZE == 4 | ||||||
| # define SUFFIX     l | # define SUFFIX     l | ||||||
| # define DATA_TYPE  uint32_t | # define DATA_TYPE  uint32_t | ||||||
| # define SDATA_TYPE int32_t |  | ||||||
| # define BSWAP      bswap32 | # define BSWAP      bswap32 | ||||||
| # define SHIFT      2 |  | ||||||
| #elif DATA_SIZE == 2 | #elif DATA_SIZE == 2 | ||||||
| # define SUFFIX     w | # define SUFFIX     w | ||||||
| # define DATA_TYPE  uint16_t | # define DATA_TYPE  uint16_t | ||||||
| # define SDATA_TYPE int16_t |  | ||||||
| # define BSWAP      bswap16 | # define BSWAP      bswap16 | ||||||
| # define SHIFT      1 |  | ||||||
| #elif DATA_SIZE == 1 | #elif DATA_SIZE == 1 | ||||||
| # define SUFFIX     b | # define SUFFIX     b | ||||||
| # define DATA_TYPE  uint8_t | # define DATA_TYPE  uint8_t | ||||||
| # define SDATA_TYPE int8_t |  | ||||||
| # define BSWAP | # define BSWAP | ||||||
| # define SHIFT      0 |  | ||||||
| #else | #else | ||||||
| # error unsupported data size | # error unsupported data size | ||||||
| #endif | #endif | ||||||
| @@ -59,37 +48,14 @@ | |||||||
| # define ABI_TYPE  uint32_t | # define ABI_TYPE  uint32_t | ||||||
| #endif | #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 | /* Define host-endian atomic operations.  Note that END is used within | ||||||
|    the ATOMIC_NAME macro, and redefined below.  */ |    the ATOMIC_NAME macro, and redefined below.  */ | ||||||
| #if DATA_SIZE == 1 | #if DATA_SIZE == 1 | ||||||
| # define END | # define END | ||||||
| # define MEND _be /* either le or be would be fine */ |  | ||||||
| #elif defined(HOST_WORDS_BIGENDIAN) | #elif defined(HOST_WORDS_BIGENDIAN) | ||||||
| # define END  _be | # define END  _be | ||||||
| # define MEND _be |  | ||||||
| #else | #else | ||||||
| # define END  _le | # define END  _le | ||||||
| # define MEND _le |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | ||||||
| @@ -97,27 +63,17 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | |||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|     DATA_TYPE ret; |     DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); | ||||||
|  |  | ||||||
|     ATOMIC_TRACE_RMW; |  | ||||||
| #if DATA_SIZE == 16 |  | ||||||
|     ret = atomic16_cmpxchg(haddr, cmpv, newv); |  | ||||||
| #else |  | ||||||
|     ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); |  | ||||||
| #endif |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| #if DATA_SIZE >= 16 | #if DATA_SIZE >= 16 | ||||||
| #if HAVE_ATOMIC128 |  | ||||||
| ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) | ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) | ||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|  |     __atomic_load(haddr, &val, __ATOMIC_RELAXED); | ||||||
|     ATOMIC_TRACE_LD; |  | ||||||
|     val = atomic16_read(haddr); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
|     return val; |     return val; | ||||||
| } | } | ||||||
| @@ -127,22 +83,16 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, | |||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|  |     __atomic_store(haddr, &val, __ATOMIC_RELAXED); | ||||||
|     ATOMIC_TRACE_ST; |  | ||||||
|     atomic16_set(haddr, val); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
| } | } | ||||||
| #endif |  | ||||||
| #else | #else | ||||||
| ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, | ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, | ||||||
|                            ABI_TYPE val EXTRA_ARGS) |                            ABI_TYPE val EXTRA_ARGS) | ||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|     DATA_TYPE ret; |     DATA_TYPE ret = atomic_xchg__nocheck(haddr, val); | ||||||
|  |  | ||||||
|     ATOMIC_TRACE_RMW; |  | ||||||
|     ret = atomic_xchg__nocheck(haddr, val); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| @@ -153,10 +103,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ | |||||||
| {                                                                   \ | {                                                                   \ | ||||||
|     ATOMIC_MMU_DECLS;                                               \ |     ATOMIC_MMU_DECLS;                                               \ | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \ |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \ | ||||||
|     DATA_TYPE ret;                                                  \ |     DATA_TYPE ret = atomic_##X(haddr, val);                         \ | ||||||
|                                                                     \ |  | ||||||
|     ATOMIC_TRACE_RMW;                                               \ |  | ||||||
|     ret = atomic_##X(haddr, val);                                   \ |  | ||||||
|     ATOMIC_MMU_CLEANUP;                                             \ |     ATOMIC_MMU_CLEANUP;                                             \ | ||||||
|     return ret;                                                     \ |     return ret;                                                     \ | ||||||
| } | } | ||||||
| @@ -171,48 +118,9 @@ GEN_ATOMIC_HELPER(or_fetch) | |||||||
| GEN_ATOMIC_HELPER(xor_fetch) | GEN_ATOMIC_HELPER(xor_fetch) | ||||||
|  |  | ||||||
| #undef GEN_ATOMIC_HELPER | #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 */ | #endif /* DATA SIZE >= 16 */ | ||||||
|  |  | ||||||
| #undef END | #undef END | ||||||
| #undef MEND |  | ||||||
|  |  | ||||||
| #if DATA_SIZE > 1 | #if DATA_SIZE > 1 | ||||||
|  |  | ||||||
| @@ -220,10 +128,8 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX,  DATA_TYPE, new) | |||||||
|    within the ATOMIC_NAME macro.  */ |    within the ATOMIC_NAME macro.  */ | ||||||
| #ifdef HOST_WORDS_BIGENDIAN | #ifdef HOST_WORDS_BIGENDIAN | ||||||
| # define END  _le | # define END  _le | ||||||
| # define MEND _le |  | ||||||
| #else | #else | ||||||
| # define END  _be | # define END  _be | ||||||
| # define MEND _be |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | ||||||
| @@ -231,27 +137,17 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | |||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|     DATA_TYPE ret; |     DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); | ||||||
|  |  | ||||||
|     ATOMIC_TRACE_RMW; |  | ||||||
| #if DATA_SIZE == 16 |  | ||||||
|     ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv)); |  | ||||||
| #else |  | ||||||
|     ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); |  | ||||||
| #endif |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
|     return BSWAP(ret); |     return BSWAP(ret); | ||||||
| } | } | ||||||
|  |  | ||||||
| #if DATA_SIZE >= 16 | #if DATA_SIZE >= 16 | ||||||
| #if HAVE_ATOMIC128 |  | ||||||
| ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) | ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) | ||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|  |     __atomic_load(haddr, &val, __ATOMIC_RELAXED); | ||||||
|     ATOMIC_TRACE_LD; |  | ||||||
|     val = atomic16_read(haddr); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
|     return BSWAP(val); |     return BSWAP(val); | ||||||
| } | } | ||||||
| @@ -261,23 +157,17 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, | |||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|  |  | ||||||
|     ATOMIC_TRACE_ST; |  | ||||||
|     val = BSWAP(val); |     val = BSWAP(val); | ||||||
|     atomic16_set(haddr, val); |     __atomic_store(haddr, &val, __ATOMIC_RELAXED); | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
| } | } | ||||||
| #endif |  | ||||||
| #else | #else | ||||||
| ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, | ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, | ||||||
|                            ABI_TYPE val EXTRA_ARGS) |                            ABI_TYPE val EXTRA_ARGS) | ||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|     ABI_TYPE ret; |     ABI_TYPE ret = atomic_xchg__nocheck(haddr, BSWAP(val)); | ||||||
|  |  | ||||||
|     ATOMIC_TRACE_RMW; |  | ||||||
|     ret = atomic_xchg__nocheck(haddr, BSWAP(val)); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
|     return BSWAP(ret); |     return BSWAP(ret); | ||||||
| } | } | ||||||
| @@ -288,10 +178,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ | |||||||
| {                                                                   \ | {                                                                   \ | ||||||
|     ATOMIC_MMU_DECLS;                                               \ |     ATOMIC_MMU_DECLS;                                               \ | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \ |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \ | ||||||
|     DATA_TYPE ret;                                                  \ |     DATA_TYPE ret = atomic_##X(haddr, BSWAP(val));                  \ | ||||||
|                                                                     \ |  | ||||||
|     ATOMIC_TRACE_RMW;                                               \ |  | ||||||
|     ret = atomic_##X(haddr, BSWAP(val));                            \ |  | ||||||
|     ATOMIC_MMU_CLEANUP;                                             \ |     ATOMIC_MMU_CLEANUP;                                             \ | ||||||
|     return BSWAP(ret);                                              \ |     return BSWAP(ret);                                              \ | ||||||
| } | } | ||||||
| @@ -305,64 +192,54 @@ GEN_ATOMIC_HELPER(xor_fetch) | |||||||
|  |  | ||||||
| #undef GEN_ATOMIC_HELPER | #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 | /* Note that for addition, we need to use a separate cmpxchg loop instead | ||||||
|    of bswaps for the reverse-host-endian helpers.  */ |    of bswaps for the reverse-host-endian helpers.  */ | ||||||
| #define ADD(X, Y)   (X + Y) | ABI_TYPE ATOMIC_NAME(fetch_add)(CPUArchState *env, target_ulong addr, | ||||||
| GEN_ATOMIC_HELPER_FN(fetch_add, ADD, DATA_TYPE, old) |                          ABI_TYPE val EXTRA_ARGS) | ||||||
| GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new) | { | ||||||
| #undef ADD |     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 */ | #endif /* DATA_SIZE >= 16 */ | ||||||
|  |  | ||||||
| #undef END | #undef END | ||||||
| #undef MEND |  | ||||||
| #endif /* DATA_SIZE > 1 */ | #endif /* DATA_SIZE > 1 */ | ||||||
|  |  | ||||||
| #undef ATOMIC_TRACE_ST |  | ||||||
| #undef ATOMIC_TRACE_LD |  | ||||||
| #undef ATOMIC_TRACE_RMW |  | ||||||
|  |  | ||||||
| #undef BSWAP | #undef BSWAP | ||||||
| #undef ABI_TYPE | #undef ABI_TYPE | ||||||
| #undef DATA_TYPE | #undef DATA_TYPE | ||||||
| #undef SDATA_TYPE |  | ||||||
| #undef SUFFIX | #undef SUFFIX | ||||||
| #undef DATA_SIZE | #undef DATA_SIZE | ||||||
| #undef SHIFT |  | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * This library is free software; you can redistribute it and/or |  * This library is free software; you can redistribute it and/or | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  * License as published by the Free Software Foundation; either |  * License as published by the Free Software Foundation; either | ||||||
|  * version 2.1 of the License, or (at your option) any later version. |  * version 2 of the License, or (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This library is distributed in the hope that it will be useful, |  * This library is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -27,8 +27,10 @@ bool tcg_allowed; | |||||||
| /* exit the current TB, but without causing any exception to be raised */ | /* exit the current TB, but without causing any exception to be raised */ | ||||||
| void cpu_loop_exit_noexc(CPUState *cpu) | void cpu_loop_exit_noexc(CPUState *cpu) | ||||||
| { | { | ||||||
|  |     /* XXX: restore cpu registers saved in host registers */ | ||||||
|  |  | ||||||
|     cpu->exception_index = -1; |     cpu->exception_index = -1; | ||||||
|     cpu_loop_exit(cpu); |     siglongjmp(cpu->jmp_env, 1); | ||||||
| } | } | ||||||
|  |  | ||||||
| #if defined(CONFIG_SOFTMMU) | #if defined(CONFIG_SOFTMMU) | ||||||
| @@ -63,17 +65,15 @@ void cpu_reloading_memory_map(void) | |||||||
|  |  | ||||||
| void cpu_loop_exit(CPUState *cpu) | void cpu_loop_exit(CPUState *cpu) | ||||||
| { | { | ||||||
|     /* Undo the setting in cpu_tb_exec.  */ |  | ||||||
|     cpu->can_do_io = 1; |  | ||||||
|     siglongjmp(cpu->jmp_env, 1); |     siglongjmp(cpu->jmp_env, 1); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) | void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) | ||||||
| { | { | ||||||
|     if (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) | void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc) | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * This library is free software; you can redistribute it and/or |  * This library is free software; you can redistribute it and/or | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  * License as published by the Free Software Foundation; either |  * License as published by the Free Software Foundation; either | ||||||
|  * version 2.1 of the License, or (at your option) any later version. |  * version 2 of the License, or (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This library is distributed in the hope that it will be useful, |  * This library is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -25,6 +25,7 @@ | |||||||
| #include "qemu/atomic.h" | #include "qemu/atomic.h" | ||||||
| #include "sysemu/qtest.h" | #include "sysemu/qtest.h" | ||||||
| #include "qemu/timer.h" | #include "qemu/timer.h" | ||||||
|  | #include "exec/address-spaces.h" | ||||||
| #include "qemu/rcu.h" | #include "qemu/rcu.h" | ||||||
| #include "exec/tb-hash.h" | #include "exec/tb-hash.h" | ||||||
| #include "exec/tb-lookup.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) |     if (qemu_loglevel_mask(CPU_LOG_TB_CPU) | ||||||
|         && qemu_log_in_addr_range(itb->pc)) { |         && qemu_log_in_addr_range(itb->pc)) { | ||||||
|         qemu_log_lock(); |         qemu_log_lock(); | ||||||
|         int flags = 0; |  | ||||||
|         if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) { |  | ||||||
|             flags |= CPU_DUMP_FPU; |  | ||||||
|         } |  | ||||||
| #if defined(TARGET_I386) | #if defined(TARGET_I386) | ||||||
|         flags |= CPU_DUMP_CCOP; |         log_cpu_state(cpu, CPU_DUMP_CCOP); | ||||||
|  | #else | ||||||
|  |         log_cpu_state(cpu, 0); | ||||||
| #endif | #endif | ||||||
|         log_cpu_state(cpu, flags); |  | ||||||
|         qemu_log_unlock(); |         qemu_log_unlock(); | ||||||
|     } |     } | ||||||
| #endif /* DEBUG_DISAS */ | #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.  */ |        We only end up here when an existing TB is too long.  */ | ||||||
|     cflags |= MIN(max_cycles, CF_COUNT_MASK); |     cflags |= MIN(max_cycles, CF_COUNT_MASK); | ||||||
|  |  | ||||||
|     mmap_lock(); |     tb_lock(); | ||||||
|     tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, |     tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, | ||||||
|                      orig_tb->flags, cflags); |                      orig_tb->flags, cflags); | ||||||
|     tb->orig_tb = orig_tb; |     tb->orig_tb = orig_tb; | ||||||
|     mmap_unlock(); |     tb_unlock(); | ||||||
|  |  | ||||||
|     /* execute the generated code */ |     /* execute the generated code */ | ||||||
|     trace_exec_tb_nocache(tb, tb->pc); |     trace_exec_tb_nocache(tb, tb->pc); | ||||||
|     cpu_tb_exec(cpu, tb); |     cpu_tb_exec(cpu, tb); | ||||||
|  |  | ||||||
|     mmap_lock(); |     tb_lock(); | ||||||
|     tb_phys_invalidate(tb, -1); |     tb_phys_invalidate(tb, -1); | ||||||
|     mmap_unlock(); |     tb_remove(tb); | ||||||
|     tcg_tb_remove(tb); |     tb_unlock(); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -244,7 +242,12 @@ void cpu_exec_step_atomic(CPUState *cpu) | |||||||
|         tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); |         tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); | ||||||
|         if (tb == NULL) { |         if (tb == NULL) { | ||||||
|             mmap_lock(); |             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 = tb_gen_code(cpu, pc, cs_base, flags, cflags); | ||||||
|  |             } | ||||||
|  |             tb_unlock(); | ||||||
|             mmap_unlock(); |             mmap_unlock(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -259,17 +262,15 @@ void cpu_exec_step_atomic(CPUState *cpu) | |||||||
|         cpu_tb_exec(cpu, tb); |         cpu_tb_exec(cpu, tb); | ||||||
|         cc->cpu_exec_exit(cpu); |         cc->cpu_exec_exit(cpu); | ||||||
|     } else { |     } 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 |          * The mmap_lock is dropped by tb_gen_code if it runs out of | ||||||
|          * memory. |          * memory. | ||||||
|          */ |          */ | ||||||
| #ifndef CONFIG_SOFTMMU | #ifndef CONFIG_SOFTMMU | ||||||
|         tcg_debug_assert(!have_mmap_lock()); |         tcg_debug_assert(!have_mmap_lock()); | ||||||
| #endif | #endif | ||||||
|         if (qemu_mutex_iothread_locked()) { |         tb_lock_reset(); | ||||||
|             qemu_mutex_unlock_iothread(); |  | ||||||
|         } |  | ||||||
|         assert_no_pages_locked(); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (in_exclusive_region) { |     if (in_exclusive_region) { | ||||||
| @@ -292,7 +293,7 @@ struct tb_desc { | |||||||
|     uint32_t trace_vcpu_dstate; |     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 TranslationBlock *tb = p; | ||||||
|     const struct tb_desc *desc = d; |     const struct tb_desc *desc = d; | ||||||
| @@ -335,12 +336,9 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, | |||||||
|     desc.trace_vcpu_dstate = *cpu->trace_dstate; |     desc.trace_vcpu_dstate = *cpu->trace_dstate; | ||||||
|     desc.pc = pc; |     desc.pc = pc; | ||||||
|     phys_pc = get_page_addr_code(desc.env, pc); |     phys_pc = get_page_addr_code(desc.env, pc); | ||||||
|     if (phys_pc == -1) { |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|     desc.phys_page1 = phys_pc & TARGET_PAGE_MASK; |     desc.phys_page1 = phys_pc & TARGET_PAGE_MASK; | ||||||
|     h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate); |     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) | void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) | ||||||
| @@ -354,43 +352,28 @@ void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Called with tb_lock held.  */ | ||||||
| static inline void tb_add_jump(TranslationBlock *tb, int n, | static inline void tb_add_jump(TranslationBlock *tb, int n, | ||||||
|                                TranslationBlock *tb_next) |                                TranslationBlock *tb_next) | ||||||
| { | { | ||||||
|     uintptr_t old; |  | ||||||
|  |  | ||||||
|     assert(n < ARRAY_SIZE(tb->jmp_list_next)); |     assert(n < ARRAY_SIZE(tb->jmp_list_next)); | ||||||
|     qemu_spin_lock(&tb_next->jmp_lock); |     if (tb->jmp_list_next[n]) { | ||||||
|  |         /* Another thread has already done this while we were | ||||||
|     /* make sure the destination TB is valid */ |          * outside of the lock; nothing to do in this case */ | ||||||
|     if (tb_next->cflags & CF_INVALID) { |         return; | ||||||
|         goto out_unlock_next; |  | ||||||
|     } |     } | ||||||
|     /* 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, |     qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc, | ||||||
|                            "Linking TBs %p [" TARGET_FMT_lx |                            "Linking TBs %p [" TARGET_FMT_lx | ||||||
|                            "] index %d -> %p [" TARGET_FMT_lx "]\n", |                            "] index %d -> %p [" TARGET_FMT_lx "]\n", | ||||||
|                            tb->tc.ptr, tb->pc, n, |                            tb->tc.ptr, tb->pc, n, | ||||||
|                            tb_next->tc.ptr, tb_next->pc); |                            tb_next->tc.ptr, tb_next->pc); | ||||||
|     return; |  | ||||||
|  |  | ||||||
|  out_unlock_next: |     /* patch the native jump address */ | ||||||
|     qemu_spin_unlock(&tb_next->jmp_lock); |     tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr); | ||||||
|     return; |  | ||||||
|  |     /* 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, | static inline TranslationBlock *tb_find(CPUState *cpu, | ||||||
| @@ -400,11 +383,27 @@ static inline TranslationBlock *tb_find(CPUState *cpu, | |||||||
|     TranslationBlock *tb; |     TranslationBlock *tb; | ||||||
|     target_ulong cs_base, pc; |     target_ulong cs_base, pc; | ||||||
|     uint32_t flags; |     uint32_t flags; | ||||||
|  |     bool acquired_tb_lock = false; | ||||||
|  |  | ||||||
|     tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); |     tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); | ||||||
|     if (tb == NULL) { |     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(); |         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); |             tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         mmap_unlock(); |         mmap_unlock(); | ||||||
|         /* We add the TB in the virtual pc hash table for the fast lookup */ |         /* 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); |         atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb); | ||||||
| @@ -419,9 +418,18 @@ static inline TranslationBlock *tb_find(CPUState *cpu, | |||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
|     /* See if we can patch the calling TB. */ |     /* See if we can patch the calling TB. */ | ||||||
|     if (last_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); |             tb_add_jump(last_tb, tb_exit, tb); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |     if (acquired_tb_lock) { | ||||||
|  |         tb_unlock(); | ||||||
|  |     } | ||||||
|     return tb; |     return tb; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -577,7 +585,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, | |||||||
|         else { |         else { | ||||||
|             if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { |             if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { | ||||||
|                 replay_interrupt(); |                 replay_interrupt(); | ||||||
|                 cpu->exception_index = -1; |  | ||||||
|                 *last_tb = NULL; |                 *last_tb = NULL; | ||||||
|             } |             } | ||||||
|             /* The target hook may have updated the 'cpu->interrupt_request'; |             /* The target hook may have updated the 'cpu->interrupt_request'; | ||||||
| @@ -599,9 +606,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, | |||||||
|     if (unlikely(atomic_read(&cpu->exit_request) |     if (unlikely(atomic_read(&cpu->exit_request) | ||||||
|         || (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) { |         || (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) { | ||||||
|         atomic_set(&cpu->exit_request, 0); |         atomic_set(&cpu->exit_request, 0); | ||||||
|         if (cpu->exception_index == -1) { |  | ||||||
|         cpu->exception_index = EXCP_INTERRUPT; |         cpu->exception_index = EXCP_INTERRUPT; | ||||||
|         } |  | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -696,13 +701,11 @@ int cpu_exec(CPUState *cpu) | |||||||
|         g_assert(cpu == current_cpu); |         g_assert(cpu == current_cpu); | ||||||
|         g_assert(cc == CPU_GET_CLASS(cpu)); |         g_assert(cc == CPU_GET_CLASS(cpu)); | ||||||
| #endif /* buggy compiler */ | #endif /* buggy compiler */ | ||||||
| #ifndef CONFIG_SOFTMMU |         cpu->can_do_io = 1; | ||||||
|         tcg_debug_assert(!have_mmap_lock()); |         tb_lock_reset(); | ||||||
| #endif |  | ||||||
|         if (qemu_mutex_iothread_locked()) { |         if (qemu_mutex_iothread_locked()) { | ||||||
|             qemu_mutex_unlock_iothread(); |             qemu_mutex_unlock_iothread(); | ||||||
|         } |         } | ||||||
|         assert_no_pages_locked(); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* if an exception is pending, we execute it here */ |     /* if an exception is pending, we execute it here */ | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -11,7 +11,7 @@ | |||||||
|  * This library is free software; you can redistribute it and/or |  * This library is free software; you can redistribute it and/or | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  * License as published by the Free Software Foundation; either |  * License as published by the Free Software Foundation; either | ||||||
|  * version 2.1 of the License, or (at your option) any later version. |  * version 2 of the License, or (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This library is distributed in the hope that it will be useful, |  * This library is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -98,23 +98,19 @@ | |||||||
| static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env, | static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env, | ||||||
|                                               size_t mmu_idx, size_t index, |                                               size_t mmu_idx, size_t index, | ||||||
|                                               target_ulong addr, |                                               target_ulong addr, | ||||||
|                                               uintptr_t retaddr, |                                               uintptr_t retaddr) | ||||||
|                                               bool recheck, |  | ||||||
|                                               MMUAccessType access_type) |  | ||||||
| { | { | ||||||
|     CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; |     CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; | ||||||
|     return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, recheck, |     return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, DATA_SIZE); | ||||||
|                     access_type, DATA_SIZE); |  | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, | WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, | ||||||
|                             TCGMemOpIdx oi, uintptr_t retaddr) |                             TCGMemOpIdx oi, uintptr_t retaddr) | ||||||
| { | { | ||||||
|     uintptr_t mmu_idx = get_mmuidx(oi); |     unsigned mmu_idx = get_mmuidx(oi); | ||||||
|     uintptr_t index = tlb_index(env, mmu_idx, addr); |     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); |     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; | ||||||
|     target_ulong tlb_addr = entry->ADDR_READ; |  | ||||||
|     unsigned a_bits = get_alignment_bits(get_memop(oi)); |     unsigned a_bits = get_alignment_bits(get_memop(oi)); | ||||||
|     uintptr_t haddr; |     uintptr_t haddr; | ||||||
|     DATA_TYPE res; |     DATA_TYPE res; | ||||||
| @@ -125,14 +121,13 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* If the TLB entry is for a different page, reload and try again.  */ |     /* If 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)) { |         if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { | ||||||
|             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, |             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, | ||||||
|                      mmu_idx, retaddr); |                      mmu_idx, retaddr); | ||||||
|             index = tlb_index(env, mmu_idx, addr); |  | ||||||
|             entry = tlb_entry(env, mmu_idx, addr); |  | ||||||
|         } |         } | ||||||
|         tlb_addr = entry->ADDR_READ; |         tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Handle an IO access.  */ |     /* Handle an IO access.  */ | ||||||
| @@ -143,9 +138,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, | |||||||
|  |  | ||||||
|         /* ??? Note that the io helpers always read data in the target |         /* ??? Note that the io helpers always read data in the target | ||||||
|            byte ordering.  We should push the LE/BE request down into io.  */ |            byte ordering.  We should push the LE/BE request down into io.  */ | ||||||
|         res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr, |         res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr); | ||||||
|                                     tlb_addr & TLB_RECHECK, |  | ||||||
|                                     READ_ACCESS_TYPE); |  | ||||||
|         res = TGT_LE(res); |         res = TGT_LE(res); | ||||||
|         return res; |         return res; | ||||||
|     } |     } | ||||||
| @@ -169,7 +162,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, | |||||||
|         return res; |         return res; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     haddr = addr + entry->addend; |     haddr = addr + env->tlb_table[mmu_idx][index].addend; | ||||||
| #if DATA_SIZE == 1 | #if DATA_SIZE == 1 | ||||||
|     res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr); |     res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr); | ||||||
| #else | #else | ||||||
| @@ -182,10 +175,9 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, | |||||||
| WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, | WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, | ||||||
|                             TCGMemOpIdx oi, uintptr_t retaddr) |                             TCGMemOpIdx oi, uintptr_t retaddr) | ||||||
| { | { | ||||||
|     uintptr_t mmu_idx = get_mmuidx(oi); |     unsigned mmu_idx = get_mmuidx(oi); | ||||||
|     uintptr_t index = tlb_index(env, mmu_idx, addr); |     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); |     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; | ||||||
|     target_ulong tlb_addr = entry->ADDR_READ; |  | ||||||
|     unsigned a_bits = get_alignment_bits(get_memop(oi)); |     unsigned a_bits = get_alignment_bits(get_memop(oi)); | ||||||
|     uintptr_t haddr; |     uintptr_t haddr; | ||||||
|     DATA_TYPE res; |     DATA_TYPE res; | ||||||
| @@ -196,14 +188,13 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* If the TLB entry is for a different page, reload and try again.  */ |     /* If 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)) { |         if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { | ||||||
|             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, |             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, | ||||||
|                      mmu_idx, retaddr); |                      mmu_idx, retaddr); | ||||||
|             index = tlb_index(env, mmu_idx, addr); |  | ||||||
|             entry = tlb_entry(env, mmu_idx, addr); |  | ||||||
|         } |         } | ||||||
|         tlb_addr = entry->ADDR_READ; |         tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Handle an IO access.  */ |     /* Handle an IO access.  */ | ||||||
| @@ -214,9 +205,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, | |||||||
|  |  | ||||||
|         /* ??? Note that the io helpers always read data in the target |         /* ??? Note that the io helpers always read data in the target | ||||||
|            byte ordering.  We should push the LE/BE request down into io.  */ |            byte ordering.  We should push the LE/BE request down into io.  */ | ||||||
|         res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr, |         res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr); | ||||||
|                                     tlb_addr & TLB_RECHECK, |  | ||||||
|                                     READ_ACCESS_TYPE); |  | ||||||
|         res = TGT_BE(res); |         res = TGT_BE(res); | ||||||
|         return res; |         return res; | ||||||
|     } |     } | ||||||
| @@ -240,7 +229,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, | |||||||
|         return res; |         return res; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     haddr = addr + entry->addend; |     haddr = addr + env->tlb_table[mmu_idx][index].addend; | ||||||
|     res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr); |     res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr); | ||||||
|     return res; |     return res; | ||||||
| } | } | ||||||
| @@ -270,21 +259,18 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env, | |||||||
|                                           size_t mmu_idx, size_t index, |                                           size_t mmu_idx, size_t index, | ||||||
|                                           DATA_TYPE val, |                                           DATA_TYPE val, | ||||||
|                                           target_ulong addr, |                                           target_ulong addr, | ||||||
|                                           uintptr_t retaddr, |                                           uintptr_t retaddr) | ||||||
|                                           bool recheck) |  | ||||||
| { | { | ||||||
|     CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; |     CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; | ||||||
|     return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr, |     return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr, DATA_SIZE); | ||||||
|                      recheck, DATA_SIZE); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||||
|                        TCGMemOpIdx oi, uintptr_t retaddr) |                        TCGMemOpIdx oi, uintptr_t retaddr) | ||||||
| { | { | ||||||
|     uintptr_t mmu_idx = get_mmuidx(oi); |     unsigned mmu_idx = get_mmuidx(oi); | ||||||
|     uintptr_t index = tlb_index(env, mmu_idx, addr); |     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); |     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; | ||||||
|     target_ulong tlb_addr = tlb_addr_write(entry); |  | ||||||
|     unsigned a_bits = get_alignment_bits(get_memop(oi)); |     unsigned a_bits = get_alignment_bits(get_memop(oi)); | ||||||
|     uintptr_t haddr; |     uintptr_t haddr; | ||||||
|  |  | ||||||
| @@ -294,14 +280,13 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* If the TLB entry is for a different page, reload and try again.  */ |     /* If 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)) { |         if (!VICTIM_TLB_HIT(addr_write, addr)) { | ||||||
|             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, |             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, | ||||||
|                      mmu_idx, retaddr); |                      mmu_idx, retaddr); | ||||||
|             index = tlb_index(env, mmu_idx, addr); |  | ||||||
|             entry = tlb_entry(env, mmu_idx, addr); |  | ||||||
|         } |         } | ||||||
|         tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK; |         tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Handle an IO access.  */ |     /* Handle an IO access.  */ | ||||||
| @@ -313,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 |         /* ??? Note that the io helpers always read data in the target | ||||||
|            byte ordering.  We should push the LE/BE request down into io.  */ |            byte ordering.  We should push the LE/BE request down into io.  */ | ||||||
|         val = TGT_LE(val); |         val = TGT_LE(val); | ||||||
|         glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, |         glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr); | ||||||
|                                retaddr, tlb_addr & TLB_RECHECK); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -322,16 +306,16 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | |||||||
|     if (DATA_SIZE > 1 |     if (DATA_SIZE > 1 | ||||||
|         && unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1 |         && unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1 | ||||||
|                      >= TARGET_PAGE_SIZE)) { |                      >= TARGET_PAGE_SIZE)) { | ||||||
|         int i; |         int i, index2; | ||||||
|         target_ulong page2; |         target_ulong page2, tlb_addr2; | ||||||
|         CPUTLBEntry *entry2; |  | ||||||
|     do_unaligned_access: |     do_unaligned_access: | ||||||
|         /* Ensure the second page is in the TLB.  Note that the first page |         /* Ensure the second page is in the TLB.  Note that the first page | ||||||
|            is already guaranteed to be filled, and that the second page |            is already guaranteed to be filled, and that the second page | ||||||
|            cannot evict the first.  */ |            cannot evict the first.  */ | ||||||
|         page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; |         page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; | ||||||
|         entry2 = tlb_entry(env, mmu_idx, page2); |         index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|         if (!tlb_hit_page(tlb_addr_write(entry2), page2) |         tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; | ||||||
|  |         if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK)) | ||||||
|             && !VICTIM_TLB_HIT(addr_write, page2)) { |             && !VICTIM_TLB_HIT(addr_write, page2)) { | ||||||
|             tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE, |             tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE, | ||||||
|                      mmu_idx, retaddr); |                      mmu_idx, retaddr); | ||||||
| @@ -349,7 +333,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     haddr = addr + entry->addend; |     haddr = addr + env->tlb_table[mmu_idx][index].addend; | ||||||
| #if DATA_SIZE == 1 | #if DATA_SIZE == 1 | ||||||
|     glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val); |     glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val); | ||||||
| #else | #else | ||||||
| @@ -361,10 +345,9 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | |||||||
| void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||||
|                        TCGMemOpIdx oi, uintptr_t retaddr) |                        TCGMemOpIdx oi, uintptr_t retaddr) | ||||||
| { | { | ||||||
|     uintptr_t mmu_idx = get_mmuidx(oi); |     unsigned mmu_idx = get_mmuidx(oi); | ||||||
|     uintptr_t index = tlb_index(env, mmu_idx, addr); |     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); |     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; | ||||||
|     target_ulong tlb_addr = tlb_addr_write(entry); |  | ||||||
|     unsigned a_bits = get_alignment_bits(get_memop(oi)); |     unsigned a_bits = get_alignment_bits(get_memop(oi)); | ||||||
|     uintptr_t haddr; |     uintptr_t haddr; | ||||||
|  |  | ||||||
| @@ -374,14 +357,13 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* If the TLB entry is for a different page, reload and try again.  */ |     /* If 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)) { |         if (!VICTIM_TLB_HIT(addr_write, addr)) { | ||||||
|             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, |             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, | ||||||
|                      mmu_idx, retaddr); |                      mmu_idx, retaddr); | ||||||
|             index = tlb_index(env, mmu_idx, addr); |  | ||||||
|             entry = tlb_entry(env, mmu_idx, addr); |  | ||||||
|         } |         } | ||||||
|         tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK; |         tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Handle an IO access.  */ |     /* Handle an IO access.  */ | ||||||
| @@ -393,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 |         /* ??? Note that the io helpers always read data in the target | ||||||
|            byte ordering.  We should push the LE/BE request down into io.  */ |            byte ordering.  We should push the LE/BE request down into io.  */ | ||||||
|         val = TGT_BE(val); |         val = TGT_BE(val); | ||||||
|         glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr, |         glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr); | ||||||
|                                tlb_addr & TLB_RECHECK); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -402,16 +383,16 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | |||||||
|     if (DATA_SIZE > 1 |     if (DATA_SIZE > 1 | ||||||
|         && unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1 |         && unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1 | ||||||
|                      >= TARGET_PAGE_SIZE)) { |                      >= TARGET_PAGE_SIZE)) { | ||||||
|         int i; |         int i, index2; | ||||||
|         target_ulong page2; |         target_ulong page2, tlb_addr2; | ||||||
|         CPUTLBEntry *entry2; |  | ||||||
|     do_unaligned_access: |     do_unaligned_access: | ||||||
|         /* Ensure the second page is in the TLB.  Note that the first page |         /* Ensure the second page is in the TLB.  Note that the first page | ||||||
|            is already guaranteed to be filled, and that the second page |            is already guaranteed to be filled, and that the second page | ||||||
|            cannot evict the first.  */ |            cannot evict the first.  */ | ||||||
|         page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; |         page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; | ||||||
|         entry2 = tlb_entry(env, mmu_idx, page2); |         index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|         if (!tlb_hit_page(tlb_addr_write(entry2), page2) |         tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; | ||||||
|  |         if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK)) | ||||||
|             && !VICTIM_TLB_HIT(addr_write, page2)) { |             && !VICTIM_TLB_HIT(addr_write, page2)) { | ||||||
|             tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE, |             tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE, | ||||||
|                      mmu_idx, retaddr); |                      mmu_idx, retaddr); | ||||||
| @@ -429,7 +410,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     haddr = addr + entry->addend; |     haddr = addr + env->tlb_table[mmu_idx][index].addend; | ||||||
|     glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val); |     glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val); | ||||||
| } | } | ||||||
| #endif /* DATA_SIZE > 1 */ | #endif /* DATA_SIZE > 1 */ | ||||||
|   | |||||||
| @@ -51,7 +51,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask) | |||||||
|     if (!qemu_cpu_is_self(cpu)) { |     if (!qemu_cpu_is_self(cpu)) { | ||||||
|         qemu_cpu_kick(cpu); |         qemu_cpu_kick(cpu); | ||||||
|     } else { |     } else { | ||||||
|         atomic_set(&cpu->icount_decr.u16.high, -1); |         cpu->icount_decr.u16.high = -1; | ||||||
|         if (use_icount && |         if (use_icount && | ||||||
|             !cpu->can_do_io |             !cpu->can_do_io | ||||||
|             && (mask & ~old_mask) != 0) { |             && (mask & ~old_mask) != 0) { | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * This library is free software; you can redistribute it and/or |  * This library is free software; you can redistribute it and/or | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  * License as published by the Free Software Foundation; either |  * License as published by the Free Software Foundation; either | ||||||
|  * version 2.1 of the License, or (at your option) any later version. |  * version 2 of the License, or (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This library is distributed in the hope that it will be useful, |  * This library is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -512,39 +512,6 @@ void HELPER(gvec_orc)(void *d, void *a, void *b, uint32_t desc) | |||||||
|     clear_high(d, oprsz, desc); |     clear_high(d, oprsz, desc); | ||||||
| } | } | ||||||
|  |  | ||||||
| void HELPER(gvec_nand)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) & *(vec64 *)(b + i)); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_nor)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) | *(vec64 *)(b + i)); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_eqv)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) ^ *(vec64 *)(b + i)); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc) | void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc) | ||||||
| { | { | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |     intptr_t oprsz = simd_oprsz(desc); | ||||||
| @@ -738,7 +705,7 @@ void HELPER(NAME)(void *d, void *a, void *b, uint32_t desc)                \ | |||||||
| {                                                                          \ | {                                                                          \ | ||||||
|     intptr_t oprsz = simd_oprsz(desc);                                     \ |     intptr_t oprsz = simd_oprsz(desc);                                     \ | ||||||
|     intptr_t i;                                                            \ |     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));  \ |         *(TYPE *)(d + i) = DO_CMP0(*(TYPE *)(a + i) OP *(TYPE *)(b + i));  \ | ||||||
|     }                                                                      \ |     }                                                                      \ | ||||||
|     clear_high(d, oprsz, desc);                                            \ |     clear_high(d, oprsz, desc);                                            \ | ||||||
| @@ -1028,227 +995,3 @@ void HELPER(gvec_ussub64)(void *d, void *a, void *b, uint32_t desc) | |||||||
|     } |     } | ||||||
|     clear_high(d, oprsz, desc); |     clear_high(d, oprsz, desc); | ||||||
| } | } | ||||||
|  |  | ||||||
| void HELPER(gvec_smin8)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(int8_t)) { |  | ||||||
|         int8_t aa = *(int8_t *)(a + i); |  | ||||||
|         int8_t bb = *(int8_t *)(b + i); |  | ||||||
|         int8_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(int8_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smin16)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(int16_t)) { |  | ||||||
|         int16_t aa = *(int16_t *)(a + i); |  | ||||||
|         int16_t bb = *(int16_t *)(b + i); |  | ||||||
|         int16_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(int16_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smin32)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(int32_t)) { |  | ||||||
|         int32_t aa = *(int32_t *)(a + i); |  | ||||||
|         int32_t bb = *(int32_t *)(b + i); |  | ||||||
|         int32_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(int32_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smin64)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(int64_t)) { |  | ||||||
|         int64_t aa = *(int64_t *)(a + i); |  | ||||||
|         int64_t bb = *(int64_t *)(b + i); |  | ||||||
|         int64_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(int64_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smax8)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(int8_t)) { |  | ||||||
|         int8_t aa = *(int8_t *)(a + i); |  | ||||||
|         int8_t bb = *(int8_t *)(b + i); |  | ||||||
|         int8_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(int8_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smax16)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(int16_t)) { |  | ||||||
|         int16_t aa = *(int16_t *)(a + i); |  | ||||||
|         int16_t bb = *(int16_t *)(b + i); |  | ||||||
|         int16_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(int16_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smax32)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(int32_t)) { |  | ||||||
|         int32_t aa = *(int32_t *)(a + i); |  | ||||||
|         int32_t bb = *(int32_t *)(b + i); |  | ||||||
|         int32_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(int32_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smax64)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(int64_t)) { |  | ||||||
|         int64_t aa = *(int64_t *)(a + i); |  | ||||||
|         int64_t bb = *(int64_t *)(b + i); |  | ||||||
|         int64_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(int64_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umin8)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint8_t)) { |  | ||||||
|         uint8_t aa = *(uint8_t *)(a + i); |  | ||||||
|         uint8_t bb = *(uint8_t *)(b + i); |  | ||||||
|         uint8_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(uint8_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umin16)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint16_t)) { |  | ||||||
|         uint16_t aa = *(uint16_t *)(a + i); |  | ||||||
|         uint16_t bb = *(uint16_t *)(b + i); |  | ||||||
|         uint16_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(uint16_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umin32)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint32_t)) { |  | ||||||
|         uint32_t aa = *(uint32_t *)(a + i); |  | ||||||
|         uint32_t bb = *(uint32_t *)(b + i); |  | ||||||
|         uint32_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(uint32_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umin64)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint64_t)) { |  | ||||||
|         uint64_t aa = *(uint64_t *)(a + i); |  | ||||||
|         uint64_t bb = *(uint64_t *)(b + i); |  | ||||||
|         uint64_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(uint64_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umax8)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint8_t)) { |  | ||||||
|         uint8_t aa = *(uint8_t *)(a + i); |  | ||||||
|         uint8_t bb = *(uint8_t *)(b + i); |  | ||||||
|         uint8_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(uint8_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umax16)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint16_t)) { |  | ||||||
|         uint16_t aa = *(uint16_t *)(a + i); |  | ||||||
|         uint16_t bb = *(uint16_t *)(b + i); |  | ||||||
|         uint16_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(uint16_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umax32)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint32_t)) { |  | ||||||
|         uint32_t aa = *(uint32_t *)(a + i); |  | ||||||
|         uint32_t bb = *(uint32_t *)(b + i); |  | ||||||
|         uint32_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(uint32_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umax64)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint64_t)) { |  | ||||||
|         uint64_t aa = *(uint64_t *)(a + i); |  | ||||||
|         uint64_t bb = *(uint64_t *)(b + i); |  | ||||||
|         uint64_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(uint64_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -125,19 +125,11 @@ GEN_ATOMIC_HELPERS(fetch_add) | |||||||
| GEN_ATOMIC_HELPERS(fetch_and) | GEN_ATOMIC_HELPERS(fetch_and) | ||||||
| GEN_ATOMIC_HELPERS(fetch_or) | GEN_ATOMIC_HELPERS(fetch_or) | ||||||
| GEN_ATOMIC_HELPERS(fetch_xor) | 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(add_fetch) | ||||||
| GEN_ATOMIC_HELPERS(and_fetch) | GEN_ATOMIC_HELPERS(and_fetch) | ||||||
| GEN_ATOMIC_HELPERS(or_fetch) | GEN_ATOMIC_HELPERS(or_fetch) | ||||||
| GEN_ATOMIC_HELPERS(xor_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) | GEN_ATOMIC_HELPERS(xchg) | ||||||
|  |  | ||||||
| @@ -200,26 +192,6 @@ DEF_HELPER_FLAGS_4(gvec_ussub16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | |||||||
| DEF_HELPER_FLAGS_4(gvec_ussub32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_ussub32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_ussub64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_ussub64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_neg8, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | DEF_HELPER_FLAGS_3(gvec_neg8, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_3(gvec_neg16, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | DEF_HELPER_FLAGS_3(gvec_neg16, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_3(gvec_neg32, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | DEF_HELPER_FLAGS_3(gvec_neg32, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | ||||||
| @@ -231,9 +203,6 @@ DEF_HELPER_FLAGS_4(gvec_or, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | |||||||
| DEF_HELPER_FLAGS_4(gvec_xor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_xor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_andc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_andc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_orc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_orc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_nand, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_nor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_eqv, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_ands, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) | DEF_HELPER_FLAGS_4(gvec_ands, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_xors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) | DEF_HELPER_FLAGS_4(gvec_xors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -6,7 +6,7 @@ | |||||||
|  * This library is free software; you can redistribute it and/or |  * This library is free software; you can redistribute it and/or | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  * License as published by the Free Software Foundation; either |  * License as published by the Free Software Foundation; either | ||||||
|  * version 2.1 of the License, or (at your option) any later version. |  * version 2 of the License, or (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This library is distributed in the hope that it will be useful, |  * This library is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -23,13 +23,10 @@ | |||||||
|  |  | ||||||
|  |  | ||||||
| /* translate-all.c */ | /* translate-all.c */ | ||||||
| struct page_collection *page_collection_lock(tb_page_addr_t start, | void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len); | ||||||
|                                              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_range(tb_page_addr_t start, tb_page_addr_t end, | void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, | ||||||
|                                    int is_cpu_write_access); |                                    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); | void tb_check_watchpoint(CPUState *cpu); | ||||||
|  |  | ||||||
| #ifdef CONFIG_USER_ONLY | #ifdef CONFIG_USER_ONLY | ||||||
|   | |||||||
| @@ -34,7 +34,7 @@ void translator_loop_temp_check(DisasContextBase *db) | |||||||
| void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | ||||||
|                      CPUState *cpu, TranslationBlock *tb) |                      CPUState *cpu, TranslationBlock *tb) | ||||||
| { | { | ||||||
|     int bp_insn = 0; |     int max_insns; | ||||||
|  |  | ||||||
|     /* Initialize DisasContext */ |     /* Initialize DisasContext */ | ||||||
|     db->tb = tb; |     db->tb = tb; | ||||||
| @@ -45,18 +45,18 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | |||||||
|     db->singlestep_enabled = cpu->singlestep_enabled; |     db->singlestep_enabled = cpu->singlestep_enabled; | ||||||
|  |  | ||||||
|     /* Instruction counting */ |     /* Instruction counting */ | ||||||
|     db->max_insns = tb_cflags(db->tb) & CF_COUNT_MASK; |     max_insns = tb_cflags(db->tb) & CF_COUNT_MASK; | ||||||
|     if (db->max_insns == 0) { |     if (max_insns == 0) { | ||||||
|         db->max_insns = CF_COUNT_MASK; |         max_insns = CF_COUNT_MASK; | ||||||
|     } |     } | ||||||
|     if (db->max_insns > TCG_MAX_INSNS) { |     if (max_insns > TCG_MAX_INSNS) { | ||||||
|         db->max_insns = TCG_MAX_INSNS; |         max_insns = TCG_MAX_INSNS; | ||||||
|     } |     } | ||||||
|     if (db->singlestep_enabled || singlestep) { |     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 */ |     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */ | ||||||
|  |  | ||||||
|     /* Reset the temp count so that we can identify leaks */ |     /* Reset the temp count so that we can identify leaks */ | ||||||
| @@ -73,13 +73,11 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | |||||||
|         tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */ |         tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */ | ||||||
|  |  | ||||||
|         /* Pass breakpoint hits to target for further processing */ |         /* Pass breakpoint hits to target for further processing */ | ||||||
|         if (!db->singlestep_enabled |         if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) { | ||||||
|             && unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) { |  | ||||||
|             CPUBreakpoint *bp; |             CPUBreakpoint *bp; | ||||||
|             QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { |             QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { | ||||||
|                 if (bp->pc == db->pc_next) { |                 if (bp->pc == db->pc_next) { | ||||||
|                     if (ops->breakpoint_check(db, cpu, bp)) { |                     if (ops->breakpoint_check(db, cpu, bp)) { | ||||||
|                         bp_insn = 1; |  | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| @@ -97,8 +95,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | |||||||
|            update db->pc_next and db->is_jmp to indicate what should be |            update db->pc_next and db->is_jmp to indicate what should be | ||||||
|            done next -- either exiting this loop or locate the start of |            done next -- either exiting this loop or locate the start of | ||||||
|            the next instruction.  */ |            the next instruction.  */ | ||||||
|         if (db->num_insns == db->max_insns |         if (db->num_insns == max_insns && (tb_cflags(db->tb) & CF_LAST_IO)) { | ||||||
|             && (tb_cflags(db->tb) & CF_LAST_IO)) { |  | ||||||
|             /* Accept I/O on the last instruction.  */ |             /* Accept I/O on the last instruction.  */ | ||||||
|             gen_io_start(); |             gen_io_start(); | ||||||
|             ops->translate_insn(db, cpu); |             ops->translate_insn(db, cpu); | ||||||
| @@ -114,7 +111,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | |||||||
|  |  | ||||||
|         /* Stop translation if the output buffer is full, |         /* Stop translation if the output buffer is full, | ||||||
|            or we have executed all of the allowed instructions.  */ |            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; |             db->is_jmp = DISAS_TOO_MANY; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| @@ -122,7 +119,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | |||||||
|  |  | ||||||
|     /* Emit code to exit the TB, as indicated by db->is_jmp.  */ |     /* Emit code to exit the TB, as indicated by db->is_jmp.  */ | ||||||
|     ops->tb_stop(db, cpu); |     ops->tb_stop(db, cpu); | ||||||
|     gen_tb_end(db->tb, db->num_insns - bp_insn); |     gen_tb_end(db->tb, db->num_insns); | ||||||
|  |  | ||||||
|     /* The disas_log hook may use these values rather than recompute.  */ |     /* The disas_log hook may use these values rather than recompute.  */ | ||||||
|     db->tb->size = db->pc_next - db->pc_first; |     db->tb->size = db->pc_next - db->pc_first; | ||||||
|   | |||||||
| @@ -2,9 +2,6 @@ | |||||||
| #include "qemu-common.h" | #include "qemu-common.h" | ||||||
| #include "qom/cpu.h" | #include "qom/cpu.h" | ||||||
| #include "sysemu/replay.h" | #include "sysemu/replay.h" | ||||||
| #include "sysemu/sysemu.h" |  | ||||||
|  |  | ||||||
| bool enable_cpu_pm = false; |  | ||||||
|  |  | ||||||
| void cpu_resume(CPUState *cpu) | void cpu_resume(CPUState *cpu) | ||||||
| { | { | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * This library is free software; you can redistribute it and/or |  * This library is free software; you can redistribute it and/or | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  * License as published by the Free Software Foundation; either |  * License as published by the Free Software Foundation; either | ||||||
|  * version 2.1 of the License, or (at your option) any later version. |  * version 2 of the License, or (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This library is distributed in the hope that it will be useful, |  * This library is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -25,7 +25,6 @@ | |||||||
| #include "exec/cpu_ldst.h" | #include "exec/cpu_ldst.h" | ||||||
| #include "translate-all.h" | #include "translate-all.h" | ||||||
| #include "exec/helper-proto.h" | #include "exec/helper-proto.h" | ||||||
| #include "qemu/atomic128.h" |  | ||||||
|  |  | ||||||
| #undef EAX | #undef EAX | ||||||
| #undef ECX | #undef ECX | ||||||
| @@ -169,7 +168,7 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Now we have a real cpu fault.  */ |     /* Now we have a real cpu fault.  */ | ||||||
|     cpu_restore_state(cpu, pc, true); |     cpu_restore_state(cpu, pc); | ||||||
|  |  | ||||||
|     sigprocmask(SIG_SETMASK, old_set, NULL); |     sigprocmask(SIG_SETMASK, old_set, NULL); | ||||||
|     cpu_loop_exit(cpu); |     cpu_loop_exit(cpu); | ||||||
| @@ -479,66 +478,28 @@ int cpu_signal_handler(int host_signum, void *pinfo, | |||||||
|  |  | ||||||
| #elif defined(__aarch64__) | #elif defined(__aarch64__) | ||||||
|  |  | ||||||
| #ifndef ESR_MAGIC |  | ||||||
| /* Pre-3.16 kernel headers don't have these, so provide fallback definitions */ |  | ||||||
| #define ESR_MAGIC 0x45535201 |  | ||||||
| struct esr_context { |  | ||||||
|     struct _aarch64_ctx head; |  | ||||||
|     uint64_t esr; |  | ||||||
| }; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc) |  | ||||||
| { |  | ||||||
|     return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr) |  | ||||||
| { |  | ||||||
|     return (struct _aarch64_ctx *)((char *)hdr + hdr->size); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int cpu_signal_handler(int host_signum, void *pinfo, void *puc) | int cpu_signal_handler(int host_signum, void *pinfo, void *puc) | ||||||
| { | { | ||||||
|     siginfo_t *info = pinfo; |     siginfo_t *info = pinfo; | ||||||
|     ucontext_t *uc = puc; |     ucontext_t *uc = puc; | ||||||
|     uintptr_t pc = uc->uc_mcontext.pc; |     uintptr_t pc = uc->uc_mcontext.pc; | ||||||
|     bool is_write; |  | ||||||
|     struct _aarch64_ctx *hdr; |  | ||||||
|     struct esr_context const *esrctx = NULL; |  | ||||||
|  |  | ||||||
|     /* Find the esr_context, which has the WnR bit in it */ |  | ||||||
|     for (hdr = first_ctx(uc); hdr->magic; hdr = next_ctx(hdr)) { |  | ||||||
|         if (hdr->magic == ESR_MAGIC) { |  | ||||||
|             esrctx = (struct esr_context const *)hdr; |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (esrctx) { |  | ||||||
|         /* For data aborts ESR.EC is 0b10010x: then bit 6 is the WnR bit */ |  | ||||||
|         uint64_t esr = esrctx->esr; |  | ||||||
|         is_write = extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1; |  | ||||||
|     } else { |  | ||||||
|         /* |  | ||||||
|          * Fall back to parsing instructions; will only be needed |  | ||||||
|          * for really ancient (pre-3.16) kernels. |  | ||||||
|          */ |  | ||||||
|     uint32_t insn = *(uint32_t *)pc; |     uint32_t insn = *(uint32_t *)pc; | ||||||
|  |     bool is_write; | ||||||
|  |  | ||||||
|         is_write = ((insn & 0xbfff0000) == 0x0c000000   /* C3.3.1 */ |     /* XXX: need kernel patch to get write flag faster.  */ | ||||||
|  |     is_write = (   (insn & 0xbfff0000) == 0x0c000000   /* C3.3.1 */ | ||||||
|                 || (insn & 0xbfe00000) == 0x0c800000   /* C3.3.2 */ |                 || (insn & 0xbfe00000) == 0x0c800000   /* C3.3.2 */ | ||||||
|                 || (insn & 0xbfdf0000) == 0x0d000000   /* C3.3.3 */ |                 || (insn & 0xbfdf0000) == 0x0d000000   /* C3.3.3 */ | ||||||
|                 || (insn & 0xbfc00000) == 0x0d800000   /* C3.3.4 */ |                 || (insn & 0xbfc00000) == 0x0d800000   /* C3.3.4 */ | ||||||
|                 || (insn & 0x3f400000) == 0x08000000   /* C3.3.6 */ |                 || (insn & 0x3f400000) == 0x08000000   /* C3.3.6 */ | ||||||
|                 || (insn & 0x3bc00000) == 0x39000000   /* C3.3.13 */ |                 || (insn & 0x3bc00000) == 0x39000000   /* C3.3.13 */ | ||||||
|                 || (insn & 0x3fc00000) == 0x3d800000   /* ... 128bit */ |                 || (insn & 0x3fc00000) == 0x3d800000   /* ... 128bit */ | ||||||
|                     /* Ignore bits 10, 11 & 21, controlling indexing.  */ |                 /* Ingore bits 10, 11 & 21, controlling indexing.  */ | ||||||
|                 || (insn & 0x3bc00000) == 0x38000000   /* C3.3.8-12 */ |                 || (insn & 0x3bc00000) == 0x38000000   /* C3.3.8-12 */ | ||||||
|                 || (insn & 0x3fe00000) == 0x3c800000   /* ... 128bit */ |                 || (insn & 0x3fe00000) == 0x3c800000   /* ... 128bit */ | ||||||
|                 /* Ignore bits 23 & 24, controlling indexing.  */ |                 /* Ignore bits 23 & 24, controlling indexing.  */ | ||||||
|                 || (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */ |                 || (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */ | ||||||
|     } |  | ||||||
|     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); |     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -609,81 +570,6 @@ int cpu_signal_handler(int host_signum, void *pinfo, | |||||||
|     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); |     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); | ||||||
| } | } | ||||||
|  |  | ||||||
| #elif defined(__riscv) |  | ||||||
|  |  | ||||||
| int cpu_signal_handler(int host_signum, void *pinfo, |  | ||||||
|                        void *puc) |  | ||||||
| { |  | ||||||
|     siginfo_t *info = pinfo; |  | ||||||
|     ucontext_t *uc = puc; |  | ||||||
|     greg_t pc = uc->uc_mcontext.__gregs[REG_PC]; |  | ||||||
|     uint32_t insn = *(uint32_t *)pc; |  | ||||||
|     int is_write = 0; |  | ||||||
|  |  | ||||||
|     /* Detect store by reading the instruction at the program |  | ||||||
|        counter. Note: we currently only generate 32-bit |  | ||||||
|        instructions so we thus only detect 32-bit stores */ |  | ||||||
|     switch (((insn >> 0) & 0b11)) { |  | ||||||
|     case 3: |  | ||||||
|         switch (((insn >> 2) & 0b11111)) { |  | ||||||
|         case 8: |  | ||||||
|             switch (((insn >> 12) & 0b111)) { |  | ||||||
|             case 0: /* sb */ |  | ||||||
|             case 1: /* sh */ |  | ||||||
|             case 2: /* sw */ |  | ||||||
|             case 3: /* sd */ |  | ||||||
|             case 4: /* sq */ |  | ||||||
|                 is_write = 1; |  | ||||||
|                 break; |  | ||||||
|             default: |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         case 9: |  | ||||||
|             switch (((insn >> 12) & 0b111)) { |  | ||||||
|             case 2: /* fsw */ |  | ||||||
|             case 3: /* fsd */ |  | ||||||
|             case 4: /* fsq */ |  | ||||||
|                 is_write = 1; |  | ||||||
|                 break; |  | ||||||
|             default: |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Check for compressed instructions */ |  | ||||||
|     switch (((insn >> 13) & 0b111)) { |  | ||||||
|     case 7: |  | ||||||
|         switch (insn & 0b11) { |  | ||||||
|         case 0: /*c.sd */ |  | ||||||
|         case 2: /* c.sdsp */ |  | ||||||
|             is_write = 1; |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     case 6: |  | ||||||
|         switch (insn & 0b11) { |  | ||||||
|         case 0: /* c.sw */ |  | ||||||
|         case 3: /* c.swsp */ |  | ||||||
|             is_write = 1; |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     default: |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #else | #else | ||||||
|  |  | ||||||
| #error host CPU specific signal handler needed | #error host CPU specific signal handler needed | ||||||
| @@ -729,7 +615,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, | |||||||
| /* The following is only callable from other helpers, and matches up | /* The following is only callable from other helpers, and matches up | ||||||
|    with the softmmu version.  */ |    with the softmmu version.  */ | ||||||
|  |  | ||||||
| #if HAVE_ATOMIC128 || HAVE_CMPXCHG128 | #ifdef CONFIG_ATOMIC128 | ||||||
|  |  | ||||||
| #undef EXTRA_ARGS | #undef EXTRA_ARGS | ||||||
| #undef ATOMIC_NAME | #undef ATOMIC_NAME | ||||||
| @@ -742,4 +628,4 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, | |||||||
|  |  | ||||||
| #define DATA_SIZE 16 | #define DATA_SIZE 16 | ||||||
| #include "atomic_template.h" | #include "atomic_template.h" | ||||||
| #endif | #endif /* CONFIG_ATOMIC128 */ | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								arch_init.c
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								arch_init.c
									
									
									
									
									
								
							| @@ -29,7 +29,6 @@ | |||||||
| #include "hw/pci/pci.h" | #include "hw/pci/pci.h" | ||||||
| #include "hw/audio/soundhw.h" | #include "hw/audio/soundhw.h" | ||||||
| #include "qapi/qapi-commands-misc.h" | #include "qapi/qapi-commands-misc.h" | ||||||
| #include "qapi/error.h" |  | ||||||
| #include "qemu/config-file.h" | #include "qemu/config-file.h" | ||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
| #include "hw/acpi/acpi.h" | #include "hw/acpi/acpi.h" | ||||||
| @@ -52,14 +51,14 @@ int graphic_depth = 32; | |||||||
| #define QEMU_ARCH QEMU_ARCH_ARM | #define QEMU_ARCH QEMU_ARCH_ARM | ||||||
| #elif defined(TARGET_CRIS) | #elif defined(TARGET_CRIS) | ||||||
| #define QEMU_ARCH QEMU_ARCH_CRIS | #define QEMU_ARCH QEMU_ARCH_CRIS | ||||||
| #elif defined(TARGET_HPPA) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_HPPA |  | ||||||
| #elif defined(TARGET_I386) | #elif defined(TARGET_I386) | ||||||
| #define QEMU_ARCH QEMU_ARCH_I386 | #define QEMU_ARCH QEMU_ARCH_I386 | ||||||
| #elif defined(TARGET_LM32) | #elif defined(TARGET_HPPA) | ||||||
| #define QEMU_ARCH QEMU_ARCH_LM32 | #define QEMU_ARCH QEMU_ARCH_HPPA | ||||||
| #elif defined(TARGET_M68K) | #elif defined(TARGET_M68K) | ||||||
| #define QEMU_ARCH QEMU_ARCH_M68K | #define QEMU_ARCH QEMU_ARCH_M68K | ||||||
|  | #elif defined(TARGET_LM32) | ||||||
|  | #define QEMU_ARCH QEMU_ARCH_LM32 | ||||||
| #elif defined(TARGET_MICROBLAZE) | #elif defined(TARGET_MICROBLAZE) | ||||||
| #define QEMU_ARCH QEMU_ARCH_MICROBLAZE | #define QEMU_ARCH QEMU_ARCH_MICROBLAZE | ||||||
| #elif defined(TARGET_MIPS) | #elif defined(TARGET_MIPS) | ||||||
| @@ -80,12 +79,12 @@ int graphic_depth = 32; | |||||||
| #define QEMU_ARCH QEMU_ARCH_SH4 | #define QEMU_ARCH QEMU_ARCH_SH4 | ||||||
| #elif defined(TARGET_SPARC) | #elif defined(TARGET_SPARC) | ||||||
| #define QEMU_ARCH QEMU_ARCH_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) | #elif defined(TARGET_XTENSA) | ||||||
| #define QEMU_ARCH QEMU_ARCH_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 | #endif | ||||||
|  |  | ||||||
| const uint32_t arch_type = QEMU_ARCH; | const uint32_t arch_type = QEMU_ARCH; | ||||||
| @@ -113,8 +112,7 @@ TargetInfo *qmp_query_target(Error **errp) | |||||||
| { | { | ||||||
|     TargetInfo *info = g_malloc0(sizeof(*info)); |     TargetInfo *info = g_malloc0(sizeof(*info)); | ||||||
|  |  | ||||||
|     info->arch = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, -1, |     info->arch = g_strdup(TARGET_NAME); | ||||||
|                                  &error_abort); |  | ||||||
|  |  | ||||||
|     return info; |     return info; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -28,7 +28,9 @@ | |||||||
| #include "audio.h" | #include "audio.h" | ||||||
| #include "trace.h" | #include "trace.h" | ||||||
|  |  | ||||||
|  | #if QEMU_GNUC_PREREQ(4, 3) | ||||||
| #pragma GCC diagnostic ignored "-Waddress" | #pragma GCC diagnostic ignored "-Waddress" | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #define AUDIO_CAP "alsa" | #define AUDIO_CAP "alsa" | ||||||
| #include "audio_int.h" | #include "audio_int.h" | ||||||
|   | |||||||
| @@ -29,7 +29,6 @@ | |||||||
| #include "sysemu/sysemu.h" | #include "sysemu/sysemu.h" | ||||||
| #include "qemu/cutils.h" | #include "qemu/cutils.h" | ||||||
| #include "sysemu/replay.h" | #include "sysemu/replay.h" | ||||||
| #include "trace.h" |  | ||||||
|  |  | ||||||
| #define AUDIO_CAP "audio" | #define AUDIO_CAP "audio" | ||||||
| #include "audio_int.h" | #include "audio_int.h" | ||||||
| @@ -336,8 +335,9 @@ static int audio_get_conf_int (const char *key, int defval, int *defaultp) | |||||||
|     char *strval; |     char *strval; | ||||||
|  |  | ||||||
|     strval = getenv (key); |     strval = getenv (key); | ||||||
|     if (strval && !qemu_strtoi(strval, NULL, 10, &val)) { |     if (strval) { | ||||||
|         *defaultp = 0; |         *defaultp = 0; | ||||||
|  |         val = atoi (strval); | ||||||
|         return val; |         return val; | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
| @@ -1130,10 +1130,6 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info) | |||||||
| /* | /* | ||||||
|  * Timer |  * Timer | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| static bool audio_timer_running; |  | ||||||
| static uint64_t audio_timer_last; |  | ||||||
|  |  | ||||||
| static int audio_is_timer_needed (void) | static int audio_is_timer_needed (void) | ||||||
| { | { | ||||||
|     HWVoiceIn *hwi = NULL; |     HWVoiceIn *hwi = NULL; | ||||||
| @@ -1153,31 +1149,14 @@ static void audio_reset_timer (AudioState *s) | |||||||
|     if (audio_is_timer_needed ()) { |     if (audio_is_timer_needed ()) { | ||||||
|         timer_mod_anticipate_ns(s->ts, |         timer_mod_anticipate_ns(s->ts, | ||||||
|             qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks); |             qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks); | ||||||
|         if (!audio_timer_running) { |  | ||||||
|             audio_timer_running = true; |  | ||||||
|             audio_timer_last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |  | ||||||
|             trace_audio_timer_start(conf.period.ticks / SCALE_MS); |  | ||||||
|         } |  | ||||||
|     } else { |  | ||||||
|         timer_del(s->ts); |  | ||||||
|         if (audio_timer_running) { |  | ||||||
|             audio_timer_running = false; |  | ||||||
|             trace_audio_timer_stop(); |  | ||||||
|     } |     } | ||||||
|  |     else { | ||||||
|  |         timer_del (s->ts); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void audio_timer (void *opaque) | 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_run ("timer"); | ||||||
|     audio_reset_timer (opaque); |     audio_reset_timer (opaque); | ||||||
| } | } | ||||||
| @@ -1762,7 +1741,7 @@ void AUD_help (void) | |||||||
|         ); |         ); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int audio_driver_init(AudioState *s, struct audio_driver *drv, bool msg) | static int audio_driver_init (AudioState *s, struct audio_driver *drv) | ||||||
| { | { | ||||||
|     if (drv->options) { |     if (drv->options) { | ||||||
|         audio_process_options (drv->name, drv->options); |         audio_process_options (drv->name, drv->options); | ||||||
| @@ -1776,9 +1755,7 @@ static int audio_driver_init(AudioState *s, struct audio_driver *drv, bool msg) | |||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
|         if (msg) { |         dolog ("Could not init `%s' audio driver\n", drv->name); | ||||||
|             dolog("Could not init `%s' audio driver\n", drv->name); |  | ||||||
|         } |  | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -1903,7 +1880,7 @@ static void audio_init (void) | |||||||
|     if (drvname) { |     if (drvname) { | ||||||
|         driver = audio_driver_lookup(drvname); |         driver = audio_driver_lookup(drvname); | ||||||
|         if (driver) { |         if (driver) { | ||||||
|             done = !audio_driver_init(s, driver, true); |             done = !audio_driver_init(s, driver); | ||||||
|         } else { |         } else { | ||||||
|             dolog ("Unknown audio driver `%s'\n", drvname); |             dolog ("Unknown audio driver `%s'\n", drvname); | ||||||
|             dolog ("Run with -audio-help to list available drivers\n"); |             dolog ("Run with -audio-help to list available drivers\n"); | ||||||
| @@ -1914,14 +1891,14 @@ static void audio_init (void) | |||||||
|         for (i = 0; !done && i < ARRAY_SIZE(audio_prio_list); i++) { |         for (i = 0; !done && i < ARRAY_SIZE(audio_prio_list); i++) { | ||||||
|             driver = audio_driver_lookup(audio_prio_list[i]); |             driver = audio_driver_lookup(audio_prio_list[i]); | ||||||
|             if (driver && driver->can_be_default) { |             if (driver && driver->can_be_default) { | ||||||
|                 done = !audio_driver_init(s, driver, false); |                 done = !audio_driver_init(s, driver); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!done) { |     if (!done) { | ||||||
|         driver = audio_driver_lookup("none"); |         driver = audio_driver_lookup("none"); | ||||||
|         done = !audio_driver_init(s, driver, false); |         done = !audio_driver_init(s, driver); | ||||||
|         assert(done); |         assert(done); | ||||||
|         dolog("warning: Using timer based audio emulation\n"); |         dolog("warning: Using timer based audio emulation\n"); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -191,7 +191,7 @@ struct SWVoiceCap { | |||||||
|     QLIST_ENTRY (SWVoiceCap) entries; |     QLIST_ENTRY (SWVoiceCap) entries; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef struct AudioState { | struct AudioState { | ||||||
|     struct audio_driver *drv; |     struct audio_driver *drv; | ||||||
|     void *drv_opaque; |     void *drv_opaque; | ||||||
|  |  | ||||||
| @@ -203,7 +203,7 @@ typedef struct AudioState { | |||||||
|     int nb_hw_voices_out; |     int nb_hw_voices_out; | ||||||
|     int nb_hw_voices_in; |     int nb_hw_voices_in; | ||||||
|     int vm_running; |     int vm_running; | ||||||
| } AudioState; | }; | ||||||
|  |  | ||||||
| extern const struct mixeng_volume nominal_volume; | extern const struct mixeng_volume nominal_volume; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -227,7 +227,7 @@ static void *qpa_thread_out (void *arg) | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         decr = to_mix = audio_MIN(pa->live, pa->g->conf.samples >> 5); |         decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2); | ||||||
|         rpos = pa->rpos; |         rpos = pa->rpos; | ||||||
|  |  | ||||||
|         if (audio_pt_unlock(&pa->pt, __func__)) { |         if (audio_pt_unlock(&pa->pt, __func__)) { | ||||||
| @@ -319,7 +319,7 @@ static void *qpa_thread_in (void *arg) | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         incr = to_grab = audio_MIN(pa->dead, pa->g->conf.samples >> 5); |         incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2); | ||||||
|         wpos = pa->wpos; |         wpos = pa->wpos; | ||||||
|  |  | ||||||
|         if (audio_pt_unlock(&pa->pt, __func__)) { |         if (audio_pt_unlock(&pa->pt, __func__)) { | ||||||
| @@ -814,21 +814,6 @@ static PAConf glob_conf = { | |||||||
|  |  | ||||||
| static void *qpa_audio_init (void) | static void *qpa_audio_init (void) | ||||||
| { | { | ||||||
|     if (glob_conf.server == NULL) { |  | ||||||
|         char pidfile[64]; |  | ||||||
|         char *runtime; |  | ||||||
|         struct stat st; |  | ||||||
|  |  | ||||||
|         runtime = getenv("XDG_RUNTIME_DIR"); |  | ||||||
|         if (!runtime) { |  | ||||||
|             return NULL; |  | ||||||
|         } |  | ||||||
|         snprintf(pidfile, sizeof(pidfile), "%s/pulse/pid", runtime); |  | ||||||
|         if (stat(pidfile, &st) != 0) { |  | ||||||
|             return NULL; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     paaudio *g = g_malloc(sizeof(paaudio)); |     paaudio *g = g_malloc(sizeof(paaudio)); | ||||||
|     g->conf = glob_conf; |     g->conf = glob_conf; | ||||||
|     g->mainloop = NULL; |     g->mainloop = NULL; | ||||||
|   | |||||||
| @@ -15,8 +15,3 @@ alsa_no_frames(int state) "No frames available and ALSA state is %d" | |||||||
| # audio/ossaudio.c | # audio/ossaudio.c | ||||||
| oss_version(int version) "OSS version = 0x%x" | oss_version(int version) "OSS version = 0x%x" | ||||||
| oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d" | 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" |  | ||||||
|   | |||||||
| @@ -38,29 +38,30 @@ static void wav_destroy (void *opaque) | |||||||
|     uint8_t dlen[4]; |     uint8_t dlen[4]; | ||||||
|     uint32_t datalen = wav->bytes; |     uint32_t datalen = wav->bytes; | ||||||
|     uint32_t rifflen = datalen + 36; |     uint32_t rifflen = datalen + 36; | ||||||
|  |     Monitor *mon = cur_mon; | ||||||
|  |  | ||||||
|     if (wav->f) { |     if (wav->f) { | ||||||
|         le_store (rlen, rifflen, 4); |         le_store (rlen, rifflen, 4); | ||||||
|         le_store (dlen, datalen, 4); |         le_store (dlen, datalen, 4); | ||||||
|  |  | ||||||
|         if (fseek (wav->f, 4, SEEK_SET)) { |         if (fseek (wav->f, 4, SEEK_SET)) { | ||||||
|             error_report("wav_destroy: rlen fseek failed: %s", |             monitor_printf (mon, "wav_destroy: rlen fseek failed\nReason: %s\n", | ||||||
|                          strerror(errno)); |                             strerror (errno)); | ||||||
|             goto doclose; |             goto doclose; | ||||||
|         } |         } | ||||||
|         if (fwrite (rlen, 4, 1, wav->f) != 1) { |         if (fwrite (rlen, 4, 1, wav->f) != 1) { | ||||||
|             error_report("wav_destroy: rlen fwrite failed: %s", |             monitor_printf (mon, "wav_destroy: rlen fwrite failed\nReason %s\n", | ||||||
|                          strerror(errno)); |                             strerror (errno)); | ||||||
|             goto doclose; |             goto doclose; | ||||||
|         } |         } | ||||||
|         if (fseek (wav->f, 32, SEEK_CUR)) { |         if (fseek (wav->f, 32, SEEK_CUR)) { | ||||||
|             error_report("wav_destroy: dlen fseek failed: %s", |             monitor_printf (mon, "wav_destroy: dlen fseek failed\nReason %s\n", | ||||||
|                          strerror(errno)); |                             strerror (errno)); | ||||||
|             goto doclose; |             goto doclose; | ||||||
|         } |         } | ||||||
|         if (fwrite (dlen, 1, 4, wav->f) != 4) { |         if (fwrite (dlen, 1, 4, wav->f) != 4) { | ||||||
|             error_report("wav_destroy: dlen fwrite failed: %s", |             monitor_printf (mon, "wav_destroy: dlen fwrite failed\nReason %s\n", | ||||||
|                          strerror(errno)); |                             strerror (errno)); | ||||||
|             goto doclose; |             goto doclose; | ||||||
|         } |         } | ||||||
|     doclose: |     doclose: | ||||||
| @@ -77,7 +78,8 @@ static void wav_capture (void *opaque, void *buf, int size) | |||||||
|     WAVState *wav = opaque; |     WAVState *wav = opaque; | ||||||
|  |  | ||||||
|     if (fwrite (buf, size, 1, wav->f) != 1) { |     if (fwrite (buf, size, 1, wav->f) != 1) { | ||||||
|         error_report("wav_capture: fwrite error: %s", strerror(errno)); |         monitor_printf (cur_mon, "wav_capture: fwrite error\nReason: %s", | ||||||
|  |                         strerror (errno)); | ||||||
|     } |     } | ||||||
|     wav->bytes += size; |     wav->bytes += size; | ||||||
| } | } | ||||||
| @@ -108,6 +110,7 @@ static struct capture_ops wav_capture_ops = { | |||||||
| int wav_start_capture (CaptureState *s, const char *path, int freq, | int wav_start_capture (CaptureState *s, const char *path, int freq, | ||||||
|                        int bits, int nchannels) |                        int bits, int nchannels) | ||||||
| { | { | ||||||
|  |     Monitor *mon = cur_mon; | ||||||
|     WAVState *wav; |     WAVState *wav; | ||||||
|     uint8_t hdr[] = { |     uint8_t hdr[] = { | ||||||
|         0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, |         0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, | ||||||
| @@ -121,12 +124,12 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, | |||||||
|     CaptureVoiceOut *cap; |     CaptureVoiceOut *cap; | ||||||
|  |  | ||||||
|     if (bits != 8 && bits != 16) { |     if (bits != 8 && bits != 16) { | ||||||
|         error_report("incorrect bit count %d, must be 8 or 16", bits); |         monitor_printf (mon, "incorrect bit count %d, must be 8 or 16\n", bits); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (nchannels != 1 && nchannels != 2) { |     if (nchannels != 1 && nchannels != 2) { | ||||||
|         error_report("incorrect channel count %d, must be 1 or 2", |         monitor_printf (mon, "incorrect channel count %d, must be 1 or 2\n", | ||||||
|                         nchannels); |                         nchannels); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
| @@ -155,8 +158,8 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, | |||||||
|  |  | ||||||
|     wav->f = fopen (path, "wb"); |     wav->f = fopen (path, "wb"); | ||||||
|     if (!wav->f) { |     if (!wav->f) { | ||||||
|         error_report("Failed to open wave file `%s': %s", |         monitor_printf (mon, "Failed to open wave file `%s'\nReason: %s\n", | ||||||
|                      path, strerror(errno)); |                         path, strerror (errno)); | ||||||
|         g_free (wav); |         g_free (wav); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
| @@ -167,13 +170,14 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, | |||||||
|     wav->freq = freq; |     wav->freq = freq; | ||||||
|  |  | ||||||
|     if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) { |     if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) { | ||||||
|         error_report("Failed to write header: %s", strerror(errno)); |         monitor_printf (mon, "Failed to write header\nReason: %s\n", | ||||||
|  |                         strerror (errno)); | ||||||
|         goto error_free; |         goto error_free; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     cap = AUD_add_capture (&as, &ops, wav); |     cap = AUD_add_capture (&as, &ops, wav); | ||||||
|     if (!cap) { |     if (!cap) { | ||||||
|         error_report("Failed to add audio capture"); |         monitor_printf (mon, "Failed to add audio capture\n"); | ||||||
|         goto error_free; |         goto error_free; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -185,7 +189,8 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, | |||||||
| error_free: | error_free: | ||||||
|     g_free (wav->path); |     g_free (wav->path); | ||||||
|     if (fclose (wav->f)) { |     if (fclose (wav->f)) { | ||||||
|         error_report("Failed to close wave file: %s", strerror(errno)); |         monitor_printf (mon, "Failed to close wave file\nReason: %s\n", | ||||||
|  |                         strerror (errno)); | ||||||
|     } |     } | ||||||
|     g_free (wav); |     g_free (wav); | ||||||
|     return -1; |     return -1; | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ common-obj-$(CONFIG_POSIX) += rng-random.o | |||||||
| common-obj-$(CONFIG_TPM) += tpm.o | common-obj-$(CONFIG_TPM) += tpm.o | ||||||
|  |  | ||||||
| common-obj-y += hostmem.o hostmem-ram.o | common-obj-y += hostmem.o hostmem-ram.o | ||||||
| common-obj-$(CONFIG_POSIX) += hostmem-file.o | common-obj-$(CONFIG_LINUX) += hostmem-file.o | ||||||
|  |  | ||||||
| common-obj-y += cryptodev.o | common-obj-y += cryptodev.o | ||||||
| common-obj-y += cryptodev-builtin.o | common-obj-y += cryptodev-builtin.o | ||||||
|   | |||||||
| @@ -26,7 +26,6 @@ | |||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qapi/qmp/qerror.h" | #include "qapi/qmp/qerror.h" | ||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
| #include "hw/virtio/vhost-user.h" |  | ||||||
| #include "standard-headers/linux/virtio_crypto.h" | #include "standard-headers/linux/virtio_crypto.h" | ||||||
| #include "sysemu/cryptodev-vhost.h" | #include "sysemu/cryptodev-vhost.h" | ||||||
| #include "chardev/char-fe.h" | #include "chardev/char-fe.h" | ||||||
| @@ -47,7 +46,6 @@ | |||||||
| typedef struct CryptoDevBackendVhostUser { | typedef struct CryptoDevBackendVhostUser { | ||||||
|     CryptoDevBackend parent_obj; |     CryptoDevBackend parent_obj; | ||||||
|  |  | ||||||
|     VhostUserState *vhost_user; |  | ||||||
|     CharBackend chr; |     CharBackend chr; | ||||||
|     char *chr_name; |     char *chr_name; | ||||||
|     bool opened; |     bool opened; | ||||||
| @@ -104,7 +102,7 @@ cryptodev_vhost_user_start(int queues, | |||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         options.opaque = s->vhost_user; |         options.opaque = &s->chr; | ||||||
|         options.backend_type = VHOST_BACKEND_TYPE_USER; |         options.backend_type = VHOST_BACKEND_TYPE_USER; | ||||||
|         options.cc = b->conf.peers.ccs[i]; |         options.cc = b->conf.peers.ccs[i]; | ||||||
|         s->vhost_crypto[i] = cryptodev_vhost_init(&options); |         s->vhost_crypto[i] = cryptodev_vhost_init(&options); | ||||||
| @@ -157,6 +155,7 @@ static void cryptodev_vhost_user_event(void *opaque, int event) | |||||||
| { | { | ||||||
|     CryptoDevBackendVhostUser *s = opaque; |     CryptoDevBackendVhostUser *s = opaque; | ||||||
|     CryptoDevBackend *b = CRYPTODEV_BACKEND(s); |     CryptoDevBackend *b = CRYPTODEV_BACKEND(s); | ||||||
|  |     Error *err = NULL; | ||||||
|     int queues = b->conf.peers.queues; |     int queues = b->conf.peers.queues; | ||||||
|  |  | ||||||
|     assert(queues < MAX_CRYPTO_QUEUE_NUM); |     assert(queues < MAX_CRYPTO_QUEUE_NUM); | ||||||
| @@ -173,6 +172,10 @@ static void cryptodev_vhost_user_event(void *opaque, int event) | |||||||
|         cryptodev_vhost_user_stop(queues, s); |         cryptodev_vhost_user_stop(queues, s); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (err) { | ||||||
|  |         error_report_err(err); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void cryptodev_vhost_user_init( | static void cryptodev_vhost_user_init( | ||||||
| @@ -182,7 +185,6 @@ static void cryptodev_vhost_user_init( | |||||||
|     size_t i; |     size_t i; | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|     Chardev *chr; |     Chardev *chr; | ||||||
|     VhostUserState *user; |  | ||||||
|     CryptoDevBackendClient *cc; |     CryptoDevBackendClient *cc; | ||||||
|     CryptoDevBackendVhostUser *s = |     CryptoDevBackendVhostUser *s = | ||||||
|                       CRYPTODEV_BACKEND_VHOST_USER(backend); |                       CRYPTODEV_BACKEND_VHOST_USER(backend); | ||||||
| @@ -213,15 +215,6 @@ static void cryptodev_vhost_user_init( | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     user = vhost_user_init(); |  | ||||||
|     if (!user) { |  | ||||||
|         error_setg(errp, "Failed to init vhost_user"); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     user->chr = &s->chr; |  | ||||||
|     s->vhost_user = user; |  | ||||||
|  |  | ||||||
|     qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, |     qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, | ||||||
|                      cryptodev_vhost_user_event, NULL, s, NULL, true); |                      cryptodev_vhost_user_event, NULL, s, NULL, true); | ||||||
|  |  | ||||||
| @@ -306,12 +299,6 @@ static void cryptodev_vhost_user_cleanup( | |||||||
|             backend->conf.peers.ccs[i] = NULL; |             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, | static void cryptodev_vhost_user_set_chardev(Object *obj, | ||||||
|   | |||||||
| @@ -12,7 +12,6 @@ | |||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qemu-common.h" | #include "qemu-common.h" | ||||||
| #include "qemu/error-report.h" |  | ||||||
| #include "sysemu/hostmem.h" | #include "sysemu/hostmem.h" | ||||||
| #include "sysemu/sysemu.h" | #include "sysemu/sysemu.h" | ||||||
| #include "qom/object_interfaces.h" | #include "qom/object_interfaces.h" | ||||||
| @@ -32,19 +31,15 @@ typedef struct HostMemoryBackendFile HostMemoryBackendFile; | |||||||
| struct HostMemoryBackendFile { | struct HostMemoryBackendFile { | ||||||
|     HostMemoryBackend parent_obj; |     HostMemoryBackend parent_obj; | ||||||
|  |  | ||||||
|  |     bool discard_data; | ||||||
|     char *mem_path; |     char *mem_path; | ||||||
|     uint64_t align; |     uint64_t align; | ||||||
|     bool discard_data; |  | ||||||
|     bool is_pmem; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void | static void | ||||||
| file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | ||||||
| { | { | ||||||
|     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend); |     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend); | ||||||
| #ifdef CONFIG_POSIX |  | ||||||
|     gchar *name; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     if (!backend->size) { |     if (!backend->size) { | ||||||
|         error_setg(errp, "can't create backend with size 0"); |         error_setg(errp, "can't create backend with size 0"); | ||||||
| @@ -54,18 +49,19 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | |||||||
|         error_setg(errp, "mem-path property not set"); |         error_setg(errp, "mem-path property not set"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| #ifndef CONFIG_POSIX | #ifndef CONFIG_LINUX | ||||||
|     error_setg(errp, "-mem-path not supported on this host"); |     error_setg(errp, "-mem-path not supported on this host"); | ||||||
| #else | #else | ||||||
|  |     if (!host_memory_backend_mr_inited(backend)) { | ||||||
|  |         gchar *path; | ||||||
|         backend->force_prealloc = mem_prealloc; |         backend->force_prealloc = mem_prealloc; | ||||||
|     name = host_memory_backend_get_name(backend); |         path = object_get_canonical_path(OBJECT(backend)); | ||||||
|         memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), |         memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), | ||||||
|                                      name, |                                  path, | ||||||
|                                      backend->size, fb->align, |                                  backend->size, fb->align, backend->share, | ||||||
|                                      (backend->share ? RAM_SHARED : 0) | |  | ||||||
|                                      (fb->is_pmem ? RAM_PMEM : 0), |  | ||||||
|                                  fb->mem_path, errp); |                                  fb->mem_path, errp); | ||||||
|     g_free(name); |         g_free(path); | ||||||
|  |     } | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -82,8 +78,7 @@ static void set_mem_path(Object *o, const char *str, Error **errp) | |||||||
|     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); |     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); | ||||||
|  |  | ||||||
|     if (host_memory_backend_mr_inited(backend)) { |     if (host_memory_backend_mr_inited(backend)) { | ||||||
|         error_setg(errp, "cannot change property 'mem-path' of %s", |         error_setg(errp, "cannot change property value"); | ||||||
|                    object_get_typename(o)); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     g_free(fb->mem_path); |     g_free(fb->mem_path); | ||||||
| @@ -121,8 +116,7 @@ static void file_memory_backend_set_align(Object *o, Visitor *v, | |||||||
|     uint64_t val; |     uint64_t val; | ||||||
|  |  | ||||||
|     if (host_memory_backend_mr_inited(backend)) { |     if (host_memory_backend_mr_inited(backend)) { | ||||||
|         error_setg(&local_err, "cannot change property '%s' of %s", |         error_setg(&local_err, "cannot change property value"); | ||||||
|                    name, object_get_typename(o)); |  | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -136,39 +130,6 @@ static void file_memory_backend_set_align(Object *o, Visitor *v, | |||||||
|     error_propagate(errp, local_err); |     error_propagate(errp, local_err); | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool file_memory_backend_get_pmem(Object *o, Error **errp) |  | ||||||
| { |  | ||||||
|     return MEMORY_BACKEND_FILE(o)->is_pmem; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); |  | ||||||
|     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); |  | ||||||
|  |  | ||||||
|     if (host_memory_backend_mr_inited(backend)) { |  | ||||||
|  |  | ||||||
|         error_setg(errp, "cannot change property 'pmem' of %s.", |  | ||||||
|                    object_get_typename(o)); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| #ifndef CONFIG_LIBPMEM |  | ||||||
|     if (value) { |  | ||||||
|         Error *local_err = NULL; |  | ||||||
|  |  | ||||||
|         error_setg(&local_err, |  | ||||||
|                    "Lack of libpmem support while setting the 'pmem=on'" |  | ||||||
|                    " of %s. We can't ensure data persistence.", |  | ||||||
|                    object_get_typename(o)); |  | ||||||
|         error_propagate(errp, local_err); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     fb->is_pmem = value; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void file_backend_unparent(Object *obj) | static void file_backend_unparent(Object *obj) | ||||||
| { | { | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||||
| @@ -200,9 +161,6 @@ file_backend_class_init(ObjectClass *oc, void *data) | |||||||
|         file_memory_backend_get_align, |         file_memory_backend_get_align, | ||||||
|         file_memory_backend_set_align, |         file_memory_backend_set_align, | ||||||
|         NULL, NULL, &error_abort); |         NULL, NULL, &error_abort); | ||||||
|     object_class_property_add_bool(oc, "pmem", |  | ||||||
|         file_memory_backend_get_pmem, file_memory_backend_set_pmem, |  | ||||||
|         &error_abort); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void file_backend_instance_finalize(Object *o) | static void file_backend_instance_finalize(Object *o) | ||||||
|   | |||||||
| @@ -44,6 +44,10 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (host_memory_backend_mr_inited(backend)) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     backend->force_prealloc = mem_prealloc; |     backend->force_prealloc = mem_prealloc; | ||||||
|     fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size, |     fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size, | ||||||
|                            m->hugetlb, m->hugetlbsize, m->seal ? |                            m->hugetlb, m->hugetlbsize, m->seal ? | ||||||
| @@ -53,10 +57,9 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     name = host_memory_backend_get_name(backend); |     name = object_get_canonical_path(OBJECT(backend)); | ||||||
|     memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), |     memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), | ||||||
|                                    name, backend->size, |                                    name, backend->size, true, fd, errp); | ||||||
|                                    backend->share, fd, errp); |  | ||||||
|     g_free(name); |     g_free(name); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -128,7 +131,6 @@ memfd_backend_instance_init(Object *obj) | |||||||
|  |  | ||||||
|     /* default to sealed file */ |     /* default to sealed file */ | ||||||
|     m->seal = true; |     m->seal = true; | ||||||
|     MEMORY_BACKEND(m)->share = true; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| @@ -138,31 +140,18 @@ memfd_backend_class_init(ObjectClass *oc, void *data) | |||||||
|  |  | ||||||
|     bc->alloc = memfd_backend_memory_alloc; |     bc->alloc = memfd_backend_memory_alloc; | ||||||
|  |  | ||||||
|     if (qemu_memfd_check(MFD_HUGETLB)) { |  | ||||||
|     object_class_property_add_bool(oc, "hugetlb", |     object_class_property_add_bool(oc, "hugetlb", | ||||||
|                                    memfd_backend_get_hugetlb, |                                    memfd_backend_get_hugetlb, | ||||||
|                                    memfd_backend_set_hugetlb, |                                    memfd_backend_set_hugetlb, | ||||||
|                                    &error_abort); |                                    &error_abort); | ||||||
|         object_class_property_set_description(oc, "hugetlb", |  | ||||||
|                                               "Use huge pages", |  | ||||||
|                                               &error_abort); |  | ||||||
|     object_class_property_add(oc, "hugetlbsize", "int", |     object_class_property_add(oc, "hugetlbsize", "int", | ||||||
|                               memfd_backend_get_hugetlbsize, |                               memfd_backend_get_hugetlbsize, | ||||||
|                               memfd_backend_set_hugetlbsize, |                               memfd_backend_set_hugetlbsize, | ||||||
|                               NULL, NULL, &error_abort); |                               NULL, NULL, &error_abort); | ||||||
|         object_class_property_set_description(oc, "hugetlbsize", |  | ||||||
|                                               "Huge pages size (ex: 2M, 1G)", |  | ||||||
|                                               &error_abort); |  | ||||||
|     } |  | ||||||
|     if (qemu_memfd_check(MFD_ALLOW_SEALING)) { |  | ||||||
|     object_class_property_add_bool(oc, "seal", |     object_class_property_add_bool(oc, "seal", | ||||||
|                                    memfd_backend_get_seal, |                                    memfd_backend_get_seal, | ||||||
|                                    memfd_backend_set_seal, |                                    memfd_backend_set_seal, | ||||||
|                                    &error_abort); |                                    &error_abort); | ||||||
|         object_class_property_set_description(oc, "seal", |  | ||||||
|                                               "Seal growing & shrinking", |  | ||||||
|                                               &error_abort); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static const TypeInfo memfd_backend_info = { | static const TypeInfo memfd_backend_info = { | ||||||
| @@ -175,9 +164,7 @@ static const TypeInfo memfd_backend_info = { | |||||||
|  |  | ||||||
| static void register_types(void) | static void register_types(void) | ||||||
| { | { | ||||||
|     if (qemu_memfd_check(0)) { |  | ||||||
|     type_register_static(&memfd_backend_info); |     type_register_static(&memfd_backend_info); | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| type_init(register_types); | type_init(register_types); | ||||||
|   | |||||||
| @@ -16,20 +16,21 @@ | |||||||
|  |  | ||||||
| #define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram" | #define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram" | ||||||
|  |  | ||||||
|  |  | ||||||
| static void | static void | ||||||
| ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | ||||||
| { | { | ||||||
|     char *name; |     char *path; | ||||||
|  |  | ||||||
|     if (!backend->size) { |     if (!backend->size) { | ||||||
|         error_setg(errp, "can't create backend with size 0"); |         error_setg(errp, "can't create backend with size 0"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     name = host_memory_backend_get_name(backend); |     path = object_get_canonical_path_component(OBJECT(backend)); | ||||||
|     memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), name, |     memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), path, | ||||||
|                            backend->size, backend->share, errp); |                            backend->size, backend->share, errp); | ||||||
|     g_free(name); |     g_free(path); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
|   | |||||||
| @@ -18,7 +18,6 @@ | |||||||
| #include "qapi/visitor.h" | #include "qapi/visitor.h" | ||||||
| #include "qemu/config-file.h" | #include "qemu/config-file.h" | ||||||
| #include "qom/object_interfaces.h" | #include "qom/object_interfaces.h" | ||||||
| #include "qemu/mmap-alloc.h" |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_NUMA | #ifdef CONFIG_NUMA | ||||||
| #include <numaif.h> | #include <numaif.h> | ||||||
| @@ -28,16 +27,6 @@ QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_BIND != MPOL_BIND); | |||||||
| QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE); | QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| char * |  | ||||||
| host_memory_backend_get_name(HostMemoryBackend *backend) |  | ||||||
| { |  | ||||||
|     if (!backend->use_canonical_path) { |  | ||||||
|         return object_get_canonical_path_component(OBJECT(backend)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return object_get_canonical_path(OBJECT(backend)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void | static void | ||||||
| host_memory_backend_get_size(Object *obj, Visitor *v, const char *name, | host_memory_backend_get_size(Object *obj, Visitor *v, const char *name, | ||||||
|                              void *opaque, Error **errp) |                              void *opaque, Error **errp) | ||||||
| @@ -57,8 +46,7 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name, | |||||||
|     uint64_t value; |     uint64_t value; | ||||||
|  |  | ||||||
|     if (host_memory_backend_mr_inited(backend)) { |     if (host_memory_backend_mr_inited(backend)) { | ||||||
|         error_setg(&local_err, "cannot change property %s of %s ", |         error_setg(&local_err, "cannot change property value"); | ||||||
|                    name, object_get_typename(obj)); |  | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -67,9 +55,8 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name, | |||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|     if (!value) { |     if (!value) { | ||||||
|         error_setg(&local_err, |         error_setg(&local_err, "Property '%s.%s' doesn't take value '%" | ||||||
|                    "property '%s' of %s doesn't take value '%" PRIu64 "'", |                    PRIu64 "'", object_get_typename(obj), name, value); | ||||||
|                    name, object_get_typename(obj), value); |  | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|     backend->size = value; |     backend->size = value; | ||||||
| @@ -115,23 +102,14 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name, | |||||||
| { | { | ||||||
| #ifdef CONFIG_NUMA | #ifdef CONFIG_NUMA | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||||
|     uint16List *l, *host_nodes = NULL; |     uint16List *l = NULL; | ||||||
|  |  | ||||||
|     visit_type_uint16List(v, name, &host_nodes, errp); |     visit_type_uint16List(v, name, &l, errp); | ||||||
|  |  | ||||||
|     for (l = host_nodes; l; l = l->next) { |     while (l) { | ||||||
|         if (l->value >= MAX_NODES) { |  | ||||||
|             error_setg(errp, "Invalid host-nodes value: %d", l->value); |  | ||||||
|             goto out; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     for (l = host_nodes; l; l = l->next) { |  | ||||||
|         bitmap_set(backend->host_nodes, l->value, 1); |         bitmap_set(backend->host_nodes, l->value, 1); | ||||||
|  |         l = l->next; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| out: |  | ||||||
|     qapi_free_uint16List(host_nodes); |  | ||||||
| #else | #else | ||||||
|     error_setg(errp, "NUMA node binding are not supported by this QEMU"); |     error_setg(errp, "NUMA node binding are not supported by this QEMU"); | ||||||
| #endif | #endif | ||||||
| @@ -259,11 +237,6 @@ static void host_memory_backend_init(Object *obj) | |||||||
|     backend->prealloc = mem_prealloc; |     backend->prealloc = mem_prealloc; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void host_memory_backend_post_init(Object *obj) |  | ||||||
| { |  | ||||||
|     object_apply_compat_props(obj); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool host_memory_backend_mr_inited(HostMemoryBackend *backend) | bool host_memory_backend_mr_inited(HostMemoryBackend *backend) | ||||||
| { | { | ||||||
|     /* |     /* | ||||||
| @@ -273,7 +246,8 @@ bool host_memory_backend_mr_inited(HostMemoryBackend *backend) | |||||||
|     return memory_region_size(&backend->mr) != 0; |     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; |     return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL; | ||||||
| } | } | ||||||
| @@ -288,23 +262,6 @@ bool host_memory_backend_is_mapped(HostMemoryBackend *backend) | |||||||
|     return backend->is_mapped; |     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 | static void | ||||||
| host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) | host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) | ||||||
| { | { | ||||||
| @@ -394,6 +351,24 @@ host_memory_backend_can_be_deleted(UserCreatable *uc) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static char *get_id(Object *o, Error **errp) | ||||||
|  | { | ||||||
|  |     HostMemoryBackend *backend = MEMORY_BACKEND(o); | ||||||
|  |  | ||||||
|  |     return g_strdup(backend->id); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void set_id(Object *o, const char *str, Error **errp) | ||||||
|  | { | ||||||
|  |     HostMemoryBackend *backend = MEMORY_BACKEND(o); | ||||||
|  |  | ||||||
|  |     if (backend->id) { | ||||||
|  |         error_setg(errp, "cannot change property value"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     backend->id = g_strdup(str); | ||||||
|  | } | ||||||
|  |  | ||||||
| static bool host_memory_backend_get_share(Object *o, Error **errp) | static bool host_memory_backend_get_share(Object *o, Error **errp) | ||||||
| { | { | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); |     HostMemoryBackend *backend = MEMORY_BACKEND(o); | ||||||
| @@ -412,23 +387,6 @@ static void host_memory_backend_set_share(Object *o, bool value, Error **errp) | |||||||
|     backend->share = value; |     backend->share = value; | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool |  | ||||||
| host_memory_backend_get_use_canonical_path(Object *obj, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |  | ||||||
|  |  | ||||||
|     return backend->use_canonical_path; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| host_memory_backend_set_use_canonical_path(Object *obj, bool value, |  | ||||||
|                                            Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |  | ||||||
|  |  | ||||||
|     backend->use_canonical_path = value; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void | static void | ||||||
| host_memory_backend_class_init(ObjectClass *oc, void *data) | host_memory_backend_class_init(ObjectClass *oc, void *data) | ||||||
| { | { | ||||||
| @@ -440,44 +398,34 @@ host_memory_backend_class_init(ObjectClass *oc, void *data) | |||||||
|     object_class_property_add_bool(oc, "merge", |     object_class_property_add_bool(oc, "merge", | ||||||
|         host_memory_backend_get_merge, |         host_memory_backend_get_merge, | ||||||
|         host_memory_backend_set_merge, &error_abort); |         host_memory_backend_set_merge, &error_abort); | ||||||
|     object_class_property_set_description(oc, "merge", |  | ||||||
|         "Mark memory as mergeable", &error_abort); |  | ||||||
|     object_class_property_add_bool(oc, "dump", |     object_class_property_add_bool(oc, "dump", | ||||||
|         host_memory_backend_get_dump, |         host_memory_backend_get_dump, | ||||||
|         host_memory_backend_set_dump, &error_abort); |         host_memory_backend_set_dump, &error_abort); | ||||||
|     object_class_property_set_description(oc, "dump", |  | ||||||
|         "Set to 'off' to exclude from core dump", &error_abort); |  | ||||||
|     object_class_property_add_bool(oc, "prealloc", |     object_class_property_add_bool(oc, "prealloc", | ||||||
|         host_memory_backend_get_prealloc, |         host_memory_backend_get_prealloc, | ||||||
|         host_memory_backend_set_prealloc, &error_abort); |         host_memory_backend_set_prealloc, &error_abort); | ||||||
|     object_class_property_set_description(oc, "prealloc", |  | ||||||
|         "Preallocate memory", &error_abort); |  | ||||||
|     object_class_property_add(oc, "size", "int", |     object_class_property_add(oc, "size", "int", | ||||||
|         host_memory_backend_get_size, |         host_memory_backend_get_size, | ||||||
|         host_memory_backend_set_size, |         host_memory_backend_set_size, | ||||||
|         NULL, NULL, &error_abort); |         NULL, NULL, &error_abort); | ||||||
|     object_class_property_set_description(oc, "size", |  | ||||||
|         "Size of the memory region (ex: 500M)", &error_abort); |  | ||||||
|     object_class_property_add(oc, "host-nodes", "int", |     object_class_property_add(oc, "host-nodes", "int", | ||||||
|         host_memory_backend_get_host_nodes, |         host_memory_backend_get_host_nodes, | ||||||
|         host_memory_backend_set_host_nodes, |         host_memory_backend_set_host_nodes, | ||||||
|         NULL, NULL, &error_abort); |         NULL, NULL, &error_abort); | ||||||
|     object_class_property_set_description(oc, "host-nodes", |  | ||||||
|         "Binds memory to the list of NUMA host nodes", &error_abort); |  | ||||||
|     object_class_property_add_enum(oc, "policy", "HostMemPolicy", |     object_class_property_add_enum(oc, "policy", "HostMemPolicy", | ||||||
|         &HostMemPolicy_lookup, |         &HostMemPolicy_lookup, | ||||||
|         host_memory_backend_get_policy, |         host_memory_backend_get_policy, | ||||||
|         host_memory_backend_set_policy, &error_abort); |         host_memory_backend_set_policy, &error_abort); | ||||||
|     object_class_property_set_description(oc, "policy", |     object_class_property_add_str(oc, "id", get_id, set_id, &error_abort); | ||||||
|         "Set the NUMA policy", &error_abort); |  | ||||||
|     object_class_property_add_bool(oc, "share", |     object_class_property_add_bool(oc, "share", | ||||||
|         host_memory_backend_get_share, host_memory_backend_set_share, |         host_memory_backend_get_share, host_memory_backend_set_share, | ||||||
|         &error_abort); |         &error_abort); | ||||||
|     object_class_property_set_description(oc, "share", | } | ||||||
|         "Mark the memory as private to QEMU or shared", &error_abort); |  | ||||||
|     object_class_property_add_bool(oc, "x-use-canonical-path-for-ramblock-id", | static void host_memory_backend_finalize(Object *o) | ||||||
|         host_memory_backend_get_use_canonical_path, | { | ||||||
|         host_memory_backend_set_use_canonical_path, &error_abort); |     HostMemoryBackend *backend = MEMORY_BACKEND(o); | ||||||
|  |     g_free(backend->id); | ||||||
| } | } | ||||||
|  |  | ||||||
| static const TypeInfo host_memory_backend_info = { | static const TypeInfo host_memory_backend_info = { | ||||||
| @@ -488,7 +436,7 @@ static const TypeInfo host_memory_backend_info = { | |||||||
|     .class_init = host_memory_backend_class_init, |     .class_init = host_memory_backend_class_init, | ||||||
|     .instance_size = sizeof(HostMemoryBackend), |     .instance_size = sizeof(HostMemoryBackend), | ||||||
|     .instance_init = host_memory_backend_init, |     .instance_init = host_memory_backend_init, | ||||||
|     .instance_post_init = host_memory_backend_post_init, |     .instance_finalize = host_memory_backend_finalize, | ||||||
|     .interfaces = (InterfaceInfo[]) { |     .interfaces = (InterfaceInfo[]) { | ||||||
|         { TYPE_USER_CREATABLE }, |         { TYPE_USER_CREATABLE }, | ||||||
|         { } |         { } | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								balloon.c
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								balloon.c
									
									
									
									
									
								
							| @@ -26,7 +26,6 @@ | |||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "qemu-common.h" | #include "qemu-common.h" | ||||||
| #include "qemu/atomic.h" |  | ||||||
| #include "exec/cpu-common.h" | #include "exec/cpu-common.h" | ||||||
| #include "sysemu/kvm.h" | #include "sysemu/kvm.h" | ||||||
| #include "sysemu/balloon.h" | #include "sysemu/balloon.h" | ||||||
| @@ -38,22 +37,16 @@ | |||||||
| static QEMUBalloonEvent *balloon_event_fn; | static QEMUBalloonEvent *balloon_event_fn; | ||||||
| static QEMUBalloonStatus *balloon_stat_fn; | static QEMUBalloonStatus *balloon_stat_fn; | ||||||
| static void *balloon_opaque; | static void *balloon_opaque; | ||||||
| static int balloon_inhibit_count; | static bool balloon_inhibited; | ||||||
|  |  | ||||||
| bool qemu_balloon_is_inhibited(void) | bool qemu_balloon_is_inhibited(void) | ||||||
| { | { | ||||||
|     return atomic_read(&balloon_inhibit_count) > 0; |     return balloon_inhibited; | ||||||
| } | } | ||||||
|  |  | ||||||
| void qemu_balloon_inhibit(bool state) | void qemu_balloon_inhibit(bool state) | ||||||
| { | { | ||||||
|     if (state) { |     balloon_inhibited = state; | ||||||
|         atomic_inc(&balloon_inhibit_count); |  | ||||||
|     } else { |  | ||||||
|         atomic_dec(&balloon_inhibit_count); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     assert(atomic_read(&balloon_inhibit_count) >= 0); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool have_balloon(Error **errp) | static bool have_balloon(Error **errp) | ||||||
|   | |||||||
| @@ -1,19 +1,10 @@ | |||||||
| block-obj-y += raw-format.o vmdk.o vpc.o | block-obj-y += raw-format.o qcow.o vdi.o vmdk.o cloop.o bochs.o vpc.o vvfat.o dmg.o | ||||||
| block-obj-$(CONFIG_QCOW1) += qcow.o |  | ||||||
| block-obj-$(CONFIG_VDI) += vdi.o |  | ||||||
| block-obj-$(CONFIG_CLOOP) += cloop.o |  | ||||||
| block-obj-$(CONFIG_BOCHS) += bochs.o |  | ||||||
| block-obj-$(CONFIG_VVFAT) += vvfat.o |  | ||||||
| block-obj-$(CONFIG_DMG) += dmg.o |  | ||||||
|  |  | ||||||
| block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o | block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o | ||||||
| block-obj-$(CONFIG_QED) += qed.o qed-l2-cache.o qed-table.o qed-cluster.o | block-obj-y += qed.o qed-l2-cache.o qed-table.o qed-cluster.o | ||||||
| block-obj-$(CONFIG_QED) += qed-check.o | block-obj-y += qed-check.o | ||||||
| block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o | block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o | ||||||
| block-obj-y += quorum.o | block-obj-y += quorum.o | ||||||
| block-obj-y += blkdebug.o blkverify.o blkreplay.o | block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o | ||||||
| block-obj-$(CONFIG_PARALLELS) += parallels.o |  | ||||||
| block-obj-y += blklogwrites.o |  | ||||||
| block-obj-y += block-backend.o snapshot.o qapi.o | block-obj-y += block-backend.o snapshot.o qapi.o | ||||||
| block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o | block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o | ||||||
| block-obj-$(CONFIG_POSIX) += file-posix.o | block-obj-$(CONFIG_POSIX) += file-posix.o | ||||||
| @@ -22,8 +13,7 @@ block-obj-y += null.o mirror.o commit.o io.o create.o | |||||||
| block-obj-y += throttle-groups.o | block-obj-y += throttle-groups.o | ||||||
| block-obj-$(CONFIG_LINUX) += nvme.o | block-obj-$(CONFIG_LINUX) += nvme.o | ||||||
|  |  | ||||||
| block-obj-y += nbd.o nbd-client.o | block-obj-y += nbd.o nbd-client.o sheepdog.o | ||||||
| block-obj-$(CONFIG_SHEEPDOG) += sheepdog.o |  | ||||||
| block-obj-$(CONFIG_LIBISCSI) += iscsi.o | block-obj-$(CONFIG_LIBISCSI) += iscsi.o | ||||||
| block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o | block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o | ||||||
| block-obj-$(CONFIG_LIBNFS) += nfs.o | block-obj-$(CONFIG_LIBNFS) += nfs.o | ||||||
| @@ -36,7 +26,7 @@ block-obj-y += accounting.o dirty-bitmap.o | |||||||
| block-obj-y += write-threshold.o | block-obj-y += write-threshold.o | ||||||
| block-obj-y += backup.o | block-obj-y += backup.o | ||||||
| block-obj-$(CONFIG_REPLICATION) += replication.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 | block-obj-y += crypto.o | ||||||
|  |  | ||||||
| @@ -54,11 +44,8 @@ gluster.o-libs     := $(GLUSTERFS_LIBS) | |||||||
| vxhs.o-libs        := $(VXHS_LIBS) | vxhs.o-libs        := $(VXHS_LIBS) | ||||||
| ssh.o-cflags       := $(LIBSSH2_CFLAGS) | ssh.o-cflags       := $(LIBSSH2_CFLAGS) | ||||||
| ssh.o-libs         := $(LIBSSH2_LIBS) | ssh.o-libs         := $(LIBSSH2_LIBS) | ||||||
| block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o | block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o | ||||||
| block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y) |  | ||||||
| dmg-bz2.o-libs     := $(BZIP2_LIBS) | dmg-bz2.o-libs     := $(BZIP2_LIBS) | ||||||
| block-obj-$(if $(CONFIG_LZFSE),m,n) += dmg-lzfse.o |  | ||||||
| dmg-lzfse.o-libs   := $(LZFSE_LIBS) |  | ||||||
| qcow.o-libs        := -lz | qcow.o-libs        := -lz | ||||||
| linux-aio.o-libs   := -laio | linux-aio.o-libs   := -laio | ||||||
| parallels.o-cflags := $(LIBXML2_CFLAGS) | parallels.o-cflags := $(LIBXML2_CFLAGS) | ||||||
|   | |||||||
| @@ -94,94 +94,6 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie, | |||||||
|     cookie->type = type; |     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, | static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, | ||||||
|                                  bool failed) |                                  bool failed) | ||||||
| { | { | ||||||
| @@ -204,9 +116,6 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, | |||||||
|         stats->nr_ops[cookie->type]++; |         stats->nr_ops[cookie->type]++; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     block_latency_histogram_account(&stats->latency_histogram[cookie->type], |  | ||||||
|                                     latency_ns); |  | ||||||
|  |  | ||||||
|     if (!failed || stats->account_failed) { |     if (!failed || stats->account_failed) { | ||||||
|         stats->total_time_ns[cookie->type] += latency_ns; |         stats->total_time_ns[cookie->type] += latency_ns; | ||||||
|         stats->last_access_time_ns = time_ns; |         stats->last_access_time_ns = time_ns; | ||||||
|   | |||||||
							
								
								
									
										358
									
								
								block/backup.c
									
									
									
									
									
								
							
							
						
						
									
										358
									
								
								block/backup.c
									
									
									
									
									
								
							| @@ -27,13 +27,7 @@ | |||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
|  |  | ||||||
| #define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16) | #define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16) | ||||||
|  | #define SLICE_TIME 100000000ULL /* ns */ | ||||||
| typedef struct CowRequest { |  | ||||||
|     int64_t start_byte; |  | ||||||
|     int64_t end_byte; |  | ||||||
|     QLIST_ENTRY(CowRequest) list; |  | ||||||
|     CoQueue wait_queue; /* coroutines blocked on this request */ |  | ||||||
| } CowRequest; |  | ||||||
|  |  | ||||||
| typedef struct BackupBlockJob { | typedef struct BackupBlockJob { | ||||||
|     BlockJob common; |     BlockJob common; | ||||||
| @@ -41,10 +35,10 @@ typedef struct BackupBlockJob { | |||||||
|     /* bitmap for sync=incremental */ |     /* bitmap for sync=incremental */ | ||||||
|     BdrvDirtyBitmap *sync_bitmap; |     BdrvDirtyBitmap *sync_bitmap; | ||||||
|     MirrorSyncMode sync_mode; |     MirrorSyncMode sync_mode; | ||||||
|  |     RateLimit limit; | ||||||
|     BlockdevOnError on_source_error; |     BlockdevOnError on_source_error; | ||||||
|     BlockdevOnError on_target_error; |     BlockdevOnError on_target_error; | ||||||
|     CoRwlock flush_rwlock; |     CoRwlock flush_rwlock; | ||||||
|     uint64_t len; |  | ||||||
|     uint64_t bytes_read; |     uint64_t bytes_read; | ||||||
|     int64_t cluster_size; |     int64_t cluster_size; | ||||||
|     bool compress; |     bool compress; | ||||||
| @@ -52,14 +46,8 @@ typedef struct BackupBlockJob { | |||||||
|     QLIST_HEAD(, CowRequest) inflight_reqs; |     QLIST_HEAD(, CowRequest) inflight_reqs; | ||||||
|  |  | ||||||
|     HBitmap *copy_bitmap; |     HBitmap *copy_bitmap; | ||||||
|     bool use_copy_range; |  | ||||||
|     int64_t copy_range_size; |  | ||||||
|  |  | ||||||
|     bool serialize_target_writes; |  | ||||||
| } BackupBlockJob; | } BackupBlockJob; | ||||||
|  |  | ||||||
| static const BlockJobDriver backup_job_driver; |  | ||||||
|  |  | ||||||
| /* See if in-flight requests overlap and wait for them to complete */ | /* See if in-flight requests overlap and wait for them to complete */ | ||||||
| static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job, | static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job, | ||||||
|                                                        int64_t start, |                                                        int64_t start, | ||||||
| @@ -97,104 +85,19 @@ static void cow_request_end(CowRequest *req) | |||||||
|     qemu_co_queue_restart_all(&req->wait_queue); |     qemu_co_queue_restart_all(&req->wait_queue); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Copy range to target with a bounce buffer and return the bytes copied. If |  | ||||||
|  * error occurred, return a negative error number */ |  | ||||||
| static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, |  | ||||||
|                                                       int64_t start, |  | ||||||
|                                                       int64_t end, |  | ||||||
|                                                       bool is_write_notifier, |  | ||||||
|                                                       bool *error_is_read, |  | ||||||
|                                                       void **bounce_buffer) |  | ||||||
| { |  | ||||||
|     int ret; |  | ||||||
|     struct iovec iov; |  | ||||||
|     QEMUIOVector qiov; |  | ||||||
|     BlockBackend *blk = job->common.blk; |  | ||||||
|     int nbytes; |  | ||||||
|     int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0; |  | ||||||
|     int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0; |  | ||||||
|  |  | ||||||
|     hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1); |  | ||||||
|     nbytes = MIN(job->cluster_size, job->len - start); |  | ||||||
|     if (!*bounce_buffer) { |  | ||||||
|         *bounce_buffer = blk_blockalign(blk, job->cluster_size); |  | ||||||
|     } |  | ||||||
|     iov.iov_base = *bounce_buffer; |  | ||||||
|     iov.iov_len = nbytes; |  | ||||||
|     qemu_iovec_init_external(&qiov, &iov, 1); |  | ||||||
|  |  | ||||||
|     ret = blk_co_preadv(blk, start, qiov.size, &qiov, read_flags); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         trace_backup_do_cow_read_fail(job, start, ret); |  | ||||||
|         if (error_is_read) { |  | ||||||
|             *error_is_read = true; |  | ||||||
|         } |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (qemu_iovec_is_zero(&qiov)) { |  | ||||||
|         ret = blk_co_pwrite_zeroes(job->target, start, |  | ||||||
|                                    qiov.size, write_flags | BDRV_REQ_MAY_UNMAP); |  | ||||||
|     } else { |  | ||||||
|         ret = blk_co_pwritev(job->target, start, |  | ||||||
|                              qiov.size, &qiov, write_flags | |  | ||||||
|                              (job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0)); |  | ||||||
|     } |  | ||||||
|     if (ret < 0) { |  | ||||||
|         trace_backup_do_cow_write_fail(job, start, ret); |  | ||||||
|         if (error_is_read) { |  | ||||||
|             *error_is_read = false; |  | ||||||
|         } |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return nbytes; |  | ||||||
| fail: |  | ||||||
|     hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1); |  | ||||||
|     return ret; |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Copy range to target and return the bytes copied. If error occurred, return a |  | ||||||
|  * negative error number. */ |  | ||||||
| static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job, |  | ||||||
|                                                 int64_t start, |  | ||||||
|                                                 int64_t end, |  | ||||||
|                                                 bool is_write_notifier) |  | ||||||
| { |  | ||||||
|     int ret; |  | ||||||
|     int nr_clusters; |  | ||||||
|     BlockBackend *blk = job->common.blk; |  | ||||||
|     int nbytes; |  | ||||||
|     int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0; |  | ||||||
|     int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0; |  | ||||||
|  |  | ||||||
|     assert(QEMU_IS_ALIGNED(job->copy_range_size, job->cluster_size)); |  | ||||||
|     nbytes = MIN(job->copy_range_size, end - start); |  | ||||||
|     nr_clusters = DIV_ROUND_UP(nbytes, job->cluster_size); |  | ||||||
|     hbitmap_reset(job->copy_bitmap, start / job->cluster_size, |  | ||||||
|                   nr_clusters); |  | ||||||
|     ret = blk_co_copy_range(blk, start, job->target, start, nbytes, |  | ||||||
|                             read_flags, write_flags); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         trace_backup_do_cow_copy_range_fail(job, start, ret); |  | ||||||
|         hbitmap_set(job->copy_bitmap, start / job->cluster_size, |  | ||||||
|                     nr_clusters); |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return nbytes; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int coroutine_fn backup_do_cow(BackupBlockJob *job, | static int coroutine_fn backup_do_cow(BackupBlockJob *job, | ||||||
|                                       int64_t offset, uint64_t bytes, |                                       int64_t offset, uint64_t bytes, | ||||||
|                                       bool *error_is_read, |                                       bool *error_is_read, | ||||||
|                                       bool is_write_notifier) |                                       bool is_write_notifier) | ||||||
| { | { | ||||||
|  |     BlockBackend *blk = job->common.blk; | ||||||
|     CowRequest cow_request; |     CowRequest cow_request; | ||||||
|  |     struct iovec iov; | ||||||
|  |     QEMUIOVector bounce_qiov; | ||||||
|  |     void *bounce_buffer = NULL; | ||||||
|     int ret = 0; |     int ret = 0; | ||||||
|     int64_t start, end; /* bytes */ |     int64_t start, end; /* bytes */ | ||||||
|     void *bounce_buffer = NULL; |     int n; /* bytes */ | ||||||
|  |  | ||||||
|     qemu_co_rwlock_rdlock(&job->flush_rwlock); |     qemu_co_rwlock_rdlock(&job->flush_rwlock); | ||||||
|  |  | ||||||
| @@ -206,38 +109,60 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job, | |||||||
|     wait_for_overlapping_requests(job, start, end); |     wait_for_overlapping_requests(job, start, end); | ||||||
|     cow_request_begin(&cow_request, 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)) { |         if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) { | ||||||
|             trace_backup_do_cow_skip(job, start); |             trace_backup_do_cow_skip(job, start); | ||||||
|             start += job->cluster_size; |  | ||||||
|             continue; /* already copied */ |             continue; /* already copied */ | ||||||
|         } |         } | ||||||
|  |         hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1); | ||||||
|  |  | ||||||
|         trace_backup_do_cow_process(job, start); |         trace_backup_do_cow_process(job, start); | ||||||
|  |  | ||||||
|         if (job->use_copy_range) { |         n = MIN(job->cluster_size, job->common.len - start); | ||||||
|             ret = backup_cow_with_offload(job, start, end, is_write_notifier); |  | ||||||
|  |         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) { |         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, |         if (buffer_is_zero(iov.iov_base, iov.iov_len)) { | ||||||
|                                                 error_is_read, &bounce_buffer); |             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) { |         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 |         /* 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. |          * offset field is an opaque progress value, it is not a disk offset. | ||||||
|          */ |          */ | ||||||
|         start += ret; |         job->bytes_read += n; | ||||||
|         job->bytes_read += ret; |         job->common.offset += n; | ||||||
|         job_progress_update(&job->common.job, ret); |  | ||||||
|         ret = 0; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | out: | ||||||
|     if (bounce_buffer) { |     if (bounce_buffer) { | ||||||
|         qemu_vfree(bounce_buffer); |         qemu_vfree(bounce_buffer); | ||||||
|     } |     } | ||||||
| @@ -265,12 +190,23 @@ static int coroutine_fn backup_before_write_notify( | |||||||
|     return backup_do_cow(job, req->offset, req->bytes, NULL, true); |     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) | static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) | ||||||
| { | { | ||||||
|     BdrvDirtyBitmap *bm; |     BdrvDirtyBitmap *bm; | ||||||
|     BlockDriverState *bs = blk_bs(job->common.blk); |     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. */ |         /* Merge the successor back into the parent, delete nothing. */ | ||||||
|         bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL); |         bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL); | ||||||
|         assert(bm); |         assert(bm); | ||||||
| @@ -281,25 +217,25 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void backup_commit(Job *job) | static void backup_commit(BlockJob *job) | ||||||
| { | { | ||||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); |     BackupBlockJob *s = container_of(job, BackupBlockJob, common); | ||||||
|     if (s->sync_bitmap) { |     if (s->sync_bitmap) { | ||||||
|         backup_cleanup_sync_bitmap(s, 0); |         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) { |     if (s->sync_bitmap) { | ||||||
|         backup_cleanup_sync_bitmap(s, -1); |         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); |     assert(s->target); | ||||||
|     blk_unref(s->target); |     blk_unref(s->target); | ||||||
|     s->target = NULL; |     s->target = NULL; | ||||||
| @@ -317,7 +253,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp) | |||||||
|     BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); |     BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); | ||||||
|     int64_t len; |     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) { |     if (backup_job->sync_mode != MIRROR_SYNC_MODE_NONE) { | ||||||
|         error_setg(errp, "The backup job only supports block checkpoint in" |         error_setg(errp, "The backup job only supports block checkpoint in" | ||||||
| @@ -325,10 +261,41 @@ void backup_do_checkpoint(BlockJob *job, Error **errp) | |||||||
|         return; |         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); |     hbitmap_set(backup_job->copy_bitmap, 0, len); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset, | ||||||
|  |                                           uint64_t bytes) | ||||||
|  | { | ||||||
|  |     BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); | ||||||
|  |     int64_t start, end; | ||||||
|  |  | ||||||
|  |     assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); | ||||||
|  |  | ||||||
|  |     start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size); | ||||||
|  |     end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size); | ||||||
|  |     wait_for_overlapping_requests(backup_job, start, end); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void backup_cow_request_begin(CowRequest *req, BlockJob *job, | ||||||
|  |                               int64_t offset, uint64_t bytes) | ||||||
|  | { | ||||||
|  |     BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); | ||||||
|  |     int64_t start, end; | ||||||
|  |  | ||||||
|  |     assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); | ||||||
|  |  | ||||||
|  |     start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size); | ||||||
|  |     end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size); | ||||||
|  |     cow_request_begin(req, backup_job, start, end); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void backup_cow_request_end(CowRequest *req) | ||||||
|  | { | ||||||
|  |     cow_request_end(req); | ||||||
|  | } | ||||||
|  |  | ||||||
| static void backup_drain(BlockJob *job) | static void backup_drain(BlockJob *job) | ||||||
| { | { | ||||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common); |     BackupBlockJob *s = container_of(job, BackupBlockJob, common); | ||||||
| @@ -356,21 +323,37 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job, | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | typedef struct { | ||||||
|  |     int ret; | ||||||
|  | } BackupCompleteData; | ||||||
|  |  | ||||||
|  | static void backup_complete(BlockJob *job, void *opaque) | ||||||
|  | { | ||||||
|  |     BackupCompleteData *data = opaque; | ||||||
|  |  | ||||||
|  |     block_job_completed(job, data->ret); | ||||||
|  |     g_free(data); | ||||||
|  | } | ||||||
|  |  | ||||||
| static bool coroutine_fn yield_and_check(BackupBlockJob *job) | static bool coroutine_fn yield_and_check(BackupBlockJob *job) | ||||||
| { | { | ||||||
|     uint64_t delay_ns; |     if (block_job_is_cancelled(&job->common)) { | ||||||
|  |  | ||||||
|     if (job_is_cancelled(&job->common.job)) { |  | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* We need to yield even for delay_ns = 0 so that bdrv_drain_all() can |     /* we need to yield so that bdrv_drain_all() returns. | ||||||
|      * return. Without a yield, the VM would not reboot. */ |      * (without, VM does not reboot) | ||||||
|     delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read); |      */ | ||||||
|  |     if (job->common.speed) { | ||||||
|  |         uint64_t delay_ns = ratelimit_calculate_delay(&job->limit, | ||||||
|  |                                                       job->bytes_read); | ||||||
|         job->bytes_read = 0; |         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; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -422,8 +405,7 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset, |         offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset); | ||||||
|                                              UINT64_MAX); |  | ||||||
|         if (offset == -1) { |         if (offset == -1) { | ||||||
|             hbitmap_set(job->copy_bitmap, cluster, end - cluster); |             hbitmap_set(job->copy_bitmap, cluster, end - cluster); | ||||||
|             break; |             break; | ||||||
| @@ -438,66 +420,64 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) | |||||||
|         bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size); |         bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* TODO job_progress_set_remaining() would make more sense */ |     job->common.offset = job->common.len - | ||||||
|     job_progress_update(&job->common.job, |                          hbitmap_count(job->copy_bitmap) * job->cluster_size; | ||||||
|         job->len - hbitmap_count(job->copy_bitmap) * job->cluster_size); |  | ||||||
|  |  | ||||||
|     bdrv_dirty_iter_free(dbi); |     bdrv_dirty_iter_free(dbi); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coroutine_fn backup_run(Job *job, Error **errp) | static void coroutine_fn backup_run(void *opaque) | ||||||
| { | { | ||||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); |     BackupBlockJob *job = opaque; | ||||||
|     BlockDriverState *bs = blk_bs(s->common.blk); |     BackupCompleteData *data; | ||||||
|  |     BlockDriverState *bs = blk_bs(job->common.blk); | ||||||
|     int64_t offset, nb_clusters; |     int64_t offset, nb_clusters; | ||||||
|     int ret = 0; |     int ret = 0; | ||||||
|  |  | ||||||
|     QLIST_INIT(&s->inflight_reqs); |     QLIST_INIT(&job->inflight_reqs); | ||||||
|     qemu_co_rwlock_init(&s->flush_rwlock); |     qemu_co_rwlock_init(&job->flush_rwlock); | ||||||
|  |  | ||||||
|     nb_clusters = DIV_ROUND_UP(s->len, s->cluster_size); |     nb_clusters = DIV_ROUND_UP(job->common.len, job->cluster_size); | ||||||
|     job_progress_set_remaining(job, s->len); |     job->copy_bitmap = hbitmap_alloc(nb_clusters, 0); | ||||||
|  |     if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { | ||||||
|     s->copy_bitmap = hbitmap_alloc(nb_clusters, 0); |         backup_incremental_init_copy_bitmap(job); | ||||||
|     if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { |  | ||||||
|         backup_incremental_init_copy_bitmap(s); |  | ||||||
|     } else { |     } else { | ||||||
|         hbitmap_set(s->copy_bitmap, 0, nb_clusters); |         hbitmap_set(job->copy_bitmap, 0, nb_clusters); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     s->before_write.notify = backup_before_write_notify; |     job->before_write.notify = backup_before_write_notify; | ||||||
|     bdrv_add_before_write_notifier(bs, &s->before_write); |     bdrv_add_before_write_notifier(bs, &job->before_write); | ||||||
|  |  | ||||||
|     if (s->sync_mode == MIRROR_SYNC_MODE_NONE) { |     if (job->sync_mode == MIRROR_SYNC_MODE_NONE) { | ||||||
|         /* All bits are set in copy_bitmap to allow any cluster to be copied. |         /* All bits are set in copy_bitmap to allow any cluster to be copied. | ||||||
|          * This does not actually require them to be copied. */ |          * This does not actually require them to be copied. */ | ||||||
|         while (!job_is_cancelled(job)) { |         while (!block_job_is_cancelled(&job->common)) { | ||||||
|             /* Yield until the job is cancelled.  We just let our before_write |             /* Yield until the job is cancelled.  We just let our before_write | ||||||
|              * notify callback service CoW requests. */ |              * notify callback service CoW requests. */ | ||||||
|             job_yield(job); |             block_job_yield(&job->common); | ||||||
|         } |         } | ||||||
|     } else if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { |     } else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { | ||||||
|         ret = backup_run_incremental(s); |         ret = backup_run_incremental(job); | ||||||
|     } else { |     } else { | ||||||
|         /* Both FULL and TOP SYNC_MODE's require copying.. */ |         /* Both FULL and TOP SYNC_MODE's require copying.. */ | ||||||
|         for (offset = 0; offset < s->len; |         for (offset = 0; offset < job->common.len; | ||||||
|              offset += s->cluster_size) { |              offset += job->cluster_size) { | ||||||
|             bool error_is_read; |             bool error_is_read; | ||||||
|             int alloced = 0; |             int alloced = 0; | ||||||
|  |  | ||||||
|             if (yield_and_check(s)) { |             if (yield_and_check(job)) { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (s->sync_mode == MIRROR_SYNC_MODE_TOP) { |             if (job->sync_mode == MIRROR_SYNC_MODE_TOP) { | ||||||
|                 int i; |                 int i; | ||||||
|                 int64_t n; |                 int64_t n; | ||||||
|  |  | ||||||
|                 /* Check to see if these blocks are already in the |                 /* Check to see if these blocks are already in the | ||||||
|                  * backing file. */ |                  * backing file. */ | ||||||
|  |  | ||||||
|                 for (i = 0; i < s->cluster_size;) { |                 for (i = 0; i < job->cluster_size;) { | ||||||
|                     /* bdrv_is_allocated() only returns true/false based |                     /* bdrv_is_allocated() only returns true/false based | ||||||
|                      * on the first set of sectors it comes across that |                      * on the first set of sectors it comes across that | ||||||
|                      * are are all in the same state. |                      * are are all in the same state. | ||||||
| @@ -506,7 +486,7 @@ static int coroutine_fn backup_run(Job *job, Error **errp) | |||||||
|                      * needed but at some point that is always the case. */ |                      * needed but at some point that is always the case. */ | ||||||
|                     alloced = |                     alloced = | ||||||
|                         bdrv_is_allocated(bs, offset + i, |                         bdrv_is_allocated(bs, offset + i, | ||||||
|                                           s->cluster_size - i, &n); |                                           job->cluster_size - i, &n); | ||||||
|                     i += n; |                     i += n; | ||||||
|  |  | ||||||
|                     if (alloced || n == 0) { |                     if (alloced || n == 0) { | ||||||
| @@ -524,45 +504,43 @@ static int coroutine_fn backup_run(Job *job, Error **errp) | |||||||
|             if (alloced < 0) { |             if (alloced < 0) { | ||||||
|                 ret = alloced; |                 ret = alloced; | ||||||
|             } else { |             } else { | ||||||
|                 ret = backup_do_cow(s, offset, s->cluster_size, |                 ret = backup_do_cow(job, offset, job->cluster_size, | ||||||
|                                     &error_is_read, false); |                                     &error_is_read, false); | ||||||
|             } |             } | ||||||
|             if (ret < 0) { |             if (ret < 0) { | ||||||
|                 /* Depending on error action, fail now or retry cluster */ |                 /* Depending on error action, fail now or retry cluster */ | ||||||
|                 BlockErrorAction action = |                 BlockErrorAction action = | ||||||
|                     backup_error_action(s, error_is_read, -ret); |                     backup_error_action(job, error_is_read, -ret); | ||||||
|                 if (action == BLOCK_ERROR_ACTION_REPORT) { |                 if (action == BLOCK_ERROR_ACTION_REPORT) { | ||||||
|                     break; |                     break; | ||||||
|                 } else { |                 } else { | ||||||
|                     offset -= s->cluster_size; |                     offset -= job->cluster_size; | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     notifier_with_return_remove(&s->before_write); |     notifier_with_return_remove(&job->before_write); | ||||||
|  |  | ||||||
|     /* wait until pending backup_do_cow() calls have completed */ |     /* wait until pending backup_do_cow() calls have completed */ | ||||||
|     qemu_co_rwlock_wrlock(&s->flush_rwlock); |     qemu_co_rwlock_wrlock(&job->flush_rwlock); | ||||||
|     qemu_co_rwlock_unlock(&s->flush_rwlock); |     qemu_co_rwlock_unlock(&job->flush_rwlock); | ||||||
|     hbitmap_free(s->copy_bitmap); |     hbitmap_free(job->copy_bitmap); | ||||||
|  |  | ||||||
|     return ret; |     data = g_malloc(sizeof(*data)); | ||||||
|  |     data->ret = ret; | ||||||
|  |     block_job_defer_to_main_loop(&job->common, backup_complete, data); | ||||||
| } | } | ||||||
|  |  | ||||||
| static const BlockJobDriver backup_job_driver = { | static const BlockJobDriver backup_job_driver = { | ||||||
|     .job_driver = { |  | ||||||
|     .instance_size          = sizeof(BackupBlockJob), |     .instance_size          = sizeof(BackupBlockJob), | ||||||
|         .job_type               = JOB_TYPE_BACKUP, |     .job_type               = BLOCK_JOB_TYPE_BACKUP, | ||||||
|         .free                   = block_job_free, |     .start                  = backup_run, | ||||||
|         .user_resume            = block_job_user_resume, |     .set_speed              = backup_set_speed, | ||||||
|         .drain                  = block_job_drain, |  | ||||||
|         .run                    = backup_run, |  | ||||||
|     .commit                 = backup_commit, |     .commit                 = backup_commit, | ||||||
|     .abort                  = backup_abort, |     .abort                  = backup_abort, | ||||||
|     .clean                  = backup_clean, |     .clean                  = backup_clean, | ||||||
|     }, |  | ||||||
|     .attached_aio_context   = backup_attached_aio_context, |     .attached_aio_context   = backup_attached_aio_context, | ||||||
|     .drain                  = backup_drain, |     .drain                  = backup_drain, | ||||||
| }; | }; | ||||||
| @@ -575,7 +553,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | |||||||
|                   BlockdevOnError on_target_error, |                   BlockdevOnError on_target_error, | ||||||
|                   int creation_flags, |                   int creation_flags, | ||||||
|                   BlockCompletionFunc *cb, void *opaque, |                   BlockCompletionFunc *cb, void *opaque, | ||||||
|                   JobTxn *txn, Error **errp) |                   BlockJobTxn *txn, Error **errp) | ||||||
| { | { | ||||||
|     int64_t len; |     int64_t len; | ||||||
|     BlockDriverInfo bdi; |     BlockDriverInfo bdi; | ||||||
| @@ -642,8 +620,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | |||||||
|         goto error; |         goto error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* job->len is fixed, so we can't allow resize */ |     /* job->common.len is fixed, so we can't allow resize */ | ||||||
|     job = block_job_create(job_id, &backup_job_driver, txn, bs, |     job = block_job_create(job_id, &backup_job_driver, bs, | ||||||
|                            BLK_PERM_CONSISTENT_READ, |                            BLK_PERM_CONSISTENT_READ, | ||||||
|                            BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | |                            BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | | ||||||
|                            BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD, |                            BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD, | ||||||
| @@ -668,9 +646,6 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | |||||||
|                        sync_bitmap : NULL; |                        sync_bitmap : NULL; | ||||||
|     job->compress = compress; |     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 |     /* 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 |      * backup cluster size is smaller than the target cluster size. Even for | ||||||
|      * targets with a backing file, try to avoid COW if possible. */ |      * targets with a backing file, try to avoid COW if possible. */ | ||||||
| @@ -697,17 +672,12 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | |||||||
|     } else { |     } else { | ||||||
|         job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size); |         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() */ |     /* Required permissions are already taken with target's blk_new() */ | ||||||
|     block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, |     block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, | ||||||
|                        &error_abort); |                        &error_abort); | ||||||
|     job->len = len; |     job->common.len = len; | ||||||
|  |     block_job_txn_add_job(txn, &job->common); | ||||||
|  |  | ||||||
|     return &job->common; |     return &job->common; | ||||||
|  |  | ||||||
| @@ -716,8 +686,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | |||||||
|         bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL); |         bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL); | ||||||
|     } |     } | ||||||
|     if (job) { |     if (job) { | ||||||
|         backup_clean(&job->common.job); |         backup_clean(&job->common); | ||||||
|         job_early_fail(&job->common.job); |         block_job_early_fail(&job->common); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return NULL; |     return NULL; | ||||||
|   | |||||||
| @@ -305,7 +305,7 @@ static void blkdebug_parse_filename(const char *filename, QDict *options, | |||||||
|  |  | ||||||
|     if (c != filename) { |     if (c != filename) { | ||||||
|         QString *config_path; |         QString *config_path; | ||||||
|         config_path = qstring_from_substr(filename, 0, c - filename); |         config_path = qstring_from_substr(filename, 0, c - filename - 1); | ||||||
|         qdict_put(options, "config", config_path); |         qdict_put(options, "config", config_path); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -398,11 +398,10 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | |     bs->supported_write_flags = BDRV_REQ_FUA & | ||||||
|         (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); |         bs->file->bs->supported_write_flags; | ||||||
|     bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | |     bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & | ||||||
|         ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & |         bs->file->bs->supported_zero_flags; | ||||||
|             bs->file->bs->supported_zero_flags); |  | ||||||
|     ret = -EINVAL; |     ret = -EINVAL; | ||||||
|  |  | ||||||
|     /* Set alignment overrides */ |     /* Set alignment overrides */ | ||||||
| @@ -625,7 +624,7 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs, | |||||||
|         return err; |         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, | 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(); |     opts = qdict_new(); | ||||||
|     qdict_put_str(opts, "driver", "blkdebug"); |     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)) { |     for (e = qdict_first(options); e; e = qdict_next(options, e)) { | ||||||
|         if (strcmp(qdict_entry_key(e), "x-image")) { |         if (strcmp(qdict_entry_key(e), "x-image")) { | ||||||
|             qdict_put_obj(opts, qdict_entry_key(e), |             qobject_incref(qdict_entry_value(e)); | ||||||
|                           qobject_ref(qdict_entry_value(e))); |             qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e)); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,549 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Write logging blk driver based on blkverify and blkdebug. |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2017 Tuomas Tynkkynen <tuomas@tuxera.com> |  | ||||||
|  * Copyright (c) 2018 Aapo Vienamo <aapo@tuxera.com> |  | ||||||
|  * Copyright (c) 2018 Ari Sundholm <ari@tuxera.com> |  | ||||||
|  * |  | ||||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or later. |  | ||||||
|  * See the COPYING file in the top-level directory. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "qapi/error.h" |  | ||||||
| #include "qemu/sockets.h" /* for EINPROGRESS on Windows */ |  | ||||||
| #include "block/block_int.h" |  | ||||||
| #include "qapi/qmp/qdict.h" |  | ||||||
| #include "qapi/qmp/qstring.h" |  | ||||||
| #include "qemu/cutils.h" |  | ||||||
| #include "qemu/option.h" |  | ||||||
|  |  | ||||||
| /* Disk format stuff - taken from Linux drivers/md/dm-log-writes.c */ |  | ||||||
|  |  | ||||||
| #define LOG_FLUSH_FLAG   (1 << 0) |  | ||||||
| #define LOG_FUA_FLAG     (1 << 1) |  | ||||||
| #define LOG_DISCARD_FLAG (1 << 2) |  | ||||||
| #define LOG_MARK_FLAG    (1 << 3) |  | ||||||
| #define LOG_FLAG_MASK    (LOG_FLUSH_FLAG \ |  | ||||||
|                          | LOG_FUA_FLAG \ |  | ||||||
|                          | LOG_DISCARD_FLAG \ |  | ||||||
|                          | LOG_MARK_FLAG) |  | ||||||
|  |  | ||||||
| #define WRITE_LOG_VERSION 1ULL |  | ||||||
| #define WRITE_LOG_MAGIC 0x6a736677736872ULL |  | ||||||
|  |  | ||||||
| /* All fields are little-endian. */ |  | ||||||
| struct log_write_super { |  | ||||||
|     uint64_t magic; |  | ||||||
|     uint64_t version; |  | ||||||
|     uint64_t nr_entries; |  | ||||||
|     uint32_t sectorsize; |  | ||||||
| } QEMU_PACKED; |  | ||||||
|  |  | ||||||
| struct log_write_entry { |  | ||||||
|     uint64_t sector; |  | ||||||
|     uint64_t nr_sectors; |  | ||||||
|     uint64_t flags; |  | ||||||
|     uint64_t data_len; |  | ||||||
| } QEMU_PACKED; |  | ||||||
|  |  | ||||||
| /* End of disk format structures. */ |  | ||||||
|  |  | ||||||
| typedef struct { |  | ||||||
|     BdrvChild *log_file; |  | ||||||
|     uint32_t sectorsize; |  | ||||||
|     uint32_t sectorbits; |  | ||||||
|     uint64_t cur_log_sector; |  | ||||||
|     uint64_t nr_entries; |  | ||||||
|     uint64_t update_interval; |  | ||||||
| } BDRVBlkLogWritesState; |  | ||||||
|  |  | ||||||
| static QemuOptsList runtime_opts = { |  | ||||||
|     .name = "blklogwrites", |  | ||||||
|     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), |  | ||||||
|     .desc = { |  | ||||||
|         { |  | ||||||
|             .name = "log-append", |  | ||||||
|             .type = QEMU_OPT_BOOL, |  | ||||||
|             .help = "Append to an existing log", |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|             .name = "log-sector-size", |  | ||||||
|             .type = QEMU_OPT_SIZE, |  | ||||||
|             .help = "Log sector size", |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|             .name = "log-super-update-interval", |  | ||||||
|             .type = QEMU_OPT_NUMBER, |  | ||||||
|             .help = "Log superblock update interval (# of write requests)", |  | ||||||
|         }, |  | ||||||
|         { /* end of list */ } |  | ||||||
|     }, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static inline uint32_t blk_log_writes_log2(uint32_t value) |  | ||||||
| { |  | ||||||
|     assert(value > 0); |  | ||||||
|     return 31 - clz32(value); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline bool blk_log_writes_sector_size_valid(uint32_t sector_size) |  | ||||||
| { |  | ||||||
|     return is_power_of_2(sector_size) && |  | ||||||
|         sector_size >= sizeof(struct log_write_super) && |  | ||||||
|         sector_size >= sizeof(struct log_write_entry) && |  | ||||||
|         sector_size < (1ull << 24); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static uint64_t blk_log_writes_find_cur_log_sector(BdrvChild *log, |  | ||||||
|                                                    uint32_t sector_size, |  | ||||||
|                                                    uint64_t nr_entries, |  | ||||||
|                                                    Error **errp) |  | ||||||
| { |  | ||||||
|     uint64_t cur_sector = 1; |  | ||||||
|     uint64_t cur_idx = 0; |  | ||||||
|     uint32_t sector_bits = blk_log_writes_log2(sector_size); |  | ||||||
|     struct log_write_entry cur_entry; |  | ||||||
|  |  | ||||||
|     while (cur_idx < nr_entries) { |  | ||||||
|         int read_ret = bdrv_pread(log, cur_sector << sector_bits, &cur_entry, |  | ||||||
|                                   sizeof(cur_entry)); |  | ||||||
|         if (read_ret < 0) { |  | ||||||
|             error_setg_errno(errp, -read_ret, |  | ||||||
|                              "Failed to read log entry %"PRIu64, cur_idx); |  | ||||||
|             return (uint64_t)-1ull; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (cur_entry.flags & ~cpu_to_le64(LOG_FLAG_MASK)) { |  | ||||||
|             error_setg(errp, "Invalid flags 0x%"PRIx64" in log entry %"PRIu64, |  | ||||||
|                        le64_to_cpu(cur_entry.flags), cur_idx); |  | ||||||
|             return (uint64_t)-1ull; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /* Account for the sector of the entry itself */ |  | ||||||
|         ++cur_sector; |  | ||||||
|  |  | ||||||
|         /* |  | ||||||
|          * Account for the data of the write. |  | ||||||
|          * For discards, this data is not present. |  | ||||||
|          */ |  | ||||||
|         if (!(cur_entry.flags & cpu_to_le64(LOG_DISCARD_FLAG))) { |  | ||||||
|             cur_sector += le64_to_cpu(cur_entry.nr_sectors); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         ++cur_idx; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return cur_sector; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, |  | ||||||
|                                Error **errp) |  | ||||||
| { |  | ||||||
|     BDRVBlkLogWritesState *s = bs->opaque; |  | ||||||
|     QemuOpts *opts; |  | ||||||
|     Error *local_err = NULL; |  | ||||||
|     int ret; |  | ||||||
|     uint64_t log_sector_size; |  | ||||||
|     bool log_append; |  | ||||||
|  |  | ||||||
|     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); |  | ||||||
|     qemu_opts_absorb_qdict(opts, options, &local_err); |  | ||||||
|     if (local_err) { |  | ||||||
|         ret = -EINVAL; |  | ||||||
|         error_propagate(errp, local_err); |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Open the file */ |  | ||||||
|     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, |  | ||||||
|                                &local_err); |  | ||||||
|     if (local_err) { |  | ||||||
|         ret = -EINVAL; |  | ||||||
|         error_propagate(errp, local_err); |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Open the log file */ |  | ||||||
|     s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_file, false, |  | ||||||
|                                   &local_err); |  | ||||||
|     if (local_err) { |  | ||||||
|         ret = -EINVAL; |  | ||||||
|         error_propagate(errp, local_err); |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     log_append = qemu_opt_get_bool(opts, "log-append", false); |  | ||||||
|  |  | ||||||
|     if (log_append) { |  | ||||||
|         struct log_write_super log_sb = { 0, 0, 0, 0 }; |  | ||||||
|  |  | ||||||
|         if (qemu_opt_find(opts, "log-sector-size")) { |  | ||||||
|             ret = -EINVAL; |  | ||||||
|             error_setg(errp, "log-append and log-sector-size are mutually " |  | ||||||
|                        "exclusive"); |  | ||||||
|             goto fail_log; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /* Read log superblock or fake one for an empty log */ |  | ||||||
|         if (!bdrv_getlength(s->log_file->bs)) { |  | ||||||
|             log_sb.magic      = cpu_to_le64(WRITE_LOG_MAGIC); |  | ||||||
|             log_sb.version    = cpu_to_le64(WRITE_LOG_VERSION); |  | ||||||
|             log_sb.nr_entries = cpu_to_le64(0); |  | ||||||
|             log_sb.sectorsize = cpu_to_le32(BDRV_SECTOR_SIZE); |  | ||||||
|         } else { |  | ||||||
|             ret = bdrv_pread(s->log_file, 0, &log_sb, sizeof(log_sb)); |  | ||||||
|             if (ret < 0) { |  | ||||||
|                 error_setg_errno(errp, -ret, "Could not read log superblock"); |  | ||||||
|                 goto fail_log; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (log_sb.magic != cpu_to_le64(WRITE_LOG_MAGIC)) { |  | ||||||
|             ret = -EINVAL; |  | ||||||
|             error_setg(errp, "Invalid log superblock magic"); |  | ||||||
|             goto fail_log; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (log_sb.version != cpu_to_le64(WRITE_LOG_VERSION)) { |  | ||||||
|             ret = -EINVAL; |  | ||||||
|             error_setg(errp, "Unsupported log version %"PRIu64, |  | ||||||
|                        le64_to_cpu(log_sb.version)); |  | ||||||
|             goto fail_log; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         log_sector_size = le32_to_cpu(log_sb.sectorsize); |  | ||||||
|         s->cur_log_sector = 1; |  | ||||||
|         s->nr_entries = 0; |  | ||||||
|  |  | ||||||
|         if (blk_log_writes_sector_size_valid(log_sector_size)) { |  | ||||||
|             s->cur_log_sector = |  | ||||||
|                 blk_log_writes_find_cur_log_sector(s->log_file, log_sector_size, |  | ||||||
|                                     le64_to_cpu(log_sb.nr_entries), &local_err); |  | ||||||
|             if (local_err) { |  | ||||||
|                 ret = -EINVAL; |  | ||||||
|                 error_propagate(errp, local_err); |  | ||||||
|                 goto fail_log; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             s->nr_entries = le64_to_cpu(log_sb.nr_entries); |  | ||||||
|         } |  | ||||||
|     } else { |  | ||||||
|         log_sector_size = qemu_opt_get_size(opts, "log-sector-size", |  | ||||||
|                                             BDRV_SECTOR_SIZE); |  | ||||||
|         s->cur_log_sector = 1; |  | ||||||
|         s->nr_entries = 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!blk_log_writes_sector_size_valid(log_sector_size)) { |  | ||||||
|         ret = -EINVAL; |  | ||||||
|         error_setg(errp, "Invalid log sector size %"PRIu64, log_sector_size); |  | ||||||
|         goto fail_log; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     s->sectorsize = log_sector_size; |  | ||||||
|     s->sectorbits = blk_log_writes_log2(log_sector_size); |  | ||||||
|     s->update_interval = qemu_opt_get_number(opts, "log-super-update-interval", |  | ||||||
|                                              4096); |  | ||||||
|     if (!s->update_interval) { |  | ||||||
|         ret = -EINVAL; |  | ||||||
|         error_setg(errp, "Invalid log superblock update interval %"PRIu64, |  | ||||||
|                    s->update_interval); |  | ||||||
|         goto fail_log; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = 0; |  | ||||||
| fail_log: |  | ||||||
|     if (ret < 0) { |  | ||||||
|         bdrv_unref_child(bs, s->log_file); |  | ||||||
|         s->log_file = NULL; |  | ||||||
|     } |  | ||||||
| fail: |  | ||||||
|     if (ret < 0) { |  | ||||||
|         bdrv_unref_child(bs, bs->file); |  | ||||||
|         bs->file = NULL; |  | ||||||
|     } |  | ||||||
|     qemu_opts_del(opts); |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void blk_log_writes_close(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     BDRVBlkLogWritesState *s = bs->opaque; |  | ||||||
|  |  | ||||||
|     bdrv_unref_child(bs, s->log_file); |  | ||||||
|     s->log_file = NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int64_t blk_log_writes_getlength(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     return bdrv_getlength(bs->file->bs); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void blk_log_writes_refresh_filename(BlockDriverState *bs, |  | ||||||
|                                             QDict *options) |  | ||||||
| { |  | ||||||
|     BDRVBlkLogWritesState *s = bs->opaque; |  | ||||||
|  |  | ||||||
|     /* bs->file->bs has already been refreshed */ |  | ||||||
|     bdrv_refresh_filename(s->log_file->bs); |  | ||||||
|  |  | ||||||
|     if (bs->file->bs->full_open_options |  | ||||||
|         && s->log_file->bs->full_open_options) |  | ||||||
|     { |  | ||||||
|         QDict *opts = qdict_new(); |  | ||||||
|         qdict_put_str(opts, "driver", "blklogwrites"); |  | ||||||
|  |  | ||||||
|         qobject_ref(bs->file->bs->full_open_options); |  | ||||||
|         qdict_put(opts, "file", bs->file->bs->full_open_options); |  | ||||||
|         qobject_ref(s->log_file->bs->full_open_options); |  | ||||||
|         qdict_put(opts, "log", s->log_file->bs->full_open_options); |  | ||||||
|         qdict_put_int(opts, "log-sector-size", s->sectorsize); |  | ||||||
|  |  | ||||||
|         bs->full_open_options = opts; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c, |  | ||||||
|                                       const BdrvChildRole *role, |  | ||||||
|                                       BlockReopenQueue *ro_q, |  | ||||||
|                                       uint64_t perm, uint64_t shrd, |  | ||||||
|                                       uint64_t *nperm, uint64_t *nshrd) |  | ||||||
| { |  | ||||||
|     if (!c) { |  | ||||||
|         *nperm = perm & DEFAULT_PERM_PASSTHROUGH; |  | ||||||
|         *nshrd = (shrd & DEFAULT_PERM_PASSTHROUGH) | DEFAULT_PERM_UNCHANGED; |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!strcmp(c->name, "log")) { |  | ||||||
|         bdrv_format_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd); |  | ||||||
|     } else { |  | ||||||
|         bdrv_filter_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void blk_log_writes_refresh_limits(BlockDriverState *bs, Error **errp) |  | ||||||
| { |  | ||||||
|     BDRVBlkLogWritesState *s = bs->opaque; |  | ||||||
|     bs->bl.request_alignment = s->sectorsize; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int coroutine_fn |  | ||||||
| blk_log_writes_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, |  | ||||||
|                          QEMUIOVector *qiov, int flags) |  | ||||||
| { |  | ||||||
|     return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| typedef struct BlkLogWritesFileReq { |  | ||||||
|     BlockDriverState *bs; |  | ||||||
|     uint64_t offset; |  | ||||||
|     uint64_t bytes; |  | ||||||
|     int file_flags; |  | ||||||
|     QEMUIOVector *qiov; |  | ||||||
|     int (*func)(struct BlkLogWritesFileReq *r); |  | ||||||
|     int file_ret; |  | ||||||
| } BlkLogWritesFileReq; |  | ||||||
|  |  | ||||||
| typedef struct { |  | ||||||
|     BlockDriverState *bs; |  | ||||||
|     QEMUIOVector *qiov; |  | ||||||
|     struct log_write_entry entry; |  | ||||||
|     uint64_t zero_size; |  | ||||||
|     int log_ret; |  | ||||||
| } BlkLogWritesLogReq; |  | ||||||
|  |  | ||||||
| static void coroutine_fn blk_log_writes_co_do_log(BlkLogWritesLogReq *lr) |  | ||||||
| { |  | ||||||
|     BDRVBlkLogWritesState *s = lr->bs->opaque; |  | ||||||
|     uint64_t cur_log_offset = s->cur_log_sector << s->sectorbits; |  | ||||||
|  |  | ||||||
|     s->nr_entries++; |  | ||||||
|     s->cur_log_sector += |  | ||||||
|             ROUND_UP(lr->qiov->size, s->sectorsize) >> s->sectorbits; |  | ||||||
|  |  | ||||||
|     lr->log_ret = bdrv_co_pwritev(s->log_file, cur_log_offset, lr->qiov->size, |  | ||||||
|                                   lr->qiov, 0); |  | ||||||
|  |  | ||||||
|     /* Logging for the "write zeroes" operation */ |  | ||||||
|     if (lr->log_ret == 0 && lr->zero_size) { |  | ||||||
|         cur_log_offset = s->cur_log_sector << s->sectorbits; |  | ||||||
|         s->cur_log_sector += |  | ||||||
|                 ROUND_UP(lr->zero_size, s->sectorsize) >> s->sectorbits; |  | ||||||
|  |  | ||||||
|         lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, cur_log_offset, |  | ||||||
|                                             lr->zero_size, 0); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Update super block on flush or every update interval */ |  | ||||||
|     if (lr->log_ret == 0 && ((lr->entry.flags & LOG_FLUSH_FLAG) |  | ||||||
|         || (s->nr_entries % s->update_interval == 0))) |  | ||||||
|     { |  | ||||||
|         struct log_write_super super = { |  | ||||||
|             .magic      = cpu_to_le64(WRITE_LOG_MAGIC), |  | ||||||
|             .version    = cpu_to_le64(WRITE_LOG_VERSION), |  | ||||||
|             .nr_entries = cpu_to_le64(s->nr_entries), |  | ||||||
|             .sectorsize = cpu_to_le32(s->sectorsize), |  | ||||||
|         }; |  | ||||||
|         void *zeroes = g_malloc0(s->sectorsize - sizeof(super)); |  | ||||||
|         QEMUIOVector qiov; |  | ||||||
|  |  | ||||||
|         qemu_iovec_init(&qiov, 2); |  | ||||||
|         qemu_iovec_add(&qiov, &super, sizeof(super)); |  | ||||||
|         qemu_iovec_add(&qiov, zeroes, s->sectorsize - sizeof(super)); |  | ||||||
|  |  | ||||||
|         lr->log_ret = |  | ||||||
|             bdrv_co_pwritev(s->log_file, 0, s->sectorsize, &qiov, 0); |  | ||||||
|         if (lr->log_ret == 0) { |  | ||||||
|             lr->log_ret = bdrv_co_flush(s->log_file->bs); |  | ||||||
|         } |  | ||||||
|         qemu_iovec_destroy(&qiov); |  | ||||||
|         g_free(zeroes); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void coroutine_fn blk_log_writes_co_do_file(BlkLogWritesFileReq *fr) |  | ||||||
| { |  | ||||||
|     fr->file_ret = fr->func(fr); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int coroutine_fn |  | ||||||
| blk_log_writes_co_log(BlockDriverState *bs, uint64_t offset, uint64_t bytes, |  | ||||||
|                       QEMUIOVector *qiov, int flags, |  | ||||||
|                       int (*file_func)(BlkLogWritesFileReq *r), |  | ||||||
|                       uint64_t entry_flags, bool is_zero_write) |  | ||||||
| { |  | ||||||
|     QEMUIOVector log_qiov; |  | ||||||
|     size_t niov = qiov ? qiov->niov : 0; |  | ||||||
|     BDRVBlkLogWritesState *s = bs->opaque; |  | ||||||
|     BlkLogWritesFileReq fr = { |  | ||||||
|         .bs         = bs, |  | ||||||
|         .offset     = offset, |  | ||||||
|         .bytes      = bytes, |  | ||||||
|         .file_flags = flags, |  | ||||||
|         .qiov       = qiov, |  | ||||||
|         .func       = file_func, |  | ||||||
|     }; |  | ||||||
|     BlkLogWritesLogReq lr = { |  | ||||||
|         .bs             = bs, |  | ||||||
|         .qiov           = &log_qiov, |  | ||||||
|         .entry = { |  | ||||||
|             .sector     = cpu_to_le64(offset >> s->sectorbits), |  | ||||||
|             .nr_sectors = cpu_to_le64(bytes >> s->sectorbits), |  | ||||||
|             .flags      = cpu_to_le64(entry_flags), |  | ||||||
|             .data_len   = 0, |  | ||||||
|         }, |  | ||||||
|         .zero_size = is_zero_write ? bytes : 0, |  | ||||||
|     }; |  | ||||||
|     void *zeroes = g_malloc0(s->sectorsize - sizeof(lr.entry)); |  | ||||||
|  |  | ||||||
|     assert((1 << s->sectorbits) == s->sectorsize); |  | ||||||
|     assert(bs->bl.request_alignment == s->sectorsize); |  | ||||||
|     assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment)); |  | ||||||
|     assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment)); |  | ||||||
|  |  | ||||||
|     qemu_iovec_init(&log_qiov, niov + 2); |  | ||||||
|     qemu_iovec_add(&log_qiov, &lr.entry, sizeof(lr.entry)); |  | ||||||
|     qemu_iovec_add(&log_qiov, zeroes, s->sectorsize - sizeof(lr.entry)); |  | ||||||
|     if (qiov) { |  | ||||||
|         qemu_iovec_concat(&log_qiov, qiov, 0, qiov->size); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     blk_log_writes_co_do_file(&fr); |  | ||||||
|     blk_log_writes_co_do_log(&lr); |  | ||||||
|  |  | ||||||
|     qemu_iovec_destroy(&log_qiov); |  | ||||||
|     g_free(zeroes); |  | ||||||
|  |  | ||||||
|     if (lr.log_ret < 0) { |  | ||||||
|         return lr.log_ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return fr.file_ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int coroutine_fn |  | ||||||
| blk_log_writes_co_do_file_pwritev(BlkLogWritesFileReq *fr) |  | ||||||
| { |  | ||||||
|     return bdrv_co_pwritev(fr->bs->file, fr->offset, fr->bytes, |  | ||||||
|                            fr->qiov, fr->file_flags); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int coroutine_fn |  | ||||||
| blk_log_writes_co_do_file_pwrite_zeroes(BlkLogWritesFileReq *fr) |  | ||||||
| { |  | ||||||
|     return bdrv_co_pwrite_zeroes(fr->bs->file, fr->offset, fr->bytes, |  | ||||||
|                                  fr->file_flags); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int coroutine_fn blk_log_writes_co_do_file_flush(BlkLogWritesFileReq *fr) |  | ||||||
| { |  | ||||||
|     return bdrv_co_flush(fr->bs->file->bs); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int coroutine_fn |  | ||||||
| blk_log_writes_co_do_file_pdiscard(BlkLogWritesFileReq *fr) |  | ||||||
| { |  | ||||||
|     return bdrv_co_pdiscard(fr->bs->file, fr->offset, fr->bytes); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int coroutine_fn |  | ||||||
| blk_log_writes_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, |  | ||||||
|                           QEMUIOVector *qiov, int flags) |  | ||||||
| { |  | ||||||
|     return blk_log_writes_co_log(bs, offset, bytes, qiov, flags, |  | ||||||
|                                  blk_log_writes_co_do_file_pwritev, 0, false); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int coroutine_fn |  | ||||||
| blk_log_writes_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, |  | ||||||
|                                 BdrvRequestFlags flags) |  | ||||||
| { |  | ||||||
|     return blk_log_writes_co_log(bs, offset, bytes, NULL, flags, |  | ||||||
|                                  blk_log_writes_co_do_file_pwrite_zeroes, 0, |  | ||||||
|                                  true); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int coroutine_fn blk_log_writes_co_flush_to_disk(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     return blk_log_writes_co_log(bs, 0, 0, NULL, 0, |  | ||||||
|                                  blk_log_writes_co_do_file_flush, |  | ||||||
|                                  LOG_FLUSH_FLAG, false); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int coroutine_fn |  | ||||||
| blk_log_writes_co_pdiscard(BlockDriverState *bs, int64_t offset, int count) |  | ||||||
| { |  | ||||||
|     return blk_log_writes_co_log(bs, offset, count, NULL, 0, |  | ||||||
|                                  blk_log_writes_co_do_file_pdiscard, |  | ||||||
|                                  LOG_DISCARD_FLAG, false); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static BlockDriver bdrv_blk_log_writes = { |  | ||||||
|     .format_name            = "blklogwrites", |  | ||||||
|     .instance_size          = sizeof(BDRVBlkLogWritesState), |  | ||||||
|  |  | ||||||
|     .bdrv_open              = blk_log_writes_open, |  | ||||||
|     .bdrv_close             = blk_log_writes_close, |  | ||||||
|     .bdrv_getlength         = blk_log_writes_getlength, |  | ||||||
|     .bdrv_refresh_filename  = blk_log_writes_refresh_filename, |  | ||||||
|     .bdrv_child_perm        = blk_log_writes_child_perm, |  | ||||||
|     .bdrv_refresh_limits    = blk_log_writes_refresh_limits, |  | ||||||
|  |  | ||||||
|     .bdrv_co_preadv         = blk_log_writes_co_preadv, |  | ||||||
|     .bdrv_co_pwritev        = blk_log_writes_co_pwritev, |  | ||||||
|     .bdrv_co_pwrite_zeroes  = blk_log_writes_co_pwrite_zeroes, |  | ||||||
|     .bdrv_co_flush_to_disk  = blk_log_writes_co_flush_to_disk, |  | ||||||
|     .bdrv_co_pdiscard       = blk_log_writes_co_pdiscard, |  | ||||||
|     .bdrv_co_block_status   = bdrv_co_block_status_from_file, |  | ||||||
|  |  | ||||||
|     .is_filter              = true, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void bdrv_blk_log_writes_init(void) |  | ||||||
| { |  | ||||||
|     bdrv_register(&bdrv_blk_log_writes); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| block_init(bdrv_blk_log_writes_init); |  | ||||||
							
								
								
									
										10
									
								
								block/blkreplay.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										10
									
								
								block/blkreplay.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -35,14 +35,15 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; |  | ||||||
|     bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; |  | ||||||
|  |  | ||||||
|     ret = 0; |     ret = 0; | ||||||
| fail: | fail: | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void blkreplay_close(BlockDriverState *bs) | ||||||
|  | { | ||||||
|  | } | ||||||
|  |  | ||||||
| static int64_t blkreplay_getlength(BlockDriverState *bs) | static int64_t blkreplay_getlength(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     return bdrv_getlength(bs->file->bs); |     return bdrv_getlength(bs->file->bs); | ||||||
| @@ -109,7 +110,7 @@ static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs, | |||||||
|                                               int64_t offset, int bytes) |                                               int64_t offset, int bytes) | ||||||
| { | { | ||||||
|     uint64_t reqid = blkreplay_next_id(); |     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()); |     block_request_create(reqid, bs, qemu_coroutine_self()); | ||||||
|     qemu_coroutine_yield(); |     qemu_coroutine_yield(); | ||||||
|  |  | ||||||
| @@ -131,6 +132,7 @@ static BlockDriver bdrv_blkreplay = { | |||||||
|     .instance_size          = 0, |     .instance_size          = 0, | ||||||
|  |  | ||||||
|     .bdrv_open              = blkreplay_open, |     .bdrv_open              = blkreplay_open, | ||||||
|  |     .bdrv_close             = blkreplay_close, | ||||||
|     .bdrv_child_perm        = bdrv_filter_default_perms, |     .bdrv_child_perm        = bdrv_filter_default_perms, | ||||||
|     .bdrv_getlength         = blkreplay_getlength, |     .bdrv_getlength         = blkreplay_getlength, | ||||||
|  |  | ||||||
|   | |||||||
| @@ -80,7 +80,7 @@ static void blkverify_parse_filename(const char *filename, QDict *options, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* TODO Implement option pass-through and set raw.filename here */ |     /* TODO Implement option pass-through and set raw.filename here */ | ||||||
|     raw_path = qstring_from_substr(filename, 0, c - filename); |     raw_path = qstring_from_substr(filename, 0, c - filename - 1); | ||||||
|     qdict_put(options, "x-raw", raw_path); |     qdict_put(options, "x-raw", raw_path); | ||||||
|  |  | ||||||
|     /* TODO Allow multi-level nesting and set file.filename here */ |     /* TODO Allow multi-level nesting and set file.filename here */ | ||||||
| @@ -141,9 +141,6 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; |  | ||||||
|     bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; |  | ||||||
|  |  | ||||||
|     ret = 0; |     ret = 0; | ||||||
| fail: | fail: | ||||||
|     qemu_opts_del(opts); |     qemu_opts_del(opts); | ||||||
| @@ -294,10 +291,10 @@ static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options) | |||||||
|         QDict *opts = qdict_new(); |         QDict *opts = qdict_new(); | ||||||
|         qdict_put_str(opts, "driver", "blkverify"); |         qdict_put_str(opts, "driver", "blkverify"); | ||||||
|  |  | ||||||
|         qdict_put(opts, "raw", |         QINCREF(bs->file->bs->full_open_options); | ||||||
|                   qobject_ref(bs->file->bs->full_open_options)); |         qdict_put(opts, "raw", bs->file->bs->full_open_options); | ||||||
|         qdict_put(opts, "test", |         QINCREF(s->test_file->bs->full_open_options); | ||||||
|                   qobject_ref(s->test_file->bs->full_open_options)); |         qdict_put(opts, "test", s->test_file->bs->full_open_options); | ||||||
|  |  | ||||||
|         bs->full_open_options = opts; |         bs->full_open_options = opts; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -31,13 +31,6 @@ | |||||||
|  |  | ||||||
| static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb); | 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 { | struct BlockBackend { | ||||||
|     char *name; |     char *name; | ||||||
|     int refcnt; |     int refcnt; | ||||||
| @@ -47,7 +40,9 @@ struct BlockBackend { | |||||||
|     QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */ |     QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */ | ||||||
|     BlockBackendPublic public; |     BlockBackendPublic public; | ||||||
|  |  | ||||||
|     DeviceState *dev;           /* attached device model, if any */ |     void *dev;                  /* attached device model, if any */ | ||||||
|  |     bool legacy_dev;            /* true if dev is not a DeviceState */ | ||||||
|  |     /* TODO change to DeviceState when all users are qdevified */ | ||||||
|     const BlockDevOps *dev_ops; |     const BlockDevOps *dev_ops; | ||||||
|     void *dev_opaque; |     void *dev_opaque; | ||||||
|  |  | ||||||
| @@ -74,7 +69,6 @@ struct BlockBackend { | |||||||
|     bool allow_write_beyond_eof; |     bool allow_write_beyond_eof; | ||||||
|  |  | ||||||
|     NotifierList remove_bs_notifiers, insert_bs_notifiers; |     NotifierList remove_bs_notifiers, insert_bs_notifiers; | ||||||
|     QLIST_HEAD(, BlockBackendAioNotifier) aio_notifiers; |  | ||||||
|  |  | ||||||
|     int quiesce_counter; |     int quiesce_counter; | ||||||
|     VMChangeStateEntry *vmsh; |     VMChangeStateEntry *vmsh; | ||||||
| @@ -86,6 +80,7 @@ struct BlockBackend { | |||||||
|      * Accessed with atomic ops. |      * Accessed with atomic ops. | ||||||
|      */ |      */ | ||||||
|     unsigned int in_flight; |     unsigned int in_flight; | ||||||
|  |     AioWait wait; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef struct BlockBackendAIOCB { | typedef struct BlockBackendAIOCB { | ||||||
| @@ -118,7 +113,6 @@ static void blk_root_inherit_options(int *child_flags, QDict *child_options, | |||||||
|     abort(); |     abort(); | ||||||
| } | } | ||||||
| static void blk_root_drained_begin(BdrvChild *child); | static void blk_root_drained_begin(BdrvChild *child); | ||||||
| static bool blk_root_drained_poll(BdrvChild *child); |  | ||||||
| static void blk_root_drained_end(BdrvChild *child); | static void blk_root_drained_end(BdrvChild *child); | ||||||
|  |  | ||||||
| static void blk_root_change_media(BdrvChild *child, bool load); | static void blk_root_change_media(BdrvChild *child, bool load); | ||||||
| @@ -253,36 +247,6 @@ static int blk_root_inactivate(BdrvChild *child) | |||||||
|     return 0; |     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 = { | static const BdrvChildRole child_root = { | ||||||
|     .inherit_options    = blk_root_inherit_options, |     .inherit_options    = blk_root_inherit_options, | ||||||
|  |  | ||||||
| @@ -292,14 +256,10 @@ static const BdrvChildRole child_root = { | |||||||
|     .get_parent_desc    = blk_root_get_parent_desc, |     .get_parent_desc    = blk_root_get_parent_desc, | ||||||
|  |  | ||||||
|     .drained_begin      = blk_root_drained_begin, |     .drained_begin      = blk_root_drained_begin, | ||||||
|     .drained_poll       = blk_root_drained_poll, |  | ||||||
|     .drained_end        = blk_root_drained_end, |     .drained_end        = blk_root_drained_end, | ||||||
|  |  | ||||||
|     .activate           = blk_root_activate, |     .activate           = blk_root_activate, | ||||||
|     .inactivate         = blk_root_inactivate, |     .inactivate         = blk_root_inactivate, | ||||||
|  |  | ||||||
|     .attach             = blk_root_attach, |  | ||||||
|     .detach             = blk_root_detach, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* | /* | ||||||
| @@ -323,14 +283,10 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm) | |||||||
|     blk->shared_perm = shared_perm; |     blk->shared_perm = shared_perm; | ||||||
|     blk_set_enable_write_cache(blk, true); |     blk_set_enable_write_cache(blk, true); | ||||||
|  |  | ||||||
|     blk->on_read_error = BLOCKDEV_ON_ERROR_REPORT; |  | ||||||
|     blk->on_write_error = BLOCKDEV_ON_ERROR_ENOSPC; |  | ||||||
|  |  | ||||||
|     block_acct_init(&blk->stats); |     block_acct_init(&blk->stats); | ||||||
|  |  | ||||||
|     notifier_list_init(&blk->remove_bs_notifiers); |     notifier_list_init(&blk->remove_bs_notifiers); | ||||||
|     notifier_list_init(&blk->insert_bs_notifiers); |     notifier_list_init(&blk->insert_bs_notifiers); | ||||||
|     QLIST_INIT(&blk->aio_notifiers); |  | ||||||
|  |  | ||||||
|     QTAILQ_INSERT_TAIL(&block_backends, blk, link); |     QTAILQ_INSERT_TAIL(&block_backends, blk, link); | ||||||
|     return blk; |     return blk; | ||||||
| @@ -408,7 +364,6 @@ static void blk_delete(BlockBackend *blk) | |||||||
|     } |     } | ||||||
|     assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers)); |     assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers)); | ||||||
|     assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers)); |     assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers)); | ||||||
|     assert(QLIST_EMPTY(&blk->aio_notifiers)); |  | ||||||
|     QTAILQ_REMOVE(&block_backends, blk, link); |     QTAILQ_REMOVE(&block_backends, blk, link); | ||||||
|     drive_info_del(blk->legacy_dinfo); |     drive_info_del(blk->legacy_dinfo); | ||||||
|     block_acct_cleanup(&blk->stats); |     block_acct_cleanup(&blk->stats); | ||||||
| @@ -421,6 +376,7 @@ static void drive_info_del(DriveInfo *dinfo) | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     qemu_opts_del(dinfo->opts); |     qemu_opts_del(dinfo->opts); | ||||||
|  |     g_free(dinfo->serial); | ||||||
|     g_free(dinfo); |     g_free(dinfo); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -435,7 +391,6 @@ int blk_get_refcnt(BlockBackend *blk) | |||||||
|  */ |  */ | ||||||
| void blk_ref(BlockBackend *blk) | void blk_ref(BlockBackend *blk) | ||||||
| { | { | ||||||
|     assert(blk->refcnt > 0); |  | ||||||
|     blk->refcnt++; |     blk->refcnt++; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -448,13 +403,7 @@ void blk_unref(BlockBackend *blk) | |||||||
| { | { | ||||||
|     if (blk) { |     if (blk) { | ||||||
|         assert(blk->refcnt > 0); |         assert(blk->refcnt > 0); | ||||||
|         if (blk->refcnt > 1) { |         if (!--blk->refcnt) { | ||||||
|             blk->refcnt--; |  | ||||||
|         } else { |  | ||||||
|             blk_drain(blk); |  | ||||||
|             /* blk_drain() cannot resurrect blk, nobody held a reference */ |  | ||||||
|             assert(blk->refcnt == 1); |  | ||||||
|             blk->refcnt = 0; |  | ||||||
|             blk_delete(blk); |             blk_delete(blk); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -776,11 +725,6 @@ void blk_remove_bs(BlockBackend *blk) | |||||||
|  |  | ||||||
|     blk_update_root_state(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); |     bdrv_root_unref_child(blk->root); | ||||||
|     blk->root = NULL; |     blk->root = NULL; | ||||||
| } | } | ||||||
| @@ -834,11 +778,7 @@ void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm) | |||||||
|     *shared_perm = blk->shared_perm; |     *shared_perm = blk->shared_perm; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | static int blk_do_attach_dev(BlockBackend *blk, void *dev) | ||||||
|  * Attach device model @dev to @blk. |  | ||||||
|  * Return 0 on success, -EBUSY when a device model is attached already. |  | ||||||
|  */ |  | ||||||
| int blk_attach_dev(BlockBackend *blk, DeviceState *dev) |  | ||||||
| { | { | ||||||
|     if (blk->dev) { |     if (blk->dev) { | ||||||
|         return -EBUSY; |         return -EBUSY; | ||||||
| @@ -853,16 +793,40 @@ int blk_attach_dev(BlockBackend *blk, DeviceState *dev) | |||||||
|  |  | ||||||
|     blk_ref(blk); |     blk_ref(blk); | ||||||
|     blk->dev = dev; |     blk->dev = dev; | ||||||
|  |     blk->legacy_dev = false; | ||||||
|     blk_iostatus_reset(blk); |     blk_iostatus_reset(blk); | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Attach device model @dev to @blk. | ||||||
|  |  * Return 0 on success, -EBUSY when a device model is attached already. | ||||||
|  |  */ | ||||||
|  | int blk_attach_dev(BlockBackend *blk, DeviceState *dev) | ||||||
|  | { | ||||||
|  |     return blk_do_attach_dev(blk, dev); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Attach device model @dev to @blk. | ||||||
|  |  * @blk must not have a device model attached already. | ||||||
|  |  * TODO qdevified devices don't use this, remove when devices are qdevified | ||||||
|  |  */ | ||||||
|  | void blk_attach_dev_legacy(BlockBackend *blk, void *dev) | ||||||
|  | { | ||||||
|  |     if (blk_do_attach_dev(blk, dev) < 0) { | ||||||
|  |         abort(); | ||||||
|  |     } | ||||||
|  |     blk->legacy_dev = true; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Detach device model @dev from @blk. |  * Detach device model @dev from @blk. | ||||||
|  * @dev must be currently attached to @blk. |  * @dev must be currently attached to @blk. | ||||||
|  */ |  */ | ||||||
| void blk_detach_dev(BlockBackend *blk, DeviceState *dev) | void blk_detach_dev(BlockBackend *blk, void *dev) | ||||||
|  | /* TODO change to DeviceState *dev when all users are qdevified */ | ||||||
| { | { | ||||||
|     assert(blk->dev == dev); |     assert(blk->dev == dev); | ||||||
|     blk->dev = NULL; |     blk->dev = NULL; | ||||||
| @@ -876,7 +840,8 @@ void blk_detach_dev(BlockBackend *blk, DeviceState *dev) | |||||||
| /* | /* | ||||||
|  * Return the device model attached to @blk if any, else null. |  * Return the device model attached to @blk if any, else null. | ||||||
|  */ |  */ | ||||||
| DeviceState *blk_get_attached_dev(BlockBackend *blk) | void *blk_get_attached_dev(BlockBackend *blk) | ||||||
|  | /* TODO change to return DeviceState * when all users are qdevified */ | ||||||
| { | { | ||||||
|     return blk->dev; |     return blk->dev; | ||||||
| } | } | ||||||
| @@ -885,15 +850,17 @@ DeviceState *blk_get_attached_dev(BlockBackend *blk) | |||||||
|  * device attached to the BlockBackend. */ |  * device attached to the BlockBackend. */ | ||||||
| char *blk_get_attached_dev_id(BlockBackend *blk) | char *blk_get_attached_dev_id(BlockBackend *blk) | ||||||
| { | { | ||||||
|     DeviceState *dev = blk->dev; |     DeviceState *dev; | ||||||
|  |  | ||||||
|  |     assert(!blk->legacy_dev); | ||||||
|  |     dev = blk->dev; | ||||||
|  |  | ||||||
|     if (!dev) { |     if (!dev) { | ||||||
|         return g_strdup(""); |         return g_strdup(""); | ||||||
|     } else if (dev->id) { |     } else if (dev->id) { | ||||||
|         return g_strdup(dev->id); |         return g_strdup(dev->id); | ||||||
|     } |     } | ||||||
|  |     return object_get_canonical_path(OBJECT(dev)); | ||||||
|     return object_get_canonical_path(OBJECT(dev)) ?: g_strdup(""); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
| @@ -923,6 +890,11 @@ BlockBackend *blk_by_dev(void *dev) | |||||||
| void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, | void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, | ||||||
|                      void *opaque) |                      void *opaque) | ||||||
| { | { | ||||||
|  |     /* All drivers that use blk_set_dev_ops() are qdevified and we want to keep | ||||||
|  |      * it that way, so we can assume blk->dev, if present, is a DeviceState if | ||||||
|  |      * blk->dev_ops is set. Non-device users may use dev_ops without device. */ | ||||||
|  |     assert(!blk->legacy_dev); | ||||||
|  |  | ||||||
|     blk->dev_ops = ops; |     blk->dev_ops = ops; | ||||||
|     blk->dev_opaque = opaque; |     blk->dev_opaque = opaque; | ||||||
|  |  | ||||||
| @@ -948,6 +920,8 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp) | |||||||
|         bool tray_was_open, tray_is_open; |         bool tray_was_open, tray_is_open; | ||||||
|         Error *local_err = NULL; |         Error *local_err = NULL; | ||||||
|  |  | ||||||
|  |         assert(!blk->legacy_dev); | ||||||
|  |  | ||||||
|         tray_was_open = blk_dev_is_tray_open(blk); |         tray_was_open = blk_dev_is_tray_open(blk); | ||||||
|         blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err); |         blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err); | ||||||
|         if (local_err) { |         if (local_err) { | ||||||
| @@ -959,7 +933,8 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp) | |||||||
|  |  | ||||||
|         if (tray_was_open != tray_is_open) { |         if (tray_was_open != tray_is_open) { | ||||||
|             char *id = blk_get_attached_dev_id(blk); |             char *id = blk_get_attached_dev_id(blk); | ||||||
|             qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open); |             qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open, | ||||||
|  |                                               &error_abort); | ||||||
|             g_free(id); |             g_free(id); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -1187,7 +1162,6 @@ static void blk_read_entry(void *opaque) | |||||||
|  |  | ||||||
|     rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size, |     rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size, | ||||||
|                               qiov, rwco->flags); |                               qiov, rwco->flags); | ||||||
|     aio_wait_kick(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void blk_write_entry(void *opaque) | static void blk_write_entry(void *opaque) | ||||||
| @@ -1197,7 +1171,6 @@ static void blk_write_entry(void *opaque) | |||||||
|  |  | ||||||
|     rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size, |     rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size, | ||||||
|                                qiov, rwco->flags); |                                qiov, rwco->flags); | ||||||
|     aio_wait_kick(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, | static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, | ||||||
| @@ -1270,7 +1243,7 @@ static void blk_inc_in_flight(BlockBackend *blk) | |||||||
| static void blk_dec_in_flight(BlockBackend *blk) | static void blk_dec_in_flight(BlockBackend *blk) | ||||||
| { | { | ||||||
|     atomic_dec(&blk->in_flight); |     atomic_dec(&blk->in_flight); | ||||||
|     aio_wait_kick(); |     aio_wait_kick(&blk->wait); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void error_callback_bh(void *opaque) | static void error_callback_bh(void *opaque) | ||||||
| @@ -1311,8 +1284,8 @@ static const AIOCBInfo blk_aio_em_aiocb_info = { | |||||||
| static void blk_aio_complete(BlkAioEmAIOCB *acb) | static void blk_aio_complete(BlkAioEmAIOCB *acb) | ||||||
| { | { | ||||||
|     if (acb->has_returned) { |     if (acb->has_returned) { | ||||||
|         acb->common.cb(acb->common.opaque, acb->rwco.ret); |  | ||||||
|         blk_dec_in_flight(acb->rwco.blk); |         blk_dec_in_flight(acb->rwco.blk); | ||||||
|  |         acb->common.cb(acb->common.opaque, acb->rwco.ret); | ||||||
|         qemu_aio_unref(acb); |         qemu_aio_unref(acb); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -1509,7 +1482,6 @@ static void blk_ioctl_entry(void *opaque) | |||||||
|  |  | ||||||
|     rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, |     rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, | ||||||
|                              qiov->iov[0].iov_base); |                              qiov->iov[0].iov_base); | ||||||
|     aio_wait_kick(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) | int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) | ||||||
| @@ -1540,7 +1512,7 @@ int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes) | |||||||
|         return ret; |         return ret; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return bdrv_co_pdiscard(blk->root, offset, bytes); |     return bdrv_co_pdiscard(blk_bs(blk), offset, bytes); | ||||||
| } | } | ||||||
|  |  | ||||||
| int blk_co_flush(BlockBackend *blk) | int blk_co_flush(BlockBackend *blk) | ||||||
| @@ -1556,7 +1528,6 @@ static void blk_flush_entry(void *opaque) | |||||||
| { | { | ||||||
|     BlkRwCo *rwco = opaque; |     BlkRwCo *rwco = opaque; | ||||||
|     rwco->ret = blk_co_flush(rwco->blk); |     rwco->ret = blk_co_flush(rwco->blk); | ||||||
|     aio_wait_kick(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| int blk_flush(BlockBackend *blk) | int blk_flush(BlockBackend *blk) | ||||||
| @@ -1573,7 +1544,8 @@ void blk_drain(BlockBackend *blk) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* We may have -ENOMEDIUM completions in flight */ |     /* We may have -ENOMEDIUM completions in flight */ | ||||||
|     AIO_WAIT_WHILE(blk_get_aio_context(blk), |     AIO_WAIT_WHILE(&blk->wait, | ||||||
|  |             blk_get_aio_context(blk), | ||||||
|             atomic_mb_read(&blk->in_flight) > 0); |             atomic_mb_read(&blk->in_flight) > 0); | ||||||
|  |  | ||||||
|     if (bs) { |     if (bs) { | ||||||
| @@ -1593,7 +1565,8 @@ void blk_drain_all(void) | |||||||
|         aio_context_acquire(ctx); |         aio_context_acquire(ctx); | ||||||
|  |  | ||||||
|         /* We may have -ENOMEDIUM completions in flight */ |         /* We may have -ENOMEDIUM completions in flight */ | ||||||
|         AIO_WAIT_WHILE(ctx, atomic_mb_read(&blk->in_flight) > 0); |         AIO_WAIT_WHILE(&blk->wait, ctx, | ||||||
|  |                 atomic_mb_read(&blk->in_flight) > 0); | ||||||
|  |  | ||||||
|         aio_context_release(ctx); |         aio_context_release(ctx); | ||||||
|     } |     } | ||||||
| @@ -1645,7 +1618,8 @@ static void send_qmp_error_event(BlockBackend *blk, | |||||||
|     qapi_event_send_block_io_error(blk_name(blk), !!bs, |     qapi_event_send_block_io_error(blk_name(blk), !!bs, | ||||||
|                                    bs ? bdrv_get_node_name(bs) : NULL, optype, |                                    bs ? bdrv_get_node_name(bs) : NULL, optype, | ||||||
|                                    action, blk_iostatus_is_enabled(blk), |                                    action, blk_iostatus_is_enabled(blk), | ||||||
|                                    error == ENOSPC, strerror(error)); |                                    error == ENOSPC, strerror(error), | ||||||
|  |                                    &error_abort); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* This is done by device models because, while the block layer knows | /* This is done by device models because, while the block layer knows | ||||||
| @@ -1680,7 +1654,7 @@ void blk_error_action(BlockBackend *blk, BlockErrorAction action, | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| bool blk_is_read_only(BlockBackend *blk) | int blk_is_read_only(BlockBackend *blk) | ||||||
| { | { | ||||||
|     BlockDriverState *bs = blk_bs(blk); |     BlockDriverState *bs = blk_bs(blk); | ||||||
|  |  | ||||||
| @@ -1691,18 +1665,18 @@ bool blk_is_read_only(BlockBackend *blk) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| bool blk_is_sg(BlockBackend *blk) | int blk_is_sg(BlockBackend *blk) | ||||||
| { | { | ||||||
|     BlockDriverState *bs = blk_bs(blk); |     BlockDriverState *bs = blk_bs(blk); | ||||||
|  |  | ||||||
|     if (!bs) { |     if (!bs) { | ||||||
|         return false; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return bdrv_is_sg(bs); |     return bdrv_is_sg(bs); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool blk_enable_write_cache(BlockBackend *blk) | int blk_enable_write_cache(BlockBackend *blk) | ||||||
| { | { | ||||||
|     return blk->enable_write_cache; |     return blk->enable_write_cache; | ||||||
| } | } | ||||||
| @@ -1750,6 +1724,9 @@ void blk_eject(BlockBackend *blk, bool eject_flag) | |||||||
|     BlockDriverState *bs = blk_bs(blk); |     BlockDriverState *bs = blk_bs(blk); | ||||||
|     char *id; |     char *id; | ||||||
|  |  | ||||||
|  |     /* blk_eject is only called by qdevified devices */ | ||||||
|  |     assert(!blk->legacy_dev); | ||||||
|  |  | ||||||
|     if (bs) { |     if (bs) { | ||||||
|         bdrv_eject(bs, eject_flag); |         bdrv_eject(bs, eject_flag); | ||||||
|     } |     } | ||||||
| @@ -1758,7 +1735,7 @@ void blk_eject(BlockBackend *blk, bool eject_flag) | |||||||
|      * the frontend experienced a tray event. */ |      * the frontend experienced a tray event. */ | ||||||
|     id = blk_get_attached_dev_id(blk); |     id = blk_get_attached_dev_id(blk); | ||||||
|     qapi_event_send_device_tray_moved(blk_name(blk), id, |     qapi_event_send_device_tray_moved(blk_name(blk), id, | ||||||
|                                       eject_flag); |                                       eject_flag, &error_abort); | ||||||
|     g_free(id); |     g_free(id); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1845,7 +1822,13 @@ void blk_op_unblock_all(BlockBackend *blk, Error *reason) | |||||||
|  |  | ||||||
| AioContext *blk_get_aio_context(BlockBackend *blk) | 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) | static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb) | ||||||
| @@ -1874,15 +1857,8 @@ void blk_add_aio_context_notifier(BlockBackend *blk, | |||||||
|         void (*attached_aio_context)(AioContext *new_context, void *opaque), |         void (*attached_aio_context)(AioContext *new_context, void *opaque), | ||||||
|         void (*detach_aio_context)(void *opaque), void *opaque) |         void (*detach_aio_context)(void *opaque), void *opaque) | ||||||
| { | { | ||||||
|     BlockBackendAioNotifier *notifier; |  | ||||||
|     BlockDriverState *bs = blk_bs(blk); |     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) { |     if (bs) { | ||||||
|         bdrv_add_aio_context_notifier(bs, attached_aio_context, |         bdrv_add_aio_context_notifier(bs, attached_aio_context, | ||||||
|                                       detach_aio_context, opaque); |                                       detach_aio_context, opaque); | ||||||
| @@ -1895,25 +1871,12 @@ void blk_remove_aio_context_notifier(BlockBackend *blk, | |||||||
|                                      void (*detach_aio_context)(void *), |                                      void (*detach_aio_context)(void *), | ||||||
|                                      void *opaque) |                                      void *opaque) | ||||||
| { | { | ||||||
|     BlockBackendAioNotifier *notifier; |  | ||||||
|     BlockDriverState *bs = blk_bs(blk); |     BlockDriverState *bs = blk_bs(blk); | ||||||
|  |  | ||||||
|     if (bs) { |     if (bs) { | ||||||
|         bdrv_remove_aio_context_notifier(bs, attached_aio_context, |         bdrv_remove_aio_context_notifier(bs, attached_aio_context, | ||||||
|                                          detach_aio_context, opaque); |                                          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) | void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify) | ||||||
| @@ -1986,7 +1949,6 @@ static void blk_pdiscard_entry(void *opaque) | |||||||
|     QEMUIOVector *qiov = rwco->iobuf; |     QEMUIOVector *qiov = rwco->iobuf; | ||||||
|  |  | ||||||
|     rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size); |     rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size); | ||||||
|     aio_wait_kick(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes) | int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes) | ||||||
| @@ -2168,13 +2130,6 @@ static void blk_root_drained_begin(BdrvChild *child) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool blk_root_drained_poll(BdrvChild *child) |  | ||||||
| { |  | ||||||
|     BlockBackend *blk = child->opaque; |  | ||||||
|     assert(blk->quiesce_counter); |  | ||||||
|     return !!blk->in_flight; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void blk_root_drained_end(BdrvChild *child) | static void blk_root_drained_end(BdrvChild *child) | ||||||
| { | { | ||||||
|     BlockBackend *blk = child->opaque; |     BlockBackend *blk = child->opaque; | ||||||
| @@ -2199,27 +2154,3 @@ void blk_unregister_buf(BlockBackend *blk, void *host) | |||||||
| { | { | ||||||
|     bdrv_unregister_buf(blk_bs(blk), host); |     bdrv_unregister_buf(blk_bs(blk), host); | ||||||
| } | } | ||||||
|  |  | ||||||
| int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, |  | ||||||
|                                    BlockBackend *blk_out, int64_t off_out, |  | ||||||
|                                    int bytes, BdrvRequestFlags read_flags, |  | ||||||
|                                    BdrvRequestFlags write_flags) |  | ||||||
| { |  | ||||||
|     int r; |  | ||||||
|     r = blk_check_byte_request(blk_in, off_in, bytes); |  | ||||||
|     if (r) { |  | ||||||
|         return r; |  | ||||||
|     } |  | ||||||
|     r = blk_check_byte_request(blk_out, off_out, bytes); |  | ||||||
|     if (r) { |  | ||||||
|         return r; |  | ||||||
|     } |  | ||||||
|     return bdrv_co_copy_range(blk_in->root, off_in, |  | ||||||
|                               blk_out->root, off_out, |  | ||||||
|                               bytes, read_flags, write_flags); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const BdrvChild *blk_root(BlockBackend *blk) |  | ||||||
| { |  | ||||||
|     return blk->root; |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -105,18 +105,23 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     struct bochs_header bochs; |     struct bochs_header bochs; | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     /* No write support yet */ |  | ||||||
|     ret = bdrv_apply_auto_read_only(bs, NULL, errp); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, |     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||||
|                                false, errp); |                                false, errp); | ||||||
|     if (!bs->file) { |     if (!bs->file) { | ||||||
|         return -EINVAL; |         return -EINVAL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (!bdrv_is_read_only(bs)) { | ||||||
|  |         error_report("Opening bochs images without an explicit read-only=on " | ||||||
|  |                      "option is deprecated. Future versions will refuse to " | ||||||
|  |                      "open the image instead of automatically marking the " | ||||||
|  |                      "image read-only."); | ||||||
|  |         ret = bdrv_set_read_only(bs, true, errp); /* no write support yet */ | ||||||
|  |         if (ret < 0) { | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); |     ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         return ret; |         return ret; | ||||||
|   | |||||||
| @@ -67,17 +67,23 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     uint32_t offsets_size, max_compressed_block_size = 1, i; |     uint32_t offsets_size, max_compressed_block_size = 1, i; | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     ret = bdrv_apply_auto_read_only(bs, NULL, errp); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, |     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||||
|                                false, errp); |                                false, errp); | ||||||
|     if (!bs->file) { |     if (!bs->file) { | ||||||
|         return -EINVAL; |         return -EINVAL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (!bdrv_is_read_only(bs)) { | ||||||
|  |         error_report("Opening cloop images without an explicit read-only=on " | ||||||
|  |                      "option is deprecated. Future versions will refuse to " | ||||||
|  |                      "open the image instead of automatically marking the " | ||||||
|  |                      "image read-only."); | ||||||
|  |         ret = bdrv_set_read_only(bs, true, errp); | ||||||
|  |         if (ret < 0) { | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /* read header */ |     /* read header */ | ||||||
|     ret = bdrv_pread(bs->file, 128, &s->block_size, 4); |     ret = bdrv_pread(bs->file, 128, &s->block_size, 4); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|   | |||||||
							
								
								
									
										191
									
								
								block/commit.c
									
									
									
									
									
								
							
							
						
						
									
										191
									
								
								block/commit.c
									
									
									
									
									
								
							| @@ -31,14 +31,16 @@ enum { | |||||||
|     COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */ |     COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */ | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | #define SLICE_TIME 100000000ULL /* ns */ | ||||||
|  |  | ||||||
| typedef struct CommitBlockJob { | typedef struct CommitBlockJob { | ||||||
|     BlockJob common; |     BlockJob common; | ||||||
|  |     RateLimit limit; | ||||||
|     BlockDriverState *commit_top_bs; |     BlockDriverState *commit_top_bs; | ||||||
|     BlockBackend *top; |     BlockBackend *top; | ||||||
|     BlockBackend *base; |     BlockBackend *base; | ||||||
|     BlockDriverState *base_bs; |  | ||||||
|     BlockdevOnError on_error; |     BlockdevOnError on_error; | ||||||
|     bool base_read_only; |     int base_flags; | ||||||
|     char *backing_file_str; |     char *backing_file_str; | ||||||
| } CommitBlockJob; | } CommitBlockJob; | ||||||
|  |  | ||||||
| @@ -69,93 +71,96 @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base, | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int commit_prepare(Job *job) | typedef struct { | ||||||
|  |     int ret; | ||||||
|  | } CommitCompleteData; | ||||||
|  |  | ||||||
|  | static void commit_complete(BlockJob *job, void *opaque) | ||||||
| { | { | ||||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); |     CommitBlockJob *s = container_of(job, CommitBlockJob, common); | ||||||
|  |     CommitCompleteData *data = opaque; | ||||||
|  |     BlockDriverState *top = blk_bs(s->top); | ||||||
|  |     BlockDriverState *base = blk_bs(s->base); | ||||||
|  |     BlockDriverState *commit_top_bs = s->commit_top_bs; | ||||||
|  |     int ret = data->ret; | ||||||
|  |     bool remove_commit_top_bs = false; | ||||||
|  |  | ||||||
|  |     /* Make sure commit_top_bs and top stay around until bdrv_replace_node() */ | ||||||
|  |     bdrv_ref(top); | ||||||
|  |     bdrv_ref(commit_top_bs); | ||||||
|  |  | ||||||
|     /* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before |     /* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before | ||||||
|      * the normal backing chain can be restored. */ |      * the normal backing chain can be restored. */ | ||||||
|     blk_unref(s->base); |     blk_unref(s->base); | ||||||
|     s->base = NULL; |  | ||||||
|  |  | ||||||
|     /* FIXME: bdrv_drop_intermediate treats total failures and partial failures |     if (!block_job_is_cancelled(&s->common) && ret == 0) { | ||||||
|      * identically. Further work is needed to disambiguate these cases. */ |         /* success */ | ||||||
|     return bdrv_drop_intermediate(s->commit_top_bs, s->base_bs, |         ret = bdrv_drop_intermediate(s->commit_top_bs, base, | ||||||
|                                      s->backing_file_str); |                                      s->backing_file_str); | ||||||
| } |     } else { | ||||||
|  |         /* XXX Can (or should) we somehow keep 'consistent read' blocked even | ||||||
| static void commit_abort(Job *job) |  | ||||||
| { |  | ||||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); |  | ||||||
|     BlockDriverState *top_bs = blk_bs(s->top); |  | ||||||
|  |  | ||||||
|     /* Make sure commit_top_bs and top stay around until bdrv_replace_node() */ |  | ||||||
|     bdrv_ref(top_bs); |  | ||||||
|     bdrv_ref(s->commit_top_bs); |  | ||||||
|  |  | ||||||
|     if (s->base) { |  | ||||||
|         blk_unref(s->base); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* free the blockers on the intermediate nodes so that bdrv_replace_nodes |  | ||||||
|      * can succeed */ |  | ||||||
|     block_job_remove_all_bdrv(&s->common); |  | ||||||
|  |  | ||||||
|     /* If bdrv_drop_intermediate() failed (or was not invoked), remove the |  | ||||||
|      * commit filter driver from the backing chain now. Do this as the final |  | ||||||
|      * step so that the 'consistent read' permission can be granted. |  | ||||||
|      * |  | ||||||
|      * XXX Can (or should) we somehow keep 'consistent read' blocked even |  | ||||||
|          * after the failed/cancelled commit job is gone? If we already wrote |          * after the failed/cancelled commit job is gone? If we already wrote | ||||||
|          * something to base, the intermediate images aren't valid any more. */ |          * something to base, the intermediate images aren't valid any more. */ | ||||||
|     bdrv_child_try_set_perm(s->commit_top_bs->backing, 0, BLK_PERM_ALL, |         remove_commit_top_bs = true; | ||||||
|                             &error_abort); |     } | ||||||
|     bdrv_replace_node(s->commit_top_bs, backing_bs(s->commit_top_bs), |  | ||||||
|                       &error_abort); |  | ||||||
|  |  | ||||||
|     bdrv_unref(s->commit_top_bs); |  | ||||||
|     bdrv_unref(top_bs); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void commit_clean(Job *job) |  | ||||||
| { |  | ||||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); |  | ||||||
|  |  | ||||||
|     /* restore base open flags here if appropriate (e.g., change the base back |     /* restore base open flags here if appropriate (e.g., change the base back | ||||||
|      * to r/o). These reopens do not need to be atomic, since we won't abort |      * to r/o). These reopens do not need to be atomic, since we won't abort | ||||||
|      * even on failure here */ |      * even on failure here */ | ||||||
|     if (s->base_read_only) { |     if (s->base_flags != bdrv_get_flags(base)) { | ||||||
|         bdrv_reopen_set_read_only(s->base_bs, true, NULL); |         bdrv_reopen(base, s->base_flags, NULL); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     g_free(s->backing_file_str); |     g_free(s->backing_file_str); | ||||||
|     blk_unref(s->top); |     blk_unref(s->top); | ||||||
|  |  | ||||||
|  |     /* If there is more than one reference to the job (e.g. if called from | ||||||
|  |      * block_job_finish_sync()), block_job_completed() won't free it and | ||||||
|  |      * therefore the blockers on the intermediate nodes remain. This would | ||||||
|  |      * cause bdrv_set_backing_hd() to fail. */ | ||||||
|  |     block_job_remove_all_bdrv(job); | ||||||
|  |  | ||||||
|  |     block_job_completed(&s->common, ret); | ||||||
|  |     g_free(data); | ||||||
|  |  | ||||||
|  |     /* If bdrv_drop_intermediate() didn't already do that, remove the commit | ||||||
|  |      * filter driver from the backing chain. Do this as the final step so that | ||||||
|  |      * the 'consistent read' permission can be granted.  */ | ||||||
|  |     if (remove_commit_top_bs) { | ||||||
|  |         bdrv_child_try_set_perm(commit_top_bs->backing, 0, BLK_PERM_ALL, | ||||||
|  |                                 &error_abort); | ||||||
|  |         bdrv_replace_node(commit_top_bs, backing_bs(commit_top_bs), | ||||||
|  |                           &error_abort); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bdrv_unref(commit_top_bs); | ||||||
|  |     bdrv_unref(top); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coroutine_fn commit_run(Job *job, Error **errp) | static void coroutine_fn commit_run(void *opaque) | ||||||
| { | { | ||||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); |     CommitBlockJob *s = opaque; | ||||||
|  |     CommitCompleteData *data; | ||||||
|     int64_t offset; |     int64_t offset; | ||||||
|     uint64_t delay_ns = 0; |     uint64_t delay_ns = 0; | ||||||
|     int ret = 0; |     int ret = 0; | ||||||
|     int64_t n = 0; /* bytes */ |     int64_t n = 0; /* bytes */ | ||||||
|     void *buf = NULL; |     void *buf = NULL; | ||||||
|     int bytes_written = 0; |     int bytes_written = 0; | ||||||
|     int64_t len, base_len; |     int64_t base_len; | ||||||
|  |  | ||||||
|     ret = len = blk_getlength(s->top); |     ret = s->common.len = blk_getlength(s->top); | ||||||
|     if (len < 0) { |  | ||||||
|  |     if (s->common.len < 0) { | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|     job_progress_set_remaining(&s->common.job, len); |  | ||||||
|  |  | ||||||
|     ret = base_len = blk_getlength(s->base); |     ret = base_len = blk_getlength(s->base); | ||||||
|     if (base_len < 0) { |     if (base_len < 0) { | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (base_len < len) { |     if (base_len < s->common.len) { | ||||||
|         ret = blk_truncate(s->base, len, PREALLOC_MODE_OFF, NULL); |         ret = blk_truncate(s->base, s->common.len, PREALLOC_MODE_OFF, NULL); | ||||||
|         if (ret) { |         if (ret) { | ||||||
|             goto out; |             goto out; | ||||||
|         } |         } | ||||||
| @@ -163,14 +168,14 @@ static int coroutine_fn commit_run(Job *job, Error **errp) | |||||||
|  |  | ||||||
|     buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE); |     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; |         bool copy; | ||||||
|  |  | ||||||
|         /* Note that even when no rate limit is applied we need to yield |         /* 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. |          * with no pending I/O here so that bdrv_drain_all() returns. | ||||||
|          */ |          */ | ||||||
|         job_sleep_ns(&s->common.job, delay_ns); |         block_job_sleep_ns(&s->common, delay_ns); | ||||||
|         if (job_is_cancelled(&s->common.job)) { |         if (block_job_is_cancelled(&s->common)) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         /* Copy if allocated above the base */ |         /* Copy if allocated above the base */ | ||||||
| @@ -193,12 +198,10 @@ static int coroutine_fn commit_run(Job *job, Error **errp) | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         /* Publish progress */ |         /* Publish progress */ | ||||||
|         job_progress_update(&s->common.job, n); |         s->common.offset += n; | ||||||
|  |  | ||||||
|         if (copy) { |         if (copy && s->common.speed) { | ||||||
|             delay_ns = block_job_ratelimit_get_delay(&s->common, n); |             delay_ns = ratelimit_calculate_delay(&s->limit, n); | ||||||
|         } else { |  | ||||||
|             delay_ns = 0; |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -207,21 +210,27 @@ static int coroutine_fn commit_run(Job *job, Error **errp) | |||||||
| out: | out: | ||||||
|     qemu_vfree(buf); |     qemu_vfree(buf); | ||||||
|  |  | ||||||
|     return ret; |     data = g_malloc(sizeof(*data)); | ||||||
|  |     data->ret = ret; | ||||||
|  |     block_job_defer_to_main_loop(&s->common, commit_complete, data); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp) | ||||||
|  | { | ||||||
|  |     CommitBlockJob *s = container_of(job, CommitBlockJob, common); | ||||||
|  |  | ||||||
|  |     if (speed < 0) { | ||||||
|  |         error_setg(errp, QERR_INVALID_PARAMETER, "speed"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     ratelimit_set_speed(&s->limit, speed, SLICE_TIME); | ||||||
| } | } | ||||||
|  |  | ||||||
| static const BlockJobDriver commit_job_driver = { | static const BlockJobDriver commit_job_driver = { | ||||||
|     .job_driver = { |  | ||||||
|     .instance_size = sizeof(CommitBlockJob), |     .instance_size = sizeof(CommitBlockJob), | ||||||
|         .job_type      = JOB_TYPE_COMMIT, |     .job_type      = BLOCK_JOB_TYPE_COMMIT, | ||||||
|         .free          = block_job_free, |     .set_speed     = commit_set_speed, | ||||||
|         .user_resume   = block_job_user_resume, |     .start         = commit_run, | ||||||
|         .drain         = block_job_drain, |  | ||||||
|         .run           = commit_run, |  | ||||||
|         .prepare       = commit_prepare, |  | ||||||
|         .abort         = commit_abort, |  | ||||||
|         .clean         = commit_clean |  | ||||||
|     }, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs, | static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs, | ||||||
| @@ -237,6 +246,10 @@ static void bdrv_commit_top_refresh_filename(BlockDriverState *bs, QDict *opts) | |||||||
|             bs->backing->bs->filename); |             bs->backing->bs->filename); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void bdrv_commit_top_close(BlockDriverState *bs) | ||||||
|  | { | ||||||
|  | } | ||||||
|  |  | ||||||
| static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c, | static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c, | ||||||
|                                        const BdrvChildRole *role, |                                        const BdrvChildRole *role, | ||||||
|                                        BlockReopenQueue *reopen_queue, |                                        BlockReopenQueue *reopen_queue, | ||||||
| @@ -254,16 +267,17 @@ static BlockDriver bdrv_commit_top = { | |||||||
|     .bdrv_co_preadv             = bdrv_commit_top_preadv, |     .bdrv_co_preadv             = bdrv_commit_top_preadv, | ||||||
|     .bdrv_co_block_status       = bdrv_co_block_status_from_backing, |     .bdrv_co_block_status       = bdrv_co_block_status_from_backing, | ||||||
|     .bdrv_refresh_filename      = bdrv_commit_top_refresh_filename, |     .bdrv_refresh_filename      = bdrv_commit_top_refresh_filename, | ||||||
|  |     .bdrv_close                 = bdrv_commit_top_close, | ||||||
|     .bdrv_child_perm            = bdrv_commit_top_child_perm, |     .bdrv_child_perm            = bdrv_commit_top_child_perm, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| void commit_start(const char *job_id, BlockDriverState *bs, | void commit_start(const char *job_id, BlockDriverState *bs, | ||||||
|                   BlockDriverState *base, BlockDriverState *top, |                   BlockDriverState *base, BlockDriverState *top, int64_t speed, | ||||||
|                   int creation_flags, int64_t speed, |  | ||||||
|                   BlockdevOnError on_error, const char *backing_file_str, |                   BlockdevOnError on_error, const char *backing_file_str, | ||||||
|                   const char *filter_node_name, Error **errp) |                   const char *filter_node_name, Error **errp) | ||||||
| { | { | ||||||
|     CommitBlockJob *s; |     CommitBlockJob *s; | ||||||
|  |     int orig_base_flags; | ||||||
|     BlockDriverState *iter; |     BlockDriverState *iter; | ||||||
|     BlockDriverState *commit_top_bs = NULL; |     BlockDriverState *commit_top_bs = NULL; | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
| @@ -275,16 +289,18 @@ void commit_start(const char *job_id, BlockDriverState *bs, | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL, |     s = block_job_create(job_id, &commit_job_driver, bs, 0, BLK_PERM_ALL, | ||||||
|                          speed, creation_flags, NULL, NULL, errp); |                          speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp); | ||||||
|     if (!s) { |     if (!s) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* convert base to r/w, if necessary */ |     /* convert base to r/w, if necessary */ | ||||||
|     s->base_read_only = bdrv_is_read_only(base); |     orig_base_flags = bdrv_get_flags(base); | ||||||
|     if (s->base_read_only) { |     if (!(orig_base_flags & BDRV_O_RDWR)) { | ||||||
|         if (bdrv_reopen_set_read_only(base, false, errp) != 0) { |         bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err); | ||||||
|  |         if (local_err != NULL) { | ||||||
|  |             error_propagate(errp, local_err); | ||||||
|             goto fail; |             goto fail; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -351,7 +367,6 @@ void commit_start(const char *job_id, BlockDriverState *bs, | |||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|     s->base_bs = base; |  | ||||||
|  |  | ||||||
|     /* Required permissions are already taken with block_job_add_bdrv() */ |     /* Required permissions are already taken with block_job_add_bdrv() */ | ||||||
|     s->top = blk_new(0, BLK_PERM_ALL); |     s->top = blk_new(0, BLK_PERM_ALL); | ||||||
| @@ -360,11 +375,12 @@ void commit_start(const char *job_id, BlockDriverState *bs, | |||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     s->base_flags = orig_base_flags; | ||||||
|     s->backing_file_str = g_strdup(backing_file_str); |     s->backing_file_str = g_strdup(backing_file_str); | ||||||
|     s->on_error = on_error; |     s->on_error = on_error; | ||||||
|  |  | ||||||
|     trace_commit_start(bs, base, top, s); |     trace_commit_start(bs, base, top, s); | ||||||
|     job_start(&s->common.job); |     block_job_start(&s->common); | ||||||
|     return; |     return; | ||||||
|  |  | ||||||
| fail: | fail: | ||||||
| @@ -377,7 +393,7 @@ fail: | |||||||
|     if (commit_top_bs) { |     if (commit_top_bs) { | ||||||
|         bdrv_replace_node(commit_top_bs, top, &error_abort); |         bdrv_replace_node(commit_top_bs, top, &error_abort); | ||||||
|     } |     } | ||||||
|     job_early_fail(&s->common.job); |     block_job_early_fail(&s->common); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -391,7 +407,7 @@ int bdrv_commit(BlockDriverState *bs) | |||||||
|     BlockDriverState *commit_top_bs = NULL; |     BlockDriverState *commit_top_bs = NULL; | ||||||
|     BlockDriver *drv = bs->drv; |     BlockDriver *drv = bs->drv; | ||||||
|     int64_t offset, length, backing_length; |     int64_t offset, length, backing_length; | ||||||
|     int ro; |     int ro, open_flags; | ||||||
|     int64_t n; |     int64_t n; | ||||||
|     int ret = 0; |     int ret = 0; | ||||||
|     uint8_t *buf = NULL; |     uint8_t *buf = NULL; | ||||||
| @@ -410,9 +426,10 @@ int bdrv_commit(BlockDriverState *bs) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     ro = bs->backing->bs->read_only; |     ro = bs->backing->bs->read_only; | ||||||
|  |     open_flags =  bs->backing->bs->open_flags; | ||||||
|  |  | ||||||
|     if (ro) { |     if (ro) { | ||||||
|         if (bdrv_reopen_set_read_only(bs->backing->bs, false, NULL)) { |         if (bdrv_reopen(bs->backing->bs, open_flags | BDRV_O_RDWR, NULL)) { | ||||||
|             return -EACCES; |             return -EACCES; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -522,7 +539,7 @@ ro_cleanup: | |||||||
|  |  | ||||||
|     if (ro) { |     if (ro) { | ||||||
|         /* ignoring error return here */ |         /* ignoring error return here */ | ||||||
|         bdrv_reopen_set_read_only(bs->backing->bs, true, NULL); |         bdrv_reopen(bs->backing->bs, open_flags & ~BDRV_O_RDWR, NULL); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return ret; |     return ret; | ||||||
|   | |||||||
| @@ -1,167 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copy-on-read filter block driver |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2018 Red Hat, Inc. |  | ||||||
|  * |  | ||||||
|  * Author: |  | ||||||
|  *   Max Reitz <mreitz@redhat.com> |  | ||||||
|  * |  | ||||||
|  * This program is free software; you can redistribute it and/or |  | ||||||
|  * modify it under the terms of the GNU General Public License as |  | ||||||
|  * published by the Free Software Foundation; either version 2 or |  | ||||||
|  * (at your option) version 3 of the License. |  | ||||||
|  * |  | ||||||
|  * This program is distributed in the hope that it will be useful, |  | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
|  * GNU General Public License for more details. |  | ||||||
|  * |  | ||||||
|  * You should have received a copy of the GNU General Public License |  | ||||||
|  * along with this program; if not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "block/block_int.h" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static int cor_open(BlockDriverState *bs, QDict *options, int flags, |  | ||||||
|                     Error **errp) |  | ||||||
| { |  | ||||||
|     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, |  | ||||||
|                                errp); |  | ||||||
|     if (!bs->file) { |  | ||||||
|         return -EINVAL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | |  | ||||||
|                                 (BDRV_REQ_FUA & |  | ||||||
|                                     bs->file->bs->supported_write_flags); |  | ||||||
|  |  | ||||||
|     bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | |  | ||||||
|                                ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & |  | ||||||
|                                     bs->file->bs->supported_zero_flags); |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #define PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \ |  | ||||||
|                           | BLK_PERM_WRITE \ |  | ||||||
|                           | BLK_PERM_RESIZE) |  | ||||||
| #define PERM_UNCHANGED (BLK_PERM_ALL & ~PERM_PASSTHROUGH) |  | ||||||
|  |  | ||||||
| static void cor_child_perm(BlockDriverState *bs, BdrvChild *c, |  | ||||||
|                            const BdrvChildRole *role, |  | ||||||
|                            BlockReopenQueue *reopen_queue, |  | ||||||
|                            uint64_t perm, uint64_t shared, |  | ||||||
|                            uint64_t *nperm, uint64_t *nshared) |  | ||||||
| { |  | ||||||
|     if (c == NULL) { |  | ||||||
|         *nperm = (perm & PERM_PASSTHROUGH) | BLK_PERM_WRITE_UNCHANGED; |  | ||||||
|         *nshared = (shared & PERM_PASSTHROUGH) | PERM_UNCHANGED; |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     *nperm = (perm & PERM_PASSTHROUGH) | |  | ||||||
|              (c->perm & PERM_UNCHANGED); |  | ||||||
|     *nshared = (shared & PERM_PASSTHROUGH) | |  | ||||||
|                (c->shared_perm & PERM_UNCHANGED); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static int64_t cor_getlength(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     return bdrv_getlength(bs->file->bs); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static int coroutine_fn cor_co_truncate(BlockDriverState *bs, int64_t offset, |  | ||||||
|                                         PreallocMode prealloc, Error **errp) |  | ||||||
| { |  | ||||||
|     return bdrv_co_truncate(bs->file, offset, prealloc, errp); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static int coroutine_fn cor_co_preadv(BlockDriverState *bs, |  | ||||||
|                                       uint64_t offset, uint64_t bytes, |  | ||||||
|                                       QEMUIOVector *qiov, int flags) |  | ||||||
| { |  | ||||||
|     return bdrv_co_preadv(bs->file, offset, bytes, qiov, |  | ||||||
|                           flags | BDRV_REQ_COPY_ON_READ); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static int coroutine_fn cor_co_pwritev(BlockDriverState *bs, |  | ||||||
|                                        uint64_t offset, uint64_t bytes, |  | ||||||
|                                        QEMUIOVector *qiov, int flags) |  | ||||||
| { |  | ||||||
|  |  | ||||||
|     return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static int coroutine_fn cor_co_pwrite_zeroes(BlockDriverState *bs, |  | ||||||
|                                              int64_t offset, int bytes, |  | ||||||
|                                              BdrvRequestFlags flags) |  | ||||||
| { |  | ||||||
|     return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static int coroutine_fn cor_co_pdiscard(BlockDriverState *bs, |  | ||||||
|                                         int64_t offset, int bytes) |  | ||||||
| { |  | ||||||
|     return bdrv_co_pdiscard(bs->file, offset, bytes); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void cor_eject(BlockDriverState *bs, bool eject_flag) |  | ||||||
| { |  | ||||||
|     bdrv_eject(bs->file->bs, eject_flag); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void cor_lock_medium(BlockDriverState *bs, bool locked) |  | ||||||
| { |  | ||||||
|     bdrv_lock_medium(bs->file->bs, locked); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static bool cor_recurse_is_first_non_filter(BlockDriverState *bs, |  | ||||||
|                                             BlockDriverState *candidate) |  | ||||||
| { |  | ||||||
|     return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| BlockDriver bdrv_copy_on_read = { |  | ||||||
|     .format_name                        = "copy-on-read", |  | ||||||
|  |  | ||||||
|     .bdrv_open                          = cor_open, |  | ||||||
|     .bdrv_child_perm                    = cor_child_perm, |  | ||||||
|  |  | ||||||
|     .bdrv_getlength                     = cor_getlength, |  | ||||||
|     .bdrv_co_truncate                   = cor_co_truncate, |  | ||||||
|  |  | ||||||
|     .bdrv_co_preadv                     = cor_co_preadv, |  | ||||||
|     .bdrv_co_pwritev                    = cor_co_pwritev, |  | ||||||
|     .bdrv_co_pwrite_zeroes              = cor_co_pwrite_zeroes, |  | ||||||
|     .bdrv_co_pdiscard                   = cor_co_pdiscard, |  | ||||||
|  |  | ||||||
|     .bdrv_eject                         = cor_eject, |  | ||||||
|     .bdrv_lock_medium                   = cor_lock_medium, |  | ||||||
|  |  | ||||||
|     .bdrv_co_block_status               = bdrv_co_block_status_from_file, |  | ||||||
|  |  | ||||||
|     .bdrv_recurse_is_first_non_filter   = cor_recurse_is_first_non_filter, |  | ||||||
|  |  | ||||||
|     .has_variable_length                = true, |  | ||||||
|     .is_filter                          = true, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void bdrv_copy_on_read_init(void) |  | ||||||
| { |  | ||||||
|     bdrv_register(&bdrv_copy_on_read); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| block_init(bdrv_copy_on_read_init); |  | ||||||
| @@ -24,44 +24,28 @@ | |||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "block/block_int.h" | #include "block/block_int.h" | ||||||
| #include "qemu/job.h" |  | ||||||
| #include "qapi/qapi-commands-block-core.h" | #include "qapi/qapi-commands-block-core.h" | ||||||
| #include "qapi/qapi-visit-block-core.h" |  | ||||||
| #include "qapi/clone-visitor.h" |  | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
|  |  | ||||||
| typedef struct BlockdevCreateJob { | typedef struct BlockdevCreateCo { | ||||||
|     Job common; |  | ||||||
|     BlockDriver *drv; |     BlockDriver *drv; | ||||||
|     BlockdevCreateOptions *opts; |     BlockdevCreateOptions *opts; | ||||||
| } BlockdevCreateJob; |  | ||||||
|  |  | ||||||
| static int coroutine_fn blockdev_create_run(Job *job, Error **errp) |  | ||||||
| { |  | ||||||
|     BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); |  | ||||||
|     int ret; |     int ret; | ||||||
|  |     Error **errp; | ||||||
|  | } BlockdevCreateCo; | ||||||
|  |  | ||||||
|     job_progress_set_remaining(&s->common, 1); | static void coroutine_fn bdrv_co_create_co_entry(void *opaque) | ||||||
|     ret = s->drv->bdrv_co_create(s->opts, errp); | { | ||||||
|     job_progress_update(&s->common, 1); |     BlockdevCreateCo *cco = opaque; | ||||||
|  |     cco->ret = cco->drv->bdrv_co_create(cco->opts, cco->errp); | ||||||
|     qapi_free_BlockdevCreateOptions(s->opts); |  | ||||||
|  |  | ||||||
|     return ret; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static const JobDriver blockdev_create_job_driver = { | void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp) | ||||||
|     .instance_size = sizeof(BlockdevCreateJob), |  | ||||||
|     .job_type      = JOB_TYPE_CREATE, |  | ||||||
|     .run           = blockdev_create_run, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options, |  | ||||||
|                          Error **errp) |  | ||||||
| { | { | ||||||
|     BlockdevCreateJob *s; |  | ||||||
|     const char *fmt = BlockdevDriver_str(options->driver); |     const char *fmt = BlockdevDriver_str(options->driver); | ||||||
|     BlockDriver *drv = bdrv_find_format(fmt); |     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 |     /* If the driver is in the schema, we know that it exists. But it may not | ||||||
|      * be whitelisted. */ |      * be whitelisted. */ | ||||||
| @@ -71,24 +55,22 @@ void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options, | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Error out if the driver doesn't support .bdrv_co_create */ |     /* Call callback if it exists */ | ||||||
|     if (!drv->bdrv_co_create) { |     if (!drv->bdrv_co_create) { | ||||||
|         error_setg(errp, "Driver does not support blockdev-create"); |         error_setg(errp, "Driver does not support blockdev-create"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Create the block job */ |     cco = (BlockdevCreateCo) { | ||||||
|     /* TODO Running in the main context. Block drivers need to error out or add |         .drv = drv, | ||||||
|      * locking when they use a BDS in a different AioContext. */ |         .opts = options, | ||||||
|     s = job_create(job_id, &blockdev_create_job_driver, NULL, |         .ret = -EINPROGRESS, | ||||||
|                    qemu_get_aio_context(), JOB_DEFAULT | JOB_MANUAL_DISMISS, |         .errp = errp, | ||||||
|                    NULL, NULL, errp); |     }; | ||||||
|     if (!s) { |  | ||||||
|         return; |     co = qemu_coroutine_create(bdrv_co_create_co_entry, &cco); | ||||||
|  |     qemu_coroutine_enter(co); | ||||||
|  |     while (cco.ret == -EINPROGRESS) { | ||||||
|  |         aio_poll(qemu_get_aio_context(), true); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     s->drv = drv, |  | ||||||
|     s->opts = QAPI_CLONE(BlockdevCreateOptions, options), |  | ||||||
|  |  | ||||||
|     job_start(&s->common); |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										274
									
								
								block/crypto.c
									
									
									
									
									
								
							
							
						
						
									
										274
									
								
								block/crypto.c
									
									
									
									
									
								
							| @@ -21,15 +21,15 @@ | |||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
|  |  | ||||||
| #include "block/block_int.h" | #include "block/block_int.h" | ||||||
| #include "block/qdict.h" |  | ||||||
| #include "sysemu/block-backend.h" | #include "sysemu/block-backend.h" | ||||||
| #include "crypto/block.h" | #include "crypto/block.h" | ||||||
| #include "qapi/opts-visitor.h" | #include "qapi/opts-visitor.h" | ||||||
| #include "qapi/qapi-visit-crypto.h" | #include "qapi/qapi-visit-crypto.h" | ||||||
|  | #include "qapi/qmp/qdict.h" | ||||||
| #include "qapi/qobject-input-visitor.h" | #include "qapi/qobject-input-visitor.h" | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qemu/option.h" | #include "qemu/option.h" | ||||||
| #include "crypto.h" | #include "block/crypto.h" | ||||||
|  |  | ||||||
| typedef struct BlockCrypto BlockCrypto; | typedef struct BlockCrypto BlockCrypto; | ||||||
|  |  | ||||||
| @@ -71,6 +71,8 @@ static ssize_t block_crypto_read_func(QCryptoBlock *block, | |||||||
|  |  | ||||||
|  |  | ||||||
| struct BlockCryptoCreateData { | struct BlockCryptoCreateData { | ||||||
|  |     const char *filename; | ||||||
|  |     QemuOpts *opts; | ||||||
|     BlockBackend *blk; |     BlockBackend *blk; | ||||||
|     uint64_t size; |     uint64_t size; | ||||||
| }; | }; | ||||||
| @@ -101,18 +103,27 @@ static ssize_t block_crypto_init_func(QCryptoBlock *block, | |||||||
|                                       Error **errp) |                                       Error **errp) | ||||||
| { | { | ||||||
|     struct BlockCryptoCreateData *data = opaque; |     struct BlockCryptoCreateData *data = opaque; | ||||||
|  |     int ret; | ||||||
|     if (data->size > INT64_MAX || headerlen > INT64_MAX - data->size) { |  | ||||||
|         error_setg(errp, "The requested file size is too large"); |  | ||||||
|         return -EFBIG; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* User provided size should reflect amount of space made |     /* User provided size should reflect amount of space made | ||||||
|      * available to the guest, so we must take account of that |      * available to the guest, so we must take account of that | ||||||
|      * which will be used by the crypto header |      * which will be used by the crypto header | ||||||
|      */ |      */ | ||||||
|     return blk_truncate(data->blk, data->size + headerlen, PREALLOC_MODE_OFF, |     data->size += headerlen; | ||||||
|                         errp); |  | ||||||
|  |     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 * | QCryptoBlockOpenOptions * | ||||||
| block_crypto_open_opts_init(QDict *opts, Error **errp) | block_crypto_open_opts_init(QCryptoBlockFormat format, | ||||||
|  |                             QDict *opts, | ||||||
|  |                             Error **errp) | ||||||
| { | { | ||||||
|     Visitor *v; |     Visitor *v; | ||||||
|     QCryptoBlockOpenOptions *ret; |     QCryptoBlockOpenOptions *ret = NULL; | ||||||
|  |     Error *local_err = NULL; | ||||||
|  |  | ||||||
|     v = qobject_input_visitor_new_flat_confused(opts, errp); |     ret = g_new0(QCryptoBlockOpenOptions, 1); | ||||||
|     if (!v) { |     ret->format = format; | ||||||
|         return NULL; |  | ||||||
|  |     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); |     visit_free(v); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| QCryptoBlockCreateOptions * | QCryptoBlockCreateOptions * | ||||||
| block_crypto_create_opts_init(QDict *opts, Error **errp) | block_crypto_create_opts_init(QCryptoBlockFormat format, | ||||||
|  |                               QDict *opts, | ||||||
|  |                               Error **errp) | ||||||
| { | { | ||||||
|     Visitor *v; |     Visitor *v; | ||||||
|     QCryptoBlockCreateOptions *ret; |     QCryptoBlockCreateOptions *ret = NULL; | ||||||
|  |     Error *local_err = NULL; | ||||||
|  |  | ||||||
|     v = qobject_input_visitor_new_flat_confused(opts, errp); |     ret = g_new0(QCryptoBlockCreateOptions, 1); | ||||||
|     if (!v) { |     ret->format = format; | ||||||
|         return NULL; |  | ||||||
|  |     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); |     visit_free(v); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| @@ -215,9 +292,8 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     cryptoopts = qemu_opts_to_qdict(opts, NULL); |     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) { |     if (!open_opts) { | ||||||
|         goto cleanup; |         goto cleanup; | ||||||
|     } |     } | ||||||
| @@ -229,7 +305,6 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, | |||||||
|                                        block_crypto_read_func, |                                        block_crypto_read_func, | ||||||
|                                        bs, |                                        bs, | ||||||
|                                        cflags, |                                        cflags, | ||||||
|                                        1, |  | ||||||
|                                        errp); |                                        errp); | ||||||
|  |  | ||||||
|     if (!crypto->block) { |     if (!crypto->block) { | ||||||
| @@ -241,35 +316,36 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, | |||||||
|  |  | ||||||
|     ret = 0; |     ret = 0; | ||||||
|  cleanup: |  cleanup: | ||||||
|     qobject_unref(cryptoopts); |     QDECREF(cryptoopts); | ||||||
|     qapi_free_QCryptoBlockOpenOptions(open_opts); |     qapi_free_QCryptoBlockOpenOptions(open_opts); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| static int block_crypto_co_create_generic(BlockDriverState *bs, | static int block_crypto_create_generic(QCryptoBlockFormat format, | ||||||
|                                           int64_t size, |                                        const char *filename, | ||||||
|                                           QCryptoBlockCreateOptions *opts, |                                        QemuOpts *opts, | ||||||
|                                        Error **errp) |                                        Error **errp) | ||||||
| { | { | ||||||
|     int ret; |     int ret = -EINVAL; | ||||||
|     BlockBackend *blk; |     QCryptoBlockCreateOptions *create_opts = NULL; | ||||||
|     QCryptoBlock *crypto = 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); |     create_opts = block_crypto_create_opts_init(format, cryptoopts, errp); | ||||||
|     if (ret < 0) { |     if (!create_opts) { | ||||||
|         goto cleanup; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     data = (struct BlockCryptoCreateData) { |     crypto = qcrypto_block_create(create_opts, NULL, | ||||||
|         .blk = blk, |  | ||||||
|         .size = size, |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     crypto = qcrypto_block_create(opts, NULL, |  | ||||||
|                                   block_crypto_init_func, |                                   block_crypto_init_func, | ||||||
|                                   block_crypto_write_func, |                                   block_crypto_write_func, | ||||||
|                                   &data, |                                   &data, | ||||||
| @@ -282,27 +358,24 @@ static int block_crypto_co_create_generic(BlockDriverState *bs, | |||||||
|  |  | ||||||
|     ret = 0; |     ret = 0; | ||||||
|  cleanup: |  cleanup: | ||||||
|  |     QDECREF(cryptoopts); | ||||||
|     qcrypto_block_free(crypto); |     qcrypto_block_free(crypto); | ||||||
|     blk_unref(blk); |     blk_unref(data.blk); | ||||||
|  |     qapi_free_QCryptoBlockCreateOptions(create_opts); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coroutine_fn | static int block_crypto_truncate(BlockDriverState *bs, int64_t offset, | ||||||
| block_crypto_co_truncate(BlockDriverState *bs, int64_t offset, |  | ||||||
|                                  PreallocMode prealloc, Error **errp) |                                  PreallocMode prealloc, Error **errp) | ||||||
| { | { | ||||||
|     BlockCrypto *crypto = bs->opaque; |     BlockCrypto *crypto = bs->opaque; | ||||||
|     uint64_t payload_offset = |     uint64_t payload_offset = | ||||||
|         qcrypto_block_get_payload_offset(crypto->block); |         qcrypto_block_get_payload_offset(crypto->block); | ||||||
|  |     assert(payload_offset < (INT64_MAX - offset)); | ||||||
|     if (payload_offset > INT64_MAX - offset) { |  | ||||||
|         error_setg(errp, "The requested file size is too large"); |  | ||||||
|         return -EFBIG; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     offset += payload_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) | static void block_crypto_close(BlockDriverState *bs) | ||||||
| @@ -464,10 +537,7 @@ static int64_t block_crypto_getlength(BlockDriverState *bs) | |||||||
|  |  | ||||||
|     uint64_t offset = qcrypto_block_get_payload_offset(crypto->block); |     uint64_t offset = qcrypto_block_get_payload_offset(crypto->block); | ||||||
|     assert(offset < INT64_MAX); |     assert(offset < INT64_MAX); | ||||||
|  |     assert(offset < len); | ||||||
|     if (offset > len) { |  | ||||||
|         return -EIO; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     len -= offset; |     len -= offset; | ||||||
|  |  | ||||||
| @@ -492,88 +562,12 @@ static int block_crypto_open_luks(BlockDriverState *bs, | |||||||
|                                      bs, options, flags, errp); |                                      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, | static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename, | ||||||
|                                                          QemuOpts *opts, |                                                          QemuOpts *opts, | ||||||
|                                                          Error **errp) |                                                          Error **errp) | ||||||
| { | { | ||||||
|     QCryptoBlockCreateOptions *create_opts = NULL; |     return block_crypto_create_generic(Q_CRYPTO_BLOCK_FORMAT_LUKS, | ||||||
|     BlockDriverState *bs = NULL; |                                        filename, opts, errp); | ||||||
|     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; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int block_crypto_get_info_luks(BlockDriverState *bs, | static int block_crypto_get_info_luks(BlockDriverState *bs, | ||||||
| @@ -594,17 +588,20 @@ static int block_crypto_get_info_luks(BlockDriverState *bs, | |||||||
| } | } | ||||||
|  |  | ||||||
| static ImageInfoSpecific * | static ImageInfoSpecific * | ||||||
| block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp) | block_crypto_get_specific_info_luks(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     BlockCrypto *crypto = bs->opaque; |     BlockCrypto *crypto = bs->opaque; | ||||||
|     ImageInfoSpecific *spec_info; |     ImageInfoSpecific *spec_info; | ||||||
|     QCryptoBlockInfo *info; |     QCryptoBlockInfo *info; | ||||||
|  |  | ||||||
|     info = qcrypto_block_get_info(crypto->block, errp); |     info = qcrypto_block_get_info(crypto->block, NULL); | ||||||
|     if (!info) { |     if (!info) { | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|     assert(info->format == Q_CRYPTO_BLOCK_FORMAT_LUKS); |     if (info->format != Q_CRYPTO_BLOCK_FORMAT_LUKS) { | ||||||
|  |         qapi_free_QCryptoBlockInfo(info); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     spec_info = g_new(ImageInfoSpecific, 1); |     spec_info = g_new(ImageInfoSpecific, 1); | ||||||
|     spec_info->type = IMAGE_INFO_SPECIFIC_KIND_LUKS; |     spec_info->type = IMAGE_INFO_SPECIFIC_KIND_LUKS; | ||||||
| @@ -625,12 +622,9 @@ BlockDriver bdrv_crypto_luks = { | |||||||
|     .bdrv_probe         = block_crypto_probe_luks, |     .bdrv_probe         = block_crypto_probe_luks, | ||||||
|     .bdrv_open          = block_crypto_open_luks, |     .bdrv_open          = block_crypto_open_luks, | ||||||
|     .bdrv_close         = block_crypto_close, |     .bdrv_close         = block_crypto_close, | ||||||
|     /* This driver doesn't modify LUKS metadata except when creating image. |     .bdrv_child_perm    = bdrv_format_default_perms, | ||||||
|      * Allow share-rw=on as a special case. */ |  | ||||||
|     .bdrv_child_perm    = bdrv_filter_default_perms, |  | ||||||
|     .bdrv_co_create     = block_crypto_co_create_luks, |  | ||||||
|     .bdrv_co_create_opts = block_crypto_co_create_opts_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, |     .create_opts        = &block_crypto_create_opts_luks, | ||||||
|  |  | ||||||
|     .bdrv_reopen_prepare = block_crypto_reopen_prepare, |     .bdrv_reopen_prepare = block_crypto_reopen_prepare, | ||||||
|   | |||||||
| @@ -89,9 +89,13 @@ | |||||||
|     } |     } | ||||||
|  |  | ||||||
| QCryptoBlockCreateOptions * | QCryptoBlockCreateOptions * | ||||||
| block_crypto_create_opts_init(QDict *opts, Error **errp); | block_crypto_create_opts_init(QCryptoBlockFormat format, | ||||||
|  |                               QDict *opts, | ||||||
|  |                               Error **errp); | ||||||
|  |  | ||||||
| QCryptoBlockOpenOptions * | 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__ */ | #endif /* BLOCK_CRYPTO_H__ */ | ||||||
|   | |||||||
							
								
								
									
										41
									
								
								block/curl.c
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								block/curl.c
									
									
									
									
									
								
							| @@ -32,10 +32,22 @@ | |||||||
| #include "crypto/secret.h" | #include "crypto/secret.h" | ||||||
| #include <curl/curl.h> | #include <curl/curl.h> | ||||||
| #include "qemu/cutils.h" | #include "qemu/cutils.h" | ||||||
| #include "trace.h" |  | ||||||
|  |  | ||||||
|  | // #define DEBUG_CURL | ||||||
| // #define DEBUG_VERBOSE | // #define DEBUG_VERBOSE | ||||||
|  |  | ||||||
|  | #ifdef DEBUG_CURL | ||||||
|  | #define DEBUG_CURL_PRINT 1 | ||||||
|  | #else | ||||||
|  | #define DEBUG_CURL_PRINT 0 | ||||||
|  | #endif | ||||||
|  | #define DPRINTF(fmt, ...)                                            \ | ||||||
|  |     do {                                                             \ | ||||||
|  |         if (DEBUG_CURL_PRINT) {                                      \ | ||||||
|  |             fprintf(stderr, fmt, ## __VA_ARGS__);                    \ | ||||||
|  |         }                                                            \ | ||||||
|  |     } while (0) | ||||||
|  |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x071000 | #if LIBCURL_VERSION_NUM >= 0x071000 | ||||||
| /* The multi interface timer callback was introduced in 7.16.0 */ | /* The multi interface timer callback was introduced in 7.16.0 */ | ||||||
| #define NEED_CURL_TIMER_CALLBACK | #define NEED_CURL_TIMER_CALLBACK | ||||||
| @@ -142,7 +154,7 @@ static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque) | |||||||
| { | { | ||||||
|     BDRVCURLState *s = opaque; |     BDRVCURLState *s = opaque; | ||||||
|  |  | ||||||
|     trace_curl_timer_cb(timeout_ms); |     DPRINTF("CURL: timer callback timeout_ms %ld\n", timeout_ms); | ||||||
|     if (timeout_ms == -1) { |     if (timeout_ms == -1) { | ||||||
|         timer_del(&s->timer); |         timer_del(&s->timer); | ||||||
|     } else { |     } else { | ||||||
| @@ -181,7 +193,7 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, | |||||||
|     } |     } | ||||||
|     socket = NULL; |     socket = NULL; | ||||||
|  |  | ||||||
|     trace_curl_sock_cb(action, (int)fd); |     DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, (int)fd); | ||||||
|     switch (action) { |     switch (action) { | ||||||
|         case CURL_POLL_IN: |         case CURL_POLL_IN: | ||||||
|             aio_set_fd_handler(s->aio_context, fd, false, |             aio_set_fd_handler(s->aio_context, fd, false, | ||||||
| @@ -226,7 +238,7 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque) | |||||||
|     size_t realsize = size * nmemb; |     size_t realsize = size * nmemb; | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
|     trace_curl_read_cb(realsize); |     DPRINTF("CURL: Just reading %zd bytes\n", realsize); | ||||||
|  |  | ||||||
|     if (!s || !s->orig_buf) { |     if (!s || !s->orig_buf) { | ||||||
|         goto read_end; |         goto read_end; | ||||||
| @@ -471,8 +483,6 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state) | |||||||
|         curl_easy_setopt(state->curl, CURLOPT_URL, s->url); |         curl_easy_setopt(state->curl, CURLOPT_URL, s->url); | ||||||
|         curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER, |         curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER, | ||||||
|                          (long) s->sslverify); |                          (long) s->sslverify); | ||||||
|         curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYHOST, |  | ||||||
|                          s->sslverify ? 2L : 0L); |  | ||||||
|         if (s->cookie) { |         if (s->cookie) { | ||||||
|             curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie); |             curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie); | ||||||
|         } |         } | ||||||
| @@ -672,10 +682,10 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     const char *protocol_delimiter; |     const char *protocol_delimiter; | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     ret = bdrv_apply_auto_read_only(bs, "curl driver does not support writes", |  | ||||||
|                                     errp); |     if (flags & BDRV_O_RDWR) { | ||||||
|     if (ret < 0) { |         error_setg(errp, "curl block device does not support writes"); | ||||||
|         return ret; |         return -EROFS; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!libcurl_initialized) { |     if (!libcurl_initialized) { | ||||||
| @@ -765,7 +775,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     trace_curl_open(file); |     DPRINTF("CURL: Opening %s\n", file); | ||||||
|     qemu_co_queue_init(&s->free_state_waitq); |     qemu_co_queue_init(&s->free_state_waitq); | ||||||
|     s->aio_context = bdrv_get_aio_context(bs); |     s->aio_context = bdrv_get_aio_context(bs); | ||||||
|     s->url = g_strdup(file); |     s->url = g_strdup(file); | ||||||
| @@ -794,7 +804,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     } |     } | ||||||
|     /* Prior CURL 7.19.4 return value of 0 could mean that the file size is not |     /* Prior CURL 7.19.4 return value of 0 could mean that the file size is not | ||||||
|      * know or the size is zero. From 7.19.4 CURL returns -1 if size is not |      * know or the size is zero. From 7.19.4 CURL returns -1 if size is not | ||||||
|      * known and zero if it is really zero-length file. */ |      * known and zero if it is realy zero-length file. */ | ||||||
| #if LIBCURL_VERSION_NUM >= 0x071304 | #if LIBCURL_VERSION_NUM >= 0x071304 | ||||||
|     if (d < 0) { |     if (d < 0) { | ||||||
|         pstrcpy(state->errmsg, CURL_ERROR_SIZE, |         pstrcpy(state->errmsg, CURL_ERROR_SIZE, | ||||||
| @@ -818,7 +828,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|                 "Server does not support 'range' (byte ranges)."); |                 "Server does not support 'range' (byte ranges)."); | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|     trace_curl_open_size(s->len); |     DPRINTF("CURL: Size = %" PRIu64 "\n", s->len); | ||||||
|  |  | ||||||
|     qemu_mutex_lock(&s->mutex); |     qemu_mutex_lock(&s->mutex); | ||||||
|     curl_clean_state(state); |     curl_clean_state(state); | ||||||
| @@ -896,7 +906,8 @@ static void curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb) | |||||||
|     state->acb[0] = acb; |     state->acb[0] = acb; | ||||||
|  |  | ||||||
|     snprintf(state->range, 127, "%" PRIu64 "-%" PRIu64, start, end); |     snprintf(state->range, 127, "%" PRIu64 "-%" PRIu64, start, end); | ||||||
|     trace_curl_setup_preadv(acb->bytes, start, state->range); |     DPRINTF("CURL (AIO): Reading %" PRIu64 " at %" PRIu64 " (%s)\n", | ||||||
|  |             acb->bytes, start, state->range); | ||||||
|     curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range); |     curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range); | ||||||
|  |  | ||||||
|     curl_multi_add_handle(s->multi, state->curl); |     curl_multi_add_handle(s->multi, state->curl); | ||||||
| @@ -930,7 +941,7 @@ static void curl_close(BlockDriverState *bs) | |||||||
| { | { | ||||||
|     BDRVCURLState *s = bs->opaque; |     BDRVCURLState *s = bs->opaque; | ||||||
|  |  | ||||||
|     trace_curl_close(); |     DPRINTF("CURL: Close\n"); | ||||||
|     curl_detach_aio_context(bs); |     curl_detach_aio_context(bs); | ||||||
|     qemu_mutex_destroy(&s->mutex); |     qemu_mutex_destroy(&s->mutex); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -40,8 +40,6 @@ struct BdrvDirtyBitmap { | |||||||
|     QemuMutex *mutex; |     QemuMutex *mutex; | ||||||
|     HBitmap *bitmap;            /* Dirty bitmap implementation */ |     HBitmap *bitmap;            /* Dirty bitmap implementation */ | ||||||
|     HBitmap *meta;              /* Meta dirty bitmap */ |     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 */ |     BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */ | ||||||
|     char *name;                 /* Optional non-empty unique ID */ |     char *name;                 /* Optional non-empty unique ID */ | ||||||
|     int64_t size;               /* Size of the bitmap, in bytes */ |     int64_t size;               /* Size of the bitmap, in bytes */ | ||||||
| @@ -55,10 +53,6 @@ struct BdrvDirtyBitmap { | |||||||
|                                    and this bitmap must remain unchanged while |                                    and this bitmap must remain unchanged while | ||||||
|                                    this flag is set. */ |                                    this flag is set. */ | ||||||
|     bool persistent;            /* bitmap must be saved to owner disk image */ |     bool persistent;            /* bitmap must be saved to owner disk image */ | ||||||
|     bool migration;             /* Bitmap is selected for migration, it should |  | ||||||
|                                    not be stored on the next inactivation |  | ||||||
|                                    (persistent flag doesn't matter until next |  | ||||||
|                                    invalidation).*/ |  | ||||||
|     QLIST_ENTRY(BdrvDirtyBitmap) list; |     QLIST_ENTRY(BdrvDirtyBitmap) list; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -101,6 +95,15 @@ BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name) | |||||||
|     return NULL; |     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.  */ | /* Called with BQL taken.  */ | ||||||
| BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, | BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, | ||||||
|                                           uint32_t granularity, |                                           uint32_t granularity, | ||||||
| @@ -180,24 +183,6 @@ bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap) | |||||||
|     return bitmap->successor; |     return bitmap->successor; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Both conditions disallow user-modification via QMP. */ |  | ||||||
| bool bdrv_dirty_bitmap_user_locked(BdrvDirtyBitmap *bitmap) { |  | ||||||
|     return bdrv_dirty_bitmap_frozen(bitmap) || |  | ||||||
|            bdrv_dirty_bitmap_qmp_locked(bitmap); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked) |  | ||||||
| { |  | ||||||
|     qemu_mutex_lock(bitmap->mutex); |  | ||||||
|     bitmap->qmp_locked = qmp_locked; |  | ||||||
|     qemu_mutex_unlock(bitmap->mutex); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap) |  | ||||||
| { |  | ||||||
|     return bitmap->qmp_locked; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Called with BQL taken.  */ | /* Called with BQL taken.  */ | ||||||
| bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap) | bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap) | ||||||
| { | { | ||||||
| @@ -209,8 +194,6 @@ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap) | |||||||
| { | { | ||||||
|     if (bdrv_dirty_bitmap_frozen(bitmap)) { |     if (bdrv_dirty_bitmap_frozen(bitmap)) { | ||||||
|         return DIRTY_BITMAP_STATUS_FROZEN; |         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)) { |     } else if (!bdrv_dirty_bitmap_enabled(bitmap)) { | ||||||
|         return DIRTY_BITMAP_STATUS_DISABLED; |         return DIRTY_BITMAP_STATUS_DISABLED; | ||||||
|     } else { |     } else { | ||||||
| @@ -251,33 +234,6 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, | |||||||
|     return 0; |     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, |  * For a bitmap with a successor, yield our name to the successor, | ||||||
|  * delete the old bitmap, and return a handle to the new bitmap. |  * delete the old bitmap, and return a handle to the new bitmap. | ||||||
| @@ -311,9 +267,9 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, | |||||||
|  * In cases of failure where we can no longer safely delete the parent, |  * In cases of failure where we can no longer safely delete the parent, | ||||||
|  * we may wish to re-join the parent and child/successor. |  * we may wish to re-join the parent and child/successor. | ||||||
|  * The merged parent will be un-frozen, but not explicitly re-enabled. |  * 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, |                                            BdrvDirtyBitmap *parent, | ||||||
|                                            Error **errp) |                                            Error **errp) | ||||||
| { | { | ||||||
| @@ -324,30 +280,16 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, | |||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap)) { |     if (!hbitmap_merge(parent->bitmap, successor->bitmap)) { | ||||||
|         error_setg(errp, "Merging of parent and successor bitmap failed"); |         error_setg(errp, "Merging of parent and successor bitmap failed"); | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|     bdrv_release_dirty_bitmap_locked(successor); |     bdrv_release_dirty_bitmap(bs, successor); | ||||||
|     parent->successor = NULL; |     parent->successor = NULL; | ||||||
|  |  | ||||||
|     return parent; |     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. |  * Truncates _all_ bitmaps attached to a BDS. | ||||||
|  * Called with BQL taken. |  * Called with BQL taken. | ||||||
| @@ -366,12 +308,45 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes) | |||||||
|     bdrv_dirty_bitmaps_unlock(bs); |     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.  */ | /* Called with BQL taken.  */ | ||||||
| void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) | void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) | ||||||
| { | { | ||||||
|     bdrv_dirty_bitmaps_lock(bs); |     bdrv_do_release_matching_dirty_bitmap(bs, bitmap, NULL); | ||||||
|     bdrv_release_dirty_bitmap_locked(bitmap); |  | ||||||
|     bdrv_dirty_bitmaps_unlock(bs); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -382,15 +357,19 @@ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) | |||||||
|  */ |  */ | ||||||
| void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) | void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     BdrvDirtyBitmap *bm, *next; |     bdrv_do_release_matching_dirty_bitmap(bs, NULL, bdrv_dirty_bitmap_has_name); | ||||||
|  | } | ||||||
|  |  | ||||||
|     bdrv_dirty_bitmaps_lock(bs); | /** | ||||||
|     QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { |  * Release all persistent dirty bitmaps attached to a BDS (for use in | ||||||
|         if (bdrv_dirty_bitmap_name(bm)) { |  * bdrv_inactivate_recurse()). | ||||||
|             bdrv_release_dirty_bitmap_locked(bm); |  * There must not be any frozen bitmaps attached. | ||||||
|         } |  * This function does not remove persistent bitmaps from the storage. | ||||||
|     } |  */ | ||||||
|     bdrv_dirty_bitmaps_unlock(bs); | void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs) | ||||||
|  | { | ||||||
|  |     bdrv_do_release_matching_dirty_bitmap(bs, NULL, | ||||||
|  |                                           bdrv_dirty_bitmap_get_persistance); | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -410,19 +389,18 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Called with BQL taken.  */ | ||||||
| void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) | void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) | ||||||
| { | { | ||||||
|     bdrv_dirty_bitmap_lock(bitmap); |  | ||||||
|     assert(!bdrv_dirty_bitmap_frozen(bitmap)); |     assert(!bdrv_dirty_bitmap_frozen(bitmap)); | ||||||
|     bitmap->disabled = true; |     bitmap->disabled = true; | ||||||
|     bdrv_dirty_bitmap_unlock(bitmap); |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Called with BQL taken.  */ | ||||||
| void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap) | void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap) | ||||||
| { | { | ||||||
|     bdrv_dirty_bitmap_lock(bitmap); |     assert(!bdrv_dirty_bitmap_frozen(bitmap)); | ||||||
|     bdrv_enable_dirty_bitmap_locked(bitmap); |     bitmap->disabled = false; | ||||||
|     bdrv_dirty_bitmap_unlock(bitmap); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) | BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) | ||||||
| @@ -554,6 +532,7 @@ void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap, | |||||||
|  |  | ||||||
| void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out) | void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out) | ||||||
| { | { | ||||||
|  |     assert(bdrv_dirty_bitmap_enabled(bitmap)); | ||||||
|     assert(!bdrv_dirty_bitmap_readonly(bitmap)); |     assert(!bdrv_dirty_bitmap_readonly(bitmap)); | ||||||
|     bdrv_dirty_bitmap_lock(bitmap); |     bdrv_dirty_bitmap_lock(bitmap); | ||||||
|     if (!out) { |     if (!out) { | ||||||
| @@ -567,11 +546,12 @@ void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out) | |||||||
|     bdrv_dirty_bitmap_unlock(bitmap); |     bdrv_dirty_bitmap_unlock(bitmap); | ||||||
| } | } | ||||||
|  |  | ||||||
| void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup) | void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in) | ||||||
| { | { | ||||||
|     HBitmap *tmp = bitmap->bitmap; |     HBitmap *tmp = bitmap->bitmap; | ||||||
|  |     assert(bdrv_dirty_bitmap_enabled(bitmap)); | ||||||
|     assert(!bdrv_dirty_bitmap_readonly(bitmap)); |     assert(!bdrv_dirty_bitmap_readonly(bitmap)); | ||||||
|     bitmap->bitmap = backup; |     bitmap->bitmap = in; | ||||||
|     hbitmap_free(tmp); |     hbitmap_free(tmp); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -689,24 +669,16 @@ void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent) | |||||||
|     qemu_mutex_unlock(bitmap->mutex); |     qemu_mutex_unlock(bitmap->mutex); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Called with BQL taken. */ |  | ||||||
| void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration) |  | ||||||
| { |  | ||||||
|     qemu_mutex_lock(bitmap->mutex); |  | ||||||
|     bitmap->migration = migration; |  | ||||||
|     qemu_mutex_unlock(bitmap->mutex); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap) | bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap) | ||||||
| { | { | ||||||
|     return bitmap->persistent && !bitmap->migration; |     return bitmap->persistent; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs) | bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     BdrvDirtyBitmap *bm; |     BdrvDirtyBitmap *bm; | ||||||
|     QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { |     QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { | ||||||
|         if (bm->persistent && !bm->readonly && !bm->migration) { |         if (bm->persistent && !bm->readonly) { | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -726,54 +698,7 @@ char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp) | |||||||
|     return hbitmap_sha256(bitmap->bitmap, errp); |     return hbitmap_sha256(bitmap->bitmap, errp); | ||||||
| } | } | ||||||
|  |  | ||||||
| int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset, | int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset) | ||||||
|                                     uint64_t bytes) |  | ||||||
| { | { | ||||||
|     return hbitmap_next_zero(bitmap->bitmap, offset, bytes); |     return hbitmap_next_zero(bitmap->bitmap, offset); | ||||||
| } |  | ||||||
|  |  | ||||||
| bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap, |  | ||||||
|                                        uint64_t *offset, uint64_t *bytes) |  | ||||||
| { |  | ||||||
|     return hbitmap_next_dirty_area(bitmap->bitmap, offset, bytes); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, |  | ||||||
|                              HBitmap **backup, Error **errp) |  | ||||||
| { |  | ||||||
|     bool ret; |  | ||||||
|  |  | ||||||
|     /* only bitmaps from one bds are supported */ |  | ||||||
|     assert(dest->mutex == src->mutex); |  | ||||||
|  |  | ||||||
|     qemu_mutex_lock(dest->mutex); |  | ||||||
|  |  | ||||||
|     if (bdrv_dirty_bitmap_user_locked(dest)) { |  | ||||||
|         error_setg(errp, "Bitmap '%s' is currently in use by another" |  | ||||||
|         " operation and cannot be modified", dest->name); |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (bdrv_dirty_bitmap_readonly(dest)) { |  | ||||||
|         error_setg(errp, "Bitmap '%s' is readonly and cannot be modified", |  | ||||||
|                    dest->name); |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!hbitmap_can_merge(dest->bitmap, src->bitmap)) { |  | ||||||
|         error_setg(errp, "Bitmaps are incompatible and can't be merged"); |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (backup) { |  | ||||||
|         *backup = dest->bitmap; |  | ||||||
|         dest->bitmap = hbitmap_alloc(dest->size, hbitmap_granularity(*backup)); |  | ||||||
|         ret = hbitmap_merge(*backup, src->bitmap, dest->bitmap); |  | ||||||
|     } else { |  | ||||||
|         ret = hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap); |  | ||||||
|     } |  | ||||||
|     assert(ret); |  | ||||||
|  |  | ||||||
| out: |  | ||||||
|     qemu_mutex_unlock(dest->mutex); |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,49 +0,0 @@ | |||||||
| /* |  | ||||||
|  * DMG lzfse uncompression |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2018 Julio Cesar Faracco |  | ||||||
|  * |  | ||||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
|  * of this software and associated documentation files (the "Software"), to deal |  | ||||||
|  * in the Software without restriction, including without limitation the rights |  | ||||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
|  * copies of the Software, and to permit persons to whom the Software is |  | ||||||
|  * furnished to do so, subject to the following conditions: |  | ||||||
|  * |  | ||||||
|  * The above copyright notice and this permission notice shall be included in |  | ||||||
|  * all copies or substantial portions of the Software. |  | ||||||
|  * |  | ||||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |  | ||||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  | ||||||
|  * THE SOFTWARE. |  | ||||||
|  */ |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "qemu-common.h" |  | ||||||
| #include "dmg.h" |  | ||||||
| #include <lzfse.h> |  | ||||||
|  |  | ||||||
| static int dmg_uncompress_lzfse_do(char *next_in, unsigned int avail_in, |  | ||||||
|                                    char *next_out, unsigned int avail_out) |  | ||||||
| { |  | ||||||
|     size_t out_size = lzfse_decode_buffer((uint8_t *) next_out, avail_out, |  | ||||||
|                                           (uint8_t *) next_in, avail_in, |  | ||||||
|                                           NULL); |  | ||||||
|  |  | ||||||
|     /* We need to decode the single chunk only. */ |  | ||||||
|     /* So, out_size == avail_out is not an error here. */ |  | ||||||
|     if (out_size > 0) { |  | ||||||
|         return out_size; |  | ||||||
|     } |  | ||||||
|     return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| __attribute__((constructor)) |  | ||||||
| static void dmg_lzfse_init(void) |  | ||||||
| { |  | ||||||
|     assert(!dmg_uncompress_lzfse); |  | ||||||
|     dmg_uncompress_lzfse = dmg_uncompress_lzfse_do; |  | ||||||
| } |  | ||||||
							
								
								
									
										104
									
								
								block/dmg.c
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								block/dmg.c
									
									
									
									
									
								
							| @@ -33,9 +33,6 @@ | |||||||
| int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in, | int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in, | ||||||
|                           char *next_out, unsigned int avail_out); |                           char *next_out, unsigned int avail_out); | ||||||
|  |  | ||||||
| int (*dmg_uncompress_lzfse)(char *next_in, unsigned int avail_in, |  | ||||||
|                             char *next_out, unsigned int avail_out); |  | ||||||
|  |  | ||||||
| enum { | enum { | ||||||
|     /* Limit chunk sizes to prevent unreasonable amounts of memory being used |     /* Limit chunk sizes to prevent unreasonable amounts of memory being used | ||||||
|      * or truncating when converting to 32-bit types |      * or truncating when converting to 32-bit types | ||||||
| @@ -44,19 +41,6 @@ enum { | |||||||
|     DMG_SECTORCOUNTS_MAX = DMG_LENGTHS_MAX / 512, |     DMG_SECTORCOUNTS_MAX = DMG_LENGTHS_MAX / 512, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| enum { |  | ||||||
|     /* DMG Block Type */ |  | ||||||
|     UDZE = 0, /* Zeroes */ |  | ||||||
|     UDRW,     /* RAW type */ |  | ||||||
|     UDIG,     /* Ignore */ |  | ||||||
|     UDCO = 0x80000004, |  | ||||||
|     UDZO, |  | ||||||
|     UDBZ, |  | ||||||
|     ULFO, |  | ||||||
|     UDCM = 0x7ffffffe, /* Comments */ |  | ||||||
|     UDLE = 0xffffffff  /* Last Entry */ |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename) | static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||||
| { | { | ||||||
|     int len; |     int len; | ||||||
| @@ -121,17 +105,15 @@ static void update_max_chunk_size(BDRVDMGState *s, uint32_t chunk, | |||||||
|     uint32_t uncompressed_sectors = 0; |     uint32_t uncompressed_sectors = 0; | ||||||
|  |  | ||||||
|     switch (s->types[chunk]) { |     switch (s->types[chunk]) { | ||||||
|     case UDZO: /* zlib compressed */ |     case 0x80000005: /* zlib compressed */ | ||||||
|     case UDBZ: /* bzip2 compressed */ |     case 0x80000006: /* bzip2 compressed */ | ||||||
|     case ULFO: /* lzfse compressed */ |  | ||||||
|         compressed_size = s->lengths[chunk]; |         compressed_size = s->lengths[chunk]; | ||||||
|         uncompressed_sectors = s->sectorcounts[chunk]; |         uncompressed_sectors = s->sectorcounts[chunk]; | ||||||
|         break; |         break; | ||||||
|     case UDRW: /* copy */ |     case 1: /* copy */ | ||||||
|         uncompressed_sectors = DIV_ROUND_UP(s->lengths[chunk], 512); |         uncompressed_sectors = DIV_ROUND_UP(s->lengths[chunk], 512); | ||||||
|         break; |         break; | ||||||
|     case UDZE: /* zero */ |     case 2: /* zero */ | ||||||
|     case UDIG: /* ignore */ |  | ||||||
|         /* as the all-zeroes block may be large, it is treated specially: the |         /* as the all-zeroes block may be large, it is treated specially: the | ||||||
|          * sector is not copied from a large buffer, a simple memset is used |          * sector is not copied from a large buffer, a simple memset is used | ||||||
|          * instead. Therefore uncompressed_sectors does not need to be set. */ |          * instead. Therefore uncompressed_sectors does not need to be set. */ | ||||||
| @@ -200,15 +182,12 @@ typedef struct DmgHeaderState { | |||||||
| static bool dmg_is_known_block_type(uint32_t entry_type) | static bool dmg_is_known_block_type(uint32_t entry_type) | ||||||
| { | { | ||||||
|     switch (entry_type) { |     switch (entry_type) { | ||||||
|     case UDZE:    /* zeros */ |     case 0x00000001:    /* uncompressed */ | ||||||
|     case UDRW:    /* uncompressed */ |     case 0x00000002:    /* zeroes */ | ||||||
|     case UDIG:    /* ignore */ |     case 0x80000005:    /* zlib */ | ||||||
|     case UDZO:    /* zlib */ |  | ||||||
|         return true; |         return true; | ||||||
|     case UDBZ:    /* bzip2 */ |     case 0x80000006:    /* bzip2 */ | ||||||
|         return !!dmg_uncompress_bz2; |         return !!dmg_uncompress_bz2; | ||||||
|     case ULFO:    /* lzfse */ |  | ||||||
|         return !!dmg_uncompress_lzfse; |  | ||||||
|     default: |     default: | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| @@ -267,10 +246,9 @@ static int dmg_read_mish_block(BDRVDMGState *s, DmgHeaderState *ds, | |||||||
|         /* sector count */ |         /* sector count */ | ||||||
|         s->sectorcounts[i] = buff_read_uint64(buffer, offset + 0x10); |         s->sectorcounts[i] = buff_read_uint64(buffer, offset + 0x10); | ||||||
|  |  | ||||||
|         /* all-zeroes sector (type UDZE and UDIG) does not need to be |         /* all-zeroes sector (type 2) does not need to be "uncompressed" and can | ||||||
|          * "uncompressed" and can therefore be unbounded. */ |          * therefore be unbounded. */ | ||||||
|         if (s->types[i] != UDZE && s->types[i] != UDIG |         if (s->types[i] != 2 && s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) { | ||||||
|             && s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) { |  | ||||||
|             error_report("sector count %" PRIu64 " for chunk %" PRIu32 |             error_report("sector count %" PRIu64 " for chunk %" PRIu32 | ||||||
|                          " is larger than max (%u)", |                          " is larger than max (%u)", | ||||||
|                          s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX); |                          s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX); | ||||||
| @@ -435,19 +413,24 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     int64_t offset; |     int64_t offset; | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     ret = bdrv_apply_auto_read_only(bs, NULL, errp); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, |     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||||
|                                false, errp); |                                false, errp); | ||||||
|     if (!bs->file) { |     if (!bs->file) { | ||||||
|         return -EINVAL; |         return -EINVAL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (!bdrv_is_read_only(bs)) { | ||||||
|  |         error_report("Opening dmg images without an explicit read-only=on " | ||||||
|  |                      "option is deprecated. Future versions will refuse to " | ||||||
|  |                      "open the image instead of automatically marking the " | ||||||
|  |                      "image read-only."); | ||||||
|  |         ret = bdrv_set_read_only(bs, true, errp); | ||||||
|  |         if (ret < 0) { | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     block_module_load_one("dmg-bz2"); |     block_module_load_one("dmg-bz2"); | ||||||
|     block_module_load_one("dmg-lzfse"); |  | ||||||
|  |  | ||||||
|     s->n_chunks = 0; |     s->n_chunks = 0; | ||||||
|     s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; |     s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; | ||||||
| @@ -575,20 +558,16 @@ static inline uint32_t search_chunk(BDRVDMGState *s, uint64_t sector_num) | |||||||
| { | { | ||||||
|     /* binary search */ |     /* binary search */ | ||||||
|     uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3; |     uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3; | ||||||
|     while (chunk1 <= chunk2) { |     while (chunk1 != chunk2) { | ||||||
|         chunk3 = (chunk1 + chunk2) / 2; |         chunk3 = (chunk1 + chunk2) / 2; | ||||||
|         if (s->sectors[chunk3] > sector_num) { |         if (s->sectors[chunk3] > sector_num) { | ||||||
|             if (chunk3 == 0) { |             chunk2 = chunk3; | ||||||
|                 goto err; |  | ||||||
|             } |  | ||||||
|             chunk2 = chunk3 - 1; |  | ||||||
|         } else if (s->sectors[chunk3] + s->sectorcounts[chunk3] > sector_num) { |         } else if (s->sectors[chunk3] + s->sectorcounts[chunk3] > sector_num) { | ||||||
|             return chunk3; |             return chunk3; | ||||||
|         } else { |         } else { | ||||||
|             chunk1 = chunk3 + 1; |             chunk1 = chunk3; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| err: |  | ||||||
|     return s->n_chunks; /* error */ |     return s->n_chunks; /* error */ | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -606,7 +585,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) | |||||||
|  |  | ||||||
|         s->current_chunk = s->n_chunks; |         s->current_chunk = s->n_chunks; | ||||||
|         switch (s->types[chunk]) { /* block entry type */ |         switch (s->types[chunk]) { /* block entry type */ | ||||||
|         case UDZO: { /* zlib compressed */ |         case 0x80000005: { /* zlib compressed */ | ||||||
|             /* we need to buffer, because only the chunk as whole can be |             /* we need to buffer, because only the chunk as whole can be | ||||||
|              * inflated. */ |              * inflated. */ | ||||||
|             ret = bdrv_pread(bs->file, s->offsets[chunk], |             ret = bdrv_pread(bs->file, s->offsets[chunk], | ||||||
| @@ -629,7 +608,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) | |||||||
|                 return -1; |                 return -1; | ||||||
|             } |             } | ||||||
|             break; } |             break; } | ||||||
|         case UDBZ: /* bzip2 compressed */ |         case 0x80000006: /* bzip2 compressed */ | ||||||
|             if (!dmg_uncompress_bz2) { |             if (!dmg_uncompress_bz2) { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
| @@ -650,36 +629,14 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) | |||||||
|                 return ret; |                 return ret; | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case ULFO: |         case 1: /* copy */ | ||||||
|             if (!dmg_uncompress_lzfse) { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             /* we need to buffer, because only the chunk as whole can be |  | ||||||
|              * inflated. */ |  | ||||||
|             ret = bdrv_pread(bs->file, s->offsets[chunk], |  | ||||||
|                              s->compressed_chunk, s->lengths[chunk]); |  | ||||||
|             if (ret != s->lengths[chunk]) { |  | ||||||
|                 return -1; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             ret = dmg_uncompress_lzfse((char *)s->compressed_chunk, |  | ||||||
|                                        (unsigned int) s->lengths[chunk], |  | ||||||
|                                        (char *)s->uncompressed_chunk, |  | ||||||
|                                        (unsigned int) |  | ||||||
|                                            (512 * s->sectorcounts[chunk])); |  | ||||||
|             if (ret < 0) { |  | ||||||
|                 return ret; |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         case UDRW: /* copy */ |  | ||||||
|             ret = bdrv_pread(bs->file, s->offsets[chunk], |             ret = bdrv_pread(bs->file, s->offsets[chunk], | ||||||
|                              s->uncompressed_chunk, s->lengths[chunk]); |                              s->uncompressed_chunk, s->lengths[chunk]); | ||||||
|             if (ret != s->lengths[chunk]) { |             if (ret != s->lengths[chunk]) { | ||||||
|                 return -1; |                 return -1; | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case UDZE: /* zeros */ |         case 2: /* zero */ | ||||||
|         case UDIG: /* ignore */ |  | ||||||
|             /* see dmg_read, it is treated specially. No buffer needs to be |             /* see dmg_read, it is treated specially. No buffer needs to be | ||||||
|              * pre-filled, the zeroes can be set directly. */ |              * pre-filled, the zeroes can be set directly. */ | ||||||
|             break; |             break; | ||||||
| @@ -714,8 +671,7 @@ dmg_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, | |||||||
|         /* Special case: current chunk is all zeroes. Do not perform a memcpy as |         /* Special case: current chunk is all zeroes. Do not perform a memcpy as | ||||||
|          * s->uncompressed_chunk may be too small to cover the large all-zeroes |          * s->uncompressed_chunk may be too small to cover the large all-zeroes | ||||||
|          * section. dmg_read_chunk is called to find s->current_chunk */ |          * section. dmg_read_chunk is called to find s->current_chunk */ | ||||||
|         if (s->types[s->current_chunk] == UDZE |         if (s->types[s->current_chunk] == 2) { /* all zeroes block entry */ | ||||||
|             || s->types[s->current_chunk] == UDIG) { /* all zeroes block entry */ |  | ||||||
|             qemu_iovec_memset(qiov, i * 512, 0, 512); |             qemu_iovec_memset(qiov, i * 512, 0, 512); | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -55,7 +55,4 @@ typedef struct BDRVDMGState { | |||||||
| extern int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in, | extern int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in, | ||||||
|                                  char *next_out, unsigned int avail_out); |                                  char *next_out, unsigned int avail_out); | ||||||
|  |  | ||||||
| extern int (*dmg_uncompress_lzfse)(char *next_in, unsigned int avail_in, |  | ||||||
|                                    char *next_out, unsigned int avail_out); |  | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
							
								
								
									
										1210
									
								
								block/file-posix.c
									
									
									
									
									
								
							
							
						
						
									
										1210
									
								
								block/file-posix.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -162,7 +162,7 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile, | |||||||
|     acb->aio_nbytes = count; |     acb->aio_nbytes = count; | ||||||
|     acb->aio_offset = offset; |     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)); |     pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); | ||||||
|     return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); |     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, |                          &dg.Geometry.BytesPerSector, | ||||||
|                          &freeClusters, &totalClusters); |                          &freeClusters, &totalClusters); | ||||||
|         bs->bl.request_alignment = dg.Geometry.BytesPerSector; |         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, | static void raw_parse_flags(int flags, bool use_aio, int *access_flags, | ||||||
| @@ -414,32 +410,32 @@ fail: | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| static BlockAIOCB *raw_aio_preadv(BlockDriverState *bs, | static BlockAIOCB *raw_aio_readv(BlockDriverState *bs, | ||||||
|                                   uint64_t offset, uint64_t bytes, |                          int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | ||||||
|                                   QEMUIOVector *qiov, int flags, |  | ||||||
|                          BlockCompletionFunc *cb, void *opaque) |                          BlockCompletionFunc *cb, void *opaque) | ||||||
| { | { | ||||||
|     BDRVRawState *s = bs->opaque; |     BDRVRawState *s = bs->opaque; | ||||||
|     if (s->aio) { |     if (s->aio) { | ||||||
|         return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov, |         return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov, | ||||||
|                                 cb, opaque, QEMU_AIO_READ); |                                 nb_sectors, cb, opaque, QEMU_AIO_READ); | ||||||
|     } else { |     } 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); |                            cb, opaque, QEMU_AIO_READ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static BlockAIOCB *raw_aio_pwritev(BlockDriverState *bs, | static BlockAIOCB *raw_aio_writev(BlockDriverState *bs, | ||||||
|                                    uint64_t offset, uint64_t bytes, |                           int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | ||||||
|                                    QEMUIOVector *qiov, int flags, |  | ||||||
|                           BlockCompletionFunc *cb, void *opaque) |                           BlockCompletionFunc *cb, void *opaque) | ||||||
| { | { | ||||||
|     BDRVRawState *s = bs->opaque; |     BDRVRawState *s = bs->opaque; | ||||||
|     if (s->aio) { |     if (s->aio) { | ||||||
|         return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov, |         return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov, | ||||||
|                                 cb, opaque, QEMU_AIO_WRITE); |                                 nb_sectors, cb, opaque, QEMU_AIO_WRITE); | ||||||
|     } else { |     } 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); |                            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) |                         PreallocMode prealloc, Error **errp) | ||||||
| { | { | ||||||
|     BDRVRawState *s = bs->opaque; |     BDRVRawState *s = bs->opaque; | ||||||
| @@ -636,11 +632,11 @@ BlockDriver bdrv_file = { | |||||||
|     .bdrv_co_create_opts = raw_co_create_opts, |     .bdrv_co_create_opts = raw_co_create_opts, | ||||||
|     .bdrv_has_zero_init = bdrv_has_zero_init_1, |     .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||||||
|  |  | ||||||
|     .bdrv_aio_preadv    = raw_aio_preadv, |     .bdrv_aio_readv     = raw_aio_readv, | ||||||
|     .bdrv_aio_pwritev   = raw_aio_pwritev, |     .bdrv_aio_writev    = raw_aio_writev, | ||||||
|     .bdrv_aio_flush     = raw_aio_flush, |     .bdrv_aio_flush     = raw_aio_flush, | ||||||
|  |  | ||||||
|     .bdrv_co_truncate   = raw_co_truncate, |     .bdrv_truncate	= raw_truncate, | ||||||
|     .bdrv_getlength	= raw_getlength, |     .bdrv_getlength	= raw_getlength, | ||||||
|     .bdrv_get_allocated_file_size |     .bdrv_get_allocated_file_size | ||||||
|                         = raw_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); |     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, | static int hdev_open(BlockDriverState *bs, QDict *options, int flags, | ||||||
|                      Error **errp) |                      Error **errp) | ||||||
| { | { | ||||||
| @@ -803,10 +793,9 @@ static BlockDriver bdrv_host_device = { | |||||||
|     .bdrv_probe_device	= hdev_probe_device, |     .bdrv_probe_device	= hdev_probe_device, | ||||||
|     .bdrv_file_open	= hdev_open, |     .bdrv_file_open	= hdev_open, | ||||||
|     .bdrv_close		= raw_close, |     .bdrv_close		= raw_close, | ||||||
|     .bdrv_refresh_limits = hdev_refresh_limits, |  | ||||||
|  |  | ||||||
|     .bdrv_aio_preadv    = raw_aio_preadv, |     .bdrv_aio_readv     = raw_aio_readv, | ||||||
|     .bdrv_aio_pwritev   = raw_aio_pwritev, |     .bdrv_aio_writev    = raw_aio_writev, | ||||||
|     .bdrv_aio_flush     = raw_aio_flush, |     .bdrv_aio_flush     = raw_aio_flush, | ||||||
|  |  | ||||||
|     .bdrv_detach_aio_context = raw_detach_aio_context, |     .bdrv_detach_aio_context = raw_detach_aio_context, | ||||||
|   | |||||||
| @@ -11,7 +11,6 @@ | |||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include <glusterfs/api/glfs.h> | #include <glusterfs/api/glfs.h> | ||||||
| #include "block/block_int.h" | #include "block/block_int.h" | ||||||
| #include "block/qdict.h" |  | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qapi/qmp/qdict.h" | #include "qapi/qmp/qdict.h" | ||||||
| #include "qapi/qmp/qerror.h" | #include "qapi/qmp/qerror.h" | ||||||
| @@ -72,7 +71,7 @@ typedef struct ListElement { | |||||||
|     GlfsPreopened saved; |     GlfsPreopened saved; | ||||||
| } ListElement; | } ListElement; | ||||||
|  |  | ||||||
| static QLIST_HEAD(, ListElement) glfs_list; | static QLIST_HEAD(glfs_list, ListElement) glfs_list; | ||||||
|  |  | ||||||
| static QemuOptsList qemu_gluster_create_opts = { | static QemuOptsList qemu_gluster_create_opts = { | ||||||
|     .name = "qemu-gluster-create-opts", |     .name = "qemu-gluster-create-opts", | ||||||
| @@ -168,12 +167,7 @@ static QemuOptsList runtime_unix_opts = { | |||||||
|         { |         { | ||||||
|             .name = GLUSTER_OPT_SOCKET, |             .name = GLUSTER_OPT_SOCKET, | ||||||
|             .type = QEMU_OPT_STRING, |             .type = QEMU_OPT_STRING, | ||||||
|             .help = "socket file path (legacy)", |             .help = "socket file path)", | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|             .name = GLUSTER_OPT_PATH, |  | ||||||
|             .type = QEMU_OPT_STRING, |  | ||||||
|             .help = "socket file path (QAPI)", |  | ||||||
|         }, |         }, | ||||||
|         { /* end of list */ } |         { /* end of list */ } | ||||||
|     }, |     }, | ||||||
| @@ -621,18 +615,10 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, | |||||||
|                 goto out; |                 goto out; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             ptr = qemu_opt_get(opts, GLUSTER_OPT_PATH); |  | ||||||
|             if (!ptr) { |  | ||||||
|             ptr = qemu_opt_get(opts, GLUSTER_OPT_SOCKET); |             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) { |             if (!ptr) { | ||||||
|                 error_setg(&local_err, QERR_MISSING_PARAMETER, |                 error_setg(&local_err, QERR_MISSING_PARAMETER, | ||||||
|                            GLUSTER_OPT_PATH); |                            GLUSTER_OPT_SOCKET); | ||||||
|                 error_append_hint(&local_err, GERR_INDEX_HINT, i); |                 error_append_hint(&local_err, GERR_INDEX_HINT, i); | ||||||
|                 goto out; |                 goto out; | ||||||
|             } |             } | ||||||
| @@ -651,7 +637,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, | |||||||
|         } |         } | ||||||
|         gsconf = NULL; |         gsconf = NULL; | ||||||
|  |  | ||||||
|         qobject_unref(backing_options); |         QDECREF(backing_options); | ||||||
|         backing_options = NULL; |         backing_options = NULL; | ||||||
|         g_free(str); |         g_free(str); | ||||||
|         str = NULL; |         str = NULL; | ||||||
| @@ -664,7 +650,7 @@ out: | |||||||
|     qapi_free_SocketAddress(gsconf); |     qapi_free_SocketAddress(gsconf); | ||||||
|     qemu_opts_del(opts); |     qemu_opts_del(opts); | ||||||
|     g_free(str); |     g_free(str); | ||||||
|     qobject_unref(backing_options); |     QDECREF(backing_options); | ||||||
|     errno = EINVAL; |     errno = EINVAL; | ||||||
|     return -errno; |     return -errno; | ||||||
| } | } | ||||||
| @@ -679,7 +665,7 @@ static int qemu_gluster_parse(BlockdevOptionsGluster *gconf, | |||||||
|     if (filename) { |     if (filename) { | ||||||
|         ret = qemu_gluster_parse_uri(gconf, filename); |         ret = qemu_gluster_parse_uri(gconf, filename); | ||||||
|         if (ret < 0) { |         if (ret < 0) { | ||||||
|             error_setg(errp, "invalid URI %s", filename); |             error_setg(errp, "invalid URI"); | ||||||
|             error_append_hint(errp, "Usage: file=gluster[+transport]://" |             error_append_hint(errp, "Usage: file=gluster[+transport]://" | ||||||
|                                     "[host[:port]]volume/path[?socket=...]" |                                     "[host[:port]]volume/path[?socket=...]" | ||||||
|                                     "[,file.debug=N]" |                                     "[,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.host=1.2.3.4," | ||||||
|                              "file.server.0.port=24007," |                              "file.server.0.port=24007," | ||||||
|                              "file.server.1.transport=unix," |                              "file.server.1.transport=unix," | ||||||
|                              "file.server.1.path=/var/run/glusterd.socket ..." |                              "file.server.1.socket=/var/run/glusterd.socket ..." | ||||||
|                              "\n"); |                              "\n"); | ||||||
|             return ret; |             return ret; | ||||||
|         } |         } | ||||||
| @@ -849,16 +835,8 @@ static int qemu_gluster_open(BlockDriverState *bs,  QDict *options, | |||||||
|     qemu_gluster_parse_flags(bdrv_flags, &open_flags); |     qemu_gluster_parse_flags(bdrv_flags, &open_flags); | ||||||
|  |  | ||||||
|     s->fd = glfs_open(s->glfs, gconf->path, open_flags); |     s->fd = glfs_open(s->glfs, gconf->path, open_flags); | ||||||
|     ret = s->fd ? 0 : -errno; |     if (!s->fd) { | ||||||
|  |         ret = -errno; | ||||||
|     if (ret == -EACCES || ret == -EROFS) { |  | ||||||
|         /* Try to degrade to read-only, but if it doesn't work, still use the |  | ||||||
|          * normal error message. */ |  | ||||||
|         if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) { |  | ||||||
|             open_flags = (open_flags & ~O_RDWR) | O_RDONLY; |  | ||||||
|             s->fd = glfs_open(s->glfs, gconf->path, open_flags); |  | ||||||
|             ret = s->fd ? 0 : -errno; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     s->supports_seek_data = qemu_gluster_test_seek(s->fd); |     s->supports_seek_data = qemu_gluster_test_seek(s->fd); | ||||||
| @@ -1185,10 +1163,8 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs, | |||||||
|     return acb.ret; |     return acb.ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| static coroutine_fn int qemu_gluster_co_truncate(BlockDriverState *bs, | static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset, | ||||||
|                                                  int64_t offset, |                                  PreallocMode prealloc, Error **errp) | ||||||
|                                                  PreallocMode prealloc, |  | ||||||
|                                                  Error **errp) |  | ||||||
| { | { | ||||||
|     BDRVGlusterState *s = bs->opaque; |     BDRVGlusterState *s = bs->opaque; | ||||||
|     return qemu_gluster_do_truncate(s->fd, offset, prealloc, errp); |     return qemu_gluster_do_truncate(s->fd, offset, prealloc, errp); | ||||||
| @@ -1205,10 +1181,8 @@ static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs, | |||||||
| static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs, | static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs, | ||||||
|                                                int64_t sector_num, |                                                int64_t sector_num, | ||||||
|                                                int nb_sectors, |                                                int nb_sectors, | ||||||
|                                                QEMUIOVector *qiov, |                                                QEMUIOVector *qiov) | ||||||
|                                                int flags) |  | ||||||
| { | { | ||||||
|     assert(!flags); |  | ||||||
|     return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1); |     return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1334,7 +1308,7 @@ static int qemu_gluster_has_zero_init(BlockDriverState *bs) | |||||||
|  * If @start is in a trailing hole or beyond EOF, return -ENXIO. |  * If @start is in a trailing hole or beyond EOF, return -ENXIO. | ||||||
|  * If we can't find out, return a negative errno other than -ENXIO. |  * If we can't find out, return a negative errno other than -ENXIO. | ||||||
|  * |  * | ||||||
|  * (Shamefully copied from file-posix.c, only minuscule adaptions.) |  * (Shamefully copied from file-posix.c, only miniscule adaptions.) | ||||||
|  */ |  */ | ||||||
| static int find_allocation(BlockDriverState *bs, off_t start, | static int find_allocation(BlockDriverState *bs, off_t start, | ||||||
|                            off_t *data, off_t *hole) |                            off_t *data, off_t *hole) | ||||||
| @@ -1509,7 +1483,7 @@ static BlockDriver bdrv_gluster = { | |||||||
|     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, |     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, | ||||||
|     .bdrv_getlength               = qemu_gluster_getlength, |     .bdrv_getlength               = qemu_gluster_getlength, | ||||||
|     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, |     .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_readv                = qemu_gluster_co_readv, | ||||||
|     .bdrv_co_writev               = qemu_gluster_co_writev, |     .bdrv_co_writev               = qemu_gluster_co_writev, | ||||||
|     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, |     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, | ||||||
| @@ -1538,7 +1512,7 @@ static BlockDriver bdrv_gluster_tcp = { | |||||||
|     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, |     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, | ||||||
|     .bdrv_getlength               = qemu_gluster_getlength, |     .bdrv_getlength               = qemu_gluster_getlength, | ||||||
|     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, |     .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_readv                = qemu_gluster_co_readv, | ||||||
|     .bdrv_co_writev               = qemu_gluster_co_writev, |     .bdrv_co_writev               = qemu_gluster_co_writev, | ||||||
|     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, |     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, | ||||||
| @@ -1567,7 +1541,7 @@ static BlockDriver bdrv_gluster_unix = { | |||||||
|     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, |     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, | ||||||
|     .bdrv_getlength               = qemu_gluster_getlength, |     .bdrv_getlength               = qemu_gluster_getlength, | ||||||
|     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, |     .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_readv                = qemu_gluster_co_readv, | ||||||
|     .bdrv_co_writev               = qemu_gluster_co_writev, |     .bdrv_co_writev               = qemu_gluster_co_writev, | ||||||
|     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, |     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, | ||||||
| @@ -1602,7 +1576,7 @@ static BlockDriver bdrv_gluster_rdma = { | |||||||
|     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, |     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, | ||||||
|     .bdrv_getlength               = qemu_gluster_getlength, |     .bdrv_getlength               = qemu_gluster_getlength, | ||||||
|     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, |     .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_readv                = qemu_gluster_co_readv, | ||||||
|     .bdrv_co_writev               = qemu_gluster_co_writev, |     .bdrv_co_writev               = qemu_gluster_co_writev, | ||||||
|     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, |     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, | ||||||
|   | |||||||
							
								
								
									
										899
									
								
								block/io.c
									
									
									
									
									
								
							
							
						
						
									
										899
									
								
								block/io.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										419
									
								
								block/iscsi.c
									
									
									
									
									
								
							
							
						
						
									
										419
									
								
								block/iscsi.c
									
									
									
									
									
								
							| @@ -33,7 +33,6 @@ | |||||||
| #include "qemu/bitops.h" | #include "qemu/bitops.h" | ||||||
| #include "qemu/bitmap.h" | #include "qemu/bitmap.h" | ||||||
| #include "block/block_int.h" | #include "block/block_int.h" | ||||||
| #include "block/qdict.h" |  | ||||||
| #include "scsi/constants.h" | #include "scsi/constants.h" | ||||||
| #include "qemu/iov.h" | #include "qemu/iov.h" | ||||||
| #include "qemu/option.h" | #include "qemu/option.h" | ||||||
| @@ -44,14 +43,11 @@ | |||||||
| #include "qapi/qmp/qstring.h" | #include "qapi/qmp/qstring.h" | ||||||
| #include "crypto/secret.h" | #include "crypto/secret.h" | ||||||
| #include "scsi/utils.h" | #include "scsi/utils.h" | ||||||
| #include "trace.h" |  | ||||||
|  |  | ||||||
| /* Conflict between scsi/utils.h and libiscsi! :( */ | /* Conflict between scsi/utils.h and libiscsi! :( */ | ||||||
| #define SCSI_XFER_NONE ISCSI_XFER_NONE | #define SCSI_XFER_NONE ISCSI_XFER_NONE | ||||||
| #include <iscsi/iscsi.h> | #include <iscsi/iscsi.h> | ||||||
| #define inline __attribute__((gnu_inline))  /* required for libiscsi v1.9.0 */ |  | ||||||
| #include <iscsi/scsi-lowlevel.h> | #include <iscsi/scsi-lowlevel.h> | ||||||
| #undef inline |  | ||||||
| #undef SCSI_XFER_NONE | #undef SCSI_XFER_NONE | ||||||
| QEMU_BUILD_BUG_ON((int)SCSI_XFER_NONE != (int)ISCSI_XFER_NONE); | QEMU_BUILD_BUG_ON((int)SCSI_XFER_NONE != (int)ISCSI_XFER_NONE); | ||||||
|  |  | ||||||
| @@ -72,7 +68,6 @@ typedef struct IscsiLun { | |||||||
|     QemuMutex mutex; |     QemuMutex mutex; | ||||||
|     struct scsi_inquiry_logical_block_provisioning lbp; |     struct scsi_inquiry_logical_block_provisioning lbp; | ||||||
|     struct scsi_inquiry_block_limits bl; |     struct scsi_inquiry_block_limits bl; | ||||||
|     struct scsi_inquiry_device_designator *dd; |  | ||||||
|     unsigned char *zeroblock; |     unsigned char *zeroblock; | ||||||
|     /* The allocmap tracks which clusters (pages) on the iSCSI target are |     /* The allocmap tracks which clusters (pages) on the iSCSI target are | ||||||
|      * allocated and which are not. In case a target returns zeros for |      * allocated and which are not. In case a target returns zeros for | ||||||
| @@ -119,6 +114,7 @@ typedef struct IscsiAIOCB { | |||||||
|     QEMUBH *bh; |     QEMUBH *bh; | ||||||
|     IscsiLun *iscsilun; |     IscsiLun *iscsilun; | ||||||
|     struct scsi_task *task; |     struct scsi_task *task; | ||||||
|  |     uint8_t *buf; | ||||||
|     int status; |     int status; | ||||||
|     int64_t sector_num; |     int64_t sector_num; | ||||||
|     int nb_sectors; |     int nb_sectors; | ||||||
| @@ -126,7 +122,6 @@ typedef struct IscsiAIOCB { | |||||||
| #ifdef __linux__ | #ifdef __linux__ | ||||||
|     sg_io_hdr_t *ioh; |     sg_io_hdr_t *ioh; | ||||||
| #endif | #endif | ||||||
|     bool cancelled; |  | ||||||
| } IscsiAIOCB; | } IscsiAIOCB; | ||||||
|  |  | ||||||
| /* libiscsi uses time_t so its enough to process events every second */ | /* libiscsi uses time_t so its enough to process events every second */ | ||||||
| @@ -152,6 +147,9 @@ iscsi_bh_cb(void *p) | |||||||
|  |  | ||||||
|     qemu_bh_delete(acb->bh); |     qemu_bh_delete(acb->bh); | ||||||
|  |  | ||||||
|  |     g_free(acb->buf); | ||||||
|  |     acb->buf = NULL; | ||||||
|  |  | ||||||
|     acb->common.cb(acb->common.opaque, acb->status); |     acb->common.cb(acb->common.opaque, acb->status); | ||||||
|  |  | ||||||
|     if (acb->task != NULL) { |     if (acb->task != NULL) { | ||||||
| @@ -290,20 +288,14 @@ static void iscsi_co_init_iscsitask(IscsiLun *iscsilun, struct IscsiTask *iTask) | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Called (via iscsi_service) with QemuMutex held. */ |  | ||||||
| static void | static void | ||||||
| iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data, | iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data, | ||||||
|                     void *private_data) |                     void *private_data) | ||||||
| { | { | ||||||
|     IscsiAIOCB *acb = private_data; |     IscsiAIOCB *acb = private_data; | ||||||
|  |  | ||||||
|     /* If the command callback hasn't been called yet, drop the task */ |     acb->status = -ECANCELED; | ||||||
|     if (!acb->bh) { |     iscsi_schedule_bh(acb); | ||||||
|         /* Call iscsi_aio_ioctl_cb() with SCSI_STATUS_CANCELLED */ |  | ||||||
|         iscsi_scsi_cancel_task(iscsi, acb->task); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     qemu_aio_unref(acb); /* acquired in iscsi_aio_cancel() */ |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| @@ -312,25 +304,14 @@ iscsi_aio_cancel(BlockAIOCB *blockacb) | |||||||
|     IscsiAIOCB *acb = (IscsiAIOCB *)blockacb; |     IscsiAIOCB *acb = (IscsiAIOCB *)blockacb; | ||||||
|     IscsiLun *iscsilun = acb->iscsilun; |     IscsiLun *iscsilun = acb->iscsilun; | ||||||
|  |  | ||||||
|     qemu_mutex_lock(&iscsilun->mutex); |     if (acb->status != -EINPROGRESS) { | ||||||
|  |  | ||||||
|     /* If it was cancelled or completed already, our work is done here */ |  | ||||||
|     if (acb->cancelled || acb->status != -EINPROGRESS) { |  | ||||||
|         qemu_mutex_unlock(&iscsilun->mutex); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     acb->cancelled = true; |  | ||||||
|  |  | ||||||
|     qemu_aio_ref(acb); /* released in iscsi_abort_task_cb() */ |  | ||||||
|  |  | ||||||
|     /* send a task mgmt call to the target to cancel the task on the target */ |     /* send a task mgmt call to the target to cancel the task on the target */ | ||||||
|     if (iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task, |     iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task, | ||||||
|                                          iscsi_abort_task_cb, acb) < 0) { |                                      iscsi_abort_task_cb, acb); | ||||||
|         qemu_aio_unref(acb); /* since iscsi_abort_task_cb() won't be called */ |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     qemu_mutex_unlock(&iscsilun->mutex); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static const AIOCBInfo iscsi_aiocb_info = { | static const AIOCBInfo iscsi_aiocb_info = { | ||||||
| @@ -364,8 +345,6 @@ static void iscsi_timed_check_events(void *opaque) | |||||||
| { | { | ||||||
|     IscsiLun *iscsilun = opaque; |     IscsiLun *iscsilun = opaque; | ||||||
|  |  | ||||||
|     qemu_mutex_lock(&iscsilun->mutex); |  | ||||||
|  |  | ||||||
|     /* check for timed out requests */ |     /* check for timed out requests */ | ||||||
|     iscsi_service(iscsilun->iscsi, 0); |     iscsi_service(iscsilun->iscsi, 0); | ||||||
|  |  | ||||||
| @@ -378,8 +357,6 @@ static void iscsi_timed_check_events(void *opaque) | |||||||
|      * to return to service once this situation changes. */ |      * to return to service once this situation changes. */ | ||||||
|     iscsi_set_events(iscsilun); |     iscsi_set_events(iscsilun); | ||||||
|  |  | ||||||
|     qemu_mutex_unlock(&iscsilun->mutex); |  | ||||||
|  |  | ||||||
|     timer_mod(iscsilun->event_timer, |     timer_mod(iscsilun->event_timer, | ||||||
|               qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL); |               qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL); | ||||||
| } | } | ||||||
| @@ -578,19 +555,8 @@ static inline bool iscsi_allocmap_is_valid(IscsiLun *iscsilun, | |||||||
|                                offset / iscsilun->cluster_size) == size); |                                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 | 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) |                       QEMUIOVector *iov, int flags) | ||||||
| { | { | ||||||
|     IscsiLun *iscsilun = bs->opaque; |     IscsiLun *iscsilun = bs->opaque; | ||||||
| @@ -650,7 +616,12 @@ retry: | |||||||
|     scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov, |     scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov, | ||||||
|                           iov->niov); |                           iov->niov); | ||||||
| #endif | #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) { |     if (iTask.task != NULL) { | ||||||
|         scsi_free_scsi_task(iTask.task); |         scsi_free_scsi_task(iTask.task); | ||||||
| @@ -721,7 +692,13 @@ retry: | |||||||
|         ret = -ENOMEM; |         ret = -ENOMEM; | ||||||
|         goto out_unlock; |         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.do_retry) { | ||||||
|         if (iTask.task != NULL) { |         if (iTask.task != NULL) { | ||||||
| @@ -755,7 +732,7 @@ retry: | |||||||
|         goto out_unlock; |         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 || |     if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED || | ||||||
|         lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) { |         lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) { | ||||||
| @@ -885,8 +862,13 @@ retry: | |||||||
| #if LIBISCSI_API_VERSION < (20160603) | #if LIBISCSI_API_VERSION < (20160603) | ||||||
|     scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov); |     scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov); | ||||||
| #endif | #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) { |     if (iTask.task != NULL) { | ||||||
|         scsi_free_scsi_task(iTask.task); |         scsi_free_scsi_task(iTask.task); | ||||||
|         iTask.task = NULL; |         iTask.task = NULL; | ||||||
| @@ -923,7 +905,12 @@ retry: | |||||||
|         return -ENOMEM; |         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) { |     if (iTask.task != NULL) { | ||||||
|         scsi_free_scsi_task(iTask.task); |         scsi_free_scsi_task(iTask.task); | ||||||
| @@ -953,13 +940,8 @@ iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status, | |||||||
| { | { | ||||||
|     IscsiAIOCB *acb = opaque; |     IscsiAIOCB *acb = opaque; | ||||||
|  |  | ||||||
|     if (status == SCSI_STATUS_CANCELLED) { |     g_free(acb->buf); | ||||||
|         if (!acb->bh) { |     acb->buf = NULL; | ||||||
|             acb->status = -ECANCELED; |  | ||||||
|             iscsi_schedule_bh(acb); |  | ||||||
|         } |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     acb->status = 0; |     acb->status = 0; | ||||||
|     if (status < 0) { |     if (status < 0) { | ||||||
| @@ -1035,8 +1017,8 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, | |||||||
|     acb->iscsilun = iscsilun; |     acb->iscsilun = iscsilun; | ||||||
|     acb->bh          = NULL; |     acb->bh          = NULL; | ||||||
|     acb->status      = -EINPROGRESS; |     acb->status      = -EINPROGRESS; | ||||||
|  |     acb->buf         = NULL; | ||||||
|     acb->ioh         = buf; |     acb->ioh         = buf; | ||||||
|     acb->cancelled   = false; |  | ||||||
|  |  | ||||||
|     if (req != SG_IO) { |     if (req != SG_IO) { | ||||||
|         iscsi_ioctl_handle_emulated(acb, req, buf); |         iscsi_ioctl_handle_emulated(acb, req, buf); | ||||||
| @@ -1160,7 +1142,12 @@ retry: | |||||||
|         goto out_unlock; |         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) { |     if (iTask.task != NULL) { | ||||||
|         scsi_free_scsi_task(iTask.task); |         scsi_free_scsi_task(iTask.task); | ||||||
| @@ -1256,7 +1243,12 @@ retry: | |||||||
|         return -ENOMEM; |         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 && |     if (iTask.status == SCSI_STATUS_CHECK_CONDITION && | ||||||
|         iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && |         iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && | ||||||
| @@ -1740,34 +1732,14 @@ static QemuOptsList runtime_opts = { | |||||||
|             .name = "timeout", |             .name = "timeout", | ||||||
|             .type = QEMU_OPT_NUMBER, |             .type = QEMU_OPT_NUMBER, | ||||||
|         }, |         }, | ||||||
|  |         { | ||||||
|  |             .name = "filename", | ||||||
|  |             .type = QEMU_OPT_STRING, | ||||||
|  |         }, | ||||||
|         { /* end of list */ } |         { /* 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, | static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | ||||||
|                       Error **errp) |                       Error **errp) | ||||||
| { | { | ||||||
| @@ -1779,12 +1751,27 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     char *initiator_name = NULL; |     char *initiator_name = NULL; | ||||||
|     QemuOpts *opts; |     QemuOpts *opts; | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|     const char *transport_name, *portal, *target; |     const char *transport_name, *portal, *target, *filename; | ||||||
| #if LIBISCSI_API_VERSION >= (20160603) | #if LIBISCSI_API_VERSION >= (20160603) | ||||||
|     enum iscsi_transport_type transport; |     enum iscsi_transport_type transport; | ||||||
| #endif | #endif | ||||||
|     int i, ret = 0, timeout = 0, lun; |     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); |     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); | ||||||
|     qemu_opts_absorb_qdict(opts, options, &local_err); |     qemu_opts_absorb_qdict(opts, options, &local_err); | ||||||
|     if (local_err) { |     if (local_err) { | ||||||
| @@ -1869,7 +1856,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     iscsi_set_timeout(iscsi, timeout); |     iscsi_set_timeout(iscsi, timeout); | ||||||
| #else | #else | ||||||
|     if (timeout) { |     if (timeout) { | ||||||
|         warn_report("iSCSI: ignoring timeout value for libiscsi <1.15.0"); |         error_report("iSCSI: ignoring timeout value for libiscsi <1.15.0"); | ||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -1903,12 +1890,10 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     /* Check the write protect flag of the LUN if we want to write */ |     /* Check the write protect flag of the LUN if we want to write */ | ||||||
|     if (iscsilun->type == TYPE_DISK && (flags & BDRV_O_RDWR) && |     if (iscsilun->type == TYPE_DISK && (flags & BDRV_O_RDWR) && | ||||||
|         iscsilun->write_protected) { |         iscsilun->write_protected) { | ||||||
|         ret = bdrv_apply_auto_read_only(bs, "LUN is write protected", errp); |         error_setg(errp, "Cannot open a write protected LUN as read-write"); | ||||||
|         if (ret < 0) { |         ret = -EACCES; | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|         flags &= ~BDRV_O_RDWR; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     iscsi_readcapacity_sync(iscsilun, &local_err); |     iscsi_readcapacity_sync(iscsilun, &local_err); | ||||||
|     if (local_err != NULL) { |     if (local_err != NULL) { | ||||||
| @@ -1937,7 +1922,6 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|         struct scsi_task *inq_task; |         struct scsi_task *inq_task; | ||||||
|         struct scsi_inquiry_logical_block_provisioning *inq_lbp; |         struct scsi_inquiry_logical_block_provisioning *inq_lbp; | ||||||
|         struct scsi_inquiry_block_limits *inq_bl; |         struct scsi_inquiry_block_limits *inq_bl; | ||||||
|         struct scsi_inquiry_device_identification *inq_di; |  | ||||||
|         switch (inq_vpd->pages[i]) { |         switch (inq_vpd->pages[i]) { | ||||||
|         case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING: |         case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING: | ||||||
|             inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, |             inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, | ||||||
| @@ -1963,17 +1947,6 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|                    sizeof(struct scsi_inquiry_block_limits)); |                    sizeof(struct scsi_inquiry_block_limits)); | ||||||
|             scsi_free_scsi_task(inq_task); |             scsi_free_scsi_task(inq_task); | ||||||
|             break; |             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: |         default: | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| @@ -2016,7 +1989,7 @@ out: | |||||||
|         } |         } | ||||||
|         memset(iscsilun, 0, sizeof(IscsiLun)); |         memset(iscsilun, 0, sizeof(IscsiLun)); | ||||||
|     } |     } | ||||||
|  | exit: | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -2030,10 +2003,6 @@ static void iscsi_close(BlockDriverState *bs) | |||||||
|         iscsi_logout_sync(iscsi); |         iscsi_logout_sync(iscsi); | ||||||
|     } |     } | ||||||
|     iscsi_destroy_context(iscsi); |     iscsi_destroy_context(iscsi); | ||||||
|     if (iscsilun->dd) { |  | ||||||
|         g_free(iscsilun->dd->designator); |  | ||||||
|         g_free(iscsilun->dd); |  | ||||||
|     } |  | ||||||
|     g_free(iscsilun->zeroblock); |     g_free(iscsilun->zeroblock); | ||||||
|     iscsi_allocmap_free(iscsilun); |     iscsi_allocmap_free(iscsilun); | ||||||
|     qemu_mutex_destroy(&iscsilun->mutex); |     qemu_mutex_destroy(&iscsilun->mutex); | ||||||
| @@ -2113,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) |                           PreallocMode prealloc, Error **errp) | ||||||
| { | { | ||||||
|     IscsiLun *iscsilun = bs->opaque; |     IscsiLun *iscsilun = bs->opaque; | ||||||
| @@ -2174,7 +2143,7 @@ static int coroutine_fn iscsi_co_create_opts(const char *filename, QemuOpts *opt | |||||||
|     } else { |     } else { | ||||||
|         ret = iscsi_open(bs, bs_options, 0, NULL); |         ret = iscsi_open(bs, bs_options, 0, NULL); | ||||||
|     } |     } | ||||||
|     qobject_unref(bs_options); |     QDECREF(bs_options); | ||||||
|  |  | ||||||
|     if (ret != 0) { |     if (ret != 0) { | ||||||
|         goto out; |         goto out; | ||||||
| @@ -2215,226 +2184,6 @@ static void coroutine_fn iscsi_co_invalidate_cache(BlockDriverState *bs, | |||||||
|     iscsi_allocmap_invalidate(iscsilun); |     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 = { | static QemuOptsList iscsi_create_opts = { | ||||||
|     .name = "iscsi-create-opts", |     .name = "iscsi-create-opts", | ||||||
|     .head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head), |     .head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head), | ||||||
| @@ -2464,16 +2213,14 @@ static BlockDriver bdrv_iscsi = { | |||||||
|  |  | ||||||
|     .bdrv_getlength  = iscsi_getlength, |     .bdrv_getlength  = iscsi_getlength, | ||||||
|     .bdrv_get_info   = iscsi_get_info, |     .bdrv_get_info   = iscsi_get_info, | ||||||
|     .bdrv_co_truncate    = iscsi_co_truncate, |     .bdrv_truncate   = iscsi_truncate, | ||||||
|     .bdrv_refresh_limits = iscsi_refresh_limits, |     .bdrv_refresh_limits = iscsi_refresh_limits, | ||||||
|  |  | ||||||
|     .bdrv_co_block_status  = iscsi_co_block_status, |     .bdrv_co_block_status  = iscsi_co_block_status, | ||||||
|     .bdrv_co_pdiscard      = iscsi_co_pdiscard, |     .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_pwrite_zeroes = iscsi_co_pwrite_zeroes, | ||||||
|     .bdrv_co_readv         = iscsi_co_readv, |     .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, |     .bdrv_co_flush_to_disk = iscsi_co_flush, | ||||||
|  |  | ||||||
| #ifdef __linux__ | #ifdef __linux__ | ||||||
| @@ -2497,20 +2244,18 @@ static BlockDriver bdrv_iser = { | |||||||
|     .create_opts            = &iscsi_create_opts, |     .create_opts            = &iscsi_create_opts, | ||||||
|     .bdrv_reopen_prepare    = iscsi_reopen_prepare, |     .bdrv_reopen_prepare    = iscsi_reopen_prepare, | ||||||
|     .bdrv_reopen_commit     = iscsi_reopen_commit, |     .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_getlength  = iscsi_getlength, | ||||||
|     .bdrv_get_info   = iscsi_get_info, |     .bdrv_get_info   = iscsi_get_info, | ||||||
|     .bdrv_co_truncate    = iscsi_co_truncate, |     .bdrv_truncate   = iscsi_truncate, | ||||||
|     .bdrv_refresh_limits = iscsi_refresh_limits, |     .bdrv_refresh_limits = iscsi_refresh_limits, | ||||||
|  |  | ||||||
|     .bdrv_co_block_status  = iscsi_co_block_status, |     .bdrv_co_block_status  = iscsi_co_block_status, | ||||||
|     .bdrv_co_pdiscard      = iscsi_co_pdiscard, |     .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_pwrite_zeroes = iscsi_co_pwrite_zeroes, | ||||||
|     .bdrv_co_readv         = iscsi_co_readv, |     .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, |     .bdrv_co_flush_to_disk = iscsi_co_flush, | ||||||
|  |  | ||||||
| #ifdef __linux__ | #ifdef __linux__ | ||||||
|   | |||||||
| @@ -15,7 +15,6 @@ | |||||||
| #include "block/raw-aio.h" | #include "block/raw-aio.h" | ||||||
| #include "qemu/event_notifier.h" | #include "qemu/event_notifier.h" | ||||||
| #include "qemu/coroutine.h" | #include "qemu/coroutine.h" | ||||||
| #include "qapi/error.h" |  | ||||||
|  |  | ||||||
| #include <libaio.h> | #include <libaio.h> | ||||||
|  |  | ||||||
| @@ -234,9 +233,9 @@ static void qemu_laio_process_completions(LinuxAioState *s) | |||||||
|  |  | ||||||
| static void qemu_laio_process_completions_and_submit(LinuxAioState *s) | static void qemu_laio_process_completions_and_submit(LinuxAioState *s) | ||||||
| { | { | ||||||
|     aio_context_acquire(s->aio_context); |  | ||||||
|     qemu_laio_process_completions(s); |     qemu_laio_process_completions(s); | ||||||
|  |  | ||||||
|  |     aio_context_acquire(s->aio_context); | ||||||
|     if (!s->io_q.plugged && !QSIMPLEQ_EMPTY(&s->io_q.pending)) { |     if (!s->io_q.plugged && !QSIMPLEQ_EMPTY(&s->io_q.pending)) { | ||||||
|         ioq_submit(s); |         ioq_submit(s); | ||||||
|     } |     } | ||||||
| @@ -471,21 +470,16 @@ void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context) | |||||||
|                            qemu_laio_poll_cb); |                            qemu_laio_poll_cb); | ||||||
| } | } | ||||||
|  |  | ||||||
| LinuxAioState *laio_init(Error **errp) | LinuxAioState *laio_init(void) | ||||||
| { | { | ||||||
|     int rc; |  | ||||||
|     LinuxAioState *s; |     LinuxAioState *s; | ||||||
|  |  | ||||||
|     s = g_malloc0(sizeof(*s)); |     s = g_malloc0(sizeof(*s)); | ||||||
|     rc = event_notifier_init(&s->e, false); |     if (event_notifier_init(&s->e, false) < 0) { | ||||||
|     if (rc < 0) { |  | ||||||
|         error_setg_errno(errp, -rc, "failed to to initialize event notifier"); |  | ||||||
|         goto out_free_state; |         goto out_free_state; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     rc = io_setup(MAX_EVENTS, &s->ctx); |     if (io_setup(MAX_EVENTS, &s->ctx) != 0) { | ||||||
|     if (rc < 0) { |  | ||||||
|         error_setg_errno(errp, -rc, "failed to create linux AIO context"); |  | ||||||
|         goto out_close_efd; |         goto out_close_efd; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										869
									
								
								block/mirror.c
									
									
									
									
									
								
							
							
						
						
									
										869
									
								
								block/mirror.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -28,8 +28,6 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
|  |  | ||||||
| #include "trace.h" |  | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "nbd-client.h" | #include "nbd-client.h" | ||||||
|  |  | ||||||
| @@ -53,13 +51,15 @@ static void nbd_teardown_connection(BlockDriverState *bs) | |||||||
| { | { | ||||||
|     NBDClientSession *client = nbd_get_client_session(bs); |     NBDClientSession *client = nbd_get_client_session(bs); | ||||||
|  |  | ||||||
|     assert(client->ioc); |     if (!client->ioc) { /* Already closed */ | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /* finish any pending coroutines */ |     /* finish any pending coroutines */ | ||||||
|     qio_channel_shutdown(client->ioc, |     qio_channel_shutdown(client->ioc, | ||||||
|                          QIO_CHANNEL_SHUTDOWN_BOTH, |                          QIO_CHANNEL_SHUTDOWN_BOTH, | ||||||
|                          NULL); |                          NULL); | ||||||
|     BDRV_POLL_WHILE(bs, client->connection_co); |     BDRV_POLL_WHILE(bs, client->read_reply_co); | ||||||
|  |  | ||||||
|     nbd_client_detach_aio_context(bs); |     nbd_client_detach_aio_context(bs); | ||||||
|     object_unref(OBJECT(client->sioc)); |     object_unref(OBJECT(client->sioc)); | ||||||
| @@ -68,7 +68,7 @@ static void nbd_teardown_connection(BlockDriverState *bs) | |||||||
|     client->ioc = NULL; |     client->ioc = NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| static coroutine_fn void nbd_connection_entry(void *opaque) | static coroutine_fn void nbd_read_reply_entry(void *opaque) | ||||||
| { | { | ||||||
|     NBDClientSession *s = opaque; |     NBDClientSession *s = opaque; | ||||||
|     uint64_t i; |     uint64_t i; | ||||||
| @@ -79,8 +79,7 @@ static coroutine_fn void nbd_connection_entry(void *opaque) | |||||||
|         assert(s->reply.handle == 0); |         assert(s->reply.handle == 0); | ||||||
|         ret = nbd_receive_reply(s->ioc, &s->reply, &local_err); |         ret = nbd_receive_reply(s->ioc, &s->reply, &local_err); | ||||||
|         if (local_err) { |         if (local_err) { | ||||||
|             trace_nbd_read_reply_entry_fail(ret, error_get_pretty(local_err)); |             error_report_err(local_err); | ||||||
|             error_free(local_err); |  | ||||||
|         } |         } | ||||||
|         if (ret <= 0) { |         if (ret <= 0) { | ||||||
|             break; |             break; | ||||||
| @@ -100,14 +99,14 @@ static coroutine_fn void nbd_connection_entry(void *opaque) | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         /* We're woken up again by the request itself.  Note that there |         /* We're woken up again by the request itself.  Note that there | ||||||
|          * is no race between yielding and reentering connection_co.  This |          * is no race between yielding and reentering read_reply_co.  This | ||||||
|          * is because: |          * is because: | ||||||
|          * |          * | ||||||
|          * - if the request runs on the same AioContext, it is only |          * - if the request runs on the same AioContext, it is only | ||||||
|          *   entered after we yield |          *   entered after we yield | ||||||
|          * |          * | ||||||
|          * - if the request runs on a different AioContext, reentering |          * - if the request runs on a different AioContext, reentering | ||||||
|          *   connection_co happens through a bottom half, which can only |          *   read_reply_co happens through a bottom half, which can only | ||||||
|          *   run after we yield. |          *   run after we yield. | ||||||
|          */ |          */ | ||||||
|         aio_co_wake(s->requests[i].coroutine); |         aio_co_wake(s->requests[i].coroutine); | ||||||
| @@ -116,8 +115,7 @@ static coroutine_fn void nbd_connection_entry(void *opaque) | |||||||
|  |  | ||||||
|     s->quit = true; |     s->quit = true; | ||||||
|     nbd_recv_coroutines_wake_all(s); |     nbd_recv_coroutines_wake_all(s); | ||||||
|     s->connection_co = NULL; |     s->read_reply_co = NULL; | ||||||
|     aio_wait_kick(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int nbd_co_send_request(BlockDriverState *bs, | static int nbd_co_send_request(BlockDriverState *bs, | ||||||
| @@ -152,7 +150,10 @@ static int nbd_co_send_request(BlockDriverState *bs, | |||||||
|         rc = -EIO; |         rc = -EIO; | ||||||
|         goto err; |         goto err; | ||||||
|     } |     } | ||||||
|     assert(s->ioc); |     if (!s->ioc) { | ||||||
|  |         rc = -EPIPE; | ||||||
|  |         goto err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (qiov) { |     if (qiov) { | ||||||
|         qio_channel_set_cork(s->ioc, true); |         qio_channel_set_cork(s->ioc, true); | ||||||
| @@ -227,52 +228,6 @@ static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk, | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* nbd_parse_blockstatus_payload |  | ||||||
|  * support only one extent in reply and only for |  | ||||||
|  * base:allocation context |  | ||||||
|  */ |  | ||||||
| static int nbd_parse_blockstatus_payload(NBDClientSession *client, |  | ||||||
|                                          NBDStructuredReplyChunk *chunk, |  | ||||||
|                                          uint8_t *payload, uint64_t orig_length, |  | ||||||
|                                          NBDExtent *extent, Error **errp) |  | ||||||
| { |  | ||||||
|     uint32_t context_id; |  | ||||||
|  |  | ||||||
|     if (chunk->length != sizeof(context_id) + sizeof(*extent)) { |  | ||||||
|         error_setg(errp, "Protocol error: invalid payload for " |  | ||||||
|                          "NBD_REPLY_TYPE_BLOCK_STATUS"); |  | ||||||
|         return -EINVAL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     context_id = payload_advance32(&payload); |  | ||||||
|     if (client->info.context_id != context_id) { |  | ||||||
|         error_setg(errp, "Protocol error: unexpected context id %d for " |  | ||||||
|                          "NBD_REPLY_TYPE_BLOCK_STATUS, when negotiated context " |  | ||||||
|                          "id is %d", context_id, |  | ||||||
|                          client->info.context_id); |  | ||||||
|         return -EINVAL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     extent->length = payload_advance32(&payload); |  | ||||||
|     extent->flags = payload_advance32(&payload); |  | ||||||
|  |  | ||||||
|     if (extent->length == 0 || |  | ||||||
|         (client->info.min_block && !QEMU_IS_ALIGNED(extent->length, |  | ||||||
|                                                     client->info.min_block))) { |  | ||||||
|         error_setg(errp, "Protocol error: server sent status chunk with " |  | ||||||
|                    "invalid length"); |  | ||||||
|         return -EINVAL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* The server is allowed to send us extra information on the final |  | ||||||
|      * extent; just clamp it to the length we requested. */ |  | ||||||
|     if (extent->length > orig_length) { |  | ||||||
|         extent->length = orig_length; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* nbd_parse_error_payload | /* nbd_parse_error_payload | ||||||
|  * on success @errp contains message describing nbd error reply |  * on success @errp contains message describing nbd error reply | ||||||
|  */ |  */ | ||||||
| @@ -333,9 +288,10 @@ static int nbd_co_receive_offset_data_payload(NBDClientSession *s, | |||||||
|         return -EINVAL; |         return -EINVAL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (nbd_read64(s->ioc, &offset, "OFFSET_DATA offset", errp) < 0) { |     if (nbd_read(s->ioc, &offset, sizeof(offset), errp) < 0) { | ||||||
|         return -EIO; |         return -EIO; | ||||||
|     } |     } | ||||||
|  |     be64_to_cpus(&offset); | ||||||
|  |  | ||||||
|     data_size = chunk->length - sizeof(offset); |     data_size = chunk->length - sizeof(offset); | ||||||
|     assert(data_size); |     assert(data_size); | ||||||
| @@ -382,7 +338,7 @@ static coroutine_fn int nbd_co_receive_structured_payload( | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     *payload = g_new(char, len); |     *payload = g_new(char, len); | ||||||
|     ret = nbd_read(s->ioc, *payload, len, "structured payload", errp); |     ret = nbd_read(s->ioc, *payload, len, errp); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         g_free(*payload); |         g_free(*payload); | ||||||
|         *payload = NULL; |         *payload = NULL; | ||||||
| @@ -420,15 +376,14 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( | |||||||
|     } |     } | ||||||
|     *request_ret = 0; |     *request_ret = 0; | ||||||
|  |  | ||||||
|     /* Wait until we're woken up by nbd_connection_entry.  */ |     /* Wait until we're woken up by nbd_read_reply_entry.  */ | ||||||
|     s->requests[i].receiving = true; |     s->requests[i].receiving = true; | ||||||
|     qemu_coroutine_yield(); |     qemu_coroutine_yield(); | ||||||
|     s->requests[i].receiving = false; |     s->requests[i].receiving = false; | ||||||
|     if (s->quit) { |     if (!s->ioc || s->quit) { | ||||||
|         error_setg(errp, "Connection closed"); |         error_setg(errp, "Connection closed"); | ||||||
|         return -EIO; |         return -EIO; | ||||||
|     } |     } | ||||||
|     assert(s->ioc); |  | ||||||
|  |  | ||||||
|     assert(s->reply.handle == handle); |     assert(s->reply.handle == handle); | ||||||
|  |  | ||||||
| @@ -495,29 +450,30 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( | |||||||
| } | } | ||||||
|  |  | ||||||
| /* nbd_co_receive_one_chunk | /* nbd_co_receive_one_chunk | ||||||
|  * Read reply, wake up connection_co and set s->quit if needed. |  * Read reply, wake up read_reply_co and set s->quit if needed. | ||||||
|  * Return value is a fatal error code or normal nbd reply error code |  * Return value is a fatal error code or normal nbd reply error code | ||||||
|  */ |  */ | ||||||
| static coroutine_fn int nbd_co_receive_one_chunk( | static coroutine_fn int nbd_co_receive_one_chunk( | ||||||
|         NBDClientSession *s, uint64_t handle, bool only_structured, |         NBDClientSession *s, uint64_t handle, bool only_structured, | ||||||
|         int *request_ret, QEMUIOVector *qiov, NBDReply *reply, void **payload, |         QEMUIOVector *qiov, NBDReply *reply, void **payload, Error **errp) | ||||||
|         Error **errp) |  | ||||||
| { | { | ||||||
|  |     int request_ret; | ||||||
|     int ret = nbd_co_do_receive_one_chunk(s, handle, only_structured, |     int ret = nbd_co_do_receive_one_chunk(s, handle, only_structured, | ||||||
|                                           request_ret, qiov, payload, errp); |                                           &request_ret, qiov, payload, errp); | ||||||
|  |  | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         s->quit = true; |         s->quit = true; | ||||||
|     } else { |     } else { | ||||||
|         /* For assert at loop start in nbd_connection_entry */ |         /* For assert at loop start in nbd_read_reply_entry */ | ||||||
|         if (reply) { |         if (reply) { | ||||||
|             *reply = s->reply; |             *reply = s->reply; | ||||||
|         } |         } | ||||||
|         s->reply.handle = 0; |         s->reply.handle = 0; | ||||||
|  |         ret = request_ret; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (s->connection_co) { |     if (s->read_reply_co) { | ||||||
|         aio_co_wake(s->connection_co); |         aio_co_wake(s->read_reply_co); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return ret; |     return ret; | ||||||
| @@ -525,17 +481,20 @@ static coroutine_fn int nbd_co_receive_one_chunk( | |||||||
|  |  | ||||||
| typedef struct NBDReplyChunkIter { | typedef struct NBDReplyChunkIter { | ||||||
|     int ret; |     int ret; | ||||||
|     int request_ret; |  | ||||||
|     Error *err; |     Error *err; | ||||||
|     bool done, only_structured; |     bool done, only_structured; | ||||||
| } NBDReplyChunkIter; | } NBDReplyChunkIter; | ||||||
|  |  | ||||||
| static void nbd_iter_channel_error(NBDReplyChunkIter *iter, | static void nbd_iter_error(NBDReplyChunkIter *iter, bool fatal, | ||||||
|                            int ret, Error **local_err) |                            int ret, Error **local_err) | ||||||
| { | { | ||||||
|     assert(ret < 0); |     assert(ret < 0); | ||||||
|  |  | ||||||
|     if (!iter->ret) { |     if (fatal || iter->ret == 0) { | ||||||
|  |         if (iter->ret != 0) { | ||||||
|  |             error_free(iter->err); | ||||||
|  |             iter->err = NULL; | ||||||
|  |         } | ||||||
|         iter->ret = ret; |         iter->ret = ret; | ||||||
|         error_propagate(&iter->err, *local_err); |         error_propagate(&iter->err, *local_err); | ||||||
|     } else { |     } else { | ||||||
| @@ -545,15 +504,6 @@ static void nbd_iter_channel_error(NBDReplyChunkIter *iter, | |||||||
|     *local_err = NULL; |     *local_err = NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void nbd_iter_request_error(NBDReplyChunkIter *iter, int ret) |  | ||||||
| { |  | ||||||
|     assert(ret < 0); |  | ||||||
|  |  | ||||||
|     if (!iter->request_ret) { |  | ||||||
|         iter->request_ret = ret; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* NBD_FOREACH_REPLY_CHUNK | /* NBD_FOREACH_REPLY_CHUNK | ||||||
|  */ |  */ | ||||||
| #define NBD_FOREACH_REPLY_CHUNK(s, iter, handle, structured, \ | #define NBD_FOREACH_REPLY_CHUNK(s, iter, handle, structured, \ | ||||||
| @@ -569,13 +519,13 @@ static bool nbd_reply_chunk_iter_receive(NBDClientSession *s, | |||||||
|                                          QEMUIOVector *qiov, NBDReply *reply, |                                          QEMUIOVector *qiov, NBDReply *reply, | ||||||
|                                          void **payload) |                                          void **payload) | ||||||
| { | { | ||||||
|     int ret, request_ret; |     int ret; | ||||||
|     NBDReply local_reply; |     NBDReply local_reply; | ||||||
|     NBDStructuredReplyChunk *chunk; |     NBDStructuredReplyChunk *chunk; | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|     if (s->quit) { |     if (s->quit) { | ||||||
|         error_setg(&local_err, "Connection closed"); |         error_setg(&local_err, "Connection closed"); | ||||||
|         nbd_iter_channel_error(iter, -EIO, &local_err); |         nbd_iter_error(iter, true, -EIO, &local_err); | ||||||
|         goto break_loop; |         goto break_loop; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -589,16 +539,14 @@ static bool nbd_reply_chunk_iter_receive(NBDClientSession *s, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     ret = nbd_co_receive_one_chunk(s, handle, iter->only_structured, |     ret = nbd_co_receive_one_chunk(s, handle, iter->only_structured, | ||||||
|                                    &request_ret, qiov, reply, payload, |                                    qiov, reply, payload, &local_err); | ||||||
|                                    &local_err); |  | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         nbd_iter_channel_error(iter, ret, &local_err); |         /* If it is a fatal error s->quit is set by nbd_co_receive_one_chunk */ | ||||||
|     } else if (request_ret < 0) { |         nbd_iter_error(iter, s->quit, ret, &local_err); | ||||||
|         nbd_iter_request_error(iter, request_ret); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Do not execute the body of NBD_FOREACH_REPLY_CHUNK for simple reply. */ |     /* Do not execute the body of NBD_FOREACH_REPLY_CHUNK for simple reply. */ | ||||||
|     if (nbd_reply_is_simple(reply) || s->quit) { |     if (nbd_reply_is_simple(&s->reply) || s->quit) { | ||||||
|         goto break_loop; |         goto break_loop; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -631,7 +579,7 @@ break_loop: | |||||||
| } | } | ||||||
|  |  | ||||||
| static int nbd_co_receive_return_code(NBDClientSession *s, uint64_t handle, | static int nbd_co_receive_return_code(NBDClientSession *s, uint64_t handle, | ||||||
|                                       int *request_ret, Error **errp) |                                       Error **errp) | ||||||
| { | { | ||||||
|     NBDReplyChunkIter iter; |     NBDReplyChunkIter iter; | ||||||
|  |  | ||||||
| @@ -640,13 +588,12 @@ static int nbd_co_receive_return_code(NBDClientSession *s, uint64_t handle, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     error_propagate(errp, iter.err); |     error_propagate(errp, iter.err); | ||||||
|     *request_ret = iter.request_ret; |  | ||||||
|     return iter.ret; |     return iter.ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle, | static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle, | ||||||
|                                         uint64_t offset, QEMUIOVector *qiov, |                                         uint64_t offset, QEMUIOVector *qiov, | ||||||
|                                         int *request_ret, Error **errp) |                                         Error **errp) | ||||||
| { | { | ||||||
|     NBDReplyChunkIter iter; |     NBDReplyChunkIter iter; | ||||||
|     NBDReply reply; |     NBDReply reply; | ||||||
| @@ -671,7 +618,7 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle, | |||||||
|                                                 offset, qiov, &local_err); |                                                 offset, qiov, &local_err); | ||||||
|             if (ret < 0) { |             if (ret < 0) { | ||||||
|                 s->quit = true; |                 s->quit = true; | ||||||
|                 nbd_iter_channel_error(&iter, ret, &local_err); |                 nbd_iter_error(&iter, true, ret, &local_err); | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
| @@ -681,7 +628,7 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle, | |||||||
|                 error_setg(&local_err, |                 error_setg(&local_err, | ||||||
|                            "Unexpected reply type: %d (%s) for CMD_READ", |                            "Unexpected reply type: %d (%s) for CMD_READ", | ||||||
|                            chunk->type, nbd_reply_type_lookup(chunk->type)); |                            chunk->type, nbd_reply_type_lookup(chunk->type)); | ||||||
|                 nbd_iter_channel_error(&iter, -EINVAL, &local_err); |                 nbd_iter_error(&iter, true, -EINVAL, &local_err); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -690,79 +637,13 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     error_propagate(errp, iter.err); |     error_propagate(errp, iter.err); | ||||||
|     *request_ret = iter.request_ret; |  | ||||||
|     return iter.ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int nbd_co_receive_blockstatus_reply(NBDClientSession *s, |  | ||||||
|                                             uint64_t handle, uint64_t length, |  | ||||||
|                                             NBDExtent *extent, |  | ||||||
|                                             int *request_ret, Error **errp) |  | ||||||
| { |  | ||||||
|     NBDReplyChunkIter iter; |  | ||||||
|     NBDReply reply; |  | ||||||
|     void *payload = NULL; |  | ||||||
|     Error *local_err = NULL; |  | ||||||
|     bool received = false; |  | ||||||
|  |  | ||||||
|     assert(!extent->length); |  | ||||||
|     NBD_FOREACH_REPLY_CHUNK(s, iter, handle, s->info.structured_reply, |  | ||||||
|                             NULL, &reply, &payload) |  | ||||||
|     { |  | ||||||
|         int ret; |  | ||||||
|         NBDStructuredReplyChunk *chunk = &reply.structured; |  | ||||||
|  |  | ||||||
|         assert(nbd_reply_is_structured(&reply)); |  | ||||||
|  |  | ||||||
|         switch (chunk->type) { |  | ||||||
|         case NBD_REPLY_TYPE_BLOCK_STATUS: |  | ||||||
|             if (received) { |  | ||||||
|                 s->quit = true; |  | ||||||
|                 error_setg(&local_err, "Several BLOCK_STATUS chunks in reply"); |  | ||||||
|                 nbd_iter_channel_error(&iter, -EINVAL, &local_err); |  | ||||||
|             } |  | ||||||
|             received = true; |  | ||||||
|  |  | ||||||
|             ret = nbd_parse_blockstatus_payload(s, &reply.structured, |  | ||||||
|                                                 payload, length, extent, |  | ||||||
|                                                 &local_err); |  | ||||||
|             if (ret < 0) { |  | ||||||
|                 s->quit = true; |  | ||||||
|                 nbd_iter_channel_error(&iter, ret, &local_err); |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             if (!nbd_reply_type_is_error(chunk->type)) { |  | ||||||
|                 s->quit = true; |  | ||||||
|                 error_setg(&local_err, |  | ||||||
|                            "Unexpected reply type: %d (%s) " |  | ||||||
|                            "for CMD_BLOCK_STATUS", |  | ||||||
|                            chunk->type, nbd_reply_type_lookup(chunk->type)); |  | ||||||
|                 nbd_iter_channel_error(&iter, -EINVAL, &local_err); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         g_free(payload); |  | ||||||
|         payload = NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!extent->length && !iter.err) { |  | ||||||
|         error_setg(&iter.err, |  | ||||||
|                    "Server did not reply with any status extents"); |  | ||||||
|         if (!iter.ret) { |  | ||||||
|             iter.ret = -EIO; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     error_propagate(errp, iter.err); |  | ||||||
|     *request_ret = iter.request_ret; |  | ||||||
|     return iter.ret; |     return iter.ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int nbd_co_request(BlockDriverState *bs, NBDRequest *request, | static int nbd_co_request(BlockDriverState *bs, NBDRequest *request, | ||||||
|                           QEMUIOVector *write_qiov) |                           QEMUIOVector *write_qiov) | ||||||
| { | { | ||||||
|     int ret, request_ret; |     int ret; | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|     NBDClientSession *client = nbd_get_client_session(bs); |     NBDClientSession *client = nbd_get_client_session(bs); | ||||||
|  |  | ||||||
| @@ -778,22 +659,17 @@ static int nbd_co_request(BlockDriverState *bs, NBDRequest *request, | |||||||
|         return ret; |         return ret; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ret = nbd_co_receive_return_code(client, request->handle, |     ret = nbd_co_receive_return_code(client, request->handle, &local_err); | ||||||
|                                      &request_ret, &local_err); |  | ||||||
|     if (local_err) { |     if (local_err) { | ||||||
|         trace_nbd_co_request_fail(request->from, request->len, request->handle, |         error_report_err(local_err); | ||||||
|                                   request->flags, request->type, |  | ||||||
|                                   nbd_cmd_lookup(request->type), |  | ||||||
|                                   ret, error_get_pretty(local_err)); |  | ||||||
|         error_free(local_err); |  | ||||||
|     } |     } | ||||||
|     return ret ? ret : request_ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, | int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, | ||||||
|                          uint64_t bytes, QEMUIOVector *qiov, int flags) |                          uint64_t bytes, QEMUIOVector *qiov, int flags) | ||||||
| { | { | ||||||
|     int ret, request_ret; |     int ret; | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|     NBDClientSession *client = nbd_get_client_session(bs); |     NBDClientSession *client = nbd_get_client_session(bs); | ||||||
|     NBDRequest request = { |     NBDRequest request = { | ||||||
| @@ -814,15 +690,11 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     ret = nbd_co_receive_cmdread_reply(client, request.handle, offset, qiov, |     ret = nbd_co_receive_cmdread_reply(client, request.handle, offset, qiov, | ||||||
|                                        &request_ret, &local_err); |                                        &local_err); | ||||||
|     if (local_err) { |     if (local_err) { | ||||||
|         trace_nbd_co_request_fail(request.from, request.len, request.handle, |         error_report_err(local_err); | ||||||
|                                   request.flags, request.type, |  | ||||||
|                                   nbd_cmd_lookup(request.type), |  | ||||||
|                                   ret, error_get_pretty(local_err)); |  | ||||||
|         error_free(local_err); |  | ||||||
|     } |     } | ||||||
|     return ret ? ret : request_ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset, | int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset, | ||||||
| @@ -910,55 +782,6 @@ int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) | |||||||
|     return nbd_co_request(bs, &request, NULL); |     return nbd_co_request(bs, &request, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs, |  | ||||||
|                                             bool want_zero, |  | ||||||
|                                             int64_t offset, int64_t bytes, |  | ||||||
|                                             int64_t *pnum, int64_t *map, |  | ||||||
|                                             BlockDriverState **file) |  | ||||||
| { |  | ||||||
|     int ret, request_ret; |  | ||||||
|     NBDExtent extent = { 0 }; |  | ||||||
|     NBDClientSession *client = nbd_get_client_session(bs); |  | ||||||
|     Error *local_err = NULL; |  | ||||||
|  |  | ||||||
|     NBDRequest request = { |  | ||||||
|         .type = NBD_CMD_BLOCK_STATUS, |  | ||||||
|         .from = offset, |  | ||||||
|         .len = MIN(MIN_NON_ZERO(QEMU_ALIGN_DOWN(INT_MAX, |  | ||||||
|                                                 bs->bl.request_alignment), |  | ||||||
|                                 client->info.max_block), bytes), |  | ||||||
|         .flags = NBD_CMD_FLAG_REQ_ONE, |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     if (!client->info.base_allocation) { |  | ||||||
|         *pnum = bytes; |  | ||||||
|         return BDRV_BLOCK_DATA; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = nbd_co_send_request(bs, &request, NULL); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = nbd_co_receive_blockstatus_reply(client, request.handle, bytes, |  | ||||||
|                                            &extent, &request_ret, &local_err); |  | ||||||
|     if (local_err) { |  | ||||||
|         trace_nbd_co_request_fail(request.from, request.len, request.handle, |  | ||||||
|                                   request.flags, request.type, |  | ||||||
|                                   nbd_cmd_lookup(request.type), |  | ||||||
|                                   ret, error_get_pretty(local_err)); |  | ||||||
|         error_free(local_err); |  | ||||||
|     } |  | ||||||
|     if (ret < 0 || request_ret < 0) { |  | ||||||
|         return ret ? ret : request_ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     assert(extent.length); |  | ||||||
|     *pnum = extent.length; |  | ||||||
|     return (extent.flags & NBD_STATE_HOLE ? 0 : BDRV_BLOCK_DATA) | |  | ||||||
|            (extent.flags & NBD_STATE_ZERO ? BDRV_BLOCK_ZERO : 0); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void nbd_client_detach_aio_context(BlockDriverState *bs) | void nbd_client_detach_aio_context(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     NBDClientSession *client = nbd_get_client_session(bs); |     NBDClientSession *client = nbd_get_client_session(bs); | ||||||
| @@ -970,7 +793,7 @@ void nbd_client_attach_aio_context(BlockDriverState *bs, | |||||||
| { | { | ||||||
|     NBDClientSession *client = nbd_get_client_session(bs); |     NBDClientSession *client = nbd_get_client_session(bs); | ||||||
|     qio_channel_attach_aio_context(QIO_CHANNEL(client->ioc), new_context); |     qio_channel_attach_aio_context(QIO_CHANNEL(client->ioc), new_context); | ||||||
|     aio_co_schedule(new_context, client->connection_co); |     aio_co_schedule(new_context, client->read_reply_co); | ||||||
| } | } | ||||||
|  |  | ||||||
| void nbd_client_close(BlockDriverState *bs) | void nbd_client_close(BlockDriverState *bs) | ||||||
| @@ -978,84 +801,43 @@ void nbd_client_close(BlockDriverState *bs) | |||||||
|     NBDClientSession *client = nbd_get_client_session(bs); |     NBDClientSession *client = nbd_get_client_session(bs); | ||||||
|     NBDRequest request = { .type = NBD_CMD_DISC }; |     NBDRequest request = { .type = NBD_CMD_DISC }; | ||||||
|  |  | ||||||
|     assert(client->ioc); |     if (client->ioc == NULL) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     nbd_send_request(client->ioc, &request); |     nbd_send_request(client->ioc, &request); | ||||||
|  |  | ||||||
|     nbd_teardown_connection(bs); |     nbd_teardown_connection(bs); | ||||||
| } | } | ||||||
|  |  | ||||||
| static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr, | int nbd_client_init(BlockDriverState *bs, | ||||||
|                                                   Error **errp) |                     QIOChannelSocket *sioc, | ||||||
| { |  | ||||||
|     QIOChannelSocket *sioc; |  | ||||||
|     Error *local_err = NULL; |  | ||||||
|  |  | ||||||
|     sioc = qio_channel_socket_new(); |  | ||||||
|     qio_channel_set_name(QIO_CHANNEL(sioc), "nbd-client"); |  | ||||||
|  |  | ||||||
|     qio_channel_socket_connect_sync(sioc, saddr, &local_err); |  | ||||||
|     if (local_err) { |  | ||||||
|         object_unref(OBJECT(sioc)); |  | ||||||
|         error_propagate(errp, local_err); |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     qio_channel_set_delay(QIO_CHANNEL(sioc), false); |  | ||||||
|  |  | ||||||
|     return sioc; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int nbd_client_connect(BlockDriverState *bs, |  | ||||||
|                               SocketAddress *saddr, |  | ||||||
|                     const char *export, |                     const char *export, | ||||||
|                     QCryptoTLSCreds *tlscreds, |                     QCryptoTLSCreds *tlscreds, | ||||||
|                     const char *hostname, |                     const char *hostname, | ||||||
|                               const char *x_dirty_bitmap, |  | ||||||
|                     Error **errp) |                     Error **errp) | ||||||
| { | { | ||||||
|     NBDClientSession *client = nbd_get_client_session(bs); |     NBDClientSession *client = nbd_get_client_session(bs); | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     /* |  | ||||||
|      * establish TCP connection, return error if it fails |  | ||||||
|      * TODO: Configurable retry-until-timeout behaviour. |  | ||||||
|      */ |  | ||||||
|     QIOChannelSocket *sioc = nbd_establish_connection(saddr, errp); |  | ||||||
|  |  | ||||||
|     if (!sioc) { |  | ||||||
|         return -ECONNREFUSED; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* NBD handshake */ |     /* NBD handshake */ | ||||||
|     logout("session init %s\n", export); |     logout("session init %s\n", export); | ||||||
|     qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL); |     qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL); | ||||||
|  |  | ||||||
|     client->info.request_sizes = true; |     client->info.request_sizes = true; | ||||||
|     client->info.structured_reply = true; |     client->info.structured_reply = true; | ||||||
|     client->info.base_allocation = true; |     ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export, | ||||||
|     client->info.x_dirty_bitmap = g_strdup(x_dirty_bitmap); |                                 tlscreds, hostname, | ||||||
|     client->info.name = g_strdup(export ?: ""); |  | ||||||
|     ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), tlscreds, hostname, |  | ||||||
|                                 &client->ioc, &client->info, errp); |                                 &client->ioc, &client->info, errp); | ||||||
|     g_free(client->info.x_dirty_bitmap); |  | ||||||
|     g_free(client->info.name); |  | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         logout("Failed to negotiate with the NBD server\n"); |         logout("Failed to negotiate with the NBD server\n"); | ||||||
|         object_unref(OBJECT(sioc)); |  | ||||||
|         return ret; |         return ret; | ||||||
|     } |     } | ||||||
|     if (x_dirty_bitmap && !client->info.base_allocation) { |     if (client->info.flags & NBD_FLAG_READ_ONLY && | ||||||
|         error_setg(errp, "requested x-dirty-bitmap %s not found", |         !bdrv_is_read_only(bs)) { | ||||||
|                    x_dirty_bitmap); |         error_setg(errp, | ||||||
|         ret = -EINVAL; |                    "request for write access conflicts with read-only export"); | ||||||
|         goto fail; |         return -EACCES; | ||||||
|     } |  | ||||||
|     if (client->info.flags & NBD_FLAG_READ_ONLY) { |  | ||||||
|         ret = bdrv_apply_auto_read_only(bs, "NBD export is read-only", errp); |  | ||||||
|         if (ret < 0) { |  | ||||||
|             goto fail; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|     if (client->info.flags & NBD_FLAG_SEND_FUA) { |     if (client->info.flags & NBD_FLAG_SEND_FUA) { | ||||||
|         bs->supported_write_flags = BDRV_REQ_FUA; |         bs->supported_write_flags = BDRV_REQ_FUA; | ||||||
| @@ -1065,7 +847,10 @@ static int nbd_client_connect(BlockDriverState *bs, | |||||||
|         bs->supported_zero_flags |= BDRV_REQ_MAY_UNMAP; |         bs->supported_zero_flags |= BDRV_REQ_MAY_UNMAP; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     qemu_co_mutex_init(&client->send_mutex); | ||||||
|  |     qemu_co_queue_init(&client->free_sema); | ||||||
|     client->sioc = sioc; |     client->sioc = sioc; | ||||||
|  |     object_ref(OBJECT(client->sioc)); | ||||||
|  |  | ||||||
|     if (!client->ioc) { |     if (!client->ioc) { | ||||||
|         client->ioc = QIO_CHANNEL(sioc); |         client->ioc = QIO_CHANNEL(sioc); | ||||||
| @@ -1075,42 +860,9 @@ static int nbd_client_connect(BlockDriverState *bs, | |||||||
|     /* Now that we're connected, set the socket to be non-blocking and |     /* Now that we're connected, set the socket to be non-blocking and | ||||||
|      * kick the reply mechanism.  */ |      * kick the reply mechanism.  */ | ||||||
|     qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL); |     qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL); | ||||||
|     client->connection_co = qemu_coroutine_create(nbd_connection_entry, client); |     client->read_reply_co = qemu_coroutine_create(nbd_read_reply_entry, client); | ||||||
|     nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs)); |     nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs)); | ||||||
|  |  | ||||||
|     logout("Established connection with NBD server\n"); |     logout("Established connection with NBD server\n"); | ||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
|  fail: |  | ||||||
|     /* |  | ||||||
|      * We have connected, but must fail for other reasons. The |  | ||||||
|      * connection is still blocking; send NBD_CMD_DISC as a courtesy |  | ||||||
|      * to the server. |  | ||||||
|      */ |  | ||||||
|     { |  | ||||||
|         NBDRequest request = { .type = NBD_CMD_DISC }; |  | ||||||
|  |  | ||||||
|         nbd_send_request(client->ioc ?: QIO_CHANNEL(sioc), &request); |  | ||||||
|  |  | ||||||
|         object_unref(OBJECT(sioc)); |  | ||||||
|  |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int nbd_client_init(BlockDriverState *bs, |  | ||||||
|                     SocketAddress *saddr, |  | ||||||
|                     const char *export, |  | ||||||
|                     QCryptoTLSCreds *tlscreds, |  | ||||||
|                     const char *hostname, |  | ||||||
|                     const char *x_dirty_bitmap, |  | ||||||
|                     Error **errp) |  | ||||||
| { |  | ||||||
|     NBDClientSession *client = nbd_get_client_session(bs); |  | ||||||
|  |  | ||||||
|     qemu_co_mutex_init(&client->send_mutex); |  | ||||||
|     qemu_co_queue_init(&client->free_sema); |  | ||||||
|  |  | ||||||
|     return nbd_client_connect(bs, saddr, export, tlscreds, hostname, |  | ||||||
|                               x_dirty_bitmap, errp); |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ | |||||||
| typedef struct { | typedef struct { | ||||||
|     Coroutine *coroutine; |     Coroutine *coroutine; | ||||||
|     uint64_t offset;        /* original offset of the request */ |     uint64_t offset;        /* original offset of the request */ | ||||||
|     bool receiving;         /* waiting for connection_co? */ |     bool receiving;         /* waiting for read_reply_co? */ | ||||||
| } NBDClientRequest; | } NBDClientRequest; | ||||||
|  |  | ||||||
| typedef struct NBDClientSession { | typedef struct NBDClientSession { | ||||||
| @@ -30,7 +30,7 @@ typedef struct NBDClientSession { | |||||||
|  |  | ||||||
|     CoMutex send_mutex; |     CoMutex send_mutex; | ||||||
|     CoQueue free_sema; |     CoQueue free_sema; | ||||||
|     Coroutine *connection_co; |     Coroutine *read_reply_co; | ||||||
|     int in_flight; |     int in_flight; | ||||||
|  |  | ||||||
|     NBDClientRequest requests[MAX_NBD_REQUESTS]; |     NBDClientRequest requests[MAX_NBD_REQUESTS]; | ||||||
| @@ -41,11 +41,10 @@ typedef struct NBDClientSession { | |||||||
| NBDClientSession *nbd_get_client_session(BlockDriverState *bs); | NBDClientSession *nbd_get_client_session(BlockDriverState *bs); | ||||||
|  |  | ||||||
| int nbd_client_init(BlockDriverState *bs, | int nbd_client_init(BlockDriverState *bs, | ||||||
|                     SocketAddress *saddr, |                     QIOChannelSocket *sock, | ||||||
|                     const char *export_name, |                     const char *export_name, | ||||||
|                     QCryptoTLSCreds *tlscreds, |                     QCryptoTLSCreds *tlscreds, | ||||||
|                     const char *hostname, |                     const char *hostname, | ||||||
|                     const char *x_dirty_bitmap, |  | ||||||
|                     Error **errp); |                     Error **errp); | ||||||
| void nbd_client_close(BlockDriverState *bs); | 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, | void nbd_client_attach_aio_context(BlockDriverState *bs, | ||||||
|                                    AioContext *new_context); |                                    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 */ | #endif /* NBD_CLIENT_H */ | ||||||
|   | |||||||
							
								
								
									
										73
									
								
								block/nbd.c
									
									
									
									
									
								
							
							
						
						
									
										73
									
								
								block/nbd.c
									
									
									
									
									
								
							| @@ -27,8 +27,7 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "nbd-client.h" | #include "block/nbd-client.h" | ||||||
| #include "block/qdict.h" |  | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qemu/uri.h" | #include "qemu/uri.h" | ||||||
| #include "block/block_int.h" | #include "block/block_int.h" | ||||||
| @@ -109,7 +108,7 @@ static int nbd_parse_uri(const char *filename, QDict *options) | |||||||
|         /* strip braces from literal IPv6 address */ |         /* strip braces from literal IPv6 address */ | ||||||
|         if (uri->server[0] == '[') { |         if (uri->server[0] == '[') { | ||||||
|             host = qstring_from_substr(uri->server, 1, |             host = qstring_from_substr(uri->server, 1, | ||||||
|                                        strlen(uri->server) - 1); |                                        strlen(uri->server) - 2); | ||||||
|         } else { |         } else { | ||||||
|             host = qstring_from_str(uri->server); |             host = qstring_from_str(uri->server); | ||||||
|         } |         } | ||||||
| @@ -263,6 +262,7 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, | |||||||
| { | { | ||||||
|     SocketAddress *saddr = NULL; |     SocketAddress *saddr = NULL; | ||||||
|     QDict *addr = NULL; |     QDict *addr = NULL; | ||||||
|  |     QObject *crumpled_addr = NULL; | ||||||
|     Visitor *iv = NULL; |     Visitor *iv = NULL; | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|  |  | ||||||
| @@ -272,11 +272,20 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, | |||||||
|         goto done; |         goto done; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     iv = qobject_input_visitor_new_flat_confused(addr, errp); |     crumpled_addr = qdict_crumple(addr, errp); | ||||||
|     if (!iv) { |     if (!crumpled_addr) { | ||||||
|         goto done; |         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); |     visit_type_SocketAddress(iv, NULL, &saddr, &local_err); | ||||||
|     if (local_err) { |     if (local_err) { | ||||||
|         error_propagate(errp, local_err); |         error_propagate(errp, local_err); | ||||||
| @@ -284,7 +293,8 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, | |||||||
|     } |     } | ||||||
|  |  | ||||||
| done: | done: | ||||||
|     qobject_unref(addr); |     QDECREF(addr); | ||||||
|  |     qobject_decref(crumpled_addr); | ||||||
|     visit_free(iv); |     visit_free(iv); | ||||||
|     return saddr; |     return saddr; | ||||||
| } | } | ||||||
| @@ -295,6 +305,30 @@ NBDClientSession *nbd_get_client_session(BlockDriverState *bs) | |||||||
|     return &s->client; |     return &s->client; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr, | ||||||
|  |                                                   Error **errp) | ||||||
|  | { | ||||||
|  |     QIOChannelSocket *sioc; | ||||||
|  |     Error *local_err = NULL; | ||||||
|  |  | ||||||
|  |     sioc = qio_channel_socket_new(); | ||||||
|  |     qio_channel_set_name(QIO_CHANNEL(sioc), "nbd-client"); | ||||||
|  |  | ||||||
|  |     qio_channel_socket_connect_sync(sioc, | ||||||
|  |                                     saddr, | ||||||
|  |                                     &local_err); | ||||||
|  |     if (local_err) { | ||||||
|  |         object_unref(OBJECT(sioc)); | ||||||
|  |         error_propagate(errp, local_err); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     qio_channel_set_delay(QIO_CHANNEL(sioc), false); | ||||||
|  |  | ||||||
|  |     return sioc; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
| static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) | static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) | ||||||
| { | { | ||||||
|     Object *obj; |     Object *obj; | ||||||
| @@ -354,12 +388,6 @@ static QemuOptsList nbd_runtime_opts = { | |||||||
|             .type = QEMU_OPT_STRING, |             .type = QEMU_OPT_STRING, | ||||||
|             .help = "ID of the TLS credentials to use", |             .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 */ } |         { /* end of list */ } | ||||||
|     }, |     }, | ||||||
| }; | }; | ||||||
| @@ -370,6 +398,7 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     BDRVNBDState *s = bs->opaque; |     BDRVNBDState *s = bs->opaque; | ||||||
|     QemuOpts *opts = NULL; |     QemuOpts *opts = NULL; | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|  |     QIOChannelSocket *sioc = NULL; | ||||||
|     QCryptoTLSCreds *tlscreds = NULL; |     QCryptoTLSCreds *tlscreds = NULL; | ||||||
|     const char *hostname = NULL; |     const char *hostname = NULL; | ||||||
|     int ret = -EINVAL; |     int ret = -EINVAL; | ||||||
| @@ -409,11 +438,22 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|         hostname = s->saddr->u.inet.host; |         hostname = s->saddr->u.inet.host; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* NBD handshake */ |     /* establish TCP connection, return error if it fails | ||||||
|     ret = nbd_client_init(bs, s->saddr, s->export, tlscreds, hostname, |      * TODO: Configurable retry-until-timeout behaviour. | ||||||
|                           qemu_opt_get(opts, "x-dirty-bitmap"), errp); |      */ | ||||||
|  |     sioc = nbd_establish_connection(s->saddr, errp); | ||||||
|  |     if (!sioc) { | ||||||
|  |         ret = -ECONNREFUSED; | ||||||
|  |         goto error; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* NBD handshake */ | ||||||
|  |     ret = nbd_client_init(bs, sioc, s->export, | ||||||
|  |                           tlscreds, hostname, errp); | ||||||
|  error: |  error: | ||||||
|  |     if (sioc) { | ||||||
|  |         object_unref(OBJECT(sioc)); | ||||||
|  |     } | ||||||
|     if (tlscreds) { |     if (tlscreds) { | ||||||
|         object_unref(OBJECT(tlscreds)); |         object_unref(OBJECT(tlscreds)); | ||||||
|     } |     } | ||||||
| @@ -545,7 +585,6 @@ static BlockDriver bdrv_nbd = { | |||||||
|     .bdrv_detach_aio_context    = nbd_detach_aio_context, |     .bdrv_detach_aio_context    = nbd_detach_aio_context, | ||||||
|     .bdrv_attach_aio_context    = nbd_attach_aio_context, |     .bdrv_attach_aio_context    = nbd_attach_aio_context, | ||||||
|     .bdrv_refresh_filename      = nbd_refresh_filename, |     .bdrv_refresh_filename      = nbd_refresh_filename, | ||||||
|     .bdrv_co_block_status       = nbd_client_co_block_status, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static BlockDriver bdrv_nbd_tcp = { | static BlockDriver bdrv_nbd_tcp = { | ||||||
| @@ -565,7 +604,6 @@ static BlockDriver bdrv_nbd_tcp = { | |||||||
|     .bdrv_detach_aio_context    = nbd_detach_aio_context, |     .bdrv_detach_aio_context    = nbd_detach_aio_context, | ||||||
|     .bdrv_attach_aio_context    = nbd_attach_aio_context, |     .bdrv_attach_aio_context    = nbd_attach_aio_context, | ||||||
|     .bdrv_refresh_filename      = nbd_refresh_filename, |     .bdrv_refresh_filename      = nbd_refresh_filename, | ||||||
|     .bdrv_co_block_status       = nbd_client_co_block_status, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static BlockDriver bdrv_nbd_unix = { | static BlockDriver bdrv_nbd_unix = { | ||||||
| @@ -585,7 +623,6 @@ static BlockDriver bdrv_nbd_unix = { | |||||||
|     .bdrv_detach_aio_context    = nbd_detach_aio_context, |     .bdrv_detach_aio_context    = nbd_detach_aio_context, | ||||||
|     .bdrv_attach_aio_context    = nbd_attach_aio_context, |     .bdrv_attach_aio_context    = nbd_attach_aio_context, | ||||||
|     .bdrv_refresh_filename      = nbd_refresh_filename, |     .bdrv_refresh_filename      = nbd_refresh_filename, | ||||||
|     .bdrv_co_block_status       = nbd_client_co_block_status, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void bdrv_nbd_init(void) | static void bdrv_nbd_init(void) | ||||||
|   | |||||||
							
								
								
									
										23
									
								
								block/nfs.c
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								block/nfs.c
									
									
									
									
									
								
							| @@ -29,7 +29,6 @@ | |||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "block/block_int.h" | #include "block/block_int.h" | ||||||
| #include "block/qdict.h" |  | ||||||
| #include "trace.h" | #include "trace.h" | ||||||
| #include "qemu/iov.h" | #include "qemu/iov.h" | ||||||
| #include "qemu/option.h" | #include "qemu/option.h" | ||||||
| @@ -556,29 +555,24 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options, | |||||||
|                                                      Error **errp) |                                                      Error **errp) | ||||||
| { | { | ||||||
|     BlockdevOptionsNfs *opts = NULL; |     BlockdevOptionsNfs *opts = NULL; | ||||||
|  |     QObject *crumpled = NULL; | ||||||
|     Visitor *v; |     Visitor *v; | ||||||
|     const QDictEntry *e; |  | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|  |  | ||||||
|     v = qobject_input_visitor_new_flat_confused(options, errp); |     crumpled = qdict_crumple(options, errp); | ||||||
|     if (!v) { |     if (crumpled == NULL) { | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     v = qobject_input_visitor_new_keyval(crumpled); | ||||||
|     visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err); |     visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err); | ||||||
|     visit_free(v); |     visit_free(v); | ||||||
|  |     qobject_decref(crumpled); | ||||||
|  |  | ||||||
|     if (local_err) { |     if (local_err) { | ||||||
|         error_propagate(errp, local_err); |  | ||||||
|         return NULL; |         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; |     return opts; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -689,7 +683,7 @@ static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts, | |||||||
|  |  | ||||||
|     ret = 0; |     ret = 0; | ||||||
| out: | out: | ||||||
|     qobject_unref(options); |     QDECREF(options); | ||||||
|     qapi_free_BlockdevCreateOptions(create_options); |     qapi_free_BlockdevCreateOptions(create_options); | ||||||
|     return ret; |     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); |     return (task.ret < 0 ? task.ret : st.st_blocks * 512); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coroutine_fn | static int nfs_file_truncate(BlockDriverState *bs, int64_t offset, | ||||||
| nfs_file_co_truncate(BlockDriverState *bs, int64_t offset, |  | ||||||
|                              PreallocMode prealloc, Error **errp) |                              PreallocMode prealloc, Error **errp) | ||||||
| { | { | ||||||
|     NFSClient *client = bs->opaque; |     NFSClient *client = bs->opaque; | ||||||
| @@ -874,7 +867,7 @@ static BlockDriver bdrv_nfs = { | |||||||
|  |  | ||||||
|     .bdrv_has_zero_init             = nfs_has_zero_init, |     .bdrv_has_zero_init             = nfs_has_zero_init, | ||||||
|     .bdrv_get_allocated_file_size   = nfs_get_allocated_file_size, |     .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_file_open                 = nfs_file_open, | ||||||
|     .bdrv_close                     = nfs_file_close, |     .bdrv_close                     = nfs_file_close, | ||||||
|   | |||||||
							
								
								
									
										46
									
								
								block/null.c
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								block/null.c
									
									
									
									
									
								
							| @@ -93,10 +93,13 @@ static int null_file_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     } |     } | ||||||
|     s->read_zeroes = qemu_opt_get_bool(opts, NULL_OPT_ZEROES, false); |     s->read_zeroes = qemu_opt_get_bool(opts, NULL_OPT_ZEROES, false); | ||||||
|     qemu_opts_del(opts); |     qemu_opts_del(opts); | ||||||
|     bs->supported_write_flags = BDRV_REQ_FUA; |  | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void null_close(BlockDriverState *bs) | ||||||
|  | { | ||||||
|  | } | ||||||
|  |  | ||||||
| static int64_t null_getlength(BlockDriverState *bs) | static int64_t null_getlength(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     BDRVNullState *s = bs->opaque; |     BDRVNullState *s = bs->opaque; | ||||||
| @@ -113,22 +116,22 @@ static coroutine_fn int null_co_common(BlockDriverState *bs) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static coroutine_fn int null_co_preadv(BlockDriverState *bs, | static coroutine_fn int null_co_readv(BlockDriverState *bs, | ||||||
|                                        uint64_t offset, uint64_t bytes, |                                       int64_t sector_num, int nb_sectors, | ||||||
|                                        QEMUIOVector *qiov, int flags) |                                       QEMUIOVector *qiov) | ||||||
| { | { | ||||||
|     BDRVNullState *s = bs->opaque; |     BDRVNullState *s = bs->opaque; | ||||||
|  |  | ||||||
|     if (s->read_zeroes) { |     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); |     return null_co_common(bs); | ||||||
| } | } | ||||||
|  |  | ||||||
| static coroutine_fn int null_co_pwritev(BlockDriverState *bs, | static coroutine_fn int null_co_writev(BlockDriverState *bs, | ||||||
|                                         uint64_t offset, uint64_t bytes, |                                        int64_t sector_num, int nb_sectors, | ||||||
|                                         QEMUIOVector *qiov, int flags) |                                        QEMUIOVector *qiov) | ||||||
| { | { | ||||||
|     return null_co_common(bs); |     return null_co_common(bs); | ||||||
| } | } | ||||||
| @@ -183,24 +186,24 @@ static inline BlockAIOCB *null_aio_common(BlockDriverState *bs, | |||||||
|     return &acb->common; |     return &acb->common; | ||||||
| } | } | ||||||
|  |  | ||||||
| static BlockAIOCB *null_aio_preadv(BlockDriverState *bs, | static BlockAIOCB *null_aio_readv(BlockDriverState *bs, | ||||||
|                                    uint64_t offset, uint64_t bytes, |                                   int64_t sector_num, QEMUIOVector *qiov, | ||||||
|                                    QEMUIOVector *qiov, int flags, |                                   int nb_sectors, | ||||||
|                                   BlockCompletionFunc *cb, |                                   BlockCompletionFunc *cb, | ||||||
|                                   void *opaque) |                                   void *opaque) | ||||||
| { | { | ||||||
|     BDRVNullState *s = bs->opaque; |     BDRVNullState *s = bs->opaque; | ||||||
|  |  | ||||||
|     if (s->read_zeroes) { |     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); |     return null_aio_common(bs, cb, opaque); | ||||||
| } | } | ||||||
|  |  | ||||||
| static BlockAIOCB *null_aio_pwritev(BlockDriverState *bs, | static BlockAIOCB *null_aio_writev(BlockDriverState *bs, | ||||||
|                                     uint64_t offset, uint64_t bytes, |                                    int64_t sector_num, QEMUIOVector *qiov, | ||||||
|                                     QEMUIOVector *qiov, int flags, |                                    int nb_sectors, | ||||||
|                                    BlockCompletionFunc *cb, |                                    BlockCompletionFunc *cb, | ||||||
|                                    void *opaque) |                                    void *opaque) | ||||||
| { | { | ||||||
| @@ -241,6 +244,7 @@ static int coroutine_fn null_co_block_status(BlockDriverState *bs, | |||||||
|  |  | ||||||
| static void null_refresh_filename(BlockDriverState *bs, QDict *opts) | static void null_refresh_filename(BlockDriverState *bs, QDict *opts) | ||||||
| { | { | ||||||
|  |     QINCREF(opts); | ||||||
|     qdict_del(opts, "filename"); |     qdict_del(opts, "filename"); | ||||||
|  |  | ||||||
|     if (!qdict_size(opts)) { |     if (!qdict_size(opts)) { | ||||||
| @@ -249,7 +253,7 @@ static void null_refresh_filename(BlockDriverState *bs, QDict *opts) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     qdict_put_str(opts, "driver", bs->drv->format_name); |     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 = { | static BlockDriver bdrv_null_co = { | ||||||
| @@ -259,10 +263,11 @@ static BlockDriver bdrv_null_co = { | |||||||
|  |  | ||||||
|     .bdrv_file_open         = null_file_open, |     .bdrv_file_open         = null_file_open, | ||||||
|     .bdrv_parse_filename    = null_co_parse_filename, |     .bdrv_parse_filename    = null_co_parse_filename, | ||||||
|  |     .bdrv_close             = null_close, | ||||||
|     .bdrv_getlength         = null_getlength, |     .bdrv_getlength         = null_getlength, | ||||||
|  |  | ||||||
|     .bdrv_co_preadv         = null_co_preadv, |     .bdrv_co_readv          = null_co_readv, | ||||||
|     .bdrv_co_pwritev        = null_co_pwritev, |     .bdrv_co_writev         = null_co_writev, | ||||||
|     .bdrv_co_flush_to_disk  = null_co_flush, |     .bdrv_co_flush_to_disk  = null_co_flush, | ||||||
|     .bdrv_reopen_prepare    = null_reopen_prepare, |     .bdrv_reopen_prepare    = null_reopen_prepare, | ||||||
|  |  | ||||||
| @@ -278,10 +283,11 @@ static BlockDriver bdrv_null_aio = { | |||||||
|  |  | ||||||
|     .bdrv_file_open         = null_file_open, |     .bdrv_file_open         = null_file_open, | ||||||
|     .bdrv_parse_filename    = null_aio_parse_filename, |     .bdrv_parse_filename    = null_aio_parse_filename, | ||||||
|  |     .bdrv_close             = null_close, | ||||||
|     .bdrv_getlength         = null_getlength, |     .bdrv_getlength         = null_getlength, | ||||||
|  |  | ||||||
|     .bdrv_aio_preadv        = null_aio_preadv, |     .bdrv_aio_readv         = null_aio_readv, | ||||||
|     .bdrv_aio_pwritev       = null_aio_pwritev, |     .bdrv_aio_writev        = null_aio_writev, | ||||||
|     .bdrv_aio_flush         = null_aio_flush, |     .bdrv_aio_flush         = null_aio_flush, | ||||||
|     .bdrv_reopen_prepare    = null_reopen_prepare, |     .bdrv_reopen_prepare    = null_reopen_prepare, | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										78
									
								
								block/nvme.c
									
									
									
									
									
								
							
							
						
						
									
										78
									
								
								block/nvme.c
									
									
									
									
									
								
							| @@ -104,7 +104,7 @@ typedef struct { | |||||||
|     uint64_t nsze; /* Namespace size reported by identify command */ |     uint64_t nsze; /* Namespace size reported by identify command */ | ||||||
|     int nsid;      /* The namespace id to read/write data. */ |     int nsid;      /* The namespace id to read/write data. */ | ||||||
|     uint64_t max_transfer; |     uint64_t max_transfer; | ||||||
|     bool plugged; |     int plugged; | ||||||
|  |  | ||||||
|     CoMutex dma_map_lock; |     CoMutex dma_map_lock; | ||||||
|     CoQueue dma_flush_queue; |     CoQueue dma_flush_queue; | ||||||
| @@ -390,7 +390,6 @@ static void nvme_cmd_sync_cb(void *opaque, int ret) | |||||||
| { | { | ||||||
|     int *pret = opaque; |     int *pret = opaque; | ||||||
|     *pret = ret; |     *pret = ret; | ||||||
|     aio_wait_kick(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int nvme_cmd_sync(BlockDriverState *bs, NVMeQueuePair *q, | static int nvme_cmd_sync(BlockDriverState *bs, NVMeQueuePair *q, | ||||||
| @@ -490,8 +489,10 @@ static void nvme_handle_event(EventNotifier *n) | |||||||
|     BDRVNVMeState *s = container_of(n, BDRVNVMeState, irq_notifier); |     BDRVNVMeState *s = container_of(n, BDRVNVMeState, irq_notifier); | ||||||
|  |  | ||||||
|     trace_nvme_handle_event(s); |     trace_nvme_handle_event(s); | ||||||
|  |     aio_context_acquire(s->aio_context); | ||||||
|     event_notifier_test_and_clear(n); |     event_notifier_test_and_clear(n); | ||||||
|     nvme_poll_queues(s); |     nvme_poll_queues(s); | ||||||
|  |     aio_context_release(s->aio_context); | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool nvme_add_io_queue(BlockDriverState *bs, Error **errp) | static bool nvme_add_io_queue(BlockDriverState *bs, Error **errp) | ||||||
| @@ -568,13 +569,13 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, | |||||||
|     s->vfio = qemu_vfio_open_pci(device, errp); |     s->vfio = qemu_vfio_open_pci(device, errp); | ||||||
|     if (!s->vfio) { |     if (!s->vfio) { | ||||||
|         ret = -EINVAL; |         ret = -EINVAL; | ||||||
|         goto out; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     s->regs = qemu_vfio_pci_map_bar(s->vfio, 0, 0, NVME_BAR_SIZE, errp); |     s->regs = qemu_vfio_pci_map_bar(s->vfio, 0, 0, NVME_BAR_SIZE, errp); | ||||||
|     if (!s->regs) { |     if (!s->regs) { | ||||||
|         ret = -EINVAL; |         ret = -EINVAL; | ||||||
|         goto out; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Perform initialize sequence as described in NVMe spec "7.6.1 |     /* Perform initialize sequence as described in NVMe spec "7.6.1 | ||||||
| @@ -584,7 +585,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, | |||||||
|     if (!(cap & (1ULL << 37))) { |     if (!(cap & (1ULL << 37))) { | ||||||
|         error_setg(errp, "Device doesn't support NVMe command set"); |         error_setg(errp, "Device doesn't support NVMe command set"); | ||||||
|         ret = -EINVAL; |         ret = -EINVAL; | ||||||
|         goto out; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     s->page_size = MAX(4096, 1 << (12 + ((cap >> 48) & 0xF))); |     s->page_size = MAX(4096, 1 << (12 + ((cap >> 48) & 0xF))); | ||||||
| @@ -602,7 +603,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, | |||||||
|                              PRId64 " ms)", |                              PRId64 " ms)", | ||||||
|                        timeout_ms); |                        timeout_ms); | ||||||
|             ret = -ETIMEDOUT; |             ret = -ETIMEDOUT; | ||||||
|             goto out; |             goto fail; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -612,7 +613,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, | |||||||
|     s->queues[0] = nvme_create_queue_pair(bs, 0, NVME_QUEUE_SIZE, errp); |     s->queues[0] = nvme_create_queue_pair(bs, 0, NVME_QUEUE_SIZE, errp); | ||||||
|     if (!s->queues[0]) { |     if (!s->queues[0]) { | ||||||
|         ret = -EINVAL; |         ret = -EINVAL; | ||||||
|         goto out; |         goto fail; | ||||||
|     } |     } | ||||||
|     QEMU_BUILD_BUG_ON(NVME_QUEUE_SIZE & 0xF000); |     QEMU_BUILD_BUG_ON(NVME_QUEUE_SIZE & 0xF000); | ||||||
|     s->regs->aqa = cpu_to_le32((NVME_QUEUE_SIZE << 16) | NVME_QUEUE_SIZE); |     s->regs->aqa = cpu_to_le32((NVME_QUEUE_SIZE << 16) | NVME_QUEUE_SIZE); | ||||||
| @@ -632,14 +633,14 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, | |||||||
|                              PRId64 " ms)", |                              PRId64 " ms)", | ||||||
|                        timeout_ms); |                        timeout_ms); | ||||||
|             ret = -ETIMEDOUT; |             ret = -ETIMEDOUT; | ||||||
|             goto out; |             goto fail_queue; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ret = qemu_vfio_pci_init_irq(s->vfio, &s->irq_notifier, |     ret = qemu_vfio_pci_init_irq(s->vfio, &s->irq_notifier, | ||||||
|                                  VFIO_PCI_MSIX_IRQ_INDEX, errp); |                                  VFIO_PCI_MSIX_IRQ_INDEX, errp); | ||||||
|     if (ret) { |     if (ret) { | ||||||
|         goto out; |         goto fail_queue; | ||||||
|     } |     } | ||||||
|     aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier, |     aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier, | ||||||
|                            false, nvme_handle_event, nvme_poll_cb); |                            false, nvme_handle_event, nvme_poll_cb); | ||||||
| @@ -648,15 +649,30 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, | |||||||
|     if (local_err) { |     if (local_err) { | ||||||
|         error_propagate(errp, local_err); |         error_propagate(errp, local_err); | ||||||
|         ret = -EIO; |         ret = -EIO; | ||||||
|         goto out; |         goto fail_handler; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Set up command queues. */ |     /* Set up command queues. */ | ||||||
|     if (!nvme_add_io_queue(bs, errp)) { |     if (!nvme_add_io_queue(bs, errp)) { | ||||||
|         ret = -EIO; |         ret = -EIO; | ||||||
|  |         goto fail_handler; | ||||||
|     } |     } | ||||||
| out: |     return 0; | ||||||
|     /* Cleaning up is done in nvme_file_open() upon error. */ |  | ||||||
|  | fail_handler: | ||||||
|  |     aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier, | ||||||
|  |                            false, NULL, NULL); | ||||||
|  | fail_queue: | ||||||
|  |     nvme_free_queue_pair(bs, s->queues[0]); | ||||||
|  | fail: | ||||||
|  |     g_free(s->queues); | ||||||
|  |     if (s->regs) { | ||||||
|  |         qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)s->regs, 0, NVME_BAR_SIZE); | ||||||
|  |     } | ||||||
|  |     if (s->vfio) { | ||||||
|  |         qemu_vfio_close(s->vfio); | ||||||
|  |     } | ||||||
|  |     event_notifier_cleanup(&s->irq_notifier); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -679,11 +695,12 @@ static void nvme_parse_filename(const char *filename, QDict *options, | |||||||
|         unsigned long ns; |         unsigned long ns; | ||||||
|         const char *slash = strchr(tmp, '/'); |         const char *slash = strchr(tmp, '/'); | ||||||
|         if (!slash) { |         if (!slash) { | ||||||
|             qdict_put_str(options, NVME_BLOCK_OPT_DEVICE, tmp); |             qdict_put(options, NVME_BLOCK_OPT_DEVICE, | ||||||
|  |                       qstring_from_str(tmp)); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         device = g_strndup(tmp, slash - tmp); |         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); |         g_free(device); | ||||||
|         namespace = slash + 1; |         namespace = slash + 1; | ||||||
|         if (*namespace && qemu_strtoul(namespace, NULL, 10, &ns)) { |         if (*namespace && qemu_strtoul(namespace, NULL, 10, &ns)) { | ||||||
| @@ -691,8 +708,8 @@ static void nvme_parse_filename(const char *filename, QDict *options, | |||||||
|                        namespace); |                        namespace); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         qdict_put_str(options, NVME_BLOCK_OPT_NAMESPACE, |         qdict_put(options, NVME_BLOCK_OPT_NAMESPACE, | ||||||
|                       *namespace ? namespace : "1"); |                   qstring_from_str(*namespace ? namespace : "1")); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -723,10 +740,8 @@ static void nvme_close(BlockDriverState *bs) | |||||||
|     for (i = 0; i < s->nr_queues; ++i) { |     for (i = 0; i < s->nr_queues; ++i) { | ||||||
|         nvme_free_queue_pair(bs, s->queues[i]); |         nvme_free_queue_pair(bs, s->queues[i]); | ||||||
|     } |     } | ||||||
|     g_free(s->queues); |  | ||||||
|     aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier, |     aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier, | ||||||
|                            false, NULL, NULL); |                            false, NULL, NULL); | ||||||
|     event_notifier_cleanup(&s->irq_notifier); |  | ||||||
|     qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)s->regs, 0, NVME_BAR_SIZE); |     qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)s->regs, 0, NVME_BAR_SIZE); | ||||||
|     qemu_vfio_close(s->vfio); |     qemu_vfio_close(s->vfio); | ||||||
| } | } | ||||||
| @@ -838,7 +853,7 @@ try_map: | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         for (j = 0; j < qiov->iov[i].iov_len / s->page_size; j++) { |         for (j = 0; j < qiov->iov[i].iov_len / s->page_size; j++) { | ||||||
|             pagelist[entries++] = cpu_to_le64(iova + j * s->page_size); |             pagelist[entries++] = iova + j * s->page_size; | ||||||
|         } |         } | ||||||
|         trace_nvme_cmd_map_qiov_iov(s, i, qiov->iov[i].iov_base, |         trace_nvme_cmd_map_qiov_iov(s, i, qiov->iov[i].iov_base, | ||||||
|                                     qiov->iov[i].iov_len / s->page_size); |                                     qiov->iov[i].iov_len / s->page_size); | ||||||
| @@ -851,16 +866,20 @@ try_map: | |||||||
|     case 0: |     case 0: | ||||||
|         abort(); |         abort(); | ||||||
|     case 1: |     case 1: | ||||||
|         cmd->prp1 = pagelist[0]; |         cmd->prp1 = cpu_to_le64(pagelist[0]); | ||||||
|         cmd->prp2 = 0; |         cmd->prp2 = 0; | ||||||
|         break; |         break; | ||||||
|     case 2: |     case 2: | ||||||
|         cmd->prp1 = pagelist[0]; |         cmd->prp1 = cpu_to_le64(pagelist[0]); | ||||||
|         cmd->prp2 = pagelist[1]; |         cmd->prp2 = cpu_to_le64(pagelist[1]);; | ||||||
|         break; |         break; | ||||||
|     default: |     default: | ||||||
|         cmd->prp1 = pagelist[0]; |         cmd->prp1 = cpu_to_le64(pagelist[0]); | ||||||
|         cmd->prp2 = cpu_to_le64(req->prp_list_iova + sizeof(uint64_t)); |         cmd->prp2 = cpu_to_le64(req->prp_list_iova); | ||||||
|  |         for (i = 0; i < entries - 1; ++i) { | ||||||
|  |             pagelist[i] = cpu_to_le64(pagelist[i + 1]); | ||||||
|  |         } | ||||||
|  |         pagelist[entries - 1] = 0; | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     trace_nvme_cmd_map_qiov(s, cmd, req, qiov, entries); |     trace_nvme_cmd_map_qiov(s, cmd, req, qiov, entries); | ||||||
| @@ -1055,6 +1074,7 @@ static int nvme_reopen_prepare(BDRVReopenState *reopen_state, | |||||||
|  |  | ||||||
| static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts) | static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts) | ||||||
| { | { | ||||||
|  |     QINCREF(opts); | ||||||
|     qdict_del(opts, "filename"); |     qdict_del(opts, "filename"); | ||||||
|  |  | ||||||
|     if (!qdict_size(opts)) { |     if (!qdict_size(opts)) { | ||||||
| @@ -1062,8 +1082,8 @@ static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts) | |||||||
|                  bs->drv->format_name); |                  bs->drv->format_name); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     qdict_put_str(opts, "driver", bs->drv->format_name); |     qdict_put(opts, "driver", qstring_from_str(bs->drv->format_name)); | ||||||
|     bs->full_open_options = qobject_ref(opts); |     bs->full_open_options = opts; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void nvme_refresh_limits(BlockDriverState *bs, Error **errp) | static void nvme_refresh_limits(BlockDriverState *bs, Error **errp) | ||||||
| @@ -1096,8 +1116,7 @@ static void nvme_attach_aio_context(BlockDriverState *bs, | |||||||
| static void nvme_aio_plug(BlockDriverState *bs) | static void nvme_aio_plug(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     BDRVNVMeState *s = bs->opaque; |     BDRVNVMeState *s = bs->opaque; | ||||||
|     assert(!s->plugged); |     s->plugged++; | ||||||
|     s->plugged = true; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void nvme_aio_unplug(BlockDriverState *bs) | static void nvme_aio_unplug(BlockDriverState *bs) | ||||||
| @@ -1105,7 +1124,7 @@ static void nvme_aio_unplug(BlockDriverState *bs) | |||||||
|     int i; |     int i; | ||||||
|     BDRVNVMeState *s = bs->opaque; |     BDRVNVMeState *s = bs->opaque; | ||||||
|     assert(s->plugged); |     assert(s->plugged); | ||||||
|     s->plugged = false; |     if (!--s->plugged) { | ||||||
|         for (i = 1; i < s->nr_queues; i++) { |         for (i = 1; i < s->nr_queues; i++) { | ||||||
|             NVMeQueuePair *q = s->queues[i]; |             NVMeQueuePair *q = s->queues[i]; | ||||||
|             qemu_mutex_lock(&q->lock); |             qemu_mutex_lock(&q->lock); | ||||||
| @@ -1113,6 +1132,7 @@ static void nvme_aio_unplug(BlockDriverState *bs) | |||||||
|             nvme_process_completion(s, q); |             nvme_process_completion(s, q); | ||||||
|             qemu_mutex_unlock(&q->lock); |             qemu_mutex_unlock(&q->lock); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void nvme_register_buf(BlockDriverState *bs, void *host, size_t size) | static void nvme_register_buf(BlockDriverState *bs, void *host, size_t size) | ||||||
|   | |||||||
| @@ -31,13 +31,9 @@ | |||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "block/block_int.h" | #include "block/block_int.h" | ||||||
| #include "block/qdict.h" |  | ||||||
| #include "sysemu/block-backend.h" | #include "sysemu/block-backend.h" | ||||||
| #include "qemu/module.h" | #include "qemu/module.h" | ||||||
| #include "qemu/option.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/bswap.h" | ||||||
| #include "qemu/bitmap.h" | #include "qemu/bitmap.h" | ||||||
| #include "migration/blocker.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) | 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); |         qemu_iovec_init_external(&qiov, &iov, 1); | ||||||
|  |  | ||||||
|         ret = bdrv_co_preadv(bs->backing, idx * s->tracks * BDRV_SECTOR_SIZE, |         ret = bdrv_co_readv(bs->backing, idx * s->tracks, nb_cow_sectors, | ||||||
|                              nb_cow_bytes, &qiov, 0); |                             &qiov); | ||||||
|         if (ret < 0) { |         if (ret < 0) { | ||||||
|             qemu_vfree(iov.iov_base); |             qemu_vfree(iov.iov_base); | ||||||
|             return ret; |             return ret; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         ret = bdrv_co_pwritev(bs->file, s->data_end * BDRV_SECTOR_SIZE, |         ret = bdrv_co_writev(bs->file, s->data_end, nb_cow_sectors, &qiov); | ||||||
|                               nb_cow_bytes, &qiov, 0); |  | ||||||
|         qemu_vfree(iov.iov_base); |         qemu_vfree(iov.iov_base); | ||||||
|         if (ret < 0) { |         if (ret < 0) { | ||||||
|             return ret; |             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, | static coroutine_fn int parallels_co_writev(BlockDriverState *bs, | ||||||
|                                             int64_t sector_num, int nb_sectors, |         int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) | ||||||
|                                             QEMUIOVector *qiov, int flags) |  | ||||||
| { | { | ||||||
|     BDRVParallelsState *s = bs->opaque; |     BDRVParallelsState *s = bs->opaque; | ||||||
|     uint64_t bytes_done = 0; |     uint64_t bytes_done = 0; | ||||||
|     QEMUIOVector hd_qiov; |     QEMUIOVector hd_qiov; | ||||||
|     int ret = 0; |     int ret = 0; | ||||||
|  |  | ||||||
|     assert(!flags); |  | ||||||
|     qemu_iovec_init(&hd_qiov, qiov->niov); |     qemu_iovec_init(&hd_qiov, qiov->niov); | ||||||
|  |  | ||||||
|     while (nb_sectors > 0) { |     while (nb_sectors > 0) { | ||||||
| @@ -341,8 +315,7 @@ static coroutine_fn int parallels_co_writev(BlockDriverState *bs, | |||||||
|         qemu_iovec_reset(&hd_qiov); |         qemu_iovec_reset(&hd_qiov); | ||||||
|         qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes); |         qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes); | ||||||
|  |  | ||||||
|         ret = bdrv_co_pwritev(bs->file, position * BDRV_SECTOR_SIZE, nbytes, |         ret = bdrv_co_writev(bs->file, position, n, &hd_qiov); | ||||||
|                               &hd_qiov, 0); |  | ||||||
|         if (ret < 0) { |         if (ret < 0) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| @@ -381,8 +354,7 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs, | |||||||
|  |  | ||||||
|         if (position < 0) { |         if (position < 0) { | ||||||
|             if (bs->backing) { |             if (bs->backing) { | ||||||
|                 ret = bdrv_co_preadv(bs->backing, sector_num * BDRV_SECTOR_SIZE, |                 ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov); | ||||||
|                                      nbytes, &hd_qiov, 0); |  | ||||||
|                 if (ret < 0) { |                 if (ret < 0) { | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
| @@ -390,8 +362,7 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs, | |||||||
|                 qemu_iovec_memset(&hd_qiov, 0, 0, nbytes); |                 qemu_iovec_memset(&hd_qiov, 0, 0, nbytes); | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             ret = bdrv_co_preadv(bs->file, position * BDRV_SECTOR_SIZE, nbytes, |             ret = bdrv_co_readv(bs->file, position, n, &hd_qiov); | ||||||
|                                  &hd_qiov, 0); |  | ||||||
|             if (ret < 0) { |             if (ret < 0) { | ||||||
|                 break; |                 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) |                                                  Error **errp) | ||||||
| { | { | ||||||
|     BlockdevCreateOptionsParallels *parallels_opts; |  | ||||||
|     BlockDriverState *bs; |  | ||||||
|     BlockBackend *blk; |  | ||||||
|     int64_t total_size, cl_size; |     int64_t total_size, cl_size; | ||||||
|  |     uint8_t tmp[BDRV_SECTOR_SIZE]; | ||||||
|  |     Error *local_err = NULL; | ||||||
|  |     BlockBackend *file; | ||||||
|     uint32_t bat_entries, bat_sectors; |     uint32_t bat_entries, bat_sectors; | ||||||
|     ParallelsHeader header; |     ParallelsHeader header; | ||||||
|     uint8_t tmp[BDRV_SECTOR_SIZE]; |  | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     assert(opts->driver == BLOCKDEV_DRIVER_PARALLELS); |     total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), | ||||||
|     parallels_opts = &opts->u.parallels; |                           BDRV_SECTOR_SIZE); | ||||||
|  |     cl_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE, | ||||||
|     /* Sanity checks */ |                           DEFAULT_CLUSTER_SIZE), BDRV_SECTOR_SIZE); | ||||||
|     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; |  | ||||||
|     } |  | ||||||
|     if (total_size >= MAX_PARALLELS_IMAGE_FACTOR * cl_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; |         return -E2BIG; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!QEMU_IS_ALIGNED(total_size, BDRV_SECTOR_SIZE)) { |     ret = bdrv_create_file(filename, opts, &local_err); | ||||||
|         error_setg(errp, "Image size must be a multiple of 512 bytes"); |     if (ret < 0) { | ||||||
|         return -EINVAL; |         error_propagate(errp, local_err); | ||||||
|  |         return ret; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!QEMU_IS_ALIGNED(cl_size, BDRV_SECTOR_SIZE)) { |     file = blk_new_open(filename, NULL, NULL, | ||||||
|         error_setg(errp, "Cluster size must be a multiple of 512 bytes"); |                         BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, | ||||||
|         return -EINVAL; |                         &local_err); | ||||||
|     } |     if (file == NULL) { | ||||||
|  |         error_propagate(errp, local_err); | ||||||
|     /* Create BlockBackend to write to the image */ |  | ||||||
|     bs = bdrv_open_blockdev_ref(parallels_opts->file, errp); |  | ||||||
|     if (bs == NULL) { |  | ||||||
|         return -EIO; |         return -EIO; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); |     blk_set_allow_write_beyond_eof(file, true); | ||||||
|     ret = blk_insert_bs(blk, bs, errp); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|     blk_set_allow_write_beyond_eof(blk, true); |  | ||||||
|  |  | ||||||
|     /* Create image format */ |     ret = blk_truncate(file, 0, PREALLOC_MODE_OFF, errp); | ||||||
|     ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp); |  | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         goto out; |         goto exit; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bat_entries = DIV_ROUND_UP(total_size, cl_size); |     bat_entries = DIV_ROUND_UP(total_size, cl_size); | ||||||
| @@ -592,103 +542,24 @@ static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts, | |||||||
|     memset(tmp, 0, sizeof(tmp)); |     memset(tmp, 0, sizeof(tmp)); | ||||||
|     memcpy(tmp, &header, sizeof(header)); |     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) { |     if (ret < 0) { | ||||||
|         goto exit; |         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); |                             (bat_sectors - 1) << BDRV_SECTOR_BITS, 0); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         goto exit; |         goto exit; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ret = 0; |     ret = 0; | ||||||
| out: |  | ||||||
|     blk_unref(blk); | done: | ||||||
|     bdrv_unref(bs); |     blk_unref(file); | ||||||
|     return ret; |     return ret; | ||||||
|  |  | ||||||
| exit: | exit: | ||||||
|     error_setg_errno(errp, -ret, "Failed to create Parallels image"); |     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; |     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; |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -900,6 +771,25 @@ static void parallels_close(BlockDriverState *bs) | |||||||
|     error_free(s->migration_blocker); |     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 = { | static BlockDriver bdrv_parallels = { | ||||||
|     .format_name	= "parallels", |     .format_name	= "parallels", | ||||||
|     .instance_size	= sizeof(BDRVParallelsState), |     .instance_size	= sizeof(BDRVParallelsState), | ||||||
| @@ -913,7 +803,6 @@ static BlockDriver bdrv_parallels = { | |||||||
|     .bdrv_co_readv  = parallels_co_readv, |     .bdrv_co_readv  = parallels_co_readv, | ||||||
|     .bdrv_co_writev = parallels_co_writev, |     .bdrv_co_writev = parallels_co_writev, | ||||||
|     .supports_backing = true, |     .supports_backing = true, | ||||||
|     .bdrv_co_create      = parallels_co_create, |  | ||||||
|     .bdrv_co_create_opts = parallels_co_create_opts, |     .bdrv_co_create_opts = parallels_co_create_opts, | ||||||
|     .bdrv_co_check  = parallels_co_check, |     .bdrv_co_check  = parallels_co_check, | ||||||
|     .create_opts    = ¶llels_create_opts, |     .create_opts    = ¶llels_create_opts, | ||||||
|   | |||||||
							
								
								
									
										81
									
								
								block/qapi.c
									
									
									
									
									
								
							
							
						
						
									
										81
									
								
								block/qapi.c
									
									
									
									
									
								
							| @@ -282,12 +282,7 @@ void bdrv_query_image_info(BlockDriverState *bs, | |||||||
|         info->dirty_flag = bdi.is_dirty; |         info->dirty_flag = bdi.is_dirty; | ||||||
|         info->has_dirty_flag = true; |         info->has_dirty_flag = true; | ||||||
|     } |     } | ||||||
|     info->format_specific = bdrv_get_specific_info(bs, &err); |     info->format_specific     = bdrv_get_specific_info(bs); | ||||||
|     if (err) { |  | ||||||
|         error_propagate(errp, err); |  | ||||||
|         qapi_free_ImageInfo(info); |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|     info->has_format_specific = info->format_specific != NULL; |     info->has_format_specific = info->format_specific != NULL; | ||||||
|  |  | ||||||
|     backing_filename = bs->backing_file; |     backing_filename = bs->backing_file; | ||||||
| @@ -399,37 +394,6 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, | |||||||
|     qapi_free_BlockInfo(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) | static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) | ||||||
| { | { | ||||||
|     BlockAcctStats *stats = blk_get_stats(blk); |     BlockAcctStats *stats = blk_get_stats(blk); | ||||||
| @@ -495,16 +459,6 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) | |||||||
|         dev_stats->avg_wr_queue_depth = |         dev_stats->avg_wr_queue_depth = | ||||||
|             block_acct_queue_depth(ts, BLOCK_ACCT_WRITE); |             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, | static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs, | ||||||
| @@ -598,33 +552,18 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, | |||||||
|             p_next = &info->next; |             p_next = &info->next; | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) { |         for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { | ||||||
|             BlockStatsList *info; |             BlockStatsList *info = g_malloc0(sizeof(*info)); | ||||||
|             AioContext *ctx = blk_get_aio_context(blk); |             AioContext *ctx = blk_get_aio_context(blk); | ||||||
|             BlockStats *s; |             BlockStats *s; | ||||||
|             char *qdev; |  | ||||||
|  |  | ||||||
|             if (!*blk_name(blk) && !blk_get_attached_dev(blk)) { |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             aio_context_acquire(ctx); |             aio_context_acquire(ctx); | ||||||
|             s = bdrv_query_bds_stats(blk_bs(blk), true); |             s = bdrv_query_bds_stats(blk_bs(blk), true); | ||||||
|             s->has_device = true; |             s->has_device = true; | ||||||
|             s->device = g_strdup(blk_name(blk)); |             s->device = g_strdup(blk_name(blk)); | ||||||
|  |  | ||||||
|             qdev = blk_get_attached_dev_id(blk); |  | ||||||
|             if (qdev && *qdev) { |  | ||||||
|                 s->has_qdev = true; |  | ||||||
|                 s->qdev = qdev; |  | ||||||
|             } else { |  | ||||||
|                 g_free(qdev); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             bdrv_query_blk_stats(s->stats, blk); |             bdrv_query_blk_stats(s->stats, blk); | ||||||
|             aio_context_release(ctx); |             aio_context_release(ctx); | ||||||
|  |  | ||||||
|             info = g_malloc0(sizeof(*info)); |  | ||||||
|             info->value = s; |             info->value = s; | ||||||
|             *p_next = info; |             *p_next = info; | ||||||
|             p_next = &info->next; |             p_next = &info->next; | ||||||
| @@ -708,29 +647,29 @@ static void dump_qobject(fprintf_function func_fprintf, void *f, | |||||||
| { | { | ||||||
|     switch (qobject_type(obj)) { |     switch (qobject_type(obj)) { | ||||||
|         case QTYPE_QNUM: { |         case QTYPE_QNUM: { | ||||||
|             QNum *value = qobject_to(QNum, obj); |             QNum *value = qobject_to_qnum(obj); | ||||||
|             char *tmp = qnum_to_string(value); |             char *tmp = qnum_to_string(value); | ||||||
|             func_fprintf(f, "%s", tmp); |             func_fprintf(f, "%s", tmp); | ||||||
|             g_free(tmp); |             g_free(tmp); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         case QTYPE_QSTRING: { |         case QTYPE_QSTRING: { | ||||||
|             QString *value = qobject_to(QString, obj); |             QString *value = qobject_to_qstring(obj); | ||||||
|             func_fprintf(f, "%s", qstring_get_str(value)); |             func_fprintf(f, "%s", qstring_get_str(value)); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         case QTYPE_QDICT: { |         case QTYPE_QDICT: { | ||||||
|             QDict *value = qobject_to(QDict, obj); |             QDict *value = qobject_to_qdict(obj); | ||||||
|             dump_qdict(func_fprintf, f, comp_indent, value); |             dump_qdict(func_fprintf, f, comp_indent, value); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         case QTYPE_QLIST: { |         case QTYPE_QLIST: { | ||||||
|             QList *value = qobject_to(QList, obj); |             QList *value = qobject_to_qlist(obj); | ||||||
|             dump_qlist(func_fprintf, f, comp_indent, value); |             dump_qlist(func_fprintf, f, comp_indent, value); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         case QTYPE_QBOOL: { |         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"); |             func_fprintf(f, "%s", qbool_get_bool(value) ? "true" : "false"); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| @@ -791,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_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort); | ||||||
|     visit_complete(v, &obj); |     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); |     dump_qobject(func_fprintf, f, 1, data); | ||||||
|     qobject_unref(obj); |     qobject_decref(obj); | ||||||
|     visit_free(v); |     visit_free(v); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										355
									
								
								block/qcow.c
									
									
									
									
									
								
							
							
						
						
									
										355
									
								
								block/qcow.c
									
									
									
									
									
								
							| @@ -26,7 +26,6 @@ | |||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
| #include "block/block_int.h" | #include "block/block_int.h" | ||||||
| #include "block/qdict.h" |  | ||||||
| #include "sysemu/block-backend.h" | #include "sysemu/block-backend.h" | ||||||
| #include "qemu/module.h" | #include "qemu/module.h" | ||||||
| #include "qemu/option.h" | #include "qemu/option.h" | ||||||
| @@ -34,11 +33,9 @@ | |||||||
| #include <zlib.h> | #include <zlib.h> | ||||||
| #include "qapi/qmp/qdict.h" | #include "qapi/qmp/qdict.h" | ||||||
| #include "qapi/qmp/qstring.h" | #include "qapi/qmp/qstring.h" | ||||||
| #include "qapi/qobject-input-visitor.h" |  | ||||||
| #include "qapi/qapi-visit-block-core.h" |  | ||||||
| #include "crypto/block.h" | #include "crypto/block.h" | ||||||
| #include "migration/blocker.h" | #include "migration/blocker.h" | ||||||
| #include "crypto.h" | #include "block/crypto.h" | ||||||
|  |  | ||||||
| /**************************************************************/ | /**************************************************************/ | ||||||
| /* QEMU COW block driver with compression and encryption support */ | /* QEMU COW block driver with compression and encryption support */ | ||||||
| @@ -70,6 +67,7 @@ typedef struct QCowHeader { | |||||||
| typedef struct BDRVQcowState { | typedef struct BDRVQcowState { | ||||||
|     int cluster_bits; |     int cluster_bits; | ||||||
|     int cluster_size; |     int cluster_size; | ||||||
|  |     int cluster_sectors; | ||||||
|     int l2_bits; |     int l2_bits; | ||||||
|     int l2_size; |     int l2_size; | ||||||
|     unsigned int l1_size; |     unsigned int l1_size; | ||||||
| @@ -88,8 +86,6 @@ typedef struct BDRVQcowState { | |||||||
|     Error *migration_blocker; |     Error *migration_blocker; | ||||||
| } BDRVQcowState; | } BDRVQcowState; | ||||||
|  |  | ||||||
| static QemuOptsList qcow_create_opts; |  | ||||||
|  |  | ||||||
| static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); | static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); | ||||||
|  |  | ||||||
| static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) | static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||||
| @@ -140,14 +136,14 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|     header.magic = be32_to_cpu(header.magic); |     be32_to_cpus(&header.magic); | ||||||
|     header.version = be32_to_cpu(header.version); |     be32_to_cpus(&header.version); | ||||||
|     header.backing_file_offset = be64_to_cpu(header.backing_file_offset); |     be64_to_cpus(&header.backing_file_offset); | ||||||
|     header.backing_file_size = be32_to_cpu(header.backing_file_size); |     be32_to_cpus(&header.backing_file_size); | ||||||
|     header.mtime = be32_to_cpu(header.mtime); |     be32_to_cpus(&header.mtime); | ||||||
|     header.size = be64_to_cpu(header.size); |     be64_to_cpus(&header.size); | ||||||
|     header.crypt_method = be32_to_cpu(header.crypt_method); |     be32_to_cpus(&header.crypt_method); | ||||||
|     header.l1_table_offset = be64_to_cpu(header.l1_table_offset); |     be64_to_cpus(&header.l1_table_offset); | ||||||
|  |  | ||||||
|     if (header.magic != QCOW_MAGIC) { |     if (header.magic != QCOW_MAGIC) { | ||||||
|         error_setg(errp, "Image not in qcow format"); |         error_setg(errp, "Image not in qcow format"); | ||||||
| @@ -202,8 +198,9 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|                 ret = -EINVAL; |                 ret = -EINVAL; | ||||||
|                 goto fail; |                 goto fail; | ||||||
|             } |             } | ||||||
|             qdict_put_str(encryptopts, "format", "qcow"); |             qdict_del(encryptopts, "format"); | ||||||
|             crypto_opts = block_crypto_open_opts_init(encryptopts, errp); |             crypto_opts = block_crypto_open_opts_init( | ||||||
|  |                 Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp); | ||||||
|             if (!crypto_opts) { |             if (!crypto_opts) { | ||||||
|                 ret = -EINVAL; |                 ret = -EINVAL; | ||||||
|                 goto fail; |                 goto fail; | ||||||
| @@ -213,7 +210,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|                 cflags |= QCRYPTO_BLOCK_OPEN_NO_IO; |                 cflags |= QCRYPTO_BLOCK_OPEN_NO_IO; | ||||||
|             } |             } | ||||||
|             s->crypto = qcrypto_block_open(crypto_opts, "encrypt.", |             s->crypto = qcrypto_block_open(crypto_opts, "encrypt.", | ||||||
|                                            NULL, NULL, cflags, 1, errp); |                                            NULL, NULL, cflags, errp); | ||||||
|             if (!s->crypto) { |             if (!s->crypto) { | ||||||
|                 ret = -EINVAL; |                 ret = -EINVAL; | ||||||
|                 goto fail; |                 goto fail; | ||||||
| @@ -234,6 +231,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     } |     } | ||||||
|     s->cluster_bits = header.cluster_bits; |     s->cluster_bits = header.cluster_bits; | ||||||
|     s->cluster_size = 1 << s->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_bits = header.l2_bits; | ||||||
|     s->l2_size = 1 << s->l2_bits; |     s->l2_size = 1 << s->l2_bits; | ||||||
|     bs->total_sectors = header.size / 512; |     bs->total_sectors = header.size / 512; | ||||||
| @@ -270,7 +268,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     for(i = 0;i < s->l1_size; i++) { |     for(i = 0;i < s->l1_size; i++) { | ||||||
|         s->l1_table[i] = be64_to_cpu(s->l1_table[i]); |         be64_to_cpus(&s->l1_table[i]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* alloc L2 cache (max. 64k * 16 * 8 = 8 MB) */ |     /* alloc L2 cache (max. 64k * 16 * 8 = 8 MB) */ | ||||||
| @@ -313,7 +311,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     qobject_unref(encryptopts); |     QDECREF(encryptopts); | ||||||
|     qapi_free_QCryptoBlockOpenOptions(crypto_opts); |     qapi_free_QCryptoBlockOpenOptions(crypto_opts); | ||||||
|     qemu_co_mutex_init(&s->lock); |     qemu_co_mutex_init(&s->lock); | ||||||
|     return 0; |     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_cache); | ||||||
|     g_free(s->cluster_data); |     g_free(s->cluster_data); | ||||||
|     qcrypto_block_free(s->crypto); |     qcrypto_block_free(s->crypto); | ||||||
|     qobject_unref(encryptopts); |     QDECREF(encryptopts); | ||||||
|     qapi_free_QCryptoBlockOpenOptions(crypto_opts); |     qapi_free_QCryptoBlockOpenOptions(crypto_opts); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| @@ -343,8 +341,8 @@ static int qcow_reopen_prepare(BDRVReopenState *state, | |||||||
|  * |  * | ||||||
|  * 0 to not allocate. |  * 0 to not allocate. | ||||||
|  * |  * | ||||||
|  * 1 to allocate a normal cluster (for sector-aligned byte offsets 'n_start' |  * 1 to allocate a normal cluster (for sector indexes 'n_start' to | ||||||
|  * to 'n_end' within the cluster) |  * 'n_end') | ||||||
|  * |  * | ||||||
|  * 2 to allocate a compressed cluster of size |  * 2 to allocate a compressed cluster of size | ||||||
|  * 'compressed_size'. 'compressed_size' must be > 0 and < |  * 'compressed_size'. 'compressed_size' must be > 0 and < | ||||||
| @@ -438,10 +436,9 @@ static int get_cluster_offset(BlockDriverState *bs, | |||||||
|         if (!allocate) |         if (!allocate) | ||||||
|             return 0; |             return 0; | ||||||
|         BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC); |         BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC); | ||||||
|         assert(QEMU_IS_ALIGNED(n_start | n_end, BDRV_SECTOR_SIZE)); |  | ||||||
|         /* allocate a new cluster */ |         /* allocate a new cluster */ | ||||||
|         if ((cluster_offset & QCOW_OFLAG_COMPRESSED) && |         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 |             /* if the cluster is already compressed, we must | ||||||
|                decompress it in the case it is not completely |                decompress it in the case it is not completely | ||||||
|                overwritten */ |                overwritten */ | ||||||
| @@ -479,15 +476,16 @@ static int get_cluster_offset(BlockDriverState *bs, | |||||||
|                 /* if encrypted, we must initialize the cluster |                 /* if encrypted, we must initialize the cluster | ||||||
|                    content which won't be written */ |                    content which won't be written */ | ||||||
|                 if (bs->encrypted && |                 if (bs->encrypted && | ||||||
|                     (n_end - n_start) < s->cluster_size) { |                     (n_end - n_start) < s->cluster_sectors) { | ||||||
|                     uint64_t start_offset; |                     uint64_t start_sect; | ||||||
|                     assert(s->crypto); |                     assert(s->crypto); | ||||||
|                     start_offset = offset & ~(s->cluster_size - 1); |                     start_sect = (offset & ~(s->cluster_size - 1)) >> 9; | ||||||
|                     for (i = 0; i < s->cluster_size; i += BDRV_SECTOR_SIZE) { |                     for(i = 0; i < s->cluster_sectors; i++) { | ||||||
|                         if (i < n_start || i >= n_end) { |                         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, |                             if (qcrypto_block_encrypt(s->crypto, | ||||||
|                                                       start_offset + i, |                                                       (start_sect + i) * | ||||||
|  |                                                       BDRV_SECTOR_SIZE, | ||||||
|                                                       s->cluster_data, |                                                       s->cluster_data, | ||||||
|                                                       BDRV_SECTOR_SIZE, |                                                       BDRV_SECTOR_SIZE, | ||||||
|                                                       NULL) < 0) { |                                                       NULL) < 0) { | ||||||
| @@ -495,9 +493,8 @@ static int get_cluster_offset(BlockDriverState *bs, | |||||||
|                             } |                             } | ||||||
|                             BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); |                             BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); | ||||||
|                             ret = bdrv_pwrite(bs->file, |                             ret = bdrv_pwrite(bs->file, | ||||||
|                                               cluster_offset + i, |                                               cluster_offset + i * 512, | ||||||
|                                               s->cluster_data, |                                               s->cluster_data, 512); | ||||||
|                                               BDRV_SECTOR_SIZE); |  | ||||||
|                             if (ret < 0) { |                             if (ret < 0) { | ||||||
|                                 return ret; |                                 return ret; | ||||||
|                             } |                             } | ||||||
| @@ -611,21 +608,11 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void qcow_refresh_limits(BlockDriverState *bs, Error **errp) | static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, | ||||||
| { |                          int nb_sectors, QEMUIOVector *qiov) | ||||||
|     /* 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) |  | ||||||
| { | { | ||||||
|     BDRVQcowState *s = bs->opaque; |     BDRVQcowState *s = bs->opaque; | ||||||
|     int offset_in_cluster; |     int index_in_cluster; | ||||||
|     int ret = 0, n; |     int ret = 0, n; | ||||||
|     uint64_t cluster_offset; |     uint64_t cluster_offset; | ||||||
|     struct iovec hd_iov; |     struct iovec hd_iov; | ||||||
| @@ -633,7 +620,6 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset, | |||||||
|     uint8_t *buf; |     uint8_t *buf; | ||||||
|     void *orig_buf; |     void *orig_buf; | ||||||
|  |  | ||||||
|     assert(!flags); |  | ||||||
|     if (qiov->niov > 1) { |     if (qiov->niov > 1) { | ||||||
|         buf = orig_buf = qemu_try_blockalign(bs, qiov->size); |         buf = orig_buf = qemu_try_blockalign(bs, qiov->size); | ||||||
|         if (buf == NULL) { |         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); |     qemu_co_mutex_lock(&s->lock); | ||||||
|  |  | ||||||
|     while (bytes != 0) { |     while (nb_sectors != 0) { | ||||||
|         /* prepare next request */ |         /* 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) { |         if (ret < 0) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         offset_in_cluster = offset & (s->cluster_size - 1); |         index_in_cluster = sector_num & (s->cluster_sectors - 1); | ||||||
|         n = s->cluster_size - offset_in_cluster; |         n = s->cluster_sectors - index_in_cluster; | ||||||
|         if (n > bytes) { |         if (n > nb_sectors) { | ||||||
|             n = bytes; |             n = nb_sectors; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (!cluster_offset) { |         if (!cluster_offset) { | ||||||
|             if (bs->backing) { |             if (bs->backing) { | ||||||
|                 /* read from the base image */ |                 /* read from the base image */ | ||||||
|                 hd_iov.iov_base = (void *)buf; |                 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_iovec_init_external(&hd_qiov, &hd_iov, 1); | ||||||
|                 qemu_co_mutex_unlock(&s->lock); |                 qemu_co_mutex_unlock(&s->lock); | ||||||
|                 /* qcow2 emits this on bs->file instead of bs->backing */ |                 /* qcow2 emits this on bs->file instead of bs->backing */ | ||||||
|                 BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); |                 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); |                 qemu_co_mutex_lock(&s->lock); | ||||||
|                 if (ret < 0) { |                 if (ret < 0) { | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 /* Note: in this case, no need to wait */ |                 /* Note: in this case, no need to wait */ | ||||||
|                 memset(buf, 0, n); |                 memset(buf, 0, 512 * n); | ||||||
|             } |             } | ||||||
|         } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { |         } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { | ||||||
|             /* add AIO support for compressed blocks ? */ |             /* add AIO support for compressed blocks ? */ | ||||||
| @@ -682,19 +669,21 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset, | |||||||
|                 ret = -EIO; |                 ret = -EIO; | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|             memcpy(buf, s->cluster_cache + offset_in_cluster, n); |             memcpy(buf, | ||||||
|  |                    s->cluster_cache + index_in_cluster * 512, 512 * n); | ||||||
|         } else { |         } else { | ||||||
|             if ((cluster_offset & 511) != 0) { |             if ((cluster_offset & 511) != 0) { | ||||||
|                 ret = -EIO; |                 ret = -EIO; | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|             hd_iov.iov_base = (void *)buf; |             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_iovec_init_external(&hd_qiov, &hd_iov, 1); | ||||||
|             qemu_co_mutex_unlock(&s->lock); |             qemu_co_mutex_unlock(&s->lock); | ||||||
|             BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); |             BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); | ||||||
|             ret = bdrv_co_preadv(bs->file, cluster_offset + offset_in_cluster, |             ret = bdrv_co_readv(bs->file, | ||||||
|                                  n, &hd_qiov, 0); |                                 (cluster_offset >> 9) + index_in_cluster, | ||||||
|  |                                 n, &hd_qiov); | ||||||
|             qemu_co_mutex_lock(&s->lock); |             qemu_co_mutex_lock(&s->lock); | ||||||
|             if (ret < 0) { |             if (ret < 0) { | ||||||
|                 break; |                 break; | ||||||
| @@ -702,7 +691,8 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset, | |||||||
|             if (bs->encrypted) { |             if (bs->encrypted) { | ||||||
|                 assert(s->crypto); |                 assert(s->crypto); | ||||||
|                 if (qcrypto_block_decrypt(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; |                     ret = -EIO; | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
| @@ -710,9 +700,9 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset, | |||||||
|         } |         } | ||||||
|         ret = 0; |         ret = 0; | ||||||
|  |  | ||||||
|         bytes -= n; |         nb_sectors -= n; | ||||||
|         offset += n; |         sector_num += n; | ||||||
|         buf += n; |         buf += n * 512; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     qemu_co_mutex_unlock(&s->lock); |     qemu_co_mutex_unlock(&s->lock); | ||||||
| @@ -725,12 +715,11 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset, | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset, | static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, | ||||||
|                                         uint64_t bytes, QEMUIOVector *qiov, |                           int nb_sectors, QEMUIOVector *qiov) | ||||||
|                                         int flags) |  | ||||||
| { | { | ||||||
|     BDRVQcowState *s = bs->opaque; |     BDRVQcowState *s = bs->opaque; | ||||||
|     int offset_in_cluster; |     int index_in_cluster; | ||||||
|     uint64_t cluster_offset; |     uint64_t cluster_offset; | ||||||
|     int ret = 0, n; |     int ret = 0, n; | ||||||
|     struct iovec hd_iov; |     struct iovec hd_iov; | ||||||
| @@ -738,7 +727,6 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset, | |||||||
|     uint8_t *buf; |     uint8_t *buf; | ||||||
|     void *orig_buf; |     void *orig_buf; | ||||||
|  |  | ||||||
|     assert(!flags); |  | ||||||
|     s->cluster_cache_offset = -1; /* disable compressed cache */ |     s->cluster_cache_offset = -1; /* disable compressed cache */ | ||||||
|  |  | ||||||
|     /* We must always copy the iov when encrypting, so we |     /* 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); |     qemu_co_mutex_lock(&s->lock); | ||||||
|  |  | ||||||
|     while (bytes != 0) { |     while (nb_sectors != 0) { | ||||||
|         offset_in_cluster = offset & (s->cluster_size - 1); |  | ||||||
|         n = s->cluster_size - offset_in_cluster; |         index_in_cluster = sector_num & (s->cluster_sectors - 1); | ||||||
|         if (n > bytes) { |         n = s->cluster_sectors - index_in_cluster; | ||||||
|             n = bytes; |         if (n > nb_sectors) { | ||||||
|  |             n = nb_sectors; | ||||||
|         } |         } | ||||||
|         ret = get_cluster_offset(bs, offset, 1, 0, offset_in_cluster, |         ret = get_cluster_offset(bs, sector_num << 9, 1, 0, | ||||||
|                                  offset_in_cluster + n, &cluster_offset); |                                  index_in_cluster, | ||||||
|  |                                  index_in_cluster + n, &cluster_offset); | ||||||
|         if (ret < 0) { |         if (ret < 0) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| @@ -773,28 +763,30 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset, | |||||||
|         } |         } | ||||||
|         if (bs->encrypted) { |         if (bs->encrypted) { | ||||||
|             assert(s->crypto); |             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; |                 ret = -EIO; | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         hd_iov.iov_base = (void *)buf; |         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_iovec_init_external(&hd_qiov, &hd_iov, 1); | ||||||
|         qemu_co_mutex_unlock(&s->lock); |         qemu_co_mutex_unlock(&s->lock); | ||||||
|         BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); |         BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); | ||||||
|         ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster, |         ret = bdrv_co_writev(bs->file, | ||||||
|                               n, &hd_qiov, 0); |                              (cluster_offset >> 9) + index_in_cluster, | ||||||
|  |                              n, &hd_qiov); | ||||||
|         qemu_co_mutex_lock(&s->lock); |         qemu_co_mutex_lock(&s->lock); | ||||||
|         if (ret < 0) { |         if (ret < 0) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         ret = 0; |         ret = 0; | ||||||
|  |  | ||||||
|         bytes -= n; |         nb_sectors -= n; | ||||||
|         offset += n; |         sector_num += n; | ||||||
|         buf += n; |         buf += n * 512; | ||||||
|     } |     } | ||||||
|     qemu_co_mutex_unlock(&s->lock); |     qemu_co_mutex_unlock(&s->lock); | ||||||
|  |  | ||||||
| @@ -818,50 +810,62 @@ static void qcow_close(BlockDriverState *bs) | |||||||
|     error_free(s->migration_blocker); |     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) |                                             Error **errp) | ||||||
| { | { | ||||||
|     BlockdevCreateOptionsQcow *qcow_opts; |  | ||||||
|     int header_size, backing_filename_len, l1_size, shift, i; |     int header_size, backing_filename_len, l1_size, shift, i; | ||||||
|     QCowHeader header; |     QCowHeader header; | ||||||
|     uint8_t *tmp; |     uint8_t *tmp; | ||||||
|     int64_t total_size = 0; |     int64_t total_size = 0; | ||||||
|  |     char *backing_file = NULL; | ||||||
|  |     Error *local_err = NULL; | ||||||
|     int ret; |     int ret; | ||||||
|     BlockDriverState *bs; |  | ||||||
|     BlockBackend *qcow_blk; |     BlockBackend *qcow_blk; | ||||||
|  |     char *encryptfmt = NULL; | ||||||
|  |     QDict *options; | ||||||
|  |     QDict *encryptopts = NULL; | ||||||
|  |     QCryptoBlockCreateOptions *crypto_opts = NULL; | ||||||
|     QCryptoBlock *crypto = NULL; |     QCryptoBlock *crypto = NULL; | ||||||
|  |  | ||||||
|     assert(opts->driver == BLOCKDEV_DRIVER_QCOW); |     /* Read out options */ | ||||||
|     qcow_opts = &opts->u.qcow; |     total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), | ||||||
|  |                           BDRV_SECTOR_SIZE); | ||||||
|     /* Sanity checks */ |  | ||||||
|     total_size = qcow_opts->size; |  | ||||||
|     if (total_size == 0) { |     if (total_size == 0) { | ||||||
|         error_setg(errp, "Image size is too small, cannot be zero length"); |         error_setg(errp, "Image size is too small, cannot be zero length"); | ||||||
|         return -EINVAL; |         ret = -EINVAL; | ||||||
|  |         goto cleanup; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (qcow_opts->has_encrypt && |     backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); | ||||||
|         qcow_opts->encrypt->format != Q_CRYPTO_BLOCK_FORMAT_QCOW) |     encryptfmt = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT); | ||||||
|     { |     if (encryptfmt) { | ||||||
|         error_setg(errp, "Unsupported encryption format"); |         if (qemu_opt_get(opts, BLOCK_OPT_ENCRYPT)) { | ||||||
|         return -EINVAL; |             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 */ |     ret = bdrv_create_file(filename, opts, &local_err); | ||||||
|     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); |  | ||||||
|     if (ret < 0) { |     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); |     blk_set_allow_write_beyond_eof(qcow_blk, true); | ||||||
|  |  | ||||||
|     /* Create image format */ |  | ||||||
|     ret = blk_truncate(qcow_blk, 0, PREALLOC_MODE_OFF, errp); |     ret = blk_truncate(qcow_blk, 0, PREALLOC_MODE_OFF, errp); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         goto exit; |         goto exit; | ||||||
| @@ -873,15 +877,16 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, | |||||||
|     header.size = cpu_to_be64(total_size); |     header.size = cpu_to_be64(total_size); | ||||||
|     header_size = sizeof(header); |     header_size = sizeof(header); | ||||||
|     backing_filename_len = 0; |     backing_filename_len = 0; | ||||||
|     if (qcow_opts->has_backing_file) { |     if (backing_file) { | ||||||
|         if (strcmp(qcow_opts->backing_file, "fat:")) { |         if (strcmp(backing_file, "fat:")) { | ||||||
|             header.backing_file_offset = cpu_to_be64(header_size); |             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.backing_file_size = cpu_to_be32(backing_filename_len); | ||||||
|             header_size += backing_filename_len; |             header_size += backing_filename_len; | ||||||
|         } else { |         } else { | ||||||
|             /* special backing file for vvfat */ |             /* 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 |         header.cluster_bits = 9; /* 512 byte cluster to avoid copying | ||||||
|                                     unmodified sectors */ |                                     unmodified sectors */ | ||||||
| @@ -896,10 +901,26 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, | |||||||
|  |  | ||||||
|     header.l1_table_offset = cpu_to_be64(header_size); |     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); |         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); |                                       NULL, NULL, NULL, errp); | ||||||
|         if (!crypto) { |         if (!crypto) { | ||||||
|             ret = -EINVAL; |             ret = -EINVAL; | ||||||
| @@ -915,9 +936,9 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, | |||||||
|         goto exit; |         goto exit; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (qcow_opts->has_backing_file) { |     if (backing_file) { | ||||||
|         ret = blk_pwrite(qcow_blk, sizeof(header), |         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) { |         if (ret != backing_filename_len) { | ||||||
|             goto exit; |             goto exit; | ||||||
|         } |         } | ||||||
| @@ -938,97 +959,12 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, | |||||||
|     ret = 0; |     ret = 0; | ||||||
| exit: | exit: | ||||||
|     blk_unref(qcow_blk); |     blk_unref(qcow_blk); | ||||||
|     bdrv_unref(bs); | cleanup: | ||||||
|  |     QDECREF(encryptopts); | ||||||
|  |     g_free(encryptfmt); | ||||||
|     qcrypto_block_free(crypto); |     qcrypto_block_free(crypto); | ||||||
|     return ret; |     qapi_free_QCryptoBlockCreateOptions(crypto_opts); | ||||||
| } |     g_free(backing_file); | ||||||
|  |  | ||||||
| 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); |  | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1110,7 +1046,8 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, | |||||||
|  |  | ||||||
|     if (ret != Z_STREAM_END || out_len >= s->cluster_size) { |     if (ret != Z_STREAM_END || out_len >= s->cluster_size) { | ||||||
|         /* could not compress: write normal cluster */ |         /* 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) { |         if (ret < 0) { | ||||||
|             goto fail; |             goto fail; | ||||||
|         } |         } | ||||||
| @@ -1191,14 +1128,12 @@ static BlockDriver bdrv_qcow = { | |||||||
|     .bdrv_close		= qcow_close, |     .bdrv_close		= qcow_close, | ||||||
|     .bdrv_child_perm        = bdrv_format_default_perms, |     .bdrv_child_perm        = bdrv_format_default_perms, | ||||||
|     .bdrv_reopen_prepare    = qcow_reopen_prepare, |     .bdrv_reopen_prepare    = qcow_reopen_prepare, | ||||||
|     .bdrv_co_create         = qcow_co_create, |  | ||||||
|     .bdrv_co_create_opts    = qcow_co_create_opts, |     .bdrv_co_create_opts    = qcow_co_create_opts, | ||||||
|     .bdrv_has_zero_init     = bdrv_has_zero_init_1, |     .bdrv_has_zero_init     = bdrv_has_zero_init_1, | ||||||
|     .supports_backing       = true, |     .supports_backing       = true, | ||||||
|     .bdrv_refresh_limits    = qcow_refresh_limits, |  | ||||||
|  |  | ||||||
|     .bdrv_co_preadv         = qcow_co_preadv, |     .bdrv_co_readv          = qcow_co_readv, | ||||||
|     .bdrv_co_pwritev        = qcow_co_pwritev, |     .bdrv_co_writev         = qcow_co_writev, | ||||||
|     .bdrv_co_block_status   = qcow_co_block_status, |     .bdrv_co_block_status   = qcow_co_block_status, | ||||||
|  |  | ||||||
|     .bdrv_make_empty        = qcow_make_empty, |     .bdrv_make_empty        = qcow_make_empty, | ||||||
|   | |||||||
| @@ -30,7 +30,7 @@ | |||||||
| #include "qemu/cutils.h" | #include "qemu/cutils.h" | ||||||
|  |  | ||||||
| #include "block/block_int.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 | /* NOTICE: BME here means Bitmaps Extension and used as a namespace for | ||||||
|  * _internal_ constants. Please do not use this _internal_ abbreviation for |  * _internal_ constants. Please do not use this _internal_ abbreviation for | ||||||
| @@ -77,6 +77,8 @@ typedef struct Qcow2BitmapTable { | |||||||
|     uint32_t size; /* number of 64bit entries */ |     uint32_t size; /* number of 64bit entries */ | ||||||
|     QSIMPLEQ_ENTRY(Qcow2BitmapTable) entry; |     QSIMPLEQ_ENTRY(Qcow2BitmapTable) entry; | ||||||
| } Qcow2BitmapTable; | } Qcow2BitmapTable; | ||||||
|  | typedef QSIMPLEQ_HEAD(Qcow2BitmapTableList, Qcow2BitmapTable) | ||||||
|  |     Qcow2BitmapTableList; | ||||||
|  |  | ||||||
| typedef struct Qcow2Bitmap { | typedef struct Qcow2Bitmap { | ||||||
|     Qcow2BitmapTable table; |     Qcow2BitmapTable table; | ||||||
| @@ -116,7 +118,7 @@ static inline void bitmap_table_to_be(uint64_t *bitmap_table, size_t size) | |||||||
|     size_t i; |     size_t i; | ||||||
|  |  | ||||||
|     for (i = 0; i < size; ++i) { |     for (i = 0; i < size; ++i) { | ||||||
|         bitmap_table[i] = cpu_to_be64(bitmap_table[i]); |         cpu_to_be64s(&bitmap_table[i]); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -229,7 +231,7 @@ static int bitmap_table_load(BlockDriverState *bs, Qcow2BitmapTable *tb, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     for (i = 0; i < tb->size; ++i) { |     for (i = 0; i < tb->size; ++i) { | ||||||
|         table[i] = be64_to_cpu(table[i]); |         be64_to_cpus(&table[i]); | ||||||
|         ret = check_table_entry(table[i], s->cluster_size); |         ret = check_table_entry(table[i], s->cluster_size); | ||||||
|         if (ret < 0) { |         if (ret < 0) { | ||||||
|             goto fail; |             goto fail; | ||||||
| @@ -252,6 +254,7 @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb) | |||||||
|  |  | ||||||
|     ret = bitmap_table_load(bs, tb, &bitmap_table); |     ret = bitmap_table_load(bs, tb, &bitmap_table); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|  |         assert(bitmap_table == NULL); | ||||||
|         return ret; |         return ret; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -392,20 +395,20 @@ fail: | |||||||
|  |  | ||||||
| static inline void bitmap_dir_entry_to_cpu(Qcow2BitmapDirEntry *entry) | static inline void bitmap_dir_entry_to_cpu(Qcow2BitmapDirEntry *entry) | ||||||
| { | { | ||||||
|     entry->bitmap_table_offset = be64_to_cpu(entry->bitmap_table_offset); |     be64_to_cpus(&entry->bitmap_table_offset); | ||||||
|     entry->bitmap_table_size = be32_to_cpu(entry->bitmap_table_size); |     be32_to_cpus(&entry->bitmap_table_size); | ||||||
|     entry->flags = be32_to_cpu(entry->flags); |     be32_to_cpus(&entry->flags); | ||||||
|     entry->name_size = be16_to_cpu(entry->name_size); |     be16_to_cpus(&entry->name_size); | ||||||
|     entry->extra_data_size = be32_to_cpu(entry->extra_data_size); |     be32_to_cpus(&entry->extra_data_size); | ||||||
| } | } | ||||||
|  |  | ||||||
| static inline void bitmap_dir_entry_to_be(Qcow2BitmapDirEntry *entry) | static inline void bitmap_dir_entry_to_be(Qcow2BitmapDirEntry *entry) | ||||||
| { | { | ||||||
|     entry->bitmap_table_offset = cpu_to_be64(entry->bitmap_table_offset); |     cpu_to_be64s(&entry->bitmap_table_offset); | ||||||
|     entry->bitmap_table_size = cpu_to_be32(entry->bitmap_table_size); |     cpu_to_be32s(&entry->bitmap_table_size); | ||||||
|     entry->flags = cpu_to_be32(entry->flags); |     cpu_to_be32s(&entry->flags); | ||||||
|     entry->name_size = cpu_to_be16(entry->name_size); |     cpu_to_be16s(&entry->name_size); | ||||||
|     entry->extra_data_size = cpu_to_be32(entry->extra_data_size); |     cpu_to_be32s(&entry->extra_data_size); | ||||||
| } | } | ||||||
|  |  | ||||||
| static inline int calc_dir_entry_size(size_t name_size, size_t extra_data_size) | static inline int calc_dir_entry_size(size_t name_size, size_t extra_data_size) | ||||||
| @@ -773,12 +776,7 @@ static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list, | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Actually, even in in-place case ignoring QCOW2_OL_BITMAP_DIRECTORY is not |     ret = qcow2_pre_write_overlap_check(bs, 0, dir_offset, dir_size); | ||||||
|      * 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); |  | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
| @@ -1006,84 +1004,7 @@ fail: | |||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp) | ||||||
| static Qcow2BitmapInfoFlagsList *get_bitmap_info_flags(uint32_t flags) |  | ||||||
| { |  | ||||||
|     Qcow2BitmapInfoFlagsList *list = NULL; |  | ||||||
|     Qcow2BitmapInfoFlagsList **plist = &list; |  | ||||||
|     int i; |  | ||||||
|  |  | ||||||
|     static const struct { |  | ||||||
|         int bme;  /* Bitmap directory entry flags */ |  | ||||||
|         int info; /* The flags to report to the user */ |  | ||||||
|     } map[] = { |  | ||||||
|         { BME_FLAG_IN_USE, QCOW2_BITMAP_INFO_FLAGS_IN_USE }, |  | ||||||
|         { BME_FLAG_AUTO,   QCOW2_BITMAP_INFO_FLAGS_AUTO }, |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     int map_size = ARRAY_SIZE(map); |  | ||||||
|  |  | ||||||
|     for (i = 0; i < map_size; ++i) { |  | ||||||
|         if (flags & map[i].bme) { |  | ||||||
|             Qcow2BitmapInfoFlagsList *entry = |  | ||||||
|                 g_new0(Qcow2BitmapInfoFlagsList, 1); |  | ||||||
|             entry->value = map[i].info; |  | ||||||
|             *plist = entry; |  | ||||||
|             plist = &entry->next; |  | ||||||
|             flags &= ~map[i].bme; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     /* Check if the BME_* mapping above is complete */ |  | ||||||
|     assert(!flags); |  | ||||||
|  |  | ||||||
|     return list; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * qcow2_get_bitmap_info_list() |  | ||||||
|  * Returns a list of QCOW2 bitmap details. |  | ||||||
|  * In case of no bitmaps, the function returns NULL and |  | ||||||
|  * the @errp parameter is not set. |  | ||||||
|  * When bitmap information can not be obtained, the function returns |  | ||||||
|  * NULL and the @errp parameter is set. |  | ||||||
|  */ |  | ||||||
| Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs, |  | ||||||
|                                                 Error **errp) |  | ||||||
| { |  | ||||||
|     BDRVQcow2State *s = bs->opaque; |  | ||||||
|     Qcow2BitmapList *bm_list; |  | ||||||
|     Qcow2Bitmap *bm; |  | ||||||
|     Qcow2BitmapInfoList *list = NULL; |  | ||||||
|     Qcow2BitmapInfoList **plist = &list; |  | ||||||
|  |  | ||||||
|     if (s->nb_bitmaps == 0) { |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, |  | ||||||
|                                s->bitmap_directory_size, errp); |  | ||||||
|     if (bm_list == NULL) { |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     QSIMPLEQ_FOREACH(bm, bm_list, entry) { |  | ||||||
|         Qcow2BitmapInfo *info = g_new0(Qcow2BitmapInfo, 1); |  | ||||||
|         Qcow2BitmapInfoList *obj = g_new0(Qcow2BitmapInfoList, 1); |  | ||||||
|         info->granularity = 1U << bm->granularity_bits; |  | ||||||
|         info->name = g_strdup(bm->name); |  | ||||||
|         info->flags = get_bitmap_info_flags(bm->flags & ~BME_RESERVED_FLAGS); |  | ||||||
|         obj->value = info; |  | ||||||
|         *plist = obj; |  | ||||||
|         plist = &obj->next; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bitmap_list_free(bm_list); |  | ||||||
|  |  | ||||||
|     return list; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated, |  | ||||||
|                                  Error **errp) |  | ||||||
| { | { | ||||||
|     BDRVQcow2State *s = bs->opaque; |     BDRVQcow2State *s = bs->opaque; | ||||||
|     Qcow2BitmapList *bm_list; |     Qcow2BitmapList *bm_list; | ||||||
| @@ -1091,10 +1012,6 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated, | |||||||
|     GSList *ro_dirty_bitmaps = NULL; |     GSList *ro_dirty_bitmaps = NULL; | ||||||
|     int ret = 0; |     int ret = 0; | ||||||
|  |  | ||||||
|     if (header_updated != NULL) { |  | ||||||
|         *header_updated = false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (s->nb_bitmaps == 0) { |     if (s->nb_bitmaps == 0) { | ||||||
|         /* No bitmaps - nothing to do */ |         /* No bitmaps - nothing to do */ | ||||||
|         return 0; |         return 0; | ||||||
| @@ -1138,9 +1055,6 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated, | |||||||
|             error_setg_errno(errp, -ret, "Can't update bitmap directory"); |             error_setg_errno(errp, -ret, "Can't update bitmap directory"); | ||||||
|             goto out; |             goto out; | ||||||
|         } |         } | ||||||
|         if (header_updated != NULL) { |  | ||||||
|             *header_updated = true; |  | ||||||
|         } |  | ||||||
|         g_slist_foreach(ro_dirty_bitmaps, set_readonly_helper, false); |         g_slist_foreach(ro_dirty_bitmaps, set_readonly_helper, false); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -1151,11 +1065,6 @@ out: | |||||||
|     return ret; |     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_data() | ||||||
|  * Store bitmap to image, filling bitmap table accordingly. |  * Store bitmap to image, filling bitmap table accordingly. | ||||||
|  */ |  */ | ||||||
| @@ -1390,7 +1299,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp) | |||||||
|     int ret; |     int ret; | ||||||
|     Qcow2BitmapList *bm_list; |     Qcow2BitmapList *bm_list; | ||||||
|     Qcow2Bitmap *bm; |     Qcow2Bitmap *bm; | ||||||
|     QSIMPLEQ_HEAD(, Qcow2BitmapTable) drop_tables; |     Qcow2BitmapTableList drop_tables; | ||||||
|     Qcow2BitmapTable *tb, *tb_next; |     Qcow2BitmapTable *tb, *tb_next; | ||||||
|  |  | ||||||
|     if (!bdrv_has_changed_persistent_bitmaps(bs)) { |     if (!bdrv_has_changed_persistent_bitmaps(bs)) { | ||||||
| @@ -1492,22 +1401,6 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp) | |||||||
|         g_free(tb); |         g_free(tb); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     QSIMPLEQ_FOREACH(bm, bm_list, entry) { |  | ||||||
|         /* For safety, we remove bitmap after storing. |  | ||||||
|          * We may be here in two cases: |  | ||||||
|          * 1. bdrv_close. It's ok to drop bitmap. |  | ||||||
|          * 2. inactivation. It means migration without 'dirty-bitmaps' |  | ||||||
|          *    capability, so bitmaps are not marked with |  | ||||||
|          *    BdrvDirtyBitmap.migration flags. It's not bad to drop them too, |  | ||||||
|          *    and reload on invalidation. |  | ||||||
|          */ |  | ||||||
|         if (bm->dirty_bitmap == NULL) { |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         bdrv_release_dirty_bitmap(bs, bm->dirty_bitmap); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bitmap_list_free(bm_list); |     bitmap_list_free(bm_list); | ||||||
|     return; |     return; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -28,7 +28,7 @@ | |||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qemu-common.h" | #include "qemu-common.h" | ||||||
| #include "block/block_int.h" | #include "block/block_int.h" | ||||||
| #include "qcow2.h" | #include "block/qcow2.h" | ||||||
| #include "qemu/bswap.h" | #include "qemu/bswap.h" | ||||||
| #include "trace.h" | #include "trace.h" | ||||||
|  |  | ||||||
| @@ -994,17 +994,6 @@ err: | |||||||
|     return ret; |     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 |  * 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, |  * write, but require COW to be performed (this includes yet unallocated space, | ||||||
| @@ -1571,6 +1560,76 @@ again: | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int decompress_buffer(uint8_t *out_buf, int out_buf_size, | ||||||
|  |                              const uint8_t *buf, int buf_size) | ||||||
|  | { | ||||||
|  |     z_stream strm1, *strm = &strm1; | ||||||
|  |     int ret, out_len; | ||||||
|  |  | ||||||
|  |     memset(strm, 0, sizeof(*strm)); | ||||||
|  |  | ||||||
|  |     strm->next_in = (uint8_t *)buf; | ||||||
|  |     strm->avail_in = buf_size; | ||||||
|  |     strm->next_out = out_buf; | ||||||
|  |     strm->avail_out = out_buf_size; | ||||||
|  |  | ||||||
|  |     ret = inflateInit2(strm, -12); | ||||||
|  |     if (ret != Z_OK) | ||||||
|  |         return -1; | ||||||
|  |     ret = inflate(strm, Z_FINISH); | ||||||
|  |     out_len = strm->next_out - out_buf; | ||||||
|  |     if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) || | ||||||
|  |         out_len != out_buf_size) { | ||||||
|  |         inflateEnd(strm); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     inflateEnd(strm); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) | ||||||
|  | { | ||||||
|  |     BDRVQcow2State *s = bs->opaque; | ||||||
|  |     int ret, csize, nb_csectors, sector_offset; | ||||||
|  |     uint64_t coffset; | ||||||
|  |  | ||||||
|  |     coffset = cluster_offset & s->cluster_offset_mask; | ||||||
|  |     if (s->cluster_cache_offset != coffset) { | ||||||
|  |         nb_csectors = ((cluster_offset >> s->csize_shift) & s->csize_mask) + 1; | ||||||
|  |         sector_offset = coffset & 511; | ||||||
|  |         csize = nb_csectors * 512 - sector_offset; | ||||||
|  |  | ||||||
|  |         /* Allocate buffers on first decompress operation, most images are | ||||||
|  |          * uncompressed and the memory overhead can be avoided.  The buffers | ||||||
|  |          * are freed in .bdrv_close(). | ||||||
|  |          */ | ||||||
|  |         if (!s->cluster_data) { | ||||||
|  |             /* one more sector for decompressed data alignment */ | ||||||
|  |             s->cluster_data = qemu_try_blockalign(bs->file->bs, | ||||||
|  |                     QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size + 512); | ||||||
|  |             if (!s->cluster_data) { | ||||||
|  |                 return -ENOMEM; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (!s->cluster_cache) { | ||||||
|  |             s->cluster_cache = g_malloc(s->cluster_size); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED); | ||||||
|  |         ret = bdrv_read(bs->file, coffset >> 9, s->cluster_data, | ||||||
|  |                         nb_csectors); | ||||||
|  |         if (ret < 0) { | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |         if (decompress_buffer(s->cluster_cache, s->cluster_size, | ||||||
|  |                               s->cluster_data + sector_offset, csize) < 0) { | ||||||
|  |             return -EIO; | ||||||
|  |         } | ||||||
|  |         s->cluster_cache_offset = coffset; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * This discards as many clusters of nb_clusters as possible at once (i.e. |  * This discards as many clusters of nb_clusters as possible at once (i.e. | ||||||
|  * all clusters in the same L2 slice) and returns the number of discarded |  * all clusters in the same L2 slice) and returns the number of discarded | ||||||
|   | |||||||
| @@ -26,13 +26,12 @@ | |||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qemu-common.h" | #include "qemu-common.h" | ||||||
| #include "block/block_int.h" | #include "block/block_int.h" | ||||||
| #include "qcow2.h" | #include "block/qcow2.h" | ||||||
| #include "qemu/range.h" | #include "qemu/range.h" | ||||||
| #include "qemu/bswap.h" | #include "qemu/bswap.h" | ||||||
| #include "qemu/cutils.h" | #include "qemu/cutils.h" | ||||||
|  |  | ||||||
| static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size, | static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size); | ||||||
|                                     uint64_t max); |  | ||||||
| static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, | static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, | ||||||
|                             int64_t offset, int64_t length, uint64_t addend, |                             int64_t offset, int64_t length, uint64_t addend, | ||||||
|                             bool decrease, enum qcow2_discard_type type); |                             bool decrease, enum qcow2_discard_type type); | ||||||
| @@ -363,14 +362,11 @@ static int alloc_refcount_block(BlockDriverState *bs, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Allocate the refcount block itself and mark it as used */ |     /* Allocate the refcount block itself and mark it as used */ | ||||||
|     int64_t new_block = alloc_clusters_noref(bs, s->cluster_size, INT64_MAX); |     int64_t new_block = alloc_clusters_noref(bs, s->cluster_size); | ||||||
|     if (new_block < 0) { |     if (new_block < 0) { | ||||||
|         return new_block; |         return new_block; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* The offset must fit in the offset field of the refcount table entry */ |  | ||||||
|     assert((new_block & REFT_OFFSET_MASK) == new_block); |  | ||||||
|  |  | ||||||
|     /* If we're allocating the block at offset 0 then something is wrong */ |     /* If we're allocating the block at offset 0 then something is wrong */ | ||||||
|     if (new_block == 0) { |     if (new_block == 0) { | ||||||
|         qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid " |         qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid " | ||||||
| @@ -738,7 +734,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret) | |||||||
|  |  | ||||||
|         /* Discard is optional, ignore the return value */ |         /* Discard is optional, ignore the return value */ | ||||||
|         if (ret >= 0) { |         if (ret >= 0) { | ||||||
|             bdrv_pdiscard(bs->file, d->offset, d->bytes); |             bdrv_pdiscard(bs->file->bs, d->offset, d->bytes); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         g_free(d); |         g_free(d); | ||||||
| @@ -843,13 +839,6 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, | |||||||
|                 qcow2_cache_put(s->refcount_block_cache, &refcount_block); |                 qcow2_cache_put(s->refcount_block_cache, &refcount_block); | ||||||
|             } |             } | ||||||
|             ret = alloc_refcount_block(bs, cluster_index, &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) { |             if (ret < 0) { | ||||||
|                 goto fail; |                 goto fail; | ||||||
|             } |             } | ||||||
| @@ -958,8 +947,7 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, | |||||||
|  |  | ||||||
|  |  | ||||||
| /* return < 0 if error */ | /* return < 0 if error */ | ||||||
| static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size, | static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size) | ||||||
|                                     uint64_t max) |  | ||||||
| { | { | ||||||
|     BDRVQcow2State *s = bs->opaque; |     BDRVQcow2State *s = bs->opaque; | ||||||
|     uint64_t i, nb_clusters, refcount; |     uint64_t i, nb_clusters, refcount; | ||||||
| @@ -984,9 +972,9 @@ retry: | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Make sure that all offsets in the "allocated" range are representable |     /* Make sure that all offsets in the "allocated" range are representable | ||||||
|      * in the requested max */ |      * in an int64_t */ | ||||||
|     if (s->free_cluster_index > 0 && |     if (s->free_cluster_index > 0 && | ||||||
|         s->free_cluster_index - 1 > (max >> s->cluster_bits)) |         s->free_cluster_index - 1 > (INT64_MAX >> s->cluster_bits)) | ||||||
|     { |     { | ||||||
|         return -EFBIG; |         return -EFBIG; | ||||||
|     } |     } | ||||||
| @@ -1006,7 +994,7 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size) | |||||||
|  |  | ||||||
|     BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC); |     BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC); | ||||||
|     do { |     do { | ||||||
|         offset = alloc_clusters_noref(bs, size, QCOW_MAX_CLUSTER_OFFSET); |         offset = alloc_clusters_noref(bs, size); | ||||||
|         if (offset < 0) { |         if (offset < 0) { | ||||||
|             return offset; |             return offset; | ||||||
|         } |         } | ||||||
| @@ -1088,11 +1076,7 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) | |||||||
|     free_in_cluster = s->cluster_size - offset_into_cluster(s, offset); |     free_in_cluster = s->cluster_size - offset_into_cluster(s, offset); | ||||||
|     do { |     do { | ||||||
|         if (!offset || free_in_cluster < size) { |         if (!offset || free_in_cluster < size) { | ||||||
|             int64_t new_cluster; |             int64_t new_cluster = alloc_clusters_noref(bs, s->cluster_size); | ||||||
|  |  | ||||||
|             new_cluster = alloc_clusters_noref(bs, s->cluster_size, |  | ||||||
|                                                MIN(s->cluster_offset_mask, |  | ||||||
|                                                    QCOW_MAX_CLUSTER_OFFSET)); |  | ||||||
|             if (new_cluster < 0) { |             if (new_cluster < 0) { | ||||||
|                 return new_cluster; |                 return new_cluster; | ||||||
|             } |             } | ||||||
| @@ -1586,9 +1570,9 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, | |||||||
|         case QCOW2_CLUSTER_COMPRESSED: |         case QCOW2_CLUSTER_COMPRESSED: | ||||||
|             /* Compressed clusters don't have QCOW_OFLAG_COPIED */ |             /* Compressed clusters don't have QCOW_OFLAG_COPIED */ | ||||||
|             if (l2_entry & 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 " |                     "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; |                 l2_entry &= ~QCOW_OFLAG_COPIED; | ||||||
|                 res->corruptions++; |                 res->corruptions++; | ||||||
|             } |             } | ||||||
| @@ -1808,19 +1792,6 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, | |||||||
|     int ret; |     int ret; | ||||||
|     uint64_t refcount; |     uint64_t refcount; | ||||||
|     int i, j; |     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++) { |     for (i = 0; i < s->l1_size; i++) { | ||||||
|         uint64_t l1_entry = s->l1_table[i]; |         uint64_t l1_entry = s->l1_table[i]; | ||||||
| @@ -1840,8 +1811,10 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, | |||||||
|         if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) { |         if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) { | ||||||
|             fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d " |             fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d " | ||||||
|                     "l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n", |                     "l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n", | ||||||
|                     repair ? "Repairing" : "ERROR", i, l1_entry, refcount); |                     fix & BDRV_FIX_ERRORS ? "Repairing" : | ||||||
|             if (repair) { |                                             "ERROR", | ||||||
|  |                     i, l1_entry, refcount); | ||||||
|  |             if (fix & BDRV_FIX_ERRORS) { | ||||||
|                 s->l1_table[i] = refcount == 1 |                 s->l1_table[i] = refcount == 1 | ||||||
|                                ? l1_entry |  QCOW_OFLAG_COPIED |                                ? l1_entry |  QCOW_OFLAG_COPIED | ||||||
|                                : l1_entry & ~QCOW_OFLAG_COPIED; |                                : l1_entry & ~QCOW_OFLAG_COPIED; | ||||||
| @@ -1882,8 +1855,10 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, | |||||||
|                 if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { |                 if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { | ||||||
|                     fprintf(stderr, "%s OFLAG_COPIED data cluster: " |                     fprintf(stderr, "%s OFLAG_COPIED data cluster: " | ||||||
|                             "l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n", |                             "l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n", | ||||||
|                             repair ? "Repairing" : "ERROR", l2_entry, refcount); |                             fix & BDRV_FIX_ERRORS ? "Repairing" : | ||||||
|                     if (repair) { |                                                     "ERROR", | ||||||
|  |                             l2_entry, refcount); | ||||||
|  |                     if (fix & BDRV_FIX_ERRORS) { | ||||||
|                         l2_table[j] = cpu_to_be64(refcount == 1 |                         l2_table[j] = cpu_to_be64(refcount == 1 | ||||||
|                                     ? l2_entry |  QCOW_OFLAG_COPIED |                                     ? l2_entry |  QCOW_OFLAG_COPIED | ||||||
|                                     : l2_entry & ~QCOW_OFLAG_COPIED); |                                     : l2_entry & ~QCOW_OFLAG_COPIED); | ||||||
| @@ -2714,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; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -2736,9 +2701,7 @@ static const char *metadata_ol_names[] = { | |||||||
|     [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table", |     [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table", | ||||||
|     [QCOW2_OL_INACTIVE_L1_BITNR]    = "inactive L1 table", |     [QCOW2_OL_INACTIVE_L1_BITNR]    = "inactive L1 table", | ||||||
|     [QCOW2_OL_INACTIVE_L2_BITNR]    = "inactive L2 table", |     [QCOW2_OL_INACTIVE_L2_BITNR]    = "inactive L2 table", | ||||||
|     [QCOW2_OL_BITMAP_DIRECTORY_BITNR]   = "bitmap directory", |  | ||||||
| }; | }; | ||||||
| QEMU_BUILD_BUG_ON(QCOW2_OL_MAX_BITNR != ARRAY_SIZE(metadata_ol_names)); |  | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * First performs a check for metadata overlaps (through |  * First performs a check for metadata overlaps (through | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ | |||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "block/block_int.h" | #include "block/block_int.h" | ||||||
| #include "qcow2.h" | #include "block/qcow2.h" | ||||||
| #include "qemu/bswap.h" | #include "qemu/bswap.h" | ||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
| #include "qemu/cutils.h" | #include "qemu/cutils.h" | ||||||
|   | |||||||
							
								
								
									
										956
									
								
								block/qcow2.c
									
									
									
									
									
								
							
							
						
						
									
										956
									
								
								block/qcow2.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -27,7 +27,6 @@ | |||||||
|  |  | ||||||
| #include "crypto/block.h" | #include "crypto/block.h" | ||||||
| #include "qemu/coroutine.h" | #include "qemu/coroutine.h" | ||||||
| #include "qemu/units.h" |  | ||||||
|  |  | ||||||
| //#define DEBUG_ALLOC | //#define DEBUG_ALLOC | ||||||
| //#define DEBUG_ALLOC2 | //#define DEBUG_ALLOC2 | ||||||
| @@ -42,19 +41,13 @@ | |||||||
| #define QCOW_MAX_CRYPT_CLUSTERS 32 | #define QCOW_MAX_CRYPT_CLUSTERS 32 | ||||||
| #define QCOW_MAX_SNAPSHOTS 65536 | #define QCOW_MAX_SNAPSHOTS 65536 | ||||||
|  |  | ||||||
| /* Field widths in qcow2 mean normal cluster offsets cannot reach |  | ||||||
|  * 64PB; depending on cluster size, compressed clusters can have a |  | ||||||
|  * smaller limit (64PB for up to 16k clusters, then ramps down to |  | ||||||
|  * 512TB for 2M clusters).  */ |  | ||||||
| #define QCOW_MAX_CLUSTER_OFFSET ((1ULL << 56) - 1) |  | ||||||
|  |  | ||||||
| /* 8 MB refcount table is enough for 2 PB images at 64k cluster size | /* 8 MB refcount table is enough for 2 PB images at 64k cluster size | ||||||
|  * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */ |  * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */ | ||||||
| #define QCOW_MAX_REFTABLE_SIZE (8 * MiB) | #define QCOW_MAX_REFTABLE_SIZE 0x800000 | ||||||
|  |  | ||||||
| /* 32 MB L1 table is enough for 2 PB images at 64k cluster size | /* 32 MB L1 table is enough for 2 PB images at 64k cluster size | ||||||
|  * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */ |  * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */ | ||||||
| #define QCOW_MAX_L1_SIZE (32 * MiB) | #define QCOW_MAX_L1_SIZE 0x2000000 | ||||||
|  |  | ||||||
| /* Allow for an average of 1k per snapshot table entry, should be plenty of | /* Allow for an average of 1k per snapshot table entry, should be plenty of | ||||||
|  * space for snapshot names and IDs */ |  * space for snapshot names and IDs */ | ||||||
| @@ -80,17 +73,17 @@ | |||||||
| /* Must be at least 4 to cover all cases of refcount table growth */ | /* Must be at least 4 to cover all cases of refcount table growth */ | ||||||
| #define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */ | #define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */ | ||||||
|  |  | ||||||
| #ifdef CONFIG_LINUX | /* Whichever is more */ | ||||||
| #define DEFAULT_L2_CACHE_MAX_SIZE (32 * MiB) | #define DEFAULT_L2_CACHE_CLUSTERS 8 /* clusters */ | ||||||
| #define DEFAULT_CACHE_CLEAN_INTERVAL 600  /* seconds */ | #define DEFAULT_L2_CACHE_BYTE_SIZE 1048576 /* bytes */ | ||||||
| #else |  | ||||||
| #define DEFAULT_L2_CACHE_MAX_SIZE (8 * MiB) | /* The refblock cache needs only a fourth of the L2 cache size to cover as many | ||||||
| /* Cache clean interval is currently available only on Linux, so must be 0 */ |  * clusters */ | ||||||
| #define DEFAULT_CACHE_CLEAN_INTERVAL 0 | #define DEFAULT_L2_REFCOUNT_SIZE_RATIO 4 | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #define DEFAULT_CLUSTER_SIZE 65536 | #define DEFAULT_CLUSTER_SIZE 65536 | ||||||
|  |  | ||||||
|  |  | ||||||
| #define QCOW2_OPT_LAZY_REFCOUNTS "lazy-refcounts" | #define QCOW2_OPT_LAZY_REFCOUNTS "lazy-refcounts" | ||||||
| #define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request" | #define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request" | ||||||
| #define QCOW2_OPT_DISCARD_SNAPSHOT "pass-discard-snapshot" | #define QCOW2_OPT_DISCARD_SNAPSHOT "pass-discard-snapshot" | ||||||
| @@ -105,7 +98,6 @@ | |||||||
| #define QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE "overlap-check.snapshot-table" | #define QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE "overlap-check.snapshot-table" | ||||||
| #define QCOW2_OPT_OVERLAP_INACTIVE_L1 "overlap-check.inactive-l1" | #define QCOW2_OPT_OVERLAP_INACTIVE_L1 "overlap-check.inactive-l1" | ||||||
| #define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2" | #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_CACHE_SIZE "cache-size" | ||||||
| #define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size" | #define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size" | ||||||
| #define QCOW2_OPT_L2_CACHE_ENTRY_SIZE "l2-cache-entry-size" | #define QCOW2_OPT_L2_CACHE_ENTRY_SIZE "l2-cache-entry-size" | ||||||
| @@ -281,7 +273,7 @@ typedef struct BDRVQcow2State { | |||||||
|     uint8_t *cluster_cache; |     uint8_t *cluster_cache; | ||||||
|     uint8_t *cluster_data; |     uint8_t *cluster_data; | ||||||
|     uint64_t cluster_cache_offset; |     uint64_t cluster_cache_offset; | ||||||
|     QLIST_HEAD(, QCowL2Meta) cluster_allocs; |     QLIST_HEAD(QCowClusterAlloc, QCowL2Meta) cluster_allocs; | ||||||
|  |  | ||||||
|     uint64_t *refcount_table; |     uint64_t *refcount_table; | ||||||
|     uint64_t refcount_table_offset; |     uint64_t refcount_table_offset; | ||||||
| @@ -337,9 +329,6 @@ typedef struct BDRVQcow2State { | |||||||
|      * override) */ |      * override) */ | ||||||
|     char *image_backing_file; |     char *image_backing_file; | ||||||
|     char *image_backing_format; |     char *image_backing_format; | ||||||
|  |  | ||||||
|     CoQueue compress_wait_queue; |  | ||||||
|     int nb_compress_threads; |  | ||||||
| } BDRVQcow2State; | } BDRVQcow2State; | ||||||
|  |  | ||||||
| typedef struct Qcow2COWRegion { | typedef struct Qcow2COWRegion { | ||||||
| @@ -419,9 +408,8 @@ typedef enum QCow2MetadataOverlap { | |||||||
|     QCOW2_OL_SNAPSHOT_TABLE_BITNR = 5, |     QCOW2_OL_SNAPSHOT_TABLE_BITNR = 5, | ||||||
|     QCOW2_OL_INACTIVE_L1_BITNR    = 6, |     QCOW2_OL_INACTIVE_L1_BITNR    = 6, | ||||||
|     QCOW2_OL_INACTIVE_L2_BITNR    = 7, |     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_NONE           = 0, | ||||||
|     QCOW2_OL_MAIN_HEADER    = (1 << QCOW2_OL_MAIN_HEADER_BITNR), |     QCOW2_OL_MAIN_HEADER    = (1 << QCOW2_OL_MAIN_HEADER_BITNR), | ||||||
| @@ -434,13 +422,12 @@ typedef enum QCow2MetadataOverlap { | |||||||
|     /* NOTE: Checking overlaps with inactive L2 tables will result in bdrv |     /* NOTE: Checking overlaps with inactive L2 tables will result in bdrv | ||||||
|      * reads. */ |      * reads. */ | ||||||
|     QCOW2_OL_INACTIVE_L2    = (1 << QCOW2_OL_INACTIVE_L2_BITNR), |     QCOW2_OL_INACTIVE_L2    = (1 << QCOW2_OL_INACTIVE_L2_BITNR), | ||||||
|     QCOW2_OL_BITMAP_DIRECTORY = (1 << QCOW2_OL_BITMAP_DIRECTORY_BITNR), |  | ||||||
| } QCow2MetadataOverlap; | } QCow2MetadataOverlap; | ||||||
|  |  | ||||||
| /* Perform all overlap checks which can be done in constant time */ | /* Perform all overlap checks which can be done in constant time */ | ||||||
| #define QCOW2_OL_CONSTANT \ | #define QCOW2_OL_CONSTANT \ | ||||||
|     (QCOW2_OL_MAIN_HEADER | QCOW2_OL_ACTIVE_L1 | QCOW2_OL_REFCOUNT_TABLE | \ |     (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 */ | /* Perform all overlap checks which don't require disk access */ | ||||||
| #define QCOW2_OL_CACHED \ | #define QCOW2_OL_CACHED \ | ||||||
| @@ -616,6 +603,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, | |||||||
|                         bool exact_size); |                         bool exact_size); | ||||||
| int qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t max_size); | int qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t max_size); | ||||||
| int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index); | int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index); | ||||||
|  | int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); | ||||||
| int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num, | int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num, | ||||||
|                           uint8_t *buf, int nb_sectors, bool enc, Error **errp); |                           uint8_t *buf, int nb_sectors, bool enc, Error **errp); | ||||||
|  |  | ||||||
| @@ -629,7 +617,6 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, | |||||||
|                                          int compressed_size); |                                          int compressed_size); | ||||||
|  |  | ||||||
| int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); | 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, | int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset, | ||||||
|                           uint64_t bytes, enum qcow2_discard_type type, |                           uint64_t bytes, enum qcow2_discard_type type, | ||||||
|                           bool full_discard); |                           bool full_discard); | ||||||
| @@ -684,10 +671,6 @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, | |||||||
|                                   void **refcount_table, |                                   void **refcount_table, | ||||||
|                                   int64_t *refcount_table_size); |                                   int64_t *refcount_table_size); | ||||||
| bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp); | bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp); | ||||||
| Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(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); | int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp); | ||||||
| void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp); | void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp); | ||||||
| int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp); | int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp); | ||||||
|   | |||||||
							
								
								
									
										219
									
								
								block/qed.c
									
									
									
									
									
								
							
							
						
						
									
										219
									
								
								block/qed.c
									
									
									
									
									
								
							| @@ -13,7 +13,6 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "block/qdict.h" |  | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qemu/timer.h" | #include "qemu/timer.h" | ||||||
| #include "qemu/bswap.h" | #include "qemu/bswap.h" | ||||||
| @@ -21,11 +20,6 @@ | |||||||
| #include "trace.h" | #include "trace.h" | ||||||
| #include "qed.h" | #include "qed.h" | ||||||
| #include "sysemu/block-backend.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, | static int bdrv_qed_probe(const uint8_t *buf, int buf_size, | ||||||
|                           const char *filename) |                           const char *filename) | ||||||
| @@ -559,7 +553,6 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     if (qemu_in_coroutine()) { |     if (qemu_in_coroutine()) { | ||||||
|         bdrv_qed_open_entry(&qoc); |         bdrv_qed_open_entry(&qoc); | ||||||
|     } else { |     } else { | ||||||
|         assert(qemu_get_current_aio_context() == qemu_get_aio_context()); |  | ||||||
|         qemu_coroutine_enter(qemu_coroutine_create(bdrv_qed_open_entry, &qoc)); |         qemu_coroutine_enter(qemu_coroutine_create(bdrv_qed_open_entry, &qoc)); | ||||||
|         BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS); |         BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS); | ||||||
|     } |     } | ||||||
| @@ -601,78 +594,43 @@ static void bdrv_qed_close(BlockDriverState *bs) | |||||||
|     qemu_vfree(s->l1_table); |     qemu_vfree(s->l1_table); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts, | static int qed_create(const char *filename, uint32_t cluster_size, | ||||||
|                                            Error **errp) |                       uint64_t image_size, uint32_t table_size, | ||||||
|  |                       const char *backing_file, const char *backing_fmt, | ||||||
|  |                       QemuOpts *opts, Error **errp) | ||||||
| { | { | ||||||
|     BlockdevCreateOptionsQed *qed_opts; |     QEDHeader header = { | ||||||
|     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) { |  | ||||||
|         .magic = QED_MAGIC, |         .magic = QED_MAGIC, | ||||||
|         .cluster_size = qed_opts->cluster_size, |         .cluster_size = cluster_size, | ||||||
|         .table_size = qed_opts->table_size, |         .table_size = table_size, | ||||||
|         .header_size = 1, |         .header_size = 1, | ||||||
|         .features = 0, |         .features = 0, | ||||||
|         .compat_features = 0, |         .compat_features = 0, | ||||||
|         .l1_table_offset = qed_opts->cluster_size, |         .l1_table_offset = cluster_size, | ||||||
|         .image_size = qed_opts->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 */ |     /* File must start empty and grow, check truncate is supported */ | ||||||
|     ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp); |     ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp); | ||||||
| @@ -680,25 +638,22 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts, | |||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (qed_opts->has_backing_file) { |     if (backing_file) { | ||||||
|         header.features |= QED_F_BACKING_FILE; |         header.features |= QED_F_BACKING_FILE; | ||||||
|         header.backing_filename_offset = sizeof(le_header); |         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)) { |         if (qed_fmt_is_raw(backing_fmt)) { | ||||||
|             header.features |= QED_F_BACKING_FORMAT_NO_PROBE; |             header.features |= QED_F_BACKING_FORMAT_NO_PROBE; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     } |  | ||||||
|  |  | ||||||
|     qed_header_cpu_to_le(&header, &le_header); |     qed_header_cpu_to_le(&header, &le_header); | ||||||
|     ret = blk_pwrite(blk, 0, &le_header, sizeof(le_header), 0); |     ret = blk_pwrite(blk, 0, &le_header, sizeof(le_header), 0); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         goto out; |         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); |                      header.backing_filename_size, 0); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         goto out; |         goto out; | ||||||
| @@ -714,7 +669,6 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts, | |||||||
| out: | out: | ||||||
|     g_free(l1_table); |     g_free(l1_table); | ||||||
|     blk_unref(blk); |     blk_unref(blk); | ||||||
|     bdrv_unref(bs); |  | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -722,74 +676,51 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, | |||||||
|                                                 QemuOpts *opts, |                                                 QemuOpts *opts, | ||||||
|                                                 Error **errp) |                                                 Error **errp) | ||||||
| { | { | ||||||
|     BlockdevCreateOptions *create_options = NULL; |     uint64_t image_size = 0; | ||||||
|     QDict *qdict; |     uint32_t cluster_size = QED_DEFAULT_CLUSTER_SIZE; | ||||||
|     Visitor *v; |     uint32_t table_size = QED_DEFAULT_TABLE_SIZE; | ||||||
|     BlockDriverState *bs = NULL; |     char *backing_file = NULL; | ||||||
|     Error *local_err = NULL; |     char *backing_fmt = NULL; | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     static const QDictRenames opt_renames[] = { |     image_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), | ||||||
|         { BLOCK_OPT_BACKING_FILE,       "backing-file" }, |                           BDRV_SECTOR_SIZE); | ||||||
|         { BLOCK_OPT_BACKING_FMT,        "backing-fmt" }, |     backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); | ||||||
|         { BLOCK_OPT_CLUSTER_SIZE,       "cluster-size" }, |     backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT); | ||||||
|         { BLOCK_OPT_TABLE_SIZE,         "table-size" }, |     cluster_size = qemu_opt_get_size_del(opts, | ||||||
|         { NULL, NULL }, |                                          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 */ |     if (!qed_is_cluster_size_valid(cluster_size)) { | ||||||
|     qdict = qemu_opts_to_qdict_filtered(opts, NULL, &qed_create_opts, true); |         error_setg(errp, "QED cluster size must be within range [%u, %u] " | ||||||
|  |                          "and power of 2", | ||||||
|     if (!qdict_rename_keys(qdict, opt_renames, errp)) { |                    QED_MIN_CLUSTER_SIZE, QED_MAX_CLUSTER_SIZE); | ||||||
|         ret = -EINVAL; |         ret = -EINVAL; | ||||||
|         goto fail; |         goto finish; | ||||||
|     } |     } | ||||||
|  |     if (!qed_is_table_size_valid(table_size)) { | ||||||
|     /* Create and open the file (protocol layer) */ |         error_setg(errp, "QED table size must be within range [%u, %u] " | ||||||
|     ret = bdrv_create_file(filename, opts, &local_err); |                          "and power of 2", | ||||||
|     if (ret < 0) { |                    QED_MIN_TABLE_SIZE, QED_MAX_TABLE_SIZE); | ||||||
|         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) { |  | ||||||
|         ret = -EINVAL; |         ret = -EINVAL; | ||||||
|         goto fail; |         goto finish; | ||||||
|     } |     } | ||||||
|  |     if (!qed_is_image_size_valid(image_size, cluster_size, table_size)) { | ||||||
|     visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); |         error_setg(errp, "QED image size must be a non-zero multiple of " | ||||||
|     visit_free(v); |                          "cluster size and less than %" PRIu64 " bytes", | ||||||
|  |                    qed_max_image_size(cluster_size, table_size)); | ||||||
|     if (local_err) { |  | ||||||
|         error_propagate(errp, local_err); |  | ||||||
|         ret = -EINVAL; |         ret = -EINVAL; | ||||||
|         goto fail; |         goto finish; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Silently round up size */ |     ret = qed_create(filename, cluster_size, image_size, table_size, | ||||||
|     assert(create_options->driver == BLOCKDEV_DRIVER_QED); |                      backing_file, backing_fmt, opts, errp); | ||||||
|     create_options->u.qed.size = |  | ||||||
|         ROUND_UP(create_options->u.qed.size, BDRV_SECTOR_SIZE); |  | ||||||
|  |  | ||||||
|     /* Create the qed image (format layer) */ | finish: | ||||||
|     ret = bdrv_qed_co_create(create_options, errp); |     g_free(backing_file); | ||||||
|  |     g_free(backing_fmt); | ||||||
| fail: |  | ||||||
|     qobject_unref(qdict); |  | ||||||
|     bdrv_unref(bs); |  | ||||||
|     qapi_free_BlockdevCreateOptions(create_options); |  | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1435,9 +1366,8 @@ static int coroutine_fn bdrv_qed_co_readv(BlockDriverState *bs, | |||||||
|  |  | ||||||
| static int coroutine_fn bdrv_qed_co_writev(BlockDriverState *bs, | static int coroutine_fn bdrv_qed_co_writev(BlockDriverState *bs, | ||||||
|                                            int64_t sector_num, int nb_sectors, |                                            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); |     return qed_co_request(bs, sector_num, qiov, nb_sectors, QED_AIOCB_WRITE); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1468,10 +1398,8 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs, | |||||||
|                           QED_AIOCB_WRITE | QED_AIOCB_ZERO); |                           QED_AIOCB_WRITE | QED_AIOCB_ZERO); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coroutine_fn bdrv_qed_co_truncate(BlockDriverState *bs, | static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset, | ||||||
|                                              int64_t offset, |                              PreallocMode prealloc, Error **errp) | ||||||
|                                              PreallocMode prealloc, |  | ||||||
|                                              Error **errp) |  | ||||||
| { | { | ||||||
|     BDRVQEDState *s = bs->opaque; |     BDRVQEDState *s = bs->opaque; | ||||||
|     uint64_t old_image_size; |     uint64_t old_image_size; | ||||||
| @@ -1607,8 +1535,8 @@ static void coroutine_fn bdrv_qed_co_invalidate_cache(BlockDriverState *bs, | |||||||
|     ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, &local_err); |     ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, &local_err); | ||||||
|     qemu_co_mutex_unlock(&s->table_lock); |     qemu_co_mutex_unlock(&s->table_lock); | ||||||
|     if (local_err) { |     if (local_err) { | ||||||
|         error_propagate_prepend(errp, local_err, |         error_propagate(errp, local_err); | ||||||
|                                 "Could not reopen qed layer: "); |         error_prepend(errp, "Could not reopen qed layer: "); | ||||||
|         return; |         return; | ||||||
|     } else if (ret < 0) { |     } else if (ret < 0) { | ||||||
|         error_setg_errno(errp, -ret, "Could not reopen qed layer"); |         error_setg_errno(errp, -ret, "Could not reopen qed layer"); | ||||||
| @@ -1674,14 +1602,13 @@ static BlockDriver bdrv_qed = { | |||||||
|     .bdrv_close               = bdrv_qed_close, |     .bdrv_close               = bdrv_qed_close, | ||||||
|     .bdrv_reopen_prepare      = bdrv_qed_reopen_prepare, |     .bdrv_reopen_prepare      = bdrv_qed_reopen_prepare, | ||||||
|     .bdrv_child_perm          = bdrv_format_default_perms, |     .bdrv_child_perm          = bdrv_format_default_perms, | ||||||
|     .bdrv_co_create           = bdrv_qed_co_create, |  | ||||||
|     .bdrv_co_create_opts      = bdrv_qed_co_create_opts, |     .bdrv_co_create_opts      = bdrv_qed_co_create_opts, | ||||||
|     .bdrv_has_zero_init       = bdrv_has_zero_init_1, |     .bdrv_has_zero_init       = bdrv_has_zero_init_1, | ||||||
|     .bdrv_co_block_status     = bdrv_qed_co_block_status, |     .bdrv_co_block_status     = bdrv_qed_co_block_status, | ||||||
|     .bdrv_co_readv            = bdrv_qed_co_readv, |     .bdrv_co_readv            = bdrv_qed_co_readv, | ||||||
|     .bdrv_co_writev           = bdrv_qed_co_writev, |     .bdrv_co_writev           = bdrv_qed_co_writev, | ||||||
|     .bdrv_co_pwrite_zeroes    = bdrv_qed_co_pwrite_zeroes, |     .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_getlength           = bdrv_qed_getlength, | ||||||
|     .bdrv_get_info            = bdrv_qed_get_info, |     .bdrv_get_info            = bdrv_qed_get_info, | ||||||
|     .bdrv_refresh_limits      = bdrv_qed_refresh_limits, |     .bdrv_refresh_limits      = bdrv_qed_refresh_limits, | ||||||
|   | |||||||
| @@ -17,7 +17,6 @@ | |||||||
| #include "qemu/cutils.h" | #include "qemu/cutils.h" | ||||||
| #include "qemu/option.h" | #include "qemu/option.h" | ||||||
| #include "block/block_int.h" | #include "block/block_int.h" | ||||||
| #include "block/qdict.h" |  | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qapi/qapi-events-block.h" | #include "qapi/qapi-events-block.h" | ||||||
| #include "qapi/qmp/qdict.h" | #include "qapi/qmp/qdict.h" | ||||||
| @@ -116,7 +115,6 @@ struct QuorumAIOCB { | |||||||
|     /* Request metadata */ |     /* Request metadata */ | ||||||
|     uint64_t offset; |     uint64_t offset; | ||||||
|     uint64_t bytes; |     uint64_t bytes; | ||||||
|     int flags; |  | ||||||
|  |  | ||||||
|     QEMUIOVector *qiov;         /* calling IOV */ |     QEMUIOVector *qiov;         /* calling IOV */ | ||||||
|  |  | ||||||
| @@ -159,8 +157,7 @@ static bool quorum_64bits_compare(QuorumVoteValue *a, QuorumVoteValue *b) | |||||||
| static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs, | static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs, | ||||||
|                                    QEMUIOVector *qiov, |                                    QEMUIOVector *qiov, | ||||||
|                                    uint64_t offset, |                                    uint64_t offset, | ||||||
|                                    uint64_t bytes, |                                    uint64_t bytes) | ||||||
|                                    int flags) |  | ||||||
| { | { | ||||||
|     BDRVQuorumState *s = bs->opaque; |     BDRVQuorumState *s = bs->opaque; | ||||||
|     QuorumAIOCB *acb = g_new(QuorumAIOCB, 1); |     QuorumAIOCB *acb = g_new(QuorumAIOCB, 1); | ||||||
| @@ -171,7 +168,6 @@ static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs, | |||||||
|         .bs                 = bs, |         .bs                 = bs, | ||||||
|         .offset             = offset, |         .offset             = offset, | ||||||
|         .bytes              = bytes, |         .bytes              = bytes, | ||||||
|         .flags              = flags, |  | ||||||
|         .qiov               = qiov, |         .qiov               = qiov, | ||||||
|         .votes.compare      = quorum_sha256_compare, |         .votes.compare      = quorum_sha256_compare, | ||||||
|         .votes.vote_list    = QLIST_HEAD_INITIALIZER(acb.votes.vote_list), |         .votes.vote_list    = QLIST_HEAD_INITIALIZER(acb.votes.vote_list), | ||||||
| @@ -199,7 +195,7 @@ static void quorum_report_bad(QuorumOpType type, uint64_t offset, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     qapi_event_send_quorum_report_bad(type, !!msg, msg, node_name, start_sector, |     qapi_event_send_quorum_report_bad(type, !!msg, msg, node_name, start_sector, | ||||||
|                                       end_sector - start_sector); |                                       end_sector - start_sector, &error_abort); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void quorum_report_failure(QuorumAIOCB *acb) | static void quorum_report_failure(QuorumAIOCB *acb) | ||||||
| @@ -210,7 +206,7 @@ static void quorum_report_failure(QuorumAIOCB *acb) | |||||||
|                                       BDRV_SECTOR_SIZE); |                                       BDRV_SECTOR_SIZE); | ||||||
|  |  | ||||||
|     qapi_event_send_quorum_failure(reference, start_sector, |     qapi_event_send_quorum_failure(reference, start_sector, | ||||||
|                                    end_sector - start_sector); |                                    end_sector - start_sector, &error_abort); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int quorum_vote_error(QuorumAIOCB *acb); | static int quorum_vote_error(QuorumAIOCB *acb); | ||||||
| @@ -275,11 +271,9 @@ static void quorum_rewrite_entry(void *opaque) | |||||||
|     BDRVQuorumState *s = acb->bs->opaque; |     BDRVQuorumState *s = acb->bs->opaque; | ||||||
|  |  | ||||||
|     /* Ignore any errors, it's just a correction attempt for already |     /* Ignore any errors, it's just a correction attempt for already | ||||||
|      * corrupted data. |      * corrupted data. */ | ||||||
|      * Mask out BDRV_REQ_WRITE_UNCHANGED because this overwrites the |  | ||||||
|      * area with different data from the other children. */ |  | ||||||
|     bdrv_co_pwritev(s->children[co->idx], acb->offset, acb->bytes, |     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 */ |     /* Wake up the caller after the last rewrite */ | ||||||
|     acb->rewrite_count--; |     acb->rewrite_count--; | ||||||
| @@ -437,7 +431,23 @@ static bool quorum_iovec_compare(QEMUIOVector *a, QEMUIOVector *b) | |||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool quorum_compare(QuorumAIOCB *acb, QEMUIOVector *a, QEMUIOVector *b) | static void GCC_FMT_ATTR(2, 3) quorum_err(QuorumAIOCB *acb, | ||||||
|  |                                           const char *fmt, ...) | ||||||
|  | { | ||||||
|  |     va_list ap; | ||||||
|  |  | ||||||
|  |     va_start(ap, fmt); | ||||||
|  |     fprintf(stderr, "quorum: offset=%" PRIu64 " bytes=%" PRIu64 " ", | ||||||
|  |             acb->offset, acb->bytes); | ||||||
|  |     vfprintf(stderr, fmt, ap); | ||||||
|  |     fprintf(stderr, "\n"); | ||||||
|  |     va_end(ap); | ||||||
|  |     exit(1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static bool quorum_compare(QuorumAIOCB *acb, | ||||||
|  |                            QEMUIOVector *a, | ||||||
|  |                            QEMUIOVector *b) | ||||||
| { | { | ||||||
|     BDRVQuorumState *s = acb->bs->opaque; |     BDRVQuorumState *s = acb->bs->opaque; | ||||||
|     ssize_t offset; |     ssize_t offset; | ||||||
| @@ -446,10 +456,8 @@ static bool quorum_compare(QuorumAIOCB *acb, QEMUIOVector *a, QEMUIOVector *b) | |||||||
|     if (s->is_blkverify) { |     if (s->is_blkverify) { | ||||||
|         offset = qemu_iovec_compare(a, b); |         offset = qemu_iovec_compare(a, b); | ||||||
|         if (offset != -1) { |         if (offset != -1) { | ||||||
|             fprintf(stderr, "quorum: offset=%" PRIu64 " bytes=%" PRIu64 |             quorum_err(acb, "contents mismatch at offset %" PRIu64, | ||||||
|                     " contents mismatch at offset %" PRIu64 "\n", |                        acb->offset + offset); | ||||||
|                     acb->offset, acb->bytes, acb->offset + offset); |  | ||||||
|             exit(1); |  | ||||||
|         } |         } | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| @@ -600,7 +608,7 @@ static void read_quorum_children_entry(void *opaque) | |||||||
| static int read_quorum_children(QuorumAIOCB *acb) | static int read_quorum_children(QuorumAIOCB *acb) | ||||||
| { | { | ||||||
|     BDRVQuorumState *s = acb->bs->opaque; |     BDRVQuorumState *s = acb->bs->opaque; | ||||||
|     int i; |     int i, ret; | ||||||
|  |  | ||||||
|     acb->children_read = s->num_children; |     acb->children_read = s->num_children; | ||||||
|     for (i = 0; i < s->num_children; i++) { |     for (i = 0; i < s->num_children; i++) { | ||||||
| @@ -635,7 +643,9 @@ static int read_quorum_children(QuorumAIOCB *acb) | |||||||
|         qemu_coroutine_yield(); |         qemu_coroutine_yield(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return acb->vote_ret; |     ret = acb->vote_ret; | ||||||
|  |  | ||||||
|  |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int read_fifo_child(QuorumAIOCB *acb) | static int read_fifo_child(QuorumAIOCB *acb) | ||||||
| @@ -663,7 +673,7 @@ static int quorum_co_preadv(BlockDriverState *bs, uint64_t offset, | |||||||
|                             uint64_t bytes, QEMUIOVector *qiov, int flags) |                             uint64_t bytes, QEMUIOVector *qiov, int flags) | ||||||
| { | { | ||||||
|     BDRVQuorumState *s = bs->opaque; |     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; |     int ret; | ||||||
|  |  | ||||||
|     acb->is_read = true; |     acb->is_read = true; | ||||||
| @@ -689,7 +699,7 @@ static void write_quorum_entry(void *opaque) | |||||||
|  |  | ||||||
|     sacb->bs = s->children[i]->bs; |     sacb->bs = s->children[i]->bs; | ||||||
|     sacb->ret = bdrv_co_pwritev(s->children[i], acb->offset, acb->bytes, |     sacb->ret = bdrv_co_pwritev(s->children[i], acb->offset, acb->bytes, | ||||||
|                                 acb->qiov, acb->flags); |                                 acb->qiov, 0); | ||||||
|     if (sacb->ret == 0) { |     if (sacb->ret == 0) { | ||||||
|         acb->success_count++; |         acb->success_count++; | ||||||
|     } else { |     } else { | ||||||
| @@ -709,7 +719,7 @@ static int quorum_co_pwritev(BlockDriverState *bs, uint64_t offset, | |||||||
|                              uint64_t bytes, QEMUIOVector *qiov, int flags) |                              uint64_t bytes, QEMUIOVector *qiov, int flags) | ||||||
| { | { | ||||||
|     BDRVQuorumState *s = bs->opaque; |     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; |     int i, ret; | ||||||
|  |  | ||||||
|     for (i = 0; i < s->num_children; i++) { |     for (i = 0; i < s->num_children; i++) { | ||||||
| @@ -912,12 +922,13 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     s->read_pattern = ret; |     s->read_pattern = ret; | ||||||
|  |  | ||||||
|     if (s->read_pattern == QUORUM_READ_PATTERN_QUORUM) { |     if (s->read_pattern == QUORUM_READ_PATTERN_QUORUM) { | ||||||
|         s->is_blkverify = qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false); |         /* is the driver in blkverify mode */ | ||||||
|         if (s->is_blkverify && (s->num_children != 2 || s->threshold != 2)) { |         if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false) && | ||||||
|             error_setg(&local_err, "blkverify=on can only be set if there are " |             s->num_children == 2 && s->threshold == 2) { | ||||||
|                        "exactly two files and vote-threshold is 2"); |             s->is_blkverify = true; | ||||||
|             ret = -EINVAL; |         } else if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false)) { | ||||||
|             goto exit; |             fprintf(stderr, "blkverify mode is set by setting blkverify=on " | ||||||
|  |                     "and using two files with vote_threshold=2\n"); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         s->rewrite_corrupted = qemu_opt_get_bool(opts, QUORUM_OPT_REWRITE, |         s->rewrite_corrupted = qemu_opt_get_bool(opts, QUORUM_OPT_REWRITE, | ||||||
| @@ -950,8 +961,6 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     } |     } | ||||||
|     s->next_child_index = s->num_children; |     s->next_child_index = s->num_children; | ||||||
|  |  | ||||||
|     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; |  | ||||||
|  |  | ||||||
|     g_free(opened); |     g_free(opened); | ||||||
|     goto exit; |     goto exit; | ||||||
|  |  | ||||||
| @@ -992,11 +1001,6 @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs, | |||||||
|     char indexstr[32]; |     char indexstr[32]; | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     if (s->is_blkverify) { |  | ||||||
|         error_setg(errp, "Cannot add a child to a quorum in blkverify mode"); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     assert(s->num_children <= INT_MAX / sizeof(BdrvChild *)); |     assert(s->num_children <= INT_MAX / sizeof(BdrvChild *)); | ||||||
|     if (s->num_children == INT_MAX / sizeof(BdrvChild *) || |     if (s->num_children == INT_MAX / sizeof(BdrvChild *) || | ||||||
|         s->next_child_index == UINT_MAX) { |         s->next_child_index == UINT_MAX) { | ||||||
| @@ -1051,9 +1055,6 @@ static void quorum_del_child(BlockDriverState *bs, BdrvChild *child, | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* We know now that num_children > threshold, so blkverify must be false */ |  | ||||||
|     assert(!s->is_blkverify); |  | ||||||
|  |  | ||||||
|     bdrv_drained_begin(bs); |     bdrv_drained_begin(bs); | ||||||
|  |  | ||||||
|     /* We can safely remove this child now */ |     /* We can safely remove this child now */ | ||||||
| @@ -1081,8 +1082,8 @@ static void quorum_refresh_filename(BlockDriverState *bs, QDict *options) | |||||||
|  |  | ||||||
|     children = qlist_new(); |     children = qlist_new(); | ||||||
|     for (i = 0; i < s->num_children; i++) { |     for (i = 0; i < s->num_children; i++) { | ||||||
|         qlist_append(children, |         QINCREF(s->children[i]->bs->full_open_options); | ||||||
|                      qobject_ref(s->children[i]->bs->full_open_options)); |         qlist_append(children, s->children[i]->bs->full_open_options); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     opts = qdict_new(); |     opts = qdict_new(); | ||||||
|   | |||||||
| @@ -167,37 +167,16 @@ static void raw_reopen_abort(BDRVReopenState *state) | |||||||
|     state->opaque = NULL; |     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, | static int coroutine_fn raw_co_preadv(BlockDriverState *bs, uint64_t offset, | ||||||
|                                       uint64_t bytes, QEMUIOVector *qiov, |                                       uint64_t bytes, QEMUIOVector *qiov, | ||||||
|                                       int flags) |                                       int flags) | ||||||
| { | { | ||||||
|     int ret; |     BDRVRawState *s = bs->opaque; | ||||||
|  |  | ||||||
|     ret = raw_adjust_offset(bs, &offset, bytes, false); |     if (offset > UINT64_MAX - s->offset) { | ||||||
|     if (ret) { |         return -EINVAL; | ||||||
|         return ret; |  | ||||||
|     } |     } | ||||||
|  |     offset += s->offset; | ||||||
|  |  | ||||||
|     BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); |     BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); | ||||||
|     return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); |     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, |                                        uint64_t bytes, QEMUIOVector *qiov, | ||||||
|                                        int flags) |                                        int flags) | ||||||
| { | { | ||||||
|  |     BDRVRawState *s = bs->opaque; | ||||||
|     void *buf = NULL; |     void *buf = NULL; | ||||||
|     BlockDriver *drv; |     BlockDriver *drv; | ||||||
|     QEMUIOVector local_qiov; |     QEMUIOVector local_qiov; | ||||||
|     int ret; |     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) { |     if (bs->probed && offset < BLOCK_PROBE_BUF_SIZE && bytes) { | ||||||
|         /* Handling partial writes would be a pain - so we just |         /* Handling partial writes would be a pain - so we just | ||||||
|          * require that guests have 512-byte request alignment if |          * 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; |         qiov = &local_qiov; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ret = raw_adjust_offset(bs, &offset, bytes, true); |     offset += s->offset; | ||||||
|     if (ret) { |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); |     BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); | ||||||
|     ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); |     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, |                                              int64_t offset, int bytes, | ||||||
|                                              BdrvRequestFlags flags) |                                              BdrvRequestFlags flags) | ||||||
| { | { | ||||||
|     int ret; |     BDRVRawState *s = bs->opaque; | ||||||
|  |     if (offset > UINT64_MAX - s->offset) { | ||||||
|     ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true); |         return -EINVAL; | ||||||
|     if (ret) { |  | ||||||
|         return ret; |  | ||||||
|     } |     } | ||||||
|  |     offset += s->offset; | ||||||
|     return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); |     return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs, | static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs, | ||||||
|                                         int64_t offset, int bytes) |                                         int64_t offset, int bytes) | ||||||
| { | { | ||||||
|     int ret; |     BDRVRawState *s = bs->opaque; | ||||||
|  |     if (offset > UINT64_MAX - s->offset) { | ||||||
|     ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true); |         return -EINVAL; | ||||||
|     if (ret) { |  | ||||||
|         return ret; |  | ||||||
|     } |     } | ||||||
|     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) | 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) |                         PreallocMode prealloc, Error **errp) | ||||||
| { | { | ||||||
|     BDRVRawState *s = bs->opaque; |     BDRVRawState *s = bs->opaque; | ||||||
| @@ -383,7 +369,7 @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, | |||||||
|  |  | ||||||
|     s->size = offset; |     s->size = offset; | ||||||
|     offset += s->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) | 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->sg = bs->file->bs->sg; | ||||||
|     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | |     bs->supported_write_flags = BDRV_REQ_FUA & | ||||||
|         (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); |         bs->file->bs->supported_write_flags; | ||||||
|     bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | |     bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & | ||||||
|         ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & |         bs->file->bs->supported_zero_flags; | ||||||
|             bs->file->bs->supported_zero_flags); |  | ||||||
|  |  | ||||||
|     if (bs->probed && !bdrv_is_read_only(bs)) { |     if (bs->probed && !bdrv_is_read_only(bs)) { | ||||||
|         fprintf(stderr, |         fprintf(stderr, | ||||||
| @@ -459,6 +444,10 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void raw_close(BlockDriverState *bs) | ||||||
|  | { | ||||||
|  | } | ||||||
|  |  | ||||||
| static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) | static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||||
| { | { | ||||||
|     /* smallest possible positive score so that raw is used if and only if no |     /* smallest possible positive score so that raw is used if and only if no | ||||||
| @@ -493,44 +482,6 @@ static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) | |||||||
|     return bdrv_probe_geometry(bs->file->bs, geo); |     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 = { | BlockDriver bdrv_raw = { | ||||||
|     .format_name          = "raw", |     .format_name          = "raw", | ||||||
|     .instance_size        = sizeof(BDRVRawState), |     .instance_size        = sizeof(BDRVRawState), | ||||||
| @@ -539,6 +490,7 @@ BlockDriver bdrv_raw = { | |||||||
|     .bdrv_reopen_commit   = &raw_reopen_commit, |     .bdrv_reopen_commit   = &raw_reopen_commit, | ||||||
|     .bdrv_reopen_abort    = &raw_reopen_abort, |     .bdrv_reopen_abort    = &raw_reopen_abort, | ||||||
|     .bdrv_open            = &raw_open, |     .bdrv_open            = &raw_open, | ||||||
|  |     .bdrv_close           = &raw_close, | ||||||
|     .bdrv_child_perm      = bdrv_filter_default_perms, |     .bdrv_child_perm      = bdrv_filter_default_perms, | ||||||
|     .bdrv_co_create_opts  = &raw_co_create_opts, |     .bdrv_co_create_opts  = &raw_co_create_opts, | ||||||
|     .bdrv_co_preadv       = &raw_co_preadv, |     .bdrv_co_preadv       = &raw_co_preadv, | ||||||
| @@ -546,9 +498,7 @@ BlockDriver bdrv_raw = { | |||||||
|     .bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes, |     .bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes, | ||||||
|     .bdrv_co_pdiscard     = &raw_co_pdiscard, |     .bdrv_co_pdiscard     = &raw_co_pdiscard, | ||||||
|     .bdrv_co_block_status = &raw_co_block_status, |     .bdrv_co_block_status = &raw_co_block_status, | ||||||
|     .bdrv_co_copy_range_from = &raw_co_copy_range_from, |     .bdrv_truncate        = &raw_truncate, | ||||||
|     .bdrv_co_copy_range_to  = &raw_co_copy_range_to, |  | ||||||
|     .bdrv_co_truncate     = &raw_co_truncate, |  | ||||||
|     .bdrv_getlength       = &raw_getlength, |     .bdrv_getlength       = &raw_getlength, | ||||||
|     .has_variable_length  = true, |     .has_variable_length  = true, | ||||||
|     .bdrv_measure         = &raw_measure, |     .bdrv_measure         = &raw_measure, | ||||||
|   | |||||||
							
								
								
									
										244
									
								
								block/rbd.c
									
									
									
									
									
								
							
							
						
						
									
										244
									
								
								block/rbd.c
									
									
									
									
									
								
							| @@ -18,7 +18,6 @@ | |||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
| #include "qemu/option.h" | #include "qemu/option.h" | ||||||
| #include "block/block_int.h" | #include "block/block_int.h" | ||||||
| #include "block/qdict.h" |  | ||||||
| #include "crypto/secret.h" | #include "crypto/secret.h" | ||||||
| #include "qemu/cutils.h" | #include "qemu/cutils.h" | ||||||
| #include "qapi/qmp/qstring.h" | #include "qapi/qmp/qstring.h" | ||||||
| @@ -227,57 +226,27 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options, | |||||||
|  |  | ||||||
| done: | done: | ||||||
|     g_free(buf); |     g_free(buf); | ||||||
|     qobject_unref(keypairs); |     QDECREF(keypairs); | ||||||
|     return; |     return; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| static void qemu_rbd_refresh_limits(BlockDriverState *bs, Error **errp) | static int qemu_rbd_set_auth(rados_t cluster, const char *secretid, | ||||||
| { |  | ||||||
|     /* 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, |  | ||||||
|                              Error **errp) |                              Error **errp) | ||||||
| { | { | ||||||
|     char *key, *acr; |     if (secretid == 0) { | ||||||
|     int r; |         return 0; | ||||||
|     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 (opts->has_auth_client_required) { |     gchar *secret = qcrypto_secret_lookup_as_base64(secretid, | ||||||
|         accu = g_string_new(""); |                                                     errp); | ||||||
|         for (auth = opts->auth_client_required; auth; auth = auth->next) { |     if (!secret) { | ||||||
|             if (accu->str[0]) { |         return -1; | ||||||
|                 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; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     rados_conf_set(cluster, "key", secret); | ||||||
|  |     g_free(secret); | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -294,29 +263,29 @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json, | |||||||
|     if (!keypairs_json) { |     if (!keypairs_json) { | ||||||
|         return ret; |         return ret; | ||||||
|     } |     } | ||||||
|     keypairs = qobject_to(QList, |     keypairs = qobject_to_qlist(qobject_from_json(keypairs_json, | ||||||
|                           qobject_from_json(keypairs_json, &error_abort)); |                                                   &error_abort)); | ||||||
|     remaining = qlist_size(keypairs) / 2; |     remaining = qlist_size(keypairs) / 2; | ||||||
|     assert(remaining); |     assert(remaining); | ||||||
|  |  | ||||||
|     while (remaining--) { |     while (remaining--) { | ||||||
|         name = qobject_to(QString, qlist_pop(keypairs)); |         name = qobject_to_qstring(qlist_pop(keypairs)); | ||||||
|         value = qobject_to(QString, qlist_pop(keypairs)); |         value = qobject_to_qstring(qlist_pop(keypairs)); | ||||||
|         assert(name && value); |         assert(name && value); | ||||||
|         key = qstring_get_str(name); |         key = qstring_get_str(name); | ||||||
|  |  | ||||||
|         ret = rados_conf_set(cluster, key, qstring_get_str(value)); |         ret = rados_conf_set(cluster, key, qstring_get_str(value)); | ||||||
|         qobject_unref(value); |         QDECREF(value); | ||||||
|         if (ret < 0) { |         if (ret < 0) { | ||||||
|             error_setg_errno(errp, -ret, "invalid conf option %s", key); |             error_setg_errno(errp, -ret, "invalid conf option %s", key); | ||||||
|             qobject_unref(name); |             QDECREF(name); | ||||||
|             ret = -EINVAL; |             ret = -EINVAL; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         qobject_unref(name); |         QDECREF(name); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     qobject_unref(keypairs); |     QDECREF(keypairs); | ||||||
|     return ret; |     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, | static int qemu_rbd_do_create(BlockdevCreateOptions *options, | ||||||
|                               const char *keypairs, const char *password_secret, |                               const char *keypairs, const char *password_secret, | ||||||
|                               Error **errp) |                               Error **errp) | ||||||
| @@ -478,7 +449,7 @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename, | |||||||
|     } |     } | ||||||
|  |  | ||||||
| exit: | exit: | ||||||
|     qobject_unref(options); |     QDECREF(options); | ||||||
|     qapi_free_BlockdevCreateOptions(create_options); |     qapi_free_BlockdevCreateOptions(create_options); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| @@ -574,16 +545,6 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, | |||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|     int r; |     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); |     mon_host = qemu_rbd_mon_host(opts, &local_err); | ||||||
|     if (local_err) { |     if (local_err) { | ||||||
|         error_propagate(errp, 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 (qemu_rbd_set_auth(*cluster, secretid, errp) < 0) { | ||||||
|     if (r < 0) { |         r = -EIO; | ||||||
|         goto failed_shutdown; |         goto failed_shutdown; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -655,66 +616,33 @@ failed_opts: | |||||||
|     return r; |     return r; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int qemu_rbd_convert_options(QDict *options, BlockdevOptionsRbd **opts, |  | ||||||
|                                     Error **errp) |  | ||||||
| { |  | ||||||
|     Visitor *v; |  | ||||||
|     Error *local_err = NULL; |  | ||||||
|  |  | ||||||
|     /* Convert the remaining options into a QAPI object */ |  | ||||||
|     v = qobject_input_visitor_new_flat_confused(options, errp); |  | ||||||
|     if (!v) { |  | ||||||
|         return -EINVAL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     visit_type_BlockdevOptionsRbd(v, NULL, opts, &local_err); |  | ||||||
|     visit_free(v); |  | ||||||
|  |  | ||||||
|     if (local_err) { |  | ||||||
|         error_propagate(errp, local_err); |  | ||||||
|         return -EINVAL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int qemu_rbd_attempt_legacy_options(QDict *options, |  | ||||||
|                                            BlockdevOptionsRbd **opts, |  | ||||||
|                                            char **keypairs) |  | ||||||
| { |  | ||||||
|     char *filename; |  | ||||||
|     int r; |  | ||||||
|  |  | ||||||
|     filename = g_strdup(qdict_get_try_str(options, "filename")); |  | ||||||
|     if (!filename) { |  | ||||||
|         return -EINVAL; |  | ||||||
|     } |  | ||||||
|     qdict_del(options, "filename"); |  | ||||||
|  |  | ||||||
|     qemu_rbd_parse_filename(filename, options, NULL); |  | ||||||
|  |  | ||||||
|     /* keypairs freed by caller */ |  | ||||||
|     *keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs")); |  | ||||||
|     if (*keypairs) { |  | ||||||
|         qdict_del(options, "=keyvalue-pairs"); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     r = qemu_rbd_convert_options(options, opts, NULL); |  | ||||||
|  |  | ||||||
|     g_free(filename); |  | ||||||
|     return r; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, | static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, | ||||||
|                          Error **errp) |                          Error **errp) | ||||||
| { | { | ||||||
|     BDRVRBDState *s = bs->opaque; |     BDRVRBDState *s = bs->opaque; | ||||||
|     BlockdevOptionsRbd *opts = NULL; |     BlockdevOptionsRbd *opts = NULL; | ||||||
|     const QDictEntry *e; |     Visitor *v; | ||||||
|  |     QObject *crumpled = NULL; | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|  |     const char *filename; | ||||||
|     char *keypairs, *secretid; |     char *keypairs, *secretid; | ||||||
|     int r; |     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")); |     keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs")); | ||||||
|     if (keypairs) { |     if (keypairs) { | ||||||
|         qdict_del(options, "=keyvalue-pairs"); |         qdict_del(options, "=keyvalue-pairs"); | ||||||
| @@ -725,41 +653,24 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|         qdict_del(options, "password-secret"); |         qdict_del(options, "password-secret"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     r = qemu_rbd_convert_options(options, &opts, &local_err); |     /* Convert the remaining options into a QAPI object */ | ||||||
|  |     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) { |     if (local_err) { | ||||||
|         /* If keypairs are present, that means some options are present in |  | ||||||
|          * the modern option format.  Don't attempt to parse legacy option |  | ||||||
|          * formats, as we won't support mixed usage. */ |  | ||||||
|         if (keypairs) { |  | ||||||
|         error_propagate(errp, local_err); |         error_propagate(errp, local_err); | ||||||
|  |         r = -EINVAL; | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|         /* If the initial attempt to convert and process the options failed, |  | ||||||
|          * we may be attempting to open an image file that has the rbd options |  | ||||||
|          * specified in the older format consisting of all key/value pairs |  | ||||||
|          * encoded in the filename.  Go ahead and attempt to parse the |  | ||||||
|          * filename, and see if we can pull out the required options. */ |  | ||||||
|         r = qemu_rbd_attempt_legacy_options(options, &opts, &keypairs); |  | ||||||
|         if (r < 0) { |  | ||||||
|             /* Propagate the original error, not the legacy parsing fallback |  | ||||||
|              * error, as the latter was just a best-effort attempt. */ |  | ||||||
|             error_propagate(errp, local_err); |  | ||||||
|             goto out; |  | ||||||
|         } |  | ||||||
|         /* Take care whenever deciding to actually deprecate; once this ability |  | ||||||
|          * is removed, we will not be able to open any images with legacy-styled |  | ||||||
|          * backing image strings. */ |  | ||||||
|         warn_report("RBD options encoded in the filename as keyvalue pairs " |  | ||||||
|                     "is deprecated"); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* 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, |     r = qemu_rbd_connect(&s->cluster, &s->io_ctx, opts, | ||||||
|                          !(flags & BDRV_O_NOCACHE), keypairs, secretid, errp); |                          !(flags & BDRV_O_NOCACHE), keypairs, secretid, errp); | ||||||
|     if (r < 0) { |     if (r < 0) { | ||||||
| @@ -780,12 +691,18 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     /* If we are using an rbd snapshot, we must be r/o, otherwise |     /* If we are using an rbd snapshot, we must be r/o, otherwise | ||||||
|      * leave as-is */ |      * leave as-is */ | ||||||
|     if (s->snap != NULL) { |     if (s->snap != NULL) { | ||||||
|         r = bdrv_apply_auto_read_only(bs, "rbd snapshots are read-only", errp); |         if (!bdrv_is_read_only(bs)) { | ||||||
|  |             error_report("Opening rbd snapshots without an explicit " | ||||||
|  |                          "read-only=on option is deprecated. Future versions " | ||||||
|  |                          "will refuse to open the image instead of " | ||||||
|  |                          "automatically marking the image read-only."); | ||||||
|  |             r = bdrv_set_read_only(bs, true, &local_err); | ||||||
|             if (r < 0) { |             if (r < 0) { | ||||||
|             rbd_close(s->image); |                 error_propagate(errp, local_err); | ||||||
|                 goto failed_open; |                 goto failed_open; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     r = 0; |     r = 0; | ||||||
|     goto out; |     goto out; | ||||||
| @@ -975,23 +892,27 @@ failed: | |||||||
|     return NULL; |     return NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| static BlockAIOCB *qemu_rbd_aio_preadv(BlockDriverState *bs, | static BlockAIOCB *qemu_rbd_aio_readv(BlockDriverState *bs, | ||||||
|                                        uint64_t offset, uint64_t bytes, |                                       int64_t sector_num, | ||||||
|                                        QEMUIOVector *qiov, int flags, |                                       QEMUIOVector *qiov, | ||||||
|  |                                       int nb_sectors, | ||||||
|                                       BlockCompletionFunc *cb, |                                       BlockCompletionFunc *cb, | ||||||
|                                       void *opaque) |                                       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); |                          RBD_AIO_READ); | ||||||
| } | } | ||||||
|  |  | ||||||
| static BlockAIOCB *qemu_rbd_aio_pwritev(BlockDriverState *bs, | static BlockAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs, | ||||||
|                                         uint64_t offset, uint64_t bytes, |                                        int64_t sector_num, | ||||||
|                                         QEMUIOVector *qiov, int flags, |                                        QEMUIOVector *qiov, | ||||||
|  |                                        int nb_sectors, | ||||||
|                                        BlockCompletionFunc *cb, |                                        BlockCompletionFunc *cb, | ||||||
|                                        void *opaque) |                                        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); |                          RBD_AIO_WRITE); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1046,10 +967,8 @@ static int64_t qemu_rbd_getlength(BlockDriverState *bs) | |||||||
|     return info.size; |     return info.size; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coroutine_fn qemu_rbd_co_truncate(BlockDriverState *bs, | static int qemu_rbd_truncate(BlockDriverState *bs, int64_t offset, | ||||||
|                                              int64_t offset, |                              PreallocMode prealloc, Error **errp) | ||||||
|                                              PreallocMode prealloc, |  | ||||||
|                                              Error **errp) |  | ||||||
| { | { | ||||||
|     BDRVRBDState *s = bs->opaque; |     BDRVRBDState *s = bs->opaque; | ||||||
|     int r; |     int r; | ||||||
| @@ -1232,7 +1151,6 @@ static BlockDriver bdrv_rbd = { | |||||||
|     .format_name            = "rbd", |     .format_name            = "rbd", | ||||||
|     .instance_size          = sizeof(BDRVRBDState), |     .instance_size          = sizeof(BDRVRBDState), | ||||||
|     .bdrv_parse_filename    = qemu_rbd_parse_filename, |     .bdrv_parse_filename    = qemu_rbd_parse_filename, | ||||||
|     .bdrv_refresh_limits    = qemu_rbd_refresh_limits, |  | ||||||
|     .bdrv_file_open         = qemu_rbd_open, |     .bdrv_file_open         = qemu_rbd_open, | ||||||
|     .bdrv_close             = qemu_rbd_close, |     .bdrv_close             = qemu_rbd_close, | ||||||
|     .bdrv_reopen_prepare    = qemu_rbd_reopen_prepare, |     .bdrv_reopen_prepare    = qemu_rbd_reopen_prepare, | ||||||
| @@ -1242,11 +1160,11 @@ static BlockDriver bdrv_rbd = { | |||||||
|     .bdrv_get_info          = qemu_rbd_getinfo, |     .bdrv_get_info          = qemu_rbd_getinfo, | ||||||
|     .create_opts            = &qemu_rbd_create_opts, |     .create_opts            = &qemu_rbd_create_opts, | ||||||
|     .bdrv_getlength         = qemu_rbd_getlength, |     .bdrv_getlength         = qemu_rbd_getlength, | ||||||
|     .bdrv_co_truncate       = qemu_rbd_co_truncate, |     .bdrv_truncate          = qemu_rbd_truncate, | ||||||
|     .protocol_name          = "rbd", |     .protocol_name          = "rbd", | ||||||
|  |  | ||||||
|     .bdrv_aio_preadv        = qemu_rbd_aio_preadv, |     .bdrv_aio_readv         = qemu_rbd_aio_readv, | ||||||
|     .bdrv_aio_pwritev       = qemu_rbd_aio_pwritev, |     .bdrv_aio_writev        = qemu_rbd_aio_writev, | ||||||
|  |  | ||||||
| #ifdef LIBRBD_SUPPORTS_AIO_FLUSH | #ifdef LIBRBD_SUPPORTS_AIO_FLUSH | ||||||
|     .bdrv_aio_flush         = qemu_rbd_aio_flush, |     .bdrv_aio_flush         = qemu_rbd_aio_flush, | ||||||
|   | |||||||
| @@ -20,7 +20,6 @@ | |||||||
| #include "block/block_backup.h" | #include "block/block_backup.h" | ||||||
| #include "sysemu/block-backend.h" | #include "sysemu/block-backend.h" | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qapi/qmp/qdict.h" |  | ||||||
| #include "replication.h" | #include "replication.h" | ||||||
|  |  | ||||||
| typedef enum { | typedef enum { | ||||||
| @@ -40,8 +39,8 @@ typedef struct BDRVReplicationState { | |||||||
|     char *top_id; |     char *top_id; | ||||||
|     ReplicationState *rs; |     ReplicationState *rs; | ||||||
|     Error *blocker; |     Error *blocker; | ||||||
|     bool orig_hidden_read_only; |     int orig_hidden_flags; | ||||||
|     bool orig_secondary_read_only; |     int orig_secondary_flags; | ||||||
|     int error; |     int error; | ||||||
| } BDRVReplicationState; | } BDRVReplicationState; | ||||||
|  |  | ||||||
| @@ -146,7 +145,7 @@ static void replication_close(BlockDriverState *bs) | |||||||
|         replication_stop(s->rs, false, NULL); |         replication_stop(s->rs, false, NULL); | ||||||
|     } |     } | ||||||
|     if (s->stage == BLOCK_REPLICATION_FAILOVER) { |     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) { |     if (s->mode == REPLICATION_MODE_SECONDARY) { | ||||||
| @@ -219,6 +218,9 @@ static coroutine_fn int replication_co_readv(BlockDriverState *bs, | |||||||
|                                              QEMUIOVector *qiov) |                                              QEMUIOVector *qiov) | ||||||
| { | { | ||||||
|     BDRVReplicationState *s = bs->opaque; |     BDRVReplicationState *s = bs->opaque; | ||||||
|  |     BdrvChild *child = s->secondary_disk; | ||||||
|  |     BlockJob *job = NULL; | ||||||
|  |     CowRequest req; | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     if (s->mode == REPLICATION_MODE_PRIMARY) { |     if (s->mode == REPLICATION_MODE_PRIMARY) { | ||||||
| @@ -231,17 +233,34 @@ static coroutine_fn int replication_co_readv(BlockDriverState *bs, | |||||||
|         return ret; |         return ret; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ret = bdrv_co_preadv(bs->file, sector_num * BDRV_SECTOR_SIZE, |     if (child && child->bs) { | ||||||
|                          remaining_sectors * BDRV_SECTOR_SIZE, qiov, 0); |         job = child->bs->job; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (job) { | ||||||
|  |         uint64_t remaining_bytes = remaining_sectors * BDRV_SECTOR_SIZE; | ||||||
|  |  | ||||||
|  |         backup_wait_for_overlapping_requests(child->bs->job, | ||||||
|  |                                              sector_num * BDRV_SECTOR_SIZE, | ||||||
|  |                                              remaining_bytes); | ||||||
|  |         backup_cow_request_begin(&req, child->bs->job, | ||||||
|  |                                  sector_num * BDRV_SECTOR_SIZE, | ||||||
|  |                                  remaining_bytes); | ||||||
|  |         ret = bdrv_co_readv(bs->file, sector_num, remaining_sectors, | ||||||
|  |                             qiov); | ||||||
|  |         backup_cow_request_end(&req); | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ret = bdrv_co_readv(bs->file, sector_num, remaining_sectors, qiov); | ||||||
|  | out: | ||||||
|     return replication_return_value(s, ret); |     return replication_return_value(s, ret); | ||||||
| } | } | ||||||
|  |  | ||||||
| static coroutine_fn int replication_co_writev(BlockDriverState *bs, | static coroutine_fn int replication_co_writev(BlockDriverState *bs, | ||||||
|                                               int64_t sector_num, |                                               int64_t sector_num, | ||||||
|                                               int remaining_sectors, |                                               int remaining_sectors, | ||||||
|                                               QEMUIOVector *qiov, |                                               QEMUIOVector *qiov) | ||||||
|                                               int flags) |  | ||||||
| { | { | ||||||
|     BDRVReplicationState *s = bs->opaque; |     BDRVReplicationState *s = bs->opaque; | ||||||
|     QEMUIOVector hd_qiov; |     QEMUIOVector hd_qiov; | ||||||
| @@ -252,15 +271,14 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs, | |||||||
|     int ret; |     int ret; | ||||||
|     int64_t n; |     int64_t n; | ||||||
|  |  | ||||||
|     assert(!flags); |  | ||||||
|     ret = replication_get_io_status(s); |     ret = replication_get_io_status(s); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (ret == 0) { |     if (ret == 0) { | ||||||
|         ret = bdrv_co_pwritev(top, sector_num * BDRV_SECTOR_SIZE, |         ret = bdrv_co_writev(top, sector_num, | ||||||
|                               remaining_sectors * BDRV_SECTOR_SIZE, qiov, 0); |                              remaining_sectors, qiov); | ||||||
|         return replication_return_value(s, ret); |         return replication_return_value(s, ret); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -286,8 +304,7 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs, | |||||||
|         qemu_iovec_concat(&hd_qiov, qiov, bytes_done, count); |         qemu_iovec_concat(&hd_qiov, qiov, bytes_done, count); | ||||||
|  |  | ||||||
|         target = ret ? top : base; |         target = ret ? top : base; | ||||||
|         ret = bdrv_co_pwritev(target, sector_num * BDRV_SECTOR_SIZE, |         ret = bdrv_co_writev(target, sector_num, n, &hd_qiov); | ||||||
|                               n * BDRV_SECTOR_SIZE, &hd_qiov, 0); |  | ||||||
|         if (ret < 0) { |         if (ret < 0) { | ||||||
|             goto out1; |             goto out1; | ||||||
|         } |         } | ||||||
| @@ -350,38 +367,44 @@ static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| /* This function is supposed to be called twice: |  | ||||||
|  * first with writable = true, then with writable = false. |  | ||||||
|  * The first call puts s->hidden_disk and s->secondary_disk in |  | ||||||
|  * r/w mode, and the second puts them back in their original state. |  | ||||||
|  */ |  | ||||||
| static void reopen_backing_file(BlockDriverState *bs, bool writable, | static void reopen_backing_file(BlockDriverState *bs, bool writable, | ||||||
|                                 Error **errp) |                                 Error **errp) | ||||||
| { | { | ||||||
|     BDRVReplicationState *s = bs->opaque; |     BDRVReplicationState *s = bs->opaque; | ||||||
|     BlockReopenQueue *reopen_queue = NULL; |     BlockReopenQueue *reopen_queue = NULL; | ||||||
|  |     int orig_hidden_flags, orig_secondary_flags; | ||||||
|  |     int new_hidden_flags, new_secondary_flags; | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|  |  | ||||||
|     if (writable) { |     if (writable) { | ||||||
|         s->orig_hidden_read_only = bdrv_is_read_only(s->hidden_disk->bs); |         orig_hidden_flags = s->orig_hidden_flags = | ||||||
|         s->orig_secondary_read_only = bdrv_is_read_only(s->secondary_disk->bs); |                                 bdrv_get_flags(s->hidden_disk->bs); | ||||||
|  |         new_hidden_flags = (orig_hidden_flags | BDRV_O_RDWR) & | ||||||
|  |                                                     ~BDRV_O_INACTIVE; | ||||||
|  |         orig_secondary_flags = s->orig_secondary_flags = | ||||||
|  |                                 bdrv_get_flags(s->secondary_disk->bs); | ||||||
|  |         new_secondary_flags = (orig_secondary_flags | BDRV_O_RDWR) & | ||||||
|  |                                                      ~BDRV_O_INACTIVE; | ||||||
|  |     } else { | ||||||
|  |         orig_hidden_flags = (s->orig_hidden_flags | BDRV_O_RDWR) & | ||||||
|  |                                                     ~BDRV_O_INACTIVE; | ||||||
|  |         new_hidden_flags = s->orig_hidden_flags; | ||||||
|  |         orig_secondary_flags = (s->orig_secondary_flags | BDRV_O_RDWR) & | ||||||
|  |                                                     ~BDRV_O_INACTIVE; | ||||||
|  |         new_secondary_flags = s->orig_secondary_flags; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bdrv_subtree_drained_begin(s->hidden_disk->bs); |     bdrv_subtree_drained_begin(s->hidden_disk->bs); | ||||||
|     bdrv_subtree_drained_begin(s->secondary_disk->bs); |     bdrv_subtree_drained_begin(s->secondary_disk->bs); | ||||||
|  |  | ||||||
|     if (s->orig_hidden_read_only) { |     if (orig_hidden_flags != new_hidden_flags) { | ||||||
|         QDict *opts = qdict_new(); |         reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, NULL, | ||||||
|         qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable); |                                          new_hidden_flags); | ||||||
|         reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, |  | ||||||
|                                          opts); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (s->orig_secondary_read_only) { |     if (!(orig_secondary_flags & BDRV_O_RDWR)) { | ||||||
|         QDict *opts = qdict_new(); |  | ||||||
|         qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable); |  | ||||||
|         reopen_queue = bdrv_reopen_queue(reopen_queue, s->secondary_disk->bs, |         reopen_queue = bdrv_reopen_queue(reopen_queue, s->secondary_disk->bs, | ||||||
|                                          opts); |                                          NULL, new_secondary_flags); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (reopen_queue) { |     if (reopen_queue) { | ||||||
| @@ -543,7 +566,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | |||||||
|         job = backup_job_create(NULL, s->secondary_disk->bs, s->hidden_disk->bs, |         job = backup_job_create(NULL, s->secondary_disk->bs, s->hidden_disk->bs, | ||||||
|                                 0, MIRROR_SYNC_MODE_NONE, NULL, false, |                                 0, MIRROR_SYNC_MODE_NONE, NULL, false, | ||||||
|                                 BLOCKDEV_ON_ERROR_REPORT, |                                 BLOCKDEV_ON_ERROR_REPORT, | ||||||
|                                 BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL, |                                 BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL, | ||||||
|                                 backup_job_completed, bs, NULL, &local_err); |                                 backup_job_completed, bs, NULL, &local_err); | ||||||
|         if (local_err) { |         if (local_err) { | ||||||
|             error_propagate(errp, local_err); |             error_propagate(errp, local_err); | ||||||
| @@ -551,7 +574,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | |||||||
|             aio_context_release(aio_context); |             aio_context_release(aio_context); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         job_start(&job->job); |         block_job_start(job); | ||||||
|         break; |         break; | ||||||
|     default: |     default: | ||||||
|         aio_context_release(aio_context); |         aio_context_release(aio_context); | ||||||
| @@ -656,7 +679,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) | |||||||
|          * disk, secondary disk in backup_job_completed(). |          * disk, secondary disk in backup_job_completed(). | ||||||
|          */ |          */ | ||||||
|         if (s->secondary_disk->bs->job) { |         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) { |         if (!failover) { | ||||||
| @@ -668,7 +691,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) | |||||||
|  |  | ||||||
|         s->stage = BLOCK_REPLICATION_FAILOVER; |         s->stage = BLOCK_REPLICATION_FAILOVER; | ||||||
|         commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs, |         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); |                             NULL, replication_done, bs, true, errp); | ||||||
|         break; |         break; | ||||||
|     default: |     default: | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user