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 | # http://editorconfig.org | ||||||
| # for maintaining consistent coding styles between different editors |  | ||||||
| # and IDEs. Most popular editors support this either natively or via |  | ||||||
| # plugin. |  | ||||||
| # |  | ||||||
| # Check https://editorconfig.org for details. |  | ||||||
|  |  | ||||||
| root = true | root = true | ||||||
|  |  | ||||||
| [*] | [*] | ||||||
| @@ -12,23 +6,10 @@ end_of_line = lf | |||||||
| insert_final_newline = true | insert_final_newline = true | ||||||
| charset = utf-8 | charset = utf-8 | ||||||
|  |  | ||||||
| [*.mak] |  | ||||||
| indent_style = tab |  | ||||||
| indent_size = 8 |  | ||||||
| file_type_emacs = makefile |  | ||||||
|  |  | ||||||
| [Makefile*] | [Makefile*] | ||||||
| indent_style = tab | indent_style = tab | ||||||
| indent_size = 8 | indent_size = 8 | ||||||
| file_type_emacs = makefile |  | ||||||
|  |  | ||||||
| [*.{c,h}] | [*.{c,h}] | ||||||
| indent_style = space | indent_style = space | ||||||
| indent_size = 4 | indent_size = 4 | ||||||
|  |  | ||||||
| [*.{vert,frag}] |  | ||||||
| file_type_emacs = glsl |  | ||||||
|  |  | ||||||
| [*.json] |  | ||||||
| indent_style = space |  | ||||||
| file_type_emacs = python |  | ||||||
|   | |||||||
							
								
								
									
										79
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										79
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1,3 @@ | |||||||
| /.doctrees |  | ||||||
| /config-devices.* | /config-devices.* | ||||||
| /config-all-devices.* | /config-all-devices.* | ||||||
| /config-all-disas.* | /config-all-disas.* | ||||||
| @@ -6,7 +5,6 @@ | |||||||
| /config-target.* | /config-target.* | ||||||
| /config.status | /config.status | ||||||
| /config-temp | /config-temp | ||||||
| /elf2dmp |  | ||||||
| /trace-events-all | /trace-events-all | ||||||
| /trace/generated-events.h | /trace/generated-events.h | ||||||
| /trace/generated-events.c | /trace/generated-events.c | ||||||
| @@ -32,21 +30,79 @@ | |||||||
| /qapi-gen-timestamp | /qapi-gen-timestamp | ||||||
| /qapi/qapi-builtin-types.[ch] | /qapi/qapi-builtin-types.[ch] | ||||||
| /qapi/qapi-builtin-visit.[ch] | /qapi/qapi-builtin-visit.[ch] | ||||||
| /qapi/qapi-commands-*.[ch] | /qapi/qapi-commands-block-core.[ch] | ||||||
|  | /qapi/qapi-commands-block.[ch] | ||||||
|  | /qapi/qapi-commands-char.[ch] | ||||||
|  | /qapi/qapi-commands-common.[ch] | ||||||
|  | /qapi/qapi-commands-crypto.[ch] | ||||||
|  | /qapi/qapi-commands-introspect.[ch] | ||||||
|  | /qapi/qapi-commands-migration.[ch] | ||||||
|  | /qapi/qapi-commands-misc.[ch] | ||||||
|  | /qapi/qapi-commands-net.[ch] | ||||||
|  | /qapi/qapi-commands-rocker.[ch] | ||||||
|  | /qapi/qapi-commands-run-state.[ch] | ||||||
|  | /qapi/qapi-commands-sockets.[ch] | ||||||
|  | /qapi/qapi-commands-tpm.[ch] | ||||||
|  | /qapi/qapi-commands-trace.[ch] | ||||||
|  | /qapi/qapi-commands-transaction.[ch] | ||||||
|  | /qapi/qapi-commands-ui.[ch] | ||||||
| /qapi/qapi-commands.[ch] | /qapi/qapi-commands.[ch] | ||||||
| /qapi/qapi-emit-events.[ch] | /qapi/qapi-events-block-core.[ch] | ||||||
| /qapi/qapi-events-*.[ch] | /qapi/qapi-events-block.[ch] | ||||||
|  | /qapi/qapi-events-char.[ch] | ||||||
|  | /qapi/qapi-events-common.[ch] | ||||||
|  | /qapi/qapi-events-crypto.[ch] | ||||||
|  | /qapi/qapi-events-introspect.[ch] | ||||||
|  | /qapi/qapi-events-migration.[ch] | ||||||
|  | /qapi/qapi-events-misc.[ch] | ||||||
|  | /qapi/qapi-events-net.[ch] | ||||||
|  | /qapi/qapi-events-rocker.[ch] | ||||||
|  | /qapi/qapi-events-run-state.[ch] | ||||||
|  | /qapi/qapi-events-sockets.[ch] | ||||||
|  | /qapi/qapi-events-tpm.[ch] | ||||||
|  | /qapi/qapi-events-trace.[ch] | ||||||
|  | /qapi/qapi-events-transaction.[ch] | ||||||
|  | /qapi/qapi-events-ui.[ch] | ||||||
| /qapi/qapi-events.[ch] | /qapi/qapi-events.[ch] | ||||||
| /qapi/qapi-introspect.[ch] | /qapi/qapi-introspect.[ch] | ||||||
| /qapi/qapi-types-*.[ch] | /qapi/qapi-types-block-core.[ch] | ||||||
|  | /qapi/qapi-types-block.[ch] | ||||||
|  | /qapi/qapi-types-char.[ch] | ||||||
|  | /qapi/qapi-types-common.[ch] | ||||||
|  | /qapi/qapi-types-crypto.[ch] | ||||||
|  | /qapi/qapi-types-introspect.[ch] | ||||||
|  | /qapi/qapi-types-migration.[ch] | ||||||
|  | /qapi/qapi-types-misc.[ch] | ||||||
|  | /qapi/qapi-types-net.[ch] | ||||||
|  | /qapi/qapi-types-rocker.[ch] | ||||||
|  | /qapi/qapi-types-run-state.[ch] | ||||||
|  | /qapi/qapi-types-sockets.[ch] | ||||||
|  | /qapi/qapi-types-tpm.[ch] | ||||||
|  | /qapi/qapi-types-trace.[ch] | ||||||
|  | /qapi/qapi-types-transaction.[ch] | ||||||
|  | /qapi/qapi-types-ui.[ch] | ||||||
| /qapi/qapi-types.[ch] | /qapi/qapi-types.[ch] | ||||||
| /qapi/qapi-visit-*.[ch] | /qapi/qapi-visit-block-core.[ch] | ||||||
|  | /qapi/qapi-visit-block.[ch] | ||||||
|  | /qapi/qapi-visit-char.[ch] | ||||||
|  | /qapi/qapi-visit-common.[ch] | ||||||
|  | /qapi/qapi-visit-crypto.[ch] | ||||||
|  | /qapi/qapi-visit-introspect.[ch] | ||||||
|  | /qapi/qapi-visit-migration.[ch] | ||||||
|  | /qapi/qapi-visit-misc.[ch] | ||||||
|  | /qapi/qapi-visit-net.[ch] | ||||||
|  | /qapi/qapi-visit-rocker.[ch] | ||||||
|  | /qapi/qapi-visit-run-state.[ch] | ||||||
|  | /qapi/qapi-visit-sockets.[ch] | ||||||
|  | /qapi/qapi-visit-tpm.[ch] | ||||||
|  | /qapi/qapi-visit-trace.[ch] | ||||||
|  | /qapi/qapi-visit-transaction.[ch] | ||||||
|  | /qapi/qapi-visit-ui.[ch] | ||||||
| /qapi/qapi-visit.[ch] | /qapi/qapi-visit.[ch] | ||||||
| /qapi/qapi-doc.texi | /qapi/qapi-doc.texi | ||||||
| /qemu-doc.html | /qemu-doc.html | ||||||
| /qemu-doc.info | /qemu-doc.info | ||||||
| /qemu-doc.txt | /qemu-doc.txt | ||||||
| /qemu-edid |  | ||||||
| /qemu-img | /qemu-img | ||||||
| /qemu-nbd | /qemu-nbd | ||||||
| /qemu-options.def | /qemu-options.def | ||||||
| @@ -95,7 +151,6 @@ | |||||||
| .sdk | .sdk | ||||||
| *.gcda | *.gcda | ||||||
| *.gcno | *.gcno | ||||||
| *.gcov |  | ||||||
| /pc-bios/bios-pq/status | /pc-bios/bios-pq/status | ||||||
| /pc-bios/vgabios-pq/status | /pc-bios/vgabios-pq/status | ||||||
| /pc-bios/optionrom/linuxboot.asm | /pc-bios/optionrom/linuxboot.asm | ||||||
| @@ -106,10 +161,6 @@ | |||||||
| /pc-bios/optionrom/linuxboot_dma.bin | /pc-bios/optionrom/linuxboot_dma.bin | ||||||
| /pc-bios/optionrom/linuxboot_dma.raw | /pc-bios/optionrom/linuxboot_dma.raw | ||||||
| /pc-bios/optionrom/linuxboot_dma.img | /pc-bios/optionrom/linuxboot_dma.img | ||||||
| /pc-bios/optionrom/pvh.asm |  | ||||||
| /pc-bios/optionrom/pvh.bin |  | ||||||
| /pc-bios/optionrom/pvh.raw |  | ||||||
| /pc-bios/optionrom/pvh.img |  | ||||||
| /pc-bios/optionrom/multiboot.asm | /pc-bios/optionrom/multiboot.asm | ||||||
| /pc-bios/optionrom/multiboot.bin | /pc-bios/optionrom/multiboot.bin | ||||||
| /pc-bios/optionrom/multiboot.raw | /pc-bios/optionrom/multiboot.raw | ||||||
| @@ -120,7 +171,6 @@ | |||||||
| /pc-bios/optionrom/kvmvapic.img | /pc-bios/optionrom/kvmvapic.img | ||||||
| /pc-bios/s390-ccw/s390-ccw.elf | /pc-bios/s390-ccw/s390-ccw.elf | ||||||
| /pc-bios/s390-ccw/s390-ccw.img | /pc-bios/s390-ccw/s390-ccw.img | ||||||
| /docs/built |  | ||||||
| /docs/interop/qemu-ga-qapi.texi | /docs/interop/qemu-ga-qapi.texi | ||||||
| /docs/interop/qemu-ga-ref.html | /docs/interop/qemu-ga-ref.html | ||||||
| /docs/interop/qemu-ga-ref.info* | /docs/interop/qemu-ga-ref.info* | ||||||
| @@ -156,4 +206,3 @@ trace-dtrace-root.h | |||||||
| trace-dtrace-root.dtrace | trace-dtrace-root.dtrace | ||||||
| trace-ust-all.h | trace-ust-all.h | ||||||
| trace-ust-all.c | trace-ust-all.c | ||||||
| /target/arm/decode-sve.inc.c |  | ||||||
|   | |||||||
| @@ -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"] | [submodule "roms/seabios"] | ||||||
| 	path = roms/seabios | 	path = roms/seabios | ||||||
| 	url = https://git.qemu.org/git/seabios.git/ | 	url = git://git.qemu-project.org/seabios.git/ | ||||||
| [submodule "roms/SLOF"] | [submodule "roms/SLOF"] | ||||||
| 	path = roms/SLOF | 	path = roms/SLOF | ||||||
| 	url = https://git.qemu.org/git/SLOF.git | 	url = git://git.qemu-project.org/SLOF.git | ||||||
| [submodule "roms/ipxe"] | [submodule "roms/ipxe"] | ||||||
| 	path = roms/ipxe | 	path = roms/ipxe | ||||||
| 	url = https://git.qemu.org/git/ipxe.git | 	url = git://git.qemu-project.org/ipxe.git | ||||||
| [submodule "roms/openbios"] | [submodule "roms/openbios"] | ||||||
| 	path = roms/openbios | 	path = roms/openbios | ||||||
| 	url = https://git.qemu.org/git/openbios.git | 	url = git://git.qemu-project.org/openbios.git | ||||||
| [submodule "roms/openhackware"] | [submodule "roms/openhackware"] | ||||||
| 	path = roms/openhackware | 	path = roms/openhackware | ||||||
| 	url = https://git.qemu.org/git/openhackware.git | 	url = git://git.qemu-project.org/openhackware.git | ||||||
| [submodule "roms/qemu-palcode"] | [submodule "roms/qemu-palcode"] | ||||||
| 	path = roms/qemu-palcode | 	path = roms/qemu-palcode | ||||||
| 	url = https://git.qemu.org/git/qemu-palcode.git | 	url = git://github.com/rth7680/qemu-palcode.git | ||||||
| [submodule "roms/sgabios"] | [submodule "roms/sgabios"] | ||||||
| 	path = roms/sgabios | 	path = roms/sgabios | ||||||
| 	url = https://git.qemu.org/git/sgabios.git | 	url = git://git.qemu-project.org/sgabios.git | ||||||
| [submodule "dtc"] | [submodule "dtc"] | ||||||
| 	path = dtc | 	path = dtc | ||||||
| 	url = https://git.qemu.org/git/dtc.git | 	url = git://git.qemu-project.org/dtc.git | ||||||
| [submodule "roms/u-boot"] | [submodule "roms/u-boot"] | ||||||
| 	path = roms/u-boot | 	path = roms/u-boot | ||||||
| 	url = https://git.qemu.org/git/u-boot.git | 	url = git://git.qemu-project.org/u-boot.git | ||||||
| [submodule "roms/skiboot"] | [submodule "roms/skiboot"] | ||||||
| 	path = roms/skiboot | 	path = roms/skiboot | ||||||
| 	url = https://git.qemu.org/git/skiboot.git | 	url = git://git.qemu.org/skiboot.git | ||||||
| [submodule "roms/QemuMacDrivers"] | [submodule "roms/QemuMacDrivers"] | ||||||
| 	path = roms/QemuMacDrivers | 	path = roms/QemuMacDrivers | ||||||
| 	url = https://git.qemu.org/git/QemuMacDrivers.git | 	url = git://git.qemu.org/QemuMacDrivers.git | ||||||
| [submodule "ui/keycodemapdb"] | [submodule "ui/keycodemapdb"] | ||||||
| 	path = ui/keycodemapdb | 	path = ui/keycodemapdb | ||||||
| 	url = https://git.qemu.org/git/keycodemapdb.git | 	url = git://git.qemu.org/keycodemapdb.git | ||||||
| [submodule "capstone"] | [submodule "capstone"] | ||||||
| 	path = capstone | 	path = capstone | ||||||
| 	url = https://git.qemu.org/git/capstone.git | 	url = git://git.qemu.org/capstone.git | ||||||
| [submodule "roms/seabios-hppa"] | [submodule "roms/seabios-hppa"] | ||||||
| 	path = roms/seabios-hppa | 	path = roms/seabios-hppa | ||||||
| 	url = https://github.com/hdeller/seabios-hppa.git | 	url = git://github.com/hdeller/seabios-hppa.git | ||||||
| [submodule "roms/u-boot-sam460ex"] | [submodule "roms/u-boot-sam460ex"] | ||||||
| 	path = roms/u-boot-sam460ex | 	path = roms/u-boot-sam460ex | ||||||
| 	url = https://git.qemu.org/git/u-boot-sam460ex.git | 	url = git://github.com/zbalaton/u-boot-sam460ex | ||||||
| [submodule "tests/fp/berkeley-testfloat-3"] |  | ||||||
| 	path = tests/fp/berkeley-testfloat-3 |  | ||||||
| 	url = https://github.com/cota/berkeley-testfloat-3 |  | ||||||
| [submodule "tests/fp/berkeley-softfloat-3"] |  | ||||||
| 	path = tests/fp/berkeley-softfloat-3 |  | ||||||
| 	url = https://github.com/cota/berkeley-softfloat-3 |  | ||||||
| [submodule "roms/edk2"] |  | ||||||
| 	path = roms/edk2 |  | ||||||
| 	url = https://github.com/tianocore/edk2.git |  | ||||||
|   | |||||||
							
								
								
									
										29
									
								
								.mailmap
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								.mailmap
									
									
									
									
									
								
							| @@ -1,7 +1,6 @@ | |||||||
| # This mailmap fixes up author names/addresses. | # This mailmap just translates the weird addresses from the original import into git | ||||||
|  | # into proper addresses so that they are counted properly in git shortlog output. | ||||||
| # The first section translates weird addresses from the original git import | # | ||||||
| # into proper addresses so that they are counted properly by git shortlog. |  | ||||||
| Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162> | Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
| Anthony Liguori <anthony@codemonkey.ws> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162> | Anthony Liguori <anthony@codemonkey.ws> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
| Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com> | Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com> | ||||||
| @@ -12,28 +11,14 @@ Fabrice Bellard <fabrice@bellard.org> bellard <bellard@c046a42c-6fe2-441c-8c8c-7 | |||||||
| James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com> | James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com> | ||||||
| Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162> | Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
| Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162> | Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
| Yongbok Kim <yongbok.kim@mips.com> <yongbok.kim@imgtec.com> | Paul Burton <paul.burton@mips.com> <paul.burton@imgtec.com> | ||||||
| Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@mips.com> | Paul Burton <paul.burton@mips.com> <paul@archlinuxmips.org> | ||||||
| Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@imgtec.com> |  | ||||||
| Paul Burton <pburton@wavecomp.com> <paul.burton@mips.com> |  | ||||||
| Paul Burton <pburton@wavecomp.com> <paul.burton@imgtec.com> |  | ||||||
| Paul Burton <pburton@wavecomp.com> <paul@archlinuxmips.org> |  | ||||||
| Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162> | Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
| malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162> | malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
|  |  | ||||||
| # There is also a: | # There is also a: | ||||||
| #    (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162> | #    (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162> | ||||||
| # for the cvs2svn initialization commit e63c3dc74bf. | # for the cvs2svn initialization commit e63c3dc74bf. | ||||||
|  | # | ||||||
| # Next, translate a few commits where mailman rewrote the From: line due |  | ||||||
| # to strict SPF, although we prefer to avoid adding more entries like that. |  | ||||||
| Ed Swierk <eswierk@skyportsystems.com> Ed Swierk via Qemu-devel <qemu-devel@nongnu.org> |  | ||||||
| Ian McKellar <ianloic@google.com> Ian McKellar via Qemu-devel <qemu-devel@nongnu.org> |  | ||||||
| Julia Suvorova <jusual@mail.ru> Julia Suvorova via Qemu-devel <qemu-devel@nongnu.org> |  | ||||||
| Justin Terry (VM) <juterry@microsoft.com> Justin Terry (VM) via Qemu-devel <qemu-devel@nongnu.org> |  | ||||||
|  |  | ||||||
|  |  | ||||||
| # Also list preferred name forms where people have changed their | # Also list preferred name forms where people have changed their | ||||||
| # git author config, or had utf8/latin1 encoding issues. | # git author config | ||||||
| Daniel P. Berrangé <berrange@redhat.com> | Daniel P. Berrangé <berrange@redhat.com> | ||||||
| Reimar Döffinger <Reimar.Doeffinger@gmx.de> |  | ||||||
|   | |||||||
| @@ -7,11 +7,10 @@ env: | |||||||
|   matrix: |   matrix: | ||||||
|     - IMAGE=debian-amd64 |     - IMAGE=debian-amd64 | ||||||
|       TARGET_LIST=x86_64-softmmu,x86_64-linux-user |       TARGET_LIST=x86_64-softmmu,x86_64-linux-user | ||||||
|     # currently disabled as the mxe.cc repos are down |     - IMAGE=debian-win32-cross | ||||||
|     # - IMAGE=debian-win32-cross |       TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu | ||||||
|     #   TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu |     - IMAGE=debian-win64-cross | ||||||
|     # - IMAGE=debian-win64-cross |       TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu | ||||||
|     #   TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu |  | ||||||
|     - IMAGE=debian-armel-cross |     - IMAGE=debian-armel-cross | ||||||
|       TARGET_LIST=arm-softmmu,arm-linux-user,armeb-linux-user |       TARGET_LIST=arm-softmmu,arm-linux-user,armeb-linux-user | ||||||
|     - IMAGE=debian-armhf-cross |     - IMAGE=debian-armhf-cross | ||||||
| @@ -36,5 +35,13 @@ build: | |||||||
|     options: "-e HOME=/root" |     options: "-e HOME=/root" | ||||||
|   ci: |   ci: | ||||||
|     - unset CC |     - unset CC | ||||||
|  |     # some targets require newer up to date packages, for example TARGET_LIST matching | ||||||
|  |     # aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu) | ||||||
|  |     # see the configure script: | ||||||
|  |     #    error_exit "DTC (libfdt) version >= 1.4.2 not present. Your options:" | ||||||
|  |     #    "  (1) Preferred: Install the DTC (libfdt) devel package" | ||||||
|  |     #    "  (2) Fetch the DTC submodule, using:" | ||||||
|  |     #    "      git submodule update --init dtc" | ||||||
|  |     - dpkg --compare-versions `dpkg-query --showformat='${Version}' --show libfdt-dev` ge 1.4.2 || git submodule update --init dtc | ||||||
|     - ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST} |     - ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST} | ||||||
|     - make -j$(($(getconf _NPROCESSORS_ONLN) + 1)) |     - make -j$(($(getconf _NPROCESSORS_ONLN) + 1)) | ||||||
|   | |||||||
							
								
								
									
										287
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										287
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,13 +1,10 @@ | |||||||
| # The current Travis default is a VM based 16.04 Xenial on GCE | sudo: false | ||||||
| # Additional builds with specific requirements for a full VM need to |  | ||||||
| # be added as additional matrix: entries later on |  | ||||||
| dist: xenial |  | ||||||
| language: c | language: c | ||||||
|  | python: | ||||||
|  |   - "2.6" | ||||||
| compiler: | compiler: | ||||||
|   - gcc |   - gcc | ||||||
| cache: ccache | cache: ccache | ||||||
|  |  | ||||||
|  |  | ||||||
| addons: | addons: | ||||||
|   apt: |   apt: | ||||||
|     packages: |     packages: | ||||||
| @@ -34,15 +31,9 @@ addons: | |||||||
|       - libssh2-1-dev |       - libssh2-1-dev | ||||||
|       - liburcu-dev |       - liburcu-dev | ||||||
|       - libusb-1.0-0-dev |       - libusb-1.0-0-dev | ||||||
|       - libvte-2.91-dev |       - libvte-2.90-dev | ||||||
|       - sparse |       - sparse | ||||||
|       - uuid-dev |       - uuid-dev | ||||||
|       - gcovr |  | ||||||
|   homebrew: |  | ||||||
|     packages: |  | ||||||
|       - glib |  | ||||||
|       - pixman |  | ||||||
|  |  | ||||||
|  |  | ||||||
| # The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu | # The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu | ||||||
| # to prevent IRC notifications from forks. This was created using: | # to prevent IRC notifications from forks. This was created using: | ||||||
| @@ -53,183 +44,130 @@ notifications: | |||||||
|       - secure: "F7GDRgjuOo5IUyRLqSkmDL7kvdU4UcH3Lm/W2db2JnDHTGCqgEdaYEYKciyCLZ57vOTsTsOgesN8iUT7hNHBd1KWKjZe9KDTZWppWRYVwAwQMzVeSOsbbU4tRoJ6Pp+3qhH1Z0eGYR9ZgKYAoTumDFgSAYRp4IscKS8jkoedOqM=" |       - secure: "F7GDRgjuOo5IUyRLqSkmDL7kvdU4UcH3Lm/W2db2JnDHTGCqgEdaYEYKciyCLZ57vOTsTsOgesN8iUT7hNHBd1KWKjZe9KDTZWppWRYVwAwQMzVeSOsbbU4tRoJ6Pp+3qhH1Z0eGYR9ZgKYAoTumDFgSAYRp4IscKS8jkoedOqM=" | ||||||
|     on_success: change |     on_success: change | ||||||
|     on_failure: always |     on_failure: always | ||||||
|  |  | ||||||
|  |  | ||||||
| env: | env: | ||||||
|   global: |   global: | ||||||
|     - SRC_DIR="." |     - TEST_CMD="make check" | ||||||
|     - BUILD_DIR="." |     - MAKEFLAGS="-j3" | ||||||
|     - BASE_CONFIG="--disable-docs --disable-tools" |   matrix: | ||||||
|     - TEST_CMD="make check -j3 V=1" |     - CONFIG="" | ||||||
|  |     - CONFIG="--enable-debug --enable-debug-tcg --enable-trace-backends=log" | ||||||
|  |     - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb" | ||||||
|  |     - CONFIG="--enable-modules --disable-linux-user" | ||||||
|  |     - CONFIG="--with-coroutine=ucontext --disable-linux-user" | ||||||
|  |     - CONFIG="--with-coroutine=sigaltstack --disable-linux-user" | ||||||
| git: | git: | ||||||
|   # we want to do this ourselves |   # we want to do this ourselves | ||||||
|   submodules: false |   submodules: false | ||||||
|  | before_install: | ||||||
|  |   - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update ; fi | ||||||
|  |   - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install libffi gettext glib pixman ; fi | ||||||
|  |   - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||||
|  |   - git submodule update --init --recursive | ||||||
| before_script: | before_script: | ||||||
|   - mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR} |   - ./configure ${CONFIG} | ||||||
|   - ${SRC_DIR}/configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; } |  | ||||||
| script: | script: | ||||||
|   - make -j3 && ${TEST_CMD} |   - make ${MAKEFLAGS} && ${TEST_CMD} | ||||||
|  |  | ||||||
|  |  | ||||||
| matrix: | matrix: | ||||||
|   include: |   include: | ||||||
|     - env: |     # Test with CLang for compile portability | ||||||
|         - CONFIG="--disable-system" |     - env: CONFIG="" | ||||||
|  |  | ||||||
|  |  | ||||||
|     - env: |  | ||||||
|         - CONFIG="--disable-user" |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     # 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" |  | ||||||
|       compiler: clang |       compiler: clang | ||||||
|  |  | ||||||
|  |  | ||||||
|     - env: |  | ||||||
|         - CONFIG="--disable-user" |  | ||||||
|       compiler: clang |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     # gprof/gcov are GCC features |     # gprof/gcov are GCC features | ||||||
|     - env: |     - env: CONFIG="--enable-gprof --enable-gcov --disable-pie" | ||||||
|         - CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu" |       compiler: gcc | ||||||
|       after_success: |  | ||||||
|         - ${SRC_DIR}/scripts/travis/coverage-summary.sh |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     # We manually include builds which we disable "make check" for |     # We manually include builds which we disable "make check" for | ||||||
|     - env: |     - env: CONFIG="--enable-debug --enable-tcg-interpreter" | ||||||
|         - CONFIG="--without-default-devices" |            TEST_CMD="" | ||||||
|         - TEST_CMD="" |       compiler: gcc | ||||||
|  |     - env: CONFIG="--enable-trace-backends=simple" | ||||||
|  |            TEST_CMD="" | ||||||
|     # We manually include builds which we disable "make check" for |       compiler: gcc | ||||||
|     - env: |     - env: CONFIG="--enable-trace-backends=ftrace" | ||||||
|         - CONFIG="--enable-debug --enable-tcg-interpreter" |            TEST_CMD="" | ||||||
|         - TEST_CMD="" |       compiler: gcc | ||||||
|  |     - env: CONFIG="--enable-trace-backends=ust" | ||||||
|  |            TEST_CMD="" | ||||||
|     # We don't need to exercise every backend with every front-end |       compiler: gcc | ||||||
|     - env: |     - env: CONFIG="--disable-tcg" | ||||||
|         - CONFIG="--enable-trace-backends=log,simple,syslog --disable-system" |            TEST_CMD="" | ||||||
|         - TEST_CMD="" |       compiler: gcc | ||||||
|  |     - env: CONFIG="" | ||||||
|  |  | ||||||
|     - 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" |  | ||||||
|       os: osx |       os: osx | ||||||
|       osx_image: xcode9.4 |  | ||||||
|       compiler: clang |       compiler: clang | ||||||
|  |     # Plain Trusty System Build | ||||||
|  |     - env: CONFIG="--disable-linux-user" | ||||||
|     - env: |       sudo: required | ||||||
|         - CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu" |       addons: | ||||||
|       os: osx |       dist: trusty | ||||||
|       osx_image: xcode10.2 |       compiler: gcc | ||||||
|       compiler: clang |       before_install: | ||||||
|  |         - sudo apt-get update -qq | ||||||
|  |         - sudo apt-get build-dep -qq qemu | ||||||
|     # Python builds |         - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||||
|     - env: |         - git submodule update --init --recursive | ||||||
|         - CONFIG="--target-list=x86_64-softmmu" |     # Plain Trusty Linux User Build | ||||||
|       language: python |     - env: CONFIG="--disable-system" | ||||||
|  |       sudo: required | ||||||
|  |       addons: | ||||||
|  |       dist: trusty | ||||||
|  |       compiler: gcc | ||||||
|  |       before_install: | ||||||
|  |         - sudo apt-get update -qq | ||||||
|  |         - sudo apt-get build-dep -qq qemu | ||||||
|  |         - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||||
|  |         - git submodule update --init --recursive | ||||||
|  |     # Trusty System build with latest stable clang & python 3.0 | ||||||
|  |     - sudo: required | ||||||
|  |       addons: | ||||||
|  |       dist: trusty | ||||||
|  |       language: generic | ||||||
|  |       compiler: none | ||||||
|       python: |       python: | ||||||
|         - "3.4" |         - "3.0" | ||||||
|  |       env: | ||||||
|  |         - COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9 | ||||||
|     - env: |         - CONFIG="--disable-linux-user --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3" | ||||||
|         - CONFIG="--target-list=x86_64-softmmu" |       before_install: | ||||||
|       language: python |         - wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - | ||||||
|  |         - sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main' | ||||||
|  |         - sudo apt-get update -qq | ||||||
|  |         - sudo apt-get install -qq -y clang-3.9 | ||||||
|  |         - sudo apt-get build-dep -qq qemu | ||||||
|  |         - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||||
|  |         - git submodule update --init --recursive | ||||||
|  |       before_script: | ||||||
|  |         - ./configure ${CONFIG} || cat config.log | ||||||
|  |     # Trusty Linux User build with latest stable clang & python 3.6 | ||||||
|  |     - sudo: required | ||||||
|  |       addons: | ||||||
|  |       dist: trusty | ||||||
|  |       language: generic | ||||||
|  |       compiler: none | ||||||
|       python: |       python: | ||||||
|         - "3.6" |         - "3.6" | ||||||
|  |       env: | ||||||
|  |         - COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9 | ||||||
|     # Acceptance (Functional) tests |         - CONFIG="--disable-system --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3" | ||||||
|     - env: |       before_install: | ||||||
|         - CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu" |         - wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - | ||||||
|         - TEST_CMD="make AVOCADO_SHOW=app check-acceptance" |         - sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main' | ||||||
|       addons: |         - sudo apt-get update -qq | ||||||
|         apt: |         - sudo apt-get install -qq -y clang-3.9 | ||||||
|           packages: |         - sudo apt-get build-dep -qq qemu | ||||||
|             - python3-pip |         - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ | ||||||
|             - python3.5-venv |         - git submodule update --init --recursive | ||||||
|  |       before_script: | ||||||
|  |         - ./configure ${CONFIG} || cat config.log | ||||||
|     # Using newer GCC with sanitizers |     # Using newer GCC with sanitizers | ||||||
|     - addons: |     - addons: | ||||||
|         apt: |         apt: | ||||||
|           update: true |  | ||||||
|           sources: |           sources: | ||||||
|             # PPAs for newer toolchains |             # PPAs for newer toolchains | ||||||
|             - ubuntu-toolchain-r-test |             - ubuntu-toolchain-r-test | ||||||
|           packages: |           packages: | ||||||
|             # Extra toolchains |             # Extra toolchains | ||||||
|             - gcc-7 |             - gcc-5 | ||||||
|             - g++-7 |             - g++-5 | ||||||
|             # Build dependencies |             # Build dependencies | ||||||
|             - libaio-dev |             - libaio-dev | ||||||
|             - libattr1-dev |             - libattr1-dev | ||||||
| @@ -252,25 +190,14 @@ matrix: | |||||||
|             - libssh2-1-dev |             - libssh2-1-dev | ||||||
|             - liburcu-dev |             - liburcu-dev | ||||||
|             - libusb-1.0-0-dev |             - libusb-1.0-0-dev | ||||||
|             - libvte-2.91-dev |             - libvte-2.90-dev | ||||||
|             - sparse |             - sparse | ||||||
|             - uuid-dev |             - uuid-dev | ||||||
|       language: generic |       language: generic | ||||||
|       compiler: none |       compiler: none | ||||||
|       env: |       env: | ||||||
|         - COMPILER_NAME=gcc CXX=g++-7 CC=gcc-7 |         - COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5 | ||||||
|         - CONFIG="--cc=gcc-7 --cxx=g++-7 --disable-pie --disable-linux-user" |         - CONFIG="--cc=gcc-5 --cxx=g++-5 --disable-pie --disable-linux-user" | ||||||
|         - TEST_CMD="" |         - TEST_CMD="" | ||||||
|       before_script: |       before_script: | ||||||
|         - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || { cat config.log && exit 1; } |         - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || cat config.log | ||||||
|  |  | ||||||
|  |  | ||||||
|     # 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" |  | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								CODING_STYLE
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								CODING_STYLE
									
									
									
									
									
								
							| @@ -124,23 +124,6 @@ We use traditional C-style /* */ comments and avoid // comments. | |||||||
| Rationale: The // form is valid in C99, so this is purely a matter of | Rationale: The // form is valid in C99, so this is purely a matter of | ||||||
| consistency of style. The checkpatch script will warn you about this. | consistency of style. The checkpatch script will warn you about this. | ||||||
|  |  | ||||||
| Multiline comment blocks should have a row of stars on the left, |  | ||||||
| and the initial /* and terminating */ both on their own lines: |  | ||||||
|     /* |  | ||||||
|      * like |  | ||||||
|      * this |  | ||||||
|      */ |  | ||||||
| This is the same format required by the Linux kernel coding style. |  | ||||||
|  |  | ||||||
| (Some of the existing comments in the codebase use the GNU Coding |  | ||||||
| Standards form which does not have stars on the left, or other |  | ||||||
| variations; avoid these when writing new comments, but don't worry |  | ||||||
| about converting to the preferred form unless you're editing that |  | ||||||
| comment anyway.) |  | ||||||
|  |  | ||||||
| Rationale: Consistency, and ease of visually picking out a multiline |  | ||||||
| comment from the surrounding code. |  | ||||||
|  |  | ||||||
| 8. trace-events style | 8. trace-events style | ||||||
|  |  | ||||||
| 8.1 0x prefix | 8.1 0x prefix | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								COPYING.LIB
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								COPYING.LIB
									
									
									
									
									
								
							| @@ -1,8 +1,8 @@ | |||||||
|                   GNU LESSER GENERAL PUBLIC LICENSE | 		  GNU LESSER GENERAL PUBLIC LICENSE | ||||||
|                        Version 2.1, February 1999 | 		       Version 2.1, February 1999 | ||||||
|  |  | ||||||
|  Copyright (C) 1991, 1999 Free Software Foundation, Inc. |  Copyright (C) 1991, 1999 Free Software Foundation, Inc. | ||||||
|  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA | 	51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||||
|  Everyone is permitted to copy and distribute verbatim copies |  Everyone is permitted to copy and distribute verbatim copies | ||||||
|  of this license document, but changing it is not allowed. |  of this license document, but changing it is not allowed. | ||||||
|  |  | ||||||
| @@ -10,7 +10,7 @@ | |||||||
|  as the successor of the GNU Library Public License, version 2, hence |  as the successor of the GNU Library Public License, version 2, hence | ||||||
|  the version number 2.1.] |  the version number 2.1.] | ||||||
|  |  | ||||||
|                             Preamble | 			    Preamble | ||||||
|  |  | ||||||
|   The licenses for most software are designed to take away your |   The licenses for most software are designed to take away your | ||||||
| freedom to share and change it.  By contrast, the GNU General Public | 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 | former contains code derived from the library, whereas the latter must | ||||||
| be combined with the library in order to run. | 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 |    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||||||
|  |  | ||||||
|   0. This License Agreement applies to any software library or other |   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 | 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 | writing it).  Whether that is true depends on what the Library does | ||||||
| and what the program that uses the Library does. | and what the program that uses the Library does. | ||||||
|  |    | ||||||
|   1. You may copy and distribute verbatim copies of the Library's |   1. You may copy and distribute verbatim copies of the Library's | ||||||
| complete source code as you receive it, in any medium, provided that | complete source code as you receive it, in any medium, provided that | ||||||
| you conspicuously and appropriately publish on each copy an | 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 | of all derivatives of our free software and of promoting the sharing | ||||||
| and reuse of software generally. | and reuse of software generally. | ||||||
|  |  | ||||||
|                             NO WARRANTY | 			    NO WARRANTY | ||||||
|  |  | ||||||
|   15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO |   15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO | ||||||
| WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. | 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 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | ||||||
| DAMAGES. | DAMAGES. | ||||||
|  |  | ||||||
|                      END OF TERMS AND CONDITIONS | 		     END OF TERMS AND CONDITIONS | ||||||
|  |  | ||||||
|            How to Apply These Terms to Your New Libraries |            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 |     This library is free software; you can redistribute it and/or | ||||||
|     modify it under the terms of the GNU Lesser General Public |     modify it under the terms of the GNU Lesser General Public | ||||||
|     License as published by the Free Software Foundation; either |     License as published by the Free Software Foundation; either | ||||||
|     version 2.1 of the License, or (at your option) any later version. |     version 2 of the License, or (at your option) any later version. | ||||||
|  |  | ||||||
|     This library is distributed in the hope that it will be useful, |     This library is distributed in the hope that it will be useful, | ||||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of |     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -485,7 +485,7 @@ convey the exclusion of warranty; and each file should have at least the | |||||||
|  |  | ||||||
|     You should have received a copy of the GNU Lesser General Public |     You should have received a copy of the GNU Lesser General Public | ||||||
|     License along with this library; if not, write to the Free Software |     License along with this library; if not, write to the Free Software | ||||||
|     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA |     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||||
|  |  | ||||||
| Also add information on how to contact you by electronic and paper mail. | Also add information on how to contact you by electronic and paper mail. | ||||||
|  |  | ||||||
| @@ -500,3 +500,5 @@ necessary.  Here is a sample; alter the names: | |||||||
|   Ty Coon, President of Vice |   Ty Coon, President of Vice | ||||||
|  |  | ||||||
| That's all there is to it! | That's all there is to it! | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										270
									
								
								COPYING.PYTHON
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										270
									
								
								COPYING.PYTHON
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,270 @@ | |||||||
|  | A. HISTORY OF THE SOFTWARE | ||||||
|  | ========================== | ||||||
|  |  | ||||||
|  | Python was created in the early 1990s by Guido van Rossum at Stichting | ||||||
|  | Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands | ||||||
|  | as a successor of a language called ABC.  Guido remains Python's | ||||||
|  | principal author, although it includes many contributions from others. | ||||||
|  |  | ||||||
|  | In 1995, Guido continued his work on Python at the Corporation for | ||||||
|  | National Research Initiatives (CNRI, see http://www.cnri.reston.va.us) | ||||||
|  | in Reston, Virginia where he released several versions of the | ||||||
|  | software. | ||||||
|  |  | ||||||
|  | In May 2000, Guido and the Python core development team moved to | ||||||
|  | BeOpen.com to form the BeOpen PythonLabs team.  In October of the same | ||||||
|  | year, the PythonLabs team moved to Digital Creations (now Zope | ||||||
|  | Corporation, see http://www.zope.com).  In 2001, the Python Software | ||||||
|  | Foundation (PSF, see http://www.python.org/psf/) was formed, a | ||||||
|  | non-profit organization created specifically to own Python-related | ||||||
|  | Intellectual Property.  Zope Corporation is a sponsoring member of | ||||||
|  | the PSF. | ||||||
|  |  | ||||||
|  | All Python releases are Open Source (see http://www.opensource.org for | ||||||
|  | the Open Source Definition).  Historically, most, but not all, Python | ||||||
|  | releases have also been GPL-compatible; the table below summarizes | ||||||
|  | the various releases. | ||||||
|  |  | ||||||
|  |     Release         Derived     Year        Owner       GPL- | ||||||
|  |                     from                                compatible? (1) | ||||||
|  |  | ||||||
|  |     0.9.0 thru 1.2              1991-1995   CWI         yes | ||||||
|  |     1.3 thru 1.5.2  1.2         1995-1999   CNRI        yes | ||||||
|  |     1.6             1.5.2       2000        CNRI        no | ||||||
|  |     2.0             1.6         2000        BeOpen.com  no | ||||||
|  |     1.6.1           1.6         2001        CNRI        yes (2) | ||||||
|  |     2.1             2.0+1.6.1   2001        PSF         no | ||||||
|  |     2.0.1           2.0+1.6.1   2001        PSF         yes | ||||||
|  |     2.1.1           2.1+2.0.1   2001        PSF         yes | ||||||
|  |     2.2             2.1.1       2001        PSF         yes | ||||||
|  |     2.1.2           2.1.1       2002        PSF         yes | ||||||
|  |     2.1.3           2.1.2       2002        PSF         yes | ||||||
|  |     2.2.1           2.2         2002        PSF         yes | ||||||
|  |     2.2.2           2.2.1       2002        PSF         yes | ||||||
|  |     2.2.3           2.2.2       2003        PSF         yes | ||||||
|  |     2.3             2.2.2       2002-2003   PSF         yes | ||||||
|  |     2.3.1           2.3         2002-2003   PSF         yes | ||||||
|  |     2.3.2           2.3.1       2002-2003   PSF         yes | ||||||
|  |     2.3.3           2.3.2       2002-2003   PSF         yes | ||||||
|  |     2.3.4           2.3.3       2004        PSF         yes | ||||||
|  |     2.3.5           2.3.4       2005        PSF         yes | ||||||
|  |     2.4             2.3         2004        PSF         yes | ||||||
|  |     2.4.1           2.4         2005        PSF         yes | ||||||
|  |     2.4.2           2.4.1       2005        PSF         yes | ||||||
|  |     2.4.3           2.4.2       2006        PSF         yes | ||||||
|  |     2.5             2.4         2006        PSF         yes | ||||||
|  |     2.7             2.6         2010        PSF         yes | ||||||
|  |  | ||||||
|  | Footnotes: | ||||||
|  |  | ||||||
|  | (1) GPL-compatible doesn't mean that we're distributing Python under | ||||||
|  |     the GPL.  All Python licenses, unlike the GPL, let you distribute | ||||||
|  |     a modified version without making your changes open source.  The | ||||||
|  |     GPL-compatible licenses make it possible to combine Python with | ||||||
|  |     other software that is released under the GPL; the others don't. | ||||||
|  |  | ||||||
|  | (2) According to Richard Stallman, 1.6.1 is not GPL-compatible, | ||||||
|  |     because its license has a choice of law clause.  According to | ||||||
|  |     CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 | ||||||
|  |     is "not incompatible" with the GPL. | ||||||
|  |  | ||||||
|  | Thanks to the many outside volunteers who have worked under Guido's | ||||||
|  | direction to make these releases possible. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON | ||||||
|  | =============================================================== | ||||||
|  |  | ||||||
|  | PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 | ||||||
|  | -------------------------------------------- | ||||||
|  |  | ||||||
|  | 1. This LICENSE AGREEMENT is between the Python Software Foundation | ||||||
|  | ("PSF"), and the Individual or Organization ("Licensee") accessing and | ||||||
|  | otherwise using this software ("Python") in source or binary form and | ||||||
|  | its associated documentation. | ||||||
|  |  | ||||||
|  | 2. Subject to the terms and conditions of this License Agreement, PSF | ||||||
|  | hereby grants Licensee a nonexclusive, royalty-free, world-wide | ||||||
|  | license to reproduce, analyze, test, perform and/or display publicly, | ||||||
|  | prepare derivative works, distribute, and otherwise use Python | ||||||
|  | alone or in any derivative version, provided, however, that PSF's | ||||||
|  | License Agreement and PSF's notice of copyright, i.e., "Copyright (c) | ||||||
|  | 2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation; All Rights | ||||||
|  | Reserved" are retained in Python alone or in any derivative version  | ||||||
|  | prepared by Licensee. | ||||||
|  |  | ||||||
|  | 3. In the event Licensee prepares a derivative work that is based on | ||||||
|  | or incorporates Python or any part thereof, and wants to make | ||||||
|  | the derivative work available to others as provided herein, then | ||||||
|  | Licensee hereby agrees to include in any such work a brief summary of | ||||||
|  | the changes made to Python. | ||||||
|  |  | ||||||
|  | 4. PSF is making Python available to Licensee on an "AS IS" | ||||||
|  | basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR | ||||||
|  | IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND | ||||||
|  | DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS | ||||||
|  | FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT | ||||||
|  | INFRINGE ANY THIRD PARTY RIGHTS. | ||||||
|  |  | ||||||
|  | 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON | ||||||
|  | FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS | ||||||
|  | A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, | ||||||
|  | OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. | ||||||
|  |  | ||||||
|  | 6. This License Agreement will automatically terminate upon a material | ||||||
|  | breach of its terms and conditions. | ||||||
|  |  | ||||||
|  | 7. Nothing in this License Agreement shall be deemed to create any | ||||||
|  | relationship of agency, partnership, or joint venture between PSF and | ||||||
|  | Licensee.  This License Agreement does not grant permission to use PSF | ||||||
|  | trademarks or trade name in a trademark sense to endorse or promote | ||||||
|  | products or services of Licensee, or any third party. | ||||||
|  |  | ||||||
|  | 8. By copying, installing or otherwise using Python, Licensee | ||||||
|  | agrees to be bound by the terms and conditions of this License | ||||||
|  | Agreement. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 | ||||||
|  | ------------------------------------------- | ||||||
|  |  | ||||||
|  | BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 | ||||||
|  |  | ||||||
|  | 1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an | ||||||
|  | office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the | ||||||
|  | Individual or Organization ("Licensee") accessing and otherwise using | ||||||
|  | this software in source or binary form and its associated | ||||||
|  | documentation ("the Software"). | ||||||
|  |  | ||||||
|  | 2. Subject to the terms and conditions of this BeOpen Python License | ||||||
|  | Agreement, BeOpen hereby grants Licensee a non-exclusive, | ||||||
|  | royalty-free, world-wide license to reproduce, analyze, test, perform | ||||||
|  | and/or display publicly, prepare derivative works, distribute, and | ||||||
|  | otherwise use the Software alone or in any derivative version, | ||||||
|  | provided, however, that the BeOpen Python License is retained in the | ||||||
|  | Software, alone or in any derivative version prepared by Licensee. | ||||||
|  |  | ||||||
|  | 3. BeOpen is making the Software available to Licensee on an "AS IS" | ||||||
|  | basis.  BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR | ||||||
|  | IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND | ||||||
|  | DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS | ||||||
|  | FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT | ||||||
|  | INFRINGE ANY THIRD PARTY RIGHTS. | ||||||
|  |  | ||||||
|  | 4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE | ||||||
|  | SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS | ||||||
|  | AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY | ||||||
|  | DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. | ||||||
|  |  | ||||||
|  | 5. This License Agreement will automatically terminate upon a material | ||||||
|  | breach of its terms and conditions. | ||||||
|  |  | ||||||
|  | 6. This License Agreement shall be governed by and interpreted in all | ||||||
|  | respects by the law of the State of California, excluding conflict of | ||||||
|  | law provisions.  Nothing in this License Agreement shall be deemed to | ||||||
|  | create any relationship of agency, partnership, or joint venture | ||||||
|  | between BeOpen and Licensee.  This License Agreement does not grant | ||||||
|  | permission to use BeOpen trademarks or trade names in a trademark | ||||||
|  | sense to endorse or promote products or services of Licensee, or any | ||||||
|  | third party.  As an exception, the "BeOpen Python" logos available at | ||||||
|  | http://www.pythonlabs.com/logos.html may be used according to the | ||||||
|  | permissions granted on that web page. | ||||||
|  |  | ||||||
|  | 7. By copying, installing or otherwise using the software, Licensee | ||||||
|  | agrees to be bound by the terms and conditions of this License | ||||||
|  | Agreement. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 | ||||||
|  | --------------------------------------- | ||||||
|  |  | ||||||
|  | 1. This LICENSE AGREEMENT is between the Corporation for National | ||||||
|  | Research Initiatives, having an office at 1895 Preston White Drive, | ||||||
|  | Reston, VA 20191 ("CNRI"), and the Individual or Organization | ||||||
|  | ("Licensee") accessing and otherwise using Python 1.6.1 software in | ||||||
|  | source or binary form and its associated documentation. | ||||||
|  |  | ||||||
|  | 2. Subject to the terms and conditions of this License Agreement, CNRI | ||||||
|  | hereby grants Licensee a nonexclusive, royalty-free, world-wide | ||||||
|  | license to reproduce, analyze, test, perform and/or display publicly, | ||||||
|  | prepare derivative works, distribute, and otherwise use Python 1.6.1 | ||||||
|  | alone or in any derivative version, provided, however, that CNRI's | ||||||
|  | License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) | ||||||
|  | 1995-2001 Corporation for National Research Initiatives; All Rights | ||||||
|  | Reserved" are retained in Python 1.6.1 alone or in any derivative | ||||||
|  | version prepared by Licensee.  Alternately, in lieu of CNRI's License | ||||||
|  | Agreement, Licensee may substitute the following text (omitting the | ||||||
|  | quotes): "Python 1.6.1 is made available subject to the terms and | ||||||
|  | conditions in CNRI's License Agreement.  This Agreement together with | ||||||
|  | Python 1.6.1 may be located on the Internet using the following | ||||||
|  | unique, persistent identifier (known as a handle): 1895.22/1013.  This | ||||||
|  | Agreement may also be obtained from a proxy server on the Internet | ||||||
|  | using the following URL: http://hdl.handle.net/1895.22/1013". | ||||||
|  |  | ||||||
|  | 3. In the event Licensee prepares a derivative work that is based on | ||||||
|  | or incorporates Python 1.6.1 or any part thereof, and wants to make | ||||||
|  | the derivative work available to others as provided herein, then | ||||||
|  | Licensee hereby agrees to include in any such work a brief summary of | ||||||
|  | the changes made to Python 1.6.1. | ||||||
|  |  | ||||||
|  | 4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" | ||||||
|  | basis.  CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR | ||||||
|  | IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND | ||||||
|  | DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS | ||||||
|  | FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT | ||||||
|  | INFRINGE ANY THIRD PARTY RIGHTS. | ||||||
|  |  | ||||||
|  | 5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON | ||||||
|  | 1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS | ||||||
|  | A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, | ||||||
|  | OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. | ||||||
|  |  | ||||||
|  | 6. This License Agreement will automatically terminate upon a material | ||||||
|  | breach of its terms and conditions. | ||||||
|  |  | ||||||
|  | 7. This License Agreement shall be governed by the federal | ||||||
|  | intellectual property law of the United States, including without | ||||||
|  | limitation the federal copyright law, and, to the extent such | ||||||
|  | U.S. federal law does not apply, by the law of the Commonwealth of | ||||||
|  | Virginia, excluding Virginia's conflict of law provisions. | ||||||
|  | Notwithstanding the foregoing, with regard to derivative works based | ||||||
|  | on Python 1.6.1 that incorporate non-separable material that was | ||||||
|  | previously distributed under the GNU General Public License (GPL), the | ||||||
|  | law of the Commonwealth of Virginia shall govern this License | ||||||
|  | Agreement only as to issues arising under or with respect to | ||||||
|  | Paragraphs 4, 5, and 7 of this License Agreement.  Nothing in this | ||||||
|  | License Agreement shall be deemed to create any relationship of | ||||||
|  | agency, partnership, or joint venture between CNRI and Licensee.  This | ||||||
|  | License Agreement does not grant permission to use CNRI trademarks or | ||||||
|  | trade name in a trademark sense to endorse or promote products or | ||||||
|  | services of Licensee, or any third party. | ||||||
|  |  | ||||||
|  | 8. By clicking on the "ACCEPT" button where indicated, or by copying, | ||||||
|  | installing or otherwise using Python 1.6.1, Licensee agrees to be | ||||||
|  | bound by the terms and conditions of this License Agreement. | ||||||
|  |  | ||||||
|  |         ACCEPT | ||||||
|  |  | ||||||
|  |  | ||||||
|  | CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 | ||||||
|  | -------------------------------------------------- | ||||||
|  |  | ||||||
|  | Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, | ||||||
|  | The Netherlands.  All rights reserved. | ||||||
|  |  | ||||||
|  | Permission to use, copy, modify, and distribute this software and its | ||||||
|  | documentation for any purpose and without fee is hereby granted, | ||||||
|  | provided that the above copyright notice appear in all copies and that | ||||||
|  | both that copyright notice and this permission notice appear in | ||||||
|  | supporting documentation, and that the name of Stichting Mathematisch | ||||||
|  | Centrum or CWI not be used in advertising or publicity pertaining to | ||||||
|  | distribution of the software without specific, written prior | ||||||
|  | permission. | ||||||
|  |  | ||||||
|  | STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO | ||||||
|  | THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | ||||||
|  | FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE | ||||||
|  | FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||||
|  | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||||
|  | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | ||||||
|  | OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||||
							
								
								
									
										9
									
								
								HACKING
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								HACKING
									
									
									
									
									
								
							| @@ -118,15 +118,6 @@ Please note that g_malloc will exit on allocation failure, so there | |||||||
| is no need to test for failure (as you would have to with malloc). | is no need to test for failure (as you would have to with malloc). | ||||||
| Calling g_malloc with a zero size is valid and will return NULL. | Calling g_malloc with a zero size is valid and will return NULL. | ||||||
|  |  | ||||||
| Prefer g_new(T, n) instead of g_malloc(sizeof(T) * n) for the following |  | ||||||
| reasons: |  | ||||||
|  |  | ||||||
|   a. It catches multiplication overflowing size_t; |  | ||||||
|   b. It returns T * instead of void *, letting compiler catch more type |  | ||||||
|      errors. |  | ||||||
|  |  | ||||||
| Declarations like T *v = g_malloc(sizeof(*v)) are acceptable, though. |  | ||||||
|  |  | ||||||
| Memory allocated by qemu_memalign or qemu_blockalign must be freed with | Memory allocated by qemu_memalign or qemu_blockalign must be freed with | ||||||
| qemu_vfree, since breaking this will cause problems on Win32. | qemu_vfree, since breaking this will cause problems on Win32. | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										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: | all: | ||||||
| include config-host.mak | include config-host.mak | ||||||
|  |  | ||||||
|  | PYTHON_UTF8 = LC_ALL= LANG=C LC_CTYPE=en_US.UTF-8 $(PYTHON) | ||||||
|  |  | ||||||
| git-submodule-update: | git-submodule-update: | ||||||
|  |  | ||||||
| .PHONY: git-submodule-update | .PHONY: git-submodule-update | ||||||
| @@ -67,7 +69,7 @@ CONFIG_ALL=y | |||||||
| -include config-all-devices.mak | -include config-all-devices.mak | ||||||
| -include config-all-disas.mak | -include config-all-disas.mak | ||||||
|  |  | ||||||
| config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios $(SRC_PATH)/VERSION | config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios | ||||||
| 	@echo $@ is out-of-date, running configure | 	@echo $@ is out-of-date, running configure | ||||||
| 	@# TODO: The next lines include code which supports a smooth | 	@# TODO: The next lines include code which supports a smooth | ||||||
| 	@# transition from old configurations without config.status. | 	@# transition from old configurations without config.status. | ||||||
| @@ -87,41 +89,79 @@ endif | |||||||
|  |  | ||||||
| include $(SRC_PATH)/rules.mak | 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_FILES = qemu-version.h config-host.h qemu-options.def | ||||||
|  | GENERATED_FILES += qapi/qapi-builtin-types.h qapi/qapi-builtin-types.c | ||||||
| GENERATED_QAPI_FILES = qapi/qapi-builtin-types.h qapi/qapi-builtin-types.c | GENERATED_FILES += qapi/qapi-types.h qapi/qapi-types.c | ||||||
| GENERATED_QAPI_FILES += qapi/qapi-types.h qapi/qapi-types.c | GENERATED_FILES += qapi/qapi-types-block-core.h qapi/qapi-types-block-core.c | ||||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-types-%.h) | GENERATED_FILES += qapi/qapi-types-block.h qapi/qapi-types-block.c | ||||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-types-%.c) | GENERATED_FILES += qapi/qapi-types-char.h qapi/qapi-types-char.c | ||||||
| GENERATED_QAPI_FILES += qapi/qapi-builtin-visit.h qapi/qapi-builtin-visit.c | GENERATED_FILES += qapi/qapi-types-common.h qapi/qapi-types-common.c | ||||||
| GENERATED_QAPI_FILES += qapi/qapi-visit.h qapi/qapi-visit.c | GENERATED_FILES += qapi/qapi-types-crypto.h qapi/qapi-types-crypto.c | ||||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.h) | GENERATED_FILES += qapi/qapi-types-introspect.h qapi/qapi-types-introspect.c | ||||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.c) | GENERATED_FILES += qapi/qapi-types-migration.h qapi/qapi-types-migration.c | ||||||
| GENERATED_QAPI_FILES += qapi/qapi-commands.h qapi/qapi-commands.c | GENERATED_FILES += qapi/qapi-types-misc.h qapi/qapi-types-misc.c | ||||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.h) | GENERATED_FILES += qapi/qapi-types-net.h qapi/qapi-types-net.c | ||||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.c) | GENERATED_FILES += qapi/qapi-types-rocker.h qapi/qapi-types-rocker.c | ||||||
| GENERATED_QAPI_FILES += qapi/qapi-emit-events.h qapi/qapi-emit-events.c | GENERATED_FILES += qapi/qapi-types-run-state.h qapi/qapi-types-run-state.c | ||||||
| GENERATED_QAPI_FILES += qapi/qapi-events.h qapi/qapi-events.c | GENERATED_FILES += qapi/qapi-types-sockets.h qapi/qapi-types-sockets.c | ||||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.h) | GENERATED_FILES += qapi/qapi-types-tpm.h qapi/qapi-types-tpm.c | ||||||
| GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.c) | GENERATED_FILES += qapi/qapi-types-trace.h qapi/qapi-types-trace.c | ||||||
| GENERATED_QAPI_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h | GENERATED_FILES += qapi/qapi-types-transaction.h qapi/qapi-types-transaction.c | ||||||
| GENERATED_QAPI_FILES += qapi/qapi-doc.texi | GENERATED_FILES += qapi/qapi-types-ui.h qapi/qapi-types-ui.c | ||||||
|  | GENERATED_FILES += qapi/qapi-builtin-visit.h qapi/qapi-builtin-visit.c | ||||||
| GENERATED_FILES += $(GENERATED_QAPI_FILES) | GENERATED_FILES += qapi/qapi-visit.h qapi/qapi-visit.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-block-core.h qapi/qapi-visit-block-core.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-block.h qapi/qapi-visit-block.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-char.h qapi/qapi-visit-char.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-common.h qapi/qapi-visit-common.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-crypto.h qapi/qapi-visit-crypto.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-introspect.h qapi/qapi-visit-introspect.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-migration.h qapi/qapi-visit-migration.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-misc.h qapi/qapi-visit-misc.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-net.h qapi/qapi-visit-net.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-rocker.h qapi/qapi-visit-rocker.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-run-state.h qapi/qapi-visit-run-state.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-sockets.h qapi/qapi-visit-sockets.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-tpm.h qapi/qapi-visit-tpm.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-trace.h qapi/qapi-visit-trace.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-transaction.h qapi/qapi-visit-transaction.c | ||||||
|  | GENERATED_FILES += qapi/qapi-visit-ui.h qapi/qapi-visit-ui.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands.h qapi/qapi-commands.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-block-core.h qapi/qapi-commands-block-core.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-block.h qapi/qapi-commands-block.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-char.h qapi/qapi-commands-char.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-common.h qapi/qapi-commands-common.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-crypto.h qapi/qapi-commands-crypto.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-introspect.h qapi/qapi-commands-introspect.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-migration.h qapi/qapi-commands-migration.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-misc.h qapi/qapi-commands-misc.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-net.h qapi/qapi-commands-net.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-rocker.h qapi/qapi-commands-rocker.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-run-state.h qapi/qapi-commands-run-state.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-sockets.h qapi/qapi-commands-sockets.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-tpm.h qapi/qapi-commands-tpm.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-trace.h qapi/qapi-commands-trace.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-transaction.h qapi/qapi-commands-transaction.c | ||||||
|  | GENERATED_FILES += qapi/qapi-commands-ui.h qapi/qapi-commands-ui.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events.h qapi/qapi-events.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-block-core.h qapi/qapi-events-block-core.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-block.h qapi/qapi-events-block.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-char.h qapi/qapi-events-char.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-common.h qapi/qapi-events-common.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-crypto.h qapi/qapi-events-crypto.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-introspect.h qapi/qapi-events-introspect.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-migration.h qapi/qapi-events-migration.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-misc.h qapi/qapi-events-misc.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-net.h qapi/qapi-events-net.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-rocker.h qapi/qapi-events-rocker.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-run-state.h qapi/qapi-events-run-state.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-sockets.h qapi/qapi-events-sockets.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-tpm.h qapi/qapi-events-tpm.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-trace.h qapi/qapi-events-trace.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-transaction.h qapi/qapi-events-transaction.c | ||||||
|  | GENERATED_FILES += qapi/qapi-events-ui.h qapi/qapi-events-ui.c | ||||||
|  | GENERATED_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h | ||||||
|  | GENERATED_FILES += qapi/qapi-doc.texi | ||||||
|  |  | ||||||
| GENERATED_FILES += trace/generated-tcg-tracers.h | GENERATED_FILES += trace/generated-tcg-tracers.h | ||||||
|  |  | ||||||
| @@ -159,7 +199,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") | |||||||
|  |  | ||||||
| %/trace.h: %/trace.h-timestamp | %/trace.h: %/trace.h-timestamp | ||||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||||
| %/trace.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | %/trace.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ | 	$(call quiet-command,$(TRACETOOL) \ | ||||||
| 		--group=$(call trace-group-name,$@) \ | 		--group=$(call trace-group-name,$@) \ | ||||||
| 		--format=h \ | 		--format=h \ | ||||||
| @@ -168,7 +208,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") | |||||||
|  |  | ||||||
| %/trace.c: %/trace.c-timestamp | %/trace.c: %/trace.c-timestamp | ||||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||||
| %/trace.c-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | %/trace.c-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ | 	$(call quiet-command,$(TRACETOOL) \ | ||||||
| 		--group=$(call trace-group-name,$@) \ | 		--group=$(call trace-group-name,$@) \ | ||||||
| 		--format=c \ | 		--format=c \ | ||||||
| @@ -177,7 +217,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") | |||||||
|  |  | ||||||
| %/trace-ust.h: %/trace-ust.h-timestamp | %/trace-ust.h: %/trace-ust.h-timestamp | ||||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||||
| %/trace-ust.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | %/trace-ust.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ | 	$(call quiet-command,$(TRACETOOL) \ | ||||||
| 		--group=$(call trace-group-name,$@) \ | 		--group=$(call trace-group-name,$@) \ | ||||||
| 		--format=ust-events-h \ | 		--format=ust-events-h \ | ||||||
| @@ -201,7 +241,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") | |||||||
|  |  | ||||||
| trace-root.h: trace-root.h-timestamp | trace-root.h: trace-root.h-timestamp | ||||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||||
| trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ | 	$(call quiet-command,$(TRACETOOL) \ | ||||||
| 		--group=root \ | 		--group=root \ | ||||||
| 		--format=h \ | 		--format=h \ | ||||||
| @@ -210,7 +250,7 @@ trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/con | |||||||
|  |  | ||||||
| trace-root.c: trace-root.c-timestamp | trace-root.c: trace-root.c-timestamp | ||||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||||
| trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ | 	$(call quiet-command,$(TRACETOOL) \ | ||||||
| 		--group=root \ | 		--group=root \ | ||||||
| 		--format=c \ | 		--format=c \ | ||||||
| @@ -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 | trace-ust-root.h: trace-ust-root.h-timestamp | ||||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||||
| trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak | trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ | 	$(call quiet-command,$(TRACETOOL) \ | ||||||
| 		--group=root \ | 		--group=root \ | ||||||
| 		--format=ust-events-h \ | 		--format=ust-events-h \ | ||||||
| @@ -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 | trace-ust-all.h: trace-ust-all.h-timestamp | ||||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||||
| trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/config-host.mak | trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y) | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ | 	$(call quiet-command,$(TRACETOOL) \ | ||||||
| 		--group=all \ | 		--group=all \ | ||||||
| 		--format=ust-events-h \ | 		--format=ust-events-h \ | ||||||
| @@ -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 | trace-ust-all.c: trace-ust-all.c-timestamp | ||||||
| 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | 	@cmp $< $@ >/dev/null 2>&1 || cp $< $@ | ||||||
| trace-ust-all.c-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/config-host.mak | trace-ust-all.c-timestamp: $(trace-events-files) $(tracetool-y) | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ | 	$(call quiet-command,$(TRACETOOL) \ | ||||||
| 		--group=all \ | 		--group=all \ | ||||||
| 		--format=ust-events-c \ | 		--format=ust-events-c \ | ||||||
| @@ -278,7 +318,6 @@ KEYCODEMAP_FILES = \ | |||||||
| 		 ui/input-keymap-xorgkbd-to-qcode.c \ | 		 ui/input-keymap-xorgkbd-to-qcode.c \ | ||||||
| 		 ui/input-keymap-xorgxquartz-to-qcode.c \ | 		 ui/input-keymap-xorgxquartz-to-qcode.c \ | ||||||
| 		 ui/input-keymap-xorgxwin-to-qcode.c \ | 		 ui/input-keymap-xorgxwin-to-qcode.c \ | ||||||
| 		 ui/input-keymap-osx-to-qcode.c \ |  | ||||||
| 		 $(NULL) | 		 $(NULL) | ||||||
|  |  | ||||||
| GENERATED_FILES += $(KEYCODEMAP_FILES) | GENERATED_FILES += $(KEYCODEMAP_FILES) | ||||||
| @@ -308,27 +347,23 @@ $(call set-vpath, $(SRC_PATH)) | |||||||
|  |  | ||||||
| LIBS+=-lz $(LIBS_TOOLS) | LIBS+=-lz $(LIBS_TOOLS) | ||||||
|  |  | ||||||
| HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) = qemu-bridge-helper$(EXESUF) | HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF) | ||||||
|  |  | ||||||
| ifdef BUILD_DOCS | ifdef BUILD_DOCS | ||||||
| DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8 | DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8 | ||||||
| DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7 | DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7 | ||||||
| DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7 | DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7 | ||||||
| DOCS+=docs/qemu-block-drivers.7 | DOCS+=docs/qemu-block-drivers.7 | ||||||
| DOCS+=docs/qemu-cpu-models.7 |  | ||||||
| ifdef CONFIG_VIRTFS | ifdef CONFIG_VIRTFS | ||||||
| DOCS+=fsdev/virtfs-proxy-helper.1 | DOCS+=fsdev/virtfs-proxy-helper.1 | ||||||
| endif | endif | ||||||
| ifdef CONFIG_TRACE_SYSTEMTAP |  | ||||||
| DOCS+=scripts/qemu-trace-stap.1 |  | ||||||
| endif |  | ||||||
| else | else | ||||||
| DOCS= | DOCS= | ||||||
| endif | endif | ||||||
|  |  | ||||||
| SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet) BUILD_DIR=$(BUILD_DIR) | 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=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS)) | ||||||
| SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %.d, $(SUBDIR_DEVICES_MAK)) | SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %-config-devices.mak.d, $(TARGET_DIRS)) | ||||||
|  |  | ||||||
| ifeq ($(SUBDIR_DEVICES_MAK),) | ifeq ($(SUBDIR_DEVICES_MAK),) | ||||||
| config-all-devices.mak: | config-all-devices.mak: | ||||||
| @@ -343,26 +378,9 @@ endif | |||||||
|  |  | ||||||
| -include $(SUBDIR_DEVICES_MAK_DEP) | -include $(SUBDIR_DEVICES_MAK_DEP) | ||||||
|  |  | ||||||
| # This has to be kept in sync with Kconfig.host. | %/config-devices.mak: default-configs/%.mak $(SRC_PATH)/scripts/make_device_config.sh | ||||||
| MINIKCONF_ARGS = \ | 	$(call quiet-command, \ | ||||||
|     $(CONFIG_MINIKCONF_MODE) \ |             $(SHELL) $(SRC_PATH)/scripts/make_device_config.sh $< $*-config-devices.mak.d $@ > $@.tmp,"GEN","$@.tmp") | ||||||
|     $@ $*-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") |  | ||||||
| 	$(call quiet-command, if test -f $@; then \ | 	$(call quiet-command, if test -f $@; then \ | ||||||
| 	  if cmp -s $@.old $@; then \ | 	  if cmp -s $@.old $@; then \ | ||||||
| 	    mv $@.tmp $@; \ | 	    mv $@.tmp $@; \ | ||||||
| @@ -390,14 +408,11 @@ endif | |||||||
|  |  | ||||||
| dummy := $(call unnest-vars,, \ | dummy := $(call unnest-vars,, \ | ||||||
|                 stub-obj-y \ |                 stub-obj-y \ | ||||||
|                 authz-obj-y \ |  | ||||||
|                 chardev-obj-y \ |                 chardev-obj-y \ | ||||||
|                 util-obj-y \ |                 util-obj-y \ | ||||||
|                 qga-obj-y \ |                 qga-obj-y \ | ||||||
|                 elf2dmp-obj-y \ |  | ||||||
|                 ivshmem-client-obj-y \ |                 ivshmem-client-obj-y \ | ||||||
|                 ivshmem-server-obj-y \ |                 ivshmem-server-obj-y \ | ||||||
|                 rdmacm-mux-obj-y \ |  | ||||||
|                 libvhost-user-obj-y \ |                 libvhost-user-obj-y \ | ||||||
|                 vhost-user-scsi-obj-y \ |                 vhost-user-scsi-obj-y \ | ||||||
|                 vhost-user-blk-obj-y \ |                 vhost-user-blk-obj-y \ | ||||||
| @@ -418,13 +433,26 @@ dummy := $(call unnest-vars,, \ | |||||||
|  |  | ||||||
| include $(SRC_PATH)/tests/Makefile.include | 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 | qemu-version.h: FORCE | ||||||
| 	$(call quiet-command, \ | 	$(call quiet-command, \ | ||||||
|                 (printf '#define QEMU_PKGVERSION "$(QEMU_PKGVERSION)"\n'; \ | 		(cd $(SRC_PATH); \ | ||||||
| 		printf '#define QEMU_FULL_VERSION "$(FULL_VERSION)"\n'; \ | 		printf '#define QEMU_PKGVERSION '; \ | ||||||
| 		) > $@.tmp) | 		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 \ | 	$(call quiet-command, if ! cmp -s $@ $@.tmp; then \ | ||||||
| 	  mv $@.tmp $@; \ | 	  mv $@.tmp $@; \ | ||||||
| 	 else \ | 	 else \ | ||||||
| @@ -439,7 +467,6 @@ qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool | |||||||
| SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS)) | SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS)) | ||||||
| SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES)) | SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES)) | ||||||
|  |  | ||||||
| $(SOFTMMU_SUBDIR_RULES): $(authz-obj-y) |  | ||||||
| $(SOFTMMU_SUBDIR_RULES): $(block-obj-y) | $(SOFTMMU_SUBDIR_RULES): $(block-obj-y) | ||||||
| $(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y) | $(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y) | ||||||
| $(SOFTMMU_SUBDIR_RULES): $(io-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,) | 	$(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt/libfdt.a,) | ||||||
|  |  | ||||||
| dtc/%: .git-submodule-status | dtc/%: .git-submodule-status | ||||||
| 	@mkdir -p $@ | 	mkdir -p $@ | ||||||
|  |  | ||||||
| # Overriding CFLAGS causes us to lose defines added in the sub-makefile. | # Overriding CFLAGS causes us to lose defines added in the sub-makefile. | ||||||
| # Not overriding CFLAGS leads to mis-matches between compilation modes. | # Not overriding CFLAGS leads to mis-matches between compilation modes. | ||||||
| @@ -473,9 +500,6 @@ CAP_CFLAGS += -DCAPSTONE_HAS_X86 | |||||||
| subdir-capstone: .git-submodule-status | subdir-capstone: .git-submodule-status | ||||||
| 	$(call quiet-command,$(MAKE) -C $(SRC_PATH)/capstone CAPSTONE_SHARED=no BUILDDIR="$(BUILD_DIR)/capstone" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(CAP_CFLAGS)" $(SUBDIR_MAKEFLAGS) $(BUILD_DIR)/capstone/$(LIBCAPSTONE)) | 	$(call quiet-command,$(MAKE) -C $(SRC_PATH)/capstone CAPSTONE_SHARED=no BUILDDIR="$(BUILD_DIR)/capstone" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(CAP_CFLAGS)" $(SUBDIR_MAKEFLAGS) $(BUILD_DIR)/capstone/$(LIBCAPSTONE)) | ||||||
|  |  | ||||||
| subdir-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) \ | $(SUBDIR_RULES): libqemuutil.a $(common-obj-y) $(chardev-obj-y) \ | ||||||
| 	$(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY)) | 	$(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY)) | ||||||
|  |  | ||||||
| @@ -497,7 +521,7 @@ Makefile: $(version-obj-y) | |||||||
| # Build libraries | # Build libraries | ||||||
|  |  | ||||||
| libqemuutil.a: $(util-obj-y) $(trace-obj-y) $(stub-obj-y) | 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.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-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 $(authz-obj-y) $(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 $(authz-obj-y) $(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-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS) | ||||||
|  |  | ||||||
| qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $(COMMON_LDADDS) | qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $(COMMON_LDADDS) | ||||||
|  |  | ||||||
| qemu-edid$(EXESUF): qemu-edid.o hw/display/edid-generate.o $(COMMON_LDADDS) |  | ||||||
|  |  | ||||||
| fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS) | fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS) | ||||||
| fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap | fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap | ||||||
|  |  | ||||||
| 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 | ifdef CONFIG_MPATH | ||||||
| scsi/qemu-pr-helper$(EXESUF): LIBS += -ludev -lmultipath -lmpathpersist | scsi/qemu-pr-helper$(EXESUF): LIBS += -ludev -lmultipath -lmpathpersist | ||||||
| endif | endif | ||||||
| @@ -539,6 +561,7 @@ $(SRC_PATH)/scripts/qapi/types.py \ | |||||||
| $(SRC_PATH)/scripts/qapi/visit.py \ | $(SRC_PATH)/scripts/qapi/visit.py \ | ||||||
| $(SRC_PATH)/scripts/qapi/common.py \ | $(SRC_PATH)/scripts/qapi/common.py \ | ||||||
| $(SRC_PATH)/scripts/qapi/doc.py \ | $(SRC_PATH)/scripts/qapi/doc.py \ | ||||||
|  | $(SRC_PATH)/scripts/ordereddict.py \ | ||||||
| $(SRC_PATH)/scripts/qapi-gen.py | $(SRC_PATH)/scripts/qapi-gen.py | ||||||
|  |  | ||||||
| qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \ | qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \ | ||||||
| @@ -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/qga-qapi-doc.texi: \ | ||||||
| qga/qapi-generated/qapi-gen-timestamp ; | qga/qapi-generated/qapi-gen-timestamp ; | ||||||
| qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py) | qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py) | ||||||
| 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ | 	$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \ | ||||||
| 		-o qga/qapi-generated -p "qga-" $<, \ | 		-o qga/qapi-generated -p "qga-" $<, \ | ||||||
| 		"GEN","$(@:%-timestamp=%)") | 		"GEN","$(@:%-timestamp=%)") | ||||||
| 	@>$@ | 	@>$@ | ||||||
|  |  | ||||||
| qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json \ | qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json $(SRC_PATH)/qapi/common.json \ | ||||||
|                $(QAPI_MODULES:%=$(SRC_PATH)/qapi/%.json) |                $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \ | ||||||
|  |                $(SRC_PATH)/qapi/char.json \ | ||||||
|  |                $(SRC_PATH)/qapi/crypto.json \ | ||||||
|  |                $(SRC_PATH)/qapi/introspect.json \ | ||||||
|  |                $(SRC_PATH)/qapi/migration.json \ | ||||||
|  |                $(SRC_PATH)/qapi/misc.json \ | ||||||
|  |                $(SRC_PATH)/qapi/net.json \ | ||||||
|  |                $(SRC_PATH)/qapi/rocker.json \ | ||||||
|  |                $(SRC_PATH)/qapi/run-state.json \ | ||||||
|  |                $(SRC_PATH)/qapi/sockets.json \ | ||||||
|  |                $(SRC_PATH)/qapi/tpm.json \ | ||||||
|  |                $(SRC_PATH)/qapi/trace.json \ | ||||||
|  |                $(SRC_PATH)/qapi/transaction.json \ | ||||||
|  |                $(SRC_PATH)/qapi/ui.json | ||||||
|  |  | ||||||
| $(GENERATED_QAPI_FILES): qapi-gen-timestamp ; | qapi/qapi-builtin-types.c qapi/qapi-builtin-types.h \ | ||||||
|  | qapi/qapi-types.c qapi/qapi-types.h \ | ||||||
|  | qapi/qapi-types-block-core.c qapi/qapi-types-block-core.h \ | ||||||
|  | qapi/qapi-types-block.c qapi/qapi-types-block.h \ | ||||||
|  | qapi/qapi-types-char.c qapi/qapi-types-char.h \ | ||||||
|  | qapi/qapi-types-common.c qapi/qapi-types-common.h \ | ||||||
|  | qapi/qapi-types-crypto.c qapi/qapi-types-crypto.h \ | ||||||
|  | qapi/qapi-types-introspect.c qapi/qapi-types-introspect.h \ | ||||||
|  | qapi/qapi-types-migration.c qapi/qapi-types-migration.h \ | ||||||
|  | qapi/qapi-types-misc.c qapi/qapi-types-misc.h \ | ||||||
|  | qapi/qapi-types-net.c qapi/qapi-types-net.h \ | ||||||
|  | qapi/qapi-types-rocker.c qapi/qapi-types-rocker.h \ | ||||||
|  | qapi/qapi-types-run-state.c qapi/qapi-types-run-state.h \ | ||||||
|  | qapi/qapi-types-sockets.c qapi/qapi-types-sockets.h \ | ||||||
|  | qapi/qapi-types-tpm.c qapi/qapi-types-tpm.h \ | ||||||
|  | qapi/qapi-types-trace.c qapi/qapi-types-trace.h \ | ||||||
|  | qapi/qapi-types-transaction.c qapi/qapi-types-transaction.h \ | ||||||
|  | qapi/qapi-types-ui.c qapi/qapi-types-ui.h \ | ||||||
|  | qapi/qapi-builtin-visit.c qapi/qapi-builtin-visit.h \ | ||||||
|  | qapi/qapi-visit.c qapi/qapi-visit.h \ | ||||||
|  | qapi/qapi-visit-block-core.c qapi/qapi-visit-block-core.h \ | ||||||
|  | qapi/qapi-visit-block.c qapi/qapi-visit-block.h \ | ||||||
|  | qapi/qapi-visit-char.c qapi/qapi-visit-char.h \ | ||||||
|  | qapi/qapi-visit-common.c qapi/qapi-visit-common.h \ | ||||||
|  | qapi/qapi-visit-crypto.c qapi/qapi-visit-crypto.h \ | ||||||
|  | qapi/qapi-visit-introspect.c qapi/qapi-visit-introspect.h \ | ||||||
|  | qapi/qapi-visit-migration.c qapi/qapi-visit-migration.h \ | ||||||
|  | qapi/qapi-visit-misc.c qapi/qapi-visit-misc.h \ | ||||||
|  | qapi/qapi-visit-net.c qapi/qapi-visit-net.h \ | ||||||
|  | qapi/qapi-visit-rocker.c qapi/qapi-visit-rocker.h \ | ||||||
|  | qapi/qapi-visit-run-state.c qapi/qapi-visit-run-state.h \ | ||||||
|  | qapi/qapi-visit-sockets.c qapi/qapi-visit-sockets.h \ | ||||||
|  | qapi/qapi-visit-tpm.c qapi/qapi-visit-tpm.h \ | ||||||
|  | qapi/qapi-visit-trace.c qapi/qapi-visit-trace.h \ | ||||||
|  | qapi/qapi-visit-transaction.c qapi/qapi-visit-transaction.h \ | ||||||
|  | qapi/qapi-visit-ui.c qapi/qapi-visit-ui.h \ | ||||||
|  | qapi/qapi-commands.h qapi/qapi-commands.c \ | ||||||
|  | qapi/qapi-commands-block-core.c qapi/qapi-commands-block-core.h \ | ||||||
|  | qapi/qapi-commands-block.c qapi/qapi-commands-block.h \ | ||||||
|  | qapi/qapi-commands-char.c qapi/qapi-commands-char.h \ | ||||||
|  | qapi/qapi-commands-common.c qapi/qapi-commands-common.h \ | ||||||
|  | qapi/qapi-commands-crypto.c qapi/qapi-commands-crypto.h \ | ||||||
|  | qapi/qapi-commands-introspect.c qapi/qapi-commands-introspect.h \ | ||||||
|  | qapi/qapi-commands-migration.c qapi/qapi-commands-migration.h \ | ||||||
|  | qapi/qapi-commands-misc.c qapi/qapi-commands-misc.h \ | ||||||
|  | qapi/qapi-commands-net.c qapi/qapi-commands-net.h \ | ||||||
|  | qapi/qapi-commands-rocker.c qapi/qapi-commands-rocker.h \ | ||||||
|  | qapi/qapi-commands-run-state.c qapi/qapi-commands-run-state.h \ | ||||||
|  | qapi/qapi-commands-sockets.c qapi/qapi-commands-sockets.h \ | ||||||
|  | qapi/qapi-commands-tpm.c qapi/qapi-commands-tpm.h \ | ||||||
|  | qapi/qapi-commands-trace.c qapi/qapi-commands-trace.h \ | ||||||
|  | qapi/qapi-commands-transaction.c qapi/qapi-commands-transaction.h \ | ||||||
|  | qapi/qapi-commands-ui.c qapi/qapi-commands-ui.h \ | ||||||
|  | qapi/qapi-events.c qapi/qapi-events.h \ | ||||||
|  | qapi/qapi-events-block-core.c qapi/qapi-events-block-core.h \ | ||||||
|  | qapi/qapi-events-block.c qapi/qapi-events-block.h \ | ||||||
|  | qapi/qapi-events-char.c qapi/qapi-events-char.h \ | ||||||
|  | qapi/qapi-events-common.c qapi/qapi-events-common.h \ | ||||||
|  | qapi/qapi-events-crypto.c qapi/qapi-events-crypto.h \ | ||||||
|  | qapi/qapi-events-introspect.c qapi/qapi-events-introspect.h \ | ||||||
|  | qapi/qapi-events-migration.c qapi/qapi-events-migration.h \ | ||||||
|  | qapi/qapi-events-misc.c qapi/qapi-events-misc.h \ | ||||||
|  | qapi/qapi-events-net.c qapi/qapi-events-net.h \ | ||||||
|  | qapi/qapi-events-rocker.c qapi/qapi-events-rocker.h \ | ||||||
|  | qapi/qapi-events-run-state.c qapi/qapi-events-run-state.h \ | ||||||
|  | qapi/qapi-events-sockets.c qapi/qapi-events-sockets.h \ | ||||||
|  | qapi/qapi-events-tpm.c qapi/qapi-events-tpm.h \ | ||||||
|  | qapi/qapi-events-trace.c qapi/qapi-events-trace.h \ | ||||||
|  | qapi/qapi-events-transaction.c qapi/qapi-events-transaction.h \ | ||||||
|  | qapi/qapi-events-ui.c qapi/qapi-events-ui.h \ | ||||||
|  | qapi/qapi-introspect.h qapi/qapi-introspect.c \ | ||||||
|  | qapi/qapi-doc.texi: \ | ||||||
|  | qapi-gen-timestamp ; | ||||||
| qapi-gen-timestamp: $(qapi-modules) $(qapi-py) | qapi-gen-timestamp: $(qapi-modules) $(qapi-py) | ||||||
| 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ | 	$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \ | ||||||
| 		-o "qapi" -b $<, \ | 		-o "qapi" -b $<, \ | ||||||
| 		"GEN","$(@:%-timestamp=%)") | 		"GEN","$(@:%-timestamp=%)") | ||||||
| 	@>$@ | 	@>$@ | ||||||
| @@ -590,10 +698,6 @@ ifneq ($(EXESUF),) | |||||||
| qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI) | qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI) | ||||||
| endif | endif | ||||||
|  |  | ||||||
| elf2dmp$(EXESUF): LIBS += $(CURL_LIBS) |  | ||||||
| elf2dmp$(EXESUF): $(elf2dmp-obj-y) |  | ||||||
| 	$(call LINK, $^) |  | ||||||
|  |  | ||||||
| ifdef CONFIG_IVSHMEM | ifdef CONFIG_IVSHMEM | ||||||
| ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS) | ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS) | ||||||
| 	$(call LINK, $^) | 	$(call LINK, $^) | ||||||
| @@ -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 | vhost-user-blk$(EXESUF): $(vhost-user-blk-obj-y) libvhost-user.a | ||||||
| 	$(call LINK, $^) | 	$(call LINK, $^) | ||||||
|  |  | ||||||
| rdmacm-mux$(EXESUF): LIBS += "-libumad" |  | ||||||
| rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS) |  | ||||||
| 	$(call LINK, $^) |  | ||||||
|  |  | ||||||
| module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak | module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak | ||||||
| 	$(call quiet-command,$(PYTHON) $< $@ \ | 	$(call quiet-command,$(PYTHON) $< $@ \ | ||||||
| 	$(addprefix $(SRC_PATH)/,$(patsubst %.mo,%.c,$(block-obj-m))), \ | 	$(addprefix $(SRC_PATH)/,$(patsubst %.mo,%.c,$(block-obj-m))), \ | ||||||
| 	"GEN","$@") | 	"GEN","$@") | ||||||
|  |  | ||||||
| ifdef CONFIG_GCOV |  | ||||||
| .PHONY: clean-coverage |  | ||||||
| clean-coverage: |  | ||||||
| 	$(call quiet-command, \ |  | ||||||
| 		find . \( -name '*.gcda' -o -name '*.gcov' \) -type f -exec rm {} +, \ |  | ||||||
| 		"CLEAN", "coverage files") |  | ||||||
| endif |  | ||||||
|  |  | ||||||
| clean: | clean: | ||||||
| # avoid old build problems by removing potentially incorrect old files | # avoid old build problems by removing potentially incorrect old files | ||||||
| 	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h | 	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h | ||||||
| 	rm -f qemu-options.def | 	rm -f qemu-options.def | ||||||
| 	rm -f *.msi | 	rm -f *.msi | ||||||
| 	find . \( -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f \ | 	find . \( -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f -exec rm {} + | ||||||
| 		! -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 {} + |  | ||||||
| 	rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~ | 	rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~ | ||||||
| 	rm -f fsdev/*.pod scsi/*.pod | 	rm -f fsdev/*.pod scsi/*.pod | ||||||
| 	rm -f qemu-img-cmds.h | 	rm -f qemu-img-cmds.h | ||||||
| @@ -646,7 +734,7 @@ clean: | |||||||
| 	if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \ | 	if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \ | ||||||
| 	rm -f $$d/qemu-options.def; \ | 	rm -f $$d/qemu-options.def; \ | ||||||
|         done |         done | ||||||
| 	rm -f config-all-devices.mak | 	rm -f $(SUBDIR_DEVICES_MAK) config-all-devices.mak | ||||||
|  |  | ||||||
| VERSION ?= $(shell cat VERSION) | VERSION ?= $(shell cat VERSION) | ||||||
|  |  | ||||||
| @@ -655,26 +743,9 @@ dist: qemu-$(VERSION).tar.bz2 | |||||||
| qemu-%.tar.bz2: | qemu-%.tar.bz2: | ||||||
| 	$(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst 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 | distclean: clean | ||||||
| 	rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi | 	rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi | ||||||
| 	rm -f config-all-devices.mak config-all-disas.mak config.status | 	rm -f config-all-devices.mak config-all-disas.mak config.status | ||||||
| 	rm -f $(SUBDIR_DEVICES_MAK) |  | ||||||
| 	rm -f po/*.mo tests/qemu-iotests/common.env | 	rm -f po/*.mo tests/qemu-iotests/common.env | ||||||
| 	rm -f roms/seabios/config.mak roms/vgabios/config.mak | 	rm -f roms/seabios/config.mak roms/vgabios/config.mak | ||||||
| 	rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps | 	rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps | ||||||
| @@ -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.pdf docs/interop/qemu-ga-ref.pdf | ||||||
| 	rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html | 	rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html | ||||||
| 	rm -f docs/qemu-block-drivers.7 | 	rm -f docs/qemu-block-drivers.7 | ||||||
| 	rm -f docs/qemu-cpu-models.7 |  | ||||||
| 	rm -rf .doctrees |  | ||||||
| 	$(call clean-manual,devel) |  | ||||||
| 	$(call clean-manual,interop) |  | ||||||
| 	for d in $(TARGET_DIRS); do \ | 	for d in $(TARGET_DIRS); do \ | ||||||
| 	rm -rf $$d || exit 1 ; \ | 	rm -rf $$d || exit 1 ; \ | ||||||
|         done |         done | ||||||
| 	rm -Rf .sdk | 	rm -Rf .sdk | ||||||
| 	if test -f dtc/version_gen.h; then $(MAKE) $(DTC_MAKE_ARGS) clean; fi | 	if test -f dtc/version_gen.h; then $(MAKE) $(DTC_MAKE_ARGS) clean; fi | ||||||
|  |  | ||||||
| KEYMAPS=da     en-gb  et  fr     fr-ch  is  lt  no  pt-br  sv \ | KEYMAPS=da     en-gb  et  fr     fr-ch  is  lt  modifiers  no  pt-br  sv \ | ||||||
| ar      de     en-us  fi  fr-be  hr     it  lv  nl         pl  ru     th \ | ar      de     en-us  fi  fr-be  hr     it  lv  nl         pl  ru     th \ | ||||||
| de-ch  es     fo  fr-ca  hu     ja  mk  pt  sl     tr \ | common  de-ch  es     fo  fr-ca  hu     ja  mk  nl-be      pt  sl     tr \ | ||||||
| bepo    cz | bepo    cz | ||||||
|  |  | ||||||
| ifdef INSTALL_BLOBS | ifdef INSTALL_BLOBS | ||||||
| BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \ | BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \ | ||||||
| vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \ | vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \ | ||||||
| vgabios-ramfb.bin vgabios-bochs-display.bin \ | acpi-dsdt.aml \ | ||||||
| ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \ | ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \ | ||||||
| pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \ | pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \ | ||||||
| pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \ | pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \ | ||||||
| efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \ | efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \ | ||||||
| efi-pcnet.rom efi-rtl8139.rom efi-virtio.rom \ | efi-pcnet.rom efi-rtl8139.rom efi-virtio.rom \ | ||||||
| efi-e1000e.rom efi-vmxnet3.rom \ | efi-e1000e.rom efi-vmxnet3.rom \ | ||||||
|  | qemu-icon.bmp qemu_logo_no_text.svg \ | ||||||
| bamboo.dtb canyonlands.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ | bamboo.dtb canyonlands.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ | ||||||
| multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin pvh.bin \ | multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin \ | ||||||
| s390-ccw.img s390-netboot.img \ | s390-ccw.img s390-netboot.img \ | ||||||
| spapr-rtas.bin slof.bin skiboot.lid \ | spapr-rtas.bin slof.bin skiboot.lid \ | ||||||
| palcode-clipper \ | palcode-clipper \ | ||||||
| @@ -727,20 +795,7 @@ else | |||||||
| BLOBS= | BLOBS= | ||||||
| endif | endif | ||||||
|  |  | ||||||
| # Note that we manually filter-out the non-Sphinx documentation which | install-doc: $(DOCS) | ||||||
| # 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_DIR) "$(DESTDIR)$(qemu_docdir)" | 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)" | ||||||
| 	$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)" | 	$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)" | ||||||
| 	$(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)" | 	$(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)" | ||||||
| @@ -752,15 +807,11 @@ ifdef CONFIG_POSIX | |||||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7" | 	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7" | ||||||
| 	$(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7" | 	$(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7" | ||||||
| 	$(INSTALL_DATA) docs/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7" | 	$(INSTALL_DATA) docs/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7" | ||||||
| 	$(INSTALL_DATA) docs/qemu-cpu-models.7 "$(DESTDIR)$(mandir)/man7" |  | ||||||
| ifneq ($(TOOLS),) | ifneq ($(TOOLS),) | ||||||
| 	$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1" | 	$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1" | ||||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8" | 	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8" | ||||||
| 	$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8" | 	$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8" | ||||||
| endif | endif | ||||||
| ifdef CONFIG_TRACE_SYSTEMTAP |  | ||||||
| 	$(INSTALL_DATA) scripts/qemu-trace-stap.1 "$(DESTDIR)$(mandir)/man1" |  | ||||||
| endif |  | ||||||
| ifneq (,$(findstring qemu-ga,$(TOOLS))) | ifneq (,$(findstring qemu-ga,$(TOOLS))) | ||||||
| 	$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8" | 	$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8" | ||||||
| 	$(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)" | 	$(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)" | ||||||
| @@ -783,7 +834,6 @@ ifneq (,$(findstring qemu-ga,$(TOOLS))) | |||||||
| endif | endif | ||||||
| endif | endif | ||||||
|  |  | ||||||
| ICON_SIZES=16x16 24x24 32x32 48x48 64x64 128x128 256x256 512x512 |  | ||||||
|  |  | ||||||
| install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir | install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir | ||||||
| ifneq ($(TOOLS),) | ifneq ($(TOOLS),) | ||||||
| @@ -800,29 +850,12 @@ endif | |||||||
| ifneq ($(HELPERS-y),) | ifneq ($(HELPERS-y),) | ||||||
| 	$(call install-prog,$(HELPERS-y),$(DESTDIR)$(libexecdir)) | 	$(call install-prog,$(HELPERS-y),$(DESTDIR)$(libexecdir)) | ||||||
| endif | endif | ||||||
| ifdef CONFIG_TRACE_SYSTEMTAP |  | ||||||
| 	$(INSTALL_PROG) "scripts/qemu-trace-stap" $(DESTDIR)$(bindir) |  | ||||||
| endif |  | ||||||
| ifneq ($(BLOBS),) | ifneq ($(BLOBS),) | ||||||
| 	set -e; for x in $(BLOBS); do \ | 	set -e; for x in $(BLOBS); do \ | ||||||
| 		$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \ | 		$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \ | ||||||
| 	done | 	done | ||||||
| endif | endif | ||||||
| 	for s in $(ICON_SIZES); do \ | ifeq ($(CONFIG_GTK),m) | ||||||
| 		mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/$${s}/apps"; \ |  | ||||||
| 		$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu_$${s}.png \ |  | ||||||
| 			"$(DESTDIR)/$(qemu_icondir)/hicolor/$${s}/apps/qemu.png"; \ |  | ||||||
| 	done; \ |  | ||||||
| 	mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/32x32/apps"; \ |  | ||||||
| 	$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu_32x32.bmp \ |  | ||||||
| 		"$(DESTDIR)/$(qemu_icondir)/hicolor/32x32/apps/qemu.bmp"; \ |  | ||||||
| 	mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/scalable/apps"; \ |  | ||||||
| 	$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu.svg \ |  | ||||||
| 		"$(DESTDIR)/$(qemu_icondir)/hicolor/scalable/apps/qemu.svg" |  | ||||||
| 	mkdir -p "$(DESTDIR)/$(qemu_desktopdir)" |  | ||||||
| 	$(INSTALL_DATA) $(SRC_PATH)/ui/qemu.desktop \ |  | ||||||
| 		"$(DESTDIR)/$(qemu_desktopdir)/qemu.desktop" |  | ||||||
| ifdef CONFIG_GTK |  | ||||||
| 	$(MAKE) -C po $@ | 	$(MAKE) -C po $@ | ||||||
| endif | endif | ||||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps" | 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps" | ||||||
| @@ -891,23 +924,6 @@ docs/version.texi: $(SRC_PATH)/VERSION | |||||||
| %.pdf: %.texi docs/version.texi | %.pdf: %.texi docs/version.texi | ||||||
| 	$(call quiet-command,texi2pdf $(TEXI2PDFFLAGS) $< -o $@,"GEN","$@") | 	$(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 | qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool | ||||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@") | 	$(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-nbd.8: qemu-nbd.texi qemu-option-trace.texi | ||||||
| qemu-ga.8: qemu-ga.texi | qemu-ga.8: qemu-ga.texi | ||||||
| docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi | docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi | ||||||
| docs/qemu-cpu-models.7: docs/qemu-cpu-models.texi |  | ||||||
| scripts/qemu-trace-stap.1: scripts/qemu-trace-stap.texi |  | ||||||
|  |  | ||||||
| html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html 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 | 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 | 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 | txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt | ||||||
|  |  | ||||||
| qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \ | qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \ | ||||||
| 	qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \ | 	qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \ | ||||||
| 	qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \ | 	qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \ | ||||||
| 	qemu-monitor-info.texi docs/qemu-block-drivers.texi \ | 	qemu-monitor-info.texi docs/qemu-block-drivers.texi | ||||||
| 	docs/qemu-cpu-models.texi |  | ||||||
|  |  | ||||||
| docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \ | docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \ | ||||||
|     docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \ |     docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \ | ||||||
| @@ -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.txt docs/interop/qemu-qmp-ref.7: \ | ||||||
| 	docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi | 	docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi | ||||||
|  |  | ||||||
| $(filter %.1 %.7 %.8,$(DOCS)): scripts/texi2pod.pl |  | ||||||
|  |  | ||||||
| # Reports/Analysis |  | ||||||
|  |  | ||||||
| %/coverage-report.html: |  | ||||||
| 	@mkdir -p $* |  | ||||||
| 	$(call quiet-command,\ |  | ||||||
| 		gcovr -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 | ifdef CONFIG_WIN32 | ||||||
|  |  | ||||||
| @@ -1050,9 +1050,6 @@ include $(SRC_PATH)/tests/vm/Makefile.include | |||||||
| help: | help: | ||||||
| 	@echo  'Generic targets:' | 	@echo  'Generic targets:' | ||||||
| 	@echo  '  all             - Build all' | 	@echo  '  all             - Build all' | ||||||
| ifdef CONFIG_MODULES |  | ||||||
| 	@echo  '  modules         - Build all modules' |  | ||||||
| endif |  | ||||||
| 	@echo  '  dir/file.o      - Build specified target only' | 	@echo  '  dir/file.o      - Build specified target only' | ||||||
| 	@echo  '  install         - Install QEMU, documentation and tools' | 	@echo  '  install         - Install QEMU, documentation and tools' | ||||||
| 	@echo  '  ctags/TAGS      - Generate tags file for editors' | 	@echo  '  ctags/TAGS      - Generate tags file for editors' | ||||||
| @@ -1065,9 +1062,6 @@ endif | |||||||
| 		echo '') | 		echo '') | ||||||
| 	@echo  'Cleaning targets:' | 	@echo  'Cleaning targets:' | ||||||
| 	@echo  '  clean           - Remove most generated files but keep the config' | 	@echo  '  clean           - Remove most generated files but keep the config' | ||||||
| ifdef CONFIG_GCOV |  | ||||||
| 	@echo  '  clean-coverage  - Remove coverage files' |  | ||||||
| endif |  | ||||||
| 	@echo  '  distclean       - Remove all generated files' | 	@echo  '  distclean       - Remove all generated files' | ||||||
| 	@echo  '  dist            - Build a distributable tarball' | 	@echo  '  dist            - Build a distributable tarball' | ||||||
| 	@echo  '' | 	@echo  '' | ||||||
| @@ -1079,9 +1073,6 @@ endif | |||||||
| 	@echo  'Documentation targets:' | 	@echo  'Documentation targets:' | ||||||
| 	@echo  '  html info pdf txt' | 	@echo  '  html info pdf txt' | ||||||
| 	@echo  '                  - Build documentation in specified format' | 	@echo  '                  - Build documentation in specified format' | ||||||
| ifdef CONFIG_GCOV |  | ||||||
| 	@echo  '  coverage-report - Create code coverage report' |  | ||||||
| endif |  | ||||||
| 	@echo  '' | 	@echo  '' | ||||||
| ifdef CONFIG_WIN32 | ifdef CONFIG_WIN32 | ||||||
| 	@echo  'Windows targets:' | 	@echo  'Windows targets:' | ||||||
|   | |||||||
							
								
								
									
										178
									
								
								Makefile.objs
									
									
									
									
									
								
							
							
						
						
									
										178
									
								
								Makefile.objs
									
									
									
									
									
								
							| @@ -1,20 +1,69 @@ | |||||||
| ####################################################################### | ####################################################################### | ||||||
| # Common libraries for tools and emulators | # 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 = 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/ | 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 is code used by both qemu system emulation and qemu-img | ||||||
|  |  | ||||||
| block-obj-y = nbd/ | block-obj-y += nbd/ | ||||||
| block-obj-y += block.o blockjob.o job.o | block-obj-y += block.o blockjob.o | ||||||
| block-obj-y += block/ scsi/ | block-obj-y += block/ scsi/ | ||||||
| block-obj-y += qemu-io-cmds.o | block-obj-y += qemu-io-cmds.o | ||||||
| block-obj-$(CONFIG_REPLICATION) += replication.o | block-obj-$(CONFIG_REPLICATION) += replication.o | ||||||
| @@ -45,7 +94,6 @@ io-obj-y = io/ | |||||||
| ifeq ($(CONFIG_SOFTMMU),y) | ifeq ($(CONFIG_SOFTMMU),y) | ||||||
| common-obj-y = blockdev.o blockdev-nbd.o block/ | common-obj-y = blockdev.o blockdev-nbd.o block/ | ||||||
| common-obj-y += bootdevice.o iothread.o | common-obj-y += bootdevice.o iothread.o | ||||||
| common-obj-y += job-qmp.o |  | ||||||
| common-obj-y += net/ | common-obj-y += net/ | ||||||
| common-obj-y += qdev-monitor.o device-hotplug.o | common-obj-y += qdev-monitor.o device-hotplug.o | ||||||
| common-obj-$(CONFIG_WIN32) += os-win32.o | common-obj-$(CONFIG_WIN32) += os-win32.o | ||||||
| @@ -71,6 +119,8 @@ common-obj-y += vl.o | |||||||
| vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS) | vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS) | ||||||
| common-obj-$(CONFIG_TPM) += tpm.o | common-obj-$(CONFIG_TPM) += tpm.o | ||||||
|  |  | ||||||
|  | common-obj-$(CONFIG_SLIRP) += slirp/ | ||||||
|  |  | ||||||
| common-obj-y += backends/ | common-obj-y += backends/ | ||||||
| common-obj-y += chardev/ | common-obj-y += chardev/ | ||||||
|  |  | ||||||
| @@ -83,8 +133,25 @@ common-obj-$(CONFIG_FDT) += device_tree.o | |||||||
| ###################################################################### | ###################################################################### | ||||||
| # qapi | # 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 += qmp.o hmp.o | ||||||
| common-obj-y += qapi/ |  | ||||||
| endif | endif | ||||||
|  |  | ||||||
| ####################################################################### | ####################################################################### | ||||||
| @@ -101,6 +168,7 @@ version-obj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.o | |||||||
| ###################################################################### | ###################################################################### | ||||||
| # tracing | # tracing | ||||||
| util-obj-y +=  trace/ | util-obj-y +=  trace/ | ||||||
|  | target-obj-y += trace/ | ||||||
|  |  | ||||||
| ###################################################################### | ###################################################################### | ||||||
| # guest agent | # guest agent | ||||||
| @@ -113,7 +181,6 @@ qga-vss-dll-obj-y = qga/ | |||||||
|  |  | ||||||
| ###################################################################### | ###################################################################### | ||||||
| # contrib | # contrib | ||||||
| elf2dmp-obj-y = contrib/elf2dmp/ |  | ||||||
| ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/ | ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/ | ||||||
| ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/ | ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/ | ||||||
| libvhost-user-obj-y = contrib/libvhost-user/ | libvhost-user-obj-y = contrib/libvhost-user/ | ||||||
| @@ -121,76 +188,69 @@ vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS) | |||||||
| vhost-user-scsi.o-libs := $(LIBISCSI_LIBS) | vhost-user-scsi.o-libs := $(LIBISCSI_LIBS) | ||||||
| vhost-user-scsi-obj-y = contrib/vhost-user-scsi/ | vhost-user-scsi-obj-y = contrib/vhost-user-scsi/ | ||||||
| vhost-user-blk-obj-y = contrib/vhost-user-blk/ | vhost-user-blk-obj-y = contrib/vhost-user-blk/ | ||||||
| rdmacm-mux-obj-y = contrib/rdmacm-mux/ |  | ||||||
|  |  | ||||||
| ###################################################################### | ###################################################################### | ||||||
| trace-events-subdirs = | trace-events-subdirs = | ||||||
| trace-events-subdirs += accel/kvm | trace-events-subdirs += util | ||||||
| trace-events-subdirs += accel/tcg | trace-events-subdirs += crypto | ||||||
| trace-events-subdirs += audio | trace-events-subdirs += io | ||||||
| trace-events-subdirs += authz | trace-events-subdirs += migration | ||||||
| trace-events-subdirs += block | trace-events-subdirs += block | ||||||
| trace-events-subdirs += chardev | trace-events-subdirs += chardev | ||||||
| trace-events-subdirs += crypto |  | ||||||
| trace-events-subdirs += hw/9pfs |  | ||||||
| trace-events-subdirs += hw/acpi |  | ||||||
| trace-events-subdirs += hw/alpha |  | ||||||
| trace-events-subdirs += hw/arm |  | ||||||
| trace-events-subdirs += hw/audio |  | ||||||
| trace-events-subdirs += hw/block | trace-events-subdirs += hw/block | ||||||
| trace-events-subdirs += hw/block/dataplane | trace-events-subdirs += hw/block/dataplane | ||||||
| trace-events-subdirs += hw/char | trace-events-subdirs += hw/char | ||||||
| trace-events-subdirs += hw/display |  | ||||||
| trace-events-subdirs += hw/dma |  | ||||||
| trace-events-subdirs += hw/hppa |  | ||||||
| trace-events-subdirs += hw/i2c |  | ||||||
| trace-events-subdirs += hw/i386 |  | ||||||
| trace-events-subdirs += hw/i386/xen |  | ||||||
| trace-events-subdirs += hw/ide |  | ||||||
| trace-events-subdirs += hw/input |  | ||||||
| trace-events-subdirs += hw/intc | trace-events-subdirs += hw/intc | ||||||
| trace-events-subdirs += hw/isa |  | ||||||
| trace-events-subdirs += hw/mem |  | ||||||
| trace-events-subdirs += hw/misc |  | ||||||
| trace-events-subdirs += hw/misc/macio |  | ||||||
| trace-events-subdirs += hw/net | trace-events-subdirs += hw/net | ||||||
| trace-events-subdirs += hw/nvram |  | ||||||
| trace-events-subdirs += hw/pci |  | ||||||
| trace-events-subdirs += hw/pci-host |  | ||||||
| trace-events-subdirs += hw/ppc |  | ||||||
| trace-events-subdirs += hw/rdma | trace-events-subdirs += hw/rdma | ||||||
| trace-events-subdirs += hw/rdma/vmw | trace-events-subdirs += hw/rdma/vmw | ||||||
| trace-events-subdirs += hw/s390x | trace-events-subdirs += hw/virtio | ||||||
|  | trace-events-subdirs += hw/audio | ||||||
|  | trace-events-subdirs += hw/misc | ||||||
|  | trace-events-subdirs += hw/misc/macio | ||||||
|  | trace-events-subdirs += hw/usb | ||||||
| trace-events-subdirs += hw/scsi | trace-events-subdirs += hw/scsi | ||||||
| trace-events-subdirs += hw/sd | trace-events-subdirs += hw/nvram | ||||||
|  | trace-events-subdirs += hw/display | ||||||
|  | trace-events-subdirs += hw/input | ||||||
|  | trace-events-subdirs += hw/timer | ||||||
|  | trace-events-subdirs += hw/dma | ||||||
| trace-events-subdirs += hw/sparc | trace-events-subdirs += hw/sparc | ||||||
| trace-events-subdirs += hw/sparc64 | trace-events-subdirs += hw/sparc64 | ||||||
| trace-events-subdirs += hw/timer | trace-events-subdirs += hw/sd | ||||||
| trace-events-subdirs += hw/tpm | trace-events-subdirs += hw/isa | ||||||
| trace-events-subdirs += hw/usb | trace-events-subdirs += hw/mem | ||||||
|  | trace-events-subdirs += hw/i386 | ||||||
|  | trace-events-subdirs += hw/i386/xen | ||||||
|  | trace-events-subdirs += hw/9pfs | ||||||
|  | trace-events-subdirs += hw/ppc | ||||||
|  | trace-events-subdirs += hw/pci | ||||||
|  | trace-events-subdirs += hw/pci-host | ||||||
|  | trace-events-subdirs += hw/s390x | ||||||
| trace-events-subdirs += hw/vfio | trace-events-subdirs += hw/vfio | ||||||
| trace-events-subdirs += hw/virtio | trace-events-subdirs += hw/acpi | ||||||
| trace-events-subdirs += hw/watchdog | trace-events-subdirs += hw/arm | ||||||
|  | trace-events-subdirs += hw/alpha | ||||||
|  | trace-events-subdirs += hw/hppa | ||||||
| trace-events-subdirs += hw/xen | trace-events-subdirs += hw/xen | ||||||
| trace-events-subdirs += hw/gpio | trace-events-subdirs += hw/ide | ||||||
| trace-events-subdirs += io | trace-events-subdirs += hw/tpm | ||||||
| trace-events-subdirs += linux-user | trace-events-subdirs += ui | ||||||
| trace-events-subdirs += migration | trace-events-subdirs += audio | ||||||
| trace-events-subdirs += nbd |  | ||||||
| trace-events-subdirs += net | trace-events-subdirs += net | ||||||
| trace-events-subdirs += qapi |  | ||||||
| trace-events-subdirs += qom |  | ||||||
| trace-events-subdirs += scsi |  | ||||||
| trace-events-subdirs += target/arm | trace-events-subdirs += target/arm | ||||||
| trace-events-subdirs += target/hppa |  | ||||||
| trace-events-subdirs += target/i386 | trace-events-subdirs += target/i386 | ||||||
| trace-events-subdirs += target/mips | trace-events-subdirs += target/mips | ||||||
| trace-events-subdirs += target/ppc |  | ||||||
| trace-events-subdirs += target/riscv |  | ||||||
| trace-events-subdirs += target/s390x |  | ||||||
| trace-events-subdirs += target/sparc | trace-events-subdirs += target/sparc | ||||||
| trace-events-subdirs += ui | trace-events-subdirs += target/s390x | ||||||
| trace-events-subdirs += util | trace-events-subdirs += target/ppc | ||||||
|  | trace-events-subdirs += qom | ||||||
|  | trace-events-subdirs += linux-user | ||||||
|  | trace-events-subdirs += qapi | ||||||
|  | trace-events-subdirs += accel/tcg | ||||||
|  | trace-events-subdirs += accel/kvm | ||||||
|  | trace-events-subdirs += nbd | ||||||
|  | trace-events-subdirs += scsi | ||||||
|  |  | ||||||
| trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events) | trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,19 +4,16 @@ BUILD_DIR?=$(CURDIR)/.. | |||||||
|  |  | ||||||
| include ../config-host.mak | include ../config-host.mak | ||||||
| include config-target.mak | include config-target.mak | ||||||
| include $(SRC_PATH)/rules.mak |  | ||||||
|  |  | ||||||
| ifdef CONFIG_SOFTMMU |  | ||||||
| include config-devices.mak | include config-devices.mak | ||||||
| endif | include $(SRC_PATH)/rules.mak | ||||||
|  |  | ||||||
| $(call set-vpath, $(SRC_PATH):$(BUILD_DIR)) | $(call set-vpath, $(SRC_PATH):$(BUILD_DIR)) | ||||||
| ifdef CONFIG_LINUX | ifdef CONFIG_LINUX | ||||||
| QEMU_CFLAGS += -I../linux-headers | QEMU_CFLAGS += -I../linux-headers | ||||||
| endif | endif | ||||||
| QEMU_CFLAGS += -iquote .. -iquote $(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H | QEMU_CFLAGS += -I.. -I$(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H | ||||||
|  |  | ||||||
| QEMU_CFLAGS+=-iquote $(SRC_PATH)/include | QEMU_CFLAGS+=-I$(SRC_PATH)/include | ||||||
|  |  | ||||||
| ifdef CONFIG_USER_ONLY | ifdef CONFIG_USER_ONLY | ||||||
| # user emulator name | # user emulator name | ||||||
| @@ -39,14 +36,11 @@ endif | |||||||
| PROGS=$(QEMU_PROG) $(QEMU_PROGW) | PROGS=$(QEMU_PROG) $(QEMU_PROGW) | ||||||
| STPFILES= | STPFILES= | ||||||
|  |  | ||||||
| # Makefile Tests |  | ||||||
| include $(SRC_PATH)/tests/tcg/Makefile.include |  | ||||||
|  |  | ||||||
| config-target.h: config-target.h-timestamp | config-target.h: config-target.h-timestamp | ||||||
| config-target.h-timestamp: config-target.mak | config-target.h-timestamp: config-target.mak | ||||||
|  |  | ||||||
| ifdef CONFIG_TRACE_SYSTEMTAP | ifdef CONFIG_TRACE_SYSTEMTAP | ||||||
| stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp $(QEMU_PROG)-log.stp | stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp | ||||||
|  |  | ||||||
| ifdef CONFIG_USER_ONLY | ifdef CONFIG_USER_ONLY | ||||||
| TARGET_TYPE=user | TARGET_TYPE=user | ||||||
| @@ -85,14 +79,6 @@ $(QEMU_PROG)-simpletrace.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y) | |||||||
| 		--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \ | 		--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \ | ||||||
| 		$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp") | 		$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp") | ||||||
|  |  | ||||||
| $(QEMU_PROG)-log.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y) |  | ||||||
| 	$(call quiet-command,$(TRACETOOL) \ |  | ||||||
| 		--group=all \ |  | ||||||
| 		--format=log-stap \ |  | ||||||
| 		--backends=$(TRACE_BACKENDS) \ |  | ||||||
| 		--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \ |  | ||||||
| 		$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-log.stp") |  | ||||||
|  |  | ||||||
| else | else | ||||||
| stap: | stap: | ||||||
| endif | endif | ||||||
| @@ -103,8 +89,6 @@ all: $(PROGS) stap | |||||||
| # Dummy command so that make thinks it has done something | # Dummy command so that make thinks it has done something | ||||||
| 	@true | 	@true | ||||||
|  |  | ||||||
| obj-y += trace/ |  | ||||||
|  |  | ||||||
| ######################################################### | ######################################################### | ||||||
| # cpu emulator library | # cpu emulator library | ||||||
| obj-y += exec.o | 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) += tcg/tcg-common.o tcg/optimize.o | ||||||
| obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o | obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o | ||||||
| obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o | obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o | ||||||
| obj-$(CONFIG_TCG) += fpu/softfloat.o | obj-y += fpu/softfloat.o | ||||||
| obj-y += target/$(TARGET_BASE_ARCH)/ | obj-y += target/$(TARGET_BASE_ARCH)/ | ||||||
| obj-y += disas.o | obj-y += disas.o | ||||||
| obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o | obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o | ||||||
| @@ -151,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 += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o | ||||||
| obj-y += qtest.o | obj-y += qtest.o | ||||||
| obj-y += hw/ | obj-y += hw/ | ||||||
| obj-y += qapi/ |  | ||||||
| obj-y += memory.o | obj-y += memory.o | ||||||
| obj-y += memory_mapping.o | obj-y += memory_mapping.o | ||||||
| obj-y += dump.o | obj-y += dump.o | ||||||
| obj-$(TARGET_X86_64) += win_dump.o |  | ||||||
| obj-y += migration/ram.o | obj-y += migration/ram.o | ||||||
| LIBS := $(libs_softmmu) $(LIBS) | LIBS := $(libs_softmmu) $(LIBS) | ||||||
|  |  | ||||||
| @@ -170,12 +152,20 @@ GENERATED_FILES += hmp-commands.h hmp-commands-info.h | |||||||
|  |  | ||||||
| endif # CONFIG_SOFTMMU | endif # CONFIG_SOFTMMU | ||||||
|  |  | ||||||
|  | # Workaround for http://gcc.gnu.org/PR55489, see configure. | ||||||
|  | %/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS) | ||||||
|  |  | ||||||
| dummy := $(call unnest-vars,,obj-y) | dummy := $(call unnest-vars,,obj-y) | ||||||
| all-obj-y := $(obj-y) | all-obj-y := $(obj-y) | ||||||
|  |  | ||||||
|  | target-obj-y := | ||||||
|  | block-obj-y := | ||||||
|  | common-obj-y := | ||||||
|  | chardev-obj-y := | ||||||
| include $(SRC_PATH)/Makefile.objs | include $(SRC_PATH)/Makefile.objs | ||||||
|  | dummy := $(call unnest-vars,,target-obj-y) | ||||||
|  | target-obj-y-save := $(target-obj-y) | ||||||
| dummy := $(call unnest-vars,.., \ | dummy := $(call unnest-vars,.., \ | ||||||
|                authz-obj-y \ |  | ||||||
|                block-obj-y \ |                block-obj-y \ | ||||||
|                block-obj-m \ |                block-obj-m \ | ||||||
|                chardev-obj-y \ |                chardev-obj-y \ | ||||||
| @@ -185,17 +175,16 @@ dummy := $(call unnest-vars,.., \ | |||||||
|                io-obj-y \ |                io-obj-y \ | ||||||
|                common-obj-y \ |                common-obj-y \ | ||||||
|                common-obj-m) |                common-obj-m) | ||||||
|  | target-obj-y := $(target-obj-y-save) | ||||||
| all-obj-y += $(common-obj-y) | all-obj-y += $(common-obj-y) | ||||||
|  | all-obj-y += $(target-obj-y) | ||||||
| all-obj-y += $(qom-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_SOFTMMU) += $(block-obj-y) $(chardev-obj-y) | ||||||
| all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y) | all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y) | ||||||
| all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y) | all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y) | ||||||
| all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y) | all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y) | ||||||
|  |  | ||||||
| ifdef CONFIG_SOFTMMU |  | ||||||
| $(QEMU_PROG_BUILD): config-devices.mak | $(QEMU_PROG_BUILD): config-devices.mak | ||||||
| endif |  | ||||||
|  |  | ||||||
| COMMON_LDADDS = ../libqemuutil.a | COMMON_LDADDS = ../libqemuutil.a | ||||||
|  |  | ||||||
| @@ -220,7 +209,6 @@ clean: clean-target | |||||||
| 	rm -f *.a *~ $(PROGS) | 	rm -f *.a *~ $(PROGS) | ||||||
| 	rm -f $(shell find . -name '*.[od]') | 	rm -f $(shell find . -name '*.[od]') | ||||||
| 	rm -f hmp-commands.h gdbstub-xml.c | 	rm -f hmp-commands.h gdbstub-xml.c | ||||||
| 	rm -f trace/generated-helpers.c trace/generated-helpers.c-timestamp |  | ||||||
| ifdef CONFIG_TRACE_SYSTEMTAP | ifdef CONFIG_TRACE_SYSTEMTAP | ||||||
| 	rm -f *.stp | 	rm -f *.stp | ||||||
| endif | endif | ||||||
| @@ -233,7 +221,6 @@ ifdef CONFIG_TRACE_SYSTEMTAP | |||||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset" | 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset" | ||||||
| 	$(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp" | 	$(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp" | ||||||
| 	$(INSTALL_DATA) $(QEMU_PROG)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp" | 	$(INSTALL_DATA) $(QEMU_PROG)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp" | ||||||
| 	$(INSTALL_DATA) $(QEMU_PROG)-log.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-log.stp" |  | ||||||
| endif | endif | ||||||
|  |  | ||||||
| GENERATED_FILES += config-target.h | GENERATED_FILES += config-target.h | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								README
									
									
									
									
									
								
							| @@ -54,7 +54,7 @@ Submitting patches | |||||||
|  |  | ||||||
| The QEMU source code is maintained under the GIT version control system. | The QEMU source code is maintained under the GIT version control system. | ||||||
|  |  | ||||||
|    git clone https://git.qemu.org/git/qemu.git |    git clone git://git.qemu.org/qemu.git | ||||||
|  |  | ||||||
| When submitting patches, one common approach is to use 'git | When submitting patches, one common approach is to use 'git | ||||||
| format-patch' and/or 'git send-email' to format & send the mail to the | format-patch' and/or 'git send-email' to format & send the mail to the | ||||||
| @@ -70,7 +70,7 @@ the QEMU website | |||||||
|  |  | ||||||
| The QEMU website is also maintained under source control. | The QEMU website is also maintained under source control. | ||||||
|  |  | ||||||
|   git clone https://git.qemu.org/git/qemu-web.git |   git clone git://git.qemu.org/qemu-web.git | ||||||
|   https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/ |   https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/ | ||||||
|  |  | ||||||
| A 'git-publish' utility was created to make above process less | A 'git-publish' utility was created to make above process less | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| obj-$(CONFIG_SOFTMMU) += accel.o | obj-$(CONFIG_SOFTMMU) += accel.o | ||||||
| obj-$(CONFIG_KVM) += kvm/ | obj-y += kvm/ | ||||||
| obj-$(CONFIG_TCG) += tcg/ | obj-$(CONFIG_TCG) += tcg/ | ||||||
| obj-y += stubs/ | obj-y += stubs/ | ||||||
|   | |||||||
| @@ -34,7 +34,6 @@ | |||||||
| #include "qom/object.h" | #include "qom/object.h" | ||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
| #include "qemu/option.h" | #include "qemu/option.h" | ||||||
| #include "qapi/error.h" |  | ||||||
|  |  | ||||||
| static const TypeInfo accel_type = { | static const TypeInfo accel_type = { | ||||||
|     .name = TYPE_ACCEL, |     .name = TYPE_ACCEL, | ||||||
| @@ -66,14 +65,13 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms) | |||||||
|         *(acc->allowed) = false; |         *(acc->allowed) = false; | ||||||
|         object_unref(OBJECT(accel)); |         object_unref(OBJECT(accel)); | ||||||
|     } |     } | ||||||
|     object_set_accelerator_compat_props(acc->compat_props); |  | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| void configure_accelerator(MachineState *ms, const char *progname) | void configure_accelerator(MachineState *ms) | ||||||
| { | { | ||||||
|     const char *accel; |     const char *accel, *p; | ||||||
|     char **accel_list, **tmp; |     char buf[10]; | ||||||
|     int ret; |     int ret; | ||||||
|     bool accel_initialised = false; |     bool accel_initialised = false; | ||||||
|     bool init_failed = false; |     bool init_failed = false; | ||||||
| @@ -81,28 +79,17 @@ void configure_accelerator(MachineState *ms, const char *progname) | |||||||
|  |  | ||||||
|     accel = qemu_opt_get(qemu_get_machine_opts(), "accel"); |     accel = qemu_opt_get(qemu_get_machine_opts(), "accel"); | ||||||
|     if (accel == NULL) { |     if (accel == NULL) { | ||||||
|         /* Select the default accelerator */ |         /* Use the default "accelerator", tcg */ | ||||||
|         int pnlen = strlen(progname); |         accel = "tcg"; | ||||||
|         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 |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     accel_list = g_strsplit(accel, ":", 0); |     p = accel; | ||||||
|  |     while (!accel_initialised && *p != '\0') { | ||||||
|     for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) { |         if (*p == ':') { | ||||||
|         acc = accel_find(*tmp); |             p++; | ||||||
|  |         } | ||||||
|  |         p = get_opt_name(buf, sizeof(buf), p, ':'); | ||||||
|  |         acc = accel_find(buf); | ||||||
|         if (!acc) { |         if (!acc) { | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
| @@ -120,7 +107,6 @@ void configure_accelerator(MachineState *ms, const char *progname) | |||||||
|             accel_initialised = true; |             accel_initialised = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     g_strfreev(accel_list); |  | ||||||
|  |  | ||||||
|     if (!accel_initialised) { |     if (!accel_initialised) { | ||||||
|         if (!init_failed) { |         if (!init_failed) { | ||||||
| @@ -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 *class = ACCEL_GET_CLASS(accel); | ||||||
|     AccelClass *acc = ACCEL_GET_CLASS(accel); |     register_compat_props_array(class->global_props); | ||||||
|     if (acc->setup_post) { |  | ||||||
|         acc->setup_post(ms, accel); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void register_accel_types(void) | static void register_accel_types(void) | ||||||
|   | |||||||
| @@ -1,2 +1 @@ | |||||||
| obj-y += kvm-all.o | obj-$(CONFIG_KVM) += kvm-all.o | ||||||
| obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o |  | ||||||
|   | |||||||
| @@ -38,8 +38,6 @@ | |||||||
| #include "qemu/event_notifier.h" | #include "qemu/event_notifier.h" | ||||||
| #include "trace.h" | #include "trace.h" | ||||||
| #include "hw/irq.h" | #include "hw/irq.h" | ||||||
| #include "sysemu/sev.h" |  | ||||||
| #include "sysemu/balloon.h" |  | ||||||
|  |  | ||||||
| #include "hw/boards.h" | #include "hw/boards.h" | ||||||
|  |  | ||||||
| @@ -79,14 +77,13 @@ struct KVMState | |||||||
|     int fd; |     int fd; | ||||||
|     int vmfd; |     int vmfd; | ||||||
|     int coalesced_mmio; |     int coalesced_mmio; | ||||||
|     int coalesced_pio; |  | ||||||
|     struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; |     struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; | ||||||
|     bool coalesced_flush_in_progress; |     bool coalesced_flush_in_progress; | ||||||
|     int vcpu_events; |     int vcpu_events; | ||||||
|     int robust_singlestep; |     int robust_singlestep; | ||||||
|     int debugregs; |     int debugregs; | ||||||
| #ifdef KVM_CAP_SET_GUEST_DEBUG | #ifdef KVM_CAP_SET_GUEST_DEBUG | ||||||
|     QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints; |     struct kvm_sw_breakpoint_head kvm_sw_breakpoints; | ||||||
| #endif | #endif | ||||||
|     int many_ioeventfds; |     int many_ioeventfds; | ||||||
|     int intx_set_mask; |     int intx_set_mask; | ||||||
| @@ -102,14 +99,10 @@ struct KVMState | |||||||
|     int nr_allocated_irq_routes; |     int nr_allocated_irq_routes; | ||||||
|     unsigned long *used_gsi_bitmap; |     unsigned long *used_gsi_bitmap; | ||||||
|     unsigned int gsi_count; |     unsigned int gsi_count; | ||||||
|     QTAILQ_HEAD(, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE]; |     QTAILQ_HEAD(msi_hashtab, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE]; | ||||||
| #endif | #endif | ||||||
|     KVMMemoryListener memory_listener; |     KVMMemoryListener memory_listener; | ||||||
|     QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus; |     QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus; | ||||||
|  |  | ||||||
|     /* memory encryption */ |  | ||||||
|     void *memcrypt_handle; |  | ||||||
|     int (*memcrypt_encrypt_data)(void *handle, uint8_t *ptr, uint64_t len); |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| KVMState *kvm_state; | KVMState *kvm_state; | ||||||
| @@ -145,26 +138,6 @@ int kvm_get_max_memslots(void) | |||||||
|     return s->nr_slots; |     return s->nr_slots; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool kvm_memcrypt_enabled(void) |  | ||||||
| { |  | ||||||
|     if (kvm_state && kvm_state->memcrypt_handle) { |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len) |  | ||||||
| { |  | ||||||
|     if (kvm_state->memcrypt_handle && |  | ||||||
|         kvm_state->memcrypt_encrypt_data) { |  | ||||||
|         return kvm_state->memcrypt_encrypt_data(kvm_state->memcrypt_handle, |  | ||||||
|                                               ptr, len); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml) | static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml) | ||||||
| { | { | ||||||
|     KVMState *s = kvm_state; |     KVMState *s = kvm_state; | ||||||
| @@ -258,7 +231,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram, | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new) | static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot) | ||||||
| { | { | ||||||
|     KVMState *s = kvm_state; |     KVMState *s = kvm_state; | ||||||
|     struct kvm_userspace_memory_region mem; |     struct kvm_userspace_memory_region mem; | ||||||
| @@ -269,7 +242,7 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo | |||||||
|     mem.userspace_addr = (unsigned long)slot->ram; |     mem.userspace_addr = (unsigned long)slot->ram; | ||||||
|     mem.flags = slot->flags; |     mem.flags = slot->flags; | ||||||
|  |  | ||||||
|     if (slot->memory_size && !new && (mem.flags ^ slot->old_flags) & KVM_MEM_READONLY) { |     if (slot->memory_size && mem.flags & KVM_MEM_READONLY) { | ||||||
|         /* Set the slot size to 0 before setting the slot to the desired |         /* Set the slot size to 0 before setting the slot to the desired | ||||||
|          * value. This is needed based on KVM commit 75d61fbc. */ |          * value. This is needed based on KVM commit 75d61fbc. */ | ||||||
|         mem.memory_size = 0; |         mem.memory_size = 0; | ||||||
| @@ -277,7 +250,6 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo | |||||||
|     } |     } | ||||||
|     mem.memory_size = slot->memory_size; |     mem.memory_size = slot->memory_size; | ||||||
|     ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); |     ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); | ||||||
|     slot->old_flags = mem.flags; |  | ||||||
|     trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr, |     trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr, | ||||||
|                               mem.memory_size, mem.userspace_addr, ret); |                               mem.memory_size, mem.userspace_addr, ret); | ||||||
|     return ret; |     return ret; | ||||||
| @@ -394,14 +366,17 @@ static int kvm_mem_flags(MemoryRegion *mr) | |||||||
| static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem, | static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem, | ||||||
|                                  MemoryRegion *mr) |                                  MemoryRegion *mr) | ||||||
| { | { | ||||||
|  |     int old_flags; | ||||||
|  |  | ||||||
|  |     old_flags = mem->flags; | ||||||
|     mem->flags = kvm_mem_flags(mr); |     mem->flags = kvm_mem_flags(mr); | ||||||
|  |  | ||||||
|     /* If nothing changed effectively, no need to issue ioctl */ |     /* If nothing changed effectively, no need to issue ioctl */ | ||||||
|     if (mem->flags == mem->old_flags) { |     if (mem->flags == old_flags) { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return kvm_set_user_memory_region(kml, mem, false); |     return kvm_set_user_memory_region(kml, mem); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int kvm_section_update_flags(KVMMemoryListener *kml, | static int kvm_section_update_flags(KVMMemoryListener *kml, | ||||||
| @@ -561,45 +536,6 @@ static void kvm_uncoalesce_mmio_region(MemoryListener *listener, | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void kvm_coalesce_pio_add(MemoryListener *listener, |  | ||||||
|                                 MemoryRegionSection *section, |  | ||||||
|                                 hwaddr start, hwaddr size) |  | ||||||
| { |  | ||||||
|     KVMState *s = kvm_state; |  | ||||||
|  |  | ||||||
|     if (s->coalesced_pio) { |  | ||||||
|         struct kvm_coalesced_mmio_zone zone; |  | ||||||
|  |  | ||||||
|         zone.addr = start; |  | ||||||
|         zone.size = size; |  | ||||||
|         zone.pio = 1; |  | ||||||
|  |  | ||||||
|         (void)kvm_vm_ioctl(s, KVM_REGISTER_COALESCED_MMIO, &zone); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void kvm_coalesce_pio_del(MemoryListener *listener, |  | ||||||
|                                 MemoryRegionSection *section, |  | ||||||
|                                 hwaddr start, hwaddr size) |  | ||||||
| { |  | ||||||
|     KVMState *s = kvm_state; |  | ||||||
|  |  | ||||||
|     if (s->coalesced_pio) { |  | ||||||
|         struct kvm_coalesced_mmio_zone zone; |  | ||||||
|  |  | ||||||
|         zone.addr = start; |  | ||||||
|         zone.size = size; |  | ||||||
|         zone.pio = 1; |  | ||||||
|  |  | ||||||
|         (void)kvm_vm_ioctl(s, KVM_UNREGISTER_COALESCED_MMIO, &zone); |  | ||||||
|      } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static MemoryListener kvm_coalesced_pio_listener = { |  | ||||||
|     .coalesced_io_add = kvm_coalesce_pio_add, |  | ||||||
|     .coalesced_io_del = kvm_coalesce_pio_del, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| int kvm_check_extension(KVMState *s, unsigned int extension) | int kvm_check_extension(KVMState *s, unsigned int extension) | ||||||
| { | { | ||||||
|     int ret; |     int ret; | ||||||
| @@ -657,8 +593,6 @@ static int kvm_set_ioeventfd_mmio(int fd, hwaddr addr, uint32_t val, | |||||||
|         .fd = fd, |         .fd = fd, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     trace_kvm_set_ioeventfd_mmio(fd, (uint64_t)addr, val, assign, size, |  | ||||||
|                                  datamatch); |  | ||||||
|     if (!kvm_enabled()) { |     if (!kvm_enabled()) { | ||||||
|         return -ENOSYS; |         return -ENOSYS; | ||||||
|     } |     } | ||||||
| @@ -690,7 +624,6 @@ static int kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint16_t val, | |||||||
|         .fd = fd, |         .fd = fd, | ||||||
|     }; |     }; | ||||||
|     int r; |     int r; | ||||||
|     trace_kvm_set_ioeventfd_pio(fd, addr, val, assign, size, datamatch); |  | ||||||
|     if (!kvm_enabled()) { |     if (!kvm_enabled()) { | ||||||
|         return -ENOSYS; |         return -ENOSYS; | ||||||
|     } |     } | ||||||
| @@ -797,8 +730,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, | |||||||
|  |  | ||||||
|         /* unregister the slot */ |         /* unregister the slot */ | ||||||
|         mem->memory_size = 0; |         mem->memory_size = 0; | ||||||
|         mem->flags = 0; |         err = kvm_set_user_memory_region(kml, mem); | ||||||
|         err = kvm_set_user_memory_region(kml, mem, false); |  | ||||||
|         if (err) { |         if (err) { | ||||||
|             fprintf(stderr, "%s: error unregistering slot: %s\n", |             fprintf(stderr, "%s: error unregistering slot: %s\n", | ||||||
|                     __func__, strerror(-err)); |                     __func__, strerror(-err)); | ||||||
| @@ -814,7 +746,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, | |||||||
|     mem->ram = ram; |     mem->ram = ram; | ||||||
|     mem->flags = kvm_mem_flags(mr); |     mem->flags = kvm_mem_flags(mr); | ||||||
|  |  | ||||||
|     err = kvm_set_user_memory_region(kml, mem, true); |     err = kvm_set_user_memory_region(kml, mem); | ||||||
|     if (err) { |     if (err) { | ||||||
|         fprintf(stderr, "%s: error registering slot: %s\n", __func__, |         fprintf(stderr, "%s: error registering slot: %s\n", __func__, | ||||||
|                 strerror(-err)); |                 strerror(-err)); | ||||||
| @@ -1593,7 +1525,7 @@ static int kvm_init(MachineState *ms) | |||||||
|  |  | ||||||
|     kvm_type = qemu_opt_get(qemu_get_machine_opts(), "kvm-type"); |     kvm_type = qemu_opt_get(qemu_get_machine_opts(), "kvm-type"); | ||||||
|     if (mc->kvm_type) { |     if (mc->kvm_type) { | ||||||
|         type = mc->kvm_type(ms, kvm_type); |         type = mc->kvm_type(kvm_type); | ||||||
|     } else if (kvm_type) { |     } else if (kvm_type) { | ||||||
|         ret = -EINVAL; |         ret = -EINVAL; | ||||||
|         fprintf(stderr, "Invalid argument kvm-type=%s\n", kvm_type); |         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_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO); | ||||||
|     s->coalesced_pio = s->coalesced_mmio && |  | ||||||
|                        kvm_check_extension(s, KVM_CAP_COALESCED_PIO); |  | ||||||
|  |  | ||||||
| #ifdef KVM_CAP_VCPU_EVENTS | #ifdef KVM_CAP_VCPU_EVENTS | ||||||
|     s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS); |     s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS); | ||||||
| @@ -1684,8 +1614,10 @@ static int kvm_init(MachineState *ms) | |||||||
|         s->irq_set_ioctl = KVM_IRQ_LINE_STATUS; |         s->irq_set_ioctl = KVM_IRQ_LINE_STATUS; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | #ifdef KVM_CAP_READONLY_MEM | ||||||
|     kvm_readonly_mem_allowed = |     kvm_readonly_mem_allowed = | ||||||
|         (kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0); |         (kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|     kvm_eventfds_allowed = |     kvm_eventfds_allowed = | ||||||
|         (kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0); |         (kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0); | ||||||
| @@ -1704,20 +1636,6 @@ static int kvm_init(MachineState *ms) | |||||||
|  |  | ||||||
|     kvm_state = s; |     kvm_state = s; | ||||||
|  |  | ||||||
|     /* |  | ||||||
|      * if memory encryption object is specified then initialize the memory |  | ||||||
|      * encryption context. |  | ||||||
|      */ |  | ||||||
|     if (ms->memory_encryption) { |  | ||||||
|         kvm_state->memcrypt_handle = sev_guest_init(ms->memory_encryption); |  | ||||||
|         if (!kvm_state->memcrypt_handle) { |  | ||||||
|             ret = -1; |  | ||||||
|             goto err; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         kvm_state->memcrypt_encrypt_data = sev_encrypt_data; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = kvm_arch_init(ms, s); |     ret = kvm_arch_init(ms, s); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         goto err; |         goto err; | ||||||
| @@ -1731,22 +1649,17 @@ static int kvm_init(MachineState *ms) | |||||||
|         s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add; |         s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add; | ||||||
|         s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del; |         s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del; | ||||||
|     } |     } | ||||||
|     s->memory_listener.listener.coalesced_io_add = kvm_coalesce_mmio_region; |     s->memory_listener.listener.coalesced_mmio_add = kvm_coalesce_mmio_region; | ||||||
|     s->memory_listener.listener.coalesced_io_del = kvm_uncoalesce_mmio_region; |     s->memory_listener.listener.coalesced_mmio_del = kvm_uncoalesce_mmio_region; | ||||||
|  |  | ||||||
|     kvm_memory_listener_register(s, &s->memory_listener, |     kvm_memory_listener_register(s, &s->memory_listener, | ||||||
|                                  &address_space_memory, 0); |                                  &address_space_memory, 0); | ||||||
|     memory_listener_register(&kvm_io_listener, |     memory_listener_register(&kvm_io_listener, | ||||||
|                              &address_space_io); |                              &address_space_io); | ||||||
|     memory_listener_register(&kvm_coalesced_pio_listener, |  | ||||||
|                              &address_space_io); |  | ||||||
|  |  | ||||||
|     s->many_ioeventfds = kvm_check_many_ioeventfds(); |     s->many_ioeventfds = kvm_check_many_ioeventfds(); | ||||||
|  |  | ||||||
|     s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU); |     s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU); | ||||||
|     if (!s->sync_mmu) { |  | ||||||
|         qemu_balloon_inhibit(true); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
| @@ -1825,13 +1738,7 @@ void kvm_flush_coalesced_mmio_buffer(void) | |||||||
|  |  | ||||||
|             ent = &ring->coalesced_mmio[ring->first]; |             ent = &ring->coalesced_mmio[ring->first]; | ||||||
|  |  | ||||||
|             if (ent->pio == 1) { |             cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len); | ||||||
|                 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); |  | ||||||
|             } |  | ||||||
|             smp_wmb(); |             smp_wmb(); | ||||||
|             ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX; |             ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -1,26 +0,0 @@ | |||||||
| /* |  | ||||||
|  * QEMU SEV stub |  | ||||||
|  * |  | ||||||
|  * Copyright Advanced Micro Devices 2018 |  | ||||||
|  * |  | ||||||
|  * Authors: |  | ||||||
|  *      Brijesh Singh <brijesh.singh@amd.com> |  | ||||||
|  * |  | ||||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or later. |  | ||||||
|  * See the COPYING file in the top-level directory. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "qemu-common.h" |  | ||||||
| #include "sysemu/sev.h" |  | ||||||
|  |  | ||||||
| int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len) |  | ||||||
| { |  | ||||||
|     abort(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void *sev_guest_init(const char *id) |  | ||||||
| { |  | ||||||
|     return NULL; |  | ||||||
| } |  | ||||||
| @@ -12,7 +12,5 @@ kvm_irqchip_commit_routes(void) "" | |||||||
| kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d" | kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d" | ||||||
| kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d" | kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d" | ||||||
| kvm_irqchip_release_virq(int virq) "virq %d" | kvm_irqchip_release_virq(int virq) "virq %d" | ||||||
| kvm_set_ioeventfd_mmio(int fd, uint64_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%" PRIx64 " val=0x%x assign: %d size: %d match: %d" |  | ||||||
| kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%x val=0x%x assign: %d size: %d match: %d" |  | ||||||
| kvm_set_user_memory(uint32_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d" | kvm_set_user_memory(uint32_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -105,16 +105,6 @@ int kvm_on_sigbus(int code, void *addr) | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool kvm_memcrypt_enabled(void) |  | ||||||
| { |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len) |  | ||||||
| { |  | ||||||
|   return 1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #ifndef CONFIG_USER_ONLY | #ifndef CONFIG_USER_ONLY | ||||||
| int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev) | int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev) | ||||||
| { | { | ||||||
|   | |||||||
| @@ -21,6 +21,10 @@ void tb_flush(CPUState *cpu) | |||||||
| { | { | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void tb_unlock(void) | ||||||
|  | { | ||||||
|  | } | ||||||
|  |  | ||||||
| void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) | void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) | ||||||
| { | { | ||||||
| } | } | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ | |||||||
|  * This library is free software; you can redistribute it and/or |  * This library is free software; you can redistribute it and/or | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  * License as published by the Free Software Foundation; either |  * License as published by the Free Software Foundation; either | ||||||
|  * version 2.1 of the License, or (at your option) any later version. |  * version 2 of the License, or (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This library is distributed in the hope that it will be useful, |  * This library is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -18,37 +18,26 @@ | |||||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>. |  * License along with this library; if not, see <http://www.gnu.org/licenses/>. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "trace/mem.h" |  | ||||||
|  |  | ||||||
| #if DATA_SIZE == 16 | #if DATA_SIZE == 16 | ||||||
| # define SUFFIX     o | # define SUFFIX     o | ||||||
| # define DATA_TYPE  Int128 | # define DATA_TYPE  Int128 | ||||||
| # define BSWAP      bswap128 | # define BSWAP      bswap128 | ||||||
| # define SHIFT      4 |  | ||||||
| #elif DATA_SIZE == 8 | #elif DATA_SIZE == 8 | ||||||
| # define SUFFIX     q | # define SUFFIX     q | ||||||
| # define DATA_TYPE  uint64_t | # define DATA_TYPE  uint64_t | ||||||
| # define SDATA_TYPE int64_t |  | ||||||
| # define BSWAP      bswap64 | # define BSWAP      bswap64 | ||||||
| # define SHIFT      3 |  | ||||||
| #elif DATA_SIZE == 4 | #elif DATA_SIZE == 4 | ||||||
| # define SUFFIX     l | # define SUFFIX     l | ||||||
| # define DATA_TYPE  uint32_t | # define DATA_TYPE  uint32_t | ||||||
| # define SDATA_TYPE int32_t |  | ||||||
| # define BSWAP      bswap32 | # define BSWAP      bswap32 | ||||||
| # define SHIFT      2 |  | ||||||
| #elif DATA_SIZE == 2 | #elif DATA_SIZE == 2 | ||||||
| # define SUFFIX     w | # define SUFFIX     w | ||||||
| # define DATA_TYPE  uint16_t | # define DATA_TYPE  uint16_t | ||||||
| # define SDATA_TYPE int16_t |  | ||||||
| # define BSWAP      bswap16 | # define BSWAP      bswap16 | ||||||
| # define SHIFT      1 |  | ||||||
| #elif DATA_SIZE == 1 | #elif DATA_SIZE == 1 | ||||||
| # define SUFFIX     b | # define SUFFIX     b | ||||||
| # define DATA_TYPE  uint8_t | # define DATA_TYPE  uint8_t | ||||||
| # define SDATA_TYPE int8_t |  | ||||||
| # define BSWAP | # define BSWAP | ||||||
| # define SHIFT      0 |  | ||||||
| #else | #else | ||||||
| # error unsupported data size | # error unsupported data size | ||||||
| #endif | #endif | ||||||
| @@ -59,37 +48,14 @@ | |||||||
| # define ABI_TYPE  uint32_t | # define ABI_TYPE  uint32_t | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #define ATOMIC_TRACE_RMW do {                                           \ |  | ||||||
|         uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \ |  | ||||||
|                                                                         \ |  | ||||||
|         trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \ |  | ||||||
|         trace_guest_mem_before_exec(ENV_GET_CPU(env), addr,             \ |  | ||||||
|                                     info | TRACE_MEM_ST);               \ |  | ||||||
|     } while (0) |  | ||||||
|  |  | ||||||
| #define ATOMIC_TRACE_LD do {                                            \ |  | ||||||
|         uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \ |  | ||||||
|                                                                         \ |  | ||||||
|         trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \ |  | ||||||
|     } while (0) |  | ||||||
|  |  | ||||||
| # define ATOMIC_TRACE_ST do {                                           \ |  | ||||||
|         uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true); \ |  | ||||||
|                                                                         \ |  | ||||||
|         trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \ |  | ||||||
|     } while (0) |  | ||||||
|  |  | ||||||
| /* Define host-endian atomic operations.  Note that END is used within | /* Define host-endian atomic operations.  Note that END is used within | ||||||
|    the ATOMIC_NAME macro, and redefined below.  */ |    the ATOMIC_NAME macro, and redefined below.  */ | ||||||
| #if DATA_SIZE == 1 | #if DATA_SIZE == 1 | ||||||
| # define END | # define END | ||||||
| # define MEND _be /* either le or be would be fine */ |  | ||||||
| #elif defined(HOST_WORDS_BIGENDIAN) | #elif defined(HOST_WORDS_BIGENDIAN) | ||||||
| # define END  _be | # define END  _be | ||||||
| # define MEND _be |  | ||||||
| #else | #else | ||||||
| # define END  _le | # define END  _le | ||||||
| # define MEND _le |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | ||||||
| @@ -97,27 +63,17 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | |||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|     DATA_TYPE ret; |     DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); | ||||||
|  |  | ||||||
|     ATOMIC_TRACE_RMW; |  | ||||||
| #if DATA_SIZE == 16 |  | ||||||
|     ret = atomic16_cmpxchg(haddr, cmpv, newv); |  | ||||||
| #else |  | ||||||
|     ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); |  | ||||||
| #endif |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| #if DATA_SIZE >= 16 | #if DATA_SIZE >= 16 | ||||||
| #if HAVE_ATOMIC128 |  | ||||||
| ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) | ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) | ||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|  |     __atomic_load(haddr, &val, __ATOMIC_RELAXED); | ||||||
|     ATOMIC_TRACE_LD; |  | ||||||
|     val = atomic16_read(haddr); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
|     return val; |     return val; | ||||||
| } | } | ||||||
| @@ -127,22 +83,16 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, | |||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|  |     __atomic_store(haddr, &val, __ATOMIC_RELAXED); | ||||||
|     ATOMIC_TRACE_ST; |  | ||||||
|     atomic16_set(haddr, val); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
| } | } | ||||||
| #endif |  | ||||||
| #else | #else | ||||||
| ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, | ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, | ||||||
|                            ABI_TYPE val EXTRA_ARGS) |                            ABI_TYPE val EXTRA_ARGS) | ||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|     DATA_TYPE ret; |     DATA_TYPE ret = atomic_xchg__nocheck(haddr, val); | ||||||
|  |  | ||||||
|     ATOMIC_TRACE_RMW; |  | ||||||
|     ret = atomic_xchg__nocheck(haddr, val); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| @@ -153,10 +103,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ | |||||||
| {                                                                   \ | {                                                                   \ | ||||||
|     ATOMIC_MMU_DECLS;                                               \ |     ATOMIC_MMU_DECLS;                                               \ | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \ |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \ | ||||||
|     DATA_TYPE ret;                                                  \ |     DATA_TYPE ret = atomic_##X(haddr, val);                         \ | ||||||
|                                                                     \ |  | ||||||
|     ATOMIC_TRACE_RMW;                                               \ |  | ||||||
|     ret = atomic_##X(haddr, val);                                   \ |  | ||||||
|     ATOMIC_MMU_CLEANUP;                                             \ |     ATOMIC_MMU_CLEANUP;                                             \ | ||||||
|     return ret;                                                     \ |     return ret;                                                     \ | ||||||
| } | } | ||||||
| @@ -171,48 +118,9 @@ GEN_ATOMIC_HELPER(or_fetch) | |||||||
| GEN_ATOMIC_HELPER(xor_fetch) | GEN_ATOMIC_HELPER(xor_fetch) | ||||||
|  |  | ||||||
| #undef GEN_ATOMIC_HELPER | #undef GEN_ATOMIC_HELPER | ||||||
|  |  | ||||||
| /* These helpers are, as a whole, full barriers.  Within the helper, |  | ||||||
|  * the leading barrier is explicit and the trailing barrier is within |  | ||||||
|  * cmpxchg primitive. |  | ||||||
|  * |  | ||||||
|  * Trace this load + RMW loop as a single RMW op. This way, regardless |  | ||||||
|  * of CF_PARALLEL's value, we'll trace just a read and a write. |  | ||||||
|  */ |  | ||||||
| #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET)                \ |  | ||||||
| ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ |  | ||||||
|                         ABI_TYPE xval EXTRA_ARGS)                   \ |  | ||||||
| {                                                                   \ |  | ||||||
|     ATOMIC_MMU_DECLS;                                               \ |  | ||||||
|     XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                          \ |  | ||||||
|     XDATA_TYPE cmp, old, new, val = xval;                           \ |  | ||||||
|                                                                     \ |  | ||||||
|     ATOMIC_TRACE_RMW;                                               \ |  | ||||||
|     smp_mb();                                                       \ |  | ||||||
|     cmp = atomic_read__nocheck(haddr);                              \ |  | ||||||
|     do {                                                            \ |  | ||||||
|         old = cmp; new = FN(old, val);                              \ |  | ||||||
|         cmp = atomic_cmpxchg__nocheck(haddr, old, new);             \ |  | ||||||
|     } while (cmp != old);                                           \ |  | ||||||
|     ATOMIC_MMU_CLEANUP;                                             \ |  | ||||||
|     return RET;                                                     \ |  | ||||||
| } |  | ||||||
|  |  | ||||||
| GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old) |  | ||||||
| GEN_ATOMIC_HELPER_FN(fetch_umin, MIN,  DATA_TYPE, old) |  | ||||||
| GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old) |  | ||||||
| GEN_ATOMIC_HELPER_FN(fetch_umax, MAX,  DATA_TYPE, old) |  | ||||||
|  |  | ||||||
| GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new) |  | ||||||
| GEN_ATOMIC_HELPER_FN(umin_fetch, MIN,  DATA_TYPE, new) |  | ||||||
| GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new) |  | ||||||
| GEN_ATOMIC_HELPER_FN(umax_fetch, MAX,  DATA_TYPE, new) |  | ||||||
|  |  | ||||||
| #undef GEN_ATOMIC_HELPER_FN |  | ||||||
| #endif /* DATA SIZE >= 16 */ | #endif /* DATA SIZE >= 16 */ | ||||||
|  |  | ||||||
| #undef END | #undef END | ||||||
| #undef MEND |  | ||||||
|  |  | ||||||
| #if DATA_SIZE > 1 | #if DATA_SIZE > 1 | ||||||
|  |  | ||||||
| @@ -220,10 +128,8 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX,  DATA_TYPE, new) | |||||||
|    within the ATOMIC_NAME macro.  */ |    within the ATOMIC_NAME macro.  */ | ||||||
| #ifdef HOST_WORDS_BIGENDIAN | #ifdef HOST_WORDS_BIGENDIAN | ||||||
| # define END  _le | # define END  _le | ||||||
| # define MEND _le |  | ||||||
| #else | #else | ||||||
| # define END  _be | # define END  _be | ||||||
| # define MEND _be |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | ||||||
| @@ -231,27 +137,17 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, | |||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|     DATA_TYPE ret; |     DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); | ||||||
|  |  | ||||||
|     ATOMIC_TRACE_RMW; |  | ||||||
| #if DATA_SIZE == 16 |  | ||||||
|     ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv)); |  | ||||||
| #else |  | ||||||
|     ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); |  | ||||||
| #endif |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
|     return BSWAP(ret); |     return BSWAP(ret); | ||||||
| } | } | ||||||
|  |  | ||||||
| #if DATA_SIZE >= 16 | #if DATA_SIZE >= 16 | ||||||
| #if HAVE_ATOMIC128 |  | ||||||
| ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) | ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) | ||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|  |     __atomic_load(haddr, &val, __ATOMIC_RELAXED); | ||||||
|     ATOMIC_TRACE_LD; |  | ||||||
|     val = atomic16_read(haddr); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
|     return BSWAP(val); |     return BSWAP(val); | ||||||
| } | } | ||||||
| @@ -261,23 +157,17 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, | |||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|  |  | ||||||
|     ATOMIC_TRACE_ST; |  | ||||||
|     val = BSWAP(val); |     val = BSWAP(val); | ||||||
|     atomic16_set(haddr, val); |     __atomic_store(haddr, &val, __ATOMIC_RELAXED); | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
| } | } | ||||||
| #endif |  | ||||||
| #else | #else | ||||||
| ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, | ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, | ||||||
|                            ABI_TYPE val EXTRA_ARGS) |                            ABI_TYPE val EXTRA_ARGS) | ||||||
| { | { | ||||||
|     ATOMIC_MMU_DECLS; |     ATOMIC_MMU_DECLS; | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|     ABI_TYPE ret; |     ABI_TYPE ret = atomic_xchg__nocheck(haddr, BSWAP(val)); | ||||||
|  |  | ||||||
|     ATOMIC_TRACE_RMW; |  | ||||||
|     ret = atomic_xchg__nocheck(haddr, BSWAP(val)); |  | ||||||
|     ATOMIC_MMU_CLEANUP; |     ATOMIC_MMU_CLEANUP; | ||||||
|     return BSWAP(ret); |     return BSWAP(ret); | ||||||
| } | } | ||||||
| @@ -288,10 +178,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ | |||||||
| {                                                                   \ | {                                                                   \ | ||||||
|     ATOMIC_MMU_DECLS;                                               \ |     ATOMIC_MMU_DECLS;                                               \ | ||||||
|     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \ |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \ | ||||||
|     DATA_TYPE ret;                                                  \ |     DATA_TYPE ret = atomic_##X(haddr, BSWAP(val));                  \ | ||||||
|                                                                     \ |  | ||||||
|     ATOMIC_TRACE_RMW;                                               \ |  | ||||||
|     ret = atomic_##X(haddr, BSWAP(val));                            \ |  | ||||||
|     ATOMIC_MMU_CLEANUP;                                             \ |     ATOMIC_MMU_CLEANUP;                                             \ | ||||||
|     return BSWAP(ret);                                              \ |     return BSWAP(ret);                                              \ | ||||||
| } | } | ||||||
| @@ -305,64 +192,54 @@ GEN_ATOMIC_HELPER(xor_fetch) | |||||||
|  |  | ||||||
| #undef GEN_ATOMIC_HELPER | #undef GEN_ATOMIC_HELPER | ||||||
|  |  | ||||||
| /* These helpers are, as a whole, full barriers.  Within the helper, |  | ||||||
|  * the leading barrier is explicit and the trailing barrier is within |  | ||||||
|  * cmpxchg primitive. |  | ||||||
|  * |  | ||||||
|  * Trace this load + RMW loop as a single RMW op. This way, regardless |  | ||||||
|  * of CF_PARALLEL's value, we'll trace just a read and a write. |  | ||||||
|  */ |  | ||||||
| #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET)                \ |  | ||||||
| ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \ |  | ||||||
|                         ABI_TYPE xval EXTRA_ARGS)                   \ |  | ||||||
| {                                                                   \ |  | ||||||
|     ATOMIC_MMU_DECLS;                                               \ |  | ||||||
|     XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                          \ |  | ||||||
|     XDATA_TYPE ldo, ldn, old, new, val = xval;                      \ |  | ||||||
|                                                                     \ |  | ||||||
|     ATOMIC_TRACE_RMW;                                               \ |  | ||||||
|     smp_mb();                                                       \ |  | ||||||
|     ldn = atomic_read__nocheck(haddr);                              \ |  | ||||||
|     do {                                                            \ |  | ||||||
|         ldo = ldn; old = BSWAP(ldo); new = FN(old, val);            \ |  | ||||||
|         ldn = atomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new));      \ |  | ||||||
|     } while (ldo != ldn);                                           \ |  | ||||||
|     ATOMIC_MMU_CLEANUP;                                             \ |  | ||||||
|     return RET;                                                     \ |  | ||||||
| } |  | ||||||
|  |  | ||||||
| GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old) |  | ||||||
| GEN_ATOMIC_HELPER_FN(fetch_umin, MIN,  DATA_TYPE, old) |  | ||||||
| GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old) |  | ||||||
| GEN_ATOMIC_HELPER_FN(fetch_umax, MAX,  DATA_TYPE, old) |  | ||||||
|  |  | ||||||
| GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new) |  | ||||||
| GEN_ATOMIC_HELPER_FN(umin_fetch, MIN,  DATA_TYPE, new) |  | ||||||
| GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new) |  | ||||||
| GEN_ATOMIC_HELPER_FN(umax_fetch, MAX,  DATA_TYPE, new) |  | ||||||
|  |  | ||||||
| /* Note that for addition, we need to use a separate cmpxchg loop instead | /* Note that for addition, we need to use a separate cmpxchg loop instead | ||||||
|    of bswaps for the reverse-host-endian helpers.  */ |    of bswaps for the reverse-host-endian helpers.  */ | ||||||
| #define ADD(X, Y)   (X + Y) | ABI_TYPE ATOMIC_NAME(fetch_add)(CPUArchState *env, target_ulong addr, | ||||||
| GEN_ATOMIC_HELPER_FN(fetch_add, ADD, DATA_TYPE, old) |                          ABI_TYPE val EXTRA_ARGS) | ||||||
| GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new) | { | ||||||
| #undef ADD |     ATOMIC_MMU_DECLS; | ||||||
|  |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|  |     DATA_TYPE ldo, ldn, ret, sto; | ||||||
|  |  | ||||||
| #undef GEN_ATOMIC_HELPER_FN |     ldo = atomic_read__nocheck(haddr); | ||||||
|  |     while (1) { | ||||||
|  |         ret = BSWAP(ldo); | ||||||
|  |         sto = BSWAP(ret + val); | ||||||
|  |         ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto); | ||||||
|  |         if (ldn == ldo) { | ||||||
|  |             ATOMIC_MMU_CLEANUP; | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |         ldo = ldn; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ABI_TYPE ATOMIC_NAME(add_fetch)(CPUArchState *env, target_ulong addr, | ||||||
|  |                          ABI_TYPE val EXTRA_ARGS) | ||||||
|  | { | ||||||
|  |     ATOMIC_MMU_DECLS; | ||||||
|  |     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; | ||||||
|  |     DATA_TYPE ldo, ldn, ret, sto; | ||||||
|  |  | ||||||
|  |     ldo = atomic_read__nocheck(haddr); | ||||||
|  |     while (1) { | ||||||
|  |         ret = BSWAP(ldo) + val; | ||||||
|  |         sto = BSWAP(ret); | ||||||
|  |         ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto); | ||||||
|  |         if (ldn == ldo) { | ||||||
|  |             ATOMIC_MMU_CLEANUP; | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |         ldo = ldn; | ||||||
|  |     } | ||||||
|  | } | ||||||
| #endif /* DATA_SIZE >= 16 */ | #endif /* DATA_SIZE >= 16 */ | ||||||
|  |  | ||||||
| #undef END | #undef END | ||||||
| #undef MEND |  | ||||||
| #endif /* DATA_SIZE > 1 */ | #endif /* DATA_SIZE > 1 */ | ||||||
|  |  | ||||||
| #undef ATOMIC_TRACE_ST |  | ||||||
| #undef ATOMIC_TRACE_LD |  | ||||||
| #undef ATOMIC_TRACE_RMW |  | ||||||
|  |  | ||||||
| #undef BSWAP | #undef BSWAP | ||||||
| #undef ABI_TYPE | #undef ABI_TYPE | ||||||
| #undef DATA_TYPE | #undef DATA_TYPE | ||||||
| #undef SDATA_TYPE |  | ||||||
| #undef SUFFIX | #undef SUFFIX | ||||||
| #undef DATA_SIZE | #undef DATA_SIZE | ||||||
| #undef SHIFT |  | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * This library is free software; you can redistribute it and/or |  * This library is free software; you can redistribute it and/or | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  * License as published by the Free Software Foundation; either |  * License as published by the Free Software Foundation; either | ||||||
|  * version 2.1 of the License, or (at your option) any later version. |  * version 2 of the License, or (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This library is distributed in the hope that it will be useful, |  * This library is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -27,8 +27,10 @@ bool tcg_allowed; | |||||||
| /* exit the current TB, but without causing any exception to be raised */ | /* exit the current TB, but without causing any exception to be raised */ | ||||||
| void cpu_loop_exit_noexc(CPUState *cpu) | void cpu_loop_exit_noexc(CPUState *cpu) | ||||||
| { | { | ||||||
|  |     /* XXX: restore cpu registers saved in host registers */ | ||||||
|  |  | ||||||
|     cpu->exception_index = -1; |     cpu->exception_index = -1; | ||||||
|     cpu_loop_exit(cpu); |     siglongjmp(cpu->jmp_env, 1); | ||||||
| } | } | ||||||
|  |  | ||||||
| #if defined(CONFIG_SOFTMMU) | #if defined(CONFIG_SOFTMMU) | ||||||
| @@ -63,17 +65,15 @@ void cpu_reloading_memory_map(void) | |||||||
|  |  | ||||||
| void cpu_loop_exit(CPUState *cpu) | void cpu_loop_exit(CPUState *cpu) | ||||||
| { | { | ||||||
|     /* Undo the setting in cpu_tb_exec.  */ |  | ||||||
|     cpu->can_do_io = 1; |  | ||||||
|     siglongjmp(cpu->jmp_env, 1); |     siglongjmp(cpu->jmp_env, 1); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) | void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) | ||||||
| { | { | ||||||
|     if (pc) { |     if (pc) { | ||||||
|         cpu_restore_state(cpu, pc, true); |         cpu_restore_state(cpu, pc); | ||||||
|     } |     } | ||||||
|     cpu_loop_exit(cpu); |     siglongjmp(cpu->jmp_env, 1); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc) | void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc) | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * This library is free software; you can redistribute it and/or |  * This library is free software; you can redistribute it and/or | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  * License as published by the Free Software Foundation; either |  * License as published by the Free Software Foundation; either | ||||||
|  * version 2.1 of the License, or (at your option) any later version. |  * version 2 of the License, or (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This library is distributed in the hope that it will be useful, |  * This library is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -25,6 +25,7 @@ | |||||||
| #include "qemu/atomic.h" | #include "qemu/atomic.h" | ||||||
| #include "sysemu/qtest.h" | #include "sysemu/qtest.h" | ||||||
| #include "qemu/timer.h" | #include "qemu/timer.h" | ||||||
|  | #include "exec/address-spaces.h" | ||||||
| #include "qemu/rcu.h" | #include "qemu/rcu.h" | ||||||
| #include "exec/tb-hash.h" | #include "exec/tb-hash.h" | ||||||
| #include "exec/tb-lookup.h" | #include "exec/tb-lookup.h" | ||||||
| @@ -155,14 +156,11 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb) | |||||||
|     if (qemu_loglevel_mask(CPU_LOG_TB_CPU) |     if (qemu_loglevel_mask(CPU_LOG_TB_CPU) | ||||||
|         && qemu_log_in_addr_range(itb->pc)) { |         && qemu_log_in_addr_range(itb->pc)) { | ||||||
|         qemu_log_lock(); |         qemu_log_lock(); | ||||||
|         int flags = 0; |  | ||||||
|         if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) { |  | ||||||
|             flags |= CPU_DUMP_FPU; |  | ||||||
|         } |  | ||||||
| #if defined(TARGET_I386) | #if defined(TARGET_I386) | ||||||
|         flags |= CPU_DUMP_CCOP; |         log_cpu_state(cpu, CPU_DUMP_CCOP); | ||||||
|  | #else | ||||||
|  |         log_cpu_state(cpu, 0); | ||||||
| #endif | #endif | ||||||
|         log_cpu_state(cpu, flags); |  | ||||||
|         qemu_log_unlock(); |         qemu_log_unlock(); | ||||||
|     } |     } | ||||||
| #endif /* DEBUG_DISAS */ | #endif /* DEBUG_DISAS */ | ||||||
| @@ -212,20 +210,20 @@ static void cpu_exec_nocache(CPUState *cpu, int max_cycles, | |||||||
|        We only end up here when an existing TB is too long.  */ |        We only end up here when an existing TB is too long.  */ | ||||||
|     cflags |= MIN(max_cycles, CF_COUNT_MASK); |     cflags |= MIN(max_cycles, CF_COUNT_MASK); | ||||||
|  |  | ||||||
|     mmap_lock(); |     tb_lock(); | ||||||
|     tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, |     tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, | ||||||
|                      orig_tb->flags, cflags); |                      orig_tb->flags, cflags); | ||||||
|     tb->orig_tb = orig_tb; |     tb->orig_tb = orig_tb; | ||||||
|     mmap_unlock(); |     tb_unlock(); | ||||||
|  |  | ||||||
|     /* execute the generated code */ |     /* execute the generated code */ | ||||||
|     trace_exec_tb_nocache(tb, tb->pc); |     trace_exec_tb_nocache(tb, tb->pc); | ||||||
|     cpu_tb_exec(cpu, tb); |     cpu_tb_exec(cpu, tb); | ||||||
|  |  | ||||||
|     mmap_lock(); |     tb_lock(); | ||||||
|     tb_phys_invalidate(tb, -1); |     tb_phys_invalidate(tb, -1); | ||||||
|     mmap_unlock(); |     tb_remove(tb); | ||||||
|     tcg_tb_remove(tb); |     tb_unlock(); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -244,7 +242,12 @@ void cpu_exec_step_atomic(CPUState *cpu) | |||||||
|         tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); |         tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); | ||||||
|         if (tb == NULL) { |         if (tb == NULL) { | ||||||
|             mmap_lock(); |             mmap_lock(); | ||||||
|             tb = 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(); |             mmap_unlock(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -259,17 +262,15 @@ void cpu_exec_step_atomic(CPUState *cpu) | |||||||
|         cpu_tb_exec(cpu, tb); |         cpu_tb_exec(cpu, tb); | ||||||
|         cc->cpu_exec_exit(cpu); |         cc->cpu_exec_exit(cpu); | ||||||
|     } else { |     } else { | ||||||
|         /* |         /* We may have exited due to another problem here, so we need | ||||||
|  |          * to reset any tb_locks we may have taken but didn't release. | ||||||
|          * The mmap_lock is dropped by tb_gen_code if it runs out of |          * The mmap_lock is dropped by tb_gen_code if it runs out of | ||||||
|          * memory. |          * memory. | ||||||
|          */ |          */ | ||||||
| #ifndef CONFIG_SOFTMMU | #ifndef CONFIG_SOFTMMU | ||||||
|         tcg_debug_assert(!have_mmap_lock()); |         tcg_debug_assert(!have_mmap_lock()); | ||||||
| #endif | #endif | ||||||
|         if (qemu_mutex_iothread_locked()) { |         tb_lock_reset(); | ||||||
|             qemu_mutex_unlock_iothread(); |  | ||||||
|         } |  | ||||||
|         assert_no_pages_locked(); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (in_exclusive_region) { |     if (in_exclusive_region) { | ||||||
| @@ -292,7 +293,7 @@ struct tb_desc { | |||||||
|     uint32_t trace_vcpu_dstate; |     uint32_t trace_vcpu_dstate; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static bool tb_lookup_cmp(const void *p, const void *d) | static bool tb_cmp(const void *p, const void *d) | ||||||
| { | { | ||||||
|     const TranslationBlock *tb = p; |     const TranslationBlock *tb = p; | ||||||
|     const struct tb_desc *desc = d; |     const struct tb_desc *desc = d; | ||||||
| @@ -335,12 +336,9 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, | |||||||
|     desc.trace_vcpu_dstate = *cpu->trace_dstate; |     desc.trace_vcpu_dstate = *cpu->trace_dstate; | ||||||
|     desc.pc = pc; |     desc.pc = pc; | ||||||
|     phys_pc = get_page_addr_code(desc.env, pc); |     phys_pc = get_page_addr_code(desc.env, pc); | ||||||
|     if (phys_pc == -1) { |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|     desc.phys_page1 = phys_pc & TARGET_PAGE_MASK; |     desc.phys_page1 = phys_pc & TARGET_PAGE_MASK; | ||||||
|     h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate); |     h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate); | ||||||
|     return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp); |     return qht_lookup(&tb_ctx.htable, tb_cmp, &desc, h); | ||||||
| } | } | ||||||
|  |  | ||||||
| void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) | void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) | ||||||
| @@ -354,43 +352,28 @@ void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Called with tb_lock held.  */ | ||||||
| static inline void tb_add_jump(TranslationBlock *tb, int n, | static inline void tb_add_jump(TranslationBlock *tb, int n, | ||||||
|                                TranslationBlock *tb_next) |                                TranslationBlock *tb_next) | ||||||
| { | { | ||||||
|     uintptr_t old; |  | ||||||
|  |  | ||||||
|     assert(n < ARRAY_SIZE(tb->jmp_list_next)); |     assert(n < ARRAY_SIZE(tb->jmp_list_next)); | ||||||
|     qemu_spin_lock(&tb_next->jmp_lock); |     if (tb->jmp_list_next[n]) { | ||||||
|  |         /* Another thread has already done this while we were | ||||||
|     /* make sure the destination TB is valid */ |          * outside of the lock; nothing to do in this case */ | ||||||
|     if (tb_next->cflags & CF_INVALID) { |         return; | ||||||
|         goto out_unlock_next; |  | ||||||
|     } |     } | ||||||
|     /* Atomically claim the jump destination slot only if it was NULL */ |  | ||||||
|     old = atomic_cmpxchg(&tb->jmp_dest[n], (uintptr_t)NULL, (uintptr_t)tb_next); |  | ||||||
|     if (old) { |  | ||||||
|         goto out_unlock_next; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* patch the native jump address */ |  | ||||||
|     tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr); |  | ||||||
|  |  | ||||||
|     /* add in TB jmp list */ |  | ||||||
|     tb->jmp_list_next[n] = tb_next->jmp_list_head; |  | ||||||
|     tb_next->jmp_list_head = (uintptr_t)tb | n; |  | ||||||
|  |  | ||||||
|     qemu_spin_unlock(&tb_next->jmp_lock); |  | ||||||
|  |  | ||||||
|     qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc, |     qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc, | ||||||
|                            "Linking TBs %p [" TARGET_FMT_lx |                            "Linking TBs %p [" TARGET_FMT_lx | ||||||
|                            "] index %d -> %p [" TARGET_FMT_lx "]\n", |                            "] index %d -> %p [" TARGET_FMT_lx "]\n", | ||||||
|                            tb->tc.ptr, tb->pc, n, |                            tb->tc.ptr, tb->pc, n, | ||||||
|                            tb_next->tc.ptr, tb_next->pc); |                            tb_next->tc.ptr, tb_next->pc); | ||||||
|     return; |  | ||||||
|  |  | ||||||
|  out_unlock_next: |     /* patch the native jump address */ | ||||||
|     qemu_spin_unlock(&tb_next->jmp_lock); |     tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr); | ||||||
|     return; |  | ||||||
|  |     /* add in TB jmp circular list */ | ||||||
|  |     tb->jmp_list_next[n] = tb_next->jmp_list_first; | ||||||
|  |     tb_next->jmp_list_first = (uintptr_t)tb | n; | ||||||
| } | } | ||||||
|  |  | ||||||
| static inline TranslationBlock *tb_find(CPUState *cpu, | static inline TranslationBlock *tb_find(CPUState *cpu, | ||||||
| @@ -400,11 +383,27 @@ static inline TranslationBlock *tb_find(CPUState *cpu, | |||||||
|     TranslationBlock *tb; |     TranslationBlock *tb; | ||||||
|     target_ulong cs_base, pc; |     target_ulong cs_base, pc; | ||||||
|     uint32_t flags; |     uint32_t flags; | ||||||
|  |     bool acquired_tb_lock = false; | ||||||
|  |  | ||||||
|     tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); |     tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); | ||||||
|     if (tb == NULL) { |     if (tb == NULL) { | ||||||
|  |         /* mmap_lock is needed by tb_gen_code, and mmap_lock must be | ||||||
|  |          * taken outside tb_lock. As system emulation is currently | ||||||
|  |          * single threaded the locks are NOPs. | ||||||
|  |          */ | ||||||
|         mmap_lock(); |         mmap_lock(); | ||||||
|         tb = 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(); |         mmap_unlock(); | ||||||
|         /* We add the TB in the virtual pc hash table for the fast lookup */ |         /* We add the TB in the virtual pc hash table for the fast lookup */ | ||||||
|         atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb); |         atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb); | ||||||
| @@ -419,8 +418,17 @@ static inline TranslationBlock *tb_find(CPUState *cpu, | |||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
|     /* See if we can patch the calling TB. */ |     /* See if we can patch the calling TB. */ | ||||||
|     if (last_tb) { |     if (last_tb && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) { | ||||||
|         tb_add_jump(last_tb, tb_exit, tb); |         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; |     return tb; | ||||||
| } | } | ||||||
| @@ -577,7 +585,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, | |||||||
|         else { |         else { | ||||||
|             if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { |             if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { | ||||||
|                 replay_interrupt(); |                 replay_interrupt(); | ||||||
|                 cpu->exception_index = -1; |  | ||||||
|                 *last_tb = NULL; |                 *last_tb = NULL; | ||||||
|             } |             } | ||||||
|             /* The target hook may have updated the 'cpu->interrupt_request'; |             /* The target hook may have updated the 'cpu->interrupt_request'; | ||||||
| @@ -599,9 +606,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, | |||||||
|     if (unlikely(atomic_read(&cpu->exit_request) |     if (unlikely(atomic_read(&cpu->exit_request) | ||||||
|         || (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) { |         || (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) { | ||||||
|         atomic_set(&cpu->exit_request, 0); |         atomic_set(&cpu->exit_request, 0); | ||||||
|         if (cpu->exception_index == -1) { |         cpu->exception_index = EXCP_INTERRUPT; | ||||||
|             cpu->exception_index = EXCP_INTERRUPT; |  | ||||||
|         } |  | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -696,13 +701,11 @@ int cpu_exec(CPUState *cpu) | |||||||
|         g_assert(cpu == current_cpu); |         g_assert(cpu == current_cpu); | ||||||
|         g_assert(cc == CPU_GET_CLASS(cpu)); |         g_assert(cc == CPU_GET_CLASS(cpu)); | ||||||
| #endif /* buggy compiler */ | #endif /* buggy compiler */ | ||||||
| #ifndef CONFIG_SOFTMMU |         cpu->can_do_io = 1; | ||||||
|         tcg_debug_assert(!have_mmap_lock()); |         tb_lock_reset(); | ||||||
| #endif |  | ||||||
|         if (qemu_mutex_iothread_locked()) { |         if (qemu_mutex_iothread_locked()) { | ||||||
|             qemu_mutex_unlock_iothread(); |             qemu_mutex_unlock_iothread(); | ||||||
|         } |         } | ||||||
|         assert_no_pages_locked(); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* if an exception is pending, we execute it here */ |     /* if an exception is pending, we execute it here */ | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -11,7 +11,7 @@ | |||||||
|  * This library is free software; you can redistribute it and/or |  * This library is free software; you can redistribute it and/or | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  * License as published by the Free Software Foundation; either |  * License as published by the Free Software Foundation; either | ||||||
|  * version 2.1 of the License, or (at your option) any later version. |  * version 2 of the License, or (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This library is distributed in the hope that it will be useful, |  * This library is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -98,23 +98,19 @@ | |||||||
| static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env, | static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env, | ||||||
|                                               size_t mmu_idx, size_t index, |                                               size_t mmu_idx, size_t index, | ||||||
|                                               target_ulong addr, |                                               target_ulong addr, | ||||||
|                                               uintptr_t retaddr, |                                               uintptr_t retaddr) | ||||||
|                                               bool recheck, |  | ||||||
|                                               MMUAccessType access_type) |  | ||||||
| { | { | ||||||
|     CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; |     CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; | ||||||
|     return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, recheck, |     return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, DATA_SIZE); | ||||||
|                     access_type, DATA_SIZE); |  | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, | WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, | ||||||
|                             TCGMemOpIdx oi, uintptr_t retaddr) |                             TCGMemOpIdx oi, uintptr_t retaddr) | ||||||
| { | { | ||||||
|     uintptr_t mmu_idx = get_mmuidx(oi); |     unsigned mmu_idx = get_mmuidx(oi); | ||||||
|     uintptr_t index = tlb_index(env, mmu_idx, addr); |     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); |     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; | ||||||
|     target_ulong tlb_addr = entry->ADDR_READ; |  | ||||||
|     unsigned a_bits = get_alignment_bits(get_memop(oi)); |     unsigned a_bits = get_alignment_bits(get_memop(oi)); | ||||||
|     uintptr_t haddr; |     uintptr_t haddr; | ||||||
|     DATA_TYPE res; |     DATA_TYPE res; | ||||||
| @@ -125,14 +121,13 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* If the TLB entry is for a different page, reload and try again.  */ |     /* If the TLB entry is for a different page, reload and try again.  */ | ||||||
|     if (!tlb_hit(tlb_addr, addr)) { |     if ((addr & TARGET_PAGE_MASK) | ||||||
|  |          != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { | ||||||
|         if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { |         if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { | ||||||
|             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, |             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, | ||||||
|                      mmu_idx, retaddr); |                      mmu_idx, retaddr); | ||||||
|             index = tlb_index(env, mmu_idx, addr); |  | ||||||
|             entry = tlb_entry(env, mmu_idx, addr); |  | ||||||
|         } |         } | ||||||
|         tlb_addr = entry->ADDR_READ; |         tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Handle an IO access.  */ |     /* Handle an IO access.  */ | ||||||
| @@ -143,9 +138,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, | |||||||
|  |  | ||||||
|         /* ??? Note that the io helpers always read data in the target |         /* ??? Note that the io helpers always read data in the target | ||||||
|            byte ordering.  We should push the LE/BE request down into io.  */ |            byte ordering.  We should push the LE/BE request down into io.  */ | ||||||
|         res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr, |         res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr); | ||||||
|                                     tlb_addr & TLB_RECHECK, |  | ||||||
|                                     READ_ACCESS_TYPE); |  | ||||||
|         res = TGT_LE(res); |         res = TGT_LE(res); | ||||||
|         return res; |         return res; | ||||||
|     } |     } | ||||||
| @@ -169,7 +162,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, | |||||||
|         return res; |         return res; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     haddr = addr + entry->addend; |     haddr = addr + env->tlb_table[mmu_idx][index].addend; | ||||||
| #if DATA_SIZE == 1 | #if DATA_SIZE == 1 | ||||||
|     res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr); |     res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr); | ||||||
| #else | #else | ||||||
| @@ -182,10 +175,9 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, | |||||||
| WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, | WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, | ||||||
|                             TCGMemOpIdx oi, uintptr_t retaddr) |                             TCGMemOpIdx oi, uintptr_t retaddr) | ||||||
| { | { | ||||||
|     uintptr_t mmu_idx = get_mmuidx(oi); |     unsigned mmu_idx = get_mmuidx(oi); | ||||||
|     uintptr_t index = tlb_index(env, mmu_idx, addr); |     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); |     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; | ||||||
|     target_ulong tlb_addr = entry->ADDR_READ; |  | ||||||
|     unsigned a_bits = get_alignment_bits(get_memop(oi)); |     unsigned a_bits = get_alignment_bits(get_memop(oi)); | ||||||
|     uintptr_t haddr; |     uintptr_t haddr; | ||||||
|     DATA_TYPE res; |     DATA_TYPE res; | ||||||
| @@ -196,14 +188,13 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* If the TLB entry is for a different page, reload and try again.  */ |     /* If the TLB entry is for a different page, reload and try again.  */ | ||||||
|     if (!tlb_hit(tlb_addr, addr)) { |     if ((addr & TARGET_PAGE_MASK) | ||||||
|  |          != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { | ||||||
|         if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { |         if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { | ||||||
|             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, |             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, | ||||||
|                      mmu_idx, retaddr); |                      mmu_idx, retaddr); | ||||||
|             index = tlb_index(env, mmu_idx, addr); |  | ||||||
|             entry = tlb_entry(env, mmu_idx, addr); |  | ||||||
|         } |         } | ||||||
|         tlb_addr = entry->ADDR_READ; |         tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Handle an IO access.  */ |     /* Handle an IO access.  */ | ||||||
| @@ -214,9 +205,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, | |||||||
|  |  | ||||||
|         /* ??? Note that the io helpers always read data in the target |         /* ??? Note that the io helpers always read data in the target | ||||||
|            byte ordering.  We should push the LE/BE request down into io.  */ |            byte ordering.  We should push the LE/BE request down into io.  */ | ||||||
|         res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr, |         res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr); | ||||||
|                                     tlb_addr & TLB_RECHECK, |  | ||||||
|                                     READ_ACCESS_TYPE); |  | ||||||
|         res = TGT_BE(res); |         res = TGT_BE(res); | ||||||
|         return res; |         return res; | ||||||
|     } |     } | ||||||
| @@ -240,7 +229,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, | |||||||
|         return res; |         return res; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     haddr = addr + entry->addend; |     haddr = addr + env->tlb_table[mmu_idx][index].addend; | ||||||
|     res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr); |     res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr); | ||||||
|     return res; |     return res; | ||||||
| } | } | ||||||
| @@ -270,21 +259,18 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env, | |||||||
|                                           size_t mmu_idx, size_t index, |                                           size_t mmu_idx, size_t index, | ||||||
|                                           DATA_TYPE val, |                                           DATA_TYPE val, | ||||||
|                                           target_ulong addr, |                                           target_ulong addr, | ||||||
|                                           uintptr_t retaddr, |                                           uintptr_t retaddr) | ||||||
|                                           bool recheck) |  | ||||||
| { | { | ||||||
|     CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; |     CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; | ||||||
|     return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr, |     return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr, DATA_SIZE); | ||||||
|                      recheck, DATA_SIZE); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||||
|                        TCGMemOpIdx oi, uintptr_t retaddr) |                        TCGMemOpIdx oi, uintptr_t retaddr) | ||||||
| { | { | ||||||
|     uintptr_t mmu_idx = get_mmuidx(oi); |     unsigned mmu_idx = get_mmuidx(oi); | ||||||
|     uintptr_t index = tlb_index(env, mmu_idx, addr); |     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); |     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; | ||||||
|     target_ulong tlb_addr = tlb_addr_write(entry); |  | ||||||
|     unsigned a_bits = get_alignment_bits(get_memop(oi)); |     unsigned a_bits = get_alignment_bits(get_memop(oi)); | ||||||
|     uintptr_t haddr; |     uintptr_t haddr; | ||||||
|  |  | ||||||
| @@ -294,14 +280,13 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* If the TLB entry is for a different page, reload and try again.  */ |     /* If the TLB entry is for a different page, reload and try again.  */ | ||||||
|     if (!tlb_hit(tlb_addr, addr)) { |     if ((addr & TARGET_PAGE_MASK) | ||||||
|  |         != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { | ||||||
|         if (!VICTIM_TLB_HIT(addr_write, addr)) { |         if (!VICTIM_TLB_HIT(addr_write, addr)) { | ||||||
|             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, |             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, | ||||||
|                      mmu_idx, retaddr); |                      mmu_idx, retaddr); | ||||||
|             index = tlb_index(env, mmu_idx, addr); |  | ||||||
|             entry = tlb_entry(env, mmu_idx, addr); |  | ||||||
|         } |         } | ||||||
|         tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK; |         tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Handle an IO access.  */ |     /* Handle an IO access.  */ | ||||||
| @@ -313,8 +298,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | |||||||
|         /* ??? Note that the io helpers always read data in the target |         /* ??? Note that the io helpers always read data in the target | ||||||
|            byte ordering.  We should push the LE/BE request down into io.  */ |            byte ordering.  We should push the LE/BE request down into io.  */ | ||||||
|         val = TGT_LE(val); |         val = TGT_LE(val); | ||||||
|         glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, |         glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr); | ||||||
|                                retaddr, tlb_addr & TLB_RECHECK); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -322,16 +306,16 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | |||||||
|     if (DATA_SIZE > 1 |     if (DATA_SIZE > 1 | ||||||
|         && unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1 |         && unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1 | ||||||
|                      >= TARGET_PAGE_SIZE)) { |                      >= TARGET_PAGE_SIZE)) { | ||||||
|         int i; |         int i, index2; | ||||||
|         target_ulong page2; |         target_ulong page2, tlb_addr2; | ||||||
|         CPUTLBEntry *entry2; |  | ||||||
|     do_unaligned_access: |     do_unaligned_access: | ||||||
|         /* Ensure the second page is in the TLB.  Note that the first page |         /* Ensure the second page is in the TLB.  Note that the first page | ||||||
|            is already guaranteed to be filled, and that the second page |            is already guaranteed to be filled, and that the second page | ||||||
|            cannot evict the first.  */ |            cannot evict the first.  */ | ||||||
|         page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; |         page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; | ||||||
|         entry2 = tlb_entry(env, mmu_idx, page2); |         index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|         if (!tlb_hit_page(tlb_addr_write(entry2), page2) |         tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; | ||||||
|  |         if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK)) | ||||||
|             && !VICTIM_TLB_HIT(addr_write, page2)) { |             && !VICTIM_TLB_HIT(addr_write, page2)) { | ||||||
|             tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE, |             tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE, | ||||||
|                      mmu_idx, retaddr); |                      mmu_idx, retaddr); | ||||||
| @@ -349,7 +333,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     haddr = addr + entry->addend; |     haddr = addr + env->tlb_table[mmu_idx][index].addend; | ||||||
| #if DATA_SIZE == 1 | #if DATA_SIZE == 1 | ||||||
|     glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val); |     glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val); | ||||||
| #else | #else | ||||||
| @@ -361,10 +345,9 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | |||||||
| void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | ||||||
|                        TCGMemOpIdx oi, uintptr_t retaddr) |                        TCGMemOpIdx oi, uintptr_t retaddr) | ||||||
| { | { | ||||||
|     uintptr_t mmu_idx = get_mmuidx(oi); |     unsigned mmu_idx = get_mmuidx(oi); | ||||||
|     uintptr_t index = tlb_index(env, mmu_idx, addr); |     int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); |     target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; | ||||||
|     target_ulong tlb_addr = tlb_addr_write(entry); |  | ||||||
|     unsigned a_bits = get_alignment_bits(get_memop(oi)); |     unsigned a_bits = get_alignment_bits(get_memop(oi)); | ||||||
|     uintptr_t haddr; |     uintptr_t haddr; | ||||||
|  |  | ||||||
| @@ -374,14 +357,13 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* If the TLB entry is for a different page, reload and try again.  */ |     /* If the TLB entry is for a different page, reload and try again.  */ | ||||||
|     if (!tlb_hit(tlb_addr, addr)) { |     if ((addr & TARGET_PAGE_MASK) | ||||||
|  |         != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { | ||||||
|         if (!VICTIM_TLB_HIT(addr_write, addr)) { |         if (!VICTIM_TLB_HIT(addr_write, addr)) { | ||||||
|             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, |             tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, | ||||||
|                      mmu_idx, retaddr); |                      mmu_idx, retaddr); | ||||||
|             index = tlb_index(env, mmu_idx, addr); |  | ||||||
|             entry = tlb_entry(env, mmu_idx, addr); |  | ||||||
|         } |         } | ||||||
|         tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK; |         tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Handle an IO access.  */ |     /* Handle an IO access.  */ | ||||||
| @@ -393,8 +375,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | |||||||
|         /* ??? Note that the io helpers always read data in the target |         /* ??? Note that the io helpers always read data in the target | ||||||
|            byte ordering.  We should push the LE/BE request down into io.  */ |            byte ordering.  We should push the LE/BE request down into io.  */ | ||||||
|         val = TGT_BE(val); |         val = TGT_BE(val); | ||||||
|         glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr, |         glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr); | ||||||
|                                tlb_addr & TLB_RECHECK); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -402,16 +383,16 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | |||||||
|     if (DATA_SIZE > 1 |     if (DATA_SIZE > 1 | ||||||
|         && unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1 |         && unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1 | ||||||
|                      >= TARGET_PAGE_SIZE)) { |                      >= TARGET_PAGE_SIZE)) { | ||||||
|         int i; |         int i, index2; | ||||||
|         target_ulong page2; |         target_ulong page2, tlb_addr2; | ||||||
|         CPUTLBEntry *entry2; |  | ||||||
|     do_unaligned_access: |     do_unaligned_access: | ||||||
|         /* Ensure the second page is in the TLB.  Note that the first page |         /* Ensure the second page is in the TLB.  Note that the first page | ||||||
|            is already guaranteed to be filled, and that the second page |            is already guaranteed to be filled, and that the second page | ||||||
|            cannot evict the first.  */ |            cannot evict the first.  */ | ||||||
|         page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; |         page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; | ||||||
|         entry2 = tlb_entry(env, mmu_idx, page2); |         index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); | ||||||
|         if (!tlb_hit_page(tlb_addr_write(entry2), page2) |         tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; | ||||||
|  |         if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK)) | ||||||
|             && !VICTIM_TLB_HIT(addr_write, page2)) { |             && !VICTIM_TLB_HIT(addr_write, page2)) { | ||||||
|             tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE, |             tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE, | ||||||
|                      mmu_idx, retaddr); |                      mmu_idx, retaddr); | ||||||
| @@ -429,7 +410,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     haddr = addr + entry->addend; |     haddr = addr + env->tlb_table[mmu_idx][index].addend; | ||||||
|     glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val); |     glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val); | ||||||
| } | } | ||||||
| #endif /* DATA_SIZE > 1 */ | #endif /* DATA_SIZE > 1 */ | ||||||
|   | |||||||
| @@ -51,7 +51,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask) | |||||||
|     if (!qemu_cpu_is_self(cpu)) { |     if (!qemu_cpu_is_self(cpu)) { | ||||||
|         qemu_cpu_kick(cpu); |         qemu_cpu_kick(cpu); | ||||||
|     } else { |     } else { | ||||||
|         atomic_set(&cpu->icount_decr.u16.high, -1); |         cpu->icount_decr.u16.high = -1; | ||||||
|         if (use_icount && |         if (use_icount && | ||||||
|             !cpu->can_do_io |             !cpu->can_do_io | ||||||
|             && (mask & ~old_mask) != 0) { |             && (mask & ~old_mask) != 0) { | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * This library is free software; you can redistribute it and/or |  * This library is free software; you can redistribute it and/or | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  * License as published by the Free Software Foundation; either |  * License as published by the Free Software Foundation; either | ||||||
|  * version 2.1 of the License, or (at your option) any later version. |  * version 2 of the License, or (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This library is distributed in the hope that it will be useful, |  * This library is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -512,39 +512,6 @@ void HELPER(gvec_orc)(void *d, void *a, void *b, uint32_t desc) | |||||||
|     clear_high(d, oprsz, desc); |     clear_high(d, oprsz, desc); | ||||||
| } | } | ||||||
|  |  | ||||||
| void HELPER(gvec_nand)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) & *(vec64 *)(b + i)); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_nor)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) | *(vec64 *)(b + i)); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_eqv)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(vec64)) { |  | ||||||
|         *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) ^ *(vec64 *)(b + i)); |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc) | void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc) | ||||||
| { | { | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |     intptr_t oprsz = simd_oprsz(desc); | ||||||
| @@ -738,7 +705,7 @@ void HELPER(NAME)(void *d, void *a, void *b, uint32_t desc)                \ | |||||||
| {                                                                          \ | {                                                                          \ | ||||||
|     intptr_t oprsz = simd_oprsz(desc);                                     \ |     intptr_t oprsz = simd_oprsz(desc);                                     \ | ||||||
|     intptr_t i;                                                            \ |     intptr_t i;                                                            \ | ||||||
|     for (i = 0; i < oprsz; i += sizeof(TYPE)) {                            \ |     for (i = 0; i < oprsz; i += sizeof(vec64)) {                           \ | ||||||
|         *(TYPE *)(d + i) = DO_CMP0(*(TYPE *)(a + i) OP *(TYPE *)(b + i));  \ |         *(TYPE *)(d + i) = DO_CMP0(*(TYPE *)(a + i) OP *(TYPE *)(b + i));  \ | ||||||
|     }                                                                      \ |     }                                                                      \ | ||||||
|     clear_high(d, oprsz, desc);                                            \ |     clear_high(d, oprsz, desc);                                            \ | ||||||
| @@ -1028,227 +995,3 @@ void HELPER(gvec_ussub64)(void *d, void *a, void *b, uint32_t desc) | |||||||
|     } |     } | ||||||
|     clear_high(d, oprsz, desc); |     clear_high(d, oprsz, desc); | ||||||
| } | } | ||||||
|  |  | ||||||
| void HELPER(gvec_smin8)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(int8_t)) { |  | ||||||
|         int8_t aa = *(int8_t *)(a + i); |  | ||||||
|         int8_t bb = *(int8_t *)(b + i); |  | ||||||
|         int8_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(int8_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smin16)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(int16_t)) { |  | ||||||
|         int16_t aa = *(int16_t *)(a + i); |  | ||||||
|         int16_t bb = *(int16_t *)(b + i); |  | ||||||
|         int16_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(int16_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smin32)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(int32_t)) { |  | ||||||
|         int32_t aa = *(int32_t *)(a + i); |  | ||||||
|         int32_t bb = *(int32_t *)(b + i); |  | ||||||
|         int32_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(int32_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smin64)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(int64_t)) { |  | ||||||
|         int64_t aa = *(int64_t *)(a + i); |  | ||||||
|         int64_t bb = *(int64_t *)(b + i); |  | ||||||
|         int64_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(int64_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smax8)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(int8_t)) { |  | ||||||
|         int8_t aa = *(int8_t *)(a + i); |  | ||||||
|         int8_t bb = *(int8_t *)(b + i); |  | ||||||
|         int8_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(int8_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smax16)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(int16_t)) { |  | ||||||
|         int16_t aa = *(int16_t *)(a + i); |  | ||||||
|         int16_t bb = *(int16_t *)(b + i); |  | ||||||
|         int16_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(int16_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smax32)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(int32_t)) { |  | ||||||
|         int32_t aa = *(int32_t *)(a + i); |  | ||||||
|         int32_t bb = *(int32_t *)(b + i); |  | ||||||
|         int32_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(int32_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_smax64)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(int64_t)) { |  | ||||||
|         int64_t aa = *(int64_t *)(a + i); |  | ||||||
|         int64_t bb = *(int64_t *)(b + i); |  | ||||||
|         int64_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(int64_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umin8)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint8_t)) { |  | ||||||
|         uint8_t aa = *(uint8_t *)(a + i); |  | ||||||
|         uint8_t bb = *(uint8_t *)(b + i); |  | ||||||
|         uint8_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(uint8_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umin16)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint16_t)) { |  | ||||||
|         uint16_t aa = *(uint16_t *)(a + i); |  | ||||||
|         uint16_t bb = *(uint16_t *)(b + i); |  | ||||||
|         uint16_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(uint16_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umin32)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint32_t)) { |  | ||||||
|         uint32_t aa = *(uint32_t *)(a + i); |  | ||||||
|         uint32_t bb = *(uint32_t *)(b + i); |  | ||||||
|         uint32_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(uint32_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umin64)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint64_t)) { |  | ||||||
|         uint64_t aa = *(uint64_t *)(a + i); |  | ||||||
|         uint64_t bb = *(uint64_t *)(b + i); |  | ||||||
|         uint64_t dd = aa < bb ? aa : bb; |  | ||||||
|         *(uint64_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umax8)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint8_t)) { |  | ||||||
|         uint8_t aa = *(uint8_t *)(a + i); |  | ||||||
|         uint8_t bb = *(uint8_t *)(b + i); |  | ||||||
|         uint8_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(uint8_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umax16)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint16_t)) { |  | ||||||
|         uint16_t aa = *(uint16_t *)(a + i); |  | ||||||
|         uint16_t bb = *(uint16_t *)(b + i); |  | ||||||
|         uint16_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(uint16_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umax32)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint32_t)) { |  | ||||||
|         uint32_t aa = *(uint32_t *)(a + i); |  | ||||||
|         uint32_t bb = *(uint32_t *)(b + i); |  | ||||||
|         uint32_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(uint32_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void HELPER(gvec_umax64)(void *d, void *a, void *b, uint32_t desc) |  | ||||||
| { |  | ||||||
|     intptr_t oprsz = simd_oprsz(desc); |  | ||||||
|     intptr_t i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < oprsz; i += sizeof(uint64_t)) { |  | ||||||
|         uint64_t aa = *(uint64_t *)(a + i); |  | ||||||
|         uint64_t bb = *(uint64_t *)(b + i); |  | ||||||
|         uint64_t dd = aa > bb ? aa : bb; |  | ||||||
|         *(uint64_t *)(d + i) = dd; |  | ||||||
|     } |  | ||||||
|     clear_high(d, oprsz, desc); |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -125,19 +125,11 @@ GEN_ATOMIC_HELPERS(fetch_add) | |||||||
| GEN_ATOMIC_HELPERS(fetch_and) | GEN_ATOMIC_HELPERS(fetch_and) | ||||||
| GEN_ATOMIC_HELPERS(fetch_or) | GEN_ATOMIC_HELPERS(fetch_or) | ||||||
| GEN_ATOMIC_HELPERS(fetch_xor) | GEN_ATOMIC_HELPERS(fetch_xor) | ||||||
| GEN_ATOMIC_HELPERS(fetch_smin) |  | ||||||
| GEN_ATOMIC_HELPERS(fetch_umin) |  | ||||||
| GEN_ATOMIC_HELPERS(fetch_smax) |  | ||||||
| GEN_ATOMIC_HELPERS(fetch_umax) |  | ||||||
|  |  | ||||||
| GEN_ATOMIC_HELPERS(add_fetch) | GEN_ATOMIC_HELPERS(add_fetch) | ||||||
| GEN_ATOMIC_HELPERS(and_fetch) | GEN_ATOMIC_HELPERS(and_fetch) | ||||||
| GEN_ATOMIC_HELPERS(or_fetch) | GEN_ATOMIC_HELPERS(or_fetch) | ||||||
| GEN_ATOMIC_HELPERS(xor_fetch) | GEN_ATOMIC_HELPERS(xor_fetch) | ||||||
| GEN_ATOMIC_HELPERS(smin_fetch) |  | ||||||
| GEN_ATOMIC_HELPERS(umin_fetch) |  | ||||||
| GEN_ATOMIC_HELPERS(smax_fetch) |  | ||||||
| GEN_ATOMIC_HELPERS(umax_fetch) |  | ||||||
|  |  | ||||||
| GEN_ATOMIC_HELPERS(xchg) | GEN_ATOMIC_HELPERS(xchg) | ||||||
|  |  | ||||||
| @@ -200,26 +192,6 @@ DEF_HELPER_FLAGS_4(gvec_ussub16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | |||||||
| DEF_HELPER_FLAGS_4(gvec_ussub32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_ussub32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_ussub64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_ussub64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_smax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_umax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_3(gvec_neg8, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | DEF_HELPER_FLAGS_3(gvec_neg8, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_3(gvec_neg16, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | DEF_HELPER_FLAGS_3(gvec_neg16, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_3(gvec_neg32, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | DEF_HELPER_FLAGS_3(gvec_neg32, TCG_CALL_NO_RWG, void, ptr, ptr, i32) | ||||||
| @@ -231,9 +203,6 @@ DEF_HELPER_FLAGS_4(gvec_or, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | |||||||
| DEF_HELPER_FLAGS_4(gvec_xor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_xor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_andc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_andc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_orc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | DEF_HELPER_FLAGS_4(gvec_orc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_nand, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_nor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_eqv, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) |  | ||||||
|  |  | ||||||
| DEF_HELPER_FLAGS_4(gvec_ands, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) | DEF_HELPER_FLAGS_4(gvec_ands, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) | ||||||
| DEF_HELPER_FLAGS_4(gvec_xors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) | DEF_HELPER_FLAGS_4(gvec_xors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -6,7 +6,7 @@ | |||||||
|  * This library is free software; you can redistribute it and/or |  * This library is free software; you can redistribute it and/or | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  * License as published by the Free Software Foundation; either |  * License as published by the Free Software Foundation; either | ||||||
|  * version 2.1 of the License, or (at your option) any later version. |  * version 2 of the License, or (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This library is distributed in the hope that it will be useful, |  * This library is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -23,13 +23,10 @@ | |||||||
|  |  | ||||||
|  |  | ||||||
| /* translate-all.c */ | /* translate-all.c */ | ||||||
| struct page_collection *page_collection_lock(tb_page_addr_t start, | void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len); | ||||||
|                                              tb_page_addr_t end); |  | ||||||
| void page_collection_unlock(struct page_collection *set); |  | ||||||
| void tb_invalidate_phys_page_fast(struct page_collection *pages, |  | ||||||
|                                   tb_page_addr_t start, int len); |  | ||||||
| void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, | void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, | ||||||
|                                    int is_cpu_write_access); |                                    int is_cpu_write_access); | ||||||
|  | void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end); | ||||||
| void tb_check_watchpoint(CPUState *cpu); | void tb_check_watchpoint(CPUState *cpu); | ||||||
|  |  | ||||||
| #ifdef CONFIG_USER_ONLY | #ifdef CONFIG_USER_ONLY | ||||||
|   | |||||||
| @@ -34,7 +34,7 @@ void translator_loop_temp_check(DisasContextBase *db) | |||||||
| void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | ||||||
|                      CPUState *cpu, TranslationBlock *tb) |                      CPUState *cpu, TranslationBlock *tb) | ||||||
| { | { | ||||||
|     int bp_insn = 0; |     int max_insns; | ||||||
|  |  | ||||||
|     /* Initialize DisasContext */ |     /* Initialize DisasContext */ | ||||||
|     db->tb = tb; |     db->tb = tb; | ||||||
| @@ -45,18 +45,18 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | |||||||
|     db->singlestep_enabled = cpu->singlestep_enabled; |     db->singlestep_enabled = cpu->singlestep_enabled; | ||||||
|  |  | ||||||
|     /* Instruction counting */ |     /* Instruction counting */ | ||||||
|     db->max_insns = tb_cflags(db->tb) & CF_COUNT_MASK; |     max_insns = tb_cflags(db->tb) & CF_COUNT_MASK; | ||||||
|     if (db->max_insns == 0) { |     if (max_insns == 0) { | ||||||
|         db->max_insns = CF_COUNT_MASK; |         max_insns = CF_COUNT_MASK; | ||||||
|     } |     } | ||||||
|     if (db->max_insns > TCG_MAX_INSNS) { |     if (max_insns > TCG_MAX_INSNS) { | ||||||
|         db->max_insns = TCG_MAX_INSNS; |         max_insns = TCG_MAX_INSNS; | ||||||
|     } |     } | ||||||
|     if (db->singlestep_enabled || singlestep) { |     if (db->singlestep_enabled || singlestep) { | ||||||
|         db->max_insns = 1; |         max_insns = 1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ops->init_disas_context(db, cpu); |     max_insns = ops->init_disas_context(db, cpu, max_insns); | ||||||
|     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */ |     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */ | ||||||
|  |  | ||||||
|     /* Reset the temp count so that we can identify leaks */ |     /* Reset the temp count so that we can identify leaks */ | ||||||
| @@ -73,13 +73,11 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | |||||||
|         tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */ |         tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */ | ||||||
|  |  | ||||||
|         /* Pass breakpoint hits to target for further processing */ |         /* Pass breakpoint hits to target for further processing */ | ||||||
|         if (!db->singlestep_enabled |         if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) { | ||||||
|             && unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) { |  | ||||||
|             CPUBreakpoint *bp; |             CPUBreakpoint *bp; | ||||||
|             QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { |             QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { | ||||||
|                 if (bp->pc == db->pc_next) { |                 if (bp->pc == db->pc_next) { | ||||||
|                     if (ops->breakpoint_check(db, cpu, bp)) { |                     if (ops->breakpoint_check(db, cpu, bp)) { | ||||||
|                         bp_insn = 1; |  | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| @@ -97,8 +95,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | |||||||
|            update db->pc_next and db->is_jmp to indicate what should be |            update db->pc_next and db->is_jmp to indicate what should be | ||||||
|            done next -- either exiting this loop or locate the start of |            done next -- either exiting this loop or locate the start of | ||||||
|            the next instruction.  */ |            the next instruction.  */ | ||||||
|         if (db->num_insns == db->max_insns |         if (db->num_insns == max_insns && (tb_cflags(db->tb) & CF_LAST_IO)) { | ||||||
|             && (tb_cflags(db->tb) & CF_LAST_IO)) { |  | ||||||
|             /* Accept I/O on the last instruction.  */ |             /* Accept I/O on the last instruction.  */ | ||||||
|             gen_io_start(); |             gen_io_start(); | ||||||
|             ops->translate_insn(db, cpu); |             ops->translate_insn(db, cpu); | ||||||
| @@ -114,7 +111,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | |||||||
|  |  | ||||||
|         /* Stop translation if the output buffer is full, |         /* Stop translation if the output buffer is full, | ||||||
|            or we have executed all of the allowed instructions.  */ |            or we have executed all of the allowed instructions.  */ | ||||||
|         if (tcg_op_buf_full() || db->num_insns >= db->max_insns) { |         if (tcg_op_buf_full() || db->num_insns >= max_insns) { | ||||||
|             db->is_jmp = DISAS_TOO_MANY; |             db->is_jmp = DISAS_TOO_MANY; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| @@ -122,7 +119,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | |||||||
|  |  | ||||||
|     /* Emit code to exit the TB, as indicated by db->is_jmp.  */ |     /* Emit code to exit the TB, as indicated by db->is_jmp.  */ | ||||||
|     ops->tb_stop(db, cpu); |     ops->tb_stop(db, cpu); | ||||||
|     gen_tb_end(db->tb, db->num_insns - bp_insn); |     gen_tb_end(db->tb, db->num_insns); | ||||||
|  |  | ||||||
|     /* The disas_log hook may use these values rather than recompute.  */ |     /* The disas_log hook may use these values rather than recompute.  */ | ||||||
|     db->tb->size = db->pc_next - db->pc_first; |     db->tb->size = db->pc_next - db->pc_first; | ||||||
|   | |||||||
| @@ -2,9 +2,6 @@ | |||||||
| #include "qemu-common.h" | #include "qemu-common.h" | ||||||
| #include "qom/cpu.h" | #include "qom/cpu.h" | ||||||
| #include "sysemu/replay.h" | #include "sysemu/replay.h" | ||||||
| #include "sysemu/sysemu.h" |  | ||||||
|  |  | ||||||
| bool enable_cpu_pm = false; |  | ||||||
|  |  | ||||||
| void cpu_resume(CPUState *cpu) | void cpu_resume(CPUState *cpu) | ||||||
| { | { | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * This library is free software; you can redistribute it and/or |  * This library is free software; you can redistribute it and/or | ||||||
|  * modify it under the terms of the GNU Lesser General Public |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  * License as published by the Free Software Foundation; either |  * License as published by the Free Software Foundation; either | ||||||
|  * version 2.1 of the License, or (at your option) any later version. |  * version 2 of the License, or (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This library is distributed in the hope that it will be useful, |  * This library is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| @@ -25,7 +25,6 @@ | |||||||
| #include "exec/cpu_ldst.h" | #include "exec/cpu_ldst.h" | ||||||
| #include "translate-all.h" | #include "translate-all.h" | ||||||
| #include "exec/helper-proto.h" | #include "exec/helper-proto.h" | ||||||
| #include "qemu/atomic128.h" |  | ||||||
|  |  | ||||||
| #undef EAX | #undef EAX | ||||||
| #undef ECX | #undef ECX | ||||||
| @@ -169,7 +168,7 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Now we have a real cpu fault.  */ |     /* Now we have a real cpu fault.  */ | ||||||
|     cpu_restore_state(cpu, pc, true); |     cpu_restore_state(cpu, pc); | ||||||
|  |  | ||||||
|     sigprocmask(SIG_SETMASK, old_set, NULL); |     sigprocmask(SIG_SETMASK, old_set, NULL); | ||||||
|     cpu_loop_exit(cpu); |     cpu_loop_exit(cpu); | ||||||
| @@ -479,66 +478,28 @@ int cpu_signal_handler(int host_signum, void *pinfo, | |||||||
|  |  | ||||||
| #elif defined(__aarch64__) | #elif defined(__aarch64__) | ||||||
|  |  | ||||||
| #ifndef ESR_MAGIC |  | ||||||
| /* Pre-3.16 kernel headers don't have these, so provide fallback definitions */ |  | ||||||
| #define ESR_MAGIC 0x45535201 |  | ||||||
| struct esr_context { |  | ||||||
|     struct _aarch64_ctx head; |  | ||||||
|     uint64_t esr; |  | ||||||
| }; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc) |  | ||||||
| { |  | ||||||
|     return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr) |  | ||||||
| { |  | ||||||
|     return (struct _aarch64_ctx *)((char *)hdr + hdr->size); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int cpu_signal_handler(int host_signum, void *pinfo, void *puc) | int cpu_signal_handler(int host_signum, void *pinfo, void *puc) | ||||||
| { | { | ||||||
|     siginfo_t *info = pinfo; |     siginfo_t *info = pinfo; | ||||||
|     ucontext_t *uc = puc; |     ucontext_t *uc = puc; | ||||||
|     uintptr_t pc = uc->uc_mcontext.pc; |     uintptr_t pc = uc->uc_mcontext.pc; | ||||||
|  |     uint32_t insn = *(uint32_t *)pc; | ||||||
|     bool is_write; |     bool is_write; | ||||||
|     struct _aarch64_ctx *hdr; |  | ||||||
|     struct esr_context const *esrctx = NULL; |  | ||||||
|  |  | ||||||
|     /* Find the esr_context, which has the WnR bit in it */ |     /* XXX: need kernel patch to get write flag faster.  */ | ||||||
|     for (hdr = first_ctx(uc); hdr->magic; hdr = next_ctx(hdr)) { |     is_write = (   (insn & 0xbfff0000) == 0x0c000000   /* C3.3.1 */ | ||||||
|         if (hdr->magic == ESR_MAGIC) { |                 || (insn & 0xbfe00000) == 0x0c800000   /* C3.3.2 */ | ||||||
|             esrctx = (struct esr_context const *)hdr; |                 || (insn & 0xbfdf0000) == 0x0d000000   /* C3.3.3 */ | ||||||
|             break; |                 || (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); |     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -609,81 +570,6 @@ int cpu_signal_handler(int host_signum, void *pinfo, | |||||||
|     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); |     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); | ||||||
| } | } | ||||||
|  |  | ||||||
| #elif defined(__riscv) |  | ||||||
|  |  | ||||||
| int cpu_signal_handler(int host_signum, void *pinfo, |  | ||||||
|                        void *puc) |  | ||||||
| { |  | ||||||
|     siginfo_t *info = pinfo; |  | ||||||
|     ucontext_t *uc = puc; |  | ||||||
|     greg_t pc = uc->uc_mcontext.__gregs[REG_PC]; |  | ||||||
|     uint32_t insn = *(uint32_t *)pc; |  | ||||||
|     int is_write = 0; |  | ||||||
|  |  | ||||||
|     /* Detect store by reading the instruction at the program |  | ||||||
|        counter. Note: we currently only generate 32-bit |  | ||||||
|        instructions so we thus only detect 32-bit stores */ |  | ||||||
|     switch (((insn >> 0) & 0b11)) { |  | ||||||
|     case 3: |  | ||||||
|         switch (((insn >> 2) & 0b11111)) { |  | ||||||
|         case 8: |  | ||||||
|             switch (((insn >> 12) & 0b111)) { |  | ||||||
|             case 0: /* sb */ |  | ||||||
|             case 1: /* sh */ |  | ||||||
|             case 2: /* sw */ |  | ||||||
|             case 3: /* sd */ |  | ||||||
|             case 4: /* sq */ |  | ||||||
|                 is_write = 1; |  | ||||||
|                 break; |  | ||||||
|             default: |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         case 9: |  | ||||||
|             switch (((insn >> 12) & 0b111)) { |  | ||||||
|             case 2: /* fsw */ |  | ||||||
|             case 3: /* fsd */ |  | ||||||
|             case 4: /* fsq */ |  | ||||||
|                 is_write = 1; |  | ||||||
|                 break; |  | ||||||
|             default: |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Check for compressed instructions */ |  | ||||||
|     switch (((insn >> 13) & 0b111)) { |  | ||||||
|     case 7: |  | ||||||
|         switch (insn & 0b11) { |  | ||||||
|         case 0: /*c.sd */ |  | ||||||
|         case 2: /* c.sdsp */ |  | ||||||
|             is_write = 1; |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     case 6: |  | ||||||
|         switch (insn & 0b11) { |  | ||||||
|         case 0: /* c.sw */ |  | ||||||
|         case 3: /* c.swsp */ |  | ||||||
|             is_write = 1; |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     default: |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #else | #else | ||||||
|  |  | ||||||
| #error host CPU specific signal handler needed | #error host CPU specific signal handler needed | ||||||
| @@ -729,7 +615,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, | |||||||
| /* The following is only callable from other helpers, and matches up | /* The following is only callable from other helpers, and matches up | ||||||
|    with the softmmu version.  */ |    with the softmmu version.  */ | ||||||
|  |  | ||||||
| #if HAVE_ATOMIC128 || HAVE_CMPXCHG128 | #ifdef CONFIG_ATOMIC128 | ||||||
|  |  | ||||||
| #undef EXTRA_ARGS | #undef EXTRA_ARGS | ||||||
| #undef ATOMIC_NAME | #undef ATOMIC_NAME | ||||||
| @@ -742,4 +628,4 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, | |||||||
|  |  | ||||||
| #define DATA_SIZE 16 | #define DATA_SIZE 16 | ||||||
| #include "atomic_template.h" | #include "atomic_template.h" | ||||||
| #endif | #endif /* CONFIG_ATOMIC128 */ | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								arch_init.c
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								arch_init.c
									
									
									
									
									
								
							| @@ -29,7 +29,6 @@ | |||||||
| #include "hw/pci/pci.h" | #include "hw/pci/pci.h" | ||||||
| #include "hw/audio/soundhw.h" | #include "hw/audio/soundhw.h" | ||||||
| #include "qapi/qapi-commands-misc.h" | #include "qapi/qapi-commands-misc.h" | ||||||
| #include "qapi/error.h" |  | ||||||
| #include "qemu/config-file.h" | #include "qemu/config-file.h" | ||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
| #include "hw/acpi/acpi.h" | #include "hw/acpi/acpi.h" | ||||||
| @@ -52,14 +51,14 @@ int graphic_depth = 32; | |||||||
| #define QEMU_ARCH QEMU_ARCH_ARM | #define QEMU_ARCH QEMU_ARCH_ARM | ||||||
| #elif defined(TARGET_CRIS) | #elif defined(TARGET_CRIS) | ||||||
| #define QEMU_ARCH QEMU_ARCH_CRIS | #define QEMU_ARCH QEMU_ARCH_CRIS | ||||||
| #elif defined(TARGET_HPPA) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_HPPA |  | ||||||
| #elif defined(TARGET_I386) | #elif defined(TARGET_I386) | ||||||
| #define QEMU_ARCH QEMU_ARCH_I386 | #define QEMU_ARCH QEMU_ARCH_I386 | ||||||
| #elif defined(TARGET_LM32) | #elif defined(TARGET_HPPA) | ||||||
| #define QEMU_ARCH QEMU_ARCH_LM32 | #define QEMU_ARCH QEMU_ARCH_HPPA | ||||||
| #elif defined(TARGET_M68K) | #elif defined(TARGET_M68K) | ||||||
| #define QEMU_ARCH QEMU_ARCH_M68K | #define QEMU_ARCH QEMU_ARCH_M68K | ||||||
|  | #elif defined(TARGET_LM32) | ||||||
|  | #define QEMU_ARCH QEMU_ARCH_LM32 | ||||||
| #elif defined(TARGET_MICROBLAZE) | #elif defined(TARGET_MICROBLAZE) | ||||||
| #define QEMU_ARCH QEMU_ARCH_MICROBLAZE | #define QEMU_ARCH QEMU_ARCH_MICROBLAZE | ||||||
| #elif defined(TARGET_MIPS) | #elif defined(TARGET_MIPS) | ||||||
| @@ -80,12 +79,12 @@ int graphic_depth = 32; | |||||||
| #define QEMU_ARCH QEMU_ARCH_SH4 | #define QEMU_ARCH QEMU_ARCH_SH4 | ||||||
| #elif defined(TARGET_SPARC) | #elif defined(TARGET_SPARC) | ||||||
| #define QEMU_ARCH QEMU_ARCH_SPARC | #define QEMU_ARCH QEMU_ARCH_SPARC | ||||||
| #elif defined(TARGET_TRICORE) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_TRICORE |  | ||||||
| #elif defined(TARGET_UNICORE32) |  | ||||||
| #define QEMU_ARCH QEMU_ARCH_UNICORE32 |  | ||||||
| #elif defined(TARGET_XTENSA) | #elif defined(TARGET_XTENSA) | ||||||
| #define QEMU_ARCH QEMU_ARCH_XTENSA | #define QEMU_ARCH QEMU_ARCH_XTENSA | ||||||
|  | #elif defined(TARGET_UNICORE32) | ||||||
|  | #define QEMU_ARCH QEMU_ARCH_UNICORE32 | ||||||
|  | #elif defined(TARGET_TRICORE) | ||||||
|  | #define QEMU_ARCH QEMU_ARCH_TRICORE | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| const uint32_t arch_type = QEMU_ARCH; | const uint32_t arch_type = QEMU_ARCH; | ||||||
| @@ -113,8 +112,7 @@ TargetInfo *qmp_query_target(Error **errp) | |||||||
| { | { | ||||||
|     TargetInfo *info = g_malloc0(sizeof(*info)); |     TargetInfo *info = g_malloc0(sizeof(*info)); | ||||||
|  |  | ||||||
|     info->arch = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, -1, |     info->arch = g_strdup(TARGET_NAME); | ||||||
|                                  &error_abort); |  | ||||||
|  |  | ||||||
|     return info; |     return info; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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_SPICE) += spiceaudio.o | ||||||
| common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o | common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o | ||||||
| common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o | common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o | ||||||
|   | |||||||
| @@ -28,14 +28,35 @@ | |||||||
| #include "audio.h" | #include "audio.h" | ||||||
| #include "trace.h" | #include "trace.h" | ||||||
|  |  | ||||||
|  | #if QEMU_GNUC_PREREQ(4, 3) | ||||||
| #pragma GCC diagnostic ignored "-Waddress" | #pragma GCC diagnostic ignored "-Waddress" | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #define AUDIO_CAP "alsa" | #define AUDIO_CAP "alsa" | ||||||
| #include "audio_int.h" | #include "audio_int.h" | ||||||
|  |  | ||||||
|  | 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 { | struct pollhlp { | ||||||
|     snd_pcm_t *handle; |     snd_pcm_t *handle; | ||||||
|     struct pollfd *pfds; |     struct pollfd *pfds; | ||||||
|  |     ALSAConf *conf; | ||||||
|     int count; |     int count; | ||||||
|     int mask; |     int mask; | ||||||
| }; | }; | ||||||
| @@ -47,7 +68,6 @@ typedef struct ALSAVoiceOut { | |||||||
|     void *pcm_buf; |     void *pcm_buf; | ||||||
|     snd_pcm_t *handle; |     snd_pcm_t *handle; | ||||||
|     struct pollhlp pollhlp; |     struct pollhlp pollhlp; | ||||||
|     Audiodev *dev; |  | ||||||
| } ALSAVoiceOut; | } ALSAVoiceOut; | ||||||
|  |  | ||||||
| typedef struct ALSAVoiceIn { | typedef struct ALSAVoiceIn { | ||||||
| @@ -55,18 +75,21 @@ typedef struct ALSAVoiceIn { | |||||||
|     snd_pcm_t *handle; |     snd_pcm_t *handle; | ||||||
|     void *pcm_buf; |     void *pcm_buf; | ||||||
|     struct pollhlp pollhlp; |     struct pollhlp pollhlp; | ||||||
|     Audiodev *dev; |  | ||||||
| } ALSAVoiceIn; | } ALSAVoiceIn; | ||||||
|  |  | ||||||
| struct alsa_params_req { | struct alsa_params_req { | ||||||
|     int freq; |     int freq; | ||||||
|     snd_pcm_format_t fmt; |     snd_pcm_format_t fmt; | ||||||
|     int nchannels; |     int nchannels; | ||||||
|  |     int size_in_usec; | ||||||
|  |     int override_mask; | ||||||
|  |     unsigned int buffer_size; | ||||||
|  |     unsigned int period_size; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct alsa_params_obt { | struct alsa_params_obt { | ||||||
|     int freq; |     int freq; | ||||||
|     AudioFormat fmt; |     audfmt_e fmt; | ||||||
|     int endianness; |     int endianness; | ||||||
|     int nchannels; |     int nchannels; | ||||||
|     snd_pcm_uframes_t samples; |     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); |     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) { |     switch (fmt) { | ||||||
|     case AUDIO_FORMAT_S8: |     case AUD_FMT_S8: | ||||||
|         return SND_PCM_FORMAT_S8; |         return SND_PCM_FORMAT_S8; | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_U8: |     case AUD_FMT_U8: | ||||||
|         return SND_PCM_FORMAT_U8; |         return SND_PCM_FORMAT_U8; | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_S16: |     case AUD_FMT_S16: | ||||||
|         if (endianness) { |         if (endianness) { | ||||||
|             return SND_PCM_FORMAT_S16_BE; |             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; |             return SND_PCM_FORMAT_S16_LE; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_U16: |     case AUD_FMT_U16: | ||||||
|         if (endianness) { |         if (endianness) { | ||||||
|             return SND_PCM_FORMAT_U16_BE; |             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; |             return SND_PCM_FORMAT_U16_LE; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_S32: |     case AUD_FMT_S32: | ||||||
|         if (endianness) { |         if (endianness) { | ||||||
|             return SND_PCM_FORMAT_S32_BE; |             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; |             return SND_PCM_FORMAT_S32_LE; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_U32: |     case AUD_FMT_U32: | ||||||
|         if (endianness) { |         if (endianness) { | ||||||
|             return SND_PCM_FORMAT_U32_BE; |             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) |                            int *endianness) | ||||||
| { | { | ||||||
|     switch (alsafmt) { |     switch (alsafmt) { | ||||||
|     case SND_PCM_FORMAT_S8: |     case SND_PCM_FORMAT_S8: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_S8; |         *fmt = AUD_FMT_S8; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case SND_PCM_FORMAT_U8: |     case SND_PCM_FORMAT_U8: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_U8; |         *fmt = AUD_FMT_U8; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case SND_PCM_FORMAT_S16_LE: |     case SND_PCM_FORMAT_S16_LE: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_S16; |         *fmt = AUD_FMT_S16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case SND_PCM_FORMAT_U16_LE: |     case SND_PCM_FORMAT_U16_LE: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_U16; |         *fmt = AUD_FMT_U16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case SND_PCM_FORMAT_S16_BE: |     case SND_PCM_FORMAT_S16_BE: | ||||||
|         *endianness = 1; |         *endianness = 1; | ||||||
|         *fmt = AUDIO_FORMAT_S16; |         *fmt = AUD_FMT_S16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case SND_PCM_FORMAT_U16_BE: |     case SND_PCM_FORMAT_U16_BE: | ||||||
|         *endianness = 1; |         *endianness = 1; | ||||||
|         *fmt = AUDIO_FORMAT_U16; |         *fmt = AUD_FMT_U16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case SND_PCM_FORMAT_S32_LE: |     case SND_PCM_FORMAT_S32_LE: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_S32; |         *fmt = AUD_FMT_S32; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case SND_PCM_FORMAT_U32_LE: |     case SND_PCM_FORMAT_U32_LE: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_U32; |         *fmt = AUD_FMT_U32; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case SND_PCM_FORMAT_S32_BE: |     case SND_PCM_FORMAT_S32_BE: | ||||||
|         *endianness = 1; |         *endianness = 1; | ||||||
|         *fmt = AUDIO_FORMAT_S32; |         *fmt = AUD_FMT_S32; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case SND_PCM_FORMAT_U32_BE: |     case SND_PCM_FORMAT_U32_BE: | ||||||
|         *endianness = 1; |         *endianness = 1; | ||||||
|         *fmt = AUDIO_FORMAT_U32; |         *fmt = AUD_FMT_U32; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     default: |     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, | static void alsa_dump_info (struct alsa_params_req *req, | ||||||
|                             struct alsa_params_obt *obt, |                             struct alsa_params_obt *obt, | ||||||
|                             snd_pcm_format_t obtfmt, |                             snd_pcm_format_t obtfmt) | ||||||
|                             AudiodevAlsaPerDirectionOptions *apdo) |  | ||||||
| { | { | ||||||
|     dolog("parameter | requested value | obtained value\n"); |     dolog ("parameter | requested value | obtained value\n"); | ||||||
|     dolog("format    |      %10d |     %10d\n", req->fmt, obtfmt); |     dolog ("format    |      %10d |     %10d\n", req->fmt, obtfmt); | ||||||
|     dolog("channels  |      %10d |     %10d\n", |     dolog ("channels  |      %10d |     %10d\n", | ||||||
|           req->nchannels, obt->nchannels); |            req->nchannels, obt->nchannels); | ||||||
|     dolog("frequency |      %10d |     %10d\n", req->freq, obt->freq); |     dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq); | ||||||
|     dolog("============================================\n"); |     dolog ("============================================\n"); | ||||||
|     dolog("requested: buffer len %" PRId32 " period len %" PRId32 "\n", |     dolog ("requested: buffer size %d period size %d\n", | ||||||
|           apdo->buffer_length, apdo->period_length); |            req->buffer_size, req->period_size); | ||||||
|     dolog("obtained: samples %ld\n", obt->samples); |     dolog ("obtained: samples %ld\n", obt->samples); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold) | 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, | static int alsa_open (int in, struct alsa_params_req *req, | ||||||
|                      struct alsa_params_obt *obt, snd_pcm_t **handlep, |                       struct alsa_params_obt *obt, snd_pcm_t **handlep, | ||||||
|                      Audiodev *dev) |                       ALSAConf *conf) | ||||||
| { | { | ||||||
|     AudiodevAlsaOptions *aopts = &dev->u.alsa; |  | ||||||
|     AudiodevAlsaPerDirectionOptions *apdo = in ? aopts->in : aopts->out; |  | ||||||
|     snd_pcm_t *handle; |     snd_pcm_t *handle; | ||||||
|     snd_pcm_hw_params_t *hw_params; |     snd_pcm_hw_params_t *hw_params; | ||||||
|     int err; |     int err; | ||||||
|  |     int size_in_usec; | ||||||
|     unsigned int freq, nchannels; |     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; |     snd_pcm_uframes_t obt_buffer_size; | ||||||
|     const char *typ = in ? "ADC" : "DAC"; |     const char *typ = in ? "ADC" : "DAC"; | ||||||
|     snd_pcm_format_t obtfmt; |     snd_pcm_format_t obtfmt; | ||||||
|  |  | ||||||
|     freq = req->freq; |     freq = req->freq; | ||||||
|     nchannels = req->nchannels; |     nchannels = req->nchannels; | ||||||
|  |     size_in_usec = req->size_in_usec; | ||||||
|  |  | ||||||
|     snd_pcm_hw_params_alloca (&hw_params); |     snd_pcm_hw_params_alloca (&hw_params); | ||||||
|  |  | ||||||
| @@ -507,42 +529,79 @@ static int alsa_open(bool in, struct alsa_params_req *req, | |||||||
|         goto err; |         goto err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (apdo->buffer_length) { |     if (req->buffer_size) { | ||||||
|         int dir = 0; |         unsigned long obt; | ||||||
|         unsigned int btime = apdo->buffer_length; |  | ||||||
|  |  | ||||||
|         err = snd_pcm_hw_params_set_buffer_time_near( |         if (size_in_usec) { | ||||||
|             handle, hw_params, &btime, &dir); |             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) { |         if (err < 0) { | ||||||
|             alsa_logerr2(err, typ, "Failed to set buffer time to %" PRId32 "\n", |             alsa_logerr2 (err, typ, "Failed to set buffer %s to %d\n", | ||||||
|                          apdo->buffer_length); |                           size_in_usec ? "time" : "size", req->buffer_size); | ||||||
|             goto err; |             goto err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (apdo->has_buffer_length && btime != apdo->buffer_length) { |         if ((req->override_mask & 2) && (obt - req->buffer_size)) | ||||||
|             dolog("Requested buffer time %" PRId32 |             dolog ("Requested buffer %s %u was rejected, using %lu\n", | ||||||
|                   " was rejected, using %u\n", apdo->buffer_length, btime); |                    size_in_usec ? "time" : "size", req->buffer_size, obt); | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (apdo->period_length) { |     if (req->period_size) { | ||||||
|         int dir = 0; |         unsigned long obt; | ||||||
|         unsigned int ptime = apdo->period_length; |  | ||||||
|  |  | ||||||
|         err = snd_pcm_hw_params_set_period_time_near(handle, hw_params, &ptime, |         if (size_in_usec) { | ||||||
|                                                      &dir); |             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) { |         if (err < 0) { | ||||||
|             alsa_logerr2(err, typ, "Failed to set period time to %" PRId32 "\n", |             alsa_logerr2 (err, typ, "Failed to set period %s to %d\n", | ||||||
|                          apdo->period_length); |                           size_in_usec ? "time" : "size", req->period_size); | ||||||
|             goto err; |             goto err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (apdo->has_period_length && ptime != apdo->period_length) { |         if (((req->override_mask & 1) && (obt - req->period_size))) | ||||||
|             dolog("Requested period time %" PRId32 " was rejected, using %d\n", |             dolog ("Requested period %s %u was rejected, using %lu\n", | ||||||
|                   apdo->period_length, ptime); |                    size_in_usec ? "time" : "size", req->period_size, obt); | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     err = snd_pcm_hw_params (handle, hw_params); |     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; |         goto err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!in && aopts->has_threshold && aopts->threshold) { |     if (!in && conf->threshold) { | ||||||
|         struct audsettings as = { .freq = freq }; |         snd_pcm_uframes_t threshold; | ||||||
|         alsa_set_threshold( |         int bytes_per_sec; | ||||||
|             handle, |  | ||||||
|             audio_buffer_frames(qapi_AudiodevAlsaPerDirectionOptions_base(apdo), |         bytes_per_sec = freq << (nchannels == 2); | ||||||
|                                 &as, aopts->threshold)); |  | ||||||
|  |         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; |     obt->nchannels = nchannels; | ||||||
| @@ -592,11 +669,11 @@ static int alsa_open(bool in, struct alsa_params_req *req, | |||||||
|          obt->nchannels != req->nchannels || |          obt->nchannels != req->nchannels || | ||||||
|          obt->freq != req->freq) { |          obt->freq != req->freq) { | ||||||
|         dolog ("Audio parameters for %s\n", typ); |         dolog ("Audio parameters for %s\n", typ); | ||||||
|         alsa_dump_info(req, obt, obtfmt, apdo); |         alsa_dump_info (req, obt, obtfmt); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #ifdef DEBUG | #ifdef DEBUG | ||||||
|     alsa_dump_info(req, obt, obtfmt, pdo); |     alsa_dump_info (req, obt, obtfmt); | ||||||
| #endif | #endif | ||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
| @@ -722,13 +799,19 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     struct alsa_params_obt obt; |     struct alsa_params_obt obt; | ||||||
|     snd_pcm_t *handle; |     snd_pcm_t *handle; | ||||||
|     struct audsettings obt_as; |     struct audsettings obt_as; | ||||||
|     Audiodev *dev = drv_opaque; |     ALSAConf *conf = drv_opaque; | ||||||
|  |  | ||||||
|     req.fmt = aud_to_alsafmt (as->fmt, as->endianness); |     req.fmt = aud_to_alsafmt (as->fmt, as->endianness); | ||||||
|     req.freq = as->freq; |     req.freq = as->freq; | ||||||
|     req.nchannels = as->nchannels; |     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; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -749,7 +832,7 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     alsa->handle = handle; |     alsa->handle = handle; | ||||||
|     alsa->dev = dev; |     alsa->pollhlp.conf = conf; | ||||||
|     return 0; |     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, ...) | static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||||
| { | { | ||||||
|     ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; |     ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; | ||||||
|     AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.out; |  | ||||||
|  |  | ||||||
|     switch (cmd) { |     switch (cmd) { | ||||||
|     case VOICE_ENABLE: |     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"); |             ldebug ("enabling voice\n"); | ||||||
|             if (poll_mode && alsa_poll_out (hw)) { |             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; |     struct alsa_params_obt obt; | ||||||
|     snd_pcm_t *handle; |     snd_pcm_t *handle; | ||||||
|     struct audsettings obt_as; |     struct audsettings obt_as; | ||||||
|     Audiodev *dev = drv_opaque; |     ALSAConf *conf = drv_opaque; | ||||||
|  |  | ||||||
|     req.fmt = aud_to_alsafmt (as->fmt, as->endianness); |     req.fmt = aud_to_alsafmt (as->fmt, as->endianness); | ||||||
|     req.freq = as->freq; |     req.freq = as->freq; | ||||||
|     req.nchannels = as->nchannels; |     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; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -850,7 +943,7 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     alsa->handle = handle; |     alsa->handle = handle; | ||||||
|     alsa->dev = dev; |     alsa->pollhlp.conf = conf; | ||||||
|     return 0; |     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, ...) | static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||||
| { | { | ||||||
|     ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; |     ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; | ||||||
|     AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.in; |  | ||||||
|  |  | ||||||
|     switch (cmd) { |     switch (cmd) { | ||||||
|     case VOICE_ENABLE: |     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"); |             ldebug ("enabling voice\n"); | ||||||
|             if (poll_mode && alsa_poll_in (hw)) { |             if (poll_mode && alsa_poll_in (hw)) { | ||||||
| @@ -1020,54 +1117,88 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) | |||||||
|     return -1; |     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) { |     ALSAConf *conf = g_malloc(sizeof(ALSAConf)); | ||||||
|         apdo->try_poll = true; |     *conf = glob_conf; | ||||||
|         apdo->has_try_poll = true; |     return conf; | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| 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; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void alsa_audio_fini (void *opaque) | 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 = { | static struct audio_pcm_ops alsa_pcm_ops = { | ||||||
|     .init_out = alsa_init_out, |     .init_out = alsa_init_out, | ||||||
|     .fini_out = alsa_fini_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 = { | static struct audio_driver alsa_audio_driver = { | ||||||
|     .name           = "alsa", |     .name           = "alsa", | ||||||
|     .descr          = "ALSA http://www.alsa-project.org", |     .descr          = "ALSA http://www.alsa-project.org", | ||||||
|  |     .options        = alsa_options, | ||||||
|     .init           = alsa_audio_init, |     .init           = alsa_audio_init, | ||||||
|     .fini           = alsa_audio_fini, |     .fini           = alsa_audio_fini, | ||||||
|     .pcm_ops        = &alsa_pcm_ops, |     .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 | #define QEMU_AUDIO_H | ||||||
|  |  | ||||||
| #include "qemu/queue.h" | #include "qemu/queue.h" | ||||||
| #include "qapi/qapi-types-audio.h" |  | ||||||
|  |  | ||||||
| typedef void (*audio_callback_fn) (void *opaque, int avail); | 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 | #ifdef HOST_WORDS_BIGENDIAN | ||||||
| #define AUDIO_HOST_ENDIANNESS 1 | #define AUDIO_HOST_ENDIANNESS 1 | ||||||
| #else | #else | ||||||
| #define AUDIO_HOST_ENDIANNESS 0 | #define AUDIO_HOST_ENDIANNESS 0 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| typedef struct audsettings { | struct audsettings { | ||||||
|     int freq; |     int freq; | ||||||
|     int nchannels; |     int nchannels; | ||||||
|     AudioFormat fmt; |     audfmt_e fmt; | ||||||
|     int endianness; |     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 { | typedef enum { | ||||||
|     AUD_CNOTIFY_ENABLE, |     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_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_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_register_card (const char *name, QEMUSoundCard *card); | ||||||
| void AUD_remove_card (QEMUSoundCard *card); | void AUD_remove_card (QEMUSoundCard *card); | ||||||
| CaptureVoiceOut *AUD_add_capture ( | 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, | void audio_sample_from_uint64(void *samples, int pos, | ||||||
|                             uint64_t left, uint64_t right); |                             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 */ | #endif /* QEMU_AUDIO_H */ | ||||||
|   | |||||||
| @@ -33,6 +33,22 @@ | |||||||
|  |  | ||||||
| struct audio_pcm_ops; | 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 { | struct audio_callback { | ||||||
|     void *opaque; |     void *opaque; | ||||||
|     audio_callback_fn fn; |     audio_callback_fn fn; | ||||||
| @@ -129,7 +145,8 @@ typedef struct audio_driver audio_driver; | |||||||
| struct audio_driver { | struct audio_driver { | ||||||
|     const char *name; |     const char *name; | ||||||
|     const char *descr; |     const char *descr; | ||||||
|     void *(*init) (Audiodev *); |     struct audio_option *options; | ||||||
|  |     void *(*init) (void); | ||||||
|     void (*fini) (void *); |     void (*fini) (void *); | ||||||
|     struct audio_pcm_ops *pcm_ops; |     struct audio_pcm_ops *pcm_ops; | ||||||
|     int can_be_default; |     int can_be_default; | ||||||
| @@ -174,9 +191,8 @@ struct SWVoiceCap { | |||||||
|     QLIST_ENTRY (SWVoiceCap) entries; |     QLIST_ENTRY (SWVoiceCap) entries; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef struct AudioState { | struct AudioState { | ||||||
|     struct audio_driver *drv; |     struct audio_driver *drv; | ||||||
|     Audiodev *dev; |  | ||||||
|     void *drv_opaque; |     void *drv_opaque; | ||||||
|  |  | ||||||
|     QEMUTimer *ts; |     QEMUTimer *ts; | ||||||
| @@ -187,13 +203,10 @@ typedef struct AudioState { | |||||||
|     int nb_hw_voices_out; |     int nb_hw_voices_out; | ||||||
|     int nb_hw_voices_in; |     int nb_hw_voices_in; | ||||||
|     int vm_running; |     int vm_running; | ||||||
|     int64_t period_ticks; | }; | ||||||
| } AudioState; |  | ||||||
|  |  | ||||||
| extern const struct mixeng_volume nominal_volume; | extern const struct mixeng_volume nominal_volume; | ||||||
|  |  | ||||||
| extern const char *audio_prio_list[]; |  | ||||||
|  |  | ||||||
| void audio_driver_register(audio_driver *drv); | void audio_driver_register(audio_driver *drv); | ||||||
| audio_driver *audio_driver_lookup(const char *name); | 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) #n | ||||||
| #define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(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 */ | #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; |     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) | static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as) | ||||||
| { | { | ||||||
|     HW *hw; |     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); |         hw = glue (audio_pcm_hw_add_new_, TYPE) (as); | ||||||
|         if (hw) { |         if (hw) { | ||||||
|             return hw; |             return hw; | ||||||
| @@ -362,11 +331,9 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) ( | |||||||
|     SW *sw; |     SW *sw; | ||||||
|     HW *hw; |     HW *hw; | ||||||
|     struct audsettings hw_as; |     struct audsettings hw_as; | ||||||
|     AudioState *s = &glob_audio_state; |  | ||||||
|     AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev); |  | ||||||
|  |  | ||||||
|     if (pdo->fixed_settings) { |     if (glue (conf.fixed_, TYPE).enabled) { | ||||||
|         hw_as = audiodev_to_audsettings(pdo); |         hw_as = glue (conf.fixed_, TYPE).settings; | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
|         hw_as = *as; |         hw_as = *as; | ||||||
| @@ -431,7 +398,6 @@ SW *glue (AUD_open_, TYPE) ( | |||||||
|     ) |     ) | ||||||
| { | { | ||||||
|     AudioState *s = &glob_audio_state; |     AudioState *s = &glob_audio_state; | ||||||
|     AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev); |  | ||||||
|  |  | ||||||
|     if (audio_bug(__func__, !card || !name || !callback_fn || !as)) { |     if (audio_bug(__func__, !card || !name || !callback_fn || !as)) { | ||||||
|         dolog ("card=%p name=%p callback_fn=%p as=%p\n", |         dolog ("card=%p name=%p callback_fn=%p as=%p\n", | ||||||
| @@ -456,7 +422,7 @@ SW *glue (AUD_open_, TYPE) ( | |||||||
|         return sw; |         return sw; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!pdo->fixed_settings && sw) { |     if (!glue (conf.fixed_, TYPE).enabled && sw) { | ||||||
|         glue (AUD_close_, TYPE) (card, sw); |         glue (AUD_close_, TYPE) (card, sw); | ||||||
|         sw = NULL; |         sw = NULL; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -24,20 +24,20 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx, | |||||||
|     wfx->cbSize = 0; |     wfx->cbSize = 0; | ||||||
|  |  | ||||||
|     switch (as->fmt) { |     switch (as->fmt) { | ||||||
|     case AUDIO_FORMAT_S8: |     case AUD_FMT_S8: | ||||||
|     case AUDIO_FORMAT_U8: |     case AUD_FMT_U8: | ||||||
|         wfx->wBitsPerSample = 8; |         wfx->wBitsPerSample = 8; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_S16: |     case AUD_FMT_S16: | ||||||
|     case AUDIO_FORMAT_U16: |     case AUD_FMT_U16: | ||||||
|         wfx->wBitsPerSample = 16; |         wfx->wBitsPerSample = 16; | ||||||
|         wfx->nAvgBytesPerSec <<= 1; |         wfx->nAvgBytesPerSec <<= 1; | ||||||
|         wfx->nBlockAlign <<= 1; |         wfx->nBlockAlign <<= 1; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_S32: |     case AUD_FMT_S32: | ||||||
|     case AUDIO_FORMAT_U32: |     case AUD_FMT_U32: | ||||||
|         wfx->wBitsPerSample = 32; |         wfx->wBitsPerSample = 32; | ||||||
|         wfx->nAvgBytesPerSec <<= 2; |         wfx->nAvgBytesPerSec <<= 2; | ||||||
|         wfx->nBlockAlign <<= 2; |         wfx->nBlockAlign <<= 2; | ||||||
| @@ -85,15 +85,15 @@ int waveformat_to_audio_settings (WAVEFORMATEX *wfx, | |||||||
|  |  | ||||||
|     switch (wfx->wBitsPerSample) { |     switch (wfx->wBitsPerSample) { | ||||||
|     case 8: |     case 8: | ||||||
|         as->fmt = AUDIO_FORMAT_U8; |         as->fmt = AUD_FMT_U8; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case 16: |     case 16: | ||||||
|         as->fmt = AUDIO_FORMAT_S16; |         as->fmt = AUD_FMT_S16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case 32: |     case 32: | ||||||
|         as->fmt = AUDIO_FORMAT_S32; |         as->fmt = AUD_FMT_S32; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     default: |     default: | ||||||
|   | |||||||
| @@ -36,6 +36,11 @@ | |||||||
| #define MAC_OS_X_VERSION_10_6 1060 | #define MAC_OS_X_VERSION_10_6 1060 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | typedef struct { | ||||||
|  |     int buffer_frames; | ||||||
|  |     int nbuffers; | ||||||
|  | } CoreaudioConf; | ||||||
|  |  | ||||||
| typedef struct coreaudioVoiceOut { | typedef struct coreaudioVoiceOut { | ||||||
|     HWVoiceOut hw; |     HWVoiceOut hw; | ||||||
|     pthread_mutex_t mutex; |     pthread_mutex_t mutex; | ||||||
| @@ -502,9 +507,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     int err; |     int err; | ||||||
|     const char *typ = "playback"; |     const char *typ = "playback"; | ||||||
|     AudioValueRange frameRange; |     AudioValueRange frameRange; | ||||||
|     Audiodev *dev = drv_opaque; |     CoreaudioConf *conf = drv_opaque; | ||||||
|     AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out; |  | ||||||
|     int frames; |  | ||||||
|  |  | ||||||
|     /* create mutex */ |     /* create mutex */ | ||||||
|     err = pthread_mutex_init(&core->mutex, NULL); |     err = pthread_mutex_init(&core->mutex, NULL); | ||||||
| @@ -535,17 +538,16 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     frames = audio_buffer_frames( |     if (frameRange.mMinimum > conf->buffer_frames) { | ||||||
|         qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610); |  | ||||||
|     if (frameRange.mMinimum > frames) { |  | ||||||
|         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; |         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; | ||||||
|         dolog ("warning: Upsizing Buffer Frames to %f\n", 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; |         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; | ||||||
|         dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); |         dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
|         core->audioDevicePropertyBufferFrameSize = frames; |         core->audioDevicePropertyBufferFrameSize = conf->buffer_frames; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* set Buffer Frame Size */ |     /* 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"); |                            "Could not get device buffer frame size\n"); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|     hw->samples = (cpdo->has_buffer_count ? cpdo->buffer_count : 4) * |     hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize; | ||||||
|         core->audioDevicePropertyBufferFrameSize; |  | ||||||
|  |  | ||||||
|     /* get StreamFormat */ |     /* get StreamFormat */ | ||||||
|     status = coreaudio_get_streamformat(core->outputDeviceID, |     status = coreaudio_get_streamformat(core->outputDeviceID, | ||||||
| @@ -679,15 +680,40 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) | |||||||
|     return 0; |     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) | 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 = { | static struct audio_pcm_ops coreaudio_pcm_ops = { | ||||||
|     .init_out = coreaudio_init_out, |     .init_out = coreaudio_init_out, | ||||||
|     .fini_out = coreaudio_fini_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 = { | static struct audio_driver coreaudio_audio_driver = { | ||||||
|     .name           = "coreaudio", |     .name           = "coreaudio", | ||||||
|     .descr          = "CoreAudio http://developer.apple.com/audio/coreaudio.html", |     .descr          = "CoreAudio http://developer.apple.com/audio/coreaudio.html", | ||||||
|  |     .options        = coreaudio_options, | ||||||
|     .init           = coreaudio_audio_init, |     .init           = coreaudio_audio_init, | ||||||
|     .fini           = coreaudio_audio_fini, |     .fini           = coreaudio_audio_fini, | ||||||
|     .pcm_ops        = &coreaudio_pcm_ops, |     .pcm_ops        = &coreaudio_pcm_ops, | ||||||
|   | |||||||
| @@ -167,18 +167,17 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     dsound *s = drv_opaque; |     dsound *s = drv_opaque; | ||||||
|     WAVEFORMATEX wfx; |     WAVEFORMATEX wfx; | ||||||
|     struct audsettings obt_as; |     struct audsettings obt_as; | ||||||
|  |     DSoundConf *conf = &s->conf; | ||||||
| #ifdef DSBTYPE_IN | #ifdef DSBTYPE_IN | ||||||
|     const char *typ = "ADC"; |     const char *typ = "ADC"; | ||||||
|     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; |     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; | ||||||
|     DSCBUFFERDESC bd; |     DSCBUFFERDESC bd; | ||||||
|     DSCBCAPS bc; |     DSCBCAPS bc; | ||||||
|     AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.in; |  | ||||||
| #else | #else | ||||||
|     const char *typ = "DAC"; |     const char *typ = "DAC"; | ||||||
|     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; |     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; | ||||||
|     DSBUFFERDESC bd; |     DSBUFFERDESC bd; | ||||||
|     DSBCAPS bc; |     DSBCAPS bc; | ||||||
|     AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.out; |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|     if (!s->FIELD2) { |     if (!s->FIELD2) { | ||||||
| @@ -194,8 +193,8 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     memset (&bd, 0, sizeof (bd)); |     memset (&bd, 0, sizeof (bd)); | ||||||
|     bd.dwSize = sizeof (bd); |     bd.dwSize = sizeof (bd); | ||||||
|     bd.lpwfxFormat = &wfx; |     bd.lpwfxFormat = &wfx; | ||||||
|     bd.dwBufferBytes = audio_buffer_bytes(pdo, as, 92880); |  | ||||||
| #ifdef DSBTYPE_IN | #ifdef DSBTYPE_IN | ||||||
|  |     bd.dwBufferBytes = conf->bufsize_in; | ||||||
|     hr = IDirectSoundCapture_CreateCaptureBuffer ( |     hr = IDirectSoundCapture_CreateCaptureBuffer ( | ||||||
|         s->dsound_capture, |         s->dsound_capture, | ||||||
|         &bd, |         &bd, | ||||||
| @@ -204,6 +203,7 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|         ); |         ); | ||||||
| #else | #else | ||||||
|     bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; |     bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; | ||||||
|  |     bd.dwBufferBytes = conf->bufsize_out; | ||||||
|     hr = IDirectSound_CreateSoundBuffer ( |     hr = IDirectSound_CreateSoundBuffer ( | ||||||
|         s->dsound, |         s->dsound, | ||||||
|         &bd, |         &bd, | ||||||
|   | |||||||
| @@ -32,7 +32,6 @@ | |||||||
|  |  | ||||||
| #define AUDIO_CAP "dsound" | #define AUDIO_CAP "dsound" | ||||||
| #include "audio_int.h" | #include "audio_int.h" | ||||||
| #include "qemu/host-utils.h" |  | ||||||
|  |  | ||||||
| #include <windows.h> | #include <windows.h> | ||||||
| #include <mmsystem.h> | #include <mmsystem.h> | ||||||
| @@ -43,11 +42,17 @@ | |||||||
|  |  | ||||||
| /* #define DEBUG_DSOUND */ | /* #define DEBUG_DSOUND */ | ||||||
|  |  | ||||||
|  | typedef struct { | ||||||
|  |     int bufsize_in; | ||||||
|  |     int bufsize_out; | ||||||
|  |     int latency_millis; | ||||||
|  | } DSoundConf; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     LPDIRECTSOUND dsound; |     LPDIRECTSOUND dsound; | ||||||
|     LPDIRECTSOUNDCAPTURE dsound_capture; |     LPDIRECTSOUNDCAPTURE dsound_capture; | ||||||
|     struct audsettings settings; |     struct audsettings settings; | ||||||
|     Audiodev *dev; |     DSoundConf conf; | ||||||
| } dsound; | } dsound; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
| @@ -243,9 +248,9 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 ( | |||||||
|     dsound_log_hresult (hr); |     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 | #ifdef DEBUG_DSOUND | ||||||
| @@ -473,7 +478,7 @@ static int dsound_run_out (HWVoiceOut *hw, int live) | |||||||
|     LPVOID p1, p2; |     LPVOID p1, p2; | ||||||
|     int bufsize; |     int bufsize; | ||||||
|     dsound *s = ds->s; |     dsound *s = ds->s; | ||||||
|     AudiodevDsoundOptions *dso = &s->dev->u.dsound; |     DSoundConf *conf = &s->conf; | ||||||
|  |  | ||||||
|     if (!dsb) { |     if (!dsb) { | ||||||
|         dolog ("Attempt to run empty with playback buffer\n"); |         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; |     len = live << hwshift; | ||||||
|  |  | ||||||
|     if (ds->first_time) { |     if (ds->first_time) { | ||||||
|         if (dso->latency) { |         if (conf->latency_millis) { | ||||||
|             DWORD cur_blat; |             DWORD cur_blat; | ||||||
|  |  | ||||||
|             cur_blat = audio_ring_dist (wpos, ppos, bufsize); |             cur_blat = audio_ring_dist (wpos, ppos, bufsize); | ||||||
|             ds->first_time = 0; |             ds->first_time = 0; | ||||||
|             old_pos = wpos; |             old_pos = wpos; | ||||||
|             old_pos += |             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 %= bufsize; | ||||||
|             old_pos &= ~hw->info.align; |             old_pos &= ~hw->info.align; | ||||||
|         } |         } | ||||||
| @@ -742,6 +747,12 @@ static int dsound_run_in (HWVoiceIn *hw) | |||||||
|     return decr; |     return decr; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static DSoundConf glob_conf = { | ||||||
|  |     .bufsize_in         = 16384, | ||||||
|  |     .bufsize_out        = 16384, | ||||||
|  |     .latency_millis     = 10 | ||||||
|  | }; | ||||||
|  |  | ||||||
| static void dsound_audio_fini (void *opaque) | static void dsound_audio_fini (void *opaque) | ||||||
| { | { | ||||||
|     HRESULT hr; |     HRESULT hr; | ||||||
| @@ -772,22 +783,13 @@ static void dsound_audio_fini (void *opaque) | |||||||
|     g_free(s); |     g_free(s); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void *dsound_audio_init(Audiodev *dev) | static void *dsound_audio_init (void) | ||||||
| { | { | ||||||
|     int err; |     int err; | ||||||
|     HRESULT hr; |     HRESULT hr; | ||||||
|     dsound *s = g_malloc0(sizeof(dsound)); |     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); |     hr = CoInitialize (NULL); | ||||||
|     if (FAILED (hr)) { |     if (FAILED (hr)) { | ||||||
|         dsound_logerr (hr, "Could not initialize COM\n"); |         dsound_logerr (hr, "Could not initialize COM\n"); | ||||||
| @@ -852,6 +854,28 @@ static void *dsound_audio_init(Audiodev *dev) | |||||||
|     return s; |     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 = { | static struct audio_pcm_ops dsound_pcm_ops = { | ||||||
|     .init_out = dsound_init_out, |     .init_out = dsound_init_out, | ||||||
|     .fini_out = dsound_fini_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 = { | static struct audio_driver dsound_audio_driver = { | ||||||
|     .name           = "dsound", |     .name           = "dsound", | ||||||
|     .descr          = "DirectSound http://wikipedia.org/wiki/DirectSound", |     .descr          = "DirectSound http://wikipedia.org/wiki/DirectSound", | ||||||
|  |     .options        = dsound_options, | ||||||
|     .init           = dsound_audio_init, |     .init           = dsound_audio_init, | ||||||
|     .fini           = dsound_audio_fini, |     .fini           = dsound_audio_fini, | ||||||
|     .pcm_ops        = &dsound_pcm_ops, |     .pcm_ops        = &dsound_pcm_ops, | ||||||
|   | |||||||
| @@ -136,7 +136,7 @@ static int no_ctl_in (HWVoiceIn *hw, int cmd, ...) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void *no_audio_init(Audiodev *dev) | static void *no_audio_init (void) | ||||||
| { | { | ||||||
|     return &no_audio_init; |     return &no_audio_init; | ||||||
| } | } | ||||||
| @@ -163,6 +163,7 @@ static struct audio_pcm_ops no_pcm_ops = { | |||||||
| static struct audio_driver no_audio_driver = { | static struct audio_driver no_audio_driver = { | ||||||
|     .name           = "none", |     .name           = "none", | ||||||
|     .descr          = "Timer based audio emulation", |     .descr          = "Timer based audio emulation", | ||||||
|  |     .options        = NULL, | ||||||
|     .init           = no_audio_init, |     .init           = no_audio_init, | ||||||
|     .fini           = no_audio_fini, |     .fini           = no_audio_fini, | ||||||
|     .pcm_ops        = &no_pcm_ops, |     .pcm_ops        = &no_pcm_ops, | ||||||
|   | |||||||
							
								
								
									
										193
									
								
								audio/ossaudio.c
									
									
									
									
									
								
							
							
						
						
									
										193
									
								
								audio/ossaudio.c
									
									
									
									
									
								
							| @@ -37,6 +37,16 @@ | |||||||
| #define USE_DSP_POLICY | #define USE_DSP_POLICY | ||||||
| #endif | #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 { | typedef struct OSSVoiceOut { | ||||||
|     HWVoiceOut hw; |     HWVoiceOut hw; | ||||||
|     void *pcm_buf; |     void *pcm_buf; | ||||||
| @@ -46,7 +56,7 @@ typedef struct OSSVoiceOut { | |||||||
|     int fragsize; |     int fragsize; | ||||||
|     int mmapped; |     int mmapped; | ||||||
|     int pending; |     int pending; | ||||||
|     Audiodev *dev; |     OSSConf *conf; | ||||||
| } OSSVoiceOut; | } OSSVoiceOut; | ||||||
|  |  | ||||||
| typedef struct OSSVoiceIn { | typedef struct OSSVoiceIn { | ||||||
| @@ -55,12 +65,12 @@ typedef struct OSSVoiceIn { | |||||||
|     int fd; |     int fd; | ||||||
|     int nfrags; |     int nfrags; | ||||||
|     int fragsize; |     int fragsize; | ||||||
|     Audiodev *dev; |     OSSConf *conf; | ||||||
| } OSSVoiceIn; | } OSSVoiceIn; | ||||||
|  |  | ||||||
| struct oss_params { | struct oss_params { | ||||||
|     int freq; |     int freq; | ||||||
|     int fmt; |     audfmt_e fmt; | ||||||
|     int nchannels; |     int nchannels; | ||||||
|     int nfrags; |     int nfrags; | ||||||
|     int fragsize; |     int fragsize; | ||||||
| @@ -138,16 +148,16 @@ static int oss_write (SWVoiceOut *sw, void *buf, int len) | |||||||
|     return audio_pcm_sw_write (sw, buf, 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) { |     switch (fmt) { | ||||||
|     case AUDIO_FORMAT_S8: |     case AUD_FMT_S8: | ||||||
|         return AFMT_S8; |         return AFMT_S8; | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_U8: |     case AUD_FMT_U8: | ||||||
|         return AFMT_U8; |         return AFMT_U8; | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_S16: |     case AUD_FMT_S16: | ||||||
|         if (endianness) { |         if (endianness) { | ||||||
|             return AFMT_S16_BE; |             return AFMT_S16_BE; | ||||||
|         } |         } | ||||||
| @@ -155,7 +165,7 @@ static int aud_to_ossfmt (AudioFormat fmt, int endianness) | |||||||
|             return AFMT_S16_LE; |             return AFMT_S16_LE; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_U16: |     case AUD_FMT_U16: | ||||||
|         if (endianness) { |         if (endianness) { | ||||||
|             return AFMT_U16_BE; |             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) { |     switch (ossfmt) { | ||||||
|     case AFMT_S8: |     case AFMT_S8: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_S8; |         *fmt = AUD_FMT_S8; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AFMT_U8: |     case AFMT_U8: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_U8; |         *fmt = AUD_FMT_U8; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AFMT_S16_LE: |     case AFMT_S16_LE: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_S16; |         *fmt = AUD_FMT_S16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AFMT_U16_LE: |     case AFMT_U16_LE: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_U16; |         *fmt = AUD_FMT_U16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AFMT_S16_BE: |     case AFMT_S16_BE: | ||||||
|         *endianness = 1; |         *endianness = 1; | ||||||
|         *fmt = AUDIO_FORMAT_S16; |         *fmt = AUD_FMT_S16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AFMT_U16_BE: |     case AFMT_U16_BE: | ||||||
|         *endianness = 1; |         *endianness = 1; | ||||||
|         *fmt = AUDIO_FORMAT_U16; |         *fmt = AUD_FMT_U16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     default: |     default: | ||||||
| @@ -252,25 +262,19 @@ static int oss_get_version (int fd, int *version, const char *typ) | |||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| static int oss_open(int in, struct oss_params *req, audsettings *as, | static int oss_open (int in, struct oss_params *req, | ||||||
|                     struct oss_params *obt, int *pfd, Audiodev *dev) |                      struct oss_params *obt, int *pfd, OSSConf* conf) | ||||||
| { | { | ||||||
|     AudiodevOssOptions *oopts = &dev->u.oss; |  | ||||||
|     AudiodevOssPerDirectionOptions *opdo = in ? oopts->in : oopts->out; |  | ||||||
|     int fd; |     int fd; | ||||||
|     int oflags = (oopts->has_exclusive && oopts->exclusive) ? O_EXCL : 0; |     int oflags = conf->exclusive ? O_EXCL : 0; | ||||||
|     audio_buf_info abinfo; |     audio_buf_info abinfo; | ||||||
|     int fmt, freq, nchannels; |     int fmt, freq, nchannels; | ||||||
|     int setfragment = 1; |     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"; |     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 */ |     /* Kludge needed to have working mmap on Linux */ | ||||||
|     oflags |= (oopts->has_try_mmap && oopts->try_mmap) ? |     oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY); | ||||||
|         O_RDWR : (in ? O_RDONLY : O_WRONLY); |  | ||||||
|  |  | ||||||
|     fd = open (dspname, oflags | O_NONBLOCK); |     fd = open (dspname, oflags | O_NONBLOCK); | ||||||
|     if (-1 == fd) { |     if (-1 == fd) { | ||||||
| @@ -281,9 +285,6 @@ static int oss_open(int in, struct oss_params *req, audsettings *as, | |||||||
|     freq = req->freq; |     freq = req->freq; | ||||||
|     nchannels = req->nchannels; |     nchannels = req->nchannels; | ||||||
|     fmt = req->fmt; |     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)) { |     if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) { | ||||||
|         oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->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 | #ifdef USE_DSP_POLICY | ||||||
|     if (policy >= 0) { |     if (conf->policy >= 0) { | ||||||
|         int version; |         int version; | ||||||
|  |  | ||||||
|         if (!oss_get_version (fd, &version, typ)) { |         if (!oss_get_version (fd, &version, typ)) { | ||||||
|             trace_oss_version(version); |             trace_oss_version(version); | ||||||
|  |  | ||||||
|             if (version >= 0x040000) { |             if (version >= 0x040000) { | ||||||
|                 int policy2 = policy; |                 int policy = conf->policy; | ||||||
|                 if (ioctl(fd, SNDCTL_DSP_POLICY, &policy2)) { |                 if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) { | ||||||
|                     oss_logerr2 (errno, typ, |                     oss_logerr2 (errno, typ, | ||||||
|                                  "Failed to set timing policy to %d\n", |                                  "Failed to set timing policy to %d\n", | ||||||
|                                  policy); |                                  conf->policy); | ||||||
|                     goto err; |                     goto err; | ||||||
|                 } |                 } | ||||||
|                 setfragment = 0; |                 setfragment = 0; | ||||||
| @@ -499,18 +500,19 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     int endianness; |     int endianness; | ||||||
|     int err; |     int err; | ||||||
|     int fd; |     int fd; | ||||||
|     AudioFormat effective_fmt; |     audfmt_e effective_fmt; | ||||||
|     struct audsettings obt_as; |     struct audsettings obt_as; | ||||||
|     Audiodev *dev = drv_opaque; |     OSSConf *conf = drv_opaque; | ||||||
|     AudiodevOssOptions *oopts = &dev->u.oss; |  | ||||||
|  |  | ||||||
|     oss->fd = -1; |     oss->fd = -1; | ||||||
|  |  | ||||||
|     req.fmt = aud_to_ossfmt (as->fmt, as->endianness); |     req.fmt = aud_to_ossfmt (as->fmt, as->endianness); | ||||||
|     req.freq = as->freq; |     req.freq = as->freq; | ||||||
|     req.nchannels = as->nchannels; |     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; |         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; |     hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; | ||||||
|  |  | ||||||
|     oss->mmapped = 0; |     oss->mmapped = 0; | ||||||
|     if (oopts->has_try_mmap && oopts->try_mmap) { |     if (conf->try_mmap) { | ||||||
|         oss->pcm_buf = mmap ( |         oss->pcm_buf = mmap ( | ||||||
|             NULL, |             NULL, | ||||||
|             hw->samples << hw->info.shift, |             hw->samples << hw->info.shift, | ||||||
| @@ -595,7 +597,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     oss->fd = fd; |     oss->fd = fd; | ||||||
|     oss->dev = dev; |     oss->conf = conf; | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -603,12 +605,16 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...) | |||||||
| { | { | ||||||
|     int trig; |     int trig; | ||||||
|     OSSVoiceOut *oss = (OSSVoiceOut *) hw; |     OSSVoiceOut *oss = (OSSVoiceOut *) hw; | ||||||
|     AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out; |  | ||||||
|  |  | ||||||
|     switch (cmd) { |     switch (cmd) { | ||||||
|     case VOICE_ENABLE: |     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"); |             ldebug ("enabling voice\n"); | ||||||
|             if (poll_mode) { |             if (poll_mode) { | ||||||
| @@ -661,16 +667,18 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | |||||||
|     int endianness; |     int endianness; | ||||||
|     int err; |     int err; | ||||||
|     int fd; |     int fd; | ||||||
|     AudioFormat effective_fmt; |     audfmt_e effective_fmt; | ||||||
|     struct audsettings obt_as; |     struct audsettings obt_as; | ||||||
|     Audiodev *dev = drv_opaque; |     OSSConf *conf = drv_opaque; | ||||||
|  |  | ||||||
|     oss->fd = -1; |     oss->fd = -1; | ||||||
|  |  | ||||||
|     req.fmt = aud_to_ossfmt (as->fmt, as->endianness); |     req.fmt = aud_to_ossfmt (as->fmt, as->endianness); | ||||||
|     req.freq = as->freq; |     req.freq = as->freq; | ||||||
|     req.nchannels = as->nchannels; |     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; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -704,7 +712,7 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     oss->fd = fd; |     oss->fd = fd; | ||||||
|     oss->dev = dev; |     oss->conf = conf; | ||||||
|     return 0; |     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, ...) | static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||||
| { | { | ||||||
|     OSSVoiceIn *oss = (OSSVoiceIn *) hw; |     OSSVoiceIn *oss = (OSSVoiceIn *) hw; | ||||||
|     AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out; |  | ||||||
|  |  | ||||||
|     switch (cmd) { |     switch (cmd) { | ||||||
|     case VOICE_ENABLE: |     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) { |             if (poll_mode) { | ||||||
|                 oss_poll_in (hw); |                 oss_poll_in (hw); | ||||||
| @@ -820,36 +832,82 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...) | |||||||
|     return 0; |     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) { |     OSSConf *conf = g_malloc(sizeof(OSSConf)); | ||||||
|         opdo->try_poll = true; |     *conf = glob_conf; | ||||||
|         opdo->has_try_poll = true; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void *oss_audio_init(Audiodev *dev) |     if (access(conf->devpath_in, R_OK | W_OK) < 0 || | ||||||
| { |         access(conf->devpath_out, R_OK | W_OK) < 0) { | ||||||
|     AudiodevOssOptions *oopts; |         g_free(conf); | ||||||
|     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) { |  | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|     return dev; |     return conf; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void oss_audio_fini (void *opaque) | 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 = { | static struct audio_pcm_ops oss_pcm_ops = { | ||||||
|     .init_out = oss_init_out, |     .init_out = oss_init_out, | ||||||
|     .fini_out = oss_fini_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 = { | static struct audio_driver oss_audio_driver = { | ||||||
|     .name           = "oss", |     .name           = "oss", | ||||||
|     .descr          = "OSS http://www.opensound.com", |     .descr          = "OSS http://www.opensound.com", | ||||||
|  |     .options        = oss_options, | ||||||
|     .init           = oss_audio_init, |     .init           = oss_audio_init, | ||||||
|     .fini           = oss_audio_fini, |     .fini           = oss_audio_fini, | ||||||
|     .pcm_ops        = &oss_pcm_ops, |     .pcm_ops        = &oss_pcm_ops, | ||||||
|   | |||||||
							
								
								
									
										164
									
								
								audio/paaudio.c
									
									
									
									
									
								
							
							
						
						
									
										164
									
								
								audio/paaudio.c
									
									
									
									
									
								
							| @@ -2,7 +2,6 @@ | |||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "qemu-common.h" | #include "qemu-common.h" | ||||||
| #include "audio.h" | #include "audio.h" | ||||||
| #include "qapi/opts-visitor.h" |  | ||||||
|  |  | ||||||
| #include <pulse/pulseaudio.h> | #include <pulse/pulseaudio.h> | ||||||
|  |  | ||||||
| @@ -11,7 +10,14 @@ | |||||||
| #include "audio_pt_int.h" | #include "audio_pt_int.h" | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     Audiodev *dev; |     int samples; | ||||||
|  |     char *server; | ||||||
|  |     char *sink; | ||||||
|  |     char *source; | ||||||
|  | } PAConf; | ||||||
|  |  | ||||||
|  | typedef struct { | ||||||
|  |     PAConf conf; | ||||||
|     pa_threaded_mainloop *mainloop; |     pa_threaded_mainloop *mainloop; | ||||||
|     pa_context *context; |     pa_context *context; | ||||||
| } paaudio; | } paaudio; | ||||||
| @@ -26,7 +32,6 @@ typedef struct { | |||||||
|     void *pcm_buf; |     void *pcm_buf; | ||||||
|     struct audio_pt pt; |     struct audio_pt pt; | ||||||
|     paaudio *g; |     paaudio *g; | ||||||
|     int samples; |  | ||||||
| } PAVoiceOut; | } PAVoiceOut; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct { | ||||||
| @@ -41,7 +46,6 @@ typedef struct { | |||||||
|     const void *read_data; |     const void *read_data; | ||||||
|     size_t read_index, read_length; |     size_t read_index, read_length; | ||||||
|     paaudio *g; |     paaudio *g; | ||||||
|     int samples; |  | ||||||
| } PAVoiceIn; | } PAVoiceIn; | ||||||
|  |  | ||||||
| static void qpa_audio_fini(void *opaque); | 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; |         rpos = pa->rpos; | ||||||
|  |  | ||||||
|         if (audio_pt_unlock(&pa->pt, __func__)) { |         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; |         wpos = pa->wpos; | ||||||
|  |  | ||||||
|         if (audio_pt_unlock(&pa->pt, __func__)) { |         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); |     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; |     int format; | ||||||
|  |  | ||||||
|     switch (afmt) { |     switch (afmt) { | ||||||
|     case AUDIO_FORMAT_S8: |     case AUD_FMT_S8: | ||||||
|     case AUDIO_FORMAT_U8: |     case AUD_FMT_U8: | ||||||
|         format = PA_SAMPLE_U8; |         format = PA_SAMPLE_U8; | ||||||
|         break; |         break; | ||||||
|     case AUDIO_FORMAT_S16: |     case AUD_FMT_S16: | ||||||
|     case AUDIO_FORMAT_U16: |     case AUD_FMT_U16: | ||||||
|         format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; |         format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; | ||||||
|         break; |         break; | ||||||
|     case AUDIO_FORMAT_S32: |     case AUD_FMT_S32: | ||||||
|     case AUDIO_FORMAT_U32: |     case AUD_FMT_U32: | ||||||
|         format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; |         format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; | ||||||
|         break; |         break; | ||||||
|     default: |     default: | ||||||
| @@ -406,26 +410,26 @@ static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness) | |||||||
|     return format; |     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) { |     switch (fmt) { | ||||||
|     case PA_SAMPLE_U8: |     case PA_SAMPLE_U8: | ||||||
|         return AUDIO_FORMAT_U8; |         return AUD_FMT_U8; | ||||||
|     case PA_SAMPLE_S16BE: |     case PA_SAMPLE_S16BE: | ||||||
|         *endianness = 1; |         *endianness = 1; | ||||||
|         return AUDIO_FORMAT_S16; |         return AUD_FMT_S16; | ||||||
|     case PA_SAMPLE_S16LE: |     case PA_SAMPLE_S16LE: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         return AUDIO_FORMAT_S16; |         return AUD_FMT_S16; | ||||||
|     case PA_SAMPLE_S32BE: |     case PA_SAMPLE_S32BE: | ||||||
|         *endianness = 1; |         *endianness = 1; | ||||||
|         return AUDIO_FORMAT_S32; |         return AUD_FMT_S32; | ||||||
|     case PA_SAMPLE_S32LE: |     case PA_SAMPLE_S32LE: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         return AUDIO_FORMAT_S32; |         return AUD_FMT_S32; | ||||||
|     default: |     default: | ||||||
|         dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt); |         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; |     struct audsettings obt_as = *as; | ||||||
|     PAVoiceOut *pa = (PAVoiceOut *) hw; |     PAVoiceOut *pa = (PAVoiceOut *) hw; | ||||||
|     paaudio *g = pa->g = drv_opaque; |     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.format = audfmt_to_pa (as->fmt, as->endianness); | ||||||
|     ss.channels = as->nchannels; |     ss.channels = as->nchannels; | ||||||
|     ss.rate = as->freq; |     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.maxlength = -1; | ||||||
|     ba.prebuf = -1; |     ba.prebuf = -1; | ||||||
|  |  | ||||||
| @@ -560,7 +566,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|         g, |         g, | ||||||
|         "qemu", |         "qemu", | ||||||
|         PA_STREAM_PLAYBACK, |         PA_STREAM_PLAYBACK, | ||||||
|         ppdo->has_name ? ppdo->name : NULL, |         g->conf.sink, | ||||||
|         &ss, |         &ss, | ||||||
|         NULL,                   /* channel map */ |         NULL,                   /* channel map */ | ||||||
|         &ba,                    /* buffering attributes */ |         &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); |     audio_pcm_init_info (&hw->info, &obt_as); | ||||||
|     hw->samples = pa->samples = audio_buffer_samples( |     hw->samples = g->conf.samples; | ||||||
|         qapi_AudiodevPaPerDirectionOptions_base(ppdo), |  | ||||||
|         &obt_as, ppdo->buffer_length); |  | ||||||
|     pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); |     pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); | ||||||
|     pa->rpos = hw->rpos; |     pa->rpos = hw->rpos; | ||||||
|     if (!pa->pcm_buf) { |     if (!pa->pcm_buf) { | ||||||
| @@ -605,32 +609,24 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | |||||||
| { | { | ||||||
|     int error; |     int error; | ||||||
|     pa_sample_spec ss; |     pa_sample_spec ss; | ||||||
|     pa_buffer_attr ba; |  | ||||||
|     struct audsettings obt_as = *as; |     struct audsettings obt_as = *as; | ||||||
|     PAVoiceIn *pa = (PAVoiceIn *) hw; |     PAVoiceIn *pa = (PAVoiceIn *) hw; | ||||||
|     paaudio *g = pa->g = drv_opaque; |     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.format = audfmt_to_pa (as->fmt, as->endianness); | ||||||
|     ss.channels = as->nchannels; |     ss.channels = as->nchannels; | ||||||
|     ss.rate = as->freq; |     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); |     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); | ||||||
|  |  | ||||||
|     pa->stream = qpa_simple_new ( |     pa->stream = qpa_simple_new ( | ||||||
|         g, |         g, | ||||||
|         "qemu", |         "qemu", | ||||||
|         PA_STREAM_RECORD, |         PA_STREAM_RECORD, | ||||||
|         ppdo->has_name ? ppdo->name : NULL, |         g->conf.source, | ||||||
|         &ss, |         &ss, | ||||||
|         NULL,                   /* channel map */ |         NULL,                   /* channel map */ | ||||||
|         &ba,                    /* buffering attributes */ |         NULL,                   /* buffering attributes */ | ||||||
|         &error |         &error | ||||||
|         ); |         ); | ||||||
|     if (!pa->stream) { |     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); |     audio_pcm_init_info (&hw->info, &obt_as); | ||||||
|     hw->samples = pa->samples = audio_buffer_samples( |     hw->samples = g->conf.samples; | ||||||
|         qapi_AudiodevPaPerDirectionOptions_base(ppdo), |  | ||||||
|         &obt_as, ppdo->buffer_length); |  | ||||||
|     pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); |     pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); | ||||||
|     pa->wpos = hw->wpos; |     pa->wpos = hw->wpos; | ||||||
|     if (!pa->pcm_buf) { |     if (!pa->pcm_buf) { | ||||||
| @@ -813,54 +807,15 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int qpa_validate_per_direction_opts(Audiodev *dev, | /* common */ | ||||||
|                                            AudiodevPaPerDirectionOptions *pdo) | static PAConf glob_conf = { | ||||||
|  |     .samples = 4096, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void *qpa_audio_init (void) | ||||||
| { | { | ||||||
|     if (!pdo->has_buffer_length) { |     paaudio *g = g_malloc(sizeof(paaudio)); | ||||||
|         pdo->has_buffer_length = true; |     g->conf = glob_conf; | ||||||
|         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; |  | ||||||
|     g->mainloop = NULL; |     g->mainloop = NULL; | ||||||
|     g->context = 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), |     g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), | ||||||
|                                  server); |                                  g->conf.server); | ||||||
|     if (!g->context) { |     if (!g->context) { | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pa_context_set_state_callback (g->context, context_state_cb, g); |     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), |         qpa_logerr (pa_context_errno (g->context), | ||||||
|                     "pa_context_connect() failed\n"); |                     "pa_context_connect() failed\n"); | ||||||
|         goto fail; |         goto fail; | ||||||
| @@ -940,6 +895,34 @@ static void qpa_audio_fini (void *opaque) | |||||||
|     g_free(g); |     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 = { | static struct audio_pcm_ops qpa_pcm_ops = { | ||||||
|     .init_out = qpa_init_out, |     .init_out = qpa_init_out, | ||||||
|     .fini_out = qpa_fini_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 = { | static struct audio_driver pa_audio_driver = { | ||||||
|     .name           = "pa", |     .name           = "pa", | ||||||
|     .descr          = "http://www.pulseaudio.org/", |     .descr          = "http://www.pulseaudio.org/", | ||||||
|  |     .options        = qpa_options, | ||||||
|     .init           = qpa_audio_init, |     .init           = qpa_audio_init, | ||||||
|     .fini           = qpa_audio_fini, |     .fini           = qpa_audio_fini, | ||||||
|     .pcm_ops        = &qpa_pcm_ops, |     .pcm_ops        = &qpa_pcm_ops, | ||||||
|   | |||||||
							
								
								
									
										236
									
								
								audio/sdlaudio.c
									
									
									
									
									
								
							
							
						
						
									
										236
									
								
								audio/sdlaudio.c
									
									
									
									
									
								
							| @@ -38,17 +38,31 @@ | |||||||
| #define AUDIO_CAP "sdl" | #define AUDIO_CAP "sdl" | ||||||
| #include "audio_int.h" | #include "audio_int.h" | ||||||
|  |  | ||||||
|  | #define USE_SEMAPHORE (SDL_MAJOR_VERSION < 2) | ||||||
|  |  | ||||||
| typedef struct SDLVoiceOut { | typedef struct SDLVoiceOut { | ||||||
|     HWVoiceOut hw; |     HWVoiceOut hw; | ||||||
|     int live; |     int live; | ||||||
|  | #if USE_SEMAPHORE | ||||||
|  |     int rpos; | ||||||
|  | #endif | ||||||
|     int decr; |     int decr; | ||||||
| } SDLVoiceOut; | } SDLVoiceOut; | ||||||
|  |  | ||||||
|  | static struct { | ||||||
|  |     int nb_samples; | ||||||
|  | } conf = { | ||||||
|  |     .nb_samples = 1024 | ||||||
|  | }; | ||||||
|  |  | ||||||
| static struct SDLAudioState { | static struct SDLAudioState { | ||||||
|     int exit; |     int exit; | ||||||
|  | #if USE_SEMAPHORE | ||||||
|  |     SDL_mutex *mutex; | ||||||
|  |     SDL_sem *sem; | ||||||
|  | #endif | ||||||
|     int initialized; |     int initialized; | ||||||
|     bool driver_created; |     bool driver_created; | ||||||
|     Audiodev *dev; |  | ||||||
| } glob_sdl; | } glob_sdl; | ||||||
| typedef struct SDLAudioState SDLAudioState; | 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 ()); |     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) { |     switch (fmt) { | ||||||
|     case AUDIO_FORMAT_S8: |     case AUD_FMT_S8: | ||||||
|         return AUDIO_S8; |         return AUDIO_S8; | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_U8: |     case AUD_FMT_U8: | ||||||
|         return AUDIO_U8; |         return AUDIO_U8; | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_S16: |     case AUD_FMT_S16: | ||||||
|         return AUDIO_S16LSB; |         return AUDIO_S16LSB; | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_U16: |     case AUD_FMT_U16: | ||||||
|         return AUDIO_U16LSB; |         return AUDIO_U16LSB; | ||||||
|  |  | ||||||
|     default: |     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) { |     switch (sdlfmt) { | ||||||
|     case AUDIO_S8: |     case AUDIO_S8: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_S8; |         *fmt = AUD_FMT_S8; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_U8: |     case AUDIO_U8: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_U8; |         *fmt = AUD_FMT_U8; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_S16LSB: |     case AUDIO_S16LSB: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_S16; |         *fmt = AUD_FMT_S16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_U16LSB: |     case AUDIO_U16LSB: | ||||||
|         *endianness = 0; |         *endianness = 0; | ||||||
|         *fmt = AUDIO_FORMAT_U16; |         *fmt = AUD_FMT_U16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_S16MSB: |     case AUDIO_S16MSB: | ||||||
|         *endianness = 1; |         *endianness = 1; | ||||||
|         *fmt = AUDIO_FORMAT_S16; |         *fmt = AUD_FMT_S16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_U16MSB: |     case AUDIO_U16MSB: | ||||||
|         *endianness = 1; |         *endianness = 1; | ||||||
|         *fmt = AUDIO_FORMAT_U16; |         *fmt = AUD_FMT_U16; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     default: |     default: | ||||||
| @@ -169,9 +243,9 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) | |||||||
| static void sdl_close (SDLAudioState *s) | static void sdl_close (SDLAudioState *s) | ||||||
| { | { | ||||||
|     if (s->initialized) { |     if (s->initialized) { | ||||||
|         SDL_LockAudio(); |         sdl_lock (s, "sdl_close"); | ||||||
|         s->exit = 1; |         s->exit = 1; | ||||||
|         SDL_UnlockAudio(); |         sdl_unlock_and_post (s, "sdl_close"); | ||||||
|         SDL_PauseAudio (1); |         SDL_PauseAudio (1); | ||||||
|         SDL_CloseAudio (); |         SDL_CloseAudio (); | ||||||
|         s->initialized = 0; |         s->initialized = 0; | ||||||
| @@ -184,36 +258,76 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len) | |||||||
|     SDLAudioState *s = &glob_sdl; |     SDLAudioState *s = &glob_sdl; | ||||||
|     HWVoiceOut *hw = &sdl->hw; |     HWVoiceOut *hw = &sdl->hw; | ||||||
|     int samples = len >> hw->info.shift; |     int samples = len >> hw->info.shift; | ||||||
|     int to_mix, decr; |  | ||||||
|  |  | ||||||
|     if (s->exit || !sdl->live) { |     if (s->exit) { | ||||||
|         return; |         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); |         /* dolog ("in callback samples=%d\n", samples); */ | ||||||
|     decr = to_mix; | #if USE_SEMAPHORE | ||||||
|     while (to_mix) { |         sdl_wait (s, "sdl_callback"); | ||||||
|         int chunk = audio_MIN(to_mix, hw->samples - hw->rpos); |         if (s->exit) { | ||||||
|         struct st_sample *src = hw->mix_buf + hw->rpos; |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ |         if (sdl_lock (s, "sdl_callback")) { | ||||||
|         hw->clip(buf, src, chunk); |             return; | ||||||
|         hw->rpos = (hw->rpos + chunk) % hw->samples; |         } | ||||||
|         to_mix -= chunk; |  | ||||||
|         buf += chunk << hw->info.shift; |         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); */ |     /* 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 */ |     /* SDL2 does not clear the remaining buffer for us, so do it on our own */ | ||||||
|     if (samples) { |     if (samples) { | ||||||
|         memset(buf, 0, samples << hw->info.shift); |         memset(buf, 0, samples << hw->info.shift); | ||||||
|     } |     } | ||||||
|  | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) | 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; |     int decr; | ||||||
|     SDLVoiceOut *sdl = (SDLVoiceOut *) hw; |     SDLVoiceOut *sdl = (SDLVoiceOut *) hw; | ||||||
|  |     SDLAudioState *s = &glob_sdl; | ||||||
|  |  | ||||||
|     SDL_LockAudio(); |     if (sdl_lock (s, "sdl_run_out")) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (sdl->decr > live) { |     if (sdl->decr > live) { | ||||||
|         ldebug ("sdl->decr %d live %d sdl->live %d\n", |         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); |     decr = audio_MIN (sdl->decr, live); | ||||||
|     sdl->decr -= decr; |     sdl->decr -= decr; | ||||||
|  |  | ||||||
|  | #if USE_SEMAPHORE | ||||||
|  |     sdl->live = live - decr; | ||||||
|  |     hw->rpos = sdl->rpos; | ||||||
|  | #else | ||||||
|     sdl->live = live; |     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; |     return decr; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -260,13 +386,13 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     SDL_AudioSpec req, obt; |     SDL_AudioSpec req, obt; | ||||||
|     int endianness; |     int endianness; | ||||||
|     int err; |     int err; | ||||||
|     AudioFormat effective_fmt; |     audfmt_e effective_fmt; | ||||||
|     struct audsettings obt_as; |     struct audsettings obt_as; | ||||||
|  |  | ||||||
|     req.freq = as->freq; |     req.freq = as->freq; | ||||||
|     req.format = aud_to_sdlfmt (as->fmt); |     req.format = aud_to_sdlfmt (as->fmt); | ||||||
|     req.channels = as->nchannels; |     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.callback = sdl_callback; | ||||||
|     req.userdata = sdl; |     req.userdata = sdl; | ||||||
|  |  | ||||||
| @@ -310,7 +436,7 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void *sdl_audio_init(Audiodev *dev) | static void *sdl_audio_init (void) | ||||||
| { | { | ||||||
|     SDLAudioState *s = &glob_sdl; |     SDLAudioState *s = &glob_sdl; | ||||||
|     if (s->driver_created) { |     if (s->driver_created) { | ||||||
| @@ -323,8 +449,24 @@ static void *sdl_audio_init(Audiodev *dev) | |||||||
|         return NULL; |         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->driver_created = true; | ||||||
|     s->dev = dev; |  | ||||||
|     return s; |     return s; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -332,11 +474,24 @@ static void sdl_audio_fini (void *opaque) | |||||||
| { | { | ||||||
|     SDLAudioState *s = opaque; |     SDLAudioState *s = opaque; | ||||||
|     sdl_close (s); |     sdl_close (s); | ||||||
|  | #if USE_SEMAPHORE | ||||||
|  |     SDL_DestroySemaphore (s->sem); | ||||||
|  |     SDL_DestroyMutex (s->mutex); | ||||||
|  | #endif | ||||||
|     SDL_QuitSubSystem (SDL_INIT_AUDIO); |     SDL_QuitSubSystem (SDL_INIT_AUDIO); | ||||||
|     s->driver_created = false; |     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 = { | static struct audio_pcm_ops sdl_pcm_ops = { | ||||||
|     .init_out = sdl_init_out, |     .init_out = sdl_init_out, | ||||||
|     .fini_out = sdl_fini_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 = { | static struct audio_driver sdl_audio_driver = { | ||||||
|     .name           = "sdl", |     .name           = "sdl", | ||||||
|     .descr          = "SDL http://www.libsdl.org", |     .descr          = "SDL http://www.libsdl.org", | ||||||
|  |     .options        = sdl_options, | ||||||
|     .init           = sdl_audio_init, |     .init           = sdl_audio_init, | ||||||
|     .fini           = sdl_audio_fini, |     .fini           = sdl_audio_fini, | ||||||
|     .pcm_ops        = &sdl_pcm_ops, |     .pcm_ops        = &sdl_pcm_ops, | ||||||
|   | |||||||
| @@ -77,7 +77,7 @@ static const SpiceRecordInterface record_sif = { | |||||||
|     .base.minor_version = SPICE_INTERFACE_RECORD_MINOR, |     .base.minor_version = SPICE_INTERFACE_RECORD_MINOR, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void *spice_audio_init(Audiodev *dev) | static void *spice_audio_init (void) | ||||||
| { | { | ||||||
|     if (!using_spice) { |     if (!using_spice) { | ||||||
|         return NULL; |         return NULL; | ||||||
| @@ -130,7 +130,7 @@ static int line_out_init(HWVoiceOut *hw, struct audsettings *as, | |||||||
|     settings.freq       = SPICE_INTERFACE_PLAYBACK_FREQ; |     settings.freq       = SPICE_INTERFACE_PLAYBACK_FREQ; | ||||||
| #endif | #endif | ||||||
|     settings.nchannels  = SPICE_INTERFACE_PLAYBACK_CHAN; |     settings.nchannels  = SPICE_INTERFACE_PLAYBACK_CHAN; | ||||||
|     settings.fmt        = AUDIO_FORMAT_S16; |     settings.fmt        = AUD_FMT_S16; | ||||||
|     settings.endianness = AUDIO_HOST_ENDIANNESS; |     settings.endianness = AUDIO_HOST_ENDIANNESS; | ||||||
|  |  | ||||||
|     audio_pcm_init_info (&hw->info, &settings); |     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; |     settings.freq       = SPICE_INTERFACE_RECORD_FREQ; | ||||||
| #endif | #endif | ||||||
|     settings.nchannels  = SPICE_INTERFACE_RECORD_CHAN; |     settings.nchannels  = SPICE_INTERFACE_RECORD_CHAN; | ||||||
|     settings.fmt        = AUDIO_FORMAT_S16; |     settings.fmt        = AUD_FMT_S16; | ||||||
|     settings.endianness = AUDIO_HOST_ENDIANNESS; |     settings.endianness = AUDIO_HOST_ENDIANNESS; | ||||||
|  |  | ||||||
|     audio_pcm_init_info (&hw->info, &settings); |     audio_pcm_init_info (&hw->info, &settings); | ||||||
| @@ -373,6 +373,10 @@ static int line_in_ctl (HWVoiceIn *hw, int cmd, ...) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static struct audio_option audio_options[] = { | ||||||
|  |     { /* end of list */ }, | ||||||
|  | }; | ||||||
|  |  | ||||||
| static struct audio_pcm_ops audio_callbacks = { | static struct audio_pcm_ops audio_callbacks = { | ||||||
|     .init_out = line_out_init, |     .init_out = line_out_init, | ||||||
|     .fini_out = line_out_fini, |     .fini_out = line_out_fini, | ||||||
| @@ -390,6 +394,7 @@ static struct audio_pcm_ops audio_callbacks = { | |||||||
| static struct audio_driver spice_audio_driver = { | static struct audio_driver spice_audio_driver = { | ||||||
|     .name           = "spice", |     .name           = "spice", | ||||||
|     .descr          = "spice audio driver", |     .descr          = "spice audio driver", | ||||||
|  |     .options        = audio_options, | ||||||
|     .init           = spice_audio_init, |     .init           = spice_audio_init, | ||||||
|     .fini           = spice_audio_fini, |     .fini           = spice_audio_fini, | ||||||
|     .pcm_ops        = &audio_callbacks, |     .pcm_ops        = &audio_callbacks, | ||||||
|   | |||||||
| @@ -15,8 +15,3 @@ alsa_no_frames(int state) "No frames available and ALSA state is %d" | |||||||
| # audio/ossaudio.c | # audio/ossaudio.c | ||||||
| oss_version(int version) "OSS version = 0x%x" | oss_version(int version) "OSS version = 0x%x" | ||||||
| oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d" | oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d" | ||||||
|  |  | ||||||
| # audio/audio.c |  | ||||||
| audio_timer_start(int interval) "interval %d ms" |  | ||||||
| audio_timer_stop(void) "" |  | ||||||
| audio_timer_delayed(int interval) "interval %d ms" |  | ||||||
|   | |||||||
| @@ -24,7 +24,6 @@ | |||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "qemu/host-utils.h" | #include "qemu/host-utils.h" | ||||||
| #include "qemu/timer.h" | #include "qemu/timer.h" | ||||||
| #include "qapi/opts-visitor.h" |  | ||||||
| #include "audio.h" | #include "audio.h" | ||||||
|  |  | ||||||
| #define AUDIO_CAP "wav" | #define AUDIO_CAP "wav" | ||||||
| @@ -38,6 +37,11 @@ typedef struct WAVVoiceOut { | |||||||
|     int total_samples; |     int total_samples; | ||||||
| } WAVVoiceOut; | } WAVVoiceOut; | ||||||
|  |  | ||||||
|  | typedef struct { | ||||||
|  |     struct audsettings settings; | ||||||
|  |     const char *wav_path; | ||||||
|  | } WAVConf; | ||||||
|  |  | ||||||
| static int wav_run_out (HWVoiceOut *hw, int live) | static int wav_run_out (HWVoiceOut *hw, int live) | ||||||
| { | { | ||||||
|     WAVVoiceOut *wav = (WAVVoiceOut *) hw; |     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, |         0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, | ||||||
|         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 |         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 | ||||||
|     }; |     }; | ||||||
|     Audiodev *dev = drv_opaque; |     WAVConf *conf = drv_opaque; | ||||||
|     AudiodevWavOptions *wopts = &dev->u.wav; |     struct audsettings wav_as = conf->settings; | ||||||
|     struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out); |  | ||||||
|     const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav"; |  | ||||||
|  |  | ||||||
|     stereo = wav_as.nchannels == 2; |     stereo = wav_as.nchannels == 2; | ||||||
|     switch (wav_as.fmt) { |     switch (wav_as.fmt) { | ||||||
|     case AUDIO_FORMAT_S8: |     case AUD_FMT_S8: | ||||||
|     case AUDIO_FORMAT_U8: |     case AUD_FMT_U8: | ||||||
|         bits16 = 0; |         bits16 = 0; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_S16: |     case AUD_FMT_S16: | ||||||
|     case AUDIO_FORMAT_U16: |     case AUD_FMT_U16: | ||||||
|         bits16 = 1; |         bits16 = 1; | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     case AUDIO_FORMAT_S32: |     case AUD_FMT_S32: | ||||||
|     case AUDIO_FORMAT_U32: |     case AUD_FMT_U32: | ||||||
|         dolog ("WAVE files can not handle 32bit formats\n"); |         dolog ("WAVE files can not handle 32bit formats\n"); | ||||||
|         return -1; |         return -1; | ||||||
|  |  | ||||||
|     default: |  | ||||||
|         abort(); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     hdr[34] = bits16 ? 0x10 : 0x08; |     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 + 28, hw->info.freq << (bits16 + stereo), 4); | ||||||
|     le_store (hdr + 32, 1 << (bits16 + stereo), 2); |     le_store (hdr + 32, 1 << (bits16 + stereo), 2); | ||||||
|  |  | ||||||
|     wav->f = fopen(wav_path, "wb"); |     wav->f = fopen (conf->wav_path, "wb"); | ||||||
|     if (!wav->f) { |     if (!wav->f) { | ||||||
|         dolog ("Failed to open wave file `%s'\nReason: %s\n", |         dolog ("Failed to open wave file `%s'\nReason: %s\n", | ||||||
|                wav_path, strerror(errno)); |                conf->wav_path, strerror (errno)); | ||||||
|         g_free (wav->pcm_buf); |         g_free (wav->pcm_buf); | ||||||
|         wav->pcm_buf = NULL; |         wav->pcm_buf = NULL; | ||||||
|         return -1; |         return -1; | ||||||
| @@ -223,17 +222,54 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...) | |||||||
|     return 0; |     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); |     WAVConf *conf = g_malloc(sizeof(WAVConf)); | ||||||
|     return dev; |     *conf = glob_conf; | ||||||
|  |     return conf; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void wav_audio_fini (void *opaque) | static void wav_audio_fini (void *opaque) | ||||||
| { | { | ||||||
|     ldebug ("wav_fini"); |     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 = { | static struct audio_pcm_ops wav_pcm_ops = { | ||||||
|     .init_out = wav_init_out, |     .init_out = wav_init_out, | ||||||
|     .fini_out = wav_fini_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 = { | static struct audio_driver wav_audio_driver = { | ||||||
|     .name           = "wav", |     .name           = "wav", | ||||||
|     .descr          = "WAV renderer http://wikipedia.org/wiki/WAV", |     .descr          = "WAV renderer http://wikipedia.org/wiki/WAV", | ||||||
|  |     .options        = wav_options, | ||||||
|     .init           = wav_audio_init, |     .init           = wav_audio_init, | ||||||
|     .fini           = wav_audio_fini, |     .fini           = wav_audio_fini, | ||||||
|     .pcm_ops        = &wav_pcm_ops, |     .pcm_ops        = &wav_pcm_ops, | ||||||
|   | |||||||
| @@ -38,29 +38,30 @@ static void wav_destroy (void *opaque) | |||||||
|     uint8_t dlen[4]; |     uint8_t dlen[4]; | ||||||
|     uint32_t datalen = wav->bytes; |     uint32_t datalen = wav->bytes; | ||||||
|     uint32_t rifflen = datalen + 36; |     uint32_t rifflen = datalen + 36; | ||||||
|  |     Monitor *mon = cur_mon; | ||||||
|  |  | ||||||
|     if (wav->f) { |     if (wav->f) { | ||||||
|         le_store (rlen, rifflen, 4); |         le_store (rlen, rifflen, 4); | ||||||
|         le_store (dlen, datalen, 4); |         le_store (dlen, datalen, 4); | ||||||
|  |  | ||||||
|         if (fseek (wav->f, 4, SEEK_SET)) { |         if (fseek (wav->f, 4, SEEK_SET)) { | ||||||
|             error_report("wav_destroy: rlen fseek failed: %s", |             monitor_printf (mon, "wav_destroy: rlen fseek failed\nReason: %s\n", | ||||||
|                          strerror(errno)); |                             strerror (errno)); | ||||||
|             goto doclose; |             goto doclose; | ||||||
|         } |         } | ||||||
|         if (fwrite (rlen, 4, 1, wav->f) != 1) { |         if (fwrite (rlen, 4, 1, wav->f) != 1) { | ||||||
|             error_report("wav_destroy: rlen fwrite failed: %s", |             monitor_printf (mon, "wav_destroy: rlen fwrite failed\nReason %s\n", | ||||||
|                          strerror(errno)); |                             strerror (errno)); | ||||||
|             goto doclose; |             goto doclose; | ||||||
|         } |         } | ||||||
|         if (fseek (wav->f, 32, SEEK_CUR)) { |         if (fseek (wav->f, 32, SEEK_CUR)) { | ||||||
|             error_report("wav_destroy: dlen fseek failed: %s", |             monitor_printf (mon, "wav_destroy: dlen fseek failed\nReason %s\n", | ||||||
|                          strerror(errno)); |                             strerror (errno)); | ||||||
|             goto doclose; |             goto doclose; | ||||||
|         } |         } | ||||||
|         if (fwrite (dlen, 1, 4, wav->f) != 4) { |         if (fwrite (dlen, 1, 4, wav->f) != 4) { | ||||||
|             error_report("wav_destroy: dlen fwrite failed: %s", |             monitor_printf (mon, "wav_destroy: dlen fwrite failed\nReason %s\n", | ||||||
|                          strerror(errno)); |                             strerror (errno)); | ||||||
|             goto doclose; |             goto doclose; | ||||||
|         } |         } | ||||||
|     doclose: |     doclose: | ||||||
| @@ -77,7 +78,8 @@ static void wav_capture (void *opaque, void *buf, int size) | |||||||
|     WAVState *wav = opaque; |     WAVState *wav = opaque; | ||||||
|  |  | ||||||
|     if (fwrite (buf, size, 1, wav->f) != 1) { |     if (fwrite (buf, size, 1, wav->f) != 1) { | ||||||
|         error_report("wav_capture: fwrite error: %s", strerror(errno)); |         monitor_printf (cur_mon, "wav_capture: fwrite error\nReason: %s", | ||||||
|  |                         strerror (errno)); | ||||||
|     } |     } | ||||||
|     wav->bytes += size; |     wav->bytes += size; | ||||||
| } | } | ||||||
| @@ -108,6 +110,7 @@ static struct capture_ops wav_capture_ops = { | |||||||
| int wav_start_capture (CaptureState *s, const char *path, int freq, | int wav_start_capture (CaptureState *s, const char *path, int freq, | ||||||
|                        int bits, int nchannels) |                        int bits, int nchannels) | ||||||
| { | { | ||||||
|  |     Monitor *mon = cur_mon; | ||||||
|     WAVState *wav; |     WAVState *wav; | ||||||
|     uint8_t hdr[] = { |     uint8_t hdr[] = { | ||||||
|         0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, |         0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, | ||||||
| @@ -121,13 +124,13 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, | |||||||
|     CaptureVoiceOut *cap; |     CaptureVoiceOut *cap; | ||||||
|  |  | ||||||
|     if (bits != 8 && bits != 16) { |     if (bits != 8 && bits != 16) { | ||||||
|         error_report("incorrect bit count %d, must be 8 or 16", bits); |         monitor_printf (mon, "incorrect bit count %d, must be 8 or 16\n", bits); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (nchannels != 1 && nchannels != 2) { |     if (nchannels != 1 && nchannels != 2) { | ||||||
|         error_report("incorrect channel count %d, must be 1 or 2", |         monitor_printf (mon, "incorrect channel count %d, must be 1 or 2\n", | ||||||
|                      nchannels); |                         nchannels); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -136,7 +139,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, | |||||||
|  |  | ||||||
|     as.freq = freq; |     as.freq = freq; | ||||||
|     as.nchannels = 1 << stereo; |     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; |     as.endianness = 0; | ||||||
|  |  | ||||||
|     ops.notify = wav_notify; |     ops.notify = wav_notify; | ||||||
| @@ -155,8 +158,8 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, | |||||||
|  |  | ||||||
|     wav->f = fopen (path, "wb"); |     wav->f = fopen (path, "wb"); | ||||||
|     if (!wav->f) { |     if (!wav->f) { | ||||||
|         error_report("Failed to open wave file `%s': %s", |         monitor_printf (mon, "Failed to open wave file `%s'\nReason: %s\n", | ||||||
|                      path, strerror(errno)); |                         path, strerror (errno)); | ||||||
|         g_free (wav); |         g_free (wav); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
| @@ -167,13 +170,14 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, | |||||||
|     wav->freq = freq; |     wav->freq = freq; | ||||||
|  |  | ||||||
|     if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) { |     if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) { | ||||||
|         error_report("Failed to write header: %s", strerror(errno)); |         monitor_printf (mon, "Failed to write header\nReason: %s\n", | ||||||
|  |                         strerror (errno)); | ||||||
|         goto error_free; |         goto error_free; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     cap = AUD_add_capture (&as, &ops, wav); |     cap = AUD_add_capture (&as, &ops, wav); | ||||||
|     if (!cap) { |     if (!cap) { | ||||||
|         error_report("Failed to add audio capture"); |         monitor_printf (mon, "Failed to add audio capture\n"); | ||||||
|         goto error_free; |         goto error_free; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -185,7 +189,8 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, | |||||||
| error_free: | error_free: | ||||||
|     g_free (wav->path); |     g_free (wav->path); | ||||||
|     if (fclose (wav->f)) { |     if (fclose (wav->f)) { | ||||||
|         error_report("Failed to close wave file: %s", strerror(errno)); |         monitor_printf (mon, "Failed to close wave file\nReason: %s\n", | ||||||
|  |                         strerror (errno)); | ||||||
|     } |     } | ||||||
|     g_free (wav); |     g_free (wav); | ||||||
|     return -1; |     return -1; | ||||||
|   | |||||||
| @@ -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-$(CONFIG_TPM) += tpm.o | ||||||
|  |  | ||||||
| common-obj-y += hostmem.o hostmem-ram.o | common-obj-y += hostmem.o hostmem-ram.o | ||||||
| common-obj-$(CONFIG_POSIX) += hostmem-file.o | common-obj-$(CONFIG_LINUX) += hostmem-file.o | ||||||
|  |  | ||||||
| common-obj-y += cryptodev.o | common-obj-y += cryptodev.o | ||||||
| common-obj-y += cryptodev-builtin.o | common-obj-y += cryptodev-builtin.o | ||||||
|  |  | ||||||
| ifeq ($(CONFIG_VIRTIO_CRYPTO),y) | ifeq ($(CONFIG_VIRTIO),y) | ||||||
| common-obj-y += cryptodev-vhost.o | 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 | endif | ||||||
|  |  | ||||||
| common-obj-$(CONFIG_LINUX) += hostmem-memfd.o | common-obj-$(CONFIG_LINUX) += hostmem-memfd.o | ||||||
|   | |||||||
| @@ -26,7 +26,6 @@ | |||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qapi/qmp/qerror.h" | #include "qapi/qmp/qerror.h" | ||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
| #include "hw/virtio/vhost-user.h" |  | ||||||
| #include "standard-headers/linux/virtio_crypto.h" | #include "standard-headers/linux/virtio_crypto.h" | ||||||
| #include "sysemu/cryptodev-vhost.h" | #include "sysemu/cryptodev-vhost.h" | ||||||
| #include "chardev/char-fe.h" | #include "chardev/char-fe.h" | ||||||
| @@ -47,7 +46,6 @@ | |||||||
| typedef struct CryptoDevBackendVhostUser { | typedef struct CryptoDevBackendVhostUser { | ||||||
|     CryptoDevBackend parent_obj; |     CryptoDevBackend parent_obj; | ||||||
|  |  | ||||||
|     VhostUserState vhost_user; |  | ||||||
|     CharBackend chr; |     CharBackend chr; | ||||||
|     char *chr_name; |     char *chr_name; | ||||||
|     bool opened; |     bool opened; | ||||||
| @@ -104,7 +102,7 @@ cryptodev_vhost_user_start(int queues, | |||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         options.opaque = &s->vhost_user; |         options.opaque = &s->chr; | ||||||
|         options.backend_type = VHOST_BACKEND_TYPE_USER; |         options.backend_type = VHOST_BACKEND_TYPE_USER; | ||||||
|         options.cc = b->conf.peers.ccs[i]; |         options.cc = b->conf.peers.ccs[i]; | ||||||
|         s->vhost_crypto[i] = cryptodev_vhost_init(&options); |         s->vhost_crypto[i] = cryptodev_vhost_init(&options); | ||||||
| @@ -157,6 +155,7 @@ static void cryptodev_vhost_user_event(void *opaque, int event) | |||||||
| { | { | ||||||
|     CryptoDevBackendVhostUser *s = opaque; |     CryptoDevBackendVhostUser *s = opaque; | ||||||
|     CryptoDevBackend *b = CRYPTODEV_BACKEND(s); |     CryptoDevBackend *b = CRYPTODEV_BACKEND(s); | ||||||
|  |     Error *err = NULL; | ||||||
|     int queues = b->conf.peers.queues; |     int queues = b->conf.peers.queues; | ||||||
|  |  | ||||||
|     assert(queues < MAX_CRYPTO_QUEUE_NUM); |     assert(queues < MAX_CRYPTO_QUEUE_NUM); | ||||||
| @@ -173,6 +172,10 @@ static void cryptodev_vhost_user_event(void *opaque, int event) | |||||||
|         cryptodev_vhost_user_stop(queues, s); |         cryptodev_vhost_user_stop(queues, s); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (err) { | ||||||
|  |         error_report_err(err); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void cryptodev_vhost_user_init( | static void cryptodev_vhost_user_init( | ||||||
| @@ -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, |     qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, | ||||||
|                      cryptodev_vhost_user_event, NULL, s, NULL, true); |                      cryptodev_vhost_user_event, NULL, s, NULL, true); | ||||||
|  |  | ||||||
| @@ -300,8 +299,6 @@ static void cryptodev_vhost_user_cleanup( | |||||||
|             backend->conf.peers.ccs[i] = NULL; |             backend->conf.peers.ccs[i] = NULL; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     vhost_user_cleanup(&s->vhost_user); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void cryptodev_vhost_user_set_chardev(Object *obj, | static void cryptodev_vhost_user_set_chardev(Object *obj, | ||||||
|   | |||||||
| @@ -12,7 +12,6 @@ | |||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qemu-common.h" | #include "qemu-common.h" | ||||||
| #include "qemu/error-report.h" |  | ||||||
| #include "sysemu/hostmem.h" | #include "sysemu/hostmem.h" | ||||||
| #include "sysemu/sysemu.h" | #include "sysemu/sysemu.h" | ||||||
| #include "qom/object_interfaces.h" | #include "qom/object_interfaces.h" | ||||||
| @@ -32,21 +31,15 @@ typedef struct HostMemoryBackendFile HostMemoryBackendFile; | |||||||
| struct HostMemoryBackendFile { | struct HostMemoryBackendFile { | ||||||
|     HostMemoryBackend parent_obj; |     HostMemoryBackend parent_obj; | ||||||
|  |  | ||||||
|  |     bool discard_data; | ||||||
|     char *mem_path; |     char *mem_path; | ||||||
|     uint64_t align; |     uint64_t align; | ||||||
|     bool discard_data; |  | ||||||
|     bool is_pmem; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void | static void | ||||||
| file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | ||||||
| { | { | ||||||
| #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); |     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend); | ||||||
|     gchar *name; |  | ||||||
|  |  | ||||||
|     if (!backend->size) { |     if (!backend->size) { | ||||||
|         error_setg(errp, "can't create backend with size 0"); |         error_setg(errp, "can't create backend with size 0"); | ||||||
| @@ -56,38 +49,19 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | |||||||
|         error_setg(errp, "mem-path property not set"); |         error_setg(errp, "mem-path property not set"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  | #ifndef CONFIG_LINUX | ||||||
|     /* |     error_setg(errp, "-mem-path not supported on this host"); | ||||||
|      * Verify pmem file size since starting a guest with an incorrect size | #else | ||||||
|      * leads to confusing failures inside the guest. |     if (!host_memory_backend_mr_inited(backend)) { | ||||||
|      */ |         gchar *path; | ||||||
|     if (fb->is_pmem) { |         backend->force_prealloc = mem_prealloc; | ||||||
|         Error *local_err = NULL; |         path = object_get_canonical_path(OBJECT(backend)); | ||||||
|         uint64_t size; |         memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), | ||||||
|  |                                  path, | ||||||
|         size = qemu_get_pmem_size(fb->mem_path, &local_err); |                                  backend->size, fb->align, backend->share, | ||||||
|         if (!size) { |                                  fb->mem_path, errp); | ||||||
|             error_propagate(errp, local_err); |         g_free(path); | ||||||
|             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; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     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 | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -104,8 +78,7 @@ static void set_mem_path(Object *o, const char *str, Error **errp) | |||||||
|     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); |     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); | ||||||
|  |  | ||||||
|     if (host_memory_backend_mr_inited(backend)) { |     if (host_memory_backend_mr_inited(backend)) { | ||||||
|         error_setg(errp, "cannot change property 'mem-path' of %s", |         error_setg(errp, "cannot change property value"); | ||||||
|                    object_get_typename(o)); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     g_free(fb->mem_path); |     g_free(fb->mem_path); | ||||||
| @@ -143,8 +116,7 @@ static void file_memory_backend_set_align(Object *o, Visitor *v, | |||||||
|     uint64_t val; |     uint64_t val; | ||||||
|  |  | ||||||
|     if (host_memory_backend_mr_inited(backend)) { |     if (host_memory_backend_mr_inited(backend)) { | ||||||
|         error_setg(&local_err, "cannot change property '%s' of %s", |         error_setg(&local_err, "cannot change property value"); | ||||||
|                    name, object_get_typename(o)); |  | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -158,39 +130,6 @@ static void file_memory_backend_set_align(Object *o, Visitor *v, | |||||||
|     error_propagate(errp, local_err); |     error_propagate(errp, local_err); | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool file_memory_backend_get_pmem(Object *o, Error **errp) |  | ||||||
| { |  | ||||||
|     return MEMORY_BACKEND_FILE(o)->is_pmem; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); |  | ||||||
|     HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); |  | ||||||
|  |  | ||||||
|     if (host_memory_backend_mr_inited(backend)) { |  | ||||||
|  |  | ||||||
|         error_setg(errp, "cannot change property 'pmem' of %s.", |  | ||||||
|                    object_get_typename(o)); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| #ifndef CONFIG_LIBPMEM |  | ||||||
|     if (value) { |  | ||||||
|         Error *local_err = NULL; |  | ||||||
|  |  | ||||||
|         error_setg(&local_err, |  | ||||||
|                    "Lack of libpmem support while setting the 'pmem=on'" |  | ||||||
|                    " of %s. We can't ensure data persistence.", |  | ||||||
|                    object_get_typename(o)); |  | ||||||
|         error_propagate(errp, local_err); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     fb->is_pmem = value; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void file_backend_unparent(Object *obj) | static void file_backend_unparent(Object *obj) | ||||||
| { | { | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||||
| @@ -222,9 +161,6 @@ file_backend_class_init(ObjectClass *oc, void *data) | |||||||
|         file_memory_backend_get_align, |         file_memory_backend_get_align, | ||||||
|         file_memory_backend_set_align, |         file_memory_backend_set_align, | ||||||
|         NULL, NULL, &error_abort); |         NULL, NULL, &error_abort); | ||||||
|     object_class_property_add_bool(oc, "pmem", |  | ||||||
|         file_memory_backend_get_pmem, file_memory_backend_set_pmem, |  | ||||||
|         &error_abort); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void file_backend_instance_finalize(Object *o) | static void file_backend_instance_finalize(Object *o) | ||||||
|   | |||||||
| @@ -44,6 +44,10 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (host_memory_backend_mr_inited(backend)) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     backend->force_prealloc = mem_prealloc; |     backend->force_prealloc = mem_prealloc; | ||||||
|     fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size, |     fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size, | ||||||
|                            m->hugetlb, m->hugetlbsize, m->seal ? |                            m->hugetlb, m->hugetlbsize, m->seal ? | ||||||
| @@ -53,10 +57,9 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     name = host_memory_backend_get_name(backend); |     name = object_get_canonical_path(OBJECT(backend)); | ||||||
|     memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), |     memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), | ||||||
|                                    name, backend->size, |                                    name, backend->size, true, fd, errp); | ||||||
|                                    backend->share, fd, errp); |  | ||||||
|     g_free(name); |     g_free(name); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -128,7 +131,6 @@ memfd_backend_instance_init(Object *obj) | |||||||
|  |  | ||||||
|     /* default to sealed file */ |     /* default to sealed file */ | ||||||
|     m->seal = true; |     m->seal = true; | ||||||
|     MEMORY_BACKEND(m)->share = true; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| @@ -138,29 +140,18 @@ memfd_backend_class_init(ObjectClass *oc, void *data) | |||||||
|  |  | ||||||
|     bc->alloc = memfd_backend_memory_alloc; |     bc->alloc = memfd_backend_memory_alloc; | ||||||
|  |  | ||||||
|     if (qemu_memfd_check(MFD_HUGETLB)) { |     object_class_property_add_bool(oc, "hugetlb", | ||||||
|         object_class_property_add_bool(oc, "hugetlb", |                                    memfd_backend_get_hugetlb, | ||||||
|                                        memfd_backend_get_hugetlb, |                                    memfd_backend_set_hugetlb, | ||||||
|                                        memfd_backend_set_hugetlb, |                                    &error_abort); | ||||||
|                                        &error_abort); |     object_class_property_add(oc, "hugetlbsize", "int", | ||||||
|         object_class_property_set_description(oc, "hugetlb", |                               memfd_backend_get_hugetlbsize, | ||||||
|                                               "Use huge pages", |                               memfd_backend_set_hugetlbsize, | ||||||
|                                               &error_abort); |                               NULL, NULL, &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, "seal", |     object_class_property_add_bool(oc, "seal", | ||||||
|                                    memfd_backend_get_seal, |                                    memfd_backend_get_seal, | ||||||
|                                    memfd_backend_set_seal, |                                    memfd_backend_set_seal, | ||||||
|                                    &error_abort); |                                    &error_abort); | ||||||
|     object_class_property_set_description(oc, "seal", |  | ||||||
|                                           "Seal growing & shrinking", |  | ||||||
|                                           &error_abort); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static const TypeInfo memfd_backend_info = { | static const TypeInfo memfd_backend_info = { | ||||||
| @@ -173,9 +164,7 @@ static const TypeInfo memfd_backend_info = { | |||||||
|  |  | ||||||
| static void register_types(void) | 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); | type_init(register_types); | ||||||
|   | |||||||
| @@ -16,20 +16,21 @@ | |||||||
|  |  | ||||||
| #define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram" | #define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram" | ||||||
|  |  | ||||||
|  |  | ||||||
| static void | static void | ||||||
| ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) | ||||||
| { | { | ||||||
|     char *name; |     char *path; | ||||||
|  |  | ||||||
|     if (!backend->size) { |     if (!backend->size) { | ||||||
|         error_setg(errp, "can't create backend with size 0"); |         error_setg(errp, "can't create backend with size 0"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     name = host_memory_backend_get_name(backend); |     path = object_get_canonical_path_component(OBJECT(backend)); | ||||||
|     memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), name, |     memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), path, | ||||||
|                            backend->size, backend->share, errp); |                            backend->size, backend->share, errp); | ||||||
|     g_free(name); |     g_free(path); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
|   | |||||||
| @@ -18,7 +18,6 @@ | |||||||
| #include "qapi/visitor.h" | #include "qapi/visitor.h" | ||||||
| #include "qemu/config-file.h" | #include "qemu/config-file.h" | ||||||
| #include "qom/object_interfaces.h" | #include "qom/object_interfaces.h" | ||||||
| #include "qemu/mmap-alloc.h" |  | ||||||
|  |  | ||||||
| #ifdef CONFIG_NUMA | #ifdef CONFIG_NUMA | ||||||
| #include <numaif.h> | #include <numaif.h> | ||||||
| @@ -28,16 +27,6 @@ QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_BIND != MPOL_BIND); | |||||||
| QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE); | QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| char * |  | ||||||
| host_memory_backend_get_name(HostMemoryBackend *backend) |  | ||||||
| { |  | ||||||
|     if (!backend->use_canonical_path) { |  | ||||||
|         return object_get_canonical_path_component(OBJECT(backend)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return object_get_canonical_path(OBJECT(backend)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void | static void | ||||||
| host_memory_backend_get_size(Object *obj, Visitor *v, const char *name, | host_memory_backend_get_size(Object *obj, Visitor *v, const char *name, | ||||||
|                              void *opaque, Error **errp) |                              void *opaque, Error **errp) | ||||||
| @@ -57,8 +46,7 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name, | |||||||
|     uint64_t value; |     uint64_t value; | ||||||
|  |  | ||||||
|     if (host_memory_backend_mr_inited(backend)) { |     if (host_memory_backend_mr_inited(backend)) { | ||||||
|         error_setg(&local_err, "cannot change property %s of %s ", |         error_setg(&local_err, "cannot change property value"); | ||||||
|                    name, object_get_typename(obj)); |  | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -67,9 +55,8 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name, | |||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|     if (!value) { |     if (!value) { | ||||||
|         error_setg(&local_err, |         error_setg(&local_err, "Property '%s.%s' doesn't take value '%" | ||||||
|                    "property '%s' of %s doesn't take value '%" PRIu64 "'", |                    PRIu64 "'", object_get_typename(obj), name, value); | ||||||
|                    name, object_get_typename(obj), value); |  | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|     backend->size = value; |     backend->size = value; | ||||||
| @@ -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); |     value = find_first_bit(backend->host_nodes, MAX_NODES); | ||||||
|     if (value == MAX_NODES) { |     if (value == MAX_NODES) { | ||||||
|         goto ret; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     *node = g_malloc0(sizeof(**node)); |     *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; |         node = &(*node)->next; | ||||||
|     } while (true); |     } while (true); | ||||||
|  |  | ||||||
| ret: |  | ||||||
|     visit_type_uint16List(v, name, &host_nodes, errp); |     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 | #ifdef CONFIG_NUMA | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |     HostMemoryBackend *backend = MEMORY_BACKEND(obj); | ||||||
|     uint16List *l, *host_nodes = NULL; |     uint16List *l = NULL; | ||||||
|  |  | ||||||
|     visit_type_uint16List(v, name, &host_nodes, errp); |     visit_type_uint16List(v, name, &l, errp); | ||||||
|  |  | ||||||
|     for (l = host_nodes; l; l = l->next) { |     while (l) { | ||||||
|         if (l->value >= MAX_NODES) { |  | ||||||
|             error_setg(errp, "Invalid host-nodes value: %d", l->value); |  | ||||||
|             goto out; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     for (l = host_nodes; l; l = l->next) { |  | ||||||
|         bitmap_set(backend->host_nodes, l->value, 1); |         bitmap_set(backend->host_nodes, l->value, 1); | ||||||
|  |         l = l->next; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| out: |  | ||||||
|     qapi_free_uint16List(host_nodes); |  | ||||||
| #else | #else | ||||||
|     error_setg(errp, "NUMA node binding are not supported by this QEMU"); |     error_setg(errp, "NUMA node binding are not supported by this QEMU"); | ||||||
| #endif | #endif | ||||||
| @@ -260,11 +237,6 @@ static void host_memory_backend_init(Object *obj) | |||||||
|     backend->prealloc = mem_prealloc; |     backend->prealloc = mem_prealloc; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void host_memory_backend_post_init(Object *obj) |  | ||||||
| { |  | ||||||
|     object_apply_compat_props(obj); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool host_memory_backend_mr_inited(HostMemoryBackend *backend) | bool host_memory_backend_mr_inited(HostMemoryBackend *backend) | ||||||
| { | { | ||||||
|     /* |     /* | ||||||
| @@ -274,7 +246,8 @@ bool host_memory_backend_mr_inited(HostMemoryBackend *backend) | |||||||
|     return memory_region_size(&backend->mr) != 0; |     return memory_region_size(&backend->mr) != 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend) | MemoryRegion * | ||||||
|  | host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp) | ||||||
| { | { | ||||||
|     return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL; |     return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL; | ||||||
| } | } | ||||||
| @@ -289,23 +262,6 @@ bool host_memory_backend_is_mapped(HostMemoryBackend *backend) | |||||||
|     return backend->is_mapped; |     return backend->is_mapped; | ||||||
| } | } | ||||||
|  |  | ||||||
| #ifdef __linux__ |  | ||||||
| size_t host_memory_backend_pagesize(HostMemoryBackend *memdev) |  | ||||||
| { |  | ||||||
|     Object *obj = OBJECT(memdev); |  | ||||||
|     char *path = object_property_get_str(obj, "mem-path", NULL); |  | ||||||
|     size_t pagesize = qemu_mempath_getpagesize(path); |  | ||||||
|  |  | ||||||
|     g_free(path); |  | ||||||
|     return pagesize; |  | ||||||
| } |  | ||||||
| #else |  | ||||||
| size_t host_memory_backend_pagesize(HostMemoryBackend *memdev) |  | ||||||
| { |  | ||||||
|     return getpagesize(); |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| static void | static void | ||||||
| host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) | host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) | ||||||
| { | { | ||||||
| @@ -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) | static bool host_memory_backend_get_share(Object *o, Error **errp) | ||||||
| { | { | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(o); |     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; |     backend->share = value; | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool |  | ||||||
| host_memory_backend_get_use_canonical_path(Object *obj, Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |  | ||||||
|  |  | ||||||
|     return backend->use_canonical_path; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void |  | ||||||
| host_memory_backend_set_use_canonical_path(Object *obj, bool value, |  | ||||||
|                                            Error **errp) |  | ||||||
| { |  | ||||||
|     HostMemoryBackend *backend = MEMORY_BACKEND(obj); |  | ||||||
|  |  | ||||||
|     backend->use_canonical_path = value; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void | static void | ||||||
| host_memory_backend_class_init(ObjectClass *oc, void *data) | host_memory_backend_class_init(ObjectClass *oc, void *data) | ||||||
| { | { | ||||||
| @@ -441,44 +398,34 @@ host_memory_backend_class_init(ObjectClass *oc, void *data) | |||||||
|     object_class_property_add_bool(oc, "merge", |     object_class_property_add_bool(oc, "merge", | ||||||
|         host_memory_backend_get_merge, |         host_memory_backend_get_merge, | ||||||
|         host_memory_backend_set_merge, &error_abort); |         host_memory_backend_set_merge, &error_abort); | ||||||
|     object_class_property_set_description(oc, "merge", |  | ||||||
|         "Mark memory as mergeable", &error_abort); |  | ||||||
|     object_class_property_add_bool(oc, "dump", |     object_class_property_add_bool(oc, "dump", | ||||||
|         host_memory_backend_get_dump, |         host_memory_backend_get_dump, | ||||||
|         host_memory_backend_set_dump, &error_abort); |         host_memory_backend_set_dump, &error_abort); | ||||||
|     object_class_property_set_description(oc, "dump", |  | ||||||
|         "Set to 'off' to exclude from core dump", &error_abort); |  | ||||||
|     object_class_property_add_bool(oc, "prealloc", |     object_class_property_add_bool(oc, "prealloc", | ||||||
|         host_memory_backend_get_prealloc, |         host_memory_backend_get_prealloc, | ||||||
|         host_memory_backend_set_prealloc, &error_abort); |         host_memory_backend_set_prealloc, &error_abort); | ||||||
|     object_class_property_set_description(oc, "prealloc", |  | ||||||
|         "Preallocate memory", &error_abort); |  | ||||||
|     object_class_property_add(oc, "size", "int", |     object_class_property_add(oc, "size", "int", | ||||||
|         host_memory_backend_get_size, |         host_memory_backend_get_size, | ||||||
|         host_memory_backend_set_size, |         host_memory_backend_set_size, | ||||||
|         NULL, NULL, &error_abort); |         NULL, NULL, &error_abort); | ||||||
|     object_class_property_set_description(oc, "size", |  | ||||||
|         "Size of the memory region (ex: 500M)", &error_abort); |  | ||||||
|     object_class_property_add(oc, "host-nodes", "int", |     object_class_property_add(oc, "host-nodes", "int", | ||||||
|         host_memory_backend_get_host_nodes, |         host_memory_backend_get_host_nodes, | ||||||
|         host_memory_backend_set_host_nodes, |         host_memory_backend_set_host_nodes, | ||||||
|         NULL, NULL, &error_abort); |         NULL, NULL, &error_abort); | ||||||
|     object_class_property_set_description(oc, "host-nodes", |  | ||||||
|         "Binds memory to the list of NUMA host nodes", &error_abort); |  | ||||||
|     object_class_property_add_enum(oc, "policy", "HostMemPolicy", |     object_class_property_add_enum(oc, "policy", "HostMemPolicy", | ||||||
|         &HostMemPolicy_lookup, |         &HostMemPolicy_lookup, | ||||||
|         host_memory_backend_get_policy, |         host_memory_backend_get_policy, | ||||||
|         host_memory_backend_set_policy, &error_abort); |         host_memory_backend_set_policy, &error_abort); | ||||||
|     object_class_property_set_description(oc, "policy", |     object_class_property_add_str(oc, "id", get_id, set_id, &error_abort); | ||||||
|         "Set the NUMA policy", &error_abort); |  | ||||||
|     object_class_property_add_bool(oc, "share", |     object_class_property_add_bool(oc, "share", | ||||||
|         host_memory_backend_get_share, host_memory_backend_set_share, |         host_memory_backend_get_share, host_memory_backend_set_share, | ||||||
|         &error_abort); |         &error_abort); | ||||||
|     object_class_property_set_description(oc, "share", | } | ||||||
|         "Mark the memory as private to QEMU or shared", &error_abort); |  | ||||||
|     object_class_property_add_bool(oc, "x-use-canonical-path-for-ramblock-id", | static void host_memory_backend_finalize(Object *o) | ||||||
|         host_memory_backend_get_use_canonical_path, | { | ||||||
|         host_memory_backend_set_use_canonical_path, &error_abort); |     HostMemoryBackend *backend = MEMORY_BACKEND(o); | ||||||
|  |     g_free(backend->id); | ||||||
| } | } | ||||||
|  |  | ||||||
| static const TypeInfo host_memory_backend_info = { | static const TypeInfo host_memory_backend_info = { | ||||||
| @@ -489,7 +436,7 @@ static const TypeInfo host_memory_backend_info = { | |||||||
|     .class_init = host_memory_backend_class_init, |     .class_init = host_memory_backend_class_init, | ||||||
|     .instance_size = sizeof(HostMemoryBackend), |     .instance_size = sizeof(HostMemoryBackend), | ||||||
|     .instance_init = host_memory_backend_init, |     .instance_init = host_memory_backend_init, | ||||||
|     .instance_post_init = host_memory_backend_post_init, |     .instance_finalize = host_memory_backend_finalize, | ||||||
|     .interfaces = (InterfaceInfo[]) { |     .interfaces = (InterfaceInfo[]) { | ||||||
|         { TYPE_USER_CREATABLE }, |         { TYPE_USER_CREATABLE }, | ||||||
|         { } |         { } | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								balloon.c
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								balloon.c
									
									
									
									
									
								
							| @@ -26,7 +26,6 @@ | |||||||
|  |  | ||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include "qemu-common.h" | #include "qemu-common.h" | ||||||
| #include "qemu/atomic.h" |  | ||||||
| #include "exec/cpu-common.h" | #include "exec/cpu-common.h" | ||||||
| #include "sysemu/kvm.h" | #include "sysemu/kvm.h" | ||||||
| #include "sysemu/balloon.h" | #include "sysemu/balloon.h" | ||||||
| @@ -38,22 +37,16 @@ | |||||||
| static QEMUBalloonEvent *balloon_event_fn; | static QEMUBalloonEvent *balloon_event_fn; | ||||||
| static QEMUBalloonStatus *balloon_stat_fn; | static QEMUBalloonStatus *balloon_stat_fn; | ||||||
| static void *balloon_opaque; | static void *balloon_opaque; | ||||||
| static int balloon_inhibit_count; | static bool balloon_inhibited; | ||||||
|  |  | ||||||
| bool qemu_balloon_is_inhibited(void) | bool qemu_balloon_is_inhibited(void) | ||||||
| { | { | ||||||
|     return atomic_read(&balloon_inhibit_count) > 0; |     return balloon_inhibited; | ||||||
| } | } | ||||||
|  |  | ||||||
| void qemu_balloon_inhibit(bool state) | void qemu_balloon_inhibit(bool state) | ||||||
| { | { | ||||||
|     if (state) { |     balloon_inhibited = state; | ||||||
|         atomic_inc(&balloon_inhibit_count); |  | ||||||
|     } else { |  | ||||||
|         atomic_dec(&balloon_inhibit_count); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     assert(atomic_read(&balloon_inhibit_count) >= 0); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool have_balloon(Error **errp) | static bool have_balloon(Error **errp) | ||||||
|   | |||||||
| @@ -1,19 +1,10 @@ | |||||||
| block-obj-y += raw-format.o vmdk.o vpc.o | block-obj-y += raw-format.o qcow.o vdi.o vmdk.o cloop.o bochs.o vpc.o vvfat.o dmg.o | ||||||
| block-obj-$(CONFIG_QCOW1) += qcow.o |  | ||||||
| block-obj-$(CONFIG_VDI) += vdi.o |  | ||||||
| block-obj-$(CONFIG_CLOOP) += cloop.o |  | ||||||
| block-obj-$(CONFIG_BOCHS) += bochs.o |  | ||||||
| block-obj-$(CONFIG_VVFAT) += vvfat.o |  | ||||||
| block-obj-$(CONFIG_DMG) += dmg.o |  | ||||||
|  |  | ||||||
| block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o | block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o | ||||||
| block-obj-$(CONFIG_QED) += qed.o qed-l2-cache.o qed-table.o qed-cluster.o | block-obj-y += qed.o qed-l2-cache.o qed-table.o qed-cluster.o | ||||||
| block-obj-$(CONFIG_QED) += qed-check.o | block-obj-y += qed-check.o | ||||||
| block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o | block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o | ||||||
| block-obj-y += quorum.o | block-obj-y += quorum.o | ||||||
| block-obj-y += blkdebug.o blkverify.o blkreplay.o | block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o | ||||||
| block-obj-$(CONFIG_PARALLELS) += parallels.o |  | ||||||
| block-obj-y += blklogwrites.o |  | ||||||
| block-obj-y += block-backend.o snapshot.o qapi.o | block-obj-y += block-backend.o snapshot.o qapi.o | ||||||
| block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o | block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o | ||||||
| block-obj-$(CONFIG_POSIX) += file-posix.o | block-obj-$(CONFIG_POSIX) += file-posix.o | ||||||
| @@ -22,8 +13,7 @@ block-obj-y += null.o mirror.o commit.o io.o create.o | |||||||
| block-obj-y += throttle-groups.o | block-obj-y += throttle-groups.o | ||||||
| block-obj-$(CONFIG_LINUX) += nvme.o | block-obj-$(CONFIG_LINUX) += nvme.o | ||||||
|  |  | ||||||
| block-obj-y += nbd.o nbd-client.o | block-obj-y += nbd.o nbd-client.o sheepdog.o | ||||||
| block-obj-$(CONFIG_SHEEPDOG) += sheepdog.o |  | ||||||
| block-obj-$(CONFIG_LIBISCSI) += iscsi.o | block-obj-$(CONFIG_LIBISCSI) += iscsi.o | ||||||
| block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o | block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o | ||||||
| block-obj-$(CONFIG_LIBNFS) += nfs.o | block-obj-$(CONFIG_LIBNFS) += nfs.o | ||||||
| @@ -36,7 +26,7 @@ block-obj-y += accounting.o dirty-bitmap.o | |||||||
| block-obj-y += write-threshold.o | block-obj-y += write-threshold.o | ||||||
| block-obj-y += backup.o | block-obj-y += backup.o | ||||||
| block-obj-$(CONFIG_REPLICATION) += replication.o | block-obj-$(CONFIG_REPLICATION) += replication.o | ||||||
| block-obj-y += throttle.o copy-on-read.o | block-obj-y += throttle.o | ||||||
|  |  | ||||||
| block-obj-y += crypto.o | block-obj-y += crypto.o | ||||||
|  |  | ||||||
| @@ -54,11 +44,8 @@ gluster.o-libs     := $(GLUSTERFS_LIBS) | |||||||
| vxhs.o-libs        := $(VXHS_LIBS) | vxhs.o-libs        := $(VXHS_LIBS) | ||||||
| ssh.o-cflags       := $(LIBSSH2_CFLAGS) | ssh.o-cflags       := $(LIBSSH2_CFLAGS) | ||||||
| ssh.o-libs         := $(LIBSSH2_LIBS) | ssh.o-libs         := $(LIBSSH2_LIBS) | ||||||
| block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o | block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o | ||||||
| block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y) |  | ||||||
| dmg-bz2.o-libs     := $(BZIP2_LIBS) | dmg-bz2.o-libs     := $(BZIP2_LIBS) | ||||||
| block-obj-$(if $(CONFIG_LZFSE),m,n) += dmg-lzfse.o |  | ||||||
| dmg-lzfse.o-libs   := $(LZFSE_LIBS) |  | ||||||
| qcow.o-libs        := -lz | qcow.o-libs        := -lz | ||||||
| linux-aio.o-libs   := -laio | linux-aio.o-libs   := -laio | ||||||
| parallels.o-cflags := $(LIBXML2_CFLAGS) | parallels.o-cflags := $(LIBXML2_CFLAGS) | ||||||
|   | |||||||
| @@ -94,94 +94,6 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie, | |||||||
|     cookie->type = type; |     cookie->type = type; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* block_latency_histogram_compare_func: |  | ||||||
|  * Compare @key with interval [@it[0], @it[1]). |  | ||||||
|  * Return: -1 if @key < @it[0] |  | ||||||
|  *          0 if @key in [@it[0], @it[1]) |  | ||||||
|  *         +1 if @key >= @it[1] |  | ||||||
|  */ |  | ||||||
| static int block_latency_histogram_compare_func(const void *key, const void *it) |  | ||||||
| { |  | ||||||
|     uint64_t k = *(uint64_t *)key; |  | ||||||
|     uint64_t a = ((uint64_t *)it)[0]; |  | ||||||
|     uint64_t b = ((uint64_t *)it)[1]; |  | ||||||
|  |  | ||||||
|     return k < a ? -1 : (k < b ? 0 : 1); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void block_latency_histogram_account(BlockLatencyHistogram *hist, |  | ||||||
|                                             int64_t latency_ns) |  | ||||||
| { |  | ||||||
|     uint64_t *pos; |  | ||||||
|  |  | ||||||
|     if (hist->bins == NULL) { |  | ||||||
|         /* histogram disabled */ |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     if (latency_ns < hist->boundaries[0]) { |  | ||||||
|         hist->bins[0]++; |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (latency_ns >= hist->boundaries[hist->nbins - 2]) { |  | ||||||
|         hist->bins[hist->nbins - 1]++; |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     pos = bsearch(&latency_ns, hist->boundaries, hist->nbins - 2, |  | ||||||
|                   sizeof(hist->boundaries[0]), |  | ||||||
|                   block_latency_histogram_compare_func); |  | ||||||
|     assert(pos != NULL); |  | ||||||
|  |  | ||||||
|     hist->bins[pos - hist->boundaries + 1]++; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type, |  | ||||||
|                                 uint64List *boundaries) |  | ||||||
| { |  | ||||||
|     BlockLatencyHistogram *hist = &stats->latency_histogram[type]; |  | ||||||
|     uint64List *entry; |  | ||||||
|     uint64_t *ptr; |  | ||||||
|     uint64_t prev = 0; |  | ||||||
|     int new_nbins = 1; |  | ||||||
|  |  | ||||||
|     for (entry = boundaries; entry; entry = entry->next) { |  | ||||||
|         if (entry->value <= prev) { |  | ||||||
|             return -EINVAL; |  | ||||||
|         } |  | ||||||
|         new_nbins++; |  | ||||||
|         prev = entry->value; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     hist->nbins = new_nbins; |  | ||||||
|     g_free(hist->boundaries); |  | ||||||
|     hist->boundaries = g_new(uint64_t, hist->nbins - 1); |  | ||||||
|     for (entry = boundaries, ptr = hist->boundaries; entry; |  | ||||||
|          entry = entry->next, ptr++) |  | ||||||
|     { |  | ||||||
|         *ptr = entry->value; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     g_free(hist->bins); |  | ||||||
|     hist->bins = g_new0(uint64_t, hist->nbins); |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void block_latency_histograms_clear(BlockAcctStats *stats) |  | ||||||
| { |  | ||||||
|     int i; |  | ||||||
|  |  | ||||||
|     for (i = 0; i < BLOCK_MAX_IOTYPE; i++) { |  | ||||||
|         BlockLatencyHistogram *hist = &stats->latency_histogram[i]; |  | ||||||
|         g_free(hist->bins); |  | ||||||
|         g_free(hist->boundaries); |  | ||||||
|         memset(hist, 0, sizeof(*hist)); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, | static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, | ||||||
|                                  bool failed) |                                  bool failed) | ||||||
| { | { | ||||||
| @@ -204,9 +116,6 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, | |||||||
|         stats->nr_ops[cookie->type]++; |         stats->nr_ops[cookie->type]++; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     block_latency_histogram_account(&stats->latency_histogram[cookie->type], |  | ||||||
|                                     latency_ns); |  | ||||||
|  |  | ||||||
|     if (!failed || stats->account_failed) { |     if (!failed || stats->account_failed) { | ||||||
|         stats->total_time_ns[cookie->type] += latency_ns; |         stats->total_time_ns[cookie->type] += latency_ns; | ||||||
|         stats->last_access_time_ns = time_ns; |         stats->last_access_time_ns = time_ns; | ||||||
|   | |||||||
							
								
								
									
										369
									
								
								block/backup.c
									
									
									
									
									
								
							
							
						
						
									
										369
									
								
								block/backup.c
									
									
									
									
									
								
							| @@ -27,13 +27,7 @@ | |||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
|  |  | ||||||
| #define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16) | #define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16) | ||||||
|  | #define SLICE_TIME 100000000ULL /* ns */ | ||||||
| typedef struct CowRequest { |  | ||||||
|     int64_t start_byte; |  | ||||||
|     int64_t end_byte; |  | ||||||
|     QLIST_ENTRY(CowRequest) list; |  | ||||||
|     CoQueue wait_queue; /* coroutines blocked on this request */ |  | ||||||
| } CowRequest; |  | ||||||
|  |  | ||||||
| typedef struct BackupBlockJob { | typedef struct BackupBlockJob { | ||||||
|     BlockJob common; |     BlockJob common; | ||||||
| @@ -41,10 +35,10 @@ typedef struct BackupBlockJob { | |||||||
|     /* bitmap for sync=incremental */ |     /* bitmap for sync=incremental */ | ||||||
|     BdrvDirtyBitmap *sync_bitmap; |     BdrvDirtyBitmap *sync_bitmap; | ||||||
|     MirrorSyncMode sync_mode; |     MirrorSyncMode sync_mode; | ||||||
|  |     RateLimit limit; | ||||||
|     BlockdevOnError on_source_error; |     BlockdevOnError on_source_error; | ||||||
|     BlockdevOnError on_target_error; |     BlockdevOnError on_target_error; | ||||||
|     CoRwlock flush_rwlock; |     CoRwlock flush_rwlock; | ||||||
|     uint64_t len; |  | ||||||
|     uint64_t bytes_read; |     uint64_t bytes_read; | ||||||
|     int64_t cluster_size; |     int64_t cluster_size; | ||||||
|     bool compress; |     bool compress; | ||||||
| @@ -52,14 +46,8 @@ typedef struct BackupBlockJob { | |||||||
|     QLIST_HEAD(, CowRequest) inflight_reqs; |     QLIST_HEAD(, CowRequest) inflight_reqs; | ||||||
|  |  | ||||||
|     HBitmap *copy_bitmap; |     HBitmap *copy_bitmap; | ||||||
|     bool use_copy_range; |  | ||||||
|     int64_t copy_range_size; |  | ||||||
|  |  | ||||||
|     bool serialize_target_writes; |  | ||||||
| } BackupBlockJob; | } BackupBlockJob; | ||||||
|  |  | ||||||
| static const BlockJobDriver backup_job_driver; |  | ||||||
|  |  | ||||||
| /* See if in-flight requests overlap and wait for them to complete */ | /* See if in-flight requests overlap and wait for them to complete */ | ||||||
| static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job, | static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job, | ||||||
|                                                        int64_t start, |                                                        int64_t start, | ||||||
| @@ -97,101 +85,19 @@ static void cow_request_end(CowRequest *req) | |||||||
|     qemu_co_queue_restart_all(&req->wait_queue); |     qemu_co_queue_restart_all(&req->wait_queue); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Copy range to target with a bounce buffer and return the bytes copied. If |  | ||||||
|  * error occurred, return a negative error number */ |  | ||||||
| static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, |  | ||||||
|                                                       int64_t start, |  | ||||||
|                                                       int64_t end, |  | ||||||
|                                                       bool is_write_notifier, |  | ||||||
|                                                       bool *error_is_read, |  | ||||||
|                                                       void **bounce_buffer) |  | ||||||
| { |  | ||||||
|     int ret; |  | ||||||
|     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, | static int coroutine_fn backup_do_cow(BackupBlockJob *job, | ||||||
|                                       int64_t offset, uint64_t bytes, |                                       int64_t offset, uint64_t bytes, | ||||||
|                                       bool *error_is_read, |                                       bool *error_is_read, | ||||||
|                                       bool is_write_notifier) |                                       bool is_write_notifier) | ||||||
| { | { | ||||||
|  |     BlockBackend *blk = job->common.blk; | ||||||
|     CowRequest cow_request; |     CowRequest cow_request; | ||||||
|  |     struct iovec iov; | ||||||
|  |     QEMUIOVector bounce_qiov; | ||||||
|  |     void *bounce_buffer = NULL; | ||||||
|     int ret = 0; |     int ret = 0; | ||||||
|     int64_t start, end; /* bytes */ |     int64_t start, end; /* bytes */ | ||||||
|     void *bounce_buffer = NULL; |     int n; /* bytes */ | ||||||
|  |  | ||||||
|     qemu_co_rwlock_rdlock(&job->flush_rwlock); |     qemu_co_rwlock_rdlock(&job->flush_rwlock); | ||||||
|  |  | ||||||
| @@ -203,38 +109,60 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job, | |||||||
|     wait_for_overlapping_requests(job, start, end); |     wait_for_overlapping_requests(job, start, end); | ||||||
|     cow_request_begin(&cow_request, job, start, end); |     cow_request_begin(&cow_request, job, start, end); | ||||||
|  |  | ||||||
|     while (start < end) { |     for (; start < end; start += job->cluster_size) { | ||||||
|         if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) { |         if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) { | ||||||
|             trace_backup_do_cow_skip(job, start); |             trace_backup_do_cow_skip(job, start); | ||||||
|             start += job->cluster_size; |  | ||||||
|             continue; /* already copied */ |             continue; /* already copied */ | ||||||
|         } |         } | ||||||
|  |         hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1); | ||||||
|  |  | ||||||
|         trace_backup_do_cow_process(job, start); |         trace_backup_do_cow_process(job, start); | ||||||
|  |  | ||||||
|         if (job->use_copy_range) { |         n = MIN(job->cluster_size, job->common.len - start); | ||||||
|             ret = backup_cow_with_offload(job, start, end, is_write_notifier); |  | ||||||
|             if (ret < 0) { |         if (!bounce_buffer) { | ||||||
|                 job->use_copy_range = false; |             bounce_buffer = blk_blockalign(blk, job->cluster_size); | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|         if (!job->use_copy_range) { |         iov.iov_base = bounce_buffer; | ||||||
|             ret = backup_cow_with_bounce_buffer(job, start, end, is_write_notifier, |         iov.iov_len = n; | ||||||
|                                                 error_is_read, &bounce_buffer); |         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) { |         if (ret < 0) { | ||||||
|             break; |             trace_backup_do_cow_write_fail(job, start, ret); | ||||||
|  |             if (error_is_read) { | ||||||
|  |                 *error_is_read = false; | ||||||
|  |             } | ||||||
|  |             hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1); | ||||||
|  |             goto out; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         /* Publish progress, guest I/O counts as progress too.  Note that the |         /* Publish progress, guest I/O counts as progress too.  Note that the | ||||||
|          * offset field is an opaque progress value, it is not a disk offset. |          * offset field is an opaque progress value, it is not a disk offset. | ||||||
|          */ |          */ | ||||||
|         start += ret; |         job->bytes_read += n; | ||||||
|         job->bytes_read += ret; |         job->common.offset += n; | ||||||
|         job_progress_update(&job->common.job, ret); |  | ||||||
|         ret = 0; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | out: | ||||||
|     if (bounce_buffer) { |     if (bounce_buffer) { | ||||||
|         qemu_vfree(bounce_buffer); |         qemu_vfree(bounce_buffer); | ||||||
|     } |     } | ||||||
| @@ -262,12 +190,23 @@ static int coroutine_fn backup_before_write_notify( | |||||||
|     return backup_do_cow(job, req->offset, req->bytes, NULL, true); |     return backup_do_cow(job, req->offset, req->bytes, NULL, true); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp) | ||||||
|  | { | ||||||
|  |     BackupBlockJob *s = container_of(job, BackupBlockJob, common); | ||||||
|  |  | ||||||
|  |     if (speed < 0) { | ||||||
|  |         error_setg(errp, QERR_INVALID_PARAMETER, "speed"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     ratelimit_set_speed(&s->limit, speed, SLICE_TIME); | ||||||
|  | } | ||||||
|  |  | ||||||
| static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) | static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) | ||||||
| { | { | ||||||
|     BdrvDirtyBitmap *bm; |     BdrvDirtyBitmap *bm; | ||||||
|     BlockDriverState *bs = blk_bs(job->common.blk); |     BlockDriverState *bs = blk_bs(job->common.blk); | ||||||
|  |  | ||||||
|     if (ret < 0) { |     if (ret < 0 || block_job_is_cancelled(&job->common)) { | ||||||
|         /* Merge the successor back into the parent, delete nothing. */ |         /* Merge the successor back into the parent, delete nothing. */ | ||||||
|         bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL); |         bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL); | ||||||
|         assert(bm); |         assert(bm); | ||||||
| @@ -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) { |     if (s->sync_bitmap) { | ||||||
|         backup_cleanup_sync_bitmap(s, 0); |         backup_cleanup_sync_bitmap(s, 0); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void backup_abort(Job *job) | static void backup_abort(BlockJob *job) | ||||||
| { | { | ||||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); |     BackupBlockJob *s = container_of(job, BackupBlockJob, common); | ||||||
|     if (s->sync_bitmap) { |     if (s->sync_bitmap) { | ||||||
|         backup_cleanup_sync_bitmap(s, -1); |         backup_cleanup_sync_bitmap(s, -1); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void backup_clean(Job *job) | static void backup_clean(BlockJob *job) | ||||||
| { | { | ||||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); |     BackupBlockJob *s = container_of(job, BackupBlockJob, common); | ||||||
|     assert(s->target); |     assert(s->target); | ||||||
|     blk_unref(s->target); |     blk_unref(s->target); | ||||||
|     s->target = NULL; |     s->target = NULL; | ||||||
| @@ -314,7 +253,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp) | |||||||
|     BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); |     BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); | ||||||
|     int64_t len; |     int64_t len; | ||||||
|  |  | ||||||
|     assert(block_job_driver(job) == &backup_job_driver); |     assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); | ||||||
|  |  | ||||||
|     if (backup_job->sync_mode != MIRROR_SYNC_MODE_NONE) { |     if (backup_job->sync_mode != MIRROR_SYNC_MODE_NONE) { | ||||||
|         error_setg(errp, "The backup job only supports block checkpoint in" |         error_setg(errp, "The backup job only supports block checkpoint in" | ||||||
| @@ -322,10 +261,41 @@ void backup_do_checkpoint(BlockJob *job, Error **errp) | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     len = DIV_ROUND_UP(backup_job->len, backup_job->cluster_size); |     len = DIV_ROUND_UP(backup_job->common.len, backup_job->cluster_size); | ||||||
|     hbitmap_set(backup_job->copy_bitmap, 0, len); |     hbitmap_set(backup_job->copy_bitmap, 0, len); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset, | ||||||
|  |                                           uint64_t bytes) | ||||||
|  | { | ||||||
|  |     BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); | ||||||
|  |     int64_t start, end; | ||||||
|  |  | ||||||
|  |     assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); | ||||||
|  |  | ||||||
|  |     start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size); | ||||||
|  |     end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size); | ||||||
|  |     wait_for_overlapping_requests(backup_job, start, end); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void backup_cow_request_begin(CowRequest *req, BlockJob *job, | ||||||
|  |                               int64_t offset, uint64_t bytes) | ||||||
|  | { | ||||||
|  |     BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); | ||||||
|  |     int64_t start, end; | ||||||
|  |  | ||||||
|  |     assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); | ||||||
|  |  | ||||||
|  |     start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size); | ||||||
|  |     end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size); | ||||||
|  |     cow_request_begin(req, backup_job, start, end); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void backup_cow_request_end(CowRequest *req) | ||||||
|  | { | ||||||
|  |     cow_request_end(req); | ||||||
|  | } | ||||||
|  |  | ||||||
| static void backup_drain(BlockJob *job) | static void backup_drain(BlockJob *job) | ||||||
| { | { | ||||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common); |     BackupBlockJob *s = container_of(job, BackupBlockJob, common); | ||||||
| @@ -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) | static bool coroutine_fn yield_and_check(BackupBlockJob *job) | ||||||
| { | { | ||||||
|     uint64_t delay_ns; |     if (block_job_is_cancelled(&job->common)) { | ||||||
|  |  | ||||||
|     if (job_is_cancelled(&job->common.job)) { |  | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* We need to yield even for delay_ns = 0 so that bdrv_drain_all() can |     /* we need to yield so that bdrv_drain_all() returns. | ||||||
|      * return. Without a yield, the VM would not reboot. */ |      * (without, VM does not reboot) | ||||||
|     delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read); |      */ | ||||||
|     job->bytes_read = 0; |     if (job->common.speed) { | ||||||
|     job_sleep_ns(&job->common.job, delay_ns); |         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; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -419,8 +405,7 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset, |         offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset); | ||||||
|                                              UINT64_MAX); |  | ||||||
|         if (offset == -1) { |         if (offset == -1) { | ||||||
|             hbitmap_set(job->copy_bitmap, cluster, end - cluster); |             hbitmap_set(job->copy_bitmap, cluster, end - cluster); | ||||||
|             break; |             break; | ||||||
| @@ -435,66 +420,64 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) | |||||||
|         bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size); |         bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* TODO job_progress_set_remaining() would make more sense */ |     job->common.offset = job->common.len - | ||||||
|     job_progress_update(&job->common.job, |                          hbitmap_count(job->copy_bitmap) * job->cluster_size; | ||||||
|         job->len - hbitmap_count(job->copy_bitmap) * job->cluster_size); |  | ||||||
|  |  | ||||||
|     bdrv_dirty_iter_free(dbi); |     bdrv_dirty_iter_free(dbi); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coroutine_fn backup_run(Job *job, Error **errp) | static void coroutine_fn backup_run(void *opaque) | ||||||
| { | { | ||||||
|     BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); |     BackupBlockJob *job = opaque; | ||||||
|     BlockDriverState *bs = blk_bs(s->common.blk); |     BackupCompleteData *data; | ||||||
|  |     BlockDriverState *bs = blk_bs(job->common.blk); | ||||||
|     int64_t offset, nb_clusters; |     int64_t offset, nb_clusters; | ||||||
|     int ret = 0; |     int ret = 0; | ||||||
|  |  | ||||||
|     QLIST_INIT(&s->inflight_reqs); |     QLIST_INIT(&job->inflight_reqs); | ||||||
|     qemu_co_rwlock_init(&s->flush_rwlock); |     qemu_co_rwlock_init(&job->flush_rwlock); | ||||||
|  |  | ||||||
|     nb_clusters = DIV_ROUND_UP(s->len, s->cluster_size); |     nb_clusters = DIV_ROUND_UP(job->common.len, job->cluster_size); | ||||||
|     job_progress_set_remaining(job, s->len); |     job->copy_bitmap = hbitmap_alloc(nb_clusters, 0); | ||||||
|  |     if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { | ||||||
|     s->copy_bitmap = hbitmap_alloc(nb_clusters, 0); |         backup_incremental_init_copy_bitmap(job); | ||||||
|     if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { |  | ||||||
|         backup_incremental_init_copy_bitmap(s); |  | ||||||
|     } else { |     } else { | ||||||
|         hbitmap_set(s->copy_bitmap, 0, nb_clusters); |         hbitmap_set(job->copy_bitmap, 0, nb_clusters); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     s->before_write.notify = backup_before_write_notify; |     job->before_write.notify = backup_before_write_notify; | ||||||
|     bdrv_add_before_write_notifier(bs, &s->before_write); |     bdrv_add_before_write_notifier(bs, &job->before_write); | ||||||
|  |  | ||||||
|     if (s->sync_mode == MIRROR_SYNC_MODE_NONE) { |     if (job->sync_mode == MIRROR_SYNC_MODE_NONE) { | ||||||
|         /* All bits are set in copy_bitmap to allow any cluster to be copied. |         /* All bits are set in copy_bitmap to allow any cluster to be copied. | ||||||
|          * This does not actually require them to be copied. */ |          * This does not actually require them to be copied. */ | ||||||
|         while (!job_is_cancelled(job)) { |         while (!block_job_is_cancelled(&job->common)) { | ||||||
|             /* Yield until the job is cancelled.  We just let our before_write |             /* Yield until the job is cancelled.  We just let our before_write | ||||||
|              * notify callback service CoW requests. */ |              * notify callback service CoW requests. */ | ||||||
|             job_yield(job); |             block_job_yield(&job->common); | ||||||
|         } |         } | ||||||
|     } else if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { |     } else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { | ||||||
|         ret = backup_run_incremental(s); |         ret = backup_run_incremental(job); | ||||||
|     } else { |     } else { | ||||||
|         /* Both FULL and TOP SYNC_MODE's require copying.. */ |         /* Both FULL and TOP SYNC_MODE's require copying.. */ | ||||||
|         for (offset = 0; offset < s->len; |         for (offset = 0; offset < job->common.len; | ||||||
|              offset += s->cluster_size) { |              offset += job->cluster_size) { | ||||||
|             bool error_is_read; |             bool error_is_read; | ||||||
|             int alloced = 0; |             int alloced = 0; | ||||||
|  |  | ||||||
|             if (yield_and_check(s)) { |             if (yield_and_check(job)) { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (s->sync_mode == MIRROR_SYNC_MODE_TOP) { |             if (job->sync_mode == MIRROR_SYNC_MODE_TOP) { | ||||||
|                 int i; |                 int i; | ||||||
|                 int64_t n; |                 int64_t n; | ||||||
|  |  | ||||||
|                 /* Check to see if these blocks are already in the |                 /* Check to see if these blocks are already in the | ||||||
|                  * backing file. */ |                  * backing file. */ | ||||||
|  |  | ||||||
|                 for (i = 0; i < s->cluster_size;) { |                 for (i = 0; i < job->cluster_size;) { | ||||||
|                     /* bdrv_is_allocated() only returns true/false based |                     /* bdrv_is_allocated() only returns true/false based | ||||||
|                      * on the first set of sectors it comes across that |                      * on the first set of sectors it comes across that | ||||||
|                      * are are all in the same state. |                      * are are all in the same state. | ||||||
| @@ -503,7 +486,7 @@ static int coroutine_fn backup_run(Job *job, Error **errp) | |||||||
|                      * needed but at some point that is always the case. */ |                      * needed but at some point that is always the case. */ | ||||||
|                     alloced = |                     alloced = | ||||||
|                         bdrv_is_allocated(bs, offset + i, |                         bdrv_is_allocated(bs, offset + i, | ||||||
|                                           s->cluster_size - i, &n); |                                           job->cluster_size - i, &n); | ||||||
|                     i += n; |                     i += n; | ||||||
|  |  | ||||||
|                     if (alloced || n == 0) { |                     if (alloced || n == 0) { | ||||||
| @@ -521,45 +504,43 @@ static int coroutine_fn backup_run(Job *job, Error **errp) | |||||||
|             if (alloced < 0) { |             if (alloced < 0) { | ||||||
|                 ret = alloced; |                 ret = alloced; | ||||||
|             } else { |             } else { | ||||||
|                 ret = backup_do_cow(s, offset, s->cluster_size, |                 ret = backup_do_cow(job, offset, job->cluster_size, | ||||||
|                                     &error_is_read, false); |                                     &error_is_read, false); | ||||||
|             } |             } | ||||||
|             if (ret < 0) { |             if (ret < 0) { | ||||||
|                 /* Depending on error action, fail now or retry cluster */ |                 /* Depending on error action, fail now or retry cluster */ | ||||||
|                 BlockErrorAction action = |                 BlockErrorAction action = | ||||||
|                     backup_error_action(s, error_is_read, -ret); |                     backup_error_action(job, error_is_read, -ret); | ||||||
|                 if (action == BLOCK_ERROR_ACTION_REPORT) { |                 if (action == BLOCK_ERROR_ACTION_REPORT) { | ||||||
|                     break; |                     break; | ||||||
|                 } else { |                 } else { | ||||||
|                     offset -= s->cluster_size; |                     offset -= job->cluster_size; | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     notifier_with_return_remove(&s->before_write); |     notifier_with_return_remove(&job->before_write); | ||||||
|  |  | ||||||
|     /* wait until pending backup_do_cow() calls have completed */ |     /* wait until pending backup_do_cow() calls have completed */ | ||||||
|     qemu_co_rwlock_wrlock(&s->flush_rwlock); |     qemu_co_rwlock_wrlock(&job->flush_rwlock); | ||||||
|     qemu_co_rwlock_unlock(&s->flush_rwlock); |     qemu_co_rwlock_unlock(&job->flush_rwlock); | ||||||
|     hbitmap_free(s->copy_bitmap); |     hbitmap_free(job->copy_bitmap); | ||||||
|  |  | ||||||
|     return ret; |     data = g_malloc(sizeof(*data)); | ||||||
|  |     data->ret = ret; | ||||||
|  |     block_job_defer_to_main_loop(&job->common, backup_complete, data); | ||||||
| } | } | ||||||
|  |  | ||||||
| static const BlockJobDriver backup_job_driver = { | static const BlockJobDriver backup_job_driver = { | ||||||
|     .job_driver = { |     .instance_size          = sizeof(BackupBlockJob), | ||||||
|         .instance_size          = sizeof(BackupBlockJob), |     .job_type               = BLOCK_JOB_TYPE_BACKUP, | ||||||
|         .job_type               = JOB_TYPE_BACKUP, |     .start                  = backup_run, | ||||||
|         .free                   = block_job_free, |     .set_speed              = backup_set_speed, | ||||||
|         .user_resume            = block_job_user_resume, |     .commit                 = backup_commit, | ||||||
|         .drain                  = block_job_drain, |     .abort                  = backup_abort, | ||||||
|         .run                    = backup_run, |     .clean                  = backup_clean, | ||||||
|         .commit                 = backup_commit, |  | ||||||
|         .abort                  = backup_abort, |  | ||||||
|         .clean                  = backup_clean, |  | ||||||
|     }, |  | ||||||
|     .attached_aio_context   = backup_attached_aio_context, |     .attached_aio_context   = backup_attached_aio_context, | ||||||
|     .drain                  = backup_drain, |     .drain                  = backup_drain, | ||||||
| }; | }; | ||||||
| @@ -572,7 +553,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | |||||||
|                   BlockdevOnError on_target_error, |                   BlockdevOnError on_target_error, | ||||||
|                   int creation_flags, |                   int creation_flags, | ||||||
|                   BlockCompletionFunc *cb, void *opaque, |                   BlockCompletionFunc *cb, void *opaque, | ||||||
|                   JobTxn *txn, Error **errp) |                   BlockJobTxn *txn, Error **errp) | ||||||
| { | { | ||||||
|     int64_t len; |     int64_t len; | ||||||
|     BlockDriverInfo bdi; |     BlockDriverInfo bdi; | ||||||
| @@ -639,8 +620,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | |||||||
|         goto error; |         goto error; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* job->len is fixed, so we can't allow resize */ |     /* job->common.len is fixed, so we can't allow resize */ | ||||||
|     job = block_job_create(job_id, &backup_job_driver, txn, bs, |     job = block_job_create(job_id, &backup_job_driver, bs, | ||||||
|                            BLK_PERM_CONSISTENT_READ, |                            BLK_PERM_CONSISTENT_READ, | ||||||
|                            BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | |                            BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | | ||||||
|                            BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD, |                            BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD, | ||||||
| @@ -665,9 +646,6 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | |||||||
|                        sync_bitmap : NULL; |                        sync_bitmap : NULL; | ||||||
|     job->compress = compress; |     job->compress = compress; | ||||||
|  |  | ||||||
|     /* Detect image-fleecing (and similar) schemes */ |  | ||||||
|     job->serialize_target_writes = bdrv_chain_contains(target, bs); |  | ||||||
|  |  | ||||||
|     /* If there is no backing file on the target, we cannot rely on COW if our |     /* If there is no backing file on the target, we cannot rely on COW if our | ||||||
|      * backup cluster size is smaller than the target cluster size. Even for |      * backup cluster size is smaller than the target cluster size. Even for | ||||||
|      * targets with a backing file, try to avoid COW if possible. */ |      * targets with a backing file, try to avoid COW if possible. */ | ||||||
| @@ -694,17 +672,12 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | |||||||
|     } else { |     } else { | ||||||
|         job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size); |         job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size); | ||||||
|     } |     } | ||||||
|     job->use_copy_range = true; |  | ||||||
|     job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk), |  | ||||||
|                                         blk_get_max_transfer(job->target)); |  | ||||||
|     job->copy_range_size = MAX(job->cluster_size, |  | ||||||
|                                QEMU_ALIGN_UP(job->copy_range_size, |  | ||||||
|                                              job->cluster_size)); |  | ||||||
|  |  | ||||||
|     /* Required permissions are already taken with target's blk_new() */ |     /* Required permissions are already taken with target's blk_new() */ | ||||||
|     block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, |     block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, | ||||||
|                        &error_abort); |                        &error_abort); | ||||||
|     job->len = len; |     job->common.len = len; | ||||||
|  |     block_job_txn_add_job(txn, &job->common); | ||||||
|  |  | ||||||
|     return &job->common; |     return &job->common; | ||||||
|  |  | ||||||
| @@ -713,8 +686,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | |||||||
|         bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL); |         bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL); | ||||||
|     } |     } | ||||||
|     if (job) { |     if (job) { | ||||||
|         backup_clean(&job->common.job); |         backup_clean(&job->common); | ||||||
|         job_early_fail(&job->common.job); |         block_job_early_fail(&job->common); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return NULL; |     return NULL; | ||||||
|   | |||||||
| @@ -305,7 +305,7 @@ static void blkdebug_parse_filename(const char *filename, QDict *options, | |||||||
|  |  | ||||||
|     if (c != filename) { |     if (c != filename) { | ||||||
|         QString *config_path; |         QString *config_path; | ||||||
|         config_path = qstring_from_substr(filename, 0, c - filename); |         config_path = qstring_from_substr(filename, 0, c - filename - 1); | ||||||
|         qdict_put(options, "config", config_path); |         qdict_put(options, "config", config_path); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -398,11 +398,10 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | |     bs->supported_write_flags = BDRV_REQ_FUA & | ||||||
|         (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); |         bs->file->bs->supported_write_flags; | ||||||
|     bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | |     bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & | ||||||
|         ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & |         bs->file->bs->supported_zero_flags; | ||||||
|             bs->file->bs->supported_zero_flags); |  | ||||||
|     ret = -EINVAL; |     ret = -EINVAL; | ||||||
|  |  | ||||||
|     /* Set alignment overrides */ |     /* Set alignment overrides */ | ||||||
| @@ -625,7 +624,7 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs, | |||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return bdrv_co_pdiscard(bs->file, offset, bytes); |     return bdrv_co_pdiscard(bs->file->bs, offset, bytes); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs, | static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs, | ||||||
| @@ -811,37 +810,52 @@ static int64_t blkdebug_getlength(BlockDriverState *bs) | |||||||
|     return bdrv_getlength(bs->file->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; |     BDRVBlkdebugState *s = bs->opaque; | ||||||
|  |     QDict *opts; | ||||||
|     const QDictEntry *e; |     const QDictEntry *e; | ||||||
|     int ret; |     bool force_json = false; | ||||||
|  |  | ||||||
|     if (!bs->file->bs->exact_filename[0]) { |     for (e = qdict_first(options); e; e = qdict_next(options, e)) { | ||||||
|         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 */ |  | ||||||
|         if (strcmp(qdict_entry_key(e), "config") && |         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), "x-image") && |  | ||||||
|             strcmp(qdict_entry_key(e), "driver")) |  | ||||||
|         { |         { | ||||||
|             return; |             force_json = true; | ||||||
|  |             break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename), |     if (force_json && !bs->file->bs->full_open_options) { | ||||||
|                    "blkdebug:%s:%s", |         /* The config file cannot be recreated, so creating a plain filename | ||||||
|                    s->config_file ?: "", bs->file->bs->exact_filename); |          * is impossible */ | ||||||
|     if (ret >= sizeof(bs->exact_filename)) { |         return; | ||||||
|         /* An overflow makes the filename unusable, so do not report any */ |  | ||||||
|         bs->exact_filename[0] = 0; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     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) | static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp) | ||||||
| @@ -874,20 +888,6 @@ static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state, | |||||||
|     return 0; |     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 = { | static BlockDriver bdrv_blkdebug = { | ||||||
|     .format_name            = "blkdebug", |     .format_name            = "blkdebug", | ||||||
|     .protocol_name          = "blkdebug", |     .protocol_name          = "blkdebug", | ||||||
| @@ -917,8 +917,6 @@ static BlockDriver bdrv_blkdebug = { | |||||||
|                                 = blkdebug_debug_remove_breakpoint, |                                 = blkdebug_debug_remove_breakpoint, | ||||||
|     .bdrv_debug_resume          = blkdebug_debug_resume, |     .bdrv_debug_resume          = blkdebug_debug_resume, | ||||||
|     .bdrv_debug_is_suspended    = blkdebug_debug_is_suspended, |     .bdrv_debug_is_suspended    = blkdebug_debug_is_suspended, | ||||||
|  |  | ||||||
|     .strong_runtime_opts        = blkdebug_strong_runtime_opts, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void bdrv_blkdebug_init(void) | 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; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; |  | ||||||
|     bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; |  | ||||||
|  |  | ||||||
|     ret = 0; |     ret = 0; | ||||||
| fail: | fail: | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void blkreplay_close(BlockDriverState *bs) | ||||||
|  | { | ||||||
|  | } | ||||||
|  |  | ||||||
| static int64_t blkreplay_getlength(BlockDriverState *bs) | static int64_t blkreplay_getlength(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     return bdrv_getlength(bs->file->bs); |     return bdrv_getlength(bs->file->bs); | ||||||
| @@ -109,7 +110,7 @@ static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs, | |||||||
|                                               int64_t offset, int bytes) |                                               int64_t offset, int bytes) | ||||||
| { | { | ||||||
|     uint64_t reqid = blkreplay_next_id(); |     uint64_t reqid = blkreplay_next_id(); | ||||||
|     int ret = bdrv_co_pdiscard(bs->file, offset, bytes); |     int ret = bdrv_co_pdiscard(bs->file->bs, offset, bytes); | ||||||
|     block_request_create(reqid, bs, qemu_coroutine_self()); |     block_request_create(reqid, bs, qemu_coroutine_self()); | ||||||
|     qemu_coroutine_yield(); |     qemu_coroutine_yield(); | ||||||
|  |  | ||||||
| @@ -131,6 +132,7 @@ static BlockDriver bdrv_blkreplay = { | |||||||
|     .instance_size          = 0, |     .instance_size          = 0, | ||||||
|  |  | ||||||
|     .bdrv_open              = blkreplay_open, |     .bdrv_open              = blkreplay_open, | ||||||
|  |     .bdrv_close             = blkreplay_close, | ||||||
|     .bdrv_child_perm        = bdrv_filter_default_perms, |     .bdrv_child_perm        = bdrv_filter_default_perms, | ||||||
|     .bdrv_getlength         = blkreplay_getlength, |     .bdrv_getlength         = blkreplay_getlength, | ||||||
|  |  | ||||||
|   | |||||||
| @@ -80,7 +80,7 @@ static void blkverify_parse_filename(const char *filename, QDict *options, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* TODO Implement option pass-through and set raw.filename here */ |     /* TODO Implement option pass-through and set raw.filename here */ | ||||||
|     raw_path = qstring_from_substr(filename, 0, c - filename); |     raw_path = qstring_from_substr(filename, 0, c - filename - 1); | ||||||
|     qdict_put(options, "x-raw", raw_path); |     qdict_put(options, "x-raw", raw_path); | ||||||
|  |  | ||||||
|     /* TODO Allow multi-level nesting and set file.filename here */ |     /* TODO Allow multi-level nesting and set file.filename here */ | ||||||
| @@ -141,9 +141,6 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; |  | ||||||
|     bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; |  | ||||||
|  |  | ||||||
|     ret = 0; |     ret = 0; | ||||||
| fail: | fail: | ||||||
|     qemu_opts_del(opts); |     qemu_opts_del(opts); | ||||||
| @@ -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); |     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; |     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] |     if (bs->file->bs->exact_filename[0] | ||||||
|         && s->test_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 = { | static BlockDriver bdrv_blkverify = { | ||||||
|     .format_name                      = "blkverify", |     .format_name                      = "blkverify", | ||||||
|     .protocol_name                    = "blkverify", |     .protocol_name                    = "blkverify", | ||||||
| @@ -319,7 +324,6 @@ static BlockDriver bdrv_blkverify = { | |||||||
|     .bdrv_child_perm                  = bdrv_filter_default_perms, |     .bdrv_child_perm                  = bdrv_filter_default_perms, | ||||||
|     .bdrv_getlength                   = blkverify_getlength, |     .bdrv_getlength                   = blkverify_getlength, | ||||||
|     .bdrv_refresh_filename            = blkverify_refresh_filename, |     .bdrv_refresh_filename            = blkverify_refresh_filename, | ||||||
|     .bdrv_dirname                     = blkverify_dirname, |  | ||||||
|  |  | ||||||
|     .bdrv_co_preadv                   = blkverify_co_preadv, |     .bdrv_co_preadv                   = blkverify_co_preadv, | ||||||
|     .bdrv_co_pwritev                  = blkverify_co_pwritev, |     .bdrv_co_pwritev                  = blkverify_co_pwritev, | ||||||
|   | |||||||
| @@ -31,13 +31,6 @@ | |||||||
|  |  | ||||||
| static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb); | static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb); | ||||||
|  |  | ||||||
| typedef struct BlockBackendAioNotifier { |  | ||||||
|     void (*attached_aio_context)(AioContext *new_context, void *opaque); |  | ||||||
|     void (*detach_aio_context)(void *opaque); |  | ||||||
|     void *opaque; |  | ||||||
|     QLIST_ENTRY(BlockBackendAioNotifier) list; |  | ||||||
| } BlockBackendAioNotifier; |  | ||||||
|  |  | ||||||
| struct BlockBackend { | struct BlockBackend { | ||||||
|     char *name; |     char *name; | ||||||
|     int refcnt; |     int refcnt; | ||||||
| @@ -47,7 +40,9 @@ struct BlockBackend { | |||||||
|     QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */ |     QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */ | ||||||
|     BlockBackendPublic public; |     BlockBackendPublic public; | ||||||
|  |  | ||||||
|     DeviceState *dev;           /* attached device model, if any */ |     void *dev;                  /* attached device model, if any */ | ||||||
|  |     bool legacy_dev;            /* true if dev is not a DeviceState */ | ||||||
|  |     /* TODO change to DeviceState when all users are qdevified */ | ||||||
|     const BlockDevOps *dev_ops; |     const BlockDevOps *dev_ops; | ||||||
|     void *dev_opaque; |     void *dev_opaque; | ||||||
|  |  | ||||||
| @@ -74,7 +69,6 @@ struct BlockBackend { | |||||||
|     bool allow_write_beyond_eof; |     bool allow_write_beyond_eof; | ||||||
|  |  | ||||||
|     NotifierList remove_bs_notifiers, insert_bs_notifiers; |     NotifierList remove_bs_notifiers, insert_bs_notifiers; | ||||||
|     QLIST_HEAD(, BlockBackendAioNotifier) aio_notifiers; |  | ||||||
|  |  | ||||||
|     int quiesce_counter; |     int quiesce_counter; | ||||||
|     VMChangeStateEntry *vmsh; |     VMChangeStateEntry *vmsh; | ||||||
| @@ -86,6 +80,7 @@ struct BlockBackend { | |||||||
|      * Accessed with atomic ops. |      * Accessed with atomic ops. | ||||||
|      */ |      */ | ||||||
|     unsigned int in_flight; |     unsigned int in_flight; | ||||||
|  |     AioWait wait; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef struct BlockBackendAIOCB { | typedef struct BlockBackendAIOCB { | ||||||
| @@ -118,7 +113,6 @@ static void blk_root_inherit_options(int *child_flags, QDict *child_options, | |||||||
|     abort(); |     abort(); | ||||||
| } | } | ||||||
| static void blk_root_drained_begin(BdrvChild *child); | static void blk_root_drained_begin(BdrvChild *child); | ||||||
| static bool blk_root_drained_poll(BdrvChild *child); |  | ||||||
| static void blk_root_drained_end(BdrvChild *child); | static void blk_root_drained_end(BdrvChild *child); | ||||||
|  |  | ||||||
| static void blk_root_change_media(BdrvChild *child, bool load); | static void blk_root_change_media(BdrvChild *child, bool load); | ||||||
| @@ -253,36 +247,6 @@ static int blk_root_inactivate(BdrvChild *child) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void blk_root_attach(BdrvChild *child) |  | ||||||
| { |  | ||||||
|     BlockBackend *blk = child->opaque; |  | ||||||
|     BlockBackendAioNotifier *notifier; |  | ||||||
|  |  | ||||||
|     trace_blk_root_attach(child, blk, child->bs); |  | ||||||
|  |  | ||||||
|     QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { |  | ||||||
|         bdrv_add_aio_context_notifier(child->bs, |  | ||||||
|                 notifier->attached_aio_context, |  | ||||||
|                 notifier->detach_aio_context, |  | ||||||
|                 notifier->opaque); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void blk_root_detach(BdrvChild *child) |  | ||||||
| { |  | ||||||
|     BlockBackend *blk = child->opaque; |  | ||||||
|     BlockBackendAioNotifier *notifier; |  | ||||||
|  |  | ||||||
|     trace_blk_root_detach(child, blk, child->bs); |  | ||||||
|  |  | ||||||
|     QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { |  | ||||||
|         bdrv_remove_aio_context_notifier(child->bs, |  | ||||||
|                 notifier->attached_aio_context, |  | ||||||
|                 notifier->detach_aio_context, |  | ||||||
|                 notifier->opaque); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static const BdrvChildRole child_root = { | static const BdrvChildRole child_root = { | ||||||
|     .inherit_options    = blk_root_inherit_options, |     .inherit_options    = blk_root_inherit_options, | ||||||
|  |  | ||||||
| @@ -292,14 +256,10 @@ static const BdrvChildRole child_root = { | |||||||
|     .get_parent_desc    = blk_root_get_parent_desc, |     .get_parent_desc    = blk_root_get_parent_desc, | ||||||
|  |  | ||||||
|     .drained_begin      = blk_root_drained_begin, |     .drained_begin      = blk_root_drained_begin, | ||||||
|     .drained_poll       = blk_root_drained_poll, |  | ||||||
|     .drained_end        = blk_root_drained_end, |     .drained_end        = blk_root_drained_end, | ||||||
|  |  | ||||||
|     .activate           = blk_root_activate, |     .activate           = blk_root_activate, | ||||||
|     .inactivate         = blk_root_inactivate, |     .inactivate         = blk_root_inactivate, | ||||||
|  |  | ||||||
|     .attach             = blk_root_attach, |  | ||||||
|     .detach             = blk_root_detach, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* | /* | ||||||
| @@ -323,14 +283,10 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm) | |||||||
|     blk->shared_perm = shared_perm; |     blk->shared_perm = shared_perm; | ||||||
|     blk_set_enable_write_cache(blk, true); |     blk_set_enable_write_cache(blk, true); | ||||||
|  |  | ||||||
|     blk->on_read_error = BLOCKDEV_ON_ERROR_REPORT; |  | ||||||
|     blk->on_write_error = BLOCKDEV_ON_ERROR_ENOSPC; |  | ||||||
|  |  | ||||||
|     block_acct_init(&blk->stats); |     block_acct_init(&blk->stats); | ||||||
|  |  | ||||||
|     notifier_list_init(&blk->remove_bs_notifiers); |     notifier_list_init(&blk->remove_bs_notifiers); | ||||||
|     notifier_list_init(&blk->insert_bs_notifiers); |     notifier_list_init(&blk->insert_bs_notifiers); | ||||||
|     QLIST_INIT(&blk->aio_notifiers); |  | ||||||
|  |  | ||||||
|     QTAILQ_INSERT_TAIL(&block_backends, blk, link); |     QTAILQ_INSERT_TAIL(&block_backends, blk, link); | ||||||
|     return blk; |     return blk; | ||||||
| @@ -408,7 +364,6 @@ static void blk_delete(BlockBackend *blk) | |||||||
|     } |     } | ||||||
|     assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers)); |     assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers)); | ||||||
|     assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers)); |     assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers)); | ||||||
|     assert(QLIST_EMPTY(&blk->aio_notifiers)); |  | ||||||
|     QTAILQ_REMOVE(&block_backends, blk, link); |     QTAILQ_REMOVE(&block_backends, blk, link); | ||||||
|     drive_info_del(blk->legacy_dinfo); |     drive_info_del(blk->legacy_dinfo); | ||||||
|     block_acct_cleanup(&blk->stats); |     block_acct_cleanup(&blk->stats); | ||||||
| @@ -421,6 +376,7 @@ static void drive_info_del(DriveInfo *dinfo) | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     qemu_opts_del(dinfo->opts); |     qemu_opts_del(dinfo->opts); | ||||||
|  |     g_free(dinfo->serial); | ||||||
|     g_free(dinfo); |     g_free(dinfo); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -435,7 +391,6 @@ int blk_get_refcnt(BlockBackend *blk) | |||||||
|  */ |  */ | ||||||
| void blk_ref(BlockBackend *blk) | void blk_ref(BlockBackend *blk) | ||||||
| { | { | ||||||
|     assert(blk->refcnt > 0); |  | ||||||
|     blk->refcnt++; |     blk->refcnt++; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -448,13 +403,7 @@ void blk_unref(BlockBackend *blk) | |||||||
| { | { | ||||||
|     if (blk) { |     if (blk) { | ||||||
|         assert(blk->refcnt > 0); |         assert(blk->refcnt > 0); | ||||||
|         if (blk->refcnt > 1) { |         if (!--blk->refcnt) { | ||||||
|             blk->refcnt--; |  | ||||||
|         } else { |  | ||||||
|             blk_drain(blk); |  | ||||||
|             /* blk_drain() cannot resurrect blk, nobody held a reference */ |  | ||||||
|             assert(blk->refcnt == 1); |  | ||||||
|             blk->refcnt = 0; |  | ||||||
|             blk_delete(blk); |             blk_delete(blk); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -776,11 +725,6 @@ void blk_remove_bs(BlockBackend *blk) | |||||||
|  |  | ||||||
|     blk_update_root_state(blk); |     blk_update_root_state(blk); | ||||||
|  |  | ||||||
|     /* bdrv_root_unref_child() will cause blk->root to become stale and may |  | ||||||
|      * switch to a completion coroutine later on. Let's drain all I/O here |  | ||||||
|      * to avoid that and a potential QEMU crash. |  | ||||||
|      */ |  | ||||||
|     blk_drain(blk); |  | ||||||
|     bdrv_root_unref_child(blk->root); |     bdrv_root_unref_child(blk->root); | ||||||
|     blk->root = NULL; |     blk->root = NULL; | ||||||
| } | } | ||||||
| @@ -834,11 +778,7 @@ void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm) | |||||||
|     *shared_perm = blk->shared_perm; |     *shared_perm = blk->shared_perm; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | static int blk_do_attach_dev(BlockBackend *blk, void *dev) | ||||||
|  * Attach device model @dev to @blk. |  | ||||||
|  * Return 0 on success, -EBUSY when a device model is attached already. |  | ||||||
|  */ |  | ||||||
| int blk_attach_dev(BlockBackend *blk, DeviceState *dev) |  | ||||||
| { | { | ||||||
|     if (blk->dev) { |     if (blk->dev) { | ||||||
|         return -EBUSY; |         return -EBUSY; | ||||||
| @@ -853,16 +793,40 @@ int blk_attach_dev(BlockBackend *blk, DeviceState *dev) | |||||||
|  |  | ||||||
|     blk_ref(blk); |     blk_ref(blk); | ||||||
|     blk->dev = dev; |     blk->dev = dev; | ||||||
|  |     blk->legacy_dev = false; | ||||||
|     blk_iostatus_reset(blk); |     blk_iostatus_reset(blk); | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Attach device model @dev to @blk. | ||||||
|  |  * Return 0 on success, -EBUSY when a device model is attached already. | ||||||
|  |  */ | ||||||
|  | int blk_attach_dev(BlockBackend *blk, DeviceState *dev) | ||||||
|  | { | ||||||
|  |     return blk_do_attach_dev(blk, dev); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Attach device model @dev to @blk. | ||||||
|  |  * @blk must not have a device model attached already. | ||||||
|  |  * TODO qdevified devices don't use this, remove when devices are qdevified | ||||||
|  |  */ | ||||||
|  | void blk_attach_dev_legacy(BlockBackend *blk, void *dev) | ||||||
|  | { | ||||||
|  |     if (blk_do_attach_dev(blk, dev) < 0) { | ||||||
|  |         abort(); | ||||||
|  |     } | ||||||
|  |     blk->legacy_dev = true; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Detach device model @dev from @blk. |  * Detach device model @dev from @blk. | ||||||
|  * @dev must be currently attached to @blk. |  * @dev must be currently attached to @blk. | ||||||
|  */ |  */ | ||||||
| void blk_detach_dev(BlockBackend *blk, DeviceState *dev) | void blk_detach_dev(BlockBackend *blk, void *dev) | ||||||
|  | /* TODO change to DeviceState *dev when all users are qdevified */ | ||||||
| { | { | ||||||
|     assert(blk->dev == dev); |     assert(blk->dev == dev); | ||||||
|     blk->dev = NULL; |     blk->dev = NULL; | ||||||
| @@ -876,7 +840,8 @@ void blk_detach_dev(BlockBackend *blk, DeviceState *dev) | |||||||
| /* | /* | ||||||
|  * Return the device model attached to @blk if any, else null. |  * Return the device model attached to @blk if any, else null. | ||||||
|  */ |  */ | ||||||
| DeviceState *blk_get_attached_dev(BlockBackend *blk) | void *blk_get_attached_dev(BlockBackend *blk) | ||||||
|  | /* TODO change to return DeviceState * when all users are qdevified */ | ||||||
| { | { | ||||||
|     return blk->dev; |     return blk->dev; | ||||||
| } | } | ||||||
| @@ -885,15 +850,17 @@ DeviceState *blk_get_attached_dev(BlockBackend *blk) | |||||||
|  * device attached to the BlockBackend. */ |  * device attached to the BlockBackend. */ | ||||||
| char *blk_get_attached_dev_id(BlockBackend *blk) | char *blk_get_attached_dev_id(BlockBackend *blk) | ||||||
| { | { | ||||||
|     DeviceState *dev = blk->dev; |     DeviceState *dev; | ||||||
|  |  | ||||||
|  |     assert(!blk->legacy_dev); | ||||||
|  |     dev = blk->dev; | ||||||
|  |  | ||||||
|     if (!dev) { |     if (!dev) { | ||||||
|         return g_strdup(""); |         return g_strdup(""); | ||||||
|     } else if (dev->id) { |     } else if (dev->id) { | ||||||
|         return g_strdup(dev->id); |         return g_strdup(dev->id); | ||||||
|     } |     } | ||||||
|  |     return object_get_canonical_path(OBJECT(dev)); | ||||||
|     return object_get_canonical_path(OBJECT(dev)) ?: g_strdup(""); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
| @@ -923,6 +890,11 @@ BlockBackend *blk_by_dev(void *dev) | |||||||
| void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, | void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, | ||||||
|                      void *opaque) |                      void *opaque) | ||||||
| { | { | ||||||
|  |     /* All drivers that use blk_set_dev_ops() are qdevified and we want to keep | ||||||
|  |      * it that way, so we can assume blk->dev, if present, is a DeviceState if | ||||||
|  |      * blk->dev_ops is set. Non-device users may use dev_ops without device. */ | ||||||
|  |     assert(!blk->legacy_dev); | ||||||
|  |  | ||||||
|     blk->dev_ops = ops; |     blk->dev_ops = ops; | ||||||
|     blk->dev_opaque = opaque; |     blk->dev_opaque = opaque; | ||||||
|  |  | ||||||
| @@ -948,6 +920,8 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp) | |||||||
|         bool tray_was_open, tray_is_open; |         bool tray_was_open, tray_is_open; | ||||||
|         Error *local_err = NULL; |         Error *local_err = NULL; | ||||||
|  |  | ||||||
|  |         assert(!blk->legacy_dev); | ||||||
|  |  | ||||||
|         tray_was_open = blk_dev_is_tray_open(blk); |         tray_was_open = blk_dev_is_tray_open(blk); | ||||||
|         blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err); |         blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err); | ||||||
|         if (local_err) { |         if (local_err) { | ||||||
| @@ -959,7 +933,8 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp) | |||||||
|  |  | ||||||
|         if (tray_was_open != tray_is_open) { |         if (tray_was_open != tray_is_open) { | ||||||
|             char *id = blk_get_attached_dev_id(blk); |             char *id = blk_get_attached_dev_id(blk); | ||||||
|             qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open); |             qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open, | ||||||
|  |                                               &error_abort); | ||||||
|             g_free(id); |             g_free(id); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -1187,7 +1162,6 @@ static void blk_read_entry(void *opaque) | |||||||
|  |  | ||||||
|     rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size, |     rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size, | ||||||
|                               qiov, rwco->flags); |                               qiov, rwco->flags); | ||||||
|     aio_wait_kick(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void blk_write_entry(void *opaque) | static void blk_write_entry(void *opaque) | ||||||
| @@ -1197,15 +1171,23 @@ static void blk_write_entry(void *opaque) | |||||||
|  |  | ||||||
|     rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size, |     rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size, | ||||||
|                                qiov, rwco->flags); |                                qiov, rwco->flags); | ||||||
|     aio_wait_kick(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, | static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, | ||||||
|                    int64_t bytes, CoroutineEntry co_entry, |                    int64_t bytes, CoroutineEntry co_entry, | ||||||
|                    BdrvRequestFlags flags) |                    BdrvRequestFlags flags) | ||||||
| { | { | ||||||
|     QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); |     QEMUIOVector qiov; | ||||||
|     BlkRwCo rwco = { |     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, |         .blk    = blk, | ||||||
|         .offset = offset, |         .offset = offset, | ||||||
|         .iobuf  = &qiov, |         .iobuf  = &qiov, | ||||||
| @@ -1253,15 +1235,15 @@ int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags) | |||||||
|     return bdrv_make_zero(blk->root, 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); |     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); |     atomic_dec(&blk->in_flight); | ||||||
|     aio_wait_kick(); |     aio_wait_kick(&blk->wait); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void error_callback_bh(void *opaque) | static void error_callback_bh(void *opaque) | ||||||
| @@ -1302,8 +1284,8 @@ static const AIOCBInfo blk_aio_em_aiocb_info = { | |||||||
| static void blk_aio_complete(BlkAioEmAIOCB *acb) | static void blk_aio_complete(BlkAioEmAIOCB *acb) | ||||||
| { | { | ||||||
|     if (acb->has_returned) { |     if (acb->has_returned) { | ||||||
|         acb->common.cb(acb->common.opaque, acb->rwco.ret); |  | ||||||
|         blk_dec_in_flight(acb->rwco.blk); |         blk_dec_in_flight(acb->rwco.blk); | ||||||
|  |         acb->common.cb(acb->common.opaque, acb->rwco.ret); | ||||||
|         qemu_aio_unref(acb); |         qemu_aio_unref(acb); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -1500,7 +1482,6 @@ static void blk_ioctl_entry(void *opaque) | |||||||
|  |  | ||||||
|     rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, |     rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, | ||||||
|                              qiov->iov[0].iov_base); |                              qiov->iov[0].iov_base); | ||||||
|     aio_wait_kick(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) | int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) | ||||||
| @@ -1531,7 +1512,7 @@ int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes) | |||||||
|         return ret; |         return ret; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return bdrv_co_pdiscard(blk->root, offset, bytes); |     return bdrv_co_pdiscard(blk_bs(blk), offset, bytes); | ||||||
| } | } | ||||||
|  |  | ||||||
| int blk_co_flush(BlockBackend *blk) | int blk_co_flush(BlockBackend *blk) | ||||||
| @@ -1547,7 +1528,6 @@ static void blk_flush_entry(void *opaque) | |||||||
| { | { | ||||||
|     BlkRwCo *rwco = opaque; |     BlkRwCo *rwco = opaque; | ||||||
|     rwco->ret = blk_co_flush(rwco->blk); |     rwco->ret = blk_co_flush(rwco->blk); | ||||||
|     aio_wait_kick(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| int blk_flush(BlockBackend *blk) | int blk_flush(BlockBackend *blk) | ||||||
| @@ -1564,8 +1544,9 @@ void blk_drain(BlockBackend *blk) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* We may have -ENOMEDIUM completions in flight */ |     /* We may have -ENOMEDIUM completions in flight */ | ||||||
|     AIO_WAIT_WHILE(blk_get_aio_context(blk), |     AIO_WAIT_WHILE(&blk->wait, | ||||||
|                    atomic_mb_read(&blk->in_flight) > 0); |             blk_get_aio_context(blk), | ||||||
|  |             atomic_mb_read(&blk->in_flight) > 0); | ||||||
|  |  | ||||||
|     if (bs) { |     if (bs) { | ||||||
|         bdrv_drained_end(bs); |         bdrv_drained_end(bs); | ||||||
| @@ -1584,7 +1565,8 @@ void blk_drain_all(void) | |||||||
|         aio_context_acquire(ctx); |         aio_context_acquire(ctx); | ||||||
|  |  | ||||||
|         /* We may have -ENOMEDIUM completions in flight */ |         /* We may have -ENOMEDIUM completions in flight */ | ||||||
|         AIO_WAIT_WHILE(ctx, atomic_mb_read(&blk->in_flight) > 0); |         AIO_WAIT_WHILE(&blk->wait, ctx, | ||||||
|  |                 atomic_mb_read(&blk->in_flight) > 0); | ||||||
|  |  | ||||||
|         aio_context_release(ctx); |         aio_context_release(ctx); | ||||||
|     } |     } | ||||||
| @@ -1636,7 +1618,8 @@ static void send_qmp_error_event(BlockBackend *blk, | |||||||
|     qapi_event_send_block_io_error(blk_name(blk), !!bs, |     qapi_event_send_block_io_error(blk_name(blk), !!bs, | ||||||
|                                    bs ? bdrv_get_node_name(bs) : NULL, optype, |                                    bs ? bdrv_get_node_name(bs) : NULL, optype, | ||||||
|                                    action, blk_iostatus_is_enabled(blk), |                                    action, blk_iostatus_is_enabled(blk), | ||||||
|                                    error == ENOSPC, strerror(error)); |                                    error == ENOSPC, strerror(error), | ||||||
|  |                                    &error_abort); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* This is done by device models because, while the block layer knows | /* This is done by device models because, while the block layer knows | ||||||
| @@ -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); |     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); |     BlockDriverState *bs = blk_bs(blk); | ||||||
|  |  | ||||||
|     if (!bs) { |     if (!bs) { | ||||||
|         return false; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return bdrv_is_sg(bs); |     return bdrv_is_sg(bs); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool blk_enable_write_cache(BlockBackend *blk) | int blk_enable_write_cache(BlockBackend *blk) | ||||||
| { | { | ||||||
|     return blk->enable_write_cache; |     return blk->enable_write_cache; | ||||||
| } | } | ||||||
| @@ -1741,6 +1724,9 @@ void blk_eject(BlockBackend *blk, bool eject_flag) | |||||||
|     BlockDriverState *bs = blk_bs(blk); |     BlockDriverState *bs = blk_bs(blk); | ||||||
|     char *id; |     char *id; | ||||||
|  |  | ||||||
|  |     /* blk_eject is only called by qdevified devices */ | ||||||
|  |     assert(!blk->legacy_dev); | ||||||
|  |  | ||||||
|     if (bs) { |     if (bs) { | ||||||
|         bdrv_eject(bs, eject_flag); |         bdrv_eject(bs, eject_flag); | ||||||
|     } |     } | ||||||
| @@ -1749,7 +1735,7 @@ void blk_eject(BlockBackend *blk, bool eject_flag) | |||||||
|      * the frontend experienced a tray event. */ |      * the frontend experienced a tray event. */ | ||||||
|     id = blk_get_attached_dev_id(blk); |     id = blk_get_attached_dev_id(blk); | ||||||
|     qapi_event_send_device_tray_moved(blk_name(blk), id, |     qapi_event_send_device_tray_moved(blk_name(blk), id, | ||||||
|                                       eject_flag); |                                       eject_flag, &error_abort); | ||||||
|     g_free(id); |     g_free(id); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1836,7 +1822,13 @@ void blk_op_unblock_all(BlockBackend *blk, Error *reason) | |||||||
|  |  | ||||||
| AioContext *blk_get_aio_context(BlockBackend *blk) | AioContext *blk_get_aio_context(BlockBackend *blk) | ||||||
| { | { | ||||||
|     return bdrv_get_aio_context(blk_bs(blk)); |     BlockDriverState *bs = blk_bs(blk); | ||||||
|  |  | ||||||
|  |     if (bs) { | ||||||
|  |         return bdrv_get_aio_context(bs); | ||||||
|  |     } else { | ||||||
|  |         return qemu_get_aio_context(); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb) | static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb) | ||||||
| @@ -1865,15 +1857,8 @@ void blk_add_aio_context_notifier(BlockBackend *blk, | |||||||
|         void (*attached_aio_context)(AioContext *new_context, void *opaque), |         void (*attached_aio_context)(AioContext *new_context, void *opaque), | ||||||
|         void (*detach_aio_context)(void *opaque), void *opaque) |         void (*detach_aio_context)(void *opaque), void *opaque) | ||||||
| { | { | ||||||
|     BlockBackendAioNotifier *notifier; |  | ||||||
|     BlockDriverState *bs = blk_bs(blk); |     BlockDriverState *bs = blk_bs(blk); | ||||||
|  |  | ||||||
|     notifier = g_new(BlockBackendAioNotifier, 1); |  | ||||||
|     notifier->attached_aio_context = attached_aio_context; |  | ||||||
|     notifier->detach_aio_context = detach_aio_context; |  | ||||||
|     notifier->opaque = opaque; |  | ||||||
|     QLIST_INSERT_HEAD(&blk->aio_notifiers, notifier, list); |  | ||||||
|  |  | ||||||
|     if (bs) { |     if (bs) { | ||||||
|         bdrv_add_aio_context_notifier(bs, attached_aio_context, |         bdrv_add_aio_context_notifier(bs, attached_aio_context, | ||||||
|                                       detach_aio_context, opaque); |                                       detach_aio_context, opaque); | ||||||
| @@ -1886,25 +1871,12 @@ void blk_remove_aio_context_notifier(BlockBackend *blk, | |||||||
|                                      void (*detach_aio_context)(void *), |                                      void (*detach_aio_context)(void *), | ||||||
|                                      void *opaque) |                                      void *opaque) | ||||||
| { | { | ||||||
|     BlockBackendAioNotifier *notifier; |  | ||||||
|     BlockDriverState *bs = blk_bs(blk); |     BlockDriverState *bs = blk_bs(blk); | ||||||
|  |  | ||||||
|     if (bs) { |     if (bs) { | ||||||
|         bdrv_remove_aio_context_notifier(bs, attached_aio_context, |         bdrv_remove_aio_context_notifier(bs, attached_aio_context, | ||||||
|                                          detach_aio_context, opaque); |                                          detach_aio_context, opaque); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { |  | ||||||
|         if (notifier->attached_aio_context == attached_aio_context && |  | ||||||
|             notifier->detach_aio_context == detach_aio_context && |  | ||||||
|             notifier->opaque == opaque) { |  | ||||||
|             QLIST_REMOVE(notifier, list); |  | ||||||
|             g_free(notifier); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     abort(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify) | void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify) | ||||||
| @@ -1977,7 +1949,6 @@ static void blk_pdiscard_entry(void *opaque) | |||||||
|     QEMUIOVector *qiov = rwco->iobuf; |     QEMUIOVector *qiov = rwco->iobuf; | ||||||
|  |  | ||||||
|     rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size); |     rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size); | ||||||
|     aio_wait_kick(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes) | int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes) | ||||||
| @@ -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) | static void blk_root_drained_end(BdrvChild *child) | ||||||
| { | { | ||||||
|     BlockBackend *blk = child->opaque; |     BlockBackend *blk = child->opaque; | ||||||
| @@ -2190,27 +2154,3 @@ void blk_unregister_buf(BlockBackend *blk, void *host) | |||||||
| { | { | ||||||
|     bdrv_unregister_buf(blk_bs(blk), host); |     bdrv_unregister_buf(blk_bs(blk), host); | ||||||
| } | } | ||||||
|  |  | ||||||
| int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, |  | ||||||
|                                    BlockBackend *blk_out, int64_t off_out, |  | ||||||
|                                    int bytes, BdrvRequestFlags read_flags, |  | ||||||
|                                    BdrvRequestFlags write_flags) |  | ||||||
| { |  | ||||||
|     int r; |  | ||||||
|     r = blk_check_byte_request(blk_in, off_in, bytes); |  | ||||||
|     if (r) { |  | ||||||
|         return r; |  | ||||||
|     } |  | ||||||
|     r = blk_check_byte_request(blk_out, off_out, bytes); |  | ||||||
|     if (r) { |  | ||||||
|         return r; |  | ||||||
|     } |  | ||||||
|     return bdrv_co_copy_range(blk_in->root, off_in, |  | ||||||
|                               blk_out->root, off_out, |  | ||||||
|                               bytes, read_flags, write_flags); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const BdrvChild *blk_root(BlockBackend *blk) |  | ||||||
| { |  | ||||||
|     return blk->root; |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -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; |     const struct bochs_header *bochs = (const void *)buf; | ||||||
|  |  | ||||||
|     if (buf_size < HEADER_SIZE) |     if (buf_size < HEADER_SIZE) | ||||||
|         return 0; | 	return 0; | ||||||
|  |  | ||||||
|     if (!strcmp(bochs->magic, HEADER_MAGIC) && |     if (!strcmp(bochs->magic, HEADER_MAGIC) && | ||||||
|         !strcmp(bochs->type, REDOLOG_TYPE) && | 	!strcmp(bochs->type, REDOLOG_TYPE) && | ||||||
|         !strcmp(bochs->subtype, GROWING_TYPE) && | 	!strcmp(bochs->subtype, GROWING_TYPE) && | ||||||
|         ((le32_to_cpu(bochs->version) == HEADER_VERSION) || | 	((le32_to_cpu(bochs->version) == HEADER_VERSION) || | ||||||
|         (le32_to_cpu(bochs->version) == HEADER_V1))) | 	(le32_to_cpu(bochs->version) == HEADER_V1))) | ||||||
|         return 100; | 	return 100; | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| @@ -105,18 +105,23 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     struct bochs_header bochs; |     struct bochs_header bochs; | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     /* No write support yet */ |  | ||||||
|     ret = bdrv_apply_auto_read_only(bs, NULL, errp); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, |     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||||
|                                false, errp); |                                false, errp); | ||||||
|     if (!bs->file) { |     if (!bs->file) { | ||||||
|         return -EINVAL; |         return -EINVAL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (!bdrv_is_read_only(bs)) { | ||||||
|  |         error_report("Opening bochs images without an explicit read-only=on " | ||||||
|  |                      "option is deprecated. Future versions will refuse to " | ||||||
|  |                      "open the image instead of automatically marking the " | ||||||
|  |                      "image read-only."); | ||||||
|  |         ret = bdrv_set_read_only(bs, true, errp); /* no write support yet */ | ||||||
|  |         if (ret < 0) { | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); |     ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         return ret; |         return ret; | ||||||
| @@ -125,8 +130,8 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     if (strcmp(bochs.magic, HEADER_MAGIC) || |     if (strcmp(bochs.magic, HEADER_MAGIC) || | ||||||
|         strcmp(bochs.type, REDOLOG_TYPE) || |         strcmp(bochs.type, REDOLOG_TYPE) || | ||||||
|         strcmp(bochs.subtype, GROWING_TYPE) || |         strcmp(bochs.subtype, GROWING_TYPE) || | ||||||
|         ((le32_to_cpu(bochs.version) != HEADER_VERSION) && | 	((le32_to_cpu(bochs.version) != HEADER_VERSION) && | ||||||
|         (le32_to_cpu(bochs.version) != HEADER_V1))) { | 	(le32_to_cpu(bochs.version) != HEADER_V1))) { | ||||||
|         error_setg(errp, "Image not in Bochs format"); |         error_setg(errp, "Image not in Bochs format"); | ||||||
|         return -EINVAL; |         return -EINVAL; | ||||||
|     } |     } | ||||||
| @@ -158,7 +163,7 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     for (i = 0; i < s->catalog_size; i++) |     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); |     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; |     extent_offset = (offset % s->extent_size) / 512; | ||||||
|  |  | ||||||
|     if (s->catalog_bitmap[extent_index] == 0xffffffff) { |     if (s->catalog_bitmap[extent_index] == 0xffffffff) { | ||||||
|         return 0; /* not allocated */ | 	return 0; /* not allocated */ | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bitmap_offset = s->data_offset + |     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)) { |     if (!((bitmap_entry >> (extent_offset % 8)) & 1)) { | ||||||
|         return 0; /* not allocated */ | 	return 0; /* not allocated */ | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return bitmap_offset + (512 * (s->bitmap_blocks + extent_offset)); |     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; |     uint32_t offsets_size, max_compressed_block_size = 1, i; | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     ret = bdrv_apply_auto_read_only(bs, NULL, errp); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, |     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||||
|                                false, errp); |                                false, errp); | ||||||
|     if (!bs->file) { |     if (!bs->file) { | ||||||
|         return -EINVAL; |         return -EINVAL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (!bdrv_is_read_only(bs)) { | ||||||
|  |         error_report("Opening cloop images without an explicit read-only=on " | ||||||
|  |                      "option is deprecated. Future versions will refuse to " | ||||||
|  |                      "open the image instead of automatically marking the " | ||||||
|  |                      "image read-only."); | ||||||
|  |         ret = bdrv_set_read_only(bs, true, errp); | ||||||
|  |         if (ret < 0) { | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /* read header */ |     /* read header */ | ||||||
|     ret = bdrv_pread(bs->file, 128, &s->block_size, 4); |     ret = bdrv_pread(bs->file, 128, &s->block_size, 4); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|   | |||||||
							
								
								
									
										225
									
								
								block/commit.c
									
									
									
									
									
								
							
							
						
						
									
										225
									
								
								block/commit.c
									
									
									
									
									
								
							| @@ -31,15 +31,16 @@ enum { | |||||||
|     COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */ |     COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */ | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | #define SLICE_TIME 100000000ULL /* ns */ | ||||||
|  |  | ||||||
| typedef struct CommitBlockJob { | typedef struct CommitBlockJob { | ||||||
|     BlockJob common; |     BlockJob common; | ||||||
|  |     RateLimit limit; | ||||||
|     BlockDriverState *commit_top_bs; |     BlockDriverState *commit_top_bs; | ||||||
|     BlockBackend *top; |     BlockBackend *top; | ||||||
|     BlockBackend *base; |     BlockBackend *base; | ||||||
|     BlockDriverState *base_bs; |  | ||||||
|     BlockdevOnError on_error; |     BlockdevOnError on_error; | ||||||
|     bool base_read_only; |     int base_flags; | ||||||
|     bool chain_frozen; |  | ||||||
|     char *backing_file_str; |     char *backing_file_str; | ||||||
| } CommitBlockJob; | } CommitBlockJob; | ||||||
|  |  | ||||||
| @@ -48,9 +49,14 @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base, | |||||||
|                                         void *buf) |                                         void *buf) | ||||||
| { | { | ||||||
|     int ret = 0; |     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); |     assert(bytes < SIZE_MAX); | ||||||
|  |     qemu_iovec_init_external(&qiov, &iov, 1); | ||||||
|  |  | ||||||
|     ret = blk_co_preadv(bs, offset, qiov.size, &qiov, 0); |     ret = blk_co_preadv(bs, offset, qiov.size, &qiov, 0); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
| @@ -65,100 +71,96 @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base, | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int commit_prepare(Job *job) | typedef struct { | ||||||
| { |     int ret; | ||||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); | } CommitCompleteData; | ||||||
|  |  | ||||||
|     bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs); | static void commit_complete(BlockJob *job, void *opaque) | ||||||
|     s->chain_frozen = false; | { | ||||||
|  |     CommitBlockJob *s = container_of(job, CommitBlockJob, common); | ||||||
|  |     CommitCompleteData *data = opaque; | ||||||
|  |     BlockDriverState *top = blk_bs(s->top); | ||||||
|  |     BlockDriverState *base = blk_bs(s->base); | ||||||
|  |     BlockDriverState *commit_top_bs = s->commit_top_bs; | ||||||
|  |     int ret = data->ret; | ||||||
|  |     bool remove_commit_top_bs = false; | ||||||
|  |  | ||||||
|  |     /* Make sure commit_top_bs and top stay around until bdrv_replace_node() */ | ||||||
|  |     bdrv_ref(top); | ||||||
|  |     bdrv_ref(commit_top_bs); | ||||||
|  |  | ||||||
|     /* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before |     /* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before | ||||||
|      * the normal backing chain can be restored. */ |      * the normal backing chain can be restored. */ | ||||||
|     blk_unref(s->base); |     blk_unref(s->base); | ||||||
|     s->base = NULL; |  | ||||||
|  |  | ||||||
|     /* FIXME: bdrv_drop_intermediate treats total failures and partial failures |     if (!block_job_is_cancelled(&s->common) && ret == 0) { | ||||||
|      * identically. Further work is needed to disambiguate these cases. */ |         /* success */ | ||||||
|     return bdrv_drop_intermediate(s->commit_top_bs, s->base_bs, |         ret = bdrv_drop_intermediate(s->commit_top_bs, base, | ||||||
|                                   s->backing_file_str); |                                      s->backing_file_str); | ||||||
| } |     } else { | ||||||
|  |         /* XXX Can (or should) we somehow keep 'consistent read' blocked even | ||||||
| static void commit_abort(Job *job) |          * after the failed/cancelled commit job is gone? If we already wrote | ||||||
| { |          * something to base, the intermediate images aren't valid any more. */ | ||||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); |         remove_commit_top_bs = true; | ||||||
|     BlockDriverState *top_bs = blk_bs(s->top); |  | ||||||
|  |  | ||||||
|     if (s->chain_frozen) { |  | ||||||
|         bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* 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 |     /* restore base open flags here if appropriate (e.g., change the base back | ||||||
|      * to r/o). These reopens do not need to be atomic, since we won't abort |      * to r/o). These reopens do not need to be atomic, since we won't abort | ||||||
|      * even on failure here */ |      * even on failure here */ | ||||||
|     if (s->base_read_only) { |     if (s->base_flags != bdrv_get_flags(base)) { | ||||||
|         bdrv_reopen_set_read_only(s->base_bs, true, NULL); |         bdrv_reopen(base, s->base_flags, NULL); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     g_free(s->backing_file_str); |     g_free(s->backing_file_str); | ||||||
|     blk_unref(s->top); |     blk_unref(s->top); | ||||||
|  |  | ||||||
|  |     /* If there is more than one reference to the job (e.g. if called from | ||||||
|  |      * block_job_finish_sync()), block_job_completed() won't free it and | ||||||
|  |      * therefore the blockers on the intermediate nodes remain. This would | ||||||
|  |      * cause bdrv_set_backing_hd() to fail. */ | ||||||
|  |     block_job_remove_all_bdrv(job); | ||||||
|  |  | ||||||
|  |     block_job_completed(&s->common, ret); | ||||||
|  |     g_free(data); | ||||||
|  |  | ||||||
|  |     /* If bdrv_drop_intermediate() didn't already do that, remove the commit | ||||||
|  |      * filter driver from the backing chain. Do this as the final step so that | ||||||
|  |      * the 'consistent read' permission can be granted.  */ | ||||||
|  |     if (remove_commit_top_bs) { | ||||||
|  |         bdrv_child_try_set_perm(commit_top_bs->backing, 0, BLK_PERM_ALL, | ||||||
|  |                                 &error_abort); | ||||||
|  |         bdrv_replace_node(commit_top_bs, backing_bs(commit_top_bs), | ||||||
|  |                           &error_abort); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bdrv_unref(commit_top_bs); | ||||||
|  |     bdrv_unref(top); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coroutine_fn commit_run(Job *job, Error **errp) | static void coroutine_fn commit_run(void *opaque) | ||||||
| { | { | ||||||
|     CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); |     CommitBlockJob *s = opaque; | ||||||
|  |     CommitCompleteData *data; | ||||||
|     int64_t offset; |     int64_t offset; | ||||||
|     uint64_t delay_ns = 0; |     uint64_t delay_ns = 0; | ||||||
|     int ret = 0; |     int ret = 0; | ||||||
|     int64_t n = 0; /* bytes */ |     int64_t n = 0; /* bytes */ | ||||||
|     void *buf = NULL; |     void *buf = NULL; | ||||||
|     int bytes_written = 0; |     int bytes_written = 0; | ||||||
|     int64_t len, base_len; |     int64_t base_len; | ||||||
|  |  | ||||||
|     ret = len = blk_getlength(s->top); |     ret = s->common.len = blk_getlength(s->top); | ||||||
|     if (len < 0) { |  | ||||||
|  |     if (s->common.len < 0) { | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|     job_progress_set_remaining(&s->common.job, len); |  | ||||||
|  |  | ||||||
|     ret = base_len = blk_getlength(s->base); |     ret = base_len = blk_getlength(s->base); | ||||||
|     if (base_len < 0) { |     if (base_len < 0) { | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (base_len < len) { |     if (base_len < s->common.len) { | ||||||
|         ret = blk_truncate(s->base, len, PREALLOC_MODE_OFF, NULL); |         ret = blk_truncate(s->base, s->common.len, PREALLOC_MODE_OFF, NULL); | ||||||
|         if (ret) { |         if (ret) { | ||||||
|             goto out; |             goto out; | ||||||
|         } |         } | ||||||
| @@ -166,14 +168,14 @@ static int coroutine_fn commit_run(Job *job, Error **errp) | |||||||
|  |  | ||||||
|     buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE); |     buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE); | ||||||
|  |  | ||||||
|     for (offset = 0; offset < len; offset += n) { |     for (offset = 0; offset < s->common.len; offset += n) { | ||||||
|         bool copy; |         bool copy; | ||||||
|  |  | ||||||
|         /* Note that even when no rate limit is applied we need to yield |         /* Note that even when no rate limit is applied we need to yield | ||||||
|          * with no pending I/O here so that bdrv_drain_all() returns. |          * with no pending I/O here so that bdrv_drain_all() returns. | ||||||
|          */ |          */ | ||||||
|         job_sleep_ns(&s->common.job, delay_ns); |         block_job_sleep_ns(&s->common, delay_ns); | ||||||
|         if (job_is_cancelled(&s->common.job)) { |         if (block_job_is_cancelled(&s->common)) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         /* Copy if allocated above the base */ |         /* Copy if allocated above the base */ | ||||||
| @@ -196,12 +198,10 @@ static int coroutine_fn commit_run(Job *job, Error **errp) | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         /* Publish progress */ |         /* Publish progress */ | ||||||
|         job_progress_update(&s->common.job, n); |         s->common.offset += n; | ||||||
|  |  | ||||||
|         if (copy) { |         if (copy && s->common.speed) { | ||||||
|             delay_ns = block_job_ratelimit_get_delay(&s->common, n); |             delay_ns = ratelimit_calculate_delay(&s->limit, n); | ||||||
|         } else { |  | ||||||
|             delay_ns = 0; |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -210,21 +210,27 @@ static int coroutine_fn commit_run(Job *job, Error **errp) | |||||||
| out: | out: | ||||||
|     qemu_vfree(buf); |     qemu_vfree(buf); | ||||||
|  |  | ||||||
|     return ret; |     data = g_malloc(sizeof(*data)); | ||||||
|  |     data->ret = ret; | ||||||
|  |     block_job_defer_to_main_loop(&s->common, commit_complete, data); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp) | ||||||
|  | { | ||||||
|  |     CommitBlockJob *s = container_of(job, CommitBlockJob, common); | ||||||
|  |  | ||||||
|  |     if (speed < 0) { | ||||||
|  |         error_setg(errp, QERR_INVALID_PARAMETER, "speed"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     ratelimit_set_speed(&s->limit, speed, SLICE_TIME); | ||||||
| } | } | ||||||
|  |  | ||||||
| static const BlockJobDriver commit_job_driver = { | static const BlockJobDriver commit_job_driver = { | ||||||
|     .job_driver = { |     .instance_size = sizeof(CommitBlockJob), | ||||||
|         .instance_size = sizeof(CommitBlockJob), |     .job_type      = BLOCK_JOB_TYPE_COMMIT, | ||||||
|         .job_type      = JOB_TYPE_COMMIT, |     .set_speed     = commit_set_speed, | ||||||
|         .free          = block_job_free, |     .start         = commit_run, | ||||||
|         .user_resume   = block_job_user_resume, |  | ||||||
|         .drain         = block_job_drain, |  | ||||||
|         .run           = commit_run, |  | ||||||
|         .prepare       = commit_prepare, |  | ||||||
|         .abort         = commit_abort, |  | ||||||
|         .clean         = commit_clean |  | ||||||
|     }, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs, | static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs, | ||||||
| @@ -233,12 +239,17 @@ static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs, | |||||||
|     return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); |     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), |     pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), | ||||||
|             bs->backing->bs->filename); |             bs->backing->bs->filename); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void bdrv_commit_top_close(BlockDriverState *bs) | ||||||
|  | { | ||||||
|  | } | ||||||
|  |  | ||||||
| static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c, | static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c, | ||||||
|                                        const BdrvChildRole *role, |                                        const BdrvChildRole *role, | ||||||
|                                        BlockReopenQueue *reopen_queue, |                                        BlockReopenQueue *reopen_queue, | ||||||
| @@ -256,16 +267,17 @@ static BlockDriver bdrv_commit_top = { | |||||||
|     .bdrv_co_preadv             = bdrv_commit_top_preadv, |     .bdrv_co_preadv             = bdrv_commit_top_preadv, | ||||||
|     .bdrv_co_block_status       = bdrv_co_block_status_from_backing, |     .bdrv_co_block_status       = bdrv_co_block_status_from_backing, | ||||||
|     .bdrv_refresh_filename      = bdrv_commit_top_refresh_filename, |     .bdrv_refresh_filename      = bdrv_commit_top_refresh_filename, | ||||||
|  |     .bdrv_close                 = bdrv_commit_top_close, | ||||||
|     .bdrv_child_perm            = bdrv_commit_top_child_perm, |     .bdrv_child_perm            = bdrv_commit_top_child_perm, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| void commit_start(const char *job_id, BlockDriverState *bs, | void commit_start(const char *job_id, BlockDriverState *bs, | ||||||
|                   BlockDriverState *base, BlockDriverState *top, |                   BlockDriverState *base, BlockDriverState *top, int64_t speed, | ||||||
|                   int creation_flags, int64_t speed, |  | ||||||
|                   BlockdevOnError on_error, const char *backing_file_str, |                   BlockdevOnError on_error, const char *backing_file_str, | ||||||
|                   const char *filter_node_name, Error **errp) |                   const char *filter_node_name, Error **errp) | ||||||
| { | { | ||||||
|     CommitBlockJob *s; |     CommitBlockJob *s; | ||||||
|  |     int orig_base_flags; | ||||||
|     BlockDriverState *iter; |     BlockDriverState *iter; | ||||||
|     BlockDriverState *commit_top_bs = NULL; |     BlockDriverState *commit_top_bs = NULL; | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
| @@ -277,16 +289,18 @@ void commit_start(const char *job_id, BlockDriverState *bs, | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL, |     s = block_job_create(job_id, &commit_job_driver, bs, 0, BLK_PERM_ALL, | ||||||
|                          speed, creation_flags, NULL, NULL, errp); |                          speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp); | ||||||
|     if (!s) { |     if (!s) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* convert base to r/w, if necessary */ |     /* convert base to r/w, if necessary */ | ||||||
|     s->base_read_only = bdrv_is_read_only(base); |     orig_base_flags = bdrv_get_flags(base); | ||||||
|     if (s->base_read_only) { |     if (!(orig_base_flags & BDRV_O_RDWR)) { | ||||||
|         if (bdrv_reopen_set_read_only(base, false, errp) != 0) { |         bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err); | ||||||
|  |         if (local_err != NULL) { | ||||||
|  |             error_propagate(errp, local_err); | ||||||
|             goto fail; |             goto fail; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -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); |     ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         goto fail; |         goto fail; | ||||||
| @@ -358,7 +367,6 @@ void commit_start(const char *job_id, BlockDriverState *bs, | |||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|     s->base_bs = base; |  | ||||||
|  |  | ||||||
|     /* Required permissions are already taken with block_job_add_bdrv() */ |     /* Required permissions are already taken with block_job_add_bdrv() */ | ||||||
|     s->top = blk_new(0, BLK_PERM_ALL); |     s->top = blk_new(0, BLK_PERM_ALL); | ||||||
| @@ -367,29 +375,25 @@ void commit_start(const char *job_id, BlockDriverState *bs, | |||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     s->base_flags = orig_base_flags; | ||||||
|     s->backing_file_str = g_strdup(backing_file_str); |     s->backing_file_str = g_strdup(backing_file_str); | ||||||
|     s->on_error = on_error; |     s->on_error = on_error; | ||||||
|  |  | ||||||
|     trace_commit_start(bs, base, top, s); |     trace_commit_start(bs, base, top, s); | ||||||
|     job_start(&s->common.job); |     block_job_start(&s->common); | ||||||
|     return; |     return; | ||||||
|  |  | ||||||
| fail: | fail: | ||||||
|     if (s->chain_frozen) { |  | ||||||
|         bdrv_unfreeze_backing_chain(commit_top_bs, base); |  | ||||||
|     } |  | ||||||
|     if (s->base) { |     if (s->base) { | ||||||
|         blk_unref(s->base); |         blk_unref(s->base); | ||||||
|     } |     } | ||||||
|     if (s->top) { |     if (s->top) { | ||||||
|         blk_unref(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) { |     if (commit_top_bs) { | ||||||
|         bdrv_replace_node(commit_top_bs, top, &error_abort); |         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; |     BlockDriverState *commit_top_bs = NULL; | ||||||
|     BlockDriver *drv = bs->drv; |     BlockDriver *drv = bs->drv; | ||||||
|     int64_t offset, length, backing_length; |     int64_t offset, length, backing_length; | ||||||
|     int ro; |     int ro, open_flags; | ||||||
|     int64_t n; |     int64_t n; | ||||||
|     int ret = 0; |     int ret = 0; | ||||||
|     uint8_t *buf = NULL; |     uint8_t *buf = NULL; | ||||||
| @@ -422,9 +426,10 @@ int bdrv_commit(BlockDriverState *bs) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     ro = bs->backing->bs->read_only; |     ro = bs->backing->bs->read_only; | ||||||
|  |     open_flags =  bs->backing->bs->open_flags; | ||||||
|  |  | ||||||
|     if (ro) { |     if (ro) { | ||||||
|         if (bdrv_reopen_set_read_only(bs->backing->bs, false, NULL)) { |         if (bdrv_reopen(bs->backing->bs, open_flags | BDRV_O_RDWR, NULL)) { | ||||||
|             return -EACCES; |             return -EACCES; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -534,7 +539,7 @@ ro_cleanup: | |||||||
|  |  | ||||||
|     if (ro) { |     if (ro) { | ||||||
|         /* ignoring error return here */ |         /* ignoring error return here */ | ||||||
|         bdrv_reopen_set_read_only(bs->backing->bs, true, NULL); |         bdrv_reopen(bs->backing->bs, open_flags & ~BDRV_O_RDWR, NULL); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return ret; |     return ret; | ||||||
|   | |||||||
| @@ -1,167 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copy-on-read filter block driver |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2018 Red Hat, Inc. |  | ||||||
|  * |  | ||||||
|  * Author: |  | ||||||
|  *   Max Reitz <mreitz@redhat.com> |  | ||||||
|  * |  | ||||||
|  * This program is free software; you can redistribute it and/or |  | ||||||
|  * modify it under the terms of the GNU General Public License as |  | ||||||
|  * published by the Free Software Foundation; either version 2 or |  | ||||||
|  * (at your option) version 3 of the License. |  | ||||||
|  * |  | ||||||
|  * This program is distributed in the hope that it will be useful, |  | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
|  * GNU General Public License for more details. |  | ||||||
|  * |  | ||||||
|  * You should have received a copy of the GNU General Public License |  | ||||||
|  * along with this program; if not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "block/block_int.h" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static int cor_open(BlockDriverState *bs, QDict *options, int flags, |  | ||||||
|                     Error **errp) |  | ||||||
| { |  | ||||||
|     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, |  | ||||||
|                                errp); |  | ||||||
|     if (!bs->file) { |  | ||||||
|         return -EINVAL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | |  | ||||||
|                                 (BDRV_REQ_FUA & |  | ||||||
|                                     bs->file->bs->supported_write_flags); |  | ||||||
|  |  | ||||||
|     bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | |  | ||||||
|                                ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & |  | ||||||
|                                     bs->file->bs->supported_zero_flags); |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #define PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \ |  | ||||||
|                           | BLK_PERM_WRITE \ |  | ||||||
|                           | BLK_PERM_RESIZE) |  | ||||||
| #define PERM_UNCHANGED (BLK_PERM_ALL & ~PERM_PASSTHROUGH) |  | ||||||
|  |  | ||||||
| static void cor_child_perm(BlockDriverState *bs, BdrvChild *c, |  | ||||||
|                            const BdrvChildRole *role, |  | ||||||
|                            BlockReopenQueue *reopen_queue, |  | ||||||
|                            uint64_t perm, uint64_t shared, |  | ||||||
|                            uint64_t *nperm, uint64_t *nshared) |  | ||||||
| { |  | ||||||
|     if (c == NULL) { |  | ||||||
|         *nperm = (perm & PERM_PASSTHROUGH) | BLK_PERM_WRITE_UNCHANGED; |  | ||||||
|         *nshared = (shared & PERM_PASSTHROUGH) | PERM_UNCHANGED; |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     *nperm = (perm & PERM_PASSTHROUGH) | |  | ||||||
|              (c->perm & PERM_UNCHANGED); |  | ||||||
|     *nshared = (shared & PERM_PASSTHROUGH) | |  | ||||||
|                (c->shared_perm & PERM_UNCHANGED); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static int64_t cor_getlength(BlockDriverState *bs) |  | ||||||
| { |  | ||||||
|     return bdrv_getlength(bs->file->bs); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static int coroutine_fn cor_co_truncate(BlockDriverState *bs, int64_t offset, |  | ||||||
|                                         PreallocMode prealloc, Error **errp) |  | ||||||
| { |  | ||||||
|     return bdrv_co_truncate(bs->file, offset, prealloc, errp); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static int coroutine_fn cor_co_preadv(BlockDriverState *bs, |  | ||||||
|                                       uint64_t offset, uint64_t bytes, |  | ||||||
|                                       QEMUIOVector *qiov, int flags) |  | ||||||
| { |  | ||||||
|     return bdrv_co_preadv(bs->file, offset, bytes, qiov, |  | ||||||
|                           flags | BDRV_REQ_COPY_ON_READ); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static int coroutine_fn cor_co_pwritev(BlockDriverState *bs, |  | ||||||
|                                        uint64_t offset, uint64_t bytes, |  | ||||||
|                                        QEMUIOVector *qiov, int flags) |  | ||||||
| { |  | ||||||
|  |  | ||||||
|     return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static int coroutine_fn cor_co_pwrite_zeroes(BlockDriverState *bs, |  | ||||||
|                                              int64_t offset, int bytes, |  | ||||||
|                                              BdrvRequestFlags flags) |  | ||||||
| { |  | ||||||
|     return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static int coroutine_fn cor_co_pdiscard(BlockDriverState *bs, |  | ||||||
|                                         int64_t offset, int bytes) |  | ||||||
| { |  | ||||||
|     return bdrv_co_pdiscard(bs->file, offset, bytes); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void cor_eject(BlockDriverState *bs, bool eject_flag) |  | ||||||
| { |  | ||||||
|     bdrv_eject(bs->file->bs, eject_flag); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static void cor_lock_medium(BlockDriverState *bs, bool locked) |  | ||||||
| { |  | ||||||
|     bdrv_lock_medium(bs->file->bs, locked); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| static bool cor_recurse_is_first_non_filter(BlockDriverState *bs, |  | ||||||
|                                             BlockDriverState *candidate) |  | ||||||
| { |  | ||||||
|     return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 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 "qemu/osdep.h" | ||||||
| #include "block/block_int.h" | #include "block/block_int.h" | ||||||
| #include "qemu/job.h" |  | ||||||
| #include "qapi/qapi-commands-block-core.h" | #include "qapi/qapi-commands-block-core.h" | ||||||
| #include "qapi/qapi-visit-block-core.h" |  | ||||||
| #include "qapi/clone-visitor.h" |  | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
|  |  | ||||||
| typedef struct BlockdevCreateJob { | typedef struct BlockdevCreateCo { | ||||||
|     Job common; |  | ||||||
|     BlockDriver *drv; |     BlockDriver *drv; | ||||||
|     BlockdevCreateOptions *opts; |     BlockdevCreateOptions *opts; | ||||||
| } BlockdevCreateJob; |  | ||||||
|  |  | ||||||
| static int coroutine_fn blockdev_create_run(Job *job, Error **errp) |  | ||||||
| { |  | ||||||
|     BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); |  | ||||||
|     int ret; |     int ret; | ||||||
|  |     Error **errp; | ||||||
|  | } BlockdevCreateCo; | ||||||
|  |  | ||||||
|     job_progress_set_remaining(&s->common, 1); | static void coroutine_fn bdrv_co_create_co_entry(void *opaque) | ||||||
|     ret = s->drv->bdrv_co_create(s->opts, errp); | { | ||||||
|     job_progress_update(&s->common, 1); |     BlockdevCreateCo *cco = opaque; | ||||||
|  |     cco->ret = cco->drv->bdrv_co_create(cco->opts, cco->errp); | ||||||
|     qapi_free_BlockdevCreateOptions(s->opts); |  | ||||||
|  |  | ||||||
|     return ret; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static const JobDriver blockdev_create_job_driver = { | void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp) | ||||||
|     .instance_size = sizeof(BlockdevCreateJob), |  | ||||||
|     .job_type      = JOB_TYPE_CREATE, |  | ||||||
|     .run           = blockdev_create_run, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options, |  | ||||||
|                          Error **errp) |  | ||||||
| { | { | ||||||
|     BlockdevCreateJob *s; |  | ||||||
|     const char *fmt = BlockdevDriver_str(options->driver); |     const char *fmt = BlockdevDriver_str(options->driver); | ||||||
|     BlockDriver *drv = bdrv_find_format(fmt); |     BlockDriver *drv = bdrv_find_format(fmt); | ||||||
|  |     Coroutine *co; | ||||||
|  |     BlockdevCreateCo cco; | ||||||
|  |  | ||||||
|     /* If the driver is in the schema, we know that it exists. But it may not |     /* If the driver is in the schema, we know that it exists. But it may not | ||||||
|      * be whitelisted. */ |      * be whitelisted. */ | ||||||
| @@ -71,24 +55,22 @@ void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options, | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Error out if the driver doesn't support .bdrv_co_create */ |     /* Call callback if it exists */ | ||||||
|     if (!drv->bdrv_co_create) { |     if (!drv->bdrv_co_create) { | ||||||
|         error_setg(errp, "Driver does not support blockdev-create"); |         error_setg(errp, "Driver does not support blockdev-create"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* Create the block job */ |     cco = (BlockdevCreateCo) { | ||||||
|     /* TODO Running in the main context. Block drivers need to error out or add |         .drv = drv, | ||||||
|      * locking when they use a BDS in a different AioContext. */ |         .opts = options, | ||||||
|     s = job_create(job_id, &blockdev_create_job_driver, NULL, |         .ret = -EINPROGRESS, | ||||||
|                    qemu_get_aio_context(), JOB_DEFAULT | JOB_MANUAL_DISMISS, |         .errp = errp, | ||||||
|                    NULL, NULL, errp); |     }; | ||||||
|     if (!s) { |  | ||||||
|         return; |     co = qemu_coroutine_create(bdrv_co_create_co_entry, &cco); | ||||||
|  |     qemu_coroutine_enter(co); | ||||||
|  |     while (cco.ret == -EINPROGRESS) { | ||||||
|  |         aio_poll(qemu_get_aio_context(), true); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     s->drv = drv, |  | ||||||
|     s->opts = QAPI_CLONE(BlockdevCreateOptions, options), |  | ||||||
|  |  | ||||||
|     job_start(&s->common); |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										288
									
								
								block/crypto.c
									
									
									
									
									
								
							
							
						
						
									
										288
									
								
								block/crypto.c
									
									
									
									
									
								
							| @@ -21,15 +21,15 @@ | |||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
|  |  | ||||||
| #include "block/block_int.h" | #include "block/block_int.h" | ||||||
| #include "block/qdict.h" |  | ||||||
| #include "sysemu/block-backend.h" | #include "sysemu/block-backend.h" | ||||||
| #include "crypto/block.h" | #include "crypto/block.h" | ||||||
| #include "qapi/opts-visitor.h" | #include "qapi/opts-visitor.h" | ||||||
| #include "qapi/qapi-visit-crypto.h" | #include "qapi/qapi-visit-crypto.h" | ||||||
|  | #include "qapi/qmp/qdict.h" | ||||||
| #include "qapi/qobject-input-visitor.h" | #include "qapi/qobject-input-visitor.h" | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qemu/option.h" | #include "qemu/option.h" | ||||||
| #include "crypto.h" | #include "block/crypto.h" | ||||||
|  |  | ||||||
| typedef struct BlockCrypto BlockCrypto; | typedef struct BlockCrypto BlockCrypto; | ||||||
|  |  | ||||||
| @@ -71,6 +71,8 @@ static ssize_t block_crypto_read_func(QCryptoBlock *block, | |||||||
|  |  | ||||||
|  |  | ||||||
| struct BlockCryptoCreateData { | struct BlockCryptoCreateData { | ||||||
|  |     const char *filename; | ||||||
|  |     QemuOpts *opts; | ||||||
|     BlockBackend *blk; |     BlockBackend *blk; | ||||||
|     uint64_t size; |     uint64_t size; | ||||||
| }; | }; | ||||||
| @@ -101,18 +103,27 @@ static ssize_t block_crypto_init_func(QCryptoBlock *block, | |||||||
|                                       Error **errp) |                                       Error **errp) | ||||||
| { | { | ||||||
|     struct BlockCryptoCreateData *data = opaque; |     struct BlockCryptoCreateData *data = opaque; | ||||||
|  |     int ret; | ||||||
|     if (data->size > INT64_MAX || headerlen > INT64_MAX - data->size) { |  | ||||||
|         error_setg(errp, "The requested file size is too large"); |  | ||||||
|         return -EFBIG; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* User provided size should reflect amount of space made |     /* User provided size should reflect amount of space made | ||||||
|      * available to the guest, so we must take account of that |      * available to the guest, so we must take account of that | ||||||
|      * which will be used by the crypto header |      * which will be used by the crypto header | ||||||
|      */ |      */ | ||||||
|     return blk_truncate(data->blk, data->size + headerlen, PREALLOC_MODE_OFF, |     data->size += headerlen; | ||||||
|                         errp); |  | ||||||
|  |     qemu_opt_set_number(data->opts, BLOCK_OPT_SIZE, data->size, &error_abort); | ||||||
|  |     ret = bdrv_create_file(data->filename, data->opts, errp); | ||||||
|  |     if (ret < 0) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     data->blk = blk_new_open(data->filename, NULL, NULL, | ||||||
|  |                              BDRV_O_RDWR | BDRV_O_PROTOCOL, errp); | ||||||
|  |     if (!data->blk) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -148,36 +159,102 @@ static QemuOptsList block_crypto_create_opts_luks = { | |||||||
|  |  | ||||||
|  |  | ||||||
| QCryptoBlockOpenOptions * | QCryptoBlockOpenOptions * | ||||||
| block_crypto_open_opts_init(QDict *opts, Error **errp) | block_crypto_open_opts_init(QCryptoBlockFormat format, | ||||||
|  |                             QDict *opts, | ||||||
|  |                             Error **errp) | ||||||
| { | { | ||||||
|     Visitor *v; |     Visitor *v; | ||||||
|     QCryptoBlockOpenOptions *ret; |     QCryptoBlockOpenOptions *ret = NULL; | ||||||
|  |     Error *local_err = NULL; | ||||||
|  |  | ||||||
|     v = qobject_input_visitor_new_flat_confused(opts, errp); |     ret = g_new0(QCryptoBlockOpenOptions, 1); | ||||||
|     if (!v) { |     ret->format = format; | ||||||
|         return NULL; |  | ||||||
|  |     v = qobject_input_visitor_new_keyval(QOBJECT(opts)); | ||||||
|  |  | ||||||
|  |     visit_start_struct(v, NULL, NULL, 0, &local_err); | ||||||
|  |     if (local_err) { | ||||||
|  |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     visit_type_QCryptoBlockOpenOptions(v, NULL, &ret, errp); |     switch (format) { | ||||||
|  |     case Q_CRYPTO_BLOCK_FORMAT_LUKS: | ||||||
|  |         visit_type_QCryptoBlockOptionsLUKS_members( | ||||||
|  |             v, &ret->u.luks, &local_err); | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case Q_CRYPTO_BLOCK_FORMAT_QCOW: | ||||||
|  |         visit_type_QCryptoBlockOptionsQCow_members( | ||||||
|  |             v, &ret->u.qcow, &local_err); | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     default: | ||||||
|  |         error_setg(&local_err, "Unsupported block format %d", format); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     if (!local_err) { | ||||||
|  |         visit_check_struct(v, &local_err); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     visit_end_struct(v, NULL); | ||||||
|  |  | ||||||
|  |  out: | ||||||
|  |     if (local_err) { | ||||||
|  |         error_propagate(errp, local_err); | ||||||
|  |         qapi_free_QCryptoBlockOpenOptions(ret); | ||||||
|  |         ret = NULL; | ||||||
|  |     } | ||||||
|     visit_free(v); |     visit_free(v); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| QCryptoBlockCreateOptions * | QCryptoBlockCreateOptions * | ||||||
| block_crypto_create_opts_init(QDict *opts, Error **errp) | block_crypto_create_opts_init(QCryptoBlockFormat format, | ||||||
|  |                               QDict *opts, | ||||||
|  |                               Error **errp) | ||||||
| { | { | ||||||
|     Visitor *v; |     Visitor *v; | ||||||
|     QCryptoBlockCreateOptions *ret; |     QCryptoBlockCreateOptions *ret = NULL; | ||||||
|  |     Error *local_err = NULL; | ||||||
|  |  | ||||||
|     v = qobject_input_visitor_new_flat_confused(opts, errp); |     ret = g_new0(QCryptoBlockCreateOptions, 1); | ||||||
|     if (!v) { |     ret->format = format; | ||||||
|         return NULL; |  | ||||||
|  |     v = qobject_input_visitor_new_keyval(QOBJECT(opts)); | ||||||
|  |  | ||||||
|  |     visit_start_struct(v, NULL, NULL, 0, &local_err); | ||||||
|  |     if (local_err) { | ||||||
|  |         goto out; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     visit_type_QCryptoBlockCreateOptions(v, NULL, &ret, errp); |     switch (format) { | ||||||
|  |     case Q_CRYPTO_BLOCK_FORMAT_LUKS: | ||||||
|  |         visit_type_QCryptoBlockCreateOptionsLUKS_members( | ||||||
|  |             v, &ret->u.luks, &local_err); | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case Q_CRYPTO_BLOCK_FORMAT_QCOW: | ||||||
|  |         visit_type_QCryptoBlockOptionsQCow_members( | ||||||
|  |             v, &ret->u.qcow, &local_err); | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     default: | ||||||
|  |         error_setg(&local_err, "Unsupported block format %d", format); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     if (!local_err) { | ||||||
|  |         visit_check_struct(v, &local_err); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     visit_end_struct(v, NULL); | ||||||
|  |  | ||||||
|  |  out: | ||||||
|  |     if (local_err) { | ||||||
|  |         error_propagate(errp, local_err); | ||||||
|  |         qapi_free_QCryptoBlockCreateOptions(ret); | ||||||
|  |         ret = NULL; | ||||||
|  |     } | ||||||
|     visit_free(v); |     visit_free(v); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| @@ -215,9 +292,8 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     cryptoopts = qemu_opts_to_qdict(opts, NULL); |     cryptoopts = qemu_opts_to_qdict(opts, NULL); | ||||||
|     qdict_put_str(cryptoopts, "format", QCryptoBlockFormat_str(format)); |  | ||||||
|  |  | ||||||
|     open_opts = block_crypto_open_opts_init(cryptoopts, errp); |     open_opts = block_crypto_open_opts_init(format, cryptoopts, errp); | ||||||
|     if (!open_opts) { |     if (!open_opts) { | ||||||
|         goto cleanup; |         goto cleanup; | ||||||
|     } |     } | ||||||
| @@ -229,7 +305,6 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, | |||||||
|                                        block_crypto_read_func, |                                        block_crypto_read_func, | ||||||
|                                        bs, |                                        bs, | ||||||
|                                        cflags, |                                        cflags, | ||||||
|                                        1, |  | ||||||
|                                        errp); |                                        errp); | ||||||
|  |  | ||||||
|     if (!crypto->block) { |     if (!crypto->block) { | ||||||
| @@ -241,35 +316,36 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, | |||||||
|  |  | ||||||
|     ret = 0; |     ret = 0; | ||||||
|  cleanup: |  cleanup: | ||||||
|     qobject_unref(cryptoopts); |     QDECREF(cryptoopts); | ||||||
|     qapi_free_QCryptoBlockOpenOptions(open_opts); |     qapi_free_QCryptoBlockOpenOptions(open_opts); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| static int block_crypto_co_create_generic(BlockDriverState *bs, | static int block_crypto_create_generic(QCryptoBlockFormat format, | ||||||
|                                           int64_t size, |                                        const char *filename, | ||||||
|                                           QCryptoBlockCreateOptions *opts, |                                        QemuOpts *opts, | ||||||
|                                           Error **errp) |                                        Error **errp) | ||||||
| { | { | ||||||
|     int ret; |     int ret = -EINVAL; | ||||||
|     BlockBackend *blk; |     QCryptoBlockCreateOptions *create_opts = NULL; | ||||||
|     QCryptoBlock *crypto = NULL; |     QCryptoBlock *crypto = NULL; | ||||||
|     struct BlockCryptoCreateData data; |     struct BlockCryptoCreateData data = { | ||||||
|  |         .size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), | ||||||
|  |                          BDRV_SECTOR_SIZE), | ||||||
|  |         .opts = opts, | ||||||
|  |         .filename = filename, | ||||||
|  |     }; | ||||||
|  |     QDict *cryptoopts; | ||||||
|  |  | ||||||
|     blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); |     cryptoopts = qemu_opts_to_qdict(opts, NULL); | ||||||
|  |  | ||||||
|     ret = blk_insert_bs(blk, bs, errp); |     create_opts = block_crypto_create_opts_init(format, cryptoopts, errp); | ||||||
|     if (ret < 0) { |     if (!create_opts) { | ||||||
|         goto cleanup; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     data = (struct BlockCryptoCreateData) { |     crypto = qcrypto_block_create(create_opts, NULL, | ||||||
|         .blk = blk, |  | ||||||
|         .size = size, |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     crypto = qcrypto_block_create(opts, NULL, |  | ||||||
|                                   block_crypto_init_func, |                                   block_crypto_init_func, | ||||||
|                                   block_crypto_write_func, |                                   block_crypto_write_func, | ||||||
|                                   &data, |                                   &data, | ||||||
| @@ -282,27 +358,24 @@ static int block_crypto_co_create_generic(BlockDriverState *bs, | |||||||
|  |  | ||||||
|     ret = 0; |     ret = 0; | ||||||
|  cleanup: |  cleanup: | ||||||
|  |     QDECREF(cryptoopts); | ||||||
|     qcrypto_block_free(crypto); |     qcrypto_block_free(crypto); | ||||||
|     blk_unref(blk); |     blk_unref(data.blk); | ||||||
|  |     qapi_free_QCryptoBlockCreateOptions(create_opts); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coroutine_fn | static int block_crypto_truncate(BlockDriverState *bs, int64_t offset, | ||||||
| block_crypto_co_truncate(BlockDriverState *bs, int64_t offset, |                                  PreallocMode prealloc, Error **errp) | ||||||
|                          PreallocMode prealloc, Error **errp) |  | ||||||
| { | { | ||||||
|     BlockCrypto *crypto = bs->opaque; |     BlockCrypto *crypto = bs->opaque; | ||||||
|     uint64_t payload_offset = |     uint64_t payload_offset = | ||||||
|         qcrypto_block_get_payload_offset(crypto->block); |         qcrypto_block_get_payload_offset(crypto->block); | ||||||
|  |     assert(payload_offset < (INT64_MAX - offset)); | ||||||
|     if (payload_offset > INT64_MAX - offset) { |  | ||||||
|         error_setg(errp, "The requested file size is too large"); |  | ||||||
|         return -EFBIG; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     offset += payload_offset; |     offset += payload_offset; | ||||||
|  |  | ||||||
|     return bdrv_co_truncate(bs->file, offset, prealloc, errp); |     return bdrv_truncate(bs->file, offset, prealloc, errp); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void block_crypto_close(BlockDriverState *bs) | static void block_crypto_close(BlockDriverState *bs) | ||||||
| @@ -464,10 +537,7 @@ static int64_t block_crypto_getlength(BlockDriverState *bs) | |||||||
|  |  | ||||||
|     uint64_t offset = qcrypto_block_get_payload_offset(crypto->block); |     uint64_t offset = qcrypto_block_get_payload_offset(crypto->block); | ||||||
|     assert(offset < INT64_MAX); |     assert(offset < INT64_MAX); | ||||||
|  |     assert(offset < len); | ||||||
|     if (offset > len) { |  | ||||||
|         return -EIO; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     len -= offset; |     len -= offset; | ||||||
|  |  | ||||||
| @@ -492,88 +562,12 @@ static int block_crypto_open_luks(BlockDriverState *bs, | |||||||
|                                      bs, options, flags, errp); |                                      bs, options, flags, errp); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coroutine_fn |  | ||||||
| block_crypto_co_create_luks(BlockdevCreateOptions *create_options, Error **errp) |  | ||||||
| { |  | ||||||
|     BlockdevCreateOptionsLUKS *luks_opts; |  | ||||||
|     BlockDriverState *bs = NULL; |  | ||||||
|     QCryptoBlockCreateOptions create_opts; |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     assert(create_options->driver == BLOCKDEV_DRIVER_LUKS); |  | ||||||
|     luks_opts = &create_options->u.luks; |  | ||||||
|  |  | ||||||
|     bs = bdrv_open_blockdev_ref(luks_opts->file, errp); |  | ||||||
|     if (bs == NULL) { |  | ||||||
|         return -EIO; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     create_opts = (QCryptoBlockCreateOptions) { |  | ||||||
|         .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, |  | ||||||
|         .u.luks = *qapi_BlockdevCreateOptionsLUKS_base(luks_opts), |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     ret = block_crypto_co_create_generic(bs, luks_opts->size, &create_opts, |  | ||||||
|                                          errp); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = 0; |  | ||||||
| fail: |  | ||||||
|     bdrv_unref(bs); |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename, | static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename, | ||||||
|                                                          QemuOpts *opts, |                                                          QemuOpts *opts, | ||||||
|                                                          Error **errp) |                                                          Error **errp) | ||||||
| { | { | ||||||
|     QCryptoBlockCreateOptions *create_opts = NULL; |     return block_crypto_create_generic(Q_CRYPTO_BLOCK_FORMAT_LUKS, | ||||||
|     BlockDriverState *bs = NULL; |                                        filename, opts, errp); | ||||||
|     QDict *cryptoopts; |  | ||||||
|     int64_t size; |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     /* Parse options */ |  | ||||||
|     size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); |  | ||||||
|  |  | ||||||
|     cryptoopts = qemu_opts_to_qdict_filtered(opts, NULL, |  | ||||||
|                                              &block_crypto_create_opts_luks, |  | ||||||
|                                              true); |  | ||||||
|  |  | ||||||
|     qdict_put_str(cryptoopts, "format", "luks"); |  | ||||||
|     create_opts = block_crypto_create_opts_init(cryptoopts, errp); |  | ||||||
|     if (!create_opts) { |  | ||||||
|         ret = -EINVAL; |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Create protocol layer */ |  | ||||||
|     ret = bdrv_create_file(filename, opts, errp); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bs = bdrv_open(filename, NULL, NULL, |  | ||||||
|                    BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); |  | ||||||
|     if (!bs) { |  | ||||||
|         ret = -EINVAL; |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* Create format layer */ |  | ||||||
|     ret = block_crypto_co_create_generic(bs, size, create_opts, errp); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         goto fail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ret = 0; |  | ||||||
| fail: |  | ||||||
|     bdrv_unref(bs); |  | ||||||
|     qapi_free_QCryptoBlockCreateOptions(create_opts); |  | ||||||
|     qobject_unref(cryptoopts); |  | ||||||
|     return ret; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static int block_crypto_get_info_luks(BlockDriverState *bs, | static int block_crypto_get_info_luks(BlockDriverState *bs, | ||||||
| @@ -594,17 +588,20 @@ static int block_crypto_get_info_luks(BlockDriverState *bs, | |||||||
| } | } | ||||||
|  |  | ||||||
| static ImageInfoSpecific * | static ImageInfoSpecific * | ||||||
| block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp) | block_crypto_get_specific_info_luks(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     BlockCrypto *crypto = bs->opaque; |     BlockCrypto *crypto = bs->opaque; | ||||||
|     ImageInfoSpecific *spec_info; |     ImageInfoSpecific *spec_info; | ||||||
|     QCryptoBlockInfo *info; |     QCryptoBlockInfo *info; | ||||||
|  |  | ||||||
|     info = qcrypto_block_get_info(crypto->block, errp); |     info = qcrypto_block_get_info(crypto->block, NULL); | ||||||
|     if (!info) { |     if (!info) { | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|     assert(info->format == Q_CRYPTO_BLOCK_FORMAT_LUKS); |     if (info->format != Q_CRYPTO_BLOCK_FORMAT_LUKS) { | ||||||
|  |         qapi_free_QCryptoBlockInfo(info); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     spec_info = g_new(ImageInfoSpecific, 1); |     spec_info = g_new(ImageInfoSpecific, 1); | ||||||
|     spec_info->type = IMAGE_INFO_SPECIFIC_KIND_LUKS; |     spec_info->type = IMAGE_INFO_SPECIFIC_KIND_LUKS; | ||||||
| @@ -619,24 +616,15 @@ block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp) | |||||||
|     return spec_info; |     return spec_info; | ||||||
| } | } | ||||||
|  |  | ||||||
| static const char *const block_crypto_strong_runtime_opts[] = { | BlockDriver bdrv_crypto_luks = { | ||||||
|     BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET, |  | ||||||
|  |  | ||||||
|     NULL |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static BlockDriver bdrv_crypto_luks = { |  | ||||||
|     .format_name        = "luks", |     .format_name        = "luks", | ||||||
|     .instance_size      = sizeof(BlockCrypto), |     .instance_size      = sizeof(BlockCrypto), | ||||||
|     .bdrv_probe         = block_crypto_probe_luks, |     .bdrv_probe         = block_crypto_probe_luks, | ||||||
|     .bdrv_open          = block_crypto_open_luks, |     .bdrv_open          = block_crypto_open_luks, | ||||||
|     .bdrv_close         = block_crypto_close, |     .bdrv_close         = block_crypto_close, | ||||||
|     /* This driver doesn't modify LUKS metadata except when creating image. |     .bdrv_child_perm    = bdrv_format_default_perms, | ||||||
|      * Allow share-rw=on as a special case. */ |  | ||||||
|     .bdrv_child_perm    = bdrv_filter_default_perms, |  | ||||||
|     .bdrv_co_create     = block_crypto_co_create_luks, |  | ||||||
|     .bdrv_co_create_opts = block_crypto_co_create_opts_luks, |     .bdrv_co_create_opts = block_crypto_co_create_opts_luks, | ||||||
|     .bdrv_co_truncate   = block_crypto_co_truncate, |     .bdrv_truncate      = block_crypto_truncate, | ||||||
|     .create_opts        = &block_crypto_create_opts_luks, |     .create_opts        = &block_crypto_create_opts_luks, | ||||||
|  |  | ||||||
|     .bdrv_reopen_prepare = block_crypto_reopen_prepare, |     .bdrv_reopen_prepare = block_crypto_reopen_prepare, | ||||||
| @@ -646,8 +634,6 @@ static BlockDriver bdrv_crypto_luks = { | |||||||
|     .bdrv_getlength     = block_crypto_getlength, |     .bdrv_getlength     = block_crypto_getlength, | ||||||
|     .bdrv_get_info      = block_crypto_get_info_luks, |     .bdrv_get_info      = block_crypto_get_info_luks, | ||||||
|     .bdrv_get_specific_info = block_crypto_get_specific_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) | static void block_crypto_init(void) | ||||||
|   | |||||||
| @@ -89,9 +89,13 @@ | |||||||
|     } |     } | ||||||
|  |  | ||||||
| QCryptoBlockCreateOptions * | QCryptoBlockCreateOptions * | ||||||
| block_crypto_create_opts_init(QDict *opts, Error **errp); | block_crypto_create_opts_init(QCryptoBlockFormat format, | ||||||
|  |                               QDict *opts, | ||||||
|  |                               Error **errp); | ||||||
|  |  | ||||||
| QCryptoBlockOpenOptions * | QCryptoBlockOpenOptions * | ||||||
| block_crypto_open_opts_init(QDict *opts, Error **errp); | block_crypto_open_opts_init(QCryptoBlockFormat format, | ||||||
|  |                             QDict *opts, | ||||||
|  |                             Error **errp); | ||||||
|  |  | ||||||
| #endif /* BLOCK_CRYPTO_H__ */ | #endif /* BLOCK_CRYPTO_H__ */ | ||||||
|   | |||||||
							
								
								
									
										96
									
								
								block/curl.c
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								block/curl.c
									
									
									
									
									
								
							| @@ -32,10 +32,22 @@ | |||||||
| #include "crypto/secret.h" | #include "crypto/secret.h" | ||||||
| #include <curl/curl.h> | #include <curl/curl.h> | ||||||
| #include "qemu/cutils.h" | #include "qemu/cutils.h" | ||||||
| #include "trace.h" |  | ||||||
|  |  | ||||||
|  | // #define DEBUG_CURL | ||||||
| // #define DEBUG_VERBOSE | // #define DEBUG_VERBOSE | ||||||
|  |  | ||||||
|  | #ifdef DEBUG_CURL | ||||||
|  | #define DEBUG_CURL_PRINT 1 | ||||||
|  | #else | ||||||
|  | #define DEBUG_CURL_PRINT 0 | ||||||
|  | #endif | ||||||
|  | #define DPRINTF(fmt, ...)                                            \ | ||||||
|  |     do {                                                             \ | ||||||
|  |         if (DEBUG_CURL_PRINT) {                                      \ | ||||||
|  |             fprintf(stderr, fmt, ## __VA_ARGS__);                    \ | ||||||
|  |         }                                                            \ | ||||||
|  |     } while (0) | ||||||
|  |  | ||||||
| #if LIBCURL_VERSION_NUM >= 0x071000 | #if LIBCURL_VERSION_NUM >= 0x071000 | ||||||
| /* The multi interface timer callback was introduced in 7.16.0 */ | /* The multi interface timer callback was introduced in 7.16.0 */ | ||||||
| #define NEED_CURL_TIMER_CALLBACK | #define NEED_CURL_TIMER_CALLBACK | ||||||
| @@ -61,6 +73,8 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle, | |||||||
|  |  | ||||||
| #define CURL_NUM_STATES 8 | #define CURL_NUM_STATES 8 | ||||||
| #define CURL_NUM_ACB    8 | #define CURL_NUM_ACB    8 | ||||||
|  | #define READ_AHEAD_DEFAULT (256 * 1024) | ||||||
|  | #define CURL_TIMEOUT_DEFAULT 5 | ||||||
| #define CURL_TIMEOUT_MAX 10000 | #define CURL_TIMEOUT_MAX 10000 | ||||||
|  |  | ||||||
| #define CURL_BLOCK_OPT_URL       "url" | #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_USERNAME "proxy-username" | ||||||
| #define CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET "proxy-password-secret" | #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; | struct BDRVCURLState; | ||||||
|  |  | ||||||
| static bool libcurl_initialized; | static bool libcurl_initialized; | ||||||
| @@ -144,7 +154,7 @@ static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque) | |||||||
| { | { | ||||||
|     BDRVCURLState *s = opaque; |     BDRVCURLState *s = opaque; | ||||||
|  |  | ||||||
|     trace_curl_timer_cb(timeout_ms); |     DPRINTF("CURL: timer callback timeout_ms %ld\n", timeout_ms); | ||||||
|     if (timeout_ms == -1) { |     if (timeout_ms == -1) { | ||||||
|         timer_del(&s->timer); |         timer_del(&s->timer); | ||||||
|     } else { |     } else { | ||||||
| @@ -183,7 +193,7 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, | |||||||
|     } |     } | ||||||
|     socket = NULL; |     socket = NULL; | ||||||
|  |  | ||||||
|     trace_curl_sock_cb(action, (int)fd); |     DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, (int)fd); | ||||||
|     switch (action) { |     switch (action) { | ||||||
|         case CURL_POLL_IN: |         case CURL_POLL_IN: | ||||||
|             aio_set_fd_handler(s->aio_context, fd, false, |             aio_set_fd_handler(s->aio_context, fd, false, | ||||||
| @@ -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; |     size_t realsize = size * nmemb; | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
|     trace_curl_read_cb(realsize); |     DPRINTF("CURL: Just reading %zd bytes\n", realsize); | ||||||
|  |  | ||||||
|     if (!s || !s->orig_buf) { |     if (!s || !s->orig_buf) { | ||||||
|         goto read_end; |         goto read_end; | ||||||
| @@ -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_URL, s->url); | ||||||
|         curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER, |         curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER, | ||||||
|                          (long) s->sslverify); |                          (long) s->sslverify); | ||||||
|         curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYHOST, |  | ||||||
|                          s->sslverify ? 2L : 0L); |  | ||||||
|         if (s->cookie) { |         if (s->cookie) { | ||||||
|             curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie); |             curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie); | ||||||
|         } |         } | ||||||
| @@ -674,10 +682,10 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     const char *protocol_delimiter; |     const char *protocol_delimiter; | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     ret = bdrv_apply_auto_read_only(bs, "curl driver does not support writes", |  | ||||||
|                                     errp); |     if (flags & BDRV_O_RDWR) { | ||||||
|     if (ret < 0) { |         error_setg(errp, "curl block device does not support writes"); | ||||||
|         return ret; |         return -EROFS; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!libcurl_initialized) { |     if (!libcurl_initialized) { | ||||||
| @@ -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, |     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) { |     if ((s->readahead_size & 0x1ff) != 0) { | ||||||
|         error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512", |         error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512", | ||||||
|                    s->readahead_size); |                    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, |     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) { |     if (s->timeout > CURL_TIMEOUT_MAX) { | ||||||
|         error_setg(errp, "timeout parameter is too large or negative"); |         error_setg(errp, "timeout parameter is too large or negative"); | ||||||
|         goto out_noclean; |         goto out_noclean; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, |     s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, true); | ||||||
|                                      CURL_BLOCK_OPT_SSLVERIFY_DEFAULT); |  | ||||||
|  |  | ||||||
|     cookie = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE); |     cookie = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE); | ||||||
|     cookie_secret = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE_SECRET); |     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); |     qemu_co_queue_init(&s->free_state_waitq); | ||||||
|     s->aio_context = bdrv_get_aio_context(bs); |     s->aio_context = bdrv_get_aio_context(bs); | ||||||
|     s->url = g_strdup(file); |     s->url = g_strdup(file); | ||||||
| @@ -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 |     /* Prior CURL 7.19.4 return value of 0 could mean that the file size is not | ||||||
|      * know or the size is zero. From 7.19.4 CURL returns -1 if size is not |      * know or the size is zero. From 7.19.4 CURL returns -1 if size is not | ||||||
|      * known and zero if it is really zero-length file. */ |      * known and zero if it is realy zero-length file. */ | ||||||
| #if LIBCURL_VERSION_NUM >= 0x071304 | #if LIBCURL_VERSION_NUM >= 0x071304 | ||||||
|     if (d < 0) { |     if (d < 0) { | ||||||
|         pstrcpy(state->errmsg, CURL_ERROR_SIZE, |         pstrcpy(state->errmsg, CURL_ERROR_SIZE, | ||||||
| @@ -821,7 +828,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|                 "Server does not support 'range' (byte ranges)."); |                 "Server does not support 'range' (byte ranges)."); | ||||||
|         goto out; |         goto out; | ||||||
|     } |     } | ||||||
|     trace_curl_open_size(s->len); |     DPRINTF("CURL: Size = %" PRIu64 "\n", s->len); | ||||||
|  |  | ||||||
|     qemu_mutex_lock(&s->mutex); |     qemu_mutex_lock(&s->mutex); | ||||||
|     curl_clean_state(state); |     curl_clean_state(state); | ||||||
| @@ -899,7 +906,8 @@ static void curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb) | |||||||
|     state->acb[0] = acb; |     state->acb[0] = acb; | ||||||
|  |  | ||||||
|     snprintf(state->range, 127, "%" PRIu64 "-%" PRIu64, start, end); |     snprintf(state->range, 127, "%" PRIu64 "-%" PRIu64, start, end); | ||||||
|     trace_curl_setup_preadv(acb->bytes, start, state->range); |     DPRINTF("CURL (AIO): Reading %" PRIu64 " at %" PRIu64 " (%s)\n", | ||||||
|  |             acb->bytes, start, state->range); | ||||||
|     curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range); |     curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range); | ||||||
|  |  | ||||||
|     curl_multi_add_handle(s->multi, state->curl); |     curl_multi_add_handle(s->multi, state->curl); | ||||||
| @@ -933,7 +941,7 @@ static void curl_close(BlockDriverState *bs) | |||||||
| { | { | ||||||
|     BDRVCURLState *s = bs->opaque; |     BDRVCURLState *s = bs->opaque; | ||||||
|  |  | ||||||
|     trace_curl_close(); |     DPRINTF("CURL: Close\n"); | ||||||
|     curl_detach_aio_context(bs); |     curl_detach_aio_context(bs); | ||||||
|     qemu_mutex_destroy(&s->mutex); |     qemu_mutex_destroy(&s->mutex); | ||||||
|  |  | ||||||
| @@ -950,36 +958,6 @@ static int64_t curl_getlength(BlockDriverState *bs) | |||||||
|     return s->len; |     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 = { | static BlockDriver bdrv_http = { | ||||||
|     .format_name                = "http", |     .format_name                = "http", | ||||||
|     .protocol_name              = "http", |     .protocol_name              = "http", | ||||||
| @@ -994,9 +972,6 @@ static BlockDriver bdrv_http = { | |||||||
|  |  | ||||||
|     .bdrv_detach_aio_context    = curl_detach_aio_context, |     .bdrv_detach_aio_context    = curl_detach_aio_context, | ||||||
|     .bdrv_attach_aio_context    = curl_attach_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 = { | static BlockDriver bdrv_https = { | ||||||
| @@ -1013,9 +988,6 @@ static BlockDriver bdrv_https = { | |||||||
|  |  | ||||||
|     .bdrv_detach_aio_context    = curl_detach_aio_context, |     .bdrv_detach_aio_context    = curl_detach_aio_context, | ||||||
|     .bdrv_attach_aio_context    = curl_attach_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 = { | static BlockDriver bdrv_ftp = { | ||||||
| @@ -1032,9 +1004,6 @@ static BlockDriver bdrv_ftp = { | |||||||
|  |  | ||||||
|     .bdrv_detach_aio_context    = curl_detach_aio_context, |     .bdrv_detach_aio_context    = curl_detach_aio_context, | ||||||
|     .bdrv_attach_aio_context    = curl_attach_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 = { | static BlockDriver bdrv_ftps = { | ||||||
| @@ -1051,9 +1020,6 @@ static BlockDriver bdrv_ftps = { | |||||||
|  |  | ||||||
|     .bdrv_detach_aio_context    = curl_detach_aio_context, |     .bdrv_detach_aio_context    = curl_detach_aio_context, | ||||||
|     .bdrv_attach_aio_context    = curl_attach_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) | static void curl_block_init(void) | ||||||
|   | |||||||
| @@ -28,12 +28,19 @@ | |||||||
| #include "block/block_int.h" | #include "block/block_int.h" | ||||||
| #include "block/blockjob.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 { | struct BdrvDirtyBitmap { | ||||||
|     QemuMutex *mutex; |     QemuMutex *mutex; | ||||||
|     HBitmap *bitmap;            /* Dirty bitmap implementation */ |     HBitmap *bitmap;            /* Dirty bitmap implementation */ | ||||||
|     HBitmap *meta;              /* Meta dirty bitmap */ |     HBitmap *meta;              /* Meta dirty bitmap */ | ||||||
|     bool busy;                  /* Bitmap is busy, it can't be used via QMP */ |     BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */ | ||||||
|     BdrvDirtyBitmap *successor; /* Anonymous child, if any. */ |  | ||||||
|     char *name;                 /* Optional non-empty unique ID */ |     char *name;                 /* Optional non-empty unique ID */ | ||||||
|     int64_t size;               /* Size of the bitmap, in bytes */ |     int64_t size;               /* Size of the bitmap, in bytes */ | ||||||
|     bool disabled;              /* Bitmap is disabled. It ignores all writes to |     bool disabled;              /* Bitmap is disabled. It ignores all writes to | ||||||
| @@ -46,13 +53,6 @@ struct BdrvDirtyBitmap { | |||||||
|                                    and this bitmap must remain unchanged while |                                    and this bitmap must remain unchanged while | ||||||
|                                    this flag is set. */ |                                    this flag is set. */ | ||||||
|     bool persistent;            /* bitmap must be saved to owner disk image */ |     bool persistent;            /* bitmap must be saved to owner disk image */ | ||||||
|     bool 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; |     QLIST_ENTRY(BdrvDirtyBitmap) list; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -95,6 +95,15 @@ BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name) | |||||||
|     return NULL; |     return NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Called with BQL taken.  */ | ||||||
|  | void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap) | ||||||
|  | { | ||||||
|  |     assert(!bdrv_dirty_bitmap_frozen(bitmap)); | ||||||
|  |     g_free(bitmap->name); | ||||||
|  |     bitmap->name = NULL; | ||||||
|  |     bitmap->persistent = false; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* Called with BQL taken.  */ | /* Called with BQL taken.  */ | ||||||
| BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, | BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, | ||||||
|                                           uint32_t granularity, |                                           uint32_t granularity, | ||||||
| @@ -169,59 +178,22 @@ const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap) | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Called with BQL taken.  */ | /* Called with BQL taken.  */ | ||||||
| bool bdrv_dirty_bitmap_has_successor(BdrvDirtyBitmap *bitmap) | bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap) | ||||||
| { | { | ||||||
|     return bitmap->successor; |     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.  */ | /* Called with BQL taken.  */ | ||||||
| bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap) | bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap) | ||||||
| { | { | ||||||
|     return !bitmap->disabled; |     return !(bitmap->disabled || bitmap->successor); | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /* Called with BQL taken.  */ | ||||||
|  * 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. |  | ||||||
|  */ |  | ||||||
| DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap) | DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap) | ||||||
| { | { | ||||||
|     if (bdrv_dirty_bitmap_inconsistent(bitmap)) { |     if (bdrv_dirty_bitmap_frozen(bitmap)) { | ||||||
|         return DIRTY_BITMAP_STATUS_INCONSISTENT; |  | ||||||
|     } else if (bdrv_dirty_bitmap_has_successor(bitmap)) { |  | ||||||
|         return DIRTY_BITMAP_STATUS_FROZEN; |         return DIRTY_BITMAP_STATUS_FROZEN; | ||||||
|     } else if (bdrv_dirty_bitmap_busy(bitmap)) { |  | ||||||
|         return DIRTY_BITMAP_STATUS_LOCKED; |  | ||||||
|     } else if (!bdrv_dirty_bitmap_enabled(bitmap)) { |     } else if (!bdrv_dirty_bitmap_enabled(bitmap)) { | ||||||
|         return DIRTY_BITMAP_STATUS_DISABLED; |         return DIRTY_BITMAP_STATUS_DISABLED; | ||||||
|     } else { |     } else { | ||||||
| @@ -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. |  * Create a successor bitmap destined to replace this bitmap after an operation. | ||||||
|  * Requires that the bitmap is not marked busy and has no successor. |  * Requires that the bitmap is not frozen and has no successor. | ||||||
|  * The successor will be enabled if the parent bitmap was. |  | ||||||
|  * Called with BQL taken. |  * Called with BQL taken. | ||||||
|  */ |  */ | ||||||
| int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, | int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, | ||||||
| @@ -275,14 +212,12 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, | |||||||
|     uint64_t granularity; |     uint64_t granularity; | ||||||
|     BdrvDirtyBitmap *child; |     BdrvDirtyBitmap *child; | ||||||
|  |  | ||||||
|     if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) { |     if (bdrv_dirty_bitmap_frozen(bitmap)) { | ||||||
|         return -1; |         error_setg(errp, "Cannot create a successor for a bitmap that is " | ||||||
|     } |                    "currently frozen"); | ||||||
|     if (bdrv_dirty_bitmap_has_successor(bitmap)) { |  | ||||||
|         error_setg(errp, "Cannot create a successor for a bitmap that already " |  | ||||||
|                    "has one"); |  | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |     assert(!bitmap->successor); | ||||||
|  |  | ||||||
|     /* Create an anonymous successor */ |     /* Create an anonymous successor */ | ||||||
|     granularity = bdrv_dirty_bitmap_granularity(bitmap); |     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. */ |     /* Successor will be on or off based on our current state. */ | ||||||
|     child->disabled = bitmap->disabled; |     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->successor = child; | ||||||
|     bitmap->busy = true; |  | ||||||
|     return 0; |     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, |  * For a bitmap with a successor, yield our name to the successor, | ||||||
|  * delete the old bitmap, and return a handle to the new bitmap. |  * delete the old bitmap, and return a handle to the new bitmap. | ||||||
| @@ -352,7 +258,6 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, | |||||||
|     bitmap->successor = NULL; |     bitmap->successor = NULL; | ||||||
|     successor->persistent = bitmap->persistent; |     successor->persistent = bitmap->persistent; | ||||||
|     bitmap->persistent = false; |     bitmap->persistent = false; | ||||||
|     bitmap->busy = false; |  | ||||||
|     bdrv_release_dirty_bitmap(bs, bitmap); |     bdrv_release_dirty_bitmap(bs, bitmap); | ||||||
|  |  | ||||||
|     return successor; |     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, |  * In cases of failure where we can no longer safely delete the parent, | ||||||
|  * we may wish to re-join the parent and child/successor. |  * we may wish to re-join the parent and child/successor. | ||||||
|  * The merged parent will be marked as not busy. |  * The merged parent will be un-frozen, but not explicitly re-enabled. | ||||||
|  * The marged parent will be enabled if and only if the successor was enabled. |  * Called with BQL taken. | ||||||
|  * Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken. |  | ||||||
|  */ |  */ | ||||||
| BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, | BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, | ||||||
|                                                   BdrvDirtyBitmap *parent, |                                            BdrvDirtyBitmap *parent, | ||||||
|                                                   Error **errp) |                                            Error **errp) | ||||||
| { | { | ||||||
|     BdrvDirtyBitmap *successor = parent->successor; |     BdrvDirtyBitmap *successor = parent->successor; | ||||||
|  |  | ||||||
| @@ -376,33 +280,16 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, | |||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap)) { |     if (!hbitmap_merge(parent->bitmap, successor->bitmap)) { | ||||||
|         error_setg(errp, "Merging of parent and successor bitmap failed"); |         error_setg(errp, "Merging of parent and successor bitmap failed"); | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |     bdrv_release_dirty_bitmap(bs, successor); | ||||||
|     parent->disabled = successor->disabled; |  | ||||||
|     parent->busy = false; |  | ||||||
|     bdrv_release_dirty_bitmap_locked(successor); |  | ||||||
|     parent->successor = NULL; |     parent->successor = NULL; | ||||||
|  |  | ||||||
|     return parent; |     return parent; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Called with BQL taken. */ |  | ||||||
| BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, |  | ||||||
|                                            BdrvDirtyBitmap *parent, |  | ||||||
|                                            Error **errp) |  | ||||||
| { |  | ||||||
|     BdrvDirtyBitmap *ret; |  | ||||||
|  |  | ||||||
|     qemu_mutex_lock(parent->mutex); |  | ||||||
|     ret = bdrv_reclaim_dirty_bitmap_locked(bs, parent, errp); |  | ||||||
|     qemu_mutex_unlock(parent->mutex); |  | ||||||
|  |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Truncates _all_ bitmaps attached to a BDS. |  * Truncates _all_ bitmaps attached to a BDS. | ||||||
|  * Called with BQL taken. |  * Called with BQL taken. | ||||||
| @@ -413,8 +300,7 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes) | |||||||
|  |  | ||||||
|     bdrv_dirty_bitmaps_lock(bs); |     bdrv_dirty_bitmaps_lock(bs); | ||||||
|     QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { |     QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { | ||||||
|         assert(!bdrv_dirty_bitmap_busy(bitmap)); |         assert(!bdrv_dirty_bitmap_frozen(bitmap)); | ||||||
|         assert(!bdrv_dirty_bitmap_has_successor(bitmap)); |  | ||||||
|         assert(!bitmap->active_iterators); |         assert(!bitmap->active_iterators); | ||||||
|         hbitmap_truncate(bitmap->bitmap, bytes); |         hbitmap_truncate(bitmap->bitmap, bytes); | ||||||
|         bitmap->size = bytes; |         bitmap->size = bytes; | ||||||
| @@ -422,31 +308,68 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes) | |||||||
|     bdrv_dirty_bitmaps_unlock(bs); |     bdrv_dirty_bitmaps_unlock(bs); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static bool bdrv_dirty_bitmap_has_name(BdrvDirtyBitmap *bitmap) | ||||||
|  | { | ||||||
|  |     return !!bdrv_dirty_bitmap_name(bitmap); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Called with BQL taken.  */ | ||||||
|  | static void bdrv_do_release_matching_dirty_bitmap( | ||||||
|  |     BlockDriverState *bs, BdrvDirtyBitmap *bitmap, | ||||||
|  |     bool (*cond)(BdrvDirtyBitmap *bitmap)) | ||||||
|  | { | ||||||
|  |     BdrvDirtyBitmap *bm, *next; | ||||||
|  |     bdrv_dirty_bitmaps_lock(bs); | ||||||
|  |     QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { | ||||||
|  |         if ((!bitmap || bm == bitmap) && (!cond || cond(bm))) { | ||||||
|  |             assert(!bm->active_iterators); | ||||||
|  |             assert(!bdrv_dirty_bitmap_frozen(bm)); | ||||||
|  |             assert(!bm->meta); | ||||||
|  |             QLIST_REMOVE(bm, list); | ||||||
|  |             hbitmap_free(bm->bitmap); | ||||||
|  |             g_free(bm->name); | ||||||
|  |             g_free(bm); | ||||||
|  |  | ||||||
|  |             if (bitmap) { | ||||||
|  |                 goto out; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (bitmap) { | ||||||
|  |         abort(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | out: | ||||||
|  |     bdrv_dirty_bitmaps_unlock(bs); | ||||||
|  | } | ||||||
|  |  | ||||||
| /* Called with BQL taken.  */ | /* Called with BQL taken.  */ | ||||||
| void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) | void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) | ||||||
| { | { | ||||||
|     bdrv_dirty_bitmaps_lock(bs); |     bdrv_do_release_matching_dirty_bitmap(bs, bitmap, NULL); | ||||||
|     bdrv_release_dirty_bitmap_locked(bitmap); |  | ||||||
|     bdrv_dirty_bitmaps_unlock(bs); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()). |  * 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. |  * This function does not remove persistent bitmaps from the storage. | ||||||
|  * Called with BQL taken. |  * Called with BQL taken. | ||||||
|  */ |  */ | ||||||
| void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) | void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     BdrvDirtyBitmap *bm, *next; |     bdrv_do_release_matching_dirty_bitmap(bs, NULL, bdrv_dirty_bitmap_has_name); | ||||||
|  | } | ||||||
|  |  | ||||||
|     bdrv_dirty_bitmaps_lock(bs); | /** | ||||||
|     QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { |  * Release all persistent dirty bitmaps attached to a BDS (for use in | ||||||
|         if (bdrv_dirty_bitmap_name(bm)) { |  * bdrv_inactivate_recurse()). | ||||||
|             bdrv_release_dirty_bitmap_locked(bm); |  * There must not be any frozen bitmaps attached. | ||||||
|         } |  * This function does not remove persistent bitmaps from the storage. | ||||||
|     } |  */ | ||||||
|     bdrv_dirty_bitmaps_unlock(bs); | void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs) | ||||||
|  | { | ||||||
|  |     bdrv_do_release_matching_dirty_bitmap(bs, NULL, | ||||||
|  |                                           bdrv_dirty_bitmap_get_persistance); | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -466,18 +389,18 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Called with BQL taken.  */ | ||||||
| void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) | void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) | ||||||
| { | { | ||||||
|     bdrv_dirty_bitmap_lock(bitmap); |     assert(!bdrv_dirty_bitmap_frozen(bitmap)); | ||||||
|     bitmap->disabled = true; |     bitmap->disabled = true; | ||||||
|     bdrv_dirty_bitmap_unlock(bitmap); |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Called with BQL taken.  */ | ||||||
| void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap) | void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap) | ||||||
| { | { | ||||||
|     bdrv_dirty_bitmap_lock(bitmap); |     assert(!bdrv_dirty_bitmap_frozen(bitmap)); | ||||||
|     bdrv_enable_dirty_bitmap_locked(bitmap); |     bitmap->disabled = false; | ||||||
|     bdrv_dirty_bitmap_unlock(bitmap); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) | BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) | ||||||
| @@ -495,11 +418,6 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) | |||||||
|         info->has_name = !!bm->name; |         info->has_name = !!bm->name; | ||||||
|         info->name = g_strdup(bm->name); |         info->name = g_strdup(bm->name); | ||||||
|         info->status = bdrv_dirty_bitmap_status(bm); |         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; |         entry->value = info; | ||||||
|         *plist = entry; |         *plist = entry; | ||||||
|         plist = &entry->next; |         plist = &entry->next; | ||||||
| @@ -582,6 +500,7 @@ int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) | |||||||
| void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, | void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, | ||||||
|                                   int64_t offset, int64_t bytes) |                                   int64_t offset, int64_t bytes) | ||||||
| { | { | ||||||
|  |     assert(bdrv_dirty_bitmap_enabled(bitmap)); | ||||||
|     assert(!bdrv_dirty_bitmap_readonly(bitmap)); |     assert(!bdrv_dirty_bitmap_readonly(bitmap)); | ||||||
|     hbitmap_set(bitmap->bitmap, offset, bytes); |     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, | void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, | ||||||
|                                     int64_t offset, int64_t bytes) |                                     int64_t offset, int64_t bytes) | ||||||
| { | { | ||||||
|  |     assert(bdrv_dirty_bitmap_enabled(bitmap)); | ||||||
|     assert(!bdrv_dirty_bitmap_readonly(bitmap)); |     assert(!bdrv_dirty_bitmap_readonly(bitmap)); | ||||||
|     hbitmap_reset(bitmap->bitmap, offset, bytes); |     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) | void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out) | ||||||
| { | { | ||||||
|  |     assert(bdrv_dirty_bitmap_enabled(bitmap)); | ||||||
|     assert(!bdrv_dirty_bitmap_readonly(bitmap)); |     assert(!bdrv_dirty_bitmap_readonly(bitmap)); | ||||||
|     bdrv_dirty_bitmap_lock(bitmap); |     bdrv_dirty_bitmap_lock(bitmap); | ||||||
|     if (!out) { |     if (!out) { | ||||||
| @@ -625,11 +546,12 @@ void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out) | |||||||
|     bdrv_dirty_bitmap_unlock(bitmap); |     bdrv_dirty_bitmap_unlock(bitmap); | ||||||
| } | } | ||||||
|  |  | ||||||
| void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup) | void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in) | ||||||
| { | { | ||||||
|     HBitmap *tmp = bitmap->bitmap; |     HBitmap *tmp = bitmap->bitmap; | ||||||
|  |     assert(bdrv_dirty_bitmap_enabled(bitmap)); | ||||||
|     assert(!bdrv_dirty_bitmap_readonly(bitmap)); |     assert(!bdrv_dirty_bitmap_readonly(bitmap)); | ||||||
|     bitmap->bitmap = backup; |     bitmap->bitmap = in; | ||||||
|     hbitmap_free(tmp); |     hbitmap_free(tmp); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -740,46 +662,23 @@ bool bdrv_has_readonly_bitmaps(BlockDriverState *bs) | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Called with BQL taken. */ | /* 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); |     qemu_mutex_lock(bitmap->mutex); | ||||||
|     bitmap->persistent = persistent; |     bitmap->persistent = persistent; | ||||||
|     qemu_mutex_unlock(bitmap->mutex); |     qemu_mutex_unlock(bitmap->mutex); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Called with BQL taken. */ | bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap) | ||||||
| void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap) |  | ||||||
| { | { | ||||||
|     qemu_mutex_lock(bitmap->mutex); |     return bitmap->persistent; | ||||||
|     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; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs) | bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs) | ||||||
| { | { | ||||||
|     BdrvDirtyBitmap *bm; |     BdrvDirtyBitmap *bm; | ||||||
|     QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { |     QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { | ||||||
|         if (bm->persistent && !bm->readonly && !bm->migration) { |         if (bm->persistent && !bm->readonly) { | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -799,50 +698,7 @@ char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp) | |||||||
|     return hbitmap_sha256(bitmap->bitmap, errp); |     return hbitmap_sha256(bitmap->bitmap, errp); | ||||||
| } | } | ||||||
|  |  | ||||||
| int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset, | int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset) | ||||||
|                                     uint64_t bytes) |  | ||||||
| { | { | ||||||
|     return hbitmap_next_zero(bitmap->bitmap, offset, bytes); |     return hbitmap_next_zero(bitmap->bitmap, offset); | ||||||
| } |  | ||||||
|  |  | ||||||
| bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap, |  | ||||||
|                                        uint64_t *offset, uint64_t *bytes) |  | ||||||
| { |  | ||||||
|     return hbitmap_next_dirty_area(bitmap->bitmap, offset, bytes); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, |  | ||||||
|                              HBitmap **backup, Error **errp) |  | ||||||
| { |  | ||||||
|     bool ret; |  | ||||||
|  |  | ||||||
|     /* only bitmaps from one bds are supported */ |  | ||||||
|     assert(dest->mutex == src->mutex); |  | ||||||
|  |  | ||||||
|     qemu_mutex_lock(dest->mutex); |  | ||||||
|  |  | ||||||
|     if (bdrv_dirty_bitmap_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); |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,49 +0,0 @@ | |||||||
| /* |  | ||||||
|  * DMG lzfse uncompression |  | ||||||
|  * |  | ||||||
|  * Copyright (c) 2018 Julio Cesar Faracco |  | ||||||
|  * |  | ||||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
|  * of this software and associated documentation files (the "Software"), to deal |  | ||||||
|  * in the Software without restriction, including without limitation the rights |  | ||||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
|  * copies of the Software, and to permit persons to whom the Software is |  | ||||||
|  * furnished to do so, subject to the following conditions: |  | ||||||
|  * |  | ||||||
|  * The above copyright notice and this permission notice shall be included in |  | ||||||
|  * all copies or substantial portions of the Software. |  | ||||||
|  * |  | ||||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |  | ||||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  | ||||||
|  * THE SOFTWARE. |  | ||||||
|  */ |  | ||||||
| #include "qemu/osdep.h" |  | ||||||
| #include "qemu-common.h" |  | ||||||
| #include "dmg.h" |  | ||||||
| #include <lzfse.h> |  | ||||||
|  |  | ||||||
| static int dmg_uncompress_lzfse_do(char *next_in, unsigned int avail_in, |  | ||||||
|                                    char *next_out, unsigned int avail_out) |  | ||||||
| { |  | ||||||
|     size_t out_size = lzfse_decode_buffer((uint8_t *) next_out, avail_out, |  | ||||||
|                                           (uint8_t *) next_in, avail_in, |  | ||||||
|                                           NULL); |  | ||||||
|  |  | ||||||
|     /* We need to decode the single chunk only. */ |  | ||||||
|     /* So, out_size == avail_out is not an error here. */ |  | ||||||
|     if (out_size > 0) { |  | ||||||
|         return out_size; |  | ||||||
|     } |  | ||||||
|     return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| __attribute__((constructor)) |  | ||||||
| static void dmg_lzfse_init(void) |  | ||||||
| { |  | ||||||
|     assert(!dmg_uncompress_lzfse); |  | ||||||
|     dmg_uncompress_lzfse = dmg_uncompress_lzfse_do; |  | ||||||
| } |  | ||||||
							
								
								
									
										104
									
								
								block/dmg.c
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								block/dmg.c
									
									
									
									
									
								
							| @@ -33,9 +33,6 @@ | |||||||
| int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in, | int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in, | ||||||
|                           char *next_out, unsigned int avail_out); |                           char *next_out, unsigned int avail_out); | ||||||
|  |  | ||||||
| int (*dmg_uncompress_lzfse)(char *next_in, unsigned int avail_in, |  | ||||||
|                             char *next_out, unsigned int avail_out); |  | ||||||
|  |  | ||||||
| enum { | enum { | ||||||
|     /* Limit chunk sizes to prevent unreasonable amounts of memory being used |     /* Limit chunk sizes to prevent unreasonable amounts of memory being used | ||||||
|      * or truncating when converting to 32-bit types |      * or truncating when converting to 32-bit types | ||||||
| @@ -44,19 +41,6 @@ enum { | |||||||
|     DMG_SECTORCOUNTS_MAX = DMG_LENGTHS_MAX / 512, |     DMG_SECTORCOUNTS_MAX = DMG_LENGTHS_MAX / 512, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| enum { |  | ||||||
|     /* DMG Block Type */ |  | ||||||
|     UDZE = 0, /* Zeroes */ |  | ||||||
|     UDRW,     /* RAW type */ |  | ||||||
|     UDIG,     /* Ignore */ |  | ||||||
|     UDCO = 0x80000004, |  | ||||||
|     UDZO, |  | ||||||
|     UDBZ, |  | ||||||
|     ULFO, |  | ||||||
|     UDCM = 0x7ffffffe, /* Comments */ |  | ||||||
|     UDLE = 0xffffffff  /* Last Entry */ |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename) | static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||||
| { | { | ||||||
|     int len; |     int len; | ||||||
| @@ -121,17 +105,15 @@ static void update_max_chunk_size(BDRVDMGState *s, uint32_t chunk, | |||||||
|     uint32_t uncompressed_sectors = 0; |     uint32_t uncompressed_sectors = 0; | ||||||
|  |  | ||||||
|     switch (s->types[chunk]) { |     switch (s->types[chunk]) { | ||||||
|     case UDZO: /* zlib compressed */ |     case 0x80000005: /* zlib compressed */ | ||||||
|     case UDBZ: /* bzip2 compressed */ |     case 0x80000006: /* bzip2 compressed */ | ||||||
|     case ULFO: /* lzfse compressed */ |  | ||||||
|         compressed_size = s->lengths[chunk]; |         compressed_size = s->lengths[chunk]; | ||||||
|         uncompressed_sectors = s->sectorcounts[chunk]; |         uncompressed_sectors = s->sectorcounts[chunk]; | ||||||
|         break; |         break; | ||||||
|     case UDRW: /* copy */ |     case 1: /* copy */ | ||||||
|         uncompressed_sectors = DIV_ROUND_UP(s->lengths[chunk], 512); |         uncompressed_sectors = DIV_ROUND_UP(s->lengths[chunk], 512); | ||||||
|         break; |         break; | ||||||
|     case UDZE: /* zero */ |     case 2: /* zero */ | ||||||
|     case UDIG: /* ignore */ |  | ||||||
|         /* as the all-zeroes block may be large, it is treated specially: the |         /* as the all-zeroes block may be large, it is treated specially: the | ||||||
|          * sector is not copied from a large buffer, a simple memset is used |          * sector is not copied from a large buffer, a simple memset is used | ||||||
|          * instead. Therefore uncompressed_sectors does not need to be set. */ |          * instead. Therefore uncompressed_sectors does not need to be set. */ | ||||||
| @@ -200,15 +182,12 @@ typedef struct DmgHeaderState { | |||||||
| static bool dmg_is_known_block_type(uint32_t entry_type) | static bool dmg_is_known_block_type(uint32_t entry_type) | ||||||
| { | { | ||||||
|     switch (entry_type) { |     switch (entry_type) { | ||||||
|     case UDZE:    /* zeros */ |     case 0x00000001:    /* uncompressed */ | ||||||
|     case UDRW:    /* uncompressed */ |     case 0x00000002:    /* zeroes */ | ||||||
|     case UDIG:    /* ignore */ |     case 0x80000005:    /* zlib */ | ||||||
|     case UDZO:    /* zlib */ |  | ||||||
|         return true; |         return true; | ||||||
|     case UDBZ:    /* bzip2 */ |     case 0x80000006:    /* bzip2 */ | ||||||
|         return !!dmg_uncompress_bz2; |         return !!dmg_uncompress_bz2; | ||||||
|     case ULFO:    /* lzfse */ |  | ||||||
|         return !!dmg_uncompress_lzfse; |  | ||||||
|     default: |     default: | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| @@ -267,10 +246,9 @@ static int dmg_read_mish_block(BDRVDMGState *s, DmgHeaderState *ds, | |||||||
|         /* sector count */ |         /* sector count */ | ||||||
|         s->sectorcounts[i] = buff_read_uint64(buffer, offset + 0x10); |         s->sectorcounts[i] = buff_read_uint64(buffer, offset + 0x10); | ||||||
|  |  | ||||||
|         /* all-zeroes sector (type UDZE and UDIG) does not need to be |         /* all-zeroes sector (type 2) does not need to be "uncompressed" and can | ||||||
|          * "uncompressed" and can therefore be unbounded. */ |          * therefore be unbounded. */ | ||||||
|         if (s->types[i] != UDZE && s->types[i] != UDIG |         if (s->types[i] != 2 && s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) { | ||||||
|             && s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) { |  | ||||||
|             error_report("sector count %" PRIu64 " for chunk %" PRIu32 |             error_report("sector count %" PRIu64 " for chunk %" PRIu32 | ||||||
|                          " is larger than max (%u)", |                          " is larger than max (%u)", | ||||||
|                          s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX); |                          s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX); | ||||||
| @@ -435,19 +413,24 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     int64_t offset; |     int64_t offset; | ||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     ret = bdrv_apply_auto_read_only(bs, NULL, errp); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, |     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, | ||||||
|                                false, errp); |                                false, errp); | ||||||
|     if (!bs->file) { |     if (!bs->file) { | ||||||
|         return -EINVAL; |         return -EINVAL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (!bdrv_is_read_only(bs)) { | ||||||
|  |         error_report("Opening dmg images without an explicit read-only=on " | ||||||
|  |                      "option is deprecated. Future versions will refuse to " | ||||||
|  |                      "open the image instead of automatically marking the " | ||||||
|  |                      "image read-only."); | ||||||
|  |         ret = bdrv_set_read_only(bs, true, errp); | ||||||
|  |         if (ret < 0) { | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     block_module_load_one("dmg-bz2"); |     block_module_load_one("dmg-bz2"); | ||||||
|     block_module_load_one("dmg-lzfse"); |  | ||||||
|  |  | ||||||
|     s->n_chunks = 0; |     s->n_chunks = 0; | ||||||
|     s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; |     s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; | ||||||
| @@ -575,20 +558,16 @@ static inline uint32_t search_chunk(BDRVDMGState *s, uint64_t sector_num) | |||||||
| { | { | ||||||
|     /* binary search */ |     /* binary search */ | ||||||
|     uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3; |     uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3; | ||||||
|     while (chunk1 <= chunk2) { |     while (chunk1 != chunk2) { | ||||||
|         chunk3 = (chunk1 + chunk2) / 2; |         chunk3 = (chunk1 + chunk2) / 2; | ||||||
|         if (s->sectors[chunk3] > sector_num) { |         if (s->sectors[chunk3] > sector_num) { | ||||||
|             if (chunk3 == 0) { |             chunk2 = chunk3; | ||||||
|                 goto err; |  | ||||||
|             } |  | ||||||
|             chunk2 = chunk3 - 1; |  | ||||||
|         } else if (s->sectors[chunk3] + s->sectorcounts[chunk3] > sector_num) { |         } else if (s->sectors[chunk3] + s->sectorcounts[chunk3] > sector_num) { | ||||||
|             return chunk3; |             return chunk3; | ||||||
|         } else { |         } else { | ||||||
|             chunk1 = chunk3 + 1; |             chunk1 = chunk3; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| err: |  | ||||||
|     return s->n_chunks; /* error */ |     return s->n_chunks; /* error */ | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -606,7 +585,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) | |||||||
|  |  | ||||||
|         s->current_chunk = s->n_chunks; |         s->current_chunk = s->n_chunks; | ||||||
|         switch (s->types[chunk]) { /* block entry type */ |         switch (s->types[chunk]) { /* block entry type */ | ||||||
|         case UDZO: { /* zlib compressed */ |         case 0x80000005: { /* zlib compressed */ | ||||||
|             /* we need to buffer, because only the chunk as whole can be |             /* we need to buffer, because only the chunk as whole can be | ||||||
|              * inflated. */ |              * inflated. */ | ||||||
|             ret = bdrv_pread(bs->file, s->offsets[chunk], |             ret = bdrv_pread(bs->file, s->offsets[chunk], | ||||||
| @@ -629,7 +608,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) | |||||||
|                 return -1; |                 return -1; | ||||||
|             } |             } | ||||||
|             break; } |             break; } | ||||||
|         case UDBZ: /* bzip2 compressed */ |         case 0x80000006: /* bzip2 compressed */ | ||||||
|             if (!dmg_uncompress_bz2) { |             if (!dmg_uncompress_bz2) { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
| @@ -650,36 +629,14 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) | |||||||
|                 return ret; |                 return ret; | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case ULFO: |         case 1: /* copy */ | ||||||
|             if (!dmg_uncompress_lzfse) { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             /* we need to buffer, because only the chunk as whole can be |  | ||||||
|              * inflated. */ |  | ||||||
|             ret = bdrv_pread(bs->file, s->offsets[chunk], |  | ||||||
|                              s->compressed_chunk, s->lengths[chunk]); |  | ||||||
|             if (ret != s->lengths[chunk]) { |  | ||||||
|                 return -1; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             ret = dmg_uncompress_lzfse((char *)s->compressed_chunk, |  | ||||||
|                                        (unsigned int) s->lengths[chunk], |  | ||||||
|                                        (char *)s->uncompressed_chunk, |  | ||||||
|                                        (unsigned int) |  | ||||||
|                                            (512 * s->sectorcounts[chunk])); |  | ||||||
|             if (ret < 0) { |  | ||||||
|                 return ret; |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         case UDRW: /* copy */ |  | ||||||
|             ret = bdrv_pread(bs->file, s->offsets[chunk], |             ret = bdrv_pread(bs->file, s->offsets[chunk], | ||||||
|                              s->uncompressed_chunk, s->lengths[chunk]); |                              s->uncompressed_chunk, s->lengths[chunk]); | ||||||
|             if (ret != s->lengths[chunk]) { |             if (ret != s->lengths[chunk]) { | ||||||
|                 return -1; |                 return -1; | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case UDZE: /* zeros */ |         case 2: /* zero */ | ||||||
|         case UDIG: /* ignore */ |  | ||||||
|             /* see dmg_read, it is treated specially. No buffer needs to be |             /* see dmg_read, it is treated specially. No buffer needs to be | ||||||
|              * pre-filled, the zeroes can be set directly. */ |              * pre-filled, the zeroes can be set directly. */ | ||||||
|             break; |             break; | ||||||
| @@ -714,8 +671,7 @@ dmg_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, | |||||||
|         /* Special case: current chunk is all zeroes. Do not perform a memcpy as |         /* Special case: current chunk is all zeroes. Do not perform a memcpy as | ||||||
|          * s->uncompressed_chunk may be too small to cover the large all-zeroes |          * s->uncompressed_chunk may be too small to cover the large all-zeroes | ||||||
|          * section. dmg_read_chunk is called to find s->current_chunk */ |          * section. dmg_read_chunk is called to find s->current_chunk */ | ||||||
|         if (s->types[s->current_chunk] == UDZE |         if (s->types[s->current_chunk] == 2) { /* all zeroes block entry */ | ||||||
|             || s->types[s->current_chunk] == UDIG) { /* all zeroes block entry */ |  | ||||||
|             qemu_iovec_memset(qiov, i * 512, 0, 512); |             qemu_iovec_memset(qiov, i * 512, 0, 512); | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -55,7 +55,4 @@ typedef struct BDRVDMGState { | |||||||
| extern int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in, | extern int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in, | ||||||
|                                  char *next_out, unsigned int avail_out); |                                  char *next_out, unsigned int avail_out); | ||||||
|  |  | ||||||
| extern int (*dmg_uncompress_lzfse)(char *next_in, unsigned int avail_in, |  | ||||||
|                                    char *next_out, unsigned int avail_out); |  | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
							
								
								
									
										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_nbytes = count; | ||||||
|     acb->aio_offset = offset; |     acb->aio_offset = offset; | ||||||
|  |  | ||||||
|     trace_file_paio_submit(acb, opaque, offset, count, type); |     trace_paio_submit(acb, opaque, offset, count, type); | ||||||
|     pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); |     pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); | ||||||
|     return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); |     return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); | ||||||
| } | } | ||||||
| @@ -176,7 +176,7 @@ int qemu_ftruncate64(int fd, int64_t length) | |||||||
|     BOOL res; |     BOOL res; | ||||||
|  |  | ||||||
|     if ((GetVersion() & 0x80000000UL) && (length >> 32) != 0) |     if ((GetVersion() & 0x80000000UL) && (length >> 32) != 0) | ||||||
|         return -1; | 	return -1; | ||||||
|  |  | ||||||
|     h = (HANDLE)_get_osfhandle(fd); |     h = (HANDLE)_get_osfhandle(fd); | ||||||
|  |  | ||||||
| @@ -184,13 +184,13 @@ int qemu_ftruncate64(int fd, int64_t length) | |||||||
|     li.HighPart = 0; |     li.HighPart = 0; | ||||||
|     li.LowPart = SetFilePointer (h, 0, &li.HighPart, FILE_CURRENT); |     li.LowPart = SetFilePointer (h, 0, &li.HighPart, FILE_CURRENT); | ||||||
|     if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { |     if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { | ||||||
|         return -1; | 	return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     high = length >> 32; |     high = length >> 32; | ||||||
|     dw = SetFilePointer(h, (DWORD) length, &high, FILE_BEGIN); |     dw = SetFilePointer(h, (DWORD) length, &high, FILE_BEGIN); | ||||||
|     if (dw == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { |     if (dw == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { | ||||||
|         return -1; | 	return -1; | ||||||
|     } |     } | ||||||
|     res = SetEndOfFile(h); |     res = SetEndOfFile(h); | ||||||
|  |  | ||||||
| @@ -203,7 +203,7 @@ static int set_sparse(int fd) | |||||||
| { | { | ||||||
|     DWORD returned; |     DWORD returned; | ||||||
|     return (int) DeviceIoControl((HANDLE)_get_osfhandle(fd), FSCTL_SET_SPARSE, |     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) | static void raw_detach_aio_context(BlockDriverState *bs) | ||||||
| @@ -251,11 +251,7 @@ static void raw_probe_alignment(BlockDriverState *bs, Error **errp) | |||||||
|                          &dg.Geometry.BytesPerSector, |                          &dg.Geometry.BytesPerSector, | ||||||
|                          &freeClusters, &totalClusters); |                          &freeClusters, &totalClusters); | ||||||
|         bs->bl.request_alignment = dg.Geometry.BytesPerSector; |         bs->bl.request_alignment = dg.Geometry.BytesPerSector; | ||||||
|         return; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* XXX Does Windows support AIO on less than 512-byte alignment? */ |  | ||||||
|     bs->bl.request_alignment = 512; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void raw_parse_flags(int flags, bool use_aio, int *access_flags, | static void raw_parse_flags(int flags, bool use_aio, int *access_flags, | ||||||
| @@ -414,32 +410,32 @@ fail: | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| static BlockAIOCB *raw_aio_preadv(BlockDriverState *bs, | static BlockAIOCB *raw_aio_readv(BlockDriverState *bs, | ||||||
|                                   uint64_t offset, uint64_t bytes, |                          int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | ||||||
|                                   QEMUIOVector *qiov, int flags, |                          BlockCompletionFunc *cb, void *opaque) | ||||||
|                                   BlockCompletionFunc *cb, void *opaque) |  | ||||||
| { | { | ||||||
|     BDRVRawState *s = bs->opaque; |     BDRVRawState *s = bs->opaque; | ||||||
|     if (s->aio) { |     if (s->aio) { | ||||||
|         return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov, |         return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov, | ||||||
|                                 cb, opaque, QEMU_AIO_READ); |                                 nb_sectors, cb, opaque, QEMU_AIO_READ); | ||||||
|     } else { |     } else { | ||||||
|         return paio_submit(bs, s->hfile, offset, qiov, bytes, |         return paio_submit(bs, s->hfile, sector_num << BDRV_SECTOR_BITS, qiov, | ||||||
|  |                            nb_sectors << BDRV_SECTOR_BITS, | ||||||
|                            cb, opaque, QEMU_AIO_READ); |                            cb, opaque, QEMU_AIO_READ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static BlockAIOCB *raw_aio_pwritev(BlockDriverState *bs, | static BlockAIOCB *raw_aio_writev(BlockDriverState *bs, | ||||||
|                                    uint64_t offset, uint64_t bytes, |                           int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | ||||||
|                                    QEMUIOVector *qiov, int flags, |                           BlockCompletionFunc *cb, void *opaque) | ||||||
|                                    BlockCompletionFunc *cb, void *opaque) |  | ||||||
| { | { | ||||||
|     BDRVRawState *s = bs->opaque; |     BDRVRawState *s = bs->opaque; | ||||||
|     if (s->aio) { |     if (s->aio) { | ||||||
|         return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov, |         return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov, | ||||||
|                                 cb, opaque, QEMU_AIO_WRITE); |                                 nb_sectors, cb, opaque, QEMU_AIO_WRITE); | ||||||
|     } else { |     } else { | ||||||
|         return paio_submit(bs, s->hfile, offset, qiov, bytes, |         return paio_submit(bs, s->hfile, sector_num << BDRV_SECTOR_BITS, qiov, | ||||||
|  |                            nb_sectors << BDRV_SECTOR_BITS, | ||||||
|                            cb, opaque, QEMU_AIO_WRITE); |                            cb, opaque, QEMU_AIO_WRITE); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -467,8 +463,8 @@ static void raw_close(BlockDriverState *bs) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, | static int raw_truncate(BlockDriverState *bs, int64_t offset, | ||||||
|                                         PreallocMode prealloc, Error **errp) |                         PreallocMode prealloc, Error **errp) | ||||||
| { | { | ||||||
|     BDRVRawState *s = bs->opaque; |     BDRVRawState *s = bs->opaque; | ||||||
|     LONG low, high; |     LONG low, high; | ||||||
| @@ -636,11 +632,11 @@ BlockDriver bdrv_file = { | |||||||
|     .bdrv_co_create_opts = raw_co_create_opts, |     .bdrv_co_create_opts = raw_co_create_opts, | ||||||
|     .bdrv_has_zero_init = bdrv_has_zero_init_1, |     .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||||||
|  |  | ||||||
|     .bdrv_aio_preadv    = raw_aio_preadv, |     .bdrv_aio_readv     = raw_aio_readv, | ||||||
|     .bdrv_aio_pwritev   = raw_aio_pwritev, |     .bdrv_aio_writev    = raw_aio_writev, | ||||||
|     .bdrv_aio_flush     = raw_aio_flush, |     .bdrv_aio_flush     = raw_aio_flush, | ||||||
|  |  | ||||||
|     .bdrv_co_truncate   = raw_co_truncate, |     .bdrv_truncate	= raw_truncate, | ||||||
|     .bdrv_getlength	= raw_getlength, |     .bdrv_getlength	= raw_getlength, | ||||||
|     .bdrv_get_allocated_file_size |     .bdrv_get_allocated_file_size | ||||||
|                         = raw_get_allocated_file_size, |                         = raw_get_allocated_file_size, | ||||||
| @@ -712,12 +708,6 @@ static void hdev_parse_filename(const char *filename, QDict *options, | |||||||
|     bdrv_parse_filename_strip_prefix(filename, "host_device:", options); |     bdrv_parse_filename_strip_prefix(filename, "host_device:", options); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void hdev_refresh_limits(BlockDriverState *bs, Error **errp) |  | ||||||
| { |  | ||||||
|     /* XXX Does Windows support AIO on less than 512-byte alignment? */ |  | ||||||
|     bs->bl.request_alignment = 512; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int hdev_open(BlockDriverState *bs, QDict *options, int flags, | static int hdev_open(BlockDriverState *bs, QDict *options, int flags, | ||||||
|                      Error **errp) |                      Error **errp) | ||||||
| { | { | ||||||
| @@ -803,10 +793,9 @@ static BlockDriver bdrv_host_device = { | |||||||
|     .bdrv_probe_device	= hdev_probe_device, |     .bdrv_probe_device	= hdev_probe_device, | ||||||
|     .bdrv_file_open	= hdev_open, |     .bdrv_file_open	= hdev_open, | ||||||
|     .bdrv_close		= raw_close, |     .bdrv_close		= raw_close, | ||||||
|     .bdrv_refresh_limits = hdev_refresh_limits, |  | ||||||
|  |  | ||||||
|     .bdrv_aio_preadv    = raw_aio_preadv, |     .bdrv_aio_readv     = raw_aio_readv, | ||||||
|     .bdrv_aio_pwritev   = raw_aio_pwritev, |     .bdrv_aio_writev    = raw_aio_writev, | ||||||
|     .bdrv_aio_flush     = raw_aio_flush, |     .bdrv_aio_flush     = raw_aio_flush, | ||||||
|  |  | ||||||
|     .bdrv_detach_aio_context = raw_detach_aio_context, |     .bdrv_detach_aio_context = raw_detach_aio_context, | ||||||
|   | |||||||
| @@ -11,7 +11,6 @@ | |||||||
| #include "qemu/osdep.h" | #include "qemu/osdep.h" | ||||||
| #include <glusterfs/api/glfs.h> | #include <glusterfs/api/glfs.h> | ||||||
| #include "block/block_int.h" | #include "block/block_int.h" | ||||||
| #include "block/qdict.h" |  | ||||||
| #include "qapi/error.h" | #include "qapi/error.h" | ||||||
| #include "qapi/qmp/qdict.h" | #include "qapi/qmp/qdict.h" | ||||||
| #include "qapi/qmp/qerror.h" | #include "qapi/qmp/qerror.h" | ||||||
| @@ -20,10 +19,6 @@ | |||||||
| #include "qemu/option.h" | #include "qemu/option.h" | ||||||
| #include "qemu/cutils.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_FILENAME        "filename" | ||||||
| #define GLUSTER_OPT_VOLUME          "volume" | #define GLUSTER_OPT_VOLUME          "volume" | ||||||
| #define GLUSTER_OPT_PATH            "path" | #define GLUSTER_OPT_PATH            "path" | ||||||
| @@ -76,7 +71,7 @@ typedef struct ListElement { | |||||||
|     GlfsPreopened saved; |     GlfsPreopened saved; | ||||||
| } ListElement; | } ListElement; | ||||||
|  |  | ||||||
| static QLIST_HEAD(, ListElement) glfs_list; | static QLIST_HEAD(glfs_list, ListElement) glfs_list; | ||||||
|  |  | ||||||
| static QemuOptsList qemu_gluster_create_opts = { | static QemuOptsList qemu_gluster_create_opts = { | ||||||
|     .name = "qemu-gluster-create-opts", |     .name = "qemu-gluster-create-opts", | ||||||
| @@ -172,12 +167,7 @@ static QemuOptsList runtime_unix_opts = { | |||||||
|         { |         { | ||||||
|             .name = GLUSTER_OPT_SOCKET, |             .name = GLUSTER_OPT_SOCKET, | ||||||
|             .type = QEMU_OPT_STRING, |             .type = QEMU_OPT_STRING, | ||||||
|             .help = "socket file path (legacy)", |             .help = "socket file path)", | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|             .name = GLUSTER_OPT_PATH, |  | ||||||
|             .type = QEMU_OPT_STRING, |  | ||||||
|             .help = "socket file path (QAPI)", |  | ||||||
|         }, |         }, | ||||||
|         { /* end of list */ } |         { /* end of list */ } | ||||||
|     }, |     }, | ||||||
| @@ -625,18 +615,10 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, | |||||||
|                 goto out; |                 goto out; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             ptr = qemu_opt_get(opts, GLUSTER_OPT_PATH); |             ptr = qemu_opt_get(opts, GLUSTER_OPT_SOCKET); | ||||||
|             if (!ptr) { |  | ||||||
|                 ptr = qemu_opt_get(opts, GLUSTER_OPT_SOCKET); |  | ||||||
|             } else if (qemu_opt_get(opts, GLUSTER_OPT_SOCKET)) { |  | ||||||
|                 error_setg(&local_err, |  | ||||||
|                            "Conflicting parameters 'path' and 'socket'"); |  | ||||||
|                 error_append_hint(&local_err, GERR_INDEX_HINT, i); |  | ||||||
|                 goto out; |  | ||||||
|             } |  | ||||||
|             if (!ptr) { |             if (!ptr) { | ||||||
|                 error_setg(&local_err, QERR_MISSING_PARAMETER, |                 error_setg(&local_err, QERR_MISSING_PARAMETER, | ||||||
|                            GLUSTER_OPT_PATH); |                            GLUSTER_OPT_SOCKET); | ||||||
|                 error_append_hint(&local_err, GERR_INDEX_HINT, i); |                 error_append_hint(&local_err, GERR_INDEX_HINT, i); | ||||||
|                 goto out; |                 goto out; | ||||||
|             } |             } | ||||||
| @@ -655,7 +637,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, | |||||||
|         } |         } | ||||||
|         gsconf = NULL; |         gsconf = NULL; | ||||||
|  |  | ||||||
|         qobject_unref(backing_options); |         QDECREF(backing_options); | ||||||
|         backing_options = NULL; |         backing_options = NULL; | ||||||
|         g_free(str); |         g_free(str); | ||||||
|         str = NULL; |         str = NULL; | ||||||
| @@ -668,7 +650,7 @@ out: | |||||||
|     qapi_free_SocketAddress(gsconf); |     qapi_free_SocketAddress(gsconf); | ||||||
|     qemu_opts_del(opts); |     qemu_opts_del(opts); | ||||||
|     g_free(str); |     g_free(str); | ||||||
|     qobject_unref(backing_options); |     QDECREF(backing_options); | ||||||
|     errno = EINVAL; |     errno = EINVAL; | ||||||
|     return -errno; |     return -errno; | ||||||
| } | } | ||||||
| @@ -683,7 +665,7 @@ static int qemu_gluster_parse(BlockdevOptionsGluster *gconf, | |||||||
|     if (filename) { |     if (filename) { | ||||||
|         ret = qemu_gluster_parse_uri(gconf, filename); |         ret = qemu_gluster_parse_uri(gconf, filename); | ||||||
|         if (ret < 0) { |         if (ret < 0) { | ||||||
|             error_setg(errp, "invalid URI %s", filename); |             error_setg(errp, "invalid URI"); | ||||||
|             error_append_hint(errp, "Usage: file=gluster[+transport]://" |             error_append_hint(errp, "Usage: file=gluster[+transport]://" | ||||||
|                                     "[host[:port]]volume/path[?socket=...]" |                                     "[host[:port]]volume/path[?socket=...]" | ||||||
|                                     "[,file.debug=N]" |                                     "[,file.debug=N]" | ||||||
| @@ -702,7 +684,7 @@ static int qemu_gluster_parse(BlockdevOptionsGluster *gconf, | |||||||
|                              "file.server.0.host=1.2.3.4," |                              "file.server.0.host=1.2.3.4," | ||||||
|                              "file.server.0.port=24007," |                              "file.server.0.port=24007," | ||||||
|                              "file.server.1.transport=unix," |                              "file.server.1.transport=unix," | ||||||
|                              "file.server.1.path=/var/run/glusterd.socket ..." |                              "file.server.1.socket=/var/run/glusterd.socket ..." | ||||||
|                              "\n"); |                              "\n"); | ||||||
|             return ret; |             return ret; | ||||||
|         } |         } | ||||||
| @@ -729,11 +711,7 @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf, | |||||||
| /* | /* | ||||||
|  * AIO callback routine called from GlusterFS thread. |  * AIO callback routine called from GlusterFS thread. | ||||||
|  */ |  */ | ||||||
| static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, | static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg) | ||||||
| #ifdef CONFIG_GLUSTERFS_IOCB_HAS_STAT |  | ||||||
|                                  struct glfs_stat *pre, struct glfs_stat *post, |  | ||||||
| #endif |  | ||||||
|                                  void *arg) |  | ||||||
| { | { | ||||||
|     GlusterAIOCB *acb = (GlusterAIOCB *)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); |     qemu_gluster_parse_flags(bdrv_flags, &open_flags); | ||||||
|  |  | ||||||
|     s->fd = glfs_open(s->glfs, gconf->path, open_flags); |     s->fd = glfs_open(s->glfs, gconf->path, open_flags); | ||||||
|     ret = s->fd ? 0 : -errno; |     if (!s->fd) { | ||||||
|  |         ret = -errno; | ||||||
|     if (ret == -EACCES || ret == -EROFS) { |  | ||||||
|         /* Try to degrade to read-only, but if it doesn't work, still use the |  | ||||||
|          * normal error message. */ |  | ||||||
|         if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) { |  | ||||||
|             open_flags = (open_flags & ~O_RDWR) | O_RDONLY; |  | ||||||
|             s->fd = glfs_open(s->glfs, gconf->path, open_flags); |  | ||||||
|             ret = s->fd ? 0 : -errno; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     s->supports_seek_data = qemu_gluster_test_seek(s->fd); |     s->supports_seek_data = qemu_gluster_test_seek(s->fd); | ||||||
| @@ -1193,10 +1163,8 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs, | |||||||
|     return acb.ret; |     return acb.ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| static coroutine_fn int qemu_gluster_co_truncate(BlockDriverState *bs, | static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset, | ||||||
|                                                  int64_t offset, |                                  PreallocMode prealloc, Error **errp) | ||||||
|                                                  PreallocMode prealloc, |  | ||||||
|                                                  Error **errp) |  | ||||||
| { | { | ||||||
|     BDRVGlusterState *s = bs->opaque; |     BDRVGlusterState *s = bs->opaque; | ||||||
|     return qemu_gluster_do_truncate(s->fd, offset, prealloc, errp); |     return qemu_gluster_do_truncate(s->fd, offset, prealloc, errp); | ||||||
| @@ -1213,10 +1181,8 @@ static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs, | |||||||
| static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs, | static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs, | ||||||
|                                                int64_t sector_num, |                                                int64_t sector_num, | ||||||
|                                                int nb_sectors, |                                                int nb_sectors, | ||||||
|                                                QEMUIOVector *qiov, |                                                QEMUIOVector *qiov) | ||||||
|                                                int flags) |  | ||||||
| { | { | ||||||
|     assert(!flags); |  | ||||||
|     return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1); |     return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -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 @start is in a trailing hole or beyond EOF, return -ENXIO. | ||||||
|  * If we can't find out, return a negative errno other than -ENXIO. |  * If we can't find out, return a negative errno other than -ENXIO. | ||||||
|  * |  * | ||||||
|  * (Shamefully copied from file-posix.c, only minuscule adaptions.) |  * (Shamefully copied from file-posix.c, only miniscule adaptions.) | ||||||
|  */ |  */ | ||||||
| static int find_allocation(BlockDriverState *bs, off_t start, | static int find_allocation(BlockDriverState *bs, off_t start, | ||||||
|                            off_t *data, off_t *hole) |                            off_t *data, off_t *hole) | ||||||
| @@ -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 = { | static BlockDriver bdrv_gluster = { | ||||||
|     .format_name                  = "gluster", |     .format_name                  = "gluster", | ||||||
|     .protocol_name                = "gluster", |     .protocol_name                = "gluster", | ||||||
| @@ -1532,7 +1483,7 @@ static BlockDriver bdrv_gluster = { | |||||||
|     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, |     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, | ||||||
|     .bdrv_getlength               = qemu_gluster_getlength, |     .bdrv_getlength               = qemu_gluster_getlength, | ||||||
|     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, |     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, | ||||||
|     .bdrv_co_truncate             = qemu_gluster_co_truncate, |     .bdrv_truncate                = qemu_gluster_truncate, | ||||||
|     .bdrv_co_readv                = qemu_gluster_co_readv, |     .bdrv_co_readv                = qemu_gluster_co_readv, | ||||||
|     .bdrv_co_writev               = qemu_gluster_co_writev, |     .bdrv_co_writev               = qemu_gluster_co_writev, | ||||||
|     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, |     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, | ||||||
| @@ -1545,7 +1496,6 @@ static BlockDriver bdrv_gluster = { | |||||||
| #endif | #endif | ||||||
|     .bdrv_co_block_status         = qemu_gluster_co_block_status, |     .bdrv_co_block_status         = qemu_gluster_co_block_status, | ||||||
|     .create_opts                  = &qemu_gluster_create_opts, |     .create_opts                  = &qemu_gluster_create_opts, | ||||||
|     .strong_runtime_opts          = gluster_strong_open_opts, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static BlockDriver bdrv_gluster_tcp = { | static BlockDriver bdrv_gluster_tcp = { | ||||||
| @@ -1562,7 +1512,7 @@ static BlockDriver bdrv_gluster_tcp = { | |||||||
|     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, |     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, | ||||||
|     .bdrv_getlength               = qemu_gluster_getlength, |     .bdrv_getlength               = qemu_gluster_getlength, | ||||||
|     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, |     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, | ||||||
|     .bdrv_co_truncate             = qemu_gluster_co_truncate, |     .bdrv_truncate                = qemu_gluster_truncate, | ||||||
|     .bdrv_co_readv                = qemu_gluster_co_readv, |     .bdrv_co_readv                = qemu_gluster_co_readv, | ||||||
|     .bdrv_co_writev               = qemu_gluster_co_writev, |     .bdrv_co_writev               = qemu_gluster_co_writev, | ||||||
|     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, |     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, | ||||||
| @@ -1575,7 +1525,6 @@ static BlockDriver bdrv_gluster_tcp = { | |||||||
| #endif | #endif | ||||||
|     .bdrv_co_block_status         = qemu_gluster_co_block_status, |     .bdrv_co_block_status         = qemu_gluster_co_block_status, | ||||||
|     .create_opts                  = &qemu_gluster_create_opts, |     .create_opts                  = &qemu_gluster_create_opts, | ||||||
|     .strong_runtime_opts          = gluster_strong_open_opts, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static BlockDriver bdrv_gluster_unix = { | static BlockDriver bdrv_gluster_unix = { | ||||||
| @@ -1592,7 +1541,7 @@ static BlockDriver bdrv_gluster_unix = { | |||||||
|     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, |     .bdrv_co_create_opts          = qemu_gluster_co_create_opts, | ||||||
|     .bdrv_getlength               = qemu_gluster_getlength, |     .bdrv_getlength               = qemu_gluster_getlength, | ||||||
|     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, |     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, | ||||||
|     .bdrv_co_truncate             = qemu_gluster_co_truncate, |     .bdrv_truncate                = qemu_gluster_truncate, | ||||||
|     .bdrv_co_readv                = qemu_gluster_co_readv, |     .bdrv_co_readv                = qemu_gluster_co_readv, | ||||||
|     .bdrv_co_writev               = qemu_gluster_co_writev, |     .bdrv_co_writev               = qemu_gluster_co_writev, | ||||||
|     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, |     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, | ||||||
| @@ -1605,7 +1554,6 @@ static BlockDriver bdrv_gluster_unix = { | |||||||
| #endif | #endif | ||||||
|     .bdrv_co_block_status         = qemu_gluster_co_block_status, |     .bdrv_co_block_status         = qemu_gluster_co_block_status, | ||||||
|     .create_opts                  = &qemu_gluster_create_opts, |     .create_opts                  = &qemu_gluster_create_opts, | ||||||
|     .strong_runtime_opts          = gluster_strong_open_opts, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* rdma is deprecated (actually never supported for volfile fetch). | /* 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_co_create_opts          = qemu_gluster_co_create_opts, | ||||||
|     .bdrv_getlength               = qemu_gluster_getlength, |     .bdrv_getlength               = qemu_gluster_getlength, | ||||||
|     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, |     .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, | ||||||
|     .bdrv_co_truncate             = qemu_gluster_co_truncate, |     .bdrv_truncate                = qemu_gluster_truncate, | ||||||
|     .bdrv_co_readv                = qemu_gluster_co_readv, |     .bdrv_co_readv                = qemu_gluster_co_readv, | ||||||
|     .bdrv_co_writev               = qemu_gluster_co_writev, |     .bdrv_co_writev               = qemu_gluster_co_writev, | ||||||
|     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, |     .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk, | ||||||
| @@ -1641,7 +1589,6 @@ static BlockDriver bdrv_gluster_rdma = { | |||||||
| #endif | #endif | ||||||
|     .bdrv_co_block_status         = qemu_gluster_co_block_status, |     .bdrv_co_block_status         = qemu_gluster_co_block_status, | ||||||
|     .create_opts                  = &qemu_gluster_create_opts, |     .create_opts                  = &qemu_gluster_create_opts, | ||||||
|     .strong_runtime_opts          = gluster_strong_open_opts, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void bdrv_gluster_init(void) | 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/bitops.h" | ||||||
| #include "qemu/bitmap.h" | #include "qemu/bitmap.h" | ||||||
| #include "block/block_int.h" | #include "block/block_int.h" | ||||||
| #include "block/qdict.h" |  | ||||||
| #include "scsi/constants.h" | #include "scsi/constants.h" | ||||||
| #include "qemu/iov.h" | #include "qemu/iov.h" | ||||||
| #include "qemu/option.h" | #include "qemu/option.h" | ||||||
| @@ -44,14 +43,11 @@ | |||||||
| #include "qapi/qmp/qstring.h" | #include "qapi/qmp/qstring.h" | ||||||
| #include "crypto/secret.h" | #include "crypto/secret.h" | ||||||
| #include "scsi/utils.h" | #include "scsi/utils.h" | ||||||
| #include "trace.h" |  | ||||||
|  |  | ||||||
| /* Conflict between scsi/utils.h and libiscsi! :( */ | /* Conflict between scsi/utils.h and libiscsi! :( */ | ||||||
| #define SCSI_XFER_NONE ISCSI_XFER_NONE | #define SCSI_XFER_NONE ISCSI_XFER_NONE | ||||||
| #include <iscsi/iscsi.h> | #include <iscsi/iscsi.h> | ||||||
| #define inline __attribute__((gnu_inline))  /* required for libiscsi v1.9.0 */ |  | ||||||
| #include <iscsi/scsi-lowlevel.h> | #include <iscsi/scsi-lowlevel.h> | ||||||
| #undef inline |  | ||||||
| #undef SCSI_XFER_NONE | #undef SCSI_XFER_NONE | ||||||
| QEMU_BUILD_BUG_ON((int)SCSI_XFER_NONE != (int)ISCSI_XFER_NONE); | QEMU_BUILD_BUG_ON((int)SCSI_XFER_NONE != (int)ISCSI_XFER_NONE); | ||||||
|  |  | ||||||
| @@ -72,7 +68,6 @@ typedef struct IscsiLun { | |||||||
|     QemuMutex mutex; |     QemuMutex mutex; | ||||||
|     struct scsi_inquiry_logical_block_provisioning lbp; |     struct scsi_inquiry_logical_block_provisioning lbp; | ||||||
|     struct scsi_inquiry_block_limits bl; |     struct scsi_inquiry_block_limits bl; | ||||||
|     struct scsi_inquiry_device_designator *dd; |  | ||||||
|     unsigned char *zeroblock; |     unsigned char *zeroblock; | ||||||
|     /* The allocmap tracks which clusters (pages) on the iSCSI target are |     /* The allocmap tracks which clusters (pages) on the iSCSI target are | ||||||
|      * allocated and which are not. In case a target returns zeros for |      * allocated and which are not. In case a target returns zeros for | ||||||
| @@ -119,6 +114,7 @@ typedef struct IscsiAIOCB { | |||||||
|     QEMUBH *bh; |     QEMUBH *bh; | ||||||
|     IscsiLun *iscsilun; |     IscsiLun *iscsilun; | ||||||
|     struct scsi_task *task; |     struct scsi_task *task; | ||||||
|  |     uint8_t *buf; | ||||||
|     int status; |     int status; | ||||||
|     int64_t sector_num; |     int64_t sector_num; | ||||||
|     int nb_sectors; |     int nb_sectors; | ||||||
| @@ -126,7 +122,6 @@ typedef struct IscsiAIOCB { | |||||||
| #ifdef __linux__ | #ifdef __linux__ | ||||||
|     sg_io_hdr_t *ioh; |     sg_io_hdr_t *ioh; | ||||||
| #endif | #endif | ||||||
|     bool cancelled; |  | ||||||
| } IscsiAIOCB; | } IscsiAIOCB; | ||||||
|  |  | ||||||
| /* libiscsi uses time_t so its enough to process events every second */ | /* libiscsi uses time_t so its enough to process events every second */ | ||||||
| @@ -145,8 +140,6 @@ static const unsigned iscsi_retry_times[] = {8, 32, 128, 512, 2048, 8192, 32768} | |||||||
|  * unallocated. */ |  * unallocated. */ | ||||||
| #define ISCSI_CHECKALLOC_THRES 64 | #define ISCSI_CHECKALLOC_THRES 64 | ||||||
|  |  | ||||||
| #ifdef __linux__ |  | ||||||
|  |  | ||||||
| static void | static void | ||||||
| iscsi_bh_cb(void *p) | iscsi_bh_cb(void *p) | ||||||
| { | { | ||||||
| @@ -154,6 +147,9 @@ iscsi_bh_cb(void *p) | |||||||
|  |  | ||||||
|     qemu_bh_delete(acb->bh); |     qemu_bh_delete(acb->bh); | ||||||
|  |  | ||||||
|  |     g_free(acb->buf); | ||||||
|  |     acb->buf = NULL; | ||||||
|  |  | ||||||
|     acb->common.cb(acb->common.opaque, acb->status); |     acb->common.cb(acb->common.opaque, acb->status); | ||||||
|  |  | ||||||
|     if (acb->task != NULL) { |     if (acb->task != NULL) { | ||||||
| @@ -174,8 +170,6 @@ iscsi_schedule_bh(IscsiAIOCB *acb) | |||||||
|     qemu_bh_schedule(acb->bh); |     qemu_bh_schedule(acb->bh); | ||||||
| } | } | ||||||
|  |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| static void iscsi_co_generic_bh_cb(void *opaque) | static void iscsi_co_generic_bh_cb(void *opaque) | ||||||
| { | { | ||||||
|     struct IscsiTask *iTask = 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 | static void | ||||||
| iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data, | iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data, | ||||||
|                     void *private_data) |                     void *private_data) | ||||||
| { | { | ||||||
|     IscsiAIOCB *acb = private_data; |     IscsiAIOCB *acb = private_data; | ||||||
|  |  | ||||||
|     /* If the command callback hasn't been called yet, drop the task */ |     acb->status = -ECANCELED; | ||||||
|     if (!acb->bh) { |     iscsi_schedule_bh(acb); | ||||||
|         /* Call iscsi_aio_ioctl_cb() with SCSI_STATUS_CANCELLED */ |  | ||||||
|         iscsi_scsi_cancel_task(iscsi, acb->task); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     qemu_aio_unref(acb); /* acquired in iscsi_aio_cancel() */ |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| @@ -318,25 +304,14 @@ iscsi_aio_cancel(BlockAIOCB *blockacb) | |||||||
|     IscsiAIOCB *acb = (IscsiAIOCB *)blockacb; |     IscsiAIOCB *acb = (IscsiAIOCB *)blockacb; | ||||||
|     IscsiLun *iscsilun = acb->iscsilun; |     IscsiLun *iscsilun = acb->iscsilun; | ||||||
|  |  | ||||||
|     qemu_mutex_lock(&iscsilun->mutex); |     if (acb->status != -EINPROGRESS) { | ||||||
|  |  | ||||||
|     /* If it was cancelled or completed already, our work is done here */ |  | ||||||
|     if (acb->cancelled || acb->status != -EINPROGRESS) { |  | ||||||
|         qemu_mutex_unlock(&iscsilun->mutex); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     acb->cancelled = true; |  | ||||||
|  |  | ||||||
|     qemu_aio_ref(acb); /* released in iscsi_abort_task_cb() */ |  | ||||||
|  |  | ||||||
|     /* send a task mgmt call to the target to cancel the task on the target */ |     /* send a task mgmt call to the target to cancel the task on the target */ | ||||||
|     if (iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task, |     iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task, | ||||||
|                                          iscsi_abort_task_cb, acb) < 0) { |                                      iscsi_abort_task_cb, acb); | ||||||
|         qemu_aio_unref(acb); /* since iscsi_abort_task_cb() won't be called */ |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     qemu_mutex_unlock(&iscsilun->mutex); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static const AIOCBInfo iscsi_aiocb_info = { | static const AIOCBInfo iscsi_aiocb_info = { | ||||||
| @@ -344,7 +319,6 @@ static const AIOCBInfo iscsi_aiocb_info = { | |||||||
|     .cancel_async       = iscsi_aio_cancel, |     .cancel_async       = iscsi_aio_cancel, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| static void iscsi_process_read(void *arg); | static void iscsi_process_read(void *arg); | ||||||
| static void iscsi_process_write(void *arg); | static void iscsi_process_write(void *arg); | ||||||
| @@ -371,8 +345,6 @@ static void iscsi_timed_check_events(void *opaque) | |||||||
| { | { | ||||||
|     IscsiLun *iscsilun = opaque; |     IscsiLun *iscsilun = opaque; | ||||||
|  |  | ||||||
|     qemu_mutex_lock(&iscsilun->mutex); |  | ||||||
|  |  | ||||||
|     /* check for timed out requests */ |     /* check for timed out requests */ | ||||||
|     iscsi_service(iscsilun->iscsi, 0); |     iscsi_service(iscsilun->iscsi, 0); | ||||||
|  |  | ||||||
| @@ -385,8 +357,6 @@ static void iscsi_timed_check_events(void *opaque) | |||||||
|      * to return to service once this situation changes. */ |      * to return to service once this situation changes. */ | ||||||
|     iscsi_set_events(iscsilun); |     iscsi_set_events(iscsilun); | ||||||
|  |  | ||||||
|     qemu_mutex_unlock(&iscsilun->mutex); |  | ||||||
|  |  | ||||||
|     timer_mod(iscsilun->event_timer, |     timer_mod(iscsilun->event_timer, | ||||||
|               qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL); |               qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL); | ||||||
| } | } | ||||||
| @@ -585,20 +555,9 @@ static inline bool iscsi_allocmap_is_valid(IscsiLun *iscsilun, | |||||||
|                                offset / iscsilun->cluster_size) == size); |                                offset / iscsilun->cluster_size) == size); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void coroutine_fn iscsi_co_wait_for_task(IscsiTask *iTask, |  | ||||||
|                                                 IscsiLun *iscsilun) |  | ||||||
| { |  | ||||||
|     while (!iTask->complete) { |  | ||||||
|         iscsi_set_events(iscsilun); |  | ||||||
|         qemu_mutex_unlock(&iscsilun->mutex); |  | ||||||
|         qemu_coroutine_yield(); |  | ||||||
|         qemu_mutex_lock(&iscsilun->mutex); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int coroutine_fn | static int coroutine_fn | ||||||
| iscsi_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, | iscsi_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors, | ||||||
|                 QEMUIOVector *iov, int flags) |                       QEMUIOVector *iov, int flags) | ||||||
| { | { | ||||||
|     IscsiLun *iscsilun = bs->opaque; |     IscsiLun *iscsilun = bs->opaque; | ||||||
|     struct IscsiTask iTask; |     struct IscsiTask iTask; | ||||||
| @@ -657,7 +616,12 @@ retry: | |||||||
|     scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov, |     scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov, | ||||||
|                           iov->niov); |                           iov->niov); | ||||||
| #endif | #endif | ||||||
|     iscsi_co_wait_for_task(&iTask, iscsilun); |     while (!iTask.complete) { | ||||||
|  |         iscsi_set_events(iscsilun); | ||||||
|  |         qemu_mutex_unlock(&iscsilun->mutex); | ||||||
|  |         qemu_coroutine_yield(); | ||||||
|  |         qemu_mutex_lock(&iscsilun->mutex); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (iTask.task != NULL) { |     if (iTask.task != NULL) { | ||||||
|         scsi_free_scsi_task(iTask.task); |         scsi_free_scsi_task(iTask.task); | ||||||
| @@ -728,7 +692,13 @@ retry: | |||||||
|         ret = -ENOMEM; |         ret = -ENOMEM; | ||||||
|         goto out_unlock; |         goto out_unlock; | ||||||
|     } |     } | ||||||
|     iscsi_co_wait_for_task(&iTask, iscsilun); |  | ||||||
|  |     while (!iTask.complete) { | ||||||
|  |         iscsi_set_events(iscsilun); | ||||||
|  |         qemu_mutex_unlock(&iscsilun->mutex); | ||||||
|  |         qemu_coroutine_yield(); | ||||||
|  |         qemu_mutex_lock(&iscsilun->mutex); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (iTask.do_retry) { |     if (iTask.do_retry) { | ||||||
|         if (iTask.task != NULL) { |         if (iTask.task != NULL) { | ||||||
| @@ -762,7 +732,7 @@ retry: | |||||||
|         goto out_unlock; |         goto out_unlock; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     *pnum = (int64_t) lbasd->num_blocks * iscsilun->block_size; |     *pnum = lbasd->num_blocks * iscsilun->block_size; | ||||||
|  |  | ||||||
|     if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED || |     if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED || | ||||||
|         lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) { |         lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) { | ||||||
| @@ -892,8 +862,13 @@ retry: | |||||||
| #if LIBISCSI_API_VERSION < (20160603) | #if LIBISCSI_API_VERSION < (20160603) | ||||||
|     scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov); |     scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov); | ||||||
| #endif | #endif | ||||||
|  |     while (!iTask.complete) { | ||||||
|  |         iscsi_set_events(iscsilun); | ||||||
|  |         qemu_mutex_unlock(&iscsilun->mutex); | ||||||
|  |         qemu_coroutine_yield(); | ||||||
|  |         qemu_mutex_lock(&iscsilun->mutex); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     iscsi_co_wait_for_task(&iTask, iscsilun); |  | ||||||
|     if (iTask.task != NULL) { |     if (iTask.task != NULL) { | ||||||
|         scsi_free_scsi_task(iTask.task); |         scsi_free_scsi_task(iTask.task); | ||||||
|         iTask.task = NULL; |         iTask.task = NULL; | ||||||
| @@ -930,7 +905,12 @@ retry: | |||||||
|         return -ENOMEM; |         return -ENOMEM; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     iscsi_co_wait_for_task(&iTask, iscsilun); |     while (!iTask.complete) { | ||||||
|  |         iscsi_set_events(iscsilun); | ||||||
|  |         qemu_mutex_unlock(&iscsilun->mutex); | ||||||
|  |         qemu_coroutine_yield(); | ||||||
|  |         qemu_mutex_lock(&iscsilun->mutex); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (iTask.task != NULL) { |     if (iTask.task != NULL) { | ||||||
|         scsi_free_scsi_task(iTask.task); |         scsi_free_scsi_task(iTask.task); | ||||||
| @@ -960,13 +940,8 @@ iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status, | |||||||
| { | { | ||||||
|     IscsiAIOCB *acb = opaque; |     IscsiAIOCB *acb = opaque; | ||||||
|  |  | ||||||
|     if (status == SCSI_STATUS_CANCELLED) { |     g_free(acb->buf); | ||||||
|         if (!acb->bh) { |     acb->buf = NULL; | ||||||
|             acb->status = -ECANCELED; |  | ||||||
|             iscsi_schedule_bh(acb); |  | ||||||
|         } |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     acb->status = 0; |     acb->status = 0; | ||||||
|     if (status < 0) { |     if (status < 0) { | ||||||
| @@ -1042,8 +1017,8 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, | |||||||
|     acb->iscsilun = iscsilun; |     acb->iscsilun = iscsilun; | ||||||
|     acb->bh          = NULL; |     acb->bh          = NULL; | ||||||
|     acb->status      = -EINPROGRESS; |     acb->status      = -EINPROGRESS; | ||||||
|  |     acb->buf         = NULL; | ||||||
|     acb->ioh         = buf; |     acb->ioh         = buf; | ||||||
|     acb->cancelled   = false; |  | ||||||
|  |  | ||||||
|     if (req != SG_IO) { |     if (req != SG_IO) { | ||||||
|         iscsi_ioctl_handle_emulated(acb, req, buf); |         iscsi_ioctl_handle_emulated(acb, req, buf); | ||||||
| @@ -1167,7 +1142,12 @@ retry: | |||||||
|         goto out_unlock; |         goto out_unlock; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     iscsi_co_wait_for_task(&iTask, iscsilun); |     while (!iTask.complete) { | ||||||
|  |         iscsi_set_events(iscsilun); | ||||||
|  |         qemu_mutex_unlock(&iscsilun->mutex); | ||||||
|  |         qemu_coroutine_yield(); | ||||||
|  |         qemu_mutex_lock(&iscsilun->mutex); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (iTask.task != NULL) { |     if (iTask.task != NULL) { | ||||||
|         scsi_free_scsi_task(iTask.task); |         scsi_free_scsi_task(iTask.task); | ||||||
| @@ -1263,7 +1243,12 @@ retry: | |||||||
|         return -ENOMEM; |         return -ENOMEM; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     iscsi_co_wait_for_task(&iTask, iscsilun); |     while (!iTask.complete) { | ||||||
|  |         iscsi_set_events(iscsilun); | ||||||
|  |         qemu_mutex_unlock(&iscsilun->mutex); | ||||||
|  |         qemu_coroutine_yield(); | ||||||
|  |         qemu_mutex_lock(&iscsilun->mutex); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (iTask.status == SCSI_STATUS_CHECK_CONDITION && |     if (iTask.status == SCSI_STATUS_CHECK_CONDITION && | ||||||
|         iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && |         iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && | ||||||
| @@ -1747,34 +1732,14 @@ static QemuOptsList runtime_opts = { | |||||||
|             .name = "timeout", |             .name = "timeout", | ||||||
|             .type = QEMU_OPT_NUMBER, |             .type = QEMU_OPT_NUMBER, | ||||||
|         }, |         }, | ||||||
|  |         { | ||||||
|  |             .name = "filename", | ||||||
|  |             .type = QEMU_OPT_STRING, | ||||||
|  |         }, | ||||||
|         { /* end of list */ } |         { /* end of list */ } | ||||||
|     }, |     }, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void iscsi_save_designator(IscsiLun *lun, |  | ||||||
|                                   struct scsi_inquiry_device_identification *inq_di) |  | ||||||
| { |  | ||||||
|     struct scsi_inquiry_device_designator *desig, *copy = NULL; |  | ||||||
|  |  | ||||||
|     for (desig = inq_di->designators; desig; desig = desig->next) { |  | ||||||
|         if (desig->association || |  | ||||||
|             desig->designator_type > SCSI_DESIGNATOR_TYPE_NAA) { |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|         /* NAA works better than T10 vendor ID based designator. */ |  | ||||||
|         if (!copy || copy->designator_type < desig->designator_type) { |  | ||||||
|             copy = desig; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     if (copy) { |  | ||||||
|         lun->dd = g_new(struct scsi_inquiry_device_designator, 1); |  | ||||||
|         *lun->dd = *copy; |  | ||||||
|         lun->dd->next = NULL; |  | ||||||
|         lun->dd->designator = g_malloc(copy->designator_length); |  | ||||||
|         memcpy(lun->dd->designator, copy->designator, copy->designator_length); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | ||||||
|                       Error **errp) |                       Error **errp) | ||||||
| { | { | ||||||
| @@ -1786,12 +1751,27 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     char *initiator_name = NULL; |     char *initiator_name = NULL; | ||||||
|     QemuOpts *opts; |     QemuOpts *opts; | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
|     const char *transport_name, *portal, *target; |     const char *transport_name, *portal, *target, *filename; | ||||||
| #if LIBISCSI_API_VERSION >= (20160603) | #if LIBISCSI_API_VERSION >= (20160603) | ||||||
|     enum iscsi_transport_type transport; |     enum iscsi_transport_type transport; | ||||||
| #endif | #endif | ||||||
|     int i, ret = 0, timeout = 0, lun; |     int i, ret = 0, timeout = 0, lun; | ||||||
|  |  | ||||||
|  |     /* If we are given a filename, parse the filename, with precedence given to | ||||||
|  |      * filename encoded options */ | ||||||
|  |     filename = qdict_get_try_str(options, "filename"); | ||||||
|  |     if (filename) { | ||||||
|  |         warn_report("'filename' option specified. " | ||||||
|  |                     "This is an unsupported option, and may be deprecated " | ||||||
|  |                     "in the future"); | ||||||
|  |         iscsi_parse_filename(filename, options, &local_err); | ||||||
|  |         if (local_err) { | ||||||
|  |             ret = -EINVAL; | ||||||
|  |             error_propagate(errp, local_err); | ||||||
|  |             goto exit; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); |     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); | ||||||
|     qemu_opts_absorb_qdict(opts, options, &local_err); |     qemu_opts_absorb_qdict(opts, options, &local_err); | ||||||
|     if (local_err) { |     if (local_err) { | ||||||
| @@ -1876,7 +1856,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     iscsi_set_timeout(iscsi, timeout); |     iscsi_set_timeout(iscsi, timeout); | ||||||
| #else | #else | ||||||
|     if (timeout) { |     if (timeout) { | ||||||
|         warn_report("iSCSI: ignoring timeout value for libiscsi <1.15.0"); |         error_report("iSCSI: ignoring timeout value for libiscsi <1.15.0"); | ||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -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 */ |     /* Check the write protect flag of the LUN if we want to write */ | ||||||
|     if (iscsilun->type == TYPE_DISK && (flags & BDRV_O_RDWR) && |     if (iscsilun->type == TYPE_DISK && (flags & BDRV_O_RDWR) && | ||||||
|         iscsilun->write_protected) { |         iscsilun->write_protected) { | ||||||
|         ret = bdrv_apply_auto_read_only(bs, "LUN is write protected", errp); |         error_setg(errp, "Cannot open a write protected LUN as read-write"); | ||||||
|         if (ret < 0) { |         ret = -EACCES; | ||||||
|             goto out; |         goto out; | ||||||
|         } |  | ||||||
|         flags &= ~BDRV_O_RDWR; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     iscsi_readcapacity_sync(iscsilun, &local_err); |     iscsi_readcapacity_sync(iscsilun, &local_err); | ||||||
| @@ -1944,7 +1922,6 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|         struct scsi_task *inq_task; |         struct scsi_task *inq_task; | ||||||
|         struct scsi_inquiry_logical_block_provisioning *inq_lbp; |         struct scsi_inquiry_logical_block_provisioning *inq_lbp; | ||||||
|         struct scsi_inquiry_block_limits *inq_bl; |         struct scsi_inquiry_block_limits *inq_bl; | ||||||
|         struct scsi_inquiry_device_identification *inq_di; |  | ||||||
|         switch (inq_vpd->pages[i]) { |         switch (inq_vpd->pages[i]) { | ||||||
|         case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING: |         case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING: | ||||||
|             inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, |             inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, | ||||||
| @@ -1970,17 +1947,6 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|                    sizeof(struct scsi_inquiry_block_limits)); |                    sizeof(struct scsi_inquiry_block_limits)); | ||||||
|             scsi_free_scsi_task(inq_task); |             scsi_free_scsi_task(inq_task); | ||||||
|             break; |             break; | ||||||
|         case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION: |  | ||||||
|             inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, |  | ||||||
|                                     SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION, |  | ||||||
|                                     (void **) &inq_di, errp); |  | ||||||
|             if (inq_task == NULL) { |  | ||||||
|                 ret = -EINVAL; |  | ||||||
|                 goto out; |  | ||||||
|             } |  | ||||||
|             iscsi_save_designator(iscsilun, inq_di); |  | ||||||
|             scsi_free_scsi_task(inq_task); |  | ||||||
|             break; |  | ||||||
|         default: |         default: | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| @@ -2023,7 +1989,7 @@ out: | |||||||
|         } |         } | ||||||
|         memset(iscsilun, 0, sizeof(IscsiLun)); |         memset(iscsilun, 0, sizeof(IscsiLun)); | ||||||
|     } |     } | ||||||
|  | exit: | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -2037,10 +2003,6 @@ static void iscsi_close(BlockDriverState *bs) | |||||||
|         iscsi_logout_sync(iscsi); |         iscsi_logout_sync(iscsi); | ||||||
|     } |     } | ||||||
|     iscsi_destroy_context(iscsi); |     iscsi_destroy_context(iscsi); | ||||||
|     if (iscsilun->dd) { |  | ||||||
|         g_free(iscsilun->dd->designator); |  | ||||||
|         g_free(iscsilun->dd); |  | ||||||
|     } |  | ||||||
|     g_free(iscsilun->zeroblock); |     g_free(iscsilun->zeroblock); | ||||||
|     iscsi_allocmap_free(iscsilun); |     iscsi_allocmap_free(iscsilun); | ||||||
|     qemu_mutex_destroy(&iscsilun->mutex); |     qemu_mutex_destroy(&iscsilun->mutex); | ||||||
| @@ -2120,8 +2082,8 @@ static void iscsi_reopen_commit(BDRVReopenState *reopen_state) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset, | static int iscsi_truncate(BlockDriverState *bs, int64_t offset, | ||||||
|                                           PreallocMode prealloc, Error **errp) |                           PreallocMode prealloc, Error **errp) | ||||||
| { | { | ||||||
|     IscsiLun *iscsilun = bs->opaque; |     IscsiLun *iscsilun = bs->opaque; | ||||||
|     Error *local_err = NULL; |     Error *local_err = NULL; | ||||||
| @@ -2181,7 +2143,7 @@ static int coroutine_fn iscsi_co_create_opts(const char *filename, QemuOpts *opt | |||||||
|     } else { |     } else { | ||||||
|         ret = iscsi_open(bs, bs_options, 0, NULL); |         ret = iscsi_open(bs, bs_options, 0, NULL); | ||||||
|     } |     } | ||||||
|     qobject_unref(bs_options); |     QDECREF(bs_options); | ||||||
|  |  | ||||||
|     if (ret != 0) { |     if (ret != 0) { | ||||||
|         goto out; |         goto out; | ||||||
| @@ -2222,226 +2184,6 @@ static void coroutine_fn iscsi_co_invalidate_cache(BlockDriverState *bs, | |||||||
|     iscsi_allocmap_invalidate(iscsilun); |     iscsi_allocmap_invalidate(iscsilun); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int coroutine_fn iscsi_co_copy_range_from(BlockDriverState *bs, |  | ||||||
|                                                  BdrvChild *src, |  | ||||||
|                                                  uint64_t src_offset, |  | ||||||
|                                                  BdrvChild *dst, |  | ||||||
|                                                  uint64_t dst_offset, |  | ||||||
|                                                  uint64_t bytes, |  | ||||||
|                                                  BdrvRequestFlags read_flags, |  | ||||||
|                                                  BdrvRequestFlags write_flags) |  | ||||||
| { |  | ||||||
|     return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, |  | ||||||
|                                  read_flags, write_flags); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static struct scsi_task *iscsi_xcopy_task(int param_len) |  | ||||||
| { |  | ||||||
|     struct scsi_task *task; |  | ||||||
|  |  | ||||||
|     task = g_new0(struct scsi_task, 1); |  | ||||||
|  |  | ||||||
|     task->cdb[0]     = EXTENDED_COPY; |  | ||||||
|     task->cdb[10]    = (param_len >> 24) & 0xFF; |  | ||||||
|     task->cdb[11]    = (param_len >> 16) & 0xFF; |  | ||||||
|     task->cdb[12]    = (param_len >> 8) & 0xFF; |  | ||||||
|     task->cdb[13]    = param_len & 0xFF; |  | ||||||
|     task->cdb_size   = 16; |  | ||||||
|     task->xfer_dir   = SCSI_XFER_WRITE; |  | ||||||
|     task->expxferlen = param_len; |  | ||||||
|  |  | ||||||
|     return task; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void iscsi_populate_target_desc(unsigned char *desc, IscsiLun *lun) |  | ||||||
| { |  | ||||||
|     struct scsi_inquiry_device_designator *dd = lun->dd; |  | ||||||
|  |  | ||||||
|     memset(desc, 0, 32); |  | ||||||
|     desc[0] = 0xE4; /* IDENT_DESCR_TGT_DESCR */ |  | ||||||
|     desc[4] = dd->code_set; |  | ||||||
|     desc[5] = (dd->designator_type & 0xF) |  | ||||||
|         | ((dd->association & 3) << 4); |  | ||||||
|     desc[7] = dd->designator_length; |  | ||||||
|     memcpy(desc + 8, dd->designator, MIN(dd->designator_length, 20)); |  | ||||||
|  |  | ||||||
|     desc[28] = 0; |  | ||||||
|     desc[29] = (lun->block_size >> 16) & 0xFF; |  | ||||||
|     desc[30] = (lun->block_size >> 8) & 0xFF; |  | ||||||
|     desc[31] = lun->block_size & 0xFF; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void iscsi_xcopy_desc_hdr(uint8_t *hdr, int dc, int cat, int src_index, |  | ||||||
|                                  int dst_index) |  | ||||||
| { |  | ||||||
|     hdr[0] = 0x02; /* BLK_TO_BLK_SEG_DESCR */ |  | ||||||
|     hdr[1] = ((dc << 1) | cat) & 0xFF; |  | ||||||
|     hdr[2] = (XCOPY_BLK2BLK_SEG_DESC_SIZE >> 8) & 0xFF; |  | ||||||
|     /* don't account for the first 4 bytes in descriptor header*/ |  | ||||||
|     hdr[3] = (XCOPY_BLK2BLK_SEG_DESC_SIZE - 4 /* SEG_DESC_SRC_INDEX_OFFSET */) & 0xFF; |  | ||||||
|     hdr[4] = (src_index >> 8) & 0xFF; |  | ||||||
|     hdr[5] = src_index & 0xFF; |  | ||||||
|     hdr[6] = (dst_index >> 8) & 0xFF; |  | ||||||
|     hdr[7] = dst_index & 0xFF; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void iscsi_xcopy_populate_desc(uint8_t *desc, int dc, int cat, |  | ||||||
|                                       int src_index, int dst_index, int num_blks, |  | ||||||
|                                       uint64_t src_lba, uint64_t dst_lba) |  | ||||||
| { |  | ||||||
|     iscsi_xcopy_desc_hdr(desc, dc, cat, src_index, dst_index); |  | ||||||
|  |  | ||||||
|     /* The caller should verify the request size */ |  | ||||||
|     assert(num_blks < 65536); |  | ||||||
|     desc[10] = (num_blks >> 8) & 0xFF; |  | ||||||
|     desc[11] = num_blks & 0xFF; |  | ||||||
|     desc[12] = (src_lba >> 56) & 0xFF; |  | ||||||
|     desc[13] = (src_lba >> 48) & 0xFF; |  | ||||||
|     desc[14] = (src_lba >> 40) & 0xFF; |  | ||||||
|     desc[15] = (src_lba >> 32) & 0xFF; |  | ||||||
|     desc[16] = (src_lba >> 24) & 0xFF; |  | ||||||
|     desc[17] = (src_lba >> 16) & 0xFF; |  | ||||||
|     desc[18] = (src_lba >> 8) & 0xFF; |  | ||||||
|     desc[19] = src_lba & 0xFF; |  | ||||||
|     desc[20] = (dst_lba >> 56) & 0xFF; |  | ||||||
|     desc[21] = (dst_lba >> 48) & 0xFF; |  | ||||||
|     desc[22] = (dst_lba >> 40) & 0xFF; |  | ||||||
|     desc[23] = (dst_lba >> 32) & 0xFF; |  | ||||||
|     desc[24] = (dst_lba >> 24) & 0xFF; |  | ||||||
|     desc[25] = (dst_lba >> 16) & 0xFF; |  | ||||||
|     desc[26] = (dst_lba >> 8) & 0xFF; |  | ||||||
|     desc[27] = dst_lba & 0xFF; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void iscsi_xcopy_populate_header(unsigned char *buf, int list_id, int str, |  | ||||||
|                                         int list_id_usage, int prio, |  | ||||||
|                                         int tgt_desc_len, |  | ||||||
|                                         int seg_desc_len, int inline_data_len) |  | ||||||
| { |  | ||||||
|     buf[0] = list_id; |  | ||||||
|     buf[1] = ((str & 1) << 5) | ((list_id_usage & 3) << 3) | (prio & 7); |  | ||||||
|     buf[2] = (tgt_desc_len >> 8) & 0xFF; |  | ||||||
|     buf[3] = tgt_desc_len & 0xFF; |  | ||||||
|     buf[8] = (seg_desc_len >> 24) & 0xFF; |  | ||||||
|     buf[9] = (seg_desc_len >> 16) & 0xFF; |  | ||||||
|     buf[10] = (seg_desc_len >> 8) & 0xFF; |  | ||||||
|     buf[11] = seg_desc_len & 0xFF; |  | ||||||
|     buf[12] = (inline_data_len >> 24) & 0xFF; |  | ||||||
|     buf[13] = (inline_data_len >> 16) & 0xFF; |  | ||||||
|     buf[14] = (inline_data_len >> 8) & 0xFF; |  | ||||||
|     buf[15] = inline_data_len & 0xFF; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void iscsi_xcopy_data(struct iscsi_data *data, |  | ||||||
|                              IscsiLun *src, int64_t src_lba, |  | ||||||
|                              IscsiLun *dst, int64_t dst_lba, |  | ||||||
|                              uint16_t num_blocks) |  | ||||||
| { |  | ||||||
|     uint8_t *buf; |  | ||||||
|     const int src_offset = XCOPY_DESC_OFFSET; |  | ||||||
|     const int dst_offset = XCOPY_DESC_OFFSET + IDENT_DESCR_TGT_DESCR_SIZE; |  | ||||||
|     const int seg_offset = dst_offset + IDENT_DESCR_TGT_DESCR_SIZE; |  | ||||||
|  |  | ||||||
|     data->size = XCOPY_DESC_OFFSET + |  | ||||||
|                  IDENT_DESCR_TGT_DESCR_SIZE * 2 + |  | ||||||
|                  XCOPY_BLK2BLK_SEG_DESC_SIZE; |  | ||||||
|     data->data = g_malloc0(data->size); |  | ||||||
|     buf = data->data; |  | ||||||
|  |  | ||||||
|     /* Initialise the parameter list header */ |  | ||||||
|     iscsi_xcopy_populate_header(buf, 1, 0, 2 /* LIST_ID_USAGE_DISCARD */, |  | ||||||
|                                 0, 2 * IDENT_DESCR_TGT_DESCR_SIZE, |  | ||||||
|                                 XCOPY_BLK2BLK_SEG_DESC_SIZE, |  | ||||||
|                                 0); |  | ||||||
|  |  | ||||||
|     /* Initialise CSCD list with one src + one dst descriptor */ |  | ||||||
|     iscsi_populate_target_desc(&buf[src_offset], src); |  | ||||||
|     iscsi_populate_target_desc(&buf[dst_offset], dst); |  | ||||||
|  |  | ||||||
|     /* Initialise one segment descriptor */ |  | ||||||
|     iscsi_xcopy_populate_desc(&buf[seg_offset], 0, 0, 0, 1, num_blocks, |  | ||||||
|                               src_lba, dst_lba); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int coroutine_fn iscsi_co_copy_range_to(BlockDriverState *bs, |  | ||||||
|                                                BdrvChild *src, |  | ||||||
|                                                uint64_t src_offset, |  | ||||||
|                                                BdrvChild *dst, |  | ||||||
|                                                uint64_t dst_offset, |  | ||||||
|                                                uint64_t bytes, |  | ||||||
|                                                BdrvRequestFlags read_flags, |  | ||||||
|                                                BdrvRequestFlags write_flags) |  | ||||||
| { |  | ||||||
|     IscsiLun *dst_lun = dst->bs->opaque; |  | ||||||
|     IscsiLun *src_lun; |  | ||||||
|     struct IscsiTask iscsi_task; |  | ||||||
|     struct iscsi_data data; |  | ||||||
|     int r = 0; |  | ||||||
|     int block_size; |  | ||||||
|  |  | ||||||
|     if (src->bs->drv->bdrv_co_copy_range_to != iscsi_co_copy_range_to) { |  | ||||||
|         return -ENOTSUP; |  | ||||||
|     } |  | ||||||
|     src_lun = src->bs->opaque; |  | ||||||
|  |  | ||||||
|     if (!src_lun->dd || !dst_lun->dd) { |  | ||||||
|         return -ENOTSUP; |  | ||||||
|     } |  | ||||||
|     if (!is_byte_request_lun_aligned(dst_offset, bytes, dst_lun)) { |  | ||||||
|         return -ENOTSUP; |  | ||||||
|     } |  | ||||||
|     if (!is_byte_request_lun_aligned(src_offset, bytes, src_lun)) { |  | ||||||
|         return -ENOTSUP; |  | ||||||
|     } |  | ||||||
|     if (dst_lun->block_size != src_lun->block_size || |  | ||||||
|         !dst_lun->block_size) { |  | ||||||
|         return -ENOTSUP; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     block_size = dst_lun->block_size; |  | ||||||
|     if (bytes / block_size > 65535) { |  | ||||||
|         return -ENOTSUP; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     iscsi_xcopy_data(&data, |  | ||||||
|                      src_lun, src_offset / block_size, |  | ||||||
|                      dst_lun, dst_offset / block_size, |  | ||||||
|                      bytes / block_size); |  | ||||||
|  |  | ||||||
|     iscsi_co_init_iscsitask(dst_lun, &iscsi_task); |  | ||||||
|  |  | ||||||
|     qemu_mutex_lock(&dst_lun->mutex); |  | ||||||
|     iscsi_task.task = iscsi_xcopy_task(data.size); |  | ||||||
| retry: |  | ||||||
|     if (iscsi_scsi_command_async(dst_lun->iscsi, dst_lun->lun, |  | ||||||
|                                  iscsi_task.task, iscsi_co_generic_cb, |  | ||||||
|                                  &data, |  | ||||||
|                                  &iscsi_task) != 0) { |  | ||||||
|         r = -EIO; |  | ||||||
|         goto out_unlock; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     iscsi_co_wait_for_task(&iscsi_task, dst_lun); |  | ||||||
|  |  | ||||||
|     if (iscsi_task.do_retry) { |  | ||||||
|         iscsi_task.complete = 0; |  | ||||||
|         goto retry; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (iscsi_task.status != SCSI_STATUS_GOOD) { |  | ||||||
|         r = iscsi_task.err_code; |  | ||||||
|         goto out_unlock; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| out_unlock: |  | ||||||
|  |  | ||||||
|     trace_iscsi_xcopy(src_lun, src_offset, dst_lun, dst_offset, bytes, r); |  | ||||||
|     g_free(iscsi_task.task); |  | ||||||
|     qemu_mutex_unlock(&dst_lun->mutex); |  | ||||||
|     g_free(iscsi_task.err_str); |  | ||||||
|     return r; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static QemuOptsList iscsi_create_opts = { | static QemuOptsList iscsi_create_opts = { | ||||||
|     .name = "iscsi-create-opts", |     .name = "iscsi-create-opts", | ||||||
|     .head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head), |     .head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head), | ||||||
| @@ -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 = { | static BlockDriver bdrv_iscsi = { | ||||||
|     .format_name     = "iscsi", |     .format_name     = "iscsi", | ||||||
|     .protocol_name   = "iscsi", |     .protocol_name   = "iscsi", | ||||||
| @@ -2485,16 +2213,14 @@ static BlockDriver bdrv_iscsi = { | |||||||
|  |  | ||||||
|     .bdrv_getlength  = iscsi_getlength, |     .bdrv_getlength  = iscsi_getlength, | ||||||
|     .bdrv_get_info   = iscsi_get_info, |     .bdrv_get_info   = iscsi_get_info, | ||||||
|     .bdrv_co_truncate    = iscsi_co_truncate, |     .bdrv_truncate   = iscsi_truncate, | ||||||
|     .bdrv_refresh_limits = iscsi_refresh_limits, |     .bdrv_refresh_limits = iscsi_refresh_limits, | ||||||
|  |  | ||||||
|     .bdrv_co_block_status  = iscsi_co_block_status, |     .bdrv_co_block_status  = iscsi_co_block_status, | ||||||
|     .bdrv_co_pdiscard      = iscsi_co_pdiscard, |     .bdrv_co_pdiscard      = iscsi_co_pdiscard, | ||||||
|     .bdrv_co_copy_range_from = iscsi_co_copy_range_from, |  | ||||||
|     .bdrv_co_copy_range_to  = iscsi_co_copy_range_to, |  | ||||||
|     .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, |     .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, | ||||||
|     .bdrv_co_readv         = iscsi_co_readv, |     .bdrv_co_readv         = iscsi_co_readv, | ||||||
|     .bdrv_co_writev        = iscsi_co_writev, |     .bdrv_co_writev_flags  = iscsi_co_writev_flags, | ||||||
|     .bdrv_co_flush_to_disk = iscsi_co_flush, |     .bdrv_co_flush_to_disk = iscsi_co_flush, | ||||||
|  |  | ||||||
| #ifdef __linux__ | #ifdef __linux__ | ||||||
| @@ -2503,8 +2229,6 @@ static BlockDriver bdrv_iscsi = { | |||||||
|  |  | ||||||
|     .bdrv_detach_aio_context = iscsi_detach_aio_context, |     .bdrv_detach_aio_context = iscsi_detach_aio_context, | ||||||
|     .bdrv_attach_aio_context = iscsi_attach_aio_context, |     .bdrv_attach_aio_context = iscsi_attach_aio_context, | ||||||
|  |  | ||||||
|     .strong_runtime_opts = iscsi_strong_runtime_opts, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #if LIBISCSI_API_VERSION >= (20160603) | #if LIBISCSI_API_VERSION >= (20160603) | ||||||
| @@ -2520,20 +2244,18 @@ static BlockDriver bdrv_iser = { | |||||||
|     .create_opts            = &iscsi_create_opts, |     .create_opts            = &iscsi_create_opts, | ||||||
|     .bdrv_reopen_prepare    = iscsi_reopen_prepare, |     .bdrv_reopen_prepare    = iscsi_reopen_prepare, | ||||||
|     .bdrv_reopen_commit     = iscsi_reopen_commit, |     .bdrv_reopen_commit     = iscsi_reopen_commit, | ||||||
|     .bdrv_co_invalidate_cache  = iscsi_co_invalidate_cache, |     .bdrv_invalidate_cache  = iscsi_invalidate_cache, | ||||||
|  |  | ||||||
|     .bdrv_getlength  = iscsi_getlength, |     .bdrv_getlength  = iscsi_getlength, | ||||||
|     .bdrv_get_info   = iscsi_get_info, |     .bdrv_get_info   = iscsi_get_info, | ||||||
|     .bdrv_co_truncate    = iscsi_co_truncate, |     .bdrv_truncate   = iscsi_truncate, | ||||||
|     .bdrv_refresh_limits = iscsi_refresh_limits, |     .bdrv_refresh_limits = iscsi_refresh_limits, | ||||||
|  |  | ||||||
|     .bdrv_co_block_status  = iscsi_co_block_status, |     .bdrv_co_block_status  = iscsi_co_block_status, | ||||||
|     .bdrv_co_pdiscard      = iscsi_co_pdiscard, |     .bdrv_co_pdiscard      = iscsi_co_pdiscard, | ||||||
|     .bdrv_co_copy_range_from = iscsi_co_copy_range_from, |  | ||||||
|     .bdrv_co_copy_range_to  = iscsi_co_copy_range_to, |  | ||||||
|     .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, |     .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, | ||||||
|     .bdrv_co_readv         = iscsi_co_readv, |     .bdrv_co_readv         = iscsi_co_readv, | ||||||
|     .bdrv_co_writev        = iscsi_co_writev, |     .bdrv_co_writev_flags  = iscsi_co_writev_flags, | ||||||
|     .bdrv_co_flush_to_disk = iscsi_co_flush, |     .bdrv_co_flush_to_disk = iscsi_co_flush, | ||||||
|  |  | ||||||
| #ifdef __linux__ | #ifdef __linux__ | ||||||
| @@ -2542,8 +2264,6 @@ static BlockDriver bdrv_iser = { | |||||||
|  |  | ||||||
|     .bdrv_detach_aio_context = iscsi_detach_aio_context, |     .bdrv_detach_aio_context = iscsi_detach_aio_context, | ||||||
|     .bdrv_attach_aio_context = iscsi_attach_aio_context, |     .bdrv_attach_aio_context = iscsi_attach_aio_context, | ||||||
|  |  | ||||||
|     .strong_runtime_opts = iscsi_strong_runtime_opts, |  | ||||||
| }; | }; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user