Compare commits
	
		
			5 Commits
		
	
	
		
			qemu-sparc
			...
			rm-protoco
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					6321d78dc5 | ||
| 
						 | 
					a0974f99aa | ||
| 
						 | 
					42883c01bf | ||
| 
						 | 
					7966c2b312 | ||
| 
						 | 
					5888011244 | 
@@ -1,10 +1,4 @@
 | 
			
		||||
# EditorConfig is a file format and collection of text editor plugins
 | 
			
		||||
# for maintaining consistent coding styles between different editors
 | 
			
		||||
# and IDEs. Most popular editors support this either natively or via
 | 
			
		||||
# plugin.
 | 
			
		||||
#
 | 
			
		||||
# Check https://editorconfig.org for details.
 | 
			
		||||
 | 
			
		||||
# http://editorconfig.org
 | 
			
		||||
root = true
 | 
			
		||||
 | 
			
		||||
[*]
 | 
			
		||||
@@ -12,23 +6,10 @@ end_of_line = lf
 | 
			
		||||
insert_final_newline = true
 | 
			
		||||
charset = utf-8
 | 
			
		||||
 | 
			
		||||
[*.mak]
 | 
			
		||||
indent_style = tab
 | 
			
		||||
indent_size = 8
 | 
			
		||||
file_type_emacs = makefile
 | 
			
		||||
 | 
			
		||||
[Makefile*]
 | 
			
		||||
indent_style = tab
 | 
			
		||||
indent_size = 8
 | 
			
		||||
file_type_emacs = makefile
 | 
			
		||||
 | 
			
		||||
[*.{c,h}]
 | 
			
		||||
indent_style = space
 | 
			
		||||
indent_size = 4
 | 
			
		||||
 | 
			
		||||
[*.{vert,frag}]
 | 
			
		||||
file_type_emacs = glsl
 | 
			
		||||
 | 
			
		||||
[*.json]
 | 
			
		||||
indent_style = space
 | 
			
		||||
file_type_emacs = python
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										71
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										71
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -30,20 +30,79 @@
 | 
			
		||||
/qapi-gen-timestamp
 | 
			
		||||
/qapi/qapi-builtin-types.[ch]
 | 
			
		||||
/qapi/qapi-builtin-visit.[ch]
 | 
			
		||||
/qapi/qapi-commands-*.[ch]
 | 
			
		||||
/qapi/qapi-commands-block-core.[ch]
 | 
			
		||||
/qapi/qapi-commands-block.[ch]
 | 
			
		||||
/qapi/qapi-commands-char.[ch]
 | 
			
		||||
/qapi/qapi-commands-common.[ch]
 | 
			
		||||
/qapi/qapi-commands-crypto.[ch]
 | 
			
		||||
/qapi/qapi-commands-introspect.[ch]
 | 
			
		||||
/qapi/qapi-commands-migration.[ch]
 | 
			
		||||
/qapi/qapi-commands-misc.[ch]
 | 
			
		||||
/qapi/qapi-commands-net.[ch]
 | 
			
		||||
/qapi/qapi-commands-rocker.[ch]
 | 
			
		||||
/qapi/qapi-commands-run-state.[ch]
 | 
			
		||||
/qapi/qapi-commands-sockets.[ch]
 | 
			
		||||
/qapi/qapi-commands-tpm.[ch]
 | 
			
		||||
/qapi/qapi-commands-trace.[ch]
 | 
			
		||||
/qapi/qapi-commands-transaction.[ch]
 | 
			
		||||
/qapi/qapi-commands-ui.[ch]
 | 
			
		||||
/qapi/qapi-commands.[ch]
 | 
			
		||||
/qapi/qapi-events-*.[ch]
 | 
			
		||||
/qapi/qapi-events-block-core.[ch]
 | 
			
		||||
/qapi/qapi-events-block.[ch]
 | 
			
		||||
/qapi/qapi-events-char.[ch]
 | 
			
		||||
/qapi/qapi-events-common.[ch]
 | 
			
		||||
/qapi/qapi-events-crypto.[ch]
 | 
			
		||||
/qapi/qapi-events-introspect.[ch]
 | 
			
		||||
/qapi/qapi-events-migration.[ch]
 | 
			
		||||
/qapi/qapi-events-misc.[ch]
 | 
			
		||||
/qapi/qapi-events-net.[ch]
 | 
			
		||||
/qapi/qapi-events-rocker.[ch]
 | 
			
		||||
/qapi/qapi-events-run-state.[ch]
 | 
			
		||||
/qapi/qapi-events-sockets.[ch]
 | 
			
		||||
/qapi/qapi-events-tpm.[ch]
 | 
			
		||||
/qapi/qapi-events-trace.[ch]
 | 
			
		||||
/qapi/qapi-events-transaction.[ch]
 | 
			
		||||
/qapi/qapi-events-ui.[ch]
 | 
			
		||||
/qapi/qapi-events.[ch]
 | 
			
		||||
/qapi/qapi-introspect.[ch]
 | 
			
		||||
/qapi/qapi-types-*.[ch]
 | 
			
		||||
/qapi/qapi-types-block-core.[ch]
 | 
			
		||||
/qapi/qapi-types-block.[ch]
 | 
			
		||||
/qapi/qapi-types-char.[ch]
 | 
			
		||||
/qapi/qapi-types-common.[ch]
 | 
			
		||||
/qapi/qapi-types-crypto.[ch]
 | 
			
		||||
/qapi/qapi-types-introspect.[ch]
 | 
			
		||||
/qapi/qapi-types-migration.[ch]
 | 
			
		||||
/qapi/qapi-types-misc.[ch]
 | 
			
		||||
/qapi/qapi-types-net.[ch]
 | 
			
		||||
/qapi/qapi-types-rocker.[ch]
 | 
			
		||||
/qapi/qapi-types-run-state.[ch]
 | 
			
		||||
/qapi/qapi-types-sockets.[ch]
 | 
			
		||||
/qapi/qapi-types-tpm.[ch]
 | 
			
		||||
/qapi/qapi-types-trace.[ch]
 | 
			
		||||
/qapi/qapi-types-transaction.[ch]
 | 
			
		||||
/qapi/qapi-types-ui.[ch]
 | 
			
		||||
/qapi/qapi-types.[ch]
 | 
			
		||||
/qapi/qapi-visit-*.[ch]
 | 
			
		||||
/qapi/qapi-visit-block-core.[ch]
 | 
			
		||||
/qapi/qapi-visit-block.[ch]
 | 
			
		||||
/qapi/qapi-visit-char.[ch]
 | 
			
		||||
/qapi/qapi-visit-common.[ch]
 | 
			
		||||
/qapi/qapi-visit-crypto.[ch]
 | 
			
		||||
/qapi/qapi-visit-introspect.[ch]
 | 
			
		||||
/qapi/qapi-visit-migration.[ch]
 | 
			
		||||
/qapi/qapi-visit-misc.[ch]
 | 
			
		||||
/qapi/qapi-visit-net.[ch]
 | 
			
		||||
/qapi/qapi-visit-rocker.[ch]
 | 
			
		||||
/qapi/qapi-visit-run-state.[ch]
 | 
			
		||||
/qapi/qapi-visit-sockets.[ch]
 | 
			
		||||
/qapi/qapi-visit-tpm.[ch]
 | 
			
		||||
/qapi/qapi-visit-trace.[ch]
 | 
			
		||||
/qapi/qapi-visit-transaction.[ch]
 | 
			
		||||
/qapi/qapi-visit-ui.[ch]
 | 
			
		||||
/qapi/qapi-visit.[ch]
 | 
			
		||||
/qapi/qapi-doc.texi
 | 
			
		||||
/qemu-doc.html
 | 
			
		||||
/qemu-doc.info
 | 
			
		||||
/qemu-doc.txt
 | 
			
		||||
/qemu-edid
 | 
			
		||||
/qemu-img
 | 
			
		||||
/qemu-nbd
 | 
			
		||||
/qemu-options.def
 | 
			
		||||
@@ -92,7 +151,6 @@
 | 
			
		||||
.sdk
 | 
			
		||||
*.gcda
 | 
			
		||||
*.gcno
 | 
			
		||||
*.gcov
 | 
			
		||||
/pc-bios/bios-pq/status
 | 
			
		||||
/pc-bios/vgabios-pq/status
 | 
			
		||||
/pc-bios/optionrom/linuxboot.asm
 | 
			
		||||
@@ -148,4 +206,3 @@ trace-dtrace-root.h
 | 
			
		||||
trace-dtrace-root.dtrace
 | 
			
		||||
trace-ust-all.h
 | 
			
		||||
trace-ust-all.c
 | 
			
		||||
/target/arm/decode-sve.inc.c
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										39
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										39
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							@@ -1,51 +1,48 @@
 | 
			
		||||
[submodule "roms/vgabios"]
 | 
			
		||||
	path = roms/vgabios
 | 
			
		||||
	url = git://git.qemu-project.org/vgabios.git/
 | 
			
		||||
[submodule "roms/seabios"]
 | 
			
		||||
	path = roms/seabios
 | 
			
		||||
	url = https://git.qemu.org/git/seabios.git/
 | 
			
		||||
	url = git://git.qemu-project.org/seabios.git/
 | 
			
		||||
[submodule "roms/SLOF"]
 | 
			
		||||
	path = roms/SLOF
 | 
			
		||||
	url = https://git.qemu.org/git/SLOF.git
 | 
			
		||||
	url = git://git.qemu-project.org/SLOF.git
 | 
			
		||||
[submodule "roms/ipxe"]
 | 
			
		||||
	path = roms/ipxe
 | 
			
		||||
	url = https://git.qemu.org/git/ipxe.git
 | 
			
		||||
	url = git://git.qemu-project.org/ipxe.git
 | 
			
		||||
[submodule "roms/openbios"]
 | 
			
		||||
	path = roms/openbios
 | 
			
		||||
	url = https://git.qemu.org/git/openbios.git
 | 
			
		||||
	url = git://git.qemu-project.org/openbios.git
 | 
			
		||||
[submodule "roms/openhackware"]
 | 
			
		||||
	path = roms/openhackware
 | 
			
		||||
	url = https://git.qemu.org/git/openhackware.git
 | 
			
		||||
	url = git://git.qemu-project.org/openhackware.git
 | 
			
		||||
[submodule "roms/qemu-palcode"]
 | 
			
		||||
	path = roms/qemu-palcode
 | 
			
		||||
	url = https://git.qemu.org/git/qemu-palcode.git
 | 
			
		||||
	url = git://github.com/rth7680/qemu-palcode.git
 | 
			
		||||
[submodule "roms/sgabios"]
 | 
			
		||||
	path = roms/sgabios
 | 
			
		||||
	url = https://git.qemu.org/git/sgabios.git
 | 
			
		||||
	url = git://git.qemu-project.org/sgabios.git
 | 
			
		||||
[submodule "dtc"]
 | 
			
		||||
	path = dtc
 | 
			
		||||
	url = https://git.qemu.org/git/dtc.git
 | 
			
		||||
	url = git://git.qemu-project.org/dtc.git
 | 
			
		||||
[submodule "roms/u-boot"]
 | 
			
		||||
	path = roms/u-boot
 | 
			
		||||
	url = https://git.qemu.org/git/u-boot.git
 | 
			
		||||
	url = git://git.qemu-project.org/u-boot.git
 | 
			
		||||
[submodule "roms/skiboot"]
 | 
			
		||||
	path = roms/skiboot
 | 
			
		||||
	url = https://git.qemu.org/git/skiboot.git
 | 
			
		||||
	url = git://git.qemu.org/skiboot.git
 | 
			
		||||
[submodule "roms/QemuMacDrivers"]
 | 
			
		||||
	path = roms/QemuMacDrivers
 | 
			
		||||
	url = https://git.qemu.org/git/QemuMacDrivers.git
 | 
			
		||||
	url = git://git.qemu.org/QemuMacDrivers.git
 | 
			
		||||
[submodule "ui/keycodemapdb"]
 | 
			
		||||
	path = ui/keycodemapdb
 | 
			
		||||
	url = https://git.qemu.org/git/keycodemapdb.git
 | 
			
		||||
	url = git://git.qemu.org/keycodemapdb.git
 | 
			
		||||
[submodule "capstone"]
 | 
			
		||||
	path = capstone
 | 
			
		||||
	url = https://git.qemu.org/git/capstone.git
 | 
			
		||||
	url = git://git.qemu.org/capstone.git
 | 
			
		||||
[submodule "roms/seabios-hppa"]
 | 
			
		||||
	path = roms/seabios-hppa
 | 
			
		||||
	url = https://github.com/hdeller/seabios-hppa.git
 | 
			
		||||
	url = git://github.com/hdeller/seabios-hppa.git
 | 
			
		||||
[submodule "roms/u-boot-sam460ex"]
 | 
			
		||||
	path = roms/u-boot-sam460ex
 | 
			
		||||
	url = https://git.qemu.org/git/u-boot-sam460ex.git
 | 
			
		||||
[submodule "tests/fp/berkeley-testfloat-3"]
 | 
			
		||||
	path = tests/fp/berkeley-testfloat-3
 | 
			
		||||
	url = https://github.com/cota/berkeley-testfloat-3
 | 
			
		||||
[submodule "tests/fp/berkeley-softfloat-3"]
 | 
			
		||||
	path = tests/fp/berkeley-softfloat-3
 | 
			
		||||
	url = https://github.com/cota/berkeley-softfloat-3
 | 
			
		||||
	url = git://github.com/zbalaton/u-boot-sam460ex
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								.mailmap
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								.mailmap
									
									
									
									
									
								
							@@ -1,7 +1,6 @@
 | 
			
		||||
# This mailmap fixes up author names/addresses.
 | 
			
		||||
 | 
			
		||||
# The first section translates weird addresses from the original git import
 | 
			
		||||
# into proper addresses so that they are counted properly by git shortlog.
 | 
			
		||||
# This mailmap just translates the weird addresses from the original import into git
 | 
			
		||||
# into proper addresses so that they are counted properly in git shortlog output.
 | 
			
		||||
#
 | 
			
		||||
Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
 | 
			
		||||
Anthony Liguori <anthony@codemonkey.ws> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162>
 | 
			
		||||
Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com>
 | 
			
		||||
@@ -12,28 +11,14 @@ Fabrice Bellard <fabrice@bellard.org> bellard <bellard@c046a42c-6fe2-441c-8c8c-7
 | 
			
		||||
James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com>
 | 
			
		||||
Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
 | 
			
		||||
Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
 | 
			
		||||
Yongbok Kim <yongbok.kim@mips.com> <yongbok.kim@imgtec.com>
 | 
			
		||||
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@mips.com>
 | 
			
		||||
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@imgtec.com>
 | 
			
		||||
Paul Burton <pburton@wavecomp.com> <paul.burton@mips.com>
 | 
			
		||||
Paul Burton <pburton@wavecomp.com> <paul.burton@imgtec.com>
 | 
			
		||||
Paul Burton <pburton@wavecomp.com> <paul@archlinuxmips.org>
 | 
			
		||||
Paul Burton <paul.burton@mips.com> <paul.burton@imgtec.com>
 | 
			
		||||
Paul Burton <paul.burton@mips.com> <paul@archlinuxmips.org>
 | 
			
		||||
Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
 | 
			
		||||
malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162>
 | 
			
		||||
 | 
			
		||||
# There is also a:
 | 
			
		||||
#    (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162>
 | 
			
		||||
# for the cvs2svn initialization commit e63c3dc74bf.
 | 
			
		||||
 | 
			
		||||
# Next, translate a few commits where mailman rewrote the From: line due
 | 
			
		||||
# to strict SPF, although we prefer to avoid adding more entries like that.
 | 
			
		||||
Ed Swierk <eswierk@skyportsystems.com> Ed Swierk via Qemu-devel <qemu-devel@nongnu.org>
 | 
			
		||||
Ian McKellar <ianloic@google.com> Ian McKellar via Qemu-devel <qemu-devel@nongnu.org>
 | 
			
		||||
Julia Suvorova <jusual@mail.ru> Julia Suvorova via Qemu-devel <qemu-devel@nongnu.org>
 | 
			
		||||
Justin Terry (VM) <juterry@microsoft.com> Justin Terry (VM) via Qemu-devel <qemu-devel@nongnu.org>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Also list preferred name forms where people have changed their
 | 
			
		||||
# git author config, or had utf8/latin1 encoding issues.
 | 
			
		||||
# git author config
 | 
			
		||||
Daniel P. Berrangé <berrange@redhat.com>
 | 
			
		||||
Reimar Döffinger <Reimar.Doeffinger@gmx.de>
 | 
			
		||||
 
 | 
			
		||||
@@ -7,11 +7,10 @@ env:
 | 
			
		||||
  matrix:
 | 
			
		||||
    - IMAGE=debian-amd64
 | 
			
		||||
      TARGET_LIST=x86_64-softmmu,x86_64-linux-user
 | 
			
		||||
    # currently disabled as the mxe.cc repos are down
 | 
			
		||||
    # - IMAGE=debian-win32-cross
 | 
			
		||||
    #   TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu
 | 
			
		||||
    # - IMAGE=debian-win64-cross
 | 
			
		||||
    #   TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu
 | 
			
		||||
    - IMAGE=debian-win32-cross
 | 
			
		||||
      TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu
 | 
			
		||||
    - IMAGE=debian-win64-cross
 | 
			
		||||
      TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu
 | 
			
		||||
    - IMAGE=debian-armel-cross
 | 
			
		||||
      TARGET_LIST=arm-softmmu,arm-linux-user,armeb-linux-user
 | 
			
		||||
    - IMAGE=debian-armhf-cross
 | 
			
		||||
@@ -36,5 +35,13 @@ build:
 | 
			
		||||
    options: "-e HOME=/root"
 | 
			
		||||
  ci:
 | 
			
		||||
    - unset CC
 | 
			
		||||
    # some targets require newer up to date packages, for example TARGET_LIST matching
 | 
			
		||||
    # aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu)
 | 
			
		||||
    # see the configure script:
 | 
			
		||||
    #    error_exit "DTC (libfdt) version >= 1.4.2 not present. Your options:"
 | 
			
		||||
    #    "  (1) Preferred: Install the DTC (libfdt) devel package"
 | 
			
		||||
    #    "  (2) Fetch the DTC submodule, using:"
 | 
			
		||||
    #    "      git submodule update --init dtc"
 | 
			
		||||
    - dpkg --compare-versions `dpkg-query --showformat='${Version}' --show libfdt-dev` ge 1.4.2 || git submodule update --init dtc
 | 
			
		||||
    - ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST}
 | 
			
		||||
    - make -j$(($(getconf _NPROCESSORS_ONLN) + 1))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										258
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										258
									
								
								.travis.yml
									
									
									
									
									
								
							@@ -1,13 +1,10 @@
 | 
			
		||||
# The current Travis default is a VM based 16.04 Xenial on GCE
 | 
			
		||||
# Additional builds with specific requirements for a full VM need to
 | 
			
		||||
# be added as additional matrix: entries later on
 | 
			
		||||
dist: xenial
 | 
			
		||||
sudo: false
 | 
			
		||||
language: c
 | 
			
		||||
python:
 | 
			
		||||
  - "2.6"
 | 
			
		||||
compiler:
 | 
			
		||||
  - gcc
 | 
			
		||||
cache: ccache
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
addons:
 | 
			
		||||
  apt:
 | 
			
		||||
    packages:
 | 
			
		||||
@@ -34,17 +31,9 @@ addons:
 | 
			
		||||
      - libssh2-1-dev
 | 
			
		||||
      - liburcu-dev
 | 
			
		||||
      - libusb-1.0-0-dev
 | 
			
		||||
      - libvte-2.91-dev
 | 
			
		||||
      - libvte-2.90-dev
 | 
			
		||||
      - sparse
 | 
			
		||||
      - uuid-dev
 | 
			
		||||
      - gcovr
 | 
			
		||||
  homebrew:
 | 
			
		||||
    packages:
 | 
			
		||||
      - libffi
 | 
			
		||||
      - gettext
 | 
			
		||||
      - glib
 | 
			
		||||
      - pixman
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu
 | 
			
		||||
# to prevent IRC notifications from forks. This was created using:
 | 
			
		||||
@@ -55,158 +44,130 @@ notifications:
 | 
			
		||||
      - secure: "F7GDRgjuOo5IUyRLqSkmDL7kvdU4UcH3Lm/W2db2JnDHTGCqgEdaYEYKciyCLZ57vOTsTsOgesN8iUT7hNHBd1KWKjZe9KDTZWppWRYVwAwQMzVeSOsbbU4tRoJ6Pp+3qhH1Z0eGYR9ZgKYAoTumDFgSAYRp4IscKS8jkoedOqM="
 | 
			
		||||
    on_success: change
 | 
			
		||||
    on_failure: always
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  global:
 | 
			
		||||
    - SRC_DIR="."
 | 
			
		||||
    - BUILD_DIR="."
 | 
			
		||||
    - TEST_CMD="make check -j3 V=1"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - TEST_CMD="make check"
 | 
			
		||||
    - MAKEFLAGS="-j3"
 | 
			
		||||
  matrix:
 | 
			
		||||
    - CONFIG=""
 | 
			
		||||
    - CONFIG="--enable-debug --enable-debug-tcg --enable-trace-backends=log"
 | 
			
		||||
    - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb"
 | 
			
		||||
    - CONFIG="--enable-modules --disable-linux-user"
 | 
			
		||||
    - CONFIG="--with-coroutine=ucontext --disable-linux-user"
 | 
			
		||||
    - CONFIG="--with-coroutine=sigaltstack --disable-linux-user"
 | 
			
		||||
git:
 | 
			
		||||
  # we want to do this ourselves
 | 
			
		||||
  submodules: false
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
before_install:
 | 
			
		||||
  - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update ; fi
 | 
			
		||||
  - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install libffi gettext glib pixman ; fi
 | 
			
		||||
  - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
 | 
			
		||||
  - git submodule update --init --recursive
 | 
			
		||||
before_script:
 | 
			
		||||
  - mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
 | 
			
		||||
  - ${SRC_DIR}/configure ${CONFIG} || { cat config.log && exit 1; }
 | 
			
		||||
  - ./configure ${CONFIG}
 | 
			
		||||
script:
 | 
			
		||||
  - make -j3 && ${TEST_CMD}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  - make ${MAKEFLAGS} && ${TEST_CMD}
 | 
			
		||||
matrix:
 | 
			
		||||
  include:
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--disable-system"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--disable-user"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--enable-debug --enable-debug-tcg"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb --disable-user"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--enable-modules --disable-linux-user"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--with-coroutine=ucontext --disable-linux-user"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--with-coroutine=sigaltstack --disable-linux-user"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # Test out-of-tree builds
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--enable-debug --enable-debug-tcg"
 | 
			
		||||
        - BUILD_DIR="out-of-tree/build/dir" SRC_DIR="../../.."
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # Test with Clang for compile portability (Travis uses clang-5.0)
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--disable-system"
 | 
			
		||||
    # Test with CLang for compile portability
 | 
			
		||||
    - env: CONFIG=""
 | 
			
		||||
      compiler: clang
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--disable-user"
 | 
			
		||||
      compiler: clang
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # gprof/gcov are GCC features
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
 | 
			
		||||
      after_success:
 | 
			
		||||
        - ${SRC_DIR}/scripts/travis/coverage-summary.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - env: CONFIG="--enable-gprof --enable-gcov --disable-pie"
 | 
			
		||||
      compiler: gcc
 | 
			
		||||
    # We manually include builds which we disable "make check" for
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--enable-debug --enable-tcg-interpreter"
 | 
			
		||||
        - TEST_CMD=""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # We don't need to exercise every backend with every front-end
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--enable-trace-backends=log,simple,syslog --disable-system"
 | 
			
		||||
        - TEST_CMD=""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu"
 | 
			
		||||
        - TEST_CMD=""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu"
 | 
			
		||||
        - TEST_CMD=""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--disable-tcg"
 | 
			
		||||
        - TEST_CMD=""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # MacOSX builds
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
 | 
			
		||||
    - env: CONFIG="--enable-debug --enable-tcg-interpreter"
 | 
			
		||||
           TEST_CMD=""
 | 
			
		||||
      compiler: gcc
 | 
			
		||||
    - env: CONFIG="--enable-trace-backends=simple"
 | 
			
		||||
           TEST_CMD=""
 | 
			
		||||
      compiler: gcc
 | 
			
		||||
    - env: CONFIG="--enable-trace-backends=ftrace"
 | 
			
		||||
           TEST_CMD=""
 | 
			
		||||
      compiler: gcc
 | 
			
		||||
    - env: CONFIG="--enable-trace-backends=ust"
 | 
			
		||||
           TEST_CMD=""
 | 
			
		||||
      compiler: gcc
 | 
			
		||||
    - env: CONFIG="--disable-tcg"
 | 
			
		||||
           TEST_CMD=""
 | 
			
		||||
      compiler: gcc
 | 
			
		||||
    - env: CONFIG=""
 | 
			
		||||
      os: osx
 | 
			
		||||
      osx_image: xcode9.4
 | 
			
		||||
      compiler: clang
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu"
 | 
			
		||||
      os: osx
 | 
			
		||||
      osx_image: xcode10
 | 
			
		||||
      compiler: clang
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # Python builds
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--target-list=x86_64-softmmu"
 | 
			
		||||
      language: python
 | 
			
		||||
    # Plain Trusty System Build
 | 
			
		||||
    - env: CONFIG="--disable-linux-user"
 | 
			
		||||
      sudo: required
 | 
			
		||||
      addons:
 | 
			
		||||
      dist: trusty
 | 
			
		||||
      compiler: gcc
 | 
			
		||||
      before_install:
 | 
			
		||||
        - sudo apt-get update -qq
 | 
			
		||||
        - sudo apt-get build-dep -qq qemu
 | 
			
		||||
        - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
 | 
			
		||||
        - git submodule update --init --recursive
 | 
			
		||||
    # Plain Trusty Linux User Build
 | 
			
		||||
    - env: CONFIG="--disable-system"
 | 
			
		||||
      sudo: required
 | 
			
		||||
      addons:
 | 
			
		||||
      dist: trusty
 | 
			
		||||
      compiler: gcc
 | 
			
		||||
      before_install:
 | 
			
		||||
        - sudo apt-get update -qq
 | 
			
		||||
        - sudo apt-get build-dep -qq qemu
 | 
			
		||||
        - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
 | 
			
		||||
        - git submodule update --init --recursive
 | 
			
		||||
    # Trusty System build with latest stable clang & python 3.0
 | 
			
		||||
    - sudo: required
 | 
			
		||||
      addons:
 | 
			
		||||
      dist: trusty
 | 
			
		||||
      language: generic
 | 
			
		||||
      compiler: none
 | 
			
		||||
      python:
 | 
			
		||||
        - "3.4"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--target-list=x86_64-softmmu"
 | 
			
		||||
      language: python
 | 
			
		||||
        - "3.0"
 | 
			
		||||
      env:
 | 
			
		||||
        - COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9
 | 
			
		||||
        - CONFIG="--disable-linux-user --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3"
 | 
			
		||||
      before_install:
 | 
			
		||||
        - wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
 | 
			
		||||
        - sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main'
 | 
			
		||||
        - sudo apt-get update -qq
 | 
			
		||||
        - sudo apt-get install -qq -y clang-3.9
 | 
			
		||||
        - sudo apt-get build-dep -qq qemu
 | 
			
		||||
        - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
 | 
			
		||||
        - git submodule update --init --recursive
 | 
			
		||||
      before_script:
 | 
			
		||||
        - ./configure ${CONFIG} || cat config.log
 | 
			
		||||
    # Trusty Linux User build with latest stable clang & python 3.6
 | 
			
		||||
    - sudo: required
 | 
			
		||||
      addons:
 | 
			
		||||
      dist: trusty
 | 
			
		||||
      language: generic
 | 
			
		||||
      compiler: none
 | 
			
		||||
      python:
 | 
			
		||||
        - "3.6"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # Acceptance (Functional) tests
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu"
 | 
			
		||||
        - TEST_CMD="make AVOCADO_SHOW=app check-acceptance"
 | 
			
		||||
      addons:
 | 
			
		||||
        apt:
 | 
			
		||||
          packages:
 | 
			
		||||
            - python3-pip
 | 
			
		||||
            - python3.5-venv
 | 
			
		||||
      env:
 | 
			
		||||
        - COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9
 | 
			
		||||
        - CONFIG="--disable-system --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3"
 | 
			
		||||
      before_install:
 | 
			
		||||
        - wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
 | 
			
		||||
        - sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main'
 | 
			
		||||
        - sudo apt-get update -qq
 | 
			
		||||
        - sudo apt-get install -qq -y clang-3.9
 | 
			
		||||
        - sudo apt-get build-dep -qq qemu
 | 
			
		||||
        - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
 | 
			
		||||
        - git submodule update --init --recursive
 | 
			
		||||
      before_script:
 | 
			
		||||
        - ./configure ${CONFIG} || cat config.log
 | 
			
		||||
    # Using newer GCC with sanitizers
 | 
			
		||||
    - addons:
 | 
			
		||||
        apt:
 | 
			
		||||
          update: true
 | 
			
		||||
          sources:
 | 
			
		||||
            # PPAs for newer toolchains
 | 
			
		||||
            - ubuntu-toolchain-r-test
 | 
			
		||||
          packages:
 | 
			
		||||
            # Extra toolchains
 | 
			
		||||
            - gcc-7
 | 
			
		||||
            - g++-7
 | 
			
		||||
            - gcc-5
 | 
			
		||||
            - g++-5
 | 
			
		||||
            # Build dependencies
 | 
			
		||||
            - libaio-dev
 | 
			
		||||
            - libattr1-dev
 | 
			
		||||
@@ -229,19 +190,14 @@ matrix:
 | 
			
		||||
            - libssh2-1-dev
 | 
			
		||||
            - liburcu-dev
 | 
			
		||||
            - libusb-1.0-0-dev
 | 
			
		||||
            - libvte-2.91-dev
 | 
			
		||||
            - libvte-2.90-dev
 | 
			
		||||
            - sparse
 | 
			
		||||
            - uuid-dev
 | 
			
		||||
      language: generic
 | 
			
		||||
      compiler: none
 | 
			
		||||
      env:
 | 
			
		||||
        - COMPILER_NAME=gcc CXX=g++-7 CC=gcc-7
 | 
			
		||||
        - CONFIG="--cc=gcc-7 --cxx=g++-7 --disable-pie --disable-linux-user"
 | 
			
		||||
        - COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5
 | 
			
		||||
        - CONFIG="--cc=gcc-5 --cxx=g++-5 --disable-pie --disable-linux-user"
 | 
			
		||||
        - TEST_CMD=""
 | 
			
		||||
      before_script:
 | 
			
		||||
        - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || { cat config.log && exit 1; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - env:
 | 
			
		||||
        - CONFIG="--disable-system --disable-docs"
 | 
			
		||||
        - TEST_CMD="make -j3 check-tcg V=1"
 | 
			
		||||
        - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || cat config.log
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								CODING_STYLE
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								CODING_STYLE
									
									
									
									
									
								
							@@ -124,23 +124,6 @@ We use traditional C-style /* */ comments and avoid // comments.
 | 
			
		||||
Rationale: The // form is valid in C99, so this is purely a matter of
 | 
			
		||||
consistency of style. The checkpatch script will warn you about this.
 | 
			
		||||
 | 
			
		||||
Multiline comment blocks should have a row of stars on the left,
 | 
			
		||||
and the initial /* and terminating */ both on their own lines:
 | 
			
		||||
    /*
 | 
			
		||||
     * like
 | 
			
		||||
     * this
 | 
			
		||||
     */
 | 
			
		||||
This is the same format required by the Linux kernel coding style.
 | 
			
		||||
 | 
			
		||||
(Some of the existing comments in the codebase use the GNU Coding
 | 
			
		||||
Standards form which does not have stars on the left, or other
 | 
			
		||||
variations; avoid these when writing new comments, but don't worry
 | 
			
		||||
about converting to the preferred form unless you're editing that
 | 
			
		||||
comment anyway.)
 | 
			
		||||
 | 
			
		||||
Rationale: Consistency, and ease of visually picking out a multiline
 | 
			
		||||
comment from the surrounding code.
 | 
			
		||||
 | 
			
		||||
8. trace-events style
 | 
			
		||||
 | 
			
		||||
8.1 0x prefix
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
		       Version 2.1, February 1999
 | 
			
		||||
 | 
			
		||||
 Copyright (C) 1991, 1999 Free Software Foundation, Inc.
 | 
			
		||||
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
	51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 Everyone is permitted to copy and distribute verbatim copies
 | 
			
		||||
 of this license document, but changing it is not allowed.
 | 
			
		||||
 | 
			
		||||
@@ -476,7 +476,7 @@ convey the exclusion of warranty; and each file should have at least the
 | 
			
		||||
    This library is free software; you can redistribute it and/or
 | 
			
		||||
    modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
    License as published by the Free Software Foundation; either
 | 
			
		||||
    version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
    version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
    This library is distributed in the hope that it will be useful,
 | 
			
		||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
@@ -485,7 +485,7 @@ convey the exclusion of warranty; and each file should have at least the
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
    License along with this library; if not, write to the Free Software
 | 
			
		||||
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 | 
			
		||||
Also add information on how to contact you by electronic and paper mail.
 | 
			
		||||
 | 
			
		||||
@@ -500,3 +500,5 @@ necessary.  Here is a sample; alter the names:
 | 
			
		||||
  Ty Coon, President of Vice
 | 
			
		||||
 | 
			
		||||
That's all there is to it!
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										270
									
								
								COPYING.PYTHON
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										270
									
								
								COPYING.PYTHON
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,270 @@
 | 
			
		||||
A. HISTORY OF THE SOFTWARE
 | 
			
		||||
==========================
 | 
			
		||||
 | 
			
		||||
Python was created in the early 1990s by Guido van Rossum at Stichting
 | 
			
		||||
Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
 | 
			
		||||
as a successor of a language called ABC.  Guido remains Python's
 | 
			
		||||
principal author, although it includes many contributions from others.
 | 
			
		||||
 | 
			
		||||
In 1995, Guido continued his work on Python at the Corporation for
 | 
			
		||||
National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
 | 
			
		||||
in Reston, Virginia where he released several versions of the
 | 
			
		||||
software.
 | 
			
		||||
 | 
			
		||||
In May 2000, Guido and the Python core development team moved to
 | 
			
		||||
BeOpen.com to form the BeOpen PythonLabs team.  In October of the same
 | 
			
		||||
year, the PythonLabs team moved to Digital Creations (now Zope
 | 
			
		||||
Corporation, see http://www.zope.com).  In 2001, the Python Software
 | 
			
		||||
Foundation (PSF, see http://www.python.org/psf/) was formed, a
 | 
			
		||||
non-profit organization created specifically to own Python-related
 | 
			
		||||
Intellectual Property.  Zope Corporation is a sponsoring member of
 | 
			
		||||
the PSF.
 | 
			
		||||
 | 
			
		||||
All Python releases are Open Source (see http://www.opensource.org for
 | 
			
		||||
the Open Source Definition).  Historically, most, but not all, Python
 | 
			
		||||
releases have also been GPL-compatible; the table below summarizes
 | 
			
		||||
the various releases.
 | 
			
		||||
 | 
			
		||||
    Release         Derived     Year        Owner       GPL-
 | 
			
		||||
                    from                                compatible? (1)
 | 
			
		||||
 | 
			
		||||
    0.9.0 thru 1.2              1991-1995   CWI         yes
 | 
			
		||||
    1.3 thru 1.5.2  1.2         1995-1999   CNRI        yes
 | 
			
		||||
    1.6             1.5.2       2000        CNRI        no
 | 
			
		||||
    2.0             1.6         2000        BeOpen.com  no
 | 
			
		||||
    1.6.1           1.6         2001        CNRI        yes (2)
 | 
			
		||||
    2.1             2.0+1.6.1   2001        PSF         no
 | 
			
		||||
    2.0.1           2.0+1.6.1   2001        PSF         yes
 | 
			
		||||
    2.1.1           2.1+2.0.1   2001        PSF         yes
 | 
			
		||||
    2.2             2.1.1       2001        PSF         yes
 | 
			
		||||
    2.1.2           2.1.1       2002        PSF         yes
 | 
			
		||||
    2.1.3           2.1.2       2002        PSF         yes
 | 
			
		||||
    2.2.1           2.2         2002        PSF         yes
 | 
			
		||||
    2.2.2           2.2.1       2002        PSF         yes
 | 
			
		||||
    2.2.3           2.2.2       2003        PSF         yes
 | 
			
		||||
    2.3             2.2.2       2002-2003   PSF         yes
 | 
			
		||||
    2.3.1           2.3         2002-2003   PSF         yes
 | 
			
		||||
    2.3.2           2.3.1       2002-2003   PSF         yes
 | 
			
		||||
    2.3.3           2.3.2       2002-2003   PSF         yes
 | 
			
		||||
    2.3.4           2.3.3       2004        PSF         yes
 | 
			
		||||
    2.3.5           2.3.4       2005        PSF         yes
 | 
			
		||||
    2.4             2.3         2004        PSF         yes
 | 
			
		||||
    2.4.1           2.4         2005        PSF         yes
 | 
			
		||||
    2.4.2           2.4.1       2005        PSF         yes
 | 
			
		||||
    2.4.3           2.4.2       2006        PSF         yes
 | 
			
		||||
    2.5             2.4         2006        PSF         yes
 | 
			
		||||
    2.7             2.6         2010        PSF         yes
 | 
			
		||||
 | 
			
		||||
Footnotes:
 | 
			
		||||
 | 
			
		||||
(1) GPL-compatible doesn't mean that we're distributing Python under
 | 
			
		||||
    the GPL.  All Python licenses, unlike the GPL, let you distribute
 | 
			
		||||
    a modified version without making your changes open source.  The
 | 
			
		||||
    GPL-compatible licenses make it possible to combine Python with
 | 
			
		||||
    other software that is released under the GPL; the others don't.
 | 
			
		||||
 | 
			
		||||
(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
 | 
			
		||||
    because its license has a choice of law clause.  According to
 | 
			
		||||
    CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
 | 
			
		||||
    is "not incompatible" with the GPL.
 | 
			
		||||
 | 
			
		||||
Thanks to the many outside volunteers who have worked under Guido's
 | 
			
		||||
direction to make these releases possible.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
 | 
			
		||||
===============================================================
 | 
			
		||||
 | 
			
		||||
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
 | 
			
		||||
--------------------------------------------
 | 
			
		||||
 | 
			
		||||
1. This LICENSE AGREEMENT is between the Python Software Foundation
 | 
			
		||||
("PSF"), and the Individual or Organization ("Licensee") accessing and
 | 
			
		||||
otherwise using this software ("Python") in source or binary form and
 | 
			
		||||
its associated documentation.
 | 
			
		||||
 | 
			
		||||
2. Subject to the terms and conditions of this License Agreement, PSF
 | 
			
		||||
hereby grants Licensee a nonexclusive, royalty-free, world-wide
 | 
			
		||||
license to reproduce, analyze, test, perform and/or display publicly,
 | 
			
		||||
prepare derivative works, distribute, and otherwise use Python
 | 
			
		||||
alone or in any derivative version, provided, however, that PSF's
 | 
			
		||||
License Agreement and PSF's notice of copyright, i.e., "Copyright (c)
 | 
			
		||||
2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation; All Rights
 | 
			
		||||
Reserved" are retained in Python alone or in any derivative version 
 | 
			
		||||
prepared by Licensee.
 | 
			
		||||
 | 
			
		||||
3. In the event Licensee prepares a derivative work that is based on
 | 
			
		||||
or incorporates Python or any part thereof, and wants to make
 | 
			
		||||
the derivative work available to others as provided herein, then
 | 
			
		||||
Licensee hereby agrees to include in any such work a brief summary of
 | 
			
		||||
the changes made to Python.
 | 
			
		||||
 | 
			
		||||
4. PSF is making Python available to Licensee on an "AS IS"
 | 
			
		||||
basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
 | 
			
		||||
IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
 | 
			
		||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
 | 
			
		||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
 | 
			
		||||
INFRINGE ANY THIRD PARTY RIGHTS.
 | 
			
		||||
 | 
			
		||||
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
 | 
			
		||||
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
 | 
			
		||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
 | 
			
		||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
 | 
			
		||||
 | 
			
		||||
6. This License Agreement will automatically terminate upon a material
 | 
			
		||||
breach of its terms and conditions.
 | 
			
		||||
 | 
			
		||||
7. Nothing in this License Agreement shall be deemed to create any
 | 
			
		||||
relationship of agency, partnership, or joint venture between PSF and
 | 
			
		||||
Licensee.  This License Agreement does not grant permission to use PSF
 | 
			
		||||
trademarks or trade name in a trademark sense to endorse or promote
 | 
			
		||||
products or services of Licensee, or any third party.
 | 
			
		||||
 | 
			
		||||
8. By copying, installing or otherwise using Python, Licensee
 | 
			
		||||
agrees to be bound by the terms and conditions of this License
 | 
			
		||||
Agreement.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
 | 
			
		||||
-------------------------------------------
 | 
			
		||||
 | 
			
		||||
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
 | 
			
		||||
 | 
			
		||||
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
 | 
			
		||||
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
 | 
			
		||||
Individual or Organization ("Licensee") accessing and otherwise using
 | 
			
		||||
this software in source or binary form and its associated
 | 
			
		||||
documentation ("the Software").
 | 
			
		||||
 | 
			
		||||
2. Subject to the terms and conditions of this BeOpen Python License
 | 
			
		||||
Agreement, BeOpen hereby grants Licensee a non-exclusive,
 | 
			
		||||
royalty-free, world-wide license to reproduce, analyze, test, perform
 | 
			
		||||
and/or display publicly, prepare derivative works, distribute, and
 | 
			
		||||
otherwise use the Software alone or in any derivative version,
 | 
			
		||||
provided, however, that the BeOpen Python License is retained in the
 | 
			
		||||
Software, alone or in any derivative version prepared by Licensee.
 | 
			
		||||
 | 
			
		||||
3. BeOpen is making the Software available to Licensee on an "AS IS"
 | 
			
		||||
basis.  BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
 | 
			
		||||
IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
 | 
			
		||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
 | 
			
		||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
 | 
			
		||||
INFRINGE ANY THIRD PARTY RIGHTS.
 | 
			
		||||
 | 
			
		||||
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
 | 
			
		||||
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
 | 
			
		||||
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
 | 
			
		||||
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
 | 
			
		||||
 | 
			
		||||
5. This License Agreement will automatically terminate upon a material
 | 
			
		||||
breach of its terms and conditions.
 | 
			
		||||
 | 
			
		||||
6. This License Agreement shall be governed by and interpreted in all
 | 
			
		||||
respects by the law of the State of California, excluding conflict of
 | 
			
		||||
law provisions.  Nothing in this License Agreement shall be deemed to
 | 
			
		||||
create any relationship of agency, partnership, or joint venture
 | 
			
		||||
between BeOpen and Licensee.  This License Agreement does not grant
 | 
			
		||||
permission to use BeOpen trademarks or trade names in a trademark
 | 
			
		||||
sense to endorse or promote products or services of Licensee, or any
 | 
			
		||||
third party.  As an exception, the "BeOpen Python" logos available at
 | 
			
		||||
http://www.pythonlabs.com/logos.html may be used according to the
 | 
			
		||||
permissions granted on that web page.
 | 
			
		||||
 | 
			
		||||
7. By copying, installing or otherwise using the software, Licensee
 | 
			
		||||
agrees to be bound by the terms and conditions of this License
 | 
			
		||||
Agreement.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
 | 
			
		||||
---------------------------------------
 | 
			
		||||
 | 
			
		||||
1. This LICENSE AGREEMENT is between the Corporation for National
 | 
			
		||||
Research Initiatives, having an office at 1895 Preston White Drive,
 | 
			
		||||
Reston, VA 20191 ("CNRI"), and the Individual or Organization
 | 
			
		||||
("Licensee") accessing and otherwise using Python 1.6.1 software in
 | 
			
		||||
source or binary form and its associated documentation.
 | 
			
		||||
 | 
			
		||||
2. Subject to the terms and conditions of this License Agreement, CNRI
 | 
			
		||||
hereby grants Licensee a nonexclusive, royalty-free, world-wide
 | 
			
		||||
license to reproduce, analyze, test, perform and/or display publicly,
 | 
			
		||||
prepare derivative works, distribute, and otherwise use Python 1.6.1
 | 
			
		||||
alone or in any derivative version, provided, however, that CNRI's
 | 
			
		||||
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
 | 
			
		||||
1995-2001 Corporation for National Research Initiatives; All Rights
 | 
			
		||||
Reserved" are retained in Python 1.6.1 alone or in any derivative
 | 
			
		||||
version prepared by Licensee.  Alternately, in lieu of CNRI's License
 | 
			
		||||
Agreement, Licensee may substitute the following text (omitting the
 | 
			
		||||
quotes): "Python 1.6.1 is made available subject to the terms and
 | 
			
		||||
conditions in CNRI's License Agreement.  This Agreement together with
 | 
			
		||||
Python 1.6.1 may be located on the Internet using the following
 | 
			
		||||
unique, persistent identifier (known as a handle): 1895.22/1013.  This
 | 
			
		||||
Agreement may also be obtained from a proxy server on the Internet
 | 
			
		||||
using the following URL: http://hdl.handle.net/1895.22/1013".
 | 
			
		||||
 | 
			
		||||
3. In the event Licensee prepares a derivative work that is based on
 | 
			
		||||
or incorporates Python 1.6.1 or any part thereof, and wants to make
 | 
			
		||||
the derivative work available to others as provided herein, then
 | 
			
		||||
Licensee hereby agrees to include in any such work a brief summary of
 | 
			
		||||
the changes made to Python 1.6.1.
 | 
			
		||||
 | 
			
		||||
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
 | 
			
		||||
basis.  CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
 | 
			
		||||
IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
 | 
			
		||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
 | 
			
		||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
 | 
			
		||||
INFRINGE ANY THIRD PARTY RIGHTS.
 | 
			
		||||
 | 
			
		||||
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
 | 
			
		||||
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
 | 
			
		||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
 | 
			
		||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
 | 
			
		||||
 | 
			
		||||
6. This License Agreement will automatically terminate upon a material
 | 
			
		||||
breach of its terms and conditions.
 | 
			
		||||
 | 
			
		||||
7. This License Agreement shall be governed by the federal
 | 
			
		||||
intellectual property law of the United States, including without
 | 
			
		||||
limitation the federal copyright law, and, to the extent such
 | 
			
		||||
U.S. federal law does not apply, by the law of the Commonwealth of
 | 
			
		||||
Virginia, excluding Virginia's conflict of law provisions.
 | 
			
		||||
Notwithstanding the foregoing, with regard to derivative works based
 | 
			
		||||
on Python 1.6.1 that incorporate non-separable material that was
 | 
			
		||||
previously distributed under the GNU General Public License (GPL), the
 | 
			
		||||
law of the Commonwealth of Virginia shall govern this License
 | 
			
		||||
Agreement only as to issues arising under or with respect to
 | 
			
		||||
Paragraphs 4, 5, and 7 of this License Agreement.  Nothing in this
 | 
			
		||||
License Agreement shall be deemed to create any relationship of
 | 
			
		||||
agency, partnership, or joint venture between CNRI and Licensee.  This
 | 
			
		||||
License Agreement does not grant permission to use CNRI trademarks or
 | 
			
		||||
trade name in a trademark sense to endorse or promote products or
 | 
			
		||||
services of Licensee, or any third party.
 | 
			
		||||
 | 
			
		||||
8. By clicking on the "ACCEPT" button where indicated, or by copying,
 | 
			
		||||
installing or otherwise using Python 1.6.1, Licensee agrees to be
 | 
			
		||||
bound by the terms and conditions of this License Agreement.
 | 
			
		||||
 | 
			
		||||
        ACCEPT
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
 | 
			
		||||
--------------------------------------------------
 | 
			
		||||
 | 
			
		||||
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
 | 
			
		||||
The Netherlands.  All rights reserved.
 | 
			
		||||
 | 
			
		||||
Permission to use, copy, modify, and distribute this software and its
 | 
			
		||||
documentation for any purpose and without fee is hereby granted,
 | 
			
		||||
provided that the above copyright notice appear in all copies and that
 | 
			
		||||
both that copyright notice and this permission notice appear in
 | 
			
		||||
supporting documentation, and that the name of Stichting Mathematisch
 | 
			
		||||
Centrum or CWI not be used in advertising or publicity pertaining to
 | 
			
		||||
distribution of the software without specific, written prior
 | 
			
		||||
permission.
 | 
			
		||||
 | 
			
		||||
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
 | 
			
		||||
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 | 
			
		||||
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
 | 
			
		||||
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
 | 
			
		||||
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
							
								
								
									
										9
									
								
								HACKING
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								HACKING
									
									
									
									
									
								
							@@ -118,15 +118,6 @@ Please note that g_malloc will exit on allocation failure, so there
 | 
			
		||||
is no need to test for failure (as you would have to with malloc).
 | 
			
		||||
Calling g_malloc with a zero size is valid and will return NULL.
 | 
			
		||||
 | 
			
		||||
Prefer g_new(T, n) instead of g_malloc(sizeof(T) * n) for the following
 | 
			
		||||
reasons:
 | 
			
		||||
 | 
			
		||||
  a. It catches multiplication overflowing size_t;
 | 
			
		||||
  b. It returns T * instead of void *, letting compiler catch more type
 | 
			
		||||
     errors.
 | 
			
		||||
 | 
			
		||||
Declarations like T *v = g_malloc(sizeof(*v)) are acceptable, though.
 | 
			
		||||
 | 
			
		||||
Memory allocated by qemu_memalign or qemu_blockalign must be freed with
 | 
			
		||||
qemu_vfree, since breaking this will cause problems on Win32.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										692
									
								
								MAINTAINERS
									
									
									
									
									
								
							
							
						
						
									
										692
									
								
								MAINTAINERS
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										323
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										323
									
								
								Makefile
									
									
									
									
									
								
							@@ -20,6 +20,8 @@ ifneq ($(wildcard config-host.mak),)
 | 
			
		||||
all:
 | 
			
		||||
include config-host.mak
 | 
			
		||||
 | 
			
		||||
PYTHON_UTF8 = LC_ALL= LANG=C LC_CTYPE=en_US.UTF-8 $(PYTHON)
 | 
			
		||||
 | 
			
		||||
git-submodule-update:
 | 
			
		||||
 | 
			
		||||
.PHONY: git-submodule-update
 | 
			
		||||
@@ -67,7 +69,7 @@ CONFIG_ALL=y
 | 
			
		||||
-include config-all-devices.mak
 | 
			
		||||
-include config-all-disas.mak
 | 
			
		||||
 | 
			
		||||
config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios $(SRC_PATH)/VERSION
 | 
			
		||||
config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios
 | 
			
		||||
	@echo $@ is out-of-date, running configure
 | 
			
		||||
	@# TODO: The next lines include code which supports a smooth
 | 
			
		||||
	@# transition from old configurations without config.status.
 | 
			
		||||
@@ -88,26 +90,78 @@ endif
 | 
			
		||||
include $(SRC_PATH)/rules.mak
 | 
			
		||||
 | 
			
		||||
GENERATED_FILES = qemu-version.h config-host.h qemu-options.def
 | 
			
		||||
 | 
			
		||||
#see Makefile.objs for the definition of QAPI_MODULES
 | 
			
		||||
GENERATED_QAPI_FILES = qapi/qapi-builtin-types.h qapi/qapi-builtin-types.c
 | 
			
		||||
GENERATED_QAPI_FILES += qapi/qapi-types.h qapi/qapi-types.c
 | 
			
		||||
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-types-%.h)
 | 
			
		||||
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-types-%.c)
 | 
			
		||||
GENERATED_QAPI_FILES += qapi/qapi-builtin-visit.h qapi/qapi-builtin-visit.c
 | 
			
		||||
GENERATED_QAPI_FILES += qapi/qapi-visit.h qapi/qapi-visit.c
 | 
			
		||||
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.h)
 | 
			
		||||
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.c)
 | 
			
		||||
GENERATED_QAPI_FILES += qapi/qapi-commands.h qapi/qapi-commands.c
 | 
			
		||||
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.h)
 | 
			
		||||
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.c)
 | 
			
		||||
GENERATED_QAPI_FILES += qapi/qapi-events.h qapi/qapi-events.c
 | 
			
		||||
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.h)
 | 
			
		||||
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.c)
 | 
			
		||||
GENERATED_QAPI_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h
 | 
			
		||||
GENERATED_QAPI_FILES += qapi/qapi-doc.texi
 | 
			
		||||
 | 
			
		||||
GENERATED_FILES += $(GENERATED_QAPI_FILES)
 | 
			
		||||
GENERATED_FILES += qapi/qapi-builtin-types.h qapi/qapi-builtin-types.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-types.h qapi/qapi-types.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-types-block-core.h qapi/qapi-types-block-core.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-types-block.h qapi/qapi-types-block.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-types-char.h qapi/qapi-types-char.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-types-common.h qapi/qapi-types-common.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-types-crypto.h qapi/qapi-types-crypto.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-types-introspect.h qapi/qapi-types-introspect.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-types-migration.h qapi/qapi-types-migration.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-types-misc.h qapi/qapi-types-misc.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-types-net.h qapi/qapi-types-net.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-types-rocker.h qapi/qapi-types-rocker.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-types-run-state.h qapi/qapi-types-run-state.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-types-sockets.h qapi/qapi-types-sockets.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-types-tpm.h qapi/qapi-types-tpm.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-types-trace.h qapi/qapi-types-trace.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-types-transaction.h qapi/qapi-types-transaction.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-types-ui.h qapi/qapi-types-ui.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-builtin-visit.h qapi/qapi-builtin-visit.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-visit.h qapi/qapi-visit.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-visit-block-core.h qapi/qapi-visit-block-core.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-visit-block.h qapi/qapi-visit-block.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-visit-char.h qapi/qapi-visit-char.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-visit-common.h qapi/qapi-visit-common.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-visit-crypto.h qapi/qapi-visit-crypto.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-visit-introspect.h qapi/qapi-visit-introspect.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-visit-migration.h qapi/qapi-visit-migration.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-visit-misc.h qapi/qapi-visit-misc.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-visit-net.h qapi/qapi-visit-net.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-visit-rocker.h qapi/qapi-visit-rocker.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-visit-run-state.h qapi/qapi-visit-run-state.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-visit-sockets.h qapi/qapi-visit-sockets.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-visit-tpm.h qapi/qapi-visit-tpm.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-visit-trace.h qapi/qapi-visit-trace.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-visit-transaction.h qapi/qapi-visit-transaction.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-visit-ui.h qapi/qapi-visit-ui.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-commands.h qapi/qapi-commands.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-commands-block-core.h qapi/qapi-commands-block-core.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-commands-block.h qapi/qapi-commands-block.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-commands-char.h qapi/qapi-commands-char.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-commands-common.h qapi/qapi-commands-common.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-commands-crypto.h qapi/qapi-commands-crypto.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-commands-introspect.h qapi/qapi-commands-introspect.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-commands-migration.h qapi/qapi-commands-migration.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-commands-misc.h qapi/qapi-commands-misc.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-commands-net.h qapi/qapi-commands-net.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-commands-rocker.h qapi/qapi-commands-rocker.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-commands-run-state.h qapi/qapi-commands-run-state.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-commands-sockets.h qapi/qapi-commands-sockets.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-commands-tpm.h qapi/qapi-commands-tpm.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-commands-trace.h qapi/qapi-commands-trace.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-commands-transaction.h qapi/qapi-commands-transaction.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-commands-ui.h qapi/qapi-commands-ui.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-events.h qapi/qapi-events.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-events-block-core.h qapi/qapi-events-block-core.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-events-block.h qapi/qapi-events-block.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-events-char.h qapi/qapi-events-char.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-events-common.h qapi/qapi-events-common.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-events-crypto.h qapi/qapi-events-crypto.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-events-introspect.h qapi/qapi-events-introspect.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-events-migration.h qapi/qapi-events-migration.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-events-misc.h qapi/qapi-events-misc.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-events-net.h qapi/qapi-events-net.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-events-rocker.h qapi/qapi-events-rocker.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-events-run-state.h qapi/qapi-events-run-state.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-events-sockets.h qapi/qapi-events-sockets.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-events-tpm.h qapi/qapi-events-tpm.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-events-trace.h qapi/qapi-events-trace.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-events-transaction.h qapi/qapi-events-transaction.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-events-ui.h qapi/qapi-events-ui.c
 | 
			
		||||
GENERATED_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h
 | 
			
		||||
GENERATED_FILES += qapi/qapi-doc.texi
 | 
			
		||||
 | 
			
		||||
GENERATED_FILES += trace/generated-tcg-tracers.h
 | 
			
		||||
 | 
			
		||||
@@ -145,7 +199,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py")
 | 
			
		||||
 | 
			
		||||
%/trace.h: %/trace.h-timestamp
 | 
			
		||||
	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
 | 
			
		||||
%/trace.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak
 | 
			
		||||
%/trace.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y)
 | 
			
		||||
	$(call quiet-command,$(TRACETOOL) \
 | 
			
		||||
		--group=$(call trace-group-name,$@) \
 | 
			
		||||
		--format=h \
 | 
			
		||||
@@ -154,7 +208,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py")
 | 
			
		||||
 | 
			
		||||
%/trace.c: %/trace.c-timestamp
 | 
			
		||||
	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
 | 
			
		||||
%/trace.c-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak
 | 
			
		||||
%/trace.c-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y)
 | 
			
		||||
	$(call quiet-command,$(TRACETOOL) \
 | 
			
		||||
		--group=$(call trace-group-name,$@) \
 | 
			
		||||
		--format=c \
 | 
			
		||||
@@ -163,7 +217,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py")
 | 
			
		||||
 | 
			
		||||
%/trace-ust.h: %/trace-ust.h-timestamp
 | 
			
		||||
	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
 | 
			
		||||
%/trace-ust.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak
 | 
			
		||||
%/trace-ust.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y)
 | 
			
		||||
	$(call quiet-command,$(TRACETOOL) \
 | 
			
		||||
		--group=$(call trace-group-name,$@) \
 | 
			
		||||
		--format=ust-events-h \
 | 
			
		||||
@@ -187,7 +241,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py")
 | 
			
		||||
 | 
			
		||||
trace-root.h: trace-root.h-timestamp
 | 
			
		||||
	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
 | 
			
		||||
trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak
 | 
			
		||||
trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
 | 
			
		||||
	$(call quiet-command,$(TRACETOOL) \
 | 
			
		||||
		--group=root \
 | 
			
		||||
		--format=h \
 | 
			
		||||
@@ -196,7 +250,7 @@ trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/con
 | 
			
		||||
 | 
			
		||||
trace-root.c: trace-root.c-timestamp
 | 
			
		||||
	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
 | 
			
		||||
trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak
 | 
			
		||||
trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
 | 
			
		||||
	$(call quiet-command,$(TRACETOOL) \
 | 
			
		||||
		--group=root \
 | 
			
		||||
		--format=c \
 | 
			
		||||
@@ -205,7 +259,7 @@ trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/con
 | 
			
		||||
 | 
			
		||||
trace-ust-root.h: trace-ust-root.h-timestamp
 | 
			
		||||
	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
 | 
			
		||||
trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak
 | 
			
		||||
trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
 | 
			
		||||
	$(call quiet-command,$(TRACETOOL) \
 | 
			
		||||
		--group=root \
 | 
			
		||||
		--format=ust-events-h \
 | 
			
		||||
@@ -214,7 +268,7 @@ trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)
 | 
			
		||||
 | 
			
		||||
trace-ust-all.h: trace-ust-all.h-timestamp
 | 
			
		||||
	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
 | 
			
		||||
trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/config-host.mak
 | 
			
		||||
trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y)
 | 
			
		||||
	$(call quiet-command,$(TRACETOOL) \
 | 
			
		||||
		--group=all \
 | 
			
		||||
		--format=ust-events-h \
 | 
			
		||||
@@ -223,7 +277,7 @@ trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/con
 | 
			
		||||
 | 
			
		||||
trace-ust-all.c: trace-ust-all.c-timestamp
 | 
			
		||||
	@cmp $< $@ >/dev/null 2>&1 || cp $< $@
 | 
			
		||||
trace-ust-all.c-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/config-host.mak
 | 
			
		||||
trace-ust-all.c-timestamp: $(trace-events-files) $(tracetool-y)
 | 
			
		||||
	$(call quiet-command,$(TRACETOOL) \
 | 
			
		||||
		--group=all \
 | 
			
		||||
		--format=ust-events-c \
 | 
			
		||||
@@ -264,7 +318,6 @@ KEYCODEMAP_FILES = \
 | 
			
		||||
		 ui/input-keymap-xorgkbd-to-qcode.c \
 | 
			
		||||
		 ui/input-keymap-xorgxquartz-to-qcode.c \
 | 
			
		||||
		 ui/input-keymap-xorgxwin-to-qcode.c \
 | 
			
		||||
		 ui/input-keymap-osx-to-qcode.c \
 | 
			
		||||
		 $(NULL)
 | 
			
		||||
 | 
			
		||||
GENERATED_FILES += $(KEYCODEMAP_FILES)
 | 
			
		||||
@@ -294,20 +347,16 @@ $(call set-vpath, $(SRC_PATH))
 | 
			
		||||
 | 
			
		||||
LIBS+=-lz $(LIBS_TOOLS)
 | 
			
		||||
 | 
			
		||||
HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) = qemu-bridge-helper$(EXESUF)
 | 
			
		||||
HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF)
 | 
			
		||||
 | 
			
		||||
ifdef BUILD_DOCS
 | 
			
		||||
DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
 | 
			
		||||
DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7
 | 
			
		||||
DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7
 | 
			
		||||
DOCS+=docs/qemu-block-drivers.7
 | 
			
		||||
DOCS+=docs/qemu-cpu-models.7
 | 
			
		||||
ifdef CONFIG_VIRTFS
 | 
			
		||||
DOCS+=fsdev/virtfs-proxy-helper.1
 | 
			
		||||
endif
 | 
			
		||||
ifdef CONFIG_TRACE_SYSTEMTAP
 | 
			
		||||
DOCS+=scripts/qemu-trace-stap.1
 | 
			
		||||
endif
 | 
			
		||||
else
 | 
			
		||||
DOCS=
 | 
			
		||||
endif
 | 
			
		||||
@@ -362,10 +411,8 @@ dummy := $(call unnest-vars,, \
 | 
			
		||||
                chardev-obj-y \
 | 
			
		||||
                util-obj-y \
 | 
			
		||||
                qga-obj-y \
 | 
			
		||||
                elf2dmp-obj-y \
 | 
			
		||||
                ivshmem-client-obj-y \
 | 
			
		||||
                ivshmem-server-obj-y \
 | 
			
		||||
                rdmacm-mux-obj-y \
 | 
			
		||||
                libvhost-user-obj-y \
 | 
			
		||||
                vhost-user-scsi-obj-y \
 | 
			
		||||
                vhost-user-blk-obj-y \
 | 
			
		||||
@@ -382,8 +429,7 @@ dummy := $(call unnest-vars,, \
 | 
			
		||||
                ui-obj-m \
 | 
			
		||||
                audio-obj-y \
 | 
			
		||||
                audio-obj-m \
 | 
			
		||||
                trace-obj-y \
 | 
			
		||||
                slirp-obj-y)
 | 
			
		||||
                trace-obj-y)
 | 
			
		||||
 | 
			
		||||
include $(SRC_PATH)/tests/Makefile.include
 | 
			
		||||
 | 
			
		||||
@@ -392,23 +438,21 @@ all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules
 | 
			
		||||
qemu-version.h: FORCE
 | 
			
		||||
	$(call quiet-command, \
 | 
			
		||||
		(cd $(SRC_PATH); \
 | 
			
		||||
		printf '#define QEMU_PKGVERSION '; \
 | 
			
		||||
		if test -n "$(PKGVERSION)"; then \
 | 
			
		||||
			pkgvers="$(PKGVERSION)"; \
 | 
			
		||||
			printf '"$(PKGVERSION)"\n'; \
 | 
			
		||||
		else \
 | 
			
		||||
			if test -d .git; then \
 | 
			
		||||
				pkgvers=$$(git describe --match 'v*' 2>/dev/null | tr -d '\n');\
 | 
			
		||||
				printf '" ('; \
 | 
			
		||||
				git describe --match 'v*' 2>/dev/null | tr -d '\n'; \
 | 
			
		||||
				if ! git diff-index --quiet HEAD &>/dev/null; then \
 | 
			
		||||
					pkgvers="$${pkgvers}-dirty"; \
 | 
			
		||||
					printf -- '-dirty'; \
 | 
			
		||||
				fi; \
 | 
			
		||||
			fi; \
 | 
			
		||||
		fi; \
 | 
			
		||||
		printf "#define QEMU_PKGVERSION \"$${pkgvers}\"\n"; \
 | 
			
		||||
		if test -n "$${pkgvers}"; then \
 | 
			
		||||
			printf '#define QEMU_FULL_VERSION QEMU_VERSION " (" QEMU_PKGVERSION ")"\n'; \
 | 
			
		||||
				printf ')"\n'; \
 | 
			
		||||
			else \
 | 
			
		||||
			printf '#define QEMU_FULL_VERSION QEMU_VERSION\n'; \
 | 
			
		||||
				printf '""\n'; \
 | 
			
		||||
			fi; \
 | 
			
		||||
		) > $@.tmp)
 | 
			
		||||
		fi) > $@.tmp)
 | 
			
		||||
	$(call quiet-command, if ! cmp -s $@ $@.tmp; then \
 | 
			
		||||
	  mv $@.tmp $@; \
 | 
			
		||||
	 else \
 | 
			
		||||
@@ -439,7 +483,7 @@ subdir-dtc: .git-submodule-status dtc/libfdt dtc/tests
 | 
			
		||||
	$(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt/libfdt.a,)
 | 
			
		||||
 | 
			
		||||
dtc/%: .git-submodule-status
 | 
			
		||||
	@mkdir -p $@
 | 
			
		||||
	mkdir -p $@
 | 
			
		||||
 | 
			
		||||
# Overriding CFLAGS causes us to lose defines added in the sub-makefile.
 | 
			
		||||
# Not overriding CFLAGS leads to mis-matches between compilation modes.
 | 
			
		||||
@@ -456,7 +500,7 @@ CAP_CFLAGS += -DCAPSTONE_HAS_X86
 | 
			
		||||
subdir-capstone: .git-submodule-status
 | 
			
		||||
	$(call quiet-command,$(MAKE) -C $(SRC_PATH)/capstone CAPSTONE_SHARED=no BUILDDIR="$(BUILD_DIR)/capstone" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(CAP_CFLAGS)" $(SUBDIR_MAKEFLAGS) $(BUILD_DIR)/capstone/$(LIBCAPSTONE))
 | 
			
		||||
 | 
			
		||||
$(SUBDIR_RULES): libqemuutil.a $(common-obj-y) $(chardev-obj-y) $(slirp-obj-y) \
 | 
			
		||||
$(SUBDIR_RULES): libqemuutil.a $(common-obj-y) $(chardev-obj-y) \
 | 
			
		||||
	$(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY))
 | 
			
		||||
 | 
			
		||||
ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
 | 
			
		||||
@@ -493,8 +537,6 @@ qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
 | 
			
		||||
 | 
			
		||||
qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $(COMMON_LDADDS)
 | 
			
		||||
 | 
			
		||||
qemu-edid$(EXESUF): qemu-edid.o hw/display/edid-generate.o $(COMMON_LDADDS)
 | 
			
		||||
 | 
			
		||||
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS)
 | 
			
		||||
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
 | 
			
		||||
 | 
			
		||||
@@ -519,6 +561,7 @@ $(SRC_PATH)/scripts/qapi/types.py \
 | 
			
		||||
$(SRC_PATH)/scripts/qapi/visit.py \
 | 
			
		||||
$(SRC_PATH)/scripts/qapi/common.py \
 | 
			
		||||
$(SRC_PATH)/scripts/qapi/doc.py \
 | 
			
		||||
$(SRC_PATH)/scripts/ordereddict.py \
 | 
			
		||||
$(SRC_PATH)/scripts/qapi-gen.py
 | 
			
		||||
 | 
			
		||||
qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \
 | 
			
		||||
@@ -527,17 +570,102 @@ qga/qapi-generated/qga-qapi-commands.h qga/qapi-generated/qga-qapi-commands.c \
 | 
			
		||||
qga/qapi-generated/qga-qapi-doc.texi: \
 | 
			
		||||
qga/qapi-generated/qapi-gen-timestamp ;
 | 
			
		||||
qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py)
 | 
			
		||||
	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
 | 
			
		||||
	$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \
 | 
			
		||||
		-o qga/qapi-generated -p "qga-" $<, \
 | 
			
		||||
		"GEN","$(@:%-timestamp=%)")
 | 
			
		||||
	@>$@
 | 
			
		||||
 | 
			
		||||
qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json \
 | 
			
		||||
               $(QAPI_MODULES:%=$(SRC_PATH)/qapi/%.json)
 | 
			
		||||
qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json $(SRC_PATH)/qapi/common.json \
 | 
			
		||||
               $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \
 | 
			
		||||
               $(SRC_PATH)/qapi/char.json \
 | 
			
		||||
               $(SRC_PATH)/qapi/crypto.json \
 | 
			
		||||
               $(SRC_PATH)/qapi/introspect.json \
 | 
			
		||||
               $(SRC_PATH)/qapi/migration.json \
 | 
			
		||||
               $(SRC_PATH)/qapi/misc.json \
 | 
			
		||||
               $(SRC_PATH)/qapi/net.json \
 | 
			
		||||
               $(SRC_PATH)/qapi/rocker.json \
 | 
			
		||||
               $(SRC_PATH)/qapi/run-state.json \
 | 
			
		||||
               $(SRC_PATH)/qapi/sockets.json \
 | 
			
		||||
               $(SRC_PATH)/qapi/tpm.json \
 | 
			
		||||
               $(SRC_PATH)/qapi/trace.json \
 | 
			
		||||
               $(SRC_PATH)/qapi/transaction.json \
 | 
			
		||||
               $(SRC_PATH)/qapi/ui.json
 | 
			
		||||
 | 
			
		||||
$(GENERATED_QAPI_FILES): qapi-gen-timestamp ;
 | 
			
		||||
qapi/qapi-builtin-types.c qapi/qapi-builtin-types.h \
 | 
			
		||||
qapi/qapi-types.c qapi/qapi-types.h \
 | 
			
		||||
qapi/qapi-types-block-core.c qapi/qapi-types-block-core.h \
 | 
			
		||||
qapi/qapi-types-block.c qapi/qapi-types-block.h \
 | 
			
		||||
qapi/qapi-types-char.c qapi/qapi-types-char.h \
 | 
			
		||||
qapi/qapi-types-common.c qapi/qapi-types-common.h \
 | 
			
		||||
qapi/qapi-types-crypto.c qapi/qapi-types-crypto.h \
 | 
			
		||||
qapi/qapi-types-introspect.c qapi/qapi-types-introspect.h \
 | 
			
		||||
qapi/qapi-types-migration.c qapi/qapi-types-migration.h \
 | 
			
		||||
qapi/qapi-types-misc.c qapi/qapi-types-misc.h \
 | 
			
		||||
qapi/qapi-types-net.c qapi/qapi-types-net.h \
 | 
			
		||||
qapi/qapi-types-rocker.c qapi/qapi-types-rocker.h \
 | 
			
		||||
qapi/qapi-types-run-state.c qapi/qapi-types-run-state.h \
 | 
			
		||||
qapi/qapi-types-sockets.c qapi/qapi-types-sockets.h \
 | 
			
		||||
qapi/qapi-types-tpm.c qapi/qapi-types-tpm.h \
 | 
			
		||||
qapi/qapi-types-trace.c qapi/qapi-types-trace.h \
 | 
			
		||||
qapi/qapi-types-transaction.c qapi/qapi-types-transaction.h \
 | 
			
		||||
qapi/qapi-types-ui.c qapi/qapi-types-ui.h \
 | 
			
		||||
qapi/qapi-builtin-visit.c qapi/qapi-builtin-visit.h \
 | 
			
		||||
qapi/qapi-visit.c qapi/qapi-visit.h \
 | 
			
		||||
qapi/qapi-visit-block-core.c qapi/qapi-visit-block-core.h \
 | 
			
		||||
qapi/qapi-visit-block.c qapi/qapi-visit-block.h \
 | 
			
		||||
qapi/qapi-visit-char.c qapi/qapi-visit-char.h \
 | 
			
		||||
qapi/qapi-visit-common.c qapi/qapi-visit-common.h \
 | 
			
		||||
qapi/qapi-visit-crypto.c qapi/qapi-visit-crypto.h \
 | 
			
		||||
qapi/qapi-visit-introspect.c qapi/qapi-visit-introspect.h \
 | 
			
		||||
qapi/qapi-visit-migration.c qapi/qapi-visit-migration.h \
 | 
			
		||||
qapi/qapi-visit-misc.c qapi/qapi-visit-misc.h \
 | 
			
		||||
qapi/qapi-visit-net.c qapi/qapi-visit-net.h \
 | 
			
		||||
qapi/qapi-visit-rocker.c qapi/qapi-visit-rocker.h \
 | 
			
		||||
qapi/qapi-visit-run-state.c qapi/qapi-visit-run-state.h \
 | 
			
		||||
qapi/qapi-visit-sockets.c qapi/qapi-visit-sockets.h \
 | 
			
		||||
qapi/qapi-visit-tpm.c qapi/qapi-visit-tpm.h \
 | 
			
		||||
qapi/qapi-visit-trace.c qapi/qapi-visit-trace.h \
 | 
			
		||||
qapi/qapi-visit-transaction.c qapi/qapi-visit-transaction.h \
 | 
			
		||||
qapi/qapi-visit-ui.c qapi/qapi-visit-ui.h \
 | 
			
		||||
qapi/qapi-commands.h qapi/qapi-commands.c \
 | 
			
		||||
qapi/qapi-commands-block-core.c qapi/qapi-commands-block-core.h \
 | 
			
		||||
qapi/qapi-commands-block.c qapi/qapi-commands-block.h \
 | 
			
		||||
qapi/qapi-commands-char.c qapi/qapi-commands-char.h \
 | 
			
		||||
qapi/qapi-commands-common.c qapi/qapi-commands-common.h \
 | 
			
		||||
qapi/qapi-commands-crypto.c qapi/qapi-commands-crypto.h \
 | 
			
		||||
qapi/qapi-commands-introspect.c qapi/qapi-commands-introspect.h \
 | 
			
		||||
qapi/qapi-commands-migration.c qapi/qapi-commands-migration.h \
 | 
			
		||||
qapi/qapi-commands-misc.c qapi/qapi-commands-misc.h \
 | 
			
		||||
qapi/qapi-commands-net.c qapi/qapi-commands-net.h \
 | 
			
		||||
qapi/qapi-commands-rocker.c qapi/qapi-commands-rocker.h \
 | 
			
		||||
qapi/qapi-commands-run-state.c qapi/qapi-commands-run-state.h \
 | 
			
		||||
qapi/qapi-commands-sockets.c qapi/qapi-commands-sockets.h \
 | 
			
		||||
qapi/qapi-commands-tpm.c qapi/qapi-commands-tpm.h \
 | 
			
		||||
qapi/qapi-commands-trace.c qapi/qapi-commands-trace.h \
 | 
			
		||||
qapi/qapi-commands-transaction.c qapi/qapi-commands-transaction.h \
 | 
			
		||||
qapi/qapi-commands-ui.c qapi/qapi-commands-ui.h \
 | 
			
		||||
qapi/qapi-events.c qapi/qapi-events.h \
 | 
			
		||||
qapi/qapi-events-block-core.c qapi/qapi-events-block-core.h \
 | 
			
		||||
qapi/qapi-events-block.c qapi/qapi-events-block.h \
 | 
			
		||||
qapi/qapi-events-char.c qapi/qapi-events-char.h \
 | 
			
		||||
qapi/qapi-events-common.c qapi/qapi-events-common.h \
 | 
			
		||||
qapi/qapi-events-crypto.c qapi/qapi-events-crypto.h \
 | 
			
		||||
qapi/qapi-events-introspect.c qapi/qapi-events-introspect.h \
 | 
			
		||||
qapi/qapi-events-migration.c qapi/qapi-events-migration.h \
 | 
			
		||||
qapi/qapi-events-misc.c qapi/qapi-events-misc.h \
 | 
			
		||||
qapi/qapi-events-net.c qapi/qapi-events-net.h \
 | 
			
		||||
qapi/qapi-events-rocker.c qapi/qapi-events-rocker.h \
 | 
			
		||||
qapi/qapi-events-run-state.c qapi/qapi-events-run-state.h \
 | 
			
		||||
qapi/qapi-events-sockets.c qapi/qapi-events-sockets.h \
 | 
			
		||||
qapi/qapi-events-tpm.c qapi/qapi-events-tpm.h \
 | 
			
		||||
qapi/qapi-events-trace.c qapi/qapi-events-trace.h \
 | 
			
		||||
qapi/qapi-events-transaction.c qapi/qapi-events-transaction.h \
 | 
			
		||||
qapi/qapi-events-ui.c qapi/qapi-events-ui.h \
 | 
			
		||||
qapi/qapi-introspect.h qapi/qapi-introspect.c \
 | 
			
		||||
qapi/qapi-doc.texi: \
 | 
			
		||||
qapi-gen-timestamp ;
 | 
			
		||||
qapi-gen-timestamp: $(qapi-modules) $(qapi-py)
 | 
			
		||||
	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
 | 
			
		||||
	$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \
 | 
			
		||||
		-o "qapi" -b $<, \
 | 
			
		||||
		"GEN","$(@:%-timestamp=%)")
 | 
			
		||||
	@>$@
 | 
			
		||||
@@ -570,10 +698,6 @@ ifneq ($(EXESUF),)
 | 
			
		||||
qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
elf2dmp: LIBS = $(CURL_LIBS)
 | 
			
		||||
elf2dmp: $(elf2dmp-obj-y)
 | 
			
		||||
	$(call LINK, $^)
 | 
			
		||||
 | 
			
		||||
ifdef CONFIG_IVSHMEM
 | 
			
		||||
ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS)
 | 
			
		||||
	$(call LINK, $^)
 | 
			
		||||
@@ -585,23 +709,11 @@ vhost-user-scsi$(EXESUF): $(vhost-user-scsi-obj-y) libvhost-user.a
 | 
			
		||||
vhost-user-blk$(EXESUF): $(vhost-user-blk-obj-y) libvhost-user.a
 | 
			
		||||
	$(call LINK, $^)
 | 
			
		||||
 | 
			
		||||
rdmacm-mux$(EXESUF): LIBS += "-libumad"
 | 
			
		||||
rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS)
 | 
			
		||||
	$(call LINK, $^)
 | 
			
		||||
 | 
			
		||||
module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak
 | 
			
		||||
	$(call quiet-command,$(PYTHON) $< $@ \
 | 
			
		||||
	$(addprefix $(SRC_PATH)/,$(patsubst %.mo,%.c,$(block-obj-m))), \
 | 
			
		||||
	"GEN","$@")
 | 
			
		||||
 | 
			
		||||
ifdef CONFIG_GCOV
 | 
			
		||||
.PHONY: clean-coverage
 | 
			
		||||
clean-coverage:
 | 
			
		||||
	$(call quiet-command, \
 | 
			
		||||
		find . \( -name '*.gcda' -o -name '*.gcov' \) -type f -exec rm {} +, \
 | 
			
		||||
		"CLEAN", "coverage files")
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
# avoid old build problems by removing potentially incorrect old files
 | 
			
		||||
	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
 | 
			
		||||
@@ -622,7 +734,7 @@ clean:
 | 
			
		||||
	if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
 | 
			
		||||
	rm -f $$d/qemu-options.def; \
 | 
			
		||||
        done
 | 
			
		||||
	rm -f config-all-devices.mak
 | 
			
		||||
	rm -f $(SUBDIR_DEVICES_MAK) config-all-devices.mak
 | 
			
		||||
 | 
			
		||||
VERSION ?= $(shell cat VERSION)
 | 
			
		||||
 | 
			
		||||
@@ -634,7 +746,6 @@ qemu-%.tar.bz2:
 | 
			
		||||
distclean: clean
 | 
			
		||||
	rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi
 | 
			
		||||
	rm -f config-all-devices.mak config-all-disas.mak config.status
 | 
			
		||||
	rm -f $(SUBDIR_DEVICES_MAK)
 | 
			
		||||
	rm -f po/*.mo tests/qemu-iotests/common.env
 | 
			
		||||
	rm -f roms/seabios/config.mak roms/vgabios/config.mak
 | 
			
		||||
	rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps
 | 
			
		||||
@@ -650,28 +761,28 @@ distclean: clean
 | 
			
		||||
	rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
 | 
			
		||||
	rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
 | 
			
		||||
	rm -f docs/qemu-block-drivers.7
 | 
			
		||||
	rm -f docs/qemu-cpu-models.7
 | 
			
		||||
	for d in $(TARGET_DIRS); do \
 | 
			
		||||
	rm -rf $$d || exit 1 ; \
 | 
			
		||||
        done
 | 
			
		||||
	rm -Rf .sdk
 | 
			
		||||
	if test -f dtc/version_gen.h; then $(MAKE) $(DTC_MAKE_ARGS) clean; fi
 | 
			
		||||
 | 
			
		||||
KEYMAPS=da     en-gb  et  fr     fr-ch  is  lt  no  pt-br  sv \
 | 
			
		||||
KEYMAPS=da     en-gb  et  fr     fr-ch  is  lt  modifiers  no  pt-br  sv \
 | 
			
		||||
ar      de     en-us  fi  fr-be  hr     it  lv  nl         pl  ru     th \
 | 
			
		||||
de-ch  es     fo  fr-ca  hu     ja  mk  pt  sl     tr \
 | 
			
		||||
common  de-ch  es     fo  fr-ca  hu     ja  mk  nl-be      pt  sl     tr \
 | 
			
		||||
bepo    cz
 | 
			
		||||
 | 
			
		||||
ifdef INSTALL_BLOBS
 | 
			
		||||
BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
 | 
			
		||||
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
 | 
			
		||||
vgabios-ramfb.bin vgabios-bochs-display.bin \
 | 
			
		||||
acpi-dsdt.aml \
 | 
			
		||||
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
 | 
			
		||||
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
 | 
			
		||||
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
 | 
			
		||||
efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \
 | 
			
		||||
efi-pcnet.rom efi-rtl8139.rom efi-virtio.rom \
 | 
			
		||||
efi-e1000e.rom efi-vmxnet3.rom \
 | 
			
		||||
qemu-icon.bmp qemu_logo_no_text.svg \
 | 
			
		||||
bamboo.dtb canyonlands.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
 | 
			
		||||
multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin \
 | 
			
		||||
s390-ccw.img s390-netboot.img \
 | 
			
		||||
@@ -696,15 +807,11 @@ ifdef CONFIG_POSIX
 | 
			
		||||
	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7"
 | 
			
		||||
	$(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
 | 
			
		||||
	$(INSTALL_DATA) docs/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7"
 | 
			
		||||
	$(INSTALL_DATA) docs/qemu-cpu-models.7 "$(DESTDIR)$(mandir)/man7"
 | 
			
		||||
ifneq ($(TOOLS),)
 | 
			
		||||
	$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
 | 
			
		||||
	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
 | 
			
		||||
	$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8"
 | 
			
		||||
endif
 | 
			
		||||
ifdef CONFIG_TRACE_SYSTEMTAP
 | 
			
		||||
	$(INSTALL_DATA) scripts/qemu-trace-stap.1 "$(DESTDIR)$(mandir)/man1"
 | 
			
		||||
endif
 | 
			
		||||
ifneq (,$(findstring qemu-ga,$(TOOLS)))
 | 
			
		||||
	$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
 | 
			
		||||
	$(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)"
 | 
			
		||||
@@ -727,7 +834,6 @@ ifneq (,$(findstring qemu-ga,$(TOOLS)))
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ICON_SIZES=16x16 24x24 32x32 48x48 64x64 128x128 256x256 512x512
 | 
			
		||||
 | 
			
		||||
install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir
 | 
			
		||||
ifneq ($(TOOLS),)
 | 
			
		||||
@@ -744,29 +850,12 @@ endif
 | 
			
		||||
ifneq ($(HELPERS-y),)
 | 
			
		||||
	$(call install-prog,$(HELPERS-y),$(DESTDIR)$(libexecdir))
 | 
			
		||||
endif
 | 
			
		||||
ifdef CONFIG_TRACE_SYSTEMTAP
 | 
			
		||||
	$(INSTALL_PROG) "scripts/qemu-trace-stap" $(DESTDIR)$(bindir)
 | 
			
		||||
endif
 | 
			
		||||
ifneq ($(BLOBS),)
 | 
			
		||||
	set -e; for x in $(BLOBS); do \
 | 
			
		||||
		$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \
 | 
			
		||||
	done
 | 
			
		||||
endif
 | 
			
		||||
	for s in $(ICON_SIZES); do \
 | 
			
		||||
		mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/$${s}/apps"; \
 | 
			
		||||
		$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu_$${s}.png \
 | 
			
		||||
			"$(DESTDIR)/$(qemu_icondir)/hicolor/$${s}/apps/qemu.png"; \
 | 
			
		||||
	done; \
 | 
			
		||||
	mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/32x32/apps"; \
 | 
			
		||||
	$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu_32x32.bmp \
 | 
			
		||||
		"$(DESTDIR)/$(qemu_icondir)/hicolor/32x32/apps/qemu.bmp"; \
 | 
			
		||||
	mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/scalable/apps"; \
 | 
			
		||||
	$(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu.svg \
 | 
			
		||||
		"$(DESTDIR)/$(qemu_icondir)/hicolor/scalable/apps/qemu.svg"
 | 
			
		||||
	mkdir -p "$(DESTDIR)/$(qemu_desktopdir)"
 | 
			
		||||
	$(INSTALL_DATA) $(SRC_PATH)/ui/qemu.desktop \
 | 
			
		||||
		"$(DESTDIR)/$(qemu_desktopdir)/qemu.desktop"
 | 
			
		||||
ifdef CONFIG_GTK
 | 
			
		||||
ifeq ($(CONFIG_GTK),m)
 | 
			
		||||
	$(MAKE) -C po $@
 | 
			
		||||
endif
 | 
			
		||||
	$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps"
 | 
			
		||||
@@ -860,8 +949,6 @@ fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi
 | 
			
		||||
qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi
 | 
			
		||||
qemu-ga.8: qemu-ga.texi
 | 
			
		||||
docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi
 | 
			
		||||
docs/qemu-cpu-models.7: docs/qemu-cpu-models.texi
 | 
			
		||||
scripts/qemu-trace-stap.1: scripts/qemu-trace-stap.texi
 | 
			
		||||
 | 
			
		||||
html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
 | 
			
		||||
info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info
 | 
			
		||||
@@ -870,9 +957,8 @@ txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
 | 
			
		||||
 | 
			
		||||
qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \
 | 
			
		||||
	qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \
 | 
			
		||||
	qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
 | 
			
		||||
	qemu-monitor-info.texi docs/qemu-block-drivers.texi \
 | 
			
		||||
	docs/qemu-cpu-models.texi
 | 
			
		||||
	qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
 | 
			
		||||
	qemu-monitor-info.texi docs/qemu-block-drivers.texi
 | 
			
		||||
 | 
			
		||||
docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \
 | 
			
		||||
    docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \
 | 
			
		||||
@@ -884,18 +970,6 @@ docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \
 | 
			
		||||
    docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \
 | 
			
		||||
	docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi
 | 
			
		||||
 | 
			
		||||
$(filter %.1 %.7 %.8,$(DOCS)): scripts/texi2pod.pl
 | 
			
		||||
 | 
			
		||||
# Reports/Analysis
 | 
			
		||||
 | 
			
		||||
%/coverage-report.html:
 | 
			
		||||
	@mkdir -p $*
 | 
			
		||||
	$(call quiet-command,\
 | 
			
		||||
		gcovr -p --html --html-details -o $@, \
 | 
			
		||||
		"GEN", "coverage-report.html")
 | 
			
		||||
 | 
			
		||||
.PHONY: coverage-report
 | 
			
		||||
coverage-report: $(CURDIR)/reports/coverage/coverage-report.html
 | 
			
		||||
 | 
			
		||||
ifdef CONFIG_WIN32
 | 
			
		||||
 | 
			
		||||
@@ -976,9 +1050,6 @@ include $(SRC_PATH)/tests/vm/Makefile.include
 | 
			
		||||
help:
 | 
			
		||||
	@echo  'Generic targets:'
 | 
			
		||||
	@echo  '  all             - Build all'
 | 
			
		||||
ifdef CONFIG_MODULES
 | 
			
		||||
	@echo  '  modules         - Build all modules'
 | 
			
		||||
endif
 | 
			
		||||
	@echo  '  dir/file.o      - Build specified target only'
 | 
			
		||||
	@echo  '  install         - Install QEMU, documentation and tools'
 | 
			
		||||
	@echo  '  ctags/TAGS      - Generate tags file for editors'
 | 
			
		||||
@@ -991,9 +1062,6 @@ endif
 | 
			
		||||
		echo '')
 | 
			
		||||
	@echo  'Cleaning targets:'
 | 
			
		||||
	@echo  '  clean           - Remove most generated files but keep the config'
 | 
			
		||||
ifdef CONFIG_GCOV
 | 
			
		||||
	@echo  '  clean-coverage  - Remove coverage files'
 | 
			
		||||
endif
 | 
			
		||||
	@echo  '  distclean       - Remove all generated files'
 | 
			
		||||
	@echo  '  dist            - Build a distributable tarball'
 | 
			
		||||
	@echo  ''
 | 
			
		||||
@@ -1005,9 +1073,6 @@ endif
 | 
			
		||||
	@echo  'Documentation targets:'
 | 
			
		||||
	@echo  '  html info pdf txt'
 | 
			
		||||
	@echo  '                  - Build documentation in specified format'
 | 
			
		||||
ifdef CONFIG_GCOV
 | 
			
		||||
	@echo  '  coverage-report - Create code coverage report'
 | 
			
		||||
endif
 | 
			
		||||
	@echo  ''
 | 
			
		||||
ifdef CONFIG_WIN32
 | 
			
		||||
	@echo  'Windows targets:'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										166
									
								
								Makefile.objs
									
									
									
									
									
								
							
							
						
						
									
										166
									
								
								Makefile.objs
									
									
									
									
									
								
							@@ -1,29 +1,69 @@
 | 
			
		||||
QAPI_MODULES = block-core block char common crypto introspect job migration
 | 
			
		||||
QAPI_MODULES += misc net rdma rocker run-state sockets tpm trace transaction
 | 
			
		||||
QAPI_MODULES += ui
 | 
			
		||||
 | 
			
		||||
#######################################################################
 | 
			
		||||
# Common libraries for tools and emulators
 | 
			
		||||
stub-obj-y = stubs/ crypto/
 | 
			
		||||
util-obj-y = util/ qobject/ qapi/
 | 
			
		||||
util-obj-y += qapi/qapi-builtin-types.o
 | 
			
		||||
util-obj-y += qapi/qapi-types.o
 | 
			
		||||
util-obj-y += $(QAPI_MODULES:%=qapi/qapi-types-%.o)
 | 
			
		||||
util-obj-y += qapi/qapi-types-block-core.o
 | 
			
		||||
util-obj-y += qapi/qapi-types-block.o
 | 
			
		||||
util-obj-y += qapi/qapi-types-char.o
 | 
			
		||||
util-obj-y += qapi/qapi-types-common.o
 | 
			
		||||
util-obj-y += qapi/qapi-types-crypto.o
 | 
			
		||||
util-obj-y += qapi/qapi-types-introspect.o
 | 
			
		||||
util-obj-y += qapi/qapi-types-migration.o
 | 
			
		||||
util-obj-y += qapi/qapi-types-misc.o
 | 
			
		||||
util-obj-y += qapi/qapi-types-net.o
 | 
			
		||||
util-obj-y += qapi/qapi-types-rocker.o
 | 
			
		||||
util-obj-y += qapi/qapi-types-run-state.o
 | 
			
		||||
util-obj-y += qapi/qapi-types-sockets.o
 | 
			
		||||
util-obj-y += qapi/qapi-types-tpm.o
 | 
			
		||||
util-obj-y += qapi/qapi-types-trace.o
 | 
			
		||||
util-obj-y += qapi/qapi-types-transaction.o
 | 
			
		||||
util-obj-y += qapi/qapi-types-ui.o
 | 
			
		||||
util-obj-y += qapi/qapi-builtin-visit.o
 | 
			
		||||
util-obj-y += qapi/qapi-visit.o
 | 
			
		||||
util-obj-y += $(QAPI_MODULES:%=qapi/qapi-visit-%.o)
 | 
			
		||||
util-obj-y += qapi/qapi-visit-block-core.o
 | 
			
		||||
util-obj-y += qapi/qapi-visit-block.o
 | 
			
		||||
util-obj-y += qapi/qapi-visit-char.o
 | 
			
		||||
util-obj-y += qapi/qapi-visit-common.o
 | 
			
		||||
util-obj-y += qapi/qapi-visit-crypto.o
 | 
			
		||||
util-obj-y += qapi/qapi-visit-introspect.o
 | 
			
		||||
util-obj-y += qapi/qapi-visit-migration.o
 | 
			
		||||
util-obj-y += qapi/qapi-visit-misc.o
 | 
			
		||||
util-obj-y += qapi/qapi-visit-net.o
 | 
			
		||||
util-obj-y += qapi/qapi-visit-rocker.o
 | 
			
		||||
util-obj-y += qapi/qapi-visit-run-state.o
 | 
			
		||||
util-obj-y += qapi/qapi-visit-sockets.o
 | 
			
		||||
util-obj-y += qapi/qapi-visit-tpm.o
 | 
			
		||||
util-obj-y += qapi/qapi-visit-trace.o
 | 
			
		||||
util-obj-y += qapi/qapi-visit-transaction.o
 | 
			
		||||
util-obj-y += qapi/qapi-visit-ui.o
 | 
			
		||||
util-obj-y += qapi/qapi-events.o
 | 
			
		||||
util-obj-y += $(QAPI_MODULES:%=qapi/qapi-events-%.o)
 | 
			
		||||
util-obj-y += qapi/qapi-events-block-core.o
 | 
			
		||||
util-obj-y += qapi/qapi-events-block.o
 | 
			
		||||
util-obj-y += qapi/qapi-events-char.o
 | 
			
		||||
util-obj-y += qapi/qapi-events-common.o
 | 
			
		||||
util-obj-y += qapi/qapi-events-crypto.o
 | 
			
		||||
util-obj-y += qapi/qapi-events-introspect.o
 | 
			
		||||
util-obj-y += qapi/qapi-events-migration.o
 | 
			
		||||
util-obj-y += qapi/qapi-events-misc.o
 | 
			
		||||
util-obj-y += qapi/qapi-events-net.o
 | 
			
		||||
util-obj-y += qapi/qapi-events-rocker.o
 | 
			
		||||
util-obj-y += qapi/qapi-events-run-state.o
 | 
			
		||||
util-obj-y += qapi/qapi-events-sockets.o
 | 
			
		||||
util-obj-y += qapi/qapi-events-tpm.o
 | 
			
		||||
util-obj-y += qapi/qapi-events-trace.o
 | 
			
		||||
util-obj-y += qapi/qapi-events-transaction.o
 | 
			
		||||
util-obj-y += qapi/qapi-events-ui.o
 | 
			
		||||
util-obj-y += qapi/qapi-introspect.o
 | 
			
		||||
 | 
			
		||||
chardev-obj-y = chardev/
 | 
			
		||||
slirp-obj-$(CONFIG_SLIRP) = slirp/
 | 
			
		||||
 | 
			
		||||
#######################################################################
 | 
			
		||||
# block-obj-y is code used by both qemu system emulation and qemu-img
 | 
			
		||||
 | 
			
		||||
block-obj-y += nbd/
 | 
			
		||||
block-obj-y += block.o blockjob.o job.o
 | 
			
		||||
block-obj-y += block.o blockjob.o
 | 
			
		||||
block-obj-y += block/ scsi/
 | 
			
		||||
block-obj-y += qemu-io-cmds.o
 | 
			
		||||
block-obj-$(CONFIG_REPLICATION) += replication.o
 | 
			
		||||
@@ -54,7 +94,6 @@ io-obj-y = io/
 | 
			
		||||
ifeq ($(CONFIG_SOFTMMU),y)
 | 
			
		||||
common-obj-y = blockdev.o blockdev-nbd.o block/
 | 
			
		||||
common-obj-y += bootdevice.o iothread.o
 | 
			
		||||
common-obj-y += job-qmp.o
 | 
			
		||||
common-obj-y += net/
 | 
			
		||||
common-obj-y += qdev-monitor.o device-hotplug.o
 | 
			
		||||
common-obj-$(CONFIG_WIN32) += os-win32.o
 | 
			
		||||
@@ -80,6 +119,8 @@ common-obj-y += vl.o
 | 
			
		||||
vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS)
 | 
			
		||||
common-obj-$(CONFIG_TPM) += tpm.o
 | 
			
		||||
 | 
			
		||||
common-obj-$(CONFIG_SLIRP) += slirp/
 | 
			
		||||
 | 
			
		||||
common-obj-y += backends/
 | 
			
		||||
common-obj-y += chardev/
 | 
			
		||||
 | 
			
		||||
@@ -93,7 +134,22 @@ common-obj-$(CONFIG_FDT) += device_tree.o
 | 
			
		||||
# qapi
 | 
			
		||||
 | 
			
		||||
common-obj-y += qapi/qapi-commands.o
 | 
			
		||||
common-obj-y += $(QAPI_MODULES:%=qapi/qapi-commands-%.o)
 | 
			
		||||
common-obj-y += qapi/qapi-commands-block-core.o
 | 
			
		||||
common-obj-y += qapi/qapi-commands-block.o
 | 
			
		||||
common-obj-y += qapi/qapi-commands-char.o
 | 
			
		||||
common-obj-y += qapi/qapi-commands-common.o
 | 
			
		||||
common-obj-y += qapi/qapi-commands-crypto.o
 | 
			
		||||
common-obj-y += qapi/qapi-commands-introspect.o
 | 
			
		||||
common-obj-y += qapi/qapi-commands-migration.o
 | 
			
		||||
common-obj-y += qapi/qapi-commands-misc.o
 | 
			
		||||
common-obj-y += qapi/qapi-commands-net.o
 | 
			
		||||
common-obj-y += qapi/qapi-commands-rocker.o
 | 
			
		||||
common-obj-y += qapi/qapi-commands-run-state.o
 | 
			
		||||
common-obj-y += qapi/qapi-commands-sockets.o
 | 
			
		||||
common-obj-y += qapi/qapi-commands-tpm.o
 | 
			
		||||
common-obj-y += qapi/qapi-commands-trace.o
 | 
			
		||||
common-obj-y += qapi/qapi-commands-transaction.o
 | 
			
		||||
common-obj-y += qapi/qapi-commands-ui.o
 | 
			
		||||
common-obj-y += qapi/qapi-introspect.o
 | 
			
		||||
common-obj-y += qmp.o hmp.o
 | 
			
		||||
endif
 | 
			
		||||
@@ -125,7 +181,6 @@ qga-vss-dll-obj-y = qga/
 | 
			
		||||
 | 
			
		||||
######################################################################
 | 
			
		||||
# contrib
 | 
			
		||||
elf2dmp-obj-y = contrib/elf2dmp/
 | 
			
		||||
ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/
 | 
			
		||||
ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/
 | 
			
		||||
libvhost-user-obj-y = contrib/libvhost-user/
 | 
			
		||||
@@ -133,74 +188,69 @@ vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS)
 | 
			
		||||
vhost-user-scsi.o-libs := $(LIBISCSI_LIBS)
 | 
			
		||||
vhost-user-scsi-obj-y = contrib/vhost-user-scsi/
 | 
			
		||||
vhost-user-blk-obj-y = contrib/vhost-user-blk/
 | 
			
		||||
rdmacm-mux-obj-y = contrib/rdmacm-mux/
 | 
			
		||||
 | 
			
		||||
######################################################################
 | 
			
		||||
trace-events-subdirs =
 | 
			
		||||
trace-events-subdirs += accel/kvm
 | 
			
		||||
trace-events-subdirs += accel/tcg
 | 
			
		||||
trace-events-subdirs += audio
 | 
			
		||||
trace-events-subdirs += util
 | 
			
		||||
trace-events-subdirs += crypto
 | 
			
		||||
trace-events-subdirs += io
 | 
			
		||||
trace-events-subdirs += migration
 | 
			
		||||
trace-events-subdirs += block
 | 
			
		||||
trace-events-subdirs += chardev
 | 
			
		||||
trace-events-subdirs += crypto
 | 
			
		||||
trace-events-subdirs += hw/9pfs
 | 
			
		||||
trace-events-subdirs += hw/acpi
 | 
			
		||||
trace-events-subdirs += hw/alpha
 | 
			
		||||
trace-events-subdirs += hw/arm
 | 
			
		||||
trace-events-subdirs += hw/audio
 | 
			
		||||
trace-events-subdirs += hw/block
 | 
			
		||||
trace-events-subdirs += hw/block/dataplane
 | 
			
		||||
trace-events-subdirs += hw/char
 | 
			
		||||
trace-events-subdirs += hw/display
 | 
			
		||||
trace-events-subdirs += hw/dma
 | 
			
		||||
trace-events-subdirs += hw/hppa
 | 
			
		||||
trace-events-subdirs += hw/i2c
 | 
			
		||||
trace-events-subdirs += hw/i386
 | 
			
		||||
trace-events-subdirs += hw/i386/xen
 | 
			
		||||
trace-events-subdirs += hw/ide
 | 
			
		||||
trace-events-subdirs += hw/input
 | 
			
		||||
trace-events-subdirs += hw/intc
 | 
			
		||||
trace-events-subdirs += hw/isa
 | 
			
		||||
trace-events-subdirs += hw/mem
 | 
			
		||||
trace-events-subdirs += hw/misc
 | 
			
		||||
trace-events-subdirs += hw/misc/macio
 | 
			
		||||
trace-events-subdirs += hw/net
 | 
			
		||||
trace-events-subdirs += hw/nvram
 | 
			
		||||
trace-events-subdirs += hw/pci
 | 
			
		||||
trace-events-subdirs += hw/pci-host
 | 
			
		||||
trace-events-subdirs += hw/ppc
 | 
			
		||||
trace-events-subdirs += hw/rdma
 | 
			
		||||
trace-events-subdirs += hw/rdma/vmw
 | 
			
		||||
trace-events-subdirs += hw/s390x
 | 
			
		||||
trace-events-subdirs += hw/virtio
 | 
			
		||||
trace-events-subdirs += hw/audio
 | 
			
		||||
trace-events-subdirs += hw/misc
 | 
			
		||||
trace-events-subdirs += hw/misc/macio
 | 
			
		||||
trace-events-subdirs += hw/usb
 | 
			
		||||
trace-events-subdirs += hw/scsi
 | 
			
		||||
trace-events-subdirs += hw/sd
 | 
			
		||||
trace-events-subdirs += hw/nvram
 | 
			
		||||
trace-events-subdirs += hw/display
 | 
			
		||||
trace-events-subdirs += hw/input
 | 
			
		||||
trace-events-subdirs += hw/timer
 | 
			
		||||
trace-events-subdirs += hw/dma
 | 
			
		||||
trace-events-subdirs += hw/sparc
 | 
			
		||||
trace-events-subdirs += hw/sparc64
 | 
			
		||||
trace-events-subdirs += hw/timer
 | 
			
		||||
trace-events-subdirs += hw/tpm
 | 
			
		||||
trace-events-subdirs += hw/usb
 | 
			
		||||
trace-events-subdirs += hw/sd
 | 
			
		||||
trace-events-subdirs += hw/isa
 | 
			
		||||
trace-events-subdirs += hw/mem
 | 
			
		||||
trace-events-subdirs += hw/i386
 | 
			
		||||
trace-events-subdirs += hw/i386/xen
 | 
			
		||||
trace-events-subdirs += hw/9pfs
 | 
			
		||||
trace-events-subdirs += hw/ppc
 | 
			
		||||
trace-events-subdirs += hw/pci
 | 
			
		||||
trace-events-subdirs += hw/pci-host
 | 
			
		||||
trace-events-subdirs += hw/s390x
 | 
			
		||||
trace-events-subdirs += hw/vfio
 | 
			
		||||
trace-events-subdirs += hw/virtio
 | 
			
		||||
trace-events-subdirs += hw/watchdog
 | 
			
		||||
trace-events-subdirs += hw/acpi
 | 
			
		||||
trace-events-subdirs += hw/arm
 | 
			
		||||
trace-events-subdirs += hw/alpha
 | 
			
		||||
trace-events-subdirs += hw/hppa
 | 
			
		||||
trace-events-subdirs += hw/xen
 | 
			
		||||
trace-events-subdirs += hw/gpio
 | 
			
		||||
trace-events-subdirs += io
 | 
			
		||||
trace-events-subdirs += linux-user
 | 
			
		||||
trace-events-subdirs += migration
 | 
			
		||||
trace-events-subdirs += nbd
 | 
			
		||||
trace-events-subdirs += hw/ide
 | 
			
		||||
trace-events-subdirs += hw/tpm
 | 
			
		||||
trace-events-subdirs += ui
 | 
			
		||||
trace-events-subdirs += audio
 | 
			
		||||
trace-events-subdirs += net
 | 
			
		||||
trace-events-subdirs += qapi
 | 
			
		||||
trace-events-subdirs += qom
 | 
			
		||||
trace-events-subdirs += scsi
 | 
			
		||||
trace-events-subdirs += slirp
 | 
			
		||||
trace-events-subdirs += target/arm
 | 
			
		||||
trace-events-subdirs += target/i386
 | 
			
		||||
trace-events-subdirs += target/mips
 | 
			
		||||
trace-events-subdirs += target/ppc
 | 
			
		||||
trace-events-subdirs += target/s390x
 | 
			
		||||
trace-events-subdirs += target/sparc
 | 
			
		||||
trace-events-subdirs += ui
 | 
			
		||||
trace-events-subdirs += util
 | 
			
		||||
trace-events-subdirs += target/s390x
 | 
			
		||||
trace-events-subdirs += target/ppc
 | 
			
		||||
trace-events-subdirs += qom
 | 
			
		||||
trace-events-subdirs += linux-user
 | 
			
		||||
trace-events-subdirs += qapi
 | 
			
		||||
trace-events-subdirs += accel/tcg
 | 
			
		||||
trace-events-subdirs += accel/kvm
 | 
			
		||||
trace-events-subdirs += nbd
 | 
			
		||||
trace-events-subdirs += scsi
 | 
			
		||||
 | 
			
		||||
trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,9 +11,9 @@ $(call set-vpath, $(SRC_PATH):$(BUILD_DIR))
 | 
			
		||||
ifdef CONFIG_LINUX
 | 
			
		||||
QEMU_CFLAGS += -I../linux-headers
 | 
			
		||||
endif
 | 
			
		||||
QEMU_CFLAGS += -iquote .. -iquote $(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H
 | 
			
		||||
QEMU_CFLAGS += -I.. -I$(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H
 | 
			
		||||
 | 
			
		||||
QEMU_CFLAGS+=-iquote $(SRC_PATH)/include
 | 
			
		||||
QEMU_CFLAGS+=-I$(SRC_PATH)/include
 | 
			
		||||
 | 
			
		||||
ifdef CONFIG_USER_ONLY
 | 
			
		||||
# user emulator name
 | 
			
		||||
@@ -36,16 +36,11 @@ endif
 | 
			
		||||
PROGS=$(QEMU_PROG) $(QEMU_PROGW)
 | 
			
		||||
STPFILES=
 | 
			
		||||
 | 
			
		||||
# Makefile Tests
 | 
			
		||||
ifdef CONFIG_USER_ONLY
 | 
			
		||||
include $(SRC_PATH)/tests/tcg/Makefile.include
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
config-target.h: config-target.h-timestamp
 | 
			
		||||
config-target.h-timestamp: config-target.mak
 | 
			
		||||
 | 
			
		||||
ifdef CONFIG_TRACE_SYSTEMTAP
 | 
			
		||||
stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp $(QEMU_PROG)-log.stp
 | 
			
		||||
stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp
 | 
			
		||||
 | 
			
		||||
ifdef CONFIG_USER_ONLY
 | 
			
		||||
TARGET_TYPE=user
 | 
			
		||||
@@ -84,14 +79,6 @@ $(QEMU_PROG)-simpletrace.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y)
 | 
			
		||||
		--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \
 | 
			
		||||
		$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp")
 | 
			
		||||
 | 
			
		||||
$(QEMU_PROG)-log.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y)
 | 
			
		||||
	$(call quiet-command,$(TRACETOOL) \
 | 
			
		||||
		--group=all \
 | 
			
		||||
		--format=log-stap \
 | 
			
		||||
		--backends=$(TRACE_BACKENDS) \
 | 
			
		||||
		--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \
 | 
			
		||||
		$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-log.stp")
 | 
			
		||||
 | 
			
		||||
else
 | 
			
		||||
stap:
 | 
			
		||||
endif
 | 
			
		||||
@@ -110,7 +97,7 @@ obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o
 | 
			
		||||
obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o
 | 
			
		||||
obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o
 | 
			
		||||
obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o
 | 
			
		||||
obj-$(CONFIG_TCG) += fpu/softfloat.o
 | 
			
		||||
obj-y += fpu/softfloat.o
 | 
			
		||||
obj-y += target/$(TARGET_BASE_ARCH)/
 | 
			
		||||
obj-y += disas.o
 | 
			
		||||
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
 | 
			
		||||
@@ -151,7 +138,6 @@ obj-y += hw/
 | 
			
		||||
obj-y += memory.o
 | 
			
		||||
obj-y += memory_mapping.o
 | 
			
		||||
obj-y += dump.o
 | 
			
		||||
obj-$(TARGET_X86_64) += win_dump.o
 | 
			
		||||
obj-y += migration/ram.o
 | 
			
		||||
LIBS := $(libs_softmmu) $(LIBS)
 | 
			
		||||
 | 
			
		||||
@@ -166,6 +152,9 @@ GENERATED_FILES += hmp-commands.h hmp-commands-info.h
 | 
			
		||||
 | 
			
		||||
endif # CONFIG_SOFTMMU
 | 
			
		||||
 | 
			
		||||
# Workaround for http://gcc.gnu.org/PR55489, see configure.
 | 
			
		||||
%/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS)
 | 
			
		||||
 | 
			
		||||
dummy := $(call unnest-vars,,obj-y)
 | 
			
		||||
all-obj-y := $(obj-y)
 | 
			
		||||
 | 
			
		||||
@@ -173,7 +162,6 @@ target-obj-y :=
 | 
			
		||||
block-obj-y :=
 | 
			
		||||
common-obj-y :=
 | 
			
		||||
chardev-obj-y :=
 | 
			
		||||
slirp-obj-y :=
 | 
			
		||||
include $(SRC_PATH)/Makefile.objs
 | 
			
		||||
dummy := $(call unnest-vars,,target-obj-y)
 | 
			
		||||
target-obj-y-save := $(target-obj-y)
 | 
			
		||||
@@ -186,8 +174,7 @@ dummy := $(call unnest-vars,.., \
 | 
			
		||||
               qom-obj-y \
 | 
			
		||||
               io-obj-y \
 | 
			
		||||
               common-obj-y \
 | 
			
		||||
               common-obj-m \
 | 
			
		||||
               slirp-obj-y)
 | 
			
		||||
               common-obj-m)
 | 
			
		||||
target-obj-y := $(target-obj-y-save)
 | 
			
		||||
all-obj-y += $(common-obj-y)
 | 
			
		||||
all-obj-y += $(target-obj-y)
 | 
			
		||||
@@ -196,7 +183,6 @@ all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y)
 | 
			
		||||
all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
 | 
			
		||||
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
 | 
			
		||||
all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
 | 
			
		||||
all-obj-$(CONFIG_SOFTMMU) += $(slirp-obj-y)
 | 
			
		||||
 | 
			
		||||
$(QEMU_PROG_BUILD): config-devices.mak
 | 
			
		||||
 | 
			
		||||
@@ -235,7 +221,6 @@ ifdef CONFIG_TRACE_SYSTEMTAP
 | 
			
		||||
	$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset"
 | 
			
		||||
	$(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp"
 | 
			
		||||
	$(INSTALL_DATA) $(QEMU_PROG)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp"
 | 
			
		||||
	$(INSTALL_DATA) $(QEMU_PROG)-log.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-log.stp"
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
GENERATED_FILES += config-target.h
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								README
									
									
									
									
									
								
							@@ -54,7 +54,7 @@ Submitting patches
 | 
			
		||||
 | 
			
		||||
The QEMU source code is maintained under the GIT version control system.
 | 
			
		||||
 | 
			
		||||
   git clone https://git.qemu.org/git/qemu.git
 | 
			
		||||
   git clone git://git.qemu.org/qemu.git
 | 
			
		||||
 | 
			
		||||
When submitting patches, one common approach is to use 'git
 | 
			
		||||
format-patch' and/or 'git send-email' to format & send the mail to the
 | 
			
		||||
@@ -70,7 +70,7 @@ the QEMU website
 | 
			
		||||
 | 
			
		||||
The QEMU website is also maintained under source control.
 | 
			
		||||
 | 
			
		||||
  git clone https://git.qemu.org/git/qemu-web.git
 | 
			
		||||
  git clone git://git.qemu.org/qemu-web.git
 | 
			
		||||
  https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/
 | 
			
		||||
 | 
			
		||||
A 'git-publish' utility was created to make above process less
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
obj-$(CONFIG_SOFTMMU) += accel.o
 | 
			
		||||
obj-$(CONFIG_KVM) += kvm/
 | 
			
		||||
obj-y += kvm/
 | 
			
		||||
obj-$(CONFIG_TCG) += tcg/
 | 
			
		||||
obj-y += stubs/
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,6 @@
 | 
			
		||||
#include "qom/object.h"
 | 
			
		||||
#include "qemu/error-report.h"
 | 
			
		||||
#include "qemu/option.h"
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
 | 
			
		||||
static const TypeInfo accel_type = {
 | 
			
		||||
    .name = TYPE_ACCEL,
 | 
			
		||||
@@ -69,10 +68,10 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms)
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void configure_accelerator(MachineState *ms, const char *progname)
 | 
			
		||||
void configure_accelerator(MachineState *ms)
 | 
			
		||||
{
 | 
			
		||||
    const char *accel;
 | 
			
		||||
    char **accel_list, **tmp;
 | 
			
		||||
    const char *accel, *p;
 | 
			
		||||
    char buf[10];
 | 
			
		||||
    int ret;
 | 
			
		||||
    bool accel_initialised = false;
 | 
			
		||||
    bool init_failed = false;
 | 
			
		||||
@@ -80,26 +79,17 @@ void configure_accelerator(MachineState *ms, const char *progname)
 | 
			
		||||
 | 
			
		||||
    accel = qemu_opt_get(qemu_get_machine_opts(), "accel");
 | 
			
		||||
    if (accel == NULL) {
 | 
			
		||||
        /* Select the default accelerator */
 | 
			
		||||
        int pnlen = strlen(progname);
 | 
			
		||||
        if (pnlen >= 3 && g_str_equal(&progname[pnlen - 3], "kvm")) {
 | 
			
		||||
            /* If the program name ends with "kvm", we prefer KVM */
 | 
			
		||||
            accel = "kvm:tcg";
 | 
			
		||||
        } else {
 | 
			
		||||
#if defined(CONFIG_TCG)
 | 
			
		||||
        /* Use the default "accelerator", tcg */
 | 
			
		||||
        accel = "tcg";
 | 
			
		||||
#elif defined(CONFIG_KVM)
 | 
			
		||||
            accel = "kvm";
 | 
			
		||||
#else
 | 
			
		||||
#error "No default accelerator available"
 | 
			
		||||
#endif
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    accel_list = g_strsplit(accel, ":", 0);
 | 
			
		||||
 | 
			
		||||
    for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) {
 | 
			
		||||
        acc = accel_find(*tmp);
 | 
			
		||||
    p = accel;
 | 
			
		||||
    while (!accel_initialised && *p != '\0') {
 | 
			
		||||
        if (*p == ':') {
 | 
			
		||||
            p++;
 | 
			
		||||
        }
 | 
			
		||||
        p = get_opt_name(buf, sizeof(buf), p, ':');
 | 
			
		||||
        acc = accel_find(buf);
 | 
			
		||||
        if (!acc) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
@@ -117,7 +107,6 @@ void configure_accelerator(MachineState *ms, const char *progname)
 | 
			
		||||
            accel_initialised = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    g_strfreev(accel_list);
 | 
			
		||||
 | 
			
		||||
    if (!accel_initialised) {
 | 
			
		||||
        if (!init_failed) {
 | 
			
		||||
@@ -131,13 +120,10 @@ void configure_accelerator(MachineState *ms, const char *progname)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void accel_setup_post(MachineState *ms)
 | 
			
		||||
void accel_register_compat_props(AccelState *accel)
 | 
			
		||||
{
 | 
			
		||||
    AccelState *accel = ms->accelerator;
 | 
			
		||||
    AccelClass *acc = ACCEL_GET_CLASS(accel);
 | 
			
		||||
    if (acc->setup_post) {
 | 
			
		||||
        acc->setup_post(ms, accel);
 | 
			
		||||
    }
 | 
			
		||||
    AccelClass *class = ACCEL_GET_CLASS(accel);
 | 
			
		||||
    register_compat_props_array(class->global_props);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void register_accel_types(void)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,2 +1 @@
 | 
			
		||||
obj-y += kvm-all.o
 | 
			
		||||
obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o
 | 
			
		||||
obj-$(CONFIG_KVM) += kvm-all.o
 | 
			
		||||
 
 | 
			
		||||
@@ -38,8 +38,6 @@
 | 
			
		||||
#include "qemu/event_notifier.h"
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
#include "hw/irq.h"
 | 
			
		||||
#include "sysemu/sev.h"
 | 
			
		||||
#include "sysemu/balloon.h"
 | 
			
		||||
 | 
			
		||||
#include "hw/boards.h"
 | 
			
		||||
 | 
			
		||||
@@ -79,14 +77,13 @@ struct KVMState
 | 
			
		||||
    int fd;
 | 
			
		||||
    int vmfd;
 | 
			
		||||
    int coalesced_mmio;
 | 
			
		||||
    int coalesced_pio;
 | 
			
		||||
    struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
 | 
			
		||||
    bool coalesced_flush_in_progress;
 | 
			
		||||
    int vcpu_events;
 | 
			
		||||
    int robust_singlestep;
 | 
			
		||||
    int debugregs;
 | 
			
		||||
#ifdef KVM_CAP_SET_GUEST_DEBUG
 | 
			
		||||
    QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints;
 | 
			
		||||
    struct kvm_sw_breakpoint_head kvm_sw_breakpoints;
 | 
			
		||||
#endif
 | 
			
		||||
    int many_ioeventfds;
 | 
			
		||||
    int intx_set_mask;
 | 
			
		||||
@@ -102,14 +99,10 @@ struct KVMState
 | 
			
		||||
    int nr_allocated_irq_routes;
 | 
			
		||||
    unsigned long *used_gsi_bitmap;
 | 
			
		||||
    unsigned int gsi_count;
 | 
			
		||||
    QTAILQ_HEAD(, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE];
 | 
			
		||||
    QTAILQ_HEAD(msi_hashtab, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE];
 | 
			
		||||
#endif
 | 
			
		||||
    KVMMemoryListener memory_listener;
 | 
			
		||||
    QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus;
 | 
			
		||||
 | 
			
		||||
    /* memory encryption */
 | 
			
		||||
    void *memcrypt_handle;
 | 
			
		||||
    int (*memcrypt_encrypt_data)(void *handle, uint8_t *ptr, uint64_t len);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
KVMState *kvm_state;
 | 
			
		||||
@@ -145,26 +138,6 @@ int kvm_get_max_memslots(void)
 | 
			
		||||
    return s->nr_slots;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool kvm_memcrypt_enabled(void)
 | 
			
		||||
{
 | 
			
		||||
    if (kvm_state && kvm_state->memcrypt_handle) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len)
 | 
			
		||||
{
 | 
			
		||||
    if (kvm_state->memcrypt_handle &&
 | 
			
		||||
        kvm_state->memcrypt_encrypt_data) {
 | 
			
		||||
        return kvm_state->memcrypt_encrypt_data(kvm_state->memcrypt_handle,
 | 
			
		||||
                                              ptr, len);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml)
 | 
			
		||||
{
 | 
			
		||||
    KVMState *s = kvm_state;
 | 
			
		||||
@@ -258,7 +231,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram,
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new)
 | 
			
		||||
static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot)
 | 
			
		||||
{
 | 
			
		||||
    KVMState *s = kvm_state;
 | 
			
		||||
    struct kvm_userspace_memory_region mem;
 | 
			
		||||
@@ -269,7 +242,7 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo
 | 
			
		||||
    mem.userspace_addr = (unsigned long)slot->ram;
 | 
			
		||||
    mem.flags = slot->flags;
 | 
			
		||||
 | 
			
		||||
    if (slot->memory_size && !new && (mem.flags ^ slot->old_flags) & KVM_MEM_READONLY) {
 | 
			
		||||
    if (slot->memory_size && mem.flags & KVM_MEM_READONLY) {
 | 
			
		||||
        /* Set the slot size to 0 before setting the slot to the desired
 | 
			
		||||
         * value. This is needed based on KVM commit 75d61fbc. */
 | 
			
		||||
        mem.memory_size = 0;
 | 
			
		||||
@@ -277,7 +250,6 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo
 | 
			
		||||
    }
 | 
			
		||||
    mem.memory_size = slot->memory_size;
 | 
			
		||||
    ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem);
 | 
			
		||||
    slot->old_flags = mem.flags;
 | 
			
		||||
    trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr,
 | 
			
		||||
                              mem.memory_size, mem.userspace_addr, ret);
 | 
			
		||||
    return ret;
 | 
			
		||||
@@ -394,14 +366,17 @@ static int kvm_mem_flags(MemoryRegion *mr)
 | 
			
		||||
static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem,
 | 
			
		||||
                                 MemoryRegion *mr)
 | 
			
		||||
{
 | 
			
		||||
    int old_flags;
 | 
			
		||||
 | 
			
		||||
    old_flags = mem->flags;
 | 
			
		||||
    mem->flags = kvm_mem_flags(mr);
 | 
			
		||||
 | 
			
		||||
    /* If nothing changed effectively, no need to issue ioctl */
 | 
			
		||||
    if (mem->flags == mem->old_flags) {
 | 
			
		||||
    if (mem->flags == old_flags) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return kvm_set_user_memory_region(kml, mem, false);
 | 
			
		||||
    return kvm_set_user_memory_region(kml, mem);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int kvm_section_update_flags(KVMMemoryListener *kml,
 | 
			
		||||
@@ -561,45 +536,6 @@ static void kvm_uncoalesce_mmio_region(MemoryListener *listener,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void kvm_coalesce_pio_add(MemoryListener *listener,
 | 
			
		||||
                                MemoryRegionSection *section,
 | 
			
		||||
                                hwaddr start, hwaddr size)
 | 
			
		||||
{
 | 
			
		||||
    KVMState *s = kvm_state;
 | 
			
		||||
 | 
			
		||||
    if (s->coalesced_pio) {
 | 
			
		||||
        struct kvm_coalesced_mmio_zone zone;
 | 
			
		||||
 | 
			
		||||
        zone.addr = start;
 | 
			
		||||
        zone.size = size;
 | 
			
		||||
        zone.pio = 1;
 | 
			
		||||
 | 
			
		||||
        (void)kvm_vm_ioctl(s, KVM_REGISTER_COALESCED_MMIO, &zone);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void kvm_coalesce_pio_del(MemoryListener *listener,
 | 
			
		||||
                                MemoryRegionSection *section,
 | 
			
		||||
                                hwaddr start, hwaddr size)
 | 
			
		||||
{
 | 
			
		||||
    KVMState *s = kvm_state;
 | 
			
		||||
 | 
			
		||||
    if (s->coalesced_pio) {
 | 
			
		||||
        struct kvm_coalesced_mmio_zone zone;
 | 
			
		||||
 | 
			
		||||
        zone.addr = start;
 | 
			
		||||
        zone.size = size;
 | 
			
		||||
        zone.pio = 1;
 | 
			
		||||
 | 
			
		||||
        (void)kvm_vm_ioctl(s, KVM_UNREGISTER_COALESCED_MMIO, &zone);
 | 
			
		||||
     }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static MemoryListener kvm_coalesced_pio_listener = {
 | 
			
		||||
    .coalesced_io_add = kvm_coalesce_pio_add,
 | 
			
		||||
    .coalesced_io_del = kvm_coalesce_pio_del,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int kvm_check_extension(KVMState *s, unsigned int extension)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
@@ -794,8 +730,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
 | 
			
		||||
 | 
			
		||||
        /* unregister the slot */
 | 
			
		||||
        mem->memory_size = 0;
 | 
			
		||||
        mem->flags = 0;
 | 
			
		||||
        err = kvm_set_user_memory_region(kml, mem, false);
 | 
			
		||||
        err = kvm_set_user_memory_region(kml, mem);
 | 
			
		||||
        if (err) {
 | 
			
		||||
            fprintf(stderr, "%s: error unregistering slot: %s\n",
 | 
			
		||||
                    __func__, strerror(-err));
 | 
			
		||||
@@ -811,7 +746,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
 | 
			
		||||
    mem->ram = ram;
 | 
			
		||||
    mem->flags = kvm_mem_flags(mr);
 | 
			
		||||
 | 
			
		||||
    err = kvm_set_user_memory_region(kml, mem, true);
 | 
			
		||||
    err = kvm_set_user_memory_region(kml, mem);
 | 
			
		||||
    if (err) {
 | 
			
		||||
        fprintf(stderr, "%s: error registering slot: %s\n", __func__,
 | 
			
		||||
                strerror(-err));
 | 
			
		||||
@@ -1656,8 +1591,6 @@ static int kvm_init(MachineState *ms)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO);
 | 
			
		||||
    s->coalesced_pio = s->coalesced_mmio &&
 | 
			
		||||
                       kvm_check_extension(s, KVM_CAP_COALESCED_PIO);
 | 
			
		||||
 | 
			
		||||
#ifdef KVM_CAP_VCPU_EVENTS
 | 
			
		||||
    s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS);
 | 
			
		||||
@@ -1681,8 +1614,10 @@ static int kvm_init(MachineState *ms)
 | 
			
		||||
        s->irq_set_ioctl = KVM_IRQ_LINE_STATUS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef KVM_CAP_READONLY_MEM
 | 
			
		||||
    kvm_readonly_mem_allowed =
 | 
			
		||||
        (kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    kvm_eventfds_allowed =
 | 
			
		||||
        (kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0);
 | 
			
		||||
@@ -1701,20 +1636,6 @@ static int kvm_init(MachineState *ms)
 | 
			
		||||
 | 
			
		||||
    kvm_state = s;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * if memory encryption object is specified then initialize the memory
 | 
			
		||||
     * encryption context.
 | 
			
		||||
     */
 | 
			
		||||
    if (ms->memory_encryption) {
 | 
			
		||||
        kvm_state->memcrypt_handle = sev_guest_init(ms->memory_encryption);
 | 
			
		||||
        if (!kvm_state->memcrypt_handle) {
 | 
			
		||||
            ret = -1;
 | 
			
		||||
            goto err;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        kvm_state->memcrypt_encrypt_data = sev_encrypt_data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = kvm_arch_init(ms, s);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto err;
 | 
			
		||||
@@ -1728,22 +1649,17 @@ static int kvm_init(MachineState *ms)
 | 
			
		||||
        s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add;
 | 
			
		||||
        s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del;
 | 
			
		||||
    }
 | 
			
		||||
    s->memory_listener.listener.coalesced_io_add = kvm_coalesce_mmio_region;
 | 
			
		||||
    s->memory_listener.listener.coalesced_io_del = kvm_uncoalesce_mmio_region;
 | 
			
		||||
    s->memory_listener.listener.coalesced_mmio_add = kvm_coalesce_mmio_region;
 | 
			
		||||
    s->memory_listener.listener.coalesced_mmio_del = kvm_uncoalesce_mmio_region;
 | 
			
		||||
 | 
			
		||||
    kvm_memory_listener_register(s, &s->memory_listener,
 | 
			
		||||
                                 &address_space_memory, 0);
 | 
			
		||||
    memory_listener_register(&kvm_io_listener,
 | 
			
		||||
                             &address_space_io);
 | 
			
		||||
    memory_listener_register(&kvm_coalesced_pio_listener,
 | 
			
		||||
                             &address_space_io);
 | 
			
		||||
 | 
			
		||||
    s->many_ioeventfds = kvm_check_many_ioeventfds();
 | 
			
		||||
 | 
			
		||||
    s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU);
 | 
			
		||||
    if (!s->sync_mmu) {
 | 
			
		||||
        qemu_balloon_inhibit(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
@@ -1822,13 +1738,7 @@ void kvm_flush_coalesced_mmio_buffer(void)
 | 
			
		||||
 | 
			
		||||
            ent = &ring->coalesced_mmio[ring->first];
 | 
			
		||||
 | 
			
		||||
            if (ent->pio == 1) {
 | 
			
		||||
                address_space_rw(&address_space_io, ent->phys_addr,
 | 
			
		||||
                                 MEMTXATTRS_UNSPECIFIED, ent->data,
 | 
			
		||||
                                 ent->len, true);
 | 
			
		||||
            } else {
 | 
			
		||||
            cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len);
 | 
			
		||||
            }
 | 
			
		||||
            smp_wmb();
 | 
			
		||||
            ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,26 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * QEMU SEV stub
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright Advanced Micro Devices 2018
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *      Brijesh Singh <brijesh.singh@amd.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 | 
			
		||||
 * See the COPYING file in the top-level directory.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "sysemu/sev.h"
 | 
			
		||||
 | 
			
		||||
int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len)
 | 
			
		||||
{
 | 
			
		||||
    abort();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *sev_guest_init(const char *id)
 | 
			
		||||
{
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
@@ -105,16 +105,6 @@ int kvm_on_sigbus(int code, void *addr)
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool kvm_memcrypt_enabled(void)
 | 
			
		||||
{
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len)
 | 
			
		||||
{
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef CONFIG_USER_ONLY
 | 
			
		||||
int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,10 @@ void tb_flush(CPUState *cpu)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void tb_unlock(void)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void tlb_set_dirty(CPUState *cpu, target_ulong vaddr)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
@@ -18,37 +18,26 @@
 | 
			
		||||
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "trace/mem.h"
 | 
			
		||||
 | 
			
		||||
#if DATA_SIZE == 16
 | 
			
		||||
# define SUFFIX     o
 | 
			
		||||
# define DATA_TYPE  Int128
 | 
			
		||||
# define BSWAP      bswap128
 | 
			
		||||
# define SHIFT      4
 | 
			
		||||
#elif DATA_SIZE == 8
 | 
			
		||||
# define SUFFIX     q
 | 
			
		||||
# define DATA_TYPE  uint64_t
 | 
			
		||||
# define SDATA_TYPE int64_t
 | 
			
		||||
# define BSWAP      bswap64
 | 
			
		||||
# define SHIFT      3
 | 
			
		||||
#elif DATA_SIZE == 4
 | 
			
		||||
# define SUFFIX     l
 | 
			
		||||
# define DATA_TYPE  uint32_t
 | 
			
		||||
# define SDATA_TYPE int32_t
 | 
			
		||||
# define BSWAP      bswap32
 | 
			
		||||
# define SHIFT      2
 | 
			
		||||
#elif DATA_SIZE == 2
 | 
			
		||||
# define SUFFIX     w
 | 
			
		||||
# define DATA_TYPE  uint16_t
 | 
			
		||||
# define SDATA_TYPE int16_t
 | 
			
		||||
# define BSWAP      bswap16
 | 
			
		||||
# define SHIFT      1
 | 
			
		||||
#elif DATA_SIZE == 1
 | 
			
		||||
# define SUFFIX     b
 | 
			
		||||
# define DATA_TYPE  uint8_t
 | 
			
		||||
# define SDATA_TYPE int8_t
 | 
			
		||||
# define BSWAP
 | 
			
		||||
# define SHIFT      0
 | 
			
		||||
#else
 | 
			
		||||
# error unsupported data size
 | 
			
		||||
#endif
 | 
			
		||||
@@ -59,37 +48,14 @@
 | 
			
		||||
# define ABI_TYPE  uint32_t
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define ATOMIC_TRACE_RMW do {                                           \
 | 
			
		||||
        uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
 | 
			
		||||
                                                                        \
 | 
			
		||||
        trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \
 | 
			
		||||
        trace_guest_mem_before_exec(ENV_GET_CPU(env), addr,             \
 | 
			
		||||
                                    info | TRACE_MEM_ST);               \
 | 
			
		||||
    } while (0)
 | 
			
		||||
 | 
			
		||||
#define ATOMIC_TRACE_LD do {                                            \
 | 
			
		||||
        uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
 | 
			
		||||
                                                                        \
 | 
			
		||||
        trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \
 | 
			
		||||
    } while (0)
 | 
			
		||||
 | 
			
		||||
# define ATOMIC_TRACE_ST do {                                           \
 | 
			
		||||
        uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true); \
 | 
			
		||||
                                                                        \
 | 
			
		||||
        trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \
 | 
			
		||||
    } while (0)
 | 
			
		||||
 | 
			
		||||
/* Define host-endian atomic operations.  Note that END is used within
 | 
			
		||||
   the ATOMIC_NAME macro, and redefined below.  */
 | 
			
		||||
#if DATA_SIZE == 1
 | 
			
		||||
# define END
 | 
			
		||||
# define MEND _be /* either le or be would be fine */
 | 
			
		||||
#elif defined(HOST_WORDS_BIGENDIAN)
 | 
			
		||||
# define END  _be
 | 
			
		||||
# define MEND _be
 | 
			
		||||
#else
 | 
			
		||||
# define END  _le
 | 
			
		||||
# define MEND _le
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
 | 
			
		||||
@@ -97,27 +63,17 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
 | 
			
		||||
{
 | 
			
		||||
    ATOMIC_MMU_DECLS;
 | 
			
		||||
    DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
 | 
			
		||||
    DATA_TYPE ret;
 | 
			
		||||
 | 
			
		||||
    ATOMIC_TRACE_RMW;
 | 
			
		||||
#if DATA_SIZE == 16
 | 
			
		||||
    ret = atomic16_cmpxchg(haddr, cmpv, newv);
 | 
			
		||||
#else
 | 
			
		||||
    ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv);
 | 
			
		||||
#endif
 | 
			
		||||
    DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv);
 | 
			
		||||
    ATOMIC_MMU_CLEANUP;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if DATA_SIZE >= 16
 | 
			
		||||
#if HAVE_ATOMIC128
 | 
			
		||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
 | 
			
		||||
{
 | 
			
		||||
    ATOMIC_MMU_DECLS;
 | 
			
		||||
    DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
 | 
			
		||||
 | 
			
		||||
    ATOMIC_TRACE_LD;
 | 
			
		||||
    val = atomic16_read(haddr);
 | 
			
		||||
    __atomic_load(haddr, &val, __ATOMIC_RELAXED);
 | 
			
		||||
    ATOMIC_MMU_CLEANUP;
 | 
			
		||||
    return val;
 | 
			
		||||
}
 | 
			
		||||
@@ -127,22 +83,16 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
 | 
			
		||||
{
 | 
			
		||||
    ATOMIC_MMU_DECLS;
 | 
			
		||||
    DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
 | 
			
		||||
 | 
			
		||||
    ATOMIC_TRACE_ST;
 | 
			
		||||
    atomic16_set(haddr, val);
 | 
			
		||||
    __atomic_store(haddr, &val, __ATOMIC_RELAXED);
 | 
			
		||||
    ATOMIC_MMU_CLEANUP;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#else
 | 
			
		||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
 | 
			
		||||
                           ABI_TYPE val EXTRA_ARGS)
 | 
			
		||||
{
 | 
			
		||||
    ATOMIC_MMU_DECLS;
 | 
			
		||||
    DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
 | 
			
		||||
    DATA_TYPE ret;
 | 
			
		||||
 | 
			
		||||
    ATOMIC_TRACE_RMW;
 | 
			
		||||
    ret = atomic_xchg__nocheck(haddr, val);
 | 
			
		||||
    DATA_TYPE ret = atomic_xchg__nocheck(haddr, val);
 | 
			
		||||
    ATOMIC_MMU_CLEANUP;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
@@ -153,10 +103,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \
 | 
			
		||||
{                                                                   \
 | 
			
		||||
    ATOMIC_MMU_DECLS;                                               \
 | 
			
		||||
    DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \
 | 
			
		||||
    DATA_TYPE ret;                                                  \
 | 
			
		||||
                                                                    \
 | 
			
		||||
    ATOMIC_TRACE_RMW;                                               \
 | 
			
		||||
    ret = atomic_##X(haddr, val);                                   \
 | 
			
		||||
    DATA_TYPE ret = atomic_##X(haddr, val);                         \
 | 
			
		||||
    ATOMIC_MMU_CLEANUP;                                             \
 | 
			
		||||
    return ret;                                                     \
 | 
			
		||||
}
 | 
			
		||||
@@ -171,48 +118,9 @@ GEN_ATOMIC_HELPER(or_fetch)
 | 
			
		||||
GEN_ATOMIC_HELPER(xor_fetch)
 | 
			
		||||
 | 
			
		||||
#undef GEN_ATOMIC_HELPER
 | 
			
		||||
 | 
			
		||||
/* These helpers are, as a whole, full barriers.  Within the helper,
 | 
			
		||||
 * the leading barrier is explicit and the trailing barrier is within
 | 
			
		||||
 * cmpxchg primitive.
 | 
			
		||||
 *
 | 
			
		||||
 * Trace this load + RMW loop as a single RMW op. This way, regardless
 | 
			
		||||
 * of CF_PARALLEL's value, we'll trace just a read and a write.
 | 
			
		||||
 */
 | 
			
		||||
#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET)                \
 | 
			
		||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \
 | 
			
		||||
                        ABI_TYPE xval EXTRA_ARGS)                   \
 | 
			
		||||
{                                                                   \
 | 
			
		||||
    ATOMIC_MMU_DECLS;                                               \
 | 
			
		||||
    XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                          \
 | 
			
		||||
    XDATA_TYPE cmp, old, new, val = xval;                           \
 | 
			
		||||
                                                                    \
 | 
			
		||||
    ATOMIC_TRACE_RMW;                                               \
 | 
			
		||||
    smp_mb();                                                       \
 | 
			
		||||
    cmp = atomic_read__nocheck(haddr);                              \
 | 
			
		||||
    do {                                                            \
 | 
			
		||||
        old = cmp; new = FN(old, val);                              \
 | 
			
		||||
        cmp = atomic_cmpxchg__nocheck(haddr, old, new);             \
 | 
			
		||||
    } while (cmp != old);                                           \
 | 
			
		||||
    ATOMIC_MMU_CLEANUP;                                             \
 | 
			
		||||
    return RET;                                                     \
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old)
 | 
			
		||||
GEN_ATOMIC_HELPER_FN(fetch_umin, MIN,  DATA_TYPE, old)
 | 
			
		||||
GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old)
 | 
			
		||||
GEN_ATOMIC_HELPER_FN(fetch_umax, MAX,  DATA_TYPE, old)
 | 
			
		||||
 | 
			
		||||
GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new)
 | 
			
		||||
GEN_ATOMIC_HELPER_FN(umin_fetch, MIN,  DATA_TYPE, new)
 | 
			
		||||
GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
 | 
			
		||||
GEN_ATOMIC_HELPER_FN(umax_fetch, MAX,  DATA_TYPE, new)
 | 
			
		||||
 | 
			
		||||
#undef GEN_ATOMIC_HELPER_FN
 | 
			
		||||
#endif /* DATA SIZE >= 16 */
 | 
			
		||||
 | 
			
		||||
#undef END
 | 
			
		||||
#undef MEND
 | 
			
		||||
 | 
			
		||||
#if DATA_SIZE > 1
 | 
			
		||||
 | 
			
		||||
@@ -220,10 +128,8 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX,  DATA_TYPE, new)
 | 
			
		||||
   within the ATOMIC_NAME macro.  */
 | 
			
		||||
#ifdef HOST_WORDS_BIGENDIAN
 | 
			
		||||
# define END  _le
 | 
			
		||||
# define MEND _le
 | 
			
		||||
#else
 | 
			
		||||
# define END  _be
 | 
			
		||||
# define MEND _be
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
 | 
			
		||||
@@ -231,27 +137,17 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
 | 
			
		||||
{
 | 
			
		||||
    ATOMIC_MMU_DECLS;
 | 
			
		||||
    DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
 | 
			
		||||
    DATA_TYPE ret;
 | 
			
		||||
 | 
			
		||||
    ATOMIC_TRACE_RMW;
 | 
			
		||||
#if DATA_SIZE == 16
 | 
			
		||||
    ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv));
 | 
			
		||||
#else
 | 
			
		||||
    ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
 | 
			
		||||
#endif
 | 
			
		||||
    DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
 | 
			
		||||
    ATOMIC_MMU_CLEANUP;
 | 
			
		||||
    return BSWAP(ret);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if DATA_SIZE >= 16
 | 
			
		||||
#if HAVE_ATOMIC128
 | 
			
		||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
 | 
			
		||||
{
 | 
			
		||||
    ATOMIC_MMU_DECLS;
 | 
			
		||||
    DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
 | 
			
		||||
 | 
			
		||||
    ATOMIC_TRACE_LD;
 | 
			
		||||
    val = atomic16_read(haddr);
 | 
			
		||||
    __atomic_load(haddr, &val, __ATOMIC_RELAXED);
 | 
			
		||||
    ATOMIC_MMU_CLEANUP;
 | 
			
		||||
    return BSWAP(val);
 | 
			
		||||
}
 | 
			
		||||
@@ -261,23 +157,17 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
 | 
			
		||||
{
 | 
			
		||||
    ATOMIC_MMU_DECLS;
 | 
			
		||||
    DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
 | 
			
		||||
 | 
			
		||||
    ATOMIC_TRACE_ST;
 | 
			
		||||
    val = BSWAP(val);
 | 
			
		||||
    atomic16_set(haddr, val);
 | 
			
		||||
    __atomic_store(haddr, &val, __ATOMIC_RELAXED);
 | 
			
		||||
    ATOMIC_MMU_CLEANUP;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#else
 | 
			
		||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
 | 
			
		||||
                           ABI_TYPE val EXTRA_ARGS)
 | 
			
		||||
{
 | 
			
		||||
    ATOMIC_MMU_DECLS;
 | 
			
		||||
    DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
 | 
			
		||||
    ABI_TYPE ret;
 | 
			
		||||
 | 
			
		||||
    ATOMIC_TRACE_RMW;
 | 
			
		||||
    ret = atomic_xchg__nocheck(haddr, BSWAP(val));
 | 
			
		||||
    ABI_TYPE ret = atomic_xchg__nocheck(haddr, BSWAP(val));
 | 
			
		||||
    ATOMIC_MMU_CLEANUP;
 | 
			
		||||
    return BSWAP(ret);
 | 
			
		||||
}
 | 
			
		||||
@@ -288,10 +178,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \
 | 
			
		||||
{                                                                   \
 | 
			
		||||
    ATOMIC_MMU_DECLS;                                               \
 | 
			
		||||
    DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \
 | 
			
		||||
    DATA_TYPE ret;                                                  \
 | 
			
		||||
                                                                    \
 | 
			
		||||
    ATOMIC_TRACE_RMW;                                               \
 | 
			
		||||
    ret = atomic_##X(haddr, BSWAP(val));                            \
 | 
			
		||||
    DATA_TYPE ret = atomic_##X(haddr, BSWAP(val));                  \
 | 
			
		||||
    ATOMIC_MMU_CLEANUP;                                             \
 | 
			
		||||
    return BSWAP(ret);                                              \
 | 
			
		||||
}
 | 
			
		||||
@@ -305,64 +192,54 @@ GEN_ATOMIC_HELPER(xor_fetch)
 | 
			
		||||
 | 
			
		||||
#undef GEN_ATOMIC_HELPER
 | 
			
		||||
 | 
			
		||||
/* These helpers are, as a whole, full barriers.  Within the helper,
 | 
			
		||||
 * the leading barrier is explicit and the trailing barrier is within
 | 
			
		||||
 * cmpxchg primitive.
 | 
			
		||||
 *
 | 
			
		||||
 * Trace this load + RMW loop as a single RMW op. This way, regardless
 | 
			
		||||
 * of CF_PARALLEL's value, we'll trace just a read and a write.
 | 
			
		||||
 */
 | 
			
		||||
#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET)                \
 | 
			
		||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \
 | 
			
		||||
                        ABI_TYPE xval EXTRA_ARGS)                   \
 | 
			
		||||
{                                                                   \
 | 
			
		||||
    ATOMIC_MMU_DECLS;                                               \
 | 
			
		||||
    XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                          \
 | 
			
		||||
    XDATA_TYPE ldo, ldn, old, new, val = xval;                      \
 | 
			
		||||
                                                                    \
 | 
			
		||||
    ATOMIC_TRACE_RMW;                                               \
 | 
			
		||||
    smp_mb();                                                       \
 | 
			
		||||
    ldn = atomic_read__nocheck(haddr);                              \
 | 
			
		||||
    do {                                                            \
 | 
			
		||||
        ldo = ldn; old = BSWAP(ldo); new = FN(old, val);            \
 | 
			
		||||
        ldn = atomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new));      \
 | 
			
		||||
    } while (ldo != ldn);                                           \
 | 
			
		||||
    ATOMIC_MMU_CLEANUP;                                             \
 | 
			
		||||
    return RET;                                                     \
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old)
 | 
			
		||||
GEN_ATOMIC_HELPER_FN(fetch_umin, MIN,  DATA_TYPE, old)
 | 
			
		||||
GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old)
 | 
			
		||||
GEN_ATOMIC_HELPER_FN(fetch_umax, MAX,  DATA_TYPE, old)
 | 
			
		||||
 | 
			
		||||
GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new)
 | 
			
		||||
GEN_ATOMIC_HELPER_FN(umin_fetch, MIN,  DATA_TYPE, new)
 | 
			
		||||
GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
 | 
			
		||||
GEN_ATOMIC_HELPER_FN(umax_fetch, MAX,  DATA_TYPE, new)
 | 
			
		||||
 | 
			
		||||
/* Note that for addition, we need to use a separate cmpxchg loop instead
 | 
			
		||||
   of bswaps for the reverse-host-endian helpers.  */
 | 
			
		||||
#define ADD(X, Y)   (X + Y)
 | 
			
		||||
GEN_ATOMIC_HELPER_FN(fetch_add, ADD, DATA_TYPE, old)
 | 
			
		||||
GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
 | 
			
		||||
#undef ADD
 | 
			
		||||
ABI_TYPE ATOMIC_NAME(fetch_add)(CPUArchState *env, target_ulong addr,
 | 
			
		||||
                         ABI_TYPE val EXTRA_ARGS)
 | 
			
		||||
{
 | 
			
		||||
    ATOMIC_MMU_DECLS;
 | 
			
		||||
    DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
 | 
			
		||||
    DATA_TYPE ldo, ldn, ret, sto;
 | 
			
		||||
 | 
			
		||||
#undef GEN_ATOMIC_HELPER_FN
 | 
			
		||||
    ldo = atomic_read__nocheck(haddr);
 | 
			
		||||
    while (1) {
 | 
			
		||||
        ret = BSWAP(ldo);
 | 
			
		||||
        sto = BSWAP(ret + val);
 | 
			
		||||
        ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto);
 | 
			
		||||
        if (ldn == ldo) {
 | 
			
		||||
            ATOMIC_MMU_CLEANUP;
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
        ldo = ldn;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ABI_TYPE ATOMIC_NAME(add_fetch)(CPUArchState *env, target_ulong addr,
 | 
			
		||||
                         ABI_TYPE val EXTRA_ARGS)
 | 
			
		||||
{
 | 
			
		||||
    ATOMIC_MMU_DECLS;
 | 
			
		||||
    DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
 | 
			
		||||
    DATA_TYPE ldo, ldn, ret, sto;
 | 
			
		||||
 | 
			
		||||
    ldo = atomic_read__nocheck(haddr);
 | 
			
		||||
    while (1) {
 | 
			
		||||
        ret = BSWAP(ldo) + val;
 | 
			
		||||
        sto = BSWAP(ret);
 | 
			
		||||
        ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto);
 | 
			
		||||
        if (ldn == ldo) {
 | 
			
		||||
            ATOMIC_MMU_CLEANUP;
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
        ldo = ldn;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#endif /* DATA_SIZE >= 16 */
 | 
			
		||||
 | 
			
		||||
#undef END
 | 
			
		||||
#undef MEND
 | 
			
		||||
#endif /* DATA_SIZE > 1 */
 | 
			
		||||
 | 
			
		||||
#undef ATOMIC_TRACE_ST
 | 
			
		||||
#undef ATOMIC_TRACE_LD
 | 
			
		||||
#undef ATOMIC_TRACE_RMW
 | 
			
		||||
 | 
			
		||||
#undef BSWAP
 | 
			
		||||
#undef ABI_TYPE
 | 
			
		||||
#undef DATA_TYPE
 | 
			
		||||
#undef SDATA_TYPE
 | 
			
		||||
#undef SUFFIX
 | 
			
		||||
#undef DATA_SIZE
 | 
			
		||||
#undef SHIFT
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
@@ -27,8 +27,10 @@ bool tcg_allowed;
 | 
			
		||||
/* exit the current TB, but without causing any exception to be raised */
 | 
			
		||||
void cpu_loop_exit_noexc(CPUState *cpu)
 | 
			
		||||
{
 | 
			
		||||
    /* XXX: restore cpu registers saved in host registers */
 | 
			
		||||
 | 
			
		||||
    cpu->exception_index = -1;
 | 
			
		||||
    cpu_loop_exit(cpu);
 | 
			
		||||
    siglongjmp(cpu->jmp_env, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if defined(CONFIG_SOFTMMU)
 | 
			
		||||
@@ -63,17 +65,15 @@ void cpu_reloading_memory_map(void)
 | 
			
		||||
 | 
			
		||||
void cpu_loop_exit(CPUState *cpu)
 | 
			
		||||
{
 | 
			
		||||
    /* Undo the setting in cpu_tb_exec.  */
 | 
			
		||||
    cpu->can_do_io = 1;
 | 
			
		||||
    siglongjmp(cpu->jmp_env, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc)
 | 
			
		||||
{
 | 
			
		||||
    if (pc) {
 | 
			
		||||
        cpu_restore_state(cpu, pc, true);
 | 
			
		||||
        cpu_restore_state(cpu, pc);
 | 
			
		||||
    }
 | 
			
		||||
    cpu_loop_exit(cpu);
 | 
			
		||||
    siglongjmp(cpu->jmp_env, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc)
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
@@ -25,6 +25,7 @@
 | 
			
		||||
#include "qemu/atomic.h"
 | 
			
		||||
#include "sysemu/qtest.h"
 | 
			
		||||
#include "qemu/timer.h"
 | 
			
		||||
#include "exec/address-spaces.h"
 | 
			
		||||
#include "qemu/rcu.h"
 | 
			
		||||
#include "exec/tb-hash.h"
 | 
			
		||||
#include "exec/tb-lookup.h"
 | 
			
		||||
@@ -155,14 +156,11 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb)
 | 
			
		||||
    if (qemu_loglevel_mask(CPU_LOG_TB_CPU)
 | 
			
		||||
        && qemu_log_in_addr_range(itb->pc)) {
 | 
			
		||||
        qemu_log_lock();
 | 
			
		||||
        int flags = 0;
 | 
			
		||||
        if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) {
 | 
			
		||||
            flags |= CPU_DUMP_FPU;
 | 
			
		||||
        }
 | 
			
		||||
#if defined(TARGET_I386)
 | 
			
		||||
        flags |= CPU_DUMP_CCOP;
 | 
			
		||||
        log_cpu_state(cpu, CPU_DUMP_CCOP);
 | 
			
		||||
#else
 | 
			
		||||
        log_cpu_state(cpu, 0);
 | 
			
		||||
#endif
 | 
			
		||||
        log_cpu_state(cpu, flags);
 | 
			
		||||
        qemu_log_unlock();
 | 
			
		||||
    }
 | 
			
		||||
#endif /* DEBUG_DISAS */
 | 
			
		||||
@@ -212,20 +210,20 @@ static void cpu_exec_nocache(CPUState *cpu, int max_cycles,
 | 
			
		||||
       We only end up here when an existing TB is too long.  */
 | 
			
		||||
    cflags |= MIN(max_cycles, CF_COUNT_MASK);
 | 
			
		||||
 | 
			
		||||
    mmap_lock();
 | 
			
		||||
    tb_lock();
 | 
			
		||||
    tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base,
 | 
			
		||||
                     orig_tb->flags, cflags);
 | 
			
		||||
    tb->orig_tb = orig_tb;
 | 
			
		||||
    mmap_unlock();
 | 
			
		||||
    tb_unlock();
 | 
			
		||||
 | 
			
		||||
    /* execute the generated code */
 | 
			
		||||
    trace_exec_tb_nocache(tb, tb->pc);
 | 
			
		||||
    cpu_tb_exec(cpu, tb);
 | 
			
		||||
 | 
			
		||||
    mmap_lock();
 | 
			
		||||
    tb_lock();
 | 
			
		||||
    tb_phys_invalidate(tb, -1);
 | 
			
		||||
    mmap_unlock();
 | 
			
		||||
    tcg_tb_remove(tb);
 | 
			
		||||
    tb_remove(tb);
 | 
			
		||||
    tb_unlock();
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -244,7 +242,12 @@ void cpu_exec_step_atomic(CPUState *cpu)
 | 
			
		||||
        tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
 | 
			
		||||
        if (tb == NULL) {
 | 
			
		||||
            mmap_lock();
 | 
			
		||||
            tb_lock();
 | 
			
		||||
            tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask);
 | 
			
		||||
            if (likely(tb == NULL)) {
 | 
			
		||||
                tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
 | 
			
		||||
            }
 | 
			
		||||
            tb_unlock();
 | 
			
		||||
            mmap_unlock();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -259,14 +262,15 @@ void cpu_exec_step_atomic(CPUState *cpu)
 | 
			
		||||
        cpu_tb_exec(cpu, tb);
 | 
			
		||||
        cc->cpu_exec_exit(cpu);
 | 
			
		||||
    } else {
 | 
			
		||||
        /*
 | 
			
		||||
        /* We may have exited due to another problem here, so we need
 | 
			
		||||
         * to reset any tb_locks we may have taken but didn't release.
 | 
			
		||||
         * The mmap_lock is dropped by tb_gen_code if it runs out of
 | 
			
		||||
         * memory.
 | 
			
		||||
         */
 | 
			
		||||
#ifndef CONFIG_SOFTMMU
 | 
			
		||||
        tcg_debug_assert(!have_mmap_lock());
 | 
			
		||||
#endif
 | 
			
		||||
        assert_no_pages_locked();
 | 
			
		||||
        tb_lock_reset();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (in_exclusive_region) {
 | 
			
		||||
@@ -289,7 +293,7 @@ struct tb_desc {
 | 
			
		||||
    uint32_t trace_vcpu_dstate;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static bool tb_lookup_cmp(const void *p, const void *d)
 | 
			
		||||
static bool tb_cmp(const void *p, const void *d)
 | 
			
		||||
{
 | 
			
		||||
    const TranslationBlock *tb = p;
 | 
			
		||||
    const struct tb_desc *desc = d;
 | 
			
		||||
@@ -325,9 +329,6 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
 | 
			
		||||
    struct tb_desc desc;
 | 
			
		||||
    uint32_t h;
 | 
			
		||||
 | 
			
		||||
    cf_mask &= ~CF_CLUSTER_MASK;
 | 
			
		||||
    cf_mask |= cpu->cluster_index << CF_CLUSTER_SHIFT;
 | 
			
		||||
 | 
			
		||||
    desc.env = (CPUArchState *)cpu->env_ptr;
 | 
			
		||||
    desc.cs_base = cs_base;
 | 
			
		||||
    desc.flags = flags;
 | 
			
		||||
@@ -335,12 +336,9 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
 | 
			
		||||
    desc.trace_vcpu_dstate = *cpu->trace_dstate;
 | 
			
		||||
    desc.pc = pc;
 | 
			
		||||
    phys_pc = get_page_addr_code(desc.env, pc);
 | 
			
		||||
    if (phys_pc == -1) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    desc.phys_page1 = phys_pc & TARGET_PAGE_MASK;
 | 
			
		||||
    h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate);
 | 
			
		||||
    return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp);
 | 
			
		||||
    return qht_lookup(&tb_ctx.htable, tb_cmp, &desc, h);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr)
 | 
			
		||||
@@ -354,43 +352,28 @@ void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called with tb_lock held.  */
 | 
			
		||||
static inline void tb_add_jump(TranslationBlock *tb, int n,
 | 
			
		||||
                               TranslationBlock *tb_next)
 | 
			
		||||
{
 | 
			
		||||
    uintptr_t old;
 | 
			
		||||
 | 
			
		||||
    assert(n < ARRAY_SIZE(tb->jmp_list_next));
 | 
			
		||||
    qemu_spin_lock(&tb_next->jmp_lock);
 | 
			
		||||
 | 
			
		||||
    /* make sure the destination TB is valid */
 | 
			
		||||
    if (tb_next->cflags & CF_INVALID) {
 | 
			
		||||
        goto out_unlock_next;
 | 
			
		||||
    if (tb->jmp_list_next[n]) {
 | 
			
		||||
        /* Another thread has already done this while we were
 | 
			
		||||
         * outside of the lock; nothing to do in this case */
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    /* Atomically claim the jump destination slot only if it was NULL */
 | 
			
		||||
    old = atomic_cmpxchg(&tb->jmp_dest[n], (uintptr_t)NULL, (uintptr_t)tb_next);
 | 
			
		||||
    if (old) {
 | 
			
		||||
        goto out_unlock_next;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* patch the native jump address */
 | 
			
		||||
    tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr);
 | 
			
		||||
 | 
			
		||||
    /* add in TB jmp list */
 | 
			
		||||
    tb->jmp_list_next[n] = tb_next->jmp_list_head;
 | 
			
		||||
    tb_next->jmp_list_head = (uintptr_t)tb | n;
 | 
			
		||||
 | 
			
		||||
    qemu_spin_unlock(&tb_next->jmp_lock);
 | 
			
		||||
 | 
			
		||||
    qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc,
 | 
			
		||||
                           "Linking TBs %p [" TARGET_FMT_lx
 | 
			
		||||
                           "] index %d -> %p [" TARGET_FMT_lx "]\n",
 | 
			
		||||
                           tb->tc.ptr, tb->pc, n,
 | 
			
		||||
                           tb_next->tc.ptr, tb_next->pc);
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
 out_unlock_next:
 | 
			
		||||
    qemu_spin_unlock(&tb_next->jmp_lock);
 | 
			
		||||
    return;
 | 
			
		||||
    /* patch the native jump address */
 | 
			
		||||
    tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr);
 | 
			
		||||
 | 
			
		||||
    /* add in TB jmp circular list */
 | 
			
		||||
    tb->jmp_list_next[n] = tb_next->jmp_list_first;
 | 
			
		||||
    tb_next->jmp_list_first = (uintptr_t)tb | n;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline TranslationBlock *tb_find(CPUState *cpu,
 | 
			
		||||
@@ -400,11 +383,27 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
 | 
			
		||||
    TranslationBlock *tb;
 | 
			
		||||
    target_ulong cs_base, pc;
 | 
			
		||||
    uint32_t flags;
 | 
			
		||||
    bool acquired_tb_lock = false;
 | 
			
		||||
 | 
			
		||||
    tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
 | 
			
		||||
    if (tb == NULL) {
 | 
			
		||||
        /* mmap_lock is needed by tb_gen_code, and mmap_lock must be
 | 
			
		||||
         * taken outside tb_lock. As system emulation is currently
 | 
			
		||||
         * single threaded the locks are NOPs.
 | 
			
		||||
         */
 | 
			
		||||
        mmap_lock();
 | 
			
		||||
        tb_lock();
 | 
			
		||||
        acquired_tb_lock = true;
 | 
			
		||||
 | 
			
		||||
        /* There's a chance that our desired tb has been translated while
 | 
			
		||||
         * taking the locks so we check again inside the lock.
 | 
			
		||||
         */
 | 
			
		||||
        tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask);
 | 
			
		||||
        if (likely(tb == NULL)) {
 | 
			
		||||
            /* if no translated code available, then translate it now */
 | 
			
		||||
            tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        mmap_unlock();
 | 
			
		||||
        /* We add the TB in the virtual pc hash table for the fast lookup */
 | 
			
		||||
        atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb);
 | 
			
		||||
@@ -419,9 +418,18 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    /* See if we can patch the calling TB. */
 | 
			
		||||
    if (last_tb) {
 | 
			
		||||
    if (last_tb && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) {
 | 
			
		||||
        if (!acquired_tb_lock) {
 | 
			
		||||
            tb_lock();
 | 
			
		||||
            acquired_tb_lock = true;
 | 
			
		||||
        }
 | 
			
		||||
        if (!(tb->cflags & CF_INVALID)) {
 | 
			
		||||
            tb_add_jump(last_tb, tb_exit, tb);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (acquired_tb_lock) {
 | 
			
		||||
        tb_unlock();
 | 
			
		||||
    }
 | 
			
		||||
    return tb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -577,7 +585,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
 | 
			
		||||
        else {
 | 
			
		||||
            if (cc->cpu_exec_interrupt(cpu, interrupt_request)) {
 | 
			
		||||
                replay_interrupt();
 | 
			
		||||
                cpu->exception_index = -1;
 | 
			
		||||
                *last_tb = NULL;
 | 
			
		||||
            }
 | 
			
		||||
            /* The target hook may have updated the 'cpu->interrupt_request';
 | 
			
		||||
@@ -599,9 +606,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
 | 
			
		||||
    if (unlikely(atomic_read(&cpu->exit_request)
 | 
			
		||||
        || (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) {
 | 
			
		||||
        atomic_set(&cpu->exit_request, 0);
 | 
			
		||||
        if (cpu->exception_index == -1) {
 | 
			
		||||
        cpu->exception_index = EXCP_INTERRUPT;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -696,9 +701,8 @@ int cpu_exec(CPUState *cpu)
 | 
			
		||||
        g_assert(cpu == current_cpu);
 | 
			
		||||
        g_assert(cc == CPU_GET_CLASS(cpu));
 | 
			
		||||
#endif /* buggy compiler */
 | 
			
		||||
#ifndef CONFIG_SOFTMMU
 | 
			
		||||
        tcg_debug_assert(!have_mmap_lock());
 | 
			
		||||
#endif
 | 
			
		||||
        cpu->can_do_io = 1;
 | 
			
		||||
        tb_lock_reset();
 | 
			
		||||
        if (qemu_mutex_iothread_locked()) {
 | 
			
		||||
            qemu_mutex_unlock_iothread();
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -11,7 +11,7 @@
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
@@ -98,23 +98,19 @@
 | 
			
		||||
static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env,
 | 
			
		||||
                                              size_t mmu_idx, size_t index,
 | 
			
		||||
                                              target_ulong addr,
 | 
			
		||||
                                              uintptr_t retaddr,
 | 
			
		||||
                                              bool recheck,
 | 
			
		||||
                                              MMUAccessType access_type)
 | 
			
		||||
                                              uintptr_t retaddr)
 | 
			
		||||
{
 | 
			
		||||
    CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
 | 
			
		||||
    return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, recheck,
 | 
			
		||||
                    access_type, DATA_SIZE);
 | 
			
		||||
    return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, DATA_SIZE);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
 | 
			
		||||
                            TCGMemOpIdx oi, uintptr_t retaddr)
 | 
			
		||||
{
 | 
			
		||||
    uintptr_t mmu_idx = get_mmuidx(oi);
 | 
			
		||||
    uintptr_t index = tlb_index(env, mmu_idx, addr);
 | 
			
		||||
    CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
 | 
			
		||||
    target_ulong tlb_addr = entry->ADDR_READ;
 | 
			
		||||
    unsigned mmu_idx = get_mmuidx(oi);
 | 
			
		||||
    int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
 | 
			
		||||
    target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
 | 
			
		||||
    unsigned a_bits = get_alignment_bits(get_memop(oi));
 | 
			
		||||
    uintptr_t haddr;
 | 
			
		||||
    DATA_TYPE res;
 | 
			
		||||
@@ -125,12 +121,13 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If the TLB entry is for a different page, reload and try again.  */
 | 
			
		||||
    if (!tlb_hit(tlb_addr, addr)) {
 | 
			
		||||
    if ((addr & TARGET_PAGE_MASK)
 | 
			
		||||
         != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
 | 
			
		||||
        if (!VICTIM_TLB_HIT(ADDR_READ, addr)) {
 | 
			
		||||
            tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
 | 
			
		||||
                     mmu_idx, retaddr);
 | 
			
		||||
        }
 | 
			
		||||
        tlb_addr = entry->ADDR_READ;
 | 
			
		||||
        tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Handle an IO access.  */
 | 
			
		||||
@@ -141,9 +138,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
 | 
			
		||||
 | 
			
		||||
        /* ??? Note that the io helpers always read data in the target
 | 
			
		||||
           byte ordering.  We should push the LE/BE request down into io.  */
 | 
			
		||||
        res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr,
 | 
			
		||||
                                    tlb_addr & TLB_RECHECK,
 | 
			
		||||
                                    READ_ACCESS_TYPE);
 | 
			
		||||
        res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr);
 | 
			
		||||
        res = TGT_LE(res);
 | 
			
		||||
        return res;
 | 
			
		||||
    }
 | 
			
		||||
@@ -167,7 +162,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
 | 
			
		||||
        return res;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    haddr = addr + entry->addend;
 | 
			
		||||
    haddr = addr + env->tlb_table[mmu_idx][index].addend;
 | 
			
		||||
#if DATA_SIZE == 1
 | 
			
		||||
    res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr);
 | 
			
		||||
#else
 | 
			
		||||
@@ -180,10 +175,9 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
 | 
			
		||||
WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
 | 
			
		||||
                            TCGMemOpIdx oi, uintptr_t retaddr)
 | 
			
		||||
{
 | 
			
		||||
    uintptr_t mmu_idx = get_mmuidx(oi);
 | 
			
		||||
    uintptr_t index = tlb_index(env, mmu_idx, addr);
 | 
			
		||||
    CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
 | 
			
		||||
    target_ulong tlb_addr = entry->ADDR_READ;
 | 
			
		||||
    unsigned mmu_idx = get_mmuidx(oi);
 | 
			
		||||
    int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
 | 
			
		||||
    target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
 | 
			
		||||
    unsigned a_bits = get_alignment_bits(get_memop(oi));
 | 
			
		||||
    uintptr_t haddr;
 | 
			
		||||
    DATA_TYPE res;
 | 
			
		||||
@@ -194,12 +188,13 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If the TLB entry is for a different page, reload and try again.  */
 | 
			
		||||
    if (!tlb_hit(tlb_addr, addr)) {
 | 
			
		||||
    if ((addr & TARGET_PAGE_MASK)
 | 
			
		||||
         != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
 | 
			
		||||
        if (!VICTIM_TLB_HIT(ADDR_READ, addr)) {
 | 
			
		||||
            tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
 | 
			
		||||
                     mmu_idx, retaddr);
 | 
			
		||||
        }
 | 
			
		||||
        tlb_addr = entry->ADDR_READ;
 | 
			
		||||
        tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Handle an IO access.  */
 | 
			
		||||
@@ -210,9 +205,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
 | 
			
		||||
 | 
			
		||||
        /* ??? Note that the io helpers always read data in the target
 | 
			
		||||
           byte ordering.  We should push the LE/BE request down into io.  */
 | 
			
		||||
        res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr,
 | 
			
		||||
                                    tlb_addr & TLB_RECHECK,
 | 
			
		||||
                                    READ_ACCESS_TYPE);
 | 
			
		||||
        res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr);
 | 
			
		||||
        res = TGT_BE(res);
 | 
			
		||||
        return res;
 | 
			
		||||
    }
 | 
			
		||||
@@ -236,7 +229,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
 | 
			
		||||
        return res;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    haddr = addr + entry->addend;
 | 
			
		||||
    haddr = addr + env->tlb_table[mmu_idx][index].addend;
 | 
			
		||||
    res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr);
 | 
			
		||||
    return res;
 | 
			
		||||
}
 | 
			
		||||
@@ -266,21 +259,18 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env,
 | 
			
		||||
                                          size_t mmu_idx, size_t index,
 | 
			
		||||
                                          DATA_TYPE val,
 | 
			
		||||
                                          target_ulong addr,
 | 
			
		||||
                                          uintptr_t retaddr,
 | 
			
		||||
                                          bool recheck)
 | 
			
		||||
                                          uintptr_t retaddr)
 | 
			
		||||
{
 | 
			
		||||
    CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
 | 
			
		||||
    return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr,
 | 
			
		||||
                     recheck, DATA_SIZE);
 | 
			
		||||
    return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr, DATA_SIZE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
 | 
			
		||||
                       TCGMemOpIdx oi, uintptr_t retaddr)
 | 
			
		||||
{
 | 
			
		||||
    uintptr_t mmu_idx = get_mmuidx(oi);
 | 
			
		||||
    uintptr_t index = tlb_index(env, mmu_idx, addr);
 | 
			
		||||
    CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
 | 
			
		||||
    target_ulong tlb_addr = tlb_addr_write(entry);
 | 
			
		||||
    unsigned mmu_idx = get_mmuidx(oi);
 | 
			
		||||
    int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
 | 
			
		||||
    target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
 | 
			
		||||
    unsigned a_bits = get_alignment_bits(get_memop(oi));
 | 
			
		||||
    uintptr_t haddr;
 | 
			
		||||
 | 
			
		||||
@@ -290,12 +280,13 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If the TLB entry is for a different page, reload and try again.  */
 | 
			
		||||
    if (!tlb_hit(tlb_addr, addr)) {
 | 
			
		||||
    if ((addr & TARGET_PAGE_MASK)
 | 
			
		||||
        != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
 | 
			
		||||
        if (!VICTIM_TLB_HIT(addr_write, addr)) {
 | 
			
		||||
            tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
 | 
			
		||||
                     mmu_idx, retaddr);
 | 
			
		||||
        }
 | 
			
		||||
        tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK;
 | 
			
		||||
        tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Handle an IO access.  */
 | 
			
		||||
@@ -307,8 +298,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
 | 
			
		||||
        /* ??? Note that the io helpers always read data in the target
 | 
			
		||||
           byte ordering.  We should push the LE/BE request down into io.  */
 | 
			
		||||
        val = TGT_LE(val);
 | 
			
		||||
        glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr,
 | 
			
		||||
                               retaddr, tlb_addr & TLB_RECHECK);
 | 
			
		||||
        glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -316,16 +306,16 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
 | 
			
		||||
    if (DATA_SIZE > 1
 | 
			
		||||
        && unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
 | 
			
		||||
                     >= TARGET_PAGE_SIZE)) {
 | 
			
		||||
        int i;
 | 
			
		||||
        target_ulong page2;
 | 
			
		||||
        CPUTLBEntry *entry2;
 | 
			
		||||
        int i, index2;
 | 
			
		||||
        target_ulong page2, tlb_addr2;
 | 
			
		||||
    do_unaligned_access:
 | 
			
		||||
        /* Ensure the second page is in the TLB.  Note that the first page
 | 
			
		||||
           is already guaranteed to be filled, and that the second page
 | 
			
		||||
           cannot evict the first.  */
 | 
			
		||||
        page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
 | 
			
		||||
        entry2 = tlb_entry(env, mmu_idx, page2);
 | 
			
		||||
        if (!tlb_hit_page(tlb_addr_write(entry2), page2)
 | 
			
		||||
        index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
 | 
			
		||||
        tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write;
 | 
			
		||||
        if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK))
 | 
			
		||||
            && !VICTIM_TLB_HIT(addr_write, page2)) {
 | 
			
		||||
            tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
 | 
			
		||||
                     mmu_idx, retaddr);
 | 
			
		||||
@@ -343,7 +333,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    haddr = addr + entry->addend;
 | 
			
		||||
    haddr = addr + env->tlb_table[mmu_idx][index].addend;
 | 
			
		||||
#if DATA_SIZE == 1
 | 
			
		||||
    glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val);
 | 
			
		||||
#else
 | 
			
		||||
@@ -355,10 +345,9 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
 | 
			
		||||
void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
 | 
			
		||||
                       TCGMemOpIdx oi, uintptr_t retaddr)
 | 
			
		||||
{
 | 
			
		||||
    uintptr_t mmu_idx = get_mmuidx(oi);
 | 
			
		||||
    uintptr_t index = tlb_index(env, mmu_idx, addr);
 | 
			
		||||
    CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
 | 
			
		||||
    target_ulong tlb_addr = tlb_addr_write(entry);
 | 
			
		||||
    unsigned mmu_idx = get_mmuidx(oi);
 | 
			
		||||
    int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
 | 
			
		||||
    target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
 | 
			
		||||
    unsigned a_bits = get_alignment_bits(get_memop(oi));
 | 
			
		||||
    uintptr_t haddr;
 | 
			
		||||
 | 
			
		||||
@@ -368,12 +357,13 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If the TLB entry is for a different page, reload and try again.  */
 | 
			
		||||
    if (!tlb_hit(tlb_addr, addr)) {
 | 
			
		||||
    if ((addr & TARGET_PAGE_MASK)
 | 
			
		||||
        != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
 | 
			
		||||
        if (!VICTIM_TLB_HIT(addr_write, addr)) {
 | 
			
		||||
            tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
 | 
			
		||||
                     mmu_idx, retaddr);
 | 
			
		||||
        }
 | 
			
		||||
        tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK;
 | 
			
		||||
        tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Handle an IO access.  */
 | 
			
		||||
@@ -385,8 +375,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
 | 
			
		||||
        /* ??? Note that the io helpers always read data in the target
 | 
			
		||||
           byte ordering.  We should push the LE/BE request down into io.  */
 | 
			
		||||
        val = TGT_BE(val);
 | 
			
		||||
        glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr,
 | 
			
		||||
                               tlb_addr & TLB_RECHECK);
 | 
			
		||||
        glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -394,16 +383,16 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
 | 
			
		||||
    if (DATA_SIZE > 1
 | 
			
		||||
        && unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
 | 
			
		||||
                     >= TARGET_PAGE_SIZE)) {
 | 
			
		||||
        int i;
 | 
			
		||||
        target_ulong page2;
 | 
			
		||||
        CPUTLBEntry *entry2;
 | 
			
		||||
        int i, index2;
 | 
			
		||||
        target_ulong page2, tlb_addr2;
 | 
			
		||||
    do_unaligned_access:
 | 
			
		||||
        /* Ensure the second page is in the TLB.  Note that the first page
 | 
			
		||||
           is already guaranteed to be filled, and that the second page
 | 
			
		||||
           cannot evict the first.  */
 | 
			
		||||
        page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
 | 
			
		||||
        entry2 = tlb_entry(env, mmu_idx, page2);
 | 
			
		||||
        if (!tlb_hit_page(tlb_addr_write(entry2), page2)
 | 
			
		||||
        index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
 | 
			
		||||
        tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write;
 | 
			
		||||
        if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK))
 | 
			
		||||
            && !VICTIM_TLB_HIT(addr_write, page2)) {
 | 
			
		||||
            tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
 | 
			
		||||
                     mmu_idx, retaddr);
 | 
			
		||||
@@ -421,7 +410,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    haddr = addr + entry->addend;
 | 
			
		||||
    haddr = addr + env->tlb_table[mmu_idx][index].addend;
 | 
			
		||||
    glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val);
 | 
			
		||||
}
 | 
			
		||||
#endif /* DATA_SIZE > 1 */
 | 
			
		||||
 
 | 
			
		||||
@@ -51,7 +51,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
 | 
			
		||||
    if (!qemu_cpu_is_self(cpu)) {
 | 
			
		||||
        qemu_cpu_kick(cpu);
 | 
			
		||||
    } else {
 | 
			
		||||
        atomic_set(&cpu->icount_decr.u16.high, -1);
 | 
			
		||||
        cpu->icount_decr.u16.high = -1;
 | 
			
		||||
        if (use_icount &&
 | 
			
		||||
            !cpu->can_do_io
 | 
			
		||||
            && (mask & ~old_mask) != 0) {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
@@ -512,39 +512,6 @@ void HELPER(gvec_orc)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(gvec_nand)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
{
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);
 | 
			
		||||
    intptr_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(vec64)) {
 | 
			
		||||
        *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) & *(vec64 *)(b + i));
 | 
			
		||||
    }
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(gvec_nor)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
{
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);
 | 
			
		||||
    intptr_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(vec64)) {
 | 
			
		||||
        *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) | *(vec64 *)(b + i));
 | 
			
		||||
    }
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(gvec_eqv)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
{
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);
 | 
			
		||||
    intptr_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(vec64)) {
 | 
			
		||||
        *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) ^ *(vec64 *)(b + i));
 | 
			
		||||
    }
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc)
 | 
			
		||||
{
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);
 | 
			
		||||
@@ -738,7 +705,7 @@ void HELPER(NAME)(void *d, void *a, void *b, uint32_t desc)                \
 | 
			
		||||
{                                                                          \
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);                                     \
 | 
			
		||||
    intptr_t i;                                                            \
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(TYPE)) {                            \
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(vec64)) {                           \
 | 
			
		||||
        *(TYPE *)(d + i) = DO_CMP0(*(TYPE *)(a + i) OP *(TYPE *)(b + i));  \
 | 
			
		||||
    }                                                                      \
 | 
			
		||||
    clear_high(d, oprsz, desc);                                            \
 | 
			
		||||
@@ -1028,227 +995,3 @@ void HELPER(gvec_ussub64)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
    }
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(gvec_smin8)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
{
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);
 | 
			
		||||
    intptr_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(int8_t)) {
 | 
			
		||||
        int8_t aa = *(int8_t *)(a + i);
 | 
			
		||||
        int8_t bb = *(int8_t *)(b + i);
 | 
			
		||||
        int8_t dd = aa < bb ? aa : bb;
 | 
			
		||||
        *(int8_t *)(d + i) = dd;
 | 
			
		||||
    }
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(gvec_smin16)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
{
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);
 | 
			
		||||
    intptr_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(int16_t)) {
 | 
			
		||||
        int16_t aa = *(int16_t *)(a + i);
 | 
			
		||||
        int16_t bb = *(int16_t *)(b + i);
 | 
			
		||||
        int16_t dd = aa < bb ? aa : bb;
 | 
			
		||||
        *(int16_t *)(d + i) = dd;
 | 
			
		||||
    }
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(gvec_smin32)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
{
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);
 | 
			
		||||
    intptr_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(int32_t)) {
 | 
			
		||||
        int32_t aa = *(int32_t *)(a + i);
 | 
			
		||||
        int32_t bb = *(int32_t *)(b + i);
 | 
			
		||||
        int32_t dd = aa < bb ? aa : bb;
 | 
			
		||||
        *(int32_t *)(d + i) = dd;
 | 
			
		||||
    }
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(gvec_smin64)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
{
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);
 | 
			
		||||
    intptr_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(int64_t)) {
 | 
			
		||||
        int64_t aa = *(int64_t *)(a + i);
 | 
			
		||||
        int64_t bb = *(int64_t *)(b + i);
 | 
			
		||||
        int64_t dd = aa < bb ? aa : bb;
 | 
			
		||||
        *(int64_t *)(d + i) = dd;
 | 
			
		||||
    }
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(gvec_smax8)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
{
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);
 | 
			
		||||
    intptr_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(int8_t)) {
 | 
			
		||||
        int8_t aa = *(int8_t *)(a + i);
 | 
			
		||||
        int8_t bb = *(int8_t *)(b + i);
 | 
			
		||||
        int8_t dd = aa > bb ? aa : bb;
 | 
			
		||||
        *(int8_t *)(d + i) = dd;
 | 
			
		||||
    }
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(gvec_smax16)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
{
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);
 | 
			
		||||
    intptr_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(int16_t)) {
 | 
			
		||||
        int16_t aa = *(int16_t *)(a + i);
 | 
			
		||||
        int16_t bb = *(int16_t *)(b + i);
 | 
			
		||||
        int16_t dd = aa > bb ? aa : bb;
 | 
			
		||||
        *(int16_t *)(d + i) = dd;
 | 
			
		||||
    }
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(gvec_smax32)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
{
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);
 | 
			
		||||
    intptr_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(int32_t)) {
 | 
			
		||||
        int32_t aa = *(int32_t *)(a + i);
 | 
			
		||||
        int32_t bb = *(int32_t *)(b + i);
 | 
			
		||||
        int32_t dd = aa > bb ? aa : bb;
 | 
			
		||||
        *(int32_t *)(d + i) = dd;
 | 
			
		||||
    }
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(gvec_smax64)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
{
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);
 | 
			
		||||
    intptr_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(int64_t)) {
 | 
			
		||||
        int64_t aa = *(int64_t *)(a + i);
 | 
			
		||||
        int64_t bb = *(int64_t *)(b + i);
 | 
			
		||||
        int64_t dd = aa > bb ? aa : bb;
 | 
			
		||||
        *(int64_t *)(d + i) = dd;
 | 
			
		||||
    }
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(gvec_umin8)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
{
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);
 | 
			
		||||
    intptr_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
 | 
			
		||||
        uint8_t aa = *(uint8_t *)(a + i);
 | 
			
		||||
        uint8_t bb = *(uint8_t *)(b + i);
 | 
			
		||||
        uint8_t dd = aa < bb ? aa : bb;
 | 
			
		||||
        *(uint8_t *)(d + i) = dd;
 | 
			
		||||
    }
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(gvec_umin16)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
{
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);
 | 
			
		||||
    intptr_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
 | 
			
		||||
        uint16_t aa = *(uint16_t *)(a + i);
 | 
			
		||||
        uint16_t bb = *(uint16_t *)(b + i);
 | 
			
		||||
        uint16_t dd = aa < bb ? aa : bb;
 | 
			
		||||
        *(uint16_t *)(d + i) = dd;
 | 
			
		||||
    }
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(gvec_umin32)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
{
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);
 | 
			
		||||
    intptr_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
 | 
			
		||||
        uint32_t aa = *(uint32_t *)(a + i);
 | 
			
		||||
        uint32_t bb = *(uint32_t *)(b + i);
 | 
			
		||||
        uint32_t dd = aa < bb ? aa : bb;
 | 
			
		||||
        *(uint32_t *)(d + i) = dd;
 | 
			
		||||
    }
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(gvec_umin64)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
{
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);
 | 
			
		||||
    intptr_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
 | 
			
		||||
        uint64_t aa = *(uint64_t *)(a + i);
 | 
			
		||||
        uint64_t bb = *(uint64_t *)(b + i);
 | 
			
		||||
        uint64_t dd = aa < bb ? aa : bb;
 | 
			
		||||
        *(uint64_t *)(d + i) = dd;
 | 
			
		||||
    }
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(gvec_umax8)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
{
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);
 | 
			
		||||
    intptr_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
 | 
			
		||||
        uint8_t aa = *(uint8_t *)(a + i);
 | 
			
		||||
        uint8_t bb = *(uint8_t *)(b + i);
 | 
			
		||||
        uint8_t dd = aa > bb ? aa : bb;
 | 
			
		||||
        *(uint8_t *)(d + i) = dd;
 | 
			
		||||
    }
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(gvec_umax16)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
{
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);
 | 
			
		||||
    intptr_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
 | 
			
		||||
        uint16_t aa = *(uint16_t *)(a + i);
 | 
			
		||||
        uint16_t bb = *(uint16_t *)(b + i);
 | 
			
		||||
        uint16_t dd = aa > bb ? aa : bb;
 | 
			
		||||
        *(uint16_t *)(d + i) = dd;
 | 
			
		||||
    }
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(gvec_umax32)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
{
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);
 | 
			
		||||
    intptr_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
 | 
			
		||||
        uint32_t aa = *(uint32_t *)(a + i);
 | 
			
		||||
        uint32_t bb = *(uint32_t *)(b + i);
 | 
			
		||||
        uint32_t dd = aa > bb ? aa : bb;
 | 
			
		||||
        *(uint32_t *)(d + i) = dd;
 | 
			
		||||
    }
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(gvec_umax64)(void *d, void *a, void *b, uint32_t desc)
 | 
			
		||||
{
 | 
			
		||||
    intptr_t oprsz = simd_oprsz(desc);
 | 
			
		||||
    intptr_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
 | 
			
		||||
        uint64_t aa = *(uint64_t *)(a + i);
 | 
			
		||||
        uint64_t bb = *(uint64_t *)(b + i);
 | 
			
		||||
        uint64_t dd = aa > bb ? aa : bb;
 | 
			
		||||
        *(uint64_t *)(d + i) = dd;
 | 
			
		||||
    }
 | 
			
		||||
    clear_high(d, oprsz, desc);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -125,19 +125,11 @@ GEN_ATOMIC_HELPERS(fetch_add)
 | 
			
		||||
GEN_ATOMIC_HELPERS(fetch_and)
 | 
			
		||||
GEN_ATOMIC_HELPERS(fetch_or)
 | 
			
		||||
GEN_ATOMIC_HELPERS(fetch_xor)
 | 
			
		||||
GEN_ATOMIC_HELPERS(fetch_smin)
 | 
			
		||||
GEN_ATOMIC_HELPERS(fetch_umin)
 | 
			
		||||
GEN_ATOMIC_HELPERS(fetch_smax)
 | 
			
		||||
GEN_ATOMIC_HELPERS(fetch_umax)
 | 
			
		||||
 | 
			
		||||
GEN_ATOMIC_HELPERS(add_fetch)
 | 
			
		||||
GEN_ATOMIC_HELPERS(and_fetch)
 | 
			
		||||
GEN_ATOMIC_HELPERS(or_fetch)
 | 
			
		||||
GEN_ATOMIC_HELPERS(xor_fetch)
 | 
			
		||||
GEN_ATOMIC_HELPERS(smin_fetch)
 | 
			
		||||
GEN_ATOMIC_HELPERS(umin_fetch)
 | 
			
		||||
GEN_ATOMIC_HELPERS(smax_fetch)
 | 
			
		||||
GEN_ATOMIC_HELPERS(umax_fetch)
 | 
			
		||||
 | 
			
		||||
GEN_ATOMIC_HELPERS(xchg)
 | 
			
		||||
 | 
			
		||||
@@ -200,26 +192,6 @@ DEF_HELPER_FLAGS_4(gvec_ussub16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_ussub32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_ussub64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_smin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_smin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_smin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_smin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_smax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_smax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_smax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_smax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_umin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_umin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_umin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_umin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_umax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_umax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_umax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_umax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
 | 
			
		||||
DEF_HELPER_FLAGS_3(gvec_neg8, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_3(gvec_neg16, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_3(gvec_neg32, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
 | 
			
		||||
@@ -231,9 +203,6 @@ DEF_HELPER_FLAGS_4(gvec_or, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_xor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_andc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_orc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_nand, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_nor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_eqv, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 | 
			
		||||
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_ands, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
 | 
			
		||||
DEF_HELPER_FLAGS_4(gvec_xors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -6,7 +6,7 @@
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
@@ -23,13 +23,10 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* translate-all.c */
 | 
			
		||||
struct page_collection *page_collection_lock(tb_page_addr_t start,
 | 
			
		||||
                                             tb_page_addr_t end);
 | 
			
		||||
void page_collection_unlock(struct page_collection *set);
 | 
			
		||||
void tb_invalidate_phys_page_fast(struct page_collection *pages,
 | 
			
		||||
                                  tb_page_addr_t start, int len);
 | 
			
		||||
void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len);
 | 
			
		||||
void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
 | 
			
		||||
                                   int is_cpu_write_access);
 | 
			
		||||
void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end);
 | 
			
		||||
void tb_check_watchpoint(CPUState *cpu);
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_USER_ONLY
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@ void translator_loop_temp_check(DisasContextBase *db)
 | 
			
		||||
void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
 | 
			
		||||
                     CPUState *cpu, TranslationBlock *tb)
 | 
			
		||||
{
 | 
			
		||||
    int bp_insn = 0;
 | 
			
		||||
    int max_insns;
 | 
			
		||||
 | 
			
		||||
    /* Initialize DisasContext */
 | 
			
		||||
    db->tb = tb;
 | 
			
		||||
@@ -45,18 +45,18 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
 | 
			
		||||
    db->singlestep_enabled = cpu->singlestep_enabled;
 | 
			
		||||
 | 
			
		||||
    /* Instruction counting */
 | 
			
		||||
    db->max_insns = tb_cflags(db->tb) & CF_COUNT_MASK;
 | 
			
		||||
    if (db->max_insns == 0) {
 | 
			
		||||
        db->max_insns = CF_COUNT_MASK;
 | 
			
		||||
    max_insns = tb_cflags(db->tb) & CF_COUNT_MASK;
 | 
			
		||||
    if (max_insns == 0) {
 | 
			
		||||
        max_insns = CF_COUNT_MASK;
 | 
			
		||||
    }
 | 
			
		||||
    if (db->max_insns > TCG_MAX_INSNS) {
 | 
			
		||||
        db->max_insns = TCG_MAX_INSNS;
 | 
			
		||||
    if (max_insns > TCG_MAX_INSNS) {
 | 
			
		||||
        max_insns = TCG_MAX_INSNS;
 | 
			
		||||
    }
 | 
			
		||||
    if (db->singlestep_enabled || singlestep) {
 | 
			
		||||
        db->max_insns = 1;
 | 
			
		||||
        max_insns = 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ops->init_disas_context(db, cpu);
 | 
			
		||||
    max_insns = ops->init_disas_context(db, cpu, max_insns);
 | 
			
		||||
    tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
 | 
			
		||||
 | 
			
		||||
    /* Reset the temp count so that we can identify leaks */
 | 
			
		||||
@@ -73,13 +73,11 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
 | 
			
		||||
        tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
 | 
			
		||||
 | 
			
		||||
        /* Pass breakpoint hits to target for further processing */
 | 
			
		||||
        if (!db->singlestep_enabled
 | 
			
		||||
            && unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
 | 
			
		||||
        if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
 | 
			
		||||
            CPUBreakpoint *bp;
 | 
			
		||||
            QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
 | 
			
		||||
                if (bp->pc == db->pc_next) {
 | 
			
		||||
                    if (ops->breakpoint_check(db, cpu, bp)) {
 | 
			
		||||
                        bp_insn = 1;
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
@@ -97,8 +95,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
 | 
			
		||||
           update db->pc_next and db->is_jmp to indicate what should be
 | 
			
		||||
           done next -- either exiting this loop or locate the start of
 | 
			
		||||
           the next instruction.  */
 | 
			
		||||
        if (db->num_insns == db->max_insns
 | 
			
		||||
            && (tb_cflags(db->tb) & CF_LAST_IO)) {
 | 
			
		||||
        if (db->num_insns == max_insns && (tb_cflags(db->tb) & CF_LAST_IO)) {
 | 
			
		||||
            /* Accept I/O on the last instruction.  */
 | 
			
		||||
            gen_io_start();
 | 
			
		||||
            ops->translate_insn(db, cpu);
 | 
			
		||||
@@ -114,7 +111,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
 | 
			
		||||
 | 
			
		||||
        /* Stop translation if the output buffer is full,
 | 
			
		||||
           or we have executed all of the allowed instructions.  */
 | 
			
		||||
        if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
 | 
			
		||||
        if (tcg_op_buf_full() || db->num_insns >= max_insns) {
 | 
			
		||||
            db->is_jmp = DISAS_TOO_MANY;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
@@ -122,7 +119,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
 | 
			
		||||
 | 
			
		||||
    /* Emit code to exit the TB, as indicated by db->is_jmp.  */
 | 
			
		||||
    ops->tb_stop(db, cpu);
 | 
			
		||||
    gen_tb_end(db->tb, db->num_insns - bp_insn);
 | 
			
		||||
    gen_tb_end(db->tb, db->num_insns);
 | 
			
		||||
 | 
			
		||||
    /* The disas_log hook may use these values rather than recompute.  */
 | 
			
		||||
    db->tb->size = db->pc_next - db->pc_first;
 | 
			
		||||
 
 | 
			
		||||
@@ -2,9 +2,6 @@
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "qom/cpu.h"
 | 
			
		||||
#include "sysemu/replay.h"
 | 
			
		||||
#include "sysemu/sysemu.h"
 | 
			
		||||
 | 
			
		||||
bool enable_cpu_pm = false;
 | 
			
		||||
 | 
			
		||||
void cpu_resume(CPUState *cpu)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
@@ -25,7 +25,6 @@
 | 
			
		||||
#include "exec/cpu_ldst.h"
 | 
			
		||||
#include "translate-all.h"
 | 
			
		||||
#include "exec/helper-proto.h"
 | 
			
		||||
#include "qemu/atomic128.h"
 | 
			
		||||
 | 
			
		||||
#undef EAX
 | 
			
		||||
#undef ECX
 | 
			
		||||
@@ -169,7 +168,7 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Now we have a real cpu fault.  */
 | 
			
		||||
    cpu_restore_state(cpu, pc, true);
 | 
			
		||||
    cpu_restore_state(cpu, pc);
 | 
			
		||||
 | 
			
		||||
    sigprocmask(SIG_SETMASK, old_set, NULL);
 | 
			
		||||
    cpu_loop_exit(cpu);
 | 
			
		||||
@@ -479,66 +478,28 @@ int cpu_signal_handler(int host_signum, void *pinfo,
 | 
			
		||||
 | 
			
		||||
#elif defined(__aarch64__)
 | 
			
		||||
 | 
			
		||||
#ifndef ESR_MAGIC
 | 
			
		||||
/* Pre-3.16 kernel headers don't have these, so provide fallback definitions */
 | 
			
		||||
#define ESR_MAGIC 0x45535201
 | 
			
		||||
struct esr_context {
 | 
			
		||||
    struct _aarch64_ctx head;
 | 
			
		||||
    uint64_t esr;
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc)
 | 
			
		||||
{
 | 
			
		||||
    return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr)
 | 
			
		||||
{
 | 
			
		||||
    return (struct _aarch64_ctx *)((char *)hdr + hdr->size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
 | 
			
		||||
{
 | 
			
		||||
    siginfo_t *info = pinfo;
 | 
			
		||||
    ucontext_t *uc = puc;
 | 
			
		||||
    uintptr_t pc = uc->uc_mcontext.pc;
 | 
			
		||||
    bool is_write;
 | 
			
		||||
    struct _aarch64_ctx *hdr;
 | 
			
		||||
    struct esr_context const *esrctx = NULL;
 | 
			
		||||
 | 
			
		||||
    /* Find the esr_context, which has the WnR bit in it */
 | 
			
		||||
    for (hdr = first_ctx(uc); hdr->magic; hdr = next_ctx(hdr)) {
 | 
			
		||||
        if (hdr->magic == ESR_MAGIC) {
 | 
			
		||||
            esrctx = (struct esr_context const *)hdr;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (esrctx) {
 | 
			
		||||
        /* For data aborts ESR.EC is 0b10010x: then bit 6 is the WnR bit */
 | 
			
		||||
        uint64_t esr = esrctx->esr;
 | 
			
		||||
        is_write = extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1;
 | 
			
		||||
    } else {
 | 
			
		||||
        /*
 | 
			
		||||
         * Fall back to parsing instructions; will only be needed
 | 
			
		||||
         * for really ancient (pre-3.16) kernels.
 | 
			
		||||
         */
 | 
			
		||||
    uint32_t insn = *(uint32_t *)pc;
 | 
			
		||||
    bool is_write;
 | 
			
		||||
 | 
			
		||||
        is_write = ((insn & 0xbfff0000) == 0x0c000000   /* C3.3.1 */
 | 
			
		||||
    /* XXX: need kernel patch to get write flag faster.  */
 | 
			
		||||
    is_write = (   (insn & 0xbfff0000) == 0x0c000000   /* C3.3.1 */
 | 
			
		||||
                || (insn & 0xbfe00000) == 0x0c800000   /* C3.3.2 */
 | 
			
		||||
                || (insn & 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.  */
 | 
			
		||||
                /* 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 */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -609,81 +570,6 @@ int cpu_signal_handler(int host_signum, void *pinfo,
 | 
			
		||||
    return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#elif defined(__riscv)
 | 
			
		||||
 | 
			
		||||
int cpu_signal_handler(int host_signum, void *pinfo,
 | 
			
		||||
                       void *puc)
 | 
			
		||||
{
 | 
			
		||||
    siginfo_t *info = pinfo;
 | 
			
		||||
    ucontext_t *uc = puc;
 | 
			
		||||
    greg_t pc = uc->uc_mcontext.__gregs[REG_PC];
 | 
			
		||||
    uint32_t insn = *(uint32_t *)pc;
 | 
			
		||||
    int is_write = 0;
 | 
			
		||||
 | 
			
		||||
    /* Detect store by reading the instruction at the program
 | 
			
		||||
       counter. Note: we currently only generate 32-bit
 | 
			
		||||
       instructions so we thus only detect 32-bit stores */
 | 
			
		||||
    switch (((insn >> 0) & 0b11)) {
 | 
			
		||||
    case 3:
 | 
			
		||||
        switch (((insn >> 2) & 0b11111)) {
 | 
			
		||||
        case 8:
 | 
			
		||||
            switch (((insn >> 12) & 0b111)) {
 | 
			
		||||
            case 0: /* sb */
 | 
			
		||||
            case 1: /* sh */
 | 
			
		||||
            case 2: /* sw */
 | 
			
		||||
            case 3: /* sd */
 | 
			
		||||
            case 4: /* sq */
 | 
			
		||||
                is_write = 1;
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case 9:
 | 
			
		||||
            switch (((insn >> 12) & 0b111)) {
 | 
			
		||||
            case 2: /* fsw */
 | 
			
		||||
            case 3: /* fsd */
 | 
			
		||||
            case 4: /* fsq */
 | 
			
		||||
                is_write = 1;
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Check for compressed instructions */
 | 
			
		||||
    switch (((insn >> 13) & 0b111)) {
 | 
			
		||||
    case 7:
 | 
			
		||||
        switch (insn & 0b11) {
 | 
			
		||||
        case 0: /*c.sd */
 | 
			
		||||
        case 2: /* c.sdsp */
 | 
			
		||||
            is_write = 1;
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    case 6:
 | 
			
		||||
        switch (insn & 0b11) {
 | 
			
		||||
        case 0: /* c.sw */
 | 
			
		||||
        case 3: /* c.swsp */
 | 
			
		||||
            is_write = 1;
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
#error host CPU specific signal handler needed
 | 
			
		||||
@@ -729,7 +615,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
 | 
			
		||||
/* The following is only callable from other helpers, and matches up
 | 
			
		||||
   with the softmmu version.  */
 | 
			
		||||
 | 
			
		||||
#if HAVE_ATOMIC128 || HAVE_CMPXCHG128
 | 
			
		||||
#ifdef CONFIG_ATOMIC128
 | 
			
		||||
 | 
			
		||||
#undef EXTRA_ARGS
 | 
			
		||||
#undef ATOMIC_NAME
 | 
			
		||||
@@ -742,4 +628,4 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
 | 
			
		||||
 | 
			
		||||
#define DATA_SIZE 16
 | 
			
		||||
#include "atomic_template.h"
 | 
			
		||||
#endif
 | 
			
		||||
#endif /* CONFIG_ATOMIC128 */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								arch_init.c
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								arch_init.c
									
									
									
									
									
								
							@@ -29,7 +29,6 @@
 | 
			
		||||
#include "hw/pci/pci.h"
 | 
			
		||||
#include "hw/audio/soundhw.h"
 | 
			
		||||
#include "qapi/qapi-commands-misc.h"
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
#include "qemu/config-file.h"
 | 
			
		||||
#include "qemu/error-report.h"
 | 
			
		||||
#include "hw/acpi/acpi.h"
 | 
			
		||||
@@ -52,14 +51,14 @@ int graphic_depth = 32;
 | 
			
		||||
#define QEMU_ARCH QEMU_ARCH_ARM
 | 
			
		||||
#elif defined(TARGET_CRIS)
 | 
			
		||||
#define QEMU_ARCH QEMU_ARCH_CRIS
 | 
			
		||||
#elif defined(TARGET_HPPA)
 | 
			
		||||
#define QEMU_ARCH QEMU_ARCH_HPPA
 | 
			
		||||
#elif defined(TARGET_I386)
 | 
			
		||||
#define QEMU_ARCH QEMU_ARCH_I386
 | 
			
		||||
#elif defined(TARGET_LM32)
 | 
			
		||||
#define QEMU_ARCH QEMU_ARCH_LM32
 | 
			
		||||
#elif defined(TARGET_HPPA)
 | 
			
		||||
#define QEMU_ARCH QEMU_ARCH_HPPA
 | 
			
		||||
#elif defined(TARGET_M68K)
 | 
			
		||||
#define QEMU_ARCH QEMU_ARCH_M68K
 | 
			
		||||
#elif defined(TARGET_LM32)
 | 
			
		||||
#define QEMU_ARCH QEMU_ARCH_LM32
 | 
			
		||||
#elif defined(TARGET_MICROBLAZE)
 | 
			
		||||
#define QEMU_ARCH QEMU_ARCH_MICROBLAZE
 | 
			
		||||
#elif defined(TARGET_MIPS)
 | 
			
		||||
@@ -80,12 +79,12 @@ int graphic_depth = 32;
 | 
			
		||||
#define QEMU_ARCH QEMU_ARCH_SH4
 | 
			
		||||
#elif defined(TARGET_SPARC)
 | 
			
		||||
#define QEMU_ARCH QEMU_ARCH_SPARC
 | 
			
		||||
#elif defined(TARGET_TRICORE)
 | 
			
		||||
#define QEMU_ARCH QEMU_ARCH_TRICORE
 | 
			
		||||
#elif defined(TARGET_UNICORE32)
 | 
			
		||||
#define QEMU_ARCH QEMU_ARCH_UNICORE32
 | 
			
		||||
#elif defined(TARGET_XTENSA)
 | 
			
		||||
#define QEMU_ARCH QEMU_ARCH_XTENSA
 | 
			
		||||
#elif defined(TARGET_UNICORE32)
 | 
			
		||||
#define QEMU_ARCH QEMU_ARCH_UNICORE32
 | 
			
		||||
#elif defined(TARGET_TRICORE)
 | 
			
		||||
#define QEMU_ARCH QEMU_ARCH_TRICORE
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
const uint32_t arch_type = QEMU_ARCH;
 | 
			
		||||
@@ -113,8 +112,7 @@ TargetInfo *qmp_query_target(Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    TargetInfo *info = g_malloc0(sizeof(*info));
 | 
			
		||||
 | 
			
		||||
    info->arch = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, -1,
 | 
			
		||||
                                 &error_abort);
 | 
			
		||||
    info->arch = g_strdup(TARGET_NAME);
 | 
			
		||||
 | 
			
		||||
    return info;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,9 @@
 | 
			
		||||
#include "audio.h"
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
 | 
			
		||||
#if QEMU_GNUC_PREREQ(4, 3)
 | 
			
		||||
#pragma GCC diagnostic ignored "-Waddress"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define AUDIO_CAP "alsa"
 | 
			
		||||
#include "audio_int.h"
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,6 @@
 | 
			
		||||
#include "sysemu/sysemu.h"
 | 
			
		||||
#include "qemu/cutils.h"
 | 
			
		||||
#include "sysemu/replay.h"
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
 | 
			
		||||
#define AUDIO_CAP "audio"
 | 
			
		||||
#include "audio_int.h"
 | 
			
		||||
@@ -336,8 +335,9 @@ static int audio_get_conf_int (const char *key, int defval, int *defaultp)
 | 
			
		||||
    char *strval;
 | 
			
		||||
 | 
			
		||||
    strval = getenv (key);
 | 
			
		||||
    if (strval && !qemu_strtoi(strval, NULL, 10, &val)) {
 | 
			
		||||
    if (strval) {
 | 
			
		||||
        *defaultp = 0;
 | 
			
		||||
        val = atoi (strval);
 | 
			
		||||
        return val;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
@@ -1130,10 +1130,6 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
 | 
			
		||||
/*
 | 
			
		||||
 * Timer
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static bool audio_timer_running;
 | 
			
		||||
static uint64_t audio_timer_last;
 | 
			
		||||
 | 
			
		||||
static int audio_is_timer_needed (void)
 | 
			
		||||
{
 | 
			
		||||
    HWVoiceIn *hwi = NULL;
 | 
			
		||||
@@ -1153,31 +1149,14 @@ static void audio_reset_timer (AudioState *s)
 | 
			
		||||
    if (audio_is_timer_needed ()) {
 | 
			
		||||
        timer_mod_anticipate_ns(s->ts,
 | 
			
		||||
            qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks);
 | 
			
		||||
        if (!audio_timer_running) {
 | 
			
		||||
            audio_timer_running = true;
 | 
			
		||||
            audio_timer_last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
 | 
			
		||||
            trace_audio_timer_start(conf.period.ticks / SCALE_MS);
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        timer_del(s->ts);
 | 
			
		||||
        if (audio_timer_running) {
 | 
			
		||||
            audio_timer_running = false;
 | 
			
		||||
            trace_audio_timer_stop();
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        timer_del (s->ts);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void audio_timer (void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    int64_t now, diff;
 | 
			
		||||
 | 
			
		||||
    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
 | 
			
		||||
    diff = now - audio_timer_last;
 | 
			
		||||
    if (diff > conf.period.ticks * 3 / 2) {
 | 
			
		||||
        trace_audio_timer_delayed(diff / SCALE_MS);
 | 
			
		||||
    }
 | 
			
		||||
    audio_timer_last = now;
 | 
			
		||||
 | 
			
		||||
    audio_run ("timer");
 | 
			
		||||
    audio_reset_timer (opaque);
 | 
			
		||||
}
 | 
			
		||||
@@ -1762,7 +1741,7 @@ void AUD_help (void)
 | 
			
		||||
        );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int audio_driver_init(AudioState *s, struct audio_driver *drv, bool msg)
 | 
			
		||||
static int audio_driver_init (AudioState *s, struct audio_driver *drv)
 | 
			
		||||
{
 | 
			
		||||
    if (drv->options) {
 | 
			
		||||
        audio_process_options (drv->name, drv->options);
 | 
			
		||||
@@ -1776,9 +1755,7 @@ static int audio_driver_init(AudioState *s, struct audio_driver *drv, bool msg)
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        if (msg) {
 | 
			
		||||
            dolog("Could not init `%s' audio driver\n", drv->name);
 | 
			
		||||
        }
 | 
			
		||||
        dolog ("Could not init `%s' audio driver\n", drv->name);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1903,7 +1880,7 @@ static void audio_init (void)
 | 
			
		||||
    if (drvname) {
 | 
			
		||||
        driver = audio_driver_lookup(drvname);
 | 
			
		||||
        if (driver) {
 | 
			
		||||
            done = !audio_driver_init(s, driver, true);
 | 
			
		||||
            done = !audio_driver_init(s, driver);
 | 
			
		||||
        } else {
 | 
			
		||||
            dolog ("Unknown audio driver `%s'\n", drvname);
 | 
			
		||||
            dolog ("Run with -audio-help to list available drivers\n");
 | 
			
		||||
@@ -1914,14 +1891,14 @@ static void audio_init (void)
 | 
			
		||||
        for (i = 0; !done && i < ARRAY_SIZE(audio_prio_list); i++) {
 | 
			
		||||
            driver = audio_driver_lookup(audio_prio_list[i]);
 | 
			
		||||
            if (driver && driver->can_be_default) {
 | 
			
		||||
                done = !audio_driver_init(s, driver, false);
 | 
			
		||||
                done = !audio_driver_init(s, driver);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!done) {
 | 
			
		||||
        driver = audio_driver_lookup("none");
 | 
			
		||||
        done = !audio_driver_init(s, driver, false);
 | 
			
		||||
        done = !audio_driver_init(s, driver);
 | 
			
		||||
        assert(done);
 | 
			
		||||
        dolog("warning: Using timer based audio emulation\n");
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -191,7 +191,7 @@ struct SWVoiceCap {
 | 
			
		||||
    QLIST_ENTRY (SWVoiceCap) entries;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct AudioState {
 | 
			
		||||
struct AudioState {
 | 
			
		||||
    struct audio_driver *drv;
 | 
			
		||||
    void *drv_opaque;
 | 
			
		||||
 | 
			
		||||
@@ -203,7 +203,7 @@ typedef struct AudioState {
 | 
			
		||||
    int nb_hw_voices_out;
 | 
			
		||||
    int nb_hw_voices_in;
 | 
			
		||||
    int vm_running;
 | 
			
		||||
} AudioState;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const struct mixeng_volume nominal_volume;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -227,7 +227,7 @@ static void *qpa_thread_out (void *arg)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        decr = to_mix = audio_MIN(pa->live, pa->g->conf.samples >> 5);
 | 
			
		||||
        decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2);
 | 
			
		||||
        rpos = pa->rpos;
 | 
			
		||||
 | 
			
		||||
        if (audio_pt_unlock(&pa->pt, __func__)) {
 | 
			
		||||
@@ -319,7 +319,7 @@ static void *qpa_thread_in (void *arg)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        incr = to_grab = audio_MIN(pa->dead, pa->g->conf.samples >> 5);
 | 
			
		||||
        incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2);
 | 
			
		||||
        wpos = pa->wpos;
 | 
			
		||||
 | 
			
		||||
        if (audio_pt_unlock(&pa->pt, __func__)) {
 | 
			
		||||
@@ -814,21 +814,6 @@ static PAConf glob_conf = {
 | 
			
		||||
 | 
			
		||||
static void *qpa_audio_init (void)
 | 
			
		||||
{
 | 
			
		||||
    if (glob_conf.server == NULL) {
 | 
			
		||||
        char pidfile[64];
 | 
			
		||||
        char *runtime;
 | 
			
		||||
        struct stat st;
 | 
			
		||||
 | 
			
		||||
        runtime = getenv("XDG_RUNTIME_DIR");
 | 
			
		||||
        if (!runtime) {
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
        snprintf(pidfile, sizeof(pidfile), "%s/pulse/pid", runtime);
 | 
			
		||||
        if (stat(pidfile, &st) != 0) {
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    paaudio *g = g_malloc(sizeof(paaudio));
 | 
			
		||||
    g->conf = glob_conf;
 | 
			
		||||
    g->mainloop = NULL;
 | 
			
		||||
 
 | 
			
		||||
@@ -15,8 +15,3 @@ alsa_no_frames(int state) "No frames available and ALSA state is %d"
 | 
			
		||||
# audio/ossaudio.c
 | 
			
		||||
oss_version(int version) "OSS version = 0x%x"
 | 
			
		||||
oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d"
 | 
			
		||||
 | 
			
		||||
# audio/audio.c
 | 
			
		||||
audio_timer_start(int interval) "interval %d ms"
 | 
			
		||||
audio_timer_stop(void) ""
 | 
			
		||||
audio_timer_delayed(int interval) "interval %d ms"
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ common-obj-$(CONFIG_POSIX) += rng-random.o
 | 
			
		||||
common-obj-$(CONFIG_TPM) += tpm.o
 | 
			
		||||
 | 
			
		||||
common-obj-y += hostmem.o hostmem-ram.o
 | 
			
		||||
common-obj-$(CONFIG_POSIX) += hostmem-file.o
 | 
			
		||||
common-obj-$(CONFIG_LINUX) += hostmem-file.o
 | 
			
		||||
 | 
			
		||||
common-obj-y += cryptodev.o
 | 
			
		||||
common-obj-y += cryptodev-builtin.o
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,6 @@
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
#include "qapi/qmp/qerror.h"
 | 
			
		||||
#include "qemu/error-report.h"
 | 
			
		||||
#include "hw/virtio/vhost-user.h"
 | 
			
		||||
#include "standard-headers/linux/virtio_crypto.h"
 | 
			
		||||
#include "sysemu/cryptodev-vhost.h"
 | 
			
		||||
#include "chardev/char-fe.h"
 | 
			
		||||
@@ -47,7 +46,6 @@
 | 
			
		||||
typedef struct CryptoDevBackendVhostUser {
 | 
			
		||||
    CryptoDevBackend parent_obj;
 | 
			
		||||
 | 
			
		||||
    VhostUserState *vhost_user;
 | 
			
		||||
    CharBackend chr;
 | 
			
		||||
    char *chr_name;
 | 
			
		||||
    bool opened;
 | 
			
		||||
@@ -104,7 +102,7 @@ cryptodev_vhost_user_start(int queues,
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        options.opaque = s->vhost_user;
 | 
			
		||||
        options.opaque = &s->chr;
 | 
			
		||||
        options.backend_type = VHOST_BACKEND_TYPE_USER;
 | 
			
		||||
        options.cc = b->conf.peers.ccs[i];
 | 
			
		||||
        s->vhost_crypto[i] = cryptodev_vhost_init(&options);
 | 
			
		||||
@@ -157,6 +155,7 @@ static void cryptodev_vhost_user_event(void *opaque, int event)
 | 
			
		||||
{
 | 
			
		||||
    CryptoDevBackendVhostUser *s = opaque;
 | 
			
		||||
    CryptoDevBackend *b = CRYPTODEV_BACKEND(s);
 | 
			
		||||
    Error *err = NULL;
 | 
			
		||||
    int queues = b->conf.peers.queues;
 | 
			
		||||
 | 
			
		||||
    assert(queues < MAX_CRYPTO_QUEUE_NUM);
 | 
			
		||||
@@ -173,6 +172,10 @@ static void cryptodev_vhost_user_event(void *opaque, int event)
 | 
			
		||||
        cryptodev_vhost_user_stop(queues, s);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (err) {
 | 
			
		||||
        error_report_err(err);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cryptodev_vhost_user_init(
 | 
			
		||||
@@ -182,7 +185,6 @@ static void cryptodev_vhost_user_init(
 | 
			
		||||
    size_t i;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    Chardev *chr;
 | 
			
		||||
    VhostUserState *user;
 | 
			
		||||
    CryptoDevBackendClient *cc;
 | 
			
		||||
    CryptoDevBackendVhostUser *s =
 | 
			
		||||
                      CRYPTODEV_BACKEND_VHOST_USER(backend);
 | 
			
		||||
@@ -213,15 +215,6 @@ static void cryptodev_vhost_user_init(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    user = vhost_user_init();
 | 
			
		||||
    if (!user) {
 | 
			
		||||
        error_setg(errp, "Failed to init vhost_user");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    user->chr = &s->chr;
 | 
			
		||||
    s->vhost_user = user;
 | 
			
		||||
 | 
			
		||||
    qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
 | 
			
		||||
                     cryptodev_vhost_user_event, NULL, s, NULL, true);
 | 
			
		||||
 | 
			
		||||
@@ -306,12 +299,6 @@ static void cryptodev_vhost_user_cleanup(
 | 
			
		||||
            backend->conf.peers.ccs[i] = NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (s->vhost_user) {
 | 
			
		||||
        vhost_user_cleanup(s->vhost_user);
 | 
			
		||||
        g_free(s->vhost_user);
 | 
			
		||||
        s->vhost_user = NULL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cryptodev_vhost_user_set_chardev(Object *obj,
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,6 @@
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "qemu/error-report.h"
 | 
			
		||||
#include "sysemu/hostmem.h"
 | 
			
		||||
#include "sysemu/sysemu.h"
 | 
			
		||||
#include "qom/object_interfaces.h"
 | 
			
		||||
@@ -32,19 +31,15 @@ typedef struct HostMemoryBackendFile HostMemoryBackendFile;
 | 
			
		||||
struct HostMemoryBackendFile {
 | 
			
		||||
    HostMemoryBackend parent_obj;
 | 
			
		||||
 | 
			
		||||
    bool discard_data;
 | 
			
		||||
    char *mem_path;
 | 
			
		||||
    uint64_t align;
 | 
			
		||||
    bool discard_data;
 | 
			
		||||
    bool is_pmem;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend);
 | 
			
		||||
#ifdef CONFIG_POSIX
 | 
			
		||||
    gchar *name;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (!backend->size) {
 | 
			
		||||
        error_setg(errp, "can't create backend with size 0");
 | 
			
		||||
@@ -54,18 +49,19 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
 | 
			
		||||
        error_setg(errp, "mem-path property not set");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
#ifndef CONFIG_POSIX
 | 
			
		||||
#ifndef CONFIG_LINUX
 | 
			
		||||
    error_setg(errp, "-mem-path not supported on this host");
 | 
			
		||||
#else
 | 
			
		||||
    if (!host_memory_backend_mr_inited(backend)) {
 | 
			
		||||
        gchar *path;
 | 
			
		||||
        backend->force_prealloc = mem_prealloc;
 | 
			
		||||
    name = host_memory_backend_get_name(backend);
 | 
			
		||||
        path = object_get_canonical_path(OBJECT(backend));
 | 
			
		||||
        memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
 | 
			
		||||
                                     name,
 | 
			
		||||
                                     backend->size, fb->align,
 | 
			
		||||
                                     (backend->share ? RAM_SHARED : 0) |
 | 
			
		||||
                                     (fb->is_pmem ? RAM_PMEM : 0),
 | 
			
		||||
                                 path,
 | 
			
		||||
                                 backend->size, fb->align, backend->share,
 | 
			
		||||
                                 fb->mem_path, errp);
 | 
			
		||||
    g_free(name);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -82,8 +78,7 @@ static void set_mem_path(Object *o, const char *str, Error **errp)
 | 
			
		||||
    HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
 | 
			
		||||
 | 
			
		||||
    if (host_memory_backend_mr_inited(backend)) {
 | 
			
		||||
        error_setg(errp, "cannot change property 'mem-path' of %s",
 | 
			
		||||
                   object_get_typename(o));
 | 
			
		||||
        error_setg(errp, "cannot change property value");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    g_free(fb->mem_path);
 | 
			
		||||
@@ -121,8 +116,7 @@ static void file_memory_backend_set_align(Object *o, Visitor *v,
 | 
			
		||||
    uint64_t val;
 | 
			
		||||
 | 
			
		||||
    if (host_memory_backend_mr_inited(backend)) {
 | 
			
		||||
        error_setg(&local_err, "cannot change property '%s' of %s",
 | 
			
		||||
                   name, object_get_typename(o));
 | 
			
		||||
        error_setg(&local_err, "cannot change property value");
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -136,39 +130,6 @@ static void file_memory_backend_set_align(Object *o, Visitor *v,
 | 
			
		||||
    error_propagate(errp, local_err);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool file_memory_backend_get_pmem(Object *o, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    return MEMORY_BACKEND_FILE(o)->is_pmem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    HostMemoryBackend *backend = MEMORY_BACKEND(o);
 | 
			
		||||
    HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
 | 
			
		||||
 | 
			
		||||
    if (host_memory_backend_mr_inited(backend)) {
 | 
			
		||||
 | 
			
		||||
        error_setg(errp, "cannot change property 'pmem' of %s.",
 | 
			
		||||
                   object_get_typename(o));
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifndef CONFIG_LIBPMEM
 | 
			
		||||
    if (value) {
 | 
			
		||||
        Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
        error_setg(&local_err,
 | 
			
		||||
                   "Lack of libpmem support while setting the 'pmem=on'"
 | 
			
		||||
                   " of %s. We can't ensure data persistence.",
 | 
			
		||||
                   object_get_typename(o));
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    fb->is_pmem = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void file_backend_unparent(Object *obj)
 | 
			
		||||
{
 | 
			
		||||
    HostMemoryBackend *backend = MEMORY_BACKEND(obj);
 | 
			
		||||
@@ -200,9 +161,6 @@ file_backend_class_init(ObjectClass *oc, void *data)
 | 
			
		||||
        file_memory_backend_get_align,
 | 
			
		||||
        file_memory_backend_set_align,
 | 
			
		||||
        NULL, NULL, &error_abort);
 | 
			
		||||
    object_class_property_add_bool(oc, "pmem",
 | 
			
		||||
        file_memory_backend_get_pmem, file_memory_backend_set_pmem,
 | 
			
		||||
        &error_abort);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void file_backend_instance_finalize(Object *o)
 | 
			
		||||
 
 | 
			
		||||
@@ -44,6 +44,10 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (host_memory_backend_mr_inited(backend)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    backend->force_prealloc = mem_prealloc;
 | 
			
		||||
    fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size,
 | 
			
		||||
                           m->hugetlb, m->hugetlbsize, m->seal ?
 | 
			
		||||
@@ -53,10 +57,9 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    name = host_memory_backend_get_name(backend);
 | 
			
		||||
    name = object_get_canonical_path(OBJECT(backend));
 | 
			
		||||
    memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend),
 | 
			
		||||
                                   name, backend->size,
 | 
			
		||||
                                   backend->share, fd, errp);
 | 
			
		||||
                                   name, backend->size, true, fd, errp);
 | 
			
		||||
    g_free(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -128,7 +131,6 @@ memfd_backend_instance_init(Object *obj)
 | 
			
		||||
 | 
			
		||||
    /* default to sealed file */
 | 
			
		||||
    m->seal = true;
 | 
			
		||||
    MEMORY_BACKEND(m)->share = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@@ -138,31 +140,18 @@ memfd_backend_class_init(ObjectClass *oc, void *data)
 | 
			
		||||
 | 
			
		||||
    bc->alloc = memfd_backend_memory_alloc;
 | 
			
		||||
 | 
			
		||||
    if (qemu_memfd_check(MFD_HUGETLB)) {
 | 
			
		||||
    object_class_property_add_bool(oc, "hugetlb",
 | 
			
		||||
                                   memfd_backend_get_hugetlb,
 | 
			
		||||
                                   memfd_backend_set_hugetlb,
 | 
			
		||||
                                   &error_abort);
 | 
			
		||||
        object_class_property_set_description(oc, "hugetlb",
 | 
			
		||||
                                              "Use huge pages",
 | 
			
		||||
                                              &error_abort);
 | 
			
		||||
    object_class_property_add(oc, "hugetlbsize", "int",
 | 
			
		||||
                              memfd_backend_get_hugetlbsize,
 | 
			
		||||
                              memfd_backend_set_hugetlbsize,
 | 
			
		||||
                              NULL, NULL, &error_abort);
 | 
			
		||||
        object_class_property_set_description(oc, "hugetlbsize",
 | 
			
		||||
                                              "Huge pages size (ex: 2M, 1G)",
 | 
			
		||||
                                              &error_abort);
 | 
			
		||||
    }
 | 
			
		||||
    if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
 | 
			
		||||
    object_class_property_add_bool(oc, "seal",
 | 
			
		||||
                                   memfd_backend_get_seal,
 | 
			
		||||
                                   memfd_backend_set_seal,
 | 
			
		||||
                                   &error_abort);
 | 
			
		||||
        object_class_property_set_description(oc, "seal",
 | 
			
		||||
                                              "Seal growing & shrinking",
 | 
			
		||||
                                              &error_abort);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const TypeInfo memfd_backend_info = {
 | 
			
		||||
@@ -175,9 +164,7 @@ static const TypeInfo memfd_backend_info = {
 | 
			
		||||
 | 
			
		||||
static void register_types(void)
 | 
			
		||||
{
 | 
			
		||||
    if (qemu_memfd_check(0)) {
 | 
			
		||||
    type_register_static(&memfd_backend_info);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type_init(register_types);
 | 
			
		||||
 
 | 
			
		||||
@@ -16,20 +16,21 @@
 | 
			
		||||
 | 
			
		||||
#define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    char *name;
 | 
			
		||||
    char *path;
 | 
			
		||||
 | 
			
		||||
    if (!backend->size) {
 | 
			
		||||
        error_setg(errp, "can't create backend with size 0");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    name = host_memory_backend_get_name(backend);
 | 
			
		||||
    memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), name,
 | 
			
		||||
    path = object_get_canonical_path_component(OBJECT(backend));
 | 
			
		||||
    memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), path,
 | 
			
		||||
                           backend->size, backend->share, errp);
 | 
			
		||||
    g_free(name);
 | 
			
		||||
    g_free(path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,6 @@
 | 
			
		||||
#include "qapi/visitor.h"
 | 
			
		||||
#include "qemu/config-file.h"
 | 
			
		||||
#include "qom/object_interfaces.h"
 | 
			
		||||
#include "qemu/mmap-alloc.h"
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_NUMA
 | 
			
		||||
#include <numaif.h>
 | 
			
		||||
@@ -28,16 +27,6 @@ QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_BIND != MPOL_BIND);
 | 
			
		||||
QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
char *
 | 
			
		||||
host_memory_backend_get_name(HostMemoryBackend *backend)
 | 
			
		||||
{
 | 
			
		||||
    if (!backend->use_canonical_path) {
 | 
			
		||||
        return object_get_canonical_path_component(OBJECT(backend));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return object_get_canonical_path(OBJECT(backend));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
host_memory_backend_get_size(Object *obj, Visitor *v, const char *name,
 | 
			
		||||
                             void *opaque, Error **errp)
 | 
			
		||||
@@ -57,8 +46,7 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name,
 | 
			
		||||
    uint64_t value;
 | 
			
		||||
 | 
			
		||||
    if (host_memory_backend_mr_inited(backend)) {
 | 
			
		||||
        error_setg(&local_err, "cannot change property %s of %s ",
 | 
			
		||||
                   name, object_get_typename(obj));
 | 
			
		||||
        error_setg(&local_err, "cannot change property value");
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -67,9 +55,8 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name,
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
    if (!value) {
 | 
			
		||||
        error_setg(&local_err,
 | 
			
		||||
                   "property '%s' of %s doesn't take value '%" PRIu64 "'",
 | 
			
		||||
                   name, object_get_typename(obj), value);
 | 
			
		||||
        error_setg(&local_err, "Property '%s.%s' doesn't take value '%"
 | 
			
		||||
                   PRIu64 "'", object_get_typename(obj), name, value);
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
    backend->size = value;
 | 
			
		||||
@@ -115,23 +102,14 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name,
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_NUMA
 | 
			
		||||
    HostMemoryBackend *backend = MEMORY_BACKEND(obj);
 | 
			
		||||
    uint16List *l, *host_nodes = NULL;
 | 
			
		||||
    uint16List *l = NULL;
 | 
			
		||||
 | 
			
		||||
    visit_type_uint16List(v, name, &host_nodes, errp);
 | 
			
		||||
    visit_type_uint16List(v, name, &l, errp);
 | 
			
		||||
 | 
			
		||||
    for (l = host_nodes; l; l = l->next) {
 | 
			
		||||
        if (l->value >= MAX_NODES) {
 | 
			
		||||
            error_setg(errp, "Invalid host-nodes value: %d", l->value);
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (l = host_nodes; l; l = l->next) {
 | 
			
		||||
    while (l) {
 | 
			
		||||
        bitmap_set(backend->host_nodes, l->value, 1);
 | 
			
		||||
        l = l->next;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
    qapi_free_uint16List(host_nodes);
 | 
			
		||||
#else
 | 
			
		||||
    error_setg(errp, "NUMA node binding are not supported by this QEMU");
 | 
			
		||||
#endif
 | 
			
		||||
@@ -259,11 +237,6 @@ static void host_memory_backend_init(Object *obj)
 | 
			
		||||
    backend->prealloc = mem_prealloc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void host_memory_backend_post_init(Object *obj)
 | 
			
		||||
{
 | 
			
		||||
    object_apply_compat_props(obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool host_memory_backend_mr_inited(HostMemoryBackend *backend)
 | 
			
		||||
{
 | 
			
		||||
    /*
 | 
			
		||||
@@ -273,7 +246,8 @@ bool host_memory_backend_mr_inited(HostMemoryBackend *backend)
 | 
			
		||||
    return memory_region_size(&backend->mr) != 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend)
 | 
			
		||||
MemoryRegion *
 | 
			
		||||
host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL;
 | 
			
		||||
}
 | 
			
		||||
@@ -288,23 +262,6 @@ bool host_memory_backend_is_mapped(HostMemoryBackend *backend)
 | 
			
		||||
    return backend->is_mapped;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
size_t host_memory_backend_pagesize(HostMemoryBackend *memdev)
 | 
			
		||||
{
 | 
			
		||||
    Object *obj = OBJECT(memdev);
 | 
			
		||||
    char *path = object_property_get_str(obj, "mem-path", NULL);
 | 
			
		||||
    size_t pagesize = qemu_mempath_getpagesize(path);
 | 
			
		||||
 | 
			
		||||
    g_free(path);
 | 
			
		||||
    return pagesize;
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
size_t host_memory_backend_pagesize(HostMemoryBackend *memdev)
 | 
			
		||||
{
 | 
			
		||||
    return getpagesize();
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
@@ -394,6 +351,24 @@ host_memory_backend_can_be_deleted(UserCreatable *uc)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char *get_id(Object *o, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    HostMemoryBackend *backend = MEMORY_BACKEND(o);
 | 
			
		||||
 | 
			
		||||
    return g_strdup(backend->id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void set_id(Object *o, const char *str, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    HostMemoryBackend *backend = MEMORY_BACKEND(o);
 | 
			
		||||
 | 
			
		||||
    if (backend->id) {
 | 
			
		||||
        error_setg(errp, "cannot change property value");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    backend->id = g_strdup(str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool host_memory_backend_get_share(Object *o, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    HostMemoryBackend *backend = MEMORY_BACKEND(o);
 | 
			
		||||
@@ -412,23 +387,6 @@ static void host_memory_backend_set_share(Object *o, bool value, Error **errp)
 | 
			
		||||
    backend->share = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
host_memory_backend_get_use_canonical_path(Object *obj, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    HostMemoryBackend *backend = MEMORY_BACKEND(obj);
 | 
			
		||||
 | 
			
		||||
    return backend->use_canonical_path;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
host_memory_backend_set_use_canonical_path(Object *obj, bool value,
 | 
			
		||||
                                           Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    HostMemoryBackend *backend = MEMORY_BACKEND(obj);
 | 
			
		||||
 | 
			
		||||
    backend->use_canonical_path = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
host_memory_backend_class_init(ObjectClass *oc, void *data)
 | 
			
		||||
{
 | 
			
		||||
@@ -440,44 +398,34 @@ host_memory_backend_class_init(ObjectClass *oc, void *data)
 | 
			
		||||
    object_class_property_add_bool(oc, "merge",
 | 
			
		||||
        host_memory_backend_get_merge,
 | 
			
		||||
        host_memory_backend_set_merge, &error_abort);
 | 
			
		||||
    object_class_property_set_description(oc, "merge",
 | 
			
		||||
        "Mark memory as mergeable", &error_abort);
 | 
			
		||||
    object_class_property_add_bool(oc, "dump",
 | 
			
		||||
        host_memory_backend_get_dump,
 | 
			
		||||
        host_memory_backend_set_dump, &error_abort);
 | 
			
		||||
    object_class_property_set_description(oc, "dump",
 | 
			
		||||
        "Set to 'off' to exclude from core dump", &error_abort);
 | 
			
		||||
    object_class_property_add_bool(oc, "prealloc",
 | 
			
		||||
        host_memory_backend_get_prealloc,
 | 
			
		||||
        host_memory_backend_set_prealloc, &error_abort);
 | 
			
		||||
    object_class_property_set_description(oc, "prealloc",
 | 
			
		||||
        "Preallocate memory", &error_abort);
 | 
			
		||||
    object_class_property_add(oc, "size", "int",
 | 
			
		||||
        host_memory_backend_get_size,
 | 
			
		||||
        host_memory_backend_set_size,
 | 
			
		||||
        NULL, NULL, &error_abort);
 | 
			
		||||
    object_class_property_set_description(oc, "size",
 | 
			
		||||
        "Size of the memory region (ex: 500M)", &error_abort);
 | 
			
		||||
    object_class_property_add(oc, "host-nodes", "int",
 | 
			
		||||
        host_memory_backend_get_host_nodes,
 | 
			
		||||
        host_memory_backend_set_host_nodes,
 | 
			
		||||
        NULL, NULL, &error_abort);
 | 
			
		||||
    object_class_property_set_description(oc, "host-nodes",
 | 
			
		||||
        "Binds memory to the list of NUMA host nodes", &error_abort);
 | 
			
		||||
    object_class_property_add_enum(oc, "policy", "HostMemPolicy",
 | 
			
		||||
        &HostMemPolicy_lookup,
 | 
			
		||||
        host_memory_backend_get_policy,
 | 
			
		||||
        host_memory_backend_set_policy, &error_abort);
 | 
			
		||||
    object_class_property_set_description(oc, "policy",
 | 
			
		||||
        "Set the NUMA policy", &error_abort);
 | 
			
		||||
    object_class_property_add_str(oc, "id", get_id, set_id, &error_abort);
 | 
			
		||||
    object_class_property_add_bool(oc, "share",
 | 
			
		||||
        host_memory_backend_get_share, host_memory_backend_set_share,
 | 
			
		||||
        &error_abort);
 | 
			
		||||
    object_class_property_set_description(oc, "share",
 | 
			
		||||
        "Mark the memory as private to QEMU or shared", &error_abort);
 | 
			
		||||
    object_class_property_add_bool(oc, "x-use-canonical-path-for-ramblock-id",
 | 
			
		||||
        host_memory_backend_get_use_canonical_path,
 | 
			
		||||
        host_memory_backend_set_use_canonical_path, &error_abort);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void host_memory_backend_finalize(Object *o)
 | 
			
		||||
{
 | 
			
		||||
    HostMemoryBackend *backend = MEMORY_BACKEND(o);
 | 
			
		||||
    g_free(backend->id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const TypeInfo host_memory_backend_info = {
 | 
			
		||||
@@ -488,7 +436,7 @@ static const TypeInfo host_memory_backend_info = {
 | 
			
		||||
    .class_init = host_memory_backend_class_init,
 | 
			
		||||
    .instance_size = sizeof(HostMemoryBackend),
 | 
			
		||||
    .instance_init = host_memory_backend_init,
 | 
			
		||||
    .instance_post_init = host_memory_backend_post_init,
 | 
			
		||||
    .instance_finalize = host_memory_backend_finalize,
 | 
			
		||||
    .interfaces = (InterfaceInfo[]) {
 | 
			
		||||
        { TYPE_USER_CREATABLE },
 | 
			
		||||
        { }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								balloon.c
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								balloon.c
									
									
									
									
									
								
							@@ -26,7 +26,6 @@
 | 
			
		||||
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "qemu/atomic.h"
 | 
			
		||||
#include "exec/cpu-common.h"
 | 
			
		||||
#include "sysemu/kvm.h"
 | 
			
		||||
#include "sysemu/balloon.h"
 | 
			
		||||
@@ -38,22 +37,16 @@
 | 
			
		||||
static QEMUBalloonEvent *balloon_event_fn;
 | 
			
		||||
static QEMUBalloonStatus *balloon_stat_fn;
 | 
			
		||||
static void *balloon_opaque;
 | 
			
		||||
static int balloon_inhibit_count;
 | 
			
		||||
static bool balloon_inhibited;
 | 
			
		||||
 | 
			
		||||
bool qemu_balloon_is_inhibited(void)
 | 
			
		||||
{
 | 
			
		||||
    return atomic_read(&balloon_inhibit_count) > 0;
 | 
			
		||||
    return balloon_inhibited;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void qemu_balloon_inhibit(bool state)
 | 
			
		||||
{
 | 
			
		||||
    if (state) {
 | 
			
		||||
        atomic_inc(&balloon_inhibit_count);
 | 
			
		||||
    } else {
 | 
			
		||||
        atomic_dec(&balloon_inhibit_count);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    assert(atomic_read(&balloon_inhibit_count) >= 0);
 | 
			
		||||
    balloon_inhibited = state;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool have_balloon(Error **errp)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,10 @@
 | 
			
		||||
block-obj-y += raw-format.o vmdk.o vpc.o
 | 
			
		||||
block-obj-$(CONFIG_QCOW1) += qcow.o
 | 
			
		||||
block-obj-$(CONFIG_VDI) += vdi.o
 | 
			
		||||
block-obj-$(CONFIG_CLOOP) += cloop.o
 | 
			
		||||
block-obj-$(CONFIG_BOCHS) += bochs.o
 | 
			
		||||
block-obj-$(CONFIG_VVFAT) += vvfat.o
 | 
			
		||||
block-obj-$(CONFIG_DMG) += dmg.o
 | 
			
		||||
 | 
			
		||||
block-obj-y += raw-format.o qcow.o vdi.o vmdk.o cloop.o bochs.o vpc.o vvfat.o dmg.o
 | 
			
		||||
block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o
 | 
			
		||||
block-obj-$(CONFIG_QED) += qed.o qed-l2-cache.o qed-table.o qed-cluster.o
 | 
			
		||||
block-obj-$(CONFIG_QED) += qed-check.o
 | 
			
		||||
block-obj-y += qed.o qed-l2-cache.o qed-table.o qed-cluster.o
 | 
			
		||||
block-obj-y += qed-check.o
 | 
			
		||||
block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o
 | 
			
		||||
block-obj-y += quorum.o
 | 
			
		||||
block-obj-y += blkdebug.o blkverify.o blkreplay.o
 | 
			
		||||
block-obj-$(CONFIG_PARALLELS) += parallels.o
 | 
			
		||||
block-obj-y += blklogwrites.o
 | 
			
		||||
block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o
 | 
			
		||||
block-obj-y += block-backend.o snapshot.o qapi.o
 | 
			
		||||
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
 | 
			
		||||
block-obj-$(CONFIG_POSIX) += file-posix.o
 | 
			
		||||
@@ -22,8 +13,7 @@ block-obj-y += null.o mirror.o commit.o io.o create.o
 | 
			
		||||
block-obj-y += throttle-groups.o
 | 
			
		||||
block-obj-$(CONFIG_LINUX) += nvme.o
 | 
			
		||||
 | 
			
		||||
block-obj-y += nbd.o nbd-client.o
 | 
			
		||||
block-obj-$(CONFIG_SHEEPDOG) += sheepdog.o
 | 
			
		||||
block-obj-y += nbd.o nbd-client.o sheepdog.o
 | 
			
		||||
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
 | 
			
		||||
block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o
 | 
			
		||||
block-obj-$(CONFIG_LIBNFS) += nfs.o
 | 
			
		||||
@@ -36,7 +26,7 @@ block-obj-y += accounting.o dirty-bitmap.o
 | 
			
		||||
block-obj-y += write-threshold.o
 | 
			
		||||
block-obj-y += backup.o
 | 
			
		||||
block-obj-$(CONFIG_REPLICATION) += replication.o
 | 
			
		||||
block-obj-y += throttle.o copy-on-read.o
 | 
			
		||||
block-obj-y += throttle.o
 | 
			
		||||
 | 
			
		||||
block-obj-y += crypto.o
 | 
			
		||||
 | 
			
		||||
@@ -54,11 +44,8 @@ gluster.o-libs     := $(GLUSTERFS_LIBS)
 | 
			
		||||
vxhs.o-libs        := $(VXHS_LIBS)
 | 
			
		||||
ssh.o-cflags       := $(LIBSSH2_CFLAGS)
 | 
			
		||||
ssh.o-libs         := $(LIBSSH2_LIBS)
 | 
			
		||||
block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o
 | 
			
		||||
block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y)
 | 
			
		||||
block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o
 | 
			
		||||
dmg-bz2.o-libs     := $(BZIP2_LIBS)
 | 
			
		||||
block-obj-$(if $(CONFIG_LZFSE),m,n) += dmg-lzfse.o
 | 
			
		||||
dmg-lzfse.o-libs   := $(LZFSE_LIBS)
 | 
			
		||||
qcow.o-libs        := -lz
 | 
			
		||||
linux-aio.o-libs   := -laio
 | 
			
		||||
parallels.o-cflags := $(LIBXML2_CFLAGS)
 | 
			
		||||
 
 | 
			
		||||
@@ -94,94 +94,6 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
 | 
			
		||||
    cookie->type = type;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* block_latency_histogram_compare_func:
 | 
			
		||||
 * Compare @key with interval [@it[0], @it[1]).
 | 
			
		||||
 * Return: -1 if @key < @it[0]
 | 
			
		||||
 *          0 if @key in [@it[0], @it[1])
 | 
			
		||||
 *         +1 if @key >= @it[1]
 | 
			
		||||
 */
 | 
			
		||||
static int block_latency_histogram_compare_func(const void *key, const void *it)
 | 
			
		||||
{
 | 
			
		||||
    uint64_t k = *(uint64_t *)key;
 | 
			
		||||
    uint64_t a = ((uint64_t *)it)[0];
 | 
			
		||||
    uint64_t b = ((uint64_t *)it)[1];
 | 
			
		||||
 | 
			
		||||
    return k < a ? -1 : (k < b ? 0 : 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void block_latency_histogram_account(BlockLatencyHistogram *hist,
 | 
			
		||||
                                            int64_t latency_ns)
 | 
			
		||||
{
 | 
			
		||||
    uint64_t *pos;
 | 
			
		||||
 | 
			
		||||
    if (hist->bins == NULL) {
 | 
			
		||||
        /* histogram disabled */
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    if (latency_ns < hist->boundaries[0]) {
 | 
			
		||||
        hist->bins[0]++;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (latency_ns >= hist->boundaries[hist->nbins - 2]) {
 | 
			
		||||
        hist->bins[hist->nbins - 1]++;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pos = bsearch(&latency_ns, hist->boundaries, hist->nbins - 2,
 | 
			
		||||
                  sizeof(hist->boundaries[0]),
 | 
			
		||||
                  block_latency_histogram_compare_func);
 | 
			
		||||
    assert(pos != NULL);
 | 
			
		||||
 | 
			
		||||
    hist->bins[pos - hist->boundaries + 1]++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type,
 | 
			
		||||
                                uint64List *boundaries)
 | 
			
		||||
{
 | 
			
		||||
    BlockLatencyHistogram *hist = &stats->latency_histogram[type];
 | 
			
		||||
    uint64List *entry;
 | 
			
		||||
    uint64_t *ptr;
 | 
			
		||||
    uint64_t prev = 0;
 | 
			
		||||
    int new_nbins = 1;
 | 
			
		||||
 | 
			
		||||
    for (entry = boundaries; entry; entry = entry->next) {
 | 
			
		||||
        if (entry->value <= prev) {
 | 
			
		||||
            return -EINVAL;
 | 
			
		||||
        }
 | 
			
		||||
        new_nbins++;
 | 
			
		||||
        prev = entry->value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hist->nbins = new_nbins;
 | 
			
		||||
    g_free(hist->boundaries);
 | 
			
		||||
    hist->boundaries = g_new(uint64_t, hist->nbins - 1);
 | 
			
		||||
    for (entry = boundaries, ptr = hist->boundaries; entry;
 | 
			
		||||
         entry = entry->next, ptr++)
 | 
			
		||||
    {
 | 
			
		||||
        *ptr = entry->value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_free(hist->bins);
 | 
			
		||||
    hist->bins = g_new0(uint64_t, hist->nbins);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void block_latency_histograms_clear(BlockAcctStats *stats)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < BLOCK_MAX_IOTYPE; i++) {
 | 
			
		||||
        BlockLatencyHistogram *hist = &stats->latency_histogram[i];
 | 
			
		||||
        g_free(hist->bins);
 | 
			
		||||
        g_free(hist->boundaries);
 | 
			
		||||
        memset(hist, 0, sizeof(*hist));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
 | 
			
		||||
                                 bool failed)
 | 
			
		||||
{
 | 
			
		||||
@@ -204,9 +116,6 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
 | 
			
		||||
        stats->nr_ops[cookie->type]++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    block_latency_histogram_account(&stats->latency_histogram[cookie->type],
 | 
			
		||||
                                    latency_ns);
 | 
			
		||||
 | 
			
		||||
    if (!failed || stats->account_failed) {
 | 
			
		||||
        stats->total_time_ns[cookie->type] += latency_ns;
 | 
			
		||||
        stats->last_access_time_ns = time_ns;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										358
									
								
								block/backup.c
									
									
									
									
									
								
							
							
						
						
									
										358
									
								
								block/backup.c
									
									
									
									
									
								
							@@ -27,13 +27,7 @@
 | 
			
		||||
#include "qemu/error-report.h"
 | 
			
		||||
 | 
			
		||||
#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
 | 
			
		||||
 | 
			
		||||
typedef struct CowRequest {
 | 
			
		||||
    int64_t start_byte;
 | 
			
		||||
    int64_t end_byte;
 | 
			
		||||
    QLIST_ENTRY(CowRequest) list;
 | 
			
		||||
    CoQueue wait_queue; /* coroutines blocked on this request */
 | 
			
		||||
} CowRequest;
 | 
			
		||||
#define SLICE_TIME 100000000ULL /* ns */
 | 
			
		||||
 | 
			
		||||
typedef struct BackupBlockJob {
 | 
			
		||||
    BlockJob common;
 | 
			
		||||
@@ -41,10 +35,10 @@ typedef struct BackupBlockJob {
 | 
			
		||||
    /* bitmap for sync=incremental */
 | 
			
		||||
    BdrvDirtyBitmap *sync_bitmap;
 | 
			
		||||
    MirrorSyncMode sync_mode;
 | 
			
		||||
    RateLimit limit;
 | 
			
		||||
    BlockdevOnError on_source_error;
 | 
			
		||||
    BlockdevOnError on_target_error;
 | 
			
		||||
    CoRwlock flush_rwlock;
 | 
			
		||||
    uint64_t len;
 | 
			
		||||
    uint64_t bytes_read;
 | 
			
		||||
    int64_t cluster_size;
 | 
			
		||||
    bool compress;
 | 
			
		||||
@@ -52,14 +46,8 @@ typedef struct BackupBlockJob {
 | 
			
		||||
    QLIST_HEAD(, CowRequest) inflight_reqs;
 | 
			
		||||
 | 
			
		||||
    HBitmap *copy_bitmap;
 | 
			
		||||
    bool use_copy_range;
 | 
			
		||||
    int64_t copy_range_size;
 | 
			
		||||
 | 
			
		||||
    bool serialize_target_writes;
 | 
			
		||||
} BackupBlockJob;
 | 
			
		||||
 | 
			
		||||
static const BlockJobDriver backup_job_driver;
 | 
			
		||||
 | 
			
		||||
/* See if in-flight requests overlap and wait for them to complete */
 | 
			
		||||
static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
 | 
			
		||||
                                                       int64_t start,
 | 
			
		||||
@@ -97,104 +85,19 @@ static void cow_request_end(CowRequest *req)
 | 
			
		||||
    qemu_co_queue_restart_all(&req->wait_queue);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Copy range to target with a bounce buffer and return the bytes copied. If
 | 
			
		||||
 * error occurred, return a negative error number */
 | 
			
		||||
static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
 | 
			
		||||
                                                      int64_t start,
 | 
			
		||||
                                                      int64_t end,
 | 
			
		||||
                                                      bool is_write_notifier,
 | 
			
		||||
                                                      bool *error_is_read,
 | 
			
		||||
                                                      void **bounce_buffer)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
    struct iovec iov;
 | 
			
		||||
    QEMUIOVector qiov;
 | 
			
		||||
    BlockBackend *blk = job->common.blk;
 | 
			
		||||
    int nbytes;
 | 
			
		||||
    int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
 | 
			
		||||
    int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0;
 | 
			
		||||
 | 
			
		||||
    hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1);
 | 
			
		||||
    nbytes = MIN(job->cluster_size, job->len - start);
 | 
			
		||||
    if (!*bounce_buffer) {
 | 
			
		||||
        *bounce_buffer = blk_blockalign(blk, job->cluster_size);
 | 
			
		||||
    }
 | 
			
		||||
    iov.iov_base = *bounce_buffer;
 | 
			
		||||
    iov.iov_len = nbytes;
 | 
			
		||||
    qemu_iovec_init_external(&qiov, &iov, 1);
 | 
			
		||||
 | 
			
		||||
    ret = blk_co_preadv(blk, start, qiov.size, &qiov, read_flags);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        trace_backup_do_cow_read_fail(job, start, ret);
 | 
			
		||||
        if (error_is_read) {
 | 
			
		||||
            *error_is_read = true;
 | 
			
		||||
        }
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (qemu_iovec_is_zero(&qiov)) {
 | 
			
		||||
        ret = blk_co_pwrite_zeroes(job->target, start,
 | 
			
		||||
                                   qiov.size, write_flags | BDRV_REQ_MAY_UNMAP);
 | 
			
		||||
    } else {
 | 
			
		||||
        ret = blk_co_pwritev(job->target, start,
 | 
			
		||||
                             qiov.size, &qiov, write_flags |
 | 
			
		||||
                             (job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0));
 | 
			
		||||
    }
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        trace_backup_do_cow_write_fail(job, start, ret);
 | 
			
		||||
        if (error_is_read) {
 | 
			
		||||
            *error_is_read = false;
 | 
			
		||||
        }
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return nbytes;
 | 
			
		||||
fail:
 | 
			
		||||
    hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
 | 
			
		||||
    return ret;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Copy range to target and return the bytes copied. If error occurred, return a
 | 
			
		||||
 * negative error number. */
 | 
			
		||||
static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job,
 | 
			
		||||
                                                int64_t start,
 | 
			
		||||
                                                int64_t end,
 | 
			
		||||
                                                bool is_write_notifier)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
    int nr_clusters;
 | 
			
		||||
    BlockBackend *blk = job->common.blk;
 | 
			
		||||
    int nbytes;
 | 
			
		||||
    int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
 | 
			
		||||
    int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0;
 | 
			
		||||
 | 
			
		||||
    assert(QEMU_IS_ALIGNED(job->copy_range_size, job->cluster_size));
 | 
			
		||||
    nbytes = MIN(job->copy_range_size, end - start);
 | 
			
		||||
    nr_clusters = DIV_ROUND_UP(nbytes, job->cluster_size);
 | 
			
		||||
    hbitmap_reset(job->copy_bitmap, start / job->cluster_size,
 | 
			
		||||
                  nr_clusters);
 | 
			
		||||
    ret = blk_co_copy_range(blk, start, job->target, start, nbytes,
 | 
			
		||||
                            read_flags, write_flags);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        trace_backup_do_cow_copy_range_fail(job, start, ret);
 | 
			
		||||
        hbitmap_set(job->copy_bitmap, start / job->cluster_size,
 | 
			
		||||
                    nr_clusters);
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return nbytes;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn backup_do_cow(BackupBlockJob *job,
 | 
			
		||||
                                      int64_t offset, uint64_t bytes,
 | 
			
		||||
                                      bool *error_is_read,
 | 
			
		||||
                                      bool is_write_notifier)
 | 
			
		||||
{
 | 
			
		||||
    BlockBackend *blk = job->common.blk;
 | 
			
		||||
    CowRequest cow_request;
 | 
			
		||||
    struct iovec iov;
 | 
			
		||||
    QEMUIOVector bounce_qiov;
 | 
			
		||||
    void *bounce_buffer = NULL;
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
    int64_t start, end; /* bytes */
 | 
			
		||||
    void *bounce_buffer = NULL;
 | 
			
		||||
    int n; /* bytes */
 | 
			
		||||
 | 
			
		||||
    qemu_co_rwlock_rdlock(&job->flush_rwlock);
 | 
			
		||||
 | 
			
		||||
@@ -206,38 +109,60 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
 | 
			
		||||
    wait_for_overlapping_requests(job, start, end);
 | 
			
		||||
    cow_request_begin(&cow_request, job, start, end);
 | 
			
		||||
 | 
			
		||||
    while (start < end) {
 | 
			
		||||
    for (; start < end; start += job->cluster_size) {
 | 
			
		||||
        if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) {
 | 
			
		||||
            trace_backup_do_cow_skip(job, start);
 | 
			
		||||
            start += job->cluster_size;
 | 
			
		||||
            continue; /* already copied */
 | 
			
		||||
        }
 | 
			
		||||
        hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1);
 | 
			
		||||
 | 
			
		||||
        trace_backup_do_cow_process(job, start);
 | 
			
		||||
 | 
			
		||||
        if (job->use_copy_range) {
 | 
			
		||||
            ret = backup_cow_with_offload(job, start, end, is_write_notifier);
 | 
			
		||||
        n = MIN(job->cluster_size, job->common.len - start);
 | 
			
		||||
 | 
			
		||||
        if (!bounce_buffer) {
 | 
			
		||||
            bounce_buffer = blk_blockalign(blk, job->cluster_size);
 | 
			
		||||
        }
 | 
			
		||||
        iov.iov_base = bounce_buffer;
 | 
			
		||||
        iov.iov_len = n;
 | 
			
		||||
        qemu_iovec_init_external(&bounce_qiov, &iov, 1);
 | 
			
		||||
 | 
			
		||||
        ret = blk_co_preadv(blk, start, bounce_qiov.size, &bounce_qiov,
 | 
			
		||||
                            is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
                job->use_copy_range = false;
 | 
			
		||||
            trace_backup_do_cow_read_fail(job, start, ret);
 | 
			
		||||
            if (error_is_read) {
 | 
			
		||||
                *error_is_read = true;
 | 
			
		||||
            }
 | 
			
		||||
            hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
        if (!job->use_copy_range) {
 | 
			
		||||
            ret = backup_cow_with_bounce_buffer(job, start, end, is_write_notifier,
 | 
			
		||||
                                                error_is_read, &bounce_buffer);
 | 
			
		||||
 | 
			
		||||
        if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
 | 
			
		||||
            ret = blk_co_pwrite_zeroes(job->target, start,
 | 
			
		||||
                                       bounce_qiov.size, BDRV_REQ_MAY_UNMAP);
 | 
			
		||||
        } else {
 | 
			
		||||
            ret = blk_co_pwritev(job->target, start,
 | 
			
		||||
                                 bounce_qiov.size, &bounce_qiov,
 | 
			
		||||
                                 job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
 | 
			
		||||
        }
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            break;
 | 
			
		||||
            trace_backup_do_cow_write_fail(job, start, ret);
 | 
			
		||||
            if (error_is_read) {
 | 
			
		||||
                *error_is_read = false;
 | 
			
		||||
            }
 | 
			
		||||
            hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Publish progress, guest I/O counts as progress too.  Note that the
 | 
			
		||||
         * offset field is an opaque progress value, it is not a disk offset.
 | 
			
		||||
         */
 | 
			
		||||
        start += ret;
 | 
			
		||||
        job->bytes_read += ret;
 | 
			
		||||
        job_progress_update(&job->common.job, ret);
 | 
			
		||||
        ret = 0;
 | 
			
		||||
        job->bytes_read += n;
 | 
			
		||||
        job->common.offset += n;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
    if (bounce_buffer) {
 | 
			
		||||
        qemu_vfree(bounce_buffer);
 | 
			
		||||
    }
 | 
			
		||||
@@ -265,12 +190,23 @@ static int coroutine_fn backup_before_write_notify(
 | 
			
		||||
    return backup_do_cow(job, req->offset, req->bytes, NULL, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BackupBlockJob *s = container_of(job, BackupBlockJob, common);
 | 
			
		||||
 | 
			
		||||
    if (speed < 0) {
 | 
			
		||||
        error_setg(errp, QERR_INVALID_PARAMETER, "speed");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    ratelimit_set_speed(&s->limit, speed, SLICE_TIME);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
 | 
			
		||||
{
 | 
			
		||||
    BdrvDirtyBitmap *bm;
 | 
			
		||||
    BlockDriverState *bs = blk_bs(job->common.blk);
 | 
			
		||||
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
    if (ret < 0 || block_job_is_cancelled(&job->common)) {
 | 
			
		||||
        /* Merge the successor back into the parent, delete nothing. */
 | 
			
		||||
        bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL);
 | 
			
		||||
        assert(bm);
 | 
			
		||||
@@ -281,25 +217,25 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void backup_commit(Job *job)
 | 
			
		||||
static void backup_commit(BlockJob *job)
 | 
			
		||||
{
 | 
			
		||||
    BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
 | 
			
		||||
    BackupBlockJob *s = container_of(job, BackupBlockJob, common);
 | 
			
		||||
    if (s->sync_bitmap) {
 | 
			
		||||
        backup_cleanup_sync_bitmap(s, 0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void backup_abort(Job *job)
 | 
			
		||||
static void backup_abort(BlockJob *job)
 | 
			
		||||
{
 | 
			
		||||
    BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
 | 
			
		||||
    BackupBlockJob *s = container_of(job, BackupBlockJob, common);
 | 
			
		||||
    if (s->sync_bitmap) {
 | 
			
		||||
        backup_cleanup_sync_bitmap(s, -1);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void backup_clean(Job *job)
 | 
			
		||||
static void backup_clean(BlockJob *job)
 | 
			
		||||
{
 | 
			
		||||
    BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
 | 
			
		||||
    BackupBlockJob *s = container_of(job, BackupBlockJob, common);
 | 
			
		||||
    assert(s->target);
 | 
			
		||||
    blk_unref(s->target);
 | 
			
		||||
    s->target = NULL;
 | 
			
		||||
@@ -317,7 +253,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
 | 
			
		||||
    BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
 | 
			
		||||
    int64_t len;
 | 
			
		||||
 | 
			
		||||
    assert(block_job_driver(job) == &backup_job_driver);
 | 
			
		||||
    assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP);
 | 
			
		||||
 | 
			
		||||
    if (backup_job->sync_mode != MIRROR_SYNC_MODE_NONE) {
 | 
			
		||||
        error_setg(errp, "The backup job only supports block checkpoint in"
 | 
			
		||||
@@ -325,10 +261,41 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    len = DIV_ROUND_UP(backup_job->len, backup_job->cluster_size);
 | 
			
		||||
    len = DIV_ROUND_UP(backup_job->common.len, backup_job->cluster_size);
 | 
			
		||||
    hbitmap_set(backup_job->copy_bitmap, 0, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset,
 | 
			
		||||
                                          uint64_t bytes)
 | 
			
		||||
{
 | 
			
		||||
    BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
 | 
			
		||||
    int64_t start, end;
 | 
			
		||||
 | 
			
		||||
    assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP);
 | 
			
		||||
 | 
			
		||||
    start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size);
 | 
			
		||||
    end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size);
 | 
			
		||||
    wait_for_overlapping_requests(backup_job, start, end);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void backup_cow_request_begin(CowRequest *req, BlockJob *job,
 | 
			
		||||
                              int64_t offset, uint64_t bytes)
 | 
			
		||||
{
 | 
			
		||||
    BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
 | 
			
		||||
    int64_t start, end;
 | 
			
		||||
 | 
			
		||||
    assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP);
 | 
			
		||||
 | 
			
		||||
    start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size);
 | 
			
		||||
    end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size);
 | 
			
		||||
    cow_request_begin(req, backup_job, start, end);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void backup_cow_request_end(CowRequest *req)
 | 
			
		||||
{
 | 
			
		||||
    cow_request_end(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void backup_drain(BlockJob *job)
 | 
			
		||||
{
 | 
			
		||||
    BackupBlockJob *s = container_of(job, BackupBlockJob, common);
 | 
			
		||||
@@ -356,21 +323,37 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int ret;
 | 
			
		||||
} BackupCompleteData;
 | 
			
		||||
 | 
			
		||||
static void backup_complete(BlockJob *job, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    BackupCompleteData *data = opaque;
 | 
			
		||||
 | 
			
		||||
    block_job_completed(job, data->ret);
 | 
			
		||||
    g_free(data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool coroutine_fn yield_and_check(BackupBlockJob *job)
 | 
			
		||||
{
 | 
			
		||||
    uint64_t delay_ns;
 | 
			
		||||
 | 
			
		||||
    if (job_is_cancelled(&job->common.job)) {
 | 
			
		||||
    if (block_job_is_cancelled(&job->common)) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* We need to yield even for delay_ns = 0 so that bdrv_drain_all() can
 | 
			
		||||
     * return. Without a yield, the VM would not reboot. */
 | 
			
		||||
    delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read);
 | 
			
		||||
    /* we need to yield so that bdrv_drain_all() returns.
 | 
			
		||||
     * (without, VM does not reboot)
 | 
			
		||||
     */
 | 
			
		||||
    if (job->common.speed) {
 | 
			
		||||
        uint64_t delay_ns = ratelimit_calculate_delay(&job->limit,
 | 
			
		||||
                                                      job->bytes_read);
 | 
			
		||||
        job->bytes_read = 0;
 | 
			
		||||
    job_sleep_ns(&job->common.job, delay_ns);
 | 
			
		||||
        block_job_sleep_ns(&job->common, delay_ns);
 | 
			
		||||
    } else {
 | 
			
		||||
        block_job_sleep_ns(&job->common, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (job_is_cancelled(&job->common.job)) {
 | 
			
		||||
    if (block_job_is_cancelled(&job->common)) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -422,8 +405,7 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset,
 | 
			
		||||
                                             UINT64_MAX);
 | 
			
		||||
        offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset);
 | 
			
		||||
        if (offset == -1) {
 | 
			
		||||
            hbitmap_set(job->copy_bitmap, cluster, end - cluster);
 | 
			
		||||
            break;
 | 
			
		||||
@@ -438,66 +420,64 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
 | 
			
		||||
        bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* TODO job_progress_set_remaining() would make more sense */
 | 
			
		||||
    job_progress_update(&job->common.job,
 | 
			
		||||
        job->len - hbitmap_count(job->copy_bitmap) * job->cluster_size);
 | 
			
		||||
    job->common.offset = job->common.len -
 | 
			
		||||
                         hbitmap_count(job->copy_bitmap) * job->cluster_size;
 | 
			
		||||
 | 
			
		||||
    bdrv_dirty_iter_free(dbi);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn backup_run(Job *job, Error **errp)
 | 
			
		||||
static void coroutine_fn backup_run(void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
 | 
			
		||||
    BlockDriverState *bs = blk_bs(s->common.blk);
 | 
			
		||||
    BackupBlockJob *job = opaque;
 | 
			
		||||
    BackupCompleteData *data;
 | 
			
		||||
    BlockDriverState *bs = blk_bs(job->common.blk);
 | 
			
		||||
    int64_t offset, nb_clusters;
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
 | 
			
		||||
    QLIST_INIT(&s->inflight_reqs);
 | 
			
		||||
    qemu_co_rwlock_init(&s->flush_rwlock);
 | 
			
		||||
    QLIST_INIT(&job->inflight_reqs);
 | 
			
		||||
    qemu_co_rwlock_init(&job->flush_rwlock);
 | 
			
		||||
 | 
			
		||||
    nb_clusters = DIV_ROUND_UP(s->len, s->cluster_size);
 | 
			
		||||
    job_progress_set_remaining(job, s->len);
 | 
			
		||||
 | 
			
		||||
    s->copy_bitmap = hbitmap_alloc(nb_clusters, 0);
 | 
			
		||||
    if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
 | 
			
		||||
        backup_incremental_init_copy_bitmap(s);
 | 
			
		||||
    nb_clusters = DIV_ROUND_UP(job->common.len, job->cluster_size);
 | 
			
		||||
    job->copy_bitmap = hbitmap_alloc(nb_clusters, 0);
 | 
			
		||||
    if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
 | 
			
		||||
        backup_incremental_init_copy_bitmap(job);
 | 
			
		||||
    } else {
 | 
			
		||||
        hbitmap_set(s->copy_bitmap, 0, nb_clusters);
 | 
			
		||||
        hbitmap_set(job->copy_bitmap, 0, nb_clusters);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    s->before_write.notify = backup_before_write_notify;
 | 
			
		||||
    bdrv_add_before_write_notifier(bs, &s->before_write);
 | 
			
		||||
    job->before_write.notify = backup_before_write_notify;
 | 
			
		||||
    bdrv_add_before_write_notifier(bs, &job->before_write);
 | 
			
		||||
 | 
			
		||||
    if (s->sync_mode == MIRROR_SYNC_MODE_NONE) {
 | 
			
		||||
    if (job->sync_mode == MIRROR_SYNC_MODE_NONE) {
 | 
			
		||||
        /* All bits are set in copy_bitmap to allow any cluster to be copied.
 | 
			
		||||
         * This does not actually require them to be copied. */
 | 
			
		||||
        while (!job_is_cancelled(job)) {
 | 
			
		||||
        while (!block_job_is_cancelled(&job->common)) {
 | 
			
		||||
            /* Yield until the job is cancelled.  We just let our before_write
 | 
			
		||||
             * notify callback service CoW requests. */
 | 
			
		||||
            job_yield(job);
 | 
			
		||||
            block_job_yield(&job->common);
 | 
			
		||||
        }
 | 
			
		||||
    } else if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
 | 
			
		||||
        ret = backup_run_incremental(s);
 | 
			
		||||
    } else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
 | 
			
		||||
        ret = backup_run_incremental(job);
 | 
			
		||||
    } else {
 | 
			
		||||
        /* Both FULL and TOP SYNC_MODE's require copying.. */
 | 
			
		||||
        for (offset = 0; offset < s->len;
 | 
			
		||||
             offset += s->cluster_size) {
 | 
			
		||||
        for (offset = 0; offset < job->common.len;
 | 
			
		||||
             offset += job->cluster_size) {
 | 
			
		||||
            bool error_is_read;
 | 
			
		||||
            int alloced = 0;
 | 
			
		||||
 | 
			
		||||
            if (yield_and_check(s)) {
 | 
			
		||||
            if (yield_and_check(job)) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (s->sync_mode == MIRROR_SYNC_MODE_TOP) {
 | 
			
		||||
            if (job->sync_mode == MIRROR_SYNC_MODE_TOP) {
 | 
			
		||||
                int i;
 | 
			
		||||
                int64_t n;
 | 
			
		||||
 | 
			
		||||
                /* Check to see if these blocks are already in the
 | 
			
		||||
                 * backing file. */
 | 
			
		||||
 | 
			
		||||
                for (i = 0; i < s->cluster_size;) {
 | 
			
		||||
                for (i = 0; i < job->cluster_size;) {
 | 
			
		||||
                    /* bdrv_is_allocated() only returns true/false based
 | 
			
		||||
                     * on the first set of sectors it comes across that
 | 
			
		||||
                     * are are all in the same state.
 | 
			
		||||
@@ -506,7 +486,7 @@ static int coroutine_fn backup_run(Job *job, Error **errp)
 | 
			
		||||
                     * needed but at some point that is always the case. */
 | 
			
		||||
                    alloced =
 | 
			
		||||
                        bdrv_is_allocated(bs, offset + i,
 | 
			
		||||
                                          s->cluster_size - i, &n);
 | 
			
		||||
                                          job->cluster_size - i, &n);
 | 
			
		||||
                    i += n;
 | 
			
		||||
 | 
			
		||||
                    if (alloced || n == 0) {
 | 
			
		||||
@@ -524,45 +504,43 @@ static int coroutine_fn backup_run(Job *job, Error **errp)
 | 
			
		||||
            if (alloced < 0) {
 | 
			
		||||
                ret = alloced;
 | 
			
		||||
            } else {
 | 
			
		||||
                ret = backup_do_cow(s, offset, s->cluster_size,
 | 
			
		||||
                ret = backup_do_cow(job, offset, job->cluster_size,
 | 
			
		||||
                                    &error_is_read, false);
 | 
			
		||||
            }
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
                /* Depending on error action, fail now or retry cluster */
 | 
			
		||||
                BlockErrorAction action =
 | 
			
		||||
                    backup_error_action(s, error_is_read, -ret);
 | 
			
		||||
                    backup_error_action(job, error_is_read, -ret);
 | 
			
		||||
                if (action == BLOCK_ERROR_ACTION_REPORT) {
 | 
			
		||||
                    break;
 | 
			
		||||
                } else {
 | 
			
		||||
                    offset -= s->cluster_size;
 | 
			
		||||
                    offset -= job->cluster_size;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    notifier_with_return_remove(&s->before_write);
 | 
			
		||||
    notifier_with_return_remove(&job->before_write);
 | 
			
		||||
 | 
			
		||||
    /* wait until pending backup_do_cow() calls have completed */
 | 
			
		||||
    qemu_co_rwlock_wrlock(&s->flush_rwlock);
 | 
			
		||||
    qemu_co_rwlock_unlock(&s->flush_rwlock);
 | 
			
		||||
    hbitmap_free(s->copy_bitmap);
 | 
			
		||||
    qemu_co_rwlock_wrlock(&job->flush_rwlock);
 | 
			
		||||
    qemu_co_rwlock_unlock(&job->flush_rwlock);
 | 
			
		||||
    hbitmap_free(job->copy_bitmap);
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
    data = g_malloc(sizeof(*data));
 | 
			
		||||
    data->ret = ret;
 | 
			
		||||
    block_job_defer_to_main_loop(&job->common, backup_complete, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const BlockJobDriver backup_job_driver = {
 | 
			
		||||
    .job_driver = {
 | 
			
		||||
    .instance_size          = sizeof(BackupBlockJob),
 | 
			
		||||
        .job_type               = JOB_TYPE_BACKUP,
 | 
			
		||||
        .free                   = block_job_free,
 | 
			
		||||
        .user_resume            = block_job_user_resume,
 | 
			
		||||
        .drain                  = block_job_drain,
 | 
			
		||||
        .run                    = backup_run,
 | 
			
		||||
    .job_type               = BLOCK_JOB_TYPE_BACKUP,
 | 
			
		||||
    .start                  = backup_run,
 | 
			
		||||
    .set_speed              = backup_set_speed,
 | 
			
		||||
    .commit                 = backup_commit,
 | 
			
		||||
    .abort                  = backup_abort,
 | 
			
		||||
    .clean                  = backup_clean,
 | 
			
		||||
    },
 | 
			
		||||
    .attached_aio_context   = backup_attached_aio_context,
 | 
			
		||||
    .drain                  = backup_drain,
 | 
			
		||||
};
 | 
			
		||||
@@ -575,7 +553,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
 | 
			
		||||
                  BlockdevOnError on_target_error,
 | 
			
		||||
                  int creation_flags,
 | 
			
		||||
                  BlockCompletionFunc *cb, void *opaque,
 | 
			
		||||
                  JobTxn *txn, Error **errp)
 | 
			
		||||
                  BlockJobTxn *txn, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    int64_t len;
 | 
			
		||||
    BlockDriverInfo bdi;
 | 
			
		||||
@@ -642,8 +620,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
 | 
			
		||||
        goto error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* job->len is fixed, so we can't allow resize */
 | 
			
		||||
    job = block_job_create(job_id, &backup_job_driver, txn, bs,
 | 
			
		||||
    /* job->common.len is fixed, so we can't allow resize */
 | 
			
		||||
    job = block_job_create(job_id, &backup_job_driver, bs,
 | 
			
		||||
                           BLK_PERM_CONSISTENT_READ,
 | 
			
		||||
                           BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
 | 
			
		||||
                           BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD,
 | 
			
		||||
@@ -668,9 +646,6 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
 | 
			
		||||
                       sync_bitmap : NULL;
 | 
			
		||||
    job->compress = compress;
 | 
			
		||||
 | 
			
		||||
    /* Detect image-fleecing (and similar) schemes */
 | 
			
		||||
    job->serialize_target_writes = bdrv_chain_contains(target, bs);
 | 
			
		||||
 | 
			
		||||
    /* If there is no backing file on the target, we cannot rely on COW if our
 | 
			
		||||
     * backup cluster size is smaller than the target cluster size. Even for
 | 
			
		||||
     * targets with a backing file, try to avoid COW if possible. */
 | 
			
		||||
@@ -697,17 +672,12 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
 | 
			
		||||
    } else {
 | 
			
		||||
        job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
 | 
			
		||||
    }
 | 
			
		||||
    job->use_copy_range = true;
 | 
			
		||||
    job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk),
 | 
			
		||||
                                        blk_get_max_transfer(job->target));
 | 
			
		||||
    job->copy_range_size = MAX(job->cluster_size,
 | 
			
		||||
                               QEMU_ALIGN_UP(job->copy_range_size,
 | 
			
		||||
                                             job->cluster_size));
 | 
			
		||||
 | 
			
		||||
    /* Required permissions are already taken with target's blk_new() */
 | 
			
		||||
    block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
 | 
			
		||||
                       &error_abort);
 | 
			
		||||
    job->len = len;
 | 
			
		||||
    job->common.len = len;
 | 
			
		||||
    block_job_txn_add_job(txn, &job->common);
 | 
			
		||||
 | 
			
		||||
    return &job->common;
 | 
			
		||||
 | 
			
		||||
@@ -716,8 +686,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
 | 
			
		||||
        bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL);
 | 
			
		||||
    }
 | 
			
		||||
    if (job) {
 | 
			
		||||
        backup_clean(&job->common.job);
 | 
			
		||||
        job_early_fail(&job->common.job);
 | 
			
		||||
        backup_clean(&job->common);
 | 
			
		||||
        block_job_early_fail(&job->common);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return NULL;
 | 
			
		||||
 
 | 
			
		||||
@@ -305,7 +305,7 @@ static void blkdebug_parse_filename(const char *filename, QDict *options,
 | 
			
		||||
 | 
			
		||||
    if (c != filename) {
 | 
			
		||||
        QString *config_path;
 | 
			
		||||
        config_path = qstring_from_substr(filename, 0, c - filename);
 | 
			
		||||
        config_path = qstring_from_substr(filename, 0, c - filename - 1);
 | 
			
		||||
        qdict_put(options, "config", config_path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -398,11 +398,10 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
 | 
			
		||||
        (BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
 | 
			
		||||
    bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
 | 
			
		||||
        ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
 | 
			
		||||
            bs->file->bs->supported_zero_flags);
 | 
			
		||||
    bs->supported_write_flags = BDRV_REQ_FUA &
 | 
			
		||||
        bs->file->bs->supported_write_flags;
 | 
			
		||||
    bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
 | 
			
		||||
        bs->file->bs->supported_zero_flags;
 | 
			
		||||
    ret = -EINVAL;
 | 
			
		||||
 | 
			
		||||
    /* Set alignment overrides */
 | 
			
		||||
@@ -625,7 +624,7 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
 | 
			
		||||
        return err;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return bdrv_co_pdiscard(bs->file, offset, bytes);
 | 
			
		||||
    return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs,
 | 
			
		||||
@@ -846,12 +845,13 @@ static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
 | 
			
		||||
    opts = qdict_new();
 | 
			
		||||
    qdict_put_str(opts, "driver", "blkdebug");
 | 
			
		||||
 | 
			
		||||
    qdict_put(opts, "image", qobject_ref(bs->file->bs->full_open_options));
 | 
			
		||||
    QINCREF(bs->file->bs->full_open_options);
 | 
			
		||||
    qdict_put(opts, "image", bs->file->bs->full_open_options);
 | 
			
		||||
 | 
			
		||||
    for (e = qdict_first(options); e; e = qdict_next(options, e)) {
 | 
			
		||||
        if (strcmp(qdict_entry_key(e), "x-image")) {
 | 
			
		||||
            qdict_put_obj(opts, qdict_entry_key(e),
 | 
			
		||||
                          qobject_ref(qdict_entry_value(e)));
 | 
			
		||||
            qobject_incref(qdict_entry_value(e));
 | 
			
		||||
            qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,549 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Write logging blk driver based on blkverify and blkdebug.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2017 Tuomas Tynkkynen <tuomas@tuxera.com>
 | 
			
		||||
 * Copyright (c) 2018 Aapo Vienamo <aapo@tuxera.com>
 | 
			
		||||
 * Copyright (c) 2018 Ari Sundholm <ari@tuxera.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 | 
			
		||||
 * See the COPYING file in the top-level directory.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
#include "qemu/sockets.h" /* for EINPROGRESS on Windows */
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
#include "qapi/qmp/qdict.h"
 | 
			
		||||
#include "qapi/qmp/qstring.h"
 | 
			
		||||
#include "qemu/cutils.h"
 | 
			
		||||
#include "qemu/option.h"
 | 
			
		||||
 | 
			
		||||
/* Disk format stuff - taken from Linux drivers/md/dm-log-writes.c */
 | 
			
		||||
 | 
			
		||||
#define LOG_FLUSH_FLAG   (1 << 0)
 | 
			
		||||
#define LOG_FUA_FLAG     (1 << 1)
 | 
			
		||||
#define LOG_DISCARD_FLAG (1 << 2)
 | 
			
		||||
#define LOG_MARK_FLAG    (1 << 3)
 | 
			
		||||
#define LOG_FLAG_MASK    (LOG_FLUSH_FLAG \
 | 
			
		||||
                         | LOG_FUA_FLAG \
 | 
			
		||||
                         | LOG_DISCARD_FLAG \
 | 
			
		||||
                         | LOG_MARK_FLAG)
 | 
			
		||||
 | 
			
		||||
#define WRITE_LOG_VERSION 1ULL
 | 
			
		||||
#define WRITE_LOG_MAGIC 0x6a736677736872ULL
 | 
			
		||||
 | 
			
		||||
/* All fields are little-endian. */
 | 
			
		||||
struct log_write_super {
 | 
			
		||||
    uint64_t magic;
 | 
			
		||||
    uint64_t version;
 | 
			
		||||
    uint64_t nr_entries;
 | 
			
		||||
    uint32_t sectorsize;
 | 
			
		||||
} QEMU_PACKED;
 | 
			
		||||
 | 
			
		||||
struct log_write_entry {
 | 
			
		||||
    uint64_t sector;
 | 
			
		||||
    uint64_t nr_sectors;
 | 
			
		||||
    uint64_t flags;
 | 
			
		||||
    uint64_t data_len;
 | 
			
		||||
} QEMU_PACKED;
 | 
			
		||||
 | 
			
		||||
/* End of disk format structures. */
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    BdrvChild *log_file;
 | 
			
		||||
    uint32_t sectorsize;
 | 
			
		||||
    uint32_t sectorbits;
 | 
			
		||||
    uint64_t cur_log_sector;
 | 
			
		||||
    uint64_t nr_entries;
 | 
			
		||||
    uint64_t update_interval;
 | 
			
		||||
} BDRVBlkLogWritesState;
 | 
			
		||||
 | 
			
		||||
static QemuOptsList runtime_opts = {
 | 
			
		||||
    .name = "blklogwrites",
 | 
			
		||||
    .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
 | 
			
		||||
    .desc = {
 | 
			
		||||
        {
 | 
			
		||||
            .name = "log-append",
 | 
			
		||||
            .type = QEMU_OPT_BOOL,
 | 
			
		||||
            .help = "Append to an existing log",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = "log-sector-size",
 | 
			
		||||
            .type = QEMU_OPT_SIZE,
 | 
			
		||||
            .help = "Log sector size",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = "log-super-update-interval",
 | 
			
		||||
            .type = QEMU_OPT_NUMBER,
 | 
			
		||||
            .help = "Log superblock update interval (# of write requests)",
 | 
			
		||||
        },
 | 
			
		||||
        { /* end of list */ }
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline uint32_t blk_log_writes_log2(uint32_t value)
 | 
			
		||||
{
 | 
			
		||||
    assert(value > 0);
 | 
			
		||||
    return 31 - clz32(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool blk_log_writes_sector_size_valid(uint32_t sector_size)
 | 
			
		||||
{
 | 
			
		||||
    return is_power_of_2(sector_size) &&
 | 
			
		||||
        sector_size >= sizeof(struct log_write_super) &&
 | 
			
		||||
        sector_size >= sizeof(struct log_write_entry) &&
 | 
			
		||||
        sector_size < (1ull << 24);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint64_t blk_log_writes_find_cur_log_sector(BdrvChild *log,
 | 
			
		||||
                                                   uint32_t sector_size,
 | 
			
		||||
                                                   uint64_t nr_entries,
 | 
			
		||||
                                                   Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    uint64_t cur_sector = 1;
 | 
			
		||||
    uint64_t cur_idx = 0;
 | 
			
		||||
    uint32_t sector_bits = blk_log_writes_log2(sector_size);
 | 
			
		||||
    struct log_write_entry cur_entry;
 | 
			
		||||
 | 
			
		||||
    while (cur_idx < nr_entries) {
 | 
			
		||||
        int read_ret = bdrv_pread(log, cur_sector << sector_bits, &cur_entry,
 | 
			
		||||
                                  sizeof(cur_entry));
 | 
			
		||||
        if (read_ret < 0) {
 | 
			
		||||
            error_setg_errno(errp, -read_ret,
 | 
			
		||||
                             "Failed to read log entry %"PRIu64, cur_idx);
 | 
			
		||||
            return (uint64_t)-1ull;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (cur_entry.flags & ~cpu_to_le64(LOG_FLAG_MASK)) {
 | 
			
		||||
            error_setg(errp, "Invalid flags 0x%"PRIx64" in log entry %"PRIu64,
 | 
			
		||||
                       le64_to_cpu(cur_entry.flags), cur_idx);
 | 
			
		||||
            return (uint64_t)-1ull;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Account for the sector of the entry itself */
 | 
			
		||||
        ++cur_sector;
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * Account for the data of the write.
 | 
			
		||||
         * For discards, this data is not present.
 | 
			
		||||
         */
 | 
			
		||||
        if (!(cur_entry.flags & cpu_to_le64(LOG_DISCARD_FLAG))) {
 | 
			
		||||
            cur_sector += le64_to_cpu(cur_entry.nr_sectors);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ++cur_idx;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return cur_sector;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
                               Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BDRVBlkLogWritesState *s = bs->opaque;
 | 
			
		||||
    QemuOpts *opts;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    int ret;
 | 
			
		||||
    uint64_t log_sector_size;
 | 
			
		||||
    bool log_append;
 | 
			
		||||
 | 
			
		||||
    opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
 | 
			
		||||
    qemu_opts_absorb_qdict(opts, options, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Open the file */
 | 
			
		||||
    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false,
 | 
			
		||||
                               &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Open the log file */
 | 
			
		||||
    s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_file, false,
 | 
			
		||||
                                  &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    log_append = qemu_opt_get_bool(opts, "log-append", false);
 | 
			
		||||
 | 
			
		||||
    if (log_append) {
 | 
			
		||||
        struct log_write_super log_sb = { 0, 0, 0, 0 };
 | 
			
		||||
 | 
			
		||||
        if (qemu_opt_find(opts, "log-sector-size")) {
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
            error_setg(errp, "log-append and log-sector-size are mutually "
 | 
			
		||||
                       "exclusive");
 | 
			
		||||
            goto fail_log;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Read log superblock or fake one for an empty log */
 | 
			
		||||
        if (!bdrv_getlength(s->log_file->bs)) {
 | 
			
		||||
            log_sb.magic      = cpu_to_le64(WRITE_LOG_MAGIC);
 | 
			
		||||
            log_sb.version    = cpu_to_le64(WRITE_LOG_VERSION);
 | 
			
		||||
            log_sb.nr_entries = cpu_to_le64(0);
 | 
			
		||||
            log_sb.sectorsize = cpu_to_le32(BDRV_SECTOR_SIZE);
 | 
			
		||||
        } else {
 | 
			
		||||
            ret = bdrv_pread(s->log_file, 0, &log_sb, sizeof(log_sb));
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
                error_setg_errno(errp, -ret, "Could not read log superblock");
 | 
			
		||||
                goto fail_log;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (log_sb.magic != cpu_to_le64(WRITE_LOG_MAGIC)) {
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
            error_setg(errp, "Invalid log superblock magic");
 | 
			
		||||
            goto fail_log;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (log_sb.version != cpu_to_le64(WRITE_LOG_VERSION)) {
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
            error_setg(errp, "Unsupported log version %"PRIu64,
 | 
			
		||||
                       le64_to_cpu(log_sb.version));
 | 
			
		||||
            goto fail_log;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        log_sector_size = le32_to_cpu(log_sb.sectorsize);
 | 
			
		||||
        s->cur_log_sector = 1;
 | 
			
		||||
        s->nr_entries = 0;
 | 
			
		||||
 | 
			
		||||
        if (blk_log_writes_sector_size_valid(log_sector_size)) {
 | 
			
		||||
            s->cur_log_sector =
 | 
			
		||||
                blk_log_writes_find_cur_log_sector(s->log_file, log_sector_size,
 | 
			
		||||
                                    le64_to_cpu(log_sb.nr_entries), &local_err);
 | 
			
		||||
            if (local_err) {
 | 
			
		||||
                ret = -EINVAL;
 | 
			
		||||
                error_propagate(errp, local_err);
 | 
			
		||||
                goto fail_log;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            s->nr_entries = le64_to_cpu(log_sb.nr_entries);
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        log_sector_size = qemu_opt_get_size(opts, "log-sector-size",
 | 
			
		||||
                                            BDRV_SECTOR_SIZE);
 | 
			
		||||
        s->cur_log_sector = 1;
 | 
			
		||||
        s->nr_entries = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!blk_log_writes_sector_size_valid(log_sector_size)) {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        error_setg(errp, "Invalid log sector size %"PRIu64, log_sector_size);
 | 
			
		||||
        goto fail_log;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->sectorsize = log_sector_size;
 | 
			
		||||
    s->sectorbits = blk_log_writes_log2(log_sector_size);
 | 
			
		||||
    s->update_interval = qemu_opt_get_number(opts, "log-super-update-interval",
 | 
			
		||||
                                             4096);
 | 
			
		||||
    if (!s->update_interval) {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        error_setg(errp, "Invalid log superblock update interval %"PRIu64,
 | 
			
		||||
                   s->update_interval);
 | 
			
		||||
        goto fail_log;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = 0;
 | 
			
		||||
fail_log:
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        bdrv_unref_child(bs, s->log_file);
 | 
			
		||||
        s->log_file = NULL;
 | 
			
		||||
    }
 | 
			
		||||
fail:
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        bdrv_unref_child(bs, bs->file);
 | 
			
		||||
        bs->file = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    qemu_opts_del(opts);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void blk_log_writes_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVBlkLogWritesState *s = bs->opaque;
 | 
			
		||||
 | 
			
		||||
    bdrv_unref_child(bs, s->log_file);
 | 
			
		||||
    s->log_file = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int64_t blk_log_writes_getlength(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_getlength(bs->file->bs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void blk_log_writes_refresh_filename(BlockDriverState *bs,
 | 
			
		||||
                                            QDict *options)
 | 
			
		||||
{
 | 
			
		||||
    BDRVBlkLogWritesState *s = bs->opaque;
 | 
			
		||||
 | 
			
		||||
    /* bs->file->bs has already been refreshed */
 | 
			
		||||
    bdrv_refresh_filename(s->log_file->bs);
 | 
			
		||||
 | 
			
		||||
    if (bs->file->bs->full_open_options
 | 
			
		||||
        && s->log_file->bs->full_open_options)
 | 
			
		||||
    {
 | 
			
		||||
        QDict *opts = qdict_new();
 | 
			
		||||
        qdict_put_str(opts, "driver", "blklogwrites");
 | 
			
		||||
 | 
			
		||||
        qobject_ref(bs->file->bs->full_open_options);
 | 
			
		||||
        qdict_put(opts, "file", bs->file->bs->full_open_options);
 | 
			
		||||
        qobject_ref(s->log_file->bs->full_open_options);
 | 
			
		||||
        qdict_put(opts, "log", s->log_file->bs->full_open_options);
 | 
			
		||||
        qdict_put_int(opts, "log-sector-size", s->sectorsize);
 | 
			
		||||
 | 
			
		||||
        bs->full_open_options = opts;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c,
 | 
			
		||||
                                      const BdrvChildRole *role,
 | 
			
		||||
                                      BlockReopenQueue *ro_q,
 | 
			
		||||
                                      uint64_t perm, uint64_t shrd,
 | 
			
		||||
                                      uint64_t *nperm, uint64_t *nshrd)
 | 
			
		||||
{
 | 
			
		||||
    if (!c) {
 | 
			
		||||
        *nperm = perm & DEFAULT_PERM_PASSTHROUGH;
 | 
			
		||||
        *nshrd = (shrd & DEFAULT_PERM_PASSTHROUGH) | DEFAULT_PERM_UNCHANGED;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!strcmp(c->name, "log")) {
 | 
			
		||||
        bdrv_format_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd);
 | 
			
		||||
    } else {
 | 
			
		||||
        bdrv_filter_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void blk_log_writes_refresh_limits(BlockDriverState *bs, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BDRVBlkLogWritesState *s = bs->opaque;
 | 
			
		||||
    bs->bl.request_alignment = s->sectorsize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn
 | 
			
		||||
blk_log_writes_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
 | 
			
		||||
                         QEMUIOVector *qiov, int flags)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef struct BlkLogWritesFileReq {
 | 
			
		||||
    BlockDriverState *bs;
 | 
			
		||||
    uint64_t offset;
 | 
			
		||||
    uint64_t bytes;
 | 
			
		||||
    int file_flags;
 | 
			
		||||
    QEMUIOVector *qiov;
 | 
			
		||||
    int (*func)(struct BlkLogWritesFileReq *r);
 | 
			
		||||
    int file_ret;
 | 
			
		||||
} BlkLogWritesFileReq;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    BlockDriverState *bs;
 | 
			
		||||
    QEMUIOVector *qiov;
 | 
			
		||||
    struct log_write_entry entry;
 | 
			
		||||
    uint64_t zero_size;
 | 
			
		||||
    int log_ret;
 | 
			
		||||
} BlkLogWritesLogReq;
 | 
			
		||||
 | 
			
		||||
static void coroutine_fn blk_log_writes_co_do_log(BlkLogWritesLogReq *lr)
 | 
			
		||||
{
 | 
			
		||||
    BDRVBlkLogWritesState *s = lr->bs->opaque;
 | 
			
		||||
    uint64_t cur_log_offset = s->cur_log_sector << s->sectorbits;
 | 
			
		||||
 | 
			
		||||
    s->nr_entries++;
 | 
			
		||||
    s->cur_log_sector +=
 | 
			
		||||
            ROUND_UP(lr->qiov->size, s->sectorsize) >> s->sectorbits;
 | 
			
		||||
 | 
			
		||||
    lr->log_ret = bdrv_co_pwritev(s->log_file, cur_log_offset, lr->qiov->size,
 | 
			
		||||
                                  lr->qiov, 0);
 | 
			
		||||
 | 
			
		||||
    /* Logging for the "write zeroes" operation */
 | 
			
		||||
    if (lr->log_ret == 0 && lr->zero_size) {
 | 
			
		||||
        cur_log_offset = s->cur_log_sector << s->sectorbits;
 | 
			
		||||
        s->cur_log_sector +=
 | 
			
		||||
                ROUND_UP(lr->zero_size, s->sectorsize) >> s->sectorbits;
 | 
			
		||||
 | 
			
		||||
        lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, cur_log_offset,
 | 
			
		||||
                                            lr->zero_size, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Update super block on flush or every update interval */
 | 
			
		||||
    if (lr->log_ret == 0 && ((lr->entry.flags & LOG_FLUSH_FLAG)
 | 
			
		||||
        || (s->nr_entries % s->update_interval == 0)))
 | 
			
		||||
    {
 | 
			
		||||
        struct log_write_super super = {
 | 
			
		||||
            .magic      = cpu_to_le64(WRITE_LOG_MAGIC),
 | 
			
		||||
            .version    = cpu_to_le64(WRITE_LOG_VERSION),
 | 
			
		||||
            .nr_entries = cpu_to_le64(s->nr_entries),
 | 
			
		||||
            .sectorsize = cpu_to_le32(s->sectorsize),
 | 
			
		||||
        };
 | 
			
		||||
        void *zeroes = g_malloc0(s->sectorsize - sizeof(super));
 | 
			
		||||
        QEMUIOVector qiov;
 | 
			
		||||
 | 
			
		||||
        qemu_iovec_init(&qiov, 2);
 | 
			
		||||
        qemu_iovec_add(&qiov, &super, sizeof(super));
 | 
			
		||||
        qemu_iovec_add(&qiov, zeroes, s->sectorsize - sizeof(super));
 | 
			
		||||
 | 
			
		||||
        lr->log_ret =
 | 
			
		||||
            bdrv_co_pwritev(s->log_file, 0, s->sectorsize, &qiov, 0);
 | 
			
		||||
        if (lr->log_ret == 0) {
 | 
			
		||||
            lr->log_ret = bdrv_co_flush(s->log_file->bs);
 | 
			
		||||
        }
 | 
			
		||||
        qemu_iovec_destroy(&qiov);
 | 
			
		||||
        g_free(zeroes);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void coroutine_fn blk_log_writes_co_do_file(BlkLogWritesFileReq *fr)
 | 
			
		||||
{
 | 
			
		||||
    fr->file_ret = fr->func(fr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn
 | 
			
		||||
blk_log_writes_co_log(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
 | 
			
		||||
                      QEMUIOVector *qiov, int flags,
 | 
			
		||||
                      int (*file_func)(BlkLogWritesFileReq *r),
 | 
			
		||||
                      uint64_t entry_flags, bool is_zero_write)
 | 
			
		||||
{
 | 
			
		||||
    QEMUIOVector log_qiov;
 | 
			
		||||
    size_t niov = qiov ? qiov->niov : 0;
 | 
			
		||||
    BDRVBlkLogWritesState *s = bs->opaque;
 | 
			
		||||
    BlkLogWritesFileReq fr = {
 | 
			
		||||
        .bs         = bs,
 | 
			
		||||
        .offset     = offset,
 | 
			
		||||
        .bytes      = bytes,
 | 
			
		||||
        .file_flags = flags,
 | 
			
		||||
        .qiov       = qiov,
 | 
			
		||||
        .func       = file_func,
 | 
			
		||||
    };
 | 
			
		||||
    BlkLogWritesLogReq lr = {
 | 
			
		||||
        .bs             = bs,
 | 
			
		||||
        .qiov           = &log_qiov,
 | 
			
		||||
        .entry = {
 | 
			
		||||
            .sector     = cpu_to_le64(offset >> s->sectorbits),
 | 
			
		||||
            .nr_sectors = cpu_to_le64(bytes >> s->sectorbits),
 | 
			
		||||
            .flags      = cpu_to_le64(entry_flags),
 | 
			
		||||
            .data_len   = 0,
 | 
			
		||||
        },
 | 
			
		||||
        .zero_size = is_zero_write ? bytes : 0,
 | 
			
		||||
    };
 | 
			
		||||
    void *zeroes = g_malloc0(s->sectorsize - sizeof(lr.entry));
 | 
			
		||||
 | 
			
		||||
    assert((1 << s->sectorbits) == s->sectorsize);
 | 
			
		||||
    assert(bs->bl.request_alignment == s->sectorsize);
 | 
			
		||||
    assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
 | 
			
		||||
    assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
 | 
			
		||||
 | 
			
		||||
    qemu_iovec_init(&log_qiov, niov + 2);
 | 
			
		||||
    qemu_iovec_add(&log_qiov, &lr.entry, sizeof(lr.entry));
 | 
			
		||||
    qemu_iovec_add(&log_qiov, zeroes, s->sectorsize - sizeof(lr.entry));
 | 
			
		||||
    if (qiov) {
 | 
			
		||||
        qemu_iovec_concat(&log_qiov, qiov, 0, qiov->size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    blk_log_writes_co_do_file(&fr);
 | 
			
		||||
    blk_log_writes_co_do_log(&lr);
 | 
			
		||||
 | 
			
		||||
    qemu_iovec_destroy(&log_qiov);
 | 
			
		||||
    g_free(zeroes);
 | 
			
		||||
 | 
			
		||||
    if (lr.log_ret < 0) {
 | 
			
		||||
        return lr.log_ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return fr.file_ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn
 | 
			
		||||
blk_log_writes_co_do_file_pwritev(BlkLogWritesFileReq *fr)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_co_pwritev(fr->bs->file, fr->offset, fr->bytes,
 | 
			
		||||
                           fr->qiov, fr->file_flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn
 | 
			
		||||
blk_log_writes_co_do_file_pwrite_zeroes(BlkLogWritesFileReq *fr)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_co_pwrite_zeroes(fr->bs->file, fr->offset, fr->bytes,
 | 
			
		||||
                                 fr->file_flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn blk_log_writes_co_do_file_flush(BlkLogWritesFileReq *fr)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_co_flush(fr->bs->file->bs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn
 | 
			
		||||
blk_log_writes_co_do_file_pdiscard(BlkLogWritesFileReq *fr)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_co_pdiscard(fr->bs->file, fr->offset, fr->bytes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn
 | 
			
		||||
blk_log_writes_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
 | 
			
		||||
                          QEMUIOVector *qiov, int flags)
 | 
			
		||||
{
 | 
			
		||||
    return blk_log_writes_co_log(bs, offset, bytes, qiov, flags,
 | 
			
		||||
                                 blk_log_writes_co_do_file_pwritev, 0, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn
 | 
			
		||||
blk_log_writes_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes,
 | 
			
		||||
                                BdrvRequestFlags flags)
 | 
			
		||||
{
 | 
			
		||||
    return blk_log_writes_co_log(bs, offset, bytes, NULL, flags,
 | 
			
		||||
                                 blk_log_writes_co_do_file_pwrite_zeroes, 0,
 | 
			
		||||
                                 true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn blk_log_writes_co_flush_to_disk(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    return blk_log_writes_co_log(bs, 0, 0, NULL, 0,
 | 
			
		||||
                                 blk_log_writes_co_do_file_flush,
 | 
			
		||||
                                 LOG_FLUSH_FLAG, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn
 | 
			
		||||
blk_log_writes_co_pdiscard(BlockDriverState *bs, int64_t offset, int count)
 | 
			
		||||
{
 | 
			
		||||
    return blk_log_writes_co_log(bs, offset, count, NULL, 0,
 | 
			
		||||
                                 blk_log_writes_co_do_file_pdiscard,
 | 
			
		||||
                                 LOG_DISCARD_FLAG, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_blk_log_writes = {
 | 
			
		||||
    .format_name            = "blklogwrites",
 | 
			
		||||
    .instance_size          = sizeof(BDRVBlkLogWritesState),
 | 
			
		||||
 | 
			
		||||
    .bdrv_open              = blk_log_writes_open,
 | 
			
		||||
    .bdrv_close             = blk_log_writes_close,
 | 
			
		||||
    .bdrv_getlength         = blk_log_writes_getlength,
 | 
			
		||||
    .bdrv_refresh_filename  = blk_log_writes_refresh_filename,
 | 
			
		||||
    .bdrv_child_perm        = blk_log_writes_child_perm,
 | 
			
		||||
    .bdrv_refresh_limits    = blk_log_writes_refresh_limits,
 | 
			
		||||
 | 
			
		||||
    .bdrv_co_preadv         = blk_log_writes_co_preadv,
 | 
			
		||||
    .bdrv_co_pwritev        = blk_log_writes_co_pwritev,
 | 
			
		||||
    .bdrv_co_pwrite_zeroes  = blk_log_writes_co_pwrite_zeroes,
 | 
			
		||||
    .bdrv_co_flush_to_disk  = blk_log_writes_co_flush_to_disk,
 | 
			
		||||
    .bdrv_co_pdiscard       = blk_log_writes_co_pdiscard,
 | 
			
		||||
    .bdrv_co_block_status   = bdrv_co_block_status_from_file,
 | 
			
		||||
 | 
			
		||||
    .is_filter              = true,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void bdrv_blk_log_writes_init(void)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_register(&bdrv_blk_log_writes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
block_init(bdrv_blk_log_writes_init);
 | 
			
		||||
							
								
								
									
										10
									
								
								block/blkreplay.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										10
									
								
								block/blkreplay.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							@@ -35,14 +35,15 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
 | 
			
		||||
    bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED;
 | 
			
		||||
 | 
			
		||||
    ret = 0;
 | 
			
		||||
fail:
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void blkreplay_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int64_t blkreplay_getlength(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_getlength(bs->file->bs);
 | 
			
		||||
@@ -109,7 +110,7 @@ static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs,
 | 
			
		||||
                                              int64_t offset, int bytes)
 | 
			
		||||
{
 | 
			
		||||
    uint64_t reqid = blkreplay_next_id();
 | 
			
		||||
    int ret = bdrv_co_pdiscard(bs->file, offset, bytes);
 | 
			
		||||
    int ret = bdrv_co_pdiscard(bs->file->bs, offset, bytes);
 | 
			
		||||
    block_request_create(reqid, bs, qemu_coroutine_self());
 | 
			
		||||
    qemu_coroutine_yield();
 | 
			
		||||
 | 
			
		||||
@@ -131,6 +132,7 @@ static BlockDriver bdrv_blkreplay = {
 | 
			
		||||
    .instance_size          = 0,
 | 
			
		||||
 | 
			
		||||
    .bdrv_open              = blkreplay_open,
 | 
			
		||||
    .bdrv_close             = blkreplay_close,
 | 
			
		||||
    .bdrv_child_perm        = bdrv_filter_default_perms,
 | 
			
		||||
    .bdrv_getlength         = blkreplay_getlength,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -80,7 +80,7 @@ static void blkverify_parse_filename(const char *filename, QDict *options,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* TODO Implement option pass-through and set raw.filename here */
 | 
			
		||||
    raw_path = qstring_from_substr(filename, 0, c - filename);
 | 
			
		||||
    raw_path = qstring_from_substr(filename, 0, c - filename - 1);
 | 
			
		||||
    qdict_put(options, "x-raw", raw_path);
 | 
			
		||||
 | 
			
		||||
    /* TODO Allow multi-level nesting and set file.filename here */
 | 
			
		||||
@@ -141,9 +141,6 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
 | 
			
		||||
    bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED;
 | 
			
		||||
 | 
			
		||||
    ret = 0;
 | 
			
		||||
fail:
 | 
			
		||||
    qemu_opts_del(opts);
 | 
			
		||||
@@ -294,10 +291,10 @@ static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
 | 
			
		||||
        QDict *opts = qdict_new();
 | 
			
		||||
        qdict_put_str(opts, "driver", "blkverify");
 | 
			
		||||
 | 
			
		||||
        qdict_put(opts, "raw",
 | 
			
		||||
                  qobject_ref(bs->file->bs->full_open_options));
 | 
			
		||||
        qdict_put(opts, "test",
 | 
			
		||||
                  qobject_ref(s->test_file->bs->full_open_options));
 | 
			
		||||
        QINCREF(bs->file->bs->full_open_options);
 | 
			
		||||
        qdict_put(opts, "raw", bs->file->bs->full_open_options);
 | 
			
		||||
        QINCREF(s->test_file->bs->full_open_options);
 | 
			
		||||
        qdict_put(opts, "test", s->test_file->bs->full_open_options);
 | 
			
		||||
 | 
			
		||||
        bs->full_open_options = opts;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -31,13 +31,6 @@
 | 
			
		||||
 | 
			
		||||
static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb);
 | 
			
		||||
 | 
			
		||||
typedef struct BlockBackendAioNotifier {
 | 
			
		||||
    void (*attached_aio_context)(AioContext *new_context, void *opaque);
 | 
			
		||||
    void (*detach_aio_context)(void *opaque);
 | 
			
		||||
    void *opaque;
 | 
			
		||||
    QLIST_ENTRY(BlockBackendAioNotifier) list;
 | 
			
		||||
} BlockBackendAioNotifier;
 | 
			
		||||
 | 
			
		||||
struct BlockBackend {
 | 
			
		||||
    char *name;
 | 
			
		||||
    int refcnt;
 | 
			
		||||
@@ -47,7 +40,9 @@ struct BlockBackend {
 | 
			
		||||
    QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */
 | 
			
		||||
    BlockBackendPublic public;
 | 
			
		||||
 | 
			
		||||
    DeviceState *dev;           /* attached device model, if any */
 | 
			
		||||
    void *dev;                  /* attached device model, if any */
 | 
			
		||||
    bool legacy_dev;            /* true if dev is not a DeviceState */
 | 
			
		||||
    /* TODO change to DeviceState when all users are qdevified */
 | 
			
		||||
    const BlockDevOps *dev_ops;
 | 
			
		||||
    void *dev_opaque;
 | 
			
		||||
 | 
			
		||||
@@ -74,7 +69,6 @@ struct BlockBackend {
 | 
			
		||||
    bool allow_write_beyond_eof;
 | 
			
		||||
 | 
			
		||||
    NotifierList remove_bs_notifiers, insert_bs_notifiers;
 | 
			
		||||
    QLIST_HEAD(, BlockBackendAioNotifier) aio_notifiers;
 | 
			
		||||
 | 
			
		||||
    int quiesce_counter;
 | 
			
		||||
    VMChangeStateEntry *vmsh;
 | 
			
		||||
@@ -86,6 +80,7 @@ struct BlockBackend {
 | 
			
		||||
     * Accessed with atomic ops.
 | 
			
		||||
     */
 | 
			
		||||
    unsigned int in_flight;
 | 
			
		||||
    AioWait wait;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct BlockBackendAIOCB {
 | 
			
		||||
@@ -118,7 +113,6 @@ static void blk_root_inherit_options(int *child_flags, QDict *child_options,
 | 
			
		||||
    abort();
 | 
			
		||||
}
 | 
			
		||||
static void blk_root_drained_begin(BdrvChild *child);
 | 
			
		||||
static bool blk_root_drained_poll(BdrvChild *child);
 | 
			
		||||
static void blk_root_drained_end(BdrvChild *child);
 | 
			
		||||
 | 
			
		||||
static void blk_root_change_media(BdrvChild *child, bool load);
 | 
			
		||||
@@ -253,36 +247,6 @@ static int blk_root_inactivate(BdrvChild *child)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void blk_root_attach(BdrvChild *child)
 | 
			
		||||
{
 | 
			
		||||
    BlockBackend *blk = child->opaque;
 | 
			
		||||
    BlockBackendAioNotifier *notifier;
 | 
			
		||||
 | 
			
		||||
    trace_blk_root_attach(child, blk, child->bs);
 | 
			
		||||
 | 
			
		||||
    QLIST_FOREACH(notifier, &blk->aio_notifiers, list) {
 | 
			
		||||
        bdrv_add_aio_context_notifier(child->bs,
 | 
			
		||||
                notifier->attached_aio_context,
 | 
			
		||||
                notifier->detach_aio_context,
 | 
			
		||||
                notifier->opaque);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void blk_root_detach(BdrvChild *child)
 | 
			
		||||
{
 | 
			
		||||
    BlockBackend *blk = child->opaque;
 | 
			
		||||
    BlockBackendAioNotifier *notifier;
 | 
			
		||||
 | 
			
		||||
    trace_blk_root_detach(child, blk, child->bs);
 | 
			
		||||
 | 
			
		||||
    QLIST_FOREACH(notifier, &blk->aio_notifiers, list) {
 | 
			
		||||
        bdrv_remove_aio_context_notifier(child->bs,
 | 
			
		||||
                notifier->attached_aio_context,
 | 
			
		||||
                notifier->detach_aio_context,
 | 
			
		||||
                notifier->opaque);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const BdrvChildRole child_root = {
 | 
			
		||||
    .inherit_options    = blk_root_inherit_options,
 | 
			
		||||
 | 
			
		||||
@@ -292,14 +256,10 @@ static const BdrvChildRole child_root = {
 | 
			
		||||
    .get_parent_desc    = blk_root_get_parent_desc,
 | 
			
		||||
 | 
			
		||||
    .drained_begin      = blk_root_drained_begin,
 | 
			
		||||
    .drained_poll       = blk_root_drained_poll,
 | 
			
		||||
    .drained_end        = blk_root_drained_end,
 | 
			
		||||
 | 
			
		||||
    .activate           = blk_root_activate,
 | 
			
		||||
    .inactivate         = blk_root_inactivate,
 | 
			
		||||
 | 
			
		||||
    .attach             = blk_root_attach,
 | 
			
		||||
    .detach             = blk_root_detach,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -323,14 +283,10 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
 | 
			
		||||
    blk->shared_perm = shared_perm;
 | 
			
		||||
    blk_set_enable_write_cache(blk, true);
 | 
			
		||||
 | 
			
		||||
    blk->on_read_error = BLOCKDEV_ON_ERROR_REPORT;
 | 
			
		||||
    blk->on_write_error = BLOCKDEV_ON_ERROR_ENOSPC;
 | 
			
		||||
 | 
			
		||||
    block_acct_init(&blk->stats);
 | 
			
		||||
 | 
			
		||||
    notifier_list_init(&blk->remove_bs_notifiers);
 | 
			
		||||
    notifier_list_init(&blk->insert_bs_notifiers);
 | 
			
		||||
    QLIST_INIT(&blk->aio_notifiers);
 | 
			
		||||
 | 
			
		||||
    QTAILQ_INSERT_TAIL(&block_backends, blk, link);
 | 
			
		||||
    return blk;
 | 
			
		||||
@@ -408,7 +364,6 @@ static void blk_delete(BlockBackend *blk)
 | 
			
		||||
    }
 | 
			
		||||
    assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers));
 | 
			
		||||
    assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers));
 | 
			
		||||
    assert(QLIST_EMPTY(&blk->aio_notifiers));
 | 
			
		||||
    QTAILQ_REMOVE(&block_backends, blk, link);
 | 
			
		||||
    drive_info_del(blk->legacy_dinfo);
 | 
			
		||||
    block_acct_cleanup(&blk->stats);
 | 
			
		||||
@@ -421,6 +376,7 @@ static void drive_info_del(DriveInfo *dinfo)
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    qemu_opts_del(dinfo->opts);
 | 
			
		||||
    g_free(dinfo->serial);
 | 
			
		||||
    g_free(dinfo);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -435,7 +391,6 @@ int blk_get_refcnt(BlockBackend *blk)
 | 
			
		||||
 */
 | 
			
		||||
void blk_ref(BlockBackend *blk)
 | 
			
		||||
{
 | 
			
		||||
    assert(blk->refcnt > 0);
 | 
			
		||||
    blk->refcnt++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -448,13 +403,7 @@ void blk_unref(BlockBackend *blk)
 | 
			
		||||
{
 | 
			
		||||
    if (blk) {
 | 
			
		||||
        assert(blk->refcnt > 0);
 | 
			
		||||
        if (blk->refcnt > 1) {
 | 
			
		||||
            blk->refcnt--;
 | 
			
		||||
        } else {
 | 
			
		||||
            blk_drain(blk);
 | 
			
		||||
            /* blk_drain() cannot resurrect blk, nobody held a reference */
 | 
			
		||||
            assert(blk->refcnt == 1);
 | 
			
		||||
            blk->refcnt = 0;
 | 
			
		||||
        if (!--blk->refcnt) {
 | 
			
		||||
            blk_delete(blk);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -776,11 +725,6 @@ void blk_remove_bs(BlockBackend *blk)
 | 
			
		||||
 | 
			
		||||
    blk_update_root_state(blk);
 | 
			
		||||
 | 
			
		||||
    /* bdrv_root_unref_child() will cause blk->root to become stale and may
 | 
			
		||||
     * switch to a completion coroutine later on. Let's drain all I/O here
 | 
			
		||||
     * to avoid that and a potential QEMU crash.
 | 
			
		||||
     */
 | 
			
		||||
    blk_drain(blk);
 | 
			
		||||
    bdrv_root_unref_child(blk->root);
 | 
			
		||||
    blk->root = NULL;
 | 
			
		||||
}
 | 
			
		||||
@@ -834,11 +778,7 @@ void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm)
 | 
			
		||||
    *shared_perm = blk->shared_perm;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Attach device model @dev to @blk.
 | 
			
		||||
 * Return 0 on success, -EBUSY when a device model is attached already.
 | 
			
		||||
 */
 | 
			
		||||
int blk_attach_dev(BlockBackend *blk, DeviceState *dev)
 | 
			
		||||
static int blk_do_attach_dev(BlockBackend *blk, void *dev)
 | 
			
		||||
{
 | 
			
		||||
    if (blk->dev) {
 | 
			
		||||
        return -EBUSY;
 | 
			
		||||
@@ -853,16 +793,40 @@ int blk_attach_dev(BlockBackend *blk, DeviceState *dev)
 | 
			
		||||
 | 
			
		||||
    blk_ref(blk);
 | 
			
		||||
    blk->dev = dev;
 | 
			
		||||
    blk->legacy_dev = false;
 | 
			
		||||
    blk_iostatus_reset(blk);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Attach device model @dev to @blk.
 | 
			
		||||
 * Return 0 on success, -EBUSY when a device model is attached already.
 | 
			
		||||
 */
 | 
			
		||||
int blk_attach_dev(BlockBackend *blk, DeviceState *dev)
 | 
			
		||||
{
 | 
			
		||||
    return blk_do_attach_dev(blk, dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Attach device model @dev to @blk.
 | 
			
		||||
 * @blk must not have a device model attached already.
 | 
			
		||||
 * TODO qdevified devices don't use this, remove when devices are qdevified
 | 
			
		||||
 */
 | 
			
		||||
void blk_attach_dev_legacy(BlockBackend *blk, void *dev)
 | 
			
		||||
{
 | 
			
		||||
    if (blk_do_attach_dev(blk, dev) < 0) {
 | 
			
		||||
        abort();
 | 
			
		||||
    }
 | 
			
		||||
    blk->legacy_dev = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Detach device model @dev from @blk.
 | 
			
		||||
 * @dev must be currently attached to @blk.
 | 
			
		||||
 */
 | 
			
		||||
void blk_detach_dev(BlockBackend *blk, DeviceState *dev)
 | 
			
		||||
void blk_detach_dev(BlockBackend *blk, void *dev)
 | 
			
		||||
/* TODO change to DeviceState *dev when all users are qdevified */
 | 
			
		||||
{
 | 
			
		||||
    assert(blk->dev == dev);
 | 
			
		||||
    blk->dev = NULL;
 | 
			
		||||
@@ -876,7 +840,8 @@ void blk_detach_dev(BlockBackend *blk, DeviceState *dev)
 | 
			
		||||
/*
 | 
			
		||||
 * Return the device model attached to @blk if any, else null.
 | 
			
		||||
 */
 | 
			
		||||
DeviceState *blk_get_attached_dev(BlockBackend *blk)
 | 
			
		||||
void *blk_get_attached_dev(BlockBackend *blk)
 | 
			
		||||
/* TODO change to return DeviceState * when all users are qdevified */
 | 
			
		||||
{
 | 
			
		||||
    return blk->dev;
 | 
			
		||||
}
 | 
			
		||||
@@ -885,15 +850,17 @@ DeviceState *blk_get_attached_dev(BlockBackend *blk)
 | 
			
		||||
 * device attached to the BlockBackend. */
 | 
			
		||||
char *blk_get_attached_dev_id(BlockBackend *blk)
 | 
			
		||||
{
 | 
			
		||||
    DeviceState *dev = blk->dev;
 | 
			
		||||
    DeviceState *dev;
 | 
			
		||||
 | 
			
		||||
    assert(!blk->legacy_dev);
 | 
			
		||||
    dev = blk->dev;
 | 
			
		||||
 | 
			
		||||
    if (!dev) {
 | 
			
		||||
        return g_strdup("");
 | 
			
		||||
    } else if (dev->id) {
 | 
			
		||||
        return g_strdup(dev->id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return object_get_canonical_path(OBJECT(dev)) ?: g_strdup("");
 | 
			
		||||
    return object_get_canonical_path(OBJECT(dev));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -923,6 +890,11 @@ BlockBackend *blk_by_dev(void *dev)
 | 
			
		||||
void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops,
 | 
			
		||||
                     void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    /* All drivers that use blk_set_dev_ops() are qdevified and we want to keep
 | 
			
		||||
     * it that way, so we can assume blk->dev, if present, is a DeviceState if
 | 
			
		||||
     * blk->dev_ops is set. Non-device users may use dev_ops without device. */
 | 
			
		||||
    assert(!blk->legacy_dev);
 | 
			
		||||
 | 
			
		||||
    blk->dev_ops = ops;
 | 
			
		||||
    blk->dev_opaque = opaque;
 | 
			
		||||
 | 
			
		||||
@@ -948,6 +920,8 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp)
 | 
			
		||||
        bool tray_was_open, tray_is_open;
 | 
			
		||||
        Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
        assert(!blk->legacy_dev);
 | 
			
		||||
 | 
			
		||||
        tray_was_open = blk_dev_is_tray_open(blk);
 | 
			
		||||
        blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err);
 | 
			
		||||
        if (local_err) {
 | 
			
		||||
@@ -959,7 +933,8 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp)
 | 
			
		||||
 | 
			
		||||
        if (tray_was_open != tray_is_open) {
 | 
			
		||||
            char *id = blk_get_attached_dev_id(blk);
 | 
			
		||||
            qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open);
 | 
			
		||||
            qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open,
 | 
			
		||||
                                              &error_abort);
 | 
			
		||||
            g_free(id);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -1187,7 +1162,6 @@ static void blk_read_entry(void *opaque)
 | 
			
		||||
 | 
			
		||||
    rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size,
 | 
			
		||||
                              qiov, rwco->flags);
 | 
			
		||||
    aio_wait_kick();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void blk_write_entry(void *opaque)
 | 
			
		||||
@@ -1197,7 +1171,6 @@ static void blk_write_entry(void *opaque)
 | 
			
		||||
 | 
			
		||||
    rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size,
 | 
			
		||||
                               qiov, rwco->flags);
 | 
			
		||||
    aio_wait_kick();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
 | 
			
		||||
@@ -1270,7 +1243,7 @@ static void blk_inc_in_flight(BlockBackend *blk)
 | 
			
		||||
static void blk_dec_in_flight(BlockBackend *blk)
 | 
			
		||||
{
 | 
			
		||||
    atomic_dec(&blk->in_flight);
 | 
			
		||||
    aio_wait_kick();
 | 
			
		||||
    aio_wait_kick(&blk->wait);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void error_callback_bh(void *opaque)
 | 
			
		||||
@@ -1311,8 +1284,8 @@ static const AIOCBInfo blk_aio_em_aiocb_info = {
 | 
			
		||||
static void blk_aio_complete(BlkAioEmAIOCB *acb)
 | 
			
		||||
{
 | 
			
		||||
    if (acb->has_returned) {
 | 
			
		||||
        acb->common.cb(acb->common.opaque, acb->rwco.ret);
 | 
			
		||||
        blk_dec_in_flight(acb->rwco.blk);
 | 
			
		||||
        acb->common.cb(acb->common.opaque, acb->rwco.ret);
 | 
			
		||||
        qemu_aio_unref(acb);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1509,7 +1482,6 @@ static void blk_ioctl_entry(void *opaque)
 | 
			
		||||
 | 
			
		||||
    rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset,
 | 
			
		||||
                             qiov->iov[0].iov_base);
 | 
			
		||||
    aio_wait_kick();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
 | 
			
		||||
@@ -1540,7 +1512,7 @@ int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return bdrv_co_pdiscard(blk->root, offset, bytes);
 | 
			
		||||
    return bdrv_co_pdiscard(blk_bs(blk), offset, bytes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int blk_co_flush(BlockBackend *blk)
 | 
			
		||||
@@ -1556,7 +1528,6 @@ static void blk_flush_entry(void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    BlkRwCo *rwco = opaque;
 | 
			
		||||
    rwco->ret = blk_co_flush(rwco->blk);
 | 
			
		||||
    aio_wait_kick();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int blk_flush(BlockBackend *blk)
 | 
			
		||||
@@ -1573,7 +1544,8 @@ void blk_drain(BlockBackend *blk)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* We may have -ENOMEDIUM completions in flight */
 | 
			
		||||
    AIO_WAIT_WHILE(blk_get_aio_context(blk),
 | 
			
		||||
    AIO_WAIT_WHILE(&blk->wait,
 | 
			
		||||
            blk_get_aio_context(blk),
 | 
			
		||||
            atomic_mb_read(&blk->in_flight) > 0);
 | 
			
		||||
 | 
			
		||||
    if (bs) {
 | 
			
		||||
@@ -1593,7 +1565,8 @@ void blk_drain_all(void)
 | 
			
		||||
        aio_context_acquire(ctx);
 | 
			
		||||
 | 
			
		||||
        /* We may have -ENOMEDIUM completions in flight */
 | 
			
		||||
        AIO_WAIT_WHILE(ctx, atomic_mb_read(&blk->in_flight) > 0);
 | 
			
		||||
        AIO_WAIT_WHILE(&blk->wait, ctx,
 | 
			
		||||
                atomic_mb_read(&blk->in_flight) > 0);
 | 
			
		||||
 | 
			
		||||
        aio_context_release(ctx);
 | 
			
		||||
    }
 | 
			
		||||
@@ -1645,7 +1618,8 @@ static void send_qmp_error_event(BlockBackend *blk,
 | 
			
		||||
    qapi_event_send_block_io_error(blk_name(blk), !!bs,
 | 
			
		||||
                                   bs ? bdrv_get_node_name(bs) : NULL, optype,
 | 
			
		||||
                                   action, blk_iostatus_is_enabled(blk),
 | 
			
		||||
                                   error == ENOSPC, strerror(error));
 | 
			
		||||
                                   error == ENOSPC, strerror(error),
 | 
			
		||||
                                   &error_abort);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This is done by device models because, while the block layer knows
 | 
			
		||||
@@ -1680,7 +1654,7 @@ void blk_error_action(BlockBackend *blk, BlockErrorAction action,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool blk_is_read_only(BlockBackend *blk)
 | 
			
		||||
int blk_is_read_only(BlockBackend *blk)
 | 
			
		||||
{
 | 
			
		||||
    BlockDriverState *bs = blk_bs(blk);
 | 
			
		||||
 | 
			
		||||
@@ -1691,18 +1665,18 @@ bool blk_is_read_only(BlockBackend *blk)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool blk_is_sg(BlockBackend *blk)
 | 
			
		||||
int blk_is_sg(BlockBackend *blk)
 | 
			
		||||
{
 | 
			
		||||
    BlockDriverState *bs = blk_bs(blk);
 | 
			
		||||
 | 
			
		||||
    if (!bs) {
 | 
			
		||||
        return false;
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return bdrv_is_sg(bs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool blk_enable_write_cache(BlockBackend *blk)
 | 
			
		||||
int blk_enable_write_cache(BlockBackend *blk)
 | 
			
		||||
{
 | 
			
		||||
    return blk->enable_write_cache;
 | 
			
		||||
}
 | 
			
		||||
@@ -1750,6 +1724,9 @@ void blk_eject(BlockBackend *blk, bool eject_flag)
 | 
			
		||||
    BlockDriverState *bs = blk_bs(blk);
 | 
			
		||||
    char *id;
 | 
			
		||||
 | 
			
		||||
    /* blk_eject is only called by qdevified devices */
 | 
			
		||||
    assert(!blk->legacy_dev);
 | 
			
		||||
 | 
			
		||||
    if (bs) {
 | 
			
		||||
        bdrv_eject(bs, eject_flag);
 | 
			
		||||
    }
 | 
			
		||||
@@ -1758,7 +1735,7 @@ void blk_eject(BlockBackend *blk, bool eject_flag)
 | 
			
		||||
     * the frontend experienced a tray event. */
 | 
			
		||||
    id = blk_get_attached_dev_id(blk);
 | 
			
		||||
    qapi_event_send_device_tray_moved(blk_name(blk), id,
 | 
			
		||||
                                      eject_flag);
 | 
			
		||||
                                      eject_flag, &error_abort);
 | 
			
		||||
    g_free(id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1845,7 +1822,13 @@ void blk_op_unblock_all(BlockBackend *blk, Error *reason)
 | 
			
		||||
 | 
			
		||||
AioContext *blk_get_aio_context(BlockBackend *blk)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_get_aio_context(blk_bs(blk));
 | 
			
		||||
    BlockDriverState *bs = blk_bs(blk);
 | 
			
		||||
 | 
			
		||||
    if (bs) {
 | 
			
		||||
        return bdrv_get_aio_context(bs);
 | 
			
		||||
    } else {
 | 
			
		||||
        return qemu_get_aio_context();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb)
 | 
			
		||||
@@ -1874,15 +1857,8 @@ void blk_add_aio_context_notifier(BlockBackend *blk,
 | 
			
		||||
        void (*attached_aio_context)(AioContext *new_context, void *opaque),
 | 
			
		||||
        void (*detach_aio_context)(void *opaque), void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    BlockBackendAioNotifier *notifier;
 | 
			
		||||
    BlockDriverState *bs = blk_bs(blk);
 | 
			
		||||
 | 
			
		||||
    notifier = g_new(BlockBackendAioNotifier, 1);
 | 
			
		||||
    notifier->attached_aio_context = attached_aio_context;
 | 
			
		||||
    notifier->detach_aio_context = detach_aio_context;
 | 
			
		||||
    notifier->opaque = opaque;
 | 
			
		||||
    QLIST_INSERT_HEAD(&blk->aio_notifiers, notifier, list);
 | 
			
		||||
 | 
			
		||||
    if (bs) {
 | 
			
		||||
        bdrv_add_aio_context_notifier(bs, attached_aio_context,
 | 
			
		||||
                                      detach_aio_context, opaque);
 | 
			
		||||
@@ -1895,25 +1871,12 @@ void blk_remove_aio_context_notifier(BlockBackend *blk,
 | 
			
		||||
                                     void (*detach_aio_context)(void *),
 | 
			
		||||
                                     void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    BlockBackendAioNotifier *notifier;
 | 
			
		||||
    BlockDriverState *bs = blk_bs(blk);
 | 
			
		||||
 | 
			
		||||
    if (bs) {
 | 
			
		||||
        bdrv_remove_aio_context_notifier(bs, attached_aio_context,
 | 
			
		||||
                                         detach_aio_context, opaque);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QLIST_FOREACH(notifier, &blk->aio_notifiers, list) {
 | 
			
		||||
        if (notifier->attached_aio_context == attached_aio_context &&
 | 
			
		||||
            notifier->detach_aio_context == detach_aio_context &&
 | 
			
		||||
            notifier->opaque == opaque) {
 | 
			
		||||
            QLIST_REMOVE(notifier, list);
 | 
			
		||||
            g_free(notifier);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    abort();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify)
 | 
			
		||||
@@ -1986,7 +1949,6 @@ static void blk_pdiscard_entry(void *opaque)
 | 
			
		||||
    QEMUIOVector *qiov = rwco->iobuf;
 | 
			
		||||
 | 
			
		||||
    rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size);
 | 
			
		||||
    aio_wait_kick();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
 | 
			
		||||
@@ -2168,13 +2130,6 @@ static void blk_root_drained_begin(BdrvChild *child)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool blk_root_drained_poll(BdrvChild *child)
 | 
			
		||||
{
 | 
			
		||||
    BlockBackend *blk = child->opaque;
 | 
			
		||||
    assert(blk->quiesce_counter);
 | 
			
		||||
    return !!blk->in_flight;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void blk_root_drained_end(BdrvChild *child)
 | 
			
		||||
{
 | 
			
		||||
    BlockBackend *blk = child->opaque;
 | 
			
		||||
@@ -2199,27 +2154,3 @@ void blk_unregister_buf(BlockBackend *blk, void *host)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_unregister_buf(blk_bs(blk), host);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in,
 | 
			
		||||
                                   BlockBackend *blk_out, int64_t off_out,
 | 
			
		||||
                                   int bytes, BdrvRequestFlags read_flags,
 | 
			
		||||
                                   BdrvRequestFlags write_flags)
 | 
			
		||||
{
 | 
			
		||||
    int r;
 | 
			
		||||
    r = blk_check_byte_request(blk_in, off_in, bytes);
 | 
			
		||||
    if (r) {
 | 
			
		||||
        return r;
 | 
			
		||||
    }
 | 
			
		||||
    r = blk_check_byte_request(blk_out, off_out, bytes);
 | 
			
		||||
    if (r) {
 | 
			
		||||
        return r;
 | 
			
		||||
    }
 | 
			
		||||
    return bdrv_co_copy_range(blk_in->root, off_in,
 | 
			
		||||
                              blk_out->root, off_out,
 | 
			
		||||
                              bytes, read_flags, write_flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const BdrvChild *blk_root(BlockBackend *blk)
 | 
			
		||||
{
 | 
			
		||||
    return blk->root;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -105,18 +105,23 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    struct bochs_header bochs;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    /* No write support yet */
 | 
			
		||||
    ret = bdrv_apply_auto_read_only(bs, NULL, errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
 | 
			
		||||
                               false, errp);
 | 
			
		||||
    if (!bs->file) {
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!bdrv_is_read_only(bs)) {
 | 
			
		||||
        error_report("Opening bochs images without an explicit read-only=on "
 | 
			
		||||
                     "option is deprecated. Future versions will refuse to "
 | 
			
		||||
                     "open the image instead of automatically marking the "
 | 
			
		||||
                     "image read-only.");
 | 
			
		||||
        ret = bdrv_set_read_only(bs, true, errp); /* no write support yet */
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs));
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
 
 | 
			
		||||
@@ -67,17 +67,23 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    uint32_t offsets_size, max_compressed_block_size = 1, i;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    ret = bdrv_apply_auto_read_only(bs, NULL, errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
 | 
			
		||||
                               false, errp);
 | 
			
		||||
    if (!bs->file) {
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!bdrv_is_read_only(bs)) {
 | 
			
		||||
        error_report("Opening cloop images without an explicit read-only=on "
 | 
			
		||||
                     "option is deprecated. Future versions will refuse to "
 | 
			
		||||
                     "open the image instead of automatically marking the "
 | 
			
		||||
                     "image read-only.");
 | 
			
		||||
        ret = bdrv_set_read_only(bs, true, errp);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* read header */
 | 
			
		||||
    ret = bdrv_pread(bs->file, 128, &s->block_size, 4);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										191
									
								
								block/commit.c
									
									
									
									
									
								
							
							
						
						
									
										191
									
								
								block/commit.c
									
									
									
									
									
								
							@@ -31,14 +31,16 @@ enum {
 | 
			
		||||
    COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define SLICE_TIME 100000000ULL /* ns */
 | 
			
		||||
 | 
			
		||||
typedef struct CommitBlockJob {
 | 
			
		||||
    BlockJob common;
 | 
			
		||||
    RateLimit limit;
 | 
			
		||||
    BlockDriverState *commit_top_bs;
 | 
			
		||||
    BlockBackend *top;
 | 
			
		||||
    BlockBackend *base;
 | 
			
		||||
    BlockDriverState *base_bs;
 | 
			
		||||
    BlockdevOnError on_error;
 | 
			
		||||
    bool base_read_only;
 | 
			
		||||
    int base_flags;
 | 
			
		||||
    char *backing_file_str;
 | 
			
		||||
} CommitBlockJob;
 | 
			
		||||
 | 
			
		||||
@@ -69,93 +71,96 @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base,
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int commit_prepare(Job *job)
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int ret;
 | 
			
		||||
} CommitCompleteData;
 | 
			
		||||
 | 
			
		||||
static void commit_complete(BlockJob *job, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
 | 
			
		||||
    CommitBlockJob *s = container_of(job, CommitBlockJob, common);
 | 
			
		||||
    CommitCompleteData *data = opaque;
 | 
			
		||||
    BlockDriverState *top = blk_bs(s->top);
 | 
			
		||||
    BlockDriverState *base = blk_bs(s->base);
 | 
			
		||||
    BlockDriverState *commit_top_bs = s->commit_top_bs;
 | 
			
		||||
    int ret = data->ret;
 | 
			
		||||
    bool remove_commit_top_bs = false;
 | 
			
		||||
 | 
			
		||||
    /* Make sure commit_top_bs and top stay around until bdrv_replace_node() */
 | 
			
		||||
    bdrv_ref(top);
 | 
			
		||||
    bdrv_ref(commit_top_bs);
 | 
			
		||||
 | 
			
		||||
    /* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before
 | 
			
		||||
     * the normal backing chain can be restored. */
 | 
			
		||||
    blk_unref(s->base);
 | 
			
		||||
    s->base = NULL;
 | 
			
		||||
 | 
			
		||||
    /* FIXME: bdrv_drop_intermediate treats total failures and partial failures
 | 
			
		||||
     * identically. Further work is needed to disambiguate these cases. */
 | 
			
		||||
    return bdrv_drop_intermediate(s->commit_top_bs, s->base_bs,
 | 
			
		||||
    if (!block_job_is_cancelled(&s->common) && ret == 0) {
 | 
			
		||||
        /* success */
 | 
			
		||||
        ret = bdrv_drop_intermediate(s->commit_top_bs, base,
 | 
			
		||||
                                     s->backing_file_str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void commit_abort(Job *job)
 | 
			
		||||
{
 | 
			
		||||
    CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
 | 
			
		||||
    BlockDriverState *top_bs = blk_bs(s->top);
 | 
			
		||||
 | 
			
		||||
    /* Make sure commit_top_bs and top stay around until bdrv_replace_node() */
 | 
			
		||||
    bdrv_ref(top_bs);
 | 
			
		||||
    bdrv_ref(s->commit_top_bs);
 | 
			
		||||
 | 
			
		||||
    if (s->base) {
 | 
			
		||||
        blk_unref(s->base);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* 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
 | 
			
		||||
    } else {
 | 
			
		||||
        /* XXX Can (or should) we somehow keep 'consistent read' blocked even
 | 
			
		||||
         * after the failed/cancelled commit job is gone? If we already wrote
 | 
			
		||||
         * something to base, the intermediate images aren't valid any more. */
 | 
			
		||||
    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);
 | 
			
		||||
        remove_commit_top_bs = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* restore base open flags here if appropriate (e.g., change the base back
 | 
			
		||||
     * to r/o). These reopens do not need to be atomic, since we won't abort
 | 
			
		||||
     * even on failure here */
 | 
			
		||||
    if (s->base_read_only) {
 | 
			
		||||
        bdrv_reopen_set_read_only(s->base_bs, true, NULL);
 | 
			
		||||
    if (s->base_flags != bdrv_get_flags(base)) {
 | 
			
		||||
        bdrv_reopen(base, s->base_flags, NULL);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_free(s->backing_file_str);
 | 
			
		||||
    blk_unref(s->top);
 | 
			
		||||
 | 
			
		||||
    /* If there is more than one reference to the job (e.g. if called from
 | 
			
		||||
     * block_job_finish_sync()), block_job_completed() won't free it and
 | 
			
		||||
     * therefore the blockers on the intermediate nodes remain. This would
 | 
			
		||||
     * cause bdrv_set_backing_hd() to fail. */
 | 
			
		||||
    block_job_remove_all_bdrv(job);
 | 
			
		||||
 | 
			
		||||
    block_job_completed(&s->common, ret);
 | 
			
		||||
    g_free(data);
 | 
			
		||||
 | 
			
		||||
    /* If bdrv_drop_intermediate() didn't already do that, remove the commit
 | 
			
		||||
     * filter driver from the backing chain. Do this as the final step so that
 | 
			
		||||
     * the 'consistent read' permission can be granted.  */
 | 
			
		||||
    if (remove_commit_top_bs) {
 | 
			
		||||
        bdrv_child_try_set_perm(commit_top_bs->backing, 0, BLK_PERM_ALL,
 | 
			
		||||
                                &error_abort);
 | 
			
		||||
        bdrv_replace_node(commit_top_bs, backing_bs(commit_top_bs),
 | 
			
		||||
                          &error_abort);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bdrv_unref(commit_top_bs);
 | 
			
		||||
    bdrv_unref(top);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn commit_run(Job *job, Error **errp)
 | 
			
		||||
static void coroutine_fn commit_run(void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
 | 
			
		||||
    CommitBlockJob *s = opaque;
 | 
			
		||||
    CommitCompleteData *data;
 | 
			
		||||
    int64_t offset;
 | 
			
		||||
    uint64_t delay_ns = 0;
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
    int64_t n = 0; /* bytes */
 | 
			
		||||
    void *buf = NULL;
 | 
			
		||||
    int bytes_written = 0;
 | 
			
		||||
    int64_t len, base_len;
 | 
			
		||||
    int64_t base_len;
 | 
			
		||||
 | 
			
		||||
    ret = len = blk_getlength(s->top);
 | 
			
		||||
    if (len < 0) {
 | 
			
		||||
    ret = s->common.len = blk_getlength(s->top);
 | 
			
		||||
 | 
			
		||||
    if (s->common.len < 0) {
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
    job_progress_set_remaining(&s->common.job, len);
 | 
			
		||||
 | 
			
		||||
    ret = base_len = blk_getlength(s->base);
 | 
			
		||||
    if (base_len < 0) {
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (base_len < len) {
 | 
			
		||||
        ret = blk_truncate(s->base, len, PREALLOC_MODE_OFF, NULL);
 | 
			
		||||
    if (base_len < s->common.len) {
 | 
			
		||||
        ret = blk_truncate(s->base, s->common.len, PREALLOC_MODE_OFF, NULL);
 | 
			
		||||
        if (ret) {
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
@@ -163,14 +168,14 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
 | 
			
		||||
 | 
			
		||||
    buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE);
 | 
			
		||||
 | 
			
		||||
    for (offset = 0; offset < len; offset += n) {
 | 
			
		||||
    for (offset = 0; offset < s->common.len; offset += n) {
 | 
			
		||||
        bool copy;
 | 
			
		||||
 | 
			
		||||
        /* Note that even when no rate limit is applied we need to yield
 | 
			
		||||
         * with no pending I/O here so that bdrv_drain_all() returns.
 | 
			
		||||
         */
 | 
			
		||||
        job_sleep_ns(&s->common.job, delay_ns);
 | 
			
		||||
        if (job_is_cancelled(&s->common.job)) {
 | 
			
		||||
        block_job_sleep_ns(&s->common, delay_ns);
 | 
			
		||||
        if (block_job_is_cancelled(&s->common)) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        /* Copy if allocated above the base */
 | 
			
		||||
@@ -193,12 +198,10 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Publish progress */
 | 
			
		||||
        job_progress_update(&s->common.job, n);
 | 
			
		||||
        s->common.offset += n;
 | 
			
		||||
 | 
			
		||||
        if (copy) {
 | 
			
		||||
            delay_ns = block_job_ratelimit_get_delay(&s->common, n);
 | 
			
		||||
        } else {
 | 
			
		||||
            delay_ns = 0;
 | 
			
		||||
        if (copy && s->common.speed) {
 | 
			
		||||
            delay_ns = ratelimit_calculate_delay(&s->limit, n);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -207,21 +210,27 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
 | 
			
		||||
out:
 | 
			
		||||
    qemu_vfree(buf);
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
    data = g_malloc(sizeof(*data));
 | 
			
		||||
    data->ret = ret;
 | 
			
		||||
    block_job_defer_to_main_loop(&s->common, commit_complete, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    CommitBlockJob *s = container_of(job, CommitBlockJob, common);
 | 
			
		||||
 | 
			
		||||
    if (speed < 0) {
 | 
			
		||||
        error_setg(errp, QERR_INVALID_PARAMETER, "speed");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    ratelimit_set_speed(&s->limit, speed, SLICE_TIME);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const BlockJobDriver commit_job_driver = {
 | 
			
		||||
    .job_driver = {
 | 
			
		||||
    .instance_size = sizeof(CommitBlockJob),
 | 
			
		||||
        .job_type      = JOB_TYPE_COMMIT,
 | 
			
		||||
        .free          = block_job_free,
 | 
			
		||||
        .user_resume   = block_job_user_resume,
 | 
			
		||||
        .drain         = block_job_drain,
 | 
			
		||||
        .run           = commit_run,
 | 
			
		||||
        .prepare       = commit_prepare,
 | 
			
		||||
        .abort         = commit_abort,
 | 
			
		||||
        .clean         = commit_clean
 | 
			
		||||
    },
 | 
			
		||||
    .job_type      = BLOCK_JOB_TYPE_COMMIT,
 | 
			
		||||
    .set_speed     = commit_set_speed,
 | 
			
		||||
    .start         = commit_run,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs,
 | 
			
		||||
@@ -237,6 +246,10 @@ static void bdrv_commit_top_refresh_filename(BlockDriverState *bs, QDict *opts)
 | 
			
		||||
            bs->backing->bs->filename);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdrv_commit_top_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c,
 | 
			
		||||
                                       const BdrvChildRole *role,
 | 
			
		||||
                                       BlockReopenQueue *reopen_queue,
 | 
			
		||||
@@ -254,16 +267,17 @@ static BlockDriver bdrv_commit_top = {
 | 
			
		||||
    .bdrv_co_preadv             = bdrv_commit_top_preadv,
 | 
			
		||||
    .bdrv_co_block_status       = bdrv_co_block_status_from_backing,
 | 
			
		||||
    .bdrv_refresh_filename      = bdrv_commit_top_refresh_filename,
 | 
			
		||||
    .bdrv_close                 = bdrv_commit_top_close,
 | 
			
		||||
    .bdrv_child_perm            = bdrv_commit_top_child_perm,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void commit_start(const char *job_id, BlockDriverState *bs,
 | 
			
		||||
                  BlockDriverState *base, BlockDriverState *top,
 | 
			
		||||
                  int creation_flags, int64_t speed,
 | 
			
		||||
                  BlockDriverState *base, BlockDriverState *top, int64_t speed,
 | 
			
		||||
                  BlockdevOnError on_error, const char *backing_file_str,
 | 
			
		||||
                  const char *filter_node_name, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    CommitBlockJob *s;
 | 
			
		||||
    int orig_base_flags;
 | 
			
		||||
    BlockDriverState *iter;
 | 
			
		||||
    BlockDriverState *commit_top_bs = NULL;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
@@ -275,16 +289,18 @@ void commit_start(const char *job_id, BlockDriverState *bs,
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL,
 | 
			
		||||
                         speed, creation_flags, NULL, NULL, errp);
 | 
			
		||||
    s = block_job_create(job_id, &commit_job_driver, bs, 0, BLK_PERM_ALL,
 | 
			
		||||
                         speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
 | 
			
		||||
    if (!s) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* convert base to r/w, if necessary */
 | 
			
		||||
    s->base_read_only = bdrv_is_read_only(base);
 | 
			
		||||
    if (s->base_read_only) {
 | 
			
		||||
        if (bdrv_reopen_set_read_only(base, false, errp) != 0) {
 | 
			
		||||
    orig_base_flags = bdrv_get_flags(base);
 | 
			
		||||
    if (!(orig_base_flags & BDRV_O_RDWR)) {
 | 
			
		||||
        bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err);
 | 
			
		||||
        if (local_err != NULL) {
 | 
			
		||||
            error_propagate(errp, local_err);
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -351,7 +367,6 @@ void commit_start(const char *job_id, BlockDriverState *bs,
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    s->base_bs = base;
 | 
			
		||||
 | 
			
		||||
    /* Required permissions are already taken with block_job_add_bdrv() */
 | 
			
		||||
    s->top = blk_new(0, BLK_PERM_ALL);
 | 
			
		||||
@@ -360,11 +375,12 @@ void commit_start(const char *job_id, BlockDriverState *bs,
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->base_flags = orig_base_flags;
 | 
			
		||||
    s->backing_file_str = g_strdup(backing_file_str);
 | 
			
		||||
    s->on_error = on_error;
 | 
			
		||||
 | 
			
		||||
    trace_commit_start(bs, base, top, s);
 | 
			
		||||
    job_start(&s->common.job);
 | 
			
		||||
    block_job_start(&s->common);
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
fail:
 | 
			
		||||
@@ -377,7 +393,7 @@ fail:
 | 
			
		||||
    if (commit_top_bs) {
 | 
			
		||||
        bdrv_replace_node(commit_top_bs, top, &error_abort);
 | 
			
		||||
    }
 | 
			
		||||
    job_early_fail(&s->common.job);
 | 
			
		||||
    block_job_early_fail(&s->common);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -391,7 +407,7 @@ int bdrv_commit(BlockDriverState *bs)
 | 
			
		||||
    BlockDriverState *commit_top_bs = NULL;
 | 
			
		||||
    BlockDriver *drv = bs->drv;
 | 
			
		||||
    int64_t offset, length, backing_length;
 | 
			
		||||
    int ro;
 | 
			
		||||
    int ro, open_flags;
 | 
			
		||||
    int64_t n;
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
    uint8_t *buf = NULL;
 | 
			
		||||
@@ -410,9 +426,10 @@ int bdrv_commit(BlockDriverState *bs)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ro = bs->backing->bs->read_only;
 | 
			
		||||
    open_flags =  bs->backing->bs->open_flags;
 | 
			
		||||
 | 
			
		||||
    if (ro) {
 | 
			
		||||
        if (bdrv_reopen_set_read_only(bs->backing->bs, false, NULL)) {
 | 
			
		||||
        if (bdrv_reopen(bs->backing->bs, open_flags | BDRV_O_RDWR, NULL)) {
 | 
			
		||||
            return -EACCES;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -522,7 +539,7 @@ ro_cleanup:
 | 
			
		||||
 | 
			
		||||
    if (ro) {
 | 
			
		||||
        /* ignoring error return here */
 | 
			
		||||
        bdrv_reopen_set_read_only(bs->backing->bs, true, NULL);
 | 
			
		||||
        bdrv_reopen(bs->backing->bs, open_flags & ~BDRV_O_RDWR, NULL);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,167 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copy-on-read filter block driver
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2018 Red Hat, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * Author:
 | 
			
		||||
 *   Max Reitz <mreitz@redhat.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License as
 | 
			
		||||
 * published by the Free Software Foundation; either version 2 or
 | 
			
		||||
 * (at your option) version 3 of the License.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int cor_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
                    Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false,
 | 
			
		||||
                               errp);
 | 
			
		||||
    if (!bs->file) {
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
 | 
			
		||||
                                (BDRV_REQ_FUA &
 | 
			
		||||
                                    bs->file->bs->supported_write_flags);
 | 
			
		||||
 | 
			
		||||
    bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
 | 
			
		||||
                               ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
 | 
			
		||||
                                    bs->file->bs->supported_zero_flags);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \
 | 
			
		||||
                          | BLK_PERM_WRITE \
 | 
			
		||||
                          | BLK_PERM_RESIZE)
 | 
			
		||||
#define PERM_UNCHANGED (BLK_PERM_ALL & ~PERM_PASSTHROUGH)
 | 
			
		||||
 | 
			
		||||
static void cor_child_perm(BlockDriverState *bs, BdrvChild *c,
 | 
			
		||||
                           const BdrvChildRole *role,
 | 
			
		||||
                           BlockReopenQueue *reopen_queue,
 | 
			
		||||
                           uint64_t perm, uint64_t shared,
 | 
			
		||||
                           uint64_t *nperm, uint64_t *nshared)
 | 
			
		||||
{
 | 
			
		||||
    if (c == NULL) {
 | 
			
		||||
        *nperm = (perm & PERM_PASSTHROUGH) | BLK_PERM_WRITE_UNCHANGED;
 | 
			
		||||
        *nshared = (shared & PERM_PASSTHROUGH) | PERM_UNCHANGED;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *nperm = (perm & PERM_PASSTHROUGH) |
 | 
			
		||||
             (c->perm & PERM_UNCHANGED);
 | 
			
		||||
    *nshared = (shared & PERM_PASSTHROUGH) |
 | 
			
		||||
               (c->shared_perm & PERM_UNCHANGED);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int64_t cor_getlength(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_getlength(bs->file->bs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn cor_co_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
                                        PreallocMode prealloc, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_co_truncate(bs->file, offset, prealloc, errp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn cor_co_preadv(BlockDriverState *bs,
 | 
			
		||||
                                      uint64_t offset, uint64_t bytes,
 | 
			
		||||
                                      QEMUIOVector *qiov, int flags)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_co_preadv(bs->file, offset, bytes, qiov,
 | 
			
		||||
                          flags | BDRV_REQ_COPY_ON_READ);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn cor_co_pwritev(BlockDriverState *bs,
 | 
			
		||||
                                       uint64_t offset, uint64_t bytes,
 | 
			
		||||
                                       QEMUIOVector *qiov, int flags)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn cor_co_pwrite_zeroes(BlockDriverState *bs,
 | 
			
		||||
                                             int64_t offset, int bytes,
 | 
			
		||||
                                             BdrvRequestFlags flags)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn cor_co_pdiscard(BlockDriverState *bs,
 | 
			
		||||
                                        int64_t offset, int bytes)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_co_pdiscard(bs->file, offset, bytes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void cor_eject(BlockDriverState *bs, bool eject_flag)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_eject(bs->file->bs, eject_flag);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void cor_lock_medium(BlockDriverState *bs, bool locked)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_lock_medium(bs->file->bs, locked);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static bool cor_recurse_is_first_non_filter(BlockDriverState *bs,
 | 
			
		||||
                                            BlockDriverState *candidate)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BlockDriver bdrv_copy_on_read = {
 | 
			
		||||
    .format_name                        = "copy-on-read",
 | 
			
		||||
 | 
			
		||||
    .bdrv_open                          = cor_open,
 | 
			
		||||
    .bdrv_child_perm                    = cor_child_perm,
 | 
			
		||||
 | 
			
		||||
    .bdrv_getlength                     = cor_getlength,
 | 
			
		||||
    .bdrv_co_truncate                   = cor_co_truncate,
 | 
			
		||||
 | 
			
		||||
    .bdrv_co_preadv                     = cor_co_preadv,
 | 
			
		||||
    .bdrv_co_pwritev                    = cor_co_pwritev,
 | 
			
		||||
    .bdrv_co_pwrite_zeroes              = cor_co_pwrite_zeroes,
 | 
			
		||||
    .bdrv_co_pdiscard                   = cor_co_pdiscard,
 | 
			
		||||
 | 
			
		||||
    .bdrv_eject                         = cor_eject,
 | 
			
		||||
    .bdrv_lock_medium                   = cor_lock_medium,
 | 
			
		||||
 | 
			
		||||
    .bdrv_co_block_status               = bdrv_co_block_status_from_file,
 | 
			
		||||
 | 
			
		||||
    .bdrv_recurse_is_first_non_filter   = cor_recurse_is_first_non_filter,
 | 
			
		||||
 | 
			
		||||
    .has_variable_length                = true,
 | 
			
		||||
    .is_filter                          = true,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void bdrv_copy_on_read_init(void)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_register(&bdrv_copy_on_read);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
block_init(bdrv_copy_on_read_init);
 | 
			
		||||
@@ -24,44 +24,28 @@
 | 
			
		||||
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
#include "qemu/job.h"
 | 
			
		||||
#include "qapi/qapi-commands-block-core.h"
 | 
			
		||||
#include "qapi/qapi-visit-block-core.h"
 | 
			
		||||
#include "qapi/clone-visitor.h"
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
 | 
			
		||||
typedef struct BlockdevCreateJob {
 | 
			
		||||
    Job common;
 | 
			
		||||
typedef struct BlockdevCreateCo {
 | 
			
		||||
    BlockDriver *drv;
 | 
			
		||||
    BlockdevCreateOptions *opts;
 | 
			
		||||
} BlockdevCreateJob;
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn blockdev_create_run(Job *job, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common);
 | 
			
		||||
    int ret;
 | 
			
		||||
    Error **errp;
 | 
			
		||||
} BlockdevCreateCo;
 | 
			
		||||
 | 
			
		||||
    job_progress_set_remaining(&s->common, 1);
 | 
			
		||||
    ret = s->drv->bdrv_co_create(s->opts, errp);
 | 
			
		||||
    job_progress_update(&s->common, 1);
 | 
			
		||||
 | 
			
		||||
    qapi_free_BlockdevCreateOptions(s->opts);
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
static void coroutine_fn bdrv_co_create_co_entry(void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    BlockdevCreateCo *cco = opaque;
 | 
			
		||||
    cco->ret = cco->drv->bdrv_co_create(cco->opts, cco->errp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JobDriver blockdev_create_job_driver = {
 | 
			
		||||
    .instance_size = sizeof(BlockdevCreateJob),
 | 
			
		||||
    .job_type      = JOB_TYPE_CREATE,
 | 
			
		||||
    .run           = blockdev_create_run,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options,
 | 
			
		||||
                         Error **errp)
 | 
			
		||||
void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BlockdevCreateJob *s;
 | 
			
		||||
    const char *fmt = BlockdevDriver_str(options->driver);
 | 
			
		||||
    BlockDriver *drv = bdrv_find_format(fmt);
 | 
			
		||||
    Coroutine *co;
 | 
			
		||||
    BlockdevCreateCo cco;
 | 
			
		||||
 | 
			
		||||
    /* If the driver is in the schema, we know that it exists. But it may not
 | 
			
		||||
     * be whitelisted. */
 | 
			
		||||
@@ -71,24 +55,22 @@ void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options,
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Error out if the driver doesn't support .bdrv_co_create */
 | 
			
		||||
    /* Call callback if it exists */
 | 
			
		||||
    if (!drv->bdrv_co_create) {
 | 
			
		||||
        error_setg(errp, "Driver does not support blockdev-create");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Create the block job */
 | 
			
		||||
    /* TODO Running in the main context. Block drivers need to error out or add
 | 
			
		||||
     * locking when they use a BDS in a different AioContext. */
 | 
			
		||||
    s = job_create(job_id, &blockdev_create_job_driver, NULL,
 | 
			
		||||
                   qemu_get_aio_context(), JOB_DEFAULT | JOB_MANUAL_DISMISS,
 | 
			
		||||
                   NULL, NULL, errp);
 | 
			
		||||
    if (!s) {
 | 
			
		||||
        return;
 | 
			
		||||
    cco = (BlockdevCreateCo) {
 | 
			
		||||
        .drv = drv,
 | 
			
		||||
        .opts = options,
 | 
			
		||||
        .ret = -EINPROGRESS,
 | 
			
		||||
        .errp = errp,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    co = qemu_coroutine_create(bdrv_co_create_co_entry, &cco);
 | 
			
		||||
    qemu_coroutine_enter(co);
 | 
			
		||||
    while (cco.ret == -EINPROGRESS) {
 | 
			
		||||
        aio_poll(qemu_get_aio_context(), true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->drv = drv,
 | 
			
		||||
    s->opts = QAPI_CLONE(BlockdevCreateOptions, options),
 | 
			
		||||
 | 
			
		||||
    job_start(&s->common);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										265
									
								
								block/crypto.c
									
									
									
									
									
								
							
							
						
						
									
										265
									
								
								block/crypto.c
									
									
									
									
									
								
							@@ -21,15 +21,15 @@
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
#include "block/qdict.h"
 | 
			
		||||
#include "sysemu/block-backend.h"
 | 
			
		||||
#include "crypto/block.h"
 | 
			
		||||
#include "qapi/opts-visitor.h"
 | 
			
		||||
#include "qapi/qapi-visit-crypto.h"
 | 
			
		||||
#include "qapi/qmp/qdict.h"
 | 
			
		||||
#include "qapi/qobject-input-visitor.h"
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
#include "qemu/option.h"
 | 
			
		||||
#include "crypto.h"
 | 
			
		||||
#include "block/crypto.h"
 | 
			
		||||
 | 
			
		||||
typedef struct BlockCrypto BlockCrypto;
 | 
			
		||||
 | 
			
		||||
@@ -71,6 +71,8 @@ static ssize_t block_crypto_read_func(QCryptoBlock *block,
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct BlockCryptoCreateData {
 | 
			
		||||
    const char *filename;
 | 
			
		||||
    QemuOpts *opts;
 | 
			
		||||
    BlockBackend *blk;
 | 
			
		||||
    uint64_t size;
 | 
			
		||||
};
 | 
			
		||||
@@ -101,18 +103,27 @@ static ssize_t block_crypto_init_func(QCryptoBlock *block,
 | 
			
		||||
                                      Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    struct BlockCryptoCreateData *data = opaque;
 | 
			
		||||
 | 
			
		||||
    if (data->size > INT64_MAX || headerlen > INT64_MAX - data->size) {
 | 
			
		||||
        error_setg(errp, "The requested file size is too large");
 | 
			
		||||
        return -EFBIG;
 | 
			
		||||
    }
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    /* User provided size should reflect amount of space made
 | 
			
		||||
     * available to the guest, so we must take account of that
 | 
			
		||||
     * which will be used by the crypto header
 | 
			
		||||
     */
 | 
			
		||||
    return blk_truncate(data->blk, data->size + headerlen, PREALLOC_MODE_OFF,
 | 
			
		||||
                        errp);
 | 
			
		||||
    data->size += headerlen;
 | 
			
		||||
 | 
			
		||||
    qemu_opt_set_number(data->opts, BLOCK_OPT_SIZE, data->size, &error_abort);
 | 
			
		||||
    ret = bdrv_create_file(data->filename, data->opts, errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    data->blk = blk_new_open(data->filename, NULL, NULL,
 | 
			
		||||
                             BDRV_O_RDWR | BDRV_O_PROTOCOL, errp);
 | 
			
		||||
    if (!data->blk) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -148,36 +159,102 @@ static QemuOptsList block_crypto_create_opts_luks = {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
QCryptoBlockOpenOptions *
 | 
			
		||||
block_crypto_open_opts_init(QDict *opts, Error **errp)
 | 
			
		||||
block_crypto_open_opts_init(QCryptoBlockFormat format,
 | 
			
		||||
                            QDict *opts,
 | 
			
		||||
                            Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    Visitor *v;
 | 
			
		||||
    QCryptoBlockOpenOptions *ret;
 | 
			
		||||
    QCryptoBlockOpenOptions *ret = NULL;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
    v = qobject_input_visitor_new_flat_confused(opts, errp);
 | 
			
		||||
    if (!v) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    ret = g_new0(QCryptoBlockOpenOptions, 1);
 | 
			
		||||
    ret->format = format;
 | 
			
		||||
 | 
			
		||||
    v = qobject_input_visitor_new_keyval(QOBJECT(opts));
 | 
			
		||||
 | 
			
		||||
    visit_start_struct(v, NULL, NULL, 0, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    visit_type_QCryptoBlockOpenOptions(v, NULL, &ret, errp);
 | 
			
		||||
    switch (format) {
 | 
			
		||||
    case Q_CRYPTO_BLOCK_FORMAT_LUKS:
 | 
			
		||||
        visit_type_QCryptoBlockOptionsLUKS_members(
 | 
			
		||||
            v, &ret->u.luks, &local_err);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case Q_CRYPTO_BLOCK_FORMAT_QCOW:
 | 
			
		||||
        visit_type_QCryptoBlockOptionsQCow_members(
 | 
			
		||||
            v, &ret->u.qcow, &local_err);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        error_setg(&local_err, "Unsupported block format %d", format);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    if (!local_err) {
 | 
			
		||||
        visit_check_struct(v, &local_err);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    visit_end_struct(v, NULL);
 | 
			
		||||
 | 
			
		||||
 out:
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        qapi_free_QCryptoBlockOpenOptions(ret);
 | 
			
		||||
        ret = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    visit_free(v);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
QCryptoBlockCreateOptions *
 | 
			
		||||
block_crypto_create_opts_init(QDict *opts, Error **errp)
 | 
			
		||||
block_crypto_create_opts_init(QCryptoBlockFormat format,
 | 
			
		||||
                              QDict *opts,
 | 
			
		||||
                              Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    Visitor *v;
 | 
			
		||||
    QCryptoBlockCreateOptions *ret;
 | 
			
		||||
    QCryptoBlockCreateOptions *ret = NULL;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
    v = qobject_input_visitor_new_flat_confused(opts, errp);
 | 
			
		||||
    if (!v) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    ret = g_new0(QCryptoBlockCreateOptions, 1);
 | 
			
		||||
    ret->format = format;
 | 
			
		||||
 | 
			
		||||
    v = qobject_input_visitor_new_keyval(QOBJECT(opts));
 | 
			
		||||
 | 
			
		||||
    visit_start_struct(v, NULL, NULL, 0, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    visit_type_QCryptoBlockCreateOptions(v, NULL, &ret, errp);
 | 
			
		||||
    switch (format) {
 | 
			
		||||
    case Q_CRYPTO_BLOCK_FORMAT_LUKS:
 | 
			
		||||
        visit_type_QCryptoBlockCreateOptionsLUKS_members(
 | 
			
		||||
            v, &ret->u.luks, &local_err);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case Q_CRYPTO_BLOCK_FORMAT_QCOW:
 | 
			
		||||
        visit_type_QCryptoBlockOptionsQCow_members(
 | 
			
		||||
            v, &ret->u.qcow, &local_err);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        error_setg(&local_err, "Unsupported block format %d", format);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    if (!local_err) {
 | 
			
		||||
        visit_check_struct(v, &local_err);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    visit_end_struct(v, NULL);
 | 
			
		||||
 | 
			
		||||
 out:
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        qapi_free_QCryptoBlockCreateOptions(ret);
 | 
			
		||||
        ret = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    visit_free(v);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
@@ -215,9 +292,8 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cryptoopts = qemu_opts_to_qdict(opts, NULL);
 | 
			
		||||
    qdict_put_str(cryptoopts, "format", QCryptoBlockFormat_str(format));
 | 
			
		||||
 | 
			
		||||
    open_opts = block_crypto_open_opts_init(cryptoopts, errp);
 | 
			
		||||
    open_opts = block_crypto_open_opts_init(format, cryptoopts, errp);
 | 
			
		||||
    if (!open_opts) {
 | 
			
		||||
        goto cleanup;
 | 
			
		||||
    }
 | 
			
		||||
@@ -229,7 +305,6 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
 | 
			
		||||
                                       block_crypto_read_func,
 | 
			
		||||
                                       bs,
 | 
			
		||||
                                       cflags,
 | 
			
		||||
                                       1,
 | 
			
		||||
                                       errp);
 | 
			
		||||
 | 
			
		||||
    if (!crypto->block) {
 | 
			
		||||
@@ -241,35 +316,36 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
 | 
			
		||||
 | 
			
		||||
    ret = 0;
 | 
			
		||||
 cleanup:
 | 
			
		||||
    qobject_unref(cryptoopts);
 | 
			
		||||
    QDECREF(cryptoopts);
 | 
			
		||||
    qapi_free_QCryptoBlockOpenOptions(open_opts);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int block_crypto_co_create_generic(BlockDriverState *bs,
 | 
			
		||||
                                          int64_t size,
 | 
			
		||||
                                          QCryptoBlockCreateOptions *opts,
 | 
			
		||||
static int block_crypto_create_generic(QCryptoBlockFormat format,
 | 
			
		||||
                                       const char *filename,
 | 
			
		||||
                                       QemuOpts *opts,
 | 
			
		||||
                                       Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
    BlockBackend *blk;
 | 
			
		||||
    int ret = -EINVAL;
 | 
			
		||||
    QCryptoBlockCreateOptions *create_opts = NULL;
 | 
			
		||||
    QCryptoBlock *crypto = NULL;
 | 
			
		||||
    struct BlockCryptoCreateData data;
 | 
			
		||||
    struct BlockCryptoCreateData data = {
 | 
			
		||||
        .size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
 | 
			
		||||
                         BDRV_SECTOR_SIZE),
 | 
			
		||||
        .opts = opts,
 | 
			
		||||
        .filename = filename,
 | 
			
		||||
    };
 | 
			
		||||
    QDict *cryptoopts;
 | 
			
		||||
 | 
			
		||||
    blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
 | 
			
		||||
    cryptoopts = qemu_opts_to_qdict(opts, NULL);
 | 
			
		||||
 | 
			
		||||
    ret = blk_insert_bs(blk, bs, errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto cleanup;
 | 
			
		||||
    create_opts = block_crypto_create_opts_init(format, cryptoopts, errp);
 | 
			
		||||
    if (!create_opts) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    data = (struct BlockCryptoCreateData) {
 | 
			
		||||
        .blk = blk,
 | 
			
		||||
        .size = size,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    crypto = qcrypto_block_create(opts, NULL,
 | 
			
		||||
    crypto = qcrypto_block_create(create_opts, NULL,
 | 
			
		||||
                                  block_crypto_init_func,
 | 
			
		||||
                                  block_crypto_write_func,
 | 
			
		||||
                                  &data,
 | 
			
		||||
@@ -282,27 +358,24 @@ static int block_crypto_co_create_generic(BlockDriverState *bs,
 | 
			
		||||
 | 
			
		||||
    ret = 0;
 | 
			
		||||
 cleanup:
 | 
			
		||||
    QDECREF(cryptoopts);
 | 
			
		||||
    qcrypto_block_free(crypto);
 | 
			
		||||
    blk_unref(blk);
 | 
			
		||||
    blk_unref(data.blk);
 | 
			
		||||
    qapi_free_QCryptoBlockCreateOptions(create_opts);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn
 | 
			
		||||
block_crypto_co_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
static int block_crypto_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
                                 PreallocMode prealloc, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BlockCrypto *crypto = bs->opaque;
 | 
			
		||||
    uint64_t payload_offset =
 | 
			
		||||
        qcrypto_block_get_payload_offset(crypto->block);
 | 
			
		||||
 | 
			
		||||
    if (payload_offset > INT64_MAX - offset) {
 | 
			
		||||
        error_setg(errp, "The requested file size is too large");
 | 
			
		||||
        return -EFBIG;
 | 
			
		||||
    }
 | 
			
		||||
    assert(payload_offset < (INT64_MAX - offset));
 | 
			
		||||
 | 
			
		||||
    offset += payload_offset;
 | 
			
		||||
 | 
			
		||||
    return bdrv_co_truncate(bs->file, offset, prealloc, errp);
 | 
			
		||||
    return bdrv_truncate(bs->file, offset, prealloc, errp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void block_crypto_close(BlockDriverState *bs)
 | 
			
		||||
@@ -464,10 +537,7 @@ static int64_t block_crypto_getlength(BlockDriverState *bs)
 | 
			
		||||
 | 
			
		||||
    uint64_t offset = qcrypto_block_get_payload_offset(crypto->block);
 | 
			
		||||
    assert(offset < INT64_MAX);
 | 
			
		||||
 | 
			
		||||
    if (offset > len) {
 | 
			
		||||
        return -EIO;
 | 
			
		||||
    }
 | 
			
		||||
    assert(offset < len);
 | 
			
		||||
 | 
			
		||||
    len -= offset;
 | 
			
		||||
 | 
			
		||||
@@ -492,88 +562,12 @@ static int block_crypto_open_luks(BlockDriverState *bs,
 | 
			
		||||
                                     bs, options, flags, errp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn
 | 
			
		||||
block_crypto_co_create_luks(BlockdevCreateOptions *create_options, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BlockdevCreateOptionsLUKS *luks_opts;
 | 
			
		||||
    BlockDriverState *bs = NULL;
 | 
			
		||||
    QCryptoBlockCreateOptions create_opts;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    assert(create_options->driver == BLOCKDEV_DRIVER_LUKS);
 | 
			
		||||
    luks_opts = &create_options->u.luks;
 | 
			
		||||
 | 
			
		||||
    bs = bdrv_open_blockdev_ref(luks_opts->file, errp);
 | 
			
		||||
    if (bs == NULL) {
 | 
			
		||||
        return -EIO;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    create_opts = (QCryptoBlockCreateOptions) {
 | 
			
		||||
        .format = Q_CRYPTO_BLOCK_FORMAT_LUKS,
 | 
			
		||||
        .u.luks = *qapi_BlockdevCreateOptionsLUKS_base(luks_opts),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    ret = block_crypto_co_create_generic(bs, luks_opts->size, &create_opts,
 | 
			
		||||
                                         errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = 0;
 | 
			
		||||
fail:
 | 
			
		||||
    bdrv_unref(bs);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
 | 
			
		||||
                                                         QemuOpts *opts,
 | 
			
		||||
                                                         Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    QCryptoBlockCreateOptions *create_opts = NULL;
 | 
			
		||||
    BlockDriverState *bs = NULL;
 | 
			
		||||
    QDict *cryptoopts;
 | 
			
		||||
    int64_t size;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    /* Parse options */
 | 
			
		||||
    size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
 | 
			
		||||
 | 
			
		||||
    cryptoopts = qemu_opts_to_qdict_filtered(opts, NULL,
 | 
			
		||||
                                             &block_crypto_create_opts_luks,
 | 
			
		||||
                                             true);
 | 
			
		||||
 | 
			
		||||
    qdict_put_str(cryptoopts, "format", "luks");
 | 
			
		||||
    create_opts = block_crypto_create_opts_init(cryptoopts, errp);
 | 
			
		||||
    if (!create_opts) {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Create protocol layer */
 | 
			
		||||
    ret = bdrv_create_file(filename, opts, errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs = bdrv_open(filename, NULL, NULL,
 | 
			
		||||
                   BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
 | 
			
		||||
    if (!bs) {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Create format layer */
 | 
			
		||||
    ret = block_crypto_co_create_generic(bs, size, create_opts, errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = 0;
 | 
			
		||||
fail:
 | 
			
		||||
    bdrv_unref(bs);
 | 
			
		||||
    qapi_free_QCryptoBlockCreateOptions(create_opts);
 | 
			
		||||
    qobject_unref(cryptoopts);
 | 
			
		||||
    return ret;
 | 
			
		||||
    return block_crypto_create_generic(Q_CRYPTO_BLOCK_FORMAT_LUKS,
 | 
			
		||||
                                       filename, opts, errp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int block_crypto_get_info_luks(BlockDriverState *bs,
 | 
			
		||||
@@ -628,12 +622,9 @@ BlockDriver bdrv_crypto_luks = {
 | 
			
		||||
    .bdrv_probe         = block_crypto_probe_luks,
 | 
			
		||||
    .bdrv_open          = block_crypto_open_luks,
 | 
			
		||||
    .bdrv_close         = block_crypto_close,
 | 
			
		||||
    /* This driver doesn't modify LUKS metadata except when creating image.
 | 
			
		||||
     * Allow share-rw=on as a special case. */
 | 
			
		||||
    .bdrv_child_perm    = bdrv_filter_default_perms,
 | 
			
		||||
    .bdrv_co_create     = block_crypto_co_create_luks,
 | 
			
		||||
    .bdrv_child_perm    = bdrv_format_default_perms,
 | 
			
		||||
    .bdrv_co_create_opts = block_crypto_co_create_opts_luks,
 | 
			
		||||
    .bdrv_co_truncate   = block_crypto_co_truncate,
 | 
			
		||||
    .bdrv_truncate      = block_crypto_truncate,
 | 
			
		||||
    .create_opts        = &block_crypto_create_opts_luks,
 | 
			
		||||
 | 
			
		||||
    .bdrv_reopen_prepare = block_crypto_reopen_prepare,
 | 
			
		||||
 
 | 
			
		||||
@@ -89,9 +89,13 @@
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
QCryptoBlockCreateOptions *
 | 
			
		||||
block_crypto_create_opts_init(QDict *opts, Error **errp);
 | 
			
		||||
block_crypto_create_opts_init(QCryptoBlockFormat format,
 | 
			
		||||
                              QDict *opts,
 | 
			
		||||
                              Error **errp);
 | 
			
		||||
 | 
			
		||||
QCryptoBlockOpenOptions *
 | 
			
		||||
block_crypto_open_opts_init(QDict *opts, Error **errp);
 | 
			
		||||
block_crypto_open_opts_init(QCryptoBlockFormat format,
 | 
			
		||||
                            QDict *opts,
 | 
			
		||||
                            Error **errp);
 | 
			
		||||
 | 
			
		||||
#endif /* BLOCK_CRYPTO_H__ */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										41
									
								
								block/curl.c
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								block/curl.c
									
									
									
									
									
								
							@@ -32,10 +32,22 @@
 | 
			
		||||
#include "crypto/secret.h"
 | 
			
		||||
#include <curl/curl.h>
 | 
			
		||||
#include "qemu/cutils.h"
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
 | 
			
		||||
// #define DEBUG_CURL
 | 
			
		||||
// #define DEBUG_VERBOSE
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_CURL
 | 
			
		||||
#define DEBUG_CURL_PRINT 1
 | 
			
		||||
#else
 | 
			
		||||
#define DEBUG_CURL_PRINT 0
 | 
			
		||||
#endif
 | 
			
		||||
#define DPRINTF(fmt, ...)                                            \
 | 
			
		||||
    do {                                                             \
 | 
			
		||||
        if (DEBUG_CURL_PRINT) {                                      \
 | 
			
		||||
            fprintf(stderr, fmt, ## __VA_ARGS__);                    \
 | 
			
		||||
        }                                                            \
 | 
			
		||||
    } while (0)
 | 
			
		||||
 | 
			
		||||
#if LIBCURL_VERSION_NUM >= 0x071000
 | 
			
		||||
/* The multi interface timer callback was introduced in 7.16.0 */
 | 
			
		||||
#define NEED_CURL_TIMER_CALLBACK
 | 
			
		||||
@@ -142,7 +154,7 @@ static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    BDRVCURLState *s = opaque;
 | 
			
		||||
 | 
			
		||||
    trace_curl_timer_cb(timeout_ms);
 | 
			
		||||
    DPRINTF("CURL: timer callback timeout_ms %ld\n", timeout_ms);
 | 
			
		||||
    if (timeout_ms == -1) {
 | 
			
		||||
        timer_del(&s->timer);
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -181,7 +193,7 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
 | 
			
		||||
    }
 | 
			
		||||
    socket = NULL;
 | 
			
		||||
 | 
			
		||||
    trace_curl_sock_cb(action, (int)fd);
 | 
			
		||||
    DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, (int)fd);
 | 
			
		||||
    switch (action) {
 | 
			
		||||
        case CURL_POLL_IN:
 | 
			
		||||
            aio_set_fd_handler(s->aio_context, fd, false,
 | 
			
		||||
@@ -226,7 +238,7 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
 | 
			
		||||
    size_t realsize = size * nmemb;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    trace_curl_read_cb(realsize);
 | 
			
		||||
    DPRINTF("CURL: Just reading %zd bytes\n", realsize);
 | 
			
		||||
 | 
			
		||||
    if (!s || !s->orig_buf) {
 | 
			
		||||
        goto read_end;
 | 
			
		||||
@@ -471,8 +483,6 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state)
 | 
			
		||||
        curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
 | 
			
		||||
        curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER,
 | 
			
		||||
                         (long) s->sslverify);
 | 
			
		||||
        curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYHOST,
 | 
			
		||||
                         s->sslverify ? 2L : 0L);
 | 
			
		||||
        if (s->cookie) {
 | 
			
		||||
            curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie);
 | 
			
		||||
        }
 | 
			
		||||
@@ -672,10 +682,10 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    const char *protocol_delimiter;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    ret = bdrv_apply_auto_read_only(bs, "curl driver does not support writes",
 | 
			
		||||
                                    errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
 | 
			
		||||
    if (flags & BDRV_O_RDWR) {
 | 
			
		||||
        error_setg(errp, "curl block device does not support writes");
 | 
			
		||||
        return -EROFS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!libcurl_initialized) {
 | 
			
		||||
@@ -765,7 +775,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    trace_curl_open(file);
 | 
			
		||||
    DPRINTF("CURL: Opening %s\n", file);
 | 
			
		||||
    qemu_co_queue_init(&s->free_state_waitq);
 | 
			
		||||
    s->aio_context = bdrv_get_aio_context(bs);
 | 
			
		||||
    s->url = g_strdup(file);
 | 
			
		||||
@@ -794,7 +804,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    }
 | 
			
		||||
    /* Prior CURL 7.19.4 return value of 0 could mean that the file size is not
 | 
			
		||||
     * know or the size is zero. From 7.19.4 CURL returns -1 if size is not
 | 
			
		||||
     * known and zero if it is really zero-length file. */
 | 
			
		||||
     * known and zero if it is realy zero-length file. */
 | 
			
		||||
#if LIBCURL_VERSION_NUM >= 0x071304
 | 
			
		||||
    if (d < 0) {
 | 
			
		||||
        pstrcpy(state->errmsg, CURL_ERROR_SIZE,
 | 
			
		||||
@@ -818,7 +828,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
                "Server does not support 'range' (byte ranges).");
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
    trace_curl_open_size(s->len);
 | 
			
		||||
    DPRINTF("CURL: Size = %" PRIu64 "\n", s->len);
 | 
			
		||||
 | 
			
		||||
    qemu_mutex_lock(&s->mutex);
 | 
			
		||||
    curl_clean_state(state);
 | 
			
		||||
@@ -896,7 +906,8 @@ static void curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
 | 
			
		||||
    state->acb[0] = acb;
 | 
			
		||||
 | 
			
		||||
    snprintf(state->range, 127, "%" PRIu64 "-%" PRIu64, start, end);
 | 
			
		||||
    trace_curl_setup_preadv(acb->bytes, start, state->range);
 | 
			
		||||
    DPRINTF("CURL (AIO): Reading %" PRIu64 " at %" PRIu64 " (%s)\n",
 | 
			
		||||
            acb->bytes, start, state->range);
 | 
			
		||||
    curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range);
 | 
			
		||||
 | 
			
		||||
    curl_multi_add_handle(s->multi, state->curl);
 | 
			
		||||
@@ -930,7 +941,7 @@ static void curl_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVCURLState *s = bs->opaque;
 | 
			
		||||
 | 
			
		||||
    trace_curl_close();
 | 
			
		||||
    DPRINTF("CURL: Close\n");
 | 
			
		||||
    curl_detach_aio_context(bs);
 | 
			
		||||
    qemu_mutex_destroy(&s->mutex);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -40,8 +40,6 @@ struct BdrvDirtyBitmap {
 | 
			
		||||
    QemuMutex *mutex;
 | 
			
		||||
    HBitmap *bitmap;            /* Dirty bitmap implementation */
 | 
			
		||||
    HBitmap *meta;              /* Meta dirty bitmap */
 | 
			
		||||
    bool qmp_locked;            /* Bitmap is locked, it can't be modified
 | 
			
		||||
                                   through QMP */
 | 
			
		||||
    BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */
 | 
			
		||||
    char *name;                 /* Optional non-empty unique ID */
 | 
			
		||||
    int64_t size;               /* Size of the bitmap, in bytes */
 | 
			
		||||
@@ -55,10 +53,6 @@ struct BdrvDirtyBitmap {
 | 
			
		||||
                                   and this bitmap must remain unchanged while
 | 
			
		||||
                                   this flag is set. */
 | 
			
		||||
    bool persistent;            /* bitmap must be saved to owner disk image */
 | 
			
		||||
    bool migration;             /* Bitmap is selected for migration, it should
 | 
			
		||||
                                   not be stored on the next inactivation
 | 
			
		||||
                                   (persistent flag doesn't matter until next
 | 
			
		||||
                                   invalidation).*/
 | 
			
		||||
    QLIST_ENTRY(BdrvDirtyBitmap) list;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -101,6 +95,15 @@ BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name)
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called with BQL taken.  */
 | 
			
		||||
void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    assert(!bdrv_dirty_bitmap_frozen(bitmap));
 | 
			
		||||
    g_free(bitmap->name);
 | 
			
		||||
    bitmap->name = NULL;
 | 
			
		||||
    bitmap->persistent = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called with BQL taken.  */
 | 
			
		||||
BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
 | 
			
		||||
                                          uint32_t granularity,
 | 
			
		||||
@@ -180,24 +183,6 @@ bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
    return bitmap->successor;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Both conditions disallow user-modification via QMP. */
 | 
			
		||||
bool bdrv_dirty_bitmap_user_locked(BdrvDirtyBitmap *bitmap) {
 | 
			
		||||
    return bdrv_dirty_bitmap_frozen(bitmap) ||
 | 
			
		||||
           bdrv_dirty_bitmap_qmp_locked(bitmap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked)
 | 
			
		||||
{
 | 
			
		||||
    qemu_mutex_lock(bitmap->mutex);
 | 
			
		||||
    bitmap->qmp_locked = qmp_locked;
 | 
			
		||||
    qemu_mutex_unlock(bitmap->mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    return bitmap->qmp_locked;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called with BQL taken.  */
 | 
			
		||||
bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
@@ -209,8 +194,6 @@ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    if (bdrv_dirty_bitmap_frozen(bitmap)) {
 | 
			
		||||
        return DIRTY_BITMAP_STATUS_FROZEN;
 | 
			
		||||
    } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) {
 | 
			
		||||
        return DIRTY_BITMAP_STATUS_LOCKED;
 | 
			
		||||
    } else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
 | 
			
		||||
        return DIRTY_BITMAP_STATUS_DISABLED;
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -251,33 +234,6 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    assert(!bdrv_dirty_bitmap_frozen(bitmap));
 | 
			
		||||
    bitmap->disabled = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called with BQL taken. */
 | 
			
		||||
void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    assert(bitmap->mutex == bitmap->successor->mutex);
 | 
			
		||||
    qemu_mutex_lock(bitmap->mutex);
 | 
			
		||||
    bdrv_enable_dirty_bitmap_locked(bitmap->successor);
 | 
			
		||||
    qemu_mutex_unlock(bitmap->mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken.  */
 | 
			
		||||
static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    assert(!bitmap->active_iterators);
 | 
			
		||||
    assert(!bdrv_dirty_bitmap_frozen(bitmap));
 | 
			
		||||
    assert(!bitmap->meta);
 | 
			
		||||
    QLIST_REMOVE(bitmap, list);
 | 
			
		||||
    hbitmap_free(bitmap->bitmap);
 | 
			
		||||
    g_free(bitmap->name);
 | 
			
		||||
    g_free(bitmap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * For a bitmap with a successor, yield our name to the successor,
 | 
			
		||||
 * delete the old bitmap, and return a handle to the new bitmap.
 | 
			
		||||
@@ -311,9 +267,9 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
 | 
			
		||||
 * In cases of failure where we can no longer safely delete the parent,
 | 
			
		||||
 * we may wish to re-join the parent and child/successor.
 | 
			
		||||
 * The merged parent will be un-frozen, but not explicitly re-enabled.
 | 
			
		||||
 * Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken.
 | 
			
		||||
 * Called with BQL taken.
 | 
			
		||||
 */
 | 
			
		||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
 | 
			
		||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
 | 
			
		||||
                                           BdrvDirtyBitmap *parent,
 | 
			
		||||
                                           Error **errp)
 | 
			
		||||
{
 | 
			
		||||
@@ -324,30 +280,16 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap)) {
 | 
			
		||||
    if (!hbitmap_merge(parent->bitmap, successor->bitmap)) {
 | 
			
		||||
        error_setg(errp, "Merging of parent and successor bitmap failed");
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    bdrv_release_dirty_bitmap_locked(successor);
 | 
			
		||||
    bdrv_release_dirty_bitmap(bs, successor);
 | 
			
		||||
    parent->successor = NULL;
 | 
			
		||||
 | 
			
		||||
    return parent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called with BQL taken. */
 | 
			
		||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
 | 
			
		||||
                                           BdrvDirtyBitmap *parent,
 | 
			
		||||
                                           Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BdrvDirtyBitmap *ret;
 | 
			
		||||
 | 
			
		||||
    qemu_mutex_lock(parent->mutex);
 | 
			
		||||
    ret = bdrv_reclaim_dirty_bitmap_locked(bs, parent, errp);
 | 
			
		||||
    qemu_mutex_unlock(parent->mutex);
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Truncates _all_ bitmaps attached to a BDS.
 | 
			
		||||
 * Called with BQL taken.
 | 
			
		||||
@@ -366,12 +308,45 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes)
 | 
			
		||||
    bdrv_dirty_bitmaps_unlock(bs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool bdrv_dirty_bitmap_has_name(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    return !!bdrv_dirty_bitmap_name(bitmap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called with BQL taken.  */
 | 
			
		||||
static void bdrv_do_release_matching_dirty_bitmap(
 | 
			
		||||
    BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
 | 
			
		||||
    bool (*cond)(BdrvDirtyBitmap *bitmap))
 | 
			
		||||
{
 | 
			
		||||
    BdrvDirtyBitmap *bm, *next;
 | 
			
		||||
    bdrv_dirty_bitmaps_lock(bs);
 | 
			
		||||
    QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
 | 
			
		||||
        if ((!bitmap || bm == bitmap) && (!cond || cond(bm))) {
 | 
			
		||||
            assert(!bm->active_iterators);
 | 
			
		||||
            assert(!bdrv_dirty_bitmap_frozen(bm));
 | 
			
		||||
            assert(!bm->meta);
 | 
			
		||||
            QLIST_REMOVE(bm, list);
 | 
			
		||||
            hbitmap_free(bm->bitmap);
 | 
			
		||||
            g_free(bm->name);
 | 
			
		||||
            g_free(bm);
 | 
			
		||||
 | 
			
		||||
            if (bitmap) {
 | 
			
		||||
                goto out;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (bitmap) {
 | 
			
		||||
        abort();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
    bdrv_dirty_bitmaps_unlock(bs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called with BQL taken.  */
 | 
			
		||||
void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_dirty_bitmaps_lock(bs);
 | 
			
		||||
    bdrv_release_dirty_bitmap_locked(bitmap);
 | 
			
		||||
    bdrv_dirty_bitmaps_unlock(bs);
 | 
			
		||||
    bdrv_do_release_matching_dirty_bitmap(bs, bitmap, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -382,15 +357,19 @@ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
 | 
			
		||||
 */
 | 
			
		||||
void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BdrvDirtyBitmap *bm, *next;
 | 
			
		||||
    bdrv_do_release_matching_dirty_bitmap(bs, NULL, bdrv_dirty_bitmap_has_name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    bdrv_dirty_bitmaps_lock(bs);
 | 
			
		||||
    QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
 | 
			
		||||
        if (bdrv_dirty_bitmap_name(bm)) {
 | 
			
		||||
            bdrv_release_dirty_bitmap_locked(bm);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    bdrv_dirty_bitmaps_unlock(bs);
 | 
			
		||||
/**
 | 
			
		||||
 * Release all persistent dirty bitmaps attached to a BDS (for use in
 | 
			
		||||
 * bdrv_inactivate_recurse()).
 | 
			
		||||
 * There must not be any frozen bitmaps attached.
 | 
			
		||||
 * This function does not remove persistent bitmaps from the storage.
 | 
			
		||||
 */
 | 
			
		||||
void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_do_release_matching_dirty_bitmap(bs, NULL,
 | 
			
		||||
                                          bdrv_dirty_bitmap_get_persistance);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -410,19 +389,18 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called with BQL taken.  */
 | 
			
		||||
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_dirty_bitmap_lock(bitmap);
 | 
			
		||||
    assert(!bdrv_dirty_bitmap_frozen(bitmap));
 | 
			
		||||
    bitmap->disabled = true;
 | 
			
		||||
    bdrv_dirty_bitmap_unlock(bitmap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called with BQL taken.  */
 | 
			
		||||
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    bdrv_dirty_bitmap_lock(bitmap);
 | 
			
		||||
    bdrv_enable_dirty_bitmap_locked(bitmap);
 | 
			
		||||
    bdrv_dirty_bitmap_unlock(bitmap);
 | 
			
		||||
    assert(!bdrv_dirty_bitmap_frozen(bitmap));
 | 
			
		||||
    bitmap->disabled = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
 | 
			
		||||
@@ -554,6 +532,7 @@ void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
 | 
			
		||||
 | 
			
		||||
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
 | 
			
		||||
{
 | 
			
		||||
    assert(bdrv_dirty_bitmap_enabled(bitmap));
 | 
			
		||||
    assert(!bdrv_dirty_bitmap_readonly(bitmap));
 | 
			
		||||
    bdrv_dirty_bitmap_lock(bitmap);
 | 
			
		||||
    if (!out) {
 | 
			
		||||
@@ -567,11 +546,12 @@ void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
 | 
			
		||||
    bdrv_dirty_bitmap_unlock(bitmap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup)
 | 
			
		||||
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in)
 | 
			
		||||
{
 | 
			
		||||
    HBitmap *tmp = bitmap->bitmap;
 | 
			
		||||
    assert(bdrv_dirty_bitmap_enabled(bitmap));
 | 
			
		||||
    assert(!bdrv_dirty_bitmap_readonly(bitmap));
 | 
			
		||||
    bitmap->bitmap = backup;
 | 
			
		||||
    bitmap->bitmap = in;
 | 
			
		||||
    hbitmap_free(tmp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -689,24 +669,16 @@ void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent)
 | 
			
		||||
    qemu_mutex_unlock(bitmap->mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called with BQL taken. */
 | 
			
		||||
void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration)
 | 
			
		||||
{
 | 
			
		||||
    qemu_mutex_lock(bitmap->mutex);
 | 
			
		||||
    bitmap->migration = migration;
 | 
			
		||||
    qemu_mutex_unlock(bitmap->mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap)
 | 
			
		||||
{
 | 
			
		||||
    return bitmap->persistent && !bitmap->migration;
 | 
			
		||||
    return bitmap->persistent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BdrvDirtyBitmap *bm;
 | 
			
		||||
    QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
 | 
			
		||||
        if (bm->persistent && !bm->readonly && !bm->migration) {
 | 
			
		||||
        if (bm->persistent && !bm->readonly) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -726,54 +698,7 @@ char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp)
 | 
			
		||||
    return hbitmap_sha256(bitmap->bitmap, errp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset,
 | 
			
		||||
                                    uint64_t bytes)
 | 
			
		||||
int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset)
 | 
			
		||||
{
 | 
			
		||||
    return hbitmap_next_zero(bitmap->bitmap, offset, bytes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap,
 | 
			
		||||
                                       uint64_t *offset, uint64_t *bytes)
 | 
			
		||||
{
 | 
			
		||||
    return hbitmap_next_dirty_area(bitmap->bitmap, offset, bytes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
 | 
			
		||||
                             HBitmap **backup, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    bool ret;
 | 
			
		||||
 | 
			
		||||
    /* only bitmaps from one bds are supported */
 | 
			
		||||
    assert(dest->mutex == src->mutex);
 | 
			
		||||
 | 
			
		||||
    qemu_mutex_lock(dest->mutex);
 | 
			
		||||
 | 
			
		||||
    if (bdrv_dirty_bitmap_user_locked(dest)) {
 | 
			
		||||
        error_setg(errp, "Bitmap '%s' is currently in use by another"
 | 
			
		||||
        " operation and cannot be modified", dest->name);
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (bdrv_dirty_bitmap_readonly(dest)) {
 | 
			
		||||
        error_setg(errp, "Bitmap '%s' is readonly and cannot be modified",
 | 
			
		||||
                   dest->name);
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!hbitmap_can_merge(dest->bitmap, src->bitmap)) {
 | 
			
		||||
        error_setg(errp, "Bitmaps are incompatible and can't be merged");
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (backup) {
 | 
			
		||||
        *backup = dest->bitmap;
 | 
			
		||||
        dest->bitmap = hbitmap_alloc(dest->size, hbitmap_granularity(*backup));
 | 
			
		||||
        ret = hbitmap_merge(*backup, src->bitmap, dest->bitmap);
 | 
			
		||||
    } else {
 | 
			
		||||
        ret = hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap);
 | 
			
		||||
    }
 | 
			
		||||
    assert(ret);
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
    qemu_mutex_unlock(dest->mutex);
 | 
			
		||||
    return hbitmap_next_zero(bitmap->bitmap, offset);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,49 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * DMG lzfse uncompression
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2018 Julio Cesar Faracco
 | 
			
		||||
 *
 | 
			
		||||
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
 * of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
 * in the Software without restriction, including without limitation the rights
 | 
			
		||||
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
 * copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
 * furnished to do so, subject to the following conditions:
 | 
			
		||||
 *
 | 
			
		||||
 * The above copyright notice and this permission notice shall be included in
 | 
			
		||||
 * all copies or substantial portions of the Software.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 | 
			
		||||
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 * THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "dmg.h"
 | 
			
		||||
#include <lzfse.h>
 | 
			
		||||
 | 
			
		||||
static int dmg_uncompress_lzfse_do(char *next_in, unsigned int avail_in,
 | 
			
		||||
                                   char *next_out, unsigned int avail_out)
 | 
			
		||||
{
 | 
			
		||||
    size_t out_size = lzfse_decode_buffer((uint8_t *) next_out, avail_out,
 | 
			
		||||
                                          (uint8_t *) next_in, avail_in,
 | 
			
		||||
                                          NULL);
 | 
			
		||||
 | 
			
		||||
    /* We need to decode the single chunk only. */
 | 
			
		||||
    /* So, out_size == avail_out is not an error here. */
 | 
			
		||||
    if (out_size > 0) {
 | 
			
		||||
        return out_size;
 | 
			
		||||
    }
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((constructor))
 | 
			
		||||
static void dmg_lzfse_init(void)
 | 
			
		||||
{
 | 
			
		||||
    assert(!dmg_uncompress_lzfse);
 | 
			
		||||
    dmg_uncompress_lzfse = dmg_uncompress_lzfse_do;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										104
									
								
								block/dmg.c
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								block/dmg.c
									
									
									
									
									
								
							@@ -33,9 +33,6 @@
 | 
			
		||||
int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in,
 | 
			
		||||
                          char *next_out, unsigned int avail_out);
 | 
			
		||||
 | 
			
		||||
int (*dmg_uncompress_lzfse)(char *next_in, unsigned int avail_in,
 | 
			
		||||
                            char *next_out, unsigned int avail_out);
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
    /* Limit chunk sizes to prevent unreasonable amounts of memory being used
 | 
			
		||||
     * or truncating when converting to 32-bit types
 | 
			
		||||
@@ -44,19 +41,6 @@ enum {
 | 
			
		||||
    DMG_SECTORCOUNTS_MAX = DMG_LENGTHS_MAX / 512,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
    /* DMG Block Type */
 | 
			
		||||
    UDZE = 0, /* Zeroes */
 | 
			
		||||
    UDRW,     /* RAW type */
 | 
			
		||||
    UDIG,     /* Ignore */
 | 
			
		||||
    UDCO = 0x80000004,
 | 
			
		||||
    UDZO,
 | 
			
		||||
    UDBZ,
 | 
			
		||||
    ULFO,
 | 
			
		||||
    UDCM = 0x7ffffffe, /* Comments */
 | 
			
		||||
    UDLE = 0xffffffff  /* Last Entry */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    int len;
 | 
			
		||||
@@ -121,17 +105,15 @@ static void update_max_chunk_size(BDRVDMGState *s, uint32_t chunk,
 | 
			
		||||
    uint32_t uncompressed_sectors = 0;
 | 
			
		||||
 | 
			
		||||
    switch (s->types[chunk]) {
 | 
			
		||||
    case UDZO: /* zlib compressed */
 | 
			
		||||
    case UDBZ: /* bzip2 compressed */
 | 
			
		||||
    case ULFO: /* lzfse compressed */
 | 
			
		||||
    case 0x80000005: /* zlib compressed */
 | 
			
		||||
    case 0x80000006: /* bzip2 compressed */
 | 
			
		||||
        compressed_size = s->lengths[chunk];
 | 
			
		||||
        uncompressed_sectors = s->sectorcounts[chunk];
 | 
			
		||||
        break;
 | 
			
		||||
    case UDRW: /* copy */
 | 
			
		||||
    case 1: /* copy */
 | 
			
		||||
        uncompressed_sectors = DIV_ROUND_UP(s->lengths[chunk], 512);
 | 
			
		||||
        break;
 | 
			
		||||
    case UDZE: /* zero */
 | 
			
		||||
    case UDIG: /* ignore */
 | 
			
		||||
    case 2: /* zero */
 | 
			
		||||
        /* as the all-zeroes block may be large, it is treated specially: the
 | 
			
		||||
         * sector is not copied from a large buffer, a simple memset is used
 | 
			
		||||
         * instead. Therefore uncompressed_sectors does not need to be set. */
 | 
			
		||||
@@ -200,15 +182,12 @@ typedef struct DmgHeaderState {
 | 
			
		||||
static bool dmg_is_known_block_type(uint32_t entry_type)
 | 
			
		||||
{
 | 
			
		||||
    switch (entry_type) {
 | 
			
		||||
    case UDZE:    /* zeros */
 | 
			
		||||
    case UDRW:    /* uncompressed */
 | 
			
		||||
    case UDIG:    /* ignore */
 | 
			
		||||
    case UDZO:    /* zlib */
 | 
			
		||||
    case 0x00000001:    /* uncompressed */
 | 
			
		||||
    case 0x00000002:    /* zeroes */
 | 
			
		||||
    case 0x80000005:    /* zlib */
 | 
			
		||||
        return true;
 | 
			
		||||
    case UDBZ:    /* bzip2 */
 | 
			
		||||
    case 0x80000006:    /* bzip2 */
 | 
			
		||||
        return !!dmg_uncompress_bz2;
 | 
			
		||||
    case ULFO:    /* lzfse */
 | 
			
		||||
        return !!dmg_uncompress_lzfse;
 | 
			
		||||
    default:
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
@@ -267,10 +246,9 @@ static int dmg_read_mish_block(BDRVDMGState *s, DmgHeaderState *ds,
 | 
			
		||||
        /* sector count */
 | 
			
		||||
        s->sectorcounts[i] = buff_read_uint64(buffer, offset + 0x10);
 | 
			
		||||
 | 
			
		||||
        /* all-zeroes sector (type UDZE and UDIG) does not need to be
 | 
			
		||||
         * "uncompressed" and can therefore be unbounded. */
 | 
			
		||||
        if (s->types[i] != UDZE && s->types[i] != UDIG
 | 
			
		||||
            && s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) {
 | 
			
		||||
        /* all-zeroes sector (type 2) does not need to be "uncompressed" and can
 | 
			
		||||
         * therefore be unbounded. */
 | 
			
		||||
        if (s->types[i] != 2 && s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) {
 | 
			
		||||
            error_report("sector count %" PRIu64 " for chunk %" PRIu32
 | 
			
		||||
                         " is larger than max (%u)",
 | 
			
		||||
                         s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX);
 | 
			
		||||
@@ -435,19 +413,24 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    int64_t offset;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    ret = bdrv_apply_auto_read_only(bs, NULL, errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
 | 
			
		||||
                               false, errp);
 | 
			
		||||
    if (!bs->file) {
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!bdrv_is_read_only(bs)) {
 | 
			
		||||
        error_report("Opening dmg images without an explicit read-only=on "
 | 
			
		||||
                     "option is deprecated. Future versions will refuse to "
 | 
			
		||||
                     "open the image instead of automatically marking the "
 | 
			
		||||
                     "image read-only.");
 | 
			
		||||
        ret = bdrv_set_read_only(bs, true, errp);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    block_module_load_one("dmg-bz2");
 | 
			
		||||
    block_module_load_one("dmg-lzfse");
 | 
			
		||||
 | 
			
		||||
    s->n_chunks = 0;
 | 
			
		||||
    s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL;
 | 
			
		||||
@@ -575,20 +558,16 @@ static inline uint32_t search_chunk(BDRVDMGState *s, uint64_t sector_num)
 | 
			
		||||
{
 | 
			
		||||
    /* binary search */
 | 
			
		||||
    uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3;
 | 
			
		||||
    while (chunk1 <= chunk2) {
 | 
			
		||||
    while (chunk1 != chunk2) {
 | 
			
		||||
        chunk3 = (chunk1 + chunk2) / 2;
 | 
			
		||||
        if (s->sectors[chunk3] > sector_num) {
 | 
			
		||||
            if (chunk3 == 0) {
 | 
			
		||||
                goto err;
 | 
			
		||||
            }
 | 
			
		||||
            chunk2 = chunk3 - 1;
 | 
			
		||||
            chunk2 = chunk3;
 | 
			
		||||
        } else if (s->sectors[chunk3] + s->sectorcounts[chunk3] > sector_num) {
 | 
			
		||||
            return chunk3;
 | 
			
		||||
        } else {
 | 
			
		||||
            chunk1 = chunk3 + 1;
 | 
			
		||||
            chunk1 = chunk3;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
err:
 | 
			
		||||
    return s->n_chunks; /* error */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -606,7 +585,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num)
 | 
			
		||||
 | 
			
		||||
        s->current_chunk = s->n_chunks;
 | 
			
		||||
        switch (s->types[chunk]) { /* block entry type */
 | 
			
		||||
        case UDZO: { /* zlib compressed */
 | 
			
		||||
        case 0x80000005: { /* zlib compressed */
 | 
			
		||||
            /* we need to buffer, because only the chunk as whole can be
 | 
			
		||||
             * inflated. */
 | 
			
		||||
            ret = bdrv_pread(bs->file, s->offsets[chunk],
 | 
			
		||||
@@ -629,7 +608,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num)
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
            break; }
 | 
			
		||||
        case UDBZ: /* bzip2 compressed */
 | 
			
		||||
        case 0x80000006: /* bzip2 compressed */
 | 
			
		||||
            if (!dmg_uncompress_bz2) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
@@ -650,36 +629,14 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num)
 | 
			
		||||
                return ret;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case ULFO:
 | 
			
		||||
            if (!dmg_uncompress_lzfse) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            /* we need to buffer, because only the chunk as whole can be
 | 
			
		||||
             * inflated. */
 | 
			
		||||
            ret = bdrv_pread(bs->file, s->offsets[chunk],
 | 
			
		||||
                             s->compressed_chunk, s->lengths[chunk]);
 | 
			
		||||
            if (ret != s->lengths[chunk]) {
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ret = dmg_uncompress_lzfse((char *)s->compressed_chunk,
 | 
			
		||||
                                       (unsigned int) s->lengths[chunk],
 | 
			
		||||
                                       (char *)s->uncompressed_chunk,
 | 
			
		||||
                                       (unsigned int)
 | 
			
		||||
                                           (512 * s->sectorcounts[chunk]));
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
                return ret;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case UDRW: /* copy */
 | 
			
		||||
        case 1: /* copy */
 | 
			
		||||
            ret = bdrv_pread(bs->file, s->offsets[chunk],
 | 
			
		||||
                             s->uncompressed_chunk, s->lengths[chunk]);
 | 
			
		||||
            if (ret != s->lengths[chunk]) {
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case UDZE: /* zeros */
 | 
			
		||||
        case UDIG: /* ignore */
 | 
			
		||||
        case 2: /* zero */
 | 
			
		||||
            /* see dmg_read, it is treated specially. No buffer needs to be
 | 
			
		||||
             * pre-filled, the zeroes can be set directly. */
 | 
			
		||||
            break;
 | 
			
		||||
@@ -714,8 +671,7 @@ dmg_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
 | 
			
		||||
        /* Special case: current chunk is all zeroes. Do not perform a memcpy as
 | 
			
		||||
         * s->uncompressed_chunk may be too small to cover the large all-zeroes
 | 
			
		||||
         * section. dmg_read_chunk is called to find s->current_chunk */
 | 
			
		||||
        if (s->types[s->current_chunk] == UDZE
 | 
			
		||||
            || s->types[s->current_chunk] == UDIG) { /* all zeroes block entry */
 | 
			
		||||
        if (s->types[s->current_chunk] == 2) { /* all zeroes block entry */
 | 
			
		||||
            qemu_iovec_memset(qiov, i * 512, 0, 512);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,4 @@ typedef struct BDRVDMGState {
 | 
			
		||||
extern int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in,
 | 
			
		||||
                                 char *next_out, unsigned int avail_out);
 | 
			
		||||
 | 
			
		||||
extern int (*dmg_uncompress_lzfse)(char *next_in, unsigned int avail_in,
 | 
			
		||||
                                   char *next_out, unsigned int avail_out);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1210
									
								
								block/file-posix.c
									
									
									
									
									
								
							
							
						
						
									
										1210
									
								
								block/file-posix.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -162,7 +162,7 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile,
 | 
			
		||||
    acb->aio_nbytes = count;
 | 
			
		||||
    acb->aio_offset = offset;
 | 
			
		||||
 | 
			
		||||
    trace_file_paio_submit(acb, opaque, offset, count, type);
 | 
			
		||||
    trace_paio_submit(acb, opaque, offset, count, type);
 | 
			
		||||
    pool = aio_get_thread_pool(bdrv_get_aio_context(bs));
 | 
			
		||||
    return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque);
 | 
			
		||||
}
 | 
			
		||||
@@ -251,11 +251,7 @@ static void raw_probe_alignment(BlockDriverState *bs, Error **errp)
 | 
			
		||||
                         &dg.Geometry.BytesPerSector,
 | 
			
		||||
                         &freeClusters, &totalClusters);
 | 
			
		||||
        bs->bl.request_alignment = dg.Geometry.BytesPerSector;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* XXX Does Windows support AIO on less than 512-byte alignment? */
 | 
			
		||||
    bs->bl.request_alignment = 512;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void raw_parse_flags(int flags, bool use_aio, int *access_flags,
 | 
			
		||||
@@ -414,32 +410,32 @@ fail:
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockAIOCB *raw_aio_preadv(BlockDriverState *bs,
 | 
			
		||||
                                  uint64_t offset, uint64_t bytes,
 | 
			
		||||
                                  QEMUIOVector *qiov, int flags,
 | 
			
		||||
static BlockAIOCB *raw_aio_readv(BlockDriverState *bs,
 | 
			
		||||
                         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
 | 
			
		||||
                         BlockCompletionFunc *cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
    if (s->aio) {
 | 
			
		||||
        return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov,
 | 
			
		||||
                                cb, opaque, QEMU_AIO_READ);
 | 
			
		||||
        return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov,
 | 
			
		||||
                                nb_sectors, cb, opaque, QEMU_AIO_READ);
 | 
			
		||||
    } else {
 | 
			
		||||
        return paio_submit(bs, s->hfile, offset, qiov, bytes,
 | 
			
		||||
        return paio_submit(bs, s->hfile, sector_num << BDRV_SECTOR_BITS, qiov,
 | 
			
		||||
                           nb_sectors << BDRV_SECTOR_BITS,
 | 
			
		||||
                           cb, opaque, QEMU_AIO_READ);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockAIOCB *raw_aio_pwritev(BlockDriverState *bs,
 | 
			
		||||
                                   uint64_t offset, uint64_t bytes,
 | 
			
		||||
                                   QEMUIOVector *qiov, int flags,
 | 
			
		||||
static BlockAIOCB *raw_aio_writev(BlockDriverState *bs,
 | 
			
		||||
                          int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
 | 
			
		||||
                          BlockCompletionFunc *cb, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
    if (s->aio) {
 | 
			
		||||
        return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov,
 | 
			
		||||
                                cb, opaque, QEMU_AIO_WRITE);
 | 
			
		||||
        return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov,
 | 
			
		||||
                                nb_sectors, cb, opaque, QEMU_AIO_WRITE);
 | 
			
		||||
    } else {
 | 
			
		||||
        return paio_submit(bs, s->hfile, offset, qiov, bytes,
 | 
			
		||||
        return paio_submit(bs, s->hfile, sector_num << BDRV_SECTOR_BITS, qiov,
 | 
			
		||||
                           nb_sectors << BDRV_SECTOR_BITS,
 | 
			
		||||
                           cb, opaque, QEMU_AIO_WRITE);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -467,7 +463,7 @@ static void raw_close(BlockDriverState *bs)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
static int raw_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
                        PreallocMode prealloc, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
@@ -636,11 +632,11 @@ BlockDriver bdrv_file = {
 | 
			
		||||
    .bdrv_co_create_opts = raw_co_create_opts,
 | 
			
		||||
    .bdrv_has_zero_init = bdrv_has_zero_init_1,
 | 
			
		||||
 | 
			
		||||
    .bdrv_aio_preadv    = raw_aio_preadv,
 | 
			
		||||
    .bdrv_aio_pwritev   = raw_aio_pwritev,
 | 
			
		||||
    .bdrv_aio_readv     = raw_aio_readv,
 | 
			
		||||
    .bdrv_aio_writev    = raw_aio_writev,
 | 
			
		||||
    .bdrv_aio_flush     = raw_aio_flush,
 | 
			
		||||
 | 
			
		||||
    .bdrv_co_truncate   = raw_co_truncate,
 | 
			
		||||
    .bdrv_truncate	= raw_truncate,
 | 
			
		||||
    .bdrv_getlength	= raw_getlength,
 | 
			
		||||
    .bdrv_get_allocated_file_size
 | 
			
		||||
                        = raw_get_allocated_file_size,
 | 
			
		||||
@@ -712,12 +708,6 @@ static void hdev_parse_filename(const char *filename, QDict *options,
 | 
			
		||||
    bdrv_parse_filename_strip_prefix(filename, "host_device:", options);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void hdev_refresh_limits(BlockDriverState *bs, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    /* XXX Does Windows support AIO on less than 512-byte alignment? */
 | 
			
		||||
    bs->bl.request_alignment = 512;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
                     Error **errp)
 | 
			
		||||
{
 | 
			
		||||
@@ -803,10 +793,9 @@ static BlockDriver bdrv_host_device = {
 | 
			
		||||
    .bdrv_probe_device	= hdev_probe_device,
 | 
			
		||||
    .bdrv_file_open	= hdev_open,
 | 
			
		||||
    .bdrv_close		= raw_close,
 | 
			
		||||
    .bdrv_refresh_limits = hdev_refresh_limits,
 | 
			
		||||
 | 
			
		||||
    .bdrv_aio_preadv    = raw_aio_preadv,
 | 
			
		||||
    .bdrv_aio_pwritev   = raw_aio_pwritev,
 | 
			
		||||
    .bdrv_aio_readv     = raw_aio_readv,
 | 
			
		||||
    .bdrv_aio_writev    = raw_aio_writev,
 | 
			
		||||
    .bdrv_aio_flush     = raw_aio_flush,
 | 
			
		||||
 | 
			
		||||
    .bdrv_detach_aio_context = raw_detach_aio_context,
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,6 @@
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include <glusterfs/api/glfs.h>
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
#include "block/qdict.h"
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
#include "qapi/qmp/qdict.h"
 | 
			
		||||
#include "qapi/qmp/qerror.h"
 | 
			
		||||
@@ -72,7 +71,7 @@ typedef struct ListElement {
 | 
			
		||||
    GlfsPreopened saved;
 | 
			
		||||
} ListElement;
 | 
			
		||||
 | 
			
		||||
static QLIST_HEAD(, ListElement) glfs_list;
 | 
			
		||||
static QLIST_HEAD(glfs_list, ListElement) glfs_list;
 | 
			
		||||
 | 
			
		||||
static QemuOptsList qemu_gluster_create_opts = {
 | 
			
		||||
    .name = "qemu-gluster-create-opts",
 | 
			
		||||
@@ -168,12 +167,7 @@ static QemuOptsList runtime_unix_opts = {
 | 
			
		||||
        {
 | 
			
		||||
            .name = GLUSTER_OPT_SOCKET,
 | 
			
		||||
            .type = QEMU_OPT_STRING,
 | 
			
		||||
            .help = "socket file path (legacy)",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = GLUSTER_OPT_PATH,
 | 
			
		||||
            .type = QEMU_OPT_STRING,
 | 
			
		||||
            .help = "socket file path (QAPI)",
 | 
			
		||||
            .help = "socket file path)",
 | 
			
		||||
        },
 | 
			
		||||
        { /* end of list */ }
 | 
			
		||||
    },
 | 
			
		||||
@@ -621,18 +615,10 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
 | 
			
		||||
                goto out;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ptr = qemu_opt_get(opts, GLUSTER_OPT_PATH);
 | 
			
		||||
            if (!ptr) {
 | 
			
		||||
            ptr = qemu_opt_get(opts, GLUSTER_OPT_SOCKET);
 | 
			
		||||
            } else if (qemu_opt_get(opts, GLUSTER_OPT_SOCKET)) {
 | 
			
		||||
                error_setg(&local_err,
 | 
			
		||||
                           "Conflicting parameters 'path' and 'socket'");
 | 
			
		||||
                error_append_hint(&local_err, GERR_INDEX_HINT, i);
 | 
			
		||||
                goto out;
 | 
			
		||||
            }
 | 
			
		||||
            if (!ptr) {
 | 
			
		||||
                error_setg(&local_err, QERR_MISSING_PARAMETER,
 | 
			
		||||
                           GLUSTER_OPT_PATH);
 | 
			
		||||
                           GLUSTER_OPT_SOCKET);
 | 
			
		||||
                error_append_hint(&local_err, GERR_INDEX_HINT, i);
 | 
			
		||||
                goto out;
 | 
			
		||||
            }
 | 
			
		||||
@@ -651,7 +637,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
 | 
			
		||||
        }
 | 
			
		||||
        gsconf = NULL;
 | 
			
		||||
 | 
			
		||||
        qobject_unref(backing_options);
 | 
			
		||||
        QDECREF(backing_options);
 | 
			
		||||
        backing_options = NULL;
 | 
			
		||||
        g_free(str);
 | 
			
		||||
        str = NULL;
 | 
			
		||||
@@ -664,7 +650,7 @@ out:
 | 
			
		||||
    qapi_free_SocketAddress(gsconf);
 | 
			
		||||
    qemu_opts_del(opts);
 | 
			
		||||
    g_free(str);
 | 
			
		||||
    qobject_unref(backing_options);
 | 
			
		||||
    QDECREF(backing_options);
 | 
			
		||||
    errno = EINVAL;
 | 
			
		||||
    return -errno;
 | 
			
		||||
}
 | 
			
		||||
@@ -679,7 +665,7 @@ static int qemu_gluster_parse(BlockdevOptionsGluster *gconf,
 | 
			
		||||
    if (filename) {
 | 
			
		||||
        ret = qemu_gluster_parse_uri(gconf, filename);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            error_setg(errp, "invalid URI %s", filename);
 | 
			
		||||
            error_setg(errp, "invalid URI");
 | 
			
		||||
            error_append_hint(errp, "Usage: file=gluster[+transport]://"
 | 
			
		||||
                                    "[host[:port]]volume/path[?socket=...]"
 | 
			
		||||
                                    "[,file.debug=N]"
 | 
			
		||||
@@ -698,7 +684,7 @@ static int qemu_gluster_parse(BlockdevOptionsGluster *gconf,
 | 
			
		||||
                             "file.server.0.host=1.2.3.4,"
 | 
			
		||||
                             "file.server.0.port=24007,"
 | 
			
		||||
                             "file.server.1.transport=unix,"
 | 
			
		||||
                             "file.server.1.path=/var/run/glusterd.socket ..."
 | 
			
		||||
                             "file.server.1.socket=/var/run/glusterd.socket ..."
 | 
			
		||||
                             "\n");
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
@@ -849,16 +835,8 @@ static int qemu_gluster_open(BlockDriverState *bs,  QDict *options,
 | 
			
		||||
    qemu_gluster_parse_flags(bdrv_flags, &open_flags);
 | 
			
		||||
 | 
			
		||||
    s->fd = glfs_open(s->glfs, gconf->path, open_flags);
 | 
			
		||||
    ret = s->fd ? 0 : -errno;
 | 
			
		||||
 | 
			
		||||
    if (ret == -EACCES || ret == -EROFS) {
 | 
			
		||||
        /* Try to degrade to read-only, but if it doesn't work, still use the
 | 
			
		||||
         * normal error message. */
 | 
			
		||||
        if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) {
 | 
			
		||||
            open_flags = (open_flags & ~O_RDWR) | O_RDONLY;
 | 
			
		||||
            s->fd = glfs_open(s->glfs, gconf->path, open_flags);
 | 
			
		||||
            ret = s->fd ? 0 : -errno;
 | 
			
		||||
        }
 | 
			
		||||
    if (!s->fd) {
 | 
			
		||||
        ret = -errno;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->supports_seek_data = qemu_gluster_test_seek(s->fd);
 | 
			
		||||
@@ -1185,10 +1163,8 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
 | 
			
		||||
    return acb.ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static coroutine_fn int qemu_gluster_co_truncate(BlockDriverState *bs,
 | 
			
		||||
                                                 int64_t offset,
 | 
			
		||||
                                                 PreallocMode prealloc,
 | 
			
		||||
                                                 Error **errp)
 | 
			
		||||
static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
                                 PreallocMode prealloc, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BDRVGlusterState *s = bs->opaque;
 | 
			
		||||
    return qemu_gluster_do_truncate(s->fd, offset, prealloc, errp);
 | 
			
		||||
@@ -1205,10 +1181,8 @@ static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs,
 | 
			
		||||
static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs,
 | 
			
		||||
                                               int64_t sector_num,
 | 
			
		||||
                                               int nb_sectors,
 | 
			
		||||
                                               QEMUIOVector *qiov,
 | 
			
		||||
                                               int flags)
 | 
			
		||||
                                               QEMUIOVector *qiov)
 | 
			
		||||
{
 | 
			
		||||
    assert(!flags);
 | 
			
		||||
    return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1334,7 +1308,7 @@ static int qemu_gluster_has_zero_init(BlockDriverState *bs)
 | 
			
		||||
 * If @start is in a trailing hole or beyond EOF, return -ENXIO.
 | 
			
		||||
 * If we can't find out, return a negative errno other than -ENXIO.
 | 
			
		||||
 *
 | 
			
		||||
 * (Shamefully copied from file-posix.c, only minuscule adaptions.)
 | 
			
		||||
 * (Shamefully copied from file-posix.c, only miniscule adaptions.)
 | 
			
		||||
 */
 | 
			
		||||
static int find_allocation(BlockDriverState *bs, off_t start,
 | 
			
		||||
                           off_t *data, off_t *hole)
 | 
			
		||||
@@ -1509,7 +1483,7 @@ static BlockDriver bdrv_gluster = {
 | 
			
		||||
    .bdrv_co_create_opts          = qemu_gluster_co_create_opts,
 | 
			
		||||
    .bdrv_getlength               = qemu_gluster_getlength,
 | 
			
		||||
    .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
 | 
			
		||||
    .bdrv_co_truncate             = qemu_gluster_co_truncate,
 | 
			
		||||
    .bdrv_truncate                = qemu_gluster_truncate,
 | 
			
		||||
    .bdrv_co_readv                = qemu_gluster_co_readv,
 | 
			
		||||
    .bdrv_co_writev               = qemu_gluster_co_writev,
 | 
			
		||||
    .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk,
 | 
			
		||||
@@ -1538,7 +1512,7 @@ static BlockDriver bdrv_gluster_tcp = {
 | 
			
		||||
    .bdrv_co_create_opts          = qemu_gluster_co_create_opts,
 | 
			
		||||
    .bdrv_getlength               = qemu_gluster_getlength,
 | 
			
		||||
    .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
 | 
			
		||||
    .bdrv_co_truncate             = qemu_gluster_co_truncate,
 | 
			
		||||
    .bdrv_truncate                = qemu_gluster_truncate,
 | 
			
		||||
    .bdrv_co_readv                = qemu_gluster_co_readv,
 | 
			
		||||
    .bdrv_co_writev               = qemu_gluster_co_writev,
 | 
			
		||||
    .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk,
 | 
			
		||||
@@ -1567,7 +1541,7 @@ static BlockDriver bdrv_gluster_unix = {
 | 
			
		||||
    .bdrv_co_create_opts          = qemu_gluster_co_create_opts,
 | 
			
		||||
    .bdrv_getlength               = qemu_gluster_getlength,
 | 
			
		||||
    .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
 | 
			
		||||
    .bdrv_co_truncate             = qemu_gluster_co_truncate,
 | 
			
		||||
    .bdrv_truncate                = qemu_gluster_truncate,
 | 
			
		||||
    .bdrv_co_readv                = qemu_gluster_co_readv,
 | 
			
		||||
    .bdrv_co_writev               = qemu_gluster_co_writev,
 | 
			
		||||
    .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk,
 | 
			
		||||
@@ -1602,7 +1576,7 @@ static BlockDriver bdrv_gluster_rdma = {
 | 
			
		||||
    .bdrv_co_create_opts          = qemu_gluster_co_create_opts,
 | 
			
		||||
    .bdrv_getlength               = qemu_gluster_getlength,
 | 
			
		||||
    .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
 | 
			
		||||
    .bdrv_co_truncate             = qemu_gluster_co_truncate,
 | 
			
		||||
    .bdrv_truncate                = qemu_gluster_truncate,
 | 
			
		||||
    .bdrv_co_readv                = qemu_gluster_co_readv,
 | 
			
		||||
    .bdrv_co_writev               = qemu_gluster_co_writev,
 | 
			
		||||
    .bdrv_co_flush_to_disk        = qemu_gluster_co_flush_to_disk,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										899
									
								
								block/io.c
									
									
									
									
									
								
							
							
						
						
									
										899
									
								
								block/io.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										419
									
								
								block/iscsi.c
									
									
									
									
									
								
							
							
						
						
									
										419
									
								
								block/iscsi.c
									
									
									
									
									
								
							@@ -33,7 +33,6 @@
 | 
			
		||||
#include "qemu/bitops.h"
 | 
			
		||||
#include "qemu/bitmap.h"
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
#include "block/qdict.h"
 | 
			
		||||
#include "scsi/constants.h"
 | 
			
		||||
#include "qemu/iov.h"
 | 
			
		||||
#include "qemu/option.h"
 | 
			
		||||
@@ -44,14 +43,11 @@
 | 
			
		||||
#include "qapi/qmp/qstring.h"
 | 
			
		||||
#include "crypto/secret.h"
 | 
			
		||||
#include "scsi/utils.h"
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
 | 
			
		||||
/* Conflict between scsi/utils.h and libiscsi! :( */
 | 
			
		||||
#define SCSI_XFER_NONE ISCSI_XFER_NONE
 | 
			
		||||
#include <iscsi/iscsi.h>
 | 
			
		||||
#define inline __attribute__((gnu_inline))  /* required for libiscsi v1.9.0 */
 | 
			
		||||
#include <iscsi/scsi-lowlevel.h>
 | 
			
		||||
#undef inline
 | 
			
		||||
#undef SCSI_XFER_NONE
 | 
			
		||||
QEMU_BUILD_BUG_ON((int)SCSI_XFER_NONE != (int)ISCSI_XFER_NONE);
 | 
			
		||||
 | 
			
		||||
@@ -72,7 +68,6 @@ typedef struct IscsiLun {
 | 
			
		||||
    QemuMutex mutex;
 | 
			
		||||
    struct scsi_inquiry_logical_block_provisioning lbp;
 | 
			
		||||
    struct scsi_inquiry_block_limits bl;
 | 
			
		||||
    struct scsi_inquiry_device_designator *dd;
 | 
			
		||||
    unsigned char *zeroblock;
 | 
			
		||||
    /* The allocmap tracks which clusters (pages) on the iSCSI target are
 | 
			
		||||
     * allocated and which are not. In case a target returns zeros for
 | 
			
		||||
@@ -119,6 +114,7 @@ typedef struct IscsiAIOCB {
 | 
			
		||||
    QEMUBH *bh;
 | 
			
		||||
    IscsiLun *iscsilun;
 | 
			
		||||
    struct scsi_task *task;
 | 
			
		||||
    uint8_t *buf;
 | 
			
		||||
    int status;
 | 
			
		||||
    int64_t sector_num;
 | 
			
		||||
    int nb_sectors;
 | 
			
		||||
@@ -126,7 +122,6 @@ typedef struct IscsiAIOCB {
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
    sg_io_hdr_t *ioh;
 | 
			
		||||
#endif
 | 
			
		||||
    bool cancelled;
 | 
			
		||||
} IscsiAIOCB;
 | 
			
		||||
 | 
			
		||||
/* libiscsi uses time_t so its enough to process events every second */
 | 
			
		||||
@@ -152,6 +147,9 @@ iscsi_bh_cb(void *p)
 | 
			
		||||
 | 
			
		||||
    qemu_bh_delete(acb->bh);
 | 
			
		||||
 | 
			
		||||
    g_free(acb->buf);
 | 
			
		||||
    acb->buf = NULL;
 | 
			
		||||
 | 
			
		||||
    acb->common.cb(acb->common.opaque, acb->status);
 | 
			
		||||
 | 
			
		||||
    if (acb->task != NULL) {
 | 
			
		||||
@@ -290,20 +288,14 @@ static void iscsi_co_init_iscsitask(IscsiLun *iscsilun, struct IscsiTask *iTask)
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called (via iscsi_service) with QemuMutex held. */
 | 
			
		||||
static void
 | 
			
		||||
iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data,
 | 
			
		||||
                    void *private_data)
 | 
			
		||||
{
 | 
			
		||||
    IscsiAIOCB *acb = private_data;
 | 
			
		||||
 | 
			
		||||
    /* If the command callback hasn't been called yet, drop the task */
 | 
			
		||||
    if (!acb->bh) {
 | 
			
		||||
        /* Call iscsi_aio_ioctl_cb() with SCSI_STATUS_CANCELLED */
 | 
			
		||||
        iscsi_scsi_cancel_task(iscsi, acb->task);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_aio_unref(acb); /* acquired in iscsi_aio_cancel() */
 | 
			
		||||
    acb->status = -ECANCELED;
 | 
			
		||||
    iscsi_schedule_bh(acb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@@ -312,25 +304,14 @@ iscsi_aio_cancel(BlockAIOCB *blockacb)
 | 
			
		||||
    IscsiAIOCB *acb = (IscsiAIOCB *)blockacb;
 | 
			
		||||
    IscsiLun *iscsilun = acb->iscsilun;
 | 
			
		||||
 | 
			
		||||
    qemu_mutex_lock(&iscsilun->mutex);
 | 
			
		||||
 | 
			
		||||
    /* If it was cancelled or completed already, our work is done here */
 | 
			
		||||
    if (acb->cancelled || acb->status != -EINPROGRESS) {
 | 
			
		||||
        qemu_mutex_unlock(&iscsilun->mutex);
 | 
			
		||||
    if (acb->status != -EINPROGRESS) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    acb->cancelled = true;
 | 
			
		||||
 | 
			
		||||
    qemu_aio_ref(acb); /* released in iscsi_abort_task_cb() */
 | 
			
		||||
 | 
			
		||||
    /* send a task mgmt call to the target to cancel the task on the target */
 | 
			
		||||
    if (iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task,
 | 
			
		||||
                                         iscsi_abort_task_cb, acb) < 0) {
 | 
			
		||||
        qemu_aio_unref(acb); /* since iscsi_abort_task_cb() won't be called */
 | 
			
		||||
    }
 | 
			
		||||
    iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task,
 | 
			
		||||
                                     iscsi_abort_task_cb, acb);
 | 
			
		||||
 | 
			
		||||
    qemu_mutex_unlock(&iscsilun->mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const AIOCBInfo iscsi_aiocb_info = {
 | 
			
		||||
@@ -364,8 +345,6 @@ static void iscsi_timed_check_events(void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    IscsiLun *iscsilun = opaque;
 | 
			
		||||
 | 
			
		||||
    qemu_mutex_lock(&iscsilun->mutex);
 | 
			
		||||
 | 
			
		||||
    /* check for timed out requests */
 | 
			
		||||
    iscsi_service(iscsilun->iscsi, 0);
 | 
			
		||||
 | 
			
		||||
@@ -378,8 +357,6 @@ static void iscsi_timed_check_events(void *opaque)
 | 
			
		||||
     * to return to service once this situation changes. */
 | 
			
		||||
    iscsi_set_events(iscsilun);
 | 
			
		||||
 | 
			
		||||
    qemu_mutex_unlock(&iscsilun->mutex);
 | 
			
		||||
 | 
			
		||||
    timer_mod(iscsilun->event_timer,
 | 
			
		||||
              qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL);
 | 
			
		||||
}
 | 
			
		||||
@@ -578,19 +555,8 @@ static inline bool iscsi_allocmap_is_valid(IscsiLun *iscsilun,
 | 
			
		||||
                               offset / iscsilun->cluster_size) == size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void coroutine_fn iscsi_co_wait_for_task(IscsiTask *iTask,
 | 
			
		||||
                                                IscsiLun *iscsilun)
 | 
			
		||||
{
 | 
			
		||||
    while (!iTask->complete) {
 | 
			
		||||
        iscsi_set_events(iscsilun);
 | 
			
		||||
        qemu_mutex_unlock(&iscsilun->mutex);
 | 
			
		||||
        qemu_coroutine_yield();
 | 
			
		||||
        qemu_mutex_lock(&iscsilun->mutex);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn
 | 
			
		||||
iscsi_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
 | 
			
		||||
iscsi_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
 | 
			
		||||
                      QEMUIOVector *iov, int flags)
 | 
			
		||||
{
 | 
			
		||||
    IscsiLun *iscsilun = bs->opaque;
 | 
			
		||||
@@ -650,7 +616,12 @@ retry:
 | 
			
		||||
    scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov,
 | 
			
		||||
                          iov->niov);
 | 
			
		||||
#endif
 | 
			
		||||
    iscsi_co_wait_for_task(&iTask, iscsilun);
 | 
			
		||||
    while (!iTask.complete) {
 | 
			
		||||
        iscsi_set_events(iscsilun);
 | 
			
		||||
        qemu_mutex_unlock(&iscsilun->mutex);
 | 
			
		||||
        qemu_coroutine_yield();
 | 
			
		||||
        qemu_mutex_lock(&iscsilun->mutex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (iTask.task != NULL) {
 | 
			
		||||
        scsi_free_scsi_task(iTask.task);
 | 
			
		||||
@@ -721,7 +692,13 @@ retry:
 | 
			
		||||
        ret = -ENOMEM;
 | 
			
		||||
        goto out_unlock;
 | 
			
		||||
    }
 | 
			
		||||
    iscsi_co_wait_for_task(&iTask, iscsilun);
 | 
			
		||||
 | 
			
		||||
    while (!iTask.complete) {
 | 
			
		||||
        iscsi_set_events(iscsilun);
 | 
			
		||||
        qemu_mutex_unlock(&iscsilun->mutex);
 | 
			
		||||
        qemu_coroutine_yield();
 | 
			
		||||
        qemu_mutex_lock(&iscsilun->mutex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (iTask.do_retry) {
 | 
			
		||||
        if (iTask.task != NULL) {
 | 
			
		||||
@@ -755,7 +732,7 @@ retry:
 | 
			
		||||
        goto out_unlock;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *pnum = (int64_t) lbasd->num_blocks * iscsilun->block_size;
 | 
			
		||||
    *pnum = lbasd->num_blocks * iscsilun->block_size;
 | 
			
		||||
 | 
			
		||||
    if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED ||
 | 
			
		||||
        lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) {
 | 
			
		||||
@@ -885,8 +862,13 @@ retry:
 | 
			
		||||
#if LIBISCSI_API_VERSION < (20160603)
 | 
			
		||||
    scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov);
 | 
			
		||||
#endif
 | 
			
		||||
    while (!iTask.complete) {
 | 
			
		||||
        iscsi_set_events(iscsilun);
 | 
			
		||||
        qemu_mutex_unlock(&iscsilun->mutex);
 | 
			
		||||
        qemu_coroutine_yield();
 | 
			
		||||
        qemu_mutex_lock(&iscsilun->mutex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    iscsi_co_wait_for_task(&iTask, iscsilun);
 | 
			
		||||
    if (iTask.task != NULL) {
 | 
			
		||||
        scsi_free_scsi_task(iTask.task);
 | 
			
		||||
        iTask.task = NULL;
 | 
			
		||||
@@ -923,7 +905,12 @@ retry:
 | 
			
		||||
        return -ENOMEM;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    iscsi_co_wait_for_task(&iTask, iscsilun);
 | 
			
		||||
    while (!iTask.complete) {
 | 
			
		||||
        iscsi_set_events(iscsilun);
 | 
			
		||||
        qemu_mutex_unlock(&iscsilun->mutex);
 | 
			
		||||
        qemu_coroutine_yield();
 | 
			
		||||
        qemu_mutex_lock(&iscsilun->mutex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (iTask.task != NULL) {
 | 
			
		||||
        scsi_free_scsi_task(iTask.task);
 | 
			
		||||
@@ -953,13 +940,8 @@ iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status,
 | 
			
		||||
{
 | 
			
		||||
    IscsiAIOCB *acb = opaque;
 | 
			
		||||
 | 
			
		||||
    if (status == SCSI_STATUS_CANCELLED) {
 | 
			
		||||
        if (!acb->bh) {
 | 
			
		||||
            acb->status = -ECANCELED;
 | 
			
		||||
            iscsi_schedule_bh(acb);
 | 
			
		||||
        }
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    g_free(acb->buf);
 | 
			
		||||
    acb->buf = NULL;
 | 
			
		||||
 | 
			
		||||
    acb->status = 0;
 | 
			
		||||
    if (status < 0) {
 | 
			
		||||
@@ -1035,8 +1017,8 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
 | 
			
		||||
    acb->iscsilun = iscsilun;
 | 
			
		||||
    acb->bh          = NULL;
 | 
			
		||||
    acb->status      = -EINPROGRESS;
 | 
			
		||||
    acb->buf         = NULL;
 | 
			
		||||
    acb->ioh         = buf;
 | 
			
		||||
    acb->cancelled   = false;
 | 
			
		||||
 | 
			
		||||
    if (req != SG_IO) {
 | 
			
		||||
        iscsi_ioctl_handle_emulated(acb, req, buf);
 | 
			
		||||
@@ -1160,7 +1142,12 @@ retry:
 | 
			
		||||
        goto out_unlock;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    iscsi_co_wait_for_task(&iTask, iscsilun);
 | 
			
		||||
    while (!iTask.complete) {
 | 
			
		||||
        iscsi_set_events(iscsilun);
 | 
			
		||||
        qemu_mutex_unlock(&iscsilun->mutex);
 | 
			
		||||
        qemu_coroutine_yield();
 | 
			
		||||
        qemu_mutex_lock(&iscsilun->mutex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (iTask.task != NULL) {
 | 
			
		||||
        scsi_free_scsi_task(iTask.task);
 | 
			
		||||
@@ -1256,7 +1243,12 @@ retry:
 | 
			
		||||
        return -ENOMEM;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    iscsi_co_wait_for_task(&iTask, iscsilun);
 | 
			
		||||
    while (!iTask.complete) {
 | 
			
		||||
        iscsi_set_events(iscsilun);
 | 
			
		||||
        qemu_mutex_unlock(&iscsilun->mutex);
 | 
			
		||||
        qemu_coroutine_yield();
 | 
			
		||||
        qemu_mutex_lock(&iscsilun->mutex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (iTask.status == SCSI_STATUS_CHECK_CONDITION &&
 | 
			
		||||
        iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST &&
 | 
			
		||||
@@ -1740,34 +1732,14 @@ static QemuOptsList runtime_opts = {
 | 
			
		||||
            .name = "timeout",
 | 
			
		||||
            .type = QEMU_OPT_NUMBER,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = "filename",
 | 
			
		||||
            .type = QEMU_OPT_STRING,
 | 
			
		||||
        },
 | 
			
		||||
        { /* end of list */ }
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void iscsi_save_designator(IscsiLun *lun,
 | 
			
		||||
                                  struct scsi_inquiry_device_identification *inq_di)
 | 
			
		||||
{
 | 
			
		||||
    struct scsi_inquiry_device_designator *desig, *copy = NULL;
 | 
			
		||||
 | 
			
		||||
    for (desig = inq_di->designators; desig; desig = desig->next) {
 | 
			
		||||
        if (desig->association ||
 | 
			
		||||
            desig->designator_type > SCSI_DESIGNATOR_TYPE_NAA) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        /* NAA works better than T10 vendor ID based designator. */
 | 
			
		||||
        if (!copy || copy->designator_type < desig->designator_type) {
 | 
			
		||||
            copy = desig;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (copy) {
 | 
			
		||||
        lun->dd = g_new(struct scsi_inquiry_device_designator, 1);
 | 
			
		||||
        *lun->dd = *copy;
 | 
			
		||||
        lun->dd->next = NULL;
 | 
			
		||||
        lun->dd->designator = g_malloc(copy->designator_length);
 | 
			
		||||
        memcpy(lun->dd->designator, copy->designator, copy->designator_length);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
                      Error **errp)
 | 
			
		||||
{
 | 
			
		||||
@@ -1779,12 +1751,27 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    char *initiator_name = NULL;
 | 
			
		||||
    QemuOpts *opts;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    const char *transport_name, *portal, *target;
 | 
			
		||||
    const char *transport_name, *portal, *target, *filename;
 | 
			
		||||
#if LIBISCSI_API_VERSION >= (20160603)
 | 
			
		||||
    enum iscsi_transport_type transport;
 | 
			
		||||
#endif
 | 
			
		||||
    int i, ret = 0, timeout = 0, lun;
 | 
			
		||||
 | 
			
		||||
    /* If we are given a filename, parse the filename, with precedence given to
 | 
			
		||||
     * filename encoded options */
 | 
			
		||||
    filename = qdict_get_try_str(options, "filename");
 | 
			
		||||
    if (filename) {
 | 
			
		||||
        warn_report("'filename' option specified. "
 | 
			
		||||
                    "This is an unsupported option, and may be deprecated "
 | 
			
		||||
                    "in the future");
 | 
			
		||||
        iscsi_parse_filename(filename, options, &local_err);
 | 
			
		||||
        if (local_err) {
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
            error_propagate(errp, local_err);
 | 
			
		||||
            goto exit;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
 | 
			
		||||
    qemu_opts_absorb_qdict(opts, options, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
@@ -1869,7 +1856,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    iscsi_set_timeout(iscsi, timeout);
 | 
			
		||||
#else
 | 
			
		||||
    if (timeout) {
 | 
			
		||||
        warn_report("iSCSI: ignoring timeout value for libiscsi <1.15.0");
 | 
			
		||||
        error_report("iSCSI: ignoring timeout value for libiscsi <1.15.0");
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -1903,12 +1890,10 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    /* Check the write protect flag of the LUN if we want to write */
 | 
			
		||||
    if (iscsilun->type == TYPE_DISK && (flags & BDRV_O_RDWR) &&
 | 
			
		||||
        iscsilun->write_protected) {
 | 
			
		||||
        ret = bdrv_apply_auto_read_only(bs, "LUN is write protected", errp);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
        error_setg(errp, "Cannot open a write protected LUN as read-write");
 | 
			
		||||
        ret = -EACCES;
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
        flags &= ~BDRV_O_RDWR;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    iscsi_readcapacity_sync(iscsilun, &local_err);
 | 
			
		||||
    if (local_err != NULL) {
 | 
			
		||||
@@ -1937,7 +1922,6 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
        struct scsi_task *inq_task;
 | 
			
		||||
        struct scsi_inquiry_logical_block_provisioning *inq_lbp;
 | 
			
		||||
        struct scsi_inquiry_block_limits *inq_bl;
 | 
			
		||||
        struct scsi_inquiry_device_identification *inq_di;
 | 
			
		||||
        switch (inq_vpd->pages[i]) {
 | 
			
		||||
        case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING:
 | 
			
		||||
            inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
 | 
			
		||||
@@ -1963,17 +1947,6 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
                   sizeof(struct scsi_inquiry_block_limits));
 | 
			
		||||
            scsi_free_scsi_task(inq_task);
 | 
			
		||||
            break;
 | 
			
		||||
        case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION:
 | 
			
		||||
            inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
 | 
			
		||||
                                    SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION,
 | 
			
		||||
                                    (void **) &inq_di, errp);
 | 
			
		||||
            if (inq_task == NULL) {
 | 
			
		||||
                ret = -EINVAL;
 | 
			
		||||
                goto out;
 | 
			
		||||
            }
 | 
			
		||||
            iscsi_save_designator(iscsilun, inq_di);
 | 
			
		||||
            scsi_free_scsi_task(inq_task);
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
@@ -2016,7 +1989,7 @@ out:
 | 
			
		||||
        }
 | 
			
		||||
        memset(iscsilun, 0, sizeof(IscsiLun));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -2030,10 +2003,6 @@ static void iscsi_close(BlockDriverState *bs)
 | 
			
		||||
        iscsi_logout_sync(iscsi);
 | 
			
		||||
    }
 | 
			
		||||
    iscsi_destroy_context(iscsi);
 | 
			
		||||
    if (iscsilun->dd) {
 | 
			
		||||
        g_free(iscsilun->dd->designator);
 | 
			
		||||
        g_free(iscsilun->dd);
 | 
			
		||||
    }
 | 
			
		||||
    g_free(iscsilun->zeroblock);
 | 
			
		||||
    iscsi_allocmap_free(iscsilun);
 | 
			
		||||
    qemu_mutex_destroy(&iscsilun->mutex);
 | 
			
		||||
@@ -2113,7 +2082,7 @@ static void iscsi_reopen_commit(BDRVReopenState *reopen_state)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
static int iscsi_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
                          PreallocMode prealloc, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    IscsiLun *iscsilun = bs->opaque;
 | 
			
		||||
@@ -2174,7 +2143,7 @@ static int coroutine_fn iscsi_co_create_opts(const char *filename, QemuOpts *opt
 | 
			
		||||
    } else {
 | 
			
		||||
        ret = iscsi_open(bs, bs_options, 0, NULL);
 | 
			
		||||
    }
 | 
			
		||||
    qobject_unref(bs_options);
 | 
			
		||||
    QDECREF(bs_options);
 | 
			
		||||
 | 
			
		||||
    if (ret != 0) {
 | 
			
		||||
        goto out;
 | 
			
		||||
@@ -2215,226 +2184,6 @@ static void coroutine_fn iscsi_co_invalidate_cache(BlockDriverState *bs,
 | 
			
		||||
    iscsi_allocmap_invalidate(iscsilun);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn iscsi_co_copy_range_from(BlockDriverState *bs,
 | 
			
		||||
                                                 BdrvChild *src,
 | 
			
		||||
                                                 uint64_t src_offset,
 | 
			
		||||
                                                 BdrvChild *dst,
 | 
			
		||||
                                                 uint64_t dst_offset,
 | 
			
		||||
                                                 uint64_t bytes,
 | 
			
		||||
                                                 BdrvRequestFlags read_flags,
 | 
			
		||||
                                                 BdrvRequestFlags write_flags)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes,
 | 
			
		||||
                                 read_flags, write_flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct scsi_task *iscsi_xcopy_task(int param_len)
 | 
			
		||||
{
 | 
			
		||||
    struct scsi_task *task;
 | 
			
		||||
 | 
			
		||||
    task = g_new0(struct scsi_task, 1);
 | 
			
		||||
 | 
			
		||||
    task->cdb[0]     = EXTENDED_COPY;
 | 
			
		||||
    task->cdb[10]    = (param_len >> 24) & 0xFF;
 | 
			
		||||
    task->cdb[11]    = (param_len >> 16) & 0xFF;
 | 
			
		||||
    task->cdb[12]    = (param_len >> 8) & 0xFF;
 | 
			
		||||
    task->cdb[13]    = param_len & 0xFF;
 | 
			
		||||
    task->cdb_size   = 16;
 | 
			
		||||
    task->xfer_dir   = SCSI_XFER_WRITE;
 | 
			
		||||
    task->expxferlen = param_len;
 | 
			
		||||
 | 
			
		||||
    return task;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void iscsi_populate_target_desc(unsigned char *desc, IscsiLun *lun)
 | 
			
		||||
{
 | 
			
		||||
    struct scsi_inquiry_device_designator *dd = lun->dd;
 | 
			
		||||
 | 
			
		||||
    memset(desc, 0, 32);
 | 
			
		||||
    desc[0] = 0xE4; /* IDENT_DESCR_TGT_DESCR */
 | 
			
		||||
    desc[4] = dd->code_set;
 | 
			
		||||
    desc[5] = (dd->designator_type & 0xF)
 | 
			
		||||
        | ((dd->association & 3) << 4);
 | 
			
		||||
    desc[7] = dd->designator_length;
 | 
			
		||||
    memcpy(desc + 8, dd->designator, MIN(dd->designator_length, 20));
 | 
			
		||||
 | 
			
		||||
    desc[28] = 0;
 | 
			
		||||
    desc[29] = (lun->block_size >> 16) & 0xFF;
 | 
			
		||||
    desc[30] = (lun->block_size >> 8) & 0xFF;
 | 
			
		||||
    desc[31] = lun->block_size & 0xFF;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void iscsi_xcopy_desc_hdr(uint8_t *hdr, int dc, int cat, int src_index,
 | 
			
		||||
                                 int dst_index)
 | 
			
		||||
{
 | 
			
		||||
    hdr[0] = 0x02; /* BLK_TO_BLK_SEG_DESCR */
 | 
			
		||||
    hdr[1] = ((dc << 1) | cat) & 0xFF;
 | 
			
		||||
    hdr[2] = (XCOPY_BLK2BLK_SEG_DESC_SIZE >> 8) & 0xFF;
 | 
			
		||||
    /* don't account for the first 4 bytes in descriptor header*/
 | 
			
		||||
    hdr[3] = (XCOPY_BLK2BLK_SEG_DESC_SIZE - 4 /* SEG_DESC_SRC_INDEX_OFFSET */) & 0xFF;
 | 
			
		||||
    hdr[4] = (src_index >> 8) & 0xFF;
 | 
			
		||||
    hdr[5] = src_index & 0xFF;
 | 
			
		||||
    hdr[6] = (dst_index >> 8) & 0xFF;
 | 
			
		||||
    hdr[7] = dst_index & 0xFF;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void iscsi_xcopy_populate_desc(uint8_t *desc, int dc, int cat,
 | 
			
		||||
                                      int src_index, int dst_index, int num_blks,
 | 
			
		||||
                                      uint64_t src_lba, uint64_t dst_lba)
 | 
			
		||||
{
 | 
			
		||||
    iscsi_xcopy_desc_hdr(desc, dc, cat, src_index, dst_index);
 | 
			
		||||
 | 
			
		||||
    /* The caller should verify the request size */
 | 
			
		||||
    assert(num_blks < 65536);
 | 
			
		||||
    desc[10] = (num_blks >> 8) & 0xFF;
 | 
			
		||||
    desc[11] = num_blks & 0xFF;
 | 
			
		||||
    desc[12] = (src_lba >> 56) & 0xFF;
 | 
			
		||||
    desc[13] = (src_lba >> 48) & 0xFF;
 | 
			
		||||
    desc[14] = (src_lba >> 40) & 0xFF;
 | 
			
		||||
    desc[15] = (src_lba >> 32) & 0xFF;
 | 
			
		||||
    desc[16] = (src_lba >> 24) & 0xFF;
 | 
			
		||||
    desc[17] = (src_lba >> 16) & 0xFF;
 | 
			
		||||
    desc[18] = (src_lba >> 8) & 0xFF;
 | 
			
		||||
    desc[19] = src_lba & 0xFF;
 | 
			
		||||
    desc[20] = (dst_lba >> 56) & 0xFF;
 | 
			
		||||
    desc[21] = (dst_lba >> 48) & 0xFF;
 | 
			
		||||
    desc[22] = (dst_lba >> 40) & 0xFF;
 | 
			
		||||
    desc[23] = (dst_lba >> 32) & 0xFF;
 | 
			
		||||
    desc[24] = (dst_lba >> 24) & 0xFF;
 | 
			
		||||
    desc[25] = (dst_lba >> 16) & 0xFF;
 | 
			
		||||
    desc[26] = (dst_lba >> 8) & 0xFF;
 | 
			
		||||
    desc[27] = dst_lba & 0xFF;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void iscsi_xcopy_populate_header(unsigned char *buf, int list_id, int str,
 | 
			
		||||
                                        int list_id_usage, int prio,
 | 
			
		||||
                                        int tgt_desc_len,
 | 
			
		||||
                                        int seg_desc_len, int inline_data_len)
 | 
			
		||||
{
 | 
			
		||||
    buf[0] = list_id;
 | 
			
		||||
    buf[1] = ((str & 1) << 5) | ((list_id_usage & 3) << 3) | (prio & 7);
 | 
			
		||||
    buf[2] = (tgt_desc_len >> 8) & 0xFF;
 | 
			
		||||
    buf[3] = tgt_desc_len & 0xFF;
 | 
			
		||||
    buf[8] = (seg_desc_len >> 24) & 0xFF;
 | 
			
		||||
    buf[9] = (seg_desc_len >> 16) & 0xFF;
 | 
			
		||||
    buf[10] = (seg_desc_len >> 8) & 0xFF;
 | 
			
		||||
    buf[11] = seg_desc_len & 0xFF;
 | 
			
		||||
    buf[12] = (inline_data_len >> 24) & 0xFF;
 | 
			
		||||
    buf[13] = (inline_data_len >> 16) & 0xFF;
 | 
			
		||||
    buf[14] = (inline_data_len >> 8) & 0xFF;
 | 
			
		||||
    buf[15] = inline_data_len & 0xFF;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void iscsi_xcopy_data(struct iscsi_data *data,
 | 
			
		||||
                             IscsiLun *src, int64_t src_lba,
 | 
			
		||||
                             IscsiLun *dst, int64_t dst_lba,
 | 
			
		||||
                             uint16_t num_blocks)
 | 
			
		||||
{
 | 
			
		||||
    uint8_t *buf;
 | 
			
		||||
    const int src_offset = XCOPY_DESC_OFFSET;
 | 
			
		||||
    const int dst_offset = XCOPY_DESC_OFFSET + IDENT_DESCR_TGT_DESCR_SIZE;
 | 
			
		||||
    const int seg_offset = dst_offset + IDENT_DESCR_TGT_DESCR_SIZE;
 | 
			
		||||
 | 
			
		||||
    data->size = XCOPY_DESC_OFFSET +
 | 
			
		||||
                 IDENT_DESCR_TGT_DESCR_SIZE * 2 +
 | 
			
		||||
                 XCOPY_BLK2BLK_SEG_DESC_SIZE;
 | 
			
		||||
    data->data = g_malloc0(data->size);
 | 
			
		||||
    buf = data->data;
 | 
			
		||||
 | 
			
		||||
    /* Initialise the parameter list header */
 | 
			
		||||
    iscsi_xcopy_populate_header(buf, 1, 0, 2 /* LIST_ID_USAGE_DISCARD */,
 | 
			
		||||
                                0, 2 * IDENT_DESCR_TGT_DESCR_SIZE,
 | 
			
		||||
                                XCOPY_BLK2BLK_SEG_DESC_SIZE,
 | 
			
		||||
                                0);
 | 
			
		||||
 | 
			
		||||
    /* Initialise CSCD list with one src + one dst descriptor */
 | 
			
		||||
    iscsi_populate_target_desc(&buf[src_offset], src);
 | 
			
		||||
    iscsi_populate_target_desc(&buf[dst_offset], dst);
 | 
			
		||||
 | 
			
		||||
    /* Initialise one segment descriptor */
 | 
			
		||||
    iscsi_xcopy_populate_desc(&buf[seg_offset], 0, 0, 0, 1, num_blocks,
 | 
			
		||||
                              src_lba, dst_lba);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn iscsi_co_copy_range_to(BlockDriverState *bs,
 | 
			
		||||
                                               BdrvChild *src,
 | 
			
		||||
                                               uint64_t src_offset,
 | 
			
		||||
                                               BdrvChild *dst,
 | 
			
		||||
                                               uint64_t dst_offset,
 | 
			
		||||
                                               uint64_t bytes,
 | 
			
		||||
                                               BdrvRequestFlags read_flags,
 | 
			
		||||
                                               BdrvRequestFlags write_flags)
 | 
			
		||||
{
 | 
			
		||||
    IscsiLun *dst_lun = dst->bs->opaque;
 | 
			
		||||
    IscsiLun *src_lun;
 | 
			
		||||
    struct IscsiTask iscsi_task;
 | 
			
		||||
    struct iscsi_data data;
 | 
			
		||||
    int r = 0;
 | 
			
		||||
    int block_size;
 | 
			
		||||
 | 
			
		||||
    if (src->bs->drv->bdrv_co_copy_range_to != iscsi_co_copy_range_to) {
 | 
			
		||||
        return -ENOTSUP;
 | 
			
		||||
    }
 | 
			
		||||
    src_lun = src->bs->opaque;
 | 
			
		||||
 | 
			
		||||
    if (!src_lun->dd || !dst_lun->dd) {
 | 
			
		||||
        return -ENOTSUP;
 | 
			
		||||
    }
 | 
			
		||||
    if (!is_byte_request_lun_aligned(dst_offset, bytes, dst_lun)) {
 | 
			
		||||
        return -ENOTSUP;
 | 
			
		||||
    }
 | 
			
		||||
    if (!is_byte_request_lun_aligned(src_offset, bytes, src_lun)) {
 | 
			
		||||
        return -ENOTSUP;
 | 
			
		||||
    }
 | 
			
		||||
    if (dst_lun->block_size != src_lun->block_size ||
 | 
			
		||||
        !dst_lun->block_size) {
 | 
			
		||||
        return -ENOTSUP;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    block_size = dst_lun->block_size;
 | 
			
		||||
    if (bytes / block_size > 65535) {
 | 
			
		||||
        return -ENOTSUP;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    iscsi_xcopy_data(&data,
 | 
			
		||||
                     src_lun, src_offset / block_size,
 | 
			
		||||
                     dst_lun, dst_offset / block_size,
 | 
			
		||||
                     bytes / block_size);
 | 
			
		||||
 | 
			
		||||
    iscsi_co_init_iscsitask(dst_lun, &iscsi_task);
 | 
			
		||||
 | 
			
		||||
    qemu_mutex_lock(&dst_lun->mutex);
 | 
			
		||||
    iscsi_task.task = iscsi_xcopy_task(data.size);
 | 
			
		||||
retry:
 | 
			
		||||
    if (iscsi_scsi_command_async(dst_lun->iscsi, dst_lun->lun,
 | 
			
		||||
                                 iscsi_task.task, iscsi_co_generic_cb,
 | 
			
		||||
                                 &data,
 | 
			
		||||
                                 &iscsi_task) != 0) {
 | 
			
		||||
        r = -EIO;
 | 
			
		||||
        goto out_unlock;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    iscsi_co_wait_for_task(&iscsi_task, dst_lun);
 | 
			
		||||
 | 
			
		||||
    if (iscsi_task.do_retry) {
 | 
			
		||||
        iscsi_task.complete = 0;
 | 
			
		||||
        goto retry;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (iscsi_task.status != SCSI_STATUS_GOOD) {
 | 
			
		||||
        r = iscsi_task.err_code;
 | 
			
		||||
        goto out_unlock;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
out_unlock:
 | 
			
		||||
 | 
			
		||||
    trace_iscsi_xcopy(src_lun, src_offset, dst_lun, dst_offset, bytes, r);
 | 
			
		||||
    g_free(iscsi_task.task);
 | 
			
		||||
    qemu_mutex_unlock(&dst_lun->mutex);
 | 
			
		||||
    g_free(iscsi_task.err_str);
 | 
			
		||||
    return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static QemuOptsList iscsi_create_opts = {
 | 
			
		||||
    .name = "iscsi-create-opts",
 | 
			
		||||
    .head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head),
 | 
			
		||||
@@ -2464,16 +2213,14 @@ static BlockDriver bdrv_iscsi = {
 | 
			
		||||
 | 
			
		||||
    .bdrv_getlength  = iscsi_getlength,
 | 
			
		||||
    .bdrv_get_info   = iscsi_get_info,
 | 
			
		||||
    .bdrv_co_truncate    = iscsi_co_truncate,
 | 
			
		||||
    .bdrv_truncate   = iscsi_truncate,
 | 
			
		||||
    .bdrv_refresh_limits = iscsi_refresh_limits,
 | 
			
		||||
 | 
			
		||||
    .bdrv_co_block_status  = iscsi_co_block_status,
 | 
			
		||||
    .bdrv_co_pdiscard      = iscsi_co_pdiscard,
 | 
			
		||||
    .bdrv_co_copy_range_from = iscsi_co_copy_range_from,
 | 
			
		||||
    .bdrv_co_copy_range_to  = iscsi_co_copy_range_to,
 | 
			
		||||
    .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes,
 | 
			
		||||
    .bdrv_co_readv         = iscsi_co_readv,
 | 
			
		||||
    .bdrv_co_writev        = iscsi_co_writev,
 | 
			
		||||
    .bdrv_co_writev_flags  = iscsi_co_writev_flags,
 | 
			
		||||
    .bdrv_co_flush_to_disk = iscsi_co_flush,
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
@@ -2497,20 +2244,18 @@ static BlockDriver bdrv_iser = {
 | 
			
		||||
    .create_opts            = &iscsi_create_opts,
 | 
			
		||||
    .bdrv_reopen_prepare    = iscsi_reopen_prepare,
 | 
			
		||||
    .bdrv_reopen_commit     = iscsi_reopen_commit,
 | 
			
		||||
    .bdrv_co_invalidate_cache  = iscsi_co_invalidate_cache,
 | 
			
		||||
    .bdrv_invalidate_cache  = iscsi_invalidate_cache,
 | 
			
		||||
 | 
			
		||||
    .bdrv_getlength  = iscsi_getlength,
 | 
			
		||||
    .bdrv_get_info   = iscsi_get_info,
 | 
			
		||||
    .bdrv_co_truncate    = iscsi_co_truncate,
 | 
			
		||||
    .bdrv_truncate   = iscsi_truncate,
 | 
			
		||||
    .bdrv_refresh_limits = iscsi_refresh_limits,
 | 
			
		||||
 | 
			
		||||
    .bdrv_co_block_status  = iscsi_co_block_status,
 | 
			
		||||
    .bdrv_co_pdiscard      = iscsi_co_pdiscard,
 | 
			
		||||
    .bdrv_co_copy_range_from = iscsi_co_copy_range_from,
 | 
			
		||||
    .bdrv_co_copy_range_to  = iscsi_co_copy_range_to,
 | 
			
		||||
    .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes,
 | 
			
		||||
    .bdrv_co_readv         = iscsi_co_readv,
 | 
			
		||||
    .bdrv_co_writev        = iscsi_co_writev,
 | 
			
		||||
    .bdrv_co_writev_flags  = iscsi_co_writev_flags,
 | 
			
		||||
    .bdrv_co_flush_to_disk = iscsi_co_flush,
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,6 @@
 | 
			
		||||
#include "block/raw-aio.h"
 | 
			
		||||
#include "qemu/event_notifier.h"
 | 
			
		||||
#include "qemu/coroutine.h"
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
 | 
			
		||||
#include <libaio.h>
 | 
			
		||||
 | 
			
		||||
@@ -234,9 +233,9 @@ static void qemu_laio_process_completions(LinuxAioState *s)
 | 
			
		||||
 | 
			
		||||
static void qemu_laio_process_completions_and_submit(LinuxAioState *s)
 | 
			
		||||
{
 | 
			
		||||
    aio_context_acquire(s->aio_context);
 | 
			
		||||
    qemu_laio_process_completions(s);
 | 
			
		||||
 | 
			
		||||
    aio_context_acquire(s->aio_context);
 | 
			
		||||
    if (!s->io_q.plugged && !QSIMPLEQ_EMPTY(&s->io_q.pending)) {
 | 
			
		||||
        ioq_submit(s);
 | 
			
		||||
    }
 | 
			
		||||
@@ -471,21 +470,16 @@ void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context)
 | 
			
		||||
                           qemu_laio_poll_cb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LinuxAioState *laio_init(Error **errp)
 | 
			
		||||
LinuxAioState *laio_init(void)
 | 
			
		||||
{
 | 
			
		||||
    int rc;
 | 
			
		||||
    LinuxAioState *s;
 | 
			
		||||
 | 
			
		||||
    s = g_malloc0(sizeof(*s));
 | 
			
		||||
    rc = event_notifier_init(&s->e, false);
 | 
			
		||||
    if (rc < 0) {
 | 
			
		||||
        error_setg_errno(errp, -rc, "failed to to initialize event notifier");
 | 
			
		||||
    if (event_notifier_init(&s->e, false) < 0) {
 | 
			
		||||
        goto out_free_state;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rc = io_setup(MAX_EVENTS, &s->ctx);
 | 
			
		||||
    if (rc < 0) {
 | 
			
		||||
        error_setg_errno(errp, -rc, "failed to create linux AIO context");
 | 
			
		||||
    if (io_setup(MAX_EVENTS, &s->ctx) != 0) {
 | 
			
		||||
        goto out_close_efd;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										869
									
								
								block/mirror.c
									
									
									
									
									
								
							
							
						
						
									
										869
									
								
								block/mirror.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -28,8 +28,6 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
#include "nbd-client.h"
 | 
			
		||||
 | 
			
		||||
@@ -53,13 +51,15 @@ static void nbd_teardown_connection(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    NBDClientSession *client = nbd_get_client_session(bs);
 | 
			
		||||
 | 
			
		||||
    assert(client->ioc);
 | 
			
		||||
    if (!client->ioc) { /* Already closed */
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* finish any pending coroutines */
 | 
			
		||||
    qio_channel_shutdown(client->ioc,
 | 
			
		||||
                         QIO_CHANNEL_SHUTDOWN_BOTH,
 | 
			
		||||
                         NULL);
 | 
			
		||||
    BDRV_POLL_WHILE(bs, client->connection_co);
 | 
			
		||||
    BDRV_POLL_WHILE(bs, client->read_reply_co);
 | 
			
		||||
 | 
			
		||||
    nbd_client_detach_aio_context(bs);
 | 
			
		||||
    object_unref(OBJECT(client->sioc));
 | 
			
		||||
@@ -68,7 +68,7 @@ static void nbd_teardown_connection(BlockDriverState *bs)
 | 
			
		||||
    client->ioc = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static coroutine_fn void nbd_connection_entry(void *opaque)
 | 
			
		||||
static coroutine_fn void nbd_read_reply_entry(void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    NBDClientSession *s = opaque;
 | 
			
		||||
    uint64_t i;
 | 
			
		||||
@@ -79,8 +79,7 @@ static coroutine_fn void nbd_connection_entry(void *opaque)
 | 
			
		||||
        assert(s->reply.handle == 0);
 | 
			
		||||
        ret = nbd_receive_reply(s->ioc, &s->reply, &local_err);
 | 
			
		||||
        if (local_err) {
 | 
			
		||||
            trace_nbd_read_reply_entry_fail(ret, error_get_pretty(local_err));
 | 
			
		||||
            error_free(local_err);
 | 
			
		||||
            error_report_err(local_err);
 | 
			
		||||
        }
 | 
			
		||||
        if (ret <= 0) {
 | 
			
		||||
            break;
 | 
			
		||||
@@ -100,14 +99,14 @@ static coroutine_fn void nbd_connection_entry(void *opaque)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* We're woken up again by the request itself.  Note that there
 | 
			
		||||
         * is no race between yielding and reentering connection_co.  This
 | 
			
		||||
         * is no race between yielding and reentering read_reply_co.  This
 | 
			
		||||
         * is because:
 | 
			
		||||
         *
 | 
			
		||||
         * - if the request runs on the same AioContext, it is only
 | 
			
		||||
         *   entered after we yield
 | 
			
		||||
         *
 | 
			
		||||
         * - if the request runs on a different AioContext, reentering
 | 
			
		||||
         *   connection_co happens through a bottom half, which can only
 | 
			
		||||
         *   read_reply_co happens through a bottom half, which can only
 | 
			
		||||
         *   run after we yield.
 | 
			
		||||
         */
 | 
			
		||||
        aio_co_wake(s->requests[i].coroutine);
 | 
			
		||||
@@ -116,8 +115,7 @@ static coroutine_fn void nbd_connection_entry(void *opaque)
 | 
			
		||||
 | 
			
		||||
    s->quit = true;
 | 
			
		||||
    nbd_recv_coroutines_wake_all(s);
 | 
			
		||||
    s->connection_co = NULL;
 | 
			
		||||
    aio_wait_kick();
 | 
			
		||||
    s->read_reply_co = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int nbd_co_send_request(BlockDriverState *bs,
 | 
			
		||||
@@ -152,7 +150,10 @@ static int nbd_co_send_request(BlockDriverState *bs,
 | 
			
		||||
        rc = -EIO;
 | 
			
		||||
        goto err;
 | 
			
		||||
    }
 | 
			
		||||
    assert(s->ioc);
 | 
			
		||||
    if (!s->ioc) {
 | 
			
		||||
        rc = -EPIPE;
 | 
			
		||||
        goto err;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (qiov) {
 | 
			
		||||
        qio_channel_set_cork(s->ioc, true);
 | 
			
		||||
@@ -227,52 +228,6 @@ static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk,
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* nbd_parse_blockstatus_payload
 | 
			
		||||
 * support only one extent in reply and only for
 | 
			
		||||
 * base:allocation context
 | 
			
		||||
 */
 | 
			
		||||
static int nbd_parse_blockstatus_payload(NBDClientSession *client,
 | 
			
		||||
                                         NBDStructuredReplyChunk *chunk,
 | 
			
		||||
                                         uint8_t *payload, uint64_t orig_length,
 | 
			
		||||
                                         NBDExtent *extent, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t context_id;
 | 
			
		||||
 | 
			
		||||
    if (chunk->length != sizeof(context_id) + sizeof(*extent)) {
 | 
			
		||||
        error_setg(errp, "Protocol error: invalid payload for "
 | 
			
		||||
                         "NBD_REPLY_TYPE_BLOCK_STATUS");
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    context_id = payload_advance32(&payload);
 | 
			
		||||
    if (client->info.context_id != context_id) {
 | 
			
		||||
        error_setg(errp, "Protocol error: unexpected context id %d for "
 | 
			
		||||
                         "NBD_REPLY_TYPE_BLOCK_STATUS, when negotiated context "
 | 
			
		||||
                         "id is %d", context_id,
 | 
			
		||||
                         client->info.context_id);
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    extent->length = payload_advance32(&payload);
 | 
			
		||||
    extent->flags = payload_advance32(&payload);
 | 
			
		||||
 | 
			
		||||
    if (extent->length == 0 ||
 | 
			
		||||
        (client->info.min_block && !QEMU_IS_ALIGNED(extent->length,
 | 
			
		||||
                                                    client->info.min_block))) {
 | 
			
		||||
        error_setg(errp, "Protocol error: server sent status chunk with "
 | 
			
		||||
                   "invalid length");
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* The server is allowed to send us extra information on the final
 | 
			
		||||
     * extent; just clamp it to the length we requested. */
 | 
			
		||||
    if (extent->length > orig_length) {
 | 
			
		||||
        extent->length = orig_length;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* nbd_parse_error_payload
 | 
			
		||||
 * on success @errp contains message describing nbd error reply
 | 
			
		||||
 */
 | 
			
		||||
@@ -333,9 +288,10 @@ static int nbd_co_receive_offset_data_payload(NBDClientSession *s,
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (nbd_read64(s->ioc, &offset, "OFFSET_DATA offset", errp) < 0) {
 | 
			
		||||
    if (nbd_read(s->ioc, &offset, sizeof(offset), errp) < 0) {
 | 
			
		||||
        return -EIO;
 | 
			
		||||
    }
 | 
			
		||||
    be64_to_cpus(&offset);
 | 
			
		||||
 | 
			
		||||
    data_size = chunk->length - sizeof(offset);
 | 
			
		||||
    assert(data_size);
 | 
			
		||||
@@ -382,7 +338,7 @@ static coroutine_fn int nbd_co_receive_structured_payload(
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *payload = g_new(char, len);
 | 
			
		||||
    ret = nbd_read(s->ioc, *payload, len, "structured payload", errp);
 | 
			
		||||
    ret = nbd_read(s->ioc, *payload, len, errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        g_free(*payload);
 | 
			
		||||
        *payload = NULL;
 | 
			
		||||
@@ -420,15 +376,14 @@ static coroutine_fn int nbd_co_do_receive_one_chunk(
 | 
			
		||||
    }
 | 
			
		||||
    *request_ret = 0;
 | 
			
		||||
 | 
			
		||||
    /* Wait until we're woken up by nbd_connection_entry.  */
 | 
			
		||||
    /* Wait until we're woken up by nbd_read_reply_entry.  */
 | 
			
		||||
    s->requests[i].receiving = true;
 | 
			
		||||
    qemu_coroutine_yield();
 | 
			
		||||
    s->requests[i].receiving = false;
 | 
			
		||||
    if (s->quit) {
 | 
			
		||||
    if (!s->ioc || s->quit) {
 | 
			
		||||
        error_setg(errp, "Connection closed");
 | 
			
		||||
        return -EIO;
 | 
			
		||||
    }
 | 
			
		||||
    assert(s->ioc);
 | 
			
		||||
 | 
			
		||||
    assert(s->reply.handle == handle);
 | 
			
		||||
 | 
			
		||||
@@ -495,29 +450,30 @@ static coroutine_fn int nbd_co_do_receive_one_chunk(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* nbd_co_receive_one_chunk
 | 
			
		||||
 * Read reply, wake up connection_co and set s->quit if needed.
 | 
			
		||||
 * Read reply, wake up read_reply_co and set s->quit if needed.
 | 
			
		||||
 * Return value is a fatal error code or normal nbd reply error code
 | 
			
		||||
 */
 | 
			
		||||
static coroutine_fn int nbd_co_receive_one_chunk(
 | 
			
		||||
        NBDClientSession *s, uint64_t handle, bool only_structured,
 | 
			
		||||
        int *request_ret, QEMUIOVector *qiov, NBDReply *reply, void **payload,
 | 
			
		||||
        Error **errp)
 | 
			
		||||
        QEMUIOVector *qiov, NBDReply *reply, void **payload, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    int request_ret;
 | 
			
		||||
    int ret = nbd_co_do_receive_one_chunk(s, handle, only_structured,
 | 
			
		||||
                                          request_ret, qiov, payload, errp);
 | 
			
		||||
                                          &request_ret, qiov, payload, errp);
 | 
			
		||||
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        s->quit = true;
 | 
			
		||||
    } else {
 | 
			
		||||
        /* For assert at loop start in nbd_connection_entry */
 | 
			
		||||
        /* For assert at loop start in nbd_read_reply_entry */
 | 
			
		||||
        if (reply) {
 | 
			
		||||
            *reply = s->reply;
 | 
			
		||||
        }
 | 
			
		||||
        s->reply.handle = 0;
 | 
			
		||||
        ret = request_ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (s->connection_co) {
 | 
			
		||||
        aio_co_wake(s->connection_co);
 | 
			
		||||
    if (s->read_reply_co) {
 | 
			
		||||
        aio_co_wake(s->read_reply_co);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
@@ -525,17 +481,20 @@ static coroutine_fn int nbd_co_receive_one_chunk(
 | 
			
		||||
 | 
			
		||||
typedef struct NBDReplyChunkIter {
 | 
			
		||||
    int ret;
 | 
			
		||||
    int request_ret;
 | 
			
		||||
    Error *err;
 | 
			
		||||
    bool done, only_structured;
 | 
			
		||||
} NBDReplyChunkIter;
 | 
			
		||||
 | 
			
		||||
static void nbd_iter_channel_error(NBDReplyChunkIter *iter,
 | 
			
		||||
static void nbd_iter_error(NBDReplyChunkIter *iter, bool fatal,
 | 
			
		||||
                           int ret, Error **local_err)
 | 
			
		||||
{
 | 
			
		||||
    assert(ret < 0);
 | 
			
		||||
 | 
			
		||||
    if (!iter->ret) {
 | 
			
		||||
    if (fatal || iter->ret == 0) {
 | 
			
		||||
        if (iter->ret != 0) {
 | 
			
		||||
            error_free(iter->err);
 | 
			
		||||
            iter->err = NULL;
 | 
			
		||||
        }
 | 
			
		||||
        iter->ret = ret;
 | 
			
		||||
        error_propagate(&iter->err, *local_err);
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -545,15 +504,6 @@ static void nbd_iter_channel_error(NBDReplyChunkIter *iter,
 | 
			
		||||
    *local_err = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void nbd_iter_request_error(NBDReplyChunkIter *iter, int ret)
 | 
			
		||||
{
 | 
			
		||||
    assert(ret < 0);
 | 
			
		||||
 | 
			
		||||
    if (!iter->request_ret) {
 | 
			
		||||
        iter->request_ret = ret;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* NBD_FOREACH_REPLY_CHUNK
 | 
			
		||||
 */
 | 
			
		||||
#define NBD_FOREACH_REPLY_CHUNK(s, iter, handle, structured, \
 | 
			
		||||
@@ -569,13 +519,13 @@ static bool nbd_reply_chunk_iter_receive(NBDClientSession *s,
 | 
			
		||||
                                         QEMUIOVector *qiov, NBDReply *reply,
 | 
			
		||||
                                         void **payload)
 | 
			
		||||
{
 | 
			
		||||
    int ret, request_ret;
 | 
			
		||||
    int ret;
 | 
			
		||||
    NBDReply local_reply;
 | 
			
		||||
    NBDStructuredReplyChunk *chunk;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    if (s->quit) {
 | 
			
		||||
        error_setg(&local_err, "Connection closed");
 | 
			
		||||
        nbd_iter_channel_error(iter, -EIO, &local_err);
 | 
			
		||||
        nbd_iter_error(iter, true, -EIO, &local_err);
 | 
			
		||||
        goto break_loop;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -589,16 +539,14 @@ static bool nbd_reply_chunk_iter_receive(NBDClientSession *s,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = nbd_co_receive_one_chunk(s, handle, iter->only_structured,
 | 
			
		||||
                                   &request_ret, qiov, reply, payload,
 | 
			
		||||
                                   &local_err);
 | 
			
		||||
                                   qiov, reply, payload, &local_err);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        nbd_iter_channel_error(iter, ret, &local_err);
 | 
			
		||||
    } else if (request_ret < 0) {
 | 
			
		||||
        nbd_iter_request_error(iter, request_ret);
 | 
			
		||||
        /* If it is a fatal error s->quit is set by nbd_co_receive_one_chunk */
 | 
			
		||||
        nbd_iter_error(iter, s->quit, ret, &local_err);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Do not execute the body of NBD_FOREACH_REPLY_CHUNK for simple reply. */
 | 
			
		||||
    if (nbd_reply_is_simple(reply) || s->quit) {
 | 
			
		||||
    if (nbd_reply_is_simple(&s->reply) || s->quit) {
 | 
			
		||||
        goto break_loop;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -631,7 +579,7 @@ break_loop:
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int nbd_co_receive_return_code(NBDClientSession *s, uint64_t handle,
 | 
			
		||||
                                      int *request_ret, Error **errp)
 | 
			
		||||
                                      Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    NBDReplyChunkIter iter;
 | 
			
		||||
 | 
			
		||||
@@ -640,13 +588,12 @@ static int nbd_co_receive_return_code(NBDClientSession *s, uint64_t handle,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    error_propagate(errp, iter.err);
 | 
			
		||||
    *request_ret = iter.request_ret;
 | 
			
		||||
    return iter.ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle,
 | 
			
		||||
                                        uint64_t offset, QEMUIOVector *qiov,
 | 
			
		||||
                                        int *request_ret, Error **errp)
 | 
			
		||||
                                        Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    NBDReplyChunkIter iter;
 | 
			
		||||
    NBDReply reply;
 | 
			
		||||
@@ -671,7 +618,7 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle,
 | 
			
		||||
                                                offset, qiov, &local_err);
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
                s->quit = true;
 | 
			
		||||
                nbd_iter_channel_error(&iter, ret, &local_err);
 | 
			
		||||
                nbd_iter_error(&iter, true, ret, &local_err);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
@@ -681,7 +628,7 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle,
 | 
			
		||||
                error_setg(&local_err,
 | 
			
		||||
                           "Unexpected reply type: %d (%s) for CMD_READ",
 | 
			
		||||
                           chunk->type, nbd_reply_type_lookup(chunk->type));
 | 
			
		||||
                nbd_iter_channel_error(&iter, -EINVAL, &local_err);
 | 
			
		||||
                nbd_iter_error(&iter, true, -EINVAL, &local_err);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -690,79 +637,13 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    error_propagate(errp, iter.err);
 | 
			
		||||
    *request_ret = iter.request_ret;
 | 
			
		||||
    return iter.ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int nbd_co_receive_blockstatus_reply(NBDClientSession *s,
 | 
			
		||||
                                            uint64_t handle, uint64_t length,
 | 
			
		||||
                                            NBDExtent *extent,
 | 
			
		||||
                                            int *request_ret, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    NBDReplyChunkIter iter;
 | 
			
		||||
    NBDReply reply;
 | 
			
		||||
    void *payload = NULL;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    bool received = false;
 | 
			
		||||
 | 
			
		||||
    assert(!extent->length);
 | 
			
		||||
    NBD_FOREACH_REPLY_CHUNK(s, iter, handle, s->info.structured_reply,
 | 
			
		||||
                            NULL, &reply, &payload)
 | 
			
		||||
    {
 | 
			
		||||
        int ret;
 | 
			
		||||
        NBDStructuredReplyChunk *chunk = &reply.structured;
 | 
			
		||||
 | 
			
		||||
        assert(nbd_reply_is_structured(&reply));
 | 
			
		||||
 | 
			
		||||
        switch (chunk->type) {
 | 
			
		||||
        case NBD_REPLY_TYPE_BLOCK_STATUS:
 | 
			
		||||
            if (received) {
 | 
			
		||||
                s->quit = true;
 | 
			
		||||
                error_setg(&local_err, "Several BLOCK_STATUS chunks in reply");
 | 
			
		||||
                nbd_iter_channel_error(&iter, -EINVAL, &local_err);
 | 
			
		||||
            }
 | 
			
		||||
            received = true;
 | 
			
		||||
 | 
			
		||||
            ret = nbd_parse_blockstatus_payload(s, &reply.structured,
 | 
			
		||||
                                                payload, length, extent,
 | 
			
		||||
                                                &local_err);
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
                s->quit = true;
 | 
			
		||||
                nbd_iter_channel_error(&iter, ret, &local_err);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            if (!nbd_reply_type_is_error(chunk->type)) {
 | 
			
		||||
                s->quit = true;
 | 
			
		||||
                error_setg(&local_err,
 | 
			
		||||
                           "Unexpected reply type: %d (%s) "
 | 
			
		||||
                           "for CMD_BLOCK_STATUS",
 | 
			
		||||
                           chunk->type, nbd_reply_type_lookup(chunk->type));
 | 
			
		||||
                nbd_iter_channel_error(&iter, -EINVAL, &local_err);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        g_free(payload);
 | 
			
		||||
        payload = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!extent->length && !iter.err) {
 | 
			
		||||
        error_setg(&iter.err,
 | 
			
		||||
                   "Server did not reply with any status extents");
 | 
			
		||||
        if (!iter.ret) {
 | 
			
		||||
            iter.ret = -EIO;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    error_propagate(errp, iter.err);
 | 
			
		||||
    *request_ret = iter.request_ret;
 | 
			
		||||
    return iter.ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int nbd_co_request(BlockDriverState *bs, NBDRequest *request,
 | 
			
		||||
                          QEMUIOVector *write_qiov)
 | 
			
		||||
{
 | 
			
		||||
    int ret, request_ret;
 | 
			
		||||
    int ret;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    NBDClientSession *client = nbd_get_client_session(bs);
 | 
			
		||||
 | 
			
		||||
@@ -778,22 +659,17 @@ static int nbd_co_request(BlockDriverState *bs, NBDRequest *request,
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = nbd_co_receive_return_code(client, request->handle,
 | 
			
		||||
                                     &request_ret, &local_err);
 | 
			
		||||
    ret = nbd_co_receive_return_code(client, request->handle, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        trace_nbd_co_request_fail(request->from, request->len, request->handle,
 | 
			
		||||
                                  request->flags, request->type,
 | 
			
		||||
                                  nbd_cmd_lookup(request->type),
 | 
			
		||||
                                  ret, error_get_pretty(local_err));
 | 
			
		||||
        error_free(local_err);
 | 
			
		||||
        error_report_err(local_err);
 | 
			
		||||
    }
 | 
			
		||||
    return ret ? ret : request_ret;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
                         uint64_t bytes, QEMUIOVector *qiov, int flags)
 | 
			
		||||
{
 | 
			
		||||
    int ret, request_ret;
 | 
			
		||||
    int ret;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    NBDClientSession *client = nbd_get_client_session(bs);
 | 
			
		||||
    NBDRequest request = {
 | 
			
		||||
@@ -814,15 +690,11 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = nbd_co_receive_cmdread_reply(client, request.handle, offset, qiov,
 | 
			
		||||
                                       &request_ret, &local_err);
 | 
			
		||||
                                       &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        trace_nbd_co_request_fail(request.from, request.len, request.handle,
 | 
			
		||||
                                  request.flags, request.type,
 | 
			
		||||
                                  nbd_cmd_lookup(request.type),
 | 
			
		||||
                                  ret, error_get_pretty(local_err));
 | 
			
		||||
        error_free(local_err);
 | 
			
		||||
        error_report_err(local_err);
 | 
			
		||||
    }
 | 
			
		||||
    return ret ? ret : request_ret;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
@@ -910,55 +782,6 @@ int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes)
 | 
			
		||||
    return nbd_co_request(bs, &request, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs,
 | 
			
		||||
                                            bool want_zero,
 | 
			
		||||
                                            int64_t offset, int64_t bytes,
 | 
			
		||||
                                            int64_t *pnum, int64_t *map,
 | 
			
		||||
                                            BlockDriverState **file)
 | 
			
		||||
{
 | 
			
		||||
    int ret, request_ret;
 | 
			
		||||
    NBDExtent extent = { 0 };
 | 
			
		||||
    NBDClientSession *client = nbd_get_client_session(bs);
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
    NBDRequest request = {
 | 
			
		||||
        .type = NBD_CMD_BLOCK_STATUS,
 | 
			
		||||
        .from = offset,
 | 
			
		||||
        .len = MIN(MIN_NON_ZERO(QEMU_ALIGN_DOWN(INT_MAX,
 | 
			
		||||
                                                bs->bl.request_alignment),
 | 
			
		||||
                                client->info.max_block), bytes),
 | 
			
		||||
        .flags = NBD_CMD_FLAG_REQ_ONE,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (!client->info.base_allocation) {
 | 
			
		||||
        *pnum = bytes;
 | 
			
		||||
        return BDRV_BLOCK_DATA;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = nbd_co_send_request(bs, &request, NULL);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = nbd_co_receive_blockstatus_reply(client, request.handle, bytes,
 | 
			
		||||
                                           &extent, &request_ret, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        trace_nbd_co_request_fail(request.from, request.len, request.handle,
 | 
			
		||||
                                  request.flags, request.type,
 | 
			
		||||
                                  nbd_cmd_lookup(request.type),
 | 
			
		||||
                                  ret, error_get_pretty(local_err));
 | 
			
		||||
        error_free(local_err);
 | 
			
		||||
    }
 | 
			
		||||
    if (ret < 0 || request_ret < 0) {
 | 
			
		||||
        return ret ? ret : request_ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    assert(extent.length);
 | 
			
		||||
    *pnum = extent.length;
 | 
			
		||||
    return (extent.flags & NBD_STATE_HOLE ? 0 : BDRV_BLOCK_DATA) |
 | 
			
		||||
           (extent.flags & NBD_STATE_ZERO ? BDRV_BLOCK_ZERO : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void nbd_client_detach_aio_context(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    NBDClientSession *client = nbd_get_client_session(bs);
 | 
			
		||||
@@ -970,7 +793,7 @@ void nbd_client_attach_aio_context(BlockDriverState *bs,
 | 
			
		||||
{
 | 
			
		||||
    NBDClientSession *client = nbd_get_client_session(bs);
 | 
			
		||||
    qio_channel_attach_aio_context(QIO_CHANNEL(client->ioc), new_context);
 | 
			
		||||
    aio_co_schedule(new_context, client->connection_co);
 | 
			
		||||
    aio_co_schedule(new_context, client->read_reply_co);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void nbd_client_close(BlockDriverState *bs)
 | 
			
		||||
@@ -978,84 +801,43 @@ void nbd_client_close(BlockDriverState *bs)
 | 
			
		||||
    NBDClientSession *client = nbd_get_client_session(bs);
 | 
			
		||||
    NBDRequest request = { .type = NBD_CMD_DISC };
 | 
			
		||||
 | 
			
		||||
    assert(client->ioc);
 | 
			
		||||
    if (client->ioc == NULL) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    nbd_send_request(client->ioc, &request);
 | 
			
		||||
 | 
			
		||||
    nbd_teardown_connection(bs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr,
 | 
			
		||||
                                                  Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    QIOChannelSocket *sioc;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
    sioc = qio_channel_socket_new();
 | 
			
		||||
    qio_channel_set_name(QIO_CHANNEL(sioc), "nbd-client");
 | 
			
		||||
 | 
			
		||||
    qio_channel_socket_connect_sync(sioc, saddr, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        object_unref(OBJECT(sioc));
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qio_channel_set_delay(QIO_CHANNEL(sioc), false);
 | 
			
		||||
 | 
			
		||||
    return sioc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int nbd_client_connect(BlockDriverState *bs,
 | 
			
		||||
                              SocketAddress *saddr,
 | 
			
		||||
int nbd_client_init(BlockDriverState *bs,
 | 
			
		||||
                    QIOChannelSocket *sioc,
 | 
			
		||||
                    const char *export,
 | 
			
		||||
                    QCryptoTLSCreds *tlscreds,
 | 
			
		||||
                    const char *hostname,
 | 
			
		||||
                              const char *x_dirty_bitmap,
 | 
			
		||||
                    Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    NBDClientSession *client = nbd_get_client_session(bs);
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * establish TCP connection, return error if it fails
 | 
			
		||||
     * TODO: Configurable retry-until-timeout behaviour.
 | 
			
		||||
     */
 | 
			
		||||
    QIOChannelSocket *sioc = nbd_establish_connection(saddr, errp);
 | 
			
		||||
 | 
			
		||||
    if (!sioc) {
 | 
			
		||||
        return -ECONNREFUSED;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* NBD handshake */
 | 
			
		||||
    logout("session init %s\n", export);
 | 
			
		||||
    qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL);
 | 
			
		||||
 | 
			
		||||
    client->info.request_sizes = true;
 | 
			
		||||
    client->info.structured_reply = true;
 | 
			
		||||
    client->info.base_allocation = true;
 | 
			
		||||
    client->info.x_dirty_bitmap = g_strdup(x_dirty_bitmap);
 | 
			
		||||
    client->info.name = g_strdup(export ?: "");
 | 
			
		||||
    ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), tlscreds, hostname,
 | 
			
		||||
    ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export,
 | 
			
		||||
                                tlscreds, hostname,
 | 
			
		||||
                                &client->ioc, &client->info, errp);
 | 
			
		||||
    g_free(client->info.x_dirty_bitmap);
 | 
			
		||||
    g_free(client->info.name);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        logout("Failed to negotiate with the NBD server\n");
 | 
			
		||||
        object_unref(OBJECT(sioc));
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
    if (x_dirty_bitmap && !client->info.base_allocation) {
 | 
			
		||||
        error_setg(errp, "requested x-dirty-bitmap %s not found",
 | 
			
		||||
                   x_dirty_bitmap);
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    if (client->info.flags & NBD_FLAG_READ_ONLY) {
 | 
			
		||||
        ret = bdrv_apply_auto_read_only(bs, "NBD export is read-only", errp);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
    if (client->info.flags & NBD_FLAG_READ_ONLY &&
 | 
			
		||||
        !bdrv_is_read_only(bs)) {
 | 
			
		||||
        error_setg(errp,
 | 
			
		||||
                   "request for write access conflicts with read-only export");
 | 
			
		||||
        return -EACCES;
 | 
			
		||||
    }
 | 
			
		||||
    if (client->info.flags & NBD_FLAG_SEND_FUA) {
 | 
			
		||||
        bs->supported_write_flags = BDRV_REQ_FUA;
 | 
			
		||||
@@ -1065,7 +847,10 @@ static int nbd_client_connect(BlockDriverState *bs,
 | 
			
		||||
        bs->supported_zero_flags |= BDRV_REQ_MAY_UNMAP;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_co_mutex_init(&client->send_mutex);
 | 
			
		||||
    qemu_co_queue_init(&client->free_sema);
 | 
			
		||||
    client->sioc = sioc;
 | 
			
		||||
    object_ref(OBJECT(client->sioc));
 | 
			
		||||
 | 
			
		||||
    if (!client->ioc) {
 | 
			
		||||
        client->ioc = QIO_CHANNEL(sioc);
 | 
			
		||||
@@ -1075,42 +860,9 @@ static int nbd_client_connect(BlockDriverState *bs,
 | 
			
		||||
    /* Now that we're connected, set the socket to be non-blocking and
 | 
			
		||||
     * kick the reply mechanism.  */
 | 
			
		||||
    qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL);
 | 
			
		||||
    client->connection_co = qemu_coroutine_create(nbd_connection_entry, client);
 | 
			
		||||
    client->read_reply_co = qemu_coroutine_create(nbd_read_reply_entry, client);
 | 
			
		||||
    nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs));
 | 
			
		||||
 | 
			
		||||
    logout("Established connection with NBD server\n");
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
 fail:
 | 
			
		||||
    /*
 | 
			
		||||
     * We have connected, but must fail for other reasons. The
 | 
			
		||||
     * connection is still blocking; send NBD_CMD_DISC as a courtesy
 | 
			
		||||
     * to the server.
 | 
			
		||||
     */
 | 
			
		||||
    {
 | 
			
		||||
        NBDRequest request = { .type = NBD_CMD_DISC };
 | 
			
		||||
 | 
			
		||||
        nbd_send_request(client->ioc ?: QIO_CHANNEL(sioc), &request);
 | 
			
		||||
 | 
			
		||||
        object_unref(OBJECT(sioc));
 | 
			
		||||
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int nbd_client_init(BlockDriverState *bs,
 | 
			
		||||
                    SocketAddress *saddr,
 | 
			
		||||
                    const char *export,
 | 
			
		||||
                    QCryptoTLSCreds *tlscreds,
 | 
			
		||||
                    const char *hostname,
 | 
			
		||||
                    const char *x_dirty_bitmap,
 | 
			
		||||
                    Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    NBDClientSession *client = nbd_get_client_session(bs);
 | 
			
		||||
 | 
			
		||||
    qemu_co_mutex_init(&client->send_mutex);
 | 
			
		||||
    qemu_co_queue_init(&client->free_sema);
 | 
			
		||||
 | 
			
		||||
    return nbd_client_connect(bs, saddr, export, tlscreds, hostname,
 | 
			
		||||
                              x_dirty_bitmap, errp);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
typedef struct {
 | 
			
		||||
    Coroutine *coroutine;
 | 
			
		||||
    uint64_t offset;        /* original offset of the request */
 | 
			
		||||
    bool receiving;         /* waiting for connection_co? */
 | 
			
		||||
    bool receiving;         /* waiting for read_reply_co? */
 | 
			
		||||
} NBDClientRequest;
 | 
			
		||||
 | 
			
		||||
typedef struct NBDClientSession {
 | 
			
		||||
@@ -30,7 +30,7 @@ typedef struct NBDClientSession {
 | 
			
		||||
 | 
			
		||||
    CoMutex send_mutex;
 | 
			
		||||
    CoQueue free_sema;
 | 
			
		||||
    Coroutine *connection_co;
 | 
			
		||||
    Coroutine *read_reply_co;
 | 
			
		||||
    int in_flight;
 | 
			
		||||
 | 
			
		||||
    NBDClientRequest requests[MAX_NBD_REQUESTS];
 | 
			
		||||
@@ -41,11 +41,10 @@ typedef struct NBDClientSession {
 | 
			
		||||
NBDClientSession *nbd_get_client_session(BlockDriverState *bs);
 | 
			
		||||
 | 
			
		||||
int nbd_client_init(BlockDriverState *bs,
 | 
			
		||||
                    SocketAddress *saddr,
 | 
			
		||||
                    QIOChannelSocket *sock,
 | 
			
		||||
                    const char *export_name,
 | 
			
		||||
                    QCryptoTLSCreds *tlscreds,
 | 
			
		||||
                    const char *hostname,
 | 
			
		||||
                    const char *x_dirty_bitmap,
 | 
			
		||||
                    Error **errp);
 | 
			
		||||
void nbd_client_close(BlockDriverState *bs);
 | 
			
		||||
 | 
			
		||||
@@ -62,10 +61,4 @@ void nbd_client_detach_aio_context(BlockDriverState *bs);
 | 
			
		||||
void nbd_client_attach_aio_context(BlockDriverState *bs,
 | 
			
		||||
                                   AioContext *new_context);
 | 
			
		||||
 | 
			
		||||
int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs,
 | 
			
		||||
                                            bool want_zero,
 | 
			
		||||
                                            int64_t offset, int64_t bytes,
 | 
			
		||||
                                            int64_t *pnum, int64_t *map,
 | 
			
		||||
                                            BlockDriverState **file);
 | 
			
		||||
 | 
			
		||||
#endif /* NBD_CLIENT_H */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										73
									
								
								block/nbd.c
									
									
									
									
									
								
							
							
						
						
									
										73
									
								
								block/nbd.c
									
									
									
									
									
								
							@@ -27,8 +27,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include "nbd-client.h"
 | 
			
		||||
#include "block/qdict.h"
 | 
			
		||||
#include "block/nbd-client.h"
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
#include "qemu/uri.h"
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
@@ -109,7 +108,7 @@ static int nbd_parse_uri(const char *filename, QDict *options)
 | 
			
		||||
        /* strip braces from literal IPv6 address */
 | 
			
		||||
        if (uri->server[0] == '[') {
 | 
			
		||||
            host = qstring_from_substr(uri->server, 1,
 | 
			
		||||
                                       strlen(uri->server) - 1);
 | 
			
		||||
                                       strlen(uri->server) - 2);
 | 
			
		||||
        } else {
 | 
			
		||||
            host = qstring_from_str(uri->server);
 | 
			
		||||
        }
 | 
			
		||||
@@ -263,6 +262,7 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
 | 
			
		||||
{
 | 
			
		||||
    SocketAddress *saddr = NULL;
 | 
			
		||||
    QDict *addr = NULL;
 | 
			
		||||
    QObject *crumpled_addr = NULL;
 | 
			
		||||
    Visitor *iv = NULL;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
@@ -272,11 +272,20 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    iv = qobject_input_visitor_new_flat_confused(addr, errp);
 | 
			
		||||
    if (!iv) {
 | 
			
		||||
    crumpled_addr = qdict_crumple(addr, errp);
 | 
			
		||||
    if (!crumpled_addr) {
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive
 | 
			
		||||
     * server.type=inet.  .to doesn't matter, it's ignored anyway.
 | 
			
		||||
     * That's because when @options come from -blockdev or
 | 
			
		||||
     * blockdev_add, members are typed according to the QAPI schema,
 | 
			
		||||
     * but when they come from -drive, they're all QString.  The
 | 
			
		||||
     * visitor expects the former.
 | 
			
		||||
     */
 | 
			
		||||
    iv = qobject_input_visitor_new(crumpled_addr);
 | 
			
		||||
    visit_type_SocketAddress(iv, NULL, &saddr, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
@@ -284,7 +293,8 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
    qobject_unref(addr);
 | 
			
		||||
    QDECREF(addr);
 | 
			
		||||
    qobject_decref(crumpled_addr);
 | 
			
		||||
    visit_free(iv);
 | 
			
		||||
    return saddr;
 | 
			
		||||
}
 | 
			
		||||
@@ -295,6 +305,30 @@ NBDClientSession *nbd_get_client_session(BlockDriverState *bs)
 | 
			
		||||
    return &s->client;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr,
 | 
			
		||||
                                                  Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    QIOChannelSocket *sioc;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
    sioc = qio_channel_socket_new();
 | 
			
		||||
    qio_channel_set_name(QIO_CHANNEL(sioc), "nbd-client");
 | 
			
		||||
 | 
			
		||||
    qio_channel_socket_connect_sync(sioc,
 | 
			
		||||
                                    saddr,
 | 
			
		||||
                                    &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        object_unref(OBJECT(sioc));
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qio_channel_set_delay(QIO_CHANNEL(sioc), false);
 | 
			
		||||
 | 
			
		||||
    return sioc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    Object *obj;
 | 
			
		||||
@@ -354,12 +388,6 @@ static QemuOptsList nbd_runtime_opts = {
 | 
			
		||||
            .type = QEMU_OPT_STRING,
 | 
			
		||||
            .help = "ID of the TLS credentials to use",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = "x-dirty-bitmap",
 | 
			
		||||
            .type = QEMU_OPT_STRING,
 | 
			
		||||
            .help = "experimental: expose named dirty bitmap in place of "
 | 
			
		||||
                    "block status",
 | 
			
		||||
        },
 | 
			
		||||
        { /* end of list */ }
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
@@ -370,6 +398,7 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    BDRVNBDState *s = bs->opaque;
 | 
			
		||||
    QemuOpts *opts = NULL;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    QIOChannelSocket *sioc = NULL;
 | 
			
		||||
    QCryptoTLSCreds *tlscreds = NULL;
 | 
			
		||||
    const char *hostname = NULL;
 | 
			
		||||
    int ret = -EINVAL;
 | 
			
		||||
@@ -409,11 +438,22 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
        hostname = s->saddr->u.inet.host;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* NBD handshake */
 | 
			
		||||
    ret = nbd_client_init(bs, s->saddr, s->export, tlscreds, hostname,
 | 
			
		||||
                          qemu_opt_get(opts, "x-dirty-bitmap"), errp);
 | 
			
		||||
    /* establish TCP connection, return error if it fails
 | 
			
		||||
     * TODO: Configurable retry-until-timeout behaviour.
 | 
			
		||||
     */
 | 
			
		||||
    sioc = nbd_establish_connection(s->saddr, errp);
 | 
			
		||||
    if (!sioc) {
 | 
			
		||||
        ret = -ECONNREFUSED;
 | 
			
		||||
        goto error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* NBD handshake */
 | 
			
		||||
    ret = nbd_client_init(bs, sioc, s->export,
 | 
			
		||||
                          tlscreds, hostname, errp);
 | 
			
		||||
 error:
 | 
			
		||||
    if (sioc) {
 | 
			
		||||
        object_unref(OBJECT(sioc));
 | 
			
		||||
    }
 | 
			
		||||
    if (tlscreds) {
 | 
			
		||||
        object_unref(OBJECT(tlscreds));
 | 
			
		||||
    }
 | 
			
		||||
@@ -545,7 +585,6 @@ static BlockDriver bdrv_nbd = {
 | 
			
		||||
    .bdrv_detach_aio_context    = nbd_detach_aio_context,
 | 
			
		||||
    .bdrv_attach_aio_context    = nbd_attach_aio_context,
 | 
			
		||||
    .bdrv_refresh_filename      = nbd_refresh_filename,
 | 
			
		||||
    .bdrv_co_block_status       = nbd_client_co_block_status,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_nbd_tcp = {
 | 
			
		||||
@@ -565,7 +604,6 @@ static BlockDriver bdrv_nbd_tcp = {
 | 
			
		||||
    .bdrv_detach_aio_context    = nbd_detach_aio_context,
 | 
			
		||||
    .bdrv_attach_aio_context    = nbd_attach_aio_context,
 | 
			
		||||
    .bdrv_refresh_filename      = nbd_refresh_filename,
 | 
			
		||||
    .bdrv_co_block_status       = nbd_client_co_block_status,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_nbd_unix = {
 | 
			
		||||
@@ -585,7 +623,6 @@ static BlockDriver bdrv_nbd_unix = {
 | 
			
		||||
    .bdrv_detach_aio_context    = nbd_detach_aio_context,
 | 
			
		||||
    .bdrv_attach_aio_context    = nbd_attach_aio_context,
 | 
			
		||||
    .bdrv_refresh_filename      = nbd_refresh_filename,
 | 
			
		||||
    .bdrv_co_block_status       = nbd_client_co_block_status,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void bdrv_nbd_init(void)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								block/nfs.c
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								block/nfs.c
									
									
									
									
									
								
							@@ -29,7 +29,6 @@
 | 
			
		||||
#include "qemu/error-report.h"
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
#include "block/qdict.h"
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
#include "qemu/iov.h"
 | 
			
		||||
#include "qemu/option.h"
 | 
			
		||||
@@ -556,29 +555,24 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options,
 | 
			
		||||
                                                     Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BlockdevOptionsNfs *opts = NULL;
 | 
			
		||||
    QObject *crumpled = NULL;
 | 
			
		||||
    Visitor *v;
 | 
			
		||||
    const QDictEntry *e;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
    v = qobject_input_visitor_new_flat_confused(options, errp);
 | 
			
		||||
    if (!v) {
 | 
			
		||||
    crumpled = qdict_crumple(options, errp);
 | 
			
		||||
    if (crumpled == NULL) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    v = qobject_input_visitor_new_keyval(crumpled);
 | 
			
		||||
    visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err);
 | 
			
		||||
    visit_free(v);
 | 
			
		||||
    qobject_decref(crumpled);
 | 
			
		||||
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Remove the processed options from the QDict (the visitor processes
 | 
			
		||||
     * _all_ options in the QDict) */
 | 
			
		||||
    while ((e = qdict_first(options))) {
 | 
			
		||||
        qdict_del(options, e->key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return opts;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -689,7 +683,7 @@ static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
 | 
			
		||||
 | 
			
		||||
    ret = 0;
 | 
			
		||||
out:
 | 
			
		||||
    qobject_unref(options);
 | 
			
		||||
    QDECREF(options);
 | 
			
		||||
    qapi_free_BlockdevCreateOptions(create_options);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
@@ -743,8 +737,7 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs)
 | 
			
		||||
    return (task.ret < 0 ? task.ret : st.st_blocks * 512);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn
 | 
			
		||||
nfs_file_co_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
static int nfs_file_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
                             PreallocMode prealloc, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    NFSClient *client = bs->opaque;
 | 
			
		||||
@@ -874,7 +867,7 @@ static BlockDriver bdrv_nfs = {
 | 
			
		||||
 | 
			
		||||
    .bdrv_has_zero_init             = nfs_has_zero_init,
 | 
			
		||||
    .bdrv_get_allocated_file_size   = nfs_get_allocated_file_size,
 | 
			
		||||
    .bdrv_co_truncate               = nfs_file_co_truncate,
 | 
			
		||||
    .bdrv_truncate                  = nfs_file_truncate,
 | 
			
		||||
 | 
			
		||||
    .bdrv_file_open                 = nfs_file_open,
 | 
			
		||||
    .bdrv_close                     = nfs_file_close,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										46
									
								
								block/null.c
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								block/null.c
									
									
									
									
									
								
							@@ -93,10 +93,13 @@ static int null_file_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    }
 | 
			
		||||
    s->read_zeroes = qemu_opt_get_bool(opts, NULL_OPT_ZEROES, false);
 | 
			
		||||
    qemu_opts_del(opts);
 | 
			
		||||
    bs->supported_write_flags = BDRV_REQ_FUA;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void null_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int64_t null_getlength(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVNullState *s = bs->opaque;
 | 
			
		||||
@@ -113,22 +116,22 @@ static coroutine_fn int null_co_common(BlockDriverState *bs)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static coroutine_fn int null_co_preadv(BlockDriverState *bs,
 | 
			
		||||
                                       uint64_t offset, uint64_t bytes,
 | 
			
		||||
                                       QEMUIOVector *qiov, int flags)
 | 
			
		||||
static coroutine_fn int null_co_readv(BlockDriverState *bs,
 | 
			
		||||
                                      int64_t sector_num, int nb_sectors,
 | 
			
		||||
                                      QEMUIOVector *qiov)
 | 
			
		||||
{
 | 
			
		||||
    BDRVNullState *s = bs->opaque;
 | 
			
		||||
 | 
			
		||||
    if (s->read_zeroes) {
 | 
			
		||||
        qemu_iovec_memset(qiov, 0, 0, bytes);
 | 
			
		||||
        qemu_iovec_memset(qiov, 0, 0, nb_sectors * BDRV_SECTOR_SIZE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return null_co_common(bs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static coroutine_fn int null_co_pwritev(BlockDriverState *bs,
 | 
			
		||||
                                        uint64_t offset, uint64_t bytes,
 | 
			
		||||
                                        QEMUIOVector *qiov, int flags)
 | 
			
		||||
static coroutine_fn int null_co_writev(BlockDriverState *bs,
 | 
			
		||||
                                       int64_t sector_num, int nb_sectors,
 | 
			
		||||
                                       QEMUIOVector *qiov)
 | 
			
		||||
{
 | 
			
		||||
    return null_co_common(bs);
 | 
			
		||||
}
 | 
			
		||||
@@ -183,24 +186,24 @@ static inline BlockAIOCB *null_aio_common(BlockDriverState *bs,
 | 
			
		||||
    return &acb->common;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockAIOCB *null_aio_preadv(BlockDriverState *bs,
 | 
			
		||||
                                   uint64_t offset, uint64_t bytes,
 | 
			
		||||
                                   QEMUIOVector *qiov, int flags,
 | 
			
		||||
static BlockAIOCB *null_aio_readv(BlockDriverState *bs,
 | 
			
		||||
                                  int64_t sector_num, QEMUIOVector *qiov,
 | 
			
		||||
                                  int nb_sectors,
 | 
			
		||||
                                  BlockCompletionFunc *cb,
 | 
			
		||||
                                  void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    BDRVNullState *s = bs->opaque;
 | 
			
		||||
 | 
			
		||||
    if (s->read_zeroes) {
 | 
			
		||||
        qemu_iovec_memset(qiov, 0, 0, bytes);
 | 
			
		||||
        qemu_iovec_memset(qiov, 0, 0, nb_sectors * BDRV_SECTOR_SIZE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return null_aio_common(bs, cb, opaque);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockAIOCB *null_aio_pwritev(BlockDriverState *bs,
 | 
			
		||||
                                    uint64_t offset, uint64_t bytes,
 | 
			
		||||
                                    QEMUIOVector *qiov, int flags,
 | 
			
		||||
static BlockAIOCB *null_aio_writev(BlockDriverState *bs,
 | 
			
		||||
                                   int64_t sector_num, QEMUIOVector *qiov,
 | 
			
		||||
                                   int nb_sectors,
 | 
			
		||||
                                   BlockCompletionFunc *cb,
 | 
			
		||||
                                   void *opaque)
 | 
			
		||||
{
 | 
			
		||||
@@ -241,6 +244,7 @@ static int coroutine_fn null_co_block_status(BlockDriverState *bs,
 | 
			
		||||
 | 
			
		||||
static void null_refresh_filename(BlockDriverState *bs, QDict *opts)
 | 
			
		||||
{
 | 
			
		||||
    QINCREF(opts);
 | 
			
		||||
    qdict_del(opts, "filename");
 | 
			
		||||
 | 
			
		||||
    if (!qdict_size(opts)) {
 | 
			
		||||
@@ -249,7 +253,7 @@ static void null_refresh_filename(BlockDriverState *bs, QDict *opts)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qdict_put_str(opts, "driver", bs->drv->format_name);
 | 
			
		||||
    bs->full_open_options = qobject_ref(opts);
 | 
			
		||||
    bs->full_open_options = opts;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_null_co = {
 | 
			
		||||
@@ -259,10 +263,11 @@ static BlockDriver bdrv_null_co = {
 | 
			
		||||
 | 
			
		||||
    .bdrv_file_open         = null_file_open,
 | 
			
		||||
    .bdrv_parse_filename    = null_co_parse_filename,
 | 
			
		||||
    .bdrv_close             = null_close,
 | 
			
		||||
    .bdrv_getlength         = null_getlength,
 | 
			
		||||
 | 
			
		||||
    .bdrv_co_preadv         = null_co_preadv,
 | 
			
		||||
    .bdrv_co_pwritev        = null_co_pwritev,
 | 
			
		||||
    .bdrv_co_readv          = null_co_readv,
 | 
			
		||||
    .bdrv_co_writev         = null_co_writev,
 | 
			
		||||
    .bdrv_co_flush_to_disk  = null_co_flush,
 | 
			
		||||
    .bdrv_reopen_prepare    = null_reopen_prepare,
 | 
			
		||||
 | 
			
		||||
@@ -278,10 +283,11 @@ static BlockDriver bdrv_null_aio = {
 | 
			
		||||
 | 
			
		||||
    .bdrv_file_open         = null_file_open,
 | 
			
		||||
    .bdrv_parse_filename    = null_aio_parse_filename,
 | 
			
		||||
    .bdrv_close             = null_close,
 | 
			
		||||
    .bdrv_getlength         = null_getlength,
 | 
			
		||||
 | 
			
		||||
    .bdrv_aio_preadv        = null_aio_preadv,
 | 
			
		||||
    .bdrv_aio_pwritev       = null_aio_pwritev,
 | 
			
		||||
    .bdrv_aio_readv         = null_aio_readv,
 | 
			
		||||
    .bdrv_aio_writev        = null_aio_writev,
 | 
			
		||||
    .bdrv_aio_flush         = null_aio_flush,
 | 
			
		||||
    .bdrv_reopen_prepare    = null_reopen_prepare,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										78
									
								
								block/nvme.c
									
									
									
									
									
								
							
							
						
						
									
										78
									
								
								block/nvme.c
									
									
									
									
									
								
							@@ -104,7 +104,7 @@ typedef struct {
 | 
			
		||||
    uint64_t nsze; /* Namespace size reported by identify command */
 | 
			
		||||
    int nsid;      /* The namespace id to read/write data. */
 | 
			
		||||
    uint64_t max_transfer;
 | 
			
		||||
    bool plugged;
 | 
			
		||||
    int plugged;
 | 
			
		||||
 | 
			
		||||
    CoMutex dma_map_lock;
 | 
			
		||||
    CoQueue dma_flush_queue;
 | 
			
		||||
@@ -390,7 +390,6 @@ static void nvme_cmd_sync_cb(void *opaque, int ret)
 | 
			
		||||
{
 | 
			
		||||
    int *pret = opaque;
 | 
			
		||||
    *pret = ret;
 | 
			
		||||
    aio_wait_kick();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int nvme_cmd_sync(BlockDriverState *bs, NVMeQueuePair *q,
 | 
			
		||||
@@ -490,8 +489,10 @@ static void nvme_handle_event(EventNotifier *n)
 | 
			
		||||
    BDRVNVMeState *s = container_of(n, BDRVNVMeState, irq_notifier);
 | 
			
		||||
 | 
			
		||||
    trace_nvme_handle_event(s);
 | 
			
		||||
    aio_context_acquire(s->aio_context);
 | 
			
		||||
    event_notifier_test_and_clear(n);
 | 
			
		||||
    nvme_poll_queues(s);
 | 
			
		||||
    aio_context_release(s->aio_context);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool nvme_add_io_queue(BlockDriverState *bs, Error **errp)
 | 
			
		||||
@@ -568,13 +569,13 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
 | 
			
		||||
    s->vfio = qemu_vfio_open_pci(device, errp);
 | 
			
		||||
    if (!s->vfio) {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto out;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->regs = qemu_vfio_pci_map_bar(s->vfio, 0, 0, NVME_BAR_SIZE, errp);
 | 
			
		||||
    if (!s->regs) {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto out;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Perform initialize sequence as described in NVMe spec "7.6.1
 | 
			
		||||
@@ -584,7 +585,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
 | 
			
		||||
    if (!(cap & (1ULL << 37))) {
 | 
			
		||||
        error_setg(errp, "Device doesn't support NVMe command set");
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto out;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->page_size = MAX(4096, 1 << (12 + ((cap >> 48) & 0xF)));
 | 
			
		||||
@@ -602,7 +603,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
 | 
			
		||||
                             PRId64 " ms)",
 | 
			
		||||
                       timeout_ms);
 | 
			
		||||
            ret = -ETIMEDOUT;
 | 
			
		||||
            goto out;
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -612,7 +613,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
 | 
			
		||||
    s->queues[0] = nvme_create_queue_pair(bs, 0, NVME_QUEUE_SIZE, errp);
 | 
			
		||||
    if (!s->queues[0]) {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto out;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    QEMU_BUILD_BUG_ON(NVME_QUEUE_SIZE & 0xF000);
 | 
			
		||||
    s->regs->aqa = cpu_to_le32((NVME_QUEUE_SIZE << 16) | NVME_QUEUE_SIZE);
 | 
			
		||||
@@ -632,14 +633,14 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
 | 
			
		||||
                             PRId64 " ms)",
 | 
			
		||||
                       timeout_ms);
 | 
			
		||||
            ret = -ETIMEDOUT;
 | 
			
		||||
            goto out;
 | 
			
		||||
            goto fail_queue;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = qemu_vfio_pci_init_irq(s->vfio, &s->irq_notifier,
 | 
			
		||||
                                 VFIO_PCI_MSIX_IRQ_INDEX, errp);
 | 
			
		||||
    if (ret) {
 | 
			
		||||
        goto out;
 | 
			
		||||
        goto fail_queue;
 | 
			
		||||
    }
 | 
			
		||||
    aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier,
 | 
			
		||||
                           false, nvme_handle_event, nvme_poll_cb);
 | 
			
		||||
@@ -648,15 +649,30 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        ret = -EIO;
 | 
			
		||||
        goto out;
 | 
			
		||||
        goto fail_handler;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Set up command queues. */
 | 
			
		||||
    if (!nvme_add_io_queue(bs, errp)) {
 | 
			
		||||
        ret = -EIO;
 | 
			
		||||
        goto fail_handler;
 | 
			
		||||
    }
 | 
			
		||||
out:
 | 
			
		||||
    /* Cleaning up is done in nvme_file_open() upon error. */
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
fail_handler:
 | 
			
		||||
    aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier,
 | 
			
		||||
                           false, NULL, NULL);
 | 
			
		||||
fail_queue:
 | 
			
		||||
    nvme_free_queue_pair(bs, s->queues[0]);
 | 
			
		||||
fail:
 | 
			
		||||
    g_free(s->queues);
 | 
			
		||||
    if (s->regs) {
 | 
			
		||||
        qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)s->regs, 0, NVME_BAR_SIZE);
 | 
			
		||||
    }
 | 
			
		||||
    if (s->vfio) {
 | 
			
		||||
        qemu_vfio_close(s->vfio);
 | 
			
		||||
    }
 | 
			
		||||
    event_notifier_cleanup(&s->irq_notifier);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -679,11 +695,12 @@ static void nvme_parse_filename(const char *filename, QDict *options,
 | 
			
		||||
        unsigned long ns;
 | 
			
		||||
        const char *slash = strchr(tmp, '/');
 | 
			
		||||
        if (!slash) {
 | 
			
		||||
            qdict_put_str(options, NVME_BLOCK_OPT_DEVICE, tmp);
 | 
			
		||||
            qdict_put(options, NVME_BLOCK_OPT_DEVICE,
 | 
			
		||||
                      qstring_from_str(tmp));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        device = g_strndup(tmp, slash - tmp);
 | 
			
		||||
        qdict_put_str(options, NVME_BLOCK_OPT_DEVICE, device);
 | 
			
		||||
        qdict_put(options, NVME_BLOCK_OPT_DEVICE, qstring_from_str(device));
 | 
			
		||||
        g_free(device);
 | 
			
		||||
        namespace = slash + 1;
 | 
			
		||||
        if (*namespace && qemu_strtoul(namespace, NULL, 10, &ns)) {
 | 
			
		||||
@@ -691,8 +708,8 @@ static void nvme_parse_filename(const char *filename, QDict *options,
 | 
			
		||||
                       namespace);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        qdict_put_str(options, NVME_BLOCK_OPT_NAMESPACE,
 | 
			
		||||
                      *namespace ? namespace : "1");
 | 
			
		||||
        qdict_put(options, NVME_BLOCK_OPT_NAMESPACE,
 | 
			
		||||
                  qstring_from_str(*namespace ? namespace : "1"));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -723,10 +740,8 @@ static void nvme_close(BlockDriverState *bs)
 | 
			
		||||
    for (i = 0; i < s->nr_queues; ++i) {
 | 
			
		||||
        nvme_free_queue_pair(bs, s->queues[i]);
 | 
			
		||||
    }
 | 
			
		||||
    g_free(s->queues);
 | 
			
		||||
    aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier,
 | 
			
		||||
                           false, NULL, NULL);
 | 
			
		||||
    event_notifier_cleanup(&s->irq_notifier);
 | 
			
		||||
    qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)s->regs, 0, NVME_BAR_SIZE);
 | 
			
		||||
    qemu_vfio_close(s->vfio);
 | 
			
		||||
}
 | 
			
		||||
@@ -838,7 +853,7 @@ try_map:
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (j = 0; j < qiov->iov[i].iov_len / s->page_size; j++) {
 | 
			
		||||
            pagelist[entries++] = cpu_to_le64(iova + j * s->page_size);
 | 
			
		||||
            pagelist[entries++] = iova + j * s->page_size;
 | 
			
		||||
        }
 | 
			
		||||
        trace_nvme_cmd_map_qiov_iov(s, i, qiov->iov[i].iov_base,
 | 
			
		||||
                                    qiov->iov[i].iov_len / s->page_size);
 | 
			
		||||
@@ -851,16 +866,20 @@ try_map:
 | 
			
		||||
    case 0:
 | 
			
		||||
        abort();
 | 
			
		||||
    case 1:
 | 
			
		||||
        cmd->prp1 = pagelist[0];
 | 
			
		||||
        cmd->prp1 = cpu_to_le64(pagelist[0]);
 | 
			
		||||
        cmd->prp2 = 0;
 | 
			
		||||
        break;
 | 
			
		||||
    case 2:
 | 
			
		||||
        cmd->prp1 = pagelist[0];
 | 
			
		||||
        cmd->prp2 = pagelist[1];
 | 
			
		||||
        cmd->prp1 = cpu_to_le64(pagelist[0]);
 | 
			
		||||
        cmd->prp2 = cpu_to_le64(pagelist[1]);;
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        cmd->prp1 = pagelist[0];
 | 
			
		||||
        cmd->prp2 = cpu_to_le64(req->prp_list_iova + sizeof(uint64_t));
 | 
			
		||||
        cmd->prp1 = cpu_to_le64(pagelist[0]);
 | 
			
		||||
        cmd->prp2 = cpu_to_le64(req->prp_list_iova);
 | 
			
		||||
        for (i = 0; i < entries - 1; ++i) {
 | 
			
		||||
            pagelist[i] = cpu_to_le64(pagelist[i + 1]);
 | 
			
		||||
        }
 | 
			
		||||
        pagelist[entries - 1] = 0;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    trace_nvme_cmd_map_qiov(s, cmd, req, qiov, entries);
 | 
			
		||||
@@ -1055,6 +1074,7 @@ static int nvme_reopen_prepare(BDRVReopenState *reopen_state,
 | 
			
		||||
 | 
			
		||||
static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts)
 | 
			
		||||
{
 | 
			
		||||
    QINCREF(opts);
 | 
			
		||||
    qdict_del(opts, "filename");
 | 
			
		||||
 | 
			
		||||
    if (!qdict_size(opts)) {
 | 
			
		||||
@@ -1062,8 +1082,8 @@ static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts)
 | 
			
		||||
                 bs->drv->format_name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qdict_put_str(opts, "driver", bs->drv->format_name);
 | 
			
		||||
    bs->full_open_options = qobject_ref(opts);
 | 
			
		||||
    qdict_put(opts, "driver", qstring_from_str(bs->drv->format_name));
 | 
			
		||||
    bs->full_open_options = opts;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void nvme_refresh_limits(BlockDriverState *bs, Error **errp)
 | 
			
		||||
@@ -1096,8 +1116,7 @@ static void nvme_attach_aio_context(BlockDriverState *bs,
 | 
			
		||||
static void nvme_aio_plug(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVNVMeState *s = bs->opaque;
 | 
			
		||||
    assert(!s->plugged);
 | 
			
		||||
    s->plugged = true;
 | 
			
		||||
    s->plugged++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void nvme_aio_unplug(BlockDriverState *bs)
 | 
			
		||||
@@ -1105,7 +1124,7 @@ static void nvme_aio_unplug(BlockDriverState *bs)
 | 
			
		||||
    int i;
 | 
			
		||||
    BDRVNVMeState *s = bs->opaque;
 | 
			
		||||
    assert(s->plugged);
 | 
			
		||||
    s->plugged = false;
 | 
			
		||||
    if (!--s->plugged) {
 | 
			
		||||
        for (i = 1; i < s->nr_queues; i++) {
 | 
			
		||||
            NVMeQueuePair *q = s->queues[i];
 | 
			
		||||
            qemu_mutex_lock(&q->lock);
 | 
			
		||||
@@ -1113,6 +1132,7 @@ static void nvme_aio_unplug(BlockDriverState *bs)
 | 
			
		||||
            nvme_process_completion(s, q);
 | 
			
		||||
            qemu_mutex_unlock(&q->lock);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void nvme_register_buf(BlockDriverState *bs, void *host, size_t size)
 | 
			
		||||
 
 | 
			
		||||
@@ -31,13 +31,9 @@
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
#include "block/qdict.h"
 | 
			
		||||
#include "sysemu/block-backend.h"
 | 
			
		||||
#include "qemu/module.h"
 | 
			
		||||
#include "qemu/option.h"
 | 
			
		||||
#include "qapi/qmp/qdict.h"
 | 
			
		||||
#include "qapi/qobject-input-visitor.h"
 | 
			
		||||
#include "qapi/qapi-visit-block-core.h"
 | 
			
		||||
#include "qemu/bswap.h"
 | 
			
		||||
#include "qemu/bitmap.h"
 | 
			
		||||
#include "migration/blocker.h"
 | 
			
		||||
@@ -83,25 +79,6 @@ static QemuOptsList parallels_runtime_opts = {
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static QemuOptsList parallels_create_opts = {
 | 
			
		||||
    .name = "parallels-create-opts",
 | 
			
		||||
    .head = QTAILQ_HEAD_INITIALIZER(parallels_create_opts.head),
 | 
			
		||||
    .desc = {
 | 
			
		||||
        {
 | 
			
		||||
            .name = BLOCK_OPT_SIZE,
 | 
			
		||||
            .type = QEMU_OPT_SIZE,
 | 
			
		||||
            .help = "Virtual disk size",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = BLOCK_OPT_CLUSTER_SIZE,
 | 
			
		||||
            .type = QEMU_OPT_SIZE,
 | 
			
		||||
            .help = "Parallels image cluster size",
 | 
			
		||||
            .def_value_str = stringify(DEFAULT_CLUSTER_SIZE),
 | 
			
		||||
        },
 | 
			
		||||
        { /* end of list */ }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int64_t bat2sect(BDRVParallelsState *s, uint32_t idx)
 | 
			
		||||
{
 | 
			
		||||
@@ -227,15 +204,14 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
        };
 | 
			
		||||
        qemu_iovec_init_external(&qiov, &iov, 1);
 | 
			
		||||
 | 
			
		||||
        ret = bdrv_co_preadv(bs->backing, idx * s->tracks * BDRV_SECTOR_SIZE,
 | 
			
		||||
                             nb_cow_bytes, &qiov, 0);
 | 
			
		||||
        ret = bdrv_co_readv(bs->backing, idx * s->tracks, nb_cow_sectors,
 | 
			
		||||
                            &qiov);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            qemu_vfree(iov.iov_base);
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = bdrv_co_pwritev(bs->file, s->data_end * BDRV_SECTOR_SIZE,
 | 
			
		||||
                              nb_cow_bytes, &qiov, 0);
 | 
			
		||||
        ret = bdrv_co_writev(bs->file, s->data_end, nb_cow_sectors, &qiov);
 | 
			
		||||
        qemu_vfree(iov.iov_base);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            return ret;
 | 
			
		||||
@@ -313,15 +289,13 @@ static int coroutine_fn parallels_co_block_status(BlockDriverState *bs,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static coroutine_fn int parallels_co_writev(BlockDriverState *bs,
 | 
			
		||||
                                            int64_t sector_num, int nb_sectors,
 | 
			
		||||
                                            QEMUIOVector *qiov, int flags)
 | 
			
		||||
        int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
 | 
			
		||||
{
 | 
			
		||||
    BDRVParallelsState *s = bs->opaque;
 | 
			
		||||
    uint64_t bytes_done = 0;
 | 
			
		||||
    QEMUIOVector hd_qiov;
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
 | 
			
		||||
    assert(!flags);
 | 
			
		||||
    qemu_iovec_init(&hd_qiov, qiov->niov);
 | 
			
		||||
 | 
			
		||||
    while (nb_sectors > 0) {
 | 
			
		||||
@@ -341,8 +315,7 @@ static coroutine_fn int parallels_co_writev(BlockDriverState *bs,
 | 
			
		||||
        qemu_iovec_reset(&hd_qiov);
 | 
			
		||||
        qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes);
 | 
			
		||||
 | 
			
		||||
        ret = bdrv_co_pwritev(bs->file, position * BDRV_SECTOR_SIZE, nbytes,
 | 
			
		||||
                              &hd_qiov, 0);
 | 
			
		||||
        ret = bdrv_co_writev(bs->file, position, n, &hd_qiov);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
@@ -381,8 +354,7 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs,
 | 
			
		||||
 | 
			
		||||
        if (position < 0) {
 | 
			
		||||
            if (bs->backing) {
 | 
			
		||||
                ret = bdrv_co_preadv(bs->backing, sector_num * BDRV_SECTOR_SIZE,
 | 
			
		||||
                                     nbytes, &hd_qiov, 0);
 | 
			
		||||
                ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov);
 | 
			
		||||
                if (ret < 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
@@ -390,8 +362,7 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs,
 | 
			
		||||
                qemu_iovec_memset(&hd_qiov, 0, 0, nbytes);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            ret = bdrv_co_preadv(bs->file, position * BDRV_SECTOR_SIZE, nbytes,
 | 
			
		||||
                                 &hd_qiov, 0);
 | 
			
		||||
            ret = bdrv_co_readv(bs->file, position, n, &hd_qiov);
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
@@ -509,67 +480,46 @@ out:
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts,
 | 
			
		||||
static int coroutine_fn parallels_co_create_opts(const char *filename,
 | 
			
		||||
                                                 QemuOpts *opts,
 | 
			
		||||
                                                 Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BlockdevCreateOptionsParallels *parallels_opts;
 | 
			
		||||
    BlockDriverState *bs;
 | 
			
		||||
    BlockBackend *blk;
 | 
			
		||||
    int64_t total_size, cl_size;
 | 
			
		||||
    uint8_t tmp[BDRV_SECTOR_SIZE];
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    BlockBackend *file;
 | 
			
		||||
    uint32_t bat_entries, bat_sectors;
 | 
			
		||||
    ParallelsHeader header;
 | 
			
		||||
    uint8_t tmp[BDRV_SECTOR_SIZE];
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    assert(opts->driver == BLOCKDEV_DRIVER_PARALLELS);
 | 
			
		||||
    parallels_opts = &opts->u.parallels;
 | 
			
		||||
 | 
			
		||||
    /* Sanity checks */
 | 
			
		||||
    total_size = parallels_opts->size;
 | 
			
		||||
 | 
			
		||||
    if (parallels_opts->has_cluster_size) {
 | 
			
		||||
        cl_size = parallels_opts->cluster_size;
 | 
			
		||||
    } else {
 | 
			
		||||
        cl_size = DEFAULT_CLUSTER_SIZE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* XXX What is the real limit here? This is an insanely large maximum. */
 | 
			
		||||
    if (cl_size >= INT64_MAX / MAX_PARALLELS_IMAGE_FACTOR) {
 | 
			
		||||
        error_setg(errp, "Cluster size is too large");
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
    total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
 | 
			
		||||
                          BDRV_SECTOR_SIZE);
 | 
			
		||||
    cl_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE,
 | 
			
		||||
                          DEFAULT_CLUSTER_SIZE), BDRV_SECTOR_SIZE);
 | 
			
		||||
    if (total_size >= MAX_PARALLELS_IMAGE_FACTOR * cl_size) {
 | 
			
		||||
        error_setg(errp, "Image size is too large for this cluster size");
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        return -E2BIG;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!QEMU_IS_ALIGNED(total_size, BDRV_SECTOR_SIZE)) {
 | 
			
		||||
        error_setg(errp, "Image size must be a multiple of 512 bytes");
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    ret = bdrv_create_file(filename, opts, &local_err);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!QEMU_IS_ALIGNED(cl_size, BDRV_SECTOR_SIZE)) {
 | 
			
		||||
        error_setg(errp, "Cluster size must be a multiple of 512 bytes");
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Create BlockBackend to write to the image */
 | 
			
		||||
    bs = bdrv_open_blockdev_ref(parallels_opts->file, errp);
 | 
			
		||||
    if (bs == NULL) {
 | 
			
		||||
    file = blk_new_open(filename, NULL, NULL,
 | 
			
		||||
                        BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
 | 
			
		||||
                        &local_err);
 | 
			
		||||
    if (file == NULL) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        return -EIO;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
 | 
			
		||||
    ret = blk_insert_bs(blk, bs, errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
    blk_set_allow_write_beyond_eof(blk, true);
 | 
			
		||||
    blk_set_allow_write_beyond_eof(file, true);
 | 
			
		||||
 | 
			
		||||
    /* Create image format */
 | 
			
		||||
    ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
 | 
			
		||||
    ret = blk_truncate(file, 0, PREALLOC_MODE_OFF, errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto out;
 | 
			
		||||
        goto exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bat_entries = DIV_ROUND_UP(total_size, cl_size);
 | 
			
		||||
@@ -592,103 +542,24 @@ static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts,
 | 
			
		||||
    memset(tmp, 0, sizeof(tmp));
 | 
			
		||||
    memcpy(tmp, &header, sizeof(header));
 | 
			
		||||
 | 
			
		||||
    ret = blk_pwrite(blk, 0, tmp, BDRV_SECTOR_SIZE, 0);
 | 
			
		||||
    ret = blk_pwrite(file, 0, tmp, BDRV_SECTOR_SIZE, 0);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto exit;
 | 
			
		||||
    }
 | 
			
		||||
    ret = blk_pwrite_zeroes(blk, BDRV_SECTOR_SIZE,
 | 
			
		||||
    ret = blk_pwrite_zeroes(file, BDRV_SECTOR_SIZE,
 | 
			
		||||
                            (bat_sectors - 1) << BDRV_SECTOR_BITS, 0);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = 0;
 | 
			
		||||
out:
 | 
			
		||||
    blk_unref(blk);
 | 
			
		||||
    bdrv_unref(bs);
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
    blk_unref(file);
 | 
			
		||||
    return ret;
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
    error_setg_errno(errp, -ret, "Failed to create Parallels image");
 | 
			
		||||
    goto out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn parallels_co_create_opts(const char *filename,
 | 
			
		||||
                                                 QemuOpts *opts,
 | 
			
		||||
                                                 Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BlockdevCreateOptions *create_options = NULL;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    BlockDriverState *bs = NULL;
 | 
			
		||||
    QDict *qdict;
 | 
			
		||||
    Visitor *v;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    static const QDictRenames opt_renames[] = {
 | 
			
		||||
        { BLOCK_OPT_CLUSTER_SIZE,       "cluster-size" },
 | 
			
		||||
        { NULL, NULL },
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /* Parse options and convert legacy syntax */
 | 
			
		||||
    qdict = qemu_opts_to_qdict_filtered(opts, NULL, ¶llels_create_opts,
 | 
			
		||||
                                        true);
 | 
			
		||||
 | 
			
		||||
    if (!qdict_rename_keys(qdict, opt_renames, errp)) {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
    goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Create and open the file (protocol layer) */
 | 
			
		||||
    ret = bdrv_create_file(filename, opts, &local_err);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs = bdrv_open(filename, NULL, NULL,
 | 
			
		||||
                   BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
 | 
			
		||||
    if (bs == NULL) {
 | 
			
		||||
        ret = -EIO;
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Now get the QAPI type BlockdevCreateOptions */
 | 
			
		||||
    qdict_put_str(qdict, "driver", "parallels");
 | 
			
		||||
    qdict_put_str(qdict, "file", bs->node_name);
 | 
			
		||||
 | 
			
		||||
    v = qobject_input_visitor_new_flat_confused(qdict, errp);
 | 
			
		||||
    if (!v) {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
 | 
			
		||||
    visit_free(v);
 | 
			
		||||
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Silently round up sizes */
 | 
			
		||||
    create_options->u.parallels.size =
 | 
			
		||||
        ROUND_UP(create_options->u.parallels.size, BDRV_SECTOR_SIZE);
 | 
			
		||||
    create_options->u.parallels.cluster_size =
 | 
			
		||||
        ROUND_UP(create_options->u.parallels.cluster_size, BDRV_SECTOR_SIZE);
 | 
			
		||||
 | 
			
		||||
    /* Create the Parallels image (format layer) */
 | 
			
		||||
    ret = parallels_co_create(create_options, errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
    ret = 0;
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
    qobject_unref(qdict);
 | 
			
		||||
    bdrv_unref(bs);
 | 
			
		||||
    qapi_free_BlockdevCreateOptions(create_options);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -900,6 +771,25 @@ static void parallels_close(BlockDriverState *bs)
 | 
			
		||||
    error_free(s->migration_blocker);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static QemuOptsList parallels_create_opts = {
 | 
			
		||||
    .name = "parallels-create-opts",
 | 
			
		||||
    .head = QTAILQ_HEAD_INITIALIZER(parallels_create_opts.head),
 | 
			
		||||
    .desc = {
 | 
			
		||||
        {
 | 
			
		||||
            .name = BLOCK_OPT_SIZE,
 | 
			
		||||
            .type = QEMU_OPT_SIZE,
 | 
			
		||||
            .help = "Virtual disk size",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = BLOCK_OPT_CLUSTER_SIZE,
 | 
			
		||||
            .type = QEMU_OPT_SIZE,
 | 
			
		||||
            .help = "Parallels image cluster size",
 | 
			
		||||
            .def_value_str = stringify(DEFAULT_CLUSTER_SIZE),
 | 
			
		||||
        },
 | 
			
		||||
        { /* end of list */ }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_parallels = {
 | 
			
		||||
    .format_name	= "parallels",
 | 
			
		||||
    .instance_size	= sizeof(BDRVParallelsState),
 | 
			
		||||
@@ -913,7 +803,6 @@ static BlockDriver bdrv_parallels = {
 | 
			
		||||
    .bdrv_co_readv  = parallels_co_readv,
 | 
			
		||||
    .bdrv_co_writev = parallels_co_writev,
 | 
			
		||||
    .supports_backing = true,
 | 
			
		||||
    .bdrv_co_create      = parallels_co_create,
 | 
			
		||||
    .bdrv_co_create_opts = parallels_co_create_opts,
 | 
			
		||||
    .bdrv_co_check  = parallels_co_check,
 | 
			
		||||
    .create_opts    = ¶llels_create_opts,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										74
									
								
								block/qapi.c
									
									
									
									
									
								
							
							
						
						
									
										74
									
								
								block/qapi.c
									
									
									
									
									
								
							@@ -394,37 +394,6 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
 | 
			
		||||
    qapi_free_BlockInfo(info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint64List *uint64_list(uint64_t *list, int size)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    uint64List *out_list = NULL;
 | 
			
		||||
    uint64List **pout_list = &out_list;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < size; i++) {
 | 
			
		||||
        uint64List *entry = g_new(uint64List, 1);
 | 
			
		||||
        entry->value = list[i];
 | 
			
		||||
        *pout_list = entry;
 | 
			
		||||
        pout_list = &entry->next;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *pout_list = NULL;
 | 
			
		||||
 | 
			
		||||
    return out_list;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdrv_latency_histogram_stats(BlockLatencyHistogram *hist,
 | 
			
		||||
                                         bool *not_null,
 | 
			
		||||
                                         BlockLatencyHistogramInfo **info)
 | 
			
		||||
{
 | 
			
		||||
    *not_null = hist->bins != NULL;
 | 
			
		||||
    if (*not_null) {
 | 
			
		||||
        *info = g_new0(BlockLatencyHistogramInfo, 1);
 | 
			
		||||
 | 
			
		||||
        (*info)->boundaries = uint64_list(hist->boundaries, hist->nbins - 1);
 | 
			
		||||
        (*info)->bins = uint64_list(hist->bins, hist->nbins);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
 | 
			
		||||
{
 | 
			
		||||
    BlockAcctStats *stats = blk_get_stats(blk);
 | 
			
		||||
@@ -490,16 +459,6 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
 | 
			
		||||
        dev_stats->avg_wr_queue_depth =
 | 
			
		||||
            block_acct_queue_depth(ts, BLOCK_ACCT_WRITE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_READ],
 | 
			
		||||
                                 &ds->has_x_rd_latency_histogram,
 | 
			
		||||
                                 &ds->x_rd_latency_histogram);
 | 
			
		||||
    bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_WRITE],
 | 
			
		||||
                                 &ds->has_x_wr_latency_histogram,
 | 
			
		||||
                                 &ds->x_wr_latency_histogram);
 | 
			
		||||
    bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_FLUSH],
 | 
			
		||||
                                 &ds->has_x_flush_latency_histogram,
 | 
			
		||||
                                 &ds->x_flush_latency_histogram);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs,
 | 
			
		||||
@@ -593,33 +552,18 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes,
 | 
			
		||||
            p_next = &info->next;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) {
 | 
			
		||||
            BlockStatsList *info;
 | 
			
		||||
        for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
 | 
			
		||||
            BlockStatsList *info = g_malloc0(sizeof(*info));
 | 
			
		||||
            AioContext *ctx = blk_get_aio_context(blk);
 | 
			
		||||
            BlockStats *s;
 | 
			
		||||
            char *qdev;
 | 
			
		||||
 | 
			
		||||
            if (!*blk_name(blk) && !blk_get_attached_dev(blk)) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            aio_context_acquire(ctx);
 | 
			
		||||
            s = bdrv_query_bds_stats(blk_bs(blk), true);
 | 
			
		||||
            s->has_device = true;
 | 
			
		||||
            s->device = g_strdup(blk_name(blk));
 | 
			
		||||
 | 
			
		||||
            qdev = blk_get_attached_dev_id(blk);
 | 
			
		||||
            if (qdev && *qdev) {
 | 
			
		||||
                s->has_qdev = true;
 | 
			
		||||
                s->qdev = qdev;
 | 
			
		||||
            } else {
 | 
			
		||||
                g_free(qdev);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            bdrv_query_blk_stats(s->stats, blk);
 | 
			
		||||
            aio_context_release(ctx);
 | 
			
		||||
 | 
			
		||||
            info = g_malloc0(sizeof(*info));
 | 
			
		||||
            info->value = s;
 | 
			
		||||
            *p_next = info;
 | 
			
		||||
            p_next = &info->next;
 | 
			
		||||
@@ -703,29 +647,29 @@ static void dump_qobject(fprintf_function func_fprintf, void *f,
 | 
			
		||||
{
 | 
			
		||||
    switch (qobject_type(obj)) {
 | 
			
		||||
        case QTYPE_QNUM: {
 | 
			
		||||
            QNum *value = qobject_to(QNum, obj);
 | 
			
		||||
            QNum *value = qobject_to_qnum(obj);
 | 
			
		||||
            char *tmp = qnum_to_string(value);
 | 
			
		||||
            func_fprintf(f, "%s", tmp);
 | 
			
		||||
            g_free(tmp);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case QTYPE_QSTRING: {
 | 
			
		||||
            QString *value = qobject_to(QString, obj);
 | 
			
		||||
            QString *value = qobject_to_qstring(obj);
 | 
			
		||||
            func_fprintf(f, "%s", qstring_get_str(value));
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case QTYPE_QDICT: {
 | 
			
		||||
            QDict *value = qobject_to(QDict, obj);
 | 
			
		||||
            QDict *value = qobject_to_qdict(obj);
 | 
			
		||||
            dump_qdict(func_fprintf, f, comp_indent, value);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case QTYPE_QLIST: {
 | 
			
		||||
            QList *value = qobject_to(QList, obj);
 | 
			
		||||
            QList *value = qobject_to_qlist(obj);
 | 
			
		||||
            dump_qlist(func_fprintf, f, comp_indent, value);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case QTYPE_QBOOL: {
 | 
			
		||||
            QBool *value = qobject_to(QBool, obj);
 | 
			
		||||
            QBool *value = qobject_to_qbool(obj);
 | 
			
		||||
            func_fprintf(f, "%s", qbool_get_bool(value) ? "true" : "false");
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
@@ -786,9 +730,9 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f,
 | 
			
		||||
 | 
			
		||||
    visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort);
 | 
			
		||||
    visit_complete(v, &obj);
 | 
			
		||||
    data = qdict_get(qobject_to(QDict, obj), "data");
 | 
			
		||||
    data = qdict_get(qobject_to_qdict(obj), "data");
 | 
			
		||||
    dump_qobject(func_fprintf, f, 1, data);
 | 
			
		||||
    qobject_unref(obj);
 | 
			
		||||
    qobject_decref(obj);
 | 
			
		||||
    visit_free(v);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										355
									
								
								block/qcow.c
									
									
									
									
									
								
							
							
						
						
									
										355
									
								
								block/qcow.c
									
									
									
									
									
								
							@@ -26,7 +26,6 @@
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
#include "qemu/error-report.h"
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
#include "block/qdict.h"
 | 
			
		||||
#include "sysemu/block-backend.h"
 | 
			
		||||
#include "qemu/module.h"
 | 
			
		||||
#include "qemu/option.h"
 | 
			
		||||
@@ -34,11 +33,9 @@
 | 
			
		||||
#include <zlib.h>
 | 
			
		||||
#include "qapi/qmp/qdict.h"
 | 
			
		||||
#include "qapi/qmp/qstring.h"
 | 
			
		||||
#include "qapi/qobject-input-visitor.h"
 | 
			
		||||
#include "qapi/qapi-visit-block-core.h"
 | 
			
		||||
#include "crypto/block.h"
 | 
			
		||||
#include "migration/blocker.h"
 | 
			
		||||
#include "crypto.h"
 | 
			
		||||
#include "block/crypto.h"
 | 
			
		||||
 | 
			
		||||
/**************************************************************/
 | 
			
		||||
/* QEMU COW block driver with compression and encryption support */
 | 
			
		||||
@@ -70,6 +67,7 @@ typedef struct QCowHeader {
 | 
			
		||||
typedef struct BDRVQcowState {
 | 
			
		||||
    int cluster_bits;
 | 
			
		||||
    int cluster_size;
 | 
			
		||||
    int cluster_sectors;
 | 
			
		||||
    int l2_bits;
 | 
			
		||||
    int l2_size;
 | 
			
		||||
    unsigned int l1_size;
 | 
			
		||||
@@ -88,8 +86,6 @@ typedef struct BDRVQcowState {
 | 
			
		||||
    Error *migration_blocker;
 | 
			
		||||
} BDRVQcowState;
 | 
			
		||||
 | 
			
		||||
static QemuOptsList qcow_create_opts;
 | 
			
		||||
 | 
			
		||||
static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
 | 
			
		||||
 | 
			
		||||
static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
 | 
			
		||||
@@ -140,14 +136,14 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    header.magic = be32_to_cpu(header.magic);
 | 
			
		||||
    header.version = be32_to_cpu(header.version);
 | 
			
		||||
    header.backing_file_offset = be64_to_cpu(header.backing_file_offset);
 | 
			
		||||
    header.backing_file_size = be32_to_cpu(header.backing_file_size);
 | 
			
		||||
    header.mtime = be32_to_cpu(header.mtime);
 | 
			
		||||
    header.size = be64_to_cpu(header.size);
 | 
			
		||||
    header.crypt_method = be32_to_cpu(header.crypt_method);
 | 
			
		||||
    header.l1_table_offset = be64_to_cpu(header.l1_table_offset);
 | 
			
		||||
    be32_to_cpus(&header.magic);
 | 
			
		||||
    be32_to_cpus(&header.version);
 | 
			
		||||
    be64_to_cpus(&header.backing_file_offset);
 | 
			
		||||
    be32_to_cpus(&header.backing_file_size);
 | 
			
		||||
    be32_to_cpus(&header.mtime);
 | 
			
		||||
    be64_to_cpus(&header.size);
 | 
			
		||||
    be32_to_cpus(&header.crypt_method);
 | 
			
		||||
    be64_to_cpus(&header.l1_table_offset);
 | 
			
		||||
 | 
			
		||||
    if (header.magic != QCOW_MAGIC) {
 | 
			
		||||
        error_setg(errp, "Image not in qcow format");
 | 
			
		||||
@@ -202,8 +198,9 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
                ret = -EINVAL;
 | 
			
		||||
                goto fail;
 | 
			
		||||
            }
 | 
			
		||||
            qdict_put_str(encryptopts, "format", "qcow");
 | 
			
		||||
            crypto_opts = block_crypto_open_opts_init(encryptopts, errp);
 | 
			
		||||
            qdict_del(encryptopts, "format");
 | 
			
		||||
            crypto_opts = block_crypto_open_opts_init(
 | 
			
		||||
                Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp);
 | 
			
		||||
            if (!crypto_opts) {
 | 
			
		||||
                ret = -EINVAL;
 | 
			
		||||
                goto fail;
 | 
			
		||||
@@ -213,7 +210,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
                cflags |= QCRYPTO_BLOCK_OPEN_NO_IO;
 | 
			
		||||
            }
 | 
			
		||||
            s->crypto = qcrypto_block_open(crypto_opts, "encrypt.",
 | 
			
		||||
                                           NULL, NULL, cflags, 1, errp);
 | 
			
		||||
                                           NULL, NULL, cflags, errp);
 | 
			
		||||
            if (!s->crypto) {
 | 
			
		||||
                ret = -EINVAL;
 | 
			
		||||
                goto fail;
 | 
			
		||||
@@ -234,6 +231,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    }
 | 
			
		||||
    s->cluster_bits = header.cluster_bits;
 | 
			
		||||
    s->cluster_size = 1 << s->cluster_bits;
 | 
			
		||||
    s->cluster_sectors = 1 << (s->cluster_bits - 9);
 | 
			
		||||
    s->l2_bits = header.l2_bits;
 | 
			
		||||
    s->l2_size = 1 << s->l2_bits;
 | 
			
		||||
    bs->total_sectors = header.size / 512;
 | 
			
		||||
@@ -270,7 +268,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for(i = 0;i < s->l1_size; i++) {
 | 
			
		||||
        s->l1_table[i] = be64_to_cpu(s->l1_table[i]);
 | 
			
		||||
        be64_to_cpus(&s->l1_table[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* alloc L2 cache (max. 64k * 16 * 8 = 8 MB) */
 | 
			
		||||
@@ -313,7 +311,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qobject_unref(encryptopts);
 | 
			
		||||
    QDECREF(encryptopts);
 | 
			
		||||
    qapi_free_QCryptoBlockOpenOptions(crypto_opts);
 | 
			
		||||
    qemu_co_mutex_init(&s->lock);
 | 
			
		||||
    return 0;
 | 
			
		||||
@@ -324,7 +322,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    g_free(s->cluster_cache);
 | 
			
		||||
    g_free(s->cluster_data);
 | 
			
		||||
    qcrypto_block_free(s->crypto);
 | 
			
		||||
    qobject_unref(encryptopts);
 | 
			
		||||
    QDECREF(encryptopts);
 | 
			
		||||
    qapi_free_QCryptoBlockOpenOptions(crypto_opts);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
@@ -343,8 +341,8 @@ static int qcow_reopen_prepare(BDRVReopenState *state,
 | 
			
		||||
 *
 | 
			
		||||
 * 0 to not allocate.
 | 
			
		||||
 *
 | 
			
		||||
 * 1 to allocate a normal cluster (for sector-aligned byte offsets 'n_start'
 | 
			
		||||
 * to 'n_end' within the cluster)
 | 
			
		||||
 * 1 to allocate a normal cluster (for sector indexes 'n_start' to
 | 
			
		||||
 * 'n_end')
 | 
			
		||||
 *
 | 
			
		||||
 * 2 to allocate a compressed cluster of size
 | 
			
		||||
 * 'compressed_size'. 'compressed_size' must be > 0 and <
 | 
			
		||||
@@ -438,10 +436,9 @@ static int get_cluster_offset(BlockDriverState *bs,
 | 
			
		||||
        if (!allocate)
 | 
			
		||||
            return 0;
 | 
			
		||||
        BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC);
 | 
			
		||||
        assert(QEMU_IS_ALIGNED(n_start | n_end, BDRV_SECTOR_SIZE));
 | 
			
		||||
        /* allocate a new cluster */
 | 
			
		||||
        if ((cluster_offset & QCOW_OFLAG_COMPRESSED) &&
 | 
			
		||||
            (n_end - n_start) < s->cluster_size) {
 | 
			
		||||
            (n_end - n_start) < s->cluster_sectors) {
 | 
			
		||||
            /* if the cluster is already compressed, we must
 | 
			
		||||
               decompress it in the case it is not completely
 | 
			
		||||
               overwritten */
 | 
			
		||||
@@ -479,15 +476,16 @@ static int get_cluster_offset(BlockDriverState *bs,
 | 
			
		||||
                /* if encrypted, we must initialize the cluster
 | 
			
		||||
                   content which won't be written */
 | 
			
		||||
                if (bs->encrypted &&
 | 
			
		||||
                    (n_end - n_start) < s->cluster_size) {
 | 
			
		||||
                    uint64_t start_offset;
 | 
			
		||||
                    (n_end - n_start) < s->cluster_sectors) {
 | 
			
		||||
                    uint64_t start_sect;
 | 
			
		||||
                    assert(s->crypto);
 | 
			
		||||
                    start_offset = offset & ~(s->cluster_size - 1);
 | 
			
		||||
                    for (i = 0; i < s->cluster_size; i += BDRV_SECTOR_SIZE) {
 | 
			
		||||
                    start_sect = (offset & ~(s->cluster_size - 1)) >> 9;
 | 
			
		||||
                    for(i = 0; i < s->cluster_sectors; i++) {
 | 
			
		||||
                        if (i < n_start || i >= n_end) {
 | 
			
		||||
                            memset(s->cluster_data, 0x00, BDRV_SECTOR_SIZE);
 | 
			
		||||
                            memset(s->cluster_data, 0x00, 512);
 | 
			
		||||
                            if (qcrypto_block_encrypt(s->crypto,
 | 
			
		||||
                                                      start_offset + i,
 | 
			
		||||
                                                      (start_sect + i) *
 | 
			
		||||
                                                      BDRV_SECTOR_SIZE,
 | 
			
		||||
                                                      s->cluster_data,
 | 
			
		||||
                                                      BDRV_SECTOR_SIZE,
 | 
			
		||||
                                                      NULL) < 0) {
 | 
			
		||||
@@ -495,9 +493,8 @@ static int get_cluster_offset(BlockDriverState *bs,
 | 
			
		||||
                            }
 | 
			
		||||
                            BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
 | 
			
		||||
                            ret = bdrv_pwrite(bs->file,
 | 
			
		||||
                                              cluster_offset + i,
 | 
			
		||||
                                              s->cluster_data,
 | 
			
		||||
                                              BDRV_SECTOR_SIZE);
 | 
			
		||||
                                              cluster_offset + i * 512,
 | 
			
		||||
                                              s->cluster_data, 512);
 | 
			
		||||
                            if (ret < 0) {
 | 
			
		||||
                                return ret;
 | 
			
		||||
                            }
 | 
			
		||||
@@ -611,21 +608,11 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qcow_refresh_limits(BlockDriverState *bs, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    /* At least encrypted images require 512-byte alignment. Apply the
 | 
			
		||||
     * limit universally, rather than just on encrypted images, as
 | 
			
		||||
     * it's easier to let the block layer handle rounding than to
 | 
			
		||||
     * audit this code further. */
 | 
			
		||||
    bs->bl.request_alignment = BDRV_SECTOR_SIZE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
                                       uint64_t bytes, QEMUIOVector *qiov,
 | 
			
		||||
                                       int flags)
 | 
			
		||||
static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                         int nb_sectors, QEMUIOVector *qiov)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    int offset_in_cluster;
 | 
			
		||||
    int index_in_cluster;
 | 
			
		||||
    int ret = 0, n;
 | 
			
		||||
    uint64_t cluster_offset;
 | 
			
		||||
    struct iovec hd_iov;
 | 
			
		||||
@@ -633,7 +620,6 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
    uint8_t *buf;
 | 
			
		||||
    void *orig_buf;
 | 
			
		||||
 | 
			
		||||
    assert(!flags);
 | 
			
		||||
    if (qiov->niov > 1) {
 | 
			
		||||
        buf = orig_buf = qemu_try_blockalign(bs, qiov->size);
 | 
			
		||||
        if (buf == NULL) {
 | 
			
		||||
@@ -646,35 +632,36 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
 | 
			
		||||
    qemu_co_mutex_lock(&s->lock);
 | 
			
		||||
 | 
			
		||||
    while (bytes != 0) {
 | 
			
		||||
    while (nb_sectors != 0) {
 | 
			
		||||
        /* prepare next request */
 | 
			
		||||
        ret = get_cluster_offset(bs, offset, 0, 0, 0, 0, &cluster_offset);
 | 
			
		||||
        ret = get_cluster_offset(bs, sector_num << 9,
 | 
			
		||||
                                 0, 0, 0, 0, &cluster_offset);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        offset_in_cluster = offset & (s->cluster_size - 1);
 | 
			
		||||
        n = s->cluster_size - offset_in_cluster;
 | 
			
		||||
        if (n > bytes) {
 | 
			
		||||
            n = bytes;
 | 
			
		||||
        index_in_cluster = sector_num & (s->cluster_sectors - 1);
 | 
			
		||||
        n = s->cluster_sectors - index_in_cluster;
 | 
			
		||||
        if (n > nb_sectors) {
 | 
			
		||||
            n = nb_sectors;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!cluster_offset) {
 | 
			
		||||
            if (bs->backing) {
 | 
			
		||||
                /* read from the base image */
 | 
			
		||||
                hd_iov.iov_base = (void *)buf;
 | 
			
		||||
                hd_iov.iov_len = n;
 | 
			
		||||
                hd_iov.iov_len = n * 512;
 | 
			
		||||
                qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
 | 
			
		||||
                qemu_co_mutex_unlock(&s->lock);
 | 
			
		||||
                /* qcow2 emits this on bs->file instead of bs->backing */
 | 
			
		||||
                BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
 | 
			
		||||
                ret = bdrv_co_preadv(bs->backing, offset, n, &hd_qiov, 0);
 | 
			
		||||
                ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov);
 | 
			
		||||
                qemu_co_mutex_lock(&s->lock);
 | 
			
		||||
                if (ret < 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                /* Note: in this case, no need to wait */
 | 
			
		||||
                memset(buf, 0, n);
 | 
			
		||||
                memset(buf, 0, 512 * n);
 | 
			
		||||
            }
 | 
			
		||||
        } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
 | 
			
		||||
            /* add AIO support for compressed blocks ? */
 | 
			
		||||
@@ -682,19 +669,21 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
                ret = -EIO;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            memcpy(buf, s->cluster_cache + offset_in_cluster, n);
 | 
			
		||||
            memcpy(buf,
 | 
			
		||||
                   s->cluster_cache + index_in_cluster * 512, 512 * n);
 | 
			
		||||
        } else {
 | 
			
		||||
            if ((cluster_offset & 511) != 0) {
 | 
			
		||||
                ret = -EIO;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            hd_iov.iov_base = (void *)buf;
 | 
			
		||||
            hd_iov.iov_len = n;
 | 
			
		||||
            hd_iov.iov_len = n * 512;
 | 
			
		||||
            qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
 | 
			
		||||
            qemu_co_mutex_unlock(&s->lock);
 | 
			
		||||
            BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
 | 
			
		||||
            ret = bdrv_co_preadv(bs->file, cluster_offset + offset_in_cluster,
 | 
			
		||||
                                 n, &hd_qiov, 0);
 | 
			
		||||
            ret = bdrv_co_readv(bs->file,
 | 
			
		||||
                                (cluster_offset >> 9) + index_in_cluster,
 | 
			
		||||
                                n, &hd_qiov);
 | 
			
		||||
            qemu_co_mutex_lock(&s->lock);
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
                break;
 | 
			
		||||
@@ -702,7 +691,8 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
            if (bs->encrypted) {
 | 
			
		||||
                assert(s->crypto);
 | 
			
		||||
                if (qcrypto_block_decrypt(s->crypto,
 | 
			
		||||
                                          offset, buf, n, NULL) < 0) {
 | 
			
		||||
                                          sector_num * BDRV_SECTOR_SIZE, buf,
 | 
			
		||||
                                          n * BDRV_SECTOR_SIZE, NULL) < 0) {
 | 
			
		||||
                    ret = -EIO;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
@@ -710,9 +700,9 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
        }
 | 
			
		||||
        ret = 0;
 | 
			
		||||
 | 
			
		||||
        bytes -= n;
 | 
			
		||||
        offset += n;
 | 
			
		||||
        buf += n;
 | 
			
		||||
        nb_sectors -= n;
 | 
			
		||||
        sector_num += n;
 | 
			
		||||
        buf += n * 512;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_co_mutex_unlock(&s->lock);
 | 
			
		||||
@@ -725,12 +715,11 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
                                        uint64_t bytes, QEMUIOVector *qiov,
 | 
			
		||||
                                        int flags)
 | 
			
		||||
static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                          int nb_sectors, QEMUIOVector *qiov)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    int offset_in_cluster;
 | 
			
		||||
    int index_in_cluster;
 | 
			
		||||
    uint64_t cluster_offset;
 | 
			
		||||
    int ret = 0, n;
 | 
			
		||||
    struct iovec hd_iov;
 | 
			
		||||
@@ -738,7 +727,6 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
    uint8_t *buf;
 | 
			
		||||
    void *orig_buf;
 | 
			
		||||
 | 
			
		||||
    assert(!flags);
 | 
			
		||||
    s->cluster_cache_offset = -1; /* disable compressed cache */
 | 
			
		||||
 | 
			
		||||
    /* We must always copy the iov when encrypting, so we
 | 
			
		||||
@@ -756,14 +744,16 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
 | 
			
		||||
    qemu_co_mutex_lock(&s->lock);
 | 
			
		||||
 | 
			
		||||
    while (bytes != 0) {
 | 
			
		||||
        offset_in_cluster = offset & (s->cluster_size - 1);
 | 
			
		||||
        n = s->cluster_size - offset_in_cluster;
 | 
			
		||||
        if (n > bytes) {
 | 
			
		||||
            n = bytes;
 | 
			
		||||
    while (nb_sectors != 0) {
 | 
			
		||||
 | 
			
		||||
        index_in_cluster = sector_num & (s->cluster_sectors - 1);
 | 
			
		||||
        n = s->cluster_sectors - index_in_cluster;
 | 
			
		||||
        if (n > nb_sectors) {
 | 
			
		||||
            n = nb_sectors;
 | 
			
		||||
        }
 | 
			
		||||
        ret = get_cluster_offset(bs, offset, 1, 0, offset_in_cluster,
 | 
			
		||||
                                 offset_in_cluster + n, &cluster_offset);
 | 
			
		||||
        ret = get_cluster_offset(bs, sector_num << 9, 1, 0,
 | 
			
		||||
                                 index_in_cluster,
 | 
			
		||||
                                 index_in_cluster + n, &cluster_offset);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
@@ -773,28 +763,30 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
        }
 | 
			
		||||
        if (bs->encrypted) {
 | 
			
		||||
            assert(s->crypto);
 | 
			
		||||
            if (qcrypto_block_encrypt(s->crypto, offset, buf, n, NULL) < 0) {
 | 
			
		||||
            if (qcrypto_block_encrypt(s->crypto, sector_num * BDRV_SECTOR_SIZE,
 | 
			
		||||
                                      buf, n * BDRV_SECTOR_SIZE, NULL) < 0) {
 | 
			
		||||
                ret = -EIO;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        hd_iov.iov_base = (void *)buf;
 | 
			
		||||
        hd_iov.iov_len = n;
 | 
			
		||||
        hd_iov.iov_len = n * 512;
 | 
			
		||||
        qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
 | 
			
		||||
        qemu_co_mutex_unlock(&s->lock);
 | 
			
		||||
        BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
 | 
			
		||||
        ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster,
 | 
			
		||||
                              n, &hd_qiov, 0);
 | 
			
		||||
        ret = bdrv_co_writev(bs->file,
 | 
			
		||||
                             (cluster_offset >> 9) + index_in_cluster,
 | 
			
		||||
                             n, &hd_qiov);
 | 
			
		||||
        qemu_co_mutex_lock(&s->lock);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        ret = 0;
 | 
			
		||||
 | 
			
		||||
        bytes -= n;
 | 
			
		||||
        offset += n;
 | 
			
		||||
        buf += n;
 | 
			
		||||
        nb_sectors -= n;
 | 
			
		||||
        sector_num += n;
 | 
			
		||||
        buf += n * 512;
 | 
			
		||||
    }
 | 
			
		||||
    qemu_co_mutex_unlock(&s->lock);
 | 
			
		||||
 | 
			
		||||
@@ -818,50 +810,62 @@ static void qcow_close(BlockDriverState *bs)
 | 
			
		||||
    error_free(s->migration_blocker);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
 | 
			
		||||
static int coroutine_fn qcow_co_create_opts(const char *filename, QemuOpts *opts,
 | 
			
		||||
                                            Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BlockdevCreateOptionsQcow *qcow_opts;
 | 
			
		||||
    int header_size, backing_filename_len, l1_size, shift, i;
 | 
			
		||||
    QCowHeader header;
 | 
			
		||||
    uint8_t *tmp;
 | 
			
		||||
    int64_t total_size = 0;
 | 
			
		||||
    char *backing_file = NULL;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    int ret;
 | 
			
		||||
    BlockDriverState *bs;
 | 
			
		||||
    BlockBackend *qcow_blk;
 | 
			
		||||
    char *encryptfmt = NULL;
 | 
			
		||||
    QDict *options;
 | 
			
		||||
    QDict *encryptopts = NULL;
 | 
			
		||||
    QCryptoBlockCreateOptions *crypto_opts = NULL;
 | 
			
		||||
    QCryptoBlock *crypto = NULL;
 | 
			
		||||
 | 
			
		||||
    assert(opts->driver == BLOCKDEV_DRIVER_QCOW);
 | 
			
		||||
    qcow_opts = &opts->u.qcow;
 | 
			
		||||
 | 
			
		||||
    /* Sanity checks */
 | 
			
		||||
    total_size = qcow_opts->size;
 | 
			
		||||
    /* Read out options */
 | 
			
		||||
    total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
 | 
			
		||||
                          BDRV_SECTOR_SIZE);
 | 
			
		||||
    if (total_size == 0) {
 | 
			
		||||
        error_setg(errp, "Image size is too small, cannot be zero length");
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto cleanup;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (qcow_opts->has_encrypt &&
 | 
			
		||||
        qcow_opts->encrypt->format != Q_CRYPTO_BLOCK_FORMAT_QCOW)
 | 
			
		||||
    {
 | 
			
		||||
        error_setg(errp, "Unsupported encryption format");
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
 | 
			
		||||
    encryptfmt = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT);
 | 
			
		||||
    if (encryptfmt) {
 | 
			
		||||
        if (qemu_opt_get(opts, BLOCK_OPT_ENCRYPT)) {
 | 
			
		||||
            error_setg(errp, "Options " BLOCK_OPT_ENCRYPT " and "
 | 
			
		||||
                       BLOCK_OPT_ENCRYPT_FORMAT " are mutually exclusive");
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
            goto cleanup;
 | 
			
		||||
        }
 | 
			
		||||
    } else if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ENCRYPT, false)) {
 | 
			
		||||
        encryptfmt = g_strdup("aes");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Create BlockBackend to write to the image */
 | 
			
		||||
    bs = bdrv_open_blockdev_ref(qcow_opts->file, errp);
 | 
			
		||||
    if (bs == NULL) {
 | 
			
		||||
        return -EIO;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qcow_blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
 | 
			
		||||
    ret = blk_insert_bs(qcow_blk, bs, errp);
 | 
			
		||||
    ret = bdrv_create_file(filename, opts, &local_err);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto exit;
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        goto cleanup;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qcow_blk = blk_new_open(filename, NULL, NULL,
 | 
			
		||||
                            BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
 | 
			
		||||
                            &local_err);
 | 
			
		||||
    if (qcow_blk == NULL) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        ret = -EIO;
 | 
			
		||||
        goto cleanup;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    blk_set_allow_write_beyond_eof(qcow_blk, true);
 | 
			
		||||
 | 
			
		||||
    /* Create image format */
 | 
			
		||||
    ret = blk_truncate(qcow_blk, 0, PREALLOC_MODE_OFF, errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto exit;
 | 
			
		||||
@@ -873,15 +877,16 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
 | 
			
		||||
    header.size = cpu_to_be64(total_size);
 | 
			
		||||
    header_size = sizeof(header);
 | 
			
		||||
    backing_filename_len = 0;
 | 
			
		||||
    if (qcow_opts->has_backing_file) {
 | 
			
		||||
        if (strcmp(qcow_opts->backing_file, "fat:")) {
 | 
			
		||||
    if (backing_file) {
 | 
			
		||||
        if (strcmp(backing_file, "fat:")) {
 | 
			
		||||
            header.backing_file_offset = cpu_to_be64(header_size);
 | 
			
		||||
            backing_filename_len = strlen(qcow_opts->backing_file);
 | 
			
		||||
            backing_filename_len = strlen(backing_file);
 | 
			
		||||
            header.backing_file_size = cpu_to_be32(backing_filename_len);
 | 
			
		||||
            header_size += backing_filename_len;
 | 
			
		||||
        } else {
 | 
			
		||||
            /* special backing file for vvfat */
 | 
			
		||||
            qcow_opts->has_backing_file = false;
 | 
			
		||||
            g_free(backing_file);
 | 
			
		||||
            backing_file = NULL;
 | 
			
		||||
        }
 | 
			
		||||
        header.cluster_bits = 9; /* 512 byte cluster to avoid copying
 | 
			
		||||
                                    unmodified sectors */
 | 
			
		||||
@@ -896,10 +901,26 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
 | 
			
		||||
 | 
			
		||||
    header.l1_table_offset = cpu_to_be64(header_size);
 | 
			
		||||
 | 
			
		||||
    if (qcow_opts->has_encrypt) {
 | 
			
		||||
    options = qemu_opts_to_qdict(opts, NULL);
 | 
			
		||||
    qdict_extract_subqdict(options, &encryptopts, "encrypt.");
 | 
			
		||||
    QDECREF(options);
 | 
			
		||||
    if (encryptfmt) {
 | 
			
		||||
        if (!g_str_equal(encryptfmt, "aes")) {
 | 
			
		||||
            error_setg(errp, "Unknown encryption format '%s', expected 'aes'",
 | 
			
		||||
                       encryptfmt);
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
            goto exit;
 | 
			
		||||
        }
 | 
			
		||||
        header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
 | 
			
		||||
 | 
			
		||||
        crypto = qcrypto_block_create(qcow_opts->encrypt, "encrypt.",
 | 
			
		||||
        crypto_opts = block_crypto_create_opts_init(
 | 
			
		||||
            Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp);
 | 
			
		||||
        if (!crypto_opts) {
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
            goto exit;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        crypto = qcrypto_block_create(crypto_opts, "encrypt.",
 | 
			
		||||
                                      NULL, NULL, NULL, errp);
 | 
			
		||||
        if (!crypto) {
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
@@ -915,9 +936,9 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
 | 
			
		||||
        goto exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (qcow_opts->has_backing_file) {
 | 
			
		||||
    if (backing_file) {
 | 
			
		||||
        ret = blk_pwrite(qcow_blk, sizeof(header),
 | 
			
		||||
                         qcow_opts->backing_file, backing_filename_len, 0);
 | 
			
		||||
                         backing_file, backing_filename_len, 0);
 | 
			
		||||
        if (ret != backing_filename_len) {
 | 
			
		||||
            goto exit;
 | 
			
		||||
        }
 | 
			
		||||
@@ -938,97 +959,12 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
 | 
			
		||||
    ret = 0;
 | 
			
		||||
exit:
 | 
			
		||||
    blk_unref(qcow_blk);
 | 
			
		||||
    bdrv_unref(bs);
 | 
			
		||||
cleanup:
 | 
			
		||||
    QDECREF(encryptopts);
 | 
			
		||||
    g_free(encryptfmt);
 | 
			
		||||
    qcrypto_block_free(crypto);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn qcow_co_create_opts(const char *filename,
 | 
			
		||||
                                            QemuOpts *opts, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BlockdevCreateOptions *create_options = NULL;
 | 
			
		||||
    BlockDriverState *bs = NULL;
 | 
			
		||||
    QDict *qdict;
 | 
			
		||||
    Visitor *v;
 | 
			
		||||
    const char *val;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    static const QDictRenames opt_renames[] = {
 | 
			
		||||
        { BLOCK_OPT_BACKING_FILE,       "backing-file" },
 | 
			
		||||
        { BLOCK_OPT_ENCRYPT,            BLOCK_OPT_ENCRYPT_FORMAT },
 | 
			
		||||
        { NULL, NULL },
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /* Parse options and convert legacy syntax */
 | 
			
		||||
    qdict = qemu_opts_to_qdict_filtered(opts, NULL, &qcow_create_opts, true);
 | 
			
		||||
 | 
			
		||||
    val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT);
 | 
			
		||||
    if (val && !strcmp(val, "on")) {
 | 
			
		||||
        qdict_put_str(qdict, BLOCK_OPT_ENCRYPT, "qcow");
 | 
			
		||||
    } else if (val && !strcmp(val, "off")) {
 | 
			
		||||
        qdict_del(qdict, BLOCK_OPT_ENCRYPT);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT);
 | 
			
		||||
    if (val && !strcmp(val, "aes")) {
 | 
			
		||||
        qdict_put_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT, "qcow");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!qdict_rename_keys(qdict, opt_renames, errp)) {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Create and open the file (protocol layer) */
 | 
			
		||||
    ret = bdrv_create_file(filename, opts, &local_err);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs = bdrv_open(filename, NULL, NULL,
 | 
			
		||||
                   BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
 | 
			
		||||
    if (bs == NULL) {
 | 
			
		||||
        ret = -EIO;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Now get the QAPI type BlockdevCreateOptions */
 | 
			
		||||
    qdict_put_str(qdict, "driver", "qcow");
 | 
			
		||||
    qdict_put_str(qdict, "file", bs->node_name);
 | 
			
		||||
 | 
			
		||||
    v = qobject_input_visitor_new_flat_confused(qdict, errp);
 | 
			
		||||
    if (!v) {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
 | 
			
		||||
    visit_free(v);
 | 
			
		||||
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Silently round up size */
 | 
			
		||||
    assert(create_options->driver == BLOCKDEV_DRIVER_QCOW);
 | 
			
		||||
    create_options->u.qcow.size =
 | 
			
		||||
        ROUND_UP(create_options->u.qcow.size, BDRV_SECTOR_SIZE);
 | 
			
		||||
 | 
			
		||||
    /* Create the qcow image (format layer) */
 | 
			
		||||
    ret = qcow_co_create(create_options, errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = 0;
 | 
			
		||||
fail:
 | 
			
		||||
    qobject_unref(qdict);
 | 
			
		||||
    bdrv_unref(bs);
 | 
			
		||||
    qapi_free_BlockdevCreateOptions(create_options);
 | 
			
		||||
    qapi_free_QCryptoBlockCreateOptions(crypto_opts);
 | 
			
		||||
    g_free(backing_file);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1110,7 +1046,8 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
 | 
			
		||||
    if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
 | 
			
		||||
        /* could not compress: write normal cluster */
 | 
			
		||||
        ret = qcow_co_pwritev(bs, offset, bytes, qiov, 0);
 | 
			
		||||
        ret = qcow_co_writev(bs, offset >> BDRV_SECTOR_BITS,
 | 
			
		||||
                             bytes >> BDRV_SECTOR_BITS, qiov);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
@@ -1191,14 +1128,12 @@ static BlockDriver bdrv_qcow = {
 | 
			
		||||
    .bdrv_close		= qcow_close,
 | 
			
		||||
    .bdrv_child_perm        = bdrv_format_default_perms,
 | 
			
		||||
    .bdrv_reopen_prepare    = qcow_reopen_prepare,
 | 
			
		||||
    .bdrv_co_create         = qcow_co_create,
 | 
			
		||||
    .bdrv_co_create_opts    = qcow_co_create_opts,
 | 
			
		||||
    .bdrv_has_zero_init     = bdrv_has_zero_init_1,
 | 
			
		||||
    .supports_backing       = true,
 | 
			
		||||
    .bdrv_refresh_limits    = qcow_refresh_limits,
 | 
			
		||||
 | 
			
		||||
    .bdrv_co_preadv         = qcow_co_preadv,
 | 
			
		||||
    .bdrv_co_pwritev        = qcow_co_pwritev,
 | 
			
		||||
    .bdrv_co_readv          = qcow_co_readv,
 | 
			
		||||
    .bdrv_co_writev         = qcow_co_writev,
 | 
			
		||||
    .bdrv_co_block_status   = qcow_co_block_status,
 | 
			
		||||
 | 
			
		||||
    .bdrv_make_empty        = qcow_make_empty,
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@
 | 
			
		||||
#include "qemu/cutils.h"
 | 
			
		||||
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
#include "qcow2.h"
 | 
			
		||||
#include "block/qcow2.h"
 | 
			
		||||
 | 
			
		||||
/* NOTICE: BME here means Bitmaps Extension and used as a namespace for
 | 
			
		||||
 * _internal_ constants. Please do not use this _internal_ abbreviation for
 | 
			
		||||
@@ -77,6 +77,8 @@ typedef struct Qcow2BitmapTable {
 | 
			
		||||
    uint32_t size; /* number of 64bit entries */
 | 
			
		||||
    QSIMPLEQ_ENTRY(Qcow2BitmapTable) entry;
 | 
			
		||||
} Qcow2BitmapTable;
 | 
			
		||||
typedef QSIMPLEQ_HEAD(Qcow2BitmapTableList, Qcow2BitmapTable)
 | 
			
		||||
    Qcow2BitmapTableList;
 | 
			
		||||
 | 
			
		||||
typedef struct Qcow2Bitmap {
 | 
			
		||||
    Qcow2BitmapTable table;
 | 
			
		||||
@@ -116,7 +118,7 @@ static inline void bitmap_table_to_be(uint64_t *bitmap_table, size_t size)
 | 
			
		||||
    size_t i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < size; ++i) {
 | 
			
		||||
        bitmap_table[i] = cpu_to_be64(bitmap_table[i]);
 | 
			
		||||
        cpu_to_be64s(&bitmap_table[i]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -229,7 +231,7 @@ static int bitmap_table_load(BlockDriverState *bs, Qcow2BitmapTable *tb,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < tb->size; ++i) {
 | 
			
		||||
        table[i] = be64_to_cpu(table[i]);
 | 
			
		||||
        be64_to_cpus(&table[i]);
 | 
			
		||||
        ret = check_table_entry(table[i], s->cluster_size);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            goto fail;
 | 
			
		||||
@@ -252,6 +254,7 @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb)
 | 
			
		||||
 | 
			
		||||
    ret = bitmap_table_load(bs, tb, &bitmap_table);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        assert(bitmap_table == NULL);
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -392,20 +395,20 @@ fail:
 | 
			
		||||
 | 
			
		||||
static inline void bitmap_dir_entry_to_cpu(Qcow2BitmapDirEntry *entry)
 | 
			
		||||
{
 | 
			
		||||
    entry->bitmap_table_offset = be64_to_cpu(entry->bitmap_table_offset);
 | 
			
		||||
    entry->bitmap_table_size = be32_to_cpu(entry->bitmap_table_size);
 | 
			
		||||
    entry->flags = be32_to_cpu(entry->flags);
 | 
			
		||||
    entry->name_size = be16_to_cpu(entry->name_size);
 | 
			
		||||
    entry->extra_data_size = be32_to_cpu(entry->extra_data_size);
 | 
			
		||||
    be64_to_cpus(&entry->bitmap_table_offset);
 | 
			
		||||
    be32_to_cpus(&entry->bitmap_table_size);
 | 
			
		||||
    be32_to_cpus(&entry->flags);
 | 
			
		||||
    be16_to_cpus(&entry->name_size);
 | 
			
		||||
    be32_to_cpus(&entry->extra_data_size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void bitmap_dir_entry_to_be(Qcow2BitmapDirEntry *entry)
 | 
			
		||||
{
 | 
			
		||||
    entry->bitmap_table_offset = cpu_to_be64(entry->bitmap_table_offset);
 | 
			
		||||
    entry->bitmap_table_size = cpu_to_be32(entry->bitmap_table_size);
 | 
			
		||||
    entry->flags = cpu_to_be32(entry->flags);
 | 
			
		||||
    entry->name_size = cpu_to_be16(entry->name_size);
 | 
			
		||||
    entry->extra_data_size = cpu_to_be32(entry->extra_data_size);
 | 
			
		||||
    cpu_to_be64s(&entry->bitmap_table_offset);
 | 
			
		||||
    cpu_to_be32s(&entry->bitmap_table_size);
 | 
			
		||||
    cpu_to_be32s(&entry->flags);
 | 
			
		||||
    cpu_to_be16s(&entry->name_size);
 | 
			
		||||
    cpu_to_be32s(&entry->extra_data_size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int calc_dir_entry_size(size_t name_size, size_t extra_data_size)
 | 
			
		||||
@@ -773,12 +776,7 @@ static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Actually, even in in-place case ignoring QCOW2_OL_BITMAP_DIRECTORY is not
 | 
			
		||||
     * necessary, because we drop QCOW2_AUTOCLEAR_BITMAPS when updating bitmap
 | 
			
		||||
     * directory in-place (actually, turn-off the extension), which is checked
 | 
			
		||||
     * in qcow2_check_metadata_overlap() */
 | 
			
		||||
    ret = qcow2_pre_write_overlap_check(
 | 
			
		||||
            bs, in_place ? QCOW2_OL_BITMAP_DIRECTORY : 0, dir_offset, dir_size);
 | 
			
		||||
    ret = qcow2_pre_write_overlap_check(bs, 0, dir_offset, dir_size);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
@@ -1006,8 +1004,7 @@ fail:
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
 | 
			
		||||
                                 Error **errp)
 | 
			
		||||
int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
    Qcow2BitmapList *bm_list;
 | 
			
		||||
@@ -1015,10 +1012,6 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
 | 
			
		||||
    GSList *ro_dirty_bitmaps = NULL;
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
 | 
			
		||||
    if (header_updated != NULL) {
 | 
			
		||||
        *header_updated = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (s->nb_bitmaps == 0) {
 | 
			
		||||
        /* No bitmaps - nothing to do */
 | 
			
		||||
        return 0;
 | 
			
		||||
@@ -1062,9 +1055,6 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
 | 
			
		||||
            error_setg_errno(errp, -ret, "Can't update bitmap directory");
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
        if (header_updated != NULL) {
 | 
			
		||||
            *header_updated = true;
 | 
			
		||||
        }
 | 
			
		||||
        g_slist_foreach(ro_dirty_bitmaps, set_readonly_helper, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1075,11 +1065,6 @@ out:
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    return qcow2_reopen_bitmaps_rw_hint(bs, NULL, errp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* store_bitmap_data()
 | 
			
		||||
 * Store bitmap to image, filling bitmap table accordingly.
 | 
			
		||||
 */
 | 
			
		||||
@@ -1314,7 +1299,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
 | 
			
		||||
    int ret;
 | 
			
		||||
    Qcow2BitmapList *bm_list;
 | 
			
		||||
    Qcow2Bitmap *bm;
 | 
			
		||||
    QSIMPLEQ_HEAD(, Qcow2BitmapTable) drop_tables;
 | 
			
		||||
    Qcow2BitmapTableList drop_tables;
 | 
			
		||||
    Qcow2BitmapTable *tb, *tb_next;
 | 
			
		||||
 | 
			
		||||
    if (!bdrv_has_changed_persistent_bitmaps(bs)) {
 | 
			
		||||
@@ -1416,22 +1401,6 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
 | 
			
		||||
        g_free(tb);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QSIMPLEQ_FOREACH(bm, bm_list, entry) {
 | 
			
		||||
        /* For safety, we remove bitmap after storing.
 | 
			
		||||
         * We may be here in two cases:
 | 
			
		||||
         * 1. bdrv_close. It's ok to drop bitmap.
 | 
			
		||||
         * 2. inactivation. It means migration without 'dirty-bitmaps'
 | 
			
		||||
         *    capability, so bitmaps are not marked with
 | 
			
		||||
         *    BdrvDirtyBitmap.migration flags. It's not bad to drop them too,
 | 
			
		||||
         *    and reload on invalidation.
 | 
			
		||||
         */
 | 
			
		||||
        if (bm->dirty_bitmap == NULL) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bdrv_release_dirty_bitmap(bs, bm->dirty_bitmap);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bitmap_list_free(bm_list);
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
#include "qcow2.h"
 | 
			
		||||
#include "block/qcow2.h"
 | 
			
		||||
#include "qemu/bswap.h"
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
 | 
			
		||||
@@ -994,17 +994,6 @@ err:
 | 
			
		||||
    return ret;
 | 
			
		||||
 }
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Frees the allocated clusters because the request failed and they won't
 | 
			
		||||
 * actually be linked.
 | 
			
		||||
 */
 | 
			
		||||
void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
    qcow2_free_clusters(bs, m->alloc_offset, m->nb_clusters << s->cluster_bits,
 | 
			
		||||
                        QCOW2_DISCARD_NEVER);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Returns the number of contiguous clusters that can be used for an allocating
 | 
			
		||||
 * write, but require COW to be performed (this includes yet unallocated space,
 | 
			
		||||
@@ -1571,6 +1560,76 @@ again:
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int decompress_buffer(uint8_t *out_buf, int out_buf_size,
 | 
			
		||||
                             const uint8_t *buf, int buf_size)
 | 
			
		||||
{
 | 
			
		||||
    z_stream strm1, *strm = &strm1;
 | 
			
		||||
    int ret, out_len;
 | 
			
		||||
 | 
			
		||||
    memset(strm, 0, sizeof(*strm));
 | 
			
		||||
 | 
			
		||||
    strm->next_in = (uint8_t *)buf;
 | 
			
		||||
    strm->avail_in = buf_size;
 | 
			
		||||
    strm->next_out = out_buf;
 | 
			
		||||
    strm->avail_out = out_buf_size;
 | 
			
		||||
 | 
			
		||||
    ret = inflateInit2(strm, -12);
 | 
			
		||||
    if (ret != Z_OK)
 | 
			
		||||
        return -1;
 | 
			
		||||
    ret = inflate(strm, Z_FINISH);
 | 
			
		||||
    out_len = strm->next_out - out_buf;
 | 
			
		||||
    if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) ||
 | 
			
		||||
        out_len != out_buf_size) {
 | 
			
		||||
        inflateEnd(strm);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    inflateEnd(strm);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
    int ret, csize, nb_csectors, sector_offset;
 | 
			
		||||
    uint64_t coffset;
 | 
			
		||||
 | 
			
		||||
    coffset = cluster_offset & s->cluster_offset_mask;
 | 
			
		||||
    if (s->cluster_cache_offset != coffset) {
 | 
			
		||||
        nb_csectors = ((cluster_offset >> s->csize_shift) & s->csize_mask) + 1;
 | 
			
		||||
        sector_offset = coffset & 511;
 | 
			
		||||
        csize = nb_csectors * 512 - sector_offset;
 | 
			
		||||
 | 
			
		||||
        /* Allocate buffers on first decompress operation, most images are
 | 
			
		||||
         * uncompressed and the memory overhead can be avoided.  The buffers
 | 
			
		||||
         * are freed in .bdrv_close().
 | 
			
		||||
         */
 | 
			
		||||
        if (!s->cluster_data) {
 | 
			
		||||
            /* one more sector for decompressed data alignment */
 | 
			
		||||
            s->cluster_data = qemu_try_blockalign(bs->file->bs,
 | 
			
		||||
                    QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size + 512);
 | 
			
		||||
            if (!s->cluster_data) {
 | 
			
		||||
                return -ENOMEM;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (!s->cluster_cache) {
 | 
			
		||||
            s->cluster_cache = g_malloc(s->cluster_size);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED);
 | 
			
		||||
        ret = bdrv_read(bs->file, coffset >> 9, s->cluster_data,
 | 
			
		||||
                        nb_csectors);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
        if (decompress_buffer(s->cluster_cache, s->cluster_size,
 | 
			
		||||
                              s->cluster_data + sector_offset, csize) < 0) {
 | 
			
		||||
            return -EIO;
 | 
			
		||||
        }
 | 
			
		||||
        s->cluster_cache_offset = coffset;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This discards as many clusters of nb_clusters as possible at once (i.e.
 | 
			
		||||
 * all clusters in the same L2 slice) and returns the number of discarded
 | 
			
		||||
 
 | 
			
		||||
@@ -26,13 +26,12 @@
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
#include "qcow2.h"
 | 
			
		||||
#include "block/qcow2.h"
 | 
			
		||||
#include "qemu/range.h"
 | 
			
		||||
#include "qemu/bswap.h"
 | 
			
		||||
#include "qemu/cutils.h"
 | 
			
		||||
 | 
			
		||||
static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size,
 | 
			
		||||
                                    uint64_t max);
 | 
			
		||||
static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size);
 | 
			
		||||
static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
 | 
			
		||||
                            int64_t offset, int64_t length, uint64_t addend,
 | 
			
		||||
                            bool decrease, enum qcow2_discard_type type);
 | 
			
		||||
@@ -363,14 +362,11 @@ static int alloc_refcount_block(BlockDriverState *bs,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Allocate the refcount block itself and mark it as used */
 | 
			
		||||
    int64_t new_block = alloc_clusters_noref(bs, s->cluster_size, INT64_MAX);
 | 
			
		||||
    int64_t new_block = alloc_clusters_noref(bs, s->cluster_size);
 | 
			
		||||
    if (new_block < 0) {
 | 
			
		||||
        return new_block;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* The offset must fit in the offset field of the refcount table entry */
 | 
			
		||||
    assert((new_block & REFT_OFFSET_MASK) == new_block);
 | 
			
		||||
 | 
			
		||||
    /* If we're allocating the block at offset 0 then something is wrong */
 | 
			
		||||
    if (new_block == 0) {
 | 
			
		||||
        qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid "
 | 
			
		||||
@@ -738,7 +734,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret)
 | 
			
		||||
 | 
			
		||||
        /* Discard is optional, ignore the return value */
 | 
			
		||||
        if (ret >= 0) {
 | 
			
		||||
            bdrv_pdiscard(bs->file, d->offset, d->bytes);
 | 
			
		||||
            bdrv_pdiscard(bs->file->bs, d->offset, d->bytes);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        g_free(d);
 | 
			
		||||
@@ -843,13 +839,6 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
 | 
			
		||||
                qcow2_cache_put(s->refcount_block_cache, &refcount_block);
 | 
			
		||||
            }
 | 
			
		||||
            ret = alloc_refcount_block(bs, cluster_index, &refcount_block);
 | 
			
		||||
            /* If the caller needs to restart the search for free clusters,
 | 
			
		||||
             * try the same ones first to see if they're still free. */
 | 
			
		||||
            if (ret == -EAGAIN) {
 | 
			
		||||
                if (s->free_cluster_index > (start >> s->cluster_bits)) {
 | 
			
		||||
                    s->free_cluster_index = (start >> s->cluster_bits);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
                goto fail;
 | 
			
		||||
            }
 | 
			
		||||
@@ -958,8 +947,7 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs,
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* return < 0 if error */
 | 
			
		||||
static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size,
 | 
			
		||||
                                    uint64_t max)
 | 
			
		||||
static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
    uint64_t i, nb_clusters, refcount;
 | 
			
		||||
@@ -984,9 +972,9 @@ retry:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Make sure that all offsets in the "allocated" range are representable
 | 
			
		||||
     * in the requested max */
 | 
			
		||||
     * in an int64_t */
 | 
			
		||||
    if (s->free_cluster_index > 0 &&
 | 
			
		||||
        s->free_cluster_index - 1 > (max >> s->cluster_bits))
 | 
			
		||||
        s->free_cluster_index - 1 > (INT64_MAX >> s->cluster_bits))
 | 
			
		||||
    {
 | 
			
		||||
        return -EFBIG;
 | 
			
		||||
    }
 | 
			
		||||
@@ -1006,7 +994,7 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size)
 | 
			
		||||
 | 
			
		||||
    BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC);
 | 
			
		||||
    do {
 | 
			
		||||
        offset = alloc_clusters_noref(bs, size, QCOW_MAX_CLUSTER_OFFSET);
 | 
			
		||||
        offset = alloc_clusters_noref(bs, size);
 | 
			
		||||
        if (offset < 0) {
 | 
			
		||||
            return offset;
 | 
			
		||||
        }
 | 
			
		||||
@@ -1088,11 +1076,7 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
 | 
			
		||||
    free_in_cluster = s->cluster_size - offset_into_cluster(s, offset);
 | 
			
		||||
    do {
 | 
			
		||||
        if (!offset || free_in_cluster < size) {
 | 
			
		||||
            int64_t new_cluster;
 | 
			
		||||
 | 
			
		||||
            new_cluster = alloc_clusters_noref(bs, s->cluster_size,
 | 
			
		||||
                                               MIN(s->cluster_offset_mask,
 | 
			
		||||
                                                   QCOW_MAX_CLUSTER_OFFSET));
 | 
			
		||||
            int64_t new_cluster = alloc_clusters_noref(bs, s->cluster_size);
 | 
			
		||||
            if (new_cluster < 0) {
 | 
			
		||||
                return new_cluster;
 | 
			
		||||
            }
 | 
			
		||||
@@ -1586,9 +1570,9 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
 | 
			
		||||
        case QCOW2_CLUSTER_COMPRESSED:
 | 
			
		||||
            /* Compressed clusters don't have QCOW_OFLAG_COPIED */
 | 
			
		||||
            if (l2_entry & QCOW_OFLAG_COPIED) {
 | 
			
		||||
                fprintf(stderr, "ERROR: coffset=0x%" PRIx64 ": "
 | 
			
		||||
                fprintf(stderr, "ERROR: cluster %" PRId64 ": "
 | 
			
		||||
                    "copied flag must never be set for compressed "
 | 
			
		||||
                    "clusters\n", l2_entry & s->cluster_offset_mask);
 | 
			
		||||
                    "clusters\n", l2_entry >> s->cluster_bits);
 | 
			
		||||
                l2_entry &= ~QCOW_OFLAG_COPIED;
 | 
			
		||||
                res->corruptions++;
 | 
			
		||||
            }
 | 
			
		||||
@@ -1808,19 +1792,6 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
 | 
			
		||||
    int ret;
 | 
			
		||||
    uint64_t refcount;
 | 
			
		||||
    int i, j;
 | 
			
		||||
    bool repair;
 | 
			
		||||
 | 
			
		||||
    if (fix & BDRV_FIX_ERRORS) {
 | 
			
		||||
        /* Always repair */
 | 
			
		||||
        repair = true;
 | 
			
		||||
    } else if (fix & BDRV_FIX_LEAKS) {
 | 
			
		||||
        /* Repair only if that seems safe: This function is always
 | 
			
		||||
         * called after the refcounts have been fixed, so the refcount
 | 
			
		||||
         * is accurate if that repair was successful */
 | 
			
		||||
        repair = !res->check_errors && !res->corruptions && !res->leaks;
 | 
			
		||||
    } else {
 | 
			
		||||
        repair = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < s->l1_size; i++) {
 | 
			
		||||
        uint64_t l1_entry = s->l1_table[i];
 | 
			
		||||
@@ -1840,8 +1811,10 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
 | 
			
		||||
        if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) {
 | 
			
		||||
            fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d "
 | 
			
		||||
                    "l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
 | 
			
		||||
                    repair ? "Repairing" : "ERROR", i, l1_entry, refcount);
 | 
			
		||||
            if (repair) {
 | 
			
		||||
                    fix & BDRV_FIX_ERRORS ? "Repairing" :
 | 
			
		||||
                                            "ERROR",
 | 
			
		||||
                    i, l1_entry, refcount);
 | 
			
		||||
            if (fix & BDRV_FIX_ERRORS) {
 | 
			
		||||
                s->l1_table[i] = refcount == 1
 | 
			
		||||
                               ? l1_entry |  QCOW_OFLAG_COPIED
 | 
			
		||||
                               : l1_entry & ~QCOW_OFLAG_COPIED;
 | 
			
		||||
@@ -1882,8 +1855,10 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
 | 
			
		||||
                if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) {
 | 
			
		||||
                    fprintf(stderr, "%s OFLAG_COPIED data cluster: "
 | 
			
		||||
                            "l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
 | 
			
		||||
                            repair ? "Repairing" : "ERROR", l2_entry, refcount);
 | 
			
		||||
                    if (repair) {
 | 
			
		||||
                            fix & BDRV_FIX_ERRORS ? "Repairing" :
 | 
			
		||||
                                                    "ERROR",
 | 
			
		||||
                            l2_entry, refcount);
 | 
			
		||||
                    if (fix & BDRV_FIX_ERRORS) {
 | 
			
		||||
                        l2_table[j] = cpu_to_be64(refcount == 1
 | 
			
		||||
                                    ? l2_entry |  QCOW_OFLAG_COPIED
 | 
			
		||||
                                    : l2_entry & ~QCOW_OFLAG_COPIED);
 | 
			
		||||
@@ -2714,16 +2689,6 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((chk & QCOW2_OL_BITMAP_DIRECTORY) &&
 | 
			
		||||
        (s->autoclear_features & QCOW2_AUTOCLEAR_BITMAPS))
 | 
			
		||||
    {
 | 
			
		||||
        if (overlaps_with(s->bitmap_directory_offset,
 | 
			
		||||
                          s->bitmap_directory_size))
 | 
			
		||||
        {
 | 
			
		||||
            return QCOW2_OL_BITMAP_DIRECTORY;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -2736,9 +2701,7 @@ static const char *metadata_ol_names[] = {
 | 
			
		||||
    [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table",
 | 
			
		||||
    [QCOW2_OL_INACTIVE_L1_BITNR]    = "inactive L1 table",
 | 
			
		||||
    [QCOW2_OL_INACTIVE_L2_BITNR]    = "inactive L2 table",
 | 
			
		||||
    [QCOW2_OL_BITMAP_DIRECTORY_BITNR]   = "bitmap directory",
 | 
			
		||||
};
 | 
			
		||||
QEMU_BUILD_BUG_ON(QCOW2_OL_MAX_BITNR != ARRAY_SIZE(metadata_ol_names));
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * First performs a check for metadata overlaps (through
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
#include "qcow2.h"
 | 
			
		||||
#include "block/qcow2.h"
 | 
			
		||||
#include "qemu/bswap.h"
 | 
			
		||||
#include "qemu/error-report.h"
 | 
			
		||||
#include "qemu/cutils.h"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										935
									
								
								block/qcow2.c
									
									
									
									
									
								
							
							
						
						
									
										935
									
								
								block/qcow2.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -27,7 +27,6 @@
 | 
			
		||||
 | 
			
		||||
#include "crypto/block.h"
 | 
			
		||||
#include "qemu/coroutine.h"
 | 
			
		||||
#include "qemu/units.h"
 | 
			
		||||
 | 
			
		||||
//#define DEBUG_ALLOC
 | 
			
		||||
//#define DEBUG_ALLOC2
 | 
			
		||||
@@ -42,19 +41,13 @@
 | 
			
		||||
#define QCOW_MAX_CRYPT_CLUSTERS 32
 | 
			
		||||
#define QCOW_MAX_SNAPSHOTS 65536
 | 
			
		||||
 | 
			
		||||
/* Field widths in qcow2 mean normal cluster offsets cannot reach
 | 
			
		||||
 * 64PB; depending on cluster size, compressed clusters can have a
 | 
			
		||||
 * smaller limit (64PB for up to 16k clusters, then ramps down to
 | 
			
		||||
 * 512TB for 2M clusters).  */
 | 
			
		||||
#define QCOW_MAX_CLUSTER_OFFSET ((1ULL << 56) - 1)
 | 
			
		||||
 | 
			
		||||
/* 8 MB refcount table is enough for 2 PB images at 64k cluster size
 | 
			
		||||
 * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
 | 
			
		||||
#define QCOW_MAX_REFTABLE_SIZE (8 * MiB)
 | 
			
		||||
#define QCOW_MAX_REFTABLE_SIZE 0x800000
 | 
			
		||||
 | 
			
		||||
/* 32 MB L1 table is enough for 2 PB images at 64k cluster size
 | 
			
		||||
 * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
 | 
			
		||||
#define QCOW_MAX_L1_SIZE (32 * MiB)
 | 
			
		||||
#define QCOW_MAX_L1_SIZE 0x2000000
 | 
			
		||||
 | 
			
		||||
/* Allow for an average of 1k per snapshot table entry, should be plenty of
 | 
			
		||||
 * space for snapshot names and IDs */
 | 
			
		||||
@@ -80,17 +73,17 @@
 | 
			
		||||
/* Must be at least 4 to cover all cases of refcount table growth */
 | 
			
		||||
#define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_LINUX
 | 
			
		||||
#define DEFAULT_L2_CACHE_MAX_SIZE (32 * MiB)
 | 
			
		||||
#define DEFAULT_CACHE_CLEAN_INTERVAL 600  /* seconds */
 | 
			
		||||
#else
 | 
			
		||||
#define DEFAULT_L2_CACHE_MAX_SIZE (8 * MiB)
 | 
			
		||||
/* Cache clean interval is currently available only on Linux, so must be 0 */
 | 
			
		||||
#define DEFAULT_CACHE_CLEAN_INTERVAL 0
 | 
			
		||||
#endif
 | 
			
		||||
/* Whichever is more */
 | 
			
		||||
#define DEFAULT_L2_CACHE_CLUSTERS 8 /* clusters */
 | 
			
		||||
#define DEFAULT_L2_CACHE_BYTE_SIZE 1048576 /* bytes */
 | 
			
		||||
 | 
			
		||||
/* The refblock cache needs only a fourth of the L2 cache size to cover as many
 | 
			
		||||
 * clusters */
 | 
			
		||||
#define DEFAULT_L2_REFCOUNT_SIZE_RATIO 4
 | 
			
		||||
 | 
			
		||||
#define DEFAULT_CLUSTER_SIZE 65536
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define QCOW2_OPT_LAZY_REFCOUNTS "lazy-refcounts"
 | 
			
		||||
#define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request"
 | 
			
		||||
#define QCOW2_OPT_DISCARD_SNAPSHOT "pass-discard-snapshot"
 | 
			
		||||
@@ -105,7 +98,6 @@
 | 
			
		||||
#define QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE "overlap-check.snapshot-table"
 | 
			
		||||
#define QCOW2_OPT_OVERLAP_INACTIVE_L1 "overlap-check.inactive-l1"
 | 
			
		||||
#define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2"
 | 
			
		||||
#define QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY "overlap-check.bitmap-directory"
 | 
			
		||||
#define QCOW2_OPT_CACHE_SIZE "cache-size"
 | 
			
		||||
#define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size"
 | 
			
		||||
#define QCOW2_OPT_L2_CACHE_ENTRY_SIZE "l2-cache-entry-size"
 | 
			
		||||
@@ -281,7 +273,7 @@ typedef struct BDRVQcow2State {
 | 
			
		||||
    uint8_t *cluster_cache;
 | 
			
		||||
    uint8_t *cluster_data;
 | 
			
		||||
    uint64_t cluster_cache_offset;
 | 
			
		||||
    QLIST_HEAD(, QCowL2Meta) cluster_allocs;
 | 
			
		||||
    QLIST_HEAD(QCowClusterAlloc, QCowL2Meta) cluster_allocs;
 | 
			
		||||
 | 
			
		||||
    uint64_t *refcount_table;
 | 
			
		||||
    uint64_t refcount_table_offset;
 | 
			
		||||
@@ -337,9 +329,6 @@ typedef struct BDRVQcow2State {
 | 
			
		||||
     * override) */
 | 
			
		||||
    char *image_backing_file;
 | 
			
		||||
    char *image_backing_format;
 | 
			
		||||
 | 
			
		||||
    CoQueue compress_wait_queue;
 | 
			
		||||
    int nb_compress_threads;
 | 
			
		||||
} BDRVQcow2State;
 | 
			
		||||
 | 
			
		||||
typedef struct Qcow2COWRegion {
 | 
			
		||||
@@ -419,9 +408,8 @@ typedef enum QCow2MetadataOverlap {
 | 
			
		||||
    QCOW2_OL_SNAPSHOT_TABLE_BITNR = 5,
 | 
			
		||||
    QCOW2_OL_INACTIVE_L1_BITNR    = 6,
 | 
			
		||||
    QCOW2_OL_INACTIVE_L2_BITNR    = 7,
 | 
			
		||||
    QCOW2_OL_BITMAP_DIRECTORY_BITNR = 8,
 | 
			
		||||
 | 
			
		||||
    QCOW2_OL_MAX_BITNR              = 9,
 | 
			
		||||
    QCOW2_OL_MAX_BITNR            = 8,
 | 
			
		||||
 | 
			
		||||
    QCOW2_OL_NONE           = 0,
 | 
			
		||||
    QCOW2_OL_MAIN_HEADER    = (1 << QCOW2_OL_MAIN_HEADER_BITNR),
 | 
			
		||||
@@ -434,13 +422,12 @@ typedef enum QCow2MetadataOverlap {
 | 
			
		||||
    /* NOTE: Checking overlaps with inactive L2 tables will result in bdrv
 | 
			
		||||
     * reads. */
 | 
			
		||||
    QCOW2_OL_INACTIVE_L2    = (1 << QCOW2_OL_INACTIVE_L2_BITNR),
 | 
			
		||||
    QCOW2_OL_BITMAP_DIRECTORY = (1 << QCOW2_OL_BITMAP_DIRECTORY_BITNR),
 | 
			
		||||
} QCow2MetadataOverlap;
 | 
			
		||||
 | 
			
		||||
/* Perform all overlap checks which can be done in constant time */
 | 
			
		||||
#define QCOW2_OL_CONSTANT \
 | 
			
		||||
    (QCOW2_OL_MAIN_HEADER | QCOW2_OL_ACTIVE_L1 | QCOW2_OL_REFCOUNT_TABLE | \
 | 
			
		||||
     QCOW2_OL_SNAPSHOT_TABLE | QCOW2_OL_BITMAP_DIRECTORY)
 | 
			
		||||
     QCOW2_OL_SNAPSHOT_TABLE)
 | 
			
		||||
 | 
			
		||||
/* Perform all overlap checks which don't require disk access */
 | 
			
		||||
#define QCOW2_OL_CACHED \
 | 
			
		||||
@@ -616,6 +603,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
 | 
			
		||||
                        bool exact_size);
 | 
			
		||||
int qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t max_size);
 | 
			
		||||
int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index);
 | 
			
		||||
int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
 | 
			
		||||
int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num,
 | 
			
		||||
                          uint8_t *buf, int nb_sectors, bool enc, Error **errp);
 | 
			
		||||
 | 
			
		||||
@@ -629,7 +617,6 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
 | 
			
		||||
                                         int compressed_size);
 | 
			
		||||
 | 
			
		||||
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
 | 
			
		||||
void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m);
 | 
			
		||||
int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
                          uint64_t bytes, enum qcow2_discard_type type,
 | 
			
		||||
                          bool full_discard);
 | 
			
		||||
@@ -684,8 +671,6 @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
 | 
			
		||||
                                  void **refcount_table,
 | 
			
		||||
                                  int64_t *refcount_table_size);
 | 
			
		||||
bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp);
 | 
			
		||||
int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
 | 
			
		||||
                                 Error **errp);
 | 
			
		||||
int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp);
 | 
			
		||||
void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp);
 | 
			
		||||
int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										219
									
								
								block/qed.c
									
									
									
									
									
								
							
							
						
						
									
										219
									
								
								block/qed.c
									
									
									
									
									
								
							@@ -13,7 +13,6 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include "block/qdict.h"
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
#include "qemu/timer.h"
 | 
			
		||||
#include "qemu/bswap.h"
 | 
			
		||||
@@ -21,11 +20,6 @@
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
#include "qed.h"
 | 
			
		||||
#include "sysemu/block-backend.h"
 | 
			
		||||
#include "qapi/qmp/qdict.h"
 | 
			
		||||
#include "qapi/qobject-input-visitor.h"
 | 
			
		||||
#include "qapi/qapi-visit-block-core.h"
 | 
			
		||||
 | 
			
		||||
static QemuOptsList qed_create_opts;
 | 
			
		||||
 | 
			
		||||
static int bdrv_qed_probe(const uint8_t *buf, int buf_size,
 | 
			
		||||
                          const char *filename)
 | 
			
		||||
@@ -559,7 +553,6 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    if (qemu_in_coroutine()) {
 | 
			
		||||
        bdrv_qed_open_entry(&qoc);
 | 
			
		||||
    } else {
 | 
			
		||||
        assert(qemu_get_current_aio_context() == qemu_get_aio_context());
 | 
			
		||||
        qemu_coroutine_enter(qemu_coroutine_create(bdrv_qed_open_entry, &qoc));
 | 
			
		||||
        BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS);
 | 
			
		||||
    }
 | 
			
		||||
@@ -601,78 +594,43 @@ static void bdrv_qed_close(BlockDriverState *bs)
 | 
			
		||||
    qemu_vfree(s->l1_table);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
 | 
			
		||||
                                           Error **errp)
 | 
			
		||||
static int qed_create(const char *filename, uint32_t cluster_size,
 | 
			
		||||
                      uint64_t image_size, uint32_t table_size,
 | 
			
		||||
                      const char *backing_file, const char *backing_fmt,
 | 
			
		||||
                      QemuOpts *opts, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BlockdevCreateOptionsQed *qed_opts;
 | 
			
		||||
    BlockBackend *blk = NULL;
 | 
			
		||||
    BlockDriverState *bs = NULL;
 | 
			
		||||
 | 
			
		||||
    QEDHeader header;
 | 
			
		||||
    QEDHeader le_header;
 | 
			
		||||
    uint8_t *l1_table = NULL;
 | 
			
		||||
    size_t l1_size;
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
 | 
			
		||||
    assert(opts->driver == BLOCKDEV_DRIVER_QED);
 | 
			
		||||
    qed_opts = &opts->u.qed;
 | 
			
		||||
 | 
			
		||||
    /* Validate options and set default values */
 | 
			
		||||
    if (!qed_opts->has_cluster_size) {
 | 
			
		||||
        qed_opts->cluster_size = QED_DEFAULT_CLUSTER_SIZE;
 | 
			
		||||
    }
 | 
			
		||||
    if (!qed_opts->has_table_size) {
 | 
			
		||||
        qed_opts->table_size = QED_DEFAULT_TABLE_SIZE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!qed_is_cluster_size_valid(qed_opts->cluster_size)) {
 | 
			
		||||
        error_setg(errp, "QED cluster size must be within range [%u, %u] "
 | 
			
		||||
                         "and power of 2",
 | 
			
		||||
                   QED_MIN_CLUSTER_SIZE, QED_MAX_CLUSTER_SIZE);
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
    if (!qed_is_table_size_valid(qed_opts->table_size)) {
 | 
			
		||||
        error_setg(errp, "QED table size must be within range [%u, %u] "
 | 
			
		||||
                         "and power of 2",
 | 
			
		||||
                   QED_MIN_TABLE_SIZE, QED_MAX_TABLE_SIZE);
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
    if (!qed_is_image_size_valid(qed_opts->size, qed_opts->cluster_size,
 | 
			
		||||
                                 qed_opts->table_size))
 | 
			
		||||
    {
 | 
			
		||||
        error_setg(errp, "QED image size must be a non-zero multiple of "
 | 
			
		||||
                         "cluster size and less than %" PRIu64 " bytes",
 | 
			
		||||
                   qed_max_image_size(qed_opts->cluster_size,
 | 
			
		||||
                                      qed_opts->table_size));
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Create BlockBackend to write to the image */
 | 
			
		||||
    bs = bdrv_open_blockdev_ref(qed_opts->file, errp);
 | 
			
		||||
    if (bs == NULL) {
 | 
			
		||||
        return -EIO;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
 | 
			
		||||
    ret = blk_insert_bs(blk, bs, errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
    blk_set_allow_write_beyond_eof(blk, true);
 | 
			
		||||
 | 
			
		||||
    /* Prepare image format */
 | 
			
		||||
    header = (QEDHeader) {
 | 
			
		||||
    QEDHeader header = {
 | 
			
		||||
        .magic = QED_MAGIC,
 | 
			
		||||
        .cluster_size = qed_opts->cluster_size,
 | 
			
		||||
        .table_size = qed_opts->table_size,
 | 
			
		||||
        .cluster_size = cluster_size,
 | 
			
		||||
        .table_size = table_size,
 | 
			
		||||
        .header_size = 1,
 | 
			
		||||
        .features = 0,
 | 
			
		||||
        .compat_features = 0,
 | 
			
		||||
        .l1_table_offset = qed_opts->cluster_size,
 | 
			
		||||
        .image_size = qed_opts->size,
 | 
			
		||||
        .l1_table_offset = cluster_size,
 | 
			
		||||
        .image_size = image_size,
 | 
			
		||||
    };
 | 
			
		||||
    QEDHeader le_header;
 | 
			
		||||
    uint8_t *l1_table = NULL;
 | 
			
		||||
    size_t l1_size = header.cluster_size * header.table_size;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
    BlockBackend *blk;
 | 
			
		||||
 | 
			
		||||
    l1_size = header.cluster_size * header.table_size;
 | 
			
		||||
    ret = bdrv_create_file(filename, opts, &local_err);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    blk = blk_new_open(filename, NULL, NULL,
 | 
			
		||||
                       BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
 | 
			
		||||
                       &local_err);
 | 
			
		||||
    if (blk == NULL) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        return -EIO;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    blk_set_allow_write_beyond_eof(blk, true);
 | 
			
		||||
 | 
			
		||||
    /* File must start empty and grow, check truncate is supported */
 | 
			
		||||
    ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
 | 
			
		||||
@@ -680,25 +638,22 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (qed_opts->has_backing_file) {
 | 
			
		||||
    if (backing_file) {
 | 
			
		||||
        header.features |= QED_F_BACKING_FILE;
 | 
			
		||||
        header.backing_filename_offset = sizeof(le_header);
 | 
			
		||||
        header.backing_filename_size = strlen(qed_opts->backing_file);
 | 
			
		||||
        header.backing_filename_size = strlen(backing_file);
 | 
			
		||||
 | 
			
		||||
        if (qed_opts->has_backing_fmt) {
 | 
			
		||||
            const char *backing_fmt = BlockdevDriver_str(qed_opts->backing_fmt);
 | 
			
		||||
        if (qed_fmt_is_raw(backing_fmt)) {
 | 
			
		||||
            header.features |= QED_F_BACKING_FORMAT_NO_PROBE;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qed_header_cpu_to_le(&header, &le_header);
 | 
			
		||||
    ret = blk_pwrite(blk, 0, &le_header, sizeof(le_header), 0);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
    ret = blk_pwrite(blk, sizeof(le_header), qed_opts->backing_file,
 | 
			
		||||
    ret = blk_pwrite(blk, sizeof(le_header), backing_file,
 | 
			
		||||
                     header.backing_filename_size, 0);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto out;
 | 
			
		||||
@@ -714,7 +669,6 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
 | 
			
		||||
out:
 | 
			
		||||
    g_free(l1_table);
 | 
			
		||||
    blk_unref(blk);
 | 
			
		||||
    bdrv_unref(bs);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -722,74 +676,51 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename,
 | 
			
		||||
                                                QemuOpts *opts,
 | 
			
		||||
                                                Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BlockdevCreateOptions *create_options = NULL;
 | 
			
		||||
    QDict *qdict;
 | 
			
		||||
    Visitor *v;
 | 
			
		||||
    BlockDriverState *bs = NULL;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    uint64_t image_size = 0;
 | 
			
		||||
    uint32_t cluster_size = QED_DEFAULT_CLUSTER_SIZE;
 | 
			
		||||
    uint32_t table_size = QED_DEFAULT_TABLE_SIZE;
 | 
			
		||||
    char *backing_file = NULL;
 | 
			
		||||
    char *backing_fmt = NULL;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    static const QDictRenames opt_renames[] = {
 | 
			
		||||
        { BLOCK_OPT_BACKING_FILE,       "backing-file" },
 | 
			
		||||
        { BLOCK_OPT_BACKING_FMT,        "backing-fmt" },
 | 
			
		||||
        { BLOCK_OPT_CLUSTER_SIZE,       "cluster-size" },
 | 
			
		||||
        { BLOCK_OPT_TABLE_SIZE,         "table-size" },
 | 
			
		||||
        { NULL, NULL },
 | 
			
		||||
    };
 | 
			
		||||
    image_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
 | 
			
		||||
                          BDRV_SECTOR_SIZE);
 | 
			
		||||
    backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
 | 
			
		||||
    backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT);
 | 
			
		||||
    cluster_size = qemu_opt_get_size_del(opts,
 | 
			
		||||
                                         BLOCK_OPT_CLUSTER_SIZE,
 | 
			
		||||
                                         QED_DEFAULT_CLUSTER_SIZE);
 | 
			
		||||
    table_size = qemu_opt_get_size_del(opts, BLOCK_OPT_TABLE_SIZE,
 | 
			
		||||
                                       QED_DEFAULT_TABLE_SIZE);
 | 
			
		||||
 | 
			
		||||
    /* Parse options and convert legacy syntax */
 | 
			
		||||
    qdict = qemu_opts_to_qdict_filtered(opts, NULL, &qed_create_opts, true);
 | 
			
		||||
 | 
			
		||||
    if (!qdict_rename_keys(qdict, opt_renames, errp)) {
 | 
			
		||||
    if (!qed_is_cluster_size_valid(cluster_size)) {
 | 
			
		||||
        error_setg(errp, "QED cluster size must be within range [%u, %u] "
 | 
			
		||||
                         "and power of 2",
 | 
			
		||||
                   QED_MIN_CLUSTER_SIZE, QED_MAX_CLUSTER_SIZE);
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
        goto finish;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Create and open the file (protocol layer) */
 | 
			
		||||
    ret = bdrv_create_file(filename, opts, &local_err);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs = bdrv_open(filename, NULL, NULL,
 | 
			
		||||
                   BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
 | 
			
		||||
    if (bs == NULL) {
 | 
			
		||||
        ret = -EIO;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Now get the QAPI type BlockdevCreateOptions */
 | 
			
		||||
    qdict_put_str(qdict, "driver", "qed");
 | 
			
		||||
    qdict_put_str(qdict, "file", bs->node_name);
 | 
			
		||||
 | 
			
		||||
    v = qobject_input_visitor_new_flat_confused(qdict, errp);
 | 
			
		||||
    if (!v) {
 | 
			
		||||
    if (!qed_is_table_size_valid(table_size)) {
 | 
			
		||||
        error_setg(errp, "QED table size must be within range [%u, %u] "
 | 
			
		||||
                         "and power of 2",
 | 
			
		||||
                   QED_MIN_TABLE_SIZE, QED_MAX_TABLE_SIZE);
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
        goto finish;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
 | 
			
		||||
    visit_free(v);
 | 
			
		||||
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
    if (!qed_is_image_size_valid(image_size, cluster_size, table_size)) {
 | 
			
		||||
        error_setg(errp, "QED image size must be a non-zero multiple of "
 | 
			
		||||
                         "cluster size and less than %" PRIu64 " bytes",
 | 
			
		||||
                   qed_max_image_size(cluster_size, table_size));
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
        goto finish;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Silently round up size */
 | 
			
		||||
    assert(create_options->driver == BLOCKDEV_DRIVER_QED);
 | 
			
		||||
    create_options->u.qed.size =
 | 
			
		||||
        ROUND_UP(create_options->u.qed.size, BDRV_SECTOR_SIZE);
 | 
			
		||||
    ret = qed_create(filename, cluster_size, image_size, table_size,
 | 
			
		||||
                     backing_file, backing_fmt, opts, errp);
 | 
			
		||||
 | 
			
		||||
    /* Create the qed image (format layer) */
 | 
			
		||||
    ret = bdrv_qed_co_create(create_options, errp);
 | 
			
		||||
 | 
			
		||||
fail:
 | 
			
		||||
    qobject_unref(qdict);
 | 
			
		||||
    bdrv_unref(bs);
 | 
			
		||||
    qapi_free_BlockdevCreateOptions(create_options);
 | 
			
		||||
finish:
 | 
			
		||||
    g_free(backing_file);
 | 
			
		||||
    g_free(backing_fmt);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1435,9 +1366,8 @@ static int coroutine_fn bdrv_qed_co_readv(BlockDriverState *bs,
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn bdrv_qed_co_writev(BlockDriverState *bs,
 | 
			
		||||
                                           int64_t sector_num, int nb_sectors,
 | 
			
		||||
                                           QEMUIOVector *qiov, int flags)
 | 
			
		||||
                                           QEMUIOVector *qiov)
 | 
			
		||||
{
 | 
			
		||||
    assert(!flags);
 | 
			
		||||
    return qed_co_request(bs, sector_num, qiov, nb_sectors, QED_AIOCB_WRITE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1468,10 +1398,8 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs,
 | 
			
		||||
                          QED_AIOCB_WRITE | QED_AIOCB_ZERO);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn bdrv_qed_co_truncate(BlockDriverState *bs,
 | 
			
		||||
                                             int64_t offset,
 | 
			
		||||
                                             PreallocMode prealloc,
 | 
			
		||||
                                             Error **errp)
 | 
			
		||||
static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
                             PreallocMode prealloc, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQEDState *s = bs->opaque;
 | 
			
		||||
    uint64_t old_image_size;
 | 
			
		||||
@@ -1607,8 +1535,8 @@ static void coroutine_fn bdrv_qed_co_invalidate_cache(BlockDriverState *bs,
 | 
			
		||||
    ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, &local_err);
 | 
			
		||||
    qemu_co_mutex_unlock(&s->table_lock);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate_prepend(errp, local_err,
 | 
			
		||||
                                "Could not reopen qed layer: ");
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        error_prepend(errp, "Could not reopen qed layer: ");
 | 
			
		||||
        return;
 | 
			
		||||
    } else if (ret < 0) {
 | 
			
		||||
        error_setg_errno(errp, -ret, "Could not reopen qed layer");
 | 
			
		||||
@@ -1674,14 +1602,13 @@ static BlockDriver bdrv_qed = {
 | 
			
		||||
    .bdrv_close               = bdrv_qed_close,
 | 
			
		||||
    .bdrv_reopen_prepare      = bdrv_qed_reopen_prepare,
 | 
			
		||||
    .bdrv_child_perm          = bdrv_format_default_perms,
 | 
			
		||||
    .bdrv_co_create           = bdrv_qed_co_create,
 | 
			
		||||
    .bdrv_co_create_opts      = bdrv_qed_co_create_opts,
 | 
			
		||||
    .bdrv_has_zero_init       = bdrv_has_zero_init_1,
 | 
			
		||||
    .bdrv_co_block_status     = bdrv_qed_co_block_status,
 | 
			
		||||
    .bdrv_co_readv            = bdrv_qed_co_readv,
 | 
			
		||||
    .bdrv_co_writev           = bdrv_qed_co_writev,
 | 
			
		||||
    .bdrv_co_pwrite_zeroes    = bdrv_qed_co_pwrite_zeroes,
 | 
			
		||||
    .bdrv_co_truncate         = bdrv_qed_co_truncate,
 | 
			
		||||
    .bdrv_truncate            = bdrv_qed_truncate,
 | 
			
		||||
    .bdrv_getlength           = bdrv_qed_getlength,
 | 
			
		||||
    .bdrv_get_info            = bdrv_qed_get_info,
 | 
			
		||||
    .bdrv_refresh_limits      = bdrv_qed_refresh_limits,
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,6 @@
 | 
			
		||||
#include "qemu/cutils.h"
 | 
			
		||||
#include "qemu/option.h"
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
#include "block/qdict.h"
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
#include "qapi/qapi-events-block.h"
 | 
			
		||||
#include "qapi/qmp/qdict.h"
 | 
			
		||||
@@ -116,7 +115,6 @@ struct QuorumAIOCB {
 | 
			
		||||
    /* Request metadata */
 | 
			
		||||
    uint64_t offset;
 | 
			
		||||
    uint64_t bytes;
 | 
			
		||||
    int flags;
 | 
			
		||||
 | 
			
		||||
    QEMUIOVector *qiov;         /* calling IOV */
 | 
			
		||||
 | 
			
		||||
@@ -159,8 +157,7 @@ static bool quorum_64bits_compare(QuorumVoteValue *a, QuorumVoteValue *b)
 | 
			
		||||
static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs,
 | 
			
		||||
                                   QEMUIOVector *qiov,
 | 
			
		||||
                                   uint64_t offset,
 | 
			
		||||
                                   uint64_t bytes,
 | 
			
		||||
                                   int flags)
 | 
			
		||||
                                   uint64_t bytes)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQuorumState *s = bs->opaque;
 | 
			
		||||
    QuorumAIOCB *acb = g_new(QuorumAIOCB, 1);
 | 
			
		||||
@@ -171,7 +168,6 @@ static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs,
 | 
			
		||||
        .bs                 = bs,
 | 
			
		||||
        .offset             = offset,
 | 
			
		||||
        .bytes              = bytes,
 | 
			
		||||
        .flags              = flags,
 | 
			
		||||
        .qiov               = qiov,
 | 
			
		||||
        .votes.compare      = quorum_sha256_compare,
 | 
			
		||||
        .votes.vote_list    = QLIST_HEAD_INITIALIZER(acb.votes.vote_list),
 | 
			
		||||
@@ -199,7 +195,7 @@ static void quorum_report_bad(QuorumOpType type, uint64_t offset,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qapi_event_send_quorum_report_bad(type, !!msg, msg, node_name, start_sector,
 | 
			
		||||
                                      end_sector - start_sector);
 | 
			
		||||
                                      end_sector - start_sector, &error_abort);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void quorum_report_failure(QuorumAIOCB *acb)
 | 
			
		||||
@@ -210,7 +206,7 @@ static void quorum_report_failure(QuorumAIOCB *acb)
 | 
			
		||||
                                      BDRV_SECTOR_SIZE);
 | 
			
		||||
 | 
			
		||||
    qapi_event_send_quorum_failure(reference, start_sector,
 | 
			
		||||
                                   end_sector - start_sector);
 | 
			
		||||
                                   end_sector - start_sector, &error_abort);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int quorum_vote_error(QuorumAIOCB *acb);
 | 
			
		||||
@@ -275,11 +271,9 @@ static void quorum_rewrite_entry(void *opaque)
 | 
			
		||||
    BDRVQuorumState *s = acb->bs->opaque;
 | 
			
		||||
 | 
			
		||||
    /* Ignore any errors, it's just a correction attempt for already
 | 
			
		||||
     * corrupted data.
 | 
			
		||||
     * Mask out BDRV_REQ_WRITE_UNCHANGED because this overwrites the
 | 
			
		||||
     * area with different data from the other children. */
 | 
			
		||||
     * corrupted data. */
 | 
			
		||||
    bdrv_co_pwritev(s->children[co->idx], acb->offset, acb->bytes,
 | 
			
		||||
                    acb->qiov, acb->flags & ~BDRV_REQ_WRITE_UNCHANGED);
 | 
			
		||||
                    acb->qiov, 0);
 | 
			
		||||
 | 
			
		||||
    /* Wake up the caller after the last rewrite */
 | 
			
		||||
    acb->rewrite_count--;
 | 
			
		||||
@@ -437,7 +431,23 @@ static bool quorum_iovec_compare(QEMUIOVector *a, QEMUIOVector *b)
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool quorum_compare(QuorumAIOCB *acb, QEMUIOVector *a, QEMUIOVector *b)
 | 
			
		||||
static void GCC_FMT_ATTR(2, 3) quorum_err(QuorumAIOCB *acb,
 | 
			
		||||
                                          const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
    va_list ap;
 | 
			
		||||
 | 
			
		||||
    va_start(ap, fmt);
 | 
			
		||||
    fprintf(stderr, "quorum: offset=%" PRIu64 " bytes=%" PRIu64 " ",
 | 
			
		||||
            acb->offset, acb->bytes);
 | 
			
		||||
    vfprintf(stderr, fmt, ap);
 | 
			
		||||
    fprintf(stderr, "\n");
 | 
			
		||||
    va_end(ap);
 | 
			
		||||
    exit(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool quorum_compare(QuorumAIOCB *acb,
 | 
			
		||||
                           QEMUIOVector *a,
 | 
			
		||||
                           QEMUIOVector *b)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQuorumState *s = acb->bs->opaque;
 | 
			
		||||
    ssize_t offset;
 | 
			
		||||
@@ -446,10 +456,8 @@ static bool quorum_compare(QuorumAIOCB *acb, QEMUIOVector *a, QEMUIOVector *b)
 | 
			
		||||
    if (s->is_blkverify) {
 | 
			
		||||
        offset = qemu_iovec_compare(a, b);
 | 
			
		||||
        if (offset != -1) {
 | 
			
		||||
            fprintf(stderr, "quorum: offset=%" PRIu64 " bytes=%" PRIu64
 | 
			
		||||
                    " contents mismatch at offset %" PRIu64 "\n",
 | 
			
		||||
                    acb->offset, acb->bytes, acb->offset + offset);
 | 
			
		||||
            exit(1);
 | 
			
		||||
            quorum_err(acb, "contents mismatch at offset %" PRIu64,
 | 
			
		||||
                       acb->offset + offset);
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
@@ -600,7 +608,7 @@ static void read_quorum_children_entry(void *opaque)
 | 
			
		||||
static int read_quorum_children(QuorumAIOCB *acb)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQuorumState *s = acb->bs->opaque;
 | 
			
		||||
    int i;
 | 
			
		||||
    int i, ret;
 | 
			
		||||
 | 
			
		||||
    acb->children_read = s->num_children;
 | 
			
		||||
    for (i = 0; i < s->num_children; i++) {
 | 
			
		||||
@@ -635,7 +643,9 @@ static int read_quorum_children(QuorumAIOCB *acb)
 | 
			
		||||
        qemu_coroutine_yield();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return acb->vote_ret;
 | 
			
		||||
    ret = acb->vote_ret;
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int read_fifo_child(QuorumAIOCB *acb)
 | 
			
		||||
@@ -663,7 +673,7 @@ static int quorum_co_preadv(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
                            uint64_t bytes, QEMUIOVector *qiov, int flags)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQuorumState *s = bs->opaque;
 | 
			
		||||
    QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes, flags);
 | 
			
		||||
    QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes);
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    acb->is_read = true;
 | 
			
		||||
@@ -689,7 +699,7 @@ static void write_quorum_entry(void *opaque)
 | 
			
		||||
 | 
			
		||||
    sacb->bs = s->children[i]->bs;
 | 
			
		||||
    sacb->ret = bdrv_co_pwritev(s->children[i], acb->offset, acb->bytes,
 | 
			
		||||
                                acb->qiov, acb->flags);
 | 
			
		||||
                                acb->qiov, 0);
 | 
			
		||||
    if (sacb->ret == 0) {
 | 
			
		||||
        acb->success_count++;
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -709,7 +719,7 @@ static int quorum_co_pwritev(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
                             uint64_t bytes, QEMUIOVector *qiov, int flags)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQuorumState *s = bs->opaque;
 | 
			
		||||
    QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes, flags);
 | 
			
		||||
    QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes);
 | 
			
		||||
    int i, ret;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < s->num_children; i++) {
 | 
			
		||||
@@ -912,12 +922,13 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    s->read_pattern = ret;
 | 
			
		||||
 | 
			
		||||
    if (s->read_pattern == QUORUM_READ_PATTERN_QUORUM) {
 | 
			
		||||
        s->is_blkverify = qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false);
 | 
			
		||||
        if (s->is_blkverify && (s->num_children != 2 || s->threshold != 2)) {
 | 
			
		||||
            error_setg(&local_err, "blkverify=on can only be set if there are "
 | 
			
		||||
                       "exactly two files and vote-threshold is 2");
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
            goto exit;
 | 
			
		||||
        /* is the driver in blkverify mode */
 | 
			
		||||
        if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false) &&
 | 
			
		||||
            s->num_children == 2 && s->threshold == 2) {
 | 
			
		||||
            s->is_blkverify = true;
 | 
			
		||||
        } else if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false)) {
 | 
			
		||||
            fprintf(stderr, "blkverify mode is set by setting blkverify=on "
 | 
			
		||||
                    "and using two files with vote_threshold=2\n");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        s->rewrite_corrupted = qemu_opt_get_bool(opts, QUORUM_OPT_REWRITE,
 | 
			
		||||
@@ -950,8 +961,6 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    }
 | 
			
		||||
    s->next_child_index = s->num_children;
 | 
			
		||||
 | 
			
		||||
    bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
 | 
			
		||||
 | 
			
		||||
    g_free(opened);
 | 
			
		||||
    goto exit;
 | 
			
		||||
 | 
			
		||||
@@ -992,11 +1001,6 @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs,
 | 
			
		||||
    char indexstr[32];
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    if (s->is_blkverify) {
 | 
			
		||||
        error_setg(errp, "Cannot add a child to a quorum in blkverify mode");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    assert(s->num_children <= INT_MAX / sizeof(BdrvChild *));
 | 
			
		||||
    if (s->num_children == INT_MAX / sizeof(BdrvChild *) ||
 | 
			
		||||
        s->next_child_index == UINT_MAX) {
 | 
			
		||||
@@ -1051,9 +1055,6 @@ static void quorum_del_child(BlockDriverState *bs, BdrvChild *child,
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* We know now that num_children > threshold, so blkverify must be false */
 | 
			
		||||
    assert(!s->is_blkverify);
 | 
			
		||||
 | 
			
		||||
    bdrv_drained_begin(bs);
 | 
			
		||||
 | 
			
		||||
    /* We can safely remove this child now */
 | 
			
		||||
@@ -1081,8 +1082,8 @@ static void quorum_refresh_filename(BlockDriverState *bs, QDict *options)
 | 
			
		||||
 | 
			
		||||
    children = qlist_new();
 | 
			
		||||
    for (i = 0; i < s->num_children; i++) {
 | 
			
		||||
        qlist_append(children,
 | 
			
		||||
                     qobject_ref(s->children[i]->bs->full_open_options));
 | 
			
		||||
        QINCREF(s->children[i]->bs->full_open_options);
 | 
			
		||||
        qlist_append(children, s->children[i]->bs->full_open_options);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    opts = qdict_new();
 | 
			
		||||
 
 | 
			
		||||
@@ -167,37 +167,16 @@ static void raw_reopen_abort(BDRVReopenState *state)
 | 
			
		||||
    state->opaque = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check and adjust the offset, against 'offset' and 'size' options. */
 | 
			
		||||
static inline int raw_adjust_offset(BlockDriverState *bs, uint64_t *offset,
 | 
			
		||||
                                    uint64_t bytes, bool is_write)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
 | 
			
		||||
    if (s->has_size && (*offset > s->size || bytes > (s->size - *offset))) {
 | 
			
		||||
        /* There's not enough space for the write, or the read request is
 | 
			
		||||
         * out-of-range. Don't read/write anything to prevent leaking out of
 | 
			
		||||
         * the size specified in options. */
 | 
			
		||||
        return is_write ? -ENOSPC : -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (*offset > INT64_MAX - s->offset) {
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
    *offset += s->offset;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn raw_co_preadv(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
                                      uint64_t bytes, QEMUIOVector *qiov,
 | 
			
		||||
                                      int flags)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
 | 
			
		||||
    ret = raw_adjust_offset(bs, &offset, bytes, false);
 | 
			
		||||
    if (ret) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    if (offset > UINT64_MAX - s->offset) {
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
    offset += s->offset;
 | 
			
		||||
 | 
			
		||||
    BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
 | 
			
		||||
    return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
 | 
			
		||||
@@ -207,11 +186,23 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
                                       uint64_t bytes, QEMUIOVector *qiov,
 | 
			
		||||
                                       int flags)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
    void *buf = NULL;
 | 
			
		||||
    BlockDriver *drv;
 | 
			
		||||
    QEMUIOVector local_qiov;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    if (s->has_size && (offset > s->size || bytes > (s->size - offset))) {
 | 
			
		||||
        /* There's not enough space for the data. Don't write anything and just
 | 
			
		||||
         * fail to prevent leaking out of the size specified in options. */
 | 
			
		||||
        return -ENOSPC;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (offset > UINT64_MAX - s->offset) {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (bs->probed && offset < BLOCK_PROBE_BUF_SIZE && bytes) {
 | 
			
		||||
        /* Handling partial writes would be a pain - so we just
 | 
			
		||||
         * require that guests have 512-byte request alignment if
 | 
			
		||||
@@ -246,10 +237,7 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
        qiov = &local_qiov;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = raw_adjust_offset(bs, &offset, bytes, true);
 | 
			
		||||
    if (ret) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    offset += s->offset;
 | 
			
		||||
 | 
			
		||||
    BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
 | 
			
		||||
    ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
 | 
			
		||||
@@ -279,25 +267,23 @@ static int coroutine_fn raw_co_pwrite_zeroes(BlockDriverState *bs,
 | 
			
		||||
                                             int64_t offset, int bytes,
 | 
			
		||||
                                             BdrvRequestFlags flags)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true);
 | 
			
		||||
    if (ret) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
    if (offset > UINT64_MAX - s->offset) {
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
    offset += s->offset;
 | 
			
		||||
    return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs,
 | 
			
		||||
                                        int64_t offset, int bytes)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true);
 | 
			
		||||
    if (ret) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
    if (offset > UINT64_MAX - s->offset) {
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
    return bdrv_co_pdiscard(bs->file, offset, bytes);
 | 
			
		||||
    offset += s->offset;
 | 
			
		||||
    return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int64_t raw_getlength(BlockDriverState *bs)
 | 
			
		||||
@@ -366,7 +352,7 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
static int raw_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
                        PreallocMode prealloc, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRawState *s = bs->opaque;
 | 
			
		||||
@@ -383,7 +369,7 @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
 | 
			
		||||
    s->size = offset;
 | 
			
		||||
    offset += s->offset;
 | 
			
		||||
    return bdrv_co_truncate(bs->file, offset, prealloc, errp);
 | 
			
		||||
    return bdrv_truncate(bs->file, offset, prealloc, errp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void raw_eject(BlockDriverState *bs, bool eject_flag)
 | 
			
		||||
@@ -429,11 +415,10 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs->sg = bs->file->bs->sg;
 | 
			
		||||
    bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
 | 
			
		||||
        (BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
 | 
			
		||||
    bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
 | 
			
		||||
        ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
 | 
			
		||||
            bs->file->bs->supported_zero_flags);
 | 
			
		||||
    bs->supported_write_flags = BDRV_REQ_FUA &
 | 
			
		||||
        bs->file->bs->supported_write_flags;
 | 
			
		||||
    bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
 | 
			
		||||
        bs->file->bs->supported_zero_flags;
 | 
			
		||||
 | 
			
		||||
    if (bs->probed && !bdrv_is_read_only(bs)) {
 | 
			
		||||
        fprintf(stderr,
 | 
			
		||||
@@ -459,6 +444,10 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void raw_close(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int raw_probe(const uint8_t *buf, int buf_size, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    /* smallest possible positive score so that raw is used if and only if no
 | 
			
		||||
@@ -493,44 +482,6 @@ static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo)
 | 
			
		||||
    return bdrv_probe_geometry(bs->file->bs, geo);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs,
 | 
			
		||||
                                               BdrvChild *src,
 | 
			
		||||
                                               uint64_t src_offset,
 | 
			
		||||
                                               BdrvChild *dst,
 | 
			
		||||
                                               uint64_t dst_offset,
 | 
			
		||||
                                               uint64_t bytes,
 | 
			
		||||
                                               BdrvRequestFlags read_flags,
 | 
			
		||||
                                               BdrvRequestFlags write_flags)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    ret = raw_adjust_offset(bs, &src_offset, bytes, false);
 | 
			
		||||
    if (ret) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
    return bdrv_co_copy_range_from(bs->file, src_offset, dst, dst_offset,
 | 
			
		||||
                                   bytes, read_flags, write_flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs,
 | 
			
		||||
                                             BdrvChild *src,
 | 
			
		||||
                                             uint64_t src_offset,
 | 
			
		||||
                                             BdrvChild *dst,
 | 
			
		||||
                                             uint64_t dst_offset,
 | 
			
		||||
                                             uint64_t bytes,
 | 
			
		||||
                                             BdrvRequestFlags read_flags,
 | 
			
		||||
                                             BdrvRequestFlags write_flags)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    ret = raw_adjust_offset(bs, &dst_offset, bytes, true);
 | 
			
		||||
    if (ret) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
    return bdrv_co_copy_range_to(src, src_offset, bs->file, dst_offset, bytes,
 | 
			
		||||
                                 read_flags, write_flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BlockDriver bdrv_raw = {
 | 
			
		||||
    .format_name          = "raw",
 | 
			
		||||
    .instance_size        = sizeof(BDRVRawState),
 | 
			
		||||
@@ -539,6 +490,7 @@ BlockDriver bdrv_raw = {
 | 
			
		||||
    .bdrv_reopen_commit   = &raw_reopen_commit,
 | 
			
		||||
    .bdrv_reopen_abort    = &raw_reopen_abort,
 | 
			
		||||
    .bdrv_open            = &raw_open,
 | 
			
		||||
    .bdrv_close           = &raw_close,
 | 
			
		||||
    .bdrv_child_perm      = bdrv_filter_default_perms,
 | 
			
		||||
    .bdrv_co_create_opts  = &raw_co_create_opts,
 | 
			
		||||
    .bdrv_co_preadv       = &raw_co_preadv,
 | 
			
		||||
@@ -546,9 +498,7 @@ BlockDriver bdrv_raw = {
 | 
			
		||||
    .bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes,
 | 
			
		||||
    .bdrv_co_pdiscard     = &raw_co_pdiscard,
 | 
			
		||||
    .bdrv_co_block_status = &raw_co_block_status,
 | 
			
		||||
    .bdrv_co_copy_range_from = &raw_co_copy_range_from,
 | 
			
		||||
    .bdrv_co_copy_range_to  = &raw_co_copy_range_to,
 | 
			
		||||
    .bdrv_co_truncate     = &raw_co_truncate,
 | 
			
		||||
    .bdrv_truncate        = &raw_truncate,
 | 
			
		||||
    .bdrv_getlength       = &raw_getlength,
 | 
			
		||||
    .has_variable_length  = true,
 | 
			
		||||
    .bdrv_measure         = &raw_measure,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										244
									
								
								block/rbd.c
									
									
									
									
									
								
							
							
						
						
									
										244
									
								
								block/rbd.c
									
									
									
									
									
								
							@@ -18,7 +18,6 @@
 | 
			
		||||
#include "qemu/error-report.h"
 | 
			
		||||
#include "qemu/option.h"
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
#include "block/qdict.h"
 | 
			
		||||
#include "crypto/secret.h"
 | 
			
		||||
#include "qemu/cutils.h"
 | 
			
		||||
#include "qapi/qmp/qstring.h"
 | 
			
		||||
@@ -227,57 +226,27 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options,
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
    g_free(buf);
 | 
			
		||||
    qobject_unref(keypairs);
 | 
			
		||||
    QDECREF(keypairs);
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void qemu_rbd_refresh_limits(BlockDriverState *bs, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    /* XXX Does RBD support AIO on less than 512-byte alignment? */
 | 
			
		||||
    bs->bl.request_alignment = 512;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int qemu_rbd_set_auth(rados_t cluster, BlockdevOptionsRbd *opts,
 | 
			
		||||
static int qemu_rbd_set_auth(rados_t cluster, const char *secretid,
 | 
			
		||||
                             Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    char *key, *acr;
 | 
			
		||||
    int r;
 | 
			
		||||
    GString *accu;
 | 
			
		||||
    RbdAuthModeList *auth;
 | 
			
		||||
 | 
			
		||||
    if (opts->key_secret) {
 | 
			
		||||
        key = qcrypto_secret_lookup_as_base64(opts->key_secret, errp);
 | 
			
		||||
        if (!key) {
 | 
			
		||||
            return -EIO;
 | 
			
		||||
        }
 | 
			
		||||
        r = rados_conf_set(cluster, "key", key);
 | 
			
		||||
        g_free(key);
 | 
			
		||||
        if (r < 0) {
 | 
			
		||||
            error_setg_errno(errp, -r, "Could not set 'key'");
 | 
			
		||||
            return r;
 | 
			
		||||
        }
 | 
			
		||||
    if (secretid == 0) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (opts->has_auth_client_required) {
 | 
			
		||||
        accu = g_string_new("");
 | 
			
		||||
        for (auth = opts->auth_client_required; auth; auth = auth->next) {
 | 
			
		||||
            if (accu->str[0]) {
 | 
			
		||||
                g_string_append_c(accu, ';');
 | 
			
		||||
            }
 | 
			
		||||
            g_string_append(accu, RbdAuthMode_str(auth->value));
 | 
			
		||||
        }
 | 
			
		||||
        acr = g_string_free(accu, FALSE);
 | 
			
		||||
        r = rados_conf_set(cluster, "auth_client_required", acr);
 | 
			
		||||
        g_free(acr);
 | 
			
		||||
        if (r < 0) {
 | 
			
		||||
            error_setg_errno(errp, -r,
 | 
			
		||||
                             "Could not set 'auth_client_required'");
 | 
			
		||||
            return r;
 | 
			
		||||
        }
 | 
			
		||||
    gchar *secret = qcrypto_secret_lookup_as_base64(secretid,
 | 
			
		||||
                                                    errp);
 | 
			
		||||
    if (!secret) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rados_conf_set(cluster, "key", secret);
 | 
			
		||||
    g_free(secret);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -294,29 +263,29 @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json,
 | 
			
		||||
    if (!keypairs_json) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
    keypairs = qobject_to(QList,
 | 
			
		||||
                          qobject_from_json(keypairs_json, &error_abort));
 | 
			
		||||
    keypairs = qobject_to_qlist(qobject_from_json(keypairs_json,
 | 
			
		||||
                                                  &error_abort));
 | 
			
		||||
    remaining = qlist_size(keypairs) / 2;
 | 
			
		||||
    assert(remaining);
 | 
			
		||||
 | 
			
		||||
    while (remaining--) {
 | 
			
		||||
        name = qobject_to(QString, qlist_pop(keypairs));
 | 
			
		||||
        value = qobject_to(QString, qlist_pop(keypairs));
 | 
			
		||||
        name = qobject_to_qstring(qlist_pop(keypairs));
 | 
			
		||||
        value = qobject_to_qstring(qlist_pop(keypairs));
 | 
			
		||||
        assert(name && value);
 | 
			
		||||
        key = qstring_get_str(name);
 | 
			
		||||
 | 
			
		||||
        ret = rados_conf_set(cluster, key, qstring_get_str(value));
 | 
			
		||||
        qobject_unref(value);
 | 
			
		||||
        QDECREF(value);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            error_setg_errno(errp, -ret, "invalid conf option %s", key);
 | 
			
		||||
            qobject_unref(name);
 | 
			
		||||
            QDECREF(name);
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        qobject_unref(name);
 | 
			
		||||
        QDECREF(name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qobject_unref(keypairs);
 | 
			
		||||
    QDECREF(keypairs);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -368,7 +337,9 @@ static QemuOptsList runtime_opts = {
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* FIXME Deprecate and remove keypairs or make it available in QMP. */
 | 
			
		||||
/* FIXME Deprecate and remove keypairs or make it available in QMP.
 | 
			
		||||
 * password_secret should eventually be configurable in opts->location. Support
 | 
			
		||||
 * for it in .bdrv_open will make it work here as well. */
 | 
			
		||||
static int qemu_rbd_do_create(BlockdevCreateOptions *options,
 | 
			
		||||
                              const char *keypairs, const char *password_secret,
 | 
			
		||||
                              Error **errp)
 | 
			
		||||
@@ -478,7 +449,7 @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
    qobject_unref(options);
 | 
			
		||||
    QDECREF(options);
 | 
			
		||||
    qapi_free_BlockdevCreateOptions(create_options);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
@@ -574,16 +545,6 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    int r;
 | 
			
		||||
 | 
			
		||||
    if (secretid) {
 | 
			
		||||
        if (opts->key_secret) {
 | 
			
		||||
            error_setg(errp,
 | 
			
		||||
                       "Legacy 'password-secret' clashes with 'key-secret'");
 | 
			
		||||
            return -EINVAL;
 | 
			
		||||
        }
 | 
			
		||||
        opts->key_secret = g_strdup(secretid);
 | 
			
		||||
        opts->has_key_secret = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mon_host = qemu_rbd_mon_host(opts, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
@@ -616,8 +577,8 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    r = qemu_rbd_set_auth(*cluster, opts, errp);
 | 
			
		||||
    if (r < 0) {
 | 
			
		||||
    if (qemu_rbd_set_auth(*cluster, secretid, errp) < 0) {
 | 
			
		||||
        r = -EIO;
 | 
			
		||||
        goto failed_shutdown;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -655,66 +616,33 @@ failed_opts:
 | 
			
		||||
    return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qemu_rbd_convert_options(QDict *options, BlockdevOptionsRbd **opts,
 | 
			
		||||
                                    Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    Visitor *v;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
    /* Convert the remaining options into a QAPI object */
 | 
			
		||||
    v = qobject_input_visitor_new_flat_confused(options, errp);
 | 
			
		||||
    if (!v) {
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    visit_type_BlockdevOptionsRbd(v, NULL, opts, &local_err);
 | 
			
		||||
    visit_free(v);
 | 
			
		||||
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qemu_rbd_attempt_legacy_options(QDict *options,
 | 
			
		||||
                                           BlockdevOptionsRbd **opts,
 | 
			
		||||
                                           char **keypairs)
 | 
			
		||||
{
 | 
			
		||||
    char *filename;
 | 
			
		||||
    int r;
 | 
			
		||||
 | 
			
		||||
    filename = g_strdup(qdict_get_try_str(options, "filename"));
 | 
			
		||||
    if (!filename) {
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
    qdict_del(options, "filename");
 | 
			
		||||
 | 
			
		||||
    qemu_rbd_parse_filename(filename, options, NULL);
 | 
			
		||||
 | 
			
		||||
    /* keypairs freed by caller */
 | 
			
		||||
    *keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs"));
 | 
			
		||||
    if (*keypairs) {
 | 
			
		||||
        qdict_del(options, "=keyvalue-pairs");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    r = qemu_rbd_convert_options(options, opts, NULL);
 | 
			
		||||
 | 
			
		||||
    g_free(filename);
 | 
			
		||||
    return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
                         Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRBDState *s = bs->opaque;
 | 
			
		||||
    BlockdevOptionsRbd *opts = NULL;
 | 
			
		||||
    const QDictEntry *e;
 | 
			
		||||
    Visitor *v;
 | 
			
		||||
    QObject *crumpled = NULL;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    const char *filename;
 | 
			
		||||
    char *keypairs, *secretid;
 | 
			
		||||
    int r;
 | 
			
		||||
 | 
			
		||||
    /* If we are given a filename, parse the filename, with precedence given to
 | 
			
		||||
     * filename encoded options */
 | 
			
		||||
    filename = qdict_get_try_str(options, "filename");
 | 
			
		||||
    if (filename) {
 | 
			
		||||
        warn_report("'filename' option specified. "
 | 
			
		||||
                    "This is an unsupported option, and may be deprecated "
 | 
			
		||||
                    "in the future");
 | 
			
		||||
        qemu_rbd_parse_filename(filename, options, &local_err);
 | 
			
		||||
        qdict_del(options, "filename");
 | 
			
		||||
        if (local_err) {
 | 
			
		||||
            error_propagate(errp, local_err);
 | 
			
		||||
            return -EINVAL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs"));
 | 
			
		||||
    if (keypairs) {
 | 
			
		||||
        qdict_del(options, "=keyvalue-pairs");
 | 
			
		||||
@@ -725,41 +653,24 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
        qdict_del(options, "password-secret");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    r = qemu_rbd_convert_options(options, &opts, &local_err);
 | 
			
		||||
    /* Convert the remaining options into a QAPI object */
 | 
			
		||||
    crumpled = qdict_crumple(options, errp);
 | 
			
		||||
    if (crumpled == NULL) {
 | 
			
		||||
        r = -EINVAL;
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    v = qobject_input_visitor_new_keyval(crumpled);
 | 
			
		||||
    visit_type_BlockdevOptionsRbd(v, NULL, &opts, &local_err);
 | 
			
		||||
    visit_free(v);
 | 
			
		||||
    qobject_decref(crumpled);
 | 
			
		||||
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        /* If keypairs are present, that means some options are present in
 | 
			
		||||
         * the modern option format.  Don't attempt to parse legacy option
 | 
			
		||||
         * formats, as we won't support mixed usage. */
 | 
			
		||||
        if (keypairs) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        r = -EINVAL;
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        /* If the initial attempt to convert and process the options failed,
 | 
			
		||||
         * we may be attempting to open an image file that has the rbd options
 | 
			
		||||
         * specified in the older format consisting of all key/value pairs
 | 
			
		||||
         * encoded in the filename.  Go ahead and attempt to parse the
 | 
			
		||||
         * filename, and see if we can pull out the required options. */
 | 
			
		||||
        r = qemu_rbd_attempt_legacy_options(options, &opts, &keypairs);
 | 
			
		||||
        if (r < 0) {
 | 
			
		||||
            /* Propagate the original error, not the legacy parsing fallback
 | 
			
		||||
             * error, as the latter was just a best-effort attempt. */
 | 
			
		||||
            error_propagate(errp, local_err);
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
        /* Take care whenever deciding to actually deprecate; once this ability
 | 
			
		||||
         * is removed, we will not be able to open any images with legacy-styled
 | 
			
		||||
         * backing image strings. */
 | 
			
		||||
        warn_report("RBD options encoded in the filename as keyvalue pairs "
 | 
			
		||||
                    "is deprecated");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Remove the processed options from the QDict (the visitor processes
 | 
			
		||||
     * _all_ options in the QDict) */
 | 
			
		||||
    while ((e = qdict_first(options))) {
 | 
			
		||||
        qdict_del(options, e->key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    r = qemu_rbd_connect(&s->cluster, &s->io_ctx, opts,
 | 
			
		||||
                         !(flags & BDRV_O_NOCACHE), keypairs, secretid, errp);
 | 
			
		||||
    if (r < 0) {
 | 
			
		||||
@@ -780,12 +691,18 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    /* If we are using an rbd snapshot, we must be r/o, otherwise
 | 
			
		||||
     * leave as-is */
 | 
			
		||||
    if (s->snap != NULL) {
 | 
			
		||||
        r = bdrv_apply_auto_read_only(bs, "rbd snapshots are read-only", errp);
 | 
			
		||||
        if (!bdrv_is_read_only(bs)) {
 | 
			
		||||
            error_report("Opening rbd snapshots without an explicit "
 | 
			
		||||
                         "read-only=on option is deprecated. Future versions "
 | 
			
		||||
                         "will refuse to open the image instead of "
 | 
			
		||||
                         "automatically marking the image read-only.");
 | 
			
		||||
            r = bdrv_set_read_only(bs, true, &local_err);
 | 
			
		||||
            if (r < 0) {
 | 
			
		||||
            rbd_close(s->image);
 | 
			
		||||
                error_propagate(errp, local_err);
 | 
			
		||||
                goto failed_open;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    r = 0;
 | 
			
		||||
    goto out;
 | 
			
		||||
@@ -975,23 +892,27 @@ failed:
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockAIOCB *qemu_rbd_aio_preadv(BlockDriverState *bs,
 | 
			
		||||
                                       uint64_t offset, uint64_t bytes,
 | 
			
		||||
                                       QEMUIOVector *qiov, int flags,
 | 
			
		||||
static BlockAIOCB *qemu_rbd_aio_readv(BlockDriverState *bs,
 | 
			
		||||
                                      int64_t sector_num,
 | 
			
		||||
                                      QEMUIOVector *qiov,
 | 
			
		||||
                                      int nb_sectors,
 | 
			
		||||
                                      BlockCompletionFunc *cb,
 | 
			
		||||
                                      void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    return rbd_start_aio(bs, offset, qiov, bytes, cb, opaque,
 | 
			
		||||
    return rbd_start_aio(bs, sector_num << BDRV_SECTOR_BITS, qiov,
 | 
			
		||||
                         (int64_t) nb_sectors << BDRV_SECTOR_BITS, cb, opaque,
 | 
			
		||||
                         RBD_AIO_READ);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockAIOCB *qemu_rbd_aio_pwritev(BlockDriverState *bs,
 | 
			
		||||
                                        uint64_t offset, uint64_t bytes,
 | 
			
		||||
                                        QEMUIOVector *qiov, int flags,
 | 
			
		||||
static BlockAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs,
 | 
			
		||||
                                       int64_t sector_num,
 | 
			
		||||
                                       QEMUIOVector *qiov,
 | 
			
		||||
                                       int nb_sectors,
 | 
			
		||||
                                       BlockCompletionFunc *cb,
 | 
			
		||||
                                       void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    return rbd_start_aio(bs, offset, qiov, bytes, cb, opaque,
 | 
			
		||||
    return rbd_start_aio(bs, sector_num << BDRV_SECTOR_BITS, qiov,
 | 
			
		||||
                         (int64_t) nb_sectors << BDRV_SECTOR_BITS, cb, opaque,
 | 
			
		||||
                         RBD_AIO_WRITE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1046,10 +967,8 @@ static int64_t qemu_rbd_getlength(BlockDriverState *bs)
 | 
			
		||||
    return info.size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn qemu_rbd_co_truncate(BlockDriverState *bs,
 | 
			
		||||
                                             int64_t offset,
 | 
			
		||||
                                             PreallocMode prealloc,
 | 
			
		||||
                                             Error **errp)
 | 
			
		||||
static int qemu_rbd_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
                             PreallocMode prealloc, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BDRVRBDState *s = bs->opaque;
 | 
			
		||||
    int r;
 | 
			
		||||
@@ -1232,7 +1151,6 @@ static BlockDriver bdrv_rbd = {
 | 
			
		||||
    .format_name            = "rbd",
 | 
			
		||||
    .instance_size          = sizeof(BDRVRBDState),
 | 
			
		||||
    .bdrv_parse_filename    = qemu_rbd_parse_filename,
 | 
			
		||||
    .bdrv_refresh_limits    = qemu_rbd_refresh_limits,
 | 
			
		||||
    .bdrv_file_open         = qemu_rbd_open,
 | 
			
		||||
    .bdrv_close             = qemu_rbd_close,
 | 
			
		||||
    .bdrv_reopen_prepare    = qemu_rbd_reopen_prepare,
 | 
			
		||||
@@ -1242,11 +1160,11 @@ static BlockDriver bdrv_rbd = {
 | 
			
		||||
    .bdrv_get_info          = qemu_rbd_getinfo,
 | 
			
		||||
    .create_opts            = &qemu_rbd_create_opts,
 | 
			
		||||
    .bdrv_getlength         = qemu_rbd_getlength,
 | 
			
		||||
    .bdrv_co_truncate       = qemu_rbd_co_truncate,
 | 
			
		||||
    .bdrv_truncate          = qemu_rbd_truncate,
 | 
			
		||||
    .protocol_name          = "rbd",
 | 
			
		||||
 | 
			
		||||
    .bdrv_aio_preadv        = qemu_rbd_aio_preadv,
 | 
			
		||||
    .bdrv_aio_pwritev       = qemu_rbd_aio_pwritev,
 | 
			
		||||
    .bdrv_aio_readv         = qemu_rbd_aio_readv,
 | 
			
		||||
    .bdrv_aio_writev        = qemu_rbd_aio_writev,
 | 
			
		||||
 | 
			
		||||
#ifdef LIBRBD_SUPPORTS_AIO_FLUSH
 | 
			
		||||
    .bdrv_aio_flush         = qemu_rbd_aio_flush,
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,6 @@
 | 
			
		||||
#include "block/block_backup.h"
 | 
			
		||||
#include "sysemu/block-backend.h"
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
#include "qapi/qmp/qdict.h"
 | 
			
		||||
#include "replication.h"
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
@@ -40,8 +39,8 @@ typedef struct BDRVReplicationState {
 | 
			
		||||
    char *top_id;
 | 
			
		||||
    ReplicationState *rs;
 | 
			
		||||
    Error *blocker;
 | 
			
		||||
    bool orig_hidden_read_only;
 | 
			
		||||
    bool orig_secondary_read_only;
 | 
			
		||||
    int orig_hidden_flags;
 | 
			
		||||
    int orig_secondary_flags;
 | 
			
		||||
    int error;
 | 
			
		||||
} BDRVReplicationState;
 | 
			
		||||
 | 
			
		||||
@@ -146,7 +145,7 @@ static void replication_close(BlockDriverState *bs)
 | 
			
		||||
        replication_stop(s->rs, false, NULL);
 | 
			
		||||
    }
 | 
			
		||||
    if (s->stage == BLOCK_REPLICATION_FAILOVER) {
 | 
			
		||||
        job_cancel_sync(&s->active_disk->bs->job->job);
 | 
			
		||||
        block_job_cancel_sync(s->active_disk->bs->job);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (s->mode == REPLICATION_MODE_SECONDARY) {
 | 
			
		||||
@@ -219,6 +218,9 @@ static coroutine_fn int replication_co_readv(BlockDriverState *bs,
 | 
			
		||||
                                             QEMUIOVector *qiov)
 | 
			
		||||
{
 | 
			
		||||
    BDRVReplicationState *s = bs->opaque;
 | 
			
		||||
    BdrvChild *child = s->secondary_disk;
 | 
			
		||||
    BlockJob *job = NULL;
 | 
			
		||||
    CowRequest req;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    if (s->mode == REPLICATION_MODE_PRIMARY) {
 | 
			
		||||
@@ -231,17 +233,34 @@ static coroutine_fn int replication_co_readv(BlockDriverState *bs,
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = bdrv_co_preadv(bs->file, sector_num * BDRV_SECTOR_SIZE,
 | 
			
		||||
                         remaining_sectors * BDRV_SECTOR_SIZE, qiov, 0);
 | 
			
		||||
    if (child && child->bs) {
 | 
			
		||||
        job = child->bs->job;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (job) {
 | 
			
		||||
        uint64_t remaining_bytes = remaining_sectors * BDRV_SECTOR_SIZE;
 | 
			
		||||
 | 
			
		||||
        backup_wait_for_overlapping_requests(child->bs->job,
 | 
			
		||||
                                             sector_num * BDRV_SECTOR_SIZE,
 | 
			
		||||
                                             remaining_bytes);
 | 
			
		||||
        backup_cow_request_begin(&req, child->bs->job,
 | 
			
		||||
                                 sector_num * BDRV_SECTOR_SIZE,
 | 
			
		||||
                                 remaining_bytes);
 | 
			
		||||
        ret = bdrv_co_readv(bs->file, sector_num, remaining_sectors,
 | 
			
		||||
                            qiov);
 | 
			
		||||
        backup_cow_request_end(&req);
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = bdrv_co_readv(bs->file, sector_num, remaining_sectors, qiov);
 | 
			
		||||
out:
 | 
			
		||||
    return replication_return_value(s, ret);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static coroutine_fn int replication_co_writev(BlockDriverState *bs,
 | 
			
		||||
                                              int64_t sector_num,
 | 
			
		||||
                                              int remaining_sectors,
 | 
			
		||||
                                              QEMUIOVector *qiov,
 | 
			
		||||
                                              int flags)
 | 
			
		||||
                                              QEMUIOVector *qiov)
 | 
			
		||||
{
 | 
			
		||||
    BDRVReplicationState *s = bs->opaque;
 | 
			
		||||
    QEMUIOVector hd_qiov;
 | 
			
		||||
@@ -252,15 +271,14 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs,
 | 
			
		||||
    int ret;
 | 
			
		||||
    int64_t n;
 | 
			
		||||
 | 
			
		||||
    assert(!flags);
 | 
			
		||||
    ret = replication_get_io_status(s);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ret == 0) {
 | 
			
		||||
        ret = bdrv_co_pwritev(top, sector_num * BDRV_SECTOR_SIZE,
 | 
			
		||||
                              remaining_sectors * BDRV_SECTOR_SIZE, qiov, 0);
 | 
			
		||||
        ret = bdrv_co_writev(top, sector_num,
 | 
			
		||||
                             remaining_sectors, qiov);
 | 
			
		||||
        return replication_return_value(s, ret);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -286,8 +304,7 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs,
 | 
			
		||||
        qemu_iovec_concat(&hd_qiov, qiov, bytes_done, count);
 | 
			
		||||
 | 
			
		||||
        target = ret ? top : base;
 | 
			
		||||
        ret = bdrv_co_pwritev(target, sector_num * BDRV_SECTOR_SIZE,
 | 
			
		||||
                              n * BDRV_SECTOR_SIZE, &hd_qiov, 0);
 | 
			
		||||
        ret = bdrv_co_writev(target, sector_num, n, &hd_qiov);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            goto out1;
 | 
			
		||||
        }
 | 
			
		||||
@@ -350,38 +367,44 @@ static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This function is supposed to be called twice:
 | 
			
		||||
 * first with writable = true, then with writable = false.
 | 
			
		||||
 * The first call puts s->hidden_disk and s->secondary_disk in
 | 
			
		||||
 * r/w mode, and the second puts them back in their original state.
 | 
			
		||||
 */
 | 
			
		||||
static void reopen_backing_file(BlockDriverState *bs, bool writable,
 | 
			
		||||
                                Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BDRVReplicationState *s = bs->opaque;
 | 
			
		||||
    BlockReopenQueue *reopen_queue = NULL;
 | 
			
		||||
    int orig_hidden_flags, orig_secondary_flags;
 | 
			
		||||
    int new_hidden_flags, new_secondary_flags;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
    if (writable) {
 | 
			
		||||
        s->orig_hidden_read_only = bdrv_is_read_only(s->hidden_disk->bs);
 | 
			
		||||
        s->orig_secondary_read_only = bdrv_is_read_only(s->secondary_disk->bs);
 | 
			
		||||
        orig_hidden_flags = s->orig_hidden_flags =
 | 
			
		||||
                                bdrv_get_flags(s->hidden_disk->bs);
 | 
			
		||||
        new_hidden_flags = (orig_hidden_flags | BDRV_O_RDWR) &
 | 
			
		||||
                                                    ~BDRV_O_INACTIVE;
 | 
			
		||||
        orig_secondary_flags = s->orig_secondary_flags =
 | 
			
		||||
                                bdrv_get_flags(s->secondary_disk->bs);
 | 
			
		||||
        new_secondary_flags = (orig_secondary_flags | BDRV_O_RDWR) &
 | 
			
		||||
                                                     ~BDRV_O_INACTIVE;
 | 
			
		||||
    } else {
 | 
			
		||||
        orig_hidden_flags = (s->orig_hidden_flags | BDRV_O_RDWR) &
 | 
			
		||||
                                                    ~BDRV_O_INACTIVE;
 | 
			
		||||
        new_hidden_flags = s->orig_hidden_flags;
 | 
			
		||||
        orig_secondary_flags = (s->orig_secondary_flags | BDRV_O_RDWR) &
 | 
			
		||||
                                                    ~BDRV_O_INACTIVE;
 | 
			
		||||
        new_secondary_flags = s->orig_secondary_flags;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bdrv_subtree_drained_begin(s->hidden_disk->bs);
 | 
			
		||||
    bdrv_subtree_drained_begin(s->secondary_disk->bs);
 | 
			
		||||
 | 
			
		||||
    if (s->orig_hidden_read_only) {
 | 
			
		||||
        QDict *opts = qdict_new();
 | 
			
		||||
        qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
 | 
			
		||||
        reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs,
 | 
			
		||||
                                         opts);
 | 
			
		||||
    if (orig_hidden_flags != new_hidden_flags) {
 | 
			
		||||
        reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, NULL,
 | 
			
		||||
                                         new_hidden_flags);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (s->orig_secondary_read_only) {
 | 
			
		||||
        QDict *opts = qdict_new();
 | 
			
		||||
        qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
 | 
			
		||||
    if (!(orig_secondary_flags & BDRV_O_RDWR)) {
 | 
			
		||||
        reopen_queue = bdrv_reopen_queue(reopen_queue, s->secondary_disk->bs,
 | 
			
		||||
                                         opts);
 | 
			
		||||
                                         NULL, new_secondary_flags);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (reopen_queue) {
 | 
			
		||||
@@ -543,7 +566,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
 | 
			
		||||
        job = backup_job_create(NULL, s->secondary_disk->bs, s->hidden_disk->bs,
 | 
			
		||||
                                0, MIRROR_SYNC_MODE_NONE, NULL, false,
 | 
			
		||||
                                BLOCKDEV_ON_ERROR_REPORT,
 | 
			
		||||
                                BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL,
 | 
			
		||||
                                BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL,
 | 
			
		||||
                                backup_job_completed, bs, NULL, &local_err);
 | 
			
		||||
        if (local_err) {
 | 
			
		||||
            error_propagate(errp, local_err);
 | 
			
		||||
@@ -551,7 +574,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
 | 
			
		||||
            aio_context_release(aio_context);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        job_start(&job->job);
 | 
			
		||||
        block_job_start(job);
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        aio_context_release(aio_context);
 | 
			
		||||
@@ -656,7 +679,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
 | 
			
		||||
         * disk, secondary disk in backup_job_completed().
 | 
			
		||||
         */
 | 
			
		||||
        if (s->secondary_disk->bs->job) {
 | 
			
		||||
            job_cancel_sync(&s->secondary_disk->bs->job->job);
 | 
			
		||||
            block_job_cancel_sync(s->secondary_disk->bs->job);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!failover) {
 | 
			
		||||
@@ -668,7 +691,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
 | 
			
		||||
 | 
			
		||||
        s->stage = BLOCK_REPLICATION_FAILOVER;
 | 
			
		||||
        commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs,
 | 
			
		||||
                            JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT,
 | 
			
		||||
                            BLOCK_JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT,
 | 
			
		||||
                            NULL, replication_done, bs, true, errp);
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										140
									
								
								block/sheepdog.c
									
									
									
									
									
								
							
							
						
						
									
										140
									
								
								block/sheepdog.c
									
									
									
									
									
								
							@@ -24,11 +24,9 @@
 | 
			
		||||
#include "qemu/option.h"
 | 
			
		||||
#include "qemu/sockets.h"
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
#include "block/qdict.h"
 | 
			
		||||
#include "sysemu/block-backend.h"
 | 
			
		||||
#include "qemu/bitops.h"
 | 
			
		||||
#include "qemu/cutils.h"
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
 | 
			
		||||
#define SD_PROTO_VER 0x01
 | 
			
		||||
 | 
			
		||||
@@ -300,6 +298,19 @@ static inline size_t count_data_objs(const struct SheepdogInode *inode)
 | 
			
		||||
                        (1UL << inode->block_size_shift));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#undef DPRINTF
 | 
			
		||||
#ifdef DEBUG_SDOG
 | 
			
		||||
#define DEBUG_SDOG_PRINT 1
 | 
			
		||||
#else
 | 
			
		||||
#define DEBUG_SDOG_PRINT 0
 | 
			
		||||
#endif
 | 
			
		||||
#define DPRINTF(fmt, args...)                                           \
 | 
			
		||||
    do {                                                                \
 | 
			
		||||
        if (DEBUG_SDOG_PRINT) {                                         \
 | 
			
		||||
            fprintf(stderr, "%s %d: " fmt, __func__, __LINE__, ##args); \
 | 
			
		||||
        }                                                               \
 | 
			
		||||
    } while (0)
 | 
			
		||||
 | 
			
		||||
typedef struct SheepdogAIOCB SheepdogAIOCB;
 | 
			
		||||
typedef struct BDRVSheepdogState BDRVSheepdogState;
 | 
			
		||||
 | 
			
		||||
@@ -379,12 +390,12 @@ struct BDRVSheepdogState {
 | 
			
		||||
    uint32_t aioreq_seq_num;
 | 
			
		||||
 | 
			
		||||
    /* Every aio request must be linked to either of these queues. */
 | 
			
		||||
    QLIST_HEAD(, AIOReq) inflight_aio_head;
 | 
			
		||||
    QLIST_HEAD(, AIOReq) failed_aio_head;
 | 
			
		||||
    QLIST_HEAD(inflight_aio_head, AIOReq) inflight_aio_head;
 | 
			
		||||
    QLIST_HEAD(failed_aio_head, AIOReq) failed_aio_head;
 | 
			
		||||
 | 
			
		||||
    CoMutex queue_lock;
 | 
			
		||||
    CoQueue overlapping_queue;
 | 
			
		||||
    QLIST_HEAD(, SheepdogAIOCB) inflight_aiocb_head;
 | 
			
		||||
    QLIST_HEAD(inflight_aiocb_head, SheepdogAIOCB) inflight_aiocb_head;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct BDRVSheepdogReopenState {
 | 
			
		||||
@@ -527,17 +538,27 @@ static void sd_aio_setup(SheepdogAIOCB *acb, BDRVSheepdogState *s,
 | 
			
		||||
static SocketAddress *sd_server_config(QDict *options, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    QDict *server = NULL;
 | 
			
		||||
    QObject *crumpled_server = NULL;
 | 
			
		||||
    Visitor *iv = NULL;
 | 
			
		||||
    SocketAddress *saddr = NULL;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
    qdict_extract_subqdict(options, &server, "server.");
 | 
			
		||||
 | 
			
		||||
    iv = qobject_input_visitor_new_flat_confused(server, errp);
 | 
			
		||||
    if (!iv) {
 | 
			
		||||
    crumpled_server = qdict_crumple(server, errp);
 | 
			
		||||
    if (!crumpled_server) {
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive
 | 
			
		||||
     * server.type=inet.  .to doesn't matter, it's ignored anyway.
 | 
			
		||||
     * That's because when @options come from -blockdev or
 | 
			
		||||
     * blockdev_add, members are typed according to the QAPI schema,
 | 
			
		||||
     * but when they come from -drive, they're all QString.  The
 | 
			
		||||
     * visitor expects the former.
 | 
			
		||||
     */
 | 
			
		||||
    iv = qobject_input_visitor_new(crumpled_server);
 | 
			
		||||
    visit_type_SocketAddress(iv, NULL, &saddr, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
@@ -546,7 +567,8 @@ static SocketAddress *sd_server_config(QDict *options, Error **errp)
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
    visit_free(iv);
 | 
			
		||||
    qobject_unref(server);
 | 
			
		||||
    qobject_decref(crumpled_server);
 | 
			
		||||
    QDECREF(server);
 | 
			
		||||
    return saddr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -560,7 +582,7 @@ static int connect_to_sdog(BDRVSheepdogState *s, Error **errp)
 | 
			
		||||
    if (s->addr->type == SOCKET_ADDRESS_TYPE_INET && fd >= 0) {
 | 
			
		||||
        int ret = socket_set_nodelay(fd);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            warn_report("can't set TCP_NODELAY: %s", strerror(errno));
 | 
			
		||||
            error_report("%s", strerror(errno));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -738,7 +760,7 @@ static coroutine_fn void reconnect_to_sdog(void *opaque)
 | 
			
		||||
        Error *local_err = NULL;
 | 
			
		||||
        s->fd = get_sheep_fd(s, &local_err);
 | 
			
		||||
        if (s->fd < 0) {
 | 
			
		||||
            trace_sheepdog_reconnect_to_sdog();
 | 
			
		||||
            DPRINTF("Wait for connection to be established\n");
 | 
			
		||||
            error_report_err(local_err);
 | 
			
		||||
            qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000000ULL);
 | 
			
		||||
        }
 | 
			
		||||
@@ -835,7 +857,7 @@ static void coroutine_fn aio_read_response(void *opaque)
 | 
			
		||||
        break;
 | 
			
		||||
    case AIOCB_FLUSH_CACHE:
 | 
			
		||||
        if (rsp.result == SD_RES_INVALID_PARMS) {
 | 
			
		||||
            trace_sheepdog_aio_read_response();
 | 
			
		||||
            DPRINTF("disable cache since the server doesn't support it\n");
 | 
			
		||||
            s->cache_flags = SD_FLAG_CMD_DIRECT;
 | 
			
		||||
            rsp.result = SD_RES_SUCCESS;
 | 
			
		||||
        }
 | 
			
		||||
@@ -1014,7 +1036,7 @@ static void sd_parse_uri(SheepdogConfig *cfg, const char *filename,
 | 
			
		||||
 | 
			
		||||
    cfg->uri = uri = uri_parse(filename);
 | 
			
		||||
    if (!uri) {
 | 
			
		||||
        error_setg(&err, "invalid URI '%s'", filename);
 | 
			
		||||
        error_setg(&err, "invalid URI");
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1212,7 +1234,7 @@ static int find_vdi_name(BDRVSheepdogState *s, const char *filename,
 | 
			
		||||
    SheepdogVdiReq hdr;
 | 
			
		||||
    SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr;
 | 
			
		||||
    unsigned int wlen, rlen = 0;
 | 
			
		||||
    char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN] QEMU_NONSTRING;
 | 
			
		||||
    char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN];
 | 
			
		||||
 | 
			
		||||
    fd = connect_to_sdog(s, errp);
 | 
			
		||||
    if (fd < 0) {
 | 
			
		||||
@@ -1627,7 +1649,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    s->discard_supported = true;
 | 
			
		||||
 | 
			
		||||
    if (snap_id || tag[0]) {
 | 
			
		||||
        trace_sheepdog_open(vid);
 | 
			
		||||
        DPRINTF("%" PRIx32 " snapshot inode was open.\n", vid);
 | 
			
		||||
        s->is_snapshot = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1837,7 +1859,9 @@ out:
 | 
			
		||||
        error_setg_errno(errp, -ret, "Can't pre-allocate");
 | 
			
		||||
    }
 | 
			
		||||
out_with_err_set:
 | 
			
		||||
    if (blk) {
 | 
			
		||||
        blk_unref(blk);
 | 
			
		||||
    }
 | 
			
		||||
    g_free(buf);
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
@@ -1859,11 +1883,11 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size,
 | 
			
		||||
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        qobject_unref(obj);
 | 
			
		||||
        qobject_decref(obj);
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qdict = qobject_to(QDict, obj);
 | 
			
		||||
    qdict = qobject_to_qdict(obj);
 | 
			
		||||
    qdict_flatten(qdict);
 | 
			
		||||
 | 
			
		||||
    qdict_put_str(qdict, "driver", "sheepdog");
 | 
			
		||||
@@ -1877,7 +1901,7 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size,
 | 
			
		||||
    ret = sd_prealloc(bs, 0, size, errp);
 | 
			
		||||
fail:
 | 
			
		||||
    bdrv_unref(bs);
 | 
			
		||||
    qobject_unref(qdict);
 | 
			
		||||
    QDECREF(qdict);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1963,7 +1987,6 @@ static SheepdogRedundancy *parse_redundancy_str(const char *opt)
 | 
			
		||||
    } else {
 | 
			
		||||
        ret = qemu_strtol(n2, NULL, 10, &parity);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            g_free(redundancy);
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -2158,8 +2181,9 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
 | 
			
		||||
{
 | 
			
		||||
    BlockdevCreateOptions *create_options = NULL;
 | 
			
		||||
    QDict *qdict, *location_qdict;
 | 
			
		||||
    QObject *crumpled;
 | 
			
		||||
    Visitor *v;
 | 
			
		||||
    char *redundancy;
 | 
			
		||||
    const char *redundancy;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
@@ -2193,14 +2217,16 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Get the QAPI object */
 | 
			
		||||
    v = qobject_input_visitor_new_flat_confused(qdict, errp);
 | 
			
		||||
    if (!v) {
 | 
			
		||||
    crumpled = qdict_crumple(qdict, errp);
 | 
			
		||||
    if (crumpled == NULL) {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    v = qobject_input_visitor_new_keyval(crumpled);
 | 
			
		||||
    visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
 | 
			
		||||
    visit_free(v);
 | 
			
		||||
    qobject_decref(crumpled);
 | 
			
		||||
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
@@ -2226,8 +2252,7 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
 | 
			
		||||
    ret = sd_co_create(create_options, errp);
 | 
			
		||||
fail:
 | 
			
		||||
    qapi_free_BlockdevCreateOptions(create_options);
 | 
			
		||||
    qobject_unref(qdict);
 | 
			
		||||
    g_free(redundancy);
 | 
			
		||||
    QDECREF(qdict);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -2240,7 +2265,7 @@ static void sd_close(BlockDriverState *bs)
 | 
			
		||||
    unsigned int wlen, rlen = 0;
 | 
			
		||||
    int fd, ret;
 | 
			
		||||
 | 
			
		||||
    trace_sheepdog_close(s->name);
 | 
			
		||||
    DPRINTF("%s\n", s->name);
 | 
			
		||||
 | 
			
		||||
    fd = connect_to_sdog(s, &local_err);
 | 
			
		||||
    if (fd < 0) {
 | 
			
		||||
@@ -2280,7 +2305,7 @@ static int64_t sd_getlength(BlockDriverState *bs)
 | 
			
		||||
    return s->inode.vdi_size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn sd_co_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
static int sd_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
                       PreallocMode prealloc, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BDRVSheepdogState *s = bs->opaque;
 | 
			
		||||
@@ -2310,7 +2335,7 @@ static int coroutine_fn sd_co_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* we don't need to update entire object */
 | 
			
		||||
    datalen = SD_INODE_HEADER_SIZE;
 | 
			
		||||
    datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id);
 | 
			
		||||
    s->inode.vdi_size = offset;
 | 
			
		||||
    ret = write_object(fd, s->bs, (char *)&s->inode,
 | 
			
		||||
                       vid_to_vdi_oid(s->inode.vdi_id), s->inode.nr_copies,
 | 
			
		||||
@@ -2417,7 +2442,7 @@ static int sd_create_branch(BDRVSheepdogState *s)
 | 
			
		||||
    char *buf;
 | 
			
		||||
    bool deleted;
 | 
			
		||||
 | 
			
		||||
    trace_sheepdog_create_branch_snapshot(s->inode.vdi_id);
 | 
			
		||||
    DPRINTF("%" PRIx32 " is snapshot.\n", s->inode.vdi_id);
 | 
			
		||||
 | 
			
		||||
    buf = g_malloc(SD_INODE_SIZE);
 | 
			
		||||
 | 
			
		||||
@@ -2433,7 +2458,7 @@ static int sd_create_branch(BDRVSheepdogState *s)
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    trace_sheepdog_create_branch_created(vid);
 | 
			
		||||
    DPRINTF("%" PRIx32 " is created.\n", vid);
 | 
			
		||||
 | 
			
		||||
    fd = connect_to_sdog(s, &local_err);
 | 
			
		||||
    if (fd < 0) {
 | 
			
		||||
@@ -2455,7 +2480,7 @@ static int sd_create_branch(BDRVSheepdogState *s)
 | 
			
		||||
 | 
			
		||||
    s->is_snapshot = false;
 | 
			
		||||
    ret = 0;
 | 
			
		||||
    trace_sheepdog_create_branch_new(s->inode.vdi_id);
 | 
			
		||||
    DPRINTF("%" PRIx32 " was newly created.\n", s->inode.vdi_id);
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
    g_free(buf);
 | 
			
		||||
@@ -2549,11 +2574,11 @@ static void coroutine_fn sd_co_rw_vector(SheepdogAIOCB *acb)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (create) {
 | 
			
		||||
            trace_sheepdog_co_rw_vector_update(inode->vdi_id, oid,
 | 
			
		||||
                                  vid_to_data_oid(inode->data_vdi_id[idx], idx),
 | 
			
		||||
                                  idx);
 | 
			
		||||
            DPRINTF("update ino (%" PRIu32 ") %" PRIu64 " %" PRIu64 " %ld\n",
 | 
			
		||||
                    inode->vdi_id, oid,
 | 
			
		||||
                    vid_to_data_oid(inode->data_vdi_id[idx], idx), idx);
 | 
			
		||||
            oid = vid_to_data_oid(inode->vdi_id, idx);
 | 
			
		||||
            trace_sheepdog_co_rw_vector_new(oid);
 | 
			
		||||
            DPRINTF("new oid %" PRIx64 "\n", oid);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        aio_req = alloc_aio_req(s, acb, oid, len, offset, flags, create,
 | 
			
		||||
@@ -2587,17 +2612,15 @@ static void sd_aio_complete(SheepdogAIOCB *acb)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
 | 
			
		||||
                                     int nb_sectors, QEMUIOVector *qiov,
 | 
			
		||||
                                     int flags)
 | 
			
		||||
                        int nb_sectors, QEMUIOVector *qiov)
 | 
			
		||||
{
 | 
			
		||||
    SheepdogAIOCB acb;
 | 
			
		||||
    int ret;
 | 
			
		||||
    int64_t offset = (sector_num + nb_sectors) * BDRV_SECTOR_SIZE;
 | 
			
		||||
    BDRVSheepdogState *s = bs->opaque;
 | 
			
		||||
 | 
			
		||||
    assert(!flags);
 | 
			
		||||
    if (offset > s->inode.vdi_size) {
 | 
			
		||||
        ret = sd_co_truncate(bs, offset, PREALLOC_MODE_OFF, NULL);
 | 
			
		||||
        ret = sd_truncate(bs, offset, PREALLOC_MODE_OFF, NULL);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
@@ -2658,8 +2681,9 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
 | 
			
		||||
    SheepdogInode *inode;
 | 
			
		||||
    unsigned int datalen;
 | 
			
		||||
 | 
			
		||||
    trace_sheepdog_snapshot_create_info(sn_info->name, sn_info->id_str, s->name,
 | 
			
		||||
                                        sn_info->vm_state_size, s->is_snapshot);
 | 
			
		||||
    DPRINTF("sn_info: name %s id_str %s s: name %s vm_state_size %" PRId64 " "
 | 
			
		||||
            "is_snapshot %d\n", sn_info->name, sn_info->id_str,
 | 
			
		||||
            s->name, sn_info->vm_state_size, s->is_snapshot);
 | 
			
		||||
 | 
			
		||||
    if (s->is_snapshot) {
 | 
			
		||||
        error_report("You can't create a snapshot of a snapshot VDI, "
 | 
			
		||||
@@ -2668,7 +2692,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    trace_sheepdog_snapshot_create(sn_info->name, sn_info->id_str);
 | 
			
		||||
    DPRINTF("%s %s\n", sn_info->name, sn_info->id_str);
 | 
			
		||||
 | 
			
		||||
    s->inode.vm_state_size = sn_info->vm_state_size;
 | 
			
		||||
    s->inode.vm_clock_nsec = sn_info->vm_clock_nsec;
 | 
			
		||||
@@ -2677,7 +2701,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
 | 
			
		||||
     */
 | 
			
		||||
    strncpy(s->inode.tag, sn_info->name, sizeof(s->inode.tag));
 | 
			
		||||
    /* we don't need to update entire object */
 | 
			
		||||
    datalen = SD_INODE_HEADER_SIZE;
 | 
			
		||||
    datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id);
 | 
			
		||||
    inode = g_malloc(datalen);
 | 
			
		||||
 | 
			
		||||
    /* refresh inode. */
 | 
			
		||||
@@ -2713,8 +2737,8 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memcpy(&s->inode, inode, datalen);
 | 
			
		||||
    trace_sheepdog_snapshot_create_inode(s->inode.name, s->inode.snap_id,
 | 
			
		||||
                                         s->inode.vdi_id);
 | 
			
		||||
    DPRINTF("s->inode: name %s snap_id %x oid %x\n",
 | 
			
		||||
            s->inode.name, s->inode.snap_id, s->inode.vdi_id);
 | 
			
		||||
 | 
			
		||||
cleanup:
 | 
			
		||||
    g_free(inode);
 | 
			
		||||
@@ -2912,14 +2936,13 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
 | 
			
		||||
    QEMUSnapshotInfo *sn_tab = NULL;
 | 
			
		||||
    unsigned wlen, rlen;
 | 
			
		||||
    int found = 0;
 | 
			
		||||
    SheepdogInode *inode;
 | 
			
		||||
    static SheepdogInode inode;
 | 
			
		||||
    unsigned long *vdi_inuse;
 | 
			
		||||
    unsigned int start_nr;
 | 
			
		||||
    uint64_t hval;
 | 
			
		||||
    uint32_t vid;
 | 
			
		||||
 | 
			
		||||
    vdi_inuse = g_malloc(max);
 | 
			
		||||
    inode = g_malloc(SD_INODE_HEADER_SIZE);
 | 
			
		||||
 | 
			
		||||
    fd = connect_to_sdog(s, &local_err);
 | 
			
		||||
    if (fd < 0) {
 | 
			
		||||
@@ -2962,26 +2985,26 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* we don't need to read entire object */
 | 
			
		||||
        ret = read_object(fd, s->bs, (char *)inode,
 | 
			
		||||
        ret = read_object(fd, s->bs, (char *)&inode,
 | 
			
		||||
                          vid_to_vdi_oid(vid),
 | 
			
		||||
                          0, SD_INODE_HEADER_SIZE, 0,
 | 
			
		||||
                          0, SD_INODE_SIZE - sizeof(inode.data_vdi_id), 0,
 | 
			
		||||
                          s->cache_flags);
 | 
			
		||||
 | 
			
		||||
        if (ret) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!strcmp(inode->name, s->name) && is_snapshot(inode)) {
 | 
			
		||||
            sn_tab[found].date_sec = inode->snap_ctime >> 32;
 | 
			
		||||
            sn_tab[found].date_nsec = inode->snap_ctime & 0xffffffff;
 | 
			
		||||
            sn_tab[found].vm_state_size = inode->vm_state_size;
 | 
			
		||||
            sn_tab[found].vm_clock_nsec = inode->vm_clock_nsec;
 | 
			
		||||
        if (!strcmp(inode.name, s->name) && is_snapshot(&inode)) {
 | 
			
		||||
            sn_tab[found].date_sec = inode.snap_ctime >> 32;
 | 
			
		||||
            sn_tab[found].date_nsec = inode.snap_ctime & 0xffffffff;
 | 
			
		||||
            sn_tab[found].vm_state_size = inode.vm_state_size;
 | 
			
		||||
            sn_tab[found].vm_clock_nsec = inode.vm_clock_nsec;
 | 
			
		||||
 | 
			
		||||
            snprintf(sn_tab[found].id_str, sizeof(sn_tab[found].id_str),
 | 
			
		||||
                     "%" PRIu32, inode->snap_id);
 | 
			
		||||
                     "%" PRIu32, inode.snap_id);
 | 
			
		||||
            pstrcpy(sn_tab[found].name,
 | 
			
		||||
                    MIN(sizeof(sn_tab[found].name), sizeof(inode->tag)),
 | 
			
		||||
                    inode->tag);
 | 
			
		||||
                    MIN(sizeof(sn_tab[found].name), sizeof(inode.tag)),
 | 
			
		||||
                    inode.tag);
 | 
			
		||||
            found++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -2991,7 +3014,6 @@ out:
 | 
			
		||||
    *psn_tab = sn_tab;
 | 
			
		||||
 | 
			
		||||
    g_free(vdi_inuse);
 | 
			
		||||
    g_free(inode);
 | 
			
		||||
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
@@ -3218,7 +3240,7 @@ static BlockDriver bdrv_sheepdog = {
 | 
			
		||||
    .bdrv_has_zero_init           = bdrv_has_zero_init_1,
 | 
			
		||||
    .bdrv_getlength               = sd_getlength,
 | 
			
		||||
    .bdrv_get_allocated_file_size = sd_get_allocated_file_size,
 | 
			
		||||
    .bdrv_co_truncate             = sd_co_truncate,
 | 
			
		||||
    .bdrv_truncate                = sd_truncate,
 | 
			
		||||
 | 
			
		||||
    .bdrv_co_readv                = sd_co_readv,
 | 
			
		||||
    .bdrv_co_writev               = sd_co_writev,
 | 
			
		||||
@@ -3255,7 +3277,7 @@ static BlockDriver bdrv_sheepdog_tcp = {
 | 
			
		||||
    .bdrv_has_zero_init           = bdrv_has_zero_init_1,
 | 
			
		||||
    .bdrv_getlength               = sd_getlength,
 | 
			
		||||
    .bdrv_get_allocated_file_size = sd_get_allocated_file_size,
 | 
			
		||||
    .bdrv_co_truncate             = sd_co_truncate,
 | 
			
		||||
    .bdrv_truncate                = sd_truncate,
 | 
			
		||||
 | 
			
		||||
    .bdrv_co_readv                = sd_co_readv,
 | 
			
		||||
    .bdrv_co_writev               = sd_co_writev,
 | 
			
		||||
@@ -3292,7 +3314,7 @@ static BlockDriver bdrv_sheepdog_unix = {
 | 
			
		||||
    .bdrv_has_zero_init           = bdrv_has_zero_init_1,
 | 
			
		||||
    .bdrv_getlength               = sd_getlength,
 | 
			
		||||
    .bdrv_get_allocated_file_size = sd_get_allocated_file_size,
 | 
			
		||||
    .bdrv_co_truncate             = sd_co_truncate,
 | 
			
		||||
    .bdrv_truncate                = sd_truncate,
 | 
			
		||||
 | 
			
		||||
    .bdrv_co_readv                = sd_co_readv,
 | 
			
		||||
    .bdrv_co_writev               = sd_co_writev,
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,6 @@
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include "block/snapshot.h"
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
#include "block/qdict.h"
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
#include "qapi/qmp/qdict.h"
 | 
			
		||||
#include "qapi/qmp/qerror.h"
 | 
			
		||||
@@ -215,18 +214,16 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
 | 
			
		||||
        bdrv_ref(file);
 | 
			
		||||
 | 
			
		||||
        qdict_extract_subqdict(options, &file_options, "file.");
 | 
			
		||||
        qobject_unref(file_options);
 | 
			
		||||
        QDECREF(file_options);
 | 
			
		||||
        qdict_put_str(options, "file", bdrv_get_node_name(file));
 | 
			
		||||
 | 
			
		||||
        if (drv->bdrv_close) {
 | 
			
		||||
        drv->bdrv_close(bs);
 | 
			
		||||
        }
 | 
			
		||||
        bdrv_unref_child(bs, bs->file);
 | 
			
		||||
        bs->file = NULL;
 | 
			
		||||
 | 
			
		||||
        ret = bdrv_snapshot_goto(file, snapshot_id, errp);
 | 
			
		||||
        open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err);
 | 
			
		||||
        qobject_unref(options);
 | 
			
		||||
        QDECREF(options);
 | 
			
		||||
        if (open_ret < 0) {
 | 
			
		||||
            bdrv_unref(file);
 | 
			
		||||
            bs->drv = NULL;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										72
									
								
								block/ssh.c
									
									
									
									
									
								
							
							
						
						
									
										72
									
								
								block/ssh.c
									
									
									
									
									
								
							@@ -28,7 +28,6 @@
 | 
			
		||||
#include <libssh2_sftp.h>
 | 
			
		||||
 | 
			
		||||
#include "block/block_int.h"
 | 
			
		||||
#include "block/qdict.h"
 | 
			
		||||
#include "qapi/error.h"
 | 
			
		||||
#include "qemu/error-report.h"
 | 
			
		||||
#include "qemu/option.h"
 | 
			
		||||
@@ -41,17 +40,27 @@
 | 
			
		||||
#include "qapi/qmp/qstring.h"
 | 
			
		||||
#include "qapi/qobject-input-visitor.h"
 | 
			
		||||
#include "qapi/qobject-output-visitor.h"
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
/* DEBUG_SSH=1 enables the DPRINTF (debugging printf) statements in
 | 
			
		||||
 * this block driver code.
 | 
			
		||||
 *
 | 
			
		||||
 * TRACE_LIBSSH2=<bitmask> enables tracing in libssh2 itself.  Note
 | 
			
		||||
 * that this requires that libssh2 was specially compiled with the
 | 
			
		||||
 * `./configure --enable-debug' option, so most likely you will have
 | 
			
		||||
 * to compile it yourself.  The meaning of <bitmask> is described
 | 
			
		||||
 * here: http://www.libssh2.org/libssh2_trace.html
 | 
			
		||||
 */
 | 
			
		||||
#define DEBUG_SSH     0
 | 
			
		||||
#define TRACE_LIBSSH2 0 /* or try: LIBSSH2_TRACE_SFTP */
 | 
			
		||||
 | 
			
		||||
#define DPRINTF(fmt, ...)                           \
 | 
			
		||||
    do {                                            \
 | 
			
		||||
        if (DEBUG_SSH) {                            \
 | 
			
		||||
            fprintf(stderr, "ssh: %-15s " fmt "\n", \
 | 
			
		||||
                    __func__, ##__VA_ARGS__);       \
 | 
			
		||||
        }                                           \
 | 
			
		||||
    } while (0)
 | 
			
		||||
 | 
			
		||||
typedef struct BDRVSSHState {
 | 
			
		||||
    /* Coroutine. */
 | 
			
		||||
    CoMutex lock;
 | 
			
		||||
@@ -326,7 +335,7 @@ static int check_host_key_knownhosts(BDRVSSHState *s,
 | 
			
		||||
    switch (r) {
 | 
			
		||||
    case LIBSSH2_KNOWNHOST_CHECK_MATCH:
 | 
			
		||||
        /* OK */
 | 
			
		||||
        trace_ssh_check_host_key_knownhosts(found->key);
 | 
			
		||||
        DPRINTF("host key OK: %s", found->key);
 | 
			
		||||
        break;
 | 
			
		||||
    case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
@@ -596,6 +605,7 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp)
 | 
			
		||||
    BlockdevOptionsSsh *result = NULL;
 | 
			
		||||
    QemuOpts *opts = NULL;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    QObject *crumpled;
 | 
			
		||||
    const QDictEntry *e;
 | 
			
		||||
    Visitor *v;
 | 
			
		||||
 | 
			
		||||
@@ -612,13 +622,23 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Create the QAPI object */
 | 
			
		||||
    v = qobject_input_visitor_new_flat_confused(options, errp);
 | 
			
		||||
    if (!v) {
 | 
			
		||||
    crumpled = qdict_crumple(options, errp);
 | 
			
		||||
    if (crumpled == NULL) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive.
 | 
			
		||||
     * .to doesn't matter, it's ignored anyway.
 | 
			
		||||
     * That's because when @options come from -blockdev or
 | 
			
		||||
     * blockdev_add, members are typed according to the QAPI schema,
 | 
			
		||||
     * but when they come from -drive, they're all QString.  The
 | 
			
		||||
     * visitor expects the former.
 | 
			
		||||
     */
 | 
			
		||||
    v = qobject_input_visitor_new(crumpled);
 | 
			
		||||
    visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err);
 | 
			
		||||
    visit_free(v);
 | 
			
		||||
    qobject_decref(crumpled);
 | 
			
		||||
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
@@ -711,7 +731,8 @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Open the remote file. */
 | 
			
		||||
    trace_ssh_connect_to_ssh(opts->path, ssh_flags, creat_mode);
 | 
			
		||||
    DPRINTF("opening file %s flags=0x%x creat_mode=0%o",
 | 
			
		||||
            opts->path, ssh_flags, creat_mode);
 | 
			
		||||
    s->sftp_handle = libssh2_sftp_open(s->sftp, opts->path, ssh_flags,
 | 
			
		||||
                                       creat_mode);
 | 
			
		||||
    if (!s->sftp_handle) {
 | 
			
		||||
@@ -879,7 +900,7 @@ static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts,
 | 
			
		||||
    /* Get desired file size. */
 | 
			
		||||
    ssh_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
 | 
			
		||||
                              BDRV_SECTOR_SIZE);
 | 
			
		||||
    trace_ssh_co_create_opts(ssh_opts->size);
 | 
			
		||||
    DPRINTF("total_size=%" PRIi64, ssh_opts->size);
 | 
			
		||||
 | 
			
		||||
    uri_options = qdict_new();
 | 
			
		||||
    ret = parse_uri(filename, uri_options, errp);
 | 
			
		||||
@@ -896,7 +917,7 @@ static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts,
 | 
			
		||||
    ret = ssh_co_create(create_options, errp);
 | 
			
		||||
 | 
			
		||||
 out:
 | 
			
		||||
    qobject_unref(uri_options);
 | 
			
		||||
    QDECREF(uri_options);
 | 
			
		||||
    qapi_free_BlockdevCreateOptions(create_options);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
@@ -935,7 +956,7 @@ static void restart_coroutine(void *opaque)
 | 
			
		||||
    BDRVSSHState *s = bs->opaque;
 | 
			
		||||
    AioContext *ctx = bdrv_get_aio_context(bs);
 | 
			
		||||
 | 
			
		||||
    trace_ssh_restart_coroutine(restart->co);
 | 
			
		||||
    DPRINTF("co=%p", restart->co);
 | 
			
		||||
    aio_set_fd_handler(ctx, s->sock, false, NULL, NULL, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
    aio_co_wake(restart->co);
 | 
			
		||||
@@ -963,12 +984,13 @@ static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs)
 | 
			
		||||
        wr_handler = restart_coroutine;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    trace_ssh_co_yield(s->sock, rd_handler, wr_handler);
 | 
			
		||||
    DPRINTF("s->sock=%d rd_handler=%p wr_handler=%p", s->sock,
 | 
			
		||||
            rd_handler, wr_handler);
 | 
			
		||||
 | 
			
		||||
    aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock,
 | 
			
		||||
                       false, rd_handler, wr_handler, NULL, &restart);
 | 
			
		||||
    qemu_coroutine_yield();
 | 
			
		||||
    trace_ssh_co_yield_back(s->sock);
 | 
			
		||||
    DPRINTF("s->sock=%d - back", s->sock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* SFTP has a function `libssh2_sftp_seek64' which seeks to a position
 | 
			
		||||
@@ -991,7 +1013,7 @@ static void ssh_seek(BDRVSSHState *s, int64_t offset, int flags)
 | 
			
		||||
    bool force = (flags & SSH_SEEK_FORCE) != 0;
 | 
			
		||||
 | 
			
		||||
    if (force || op_read != s->offset_op_read || offset != s->offset) {
 | 
			
		||||
        trace_ssh_seek(offset);
 | 
			
		||||
        DPRINTF("seeking to offset=%" PRIi64, offset);
 | 
			
		||||
        libssh2_sftp_seek64(s->sftp_handle, offset);
 | 
			
		||||
        s->offset = offset;
 | 
			
		||||
        s->offset_op_read = op_read;
 | 
			
		||||
@@ -1007,7 +1029,7 @@ static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs,
 | 
			
		||||
    char *buf, *end_of_vec;
 | 
			
		||||
    struct iovec *i;
 | 
			
		||||
 | 
			
		||||
    trace_ssh_read(offset, size);
 | 
			
		||||
    DPRINTF("offset=%" PRIi64 " size=%zu", offset, size);
 | 
			
		||||
 | 
			
		||||
    ssh_seek(s, offset, SSH_SEEK_READ);
 | 
			
		||||
 | 
			
		||||
@@ -1026,9 +1048,9 @@ static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs,
 | 
			
		||||
     */
 | 
			
		||||
    for (got = 0; got < size; ) {
 | 
			
		||||
    again:
 | 
			
		||||
        trace_ssh_read_buf(buf, end_of_vec - buf);
 | 
			
		||||
        DPRINTF("sftp_read buf=%p size=%zu", buf, end_of_vec - buf);
 | 
			
		||||
        r = libssh2_sftp_read(s->sftp_handle, buf, end_of_vec - buf);
 | 
			
		||||
        trace_ssh_read_return(r);
 | 
			
		||||
        DPRINTF("sftp_read returned %zd", r);
 | 
			
		||||
 | 
			
		||||
        if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
 | 
			
		||||
            co_yield(s, bs);
 | 
			
		||||
@@ -1082,7 +1104,7 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
 | 
			
		||||
    char *buf, *end_of_vec;
 | 
			
		||||
    struct iovec *i;
 | 
			
		||||
 | 
			
		||||
    trace_ssh_write(offset, size);
 | 
			
		||||
    DPRINTF("offset=%" PRIi64 " size=%zu", offset, size);
 | 
			
		||||
 | 
			
		||||
    ssh_seek(s, offset, SSH_SEEK_WRITE);
 | 
			
		||||
 | 
			
		||||
@@ -1096,9 +1118,9 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
 | 
			
		||||
 | 
			
		||||
    for (written = 0; written < size; ) {
 | 
			
		||||
    again:
 | 
			
		||||
        trace_ssh_write_buf(buf, end_of_vec - buf);
 | 
			
		||||
        DPRINTF("sftp_write buf=%p size=%zu", buf, end_of_vec - buf);
 | 
			
		||||
        r = libssh2_sftp_write(s->sftp_handle, buf, end_of_vec - buf);
 | 
			
		||||
        trace_ssh_write_return(r);
 | 
			
		||||
        DPRINTF("sftp_write returned %zd", r);
 | 
			
		||||
 | 
			
		||||
        if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
 | 
			
		||||
            co_yield(s, bs);
 | 
			
		||||
@@ -1142,13 +1164,11 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
 | 
			
		||||
 | 
			
		||||
static coroutine_fn int ssh_co_writev(BlockDriverState *bs,
 | 
			
		||||
                                      int64_t sector_num,
 | 
			
		||||
                                      int nb_sectors, QEMUIOVector *qiov,
 | 
			
		||||
                                      int flags)
 | 
			
		||||
                                      int nb_sectors, QEMUIOVector *qiov)
 | 
			
		||||
{
 | 
			
		||||
    BDRVSSHState *s = bs->opaque;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    assert(!flags);
 | 
			
		||||
    qemu_co_mutex_lock(&s->lock);
 | 
			
		||||
    ret = ssh_write(s, bs, sector_num * BDRV_SECTOR_SIZE,
 | 
			
		||||
                    nb_sectors * BDRV_SECTOR_SIZE, qiov);
 | 
			
		||||
@@ -1175,7 +1195,7 @@ static coroutine_fn int ssh_flush(BDRVSSHState *s, BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    int r;
 | 
			
		||||
 | 
			
		||||
    trace_ssh_flush();
 | 
			
		||||
    DPRINTF("fsync");
 | 
			
		||||
 again:
 | 
			
		||||
    r = libssh2_sftp_fsync(s->sftp_handle);
 | 
			
		||||
    if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
 | 
			
		||||
@@ -1226,12 +1246,12 @@ static int64_t ssh_getlength(BlockDriverState *bs)
 | 
			
		||||
 | 
			
		||||
    /* Note we cannot make a libssh2 call here. */
 | 
			
		||||
    length = (int64_t) s->attrs.filesize;
 | 
			
		||||
    trace_ssh_getlength(length);
 | 
			
		||||
    DPRINTF("length=%" PRIi64, length);
 | 
			
		||||
 | 
			
		||||
    return length;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
static int ssh_truncate(BlockDriverState *bs, int64_t offset,
 | 
			
		||||
                        PreallocMode prealloc, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BDRVSSHState *s = bs->opaque;
 | 
			
		||||
@@ -1267,7 +1287,7 @@ static BlockDriver bdrv_ssh = {
 | 
			
		||||
    .bdrv_co_readv                = ssh_co_readv,
 | 
			
		||||
    .bdrv_co_writev               = ssh_co_writev,
 | 
			
		||||
    .bdrv_getlength               = ssh_getlength,
 | 
			
		||||
    .bdrv_co_truncate             = ssh_co_truncate,
 | 
			
		||||
    .bdrv_truncate                = ssh_truncate,
 | 
			
		||||
    .bdrv_co_flush_to_disk        = ssh_co_flush,
 | 
			
		||||
    .create_opts                  = &ssh_create_opts,
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user