Compare commits
	
		
			5 Commits
		
	
	
		
			v4.0.0-rc0
			...
			rm-protoco
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 6321d78dc5 | ||
|  | a0974f99aa | ||
|  | 42883c01bf | ||
|  | 7966c2b312 | ||
|  | 5888011244 | 
							
								
								
									
										27
									
								
								.cirrus.yml
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								.cirrus.yml
									
									
									
									
									
								
							| @@ -1,27 +0,0 @@ | ||||
| env: | ||||
|   CIRRUS_CLONE_DEPTH: 1 | ||||
|  | ||||
| freebsd_12_task: | ||||
|   freebsd_instance: | ||||
|     image: freebsd-12-0-release-amd64 | ||||
|     cpu: 8 | ||||
|     memory: 8G | ||||
|   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 | ||||
|  | ||||
| macos_task: | ||||
|   osx_instance: | ||||
|     image: mojave-base | ||||
|   install_script: | ||||
|     - brew install pkg-config python glib pixman make sdl2 | ||||
|   script: | ||||
|     - ./configure --python=/usr/local/bin/python3 || { cat config.log; exit 1; } | ||||
|     - gmake -j$(sysctl -n hw.ncpu) | ||||
|     - gmake check -j$(sysctl -n hw.ncpu) | ||||
| @@ -1,10 +1,4 @@ | ||||
| # EditorConfig is a file format and collection of text editor plugins | ||||
| # for maintaining consistent coding styles between different editors | ||||
| # and IDEs. Most popular editors support this either natively or via | ||||
| # plugin. | ||||
| # | ||||
| # Check https://editorconfig.org for details. | ||||
|  | ||||
| # http://editorconfig.org | ||||
| root = true | ||||
|  | ||||
| [*] | ||||
| @@ -12,23 +6,10 @@ end_of_line = lf | ||||
| insert_final_newline = true | ||||
| charset = utf-8 | ||||
|  | ||||
| [*.mak] | ||||
| indent_style = tab | ||||
| indent_size = 8 | ||||
| file_type_emacs = makefile | ||||
|  | ||||
| [Makefile*] | ||||
| indent_style = tab | ||||
| indent_size = 8 | ||||
| file_type_emacs = makefile | ||||
|  | ||||
| [*.{c,h}] | ||||
| indent_style = space | ||||
| indent_size = 4 | ||||
|  | ||||
| [*.{vert,frag}] | ||||
| file_type_emacs = glsl | ||||
|  | ||||
| [*.json] | ||||
| indent_style = space | ||||
| file_type_emacs = python | ||||
|   | ||||
							
								
								
									
										79
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										79
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1,3 @@ | ||||
| /.doctrees | ||||
| /config-devices.* | ||||
| /config-all-devices.* | ||||
| /config-all-disas.* | ||||
| @@ -6,7 +5,6 @@ | ||||
| /config-target.* | ||||
| /config.status | ||||
| /config-temp | ||||
| /elf2dmp | ||||
| /trace-events-all | ||||
| /trace/generated-events.h | ||||
| /trace/generated-events.c | ||||
| @@ -32,21 +30,79 @@ | ||||
| /qapi-gen-timestamp | ||||
| /qapi/qapi-builtin-types.[ch] | ||||
| /qapi/qapi-builtin-visit.[ch] | ||||
| /qapi/qapi-commands-*.[ch] | ||||
| /qapi/qapi-commands-block-core.[ch] | ||||
| /qapi/qapi-commands-block.[ch] | ||||
| /qapi/qapi-commands-char.[ch] | ||||
| /qapi/qapi-commands-common.[ch] | ||||
| /qapi/qapi-commands-crypto.[ch] | ||||
| /qapi/qapi-commands-introspect.[ch] | ||||
| /qapi/qapi-commands-migration.[ch] | ||||
| /qapi/qapi-commands-misc.[ch] | ||||
| /qapi/qapi-commands-net.[ch] | ||||
| /qapi/qapi-commands-rocker.[ch] | ||||
| /qapi/qapi-commands-run-state.[ch] | ||||
| /qapi/qapi-commands-sockets.[ch] | ||||
| /qapi/qapi-commands-tpm.[ch] | ||||
| /qapi/qapi-commands-trace.[ch] | ||||
| /qapi/qapi-commands-transaction.[ch] | ||||
| /qapi/qapi-commands-ui.[ch] | ||||
| /qapi/qapi-commands.[ch] | ||||
| /qapi/qapi-emit-events.[ch] | ||||
| /qapi/qapi-events-*.[ch] | ||||
| /qapi/qapi-events-block-core.[ch] | ||||
| /qapi/qapi-events-block.[ch] | ||||
| /qapi/qapi-events-char.[ch] | ||||
| /qapi/qapi-events-common.[ch] | ||||
| /qapi/qapi-events-crypto.[ch] | ||||
| /qapi/qapi-events-introspect.[ch] | ||||
| /qapi/qapi-events-migration.[ch] | ||||
| /qapi/qapi-events-misc.[ch] | ||||
| /qapi/qapi-events-net.[ch] | ||||
| /qapi/qapi-events-rocker.[ch] | ||||
| /qapi/qapi-events-run-state.[ch] | ||||
| /qapi/qapi-events-sockets.[ch] | ||||
| /qapi/qapi-events-tpm.[ch] | ||||
| /qapi/qapi-events-trace.[ch] | ||||
| /qapi/qapi-events-transaction.[ch] | ||||
| /qapi/qapi-events-ui.[ch] | ||||
| /qapi/qapi-events.[ch] | ||||
| /qapi/qapi-introspect.[ch] | ||||
| /qapi/qapi-types-*.[ch] | ||||
| /qapi/qapi-types-block-core.[ch] | ||||
| /qapi/qapi-types-block.[ch] | ||||
| /qapi/qapi-types-char.[ch] | ||||
| /qapi/qapi-types-common.[ch] | ||||
| /qapi/qapi-types-crypto.[ch] | ||||
| /qapi/qapi-types-introspect.[ch] | ||||
| /qapi/qapi-types-migration.[ch] | ||||
| /qapi/qapi-types-misc.[ch] | ||||
| /qapi/qapi-types-net.[ch] | ||||
| /qapi/qapi-types-rocker.[ch] | ||||
| /qapi/qapi-types-run-state.[ch] | ||||
| /qapi/qapi-types-sockets.[ch] | ||||
| /qapi/qapi-types-tpm.[ch] | ||||
| /qapi/qapi-types-trace.[ch] | ||||
| /qapi/qapi-types-transaction.[ch] | ||||
| /qapi/qapi-types-ui.[ch] | ||||
| /qapi/qapi-types.[ch] | ||||
| /qapi/qapi-visit-*.[ch] | ||||
| /qapi/qapi-visit-block-core.[ch] | ||||
| /qapi/qapi-visit-block.[ch] | ||||
| /qapi/qapi-visit-char.[ch] | ||||
| /qapi/qapi-visit-common.[ch] | ||||
| /qapi/qapi-visit-crypto.[ch] | ||||
| /qapi/qapi-visit-introspect.[ch] | ||||
| /qapi/qapi-visit-migration.[ch] | ||||
| /qapi/qapi-visit-misc.[ch] | ||||
| /qapi/qapi-visit-net.[ch] | ||||
| /qapi/qapi-visit-rocker.[ch] | ||||
| /qapi/qapi-visit-run-state.[ch] | ||||
| /qapi/qapi-visit-sockets.[ch] | ||||
| /qapi/qapi-visit-tpm.[ch] | ||||
| /qapi/qapi-visit-trace.[ch] | ||||
| /qapi/qapi-visit-transaction.[ch] | ||||
| /qapi/qapi-visit-ui.[ch] | ||||
| /qapi/qapi-visit.[ch] | ||||
| /qapi/qapi-doc.texi | ||||
| /qemu-doc.html | ||||
| /qemu-doc.info | ||||
| /qemu-doc.txt | ||||
| /qemu-edid | ||||
| /qemu-img | ||||
| /qemu-nbd | ||||
| /qemu-options.def | ||||
| @@ -95,7 +151,6 @@ | ||||
| .sdk | ||||
| *.gcda | ||||
| *.gcno | ||||
| *.gcov | ||||
| /pc-bios/bios-pq/status | ||||
| /pc-bios/vgabios-pq/status | ||||
| /pc-bios/optionrom/linuxboot.asm | ||||
| @@ -106,10 +161,6 @@ | ||||
| /pc-bios/optionrom/linuxboot_dma.bin | ||||
| /pc-bios/optionrom/linuxboot_dma.raw | ||||
| /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.bin | ||||
| /pc-bios/optionrom/multiboot.raw | ||||
| @@ -120,7 +171,6 @@ | ||||
| /pc-bios/optionrom/kvmvapic.img | ||||
| /pc-bios/s390-ccw/s390-ccw.elf | ||||
| /pc-bios/s390-ccw/s390-ccw.img | ||||
| /docs/built | ||||
| /docs/interop/qemu-ga-qapi.texi | ||||
| /docs/interop/qemu-ga-ref.html | ||||
| /docs/interop/qemu-ga-ref.info* | ||||
| @@ -156,4 +206,3 @@ trace-dtrace-root.h | ||||
| trace-dtrace-root.dtrace | ||||
| trace-ust-all.h | ||||
| trace-ust-all.c | ||||
| /target/arm/decode-sve.inc.c | ||||
|   | ||||
| @@ -1,73 +0,0 @@ | ||||
| before_script: | ||||
|  - apt-get update -qq | ||||
|  - apt-get install -y -qq flex bison libglib2.0-dev libpixman-1-dev genisoimage | ||||
|  | ||||
| build-system1: | ||||
|  script: | ||||
|  - apt-get install -y -qq libgtk-3-dev libvte-dev nettle-dev libcacard-dev | ||||
|       libusb-dev libvde-dev libspice-protocol-dev libgl1-mesa-dev | ||||
|  - ./configure --enable-werror --target-list="aarch64-softmmu alpha-softmmu | ||||
|       cris-softmmu hppa-softmmu lm32-softmmu moxie-softmmu microblazeel-softmmu | ||||
|       mips64el-softmmu m68k-softmmu ppc-softmmu riscv64-softmmu sparc-softmmu" | ||||
|  - make -j2 | ||||
|  - make -j2 check | ||||
|  | ||||
| build-system2: | ||||
|  script: | ||||
|  - apt-get install -y -qq libsdl2-dev libgcrypt-dev libbrlapi-dev libaio-dev | ||||
|       libfdt-dev liblzo2-dev librdmacm-dev libibverbs-dev libibumad-dev | ||||
|  - ./configure --enable-werror --target-list="tricore-softmmu unicore32-softmmu | ||||
|       microblaze-softmmu mips-softmmu riscv32-softmmu s390x-softmmu sh4-softmmu | ||||
|       sparc64-softmmu x86_64-softmmu xtensa-softmmu nios2-softmmu or1k-softmmu" | ||||
|  - make -j2 | ||||
|  - make -j2 check | ||||
|  | ||||
| build-disabled: | ||||
|  script: | ||||
|  - ./configure --enable-werror --disable-rdma --disable-slirp --disable-curl | ||||
|       --disable-capstone --disable-live-block-migration --disable-glusterfs | ||||
|       --disable-replication --disable-coroutine-pool --disable-smartcard | ||||
|       --disable-guest-agent --disable-curses --disable-libxml2 --disable-tpm | ||||
|       --disable-qom-cast-debug --disable-spice --disable-vhost-vsock | ||||
|       --disable-vhost-net --disable-vhost-crypto --disable-vhost-user | ||||
|       --target-list="i386-softmmu ppc64-softmmu mips64-softmmu i386-linux-user" | ||||
|  - make -j2 | ||||
|  - make -j2 check-qtest SPEED=slow | ||||
|  | ||||
| build-tcg-disabled: | ||||
|  script: | ||||
|  - apt-get install -y -qq clang libgtk-3-dev libbluetooth-dev libusb-dev | ||||
|  - ./configure --cc=clang --enable-werror --disable-tcg --audio-drv-list="" | ||||
|  - make -j2 | ||||
|  - make check-unit | ||||
|  - make check-qapi-schema | ||||
|  - cd tests/qemu-iotests/ | ||||
|  - ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048 | ||||
|             052 063 077 086 101 104 106 113 147 148 150 151 152 157 159 160 | ||||
|             163 170 171 183 184 192 194 197 205 208 215 221 222 226 227 236 | ||||
|  - ./check -qcow2 001 002 003 004 005 007 008 009 010 011 012 013 017 018 019 | ||||
|             020 021 022 024 025 027 028 029 031 032 033 034 035 036 037 038 | ||||
|             039 040 042 043 046 047 048 049 050 051 052 053 054 056 057 058 | ||||
|             060 061 062 063 065 066 067 068 069 071 072 073 074 079 080 082 | ||||
|             085 086 089 090 091 095 096 097 098 099 102 103 104 105 107 108 | ||||
|             110 111 114 117 120 122 124 126 127 129 130 132 133 134 137 138 | ||||
|             139 140 141 142 143 144 145 147 150 151 152 154 155 156 157 158 | ||||
|             161 165 170 172 174 176 177 179 184 186 187 190 192 194 195 196 | ||||
|             197 200 202 203 205 208 209 214 215 216 217 218 222 226 227 229 234 | ||||
|  | ||||
| build-user: | ||||
|  script: | ||||
|  - ./configure --enable-werror --disable-system --disable-guest-agent | ||||
|                --disable-capstone --disable-slirp --disable-fdt | ||||
|  - make -j2 | ||||
|  - make run-tcg-tests-i386-linux-user run-tcg-tests-x86_64-linux-user | ||||
|  | ||||
| build-clang: | ||||
|  script: | ||||
|  - apt-get install -y -qq clang libsdl2-dev | ||||
|       xfslibs-dev libiscsi-dev libnfs-dev libseccomp-dev gnutls-dev librbd-dev | ||||
|  - ./configure --cc=clang --cxx=clang++ --enable-werror | ||||
|       --target-list="alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu | ||||
|                      ppc-softmmu s390x-softmmu x86_64-softmmu arm-linux-user" | ||||
|  - make -j2 | ||||
|  - make -j2 check | ||||
							
								
								
									
										42
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										42
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @@ -1,54 +1,48 @@ | ||||
| [submodule "roms/vgabios"] | ||||
| 	path = roms/vgabios | ||||
| 	url = git://git.qemu-project.org/vgabios.git/ | ||||
| [submodule "roms/seabios"] | ||||
| 	path = roms/seabios | ||||
| 	url = https://git.qemu.org/git/seabios.git/ | ||||
| 	url = git://git.qemu-project.org/seabios.git/ | ||||
| [submodule "roms/SLOF"] | ||||
| 	path = roms/SLOF | ||||
| 	url = https://git.qemu.org/git/SLOF.git | ||||
| 	url = git://git.qemu-project.org/SLOF.git | ||||
| [submodule "roms/ipxe"] | ||||
| 	path = roms/ipxe | ||||
| 	url = https://git.qemu.org/git/ipxe.git | ||||
| 	url = git://git.qemu-project.org/ipxe.git | ||||
| [submodule "roms/openbios"] | ||||
| 	path = roms/openbios | ||||
| 	url = https://git.qemu.org/git/openbios.git | ||||
| 	url = git://git.qemu-project.org/openbios.git | ||||
| [submodule "roms/openhackware"] | ||||
| 	path = roms/openhackware | ||||
| 	url = https://git.qemu.org/git/openhackware.git | ||||
| 	url = git://git.qemu-project.org/openhackware.git | ||||
| [submodule "roms/qemu-palcode"] | ||||
| 	path = roms/qemu-palcode | ||||
| 	url = https://git.qemu.org/git/qemu-palcode.git | ||||
| 	url = git://github.com/rth7680/qemu-palcode.git | ||||
| [submodule "roms/sgabios"] | ||||
| 	path = roms/sgabios | ||||
| 	url = https://git.qemu.org/git/sgabios.git | ||||
| 	url = git://git.qemu-project.org/sgabios.git | ||||
| [submodule "dtc"] | ||||
| 	path = dtc | ||||
| 	url = https://git.qemu.org/git/dtc.git | ||||
| 	url = git://git.qemu-project.org/dtc.git | ||||
| [submodule "roms/u-boot"] | ||||
| 	path = roms/u-boot | ||||
| 	url = https://git.qemu.org/git/u-boot.git | ||||
| 	url = git://git.qemu-project.org/u-boot.git | ||||
| [submodule "roms/skiboot"] | ||||
| 	path = roms/skiboot | ||||
| 	url = https://git.qemu.org/git/skiboot.git | ||||
| 	url = git://git.qemu.org/skiboot.git | ||||
| [submodule "roms/QemuMacDrivers"] | ||||
| 	path = roms/QemuMacDrivers | ||||
| 	url = https://git.qemu.org/git/QemuMacDrivers.git | ||||
| 	url = git://git.qemu.org/QemuMacDrivers.git | ||||
| [submodule "ui/keycodemapdb"] | ||||
| 	path = ui/keycodemapdb | ||||
| 	url = https://git.qemu.org/git/keycodemapdb.git | ||||
| 	url = git://git.qemu.org/keycodemapdb.git | ||||
| [submodule "capstone"] | ||||
| 	path = capstone | ||||
| 	url = https://git.qemu.org/git/capstone.git | ||||
| 	url = git://git.qemu.org/capstone.git | ||||
| [submodule "roms/seabios-hppa"] | ||||
| 	path = roms/seabios-hppa | ||||
| 	url = https://github.com/hdeller/seabios-hppa.git | ||||
| 	url = git://github.com/hdeller/seabios-hppa.git | ||||
| [submodule "roms/u-boot-sam460ex"] | ||||
| 	path = roms/u-boot-sam460ex | ||||
| 	url = https://git.qemu.org/git/u-boot-sam460ex.git | ||||
| [submodule "tests/fp/berkeley-testfloat-3"] | ||||
| 	path = tests/fp/berkeley-testfloat-3 | ||||
| 	url = https://github.com/cota/berkeley-testfloat-3 | ||||
| [submodule "tests/fp/berkeley-softfloat-3"] | ||||
| 	path = tests/fp/berkeley-softfloat-3 | ||||
| 	url = https://github.com/cota/berkeley-softfloat-3 | ||||
| [submodule "roms/edk2"] | ||||
| 	path = roms/edk2 | ||||
| 	url = https://github.com/tianocore/edk2.git | ||||
| 	url = git://github.com/zbalaton/u-boot-sam460ex | ||||
|   | ||||
							
								
								
									
										29
									
								
								.mailmap
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								.mailmap
									
									
									
									
									
								
							| @@ -1,7 +1,6 @@ | ||||
| # This mailmap fixes up author names/addresses. | ||||
|  | ||||
| # The first section translates weird addresses from the original git import | ||||
| # into proper addresses so that they are counted properly by git shortlog. | ||||
| # This mailmap just translates the weird addresses from the original import into git | ||||
| # into proper addresses so that they are counted properly in git shortlog output. | ||||
| # | ||||
| Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||
| Anthony Liguori <anthony@codemonkey.ws> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||
| Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com> | ||||
| @@ -12,28 +11,14 @@ Fabrice Bellard <fabrice@bellard.org> bellard <bellard@c046a42c-6fe2-441c-8c8c-7 | ||||
| James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com> | ||||
| Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||
| Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||
| Yongbok Kim <yongbok.kim@mips.com> <yongbok.kim@imgtec.com> | ||||
| Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@mips.com> | ||||
| Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@imgtec.com> | ||||
| Paul Burton <pburton@wavecomp.com> <paul.burton@mips.com> | ||||
| Paul Burton <pburton@wavecomp.com> <paul.burton@imgtec.com> | ||||
| Paul Burton <pburton@wavecomp.com> <paul@archlinuxmips.org> | ||||
| Paul Burton <paul.burton@mips.com> <paul.burton@imgtec.com> | ||||
| Paul Burton <paul.burton@mips.com> <paul@archlinuxmips.org> | ||||
| Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||
| malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||
|  | ||||
| # There is also a: | ||||
| #    (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||
| # for the cvs2svn initialization commit e63c3dc74bf. | ||||
|  | ||||
| # Next, translate a few commits where mailman rewrote the From: line due | ||||
| # to strict SPF, although we prefer to avoid adding more entries like that. | ||||
| Ed Swierk <eswierk@skyportsystems.com> Ed Swierk via Qemu-devel <qemu-devel@nongnu.org> | ||||
| Ian McKellar <ianloic@google.com> Ian McKellar via Qemu-devel <qemu-devel@nongnu.org> | ||||
| Julia Suvorova <jusual@mail.ru> Julia Suvorova via Qemu-devel <qemu-devel@nongnu.org> | ||||
| Justin Terry (VM) <juterry@microsoft.com> Justin Terry (VM) via Qemu-devel <qemu-devel@nongnu.org> | ||||
|  | ||||
|  | ||||
| # | ||||
| # Also list preferred name forms where people have changed their | ||||
| # git author config, or had utf8/latin1 encoding issues. | ||||
| # git author config | ||||
| Daniel P. Berrangé <berrange@redhat.com> | ||||
| Reimar Döffinger <Reimar.Doeffinger@gmx.de> | ||||
|   | ||||
| @@ -7,11 +7,10 @@ env: | ||||
|   matrix: | ||||
|     - IMAGE=debian-amd64 | ||||
|       TARGET_LIST=x86_64-softmmu,x86_64-linux-user | ||||
|     # currently disabled as the mxe.cc repos are down | ||||
|     # - IMAGE=debian-win32-cross | ||||
|     #   TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu | ||||
|     # - IMAGE=debian-win64-cross | ||||
|     #   TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu | ||||
|     - IMAGE=debian-win32-cross | ||||
|       TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu | ||||
|     - IMAGE=debian-win64-cross | ||||
|       TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu | ||||
|     - IMAGE=debian-armel-cross | ||||
|       TARGET_LIST=arm-softmmu,arm-linux-user,armeb-linux-user | ||||
|     - IMAGE=debian-armhf-cross | ||||
| @@ -36,5 +35,13 @@ build: | ||||
|     options: "-e HOME=/root" | ||||
|   ci: | ||||
|     - unset CC | ||||
|     # some targets require newer up to date packages, for example TARGET_LIST matching | ||||
|     # aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu) | ||||
|     # see the configure script: | ||||
|     #    error_exit "DTC (libfdt) version >= 1.4.2 not present. Your options:" | ||||
|     #    "  (1) Preferred: Install the DTC (libfdt) devel package" | ||||
|     #    "  (2) Fetch the DTC submodule, using:" | ||||
|     #    "      git submodule update --init dtc" | ||||
|     - dpkg --compare-versions `dpkg-query --showformat='${Version}' --show libfdt-dev` ge 1.4.2 || git submodule update --init dtc | ||||
|     - ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST} | ||||
|     - make -j$(($(getconf _NPROCESSORS_ONLN) + 1)) | ||||
|   | ||||
							
								
								
									
										287
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										287
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,13 +1,10 @@ | ||||
| # The current Travis default is a VM based 16.04 Xenial on GCE | ||||
| # Additional builds with specific requirements for a full VM need to | ||||
| # be added as additional matrix: entries later on | ||||
| dist: xenial | ||||
| sudo: false | ||||
| language: c | ||||
| python: | ||||
|   - "2.6" | ||||
| compiler: | ||||
|   - gcc | ||||
| cache: ccache | ||||
|  | ||||
|  | ||||
| addons: | ||||
|   apt: | ||||
|     packages: | ||||
| @@ -34,15 +31,9 @@ addons: | ||||
|       - libssh2-1-dev | ||||
|       - liburcu-dev | ||||
|       - libusb-1.0-0-dev | ||||
|       - libvte-2.91-dev | ||||
|       - libvte-2.90-dev | ||||
|       - sparse | ||||
|       - uuid-dev | ||||
|       - gcovr | ||||
|   homebrew: | ||||
|     packages: | ||||
|       - glib | ||||
|       - pixman | ||||
|  | ||||
|  | ||||
| # The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu | ||||
| # to prevent IRC notifications from forks. This was created using: | ||||
| @@ -53,183 +44,130 @@ notifications: | ||||
|       - secure: "F7GDRgjuOo5IUyRLqSkmDL7kvdU4UcH3Lm/W2db2JnDHTGCqgEdaYEYKciyCLZ57vOTsTsOgesN8iUT7hNHBd1KWKjZe9KDTZWppWRYVwAwQMzVeSOsbbU4tRoJ6Pp+3qhH1Z0eGYR9ZgKYAoTumDFgSAYRp4IscKS8jkoedOqM=" | ||||
|     on_success: change | ||||
|     on_failure: always | ||||
|  | ||||
|  | ||||
| env: | ||||
|   global: | ||||
|     - SRC_DIR="." | ||||
|     - BUILD_DIR="." | ||||
|     - BASE_CONFIG="--disable-docs --disable-tools" | ||||
|     - TEST_CMD="make check -j3 V=1" | ||||
|  | ||||
|  | ||||
|     - TEST_CMD="make check" | ||||
|     - MAKEFLAGS="-j3" | ||||
|   matrix: | ||||
|     - CONFIG="" | ||||
|     - CONFIG="--enable-debug --enable-debug-tcg --enable-trace-backends=log" | ||||
|     - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb" | ||||
|     - CONFIG="--enable-modules --disable-linux-user" | ||||
|     - CONFIG="--with-coroutine=ucontext --disable-linux-user" | ||||
|     - CONFIG="--with-coroutine=sigaltstack --disable-linux-user" | ||||
| git: | ||||
|   # we want to do this ourselves | ||||
|   submodules: false | ||||
|  | ||||
|  | ||||
| before_install: | ||||
|   - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update ; fi | ||||
|   - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install libffi gettext glib pixman ; fi | ||||
|   - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||
|   - git submodule update --init --recursive | ||||
| before_script: | ||||
|   - mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR} | ||||
|   - ${SRC_DIR}/configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; } | ||||
|   - ./configure ${CONFIG} | ||||
| script: | ||||
|   - make -j3 && ${TEST_CMD} | ||||
|  | ||||
|  | ||||
|   - make ${MAKEFLAGS} && ${TEST_CMD} | ||||
| matrix: | ||||
|   include: | ||||
|     - env: | ||||
|         - CONFIG="--disable-system" | ||||
|  | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--disable-user" | ||||
|  | ||||
|  | ||||
|     # Just build tools and run minimal unit and softfloat checks | ||||
|     - env: | ||||
|         - BASE_CONFIG="--enable-tools" | ||||
|         - CONFIG="--disable-user --disable-system" | ||||
|         - TEST_CMD="make check-unit check-softfloat -j3" | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--enable-debug --enable-debug-tcg --disable-user" | ||||
|  | ||||
|  | ||||
|     # TCG debug can be run just on it's own and is mostly agnostic to user/softmmu distinctions | ||||
|     - env: | ||||
|         - CONFIG="--enable-debug-tcg --disable-system" | ||||
|  | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-libusb --disable-user --disable-replication" | ||||
|  | ||||
|  | ||||
|     # Module builds are mostly of interest to major distros | ||||
|     - env: | ||||
|         - CONFIG="--enable-modules --target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu" | ||||
|  | ||||
|  | ||||
|     # 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 (out of tree) | ||||
|     - env: | ||||
|         - BUILD_DIR="out-of-tree/build/dir" SRC_DIR="../../.." | ||||
|         - BASE_CONFIG="--enable-tools --enable-docs" | ||||
|         - CONFIG="--target-list=x86_64-softmmu,aarch64-linux-user" | ||||
|       addons: | ||||
|         apt: | ||||
|           packages: | ||||
|             - python-sphinx | ||||
|             - texinfo | ||||
|             - perl | ||||
|  | ||||
|  | ||||
|     # Test with Clang for compile portability (Travis uses clang-5.0) | ||||
|     - env: | ||||
|         - CONFIG="--disable-system" | ||||
|     # Test with CLang for compile portability | ||||
|     - env: CONFIG="" | ||||
|       compiler: clang | ||||
|  | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--disable-user" | ||||
|       compiler: clang | ||||
|  | ||||
|  | ||||
|     # gprof/gcov are GCC features | ||||
|     - env: | ||||
|         - CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu" | ||||
|       after_success: | ||||
|         - ${SRC_DIR}/scripts/travis/coverage-summary.sh | ||||
|  | ||||
|  | ||||
|     - env: CONFIG="--enable-gprof --enable-gcov --disable-pie" | ||||
|       compiler: gcc | ||||
|     # We manually include builds which we disable "make check" for | ||||
|     - env: | ||||
|         - CONFIG="--without-default-devices" | ||||
|         - TEST_CMD="" | ||||
|  | ||||
|  | ||||
|     # We manually include builds which we disable "make check" for | ||||
|     - env: | ||||
|         - CONFIG="--enable-debug --enable-tcg-interpreter" | ||||
|         - TEST_CMD="" | ||||
|  | ||||
|  | ||||
|     # We don't need to exercise every backend with every front-end | ||||
|     - env: | ||||
|         - CONFIG="--enable-trace-backends=log,simple,syslog --disable-system" | ||||
|         - TEST_CMD="" | ||||
|  | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu" | ||||
|         - TEST_CMD="" | ||||
|  | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu" | ||||
|         - TEST_CMD="" | ||||
|  | ||||
|  | ||||
|     # MacOSX builds | ||||
|     - env: | ||||
|         - CONFIG="--target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu" | ||||
|     - env: CONFIG="--enable-debug --enable-tcg-interpreter" | ||||
|            TEST_CMD="" | ||||
|       compiler: gcc | ||||
|     - env: CONFIG="--enable-trace-backends=simple" | ||||
|            TEST_CMD="" | ||||
|       compiler: gcc | ||||
|     - env: CONFIG="--enable-trace-backends=ftrace" | ||||
|            TEST_CMD="" | ||||
|       compiler: gcc | ||||
|     - env: CONFIG="--enable-trace-backends=ust" | ||||
|            TEST_CMD="" | ||||
|       compiler: gcc | ||||
|     - env: CONFIG="--disable-tcg" | ||||
|            TEST_CMD="" | ||||
|       compiler: gcc | ||||
|     - env: CONFIG="" | ||||
|       os: osx | ||||
|       osx_image: xcode9.4 | ||||
|       compiler: clang | ||||
|  | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu" | ||||
|       os: osx | ||||
|       osx_image: xcode10.2 | ||||
|       compiler: clang | ||||
|  | ||||
|  | ||||
|     # Python builds | ||||
|     - env: | ||||
|         - CONFIG="--target-list=x86_64-softmmu" | ||||
|       language: python | ||||
|     # Plain Trusty System Build | ||||
|     - env: CONFIG="--disable-linux-user" | ||||
|       sudo: required | ||||
|       addons: | ||||
|       dist: trusty | ||||
|       compiler: gcc | ||||
|       before_install: | ||||
|         - sudo apt-get update -qq | ||||
|         - sudo apt-get build-dep -qq qemu | ||||
|         - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||
|         - git submodule update --init --recursive | ||||
|     # Plain Trusty Linux User Build | ||||
|     - env: CONFIG="--disable-system" | ||||
|       sudo: required | ||||
|       addons: | ||||
|       dist: trusty | ||||
|       compiler: gcc | ||||
|       before_install: | ||||
|         - sudo apt-get update -qq | ||||
|         - sudo apt-get build-dep -qq qemu | ||||
|         - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||
|         - git submodule update --init --recursive | ||||
|     # Trusty System build with latest stable clang & python 3.0 | ||||
|     - sudo: required | ||||
|       addons: | ||||
|       dist: trusty | ||||
|       language: generic | ||||
|       compiler: none | ||||
|       python: | ||||
|         - "3.4" | ||||
|  | ||||
|  | ||||
|     - env: | ||||
|         - CONFIG="--target-list=x86_64-softmmu" | ||||
|       language: python | ||||
|         - "3.0" | ||||
|       env: | ||||
|         - COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9 | ||||
|         - CONFIG="--disable-linux-user --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3" | ||||
|       before_install: | ||||
|         - wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - | ||||
|         - sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main' | ||||
|         - sudo apt-get update -qq | ||||
|         - sudo apt-get install -qq -y clang-3.9 | ||||
|         - sudo apt-get build-dep -qq qemu | ||||
|         - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||
|         - git submodule update --init --recursive | ||||
|       before_script: | ||||
|         - ./configure ${CONFIG} || cat config.log | ||||
|     # Trusty Linux User build with latest stable clang & python 3.6 | ||||
|     - sudo: required | ||||
|       addons: | ||||
|       dist: trusty | ||||
|       language: generic | ||||
|       compiler: none | ||||
|       python: | ||||
|         - "3.6" | ||||
|  | ||||
|  | ||||
|     # Acceptance (Functional) tests | ||||
|     - env: | ||||
|         - CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu" | ||||
|         - TEST_CMD="make AVOCADO_SHOW=app check-acceptance" | ||||
|       addons: | ||||
|         apt: | ||||
|           packages: | ||||
|             - python3-pip | ||||
|             - python3.5-venv | ||||
|       env: | ||||
|         - COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9 | ||||
|         - CONFIG="--disable-system --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3" | ||||
|       before_install: | ||||
|         - wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - | ||||
|         - sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main' | ||||
|         - sudo apt-get update -qq | ||||
|         - sudo apt-get install -qq -y clang-3.9 | ||||
|         - sudo apt-get build-dep -qq qemu | ||||
|         - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||
|         - git submodule update --init --recursive | ||||
|       before_script: | ||||
|         - ./configure ${CONFIG} || cat config.log | ||||
|     # Using newer GCC with sanitizers | ||||
|     - addons: | ||||
|         apt: | ||||
|           update: true | ||||
|           sources: | ||||
|             # PPAs for newer toolchains | ||||
|             - ubuntu-toolchain-r-test | ||||
|           packages: | ||||
|             # Extra toolchains | ||||
|             - gcc-7 | ||||
|             - g++-7 | ||||
|             - gcc-5 | ||||
|             - g++-5 | ||||
|             # Build dependencies | ||||
|             - libaio-dev | ||||
|             - libattr1-dev | ||||
| @@ -252,25 +190,14 @@ matrix: | ||||
|             - libssh2-1-dev | ||||
|             - liburcu-dev | ||||
|             - libusb-1.0-0-dev | ||||
|             - libvte-2.91-dev | ||||
|             - libvte-2.90-dev | ||||
|             - sparse | ||||
|             - uuid-dev | ||||
|       language: generic | ||||
|       compiler: none | ||||
|       env: | ||||
|         - COMPILER_NAME=gcc CXX=g++-7 CC=gcc-7 | ||||
|         - CONFIG="--cc=gcc-7 --cxx=g++-7 --disable-pie --disable-linux-user" | ||||
|         - COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5 | ||||
|         - CONFIG="--cc=gcc-5 --cxx=g++-5 --disable-pie --disable-linux-user" | ||||
|         - TEST_CMD="" | ||||
|       before_script: | ||||
|         - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || { cat config.log && exit 1; } | ||||
|  | ||||
|  | ||||
|     # Run check-tcg against linux-user | ||||
|     - env: | ||||
|         - CONFIG="--disable-system" | ||||
|         - TEST_CMD="make -j3 check-tcg V=1" | ||||
|  | ||||
|     # Run check-tcg against softmmu targets | ||||
|     - env: | ||||
|         - CONFIG="--target-list=xtensa-softmmu,arm-softmmu" | ||||
|         - TEST_CMD="make -j3 check-tcg V=1" | ||||
|         - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || cat config.log | ||||
|   | ||||
							
								
								
									
										17
									
								
								CODING_STYLE
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								CODING_STYLE
									
									
									
									
									
								
							| @@ -124,23 +124,6 @@ We use traditional C-style /* */ comments and avoid // comments. | ||||
| Rationale: The // form is valid in C99, so this is purely a matter of | ||||
| consistency of style. The checkpatch script will warn you about this. | ||||
|  | ||||
| Multiline comment blocks should have a row of stars on the left, | ||||
| and the initial /* and terminating */ both on their own lines: | ||||
|     /* | ||||
|      * like | ||||
|      * this | ||||
|      */ | ||||
| This is the same format required by the Linux kernel coding style. | ||||
|  | ||||
| (Some of the existing comments in the codebase use the GNU Coding | ||||
| Standards form which does not have stars on the left, or other | ||||
| variations; avoid these when writing new comments, but don't worry | ||||
| about converting to the preferred form unless you're editing that | ||||
| comment anyway.) | ||||
|  | ||||
| Rationale: Consistency, and ease of visually picking out a multiline | ||||
| comment from the surrounding code. | ||||
|  | ||||
| 8. trace-events style | ||||
|  | ||||
| 8.1 0x prefix | ||||
|   | ||||
							
								
								
									
										22
									
								
								COPYING.LIB
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								COPYING.LIB
									
									
									
									
									
								
							| @@ -1,8 +1,8 @@ | ||||
|                   GNU LESSER GENERAL PUBLIC LICENSE | ||||
|                        Version 2.1, February 1999 | ||||
| 		  GNU LESSER GENERAL PUBLIC LICENSE | ||||
| 		       Version 2.1, February 1999 | ||||
|  | ||||
|  Copyright (C) 1991, 1999 Free Software Foundation, Inc. | ||||
|  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA | ||||
| 	51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||
|  Everyone is permitted to copy and distribute verbatim copies | ||||
|  of this license document, but changing it is not allowed. | ||||
|  | ||||
| @@ -10,7 +10,7 @@ | ||||
|  as the successor of the GNU Library Public License, version 2, hence | ||||
|  the version number 2.1.] | ||||
|  | ||||
|                             Preamble | ||||
| 			    Preamble | ||||
|  | ||||
|   The licenses for most software are designed to take away your | ||||
| freedom to share and change it.  By contrast, the GNU General Public | ||||
| @@ -112,7 +112,7 @@ modification follow.  Pay close attention to the difference between a | ||||
| former contains code derived from the library, whereas the latter must | ||||
| be combined with the library in order to run. | ||||
|  | ||||
|                   GNU LESSER GENERAL PUBLIC LICENSE | ||||
| 		  GNU LESSER GENERAL PUBLIC LICENSE | ||||
|    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||||
|  | ||||
|   0. This License Agreement applies to any software library or other | ||||
| @@ -146,7 +146,7 @@ such a program is covered only if its contents constitute a work based | ||||
| on the Library (independent of the use of the Library in a tool for | ||||
| writing it).  Whether that is true depends on what the Library does | ||||
| and what the program that uses the Library does. | ||||
|  | ||||
|    | ||||
|   1. You may copy and distribute verbatim copies of the Library's | ||||
| complete source code as you receive it, in any medium, provided that | ||||
| you conspicuously and appropriately publish on each copy an | ||||
| @@ -432,7 +432,7 @@ decision will be guided by the two goals of preserving the free status | ||||
| of all derivatives of our free software and of promoting the sharing | ||||
| and reuse of software generally. | ||||
|  | ||||
|                             NO WARRANTY | ||||
| 			    NO WARRANTY | ||||
|  | ||||
|   15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO | ||||
| WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. | ||||
| @@ -455,7 +455,7 @@ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF | ||||
| SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | ||||
| DAMAGES. | ||||
|  | ||||
|                      END OF TERMS AND CONDITIONS | ||||
| 		     END OF TERMS AND CONDITIONS | ||||
|  | ||||
|            How to Apply These Terms to Your New Libraries | ||||
|  | ||||
| @@ -476,7 +476,7 @@ convey the exclusion of warranty; and each file should have at least the | ||||
|     This library is free software; you can redistribute it and/or | ||||
|     modify it under the terms of the GNU Lesser General Public | ||||
|     License as published by the Free Software Foundation; either | ||||
|     version 2.1 of the License, or (at your option) any later version. | ||||
|     version 2 of the License, or (at your option) any later version. | ||||
|  | ||||
|     This library is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| @@ -485,7 +485,7 @@ convey the exclusion of warranty; and each file should have at least the | ||||
|  | ||||
|     You should have received a copy of the GNU Lesser General Public | ||||
|     License along with this library; if not, write to the Free Software | ||||
|     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA | ||||
|     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||
|  | ||||
| Also add information on how to contact you by electronic and paper mail. | ||||
|  | ||||
| @@ -500,3 +500,5 @@ necessary.  Here is a sample; alter the names: | ||||
|   Ty Coon, President of Vice | ||||
|  | ||||
| That's all there is to it! | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										270
									
								
								COPYING.PYTHON
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										270
									
								
								COPYING.PYTHON
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,270 @@ | ||||
| A. HISTORY OF THE SOFTWARE | ||||
| ========================== | ||||
|  | ||||
| Python was created in the early 1990s by Guido van Rossum at Stichting | ||||
| Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands | ||||
| as a successor of a language called ABC.  Guido remains Python's | ||||
| principal author, although it includes many contributions from others. | ||||
|  | ||||
| In 1995, Guido continued his work on Python at the Corporation for | ||||
| National Research Initiatives (CNRI, see http://www.cnri.reston.va.us) | ||||
| in Reston, Virginia where he released several versions of the | ||||
| software. | ||||
|  | ||||
| In May 2000, Guido and the Python core development team moved to | ||||
| BeOpen.com to form the BeOpen PythonLabs team.  In October of the same | ||||
| year, the PythonLabs team moved to Digital Creations (now Zope | ||||
| Corporation, see http://www.zope.com).  In 2001, the Python Software | ||||
| Foundation (PSF, see http://www.python.org/psf/) was formed, a | ||||
| non-profit organization created specifically to own Python-related | ||||
| Intellectual Property.  Zope Corporation is a sponsoring member of | ||||
| the PSF. | ||||
|  | ||||
| All Python releases are Open Source (see http://www.opensource.org for | ||||
| the Open Source Definition).  Historically, most, but not all, Python | ||||
| releases have also been GPL-compatible; the table below summarizes | ||||
| the various releases. | ||||
|  | ||||
|     Release         Derived     Year        Owner       GPL- | ||||
|                     from                                compatible? (1) | ||||
|  | ||||
|     0.9.0 thru 1.2              1991-1995   CWI         yes | ||||
|     1.3 thru 1.5.2  1.2         1995-1999   CNRI        yes | ||||
|     1.6             1.5.2       2000        CNRI        no | ||||
|     2.0             1.6         2000        BeOpen.com  no | ||||
|     1.6.1           1.6         2001        CNRI        yes (2) | ||||
|     2.1             2.0+1.6.1   2001        PSF         no | ||||
|     2.0.1           2.0+1.6.1   2001        PSF         yes | ||||
|     2.1.1           2.1+2.0.1   2001        PSF         yes | ||||
|     2.2             2.1.1       2001        PSF         yes | ||||
|     2.1.2           2.1.1       2002        PSF         yes | ||||
|     2.1.3           2.1.2       2002        PSF         yes | ||||
|     2.2.1           2.2         2002        PSF         yes | ||||
|     2.2.2           2.2.1       2002        PSF         yes | ||||
|     2.2.3           2.2.2       2003        PSF         yes | ||||
|     2.3             2.2.2       2002-2003   PSF         yes | ||||
|     2.3.1           2.3         2002-2003   PSF         yes | ||||
|     2.3.2           2.3.1       2002-2003   PSF         yes | ||||
|     2.3.3           2.3.2       2002-2003   PSF         yes | ||||
|     2.3.4           2.3.3       2004        PSF         yes | ||||
|     2.3.5           2.3.4       2005        PSF         yes | ||||
|     2.4             2.3         2004        PSF         yes | ||||
|     2.4.1           2.4         2005        PSF         yes | ||||
|     2.4.2           2.4.1       2005        PSF         yes | ||||
|     2.4.3           2.4.2       2006        PSF         yes | ||||
|     2.5             2.4         2006        PSF         yes | ||||
|     2.7             2.6         2010        PSF         yes | ||||
|  | ||||
| Footnotes: | ||||
|  | ||||
| (1) GPL-compatible doesn't mean that we're distributing Python under | ||||
|     the GPL.  All Python licenses, unlike the GPL, let you distribute | ||||
|     a modified version without making your changes open source.  The | ||||
|     GPL-compatible licenses make it possible to combine Python with | ||||
|     other software that is released under the GPL; the others don't. | ||||
|  | ||||
| (2) According to Richard Stallman, 1.6.1 is not GPL-compatible, | ||||
|     because its license has a choice of law clause.  According to | ||||
|     CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 | ||||
|     is "not incompatible" with the GPL. | ||||
|  | ||||
| Thanks to the many outside volunteers who have worked under Guido's | ||||
| direction to make these releases possible. | ||||
|  | ||||
|  | ||||
| B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON | ||||
| =============================================================== | ||||
|  | ||||
| PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 | ||||
| -------------------------------------------- | ||||
|  | ||||
| 1. This LICENSE AGREEMENT is between the Python Software Foundation | ||||
| ("PSF"), and the Individual or Organization ("Licensee") accessing and | ||||
| otherwise using this software ("Python") in source or binary form and | ||||
| its associated documentation. | ||||
|  | ||||
| 2. Subject to the terms and conditions of this License Agreement, PSF | ||||
| hereby grants Licensee a nonexclusive, royalty-free, world-wide | ||||
| license to reproduce, analyze, test, perform and/or display publicly, | ||||
| prepare derivative works, distribute, and otherwise use Python | ||||
| alone or in any derivative version, provided, however, that PSF's | ||||
| License Agreement and PSF's notice of copyright, i.e., "Copyright (c) | ||||
| 2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation; All Rights | ||||
| Reserved" are retained in Python alone or in any derivative version  | ||||
| prepared by Licensee. | ||||
|  | ||||
| 3. In the event Licensee prepares a derivative work that is based on | ||||
| or incorporates Python or any part thereof, and wants to make | ||||
| the derivative work available to others as provided herein, then | ||||
| Licensee hereby agrees to include in any such work a brief summary of | ||||
| the changes made to Python. | ||||
|  | ||||
| 4. PSF is making Python available to Licensee on an "AS IS" | ||||
| basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR | ||||
| IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND | ||||
| DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS | ||||
| FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT | ||||
| INFRINGE ANY THIRD PARTY RIGHTS. | ||||
|  | ||||
| 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON | ||||
| FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS | ||||
| A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, | ||||
| OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. | ||||
|  | ||||
| 6. This License Agreement will automatically terminate upon a material | ||||
| breach of its terms and conditions. | ||||
|  | ||||
| 7. Nothing in this License Agreement shall be deemed to create any | ||||
| relationship of agency, partnership, or joint venture between PSF and | ||||
| Licensee.  This License Agreement does not grant permission to use PSF | ||||
| trademarks or trade name in a trademark sense to endorse or promote | ||||
| products or services of Licensee, or any third party. | ||||
|  | ||||
| 8. By copying, installing or otherwise using Python, Licensee | ||||
| agrees to be bound by the terms and conditions of this License | ||||
| Agreement. | ||||
|  | ||||
|  | ||||
| BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 | ||||
| ------------------------------------------- | ||||
|  | ||||
| BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 | ||||
|  | ||||
| 1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an | ||||
| office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the | ||||
| Individual or Organization ("Licensee") accessing and otherwise using | ||||
| this software in source or binary form and its associated | ||||
| documentation ("the Software"). | ||||
|  | ||||
| 2. Subject to the terms and conditions of this BeOpen Python License | ||||
| Agreement, BeOpen hereby grants Licensee a non-exclusive, | ||||
| royalty-free, world-wide license to reproduce, analyze, test, perform | ||||
| and/or display publicly, prepare derivative works, distribute, and | ||||
| otherwise use the Software alone or in any derivative version, | ||||
| provided, however, that the BeOpen Python License is retained in the | ||||
| Software, alone or in any derivative version prepared by Licensee. | ||||
|  | ||||
| 3. BeOpen is making the Software available to Licensee on an "AS IS" | ||||
| basis.  BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR | ||||
| IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND | ||||
| DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS | ||||
| FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT | ||||
| INFRINGE ANY THIRD PARTY RIGHTS. | ||||
|  | ||||
| 4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE | ||||
| SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS | ||||
| AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY | ||||
| DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. | ||||
|  | ||||
| 5. This License Agreement will automatically terminate upon a material | ||||
| breach of its terms and conditions. | ||||
|  | ||||
| 6. This License Agreement shall be governed by and interpreted in all | ||||
| respects by the law of the State of California, excluding conflict of | ||||
| law provisions.  Nothing in this License Agreement shall be deemed to | ||||
| create any relationship of agency, partnership, or joint venture | ||||
| between BeOpen and Licensee.  This License Agreement does not grant | ||||
| permission to use BeOpen trademarks or trade names in a trademark | ||||
| sense to endorse or promote products or services of Licensee, or any | ||||
| third party.  As an exception, the "BeOpen Python" logos available at | ||||
| http://www.pythonlabs.com/logos.html may be used according to the | ||||
| permissions granted on that web page. | ||||
|  | ||||
| 7. By copying, installing or otherwise using the software, Licensee | ||||
| agrees to be bound by the terms and conditions of this License | ||||
| Agreement. | ||||
|  | ||||
|  | ||||
| CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 | ||||
| --------------------------------------- | ||||
|  | ||||
| 1. This LICENSE AGREEMENT is between the Corporation for National | ||||
| Research Initiatives, having an office at 1895 Preston White Drive, | ||||
| Reston, VA 20191 ("CNRI"), and the Individual or Organization | ||||
| ("Licensee") accessing and otherwise using Python 1.6.1 software in | ||||
| source or binary form and its associated documentation. | ||||
|  | ||||
| 2. Subject to the terms and conditions of this License Agreement, CNRI | ||||
| hereby grants Licensee a nonexclusive, royalty-free, world-wide | ||||
| license to reproduce, analyze, test, perform and/or display publicly, | ||||
| prepare derivative works, distribute, and otherwise use Python 1.6.1 | ||||
| alone or in any derivative version, provided, however, that CNRI's | ||||
| License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) | ||||
| 1995-2001 Corporation for National Research Initiatives; All Rights | ||||
| Reserved" are retained in Python 1.6.1 alone or in any derivative | ||||
| version prepared by Licensee.  Alternately, in lieu of CNRI's License | ||||
| Agreement, Licensee may substitute the following text (omitting the | ||||
| quotes): "Python 1.6.1 is made available subject to the terms and | ||||
| conditions in CNRI's License Agreement.  This Agreement together with | ||||
| Python 1.6.1 may be located on the Internet using the following | ||||
| unique, persistent identifier (known as a handle): 1895.22/1013.  This | ||||
| Agreement may also be obtained from a proxy server on the Internet | ||||
| using the following URL: http://hdl.handle.net/1895.22/1013". | ||||
|  | ||||
| 3. In the event Licensee prepares a derivative work that is based on | ||||
| or incorporates Python 1.6.1 or any part thereof, and wants to make | ||||
| the derivative work available to others as provided herein, then | ||||
| Licensee hereby agrees to include in any such work a brief summary of | ||||
| the changes made to Python 1.6.1. | ||||
|  | ||||
| 4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" | ||||
| basis.  CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR | ||||
| IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND | ||||
| DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS | ||||
| FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT | ||||
| INFRINGE ANY THIRD PARTY RIGHTS. | ||||
|  | ||||
| 5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON | ||||
| 1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS | ||||
| A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, | ||||
| OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. | ||||
|  | ||||
| 6. This License Agreement will automatically terminate upon a material | ||||
| breach of its terms and conditions. | ||||
|  | ||||
| 7. This License Agreement shall be governed by the federal | ||||
| intellectual property law of the United States, including without | ||||
| limitation the federal copyright law, and, to the extent such | ||||
| U.S. federal law does not apply, by the law of the Commonwealth of | ||||
| Virginia, excluding Virginia's conflict of law provisions. | ||||
| Notwithstanding the foregoing, with regard to derivative works based | ||||
| on Python 1.6.1 that incorporate non-separable material that was | ||||
| previously distributed under the GNU General Public License (GPL), the | ||||
| law of the Commonwealth of Virginia shall govern this License | ||||
| Agreement only as to issues arising under or with respect to | ||||
| Paragraphs 4, 5, and 7 of this License Agreement.  Nothing in this | ||||
| License Agreement shall be deemed to create any relationship of | ||||
| agency, partnership, or joint venture between CNRI and Licensee.  This | ||||
| License Agreement does not grant permission to use CNRI trademarks or | ||||
| trade name in a trademark sense to endorse or promote products or | ||||
| services of Licensee, or any third party. | ||||
|  | ||||
| 8. By clicking on the "ACCEPT" button where indicated, or by copying, | ||||
| installing or otherwise using Python 1.6.1, Licensee agrees to be | ||||
| bound by the terms and conditions of this License Agreement. | ||||
|  | ||||
|         ACCEPT | ||||
|  | ||||
|  | ||||
| CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 | ||||
| -------------------------------------------------- | ||||
|  | ||||
| Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, | ||||
| The Netherlands.  All rights reserved. | ||||
|  | ||||
| Permission to use, copy, modify, and distribute this software and its | ||||
| documentation for any purpose and without fee is hereby granted, | ||||
| provided that the above copyright notice appear in all copies and that | ||||
| both that copyright notice and this permission notice appear in | ||||
| supporting documentation, and that the name of Stichting Mathematisch | ||||
| Centrum or CWI not be used in advertising or publicity pertaining to | ||||
| distribution of the software without specific, written prior | ||||
| permission. | ||||
|  | ||||
| STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO | ||||
| THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | ||||
| FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE | ||||
| FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | ||||
| OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
							
								
								
									
										9
									
								
								HACKING
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								HACKING
									
									
									
									
									
								
							| @@ -118,15 +118,6 @@ Please note that g_malloc will exit on allocation failure, so there | ||||
| is no need to test for failure (as you would have to with malloc). | ||||
| Calling g_malloc with a zero size is valid and will return NULL. | ||||
|  | ||||
| Prefer g_new(T, n) instead of g_malloc(sizeof(T) * n) for the following | ||||
| reasons: | ||||
|  | ||||
|   a. It catches multiplication overflowing size_t; | ||||
|   b. It returns T * instead of void *, letting compiler catch more type | ||||
|      errors. | ||||
|  | ||||
| Declarations like T *v = g_malloc(sizeof(*v)) are acceptable, though. | ||||
|  | ||||
| Memory allocated by qemu_memalign or qemu_blockalign must be freed with | ||||
| qemu_vfree, since breaking this will cause problems on Win32. | ||||
|  | ||||
|   | ||||
							
								
								
									
										33
									
								
								Kconfig.host
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								Kconfig.host
									
									
									
									
									
								
							| @@ -1,33 +0,0 @@ | ||||
| # These are "proxy" symbols used to pass config-host.mak values | ||||
| # down to Kconfig.  See also MINIKCONF_ARGS in the Makefile: | ||||
| # these two need to be kept in sync. | ||||
|  | ||||
| config KVM | ||||
|     bool | ||||
|  | ||||
| config LINUX | ||||
|     bool | ||||
|  | ||||
| config OPENGL | ||||
|     bool | ||||
|  | ||||
| config X11 | ||||
|     bool | ||||
|  | ||||
| config SPICE | ||||
|     bool | ||||
|  | ||||
| config IVSHMEM | ||||
|     bool | ||||
|  | ||||
| config TPM | ||||
|     bool | ||||
|  | ||||
| config VHOST_USER | ||||
|     bool | ||||
|  | ||||
| config XEN | ||||
|     bool | ||||
|  | ||||
| config VIRTFS | ||||
|     bool | ||||
							
								
								
									
										826
									
								
								MAINTAINERS
									
									
									
									
									
								
							
							
						
						
									
										826
									
								
								MAINTAINERS
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										439
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										439
									
								
								Makefile
									
									
									
									
									
								
							| @@ -20,6 +20,8 @@ ifneq ($(wildcard config-host.mak),) | ||||
| all: | ||||
| include config-host.mak | ||||
|  | ||||
| PYTHON_UTF8 = LC_ALL= LANG=C LC_CTYPE=en_US.UTF-8 $(PYTHON) | ||||
|  | ||||
| git-submodule-update: | ||||
|  | ||||
| .PHONY: git-submodule-update | ||||
| @@ -67,7 +69,7 @@ CONFIG_ALL=y | ||||
| -include config-all-devices.mak | ||||
| -include config-all-disas.mak | ||||
|  | ||||
| config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios $(SRC_PATH)/VERSION | ||||
| config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios | ||||
| 	@echo $@ is out-of-date, running configure | ||||
| 	@# TODO: The next lines include code which supports a smooth | ||||
| 	@# transition from old configurations without config.status. | ||||
| @@ -87,41 +89,79 @@ endif | ||||
|  | ||||
| include $(SRC_PATH)/rules.mak | ||||
|  | ||||
| # Create QEMU_PKGVERSION and FULL_VERSION strings | ||||
| # If PKGVERSION is set, use that; otherwise get version and -dirty status from git | ||||
| QEMU_PKGVERSION := $(if $(PKGVERSION),$(PKGVERSION),$(shell \ | ||||
|   cd $(SRC_PATH); \ | ||||
|   if test -e .git; then \ | ||||
|     git describe --match 'v*' 2>/dev/null | tr -d '\n'; \ | ||||
|     if ! git diff-index --quiet HEAD &>/dev/null; then \ | ||||
|       echo "-dirty"; \ | ||||
|     fi; \ | ||||
|   fi)) | ||||
|  | ||||
| # Either "version (pkgversion)", or just "version" if pkgversion not set | ||||
| FULL_VERSION := $(if $(QEMU_PKGVERSION),$(VERSION) ($(QEMU_PKGVERSION)),$(VERSION)) | ||||
|  | ||||
| GENERATED_FILES = qemu-version.h config-host.h qemu-options.def | ||||
|  | ||||
| GENERATED_QAPI_FILES = qapi/qapi-builtin-types.h qapi/qapi-builtin-types.c | ||||
| GENERATED_QAPI_FILES += qapi/qapi-types.h qapi/qapi-types.c | ||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-types-%.h) | ||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-types-%.c) | ||||
| GENERATED_QAPI_FILES += qapi/qapi-builtin-visit.h qapi/qapi-builtin-visit.c | ||||
| GENERATED_QAPI_FILES += qapi/qapi-visit.h qapi/qapi-visit.c | ||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.h) | ||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.c) | ||||
| GENERATED_QAPI_FILES += qapi/qapi-commands.h qapi/qapi-commands.c | ||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.h) | ||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.c) | ||||
| GENERATED_QAPI_FILES += qapi/qapi-emit-events.h qapi/qapi-emit-events.c | ||||
| GENERATED_QAPI_FILES += qapi/qapi-events.h qapi/qapi-events.c | ||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.h) | ||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.c) | ||||
| GENERATED_QAPI_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h | ||||
| GENERATED_QAPI_FILES += qapi/qapi-doc.texi | ||||
|  | ||||
| GENERATED_FILES += $(GENERATED_QAPI_FILES) | ||||
| GENERATED_FILES += qapi/qapi-builtin-types.h qapi/qapi-builtin-types.c | ||||
| GENERATED_FILES += qapi/qapi-types.h qapi/qapi-types.c | ||||
| GENERATED_FILES += qapi/qapi-types-block-core.h qapi/qapi-types-block-core.c | ||||
| GENERATED_FILES += qapi/qapi-types-block.h qapi/qapi-types-block.c | ||||
| GENERATED_FILES += qapi/qapi-types-char.h qapi/qapi-types-char.c | ||||
| GENERATED_FILES += qapi/qapi-types-common.h qapi/qapi-types-common.c | ||||
| GENERATED_FILES += qapi/qapi-types-crypto.h qapi/qapi-types-crypto.c | ||||
| GENERATED_FILES += qapi/qapi-types-introspect.h qapi/qapi-types-introspect.c | ||||
| GENERATED_FILES += qapi/qapi-types-migration.h qapi/qapi-types-migration.c | ||||
| GENERATED_FILES += qapi/qapi-types-misc.h qapi/qapi-types-misc.c | ||||
| GENERATED_FILES += qapi/qapi-types-net.h qapi/qapi-types-net.c | ||||
| GENERATED_FILES += qapi/qapi-types-rocker.h qapi/qapi-types-rocker.c | ||||
| GENERATED_FILES += qapi/qapi-types-run-state.h qapi/qapi-types-run-state.c | ||||
| GENERATED_FILES += qapi/qapi-types-sockets.h qapi/qapi-types-sockets.c | ||||
| GENERATED_FILES += qapi/qapi-types-tpm.h qapi/qapi-types-tpm.c | ||||
| GENERATED_FILES += qapi/qapi-types-trace.h qapi/qapi-types-trace.c | ||||
| GENERATED_FILES += qapi/qapi-types-transaction.h qapi/qapi-types-transaction.c | ||||
| GENERATED_FILES += qapi/qapi-types-ui.h qapi/qapi-types-ui.c | ||||
| GENERATED_FILES += qapi/qapi-builtin-visit.h qapi/qapi-builtin-visit.c | ||||
| GENERATED_FILES += qapi/qapi-visit.h qapi/qapi-visit.c | ||||
| GENERATED_FILES += qapi/qapi-visit-block-core.h qapi/qapi-visit-block-core.c | ||||
| GENERATED_FILES += qapi/qapi-visit-block.h qapi/qapi-visit-block.c | ||||
| GENERATED_FILES += qapi/qapi-visit-char.h qapi/qapi-visit-char.c | ||||
| GENERATED_FILES += qapi/qapi-visit-common.h qapi/qapi-visit-common.c | ||||
| GENERATED_FILES += qapi/qapi-visit-crypto.h qapi/qapi-visit-crypto.c | ||||
| GENERATED_FILES += qapi/qapi-visit-introspect.h qapi/qapi-visit-introspect.c | ||||
| GENERATED_FILES += qapi/qapi-visit-migration.h qapi/qapi-visit-migration.c | ||||
| GENERATED_FILES += qapi/qapi-visit-misc.h qapi/qapi-visit-misc.c | ||||
| GENERATED_FILES += qapi/qapi-visit-net.h qapi/qapi-visit-net.c | ||||
| GENERATED_FILES += qapi/qapi-visit-rocker.h qapi/qapi-visit-rocker.c | ||||
| GENERATED_FILES += qapi/qapi-visit-run-state.h qapi/qapi-visit-run-state.c | ||||
| GENERATED_FILES += qapi/qapi-visit-sockets.h qapi/qapi-visit-sockets.c | ||||
| GENERATED_FILES += qapi/qapi-visit-tpm.h qapi/qapi-visit-tpm.c | ||||
| GENERATED_FILES += qapi/qapi-visit-trace.h qapi/qapi-visit-trace.c | ||||
| GENERATED_FILES += qapi/qapi-visit-transaction.h qapi/qapi-visit-transaction.c | ||||
| GENERATED_FILES += qapi/qapi-visit-ui.h qapi/qapi-visit-ui.c | ||||
| GENERATED_FILES += qapi/qapi-commands.h qapi/qapi-commands.c | ||||
| GENERATED_FILES += qapi/qapi-commands-block-core.h qapi/qapi-commands-block-core.c | ||||
| GENERATED_FILES += qapi/qapi-commands-block.h qapi/qapi-commands-block.c | ||||
| GENERATED_FILES += qapi/qapi-commands-char.h qapi/qapi-commands-char.c | ||||
| GENERATED_FILES += qapi/qapi-commands-common.h qapi/qapi-commands-common.c | ||||
| GENERATED_FILES += qapi/qapi-commands-crypto.h qapi/qapi-commands-crypto.c | ||||
| GENERATED_FILES += qapi/qapi-commands-introspect.h qapi/qapi-commands-introspect.c | ||||
| GENERATED_FILES += qapi/qapi-commands-migration.h qapi/qapi-commands-migration.c | ||||
| GENERATED_FILES += qapi/qapi-commands-misc.h qapi/qapi-commands-misc.c | ||||
| GENERATED_FILES += qapi/qapi-commands-net.h qapi/qapi-commands-net.c | ||||
| GENERATED_FILES += qapi/qapi-commands-rocker.h qapi/qapi-commands-rocker.c | ||||
| GENERATED_FILES += qapi/qapi-commands-run-state.h qapi/qapi-commands-run-state.c | ||||
| GENERATED_FILES += qapi/qapi-commands-sockets.h qapi/qapi-commands-sockets.c | ||||
| GENERATED_FILES += qapi/qapi-commands-tpm.h qapi/qapi-commands-tpm.c | ||||
| GENERATED_FILES += qapi/qapi-commands-trace.h qapi/qapi-commands-trace.c | ||||
| GENERATED_FILES += qapi/qapi-commands-transaction.h qapi/qapi-commands-transaction.c | ||||
| GENERATED_FILES += qapi/qapi-commands-ui.h qapi/qapi-commands-ui.c | ||||
| GENERATED_FILES += qapi/qapi-events.h qapi/qapi-events.c | ||||
| GENERATED_FILES += qapi/qapi-events-block-core.h qapi/qapi-events-block-core.c | ||||
| GENERATED_FILES += qapi/qapi-events-block.h qapi/qapi-events-block.c | ||||
| GENERATED_FILES += qapi/qapi-events-char.h qapi/qapi-events-char.c | ||||
| GENERATED_FILES += qapi/qapi-events-common.h qapi/qapi-events-common.c | ||||
| GENERATED_FILES += qapi/qapi-events-crypto.h qapi/qapi-events-crypto.c | ||||
| GENERATED_FILES += qapi/qapi-events-introspect.h qapi/qapi-events-introspect.c | ||||
| GENERATED_FILES += qapi/qapi-events-migration.h qapi/qapi-events-migration.c | ||||
| GENERATED_FILES += qapi/qapi-events-misc.h qapi/qapi-events-misc.c | ||||
| GENERATED_FILES += qapi/qapi-events-net.h qapi/qapi-events-net.c | ||||
| GENERATED_FILES += qapi/qapi-events-rocker.h qapi/qapi-events-rocker.c | ||||
| GENERATED_FILES += qapi/qapi-events-run-state.h qapi/qapi-events-run-state.c | ||||
| GENERATED_FILES += qapi/qapi-events-sockets.h qapi/qapi-events-sockets.c | ||||
| GENERATED_FILES += qapi/qapi-events-tpm.h qapi/qapi-events-tpm.c | ||||
| GENERATED_FILES += qapi/qapi-events-trace.h qapi/qapi-events-trace.c | ||||
| GENERATED_FILES += qapi/qapi-events-transaction.h qapi/qapi-events-transaction.c | ||||
| GENERATED_FILES += qapi/qapi-events-ui.h qapi/qapi-events-ui.c | ||||
| GENERATED_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h | ||||
| GENERATED_FILES += qapi/qapi-doc.texi | ||||
|  | ||||
| GENERATED_FILES += trace/generated-tcg-tracers.h | ||||
|  | ||||
| @@ -159,7 +199,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") | ||||
|  | ||||
| %/trace.h: %/trace.h-timestamp | ||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||
| %/trace.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | ||||
| %/trace.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) | ||||
| 	$(call quiet-command,$(TRACETOOL) \ | ||||
| 		--group=$(call trace-group-name,$@) \ | ||||
| 		--format=h \ | ||||
| @@ -168,7 +208,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") | ||||
|  | ||||
| %/trace.c: %/trace.c-timestamp | ||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||
| %/trace.c-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | ||||
| %/trace.c-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) | ||||
| 	$(call quiet-command,$(TRACETOOL) \ | ||||
| 		--group=$(call trace-group-name,$@) \ | ||||
| 		--format=c \ | ||||
| @@ -177,7 +217,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") | ||||
|  | ||||
| %/trace-ust.h: %/trace-ust.h-timestamp | ||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||
| %/trace-ust.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | ||||
| %/trace-ust.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) | ||||
| 	$(call quiet-command,$(TRACETOOL) \ | ||||
| 		--group=$(call trace-group-name,$@) \ | ||||
| 		--format=ust-events-h \ | ||||
| @@ -201,7 +241,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") | ||||
|  | ||||
| trace-root.h: trace-root.h-timestamp | ||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||
| trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | ||||
| trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) | ||||
| 	$(call quiet-command,$(TRACETOOL) \ | ||||
| 		--group=root \ | ||||
| 		--format=h \ | ||||
| @@ -210,7 +250,7 @@ trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/con | ||||
|  | ||||
| trace-root.c: trace-root.c-timestamp | ||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||
| trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | ||||
| trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) | ||||
| 	$(call quiet-command,$(TRACETOOL) \ | ||||
| 		--group=root \ | ||||
| 		--format=c \ | ||||
| @@ -219,7 +259,7 @@ trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/con | ||||
|  | ||||
| trace-ust-root.h: trace-ust-root.h-timestamp | ||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||
| trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | ||||
| trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) | ||||
| 	$(call quiet-command,$(TRACETOOL) \ | ||||
| 		--group=root \ | ||||
| 		--format=ust-events-h \ | ||||
| @@ -228,7 +268,7 @@ trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR) | ||||
|  | ||||
| trace-ust-all.h: trace-ust-all.h-timestamp | ||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||
| trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/config-host.mak | ||||
| trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y) | ||||
| 	$(call quiet-command,$(TRACETOOL) \ | ||||
| 		--group=all \ | ||||
| 		--format=ust-events-h \ | ||||
| @@ -237,7 +277,7 @@ trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/con | ||||
|  | ||||
| trace-ust-all.c: trace-ust-all.c-timestamp | ||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||
| trace-ust-all.c-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/config-host.mak | ||||
| trace-ust-all.c-timestamp: $(trace-events-files) $(tracetool-y) | ||||
| 	$(call quiet-command,$(TRACETOOL) \ | ||||
| 		--group=all \ | ||||
| 		--format=ust-events-c \ | ||||
| @@ -278,7 +318,6 @@ KEYCODEMAP_FILES = \ | ||||
| 		 ui/input-keymap-xorgkbd-to-qcode.c \ | ||||
| 		 ui/input-keymap-xorgxquartz-to-qcode.c \ | ||||
| 		 ui/input-keymap-xorgxwin-to-qcode.c \ | ||||
| 		 ui/input-keymap-osx-to-qcode.c \ | ||||
| 		 $(NULL) | ||||
|  | ||||
| GENERATED_FILES += $(KEYCODEMAP_FILES) | ||||
| @@ -308,27 +347,23 @@ $(call set-vpath, $(SRC_PATH)) | ||||
|  | ||||
| LIBS+=-lz $(LIBS_TOOLS) | ||||
|  | ||||
| HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) = qemu-bridge-helper$(EXESUF) | ||||
| HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF) | ||||
|  | ||||
| ifdef BUILD_DOCS | ||||
| DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8 | ||||
| DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7 | ||||
| DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7 | ||||
| DOCS+=docs/qemu-block-drivers.7 | ||||
| DOCS+=docs/qemu-cpu-models.7 | ||||
| ifdef CONFIG_VIRTFS | ||||
| DOCS+=fsdev/virtfs-proxy-helper.1 | ||||
| endif | ||||
| ifdef CONFIG_TRACE_SYSTEMTAP | ||||
| DOCS+=scripts/qemu-trace-stap.1 | ||||
| endif | ||||
| else | ||||
| DOCS= | ||||
| endif | ||||
|  | ||||
| SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet) BUILD_DIR=$(BUILD_DIR) | ||||
| SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(filter %-softmmu, $(TARGET_DIRS))) | ||||
| SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %.d, $(SUBDIR_DEVICES_MAK)) | ||||
| SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS)) | ||||
| SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %-config-devices.mak.d, $(TARGET_DIRS)) | ||||
|  | ||||
| ifeq ($(SUBDIR_DEVICES_MAK),) | ||||
| config-all-devices.mak: | ||||
| @@ -343,26 +378,9 @@ endif | ||||
|  | ||||
| -include $(SUBDIR_DEVICES_MAK_DEP) | ||||
|  | ||||
| # This has to be kept in sync with Kconfig.host. | ||||
| MINIKCONF_ARGS = \ | ||||
|     $(CONFIG_MINIKCONF_MODE) \ | ||||
|     $@ $*-config.devices.mak.d $< $(MINIKCONF_INPUTS) \ | ||||
|     CONFIG_KVM=$(CONFIG_KVM) \ | ||||
|     CONFIG_SPICE=$(CONFIG_SPICE) \ | ||||
|     CONFIG_IVSHMEM=$(CONFIG_IVSHMEM) \ | ||||
|     CONFIG_TPM=$(CONFIG_TPM) \ | ||||
|     CONFIG_XEN=$(CONFIG_XEN) \ | ||||
|     CONFIG_OPENGL=$(CONFIG_OPENGL) \ | ||||
|     CONFIG_X11=$(CONFIG_X11) \ | ||||
|     CONFIG_VHOST_USER=$(CONFIG_VHOST_USER) \ | ||||
|     CONFIG_VIRTFS=$(CONFIG_VIRTFS) \ | ||||
|     CONFIG_LINUX=$(CONFIG_LINUX) | ||||
|  | ||||
| MINIKCONF_INPUTS = $(SRC_PATH)/Kconfig.host $(SRC_PATH)/hw/Kconfig | ||||
| MINIKCONF = $(PYTHON) $(SRC_PATH)/scripts/minikconf.py \ | ||||
|  | ||||
| $(SUBDIR_DEVICES_MAK): %/config-devices.mak: default-configs/%.mak $(MINIKCONF_INPUTS) $(BUILD_DIR)/config-host.mak | ||||
| 	$(call quiet-command, $(MINIKCONF) $(MINIKCONF_ARGS) > $@.tmp, "GEN", "$@.tmp") | ||||
| %/config-devices.mak: default-configs/%.mak $(SRC_PATH)/scripts/make_device_config.sh | ||||
| 	$(call quiet-command, \ | ||||
|             $(SHELL) $(SRC_PATH)/scripts/make_device_config.sh $< $*-config-devices.mak.d $@ > $@.tmp,"GEN","$@.tmp") | ||||
| 	$(call quiet-command, if test -f $@; then \ | ||||
| 	  if cmp -s $@.old $@; then \ | ||||
| 	    mv $@.tmp $@; \ | ||||
| @@ -390,14 +408,11 @@ endif | ||||
|  | ||||
| dummy := $(call unnest-vars,, \ | ||||
|                 stub-obj-y \ | ||||
|                 authz-obj-y \ | ||||
|                 chardev-obj-y \ | ||||
|                 util-obj-y \ | ||||
|                 qga-obj-y \ | ||||
|                 elf2dmp-obj-y \ | ||||
|                 ivshmem-client-obj-y \ | ||||
|                 ivshmem-server-obj-y \ | ||||
|                 rdmacm-mux-obj-y \ | ||||
|                 libvhost-user-obj-y \ | ||||
|                 vhost-user-scsi-obj-y \ | ||||
|                 vhost-user-blk-obj-y \ | ||||
| @@ -418,13 +433,26 @@ dummy := $(call unnest-vars,, \ | ||||
|  | ||||
| include $(SRC_PATH)/tests/Makefile.include | ||||
|  | ||||
| all: $(DOCS) $(if $(BUILD_DOCS),sphinxdocs) $(TOOLS) $(HELPERS-y) recurse-all modules | ||||
| all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules | ||||
|  | ||||
| qemu-version.h: FORCE | ||||
| 	$(call quiet-command, \ | ||||
|                 (printf '#define QEMU_PKGVERSION "$(QEMU_PKGVERSION)"\n'; \ | ||||
| 		printf '#define QEMU_FULL_VERSION "$(FULL_VERSION)"\n'; \ | ||||
| 		) > $@.tmp) | ||||
| 		(cd $(SRC_PATH); \ | ||||
| 		printf '#define QEMU_PKGVERSION '; \ | ||||
| 		if test -n "$(PKGVERSION)"; then \ | ||||
| 			printf '"$(PKGVERSION)"\n'; \ | ||||
| 		else \ | ||||
| 			if test -d .git; then \ | ||||
| 				printf '" ('; \ | ||||
| 				git describe --match 'v*' 2>/dev/null | tr -d '\n'; \ | ||||
| 				if ! git diff-index --quiet HEAD &>/dev/null; then \ | ||||
| 					printf -- '-dirty'; \ | ||||
| 				fi; \ | ||||
| 				printf ')"\n'; \ | ||||
| 			else \ | ||||
| 				printf '""\n'; \ | ||||
| 			fi; \ | ||||
| 		fi) > $@.tmp) | ||||
| 	$(call quiet-command, if ! cmp -s $@ $@.tmp; then \ | ||||
| 	  mv $@.tmp $@; \ | ||||
| 	 else \ | ||||
| @@ -439,7 +467,6 @@ qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool | ||||
| SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS)) | ||||
| SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES)) | ||||
|  | ||||
| $(SOFTMMU_SUBDIR_RULES): $(authz-obj-y) | ||||
| $(SOFTMMU_SUBDIR_RULES): $(block-obj-y) | ||||
| $(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y) | ||||
| $(SOFTMMU_SUBDIR_RULES): $(io-obj-y) | ||||
| @@ -456,7 +483,7 @@ subdir-dtc: .git-submodule-status dtc/libfdt dtc/tests | ||||
| 	$(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt/libfdt.a,) | ||||
|  | ||||
| dtc/%: .git-submodule-status | ||||
| 	@mkdir -p $@ | ||||
| 	mkdir -p $@ | ||||
|  | ||||
| # Overriding CFLAGS causes us to lose defines added in the sub-makefile. | ||||
| # Not overriding CFLAGS leads to mis-matches between compilation modes. | ||||
| @@ -473,9 +500,6 @@ CAP_CFLAGS += -DCAPSTONE_HAS_X86 | ||||
| subdir-capstone: .git-submodule-status | ||||
| 	$(call quiet-command,$(MAKE) -C $(SRC_PATH)/capstone CAPSTONE_SHARED=no BUILDDIR="$(BUILD_DIR)/capstone" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(CAP_CFLAGS)" $(SUBDIR_MAKEFLAGS) $(BUILD_DIR)/capstone/$(LIBCAPSTONE)) | ||||
|  | ||||
| subdir-slirp: .git-submodule-status | ||||
| 	$(call quiet-command,$(MAKE) -C $(SRC_PATH)/slirp BUILD_DIR="$(BUILD_DIR)/slirp" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(QEMU_CFLAGS)") | ||||
|  | ||||
| $(SUBDIR_RULES): libqemuutil.a $(common-obj-y) $(chardev-obj-y) \ | ||||
| 	$(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY)) | ||||
|  | ||||
| @@ -497,7 +521,7 @@ Makefile: $(version-obj-y) | ||||
| # Build libraries | ||||
|  | ||||
| libqemuutil.a: $(util-obj-y) $(trace-obj-y) $(stub-obj-y) | ||||
| libvhost-user.a: $(libvhost-user-obj-y) $(util-obj-y) $(stub-obj-y) | ||||
| libvhost-user.a: $(libvhost-user-obj-y) | ||||
|  | ||||
| ###################################################################### | ||||
|  | ||||
| @@ -505,20 +529,18 @@ COMMON_LDADDS = libqemuutil.a | ||||
|  | ||||
| qemu-img.o: qemu-img-cmds.h | ||||
|  | ||||
| qemu-img$(EXESUF): qemu-img.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) | ||||
| qemu-nbd$(EXESUF): qemu-nbd.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) | ||||
| qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) | ||||
| qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) | ||||
| qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) | ||||
| qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) | ||||
|  | ||||
| qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS) | ||||
|  | ||||
| qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $(COMMON_LDADDS) | ||||
|  | ||||
| qemu-edid$(EXESUF): qemu-edid.o hw/display/edid-generate.o $(COMMON_LDADDS) | ||||
|  | ||||
| fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS) | ||||
| fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap | ||||
|  | ||||
| scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(authz-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) | ||||
| scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) | ||||
| ifdef CONFIG_MPATH | ||||
| scsi/qemu-pr-helper$(EXESUF): LIBS += -ludev -lmultipath -lmpathpersist | ||||
| endif | ||||
| @@ -539,6 +561,7 @@ $(SRC_PATH)/scripts/qapi/types.py \ | ||||
| $(SRC_PATH)/scripts/qapi/visit.py \ | ||||
| $(SRC_PATH)/scripts/qapi/common.py \ | ||||
| $(SRC_PATH)/scripts/qapi/doc.py \ | ||||
| $(SRC_PATH)/scripts/ordereddict.py \ | ||||
| $(SRC_PATH)/scripts/qapi-gen.py | ||||
|  | ||||
| qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \ | ||||
| @@ -547,17 +570,102 @@ qga/qapi-generated/qga-qapi-commands.h qga/qapi-generated/qga-qapi-commands.c \ | ||||
| qga/qapi-generated/qga-qapi-doc.texi: \ | ||||
| qga/qapi-generated/qapi-gen-timestamp ; | ||||
| qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py) | ||||
| 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ | ||||
| 	$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \ | ||||
| 		-o qga/qapi-generated -p "qga-" $<, \ | ||||
| 		"GEN","$(@:%-timestamp=%)") | ||||
| 	@>$@ | ||||
|  | ||||
| qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json \ | ||||
|                $(QAPI_MODULES:%=$(SRC_PATH)/qapi/%.json) | ||||
| qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json $(SRC_PATH)/qapi/common.json \ | ||||
|                $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \ | ||||
|                $(SRC_PATH)/qapi/char.json \ | ||||
|                $(SRC_PATH)/qapi/crypto.json \ | ||||
|                $(SRC_PATH)/qapi/introspect.json \ | ||||
|                $(SRC_PATH)/qapi/migration.json \ | ||||
|                $(SRC_PATH)/qapi/misc.json \ | ||||
|                $(SRC_PATH)/qapi/net.json \ | ||||
|                $(SRC_PATH)/qapi/rocker.json \ | ||||
|                $(SRC_PATH)/qapi/run-state.json \ | ||||
|                $(SRC_PATH)/qapi/sockets.json \ | ||||
|                $(SRC_PATH)/qapi/tpm.json \ | ||||
|                $(SRC_PATH)/qapi/trace.json \ | ||||
|                $(SRC_PATH)/qapi/transaction.json \ | ||||
|                $(SRC_PATH)/qapi/ui.json | ||||
|  | ||||
| $(GENERATED_QAPI_FILES): qapi-gen-timestamp ; | ||||
| qapi/qapi-builtin-types.c qapi/qapi-builtin-types.h \ | ||||
| qapi/qapi-types.c qapi/qapi-types.h \ | ||||
| qapi/qapi-types-block-core.c qapi/qapi-types-block-core.h \ | ||||
| qapi/qapi-types-block.c qapi/qapi-types-block.h \ | ||||
| qapi/qapi-types-char.c qapi/qapi-types-char.h \ | ||||
| qapi/qapi-types-common.c qapi/qapi-types-common.h \ | ||||
| qapi/qapi-types-crypto.c qapi/qapi-types-crypto.h \ | ||||
| qapi/qapi-types-introspect.c qapi/qapi-types-introspect.h \ | ||||
| qapi/qapi-types-migration.c qapi/qapi-types-migration.h \ | ||||
| qapi/qapi-types-misc.c qapi/qapi-types-misc.h \ | ||||
| qapi/qapi-types-net.c qapi/qapi-types-net.h \ | ||||
| qapi/qapi-types-rocker.c qapi/qapi-types-rocker.h \ | ||||
| qapi/qapi-types-run-state.c qapi/qapi-types-run-state.h \ | ||||
| qapi/qapi-types-sockets.c qapi/qapi-types-sockets.h \ | ||||
| qapi/qapi-types-tpm.c qapi/qapi-types-tpm.h \ | ||||
| qapi/qapi-types-trace.c qapi/qapi-types-trace.h \ | ||||
| qapi/qapi-types-transaction.c qapi/qapi-types-transaction.h \ | ||||
| qapi/qapi-types-ui.c qapi/qapi-types-ui.h \ | ||||
| qapi/qapi-builtin-visit.c qapi/qapi-builtin-visit.h \ | ||||
| qapi/qapi-visit.c qapi/qapi-visit.h \ | ||||
| qapi/qapi-visit-block-core.c qapi/qapi-visit-block-core.h \ | ||||
| qapi/qapi-visit-block.c qapi/qapi-visit-block.h \ | ||||
| qapi/qapi-visit-char.c qapi/qapi-visit-char.h \ | ||||
| qapi/qapi-visit-common.c qapi/qapi-visit-common.h \ | ||||
| qapi/qapi-visit-crypto.c qapi/qapi-visit-crypto.h \ | ||||
| qapi/qapi-visit-introspect.c qapi/qapi-visit-introspect.h \ | ||||
| qapi/qapi-visit-migration.c qapi/qapi-visit-migration.h \ | ||||
| qapi/qapi-visit-misc.c qapi/qapi-visit-misc.h \ | ||||
| qapi/qapi-visit-net.c qapi/qapi-visit-net.h \ | ||||
| qapi/qapi-visit-rocker.c qapi/qapi-visit-rocker.h \ | ||||
| qapi/qapi-visit-run-state.c qapi/qapi-visit-run-state.h \ | ||||
| qapi/qapi-visit-sockets.c qapi/qapi-visit-sockets.h \ | ||||
| qapi/qapi-visit-tpm.c qapi/qapi-visit-tpm.h \ | ||||
| qapi/qapi-visit-trace.c qapi/qapi-visit-trace.h \ | ||||
| qapi/qapi-visit-transaction.c qapi/qapi-visit-transaction.h \ | ||||
| qapi/qapi-visit-ui.c qapi/qapi-visit-ui.h \ | ||||
| qapi/qapi-commands.h qapi/qapi-commands.c \ | ||||
| qapi/qapi-commands-block-core.c qapi/qapi-commands-block-core.h \ | ||||
| qapi/qapi-commands-block.c qapi/qapi-commands-block.h \ | ||||
| qapi/qapi-commands-char.c qapi/qapi-commands-char.h \ | ||||
| qapi/qapi-commands-common.c qapi/qapi-commands-common.h \ | ||||
| qapi/qapi-commands-crypto.c qapi/qapi-commands-crypto.h \ | ||||
| qapi/qapi-commands-introspect.c qapi/qapi-commands-introspect.h \ | ||||
| qapi/qapi-commands-migration.c qapi/qapi-commands-migration.h \ | ||||
| qapi/qapi-commands-misc.c qapi/qapi-commands-misc.h \ | ||||
| qapi/qapi-commands-net.c qapi/qapi-commands-net.h \ | ||||
| qapi/qapi-commands-rocker.c qapi/qapi-commands-rocker.h \ | ||||
| qapi/qapi-commands-run-state.c qapi/qapi-commands-run-state.h \ | ||||
| qapi/qapi-commands-sockets.c qapi/qapi-commands-sockets.h \ | ||||
| qapi/qapi-commands-tpm.c qapi/qapi-commands-tpm.h \ | ||||
| qapi/qapi-commands-trace.c qapi/qapi-commands-trace.h \ | ||||
| qapi/qapi-commands-transaction.c qapi/qapi-commands-transaction.h \ | ||||
| qapi/qapi-commands-ui.c qapi/qapi-commands-ui.h \ | ||||
| qapi/qapi-events.c qapi/qapi-events.h \ | ||||
| qapi/qapi-events-block-core.c qapi/qapi-events-block-core.h \ | ||||
| qapi/qapi-events-block.c qapi/qapi-events-block.h \ | ||||
| qapi/qapi-events-char.c qapi/qapi-events-char.h \ | ||||
| qapi/qapi-events-common.c qapi/qapi-events-common.h \ | ||||
| qapi/qapi-events-crypto.c qapi/qapi-events-crypto.h \ | ||||
| qapi/qapi-events-introspect.c qapi/qapi-events-introspect.h \ | ||||
| qapi/qapi-events-migration.c qapi/qapi-events-migration.h \ | ||||
| qapi/qapi-events-misc.c qapi/qapi-events-misc.h \ | ||||
| qapi/qapi-events-net.c qapi/qapi-events-net.h \ | ||||
| qapi/qapi-events-rocker.c qapi/qapi-events-rocker.h \ | ||||
| qapi/qapi-events-run-state.c qapi/qapi-events-run-state.h \ | ||||
| qapi/qapi-events-sockets.c qapi/qapi-events-sockets.h \ | ||||
| qapi/qapi-events-tpm.c qapi/qapi-events-tpm.h \ | ||||
| qapi/qapi-events-trace.c qapi/qapi-events-trace.h \ | ||||
| qapi/qapi-events-transaction.c qapi/qapi-events-transaction.h \ | ||||
| qapi/qapi-events-ui.c qapi/qapi-events-ui.h \ | ||||
| qapi/qapi-introspect.h qapi/qapi-introspect.c \ | ||||
| qapi/qapi-doc.texi: \ | ||||
| qapi-gen-timestamp ; | ||||
| qapi-gen-timestamp: $(qapi-modules) $(qapi-py) | ||||
| 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ | ||||
| 	$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \ | ||||
| 		-o "qapi" -b $<, \ | ||||
| 		"GEN","$(@:%-timestamp=%)") | ||||
| 	@>$@ | ||||
| @@ -590,10 +698,6 @@ ifneq ($(EXESUF),) | ||||
| qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI) | ||||
| endif | ||||
|  | ||||
| elf2dmp$(EXESUF): LIBS += $(CURL_LIBS) | ||||
| elf2dmp$(EXESUF): $(elf2dmp-obj-y) | ||||
| 	$(call LINK, $^) | ||||
|  | ||||
| ifdef CONFIG_IVSHMEM | ||||
| ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS) | ||||
| 	$(call LINK, $^) | ||||
| @@ -605,33 +709,17 @@ vhost-user-scsi$(EXESUF): $(vhost-user-scsi-obj-y) libvhost-user.a | ||||
| vhost-user-blk$(EXESUF): $(vhost-user-blk-obj-y) libvhost-user.a | ||||
| 	$(call LINK, $^) | ||||
|  | ||||
| rdmacm-mux$(EXESUF): LIBS += "-libumad" | ||||
| rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS) | ||||
| 	$(call LINK, $^) | ||||
|  | ||||
| module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak | ||||
| 	$(call quiet-command,$(PYTHON) $< $@ \ | ||||
| 	$(addprefix $(SRC_PATH)/,$(patsubst %.mo,%.c,$(block-obj-m))), \ | ||||
| 	"GEN","$@") | ||||
|  | ||||
| ifdef CONFIG_GCOV | ||||
| .PHONY: clean-coverage | ||||
| clean-coverage: | ||||
| 	$(call quiet-command, \ | ||||
| 		find . \( -name '*.gcda' -o -name '*.gcov' \) -type f -exec rm {} +, \ | ||||
| 		"CLEAN", "coverage files") | ||||
| endif | ||||
|  | ||||
| clean: | ||||
| # avoid old build problems by removing potentially incorrect old files | ||||
| 	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h | ||||
| 	rm -f qemu-options.def | ||||
| 	rm -f *.msi | ||||
| 	find . \( -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f \ | ||||
| 		! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-aarch64.a \ | ||||
| 		! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-arm.a \ | ||||
| 		! -path ./roms/edk2/BaseTools/Source/Python/UPT/Dll/sqlite3.dll \ | ||||
| 		-exec rm {} + | ||||
| 	find . \( -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f -exec rm {} + | ||||
| 	rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~ | ||||
| 	rm -f fsdev/*.pod scsi/*.pod | ||||
| 	rm -f qemu-img-cmds.h | ||||
| @@ -646,7 +734,7 @@ clean: | ||||
| 	if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \ | ||||
| 	rm -f $$d/qemu-options.def; \ | ||||
|         done | ||||
| 	rm -f config-all-devices.mak | ||||
| 	rm -f $(SUBDIR_DEVICES_MAK) config-all-devices.mak | ||||
|  | ||||
| VERSION ?= $(shell cat VERSION) | ||||
|  | ||||
| @@ -655,26 +743,9 @@ dist: qemu-$(VERSION).tar.bz2 | ||||
| qemu-%.tar.bz2: | ||||
| 	$(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)" | ||||
|  | ||||
| # Sphinx does not allow building manuals into the same directory as | ||||
| # the source files, so if we're doing an in-tree QEMU build we must | ||||
| # build the manuals into a subdirectory (and then install them from | ||||
| # there for 'make install'). For an out-of-tree build we can just | ||||
| # use the docs/ subdirectory in the build tree as normal. | ||||
| ifeq ($(realpath $(SRC_PATH)),$(realpath .)) | ||||
| MANUAL_BUILDDIR := docs/built | ||||
| else | ||||
| MANUAL_BUILDDIR := docs | ||||
| endif | ||||
|  | ||||
| define clean-manual = | ||||
| rm -rf $(MANUAL_BUILDDIR)/$1/_static | ||||
| rm -f $(MANUAL_BUILDDIR)/$1/objects.inv $(MANUAL_BUILDDIR)/$1/searchindex.js $(MANUAL_BUILDDIR)/$1/*.html | ||||
| endef | ||||
|  | ||||
| distclean: clean | ||||
| 	rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi | ||||
| 	rm -f config-all-devices.mak config-all-disas.mak config.status | ||||
| 	rm -f $(SUBDIR_DEVICES_MAK) | ||||
| 	rm -f po/*.mo tests/qemu-iotests/common.env | ||||
| 	rm -f roms/seabios/config.mak roms/vgabios/config.mak | ||||
| 	rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps | ||||
| @@ -690,33 +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.html docs/interop/qemu-ga-ref.html | ||||
| 	rm -f docs/qemu-block-drivers.7 | ||||
| 	rm -f docs/qemu-cpu-models.7 | ||||
| 	rm -rf .doctrees | ||||
| 	$(call clean-manual,devel) | ||||
| 	$(call clean-manual,interop) | ||||
| 	for d in $(TARGET_DIRS); do \ | ||||
| 	rm -rf $$d || exit 1 ; \ | ||||
|         done | ||||
| 	rm -Rf .sdk | ||||
| 	if test -f dtc/version_gen.h; then $(MAKE) $(DTC_MAKE_ARGS) clean; fi | ||||
|  | ||||
| KEYMAPS=da     en-gb  et  fr     fr-ch  is  lt  no  pt-br  sv \ | ||||
| KEYMAPS=da     en-gb  et  fr     fr-ch  is  lt  modifiers  no  pt-br  sv \ | ||||
| ar      de     en-us  fi  fr-be  hr     it  lv  nl         pl  ru     th \ | ||||
| de-ch  es     fo  fr-ca  hu     ja  mk  pt  sl     tr \ | ||||
| common  de-ch  es     fo  fr-ca  hu     ja  mk  nl-be      pt  sl     tr \ | ||||
| bepo    cz | ||||
|  | ||||
| ifdef INSTALL_BLOBS | ||||
| BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \ | ||||
| vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \ | ||||
| vgabios-ramfb.bin vgabios-bochs-display.bin \ | ||||
| acpi-dsdt.aml \ | ||||
| ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \ | ||||
| pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \ | ||||
| pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \ | ||||
| efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \ | ||||
| efi-pcnet.rom efi-rtl8139.rom efi-virtio.rom \ | ||||
| efi-e1000e.rom efi-vmxnet3.rom \ | ||||
| qemu-icon.bmp qemu_logo_no_text.svg \ | ||||
| bamboo.dtb canyonlands.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ | ||||
| multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin pvh.bin \ | ||||
| multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin \ | ||||
| s390-ccw.img s390-netboot.img \ | ||||
| spapr-rtas.bin slof.bin skiboot.lid \ | ||||
| palcode-clipper \ | ||||
| @@ -727,20 +795,7 @@ else | ||||
| BLOBS= | ||||
| endif | ||||
|  | ||||
| # Note that we manually filter-out the non-Sphinx documentation which | ||||
| # is currently built into the docs/interop directory in the build tree. | ||||
| define install-manual = | ||||
| for d in $$(cd $(MANUAL_BUILDDIR) && find $1 -type d); do $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)/$$d"; done | ||||
| for f in $$(cd $(MANUAL_BUILDDIR) && find $1 -type f -a '!' '(' -name 'qemu-*-qapi.*' -o -name 'qemu-*-ref.*' ')' ); do $(INSTALL_DATA) "$(MANUAL_BUILDDIR)/$$f" "$(DESTDIR)$(qemu_docdir)/$$f"; done | ||||
| endef | ||||
|  | ||||
| # Note that we deliberately do not install the "devel" manual: it is | ||||
| # for QEMU developers, and not interesting to our users. | ||||
| .PHONY: install-sphinxdocs | ||||
| install-sphinxdocs: sphinxdocs | ||||
| 	$(call install-manual,interop) | ||||
|  | ||||
| install-doc: $(DOCS) install-sphinxdocs | ||||
| install-doc: $(DOCS) | ||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)" | ||||
| 	$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)" | ||||
| 	$(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)" | ||||
| @@ -752,15 +807,11 @@ ifdef CONFIG_POSIX | ||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7" | ||||
| 	$(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7" | ||||
| 	$(INSTALL_DATA) docs/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7" | ||||
| 	$(INSTALL_DATA) docs/qemu-cpu-models.7 "$(DESTDIR)$(mandir)/man7" | ||||
| ifneq ($(TOOLS),) | ||||
| 	$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1" | ||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8" | ||||
| 	$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8" | ||||
| endif | ||||
| ifdef CONFIG_TRACE_SYSTEMTAP | ||||
| 	$(INSTALL_DATA) scripts/qemu-trace-stap.1 "$(DESTDIR)$(mandir)/man1" | ||||
| endif | ||||
| ifneq (,$(findstring qemu-ga,$(TOOLS))) | ||||
| 	$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8" | ||||
| 	$(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)" | ||||
| @@ -783,7 +834,6 @@ ifneq (,$(findstring qemu-ga,$(TOOLS))) | ||||
| endif | ||||
| endif | ||||
|  | ||||
| ICON_SIZES=16x16 24x24 32x32 48x48 64x64 128x128 256x256 512x512 | ||||
|  | ||||
| install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir | ||||
| ifneq ($(TOOLS),) | ||||
| @@ -800,29 +850,12 @@ endif | ||||
| ifneq ($(HELPERS-y),) | ||||
| 	$(call install-prog,$(HELPERS-y),$(DESTDIR)$(libexecdir)) | ||||
| endif | ||||
| ifdef CONFIG_TRACE_SYSTEMTAP | ||||
| 	$(INSTALL_PROG) "scripts/qemu-trace-stap" $(DESTDIR)$(bindir) | ||||
| endif | ||||
| ifneq ($(BLOBS),) | ||||
| 	set -e; for x in $(BLOBS); do \ | ||||
| 		$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \ | ||||
| 	done | ||||
| endif | ||||
| 	for s in $(ICON_SIZES); do \ | ||||
| 		mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/$${s}/apps"; \ | ||||
| 		$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu_$${s}.png \ | ||||
| 			"$(DESTDIR)/$(qemu_icondir)/hicolor/$${s}/apps/qemu.png"; \ | ||||
| 	done; \ | ||||
| 	mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/32x32/apps"; \ | ||||
| 	$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu_32x32.bmp \ | ||||
| 		"$(DESTDIR)/$(qemu_icondir)/hicolor/32x32/apps/qemu.bmp"; \ | ||||
| 	mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/scalable/apps"; \ | ||||
| 	$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu.svg \ | ||||
| 		"$(DESTDIR)/$(qemu_icondir)/hicolor/scalable/apps/qemu.svg" | ||||
| 	mkdir -p "$(DESTDIR)/$(qemu_desktopdir)" | ||||
| 	$(INSTALL_DATA) $(SRC_PATH)/ui/qemu.desktop \ | ||||
| 		"$(DESTDIR)/$(qemu_desktopdir)/qemu.desktop" | ||||
| ifdef CONFIG_GTK | ||||
| ifeq ($(CONFIG_GTK),m) | ||||
| 	$(MAKE) -C po $@ | ||||
| endif | ||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps" | ||||
| @@ -891,23 +924,6 @@ docs/version.texi: $(SRC_PATH)/VERSION | ||||
| %.pdf: %.texi docs/version.texi | ||||
| 	$(call quiet-command,texi2pdf $(TEXI2PDFFLAGS) $< -o $@,"GEN","$@") | ||||
|  | ||||
| # Sphinx builds all its documentation at once in one invocation | ||||
| # and handles "don't rebuild things unless necessary" itself. | ||||
| # The '.doctrees' files are cached information to speed this up. | ||||
| .PHONY: sphinxdocs | ||||
| sphinxdocs: $(MANUAL_BUILDDIR)/devel/index.html $(MANUAL_BUILDDIR)/interop/index.html | ||||
|  | ||||
| # Canned command to build a single manual | ||||
| build-manual = $(call quiet-command,sphinx-build $(if $(V),,-q) -b html -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1") | ||||
| # We assume all RST files in the manual's directory are used in it | ||||
| manual-deps = $(wildcard $(SRC_PATH)/docs/$1/*.rst) $(SRC_PATH)/docs/$1/conf.py $(SRC_PATH)/docs/conf.py | ||||
|  | ||||
| $(MANUAL_BUILDDIR)/devel/index.html: $(call manual-deps,devel) | ||||
| 	$(call build-manual,devel) | ||||
|  | ||||
| $(MANUAL_BUILDDIR)/interop/index.html: $(call manual-deps,interop) | ||||
| 	$(call build-manual,interop) | ||||
|  | ||||
| qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@") | ||||
|  | ||||
| @@ -933,19 +949,16 @@ fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi | ||||
| qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi | ||||
| qemu-ga.8: qemu-ga.texi | ||||
| docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi | ||||
| docs/qemu-cpu-models.7: docs/qemu-cpu-models.texi | ||||
| scripts/qemu-trace-stap.1: scripts/qemu-trace-stap.texi | ||||
|  | ||||
| html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html sphinxdocs | ||||
| 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 | ||||
| pdf: qemu-doc.pdf docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf | ||||
| txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt | ||||
|  | ||||
| qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \ | ||||
| 	qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \ | ||||
| 	qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \ | ||||
| 	qemu-monitor-info.texi docs/qemu-block-drivers.texi \ | ||||
| 	docs/qemu-cpu-models.texi | ||||
| 	qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \ | ||||
| 	qemu-monitor-info.texi docs/qemu-block-drivers.texi | ||||
|  | ||||
| docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \ | ||||
|     docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \ | ||||
| @@ -957,19 +970,6 @@ docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \ | ||||
|     docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \ | ||||
| 	docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi | ||||
|  | ||||
| $(filter %.1 %.7 %.8,$(DOCS)): scripts/texi2pod.pl | ||||
|  | ||||
| # Reports/Analysis | ||||
|  | ||||
| %/coverage-report.html: | ||||
| 	@mkdir -p $* | ||||
| 	$(call quiet-command,\ | ||||
| 		gcovr -r $(SRC_PATH) --object-directory $(BUILD_PATH) \ | ||||
| 		-p --html --html-details -o $@, \ | ||||
| 		"GEN", "coverage-report.html") | ||||
|  | ||||
| .PHONY: coverage-report | ||||
| coverage-report: $(CURDIR)/reports/coverage/coverage-report.html | ||||
|  | ||||
| ifdef CONFIG_WIN32 | ||||
|  | ||||
| @@ -1050,9 +1050,6 @@ include $(SRC_PATH)/tests/vm/Makefile.include | ||||
| help: | ||||
| 	@echo  'Generic targets:' | ||||
| 	@echo  '  all             - Build all' | ||||
| ifdef CONFIG_MODULES | ||||
| 	@echo  '  modules         - Build all modules' | ||||
| endif | ||||
| 	@echo  '  dir/file.o      - Build specified target only' | ||||
| 	@echo  '  install         - Install QEMU, documentation and tools' | ||||
| 	@echo  '  ctags/TAGS      - Generate tags file for editors' | ||||
| @@ -1065,9 +1062,6 @@ endif | ||||
| 		echo '') | ||||
| 	@echo  'Cleaning targets:' | ||||
| 	@echo  '  clean           - Remove most generated files but keep the config' | ||||
| ifdef CONFIG_GCOV | ||||
| 	@echo  '  clean-coverage  - Remove coverage files' | ||||
| endif | ||||
| 	@echo  '  distclean       - Remove all generated files' | ||||
| 	@echo  '  dist            - Build a distributable tarball' | ||||
| 	@echo  '' | ||||
| @@ -1079,9 +1073,6 @@ endif | ||||
| 	@echo  'Documentation targets:' | ||||
| 	@echo  '  html info pdf txt' | ||||
| 	@echo  '                  - Build documentation in specified format' | ||||
| ifdef CONFIG_GCOV | ||||
| 	@echo  '  coverage-report - Create code coverage report' | ||||
| endif | ||||
| 	@echo  '' | ||||
| ifdef CONFIG_WIN32 | ||||
| 	@echo  'Windows targets:' | ||||
|   | ||||
							
								
								
									
										178
									
								
								Makefile.objs
									
									
									
									
									
								
							
							
						
						
									
										178
									
								
								Makefile.objs
									
									
									
									
									
								
							| @@ -1,20 +1,69 @@ | ||||
| ####################################################################### | ||||
| # Common libraries for tools and emulators | ||||
| stub-obj-y = stubs/ util/ crypto/ | ||||
| stub-obj-y = stubs/ crypto/ | ||||
| util-obj-y = util/ qobject/ qapi/ | ||||
| util-obj-y += qapi/qapi-builtin-types.o | ||||
| util-obj-y += qapi/qapi-types.o | ||||
| util-obj-y += qapi/qapi-types-block-core.o | ||||
| util-obj-y += qapi/qapi-types-block.o | ||||
| util-obj-y += qapi/qapi-types-char.o | ||||
| util-obj-y += qapi/qapi-types-common.o | ||||
| util-obj-y += qapi/qapi-types-crypto.o | ||||
| util-obj-y += qapi/qapi-types-introspect.o | ||||
| util-obj-y += qapi/qapi-types-migration.o | ||||
| util-obj-y += qapi/qapi-types-misc.o | ||||
| util-obj-y += qapi/qapi-types-net.o | ||||
| util-obj-y += qapi/qapi-types-rocker.o | ||||
| util-obj-y += qapi/qapi-types-run-state.o | ||||
| util-obj-y += qapi/qapi-types-sockets.o | ||||
| util-obj-y += qapi/qapi-types-tpm.o | ||||
| util-obj-y += qapi/qapi-types-trace.o | ||||
| util-obj-y += qapi/qapi-types-transaction.o | ||||
| util-obj-y += qapi/qapi-types-ui.o | ||||
| util-obj-y += qapi/qapi-builtin-visit.o | ||||
| util-obj-y += qapi/qapi-visit.o | ||||
| util-obj-y += qapi/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-block-core.o | ||||
| util-obj-y += qapi/qapi-events-block.o | ||||
| util-obj-y += qapi/qapi-events-char.o | ||||
| util-obj-y += qapi/qapi-events-common.o | ||||
| util-obj-y += qapi/qapi-events-crypto.o | ||||
| util-obj-y += qapi/qapi-events-introspect.o | ||||
| util-obj-y += qapi/qapi-events-migration.o | ||||
| util-obj-y += qapi/qapi-events-misc.o | ||||
| util-obj-y += qapi/qapi-events-net.o | ||||
| util-obj-y += qapi/qapi-events-rocker.o | ||||
| util-obj-y += qapi/qapi-events-run-state.o | ||||
| util-obj-y += qapi/qapi-events-sockets.o | ||||
| util-obj-y += qapi/qapi-events-tpm.o | ||||
| util-obj-y += qapi/qapi-events-trace.o | ||||
| util-obj-y += qapi/qapi-events-transaction.o | ||||
| util-obj-y += qapi/qapi-events-ui.o | ||||
| util-obj-y += qapi/qapi-introspect.o | ||||
|  | ||||
| chardev-obj-y = chardev/ | ||||
|  | ||||
| ####################################################################### | ||||
| # authz-obj-y is code used by both qemu system emulation and qemu-img | ||||
|  | ||||
| authz-obj-y = authz/ | ||||
|  | ||||
| ####################################################################### | ||||
| # block-obj-y is code used by both qemu system emulation and qemu-img | ||||
|  | ||||
| block-obj-y = nbd/ | ||||
| block-obj-y += block.o blockjob.o job.o | ||||
| block-obj-y += nbd/ | ||||
| block-obj-y += block.o blockjob.o | ||||
| block-obj-y += block/ scsi/ | ||||
| block-obj-y += qemu-io-cmds.o | ||||
| block-obj-$(CONFIG_REPLICATION) += replication.o | ||||
| @@ -45,7 +94,6 @@ io-obj-y = io/ | ||||
| ifeq ($(CONFIG_SOFTMMU),y) | ||||
| common-obj-y = blockdev.o blockdev-nbd.o block/ | ||||
| common-obj-y += bootdevice.o iothread.o | ||||
| common-obj-y += job-qmp.o | ||||
| common-obj-y += net/ | ||||
| common-obj-y += qdev-monitor.o device-hotplug.o | ||||
| common-obj-$(CONFIG_WIN32) += os-win32.o | ||||
| @@ -71,6 +119,8 @@ common-obj-y += vl.o | ||||
| vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS) | ||||
| common-obj-$(CONFIG_TPM) += tpm.o | ||||
|  | ||||
| common-obj-$(CONFIG_SLIRP) += slirp/ | ||||
|  | ||||
| common-obj-y += backends/ | ||||
| common-obj-y += chardev/ | ||||
|  | ||||
| @@ -83,8 +133,25 @@ common-obj-$(CONFIG_FDT) += device_tree.o | ||||
| ###################################################################### | ||||
| # qapi | ||||
|  | ||||
| common-obj-y += qapi/qapi-commands.o | ||||
| common-obj-y += qapi/qapi-commands-block-core.o | ||||
| common-obj-y += qapi/qapi-commands-block.o | ||||
| common-obj-y += qapi/qapi-commands-char.o | ||||
| common-obj-y += qapi/qapi-commands-common.o | ||||
| common-obj-y += qapi/qapi-commands-crypto.o | ||||
| common-obj-y += qapi/qapi-commands-introspect.o | ||||
| common-obj-y += qapi/qapi-commands-migration.o | ||||
| common-obj-y += qapi/qapi-commands-misc.o | ||||
| common-obj-y += qapi/qapi-commands-net.o | ||||
| common-obj-y += qapi/qapi-commands-rocker.o | ||||
| common-obj-y += qapi/qapi-commands-run-state.o | ||||
| common-obj-y += qapi/qapi-commands-sockets.o | ||||
| common-obj-y += qapi/qapi-commands-tpm.o | ||||
| common-obj-y += qapi/qapi-commands-trace.o | ||||
| common-obj-y += qapi/qapi-commands-transaction.o | ||||
| common-obj-y += qapi/qapi-commands-ui.o | ||||
| common-obj-y += qapi/qapi-introspect.o | ||||
| common-obj-y += qmp.o hmp.o | ||||
| common-obj-y += qapi/ | ||||
| endif | ||||
|  | ||||
| ####################################################################### | ||||
| @@ -101,6 +168,7 @@ version-obj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.o | ||||
| ###################################################################### | ||||
| # tracing | ||||
| util-obj-y +=  trace/ | ||||
| target-obj-y += trace/ | ||||
|  | ||||
| ###################################################################### | ||||
| # guest agent | ||||
| @@ -113,7 +181,6 @@ qga-vss-dll-obj-y = qga/ | ||||
|  | ||||
| ###################################################################### | ||||
| # contrib | ||||
| elf2dmp-obj-y = contrib/elf2dmp/ | ||||
| ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/ | ||||
| ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/ | ||||
| libvhost-user-obj-y = contrib/libvhost-user/ | ||||
| @@ -121,76 +188,69 @@ vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS) | ||||
| vhost-user-scsi.o-libs := $(LIBISCSI_LIBS) | ||||
| vhost-user-scsi-obj-y = contrib/vhost-user-scsi/ | ||||
| vhost-user-blk-obj-y = contrib/vhost-user-blk/ | ||||
| rdmacm-mux-obj-y = contrib/rdmacm-mux/ | ||||
|  | ||||
| ###################################################################### | ||||
| trace-events-subdirs = | ||||
| trace-events-subdirs += accel/kvm | ||||
| trace-events-subdirs += accel/tcg | ||||
| trace-events-subdirs += audio | ||||
| trace-events-subdirs += authz | ||||
| trace-events-subdirs += util | ||||
| trace-events-subdirs += crypto | ||||
| trace-events-subdirs += io | ||||
| trace-events-subdirs += migration | ||||
| trace-events-subdirs += block | ||||
| trace-events-subdirs += chardev | ||||
| trace-events-subdirs += crypto | ||||
| trace-events-subdirs += hw/9pfs | ||||
| trace-events-subdirs += hw/acpi | ||||
| trace-events-subdirs += hw/alpha | ||||
| trace-events-subdirs += hw/arm | ||||
| trace-events-subdirs += hw/audio | ||||
| trace-events-subdirs += hw/block | ||||
| trace-events-subdirs += hw/block/dataplane | ||||
| trace-events-subdirs += hw/char | ||||
| trace-events-subdirs += hw/display | ||||
| trace-events-subdirs += hw/dma | ||||
| trace-events-subdirs += hw/hppa | ||||
| trace-events-subdirs += hw/i2c | ||||
| trace-events-subdirs += hw/i386 | ||||
| trace-events-subdirs += hw/i386/xen | ||||
| trace-events-subdirs += hw/ide | ||||
| trace-events-subdirs += hw/input | ||||
| trace-events-subdirs += hw/intc | ||||
| trace-events-subdirs += hw/isa | ||||
| trace-events-subdirs += hw/mem | ||||
| trace-events-subdirs += hw/misc | ||||
| trace-events-subdirs += hw/misc/macio | ||||
| trace-events-subdirs += hw/net | ||||
| trace-events-subdirs += hw/nvram | ||||
| trace-events-subdirs += hw/pci | ||||
| trace-events-subdirs += hw/pci-host | ||||
| trace-events-subdirs += hw/ppc | ||||
| trace-events-subdirs += hw/rdma | ||||
| trace-events-subdirs += hw/rdma/vmw | ||||
| trace-events-subdirs += hw/s390x | ||||
| trace-events-subdirs += hw/virtio | ||||
| trace-events-subdirs += hw/audio | ||||
| trace-events-subdirs += hw/misc | ||||
| trace-events-subdirs += hw/misc/macio | ||||
| trace-events-subdirs += hw/usb | ||||
| trace-events-subdirs += hw/scsi | ||||
| trace-events-subdirs += hw/sd | ||||
| trace-events-subdirs += hw/nvram | ||||
| trace-events-subdirs += hw/display | ||||
| trace-events-subdirs += hw/input | ||||
| trace-events-subdirs += hw/timer | ||||
| trace-events-subdirs += hw/dma | ||||
| trace-events-subdirs += hw/sparc | ||||
| trace-events-subdirs += hw/sparc64 | ||||
| trace-events-subdirs += hw/timer | ||||
| trace-events-subdirs += hw/tpm | ||||
| trace-events-subdirs += hw/usb | ||||
| trace-events-subdirs += hw/sd | ||||
| trace-events-subdirs += hw/isa | ||||
| trace-events-subdirs += hw/mem | ||||
| trace-events-subdirs += hw/i386 | ||||
| trace-events-subdirs += hw/i386/xen | ||||
| trace-events-subdirs += hw/9pfs | ||||
| trace-events-subdirs += hw/ppc | ||||
| trace-events-subdirs += hw/pci | ||||
| trace-events-subdirs += hw/pci-host | ||||
| trace-events-subdirs += hw/s390x | ||||
| trace-events-subdirs += hw/vfio | ||||
| trace-events-subdirs += hw/virtio | ||||
| trace-events-subdirs += hw/watchdog | ||||
| trace-events-subdirs += hw/acpi | ||||
| trace-events-subdirs += hw/arm | ||||
| trace-events-subdirs += hw/alpha | ||||
| trace-events-subdirs += hw/hppa | ||||
| trace-events-subdirs += hw/xen | ||||
| trace-events-subdirs += hw/gpio | ||||
| trace-events-subdirs += io | ||||
| trace-events-subdirs += linux-user | ||||
| trace-events-subdirs += migration | ||||
| trace-events-subdirs += nbd | ||||
| trace-events-subdirs += hw/ide | ||||
| trace-events-subdirs += hw/tpm | ||||
| trace-events-subdirs += ui | ||||
| trace-events-subdirs += audio | ||||
| trace-events-subdirs += net | ||||
| trace-events-subdirs += qapi | ||||
| trace-events-subdirs += qom | ||||
| trace-events-subdirs += scsi | ||||
| trace-events-subdirs += target/arm | ||||
| trace-events-subdirs += target/hppa | ||||
| trace-events-subdirs += target/i386 | ||||
| trace-events-subdirs += target/mips | ||||
| trace-events-subdirs += target/ppc | ||||
| trace-events-subdirs += target/riscv | ||||
| trace-events-subdirs += target/s390x | ||||
| trace-events-subdirs += target/sparc | ||||
| trace-events-subdirs += ui | ||||
| trace-events-subdirs += util | ||||
| trace-events-subdirs += target/s390x | ||||
| trace-events-subdirs += target/ppc | ||||
| trace-events-subdirs += qom | ||||
| trace-events-subdirs += linux-user | ||||
| trace-events-subdirs += qapi | ||||
| trace-events-subdirs += accel/tcg | ||||
| trace-events-subdirs += accel/kvm | ||||
| trace-events-subdirs += nbd | ||||
| trace-events-subdirs += scsi | ||||
|  | ||||
| trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events) | ||||
|  | ||||
|   | ||||
| @@ -4,19 +4,16 @@ BUILD_DIR?=$(CURDIR)/.. | ||||
|  | ||||
| include ../config-host.mak | ||||
| include config-target.mak | ||||
| include $(SRC_PATH)/rules.mak | ||||
|  | ||||
| ifdef CONFIG_SOFTMMU | ||||
| include config-devices.mak | ||||
| endif | ||||
| include $(SRC_PATH)/rules.mak | ||||
|  | ||||
| $(call set-vpath, $(SRC_PATH):$(BUILD_DIR)) | ||||
| ifdef CONFIG_LINUX | ||||
| QEMU_CFLAGS += -I../linux-headers | ||||
| endif | ||||
| QEMU_CFLAGS += -iquote .. -iquote $(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H | ||||
| QEMU_CFLAGS += -I.. -I$(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H | ||||
|  | ||||
| QEMU_CFLAGS+=-iquote $(SRC_PATH)/include | ||||
| QEMU_CFLAGS+=-I$(SRC_PATH)/include | ||||
|  | ||||
| ifdef CONFIG_USER_ONLY | ||||
| # user emulator name | ||||
| @@ -39,14 +36,11 @@ endif | ||||
| PROGS=$(QEMU_PROG) $(QEMU_PROGW) | ||||
| STPFILES= | ||||
|  | ||||
| # Makefile Tests | ||||
| include $(SRC_PATH)/tests/tcg/Makefile.include | ||||
|  | ||||
| config-target.h: config-target.h-timestamp | ||||
| config-target.h-timestamp: config-target.mak | ||||
|  | ||||
| ifdef CONFIG_TRACE_SYSTEMTAP | ||||
| stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp $(QEMU_PROG)-log.stp | ||||
| stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp | ||||
|  | ||||
| ifdef CONFIG_USER_ONLY | ||||
| TARGET_TYPE=user | ||||
| @@ -85,14 +79,6 @@ $(QEMU_PROG)-simpletrace.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y) | ||||
| 		--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \ | ||||
| 		$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp") | ||||
|  | ||||
| $(QEMU_PROG)-log.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y) | ||||
| 	$(call quiet-command,$(TRACETOOL) \ | ||||
| 		--group=all \ | ||||
| 		--format=log-stap \ | ||||
| 		--backends=$(TRACE_BACKENDS) \ | ||||
| 		--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \ | ||||
| 		$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-log.stp") | ||||
|  | ||||
| else | ||||
| stap: | ||||
| endif | ||||
| @@ -103,8 +89,6 @@ all: $(PROGS) stap | ||||
| # Dummy command so that make thinks it has done something | ||||
| 	@true | ||||
|  | ||||
| obj-y += trace/ | ||||
|  | ||||
| ######################################################### | ||||
| # cpu emulator library | ||||
| obj-y += exec.o | ||||
| @@ -113,7 +97,7 @@ obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o | ||||
| obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o | ||||
| obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o | ||||
| obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o | ||||
| obj-$(CONFIG_TCG) += fpu/softfloat.o | ||||
| obj-y += fpu/softfloat.o | ||||
| obj-y += target/$(TARGET_BASE_ARCH)/ | ||||
| obj-y += disas.o | ||||
| obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o | ||||
| @@ -151,11 +135,9 @@ ifdef CONFIG_SOFTMMU | ||||
| obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o | ||||
| obj-y += qtest.o | ||||
| obj-y += hw/ | ||||
| obj-y += qapi/ | ||||
| obj-y += memory.o | ||||
| obj-y += memory_mapping.o | ||||
| obj-y += dump.o | ||||
| obj-$(TARGET_X86_64) += win_dump.o | ||||
| obj-y += migration/ram.o | ||||
| LIBS := $(libs_softmmu) $(LIBS) | ||||
|  | ||||
| @@ -170,12 +152,20 @@ GENERATED_FILES += hmp-commands.h hmp-commands-info.h | ||||
|  | ||||
| endif # CONFIG_SOFTMMU | ||||
|  | ||||
| # Workaround for http://gcc.gnu.org/PR55489, see configure. | ||||
| %/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS) | ||||
|  | ||||
| dummy := $(call unnest-vars,,obj-y) | ||||
| all-obj-y := $(obj-y) | ||||
|  | ||||
| target-obj-y := | ||||
| block-obj-y := | ||||
| common-obj-y := | ||||
| chardev-obj-y := | ||||
| include $(SRC_PATH)/Makefile.objs | ||||
| dummy := $(call unnest-vars,,target-obj-y) | ||||
| target-obj-y-save := $(target-obj-y) | ||||
| dummy := $(call unnest-vars,.., \ | ||||
|                authz-obj-y \ | ||||
|                block-obj-y \ | ||||
|                block-obj-m \ | ||||
|                chardev-obj-y \ | ||||
| @@ -185,17 +175,16 @@ dummy := $(call unnest-vars,.., \ | ||||
|                io-obj-y \ | ||||
|                common-obj-y \ | ||||
|                common-obj-m) | ||||
| target-obj-y := $(target-obj-y-save) | ||||
| all-obj-y += $(common-obj-y) | ||||
| all-obj-y += $(target-obj-y) | ||||
| all-obj-y += $(qom-obj-y) | ||||
| all-obj-$(CONFIG_SOFTMMU) += $(authz-obj-y) | ||||
| all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y) | ||||
| all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y) | ||||
| all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y) | ||||
| all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y) | ||||
|  | ||||
| ifdef CONFIG_SOFTMMU | ||||
| $(QEMU_PROG_BUILD): config-devices.mak | ||||
| endif | ||||
|  | ||||
| COMMON_LDADDS = ../libqemuutil.a | ||||
|  | ||||
| @@ -220,7 +209,6 @@ clean: clean-target | ||||
| 	rm -f *.a *~ $(PROGS) | ||||
| 	rm -f $(shell find . -name '*.[od]') | ||||
| 	rm -f hmp-commands.h gdbstub-xml.c | ||||
| 	rm -f trace/generated-helpers.c trace/generated-helpers.c-timestamp | ||||
| ifdef CONFIG_TRACE_SYSTEMTAP | ||||
| 	rm -f *.stp | ||||
| endif | ||||
| @@ -233,7 +221,6 @@ ifdef CONFIG_TRACE_SYSTEMTAP | ||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset" | ||||
| 	$(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp" | ||||
| 	$(INSTALL_DATA) $(QEMU_PROG)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp" | ||||
| 	$(INSTALL_DATA) $(QEMU_PROG)-log.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-log.stp" | ||||
| endif | ||||
|  | ||||
| GENERATED_FILES += config-target.h | ||||
|   | ||||
							
								
								
									
										4
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								README
									
									
									
									
									
								
							| @@ -54,7 +54,7 @@ Submitting patches | ||||
|  | ||||
| The QEMU source code is maintained under the GIT version control system. | ||||
|  | ||||
|    git clone https://git.qemu.org/git/qemu.git | ||||
|    git clone git://git.qemu.org/qemu.git | ||||
|  | ||||
| When submitting patches, one common approach is to use 'git | ||||
| format-patch' and/or 'git send-email' to format & send the mail to the | ||||
| @@ -70,7 +70,7 @@ the QEMU website | ||||
|  | ||||
| The QEMU website is also maintained under source control. | ||||
|  | ||||
|   git clone https://git.qemu.org/git/qemu-web.git | ||||
|   git clone git://git.qemu.org/qemu-web.git | ||||
|   https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/ | ||||
|  | ||||
| A 'git-publish' utility was created to make above process less | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| obj-$(CONFIG_SOFTMMU) += accel.o | ||||
| obj-$(CONFIG_KVM) += kvm/ | ||||
| obj-y += kvm/ | ||||
| obj-$(CONFIG_TCG) += tcg/ | ||||
| obj-y += stubs/ | ||||
|   | ||||
| @@ -34,7 +34,6 @@ | ||||
| #include "qom/object.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "qemu/option.h" | ||||
| #include "qapi/error.h" | ||||
|  | ||||
| static const TypeInfo accel_type = { | ||||
|     .name = TYPE_ACCEL, | ||||
| @@ -66,14 +65,13 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms) | ||||
|         *(acc->allowed) = false; | ||||
|         object_unref(OBJECT(accel)); | ||||
|     } | ||||
|     object_set_accelerator_compat_props(acc->compat_props); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| void configure_accelerator(MachineState *ms, const char *progname) | ||||
| void configure_accelerator(MachineState *ms) | ||||
| { | ||||
|     const char *accel; | ||||
|     char **accel_list, **tmp; | ||||
|     const char *accel, *p; | ||||
|     char buf[10]; | ||||
|     int ret; | ||||
|     bool accel_initialised = false; | ||||
|     bool init_failed = false; | ||||
| @@ -81,28 +79,17 @@ void configure_accelerator(MachineState *ms, const char *progname) | ||||
|  | ||||
|     accel = qemu_opt_get(qemu_get_machine_opts(), "accel"); | ||||
|     if (accel == NULL) { | ||||
|         /* Select the default accelerator */ | ||||
|         int pnlen = strlen(progname); | ||||
|         if (pnlen >= 3 && g_str_equal(&progname[pnlen - 3], "kvm")) { | ||||
|             /* If the program name ends with "kvm", we prefer KVM */ | ||||
|             accel = "kvm:tcg"; | ||||
|         } else { | ||||
| #if defined(CONFIG_TCG) | ||||
|             accel = "tcg"; | ||||
| #elif defined(CONFIG_KVM) | ||||
|             accel = "kvm"; | ||||
| #else | ||||
|             error_report("No accelerator selected and" | ||||
|                          " no default accelerator available"); | ||||
|             exit(1); | ||||
| #endif | ||||
|         } | ||||
|         /* Use the default "accelerator", tcg */ | ||||
|         accel = "tcg"; | ||||
|     } | ||||
|  | ||||
|     accel_list = g_strsplit(accel, ":", 0); | ||||
|  | ||||
|     for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) { | ||||
|         acc = accel_find(*tmp); | ||||
|     p = accel; | ||||
|     while (!accel_initialised && *p != '\0') { | ||||
|         if (*p == ':') { | ||||
|             p++; | ||||
|         } | ||||
|         p = get_opt_name(buf, sizeof(buf), p, ':'); | ||||
|         acc = accel_find(buf); | ||||
|         if (!acc) { | ||||
|             continue; | ||||
|         } | ||||
| @@ -120,7 +107,6 @@ void configure_accelerator(MachineState *ms, const char *progname) | ||||
|             accel_initialised = true; | ||||
|         } | ||||
|     } | ||||
|     g_strfreev(accel_list); | ||||
|  | ||||
|     if (!accel_initialised) { | ||||
|         if (!init_failed) { | ||||
| @@ -134,13 +120,10 @@ void configure_accelerator(MachineState *ms, const char *progname) | ||||
|     } | ||||
| } | ||||
|  | ||||
| void accel_setup_post(MachineState *ms) | ||||
| void accel_register_compat_props(AccelState *accel) | ||||
| { | ||||
|     AccelState *accel = ms->accelerator; | ||||
|     AccelClass *acc = ACCEL_GET_CLASS(accel); | ||||
|     if (acc->setup_post) { | ||||
|         acc->setup_post(ms, accel); | ||||
|     } | ||||
|     AccelClass *class = ACCEL_GET_CLASS(accel); | ||||
|     register_compat_props_array(class->global_props); | ||||
| } | ||||
|  | ||||
| static void register_accel_types(void) | ||||
|   | ||||
| @@ -1,2 +1 @@ | ||||
| obj-y += kvm-all.o | ||||
| obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o | ||||
| obj-$(CONFIG_KVM) += kvm-all.o | ||||
|   | ||||
| @@ -38,8 +38,6 @@ | ||||
| #include "qemu/event_notifier.h" | ||||
| #include "trace.h" | ||||
| #include "hw/irq.h" | ||||
| #include "sysemu/sev.h" | ||||
| #include "sysemu/balloon.h" | ||||
|  | ||||
| #include "hw/boards.h" | ||||
|  | ||||
| @@ -79,14 +77,13 @@ struct KVMState | ||||
|     int fd; | ||||
|     int vmfd; | ||||
|     int coalesced_mmio; | ||||
|     int coalesced_pio; | ||||
|     struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; | ||||
|     bool coalesced_flush_in_progress; | ||||
|     int vcpu_events; | ||||
|     int robust_singlestep; | ||||
|     int debugregs; | ||||
| #ifdef KVM_CAP_SET_GUEST_DEBUG | ||||
|     QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints; | ||||
|     struct kvm_sw_breakpoint_head kvm_sw_breakpoints; | ||||
| #endif | ||||
|     int many_ioeventfds; | ||||
|     int intx_set_mask; | ||||
| @@ -102,14 +99,10 @@ struct KVMState | ||||
|     int nr_allocated_irq_routes; | ||||
|     unsigned long *used_gsi_bitmap; | ||||
|     unsigned int gsi_count; | ||||
|     QTAILQ_HEAD(, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE]; | ||||
|     QTAILQ_HEAD(msi_hashtab, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE]; | ||||
| #endif | ||||
|     KVMMemoryListener memory_listener; | ||||
|     QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus; | ||||
|  | ||||
|     /* memory encryption */ | ||||
|     void *memcrypt_handle; | ||||
|     int (*memcrypt_encrypt_data)(void *handle, uint8_t *ptr, uint64_t len); | ||||
| }; | ||||
|  | ||||
| KVMState *kvm_state; | ||||
| @@ -145,26 +138,6 @@ int kvm_get_max_memslots(void) | ||||
|     return s->nr_slots; | ||||
| } | ||||
|  | ||||
| bool kvm_memcrypt_enabled(void) | ||||
| { | ||||
|     if (kvm_state && kvm_state->memcrypt_handle) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len) | ||||
| { | ||||
|     if (kvm_state->memcrypt_handle && | ||||
|         kvm_state->memcrypt_encrypt_data) { | ||||
|         return kvm_state->memcrypt_encrypt_data(kvm_state->memcrypt_handle, | ||||
|                                               ptr, len); | ||||
|     } | ||||
|  | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml) | ||||
| { | ||||
|     KVMState *s = kvm_state; | ||||
| @@ -258,7 +231,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new) | ||||
| static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot) | ||||
| { | ||||
|     KVMState *s = kvm_state; | ||||
|     struct kvm_userspace_memory_region mem; | ||||
| @@ -269,7 +242,7 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo | ||||
|     mem.userspace_addr = (unsigned long)slot->ram; | ||||
|     mem.flags = slot->flags; | ||||
|  | ||||
|     if (slot->memory_size && !new && (mem.flags ^ slot->old_flags) & KVM_MEM_READONLY) { | ||||
|     if (slot->memory_size && mem.flags & KVM_MEM_READONLY) { | ||||
|         /* Set the slot size to 0 before setting the slot to the desired | ||||
|          * value. This is needed based on KVM commit 75d61fbc. */ | ||||
|         mem.memory_size = 0; | ||||
| @@ -277,7 +250,6 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo | ||||
|     } | ||||
|     mem.memory_size = slot->memory_size; | ||||
|     ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); | ||||
|     slot->old_flags = mem.flags; | ||||
|     trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr, | ||||
|                               mem.memory_size, mem.userspace_addr, ret); | ||||
|     return ret; | ||||
| @@ -394,14 +366,17 @@ static int kvm_mem_flags(MemoryRegion *mr) | ||||
| static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem, | ||||
|                                  MemoryRegion *mr) | ||||
| { | ||||
|     int old_flags; | ||||
|  | ||||
|     old_flags = mem->flags; | ||||
|     mem->flags = kvm_mem_flags(mr); | ||||
|  | ||||
|     /* If nothing changed effectively, no need to issue ioctl */ | ||||
|     if (mem->flags == mem->old_flags) { | ||||
|     if (mem->flags == old_flags) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     return kvm_set_user_memory_region(kml, mem, false); | ||||
|     return kvm_set_user_memory_region(kml, mem); | ||||
| } | ||||
|  | ||||
| static int kvm_section_update_flags(KVMMemoryListener *kml, | ||||
| @@ -561,45 +536,6 @@ static void kvm_uncoalesce_mmio_region(MemoryListener *listener, | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void kvm_coalesce_pio_add(MemoryListener *listener, | ||||
|                                 MemoryRegionSection *section, | ||||
|                                 hwaddr start, hwaddr size) | ||||
| { | ||||
|     KVMState *s = kvm_state; | ||||
|  | ||||
|     if (s->coalesced_pio) { | ||||
|         struct kvm_coalesced_mmio_zone zone; | ||||
|  | ||||
|         zone.addr = start; | ||||
|         zone.size = size; | ||||
|         zone.pio = 1; | ||||
|  | ||||
|         (void)kvm_vm_ioctl(s, KVM_REGISTER_COALESCED_MMIO, &zone); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void kvm_coalesce_pio_del(MemoryListener *listener, | ||||
|                                 MemoryRegionSection *section, | ||||
|                                 hwaddr start, hwaddr size) | ||||
| { | ||||
|     KVMState *s = kvm_state; | ||||
|  | ||||
|     if (s->coalesced_pio) { | ||||
|         struct kvm_coalesced_mmio_zone zone; | ||||
|  | ||||
|         zone.addr = start; | ||||
|         zone.size = size; | ||||
|         zone.pio = 1; | ||||
|  | ||||
|         (void)kvm_vm_ioctl(s, KVM_UNREGISTER_COALESCED_MMIO, &zone); | ||||
|      } | ||||
| } | ||||
|  | ||||
| static MemoryListener kvm_coalesced_pio_listener = { | ||||
|     .coalesced_io_add = kvm_coalesce_pio_add, | ||||
|     .coalesced_io_del = kvm_coalesce_pio_del, | ||||
| }; | ||||
|  | ||||
| int kvm_check_extension(KVMState *s, unsigned int extension) | ||||
| { | ||||
|     int ret; | ||||
| @@ -657,8 +593,6 @@ static int kvm_set_ioeventfd_mmio(int fd, hwaddr addr, uint32_t val, | ||||
|         .fd = fd, | ||||
|     }; | ||||
|  | ||||
|     trace_kvm_set_ioeventfd_mmio(fd, (uint64_t)addr, val, assign, size, | ||||
|                                  datamatch); | ||||
|     if (!kvm_enabled()) { | ||||
|         return -ENOSYS; | ||||
|     } | ||||
| @@ -690,7 +624,6 @@ static int kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint16_t val, | ||||
|         .fd = fd, | ||||
|     }; | ||||
|     int r; | ||||
|     trace_kvm_set_ioeventfd_pio(fd, addr, val, assign, size, datamatch); | ||||
|     if (!kvm_enabled()) { | ||||
|         return -ENOSYS; | ||||
|     } | ||||
| @@ -797,8 +730,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, | ||||
|  | ||||
|         /* unregister the slot */ | ||||
|         mem->memory_size = 0; | ||||
|         mem->flags = 0; | ||||
|         err = kvm_set_user_memory_region(kml, mem, false); | ||||
|         err = kvm_set_user_memory_region(kml, mem); | ||||
|         if (err) { | ||||
|             fprintf(stderr, "%s: error unregistering slot: %s\n", | ||||
|                     __func__, strerror(-err)); | ||||
| @@ -814,7 +746,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, | ||||
|     mem->ram = ram; | ||||
|     mem->flags = kvm_mem_flags(mr); | ||||
|  | ||||
|     err = kvm_set_user_memory_region(kml, mem, true); | ||||
|     err = kvm_set_user_memory_region(kml, mem); | ||||
|     if (err) { | ||||
|         fprintf(stderr, "%s: error registering slot: %s\n", __func__, | ||||
|                 strerror(-err)); | ||||
| @@ -1593,7 +1525,7 @@ static int kvm_init(MachineState *ms) | ||||
|  | ||||
|     kvm_type = qemu_opt_get(qemu_get_machine_opts(), "kvm-type"); | ||||
|     if (mc->kvm_type) { | ||||
|         type = mc->kvm_type(ms, kvm_type); | ||||
|         type = mc->kvm_type(kvm_type); | ||||
|     } else if (kvm_type) { | ||||
|         ret = -EINVAL; | ||||
|         fprintf(stderr, "Invalid argument kvm-type=%s\n", kvm_type); | ||||
| @@ -1659,8 +1591,6 @@ static int kvm_init(MachineState *ms) | ||||
|     } | ||||
|  | ||||
|     s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO); | ||||
|     s->coalesced_pio = s->coalesced_mmio && | ||||
|                        kvm_check_extension(s, KVM_CAP_COALESCED_PIO); | ||||
|  | ||||
| #ifdef KVM_CAP_VCPU_EVENTS | ||||
|     s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS); | ||||
| @@ -1684,8 +1614,10 @@ static int kvm_init(MachineState *ms) | ||||
|         s->irq_set_ioctl = KVM_IRQ_LINE_STATUS; | ||||
|     } | ||||
|  | ||||
| #ifdef KVM_CAP_READONLY_MEM | ||||
|     kvm_readonly_mem_allowed = | ||||
|         (kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0); | ||||
| #endif | ||||
|  | ||||
|     kvm_eventfds_allowed = | ||||
|         (kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0); | ||||
| @@ -1704,20 +1636,6 @@ static int kvm_init(MachineState *ms) | ||||
|  | ||||
|     kvm_state = s; | ||||
|  | ||||
|     /* | ||||
|      * if memory encryption object is specified then initialize the memory | ||||
|      * encryption context. | ||||
|      */ | ||||
|     if (ms->memory_encryption) { | ||||
|         kvm_state->memcrypt_handle = sev_guest_init(ms->memory_encryption); | ||||
|         if (!kvm_state->memcrypt_handle) { | ||||
|             ret = -1; | ||||
|             goto err; | ||||
|         } | ||||
|  | ||||
|         kvm_state->memcrypt_encrypt_data = sev_encrypt_data; | ||||
|     } | ||||
|  | ||||
|     ret = kvm_arch_init(ms, s); | ||||
|     if (ret < 0) { | ||||
|         goto err; | ||||
| @@ -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_del = kvm_mem_ioeventfd_del; | ||||
|     } | ||||
|     s->memory_listener.listener.coalesced_io_add = kvm_coalesce_mmio_region; | ||||
|     s->memory_listener.listener.coalesced_io_del = kvm_uncoalesce_mmio_region; | ||||
|     s->memory_listener.listener.coalesced_mmio_add = kvm_coalesce_mmio_region; | ||||
|     s->memory_listener.listener.coalesced_mmio_del = kvm_uncoalesce_mmio_region; | ||||
|  | ||||
|     kvm_memory_listener_register(s, &s->memory_listener, | ||||
|                                  &address_space_memory, 0); | ||||
|     memory_listener_register(&kvm_io_listener, | ||||
|                              &address_space_io); | ||||
|     memory_listener_register(&kvm_coalesced_pio_listener, | ||||
|                              &address_space_io); | ||||
|  | ||||
|     s->many_ioeventfds = kvm_check_many_ioeventfds(); | ||||
|  | ||||
|     s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU); | ||||
|     if (!s->sync_mmu) { | ||||
|         qemu_balloon_inhibit(true); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
|  | ||||
| @@ -1825,13 +1738,7 @@ void kvm_flush_coalesced_mmio_buffer(void) | ||||
|  | ||||
|             ent = &ring->coalesced_mmio[ring->first]; | ||||
|  | ||||
|             if (ent->pio == 1) { | ||||
|                 address_space_rw(&address_space_io, ent->phys_addr, | ||||
|                                  MEMTXATTRS_UNSPECIFIED, ent->data, | ||||
|                                  ent->len, true); | ||||
|             } else { | ||||
|                 cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len); | ||||
|             } | ||||
|             cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len); | ||||
|             smp_wmb(); | ||||
|             ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX; | ||||
|         } | ||||
|   | ||||
| @@ -1,26 +0,0 @@ | ||||
| /* | ||||
|  * QEMU SEV stub | ||||
|  * | ||||
|  * Copyright Advanced Micro Devices 2018 | ||||
|  * | ||||
|  * Authors: | ||||
|  *      Brijesh Singh <brijesh.singh@amd.com> | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||||
|  * See the COPYING file in the top-level directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "sysemu/sev.h" | ||||
|  | ||||
| int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len) | ||||
| { | ||||
|     abort(); | ||||
| } | ||||
|  | ||||
| void *sev_guest_init(const char *id) | ||||
| { | ||||
|     return NULL; | ||||
| } | ||||
| @@ -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_update_msi_route(int virq) "Updating MSI route 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" | ||||
|  | ||||
|   | ||||
| @@ -105,16 +105,6 @@ int kvm_on_sigbus(int code, void *addr) | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| bool kvm_memcrypt_enabled(void) | ||||
| { | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len) | ||||
| { | ||||
|   return 1; | ||||
| } | ||||
|  | ||||
| #ifndef CONFIG_USER_ONLY | ||||
| int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev) | ||||
| { | ||||
|   | ||||
| @@ -21,6 +21,10 @@ void tb_flush(CPUState *cpu) | ||||
| { | ||||
| } | ||||
|  | ||||
| void tb_unlock(void) | ||||
| { | ||||
| } | ||||
|  | ||||
| void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) | ||||
| { | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| @@ -18,37 +18,26 @@ | ||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #include "trace/mem.h" | ||||
|  | ||||
| #if DATA_SIZE == 16 | ||||
| # define SUFFIX     o | ||||
| # define DATA_TYPE  Int128 | ||||
| # define BSWAP      bswap128 | ||||
| # define SHIFT      4 | ||||
| #elif DATA_SIZE == 8 | ||||
| # define SUFFIX     q | ||||
| # define DATA_TYPE  uint64_t | ||||
| # define SDATA_TYPE int64_t | ||||
| # define BSWAP      bswap64 | ||||
| # define SHIFT      3 | ||||
| #elif DATA_SIZE == 4 | ||||
| # define SUFFIX     l | ||||
| # define DATA_TYPE  uint32_t | ||||
| # define SDATA_TYPE int32_t | ||||
| # define BSWAP      bswap32 | ||||
| # define SHIFT      2 | ||||
| #elif DATA_SIZE == 2 | ||||
| # define SUFFIX     w | ||||
| # define DATA_TYPE  uint16_t | ||||
| # define SDATA_TYPE int16_t | ||||
| # define BSWAP      bswap16 | ||||
| # define SHIFT      1 | ||||
| #elif DATA_SIZE == 1 | ||||
| # define SUFFIX     b | ||||
| # define DATA_TYPE  uint8_t | ||||
| # define SDATA_TYPE int8_t | ||||
| # define BSWAP | ||||
| # define SHIFT      0 | ||||
| #else | ||||
| # error unsupported data size | ||||
| #endif | ||||
| @@ -59,37 +48,14 @@ | ||||
| # define ABI_TYPE  uint32_t | ||||
| #endif | ||||
|  | ||||
| #define ATOMIC_TRACE_RMW do {                                           \ | ||||
|         uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \ | ||||
|                                                                         \ | ||||
|         trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \ | ||||
|         trace_guest_mem_before_exec(ENV_GET_CPU(env), addr,             \ | ||||
|                                     info | TRACE_MEM_ST);               \ | ||||
|     } while (0) | ||||
|  | ||||
| #define ATOMIC_TRACE_LD do {                                            \ | ||||
|         uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \ | ||||
|                                                                         \ | ||||
|         trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \ | ||||
|     } while (0) | ||||
|  | ||||
| # define ATOMIC_TRACE_ST do {                                           \ | ||||
|         uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true); \ | ||||
|                                                                         \ | ||||
|         trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \ | ||||
|     } while (0) | ||||
|  | ||||
| /* Define host-endian atomic operations.  Note that END is used within | ||||
|    the ATOMIC_NAME macro, and redefined below.  */ | ||||
| #if DATA_SIZE == 1 | ||||
| # define END | ||||
| # define MEND _be /* either le or be would be fine */ | ||||
| #elif defined(HOST_WORDS_BIGENDIAN) | ||||
| # define END  _be | ||||
| # define MEND _be | ||||
| #else | ||||
| # define END  _le | ||||
| # define MEND _le | ||||
| #endif | ||||
|  | ||||
| ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | ||||
| @@ -97,27 +63,17 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||
|     DATA_TYPE ret; | ||||
|  | ||||
|     ATOMIC_TRACE_RMW; | ||||
| #if DATA_SIZE == 16 | ||||
|     ret = atomic16_cmpxchg(haddr, cmpv, newv); | ||||
| #else | ||||
|     ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); | ||||
| #endif | ||||
|     DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| #if DATA_SIZE >= 16 | ||||
| #if HAVE_ATOMIC128 | ||||
| ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; | ||||
|  | ||||
|     ATOMIC_TRACE_LD; | ||||
|     val = atomic16_read(haddr); | ||||
|     __atomic_load(haddr, &val, __ATOMIC_RELAXED); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
|     return val; | ||||
| } | ||||
| @@ -127,22 +83,16 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||
|  | ||||
|     ATOMIC_TRACE_ST; | ||||
|     atomic16_set(haddr, val); | ||||
|     __atomic_store(haddr, &val, __ATOMIC_RELAXED); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
| } | ||||
| #endif | ||||
| #else | ||||
| ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, | ||||
|                            ABI_TYPE val EXTRA_ARGS) | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||
|     DATA_TYPE ret; | ||||
|  | ||||
|     ATOMIC_TRACE_RMW; | ||||
|     ret = atomic_xchg__nocheck(haddr, val); | ||||
|     DATA_TYPE ret = atomic_xchg__nocheck(haddr, val); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
|     return ret; | ||||
| } | ||||
| @@ -153,10 +103,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ | ||||
| {                                                                   \ | ||||
|     ATOMIC_MMU_DECLS;                                               \ | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \ | ||||
|     DATA_TYPE ret;                                                  \ | ||||
|                                                                     \ | ||||
|     ATOMIC_TRACE_RMW;                                               \ | ||||
|     ret = atomic_##X(haddr, val);                                   \ | ||||
|     DATA_TYPE ret = atomic_##X(haddr, val);                         \ | ||||
|     ATOMIC_MMU_CLEANUP;                                             \ | ||||
|     return ret;                                                     \ | ||||
| } | ||||
| @@ -171,48 +118,9 @@ GEN_ATOMIC_HELPER(or_fetch) | ||||
| GEN_ATOMIC_HELPER(xor_fetch) | ||||
|  | ||||
| #undef GEN_ATOMIC_HELPER | ||||
|  | ||||
| /* These helpers are, as a whole, full barriers.  Within the helper, | ||||
|  * the leading barrier is explicit and the trailing barrier is within | ||||
|  * cmpxchg primitive. | ||||
|  * | ||||
|  * Trace this load + RMW loop as a single RMW op. This way, regardless | ||||
|  * of CF_PARALLEL's value, we'll trace just a read and a write. | ||||
|  */ | ||||
| #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET)                \ | ||||
| ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ | ||||
|                         ABI_TYPE xval EXTRA_ARGS)                   \ | ||||
| {                                                                   \ | ||||
|     ATOMIC_MMU_DECLS;                                               \ | ||||
|     XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                          \ | ||||
|     XDATA_TYPE cmp, old, new, val = xval;                           \ | ||||
|                                                                     \ | ||||
|     ATOMIC_TRACE_RMW;                                               \ | ||||
|     smp_mb();                                                       \ | ||||
|     cmp = atomic_read__nocheck(haddr);                              \ | ||||
|     do {                                                            \ | ||||
|         old = cmp; new = FN(old, val);                              \ | ||||
|         cmp = atomic_cmpxchg__nocheck(haddr, old, new);             \ | ||||
|     } while (cmp != old);                                           \ | ||||
|     ATOMIC_MMU_CLEANUP;                                             \ | ||||
|     return RET;                                                     \ | ||||
| } | ||||
|  | ||||
| GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old) | ||||
| GEN_ATOMIC_HELPER_FN(fetch_umin, MIN,  DATA_TYPE, old) | ||||
| GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old) | ||||
| GEN_ATOMIC_HELPER_FN(fetch_umax, MAX,  DATA_TYPE, old) | ||||
|  | ||||
| GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new) | ||||
| GEN_ATOMIC_HELPER_FN(umin_fetch, MIN,  DATA_TYPE, new) | ||||
| GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new) | ||||
| GEN_ATOMIC_HELPER_FN(umax_fetch, MAX,  DATA_TYPE, new) | ||||
|  | ||||
| #undef GEN_ATOMIC_HELPER_FN | ||||
| #endif /* DATA SIZE >= 16 */ | ||||
|  | ||||
| #undef END | ||||
| #undef MEND | ||||
|  | ||||
| #if DATA_SIZE > 1 | ||||
|  | ||||
| @@ -220,10 +128,8 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX,  DATA_TYPE, new) | ||||
|    within the ATOMIC_NAME macro.  */ | ||||
| #ifdef HOST_WORDS_BIGENDIAN | ||||
| # define END  _le | ||||
| # define MEND _le | ||||
| #else | ||||
| # define END  _be | ||||
| # define MEND _be | ||||
| #endif | ||||
|  | ||||
| ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | ||||
| @@ -231,27 +137,17 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||
|     DATA_TYPE ret; | ||||
|  | ||||
|     ATOMIC_TRACE_RMW; | ||||
| #if DATA_SIZE == 16 | ||||
|     ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv)); | ||||
| #else | ||||
|     ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); | ||||
| #endif | ||||
|     DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
|     return BSWAP(ret); | ||||
| } | ||||
|  | ||||
| #if DATA_SIZE >= 16 | ||||
| #if HAVE_ATOMIC128 | ||||
| ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; | ||||
|  | ||||
|     ATOMIC_TRACE_LD; | ||||
|     val = atomic16_read(haddr); | ||||
|     __atomic_load(haddr, &val, __ATOMIC_RELAXED); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
|     return BSWAP(val); | ||||
| } | ||||
| @@ -261,23 +157,17 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||
|  | ||||
|     ATOMIC_TRACE_ST; | ||||
|     val = BSWAP(val); | ||||
|     atomic16_set(haddr, val); | ||||
|     __atomic_store(haddr, &val, __ATOMIC_RELAXED); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
| } | ||||
| #endif | ||||
| #else | ||||
| ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, | ||||
|                            ABI_TYPE val EXTRA_ARGS) | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||
|     ABI_TYPE ret; | ||||
|  | ||||
|     ATOMIC_TRACE_RMW; | ||||
|     ret = atomic_xchg__nocheck(haddr, BSWAP(val)); | ||||
|     ABI_TYPE ret = atomic_xchg__nocheck(haddr, BSWAP(val)); | ||||
|     ATOMIC_MMU_CLEANUP; | ||||
|     return BSWAP(ret); | ||||
| } | ||||
| @@ -288,10 +178,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ | ||||
| {                                                                   \ | ||||
|     ATOMIC_MMU_DECLS;                                               \ | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \ | ||||
|     DATA_TYPE ret;                                                  \ | ||||
|                                                                     \ | ||||
|     ATOMIC_TRACE_RMW;                                               \ | ||||
|     ret = atomic_##X(haddr, BSWAP(val));                            \ | ||||
|     DATA_TYPE ret = atomic_##X(haddr, BSWAP(val));                  \ | ||||
|     ATOMIC_MMU_CLEANUP;                                             \ | ||||
|     return BSWAP(ret);                                              \ | ||||
| } | ||||
| @@ -305,64 +192,54 @@ GEN_ATOMIC_HELPER(xor_fetch) | ||||
|  | ||||
| #undef GEN_ATOMIC_HELPER | ||||
|  | ||||
| /* These helpers are, as a whole, full barriers.  Within the helper, | ||||
|  * the leading barrier is explicit and the trailing barrier is within | ||||
|  * cmpxchg primitive. | ||||
|  * | ||||
|  * Trace this load + RMW loop as a single RMW op. This way, regardless | ||||
|  * of CF_PARALLEL's value, we'll trace just a read and a write. | ||||
|  */ | ||||
| #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET)                \ | ||||
| ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ | ||||
|                         ABI_TYPE xval EXTRA_ARGS)                   \ | ||||
| {                                                                   \ | ||||
|     ATOMIC_MMU_DECLS;                                               \ | ||||
|     XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                          \ | ||||
|     XDATA_TYPE ldo, ldn, old, new, val = xval;                      \ | ||||
|                                                                     \ | ||||
|     ATOMIC_TRACE_RMW;                                               \ | ||||
|     smp_mb();                                                       \ | ||||
|     ldn = atomic_read__nocheck(haddr);                              \ | ||||
|     do {                                                            \ | ||||
|         ldo = ldn; old = BSWAP(ldo); new = FN(old, val);            \ | ||||
|         ldn = atomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new));      \ | ||||
|     } while (ldo != ldn);                                           \ | ||||
|     ATOMIC_MMU_CLEANUP;                                             \ | ||||
|     return RET;                                                     \ | ||||
| } | ||||
|  | ||||
| GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old) | ||||
| GEN_ATOMIC_HELPER_FN(fetch_umin, MIN,  DATA_TYPE, old) | ||||
| GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old) | ||||
| GEN_ATOMIC_HELPER_FN(fetch_umax, MAX,  DATA_TYPE, old) | ||||
|  | ||||
| GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new) | ||||
| GEN_ATOMIC_HELPER_FN(umin_fetch, MIN,  DATA_TYPE, new) | ||||
| GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new) | ||||
| GEN_ATOMIC_HELPER_FN(umax_fetch, MAX,  DATA_TYPE, new) | ||||
|  | ||||
| /* Note that for addition, we need to use a separate cmpxchg loop instead | ||||
|    of bswaps for the reverse-host-endian helpers.  */ | ||||
| #define ADD(X, Y)   (X + Y) | ||||
| GEN_ATOMIC_HELPER_FN(fetch_add, ADD, DATA_TYPE, old) | ||||
| GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new) | ||||
| #undef ADD | ||||
| ABI_TYPE ATOMIC_NAME(fetch_add)(CPUArchState *env, target_ulong addr, | ||||
|                          ABI_TYPE val EXTRA_ARGS) | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||
|     DATA_TYPE ldo, ldn, ret, sto; | ||||
|  | ||||
| #undef GEN_ATOMIC_HELPER_FN | ||||
|     ldo = atomic_read__nocheck(haddr); | ||||
|     while (1) { | ||||
|         ret = BSWAP(ldo); | ||||
|         sto = BSWAP(ret + val); | ||||
|         ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto); | ||||
|         if (ldn == ldo) { | ||||
|             ATOMIC_MMU_CLEANUP; | ||||
|             return ret; | ||||
|         } | ||||
|         ldo = ldn; | ||||
|     } | ||||
| } | ||||
|  | ||||
| ABI_TYPE ATOMIC_NAME(add_fetch)(CPUArchState *env, target_ulong addr, | ||||
|                          ABI_TYPE val EXTRA_ARGS) | ||||
| { | ||||
|     ATOMIC_MMU_DECLS; | ||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||
|     DATA_TYPE ldo, ldn, ret, sto; | ||||
|  | ||||
|     ldo = atomic_read__nocheck(haddr); | ||||
|     while (1) { | ||||
|         ret = BSWAP(ldo) + val; | ||||
|         sto = BSWAP(ret); | ||||
|         ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto); | ||||
|         if (ldn == ldo) { | ||||
|             ATOMIC_MMU_CLEANUP; | ||||
|             return ret; | ||||
|         } | ||||
|         ldo = ldn; | ||||
|     } | ||||
| } | ||||
| #endif /* DATA_SIZE >= 16 */ | ||||
|  | ||||
| #undef END | ||||
| #undef MEND | ||||
| #endif /* DATA_SIZE > 1 */ | ||||
|  | ||||
| #undef ATOMIC_TRACE_ST | ||||
| #undef ATOMIC_TRACE_LD | ||||
| #undef ATOMIC_TRACE_RMW | ||||
|  | ||||
| #undef BSWAP | ||||
| #undef ABI_TYPE | ||||
| #undef DATA_TYPE | ||||
| #undef SDATA_TYPE | ||||
| #undef SUFFIX | ||||
| #undef DATA_SIZE | ||||
| #undef SHIFT | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| @@ -27,8 +27,10 @@ bool tcg_allowed; | ||||
| /* exit the current TB, but without causing any exception to be raised */ | ||||
| void cpu_loop_exit_noexc(CPUState *cpu) | ||||
| { | ||||
|     /* XXX: restore cpu registers saved in host registers */ | ||||
|  | ||||
|     cpu->exception_index = -1; | ||||
|     cpu_loop_exit(cpu); | ||||
|     siglongjmp(cpu->jmp_env, 1); | ||||
| } | ||||
|  | ||||
| #if defined(CONFIG_SOFTMMU) | ||||
| @@ -63,17 +65,15 @@ void cpu_reloading_memory_map(void) | ||||
|  | ||||
| void cpu_loop_exit(CPUState *cpu) | ||||
| { | ||||
|     /* Undo the setting in cpu_tb_exec.  */ | ||||
|     cpu->can_do_io = 1; | ||||
|     siglongjmp(cpu->jmp_env, 1); | ||||
| } | ||||
|  | ||||
| void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) | ||||
| { | ||||
|     if (pc) { | ||||
|         cpu_restore_state(cpu, pc, true); | ||||
|         cpu_restore_state(cpu, pc); | ||||
|     } | ||||
|     cpu_loop_exit(cpu); | ||||
|     siglongjmp(cpu->jmp_env, 1); | ||||
| } | ||||
|  | ||||
| void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc) | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| @@ -25,6 +25,7 @@ | ||||
| #include "qemu/atomic.h" | ||||
| #include "sysemu/qtest.h" | ||||
| #include "qemu/timer.h" | ||||
| #include "exec/address-spaces.h" | ||||
| #include "qemu/rcu.h" | ||||
| #include "exec/tb-hash.h" | ||||
| #include "exec/tb-lookup.h" | ||||
| @@ -155,14 +156,11 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb) | ||||
|     if (qemu_loglevel_mask(CPU_LOG_TB_CPU) | ||||
|         && qemu_log_in_addr_range(itb->pc)) { | ||||
|         qemu_log_lock(); | ||||
|         int flags = 0; | ||||
|         if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) { | ||||
|             flags |= CPU_DUMP_FPU; | ||||
|         } | ||||
| #if defined(TARGET_I386) | ||||
|         flags |= CPU_DUMP_CCOP; | ||||
|         log_cpu_state(cpu, CPU_DUMP_CCOP); | ||||
| #else | ||||
|         log_cpu_state(cpu, 0); | ||||
| #endif | ||||
|         log_cpu_state(cpu, flags); | ||||
|         qemu_log_unlock(); | ||||
|     } | ||||
| #endif /* DEBUG_DISAS */ | ||||
| @@ -212,20 +210,20 @@ static void cpu_exec_nocache(CPUState *cpu, int max_cycles, | ||||
|        We only end up here when an existing TB is too long.  */ | ||||
|     cflags |= MIN(max_cycles, CF_COUNT_MASK); | ||||
|  | ||||
|     mmap_lock(); | ||||
|     tb_lock(); | ||||
|     tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, | ||||
|                      orig_tb->flags, cflags); | ||||
|     tb->orig_tb = orig_tb; | ||||
|     mmap_unlock(); | ||||
|     tb_unlock(); | ||||
|  | ||||
|     /* execute the generated code */ | ||||
|     trace_exec_tb_nocache(tb, tb->pc); | ||||
|     cpu_tb_exec(cpu, tb); | ||||
|  | ||||
|     mmap_lock(); | ||||
|     tb_lock(); | ||||
|     tb_phys_invalidate(tb, -1); | ||||
|     mmap_unlock(); | ||||
|     tcg_tb_remove(tb); | ||||
|     tb_remove(tb); | ||||
|     tb_unlock(); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @@ -244,7 +242,12 @@ void cpu_exec_step_atomic(CPUState *cpu) | ||||
|         tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); | ||||
|         if (tb == NULL) { | ||||
|             mmap_lock(); | ||||
|             tb = tb_gen_code(cpu, pc, cs_base, flags, cflags); | ||||
|             tb_lock(); | ||||
|             tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask); | ||||
|             if (likely(tb == NULL)) { | ||||
|                 tb = tb_gen_code(cpu, pc, cs_base, flags, cflags); | ||||
|             } | ||||
|             tb_unlock(); | ||||
|             mmap_unlock(); | ||||
|         } | ||||
|  | ||||
| @@ -259,17 +262,15 @@ void cpu_exec_step_atomic(CPUState *cpu) | ||||
|         cpu_tb_exec(cpu, tb); | ||||
|         cc->cpu_exec_exit(cpu); | ||||
|     } else { | ||||
|         /* | ||||
|         /* We may have exited due to another problem here, so we need | ||||
|          * to reset any tb_locks we may have taken but didn't release. | ||||
|          * The mmap_lock is dropped by tb_gen_code if it runs out of | ||||
|          * memory. | ||||
|          */ | ||||
| #ifndef CONFIG_SOFTMMU | ||||
|         tcg_debug_assert(!have_mmap_lock()); | ||||
| #endif | ||||
|         if (qemu_mutex_iothread_locked()) { | ||||
|             qemu_mutex_unlock_iothread(); | ||||
|         } | ||||
|         assert_no_pages_locked(); | ||||
|         tb_lock_reset(); | ||||
|     } | ||||
|  | ||||
|     if (in_exclusive_region) { | ||||
| @@ -292,7 +293,7 @@ struct tb_desc { | ||||
|     uint32_t trace_vcpu_dstate; | ||||
| }; | ||||
|  | ||||
| static bool tb_lookup_cmp(const void *p, const void *d) | ||||
| static bool tb_cmp(const void *p, const void *d) | ||||
| { | ||||
|     const TranslationBlock *tb = p; | ||||
|     const struct tb_desc *desc = d; | ||||
| @@ -335,12 +336,9 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, | ||||
|     desc.trace_vcpu_dstate = *cpu->trace_dstate; | ||||
|     desc.pc = pc; | ||||
|     phys_pc = get_page_addr_code(desc.env, pc); | ||||
|     if (phys_pc == -1) { | ||||
|         return NULL; | ||||
|     } | ||||
|     desc.phys_page1 = phys_pc & TARGET_PAGE_MASK; | ||||
|     h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate); | ||||
|     return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp); | ||||
|     return qht_lookup(&tb_ctx.htable, tb_cmp, &desc, h); | ||||
| } | ||||
|  | ||||
| void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) | ||||
| @@ -354,43 +352,28 @@ void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Called with tb_lock held.  */ | ||||
| static inline void tb_add_jump(TranslationBlock *tb, int n, | ||||
|                                TranslationBlock *tb_next) | ||||
| { | ||||
|     uintptr_t old; | ||||
|  | ||||
|     assert(n < ARRAY_SIZE(tb->jmp_list_next)); | ||||
|     qemu_spin_lock(&tb_next->jmp_lock); | ||||
|  | ||||
|     /* make sure the destination TB is valid */ | ||||
|     if (tb_next->cflags & CF_INVALID) { | ||||
|         goto out_unlock_next; | ||||
|     if (tb->jmp_list_next[n]) { | ||||
|         /* Another thread has already done this while we were | ||||
|          * outside of the lock; nothing to do in this case */ | ||||
|         return; | ||||
|     } | ||||
|     /* Atomically claim the jump destination slot only if it was NULL */ | ||||
|     old = atomic_cmpxchg(&tb->jmp_dest[n], (uintptr_t)NULL, (uintptr_t)tb_next); | ||||
|     if (old) { | ||||
|         goto out_unlock_next; | ||||
|     } | ||||
|  | ||||
|     /* patch the native jump address */ | ||||
|     tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr); | ||||
|  | ||||
|     /* add in TB jmp list */ | ||||
|     tb->jmp_list_next[n] = tb_next->jmp_list_head; | ||||
|     tb_next->jmp_list_head = (uintptr_t)tb | n; | ||||
|  | ||||
|     qemu_spin_unlock(&tb_next->jmp_lock); | ||||
|  | ||||
|     qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc, | ||||
|                            "Linking TBs %p [" TARGET_FMT_lx | ||||
|                            "] index %d -> %p [" TARGET_FMT_lx "]\n", | ||||
|                            tb->tc.ptr, tb->pc, n, | ||||
|                            tb_next->tc.ptr, tb_next->pc); | ||||
|     return; | ||||
|  | ||||
|  out_unlock_next: | ||||
|     qemu_spin_unlock(&tb_next->jmp_lock); | ||||
|     return; | ||||
|     /* patch the native jump address */ | ||||
|     tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr); | ||||
|  | ||||
|     /* add in TB jmp circular list */ | ||||
|     tb->jmp_list_next[n] = tb_next->jmp_list_first; | ||||
|     tb_next->jmp_list_first = (uintptr_t)tb | n; | ||||
| } | ||||
|  | ||||
| static inline TranslationBlock *tb_find(CPUState *cpu, | ||||
| @@ -400,11 +383,27 @@ static inline TranslationBlock *tb_find(CPUState *cpu, | ||||
|     TranslationBlock *tb; | ||||
|     target_ulong cs_base, pc; | ||||
|     uint32_t flags; | ||||
|     bool acquired_tb_lock = false; | ||||
|  | ||||
|     tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); | ||||
|     if (tb == NULL) { | ||||
|         /* mmap_lock is needed by tb_gen_code, and mmap_lock must be | ||||
|          * taken outside tb_lock. As system emulation is currently | ||||
|          * single threaded the locks are NOPs. | ||||
|          */ | ||||
|         mmap_lock(); | ||||
|         tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask); | ||||
|         tb_lock(); | ||||
|         acquired_tb_lock = true; | ||||
|  | ||||
|         /* There's a chance that our desired tb has been translated while | ||||
|          * taking the locks so we check again inside the lock. | ||||
|          */ | ||||
|         tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask); | ||||
|         if (likely(tb == NULL)) { | ||||
|             /* if no translated code available, then translate it now */ | ||||
|             tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask); | ||||
|         } | ||||
|  | ||||
|         mmap_unlock(); | ||||
|         /* We add the TB in the virtual pc hash table for the fast lookup */ | ||||
|         atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb); | ||||
| @@ -419,8 +418,17 @@ static inline TranslationBlock *tb_find(CPUState *cpu, | ||||
|     } | ||||
| #endif | ||||
|     /* See if we can patch the calling TB. */ | ||||
|     if (last_tb) { | ||||
|         tb_add_jump(last_tb, tb_exit, tb); | ||||
|     if (last_tb && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) { | ||||
|         if (!acquired_tb_lock) { | ||||
|             tb_lock(); | ||||
|             acquired_tb_lock = true; | ||||
|         } | ||||
|         if (!(tb->cflags & CF_INVALID)) { | ||||
|             tb_add_jump(last_tb, tb_exit, tb); | ||||
|         } | ||||
|     } | ||||
|     if (acquired_tb_lock) { | ||||
|         tb_unlock(); | ||||
|     } | ||||
|     return tb; | ||||
| } | ||||
| @@ -577,7 +585,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, | ||||
|         else { | ||||
|             if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { | ||||
|                 replay_interrupt(); | ||||
|                 cpu->exception_index = -1; | ||||
|                 *last_tb = NULL; | ||||
|             } | ||||
|             /* The target hook may have updated the 'cpu->interrupt_request'; | ||||
| @@ -599,9 +606,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, | ||||
|     if (unlikely(atomic_read(&cpu->exit_request) | ||||
|         || (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) { | ||||
|         atomic_set(&cpu->exit_request, 0); | ||||
|         if (cpu->exception_index == -1) { | ||||
|             cpu->exception_index = EXCP_INTERRUPT; | ||||
|         } | ||||
|         cpu->exception_index = EXCP_INTERRUPT; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
| @@ -696,13 +701,11 @@ int cpu_exec(CPUState *cpu) | ||||
|         g_assert(cpu == current_cpu); | ||||
|         g_assert(cc == CPU_GET_CLASS(cpu)); | ||||
| #endif /* buggy compiler */ | ||||
| #ifndef CONFIG_SOFTMMU | ||||
|         tcg_debug_assert(!have_mmap_lock()); | ||||
| #endif | ||||
|         cpu->can_do_io = 1; | ||||
|         tb_lock_reset(); | ||||
|         if (qemu_mutex_iothread_locked()) { | ||||
|             qemu_mutex_unlock_iothread(); | ||||
|         } | ||||
|         assert_no_pages_locked(); | ||||
|     } | ||||
|  | ||||
|     /* 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 | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| @@ -98,23 +98,19 @@ | ||||
| static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env, | ||||
|                                               size_t mmu_idx, size_t index, | ||||
|                                               target_ulong addr, | ||||
|                                               uintptr_t retaddr, | ||||
|                                               bool recheck, | ||||
|                                               MMUAccessType access_type) | ||||
|                                               uintptr_t retaddr) | ||||
| { | ||||
|     CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; | ||||
|     return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, recheck, | ||||
|                     access_type, DATA_SIZE); | ||||
|     return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, DATA_SIZE); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, | ||||
|                             TCGMemOpIdx oi, uintptr_t retaddr) | ||||
| { | ||||
|     uintptr_t mmu_idx = get_mmuidx(oi); | ||||
|     uintptr_t index = tlb_index(env, mmu_idx, addr); | ||||
|     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); | ||||
|     target_ulong tlb_addr = entry->ADDR_READ; | ||||
|     unsigned mmu_idx = get_mmuidx(oi); | ||||
|     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; | ||||
|     unsigned a_bits = get_alignment_bits(get_memop(oi)); | ||||
|     uintptr_t haddr; | ||||
|     DATA_TYPE res; | ||||
| @@ -125,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 (!tlb_hit(tlb_addr, addr)) { | ||||
|     if ((addr & TARGET_PAGE_MASK) | ||||
|          != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { | ||||
|         if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { | ||||
|             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, | ||||
|                      mmu_idx, retaddr); | ||||
|             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.  */ | ||||
| @@ -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 | ||||
|            byte ordering.  We should push the LE/BE request down into io.  */ | ||||
|         res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr, | ||||
|                                     tlb_addr & TLB_RECHECK, | ||||
|                                     READ_ACCESS_TYPE); | ||||
|         res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr); | ||||
|         res = TGT_LE(res); | ||||
|         return res; | ||||
|     } | ||||
| @@ -169,7 +162,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, | ||||
|         return res; | ||||
|     } | ||||
|  | ||||
|     haddr = addr + entry->addend; | ||||
|     haddr = addr + env->tlb_table[mmu_idx][index].addend; | ||||
| #if DATA_SIZE == 1 | ||||
|     res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr); | ||||
| #else | ||||
| @@ -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, | ||||
|                             TCGMemOpIdx oi, uintptr_t retaddr) | ||||
| { | ||||
|     uintptr_t mmu_idx = get_mmuidx(oi); | ||||
|     uintptr_t index = tlb_index(env, mmu_idx, addr); | ||||
|     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); | ||||
|     target_ulong tlb_addr = entry->ADDR_READ; | ||||
|     unsigned mmu_idx = get_mmuidx(oi); | ||||
|     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; | ||||
|     unsigned a_bits = get_alignment_bits(get_memop(oi)); | ||||
|     uintptr_t haddr; | ||||
|     DATA_TYPE res; | ||||
| @@ -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 (!tlb_hit(tlb_addr, addr)) { | ||||
|     if ((addr & TARGET_PAGE_MASK) | ||||
|          != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { | ||||
|         if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { | ||||
|             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, | ||||
|                      mmu_idx, retaddr); | ||||
|             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.  */ | ||||
| @@ -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 | ||||
|            byte ordering.  We should push the LE/BE request down into io.  */ | ||||
|         res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr, | ||||
|                                     tlb_addr & TLB_RECHECK, | ||||
|                                     READ_ACCESS_TYPE); | ||||
|         res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr); | ||||
|         res = TGT_BE(res); | ||||
|         return res; | ||||
|     } | ||||
| @@ -240,7 +229,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, | ||||
|         return res; | ||||
|     } | ||||
|  | ||||
|     haddr = addr + entry->addend; | ||||
|     haddr = addr + env->tlb_table[mmu_idx][index].addend; | ||||
|     res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr); | ||||
|     return res; | ||||
| } | ||||
| @@ -270,21 +259,18 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env, | ||||
|                                           size_t mmu_idx, size_t index, | ||||
|                                           DATA_TYPE val, | ||||
|                                           target_ulong addr, | ||||
|                                           uintptr_t retaddr, | ||||
|                                           bool recheck) | ||||
|                                           uintptr_t retaddr) | ||||
| { | ||||
|     CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; | ||||
|     return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr, | ||||
|                      recheck, DATA_SIZE); | ||||
|     return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr, DATA_SIZE); | ||||
| } | ||||
|  | ||||
| void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||
|                        TCGMemOpIdx oi, uintptr_t retaddr) | ||||
| { | ||||
|     uintptr_t mmu_idx = get_mmuidx(oi); | ||||
|     uintptr_t index = tlb_index(env, mmu_idx, addr); | ||||
|     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); | ||||
|     target_ulong tlb_addr = tlb_addr_write(entry); | ||||
|     unsigned mmu_idx = get_mmuidx(oi); | ||||
|     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; | ||||
|     unsigned a_bits = get_alignment_bits(get_memop(oi)); | ||||
|     uintptr_t haddr; | ||||
|  | ||||
| @@ -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 (!tlb_hit(tlb_addr, addr)) { | ||||
|     if ((addr & TARGET_PAGE_MASK) | ||||
|         != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { | ||||
|         if (!VICTIM_TLB_HIT(addr_write, addr)) { | ||||
|             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, | ||||
|                      mmu_idx, retaddr); | ||||
|             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.  */ | ||||
| @@ -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 | ||||
|            byte ordering.  We should push the LE/BE request down into io.  */ | ||||
|         val = TGT_LE(val); | ||||
|         glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, | ||||
|                                retaddr, tlb_addr & TLB_RECHECK); | ||||
|         glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -322,16 +306,16 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||
|     if (DATA_SIZE > 1 | ||||
|         && unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1 | ||||
|                      >= TARGET_PAGE_SIZE)) { | ||||
|         int i; | ||||
|         target_ulong page2; | ||||
|         CPUTLBEntry *entry2; | ||||
|         int i, index2; | ||||
|         target_ulong page2, tlb_addr2; | ||||
|     do_unaligned_access: | ||||
|         /* Ensure the second page is in the TLB.  Note that the first page | ||||
|            is already guaranteed to be filled, and that the second page | ||||
|            cannot evict the first.  */ | ||||
|         page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; | ||||
|         entry2 = tlb_entry(env, mmu_idx, page2); | ||||
|         if (!tlb_hit_page(tlb_addr_write(entry2), page2) | ||||
|         index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|         tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; | ||||
|         if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK)) | ||||
|             && !VICTIM_TLB_HIT(addr_write, page2)) { | ||||
|             tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE, | ||||
|                      mmu_idx, retaddr); | ||||
| @@ -349,7 +333,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     haddr = addr + entry->addend; | ||||
|     haddr = addr + env->tlb_table[mmu_idx][index].addend; | ||||
| #if DATA_SIZE == 1 | ||||
|     glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val); | ||||
| #else | ||||
| @@ -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, | ||||
|                        TCGMemOpIdx oi, uintptr_t retaddr) | ||||
| { | ||||
|     uintptr_t mmu_idx = get_mmuidx(oi); | ||||
|     uintptr_t index = tlb_index(env, mmu_idx, addr); | ||||
|     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); | ||||
|     target_ulong tlb_addr = tlb_addr_write(entry); | ||||
|     unsigned mmu_idx = get_mmuidx(oi); | ||||
|     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; | ||||
|     unsigned a_bits = get_alignment_bits(get_memop(oi)); | ||||
|     uintptr_t haddr; | ||||
|  | ||||
| @@ -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 (!tlb_hit(tlb_addr, addr)) { | ||||
|     if ((addr & TARGET_PAGE_MASK) | ||||
|         != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { | ||||
|         if (!VICTIM_TLB_HIT(addr_write, addr)) { | ||||
|             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, | ||||
|                      mmu_idx, retaddr); | ||||
|             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.  */ | ||||
| @@ -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 | ||||
|            byte ordering.  We should push the LE/BE request down into io.  */ | ||||
|         val = TGT_BE(val); | ||||
|         glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr, | ||||
|                                tlb_addr & TLB_RECHECK); | ||||
|         glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -402,16 +383,16 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||
|     if (DATA_SIZE > 1 | ||||
|         && unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1 | ||||
|                      >= TARGET_PAGE_SIZE)) { | ||||
|         int i; | ||||
|         target_ulong page2; | ||||
|         CPUTLBEntry *entry2; | ||||
|         int i, index2; | ||||
|         target_ulong page2, tlb_addr2; | ||||
|     do_unaligned_access: | ||||
|         /* Ensure the second page is in the TLB.  Note that the first page | ||||
|            is already guaranteed to be filled, and that the second page | ||||
|            cannot evict the first.  */ | ||||
|         page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; | ||||
|         entry2 = tlb_entry(env, mmu_idx, page2); | ||||
|         if (!tlb_hit_page(tlb_addr_write(entry2), page2) | ||||
|         index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||
|         tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; | ||||
|         if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK)) | ||||
|             && !VICTIM_TLB_HIT(addr_write, page2)) { | ||||
|             tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE, | ||||
|                      mmu_idx, retaddr); | ||||
| @@ -429,7 +410,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     haddr = addr + entry->addend; | ||||
|     haddr = addr + env->tlb_table[mmu_idx][index].addend; | ||||
|     glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val); | ||||
| } | ||||
| #endif /* DATA_SIZE > 1 */ | ||||
|   | ||||
| @@ -51,7 +51,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask) | ||||
|     if (!qemu_cpu_is_self(cpu)) { | ||||
|         qemu_cpu_kick(cpu); | ||||
|     } else { | ||||
|         atomic_set(&cpu->icount_decr.u16.high, -1); | ||||
|         cpu->icount_decr.u16.high = -1; | ||||
|         if (use_icount && | ||||
|             !cpu->can_do_io | ||||
|             && (mask & ~old_mask) != 0) { | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| @@ -512,39 +512,6 @@ void HELPER(gvec_orc)(void *d, void *a, void *b, uint32_t desc) | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_nand)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { | ||||
|         *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) & *(vec64 *)(b + i)); | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_nor)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { | ||||
|         *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) | *(vec64 *)(b + i)); | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_eqv)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { | ||||
|         *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) ^ *(vec64 *)(b + i)); | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
| @@ -738,7 +705,7 @@ void HELPER(NAME)(void *d, void *a, void *b, uint32_t desc)                \ | ||||
| {                                                                          \ | ||||
|     intptr_t oprsz = simd_oprsz(desc);                                     \ | ||||
|     intptr_t i;                                                            \ | ||||
|     for (i = 0; i < oprsz; i += sizeof(TYPE)) {                            \ | ||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) {                           \ | ||||
|         *(TYPE *)(d + i) = DO_CMP0(*(TYPE *)(a + i) OP *(TYPE *)(b + i));  \ | ||||
|     }                                                                      \ | ||||
|     clear_high(d, oprsz, desc);                                            \ | ||||
| @@ -1028,227 +995,3 @@ void HELPER(gvec_ussub64)(void *d, void *a, void *b, uint32_t desc) | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_smin8)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(int8_t)) { | ||||
|         int8_t aa = *(int8_t *)(a + i); | ||||
|         int8_t bb = *(int8_t *)(b + i); | ||||
|         int8_t dd = aa < bb ? aa : bb; | ||||
|         *(int8_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_smin16)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(int16_t)) { | ||||
|         int16_t aa = *(int16_t *)(a + i); | ||||
|         int16_t bb = *(int16_t *)(b + i); | ||||
|         int16_t dd = aa < bb ? aa : bb; | ||||
|         *(int16_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_smin32)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(int32_t)) { | ||||
|         int32_t aa = *(int32_t *)(a + i); | ||||
|         int32_t bb = *(int32_t *)(b + i); | ||||
|         int32_t dd = aa < bb ? aa : bb; | ||||
|         *(int32_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_smin64)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(int64_t)) { | ||||
|         int64_t aa = *(int64_t *)(a + i); | ||||
|         int64_t bb = *(int64_t *)(b + i); | ||||
|         int64_t dd = aa < bb ? aa : bb; | ||||
|         *(int64_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_smax8)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(int8_t)) { | ||||
|         int8_t aa = *(int8_t *)(a + i); | ||||
|         int8_t bb = *(int8_t *)(b + i); | ||||
|         int8_t dd = aa > bb ? aa : bb; | ||||
|         *(int8_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_smax16)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(int16_t)) { | ||||
|         int16_t aa = *(int16_t *)(a + i); | ||||
|         int16_t bb = *(int16_t *)(b + i); | ||||
|         int16_t dd = aa > bb ? aa : bb; | ||||
|         *(int16_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_smax32)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(int32_t)) { | ||||
|         int32_t aa = *(int32_t *)(a + i); | ||||
|         int32_t bb = *(int32_t *)(b + i); | ||||
|         int32_t dd = aa > bb ? aa : bb; | ||||
|         *(int32_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_smax64)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(int64_t)) { | ||||
|         int64_t aa = *(int64_t *)(a + i); | ||||
|         int64_t bb = *(int64_t *)(b + i); | ||||
|         int64_t dd = aa > bb ? aa : bb; | ||||
|         *(int64_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_umin8)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(uint8_t)) { | ||||
|         uint8_t aa = *(uint8_t *)(a + i); | ||||
|         uint8_t bb = *(uint8_t *)(b + i); | ||||
|         uint8_t dd = aa < bb ? aa : bb; | ||||
|         *(uint8_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_umin16)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(uint16_t)) { | ||||
|         uint16_t aa = *(uint16_t *)(a + i); | ||||
|         uint16_t bb = *(uint16_t *)(b + i); | ||||
|         uint16_t dd = aa < bb ? aa : bb; | ||||
|         *(uint16_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_umin32)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(uint32_t)) { | ||||
|         uint32_t aa = *(uint32_t *)(a + i); | ||||
|         uint32_t bb = *(uint32_t *)(b + i); | ||||
|         uint32_t dd = aa < bb ? aa : bb; | ||||
|         *(uint32_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_umin64)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(uint64_t)) { | ||||
|         uint64_t aa = *(uint64_t *)(a + i); | ||||
|         uint64_t bb = *(uint64_t *)(b + i); | ||||
|         uint64_t dd = aa < bb ? aa : bb; | ||||
|         *(uint64_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_umax8)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(uint8_t)) { | ||||
|         uint8_t aa = *(uint8_t *)(a + i); | ||||
|         uint8_t bb = *(uint8_t *)(b + i); | ||||
|         uint8_t dd = aa > bb ? aa : bb; | ||||
|         *(uint8_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_umax16)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(uint16_t)) { | ||||
|         uint16_t aa = *(uint16_t *)(a + i); | ||||
|         uint16_t bb = *(uint16_t *)(b + i); | ||||
|         uint16_t dd = aa > bb ? aa : bb; | ||||
|         *(uint16_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_umax32)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(uint32_t)) { | ||||
|         uint32_t aa = *(uint32_t *)(a + i); | ||||
|         uint32_t bb = *(uint32_t *)(b + i); | ||||
|         uint32_t dd = aa > bb ? aa : bb; | ||||
|         *(uint32_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|  | ||||
| void HELPER(gvec_umax64)(void *d, void *a, void *b, uint32_t desc) | ||||
| { | ||||
|     intptr_t oprsz = simd_oprsz(desc); | ||||
|     intptr_t i; | ||||
|  | ||||
|     for (i = 0; i < oprsz; i += sizeof(uint64_t)) { | ||||
|         uint64_t aa = *(uint64_t *)(a + i); | ||||
|         uint64_t bb = *(uint64_t *)(b + i); | ||||
|         uint64_t dd = aa > bb ? aa : bb; | ||||
|         *(uint64_t *)(d + i) = dd; | ||||
|     } | ||||
|     clear_high(d, oprsz, desc); | ||||
| } | ||||
|   | ||||
| @@ -125,19 +125,11 @@ GEN_ATOMIC_HELPERS(fetch_add) | ||||
| GEN_ATOMIC_HELPERS(fetch_and) | ||||
| GEN_ATOMIC_HELPERS(fetch_or) | ||||
| GEN_ATOMIC_HELPERS(fetch_xor) | ||||
| GEN_ATOMIC_HELPERS(fetch_smin) | ||||
| GEN_ATOMIC_HELPERS(fetch_umin) | ||||
| GEN_ATOMIC_HELPERS(fetch_smax) | ||||
| GEN_ATOMIC_HELPERS(fetch_umax) | ||||
|  | ||||
| GEN_ATOMIC_HELPERS(add_fetch) | ||||
| GEN_ATOMIC_HELPERS(and_fetch) | ||||
| GEN_ATOMIC_HELPERS(or_fetch) | ||||
| GEN_ATOMIC_HELPERS(xor_fetch) | ||||
| GEN_ATOMIC_HELPERS(smin_fetch) | ||||
| GEN_ATOMIC_HELPERS(umin_fetch) | ||||
| GEN_ATOMIC_HELPERS(smax_fetch) | ||||
| GEN_ATOMIC_HELPERS(umax_fetch) | ||||
|  | ||||
| GEN_ATOMIC_HELPERS(xchg) | ||||
|  | ||||
| @@ -200,26 +192,6 @@ DEF_HELPER_FLAGS_4(gvec_ussub16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_ussub32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_ussub64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
|  | ||||
| DEF_HELPER_FLAGS_4(gvec_smin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_smin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_smin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_smin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
|  | ||||
| DEF_HELPER_FLAGS_4(gvec_smax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_smax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_smax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_smax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
|  | ||||
| DEF_HELPER_FLAGS_4(gvec_umin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_umin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_umin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_umin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
|  | ||||
| DEF_HELPER_FLAGS_4(gvec_umax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_umax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_umax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_umax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
|  | ||||
| DEF_HELPER_FLAGS_3(gvec_neg8, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_3(gvec_neg16, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_3(gvec_neg32, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | ||||
| @@ -231,9 +203,6 @@ DEF_HELPER_FLAGS_4(gvec_or, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_xor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_andc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_orc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_nand, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_nor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_eqv, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||
|  | ||||
| DEF_HELPER_FLAGS_4(gvec_ands, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) | ||||
| DEF_HELPER_FLAGS_4(gvec_xors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -6,7 +6,7 @@ | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| @@ -23,13 +23,10 @@ | ||||
|  | ||||
|  | ||||
| /* translate-all.c */ | ||||
| struct page_collection *page_collection_lock(tb_page_addr_t start, | ||||
|                                              tb_page_addr_t end); | ||||
| void page_collection_unlock(struct page_collection *set); | ||||
| void tb_invalidate_phys_page_fast(struct page_collection *pages, | ||||
|                                   tb_page_addr_t start, int len); | ||||
| void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len); | ||||
| void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, | ||||
|                                    int is_cpu_write_access); | ||||
| void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end); | ||||
| void tb_check_watchpoint(CPUState *cpu); | ||||
|  | ||||
| #ifdef CONFIG_USER_ONLY | ||||
|   | ||||
| @@ -34,7 +34,7 @@ void translator_loop_temp_check(DisasContextBase *db) | ||||
| void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | ||||
|                      CPUState *cpu, TranslationBlock *tb) | ||||
| { | ||||
|     int bp_insn = 0; | ||||
|     int max_insns; | ||||
|  | ||||
|     /* Initialize DisasContext */ | ||||
|     db->tb = tb; | ||||
| @@ -45,18 +45,18 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | ||||
|     db->singlestep_enabled = cpu->singlestep_enabled; | ||||
|  | ||||
|     /* Instruction counting */ | ||||
|     db->max_insns = tb_cflags(db->tb) & CF_COUNT_MASK; | ||||
|     if (db->max_insns == 0) { | ||||
|         db->max_insns = CF_COUNT_MASK; | ||||
|     max_insns = tb_cflags(db->tb) & CF_COUNT_MASK; | ||||
|     if (max_insns == 0) { | ||||
|         max_insns = CF_COUNT_MASK; | ||||
|     } | ||||
|     if (db->max_insns > TCG_MAX_INSNS) { | ||||
|         db->max_insns = TCG_MAX_INSNS; | ||||
|     if (max_insns > TCG_MAX_INSNS) { | ||||
|         max_insns = TCG_MAX_INSNS; | ||||
|     } | ||||
|     if (db->singlestep_enabled || singlestep) { | ||||
|         db->max_insns = 1; | ||||
|         max_insns = 1; | ||||
|     } | ||||
|  | ||||
|     ops->init_disas_context(db, cpu); | ||||
|     max_insns = ops->init_disas_context(db, cpu, max_insns); | ||||
|     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */ | ||||
|  | ||||
|     /* Reset the temp count so that we can identify leaks */ | ||||
| @@ -73,13 +73,11 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | ||||
|         tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */ | ||||
|  | ||||
|         /* Pass breakpoint hits to target for further processing */ | ||||
|         if (!db->singlestep_enabled | ||||
|             && unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) { | ||||
|         if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) { | ||||
|             CPUBreakpoint *bp; | ||||
|             QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { | ||||
|                 if (bp->pc == db->pc_next) { | ||||
|                     if (ops->breakpoint_check(db, cpu, bp)) { | ||||
|                         bp_insn = 1; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
| @@ -97,8 +95,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | ||||
|            update db->pc_next and db->is_jmp to indicate what should be | ||||
|            done next -- either exiting this loop or locate the start of | ||||
|            the next instruction.  */ | ||||
|         if (db->num_insns == db->max_insns | ||||
|             && (tb_cflags(db->tb) & CF_LAST_IO)) { | ||||
|         if (db->num_insns == max_insns && (tb_cflags(db->tb) & CF_LAST_IO)) { | ||||
|             /* Accept I/O on the last instruction.  */ | ||||
|             gen_io_start(); | ||||
|             ops->translate_insn(db, cpu); | ||||
| @@ -114,7 +111,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | ||||
|  | ||||
|         /* Stop translation if the output buffer is full, | ||||
|            or we have executed all of the allowed instructions.  */ | ||||
|         if (tcg_op_buf_full() || db->num_insns >= db->max_insns) { | ||||
|         if (tcg_op_buf_full() || db->num_insns >= max_insns) { | ||||
|             db->is_jmp = DISAS_TOO_MANY; | ||||
|             break; | ||||
|         } | ||||
| @@ -122,7 +119,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | ||||
|  | ||||
|     /* Emit code to exit the TB, as indicated by db->is_jmp.  */ | ||||
|     ops->tb_stop(db, cpu); | ||||
|     gen_tb_end(db->tb, db->num_insns - bp_insn); | ||||
|     gen_tb_end(db->tb, db->num_insns); | ||||
|  | ||||
|     /* The disas_log hook may use these values rather than recompute.  */ | ||||
|     db->tb->size = db->pc_next - db->pc_first; | ||||
|   | ||||
| @@ -2,9 +2,6 @@ | ||||
| #include "qemu-common.h" | ||||
| #include "qom/cpu.h" | ||||
| #include "sysemu/replay.h" | ||||
| #include "sysemu/sysemu.h" | ||||
|  | ||||
| bool enable_cpu_pm = false; | ||||
|  | ||||
| void cpu_resume(CPUState *cpu) | ||||
| { | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| @@ -25,7 +25,6 @@ | ||||
| #include "exec/cpu_ldst.h" | ||||
| #include "translate-all.h" | ||||
| #include "exec/helper-proto.h" | ||||
| #include "qemu/atomic128.h" | ||||
|  | ||||
| #undef EAX | ||||
| #undef ECX | ||||
| @@ -169,7 +168,7 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, | ||||
|     } | ||||
|  | ||||
|     /* Now we have a real cpu fault.  */ | ||||
|     cpu_restore_state(cpu, pc, true); | ||||
|     cpu_restore_state(cpu, pc); | ||||
|  | ||||
|     sigprocmask(SIG_SETMASK, old_set, NULL); | ||||
|     cpu_loop_exit(cpu); | ||||
| @@ -479,66 +478,28 @@ int cpu_signal_handler(int host_signum, void *pinfo, | ||||
|  | ||||
| #elif defined(__aarch64__) | ||||
|  | ||||
| #ifndef ESR_MAGIC | ||||
| /* Pre-3.16 kernel headers don't have these, so provide fallback definitions */ | ||||
| #define ESR_MAGIC 0x45535201 | ||||
| struct esr_context { | ||||
|     struct _aarch64_ctx head; | ||||
|     uint64_t esr; | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc) | ||||
| { | ||||
|     return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved; | ||||
| } | ||||
|  | ||||
| static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr) | ||||
| { | ||||
|     return (struct _aarch64_ctx *)((char *)hdr + hdr->size); | ||||
| } | ||||
|  | ||||
| int cpu_signal_handler(int host_signum, void *pinfo, void *puc) | ||||
| { | ||||
|     siginfo_t *info = pinfo; | ||||
|     ucontext_t *uc = puc; | ||||
|     uintptr_t pc = uc->uc_mcontext.pc; | ||||
|     uint32_t insn = *(uint32_t *)pc; | ||||
|     bool is_write; | ||||
|     struct _aarch64_ctx *hdr; | ||||
|     struct esr_context const *esrctx = NULL; | ||||
|  | ||||
|     /* Find the esr_context, which has the WnR bit in it */ | ||||
|     for (hdr = first_ctx(uc); hdr->magic; hdr = next_ctx(hdr)) { | ||||
|         if (hdr->magic == ESR_MAGIC) { | ||||
|             esrctx = (struct esr_context const *)hdr; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     /* XXX: need kernel patch to get write flag faster.  */ | ||||
|     is_write = (   (insn & 0xbfff0000) == 0x0c000000   /* C3.3.1 */ | ||||
|                 || (insn & 0xbfe00000) == 0x0c800000   /* C3.3.2 */ | ||||
|                 || (insn & 0xbfdf0000) == 0x0d000000   /* C3.3.3 */ | ||||
|                 || (insn & 0xbfc00000) == 0x0d800000   /* C3.3.4 */ | ||||
|                 || (insn & 0x3f400000) == 0x08000000   /* C3.3.6 */ | ||||
|                 || (insn & 0x3bc00000) == 0x39000000   /* C3.3.13 */ | ||||
|                 || (insn & 0x3fc00000) == 0x3d800000   /* ... 128bit */ | ||||
|                 /* Ingore bits 10, 11 & 21, controlling indexing.  */ | ||||
|                 || (insn & 0x3bc00000) == 0x38000000   /* C3.3.8-12 */ | ||||
|                 || (insn & 0x3fe00000) == 0x3c800000   /* ... 128bit */ | ||||
|                 /* Ignore bits 23 & 24, controlling indexing.  */ | ||||
|                 || (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */ | ||||
|  | ||||
|     if (esrctx) { | ||||
|         /* For data aborts ESR.EC is 0b10010x: then bit 6 is the WnR bit */ | ||||
|         uint64_t esr = esrctx->esr; | ||||
|         is_write = extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1; | ||||
|     } else { | ||||
|         /* | ||||
|          * Fall back to parsing instructions; will only be needed | ||||
|          * for really ancient (pre-3.16) kernels. | ||||
|          */ | ||||
|         uint32_t insn = *(uint32_t *)pc; | ||||
|  | ||||
|         is_write = ((insn & 0xbfff0000) == 0x0c000000   /* C3.3.1 */ | ||||
|                     || (insn & 0xbfe00000) == 0x0c800000   /* C3.3.2 */ | ||||
|                     || (insn & 0xbfdf0000) == 0x0d000000   /* C3.3.3 */ | ||||
|                     || (insn & 0xbfc00000) == 0x0d800000   /* C3.3.4 */ | ||||
|                     || (insn & 0x3f400000) == 0x08000000   /* C3.3.6 */ | ||||
|                     || (insn & 0x3bc00000) == 0x39000000   /* C3.3.13 */ | ||||
|                     || (insn & 0x3fc00000) == 0x3d800000   /* ... 128bit */ | ||||
|                     /* Ignore bits 10, 11 & 21, controlling indexing.  */ | ||||
|                     || (insn & 0x3bc00000) == 0x38000000   /* C3.3.8-12 */ | ||||
|                     || (insn & 0x3fe00000) == 0x3c800000   /* ... 128bit */ | ||||
|                     /* Ignore bits 23 & 24, controlling indexing.  */ | ||||
|                     || (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */ | ||||
|     } | ||||
|     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); | ||||
| } | ||||
|  | ||||
| @@ -609,81 +570,6 @@ int cpu_signal_handler(int host_signum, void *pinfo, | ||||
|     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); | ||||
| } | ||||
|  | ||||
| #elif defined(__riscv) | ||||
|  | ||||
| int cpu_signal_handler(int host_signum, void *pinfo, | ||||
|                        void *puc) | ||||
| { | ||||
|     siginfo_t *info = pinfo; | ||||
|     ucontext_t *uc = puc; | ||||
|     greg_t pc = uc->uc_mcontext.__gregs[REG_PC]; | ||||
|     uint32_t insn = *(uint32_t *)pc; | ||||
|     int is_write = 0; | ||||
|  | ||||
|     /* Detect store by reading the instruction at the program | ||||
|        counter. Note: we currently only generate 32-bit | ||||
|        instructions so we thus only detect 32-bit stores */ | ||||
|     switch (((insn >> 0) & 0b11)) { | ||||
|     case 3: | ||||
|         switch (((insn >> 2) & 0b11111)) { | ||||
|         case 8: | ||||
|             switch (((insn >> 12) & 0b111)) { | ||||
|             case 0: /* sb */ | ||||
|             case 1: /* sh */ | ||||
|             case 2: /* sw */ | ||||
|             case 3: /* sd */ | ||||
|             case 4: /* sq */ | ||||
|                 is_write = 1; | ||||
|                 break; | ||||
|             default: | ||||
|                 break; | ||||
|             } | ||||
|             break; | ||||
|         case 9: | ||||
|             switch (((insn >> 12) & 0b111)) { | ||||
|             case 2: /* fsw */ | ||||
|             case 3: /* fsd */ | ||||
|             case 4: /* fsq */ | ||||
|                 is_write = 1; | ||||
|                 break; | ||||
|             default: | ||||
|                 break; | ||||
|             } | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* Check for compressed instructions */ | ||||
|     switch (((insn >> 13) & 0b111)) { | ||||
|     case 7: | ||||
|         switch (insn & 0b11) { | ||||
|         case 0: /*c.sd */ | ||||
|         case 2: /* c.sdsp */ | ||||
|             is_write = 1; | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|         break; | ||||
|     case 6: | ||||
|         switch (insn & 0b11) { | ||||
|         case 0: /* c.sw */ | ||||
|         case 3: /* c.swsp */ | ||||
|             is_write = 1; | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|         break; | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); | ||||
| } | ||||
|  | ||||
| #else | ||||
|  | ||||
| #error host CPU specific signal handler needed | ||||
| @@ -729,7 +615,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, | ||||
| /* The following is only callable from other helpers, and matches up | ||||
|    with the softmmu version.  */ | ||||
|  | ||||
| #if HAVE_ATOMIC128 || HAVE_CMPXCHG128 | ||||
| #ifdef CONFIG_ATOMIC128 | ||||
|  | ||||
| #undef EXTRA_ARGS | ||||
| #undef ATOMIC_NAME | ||||
| @@ -742,4 +628,4 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, | ||||
|  | ||||
| #define DATA_SIZE 16 | ||||
| #include "atomic_template.h" | ||||
| #endif | ||||
| #endif /* CONFIG_ATOMIC128 */ | ||||
|   | ||||
							
								
								
									
										20
									
								
								arch_init.c
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								arch_init.c
									
									
									
									
									
								
							| @@ -29,7 +29,6 @@ | ||||
| #include "hw/pci/pci.h" | ||||
| #include "hw/audio/soundhw.h" | ||||
| #include "qapi/qapi-commands-misc.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qemu/config-file.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "hw/acpi/acpi.h" | ||||
| @@ -52,14 +51,14 @@ int graphic_depth = 32; | ||||
| #define QEMU_ARCH QEMU_ARCH_ARM | ||||
| #elif defined(TARGET_CRIS) | ||||
| #define QEMU_ARCH QEMU_ARCH_CRIS | ||||
| #elif defined(TARGET_HPPA) | ||||
| #define QEMU_ARCH QEMU_ARCH_HPPA | ||||
| #elif defined(TARGET_I386) | ||||
| #define QEMU_ARCH QEMU_ARCH_I386 | ||||
| #elif defined(TARGET_LM32) | ||||
| #define QEMU_ARCH QEMU_ARCH_LM32 | ||||
| #elif defined(TARGET_HPPA) | ||||
| #define QEMU_ARCH QEMU_ARCH_HPPA | ||||
| #elif defined(TARGET_M68K) | ||||
| #define QEMU_ARCH QEMU_ARCH_M68K | ||||
| #elif defined(TARGET_LM32) | ||||
| #define QEMU_ARCH QEMU_ARCH_LM32 | ||||
| #elif defined(TARGET_MICROBLAZE) | ||||
| #define QEMU_ARCH QEMU_ARCH_MICROBLAZE | ||||
| #elif defined(TARGET_MIPS) | ||||
| @@ -80,12 +79,12 @@ int graphic_depth = 32; | ||||
| #define QEMU_ARCH QEMU_ARCH_SH4 | ||||
| #elif defined(TARGET_SPARC) | ||||
| #define QEMU_ARCH QEMU_ARCH_SPARC | ||||
| #elif defined(TARGET_TRICORE) | ||||
| #define QEMU_ARCH QEMU_ARCH_TRICORE | ||||
| #elif defined(TARGET_UNICORE32) | ||||
| #define QEMU_ARCH QEMU_ARCH_UNICORE32 | ||||
| #elif defined(TARGET_XTENSA) | ||||
| #define QEMU_ARCH QEMU_ARCH_XTENSA | ||||
| #elif defined(TARGET_UNICORE32) | ||||
| #define QEMU_ARCH QEMU_ARCH_UNICORE32 | ||||
| #elif defined(TARGET_TRICORE) | ||||
| #define QEMU_ARCH QEMU_ARCH_TRICORE | ||||
| #endif | ||||
|  | ||||
| const uint32_t arch_type = QEMU_ARCH; | ||||
| @@ -113,8 +112,7 @@ TargetInfo *qmp_query_target(Error **errp) | ||||
| { | ||||
|     TargetInfo *info = g_malloc0(sizeof(*info)); | ||||
|  | ||||
|     info->arch = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, -1, | ||||
|                                  &error_abort); | ||||
|     info->arch = g_strdup(TARGET_NAME); | ||||
|  | ||||
|     return info; | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o | ||||
| common-obj-y = audio.o noaudio.o wavaudio.o mixeng.o | ||||
| common-obj-$(CONFIG_SPICE) += spiceaudio.o | ||||
| common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o | ||||
| common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o | ||||
|   | ||||
| @@ -28,14 +28,35 @@ | ||||
| #include "audio.h" | ||||
| #include "trace.h" | ||||
|  | ||||
| #if QEMU_GNUC_PREREQ(4, 3) | ||||
| #pragma GCC diagnostic ignored "-Waddress" | ||||
| #endif | ||||
|  | ||||
| #define AUDIO_CAP "alsa" | ||||
| #include "audio_int.h" | ||||
|  | ||||
| typedef struct ALSAConf { | ||||
|     int size_in_usec_in; | ||||
|     int size_in_usec_out; | ||||
|     const char *pcm_name_in; | ||||
|     const char *pcm_name_out; | ||||
|     unsigned int buffer_size_in; | ||||
|     unsigned int period_size_in; | ||||
|     unsigned int buffer_size_out; | ||||
|     unsigned int period_size_out; | ||||
|     unsigned int threshold; | ||||
|  | ||||
|     int buffer_size_in_overridden; | ||||
|     int period_size_in_overridden; | ||||
|  | ||||
|     int buffer_size_out_overridden; | ||||
|     int period_size_out_overridden; | ||||
| } ALSAConf; | ||||
|  | ||||
| struct pollhlp { | ||||
|     snd_pcm_t *handle; | ||||
|     struct pollfd *pfds; | ||||
|     ALSAConf *conf; | ||||
|     int count; | ||||
|     int mask; | ||||
| }; | ||||
| @@ -47,7 +68,6 @@ typedef struct ALSAVoiceOut { | ||||
|     void *pcm_buf; | ||||
|     snd_pcm_t *handle; | ||||
|     struct pollhlp pollhlp; | ||||
|     Audiodev *dev; | ||||
| } ALSAVoiceOut; | ||||
|  | ||||
| typedef struct ALSAVoiceIn { | ||||
| @@ -55,18 +75,21 @@ typedef struct ALSAVoiceIn { | ||||
|     snd_pcm_t *handle; | ||||
|     void *pcm_buf; | ||||
|     struct pollhlp pollhlp; | ||||
|     Audiodev *dev; | ||||
| } ALSAVoiceIn; | ||||
|  | ||||
| struct alsa_params_req { | ||||
|     int freq; | ||||
|     snd_pcm_format_t fmt; | ||||
|     int nchannels; | ||||
|     int size_in_usec; | ||||
|     int override_mask; | ||||
|     unsigned int buffer_size; | ||||
|     unsigned int period_size; | ||||
| }; | ||||
|  | ||||
| struct alsa_params_obt { | ||||
|     int freq; | ||||
|     AudioFormat fmt; | ||||
|     audfmt_e fmt; | ||||
|     int endianness; | ||||
|     int nchannels; | ||||
|     snd_pcm_uframes_t samples; | ||||
| @@ -273,16 +296,16 @@ static int alsa_write (SWVoiceOut *sw, void *buf, int len) | ||||
|     return audio_pcm_sw_write (sw, buf, len); | ||||
| } | ||||
|  | ||||
| static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness) | ||||
| static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness) | ||||
| { | ||||
|     switch (fmt) { | ||||
|     case AUDIO_FORMAT_S8: | ||||
|     case AUD_FMT_S8: | ||||
|         return SND_PCM_FORMAT_S8; | ||||
|  | ||||
|     case AUDIO_FORMAT_U8: | ||||
|     case AUD_FMT_U8: | ||||
|         return SND_PCM_FORMAT_U8; | ||||
|  | ||||
|     case AUDIO_FORMAT_S16: | ||||
|     case AUD_FMT_S16: | ||||
|         if (endianness) { | ||||
|             return SND_PCM_FORMAT_S16_BE; | ||||
|         } | ||||
| @@ -290,7 +313,7 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness) | ||||
|             return SND_PCM_FORMAT_S16_LE; | ||||
|         } | ||||
|  | ||||
|     case AUDIO_FORMAT_U16: | ||||
|     case AUD_FMT_U16: | ||||
|         if (endianness) { | ||||
|             return SND_PCM_FORMAT_U16_BE; | ||||
|         } | ||||
| @@ -298,7 +321,7 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness) | ||||
|             return SND_PCM_FORMAT_U16_LE; | ||||
|         } | ||||
|  | ||||
|     case AUDIO_FORMAT_S32: | ||||
|     case AUD_FMT_S32: | ||||
|         if (endianness) { | ||||
|             return SND_PCM_FORMAT_S32_BE; | ||||
|         } | ||||
| @@ -306,7 +329,7 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness) | ||||
|             return SND_PCM_FORMAT_S32_LE; | ||||
|         } | ||||
|  | ||||
|     case AUDIO_FORMAT_U32: | ||||
|     case AUD_FMT_U32: | ||||
|         if (endianness) { | ||||
|             return SND_PCM_FORMAT_U32_BE; | ||||
|         } | ||||
| @@ -323,58 +346,58 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt, | ||||
| static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt, | ||||
|                            int *endianness) | ||||
| { | ||||
|     switch (alsafmt) { | ||||
|     case SND_PCM_FORMAT_S8: | ||||
|         *endianness = 0; | ||||
|         *fmt = AUDIO_FORMAT_S8; | ||||
|         *fmt = AUD_FMT_S8; | ||||
|         break; | ||||
|  | ||||
|     case SND_PCM_FORMAT_U8: | ||||
|         *endianness = 0; | ||||
|         *fmt = AUDIO_FORMAT_U8; | ||||
|         *fmt = AUD_FMT_U8; | ||||
|         break; | ||||
|  | ||||
|     case SND_PCM_FORMAT_S16_LE: | ||||
|         *endianness = 0; | ||||
|         *fmt = AUDIO_FORMAT_S16; | ||||
|         *fmt = AUD_FMT_S16; | ||||
|         break; | ||||
|  | ||||
|     case SND_PCM_FORMAT_U16_LE: | ||||
|         *endianness = 0; | ||||
|         *fmt = AUDIO_FORMAT_U16; | ||||
|         *fmt = AUD_FMT_U16; | ||||
|         break; | ||||
|  | ||||
|     case SND_PCM_FORMAT_S16_BE: | ||||
|         *endianness = 1; | ||||
|         *fmt = AUDIO_FORMAT_S16; | ||||
|         *fmt = AUD_FMT_S16; | ||||
|         break; | ||||
|  | ||||
|     case SND_PCM_FORMAT_U16_BE: | ||||
|         *endianness = 1; | ||||
|         *fmt = AUDIO_FORMAT_U16; | ||||
|         *fmt = AUD_FMT_U16; | ||||
|         break; | ||||
|  | ||||
|     case SND_PCM_FORMAT_S32_LE: | ||||
|         *endianness = 0; | ||||
|         *fmt = AUDIO_FORMAT_S32; | ||||
|         *fmt = AUD_FMT_S32; | ||||
|         break; | ||||
|  | ||||
|     case SND_PCM_FORMAT_U32_LE: | ||||
|         *endianness = 0; | ||||
|         *fmt = AUDIO_FORMAT_U32; | ||||
|         *fmt = AUD_FMT_U32; | ||||
|         break; | ||||
|  | ||||
|     case SND_PCM_FORMAT_S32_BE: | ||||
|         *endianness = 1; | ||||
|         *fmt = AUDIO_FORMAT_S32; | ||||
|         *fmt = AUD_FMT_S32; | ||||
|         break; | ||||
|  | ||||
|     case SND_PCM_FORMAT_U32_BE: | ||||
|         *endianness = 1; | ||||
|         *fmt = AUDIO_FORMAT_U32; | ||||
|         *fmt = AUD_FMT_U32; | ||||
|         break; | ||||
|  | ||||
|     default: | ||||
| @@ -387,18 +410,17 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt, | ||||
|  | ||||
| static void alsa_dump_info (struct alsa_params_req *req, | ||||
|                             struct alsa_params_obt *obt, | ||||
|                             snd_pcm_format_t obtfmt, | ||||
|                             AudiodevAlsaPerDirectionOptions *apdo) | ||||
|                             snd_pcm_format_t obtfmt) | ||||
| { | ||||
|     dolog("parameter | requested value | obtained value\n"); | ||||
|     dolog("format    |      %10d |     %10d\n", req->fmt, obtfmt); | ||||
|     dolog("channels  |      %10d |     %10d\n", | ||||
|           req->nchannels, obt->nchannels); | ||||
|     dolog("frequency |      %10d |     %10d\n", req->freq, obt->freq); | ||||
|     dolog("============================================\n"); | ||||
|     dolog("requested: buffer len %" PRId32 " period len %" PRId32 "\n", | ||||
|           apdo->buffer_length, apdo->period_length); | ||||
|     dolog("obtained: samples %ld\n", obt->samples); | ||||
|     dolog ("parameter | requested value | obtained value\n"); | ||||
|     dolog ("format    |      %10d |     %10d\n", req->fmt, obtfmt); | ||||
|     dolog ("channels  |      %10d |     %10d\n", | ||||
|            req->nchannels, obt->nchannels); | ||||
|     dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq); | ||||
|     dolog ("============================================\n"); | ||||
|     dolog ("requested: buffer size %d period size %d\n", | ||||
|            req->buffer_size, req->period_size); | ||||
|     dolog ("obtained: samples %ld\n", obt->samples); | ||||
| } | ||||
|  | ||||
| static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold) | ||||
| @@ -431,23 +453,23 @@ static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int alsa_open(bool in, struct alsa_params_req *req, | ||||
|                      struct alsa_params_obt *obt, snd_pcm_t **handlep, | ||||
|                      Audiodev *dev) | ||||
| static int alsa_open (int in, struct alsa_params_req *req, | ||||
|                       struct alsa_params_obt *obt, snd_pcm_t **handlep, | ||||
|                       ALSAConf *conf) | ||||
| { | ||||
|     AudiodevAlsaOptions *aopts = &dev->u.alsa; | ||||
|     AudiodevAlsaPerDirectionOptions *apdo = in ? aopts->in : aopts->out; | ||||
|     snd_pcm_t *handle; | ||||
|     snd_pcm_hw_params_t *hw_params; | ||||
|     int err; | ||||
|     int size_in_usec; | ||||
|     unsigned int freq, nchannels; | ||||
|     const char *pcm_name = apdo->has_dev ? apdo->dev : "default"; | ||||
|     const char *pcm_name = in ? conf->pcm_name_in : conf->pcm_name_out; | ||||
|     snd_pcm_uframes_t obt_buffer_size; | ||||
|     const char *typ = in ? "ADC" : "DAC"; | ||||
|     snd_pcm_format_t obtfmt; | ||||
|  | ||||
|     freq = req->freq; | ||||
|     nchannels = req->nchannels; | ||||
|     size_in_usec = req->size_in_usec; | ||||
|  | ||||
|     snd_pcm_hw_params_alloca (&hw_params); | ||||
|  | ||||
| @@ -507,42 +529,79 @@ static int alsa_open(bool in, struct alsa_params_req *req, | ||||
|         goto err; | ||||
|     } | ||||
|  | ||||
|     if (apdo->buffer_length) { | ||||
|         int dir = 0; | ||||
|         unsigned int btime = apdo->buffer_length; | ||||
|     if (req->buffer_size) { | ||||
|         unsigned long obt; | ||||
|  | ||||
|         err = snd_pcm_hw_params_set_buffer_time_near( | ||||
|             handle, hw_params, &btime, &dir); | ||||
|         if (size_in_usec) { | ||||
|             int dir = 0; | ||||
|             unsigned int btime = req->buffer_size; | ||||
|  | ||||
|             err = snd_pcm_hw_params_set_buffer_time_near ( | ||||
|                 handle, | ||||
|                 hw_params, | ||||
|                 &btime, | ||||
|                 &dir | ||||
|                 ); | ||||
|             obt = btime; | ||||
|         } | ||||
|         else { | ||||
|             snd_pcm_uframes_t bsize = req->buffer_size; | ||||
|  | ||||
|             err = snd_pcm_hw_params_set_buffer_size_near ( | ||||
|                 handle, | ||||
|                 hw_params, | ||||
|                 &bsize | ||||
|                 ); | ||||
|             obt = bsize; | ||||
|         } | ||||
|         if (err < 0) { | ||||
|             alsa_logerr2(err, typ, "Failed to set buffer time to %" PRId32 "\n", | ||||
|                          apdo->buffer_length); | ||||
|             alsa_logerr2 (err, typ, "Failed to set buffer %s to %d\n", | ||||
|                           size_in_usec ? "time" : "size", req->buffer_size); | ||||
|             goto err; | ||||
|         } | ||||
|  | ||||
|         if (apdo->has_buffer_length && btime != apdo->buffer_length) { | ||||
|             dolog("Requested buffer time %" PRId32 | ||||
|                   " was rejected, using %u\n", apdo->buffer_length, btime); | ||||
|         } | ||||
|         if ((req->override_mask & 2) && (obt - req->buffer_size)) | ||||
|             dolog ("Requested buffer %s %u was rejected, using %lu\n", | ||||
|                    size_in_usec ? "time" : "size", req->buffer_size, obt); | ||||
|     } | ||||
|  | ||||
|     if (apdo->period_length) { | ||||
|         int dir = 0; | ||||
|         unsigned int ptime = apdo->period_length; | ||||
|     if (req->period_size) { | ||||
|         unsigned long obt; | ||||
|  | ||||
|         err = snd_pcm_hw_params_set_period_time_near(handle, hw_params, &ptime, | ||||
|                                                      &dir); | ||||
|         if (size_in_usec) { | ||||
|             int dir = 0; | ||||
|             unsigned int ptime = req->period_size; | ||||
|  | ||||
|             err = snd_pcm_hw_params_set_period_time_near ( | ||||
|                 handle, | ||||
|                 hw_params, | ||||
|                 &ptime, | ||||
|                 &dir | ||||
|                 ); | ||||
|             obt = ptime; | ||||
|         } | ||||
|         else { | ||||
|             int dir = 0; | ||||
|             snd_pcm_uframes_t psize = req->period_size; | ||||
|  | ||||
|             err = snd_pcm_hw_params_set_period_size_near ( | ||||
|                 handle, | ||||
|                 hw_params, | ||||
|                 &psize, | ||||
|                 &dir | ||||
|                 ); | ||||
|             obt = psize; | ||||
|         } | ||||
|  | ||||
|         if (err < 0) { | ||||
|             alsa_logerr2(err, typ, "Failed to set period time to %" PRId32 "\n", | ||||
|                          apdo->period_length); | ||||
|             alsa_logerr2 (err, typ, "Failed to set period %s to %d\n", | ||||
|                           size_in_usec ? "time" : "size", req->period_size); | ||||
|             goto err; | ||||
|         } | ||||
|  | ||||
|         if (apdo->has_period_length && ptime != apdo->period_length) { | ||||
|             dolog("Requested period time %" PRId32 " was rejected, using %d\n", | ||||
|                   apdo->period_length, ptime); | ||||
|         } | ||||
|         if (((req->override_mask & 1) && (obt - req->period_size))) | ||||
|             dolog ("Requested period %s %u was rejected, using %lu\n", | ||||
|                    size_in_usec ? "time" : "size", req->period_size, obt); | ||||
|     } | ||||
|  | ||||
|     err = snd_pcm_hw_params (handle, hw_params); | ||||
| @@ -574,12 +633,30 @@ static int alsa_open(bool in, struct alsa_params_req *req, | ||||
|         goto err; | ||||
|     } | ||||
|  | ||||
|     if (!in && aopts->has_threshold && aopts->threshold) { | ||||
|         struct audsettings as = { .freq = freq }; | ||||
|         alsa_set_threshold( | ||||
|             handle, | ||||
|             audio_buffer_frames(qapi_AudiodevAlsaPerDirectionOptions_base(apdo), | ||||
|                                 &as, aopts->threshold)); | ||||
|     if (!in && conf->threshold) { | ||||
|         snd_pcm_uframes_t threshold; | ||||
|         int bytes_per_sec; | ||||
|  | ||||
|         bytes_per_sec = freq << (nchannels == 2); | ||||
|  | ||||
|         switch (obt->fmt) { | ||||
|         case AUD_FMT_S8: | ||||
|         case AUD_FMT_U8: | ||||
|             break; | ||||
|  | ||||
|         case AUD_FMT_S16: | ||||
|         case AUD_FMT_U16: | ||||
|             bytes_per_sec <<= 1; | ||||
|             break; | ||||
|  | ||||
|         case AUD_FMT_S32: | ||||
|         case AUD_FMT_U32: | ||||
|             bytes_per_sec <<= 2; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         threshold = (conf->threshold * bytes_per_sec) / 1000; | ||||
|         alsa_set_threshold (handle, threshold); | ||||
|     } | ||||
|  | ||||
|     obt->nchannels = nchannels; | ||||
| @@ -592,11 +669,11 @@ static int alsa_open(bool in, struct alsa_params_req *req, | ||||
|          obt->nchannels != req->nchannels || | ||||
|          obt->freq != req->freq) { | ||||
|         dolog ("Audio parameters for %s\n", typ); | ||||
|         alsa_dump_info(req, obt, obtfmt, apdo); | ||||
|         alsa_dump_info (req, obt, obtfmt); | ||||
|     } | ||||
|  | ||||
| #ifdef DEBUG | ||||
|     alsa_dump_info(req, obt, obtfmt, pdo); | ||||
|     alsa_dump_info (req, obt, obtfmt); | ||||
| #endif | ||||
|     return 0; | ||||
|  | ||||
| @@ -722,13 +799,19 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     struct alsa_params_obt obt; | ||||
|     snd_pcm_t *handle; | ||||
|     struct audsettings obt_as; | ||||
|     Audiodev *dev = drv_opaque; | ||||
|     ALSAConf *conf = drv_opaque; | ||||
|  | ||||
|     req.fmt = aud_to_alsafmt (as->fmt, as->endianness); | ||||
|     req.freq = as->freq; | ||||
|     req.nchannels = as->nchannels; | ||||
|     req.period_size = conf->period_size_out; | ||||
|     req.buffer_size = conf->buffer_size_out; | ||||
|     req.size_in_usec = conf->size_in_usec_out; | ||||
|     req.override_mask = | ||||
|         (conf->period_size_out_overridden ? 1 : 0) | | ||||
|         (conf->buffer_size_out_overridden ? 2 : 0); | ||||
|  | ||||
|     if (alsa_open(0, &req, &obt, &handle, dev)) { | ||||
|     if (alsa_open (0, &req, &obt, &handle, conf)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| @@ -749,7 +832,7 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     } | ||||
|  | ||||
|     alsa->handle = handle; | ||||
|     alsa->dev = dev; | ||||
|     alsa->pollhlp.conf = conf; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @@ -789,12 +872,16 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl) | ||||
| static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
| { | ||||
|     ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; | ||||
|     AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.out; | ||||
|  | ||||
|     switch (cmd) { | ||||
|     case VOICE_ENABLE: | ||||
|         { | ||||
|             bool poll_mode = apdo->try_poll; | ||||
|             va_list ap; | ||||
|             int poll_mode; | ||||
|  | ||||
|             va_start (ap, cmd); | ||||
|             poll_mode = va_arg (ap, int); | ||||
|             va_end (ap); | ||||
|  | ||||
|             ldebug ("enabling voice\n"); | ||||
|             if (poll_mode && alsa_poll_out (hw)) { | ||||
| @@ -823,13 +910,19 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | ||||
|     struct alsa_params_obt obt; | ||||
|     snd_pcm_t *handle; | ||||
|     struct audsettings obt_as; | ||||
|     Audiodev *dev = drv_opaque; | ||||
|     ALSAConf *conf = drv_opaque; | ||||
|  | ||||
|     req.fmt = aud_to_alsafmt (as->fmt, as->endianness); | ||||
|     req.freq = as->freq; | ||||
|     req.nchannels = as->nchannels; | ||||
|     req.period_size = conf->period_size_in; | ||||
|     req.buffer_size = conf->buffer_size_in; | ||||
|     req.size_in_usec = conf->size_in_usec_in; | ||||
|     req.override_mask = | ||||
|         (conf->period_size_in_overridden ? 1 : 0) | | ||||
|         (conf->buffer_size_in_overridden ? 2 : 0); | ||||
|  | ||||
|     if (alsa_open(1, &req, &obt, &handle, dev)) { | ||||
|     if (alsa_open (1, &req, &obt, &handle, conf)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| @@ -850,7 +943,7 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | ||||
|     } | ||||
|  | ||||
|     alsa->handle = handle; | ||||
|     alsa->dev = dev; | ||||
|     alsa->pollhlp.conf = conf; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @@ -992,12 +1085,16 @@ static int alsa_read (SWVoiceIn *sw, void *buf, int size) | ||||
| static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||
| { | ||||
|     ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; | ||||
|     AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.in; | ||||
|  | ||||
|     switch (cmd) { | ||||
|     case VOICE_ENABLE: | ||||
|         { | ||||
|             bool poll_mode = apdo->try_poll; | ||||
|             va_list ap; | ||||
|             int poll_mode; | ||||
|  | ||||
|             va_start (ap, cmd); | ||||
|             poll_mode = va_arg (ap, int); | ||||
|             va_end (ap); | ||||
|  | ||||
|             ldebug ("enabling voice\n"); | ||||
|             if (poll_mode && alsa_poll_in (hw)) { | ||||
| @@ -1020,54 +1117,88 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo) | ||||
| static ALSAConf glob_conf = { | ||||
|     .buffer_size_out = 4096, | ||||
|     .period_size_out = 1024, | ||||
|     .pcm_name_out = "default", | ||||
|     .pcm_name_in = "default", | ||||
| }; | ||||
|  | ||||
| static void *alsa_audio_init (void) | ||||
| { | ||||
|     if (!apdo->has_try_poll) { | ||||
|         apdo->try_poll = true; | ||||
|         apdo->has_try_poll = true; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void *alsa_audio_init(Audiodev *dev) | ||||
| { | ||||
|     AudiodevAlsaOptions *aopts; | ||||
|     assert(dev->driver == AUDIODEV_DRIVER_ALSA); | ||||
|  | ||||
|     aopts = &dev->u.alsa; | ||||
|     alsa_init_per_direction(aopts->in); | ||||
|     alsa_init_per_direction(aopts->out); | ||||
|  | ||||
|     /* | ||||
|      * need to define them, as otherwise alsa produces no sound | ||||
|      * doesn't set has_* so alsa_open can identify it wasn't set by the user | ||||
|      */ | ||||
|     if (!dev->u.alsa.out->has_period_length) { | ||||
|         /* 1024 frames assuming 44100Hz */ | ||||
|         dev->u.alsa.out->period_length = 1024 * 1000000 / 44100; | ||||
|     } | ||||
|     if (!dev->u.alsa.out->has_buffer_length) { | ||||
|         /* 4096 frames assuming 44100Hz */ | ||||
|         dev->u.alsa.out->buffer_length = 4096ll * 1000000 / 44100; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * OptsVisitor sets unspecified optional fields to zero, but do not depend | ||||
|      * on it... | ||||
|      */ | ||||
|     if (!dev->u.alsa.in->has_period_length) { | ||||
|         dev->u.alsa.in->period_length = 0; | ||||
|     } | ||||
|     if (!dev->u.alsa.in->has_buffer_length) { | ||||
|         dev->u.alsa.in->buffer_length = 0; | ||||
|     } | ||||
|  | ||||
|     return dev; | ||||
|     ALSAConf *conf = g_malloc(sizeof(ALSAConf)); | ||||
|     *conf = glob_conf; | ||||
|     return conf; | ||||
| } | ||||
|  | ||||
| static void alsa_audio_fini (void *opaque) | ||||
| { | ||||
|     g_free(opaque); | ||||
| } | ||||
|  | ||||
| static struct audio_option alsa_options[] = { | ||||
|     { | ||||
|         .name        = "DAC_SIZE_IN_USEC", | ||||
|         .tag         = AUD_OPT_BOOL, | ||||
|         .valp        = &glob_conf.size_in_usec_out, | ||||
|         .descr       = "DAC period/buffer size in microseconds (otherwise in frames)" | ||||
|     }, | ||||
|     { | ||||
|         .name        = "DAC_PERIOD_SIZE", | ||||
|         .tag         = AUD_OPT_INT, | ||||
|         .valp        = &glob_conf.period_size_out, | ||||
|         .descr       = "DAC period size (0 to go with system default)", | ||||
|         .overriddenp = &glob_conf.period_size_out_overridden | ||||
|     }, | ||||
|     { | ||||
|         .name        = "DAC_BUFFER_SIZE", | ||||
|         .tag         = AUD_OPT_INT, | ||||
|         .valp        = &glob_conf.buffer_size_out, | ||||
|         .descr       = "DAC buffer size (0 to go with system default)", | ||||
|         .overriddenp = &glob_conf.buffer_size_out_overridden | ||||
|     }, | ||||
|     { | ||||
|         .name        = "ADC_SIZE_IN_USEC", | ||||
|         .tag         = AUD_OPT_BOOL, | ||||
|         .valp        = &glob_conf.size_in_usec_in, | ||||
|         .descr       = | ||||
|         "ADC period/buffer size in microseconds (otherwise in frames)" | ||||
|     }, | ||||
|     { | ||||
|         .name        = "ADC_PERIOD_SIZE", | ||||
|         .tag         = AUD_OPT_INT, | ||||
|         .valp        = &glob_conf.period_size_in, | ||||
|         .descr       = "ADC period size (0 to go with system default)", | ||||
|         .overriddenp = &glob_conf.period_size_in_overridden | ||||
|     }, | ||||
|     { | ||||
|         .name        = "ADC_BUFFER_SIZE", | ||||
|         .tag         = AUD_OPT_INT, | ||||
|         .valp        = &glob_conf.buffer_size_in, | ||||
|         .descr       = "ADC buffer size (0 to go with system default)", | ||||
|         .overriddenp = &glob_conf.buffer_size_in_overridden | ||||
|     }, | ||||
|     { | ||||
|         .name        = "THRESHOLD", | ||||
|         .tag         = AUD_OPT_INT, | ||||
|         .valp        = &glob_conf.threshold, | ||||
|         .descr       = "(undocumented)" | ||||
|     }, | ||||
|     { | ||||
|         .name        = "DAC_DEV", | ||||
|         .tag         = AUD_OPT_STR, | ||||
|         .valp        = &glob_conf.pcm_name_out, | ||||
|         .descr       = "DAC device name (for instance dmix)" | ||||
|     }, | ||||
|     { | ||||
|         .name        = "ADC_DEV", | ||||
|         .tag         = AUD_OPT_STR, | ||||
|         .valp        = &glob_conf.pcm_name_in, | ||||
|         .descr       = "ADC device name" | ||||
|     }, | ||||
|     { /* End of list */ } | ||||
| }; | ||||
|  | ||||
| static struct audio_pcm_ops alsa_pcm_ops = { | ||||
|     .init_out = alsa_init_out, | ||||
|     .fini_out = alsa_fini_out, | ||||
| @@ -1085,6 +1216,7 @@ static struct audio_pcm_ops alsa_pcm_ops = { | ||||
| static struct audio_driver alsa_audio_driver = { | ||||
|     .name           = "alsa", | ||||
|     .descr          = "ALSA http://www.alsa-project.org", | ||||
|     .options        = alsa_options, | ||||
|     .init           = alsa_audio_init, | ||||
|     .fini           = alsa_audio_fini, | ||||
|     .pcm_ops        = &alsa_pcm_ops, | ||||
|   | ||||
							
								
								
									
										951
									
								
								audio/audio.c
									
									
									
									
									
								
							
							
						
						
									
										951
									
								
								audio/audio.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -26,31 +26,30 @@ | ||||
| #define QEMU_AUDIO_H | ||||
|  | ||||
| #include "qemu/queue.h" | ||||
| #include "qapi/qapi-types-audio.h" | ||||
|  | ||||
| typedef void (*audio_callback_fn) (void *opaque, int avail); | ||||
|  | ||||
| typedef enum { | ||||
|     AUD_FMT_U8, | ||||
|     AUD_FMT_S8, | ||||
|     AUD_FMT_U16, | ||||
|     AUD_FMT_S16, | ||||
|     AUD_FMT_U32, | ||||
|     AUD_FMT_S32 | ||||
| } audfmt_e; | ||||
|  | ||||
| #ifdef HOST_WORDS_BIGENDIAN | ||||
| #define AUDIO_HOST_ENDIANNESS 1 | ||||
| #else | ||||
| #define AUDIO_HOST_ENDIANNESS 0 | ||||
| #endif | ||||
|  | ||||
| typedef struct audsettings { | ||||
| struct audsettings { | ||||
|     int freq; | ||||
|     int nchannels; | ||||
|     AudioFormat fmt; | ||||
|     audfmt_e fmt; | ||||
|     int endianness; | ||||
| } audsettings; | ||||
|  | ||||
| audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo); | ||||
| int audioformat_bytes_per_sample(AudioFormat fmt); | ||||
| int audio_buffer_frames(AudiodevPerDirectionOptions *pdo, | ||||
|                         audsettings *as, int def_usecs); | ||||
| int audio_buffer_samples(AudiodevPerDirectionOptions *pdo, | ||||
|                          audsettings *as, int def_usecs); | ||||
| int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo, | ||||
|                        audsettings *as, int def_usecs); | ||||
| }; | ||||
|  | ||||
| typedef enum { | ||||
|     AUD_CNOTIFY_ENABLE, | ||||
| @@ -90,6 +89,7 @@ typedef struct QEMUAudioTimeStamp { | ||||
| void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); | ||||
| void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3); | ||||
|  | ||||
| void AUD_help (void); | ||||
| void AUD_register_card (const char *name, QEMUSoundCard *card); | ||||
| void AUD_remove_card (QEMUSoundCard *card); | ||||
| CaptureVoiceOut *AUD_add_capture ( | ||||
| @@ -171,8 +171,4 @@ void audio_sample_to_uint64(void *samples, int pos, | ||||
| void audio_sample_from_uint64(void *samples, int pos, | ||||
|                             uint64_t left, uint64_t right); | ||||
|  | ||||
| void audio_parse_option(const char *opt); | ||||
| void audio_init_audiodevs(void); | ||||
| void audio_legacy_help(void); | ||||
|  | ||||
| #endif /* QEMU_AUDIO_H */ | ||||
|   | ||||
| @@ -33,6 +33,22 @@ | ||||
|  | ||||
| struct audio_pcm_ops; | ||||
|  | ||||
| typedef enum { | ||||
|     AUD_OPT_INT, | ||||
|     AUD_OPT_FMT, | ||||
|     AUD_OPT_STR, | ||||
|     AUD_OPT_BOOL | ||||
| } audio_option_tag_e; | ||||
|  | ||||
| struct audio_option { | ||||
|     const char *name; | ||||
|     audio_option_tag_e tag; | ||||
|     void *valp; | ||||
|     const char *descr; | ||||
|     int *overriddenp; | ||||
|     int overridden; | ||||
| }; | ||||
|  | ||||
| struct audio_callback { | ||||
|     void *opaque; | ||||
|     audio_callback_fn fn; | ||||
| @@ -129,7 +145,8 @@ typedef struct audio_driver audio_driver; | ||||
| struct audio_driver { | ||||
|     const char *name; | ||||
|     const char *descr; | ||||
|     void *(*init) (Audiodev *); | ||||
|     struct audio_option *options; | ||||
|     void *(*init) (void); | ||||
|     void (*fini) (void *); | ||||
|     struct audio_pcm_ops *pcm_ops; | ||||
|     int can_be_default; | ||||
| @@ -174,9 +191,8 @@ struct SWVoiceCap { | ||||
|     QLIST_ENTRY (SWVoiceCap) entries; | ||||
| }; | ||||
|  | ||||
| typedef struct AudioState { | ||||
| struct AudioState { | ||||
|     struct audio_driver *drv; | ||||
|     Audiodev *dev; | ||||
|     void *drv_opaque; | ||||
|  | ||||
|     QEMUTimer *ts; | ||||
| @@ -187,13 +203,10 @@ typedef struct AudioState { | ||||
|     int nb_hw_voices_out; | ||||
|     int nb_hw_voices_in; | ||||
|     int vm_running; | ||||
|     int64_t period_ticks; | ||||
| } AudioState; | ||||
| }; | ||||
|  | ||||
| extern const struct mixeng_volume nominal_volume; | ||||
|  | ||||
| extern const char *audio_prio_list[]; | ||||
|  | ||||
| void audio_driver_register(audio_driver *drv); | ||||
| audio_driver *audio_driver_lookup(const char *name); | ||||
|  | ||||
| @@ -235,18 +248,4 @@ static inline int audio_ring_dist (int dst, int src, int len) | ||||
| #define AUDIO_STRINGIFY_(n) #n | ||||
| #define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n) | ||||
|  | ||||
| typedef struct AudiodevListEntry { | ||||
|     Audiodev *dev; | ||||
|     QSIMPLEQ_ENTRY(AudiodevListEntry) next; | ||||
| } AudiodevListEntry; | ||||
|  | ||||
| typedef QSIMPLEQ_HEAD(, AudiodevListEntry) AudiodevListHead; | ||||
| AudiodevListHead audio_handle_legacy_opts(void); | ||||
|  | ||||
| void audio_free_audiodev_list(AudiodevListHead *head); | ||||
|  | ||||
| void audio_create_pdos(Audiodev *dev); | ||||
| AudiodevPerDirectionOptions *audio_get_pdo_in(Audiodev *dev); | ||||
| AudiodevPerDirectionOptions *audio_get_pdo_out(Audiodev *dev); | ||||
|  | ||||
| #endif /* QEMU_AUDIO_INT_H */ | ||||
|   | ||||
| @@ -1,544 +0,0 @@ | ||||
| /* | ||||
|  * QEMU Audio subsystem: legacy configuration handling | ||||
|  * | ||||
|  * Copyright (c) 2015-2019 Zoltán Kővágó <DirtY.iCE.hu@gmail.com> | ||||
|  * | ||||
|  * 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 "audio.h" | ||||
| #include "audio_int.h" | ||||
| #include "qemu-common.h" | ||||
| #include "qemu/cutils.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qapi/qapi-visit-audio.h" | ||||
| #include "qapi/visitor-impl.h" | ||||
|  | ||||
| #define AUDIO_CAP "audio-legacy" | ||||
| #include "audio_int.h" | ||||
|  | ||||
| static uint32_t toui32(const char *str) | ||||
| { | ||||
|     unsigned long long ret; | ||||
|     if (parse_uint_full(str, &ret, 10) || ret > UINT32_MAX) { | ||||
|         dolog("Invalid integer value `%s'\n", str); | ||||
|         exit(1); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /* helper functions to convert env variables */ | ||||
| static void get_bool(const char *env, bool *dst, bool *has_dst) | ||||
| { | ||||
|     const char *val = getenv(env); | ||||
|     if (val) { | ||||
|         *dst = toui32(val) != 0; | ||||
|         *has_dst = true; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void get_int(const char *env, uint32_t *dst, bool *has_dst) | ||||
| { | ||||
|     const char *val = getenv(env); | ||||
|     if (val) { | ||||
|         *dst = toui32(val); | ||||
|         *has_dst = true; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void get_str(const char *env, char **dst, bool *has_dst) | ||||
| { | ||||
|     const char *val = getenv(env); | ||||
|     if (val) { | ||||
|         if (*has_dst) { | ||||
|             g_free(*dst); | ||||
|         } | ||||
|         *dst = g_strdup(val); | ||||
|         *has_dst = true; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void get_fmt(const char *env, AudioFormat *dst, bool *has_dst) | ||||
| { | ||||
|     const char *val = getenv(env); | ||||
|     if (val) { | ||||
|         size_t i; | ||||
|         for (i = 0; AudioFormat_lookup.size; ++i) { | ||||
|             if (strcasecmp(val, AudioFormat_lookup.array[i]) == 0) { | ||||
|                 *dst = i; | ||||
|                 *has_dst = true; | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         dolog("Invalid audio format `%s'\n", val); | ||||
|         exit(1); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| static void get_millis_to_usecs(const char *env, uint32_t *dst, bool *has_dst) | ||||
| { | ||||
|     const char *val = getenv(env); | ||||
|     if (val) { | ||||
|         *dst = toui32(val) * 1000; | ||||
|         *has_dst = true; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static uint32_t frames_to_usecs(uint32_t frames, | ||||
|                                 AudiodevPerDirectionOptions *pdo) | ||||
| { | ||||
|     uint32_t freq = pdo->has_frequency ? pdo->frequency : 44100; | ||||
|     return (frames * 1000000 + freq / 2) / freq; | ||||
| } | ||||
|  | ||||
|  | ||||
| static void get_frames_to_usecs(const char *env, uint32_t *dst, bool *has_dst, | ||||
|                                 AudiodevPerDirectionOptions *pdo) | ||||
| { | ||||
|     const char *val = getenv(env); | ||||
|     if (val) { | ||||
|         *dst = frames_to_usecs(toui32(val), pdo); | ||||
|         *has_dst = true; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static uint32_t samples_to_usecs(uint32_t samples, | ||||
|                                  AudiodevPerDirectionOptions *pdo) | ||||
| { | ||||
|     uint32_t channels = pdo->has_channels ? pdo->channels : 2; | ||||
|     return frames_to_usecs(samples / channels, pdo); | ||||
| } | ||||
|  | ||||
| static void get_samples_to_usecs(const char *env, uint32_t *dst, bool *has_dst, | ||||
|                                  AudiodevPerDirectionOptions *pdo) | ||||
| { | ||||
|     const char *val = getenv(env); | ||||
|     if (val) { | ||||
|         *dst = samples_to_usecs(toui32(val), pdo); | ||||
|         *has_dst = true; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static uint32_t bytes_to_usecs(uint32_t bytes, AudiodevPerDirectionOptions *pdo) | ||||
| { | ||||
|     AudioFormat fmt = pdo->has_format ? pdo->format : AUDIO_FORMAT_S16; | ||||
|     uint32_t bytes_per_sample = audioformat_bytes_per_sample(fmt); | ||||
|     return samples_to_usecs(bytes / bytes_per_sample, pdo); | ||||
| } | ||||
|  | ||||
| static void get_bytes_to_usecs(const char *env, uint32_t *dst, bool *has_dst, | ||||
|                                AudiodevPerDirectionOptions *pdo) | ||||
| { | ||||
|     const char *val = getenv(env); | ||||
|     if (val) { | ||||
|         *dst = bytes_to_usecs(toui32(val), pdo); | ||||
|         *has_dst = true; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* backend specific functions */ | ||||
| /* ALSA */ | ||||
| static void handle_alsa_per_direction( | ||||
|     AudiodevAlsaPerDirectionOptions *apdo, const char *prefix) | ||||
| { | ||||
|     char buf[64]; | ||||
|     size_t len = strlen(prefix); | ||||
|     bool size_in_usecs = false; | ||||
|     bool dummy; | ||||
|  | ||||
|     memcpy(buf, prefix, len); | ||||
|     strcpy(buf + len, "TRY_POLL"); | ||||
|     get_bool(buf, &apdo->try_poll, &apdo->has_try_poll); | ||||
|  | ||||
|     strcpy(buf + len, "DEV"); | ||||
|     get_str(buf, &apdo->dev, &apdo->has_dev); | ||||
|  | ||||
|     strcpy(buf + len, "SIZE_IN_USEC"); | ||||
|     get_bool(buf, &size_in_usecs, &dummy); | ||||
|  | ||||
|     strcpy(buf + len, "PERIOD_SIZE"); | ||||
|     get_int(buf, &apdo->period_length, &apdo->has_period_length); | ||||
|     if (apdo->has_period_length && !size_in_usecs) { | ||||
|         apdo->period_length = frames_to_usecs( | ||||
|             apdo->period_length, | ||||
|             qapi_AudiodevAlsaPerDirectionOptions_base(apdo)); | ||||
|     } | ||||
|  | ||||
|     strcpy(buf + len, "BUFFER_SIZE"); | ||||
|     get_int(buf, &apdo->buffer_length, &apdo->has_buffer_length); | ||||
|     if (apdo->has_buffer_length && !size_in_usecs) { | ||||
|         apdo->buffer_length = frames_to_usecs( | ||||
|             apdo->buffer_length, | ||||
|             qapi_AudiodevAlsaPerDirectionOptions_base(apdo)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void handle_alsa(Audiodev *dev) | ||||
| { | ||||
|     AudiodevAlsaOptions *aopt = &dev->u.alsa; | ||||
|     handle_alsa_per_direction(aopt->in, "QEMU_ALSA_ADC_"); | ||||
|     handle_alsa_per_direction(aopt->out, "QEMU_ALSA_DAC_"); | ||||
|  | ||||
|     get_millis_to_usecs("QEMU_ALSA_THRESHOLD", | ||||
|                         &aopt->threshold, &aopt->has_threshold); | ||||
| } | ||||
|  | ||||
| /* coreaudio */ | ||||
| static void handle_coreaudio(Audiodev *dev) | ||||
| { | ||||
|     get_frames_to_usecs( | ||||
|         "QEMU_COREAUDIO_BUFFER_SIZE", | ||||
|         &dev->u.coreaudio.out->buffer_length, | ||||
|         &dev->u.coreaudio.out->has_buffer_length, | ||||
|         qapi_AudiodevCoreaudioPerDirectionOptions_base(dev->u.coreaudio.out)); | ||||
|     get_int("QEMU_COREAUDIO_BUFFER_COUNT", | ||||
|             &dev->u.coreaudio.out->buffer_count, | ||||
|             &dev->u.coreaudio.out->has_buffer_count); | ||||
| } | ||||
|  | ||||
| /* dsound */ | ||||
| static void handle_dsound(Audiodev *dev) | ||||
| { | ||||
|     get_millis_to_usecs("QEMU_DSOUND_LATENCY_MILLIS", | ||||
|                         &dev->u.dsound.latency, &dev->u.dsound.has_latency); | ||||
|     get_bytes_to_usecs("QEMU_DSOUND_BUFSIZE_OUT", | ||||
|                        &dev->u.dsound.out->buffer_length, | ||||
|                        &dev->u.dsound.out->has_buffer_length, | ||||
|                        dev->u.dsound.out); | ||||
|     get_bytes_to_usecs("QEMU_DSOUND_BUFSIZE_IN", | ||||
|                        &dev->u.dsound.in->buffer_length, | ||||
|                        &dev->u.dsound.in->has_buffer_length, | ||||
|                        dev->u.dsound.in); | ||||
| } | ||||
|  | ||||
| /* OSS */ | ||||
| static void handle_oss_per_direction( | ||||
|     AudiodevOssPerDirectionOptions *opdo, const char *try_poll_env, | ||||
|     const char *dev_env) | ||||
| { | ||||
|     get_bool(try_poll_env, &opdo->try_poll, &opdo->has_try_poll); | ||||
|     get_str(dev_env, &opdo->dev, &opdo->has_dev); | ||||
|  | ||||
|     get_bytes_to_usecs("QEMU_OSS_FRAGSIZE", | ||||
|                        &opdo->buffer_length, &opdo->has_buffer_length, | ||||
|                        qapi_AudiodevOssPerDirectionOptions_base(opdo)); | ||||
|     get_int("QEMU_OSS_NFRAGS", &opdo->buffer_count, | ||||
|             &opdo->has_buffer_count); | ||||
| } | ||||
|  | ||||
| static void handle_oss(Audiodev *dev) | ||||
| { | ||||
|     AudiodevOssOptions *oopt = &dev->u.oss; | ||||
|     handle_oss_per_direction(oopt->in, "QEMU_AUDIO_ADC_TRY_POLL", | ||||
|                              "QEMU_OSS_ADC_DEV"); | ||||
|     handle_oss_per_direction(oopt->out, "QEMU_AUDIO_DAC_TRY_POLL", | ||||
|                              "QEMU_OSS_DAC_DEV"); | ||||
|  | ||||
|     get_bool("QEMU_OSS_MMAP", &oopt->try_mmap, &oopt->has_try_mmap); | ||||
|     get_bool("QEMU_OSS_EXCLUSIVE", &oopt->exclusive, &oopt->has_exclusive); | ||||
|     get_int("QEMU_OSS_POLICY", &oopt->dsp_policy, &oopt->has_dsp_policy); | ||||
| } | ||||
|  | ||||
| /* pulseaudio */ | ||||
| static void handle_pa_per_direction( | ||||
|     AudiodevPaPerDirectionOptions *ppdo, const char *env) | ||||
| { | ||||
|     get_str(env, &ppdo->name, &ppdo->has_name); | ||||
| } | ||||
|  | ||||
| static void handle_pa(Audiodev *dev) | ||||
| { | ||||
|     handle_pa_per_direction(dev->u.pa.in, "QEMU_PA_SOURCE"); | ||||
|     handle_pa_per_direction(dev->u.pa.out, "QEMU_PA_SINK"); | ||||
|  | ||||
|     get_samples_to_usecs( | ||||
|         "QEMU_PA_SAMPLES", &dev->u.pa.in->buffer_length, | ||||
|         &dev->u.pa.in->has_buffer_length, | ||||
|         qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.in)); | ||||
|     get_samples_to_usecs( | ||||
|         "QEMU_PA_SAMPLES", &dev->u.pa.out->buffer_length, | ||||
|         &dev->u.pa.out->has_buffer_length, | ||||
|         qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.out)); | ||||
|  | ||||
|     get_str("QEMU_PA_SERVER", &dev->u.pa.server, &dev->u.pa.has_server); | ||||
| } | ||||
|  | ||||
| /* SDL */ | ||||
| static void handle_sdl(Audiodev *dev) | ||||
| { | ||||
|     /* SDL is output only */ | ||||
|     get_samples_to_usecs("QEMU_SDL_SAMPLES", &dev->u.sdl.out->buffer_length, | ||||
|                          &dev->u.sdl.out->has_buffer_length, dev->u.sdl.out); | ||||
| } | ||||
|  | ||||
| /* wav */ | ||||
| static void handle_wav(Audiodev *dev) | ||||
| { | ||||
|     get_int("QEMU_WAV_FREQUENCY", | ||||
|             &dev->u.wav.out->frequency, &dev->u.wav.out->has_frequency); | ||||
|     get_fmt("QEMU_WAV_FORMAT", &dev->u.wav.out->format, | ||||
|             &dev->u.wav.out->has_format); | ||||
|     get_int("QEMU_WAV_DAC_FIXED_CHANNELS", | ||||
|             &dev->u.wav.out->channels, &dev->u.wav.out->has_channels); | ||||
|     get_str("QEMU_WAV_PATH", &dev->u.wav.path, &dev->u.wav.has_path); | ||||
| } | ||||
|  | ||||
| /* general */ | ||||
| static void handle_per_direction( | ||||
|     AudiodevPerDirectionOptions *pdo, const char *prefix) | ||||
| { | ||||
|     char buf[64]; | ||||
|     size_t len = strlen(prefix); | ||||
|  | ||||
|     memcpy(buf, prefix, len); | ||||
|     strcpy(buf + len, "FIXED_SETTINGS"); | ||||
|     get_bool(buf, &pdo->fixed_settings, &pdo->has_fixed_settings); | ||||
|  | ||||
|     strcpy(buf + len, "FIXED_FREQ"); | ||||
|     get_int(buf, &pdo->frequency, &pdo->has_frequency); | ||||
|  | ||||
|     strcpy(buf + len, "FIXED_FMT"); | ||||
|     get_fmt(buf, &pdo->format, &pdo->has_format); | ||||
|  | ||||
|     strcpy(buf + len, "FIXED_CHANNELS"); | ||||
|     get_int(buf, &pdo->channels, &pdo->has_channels); | ||||
|  | ||||
|     strcpy(buf + len, "VOICES"); | ||||
|     get_int(buf, &pdo->voices, &pdo->has_voices); | ||||
| } | ||||
|  | ||||
| static AudiodevListEntry *legacy_opt(const char *drvname) | ||||
| { | ||||
|     AudiodevListEntry *e = g_malloc0(sizeof(AudiodevListEntry)); | ||||
|     e->dev = g_malloc0(sizeof(Audiodev)); | ||||
|     e->dev->id = g_strdup(drvname); | ||||
|     e->dev->driver = qapi_enum_parse( | ||||
|         &AudiodevDriver_lookup, drvname, -1, &error_abort); | ||||
|  | ||||
|     audio_create_pdos(e->dev); | ||||
|  | ||||
|     handle_per_direction(audio_get_pdo_in(e->dev), "QEMU_AUDIO_ADC_"); | ||||
|     handle_per_direction(audio_get_pdo_out(e->dev), "QEMU_AUDIO_DAC_"); | ||||
|  | ||||
|     get_int("QEMU_AUDIO_TIMER_PERIOD", | ||||
|             &e->dev->timer_period, &e->dev->has_timer_period); | ||||
|  | ||||
|     switch (e->dev->driver) { | ||||
|     case AUDIODEV_DRIVER_ALSA: | ||||
|         handle_alsa(e->dev); | ||||
|         break; | ||||
|  | ||||
|     case AUDIODEV_DRIVER_COREAUDIO: | ||||
|         handle_coreaudio(e->dev); | ||||
|         break; | ||||
|  | ||||
|     case AUDIODEV_DRIVER_DSOUND: | ||||
|         handle_dsound(e->dev); | ||||
|         break; | ||||
|  | ||||
|     case AUDIODEV_DRIVER_OSS: | ||||
|         handle_oss(e->dev); | ||||
|         break; | ||||
|  | ||||
|     case AUDIODEV_DRIVER_PA: | ||||
|         handle_pa(e->dev); | ||||
|         break; | ||||
|  | ||||
|     case AUDIODEV_DRIVER_SDL: | ||||
|         handle_sdl(e->dev); | ||||
|         break; | ||||
|  | ||||
|     case AUDIODEV_DRIVER_WAV: | ||||
|         handle_wav(e->dev); | ||||
|         break; | ||||
|  | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     return e; | ||||
| } | ||||
|  | ||||
| AudiodevListHead audio_handle_legacy_opts(void) | ||||
| { | ||||
|     const char *drvname = getenv("QEMU_AUDIO_DRV"); | ||||
|     AudiodevListHead head = QSIMPLEQ_HEAD_INITIALIZER(head); | ||||
|  | ||||
|     if (drvname) { | ||||
|         AudiodevListEntry *e; | ||||
|         audio_driver *driver = audio_driver_lookup(drvname); | ||||
|         if (!driver) { | ||||
|             dolog("Unknown audio driver `%s'\n", drvname); | ||||
|             exit(1); | ||||
|         } | ||||
|         e = legacy_opt(drvname); | ||||
|         QSIMPLEQ_INSERT_TAIL(&head, e, next); | ||||
|     } else { | ||||
|         for (int i = 0; audio_prio_list[i]; i++) { | ||||
|             audio_driver *driver = audio_driver_lookup(audio_prio_list[i]); | ||||
|             if (driver && driver->can_be_default) { | ||||
|                 AudiodevListEntry *e = legacy_opt(driver->name); | ||||
|                 QSIMPLEQ_INSERT_TAIL(&head, e, next); | ||||
|             } | ||||
|         } | ||||
|         if (QSIMPLEQ_EMPTY(&head)) { | ||||
|             dolog("Internal error: no default audio driver available\n"); | ||||
|             exit(1); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return head; | ||||
| } | ||||
|  | ||||
| /* visitor to print -audiodev option */ | ||||
| typedef struct { | ||||
|     Visitor visitor; | ||||
|  | ||||
|     bool comma; | ||||
|     GList *path; | ||||
| } LegacyPrintVisitor; | ||||
|  | ||||
| static void lv_start_struct(Visitor *v, const char *name, void **obj, | ||||
|                             size_t size, Error **errp) | ||||
| { | ||||
|     LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v; | ||||
|     lv->path = g_list_append(lv->path, g_strdup(name)); | ||||
| } | ||||
|  | ||||
| static void lv_end_struct(Visitor *v, void **obj) | ||||
| { | ||||
|     LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v; | ||||
|     lv->path = g_list_delete_link(lv->path, g_list_last(lv->path)); | ||||
| } | ||||
|  | ||||
| static void lv_print_key(Visitor *v, const char *name) | ||||
| { | ||||
|     GList *e; | ||||
|     LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v; | ||||
|     if (lv->comma) { | ||||
|         putchar(','); | ||||
|     } else { | ||||
|         lv->comma = true; | ||||
|     } | ||||
|  | ||||
|     for (e = lv->path; e; e = e->next) { | ||||
|         if (e->data) { | ||||
|             printf("%s.", (const char *) e->data); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     printf("%s=", name); | ||||
| } | ||||
|  | ||||
| static void lv_type_int64(Visitor *v, const char *name, int64_t *obj, | ||||
|                           Error **errp) | ||||
| { | ||||
|     lv_print_key(v, name); | ||||
|     printf("%" PRIi64, *obj); | ||||
| } | ||||
|  | ||||
| static void lv_type_uint64(Visitor *v, const char *name, uint64_t *obj, | ||||
|                            Error **errp) | ||||
| { | ||||
|     lv_print_key(v, name); | ||||
|     printf("%" PRIu64, *obj); | ||||
| } | ||||
|  | ||||
| static void lv_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) | ||||
| { | ||||
|     lv_print_key(v, name); | ||||
|     printf("%s", *obj ? "on" : "off"); | ||||
| } | ||||
|  | ||||
| static void lv_type_str(Visitor *v, const char *name, char **obj, Error **errp) | ||||
| { | ||||
|     const char *str = *obj; | ||||
|     lv_print_key(v, name); | ||||
|  | ||||
|     while (*str) { | ||||
|         if (*str == ',') { | ||||
|             putchar(','); | ||||
|         } | ||||
|         putchar(*str++); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void lv_complete(Visitor *v, void *opaque) | ||||
| { | ||||
|     LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v; | ||||
|     assert(lv->path == NULL); | ||||
| } | ||||
|  | ||||
| static void lv_free(Visitor *v) | ||||
| { | ||||
|     LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v; | ||||
|  | ||||
|     g_list_free_full(lv->path, g_free); | ||||
|     g_free(lv); | ||||
| } | ||||
|  | ||||
| static Visitor *legacy_visitor_new(void) | ||||
| { | ||||
|     LegacyPrintVisitor *lv = g_malloc0(sizeof(LegacyPrintVisitor)); | ||||
|  | ||||
|     lv->visitor.start_struct = lv_start_struct; | ||||
|     lv->visitor.end_struct = lv_end_struct; | ||||
|     /* lists not supported */ | ||||
|     lv->visitor.type_int64 = lv_type_int64; | ||||
|     lv->visitor.type_uint64 = lv_type_uint64; | ||||
|     lv->visitor.type_bool = lv_type_bool; | ||||
|     lv->visitor.type_str = lv_type_str; | ||||
|  | ||||
|     lv->visitor.type = VISITOR_OUTPUT; | ||||
|     lv->visitor.complete = lv_complete; | ||||
|     lv->visitor.free = lv_free; | ||||
|  | ||||
|     return &lv->visitor; | ||||
| } | ||||
|  | ||||
| void audio_legacy_help(void) | ||||
| { | ||||
|     AudiodevListHead head; | ||||
|     AudiodevListEntry *e; | ||||
|  | ||||
|     printf("Environment variable based configuration deprecated.\n"); | ||||
|     printf("Please use the new -audiodev option.\n"); | ||||
|  | ||||
|     head = audio_handle_legacy_opts(); | ||||
|     printf("\nEquivalent -audiodev to your current environment variables:\n"); | ||||
|     if (!getenv("QEMU_AUDIO_DRV")) { | ||||
|         printf("(Since you didn't specify QEMU_AUDIO_DRV, I'll list all " | ||||
|                "possibilities)\n"); | ||||
|     } | ||||
|  | ||||
|     QSIMPLEQ_FOREACH(e, &head, next) { | ||||
|         Visitor *v; | ||||
|         Audiodev *dev = e->dev; | ||||
|         printf("-audiodev "); | ||||
|  | ||||
|         v = legacy_visitor_new(); | ||||
|         visit_type_Audiodev(v, NULL, &dev, &error_abort); | ||||
|         visit_free(v); | ||||
|  | ||||
|         printf("\n"); | ||||
|     } | ||||
|     audio_free_audiodev_list(&head); | ||||
| } | ||||
| @@ -299,42 +299,11 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as) | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev) | ||||
| { | ||||
|     switch (dev->driver) { | ||||
|     case AUDIODEV_DRIVER_NONE: | ||||
|         return dev->u.none.TYPE; | ||||
|     case AUDIODEV_DRIVER_ALSA: | ||||
|         return qapi_AudiodevAlsaPerDirectionOptions_base(dev->u.alsa.TYPE); | ||||
|     case AUDIODEV_DRIVER_COREAUDIO: | ||||
|         return qapi_AudiodevCoreaudioPerDirectionOptions_base( | ||||
|             dev->u.coreaudio.TYPE); | ||||
|     case AUDIODEV_DRIVER_DSOUND: | ||||
|         return dev->u.dsound.TYPE; | ||||
|     case AUDIODEV_DRIVER_OSS: | ||||
|         return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.TYPE); | ||||
|     case AUDIODEV_DRIVER_PA: | ||||
|         return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE); | ||||
|     case AUDIODEV_DRIVER_SDL: | ||||
|         return dev->u.sdl.TYPE; | ||||
|     case AUDIODEV_DRIVER_SPICE: | ||||
|         return dev->u.spice.TYPE; | ||||
|     case AUDIODEV_DRIVER_WAV: | ||||
|         return dev->u.wav.TYPE; | ||||
|  | ||||
|     case AUDIODEV_DRIVER__MAX: | ||||
|         break; | ||||
|     } | ||||
|     abort(); | ||||
| } | ||||
|  | ||||
| static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as) | ||||
| { | ||||
|     HW *hw; | ||||
|     AudioState *s = &glob_audio_state; | ||||
|     AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev); | ||||
|  | ||||
|     if (pdo->fixed_settings) { | ||||
|     if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) { | ||||
|         hw = glue (audio_pcm_hw_add_new_, TYPE) (as); | ||||
|         if (hw) { | ||||
|             return hw; | ||||
| @@ -362,11 +331,9 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) ( | ||||
|     SW *sw; | ||||
|     HW *hw; | ||||
|     struct audsettings hw_as; | ||||
|     AudioState *s = &glob_audio_state; | ||||
|     AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev); | ||||
|  | ||||
|     if (pdo->fixed_settings) { | ||||
|         hw_as = audiodev_to_audsettings(pdo); | ||||
|     if (glue (conf.fixed_, TYPE).enabled) { | ||||
|         hw_as = glue (conf.fixed_, TYPE).settings; | ||||
|     } | ||||
|     else { | ||||
|         hw_as = *as; | ||||
| @@ -431,7 +398,6 @@ SW *glue (AUD_open_, TYPE) ( | ||||
|     ) | ||||
| { | ||||
|     AudioState *s = &glob_audio_state; | ||||
|     AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev); | ||||
|  | ||||
|     if (audio_bug(__func__, !card || !name || !callback_fn || !as)) { | ||||
|         dolog ("card=%p name=%p callback_fn=%p as=%p\n", | ||||
| @@ -456,7 +422,7 @@ SW *glue (AUD_open_, TYPE) ( | ||||
|         return sw; | ||||
|     } | ||||
|  | ||||
|     if (!pdo->fixed_settings && sw) { | ||||
|     if (!glue (conf.fixed_, TYPE).enabled && sw) { | ||||
|         glue (AUD_close_, TYPE) (card, sw); | ||||
|         sw = NULL; | ||||
|     } | ||||
|   | ||||
| @@ -24,20 +24,20 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx, | ||||
|     wfx->cbSize = 0; | ||||
|  | ||||
|     switch (as->fmt) { | ||||
|     case AUDIO_FORMAT_S8: | ||||
|     case AUDIO_FORMAT_U8: | ||||
|     case AUD_FMT_S8: | ||||
|     case AUD_FMT_U8: | ||||
|         wfx->wBitsPerSample = 8; | ||||
|         break; | ||||
|  | ||||
|     case AUDIO_FORMAT_S16: | ||||
|     case AUDIO_FORMAT_U16: | ||||
|     case AUD_FMT_S16: | ||||
|     case AUD_FMT_U16: | ||||
|         wfx->wBitsPerSample = 16; | ||||
|         wfx->nAvgBytesPerSec <<= 1; | ||||
|         wfx->nBlockAlign <<= 1; | ||||
|         break; | ||||
|  | ||||
|     case AUDIO_FORMAT_S32: | ||||
|     case AUDIO_FORMAT_U32: | ||||
|     case AUD_FMT_S32: | ||||
|     case AUD_FMT_U32: | ||||
|         wfx->wBitsPerSample = 32; | ||||
|         wfx->nAvgBytesPerSec <<= 2; | ||||
|         wfx->nBlockAlign <<= 2; | ||||
| @@ -85,15 +85,15 @@ int waveformat_to_audio_settings (WAVEFORMATEX *wfx, | ||||
|  | ||||
|     switch (wfx->wBitsPerSample) { | ||||
|     case 8: | ||||
|         as->fmt = AUDIO_FORMAT_U8; | ||||
|         as->fmt = AUD_FMT_U8; | ||||
|         break; | ||||
|  | ||||
|     case 16: | ||||
|         as->fmt = AUDIO_FORMAT_S16; | ||||
|         as->fmt = AUD_FMT_S16; | ||||
|         break; | ||||
|  | ||||
|     case 32: | ||||
|         as->fmt = AUDIO_FORMAT_S32; | ||||
|         as->fmt = AUD_FMT_S32; | ||||
|         break; | ||||
|  | ||||
|     default: | ||||
|   | ||||
| @@ -36,6 +36,11 @@ | ||||
| #define MAC_OS_X_VERSION_10_6 1060 | ||||
| #endif | ||||
|  | ||||
| typedef struct { | ||||
|     int buffer_frames; | ||||
|     int nbuffers; | ||||
| } CoreaudioConf; | ||||
|  | ||||
| typedef struct coreaudioVoiceOut { | ||||
|     HWVoiceOut hw; | ||||
|     pthread_mutex_t mutex; | ||||
| @@ -502,9 +507,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     int err; | ||||
|     const char *typ = "playback"; | ||||
|     AudioValueRange frameRange; | ||||
|     Audiodev *dev = drv_opaque; | ||||
|     AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out; | ||||
|     int frames; | ||||
|     CoreaudioConf *conf = drv_opaque; | ||||
|  | ||||
|     /* create mutex */ | ||||
|     err = pthread_mutex_init(&core->mutex, NULL); | ||||
| @@ -535,17 +538,16 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     frames = audio_buffer_frames( | ||||
|         qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610); | ||||
|     if (frameRange.mMinimum > frames) { | ||||
|     if (frameRange.mMinimum > conf->buffer_frames) { | ||||
|         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; | ||||
|         dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum); | ||||
|     } else if (frameRange.mMaximum < frames) { | ||||
|     } | ||||
|     else if (frameRange.mMaximum < conf->buffer_frames) { | ||||
|         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; | ||||
|         dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); | ||||
|     } | ||||
|     else { | ||||
|         core->audioDevicePropertyBufferFrameSize = frames; | ||||
|         core->audioDevicePropertyBufferFrameSize = conf->buffer_frames; | ||||
|     } | ||||
|  | ||||
|     /* set Buffer Frame Size */ | ||||
| @@ -566,8 +568,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|                            "Could not get device buffer frame size\n"); | ||||
|         return -1; | ||||
|     } | ||||
|     hw->samples = (cpdo->has_buffer_count ? cpdo->buffer_count : 4) * | ||||
|         core->audioDevicePropertyBufferFrameSize; | ||||
|     hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize; | ||||
|  | ||||
|     /* get StreamFormat */ | ||||
|     status = coreaudio_get_streamformat(core->outputDeviceID, | ||||
| @@ -679,15 +680,40 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void *coreaudio_audio_init(Audiodev *dev) | ||||
| static CoreaudioConf glob_conf = { | ||||
|     .buffer_frames = 512, | ||||
|     .nbuffers = 4, | ||||
| }; | ||||
|  | ||||
| static void *coreaudio_audio_init (void) | ||||
| { | ||||
|     return dev; | ||||
|     CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf)); | ||||
|     *conf = glob_conf; | ||||
|  | ||||
|     return conf; | ||||
| } | ||||
|  | ||||
| static void coreaudio_audio_fini (void *opaque) | ||||
| { | ||||
|     g_free(opaque); | ||||
| } | ||||
|  | ||||
| static struct audio_option coreaudio_options[] = { | ||||
|     { | ||||
|         .name  = "BUFFER_SIZE", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.buffer_frames, | ||||
|         .descr = "Size of the buffer in frames" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "BUFFER_COUNT", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.nbuffers, | ||||
|         .descr = "Number of buffers" | ||||
|     }, | ||||
|     { /* End of list */ } | ||||
| }; | ||||
|  | ||||
| static struct audio_pcm_ops coreaudio_pcm_ops = { | ||||
|     .init_out = coreaudio_init_out, | ||||
|     .fini_out = coreaudio_fini_out, | ||||
| @@ -699,6 +725,7 @@ static struct audio_pcm_ops coreaudio_pcm_ops = { | ||||
| static struct audio_driver coreaudio_audio_driver = { | ||||
|     .name           = "coreaudio", | ||||
|     .descr          = "CoreAudio http://developer.apple.com/audio/coreaudio.html", | ||||
|     .options        = coreaudio_options, | ||||
|     .init           = coreaudio_audio_init, | ||||
|     .fini           = coreaudio_audio_fini, | ||||
|     .pcm_ops        = &coreaudio_pcm_ops, | ||||
|   | ||||
| @@ -167,18 +167,17 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     dsound *s = drv_opaque; | ||||
|     WAVEFORMATEX wfx; | ||||
|     struct audsettings obt_as; | ||||
|     DSoundConf *conf = &s->conf; | ||||
| #ifdef DSBTYPE_IN | ||||
|     const char *typ = "ADC"; | ||||
|     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; | ||||
|     DSCBUFFERDESC bd; | ||||
|     DSCBCAPS bc; | ||||
|     AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.in; | ||||
| #else | ||||
|     const char *typ = "DAC"; | ||||
|     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; | ||||
|     DSBUFFERDESC bd; | ||||
|     DSBCAPS bc; | ||||
|     AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.out; | ||||
| #endif | ||||
|  | ||||
|     if (!s->FIELD2) { | ||||
| @@ -194,8 +193,8 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     memset (&bd, 0, sizeof (bd)); | ||||
|     bd.dwSize = sizeof (bd); | ||||
|     bd.lpwfxFormat = &wfx; | ||||
|     bd.dwBufferBytes = audio_buffer_bytes(pdo, as, 92880); | ||||
| #ifdef DSBTYPE_IN | ||||
|     bd.dwBufferBytes = conf->bufsize_in; | ||||
|     hr = IDirectSoundCapture_CreateCaptureBuffer ( | ||||
|         s->dsound_capture, | ||||
|         &bd, | ||||
| @@ -204,6 +203,7 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|         ); | ||||
| #else | ||||
|     bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; | ||||
|     bd.dwBufferBytes = conf->bufsize_out; | ||||
|     hr = IDirectSound_CreateSoundBuffer ( | ||||
|         s->dsound, | ||||
|         &bd, | ||||
|   | ||||
| @@ -32,7 +32,6 @@ | ||||
|  | ||||
| #define AUDIO_CAP "dsound" | ||||
| #include "audio_int.h" | ||||
| #include "qemu/host-utils.h" | ||||
|  | ||||
| #include <windows.h> | ||||
| #include <mmsystem.h> | ||||
| @@ -43,11 +42,17 @@ | ||||
|  | ||||
| /* #define DEBUG_DSOUND */ | ||||
|  | ||||
| typedef struct { | ||||
|     int bufsize_in; | ||||
|     int bufsize_out; | ||||
|     int latency_millis; | ||||
| } DSoundConf; | ||||
|  | ||||
| typedef struct { | ||||
|     LPDIRECTSOUND dsound; | ||||
|     LPDIRECTSOUNDCAPTURE dsound_capture; | ||||
|     struct audsettings settings; | ||||
|     Audiodev *dev; | ||||
|     DSoundConf conf; | ||||
| } dsound; | ||||
|  | ||||
| typedef struct { | ||||
| @@ -243,9 +248,9 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 ( | ||||
|     dsound_log_hresult (hr); | ||||
| } | ||||
|  | ||||
| static uint64_t usecs_to_bytes(struct audio_pcm_info *info, uint32_t usecs) | ||||
| static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis) | ||||
| { | ||||
|     return muldiv64(usecs, info->bytes_per_second, 1000000); | ||||
|     return (millis * info->bytes_per_second) / 1000; | ||||
| } | ||||
|  | ||||
| #ifdef DEBUG_DSOUND | ||||
| @@ -473,7 +478,7 @@ static int dsound_run_out (HWVoiceOut *hw, int live) | ||||
|     LPVOID p1, p2; | ||||
|     int bufsize; | ||||
|     dsound *s = ds->s; | ||||
|     AudiodevDsoundOptions *dso = &s->dev->u.dsound; | ||||
|     DSoundConf *conf = &s->conf; | ||||
|  | ||||
|     if (!dsb) { | ||||
|         dolog ("Attempt to run empty with playback buffer\n"); | ||||
| @@ -496,14 +501,14 @@ static int dsound_run_out (HWVoiceOut *hw, int live) | ||||
|     len = live << hwshift; | ||||
|  | ||||
|     if (ds->first_time) { | ||||
|         if (dso->latency) { | ||||
|         if (conf->latency_millis) { | ||||
|             DWORD cur_blat; | ||||
|  | ||||
|             cur_blat = audio_ring_dist (wpos, ppos, bufsize); | ||||
|             ds->first_time = 0; | ||||
|             old_pos = wpos; | ||||
|             old_pos += | ||||
|                 usecs_to_bytes(&hw->info, dso->latency) - cur_blat; | ||||
|                 millis_to_bytes (&hw->info, conf->latency_millis) - cur_blat; | ||||
|             old_pos %= bufsize; | ||||
|             old_pos &= ~hw->info.align; | ||||
|         } | ||||
| @@ -742,6 +747,12 @@ static int dsound_run_in (HWVoiceIn *hw) | ||||
|     return decr; | ||||
| } | ||||
|  | ||||
| static DSoundConf glob_conf = { | ||||
|     .bufsize_in         = 16384, | ||||
|     .bufsize_out        = 16384, | ||||
|     .latency_millis     = 10 | ||||
| }; | ||||
|  | ||||
| static void dsound_audio_fini (void *opaque) | ||||
| { | ||||
|     HRESULT hr; | ||||
| @@ -772,22 +783,13 @@ static void dsound_audio_fini (void *opaque) | ||||
|     g_free(s); | ||||
| } | ||||
|  | ||||
| static void *dsound_audio_init(Audiodev *dev) | ||||
| static void *dsound_audio_init (void) | ||||
| { | ||||
|     int err; | ||||
|     HRESULT hr; | ||||
|     dsound *s = g_malloc0(sizeof(dsound)); | ||||
|     AudiodevDsoundOptions *dso; | ||||
|  | ||||
|     assert(dev->driver == AUDIODEV_DRIVER_DSOUND); | ||||
|     s->dev = dev; | ||||
|     dso = &dev->u.dsound; | ||||
|  | ||||
|     if (!dso->has_latency) { | ||||
|         dso->has_latency = true; | ||||
|         dso->latency = 10000; /* 10 ms */ | ||||
|     } | ||||
|  | ||||
|     s->conf = glob_conf; | ||||
|     hr = CoInitialize (NULL); | ||||
|     if (FAILED (hr)) { | ||||
|         dsound_logerr (hr, "Could not initialize COM\n"); | ||||
| @@ -852,6 +854,28 @@ static void *dsound_audio_init(Audiodev *dev) | ||||
|     return s; | ||||
| } | ||||
|  | ||||
| static struct audio_option dsound_options[] = { | ||||
|     { | ||||
|         .name  = "LATENCY_MILLIS", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.latency_millis, | ||||
|         .descr = "(undocumented)" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "BUFSIZE_OUT", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.bufsize_out, | ||||
|         .descr = "(undocumented)" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "BUFSIZE_IN", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.bufsize_in, | ||||
|         .descr = "(undocumented)" | ||||
|     }, | ||||
|     { /* End of list */ } | ||||
| }; | ||||
|  | ||||
| static struct audio_pcm_ops dsound_pcm_ops = { | ||||
|     .init_out = dsound_init_out, | ||||
|     .fini_out = dsound_fini_out, | ||||
| @@ -869,6 +893,7 @@ static struct audio_pcm_ops dsound_pcm_ops = { | ||||
| static struct audio_driver dsound_audio_driver = { | ||||
|     .name           = "dsound", | ||||
|     .descr          = "DirectSound http://wikipedia.org/wiki/DirectSound", | ||||
|     .options        = dsound_options, | ||||
|     .init           = dsound_audio_init, | ||||
|     .fini           = dsound_audio_fini, | ||||
|     .pcm_ops        = &dsound_pcm_ops, | ||||
|   | ||||
| @@ -136,7 +136,7 @@ static int no_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void *no_audio_init(Audiodev *dev) | ||||
| static void *no_audio_init (void) | ||||
| { | ||||
|     return &no_audio_init; | ||||
| } | ||||
| @@ -163,6 +163,7 @@ static struct audio_pcm_ops no_pcm_ops = { | ||||
| static struct audio_driver no_audio_driver = { | ||||
|     .name           = "none", | ||||
|     .descr          = "Timer based audio emulation", | ||||
|     .options        = NULL, | ||||
|     .init           = no_audio_init, | ||||
|     .fini           = no_audio_fini, | ||||
|     .pcm_ops        = &no_pcm_ops, | ||||
|   | ||||
							
								
								
									
										193
									
								
								audio/ossaudio.c
									
									
									
									
									
								
							
							
						
						
									
										193
									
								
								audio/ossaudio.c
									
									
									
									
									
								
							| @@ -37,6 +37,16 @@ | ||||
| #define USE_DSP_POLICY | ||||
| #endif | ||||
|  | ||||
| typedef struct OSSConf { | ||||
|     int try_mmap; | ||||
|     int nfrags; | ||||
|     int fragsize; | ||||
|     const char *devpath_out; | ||||
|     const char *devpath_in; | ||||
|     int exclusive; | ||||
|     int policy; | ||||
| } OSSConf; | ||||
|  | ||||
| typedef struct OSSVoiceOut { | ||||
|     HWVoiceOut hw; | ||||
|     void *pcm_buf; | ||||
| @@ -46,7 +56,7 @@ typedef struct OSSVoiceOut { | ||||
|     int fragsize; | ||||
|     int mmapped; | ||||
|     int pending; | ||||
|     Audiodev *dev; | ||||
|     OSSConf *conf; | ||||
| } OSSVoiceOut; | ||||
|  | ||||
| typedef struct OSSVoiceIn { | ||||
| @@ -55,12 +65,12 @@ typedef struct OSSVoiceIn { | ||||
|     int fd; | ||||
|     int nfrags; | ||||
|     int fragsize; | ||||
|     Audiodev *dev; | ||||
|     OSSConf *conf; | ||||
| } OSSVoiceIn; | ||||
|  | ||||
| struct oss_params { | ||||
|     int freq; | ||||
|     int fmt; | ||||
|     audfmt_e fmt; | ||||
|     int nchannels; | ||||
|     int nfrags; | ||||
|     int fragsize; | ||||
| @@ -138,16 +148,16 @@ static int oss_write (SWVoiceOut *sw, void *buf, int len) | ||||
|     return audio_pcm_sw_write (sw, buf, len); | ||||
| } | ||||
|  | ||||
| static int aud_to_ossfmt (AudioFormat fmt, int endianness) | ||||
| static int aud_to_ossfmt (audfmt_e fmt, int endianness) | ||||
| { | ||||
|     switch (fmt) { | ||||
|     case AUDIO_FORMAT_S8: | ||||
|     case AUD_FMT_S8: | ||||
|         return AFMT_S8; | ||||
|  | ||||
|     case AUDIO_FORMAT_U8: | ||||
|     case AUD_FMT_U8: | ||||
|         return AFMT_U8; | ||||
|  | ||||
|     case AUDIO_FORMAT_S16: | ||||
|     case AUD_FMT_S16: | ||||
|         if (endianness) { | ||||
|             return AFMT_S16_BE; | ||||
|         } | ||||
| @@ -155,7 +165,7 @@ static int aud_to_ossfmt (AudioFormat fmt, int endianness) | ||||
|             return AFMT_S16_LE; | ||||
|         } | ||||
|  | ||||
|     case AUDIO_FORMAT_U16: | ||||
|     case AUD_FMT_U16: | ||||
|         if (endianness) { | ||||
|             return AFMT_U16_BE; | ||||
|         } | ||||
| @@ -172,37 +182,37 @@ static int aud_to_ossfmt (AudioFormat fmt, int endianness) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int oss_to_audfmt (int ossfmt, AudioFormat *fmt, int *endianness) | ||||
| static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness) | ||||
| { | ||||
|     switch (ossfmt) { | ||||
|     case AFMT_S8: | ||||
|         *endianness = 0; | ||||
|         *fmt = AUDIO_FORMAT_S8; | ||||
|         *fmt = AUD_FMT_S8; | ||||
|         break; | ||||
|  | ||||
|     case AFMT_U8: | ||||
|         *endianness = 0; | ||||
|         *fmt = AUDIO_FORMAT_U8; | ||||
|         *fmt = AUD_FMT_U8; | ||||
|         break; | ||||
|  | ||||
|     case AFMT_S16_LE: | ||||
|         *endianness = 0; | ||||
|         *fmt = AUDIO_FORMAT_S16; | ||||
|         *fmt = AUD_FMT_S16; | ||||
|         break; | ||||
|  | ||||
|     case AFMT_U16_LE: | ||||
|         *endianness = 0; | ||||
|         *fmt = AUDIO_FORMAT_U16; | ||||
|         *fmt = AUD_FMT_U16; | ||||
|         break; | ||||
|  | ||||
|     case AFMT_S16_BE: | ||||
|         *endianness = 1; | ||||
|         *fmt = AUDIO_FORMAT_S16; | ||||
|         *fmt = AUD_FMT_S16; | ||||
|         break; | ||||
|  | ||||
|     case AFMT_U16_BE: | ||||
|         *endianness = 1; | ||||
|         *fmt = AUDIO_FORMAT_U16; | ||||
|         *fmt = AUD_FMT_U16; | ||||
|         break; | ||||
|  | ||||
|     default: | ||||
| @@ -252,25 +262,19 @@ static int oss_get_version (int fd, int *version, const char *typ) | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static int oss_open(int in, struct oss_params *req, audsettings *as, | ||||
|                     struct oss_params *obt, int *pfd, Audiodev *dev) | ||||
| static int oss_open (int in, struct oss_params *req, | ||||
|                      struct oss_params *obt, int *pfd, OSSConf* conf) | ||||
| { | ||||
|     AudiodevOssOptions *oopts = &dev->u.oss; | ||||
|     AudiodevOssPerDirectionOptions *opdo = in ? oopts->in : oopts->out; | ||||
|     int fd; | ||||
|     int oflags = (oopts->has_exclusive && oopts->exclusive) ? O_EXCL : 0; | ||||
|     int oflags = conf->exclusive ? O_EXCL : 0; | ||||
|     audio_buf_info abinfo; | ||||
|     int fmt, freq, nchannels; | ||||
|     int setfragment = 1; | ||||
|     const char *dspname = opdo->has_dev ? opdo->dev : "/dev/dsp"; | ||||
|     const char *dspname = in ? conf->devpath_in : conf->devpath_out; | ||||
|     const char *typ = in ? "ADC" : "DAC"; | ||||
| #ifdef USE_DSP_POLICY | ||||
|     int policy = oopts->has_dsp_policy ? oopts->dsp_policy : 5; | ||||
| #endif | ||||
|  | ||||
|     /* Kludge needed to have working mmap on Linux */ | ||||
|     oflags |= (oopts->has_try_mmap && oopts->try_mmap) ? | ||||
|         O_RDWR : (in ? O_RDONLY : O_WRONLY); | ||||
|     oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY); | ||||
|  | ||||
|     fd = open (dspname, oflags | O_NONBLOCK); | ||||
|     if (-1 == fd) { | ||||
| @@ -281,9 +285,6 @@ static int oss_open(int in, struct oss_params *req, audsettings *as, | ||||
|     freq = req->freq; | ||||
|     nchannels = req->nchannels; | ||||
|     fmt = req->fmt; | ||||
|     req->nfrags = opdo->has_buffer_count ? opdo->buffer_count : 4; | ||||
|     req->fragsize = audio_buffer_bytes( | ||||
|         qapi_AudiodevOssPerDirectionOptions_base(opdo), as, 23220); | ||||
|  | ||||
|     if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) { | ||||
|         oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt); | ||||
| @@ -307,18 +308,18 @@ static int oss_open(int in, struct oss_params *req, audsettings *as, | ||||
|     } | ||||
|  | ||||
| #ifdef USE_DSP_POLICY | ||||
|     if (policy >= 0) { | ||||
|     if (conf->policy >= 0) { | ||||
|         int version; | ||||
|  | ||||
|         if (!oss_get_version (fd, &version, typ)) { | ||||
|             trace_oss_version(version); | ||||
|  | ||||
|             if (version >= 0x040000) { | ||||
|                 int policy2 = policy; | ||||
|                 if (ioctl(fd, SNDCTL_DSP_POLICY, &policy2)) { | ||||
|                 int policy = conf->policy; | ||||
|                 if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) { | ||||
|                     oss_logerr2 (errno, typ, | ||||
|                                  "Failed to set timing policy to %d\n", | ||||
|                                  policy); | ||||
|                                  conf->policy); | ||||
|                     goto err; | ||||
|                 } | ||||
|                 setfragment = 0; | ||||
| @@ -499,18 +500,19 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     int endianness; | ||||
|     int err; | ||||
|     int fd; | ||||
|     AudioFormat effective_fmt; | ||||
|     audfmt_e effective_fmt; | ||||
|     struct audsettings obt_as; | ||||
|     Audiodev *dev = drv_opaque; | ||||
|     AudiodevOssOptions *oopts = &dev->u.oss; | ||||
|     OSSConf *conf = drv_opaque; | ||||
|  | ||||
|     oss->fd = -1; | ||||
|  | ||||
|     req.fmt = aud_to_ossfmt (as->fmt, as->endianness); | ||||
|     req.freq = as->freq; | ||||
|     req.nchannels = as->nchannels; | ||||
|     req.fragsize = conf->fragsize; | ||||
|     req.nfrags = conf->nfrags; | ||||
|  | ||||
|     if (oss_open(0, &req, as, &obt, &fd, dev)) { | ||||
|     if (oss_open (0, &req, &obt, &fd, conf)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| @@ -537,7 +539,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; | ||||
|  | ||||
|     oss->mmapped = 0; | ||||
|     if (oopts->has_try_mmap && oopts->try_mmap) { | ||||
|     if (conf->try_mmap) { | ||||
|         oss->pcm_buf = mmap ( | ||||
|             NULL, | ||||
|             hw->samples << hw->info.shift, | ||||
| @@ -595,7 +597,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     } | ||||
|  | ||||
|     oss->fd = fd; | ||||
|     oss->dev = dev; | ||||
|     oss->conf = conf; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @@ -603,12 +605,16 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
| { | ||||
|     int trig; | ||||
|     OSSVoiceOut *oss = (OSSVoiceOut *) hw; | ||||
|     AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out; | ||||
|  | ||||
|     switch (cmd) { | ||||
|     case VOICE_ENABLE: | ||||
|         { | ||||
|             bool poll_mode = opdo->try_poll; | ||||
|             va_list ap; | ||||
|             int poll_mode; | ||||
|  | ||||
|             va_start (ap, cmd); | ||||
|             poll_mode = va_arg (ap, int); | ||||
|             va_end (ap); | ||||
|  | ||||
|             ldebug ("enabling voice\n"); | ||||
|             if (poll_mode) { | ||||
| @@ -661,16 +667,18 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | ||||
|     int endianness; | ||||
|     int err; | ||||
|     int fd; | ||||
|     AudioFormat effective_fmt; | ||||
|     audfmt_e effective_fmt; | ||||
|     struct audsettings obt_as; | ||||
|     Audiodev *dev = drv_opaque; | ||||
|     OSSConf *conf = drv_opaque; | ||||
|  | ||||
|     oss->fd = -1; | ||||
|  | ||||
|     req.fmt = aud_to_ossfmt (as->fmt, as->endianness); | ||||
|     req.freq = as->freq; | ||||
|     req.nchannels = as->nchannels; | ||||
|     if (oss_open(1, &req, as, &obt, &fd, dev)) { | ||||
|     req.fragsize = conf->fragsize; | ||||
|     req.nfrags = conf->nfrags; | ||||
|     if (oss_open (1, &req, &obt, &fd, conf)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| @@ -704,7 +712,7 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | ||||
|     } | ||||
|  | ||||
|     oss->fd = fd; | ||||
|     oss->dev = dev; | ||||
|     oss->conf = conf; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @@ -795,12 +803,16 @@ static int oss_read (SWVoiceIn *sw, void *buf, int size) | ||||
| static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||
| { | ||||
|     OSSVoiceIn *oss = (OSSVoiceIn *) hw; | ||||
|     AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out; | ||||
|  | ||||
|     switch (cmd) { | ||||
|     case VOICE_ENABLE: | ||||
|         { | ||||
|             bool poll_mode = opdo->try_poll; | ||||
|             va_list ap; | ||||
|             int poll_mode; | ||||
|  | ||||
|             va_start (ap, cmd); | ||||
|             poll_mode = va_arg (ap, int); | ||||
|             va_end (ap); | ||||
|  | ||||
|             if (poll_mode) { | ||||
|                 oss_poll_in (hw); | ||||
| @@ -820,36 +832,82 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void oss_init_per_direction(AudiodevOssPerDirectionOptions *opdo) | ||||
| static OSSConf glob_conf = { | ||||
|     .try_mmap = 0, | ||||
|     .nfrags = 4, | ||||
|     .fragsize = 4096, | ||||
|     .devpath_out = "/dev/dsp", | ||||
|     .devpath_in = "/dev/dsp", | ||||
|     .exclusive = 0, | ||||
|     .policy = 5 | ||||
| }; | ||||
|  | ||||
| static void *oss_audio_init (void) | ||||
| { | ||||
|     if (!opdo->has_try_poll) { | ||||
|         opdo->try_poll = true; | ||||
|         opdo->has_try_poll = true; | ||||
|     } | ||||
| } | ||||
|     OSSConf *conf = g_malloc(sizeof(OSSConf)); | ||||
|     *conf = glob_conf; | ||||
|  | ||||
| static void *oss_audio_init(Audiodev *dev) | ||||
| { | ||||
|     AudiodevOssOptions *oopts; | ||||
|     assert(dev->driver == AUDIODEV_DRIVER_OSS); | ||||
|  | ||||
|     oopts = &dev->u.oss; | ||||
|     oss_init_per_direction(oopts->in); | ||||
|     oss_init_per_direction(oopts->out); | ||||
|  | ||||
|     if (access(oopts->in->has_dev ? oopts->in->dev : "/dev/dsp", | ||||
|                R_OK | W_OK) < 0 || | ||||
|         access(oopts->out->has_dev ? oopts->out->dev : "/dev/dsp", | ||||
|                R_OK | W_OK) < 0) { | ||||
|     if (access(conf->devpath_in, R_OK | W_OK) < 0 || | ||||
|         access(conf->devpath_out, R_OK | W_OK) < 0) { | ||||
|         g_free(conf); | ||||
|         return NULL; | ||||
|     } | ||||
|     return dev; | ||||
|     return conf; | ||||
| } | ||||
|  | ||||
| static void oss_audio_fini (void *opaque) | ||||
| { | ||||
|     g_free(opaque); | ||||
| } | ||||
|  | ||||
| static struct audio_option oss_options[] = { | ||||
|     { | ||||
|         .name  = "FRAGSIZE", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.fragsize, | ||||
|         .descr = "Fragment size in bytes" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "NFRAGS", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.nfrags, | ||||
|         .descr = "Number of fragments" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "MMAP", | ||||
|         .tag   = AUD_OPT_BOOL, | ||||
|         .valp  = &glob_conf.try_mmap, | ||||
|         .descr = "Try using memory mapped access" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "DAC_DEV", | ||||
|         .tag   = AUD_OPT_STR, | ||||
|         .valp  = &glob_conf.devpath_out, | ||||
|         .descr = "Path to DAC device" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "ADC_DEV", | ||||
|         .tag   = AUD_OPT_STR, | ||||
|         .valp  = &glob_conf.devpath_in, | ||||
|         .descr = "Path to ADC device" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "EXCLUSIVE", | ||||
|         .tag   = AUD_OPT_BOOL, | ||||
|         .valp  = &glob_conf.exclusive, | ||||
|         .descr = "Open device in exclusive mode (vmix won't work)" | ||||
|     }, | ||||
| #ifdef USE_DSP_POLICY | ||||
|     { | ||||
|         .name  = "POLICY", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.policy, | ||||
|         .descr = "Set the timing policy of the device, -1 to use fragment mode", | ||||
|     }, | ||||
| #endif | ||||
|     { /* End of list */ } | ||||
| }; | ||||
|  | ||||
| static struct audio_pcm_ops oss_pcm_ops = { | ||||
|     .init_out = oss_init_out, | ||||
|     .fini_out = oss_fini_out, | ||||
| @@ -867,6 +925,7 @@ static struct audio_pcm_ops oss_pcm_ops = { | ||||
| static struct audio_driver oss_audio_driver = { | ||||
|     .name           = "oss", | ||||
|     .descr          = "OSS http://www.opensound.com", | ||||
|     .options        = oss_options, | ||||
|     .init           = oss_audio_init, | ||||
|     .fini           = oss_audio_fini, | ||||
|     .pcm_ops        = &oss_pcm_ops, | ||||
|   | ||||
							
								
								
									
										164
									
								
								audio/paaudio.c
									
									
									
									
									
								
							
							
						
						
									
										164
									
								
								audio/paaudio.c
									
									
									
									
									
								
							| @@ -2,7 +2,6 @@ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "audio.h" | ||||
| #include "qapi/opts-visitor.h" | ||||
|  | ||||
| #include <pulse/pulseaudio.h> | ||||
|  | ||||
| @@ -11,7 +10,14 @@ | ||||
| #include "audio_pt_int.h" | ||||
|  | ||||
| typedef struct { | ||||
|     Audiodev *dev; | ||||
|     int samples; | ||||
|     char *server; | ||||
|     char *sink; | ||||
|     char *source; | ||||
| } PAConf; | ||||
|  | ||||
| typedef struct { | ||||
|     PAConf conf; | ||||
|     pa_threaded_mainloop *mainloop; | ||||
|     pa_context *context; | ||||
| } paaudio; | ||||
| @@ -26,7 +32,6 @@ typedef struct { | ||||
|     void *pcm_buf; | ||||
|     struct audio_pt pt; | ||||
|     paaudio *g; | ||||
|     int samples; | ||||
| } PAVoiceOut; | ||||
|  | ||||
| typedef struct { | ||||
| @@ -41,7 +46,6 @@ typedef struct { | ||||
|     const void *read_data; | ||||
|     size_t read_index, read_length; | ||||
|     paaudio *g; | ||||
|     int samples; | ||||
| } PAVoiceIn; | ||||
|  | ||||
| static void qpa_audio_fini(void *opaque); | ||||
| @@ -223,7 +227,7 @@ static void *qpa_thread_out (void *arg) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         decr = to_mix = audio_MIN(pa->live, pa->samples >> 5); | ||||
|         decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2); | ||||
|         rpos = pa->rpos; | ||||
|  | ||||
|         if (audio_pt_unlock(&pa->pt, __func__)) { | ||||
| @@ -315,7 +319,7 @@ static void *qpa_thread_in (void *arg) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         incr = to_grab = audio_MIN(pa->dead, pa->samples >> 5); | ||||
|         incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2); | ||||
|         wpos = pa->wpos; | ||||
|  | ||||
|         if (audio_pt_unlock(&pa->pt, __func__)) { | ||||
| @@ -381,21 +385,21 @@ static int qpa_read (SWVoiceIn *sw, void *buf, int len) | ||||
|     return audio_pcm_sw_read (sw, buf, len); | ||||
| } | ||||
|  | ||||
| static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness) | ||||
| static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness) | ||||
| { | ||||
|     int format; | ||||
|  | ||||
|     switch (afmt) { | ||||
|     case AUDIO_FORMAT_S8: | ||||
|     case AUDIO_FORMAT_U8: | ||||
|     case AUD_FMT_S8: | ||||
|     case AUD_FMT_U8: | ||||
|         format = PA_SAMPLE_U8; | ||||
|         break; | ||||
|     case AUDIO_FORMAT_S16: | ||||
|     case AUDIO_FORMAT_U16: | ||||
|     case AUD_FMT_S16: | ||||
|     case AUD_FMT_U16: | ||||
|         format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; | ||||
|         break; | ||||
|     case AUDIO_FORMAT_S32: | ||||
|     case AUDIO_FORMAT_U32: | ||||
|     case AUD_FMT_S32: | ||||
|     case AUD_FMT_U32: | ||||
|         format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; | ||||
|         break; | ||||
|     default: | ||||
| @@ -406,26 +410,26 @@ static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness) | ||||
|     return format; | ||||
| } | ||||
|  | ||||
| static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness) | ||||
| static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness) | ||||
| { | ||||
|     switch (fmt) { | ||||
|     case PA_SAMPLE_U8: | ||||
|         return AUDIO_FORMAT_U8; | ||||
|         return AUD_FMT_U8; | ||||
|     case PA_SAMPLE_S16BE: | ||||
|         *endianness = 1; | ||||
|         return AUDIO_FORMAT_S16; | ||||
|         return AUD_FMT_S16; | ||||
|     case PA_SAMPLE_S16LE: | ||||
|         *endianness = 0; | ||||
|         return AUDIO_FORMAT_S16; | ||||
|         return AUD_FMT_S16; | ||||
|     case PA_SAMPLE_S32BE: | ||||
|         *endianness = 1; | ||||
|         return AUDIO_FORMAT_S32; | ||||
|         return AUD_FMT_S32; | ||||
|     case PA_SAMPLE_S32LE: | ||||
|         *endianness = 0; | ||||
|         return AUDIO_FORMAT_S32; | ||||
|         return AUD_FMT_S32; | ||||
|     default: | ||||
|         dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt); | ||||
|         return AUDIO_FORMAT_U8; | ||||
|         return AUD_FMT_U8; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -542,15 +546,17 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     struct audsettings obt_as = *as; | ||||
|     PAVoiceOut *pa = (PAVoiceOut *) hw; | ||||
|     paaudio *g = pa->g = drv_opaque; | ||||
|     AudiodevPaOptions *popts = &g->dev->u.pa; | ||||
|     AudiodevPaPerDirectionOptions *ppdo = popts->out; | ||||
|  | ||||
|     ss.format = audfmt_to_pa (as->fmt, as->endianness); | ||||
|     ss.channels = as->nchannels; | ||||
|     ss.rate = as->freq; | ||||
|  | ||||
|     ba.tlength = pa_usec_to_bytes(ppdo->latency, &ss); | ||||
|     ba.minreq = -1; | ||||
|     /* | ||||
|      * qemu audio tick runs at 100 Hz (by default), so processing | ||||
|      * data chunks worth 10 ms of sound should be a good fit. | ||||
|      */ | ||||
|     ba.tlength = pa_usec_to_bytes (10 * 1000, &ss); | ||||
|     ba.minreq = pa_usec_to_bytes (5 * 1000, &ss); | ||||
|     ba.maxlength = -1; | ||||
|     ba.prebuf = -1; | ||||
|  | ||||
| @@ -560,7 +566,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|         g, | ||||
|         "qemu", | ||||
|         PA_STREAM_PLAYBACK, | ||||
|         ppdo->has_name ? ppdo->name : NULL, | ||||
|         g->conf.sink, | ||||
|         &ss, | ||||
|         NULL,                   /* channel map */ | ||||
|         &ba,                    /* buffering attributes */ | ||||
| @@ -572,9 +578,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     } | ||||
|  | ||||
|     audio_pcm_init_info (&hw->info, &obt_as); | ||||
|     hw->samples = pa->samples = audio_buffer_samples( | ||||
|         qapi_AudiodevPaPerDirectionOptions_base(ppdo), | ||||
|         &obt_as, ppdo->buffer_length); | ||||
|     hw->samples = g->conf.samples; | ||||
|     pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); | ||||
|     pa->rpos = hw->rpos; | ||||
|     if (!pa->pcm_buf) { | ||||
| @@ -605,32 +609,24 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | ||||
| { | ||||
|     int error; | ||||
|     pa_sample_spec ss; | ||||
|     pa_buffer_attr ba; | ||||
|     struct audsettings obt_as = *as; | ||||
|     PAVoiceIn *pa = (PAVoiceIn *) hw; | ||||
|     paaudio *g = pa->g = drv_opaque; | ||||
|     AudiodevPaOptions *popts = &g->dev->u.pa; | ||||
|     AudiodevPaPerDirectionOptions *ppdo = popts->in; | ||||
|  | ||||
|     ss.format = audfmt_to_pa (as->fmt, as->endianness); | ||||
|     ss.channels = as->nchannels; | ||||
|     ss.rate = as->freq; | ||||
|  | ||||
|     ba.fragsize = pa_usec_to_bytes(ppdo->latency, &ss); | ||||
|     ba.maxlength = -1; | ||||
|     ba.minreq = -1; | ||||
|     ba.prebuf = -1; | ||||
|  | ||||
|     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); | ||||
|  | ||||
|     pa->stream = qpa_simple_new ( | ||||
|         g, | ||||
|         "qemu", | ||||
|         PA_STREAM_RECORD, | ||||
|         ppdo->has_name ? ppdo->name : NULL, | ||||
|         g->conf.source, | ||||
|         &ss, | ||||
|         NULL,                   /* channel map */ | ||||
|         &ba,                    /* buffering attributes */ | ||||
|         NULL,                   /* buffering attributes */ | ||||
|         &error | ||||
|         ); | ||||
|     if (!pa->stream) { | ||||
| @@ -639,9 +635,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | ||||
|     } | ||||
|  | ||||
|     audio_pcm_init_info (&hw->info, &obt_as); | ||||
|     hw->samples = pa->samples = audio_buffer_samples( | ||||
|         qapi_AudiodevPaPerDirectionOptions_base(ppdo), | ||||
|         &obt_as, ppdo->buffer_length); | ||||
|     hw->samples = g->conf.samples; | ||||
|     pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); | ||||
|     pa->wpos = hw->wpos; | ||||
|     if (!pa->pcm_buf) { | ||||
| @@ -813,54 +807,15 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int qpa_validate_per_direction_opts(Audiodev *dev, | ||||
|                                            AudiodevPaPerDirectionOptions *pdo) | ||||
| /* common */ | ||||
| static PAConf glob_conf = { | ||||
|     .samples = 4096, | ||||
| }; | ||||
|  | ||||
| static void *qpa_audio_init (void) | ||||
| { | ||||
|     if (!pdo->has_buffer_length) { | ||||
|         pdo->has_buffer_length = true; | ||||
|         pdo->buffer_length = 46440; | ||||
|     } | ||||
|     if (!pdo->has_latency) { | ||||
|         pdo->has_latency = true; | ||||
|         pdo->latency = 15000; | ||||
|     } | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| static void *qpa_audio_init(Audiodev *dev) | ||||
| { | ||||
|     paaudio *g; | ||||
|     AudiodevPaOptions *popts = &dev->u.pa; | ||||
|     const char *server; | ||||
|  | ||||
|     if (!popts->has_server) { | ||||
|         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; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     assert(dev->driver == AUDIODEV_DRIVER_PA); | ||||
|  | ||||
|     g = g_malloc(sizeof(paaudio)); | ||||
|     server = popts->has_server ? popts->server : NULL; | ||||
|  | ||||
|     if (!qpa_validate_per_direction_opts(dev, popts->in)) { | ||||
|         goto fail; | ||||
|     } | ||||
|     if (!qpa_validate_per_direction_opts(dev, popts->out)) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     g->dev = dev; | ||||
|     paaudio *g = g_malloc(sizeof(paaudio)); | ||||
|     g->conf = glob_conf; | ||||
|     g->mainloop = NULL; | ||||
|     g->context = NULL; | ||||
|  | ||||
| @@ -870,14 +825,14 @@ static void *qpa_audio_init(Audiodev *dev) | ||||
|     } | ||||
|  | ||||
|     g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), | ||||
|                                  server); | ||||
|                                  g->conf.server); | ||||
|     if (!g->context) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     pa_context_set_state_callback (g->context, context_state_cb, g); | ||||
|  | ||||
|     if (pa_context_connect(g->context, server, 0, NULL) < 0) { | ||||
|     if (pa_context_connect (g->context, g->conf.server, 0, NULL) < 0) { | ||||
|         qpa_logerr (pa_context_errno (g->context), | ||||
|                     "pa_context_connect() failed\n"); | ||||
|         goto fail; | ||||
| @@ -940,6 +895,34 @@ static void qpa_audio_fini (void *opaque) | ||||
|     g_free(g); | ||||
| } | ||||
|  | ||||
| struct audio_option qpa_options[] = { | ||||
|     { | ||||
|         .name  = "SAMPLES", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.samples, | ||||
|         .descr = "buffer size in samples" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "SERVER", | ||||
|         .tag   = AUD_OPT_STR, | ||||
|         .valp  = &glob_conf.server, | ||||
|         .descr = "server address" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "SINK", | ||||
|         .tag   = AUD_OPT_STR, | ||||
|         .valp  = &glob_conf.sink, | ||||
|         .descr = "sink device name" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "SOURCE", | ||||
|         .tag   = AUD_OPT_STR, | ||||
|         .valp  = &glob_conf.source, | ||||
|         .descr = "source device name" | ||||
|     }, | ||||
|     { /* End of list */ } | ||||
| }; | ||||
|  | ||||
| static struct audio_pcm_ops qpa_pcm_ops = { | ||||
|     .init_out = qpa_init_out, | ||||
|     .fini_out = qpa_fini_out, | ||||
| @@ -957,6 +940,7 @@ static struct audio_pcm_ops qpa_pcm_ops = { | ||||
| static struct audio_driver pa_audio_driver = { | ||||
|     .name           = "pa", | ||||
|     .descr          = "http://www.pulseaudio.org/", | ||||
|     .options        = qpa_options, | ||||
|     .init           = qpa_audio_init, | ||||
|     .fini           = qpa_audio_fini, | ||||
|     .pcm_ops        = &qpa_pcm_ops, | ||||
|   | ||||
							
								
								
									
										236
									
								
								audio/sdlaudio.c
									
									
									
									
									
								
							
							
						
						
									
										236
									
								
								audio/sdlaudio.c
									
									
									
									
									
								
							| @@ -38,17 +38,31 @@ | ||||
| #define AUDIO_CAP "sdl" | ||||
| #include "audio_int.h" | ||||
|  | ||||
| #define USE_SEMAPHORE (SDL_MAJOR_VERSION < 2) | ||||
|  | ||||
| typedef struct SDLVoiceOut { | ||||
|     HWVoiceOut hw; | ||||
|     int live; | ||||
| #if USE_SEMAPHORE | ||||
|     int rpos; | ||||
| #endif | ||||
|     int decr; | ||||
| } SDLVoiceOut; | ||||
|  | ||||
| static struct { | ||||
|     int nb_samples; | ||||
| } conf = { | ||||
|     .nb_samples = 1024 | ||||
| }; | ||||
|  | ||||
| static struct SDLAudioState { | ||||
|     int exit; | ||||
| #if USE_SEMAPHORE | ||||
|     SDL_mutex *mutex; | ||||
|     SDL_sem *sem; | ||||
| #endif | ||||
|     int initialized; | ||||
|     bool driver_created; | ||||
|     Audiodev *dev; | ||||
| } glob_sdl; | ||||
| typedef struct SDLAudioState SDLAudioState; | ||||
|  | ||||
| @@ -63,19 +77,79 @@ static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...) | ||||
|     AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ()); | ||||
| } | ||||
|  | ||||
| static int aud_to_sdlfmt (AudioFormat fmt) | ||||
| static int sdl_lock (SDLAudioState *s, const char *forfn) | ||||
| { | ||||
| #if USE_SEMAPHORE | ||||
|     if (SDL_LockMutex (s->mutex)) { | ||||
|         sdl_logerr ("SDL_LockMutex for %s failed\n", forfn); | ||||
|         return -1; | ||||
|     } | ||||
| #else | ||||
|     SDL_LockAudio(); | ||||
| #endif | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int sdl_unlock (SDLAudioState *s, const char *forfn) | ||||
| { | ||||
| #if USE_SEMAPHORE | ||||
|     if (SDL_UnlockMutex (s->mutex)) { | ||||
|         sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn); | ||||
|         return -1; | ||||
|     } | ||||
| #else | ||||
|     SDL_UnlockAudio(); | ||||
| #endif | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int sdl_post (SDLAudioState *s, const char *forfn) | ||||
| { | ||||
| #if USE_SEMAPHORE | ||||
|     if (SDL_SemPost (s->sem)) { | ||||
|         sdl_logerr ("SDL_SemPost for %s failed\n", forfn); | ||||
|         return -1; | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| #if USE_SEMAPHORE | ||||
| static int sdl_wait (SDLAudioState *s, const char *forfn) | ||||
| { | ||||
|     if (SDL_SemWait (s->sem)) { | ||||
|         sdl_logerr ("SDL_SemWait for %s failed\n", forfn); | ||||
|         return -1; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn) | ||||
| { | ||||
|     if (sdl_unlock (s, forfn)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return sdl_post (s, forfn); | ||||
| } | ||||
|  | ||||
| static int aud_to_sdlfmt (audfmt_e fmt) | ||||
| { | ||||
|     switch (fmt) { | ||||
|     case AUDIO_FORMAT_S8: | ||||
|     case AUD_FMT_S8: | ||||
|         return AUDIO_S8; | ||||
|  | ||||
|     case AUDIO_FORMAT_U8: | ||||
|     case AUD_FMT_U8: | ||||
|         return AUDIO_U8; | ||||
|  | ||||
|     case AUDIO_FORMAT_S16: | ||||
|     case AUD_FMT_S16: | ||||
|         return AUDIO_S16LSB; | ||||
|  | ||||
|     case AUDIO_FORMAT_U16: | ||||
|     case AUD_FMT_U16: | ||||
|         return AUDIO_U16LSB; | ||||
|  | ||||
|     default: | ||||
| @@ -87,37 +161,37 @@ static int aud_to_sdlfmt (AudioFormat fmt) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness) | ||||
| static int sdl_to_audfmt(int sdlfmt, audfmt_e *fmt, int *endianness) | ||||
| { | ||||
|     switch (sdlfmt) { | ||||
|     case AUDIO_S8: | ||||
|         *endianness = 0; | ||||
|         *fmt = AUDIO_FORMAT_S8; | ||||
|         *fmt = AUD_FMT_S8; | ||||
|         break; | ||||
|  | ||||
|     case AUDIO_U8: | ||||
|         *endianness = 0; | ||||
|         *fmt = AUDIO_FORMAT_U8; | ||||
|         *fmt = AUD_FMT_U8; | ||||
|         break; | ||||
|  | ||||
|     case AUDIO_S16LSB: | ||||
|         *endianness = 0; | ||||
|         *fmt = AUDIO_FORMAT_S16; | ||||
|         *fmt = AUD_FMT_S16; | ||||
|         break; | ||||
|  | ||||
|     case AUDIO_U16LSB: | ||||
|         *endianness = 0; | ||||
|         *fmt = AUDIO_FORMAT_U16; | ||||
|         *fmt = AUD_FMT_U16; | ||||
|         break; | ||||
|  | ||||
|     case AUDIO_S16MSB: | ||||
|         *endianness = 1; | ||||
|         *fmt = AUDIO_FORMAT_S16; | ||||
|         *fmt = AUD_FMT_S16; | ||||
|         break; | ||||
|  | ||||
|     case AUDIO_U16MSB: | ||||
|         *endianness = 1; | ||||
|         *fmt = AUDIO_FORMAT_U16; | ||||
|         *fmt = AUD_FMT_U16; | ||||
|         break; | ||||
|  | ||||
|     default: | ||||
| @@ -169,9 +243,9 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) | ||||
| static void sdl_close (SDLAudioState *s) | ||||
| { | ||||
|     if (s->initialized) { | ||||
|         SDL_LockAudio(); | ||||
|         sdl_lock (s, "sdl_close"); | ||||
|         s->exit = 1; | ||||
|         SDL_UnlockAudio(); | ||||
|         sdl_unlock_and_post (s, "sdl_close"); | ||||
|         SDL_PauseAudio (1); | ||||
|         SDL_CloseAudio (); | ||||
|         s->initialized = 0; | ||||
| @@ -184,36 +258,76 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len) | ||||
|     SDLAudioState *s = &glob_sdl; | ||||
|     HWVoiceOut *hw = &sdl->hw; | ||||
|     int samples = len >> hw->info.shift; | ||||
|     int to_mix, decr; | ||||
|  | ||||
|     if (s->exit || !sdl->live) { | ||||
|     if (s->exit) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* dolog ("in callback samples=%d live=%d\n", samples, sdl->live); */ | ||||
|     while (samples) { | ||||
|         int to_mix, decr; | ||||
|  | ||||
|     to_mix = audio_MIN(samples, sdl->live); | ||||
|     decr = to_mix; | ||||
|     while (to_mix) { | ||||
|         int chunk = audio_MIN(to_mix, hw->samples - hw->rpos); | ||||
|         struct st_sample *src = hw->mix_buf + hw->rpos; | ||||
|         /* dolog ("in callback samples=%d\n", samples); */ | ||||
| #if USE_SEMAPHORE | ||||
|         sdl_wait (s, "sdl_callback"); | ||||
|         if (s->exit) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ | ||||
|         hw->clip(buf, src, chunk); | ||||
|         hw->rpos = (hw->rpos + chunk) % hw->samples; | ||||
|         to_mix -= chunk; | ||||
|         buf += chunk << hw->info.shift; | ||||
|         if (sdl_lock (s, "sdl_callback")) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (audio_bug(__func__, sdl->live < 0 || sdl->live > hw->samples)) { | ||||
|             dolog ("sdl->live=%d hw->samples=%d\n", | ||||
|                    sdl->live, hw->samples); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (!sdl->live) { | ||||
|             goto again; | ||||
|         } | ||||
| #else | ||||
|         if (s->exit || !sdl->live) { | ||||
|             break; | ||||
|         } | ||||
| #endif | ||||
|  | ||||
|         /* dolog ("in callback live=%d\n", live); */ | ||||
|         to_mix = audio_MIN (samples, sdl->live); | ||||
|         decr = to_mix; | ||||
|         while (to_mix) { | ||||
|             int chunk = audio_MIN (to_mix, hw->samples - hw->rpos); | ||||
|             struct st_sample *src = hw->mix_buf + hw->rpos; | ||||
|  | ||||
|             /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ | ||||
|             hw->clip (buf, src, chunk); | ||||
| #if USE_SEMAPHORE | ||||
|             sdl->rpos = (sdl->rpos + chunk) % hw->samples; | ||||
| #else | ||||
|             hw->rpos = (hw->rpos + chunk) % hw->samples; | ||||
| #endif | ||||
|             to_mix -= chunk; | ||||
|             buf += chunk << hw->info.shift; | ||||
|         } | ||||
|         samples -= decr; | ||||
|         sdl->live -= decr; | ||||
|         sdl->decr += decr; | ||||
|  | ||||
| #if USE_SEMAPHORE | ||||
|     again: | ||||
|         if (sdl_unlock (s, "sdl_callback")) { | ||||
|             return; | ||||
|         } | ||||
| #endif | ||||
|     } | ||||
|     samples -= decr; | ||||
|     sdl->live -= decr; | ||||
|     sdl->decr += decr; | ||||
|  | ||||
|     /* dolog ("done len=%d\n", len); */ | ||||
|  | ||||
| #if (SDL_MAJOR_VERSION >= 2) | ||||
|     /* SDL2 does not clear the remaining buffer for us, so do it on our own */ | ||||
|     if (samples) { | ||||
|         memset(buf, 0, samples << hw->info.shift); | ||||
|     } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) | ||||
| @@ -225,8 +339,11 @@ static int sdl_run_out (HWVoiceOut *hw, int live) | ||||
| { | ||||
|     int decr; | ||||
|     SDLVoiceOut *sdl = (SDLVoiceOut *) hw; | ||||
|     SDLAudioState *s = &glob_sdl; | ||||
|  | ||||
|     SDL_LockAudio(); | ||||
|     if (sdl_lock (s, "sdl_run_out")) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if (sdl->decr > live) { | ||||
|         ldebug ("sdl->decr %d live %d sdl->live %d\n", | ||||
| @@ -238,10 +355,19 @@ static int sdl_run_out (HWVoiceOut *hw, int live) | ||||
|     decr = audio_MIN (sdl->decr, live); | ||||
|     sdl->decr -= decr; | ||||
|  | ||||
| #if USE_SEMAPHORE | ||||
|     sdl->live = live - decr; | ||||
|     hw->rpos = sdl->rpos; | ||||
| #else | ||||
|     sdl->live = live; | ||||
| #endif | ||||
|  | ||||
|     SDL_UnlockAudio(); | ||||
|  | ||||
|     if (sdl->live > 0) { | ||||
|         sdl_unlock_and_post (s, "sdl_run_out"); | ||||
|     } | ||||
|     else { | ||||
|         sdl_unlock (s, "sdl_run_out"); | ||||
|     } | ||||
|     return decr; | ||||
| } | ||||
|  | ||||
| @@ -260,13 +386,13 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     SDL_AudioSpec req, obt; | ||||
|     int endianness; | ||||
|     int err; | ||||
|     AudioFormat effective_fmt; | ||||
|     audfmt_e effective_fmt; | ||||
|     struct audsettings obt_as; | ||||
|  | ||||
|     req.freq = as->freq; | ||||
|     req.format = aud_to_sdlfmt (as->fmt); | ||||
|     req.channels = as->nchannels; | ||||
|     req.samples = audio_buffer_samples(s->dev->u.sdl.out, as, 11610); | ||||
|     req.samples = conf.nb_samples; | ||||
|     req.callback = sdl_callback; | ||||
|     req.userdata = sdl; | ||||
|  | ||||
| @@ -310,7 +436,7 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void *sdl_audio_init(Audiodev *dev) | ||||
| static void *sdl_audio_init (void) | ||||
| { | ||||
|     SDLAudioState *s = &glob_sdl; | ||||
|     if (s->driver_created) { | ||||
| @@ -323,8 +449,24 @@ static void *sdl_audio_init(Audiodev *dev) | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
| #if USE_SEMAPHORE | ||||
|     s->mutex = SDL_CreateMutex (); | ||||
|     if (!s->mutex) { | ||||
|         sdl_logerr ("Failed to create SDL mutex\n"); | ||||
|         SDL_QuitSubSystem (SDL_INIT_AUDIO); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     s->sem = SDL_CreateSemaphore (0); | ||||
|     if (!s->sem) { | ||||
|         sdl_logerr ("Failed to create SDL semaphore\n"); | ||||
|         SDL_DestroyMutex (s->mutex); | ||||
|         SDL_QuitSubSystem (SDL_INIT_AUDIO); | ||||
|         return NULL; | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     s->driver_created = true; | ||||
|     s->dev = dev; | ||||
|     return s; | ||||
| } | ||||
|  | ||||
| @@ -332,11 +474,24 @@ static void sdl_audio_fini (void *opaque) | ||||
| { | ||||
|     SDLAudioState *s = opaque; | ||||
|     sdl_close (s); | ||||
| #if USE_SEMAPHORE | ||||
|     SDL_DestroySemaphore (s->sem); | ||||
|     SDL_DestroyMutex (s->mutex); | ||||
| #endif | ||||
|     SDL_QuitSubSystem (SDL_INIT_AUDIO); | ||||
|     s->driver_created = false; | ||||
|     s->dev = NULL; | ||||
| } | ||||
|  | ||||
| static struct audio_option sdl_options[] = { | ||||
|     { | ||||
|         .name  = "SAMPLES", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &conf.nb_samples, | ||||
|         .descr = "Size of SDL buffer in samples" | ||||
|     }, | ||||
|     { /* End of list */ } | ||||
| }; | ||||
|  | ||||
| static struct audio_pcm_ops sdl_pcm_ops = { | ||||
|     .init_out = sdl_init_out, | ||||
|     .fini_out = sdl_fini_out, | ||||
| @@ -348,6 +503,7 @@ static struct audio_pcm_ops sdl_pcm_ops = { | ||||
| static struct audio_driver sdl_audio_driver = { | ||||
|     .name           = "sdl", | ||||
|     .descr          = "SDL http://www.libsdl.org", | ||||
|     .options        = sdl_options, | ||||
|     .init           = sdl_audio_init, | ||||
|     .fini           = sdl_audio_fini, | ||||
|     .pcm_ops        = &sdl_pcm_ops, | ||||
|   | ||||
| @@ -77,7 +77,7 @@ static const SpiceRecordInterface record_sif = { | ||||
|     .base.minor_version = SPICE_INTERFACE_RECORD_MINOR, | ||||
| }; | ||||
|  | ||||
| static void *spice_audio_init(Audiodev *dev) | ||||
| static void *spice_audio_init (void) | ||||
| { | ||||
|     if (!using_spice) { | ||||
|         return NULL; | ||||
| @@ -130,7 +130,7 @@ static int line_out_init(HWVoiceOut *hw, struct audsettings *as, | ||||
|     settings.freq       = SPICE_INTERFACE_PLAYBACK_FREQ; | ||||
| #endif | ||||
|     settings.nchannels  = SPICE_INTERFACE_PLAYBACK_CHAN; | ||||
|     settings.fmt        = AUDIO_FORMAT_S16; | ||||
|     settings.fmt        = AUD_FMT_S16; | ||||
|     settings.endianness = AUDIO_HOST_ENDIANNESS; | ||||
|  | ||||
|     audio_pcm_init_info (&hw->info, &settings); | ||||
| @@ -258,7 +258,7 @@ static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | ||||
|     settings.freq       = SPICE_INTERFACE_RECORD_FREQ; | ||||
| #endif | ||||
|     settings.nchannels  = SPICE_INTERFACE_RECORD_CHAN; | ||||
|     settings.fmt        = AUDIO_FORMAT_S16; | ||||
|     settings.fmt        = AUD_FMT_S16; | ||||
|     settings.endianness = AUDIO_HOST_ENDIANNESS; | ||||
|  | ||||
|     audio_pcm_init_info (&hw->info, &settings); | ||||
| @@ -373,6 +373,10 @@ static int line_in_ctl (HWVoiceIn *hw, int cmd, ...) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static struct audio_option audio_options[] = { | ||||
|     { /* end of list */ }, | ||||
| }; | ||||
|  | ||||
| static struct audio_pcm_ops audio_callbacks = { | ||||
|     .init_out = line_out_init, | ||||
|     .fini_out = line_out_fini, | ||||
| @@ -390,6 +394,7 @@ static struct audio_pcm_ops audio_callbacks = { | ||||
| static struct audio_driver spice_audio_driver = { | ||||
|     .name           = "spice", | ||||
|     .descr          = "spice audio driver", | ||||
|     .options        = audio_options, | ||||
|     .init           = spice_audio_init, | ||||
|     .fini           = spice_audio_fini, | ||||
|     .pcm_ops        = &audio_callbacks, | ||||
|   | ||||
| @@ -15,8 +15,3 @@ alsa_no_frames(int state) "No frames available and ALSA state is %d" | ||||
| # audio/ossaudio.c | ||||
| oss_version(int version) "OSS version = 0x%x" | ||||
| oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d" | ||||
|  | ||||
| # audio/audio.c | ||||
| audio_timer_start(int interval) "interval %d ms" | ||||
| audio_timer_stop(void) "" | ||||
| audio_timer_delayed(int interval) "interval %d ms" | ||||
|   | ||||
| @@ -24,7 +24,6 @@ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu/host-utils.h" | ||||
| #include "qemu/timer.h" | ||||
| #include "qapi/opts-visitor.h" | ||||
| #include "audio.h" | ||||
|  | ||||
| #define AUDIO_CAP "wav" | ||||
| @@ -38,6 +37,11 @@ typedef struct WAVVoiceOut { | ||||
|     int total_samples; | ||||
| } WAVVoiceOut; | ||||
|  | ||||
| typedef struct { | ||||
|     struct audsettings settings; | ||||
|     const char *wav_path; | ||||
| } WAVConf; | ||||
|  | ||||
| static int wav_run_out (HWVoiceOut *hw, int live) | ||||
| { | ||||
|     WAVVoiceOut *wav = (WAVVoiceOut *) hw; | ||||
| @@ -108,30 +112,25 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|         0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, | ||||
|         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 | ||||
|     }; | ||||
|     Audiodev *dev = drv_opaque; | ||||
|     AudiodevWavOptions *wopts = &dev->u.wav; | ||||
|     struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out); | ||||
|     const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav"; | ||||
|     WAVConf *conf = drv_opaque; | ||||
|     struct audsettings wav_as = conf->settings; | ||||
|  | ||||
|     stereo = wav_as.nchannels == 2; | ||||
|     switch (wav_as.fmt) { | ||||
|     case AUDIO_FORMAT_S8: | ||||
|     case AUDIO_FORMAT_U8: | ||||
|     case AUD_FMT_S8: | ||||
|     case AUD_FMT_U8: | ||||
|         bits16 = 0; | ||||
|         break; | ||||
|  | ||||
|     case AUDIO_FORMAT_S16: | ||||
|     case AUDIO_FORMAT_U16: | ||||
|     case AUD_FMT_S16: | ||||
|     case AUD_FMT_U16: | ||||
|         bits16 = 1; | ||||
|         break; | ||||
|  | ||||
|     case AUDIO_FORMAT_S32: | ||||
|     case AUDIO_FORMAT_U32: | ||||
|     case AUD_FMT_S32: | ||||
|     case AUD_FMT_U32: | ||||
|         dolog ("WAVE files can not handle 32bit formats\n"); | ||||
|         return -1; | ||||
|  | ||||
|     default: | ||||
|         abort(); | ||||
|     } | ||||
|  | ||||
|     hdr[34] = bits16 ? 0x10 : 0x08; | ||||
| @@ -152,10 +151,10 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as, | ||||
|     le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4); | ||||
|     le_store (hdr + 32, 1 << (bits16 + stereo), 2); | ||||
|  | ||||
|     wav->f = fopen(wav_path, "wb"); | ||||
|     wav->f = fopen (conf->wav_path, "wb"); | ||||
|     if (!wav->f) { | ||||
|         dolog ("Failed to open wave file `%s'\nReason: %s\n", | ||||
|                wav_path, strerror(errno)); | ||||
|                conf->wav_path, strerror (errno)); | ||||
|         g_free (wav->pcm_buf); | ||||
|         wav->pcm_buf = NULL; | ||||
|         return -1; | ||||
| @@ -223,17 +222,54 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void *wav_audio_init(Audiodev *dev) | ||||
| static WAVConf glob_conf = { | ||||
|     .settings.freq      = 44100, | ||||
|     .settings.nchannels = 2, | ||||
|     .settings.fmt       = AUD_FMT_S16, | ||||
|     .wav_path           = "qemu.wav" | ||||
| }; | ||||
|  | ||||
| static void *wav_audio_init (void) | ||||
| { | ||||
|     assert(dev->driver == AUDIODEV_DRIVER_WAV); | ||||
|     return dev; | ||||
|     WAVConf *conf = g_malloc(sizeof(WAVConf)); | ||||
|     *conf = glob_conf; | ||||
|     return conf; | ||||
| } | ||||
|  | ||||
| static void wav_audio_fini (void *opaque) | ||||
| { | ||||
|     ldebug ("wav_fini"); | ||||
|     g_free(opaque); | ||||
| } | ||||
|  | ||||
| static struct audio_option wav_options[] = { | ||||
|     { | ||||
|         .name  = "FREQUENCY", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.settings.freq, | ||||
|         .descr = "Frequency" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "FORMAT", | ||||
|         .tag   = AUD_OPT_FMT, | ||||
|         .valp  = &glob_conf.settings.fmt, | ||||
|         .descr = "Format" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "DAC_FIXED_CHANNELS", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &glob_conf.settings.nchannels, | ||||
|         .descr = "Number of channels (1 - mono, 2 - stereo)" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "PATH", | ||||
|         .tag   = AUD_OPT_STR, | ||||
|         .valp  = &glob_conf.wav_path, | ||||
|         .descr = "Path to wave file" | ||||
|     }, | ||||
|     { /* End of list */ } | ||||
| }; | ||||
|  | ||||
| static struct audio_pcm_ops wav_pcm_ops = { | ||||
|     .init_out = wav_init_out, | ||||
|     .fini_out = wav_fini_out, | ||||
| @@ -245,6 +281,7 @@ static struct audio_pcm_ops wav_pcm_ops = { | ||||
| static struct audio_driver wav_audio_driver = { | ||||
|     .name           = "wav", | ||||
|     .descr          = "WAV renderer http://wikipedia.org/wiki/WAV", | ||||
|     .options        = wav_options, | ||||
|     .init           = wav_audio_init, | ||||
|     .fini           = wav_audio_fini, | ||||
|     .pcm_ops        = &wav_pcm_ops, | ||||
|   | ||||
| @@ -38,29 +38,30 @@ static void wav_destroy (void *opaque) | ||||
|     uint8_t dlen[4]; | ||||
|     uint32_t datalen = wav->bytes; | ||||
|     uint32_t rifflen = datalen + 36; | ||||
|     Monitor *mon = cur_mon; | ||||
|  | ||||
|     if (wav->f) { | ||||
|         le_store (rlen, rifflen, 4); | ||||
|         le_store (dlen, datalen, 4); | ||||
|  | ||||
|         if (fseek (wav->f, 4, SEEK_SET)) { | ||||
|             error_report("wav_destroy: rlen fseek failed: %s", | ||||
|                          strerror(errno)); | ||||
|             monitor_printf (mon, "wav_destroy: rlen fseek failed\nReason: %s\n", | ||||
|                             strerror (errno)); | ||||
|             goto doclose; | ||||
|         } | ||||
|         if (fwrite (rlen, 4, 1, wav->f) != 1) { | ||||
|             error_report("wav_destroy: rlen fwrite failed: %s", | ||||
|                          strerror(errno)); | ||||
|             monitor_printf (mon, "wav_destroy: rlen fwrite failed\nReason %s\n", | ||||
|                             strerror (errno)); | ||||
|             goto doclose; | ||||
|         } | ||||
|         if (fseek (wav->f, 32, SEEK_CUR)) { | ||||
|             error_report("wav_destroy: dlen fseek failed: %s", | ||||
|                          strerror(errno)); | ||||
|             monitor_printf (mon, "wav_destroy: dlen fseek failed\nReason %s\n", | ||||
|                             strerror (errno)); | ||||
|             goto doclose; | ||||
|         } | ||||
|         if (fwrite (dlen, 1, 4, wav->f) != 4) { | ||||
|             error_report("wav_destroy: dlen fwrite failed: %s", | ||||
|                          strerror(errno)); | ||||
|             monitor_printf (mon, "wav_destroy: dlen fwrite failed\nReason %s\n", | ||||
|                             strerror (errno)); | ||||
|             goto doclose; | ||||
|         } | ||||
|     doclose: | ||||
| @@ -77,7 +78,8 @@ static void wav_capture (void *opaque, void *buf, int size) | ||||
|     WAVState *wav = opaque; | ||||
|  | ||||
|     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; | ||||
| } | ||||
| @@ -108,6 +110,7 @@ static struct capture_ops wav_capture_ops = { | ||||
| int wav_start_capture (CaptureState *s, const char *path, int freq, | ||||
|                        int bits, int nchannels) | ||||
| { | ||||
|     Monitor *mon = cur_mon; | ||||
|     WAVState *wav; | ||||
|     uint8_t hdr[] = { | ||||
|         0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, | ||||
| @@ -121,13 +124,13 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, | ||||
|     CaptureVoiceOut *cap; | ||||
|  | ||||
|     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; | ||||
|     } | ||||
|  | ||||
|     if (nchannels != 1 && nchannels != 2) { | ||||
|         error_report("incorrect channel count %d, must be 1 or 2", | ||||
|                      nchannels); | ||||
|         monitor_printf (mon, "incorrect channel count %d, must be 1 or 2\n", | ||||
|                         nchannels); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| @@ -136,7 +139,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, | ||||
|  | ||||
|     as.freq = freq; | ||||
|     as.nchannels = 1 << stereo; | ||||
|     as.fmt = bits16 ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8; | ||||
|     as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8; | ||||
|     as.endianness = 0; | ||||
|  | ||||
|     ops.notify = wav_notify; | ||||
| @@ -155,8 +158,8 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, | ||||
|  | ||||
|     wav->f = fopen (path, "wb"); | ||||
|     if (!wav->f) { | ||||
|         error_report("Failed to open wave file `%s': %s", | ||||
|                      path, strerror(errno)); | ||||
|         monitor_printf (mon, "Failed to open wave file `%s'\nReason: %s\n", | ||||
|                         path, strerror (errno)); | ||||
|         g_free (wav); | ||||
|         return -1; | ||||
|     } | ||||
| @@ -167,13 +170,14 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, | ||||
|     wav->freq = freq; | ||||
|  | ||||
|     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; | ||||
|     } | ||||
|  | ||||
|     cap = AUD_add_capture (&as, &ops, wav); | ||||
|     if (!cap) { | ||||
|         error_report("Failed to add audio capture"); | ||||
|         monitor_printf (mon, "Failed to add audio capture\n"); | ||||
|         goto error_free; | ||||
|     } | ||||
|  | ||||
| @@ -185,7 +189,8 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, | ||||
| error_free: | ||||
|     g_free (wav->path); | ||||
|     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); | ||||
|     return -1; | ||||
|   | ||||
| @@ -1,7 +0,0 @@ | ||||
| authz-obj-y += base.o | ||||
| authz-obj-y += simple.o | ||||
| authz-obj-y += list.o | ||||
| authz-obj-y += listfile.o | ||||
| authz-obj-$(CONFIG_AUTH_PAM) += pamacct.o | ||||
|  | ||||
| pamacct.o-libs = -lpam | ||||
							
								
								
									
										82
									
								
								authz/base.c
									
									
									
									
									
								
							
							
						
						
									
										82
									
								
								authz/base.c
									
									
									
									
									
								
							| @@ -1,82 +0,0 @@ | ||||
| /* | ||||
|  * QEMU authorization framework base class | ||||
|  * | ||||
|  * Copyright (c) 2018 Red Hat, Inc. | ||||
|  * | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public | ||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "authz/base.h" | ||||
| #include "authz/trace.h" | ||||
|  | ||||
| bool qauthz_is_allowed(QAuthZ *authz, | ||||
|                        const char *identity, | ||||
|                        Error **errp) | ||||
| { | ||||
|     QAuthZClass *cls = QAUTHZ_GET_CLASS(authz); | ||||
|     bool allowed; | ||||
|  | ||||
|     allowed = cls->is_allowed(authz, identity, errp); | ||||
|     trace_qauthz_is_allowed(authz, identity, allowed); | ||||
|  | ||||
|     return allowed; | ||||
| } | ||||
|  | ||||
|  | ||||
| bool qauthz_is_allowed_by_id(const char *authzid, | ||||
|                              const char *identity, | ||||
|                              Error **errp) | ||||
| { | ||||
|     QAuthZ *authz; | ||||
|     Object *obj; | ||||
|     Object *container; | ||||
|  | ||||
|     container = object_get_objects_root(); | ||||
|     obj = object_resolve_path_component(container, | ||||
|                                         authzid); | ||||
|     if (!obj) { | ||||
|         error_setg(errp, "Cannot find QAuthZ object ID %s", | ||||
|                    authzid); | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (!object_dynamic_cast(obj, TYPE_QAUTHZ)) { | ||||
|         error_setg(errp, "Object '%s' is not a QAuthZ subclass", | ||||
|                    authzid); | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     authz = QAUTHZ(obj); | ||||
|  | ||||
|     return qauthz_is_allowed(authz, identity, errp); | ||||
| } | ||||
|  | ||||
|  | ||||
| static const TypeInfo authz_info = { | ||||
|     .parent = TYPE_OBJECT, | ||||
|     .name = TYPE_QAUTHZ, | ||||
|     .instance_size = sizeof(QAuthZ), | ||||
|     .class_size = sizeof(QAuthZClass), | ||||
|     .abstract = true, | ||||
| }; | ||||
|  | ||||
| static void qauthz_register_types(void) | ||||
| { | ||||
|     type_register_static(&authz_info); | ||||
| } | ||||
|  | ||||
| type_init(qauthz_register_types) | ||||
|  | ||||
							
								
								
									
										271
									
								
								authz/list.c
									
									
									
									
									
								
							
							
						
						
									
										271
									
								
								authz/list.c
									
									
									
									
									
								
							| @@ -1,271 +0,0 @@ | ||||
| /* | ||||
|  * QEMU access control list authorization driver | ||||
|  * | ||||
|  * Copyright (c) 2018 Red Hat, Inc. | ||||
|  * | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public | ||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "authz/list.h" | ||||
| #include "authz/trace.h" | ||||
| #include "qom/object_interfaces.h" | ||||
| #include "qapi/qapi-visit-authz.h" | ||||
|  | ||||
| static bool qauthz_list_is_allowed(QAuthZ *authz, | ||||
|                                    const char *identity, | ||||
|                                    Error **errp) | ||||
| { | ||||
|     QAuthZList *lauthz = QAUTHZ_LIST(authz); | ||||
|     QAuthZListRuleList *rules = lauthz->rules; | ||||
|  | ||||
|     while (rules) { | ||||
|         QAuthZListRule *rule = rules->value; | ||||
|         QAuthZListFormat format = rule->has_format ? rule->format : | ||||
|             QAUTHZ_LIST_FORMAT_EXACT; | ||||
|  | ||||
|         trace_qauthz_list_check_rule(authz, rule->match, identity, | ||||
|                                      format, rule->policy); | ||||
|         switch (format) { | ||||
|         case QAUTHZ_LIST_FORMAT_EXACT: | ||||
|             if (g_str_equal(rule->match, identity)) { | ||||
|                 return rule->policy == QAUTHZ_LIST_POLICY_ALLOW; | ||||
|             } | ||||
|             break; | ||||
|         case QAUTHZ_LIST_FORMAT_GLOB: | ||||
|             if (g_pattern_match_simple(rule->match, identity)) { | ||||
|                 return rule->policy == QAUTHZ_LIST_POLICY_ALLOW; | ||||
|             } | ||||
|             break; | ||||
|         default: | ||||
|             g_warn_if_reached(); | ||||
|             return false; | ||||
|         } | ||||
|         rules = rules->next; | ||||
|     } | ||||
|  | ||||
|     trace_qauthz_list_default_policy(authz, identity, lauthz->policy); | ||||
|     return lauthz->policy == QAUTHZ_LIST_POLICY_ALLOW; | ||||
| } | ||||
|  | ||||
|  | ||||
| static void | ||||
| qauthz_list_prop_set_policy(Object *obj, | ||||
|                             int value, | ||||
|                             Error **errp G_GNUC_UNUSED) | ||||
| { | ||||
|     QAuthZList *lauthz = QAUTHZ_LIST(obj); | ||||
|  | ||||
|     lauthz->policy = value; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int | ||||
| qauthz_list_prop_get_policy(Object *obj, | ||||
|                             Error **errp G_GNUC_UNUSED) | ||||
| { | ||||
|     QAuthZList *lauthz = QAUTHZ_LIST(obj); | ||||
|  | ||||
|     return lauthz->policy; | ||||
| } | ||||
|  | ||||
|  | ||||
| static void | ||||
| qauthz_list_prop_get_rules(Object *obj, Visitor *v, const char *name, | ||||
|                            void *opaque, Error **errp) | ||||
| { | ||||
|     QAuthZList *lauthz = QAUTHZ_LIST(obj); | ||||
|  | ||||
|     visit_type_QAuthZListRuleList(v, name, &lauthz->rules, errp); | ||||
| } | ||||
|  | ||||
| static void | ||||
| qauthz_list_prop_set_rules(Object *obj, Visitor *v, const char *name, | ||||
|                            void *opaque, Error **errp) | ||||
| { | ||||
|     QAuthZList *lauthz = QAUTHZ_LIST(obj); | ||||
|     QAuthZListRuleList *oldrules; | ||||
|  | ||||
|     oldrules = lauthz->rules; | ||||
|     visit_type_QAuthZListRuleList(v, name, &lauthz->rules, errp); | ||||
|  | ||||
|     qapi_free_QAuthZListRuleList(oldrules); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void | ||||
| qauthz_list_finalize(Object *obj) | ||||
| { | ||||
|     QAuthZList *lauthz = QAUTHZ_LIST(obj); | ||||
|  | ||||
|     qapi_free_QAuthZListRuleList(lauthz->rules); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void | ||||
| qauthz_list_class_init(ObjectClass *oc, void *data) | ||||
| { | ||||
|     QAuthZClass *authz = QAUTHZ_CLASS(oc); | ||||
|  | ||||
|     object_class_property_add_enum(oc, "policy", | ||||
|                                    "QAuthZListPolicy", | ||||
|                                    &QAuthZListPolicy_lookup, | ||||
|                                    qauthz_list_prop_get_policy, | ||||
|                                    qauthz_list_prop_set_policy, | ||||
|                                    NULL); | ||||
|  | ||||
|     object_class_property_add(oc, "rules", "QAuthZListRule", | ||||
|                               qauthz_list_prop_get_rules, | ||||
|                               qauthz_list_prop_set_rules, | ||||
|                               NULL, NULL, NULL); | ||||
|  | ||||
|     authz->is_allowed = qauthz_list_is_allowed; | ||||
| } | ||||
|  | ||||
|  | ||||
| QAuthZList *qauthz_list_new(const char *id, | ||||
|                             QAuthZListPolicy policy, | ||||
|                             Error **errp) | ||||
| { | ||||
|     return QAUTHZ_LIST( | ||||
|         object_new_with_props(TYPE_QAUTHZ_LIST, | ||||
|                               object_get_objects_root(), | ||||
|                               id, errp, | ||||
|                               "policy", QAuthZListPolicy_str(policy), | ||||
|                               NULL)); | ||||
| } | ||||
|  | ||||
| ssize_t qauthz_list_append_rule(QAuthZList *auth, | ||||
|                                 const char *match, | ||||
|                                 QAuthZListPolicy policy, | ||||
|                                 QAuthZListFormat format, | ||||
|                                 Error **errp) | ||||
| { | ||||
|     QAuthZListRule *rule; | ||||
|     QAuthZListRuleList *rules, *tmp; | ||||
|     size_t i = 0; | ||||
|  | ||||
|     rule = g_new0(QAuthZListRule, 1); | ||||
|     rule->policy = policy; | ||||
|     rule->match = g_strdup(match); | ||||
|     rule->format = format; | ||||
|     rule->has_format = true; | ||||
|  | ||||
|     tmp = g_new0(QAuthZListRuleList, 1); | ||||
|     tmp->value = rule; | ||||
|  | ||||
|     rules = auth->rules; | ||||
|     if (rules) { | ||||
|         while (rules->next) { | ||||
|             i++; | ||||
|             rules = rules->next; | ||||
|         } | ||||
|         rules->next = tmp; | ||||
|         return i + 1; | ||||
|     } else { | ||||
|         auth->rules = tmp; | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| ssize_t qauthz_list_insert_rule(QAuthZList *auth, | ||||
|                                 const char *match, | ||||
|                                 QAuthZListPolicy policy, | ||||
|                                 QAuthZListFormat format, | ||||
|                                 size_t index, | ||||
|                                 Error **errp) | ||||
| { | ||||
|     QAuthZListRule *rule; | ||||
|     QAuthZListRuleList *rules, *tmp; | ||||
|     size_t i = 0; | ||||
|  | ||||
|     rule = g_new0(QAuthZListRule, 1); | ||||
|     rule->policy = policy; | ||||
|     rule->match = g_strdup(match); | ||||
|     rule->format = format; | ||||
|     rule->has_format = true; | ||||
|  | ||||
|     tmp = g_new0(QAuthZListRuleList, 1); | ||||
|     tmp->value = rule; | ||||
|  | ||||
|     rules = auth->rules; | ||||
|     if (rules && index > 0) { | ||||
|         while (rules->next && i < (index - 1)) { | ||||
|             i++; | ||||
|             rules = rules->next; | ||||
|         } | ||||
|         tmp->next = rules->next; | ||||
|         rules->next = tmp; | ||||
|         return i + 1; | ||||
|     } else { | ||||
|         tmp->next = auth->rules; | ||||
|         auth->rules = tmp; | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| ssize_t qauthz_list_delete_rule(QAuthZList *auth, const char *match) | ||||
| { | ||||
|     QAuthZListRule *rule; | ||||
|     QAuthZListRuleList *rules, *prev; | ||||
|     size_t i = 0; | ||||
|  | ||||
|     prev = NULL; | ||||
|     rules = auth->rules; | ||||
|     while (rules) { | ||||
|         rule = rules->value; | ||||
|         if (g_str_equal(rule->match, match)) { | ||||
|             if (prev) { | ||||
|                 prev->next = rules->next; | ||||
|             } else { | ||||
|                 auth->rules = rules->next; | ||||
|             } | ||||
|             rules->next = NULL; | ||||
|             qapi_free_QAuthZListRuleList(rules); | ||||
|             return i; | ||||
|         } | ||||
|         prev = rules; | ||||
|         rules = rules->next; | ||||
|         i++; | ||||
|     } | ||||
|  | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
|  | ||||
| static const TypeInfo qauthz_list_info = { | ||||
|     .parent = TYPE_QAUTHZ, | ||||
|     .name = TYPE_QAUTHZ_LIST, | ||||
|     .instance_size = sizeof(QAuthZList), | ||||
|     .instance_finalize = qauthz_list_finalize, | ||||
|     .class_size = sizeof(QAuthZListClass), | ||||
|     .class_init = qauthz_list_class_init, | ||||
|     .interfaces = (InterfaceInfo[]) { | ||||
|         { TYPE_USER_CREATABLE }, | ||||
|         { } | ||||
|     } | ||||
| }; | ||||
|  | ||||
|  | ||||
| static void | ||||
| qauthz_list_register_types(void) | ||||
| { | ||||
|     type_register_static(&qauthz_list_info); | ||||
| } | ||||
|  | ||||
|  | ||||
| type_init(qauthz_list_register_types); | ||||
							
								
								
									
										283
									
								
								authz/listfile.c
									
									
									
									
									
								
							
							
						
						
									
										283
									
								
								authz/listfile.c
									
									
									
									
									
								
							| @@ -1,283 +0,0 @@ | ||||
| /* | ||||
|  * QEMU access control list file authorization driver | ||||
|  * | ||||
|  * Copyright (c) 2018 Red Hat, Inc. | ||||
|  * | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public | ||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "authz/listfile.h" | ||||
| #include "authz/trace.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "qemu/main-loop.h" | ||||
| #include "qemu/sockets.h" | ||||
| #include "qemu/filemonitor.h" | ||||
| #include "qom/object_interfaces.h" | ||||
| #include "qapi/qapi-visit-authz.h" | ||||
| #include "qapi/qmp/qjson.h" | ||||
| #include "qapi/qmp/qobject.h" | ||||
| #include "qapi/qmp/qerror.h" | ||||
| #include "qapi/qobject-input-visitor.h" | ||||
|  | ||||
|  | ||||
| static bool | ||||
| qauthz_list_file_is_allowed(QAuthZ *authz, | ||||
|                             const char *identity, | ||||
|                             Error **errp) | ||||
| { | ||||
|     QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(authz); | ||||
|     if (fauthz->list) { | ||||
|         return qauthz_is_allowed(fauthz->list, identity, errp); | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
|  | ||||
| static QAuthZ * | ||||
| qauthz_list_file_load(QAuthZListFile *fauthz, Error **errp) | ||||
| { | ||||
|     GError *err = NULL; | ||||
|     gchar *content = NULL; | ||||
|     gsize len; | ||||
|     QObject *obj = NULL; | ||||
|     QDict *pdict; | ||||
|     Visitor *v = NULL; | ||||
|     QAuthZ *ret = NULL; | ||||
|  | ||||
|     trace_qauthz_list_file_load(fauthz, fauthz->filename); | ||||
|     if (!g_file_get_contents(fauthz->filename, &content, &len, &err)) { | ||||
|         error_setg(errp, "Unable to read '%s': %s", | ||||
|                    fauthz->filename, err->message); | ||||
|         goto cleanup; | ||||
|     } | ||||
|  | ||||
|     obj = qobject_from_json(content, errp); | ||||
|     if (!obj) { | ||||
|         goto cleanup; | ||||
|     } | ||||
|  | ||||
|     pdict = qobject_to(QDict, obj); | ||||
|     if (!pdict) { | ||||
|         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "obj", "dict"); | ||||
|         goto cleanup; | ||||
|     } | ||||
|  | ||||
|     v = qobject_input_visitor_new(obj); | ||||
|  | ||||
|     ret = (QAuthZ *)user_creatable_add_type(TYPE_QAUTHZ_LIST, | ||||
|                                             NULL, pdict, v, errp); | ||||
|  | ||||
|  cleanup: | ||||
|     visit_free(v); | ||||
|     qobject_unref(obj); | ||||
|     if (err) { | ||||
|         g_error_free(err); | ||||
|     } | ||||
|     g_free(content); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
|  | ||||
| static void | ||||
| qauthz_list_file_event(int wd G_GNUC_UNUSED, | ||||
|                        QFileMonitorEvent ev G_GNUC_UNUSED, | ||||
|                        const char *name G_GNUC_UNUSED, | ||||
|                        void *opaque) | ||||
| { | ||||
|     QAuthZListFile *fauthz = opaque; | ||||
|     Error *err = NULL; | ||||
|  | ||||
|     if (ev != QFILE_MONITOR_EVENT_MODIFIED && | ||||
|         ev != QFILE_MONITOR_EVENT_CREATED) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     object_unref(OBJECT(fauthz->list)); | ||||
|     fauthz->list = qauthz_list_file_load(fauthz, &err); | ||||
|     trace_qauthz_list_file_refresh(fauthz, | ||||
|                                    fauthz->filename, fauthz->list ? 1 : 0); | ||||
|     if (!fauthz->list) { | ||||
|         error_report_err(err); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| qauthz_list_file_complete(UserCreatable *uc, Error **errp) | ||||
| { | ||||
|     QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(uc); | ||||
|     gchar *dir = NULL, *file = NULL; | ||||
|  | ||||
|     fauthz->list = qauthz_list_file_load(fauthz, errp); | ||||
|  | ||||
|     if (!fauthz->refresh) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     fauthz->file_monitor = qemu_file_monitor_new(errp); | ||||
|     if (!fauthz->file_monitor) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     dir = g_path_get_dirname(fauthz->filename); | ||||
|     if (g_str_equal(dir, ".")) { | ||||
|         error_setg(errp, "Filename must be an absolute path"); | ||||
|         goto cleanup; | ||||
|     } | ||||
|     file = g_path_get_basename(fauthz->filename); | ||||
|     if (g_str_equal(file, ".")) { | ||||
|         error_setg(errp, "Path has no trailing filename component"); | ||||
|         goto cleanup; | ||||
|     } | ||||
|  | ||||
|     fauthz->file_watch = qemu_file_monitor_add_watch( | ||||
|         fauthz->file_monitor, dir, file, | ||||
|         qauthz_list_file_event, fauthz, errp); | ||||
|     if (fauthz->file_watch < 0) { | ||||
|         goto cleanup; | ||||
|     } | ||||
|  | ||||
|  cleanup: | ||||
|     g_free(file); | ||||
|     g_free(dir); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void | ||||
| qauthz_list_file_prop_set_filename(Object *obj, | ||||
|                                    const char *value, | ||||
|                                    Error **errp G_GNUC_UNUSED) | ||||
| { | ||||
|     QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj); | ||||
|  | ||||
|     g_free(fauthz->filename); | ||||
|     fauthz->filename = g_strdup(value); | ||||
| } | ||||
|  | ||||
|  | ||||
| static char * | ||||
| qauthz_list_file_prop_get_filename(Object *obj, | ||||
|                                    Error **errp G_GNUC_UNUSED) | ||||
| { | ||||
|     QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj); | ||||
|  | ||||
|     return g_strdup(fauthz->filename); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void | ||||
| qauthz_list_file_prop_set_refresh(Object *obj, | ||||
|                                   bool value, | ||||
|                                   Error **errp G_GNUC_UNUSED) | ||||
| { | ||||
|     QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj); | ||||
|  | ||||
|     fauthz->refresh = value; | ||||
| } | ||||
|  | ||||
|  | ||||
| static bool | ||||
| qauthz_list_file_prop_get_refresh(Object *obj, | ||||
|                                   Error **errp G_GNUC_UNUSED) | ||||
| { | ||||
|     QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj); | ||||
|  | ||||
|     return fauthz->refresh; | ||||
| } | ||||
|  | ||||
|  | ||||
| static void | ||||
| qauthz_list_file_finalize(Object *obj) | ||||
| { | ||||
|     QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj); | ||||
|  | ||||
|     object_unref(OBJECT(fauthz->list)); | ||||
|     g_free(fauthz->filename); | ||||
|     qemu_file_monitor_free(fauthz->file_monitor); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void | ||||
| qauthz_list_file_class_init(ObjectClass *oc, void *data) | ||||
| { | ||||
|     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); | ||||
|     QAuthZClass *authz = QAUTHZ_CLASS(oc); | ||||
|  | ||||
|     ucc->complete = qauthz_list_file_complete; | ||||
|  | ||||
|     object_class_property_add_str(oc, "filename", | ||||
|                                   qauthz_list_file_prop_get_filename, | ||||
|                                   qauthz_list_file_prop_set_filename, | ||||
|                                   NULL); | ||||
|     object_class_property_add_bool(oc, "refresh", | ||||
|                                    qauthz_list_file_prop_get_refresh, | ||||
|                                    qauthz_list_file_prop_set_refresh, | ||||
|                                    NULL); | ||||
|  | ||||
|     authz->is_allowed = qauthz_list_file_is_allowed; | ||||
| } | ||||
|  | ||||
|  | ||||
| static void | ||||
| qauthz_list_file_init(Object *obj) | ||||
| { | ||||
|     QAuthZListFile *authz = QAUTHZ_LIST_FILE(obj); | ||||
|  | ||||
|     authz->file_watch = -1; | ||||
| #ifdef CONFIG_INOTIFY1 | ||||
|     authz->refresh = TRUE; | ||||
| #endif | ||||
| } | ||||
|  | ||||
|  | ||||
| QAuthZListFile *qauthz_list_file_new(const char *id, | ||||
|                                      const char *filename, | ||||
|                                      bool refresh, | ||||
|                                      Error **errp) | ||||
| { | ||||
|     return QAUTHZ_LIST_FILE( | ||||
|         object_new_with_props(TYPE_QAUTHZ_LIST_FILE, | ||||
|                               object_get_objects_root(), | ||||
|                               id, errp, | ||||
|                               "filename", filename, | ||||
|                               "refresh", refresh ? "yes" : "no", | ||||
|                               NULL)); | ||||
| } | ||||
|  | ||||
|  | ||||
| static const TypeInfo qauthz_list_file_info = { | ||||
|     .parent = TYPE_QAUTHZ, | ||||
|     .name = TYPE_QAUTHZ_LIST_FILE, | ||||
|     .instance_init = qauthz_list_file_init, | ||||
|     .instance_size = sizeof(QAuthZListFile), | ||||
|     .instance_finalize = qauthz_list_file_finalize, | ||||
|     .class_size = sizeof(QAuthZListFileClass), | ||||
|     .class_init = qauthz_list_file_class_init, | ||||
|     .interfaces = (InterfaceInfo[]) { | ||||
|         { TYPE_USER_CREATABLE }, | ||||
|         { } | ||||
|     } | ||||
| }; | ||||
|  | ||||
|  | ||||
| static void | ||||
| qauthz_list_file_register_types(void) | ||||
| { | ||||
|     type_register_static(&qauthz_list_file_info); | ||||
| } | ||||
|  | ||||
|  | ||||
| type_init(qauthz_list_file_register_types); | ||||
							
								
								
									
										148
									
								
								authz/pamacct.c
									
									
									
									
									
								
							
							
						
						
									
										148
									
								
								authz/pamacct.c
									
									
									
									
									
								
							| @@ -1,148 +0,0 @@ | ||||
| /* | ||||
|  * QEMU PAM authorization driver | ||||
|  * | ||||
|  * Copyright (c) 2018 Red Hat, Inc. | ||||
|  * | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public | ||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "authz/pamacct.h" | ||||
| #include "authz/trace.h" | ||||
| #include "qom/object_interfaces.h" | ||||
|  | ||||
| #include <security/pam_appl.h> | ||||
|  | ||||
|  | ||||
| static bool qauthz_pam_is_allowed(QAuthZ *authz, | ||||
|                                   const char *identity, | ||||
|                                   Error **errp) | ||||
| { | ||||
|     QAuthZPAM *pauthz = QAUTHZ_PAM(authz); | ||||
|     const struct pam_conv pam_conversation = { 0 }; | ||||
|     pam_handle_t *pamh = NULL; | ||||
|     int ret; | ||||
|  | ||||
|     trace_qauthz_pam_check(authz, identity, pauthz->service); | ||||
|     ret = pam_start(pauthz->service, | ||||
|                     identity, | ||||
|                     &pam_conversation, | ||||
|                     &pamh); | ||||
|     if (ret != PAM_SUCCESS) { | ||||
|         error_setg(errp, "Unable to start PAM transaction: %s", | ||||
|                    pam_strerror(NULL, ret)); | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     ret = pam_acct_mgmt(pamh, PAM_SILENT); | ||||
|     pam_end(pamh, ret); | ||||
|     if (ret != PAM_SUCCESS) { | ||||
|         error_setg(errp, "Unable to authorize user '%s': %s", | ||||
|                    identity, pam_strerror(pamh, ret)); | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
|  | ||||
| static void | ||||
| qauthz_pam_prop_set_service(Object *obj, | ||||
|                             const char *service, | ||||
|                             Error **errp G_GNUC_UNUSED) | ||||
| { | ||||
|     QAuthZPAM *pauthz = QAUTHZ_PAM(obj); | ||||
|  | ||||
|     g_free(pauthz->service); | ||||
|     pauthz->service = g_strdup(service); | ||||
| } | ||||
|  | ||||
|  | ||||
| static char * | ||||
| qauthz_pam_prop_get_service(Object *obj, | ||||
|                             Error **errp G_GNUC_UNUSED) | ||||
| { | ||||
|     QAuthZPAM *pauthz = QAUTHZ_PAM(obj); | ||||
|  | ||||
|     return g_strdup(pauthz->service); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void | ||||
| qauthz_pam_complete(UserCreatable *uc, Error **errp) | ||||
| { | ||||
| } | ||||
|  | ||||
|  | ||||
| static void | ||||
| qauthz_pam_finalize(Object *obj) | ||||
| { | ||||
|     QAuthZPAM *pauthz = QAUTHZ_PAM(obj); | ||||
|  | ||||
|     g_free(pauthz->service); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void | ||||
| qauthz_pam_class_init(ObjectClass *oc, void *data) | ||||
| { | ||||
|     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); | ||||
|     QAuthZClass *authz = QAUTHZ_CLASS(oc); | ||||
|  | ||||
|     ucc->complete = qauthz_pam_complete; | ||||
|     authz->is_allowed = qauthz_pam_is_allowed; | ||||
|  | ||||
|     object_class_property_add_str(oc, "service", | ||||
|                                   qauthz_pam_prop_get_service, | ||||
|                                   qauthz_pam_prop_set_service, | ||||
|                                   NULL); | ||||
| } | ||||
|  | ||||
|  | ||||
| QAuthZPAM *qauthz_pam_new(const char *id, | ||||
|                           const char *service, | ||||
|                           Error **errp) | ||||
| { | ||||
|     return QAUTHZ_PAM( | ||||
|         object_new_with_props(TYPE_QAUTHZ_PAM, | ||||
|                               object_get_objects_root(), | ||||
|                               id, errp, | ||||
|                               "service", service, | ||||
|                               NULL)); | ||||
| } | ||||
|  | ||||
|  | ||||
| static const TypeInfo qauthz_pam_info = { | ||||
|     .parent = TYPE_QAUTHZ, | ||||
|     .name = TYPE_QAUTHZ_PAM, | ||||
|     .instance_size = sizeof(QAuthZPAM), | ||||
|     .instance_finalize = qauthz_pam_finalize, | ||||
|     .class_size = sizeof(QAuthZPAMClass), | ||||
|     .class_init = qauthz_pam_class_init, | ||||
|     .interfaces = (InterfaceInfo[]) { | ||||
|         { TYPE_USER_CREATABLE }, | ||||
|         { } | ||||
|     } | ||||
| }; | ||||
|  | ||||
|  | ||||
| static void | ||||
| qauthz_pam_register_types(void) | ||||
| { | ||||
|     type_register_static(&qauthz_pam_info); | ||||
| } | ||||
|  | ||||
|  | ||||
| type_init(qauthz_pam_register_types); | ||||
							
								
								
									
										115
									
								
								authz/simple.c
									
									
									
									
									
								
							
							
						
						
									
										115
									
								
								authz/simple.c
									
									
									
									
									
								
							| @@ -1,115 +0,0 @@ | ||||
| /* | ||||
|  * QEMU simple authorization driver | ||||
|  * | ||||
|  * Copyright (c) 2018 Red Hat, Inc. | ||||
|  * | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public | ||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "authz/simple.h" | ||||
| #include "authz/trace.h" | ||||
| #include "qom/object_interfaces.h" | ||||
|  | ||||
| static bool qauthz_simple_is_allowed(QAuthZ *authz, | ||||
|                                      const char *identity, | ||||
|                                      Error **errp) | ||||
| { | ||||
|     QAuthZSimple *sauthz = QAUTHZ_SIMPLE(authz); | ||||
|  | ||||
|     trace_qauthz_simple_is_allowed(authz, sauthz->identity, identity); | ||||
|     return g_str_equal(identity, sauthz->identity); | ||||
| } | ||||
|  | ||||
| static void | ||||
| qauthz_simple_prop_set_identity(Object *obj, | ||||
|                                 const char *value, | ||||
|                                 Error **errp G_GNUC_UNUSED) | ||||
| { | ||||
|     QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj); | ||||
|  | ||||
|     g_free(sauthz->identity); | ||||
|     sauthz->identity = g_strdup(value); | ||||
| } | ||||
|  | ||||
|  | ||||
| static char * | ||||
| qauthz_simple_prop_get_identity(Object *obj, | ||||
|                                 Error **errp G_GNUC_UNUSED) | ||||
| { | ||||
|     QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj); | ||||
|  | ||||
|     return g_strdup(sauthz->identity); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void | ||||
| qauthz_simple_finalize(Object *obj) | ||||
| { | ||||
|     QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj); | ||||
|  | ||||
|     g_free(sauthz->identity); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void | ||||
| qauthz_simple_class_init(ObjectClass *oc, void *data) | ||||
| { | ||||
|     QAuthZClass *authz = QAUTHZ_CLASS(oc); | ||||
|  | ||||
|     authz->is_allowed = qauthz_simple_is_allowed; | ||||
|  | ||||
|     object_class_property_add_str(oc, "identity", | ||||
|                                   qauthz_simple_prop_get_identity, | ||||
|                                   qauthz_simple_prop_set_identity, | ||||
|                                   NULL); | ||||
| } | ||||
|  | ||||
|  | ||||
| QAuthZSimple *qauthz_simple_new(const char *id, | ||||
|                                 const char *identity, | ||||
|                                 Error **errp) | ||||
| { | ||||
|     return QAUTHZ_SIMPLE( | ||||
|         object_new_with_props(TYPE_QAUTHZ_SIMPLE, | ||||
|                               object_get_objects_root(), | ||||
|                               id, errp, | ||||
|                               "identity", identity, | ||||
|                               NULL)); | ||||
| } | ||||
|  | ||||
|  | ||||
| static const TypeInfo qauthz_simple_info = { | ||||
|     .parent = TYPE_QAUTHZ, | ||||
|     .name = TYPE_QAUTHZ_SIMPLE, | ||||
|     .instance_size = sizeof(QAuthZSimple), | ||||
|     .instance_finalize = qauthz_simple_finalize, | ||||
|     .class_size = sizeof(QAuthZSimpleClass), | ||||
|     .class_init = qauthz_simple_class_init, | ||||
|     .interfaces = (InterfaceInfo[]) { | ||||
|         { TYPE_USER_CREATABLE }, | ||||
|         { } | ||||
|     } | ||||
| }; | ||||
|  | ||||
|  | ||||
| static void | ||||
| qauthz_simple_register_types(void) | ||||
| { | ||||
|     type_register_static(&qauthz_simple_info); | ||||
| } | ||||
|  | ||||
|  | ||||
| type_init(qauthz_simple_register_types); | ||||
| @@ -1,18 +0,0 @@ | ||||
| # See docs/devel/tracing.txt for syntax documentation. | ||||
|  | ||||
| # authz/base.c | ||||
| qauthz_is_allowed(void *authz, const char *identity, bool allowed) "AuthZ %p check identity=%s allowed=%d" | ||||
|  | ||||
| # auth/simple.c | ||||
| qauthz_simple_is_allowed(void *authz, const char *wantidentity, const char *gotidentity) "AuthZ simple %p check want identity=%s got identity=%s" | ||||
|  | ||||
| # auth/list.c | ||||
| qauthz_list_check_rule(void *authz, const char *identity, const char *rule, int format, int policy) "AuthZ list %p check rule=%s identity=%s format=%d policy=%d" | ||||
| qauthz_list_default_policy(void *authz, const char *identity, int policy) "AuthZ list %p default identity=%s policy=%d" | ||||
|  | ||||
| # auth/listfile.c | ||||
| qauthz_list_file_load(void *authz, const char *filename) "AuthZ file %p load filename=%s" | ||||
| qauthz_list_file_refresh(void *authz, const char *filename, int success) "AuthZ file %p load filename=%s success=%d" | ||||
|  | ||||
| # auth/pam.c | ||||
| qauthz_pam_check(void *authz, const char *identity, const char *service) "AuthZ PAM %p identity=%s service=%s" | ||||
| @@ -4,14 +4,15 @@ common-obj-$(CONFIG_POSIX) += rng-random.o | ||||
| common-obj-$(CONFIG_TPM) += tpm.o | ||||
|  | ||||
| common-obj-y += hostmem.o hostmem-ram.o | ||||
| common-obj-$(CONFIG_POSIX) += hostmem-file.o | ||||
| common-obj-$(CONFIG_LINUX) += hostmem-file.o | ||||
|  | ||||
| common-obj-y += cryptodev.o | ||||
| common-obj-y += cryptodev-builtin.o | ||||
|  | ||||
| ifeq ($(CONFIG_VIRTIO_CRYPTO),y) | ||||
| ifeq ($(CONFIG_VIRTIO),y) | ||||
| common-obj-y += cryptodev-vhost.o | ||||
| common-obj-$(CONFIG_VHOST_CRYPTO) += cryptodev-vhost-user.o | ||||
| common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) += \ | ||||
|     cryptodev-vhost-user.o | ||||
| endif | ||||
|  | ||||
| common-obj-$(CONFIG_LINUX) += hostmem-memfd.o | ||||
|   | ||||
| @@ -26,7 +26,6 @@ | ||||
| #include "qapi/error.h" | ||||
| #include "qapi/qmp/qerror.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "hw/virtio/vhost-user.h" | ||||
| #include "standard-headers/linux/virtio_crypto.h" | ||||
| #include "sysemu/cryptodev-vhost.h" | ||||
| #include "chardev/char-fe.h" | ||||
| @@ -47,7 +46,6 @@ | ||||
| typedef struct CryptoDevBackendVhostUser { | ||||
|     CryptoDevBackend parent_obj; | ||||
|  | ||||
|     VhostUserState vhost_user; | ||||
|     CharBackend chr; | ||||
|     char *chr_name; | ||||
|     bool opened; | ||||
| @@ -104,7 +102,7 @@ cryptodev_vhost_user_start(int queues, | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         options.opaque = &s->vhost_user; | ||||
|         options.opaque = &s->chr; | ||||
|         options.backend_type = VHOST_BACKEND_TYPE_USER; | ||||
|         options.cc = b->conf.peers.ccs[i]; | ||||
|         s->vhost_crypto[i] = cryptodev_vhost_init(&options); | ||||
| @@ -157,6 +155,7 @@ static void cryptodev_vhost_user_event(void *opaque, int event) | ||||
| { | ||||
|     CryptoDevBackendVhostUser *s = opaque; | ||||
|     CryptoDevBackend *b = CRYPTODEV_BACKEND(s); | ||||
|     Error *err = NULL; | ||||
|     int queues = b->conf.peers.queues; | ||||
|  | ||||
|     assert(queues < MAX_CRYPTO_QUEUE_NUM); | ||||
| @@ -173,6 +172,10 @@ static void cryptodev_vhost_user_event(void *opaque, int event) | ||||
|         cryptodev_vhost_user_stop(queues, s); | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     if (err) { | ||||
|         error_report_err(err); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void cryptodev_vhost_user_init( | ||||
| @@ -212,10 +215,6 @@ static void cryptodev_vhost_user_init( | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (!vhost_user_init(&s->vhost_user, &s->chr, errp)) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, | ||||
|                      cryptodev_vhost_user_event, NULL, s, NULL, true); | ||||
|  | ||||
| @@ -300,8 +299,6 @@ static void cryptodev_vhost_user_cleanup( | ||||
|             backend->conf.peers.ccs[i] = NULL; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     vhost_user_cleanup(&s->vhost_user); | ||||
| } | ||||
|  | ||||
| static void cryptodev_vhost_user_set_chardev(Object *obj, | ||||
|   | ||||
| @@ -12,7 +12,6 @@ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qemu-common.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "sysemu/hostmem.h" | ||||
| #include "sysemu/sysemu.h" | ||||
| #include "qom/object_interfaces.h" | ||||
| @@ -32,21 +31,15 @@ typedef struct HostMemoryBackendFile HostMemoryBackendFile; | ||||
| struct HostMemoryBackendFile { | ||||
|     HostMemoryBackend parent_obj; | ||||
|  | ||||
|     bool discard_data; | ||||
|     char *mem_path; | ||||
|     uint64_t align; | ||||
|     bool discard_data; | ||||
|     bool is_pmem; | ||||
| }; | ||||
|  | ||||
| static void | ||||
| file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | ||||
| { | ||||
| #ifndef CONFIG_POSIX | ||||
|     error_setg(errp, "backend '%s' not supported on this host", | ||||
|                object_get_typename(OBJECT(backend))); | ||||
| #else | ||||
|     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend); | ||||
|     gchar *name; | ||||
|  | ||||
|     if (!backend->size) { | ||||
|         error_setg(errp, "can't create backend with size 0"); | ||||
| @@ -56,38 +49,19 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | ||||
|         error_setg(errp, "mem-path property not set"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * Verify pmem file size since starting a guest with an incorrect size | ||||
|      * leads to confusing failures inside the guest. | ||||
|      */ | ||||
|     if (fb->is_pmem) { | ||||
|         Error *local_err = NULL; | ||||
|         uint64_t size; | ||||
|  | ||||
|         size = qemu_get_pmem_size(fb->mem_path, &local_err); | ||||
|         if (!size) { | ||||
|             error_propagate(errp, local_err); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (backend->size > size) { | ||||
|             error_setg(errp, "size property %" PRIu64 " is larger than " | ||||
|                        "pmem file \"%s\" size %" PRIu64, backend->size, | ||||
|                        fb->mem_path, size); | ||||
|             return; | ||||
|         } | ||||
| #ifndef CONFIG_LINUX | ||||
|     error_setg(errp, "-mem-path not supported on this host"); | ||||
| #else | ||||
|     if (!host_memory_backend_mr_inited(backend)) { | ||||
|         gchar *path; | ||||
|         backend->force_prealloc = mem_prealloc; | ||||
|         path = object_get_canonical_path(OBJECT(backend)); | ||||
|         memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), | ||||
|                                  path, | ||||
|                                  backend->size, fb->align, backend->share, | ||||
|                                  fb->mem_path, errp); | ||||
|         g_free(path); | ||||
|     } | ||||
|  | ||||
|     backend->force_prealloc = mem_prealloc; | ||||
|     name = host_memory_backend_get_name(backend); | ||||
|     memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), | ||||
|                                      name, | ||||
|                                      backend->size, fb->align, | ||||
|                                      (backend->share ? RAM_SHARED : 0) | | ||||
|                                      (fb->is_pmem ? RAM_PMEM : 0), | ||||
|                                      fb->mem_path, errp); | ||||
|     g_free(name); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| @@ -104,8 +78,7 @@ static void set_mem_path(Object *o, const char *str, Error **errp) | ||||
|     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); | ||||
|  | ||||
|     if (host_memory_backend_mr_inited(backend)) { | ||||
|         error_setg(errp, "cannot change property 'mem-path' of %s", | ||||
|                    object_get_typename(o)); | ||||
|         error_setg(errp, "cannot change property value"); | ||||
|         return; | ||||
|     } | ||||
|     g_free(fb->mem_path); | ||||
| @@ -143,8 +116,7 @@ static void file_memory_backend_set_align(Object *o, Visitor *v, | ||||
|     uint64_t val; | ||||
|  | ||||
|     if (host_memory_backend_mr_inited(backend)) { | ||||
|         error_setg(&local_err, "cannot change property '%s' of %s", | ||||
|                    name, object_get_typename(o)); | ||||
|         error_setg(&local_err, "cannot change property value"); | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
| @@ -158,39 +130,6 @@ static void file_memory_backend_set_align(Object *o, Visitor *v, | ||||
|     error_propagate(errp, local_err); | ||||
| } | ||||
|  | ||||
| static bool file_memory_backend_get_pmem(Object *o, Error **errp) | ||||
| { | ||||
|     return MEMORY_BACKEND_FILE(o)->is_pmem; | ||||
| } | ||||
|  | ||||
| static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); | ||||
|     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); | ||||
|  | ||||
|     if (host_memory_backend_mr_inited(backend)) { | ||||
|  | ||||
|         error_setg(errp, "cannot change property 'pmem' of %s.", | ||||
|                    object_get_typename(o)); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| #ifndef CONFIG_LIBPMEM | ||||
|     if (value) { | ||||
|         Error *local_err = NULL; | ||||
|  | ||||
|         error_setg(&local_err, | ||||
|                    "Lack of libpmem support while setting the 'pmem=on'" | ||||
|                    " of %s. We can't ensure data persistence.", | ||||
|                    object_get_typename(o)); | ||||
|         error_propagate(errp, local_err); | ||||
|         return; | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     fb->is_pmem = value; | ||||
| } | ||||
|  | ||||
| static void file_backend_unparent(Object *obj) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||
| @@ -222,9 +161,6 @@ file_backend_class_init(ObjectClass *oc, void *data) | ||||
|         file_memory_backend_get_align, | ||||
|         file_memory_backend_set_align, | ||||
|         NULL, NULL, &error_abort); | ||||
|     object_class_property_add_bool(oc, "pmem", | ||||
|         file_memory_backend_get_pmem, file_memory_backend_set_pmem, | ||||
|         &error_abort); | ||||
| } | ||||
|  | ||||
| static void file_backend_instance_finalize(Object *o) | ||||
|   | ||||
| @@ -44,6 +44,10 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (host_memory_backend_mr_inited(backend)) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     backend->force_prealloc = mem_prealloc; | ||||
|     fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size, | ||||
|                            m->hugetlb, m->hugetlbsize, m->seal ? | ||||
| @@ -53,10 +57,9 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     name = host_memory_backend_get_name(backend); | ||||
|     name = object_get_canonical_path(OBJECT(backend)); | ||||
|     memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), | ||||
|                                    name, backend->size, | ||||
|                                    backend->share, fd, errp); | ||||
|                                    name, backend->size, true, fd, errp); | ||||
|     g_free(name); | ||||
| } | ||||
|  | ||||
| @@ -128,7 +131,6 @@ memfd_backend_instance_init(Object *obj) | ||||
|  | ||||
|     /* default to sealed file */ | ||||
|     m->seal = true; | ||||
|     MEMORY_BACKEND(m)->share = true; | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -138,29 +140,18 @@ memfd_backend_class_init(ObjectClass *oc, void *data) | ||||
|  | ||||
|     bc->alloc = memfd_backend_memory_alloc; | ||||
|  | ||||
|     if (qemu_memfd_check(MFD_HUGETLB)) { | ||||
|         object_class_property_add_bool(oc, "hugetlb", | ||||
|                                        memfd_backend_get_hugetlb, | ||||
|                                        memfd_backend_set_hugetlb, | ||||
|                                        &error_abort); | ||||
|         object_class_property_set_description(oc, "hugetlb", | ||||
|                                               "Use huge pages", | ||||
|                                               &error_abort); | ||||
|         object_class_property_add(oc, "hugetlbsize", "int", | ||||
|                                   memfd_backend_get_hugetlbsize, | ||||
|                                   memfd_backend_set_hugetlbsize, | ||||
|                                   NULL, NULL, &error_abort); | ||||
|         object_class_property_set_description(oc, "hugetlbsize", | ||||
|                                               "Huge pages size (ex: 2M, 1G)", | ||||
|                                               &error_abort); | ||||
|     } | ||||
|     object_class_property_add_bool(oc, "hugetlb", | ||||
|                                    memfd_backend_get_hugetlb, | ||||
|                                    memfd_backend_set_hugetlb, | ||||
|                                    &error_abort); | ||||
|     object_class_property_add(oc, "hugetlbsize", "int", | ||||
|                               memfd_backend_get_hugetlbsize, | ||||
|                               memfd_backend_set_hugetlbsize, | ||||
|                               NULL, NULL, &error_abort); | ||||
|     object_class_property_add_bool(oc, "seal", | ||||
|                                    memfd_backend_get_seal, | ||||
|                                    memfd_backend_set_seal, | ||||
|                                    &error_abort); | ||||
|     object_class_property_set_description(oc, "seal", | ||||
|                                           "Seal growing & shrinking", | ||||
|                                           &error_abort); | ||||
| } | ||||
|  | ||||
| static const TypeInfo memfd_backend_info = { | ||||
| @@ -173,9 +164,7 @@ static const TypeInfo memfd_backend_info = { | ||||
|  | ||||
| static void register_types(void) | ||||
| { | ||||
|     if (qemu_memfd_check(MFD_ALLOW_SEALING)) { | ||||
|         type_register_static(&memfd_backend_info); | ||||
|     } | ||||
|     type_register_static(&memfd_backend_info); | ||||
| } | ||||
|  | ||||
| type_init(register_types); | ||||
|   | ||||
| @@ -16,20 +16,21 @@ | ||||
|  | ||||
| #define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram" | ||||
|  | ||||
|  | ||||
| static void | ||||
| ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | ||||
| { | ||||
|     char *name; | ||||
|     char *path; | ||||
|  | ||||
|     if (!backend->size) { | ||||
|         error_setg(errp, "can't create backend with size 0"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     name = host_memory_backend_get_name(backend); | ||||
|     memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), name, | ||||
|     path = object_get_canonical_path_component(OBJECT(backend)); | ||||
|     memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), path, | ||||
|                            backend->size, backend->share, errp); | ||||
|     g_free(name); | ||||
|     g_free(path); | ||||
| } | ||||
|  | ||||
| static void | ||||
|   | ||||
| @@ -18,7 +18,6 @@ | ||||
| #include "qapi/visitor.h" | ||||
| #include "qemu/config-file.h" | ||||
| #include "qom/object_interfaces.h" | ||||
| #include "qemu/mmap-alloc.h" | ||||
|  | ||||
| #ifdef CONFIG_NUMA | ||||
| #include <numaif.h> | ||||
| @@ -28,16 +27,6 @@ QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_BIND != MPOL_BIND); | ||||
| QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE); | ||||
| #endif | ||||
|  | ||||
| char * | ||||
| host_memory_backend_get_name(HostMemoryBackend *backend) | ||||
| { | ||||
|     if (!backend->use_canonical_path) { | ||||
|         return object_get_canonical_path_component(OBJECT(backend)); | ||||
|     } | ||||
|  | ||||
|     return object_get_canonical_path(OBJECT(backend)); | ||||
| } | ||||
|  | ||||
| static void | ||||
| host_memory_backend_get_size(Object *obj, Visitor *v, const char *name, | ||||
|                              void *opaque, Error **errp) | ||||
| @@ -57,8 +46,7 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name, | ||||
|     uint64_t value; | ||||
|  | ||||
|     if (host_memory_backend_mr_inited(backend)) { | ||||
|         error_setg(&local_err, "cannot change property %s of %s ", | ||||
|                    name, object_get_typename(obj)); | ||||
|         error_setg(&local_err, "cannot change property value"); | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
| @@ -67,9 +55,8 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name, | ||||
|         goto out; | ||||
|     } | ||||
|     if (!value) { | ||||
|         error_setg(&local_err, | ||||
|                    "property '%s' of %s doesn't take value '%" PRIu64 "'", | ||||
|                    name, object_get_typename(obj), value); | ||||
|         error_setg(&local_err, "Property '%s.%s' doesn't take value '%" | ||||
|                    PRIu64 "'", object_get_typename(obj), name, value); | ||||
|         goto out; | ||||
|     } | ||||
|     backend->size = value; | ||||
| @@ -88,7 +75,7 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name, | ||||
|  | ||||
|     value = find_first_bit(backend->host_nodes, MAX_NODES); | ||||
|     if (value == MAX_NODES) { | ||||
|         goto ret; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     *node = g_malloc0(sizeof(**node)); | ||||
| @@ -106,7 +93,6 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name, | ||||
|         node = &(*node)->next; | ||||
|     } while (true); | ||||
|  | ||||
| ret: | ||||
|     visit_type_uint16List(v, name, &host_nodes, errp); | ||||
| } | ||||
|  | ||||
| @@ -116,23 +102,14 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name, | ||||
| { | ||||
| #ifdef CONFIG_NUMA | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||
|     uint16List *l, *host_nodes = NULL; | ||||
|     uint16List *l = NULL; | ||||
|  | ||||
|     visit_type_uint16List(v, name, &host_nodes, errp); | ||||
|     visit_type_uint16List(v, name, &l, errp); | ||||
|  | ||||
|     for (l = host_nodes; l; l = l->next) { | ||||
|         if (l->value >= MAX_NODES) { | ||||
|             error_setg(errp, "Invalid host-nodes value: %d", l->value); | ||||
|             goto out; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     for (l = host_nodes; l; l = l->next) { | ||||
|     while (l) { | ||||
|         bitmap_set(backend->host_nodes, l->value, 1); | ||||
|         l = l->next; | ||||
|     } | ||||
|  | ||||
| out: | ||||
|     qapi_free_uint16List(host_nodes); | ||||
| #else | ||||
|     error_setg(errp, "NUMA node binding are not supported by this QEMU"); | ||||
| #endif | ||||
| @@ -260,11 +237,6 @@ static void host_memory_backend_init(Object *obj) | ||||
|     backend->prealloc = mem_prealloc; | ||||
| } | ||||
|  | ||||
| static void host_memory_backend_post_init(Object *obj) | ||||
| { | ||||
|     object_apply_compat_props(obj); | ||||
| } | ||||
|  | ||||
| bool host_memory_backend_mr_inited(HostMemoryBackend *backend) | ||||
| { | ||||
|     /* | ||||
| @@ -274,7 +246,8 @@ bool host_memory_backend_mr_inited(HostMemoryBackend *backend) | ||||
|     return memory_region_size(&backend->mr) != 0; | ||||
| } | ||||
|  | ||||
| MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend) | ||||
| MemoryRegion * | ||||
| host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp) | ||||
| { | ||||
|     return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL; | ||||
| } | ||||
| @@ -289,23 +262,6 @@ bool host_memory_backend_is_mapped(HostMemoryBackend *backend) | ||||
|     return backend->is_mapped; | ||||
| } | ||||
|  | ||||
| #ifdef __linux__ | ||||
| size_t host_memory_backend_pagesize(HostMemoryBackend *memdev) | ||||
| { | ||||
|     Object *obj = OBJECT(memdev); | ||||
|     char *path = object_property_get_str(obj, "mem-path", NULL); | ||||
|     size_t pagesize = qemu_mempath_getpagesize(path); | ||||
|  | ||||
|     g_free(path); | ||||
|     return pagesize; | ||||
| } | ||||
| #else | ||||
| size_t host_memory_backend_pagesize(HostMemoryBackend *memdev) | ||||
| { | ||||
|     return getpagesize(); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static void | ||||
| host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) | ||||
| { | ||||
| @@ -395,6 +351,24 @@ host_memory_backend_can_be_deleted(UserCreatable *uc) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static char *get_id(Object *o, Error **errp) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); | ||||
|  | ||||
|     return g_strdup(backend->id); | ||||
| } | ||||
|  | ||||
| static void set_id(Object *o, const char *str, Error **errp) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); | ||||
|  | ||||
|     if (backend->id) { | ||||
|         error_setg(errp, "cannot change property value"); | ||||
|         return; | ||||
|     } | ||||
|     backend->id = g_strdup(str); | ||||
| } | ||||
|  | ||||
| static bool host_memory_backend_get_share(Object *o, Error **errp) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); | ||||
| @@ -413,23 +387,6 @@ static void host_memory_backend_set_share(Object *o, bool value, Error **errp) | ||||
|     backend->share = value; | ||||
| } | ||||
|  | ||||
| static bool | ||||
| host_memory_backend_get_use_canonical_path(Object *obj, Error **errp) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||
|  | ||||
|     return backend->use_canonical_path; | ||||
| } | ||||
|  | ||||
| static void | ||||
| host_memory_backend_set_use_canonical_path(Object *obj, bool value, | ||||
|                                            Error **errp) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||
|  | ||||
|     backend->use_canonical_path = value; | ||||
| } | ||||
|  | ||||
| static void | ||||
| host_memory_backend_class_init(ObjectClass *oc, void *data) | ||||
| { | ||||
| @@ -441,44 +398,34 @@ host_memory_backend_class_init(ObjectClass *oc, void *data) | ||||
|     object_class_property_add_bool(oc, "merge", | ||||
|         host_memory_backend_get_merge, | ||||
|         host_memory_backend_set_merge, &error_abort); | ||||
|     object_class_property_set_description(oc, "merge", | ||||
|         "Mark memory as mergeable", &error_abort); | ||||
|     object_class_property_add_bool(oc, "dump", | ||||
|         host_memory_backend_get_dump, | ||||
|         host_memory_backend_set_dump, &error_abort); | ||||
|     object_class_property_set_description(oc, "dump", | ||||
|         "Set to 'off' to exclude from core dump", &error_abort); | ||||
|     object_class_property_add_bool(oc, "prealloc", | ||||
|         host_memory_backend_get_prealloc, | ||||
|         host_memory_backend_set_prealloc, &error_abort); | ||||
|     object_class_property_set_description(oc, "prealloc", | ||||
|         "Preallocate memory", &error_abort); | ||||
|     object_class_property_add(oc, "size", "int", | ||||
|         host_memory_backend_get_size, | ||||
|         host_memory_backend_set_size, | ||||
|         NULL, NULL, &error_abort); | ||||
|     object_class_property_set_description(oc, "size", | ||||
|         "Size of the memory region (ex: 500M)", &error_abort); | ||||
|     object_class_property_add(oc, "host-nodes", "int", | ||||
|         host_memory_backend_get_host_nodes, | ||||
|         host_memory_backend_set_host_nodes, | ||||
|         NULL, NULL, &error_abort); | ||||
|     object_class_property_set_description(oc, "host-nodes", | ||||
|         "Binds memory to the list of NUMA host nodes", &error_abort); | ||||
|     object_class_property_add_enum(oc, "policy", "HostMemPolicy", | ||||
|         &HostMemPolicy_lookup, | ||||
|         host_memory_backend_get_policy, | ||||
|         host_memory_backend_set_policy, &error_abort); | ||||
|     object_class_property_set_description(oc, "policy", | ||||
|         "Set the NUMA policy", &error_abort); | ||||
|     object_class_property_add_str(oc, "id", get_id, set_id, &error_abort); | ||||
|     object_class_property_add_bool(oc, "share", | ||||
|         host_memory_backend_get_share, host_memory_backend_set_share, | ||||
|         &error_abort); | ||||
|     object_class_property_set_description(oc, "share", | ||||
|         "Mark the memory as private to QEMU or shared", &error_abort); | ||||
|     object_class_property_add_bool(oc, "x-use-canonical-path-for-ramblock-id", | ||||
|         host_memory_backend_get_use_canonical_path, | ||||
|         host_memory_backend_set_use_canonical_path, &error_abort); | ||||
| } | ||||
|  | ||||
| static void host_memory_backend_finalize(Object *o) | ||||
| { | ||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); | ||||
|     g_free(backend->id); | ||||
| } | ||||
|  | ||||
| static const TypeInfo host_memory_backend_info = { | ||||
| @@ -489,7 +436,7 @@ static const TypeInfo host_memory_backend_info = { | ||||
|     .class_init = host_memory_backend_class_init, | ||||
|     .instance_size = sizeof(HostMemoryBackend), | ||||
|     .instance_init = host_memory_backend_init, | ||||
|     .instance_post_init = host_memory_backend_post_init, | ||||
|     .instance_finalize = host_memory_backend_finalize, | ||||
|     .interfaces = (InterfaceInfo[]) { | ||||
|         { TYPE_USER_CREATABLE }, | ||||
|         { } | ||||
|   | ||||
							
								
								
									
										13
									
								
								balloon.c
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								balloon.c
									
									
									
									
									
								
							| @@ -26,7 +26,6 @@ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "qemu/atomic.h" | ||||
| #include "exec/cpu-common.h" | ||||
| #include "sysemu/kvm.h" | ||||
| #include "sysemu/balloon.h" | ||||
| @@ -38,22 +37,16 @@ | ||||
| static QEMUBalloonEvent *balloon_event_fn; | ||||
| static QEMUBalloonStatus *balloon_stat_fn; | ||||
| static void *balloon_opaque; | ||||
| static int balloon_inhibit_count; | ||||
| static bool balloon_inhibited; | ||||
|  | ||||
| bool qemu_balloon_is_inhibited(void) | ||||
| { | ||||
|     return atomic_read(&balloon_inhibit_count) > 0; | ||||
|     return balloon_inhibited; | ||||
| } | ||||
|  | ||||
| void qemu_balloon_inhibit(bool state) | ||||
| { | ||||
|     if (state) { | ||||
|         atomic_inc(&balloon_inhibit_count); | ||||
|     } else { | ||||
|         atomic_dec(&balloon_inhibit_count); | ||||
|     } | ||||
|  | ||||
|     assert(atomic_read(&balloon_inhibit_count) >= 0); | ||||
|     balloon_inhibited = state; | ||||
| } | ||||
|  | ||||
| static bool have_balloon(Error **errp) | ||||
|   | ||||
| @@ -1,19 +1,10 @@ | ||||
| block-obj-y += raw-format.o vmdk.o vpc.o | ||||
| block-obj-$(CONFIG_QCOW1) += qcow.o | ||||
| block-obj-$(CONFIG_VDI) += vdi.o | ||||
| block-obj-$(CONFIG_CLOOP) += cloop.o | ||||
| block-obj-$(CONFIG_BOCHS) += bochs.o | ||||
| block-obj-$(CONFIG_VVFAT) += vvfat.o | ||||
| block-obj-$(CONFIG_DMG) += dmg.o | ||||
|  | ||||
| block-obj-y += raw-format.o qcow.o vdi.o vmdk.o cloop.o bochs.o vpc.o vvfat.o dmg.o | ||||
| block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o | ||||
| block-obj-$(CONFIG_QED) += qed.o qed-l2-cache.o qed-table.o qed-cluster.o | ||||
| block-obj-$(CONFIG_QED) += qed-check.o | ||||
| block-obj-y += qed.o qed-l2-cache.o qed-table.o qed-cluster.o | ||||
| block-obj-y += qed-check.o | ||||
| block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o | ||||
| block-obj-y += quorum.o | ||||
| block-obj-y += blkdebug.o blkverify.o blkreplay.o | ||||
| block-obj-$(CONFIG_PARALLELS) += parallels.o | ||||
| block-obj-y += blklogwrites.o | ||||
| block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o | ||||
| block-obj-y += block-backend.o snapshot.o qapi.o | ||||
| block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o | ||||
| block-obj-$(CONFIG_POSIX) += file-posix.o | ||||
| @@ -22,8 +13,7 @@ block-obj-y += null.o mirror.o commit.o io.o create.o | ||||
| block-obj-y += throttle-groups.o | ||||
| block-obj-$(CONFIG_LINUX) += nvme.o | ||||
|  | ||||
| block-obj-y += nbd.o nbd-client.o | ||||
| block-obj-$(CONFIG_SHEEPDOG) += sheepdog.o | ||||
| block-obj-y += nbd.o nbd-client.o sheepdog.o | ||||
| block-obj-$(CONFIG_LIBISCSI) += iscsi.o | ||||
| block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o | ||||
| block-obj-$(CONFIG_LIBNFS) += nfs.o | ||||
| @@ -36,7 +26,7 @@ block-obj-y += accounting.o dirty-bitmap.o | ||||
| block-obj-y += write-threshold.o | ||||
| block-obj-y += backup.o | ||||
| block-obj-$(CONFIG_REPLICATION) += replication.o | ||||
| block-obj-y += throttle.o copy-on-read.o | ||||
| block-obj-y += throttle.o | ||||
|  | ||||
| block-obj-y += crypto.o | ||||
|  | ||||
| @@ -54,11 +44,8 @@ gluster.o-libs     := $(GLUSTERFS_LIBS) | ||||
| vxhs.o-libs        := $(VXHS_LIBS) | ||||
| ssh.o-cflags       := $(LIBSSH2_CFLAGS) | ||||
| ssh.o-libs         := $(LIBSSH2_LIBS) | ||||
| block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o | ||||
| block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y) | ||||
| block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o | ||||
| dmg-bz2.o-libs     := $(BZIP2_LIBS) | ||||
| block-obj-$(if $(CONFIG_LZFSE),m,n) += dmg-lzfse.o | ||||
| dmg-lzfse.o-libs   := $(LZFSE_LIBS) | ||||
| qcow.o-libs        := -lz | ||||
| linux-aio.o-libs   := -laio | ||||
| parallels.o-cflags := $(LIBXML2_CFLAGS) | ||||
|   | ||||
| @@ -94,94 +94,6 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie, | ||||
|     cookie->type = type; | ||||
| } | ||||
|  | ||||
| /* block_latency_histogram_compare_func: | ||||
|  * Compare @key with interval [@it[0], @it[1]). | ||||
|  * Return: -1 if @key < @it[0] | ||||
|  *          0 if @key in [@it[0], @it[1]) | ||||
|  *         +1 if @key >= @it[1] | ||||
|  */ | ||||
| static int block_latency_histogram_compare_func(const void *key, const void *it) | ||||
| { | ||||
|     uint64_t k = *(uint64_t *)key; | ||||
|     uint64_t a = ((uint64_t *)it)[0]; | ||||
|     uint64_t b = ((uint64_t *)it)[1]; | ||||
|  | ||||
|     return k < a ? -1 : (k < b ? 0 : 1); | ||||
| } | ||||
|  | ||||
| static void block_latency_histogram_account(BlockLatencyHistogram *hist, | ||||
|                                             int64_t latency_ns) | ||||
| { | ||||
|     uint64_t *pos; | ||||
|  | ||||
|     if (hist->bins == NULL) { | ||||
|         /* histogram disabled */ | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     if (latency_ns < hist->boundaries[0]) { | ||||
|         hist->bins[0]++; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (latency_ns >= hist->boundaries[hist->nbins - 2]) { | ||||
|         hist->bins[hist->nbins - 1]++; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     pos = bsearch(&latency_ns, hist->boundaries, hist->nbins - 2, | ||||
|                   sizeof(hist->boundaries[0]), | ||||
|                   block_latency_histogram_compare_func); | ||||
|     assert(pos != NULL); | ||||
|  | ||||
|     hist->bins[pos - hist->boundaries + 1]++; | ||||
| } | ||||
|  | ||||
| int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type, | ||||
|                                 uint64List *boundaries) | ||||
| { | ||||
|     BlockLatencyHistogram *hist = &stats->latency_histogram[type]; | ||||
|     uint64List *entry; | ||||
|     uint64_t *ptr; | ||||
|     uint64_t prev = 0; | ||||
|     int new_nbins = 1; | ||||
|  | ||||
|     for (entry = boundaries; entry; entry = entry->next) { | ||||
|         if (entry->value <= prev) { | ||||
|             return -EINVAL; | ||||
|         } | ||||
|         new_nbins++; | ||||
|         prev = entry->value; | ||||
|     } | ||||
|  | ||||
|     hist->nbins = new_nbins; | ||||
|     g_free(hist->boundaries); | ||||
|     hist->boundaries = g_new(uint64_t, hist->nbins - 1); | ||||
|     for (entry = boundaries, ptr = hist->boundaries; entry; | ||||
|          entry = entry->next, ptr++) | ||||
|     { | ||||
|         *ptr = entry->value; | ||||
|     } | ||||
|  | ||||
|     g_free(hist->bins); | ||||
|     hist->bins = g_new0(uint64_t, hist->nbins); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void block_latency_histograms_clear(BlockAcctStats *stats) | ||||
| { | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < BLOCK_MAX_IOTYPE; i++) { | ||||
|         BlockLatencyHistogram *hist = &stats->latency_histogram[i]; | ||||
|         g_free(hist->bins); | ||||
|         g_free(hist->boundaries); | ||||
|         memset(hist, 0, sizeof(*hist)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, | ||||
|                                  bool failed) | ||||
| { | ||||
| @@ -204,9 +116,6 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, | ||||
|         stats->nr_ops[cookie->type]++; | ||||
|     } | ||||
|  | ||||
|     block_latency_histogram_account(&stats->latency_histogram[cookie->type], | ||||
|                                     latency_ns); | ||||
|  | ||||
|     if (!failed || stats->account_failed) { | ||||
|         stats->total_time_ns[cookie->type] += latency_ns; | ||||
|         stats->last_access_time_ns = time_ns; | ||||
|   | ||||
							
								
								
									
										369
									
								
								block/backup.c
									
									
									
									
									
								
							
							
						
						
									
										369
									
								
								block/backup.c
									
									
									
									
									
								
							| @@ -27,13 +27,7 @@ | ||||
| #include "qemu/error-report.h" | ||||
|  | ||||
| #define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16) | ||||
|  | ||||
| typedef struct CowRequest { | ||||
|     int64_t start_byte; | ||||
|     int64_t end_byte; | ||||
|     QLIST_ENTRY(CowRequest) list; | ||||
|     CoQueue wait_queue; /* coroutines blocked on this request */ | ||||
| } CowRequest; | ||||
| #define SLICE_TIME 100000000ULL /* ns */ | ||||
|  | ||||
| typedef struct BackupBlockJob { | ||||
|     BlockJob common; | ||||
| @@ -41,10 +35,10 @@ typedef struct BackupBlockJob { | ||||
|     /* bitmap for sync=incremental */ | ||||
|     BdrvDirtyBitmap *sync_bitmap; | ||||
|     MirrorSyncMode sync_mode; | ||||
|     RateLimit limit; | ||||
|     BlockdevOnError on_source_error; | ||||
|     BlockdevOnError on_target_error; | ||||
|     CoRwlock flush_rwlock; | ||||
|     uint64_t len; | ||||
|     uint64_t bytes_read; | ||||
|     int64_t cluster_size; | ||||
|     bool compress; | ||||
| @@ -52,14 +46,8 @@ typedef struct BackupBlockJob { | ||||
|     QLIST_HEAD(, CowRequest) inflight_reqs; | ||||
|  | ||||
|     HBitmap *copy_bitmap; | ||||
|     bool use_copy_range; | ||||
|     int64_t copy_range_size; | ||||
|  | ||||
|     bool serialize_target_writes; | ||||
| } BackupBlockJob; | ||||
|  | ||||
| static const BlockJobDriver backup_job_driver; | ||||
|  | ||||
| /* See if in-flight requests overlap and wait for them to complete */ | ||||
| static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job, | ||||
|                                                        int64_t start, | ||||
| @@ -97,101 +85,19 @@ static void cow_request_end(CowRequest *req) | ||||
|     qemu_co_queue_restart_all(&req->wait_queue); | ||||
| } | ||||
|  | ||||
| /* Copy range to target with a bounce buffer and return the bytes copied. If | ||||
|  * error occurred, return a negative error number */ | ||||
| static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, | ||||
|                                                       int64_t start, | ||||
|                                                       int64_t end, | ||||
|                                                       bool is_write_notifier, | ||||
|                                                       bool *error_is_read, | ||||
|                                                       void **bounce_buffer) | ||||
| { | ||||
|     int ret; | ||||
|     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); | ||||
|     } | ||||
|     qemu_iovec_init_buf(&qiov, *bounce_buffer, nbytes); | ||||
|  | ||||
|     ret = blk_co_preadv(blk, start, qiov.size, &qiov, read_flags); | ||||
|     if (ret < 0) { | ||||
|         trace_backup_do_cow_read_fail(job, start, ret); | ||||
|         if (error_is_read) { | ||||
|             *error_is_read = true; | ||||
|         } | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     if (qemu_iovec_is_zero(&qiov)) { | ||||
|         ret = blk_co_pwrite_zeroes(job->target, start, | ||||
|                                    qiov.size, write_flags | BDRV_REQ_MAY_UNMAP); | ||||
|     } else { | ||||
|         ret = blk_co_pwritev(job->target, start, | ||||
|                              qiov.size, &qiov, write_flags | | ||||
|                              (job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0)); | ||||
|     } | ||||
|     if (ret < 0) { | ||||
|         trace_backup_do_cow_write_fail(job, start, ret); | ||||
|         if (error_is_read) { | ||||
|             *error_is_read = false; | ||||
|         } | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     return nbytes; | ||||
| fail: | ||||
|     hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1); | ||||
|     return ret; | ||||
|  | ||||
| } | ||||
|  | ||||
| /* Copy range to target and return the bytes copied. If error occurred, return a | ||||
|  * negative error number. */ | ||||
| static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job, | ||||
|                                                 int64_t start, | ||||
|                                                 int64_t end, | ||||
|                                                 bool is_write_notifier) | ||||
| { | ||||
|     int ret; | ||||
|     int nr_clusters; | ||||
|     BlockBackend *blk = job->common.blk; | ||||
|     int nbytes; | ||||
|     int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0; | ||||
|     int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0; | ||||
|  | ||||
|     assert(QEMU_IS_ALIGNED(job->copy_range_size, job->cluster_size)); | ||||
|     nbytes = MIN(job->copy_range_size, end - start); | ||||
|     nr_clusters = DIV_ROUND_UP(nbytes, job->cluster_size); | ||||
|     hbitmap_reset(job->copy_bitmap, start / job->cluster_size, | ||||
|                   nr_clusters); | ||||
|     ret = blk_co_copy_range(blk, start, job->target, start, nbytes, | ||||
|                             read_flags, write_flags); | ||||
|     if (ret < 0) { | ||||
|         trace_backup_do_cow_copy_range_fail(job, start, ret); | ||||
|         hbitmap_set(job->copy_bitmap, start / job->cluster_size, | ||||
|                     nr_clusters); | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     return nbytes; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn backup_do_cow(BackupBlockJob *job, | ||||
|                                       int64_t offset, uint64_t bytes, | ||||
|                                       bool *error_is_read, | ||||
|                                       bool is_write_notifier) | ||||
| { | ||||
|     BlockBackend *blk = job->common.blk; | ||||
|     CowRequest cow_request; | ||||
|     struct iovec iov; | ||||
|     QEMUIOVector bounce_qiov; | ||||
|     void *bounce_buffer = NULL; | ||||
|     int ret = 0; | ||||
|     int64_t start, end; /* bytes */ | ||||
|     void *bounce_buffer = NULL; | ||||
|     int n; /* bytes */ | ||||
|  | ||||
|     qemu_co_rwlock_rdlock(&job->flush_rwlock); | ||||
|  | ||||
| @@ -203,38 +109,60 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job, | ||||
|     wait_for_overlapping_requests(job, start, end); | ||||
|     cow_request_begin(&cow_request, job, start, end); | ||||
|  | ||||
|     while (start < end) { | ||||
|     for (; start < end; start += job->cluster_size) { | ||||
|         if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) { | ||||
|             trace_backup_do_cow_skip(job, start); | ||||
|             start += job->cluster_size; | ||||
|             continue; /* already copied */ | ||||
|         } | ||||
|         hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1); | ||||
|  | ||||
|         trace_backup_do_cow_process(job, start); | ||||
|  | ||||
|         if (job->use_copy_range) { | ||||
|             ret = backup_cow_with_offload(job, start, end, is_write_notifier); | ||||
|             if (ret < 0) { | ||||
|                 job->use_copy_range = false; | ||||
|             } | ||||
|         n = MIN(job->cluster_size, job->common.len - start); | ||||
|  | ||||
|         if (!bounce_buffer) { | ||||
|             bounce_buffer = blk_blockalign(blk, job->cluster_size); | ||||
|         } | ||||
|         if (!job->use_copy_range) { | ||||
|             ret = backup_cow_with_bounce_buffer(job, start, end, is_write_notifier, | ||||
|                                                 error_is_read, &bounce_buffer); | ||||
|         iov.iov_base = bounce_buffer; | ||||
|         iov.iov_len = n; | ||||
|         qemu_iovec_init_external(&bounce_qiov, &iov, 1); | ||||
|  | ||||
|         ret = blk_co_preadv(blk, start, bounce_qiov.size, &bounce_qiov, | ||||
|                             is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0); | ||||
|         if (ret < 0) { | ||||
|             trace_backup_do_cow_read_fail(job, start, ret); | ||||
|             if (error_is_read) { | ||||
|                 *error_is_read = true; | ||||
|             } | ||||
|             hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1); | ||||
|             goto out; | ||||
|         } | ||||
|  | ||||
|         if (buffer_is_zero(iov.iov_base, iov.iov_len)) { | ||||
|             ret = blk_co_pwrite_zeroes(job->target, start, | ||||
|                                        bounce_qiov.size, BDRV_REQ_MAY_UNMAP); | ||||
|         } else { | ||||
|             ret = blk_co_pwritev(job->target, start, | ||||
|                                  bounce_qiov.size, &bounce_qiov, | ||||
|                                  job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0); | ||||
|         } | ||||
|         if (ret < 0) { | ||||
|             break; | ||||
|             trace_backup_do_cow_write_fail(job, start, ret); | ||||
|             if (error_is_read) { | ||||
|                 *error_is_read = false; | ||||
|             } | ||||
|             hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1); | ||||
|             goto out; | ||||
|         } | ||||
|  | ||||
|         /* Publish progress, guest I/O counts as progress too.  Note that the | ||||
|          * offset field is an opaque progress value, it is not a disk offset. | ||||
|          */ | ||||
|         start += ret; | ||||
|         job->bytes_read += ret; | ||||
|         job_progress_update(&job->common.job, ret); | ||||
|         ret = 0; | ||||
|         job->bytes_read += n; | ||||
|         job->common.offset += n; | ||||
|     } | ||||
|  | ||||
| out: | ||||
|     if (bounce_buffer) { | ||||
|         qemu_vfree(bounce_buffer); | ||||
|     } | ||||
| @@ -262,12 +190,23 @@ static int coroutine_fn backup_before_write_notify( | ||||
|     return backup_do_cow(job, req->offset, req->bytes, NULL, true); | ||||
| } | ||||
|  | ||||
| static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp) | ||||
| { | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common); | ||||
|  | ||||
|     if (speed < 0) { | ||||
|         error_setg(errp, QERR_INVALID_PARAMETER, "speed"); | ||||
|         return; | ||||
|     } | ||||
|     ratelimit_set_speed(&s->limit, speed, SLICE_TIME); | ||||
| } | ||||
|  | ||||
| static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) | ||||
| { | ||||
|     BdrvDirtyBitmap *bm; | ||||
|     BlockDriverState *bs = blk_bs(job->common.blk); | ||||
|  | ||||
|     if (ret < 0) { | ||||
|     if (ret < 0 || block_job_is_cancelled(&job->common)) { | ||||
|         /* Merge the successor back into the parent, delete nothing. */ | ||||
|         bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL); | ||||
|         assert(bm); | ||||
| @@ -278,25 +217,25 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void backup_commit(Job *job) | ||||
| static void backup_commit(BlockJob *job) | ||||
| { | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common); | ||||
|     if (s->sync_bitmap) { | ||||
|         backup_cleanup_sync_bitmap(s, 0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void backup_abort(Job *job) | ||||
| static void backup_abort(BlockJob *job) | ||||
| { | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common); | ||||
|     if (s->sync_bitmap) { | ||||
|         backup_cleanup_sync_bitmap(s, -1); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void backup_clean(Job *job) | ||||
| static void backup_clean(BlockJob *job) | ||||
| { | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common); | ||||
|     assert(s->target); | ||||
|     blk_unref(s->target); | ||||
|     s->target = NULL; | ||||
| @@ -314,7 +253,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp) | ||||
|     BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); | ||||
|     int64_t len; | ||||
|  | ||||
|     assert(block_job_driver(job) == &backup_job_driver); | ||||
|     assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); | ||||
|  | ||||
|     if (backup_job->sync_mode != MIRROR_SYNC_MODE_NONE) { | ||||
|         error_setg(errp, "The backup job only supports block checkpoint in" | ||||
| @@ -322,10 +261,41 @@ void backup_do_checkpoint(BlockJob *job, Error **errp) | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     len = DIV_ROUND_UP(backup_job->len, backup_job->cluster_size); | ||||
|     len = DIV_ROUND_UP(backup_job->common.len, backup_job->cluster_size); | ||||
|     hbitmap_set(backup_job->copy_bitmap, 0, len); | ||||
| } | ||||
|  | ||||
| void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset, | ||||
|                                           uint64_t bytes) | ||||
| { | ||||
|     BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); | ||||
|     int64_t start, end; | ||||
|  | ||||
|     assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); | ||||
|  | ||||
|     start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size); | ||||
|     end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size); | ||||
|     wait_for_overlapping_requests(backup_job, start, end); | ||||
| } | ||||
|  | ||||
| void backup_cow_request_begin(CowRequest *req, BlockJob *job, | ||||
|                               int64_t offset, uint64_t bytes) | ||||
| { | ||||
|     BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); | ||||
|     int64_t start, end; | ||||
|  | ||||
|     assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); | ||||
|  | ||||
|     start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size); | ||||
|     end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size); | ||||
|     cow_request_begin(req, backup_job, start, end); | ||||
| } | ||||
|  | ||||
| void backup_cow_request_end(CowRequest *req) | ||||
| { | ||||
|     cow_request_end(req); | ||||
| } | ||||
|  | ||||
| static void backup_drain(BlockJob *job) | ||||
| { | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common); | ||||
| @@ -353,21 +323,37 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job, | ||||
|     } | ||||
| } | ||||
|  | ||||
| typedef struct { | ||||
|     int ret; | ||||
| } BackupCompleteData; | ||||
|  | ||||
| static void backup_complete(BlockJob *job, void *opaque) | ||||
| { | ||||
|     BackupCompleteData *data = opaque; | ||||
|  | ||||
|     block_job_completed(job, data->ret); | ||||
|     g_free(data); | ||||
| } | ||||
|  | ||||
| static bool coroutine_fn yield_and_check(BackupBlockJob *job) | ||||
| { | ||||
|     uint64_t delay_ns; | ||||
|  | ||||
|     if (job_is_cancelled(&job->common.job)) { | ||||
|     if (block_job_is_cancelled(&job->common)) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /* We need to yield even for delay_ns = 0 so that bdrv_drain_all() can | ||||
|      * return. Without a yield, the VM would not reboot. */ | ||||
|     delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read); | ||||
|     job->bytes_read = 0; | ||||
|     job_sleep_ns(&job->common.job, delay_ns); | ||||
|     /* we need to yield so that bdrv_drain_all() returns. | ||||
|      * (without, VM does not reboot) | ||||
|      */ | ||||
|     if (job->common.speed) { | ||||
|         uint64_t delay_ns = ratelimit_calculate_delay(&job->limit, | ||||
|                                                       job->bytes_read); | ||||
|         job->bytes_read = 0; | ||||
|         block_job_sleep_ns(&job->common, delay_ns); | ||||
|     } else { | ||||
|         block_job_sleep_ns(&job->common, 0); | ||||
|     } | ||||
|  | ||||
|     if (job_is_cancelled(&job->common.job)) { | ||||
|     if (block_job_is_cancelled(&job->common)) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
| @@ -419,8 +405,7 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset, | ||||
|                                              UINT64_MAX); | ||||
|         offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset); | ||||
|         if (offset == -1) { | ||||
|             hbitmap_set(job->copy_bitmap, cluster, end - cluster); | ||||
|             break; | ||||
| @@ -435,66 +420,64 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) | ||||
|         bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size); | ||||
|     } | ||||
|  | ||||
|     /* TODO job_progress_set_remaining() would make more sense */ | ||||
|     job_progress_update(&job->common.job, | ||||
|         job->len - hbitmap_count(job->copy_bitmap) * job->cluster_size); | ||||
|     job->common.offset = job->common.len - | ||||
|                          hbitmap_count(job->copy_bitmap) * job->cluster_size; | ||||
|  | ||||
|     bdrv_dirty_iter_free(dbi); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn backup_run(Job *job, Error **errp) | ||||
| static void coroutine_fn backup_run(void *opaque) | ||||
| { | ||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); | ||||
|     BlockDriverState *bs = blk_bs(s->common.blk); | ||||
|     BackupBlockJob *job = opaque; | ||||
|     BackupCompleteData *data; | ||||
|     BlockDriverState *bs = blk_bs(job->common.blk); | ||||
|     int64_t offset, nb_clusters; | ||||
|     int ret = 0; | ||||
|  | ||||
|     QLIST_INIT(&s->inflight_reqs); | ||||
|     qemu_co_rwlock_init(&s->flush_rwlock); | ||||
|     QLIST_INIT(&job->inflight_reqs); | ||||
|     qemu_co_rwlock_init(&job->flush_rwlock); | ||||
|  | ||||
|     nb_clusters = DIV_ROUND_UP(s->len, s->cluster_size); | ||||
|     job_progress_set_remaining(job, s->len); | ||||
|  | ||||
|     s->copy_bitmap = hbitmap_alloc(nb_clusters, 0); | ||||
|     if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { | ||||
|         backup_incremental_init_copy_bitmap(s); | ||||
|     nb_clusters = DIV_ROUND_UP(job->common.len, job->cluster_size); | ||||
|     job->copy_bitmap = hbitmap_alloc(nb_clusters, 0); | ||||
|     if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { | ||||
|         backup_incremental_init_copy_bitmap(job); | ||||
|     } else { | ||||
|         hbitmap_set(s->copy_bitmap, 0, nb_clusters); | ||||
|         hbitmap_set(job->copy_bitmap, 0, nb_clusters); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     s->before_write.notify = backup_before_write_notify; | ||||
|     bdrv_add_before_write_notifier(bs, &s->before_write); | ||||
|     job->before_write.notify = backup_before_write_notify; | ||||
|     bdrv_add_before_write_notifier(bs, &job->before_write); | ||||
|  | ||||
|     if (s->sync_mode == MIRROR_SYNC_MODE_NONE) { | ||||
|     if (job->sync_mode == MIRROR_SYNC_MODE_NONE) { | ||||
|         /* All bits are set in copy_bitmap to allow any cluster to be copied. | ||||
|          * This does not actually require them to be copied. */ | ||||
|         while (!job_is_cancelled(job)) { | ||||
|         while (!block_job_is_cancelled(&job->common)) { | ||||
|             /* Yield until the job is cancelled.  We just let our before_write | ||||
|              * notify callback service CoW requests. */ | ||||
|             job_yield(job); | ||||
|             block_job_yield(&job->common); | ||||
|         } | ||||
|     } else if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { | ||||
|         ret = backup_run_incremental(s); | ||||
|     } else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { | ||||
|         ret = backup_run_incremental(job); | ||||
|     } else { | ||||
|         /* Both FULL and TOP SYNC_MODE's require copying.. */ | ||||
|         for (offset = 0; offset < s->len; | ||||
|              offset += s->cluster_size) { | ||||
|         for (offset = 0; offset < job->common.len; | ||||
|              offset += job->cluster_size) { | ||||
|             bool error_is_read; | ||||
|             int alloced = 0; | ||||
|  | ||||
|             if (yield_and_check(s)) { | ||||
|             if (yield_and_check(job)) { | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             if (s->sync_mode == MIRROR_SYNC_MODE_TOP) { | ||||
|             if (job->sync_mode == MIRROR_SYNC_MODE_TOP) { | ||||
|                 int i; | ||||
|                 int64_t n; | ||||
|  | ||||
|                 /* Check to see if these blocks are already in the | ||||
|                  * backing file. */ | ||||
|  | ||||
|                 for (i = 0; i < s->cluster_size;) { | ||||
|                 for (i = 0; i < job->cluster_size;) { | ||||
|                     /* bdrv_is_allocated() only returns true/false based | ||||
|                      * on the first set of sectors it comes across that | ||||
|                      * are are all in the same state. | ||||
| @@ -503,7 +486,7 @@ static int coroutine_fn backup_run(Job *job, Error **errp) | ||||
|                      * needed but at some point that is always the case. */ | ||||
|                     alloced = | ||||
|                         bdrv_is_allocated(bs, offset + i, | ||||
|                                           s->cluster_size - i, &n); | ||||
|                                           job->cluster_size - i, &n); | ||||
|                     i += n; | ||||
|  | ||||
|                     if (alloced || n == 0) { | ||||
| @@ -521,45 +504,43 @@ static int coroutine_fn backup_run(Job *job, Error **errp) | ||||
|             if (alloced < 0) { | ||||
|                 ret = alloced; | ||||
|             } else { | ||||
|                 ret = backup_do_cow(s, offset, s->cluster_size, | ||||
|                 ret = backup_do_cow(job, offset, job->cluster_size, | ||||
|                                     &error_is_read, false); | ||||
|             } | ||||
|             if (ret < 0) { | ||||
|                 /* Depending on error action, fail now or retry cluster */ | ||||
|                 BlockErrorAction action = | ||||
|                     backup_error_action(s, error_is_read, -ret); | ||||
|                     backup_error_action(job, error_is_read, -ret); | ||||
|                 if (action == BLOCK_ERROR_ACTION_REPORT) { | ||||
|                     break; | ||||
|                 } else { | ||||
|                     offset -= s->cluster_size; | ||||
|                     offset -= job->cluster_size; | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     notifier_with_return_remove(&s->before_write); | ||||
|     notifier_with_return_remove(&job->before_write); | ||||
|  | ||||
|     /* wait until pending backup_do_cow() calls have completed */ | ||||
|     qemu_co_rwlock_wrlock(&s->flush_rwlock); | ||||
|     qemu_co_rwlock_unlock(&s->flush_rwlock); | ||||
|     hbitmap_free(s->copy_bitmap); | ||||
|     qemu_co_rwlock_wrlock(&job->flush_rwlock); | ||||
|     qemu_co_rwlock_unlock(&job->flush_rwlock); | ||||
|     hbitmap_free(job->copy_bitmap); | ||||
|  | ||||
|     return ret; | ||||
|     data = g_malloc(sizeof(*data)); | ||||
|     data->ret = ret; | ||||
|     block_job_defer_to_main_loop(&job->common, backup_complete, data); | ||||
| } | ||||
|  | ||||
| static const BlockJobDriver backup_job_driver = { | ||||
|     .job_driver = { | ||||
|         .instance_size          = sizeof(BackupBlockJob), | ||||
|         .job_type               = JOB_TYPE_BACKUP, | ||||
|         .free                   = block_job_free, | ||||
|         .user_resume            = block_job_user_resume, | ||||
|         .drain                  = block_job_drain, | ||||
|         .run                    = backup_run, | ||||
|         .commit                 = backup_commit, | ||||
|         .abort                  = backup_abort, | ||||
|         .clean                  = backup_clean, | ||||
|     }, | ||||
|     .instance_size          = sizeof(BackupBlockJob), | ||||
|     .job_type               = BLOCK_JOB_TYPE_BACKUP, | ||||
|     .start                  = backup_run, | ||||
|     .set_speed              = backup_set_speed, | ||||
|     .commit                 = backup_commit, | ||||
|     .abort                  = backup_abort, | ||||
|     .clean                  = backup_clean, | ||||
|     .attached_aio_context   = backup_attached_aio_context, | ||||
|     .drain                  = backup_drain, | ||||
| }; | ||||
| @@ -572,7 +553,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||||
|                   BlockdevOnError on_target_error, | ||||
|                   int creation_flags, | ||||
|                   BlockCompletionFunc *cb, void *opaque, | ||||
|                   JobTxn *txn, Error **errp) | ||||
|                   BlockJobTxn *txn, Error **errp) | ||||
| { | ||||
|     int64_t len; | ||||
|     BlockDriverInfo bdi; | ||||
| @@ -639,8 +620,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||||
|         goto error; | ||||
|     } | ||||
|  | ||||
|     /* job->len is fixed, so we can't allow resize */ | ||||
|     job = block_job_create(job_id, &backup_job_driver, txn, bs, | ||||
|     /* job->common.len is fixed, so we can't allow resize */ | ||||
|     job = block_job_create(job_id, &backup_job_driver, bs, | ||||
|                            BLK_PERM_CONSISTENT_READ, | ||||
|                            BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | | ||||
|                            BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD, | ||||
| @@ -665,9 +646,6 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||||
|                        sync_bitmap : NULL; | ||||
|     job->compress = compress; | ||||
|  | ||||
|     /* Detect image-fleecing (and similar) schemes */ | ||||
|     job->serialize_target_writes = bdrv_chain_contains(target, bs); | ||||
|  | ||||
|     /* If there is no backing file on the target, we cannot rely on COW if our | ||||
|      * backup cluster size is smaller than the target cluster size. Even for | ||||
|      * targets with a backing file, try to avoid COW if possible. */ | ||||
| @@ -694,17 +672,12 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||||
|     } else { | ||||
|         job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size); | ||||
|     } | ||||
|     job->use_copy_range = true; | ||||
|     job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk), | ||||
|                                         blk_get_max_transfer(job->target)); | ||||
|     job->copy_range_size = MAX(job->cluster_size, | ||||
|                                QEMU_ALIGN_UP(job->copy_range_size, | ||||
|                                              job->cluster_size)); | ||||
|  | ||||
|     /* Required permissions are already taken with target's blk_new() */ | ||||
|     block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, | ||||
|                        &error_abort); | ||||
|     job->len = len; | ||||
|     job->common.len = len; | ||||
|     block_job_txn_add_job(txn, &job->common); | ||||
|  | ||||
|     return &job->common; | ||||
|  | ||||
| @@ -713,8 +686,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||||
|         bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL); | ||||
|     } | ||||
|     if (job) { | ||||
|         backup_clean(&job->common.job); | ||||
|         job_early_fail(&job->common.job); | ||||
|         backup_clean(&job->common); | ||||
|         block_job_early_fail(&job->common); | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
|   | ||||
| @@ -305,7 +305,7 @@ static void blkdebug_parse_filename(const char *filename, QDict *options, | ||||
|  | ||||
|     if (c != filename) { | ||||
|         QString *config_path; | ||||
|         config_path = qstring_from_substr(filename, 0, c - filename); | ||||
|         config_path = qstring_from_substr(filename, 0, c - filename - 1); | ||||
|         qdict_put(options, "config", config_path); | ||||
|     } | ||||
|  | ||||
| @@ -398,11 +398,10 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | | ||||
|         (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); | ||||
|     bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | | ||||
|         ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & | ||||
|             bs->file->bs->supported_zero_flags); | ||||
|     bs->supported_write_flags = BDRV_REQ_FUA & | ||||
|         bs->file->bs->supported_write_flags; | ||||
|     bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & | ||||
|         bs->file->bs->supported_zero_flags; | ||||
|     ret = -EINVAL; | ||||
|  | ||||
|     /* Set alignment overrides */ | ||||
| @@ -625,7 +624,7 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs, | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     return bdrv_co_pdiscard(bs->file, offset, bytes); | ||||
|     return bdrv_co_pdiscard(bs->file->bs, offset, bytes); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs, | ||||
| @@ -811,37 +810,52 @@ static int64_t blkdebug_getlength(BlockDriverState *bs) | ||||
|     return bdrv_getlength(bs->file->bs); | ||||
| } | ||||
|  | ||||
| static void blkdebug_refresh_filename(BlockDriverState *bs) | ||||
| static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options) | ||||
| { | ||||
|     BDRVBlkdebugState *s = bs->opaque; | ||||
|     QDict *opts; | ||||
|     const QDictEntry *e; | ||||
|     int ret; | ||||
|     bool force_json = false; | ||||
|  | ||||
|     if (!bs->file->bs->exact_filename[0]) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     for (e = qdict_first(bs->full_open_options); e; | ||||
|          e = qdict_next(bs->full_open_options, e)) | ||||
|     { | ||||
|         /* Real child options are under "image", but "x-image" may | ||||
|          * contain a filename */ | ||||
|     for (e = qdict_first(options); e; e = qdict_next(options, e)) { | ||||
|         if (strcmp(qdict_entry_key(e), "config") && | ||||
|             strcmp(qdict_entry_key(e), "image") && | ||||
|             strcmp(qdict_entry_key(e), "x-image") && | ||||
|             strcmp(qdict_entry_key(e), "driver")) | ||||
|             strcmp(qdict_entry_key(e), "x-image")) | ||||
|         { | ||||
|             return; | ||||
|             force_json = true; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename), | ||||
|                    "blkdebug:%s:%s", | ||||
|                    s->config_file ?: "", bs->file->bs->exact_filename); | ||||
|     if (ret >= sizeof(bs->exact_filename)) { | ||||
|         /* An overflow makes the filename unusable, so do not report any */ | ||||
|         bs->exact_filename[0] = 0; | ||||
|     if (force_json && !bs->file->bs->full_open_options) { | ||||
|         /* The config file cannot be recreated, so creating a plain filename | ||||
|          * is impossible */ | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!force_json && bs->file->bs->exact_filename[0]) { | ||||
|         int ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename), | ||||
|                            "blkdebug:%s:%s", s->config_file ?: "", | ||||
|                            bs->file->bs->exact_filename); | ||||
|         if (ret >= sizeof(bs->exact_filename)) { | ||||
|             /* An overflow makes the filename unusable, so do not report any */ | ||||
|             bs->exact_filename[0] = 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     opts = qdict_new(); | ||||
|     qdict_put_str(opts, "driver", "blkdebug"); | ||||
|  | ||||
|     QINCREF(bs->file->bs->full_open_options); | ||||
|     qdict_put(opts, "image", bs->file->bs->full_open_options); | ||||
|  | ||||
|     for (e = qdict_first(options); e; e = qdict_next(options, e)) { | ||||
|         if (strcmp(qdict_entry_key(e), "x-image")) { | ||||
|             qobject_incref(qdict_entry_value(e)); | ||||
|             qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     bs->full_open_options = opts; | ||||
| } | ||||
|  | ||||
| static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp) | ||||
| @@ -874,20 +888,6 @@ static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static const char *const blkdebug_strong_runtime_opts[] = { | ||||
|     "config", | ||||
|     "inject-error.", | ||||
|     "set-state.", | ||||
|     "align", | ||||
|     "max-transfer", | ||||
|     "opt-write-zero", | ||||
|     "max-write-zero", | ||||
|     "opt-discard", | ||||
|     "max-discard", | ||||
|  | ||||
|     NULL | ||||
| }; | ||||
|  | ||||
| static BlockDriver bdrv_blkdebug = { | ||||
|     .format_name            = "blkdebug", | ||||
|     .protocol_name          = "blkdebug", | ||||
| @@ -917,8 +917,6 @@ static BlockDriver bdrv_blkdebug = { | ||||
|                                 = blkdebug_debug_remove_breakpoint, | ||||
|     .bdrv_debug_resume          = blkdebug_debug_resume, | ||||
|     .bdrv_debug_is_suspended    = blkdebug_debug_is_suspended, | ||||
|  | ||||
|     .strong_runtime_opts        = blkdebug_strong_runtime_opts, | ||||
| }; | ||||
|  | ||||
| static void bdrv_blkdebug_init(void) | ||||
|   | ||||
| @@ -1,532 +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_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 const char *const blk_log_writes_strong_runtime_opts[] = { | ||||
|     "log-append", | ||||
|     "log-sector-size", | ||||
|  | ||||
|     NULL | ||||
| }; | ||||
|  | ||||
| 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_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, | ||||
|     .strong_runtime_opts    = blk_log_writes_strong_runtime_opts, | ||||
| }; | ||||
|  | ||||
| static void bdrv_blk_log_writes_init(void) | ||||
| { | ||||
|     bdrv_register(&bdrv_blk_log_writes); | ||||
| } | ||||
|  | ||||
| block_init(bdrv_blk_log_writes_init); | ||||
							
								
								
									
										10
									
								
								block/blkreplay.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										10
									
								
								block/blkreplay.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -35,14 +35,15 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; | ||||
|     bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; | ||||
|  | ||||
|     ret = 0; | ||||
| fail: | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static void blkreplay_close(BlockDriverState *bs) | ||||
| { | ||||
| } | ||||
|  | ||||
| static int64_t blkreplay_getlength(BlockDriverState *bs) | ||||
| { | ||||
|     return bdrv_getlength(bs->file->bs); | ||||
| @@ -109,7 +110,7 @@ static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs, | ||||
|                                               int64_t offset, int bytes) | ||||
| { | ||||
|     uint64_t reqid = blkreplay_next_id(); | ||||
|     int ret = bdrv_co_pdiscard(bs->file, offset, bytes); | ||||
|     int ret = bdrv_co_pdiscard(bs->file->bs, offset, bytes); | ||||
|     block_request_create(reqid, bs, qemu_coroutine_self()); | ||||
|     qemu_coroutine_yield(); | ||||
|  | ||||
| @@ -131,6 +132,7 @@ static BlockDriver bdrv_blkreplay = { | ||||
|     .instance_size          = 0, | ||||
|  | ||||
|     .bdrv_open              = blkreplay_open, | ||||
|     .bdrv_close             = blkreplay_close, | ||||
|     .bdrv_child_perm        = bdrv_filter_default_perms, | ||||
|     .bdrv_getlength         = blkreplay_getlength, | ||||
|  | ||||
|   | ||||
| @@ -80,7 +80,7 @@ static void blkverify_parse_filename(const char *filename, QDict *options, | ||||
|     } | ||||
|  | ||||
|     /* TODO Implement option pass-through and set raw.filename here */ | ||||
|     raw_path = qstring_from_substr(filename, 0, c - filename); | ||||
|     raw_path = qstring_from_substr(filename, 0, c - filename - 1); | ||||
|     qdict_put(options, "x-raw", raw_path); | ||||
|  | ||||
|     /* TODO Allow multi-level nesting and set file.filename here */ | ||||
| @@ -141,9 +141,6 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; | ||||
|     bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; | ||||
|  | ||||
|     ret = 0; | ||||
| fail: | ||||
|     qemu_opts_del(opts); | ||||
| @@ -281,10 +278,27 @@ static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs, | ||||
|     return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate); | ||||
| } | ||||
|  | ||||
| static void blkverify_refresh_filename(BlockDriverState *bs) | ||||
| static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options) | ||||
| { | ||||
|     BDRVBlkverifyState *s = bs->opaque; | ||||
|  | ||||
|     /* bs->file->bs has already been refreshed */ | ||||
|     bdrv_refresh_filename(s->test_file->bs); | ||||
|  | ||||
|     if (bs->file->bs->full_open_options | ||||
|         && s->test_file->bs->full_open_options) | ||||
|     { | ||||
|         QDict *opts = qdict_new(); | ||||
|         qdict_put_str(opts, "driver", "blkverify"); | ||||
|  | ||||
|         QINCREF(bs->file->bs->full_open_options); | ||||
|         qdict_put(opts, "raw", bs->file->bs->full_open_options); | ||||
|         QINCREF(s->test_file->bs->full_open_options); | ||||
|         qdict_put(opts, "test", s->test_file->bs->full_open_options); | ||||
|  | ||||
|         bs->full_open_options = opts; | ||||
|     } | ||||
|  | ||||
|     if (bs->file->bs->exact_filename[0] | ||||
|         && s->test_file->bs->exact_filename[0]) | ||||
|     { | ||||
| @@ -299,15 +313,6 @@ static void blkverify_refresh_filename(BlockDriverState *bs) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static char *blkverify_dirname(BlockDriverState *bs, Error **errp) | ||||
| { | ||||
|     /* In general, there are two BDSs with different dirnames below this one; | ||||
|      * so there is no unique dirname we could return (unless both are equal by | ||||
|      * chance). Therefore, to be consistent, just always return NULL. */ | ||||
|     error_setg(errp, "Cannot generate a base directory for blkverify nodes"); | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| static BlockDriver bdrv_blkverify = { | ||||
|     .format_name                      = "blkverify", | ||||
|     .protocol_name                    = "blkverify", | ||||
| @@ -319,7 +324,6 @@ static BlockDriver bdrv_blkverify = { | ||||
|     .bdrv_child_perm                  = bdrv_filter_default_perms, | ||||
|     .bdrv_getlength                   = blkverify_getlength, | ||||
|     .bdrv_refresh_filename            = blkverify_refresh_filename, | ||||
|     .bdrv_dirname                     = blkverify_dirname, | ||||
|  | ||||
|     .bdrv_co_preadv                   = blkverify_co_preadv, | ||||
|     .bdrv_co_pwritev                  = blkverify_co_pwritev, | ||||
|   | ||||
| @@ -31,13 +31,6 @@ | ||||
|  | ||||
| static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb); | ||||
|  | ||||
| typedef struct BlockBackendAioNotifier { | ||||
|     void (*attached_aio_context)(AioContext *new_context, void *opaque); | ||||
|     void (*detach_aio_context)(void *opaque); | ||||
|     void *opaque; | ||||
|     QLIST_ENTRY(BlockBackendAioNotifier) list; | ||||
| } BlockBackendAioNotifier; | ||||
|  | ||||
| struct BlockBackend { | ||||
|     char *name; | ||||
|     int refcnt; | ||||
| @@ -47,7 +40,9 @@ struct BlockBackend { | ||||
|     QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */ | ||||
|     BlockBackendPublic public; | ||||
|  | ||||
|     DeviceState *dev;           /* attached device model, if any */ | ||||
|     void *dev;                  /* attached device model, if any */ | ||||
|     bool legacy_dev;            /* true if dev is not a DeviceState */ | ||||
|     /* TODO change to DeviceState when all users are qdevified */ | ||||
|     const BlockDevOps *dev_ops; | ||||
|     void *dev_opaque; | ||||
|  | ||||
| @@ -74,7 +69,6 @@ struct BlockBackend { | ||||
|     bool allow_write_beyond_eof; | ||||
|  | ||||
|     NotifierList remove_bs_notifiers, insert_bs_notifiers; | ||||
|     QLIST_HEAD(, BlockBackendAioNotifier) aio_notifiers; | ||||
|  | ||||
|     int quiesce_counter; | ||||
|     VMChangeStateEntry *vmsh; | ||||
| @@ -86,6 +80,7 @@ struct BlockBackend { | ||||
|      * Accessed with atomic ops. | ||||
|      */ | ||||
|     unsigned int in_flight; | ||||
|     AioWait wait; | ||||
| }; | ||||
|  | ||||
| typedef struct BlockBackendAIOCB { | ||||
| @@ -118,7 +113,6 @@ static void blk_root_inherit_options(int *child_flags, QDict *child_options, | ||||
|     abort(); | ||||
| } | ||||
| static void blk_root_drained_begin(BdrvChild *child); | ||||
| static bool blk_root_drained_poll(BdrvChild *child); | ||||
| static void blk_root_drained_end(BdrvChild *child); | ||||
|  | ||||
| static void blk_root_change_media(BdrvChild *child, bool load); | ||||
| @@ -253,36 +247,6 @@ static int blk_root_inactivate(BdrvChild *child) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void blk_root_attach(BdrvChild *child) | ||||
| { | ||||
|     BlockBackend *blk = child->opaque; | ||||
|     BlockBackendAioNotifier *notifier; | ||||
|  | ||||
|     trace_blk_root_attach(child, blk, child->bs); | ||||
|  | ||||
|     QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { | ||||
|         bdrv_add_aio_context_notifier(child->bs, | ||||
|                 notifier->attached_aio_context, | ||||
|                 notifier->detach_aio_context, | ||||
|                 notifier->opaque); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void blk_root_detach(BdrvChild *child) | ||||
| { | ||||
|     BlockBackend *blk = child->opaque; | ||||
|     BlockBackendAioNotifier *notifier; | ||||
|  | ||||
|     trace_blk_root_detach(child, blk, child->bs); | ||||
|  | ||||
|     QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { | ||||
|         bdrv_remove_aio_context_notifier(child->bs, | ||||
|                 notifier->attached_aio_context, | ||||
|                 notifier->detach_aio_context, | ||||
|                 notifier->opaque); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static const BdrvChildRole child_root = { | ||||
|     .inherit_options    = blk_root_inherit_options, | ||||
|  | ||||
| @@ -292,14 +256,10 @@ static const BdrvChildRole child_root = { | ||||
|     .get_parent_desc    = blk_root_get_parent_desc, | ||||
|  | ||||
|     .drained_begin      = blk_root_drained_begin, | ||||
|     .drained_poll       = blk_root_drained_poll, | ||||
|     .drained_end        = blk_root_drained_end, | ||||
|  | ||||
|     .activate           = blk_root_activate, | ||||
|     .inactivate         = blk_root_inactivate, | ||||
|  | ||||
|     .attach             = blk_root_attach, | ||||
|     .detach             = blk_root_detach, | ||||
| }; | ||||
|  | ||||
| /* | ||||
| @@ -323,14 +283,10 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm) | ||||
|     blk->shared_perm = shared_perm; | ||||
|     blk_set_enable_write_cache(blk, true); | ||||
|  | ||||
|     blk->on_read_error = BLOCKDEV_ON_ERROR_REPORT; | ||||
|     blk->on_write_error = BLOCKDEV_ON_ERROR_ENOSPC; | ||||
|  | ||||
|     block_acct_init(&blk->stats); | ||||
|  | ||||
|     notifier_list_init(&blk->remove_bs_notifiers); | ||||
|     notifier_list_init(&blk->insert_bs_notifiers); | ||||
|     QLIST_INIT(&blk->aio_notifiers); | ||||
|  | ||||
|     QTAILQ_INSERT_TAIL(&block_backends, blk, link); | ||||
|     return blk; | ||||
| @@ -408,7 +364,6 @@ static void blk_delete(BlockBackend *blk) | ||||
|     } | ||||
|     assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers)); | ||||
|     assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers)); | ||||
|     assert(QLIST_EMPTY(&blk->aio_notifiers)); | ||||
|     QTAILQ_REMOVE(&block_backends, blk, link); | ||||
|     drive_info_del(blk->legacy_dinfo); | ||||
|     block_acct_cleanup(&blk->stats); | ||||
| @@ -421,6 +376,7 @@ static void drive_info_del(DriveInfo *dinfo) | ||||
|         return; | ||||
|     } | ||||
|     qemu_opts_del(dinfo->opts); | ||||
|     g_free(dinfo->serial); | ||||
|     g_free(dinfo); | ||||
| } | ||||
|  | ||||
| @@ -435,7 +391,6 @@ int blk_get_refcnt(BlockBackend *blk) | ||||
|  */ | ||||
| void blk_ref(BlockBackend *blk) | ||||
| { | ||||
|     assert(blk->refcnt > 0); | ||||
|     blk->refcnt++; | ||||
| } | ||||
|  | ||||
| @@ -448,13 +403,7 @@ void blk_unref(BlockBackend *blk) | ||||
| { | ||||
|     if (blk) { | ||||
|         assert(blk->refcnt > 0); | ||||
|         if (blk->refcnt > 1) { | ||||
|             blk->refcnt--; | ||||
|         } else { | ||||
|             blk_drain(blk); | ||||
|             /* blk_drain() cannot resurrect blk, nobody held a reference */ | ||||
|             assert(blk->refcnt == 1); | ||||
|             blk->refcnt = 0; | ||||
|         if (!--blk->refcnt) { | ||||
|             blk_delete(blk); | ||||
|         } | ||||
|     } | ||||
| @@ -776,11 +725,6 @@ void blk_remove_bs(BlockBackend *blk) | ||||
|  | ||||
|     blk_update_root_state(blk); | ||||
|  | ||||
|     /* bdrv_root_unref_child() will cause blk->root to become stale and may | ||||
|      * switch to a completion coroutine later on. Let's drain all I/O here | ||||
|      * to avoid that and a potential QEMU crash. | ||||
|      */ | ||||
|     blk_drain(blk); | ||||
|     bdrv_root_unref_child(blk->root); | ||||
|     blk->root = NULL; | ||||
| } | ||||
| @@ -834,11 +778,7 @@ void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm) | ||||
|     *shared_perm = blk->shared_perm; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Attach device model @dev to @blk. | ||||
|  * Return 0 on success, -EBUSY when a device model is attached already. | ||||
|  */ | ||||
| int blk_attach_dev(BlockBackend *blk, DeviceState *dev) | ||||
| static int blk_do_attach_dev(BlockBackend *blk, void *dev) | ||||
| { | ||||
|     if (blk->dev) { | ||||
|         return -EBUSY; | ||||
| @@ -853,16 +793,40 @@ int blk_attach_dev(BlockBackend *blk, DeviceState *dev) | ||||
|  | ||||
|     blk_ref(blk); | ||||
|     blk->dev = dev; | ||||
|     blk->legacy_dev = false; | ||||
|     blk_iostatus_reset(blk); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Attach device model @dev to @blk. | ||||
|  * Return 0 on success, -EBUSY when a device model is attached already. | ||||
|  */ | ||||
| int blk_attach_dev(BlockBackend *blk, DeviceState *dev) | ||||
| { | ||||
|     return blk_do_attach_dev(blk, dev); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Attach device model @dev to @blk. | ||||
|  * @blk must not have a device model attached already. | ||||
|  * TODO qdevified devices don't use this, remove when devices are qdevified | ||||
|  */ | ||||
| void blk_attach_dev_legacy(BlockBackend *blk, void *dev) | ||||
| { | ||||
|     if (blk_do_attach_dev(blk, dev) < 0) { | ||||
|         abort(); | ||||
|     } | ||||
|     blk->legacy_dev = true; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Detach device model @dev from @blk. | ||||
|  * @dev must be currently attached to @blk. | ||||
|  */ | ||||
| void blk_detach_dev(BlockBackend *blk, DeviceState *dev) | ||||
| void blk_detach_dev(BlockBackend *blk, void *dev) | ||||
| /* TODO change to DeviceState *dev when all users are qdevified */ | ||||
| { | ||||
|     assert(blk->dev == dev); | ||||
|     blk->dev = NULL; | ||||
| @@ -876,7 +840,8 @@ void blk_detach_dev(BlockBackend *blk, DeviceState *dev) | ||||
| /* | ||||
|  * Return the device model attached to @blk if any, else null. | ||||
|  */ | ||||
| DeviceState *blk_get_attached_dev(BlockBackend *blk) | ||||
| void *blk_get_attached_dev(BlockBackend *blk) | ||||
| /* TODO change to return DeviceState * when all users are qdevified */ | ||||
| { | ||||
|     return blk->dev; | ||||
| } | ||||
| @@ -885,15 +850,17 @@ DeviceState *blk_get_attached_dev(BlockBackend *blk) | ||||
|  * device attached to the BlockBackend. */ | ||||
| char *blk_get_attached_dev_id(BlockBackend *blk) | ||||
| { | ||||
|     DeviceState *dev = blk->dev; | ||||
|     DeviceState *dev; | ||||
|  | ||||
|     assert(!blk->legacy_dev); | ||||
|     dev = blk->dev; | ||||
|  | ||||
|     if (!dev) { | ||||
|         return g_strdup(""); | ||||
|     } else if (dev->id) { | ||||
|         return g_strdup(dev->id); | ||||
|     } | ||||
|  | ||||
|     return object_get_canonical_path(OBJECT(dev)) ?: g_strdup(""); | ||||
|     return object_get_canonical_path(OBJECT(dev)); | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -923,6 +890,11 @@ BlockBackend *blk_by_dev(void *dev) | ||||
| void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, | ||||
|                      void *opaque) | ||||
| { | ||||
|     /* All drivers that use blk_set_dev_ops() are qdevified and we want to keep | ||||
|      * it that way, so we can assume blk->dev, if present, is a DeviceState if | ||||
|      * blk->dev_ops is set. Non-device users may use dev_ops without device. */ | ||||
|     assert(!blk->legacy_dev); | ||||
|  | ||||
|     blk->dev_ops = ops; | ||||
|     blk->dev_opaque = opaque; | ||||
|  | ||||
| @@ -948,6 +920,8 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp) | ||||
|         bool tray_was_open, tray_is_open; | ||||
|         Error *local_err = NULL; | ||||
|  | ||||
|         assert(!blk->legacy_dev); | ||||
|  | ||||
|         tray_was_open = blk_dev_is_tray_open(blk); | ||||
|         blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err); | ||||
|         if (local_err) { | ||||
| @@ -959,7 +933,8 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp) | ||||
|  | ||||
|         if (tray_was_open != tray_is_open) { | ||||
|             char *id = blk_get_attached_dev_id(blk); | ||||
|             qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open); | ||||
|             qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open, | ||||
|                                               &error_abort); | ||||
|             g_free(id); | ||||
|         } | ||||
|     } | ||||
| @@ -1187,7 +1162,6 @@ static void blk_read_entry(void *opaque) | ||||
|  | ||||
|     rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size, | ||||
|                               qiov, rwco->flags); | ||||
|     aio_wait_kick(); | ||||
| } | ||||
|  | ||||
| static void blk_write_entry(void *opaque) | ||||
| @@ -1197,15 +1171,23 @@ static void blk_write_entry(void *opaque) | ||||
|  | ||||
|     rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size, | ||||
|                                qiov, rwco->flags); | ||||
|     aio_wait_kick(); | ||||
| } | ||||
|  | ||||
| static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, | ||||
|                    int64_t bytes, CoroutineEntry co_entry, | ||||
|                    BdrvRequestFlags flags) | ||||
| { | ||||
|     QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); | ||||
|     BlkRwCo rwco = { | ||||
|     QEMUIOVector qiov; | ||||
|     struct iovec iov; | ||||
|     BlkRwCo rwco; | ||||
|  | ||||
|     iov = (struct iovec) { | ||||
|         .iov_base = buf, | ||||
|         .iov_len = bytes, | ||||
|     }; | ||||
|     qemu_iovec_init_external(&qiov, &iov, 1); | ||||
|  | ||||
|     rwco = (BlkRwCo) { | ||||
|         .blk    = blk, | ||||
|         .offset = offset, | ||||
|         .iobuf  = &qiov, | ||||
| @@ -1253,15 +1235,15 @@ int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags) | ||||
|     return bdrv_make_zero(blk->root, flags); | ||||
| } | ||||
|  | ||||
| void blk_inc_in_flight(BlockBackend *blk) | ||||
| static void blk_inc_in_flight(BlockBackend *blk) | ||||
| { | ||||
|     atomic_inc(&blk->in_flight); | ||||
| } | ||||
|  | ||||
| void blk_dec_in_flight(BlockBackend *blk) | ||||
| static void blk_dec_in_flight(BlockBackend *blk) | ||||
| { | ||||
|     atomic_dec(&blk->in_flight); | ||||
|     aio_wait_kick(); | ||||
|     aio_wait_kick(&blk->wait); | ||||
| } | ||||
|  | ||||
| static void error_callback_bh(void *opaque) | ||||
| @@ -1302,8 +1284,8 @@ static const AIOCBInfo blk_aio_em_aiocb_info = { | ||||
| static void blk_aio_complete(BlkAioEmAIOCB *acb) | ||||
| { | ||||
|     if (acb->has_returned) { | ||||
|         acb->common.cb(acb->common.opaque, acb->rwco.ret); | ||||
|         blk_dec_in_flight(acb->rwco.blk); | ||||
|         acb->common.cb(acb->common.opaque, acb->rwco.ret); | ||||
|         qemu_aio_unref(acb); | ||||
|     } | ||||
| } | ||||
| @@ -1500,7 +1482,6 @@ static void blk_ioctl_entry(void *opaque) | ||||
|  | ||||
|     rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, | ||||
|                              qiov->iov[0].iov_base); | ||||
|     aio_wait_kick(); | ||||
| } | ||||
|  | ||||
| int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) | ||||
| @@ -1531,7 +1512,7 @@ int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes) | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     return bdrv_co_pdiscard(blk->root, offset, bytes); | ||||
|     return bdrv_co_pdiscard(blk_bs(blk), offset, bytes); | ||||
| } | ||||
|  | ||||
| int blk_co_flush(BlockBackend *blk) | ||||
| @@ -1547,7 +1528,6 @@ static void blk_flush_entry(void *opaque) | ||||
| { | ||||
|     BlkRwCo *rwco = opaque; | ||||
|     rwco->ret = blk_co_flush(rwco->blk); | ||||
|     aio_wait_kick(); | ||||
| } | ||||
|  | ||||
| int blk_flush(BlockBackend *blk) | ||||
| @@ -1564,8 +1544,9 @@ void blk_drain(BlockBackend *blk) | ||||
|     } | ||||
|  | ||||
|     /* We may have -ENOMEDIUM completions in flight */ | ||||
|     AIO_WAIT_WHILE(blk_get_aio_context(blk), | ||||
|                    atomic_mb_read(&blk->in_flight) > 0); | ||||
|     AIO_WAIT_WHILE(&blk->wait, | ||||
|             blk_get_aio_context(blk), | ||||
|             atomic_mb_read(&blk->in_flight) > 0); | ||||
|  | ||||
|     if (bs) { | ||||
|         bdrv_drained_end(bs); | ||||
| @@ -1584,7 +1565,8 @@ void blk_drain_all(void) | ||||
|         aio_context_acquire(ctx); | ||||
|  | ||||
|         /* We may have -ENOMEDIUM completions in flight */ | ||||
|         AIO_WAIT_WHILE(ctx, atomic_mb_read(&blk->in_flight) > 0); | ||||
|         AIO_WAIT_WHILE(&blk->wait, ctx, | ||||
|                 atomic_mb_read(&blk->in_flight) > 0); | ||||
|  | ||||
|         aio_context_release(ctx); | ||||
|     } | ||||
| @@ -1636,7 +1618,8 @@ static void send_qmp_error_event(BlockBackend *blk, | ||||
|     qapi_event_send_block_io_error(blk_name(blk), !!bs, | ||||
|                                    bs ? bdrv_get_node_name(bs) : NULL, optype, | ||||
|                                    action, blk_iostatus_is_enabled(blk), | ||||
|                                    error == ENOSPC, strerror(error)); | ||||
|                                    error == ENOSPC, strerror(error), | ||||
|                                    &error_abort); | ||||
| } | ||||
|  | ||||
| /* This is done by device models because, while the block layer knows | ||||
| @@ -1671,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); | ||||
|  | ||||
| @@ -1682,18 +1665,18 @@ bool blk_is_read_only(BlockBackend *blk) | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool blk_is_sg(BlockBackend *blk) | ||||
| int blk_is_sg(BlockBackend *blk) | ||||
| { | ||||
|     BlockDriverState *bs = blk_bs(blk); | ||||
|  | ||||
|     if (!bs) { | ||||
|         return false; | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     return bdrv_is_sg(bs); | ||||
| } | ||||
|  | ||||
| bool blk_enable_write_cache(BlockBackend *blk) | ||||
| int blk_enable_write_cache(BlockBackend *blk) | ||||
| { | ||||
|     return blk->enable_write_cache; | ||||
| } | ||||
| @@ -1741,6 +1724,9 @@ void blk_eject(BlockBackend *blk, bool eject_flag) | ||||
|     BlockDriverState *bs = blk_bs(blk); | ||||
|     char *id; | ||||
|  | ||||
|     /* blk_eject is only called by qdevified devices */ | ||||
|     assert(!blk->legacy_dev); | ||||
|  | ||||
|     if (bs) { | ||||
|         bdrv_eject(bs, eject_flag); | ||||
|     } | ||||
| @@ -1749,7 +1735,7 @@ void blk_eject(BlockBackend *blk, bool eject_flag) | ||||
|      * the frontend experienced a tray event. */ | ||||
|     id = blk_get_attached_dev_id(blk); | ||||
|     qapi_event_send_device_tray_moved(blk_name(blk), id, | ||||
|                                       eject_flag); | ||||
|                                       eject_flag, &error_abort); | ||||
|     g_free(id); | ||||
| } | ||||
|  | ||||
| @@ -1836,7 +1822,13 @@ void blk_op_unblock_all(BlockBackend *blk, Error *reason) | ||||
|  | ||||
| AioContext *blk_get_aio_context(BlockBackend *blk) | ||||
| { | ||||
|     return bdrv_get_aio_context(blk_bs(blk)); | ||||
|     BlockDriverState *bs = blk_bs(blk); | ||||
|  | ||||
|     if (bs) { | ||||
|         return bdrv_get_aio_context(bs); | ||||
|     } else { | ||||
|         return qemu_get_aio_context(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb) | ||||
| @@ -1865,15 +1857,8 @@ void blk_add_aio_context_notifier(BlockBackend *blk, | ||||
|         void (*attached_aio_context)(AioContext *new_context, void *opaque), | ||||
|         void (*detach_aio_context)(void *opaque), void *opaque) | ||||
| { | ||||
|     BlockBackendAioNotifier *notifier; | ||||
|     BlockDriverState *bs = blk_bs(blk); | ||||
|  | ||||
|     notifier = g_new(BlockBackendAioNotifier, 1); | ||||
|     notifier->attached_aio_context = attached_aio_context; | ||||
|     notifier->detach_aio_context = detach_aio_context; | ||||
|     notifier->opaque = opaque; | ||||
|     QLIST_INSERT_HEAD(&blk->aio_notifiers, notifier, list); | ||||
|  | ||||
|     if (bs) { | ||||
|         bdrv_add_aio_context_notifier(bs, attached_aio_context, | ||||
|                                       detach_aio_context, opaque); | ||||
| @@ -1886,25 +1871,12 @@ void blk_remove_aio_context_notifier(BlockBackend *blk, | ||||
|                                      void (*detach_aio_context)(void *), | ||||
|                                      void *opaque) | ||||
| { | ||||
|     BlockBackendAioNotifier *notifier; | ||||
|     BlockDriverState *bs = blk_bs(blk); | ||||
|  | ||||
|     if (bs) { | ||||
|         bdrv_remove_aio_context_notifier(bs, attached_aio_context, | ||||
|                                          detach_aio_context, opaque); | ||||
|     } | ||||
|  | ||||
|     QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { | ||||
|         if (notifier->attached_aio_context == attached_aio_context && | ||||
|             notifier->detach_aio_context == detach_aio_context && | ||||
|             notifier->opaque == opaque) { | ||||
|             QLIST_REMOVE(notifier, list); | ||||
|             g_free(notifier); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     abort(); | ||||
| } | ||||
|  | ||||
| void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify) | ||||
| @@ -1977,7 +1949,6 @@ static void blk_pdiscard_entry(void *opaque) | ||||
|     QEMUIOVector *qiov = rwco->iobuf; | ||||
|  | ||||
|     rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size); | ||||
|     aio_wait_kick(); | ||||
| } | ||||
|  | ||||
| int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes) | ||||
| @@ -2159,13 +2130,6 @@ static void blk_root_drained_begin(BdrvChild *child) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static bool blk_root_drained_poll(BdrvChild *child) | ||||
| { | ||||
|     BlockBackend *blk = child->opaque; | ||||
|     assert(blk->quiesce_counter); | ||||
|     return !!blk->in_flight; | ||||
| } | ||||
|  | ||||
| static void blk_root_drained_end(BdrvChild *child) | ||||
| { | ||||
|     BlockBackend *blk = child->opaque; | ||||
| @@ -2190,27 +2154,3 @@ void blk_unregister_buf(BlockBackend *blk, void *host) | ||||
| { | ||||
|     bdrv_unregister_buf(blk_bs(blk), host); | ||||
| } | ||||
|  | ||||
| int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, | ||||
|                                    BlockBackend *blk_out, int64_t off_out, | ||||
|                                    int bytes, BdrvRequestFlags read_flags, | ||||
|                                    BdrvRequestFlags write_flags) | ||||
| { | ||||
|     int r; | ||||
|     r = blk_check_byte_request(blk_in, off_in, bytes); | ||||
|     if (r) { | ||||
|         return r; | ||||
|     } | ||||
|     r = blk_check_byte_request(blk_out, off_out, bytes); | ||||
|     if (r) { | ||||
|         return r; | ||||
|     } | ||||
|     return bdrv_co_copy_range(blk_in->root, off_in, | ||||
|                               blk_out->root, off_out, | ||||
|                               bytes, read_flags, write_flags); | ||||
| } | ||||
|  | ||||
| const BdrvChild *blk_root(BlockBackend *blk) | ||||
| { | ||||
|     return blk->root; | ||||
| } | ||||
|   | ||||
| @@ -85,14 +85,14 @@ static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||
|     const struct bochs_header *bochs = (const void *)buf; | ||||
|  | ||||
|     if (buf_size < HEADER_SIZE) | ||||
|         return 0; | ||||
| 	return 0; | ||||
|  | ||||
|     if (!strcmp(bochs->magic, HEADER_MAGIC) && | ||||
|         !strcmp(bochs->type, REDOLOG_TYPE) && | ||||
|         !strcmp(bochs->subtype, GROWING_TYPE) && | ||||
|         ((le32_to_cpu(bochs->version) == HEADER_VERSION) || | ||||
|         (le32_to_cpu(bochs->version) == HEADER_V1))) | ||||
|         return 100; | ||||
| 	!strcmp(bochs->type, REDOLOG_TYPE) && | ||||
| 	!strcmp(bochs->subtype, GROWING_TYPE) && | ||||
| 	((le32_to_cpu(bochs->version) == HEADER_VERSION) || | ||||
| 	(le32_to_cpu(bochs->version) == HEADER_V1))) | ||||
| 	return 100; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| @@ -105,18 +105,23 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     struct bochs_header bochs; | ||||
|     int ret; | ||||
|  | ||||
|     /* No write support yet */ | ||||
|     ret = bdrv_apply_auto_read_only(bs, NULL, errp); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||
|                                false, errp); | ||||
|     if (!bs->file) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     if (!bdrv_is_read_only(bs)) { | ||||
|         error_report("Opening bochs images without an explicit read-only=on " | ||||
|                      "option is deprecated. Future versions will refuse to " | ||||
|                      "open the image instead of automatically marking the " | ||||
|                      "image read-only."); | ||||
|         ret = bdrv_set_read_only(bs, true, errp); /* no write support yet */ | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
| @@ -125,8 +130,8 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     if (strcmp(bochs.magic, HEADER_MAGIC) || | ||||
|         strcmp(bochs.type, REDOLOG_TYPE) || | ||||
|         strcmp(bochs.subtype, GROWING_TYPE) || | ||||
|         ((le32_to_cpu(bochs.version) != HEADER_VERSION) && | ||||
|         (le32_to_cpu(bochs.version) != HEADER_V1))) { | ||||
| 	((le32_to_cpu(bochs.version) != HEADER_VERSION) && | ||||
| 	(le32_to_cpu(bochs.version) != HEADER_V1))) { | ||||
|         error_setg(errp, "Image not in Bochs format"); | ||||
|         return -EINVAL; | ||||
|     } | ||||
| @@ -158,7 +163,7 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     } | ||||
|  | ||||
|     for (i = 0; i < s->catalog_size; i++) | ||||
|         le32_to_cpus(&s->catalog_bitmap[i]); | ||||
| 	le32_to_cpus(&s->catalog_bitmap[i]); | ||||
|  | ||||
|     s->data_offset = le32_to_cpu(bochs.header) + (s->catalog_size * 4); | ||||
|  | ||||
| @@ -217,7 +222,7 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) | ||||
|     extent_offset = (offset % s->extent_size) / 512; | ||||
|  | ||||
|     if (s->catalog_bitmap[extent_index] == 0xffffffff) { | ||||
|         return 0; /* not allocated */ | ||||
| 	return 0; /* not allocated */ | ||||
|     } | ||||
|  | ||||
|     bitmap_offset = s->data_offset + | ||||
| @@ -232,7 +237,7 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) | ||||
|     } | ||||
|  | ||||
|     if (!((bitmap_entry >> (extent_offset % 8)) & 1)) { | ||||
|         return 0; /* not allocated */ | ||||
| 	return 0; /* not allocated */ | ||||
|     } | ||||
|  | ||||
|     return bitmap_offset + (512 * (s->bitmap_blocks + extent_offset)); | ||||
|   | ||||
| @@ -67,17 +67,23 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     uint32_t offsets_size, max_compressed_block_size = 1, i; | ||||
|     int ret; | ||||
|  | ||||
|     ret = bdrv_apply_auto_read_only(bs, NULL, errp); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||
|                                false, errp); | ||||
|     if (!bs->file) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     if (!bdrv_is_read_only(bs)) { | ||||
|         error_report("Opening cloop images without an explicit read-only=on " | ||||
|                      "option is deprecated. Future versions will refuse to " | ||||
|                      "open the image instead of automatically marking the " | ||||
|                      "image read-only."); | ||||
|         ret = bdrv_set_read_only(bs, true, errp); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* read header */ | ||||
|     ret = bdrv_pread(bs->file, 128, &s->block_size, 4); | ||||
|     if (ret < 0) { | ||||
|   | ||||
							
								
								
									
										225
									
								
								block/commit.c
									
									
									
									
									
								
							
							
						
						
									
										225
									
								
								block/commit.c
									
									
									
									
									
								
							| @@ -31,15 +31,16 @@ enum { | ||||
|     COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */ | ||||
| }; | ||||
|  | ||||
| #define SLICE_TIME 100000000ULL /* ns */ | ||||
|  | ||||
| typedef struct CommitBlockJob { | ||||
|     BlockJob common; | ||||
|     RateLimit limit; | ||||
|     BlockDriverState *commit_top_bs; | ||||
|     BlockBackend *top; | ||||
|     BlockBackend *base; | ||||
|     BlockDriverState *base_bs; | ||||
|     BlockdevOnError on_error; | ||||
|     bool base_read_only; | ||||
|     bool chain_frozen; | ||||
|     int base_flags; | ||||
|     char *backing_file_str; | ||||
| } CommitBlockJob; | ||||
|  | ||||
| @@ -48,9 +49,14 @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base, | ||||
|                                         void *buf) | ||||
| { | ||||
|     int ret = 0; | ||||
|     QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); | ||||
|     QEMUIOVector qiov; | ||||
|     struct iovec iov = { | ||||
|         .iov_base = buf, | ||||
|         .iov_len = bytes, | ||||
|     }; | ||||
|  | ||||
|     assert(bytes < SIZE_MAX); | ||||
|     qemu_iovec_init_external(&qiov, &iov, 1); | ||||
|  | ||||
|     ret = blk_co_preadv(bs, offset, qiov.size, &qiov, 0); | ||||
|     if (ret < 0) { | ||||
| @@ -65,100 +71,96 @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int commit_prepare(Job *job) | ||||
| { | ||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); | ||||
| typedef struct { | ||||
|     int ret; | ||||
| } CommitCompleteData; | ||||
|  | ||||
|     bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs); | ||||
|     s->chain_frozen = false; | ||||
| static void commit_complete(BlockJob *job, void *opaque) | ||||
| { | ||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common); | ||||
|     CommitCompleteData *data = opaque; | ||||
|     BlockDriverState *top = blk_bs(s->top); | ||||
|     BlockDriverState *base = blk_bs(s->base); | ||||
|     BlockDriverState *commit_top_bs = s->commit_top_bs; | ||||
|     int ret = data->ret; | ||||
|     bool remove_commit_top_bs = false; | ||||
|  | ||||
|     /* Make sure commit_top_bs and top stay around until bdrv_replace_node() */ | ||||
|     bdrv_ref(top); | ||||
|     bdrv_ref(commit_top_bs); | ||||
|  | ||||
|     /* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before | ||||
|      * the normal backing chain can be restored. */ | ||||
|     blk_unref(s->base); | ||||
|     s->base = NULL; | ||||
|  | ||||
|     /* FIXME: bdrv_drop_intermediate treats total failures and partial failures | ||||
|      * identically. Further work is needed to disambiguate these cases. */ | ||||
|     return bdrv_drop_intermediate(s->commit_top_bs, s->base_bs, | ||||
|                                   s->backing_file_str); | ||||
| } | ||||
|  | ||||
| static void commit_abort(Job *job) | ||||
| { | ||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); | ||||
|     BlockDriverState *top_bs = blk_bs(s->top); | ||||
|  | ||||
|     if (s->chain_frozen) { | ||||
|         bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs); | ||||
|     if (!block_job_is_cancelled(&s->common) && ret == 0) { | ||||
|         /* success */ | ||||
|         ret = bdrv_drop_intermediate(s->commit_top_bs, base, | ||||
|                                      s->backing_file_str); | ||||
|     } else { | ||||
|         /* XXX Can (or should) we somehow keep 'consistent read' blocked even | ||||
|          * after the failed/cancelled commit job is gone? If we already wrote | ||||
|          * something to base, the intermediate images aren't valid any more. */ | ||||
|         remove_commit_top_bs = true; | ||||
|     } | ||||
|  | ||||
|     /* 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 | ||||
|      * something to base, the intermediate images aren't valid any more. */ | ||||
|     bdrv_child_try_set_perm(s->commit_top_bs->backing, 0, BLK_PERM_ALL, | ||||
|                             &error_abort); | ||||
|     bdrv_replace_node(s->commit_top_bs, backing_bs(s->commit_top_bs), | ||||
|                       &error_abort); | ||||
|  | ||||
|     bdrv_unref(s->commit_top_bs); | ||||
|     bdrv_unref(top_bs); | ||||
| } | ||||
|  | ||||
| static void commit_clean(Job *job) | ||||
| { | ||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); | ||||
|  | ||||
|     /* restore base open flags here if appropriate (e.g., change the base back | ||||
|      * to r/o). These reopens do not need to be atomic, since we won't abort | ||||
|      * even on failure here */ | ||||
|     if (s->base_read_only) { | ||||
|         bdrv_reopen_set_read_only(s->base_bs, true, NULL); | ||||
|     if (s->base_flags != bdrv_get_flags(base)) { | ||||
|         bdrv_reopen(base, s->base_flags, NULL); | ||||
|     } | ||||
|  | ||||
|     g_free(s->backing_file_str); | ||||
|     blk_unref(s->top); | ||||
|  | ||||
|     /* If there is more than one reference to the job (e.g. if called from | ||||
|      * block_job_finish_sync()), block_job_completed() won't free it and | ||||
|      * therefore the blockers on the intermediate nodes remain. This would | ||||
|      * cause bdrv_set_backing_hd() to fail. */ | ||||
|     block_job_remove_all_bdrv(job); | ||||
|  | ||||
|     block_job_completed(&s->common, ret); | ||||
|     g_free(data); | ||||
|  | ||||
|     /* If bdrv_drop_intermediate() didn't already do that, remove the commit | ||||
|      * filter driver from the backing chain. Do this as the final step so that | ||||
|      * the 'consistent read' permission can be granted.  */ | ||||
|     if (remove_commit_top_bs) { | ||||
|         bdrv_child_try_set_perm(commit_top_bs->backing, 0, BLK_PERM_ALL, | ||||
|                                 &error_abort); | ||||
|         bdrv_replace_node(commit_top_bs, backing_bs(commit_top_bs), | ||||
|                           &error_abort); | ||||
|     } | ||||
|  | ||||
|     bdrv_unref(commit_top_bs); | ||||
|     bdrv_unref(top); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn commit_run(Job *job, Error **errp) | ||||
| static void coroutine_fn commit_run(void *opaque) | ||||
| { | ||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); | ||||
|     CommitBlockJob *s = opaque; | ||||
|     CommitCompleteData *data; | ||||
|     int64_t offset; | ||||
|     uint64_t delay_ns = 0; | ||||
|     int ret = 0; | ||||
|     int64_t n = 0; /* bytes */ | ||||
|     void *buf = NULL; | ||||
|     int bytes_written = 0; | ||||
|     int64_t len, base_len; | ||||
|     int64_t base_len; | ||||
|  | ||||
|     ret = len = blk_getlength(s->top); | ||||
|     if (len < 0) { | ||||
|     ret = s->common.len = blk_getlength(s->top); | ||||
|  | ||||
|     if (s->common.len < 0) { | ||||
|         goto out; | ||||
|     } | ||||
|     job_progress_set_remaining(&s->common.job, len); | ||||
|  | ||||
|     ret = base_len = blk_getlength(s->base); | ||||
|     if (base_len < 0) { | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     if (base_len < len) { | ||||
|         ret = blk_truncate(s->base, len, PREALLOC_MODE_OFF, NULL); | ||||
|     if (base_len < s->common.len) { | ||||
|         ret = blk_truncate(s->base, s->common.len, PREALLOC_MODE_OFF, NULL); | ||||
|         if (ret) { | ||||
|             goto out; | ||||
|         } | ||||
| @@ -166,14 +168,14 @@ static int coroutine_fn commit_run(Job *job, Error **errp) | ||||
|  | ||||
|     buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE); | ||||
|  | ||||
|     for (offset = 0; offset < len; offset += n) { | ||||
|     for (offset = 0; offset < s->common.len; offset += n) { | ||||
|         bool copy; | ||||
|  | ||||
|         /* Note that even when no rate limit is applied we need to yield | ||||
|          * with no pending I/O here so that bdrv_drain_all() returns. | ||||
|          */ | ||||
|         job_sleep_ns(&s->common.job, delay_ns); | ||||
|         if (job_is_cancelled(&s->common.job)) { | ||||
|         block_job_sleep_ns(&s->common, delay_ns); | ||||
|         if (block_job_is_cancelled(&s->common)) { | ||||
|             break; | ||||
|         } | ||||
|         /* Copy if allocated above the base */ | ||||
| @@ -196,12 +198,10 @@ static int coroutine_fn commit_run(Job *job, Error **errp) | ||||
|             } | ||||
|         } | ||||
|         /* Publish progress */ | ||||
|         job_progress_update(&s->common.job, n); | ||||
|         s->common.offset += n; | ||||
|  | ||||
|         if (copy) { | ||||
|             delay_ns = block_job_ratelimit_get_delay(&s->common, n); | ||||
|         } else { | ||||
|             delay_ns = 0; | ||||
|         if (copy && s->common.speed) { | ||||
|             delay_ns = ratelimit_calculate_delay(&s->limit, n); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -210,21 +210,27 @@ static int coroutine_fn commit_run(Job *job, Error **errp) | ||||
| out: | ||||
|     qemu_vfree(buf); | ||||
|  | ||||
|     return ret; | ||||
|     data = g_malloc(sizeof(*data)); | ||||
|     data->ret = ret; | ||||
|     block_job_defer_to_main_loop(&s->common, commit_complete, data); | ||||
| } | ||||
|  | ||||
| static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp) | ||||
| { | ||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common); | ||||
|  | ||||
|     if (speed < 0) { | ||||
|         error_setg(errp, QERR_INVALID_PARAMETER, "speed"); | ||||
|         return; | ||||
|     } | ||||
|     ratelimit_set_speed(&s->limit, speed, SLICE_TIME); | ||||
| } | ||||
|  | ||||
| static const BlockJobDriver commit_job_driver = { | ||||
|     .job_driver = { | ||||
|         .instance_size = sizeof(CommitBlockJob), | ||||
|         .job_type      = JOB_TYPE_COMMIT, | ||||
|         .free          = block_job_free, | ||||
|         .user_resume   = block_job_user_resume, | ||||
|         .drain         = block_job_drain, | ||||
|         .run           = commit_run, | ||||
|         .prepare       = commit_prepare, | ||||
|         .abort         = commit_abort, | ||||
|         .clean         = commit_clean | ||||
|     }, | ||||
|     .instance_size = sizeof(CommitBlockJob), | ||||
|     .job_type      = BLOCK_JOB_TYPE_COMMIT, | ||||
|     .set_speed     = commit_set_speed, | ||||
|     .start         = commit_run, | ||||
| }; | ||||
|  | ||||
| static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs, | ||||
| @@ -233,12 +239,17 @@ static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs, | ||||
|     return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); | ||||
| } | ||||
|  | ||||
| static void bdrv_commit_top_refresh_filename(BlockDriverState *bs) | ||||
| static void bdrv_commit_top_refresh_filename(BlockDriverState *bs, QDict *opts) | ||||
| { | ||||
|     bdrv_refresh_filename(bs->backing->bs); | ||||
|     pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), | ||||
|             bs->backing->bs->filename); | ||||
| } | ||||
|  | ||||
| static void bdrv_commit_top_close(BlockDriverState *bs) | ||||
| { | ||||
| } | ||||
|  | ||||
| static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c, | ||||
|                                        const BdrvChildRole *role, | ||||
|                                        BlockReopenQueue *reopen_queue, | ||||
| @@ -256,16 +267,17 @@ static BlockDriver bdrv_commit_top = { | ||||
|     .bdrv_co_preadv             = bdrv_commit_top_preadv, | ||||
|     .bdrv_co_block_status       = bdrv_co_block_status_from_backing, | ||||
|     .bdrv_refresh_filename      = bdrv_commit_top_refresh_filename, | ||||
|     .bdrv_close                 = bdrv_commit_top_close, | ||||
|     .bdrv_child_perm            = bdrv_commit_top_child_perm, | ||||
| }; | ||||
|  | ||||
| void commit_start(const char *job_id, BlockDriverState *bs, | ||||
|                   BlockDriverState *base, BlockDriverState *top, | ||||
|                   int creation_flags, int64_t speed, | ||||
|                   BlockDriverState *base, BlockDriverState *top, int64_t speed, | ||||
|                   BlockdevOnError on_error, const char *backing_file_str, | ||||
|                   const char *filter_node_name, Error **errp) | ||||
| { | ||||
|     CommitBlockJob *s; | ||||
|     int orig_base_flags; | ||||
|     BlockDriverState *iter; | ||||
|     BlockDriverState *commit_top_bs = NULL; | ||||
|     Error *local_err = NULL; | ||||
| @@ -277,16 +289,18 @@ void commit_start(const char *job_id, BlockDriverState *bs, | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL, | ||||
|                          speed, creation_flags, NULL, NULL, errp); | ||||
|     s = block_job_create(job_id, &commit_job_driver, bs, 0, BLK_PERM_ALL, | ||||
|                          speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp); | ||||
|     if (!s) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* convert base to r/w, if necessary */ | ||||
|     s->base_read_only = bdrv_is_read_only(base); | ||||
|     if (s->base_read_only) { | ||||
|         if (bdrv_reopen_set_read_only(base, false, errp) != 0) { | ||||
|     orig_base_flags = bdrv_get_flags(base); | ||||
|     if (!(orig_base_flags & BDRV_O_RDWR)) { | ||||
|         bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err); | ||||
|         if (local_err != NULL) { | ||||
|             error_propagate(errp, local_err); | ||||
|             goto fail; | ||||
|         } | ||||
|     } | ||||
| @@ -338,11 +352,6 @@ void commit_start(const char *job_id, BlockDriverState *bs, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (bdrv_freeze_backing_chain(commit_top_bs, base, errp) < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|     s->chain_frozen = true; | ||||
|  | ||||
|     ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
| @@ -358,7 +367,6 @@ void commit_start(const char *job_id, BlockDriverState *bs, | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|     s->base_bs = base; | ||||
|  | ||||
|     /* Required permissions are already taken with block_job_add_bdrv() */ | ||||
|     s->top = blk_new(0, BLK_PERM_ALL); | ||||
| @@ -367,29 +375,25 @@ void commit_start(const char *job_id, BlockDriverState *bs, | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     s->base_flags = orig_base_flags; | ||||
|     s->backing_file_str = g_strdup(backing_file_str); | ||||
|     s->on_error = on_error; | ||||
|  | ||||
|     trace_commit_start(bs, base, top, s); | ||||
|     job_start(&s->common.job); | ||||
|     block_job_start(&s->common); | ||||
|     return; | ||||
|  | ||||
| fail: | ||||
|     if (s->chain_frozen) { | ||||
|         bdrv_unfreeze_backing_chain(commit_top_bs, base); | ||||
|     } | ||||
|     if (s->base) { | ||||
|         blk_unref(s->base); | ||||
|     } | ||||
|     if (s->top) { | ||||
|         blk_unref(s->top); | ||||
|     } | ||||
|     job_early_fail(&s->common.job); | ||||
|     /* commit_top_bs has to be replaced after deleting the block job, | ||||
|      * otherwise this would fail because of lack of permissions. */ | ||||
|     if (commit_top_bs) { | ||||
|         bdrv_replace_node(commit_top_bs, top, &error_abort); | ||||
|     } | ||||
|     block_job_early_fail(&s->common); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -403,7 +407,7 @@ int bdrv_commit(BlockDriverState *bs) | ||||
|     BlockDriverState *commit_top_bs = NULL; | ||||
|     BlockDriver *drv = bs->drv; | ||||
|     int64_t offset, length, backing_length; | ||||
|     int ro; | ||||
|     int ro, open_flags; | ||||
|     int64_t n; | ||||
|     int ret = 0; | ||||
|     uint8_t *buf = NULL; | ||||
| @@ -422,9 +426,10 @@ int bdrv_commit(BlockDriverState *bs) | ||||
|     } | ||||
|  | ||||
|     ro = bs->backing->bs->read_only; | ||||
|     open_flags =  bs->backing->bs->open_flags; | ||||
|  | ||||
|     if (ro) { | ||||
|         if (bdrv_reopen_set_read_only(bs->backing->bs, false, NULL)) { | ||||
|         if (bdrv_reopen(bs->backing->bs, open_flags | BDRV_O_RDWR, NULL)) { | ||||
|             return -EACCES; | ||||
|         } | ||||
|     } | ||||
| @@ -534,7 +539,7 @@ ro_cleanup: | ||||
|  | ||||
|     if (ro) { | ||||
|         /* ignoring error return here */ | ||||
|         bdrv_reopen_set_read_only(bs->backing->bs, true, NULL); | ||||
|         bdrv_reopen(bs->backing->bs, open_flags & ~BDRV_O_RDWR, NULL); | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
|   | ||||
| @@ -1,167 +0,0 @@ | ||||
| /* | ||||
|  * Copy-on-read filter block driver | ||||
|  * | ||||
|  * Copyright (c) 2018 Red Hat, Inc. | ||||
|  * | ||||
|  * Author: | ||||
|  *   Max Reitz <mreitz@redhat.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License as | ||||
|  * published by the Free Software Foundation; either version 2 or | ||||
|  * (at your option) version 3 of the License. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "block/block_int.h" | ||||
|  | ||||
|  | ||||
| static int cor_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                     Error **errp) | ||||
| { | ||||
|     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, | ||||
|                                errp); | ||||
|     if (!bs->file) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | | ||||
|                                 (BDRV_REQ_FUA & | ||||
|                                     bs->file->bs->supported_write_flags); | ||||
|  | ||||
|     bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | | ||||
|                                ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & | ||||
|                                     bs->file->bs->supported_zero_flags); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| #define PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \ | ||||
|                           | BLK_PERM_WRITE \ | ||||
|                           | BLK_PERM_RESIZE) | ||||
| #define PERM_UNCHANGED (BLK_PERM_ALL & ~PERM_PASSTHROUGH) | ||||
|  | ||||
| static void cor_child_perm(BlockDriverState *bs, BdrvChild *c, | ||||
|                            const BdrvChildRole *role, | ||||
|                            BlockReopenQueue *reopen_queue, | ||||
|                            uint64_t perm, uint64_t shared, | ||||
|                            uint64_t *nperm, uint64_t *nshared) | ||||
| { | ||||
|     if (c == NULL) { | ||||
|         *nperm = (perm & PERM_PASSTHROUGH) | BLK_PERM_WRITE_UNCHANGED; | ||||
|         *nshared = (shared & PERM_PASSTHROUGH) | PERM_UNCHANGED; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     *nperm = (perm & PERM_PASSTHROUGH) | | ||||
|              (c->perm & PERM_UNCHANGED); | ||||
|     *nshared = (shared & PERM_PASSTHROUGH) | | ||||
|                (c->shared_perm & PERM_UNCHANGED); | ||||
| } | ||||
|  | ||||
|  | ||||
| static int64_t cor_getlength(BlockDriverState *bs) | ||||
| { | ||||
|     return bdrv_getlength(bs->file->bs); | ||||
| } | ||||
|  | ||||
|  | ||||
| static int coroutine_fn cor_co_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                                         PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     return bdrv_co_truncate(bs->file, offset, prealloc, errp); | ||||
| } | ||||
|  | ||||
|  | ||||
| static int coroutine_fn cor_co_preadv(BlockDriverState *bs, | ||||
|                                       uint64_t offset, uint64_t bytes, | ||||
|                                       QEMUIOVector *qiov, int flags) | ||||
| { | ||||
|     return bdrv_co_preadv(bs->file, offset, bytes, qiov, | ||||
|                           flags | BDRV_REQ_COPY_ON_READ); | ||||
| } | ||||
|  | ||||
|  | ||||
| static int coroutine_fn cor_co_pwritev(BlockDriverState *bs, | ||||
|                                        uint64_t offset, uint64_t bytes, | ||||
|                                        QEMUIOVector *qiov, int flags) | ||||
| { | ||||
|  | ||||
|     return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); | ||||
| } | ||||
|  | ||||
|  | ||||
| static int coroutine_fn cor_co_pwrite_zeroes(BlockDriverState *bs, | ||||
|                                              int64_t offset, int bytes, | ||||
|                                              BdrvRequestFlags flags) | ||||
| { | ||||
|     return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); | ||||
| } | ||||
|  | ||||
|  | ||||
| static int coroutine_fn cor_co_pdiscard(BlockDriverState *bs, | ||||
|                                         int64_t offset, int bytes) | ||||
| { | ||||
|     return bdrv_co_pdiscard(bs->file, offset, bytes); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void cor_eject(BlockDriverState *bs, bool eject_flag) | ||||
| { | ||||
|     bdrv_eject(bs->file->bs, eject_flag); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void cor_lock_medium(BlockDriverState *bs, bool locked) | ||||
| { | ||||
|     bdrv_lock_medium(bs->file->bs, locked); | ||||
| } | ||||
|  | ||||
|  | ||||
| static bool cor_recurse_is_first_non_filter(BlockDriverState *bs, | ||||
|                                             BlockDriverState *candidate) | ||||
| { | ||||
|     return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate); | ||||
| } | ||||
|  | ||||
|  | ||||
| static BlockDriver bdrv_copy_on_read = { | ||||
|     .format_name                        = "copy-on-read", | ||||
|  | ||||
|     .bdrv_open                          = cor_open, | ||||
|     .bdrv_child_perm                    = cor_child_perm, | ||||
|  | ||||
|     .bdrv_getlength                     = cor_getlength, | ||||
|     .bdrv_co_truncate                   = cor_co_truncate, | ||||
|  | ||||
|     .bdrv_co_preadv                     = cor_co_preadv, | ||||
|     .bdrv_co_pwritev                    = cor_co_pwritev, | ||||
|     .bdrv_co_pwrite_zeroes              = cor_co_pwrite_zeroes, | ||||
|     .bdrv_co_pdiscard                   = cor_co_pdiscard, | ||||
|  | ||||
|     .bdrv_eject                         = cor_eject, | ||||
|     .bdrv_lock_medium                   = cor_lock_medium, | ||||
|  | ||||
|     .bdrv_co_block_status               = bdrv_co_block_status_from_file, | ||||
|  | ||||
|     .bdrv_recurse_is_first_non_filter   = cor_recurse_is_first_non_filter, | ||||
|  | ||||
|     .has_variable_length                = true, | ||||
|     .is_filter                          = true, | ||||
| }; | ||||
|  | ||||
| static void bdrv_copy_on_read_init(void) | ||||
| { | ||||
|     bdrv_register(&bdrv_copy_on_read); | ||||
| } | ||||
|  | ||||
| block_init(bdrv_copy_on_read_init); | ||||
| @@ -24,44 +24,28 @@ | ||||
|  | ||||
| #include "qemu/osdep.h" | ||||
| #include "block/block_int.h" | ||||
| #include "qemu/job.h" | ||||
| #include "qapi/qapi-commands-block-core.h" | ||||
| #include "qapi/qapi-visit-block-core.h" | ||||
| #include "qapi/clone-visitor.h" | ||||
| #include "qapi/error.h" | ||||
|  | ||||
| typedef struct BlockdevCreateJob { | ||||
|     Job common; | ||||
| typedef struct BlockdevCreateCo { | ||||
|     BlockDriver *drv; | ||||
|     BlockdevCreateOptions *opts; | ||||
| } BlockdevCreateJob; | ||||
|  | ||||
| static int coroutine_fn blockdev_create_run(Job *job, Error **errp) | ||||
| { | ||||
|     BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); | ||||
|     int ret; | ||||
|     Error **errp; | ||||
| } BlockdevCreateCo; | ||||
|  | ||||
|     job_progress_set_remaining(&s->common, 1); | ||||
|     ret = s->drv->bdrv_co_create(s->opts, errp); | ||||
|     job_progress_update(&s->common, 1); | ||||
|  | ||||
|     qapi_free_BlockdevCreateOptions(s->opts); | ||||
|  | ||||
|     return ret; | ||||
| static void coroutine_fn bdrv_co_create_co_entry(void *opaque) | ||||
| { | ||||
|     BlockdevCreateCo *cco = opaque; | ||||
|     cco->ret = cco->drv->bdrv_co_create(cco->opts, cco->errp); | ||||
| } | ||||
|  | ||||
| static const JobDriver blockdev_create_job_driver = { | ||||
|     .instance_size = sizeof(BlockdevCreateJob), | ||||
|     .job_type      = JOB_TYPE_CREATE, | ||||
|     .run           = blockdev_create_run, | ||||
| }; | ||||
|  | ||||
| void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options, | ||||
|                          Error **errp) | ||||
| void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp) | ||||
| { | ||||
|     BlockdevCreateJob *s; | ||||
|     const char *fmt = BlockdevDriver_str(options->driver); | ||||
|     BlockDriver *drv = bdrv_find_format(fmt); | ||||
|     Coroutine *co; | ||||
|     BlockdevCreateCo cco; | ||||
|  | ||||
|     /* If the driver is in the schema, we know that it exists. But it may not | ||||
|      * be whitelisted. */ | ||||
| @@ -71,24 +55,22 @@ void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options, | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* Error out if the driver doesn't support .bdrv_co_create */ | ||||
|     /* Call callback if it exists */ | ||||
|     if (!drv->bdrv_co_create) { | ||||
|         error_setg(errp, "Driver does not support blockdev-create"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* Create the block job */ | ||||
|     /* TODO Running in the main context. Block drivers need to error out or add | ||||
|      * locking when they use a BDS in a different AioContext. */ | ||||
|     s = job_create(job_id, &blockdev_create_job_driver, NULL, | ||||
|                    qemu_get_aio_context(), JOB_DEFAULT | JOB_MANUAL_DISMISS, | ||||
|                    NULL, NULL, errp); | ||||
|     if (!s) { | ||||
|         return; | ||||
|     cco = (BlockdevCreateCo) { | ||||
|         .drv = drv, | ||||
|         .opts = options, | ||||
|         .ret = -EINPROGRESS, | ||||
|         .errp = errp, | ||||
|     }; | ||||
|  | ||||
|     co = qemu_coroutine_create(bdrv_co_create_co_entry, &cco); | ||||
|     qemu_coroutine_enter(co); | ||||
|     while (cco.ret == -EINPROGRESS) { | ||||
|         aio_poll(qemu_get_aio_context(), true); | ||||
|     } | ||||
|  | ||||
|     s->drv = drv, | ||||
|     s->opts = QAPI_CLONE(BlockdevCreateOptions, options), | ||||
|  | ||||
|     job_start(&s->common); | ||||
| } | ||||
|   | ||||
							
								
								
									
										288
									
								
								block/crypto.c
									
									
									
									
									
								
							
							
						
						
									
										288
									
								
								block/crypto.c
									
									
									
									
									
								
							| @@ -21,15 +21,15 @@ | ||||
| #include "qemu/osdep.h" | ||||
|  | ||||
| #include "block/block_int.h" | ||||
| #include "block/qdict.h" | ||||
| #include "sysemu/block-backend.h" | ||||
| #include "crypto/block.h" | ||||
| #include "qapi/opts-visitor.h" | ||||
| #include "qapi/qapi-visit-crypto.h" | ||||
| #include "qapi/qmp/qdict.h" | ||||
| #include "qapi/qobject-input-visitor.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qemu/option.h" | ||||
| #include "crypto.h" | ||||
| #include "block/crypto.h" | ||||
|  | ||||
| typedef struct BlockCrypto BlockCrypto; | ||||
|  | ||||
| @@ -71,6 +71,8 @@ static ssize_t block_crypto_read_func(QCryptoBlock *block, | ||||
|  | ||||
|  | ||||
| struct BlockCryptoCreateData { | ||||
|     const char *filename; | ||||
|     QemuOpts *opts; | ||||
|     BlockBackend *blk; | ||||
|     uint64_t size; | ||||
| }; | ||||
| @@ -101,18 +103,27 @@ static ssize_t block_crypto_init_func(QCryptoBlock *block, | ||||
|                                       Error **errp) | ||||
| { | ||||
|     struct BlockCryptoCreateData *data = opaque; | ||||
|  | ||||
|     if (data->size > INT64_MAX || headerlen > INT64_MAX - data->size) { | ||||
|         error_setg(errp, "The requested file size is too large"); | ||||
|         return -EFBIG; | ||||
|     } | ||||
|     int ret; | ||||
|  | ||||
|     /* User provided size should reflect amount of space made | ||||
|      * available to the guest, so we must take account of that | ||||
|      * which will be used by the crypto header | ||||
|      */ | ||||
|     return blk_truncate(data->blk, data->size + headerlen, PREALLOC_MODE_OFF, | ||||
|                         errp); | ||||
|     data->size += headerlen; | ||||
|  | ||||
|     qemu_opt_set_number(data->opts, BLOCK_OPT_SIZE, data->size, &error_abort); | ||||
|     ret = bdrv_create_file(data->filename, data->opts, errp); | ||||
|     if (ret < 0) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     data->blk = blk_new_open(data->filename, NULL, NULL, | ||||
|                              BDRV_O_RDWR | BDRV_O_PROTOCOL, errp); | ||||
|     if (!data->blk) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -148,36 +159,102 @@ static QemuOptsList block_crypto_create_opts_luks = { | ||||
|  | ||||
|  | ||||
| QCryptoBlockOpenOptions * | ||||
| block_crypto_open_opts_init(QDict *opts, Error **errp) | ||||
| block_crypto_open_opts_init(QCryptoBlockFormat format, | ||||
|                             QDict *opts, | ||||
|                             Error **errp) | ||||
| { | ||||
|     Visitor *v; | ||||
|     QCryptoBlockOpenOptions *ret; | ||||
|     QCryptoBlockOpenOptions *ret = NULL; | ||||
|     Error *local_err = NULL; | ||||
|  | ||||
|     v = qobject_input_visitor_new_flat_confused(opts, errp); | ||||
|     if (!v) { | ||||
|         return NULL; | ||||
|     ret = g_new0(QCryptoBlockOpenOptions, 1); | ||||
|     ret->format = format; | ||||
|  | ||||
|     v = qobject_input_visitor_new_keyval(QOBJECT(opts)); | ||||
|  | ||||
|     visit_start_struct(v, NULL, NULL, 0, &local_err); | ||||
|     if (local_err) { | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     visit_type_QCryptoBlockOpenOptions(v, NULL, &ret, errp); | ||||
|     switch (format) { | ||||
|     case Q_CRYPTO_BLOCK_FORMAT_LUKS: | ||||
|         visit_type_QCryptoBlockOptionsLUKS_members( | ||||
|             v, &ret->u.luks, &local_err); | ||||
|         break; | ||||
|  | ||||
|     case Q_CRYPTO_BLOCK_FORMAT_QCOW: | ||||
|         visit_type_QCryptoBlockOptionsQCow_members( | ||||
|             v, &ret->u.qcow, &local_err); | ||||
|         break; | ||||
|  | ||||
|     default: | ||||
|         error_setg(&local_err, "Unsupported block format %d", format); | ||||
|         break; | ||||
|     } | ||||
|     if (!local_err) { | ||||
|         visit_check_struct(v, &local_err); | ||||
|     } | ||||
|  | ||||
|     visit_end_struct(v, NULL); | ||||
|  | ||||
|  out: | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         qapi_free_QCryptoBlockOpenOptions(ret); | ||||
|         ret = NULL; | ||||
|     } | ||||
|     visit_free(v); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
|  | ||||
| QCryptoBlockCreateOptions * | ||||
| block_crypto_create_opts_init(QDict *opts, Error **errp) | ||||
| block_crypto_create_opts_init(QCryptoBlockFormat format, | ||||
|                               QDict *opts, | ||||
|                               Error **errp) | ||||
| { | ||||
|     Visitor *v; | ||||
|     QCryptoBlockCreateOptions *ret; | ||||
|     QCryptoBlockCreateOptions *ret = NULL; | ||||
|     Error *local_err = NULL; | ||||
|  | ||||
|     v = qobject_input_visitor_new_flat_confused(opts, errp); | ||||
|     if (!v) { | ||||
|         return NULL; | ||||
|     ret = g_new0(QCryptoBlockCreateOptions, 1); | ||||
|     ret->format = format; | ||||
|  | ||||
|     v = qobject_input_visitor_new_keyval(QOBJECT(opts)); | ||||
|  | ||||
|     visit_start_struct(v, NULL, NULL, 0, &local_err); | ||||
|     if (local_err) { | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     visit_type_QCryptoBlockCreateOptions(v, NULL, &ret, errp); | ||||
|     switch (format) { | ||||
|     case Q_CRYPTO_BLOCK_FORMAT_LUKS: | ||||
|         visit_type_QCryptoBlockCreateOptionsLUKS_members( | ||||
|             v, &ret->u.luks, &local_err); | ||||
|         break; | ||||
|  | ||||
|     case Q_CRYPTO_BLOCK_FORMAT_QCOW: | ||||
|         visit_type_QCryptoBlockOptionsQCow_members( | ||||
|             v, &ret->u.qcow, &local_err); | ||||
|         break; | ||||
|  | ||||
|     default: | ||||
|         error_setg(&local_err, "Unsupported block format %d", format); | ||||
|         break; | ||||
|     } | ||||
|     if (!local_err) { | ||||
|         visit_check_struct(v, &local_err); | ||||
|     } | ||||
|  | ||||
|     visit_end_struct(v, NULL); | ||||
|  | ||||
|  out: | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         qapi_free_QCryptoBlockCreateOptions(ret); | ||||
|         ret = NULL; | ||||
|     } | ||||
|     visit_free(v); | ||||
|     return ret; | ||||
| } | ||||
| @@ -215,9 +292,8 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, | ||||
|     } | ||||
|  | ||||
|     cryptoopts = qemu_opts_to_qdict(opts, NULL); | ||||
|     qdict_put_str(cryptoopts, "format", QCryptoBlockFormat_str(format)); | ||||
|  | ||||
|     open_opts = block_crypto_open_opts_init(cryptoopts, errp); | ||||
|     open_opts = block_crypto_open_opts_init(format, cryptoopts, errp); | ||||
|     if (!open_opts) { | ||||
|         goto cleanup; | ||||
|     } | ||||
| @@ -229,7 +305,6 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, | ||||
|                                        block_crypto_read_func, | ||||
|                                        bs, | ||||
|                                        cflags, | ||||
|                                        1, | ||||
|                                        errp); | ||||
|  | ||||
|     if (!crypto->block) { | ||||
| @@ -241,35 +316,36 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, | ||||
|  | ||||
|     ret = 0; | ||||
|  cleanup: | ||||
|     qobject_unref(cryptoopts); | ||||
|     QDECREF(cryptoopts); | ||||
|     qapi_free_QCryptoBlockOpenOptions(open_opts); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int block_crypto_co_create_generic(BlockDriverState *bs, | ||||
|                                           int64_t size, | ||||
|                                           QCryptoBlockCreateOptions *opts, | ||||
|                                           Error **errp) | ||||
| static int block_crypto_create_generic(QCryptoBlockFormat format, | ||||
|                                        const char *filename, | ||||
|                                        QemuOpts *opts, | ||||
|                                        Error **errp) | ||||
| { | ||||
|     int ret; | ||||
|     BlockBackend *blk; | ||||
|     int ret = -EINVAL; | ||||
|     QCryptoBlockCreateOptions *create_opts = NULL; | ||||
|     QCryptoBlock *crypto = NULL; | ||||
|     struct BlockCryptoCreateData data; | ||||
|     struct BlockCryptoCreateData data = { | ||||
|         .size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), | ||||
|                          BDRV_SECTOR_SIZE), | ||||
|         .opts = opts, | ||||
|         .filename = filename, | ||||
|     }; | ||||
|     QDict *cryptoopts; | ||||
|  | ||||
|     blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); | ||||
|     cryptoopts = qemu_opts_to_qdict(opts, NULL); | ||||
|  | ||||
|     ret = blk_insert_bs(blk, bs, errp); | ||||
|     if (ret < 0) { | ||||
|         goto cleanup; | ||||
|     create_opts = block_crypto_create_opts_init(format, cryptoopts, errp); | ||||
|     if (!create_opts) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     data = (struct BlockCryptoCreateData) { | ||||
|         .blk = blk, | ||||
|         .size = size, | ||||
|     }; | ||||
|  | ||||
|     crypto = qcrypto_block_create(opts, NULL, | ||||
|     crypto = qcrypto_block_create(create_opts, NULL, | ||||
|                                   block_crypto_init_func, | ||||
|                                   block_crypto_write_func, | ||||
|                                   &data, | ||||
| @@ -282,27 +358,24 @@ static int block_crypto_co_create_generic(BlockDriverState *bs, | ||||
|  | ||||
|     ret = 0; | ||||
|  cleanup: | ||||
|     QDECREF(cryptoopts); | ||||
|     qcrypto_block_free(crypto); | ||||
|     blk_unref(blk); | ||||
|     blk_unref(data.blk); | ||||
|     qapi_free_QCryptoBlockCreateOptions(create_opts); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn | ||||
| block_crypto_co_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                          PreallocMode prealloc, Error **errp) | ||||
| static int block_crypto_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                                  PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     BlockCrypto *crypto = bs->opaque; | ||||
|     uint64_t payload_offset = | ||||
|         qcrypto_block_get_payload_offset(crypto->block); | ||||
|  | ||||
|     if (payload_offset > INT64_MAX - offset) { | ||||
|         error_setg(errp, "The requested file size is too large"); | ||||
|         return -EFBIG; | ||||
|     } | ||||
|     assert(payload_offset < (INT64_MAX - offset)); | ||||
|  | ||||
|     offset += payload_offset; | ||||
|  | ||||
|     return bdrv_co_truncate(bs->file, offset, prealloc, errp); | ||||
|     return bdrv_truncate(bs->file, offset, prealloc, errp); | ||||
| } | ||||
|  | ||||
| static void block_crypto_close(BlockDriverState *bs) | ||||
| @@ -464,10 +537,7 @@ static int64_t block_crypto_getlength(BlockDriverState *bs) | ||||
|  | ||||
|     uint64_t offset = qcrypto_block_get_payload_offset(crypto->block); | ||||
|     assert(offset < INT64_MAX); | ||||
|  | ||||
|     if (offset > len) { | ||||
|         return -EIO; | ||||
|     } | ||||
|     assert(offset < len); | ||||
|  | ||||
|     len -= offset; | ||||
|  | ||||
| @@ -492,88 +562,12 @@ static int block_crypto_open_luks(BlockDriverState *bs, | ||||
|                                      bs, options, flags, errp); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn | ||||
| block_crypto_co_create_luks(BlockdevCreateOptions *create_options, Error **errp) | ||||
| { | ||||
|     BlockdevCreateOptionsLUKS *luks_opts; | ||||
|     BlockDriverState *bs = NULL; | ||||
|     QCryptoBlockCreateOptions create_opts; | ||||
|     int ret; | ||||
|  | ||||
|     assert(create_options->driver == BLOCKDEV_DRIVER_LUKS); | ||||
|     luks_opts = &create_options->u.luks; | ||||
|  | ||||
|     bs = bdrv_open_blockdev_ref(luks_opts->file, errp); | ||||
|     if (bs == NULL) { | ||||
|         return -EIO; | ||||
|     } | ||||
|  | ||||
|     create_opts = (QCryptoBlockCreateOptions) { | ||||
|         .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, | ||||
|         .u.luks = *qapi_BlockdevCreateOptionsLUKS_base(luks_opts), | ||||
|     }; | ||||
|  | ||||
|     ret = block_crypto_co_create_generic(bs, luks_opts->size, &create_opts, | ||||
|                                          errp); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     ret = 0; | ||||
| fail: | ||||
|     bdrv_unref(bs); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename, | ||||
|                                                          QemuOpts *opts, | ||||
|                                                          Error **errp) | ||||
| { | ||||
|     QCryptoBlockCreateOptions *create_opts = NULL; | ||||
|     BlockDriverState *bs = NULL; | ||||
|     QDict *cryptoopts; | ||||
|     int64_t size; | ||||
|     int ret; | ||||
|  | ||||
|     /* Parse options */ | ||||
|     size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); | ||||
|  | ||||
|     cryptoopts = qemu_opts_to_qdict_filtered(opts, NULL, | ||||
|                                              &block_crypto_create_opts_luks, | ||||
|                                              true); | ||||
|  | ||||
|     qdict_put_str(cryptoopts, "format", "luks"); | ||||
|     create_opts = block_crypto_create_opts_init(cryptoopts, errp); | ||||
|     if (!create_opts) { | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     /* Create protocol layer */ | ||||
|     ret = bdrv_create_file(filename, opts, errp); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     bs = bdrv_open(filename, NULL, NULL, | ||||
|                    BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); | ||||
|     if (!bs) { | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     /* Create format layer */ | ||||
|     ret = block_crypto_co_create_generic(bs, size, create_opts, errp); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     ret = 0; | ||||
| fail: | ||||
|     bdrv_unref(bs); | ||||
|     qapi_free_QCryptoBlockCreateOptions(create_opts); | ||||
|     qobject_unref(cryptoopts); | ||||
|     return ret; | ||||
|     return block_crypto_create_generic(Q_CRYPTO_BLOCK_FORMAT_LUKS, | ||||
|                                        filename, opts, errp); | ||||
| } | ||||
|  | ||||
| static int block_crypto_get_info_luks(BlockDriverState *bs, | ||||
| @@ -594,17 +588,20 @@ static int block_crypto_get_info_luks(BlockDriverState *bs, | ||||
| } | ||||
|  | ||||
| static ImageInfoSpecific * | ||||
| block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp) | ||||
| block_crypto_get_specific_info_luks(BlockDriverState *bs) | ||||
| { | ||||
|     BlockCrypto *crypto = bs->opaque; | ||||
|     ImageInfoSpecific *spec_info; | ||||
|     QCryptoBlockInfo *info; | ||||
|  | ||||
|     info = qcrypto_block_get_info(crypto->block, errp); | ||||
|     info = qcrypto_block_get_info(crypto->block, NULL); | ||||
|     if (!info) { | ||||
|         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->type = IMAGE_INFO_SPECIFIC_KIND_LUKS; | ||||
| @@ -619,24 +616,15 @@ block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp) | ||||
|     return spec_info; | ||||
| } | ||||
|  | ||||
| static const char *const block_crypto_strong_runtime_opts[] = { | ||||
|     BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET, | ||||
|  | ||||
|     NULL | ||||
| }; | ||||
|  | ||||
| static BlockDriver bdrv_crypto_luks = { | ||||
| BlockDriver bdrv_crypto_luks = { | ||||
|     .format_name        = "luks", | ||||
|     .instance_size      = sizeof(BlockCrypto), | ||||
|     .bdrv_probe         = block_crypto_probe_luks, | ||||
|     .bdrv_open          = block_crypto_open_luks, | ||||
|     .bdrv_close         = block_crypto_close, | ||||
|     /* This driver doesn't modify LUKS metadata except when creating image. | ||||
|      * Allow share-rw=on as a special case. */ | ||||
|     .bdrv_child_perm    = bdrv_filter_default_perms, | ||||
|     .bdrv_co_create     = block_crypto_co_create_luks, | ||||
|     .bdrv_child_perm    = bdrv_format_default_perms, | ||||
|     .bdrv_co_create_opts = block_crypto_co_create_opts_luks, | ||||
|     .bdrv_co_truncate   = block_crypto_co_truncate, | ||||
|     .bdrv_truncate      = block_crypto_truncate, | ||||
|     .create_opts        = &block_crypto_create_opts_luks, | ||||
|  | ||||
|     .bdrv_reopen_prepare = block_crypto_reopen_prepare, | ||||
| @@ -646,8 +634,6 @@ static BlockDriver bdrv_crypto_luks = { | ||||
|     .bdrv_getlength     = block_crypto_getlength, | ||||
|     .bdrv_get_info      = block_crypto_get_info_luks, | ||||
|     .bdrv_get_specific_info = block_crypto_get_specific_info_luks, | ||||
|  | ||||
|     .strong_runtime_opts = block_crypto_strong_runtime_opts, | ||||
| }; | ||||
|  | ||||
| static void block_crypto_init(void) | ||||
|   | ||||
| @@ -89,9 +89,13 @@ | ||||
|     } | ||||
|  | ||||
| QCryptoBlockCreateOptions * | ||||
| block_crypto_create_opts_init(QDict *opts, Error **errp); | ||||
| block_crypto_create_opts_init(QCryptoBlockFormat format, | ||||
|                               QDict *opts, | ||||
|                               Error **errp); | ||||
|  | ||||
| QCryptoBlockOpenOptions * | ||||
| block_crypto_open_opts_init(QDict *opts, Error **errp); | ||||
| block_crypto_open_opts_init(QCryptoBlockFormat format, | ||||
|                             QDict *opts, | ||||
|                             Error **errp); | ||||
|  | ||||
| #endif /* BLOCK_CRYPTO_H__ */ | ||||
|   | ||||
							
								
								
									
										96
									
								
								block/curl.c
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								block/curl.c
									
									
									
									
									
								
							| @@ -32,10 +32,22 @@ | ||||
| #include "crypto/secret.h" | ||||
| #include <curl/curl.h> | ||||
| #include "qemu/cutils.h" | ||||
| #include "trace.h" | ||||
|  | ||||
| // #define DEBUG_CURL | ||||
| // #define DEBUG_VERBOSE | ||||
|  | ||||
| #ifdef DEBUG_CURL | ||||
| #define DEBUG_CURL_PRINT 1 | ||||
| #else | ||||
| #define DEBUG_CURL_PRINT 0 | ||||
| #endif | ||||
| #define DPRINTF(fmt, ...)                                            \ | ||||
|     do {                                                             \ | ||||
|         if (DEBUG_CURL_PRINT) {                                      \ | ||||
|             fprintf(stderr, fmt, ## __VA_ARGS__);                    \ | ||||
|         }                                                            \ | ||||
|     } while (0) | ||||
|  | ||||
| #if LIBCURL_VERSION_NUM >= 0x071000 | ||||
| /* The multi interface timer callback was introduced in 7.16.0 */ | ||||
| #define NEED_CURL_TIMER_CALLBACK | ||||
| @@ -61,6 +73,8 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle, | ||||
|  | ||||
| #define CURL_NUM_STATES 8 | ||||
| #define CURL_NUM_ACB    8 | ||||
| #define READ_AHEAD_DEFAULT (256 * 1024) | ||||
| #define CURL_TIMEOUT_DEFAULT 5 | ||||
| #define CURL_TIMEOUT_MAX 10000 | ||||
|  | ||||
| #define CURL_BLOCK_OPT_URL       "url" | ||||
| @@ -74,10 +88,6 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle, | ||||
| #define CURL_BLOCK_OPT_PROXY_USERNAME "proxy-username" | ||||
| #define CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET "proxy-password-secret" | ||||
|  | ||||
| #define CURL_BLOCK_OPT_READAHEAD_DEFAULT (256 * 1024) | ||||
| #define CURL_BLOCK_OPT_SSLVERIFY_DEFAULT true | ||||
| #define CURL_BLOCK_OPT_TIMEOUT_DEFAULT 5 | ||||
|  | ||||
| struct BDRVCURLState; | ||||
|  | ||||
| static bool libcurl_initialized; | ||||
| @@ -144,7 +154,7 @@ static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque) | ||||
| { | ||||
|     BDRVCURLState *s = opaque; | ||||
|  | ||||
|     trace_curl_timer_cb(timeout_ms); | ||||
|     DPRINTF("CURL: timer callback timeout_ms %ld\n", timeout_ms); | ||||
|     if (timeout_ms == -1) { | ||||
|         timer_del(&s->timer); | ||||
|     } else { | ||||
| @@ -183,7 +193,7 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, | ||||
|     } | ||||
|     socket = NULL; | ||||
|  | ||||
|     trace_curl_sock_cb(action, (int)fd); | ||||
|     DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, (int)fd); | ||||
|     switch (action) { | ||||
|         case CURL_POLL_IN: | ||||
|             aio_set_fd_handler(s->aio_context, fd, false, | ||||
| @@ -228,7 +238,7 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque) | ||||
|     size_t realsize = size * nmemb; | ||||
|     int i; | ||||
|  | ||||
|     trace_curl_read_cb(realsize); | ||||
|     DPRINTF("CURL: Just reading %zd bytes\n", realsize); | ||||
|  | ||||
|     if (!s || !s->orig_buf) { | ||||
|         goto read_end; | ||||
| @@ -473,8 +483,6 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state) | ||||
|         curl_easy_setopt(state->curl, CURLOPT_URL, s->url); | ||||
|         curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER, | ||||
|                          (long) s->sslverify); | ||||
|         curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYHOST, | ||||
|                          s->sslverify ? 2L : 0L); | ||||
|         if (s->cookie) { | ||||
|             curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie); | ||||
|         } | ||||
| @@ -674,10 +682,10 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     const char *protocol_delimiter; | ||||
|     int ret; | ||||
|  | ||||
|     ret = bdrv_apply_auto_read_only(bs, "curl driver does not support writes", | ||||
|                                     errp); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|  | ||||
|     if (flags & BDRV_O_RDWR) { | ||||
|         error_setg(errp, "curl block device does not support writes"); | ||||
|         return -EROFS; | ||||
|     } | ||||
|  | ||||
|     if (!libcurl_initialized) { | ||||
| @@ -698,7 +706,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     } | ||||
|  | ||||
|     s->readahead_size = qemu_opt_get_size(opts, CURL_BLOCK_OPT_READAHEAD, | ||||
|                                           CURL_BLOCK_OPT_READAHEAD_DEFAULT); | ||||
|                                           READ_AHEAD_DEFAULT); | ||||
|     if ((s->readahead_size & 0x1ff) != 0) { | ||||
|         error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512", | ||||
|                    s->readahead_size); | ||||
| @@ -706,14 +714,13 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     } | ||||
|  | ||||
|     s->timeout = qemu_opt_get_number(opts, CURL_BLOCK_OPT_TIMEOUT, | ||||
|                                      CURL_BLOCK_OPT_TIMEOUT_DEFAULT); | ||||
|                                      CURL_TIMEOUT_DEFAULT); | ||||
|     if (s->timeout > CURL_TIMEOUT_MAX) { | ||||
|         error_setg(errp, "timeout parameter is too large or negative"); | ||||
|         goto out_noclean; | ||||
|     } | ||||
|  | ||||
|     s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, | ||||
|                                      CURL_BLOCK_OPT_SSLVERIFY_DEFAULT); | ||||
|     s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, true); | ||||
|  | ||||
|     cookie = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE); | ||||
|     cookie_secret = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE_SECRET); | ||||
| @@ -768,7 +775,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     trace_curl_open(file); | ||||
|     DPRINTF("CURL: Opening %s\n", file); | ||||
|     qemu_co_queue_init(&s->free_state_waitq); | ||||
|     s->aio_context = bdrv_get_aio_context(bs); | ||||
|     s->url = g_strdup(file); | ||||
| @@ -797,7 +804,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     } | ||||
|     /* Prior CURL 7.19.4 return value of 0 could mean that the file size is not | ||||
|      * know or the size is zero. From 7.19.4 CURL returns -1 if size is not | ||||
|      * known and zero if it is really zero-length file. */ | ||||
|      * known and zero if it is realy zero-length file. */ | ||||
| #if LIBCURL_VERSION_NUM >= 0x071304 | ||||
|     if (d < 0) { | ||||
|         pstrcpy(state->errmsg, CURL_ERROR_SIZE, | ||||
| @@ -821,7 +828,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                 "Server does not support 'range' (byte ranges)."); | ||||
|         goto out; | ||||
|     } | ||||
|     trace_curl_open_size(s->len); | ||||
|     DPRINTF("CURL: Size = %" PRIu64 "\n", s->len); | ||||
|  | ||||
|     qemu_mutex_lock(&s->mutex); | ||||
|     curl_clean_state(state); | ||||
| @@ -899,7 +906,8 @@ static void curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb) | ||||
|     state->acb[0] = acb; | ||||
|  | ||||
|     snprintf(state->range, 127, "%" PRIu64 "-%" PRIu64, start, end); | ||||
|     trace_curl_setup_preadv(acb->bytes, start, state->range); | ||||
|     DPRINTF("CURL (AIO): Reading %" PRIu64 " at %" PRIu64 " (%s)\n", | ||||
|             acb->bytes, start, state->range); | ||||
|     curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range); | ||||
|  | ||||
|     curl_multi_add_handle(s->multi, state->curl); | ||||
| @@ -933,7 +941,7 @@ static void curl_close(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVCURLState *s = bs->opaque; | ||||
|  | ||||
|     trace_curl_close(); | ||||
|     DPRINTF("CURL: Close\n"); | ||||
|     curl_detach_aio_context(bs); | ||||
|     qemu_mutex_destroy(&s->mutex); | ||||
|  | ||||
| @@ -950,36 +958,6 @@ static int64_t curl_getlength(BlockDriverState *bs) | ||||
|     return s->len; | ||||
| } | ||||
|  | ||||
| static void curl_refresh_filename(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVCURLState *s = bs->opaque; | ||||
|  | ||||
|     /* "readahead" and "timeout" do not change the guest-visible data, | ||||
|      * so ignore them */ | ||||
|     if (s->sslverify != CURL_BLOCK_OPT_SSLVERIFY_DEFAULT || | ||||
|         s->cookie || s->username || s->password || s->proxyusername || | ||||
|         s->proxypassword) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), s->url); | ||||
| } | ||||
|  | ||||
|  | ||||
| static const char *const curl_strong_runtime_opts[] = { | ||||
|     CURL_BLOCK_OPT_URL, | ||||
|     CURL_BLOCK_OPT_SSLVERIFY, | ||||
|     CURL_BLOCK_OPT_COOKIE, | ||||
|     CURL_BLOCK_OPT_COOKIE_SECRET, | ||||
|     CURL_BLOCK_OPT_USERNAME, | ||||
|     CURL_BLOCK_OPT_PASSWORD_SECRET, | ||||
|     CURL_BLOCK_OPT_PROXY_USERNAME, | ||||
|     CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET, | ||||
|  | ||||
|     NULL | ||||
| }; | ||||
|  | ||||
| static BlockDriver bdrv_http = { | ||||
|     .format_name                = "http", | ||||
|     .protocol_name              = "http", | ||||
| @@ -994,9 +972,6 @@ static BlockDriver bdrv_http = { | ||||
|  | ||||
|     .bdrv_detach_aio_context    = curl_detach_aio_context, | ||||
|     .bdrv_attach_aio_context    = curl_attach_aio_context, | ||||
|  | ||||
|     .bdrv_refresh_filename      = curl_refresh_filename, | ||||
|     .strong_runtime_opts        = curl_strong_runtime_opts, | ||||
| }; | ||||
|  | ||||
| static BlockDriver bdrv_https = { | ||||
| @@ -1013,9 +988,6 @@ static BlockDriver bdrv_https = { | ||||
|  | ||||
|     .bdrv_detach_aio_context    = curl_detach_aio_context, | ||||
|     .bdrv_attach_aio_context    = curl_attach_aio_context, | ||||
|  | ||||
|     .bdrv_refresh_filename      = curl_refresh_filename, | ||||
|     .strong_runtime_opts        = curl_strong_runtime_opts, | ||||
| }; | ||||
|  | ||||
| static BlockDriver bdrv_ftp = { | ||||
| @@ -1032,9 +1004,6 @@ static BlockDriver bdrv_ftp = { | ||||
|  | ||||
|     .bdrv_detach_aio_context    = curl_detach_aio_context, | ||||
|     .bdrv_attach_aio_context    = curl_attach_aio_context, | ||||
|  | ||||
|     .bdrv_refresh_filename      = curl_refresh_filename, | ||||
|     .strong_runtime_opts        = curl_strong_runtime_opts, | ||||
| }; | ||||
|  | ||||
| static BlockDriver bdrv_ftps = { | ||||
| @@ -1051,9 +1020,6 @@ static BlockDriver bdrv_ftps = { | ||||
|  | ||||
|     .bdrv_detach_aio_context    = curl_detach_aio_context, | ||||
|     .bdrv_attach_aio_context    = curl_attach_aio_context, | ||||
|  | ||||
|     .bdrv_refresh_filename      = curl_refresh_filename, | ||||
|     .strong_runtime_opts        = curl_strong_runtime_opts, | ||||
| }; | ||||
|  | ||||
| static void curl_block_init(void) | ||||
|   | ||||
| @@ -28,12 +28,19 @@ | ||||
| #include "block/block_int.h" | ||||
| #include "block/blockjob.h" | ||||
|  | ||||
| /** | ||||
|  * A BdrvDirtyBitmap can be in three possible states: | ||||
|  * (1) successor is NULL and disabled is false: full r/w mode | ||||
|  * (2) successor is NULL and disabled is true: read only mode ("disabled") | ||||
|  * (3) successor is set: frozen mode. | ||||
|  *     A frozen bitmap cannot be renamed, deleted, anonymized, cleared, set, | ||||
|  *     or enabled. A frozen bitmap can only abdicate() or reclaim(). | ||||
|  */ | ||||
| struct BdrvDirtyBitmap { | ||||
|     QemuMutex *mutex; | ||||
|     HBitmap *bitmap;            /* Dirty bitmap implementation */ | ||||
|     HBitmap *meta;              /* Meta dirty bitmap */ | ||||
|     bool busy;                  /* Bitmap is busy, it can't be used via QMP */ | ||||
|     BdrvDirtyBitmap *successor; /* Anonymous child, if any. */ | ||||
|     BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */ | ||||
|     char *name;                 /* Optional non-empty unique ID */ | ||||
|     int64_t size;               /* Size of the bitmap, in bytes */ | ||||
|     bool disabled;              /* Bitmap is disabled. It ignores all writes to | ||||
| @@ -46,13 +53,6 @@ struct BdrvDirtyBitmap { | ||||
|                                    and this bitmap must remain unchanged while | ||||
|                                    this flag is set. */ | ||||
|     bool persistent;            /* bitmap must be saved to owner disk image */ | ||||
|     bool inconsistent;          /* bitmap is persistent, but inconsistent. | ||||
|                                    It cannot be used at all in any way, except | ||||
|                                    a QMP user can remove it. */ | ||||
|     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; | ||||
| }; | ||||
|  | ||||
| @@ -95,6 +95,15 @@ BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name) | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken.  */ | ||||
| void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     assert(!bdrv_dirty_bitmap_frozen(bitmap)); | ||||
|     g_free(bitmap->name); | ||||
|     bitmap->name = NULL; | ||||
|     bitmap->persistent = false; | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken.  */ | ||||
| BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, | ||||
|                                           uint32_t granularity, | ||||
| @@ -169,59 +178,22 @@ const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap) | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken.  */ | ||||
| bool bdrv_dirty_bitmap_has_successor(BdrvDirtyBitmap *bitmap) | ||||
| bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     return bitmap->successor; | ||||
| } | ||||
|  | ||||
| static bool bdrv_dirty_bitmap_busy(const BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     return bitmap->busy; | ||||
| } | ||||
|  | ||||
| void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy) | ||||
| { | ||||
|     qemu_mutex_lock(bitmap->mutex); | ||||
|     bitmap->busy = busy; | ||||
|     qemu_mutex_unlock(bitmap->mutex); | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken.  */ | ||||
| bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     return !bitmap->disabled; | ||||
|     return !(bitmap->disabled || bitmap->successor); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * bdrv_dirty_bitmap_status: This API is now deprecated. | ||||
|  * Called with BQL taken. | ||||
|  * | ||||
|  * A BdrvDirtyBitmap can be in four possible user-visible states: | ||||
|  * (1) Active:   successor is NULL, and disabled is false: full r/w mode | ||||
|  * (2) Disabled: successor is NULL, and disabled is true: qualified r/w mode, | ||||
|  *               guest writes are dropped, but monitor writes are possible, | ||||
|  *               through commands like merge and clear. | ||||
|  * (3) Frozen:   successor is not NULL. | ||||
|  *               A frozen bitmap cannot be renamed, deleted, cleared, set, | ||||
|  *               enabled, merged to, etc. A frozen bitmap can only abdicate() | ||||
|  *               or reclaim(). | ||||
|  *               In this state, the anonymous successor bitmap may be either | ||||
|  *               Active and recording writes from the guest (e.g. backup jobs), | ||||
|  *               or it can be Disabled and not recording writes. | ||||
|  * (4) Locked:   Whether Active or Disabled, the user cannot modify this bitmap | ||||
|  *               in any way from the monitor. | ||||
|  * (5) Inconsistent: This is a persistent bitmap whose "in use" bit is set, and | ||||
|  *                   is unusable by QEMU. It can be deleted to remove it from | ||||
|  *                   the qcow2. | ||||
|  */ | ||||
| /* Called with BQL taken.  */ | ||||
| DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     if (bdrv_dirty_bitmap_inconsistent(bitmap)) { | ||||
|         return DIRTY_BITMAP_STATUS_INCONSISTENT; | ||||
|     } else if (bdrv_dirty_bitmap_has_successor(bitmap)) { | ||||
|     if (bdrv_dirty_bitmap_frozen(bitmap)) { | ||||
|         return DIRTY_BITMAP_STATUS_FROZEN; | ||||
|     } else if (bdrv_dirty_bitmap_busy(bitmap)) { | ||||
|         return DIRTY_BITMAP_STATUS_LOCKED; | ||||
|     } else if (!bdrv_dirty_bitmap_enabled(bitmap)) { | ||||
|         return DIRTY_BITMAP_STATUS_DISABLED; | ||||
|     } else { | ||||
| @@ -229,44 +201,9 @@ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken.  */ | ||||
| static bool bdrv_dirty_bitmap_recording(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     return !bitmap->disabled || (bitmap->successor && | ||||
|                                  !bitmap->successor->disabled); | ||||
| } | ||||
|  | ||||
| int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags, | ||||
|                             Error **errp) | ||||
| { | ||||
|     if ((flags & BDRV_BITMAP_BUSY) && bdrv_dirty_bitmap_busy(bitmap)) { | ||||
|         error_setg(errp, "Bitmap '%s' is currently in use by another" | ||||
|                    " operation and cannot be used", bitmap->name); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if ((flags & BDRV_BITMAP_RO) && bdrv_dirty_bitmap_readonly(bitmap)) { | ||||
|         error_setg(errp, "Bitmap '%s' is readonly and cannot be modified", | ||||
|                    bitmap->name); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if ((flags & BDRV_BITMAP_INCONSISTENT) && | ||||
|         bdrv_dirty_bitmap_inconsistent(bitmap)) { | ||||
|         error_setg(errp, "Bitmap '%s' is inconsistent and cannot be used", | ||||
|                    bitmap->name); | ||||
|         error_append_hint(errp, "Try block-dirty-bitmap-remove to delete" | ||||
|                           " this bitmap from disk"); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Create a successor bitmap destined to replace this bitmap after an operation. | ||||
|  * Requires that the bitmap is not marked busy and has no successor. | ||||
|  * The successor will be enabled if the parent bitmap was. | ||||
|  * Requires that the bitmap is not frozen and has no successor. | ||||
|  * Called with BQL taken. | ||||
|  */ | ||||
| int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, | ||||
| @@ -275,14 +212,12 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, | ||||
|     uint64_t granularity; | ||||
|     BdrvDirtyBitmap *child; | ||||
|  | ||||
|     if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) { | ||||
|         return -1; | ||||
|     } | ||||
|     if (bdrv_dirty_bitmap_has_successor(bitmap)) { | ||||
|         error_setg(errp, "Cannot create a successor for a bitmap that already " | ||||
|                    "has one"); | ||||
|     if (bdrv_dirty_bitmap_frozen(bitmap)) { | ||||
|         error_setg(errp, "Cannot create a successor for a bitmap that is " | ||||
|                    "currently frozen"); | ||||
|         return -1; | ||||
|     } | ||||
|     assert(!bitmap->successor); | ||||
|  | ||||
|     /* Create an anonymous successor */ | ||||
|     granularity = bdrv_dirty_bitmap_granularity(bitmap); | ||||
| @@ -293,41 +228,12 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, | ||||
|  | ||||
|     /* Successor will be on or off based on our current state. */ | ||||
|     child->disabled = bitmap->disabled; | ||||
|     bitmap->disabled = true; | ||||
|  | ||||
|     /* Install the successor and mark the parent as busy */ | ||||
|     /* Install the successor and freeze the parent */ | ||||
|     bitmap->successor = child; | ||||
|     bitmap->busy = true; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *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_busy(bitmap)); | ||||
|     assert(!bdrv_dirty_bitmap_has_successor(bitmap)); | ||||
|     assert(!bitmap->meta); | ||||
|     QLIST_REMOVE(bitmap, list); | ||||
|     hbitmap_free(bitmap->bitmap); | ||||
|     g_free(bitmap->name); | ||||
|     g_free(bitmap); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * For a bitmap with a successor, yield our name to the successor, | ||||
|  * delete the old bitmap, and return a handle to the new bitmap. | ||||
| @@ -352,7 +258,6 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, | ||||
|     bitmap->successor = NULL; | ||||
|     successor->persistent = bitmap->persistent; | ||||
|     bitmap->persistent = false; | ||||
|     bitmap->busy = false; | ||||
|     bdrv_release_dirty_bitmap(bs, bitmap); | ||||
|  | ||||
|     return successor; | ||||
| @@ -361,13 +266,12 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, | ||||
| /** | ||||
|  * In cases of failure where we can no longer safely delete the parent, | ||||
|  * we may wish to re-join the parent and child/successor. | ||||
|  * The merged parent will be marked as not busy. | ||||
|  * The marged parent will be enabled if and only if the successor was enabled. | ||||
|  * Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken. | ||||
|  * The merged parent will be un-frozen, but not explicitly re-enabled. | ||||
|  * Called with BQL taken. | ||||
|  */ | ||||
| BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, | ||||
|                                                   BdrvDirtyBitmap *parent, | ||||
|                                                   Error **errp) | ||||
| BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, | ||||
|                                            BdrvDirtyBitmap *parent, | ||||
|                                            Error **errp) | ||||
| { | ||||
|     BdrvDirtyBitmap *successor = parent->successor; | ||||
|  | ||||
| @@ -376,33 +280,16 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     if (!hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap)) { | ||||
|     if (!hbitmap_merge(parent->bitmap, successor->bitmap)) { | ||||
|         error_setg(errp, "Merging of parent and successor bitmap failed"); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     parent->disabled = successor->disabled; | ||||
|     parent->busy = false; | ||||
|     bdrv_release_dirty_bitmap_locked(successor); | ||||
|     bdrv_release_dirty_bitmap(bs, successor); | ||||
|     parent->successor = NULL; | ||||
|  | ||||
|     return parent; | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken. */ | ||||
| BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, | ||||
|                                            BdrvDirtyBitmap *parent, | ||||
|                                            Error **errp) | ||||
| { | ||||
|     BdrvDirtyBitmap *ret; | ||||
|  | ||||
|     qemu_mutex_lock(parent->mutex); | ||||
|     ret = bdrv_reclaim_dirty_bitmap_locked(bs, parent, errp); | ||||
|     qemu_mutex_unlock(parent->mutex); | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Truncates _all_ bitmaps attached to a BDS. | ||||
|  * Called with BQL taken. | ||||
| @@ -413,8 +300,7 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes) | ||||
|  | ||||
|     bdrv_dirty_bitmaps_lock(bs); | ||||
|     QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { | ||||
|         assert(!bdrv_dirty_bitmap_busy(bitmap)); | ||||
|         assert(!bdrv_dirty_bitmap_has_successor(bitmap)); | ||||
|         assert(!bdrv_dirty_bitmap_frozen(bitmap)); | ||||
|         assert(!bitmap->active_iterators); | ||||
|         hbitmap_truncate(bitmap->bitmap, bytes); | ||||
|         bitmap->size = bytes; | ||||
| @@ -422,31 +308,68 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes) | ||||
|     bdrv_dirty_bitmaps_unlock(bs); | ||||
| } | ||||
|  | ||||
| static bool bdrv_dirty_bitmap_has_name(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     return !!bdrv_dirty_bitmap_name(bitmap); | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken.  */ | ||||
| static void bdrv_do_release_matching_dirty_bitmap( | ||||
|     BlockDriverState *bs, BdrvDirtyBitmap *bitmap, | ||||
|     bool (*cond)(BdrvDirtyBitmap *bitmap)) | ||||
| { | ||||
|     BdrvDirtyBitmap *bm, *next; | ||||
|     bdrv_dirty_bitmaps_lock(bs); | ||||
|     QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { | ||||
|         if ((!bitmap || bm == bitmap) && (!cond || cond(bm))) { | ||||
|             assert(!bm->active_iterators); | ||||
|             assert(!bdrv_dirty_bitmap_frozen(bm)); | ||||
|             assert(!bm->meta); | ||||
|             QLIST_REMOVE(bm, list); | ||||
|             hbitmap_free(bm->bitmap); | ||||
|             g_free(bm->name); | ||||
|             g_free(bm); | ||||
|  | ||||
|             if (bitmap) { | ||||
|                 goto out; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     if (bitmap) { | ||||
|         abort(); | ||||
|     } | ||||
|  | ||||
| out: | ||||
|     bdrv_dirty_bitmaps_unlock(bs); | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken.  */ | ||||
| void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     bdrv_dirty_bitmaps_lock(bs); | ||||
|     bdrv_release_dirty_bitmap_locked(bitmap); | ||||
|     bdrv_dirty_bitmaps_unlock(bs); | ||||
|     bdrv_do_release_matching_dirty_bitmap(bs, bitmap, NULL); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()). | ||||
|  * There must not be any busy bitmaps attached. | ||||
|  * There must not be any frozen bitmaps attached. | ||||
|  * This function does not remove persistent bitmaps from the storage. | ||||
|  * Called with BQL taken. | ||||
|  */ | ||||
| void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) | ||||
| { | ||||
|     BdrvDirtyBitmap *bm, *next; | ||||
|     bdrv_do_release_matching_dirty_bitmap(bs, NULL, bdrv_dirty_bitmap_has_name); | ||||
| } | ||||
|  | ||||
|     bdrv_dirty_bitmaps_lock(bs); | ||||
|     QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { | ||||
|         if (bdrv_dirty_bitmap_name(bm)) { | ||||
|             bdrv_release_dirty_bitmap_locked(bm); | ||||
|         } | ||||
|     } | ||||
|     bdrv_dirty_bitmaps_unlock(bs); | ||||
| /** | ||||
|  * Release all persistent dirty bitmaps attached to a BDS (for use in | ||||
|  * bdrv_inactivate_recurse()). | ||||
|  * There must not be any frozen bitmaps attached. | ||||
|  * This function does not remove persistent bitmaps from the storage. | ||||
|  */ | ||||
| void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs) | ||||
| { | ||||
|     bdrv_do_release_matching_dirty_bitmap(bs, NULL, | ||||
|                                           bdrv_dirty_bitmap_get_persistance); | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -466,18 +389,18 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken.  */ | ||||
| void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     bdrv_dirty_bitmap_lock(bitmap); | ||||
|     assert(!bdrv_dirty_bitmap_frozen(bitmap)); | ||||
|     bitmap->disabled = true; | ||||
|     bdrv_dirty_bitmap_unlock(bitmap); | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken.  */ | ||||
| void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     bdrv_dirty_bitmap_lock(bitmap); | ||||
|     bdrv_enable_dirty_bitmap_locked(bitmap); | ||||
|     bdrv_dirty_bitmap_unlock(bitmap); | ||||
|     assert(!bdrv_dirty_bitmap_frozen(bitmap)); | ||||
|     bitmap->disabled = false; | ||||
| } | ||||
|  | ||||
| BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) | ||||
| @@ -495,11 +418,6 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) | ||||
|         info->has_name = !!bm->name; | ||||
|         info->name = g_strdup(bm->name); | ||||
|         info->status = bdrv_dirty_bitmap_status(bm); | ||||
|         info->recording = bdrv_dirty_bitmap_recording(bm); | ||||
|         info->busy = bdrv_dirty_bitmap_busy(bm); | ||||
|         info->persistent = bm->persistent; | ||||
|         info->has_inconsistent = bm->inconsistent; | ||||
|         info->inconsistent = bm->inconsistent; | ||||
|         entry->value = info; | ||||
|         *plist = entry; | ||||
|         plist = &entry->next; | ||||
| @@ -582,6 +500,7 @@ int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) | ||||
| void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, | ||||
|                                   int64_t offset, int64_t bytes) | ||||
| { | ||||
|     assert(bdrv_dirty_bitmap_enabled(bitmap)); | ||||
|     assert(!bdrv_dirty_bitmap_readonly(bitmap)); | ||||
|     hbitmap_set(bitmap->bitmap, offset, bytes); | ||||
| } | ||||
| @@ -598,6 +517,7 @@ void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap, | ||||
| void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, | ||||
|                                     int64_t offset, int64_t bytes) | ||||
| { | ||||
|     assert(bdrv_dirty_bitmap_enabled(bitmap)); | ||||
|     assert(!bdrv_dirty_bitmap_readonly(bitmap)); | ||||
|     hbitmap_reset(bitmap->bitmap, offset, bytes); | ||||
| } | ||||
| @@ -612,6 +532,7 @@ void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap, | ||||
|  | ||||
| void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out) | ||||
| { | ||||
|     assert(bdrv_dirty_bitmap_enabled(bitmap)); | ||||
|     assert(!bdrv_dirty_bitmap_readonly(bitmap)); | ||||
|     bdrv_dirty_bitmap_lock(bitmap); | ||||
|     if (!out) { | ||||
| @@ -625,11 +546,12 @@ void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out) | ||||
|     bdrv_dirty_bitmap_unlock(bitmap); | ||||
| } | ||||
|  | ||||
| void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup) | ||||
| void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in) | ||||
| { | ||||
|     HBitmap *tmp = bitmap->bitmap; | ||||
|     assert(bdrv_dirty_bitmap_enabled(bitmap)); | ||||
|     assert(!bdrv_dirty_bitmap_readonly(bitmap)); | ||||
|     bitmap->bitmap = backup; | ||||
|     bitmap->bitmap = in; | ||||
|     hbitmap_free(tmp); | ||||
| } | ||||
|  | ||||
| @@ -740,46 +662,23 @@ bool bdrv_has_readonly_bitmaps(BlockDriverState *bs) | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken. */ | ||||
| void bdrv_dirty_bitmap_set_persistence(BdrvDirtyBitmap *bitmap, bool persistent) | ||||
| void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent) | ||||
| { | ||||
|     qemu_mutex_lock(bitmap->mutex); | ||||
|     bitmap->persistent = persistent; | ||||
|     qemu_mutex_unlock(bitmap->mutex); | ||||
| } | ||||
|  | ||||
| /* Called with BQL taken. */ | ||||
| void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap) | ||||
| bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     qemu_mutex_lock(bitmap->mutex); | ||||
|     assert(bitmap->persistent == true); | ||||
|     bitmap->inconsistent = true; | ||||
|     bitmap->disabled = true; | ||||
|     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_persistence(BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     return bitmap->persistent && !bitmap->migration; | ||||
| } | ||||
|  | ||||
| bool bdrv_dirty_bitmap_inconsistent(const BdrvDirtyBitmap *bitmap) | ||||
| { | ||||
|     return bitmap->inconsistent; | ||||
|     return bitmap->persistent; | ||||
| } | ||||
|  | ||||
| bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs) | ||||
| { | ||||
|     BdrvDirtyBitmap *bm; | ||||
|     QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { | ||||
|         if (bm->persistent && !bm->readonly && !bm->migration) { | ||||
|         if (bm->persistent && !bm->readonly) { | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| @@ -799,50 +698,7 @@ char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp) | ||||
|     return hbitmap_sha256(bitmap->bitmap, errp); | ||||
| } | ||||
|  | ||||
| int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset, | ||||
|                                     uint64_t bytes) | ||||
| int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset) | ||||
| { | ||||
|     return hbitmap_next_zero(bitmap->bitmap, offset, bytes); | ||||
| } | ||||
|  | ||||
| bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap, | ||||
|                                        uint64_t *offset, uint64_t *bytes) | ||||
| { | ||||
|     return hbitmap_next_dirty_area(bitmap->bitmap, offset, bytes); | ||||
| } | ||||
|  | ||||
| void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, | ||||
|                              HBitmap **backup, Error **errp) | ||||
| { | ||||
|     bool ret; | ||||
|  | ||||
|     /* only bitmaps from one bds are supported */ | ||||
|     assert(dest->mutex == src->mutex); | ||||
|  | ||||
|     qemu_mutex_lock(dest->mutex); | ||||
|  | ||||
|     if (bdrv_dirty_bitmap_check(dest, BDRV_BITMAP_DEFAULT, errp)) { | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     if (bdrv_dirty_bitmap_check(src, BDRV_BITMAP_ALLOW_RO, errp)) { | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     if (!hbitmap_can_merge(dest->bitmap, src->bitmap)) { | ||||
|         error_setg(errp, "Bitmaps are incompatible and can't be merged"); | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     if (backup) { | ||||
|         *backup = dest->bitmap; | ||||
|         dest->bitmap = hbitmap_alloc(dest->size, hbitmap_granularity(*backup)); | ||||
|         ret = hbitmap_merge(*backup, src->bitmap, dest->bitmap); | ||||
|     } else { | ||||
|         ret = hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap); | ||||
|     } | ||||
|     assert(ret); | ||||
|  | ||||
| out: | ||||
|     qemu_mutex_unlock(dest->mutex); | ||||
|     return hbitmap_next_zero(bitmap->bitmap, offset); | ||||
| } | ||||
|   | ||||
| @@ -1,49 +0,0 @@ | ||||
| /* | ||||
|  * DMG lzfse uncompression | ||||
|  * | ||||
|  * Copyright (c) 2018 Julio Cesar Faracco | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "dmg.h" | ||||
| #include <lzfse.h> | ||||
|  | ||||
| static int dmg_uncompress_lzfse_do(char *next_in, unsigned int avail_in, | ||||
|                                    char *next_out, unsigned int avail_out) | ||||
| { | ||||
|     size_t out_size = lzfse_decode_buffer((uint8_t *) next_out, avail_out, | ||||
|                                           (uint8_t *) next_in, avail_in, | ||||
|                                           NULL); | ||||
|  | ||||
|     /* We need to decode the single chunk only. */ | ||||
|     /* So, out_size == avail_out is not an error here. */ | ||||
|     if (out_size > 0) { | ||||
|         return out_size; | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| __attribute__((constructor)) | ||||
| static void dmg_lzfse_init(void) | ||||
| { | ||||
|     assert(!dmg_uncompress_lzfse); | ||||
|     dmg_uncompress_lzfse = dmg_uncompress_lzfse_do; | ||||
| } | ||||
							
								
								
									
										104
									
								
								block/dmg.c
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								block/dmg.c
									
									
									
									
									
								
							| @@ -33,9 +33,6 @@ | ||||
| int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in, | ||||
|                           char *next_out, unsigned int avail_out); | ||||
|  | ||||
| int (*dmg_uncompress_lzfse)(char *next_in, unsigned int avail_in, | ||||
|                             char *next_out, unsigned int avail_out); | ||||
|  | ||||
| enum { | ||||
|     /* Limit chunk sizes to prevent unreasonable amounts of memory being used | ||||
|      * or truncating when converting to 32-bit types | ||||
| @@ -44,19 +41,6 @@ enum { | ||||
|     DMG_SECTORCOUNTS_MAX = DMG_LENGTHS_MAX / 512, | ||||
| }; | ||||
|  | ||||
| enum { | ||||
|     /* DMG Block Type */ | ||||
|     UDZE = 0, /* Zeroes */ | ||||
|     UDRW,     /* RAW type */ | ||||
|     UDIG,     /* Ignore */ | ||||
|     UDCO = 0x80000004, | ||||
|     UDZO, | ||||
|     UDBZ, | ||||
|     ULFO, | ||||
|     UDCM = 0x7ffffffe, /* Comments */ | ||||
|     UDLE = 0xffffffff  /* Last Entry */ | ||||
| }; | ||||
|  | ||||
| static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||
| { | ||||
|     int len; | ||||
| @@ -121,17 +105,15 @@ static void update_max_chunk_size(BDRVDMGState *s, uint32_t chunk, | ||||
|     uint32_t uncompressed_sectors = 0; | ||||
|  | ||||
|     switch (s->types[chunk]) { | ||||
|     case UDZO: /* zlib compressed */ | ||||
|     case UDBZ: /* bzip2 compressed */ | ||||
|     case ULFO: /* lzfse compressed */ | ||||
|     case 0x80000005: /* zlib compressed */ | ||||
|     case 0x80000006: /* bzip2 compressed */ | ||||
|         compressed_size = s->lengths[chunk]; | ||||
|         uncompressed_sectors = s->sectorcounts[chunk]; | ||||
|         break; | ||||
|     case UDRW: /* copy */ | ||||
|     case 1: /* copy */ | ||||
|         uncompressed_sectors = DIV_ROUND_UP(s->lengths[chunk], 512); | ||||
|         break; | ||||
|     case UDZE: /* zero */ | ||||
|     case UDIG: /* ignore */ | ||||
|     case 2: /* zero */ | ||||
|         /* as the all-zeroes block may be large, it is treated specially: the | ||||
|          * sector is not copied from a large buffer, a simple memset is used | ||||
|          * instead. Therefore uncompressed_sectors does not need to be set. */ | ||||
| @@ -200,15 +182,12 @@ typedef struct DmgHeaderState { | ||||
| static bool dmg_is_known_block_type(uint32_t entry_type) | ||||
| { | ||||
|     switch (entry_type) { | ||||
|     case UDZE:    /* zeros */ | ||||
|     case UDRW:    /* uncompressed */ | ||||
|     case UDIG:    /* ignore */ | ||||
|     case UDZO:    /* zlib */ | ||||
|     case 0x00000001:    /* uncompressed */ | ||||
|     case 0x00000002:    /* zeroes */ | ||||
|     case 0x80000005:    /* zlib */ | ||||
|         return true; | ||||
|     case UDBZ:    /* bzip2 */ | ||||
|     case 0x80000006:    /* bzip2 */ | ||||
|         return !!dmg_uncompress_bz2; | ||||
|     case ULFO:    /* lzfse */ | ||||
|         return !!dmg_uncompress_lzfse; | ||||
|     default: | ||||
|         return false; | ||||
|     } | ||||
| @@ -267,10 +246,9 @@ static int dmg_read_mish_block(BDRVDMGState *s, DmgHeaderState *ds, | ||||
|         /* sector count */ | ||||
|         s->sectorcounts[i] = buff_read_uint64(buffer, offset + 0x10); | ||||
|  | ||||
|         /* all-zeroes sector (type UDZE and UDIG) does not need to be | ||||
|          * "uncompressed" and can therefore be unbounded. */ | ||||
|         if (s->types[i] != UDZE && s->types[i] != UDIG | ||||
|             && s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) { | ||||
|         /* all-zeroes sector (type 2) does not need to be "uncompressed" and can | ||||
|          * therefore be unbounded. */ | ||||
|         if (s->types[i] != 2 && s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) { | ||||
|             error_report("sector count %" PRIu64 " for chunk %" PRIu32 | ||||
|                          " is larger than max (%u)", | ||||
|                          s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX); | ||||
| @@ -435,19 +413,24 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     int64_t offset; | ||||
|     int ret; | ||||
|  | ||||
|     ret = bdrv_apply_auto_read_only(bs, NULL, errp); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||
|                                false, errp); | ||||
|     if (!bs->file) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     if (!bdrv_is_read_only(bs)) { | ||||
|         error_report("Opening dmg images without an explicit read-only=on " | ||||
|                      "option is deprecated. Future versions will refuse to " | ||||
|                      "open the image instead of automatically marking the " | ||||
|                      "image read-only."); | ||||
|         ret = bdrv_set_read_only(bs, true, errp); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     block_module_load_one("dmg-bz2"); | ||||
|     block_module_load_one("dmg-lzfse"); | ||||
|  | ||||
|     s->n_chunks = 0; | ||||
|     s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; | ||||
| @@ -575,20 +558,16 @@ static inline uint32_t search_chunk(BDRVDMGState *s, uint64_t sector_num) | ||||
| { | ||||
|     /* binary search */ | ||||
|     uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3; | ||||
|     while (chunk1 <= chunk2) { | ||||
|     while (chunk1 != chunk2) { | ||||
|         chunk3 = (chunk1 + chunk2) / 2; | ||||
|         if (s->sectors[chunk3] > sector_num) { | ||||
|             if (chunk3 == 0) { | ||||
|                 goto err; | ||||
|             } | ||||
|             chunk2 = chunk3 - 1; | ||||
|             chunk2 = chunk3; | ||||
|         } else if (s->sectors[chunk3] + s->sectorcounts[chunk3] > sector_num) { | ||||
|             return chunk3; | ||||
|         } else { | ||||
|             chunk1 = chunk3 + 1; | ||||
|             chunk1 = chunk3; | ||||
|         } | ||||
|     } | ||||
| err: | ||||
|     return s->n_chunks; /* error */ | ||||
| } | ||||
|  | ||||
| @@ -606,7 +585,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) | ||||
|  | ||||
|         s->current_chunk = s->n_chunks; | ||||
|         switch (s->types[chunk]) { /* block entry type */ | ||||
|         case UDZO: { /* zlib compressed */ | ||||
|         case 0x80000005: { /* zlib compressed */ | ||||
|             /* we need to buffer, because only the chunk as whole can be | ||||
|              * inflated. */ | ||||
|             ret = bdrv_pread(bs->file, s->offsets[chunk], | ||||
| @@ -629,7 +608,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) | ||||
|                 return -1; | ||||
|             } | ||||
|             break; } | ||||
|         case UDBZ: /* bzip2 compressed */ | ||||
|         case 0x80000006: /* bzip2 compressed */ | ||||
|             if (!dmg_uncompress_bz2) { | ||||
|                 break; | ||||
|             } | ||||
| @@ -650,36 +629,14 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) | ||||
|                 return ret; | ||||
|             } | ||||
|             break; | ||||
|         case ULFO: | ||||
|             if (!dmg_uncompress_lzfse) { | ||||
|                 break; | ||||
|             } | ||||
|             /* we need to buffer, because only the chunk as whole can be | ||||
|              * inflated. */ | ||||
|             ret = bdrv_pread(bs->file, s->offsets[chunk], | ||||
|                              s->compressed_chunk, s->lengths[chunk]); | ||||
|             if (ret != s->lengths[chunk]) { | ||||
|                 return -1; | ||||
|             } | ||||
|  | ||||
|             ret = dmg_uncompress_lzfse((char *)s->compressed_chunk, | ||||
|                                        (unsigned int) s->lengths[chunk], | ||||
|                                        (char *)s->uncompressed_chunk, | ||||
|                                        (unsigned int) | ||||
|                                            (512 * s->sectorcounts[chunk])); | ||||
|             if (ret < 0) { | ||||
|                 return ret; | ||||
|             } | ||||
|             break; | ||||
|         case UDRW: /* copy */ | ||||
|         case 1: /* copy */ | ||||
|             ret = bdrv_pread(bs->file, s->offsets[chunk], | ||||
|                              s->uncompressed_chunk, s->lengths[chunk]); | ||||
|             if (ret != s->lengths[chunk]) { | ||||
|                 return -1; | ||||
|             } | ||||
|             break; | ||||
|         case UDZE: /* zeros */ | ||||
|         case UDIG: /* ignore */ | ||||
|         case 2: /* zero */ | ||||
|             /* see dmg_read, it is treated specially. No buffer needs to be | ||||
|              * pre-filled, the zeroes can be set directly. */ | ||||
|             break; | ||||
| @@ -714,8 +671,7 @@ dmg_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, | ||||
|         /* Special case: current chunk is all zeroes. Do not perform a memcpy as | ||||
|          * s->uncompressed_chunk may be too small to cover the large all-zeroes | ||||
|          * section. dmg_read_chunk is called to find s->current_chunk */ | ||||
|         if (s->types[s->current_chunk] == UDZE | ||||
|             || s->types[s->current_chunk] == UDIG) { /* all zeroes block entry */ | ||||
|         if (s->types[s->current_chunk] == 2) { /* all zeroes block entry */ | ||||
|             qemu_iovec_memset(qiov, i * 512, 0, 512); | ||||
|             continue; | ||||
|         } | ||||
|   | ||||
| @@ -55,7 +55,4 @@ typedef struct BDRVDMGState { | ||||
| extern int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in, | ||||
|                                  char *next_out, unsigned int avail_out); | ||||
|  | ||||
| extern int (*dmg_uncompress_lzfse)(char *next_in, unsigned int avail_in, | ||||
|                                    char *next_out, unsigned int avail_out); | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										1446
									
								
								block/file-posix.c
									
									
									
									
									
								
							
							
						
						
									
										1446
									
								
								block/file-posix.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -162,7 +162,7 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile, | ||||
|     acb->aio_nbytes = count; | ||||
|     acb->aio_offset = offset; | ||||
|  | ||||
|     trace_file_paio_submit(acb, opaque, offset, count, type); | ||||
|     trace_paio_submit(acb, opaque, offset, count, type); | ||||
|     pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); | ||||
|     return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); | ||||
| } | ||||
| @@ -176,7 +176,7 @@ int qemu_ftruncate64(int fd, int64_t length) | ||||
|     BOOL res; | ||||
|  | ||||
|     if ((GetVersion() & 0x80000000UL) && (length >> 32) != 0) | ||||
|         return -1; | ||||
| 	return -1; | ||||
|  | ||||
|     h = (HANDLE)_get_osfhandle(fd); | ||||
|  | ||||
| @@ -184,13 +184,13 @@ int qemu_ftruncate64(int fd, int64_t length) | ||||
|     li.HighPart = 0; | ||||
|     li.LowPart = SetFilePointer (h, 0, &li.HighPart, FILE_CURRENT); | ||||
|     if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { | ||||
|         return -1; | ||||
| 	return -1; | ||||
|     } | ||||
|  | ||||
|     high = length >> 32; | ||||
|     dw = SetFilePointer(h, (DWORD) length, &high, FILE_BEGIN); | ||||
|     if (dw == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { | ||||
|         return -1; | ||||
| 	return -1; | ||||
|     } | ||||
|     res = SetEndOfFile(h); | ||||
|  | ||||
| @@ -203,7 +203,7 @@ static int set_sparse(int fd) | ||||
| { | ||||
|     DWORD returned; | ||||
|     return (int) DeviceIoControl((HANDLE)_get_osfhandle(fd), FSCTL_SET_SPARSE, | ||||
|                                  NULL, 0, NULL, 0, &returned, NULL); | ||||
| 				 NULL, 0, NULL, 0, &returned, NULL); | ||||
| } | ||||
|  | ||||
| static void raw_detach_aio_context(BlockDriverState *bs) | ||||
| @@ -251,11 +251,7 @@ static void raw_probe_alignment(BlockDriverState *bs, Error **errp) | ||||
|                          &dg.Geometry.BytesPerSector, | ||||
|                          &freeClusters, &totalClusters); | ||||
|         bs->bl.request_alignment = dg.Geometry.BytesPerSector; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* XXX Does Windows support AIO on less than 512-byte alignment? */ | ||||
|     bs->bl.request_alignment = 512; | ||||
| } | ||||
|  | ||||
| static void raw_parse_flags(int flags, bool use_aio, int *access_flags, | ||||
| @@ -414,32 +410,32 @@ fail: | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static BlockAIOCB *raw_aio_preadv(BlockDriverState *bs, | ||||
|                                   uint64_t offset, uint64_t bytes, | ||||
|                                   QEMUIOVector *qiov, int flags, | ||||
|                                   BlockCompletionFunc *cb, void *opaque) | ||||
| static BlockAIOCB *raw_aio_readv(BlockDriverState *bs, | ||||
|                          int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | ||||
|                          BlockCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     if (s->aio) { | ||||
|         return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov, | ||||
|                                 cb, opaque, QEMU_AIO_READ); | ||||
|         return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov, | ||||
|                                 nb_sectors, cb, opaque, QEMU_AIO_READ); | ||||
|     } else { | ||||
|         return paio_submit(bs, s->hfile, offset, qiov, bytes, | ||||
|         return paio_submit(bs, s->hfile, sector_num << BDRV_SECTOR_BITS, qiov, | ||||
|                            nb_sectors << BDRV_SECTOR_BITS, | ||||
|                            cb, opaque, QEMU_AIO_READ); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static BlockAIOCB *raw_aio_pwritev(BlockDriverState *bs, | ||||
|                                    uint64_t offset, uint64_t bytes, | ||||
|                                    QEMUIOVector *qiov, int flags, | ||||
|                                    BlockCompletionFunc *cb, void *opaque) | ||||
| static BlockAIOCB *raw_aio_writev(BlockDriverState *bs, | ||||
|                           int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | ||||
|                           BlockCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     if (s->aio) { | ||||
|         return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov, | ||||
|                                 cb, opaque, QEMU_AIO_WRITE); | ||||
|         return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov, | ||||
|                                 nb_sectors, cb, opaque, QEMU_AIO_WRITE); | ||||
|     } else { | ||||
|         return paio_submit(bs, s->hfile, offset, qiov, bytes, | ||||
|         return paio_submit(bs, s->hfile, sector_num << BDRV_SECTOR_BITS, qiov, | ||||
|                            nb_sectors << BDRV_SECTOR_BITS, | ||||
|                            cb, opaque, QEMU_AIO_WRITE); | ||||
|     } | ||||
| } | ||||
| @@ -467,8 +463,8 @@ static void raw_close(BlockDriverState *bs) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                                         PreallocMode prealloc, Error **errp) | ||||
| static int raw_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                         PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     LONG low, high; | ||||
| @@ -636,11 +632,11 @@ BlockDriver bdrv_file = { | ||||
|     .bdrv_co_create_opts = raw_co_create_opts, | ||||
|     .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||||
|  | ||||
|     .bdrv_aio_preadv    = raw_aio_preadv, | ||||
|     .bdrv_aio_pwritev   = raw_aio_pwritev, | ||||
|     .bdrv_aio_readv     = raw_aio_readv, | ||||
|     .bdrv_aio_writev    = raw_aio_writev, | ||||
|     .bdrv_aio_flush     = raw_aio_flush, | ||||
|  | ||||
|     .bdrv_co_truncate   = raw_co_truncate, | ||||
|     .bdrv_truncate	= raw_truncate, | ||||
|     .bdrv_getlength	= raw_getlength, | ||||
|     .bdrv_get_allocated_file_size | ||||
|                         = raw_get_allocated_file_size, | ||||
| @@ -712,12 +708,6 @@ static void hdev_parse_filename(const char *filename, QDict *options, | ||||
|     bdrv_parse_filename_strip_prefix(filename, "host_device:", options); | ||||
| } | ||||
|  | ||||
| static void hdev_refresh_limits(BlockDriverState *bs, Error **errp) | ||||
| { | ||||
|     /* XXX Does Windows support AIO on less than 512-byte alignment? */ | ||||
|     bs->bl.request_alignment = 512; | ||||
| } | ||||
|  | ||||
| static int hdev_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                      Error **errp) | ||||
| { | ||||
| @@ -803,10 +793,9 @@ static BlockDriver bdrv_host_device = { | ||||
|     .bdrv_probe_device	= hdev_probe_device, | ||||
|     .bdrv_file_open	= hdev_open, | ||||
|     .bdrv_close		= raw_close, | ||||
|     .bdrv_refresh_limits = hdev_refresh_limits, | ||||
|  | ||||
|     .bdrv_aio_preadv    = raw_aio_preadv, | ||||
|     .bdrv_aio_pwritev   = raw_aio_pwritev, | ||||
|     .bdrv_aio_readv     = raw_aio_readv, | ||||
|     .bdrv_aio_writev    = raw_aio_writev, | ||||
|     .bdrv_aio_flush     = raw_aio_flush, | ||||
|  | ||||
|     .bdrv_detach_aio_context = raw_detach_aio_context, | ||||
|   | ||||
| @@ -11,7 +11,6 @@ | ||||
| #include "qemu/osdep.h" | ||||
| #include <glusterfs/api/glfs.h> | ||||
| #include "block/block_int.h" | ||||
| #include "block/qdict.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qapi/qmp/qdict.h" | ||||
| #include "qapi/qmp/qerror.h" | ||||
| @@ -20,10 +19,6 @@ | ||||
| #include "qemu/option.h" | ||||
| #include "qemu/cutils.h" | ||||
|  | ||||
| #ifdef CONFIG_GLUSTERFS_FTRUNCATE_HAS_STAT | ||||
| # define glfs_ftruncate(fd, offset) glfs_ftruncate(fd, offset, NULL, NULL) | ||||
| #endif | ||||
|  | ||||
| #define GLUSTER_OPT_FILENAME        "filename" | ||||
| #define GLUSTER_OPT_VOLUME          "volume" | ||||
| #define GLUSTER_OPT_PATH            "path" | ||||
| @@ -76,7 +71,7 @@ typedef struct ListElement { | ||||
|     GlfsPreopened saved; | ||||
| } ListElement; | ||||
|  | ||||
| static QLIST_HEAD(, ListElement) glfs_list; | ||||
| static QLIST_HEAD(glfs_list, ListElement) glfs_list; | ||||
|  | ||||
| static QemuOptsList qemu_gluster_create_opts = { | ||||
|     .name = "qemu-gluster-create-opts", | ||||
| @@ -172,12 +167,7 @@ static QemuOptsList runtime_unix_opts = { | ||||
|         { | ||||
|             .name = GLUSTER_OPT_SOCKET, | ||||
|             .type = QEMU_OPT_STRING, | ||||
|             .help = "socket file path (legacy)", | ||||
|         }, | ||||
|         { | ||||
|             .name = GLUSTER_OPT_PATH, | ||||
|             .type = QEMU_OPT_STRING, | ||||
|             .help = "socket file path (QAPI)", | ||||
|             .help = "socket file path)", | ||||
|         }, | ||||
|         { /* end of list */ } | ||||
|     }, | ||||
| @@ -625,18 +615,10 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, | ||||
|                 goto out; | ||||
|             } | ||||
|  | ||||
|             ptr = qemu_opt_get(opts, GLUSTER_OPT_PATH); | ||||
|             if (!ptr) { | ||||
|                 ptr = qemu_opt_get(opts, GLUSTER_OPT_SOCKET); | ||||
|             } else if (qemu_opt_get(opts, GLUSTER_OPT_SOCKET)) { | ||||
|                 error_setg(&local_err, | ||||
|                            "Conflicting parameters 'path' and 'socket'"); | ||||
|                 error_append_hint(&local_err, GERR_INDEX_HINT, i); | ||||
|                 goto out; | ||||
|             } | ||||
|             ptr = qemu_opt_get(opts, GLUSTER_OPT_SOCKET); | ||||
|             if (!ptr) { | ||||
|                 error_setg(&local_err, QERR_MISSING_PARAMETER, | ||||
|                            GLUSTER_OPT_PATH); | ||||
|                            GLUSTER_OPT_SOCKET); | ||||
|                 error_append_hint(&local_err, GERR_INDEX_HINT, i); | ||||
|                 goto out; | ||||
|             } | ||||
| @@ -655,7 +637,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, | ||||
|         } | ||||
|         gsconf = NULL; | ||||
|  | ||||
|         qobject_unref(backing_options); | ||||
|         QDECREF(backing_options); | ||||
|         backing_options = NULL; | ||||
|         g_free(str); | ||||
|         str = NULL; | ||||
| @@ -668,7 +650,7 @@ out: | ||||
|     qapi_free_SocketAddress(gsconf); | ||||
|     qemu_opts_del(opts); | ||||
|     g_free(str); | ||||
|     qobject_unref(backing_options); | ||||
|     QDECREF(backing_options); | ||||
|     errno = EINVAL; | ||||
|     return -errno; | ||||
| } | ||||
| @@ -683,7 +665,7 @@ static int qemu_gluster_parse(BlockdevOptionsGluster *gconf, | ||||
|     if (filename) { | ||||
|         ret = qemu_gluster_parse_uri(gconf, filename); | ||||
|         if (ret < 0) { | ||||
|             error_setg(errp, "invalid URI %s", filename); | ||||
|             error_setg(errp, "invalid URI"); | ||||
|             error_append_hint(errp, "Usage: file=gluster[+transport]://" | ||||
|                                     "[host[:port]]volume/path[?socket=...]" | ||||
|                                     "[,file.debug=N]" | ||||
| @@ -702,7 +684,7 @@ static int qemu_gluster_parse(BlockdevOptionsGluster *gconf, | ||||
|                              "file.server.0.host=1.2.3.4," | ||||
|                              "file.server.0.port=24007," | ||||
|                              "file.server.1.transport=unix," | ||||
|                              "file.server.1.path=/var/run/glusterd.socket ..." | ||||
|                              "file.server.1.socket=/var/run/glusterd.socket ..." | ||||
|                              "\n"); | ||||
|             return ret; | ||||
|         } | ||||
| @@ -729,11 +711,7 @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf, | ||||
| /* | ||||
|  * AIO callback routine called from GlusterFS thread. | ||||
|  */ | ||||
| static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, | ||||
| #ifdef CONFIG_GLUSTERFS_IOCB_HAS_STAT | ||||
|                                  struct glfs_stat *pre, struct glfs_stat *post, | ||||
| #endif | ||||
|                                  void *arg) | ||||
| static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg) | ||||
| { | ||||
|     GlusterAIOCB *acb = (GlusterAIOCB *)arg; | ||||
|  | ||||
| @@ -857,16 +835,8 @@ static int qemu_gluster_open(BlockDriverState *bs,  QDict *options, | ||||
|     qemu_gluster_parse_flags(bdrv_flags, &open_flags); | ||||
|  | ||||
|     s->fd = glfs_open(s->glfs, gconf->path, open_flags); | ||||
|     ret = s->fd ? 0 : -errno; | ||||
|  | ||||
|     if (ret == -EACCES || ret == -EROFS) { | ||||
|         /* Try to degrade to read-only, but if it doesn't work, still use the | ||||
|          * normal error message. */ | ||||
|         if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) { | ||||
|             open_flags = (open_flags & ~O_RDWR) | O_RDONLY; | ||||
|             s->fd = glfs_open(s->glfs, gconf->path, open_flags); | ||||
|             ret = s->fd ? 0 : -errno; | ||||
|         } | ||||
|     if (!s->fd) { | ||||
|         ret = -errno; | ||||
|     } | ||||
|  | ||||
|     s->supports_seek_data = qemu_gluster_test_seek(s->fd); | ||||
| @@ -1193,10 +1163,8 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs, | ||||
|     return acb.ret; | ||||
| } | ||||
|  | ||||
| static coroutine_fn int qemu_gluster_co_truncate(BlockDriverState *bs, | ||||
|                                                  int64_t offset, | ||||
|                                                  PreallocMode prealloc, | ||||
|                                                  Error **errp) | ||||
| static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                                  PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     BDRVGlusterState *s = bs->opaque; | ||||
|     return qemu_gluster_do_truncate(s->fd, offset, prealloc, errp); | ||||
| @@ -1213,10 +1181,8 @@ static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs, | ||||
| static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs, | ||||
|                                                int64_t sector_num, | ||||
|                                                int nb_sectors, | ||||
|                                                QEMUIOVector *qiov, | ||||
|                                                int flags) | ||||
|                                                QEMUIOVector *qiov) | ||||
| { | ||||
|     assert(!flags); | ||||
|     return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1); | ||||
| } | ||||
|  | ||||
| @@ -1342,7 +1308,7 @@ static int qemu_gluster_has_zero_init(BlockDriverState *bs) | ||||
|  * If @start is in a trailing hole or beyond EOF, return -ENXIO. | ||||
|  * If we can't find out, return a negative errno other than -ENXIO. | ||||
|  * | ||||
|  * (Shamefully copied from file-posix.c, only minuscule adaptions.) | ||||
|  * (Shamefully copied from file-posix.c, only miniscule adaptions.) | ||||
|  */ | ||||
| static int find_allocation(BlockDriverState *bs, off_t start, | ||||
|                            off_t *data, off_t *hole) | ||||
| @@ -1503,21 +1469,6 @@ static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs, | ||||
| } | ||||
|  | ||||
|  | ||||
| static const char *const gluster_strong_open_opts[] = { | ||||
|     GLUSTER_OPT_VOLUME, | ||||
|     GLUSTER_OPT_PATH, | ||||
|     GLUSTER_OPT_TYPE, | ||||
|     GLUSTER_OPT_SERVER_PATTERN, | ||||
|     GLUSTER_OPT_HOST, | ||||
|     GLUSTER_OPT_PORT, | ||||
|     GLUSTER_OPT_TO, | ||||
|     GLUSTER_OPT_IPV4, | ||||
|     GLUSTER_OPT_IPV6, | ||||
|     GLUSTER_OPT_SOCKET, | ||||
|  | ||||
|     NULL | ||||
| }; | ||||
|  | ||||
| static BlockDriver bdrv_gluster = { | ||||
|     .format_name                  = "gluster", | ||||
|     .protocol_name                = "gluster", | ||||
| @@ -1532,7 +1483,7 @@ static BlockDriver bdrv_gluster = { | ||||
|     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, | ||||
|     .bdrv_getlength               = qemu_gluster_getlength, | ||||
|     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, | ||||
|     .bdrv_co_truncate             = qemu_gluster_co_truncate, | ||||
|     .bdrv_truncate                = qemu_gluster_truncate, | ||||
|     .bdrv_co_readv                = qemu_gluster_co_readv, | ||||
|     .bdrv_co_writev               = qemu_gluster_co_writev, | ||||
|     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, | ||||
| @@ -1545,7 +1496,6 @@ static BlockDriver bdrv_gluster = { | ||||
| #endif | ||||
|     .bdrv_co_block_status         = qemu_gluster_co_block_status, | ||||
|     .create_opts                  = &qemu_gluster_create_opts, | ||||
|     .strong_runtime_opts          = gluster_strong_open_opts, | ||||
| }; | ||||
|  | ||||
| static BlockDriver bdrv_gluster_tcp = { | ||||
| @@ -1562,7 +1512,7 @@ static BlockDriver bdrv_gluster_tcp = { | ||||
|     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, | ||||
|     .bdrv_getlength               = qemu_gluster_getlength, | ||||
|     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, | ||||
|     .bdrv_co_truncate             = qemu_gluster_co_truncate, | ||||
|     .bdrv_truncate                = qemu_gluster_truncate, | ||||
|     .bdrv_co_readv                = qemu_gluster_co_readv, | ||||
|     .bdrv_co_writev               = qemu_gluster_co_writev, | ||||
|     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, | ||||
| @@ -1575,7 +1525,6 @@ static BlockDriver bdrv_gluster_tcp = { | ||||
| #endif | ||||
|     .bdrv_co_block_status         = qemu_gluster_co_block_status, | ||||
|     .create_opts                  = &qemu_gluster_create_opts, | ||||
|     .strong_runtime_opts          = gluster_strong_open_opts, | ||||
| }; | ||||
|  | ||||
| static BlockDriver bdrv_gluster_unix = { | ||||
| @@ -1592,7 +1541,7 @@ static BlockDriver bdrv_gluster_unix = { | ||||
|     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, | ||||
|     .bdrv_getlength               = qemu_gluster_getlength, | ||||
|     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, | ||||
|     .bdrv_co_truncate             = qemu_gluster_co_truncate, | ||||
|     .bdrv_truncate                = qemu_gluster_truncate, | ||||
|     .bdrv_co_readv                = qemu_gluster_co_readv, | ||||
|     .bdrv_co_writev               = qemu_gluster_co_writev, | ||||
|     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, | ||||
| @@ -1605,7 +1554,6 @@ static BlockDriver bdrv_gluster_unix = { | ||||
| #endif | ||||
|     .bdrv_co_block_status         = qemu_gluster_co_block_status, | ||||
|     .create_opts                  = &qemu_gluster_create_opts, | ||||
|     .strong_runtime_opts          = gluster_strong_open_opts, | ||||
| }; | ||||
|  | ||||
| /* rdma is deprecated (actually never supported for volfile fetch). | ||||
| @@ -1628,7 +1576,7 @@ static BlockDriver bdrv_gluster_rdma = { | ||||
|     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, | ||||
|     .bdrv_getlength               = qemu_gluster_getlength, | ||||
|     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, | ||||
|     .bdrv_co_truncate             = qemu_gluster_co_truncate, | ||||
|     .bdrv_truncate                = qemu_gluster_truncate, | ||||
|     .bdrv_co_readv                = qemu_gluster_co_readv, | ||||
|     .bdrv_co_writev               = qemu_gluster_co_writev, | ||||
|     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, | ||||
| @@ -1641,7 +1589,6 @@ static BlockDriver bdrv_gluster_rdma = { | ||||
| #endif | ||||
|     .bdrv_co_block_status         = qemu_gluster_co_block_status, | ||||
|     .create_opts                  = &qemu_gluster_create_opts, | ||||
|     .strong_runtime_opts          = gluster_strong_open_opts, | ||||
| }; | ||||
|  | ||||
| static void bdrv_gluster_init(void) | ||||
|   | ||||
							
								
								
									
										990
									
								
								block/io.c
									
									
									
									
									
								
							
							
						
						
									
										990
									
								
								block/io.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										450
									
								
								block/iscsi.c
									
									
									
									
									
								
							
							
						
						
									
										450
									
								
								block/iscsi.c
									
									
									
									
									
								
							| @@ -33,7 +33,6 @@ | ||||
| #include "qemu/bitops.h" | ||||
| #include "qemu/bitmap.h" | ||||
| #include "block/block_int.h" | ||||
| #include "block/qdict.h" | ||||
| #include "scsi/constants.h" | ||||
| #include "qemu/iov.h" | ||||
| #include "qemu/option.h" | ||||
| @@ -44,14 +43,11 @@ | ||||
| #include "qapi/qmp/qstring.h" | ||||
| #include "crypto/secret.h" | ||||
| #include "scsi/utils.h" | ||||
| #include "trace.h" | ||||
|  | ||||
| /* Conflict between scsi/utils.h and libiscsi! :( */ | ||||
| #define SCSI_XFER_NONE ISCSI_XFER_NONE | ||||
| #include <iscsi/iscsi.h> | ||||
| #define inline __attribute__((gnu_inline))  /* required for libiscsi v1.9.0 */ | ||||
| #include <iscsi/scsi-lowlevel.h> | ||||
| #undef inline | ||||
| #undef SCSI_XFER_NONE | ||||
| QEMU_BUILD_BUG_ON((int)SCSI_XFER_NONE != (int)ISCSI_XFER_NONE); | ||||
|  | ||||
| @@ -72,7 +68,6 @@ typedef struct IscsiLun { | ||||
|     QemuMutex mutex; | ||||
|     struct scsi_inquiry_logical_block_provisioning lbp; | ||||
|     struct scsi_inquiry_block_limits bl; | ||||
|     struct scsi_inquiry_device_designator *dd; | ||||
|     unsigned char *zeroblock; | ||||
|     /* The allocmap tracks which clusters (pages) on the iSCSI target are | ||||
|      * allocated and which are not. In case a target returns zeros for | ||||
| @@ -119,6 +114,7 @@ typedef struct IscsiAIOCB { | ||||
|     QEMUBH *bh; | ||||
|     IscsiLun *iscsilun; | ||||
|     struct scsi_task *task; | ||||
|     uint8_t *buf; | ||||
|     int status; | ||||
|     int64_t sector_num; | ||||
|     int nb_sectors; | ||||
| @@ -126,7 +122,6 @@ typedef struct IscsiAIOCB { | ||||
| #ifdef __linux__ | ||||
|     sg_io_hdr_t *ioh; | ||||
| #endif | ||||
|     bool cancelled; | ||||
| } IscsiAIOCB; | ||||
|  | ||||
| /* libiscsi uses time_t so its enough to process events every second */ | ||||
| @@ -145,8 +140,6 @@ static const unsigned iscsi_retry_times[] = {8, 32, 128, 512, 2048, 8192, 32768} | ||||
|  * unallocated. */ | ||||
| #define ISCSI_CHECKALLOC_THRES 64 | ||||
|  | ||||
| #ifdef __linux__ | ||||
|  | ||||
| static void | ||||
| iscsi_bh_cb(void *p) | ||||
| { | ||||
| @@ -154,6 +147,9 @@ iscsi_bh_cb(void *p) | ||||
|  | ||||
|     qemu_bh_delete(acb->bh); | ||||
|  | ||||
|     g_free(acb->buf); | ||||
|     acb->buf = NULL; | ||||
|  | ||||
|     acb->common.cb(acb->common.opaque, acb->status); | ||||
|  | ||||
|     if (acb->task != NULL) { | ||||
| @@ -174,8 +170,6 @@ iscsi_schedule_bh(IscsiAIOCB *acb) | ||||
|     qemu_bh_schedule(acb->bh); | ||||
| } | ||||
|  | ||||
| #endif | ||||
|  | ||||
| static void iscsi_co_generic_bh_cb(void *opaque) | ||||
| { | ||||
|     struct IscsiTask *iTask = opaque; | ||||
| @@ -294,22 +288,14 @@ static void iscsi_co_init_iscsitask(IscsiLun *iscsilun, struct IscsiTask *iTask) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #ifdef __linux__ | ||||
|  | ||||
| /* Called (via iscsi_service) with QemuMutex held. */ | ||||
| static void | ||||
| iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data, | ||||
|                     void *private_data) | ||||
| { | ||||
|     IscsiAIOCB *acb = private_data; | ||||
|  | ||||
|     /* If the command callback hasn't been called yet, drop the task */ | ||||
|     if (!acb->bh) { | ||||
|         /* Call iscsi_aio_ioctl_cb() with SCSI_STATUS_CANCELLED */ | ||||
|         iscsi_scsi_cancel_task(iscsi, acb->task); | ||||
|     } | ||||
|  | ||||
|     qemu_aio_unref(acb); /* acquired in iscsi_aio_cancel() */ | ||||
|     acb->status = -ECANCELED; | ||||
|     iscsi_schedule_bh(acb); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -318,25 +304,14 @@ iscsi_aio_cancel(BlockAIOCB *blockacb) | ||||
|     IscsiAIOCB *acb = (IscsiAIOCB *)blockacb; | ||||
|     IscsiLun *iscsilun = acb->iscsilun; | ||||
|  | ||||
|     qemu_mutex_lock(&iscsilun->mutex); | ||||
|  | ||||
|     /* If it was cancelled or completed already, our work is done here */ | ||||
|     if (acb->cancelled || acb->status != -EINPROGRESS) { | ||||
|         qemu_mutex_unlock(&iscsilun->mutex); | ||||
|     if (acb->status != -EINPROGRESS) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     acb->cancelled = true; | ||||
|  | ||||
|     qemu_aio_ref(acb); /* released in iscsi_abort_task_cb() */ | ||||
|  | ||||
|     /* send a task mgmt call to the target to cancel the task on the target */ | ||||
|     if (iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task, | ||||
|                                          iscsi_abort_task_cb, acb) < 0) { | ||||
|         qemu_aio_unref(acb); /* since iscsi_abort_task_cb() won't be called */ | ||||
|     } | ||||
|     iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task, | ||||
|                                      iscsi_abort_task_cb, acb); | ||||
|  | ||||
|     qemu_mutex_unlock(&iscsilun->mutex); | ||||
| } | ||||
|  | ||||
| static const AIOCBInfo iscsi_aiocb_info = { | ||||
| @@ -344,7 +319,6 @@ static const AIOCBInfo iscsi_aiocb_info = { | ||||
|     .cancel_async       = iscsi_aio_cancel, | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|  | ||||
| static void iscsi_process_read(void *arg); | ||||
| static void iscsi_process_write(void *arg); | ||||
| @@ -371,8 +345,6 @@ static void iscsi_timed_check_events(void *opaque) | ||||
| { | ||||
|     IscsiLun *iscsilun = opaque; | ||||
|  | ||||
|     qemu_mutex_lock(&iscsilun->mutex); | ||||
|  | ||||
|     /* check for timed out requests */ | ||||
|     iscsi_service(iscsilun->iscsi, 0); | ||||
|  | ||||
| @@ -385,8 +357,6 @@ static void iscsi_timed_check_events(void *opaque) | ||||
|      * to return to service once this situation changes. */ | ||||
|     iscsi_set_events(iscsilun); | ||||
|  | ||||
|     qemu_mutex_unlock(&iscsilun->mutex); | ||||
|  | ||||
|     timer_mod(iscsilun->event_timer, | ||||
|               qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL); | ||||
| } | ||||
| @@ -585,20 +555,9 @@ static inline bool iscsi_allocmap_is_valid(IscsiLun *iscsilun, | ||||
|                                offset / iscsilun->cluster_size) == size); | ||||
| } | ||||
|  | ||||
| static void coroutine_fn iscsi_co_wait_for_task(IscsiTask *iTask, | ||||
|                                                 IscsiLun *iscsilun) | ||||
| { | ||||
|     while (!iTask->complete) { | ||||
|         iscsi_set_events(iscsilun); | ||||
|         qemu_mutex_unlock(&iscsilun->mutex); | ||||
|         qemu_coroutine_yield(); | ||||
|         qemu_mutex_lock(&iscsilun->mutex); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int coroutine_fn | ||||
| iscsi_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, | ||||
|                 QEMUIOVector *iov, int flags) | ||||
| iscsi_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors, | ||||
|                       QEMUIOVector *iov, int flags) | ||||
| { | ||||
|     IscsiLun *iscsilun = bs->opaque; | ||||
|     struct IscsiTask iTask; | ||||
| @@ -657,7 +616,12 @@ retry: | ||||
|     scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov, | ||||
|                           iov->niov); | ||||
| #endif | ||||
|     iscsi_co_wait_for_task(&iTask, iscsilun); | ||||
|     while (!iTask.complete) { | ||||
|         iscsi_set_events(iscsilun); | ||||
|         qemu_mutex_unlock(&iscsilun->mutex); | ||||
|         qemu_coroutine_yield(); | ||||
|         qemu_mutex_lock(&iscsilun->mutex); | ||||
|     } | ||||
|  | ||||
|     if (iTask.task != NULL) { | ||||
|         scsi_free_scsi_task(iTask.task); | ||||
| @@ -728,7 +692,13 @@ retry: | ||||
|         ret = -ENOMEM; | ||||
|         goto out_unlock; | ||||
|     } | ||||
|     iscsi_co_wait_for_task(&iTask, iscsilun); | ||||
|  | ||||
|     while (!iTask.complete) { | ||||
|         iscsi_set_events(iscsilun); | ||||
|         qemu_mutex_unlock(&iscsilun->mutex); | ||||
|         qemu_coroutine_yield(); | ||||
|         qemu_mutex_lock(&iscsilun->mutex); | ||||
|     } | ||||
|  | ||||
|     if (iTask.do_retry) { | ||||
|         if (iTask.task != NULL) { | ||||
| @@ -762,7 +732,7 @@ retry: | ||||
|         goto out_unlock; | ||||
|     } | ||||
|  | ||||
|     *pnum = (int64_t) lbasd->num_blocks * iscsilun->block_size; | ||||
|     *pnum = lbasd->num_blocks * iscsilun->block_size; | ||||
|  | ||||
|     if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED || | ||||
|         lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) { | ||||
| @@ -892,8 +862,13 @@ retry: | ||||
| #if LIBISCSI_API_VERSION < (20160603) | ||||
|     scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov); | ||||
| #endif | ||||
|     while (!iTask.complete) { | ||||
|         iscsi_set_events(iscsilun); | ||||
|         qemu_mutex_unlock(&iscsilun->mutex); | ||||
|         qemu_coroutine_yield(); | ||||
|         qemu_mutex_lock(&iscsilun->mutex); | ||||
|     } | ||||
|  | ||||
|     iscsi_co_wait_for_task(&iTask, iscsilun); | ||||
|     if (iTask.task != NULL) { | ||||
|         scsi_free_scsi_task(iTask.task); | ||||
|         iTask.task = NULL; | ||||
| @@ -930,7 +905,12 @@ retry: | ||||
|         return -ENOMEM; | ||||
|     } | ||||
|  | ||||
|     iscsi_co_wait_for_task(&iTask, iscsilun); | ||||
|     while (!iTask.complete) { | ||||
|         iscsi_set_events(iscsilun); | ||||
|         qemu_mutex_unlock(&iscsilun->mutex); | ||||
|         qemu_coroutine_yield(); | ||||
|         qemu_mutex_lock(&iscsilun->mutex); | ||||
|     } | ||||
|  | ||||
|     if (iTask.task != NULL) { | ||||
|         scsi_free_scsi_task(iTask.task); | ||||
| @@ -960,13 +940,8 @@ iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status, | ||||
| { | ||||
|     IscsiAIOCB *acb = opaque; | ||||
|  | ||||
|     if (status == SCSI_STATUS_CANCELLED) { | ||||
|         if (!acb->bh) { | ||||
|             acb->status = -ECANCELED; | ||||
|             iscsi_schedule_bh(acb); | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
|     g_free(acb->buf); | ||||
|     acb->buf = NULL; | ||||
|  | ||||
|     acb->status = 0; | ||||
|     if (status < 0) { | ||||
| @@ -1042,8 +1017,8 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, | ||||
|     acb->iscsilun = iscsilun; | ||||
|     acb->bh          = NULL; | ||||
|     acb->status      = -EINPROGRESS; | ||||
|     acb->buf         = NULL; | ||||
|     acb->ioh         = buf; | ||||
|     acb->cancelled   = false; | ||||
|  | ||||
|     if (req != SG_IO) { | ||||
|         iscsi_ioctl_handle_emulated(acb, req, buf); | ||||
| @@ -1167,7 +1142,12 @@ retry: | ||||
|         goto out_unlock; | ||||
|     } | ||||
|  | ||||
|     iscsi_co_wait_for_task(&iTask, iscsilun); | ||||
|     while (!iTask.complete) { | ||||
|         iscsi_set_events(iscsilun); | ||||
|         qemu_mutex_unlock(&iscsilun->mutex); | ||||
|         qemu_coroutine_yield(); | ||||
|         qemu_mutex_lock(&iscsilun->mutex); | ||||
|     } | ||||
|  | ||||
|     if (iTask.task != NULL) { | ||||
|         scsi_free_scsi_task(iTask.task); | ||||
| @@ -1263,7 +1243,12 @@ retry: | ||||
|         return -ENOMEM; | ||||
|     } | ||||
|  | ||||
|     iscsi_co_wait_for_task(&iTask, iscsilun); | ||||
|     while (!iTask.complete) { | ||||
|         iscsi_set_events(iscsilun); | ||||
|         qemu_mutex_unlock(&iscsilun->mutex); | ||||
|         qemu_coroutine_yield(); | ||||
|         qemu_mutex_lock(&iscsilun->mutex); | ||||
|     } | ||||
|  | ||||
|     if (iTask.status == SCSI_STATUS_CHECK_CONDITION && | ||||
|         iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && | ||||
| @@ -1747,34 +1732,14 @@ static QemuOptsList runtime_opts = { | ||||
|             .name = "timeout", | ||||
|             .type = QEMU_OPT_NUMBER, | ||||
|         }, | ||||
|         { | ||||
|             .name = "filename", | ||||
|             .type = QEMU_OPT_STRING, | ||||
|         }, | ||||
|         { /* end of list */ } | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| static void iscsi_save_designator(IscsiLun *lun, | ||||
|                                   struct scsi_inquiry_device_identification *inq_di) | ||||
| { | ||||
|     struct scsi_inquiry_device_designator *desig, *copy = NULL; | ||||
|  | ||||
|     for (desig = inq_di->designators; desig; desig = desig->next) { | ||||
|         if (desig->association || | ||||
|             desig->designator_type > SCSI_DESIGNATOR_TYPE_NAA) { | ||||
|             continue; | ||||
|         } | ||||
|         /* NAA works better than T10 vendor ID based designator. */ | ||||
|         if (!copy || copy->designator_type < desig->designator_type) { | ||||
|             copy = desig; | ||||
|         } | ||||
|     } | ||||
|     if (copy) { | ||||
|         lun->dd = g_new(struct scsi_inquiry_device_designator, 1); | ||||
|         *lun->dd = *copy; | ||||
|         lun->dd->next = NULL; | ||||
|         lun->dd->designator = g_malloc(copy->designator_length); | ||||
|         memcpy(lun->dd->designator, copy->designator, copy->designator_length); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                       Error **errp) | ||||
| { | ||||
| @@ -1786,12 +1751,27 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     char *initiator_name = NULL; | ||||
|     QemuOpts *opts; | ||||
|     Error *local_err = NULL; | ||||
|     const char *transport_name, *portal, *target; | ||||
|     const char *transport_name, *portal, *target, *filename; | ||||
| #if LIBISCSI_API_VERSION >= (20160603) | ||||
|     enum iscsi_transport_type transport; | ||||
| #endif | ||||
|     int i, ret = 0, timeout = 0, lun; | ||||
|  | ||||
|     /* If we are given a filename, parse the filename, with precedence given to | ||||
|      * filename encoded options */ | ||||
|     filename = qdict_get_try_str(options, "filename"); | ||||
|     if (filename) { | ||||
|         warn_report("'filename' option specified. " | ||||
|                     "This is an unsupported option, and may be deprecated " | ||||
|                     "in the future"); | ||||
|         iscsi_parse_filename(filename, options, &local_err); | ||||
|         if (local_err) { | ||||
|             ret = -EINVAL; | ||||
|             error_propagate(errp, local_err); | ||||
|             goto exit; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); | ||||
|     qemu_opts_absorb_qdict(opts, options, &local_err); | ||||
|     if (local_err) { | ||||
| @@ -1876,7 +1856,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     iscsi_set_timeout(iscsi, timeout); | ||||
| #else | ||||
|     if (timeout) { | ||||
|         warn_report("iSCSI: ignoring timeout value for libiscsi <1.15.0"); | ||||
|         error_report("iSCSI: ignoring timeout value for libiscsi <1.15.0"); | ||||
|     } | ||||
| #endif | ||||
|  | ||||
| @@ -1910,11 +1890,9 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     /* Check the write protect flag of the LUN if we want to write */ | ||||
|     if (iscsilun->type == TYPE_DISK && (flags & BDRV_O_RDWR) && | ||||
|         iscsilun->write_protected) { | ||||
|         ret = bdrv_apply_auto_read_only(bs, "LUN is write protected", errp); | ||||
|         if (ret < 0) { | ||||
|             goto out; | ||||
|         } | ||||
|         flags &= ~BDRV_O_RDWR; | ||||
|         error_setg(errp, "Cannot open a write protected LUN as read-write"); | ||||
|         ret = -EACCES; | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     iscsi_readcapacity_sync(iscsilun, &local_err); | ||||
| @@ -1944,7 +1922,6 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         struct scsi_task *inq_task; | ||||
|         struct scsi_inquiry_logical_block_provisioning *inq_lbp; | ||||
|         struct scsi_inquiry_block_limits *inq_bl; | ||||
|         struct scsi_inquiry_device_identification *inq_di; | ||||
|         switch (inq_vpd->pages[i]) { | ||||
|         case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING: | ||||
|             inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, | ||||
| @@ -1970,17 +1947,6 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|                    sizeof(struct scsi_inquiry_block_limits)); | ||||
|             scsi_free_scsi_task(inq_task); | ||||
|             break; | ||||
|         case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION: | ||||
|             inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, | ||||
|                                     SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION, | ||||
|                                     (void **) &inq_di, errp); | ||||
|             if (inq_task == NULL) { | ||||
|                 ret = -EINVAL; | ||||
|                 goto out; | ||||
|             } | ||||
|             iscsi_save_designator(iscsilun, inq_di); | ||||
|             scsi_free_scsi_task(inq_task); | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
| @@ -2023,7 +1989,7 @@ out: | ||||
|         } | ||||
|         memset(iscsilun, 0, sizeof(IscsiLun)); | ||||
|     } | ||||
|  | ||||
| exit: | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| @@ -2037,10 +2003,6 @@ static void iscsi_close(BlockDriverState *bs) | ||||
|         iscsi_logout_sync(iscsi); | ||||
|     } | ||||
|     iscsi_destroy_context(iscsi); | ||||
|     if (iscsilun->dd) { | ||||
|         g_free(iscsilun->dd->designator); | ||||
|         g_free(iscsilun->dd); | ||||
|     } | ||||
|     g_free(iscsilun->zeroblock); | ||||
|     iscsi_allocmap_free(iscsilun); | ||||
|     qemu_mutex_destroy(&iscsilun->mutex); | ||||
| @@ -2120,8 +2082,8 @@ static void iscsi_reopen_commit(BDRVReopenState *reopen_state) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                                           PreallocMode prealloc, Error **errp) | ||||
| static int iscsi_truncate(BlockDriverState *bs, int64_t offset, | ||||
|                           PreallocMode prealloc, Error **errp) | ||||
| { | ||||
|     IscsiLun *iscsilun = bs->opaque; | ||||
|     Error *local_err = NULL; | ||||
| @@ -2181,7 +2143,7 @@ static int coroutine_fn iscsi_co_create_opts(const char *filename, QemuOpts *opt | ||||
|     } else { | ||||
|         ret = iscsi_open(bs, bs_options, 0, NULL); | ||||
|     } | ||||
|     qobject_unref(bs_options); | ||||
|     QDECREF(bs_options); | ||||
|  | ||||
|     if (ret != 0) { | ||||
|         goto out; | ||||
| @@ -2222,226 +2184,6 @@ static void coroutine_fn iscsi_co_invalidate_cache(BlockDriverState *bs, | ||||
|     iscsi_allocmap_invalidate(iscsilun); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn iscsi_co_copy_range_from(BlockDriverState *bs, | ||||
|                                                  BdrvChild *src, | ||||
|                                                  uint64_t src_offset, | ||||
|                                                  BdrvChild *dst, | ||||
|                                                  uint64_t dst_offset, | ||||
|                                                  uint64_t bytes, | ||||
|                                                  BdrvRequestFlags read_flags, | ||||
|                                                  BdrvRequestFlags write_flags) | ||||
| { | ||||
|     return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, | ||||
|                                  read_flags, write_flags); | ||||
| } | ||||
|  | ||||
| static struct scsi_task *iscsi_xcopy_task(int param_len) | ||||
| { | ||||
|     struct scsi_task *task; | ||||
|  | ||||
|     task = g_new0(struct scsi_task, 1); | ||||
|  | ||||
|     task->cdb[0]     = EXTENDED_COPY; | ||||
|     task->cdb[10]    = (param_len >> 24) & 0xFF; | ||||
|     task->cdb[11]    = (param_len >> 16) & 0xFF; | ||||
|     task->cdb[12]    = (param_len >> 8) & 0xFF; | ||||
|     task->cdb[13]    = param_len & 0xFF; | ||||
|     task->cdb_size   = 16; | ||||
|     task->xfer_dir   = SCSI_XFER_WRITE; | ||||
|     task->expxferlen = param_len; | ||||
|  | ||||
|     return task; | ||||
| } | ||||
|  | ||||
| static void iscsi_populate_target_desc(unsigned char *desc, IscsiLun *lun) | ||||
| { | ||||
|     struct scsi_inquiry_device_designator *dd = lun->dd; | ||||
|  | ||||
|     memset(desc, 0, 32); | ||||
|     desc[0] = 0xE4; /* IDENT_DESCR_TGT_DESCR */ | ||||
|     desc[4] = dd->code_set; | ||||
|     desc[5] = (dd->designator_type & 0xF) | ||||
|         | ((dd->association & 3) << 4); | ||||
|     desc[7] = dd->designator_length; | ||||
|     memcpy(desc + 8, dd->designator, MIN(dd->designator_length, 20)); | ||||
|  | ||||
|     desc[28] = 0; | ||||
|     desc[29] = (lun->block_size >> 16) & 0xFF; | ||||
|     desc[30] = (lun->block_size >> 8) & 0xFF; | ||||
|     desc[31] = lun->block_size & 0xFF; | ||||
| } | ||||
|  | ||||
| static void iscsi_xcopy_desc_hdr(uint8_t *hdr, int dc, int cat, int src_index, | ||||
|                                  int dst_index) | ||||
| { | ||||
|     hdr[0] = 0x02; /* BLK_TO_BLK_SEG_DESCR */ | ||||
|     hdr[1] = ((dc << 1) | cat) & 0xFF; | ||||
|     hdr[2] = (XCOPY_BLK2BLK_SEG_DESC_SIZE >> 8) & 0xFF; | ||||
|     /* don't account for the first 4 bytes in descriptor header*/ | ||||
|     hdr[3] = (XCOPY_BLK2BLK_SEG_DESC_SIZE - 4 /* SEG_DESC_SRC_INDEX_OFFSET */) & 0xFF; | ||||
|     hdr[4] = (src_index >> 8) & 0xFF; | ||||
|     hdr[5] = src_index & 0xFF; | ||||
|     hdr[6] = (dst_index >> 8) & 0xFF; | ||||
|     hdr[7] = dst_index & 0xFF; | ||||
| } | ||||
|  | ||||
| static void iscsi_xcopy_populate_desc(uint8_t *desc, int dc, int cat, | ||||
|                                       int src_index, int dst_index, int num_blks, | ||||
|                                       uint64_t src_lba, uint64_t dst_lba) | ||||
| { | ||||
|     iscsi_xcopy_desc_hdr(desc, dc, cat, src_index, dst_index); | ||||
|  | ||||
|     /* The caller should verify the request size */ | ||||
|     assert(num_blks < 65536); | ||||
|     desc[10] = (num_blks >> 8) & 0xFF; | ||||
|     desc[11] = num_blks & 0xFF; | ||||
|     desc[12] = (src_lba >> 56) & 0xFF; | ||||
|     desc[13] = (src_lba >> 48) & 0xFF; | ||||
|     desc[14] = (src_lba >> 40) & 0xFF; | ||||
|     desc[15] = (src_lba >> 32) & 0xFF; | ||||
|     desc[16] = (src_lba >> 24) & 0xFF; | ||||
|     desc[17] = (src_lba >> 16) & 0xFF; | ||||
|     desc[18] = (src_lba >> 8) & 0xFF; | ||||
|     desc[19] = src_lba & 0xFF; | ||||
|     desc[20] = (dst_lba >> 56) & 0xFF; | ||||
|     desc[21] = (dst_lba >> 48) & 0xFF; | ||||
|     desc[22] = (dst_lba >> 40) & 0xFF; | ||||
|     desc[23] = (dst_lba >> 32) & 0xFF; | ||||
|     desc[24] = (dst_lba >> 24) & 0xFF; | ||||
|     desc[25] = (dst_lba >> 16) & 0xFF; | ||||
|     desc[26] = (dst_lba >> 8) & 0xFF; | ||||
|     desc[27] = dst_lba & 0xFF; | ||||
| } | ||||
|  | ||||
| static void iscsi_xcopy_populate_header(unsigned char *buf, int list_id, int str, | ||||
|                                         int list_id_usage, int prio, | ||||
|                                         int tgt_desc_len, | ||||
|                                         int seg_desc_len, int inline_data_len) | ||||
| { | ||||
|     buf[0] = list_id; | ||||
|     buf[1] = ((str & 1) << 5) | ((list_id_usage & 3) << 3) | (prio & 7); | ||||
|     buf[2] = (tgt_desc_len >> 8) & 0xFF; | ||||
|     buf[3] = tgt_desc_len & 0xFF; | ||||
|     buf[8] = (seg_desc_len >> 24) & 0xFF; | ||||
|     buf[9] = (seg_desc_len >> 16) & 0xFF; | ||||
|     buf[10] = (seg_desc_len >> 8) & 0xFF; | ||||
|     buf[11] = seg_desc_len & 0xFF; | ||||
|     buf[12] = (inline_data_len >> 24) & 0xFF; | ||||
|     buf[13] = (inline_data_len >> 16) & 0xFF; | ||||
|     buf[14] = (inline_data_len >> 8) & 0xFF; | ||||
|     buf[15] = inline_data_len & 0xFF; | ||||
| } | ||||
|  | ||||
| static void iscsi_xcopy_data(struct iscsi_data *data, | ||||
|                              IscsiLun *src, int64_t src_lba, | ||||
|                              IscsiLun *dst, int64_t dst_lba, | ||||
|                              uint16_t num_blocks) | ||||
| { | ||||
|     uint8_t *buf; | ||||
|     const int src_offset = XCOPY_DESC_OFFSET; | ||||
|     const int dst_offset = XCOPY_DESC_OFFSET + IDENT_DESCR_TGT_DESCR_SIZE; | ||||
|     const int seg_offset = dst_offset + IDENT_DESCR_TGT_DESCR_SIZE; | ||||
|  | ||||
|     data->size = XCOPY_DESC_OFFSET + | ||||
|                  IDENT_DESCR_TGT_DESCR_SIZE * 2 + | ||||
|                  XCOPY_BLK2BLK_SEG_DESC_SIZE; | ||||
|     data->data = g_malloc0(data->size); | ||||
|     buf = data->data; | ||||
|  | ||||
|     /* Initialise the parameter list header */ | ||||
|     iscsi_xcopy_populate_header(buf, 1, 0, 2 /* LIST_ID_USAGE_DISCARD */, | ||||
|                                 0, 2 * IDENT_DESCR_TGT_DESCR_SIZE, | ||||
|                                 XCOPY_BLK2BLK_SEG_DESC_SIZE, | ||||
|                                 0); | ||||
|  | ||||
|     /* Initialise CSCD list with one src + one dst descriptor */ | ||||
|     iscsi_populate_target_desc(&buf[src_offset], src); | ||||
|     iscsi_populate_target_desc(&buf[dst_offset], dst); | ||||
|  | ||||
|     /* Initialise one segment descriptor */ | ||||
|     iscsi_xcopy_populate_desc(&buf[seg_offset], 0, 0, 0, 1, num_blocks, | ||||
|                               src_lba, dst_lba); | ||||
| } | ||||
|  | ||||
| static int coroutine_fn iscsi_co_copy_range_to(BlockDriverState *bs, | ||||
|                                                BdrvChild *src, | ||||
|                                                uint64_t src_offset, | ||||
|                                                BdrvChild *dst, | ||||
|                                                uint64_t dst_offset, | ||||
|                                                uint64_t bytes, | ||||
|                                                BdrvRequestFlags read_flags, | ||||
|                                                BdrvRequestFlags write_flags) | ||||
| { | ||||
|     IscsiLun *dst_lun = dst->bs->opaque; | ||||
|     IscsiLun *src_lun; | ||||
|     struct IscsiTask iscsi_task; | ||||
|     struct iscsi_data data; | ||||
|     int r = 0; | ||||
|     int block_size; | ||||
|  | ||||
|     if (src->bs->drv->bdrv_co_copy_range_to != iscsi_co_copy_range_to) { | ||||
|         return -ENOTSUP; | ||||
|     } | ||||
|     src_lun = src->bs->opaque; | ||||
|  | ||||
|     if (!src_lun->dd || !dst_lun->dd) { | ||||
|         return -ENOTSUP; | ||||
|     } | ||||
|     if (!is_byte_request_lun_aligned(dst_offset, bytes, dst_lun)) { | ||||
|         return -ENOTSUP; | ||||
|     } | ||||
|     if (!is_byte_request_lun_aligned(src_offset, bytes, src_lun)) { | ||||
|         return -ENOTSUP; | ||||
|     } | ||||
|     if (dst_lun->block_size != src_lun->block_size || | ||||
|         !dst_lun->block_size) { | ||||
|         return -ENOTSUP; | ||||
|     } | ||||
|  | ||||
|     block_size = dst_lun->block_size; | ||||
|     if (bytes / block_size > 65535) { | ||||
|         return -ENOTSUP; | ||||
|     } | ||||
|  | ||||
|     iscsi_xcopy_data(&data, | ||||
|                      src_lun, src_offset / block_size, | ||||
|                      dst_lun, dst_offset / block_size, | ||||
|                      bytes / block_size); | ||||
|  | ||||
|     iscsi_co_init_iscsitask(dst_lun, &iscsi_task); | ||||
|  | ||||
|     qemu_mutex_lock(&dst_lun->mutex); | ||||
|     iscsi_task.task = iscsi_xcopy_task(data.size); | ||||
| retry: | ||||
|     if (iscsi_scsi_command_async(dst_lun->iscsi, dst_lun->lun, | ||||
|                                  iscsi_task.task, iscsi_co_generic_cb, | ||||
|                                  &data, | ||||
|                                  &iscsi_task) != 0) { | ||||
|         r = -EIO; | ||||
|         goto out_unlock; | ||||
|     } | ||||
|  | ||||
|     iscsi_co_wait_for_task(&iscsi_task, dst_lun); | ||||
|  | ||||
|     if (iscsi_task.do_retry) { | ||||
|         iscsi_task.complete = 0; | ||||
|         goto retry; | ||||
|     } | ||||
|  | ||||
|     if (iscsi_task.status != SCSI_STATUS_GOOD) { | ||||
|         r = iscsi_task.err_code; | ||||
|         goto out_unlock; | ||||
|     } | ||||
|  | ||||
| out_unlock: | ||||
|  | ||||
|     trace_iscsi_xcopy(src_lun, src_offset, dst_lun, dst_offset, bytes, r); | ||||
|     g_free(iscsi_task.task); | ||||
|     qemu_mutex_unlock(&dst_lun->mutex); | ||||
|     g_free(iscsi_task.err_str); | ||||
|     return r; | ||||
| } | ||||
|  | ||||
| static QemuOptsList iscsi_create_opts = { | ||||
|     .name = "iscsi-create-opts", | ||||
|     .head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head), | ||||
| @@ -2455,20 +2197,6 @@ static QemuOptsList iscsi_create_opts = { | ||||
|     } | ||||
| }; | ||||
|  | ||||
| static const char *const iscsi_strong_runtime_opts[] = { | ||||
|     "transport", | ||||
|     "portal", | ||||
|     "target", | ||||
|     "user", | ||||
|     "password", | ||||
|     "password-secret", | ||||
|     "lun", | ||||
|     "initiator-name", | ||||
|     "header-digest", | ||||
|  | ||||
|     NULL | ||||
| }; | ||||
|  | ||||
| static BlockDriver bdrv_iscsi = { | ||||
|     .format_name     = "iscsi", | ||||
|     .protocol_name   = "iscsi", | ||||
| @@ -2485,16 +2213,14 @@ static BlockDriver bdrv_iscsi = { | ||||
|  | ||||
|     .bdrv_getlength  = iscsi_getlength, | ||||
|     .bdrv_get_info   = iscsi_get_info, | ||||
|     .bdrv_co_truncate    = iscsi_co_truncate, | ||||
|     .bdrv_truncate   = iscsi_truncate, | ||||
|     .bdrv_refresh_limits = iscsi_refresh_limits, | ||||
|  | ||||
|     .bdrv_co_block_status  = iscsi_co_block_status, | ||||
|     .bdrv_co_pdiscard      = iscsi_co_pdiscard, | ||||
|     .bdrv_co_copy_range_from = iscsi_co_copy_range_from, | ||||
|     .bdrv_co_copy_range_to  = iscsi_co_copy_range_to, | ||||
|     .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, | ||||
|     .bdrv_co_readv         = iscsi_co_readv, | ||||
|     .bdrv_co_writev        = iscsi_co_writev, | ||||
|     .bdrv_co_writev_flags  = iscsi_co_writev_flags, | ||||
|     .bdrv_co_flush_to_disk = iscsi_co_flush, | ||||
|  | ||||
| #ifdef __linux__ | ||||
| @@ -2503,8 +2229,6 @@ static BlockDriver bdrv_iscsi = { | ||||
|  | ||||
|     .bdrv_detach_aio_context = iscsi_detach_aio_context, | ||||
|     .bdrv_attach_aio_context = iscsi_attach_aio_context, | ||||
|  | ||||
|     .strong_runtime_opts = iscsi_strong_runtime_opts, | ||||
| }; | ||||
|  | ||||
| #if LIBISCSI_API_VERSION >= (20160603) | ||||
| @@ -2520,20 +2244,18 @@ static BlockDriver bdrv_iser = { | ||||
|     .create_opts            = &iscsi_create_opts, | ||||
|     .bdrv_reopen_prepare    = iscsi_reopen_prepare, | ||||
|     .bdrv_reopen_commit     = iscsi_reopen_commit, | ||||
|     .bdrv_co_invalidate_cache  = iscsi_co_invalidate_cache, | ||||
|     .bdrv_invalidate_cache  = iscsi_invalidate_cache, | ||||
|  | ||||
|     .bdrv_getlength  = iscsi_getlength, | ||||
|     .bdrv_get_info   = iscsi_get_info, | ||||
|     .bdrv_co_truncate    = iscsi_co_truncate, | ||||
|     .bdrv_truncate   = iscsi_truncate, | ||||
|     .bdrv_refresh_limits = iscsi_refresh_limits, | ||||
|  | ||||
|     .bdrv_co_block_status  = iscsi_co_block_status, | ||||
|     .bdrv_co_pdiscard      = iscsi_co_pdiscard, | ||||
|     .bdrv_co_copy_range_from = iscsi_co_copy_range_from, | ||||
|     .bdrv_co_copy_range_to  = iscsi_co_copy_range_to, | ||||
|     .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, | ||||
|     .bdrv_co_readv         = iscsi_co_readv, | ||||
|     .bdrv_co_writev        = iscsi_co_writev, | ||||
|     .bdrv_co_writev_flags  = iscsi_co_writev_flags, | ||||
|     .bdrv_co_flush_to_disk = iscsi_co_flush, | ||||
|  | ||||
| #ifdef __linux__ | ||||
| @@ -2542,8 +2264,6 @@ static BlockDriver bdrv_iser = { | ||||
|  | ||||
|     .bdrv_detach_aio_context = iscsi_detach_aio_context, | ||||
|     .bdrv_attach_aio_context = iscsi_attach_aio_context, | ||||
|  | ||||
|     .strong_runtime_opts = iscsi_strong_runtime_opts, | ||||
| }; | ||||
| #endif | ||||
|  | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user