mirror of https://github.com/xqemu/xqemu.git
v4.0.0 release
-----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAly/PhEZHHBldGVyLm1h eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3kAfD/9SnusF4bCaeHG+oq/cRhte LbS1uCoSxynMKRvhx+s/tk7kzovW9twChMfE4xVcxHBY9hUFgMLnnq1lrJZ9GN9B 6zTrO9UDnkAfgkUq9B3lL0b+OVn4QFlcOFUl6U0q9E3zFUiCneLK9cEpw20t+2EL 78sjrpENms7nCeuUhiwZm00lbn4stY9vAiOZpu8qrg9lzDVaRivK5BrtkutfmDRn REHll2gduZp3FNkexiJs73YU+BFZMBXM+PqldU+c4iU4Cq2lUNco+Q4Ks32Q7Nf7 9/U1j5znW9M4X9jDi8jSU5Bd0rJQMid1h0wV0SrE1PWKJOAvF8w+0FPmEJDERRx/ W7Pz7+rYr2iOsOyJT4CuJQZUvJmIyMUz7JNVHOh/P8Hmb1PKp7Egy5Kamo2o7slA I/5wmI6HDAizyjaV6UL2D8KqfedihZoTS6HmCc2eX75nfa0eauDFKCMwZKOb1FYI dldRhOE1wiFKCV/jPEdBNJbE8jH9e5kH3CpcB1vnmphqkmHz1yKIToFgTDGrc8e3 mj7e67iNG1oIUys/w3zgEUYI6iSbkSyIYv9nlUv8NNSTUKK2kfpUMbJW3FyXrFR2 QvaaNOYJJHG+x8sCpPwWRBQiix/x5F/s6RKMpRgIa/QYKPwGKniEjgqcGSMdmyxM RnuxJvLfYcyAILZx20nCIA== =92OI -----END PGP SIGNATURE----- Merge tag 'v4.0.0' into merge-v4.0.0 v4.0.0 release
This commit is contained in:
commit
a1b119ce44
|
@ -0,0 +1,27 @@
|
||||||
|
env:
|
||||||
|
CIRRUS_CLONE_DEPTH: 1
|
||||||
|
|
||||||
|
freebsd_12_task:
|
||||||
|
freebsd_instance:
|
||||||
|
image: freebsd-12-0-release-amd64
|
||||||
|
cpu: 8
|
||||||
|
memory: 8G
|
||||||
|
install_script: pkg install -y
|
||||||
|
bison curl cyrus-sasl git glib gmake gnutls
|
||||||
|
nettle perl5 pixman pkgconf png usbredir
|
||||||
|
script:
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- ../configure || { cat config.log; exit 1; }
|
||||||
|
- gmake -j8
|
||||||
|
- gmake -j8 V=1 check
|
||||||
|
|
||||||
|
macos_task:
|
||||||
|
osx_instance:
|
||||||
|
image: mojave-base
|
||||||
|
install_script:
|
||||||
|
- brew install pkg-config python glib pixman make sdl2
|
||||||
|
script:
|
||||||
|
- ./configure --python=/usr/local/bin/python3 || { cat config.log; exit 1; }
|
||||||
|
- gmake -j$(sysctl -n hw.ncpu)
|
||||||
|
- gmake check -j$(sysctl -n hw.ncpu)
|
|
@ -1,4 +1,10 @@
|
||||||
# http://editorconfig.org
|
# 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.
|
||||||
|
|
||||||
root = true
|
root = true
|
||||||
|
|
||||||
[*]
|
[*]
|
||||||
|
@ -6,10 +12,23 @@ end_of_line = lf
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
|
|
||||||
|
[*.mak]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 8
|
||||||
|
file_type_emacs = makefile
|
||||||
|
|
||||||
[Makefile*]
|
[Makefile*]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 8
|
indent_size = 8
|
||||||
|
file_type_emacs = makefile
|
||||||
|
|
||||||
[*.{c,h}]
|
[*.{c,h}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.{vert,frag}]
|
||||||
|
file_type_emacs = glsl
|
||||||
|
|
||||||
|
[*.json]
|
||||||
|
indent_style = space
|
||||||
|
file_type_emacs = python
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/.doctrees
|
||||||
/config-devices.*
|
/config-devices.*
|
||||||
/config-all-devices.*
|
/config-all-devices.*
|
||||||
/config-all-disas.*
|
/config-all-disas.*
|
||||||
|
@ -5,6 +6,7 @@
|
||||||
/config-target.*
|
/config-target.*
|
||||||
/config.status
|
/config.status
|
||||||
/config-temp
|
/config-temp
|
||||||
|
/elf2dmp
|
||||||
/trace-events-all
|
/trace-events-all
|
||||||
/trace/generated-events.h
|
/trace/generated-events.h
|
||||||
/trace/generated-events.c
|
/trace/generated-events.c
|
||||||
|
@ -30,83 +32,21 @@
|
||||||
/qapi-gen-timestamp
|
/qapi-gen-timestamp
|
||||||
/qapi/qapi-builtin-types.[ch]
|
/qapi/qapi-builtin-types.[ch]
|
||||||
/qapi/qapi-builtin-visit.[ch]
|
/qapi/qapi-builtin-visit.[ch]
|
||||||
/qapi/qapi-commands-block-core.[ch]
|
/qapi/qapi-commands-*.[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-job.[ch]
|
|
||||||
/qapi/qapi-commands-migration.[ch]
|
|
||||||
/qapi/qapi-commands-misc.[ch]
|
|
||||||
/qapi/qapi-commands-net.[ch]
|
|
||||||
/qapi/qapi-commands-rocker.[ch]
|
|
||||||
/qapi/qapi-commands-run-state.[ch]
|
|
||||||
/qapi/qapi-commands-sockets.[ch]
|
|
||||||
/qapi/qapi-commands-tpm.[ch]
|
|
||||||
/qapi/qapi-commands-trace.[ch]
|
|
||||||
/qapi/qapi-commands-transaction.[ch]
|
|
||||||
/qapi/qapi-commands-ui.[ch]
|
|
||||||
/qapi/qapi-commands.[ch]
|
/qapi/qapi-commands.[ch]
|
||||||
/qapi/qapi-events-block-core.[ch]
|
/qapi/qapi-emit-events.[ch]
|
||||||
/qapi/qapi-events-block.[ch]
|
/qapi/qapi-events-*.[ch]
|
||||||
/qapi/qapi-events-char.[ch]
|
|
||||||
/qapi/qapi-events-common.[ch]
|
|
||||||
/qapi/qapi-events-crypto.[ch]
|
|
||||||
/qapi/qapi-events-introspect.[ch]
|
|
||||||
/qapi/qapi-events-job.[ch]
|
|
||||||
/qapi/qapi-events-migration.[ch]
|
|
||||||
/qapi/qapi-events-misc.[ch]
|
|
||||||
/qapi/qapi-events-net.[ch]
|
|
||||||
/qapi/qapi-events-rocker.[ch]
|
|
||||||
/qapi/qapi-events-run-state.[ch]
|
|
||||||
/qapi/qapi-events-sockets.[ch]
|
|
||||||
/qapi/qapi-events-tpm.[ch]
|
|
||||||
/qapi/qapi-events-trace.[ch]
|
|
||||||
/qapi/qapi-events-transaction.[ch]
|
|
||||||
/qapi/qapi-events-ui.[ch]
|
|
||||||
/qapi/qapi-events.[ch]
|
/qapi/qapi-events.[ch]
|
||||||
/qapi/qapi-introspect.[ch]
|
/qapi/qapi-introspect.[ch]
|
||||||
/qapi/qapi-types-block-core.[ch]
|
/qapi/qapi-types-*.[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-job.[ch]
|
|
||||||
/qapi/qapi-types-migration.[ch]
|
|
||||||
/qapi/qapi-types-misc.[ch]
|
|
||||||
/qapi/qapi-types-net.[ch]
|
|
||||||
/qapi/qapi-types-rocker.[ch]
|
|
||||||
/qapi/qapi-types-run-state.[ch]
|
|
||||||
/qapi/qapi-types-sockets.[ch]
|
|
||||||
/qapi/qapi-types-tpm.[ch]
|
|
||||||
/qapi/qapi-types-trace.[ch]
|
|
||||||
/qapi/qapi-types-transaction.[ch]
|
|
||||||
/qapi/qapi-types-ui.[ch]
|
|
||||||
/qapi/qapi-types.[ch]
|
/qapi/qapi-types.[ch]
|
||||||
/qapi/qapi-visit-block-core.[ch]
|
/qapi/qapi-visit-*.[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-job.[ch]
|
|
||||||
/qapi/qapi-visit-migration.[ch]
|
|
||||||
/qapi/qapi-visit-misc.[ch]
|
|
||||||
/qapi/qapi-visit-net.[ch]
|
|
||||||
/qapi/qapi-visit-rocker.[ch]
|
|
||||||
/qapi/qapi-visit-run-state.[ch]
|
|
||||||
/qapi/qapi-visit-sockets.[ch]
|
|
||||||
/qapi/qapi-visit-tpm.[ch]
|
|
||||||
/qapi/qapi-visit-trace.[ch]
|
|
||||||
/qapi/qapi-visit-transaction.[ch]
|
|
||||||
/qapi/qapi-visit-ui.[ch]
|
|
||||||
/qapi/qapi-visit.[ch]
|
/qapi/qapi-visit.[ch]
|
||||||
/qapi/qapi-doc.texi
|
/qapi/qapi-doc.texi
|
||||||
/qemu-doc.html
|
/qemu-doc.html
|
||||||
/qemu-doc.info
|
/qemu-doc.info
|
||||||
/qemu-doc.txt
|
/qemu-doc.txt
|
||||||
|
/qemu-edid
|
||||||
/qemu-img
|
/qemu-img
|
||||||
/qemu-nbd
|
/qemu-nbd
|
||||||
/qemu-options.def
|
/qemu-options.def
|
||||||
|
@ -166,6 +106,10 @@
|
||||||
/pc-bios/optionrom/linuxboot_dma.bin
|
/pc-bios/optionrom/linuxboot_dma.bin
|
||||||
/pc-bios/optionrom/linuxboot_dma.raw
|
/pc-bios/optionrom/linuxboot_dma.raw
|
||||||
/pc-bios/optionrom/linuxboot_dma.img
|
/pc-bios/optionrom/linuxboot_dma.img
|
||||||
|
/pc-bios/optionrom/pvh.asm
|
||||||
|
/pc-bios/optionrom/pvh.bin
|
||||||
|
/pc-bios/optionrom/pvh.raw
|
||||||
|
/pc-bios/optionrom/pvh.img
|
||||||
/pc-bios/optionrom/multiboot.asm
|
/pc-bios/optionrom/multiboot.asm
|
||||||
/pc-bios/optionrom/multiboot.bin
|
/pc-bios/optionrom/multiboot.bin
|
||||||
/pc-bios/optionrom/multiboot.raw
|
/pc-bios/optionrom/multiboot.raw
|
||||||
|
@ -176,6 +120,7 @@
|
||||||
/pc-bios/optionrom/kvmvapic.img
|
/pc-bios/optionrom/kvmvapic.img
|
||||||
/pc-bios/s390-ccw/s390-ccw.elf
|
/pc-bios/s390-ccw/s390-ccw.elf
|
||||||
/pc-bios/s390-ccw/s390-ccw.img
|
/pc-bios/s390-ccw/s390-ccw.img
|
||||||
|
/docs/built
|
||||||
/docs/interop/qemu-ga-qapi.texi
|
/docs/interop/qemu-ga-qapi.texi
|
||||||
/docs/interop/qemu-ga-ref.html
|
/docs/interop/qemu-ga-ref.html
|
||||||
/docs/interop/qemu-ga-ref.info*
|
/docs/interop/qemu-ga-ref.info*
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
before_script:
|
||||||
|
- apt-get update -qq
|
||||||
|
- apt-get install -y -qq flex bison libglib2.0-dev libpixman-1-dev genisoimage
|
||||||
|
|
||||||
|
build-system1:
|
||||||
|
script:
|
||||||
|
- apt-get install -y -qq libgtk-3-dev libvte-dev nettle-dev libcacard-dev
|
||||||
|
libusb-dev libvde-dev libspice-protocol-dev libgl1-mesa-dev
|
||||||
|
- ./configure --enable-werror --target-list="aarch64-softmmu alpha-softmmu
|
||||||
|
cris-softmmu hppa-softmmu lm32-softmmu moxie-softmmu microblazeel-softmmu
|
||||||
|
mips64el-softmmu m68k-softmmu ppc-softmmu riscv64-softmmu sparc-softmmu"
|
||||||
|
- make -j2
|
||||||
|
- make -j2 check
|
||||||
|
|
||||||
|
build-system2:
|
||||||
|
script:
|
||||||
|
- apt-get install -y -qq libsdl2-dev libgcrypt-dev libbrlapi-dev libaio-dev
|
||||||
|
libfdt-dev liblzo2-dev librdmacm-dev libibverbs-dev libibumad-dev
|
||||||
|
- ./configure --enable-werror --target-list="tricore-softmmu unicore32-softmmu
|
||||||
|
microblaze-softmmu mips-softmmu riscv32-softmmu s390x-softmmu sh4-softmmu
|
||||||
|
sparc64-softmmu x86_64-softmmu xtensa-softmmu nios2-softmmu or1k-softmmu"
|
||||||
|
- make -j2
|
||||||
|
- make -j2 check
|
||||||
|
|
||||||
|
build-disabled:
|
||||||
|
script:
|
||||||
|
- ./configure --enable-werror --disable-rdma --disable-slirp --disable-curl
|
||||||
|
--disable-capstone --disable-live-block-migration --disable-glusterfs
|
||||||
|
--disable-replication --disable-coroutine-pool --disable-smartcard
|
||||||
|
--disable-guest-agent --disable-curses --disable-libxml2 --disable-tpm
|
||||||
|
--disable-qom-cast-debug --disable-spice --disable-vhost-vsock
|
||||||
|
--disable-vhost-net --disable-vhost-crypto --disable-vhost-user
|
||||||
|
--target-list="i386-softmmu ppc64-softmmu mips64-softmmu i386-linux-user"
|
||||||
|
- make -j2
|
||||||
|
- make -j2 check-qtest SPEED=slow
|
||||||
|
|
||||||
|
build-tcg-disabled:
|
||||||
|
script:
|
||||||
|
- apt-get install -y -qq clang libgtk-3-dev libbluetooth-dev libusb-dev
|
||||||
|
- ./configure --cc=clang --enable-werror --disable-tcg --audio-drv-list=""
|
||||||
|
- make -j2
|
||||||
|
- make check-unit
|
||||||
|
- make check-qapi-schema
|
||||||
|
- cd tests/qemu-iotests/
|
||||||
|
- ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048
|
||||||
|
052 063 077 086 101 104 106 113 147 148 150 151 152 157 159 160
|
||||||
|
163 170 171 183 184 192 194 197 205 208 215 221 222 226 227 236
|
||||||
|
- ./check -qcow2 001 002 003 004 005 007 008 009 010 011 012 013 017 018 019
|
||||||
|
020 021 022 024 025 027 028 029 031 032 033 034 035 036 037 038
|
||||||
|
039 040 042 043 046 047 048 049 050 051 052 053 054 056 057 058
|
||||||
|
060 061 062 063 065 066 067 068 069 071 072 073 074 079 080 082
|
||||||
|
085 086 089 090 091 095 096 097 098 099 102 103 104 105 107 108
|
||||||
|
110 111 114 117 120 122 124 126 127 129 130 132 133 134 137 138
|
||||||
|
139 140 141 142 143 144 145 147 150 151 152 154 155 156 157 158
|
||||||
|
161 165 170 172 174 176 177 179 184 186 187 190 192 194 195 196
|
||||||
|
197 200 202 203 205 208 209 214 215 216 217 218 222 226 227 229 234
|
||||||
|
|
||||||
|
build-user:
|
||||||
|
script:
|
||||||
|
- ./configure --enable-werror --disable-system --disable-guest-agent
|
||||||
|
--disable-capstone --disable-slirp --disable-fdt
|
||||||
|
- make -j2
|
||||||
|
- make run-tcg-tests-i386-linux-user run-tcg-tests-x86_64-linux-user
|
||||||
|
|
||||||
|
build-clang:
|
||||||
|
script:
|
||||||
|
- apt-get install -y -qq clang libsdl2-dev
|
||||||
|
xfslibs-dev libiscsi-dev libnfs-dev libseccomp-dev gnutls-dev librbd-dev
|
||||||
|
- ./configure --cc=clang --cxx=clang++ --enable-werror
|
||||||
|
--target-list="alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu
|
||||||
|
ppc-softmmu s390x-softmmu x86_64-softmmu arm-linux-user"
|
||||||
|
- make -j2
|
||||||
|
- make -j2 check
|
|
@ -1,45 +1,54 @@
|
||||||
[submodule "roms/seabios"]
|
[submodule "roms/seabios"]
|
||||||
path = roms/seabios
|
path = roms/seabios
|
||||||
url = git://git.qemu-project.org/seabios.git/
|
url = https://git.qemu.org/git/seabios.git/
|
||||||
[submodule "roms/SLOF"]
|
[submodule "roms/SLOF"]
|
||||||
path = roms/SLOF
|
path = roms/SLOF
|
||||||
url = git://git.qemu-project.org/SLOF.git
|
url = https://git.qemu.org/git/SLOF.git
|
||||||
[submodule "roms/ipxe"]
|
[submodule "roms/ipxe"]
|
||||||
path = roms/ipxe
|
path = roms/ipxe
|
||||||
url = git://git.qemu-project.org/ipxe.git
|
url = https://git.qemu.org/git/ipxe.git
|
||||||
[submodule "roms/openbios"]
|
[submodule "roms/openbios"]
|
||||||
path = roms/openbios
|
path = roms/openbios
|
||||||
url = git://git.qemu-project.org/openbios.git
|
url = https://git.qemu.org/git/openbios.git
|
||||||
[submodule "roms/openhackware"]
|
[submodule "roms/openhackware"]
|
||||||
path = roms/openhackware
|
path = roms/openhackware
|
||||||
url = git://git.qemu-project.org/openhackware.git
|
url = https://git.qemu.org/git/openhackware.git
|
||||||
[submodule "roms/qemu-palcode"]
|
[submodule "roms/qemu-palcode"]
|
||||||
path = roms/qemu-palcode
|
path = roms/qemu-palcode
|
||||||
url = git://git.qemu.org/qemu-palcode.git
|
url = https://git.qemu.org/git/qemu-palcode.git
|
||||||
[submodule "roms/sgabios"]
|
[submodule "roms/sgabios"]
|
||||||
path = roms/sgabios
|
path = roms/sgabios
|
||||||
url = git://git.qemu-project.org/sgabios.git
|
url = https://git.qemu.org/git/sgabios.git
|
||||||
[submodule "dtc"]
|
[submodule "dtc"]
|
||||||
path = dtc
|
path = dtc
|
||||||
url = git://git.qemu-project.org/dtc.git
|
url = https://git.qemu.org/git/dtc.git
|
||||||
[submodule "roms/u-boot"]
|
[submodule "roms/u-boot"]
|
||||||
path = roms/u-boot
|
path = roms/u-boot
|
||||||
url = git://git.qemu-project.org/u-boot.git
|
url = https://git.qemu.org/git/u-boot.git
|
||||||
[submodule "roms/skiboot"]
|
[submodule "roms/skiboot"]
|
||||||
path = roms/skiboot
|
path = roms/skiboot
|
||||||
url = git://git.qemu.org/skiboot.git
|
url = https://git.qemu.org/git/skiboot.git
|
||||||
[submodule "roms/QemuMacDrivers"]
|
[submodule "roms/QemuMacDrivers"]
|
||||||
path = roms/QemuMacDrivers
|
path = roms/QemuMacDrivers
|
||||||
url = git://git.qemu.org/QemuMacDrivers.git
|
url = https://git.qemu.org/git/QemuMacDrivers.git
|
||||||
[submodule "ui/keycodemapdb"]
|
[submodule "ui/keycodemapdb"]
|
||||||
path = ui/keycodemapdb
|
path = ui/keycodemapdb
|
||||||
url = git://git.qemu.org/keycodemapdb.git
|
url = https://git.qemu.org/git/keycodemapdb.git
|
||||||
[submodule "capstone"]
|
[submodule "capstone"]
|
||||||
path = capstone
|
path = capstone
|
||||||
url = git://git.qemu.org/capstone.git
|
url = https://git.qemu.org/git/capstone.git
|
||||||
[submodule "roms/seabios-hppa"]
|
[submodule "roms/seabios-hppa"]
|
||||||
path = roms/seabios-hppa
|
path = roms/seabios-hppa
|
||||||
url = git://github.com/hdeller/seabios-hppa.git
|
url = https://github.com/hdeller/seabios-hppa.git
|
||||||
[submodule "roms/u-boot-sam460ex"]
|
[submodule "roms/u-boot-sam460ex"]
|
||||||
path = roms/u-boot-sam460ex
|
path = roms/u-boot-sam460ex
|
||||||
url = git://git.qemu.org/u-boot-sam460ex.git
|
url = https://git.qemu.org/git/u-boot-sam460ex.git
|
||||||
|
[submodule "tests/fp/berkeley-testfloat-3"]
|
||||||
|
path = tests/fp/berkeley-testfloat-3
|
||||||
|
url = https://github.com/cota/berkeley-testfloat-3
|
||||||
|
[submodule "tests/fp/berkeley-softfloat-3"]
|
||||||
|
path = tests/fp/berkeley-softfloat-3
|
||||||
|
url = https://github.com/cota/berkeley-softfloat-3
|
||||||
|
[submodule "roms/edk2"]
|
||||||
|
path = roms/edk2
|
||||||
|
url = https://github.com/tianocore/edk2.git
|
||||||
|
|
11
.mailmap
11
.mailmap
|
@ -12,8 +12,12 @@ Fabrice Bellard <fabrice@bellard.org> bellard <bellard@c046a42c-6fe2-441c-8c8c-7
|
||||||
James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com>
|
James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com>
|
||||||
Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
|
Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||||
Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
|
Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||||
Paul Burton <paul.burton@mips.com> <paul.burton@imgtec.com>
|
Yongbok Kim <yongbok.kim@mips.com> <yongbok.kim@imgtec.com>
|
||||||
Paul Burton <paul.burton@mips.com> <paul@archlinuxmips.org>
|
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>
|
||||||
Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
|
Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||||
malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162>
|
malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||||
|
|
||||||
|
@ -30,5 +34,6 @@ Justin Terry (VM) <juterry@microsoft.com> Justin Terry (VM) via Qemu-devel <qemu
|
||||||
|
|
||||||
|
|
||||||
# Also list preferred name forms where people have changed their
|
# Also list preferred name forms where people have changed their
|
||||||
# git author config
|
# git author config, or had utf8/latin1 encoding issues.
|
||||||
Daniel P. Berrangé <berrange@redhat.com>
|
Daniel P. Berrangé <berrange@redhat.com>
|
||||||
|
Reimar Döffinger <Reimar.Doeffinger@gmx.de>
|
||||||
|
|
|
@ -7,10 +7,11 @@ env:
|
||||||
matrix:
|
matrix:
|
||||||
- IMAGE=debian-amd64
|
- IMAGE=debian-amd64
|
||||||
TARGET_LIST=x86_64-softmmu,x86_64-linux-user
|
TARGET_LIST=x86_64-softmmu,x86_64-linux-user
|
||||||
- IMAGE=debian-win32-cross
|
# currently disabled as the mxe.cc repos are down
|
||||||
TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu
|
# - IMAGE=debian-win32-cross
|
||||||
- IMAGE=debian-win64-cross
|
# TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu
|
||||||
TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu
|
# - IMAGE=debian-win64-cross
|
||||||
|
# TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu
|
||||||
- IMAGE=debian-armel-cross
|
- IMAGE=debian-armel-cross
|
||||||
TARGET_LIST=arm-softmmu,arm-linux-user,armeb-linux-user
|
TARGET_LIST=arm-softmmu,arm-linux-user,armeb-linux-user
|
||||||
- IMAGE=debian-armhf-cross
|
- IMAGE=debian-armhf-cross
|
||||||
|
|
22
COPYING.LIB
22
COPYING.LIB
|
@ -1,8 +1,8 @@
|
||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
Version 2.1, February 1999
|
Version 2.1, February 1999
|
||||||
|
|
||||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||||
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
of this license document, but changing it is not allowed.
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
as the successor of the GNU Library Public License, version 2, hence
|
as the successor of the GNU Library Public License, version 2, hence
|
||||||
the version number 2.1.]
|
the version number 2.1.]
|
||||||
|
|
||||||
Preamble
|
Preamble
|
||||||
|
|
||||||
The licenses for most software are designed to take away your
|
The licenses for most software are designed to take away your
|
||||||
freedom to share and change it. By contrast, the GNU General Public
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
@ -112,7 +112,7 @@ modification follow. Pay close attention to the difference between a
|
||||||
former contains code derived from the library, whereas the latter must
|
former contains code derived from the library, whereas the latter must
|
||||||
be combined with the library in order to run.
|
be combined with the library in order to run.
|
||||||
|
|
||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
0. This License Agreement applies to any software library or other
|
0. This License Agreement applies to any software library or other
|
||||||
|
@ -146,7 +146,7 @@ such a program is covered only if its contents constitute a work based
|
||||||
on the Library (independent of the use of the Library in a tool for
|
on the Library (independent of the use of the Library in a tool for
|
||||||
writing it). Whether that is true depends on what the Library does
|
writing it). Whether that is true depends on what the Library does
|
||||||
and what the program that uses the Library does.
|
and what the program that uses the Library does.
|
||||||
|
|
||||||
1. You may copy and distribute verbatim copies of the Library's
|
1. You may copy and distribute verbatim copies of the Library's
|
||||||
complete source code as you receive it, in any medium, provided that
|
complete source code as you receive it, in any medium, provided that
|
||||||
you conspicuously and appropriately publish on each copy an
|
you conspicuously and appropriately publish on each copy an
|
||||||
|
@ -432,7 +432,7 @@ decision will be guided by the two goals of preserving the free status
|
||||||
of all derivatives of our free software and of promoting the sharing
|
of all derivatives of our free software and of promoting the sharing
|
||||||
and reuse of software generally.
|
and reuse of software generally.
|
||||||
|
|
||||||
NO WARRANTY
|
NO WARRANTY
|
||||||
|
|
||||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||||
|
@ -455,7 +455,7 @@ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
DAMAGES.
|
DAMAGES.
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
How to Apply These Terms to Your New Libraries
|
How to Apply These Terms to Your New Libraries
|
||||||
|
|
||||||
|
@ -476,7 +476,7 @@ convey the exclusion of warranty; and each file should have at least the
|
||||||
This library is free software; you can redistribute it and/or
|
This library is free software; you can redistribute it and/or
|
||||||
modify it under the terms of the GNU Lesser General Public
|
modify it under the terms of the GNU Lesser General Public
|
||||||
License as published by the Free Software Foundation; either
|
License as published by the Free Software Foundation; either
|
||||||
version 2 of the License, or (at your option) any later version.
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
This library is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
@ -485,7 +485,7 @@ convey the exclusion of warranty; and each file should have at least the
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
You should have received a copy of the GNU Lesser General Public
|
||||||
License along with this library; if not, write to the Free Software
|
License along with this library; if not, write to the Free Software
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
@ -500,5 +500,3 @@ necessary. Here is a sample; alter the names:
|
||||||
Ty Coon, President of Vice
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
That's all there is to it!
|
That's all there is to it!
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
# These are "proxy" symbols used to pass config-host.mak values
|
||||||
|
# down to Kconfig. See also MINIKCONF_ARGS in the Makefile:
|
||||||
|
# these two need to be kept in sync.
|
||||||
|
|
||||||
|
config KVM
|
||||||
|
bool
|
||||||
|
|
||||||
|
config LINUX
|
||||||
|
bool
|
||||||
|
|
||||||
|
config OPENGL
|
||||||
|
bool
|
||||||
|
|
||||||
|
config X11
|
||||||
|
bool
|
||||||
|
|
||||||
|
config SPICE
|
||||||
|
bool
|
||||||
|
|
||||||
|
config IVSHMEM
|
||||||
|
bool
|
||||||
|
|
||||||
|
config TPM
|
||||||
|
bool
|
||||||
|
|
||||||
|
config VHOST_USER
|
||||||
|
bool
|
||||||
|
|
||||||
|
config XEN
|
||||||
|
bool
|
||||||
|
|
||||||
|
config VIRTFS
|
||||||
|
bool
|
||||||
|
|
||||||
|
config PVRDMA
|
||||||
|
bool
|
708
MAINTAINERS
708
MAINTAINERS
File diff suppressed because it is too large
Load Diff
413
Makefile
413
Makefile
|
@ -67,7 +67,7 @@ CONFIG_ALL=y
|
||||||
-include config-all-devices.mak
|
-include config-all-devices.mak
|
||||||
-include config-all-disas.mak
|
-include config-all-disas.mak
|
||||||
|
|
||||||
config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios
|
config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios $(SRC_PATH)/VERSION
|
||||||
@echo $@ is out-of-date, running configure
|
@echo $@ is out-of-date, running configure
|
||||||
@# TODO: The next lines include code which supports a smooth
|
@# TODO: The next lines include code which supports a smooth
|
||||||
@# transition from old configurations without config.status.
|
@# transition from old configurations without config.status.
|
||||||
|
@ -87,83 +87,41 @@ endif
|
||||||
|
|
||||||
include $(SRC_PATH)/rules.mak
|
include $(SRC_PATH)/rules.mak
|
||||||
|
|
||||||
|
# Create QEMU_PKGVERSION and FULL_VERSION strings
|
||||||
|
# If PKGVERSION is set, use that; otherwise get version and -dirty status from git
|
||||||
|
QEMU_PKGVERSION := $(if $(PKGVERSION),$(PKGVERSION),$(shell \
|
||||||
|
cd $(SRC_PATH); \
|
||||||
|
if test -e .git; then \
|
||||||
|
git describe --match 'v*' 2>/dev/null | tr -d '\n'; \
|
||||||
|
if ! git diff-index --quiet HEAD &>/dev/null; then \
|
||||||
|
echo "-dirty"; \
|
||||||
|
fi; \
|
||||||
|
fi))
|
||||||
|
|
||||||
|
# Either "version (pkgversion)", or just "version" if pkgversion not set
|
||||||
|
FULL_VERSION := $(if $(QEMU_PKGVERSION),$(VERSION) ($(QEMU_PKGVERSION)),$(VERSION))
|
||||||
|
|
||||||
GENERATED_FILES = qemu-version.h config-host.h qemu-options.def
|
GENERATED_FILES = qemu-version.h config-host.h qemu-options.def
|
||||||
GENERATED_FILES += qapi/qapi-builtin-types.h qapi/qapi-builtin-types.c
|
|
||||||
GENERATED_FILES += qapi/qapi-types.h qapi/qapi-types.c
|
GENERATED_QAPI_FILES = qapi/qapi-builtin-types.h qapi/qapi-builtin-types.c
|
||||||
GENERATED_FILES += qapi/qapi-types-block-core.h qapi/qapi-types-block-core.c
|
GENERATED_QAPI_FILES += qapi/qapi-types.h qapi/qapi-types.c
|
||||||
GENERATED_FILES += qapi/qapi-types-block.h qapi/qapi-types-block.c
|
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-types-%.h)
|
||||||
GENERATED_FILES += qapi/qapi-types-char.h qapi/qapi-types-char.c
|
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-types-%.c)
|
||||||
GENERATED_FILES += qapi/qapi-types-common.h qapi/qapi-types-common.c
|
GENERATED_QAPI_FILES += qapi/qapi-builtin-visit.h qapi/qapi-builtin-visit.c
|
||||||
GENERATED_FILES += qapi/qapi-types-crypto.h qapi/qapi-types-crypto.c
|
GENERATED_QAPI_FILES += qapi/qapi-visit.h qapi/qapi-visit.c
|
||||||
GENERATED_FILES += qapi/qapi-types-introspect.h qapi/qapi-types-introspect.c
|
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.h)
|
||||||
GENERATED_FILES += qapi/qapi-types-job.h qapi/qapi-types-job.c
|
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.c)
|
||||||
GENERATED_FILES += qapi/qapi-types-migration.h qapi/qapi-types-migration.c
|
GENERATED_QAPI_FILES += qapi/qapi-commands.h qapi/qapi-commands.c
|
||||||
GENERATED_FILES += qapi/qapi-types-misc.h qapi/qapi-types-misc.c
|
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.h)
|
||||||
GENERATED_FILES += qapi/qapi-types-net.h qapi/qapi-types-net.c
|
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.c)
|
||||||
GENERATED_FILES += qapi/qapi-types-rocker.h qapi/qapi-types-rocker.c
|
GENERATED_QAPI_FILES += qapi/qapi-emit-events.h qapi/qapi-emit-events.c
|
||||||
GENERATED_FILES += qapi/qapi-types-run-state.h qapi/qapi-types-run-state.c
|
GENERATED_QAPI_FILES += qapi/qapi-events.h qapi/qapi-events.c
|
||||||
GENERATED_FILES += qapi/qapi-types-sockets.h qapi/qapi-types-sockets.c
|
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.h)
|
||||||
GENERATED_FILES += qapi/qapi-types-tpm.h qapi/qapi-types-tpm.c
|
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.c)
|
||||||
GENERATED_FILES += qapi/qapi-types-trace.h qapi/qapi-types-trace.c
|
GENERATED_QAPI_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h
|
||||||
GENERATED_FILES += qapi/qapi-types-transaction.h qapi/qapi-types-transaction.c
|
GENERATED_QAPI_FILES += qapi/qapi-doc.texi
|
||||||
GENERATED_FILES += qapi/qapi-types-ui.h qapi/qapi-types-ui.c
|
|
||||||
GENERATED_FILES += qapi/qapi-builtin-visit.h qapi/qapi-builtin-visit.c
|
GENERATED_FILES += $(GENERATED_QAPI_FILES)
|
||||||
GENERATED_FILES += qapi/qapi-visit.h qapi/qapi-visit.c
|
|
||||||
GENERATED_FILES += qapi/qapi-visit-block-core.h qapi/qapi-visit-block-core.c
|
|
||||||
GENERATED_FILES += qapi/qapi-visit-block.h qapi/qapi-visit-block.c
|
|
||||||
GENERATED_FILES += qapi/qapi-visit-char.h qapi/qapi-visit-char.c
|
|
||||||
GENERATED_FILES += qapi/qapi-visit-common.h qapi/qapi-visit-common.c
|
|
||||||
GENERATED_FILES += qapi/qapi-visit-crypto.h qapi/qapi-visit-crypto.c
|
|
||||||
GENERATED_FILES += qapi/qapi-visit-introspect.h qapi/qapi-visit-introspect.c
|
|
||||||
GENERATED_FILES += qapi/qapi-visit-job.h qapi/qapi-visit-job.c
|
|
||||||
GENERATED_FILES += qapi/qapi-visit-migration.h qapi/qapi-visit-migration.c
|
|
||||||
GENERATED_FILES += qapi/qapi-visit-misc.h qapi/qapi-visit-misc.c
|
|
||||||
GENERATED_FILES += qapi/qapi-visit-net.h qapi/qapi-visit-net.c
|
|
||||||
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-job.h qapi/qapi-commands-job.c
|
|
||||||
GENERATED_FILES += qapi/qapi-commands-migration.h qapi/qapi-commands-migration.c
|
|
||||||
GENERATED_FILES += qapi/qapi-commands-misc.h qapi/qapi-commands-misc.c
|
|
||||||
GENERATED_FILES += qapi/qapi-commands-net.h qapi/qapi-commands-net.c
|
|
||||||
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-job.h qapi/qapi-events-job.c
|
|
||||||
GENERATED_FILES += qapi/qapi-events-migration.h qapi/qapi-events-migration.c
|
|
||||||
GENERATED_FILES += qapi/qapi-events-misc.h qapi/qapi-events-misc.c
|
|
||||||
GENERATED_FILES += qapi/qapi-events-net.h qapi/qapi-events-net.c
|
|
||||||
GENERATED_FILES += qapi/qapi-events-rocker.h qapi/qapi-events-rocker.c
|
|
||||||
GENERATED_FILES += qapi/qapi-events-run-state.h qapi/qapi-events-run-state.c
|
|
||||||
GENERATED_FILES += qapi/qapi-events-sockets.h qapi/qapi-events-sockets.c
|
|
||||||
GENERATED_FILES += qapi/qapi-events-tpm.h qapi/qapi-events-tpm.c
|
|
||||||
GENERATED_FILES += qapi/qapi-events-trace.h qapi/qapi-events-trace.c
|
|
||||||
GENERATED_FILES += qapi/qapi-events-transaction.h qapi/qapi-events-transaction.c
|
|
||||||
GENERATED_FILES += qapi/qapi-events-ui.h qapi/qapi-events-ui.c
|
|
||||||
GENERATED_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h
|
|
||||||
GENERATED_FILES += qapi/qapi-doc.texi
|
|
||||||
|
|
||||||
GENERATED_FILES += trace/generated-tcg-tracers.h
|
GENERATED_FILES += trace/generated-tcg-tracers.h
|
||||||
|
|
||||||
|
@ -201,7 +159,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py")
|
||||||
|
|
||||||
%/trace.h: %/trace.h-timestamp
|
%/trace.h: %/trace.h-timestamp
|
||||||
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
||||||
%/trace.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y)
|
%/trace.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak
|
||||||
$(call quiet-command,$(TRACETOOL) \
|
$(call quiet-command,$(TRACETOOL) \
|
||||||
--group=$(call trace-group-name,$@) \
|
--group=$(call trace-group-name,$@) \
|
||||||
--format=h \
|
--format=h \
|
||||||
|
@ -210,7 +168,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py")
|
||||||
|
|
||||||
%/trace.c: %/trace.c-timestamp
|
%/trace.c: %/trace.c-timestamp
|
||||||
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
||||||
%/trace.c-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y)
|
%/trace.c-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak
|
||||||
$(call quiet-command,$(TRACETOOL) \
|
$(call quiet-command,$(TRACETOOL) \
|
||||||
--group=$(call trace-group-name,$@) \
|
--group=$(call trace-group-name,$@) \
|
||||||
--format=c \
|
--format=c \
|
||||||
|
@ -219,7 +177,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py")
|
||||||
|
|
||||||
%/trace-ust.h: %/trace-ust.h-timestamp
|
%/trace-ust.h: %/trace-ust.h-timestamp
|
||||||
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
||||||
%/trace-ust.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y)
|
%/trace-ust.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak
|
||||||
$(call quiet-command,$(TRACETOOL) \
|
$(call quiet-command,$(TRACETOOL) \
|
||||||
--group=$(call trace-group-name,$@) \
|
--group=$(call trace-group-name,$@) \
|
||||||
--format=ust-events-h \
|
--format=ust-events-h \
|
||||||
|
@ -243,7 +201,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py")
|
||||||
|
|
||||||
trace-root.h: trace-root.h-timestamp
|
trace-root.h: trace-root.h-timestamp
|
||||||
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
||||||
trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
|
trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak
|
||||||
$(call quiet-command,$(TRACETOOL) \
|
$(call quiet-command,$(TRACETOOL) \
|
||||||
--group=root \
|
--group=root \
|
||||||
--format=h \
|
--format=h \
|
||||||
|
@ -252,7 +210,7 @@ trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
|
||||||
|
|
||||||
trace-root.c: trace-root.c-timestamp
|
trace-root.c: trace-root.c-timestamp
|
||||||
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
||||||
trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
|
trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak
|
||||||
$(call quiet-command,$(TRACETOOL) \
|
$(call quiet-command,$(TRACETOOL) \
|
||||||
--group=root \
|
--group=root \
|
||||||
--format=c \
|
--format=c \
|
||||||
|
@ -261,7 +219,7 @@ trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
|
||||||
|
|
||||||
trace-ust-root.h: trace-ust-root.h-timestamp
|
trace-ust-root.h: trace-ust-root.h-timestamp
|
||||||
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
||||||
trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
|
trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak
|
||||||
$(call quiet-command,$(TRACETOOL) \
|
$(call quiet-command,$(TRACETOOL) \
|
||||||
--group=root \
|
--group=root \
|
||||||
--format=ust-events-h \
|
--format=ust-events-h \
|
||||||
|
@ -270,7 +228,7 @@ trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
|
||||||
|
|
||||||
trace-ust-all.h: trace-ust-all.h-timestamp
|
trace-ust-all.h: trace-ust-all.h-timestamp
|
||||||
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
||||||
trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y)
|
trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/config-host.mak
|
||||||
$(call quiet-command,$(TRACETOOL) \
|
$(call quiet-command,$(TRACETOOL) \
|
||||||
--group=all \
|
--group=all \
|
||||||
--format=ust-events-h \
|
--format=ust-events-h \
|
||||||
|
@ -279,7 +237,7 @@ trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y)
|
||||||
|
|
||||||
trace-ust-all.c: trace-ust-all.c-timestamp
|
trace-ust-all.c: trace-ust-all.c-timestamp
|
||||||
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
||||||
trace-ust-all.c-timestamp: $(trace-events-files) $(tracetool-y)
|
trace-ust-all.c-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/config-host.mak
|
||||||
$(call quiet-command,$(TRACETOOL) \
|
$(call quiet-command,$(TRACETOOL) \
|
||||||
--group=all \
|
--group=all \
|
||||||
--format=ust-events-c \
|
--format=ust-events-c \
|
||||||
|
@ -357,22 +315,26 @@ DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
|
||||||
DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7
|
DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7
|
||||||
DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7
|
DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7
|
||||||
DOCS+=docs/qemu-block-drivers.7
|
DOCS+=docs/qemu-block-drivers.7
|
||||||
|
DOCS+=docs/qemu-cpu-models.7
|
||||||
ifdef CONFIG_VIRTFS
|
ifdef CONFIG_VIRTFS
|
||||||
DOCS+=fsdev/virtfs-proxy-helper.1
|
DOCS+=fsdev/virtfs-proxy-helper.1
|
||||||
endif
|
endif
|
||||||
|
ifdef CONFIG_TRACE_SYSTEMTAP
|
||||||
|
DOCS+=scripts/qemu-trace-stap.1
|
||||||
|
endif
|
||||||
else
|
else
|
||||||
DOCS=
|
DOCS=
|
||||||
endif
|
endif
|
||||||
|
|
||||||
SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet) BUILD_DIR=$(BUILD_DIR)
|
SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet) BUILD_DIR=$(BUILD_DIR)
|
||||||
SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS))
|
SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(filter %-softmmu, $(TARGET_DIRS)))
|
||||||
SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %-config-devices.mak.d, $(TARGET_DIRS))
|
SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %.d, $(SUBDIR_DEVICES_MAK))
|
||||||
|
|
||||||
ifeq ($(SUBDIR_DEVICES_MAK),)
|
ifeq ($(SUBDIR_DEVICES_MAK),)
|
||||||
config-all-devices.mak:
|
config-all-devices.mak: config-host.mak
|
||||||
$(call quiet-command,echo '# no devices' > $@,"GEN","$@")
|
$(call quiet-command,echo '# no devices' > $@,"GEN","$@")
|
||||||
else
|
else
|
||||||
config-all-devices.mak: $(SUBDIR_DEVICES_MAK)
|
config-all-devices.mak: $(SUBDIR_DEVICES_MAK) config-host.mak
|
||||||
$(call quiet-command, sed -n \
|
$(call quiet-command, sed -n \
|
||||||
's|^\([^=]*\)=\(.*\)$$|\1:=$$(findstring y,$$(\1)\2)|p' \
|
's|^\([^=]*\)=\(.*\)$$|\1:=$$(findstring y,$$(\1)\2)|p' \
|
||||||
$(SUBDIR_DEVICES_MAK) | sort -u > $@, \
|
$(SUBDIR_DEVICES_MAK) | sort -u > $@, \
|
||||||
|
@ -381,9 +343,27 @@ endif
|
||||||
|
|
||||||
-include $(SUBDIR_DEVICES_MAK_DEP)
|
-include $(SUBDIR_DEVICES_MAK_DEP)
|
||||||
|
|
||||||
%/config-devices.mak: default-configs/%.mak $(SRC_PATH)/scripts/make_device_config.sh
|
# This has to be kept in sync with Kconfig.host.
|
||||||
$(call quiet-command, \
|
MINIKCONF_ARGS = \
|
||||||
$(SHELL) $(SRC_PATH)/scripts/make_device_config.sh $< $*-config-devices.mak.d $@ > $@.tmp,"GEN","$@.tmp")
|
$(CONFIG_MINIKCONF_MODE) \
|
||||||
|
$@ $*-config.devices.mak.d $< $(MINIKCONF_INPUTS) \
|
||||||
|
CONFIG_KVM=$(CONFIG_KVM) \
|
||||||
|
CONFIG_SPICE=$(CONFIG_SPICE) \
|
||||||
|
CONFIG_IVSHMEM=$(CONFIG_IVSHMEM) \
|
||||||
|
CONFIG_TPM=$(CONFIG_TPM) \
|
||||||
|
CONFIG_XEN=$(CONFIG_XEN) \
|
||||||
|
CONFIG_OPENGL=$(CONFIG_OPENGL) \
|
||||||
|
CONFIG_X11=$(CONFIG_X11) \
|
||||||
|
CONFIG_VHOST_USER=$(CONFIG_VHOST_USER) \
|
||||||
|
CONFIG_VIRTFS=$(CONFIG_VIRTFS) \
|
||||||
|
CONFIG_LINUX=$(CONFIG_LINUX) \
|
||||||
|
CONFIG_PVRDMA=$(CONFIG_PVRDMA)
|
||||||
|
|
||||||
|
MINIKCONF_INPUTS = $(SRC_PATH)/Kconfig.host $(SRC_PATH)/hw/Kconfig
|
||||||
|
MINIKCONF = $(PYTHON) $(SRC_PATH)/scripts/minikconf.py \
|
||||||
|
|
||||||
|
$(SUBDIR_DEVICES_MAK): %/config-devices.mak: default-configs/%.mak $(MINIKCONF_INPUTS) $(BUILD_DIR)/config-host.mak
|
||||||
|
$(call quiet-command, $(MINIKCONF) $(MINIKCONF_ARGS) > $@.tmp, "GEN", "$@.tmp")
|
||||||
$(call quiet-command, if test -f $@; then \
|
$(call quiet-command, if test -f $@; then \
|
||||||
if cmp -s $@.old $@; then \
|
if cmp -s $@.old $@; then \
|
||||||
mv $@.tmp $@; \
|
mv $@.tmp $@; \
|
||||||
|
@ -411,11 +391,14 @@ endif
|
||||||
|
|
||||||
dummy := $(call unnest-vars,, \
|
dummy := $(call unnest-vars,, \
|
||||||
stub-obj-y \
|
stub-obj-y \
|
||||||
|
authz-obj-y \
|
||||||
chardev-obj-y \
|
chardev-obj-y \
|
||||||
util-obj-y \
|
util-obj-y \
|
||||||
qga-obj-y \
|
qga-obj-y \
|
||||||
|
elf2dmp-obj-y \
|
||||||
ivshmem-client-obj-y \
|
ivshmem-client-obj-y \
|
||||||
ivshmem-server-obj-y \
|
ivshmem-server-obj-y \
|
||||||
|
rdmacm-mux-obj-y \
|
||||||
libvhost-user-obj-y \
|
libvhost-user-obj-y \
|
||||||
vhost-user-scsi-obj-y \
|
vhost-user-scsi-obj-y \
|
||||||
vhost-user-blk-obj-y \
|
vhost-user-blk-obj-y \
|
||||||
|
@ -436,27 +419,12 @@ dummy := $(call unnest-vars,, \
|
||||||
|
|
||||||
include $(SRC_PATH)/tests/Makefile.include
|
include $(SRC_PATH)/tests/Makefile.include
|
||||||
|
|
||||||
all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules
|
all: $(DOCS) $(if $(BUILD_DOCS),sphinxdocs) $(TOOLS) $(HELPERS-y) recurse-all modules
|
||||||
|
|
||||||
qemu-version.h: FORCE
|
qemu-version.h: FORCE
|
||||||
$(call quiet-command, \
|
$(call quiet-command, \
|
||||||
(cd $(SRC_PATH); \
|
(printf '#define QEMU_PKGVERSION "$(QEMU_PKGVERSION)"\n'; \
|
||||||
if test -n "$(PKGVERSION)"; then \
|
printf '#define QEMU_FULL_VERSION "$(FULL_VERSION)"\n'; \
|
||||||
pkgvers="$(PKGVERSION)"; \
|
|
||||||
else \
|
|
||||||
if test -d .git; then \
|
|
||||||
pkgvers=$$(git rev-parse --short HEAD 2>/dev/null | tr -d '\n');\
|
|
||||||
if ! git diff --quiet HEAD >/dev/null 2>&1; then \
|
|
||||||
pkgvers="$${pkgvers}-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'; \
|
|
||||||
else \
|
|
||||||
printf '#define QEMU_FULL_VERSION QEMU_VERSION\n'; \
|
|
||||||
fi; \
|
|
||||||
) > $@.tmp)
|
) > $@.tmp)
|
||||||
$(call quiet-command, if ! cmp -s $@ $@.tmp; then \
|
$(call quiet-command, if ! cmp -s $@ $@.tmp; then \
|
||||||
mv $@.tmp $@; \
|
mv $@.tmp $@; \
|
||||||
|
@ -472,6 +440,7 @@ qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
|
||||||
SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
|
SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
|
||||||
SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES))
|
SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES))
|
||||||
|
|
||||||
|
$(SOFTMMU_SUBDIR_RULES): $(authz-obj-y)
|
||||||
$(SOFTMMU_SUBDIR_RULES): $(block-obj-y)
|
$(SOFTMMU_SUBDIR_RULES): $(block-obj-y)
|
||||||
$(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y)
|
$(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y)
|
||||||
$(SOFTMMU_SUBDIR_RULES): $(io-obj-y)
|
$(SOFTMMU_SUBDIR_RULES): $(io-obj-y)
|
||||||
|
@ -505,6 +474,9 @@ CAP_CFLAGS += -DCAPSTONE_HAS_X86
|
||||||
subdir-capstone: .git-submodule-status
|
subdir-capstone: .git-submodule-status
|
||||||
$(call quiet-command,$(MAKE) -C $(SRC_PATH)/capstone CAPSTONE_SHARED=no BUILDDIR="$(BUILD_DIR)/capstone" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(CAP_CFLAGS)" $(SUBDIR_MAKEFLAGS) $(BUILD_DIR)/capstone/$(LIBCAPSTONE))
|
$(call quiet-command,$(MAKE) -C $(SRC_PATH)/capstone CAPSTONE_SHARED=no BUILDDIR="$(BUILD_DIR)/capstone" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(CAP_CFLAGS)" $(SUBDIR_MAKEFLAGS) $(BUILD_DIR)/capstone/$(LIBCAPSTONE))
|
||||||
|
|
||||||
|
subdir-slirp: .git-submodule-status
|
||||||
|
$(call quiet-command,$(MAKE) -C $(SRC_PATH)/slirp BUILD_DIR="$(BUILD_DIR)/slirp" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(QEMU_CFLAGS)")
|
||||||
|
|
||||||
$(SUBDIR_RULES): libqemuutil.a $(common-obj-y) $(chardev-obj-y) \
|
$(SUBDIR_RULES): libqemuutil.a $(common-obj-y) $(chardev-obj-y) \
|
||||||
$(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY))
|
$(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY))
|
||||||
|
|
||||||
|
@ -526,7 +498,7 @@ Makefile: $(version-obj-y)
|
||||||
# Build libraries
|
# Build libraries
|
||||||
|
|
||||||
libqemuutil.a: $(util-obj-y) $(trace-obj-y) $(stub-obj-y)
|
libqemuutil.a: $(util-obj-y) $(trace-obj-y) $(stub-obj-y)
|
||||||
libvhost-user.a: $(libvhost-user-obj-y)
|
libvhost-user.a: $(libvhost-user-obj-y) $(util-obj-y) $(stub-obj-y)
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
|
@ -534,18 +506,20 @@ COMMON_LDADDS = libqemuutil.a
|
||||||
|
|
||||||
qemu-img.o: qemu-img-cmds.h
|
qemu-img.o: qemu-img-cmds.h
|
||||||
|
|
||||||
qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
|
qemu-img$(EXESUF): qemu-img.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
|
||||||
qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
|
qemu-nbd$(EXESUF): qemu-nbd.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
|
||||||
qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
|
qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
|
||||||
|
|
||||||
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
|
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
|
||||||
|
|
||||||
qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $(COMMON_LDADDS)
|
qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $(COMMON_LDADDS)
|
||||||
|
|
||||||
|
qemu-edid$(EXESUF): qemu-edid.o hw/display/edid-generate.o $(COMMON_LDADDS)
|
||||||
|
|
||||||
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS)
|
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS)
|
||||||
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
|
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
|
||||||
|
|
||||||
scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
|
scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(authz-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
|
||||||
ifdef CONFIG_MPATH
|
ifdef CONFIG_MPATH
|
||||||
scsi/qemu-pr-helper$(EXESUF): LIBS += -ludev -lmultipath -lmpathpersist
|
scsi/qemu-pr-helper$(EXESUF): LIBS += -ludev -lmultipath -lmpathpersist
|
||||||
endif
|
endif
|
||||||
|
@ -579,100 +553,10 @@ qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-p
|
||||||
"GEN","$(@:%-timestamp=%)")
|
"GEN","$(@:%-timestamp=%)")
|
||||||
@>$@
|
@>$@
|
||||||
|
|
||||||
qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json $(SRC_PATH)/qapi/common.json \
|
qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json \
|
||||||
$(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \
|
$(QAPI_MODULES:%=$(SRC_PATH)/qapi/%.json)
|
||||||
$(SRC_PATH)/qapi/char.json \
|
|
||||||
$(SRC_PATH)/qapi/crypto.json \
|
|
||||||
$(SRC_PATH)/qapi/introspect.json \
|
|
||||||
$(SRC_PATH)/qapi/job.json \
|
|
||||||
$(SRC_PATH)/qapi/migration.json \
|
|
||||||
$(SRC_PATH)/qapi/misc.json \
|
|
||||||
$(SRC_PATH)/qapi/net.json \
|
|
||||||
$(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
|
|
||||||
|
|
||||||
qapi/qapi-builtin-types.c qapi/qapi-builtin-types.h \
|
$(GENERATED_QAPI_FILES): qapi-gen-timestamp ;
|
||||||
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-job.c qapi/qapi-types-job.h \
|
|
||||||
qapi/qapi-types-migration.c qapi/qapi-types-migration.h \
|
|
||||||
qapi/qapi-types-misc.c qapi/qapi-types-misc.h \
|
|
||||||
qapi/qapi-types-net.c qapi/qapi-types-net.h \
|
|
||||||
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-job.c qapi/qapi-visit-job.h \
|
|
||||||
qapi/qapi-visit-migration.c qapi/qapi-visit-migration.h \
|
|
||||||
qapi/qapi-visit-misc.c qapi/qapi-visit-misc.h \
|
|
||||||
qapi/qapi-visit-net.c qapi/qapi-visit-net.h \
|
|
||||||
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-job.c qapi/qapi-commands-job.h \
|
|
||||||
qapi/qapi-commands-migration.c qapi/qapi-commands-migration.h \
|
|
||||||
qapi/qapi-commands-misc.c qapi/qapi-commands-misc.h \
|
|
||||||
qapi/qapi-commands-net.c qapi/qapi-commands-net.h \
|
|
||||||
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-job.c qapi/qapi-events-job.h \
|
|
||||||
qapi/qapi-events-migration.c qapi/qapi-events-migration.h \
|
|
||||||
qapi/qapi-events-misc.c qapi/qapi-events-misc.h \
|
|
||||||
qapi/qapi-events-net.c qapi/qapi-events-net.h \
|
|
||||||
qapi/qapi-events-rocker.c qapi/qapi-events-rocker.h \
|
|
||||||
qapi/qapi-events-run-state.c qapi/qapi-events-run-state.h \
|
|
||||||
qapi/qapi-events-sockets.c qapi/qapi-events-sockets.h \
|
|
||||||
qapi/qapi-events-tpm.c qapi/qapi-events-tpm.h \
|
|
||||||
qapi/qapi-events-trace.c qapi/qapi-events-trace.h \
|
|
||||||
qapi/qapi-events-transaction.c qapi/qapi-events-transaction.h \
|
|
||||||
qapi/qapi-events-ui.c qapi/qapi-events-ui.h \
|
|
||||||
qapi/qapi-introspect.h qapi/qapi-introspect.c \
|
|
||||||
qapi/qapi-doc.texi: \
|
|
||||||
qapi-gen-timestamp ;
|
|
||||||
qapi-gen-timestamp: $(qapi-modules) $(qapi-py)
|
qapi-gen-timestamp: $(qapi-modules) $(qapi-py)
|
||||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
|
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
|
||||||
-o "qapi" -b $<, \
|
-o "qapi" -b $<, \
|
||||||
|
@ -707,6 +591,10 @@ ifneq ($(EXESUF),)
|
||||||
qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI)
|
qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
elf2dmp$(EXESUF): LIBS += $(CURL_LIBS)
|
||||||
|
elf2dmp$(EXESUF): $(elf2dmp-obj-y)
|
||||||
|
$(call LINK, $^)
|
||||||
|
|
||||||
ifdef CONFIG_IVSHMEM
|
ifdef CONFIG_IVSHMEM
|
||||||
ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS)
|
ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS)
|
||||||
$(call LINK, $^)
|
$(call LINK, $^)
|
||||||
|
@ -718,6 +606,10 @@ vhost-user-scsi$(EXESUF): $(vhost-user-scsi-obj-y) libvhost-user.a
|
||||||
vhost-user-blk$(EXESUF): $(vhost-user-blk-obj-y) libvhost-user.a
|
vhost-user-blk$(EXESUF): $(vhost-user-blk-obj-y) libvhost-user.a
|
||||||
$(call LINK, $^)
|
$(call LINK, $^)
|
||||||
|
|
||||||
|
rdmacm-mux$(EXESUF): LIBS += "-libumad"
|
||||||
|
rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS)
|
||||||
|
$(call LINK, $^)
|
||||||
|
|
||||||
module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak
|
module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak
|
||||||
$(call quiet-command,$(PYTHON) $< $@ \
|
$(call quiet-command,$(PYTHON) $< $@ \
|
||||||
$(addprefix $(SRC_PATH)/,$(patsubst %.mo,%.c,$(block-obj-m))), \
|
$(addprefix $(SRC_PATH)/,$(patsubst %.mo,%.c,$(block-obj-m))), \
|
||||||
|
@ -736,7 +628,11 @@ clean:
|
||||||
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
|
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
|
||||||
rm -f qemu-options.def
|
rm -f qemu-options.def
|
||||||
rm -f *.msi
|
rm -f *.msi
|
||||||
find . \( -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f -exec rm {} +
|
find . \( -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f \
|
||||||
|
! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-aarch64.a \
|
||||||
|
! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-arm.a \
|
||||||
|
! -path ./roms/edk2/BaseTools/Source/Python/UPT/Dll/sqlite3.dll \
|
||||||
|
-exec rm {} +
|
||||||
rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
|
rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
|
||||||
rm -f fsdev/*.pod scsi/*.pod
|
rm -f fsdev/*.pod scsi/*.pod
|
||||||
rm -f qemu-img-cmds.h
|
rm -f qemu-img-cmds.h
|
||||||
|
@ -751,7 +647,7 @@ clean:
|
||||||
if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
|
if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
|
||||||
rm -f $$d/qemu-options.def; \
|
rm -f $$d/qemu-options.def; \
|
||||||
done
|
done
|
||||||
rm -f $(SUBDIR_DEVICES_MAK) config-all-devices.mak
|
rm -f config-all-devices.mak
|
||||||
|
|
||||||
VERSION ?= $(shell cat VERSION)
|
VERSION ?= $(shell cat VERSION)
|
||||||
|
|
||||||
|
@ -760,9 +656,26 @@ dist: qemu-$(VERSION).tar.bz2
|
||||||
qemu-%.tar.bz2:
|
qemu-%.tar.bz2:
|
||||||
$(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)"
|
$(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)"
|
||||||
|
|
||||||
|
# Sphinx does not allow building manuals into the same directory as
|
||||||
|
# the source files, so if we're doing an in-tree QEMU build we must
|
||||||
|
# build the manuals into a subdirectory (and then install them from
|
||||||
|
# there for 'make install'). For an out-of-tree build we can just
|
||||||
|
# use the docs/ subdirectory in the build tree as normal.
|
||||||
|
ifeq ($(realpath $(SRC_PATH)),$(realpath .))
|
||||||
|
MANUAL_BUILDDIR := docs/built
|
||||||
|
else
|
||||||
|
MANUAL_BUILDDIR := docs
|
||||||
|
endif
|
||||||
|
|
||||||
|
define clean-manual =
|
||||||
|
rm -rf $(MANUAL_BUILDDIR)/$1/_static
|
||||||
|
rm -f $(MANUAL_BUILDDIR)/$1/objects.inv $(MANUAL_BUILDDIR)/$1/searchindex.js $(MANUAL_BUILDDIR)/$1/*.html
|
||||||
|
endef
|
||||||
|
|
||||||
distclean: clean
|
distclean: clean
|
||||||
rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi
|
rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi
|
||||||
rm -f config-all-devices.mak config-all-disas.mak config.status
|
rm -f config-all-devices.mak config-all-disas.mak config.status
|
||||||
|
rm -f $(SUBDIR_DEVICES_MAK)
|
||||||
rm -f po/*.mo tests/qemu-iotests/common.env
|
rm -f po/*.mo tests/qemu-iotests/common.env
|
||||||
rm -f roms/seabios/config.mak roms/vgabios/config.mak
|
rm -f roms/seabios/config.mak roms/vgabios/config.mak
|
||||||
rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps
|
rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps
|
||||||
|
@ -778,29 +691,33 @@ distclean: clean
|
||||||
rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
|
rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
|
||||||
rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
|
rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
|
||||||
rm -f docs/qemu-block-drivers.7
|
rm -f docs/qemu-block-drivers.7
|
||||||
|
rm -f docs/qemu-cpu-models.7
|
||||||
|
rm -rf .doctrees
|
||||||
|
$(call clean-manual,devel)
|
||||||
|
$(call clean-manual,interop)
|
||||||
for d in $(TARGET_DIRS); do \
|
for d in $(TARGET_DIRS); do \
|
||||||
rm -rf $$d || exit 1 ; \
|
rm -rf $$d || exit 1 ; \
|
||||||
done
|
done
|
||||||
rm -Rf .sdk
|
rm -Rf .sdk
|
||||||
if test -f dtc/version_gen.h; then $(MAKE) $(DTC_MAKE_ARGS) clean; fi
|
if test -f dtc/version_gen.h; then $(MAKE) $(DTC_MAKE_ARGS) clean; fi
|
||||||
|
|
||||||
KEYMAPS=da en-gb et fr fr-ch is lt modifiers no pt-br sv \
|
KEYMAPS=da en-gb et fr fr-ch is lt no pt-br sv \
|
||||||
ar de en-us fi fr-be hr it lv nl pl ru th \
|
ar de en-us fi fr-be hr it lv nl pl ru th \
|
||||||
common de-ch es fo fr-ca hu ja mk nl-be pt sl tr \
|
de-ch es fo fr-ca hu ja mk pt sl tr \
|
||||||
bepo cz
|
bepo cz
|
||||||
|
|
||||||
ifdef INSTALL_BLOBS
|
ifdef INSTALL_BLOBS
|
||||||
BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
||||||
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
|
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
|
||||||
|
vgabios-ramfb.bin vgabios-bochs-display.bin \
|
||||||
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
|
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
|
||||||
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
|
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
|
||||||
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
|
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
|
||||||
efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \
|
efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \
|
||||||
efi-pcnet.rom efi-rtl8139.rom efi-virtio.rom \
|
efi-pcnet.rom efi-rtl8139.rom efi-virtio.rom \
|
||||||
efi-e1000e.rom efi-vmxnet3.rom \
|
efi-e1000e.rom efi-vmxnet3.rom \
|
||||||
qemu-icon.bmp qemu_logo_no_text.svg \
|
|
||||||
bamboo.dtb canyonlands.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
|
bamboo.dtb canyonlands.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
|
||||||
multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin \
|
multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin pvh.bin \
|
||||||
s390-ccw.img s390-netboot.img \
|
s390-ccw.img s390-netboot.img \
|
||||||
spapr-rtas.bin slof.bin skiboot.lid \
|
spapr-rtas.bin slof.bin skiboot.lid \
|
||||||
palcode-clipper \
|
palcode-clipper \
|
||||||
|
@ -811,7 +728,20 @@ else
|
||||||
BLOBS=
|
BLOBS=
|
||||||
endif
|
endif
|
||||||
|
|
||||||
install-doc: $(DOCS)
|
# Note that we manually filter-out the non-Sphinx documentation which
|
||||||
|
# is currently built into the docs/interop directory in the build tree.
|
||||||
|
define install-manual =
|
||||||
|
for d in $$(cd $(MANUAL_BUILDDIR) && find $1 -type d); do $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)/$$d"; done
|
||||||
|
for f in $$(cd $(MANUAL_BUILDDIR) && find $1 -type f -a '!' '(' -name 'qemu-*-qapi.*' -o -name 'qemu-*-ref.*' ')' ); do $(INSTALL_DATA) "$(MANUAL_BUILDDIR)/$$f" "$(DESTDIR)$(qemu_docdir)/$$f"; done
|
||||||
|
endef
|
||||||
|
|
||||||
|
# Note that we deliberately do not install the "devel" manual: it is
|
||||||
|
# for QEMU developers, and not interesting to our users.
|
||||||
|
.PHONY: install-sphinxdocs
|
||||||
|
install-sphinxdocs: sphinxdocs
|
||||||
|
$(call install-manual,interop)
|
||||||
|
|
||||||
|
install-doc: $(DOCS) install-sphinxdocs
|
||||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
|
$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
|
||||||
$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)"
|
$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)"
|
||||||
$(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)"
|
$(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)"
|
||||||
|
@ -823,11 +753,15 @@ ifdef CONFIG_POSIX
|
||||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7"
|
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7"
|
||||||
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
|
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
|
||||||
$(INSTALL_DATA) docs/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7"
|
$(INSTALL_DATA) docs/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7"
|
||||||
|
$(INSTALL_DATA) docs/qemu-cpu-models.7 "$(DESTDIR)$(mandir)/man7"
|
||||||
ifneq ($(TOOLS),)
|
ifneq ($(TOOLS),)
|
||||||
$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
|
$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
|
||||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
|
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
|
||||||
$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8"
|
$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8"
|
||||||
endif
|
endif
|
||||||
|
ifdef CONFIG_TRACE_SYSTEMTAP
|
||||||
|
$(INSTALL_DATA) scripts/qemu-trace-stap.1 "$(DESTDIR)$(mandir)/man1"
|
||||||
|
endif
|
||||||
ifneq (,$(findstring qemu-ga,$(TOOLS)))
|
ifneq (,$(findstring qemu-ga,$(TOOLS)))
|
||||||
$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
|
$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
|
||||||
$(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)"
|
$(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)"
|
||||||
|
@ -850,6 +784,7 @@ ifneq (,$(findstring qemu-ga,$(TOOLS)))
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ICON_SIZES=16x16 24x24 32x32 48x48 64x64 128x128 256x256 512x512
|
||||||
|
|
||||||
install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir
|
install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir
|
||||||
ifneq ($(TOOLS),)
|
ifneq ($(TOOLS),)
|
||||||
|
@ -866,11 +801,28 @@ endif
|
||||||
ifneq ($(HELPERS-y),)
|
ifneq ($(HELPERS-y),)
|
||||||
$(call install-prog,$(HELPERS-y),$(DESTDIR)$(libexecdir))
|
$(call install-prog,$(HELPERS-y),$(DESTDIR)$(libexecdir))
|
||||||
endif
|
endif
|
||||||
|
ifdef CONFIG_TRACE_SYSTEMTAP
|
||||||
|
$(INSTALL_PROG) "scripts/qemu-trace-stap" $(DESTDIR)$(bindir)
|
||||||
|
endif
|
||||||
ifneq ($(BLOBS),)
|
ifneq ($(BLOBS),)
|
||||||
set -e; for x in $(BLOBS); do \
|
set -e; for x in $(BLOBS); do \
|
||||||
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \
|
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \
|
||||||
done
|
done
|
||||||
endif
|
endif
|
||||||
|
for s in $(ICON_SIZES); do \
|
||||||
|
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
|
ifdef CONFIG_GTK
|
||||||
$(MAKE) -C po $@
|
$(MAKE) -C po $@
|
||||||
endif
|
endif
|
||||||
|
@ -940,6 +892,23 @@ docs/version.texi: $(SRC_PATH)/VERSION
|
||||||
%.pdf: %.texi docs/version.texi
|
%.pdf: %.texi docs/version.texi
|
||||||
$(call quiet-command,texi2pdf $(TEXI2PDFFLAGS) $< -o $@,"GEN","$@")
|
$(call quiet-command,texi2pdf $(TEXI2PDFFLAGS) $< -o $@,"GEN","$@")
|
||||||
|
|
||||||
|
# Sphinx builds all its documentation at once in one invocation
|
||||||
|
# and handles "don't rebuild things unless necessary" itself.
|
||||||
|
# The '.doctrees' files are cached information to speed this up.
|
||||||
|
.PHONY: sphinxdocs
|
||||||
|
sphinxdocs: $(MANUAL_BUILDDIR)/devel/index.html $(MANUAL_BUILDDIR)/interop/index.html
|
||||||
|
|
||||||
|
# Canned command to build a single manual
|
||||||
|
build-manual = $(call quiet-command,sphinx-build $(if $(V),,-q) -b html -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1")
|
||||||
|
# We assume all RST files in the manual's directory are used in it
|
||||||
|
manual-deps = $(wildcard $(SRC_PATH)/docs/$1/*.rst) $(SRC_PATH)/docs/$1/conf.py $(SRC_PATH)/docs/conf.py
|
||||||
|
|
||||||
|
$(MANUAL_BUILDDIR)/devel/index.html: $(call manual-deps,devel)
|
||||||
|
$(call build-manual,devel)
|
||||||
|
|
||||||
|
$(MANUAL_BUILDDIR)/interop/index.html: $(call manual-deps,interop)
|
||||||
|
$(call build-manual,interop)
|
||||||
|
|
||||||
qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
|
qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
|
||||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
|
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
|
||||||
|
|
||||||
|
@ -965,16 +934,19 @@ fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi
|
||||||
qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi
|
qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi
|
||||||
qemu-ga.8: qemu-ga.texi
|
qemu-ga.8: qemu-ga.texi
|
||||||
docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi
|
docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi
|
||||||
|
docs/qemu-cpu-models.7: docs/qemu-cpu-models.texi
|
||||||
|
scripts/qemu-trace-stap.1: scripts/qemu-trace-stap.texi
|
||||||
|
|
||||||
html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
|
html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html sphinxdocs
|
||||||
info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info
|
info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info
|
||||||
pdf: qemu-doc.pdf docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
|
pdf: qemu-doc.pdf docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
|
||||||
txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
|
txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
|
||||||
|
|
||||||
qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \
|
qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \
|
||||||
qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \
|
qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \
|
||||||
qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
|
qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
|
||||||
qemu-monitor-info.texi docs/qemu-block-drivers.texi
|
qemu-monitor-info.texi docs/qemu-block-drivers.texi \
|
||||||
|
docs/qemu-cpu-models.texi
|
||||||
|
|
||||||
docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \
|
docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \
|
||||||
docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \
|
docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \
|
||||||
|
@ -986,12 +958,15 @@ docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \
|
||||||
docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \
|
docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \
|
||||||
docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi
|
docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi
|
||||||
|
|
||||||
|
$(filter %.1 %.7 %.8,$(DOCS)): scripts/texi2pod.pl
|
||||||
|
|
||||||
# Reports/Analysis
|
# Reports/Analysis
|
||||||
|
|
||||||
%/coverage-report.html:
|
%/coverage-report.html:
|
||||||
@mkdir -p $*
|
@mkdir -p $*
|
||||||
$(call quiet-command,\
|
$(call quiet-command,\
|
||||||
gcovr -p --html --html-details -o $@, \
|
gcovr -r $(SRC_PATH) --object-directory $(BUILD_PATH) \
|
||||||
|
-p --html --html-details -o $@, \
|
||||||
"GEN", "coverage-report.html")
|
"GEN", "coverage-report.html")
|
||||||
|
|
||||||
.PHONY: coverage-report
|
.PHONY: coverage-report
|
||||||
|
|
|
@ -1,71 +1,19 @@
|
||||||
#######################################################################
|
#######################################################################
|
||||||
# Common libraries for tools and emulators
|
# Common libraries for tools and emulators
|
||||||
stub-obj-y = stubs/ crypto/
|
stub-obj-y = stubs/ util/ crypto/
|
||||||
util-obj-y = util/ qobject/ qapi/
|
util-obj-y = util/ qobject/ qapi/
|
||||||
util-obj-y += qapi/qapi-builtin-types.o
|
|
||||||
util-obj-y += qapi/qapi-types.o
|
|
||||||
util-obj-y += qapi/qapi-types-block-core.o
|
|
||||||
util-obj-y += qapi/qapi-types-block.o
|
|
||||||
util-obj-y += qapi/qapi-types-char.o
|
|
||||||
util-obj-y += qapi/qapi-types-common.o
|
|
||||||
util-obj-y += qapi/qapi-types-crypto.o
|
|
||||||
util-obj-y += qapi/qapi-types-introspect.o
|
|
||||||
util-obj-y += qapi/qapi-types-job.o
|
|
||||||
util-obj-y += qapi/qapi-types-migration.o
|
|
||||||
util-obj-y += qapi/qapi-types-misc.o
|
|
||||||
util-obj-y += qapi/qapi-types-net.o
|
|
||||||
util-obj-y += qapi/qapi-types-rocker.o
|
|
||||||
util-obj-y += qapi/qapi-types-run-state.o
|
|
||||||
util-obj-y += qapi/qapi-types-sockets.o
|
|
||||||
util-obj-y += qapi/qapi-types-tpm.o
|
|
||||||
util-obj-y += qapi/qapi-types-trace.o
|
|
||||||
util-obj-y += qapi/qapi-types-transaction.o
|
|
||||||
util-obj-y += qapi/qapi-types-ui.o
|
|
||||||
util-obj-y += qapi/qapi-builtin-visit.o
|
|
||||||
util-obj-y += qapi/qapi-visit.o
|
|
||||||
util-obj-y += qapi/qapi-visit-block-core.o
|
|
||||||
util-obj-y += qapi/qapi-visit-block.o
|
|
||||||
util-obj-y += qapi/qapi-visit-char.o
|
|
||||||
util-obj-y += qapi/qapi-visit-common.o
|
|
||||||
util-obj-y += qapi/qapi-visit-crypto.o
|
|
||||||
util-obj-y += qapi/qapi-visit-introspect.o
|
|
||||||
util-obj-y += qapi/qapi-visit-job.o
|
|
||||||
util-obj-y += qapi/qapi-visit-migration.o
|
|
||||||
util-obj-y += qapi/qapi-visit-misc.o
|
|
||||||
util-obj-y += qapi/qapi-visit-net.o
|
|
||||||
util-obj-y += qapi/qapi-visit-rocker.o
|
|
||||||
util-obj-y += qapi/qapi-visit-run-state.o
|
|
||||||
util-obj-y += qapi/qapi-visit-sockets.o
|
|
||||||
util-obj-y += qapi/qapi-visit-tpm.o
|
|
||||||
util-obj-y += qapi/qapi-visit-trace.o
|
|
||||||
util-obj-y += qapi/qapi-visit-transaction.o
|
|
||||||
util-obj-y += qapi/qapi-visit-ui.o
|
|
||||||
util-obj-y += qapi/qapi-events.o
|
|
||||||
util-obj-y += qapi/qapi-events-block-core.o
|
|
||||||
util-obj-y += qapi/qapi-events-block.o
|
|
||||||
util-obj-y += qapi/qapi-events-char.o
|
|
||||||
util-obj-y += qapi/qapi-events-common.o
|
|
||||||
util-obj-y += qapi/qapi-events-crypto.o
|
|
||||||
util-obj-y += qapi/qapi-events-introspect.o
|
|
||||||
util-obj-y += qapi/qapi-events-job.o
|
|
||||||
util-obj-y += qapi/qapi-events-migration.o
|
|
||||||
util-obj-y += qapi/qapi-events-misc.o
|
|
||||||
util-obj-y += qapi/qapi-events-net.o
|
|
||||||
util-obj-y += qapi/qapi-events-rocker.o
|
|
||||||
util-obj-y += qapi/qapi-events-run-state.o
|
|
||||||
util-obj-y += qapi/qapi-events-sockets.o
|
|
||||||
util-obj-y += qapi/qapi-events-tpm.o
|
|
||||||
util-obj-y += qapi/qapi-events-trace.o
|
|
||||||
util-obj-y += qapi/qapi-events-transaction.o
|
|
||||||
util-obj-y += qapi/qapi-events-ui.o
|
|
||||||
util-obj-y += qapi/qapi-introspect.o
|
|
||||||
|
|
||||||
chardev-obj-y = chardev/
|
chardev-obj-y = chardev/
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
# authz-obj-y is code used by both qemu system emulation and qemu-img
|
||||||
|
|
||||||
|
authz-obj-y = authz/
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
# block-obj-y is code used by both qemu system emulation and qemu-img
|
# block-obj-y is code used by both qemu system emulation and qemu-img
|
||||||
|
|
||||||
block-obj-y += nbd/
|
block-obj-y = nbd/
|
||||||
block-obj-y += block.o blockjob.o job.o
|
block-obj-y += block.o blockjob.o job.o
|
||||||
block-obj-y += block/ scsi/
|
block-obj-y += block/ scsi/
|
||||||
block-obj-y += qemu-io-cmds.o
|
block-obj-y += qemu-io-cmds.o
|
||||||
|
@ -123,8 +71,6 @@ common-obj-y += vl.o
|
||||||
vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS)
|
vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS)
|
||||||
common-obj-$(CONFIG_TPM) += tpm.o
|
common-obj-$(CONFIG_TPM) += tpm.o
|
||||||
|
|
||||||
common-obj-$(CONFIG_SLIRP) += slirp/
|
|
||||||
|
|
||||||
common-obj-y += backends/
|
common-obj-y += backends/
|
||||||
common-obj-y += chardev/
|
common-obj-y += chardev/
|
||||||
|
|
||||||
|
@ -137,26 +83,8 @@ common-obj-$(CONFIG_FDT) += device_tree.o
|
||||||
######################################################################
|
######################################################################
|
||||||
# qapi
|
# qapi
|
||||||
|
|
||||||
common-obj-y += qapi/qapi-commands.o
|
|
||||||
common-obj-y += qapi/qapi-commands-block-core.o
|
|
||||||
common-obj-y += qapi/qapi-commands-block.o
|
|
||||||
common-obj-y += qapi/qapi-commands-char.o
|
|
||||||
common-obj-y += qapi/qapi-commands-common.o
|
|
||||||
common-obj-y += qapi/qapi-commands-crypto.o
|
|
||||||
common-obj-y += qapi/qapi-commands-introspect.o
|
|
||||||
common-obj-y += qapi/qapi-commands-job.o
|
|
||||||
common-obj-y += qapi/qapi-commands-migration.o
|
|
||||||
common-obj-y += qapi/qapi-commands-misc.o
|
|
||||||
common-obj-y += qapi/qapi-commands-net.o
|
|
||||||
common-obj-y += qapi/qapi-commands-rocker.o
|
|
||||||
common-obj-y += qapi/qapi-commands-run-state.o
|
|
||||||
common-obj-y += qapi/qapi-commands-sockets.o
|
|
||||||
common-obj-y += qapi/qapi-commands-tpm.o
|
|
||||||
common-obj-y += qapi/qapi-commands-trace.o
|
|
||||||
common-obj-y += qapi/qapi-commands-transaction.o
|
|
||||||
common-obj-y += qapi/qapi-commands-ui.o
|
|
||||||
common-obj-y += qapi/qapi-introspect.o
|
|
||||||
common-obj-y += qmp.o hmp.o
|
common-obj-y += qmp.o hmp.o
|
||||||
|
common-obj-y += qapi/
|
||||||
endif
|
endif
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
|
@ -173,7 +101,6 @@ version-obj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.o
|
||||||
######################################################################
|
######################################################################
|
||||||
# tracing
|
# tracing
|
||||||
util-obj-y += trace/
|
util-obj-y += trace/
|
||||||
target-obj-y += trace/
|
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# guest agent
|
# guest agent
|
||||||
|
@ -186,6 +113,7 @@ qga-vss-dll-obj-y = qga/
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# contrib
|
# contrib
|
||||||
|
elf2dmp-obj-y = contrib/elf2dmp/
|
||||||
ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/
|
ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/
|
||||||
ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/
|
ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/
|
||||||
libvhost-user-obj-y = contrib/libvhost-user/
|
libvhost-user-obj-y = contrib/libvhost-user/
|
||||||
|
@ -193,12 +121,14 @@ vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS)
|
||||||
vhost-user-scsi.o-libs := $(LIBISCSI_LIBS)
|
vhost-user-scsi.o-libs := $(LIBISCSI_LIBS)
|
||||||
vhost-user-scsi-obj-y = contrib/vhost-user-scsi/
|
vhost-user-scsi-obj-y = contrib/vhost-user-scsi/
|
||||||
vhost-user-blk-obj-y = contrib/vhost-user-blk/
|
vhost-user-blk-obj-y = contrib/vhost-user-blk/
|
||||||
|
rdmacm-mux-obj-y = contrib/rdmacm-mux/
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
trace-events-subdirs =
|
trace-events-subdirs =
|
||||||
trace-events-subdirs += accel/kvm
|
trace-events-subdirs += accel/kvm
|
||||||
trace-events-subdirs += accel/tcg
|
trace-events-subdirs += accel/tcg
|
||||||
trace-events-subdirs += audio
|
trace-events-subdirs += audio
|
||||||
|
trace-events-subdirs += authz
|
||||||
trace-events-subdirs += block
|
trace-events-subdirs += block
|
||||||
trace-events-subdirs += chardev
|
trace-events-subdirs += chardev
|
||||||
trace-events-subdirs += crypto
|
trace-events-subdirs += crypto
|
||||||
|
@ -240,7 +170,9 @@ trace-events-subdirs += hw/tpm
|
||||||
trace-events-subdirs += hw/usb
|
trace-events-subdirs += hw/usb
|
||||||
trace-events-subdirs += hw/vfio
|
trace-events-subdirs += hw/vfio
|
||||||
trace-events-subdirs += hw/virtio
|
trace-events-subdirs += hw/virtio
|
||||||
|
trace-events-subdirs += hw/watchdog
|
||||||
trace-events-subdirs += hw/xen
|
trace-events-subdirs += hw/xen
|
||||||
|
trace-events-subdirs += hw/gpio
|
||||||
trace-events-subdirs += io
|
trace-events-subdirs += io
|
||||||
trace-events-subdirs += linux-user
|
trace-events-subdirs += linux-user
|
||||||
trace-events-subdirs += migration
|
trace-events-subdirs += migration
|
||||||
|
@ -250,9 +182,11 @@ trace-events-subdirs += qapi
|
||||||
trace-events-subdirs += qom
|
trace-events-subdirs += qom
|
||||||
trace-events-subdirs += scsi
|
trace-events-subdirs += scsi
|
||||||
trace-events-subdirs += target/arm
|
trace-events-subdirs += target/arm
|
||||||
|
trace-events-subdirs += target/hppa
|
||||||
trace-events-subdirs += target/i386
|
trace-events-subdirs += target/i386
|
||||||
trace-events-subdirs += target/mips
|
trace-events-subdirs += target/mips
|
||||||
trace-events-subdirs += target/ppc
|
trace-events-subdirs += target/ppc
|
||||||
|
trace-events-subdirs += target/riscv
|
||||||
trace-events-subdirs += target/s390x
|
trace-events-subdirs += target/s390x
|
||||||
trace-events-subdirs += target/sparc
|
trace-events-subdirs += target/sparc
|
||||||
trace-events-subdirs += ui
|
trace-events-subdirs += ui
|
||||||
|
|
|
@ -4,9 +4,12 @@ BUILD_DIR?=$(CURDIR)/..
|
||||||
|
|
||||||
include ../config-host.mak
|
include ../config-host.mak
|
||||||
include config-target.mak
|
include config-target.mak
|
||||||
include config-devices.mak
|
|
||||||
include $(SRC_PATH)/rules.mak
|
include $(SRC_PATH)/rules.mak
|
||||||
|
|
||||||
|
ifdef CONFIG_SOFTMMU
|
||||||
|
include config-devices.mak
|
||||||
|
endif
|
||||||
|
|
||||||
$(call set-vpath, $(SRC_PATH):$(BUILD_DIR))
|
$(call set-vpath, $(SRC_PATH):$(BUILD_DIR))
|
||||||
ifdef CONFIG_LINUX
|
ifdef CONFIG_LINUX
|
||||||
QEMU_CFLAGS += -I../linux-headers
|
QEMU_CFLAGS += -I../linux-headers
|
||||||
|
@ -37,15 +40,13 @@ PROGS=$(QEMU_PROG) $(QEMU_PROGW)
|
||||||
STPFILES=
|
STPFILES=
|
||||||
|
|
||||||
# Makefile Tests
|
# Makefile Tests
|
||||||
ifdef CONFIG_USER_ONLY
|
|
||||||
include $(SRC_PATH)/tests/tcg/Makefile.include
|
include $(SRC_PATH)/tests/tcg/Makefile.include
|
||||||
endif
|
|
||||||
|
|
||||||
config-target.h: config-target.h-timestamp
|
config-target.h: config-target.h-timestamp
|
||||||
config-target.h-timestamp: config-target.mak
|
config-target.h-timestamp: config-target.mak
|
||||||
|
|
||||||
ifdef CONFIG_TRACE_SYSTEMTAP
|
ifdef CONFIG_TRACE_SYSTEMTAP
|
||||||
stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp
|
stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp $(QEMU_PROG)-log.stp
|
||||||
|
|
||||||
ifdef CONFIG_USER_ONLY
|
ifdef CONFIG_USER_ONLY
|
||||||
TARGET_TYPE=user
|
TARGET_TYPE=user
|
||||||
|
@ -84,6 +85,14 @@ $(QEMU_PROG)-simpletrace.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y)
|
||||||
--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \
|
--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \
|
||||||
$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp")
|
$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp")
|
||||||
|
|
||||||
|
$(QEMU_PROG)-log.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y)
|
||||||
|
$(call quiet-command,$(TRACETOOL) \
|
||||||
|
--group=all \
|
||||||
|
--format=log-stap \
|
||||||
|
--backends=$(TRACE_BACKENDS) \
|
||||||
|
--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \
|
||||||
|
$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-log.stp")
|
||||||
|
|
||||||
else
|
else
|
||||||
stap:
|
stap:
|
||||||
endif
|
endif
|
||||||
|
@ -94,6 +103,8 @@ all: $(PROGS) stap
|
||||||
# Dummy command so that make thinks it has done something
|
# Dummy command so that make thinks it has done something
|
||||||
@true
|
@true
|
||||||
|
|
||||||
|
obj-y += trace/
|
||||||
|
|
||||||
#########################################################
|
#########################################################
|
||||||
# cpu emulator library
|
# cpu emulator library
|
||||||
obj-y += exec.o
|
obj-y += exec.o
|
||||||
|
@ -140,6 +151,7 @@ ifdef CONFIG_SOFTMMU
|
||||||
obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
|
obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
|
||||||
obj-y += qtest.o
|
obj-y += qtest.o
|
||||||
obj-y += hw/
|
obj-y += hw/
|
||||||
|
obj-y += qapi/
|
||||||
obj-y += memory.o
|
obj-y += memory.o
|
||||||
obj-y += memory_mapping.o
|
obj-y += memory_mapping.o
|
||||||
obj-y += dump.o
|
obj-y += dump.o
|
||||||
|
@ -158,20 +170,12 @@ GENERATED_FILES += hmp-commands.h hmp-commands-info.h
|
||||||
|
|
||||||
endif # CONFIG_SOFTMMU
|
endif # CONFIG_SOFTMMU
|
||||||
|
|
||||||
# Workaround for http://gcc.gnu.org/PR55489, see configure.
|
|
||||||
%/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS)
|
|
||||||
|
|
||||||
dummy := $(call unnest-vars,,obj-y)
|
dummy := $(call unnest-vars,,obj-y)
|
||||||
all-obj-y := $(obj-y)
|
all-obj-y := $(obj-y)
|
||||||
|
|
||||||
target-obj-y :=
|
|
||||||
block-obj-y :=
|
|
||||||
common-obj-y :=
|
|
||||||
chardev-obj-y :=
|
|
||||||
include $(SRC_PATH)/Makefile.objs
|
include $(SRC_PATH)/Makefile.objs
|
||||||
dummy := $(call unnest-vars,,target-obj-y)
|
|
||||||
target-obj-y-save := $(target-obj-y)
|
|
||||||
dummy := $(call unnest-vars,.., \
|
dummy := $(call unnest-vars,.., \
|
||||||
|
authz-obj-y \
|
||||||
block-obj-y \
|
block-obj-y \
|
||||||
block-obj-m \
|
block-obj-m \
|
||||||
chardev-obj-y \
|
chardev-obj-y \
|
||||||
|
@ -181,16 +185,17 @@ dummy := $(call unnest-vars,.., \
|
||||||
io-obj-y \
|
io-obj-y \
|
||||||
common-obj-y \
|
common-obj-y \
|
||||||
common-obj-m)
|
common-obj-m)
|
||||||
target-obj-y := $(target-obj-y-save)
|
|
||||||
all-obj-y += $(common-obj-y)
|
all-obj-y += $(common-obj-y)
|
||||||
all-obj-y += $(target-obj-y)
|
|
||||||
all-obj-y += $(qom-obj-y)
|
all-obj-y += $(qom-obj-y)
|
||||||
|
all-obj-$(CONFIG_SOFTMMU) += $(authz-obj-y)
|
||||||
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y)
|
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y)
|
||||||
all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
|
all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
|
||||||
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
|
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
|
||||||
all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
|
all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
|
||||||
|
|
||||||
|
ifdef CONFIG_SOFTMMU
|
||||||
$(QEMU_PROG_BUILD): config-devices.mak
|
$(QEMU_PROG_BUILD): config-devices.mak
|
||||||
|
endif
|
||||||
|
|
||||||
COMMON_LDADDS = ../libqemuutil.a
|
COMMON_LDADDS = ../libqemuutil.a
|
||||||
|
|
||||||
|
@ -215,6 +220,7 @@ clean: clean-target
|
||||||
rm -f *.a *~ $(PROGS)
|
rm -f *.a *~ $(PROGS)
|
||||||
rm -f $(shell find . -name '*.[od]')
|
rm -f $(shell find . -name '*.[od]')
|
||||||
rm -f hmp-commands.h gdbstub-xml.c
|
rm -f hmp-commands.h gdbstub-xml.c
|
||||||
|
rm -f trace/generated-helpers.c trace/generated-helpers.c-timestamp
|
||||||
ifdef CONFIG_TRACE_SYSTEMTAP
|
ifdef CONFIG_TRACE_SYSTEMTAP
|
||||||
rm -f *.stp
|
rm -f *.stp
|
||||||
endif
|
endif
|
||||||
|
@ -227,6 +233,7 @@ ifdef CONFIG_TRACE_SYSTEMTAP
|
||||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset"
|
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset"
|
||||||
$(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp"
|
$(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp"
|
||||||
$(INSTALL_DATA) $(QEMU_PROG)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp"
|
$(INSTALL_DATA) $(QEMU_PROG)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp"
|
||||||
|
$(INSTALL_DATA) $(QEMU_PROG)-log.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-log.stp"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
GENERATED_FILES += config-target.h
|
GENERATED_FILES += config-target.h
|
||||||
|
|
4
README
4
README
|
@ -54,7 +54,7 @@ Submitting patches
|
||||||
|
|
||||||
The QEMU source code is maintained under the GIT version control system.
|
The QEMU source code is maintained under the GIT version control system.
|
||||||
|
|
||||||
git clone git://git.qemu.org/qemu.git
|
git clone https://git.qemu.org/git/qemu.git
|
||||||
|
|
||||||
When submitting patches, one common approach is to use 'git
|
When submitting patches, one common approach is to use 'git
|
||||||
format-patch' and/or 'git send-email' to format & send the mail to the
|
format-patch' and/or 'git send-email' to format & send the mail to the
|
||||||
|
@ -70,7 +70,7 @@ the QEMU website
|
||||||
|
|
||||||
The QEMU website is also maintained under source control.
|
The QEMU website is also maintained under source control.
|
||||||
|
|
||||||
git clone git://git.qemu.org/qemu-web.git
|
git clone https://git.qemu.org/git/qemu-web.git
|
||||||
https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/
|
https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/
|
||||||
|
|
||||||
A 'git-publish' utility was created to make above process less
|
A 'git-publish' utility was created to make above process less
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include "qom/object.h"
|
#include "qom/object.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
|
||||||
static const TypeInfo accel_type = {
|
static const TypeInfo accel_type = {
|
||||||
.name = TYPE_ACCEL,
|
.name = TYPE_ACCEL,
|
||||||
|
@ -64,11 +65,13 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms)
|
||||||
ms->accelerator = NULL;
|
ms->accelerator = NULL;
|
||||||
*(acc->allowed) = false;
|
*(acc->allowed) = false;
|
||||||
object_unref(OBJECT(accel));
|
object_unref(OBJECT(accel));
|
||||||
|
} else {
|
||||||
|
object_set_accelerator_compat_props(acc->compat_props);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void configure_accelerator(MachineState *ms)
|
void configure_accelerator(MachineState *ms, const char *progname)
|
||||||
{
|
{
|
||||||
const char *accel;
|
const char *accel;
|
||||||
char **accel_list, **tmp;
|
char **accel_list, **tmp;
|
||||||
|
@ -79,8 +82,22 @@ void configure_accelerator(MachineState *ms)
|
||||||
|
|
||||||
accel = qemu_opt_get(qemu_get_machine_opts(), "accel");
|
accel = qemu_opt_get(qemu_get_machine_opts(), "accel");
|
||||||
if (accel == NULL) {
|
if (accel == NULL) {
|
||||||
/* Use the default "accelerator", tcg */
|
/* Select the default accelerator */
|
||||||
accel = "tcg";
|
int pnlen = strlen(progname);
|
||||||
|
if (pnlen >= 3 && g_str_equal(&progname[pnlen - 3], "kvm")) {
|
||||||
|
/* If the program name ends with "kvm", we prefer KVM */
|
||||||
|
accel = "kvm:tcg";
|
||||||
|
} else {
|
||||||
|
#if defined(CONFIG_TCG)
|
||||||
|
accel = "tcg";
|
||||||
|
#elif defined(CONFIG_KVM)
|
||||||
|
accel = "kvm";
|
||||||
|
#else
|
||||||
|
error_report("No accelerator selected and"
|
||||||
|
" no default accelerator available");
|
||||||
|
exit(1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
accel_list = g_strsplit(accel, ":", 0);
|
accel_list = g_strsplit(accel, ":", 0);
|
||||||
|
@ -118,12 +135,6 @@ void configure_accelerator(MachineState *ms)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void accel_register_compat_props(AccelState *accel)
|
|
||||||
{
|
|
||||||
AccelClass *class = ACCEL_GET_CLASS(accel);
|
|
||||||
register_compat_props_array(class->global_props);
|
|
||||||
}
|
|
||||||
|
|
||||||
void accel_setup_post(MachineState *ms)
|
void accel_setup_post(MachineState *ms)
|
||||||
{
|
{
|
||||||
AccelState *accel = ms->accelerator;
|
AccelState *accel = ms->accelerator;
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "hw/irq.h"
|
#include "hw/irq.h"
|
||||||
#include "sysemu/sev.h"
|
#include "sysemu/sev.h"
|
||||||
|
#include "sysemu/balloon.h"
|
||||||
|
|
||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
|
|
||||||
|
@ -78,13 +79,14 @@ struct KVMState
|
||||||
int fd;
|
int fd;
|
||||||
int vmfd;
|
int vmfd;
|
||||||
int coalesced_mmio;
|
int coalesced_mmio;
|
||||||
|
int coalesced_pio;
|
||||||
struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
|
struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
|
||||||
bool coalesced_flush_in_progress;
|
bool coalesced_flush_in_progress;
|
||||||
int vcpu_events;
|
int vcpu_events;
|
||||||
int robust_singlestep;
|
int robust_singlestep;
|
||||||
int debugregs;
|
int debugregs;
|
||||||
#ifdef KVM_CAP_SET_GUEST_DEBUG
|
#ifdef KVM_CAP_SET_GUEST_DEBUG
|
||||||
struct kvm_sw_breakpoint_head kvm_sw_breakpoints;
|
QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints;
|
||||||
#endif
|
#endif
|
||||||
int many_ioeventfds;
|
int many_ioeventfds;
|
||||||
int intx_set_mask;
|
int intx_set_mask;
|
||||||
|
@ -100,7 +102,7 @@ struct KVMState
|
||||||
int nr_allocated_irq_routes;
|
int nr_allocated_irq_routes;
|
||||||
unsigned long *used_gsi_bitmap;
|
unsigned long *used_gsi_bitmap;
|
||||||
unsigned int gsi_count;
|
unsigned int gsi_count;
|
||||||
QTAILQ_HEAD(msi_hashtab, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE];
|
QTAILQ_HEAD(, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE];
|
||||||
#endif
|
#endif
|
||||||
KVMMemoryListener memory_listener;
|
KVMMemoryListener memory_listener;
|
||||||
QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus;
|
QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus;
|
||||||
|
@ -559,6 +561,45 @@ static void kvm_uncoalesce_mmio_region(MemoryListener *listener,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void kvm_coalesce_pio_add(MemoryListener *listener,
|
||||||
|
MemoryRegionSection *section,
|
||||||
|
hwaddr start, hwaddr size)
|
||||||
|
{
|
||||||
|
KVMState *s = kvm_state;
|
||||||
|
|
||||||
|
if (s->coalesced_pio) {
|
||||||
|
struct kvm_coalesced_mmio_zone zone;
|
||||||
|
|
||||||
|
zone.addr = start;
|
||||||
|
zone.size = size;
|
||||||
|
zone.pio = 1;
|
||||||
|
|
||||||
|
(void)kvm_vm_ioctl(s, KVM_REGISTER_COALESCED_MMIO, &zone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_coalesce_pio_del(MemoryListener *listener,
|
||||||
|
MemoryRegionSection *section,
|
||||||
|
hwaddr start, hwaddr size)
|
||||||
|
{
|
||||||
|
KVMState *s = kvm_state;
|
||||||
|
|
||||||
|
if (s->coalesced_pio) {
|
||||||
|
struct kvm_coalesced_mmio_zone zone;
|
||||||
|
|
||||||
|
zone.addr = start;
|
||||||
|
zone.size = size;
|
||||||
|
zone.pio = 1;
|
||||||
|
|
||||||
|
(void)kvm_vm_ioctl(s, KVM_UNREGISTER_COALESCED_MMIO, &zone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static MemoryListener kvm_coalesced_pio_listener = {
|
||||||
|
.coalesced_io_add = kvm_coalesce_pio_add,
|
||||||
|
.coalesced_io_del = kvm_coalesce_pio_del,
|
||||||
|
};
|
||||||
|
|
||||||
int kvm_check_extension(KVMState *s, unsigned int extension)
|
int kvm_check_extension(KVMState *s, unsigned int extension)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -616,6 +657,8 @@ static int kvm_set_ioeventfd_mmio(int fd, hwaddr addr, uint32_t val,
|
||||||
.fd = fd,
|
.fd = fd,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
trace_kvm_set_ioeventfd_mmio(fd, (uint64_t)addr, val, assign, size,
|
||||||
|
datamatch);
|
||||||
if (!kvm_enabled()) {
|
if (!kvm_enabled()) {
|
||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
}
|
}
|
||||||
|
@ -647,6 +690,7 @@ static int kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint16_t val,
|
||||||
.fd = fd,
|
.fd = fd,
|
||||||
};
|
};
|
||||||
int r;
|
int r;
|
||||||
|
trace_kvm_set_ioeventfd_pio(fd, addr, val, assign, size, datamatch);
|
||||||
if (!kvm_enabled()) {
|
if (!kvm_enabled()) {
|
||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
}
|
}
|
||||||
|
@ -1549,7 +1593,7 @@ static int kvm_init(MachineState *ms)
|
||||||
|
|
||||||
kvm_type = qemu_opt_get(qemu_get_machine_opts(), "kvm-type");
|
kvm_type = qemu_opt_get(qemu_get_machine_opts(), "kvm-type");
|
||||||
if (mc->kvm_type) {
|
if (mc->kvm_type) {
|
||||||
type = mc->kvm_type(kvm_type);
|
type = mc->kvm_type(ms, kvm_type);
|
||||||
} else if (kvm_type) {
|
} else if (kvm_type) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
fprintf(stderr, "Invalid argument kvm-type=%s\n", kvm_type);
|
fprintf(stderr, "Invalid argument kvm-type=%s\n", kvm_type);
|
||||||
|
@ -1615,6 +1659,8 @@ static int kvm_init(MachineState *ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO);
|
s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO);
|
||||||
|
s->coalesced_pio = s->coalesced_mmio &&
|
||||||
|
kvm_check_extension(s, KVM_CAP_COALESCED_PIO);
|
||||||
|
|
||||||
#ifdef KVM_CAP_VCPU_EVENTS
|
#ifdef KVM_CAP_VCPU_EVENTS
|
||||||
s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS);
|
s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS);
|
||||||
|
@ -1638,10 +1684,8 @@ static int kvm_init(MachineState *ms)
|
||||||
s->irq_set_ioctl = KVM_IRQ_LINE_STATUS;
|
s->irq_set_ioctl = KVM_IRQ_LINE_STATUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef KVM_CAP_READONLY_MEM
|
|
||||||
kvm_readonly_mem_allowed =
|
kvm_readonly_mem_allowed =
|
||||||
(kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0);
|
(kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0);
|
||||||
#endif
|
|
||||||
|
|
||||||
kvm_eventfds_allowed =
|
kvm_eventfds_allowed =
|
||||||
(kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0);
|
(kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0);
|
||||||
|
@ -1687,17 +1731,22 @@ static int kvm_init(MachineState *ms)
|
||||||
s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add;
|
s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add;
|
||||||
s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del;
|
s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del;
|
||||||
}
|
}
|
||||||
s->memory_listener.listener.coalesced_mmio_add = kvm_coalesce_mmio_region;
|
s->memory_listener.listener.coalesced_io_add = kvm_coalesce_mmio_region;
|
||||||
s->memory_listener.listener.coalesced_mmio_del = kvm_uncoalesce_mmio_region;
|
s->memory_listener.listener.coalesced_io_del = kvm_uncoalesce_mmio_region;
|
||||||
|
|
||||||
kvm_memory_listener_register(s, &s->memory_listener,
|
kvm_memory_listener_register(s, &s->memory_listener,
|
||||||
&address_space_memory, 0);
|
&address_space_memory, 0);
|
||||||
memory_listener_register(&kvm_io_listener,
|
memory_listener_register(&kvm_io_listener,
|
||||||
&address_space_io);
|
&address_space_io);
|
||||||
|
memory_listener_register(&kvm_coalesced_pio_listener,
|
||||||
|
&address_space_io);
|
||||||
|
|
||||||
s->many_ioeventfds = kvm_check_many_ioeventfds();
|
s->many_ioeventfds = kvm_check_many_ioeventfds();
|
||||||
|
|
||||||
s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU);
|
s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU);
|
||||||
|
if (!s->sync_mmu) {
|
||||||
|
qemu_balloon_inhibit(true);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -1776,7 +1825,13 @@ void kvm_flush_coalesced_mmio_buffer(void)
|
||||||
|
|
||||||
ent = &ring->coalesced_mmio[ring->first];
|
ent = &ring->coalesced_mmio[ring->first];
|
||||||
|
|
||||||
cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len);
|
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();
|
smp_wmb();
|
||||||
ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX;
|
ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Trace events for debugging and performance instrumentation
|
# See docs/devel/tracing.txt for syntax documentation.
|
||||||
|
|
||||||
# kvm-all.c
|
# kvm-all.c
|
||||||
kvm_ioctl(int type, void *arg) "type 0x%x, arg %p"
|
kvm_ioctl(int type, void *arg) "type 0x%x, arg %p"
|
||||||
|
@ -12,5 +12,7 @@ kvm_irqchip_commit_routes(void) ""
|
||||||
kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d"
|
kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d"
|
||||||
kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d"
|
kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d"
|
||||||
kvm_irqchip_release_virq(int virq) "virq %d"
|
kvm_irqchip_release_virq(int virq) "virq %d"
|
||||||
|
kvm_set_ioeventfd_mmio(int fd, uint64_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%" PRIx64 " val=0x%x assign: %d size: %d match: %d"
|
||||||
|
kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%x val=0x%x assign: %d size: %d match: %d"
|
||||||
kvm_set_user_memory(uint32_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d"
|
kvm_set_user_memory(uint32_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d"
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* License as published by the Free Software Foundation; either
|
* License as published by the Free Software Foundation; either
|
||||||
* version 2 of the License, or (at your option) any later version.
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This library is distributed in the hope that it will be useful,
|
* This library is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
@ -100,19 +100,24 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||||
DATA_TYPE ret;
|
DATA_TYPE ret;
|
||||||
|
|
||||||
ATOMIC_TRACE_RMW;
|
ATOMIC_TRACE_RMW;
|
||||||
|
#if DATA_SIZE == 16
|
||||||
|
ret = atomic16_cmpxchg(haddr, cmpv, newv);
|
||||||
|
#else
|
||||||
ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv);
|
ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv);
|
||||||
|
#endif
|
||||||
ATOMIC_MMU_CLEANUP;
|
ATOMIC_MMU_CLEANUP;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DATA_SIZE >= 16
|
#if DATA_SIZE >= 16
|
||||||
|
#if HAVE_ATOMIC128
|
||||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
||||||
{
|
{
|
||||||
ATOMIC_MMU_DECLS;
|
ATOMIC_MMU_DECLS;
|
||||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
||||||
|
|
||||||
ATOMIC_TRACE_LD;
|
ATOMIC_TRACE_LD;
|
||||||
__atomic_load(haddr, &val, __ATOMIC_RELAXED);
|
val = atomic16_read(haddr);
|
||||||
ATOMIC_MMU_CLEANUP;
|
ATOMIC_MMU_CLEANUP;
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
@ -124,9 +129,10 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
||||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||||
|
|
||||||
ATOMIC_TRACE_ST;
|
ATOMIC_TRACE_ST;
|
||||||
__atomic_store(haddr, &val, __ATOMIC_RELAXED);
|
atomic16_set(haddr, val);
|
||||||
ATOMIC_MMU_CLEANUP;
|
ATOMIC_MMU_CLEANUP;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
||||||
ABI_TYPE val EXTRA_ARGS)
|
ABI_TYPE val EXTRA_ARGS)
|
||||||
|
@ -228,19 +234,24 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||||
DATA_TYPE ret;
|
DATA_TYPE ret;
|
||||||
|
|
||||||
ATOMIC_TRACE_RMW;
|
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));
|
ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
|
||||||
|
#endif
|
||||||
ATOMIC_MMU_CLEANUP;
|
ATOMIC_MMU_CLEANUP;
|
||||||
return BSWAP(ret);
|
return BSWAP(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DATA_SIZE >= 16
|
#if DATA_SIZE >= 16
|
||||||
|
#if HAVE_ATOMIC128
|
||||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
||||||
{
|
{
|
||||||
ATOMIC_MMU_DECLS;
|
ATOMIC_MMU_DECLS;
|
||||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
||||||
|
|
||||||
ATOMIC_TRACE_LD;
|
ATOMIC_TRACE_LD;
|
||||||
__atomic_load(haddr, &val, __ATOMIC_RELAXED);
|
val = atomic16_read(haddr);
|
||||||
ATOMIC_MMU_CLEANUP;
|
ATOMIC_MMU_CLEANUP;
|
||||||
return BSWAP(val);
|
return BSWAP(val);
|
||||||
}
|
}
|
||||||
|
@ -253,9 +264,10 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
||||||
|
|
||||||
ATOMIC_TRACE_ST;
|
ATOMIC_TRACE_ST;
|
||||||
val = BSWAP(val);
|
val = BSWAP(val);
|
||||||
__atomic_store(haddr, &val, __ATOMIC_RELAXED);
|
atomic16_set(haddr, val);
|
||||||
ATOMIC_MMU_CLEANUP;
|
ATOMIC_MMU_CLEANUP;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
||||||
ABI_TYPE val EXTRA_ARGS)
|
ABI_TYPE val EXTRA_ARGS)
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* License as published by the Free Software Foundation; either
|
* License as published by the Free Software Foundation; either
|
||||||
* version 2 of the License, or (at your option) any later version.
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This library is distributed in the hope that it will be useful,
|
* This library is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* License as published by the Free Software Foundation; either
|
* License as published by the Free Software Foundation; either
|
||||||
* version 2 of the License, or (at your option) any later version.
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This library is distributed in the hope that it will be useful,
|
* This library is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
@ -266,6 +266,9 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
||||||
#ifndef CONFIG_SOFTMMU
|
#ifndef CONFIG_SOFTMMU
|
||||||
tcg_debug_assert(!have_mmap_lock());
|
tcg_debug_assert(!have_mmap_lock());
|
||||||
#endif
|
#endif
|
||||||
|
if (qemu_mutex_iothread_locked()) {
|
||||||
|
qemu_mutex_unlock_iothread();
|
||||||
|
}
|
||||||
assert_no_pages_locked();
|
assert_no_pages_locked();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,6 +335,9 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
|
||||||
desc.trace_vcpu_dstate = *cpu->trace_dstate;
|
desc.trace_vcpu_dstate = *cpu->trace_dstate;
|
||||||
desc.pc = pc;
|
desc.pc = pc;
|
||||||
phys_pc = get_page_addr_code(desc.env, pc);
|
phys_pc = get_page_addr_code(desc.env, pc);
|
||||||
|
if (phys_pc == -1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
desc.phys_page1 = phys_pc & TARGET_PAGE_MASK;
|
desc.phys_page1 = phys_pc & TARGET_PAGE_MASK;
|
||||||
h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate);
|
h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate);
|
||||||
return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp);
|
return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp);
|
||||||
|
@ -413,7 +419,7 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
/* See if we can patch the calling TB. */
|
/* See if we can patch the calling TB. */
|
||||||
if (last_tb && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) {
|
if (last_tb) {
|
||||||
tb_add_jump(last_tb, tb_exit, tb);
|
tb_add_jump(last_tb, tb_exit, tb);
|
||||||
}
|
}
|
||||||
return tb;
|
return tb;
|
||||||
|
@ -696,6 +702,7 @@ int cpu_exec(CPUState *cpu)
|
||||||
if (qemu_mutex_iothread_locked()) {
|
if (qemu_mutex_iothread_locked()) {
|
||||||
qemu_mutex_unlock_iothread();
|
qemu_mutex_unlock_iothread();
|
||||||
}
|
}
|
||||||
|
assert_no_pages_locked();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if an exception is pending, we execute it here */
|
/* if an exception is pending, we execute it here */
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -11,7 +11,7 @@
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* License as published by the Free Software Foundation; either
|
* License as published by the Free Software Foundation; either
|
||||||
* version 2 of the License, or (at your option) any later version.
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This library is distributed in the hope that it will be useful,
|
* This library is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
@ -99,20 +99,22 @@ static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env,
|
||||||
size_t mmu_idx, size_t index,
|
size_t mmu_idx, size_t index,
|
||||||
target_ulong addr,
|
target_ulong addr,
|
||||||
uintptr_t retaddr,
|
uintptr_t retaddr,
|
||||||
bool recheck)
|
bool recheck,
|
||||||
|
MMUAccessType access_type)
|
||||||
{
|
{
|
||||||
CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
|
CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
|
||||||
return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, recheck,
|
return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, recheck,
|
||||||
DATA_SIZE);
|
access_type, DATA_SIZE);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
||||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||||
{
|
{
|
||||||
unsigned mmu_idx = get_mmuidx(oi);
|
uintptr_t mmu_idx = get_mmuidx(oi);
|
||||||
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
|
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||||
|
target_ulong tlb_addr = entry->ADDR_READ;
|
||||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||||
uintptr_t haddr;
|
uintptr_t haddr;
|
||||||
DATA_TYPE res;
|
DATA_TYPE res;
|
||||||
|
@ -127,8 +129,10 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
||||||
if (!VICTIM_TLB_HIT(ADDR_READ, addr)) {
|
if (!VICTIM_TLB_HIT(ADDR_READ, addr)) {
|
||||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
|
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
|
||||||
mmu_idx, retaddr);
|
mmu_idx, retaddr);
|
||||||
|
index = tlb_index(env, mmu_idx, addr);
|
||||||
|
entry = tlb_entry(env, mmu_idx, addr);
|
||||||
}
|
}
|
||||||
tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
|
tlb_addr = entry->ADDR_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle an IO access. */
|
/* Handle an IO access. */
|
||||||
|
@ -140,7 +144,8 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
||||||
/* ??? Note that the io helpers always read data in the target
|
/* ??? Note that the io helpers always read data in the target
|
||||||
byte ordering. We should push the LE/BE request down into io. */
|
byte ordering. We should push the LE/BE request down into io. */
|
||||||
res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr,
|
res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr,
|
||||||
tlb_addr & TLB_RECHECK);
|
tlb_addr & TLB_RECHECK,
|
||||||
|
READ_ACCESS_TYPE);
|
||||||
res = TGT_LE(res);
|
res = TGT_LE(res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -164,7 +169,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
haddr = addr + entry->addend;
|
||||||
#if DATA_SIZE == 1
|
#if DATA_SIZE == 1
|
||||||
res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr);
|
res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr);
|
||||||
#else
|
#else
|
||||||
|
@ -177,9 +182,10 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
||||||
WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
|
WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
|
||||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||||
{
|
{
|
||||||
unsigned mmu_idx = get_mmuidx(oi);
|
uintptr_t mmu_idx = get_mmuidx(oi);
|
||||||
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
|
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||||
|
target_ulong tlb_addr = entry->ADDR_READ;
|
||||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||||
uintptr_t haddr;
|
uintptr_t haddr;
|
||||||
DATA_TYPE res;
|
DATA_TYPE res;
|
||||||
|
@ -194,8 +200,10 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
|
||||||
if (!VICTIM_TLB_HIT(ADDR_READ, addr)) {
|
if (!VICTIM_TLB_HIT(ADDR_READ, addr)) {
|
||||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
|
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
|
||||||
mmu_idx, retaddr);
|
mmu_idx, retaddr);
|
||||||
|
index = tlb_index(env, mmu_idx, addr);
|
||||||
|
entry = tlb_entry(env, mmu_idx, addr);
|
||||||
}
|
}
|
||||||
tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
|
tlb_addr = entry->ADDR_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle an IO access. */
|
/* Handle an IO access. */
|
||||||
|
@ -207,7 +215,8 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
|
||||||
/* ??? Note that the io helpers always read data in the target
|
/* ??? Note that the io helpers always read data in the target
|
||||||
byte ordering. We should push the LE/BE request down into io. */
|
byte ordering. We should push the LE/BE request down into io. */
|
||||||
res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr,
|
res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr,
|
||||||
tlb_addr & TLB_RECHECK);
|
tlb_addr & TLB_RECHECK,
|
||||||
|
READ_ACCESS_TYPE);
|
||||||
res = TGT_BE(res);
|
res = TGT_BE(res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -231,7 +240,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
haddr = addr + entry->addend;
|
||||||
res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr);
|
res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -272,9 +281,10 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env,
|
||||||
void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||||
{
|
{
|
||||||
unsigned mmu_idx = get_mmuidx(oi);
|
uintptr_t mmu_idx = get_mmuidx(oi);
|
||||||
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||||
|
target_ulong tlb_addr = tlb_addr_write(entry);
|
||||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||||
uintptr_t haddr;
|
uintptr_t haddr;
|
||||||
|
|
||||||
|
@ -288,8 +298,10 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||||
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
||||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
|
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
|
||||||
mmu_idx, retaddr);
|
mmu_idx, retaddr);
|
||||||
|
index = tlb_index(env, mmu_idx, addr);
|
||||||
|
entry = tlb_entry(env, mmu_idx, addr);
|
||||||
}
|
}
|
||||||
tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK;
|
tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle an IO access. */
|
/* Handle an IO access. */
|
||||||
|
@ -310,16 +322,16 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||||
if (DATA_SIZE > 1
|
if (DATA_SIZE > 1
|
||||||
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
|
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
|
||||||
>= TARGET_PAGE_SIZE)) {
|
>= TARGET_PAGE_SIZE)) {
|
||||||
int i, index2;
|
int i;
|
||||||
target_ulong page2, tlb_addr2;
|
target_ulong page2;
|
||||||
|
CPUTLBEntry *entry2;
|
||||||
do_unaligned_access:
|
do_unaligned_access:
|
||||||
/* Ensure the second page is in the TLB. Note that the first page
|
/* Ensure the second page is in the TLB. Note that the first page
|
||||||
is already guaranteed to be filled, and that the second page
|
is already guaranteed to be filled, and that the second page
|
||||||
cannot evict the first. */
|
cannot evict the first. */
|
||||||
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
|
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
|
||||||
index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
entry2 = tlb_entry(env, mmu_idx, page2);
|
||||||
tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write;
|
if (!tlb_hit_page(tlb_addr_write(entry2), page2)
|
||||||
if (!tlb_hit_page(tlb_addr2, page2)
|
|
||||||
&& !VICTIM_TLB_HIT(addr_write, page2)) {
|
&& !VICTIM_TLB_HIT(addr_write, page2)) {
|
||||||
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
|
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
|
||||||
mmu_idx, retaddr);
|
mmu_idx, retaddr);
|
||||||
|
@ -337,7 +349,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
haddr = addr + entry->addend;
|
||||||
#if DATA_SIZE == 1
|
#if DATA_SIZE == 1
|
||||||
glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val);
|
glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val);
|
||||||
#else
|
#else
|
||||||
|
@ -349,9 +361,10 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||||
void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||||
{
|
{
|
||||||
unsigned mmu_idx = get_mmuidx(oi);
|
uintptr_t mmu_idx = get_mmuidx(oi);
|
||||||
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||||
|
target_ulong tlb_addr = tlb_addr_write(entry);
|
||||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||||
uintptr_t haddr;
|
uintptr_t haddr;
|
||||||
|
|
||||||
|
@ -365,8 +378,10 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||||
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
||||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
|
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
|
||||||
mmu_idx, retaddr);
|
mmu_idx, retaddr);
|
||||||
|
index = tlb_index(env, mmu_idx, addr);
|
||||||
|
entry = tlb_entry(env, mmu_idx, addr);
|
||||||
}
|
}
|
||||||
tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK;
|
tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle an IO access. */
|
/* Handle an IO access. */
|
||||||
|
@ -387,16 +402,16 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||||
if (DATA_SIZE > 1
|
if (DATA_SIZE > 1
|
||||||
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
|
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
|
||||||
>= TARGET_PAGE_SIZE)) {
|
>= TARGET_PAGE_SIZE)) {
|
||||||
int i, index2;
|
int i;
|
||||||
target_ulong page2, tlb_addr2;
|
target_ulong page2;
|
||||||
|
CPUTLBEntry *entry2;
|
||||||
do_unaligned_access:
|
do_unaligned_access:
|
||||||
/* Ensure the second page is in the TLB. Note that the first page
|
/* Ensure the second page is in the TLB. Note that the first page
|
||||||
is already guaranteed to be filled, and that the second page
|
is already guaranteed to be filled, and that the second page
|
||||||
cannot evict the first. */
|
cannot evict the first. */
|
||||||
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
|
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
|
||||||
index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
entry2 = tlb_entry(env, mmu_idx, page2);
|
||||||
tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write;
|
if (!tlb_hit_page(tlb_addr_write(entry2), page2)
|
||||||
if (!tlb_hit_page(tlb_addr2, page2)
|
|
||||||
&& !VICTIM_TLB_HIT(addr_write, page2)) {
|
&& !VICTIM_TLB_HIT(addr_write, page2)) {
|
||||||
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
|
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
|
||||||
mmu_idx, retaddr);
|
mmu_idx, retaddr);
|
||||||
|
@ -414,7 +429,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
haddr = addr + entry->addend;
|
||||||
glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val);
|
glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val);
|
||||||
}
|
}
|
||||||
#endif /* DATA_SIZE > 1 */
|
#endif /* DATA_SIZE > 1 */
|
||||||
|
|
|
@ -51,7 +51,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
|
||||||
if (!qemu_cpu_is_self(cpu)) {
|
if (!qemu_cpu_is_self(cpu)) {
|
||||||
qemu_cpu_kick(cpu);
|
qemu_cpu_kick(cpu);
|
||||||
} else {
|
} else {
|
||||||
cpu->icount_decr.u16.high = -1;
|
atomic_set(&cpu->icount_decr.u16.high, -1);
|
||||||
if (use_icount &&
|
if (use_icount &&
|
||||||
!cpu->can_do_io
|
!cpu->can_do_io
|
||||||
&& (mask & ~old_mask) != 0) {
|
&& (mask & ~old_mask) != 0) {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* License as published by the Free Software Foundation; either
|
* License as published by the Free Software Foundation; either
|
||||||
* version 2 of the License, or (at your option) any later version.
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This library is distributed in the hope that it will be useful,
|
* This library is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
@ -512,6 +512,39 @@ void HELPER(gvec_orc)(void *d, void *a, void *b, uint32_t desc)
|
||||||
clear_high(d, oprsz, desc);
|
clear_high(d, oprsz, desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HELPER(gvec_nand)(void *d, void *a, void *b, uint32_t desc)
|
||||||
|
{
|
||||||
|
intptr_t oprsz = simd_oprsz(desc);
|
||||||
|
intptr_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||||
|
*(vec64 *)(d + i) = ~(*(vec64 *)(a + i) & *(vec64 *)(b + i));
|
||||||
|
}
|
||||||
|
clear_high(d, oprsz, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HELPER(gvec_nor)(void *d, void *a, void *b, uint32_t desc)
|
||||||
|
{
|
||||||
|
intptr_t oprsz = simd_oprsz(desc);
|
||||||
|
intptr_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||||
|
*(vec64 *)(d + i) = ~(*(vec64 *)(a + i) | *(vec64 *)(b + i));
|
||||||
|
}
|
||||||
|
clear_high(d, oprsz, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HELPER(gvec_eqv)(void *d, void *a, void *b, uint32_t desc)
|
||||||
|
{
|
||||||
|
intptr_t oprsz = simd_oprsz(desc);
|
||||||
|
intptr_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||||
|
*(vec64 *)(d + i) = ~(*(vec64 *)(a + i) ^ *(vec64 *)(b + i));
|
||||||
|
}
|
||||||
|
clear_high(d, oprsz, desc);
|
||||||
|
}
|
||||||
|
|
||||||
void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc)
|
void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||||
{
|
{
|
||||||
intptr_t oprsz = simd_oprsz(desc);
|
intptr_t oprsz = simd_oprsz(desc);
|
||||||
|
@ -995,3 +1028,227 @@ void HELPER(gvec_ussub64)(void *d, void *a, void *b, uint32_t desc)
|
||||||
}
|
}
|
||||||
clear_high(d, oprsz, desc);
|
clear_high(d, oprsz, desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HELPER(gvec_smin8)(void *d, void *a, void *b, uint32_t desc)
|
||||||
|
{
|
||||||
|
intptr_t oprsz = simd_oprsz(desc);
|
||||||
|
intptr_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < oprsz; i += sizeof(int8_t)) {
|
||||||
|
int8_t aa = *(int8_t *)(a + i);
|
||||||
|
int8_t bb = *(int8_t *)(b + i);
|
||||||
|
int8_t dd = aa < bb ? aa : bb;
|
||||||
|
*(int8_t *)(d + i) = dd;
|
||||||
|
}
|
||||||
|
clear_high(d, oprsz, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HELPER(gvec_smin16)(void *d, void *a, void *b, uint32_t desc)
|
||||||
|
{
|
||||||
|
intptr_t oprsz = simd_oprsz(desc);
|
||||||
|
intptr_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < oprsz; i += sizeof(int16_t)) {
|
||||||
|
int16_t aa = *(int16_t *)(a + i);
|
||||||
|
int16_t bb = *(int16_t *)(b + i);
|
||||||
|
int16_t dd = aa < bb ? aa : bb;
|
||||||
|
*(int16_t *)(d + i) = dd;
|
||||||
|
}
|
||||||
|
clear_high(d, oprsz, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HELPER(gvec_smin32)(void *d, void *a, void *b, uint32_t desc)
|
||||||
|
{
|
||||||
|
intptr_t oprsz = simd_oprsz(desc);
|
||||||
|
intptr_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < oprsz; i += sizeof(int32_t)) {
|
||||||
|
int32_t aa = *(int32_t *)(a + i);
|
||||||
|
int32_t bb = *(int32_t *)(b + i);
|
||||||
|
int32_t dd = aa < bb ? aa : bb;
|
||||||
|
*(int32_t *)(d + i) = dd;
|
||||||
|
}
|
||||||
|
clear_high(d, oprsz, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HELPER(gvec_smin64)(void *d, void *a, void *b, uint32_t desc)
|
||||||
|
{
|
||||||
|
intptr_t oprsz = simd_oprsz(desc);
|
||||||
|
intptr_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < oprsz; i += sizeof(int64_t)) {
|
||||||
|
int64_t aa = *(int64_t *)(a + i);
|
||||||
|
int64_t bb = *(int64_t *)(b + i);
|
||||||
|
int64_t dd = aa < bb ? aa : bb;
|
||||||
|
*(int64_t *)(d + i) = dd;
|
||||||
|
}
|
||||||
|
clear_high(d, oprsz, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HELPER(gvec_smax8)(void *d, void *a, void *b, uint32_t desc)
|
||||||
|
{
|
||||||
|
intptr_t oprsz = simd_oprsz(desc);
|
||||||
|
intptr_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < oprsz; i += sizeof(int8_t)) {
|
||||||
|
int8_t aa = *(int8_t *)(a + i);
|
||||||
|
int8_t bb = *(int8_t *)(b + i);
|
||||||
|
int8_t dd = aa > bb ? aa : bb;
|
||||||
|
*(int8_t *)(d + i) = dd;
|
||||||
|
}
|
||||||
|
clear_high(d, oprsz, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HELPER(gvec_smax16)(void *d, void *a, void *b, uint32_t desc)
|
||||||
|
{
|
||||||
|
intptr_t oprsz = simd_oprsz(desc);
|
||||||
|
intptr_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < oprsz; i += sizeof(int16_t)) {
|
||||||
|
int16_t aa = *(int16_t *)(a + i);
|
||||||
|
int16_t bb = *(int16_t *)(b + i);
|
||||||
|
int16_t dd = aa > bb ? aa : bb;
|
||||||
|
*(int16_t *)(d + i) = dd;
|
||||||
|
}
|
||||||
|
clear_high(d, oprsz, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HELPER(gvec_smax32)(void *d, void *a, void *b, uint32_t desc)
|
||||||
|
{
|
||||||
|
intptr_t oprsz = simd_oprsz(desc);
|
||||||
|
intptr_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < oprsz; i += sizeof(int32_t)) {
|
||||||
|
int32_t aa = *(int32_t *)(a + i);
|
||||||
|
int32_t bb = *(int32_t *)(b + i);
|
||||||
|
int32_t dd = aa > bb ? aa : bb;
|
||||||
|
*(int32_t *)(d + i) = dd;
|
||||||
|
}
|
||||||
|
clear_high(d, oprsz, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HELPER(gvec_smax64)(void *d, void *a, void *b, uint32_t desc)
|
||||||
|
{
|
||||||
|
intptr_t oprsz = simd_oprsz(desc);
|
||||||
|
intptr_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < oprsz; i += sizeof(int64_t)) {
|
||||||
|
int64_t aa = *(int64_t *)(a + i);
|
||||||
|
int64_t bb = *(int64_t *)(b + i);
|
||||||
|
int64_t dd = aa > bb ? aa : bb;
|
||||||
|
*(int64_t *)(d + i) = dd;
|
||||||
|
}
|
||||||
|
clear_high(d, oprsz, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HELPER(gvec_umin8)(void *d, void *a, void *b, uint32_t desc)
|
||||||
|
{
|
||||||
|
intptr_t oprsz = simd_oprsz(desc);
|
||||||
|
intptr_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||||
|
uint8_t aa = *(uint8_t *)(a + i);
|
||||||
|
uint8_t bb = *(uint8_t *)(b + i);
|
||||||
|
uint8_t dd = aa < bb ? aa : bb;
|
||||||
|
*(uint8_t *)(d + i) = dd;
|
||||||
|
}
|
||||||
|
clear_high(d, oprsz, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HELPER(gvec_umin16)(void *d, void *a, void *b, uint32_t desc)
|
||||||
|
{
|
||||||
|
intptr_t oprsz = simd_oprsz(desc);
|
||||||
|
intptr_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||||
|
uint16_t aa = *(uint16_t *)(a + i);
|
||||||
|
uint16_t bb = *(uint16_t *)(b + i);
|
||||||
|
uint16_t dd = aa < bb ? aa : bb;
|
||||||
|
*(uint16_t *)(d + i) = dd;
|
||||||
|
}
|
||||||
|
clear_high(d, oprsz, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HELPER(gvec_umin32)(void *d, void *a, void *b, uint32_t desc)
|
||||||
|
{
|
||||||
|
intptr_t oprsz = simd_oprsz(desc);
|
||||||
|
intptr_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||||
|
uint32_t aa = *(uint32_t *)(a + i);
|
||||||
|
uint32_t bb = *(uint32_t *)(b + i);
|
||||||
|
uint32_t dd = aa < bb ? aa : bb;
|
||||||
|
*(uint32_t *)(d + i) = dd;
|
||||||
|
}
|
||||||
|
clear_high(d, oprsz, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HELPER(gvec_umin64)(void *d, void *a, void *b, uint32_t desc)
|
||||||
|
{
|
||||||
|
intptr_t oprsz = simd_oprsz(desc);
|
||||||
|
intptr_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||||
|
uint64_t aa = *(uint64_t *)(a + i);
|
||||||
|
uint64_t bb = *(uint64_t *)(b + i);
|
||||||
|
uint64_t dd = aa < bb ? aa : bb;
|
||||||
|
*(uint64_t *)(d + i) = dd;
|
||||||
|
}
|
||||||
|
clear_high(d, oprsz, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HELPER(gvec_umax8)(void *d, void *a, void *b, uint32_t desc)
|
||||||
|
{
|
||||||
|
intptr_t oprsz = simd_oprsz(desc);
|
||||||
|
intptr_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||||
|
uint8_t aa = *(uint8_t *)(a + i);
|
||||||
|
uint8_t bb = *(uint8_t *)(b + i);
|
||||||
|
uint8_t dd = aa > bb ? aa : bb;
|
||||||
|
*(uint8_t *)(d + i) = dd;
|
||||||
|
}
|
||||||
|
clear_high(d, oprsz, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HELPER(gvec_umax16)(void *d, void *a, void *b, uint32_t desc)
|
||||||
|
{
|
||||||
|
intptr_t oprsz = simd_oprsz(desc);
|
||||||
|
intptr_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||||
|
uint16_t aa = *(uint16_t *)(a + i);
|
||||||
|
uint16_t bb = *(uint16_t *)(b + i);
|
||||||
|
uint16_t dd = aa > bb ? aa : bb;
|
||||||
|
*(uint16_t *)(d + i) = dd;
|
||||||
|
}
|
||||||
|
clear_high(d, oprsz, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HELPER(gvec_umax32)(void *d, void *a, void *b, uint32_t desc)
|
||||||
|
{
|
||||||
|
intptr_t oprsz = simd_oprsz(desc);
|
||||||
|
intptr_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||||
|
uint32_t aa = *(uint32_t *)(a + i);
|
||||||
|
uint32_t bb = *(uint32_t *)(b + i);
|
||||||
|
uint32_t dd = aa > bb ? aa : bb;
|
||||||
|
*(uint32_t *)(d + i) = dd;
|
||||||
|
}
|
||||||
|
clear_high(d, oprsz, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HELPER(gvec_umax64)(void *d, void *a, void *b, uint32_t desc)
|
||||||
|
{
|
||||||
|
intptr_t oprsz = simd_oprsz(desc);
|
||||||
|
intptr_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||||
|
uint64_t aa = *(uint64_t *)(a + i);
|
||||||
|
uint64_t bb = *(uint64_t *)(b + i);
|
||||||
|
uint64_t dd = aa > bb ? aa : bb;
|
||||||
|
*(uint64_t *)(d + i) = dd;
|
||||||
|
}
|
||||||
|
clear_high(d, oprsz, desc);
|
||||||
|
}
|
||||||
|
|
|
@ -200,6 +200,26 @@ DEF_HELPER_FLAGS_4(gvec_ussub16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
DEF_HELPER_FLAGS_4(gvec_ussub32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
DEF_HELPER_FLAGS_4(gvec_ussub32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
DEF_HELPER_FLAGS_4(gvec_ussub64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
DEF_HELPER_FLAGS_4(gvec_ussub64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
|
||||||
|
DEF_HELPER_FLAGS_4(gvec_smin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(gvec_smin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(gvec_smin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(gvec_smin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
|
||||||
|
DEF_HELPER_FLAGS_4(gvec_smax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(gvec_smax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(gvec_smax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(gvec_smax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
|
||||||
|
DEF_HELPER_FLAGS_4(gvec_umin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(gvec_umin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(gvec_umin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(gvec_umin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
|
||||||
|
DEF_HELPER_FLAGS_4(gvec_umax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(gvec_umax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(gvec_umax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(gvec_umax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
|
||||||
DEF_HELPER_FLAGS_3(gvec_neg8, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
DEF_HELPER_FLAGS_3(gvec_neg8, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||||
DEF_HELPER_FLAGS_3(gvec_neg16, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
DEF_HELPER_FLAGS_3(gvec_neg16, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||||
DEF_HELPER_FLAGS_3(gvec_neg32, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
DEF_HELPER_FLAGS_3(gvec_neg32, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||||
|
@ -211,6 +231,9 @@ DEF_HELPER_FLAGS_4(gvec_or, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
DEF_HELPER_FLAGS_4(gvec_xor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
DEF_HELPER_FLAGS_4(gvec_xor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
DEF_HELPER_FLAGS_4(gvec_andc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
DEF_HELPER_FLAGS_4(gvec_andc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
DEF_HELPER_FLAGS_4(gvec_orc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
DEF_HELPER_FLAGS_4(gvec_orc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(gvec_nand, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(gvec_nor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
DEF_HELPER_FLAGS_4(gvec_eqv, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||||
|
|
||||||
DEF_HELPER_FLAGS_4(gvec_ands, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
|
DEF_HELPER_FLAGS_4(gvec_ands, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
|
||||||
DEF_HELPER_FLAGS_4(gvec_xors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
|
DEF_HELPER_FLAGS_4(gvec_xors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Trace events for debugging and performance instrumentation
|
# See docs/devel/tracing.txt for syntax documentation.
|
||||||
|
|
||||||
# TCG related tracing (mostly disabled by default)
|
# TCG related tracing (mostly disabled by default)
|
||||||
# cpu-exec.c
|
# cpu-exec.c
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* License as published by the Free Software Foundation; either
|
* License as published by the Free Software Foundation; either
|
||||||
* version 2 of the License, or (at your option) any later version.
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This library is distributed in the hope that it will be useful,
|
* This library is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
@ -16,12 +16,8 @@
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
|
||||||
|
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#define NO_CPU_IO_DEFS
|
#define NO_CPU_IO_DEFS
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
|
@ -1282,8 +1278,7 @@ void tb_flush(CPUState *cpu)
|
||||||
*/
|
*/
|
||||||
#ifdef CONFIG_USER_ONLY
|
#ifdef CONFIG_USER_ONLY
|
||||||
|
|
||||||
static void
|
static void do_tb_invalidate_check(void *p, uint32_t hash, void *userp)
|
||||||
do_tb_invalidate_check(struct qht *ht, void *p, uint32_t hash, void *userp)
|
|
||||||
{
|
{
|
||||||
TranslationBlock *tb = p;
|
TranslationBlock *tb = p;
|
||||||
target_ulong addr = *(target_ulong *)userp;
|
target_ulong addr = *(target_ulong *)userp;
|
||||||
|
@ -1304,8 +1299,7 @@ static void tb_invalidate_check(target_ulong address)
|
||||||
qht_iter(&tb_ctx.htable, do_tb_invalidate_check, &address);
|
qht_iter(&tb_ctx.htable, do_tb_invalidate_check, &address);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void do_tb_page_check(void *p, uint32_t hash, void *userp)
|
||||||
do_tb_page_check(struct qht *ht, void *p, uint32_t hash, void *userp)
|
|
||||||
{
|
{
|
||||||
TranslationBlock *tb = p;
|
TranslationBlock *tb = p;
|
||||||
int flags1, flags2;
|
int flags1, flags2;
|
||||||
|
@ -1493,7 +1487,7 @@ static void tb_phys_invalidate__locked(TranslationBlock *tb)
|
||||||
*/
|
*/
|
||||||
void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr)
|
void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr)
|
||||||
{
|
{
|
||||||
if (page_addr == -1) {
|
if (page_addr == -1 && tb->page_addr[0] != -1) {
|
||||||
page_lock_tb(tb);
|
page_lock_tb(tb);
|
||||||
do_tb_phys_invalidate(tb, true);
|
do_tb_phys_invalidate(tb, true);
|
||||||
page_unlock_tb(tb);
|
page_unlock_tb(tb);
|
||||||
|
@ -1608,6 +1602,17 @@ tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc,
|
||||||
|
|
||||||
assert_memory_lock();
|
assert_memory_lock();
|
||||||
|
|
||||||
|
if (phys_pc == -1) {
|
||||||
|
/*
|
||||||
|
* If the TB is not associated with a physical RAM page then
|
||||||
|
* it must be a temporary one-insn TB, and we have nothing to do
|
||||||
|
* except fill in the page_addr[] fields.
|
||||||
|
*/
|
||||||
|
assert(tb->cflags & CF_NOCACHE);
|
||||||
|
tb->page_addr[0] = tb->page_addr[1] = -1;
|
||||||
|
return tb;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add the TB to the page list, acquiring first the pages's locks.
|
* Add the TB to the page list, acquiring first the pages's locks.
|
||||||
* We keep the locks held until after inserting the TB in the hash table,
|
* We keep the locks held until after inserting the TB in the hash table,
|
||||||
|
@ -1677,6 +1682,15 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||||
|
|
||||||
phys_pc = get_page_addr_code(env, pc);
|
phys_pc = get_page_addr_code(env, pc);
|
||||||
|
|
||||||
|
if (phys_pc == -1) {
|
||||||
|
/* Generate a temporary TB with 1 insn in it */
|
||||||
|
cflags &= ~CF_COUNT_MASK;
|
||||||
|
cflags |= CF_NOCACHE | 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cflags &= ~CF_CLUSTER_MASK;
|
||||||
|
cflags |= cpu->cluster_index << CF_CLUSTER_SHIFT;
|
||||||
|
|
||||||
buffer_overflow:
|
buffer_overflow:
|
||||||
tb = tb_alloc(pc);
|
tb = tb_alloc(pc);
|
||||||
if (unlikely(!tb)) {
|
if (unlikely(!tb)) {
|
||||||
|
@ -1994,15 +2008,6 @@ void tb_invalidate_phys_page_fast(struct page_collection *pages,
|
||||||
{
|
{
|
||||||
PageDesc *p;
|
PageDesc *p;
|
||||||
|
|
||||||
#if 0
|
|
||||||
if (1) {
|
|
||||||
qemu_log("modifying code at 0x%x size=%d EIP=%x PC=%08x\n",
|
|
||||||
cpu_single_env->mem_io_vaddr, len,
|
|
||||||
cpu_single_env->eip,
|
|
||||||
cpu_single_env->eip +
|
|
||||||
(intptr_t)cpu_single_env->segs[R_CS].base);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
assert_memory_lock();
|
assert_memory_lock();
|
||||||
|
|
||||||
p = page_find(start >> TARGET_PAGE_BITS);
|
p = page_find(start >> TARGET_PAGE_BITS);
|
||||||
|
@ -2121,7 +2126,9 @@ void tb_check_watchpoint(CPUState *cpu)
|
||||||
|
|
||||||
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
|
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
|
||||||
addr = get_page_addr_code(env, pc);
|
addr = get_page_addr_code(env, pc);
|
||||||
tb_invalidate_phys_range(addr, addr + 1);
|
if (addr != -1) {
|
||||||
|
tb_invalidate_phys_range(addr, addr + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2282,7 +2289,7 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf)
|
||||||
{
|
{
|
||||||
struct tb_tree_stats tst = {};
|
struct tb_tree_stats tst = {};
|
||||||
struct qht_stats hst;
|
struct qht_stats hst;
|
||||||
size_t nb_tbs;
|
size_t nb_tbs, flush_full, flush_part, flush_elide;
|
||||||
|
|
||||||
tcg_tb_foreach(tb_tree_stats_iter, &tst);
|
tcg_tb_foreach(tb_tree_stats_iter, &tst);
|
||||||
nb_tbs = tst.nb_tbs;
|
nb_tbs = tst.nb_tbs;
|
||||||
|
@ -2318,7 +2325,11 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf)
|
||||||
cpu_fprintf(f, "TB flush count %u\n",
|
cpu_fprintf(f, "TB flush count %u\n",
|
||||||
atomic_read(&tb_ctx.tb_flush_count));
|
atomic_read(&tb_ctx.tb_flush_count));
|
||||||
cpu_fprintf(f, "TB invalidate count %zu\n", tcg_tb_phys_invalidate_count());
|
cpu_fprintf(f, "TB invalidate count %zu\n", tcg_tb_phys_invalidate_count());
|
||||||
cpu_fprintf(f, "TLB flush count %zu\n", tlb_flush_count());
|
|
||||||
|
tlb_flush_counts(&flush_full, &flush_part, &flush_elide);
|
||||||
|
cpu_fprintf(f, "TLB full flushes %zu\n", flush_full);
|
||||||
|
cpu_fprintf(f, "TLB partial flushes %zu\n", flush_part);
|
||||||
|
cpu_fprintf(f, "TLB elided flushes %zu\n", flush_elide);
|
||||||
tcg_dump_info(f, cpu_fprintf);
|
tcg_dump_info(f, cpu_fprintf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2333,7 +2344,7 @@ void cpu_interrupt(CPUState *cpu, int mask)
|
||||||
{
|
{
|
||||||
g_assert(qemu_mutex_iothread_locked());
|
g_assert(qemu_mutex_iothread_locked());
|
||||||
cpu->interrupt_request |= mask;
|
cpu->interrupt_request |= mask;
|
||||||
cpu->icount_decr.u16.high = -1;
|
atomic_set(&cpu->icount_decr.u16.high, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* License as published by the Free Software Foundation; either
|
* License as published by the Free Software Foundation; either
|
||||||
* version 2 of the License, or (at your option) any later version.
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This library is distributed in the hope that it will be useful,
|
* This library is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
|
|
@ -34,6 +34,8 @@ void translator_loop_temp_check(DisasContextBase *db)
|
||||||
void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||||
CPUState *cpu, TranslationBlock *tb)
|
CPUState *cpu, TranslationBlock *tb)
|
||||||
{
|
{
|
||||||
|
int bp_insn = 0;
|
||||||
|
|
||||||
/* Initialize DisasContext */
|
/* Initialize DisasContext */
|
||||||
db->tb = tb;
|
db->tb = tb;
|
||||||
db->pc_first = tb->pc;
|
db->pc_first = tb->pc;
|
||||||
|
@ -71,11 +73,13 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||||
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
||||||
|
|
||||||
/* Pass breakpoint hits to target for further processing */
|
/* Pass breakpoint hits to target for further processing */
|
||||||
if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
|
if (!db->singlestep_enabled
|
||||||
|
&& unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
|
||||||
CPUBreakpoint *bp;
|
CPUBreakpoint *bp;
|
||||||
QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
|
QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
|
||||||
if (bp->pc == db->pc_next) {
|
if (bp->pc == db->pc_next) {
|
||||||
if (ops->breakpoint_check(db, cpu, bp)) {
|
if (ops->breakpoint_check(db, cpu, bp)) {
|
||||||
|
bp_insn = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,7 +122,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||||
|
|
||||||
/* Emit code to exit the TB, as indicated by db->is_jmp. */
|
/* Emit code to exit the TB, as indicated by db->is_jmp. */
|
||||||
ops->tb_stop(db, cpu);
|
ops->tb_stop(db, cpu);
|
||||||
gen_tb_end(db->tb, db->num_insns);
|
gen_tb_end(db->tb, db->num_insns - bp_insn);
|
||||||
|
|
||||||
/* The disas_log hook may use these values rather than recompute. */
|
/* The disas_log hook may use these values rather than recompute. */
|
||||||
db->tb->size = db->pc_next - db->pc_first;
|
db->tb->size = db->pc_next - db->pc_first;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* License as published by the Free Software Foundation; either
|
* License as published by the Free Software Foundation; either
|
||||||
* version 2 of the License, or (at your option) any later version.
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This library is distributed in the hope that it will be useful,
|
* This library is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
@ -25,6 +25,7 @@
|
||||||
#include "exec/cpu_ldst.h"
|
#include "exec/cpu_ldst.h"
|
||||||
#include "translate-all.h"
|
#include "translate-all.h"
|
||||||
#include "exec/helper-proto.h"
|
#include "exec/helper-proto.h"
|
||||||
|
#include "qemu/atomic128.h"
|
||||||
|
|
||||||
#undef EAX
|
#undef EAX
|
||||||
#undef ECX
|
#undef ECX
|
||||||
|
@ -478,28 +479,66 @@ int cpu_signal_handler(int host_signum, void *pinfo,
|
||||||
|
|
||||||
#elif defined(__aarch64__)
|
#elif defined(__aarch64__)
|
||||||
|
|
||||||
|
#ifndef ESR_MAGIC
|
||||||
|
/* Pre-3.16 kernel headers don't have these, so provide fallback definitions */
|
||||||
|
#define ESR_MAGIC 0x45535201
|
||||||
|
struct esr_context {
|
||||||
|
struct _aarch64_ctx head;
|
||||||
|
uint64_t esr;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc)
|
||||||
|
{
|
||||||
|
return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr)
|
||||||
|
{
|
||||||
|
return (struct _aarch64_ctx *)((char *)hdr + hdr->size);
|
||||||
|
}
|
||||||
|
|
||||||
int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
|
int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
|
||||||
{
|
{
|
||||||
siginfo_t *info = pinfo;
|
siginfo_t *info = pinfo;
|
||||||
ucontext_t *uc = puc;
|
ucontext_t *uc = puc;
|
||||||
uintptr_t pc = uc->uc_mcontext.pc;
|
uintptr_t pc = uc->uc_mcontext.pc;
|
||||||
uint32_t insn = *(uint32_t *)pc;
|
|
||||||
bool is_write;
|
bool is_write;
|
||||||
|
struct _aarch64_ctx *hdr;
|
||||||
|
struct esr_context const *esrctx = NULL;
|
||||||
|
|
||||||
/* XXX: need kernel patch to get write flag faster. */
|
/* Find the esr_context, which has the WnR bit in it */
|
||||||
is_write = ( (insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */
|
for (hdr = first_ctx(uc); hdr->magic; hdr = next_ctx(hdr)) {
|
||||||
|| (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */
|
if (hdr->magic == ESR_MAGIC) {
|
||||||
|| (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */
|
esrctx = (struct esr_context const *)hdr;
|
||||||
|| (insn & 0xbfc00000) == 0x0d800000 /* C3.3.4 */
|
break;
|
||||||
|| (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */
|
}
|
||||||
|| (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */
|
}
|
||||||
|| (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */
|
|
||||||
/* Ingore bits 10, 11 & 21, controlling indexing. */
|
|
||||||
|| (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */
|
|
||||||
|| (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */
|
|
||||||
/* Ignore bits 23 & 24, controlling indexing. */
|
|
||||||
|| (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */
|
|
||||||
|
|
||||||
|
if (esrctx) {
|
||||||
|
/* For data aborts ESR.EC is 0b10010x: then bit 6 is the WnR bit */
|
||||||
|
uint64_t esr = esrctx->esr;
|
||||||
|
is_write = extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Fall back to parsing instructions; will only be needed
|
||||||
|
* for really ancient (pre-3.16) kernels.
|
||||||
|
*/
|
||||||
|
uint32_t insn = *(uint32_t *)pc;
|
||||||
|
|
||||||
|
is_write = ((insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */
|
||||||
|
|| (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */
|
||||||
|
|| (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */
|
||||||
|
|| (insn & 0xbfc00000) == 0x0d800000 /* C3.3.4 */
|
||||||
|
|| (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */
|
||||||
|
|| (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */
|
||||||
|
|| (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */
|
||||||
|
/* Ignore bits 10, 11 & 21, controlling indexing. */
|
||||||
|
|| (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */
|
||||||
|
|| (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */
|
||||||
|
/* Ignore bits 23 & 24, controlling indexing. */
|
||||||
|
|| (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */
|
||||||
|
}
|
||||||
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
|
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,6 +609,81 @@ int cpu_signal_handler(int host_signum, void *pinfo,
|
||||||
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
|
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#elif defined(__riscv)
|
||||||
|
|
||||||
|
int cpu_signal_handler(int host_signum, void *pinfo,
|
||||||
|
void *puc)
|
||||||
|
{
|
||||||
|
siginfo_t *info = pinfo;
|
||||||
|
ucontext_t *uc = puc;
|
||||||
|
greg_t pc = uc->uc_mcontext.__gregs[REG_PC];
|
||||||
|
uint32_t insn = *(uint32_t *)pc;
|
||||||
|
int is_write = 0;
|
||||||
|
|
||||||
|
/* Detect store by reading the instruction at the program
|
||||||
|
counter. Note: we currently only generate 32-bit
|
||||||
|
instructions so we thus only detect 32-bit stores */
|
||||||
|
switch (((insn >> 0) & 0b11)) {
|
||||||
|
case 3:
|
||||||
|
switch (((insn >> 2) & 0b11111)) {
|
||||||
|
case 8:
|
||||||
|
switch (((insn >> 12) & 0b111)) {
|
||||||
|
case 0: /* sb */
|
||||||
|
case 1: /* sh */
|
||||||
|
case 2: /* sw */
|
||||||
|
case 3: /* sd */
|
||||||
|
case 4: /* sq */
|
||||||
|
is_write = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
switch (((insn >> 12) & 0b111)) {
|
||||||
|
case 2: /* fsw */
|
||||||
|
case 3: /* fsd */
|
||||||
|
case 4: /* fsq */
|
||||||
|
is_write = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for compressed instructions */
|
||||||
|
switch (((insn >> 13) & 0b111)) {
|
||||||
|
case 7:
|
||||||
|
switch (insn & 0b11) {
|
||||||
|
case 0: /*c.sd */
|
||||||
|
case 2: /* c.sdsp */
|
||||||
|
is_write = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
switch (insn & 0b11) {
|
||||||
|
case 0: /* c.sw */
|
||||||
|
case 3: /* c.swsp */
|
||||||
|
is_write = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#error host CPU specific signal handler needed
|
#error host CPU specific signal handler needed
|
||||||
|
@ -615,7 +729,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||||
/* The following is only callable from other helpers, and matches up
|
/* The following is only callable from other helpers, and matches up
|
||||||
with the softmmu version. */
|
with the softmmu version. */
|
||||||
|
|
||||||
#ifdef CONFIG_ATOMIC128
|
#if HAVE_ATOMIC128 || HAVE_CMPXCHG128
|
||||||
|
|
||||||
#undef EXTRA_ARGS
|
#undef EXTRA_ARGS
|
||||||
#undef ATOMIC_NAME
|
#undef ATOMIC_NAME
|
||||||
|
@ -628,4 +742,4 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||||
|
|
||||||
#define DATA_SIZE 16
|
#define DATA_SIZE 16
|
||||||
#include "atomic_template.h"
|
#include "atomic_template.h"
|
||||||
#endif /* CONFIG_ATOMIC128 */
|
#endif
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
common-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
|
common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o
|
||||||
common-obj-$(CONFIG_SPICE) += spiceaudio.o
|
common-obj-$(CONFIG_SPICE) += spiceaudio.o
|
||||||
common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o
|
common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o
|
||||||
common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o
|
common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o
|
||||||
|
|
|
@ -28,35 +28,14 @@
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
#if QEMU_GNUC_PREREQ(4, 3)
|
|
||||||
#pragma GCC diagnostic ignored "-Waddress"
|
#pragma GCC diagnostic ignored "-Waddress"
|
||||||
#endif
|
|
||||||
|
|
||||||
#define AUDIO_CAP "alsa"
|
#define AUDIO_CAP "alsa"
|
||||||
#include "audio_int.h"
|
#include "audio_int.h"
|
||||||
|
|
||||||
typedef struct ALSAConf {
|
|
||||||
int size_in_usec_in;
|
|
||||||
int size_in_usec_out;
|
|
||||||
const char *pcm_name_in;
|
|
||||||
const char *pcm_name_out;
|
|
||||||
unsigned int buffer_size_in;
|
|
||||||
unsigned int period_size_in;
|
|
||||||
unsigned int buffer_size_out;
|
|
||||||
unsigned int period_size_out;
|
|
||||||
unsigned int threshold;
|
|
||||||
|
|
||||||
int buffer_size_in_overridden;
|
|
||||||
int period_size_in_overridden;
|
|
||||||
|
|
||||||
int buffer_size_out_overridden;
|
|
||||||
int period_size_out_overridden;
|
|
||||||
} ALSAConf;
|
|
||||||
|
|
||||||
struct pollhlp {
|
struct pollhlp {
|
||||||
snd_pcm_t *handle;
|
snd_pcm_t *handle;
|
||||||
struct pollfd *pfds;
|
struct pollfd *pfds;
|
||||||
ALSAConf *conf;
|
|
||||||
int count;
|
int count;
|
||||||
int mask;
|
int mask;
|
||||||
};
|
};
|
||||||
|
@ -68,6 +47,7 @@ typedef struct ALSAVoiceOut {
|
||||||
void *pcm_buf;
|
void *pcm_buf;
|
||||||
snd_pcm_t *handle;
|
snd_pcm_t *handle;
|
||||||
struct pollhlp pollhlp;
|
struct pollhlp pollhlp;
|
||||||
|
Audiodev *dev;
|
||||||
} ALSAVoiceOut;
|
} ALSAVoiceOut;
|
||||||
|
|
||||||
typedef struct ALSAVoiceIn {
|
typedef struct ALSAVoiceIn {
|
||||||
|
@ -75,21 +55,18 @@ typedef struct ALSAVoiceIn {
|
||||||
snd_pcm_t *handle;
|
snd_pcm_t *handle;
|
||||||
void *pcm_buf;
|
void *pcm_buf;
|
||||||
struct pollhlp pollhlp;
|
struct pollhlp pollhlp;
|
||||||
|
Audiodev *dev;
|
||||||
} ALSAVoiceIn;
|
} ALSAVoiceIn;
|
||||||
|
|
||||||
struct alsa_params_req {
|
struct alsa_params_req {
|
||||||
int freq;
|
int freq;
|
||||||
snd_pcm_format_t fmt;
|
snd_pcm_format_t fmt;
|
||||||
int nchannels;
|
int nchannels;
|
||||||
int size_in_usec;
|
|
||||||
int override_mask;
|
|
||||||
unsigned int buffer_size;
|
|
||||||
unsigned int period_size;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct alsa_params_obt {
|
struct alsa_params_obt {
|
||||||
int freq;
|
int freq;
|
||||||
audfmt_e fmt;
|
AudioFormat fmt;
|
||||||
int endianness;
|
int endianness;
|
||||||
int nchannels;
|
int nchannels;
|
||||||
snd_pcm_uframes_t samples;
|
snd_pcm_uframes_t samples;
|
||||||
|
@ -296,16 +273,16 @@ static int alsa_write (SWVoiceOut *sw, void *buf, int len)
|
||||||
return audio_pcm_sw_write (sw, buf, len);
|
return audio_pcm_sw_write (sw, buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
|
static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
|
||||||
{
|
{
|
||||||
switch (fmt) {
|
switch (fmt) {
|
||||||
case AUD_FMT_S8:
|
case AUDIO_FORMAT_S8:
|
||||||
return SND_PCM_FORMAT_S8;
|
return SND_PCM_FORMAT_S8;
|
||||||
|
|
||||||
case AUD_FMT_U8:
|
case AUDIO_FORMAT_U8:
|
||||||
return SND_PCM_FORMAT_U8;
|
return SND_PCM_FORMAT_U8;
|
||||||
|
|
||||||
case AUD_FMT_S16:
|
case AUDIO_FORMAT_S16:
|
||||||
if (endianness) {
|
if (endianness) {
|
||||||
return SND_PCM_FORMAT_S16_BE;
|
return SND_PCM_FORMAT_S16_BE;
|
||||||
}
|
}
|
||||||
|
@ -313,7 +290,7 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
|
||||||
return SND_PCM_FORMAT_S16_LE;
|
return SND_PCM_FORMAT_S16_LE;
|
||||||
}
|
}
|
||||||
|
|
||||||
case AUD_FMT_U16:
|
case AUDIO_FORMAT_U16:
|
||||||
if (endianness) {
|
if (endianness) {
|
||||||
return SND_PCM_FORMAT_U16_BE;
|
return SND_PCM_FORMAT_U16_BE;
|
||||||
}
|
}
|
||||||
|
@ -321,7 +298,7 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
|
||||||
return SND_PCM_FORMAT_U16_LE;
|
return SND_PCM_FORMAT_U16_LE;
|
||||||
}
|
}
|
||||||
|
|
||||||
case AUD_FMT_S32:
|
case AUDIO_FORMAT_S32:
|
||||||
if (endianness) {
|
if (endianness) {
|
||||||
return SND_PCM_FORMAT_S32_BE;
|
return SND_PCM_FORMAT_S32_BE;
|
||||||
}
|
}
|
||||||
|
@ -329,7 +306,7 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
|
||||||
return SND_PCM_FORMAT_S32_LE;
|
return SND_PCM_FORMAT_S32_LE;
|
||||||
}
|
}
|
||||||
|
|
||||||
case AUD_FMT_U32:
|
case AUDIO_FORMAT_U32:
|
||||||
if (endianness) {
|
if (endianness) {
|
||||||
return SND_PCM_FORMAT_U32_BE;
|
return SND_PCM_FORMAT_U32_BE;
|
||||||
}
|
}
|
||||||
|
@ -346,58 +323,58 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt,
|
static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt,
|
||||||
int *endianness)
|
int *endianness)
|
||||||
{
|
{
|
||||||
switch (alsafmt) {
|
switch (alsafmt) {
|
||||||
case SND_PCM_FORMAT_S8:
|
case SND_PCM_FORMAT_S8:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_S8;
|
*fmt = AUDIO_FORMAT_S8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_PCM_FORMAT_U8:
|
case SND_PCM_FORMAT_U8:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_U8;
|
*fmt = AUDIO_FORMAT_U8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_PCM_FORMAT_S16_LE:
|
case SND_PCM_FORMAT_S16_LE:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_S16;
|
*fmt = AUDIO_FORMAT_S16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_PCM_FORMAT_U16_LE:
|
case SND_PCM_FORMAT_U16_LE:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_U16;
|
*fmt = AUDIO_FORMAT_U16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_PCM_FORMAT_S16_BE:
|
case SND_PCM_FORMAT_S16_BE:
|
||||||
*endianness = 1;
|
*endianness = 1;
|
||||||
*fmt = AUD_FMT_S16;
|
*fmt = AUDIO_FORMAT_S16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_PCM_FORMAT_U16_BE:
|
case SND_PCM_FORMAT_U16_BE:
|
||||||
*endianness = 1;
|
*endianness = 1;
|
||||||
*fmt = AUD_FMT_U16;
|
*fmt = AUDIO_FORMAT_U16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_PCM_FORMAT_S32_LE:
|
case SND_PCM_FORMAT_S32_LE:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_S32;
|
*fmt = AUDIO_FORMAT_S32;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_PCM_FORMAT_U32_LE:
|
case SND_PCM_FORMAT_U32_LE:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_U32;
|
*fmt = AUDIO_FORMAT_U32;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_PCM_FORMAT_S32_BE:
|
case SND_PCM_FORMAT_S32_BE:
|
||||||
*endianness = 1;
|
*endianness = 1;
|
||||||
*fmt = AUD_FMT_S32;
|
*fmt = AUDIO_FORMAT_S32;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_PCM_FORMAT_U32_BE:
|
case SND_PCM_FORMAT_U32_BE:
|
||||||
*endianness = 1;
|
*endianness = 1;
|
||||||
*fmt = AUD_FMT_U32;
|
*fmt = AUDIO_FORMAT_U32;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -410,17 +387,18 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt,
|
||||||
|
|
||||||
static void alsa_dump_info (struct alsa_params_req *req,
|
static void alsa_dump_info (struct alsa_params_req *req,
|
||||||
struct alsa_params_obt *obt,
|
struct alsa_params_obt *obt,
|
||||||
snd_pcm_format_t obtfmt)
|
snd_pcm_format_t obtfmt,
|
||||||
|
AudiodevAlsaPerDirectionOptions *apdo)
|
||||||
{
|
{
|
||||||
dolog ("parameter | requested value | obtained value\n");
|
dolog("parameter | requested value | obtained value\n");
|
||||||
dolog ("format | %10d | %10d\n", req->fmt, obtfmt);
|
dolog("format | %10d | %10d\n", req->fmt, obtfmt);
|
||||||
dolog ("channels | %10d | %10d\n",
|
dolog("channels | %10d | %10d\n",
|
||||||
req->nchannels, obt->nchannels);
|
req->nchannels, obt->nchannels);
|
||||||
dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
|
dolog("frequency | %10d | %10d\n", req->freq, obt->freq);
|
||||||
dolog ("============================================\n");
|
dolog("============================================\n");
|
||||||
dolog ("requested: buffer size %d period size %d\n",
|
dolog("requested: buffer len %" PRId32 " period len %" PRId32 "\n",
|
||||||
req->buffer_size, req->period_size);
|
apdo->buffer_length, apdo->period_length);
|
||||||
dolog ("obtained: samples %ld\n", obt->samples);
|
dolog("obtained: samples %ld\n", obt->samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
|
static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
|
||||||
|
@ -453,23 +431,23 @@ static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int alsa_open (int in, struct alsa_params_req *req,
|
static int alsa_open(bool in, struct alsa_params_req *req,
|
||||||
struct alsa_params_obt *obt, snd_pcm_t **handlep,
|
struct alsa_params_obt *obt, snd_pcm_t **handlep,
|
||||||
ALSAConf *conf)
|
Audiodev *dev)
|
||||||
{
|
{
|
||||||
|
AudiodevAlsaOptions *aopts = &dev->u.alsa;
|
||||||
|
AudiodevAlsaPerDirectionOptions *apdo = in ? aopts->in : aopts->out;
|
||||||
snd_pcm_t *handle;
|
snd_pcm_t *handle;
|
||||||
snd_pcm_hw_params_t *hw_params;
|
snd_pcm_hw_params_t *hw_params;
|
||||||
int err;
|
int err;
|
||||||
int size_in_usec;
|
|
||||||
unsigned int freq, nchannels;
|
unsigned int freq, nchannels;
|
||||||
const char *pcm_name = in ? conf->pcm_name_in : conf->pcm_name_out;
|
const char *pcm_name = apdo->has_dev ? apdo->dev : "default";
|
||||||
snd_pcm_uframes_t obt_buffer_size;
|
snd_pcm_uframes_t obt_buffer_size;
|
||||||
const char *typ = in ? "ADC" : "DAC";
|
const char *typ = in ? "ADC" : "DAC";
|
||||||
snd_pcm_format_t obtfmt;
|
snd_pcm_format_t obtfmt;
|
||||||
|
|
||||||
freq = req->freq;
|
freq = req->freq;
|
||||||
nchannels = req->nchannels;
|
nchannels = req->nchannels;
|
||||||
size_in_usec = req->size_in_usec;
|
|
||||||
|
|
||||||
snd_pcm_hw_params_alloca (&hw_params);
|
snd_pcm_hw_params_alloca (&hw_params);
|
||||||
|
|
||||||
|
@ -529,79 +507,42 @@ static int alsa_open (int in, struct alsa_params_req *req,
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req->buffer_size) {
|
if (apdo->buffer_length) {
|
||||||
unsigned long obt;
|
int dir = 0;
|
||||||
|
unsigned int btime = apdo->buffer_length;
|
||||||
|
|
||||||
if (size_in_usec) {
|
err = snd_pcm_hw_params_set_buffer_time_near(
|
||||||
int dir = 0;
|
handle, hw_params, &btime, &dir);
|
||||||
unsigned int btime = req->buffer_size;
|
|
||||||
|
|
||||||
err = snd_pcm_hw_params_set_buffer_time_near (
|
|
||||||
handle,
|
|
||||||
hw_params,
|
|
||||||
&btime,
|
|
||||||
&dir
|
|
||||||
);
|
|
||||||
obt = btime;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
snd_pcm_uframes_t bsize = req->buffer_size;
|
|
||||||
|
|
||||||
err = snd_pcm_hw_params_set_buffer_size_near (
|
|
||||||
handle,
|
|
||||||
hw_params,
|
|
||||||
&bsize
|
|
||||||
);
|
|
||||||
obt = bsize;
|
|
||||||
}
|
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
alsa_logerr2 (err, typ, "Failed to set buffer %s to %d\n",
|
alsa_logerr2(err, typ, "Failed to set buffer time to %" PRId32 "\n",
|
||||||
size_in_usec ? "time" : "size", req->buffer_size);
|
apdo->buffer_length);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((req->override_mask & 2) && (obt - req->buffer_size))
|
if (apdo->has_buffer_length && btime != apdo->buffer_length) {
|
||||||
dolog ("Requested buffer %s %u was rejected, using %lu\n",
|
dolog("Requested buffer time %" PRId32
|
||||||
size_in_usec ? "time" : "size", req->buffer_size, obt);
|
" was rejected, using %u\n", apdo->buffer_length, btime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req->period_size) {
|
if (apdo->period_length) {
|
||||||
unsigned long obt;
|
int dir = 0;
|
||||||
|
unsigned int ptime = apdo->period_length;
|
||||||
|
|
||||||
if (size_in_usec) {
|
err = snd_pcm_hw_params_set_period_time_near(handle, hw_params, &ptime,
|
||||||
int dir = 0;
|
&dir);
|
||||||
unsigned int ptime = req->period_size;
|
|
||||||
|
|
||||||
err = snd_pcm_hw_params_set_period_time_near (
|
|
||||||
handle,
|
|
||||||
hw_params,
|
|
||||||
&ptime,
|
|
||||||
&dir
|
|
||||||
);
|
|
||||||
obt = ptime;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
int dir = 0;
|
|
||||||
snd_pcm_uframes_t psize = req->period_size;
|
|
||||||
|
|
||||||
err = snd_pcm_hw_params_set_period_size_near (
|
|
||||||
handle,
|
|
||||||
hw_params,
|
|
||||||
&psize,
|
|
||||||
&dir
|
|
||||||
);
|
|
||||||
obt = psize;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
alsa_logerr2 (err, typ, "Failed to set period %s to %d\n",
|
alsa_logerr2(err, typ, "Failed to set period time to %" PRId32 "\n",
|
||||||
size_in_usec ? "time" : "size", req->period_size);
|
apdo->period_length);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (((req->override_mask & 1) && (obt - req->period_size)))
|
if (apdo->has_period_length && ptime != apdo->period_length) {
|
||||||
dolog ("Requested period %s %u was rejected, using %lu\n",
|
dolog("Requested period time %" PRId32 " was rejected, using %d\n",
|
||||||
size_in_usec ? "time" : "size", req->period_size, obt);
|
apdo->period_length, ptime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = snd_pcm_hw_params (handle, hw_params);
|
err = snd_pcm_hw_params (handle, hw_params);
|
||||||
|
@ -633,30 +574,12 @@ static int alsa_open (int in, struct alsa_params_req *req,
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!in && conf->threshold) {
|
if (!in && aopts->has_threshold && aopts->threshold) {
|
||||||
snd_pcm_uframes_t threshold;
|
struct audsettings as = { .freq = freq };
|
||||||
int bytes_per_sec;
|
alsa_set_threshold(
|
||||||
|
handle,
|
||||||
bytes_per_sec = freq << (nchannels == 2);
|
audio_buffer_frames(qapi_AudiodevAlsaPerDirectionOptions_base(apdo),
|
||||||
|
&as, aopts->threshold));
|
||||||
switch (obt->fmt) {
|
|
||||||
case AUD_FMT_S8:
|
|
||||||
case AUD_FMT_U8:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AUD_FMT_S16:
|
|
||||||
case AUD_FMT_U16:
|
|
||||||
bytes_per_sec <<= 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AUD_FMT_S32:
|
|
||||||
case AUD_FMT_U32:
|
|
||||||
bytes_per_sec <<= 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
threshold = (conf->threshold * bytes_per_sec) / 1000;
|
|
||||||
alsa_set_threshold (handle, threshold);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
obt->nchannels = nchannels;
|
obt->nchannels = nchannels;
|
||||||
|
@ -669,11 +592,11 @@ static int alsa_open (int in, struct alsa_params_req *req,
|
||||||
obt->nchannels != req->nchannels ||
|
obt->nchannels != req->nchannels ||
|
||||||
obt->freq != req->freq) {
|
obt->freq != req->freq) {
|
||||||
dolog ("Audio parameters for %s\n", typ);
|
dolog ("Audio parameters for %s\n", typ);
|
||||||
alsa_dump_info (req, obt, obtfmt);
|
alsa_dump_info(req, obt, obtfmt, apdo);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
alsa_dump_info (req, obt, obtfmt);
|
alsa_dump_info(req, obt, obtfmt, pdo);
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -799,19 +722,13 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
struct alsa_params_obt obt;
|
struct alsa_params_obt obt;
|
||||||
snd_pcm_t *handle;
|
snd_pcm_t *handle;
|
||||||
struct audsettings obt_as;
|
struct audsettings obt_as;
|
||||||
ALSAConf *conf = drv_opaque;
|
Audiodev *dev = drv_opaque;
|
||||||
|
|
||||||
req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
|
req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
|
||||||
req.freq = as->freq;
|
req.freq = as->freq;
|
||||||
req.nchannels = as->nchannels;
|
req.nchannels = as->nchannels;
|
||||||
req.period_size = conf->period_size_out;
|
|
||||||
req.buffer_size = conf->buffer_size_out;
|
|
||||||
req.size_in_usec = conf->size_in_usec_out;
|
|
||||||
req.override_mask =
|
|
||||||
(conf->period_size_out_overridden ? 1 : 0) |
|
|
||||||
(conf->buffer_size_out_overridden ? 2 : 0);
|
|
||||||
|
|
||||||
if (alsa_open (0, &req, &obt, &handle, conf)) {
|
if (alsa_open(0, &req, &obt, &handle, dev)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -832,7 +749,7 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
}
|
}
|
||||||
|
|
||||||
alsa->handle = handle;
|
alsa->handle = handle;
|
||||||
alsa->pollhlp.conf = conf;
|
alsa->dev = dev;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -872,16 +789,12 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl)
|
||||||
static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||||
{
|
{
|
||||||
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
||||||
|
AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.out;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case VOICE_ENABLE:
|
case VOICE_ENABLE:
|
||||||
{
|
{
|
||||||
va_list ap;
|
bool poll_mode = apdo->try_poll;
|
||||||
int poll_mode;
|
|
||||||
|
|
||||||
va_start (ap, cmd);
|
|
||||||
poll_mode = va_arg (ap, int);
|
|
||||||
va_end (ap);
|
|
||||||
|
|
||||||
ldebug ("enabling voice\n");
|
ldebug ("enabling voice\n");
|
||||||
if (poll_mode && alsa_poll_out (hw)) {
|
if (poll_mode && alsa_poll_out (hw)) {
|
||||||
|
@ -910,19 +823,13 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||||
struct alsa_params_obt obt;
|
struct alsa_params_obt obt;
|
||||||
snd_pcm_t *handle;
|
snd_pcm_t *handle;
|
||||||
struct audsettings obt_as;
|
struct audsettings obt_as;
|
||||||
ALSAConf *conf = drv_opaque;
|
Audiodev *dev = drv_opaque;
|
||||||
|
|
||||||
req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
|
req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
|
||||||
req.freq = as->freq;
|
req.freq = as->freq;
|
||||||
req.nchannels = as->nchannels;
|
req.nchannels = as->nchannels;
|
||||||
req.period_size = conf->period_size_in;
|
|
||||||
req.buffer_size = conf->buffer_size_in;
|
|
||||||
req.size_in_usec = conf->size_in_usec_in;
|
|
||||||
req.override_mask =
|
|
||||||
(conf->period_size_in_overridden ? 1 : 0) |
|
|
||||||
(conf->buffer_size_in_overridden ? 2 : 0);
|
|
||||||
|
|
||||||
if (alsa_open (1, &req, &obt, &handle, conf)) {
|
if (alsa_open(1, &req, &obt, &handle, dev)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -943,7 +850,7 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||||
}
|
}
|
||||||
|
|
||||||
alsa->handle = handle;
|
alsa->handle = handle;
|
||||||
alsa->pollhlp.conf = conf;
|
alsa->dev = dev;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1085,16 +992,12 @@ static int alsa_read (SWVoiceIn *sw, void *buf, int size)
|
||||||
static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||||
{
|
{
|
||||||
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
||||||
|
AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.in;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case VOICE_ENABLE:
|
case VOICE_ENABLE:
|
||||||
{
|
{
|
||||||
va_list ap;
|
bool poll_mode = apdo->try_poll;
|
||||||
int poll_mode;
|
|
||||||
|
|
||||||
va_start (ap, cmd);
|
|
||||||
poll_mode = va_arg (ap, int);
|
|
||||||
va_end (ap);
|
|
||||||
|
|
||||||
ldebug ("enabling voice\n");
|
ldebug ("enabling voice\n");
|
||||||
if (poll_mode && alsa_poll_in (hw)) {
|
if (poll_mode && alsa_poll_in (hw)) {
|
||||||
|
@ -1117,88 +1020,54 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ALSAConf glob_conf = {
|
static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo)
|
||||||
.buffer_size_out = 4096,
|
|
||||||
.period_size_out = 1024,
|
|
||||||
.pcm_name_out = "default",
|
|
||||||
.pcm_name_in = "default",
|
|
||||||
};
|
|
||||||
|
|
||||||
static void *alsa_audio_init (void)
|
|
||||||
{
|
{
|
||||||
ALSAConf *conf = g_malloc(sizeof(ALSAConf));
|
if (!apdo->has_try_poll) {
|
||||||
*conf = glob_conf;
|
apdo->try_poll = true;
|
||||||
return conf;
|
apdo->has_try_poll = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *alsa_audio_init(Audiodev *dev)
|
||||||
|
{
|
||||||
|
AudiodevAlsaOptions *aopts;
|
||||||
|
assert(dev->driver == AUDIODEV_DRIVER_ALSA);
|
||||||
|
|
||||||
|
aopts = &dev->u.alsa;
|
||||||
|
alsa_init_per_direction(aopts->in);
|
||||||
|
alsa_init_per_direction(aopts->out);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* need to define them, as otherwise alsa produces no sound
|
||||||
|
* doesn't set has_* so alsa_open can identify it wasn't set by the user
|
||||||
|
*/
|
||||||
|
if (!dev->u.alsa.out->has_period_length) {
|
||||||
|
/* 1024 frames assuming 44100Hz */
|
||||||
|
dev->u.alsa.out->period_length = 1024 * 1000000 / 44100;
|
||||||
|
}
|
||||||
|
if (!dev->u.alsa.out->has_buffer_length) {
|
||||||
|
/* 4096 frames assuming 44100Hz */
|
||||||
|
dev->u.alsa.out->buffer_length = 4096ll * 1000000 / 44100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* OptsVisitor sets unspecified optional fields to zero, but do not depend
|
||||||
|
* on it...
|
||||||
|
*/
|
||||||
|
if (!dev->u.alsa.in->has_period_length) {
|
||||||
|
dev->u.alsa.in->period_length = 0;
|
||||||
|
}
|
||||||
|
if (!dev->u.alsa.in->has_buffer_length) {
|
||||||
|
dev->u.alsa.in->buffer_length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void alsa_audio_fini (void *opaque)
|
static void alsa_audio_fini (void *opaque)
|
||||||
{
|
{
|
||||||
g_free(opaque);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct audio_option alsa_options[] = {
|
|
||||||
{
|
|
||||||
.name = "DAC_SIZE_IN_USEC",
|
|
||||||
.tag = AUD_OPT_BOOL,
|
|
||||||
.valp = &glob_conf.size_in_usec_out,
|
|
||||||
.descr = "DAC period/buffer size in microseconds (otherwise in frames)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "DAC_PERIOD_SIZE",
|
|
||||||
.tag = AUD_OPT_INT,
|
|
||||||
.valp = &glob_conf.period_size_out,
|
|
||||||
.descr = "DAC period size (0 to go with system default)",
|
|
||||||
.overriddenp = &glob_conf.period_size_out_overridden
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "DAC_BUFFER_SIZE",
|
|
||||||
.tag = AUD_OPT_INT,
|
|
||||||
.valp = &glob_conf.buffer_size_out,
|
|
||||||
.descr = "DAC buffer size (0 to go with system default)",
|
|
||||||
.overriddenp = &glob_conf.buffer_size_out_overridden
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "ADC_SIZE_IN_USEC",
|
|
||||||
.tag = AUD_OPT_BOOL,
|
|
||||||
.valp = &glob_conf.size_in_usec_in,
|
|
||||||
.descr =
|
|
||||||
"ADC period/buffer size in microseconds (otherwise in frames)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "ADC_PERIOD_SIZE",
|
|
||||||
.tag = AUD_OPT_INT,
|
|
||||||
.valp = &glob_conf.period_size_in,
|
|
||||||
.descr = "ADC period size (0 to go with system default)",
|
|
||||||
.overriddenp = &glob_conf.period_size_in_overridden
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "ADC_BUFFER_SIZE",
|
|
||||||
.tag = AUD_OPT_INT,
|
|
||||||
.valp = &glob_conf.buffer_size_in,
|
|
||||||
.descr = "ADC buffer size (0 to go with system default)",
|
|
||||||
.overriddenp = &glob_conf.buffer_size_in_overridden
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "THRESHOLD",
|
|
||||||
.tag = AUD_OPT_INT,
|
|
||||||
.valp = &glob_conf.threshold,
|
|
||||||
.descr = "(undocumented)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "DAC_DEV",
|
|
||||||
.tag = AUD_OPT_STR,
|
|
||||||
.valp = &glob_conf.pcm_name_out,
|
|
||||||
.descr = "DAC device name (for instance dmix)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "ADC_DEV",
|
|
||||||
.tag = AUD_OPT_STR,
|
|
||||||
.valp = &glob_conf.pcm_name_in,
|
|
||||||
.descr = "ADC device name"
|
|
||||||
},
|
|
||||||
{ /* End of list */ }
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct audio_pcm_ops alsa_pcm_ops = {
|
static struct audio_pcm_ops alsa_pcm_ops = {
|
||||||
.init_out = alsa_init_out,
|
.init_out = alsa_init_out,
|
||||||
.fini_out = alsa_fini_out,
|
.fini_out = alsa_fini_out,
|
||||||
|
@ -1216,7 +1085,6 @@ static struct audio_pcm_ops alsa_pcm_ops = {
|
||||||
static struct audio_driver alsa_audio_driver = {
|
static struct audio_driver alsa_audio_driver = {
|
||||||
.name = "alsa",
|
.name = "alsa",
|
||||||
.descr = "ALSA http://www.alsa-project.org",
|
.descr = "ALSA http://www.alsa-project.org",
|
||||||
.options = alsa_options,
|
|
||||||
.init = alsa_audio_init,
|
.init = alsa_audio_init,
|
||||||
.fini = alsa_audio_fini,
|
.fini = alsa_audio_fini,
|
||||||
.pcm_ops = &alsa_pcm_ops,
|
.pcm_ops = &alsa_pcm_ops,
|
||||||
|
|
926
audio/audio.c
926
audio/audio.c
File diff suppressed because it is too large
Load Diff
|
@ -26,30 +26,31 @@
|
||||||
#define QEMU_AUDIO_H
|
#define QEMU_AUDIO_H
|
||||||
|
|
||||||
#include "qemu/queue.h"
|
#include "qemu/queue.h"
|
||||||
|
#include "qapi/qapi-types-audio.h"
|
||||||
|
|
||||||
typedef void (*audio_callback_fn) (void *opaque, int avail);
|
typedef void (*audio_callback_fn) (void *opaque, int avail);
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
AUD_FMT_U8,
|
|
||||||
AUD_FMT_S8,
|
|
||||||
AUD_FMT_U16,
|
|
||||||
AUD_FMT_S16,
|
|
||||||
AUD_FMT_U32,
|
|
||||||
AUD_FMT_S32
|
|
||||||
} audfmt_e;
|
|
||||||
|
|
||||||
#ifdef HOST_WORDS_BIGENDIAN
|
#ifdef HOST_WORDS_BIGENDIAN
|
||||||
#define AUDIO_HOST_ENDIANNESS 1
|
#define AUDIO_HOST_ENDIANNESS 1
|
||||||
#else
|
#else
|
||||||
#define AUDIO_HOST_ENDIANNESS 0
|
#define AUDIO_HOST_ENDIANNESS 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct audsettings {
|
typedef struct audsettings {
|
||||||
int freq;
|
int freq;
|
||||||
int nchannels;
|
int nchannels;
|
||||||
audfmt_e fmt;
|
AudioFormat fmt;
|
||||||
int endianness;
|
int endianness;
|
||||||
};
|
} audsettings;
|
||||||
|
|
||||||
|
audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo);
|
||||||
|
int audioformat_bytes_per_sample(AudioFormat fmt);
|
||||||
|
int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
|
||||||
|
audsettings *as, int def_usecs);
|
||||||
|
int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
|
||||||
|
audsettings *as, int def_usecs);
|
||||||
|
int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
|
||||||
|
audsettings *as, int def_usecs);
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
AUD_CNOTIFY_ENABLE,
|
AUD_CNOTIFY_ENABLE,
|
||||||
|
@ -89,7 +90,6 @@ typedef struct QEMUAudioTimeStamp {
|
||||||
void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0);
|
void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0);
|
||||||
void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
|
void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
|
||||||
|
|
||||||
void AUD_help (void);
|
|
||||||
void AUD_register_card (const char *name, QEMUSoundCard *card);
|
void AUD_register_card (const char *name, QEMUSoundCard *card);
|
||||||
void AUD_remove_card (QEMUSoundCard *card);
|
void AUD_remove_card (QEMUSoundCard *card);
|
||||||
CaptureVoiceOut *AUD_add_capture (
|
CaptureVoiceOut *AUD_add_capture (
|
||||||
|
@ -171,4 +171,8 @@ void audio_sample_to_uint64(void *samples, int pos,
|
||||||
void audio_sample_from_uint64(void *samples, int pos,
|
void audio_sample_from_uint64(void *samples, int pos,
|
||||||
uint64_t left, uint64_t right);
|
uint64_t left, uint64_t right);
|
||||||
|
|
||||||
|
void audio_parse_option(const char *opt);
|
||||||
|
void audio_init_audiodevs(void);
|
||||||
|
void audio_legacy_help(void);
|
||||||
|
|
||||||
#endif /* QEMU_AUDIO_H */
|
#endif /* QEMU_AUDIO_H */
|
||||||
|
|
|
@ -33,22 +33,6 @@
|
||||||
|
|
||||||
struct audio_pcm_ops;
|
struct audio_pcm_ops;
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
AUD_OPT_INT,
|
|
||||||
AUD_OPT_FMT,
|
|
||||||
AUD_OPT_STR,
|
|
||||||
AUD_OPT_BOOL
|
|
||||||
} audio_option_tag_e;
|
|
||||||
|
|
||||||
struct audio_option {
|
|
||||||
const char *name;
|
|
||||||
audio_option_tag_e tag;
|
|
||||||
void *valp;
|
|
||||||
const char *descr;
|
|
||||||
int *overriddenp;
|
|
||||||
int overridden;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct audio_callback {
|
struct audio_callback {
|
||||||
void *opaque;
|
void *opaque;
|
||||||
audio_callback_fn fn;
|
audio_callback_fn fn;
|
||||||
|
@ -145,8 +129,7 @@ typedef struct audio_driver audio_driver;
|
||||||
struct audio_driver {
|
struct audio_driver {
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *descr;
|
const char *descr;
|
||||||
struct audio_option *options;
|
void *(*init) (Audiodev *);
|
||||||
void *(*init) (void);
|
|
||||||
void (*fini) (void *);
|
void (*fini) (void *);
|
||||||
struct audio_pcm_ops *pcm_ops;
|
struct audio_pcm_ops *pcm_ops;
|
||||||
int can_be_default;
|
int can_be_default;
|
||||||
|
@ -191,8 +174,9 @@ struct SWVoiceCap {
|
||||||
QLIST_ENTRY (SWVoiceCap) entries;
|
QLIST_ENTRY (SWVoiceCap) entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AudioState {
|
typedef struct AudioState {
|
||||||
struct audio_driver *drv;
|
struct audio_driver *drv;
|
||||||
|
Audiodev *dev;
|
||||||
void *drv_opaque;
|
void *drv_opaque;
|
||||||
|
|
||||||
QEMUTimer *ts;
|
QEMUTimer *ts;
|
||||||
|
@ -203,10 +187,13 @@ struct AudioState {
|
||||||
int nb_hw_voices_out;
|
int nb_hw_voices_out;
|
||||||
int nb_hw_voices_in;
|
int nb_hw_voices_in;
|
||||||
int vm_running;
|
int vm_running;
|
||||||
};
|
int64_t period_ticks;
|
||||||
|
} AudioState;
|
||||||
|
|
||||||
extern const struct mixeng_volume nominal_volume;
|
extern const struct mixeng_volume nominal_volume;
|
||||||
|
|
||||||
|
extern const char *audio_prio_list[];
|
||||||
|
|
||||||
void audio_driver_register(audio_driver *drv);
|
void audio_driver_register(audio_driver *drv);
|
||||||
audio_driver *audio_driver_lookup(const char *name);
|
audio_driver *audio_driver_lookup(const char *name);
|
||||||
|
|
||||||
|
@ -248,4 +235,18 @@ static inline int audio_ring_dist (int dst, int src, int len)
|
||||||
#define AUDIO_STRINGIFY_(n) #n
|
#define AUDIO_STRINGIFY_(n) #n
|
||||||
#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n)
|
#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n)
|
||||||
|
|
||||||
|
typedef struct AudiodevListEntry {
|
||||||
|
Audiodev *dev;
|
||||||
|
QSIMPLEQ_ENTRY(AudiodevListEntry) next;
|
||||||
|
} AudiodevListEntry;
|
||||||
|
|
||||||
|
typedef QSIMPLEQ_HEAD(, AudiodevListEntry) AudiodevListHead;
|
||||||
|
AudiodevListHead audio_handle_legacy_opts(void);
|
||||||
|
|
||||||
|
void audio_free_audiodev_list(AudiodevListHead *head);
|
||||||
|
|
||||||
|
void audio_create_pdos(Audiodev *dev);
|
||||||
|
AudiodevPerDirectionOptions *audio_get_pdo_in(Audiodev *dev);
|
||||||
|
AudiodevPerDirectionOptions *audio_get_pdo_out(Audiodev *dev);
|
||||||
|
|
||||||
#endif /* QEMU_AUDIO_INT_H */
|
#endif /* QEMU_AUDIO_INT_H */
|
||||||
|
|
|
@ -0,0 +1,550 @@
|
||||||
|
/*
|
||||||
|
* QEMU Audio subsystem: legacy configuration handling
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015-2019 Zoltán Kővágó <DirtY.iCE.hu@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "audio.h"
|
||||||
|
#include "audio_int.h"
|
||||||
|
#include "qemu-common.h"
|
||||||
|
#include "qemu/cutils.h"
|
||||||
|
#include "qemu/timer.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qapi/qapi-visit-audio.h"
|
||||||
|
#include "qapi/visitor-impl.h"
|
||||||
|
|
||||||
|
#define AUDIO_CAP "audio-legacy"
|
||||||
|
#include "audio_int.h"
|
||||||
|
|
||||||
|
static uint32_t toui32(const char *str)
|
||||||
|
{
|
||||||
|
unsigned long long ret;
|
||||||
|
if (parse_uint_full(str, &ret, 10) || ret > UINT32_MAX) {
|
||||||
|
dolog("Invalid integer value `%s'\n", str);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper functions to convert env variables */
|
||||||
|
static void get_bool(const char *env, bool *dst, bool *has_dst)
|
||||||
|
{
|
||||||
|
const char *val = getenv(env);
|
||||||
|
if (val) {
|
||||||
|
*dst = toui32(val) != 0;
|
||||||
|
*has_dst = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_int(const char *env, uint32_t *dst, bool *has_dst)
|
||||||
|
{
|
||||||
|
const char *val = getenv(env);
|
||||||
|
if (val) {
|
||||||
|
*dst = toui32(val);
|
||||||
|
*has_dst = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_str(const char *env, char **dst, bool *has_dst)
|
||||||
|
{
|
||||||
|
const char *val = getenv(env);
|
||||||
|
if (val) {
|
||||||
|
if (*has_dst) {
|
||||||
|
g_free(*dst);
|
||||||
|
}
|
||||||
|
*dst = g_strdup(val);
|
||||||
|
*has_dst = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_fmt(const char *env, AudioFormat *dst, bool *has_dst)
|
||||||
|
{
|
||||||
|
const char *val = getenv(env);
|
||||||
|
if (val) {
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; AudioFormat_lookup.size; ++i) {
|
||||||
|
if (strcasecmp(val, AudioFormat_lookup.array[i]) == 0) {
|
||||||
|
*dst = i;
|
||||||
|
*has_dst = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dolog("Invalid audio format `%s'\n", val);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void get_millis_to_usecs(const char *env, uint32_t *dst, bool *has_dst)
|
||||||
|
{
|
||||||
|
const char *val = getenv(env);
|
||||||
|
if (val) {
|
||||||
|
*dst = toui32(val) * 1000;
|
||||||
|
*has_dst = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t frames_to_usecs(uint32_t frames,
|
||||||
|
AudiodevPerDirectionOptions *pdo)
|
||||||
|
{
|
||||||
|
uint32_t freq = pdo->has_frequency ? pdo->frequency : 44100;
|
||||||
|
return (frames * 1000000 + freq / 2) / freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void get_frames_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
|
||||||
|
AudiodevPerDirectionOptions *pdo)
|
||||||
|
{
|
||||||
|
const char *val = getenv(env);
|
||||||
|
if (val) {
|
||||||
|
*dst = frames_to_usecs(toui32(val), pdo);
|
||||||
|
*has_dst = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t samples_to_usecs(uint32_t samples,
|
||||||
|
AudiodevPerDirectionOptions *pdo)
|
||||||
|
{
|
||||||
|
uint32_t channels = pdo->has_channels ? pdo->channels : 2;
|
||||||
|
return frames_to_usecs(samples / channels, pdo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_samples_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
|
||||||
|
AudiodevPerDirectionOptions *pdo)
|
||||||
|
{
|
||||||
|
const char *val = getenv(env);
|
||||||
|
if (val) {
|
||||||
|
*dst = samples_to_usecs(toui32(val), pdo);
|
||||||
|
*has_dst = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t bytes_to_usecs(uint32_t bytes, AudiodevPerDirectionOptions *pdo)
|
||||||
|
{
|
||||||
|
AudioFormat fmt = pdo->has_format ? pdo->format : AUDIO_FORMAT_S16;
|
||||||
|
uint32_t bytes_per_sample = audioformat_bytes_per_sample(fmt);
|
||||||
|
return samples_to_usecs(bytes / bytes_per_sample, pdo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_bytes_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
|
||||||
|
AudiodevPerDirectionOptions *pdo)
|
||||||
|
{
|
||||||
|
const char *val = getenv(env);
|
||||||
|
if (val) {
|
||||||
|
*dst = bytes_to_usecs(toui32(val), pdo);
|
||||||
|
*has_dst = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* backend specific functions */
|
||||||
|
/* ALSA */
|
||||||
|
static void handle_alsa_per_direction(
|
||||||
|
AudiodevAlsaPerDirectionOptions *apdo, const char *prefix)
|
||||||
|
{
|
||||||
|
char buf[64];
|
||||||
|
size_t len = strlen(prefix);
|
||||||
|
bool size_in_usecs = false;
|
||||||
|
bool dummy;
|
||||||
|
|
||||||
|
memcpy(buf, prefix, len);
|
||||||
|
strcpy(buf + len, "TRY_POLL");
|
||||||
|
get_bool(buf, &apdo->try_poll, &apdo->has_try_poll);
|
||||||
|
|
||||||
|
strcpy(buf + len, "DEV");
|
||||||
|
get_str(buf, &apdo->dev, &apdo->has_dev);
|
||||||
|
|
||||||
|
strcpy(buf + len, "SIZE_IN_USEC");
|
||||||
|
get_bool(buf, &size_in_usecs, &dummy);
|
||||||
|
|
||||||
|
strcpy(buf + len, "PERIOD_SIZE");
|
||||||
|
get_int(buf, &apdo->period_length, &apdo->has_period_length);
|
||||||
|
if (apdo->has_period_length && !size_in_usecs) {
|
||||||
|
apdo->period_length = frames_to_usecs(
|
||||||
|
apdo->period_length,
|
||||||
|
qapi_AudiodevAlsaPerDirectionOptions_base(apdo));
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(buf + len, "BUFFER_SIZE");
|
||||||
|
get_int(buf, &apdo->buffer_length, &apdo->has_buffer_length);
|
||||||
|
if (apdo->has_buffer_length && !size_in_usecs) {
|
||||||
|
apdo->buffer_length = frames_to_usecs(
|
||||||
|
apdo->buffer_length,
|
||||||
|
qapi_AudiodevAlsaPerDirectionOptions_base(apdo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_alsa(Audiodev *dev)
|
||||||
|
{
|
||||||
|
AudiodevAlsaOptions *aopt = &dev->u.alsa;
|
||||||
|
handle_alsa_per_direction(aopt->in, "QEMU_ALSA_ADC_");
|
||||||
|
handle_alsa_per_direction(aopt->out, "QEMU_ALSA_DAC_");
|
||||||
|
|
||||||
|
get_millis_to_usecs("QEMU_ALSA_THRESHOLD",
|
||||||
|
&aopt->threshold, &aopt->has_threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* coreaudio */
|
||||||
|
static void handle_coreaudio(Audiodev *dev)
|
||||||
|
{
|
||||||
|
get_frames_to_usecs(
|
||||||
|
"QEMU_COREAUDIO_BUFFER_SIZE",
|
||||||
|
&dev->u.coreaudio.out->buffer_length,
|
||||||
|
&dev->u.coreaudio.out->has_buffer_length,
|
||||||
|
qapi_AudiodevCoreaudioPerDirectionOptions_base(dev->u.coreaudio.out));
|
||||||
|
get_int("QEMU_COREAUDIO_BUFFER_COUNT",
|
||||||
|
&dev->u.coreaudio.out->buffer_count,
|
||||||
|
&dev->u.coreaudio.out->has_buffer_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dsound */
|
||||||
|
static void handle_dsound(Audiodev *dev)
|
||||||
|
{
|
||||||
|
get_millis_to_usecs("QEMU_DSOUND_LATENCY_MILLIS",
|
||||||
|
&dev->u.dsound.latency, &dev->u.dsound.has_latency);
|
||||||
|
get_bytes_to_usecs("QEMU_DSOUND_BUFSIZE_OUT",
|
||||||
|
&dev->u.dsound.out->buffer_length,
|
||||||
|
&dev->u.dsound.out->has_buffer_length,
|
||||||
|
dev->u.dsound.out);
|
||||||
|
get_bytes_to_usecs("QEMU_DSOUND_BUFSIZE_IN",
|
||||||
|
&dev->u.dsound.in->buffer_length,
|
||||||
|
&dev->u.dsound.in->has_buffer_length,
|
||||||
|
dev->u.dsound.in);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OSS */
|
||||||
|
static void handle_oss_per_direction(
|
||||||
|
AudiodevOssPerDirectionOptions *opdo, const char *try_poll_env,
|
||||||
|
const char *dev_env)
|
||||||
|
{
|
||||||
|
get_bool(try_poll_env, &opdo->try_poll, &opdo->has_try_poll);
|
||||||
|
get_str(dev_env, &opdo->dev, &opdo->has_dev);
|
||||||
|
|
||||||
|
get_bytes_to_usecs("QEMU_OSS_FRAGSIZE",
|
||||||
|
&opdo->buffer_length, &opdo->has_buffer_length,
|
||||||
|
qapi_AudiodevOssPerDirectionOptions_base(opdo));
|
||||||
|
get_int("QEMU_OSS_NFRAGS", &opdo->buffer_count,
|
||||||
|
&opdo->has_buffer_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_oss(Audiodev *dev)
|
||||||
|
{
|
||||||
|
AudiodevOssOptions *oopt = &dev->u.oss;
|
||||||
|
handle_oss_per_direction(oopt->in, "QEMU_AUDIO_ADC_TRY_POLL",
|
||||||
|
"QEMU_OSS_ADC_DEV");
|
||||||
|
handle_oss_per_direction(oopt->out, "QEMU_AUDIO_DAC_TRY_POLL",
|
||||||
|
"QEMU_OSS_DAC_DEV");
|
||||||
|
|
||||||
|
get_bool("QEMU_OSS_MMAP", &oopt->try_mmap, &oopt->has_try_mmap);
|
||||||
|
get_bool("QEMU_OSS_EXCLUSIVE", &oopt->exclusive, &oopt->has_exclusive);
|
||||||
|
get_int("QEMU_OSS_POLICY", &oopt->dsp_policy, &oopt->has_dsp_policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pulseaudio */
|
||||||
|
static void handle_pa_per_direction(
|
||||||
|
AudiodevPaPerDirectionOptions *ppdo, const char *env)
|
||||||
|
{
|
||||||
|
get_str(env, &ppdo->name, &ppdo->has_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_pa(Audiodev *dev)
|
||||||
|
{
|
||||||
|
handle_pa_per_direction(dev->u.pa.in, "QEMU_PA_SOURCE");
|
||||||
|
handle_pa_per_direction(dev->u.pa.out, "QEMU_PA_SINK");
|
||||||
|
|
||||||
|
get_samples_to_usecs(
|
||||||
|
"QEMU_PA_SAMPLES", &dev->u.pa.in->buffer_length,
|
||||||
|
&dev->u.pa.in->has_buffer_length,
|
||||||
|
qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.in));
|
||||||
|
get_samples_to_usecs(
|
||||||
|
"QEMU_PA_SAMPLES", &dev->u.pa.out->buffer_length,
|
||||||
|
&dev->u.pa.out->has_buffer_length,
|
||||||
|
qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.out));
|
||||||
|
|
||||||
|
get_str("QEMU_PA_SERVER", &dev->u.pa.server, &dev->u.pa.has_server);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SDL */
|
||||||
|
static void handle_sdl(Audiodev *dev)
|
||||||
|
{
|
||||||
|
/* SDL is output only */
|
||||||
|
get_samples_to_usecs("QEMU_SDL_SAMPLES", &dev->u.sdl.out->buffer_length,
|
||||||
|
&dev->u.sdl.out->has_buffer_length, dev->u.sdl.out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* wav */
|
||||||
|
static void handle_wav(Audiodev *dev)
|
||||||
|
{
|
||||||
|
get_int("QEMU_WAV_FREQUENCY",
|
||||||
|
&dev->u.wav.out->frequency, &dev->u.wav.out->has_frequency);
|
||||||
|
get_fmt("QEMU_WAV_FORMAT", &dev->u.wav.out->format,
|
||||||
|
&dev->u.wav.out->has_format);
|
||||||
|
get_int("QEMU_WAV_DAC_FIXED_CHANNELS",
|
||||||
|
&dev->u.wav.out->channels, &dev->u.wav.out->has_channels);
|
||||||
|
get_str("QEMU_WAV_PATH", &dev->u.wav.path, &dev->u.wav.has_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* general */
|
||||||
|
static void handle_per_direction(
|
||||||
|
AudiodevPerDirectionOptions *pdo, const char *prefix)
|
||||||
|
{
|
||||||
|
char buf[64];
|
||||||
|
size_t len = strlen(prefix);
|
||||||
|
|
||||||
|
memcpy(buf, prefix, len);
|
||||||
|
strcpy(buf + len, "FIXED_SETTINGS");
|
||||||
|
get_bool(buf, &pdo->fixed_settings, &pdo->has_fixed_settings);
|
||||||
|
|
||||||
|
strcpy(buf + len, "FIXED_FREQ");
|
||||||
|
get_int(buf, &pdo->frequency, &pdo->has_frequency);
|
||||||
|
|
||||||
|
strcpy(buf + len, "FIXED_FMT");
|
||||||
|
get_fmt(buf, &pdo->format, &pdo->has_format);
|
||||||
|
|
||||||
|
strcpy(buf + len, "FIXED_CHANNELS");
|
||||||
|
get_int(buf, &pdo->channels, &pdo->has_channels);
|
||||||
|
|
||||||
|
strcpy(buf + len, "VOICES");
|
||||||
|
get_int(buf, &pdo->voices, &pdo->has_voices);
|
||||||
|
}
|
||||||
|
|
||||||
|
static AudiodevListEntry *legacy_opt(const char *drvname)
|
||||||
|
{
|
||||||
|
AudiodevListEntry *e = g_malloc0(sizeof(AudiodevListEntry));
|
||||||
|
e->dev = g_malloc0(sizeof(Audiodev));
|
||||||
|
e->dev->id = g_strdup(drvname);
|
||||||
|
e->dev->driver = qapi_enum_parse(
|
||||||
|
&AudiodevDriver_lookup, drvname, -1, &error_abort);
|
||||||
|
|
||||||
|
audio_create_pdos(e->dev);
|
||||||
|
|
||||||
|
handle_per_direction(audio_get_pdo_in(e->dev), "QEMU_AUDIO_ADC_");
|
||||||
|
handle_per_direction(audio_get_pdo_out(e->dev), "QEMU_AUDIO_DAC_");
|
||||||
|
|
||||||
|
/* Original description: Timer period in HZ (0 - use lowest possible) */
|
||||||
|
get_int("QEMU_AUDIO_TIMER_PERIOD",
|
||||||
|
&e->dev->timer_period, &e->dev->has_timer_period);
|
||||||
|
if (e->dev->has_timer_period && e->dev->timer_period) {
|
||||||
|
e->dev->timer_period = NANOSECONDS_PER_SECOND / 1000 /
|
||||||
|
e->dev->timer_period;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (e->dev->driver) {
|
||||||
|
case AUDIODEV_DRIVER_ALSA:
|
||||||
|
handle_alsa(e->dev);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUDIODEV_DRIVER_COREAUDIO:
|
||||||
|
handle_coreaudio(e->dev);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUDIODEV_DRIVER_DSOUND:
|
||||||
|
handle_dsound(e->dev);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUDIODEV_DRIVER_OSS:
|
||||||
|
handle_oss(e->dev);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUDIODEV_DRIVER_PA:
|
||||||
|
handle_pa(e->dev);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUDIODEV_DRIVER_SDL:
|
||||||
|
handle_sdl(e->dev);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUDIODEV_DRIVER_WAV:
|
||||||
|
handle_wav(e->dev);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudiodevListHead audio_handle_legacy_opts(void)
|
||||||
|
{
|
||||||
|
const char *drvname = getenv("QEMU_AUDIO_DRV");
|
||||||
|
AudiodevListHead head = QSIMPLEQ_HEAD_INITIALIZER(head);
|
||||||
|
|
||||||
|
if (drvname) {
|
||||||
|
AudiodevListEntry *e;
|
||||||
|
audio_driver *driver = audio_driver_lookup(drvname);
|
||||||
|
if (!driver) {
|
||||||
|
dolog("Unknown audio driver `%s'\n", drvname);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
e = legacy_opt(drvname);
|
||||||
|
QSIMPLEQ_INSERT_TAIL(&head, e, next);
|
||||||
|
} else {
|
||||||
|
for (int i = 0; audio_prio_list[i]; i++) {
|
||||||
|
audio_driver *driver = audio_driver_lookup(audio_prio_list[i]);
|
||||||
|
if (driver && driver->can_be_default) {
|
||||||
|
AudiodevListEntry *e = legacy_opt(driver->name);
|
||||||
|
QSIMPLEQ_INSERT_TAIL(&head, e, next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (QSIMPLEQ_EMPTY(&head)) {
|
||||||
|
dolog("Internal error: no default audio driver available\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* visitor to print -audiodev option */
|
||||||
|
typedef struct {
|
||||||
|
Visitor visitor;
|
||||||
|
|
||||||
|
bool comma;
|
||||||
|
GList *path;
|
||||||
|
} LegacyPrintVisitor;
|
||||||
|
|
||||||
|
static void lv_start_struct(Visitor *v, const char *name, void **obj,
|
||||||
|
size_t size, Error **errp)
|
||||||
|
{
|
||||||
|
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
|
||||||
|
lv->path = g_list_append(lv->path, g_strdup(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lv_end_struct(Visitor *v, void **obj)
|
||||||
|
{
|
||||||
|
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
|
||||||
|
lv->path = g_list_delete_link(lv->path, g_list_last(lv->path));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lv_print_key(Visitor *v, const char *name)
|
||||||
|
{
|
||||||
|
GList *e;
|
||||||
|
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
|
||||||
|
if (lv->comma) {
|
||||||
|
putchar(',');
|
||||||
|
} else {
|
||||||
|
lv->comma = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (e = lv->path; e; e = e->next) {
|
||||||
|
if (e->data) {
|
||||||
|
printf("%s.", (const char *) e->data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s=", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lv_type_int64(Visitor *v, const char *name, int64_t *obj,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
lv_print_key(v, name);
|
||||||
|
printf("%" PRIi64, *obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lv_type_uint64(Visitor *v, const char *name, uint64_t *obj,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
lv_print_key(v, name);
|
||||||
|
printf("%" PRIu64, *obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lv_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
|
||||||
|
{
|
||||||
|
lv_print_key(v, name);
|
||||||
|
printf("%s", *obj ? "on" : "off");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lv_type_str(Visitor *v, const char *name, char **obj, Error **errp)
|
||||||
|
{
|
||||||
|
const char *str = *obj;
|
||||||
|
lv_print_key(v, name);
|
||||||
|
|
||||||
|
while (*str) {
|
||||||
|
if (*str == ',') {
|
||||||
|
putchar(',');
|
||||||
|
}
|
||||||
|
putchar(*str++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lv_complete(Visitor *v, void *opaque)
|
||||||
|
{
|
||||||
|
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
|
||||||
|
assert(lv->path == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lv_free(Visitor *v)
|
||||||
|
{
|
||||||
|
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
|
||||||
|
|
||||||
|
g_list_free_full(lv->path, g_free);
|
||||||
|
g_free(lv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Visitor *legacy_visitor_new(void)
|
||||||
|
{
|
||||||
|
LegacyPrintVisitor *lv = g_malloc0(sizeof(LegacyPrintVisitor));
|
||||||
|
|
||||||
|
lv->visitor.start_struct = lv_start_struct;
|
||||||
|
lv->visitor.end_struct = lv_end_struct;
|
||||||
|
/* lists not supported */
|
||||||
|
lv->visitor.type_int64 = lv_type_int64;
|
||||||
|
lv->visitor.type_uint64 = lv_type_uint64;
|
||||||
|
lv->visitor.type_bool = lv_type_bool;
|
||||||
|
lv->visitor.type_str = lv_type_str;
|
||||||
|
|
||||||
|
lv->visitor.type = VISITOR_OUTPUT;
|
||||||
|
lv->visitor.complete = lv_complete;
|
||||||
|
lv->visitor.free = lv_free;
|
||||||
|
|
||||||
|
return &lv->visitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_legacy_help(void)
|
||||||
|
{
|
||||||
|
AudiodevListHead head;
|
||||||
|
AudiodevListEntry *e;
|
||||||
|
|
||||||
|
printf("Environment variable based configuration deprecated.\n");
|
||||||
|
printf("Please use the new -audiodev option.\n");
|
||||||
|
|
||||||
|
head = audio_handle_legacy_opts();
|
||||||
|
printf("\nEquivalent -audiodev to your current environment variables:\n");
|
||||||
|
if (!getenv("QEMU_AUDIO_DRV")) {
|
||||||
|
printf("(Since you didn't specify QEMU_AUDIO_DRV, I'll list all "
|
||||||
|
"possibilities)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
QSIMPLEQ_FOREACH(e, &head, next) {
|
||||||
|
Visitor *v;
|
||||||
|
Audiodev *dev = e->dev;
|
||||||
|
printf("-audiodev ");
|
||||||
|
|
||||||
|
v = legacy_visitor_new();
|
||||||
|
visit_type_Audiodev(v, NULL, &dev, &error_abort);
|
||||||
|
visit_free(v);
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
audio_free_audiodev_list(&head);
|
||||||
|
}
|
|
@ -299,11 +299,42 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
|
||||||
|
{
|
||||||
|
switch (dev->driver) {
|
||||||
|
case AUDIODEV_DRIVER_NONE:
|
||||||
|
return dev->u.none.TYPE;
|
||||||
|
case AUDIODEV_DRIVER_ALSA:
|
||||||
|
return qapi_AudiodevAlsaPerDirectionOptions_base(dev->u.alsa.TYPE);
|
||||||
|
case AUDIODEV_DRIVER_COREAUDIO:
|
||||||
|
return qapi_AudiodevCoreaudioPerDirectionOptions_base(
|
||||||
|
dev->u.coreaudio.TYPE);
|
||||||
|
case AUDIODEV_DRIVER_DSOUND:
|
||||||
|
return dev->u.dsound.TYPE;
|
||||||
|
case AUDIODEV_DRIVER_OSS:
|
||||||
|
return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.TYPE);
|
||||||
|
case AUDIODEV_DRIVER_PA:
|
||||||
|
return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE);
|
||||||
|
case AUDIODEV_DRIVER_SDL:
|
||||||
|
return dev->u.sdl.TYPE;
|
||||||
|
case AUDIODEV_DRIVER_SPICE:
|
||||||
|
return dev->u.spice.TYPE;
|
||||||
|
case AUDIODEV_DRIVER_WAV:
|
||||||
|
return dev->u.wav.TYPE;
|
||||||
|
|
||||||
|
case AUDIODEV_DRIVER__MAX:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as)
|
static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as)
|
||||||
{
|
{
|
||||||
HW *hw;
|
HW *hw;
|
||||||
|
AudioState *s = &glob_audio_state;
|
||||||
|
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
|
||||||
|
|
||||||
if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) {
|
if (pdo->fixed_settings) {
|
||||||
hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
|
hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
|
||||||
if (hw) {
|
if (hw) {
|
||||||
return hw;
|
return hw;
|
||||||
|
@ -331,9 +362,11 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
|
||||||
SW *sw;
|
SW *sw;
|
||||||
HW *hw;
|
HW *hw;
|
||||||
struct audsettings hw_as;
|
struct audsettings hw_as;
|
||||||
|
AudioState *s = &glob_audio_state;
|
||||||
|
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
|
||||||
|
|
||||||
if (glue (conf.fixed_, TYPE).enabled) {
|
if (pdo->fixed_settings) {
|
||||||
hw_as = glue (conf.fixed_, TYPE).settings;
|
hw_as = audiodev_to_audsettings(pdo);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
hw_as = *as;
|
hw_as = *as;
|
||||||
|
@ -398,6 +431,7 @@ SW *glue (AUD_open_, TYPE) (
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
AudioState *s = &glob_audio_state;
|
AudioState *s = &glob_audio_state;
|
||||||
|
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
|
||||||
|
|
||||||
if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
|
if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
|
||||||
dolog ("card=%p name=%p callback_fn=%p as=%p\n",
|
dolog ("card=%p name=%p callback_fn=%p as=%p\n",
|
||||||
|
@ -422,7 +456,7 @@ SW *glue (AUD_open_, TYPE) (
|
||||||
return sw;
|
return sw;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!glue (conf.fixed_, TYPE).enabled && sw) {
|
if (!pdo->fixed_settings && sw) {
|
||||||
glue (AUD_close_, TYPE) (card, sw);
|
glue (AUD_close_, TYPE) (card, sw);
|
||||||
sw = NULL;
|
sw = NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,20 +24,20 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx,
|
||||||
wfx->cbSize = 0;
|
wfx->cbSize = 0;
|
||||||
|
|
||||||
switch (as->fmt) {
|
switch (as->fmt) {
|
||||||
case AUD_FMT_S8:
|
case AUDIO_FORMAT_S8:
|
||||||
case AUD_FMT_U8:
|
case AUDIO_FORMAT_U8:
|
||||||
wfx->wBitsPerSample = 8;
|
wfx->wBitsPerSample = 8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AUD_FMT_S16:
|
case AUDIO_FORMAT_S16:
|
||||||
case AUD_FMT_U16:
|
case AUDIO_FORMAT_U16:
|
||||||
wfx->wBitsPerSample = 16;
|
wfx->wBitsPerSample = 16;
|
||||||
wfx->nAvgBytesPerSec <<= 1;
|
wfx->nAvgBytesPerSec <<= 1;
|
||||||
wfx->nBlockAlign <<= 1;
|
wfx->nBlockAlign <<= 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AUD_FMT_S32:
|
case AUDIO_FORMAT_S32:
|
||||||
case AUD_FMT_U32:
|
case AUDIO_FORMAT_U32:
|
||||||
wfx->wBitsPerSample = 32;
|
wfx->wBitsPerSample = 32;
|
||||||
wfx->nAvgBytesPerSec <<= 2;
|
wfx->nAvgBytesPerSec <<= 2;
|
||||||
wfx->nBlockAlign <<= 2;
|
wfx->nBlockAlign <<= 2;
|
||||||
|
@ -85,15 +85,15 @@ int waveformat_to_audio_settings (WAVEFORMATEX *wfx,
|
||||||
|
|
||||||
switch (wfx->wBitsPerSample) {
|
switch (wfx->wBitsPerSample) {
|
||||||
case 8:
|
case 8:
|
||||||
as->fmt = AUD_FMT_U8;
|
as->fmt = AUDIO_FORMAT_U8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 16:
|
case 16:
|
||||||
as->fmt = AUD_FMT_S16;
|
as->fmt = AUDIO_FORMAT_S16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 32:
|
case 32:
|
||||||
as->fmt = AUD_FMT_S32;
|
as->fmt = AUDIO_FORMAT_S32;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -36,11 +36,6 @@
|
||||||
#define MAC_OS_X_VERSION_10_6 1060
|
#define MAC_OS_X_VERSION_10_6 1060
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int buffer_frames;
|
|
||||||
int nbuffers;
|
|
||||||
} CoreaudioConf;
|
|
||||||
|
|
||||||
typedef struct coreaudioVoiceOut {
|
typedef struct coreaudioVoiceOut {
|
||||||
HWVoiceOut hw;
|
HWVoiceOut hw;
|
||||||
pthread_mutex_t mutex;
|
pthread_mutex_t mutex;
|
||||||
|
@ -507,7 +502,9 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
int err;
|
int err;
|
||||||
const char *typ = "playback";
|
const char *typ = "playback";
|
||||||
AudioValueRange frameRange;
|
AudioValueRange frameRange;
|
||||||
CoreaudioConf *conf = drv_opaque;
|
Audiodev *dev = drv_opaque;
|
||||||
|
AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
|
||||||
|
int frames;
|
||||||
|
|
||||||
/* create mutex */
|
/* create mutex */
|
||||||
err = pthread_mutex_init(&core->mutex, NULL);
|
err = pthread_mutex_init(&core->mutex, NULL);
|
||||||
|
@ -538,16 +535,17 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frameRange.mMinimum > conf->buffer_frames) {
|
frames = audio_buffer_frames(
|
||||||
|
qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
|
||||||
|
if (frameRange.mMinimum > frames) {
|
||||||
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
|
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
|
||||||
dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
|
dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
|
||||||
}
|
} else if (frameRange.mMaximum < frames) {
|
||||||
else if (frameRange.mMaximum < conf->buffer_frames) {
|
|
||||||
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
|
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
|
||||||
dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
|
dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
core->audioDevicePropertyBufferFrameSize = conf->buffer_frames;
|
core->audioDevicePropertyBufferFrameSize = frames;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set Buffer Frame Size */
|
/* set Buffer Frame Size */
|
||||||
|
@ -568,7 +566,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
"Could not get device buffer frame size\n");
|
"Could not get device buffer frame size\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
|
hw->samples = (cpdo->has_buffer_count ? cpdo->buffer_count : 4) *
|
||||||
|
core->audioDevicePropertyBufferFrameSize;
|
||||||
|
|
||||||
/* get StreamFormat */
|
/* get StreamFormat */
|
||||||
status = coreaudio_get_streamformat(core->outputDeviceID,
|
status = coreaudio_get_streamformat(core->outputDeviceID,
|
||||||
|
@ -680,40 +679,15 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CoreaudioConf glob_conf = {
|
static void *coreaudio_audio_init(Audiodev *dev)
|
||||||
.buffer_frames = 512,
|
|
||||||
.nbuffers = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void *coreaudio_audio_init (void)
|
|
||||||
{
|
{
|
||||||
CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf));
|
return dev;
|
||||||
*conf = glob_conf;
|
|
||||||
|
|
||||||
return conf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void coreaudio_audio_fini (void *opaque)
|
static void coreaudio_audio_fini (void *opaque)
|
||||||
{
|
{
|
||||||
g_free(opaque);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct audio_option coreaudio_options[] = {
|
|
||||||
{
|
|
||||||
.name = "BUFFER_SIZE",
|
|
||||||
.tag = AUD_OPT_INT,
|
|
||||||
.valp = &glob_conf.buffer_frames,
|
|
||||||
.descr = "Size of the buffer in frames"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "BUFFER_COUNT",
|
|
||||||
.tag = AUD_OPT_INT,
|
|
||||||
.valp = &glob_conf.nbuffers,
|
|
||||||
.descr = "Number of buffers"
|
|
||||||
},
|
|
||||||
{ /* End of list */ }
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct audio_pcm_ops coreaudio_pcm_ops = {
|
static struct audio_pcm_ops coreaudio_pcm_ops = {
|
||||||
.init_out = coreaudio_init_out,
|
.init_out = coreaudio_init_out,
|
||||||
.fini_out = coreaudio_fini_out,
|
.fini_out = coreaudio_fini_out,
|
||||||
|
@ -725,7 +699,6 @@ static struct audio_pcm_ops coreaudio_pcm_ops = {
|
||||||
static struct audio_driver coreaudio_audio_driver = {
|
static struct audio_driver coreaudio_audio_driver = {
|
||||||
.name = "coreaudio",
|
.name = "coreaudio",
|
||||||
.descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
|
.descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
|
||||||
.options = coreaudio_options,
|
|
||||||
.init = coreaudio_audio_init,
|
.init = coreaudio_audio_init,
|
||||||
.fini = coreaudio_audio_fini,
|
.fini = coreaudio_audio_fini,
|
||||||
.pcm_ops = &coreaudio_pcm_ops,
|
.pcm_ops = &coreaudio_pcm_ops,
|
||||||
|
|
|
@ -167,17 +167,18 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
dsound *s = drv_opaque;
|
dsound *s = drv_opaque;
|
||||||
WAVEFORMATEX wfx;
|
WAVEFORMATEX wfx;
|
||||||
struct audsettings obt_as;
|
struct audsettings obt_as;
|
||||||
DSoundConf *conf = &s->conf;
|
|
||||||
#ifdef DSBTYPE_IN
|
#ifdef DSBTYPE_IN
|
||||||
const char *typ = "ADC";
|
const char *typ = "ADC";
|
||||||
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
|
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
|
||||||
DSCBUFFERDESC bd;
|
DSCBUFFERDESC bd;
|
||||||
DSCBCAPS bc;
|
DSCBCAPS bc;
|
||||||
|
AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.in;
|
||||||
#else
|
#else
|
||||||
const char *typ = "DAC";
|
const char *typ = "DAC";
|
||||||
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
|
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
|
||||||
DSBUFFERDESC bd;
|
DSBUFFERDESC bd;
|
||||||
DSBCAPS bc;
|
DSBCAPS bc;
|
||||||
|
AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.out;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!s->FIELD2) {
|
if (!s->FIELD2) {
|
||||||
|
@ -193,8 +194,8 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
memset (&bd, 0, sizeof (bd));
|
memset (&bd, 0, sizeof (bd));
|
||||||
bd.dwSize = sizeof (bd);
|
bd.dwSize = sizeof (bd);
|
||||||
bd.lpwfxFormat = &wfx;
|
bd.lpwfxFormat = &wfx;
|
||||||
|
bd.dwBufferBytes = audio_buffer_bytes(pdo, as, 92880);
|
||||||
#ifdef DSBTYPE_IN
|
#ifdef DSBTYPE_IN
|
||||||
bd.dwBufferBytes = conf->bufsize_in;
|
|
||||||
hr = IDirectSoundCapture_CreateCaptureBuffer (
|
hr = IDirectSoundCapture_CreateCaptureBuffer (
|
||||||
s->dsound_capture,
|
s->dsound_capture,
|
||||||
&bd,
|
&bd,
|
||||||
|
@ -203,7 +204,6 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
);
|
);
|
||||||
#else
|
#else
|
||||||
bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
|
bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
|
||||||
bd.dwBufferBytes = conf->bufsize_out;
|
|
||||||
hr = IDirectSound_CreateSoundBuffer (
|
hr = IDirectSound_CreateSoundBuffer (
|
||||||
s->dsound,
|
s->dsound,
|
||||||
&bd,
|
&bd,
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
|
|
||||||
#define AUDIO_CAP "dsound"
|
#define AUDIO_CAP "dsound"
|
||||||
#include "audio_int.h"
|
#include "audio_int.h"
|
||||||
|
#include "qemu/host-utils.h"
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <mmsystem.h>
|
#include <mmsystem.h>
|
||||||
|
@ -42,17 +43,11 @@
|
||||||
|
|
||||||
/* #define DEBUG_DSOUND */
|
/* #define DEBUG_DSOUND */
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int bufsize_in;
|
|
||||||
int bufsize_out;
|
|
||||||
int latency_millis;
|
|
||||||
} DSoundConf;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
LPDIRECTSOUND dsound;
|
LPDIRECTSOUND dsound;
|
||||||
LPDIRECTSOUNDCAPTURE dsound_capture;
|
LPDIRECTSOUNDCAPTURE dsound_capture;
|
||||||
struct audsettings settings;
|
struct audsettings settings;
|
||||||
DSoundConf conf;
|
Audiodev *dev;
|
||||||
} dsound;
|
} dsound;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -248,9 +243,9 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
|
||||||
dsound_log_hresult (hr);
|
dsound_log_hresult (hr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis)
|
static uint64_t usecs_to_bytes(struct audio_pcm_info *info, uint32_t usecs)
|
||||||
{
|
{
|
||||||
return (millis * info->bytes_per_second) / 1000;
|
return muldiv64(usecs, info->bytes_per_second, 1000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_DSOUND
|
#ifdef DEBUG_DSOUND
|
||||||
|
@ -478,7 +473,7 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
|
||||||
LPVOID p1, p2;
|
LPVOID p1, p2;
|
||||||
int bufsize;
|
int bufsize;
|
||||||
dsound *s = ds->s;
|
dsound *s = ds->s;
|
||||||
DSoundConf *conf = &s->conf;
|
AudiodevDsoundOptions *dso = &s->dev->u.dsound;
|
||||||
|
|
||||||
if (!dsb) {
|
if (!dsb) {
|
||||||
dolog ("Attempt to run empty with playback buffer\n");
|
dolog ("Attempt to run empty with playback buffer\n");
|
||||||
|
@ -501,14 +496,14 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
|
||||||
len = live << hwshift;
|
len = live << hwshift;
|
||||||
|
|
||||||
if (ds->first_time) {
|
if (ds->first_time) {
|
||||||
if (conf->latency_millis) {
|
if (dso->latency) {
|
||||||
DWORD cur_blat;
|
DWORD cur_blat;
|
||||||
|
|
||||||
cur_blat = audio_ring_dist (wpos, ppos, bufsize);
|
cur_blat = audio_ring_dist (wpos, ppos, bufsize);
|
||||||
ds->first_time = 0;
|
ds->first_time = 0;
|
||||||
old_pos = wpos;
|
old_pos = wpos;
|
||||||
old_pos +=
|
old_pos +=
|
||||||
millis_to_bytes (&hw->info, conf->latency_millis) - cur_blat;
|
usecs_to_bytes(&hw->info, dso->latency) - cur_blat;
|
||||||
old_pos %= bufsize;
|
old_pos %= bufsize;
|
||||||
old_pos &= ~hw->info.align;
|
old_pos &= ~hw->info.align;
|
||||||
}
|
}
|
||||||
|
@ -747,12 +742,6 @@ static int dsound_run_in (HWVoiceIn *hw)
|
||||||
return decr;
|
return decr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DSoundConf glob_conf = {
|
|
||||||
.bufsize_in = 16384,
|
|
||||||
.bufsize_out = 16384,
|
|
||||||
.latency_millis = 10
|
|
||||||
};
|
|
||||||
|
|
||||||
static void dsound_audio_fini (void *opaque)
|
static void dsound_audio_fini (void *opaque)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
@ -783,13 +772,22 @@ static void dsound_audio_fini (void *opaque)
|
||||||
g_free(s);
|
g_free(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *dsound_audio_init (void)
|
static void *dsound_audio_init(Audiodev *dev)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
dsound *s = g_malloc0(sizeof(dsound));
|
dsound *s = g_malloc0(sizeof(dsound));
|
||||||
|
AudiodevDsoundOptions *dso;
|
||||||
|
|
||||||
|
assert(dev->driver == AUDIODEV_DRIVER_DSOUND);
|
||||||
|
s->dev = dev;
|
||||||
|
dso = &dev->u.dsound;
|
||||||
|
|
||||||
|
if (!dso->has_latency) {
|
||||||
|
dso->has_latency = true;
|
||||||
|
dso->latency = 10000; /* 10 ms */
|
||||||
|
}
|
||||||
|
|
||||||
s->conf = glob_conf;
|
|
||||||
hr = CoInitialize (NULL);
|
hr = CoInitialize (NULL);
|
||||||
if (FAILED (hr)) {
|
if (FAILED (hr)) {
|
||||||
dsound_logerr (hr, "Could not initialize COM\n");
|
dsound_logerr (hr, "Could not initialize COM\n");
|
||||||
|
@ -854,28 +852,6 @@ static void *dsound_audio_init (void)
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct audio_option dsound_options[] = {
|
|
||||||
{
|
|
||||||
.name = "LATENCY_MILLIS",
|
|
||||||
.tag = AUD_OPT_INT,
|
|
||||||
.valp = &glob_conf.latency_millis,
|
|
||||||
.descr = "(undocumented)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "BUFSIZE_OUT",
|
|
||||||
.tag = AUD_OPT_INT,
|
|
||||||
.valp = &glob_conf.bufsize_out,
|
|
||||||
.descr = "(undocumented)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "BUFSIZE_IN",
|
|
||||||
.tag = AUD_OPT_INT,
|
|
||||||
.valp = &glob_conf.bufsize_in,
|
|
||||||
.descr = "(undocumented)"
|
|
||||||
},
|
|
||||||
{ /* End of list */ }
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct audio_pcm_ops dsound_pcm_ops = {
|
static struct audio_pcm_ops dsound_pcm_ops = {
|
||||||
.init_out = dsound_init_out,
|
.init_out = dsound_init_out,
|
||||||
.fini_out = dsound_fini_out,
|
.fini_out = dsound_fini_out,
|
||||||
|
@ -893,7 +869,6 @@ static struct audio_pcm_ops dsound_pcm_ops = {
|
||||||
static struct audio_driver dsound_audio_driver = {
|
static struct audio_driver dsound_audio_driver = {
|
||||||
.name = "dsound",
|
.name = "dsound",
|
||||||
.descr = "DirectSound http://wikipedia.org/wiki/DirectSound",
|
.descr = "DirectSound http://wikipedia.org/wiki/DirectSound",
|
||||||
.options = dsound_options,
|
|
||||||
.init = dsound_audio_init,
|
.init = dsound_audio_init,
|
||||||
.fini = dsound_audio_fini,
|
.fini = dsound_audio_fini,
|
||||||
.pcm_ops = &dsound_pcm_ops,
|
.pcm_ops = &dsound_pcm_ops,
|
||||||
|
|
|
@ -136,7 +136,7 @@ static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *no_audio_init (void)
|
static void *no_audio_init(Audiodev *dev)
|
||||||
{
|
{
|
||||||
return &no_audio_init;
|
return &no_audio_init;
|
||||||
}
|
}
|
||||||
|
@ -163,7 +163,6 @@ static struct audio_pcm_ops no_pcm_ops = {
|
||||||
static struct audio_driver no_audio_driver = {
|
static struct audio_driver no_audio_driver = {
|
||||||
.name = "none",
|
.name = "none",
|
||||||
.descr = "Timer based audio emulation",
|
.descr = "Timer based audio emulation",
|
||||||
.options = NULL,
|
|
||||||
.init = no_audio_init,
|
.init = no_audio_init,
|
||||||
.fini = no_audio_fini,
|
.fini = no_audio_fini,
|
||||||
.pcm_ops = &no_pcm_ops,
|
.pcm_ops = &no_pcm_ops,
|
||||||
|
|
193
audio/ossaudio.c
193
audio/ossaudio.c
|
@ -37,16 +37,6 @@
|
||||||
#define USE_DSP_POLICY
|
#define USE_DSP_POLICY
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct OSSConf {
|
|
||||||
int try_mmap;
|
|
||||||
int nfrags;
|
|
||||||
int fragsize;
|
|
||||||
const char *devpath_out;
|
|
||||||
const char *devpath_in;
|
|
||||||
int exclusive;
|
|
||||||
int policy;
|
|
||||||
} OSSConf;
|
|
||||||
|
|
||||||
typedef struct OSSVoiceOut {
|
typedef struct OSSVoiceOut {
|
||||||
HWVoiceOut hw;
|
HWVoiceOut hw;
|
||||||
void *pcm_buf;
|
void *pcm_buf;
|
||||||
|
@ -56,7 +46,7 @@ typedef struct OSSVoiceOut {
|
||||||
int fragsize;
|
int fragsize;
|
||||||
int mmapped;
|
int mmapped;
|
||||||
int pending;
|
int pending;
|
||||||
OSSConf *conf;
|
Audiodev *dev;
|
||||||
} OSSVoiceOut;
|
} OSSVoiceOut;
|
||||||
|
|
||||||
typedef struct OSSVoiceIn {
|
typedef struct OSSVoiceIn {
|
||||||
|
@ -65,12 +55,12 @@ typedef struct OSSVoiceIn {
|
||||||
int fd;
|
int fd;
|
||||||
int nfrags;
|
int nfrags;
|
||||||
int fragsize;
|
int fragsize;
|
||||||
OSSConf *conf;
|
Audiodev *dev;
|
||||||
} OSSVoiceIn;
|
} OSSVoiceIn;
|
||||||
|
|
||||||
struct oss_params {
|
struct oss_params {
|
||||||
int freq;
|
int freq;
|
||||||
audfmt_e fmt;
|
int fmt;
|
||||||
int nchannels;
|
int nchannels;
|
||||||
int nfrags;
|
int nfrags;
|
||||||
int fragsize;
|
int fragsize;
|
||||||
|
@ -148,16 +138,16 @@ static int oss_write (SWVoiceOut *sw, void *buf, int len)
|
||||||
return audio_pcm_sw_write (sw, buf, len);
|
return audio_pcm_sw_write (sw, buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int aud_to_ossfmt (audfmt_e fmt, int endianness)
|
static int aud_to_ossfmt (AudioFormat fmt, int endianness)
|
||||||
{
|
{
|
||||||
switch (fmt) {
|
switch (fmt) {
|
||||||
case AUD_FMT_S8:
|
case AUDIO_FORMAT_S8:
|
||||||
return AFMT_S8;
|
return AFMT_S8;
|
||||||
|
|
||||||
case AUD_FMT_U8:
|
case AUDIO_FORMAT_U8:
|
||||||
return AFMT_U8;
|
return AFMT_U8;
|
||||||
|
|
||||||
case AUD_FMT_S16:
|
case AUDIO_FORMAT_S16:
|
||||||
if (endianness) {
|
if (endianness) {
|
||||||
return AFMT_S16_BE;
|
return AFMT_S16_BE;
|
||||||
}
|
}
|
||||||
|
@ -165,7 +155,7 @@ static int aud_to_ossfmt (audfmt_e fmt, int endianness)
|
||||||
return AFMT_S16_LE;
|
return AFMT_S16_LE;
|
||||||
}
|
}
|
||||||
|
|
||||||
case AUD_FMT_U16:
|
case AUDIO_FORMAT_U16:
|
||||||
if (endianness) {
|
if (endianness) {
|
||||||
return AFMT_U16_BE;
|
return AFMT_U16_BE;
|
||||||
}
|
}
|
||||||
|
@ -182,37 +172,37 @@ static int aud_to_ossfmt (audfmt_e fmt, int endianness)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
|
static int oss_to_audfmt (int ossfmt, AudioFormat *fmt, int *endianness)
|
||||||
{
|
{
|
||||||
switch (ossfmt) {
|
switch (ossfmt) {
|
||||||
case AFMT_S8:
|
case AFMT_S8:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_S8;
|
*fmt = AUDIO_FORMAT_S8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AFMT_U8:
|
case AFMT_U8:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_U8;
|
*fmt = AUDIO_FORMAT_U8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AFMT_S16_LE:
|
case AFMT_S16_LE:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_S16;
|
*fmt = AUDIO_FORMAT_S16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AFMT_U16_LE:
|
case AFMT_U16_LE:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_U16;
|
*fmt = AUDIO_FORMAT_U16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AFMT_S16_BE:
|
case AFMT_S16_BE:
|
||||||
*endianness = 1;
|
*endianness = 1;
|
||||||
*fmt = AUD_FMT_S16;
|
*fmt = AUDIO_FORMAT_S16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AFMT_U16_BE:
|
case AFMT_U16_BE:
|
||||||
*endianness = 1;
|
*endianness = 1;
|
||||||
*fmt = AUD_FMT_U16;
|
*fmt = AUDIO_FORMAT_U16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -262,19 +252,25 @@ static int oss_get_version (int fd, int *version, const char *typ)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int oss_open (int in, struct oss_params *req,
|
static int oss_open(int in, struct oss_params *req, audsettings *as,
|
||||||
struct oss_params *obt, int *pfd, OSSConf* conf)
|
struct oss_params *obt, int *pfd, Audiodev *dev)
|
||||||
{
|
{
|
||||||
|
AudiodevOssOptions *oopts = &dev->u.oss;
|
||||||
|
AudiodevOssPerDirectionOptions *opdo = in ? oopts->in : oopts->out;
|
||||||
int fd;
|
int fd;
|
||||||
int oflags = conf->exclusive ? O_EXCL : 0;
|
int oflags = (oopts->has_exclusive && oopts->exclusive) ? O_EXCL : 0;
|
||||||
audio_buf_info abinfo;
|
audio_buf_info abinfo;
|
||||||
int fmt, freq, nchannels;
|
int fmt, freq, nchannels;
|
||||||
int setfragment = 1;
|
int setfragment = 1;
|
||||||
const char *dspname = in ? conf->devpath_in : conf->devpath_out;
|
const char *dspname = opdo->has_dev ? opdo->dev : "/dev/dsp";
|
||||||
const char *typ = in ? "ADC" : "DAC";
|
const char *typ = in ? "ADC" : "DAC";
|
||||||
|
#ifdef USE_DSP_POLICY
|
||||||
|
int policy = oopts->has_dsp_policy ? oopts->dsp_policy : 5;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Kludge needed to have working mmap on Linux */
|
/* Kludge needed to have working mmap on Linux */
|
||||||
oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
|
oflags |= (oopts->has_try_mmap && oopts->try_mmap) ?
|
||||||
|
O_RDWR : (in ? O_RDONLY : O_WRONLY);
|
||||||
|
|
||||||
fd = open (dspname, oflags | O_NONBLOCK);
|
fd = open (dspname, oflags | O_NONBLOCK);
|
||||||
if (-1 == fd) {
|
if (-1 == fd) {
|
||||||
|
@ -285,6 +281,9 @@ static int oss_open (int in, struct oss_params *req,
|
||||||
freq = req->freq;
|
freq = req->freq;
|
||||||
nchannels = req->nchannels;
|
nchannels = req->nchannels;
|
||||||
fmt = req->fmt;
|
fmt = req->fmt;
|
||||||
|
req->nfrags = opdo->has_buffer_count ? opdo->buffer_count : 4;
|
||||||
|
req->fragsize = audio_buffer_bytes(
|
||||||
|
qapi_AudiodevOssPerDirectionOptions_base(opdo), as, 23220);
|
||||||
|
|
||||||
if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
|
if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
|
||||||
oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
|
oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
|
||||||
|
@ -308,18 +307,18 @@ static int oss_open (int in, struct oss_params *req,
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_DSP_POLICY
|
#ifdef USE_DSP_POLICY
|
||||||
if (conf->policy >= 0) {
|
if (policy >= 0) {
|
||||||
int version;
|
int version;
|
||||||
|
|
||||||
if (!oss_get_version (fd, &version, typ)) {
|
if (!oss_get_version (fd, &version, typ)) {
|
||||||
trace_oss_version(version);
|
trace_oss_version(version);
|
||||||
|
|
||||||
if (version >= 0x040000) {
|
if (version >= 0x040000) {
|
||||||
int policy = conf->policy;
|
int policy2 = policy;
|
||||||
if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
|
if (ioctl(fd, SNDCTL_DSP_POLICY, &policy2)) {
|
||||||
oss_logerr2 (errno, typ,
|
oss_logerr2 (errno, typ,
|
||||||
"Failed to set timing policy to %d\n",
|
"Failed to set timing policy to %d\n",
|
||||||
conf->policy);
|
policy);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
setfragment = 0;
|
setfragment = 0;
|
||||||
|
@ -500,19 +499,18 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
int endianness;
|
int endianness;
|
||||||
int err;
|
int err;
|
||||||
int fd;
|
int fd;
|
||||||
audfmt_e effective_fmt;
|
AudioFormat effective_fmt;
|
||||||
struct audsettings obt_as;
|
struct audsettings obt_as;
|
||||||
OSSConf *conf = drv_opaque;
|
Audiodev *dev = drv_opaque;
|
||||||
|
AudiodevOssOptions *oopts = &dev->u.oss;
|
||||||
|
|
||||||
oss->fd = -1;
|
oss->fd = -1;
|
||||||
|
|
||||||
req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
|
req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
|
||||||
req.freq = as->freq;
|
req.freq = as->freq;
|
||||||
req.nchannels = as->nchannels;
|
req.nchannels = as->nchannels;
|
||||||
req.fragsize = conf->fragsize;
|
|
||||||
req.nfrags = conf->nfrags;
|
|
||||||
|
|
||||||
if (oss_open (0, &req, &obt, &fd, conf)) {
|
if (oss_open(0, &req, as, &obt, &fd, dev)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -539,7 +537,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
|
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
|
||||||
|
|
||||||
oss->mmapped = 0;
|
oss->mmapped = 0;
|
||||||
if (conf->try_mmap) {
|
if (oopts->has_try_mmap && oopts->try_mmap) {
|
||||||
oss->pcm_buf = mmap (
|
oss->pcm_buf = mmap (
|
||||||
NULL,
|
NULL,
|
||||||
hw->samples << hw->info.shift,
|
hw->samples << hw->info.shift,
|
||||||
|
@ -597,7 +595,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
}
|
}
|
||||||
|
|
||||||
oss->fd = fd;
|
oss->fd = fd;
|
||||||
oss->conf = conf;
|
oss->dev = dev;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -605,16 +603,12 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||||
{
|
{
|
||||||
int trig;
|
int trig;
|
||||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||||
|
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case VOICE_ENABLE:
|
case VOICE_ENABLE:
|
||||||
{
|
{
|
||||||
va_list ap;
|
bool poll_mode = opdo->try_poll;
|
||||||
int poll_mode;
|
|
||||||
|
|
||||||
va_start (ap, cmd);
|
|
||||||
poll_mode = va_arg (ap, int);
|
|
||||||
va_end (ap);
|
|
||||||
|
|
||||||
ldebug ("enabling voice\n");
|
ldebug ("enabling voice\n");
|
||||||
if (poll_mode) {
|
if (poll_mode) {
|
||||||
|
@ -667,18 +661,16 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||||
int endianness;
|
int endianness;
|
||||||
int err;
|
int err;
|
||||||
int fd;
|
int fd;
|
||||||
audfmt_e effective_fmt;
|
AudioFormat effective_fmt;
|
||||||
struct audsettings obt_as;
|
struct audsettings obt_as;
|
||||||
OSSConf *conf = drv_opaque;
|
Audiodev *dev = drv_opaque;
|
||||||
|
|
||||||
oss->fd = -1;
|
oss->fd = -1;
|
||||||
|
|
||||||
req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
|
req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
|
||||||
req.freq = as->freq;
|
req.freq = as->freq;
|
||||||
req.nchannels = as->nchannels;
|
req.nchannels = as->nchannels;
|
||||||
req.fragsize = conf->fragsize;
|
if (oss_open(1, &req, as, &obt, &fd, dev)) {
|
||||||
req.nfrags = conf->nfrags;
|
|
||||||
if (oss_open (1, &req, &obt, &fd, conf)) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -712,7 +704,7 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||||
}
|
}
|
||||||
|
|
||||||
oss->fd = fd;
|
oss->fd = fd;
|
||||||
oss->conf = conf;
|
oss->dev = dev;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -803,16 +795,12 @@ static int oss_read (SWVoiceIn *sw, void *buf, int size)
|
||||||
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||||
{
|
{
|
||||||
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
||||||
|
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case VOICE_ENABLE:
|
case VOICE_ENABLE:
|
||||||
{
|
{
|
||||||
va_list ap;
|
bool poll_mode = opdo->try_poll;
|
||||||
int poll_mode;
|
|
||||||
|
|
||||||
va_start (ap, cmd);
|
|
||||||
poll_mode = va_arg (ap, int);
|
|
||||||
va_end (ap);
|
|
||||||
|
|
||||||
if (poll_mode) {
|
if (poll_mode) {
|
||||||
oss_poll_in (hw);
|
oss_poll_in (hw);
|
||||||
|
@ -832,82 +820,36 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static OSSConf glob_conf = {
|
static void oss_init_per_direction(AudiodevOssPerDirectionOptions *opdo)
|
||||||
.try_mmap = 0,
|
|
||||||
.nfrags = 4,
|
|
||||||
.fragsize = 4096,
|
|
||||||
.devpath_out = "/dev/dsp",
|
|
||||||
.devpath_in = "/dev/dsp",
|
|
||||||
.exclusive = 0,
|
|
||||||
.policy = 5
|
|
||||||
};
|
|
||||||
|
|
||||||
static void *oss_audio_init (void)
|
|
||||||
{
|
{
|
||||||
OSSConf *conf = g_malloc(sizeof(OSSConf));
|
if (!opdo->has_try_poll) {
|
||||||
*conf = glob_conf;
|
opdo->try_poll = true;
|
||||||
|
opdo->has_try_poll = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (access(conf->devpath_in, R_OK | W_OK) < 0 ||
|
static void *oss_audio_init(Audiodev *dev)
|
||||||
access(conf->devpath_out, R_OK | W_OK) < 0) {
|
{
|
||||||
g_free(conf);
|
AudiodevOssOptions *oopts;
|
||||||
|
assert(dev->driver == AUDIODEV_DRIVER_OSS);
|
||||||
|
|
||||||
|
oopts = &dev->u.oss;
|
||||||
|
oss_init_per_direction(oopts->in);
|
||||||
|
oss_init_per_direction(oopts->out);
|
||||||
|
|
||||||
|
if (access(oopts->in->has_dev ? oopts->in->dev : "/dev/dsp",
|
||||||
|
R_OK | W_OK) < 0 ||
|
||||||
|
access(oopts->out->has_dev ? oopts->out->dev : "/dev/dsp",
|
||||||
|
R_OK | W_OK) < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return conf;
|
return dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void oss_audio_fini (void *opaque)
|
static void oss_audio_fini (void *opaque)
|
||||||
{
|
{
|
||||||
g_free(opaque);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct audio_option oss_options[] = {
|
|
||||||
{
|
|
||||||
.name = "FRAGSIZE",
|
|
||||||
.tag = AUD_OPT_INT,
|
|
||||||
.valp = &glob_conf.fragsize,
|
|
||||||
.descr = "Fragment size in bytes"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "NFRAGS",
|
|
||||||
.tag = AUD_OPT_INT,
|
|
||||||
.valp = &glob_conf.nfrags,
|
|
||||||
.descr = "Number of fragments"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "MMAP",
|
|
||||||
.tag = AUD_OPT_BOOL,
|
|
||||||
.valp = &glob_conf.try_mmap,
|
|
||||||
.descr = "Try using memory mapped access"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "DAC_DEV",
|
|
||||||
.tag = AUD_OPT_STR,
|
|
||||||
.valp = &glob_conf.devpath_out,
|
|
||||||
.descr = "Path to DAC device"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "ADC_DEV",
|
|
||||||
.tag = AUD_OPT_STR,
|
|
||||||
.valp = &glob_conf.devpath_in,
|
|
||||||
.descr = "Path to ADC device"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "EXCLUSIVE",
|
|
||||||
.tag = AUD_OPT_BOOL,
|
|
||||||
.valp = &glob_conf.exclusive,
|
|
||||||
.descr = "Open device in exclusive mode (vmix won't work)"
|
|
||||||
},
|
|
||||||
#ifdef USE_DSP_POLICY
|
|
||||||
{
|
|
||||||
.name = "POLICY",
|
|
||||||
.tag = AUD_OPT_INT,
|
|
||||||
.valp = &glob_conf.policy,
|
|
||||||
.descr = "Set the timing policy of the device, -1 to use fragment mode",
|
|
||||||
},
|
|
||||||
#endif
|
|
||||||
{ /* End of list */ }
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct audio_pcm_ops oss_pcm_ops = {
|
static struct audio_pcm_ops oss_pcm_ops = {
|
||||||
.init_out = oss_init_out,
|
.init_out = oss_init_out,
|
||||||
.fini_out = oss_fini_out,
|
.fini_out = oss_fini_out,
|
||||||
|
@ -925,7 +867,6 @@ static struct audio_pcm_ops oss_pcm_ops = {
|
||||||
static struct audio_driver oss_audio_driver = {
|
static struct audio_driver oss_audio_driver = {
|
||||||
.name = "oss",
|
.name = "oss",
|
||||||
.descr = "OSS http://www.opensound.com",
|
.descr = "OSS http://www.opensound.com",
|
||||||
.options = oss_options,
|
|
||||||
.init = oss_audio_init,
|
.init = oss_audio_init,
|
||||||
.fini = oss_audio_fini,
|
.fini = oss_audio_fini,
|
||||||
.pcm_ops = &oss_pcm_ops,
|
.pcm_ops = &oss_pcm_ops,
|
||||||
|
|
164
audio/paaudio.c
164
audio/paaudio.c
|
@ -2,6 +2,7 @@
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
|
#include "qapi/opts-visitor.h"
|
||||||
|
|
||||||
#include <pulse/pulseaudio.h>
|
#include <pulse/pulseaudio.h>
|
||||||
|
|
||||||
|
@ -10,14 +11,7 @@
|
||||||
#include "audio_pt_int.h"
|
#include "audio_pt_int.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int samples;
|
Audiodev *dev;
|
||||||
char *server;
|
|
||||||
char *sink;
|
|
||||||
char *source;
|
|
||||||
} PAConf;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
PAConf conf;
|
|
||||||
pa_threaded_mainloop *mainloop;
|
pa_threaded_mainloop *mainloop;
|
||||||
pa_context *context;
|
pa_context *context;
|
||||||
} paaudio;
|
} paaudio;
|
||||||
|
@ -32,6 +26,7 @@ typedef struct {
|
||||||
void *pcm_buf;
|
void *pcm_buf;
|
||||||
struct audio_pt pt;
|
struct audio_pt pt;
|
||||||
paaudio *g;
|
paaudio *g;
|
||||||
|
int samples;
|
||||||
} PAVoiceOut;
|
} PAVoiceOut;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -46,6 +41,7 @@ typedef struct {
|
||||||
const void *read_data;
|
const void *read_data;
|
||||||
size_t read_index, read_length;
|
size_t read_index, read_length;
|
||||||
paaudio *g;
|
paaudio *g;
|
||||||
|
int samples;
|
||||||
} PAVoiceIn;
|
} PAVoiceIn;
|
||||||
|
|
||||||
static void qpa_audio_fini(void *opaque);
|
static void qpa_audio_fini(void *opaque);
|
||||||
|
@ -227,7 +223,7 @@ static void *qpa_thread_out (void *arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2);
|
decr = to_mix = audio_MIN(pa->live, pa->samples >> 5);
|
||||||
rpos = pa->rpos;
|
rpos = pa->rpos;
|
||||||
|
|
||||||
if (audio_pt_unlock(&pa->pt, __func__)) {
|
if (audio_pt_unlock(&pa->pt, __func__)) {
|
||||||
|
@ -319,7 +315,7 @@ static void *qpa_thread_in (void *arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2);
|
incr = to_grab = audio_MIN(pa->dead, pa->samples >> 5);
|
||||||
wpos = pa->wpos;
|
wpos = pa->wpos;
|
||||||
|
|
||||||
if (audio_pt_unlock(&pa->pt, __func__)) {
|
if (audio_pt_unlock(&pa->pt, __func__)) {
|
||||||
|
@ -385,21 +381,21 @@ static int qpa_read (SWVoiceIn *sw, void *buf, int len)
|
||||||
return audio_pcm_sw_read (sw, buf, len);
|
return audio_pcm_sw_read (sw, buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
|
static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
|
||||||
{
|
{
|
||||||
int format;
|
int format;
|
||||||
|
|
||||||
switch (afmt) {
|
switch (afmt) {
|
||||||
case AUD_FMT_S8:
|
case AUDIO_FORMAT_S8:
|
||||||
case AUD_FMT_U8:
|
case AUDIO_FORMAT_U8:
|
||||||
format = PA_SAMPLE_U8;
|
format = PA_SAMPLE_U8;
|
||||||
break;
|
break;
|
||||||
case AUD_FMT_S16:
|
case AUDIO_FORMAT_S16:
|
||||||
case AUD_FMT_U16:
|
case AUDIO_FORMAT_U16:
|
||||||
format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
|
format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
|
||||||
break;
|
break;
|
||||||
case AUD_FMT_S32:
|
case AUDIO_FORMAT_S32:
|
||||||
case AUD_FMT_U32:
|
case AUDIO_FORMAT_U32:
|
||||||
format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
|
format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -410,26 +406,26 @@ static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
|
||||||
return format;
|
return format;
|
||||||
}
|
}
|
||||||
|
|
||||||
static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
|
static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
|
||||||
{
|
{
|
||||||
switch (fmt) {
|
switch (fmt) {
|
||||||
case PA_SAMPLE_U8:
|
case PA_SAMPLE_U8:
|
||||||
return AUD_FMT_U8;
|
return AUDIO_FORMAT_U8;
|
||||||
case PA_SAMPLE_S16BE:
|
case PA_SAMPLE_S16BE:
|
||||||
*endianness = 1;
|
*endianness = 1;
|
||||||
return AUD_FMT_S16;
|
return AUDIO_FORMAT_S16;
|
||||||
case PA_SAMPLE_S16LE:
|
case PA_SAMPLE_S16LE:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
return AUD_FMT_S16;
|
return AUDIO_FORMAT_S16;
|
||||||
case PA_SAMPLE_S32BE:
|
case PA_SAMPLE_S32BE:
|
||||||
*endianness = 1;
|
*endianness = 1;
|
||||||
return AUD_FMT_S32;
|
return AUDIO_FORMAT_S32;
|
||||||
case PA_SAMPLE_S32LE:
|
case PA_SAMPLE_S32LE:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
return AUD_FMT_S32;
|
return AUDIO_FORMAT_S32;
|
||||||
default:
|
default:
|
||||||
dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
|
dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
|
||||||
return AUD_FMT_U8;
|
return AUDIO_FORMAT_U8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,17 +542,15 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
struct audsettings obt_as = *as;
|
struct audsettings obt_as = *as;
|
||||||
PAVoiceOut *pa = (PAVoiceOut *) hw;
|
PAVoiceOut *pa = (PAVoiceOut *) hw;
|
||||||
paaudio *g = pa->g = drv_opaque;
|
paaudio *g = pa->g = drv_opaque;
|
||||||
|
AudiodevPaOptions *popts = &g->dev->u.pa;
|
||||||
|
AudiodevPaPerDirectionOptions *ppdo = popts->out;
|
||||||
|
|
||||||
ss.format = audfmt_to_pa (as->fmt, as->endianness);
|
ss.format = audfmt_to_pa (as->fmt, as->endianness);
|
||||||
ss.channels = as->nchannels;
|
ss.channels = as->nchannels;
|
||||||
ss.rate = as->freq;
|
ss.rate = as->freq;
|
||||||
|
|
||||||
/*
|
ba.tlength = pa_usec_to_bytes(ppdo->latency, &ss);
|
||||||
* qemu audio tick runs at 100 Hz (by default), so processing
|
ba.minreq = -1;
|
||||||
* data chunks worth 10 ms of sound should be a good fit.
|
|
||||||
*/
|
|
||||||
ba.tlength = pa_usec_to_bytes (10 * 1000, &ss);
|
|
||||||
ba.minreq = pa_usec_to_bytes (5 * 1000, &ss);
|
|
||||||
ba.maxlength = -1;
|
ba.maxlength = -1;
|
||||||
ba.prebuf = -1;
|
ba.prebuf = -1;
|
||||||
|
|
||||||
|
@ -566,7 +560,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
g,
|
g,
|
||||||
"qemu",
|
"qemu",
|
||||||
PA_STREAM_PLAYBACK,
|
PA_STREAM_PLAYBACK,
|
||||||
g->conf.sink,
|
ppdo->has_name ? ppdo->name : NULL,
|
||||||
&ss,
|
&ss,
|
||||||
NULL, /* channel map */
|
NULL, /* channel map */
|
||||||
&ba, /* buffering attributes */
|
&ba, /* buffering attributes */
|
||||||
|
@ -578,7 +572,9 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
}
|
}
|
||||||
|
|
||||||
audio_pcm_init_info (&hw->info, &obt_as);
|
audio_pcm_init_info (&hw->info, &obt_as);
|
||||||
hw->samples = g->conf.samples;
|
hw->samples = pa->samples = audio_buffer_samples(
|
||||||
|
qapi_AudiodevPaPerDirectionOptions_base(ppdo),
|
||||||
|
&obt_as, ppdo->buffer_length);
|
||||||
pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
|
pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
|
||||||
pa->rpos = hw->rpos;
|
pa->rpos = hw->rpos;
|
||||||
if (!pa->pcm_buf) {
|
if (!pa->pcm_buf) {
|
||||||
|
@ -609,24 +605,32 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
pa_sample_spec ss;
|
pa_sample_spec ss;
|
||||||
|
pa_buffer_attr ba;
|
||||||
struct audsettings obt_as = *as;
|
struct audsettings obt_as = *as;
|
||||||
PAVoiceIn *pa = (PAVoiceIn *) hw;
|
PAVoiceIn *pa = (PAVoiceIn *) hw;
|
||||||
paaudio *g = pa->g = drv_opaque;
|
paaudio *g = pa->g = drv_opaque;
|
||||||
|
AudiodevPaOptions *popts = &g->dev->u.pa;
|
||||||
|
AudiodevPaPerDirectionOptions *ppdo = popts->in;
|
||||||
|
|
||||||
ss.format = audfmt_to_pa (as->fmt, as->endianness);
|
ss.format = audfmt_to_pa (as->fmt, as->endianness);
|
||||||
ss.channels = as->nchannels;
|
ss.channels = as->nchannels;
|
||||||
ss.rate = as->freq;
|
ss.rate = as->freq;
|
||||||
|
|
||||||
|
ba.fragsize = pa_usec_to_bytes(ppdo->latency, &ss);
|
||||||
|
ba.maxlength = -1;
|
||||||
|
ba.minreq = -1;
|
||||||
|
ba.prebuf = -1;
|
||||||
|
|
||||||
obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
|
obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
|
||||||
|
|
||||||
pa->stream = qpa_simple_new (
|
pa->stream = qpa_simple_new (
|
||||||
g,
|
g,
|
||||||
"qemu",
|
"qemu",
|
||||||
PA_STREAM_RECORD,
|
PA_STREAM_RECORD,
|
||||||
g->conf.source,
|
ppdo->has_name ? ppdo->name : NULL,
|
||||||
&ss,
|
&ss,
|
||||||
NULL, /* channel map */
|
NULL, /* channel map */
|
||||||
NULL, /* buffering attributes */
|
&ba, /* buffering attributes */
|
||||||
&error
|
&error
|
||||||
);
|
);
|
||||||
if (!pa->stream) {
|
if (!pa->stream) {
|
||||||
|
@ -635,7 +639,9 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||||
}
|
}
|
||||||
|
|
||||||
audio_pcm_init_info (&hw->info, &obt_as);
|
audio_pcm_init_info (&hw->info, &obt_as);
|
||||||
hw->samples = g->conf.samples;
|
hw->samples = pa->samples = audio_buffer_samples(
|
||||||
|
qapi_AudiodevPaPerDirectionOptions_base(ppdo),
|
||||||
|
&obt_as, ppdo->buffer_length);
|
||||||
pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
|
pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
|
||||||
pa->wpos = hw->wpos;
|
pa->wpos = hw->wpos;
|
||||||
if (!pa->pcm_buf) {
|
if (!pa->pcm_buf) {
|
||||||
|
@ -807,15 +813,54 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* common */
|
static int qpa_validate_per_direction_opts(Audiodev *dev,
|
||||||
static PAConf glob_conf = {
|
AudiodevPaPerDirectionOptions *pdo)
|
||||||
.samples = 4096,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void *qpa_audio_init (void)
|
|
||||||
{
|
{
|
||||||
paaudio *g = g_malloc(sizeof(paaudio));
|
if (!pdo->has_buffer_length) {
|
||||||
g->conf = glob_conf;
|
pdo->has_buffer_length = true;
|
||||||
|
pdo->buffer_length = 46440;
|
||||||
|
}
|
||||||
|
if (!pdo->has_latency) {
|
||||||
|
pdo->has_latency = true;
|
||||||
|
pdo->latency = 15000;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *qpa_audio_init(Audiodev *dev)
|
||||||
|
{
|
||||||
|
paaudio *g;
|
||||||
|
AudiodevPaOptions *popts = &dev->u.pa;
|
||||||
|
const char *server;
|
||||||
|
|
||||||
|
if (!popts->has_server) {
|
||||||
|
char pidfile[64];
|
||||||
|
char *runtime;
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
runtime = getenv("XDG_RUNTIME_DIR");
|
||||||
|
if (!runtime) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
snprintf(pidfile, sizeof(pidfile), "%s/pulse/pid", runtime);
|
||||||
|
if (stat(pidfile, &st) != 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(dev->driver == AUDIODEV_DRIVER_PA);
|
||||||
|
|
||||||
|
g = g_malloc(sizeof(paaudio));
|
||||||
|
server = popts->has_server ? popts->server : NULL;
|
||||||
|
|
||||||
|
if (!qpa_validate_per_direction_opts(dev, popts->in)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (!qpa_validate_per_direction_opts(dev, popts->out)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
g->dev = dev;
|
||||||
g->mainloop = NULL;
|
g->mainloop = NULL;
|
||||||
g->context = NULL;
|
g->context = NULL;
|
||||||
|
|
||||||
|
@ -825,14 +870,14 @@ static void *qpa_audio_init (void)
|
||||||
}
|
}
|
||||||
|
|
||||||
g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop),
|
g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop),
|
||||||
g->conf.server);
|
server);
|
||||||
if (!g->context) {
|
if (!g->context) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_context_set_state_callback (g->context, context_state_cb, g);
|
pa_context_set_state_callback (g->context, context_state_cb, g);
|
||||||
|
|
||||||
if (pa_context_connect (g->context, g->conf.server, 0, NULL) < 0) {
|
if (pa_context_connect(g->context, server, 0, NULL) < 0) {
|
||||||
qpa_logerr (pa_context_errno (g->context),
|
qpa_logerr (pa_context_errno (g->context),
|
||||||
"pa_context_connect() failed\n");
|
"pa_context_connect() failed\n");
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -895,34 +940,6 @@ static void qpa_audio_fini (void *opaque)
|
||||||
g_free(g);
|
g_free(g);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct audio_option qpa_options[] = {
|
|
||||||
{
|
|
||||||
.name = "SAMPLES",
|
|
||||||
.tag = AUD_OPT_INT,
|
|
||||||
.valp = &glob_conf.samples,
|
|
||||||
.descr = "buffer size in samples"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "SERVER",
|
|
||||||
.tag = AUD_OPT_STR,
|
|
||||||
.valp = &glob_conf.server,
|
|
||||||
.descr = "server address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "SINK",
|
|
||||||
.tag = AUD_OPT_STR,
|
|
||||||
.valp = &glob_conf.sink,
|
|
||||||
.descr = "sink device name"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "SOURCE",
|
|
||||||
.tag = AUD_OPT_STR,
|
|
||||||
.valp = &glob_conf.source,
|
|
||||||
.descr = "source device name"
|
|
||||||
},
|
|
||||||
{ /* End of list */ }
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct audio_pcm_ops qpa_pcm_ops = {
|
static struct audio_pcm_ops qpa_pcm_ops = {
|
||||||
.init_out = qpa_init_out,
|
.init_out = qpa_init_out,
|
||||||
.fini_out = qpa_fini_out,
|
.fini_out = qpa_fini_out,
|
||||||
|
@ -940,7 +957,6 @@ static struct audio_pcm_ops qpa_pcm_ops = {
|
||||||
static struct audio_driver pa_audio_driver = {
|
static struct audio_driver pa_audio_driver = {
|
||||||
.name = "pa",
|
.name = "pa",
|
||||||
.descr = "http://www.pulseaudio.org/",
|
.descr = "http://www.pulseaudio.org/",
|
||||||
.options = qpa_options,
|
|
||||||
.init = qpa_audio_init,
|
.init = qpa_audio_init,
|
||||||
.fini = qpa_audio_fini,
|
.fini = qpa_audio_fini,
|
||||||
.pcm_ops = &qpa_pcm_ops,
|
.pcm_ops = &qpa_pcm_ops,
|
||||||
|
|
236
audio/sdlaudio.c
236
audio/sdlaudio.c
|
@ -38,31 +38,17 @@
|
||||||
#define AUDIO_CAP "sdl"
|
#define AUDIO_CAP "sdl"
|
||||||
#include "audio_int.h"
|
#include "audio_int.h"
|
||||||
|
|
||||||
#define USE_SEMAPHORE (SDL_MAJOR_VERSION < 2)
|
|
||||||
|
|
||||||
typedef struct SDLVoiceOut {
|
typedef struct SDLVoiceOut {
|
||||||
HWVoiceOut hw;
|
HWVoiceOut hw;
|
||||||
int live;
|
int live;
|
||||||
#if USE_SEMAPHORE
|
|
||||||
int rpos;
|
|
||||||
#endif
|
|
||||||
int decr;
|
int decr;
|
||||||
} SDLVoiceOut;
|
} SDLVoiceOut;
|
||||||
|
|
||||||
static struct {
|
|
||||||
int nb_samples;
|
|
||||||
} conf = {
|
|
||||||
.nb_samples = 1024
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct SDLAudioState {
|
static struct SDLAudioState {
|
||||||
int exit;
|
int exit;
|
||||||
#if USE_SEMAPHORE
|
|
||||||
SDL_mutex *mutex;
|
|
||||||
SDL_sem *sem;
|
|
||||||
#endif
|
|
||||||
int initialized;
|
int initialized;
|
||||||
bool driver_created;
|
bool driver_created;
|
||||||
|
Audiodev *dev;
|
||||||
} glob_sdl;
|
} glob_sdl;
|
||||||
typedef struct SDLAudioState SDLAudioState;
|
typedef struct SDLAudioState SDLAudioState;
|
||||||
|
|
||||||
|
@ -77,79 +63,19 @@ static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
|
||||||
AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
|
AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sdl_lock (SDLAudioState *s, const char *forfn)
|
static int aud_to_sdlfmt (AudioFormat fmt)
|
||||||
{
|
|
||||||
#if USE_SEMAPHORE
|
|
||||||
if (SDL_LockMutex (s->mutex)) {
|
|
||||||
sdl_logerr ("SDL_LockMutex for %s failed\n", forfn);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
SDL_LockAudio();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sdl_unlock (SDLAudioState *s, const char *forfn)
|
|
||||||
{
|
|
||||||
#if USE_SEMAPHORE
|
|
||||||
if (SDL_UnlockMutex (s->mutex)) {
|
|
||||||
sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
SDL_UnlockAudio();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sdl_post (SDLAudioState *s, const char *forfn)
|
|
||||||
{
|
|
||||||
#if USE_SEMAPHORE
|
|
||||||
if (SDL_SemPost (s->sem)) {
|
|
||||||
sdl_logerr ("SDL_SemPost for %s failed\n", forfn);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if USE_SEMAPHORE
|
|
||||||
static int sdl_wait (SDLAudioState *s, const char *forfn)
|
|
||||||
{
|
|
||||||
if (SDL_SemWait (s->sem)) {
|
|
||||||
sdl_logerr ("SDL_SemWait for %s failed\n", forfn);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
|
|
||||||
{
|
|
||||||
if (sdl_unlock (s, forfn)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sdl_post (s, forfn);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int aud_to_sdlfmt (audfmt_e fmt)
|
|
||||||
{
|
{
|
||||||
switch (fmt) {
|
switch (fmt) {
|
||||||
case AUD_FMT_S8:
|
case AUDIO_FORMAT_S8:
|
||||||
return AUDIO_S8;
|
return AUDIO_S8;
|
||||||
|
|
||||||
case AUD_FMT_U8:
|
case AUDIO_FORMAT_U8:
|
||||||
return AUDIO_U8;
|
return AUDIO_U8;
|
||||||
|
|
||||||
case AUD_FMT_S16:
|
case AUDIO_FORMAT_S16:
|
||||||
return AUDIO_S16LSB;
|
return AUDIO_S16LSB;
|
||||||
|
|
||||||
case AUD_FMT_U16:
|
case AUDIO_FORMAT_U16:
|
||||||
return AUDIO_U16LSB;
|
return AUDIO_U16LSB;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -161,37 +87,37 @@ static int aud_to_sdlfmt (audfmt_e fmt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sdl_to_audfmt(int sdlfmt, audfmt_e *fmt, int *endianness)
|
static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness)
|
||||||
{
|
{
|
||||||
switch (sdlfmt) {
|
switch (sdlfmt) {
|
||||||
case AUDIO_S8:
|
case AUDIO_S8:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_S8;
|
*fmt = AUDIO_FORMAT_S8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AUDIO_U8:
|
case AUDIO_U8:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_U8;
|
*fmt = AUDIO_FORMAT_U8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AUDIO_S16LSB:
|
case AUDIO_S16LSB:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_S16;
|
*fmt = AUDIO_FORMAT_S16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AUDIO_U16LSB:
|
case AUDIO_U16LSB:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_U16;
|
*fmt = AUDIO_FORMAT_U16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AUDIO_S16MSB:
|
case AUDIO_S16MSB:
|
||||||
*endianness = 1;
|
*endianness = 1;
|
||||||
*fmt = AUD_FMT_S16;
|
*fmt = AUDIO_FORMAT_S16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AUDIO_U16MSB:
|
case AUDIO_U16MSB:
|
||||||
*endianness = 1;
|
*endianness = 1;
|
||||||
*fmt = AUD_FMT_U16;
|
*fmt = AUDIO_FORMAT_U16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -243,9 +169,9 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
|
||||||
static void sdl_close (SDLAudioState *s)
|
static void sdl_close (SDLAudioState *s)
|
||||||
{
|
{
|
||||||
if (s->initialized) {
|
if (s->initialized) {
|
||||||
sdl_lock (s, "sdl_close");
|
SDL_LockAudio();
|
||||||
s->exit = 1;
|
s->exit = 1;
|
||||||
sdl_unlock_and_post (s, "sdl_close");
|
SDL_UnlockAudio();
|
||||||
SDL_PauseAudio (1);
|
SDL_PauseAudio (1);
|
||||||
SDL_CloseAudio ();
|
SDL_CloseAudio ();
|
||||||
s->initialized = 0;
|
s->initialized = 0;
|
||||||
|
@ -258,76 +184,36 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
|
||||||
SDLAudioState *s = &glob_sdl;
|
SDLAudioState *s = &glob_sdl;
|
||||||
HWVoiceOut *hw = &sdl->hw;
|
HWVoiceOut *hw = &sdl->hw;
|
||||||
int samples = len >> hw->info.shift;
|
int samples = len >> hw->info.shift;
|
||||||
|
int to_mix, decr;
|
||||||
|
|
||||||
if (s->exit) {
|
if (s->exit || !sdl->live) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (samples) {
|
/* dolog ("in callback samples=%d live=%d\n", samples, sdl->live); */
|
||||||
int to_mix, decr;
|
|
||||||
|
|
||||||
/* dolog ("in callback samples=%d\n", samples); */
|
to_mix = audio_MIN(samples, sdl->live);
|
||||||
#if USE_SEMAPHORE
|
decr = to_mix;
|
||||||
sdl_wait (s, "sdl_callback");
|
while (to_mix) {
|
||||||
if (s->exit) {
|
int chunk = audio_MIN(to_mix, hw->samples - hw->rpos);
|
||||||
return;
|
struct st_sample *src = hw->mix_buf + hw->rpos;
|
||||||
}
|
|
||||||
|
|
||||||
if (sdl_lock (s, "sdl_callback")) {
|
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
|
||||||
return;
|
hw->clip(buf, src, chunk);
|
||||||
}
|
hw->rpos = (hw->rpos + chunk) % hw->samples;
|
||||||
|
to_mix -= chunk;
|
||||||
if (audio_bug(__func__, sdl->live < 0 || sdl->live > hw->samples)) {
|
buf += chunk << hw->info.shift;
|
||||||
dolog ("sdl->live=%d hw->samples=%d\n",
|
|
||||||
sdl->live, hw->samples);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sdl->live) {
|
|
||||||
goto again;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (s->exit || !sdl->live) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* dolog ("in callback live=%d\n", live); */
|
|
||||||
to_mix = audio_MIN (samples, sdl->live);
|
|
||||||
decr = to_mix;
|
|
||||||
while (to_mix) {
|
|
||||||
int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
|
|
||||||
struct st_sample *src = hw->mix_buf + hw->rpos;
|
|
||||||
|
|
||||||
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
|
|
||||||
hw->clip (buf, src, chunk);
|
|
||||||
#if USE_SEMAPHORE
|
|
||||||
sdl->rpos = (sdl->rpos + chunk) % hw->samples;
|
|
||||||
#else
|
|
||||||
hw->rpos = (hw->rpos + chunk) % hw->samples;
|
|
||||||
#endif
|
|
||||||
to_mix -= chunk;
|
|
||||||
buf += chunk << hw->info.shift;
|
|
||||||
}
|
|
||||||
samples -= decr;
|
|
||||||
sdl->live -= decr;
|
|
||||||
sdl->decr += decr;
|
|
||||||
|
|
||||||
#if USE_SEMAPHORE
|
|
||||||
again:
|
|
||||||
if (sdl_unlock (s, "sdl_callback")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
samples -= decr;
|
||||||
|
sdl->live -= decr;
|
||||||
|
sdl->decr += decr;
|
||||||
|
|
||||||
/* dolog ("done len=%d\n", len); */
|
/* dolog ("done len=%d\n", len); */
|
||||||
|
|
||||||
#if (SDL_MAJOR_VERSION >= 2)
|
|
||||||
/* SDL2 does not clear the remaining buffer for us, so do it on our own */
|
/* SDL2 does not clear the remaining buffer for us, so do it on our own */
|
||||||
if (samples) {
|
if (samples) {
|
||||||
memset(buf, 0, samples << hw->info.shift);
|
memset(buf, 0, samples << hw->info.shift);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
|
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
|
||||||
|
@ -339,11 +225,8 @@ static int sdl_run_out (HWVoiceOut *hw, int live)
|
||||||
{
|
{
|
||||||
int decr;
|
int decr;
|
||||||
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
|
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
|
||||||
SDLAudioState *s = &glob_sdl;
|
|
||||||
|
|
||||||
if (sdl_lock (s, "sdl_run_out")) {
|
SDL_LockAudio();
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sdl->decr > live) {
|
if (sdl->decr > live) {
|
||||||
ldebug ("sdl->decr %d live %d sdl->live %d\n",
|
ldebug ("sdl->decr %d live %d sdl->live %d\n",
|
||||||
|
@ -355,19 +238,10 @@ static int sdl_run_out (HWVoiceOut *hw, int live)
|
||||||
decr = audio_MIN (sdl->decr, live);
|
decr = audio_MIN (sdl->decr, live);
|
||||||
sdl->decr -= decr;
|
sdl->decr -= decr;
|
||||||
|
|
||||||
#if USE_SEMAPHORE
|
|
||||||
sdl->live = live - decr;
|
|
||||||
hw->rpos = sdl->rpos;
|
|
||||||
#else
|
|
||||||
sdl->live = live;
|
sdl->live = live;
|
||||||
#endif
|
|
||||||
|
|
||||||
if (sdl->live > 0) {
|
SDL_UnlockAudio();
|
||||||
sdl_unlock_and_post (s, "sdl_run_out");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sdl_unlock (s, "sdl_run_out");
|
|
||||||
}
|
|
||||||
return decr;
|
return decr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,13 +260,13 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
SDL_AudioSpec req, obt;
|
SDL_AudioSpec req, obt;
|
||||||
int endianness;
|
int endianness;
|
||||||
int err;
|
int err;
|
||||||
audfmt_e effective_fmt;
|
AudioFormat effective_fmt;
|
||||||
struct audsettings obt_as;
|
struct audsettings obt_as;
|
||||||
|
|
||||||
req.freq = as->freq;
|
req.freq = as->freq;
|
||||||
req.format = aud_to_sdlfmt (as->fmt);
|
req.format = aud_to_sdlfmt (as->fmt);
|
||||||
req.channels = as->nchannels;
|
req.channels = as->nchannels;
|
||||||
req.samples = conf.nb_samples;
|
req.samples = audio_buffer_samples(s->dev->u.sdl.out, as, 11610);
|
||||||
req.callback = sdl_callback;
|
req.callback = sdl_callback;
|
||||||
req.userdata = sdl;
|
req.userdata = sdl;
|
||||||
|
|
||||||
|
@ -436,7 +310,7 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *sdl_audio_init (void)
|
static void *sdl_audio_init(Audiodev *dev)
|
||||||
{
|
{
|
||||||
SDLAudioState *s = &glob_sdl;
|
SDLAudioState *s = &glob_sdl;
|
||||||
if (s->driver_created) {
|
if (s->driver_created) {
|
||||||
|
@ -449,24 +323,8 @@ static void *sdl_audio_init (void)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if USE_SEMAPHORE
|
|
||||||
s->mutex = SDL_CreateMutex ();
|
|
||||||
if (!s->mutex) {
|
|
||||||
sdl_logerr ("Failed to create SDL mutex\n");
|
|
||||||
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
s->sem = SDL_CreateSemaphore (0);
|
|
||||||
if (!s->sem) {
|
|
||||||
sdl_logerr ("Failed to create SDL semaphore\n");
|
|
||||||
SDL_DestroyMutex (s->mutex);
|
|
||||||
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
s->driver_created = true;
|
s->driver_created = true;
|
||||||
|
s->dev = dev;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -474,24 +332,11 @@ static void sdl_audio_fini (void *opaque)
|
||||||
{
|
{
|
||||||
SDLAudioState *s = opaque;
|
SDLAudioState *s = opaque;
|
||||||
sdl_close (s);
|
sdl_close (s);
|
||||||
#if USE_SEMAPHORE
|
|
||||||
SDL_DestroySemaphore (s->sem);
|
|
||||||
SDL_DestroyMutex (s->mutex);
|
|
||||||
#endif
|
|
||||||
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
||||||
s->driver_created = false;
|
s->driver_created = false;
|
||||||
|
s->dev = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct audio_option sdl_options[] = {
|
|
||||||
{
|
|
||||||
.name = "SAMPLES",
|
|
||||||
.tag = AUD_OPT_INT,
|
|
||||||
.valp = &conf.nb_samples,
|
|
||||||
.descr = "Size of SDL buffer in samples"
|
|
||||||
},
|
|
||||||
{ /* End of list */ }
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct audio_pcm_ops sdl_pcm_ops = {
|
static struct audio_pcm_ops sdl_pcm_ops = {
|
||||||
.init_out = sdl_init_out,
|
.init_out = sdl_init_out,
|
||||||
.fini_out = sdl_fini_out,
|
.fini_out = sdl_fini_out,
|
||||||
|
@ -503,7 +348,6 @@ static struct audio_pcm_ops sdl_pcm_ops = {
|
||||||
static struct audio_driver sdl_audio_driver = {
|
static struct audio_driver sdl_audio_driver = {
|
||||||
.name = "sdl",
|
.name = "sdl",
|
||||||
.descr = "SDL http://www.libsdl.org",
|
.descr = "SDL http://www.libsdl.org",
|
||||||
.options = sdl_options,
|
|
||||||
.init = sdl_audio_init,
|
.init = sdl_audio_init,
|
||||||
.fini = sdl_audio_fini,
|
.fini = sdl_audio_fini,
|
||||||
.pcm_ops = &sdl_pcm_ops,
|
.pcm_ops = &sdl_pcm_ops,
|
||||||
|
|
|
@ -77,7 +77,7 @@ static const SpiceRecordInterface record_sif = {
|
||||||
.base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
|
.base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void *spice_audio_init (void)
|
static void *spice_audio_init(Audiodev *dev)
|
||||||
{
|
{
|
||||||
if (!using_spice) {
|
if (!using_spice) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -130,7 +130,7 @@ static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
|
||||||
settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ;
|
settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ;
|
||||||
#endif
|
#endif
|
||||||
settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
|
settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
|
||||||
settings.fmt = AUD_FMT_S16;
|
settings.fmt = AUDIO_FORMAT_S16;
|
||||||
settings.endianness = AUDIO_HOST_ENDIANNESS;
|
settings.endianness = AUDIO_HOST_ENDIANNESS;
|
||||||
|
|
||||||
audio_pcm_init_info (&hw->info, &settings);
|
audio_pcm_init_info (&hw->info, &settings);
|
||||||
|
@ -258,7 +258,7 @@ static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||||
settings.freq = SPICE_INTERFACE_RECORD_FREQ;
|
settings.freq = SPICE_INTERFACE_RECORD_FREQ;
|
||||||
#endif
|
#endif
|
||||||
settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
|
settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
|
||||||
settings.fmt = AUD_FMT_S16;
|
settings.fmt = AUDIO_FORMAT_S16;
|
||||||
settings.endianness = AUDIO_HOST_ENDIANNESS;
|
settings.endianness = AUDIO_HOST_ENDIANNESS;
|
||||||
|
|
||||||
audio_pcm_init_info (&hw->info, &settings);
|
audio_pcm_init_info (&hw->info, &settings);
|
||||||
|
@ -373,10 +373,6 @@ static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct audio_option audio_options[] = {
|
|
||||||
{ /* end of list */ },
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct audio_pcm_ops audio_callbacks = {
|
static struct audio_pcm_ops audio_callbacks = {
|
||||||
.init_out = line_out_init,
|
.init_out = line_out_init,
|
||||||
.fini_out = line_out_fini,
|
.fini_out = line_out_fini,
|
||||||
|
@ -394,7 +390,6 @@ static struct audio_pcm_ops audio_callbacks = {
|
||||||
static struct audio_driver spice_audio_driver = {
|
static struct audio_driver spice_audio_driver = {
|
||||||
.name = "spice",
|
.name = "spice",
|
||||||
.descr = "spice audio driver",
|
.descr = "spice audio driver",
|
||||||
.options = audio_options,
|
|
||||||
.init = spice_audio_init,
|
.init = spice_audio_init,
|
||||||
.fini = spice_audio_fini,
|
.fini = spice_audio_fini,
|
||||||
.pcm_ops = &audio_callbacks,
|
.pcm_ops = &audio_callbacks,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# See docs/devel/tracing.txt for syntax documentation.
|
# See docs/devel/tracing.txt for syntax documentation.
|
||||||
|
|
||||||
# audio/alsaaudio.c
|
# alsaaudio.c
|
||||||
alsa_revents(int revents) "revents = %d"
|
alsa_revents(int revents) "revents = %d"
|
||||||
alsa_pollout(int i, int fd) "i = %d fd = %d"
|
alsa_pollout(int i, int fd) "i = %d fd = %d"
|
||||||
alsa_set_handler(int events, int index, int fd, int err) "events=0x%x index=%d fd=%d err=%d"
|
alsa_set_handler(int events, int index, int fd, int err) "events=0x%x index=%d fd=%d err=%d"
|
||||||
|
@ -12,11 +12,11 @@ alsa_resume_out(void) "Resuming suspended output stream"
|
||||||
alsa_resume_in(void) "Resuming suspended input stream"
|
alsa_resume_in(void) "Resuming suspended input stream"
|
||||||
alsa_no_frames(int state) "No frames available and ALSA state is %d"
|
alsa_no_frames(int state) "No frames available and ALSA state is %d"
|
||||||
|
|
||||||
# audio/ossaudio.c
|
# ossaudio.c
|
||||||
oss_version(int version) "OSS version = 0x%x"
|
oss_version(int version) "OSS version = 0x%x"
|
||||||
oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d"
|
oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d"
|
||||||
|
|
||||||
# audio/audio.c
|
# audio.c
|
||||||
audio_timer_start(int interval) "interval %d ms"
|
audio_timer_start(int interval) "interval %d ms"
|
||||||
audio_timer_stop(void) ""
|
audio_timer_stop(void) ""
|
||||||
audio_timer_delayed(int interval) "interval %d ms"
|
audio_timer_delayed(int interval) "interval %d ms"
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/host-utils.h"
|
#include "qemu/host-utils.h"
|
||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
|
#include "qapi/opts-visitor.h"
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
|
|
||||||
#define AUDIO_CAP "wav"
|
#define AUDIO_CAP "wav"
|
||||||
|
@ -37,11 +38,6 @@ typedef struct WAVVoiceOut {
|
||||||
int total_samples;
|
int total_samples;
|
||||||
} WAVVoiceOut;
|
} WAVVoiceOut;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
struct audsettings settings;
|
|
||||||
const char *wav_path;
|
|
||||||
} WAVConf;
|
|
||||||
|
|
||||||
static int wav_run_out (HWVoiceOut *hw, int live)
|
static int wav_run_out (HWVoiceOut *hw, int live)
|
||||||
{
|
{
|
||||||
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
|
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
|
||||||
|
@ -112,25 +108,30 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
|
0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
|
||||||
0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
|
0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
|
||||||
};
|
};
|
||||||
WAVConf *conf = drv_opaque;
|
Audiodev *dev = drv_opaque;
|
||||||
struct audsettings wav_as = conf->settings;
|
AudiodevWavOptions *wopts = &dev->u.wav;
|
||||||
|
struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out);
|
||||||
|
const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav";
|
||||||
|
|
||||||
stereo = wav_as.nchannels == 2;
|
stereo = wav_as.nchannels == 2;
|
||||||
switch (wav_as.fmt) {
|
switch (wav_as.fmt) {
|
||||||
case AUD_FMT_S8:
|
case AUDIO_FORMAT_S8:
|
||||||
case AUD_FMT_U8:
|
case AUDIO_FORMAT_U8:
|
||||||
bits16 = 0;
|
bits16 = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AUD_FMT_S16:
|
case AUDIO_FORMAT_S16:
|
||||||
case AUD_FMT_U16:
|
case AUDIO_FORMAT_U16:
|
||||||
bits16 = 1;
|
bits16 = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AUD_FMT_S32:
|
case AUDIO_FORMAT_S32:
|
||||||
case AUD_FMT_U32:
|
case AUDIO_FORMAT_U32:
|
||||||
dolog ("WAVE files can not handle 32bit formats\n");
|
dolog ("WAVE files can not handle 32bit formats\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
hdr[34] = bits16 ? 0x10 : 0x08;
|
hdr[34] = bits16 ? 0x10 : 0x08;
|
||||||
|
@ -151,10 +152,10 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
|
le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
|
||||||
le_store (hdr + 32, 1 << (bits16 + stereo), 2);
|
le_store (hdr + 32, 1 << (bits16 + stereo), 2);
|
||||||
|
|
||||||
wav->f = fopen (conf->wav_path, "wb");
|
wav->f = fopen(wav_path, "wb");
|
||||||
if (!wav->f) {
|
if (!wav->f) {
|
||||||
dolog ("Failed to open wave file `%s'\nReason: %s\n",
|
dolog ("Failed to open wave file `%s'\nReason: %s\n",
|
||||||
conf->wav_path, strerror (errno));
|
wav_path, strerror(errno));
|
||||||
g_free (wav->pcm_buf);
|
g_free (wav->pcm_buf);
|
||||||
wav->pcm_buf = NULL;
|
wav->pcm_buf = NULL;
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -222,54 +223,17 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static WAVConf glob_conf = {
|
static void *wav_audio_init(Audiodev *dev)
|
||||||
.settings.freq = 44100,
|
|
||||||
.settings.nchannels = 2,
|
|
||||||
.settings.fmt = AUD_FMT_S16,
|
|
||||||
.wav_path = "qemu.wav"
|
|
||||||
};
|
|
||||||
|
|
||||||
static void *wav_audio_init (void)
|
|
||||||
{
|
{
|
||||||
WAVConf *conf = g_malloc(sizeof(WAVConf));
|
assert(dev->driver == AUDIODEV_DRIVER_WAV);
|
||||||
*conf = glob_conf;
|
return dev;
|
||||||
return conf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wav_audio_fini (void *opaque)
|
static void wav_audio_fini (void *opaque)
|
||||||
{
|
{
|
||||||
ldebug ("wav_fini");
|
ldebug ("wav_fini");
|
||||||
g_free(opaque);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct audio_option wav_options[] = {
|
|
||||||
{
|
|
||||||
.name = "FREQUENCY",
|
|
||||||
.tag = AUD_OPT_INT,
|
|
||||||
.valp = &glob_conf.settings.freq,
|
|
||||||
.descr = "Frequency"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "FORMAT",
|
|
||||||
.tag = AUD_OPT_FMT,
|
|
||||||
.valp = &glob_conf.settings.fmt,
|
|
||||||
.descr = "Format"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "DAC_FIXED_CHANNELS",
|
|
||||||
.tag = AUD_OPT_INT,
|
|
||||||
.valp = &glob_conf.settings.nchannels,
|
|
||||||
.descr = "Number of channels (1 - mono, 2 - stereo)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "PATH",
|
|
||||||
.tag = AUD_OPT_STR,
|
|
||||||
.valp = &glob_conf.wav_path,
|
|
||||||
.descr = "Path to wave file"
|
|
||||||
},
|
|
||||||
{ /* End of list */ }
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct audio_pcm_ops wav_pcm_ops = {
|
static struct audio_pcm_ops wav_pcm_ops = {
|
||||||
.init_out = wav_init_out,
|
.init_out = wav_init_out,
|
||||||
.fini_out = wav_fini_out,
|
.fini_out = wav_fini_out,
|
||||||
|
@ -281,7 +245,6 @@ static struct audio_pcm_ops wav_pcm_ops = {
|
||||||
static struct audio_driver wav_audio_driver = {
|
static struct audio_driver wav_audio_driver = {
|
||||||
.name = "wav",
|
.name = "wav",
|
||||||
.descr = "WAV renderer http://wikipedia.org/wiki/WAV",
|
.descr = "WAV renderer http://wikipedia.org/wiki/WAV",
|
||||||
.options = wav_options,
|
|
||||||
.init = wav_audio_init,
|
.init = wav_audio_init,
|
||||||
.fini = wav_audio_fini,
|
.fini = wav_audio_fini,
|
||||||
.pcm_ops = &wav_pcm_ops,
|
.pcm_ops = &wav_pcm_ops,
|
||||||
|
|
|
@ -38,30 +38,29 @@ static void wav_destroy (void *opaque)
|
||||||
uint8_t dlen[4];
|
uint8_t dlen[4];
|
||||||
uint32_t datalen = wav->bytes;
|
uint32_t datalen = wav->bytes;
|
||||||
uint32_t rifflen = datalen + 36;
|
uint32_t rifflen = datalen + 36;
|
||||||
Monitor *mon = cur_mon;
|
|
||||||
|
|
||||||
if (wav->f) {
|
if (wav->f) {
|
||||||
le_store (rlen, rifflen, 4);
|
le_store (rlen, rifflen, 4);
|
||||||
le_store (dlen, datalen, 4);
|
le_store (dlen, datalen, 4);
|
||||||
|
|
||||||
if (fseek (wav->f, 4, SEEK_SET)) {
|
if (fseek (wav->f, 4, SEEK_SET)) {
|
||||||
monitor_printf (mon, "wav_destroy: rlen fseek failed\nReason: %s\n",
|
error_report("wav_destroy: rlen fseek failed: %s",
|
||||||
strerror (errno));
|
strerror(errno));
|
||||||
goto doclose;
|
goto doclose;
|
||||||
}
|
}
|
||||||
if (fwrite (rlen, 4, 1, wav->f) != 1) {
|
if (fwrite (rlen, 4, 1, wav->f) != 1) {
|
||||||
monitor_printf (mon, "wav_destroy: rlen fwrite failed\nReason %s\n",
|
error_report("wav_destroy: rlen fwrite failed: %s",
|
||||||
strerror (errno));
|
strerror(errno));
|
||||||
goto doclose;
|
goto doclose;
|
||||||
}
|
}
|
||||||
if (fseek (wav->f, 32, SEEK_CUR)) {
|
if (fseek (wav->f, 32, SEEK_CUR)) {
|
||||||
monitor_printf (mon, "wav_destroy: dlen fseek failed\nReason %s\n",
|
error_report("wav_destroy: dlen fseek failed: %s",
|
||||||
strerror (errno));
|
strerror(errno));
|
||||||
goto doclose;
|
goto doclose;
|
||||||
}
|
}
|
||||||
if (fwrite (dlen, 1, 4, wav->f) != 4) {
|
if (fwrite (dlen, 1, 4, wav->f) != 4) {
|
||||||
monitor_printf (mon, "wav_destroy: dlen fwrite failed\nReason %s\n",
|
error_report("wav_destroy: dlen fwrite failed: %s",
|
||||||
strerror (errno));
|
strerror(errno));
|
||||||
goto doclose;
|
goto doclose;
|
||||||
}
|
}
|
||||||
doclose:
|
doclose:
|
||||||
|
@ -78,8 +77,7 @@ static void wav_capture (void *opaque, void *buf, int size)
|
||||||
WAVState *wav = opaque;
|
WAVState *wav = opaque;
|
||||||
|
|
||||||
if (fwrite (buf, size, 1, wav->f) != 1) {
|
if (fwrite (buf, size, 1, wav->f) != 1) {
|
||||||
monitor_printf (cur_mon, "wav_capture: fwrite error\nReason: %s",
|
error_report("wav_capture: fwrite error: %s", strerror(errno));
|
||||||
strerror (errno));
|
|
||||||
}
|
}
|
||||||
wav->bytes += size;
|
wav->bytes += size;
|
||||||
}
|
}
|
||||||
|
@ -110,7 +108,6 @@ static struct capture_ops wav_capture_ops = {
|
||||||
int wav_start_capture (CaptureState *s, const char *path, int freq,
|
int wav_start_capture (CaptureState *s, const char *path, int freq,
|
||||||
int bits, int nchannels)
|
int bits, int nchannels)
|
||||||
{
|
{
|
||||||
Monitor *mon = cur_mon;
|
|
||||||
WAVState *wav;
|
WAVState *wav;
|
||||||
uint8_t hdr[] = {
|
uint8_t hdr[] = {
|
||||||
0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
|
0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
|
||||||
|
@ -124,13 +121,13 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
|
||||||
CaptureVoiceOut *cap;
|
CaptureVoiceOut *cap;
|
||||||
|
|
||||||
if (bits != 8 && bits != 16) {
|
if (bits != 8 && bits != 16) {
|
||||||
monitor_printf (mon, "incorrect bit count %d, must be 8 or 16\n", bits);
|
error_report("incorrect bit count %d, must be 8 or 16", bits);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nchannels != 1 && nchannels != 2) {
|
if (nchannels != 1 && nchannels != 2) {
|
||||||
monitor_printf (mon, "incorrect channel count %d, must be 1 or 2\n",
|
error_report("incorrect channel count %d, must be 1 or 2",
|
||||||
nchannels);
|
nchannels);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +136,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
|
||||||
|
|
||||||
as.freq = freq;
|
as.freq = freq;
|
||||||
as.nchannels = 1 << stereo;
|
as.nchannels = 1 << stereo;
|
||||||
as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
|
as.fmt = bits16 ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8;
|
||||||
as.endianness = 0;
|
as.endianness = 0;
|
||||||
|
|
||||||
ops.notify = wav_notify;
|
ops.notify = wav_notify;
|
||||||
|
@ -158,8 +155,8 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
|
||||||
|
|
||||||
wav->f = fopen (path, "wb");
|
wav->f = fopen (path, "wb");
|
||||||
if (!wav->f) {
|
if (!wav->f) {
|
||||||
monitor_printf (mon, "Failed to open wave file `%s'\nReason: %s\n",
|
error_report("Failed to open wave file `%s': %s",
|
||||||
path, strerror (errno));
|
path, strerror(errno));
|
||||||
g_free (wav);
|
g_free (wav);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -170,14 +167,13 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
|
||||||
wav->freq = freq;
|
wav->freq = freq;
|
||||||
|
|
||||||
if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) {
|
if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) {
|
||||||
monitor_printf (mon, "Failed to write header\nReason: %s\n",
|
error_report("Failed to write header: %s", strerror(errno));
|
||||||
strerror (errno));
|
|
||||||
goto error_free;
|
goto error_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
cap = AUD_add_capture (&as, &ops, wav);
|
cap = AUD_add_capture (&as, &ops, wav);
|
||||||
if (!cap) {
|
if (!cap) {
|
||||||
monitor_printf (mon, "Failed to add audio capture\n");
|
error_report("Failed to add audio capture");
|
||||||
goto error_free;
|
goto error_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,8 +185,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
|
||||||
error_free:
|
error_free:
|
||||||
g_free (wav->path);
|
g_free (wav->path);
|
||||||
if (fclose (wav->f)) {
|
if (fclose (wav->f)) {
|
||||||
monitor_printf (mon, "Failed to close wave file\nReason: %s\n",
|
error_report("Failed to close wave file: %s", strerror(errno));
|
||||||
strerror (errno));
|
|
||||||
}
|
}
|
||||||
g_free (wav);
|
g_free (wav);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
authz-obj-y += base.o
|
||||||
|
authz-obj-y += simple.o
|
||||||
|
authz-obj-y += list.o
|
||||||
|
authz-obj-y += listfile.o
|
||||||
|
authz-obj-$(CONFIG_AUTH_PAM) += pamacct.o
|
||||||
|
|
||||||
|
pamacct.o-libs = -lpam
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* QEMU authorization framework base class
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "authz/base.h"
|
||||||
|
#include "authz/trace.h"
|
||||||
|
|
||||||
|
bool qauthz_is_allowed(QAuthZ *authz,
|
||||||
|
const char *identity,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
QAuthZClass *cls = QAUTHZ_GET_CLASS(authz);
|
||||||
|
bool allowed;
|
||||||
|
|
||||||
|
allowed = cls->is_allowed(authz, identity, errp);
|
||||||
|
trace_qauthz_is_allowed(authz, identity, allowed);
|
||||||
|
|
||||||
|
return allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool qauthz_is_allowed_by_id(const char *authzid,
|
||||||
|
const char *identity,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
QAuthZ *authz;
|
||||||
|
Object *obj;
|
||||||
|
Object *container;
|
||||||
|
|
||||||
|
container = object_get_objects_root();
|
||||||
|
obj = object_resolve_path_component(container,
|
||||||
|
authzid);
|
||||||
|
if (!obj) {
|
||||||
|
error_setg(errp, "Cannot find QAuthZ object ID %s",
|
||||||
|
authzid);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!object_dynamic_cast(obj, TYPE_QAUTHZ)) {
|
||||||
|
error_setg(errp, "Object '%s' is not a QAuthZ subclass",
|
||||||
|
authzid);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
authz = QAUTHZ(obj);
|
||||||
|
|
||||||
|
return qauthz_is_allowed(authz, identity, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const TypeInfo authz_info = {
|
||||||
|
.parent = TYPE_OBJECT,
|
||||||
|
.name = TYPE_QAUTHZ,
|
||||||
|
.instance_size = sizeof(QAuthZ),
|
||||||
|
.class_size = sizeof(QAuthZClass),
|
||||||
|
.abstract = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void qauthz_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&authz_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(qauthz_register_types)
|
||||||
|
|
|
@ -0,0 +1,271 @@
|
||||||
|
/*
|
||||||
|
* QEMU access control list authorization driver
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "authz/list.h"
|
||||||
|
#include "authz/trace.h"
|
||||||
|
#include "qom/object_interfaces.h"
|
||||||
|
#include "qapi/qapi-visit-authz.h"
|
||||||
|
|
||||||
|
static bool qauthz_list_is_allowed(QAuthZ *authz,
|
||||||
|
const char *identity,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
QAuthZList *lauthz = QAUTHZ_LIST(authz);
|
||||||
|
QAuthZListRuleList *rules = lauthz->rules;
|
||||||
|
|
||||||
|
while (rules) {
|
||||||
|
QAuthZListRule *rule = rules->value;
|
||||||
|
QAuthZListFormat format = rule->has_format ? rule->format :
|
||||||
|
QAUTHZ_LIST_FORMAT_EXACT;
|
||||||
|
|
||||||
|
trace_qauthz_list_check_rule(authz, rule->match, identity,
|
||||||
|
format, rule->policy);
|
||||||
|
switch (format) {
|
||||||
|
case QAUTHZ_LIST_FORMAT_EXACT:
|
||||||
|
if (g_str_equal(rule->match, identity)) {
|
||||||
|
return rule->policy == QAUTHZ_LIST_POLICY_ALLOW;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QAUTHZ_LIST_FORMAT_GLOB:
|
||||||
|
if (g_pattern_match_simple(rule->match, identity)) {
|
||||||
|
return rule->policy == QAUTHZ_LIST_POLICY_ALLOW;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_warn_if_reached();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
rules = rules->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_qauthz_list_default_policy(authz, identity, lauthz->policy);
|
||||||
|
return lauthz->policy == QAUTHZ_LIST_POLICY_ALLOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_list_prop_set_policy(Object *obj,
|
||||||
|
int value,
|
||||||
|
Error **errp G_GNUC_UNUSED)
|
||||||
|
{
|
||||||
|
QAuthZList *lauthz = QAUTHZ_LIST(obj);
|
||||||
|
|
||||||
|
lauthz->policy = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
qauthz_list_prop_get_policy(Object *obj,
|
||||||
|
Error **errp G_GNUC_UNUSED)
|
||||||
|
{
|
||||||
|
QAuthZList *lauthz = QAUTHZ_LIST(obj);
|
||||||
|
|
||||||
|
return lauthz->policy;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_list_prop_get_rules(Object *obj, Visitor *v, const char *name,
|
||||||
|
void *opaque, Error **errp)
|
||||||
|
{
|
||||||
|
QAuthZList *lauthz = QAUTHZ_LIST(obj);
|
||||||
|
|
||||||
|
visit_type_QAuthZListRuleList(v, name, &lauthz->rules, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_list_prop_set_rules(Object *obj, Visitor *v, const char *name,
|
||||||
|
void *opaque, Error **errp)
|
||||||
|
{
|
||||||
|
QAuthZList *lauthz = QAUTHZ_LIST(obj);
|
||||||
|
QAuthZListRuleList *oldrules;
|
||||||
|
|
||||||
|
oldrules = lauthz->rules;
|
||||||
|
visit_type_QAuthZListRuleList(v, name, &lauthz->rules, errp);
|
||||||
|
|
||||||
|
qapi_free_QAuthZListRuleList(oldrules);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_list_finalize(Object *obj)
|
||||||
|
{
|
||||||
|
QAuthZList *lauthz = QAUTHZ_LIST(obj);
|
||||||
|
|
||||||
|
qapi_free_QAuthZListRuleList(lauthz->rules);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_list_class_init(ObjectClass *oc, void *data)
|
||||||
|
{
|
||||||
|
QAuthZClass *authz = QAUTHZ_CLASS(oc);
|
||||||
|
|
||||||
|
object_class_property_add_enum(oc, "policy",
|
||||||
|
"QAuthZListPolicy",
|
||||||
|
&QAuthZListPolicy_lookup,
|
||||||
|
qauthz_list_prop_get_policy,
|
||||||
|
qauthz_list_prop_set_policy,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
object_class_property_add(oc, "rules", "QAuthZListRule",
|
||||||
|
qauthz_list_prop_get_rules,
|
||||||
|
qauthz_list_prop_set_rules,
|
||||||
|
NULL, NULL, NULL);
|
||||||
|
|
||||||
|
authz->is_allowed = qauthz_list_is_allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QAuthZList *qauthz_list_new(const char *id,
|
||||||
|
QAuthZListPolicy policy,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
return QAUTHZ_LIST(
|
||||||
|
object_new_with_props(TYPE_QAUTHZ_LIST,
|
||||||
|
object_get_objects_root(),
|
||||||
|
id, errp,
|
||||||
|
"policy", QAuthZListPolicy_str(policy),
|
||||||
|
NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t qauthz_list_append_rule(QAuthZList *auth,
|
||||||
|
const char *match,
|
||||||
|
QAuthZListPolicy policy,
|
||||||
|
QAuthZListFormat format,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
QAuthZListRule *rule;
|
||||||
|
QAuthZListRuleList *rules, *tmp;
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
rule = g_new0(QAuthZListRule, 1);
|
||||||
|
rule->policy = policy;
|
||||||
|
rule->match = g_strdup(match);
|
||||||
|
rule->format = format;
|
||||||
|
rule->has_format = true;
|
||||||
|
|
||||||
|
tmp = g_new0(QAuthZListRuleList, 1);
|
||||||
|
tmp->value = rule;
|
||||||
|
|
||||||
|
rules = auth->rules;
|
||||||
|
if (rules) {
|
||||||
|
while (rules->next) {
|
||||||
|
i++;
|
||||||
|
rules = rules->next;
|
||||||
|
}
|
||||||
|
rules->next = tmp;
|
||||||
|
return i + 1;
|
||||||
|
} else {
|
||||||
|
auth->rules = tmp;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ssize_t qauthz_list_insert_rule(QAuthZList *auth,
|
||||||
|
const char *match,
|
||||||
|
QAuthZListPolicy policy,
|
||||||
|
QAuthZListFormat format,
|
||||||
|
size_t index,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
QAuthZListRule *rule;
|
||||||
|
QAuthZListRuleList *rules, *tmp;
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
rule = g_new0(QAuthZListRule, 1);
|
||||||
|
rule->policy = policy;
|
||||||
|
rule->match = g_strdup(match);
|
||||||
|
rule->format = format;
|
||||||
|
rule->has_format = true;
|
||||||
|
|
||||||
|
tmp = g_new0(QAuthZListRuleList, 1);
|
||||||
|
tmp->value = rule;
|
||||||
|
|
||||||
|
rules = auth->rules;
|
||||||
|
if (rules && index > 0) {
|
||||||
|
while (rules->next && i < (index - 1)) {
|
||||||
|
i++;
|
||||||
|
rules = rules->next;
|
||||||
|
}
|
||||||
|
tmp->next = rules->next;
|
||||||
|
rules->next = tmp;
|
||||||
|
return i + 1;
|
||||||
|
} else {
|
||||||
|
tmp->next = auth->rules;
|
||||||
|
auth->rules = tmp;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ssize_t qauthz_list_delete_rule(QAuthZList *auth, const char *match)
|
||||||
|
{
|
||||||
|
QAuthZListRule *rule;
|
||||||
|
QAuthZListRuleList *rules, *prev;
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
prev = NULL;
|
||||||
|
rules = auth->rules;
|
||||||
|
while (rules) {
|
||||||
|
rule = rules->value;
|
||||||
|
if (g_str_equal(rule->match, match)) {
|
||||||
|
if (prev) {
|
||||||
|
prev->next = rules->next;
|
||||||
|
} else {
|
||||||
|
auth->rules = rules->next;
|
||||||
|
}
|
||||||
|
rules->next = NULL;
|
||||||
|
qapi_free_QAuthZListRuleList(rules);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
prev = rules;
|
||||||
|
rules = rules->next;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const TypeInfo qauthz_list_info = {
|
||||||
|
.parent = TYPE_QAUTHZ,
|
||||||
|
.name = TYPE_QAUTHZ_LIST,
|
||||||
|
.instance_size = sizeof(QAuthZList),
|
||||||
|
.instance_finalize = qauthz_list_finalize,
|
||||||
|
.class_size = sizeof(QAuthZListClass),
|
||||||
|
.class_init = qauthz_list_class_init,
|
||||||
|
.interfaces = (InterfaceInfo[]) {
|
||||||
|
{ TYPE_USER_CREATABLE },
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_list_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&qauthz_list_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type_init(qauthz_list_register_types);
|
|
@ -0,0 +1,283 @@
|
||||||
|
/*
|
||||||
|
* QEMU access control list file authorization driver
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "authz/listfile.h"
|
||||||
|
#include "authz/trace.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "qemu/main-loop.h"
|
||||||
|
#include "qemu/sockets.h"
|
||||||
|
#include "qemu/filemonitor.h"
|
||||||
|
#include "qom/object_interfaces.h"
|
||||||
|
#include "qapi/qapi-visit-authz.h"
|
||||||
|
#include "qapi/qmp/qjson.h"
|
||||||
|
#include "qapi/qmp/qobject.h"
|
||||||
|
#include "qapi/qmp/qerror.h"
|
||||||
|
#include "qapi/qobject-input-visitor.h"
|
||||||
|
|
||||||
|
|
||||||
|
static bool
|
||||||
|
qauthz_list_file_is_allowed(QAuthZ *authz,
|
||||||
|
const char *identity,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(authz);
|
||||||
|
if (fauthz->list) {
|
||||||
|
return qauthz_is_allowed(fauthz->list, identity, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static QAuthZ *
|
||||||
|
qauthz_list_file_load(QAuthZListFile *fauthz, Error **errp)
|
||||||
|
{
|
||||||
|
GError *err = NULL;
|
||||||
|
gchar *content = NULL;
|
||||||
|
gsize len;
|
||||||
|
QObject *obj = NULL;
|
||||||
|
QDict *pdict;
|
||||||
|
Visitor *v = NULL;
|
||||||
|
QAuthZ *ret = NULL;
|
||||||
|
|
||||||
|
trace_qauthz_list_file_load(fauthz, fauthz->filename);
|
||||||
|
if (!g_file_get_contents(fauthz->filename, &content, &len, &err)) {
|
||||||
|
error_setg(errp, "Unable to read '%s': %s",
|
||||||
|
fauthz->filename, err->message);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = qobject_from_json(content, errp);
|
||||||
|
if (!obj) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdict = qobject_to(QDict, obj);
|
||||||
|
if (!pdict) {
|
||||||
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "obj", "dict");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
v = qobject_input_visitor_new(obj);
|
||||||
|
|
||||||
|
ret = (QAuthZ *)user_creatable_add_type(TYPE_QAUTHZ_LIST,
|
||||||
|
NULL, pdict, v, errp);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
visit_free(v);
|
||||||
|
qobject_unref(obj);
|
||||||
|
if (err) {
|
||||||
|
g_error_free(err);
|
||||||
|
}
|
||||||
|
g_free(content);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_list_file_event(int64_t wd G_GNUC_UNUSED,
|
||||||
|
QFileMonitorEvent ev G_GNUC_UNUSED,
|
||||||
|
const char *name G_GNUC_UNUSED,
|
||||||
|
void *opaque)
|
||||||
|
{
|
||||||
|
QAuthZListFile *fauthz = opaque;
|
||||||
|
Error *err = NULL;
|
||||||
|
|
||||||
|
if (ev != QFILE_MONITOR_EVENT_MODIFIED &&
|
||||||
|
ev != QFILE_MONITOR_EVENT_CREATED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
object_unref(OBJECT(fauthz->list));
|
||||||
|
fauthz->list = qauthz_list_file_load(fauthz, &err);
|
||||||
|
trace_qauthz_list_file_refresh(fauthz,
|
||||||
|
fauthz->filename, fauthz->list ? 1 : 0);
|
||||||
|
if (!fauthz->list) {
|
||||||
|
error_report_err(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_list_file_complete(UserCreatable *uc, Error **errp)
|
||||||
|
{
|
||||||
|
QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(uc);
|
||||||
|
gchar *dir = NULL, *file = NULL;
|
||||||
|
|
||||||
|
fauthz->list = qauthz_list_file_load(fauthz, errp);
|
||||||
|
|
||||||
|
if (!fauthz->refresh) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fauthz->file_monitor = qemu_file_monitor_new(errp);
|
||||||
|
if (!fauthz->file_monitor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dir = g_path_get_dirname(fauthz->filename);
|
||||||
|
if (g_str_equal(dir, ".")) {
|
||||||
|
error_setg(errp, "Filename must be an absolute path");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
file = g_path_get_basename(fauthz->filename);
|
||||||
|
if (g_str_equal(file, ".")) {
|
||||||
|
error_setg(errp, "Path has no trailing filename component");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
fauthz->file_watch = qemu_file_monitor_add_watch(
|
||||||
|
fauthz->file_monitor, dir, file,
|
||||||
|
qauthz_list_file_event, fauthz, errp);
|
||||||
|
if (fauthz->file_watch < 0) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
g_free(file);
|
||||||
|
g_free(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_list_file_prop_set_filename(Object *obj,
|
||||||
|
const char *value,
|
||||||
|
Error **errp G_GNUC_UNUSED)
|
||||||
|
{
|
||||||
|
QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
|
||||||
|
|
||||||
|
g_free(fauthz->filename);
|
||||||
|
fauthz->filename = g_strdup(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char *
|
||||||
|
qauthz_list_file_prop_get_filename(Object *obj,
|
||||||
|
Error **errp G_GNUC_UNUSED)
|
||||||
|
{
|
||||||
|
QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
|
||||||
|
|
||||||
|
return g_strdup(fauthz->filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_list_file_prop_set_refresh(Object *obj,
|
||||||
|
bool value,
|
||||||
|
Error **errp G_GNUC_UNUSED)
|
||||||
|
{
|
||||||
|
QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
|
||||||
|
|
||||||
|
fauthz->refresh = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool
|
||||||
|
qauthz_list_file_prop_get_refresh(Object *obj,
|
||||||
|
Error **errp G_GNUC_UNUSED)
|
||||||
|
{
|
||||||
|
QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
|
||||||
|
|
||||||
|
return fauthz->refresh;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_list_file_finalize(Object *obj)
|
||||||
|
{
|
||||||
|
QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
|
||||||
|
|
||||||
|
object_unref(OBJECT(fauthz->list));
|
||||||
|
g_free(fauthz->filename);
|
||||||
|
qemu_file_monitor_free(fauthz->file_monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_list_file_class_init(ObjectClass *oc, void *data)
|
||||||
|
{
|
||||||
|
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||||
|
QAuthZClass *authz = QAUTHZ_CLASS(oc);
|
||||||
|
|
||||||
|
ucc->complete = qauthz_list_file_complete;
|
||||||
|
|
||||||
|
object_class_property_add_str(oc, "filename",
|
||||||
|
qauthz_list_file_prop_get_filename,
|
||||||
|
qauthz_list_file_prop_set_filename,
|
||||||
|
NULL);
|
||||||
|
object_class_property_add_bool(oc, "refresh",
|
||||||
|
qauthz_list_file_prop_get_refresh,
|
||||||
|
qauthz_list_file_prop_set_refresh,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
authz->is_allowed = qauthz_list_file_is_allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_list_file_init(Object *obj)
|
||||||
|
{
|
||||||
|
QAuthZListFile *authz = QAUTHZ_LIST_FILE(obj);
|
||||||
|
|
||||||
|
authz->file_watch = -1;
|
||||||
|
#ifdef CONFIG_INOTIFY1
|
||||||
|
authz->refresh = TRUE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QAuthZListFile *qauthz_list_file_new(const char *id,
|
||||||
|
const char *filename,
|
||||||
|
bool refresh,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
return QAUTHZ_LIST_FILE(
|
||||||
|
object_new_with_props(TYPE_QAUTHZ_LIST_FILE,
|
||||||
|
object_get_objects_root(),
|
||||||
|
id, errp,
|
||||||
|
"filename", filename,
|
||||||
|
"refresh", refresh ? "yes" : "no",
|
||||||
|
NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const TypeInfo qauthz_list_file_info = {
|
||||||
|
.parent = TYPE_QAUTHZ,
|
||||||
|
.name = TYPE_QAUTHZ_LIST_FILE,
|
||||||
|
.instance_init = qauthz_list_file_init,
|
||||||
|
.instance_size = sizeof(QAuthZListFile),
|
||||||
|
.instance_finalize = qauthz_list_file_finalize,
|
||||||
|
.class_size = sizeof(QAuthZListFileClass),
|
||||||
|
.class_init = qauthz_list_file_class_init,
|
||||||
|
.interfaces = (InterfaceInfo[]) {
|
||||||
|
{ TYPE_USER_CREATABLE },
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_list_file_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&qauthz_list_file_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type_init(qauthz_list_file_register_types);
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* QEMU PAM authorization driver
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "authz/pamacct.h"
|
||||||
|
#include "authz/trace.h"
|
||||||
|
#include "qom/object_interfaces.h"
|
||||||
|
|
||||||
|
#include <security/pam_appl.h>
|
||||||
|
|
||||||
|
|
||||||
|
static bool qauthz_pam_is_allowed(QAuthZ *authz,
|
||||||
|
const char *identity,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
QAuthZPAM *pauthz = QAUTHZ_PAM(authz);
|
||||||
|
const struct pam_conv pam_conversation = { 0 };
|
||||||
|
pam_handle_t *pamh = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
trace_qauthz_pam_check(authz, identity, pauthz->service);
|
||||||
|
ret = pam_start(pauthz->service,
|
||||||
|
identity,
|
||||||
|
&pam_conversation,
|
||||||
|
&pamh);
|
||||||
|
if (ret != PAM_SUCCESS) {
|
||||||
|
error_setg(errp, "Unable to start PAM transaction: %s",
|
||||||
|
pam_strerror(NULL, ret));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = pam_acct_mgmt(pamh, PAM_SILENT);
|
||||||
|
pam_end(pamh, ret);
|
||||||
|
if (ret != PAM_SUCCESS) {
|
||||||
|
error_setg(errp, "Unable to authorize user '%s': %s",
|
||||||
|
identity, pam_strerror(pamh, ret));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_pam_prop_set_service(Object *obj,
|
||||||
|
const char *service,
|
||||||
|
Error **errp G_GNUC_UNUSED)
|
||||||
|
{
|
||||||
|
QAuthZPAM *pauthz = QAUTHZ_PAM(obj);
|
||||||
|
|
||||||
|
g_free(pauthz->service);
|
||||||
|
pauthz->service = g_strdup(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char *
|
||||||
|
qauthz_pam_prop_get_service(Object *obj,
|
||||||
|
Error **errp G_GNUC_UNUSED)
|
||||||
|
{
|
||||||
|
QAuthZPAM *pauthz = QAUTHZ_PAM(obj);
|
||||||
|
|
||||||
|
return g_strdup(pauthz->service);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_pam_complete(UserCreatable *uc, Error **errp)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_pam_finalize(Object *obj)
|
||||||
|
{
|
||||||
|
QAuthZPAM *pauthz = QAUTHZ_PAM(obj);
|
||||||
|
|
||||||
|
g_free(pauthz->service);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_pam_class_init(ObjectClass *oc, void *data)
|
||||||
|
{
|
||||||
|
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||||
|
QAuthZClass *authz = QAUTHZ_CLASS(oc);
|
||||||
|
|
||||||
|
ucc->complete = qauthz_pam_complete;
|
||||||
|
authz->is_allowed = qauthz_pam_is_allowed;
|
||||||
|
|
||||||
|
object_class_property_add_str(oc, "service",
|
||||||
|
qauthz_pam_prop_get_service,
|
||||||
|
qauthz_pam_prop_set_service,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QAuthZPAM *qauthz_pam_new(const char *id,
|
||||||
|
const char *service,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
return QAUTHZ_PAM(
|
||||||
|
object_new_with_props(TYPE_QAUTHZ_PAM,
|
||||||
|
object_get_objects_root(),
|
||||||
|
id, errp,
|
||||||
|
"service", service,
|
||||||
|
NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const TypeInfo qauthz_pam_info = {
|
||||||
|
.parent = TYPE_QAUTHZ,
|
||||||
|
.name = TYPE_QAUTHZ_PAM,
|
||||||
|
.instance_size = sizeof(QAuthZPAM),
|
||||||
|
.instance_finalize = qauthz_pam_finalize,
|
||||||
|
.class_size = sizeof(QAuthZPAMClass),
|
||||||
|
.class_init = qauthz_pam_class_init,
|
||||||
|
.interfaces = (InterfaceInfo[]) {
|
||||||
|
{ TYPE_USER_CREATABLE },
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_pam_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&qauthz_pam_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type_init(qauthz_pam_register_types);
|
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
* QEMU simple authorization driver
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "authz/simple.h"
|
||||||
|
#include "authz/trace.h"
|
||||||
|
#include "qom/object_interfaces.h"
|
||||||
|
|
||||||
|
static bool qauthz_simple_is_allowed(QAuthZ *authz,
|
||||||
|
const char *identity,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
QAuthZSimple *sauthz = QAUTHZ_SIMPLE(authz);
|
||||||
|
|
||||||
|
trace_qauthz_simple_is_allowed(authz, sauthz->identity, identity);
|
||||||
|
return g_str_equal(identity, sauthz->identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_simple_prop_set_identity(Object *obj,
|
||||||
|
const char *value,
|
||||||
|
Error **errp G_GNUC_UNUSED)
|
||||||
|
{
|
||||||
|
QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
|
||||||
|
|
||||||
|
g_free(sauthz->identity);
|
||||||
|
sauthz->identity = g_strdup(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char *
|
||||||
|
qauthz_simple_prop_get_identity(Object *obj,
|
||||||
|
Error **errp G_GNUC_UNUSED)
|
||||||
|
{
|
||||||
|
QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
|
||||||
|
|
||||||
|
return g_strdup(sauthz->identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_simple_finalize(Object *obj)
|
||||||
|
{
|
||||||
|
QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
|
||||||
|
|
||||||
|
g_free(sauthz->identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_simple_class_init(ObjectClass *oc, void *data)
|
||||||
|
{
|
||||||
|
QAuthZClass *authz = QAUTHZ_CLASS(oc);
|
||||||
|
|
||||||
|
authz->is_allowed = qauthz_simple_is_allowed;
|
||||||
|
|
||||||
|
object_class_property_add_str(oc, "identity",
|
||||||
|
qauthz_simple_prop_get_identity,
|
||||||
|
qauthz_simple_prop_set_identity,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QAuthZSimple *qauthz_simple_new(const char *id,
|
||||||
|
const char *identity,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
return QAUTHZ_SIMPLE(
|
||||||
|
object_new_with_props(TYPE_QAUTHZ_SIMPLE,
|
||||||
|
object_get_objects_root(),
|
||||||
|
id, errp,
|
||||||
|
"identity", identity,
|
||||||
|
NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const TypeInfo qauthz_simple_info = {
|
||||||
|
.parent = TYPE_QAUTHZ,
|
||||||
|
.name = TYPE_QAUTHZ_SIMPLE,
|
||||||
|
.instance_size = sizeof(QAuthZSimple),
|
||||||
|
.instance_finalize = qauthz_simple_finalize,
|
||||||
|
.class_size = sizeof(QAuthZSimpleClass),
|
||||||
|
.class_init = qauthz_simple_class_init,
|
||||||
|
.interfaces = (InterfaceInfo[]) {
|
||||||
|
{ TYPE_USER_CREATABLE },
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qauthz_simple_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&qauthz_simple_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type_init(qauthz_simple_register_types);
|
|
@ -0,0 +1,18 @@
|
||||||
|
# See docs/devel/tracing.txt for syntax documentation.
|
||||||
|
|
||||||
|
# base.c
|
||||||
|
qauthz_is_allowed(void *authz, const char *identity, bool allowed) "AuthZ %p check identity=%s allowed=%d"
|
||||||
|
|
||||||
|
# simple.c
|
||||||
|
qauthz_simple_is_allowed(void *authz, const char *wantidentity, const char *gotidentity) "AuthZ simple %p check want identity=%s got identity=%s"
|
||||||
|
|
||||||
|
# list.c
|
||||||
|
qauthz_list_check_rule(void *authz, const char *identity, const char *rule, int format, int policy) "AuthZ list %p check rule=%s identity=%s format=%d policy=%d"
|
||||||
|
qauthz_list_default_policy(void *authz, const char *identity, int policy) "AuthZ list %p default identity=%s policy=%d"
|
||||||
|
|
||||||
|
# listfile.c
|
||||||
|
qauthz_list_file_load(void *authz, const char *filename) "AuthZ file %p load filename=%s"
|
||||||
|
qauthz_list_file_refresh(void *authz, const char *filename, int success) "AuthZ file %p load filename=%s success=%d"
|
||||||
|
|
||||||
|
# pamacct.c
|
||||||
|
qauthz_pam_check(void *authz, const char *identity, const char *service) "AuthZ PAM %p identity=%s service=%s"
|
|
@ -4,15 +4,14 @@ common-obj-$(CONFIG_POSIX) += rng-random.o
|
||||||
common-obj-$(CONFIG_TPM) += tpm.o
|
common-obj-$(CONFIG_TPM) += tpm.o
|
||||||
|
|
||||||
common-obj-y += hostmem.o hostmem-ram.o
|
common-obj-y += hostmem.o hostmem-ram.o
|
||||||
common-obj-$(CONFIG_LINUX) += hostmem-file.o
|
common-obj-$(CONFIG_POSIX) += hostmem-file.o
|
||||||
|
|
||||||
common-obj-y += cryptodev.o
|
common-obj-y += cryptodev.o
|
||||||
common-obj-y += cryptodev-builtin.o
|
common-obj-y += cryptodev-builtin.o
|
||||||
|
|
||||||
ifeq ($(CONFIG_VIRTIO),y)
|
ifeq ($(CONFIG_VIRTIO_CRYPTO),y)
|
||||||
common-obj-y += cryptodev-vhost.o
|
common-obj-y += cryptodev-vhost.o
|
||||||
common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) += \
|
common-obj-$(CONFIG_VHOST_CRYPTO) += cryptodev-vhost-user.o
|
||||||
cryptodev-vhost-user.o
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
common-obj-$(CONFIG_LINUX) += hostmem-memfd.o
|
common-obj-$(CONFIG_LINUX) += hostmem-memfd.o
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
typedef struct CryptoDevBackendVhostUser {
|
typedef struct CryptoDevBackendVhostUser {
|
||||||
CryptoDevBackend parent_obj;
|
CryptoDevBackend parent_obj;
|
||||||
|
|
||||||
VhostUserState *vhost_user;
|
VhostUserState vhost_user;
|
||||||
CharBackend chr;
|
CharBackend chr;
|
||||||
char *chr_name;
|
char *chr_name;
|
||||||
bool opened;
|
bool opened;
|
||||||
|
@ -104,7 +104,7 @@ cryptodev_vhost_user_start(int queues,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
options.opaque = s->vhost_user;
|
options.opaque = &s->vhost_user;
|
||||||
options.backend_type = VHOST_BACKEND_TYPE_USER;
|
options.backend_type = VHOST_BACKEND_TYPE_USER;
|
||||||
options.cc = b->conf.peers.ccs[i];
|
options.cc = b->conf.peers.ccs[i];
|
||||||
s->vhost_crypto[i] = cryptodev_vhost_init(&options);
|
s->vhost_crypto[i] = cryptodev_vhost_init(&options);
|
||||||
|
@ -182,7 +182,6 @@ static void cryptodev_vhost_user_init(
|
||||||
size_t i;
|
size_t i;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
Chardev *chr;
|
Chardev *chr;
|
||||||
VhostUserState *user;
|
|
||||||
CryptoDevBackendClient *cc;
|
CryptoDevBackendClient *cc;
|
||||||
CryptoDevBackendVhostUser *s =
|
CryptoDevBackendVhostUser *s =
|
||||||
CRYPTODEV_BACKEND_VHOST_USER(backend);
|
CRYPTODEV_BACKEND_VHOST_USER(backend);
|
||||||
|
@ -213,15 +212,10 @@ static void cryptodev_vhost_user_init(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
user = vhost_user_init();
|
if (!vhost_user_init(&s->vhost_user, &s->chr, errp)) {
|
||||||
if (!user) {
|
|
||||||
error_setg(errp, "Failed to init vhost_user");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
user->chr = &s->chr;
|
|
||||||
s->vhost_user = user;
|
|
||||||
|
|
||||||
qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
|
qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
|
||||||
cryptodev_vhost_user_event, NULL, s, NULL, true);
|
cryptodev_vhost_user_event, NULL, s, NULL, true);
|
||||||
|
|
||||||
|
@ -307,11 +301,7 @@ static void cryptodev_vhost_user_cleanup(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->vhost_user) {
|
vhost_user_cleanup(&s->vhost_user);
|
||||||
vhost_user_cleanup(s->vhost_user);
|
|
||||||
g_free(s->vhost_user);
|
|
||||||
s->vhost_user = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cryptodev_vhost_user_set_chardev(Object *obj,
|
static void cryptodev_vhost_user_set_chardev(Object *obj,
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
#include "sysemu/hostmem.h"
|
#include "sysemu/hostmem.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "qom/object_interfaces.h"
|
#include "qom/object_interfaces.h"
|
||||||
|
@ -31,15 +32,21 @@ typedef struct HostMemoryBackendFile HostMemoryBackendFile;
|
||||||
struct HostMemoryBackendFile {
|
struct HostMemoryBackendFile {
|
||||||
HostMemoryBackend parent_obj;
|
HostMemoryBackend parent_obj;
|
||||||
|
|
||||||
bool discard_data;
|
|
||||||
char *mem_path;
|
char *mem_path;
|
||||||
uint64_t align;
|
uint64_t align;
|
||||||
|
bool discard_data;
|
||||||
|
bool is_pmem;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||||
{
|
{
|
||||||
|
#ifndef CONFIG_POSIX
|
||||||
|
error_setg(errp, "backend '%s' not supported on this host",
|
||||||
|
object_get_typename(OBJECT(backend)));
|
||||||
|
#else
|
||||||
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend);
|
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend);
|
||||||
|
gchar *name;
|
||||||
|
|
||||||
if (!backend->size) {
|
if (!backend->size) {
|
||||||
error_setg(errp, "can't create backend with size 0");
|
error_setg(errp, "can't create backend with size 0");
|
||||||
|
@ -49,19 +56,38 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||||
error_setg(errp, "mem-path property not set");
|
error_setg(errp, "mem-path property not set");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#ifndef CONFIG_LINUX
|
|
||||||
error_setg(errp, "-mem-path not supported on this host");
|
/*
|
||||||
#else
|
* Verify pmem file size since starting a guest with an incorrect size
|
||||||
if (!host_memory_backend_mr_inited(backend)) {
|
* leads to confusing failures inside the guest.
|
||||||
gchar *path;
|
*/
|
||||||
backend->force_prealloc = mem_prealloc;
|
if (fb->is_pmem) {
|
||||||
path = object_get_canonical_path(OBJECT(backend));
|
Error *local_err = NULL;
|
||||||
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
|
uint64_t size;
|
||||||
path,
|
|
||||||
backend->size, fb->align, backend->share,
|
size = qemu_get_pmem_size(fb->mem_path, &local_err);
|
||||||
fb->mem_path, errp);
|
if (!size) {
|
||||||
g_free(path);
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backend->size > size) {
|
||||||
|
error_setg(errp, "size property %" PRIu64 " is larger than "
|
||||||
|
"pmem file \"%s\" size %" PRIu64, backend->size,
|
||||||
|
fb->mem_path, size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backend->force_prealloc = mem_prealloc;
|
||||||
|
name = host_memory_backend_get_name(backend);
|
||||||
|
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
|
||||||
|
name,
|
||||||
|
backend->size, fb->align,
|
||||||
|
(backend->share ? RAM_SHARED : 0) |
|
||||||
|
(fb->is_pmem ? RAM_PMEM : 0),
|
||||||
|
fb->mem_path, errp);
|
||||||
|
g_free(name);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +104,8 @@ static void set_mem_path(Object *o, const char *str, Error **errp)
|
||||||
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
|
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
|
||||||
|
|
||||||
if (host_memory_backend_mr_inited(backend)) {
|
if (host_memory_backend_mr_inited(backend)) {
|
||||||
error_setg(errp, "cannot change property value");
|
error_setg(errp, "cannot change property 'mem-path' of %s",
|
||||||
|
object_get_typename(o));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
g_free(fb->mem_path);
|
g_free(fb->mem_path);
|
||||||
|
@ -116,7 +143,8 @@ static void file_memory_backend_set_align(Object *o, Visitor *v,
|
||||||
uint64_t val;
|
uint64_t val;
|
||||||
|
|
||||||
if (host_memory_backend_mr_inited(backend)) {
|
if (host_memory_backend_mr_inited(backend)) {
|
||||||
error_setg(&local_err, "cannot change property value");
|
error_setg(&local_err, "cannot change property '%s' of %s",
|
||||||
|
name, object_get_typename(o));
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,6 +158,39 @@ static void file_memory_backend_set_align(Object *o, Visitor *v,
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool file_memory_backend_get_pmem(Object *o, Error **errp)
|
||||||
|
{
|
||||||
|
return MEMORY_BACKEND_FILE(o)->is_pmem;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp)
|
||||||
|
{
|
||||||
|
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||||
|
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
|
||||||
|
|
||||||
|
if (host_memory_backend_mr_inited(backend)) {
|
||||||
|
|
||||||
|
error_setg(errp, "cannot change property 'pmem' of %s.",
|
||||||
|
object_get_typename(o));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef CONFIG_LIBPMEM
|
||||||
|
if (value) {
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
error_setg(&local_err,
|
||||||
|
"Lack of libpmem support while setting the 'pmem=on'"
|
||||||
|
" of %s. We can't ensure data persistence.",
|
||||||
|
object_get_typename(o));
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
fb->is_pmem = value;
|
||||||
|
}
|
||||||
|
|
||||||
static void file_backend_unparent(Object *obj)
|
static void file_backend_unparent(Object *obj)
|
||||||
{
|
{
|
||||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||||
|
@ -161,6 +222,9 @@ file_backend_class_init(ObjectClass *oc, void *data)
|
||||||
file_memory_backend_get_align,
|
file_memory_backend_get_align,
|
||||||
file_memory_backend_set_align,
|
file_memory_backend_set_align,
|
||||||
NULL, NULL, &error_abort);
|
NULL, NULL, &error_abort);
|
||||||
|
object_class_property_add_bool(oc, "pmem",
|
||||||
|
file_memory_backend_get_pmem, file_memory_backend_set_pmem,
|
||||||
|
&error_abort);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void file_backend_instance_finalize(Object *o)
|
static void file_backend_instance_finalize(Object *o)
|
||||||
|
|
|
@ -44,10 +44,6 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (host_memory_backend_mr_inited(backend)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
backend->force_prealloc = mem_prealloc;
|
backend->force_prealloc = mem_prealloc;
|
||||||
fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size,
|
fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size,
|
||||||
m->hugetlb, m->hugetlbsize, m->seal ?
|
m->hugetlb, m->hugetlbsize, m->seal ?
|
||||||
|
@ -57,9 +53,10 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
name = object_get_canonical_path(OBJECT(backend));
|
name = host_memory_backend_get_name(backend);
|
||||||
memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend),
|
memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend),
|
||||||
name, backend->size, true, fd, errp);
|
name, backend->size,
|
||||||
|
backend->share, fd, errp);
|
||||||
g_free(name);
|
g_free(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,6 +128,7 @@ memfd_backend_instance_init(Object *obj)
|
||||||
|
|
||||||
/* default to sealed file */
|
/* default to sealed file */
|
||||||
m->seal = true;
|
m->seal = true;
|
||||||
|
MEMORY_BACKEND(m)->share = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -140,18 +138,29 @@ memfd_backend_class_init(ObjectClass *oc, void *data)
|
||||||
|
|
||||||
bc->alloc = memfd_backend_memory_alloc;
|
bc->alloc = memfd_backend_memory_alloc;
|
||||||
|
|
||||||
object_class_property_add_bool(oc, "hugetlb",
|
if (qemu_memfd_check(MFD_HUGETLB)) {
|
||||||
memfd_backend_get_hugetlb,
|
object_class_property_add_bool(oc, "hugetlb",
|
||||||
memfd_backend_set_hugetlb,
|
memfd_backend_get_hugetlb,
|
||||||
&error_abort);
|
memfd_backend_set_hugetlb,
|
||||||
object_class_property_add(oc, "hugetlbsize", "int",
|
&error_abort);
|
||||||
memfd_backend_get_hugetlbsize,
|
object_class_property_set_description(oc, "hugetlb",
|
||||||
memfd_backend_set_hugetlbsize,
|
"Use huge pages",
|
||||||
NULL, NULL, &error_abort);
|
&error_abort);
|
||||||
|
object_class_property_add(oc, "hugetlbsize", "int",
|
||||||
|
memfd_backend_get_hugetlbsize,
|
||||||
|
memfd_backend_set_hugetlbsize,
|
||||||
|
NULL, NULL, &error_abort);
|
||||||
|
object_class_property_set_description(oc, "hugetlbsize",
|
||||||
|
"Huge pages size (ex: 2M, 1G)",
|
||||||
|
&error_abort);
|
||||||
|
}
|
||||||
object_class_property_add_bool(oc, "seal",
|
object_class_property_add_bool(oc, "seal",
|
||||||
memfd_backend_get_seal,
|
memfd_backend_get_seal,
|
||||||
memfd_backend_set_seal,
|
memfd_backend_set_seal,
|
||||||
&error_abort);
|
&error_abort);
|
||||||
|
object_class_property_set_description(oc, "seal",
|
||||||
|
"Seal growing & shrinking",
|
||||||
|
&error_abort);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo memfd_backend_info = {
|
static const TypeInfo memfd_backend_info = {
|
||||||
|
@ -164,7 +173,9 @@ static const TypeInfo memfd_backend_info = {
|
||||||
|
|
||||||
static void register_types(void)
|
static void register_types(void)
|
||||||
{
|
{
|
||||||
type_register_static(&memfd_backend_info);
|
if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
|
||||||
|
type_register_static(&memfd_backend_info);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type_init(register_types);
|
type_init(register_types);
|
||||||
|
|
|
@ -16,21 +16,20 @@
|
||||||
|
|
||||||
#define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram"
|
#define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram"
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||||
{
|
{
|
||||||
char *path;
|
char *name;
|
||||||
|
|
||||||
if (!backend->size) {
|
if (!backend->size) {
|
||||||
error_setg(errp, "can't create backend with size 0");
|
error_setg(errp, "can't create backend with size 0");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
path = object_get_canonical_path_component(OBJECT(backend));
|
name = host_memory_backend_get_name(backend);
|
||||||
memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), path,
|
memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), name,
|
||||||
backend->size, backend->share, errp);
|
backend->size, backend->share, errp);
|
||||||
g_free(path);
|
g_free(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -28,6 +28,16 @@ QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_BIND != MPOL_BIND);
|
||||||
QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE);
|
QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
char *
|
||||||
|
host_memory_backend_get_name(HostMemoryBackend *backend)
|
||||||
|
{
|
||||||
|
if (!backend->use_canonical_path) {
|
||||||
|
return object_get_canonical_path_component(OBJECT(backend));
|
||||||
|
}
|
||||||
|
|
||||||
|
return object_get_canonical_path(OBJECT(backend));
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
host_memory_backend_get_size(Object *obj, Visitor *v, const char *name,
|
host_memory_backend_get_size(Object *obj, Visitor *v, const char *name,
|
||||||
void *opaque, Error **errp)
|
void *opaque, Error **errp)
|
||||||
|
@ -47,7 +57,8 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name,
|
||||||
uint64_t value;
|
uint64_t value;
|
||||||
|
|
||||||
if (host_memory_backend_mr_inited(backend)) {
|
if (host_memory_backend_mr_inited(backend)) {
|
||||||
error_setg(&local_err, "cannot change property value");
|
error_setg(&local_err, "cannot change property %s of %s ",
|
||||||
|
name, object_get_typename(obj));
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,8 +67,9 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (!value) {
|
if (!value) {
|
||||||
error_setg(&local_err, "Property '%s.%s' doesn't take value '%"
|
error_setg(&local_err,
|
||||||
PRIu64 "'", object_get_typename(obj), name, value);
|
"property '%s' of %s doesn't take value '%" PRIu64 "'",
|
||||||
|
name, object_get_typename(obj), value);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
backend->size = value;
|
backend->size = value;
|
||||||
|
@ -76,7 +88,7 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name,
|
||||||
|
|
||||||
value = find_first_bit(backend->host_nodes, MAX_NODES);
|
value = find_first_bit(backend->host_nodes, MAX_NODES);
|
||||||
if (value == MAX_NODES) {
|
if (value == MAX_NODES) {
|
||||||
return;
|
goto ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
*node = g_malloc0(sizeof(**node));
|
*node = g_malloc0(sizeof(**node));
|
||||||
|
@ -94,6 +106,7 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name,
|
||||||
node = &(*node)->next;
|
node = &(*node)->next;
|
||||||
} while (true);
|
} while (true);
|
||||||
|
|
||||||
|
ret:
|
||||||
visit_type_uint16List(v, name, &host_nodes, errp);
|
visit_type_uint16List(v, name, &host_nodes, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,14 +116,23 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name,
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_NUMA
|
#ifdef CONFIG_NUMA
|
||||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||||
uint16List *l = NULL;
|
uint16List *l, *host_nodes = NULL;
|
||||||
|
|
||||||
visit_type_uint16List(v, name, &l, errp);
|
visit_type_uint16List(v, name, &host_nodes, errp);
|
||||||
|
|
||||||
while (l) {
|
for (l = host_nodes; l; l = l->next) {
|
||||||
bitmap_set(backend->host_nodes, l->value, 1);
|
if (l->value >= MAX_NODES) {
|
||||||
l = l->next;
|
error_setg(errp, "Invalid host-nodes value: %d", l->value);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (l = host_nodes; l; l = l->next) {
|
||||||
|
bitmap_set(backend->host_nodes, l->value, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
qapi_free_uint16List(host_nodes);
|
||||||
#else
|
#else
|
||||||
error_setg(errp, "NUMA node binding are not supported by this QEMU");
|
error_setg(errp, "NUMA node binding are not supported by this QEMU");
|
||||||
#endif
|
#endif
|
||||||
|
@ -238,6 +260,11 @@ static void host_memory_backend_init(Object *obj)
|
||||||
backend->prealloc = mem_prealloc;
|
backend->prealloc = mem_prealloc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void host_memory_backend_post_init(Object *obj)
|
||||||
|
{
|
||||||
|
object_apply_compat_props(obj);
|
||||||
|
}
|
||||||
|
|
||||||
bool host_memory_backend_mr_inited(HostMemoryBackend *backend)
|
bool host_memory_backend_mr_inited(HostMemoryBackend *backend)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -386,6 +413,23 @@ static void host_memory_backend_set_share(Object *o, bool value, Error **errp)
|
||||||
backend->share = value;
|
backend->share = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
host_memory_backend_get_use_canonical_path(Object *obj, Error **errp)
|
||||||
|
{
|
||||||
|
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||||
|
|
||||||
|
return backend->use_canonical_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
host_memory_backend_set_use_canonical_path(Object *obj, bool value,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||||
|
|
||||||
|
backend->use_canonical_path = value;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
host_memory_backend_class_init(ObjectClass *oc, void *data)
|
host_memory_backend_class_init(ObjectClass *oc, void *data)
|
||||||
{
|
{
|
||||||
|
@ -397,27 +441,44 @@ host_memory_backend_class_init(ObjectClass *oc, void *data)
|
||||||
object_class_property_add_bool(oc, "merge",
|
object_class_property_add_bool(oc, "merge",
|
||||||
host_memory_backend_get_merge,
|
host_memory_backend_get_merge,
|
||||||
host_memory_backend_set_merge, &error_abort);
|
host_memory_backend_set_merge, &error_abort);
|
||||||
|
object_class_property_set_description(oc, "merge",
|
||||||
|
"Mark memory as mergeable", &error_abort);
|
||||||
object_class_property_add_bool(oc, "dump",
|
object_class_property_add_bool(oc, "dump",
|
||||||
host_memory_backend_get_dump,
|
host_memory_backend_get_dump,
|
||||||
host_memory_backend_set_dump, &error_abort);
|
host_memory_backend_set_dump, &error_abort);
|
||||||
|
object_class_property_set_description(oc, "dump",
|
||||||
|
"Set to 'off' to exclude from core dump", &error_abort);
|
||||||
object_class_property_add_bool(oc, "prealloc",
|
object_class_property_add_bool(oc, "prealloc",
|
||||||
host_memory_backend_get_prealloc,
|
host_memory_backend_get_prealloc,
|
||||||
host_memory_backend_set_prealloc, &error_abort);
|
host_memory_backend_set_prealloc, &error_abort);
|
||||||
|
object_class_property_set_description(oc, "prealloc",
|
||||||
|
"Preallocate memory", &error_abort);
|
||||||
object_class_property_add(oc, "size", "int",
|
object_class_property_add(oc, "size", "int",
|
||||||
host_memory_backend_get_size,
|
host_memory_backend_get_size,
|
||||||
host_memory_backend_set_size,
|
host_memory_backend_set_size,
|
||||||
NULL, NULL, &error_abort);
|
NULL, NULL, &error_abort);
|
||||||
|
object_class_property_set_description(oc, "size",
|
||||||
|
"Size of the memory region (ex: 500M)", &error_abort);
|
||||||
object_class_property_add(oc, "host-nodes", "int",
|
object_class_property_add(oc, "host-nodes", "int",
|
||||||
host_memory_backend_get_host_nodes,
|
host_memory_backend_get_host_nodes,
|
||||||
host_memory_backend_set_host_nodes,
|
host_memory_backend_set_host_nodes,
|
||||||
NULL, NULL, &error_abort);
|
NULL, NULL, &error_abort);
|
||||||
|
object_class_property_set_description(oc, "host-nodes",
|
||||||
|
"Binds memory to the list of NUMA host nodes", &error_abort);
|
||||||
object_class_property_add_enum(oc, "policy", "HostMemPolicy",
|
object_class_property_add_enum(oc, "policy", "HostMemPolicy",
|
||||||
&HostMemPolicy_lookup,
|
&HostMemPolicy_lookup,
|
||||||
host_memory_backend_get_policy,
|
host_memory_backend_get_policy,
|
||||||
host_memory_backend_set_policy, &error_abort);
|
host_memory_backend_set_policy, &error_abort);
|
||||||
|
object_class_property_set_description(oc, "policy",
|
||||||
|
"Set the NUMA policy", &error_abort);
|
||||||
object_class_property_add_bool(oc, "share",
|
object_class_property_add_bool(oc, "share",
|
||||||
host_memory_backend_get_share, host_memory_backend_set_share,
|
host_memory_backend_get_share, host_memory_backend_set_share,
|
||||||
&error_abort);
|
&error_abort);
|
||||||
|
object_class_property_set_description(oc, "share",
|
||||||
|
"Mark the memory as private to QEMU or shared", &error_abort);
|
||||||
|
object_class_property_add_bool(oc, "x-use-canonical-path-for-ramblock-id",
|
||||||
|
host_memory_backend_get_use_canonical_path,
|
||||||
|
host_memory_backend_set_use_canonical_path, &error_abort);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo host_memory_backend_info = {
|
static const TypeInfo host_memory_backend_info = {
|
||||||
|
@ -428,6 +489,7 @@ static const TypeInfo host_memory_backend_info = {
|
||||||
.class_init = host_memory_backend_class_init,
|
.class_init = host_memory_backend_class_init,
|
||||||
.instance_size = sizeof(HostMemoryBackend),
|
.instance_size = sizeof(HostMemoryBackend),
|
||||||
.instance_init = host_memory_backend_init,
|
.instance_init = host_memory_backend_init,
|
||||||
|
.instance_post_init = host_memory_backend_post_init,
|
||||||
.interfaces = (InterfaceInfo[]) {
|
.interfaces = (InterfaceInfo[]) {
|
||||||
{ TYPE_USER_CREATABLE },
|
{ TYPE_USER_CREATABLE },
|
||||||
{ }
|
{ }
|
||||||
|
|
13
balloon.c
13
balloon.c
|
@ -26,6 +26,7 @@
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
|
#include "qemu/atomic.h"
|
||||||
#include "exec/cpu-common.h"
|
#include "exec/cpu-common.h"
|
||||||
#include "sysemu/kvm.h"
|
#include "sysemu/kvm.h"
|
||||||
#include "sysemu/balloon.h"
|
#include "sysemu/balloon.h"
|
||||||
|
@ -37,16 +38,22 @@
|
||||||
static QEMUBalloonEvent *balloon_event_fn;
|
static QEMUBalloonEvent *balloon_event_fn;
|
||||||
static QEMUBalloonStatus *balloon_stat_fn;
|
static QEMUBalloonStatus *balloon_stat_fn;
|
||||||
static void *balloon_opaque;
|
static void *balloon_opaque;
|
||||||
static bool balloon_inhibited;
|
static int balloon_inhibit_count;
|
||||||
|
|
||||||
bool qemu_balloon_is_inhibited(void)
|
bool qemu_balloon_is_inhibited(void)
|
||||||
{
|
{
|
||||||
return balloon_inhibited;
|
return atomic_read(&balloon_inhibit_count) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_balloon_inhibit(bool state)
|
void qemu_balloon_inhibit(bool state)
|
||||||
{
|
{
|
||||||
balloon_inhibited = state;
|
if (state) {
|
||||||
|
atomic_inc(&balloon_inhibit_count);
|
||||||
|
} else {
|
||||||
|
atomic_dec(&balloon_inhibit_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(atomic_read(&balloon_inhibit_count) >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool have_balloon(Error **errp)
|
static bool have_balloon(Error **errp)
|
||||||
|
|
|
@ -1,10 +1,18 @@
|
||||||
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 += 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 += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o
|
block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o
|
||||||
block-obj-y += qed.o qed-l2-cache.o qed-table.o qed-cluster.o
|
block-obj-$(CONFIG_QED) += qed.o qed-l2-cache.o qed-table.o qed-cluster.o
|
||||||
block-obj-y += qed-check.o
|
block-obj-$(CONFIG_QED) += qed-check.o
|
||||||
block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o
|
block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o
|
||||||
block-obj-y += quorum.o
|
block-obj-y += quorum.o
|
||||||
block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o
|
block-obj-y += blkdebug.o blkverify.o blkreplay.o
|
||||||
|
block-obj-$(CONFIG_PARALLELS) += parallels.o
|
||||||
block-obj-y += blklogwrites.o
|
block-obj-y += blklogwrites.o
|
||||||
block-obj-y += block-backend.o snapshot.o qapi.o
|
block-obj-y += block-backend.o snapshot.o qapi.o
|
||||||
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
|
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
|
||||||
|
@ -14,7 +22,8 @@ block-obj-y += null.o mirror.o commit.o io.o create.o
|
||||||
block-obj-y += throttle-groups.o
|
block-obj-y += throttle-groups.o
|
||||||
block-obj-$(CONFIG_LINUX) += nvme.o
|
block-obj-$(CONFIG_LINUX) += nvme.o
|
||||||
|
|
||||||
block-obj-y += nbd.o nbd-client.o sheepdog.o
|
block-obj-y += nbd.o nbd-client.o
|
||||||
|
block-obj-$(CONFIG_SHEEPDOG) += sheepdog.o
|
||||||
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
|
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
|
||||||
block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o
|
block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o
|
||||||
block-obj-$(CONFIG_LIBNFS) += nfs.o
|
block-obj-$(CONFIG_LIBNFS) += nfs.o
|
||||||
|
@ -45,8 +54,11 @@ gluster.o-libs := $(GLUSTERFS_LIBS)
|
||||||
vxhs.o-libs := $(VXHS_LIBS)
|
vxhs.o-libs := $(VXHS_LIBS)
|
||||||
ssh.o-cflags := $(LIBSSH2_CFLAGS)
|
ssh.o-cflags := $(LIBSSH2_CFLAGS)
|
||||||
ssh.o-libs := $(LIBSSH2_LIBS)
|
ssh.o-libs := $(LIBSSH2_LIBS)
|
||||||
block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o
|
block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o
|
||||||
|
block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y)
|
||||||
dmg-bz2.o-libs := $(BZIP2_LIBS)
|
dmg-bz2.o-libs := $(BZIP2_LIBS)
|
||||||
|
block-obj-$(if $(CONFIG_LZFSE),m,n) += dmg-lzfse.o
|
||||||
|
dmg-lzfse.o-libs := $(LZFSE_LIBS)
|
||||||
qcow.o-libs := -lz
|
qcow.o-libs := -lz
|
||||||
linux-aio.o-libs := -laio
|
linux-aio.o-libs := -laio
|
||||||
parallels.o-cflags := $(LIBXML2_CFLAGS)
|
parallels.o-cflags := $(LIBXML2_CFLAGS)
|
||||||
|
|
129
block/backup.c
129
block/backup.c
|
@ -28,6 +28,13 @@
|
||||||
|
|
||||||
#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
|
#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;
|
||||||
|
|
||||||
typedef struct BackupBlockJob {
|
typedef struct BackupBlockJob {
|
||||||
BlockJob common;
|
BlockJob common;
|
||||||
BlockBackend *target;
|
BlockBackend *target;
|
||||||
|
@ -100,7 +107,6 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
|
||||||
void **bounce_buffer)
|
void **bounce_buffer)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct iovec iov;
|
|
||||||
QEMUIOVector qiov;
|
QEMUIOVector qiov;
|
||||||
BlockBackend *blk = job->common.blk;
|
BlockBackend *blk = job->common.blk;
|
||||||
int nbytes;
|
int nbytes;
|
||||||
|
@ -112,9 +118,7 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
|
||||||
if (!*bounce_buffer) {
|
if (!*bounce_buffer) {
|
||||||
*bounce_buffer = blk_blockalign(blk, job->cluster_size);
|
*bounce_buffer = blk_blockalign(blk, job->cluster_size);
|
||||||
}
|
}
|
||||||
iov.iov_base = *bounce_buffer;
|
qemu_iovec_init_buf(&qiov, *bounce_buffer, nbytes);
|
||||||
iov.iov_len = nbytes;
|
|
||||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
|
||||||
|
|
||||||
ret = blk_co_preadv(blk, start, qiov.size, &qiov, read_flags);
|
ret = blk_co_preadv(blk, start, qiov.size, &qiov, read_flags);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -322,37 +326,6 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
|
||||||
hbitmap_set(backup_job->copy_bitmap, 0, len);
|
hbitmap_set(backup_job->copy_bitmap, 0, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset,
|
|
||||||
uint64_t bytes)
|
|
||||||
{
|
|
||||||
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
|
|
||||||
int64_t start, end;
|
|
||||||
|
|
||||||
assert(block_job_driver(job) == &backup_job_driver);
|
|
||||||
|
|
||||||
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(block_job_driver(job) == &backup_job_driver);
|
|
||||||
|
|
||||||
start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size);
|
|
||||||
end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size);
|
|
||||||
cow_request_begin(req, backup_job, start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
void backup_cow_request_end(CowRequest *req)
|
|
||||||
{
|
|
||||||
cow_request_end(req);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void backup_drain(BlockJob *job)
|
static void backup_drain(BlockJob *job)
|
||||||
{
|
{
|
||||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
|
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
|
||||||
|
@ -380,18 +353,6 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int ret;
|
|
||||||
} BackupCompleteData;
|
|
||||||
|
|
||||||
static void backup_complete(Job *job, void *opaque)
|
|
||||||
{
|
|
||||||
BackupCompleteData *data = opaque;
|
|
||||||
|
|
||||||
job_completed(job, data->ret, NULL);
|
|
||||||
g_free(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool coroutine_fn yield_and_check(BackupBlockJob *job)
|
static bool coroutine_fn yield_and_check(BackupBlockJob *job)
|
||||||
{
|
{
|
||||||
uint64_t delay_ns;
|
uint64_t delay_ns;
|
||||||
|
@ -421,7 +382,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
||||||
HBitmapIter hbi;
|
HBitmapIter hbi;
|
||||||
|
|
||||||
hbitmap_iter_init(&hbi, job->copy_bitmap, 0);
|
hbitmap_iter_init(&hbi, job->copy_bitmap, 0);
|
||||||
while ((cluster = hbitmap_iter_next(&hbi, true)) != -1) {
|
while ((cluster = hbitmap_iter_next(&hbi)) != -1) {
|
||||||
do {
|
do {
|
||||||
if (yield_and_check(job)) {
|
if (yield_and_check(job)) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -458,7 +419,8 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset);
|
offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset,
|
||||||
|
UINT64_MAX);
|
||||||
if (offset == -1) {
|
if (offset == -1) {
|
||||||
hbitmap_set(job->copy_bitmap, cluster, end - cluster);
|
hbitmap_set(job->copy_bitmap, cluster, end - cluster);
|
||||||
break;
|
break;
|
||||||
|
@ -480,60 +442,59 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
|
||||||
bdrv_dirty_iter_free(dbi);
|
bdrv_dirty_iter_free(dbi);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void coroutine_fn backup_run(void *opaque)
|
static int coroutine_fn backup_run(Job *job, Error **errp)
|
||||||
{
|
{
|
||||||
BackupBlockJob *job = opaque;
|
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
|
||||||
BackupCompleteData *data;
|
BlockDriverState *bs = blk_bs(s->common.blk);
|
||||||
BlockDriverState *bs = blk_bs(job->common.blk);
|
|
||||||
int64_t offset, nb_clusters;
|
int64_t offset, nb_clusters;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
QLIST_INIT(&job->inflight_reqs);
|
QLIST_INIT(&s->inflight_reqs);
|
||||||
qemu_co_rwlock_init(&job->flush_rwlock);
|
qemu_co_rwlock_init(&s->flush_rwlock);
|
||||||
|
|
||||||
nb_clusters = DIV_ROUND_UP(job->len, job->cluster_size);
|
nb_clusters = DIV_ROUND_UP(s->len, s->cluster_size);
|
||||||
job_progress_set_remaining(&job->common.job, job->len);
|
job_progress_set_remaining(job, s->len);
|
||||||
|
|
||||||
job->copy_bitmap = hbitmap_alloc(nb_clusters, 0);
|
s->copy_bitmap = hbitmap_alloc(nb_clusters, 0);
|
||||||
if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
||||||
backup_incremental_init_copy_bitmap(job);
|
backup_incremental_init_copy_bitmap(s);
|
||||||
} else {
|
} else {
|
||||||
hbitmap_set(job->copy_bitmap, 0, nb_clusters);
|
hbitmap_set(s->copy_bitmap, 0, nb_clusters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
job->before_write.notify = backup_before_write_notify;
|
s->before_write.notify = backup_before_write_notify;
|
||||||
bdrv_add_before_write_notifier(bs, &job->before_write);
|
bdrv_add_before_write_notifier(bs, &s->before_write);
|
||||||
|
|
||||||
if (job->sync_mode == MIRROR_SYNC_MODE_NONE) {
|
if (s->sync_mode == MIRROR_SYNC_MODE_NONE) {
|
||||||
/* All bits are set in copy_bitmap to allow any cluster to be copied.
|
/* All bits are set in copy_bitmap to allow any cluster to be copied.
|
||||||
* This does not actually require them to be copied. */
|
* This does not actually require them to be copied. */
|
||||||
while (!job_is_cancelled(&job->common.job)) {
|
while (!job_is_cancelled(job)) {
|
||||||
/* Yield until the job is cancelled. We just let our before_write
|
/* Yield until the job is cancelled. We just let our before_write
|
||||||
* notify callback service CoW requests. */
|
* notify callback service CoW requests. */
|
||||||
job_yield(&job->common.job);
|
job_yield(job);
|
||||||
}
|
}
|
||||||
} else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
} else if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
||||||
ret = backup_run_incremental(job);
|
ret = backup_run_incremental(s);
|
||||||
} else {
|
} else {
|
||||||
/* Both FULL and TOP SYNC_MODE's require copying.. */
|
/* Both FULL and TOP SYNC_MODE's require copying.. */
|
||||||
for (offset = 0; offset < job->len;
|
for (offset = 0; offset < s->len;
|
||||||
offset += job->cluster_size) {
|
offset += s->cluster_size) {
|
||||||
bool error_is_read;
|
bool error_is_read;
|
||||||
int alloced = 0;
|
int alloced = 0;
|
||||||
|
|
||||||
if (yield_and_check(job)) {
|
if (yield_and_check(s)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (job->sync_mode == MIRROR_SYNC_MODE_TOP) {
|
if (s->sync_mode == MIRROR_SYNC_MODE_TOP) {
|
||||||
int i;
|
int i;
|
||||||
int64_t n;
|
int64_t n;
|
||||||
|
|
||||||
/* Check to see if these blocks are already in the
|
/* Check to see if these blocks are already in the
|
||||||
* backing file. */
|
* backing file. */
|
||||||
|
|
||||||
for (i = 0; i < job->cluster_size;) {
|
for (i = 0; i < s->cluster_size;) {
|
||||||
/* bdrv_is_allocated() only returns true/false based
|
/* bdrv_is_allocated() only returns true/false based
|
||||||
* on the first set of sectors it comes across that
|
* on the first set of sectors it comes across that
|
||||||
* are are all in the same state.
|
* are are all in the same state.
|
||||||
|
@ -542,7 +503,7 @@ static void coroutine_fn backup_run(void *opaque)
|
||||||
* needed but at some point that is always the case. */
|
* needed but at some point that is always the case. */
|
||||||
alloced =
|
alloced =
|
||||||
bdrv_is_allocated(bs, offset + i,
|
bdrv_is_allocated(bs, offset + i,
|
||||||
job->cluster_size - i, &n);
|
s->cluster_size - i, &n);
|
||||||
i += n;
|
i += n;
|
||||||
|
|
||||||
if (alloced || n == 0) {
|
if (alloced || n == 0) {
|
||||||
|
@ -560,33 +521,31 @@ static void coroutine_fn backup_run(void *opaque)
|
||||||
if (alloced < 0) {
|
if (alloced < 0) {
|
||||||
ret = alloced;
|
ret = alloced;
|
||||||
} else {
|
} else {
|
||||||
ret = backup_do_cow(job, offset, job->cluster_size,
|
ret = backup_do_cow(s, offset, s->cluster_size,
|
||||||
&error_is_read, false);
|
&error_is_read, false);
|
||||||
}
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
/* Depending on error action, fail now or retry cluster */
|
/* Depending on error action, fail now or retry cluster */
|
||||||
BlockErrorAction action =
|
BlockErrorAction action =
|
||||||
backup_error_action(job, error_is_read, -ret);
|
backup_error_action(s, error_is_read, -ret);
|
||||||
if (action == BLOCK_ERROR_ACTION_REPORT) {
|
if (action == BLOCK_ERROR_ACTION_REPORT) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
offset -= job->cluster_size;
|
offset -= s->cluster_size;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
notifier_with_return_remove(&job->before_write);
|
notifier_with_return_remove(&s->before_write);
|
||||||
|
|
||||||
/* wait until pending backup_do_cow() calls have completed */
|
/* wait until pending backup_do_cow() calls have completed */
|
||||||
qemu_co_rwlock_wrlock(&job->flush_rwlock);
|
qemu_co_rwlock_wrlock(&s->flush_rwlock);
|
||||||
qemu_co_rwlock_unlock(&job->flush_rwlock);
|
qemu_co_rwlock_unlock(&s->flush_rwlock);
|
||||||
hbitmap_free(job->copy_bitmap);
|
hbitmap_free(s->copy_bitmap);
|
||||||
|
|
||||||
data = g_malloc(sizeof(*data));
|
return ret;
|
||||||
data->ret = ret;
|
|
||||||
job_defer_to_main_loop(&job->common.job, backup_complete, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const BlockJobDriver backup_job_driver = {
|
static const BlockJobDriver backup_job_driver = {
|
||||||
|
@ -596,7 +555,7 @@ static const BlockJobDriver backup_job_driver = {
|
||||||
.free = block_job_free,
|
.free = block_job_free,
|
||||||
.user_resume = block_job_user_resume,
|
.user_resume = block_job_user_resume,
|
||||||
.drain = block_job_drain,
|
.drain = block_job_drain,
|
||||||
.start = backup_run,
|
.run = backup_run,
|
||||||
.commit = backup_commit,
|
.commit = backup_commit,
|
||||||
.abort = backup_abort,
|
.abort = backup_abort,
|
||||||
.clean = backup_clean,
|
.clean = backup_clean,
|
||||||
|
|
|
@ -401,7 +401,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
|
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||||
(BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
|
(BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
|
||||||
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
|
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||||
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
|
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
|
||||||
bs->file->bs->supported_zero_flags);
|
bs->file->bs->supported_zero_flags);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
|
||||||
|
@ -811,51 +811,37 @@ static int64_t blkdebug_getlength(BlockDriverState *bs)
|
||||||
return bdrv_getlength(bs->file->bs);
|
return bdrv_getlength(bs->file->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
|
static void blkdebug_refresh_filename(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVBlkdebugState *s = bs->opaque;
|
BDRVBlkdebugState *s = bs->opaque;
|
||||||
QDict *opts;
|
|
||||||
const QDictEntry *e;
|
const QDictEntry *e;
|
||||||
bool force_json = false;
|
int ret;
|
||||||
|
|
||||||
for (e = qdict_first(options); e; e = qdict_next(options, e)) {
|
if (!bs->file->bs->exact_filename[0]) {
|
||||||
if (strcmp(qdict_entry_key(e), "config") &&
|
|
||||||
strcmp(qdict_entry_key(e), "x-image"))
|
|
||||||
{
|
|
||||||
force_json = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (force_json && !bs->file->bs->full_open_options) {
|
|
||||||
/* The config file cannot be recreated, so creating a plain filename
|
|
||||||
* is impossible */
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!force_json && bs->file->bs->exact_filename[0]) {
|
for (e = qdict_first(bs->full_open_options); e;
|
||||||
int ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
e = qdict_next(bs->full_open_options, e))
|
||||||
"blkdebug:%s:%s", s->config_file ?: "",
|
{
|
||||||
bs->file->bs->exact_filename);
|
/* Real child options are under "image", but "x-image" may
|
||||||
if (ret >= sizeof(bs->exact_filename)) {
|
* contain a filename */
|
||||||
/* An overflow makes the filename unusable, so do not report any */
|
if (strcmp(qdict_entry_key(e), "config") &&
|
||||||
bs->exact_filename[0] = 0;
|
strcmp(qdict_entry_key(e), "image") &&
|
||||||
|
strcmp(qdict_entry_key(e), "x-image") &&
|
||||||
|
strcmp(qdict_entry_key(e), "driver"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
opts = qdict_new();
|
ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||||
qdict_put_str(opts, "driver", "blkdebug");
|
"blkdebug:%s:%s",
|
||||||
|
s->config_file ?: "", bs->file->bs->exact_filename);
|
||||||
qdict_put(opts, "image", qobject_ref(bs->file->bs->full_open_options));
|
if (ret >= sizeof(bs->exact_filename)) {
|
||||||
|
/* An overflow makes the filename unusable, so do not report any */
|
||||||
for (e = qdict_first(options); e; e = qdict_next(options, e)) {
|
bs->exact_filename[0] = 0;
|
||||||
if (strcmp(qdict_entry_key(e), "x-image")) {
|
|
||||||
qdict_put_obj(opts, qdict_entry_key(e),
|
|
||||||
qobject_ref(qdict_entry_value(e)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bs->full_open_options = opts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp)
|
static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||||
|
@ -888,6 +874,20 @@ static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *const blkdebug_strong_runtime_opts[] = {
|
||||||
|
"config",
|
||||||
|
"inject-error.",
|
||||||
|
"set-state.",
|
||||||
|
"align",
|
||||||
|
"max-transfer",
|
||||||
|
"opt-write-zero",
|
||||||
|
"max-write-zero",
|
||||||
|
"opt-discard",
|
||||||
|
"max-discard",
|
||||||
|
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_blkdebug = {
|
static BlockDriver bdrv_blkdebug = {
|
||||||
.format_name = "blkdebug",
|
.format_name = "blkdebug",
|
||||||
.protocol_name = "blkdebug",
|
.protocol_name = "blkdebug",
|
||||||
|
@ -917,6 +917,8 @@ static BlockDriver bdrv_blkdebug = {
|
||||||
= blkdebug_debug_remove_breakpoint,
|
= blkdebug_debug_remove_breakpoint,
|
||||||
.bdrv_debug_resume = blkdebug_debug_resume,
|
.bdrv_debug_resume = blkdebug_debug_resume,
|
||||||
.bdrv_debug_is_suspended = blkdebug_debug_is_suspended,
|
.bdrv_debug_is_suspended = blkdebug_debug_is_suspended,
|
||||||
|
|
||||||
|
.strong_runtime_opts = blkdebug_strong_runtime_opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void bdrv_blkdebug_init(void)
|
static void bdrv_blkdebug_init(void)
|
||||||
|
|
|
@ -280,31 +280,6 @@ static int64_t blk_log_writes_getlength(BlockDriverState *bs)
|
||||||
return bdrv_getlength(bs->file->bs);
|
return bdrv_getlength(bs->file->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void blk_log_writes_refresh_filename(BlockDriverState *bs,
|
|
||||||
QDict *options)
|
|
||||||
{
|
|
||||||
BDRVBlkLogWritesState *s = bs->opaque;
|
|
||||||
|
|
||||||
/* bs->file->bs has already been refreshed */
|
|
||||||
bdrv_refresh_filename(s->log_file->bs);
|
|
||||||
|
|
||||||
if (bs->file->bs->full_open_options
|
|
||||||
&& s->log_file->bs->full_open_options)
|
|
||||||
{
|
|
||||||
QDict *opts = qdict_new();
|
|
||||||
qdict_put_str(opts, "driver", "blklogwrites");
|
|
||||||
|
|
||||||
qobject_ref(bs->file->bs->full_open_options);
|
|
||||||
qdict_put_obj(opts, "file", QOBJECT(bs->file->bs->full_open_options));
|
|
||||||
qobject_ref(s->log_file->bs->full_open_options);
|
|
||||||
qdict_put_obj(opts, "log",
|
|
||||||
QOBJECT(s->log_file->bs->full_open_options));
|
|
||||||
qdict_put_int(opts, "log-sector-size", s->sectorsize);
|
|
||||||
|
|
||||||
bs->full_open_options = opts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c,
|
static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||||
const BdrvChildRole *role,
|
const BdrvChildRole *role,
|
||||||
BlockReopenQueue *ro_q,
|
BlockReopenQueue *ro_q,
|
||||||
|
@ -521,6 +496,13 @@ blk_log_writes_co_pdiscard(BlockDriverState *bs, int64_t offset, int count)
|
||||||
LOG_DISCARD_FLAG, false);
|
LOG_DISCARD_FLAG, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *const blk_log_writes_strong_runtime_opts[] = {
|
||||||
|
"log-append",
|
||||||
|
"log-sector-size",
|
||||||
|
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_blk_log_writes = {
|
static BlockDriver bdrv_blk_log_writes = {
|
||||||
.format_name = "blklogwrites",
|
.format_name = "blklogwrites",
|
||||||
.instance_size = sizeof(BDRVBlkLogWritesState),
|
.instance_size = sizeof(BDRVBlkLogWritesState),
|
||||||
|
@ -528,7 +510,6 @@ static BlockDriver bdrv_blk_log_writes = {
|
||||||
.bdrv_open = blk_log_writes_open,
|
.bdrv_open = blk_log_writes_open,
|
||||||
.bdrv_close = blk_log_writes_close,
|
.bdrv_close = blk_log_writes_close,
|
||||||
.bdrv_getlength = blk_log_writes_getlength,
|
.bdrv_getlength = blk_log_writes_getlength,
|
||||||
.bdrv_refresh_filename = blk_log_writes_refresh_filename,
|
|
||||||
.bdrv_child_perm = blk_log_writes_child_perm,
|
.bdrv_child_perm = blk_log_writes_child_perm,
|
||||||
.bdrv_refresh_limits = blk_log_writes_refresh_limits,
|
.bdrv_refresh_limits = blk_log_writes_refresh_limits,
|
||||||
|
|
||||||
|
@ -540,6 +521,7 @@ static BlockDriver bdrv_blk_log_writes = {
|
||||||
.bdrv_co_block_status = bdrv_co_block_status_from_file,
|
.bdrv_co_block_status = bdrv_co_block_status_from_file,
|
||||||
|
|
||||||
.is_filter = true,
|
.is_filter = true,
|
||||||
|
.strong_runtime_opts = blk_log_writes_strong_runtime_opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void bdrv_blk_log_writes_init(void)
|
static void bdrv_blk_log_writes_init(void)
|
||||||
|
|
|
@ -43,10 +43,6 @@ fail:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void blkreplay_close(BlockDriverState *bs)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static int64_t blkreplay_getlength(BlockDriverState *bs)
|
static int64_t blkreplay_getlength(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
return bdrv_getlength(bs->file->bs);
|
return bdrv_getlength(bs->file->bs);
|
||||||
|
@ -135,7 +131,6 @@ static BlockDriver bdrv_blkreplay = {
|
||||||
.instance_size = 0,
|
.instance_size = 0,
|
||||||
|
|
||||||
.bdrv_open = blkreplay_open,
|
.bdrv_open = blkreplay_open,
|
||||||
.bdrv_close = blkreplay_close,
|
|
||||||
.bdrv_child_perm = bdrv_filter_default_perms,
|
.bdrv_child_perm = bdrv_filter_default_perms,
|
||||||
.bdrv_getlength = blkreplay_getlength,
|
.bdrv_getlength = blkreplay_getlength,
|
||||||
|
|
||||||
|
|
|
@ -281,27 +281,10 @@ static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||||
return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate);
|
return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
|
static void blkverify_refresh_filename(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVBlkverifyState *s = bs->opaque;
|
BDRVBlkverifyState *s = bs->opaque;
|
||||||
|
|
||||||
/* bs->file->bs has already been refreshed */
|
|
||||||
bdrv_refresh_filename(s->test_file->bs);
|
|
||||||
|
|
||||||
if (bs->file->bs->full_open_options
|
|
||||||
&& s->test_file->bs->full_open_options)
|
|
||||||
{
|
|
||||||
QDict *opts = qdict_new();
|
|
||||||
qdict_put_str(opts, "driver", "blkverify");
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
bs->full_open_options = opts;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bs->file->bs->exact_filename[0]
|
if (bs->file->bs->exact_filename[0]
|
||||||
&& s->test_file->bs->exact_filename[0])
|
&& s->test_file->bs->exact_filename[0])
|
||||||
{
|
{
|
||||||
|
@ -316,6 +299,15 @@ static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *blkverify_dirname(BlockDriverState *bs, Error **errp)
|
||||||
|
{
|
||||||
|
/* In general, there are two BDSs with different dirnames below this one;
|
||||||
|
* so there is no unique dirname we could return (unless both are equal by
|
||||||
|
* chance). Therefore, to be consistent, just always return NULL. */
|
||||||
|
error_setg(errp, "Cannot generate a base directory for blkverify nodes");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static BlockDriver bdrv_blkverify = {
|
static BlockDriver bdrv_blkverify = {
|
||||||
.format_name = "blkverify",
|
.format_name = "blkverify",
|
||||||
.protocol_name = "blkverify",
|
.protocol_name = "blkverify",
|
||||||
|
@ -327,6 +319,7 @@ static BlockDriver bdrv_blkverify = {
|
||||||
.bdrv_child_perm = bdrv_filter_default_perms,
|
.bdrv_child_perm = bdrv_filter_default_perms,
|
||||||
.bdrv_getlength = blkverify_getlength,
|
.bdrv_getlength = blkverify_getlength,
|
||||||
.bdrv_refresh_filename = blkverify_refresh_filename,
|
.bdrv_refresh_filename = blkverify_refresh_filename,
|
||||||
|
.bdrv_dirname = blkverify_dirname,
|
||||||
|
|
||||||
.bdrv_co_preadv = blkverify_co_preadv,
|
.bdrv_co_preadv = blkverify_co_preadv,
|
||||||
.bdrv_co_pwritev = blkverify_co_pwritev,
|
.bdrv_co_pwritev = blkverify_co_pwritev,
|
||||||
|
|
|
@ -47,9 +47,7 @@ struct BlockBackend {
|
||||||
QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */
|
QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */
|
||||||
BlockBackendPublic public;
|
BlockBackendPublic public;
|
||||||
|
|
||||||
void *dev; /* attached device model, if any */
|
DeviceState *dev; /* attached device model, if any */
|
||||||
bool legacy_dev; /* true if dev is not a DeviceState */
|
|
||||||
/* TODO change to DeviceState when all users are qdevified */
|
|
||||||
const BlockDevOps *dev_ops;
|
const BlockDevOps *dev_ops;
|
||||||
void *dev_opaque;
|
void *dev_opaque;
|
||||||
|
|
||||||
|
@ -88,7 +86,6 @@ struct BlockBackend {
|
||||||
* Accessed with atomic ops.
|
* Accessed with atomic ops.
|
||||||
*/
|
*/
|
||||||
unsigned int in_flight;
|
unsigned int in_flight;
|
||||||
AioWait wait;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct BlockBackendAIOCB {
|
typedef struct BlockBackendAIOCB {
|
||||||
|
@ -121,6 +118,7 @@ static void blk_root_inherit_options(int *child_flags, QDict *child_options,
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
static void blk_root_drained_begin(BdrvChild *child);
|
static void blk_root_drained_begin(BdrvChild *child);
|
||||||
|
static bool blk_root_drained_poll(BdrvChild *child);
|
||||||
static void blk_root_drained_end(BdrvChild *child);
|
static void blk_root_drained_end(BdrvChild *child);
|
||||||
|
|
||||||
static void blk_root_change_media(BdrvChild *child, bool load);
|
static void blk_root_change_media(BdrvChild *child, bool load);
|
||||||
|
@ -294,6 +292,7 @@ static const BdrvChildRole child_root = {
|
||||||
.get_parent_desc = blk_root_get_parent_desc,
|
.get_parent_desc = blk_root_get_parent_desc,
|
||||||
|
|
||||||
.drained_begin = blk_root_drained_begin,
|
.drained_begin = blk_root_drained_begin,
|
||||||
|
.drained_poll = blk_root_drained_poll,
|
||||||
.drained_end = blk_root_drained_end,
|
.drained_end = blk_root_drained_end,
|
||||||
|
|
||||||
.activate = blk_root_activate,
|
.activate = blk_root_activate,
|
||||||
|
@ -324,6 +323,9 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
|
||||||
blk->shared_perm = shared_perm;
|
blk->shared_perm = shared_perm;
|
||||||
blk_set_enable_write_cache(blk, true);
|
blk_set_enable_write_cache(blk, true);
|
||||||
|
|
||||||
|
blk->on_read_error = BLOCKDEV_ON_ERROR_REPORT;
|
||||||
|
blk->on_write_error = BLOCKDEV_ON_ERROR_ENOSPC;
|
||||||
|
|
||||||
block_acct_init(&blk->stats);
|
block_acct_init(&blk->stats);
|
||||||
|
|
||||||
notifier_list_init(&blk->remove_bs_notifiers);
|
notifier_list_init(&blk->remove_bs_notifiers);
|
||||||
|
@ -419,7 +421,6 @@ static void drive_info_del(DriveInfo *dinfo)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
qemu_opts_del(dinfo->opts);
|
qemu_opts_del(dinfo->opts);
|
||||||
g_free(dinfo->serial);
|
|
||||||
g_free(dinfo);
|
g_free(dinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -434,6 +435,7 @@ int blk_get_refcnt(BlockBackend *blk)
|
||||||
*/
|
*/
|
||||||
void blk_ref(BlockBackend *blk)
|
void blk_ref(BlockBackend *blk)
|
||||||
{
|
{
|
||||||
|
assert(blk->refcnt > 0);
|
||||||
blk->refcnt++;
|
blk->refcnt++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,7 +448,13 @@ void blk_unref(BlockBackend *blk)
|
||||||
{
|
{
|
||||||
if (blk) {
|
if (blk) {
|
||||||
assert(blk->refcnt > 0);
|
assert(blk->refcnt > 0);
|
||||||
if (!--blk->refcnt) {
|
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;
|
||||||
blk_delete(blk);
|
blk_delete(blk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -826,7 +834,11 @@ void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm)
|
||||||
*shared_perm = blk->shared_perm;
|
*shared_perm = blk->shared_perm;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int blk_do_attach_dev(BlockBackend *blk, void *dev)
|
/*
|
||||||
|
* Attach device model @dev to @blk.
|
||||||
|
* Return 0 on success, -EBUSY when a device model is attached already.
|
||||||
|
*/
|
||||||
|
int blk_attach_dev(BlockBackend *blk, DeviceState *dev)
|
||||||
{
|
{
|
||||||
if (blk->dev) {
|
if (blk->dev) {
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
@ -841,40 +853,16 @@ static int blk_do_attach_dev(BlockBackend *blk, void *dev)
|
||||||
|
|
||||||
blk_ref(blk);
|
blk_ref(blk);
|
||||||
blk->dev = dev;
|
blk->dev = dev;
|
||||||
blk->legacy_dev = false;
|
|
||||||
blk_iostatus_reset(blk);
|
blk_iostatus_reset(blk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Attach device model @dev to @blk.
|
|
||||||
* Return 0 on success, -EBUSY when a device model is attached already.
|
|
||||||
*/
|
|
||||||
int blk_attach_dev(BlockBackend *blk, DeviceState *dev)
|
|
||||||
{
|
|
||||||
return blk_do_attach_dev(blk, dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Attach device model @dev to @blk.
|
|
||||||
* @blk must not have a device model attached already.
|
|
||||||
* TODO qdevified devices don't use this, remove when devices are qdevified
|
|
||||||
*/
|
|
||||||
void blk_attach_dev_legacy(BlockBackend *blk, void *dev)
|
|
||||||
{
|
|
||||||
if (blk_do_attach_dev(blk, dev) < 0) {
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
blk->legacy_dev = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Detach device model @dev from @blk.
|
* Detach device model @dev from @blk.
|
||||||
* @dev must be currently attached to @blk.
|
* @dev must be currently attached to @blk.
|
||||||
*/
|
*/
|
||||||
void blk_detach_dev(BlockBackend *blk, void *dev)
|
void blk_detach_dev(BlockBackend *blk, DeviceState *dev)
|
||||||
/* TODO change to DeviceState *dev when all users are qdevified */
|
|
||||||
{
|
{
|
||||||
assert(blk->dev == dev);
|
assert(blk->dev == dev);
|
||||||
blk->dev = NULL;
|
blk->dev = NULL;
|
||||||
|
@ -888,8 +876,7 @@ void blk_detach_dev(BlockBackend *blk, void *dev)
|
||||||
/*
|
/*
|
||||||
* Return the device model attached to @blk if any, else null.
|
* Return the device model attached to @blk if any, else null.
|
||||||
*/
|
*/
|
||||||
void *blk_get_attached_dev(BlockBackend *blk)
|
DeviceState *blk_get_attached_dev(BlockBackend *blk)
|
||||||
/* TODO change to return DeviceState * when all users are qdevified */
|
|
||||||
{
|
{
|
||||||
return blk->dev;
|
return blk->dev;
|
||||||
}
|
}
|
||||||
|
@ -898,17 +885,15 @@ void *blk_get_attached_dev(BlockBackend *blk)
|
||||||
* device attached to the BlockBackend. */
|
* device attached to the BlockBackend. */
|
||||||
char *blk_get_attached_dev_id(BlockBackend *blk)
|
char *blk_get_attached_dev_id(BlockBackend *blk)
|
||||||
{
|
{
|
||||||
DeviceState *dev;
|
DeviceState *dev = blk->dev;
|
||||||
|
|
||||||
assert(!blk->legacy_dev);
|
|
||||||
dev = blk->dev;
|
|
||||||
|
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
return g_strdup("");
|
return g_strdup("");
|
||||||
} else if (dev->id) {
|
} else if (dev->id) {
|
||||||
return g_strdup(dev->id);
|
return g_strdup(dev->id);
|
||||||
}
|
}
|
||||||
return object_get_canonical_path(OBJECT(dev));
|
|
||||||
|
return object_get_canonical_path(OBJECT(dev)) ?: g_strdup("");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -938,11 +923,6 @@ BlockBackend *blk_by_dev(void *dev)
|
||||||
void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops,
|
void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops,
|
||||||
void *opaque)
|
void *opaque)
|
||||||
{
|
{
|
||||||
/* All drivers that use blk_set_dev_ops() are qdevified and we want to keep
|
|
||||||
* it that way, so we can assume blk->dev, if present, is a DeviceState if
|
|
||||||
* blk->dev_ops is set. Non-device users may use dev_ops without device. */
|
|
||||||
assert(!blk->legacy_dev);
|
|
||||||
|
|
||||||
blk->dev_ops = ops;
|
blk->dev_ops = ops;
|
||||||
blk->dev_opaque = opaque;
|
blk->dev_opaque = opaque;
|
||||||
|
|
||||||
|
@ -968,8 +948,6 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp)
|
||||||
bool tray_was_open, tray_is_open;
|
bool tray_was_open, tray_is_open;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
assert(!blk->legacy_dev);
|
|
||||||
|
|
||||||
tray_was_open = blk_dev_is_tray_open(blk);
|
tray_was_open = blk_dev_is_tray_open(blk);
|
||||||
blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err);
|
blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
|
@ -981,8 +959,7 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp)
|
||||||
|
|
||||||
if (tray_was_open != tray_is_open) {
|
if (tray_was_open != tray_is_open) {
|
||||||
char *id = blk_get_attached_dev_id(blk);
|
char *id = blk_get_attached_dev_id(blk);
|
||||||
qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open,
|
qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open);
|
||||||
&error_abort);
|
|
||||||
g_free(id);
|
g_free(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1210,6 +1187,7 @@ static void blk_read_entry(void *opaque)
|
||||||
|
|
||||||
rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size,
|
rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size,
|
||||||
qiov, rwco->flags);
|
qiov, rwco->flags);
|
||||||
|
aio_wait_kick();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void blk_write_entry(void *opaque)
|
static void blk_write_entry(void *opaque)
|
||||||
|
@ -1219,23 +1197,15 @@ static void blk_write_entry(void *opaque)
|
||||||
|
|
||||||
rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size,
|
rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size,
|
||||||
qiov, rwco->flags);
|
qiov, rwco->flags);
|
||||||
|
aio_wait_kick();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
|
static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
|
||||||
int64_t bytes, CoroutineEntry co_entry,
|
int64_t bytes, CoroutineEntry co_entry,
|
||||||
BdrvRequestFlags flags)
|
BdrvRequestFlags flags)
|
||||||
{
|
{
|
||||||
QEMUIOVector qiov;
|
QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);
|
||||||
struct iovec iov;
|
BlkRwCo rwco = {
|
||||||
BlkRwCo rwco;
|
|
||||||
|
|
||||||
iov = (struct iovec) {
|
|
||||||
.iov_base = buf,
|
|
||||||
.iov_len = bytes,
|
|
||||||
};
|
|
||||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
|
||||||
|
|
||||||
rwco = (BlkRwCo) {
|
|
||||||
.blk = blk,
|
.blk = blk,
|
||||||
.offset = offset,
|
.offset = offset,
|
||||||
.iobuf = &qiov,
|
.iobuf = &qiov,
|
||||||
|
@ -1283,15 +1253,15 @@ int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags)
|
||||||
return bdrv_make_zero(blk->root, flags);
|
return bdrv_make_zero(blk->root, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void blk_inc_in_flight(BlockBackend *blk)
|
void blk_inc_in_flight(BlockBackend *blk)
|
||||||
{
|
{
|
||||||
atomic_inc(&blk->in_flight);
|
atomic_inc(&blk->in_flight);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void blk_dec_in_flight(BlockBackend *blk)
|
void blk_dec_in_flight(BlockBackend *blk)
|
||||||
{
|
{
|
||||||
atomic_dec(&blk->in_flight);
|
atomic_dec(&blk->in_flight);
|
||||||
aio_wait_kick(&blk->wait);
|
aio_wait_kick();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void error_callback_bh(void *opaque)
|
static void error_callback_bh(void *opaque)
|
||||||
|
@ -1332,8 +1302,8 @@ static const AIOCBInfo blk_aio_em_aiocb_info = {
|
||||||
static void blk_aio_complete(BlkAioEmAIOCB *acb)
|
static void blk_aio_complete(BlkAioEmAIOCB *acb)
|
||||||
{
|
{
|
||||||
if (acb->has_returned) {
|
if (acb->has_returned) {
|
||||||
blk_dec_in_flight(acb->rwco.blk);
|
|
||||||
acb->common.cb(acb->common.opaque, acb->rwco.ret);
|
acb->common.cb(acb->common.opaque, acb->rwco.ret);
|
||||||
|
blk_dec_in_flight(acb->rwco.blk);
|
||||||
qemu_aio_unref(acb);
|
qemu_aio_unref(acb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1530,6 +1500,7 @@ static void blk_ioctl_entry(void *opaque)
|
||||||
|
|
||||||
rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset,
|
rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset,
|
||||||
qiov->iov[0].iov_base);
|
qiov->iov[0].iov_base);
|
||||||
|
aio_wait_kick();
|
||||||
}
|
}
|
||||||
|
|
||||||
int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
|
int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
|
||||||
|
@ -1576,6 +1547,7 @@ static void blk_flush_entry(void *opaque)
|
||||||
{
|
{
|
||||||
BlkRwCo *rwco = opaque;
|
BlkRwCo *rwco = opaque;
|
||||||
rwco->ret = blk_co_flush(rwco->blk);
|
rwco->ret = blk_co_flush(rwco->blk);
|
||||||
|
aio_wait_kick();
|
||||||
}
|
}
|
||||||
|
|
||||||
int blk_flush(BlockBackend *blk)
|
int blk_flush(BlockBackend *blk)
|
||||||
|
@ -1592,9 +1564,8 @@ void blk_drain(BlockBackend *blk)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We may have -ENOMEDIUM completions in flight */
|
/* We may have -ENOMEDIUM completions in flight */
|
||||||
AIO_WAIT_WHILE(&blk->wait,
|
AIO_WAIT_WHILE(blk_get_aio_context(blk),
|
||||||
blk_get_aio_context(blk),
|
atomic_mb_read(&blk->in_flight) > 0);
|
||||||
atomic_mb_read(&blk->in_flight) > 0);
|
|
||||||
|
|
||||||
if (bs) {
|
if (bs) {
|
||||||
bdrv_drained_end(bs);
|
bdrv_drained_end(bs);
|
||||||
|
@ -1613,8 +1584,7 @@ void blk_drain_all(void)
|
||||||
aio_context_acquire(ctx);
|
aio_context_acquire(ctx);
|
||||||
|
|
||||||
/* We may have -ENOMEDIUM completions in flight */
|
/* We may have -ENOMEDIUM completions in flight */
|
||||||
AIO_WAIT_WHILE(&blk->wait, ctx,
|
AIO_WAIT_WHILE(ctx, atomic_mb_read(&blk->in_flight) > 0);
|
||||||
atomic_mb_read(&blk->in_flight) > 0);
|
|
||||||
|
|
||||||
aio_context_release(ctx);
|
aio_context_release(ctx);
|
||||||
}
|
}
|
||||||
|
@ -1666,8 +1636,7 @@ static void send_qmp_error_event(BlockBackend *blk,
|
||||||
qapi_event_send_block_io_error(blk_name(blk), !!bs,
|
qapi_event_send_block_io_error(blk_name(blk), !!bs,
|
||||||
bs ? bdrv_get_node_name(bs) : NULL, optype,
|
bs ? bdrv_get_node_name(bs) : NULL, optype,
|
||||||
action, blk_iostatus_is_enabled(blk),
|
action, blk_iostatus_is_enabled(blk),
|
||||||
error == ENOSPC, strerror(error),
|
error == ENOSPC, strerror(error));
|
||||||
&error_abort);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is done by device models because, while the block layer knows
|
/* This is done by device models because, while the block layer knows
|
||||||
|
@ -1702,7 +1671,7 @@ void blk_error_action(BlockBackend *blk, BlockErrorAction action,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int blk_is_read_only(BlockBackend *blk)
|
bool blk_is_read_only(BlockBackend *blk)
|
||||||
{
|
{
|
||||||
BlockDriverState *bs = blk_bs(blk);
|
BlockDriverState *bs = blk_bs(blk);
|
||||||
|
|
||||||
|
@ -1713,18 +1682,18 @@ int blk_is_read_only(BlockBackend *blk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int blk_is_sg(BlockBackend *blk)
|
bool blk_is_sg(BlockBackend *blk)
|
||||||
{
|
{
|
||||||
BlockDriverState *bs = blk_bs(blk);
|
BlockDriverState *bs = blk_bs(blk);
|
||||||
|
|
||||||
if (!bs) {
|
if (!bs) {
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return bdrv_is_sg(bs);
|
return bdrv_is_sg(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
int blk_enable_write_cache(BlockBackend *blk)
|
bool blk_enable_write_cache(BlockBackend *blk)
|
||||||
{
|
{
|
||||||
return blk->enable_write_cache;
|
return blk->enable_write_cache;
|
||||||
}
|
}
|
||||||
|
@ -1772,9 +1741,6 @@ void blk_eject(BlockBackend *blk, bool eject_flag)
|
||||||
BlockDriverState *bs = blk_bs(blk);
|
BlockDriverState *bs = blk_bs(blk);
|
||||||
char *id;
|
char *id;
|
||||||
|
|
||||||
/* blk_eject is only called by qdevified devices */
|
|
||||||
assert(!blk->legacy_dev);
|
|
||||||
|
|
||||||
if (bs) {
|
if (bs) {
|
||||||
bdrv_eject(bs, eject_flag);
|
bdrv_eject(bs, eject_flag);
|
||||||
}
|
}
|
||||||
|
@ -1783,7 +1749,7 @@ void blk_eject(BlockBackend *blk, bool eject_flag)
|
||||||
* the frontend experienced a tray event. */
|
* the frontend experienced a tray event. */
|
||||||
id = blk_get_attached_dev_id(blk);
|
id = blk_get_attached_dev_id(blk);
|
||||||
qapi_event_send_device_tray_moved(blk_name(blk), id,
|
qapi_event_send_device_tray_moved(blk_name(blk), id,
|
||||||
eject_flag, &error_abort);
|
eject_flag);
|
||||||
g_free(id);
|
g_free(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1798,6 +1764,13 @@ int blk_get_flags(BlockBackend *blk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returns the minimum request alignment, in bytes; guaranteed nonzero */
|
||||||
|
uint32_t blk_get_request_alignment(BlockBackend *blk)
|
||||||
|
{
|
||||||
|
BlockDriverState *bs = blk_bs(blk);
|
||||||
|
return bs ? bs->bl.request_alignment : BDRV_SECTOR_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns the maximum transfer length, in bytes; guaranteed nonzero */
|
/* Returns the maximum transfer length, in bytes; guaranteed nonzero */
|
||||||
uint32_t blk_get_max_transfer(BlockBackend *blk)
|
uint32_t blk_get_max_transfer(BlockBackend *blk)
|
||||||
{
|
{
|
||||||
|
@ -2011,6 +1984,7 @@ static void blk_pdiscard_entry(void *opaque)
|
||||||
QEMUIOVector *qiov = rwco->iobuf;
|
QEMUIOVector *qiov = rwco->iobuf;
|
||||||
|
|
||||||
rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size);
|
rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size);
|
||||||
|
aio_wait_kick();
|
||||||
}
|
}
|
||||||
|
|
||||||
int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
|
int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
|
||||||
|
@ -2192,6 +2166,13 @@ static void blk_root_drained_begin(BdrvChild *child)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool blk_root_drained_poll(BdrvChild *child)
|
||||||
|
{
|
||||||
|
BlockBackend *blk = child->opaque;
|
||||||
|
assert(blk->quiesce_counter);
|
||||||
|
return !!blk->in_flight;
|
||||||
|
}
|
||||||
|
|
||||||
static void blk_root_drained_end(BdrvChild *child)
|
static void blk_root_drained_end(BdrvChild *child)
|
||||||
{
|
{
|
||||||
BlockBackend *blk = child->opaque;
|
BlockBackend *blk = child->opaque;
|
||||||
|
@ -2235,3 +2216,8 @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in,
|
||||||
blk_out->root, off_out,
|
blk_out->root, off_out,
|
||||||
bytes, read_flags, write_flags);
|
bytes, read_flags, write_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const BdrvChild *blk_root(BlockBackend *blk)
|
||||||
|
{
|
||||||
|
return blk->root;
|
||||||
|
}
|
||||||
|
|
|
@ -85,14 +85,14 @@ static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||||
const struct bochs_header *bochs = (const void *)buf;
|
const struct bochs_header *bochs = (const void *)buf;
|
||||||
|
|
||||||
if (buf_size < HEADER_SIZE)
|
if (buf_size < HEADER_SIZE)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!strcmp(bochs->magic, HEADER_MAGIC) &&
|
if (!strcmp(bochs->magic, HEADER_MAGIC) &&
|
||||||
!strcmp(bochs->type, REDOLOG_TYPE) &&
|
!strcmp(bochs->type, REDOLOG_TYPE) &&
|
||||||
!strcmp(bochs->subtype, GROWING_TYPE) &&
|
!strcmp(bochs->subtype, GROWING_TYPE) &&
|
||||||
((le32_to_cpu(bochs->version) == HEADER_VERSION) ||
|
((le32_to_cpu(bochs->version) == HEADER_VERSION) ||
|
||||||
(le32_to_cpu(bochs->version) == HEADER_V1)))
|
(le32_to_cpu(bochs->version) == HEADER_V1)))
|
||||||
return 100;
|
return 100;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -105,23 +105,18 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
struct bochs_header bochs;
|
struct bochs_header bochs;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/* No write support yet */
|
||||||
|
ret = bdrv_apply_auto_read_only(bs, NULL, errp);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||||
false, errp);
|
false, errp);
|
||||||
if (!bs->file) {
|
if (!bs->file) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bdrv_is_read_only(bs)) {
|
|
||||||
error_report("Opening bochs images without an explicit read-only=on "
|
|
||||||
"option is deprecated. Future versions will refuse to "
|
|
||||||
"open the image instead of automatically marking the "
|
|
||||||
"image read-only.");
|
|
||||||
ret = bdrv_set_read_only(bs, true, errp); /* no write support yet */
|
|
||||||
if (ret < 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs));
|
ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -130,8 +125,8 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
if (strcmp(bochs.magic, HEADER_MAGIC) ||
|
if (strcmp(bochs.magic, HEADER_MAGIC) ||
|
||||||
strcmp(bochs.type, REDOLOG_TYPE) ||
|
strcmp(bochs.type, REDOLOG_TYPE) ||
|
||||||
strcmp(bochs.subtype, GROWING_TYPE) ||
|
strcmp(bochs.subtype, GROWING_TYPE) ||
|
||||||
((le32_to_cpu(bochs.version) != HEADER_VERSION) &&
|
((le32_to_cpu(bochs.version) != HEADER_VERSION) &&
|
||||||
(le32_to_cpu(bochs.version) != HEADER_V1))) {
|
(le32_to_cpu(bochs.version) != HEADER_V1))) {
|
||||||
error_setg(errp, "Image not in Bochs format");
|
error_setg(errp, "Image not in Bochs format");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -163,7 +158,7 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < s->catalog_size; i++)
|
for (i = 0; i < s->catalog_size; i++)
|
||||||
le32_to_cpus(&s->catalog_bitmap[i]);
|
le32_to_cpus(&s->catalog_bitmap[i]);
|
||||||
|
|
||||||
s->data_offset = le32_to_cpu(bochs.header) + (s->catalog_size * 4);
|
s->data_offset = le32_to_cpu(bochs.header) + (s->catalog_size * 4);
|
||||||
|
|
||||||
|
@ -222,7 +217,7 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||||
extent_offset = (offset % s->extent_size) / 512;
|
extent_offset = (offset % s->extent_size) / 512;
|
||||||
|
|
||||||
if (s->catalog_bitmap[extent_index] == 0xffffffff) {
|
if (s->catalog_bitmap[extent_index] == 0xffffffff) {
|
||||||
return 0; /* not allocated */
|
return 0; /* not allocated */
|
||||||
}
|
}
|
||||||
|
|
||||||
bitmap_offset = s->data_offset +
|
bitmap_offset = s->data_offset +
|
||||||
|
@ -237,7 +232,7 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!((bitmap_entry >> (extent_offset % 8)) & 1)) {
|
if (!((bitmap_entry >> (extent_offset % 8)) & 1)) {
|
||||||
return 0; /* not allocated */
|
return 0; /* not allocated */
|
||||||
}
|
}
|
||||||
|
|
||||||
return bitmap_offset + (512 * (s->bitmap_blocks + extent_offset));
|
return bitmap_offset + (512 * (s->bitmap_blocks + extent_offset));
|
||||||
|
|
|
@ -67,23 +67,17 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
uint32_t offsets_size, max_compressed_block_size = 1, i;
|
uint32_t offsets_size, max_compressed_block_size = 1, i;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
ret = bdrv_apply_auto_read_only(bs, NULL, errp);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||||
false, errp);
|
false, errp);
|
||||||
if (!bs->file) {
|
if (!bs->file) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bdrv_is_read_only(bs)) {
|
|
||||||
error_report("Opening cloop images without an explicit read-only=on "
|
|
||||||
"option is deprecated. Future versions will refuse to "
|
|
||||||
"open the image instead of automatically marking the "
|
|
||||||
"image read-only.");
|
|
||||||
ret = bdrv_set_read_only(bs, true, errp);
|
|
||||||
if (ret < 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read header */
|
/* read header */
|
||||||
ret = bdrv_pread(bs->file, 128, &s->block_size, 4);
|
ret = bdrv_pread(bs->file, 128, &s->block_size, 4);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
|
168
block/commit.c
168
block/commit.c
|
@ -36,8 +36,10 @@ typedef struct CommitBlockJob {
|
||||||
BlockDriverState *commit_top_bs;
|
BlockDriverState *commit_top_bs;
|
||||||
BlockBackend *top;
|
BlockBackend *top;
|
||||||
BlockBackend *base;
|
BlockBackend *base;
|
||||||
|
BlockDriverState *base_bs;
|
||||||
BlockdevOnError on_error;
|
BlockdevOnError on_error;
|
||||||
int base_flags;
|
bool base_read_only;
|
||||||
|
bool chain_frozen;
|
||||||
char *backing_file_str;
|
char *backing_file_str;
|
||||||
} CommitBlockJob;
|
} CommitBlockJob;
|
||||||
|
|
||||||
|
@ -46,14 +48,9 @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base,
|
||||||
void *buf)
|
void *buf)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
QEMUIOVector qiov;
|
QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);
|
||||||
struct iovec iov = {
|
|
||||||
.iov_base = buf,
|
|
||||||
.iov_len = bytes,
|
|
||||||
};
|
|
||||||
|
|
||||||
assert(bytes < SIZE_MAX);
|
assert(bytes < SIZE_MAX);
|
||||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
|
||||||
|
|
||||||
ret = blk_co_preadv(bs, offset, qiov.size, &qiov, 0);
|
ret = blk_co_preadv(bs, offset, qiov.size, &qiov, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -68,76 +65,79 @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
static int commit_prepare(Job *job)
|
||||||
int ret;
|
|
||||||
} CommitCompleteData;
|
|
||||||
|
|
||||||
static void commit_complete(Job *job, void *opaque)
|
|
||||||
{
|
{
|
||||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
|
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
|
||||||
BlockJob *bjob = &s->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_unfreeze_backing_chain(s->commit_top_bs, s->base_bs);
|
||||||
bdrv_ref(top);
|
s->chain_frozen = false;
|
||||||
bdrv_ref(commit_top_bs);
|
|
||||||
|
|
||||||
/* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before
|
/* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before
|
||||||
* the normal backing chain can be restored. */
|
* the normal backing chain can be restored. */
|
||||||
blk_unref(s->base);
|
blk_unref(s->base);
|
||||||
|
s->base = NULL;
|
||||||
|
|
||||||
if (!job_is_cancelled(job) && ret == 0) {
|
/* FIXME: bdrv_drop_intermediate treats total failures and partial failures
|
||||||
/* success */
|
* identically. Further work is needed to disambiguate these cases. */
|
||||||
ret = bdrv_drop_intermediate(s->commit_top_bs, base,
|
return bdrv_drop_intermediate(s->commit_top_bs, s->base_bs,
|
||||||
s->backing_file_str);
|
s->backing_file_str);
|
||||||
} else {
|
}
|
||||||
/* XXX Can (or should) we somehow keep 'consistent read' blocked even
|
|
||||||
* after the failed/cancelled commit job is gone? If we already wrote
|
static void commit_abort(Job *job)
|
||||||
* something to base, the intermediate images aren't valid any more. */
|
{
|
||||||
remove_commit_top_bs = true;
|
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
|
||||||
|
BlockDriverState *top_bs = blk_bs(s->top);
|
||||||
|
|
||||||
|
if (s->chain_frozen) {
|
||||||
|
bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Make sure commit_top_bs and top stay around until bdrv_replace_node() */
|
||||||
|
bdrv_ref(top_bs);
|
||||||
|
bdrv_ref(s->commit_top_bs);
|
||||||
|
|
||||||
|
if (s->base) {
|
||||||
|
blk_unref(s->base);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* free the blockers on the intermediate nodes so that bdrv_replace_nodes
|
||||||
|
* can succeed */
|
||||||
|
block_job_remove_all_bdrv(&s->common);
|
||||||
|
|
||||||
|
/* If bdrv_drop_intermediate() failed (or was not invoked), remove the
|
||||||
|
* commit filter driver from the backing chain now. Do this as the final
|
||||||
|
* step so that the 'consistent read' permission can be granted.
|
||||||
|
*
|
||||||
|
* XXX Can (or should) we somehow keep 'consistent read' blocked even
|
||||||
|
* after the failed/cancelled commit job is gone? If we already wrote
|
||||||
|
* something to base, the intermediate images aren't valid any more. */
|
||||||
|
bdrv_child_try_set_perm(s->commit_top_bs->backing, 0, BLK_PERM_ALL,
|
||||||
|
&error_abort);
|
||||||
|
bdrv_replace_node(s->commit_top_bs, backing_bs(s->commit_top_bs),
|
||||||
|
&error_abort);
|
||||||
|
|
||||||
|
bdrv_unref(s->commit_top_bs);
|
||||||
|
bdrv_unref(top_bs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void commit_clean(Job *job)
|
||||||
|
{
|
||||||
|
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
|
||||||
|
|
||||||
/* restore base open flags here if appropriate (e.g., change the base back
|
/* restore base open flags here if appropriate (e.g., change the base back
|
||||||
* to r/o). These reopens do not need to be atomic, since we won't abort
|
* to r/o). These reopens do not need to be atomic, since we won't abort
|
||||||
* even on failure here */
|
* even on failure here */
|
||||||
if (s->base_flags != bdrv_get_flags(base)) {
|
if (s->base_read_only) {
|
||||||
bdrv_reopen(base, s->base_flags, NULL);
|
bdrv_reopen_set_read_only(s->base_bs, true, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free(s->backing_file_str);
|
g_free(s->backing_file_str);
|
||||||
blk_unref(s->top);
|
blk_unref(s->top);
|
||||||
|
|
||||||
/* If there is more than one reference to the job (e.g. if called from
|
|
||||||
* job_finish_sync()), job_completed() won't free it and therefore the
|
|
||||||
* blockers on the intermediate nodes remain. This would cause
|
|
||||||
* bdrv_set_backing_hd() to fail. */
|
|
||||||
block_job_remove_all_bdrv(bjob);
|
|
||||||
|
|
||||||
job_completed(job, ret, NULL);
|
|
||||||
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 void coroutine_fn commit_run(void *opaque)
|
static int coroutine_fn commit_run(Job *job, Error **errp)
|
||||||
{
|
{
|
||||||
CommitBlockJob *s = opaque;
|
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
|
||||||
CommitCompleteData *data;
|
|
||||||
int64_t offset;
|
int64_t offset;
|
||||||
uint64_t delay_ns = 0;
|
uint64_t delay_ns = 0;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
@ -210,9 +210,7 @@ static void coroutine_fn commit_run(void *opaque)
|
||||||
out:
|
out:
|
||||||
qemu_vfree(buf);
|
qemu_vfree(buf);
|
||||||
|
|
||||||
data = g_malloc(sizeof(*data));
|
return ret;
|
||||||
data->ret = ret;
|
|
||||||
job_defer_to_main_loop(&s->common.job, commit_complete, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const BlockJobDriver commit_job_driver = {
|
static const BlockJobDriver commit_job_driver = {
|
||||||
|
@ -222,7 +220,10 @@ static const BlockJobDriver commit_job_driver = {
|
||||||
.free = block_job_free,
|
.free = block_job_free,
|
||||||
.user_resume = block_job_user_resume,
|
.user_resume = block_job_user_resume,
|
||||||
.drain = block_job_drain,
|
.drain = block_job_drain,
|
||||||
.start = commit_run,
|
.run = commit_run,
|
||||||
|
.prepare = commit_prepare,
|
||||||
|
.abort = commit_abort,
|
||||||
|
.clean = commit_clean
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -232,17 +233,12 @@ static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs,
|
||||||
return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
|
return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bdrv_commit_top_refresh_filename(BlockDriverState *bs, QDict *opts)
|
static void bdrv_commit_top_refresh_filename(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
bdrv_refresh_filename(bs->backing->bs);
|
|
||||||
pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
|
pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
|
||||||
bs->backing->bs->filename);
|
bs->backing->bs->filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bdrv_commit_top_close(BlockDriverState *bs)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c,
|
static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||||
const BdrvChildRole *role,
|
const BdrvChildRole *role,
|
||||||
BlockReopenQueue *reopen_queue,
|
BlockReopenQueue *reopen_queue,
|
||||||
|
@ -260,17 +256,16 @@ static BlockDriver bdrv_commit_top = {
|
||||||
.bdrv_co_preadv = bdrv_commit_top_preadv,
|
.bdrv_co_preadv = bdrv_commit_top_preadv,
|
||||||
.bdrv_co_block_status = bdrv_co_block_status_from_backing,
|
.bdrv_co_block_status = bdrv_co_block_status_from_backing,
|
||||||
.bdrv_refresh_filename = bdrv_commit_top_refresh_filename,
|
.bdrv_refresh_filename = bdrv_commit_top_refresh_filename,
|
||||||
.bdrv_close = bdrv_commit_top_close,
|
|
||||||
.bdrv_child_perm = bdrv_commit_top_child_perm,
|
.bdrv_child_perm = bdrv_commit_top_child_perm,
|
||||||
};
|
};
|
||||||
|
|
||||||
void commit_start(const char *job_id, BlockDriverState *bs,
|
void commit_start(const char *job_id, BlockDriverState *bs,
|
||||||
BlockDriverState *base, BlockDriverState *top, int64_t speed,
|
BlockDriverState *base, BlockDriverState *top,
|
||||||
|
int creation_flags, int64_t speed,
|
||||||
BlockdevOnError on_error, const char *backing_file_str,
|
BlockdevOnError on_error, const char *backing_file_str,
|
||||||
const char *filter_node_name, Error **errp)
|
const char *filter_node_name, Error **errp)
|
||||||
{
|
{
|
||||||
CommitBlockJob *s;
|
CommitBlockJob *s;
|
||||||
int orig_base_flags;
|
|
||||||
BlockDriverState *iter;
|
BlockDriverState *iter;
|
||||||
BlockDriverState *commit_top_bs = NULL;
|
BlockDriverState *commit_top_bs = NULL;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
@ -283,17 +278,15 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||||
}
|
}
|
||||||
|
|
||||||
s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL,
|
s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL,
|
||||||
speed, JOB_DEFAULT, NULL, NULL, errp);
|
speed, creation_flags, NULL, NULL, errp);
|
||||||
if (!s) {
|
if (!s) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* convert base to r/w, if necessary */
|
/* convert base to r/w, if necessary */
|
||||||
orig_base_flags = bdrv_get_flags(base);
|
s->base_read_only = bdrv_is_read_only(base);
|
||||||
if (!(orig_base_flags & BDRV_O_RDWR)) {
|
if (s->base_read_only) {
|
||||||
bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err);
|
if (bdrv_reopen_set_read_only(base, false, errp) != 0) {
|
||||||
if (local_err != NULL) {
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,6 +338,11 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bdrv_freeze_backing_chain(commit_top_bs, base, errp) < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
s->chain_frozen = true;
|
||||||
|
|
||||||
ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp);
|
ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -360,6 +358,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
s->base_bs = base;
|
||||||
|
|
||||||
/* Required permissions are already taken with block_job_add_bdrv() */
|
/* Required permissions are already taken with block_job_add_bdrv() */
|
||||||
s->top = blk_new(0, BLK_PERM_ALL);
|
s->top = blk_new(0, BLK_PERM_ALL);
|
||||||
|
@ -368,7 +367,6 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->base_flags = orig_base_flags;
|
|
||||||
s->backing_file_str = g_strdup(backing_file_str);
|
s->backing_file_str = g_strdup(backing_file_str);
|
||||||
s->on_error = on_error;
|
s->on_error = on_error;
|
||||||
|
|
||||||
|
@ -377,16 +375,21 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
if (s->chain_frozen) {
|
||||||
|
bdrv_unfreeze_backing_chain(commit_top_bs, base);
|
||||||
|
}
|
||||||
if (s->base) {
|
if (s->base) {
|
||||||
blk_unref(s->base);
|
blk_unref(s->base);
|
||||||
}
|
}
|
||||||
if (s->top) {
|
if (s->top) {
|
||||||
blk_unref(s->top);
|
blk_unref(s->top);
|
||||||
}
|
}
|
||||||
|
job_early_fail(&s->common.job);
|
||||||
|
/* commit_top_bs has to be replaced after deleting the block job,
|
||||||
|
* otherwise this would fail because of lack of permissions. */
|
||||||
if (commit_top_bs) {
|
if (commit_top_bs) {
|
||||||
bdrv_replace_node(commit_top_bs, top, &error_abort);
|
bdrv_replace_node(commit_top_bs, top, &error_abort);
|
||||||
}
|
}
|
||||||
job_early_fail(&s->common.job);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -400,7 +403,7 @@ int bdrv_commit(BlockDriverState *bs)
|
||||||
BlockDriverState *commit_top_bs = NULL;
|
BlockDriverState *commit_top_bs = NULL;
|
||||||
BlockDriver *drv = bs->drv;
|
BlockDriver *drv = bs->drv;
|
||||||
int64_t offset, length, backing_length;
|
int64_t offset, length, backing_length;
|
||||||
int ro, open_flags;
|
int ro;
|
||||||
int64_t n;
|
int64_t n;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
uint8_t *buf = NULL;
|
uint8_t *buf = NULL;
|
||||||
|
@ -419,10 +422,9 @@ int bdrv_commit(BlockDriverState *bs)
|
||||||
}
|
}
|
||||||
|
|
||||||
ro = bs->backing->bs->read_only;
|
ro = bs->backing->bs->read_only;
|
||||||
open_flags = bs->backing->bs->open_flags;
|
|
||||||
|
|
||||||
if (ro) {
|
if (ro) {
|
||||||
if (bdrv_reopen(bs->backing->bs, open_flags | BDRV_O_RDWR, NULL)) {
|
if (bdrv_reopen_set_read_only(bs->backing->bs, false, NULL)) {
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -532,7 +534,7 @@ ro_cleanup:
|
||||||
|
|
||||||
if (ro) {
|
if (ro) {
|
||||||
/* ignoring error return here */
|
/* ignoring error return here */
|
||||||
bdrv_reopen(bs->backing->bs, open_flags & ~BDRV_O_RDWR, NULL);
|
bdrv_reopen_set_read_only(bs->backing->bs, true, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -34,22 +34,16 @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
}
|
}
|
||||||
|
|
||||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
|
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||||
(BDRV_REQ_FUA &
|
(BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
|
||||||
bs->file->bs->supported_write_flags);
|
|
||||||
|
|
||||||
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
|
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||||
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
|
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
|
||||||
bs->file->bs->supported_zero_flags);
|
bs->file->bs->supported_zero_flags);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void cor_close(BlockDriverState *bs)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \
|
#define PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \
|
||||||
| BLK_PERM_WRITE \
|
| BLK_PERM_WRITE \
|
||||||
| BLK_PERM_RESIZE)
|
| BLK_PERM_RESIZE)
|
||||||
|
@ -139,11 +133,10 @@ static bool cor_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BlockDriver bdrv_copy_on_read = {
|
static BlockDriver bdrv_copy_on_read = {
|
||||||
.format_name = "copy-on-read",
|
.format_name = "copy-on-read",
|
||||||
|
|
||||||
.bdrv_open = cor_open,
|
.bdrv_open = cor_open,
|
||||||
.bdrv_close = cor_close,
|
|
||||||
.bdrv_child_perm = cor_child_perm,
|
.bdrv_child_perm = cor_child_perm,
|
||||||
|
|
||||||
.bdrv_getlength = cor_getlength,
|
.bdrv_getlength = cor_getlength,
|
||||||
|
|
|
@ -34,33 +34,26 @@ typedef struct BlockdevCreateJob {
|
||||||
Job common;
|
Job common;
|
||||||
BlockDriver *drv;
|
BlockDriver *drv;
|
||||||
BlockdevCreateOptions *opts;
|
BlockdevCreateOptions *opts;
|
||||||
int ret;
|
|
||||||
Error *err;
|
|
||||||
} BlockdevCreateJob;
|
} BlockdevCreateJob;
|
||||||
|
|
||||||
static void blockdev_create_complete(Job *job, void *opaque)
|
static int coroutine_fn blockdev_create_run(Job *job, Error **errp)
|
||||||
{
|
{
|
||||||
BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common);
|
BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common);
|
||||||
|
int ret;
|
||||||
job_completed(job, s->ret, s->err);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void coroutine_fn blockdev_create_run(void *opaque)
|
|
||||||
{
|
|
||||||
BlockdevCreateJob *s = opaque;
|
|
||||||
|
|
||||||
job_progress_set_remaining(&s->common, 1);
|
job_progress_set_remaining(&s->common, 1);
|
||||||
s->ret = s->drv->bdrv_co_create(s->opts, &s->err);
|
ret = s->drv->bdrv_co_create(s->opts, errp);
|
||||||
job_progress_update(&s->common, 1);
|
job_progress_update(&s->common, 1);
|
||||||
|
|
||||||
qapi_free_BlockdevCreateOptions(s->opts);
|
qapi_free_BlockdevCreateOptions(s->opts);
|
||||||
job_defer_to_main_loop(&s->common, blockdev_create_complete, NULL);
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const JobDriver blockdev_create_job_driver = {
|
static const JobDriver blockdev_create_job_driver = {
|
||||||
.instance_size = sizeof(BlockdevCreateJob),
|
.instance_size = sizeof(BlockdevCreateJob),
|
||||||
.job_type = JOB_TYPE_CREATE,
|
.job_type = JOB_TYPE_CREATE,
|
||||||
.start = blockdev_create_run,
|
.run = blockdev_create_run,
|
||||||
};
|
};
|
||||||
|
|
||||||
void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options,
|
void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options,
|
||||||
|
|
|
@ -229,6 +229,7 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
|
||||||
block_crypto_read_func,
|
block_crypto_read_func,
|
||||||
bs,
|
bs,
|
||||||
cflags,
|
cflags,
|
||||||
|
1,
|
||||||
errp);
|
errp);
|
||||||
|
|
||||||
if (!crypto->block) {
|
if (!crypto->block) {
|
||||||
|
@ -593,20 +594,17 @@ static int block_crypto_get_info_luks(BlockDriverState *bs,
|
||||||
}
|
}
|
||||||
|
|
||||||
static ImageInfoSpecific *
|
static ImageInfoSpecific *
|
||||||
block_crypto_get_specific_info_luks(BlockDriverState *bs)
|
block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp)
|
||||||
{
|
{
|
||||||
BlockCrypto *crypto = bs->opaque;
|
BlockCrypto *crypto = bs->opaque;
|
||||||
ImageInfoSpecific *spec_info;
|
ImageInfoSpecific *spec_info;
|
||||||
QCryptoBlockInfo *info;
|
QCryptoBlockInfo *info;
|
||||||
|
|
||||||
info = qcrypto_block_get_info(crypto->block, NULL);
|
info = qcrypto_block_get_info(crypto->block, errp);
|
||||||
if (!info) {
|
if (!info) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (info->format != Q_CRYPTO_BLOCK_FORMAT_LUKS) {
|
assert(info->format == Q_CRYPTO_BLOCK_FORMAT_LUKS);
|
||||||
qapi_free_QCryptoBlockInfo(info);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
spec_info = g_new(ImageInfoSpecific, 1);
|
spec_info = g_new(ImageInfoSpecific, 1);
|
||||||
spec_info->type = IMAGE_INFO_SPECIFIC_KIND_LUKS;
|
spec_info->type = IMAGE_INFO_SPECIFIC_KIND_LUKS;
|
||||||
|
@ -621,13 +619,21 @@ block_crypto_get_specific_info_luks(BlockDriverState *bs)
|
||||||
return spec_info;
|
return spec_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockDriver bdrv_crypto_luks = {
|
static const char *const block_crypto_strong_runtime_opts[] = {
|
||||||
|
BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET,
|
||||||
|
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static BlockDriver bdrv_crypto_luks = {
|
||||||
.format_name = "luks",
|
.format_name = "luks",
|
||||||
.instance_size = sizeof(BlockCrypto),
|
.instance_size = sizeof(BlockCrypto),
|
||||||
.bdrv_probe = block_crypto_probe_luks,
|
.bdrv_probe = block_crypto_probe_luks,
|
||||||
.bdrv_open = block_crypto_open_luks,
|
.bdrv_open = block_crypto_open_luks,
|
||||||
.bdrv_close = block_crypto_close,
|
.bdrv_close = block_crypto_close,
|
||||||
.bdrv_child_perm = bdrv_format_default_perms,
|
/* 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_co_create = block_crypto_co_create_luks,
|
||||||
.bdrv_co_create_opts = block_crypto_co_create_opts_luks,
|
.bdrv_co_create_opts = block_crypto_co_create_opts_luks,
|
||||||
.bdrv_co_truncate = block_crypto_co_truncate,
|
.bdrv_co_truncate = block_crypto_co_truncate,
|
||||||
|
@ -640,6 +646,8 @@ BlockDriver bdrv_crypto_luks = {
|
||||||
.bdrv_getlength = block_crypto_getlength,
|
.bdrv_getlength = block_crypto_getlength,
|
||||||
.bdrv_get_info = block_crypto_get_info_luks,
|
.bdrv_get_info = block_crypto_get_info_luks,
|
||||||
.bdrv_get_specific_info = block_crypto_get_specific_info_luks,
|
.bdrv_get_specific_info = block_crypto_get_specific_info_luks,
|
||||||
|
|
||||||
|
.strong_runtime_opts = block_crypto_strong_runtime_opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void block_crypto_init(void)
|
static void block_crypto_init(void)
|
||||||
|
|
94
block/curl.c
94
block/curl.c
|
@ -32,22 +32,10 @@
|
||||||
#include "crypto/secret.h"
|
#include "crypto/secret.h"
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
// #define DEBUG_CURL
|
|
||||||
// #define DEBUG_VERBOSE
|
// #define DEBUG_VERBOSE
|
||||||
|
|
||||||
#ifdef DEBUG_CURL
|
|
||||||
#define DEBUG_CURL_PRINT 1
|
|
||||||
#else
|
|
||||||
#define DEBUG_CURL_PRINT 0
|
|
||||||
#endif
|
|
||||||
#define DPRINTF(fmt, ...) \
|
|
||||||
do { \
|
|
||||||
if (DEBUG_CURL_PRINT) { \
|
|
||||||
fprintf(stderr, fmt, ## __VA_ARGS__); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#if LIBCURL_VERSION_NUM >= 0x071000
|
#if LIBCURL_VERSION_NUM >= 0x071000
|
||||||
/* The multi interface timer callback was introduced in 7.16.0 */
|
/* The multi interface timer callback was introduced in 7.16.0 */
|
||||||
#define NEED_CURL_TIMER_CALLBACK
|
#define NEED_CURL_TIMER_CALLBACK
|
||||||
|
@ -73,8 +61,6 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
|
||||||
|
|
||||||
#define CURL_NUM_STATES 8
|
#define CURL_NUM_STATES 8
|
||||||
#define CURL_NUM_ACB 8
|
#define CURL_NUM_ACB 8
|
||||||
#define READ_AHEAD_DEFAULT (256 * 1024)
|
|
||||||
#define CURL_TIMEOUT_DEFAULT 5
|
|
||||||
#define CURL_TIMEOUT_MAX 10000
|
#define CURL_TIMEOUT_MAX 10000
|
||||||
|
|
||||||
#define CURL_BLOCK_OPT_URL "url"
|
#define CURL_BLOCK_OPT_URL "url"
|
||||||
|
@ -88,6 +74,10 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
|
||||||
#define CURL_BLOCK_OPT_PROXY_USERNAME "proxy-username"
|
#define CURL_BLOCK_OPT_PROXY_USERNAME "proxy-username"
|
||||||
#define CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET "proxy-password-secret"
|
#define CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET "proxy-password-secret"
|
||||||
|
|
||||||
|
#define CURL_BLOCK_OPT_READAHEAD_DEFAULT (256 * 1024)
|
||||||
|
#define CURL_BLOCK_OPT_SSLVERIFY_DEFAULT true
|
||||||
|
#define CURL_BLOCK_OPT_TIMEOUT_DEFAULT 5
|
||||||
|
|
||||||
struct BDRVCURLState;
|
struct BDRVCURLState;
|
||||||
|
|
||||||
static bool libcurl_initialized;
|
static bool libcurl_initialized;
|
||||||
|
@ -154,7 +144,7 @@ static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque)
|
||||||
{
|
{
|
||||||
BDRVCURLState *s = opaque;
|
BDRVCURLState *s = opaque;
|
||||||
|
|
||||||
DPRINTF("CURL: timer callback timeout_ms %ld\n", timeout_ms);
|
trace_curl_timer_cb(timeout_ms);
|
||||||
if (timeout_ms == -1) {
|
if (timeout_ms == -1) {
|
||||||
timer_del(&s->timer);
|
timer_del(&s->timer);
|
||||||
} else {
|
} else {
|
||||||
|
@ -193,7 +183,7 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
|
||||||
}
|
}
|
||||||
socket = NULL;
|
socket = NULL;
|
||||||
|
|
||||||
DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, (int)fd);
|
trace_curl_sock_cb(action, (int)fd);
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case CURL_POLL_IN:
|
case CURL_POLL_IN:
|
||||||
aio_set_fd_handler(s->aio_context, fd, false,
|
aio_set_fd_handler(s->aio_context, fd, false,
|
||||||
|
@ -238,7 +228,7 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
|
||||||
size_t realsize = size * nmemb;
|
size_t realsize = size * nmemb;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
DPRINTF("CURL: Just reading %zd bytes\n", realsize);
|
trace_curl_read_cb(realsize);
|
||||||
|
|
||||||
if (!s || !s->orig_buf) {
|
if (!s || !s->orig_buf) {
|
||||||
goto read_end;
|
goto read_end;
|
||||||
|
@ -483,6 +473,8 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state)
|
||||||
curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
|
curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
|
||||||
curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER,
|
curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER,
|
||||||
(long) s->sslverify);
|
(long) s->sslverify);
|
||||||
|
curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYHOST,
|
||||||
|
s->sslverify ? 2L : 0L);
|
||||||
if (s->cookie) {
|
if (s->cookie) {
|
||||||
curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie);
|
curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie);
|
||||||
}
|
}
|
||||||
|
@ -682,10 +674,10 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
const char *protocol_delimiter;
|
const char *protocol_delimiter;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
ret = bdrv_apply_auto_read_only(bs, "curl driver does not support writes",
|
||||||
if (flags & BDRV_O_RDWR) {
|
errp);
|
||||||
error_setg(errp, "curl block device does not support writes");
|
if (ret < 0) {
|
||||||
return -EROFS;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!libcurl_initialized) {
|
if (!libcurl_initialized) {
|
||||||
|
@ -706,7 +698,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
}
|
}
|
||||||
|
|
||||||
s->readahead_size = qemu_opt_get_size(opts, CURL_BLOCK_OPT_READAHEAD,
|
s->readahead_size = qemu_opt_get_size(opts, CURL_BLOCK_OPT_READAHEAD,
|
||||||
READ_AHEAD_DEFAULT);
|
CURL_BLOCK_OPT_READAHEAD_DEFAULT);
|
||||||
if ((s->readahead_size & 0x1ff) != 0) {
|
if ((s->readahead_size & 0x1ff) != 0) {
|
||||||
error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512",
|
error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512",
|
||||||
s->readahead_size);
|
s->readahead_size);
|
||||||
|
@ -714,13 +706,14 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
}
|
}
|
||||||
|
|
||||||
s->timeout = qemu_opt_get_number(opts, CURL_BLOCK_OPT_TIMEOUT,
|
s->timeout = qemu_opt_get_number(opts, CURL_BLOCK_OPT_TIMEOUT,
|
||||||
CURL_TIMEOUT_DEFAULT);
|
CURL_BLOCK_OPT_TIMEOUT_DEFAULT);
|
||||||
if (s->timeout > CURL_TIMEOUT_MAX) {
|
if (s->timeout > CURL_TIMEOUT_MAX) {
|
||||||
error_setg(errp, "timeout parameter is too large or negative");
|
error_setg(errp, "timeout parameter is too large or negative");
|
||||||
goto out_noclean;
|
goto out_noclean;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, true);
|
s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY,
|
||||||
|
CURL_BLOCK_OPT_SSLVERIFY_DEFAULT);
|
||||||
|
|
||||||
cookie = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE);
|
cookie = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE);
|
||||||
cookie_secret = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE_SECRET);
|
cookie_secret = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE_SECRET);
|
||||||
|
@ -775,7 +768,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DPRINTF("CURL: Opening %s\n", file);
|
trace_curl_open(file);
|
||||||
qemu_co_queue_init(&s->free_state_waitq);
|
qemu_co_queue_init(&s->free_state_waitq);
|
||||||
s->aio_context = bdrv_get_aio_context(bs);
|
s->aio_context = bdrv_get_aio_context(bs);
|
||||||
s->url = g_strdup(file);
|
s->url = g_strdup(file);
|
||||||
|
@ -828,7 +821,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
"Server does not support 'range' (byte ranges).");
|
"Server does not support 'range' (byte ranges).");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
DPRINTF("CURL: Size = %" PRIu64 "\n", s->len);
|
trace_curl_open_size(s->len);
|
||||||
|
|
||||||
qemu_mutex_lock(&s->mutex);
|
qemu_mutex_lock(&s->mutex);
|
||||||
curl_clean_state(state);
|
curl_clean_state(state);
|
||||||
|
@ -906,8 +899,7 @@ static void curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
|
||||||
state->acb[0] = acb;
|
state->acb[0] = acb;
|
||||||
|
|
||||||
snprintf(state->range, 127, "%" PRIu64 "-%" PRIu64, start, end);
|
snprintf(state->range, 127, "%" PRIu64 "-%" PRIu64, start, end);
|
||||||
DPRINTF("CURL (AIO): Reading %" PRIu64 " at %" PRIu64 " (%s)\n",
|
trace_curl_setup_preadv(acb->bytes, start, state->range);
|
||||||
acb->bytes, start, state->range);
|
|
||||||
curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range);
|
curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range);
|
||||||
|
|
||||||
curl_multi_add_handle(s->multi, state->curl);
|
curl_multi_add_handle(s->multi, state->curl);
|
||||||
|
@ -941,7 +933,7 @@ static void curl_close(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVCURLState *s = bs->opaque;
|
BDRVCURLState *s = bs->opaque;
|
||||||
|
|
||||||
DPRINTF("CURL: Close\n");
|
trace_curl_close();
|
||||||
curl_detach_aio_context(bs);
|
curl_detach_aio_context(bs);
|
||||||
qemu_mutex_destroy(&s->mutex);
|
qemu_mutex_destroy(&s->mutex);
|
||||||
|
|
||||||
|
@ -958,6 +950,36 @@ static int64_t curl_getlength(BlockDriverState *bs)
|
||||||
return s->len;
|
return s->len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void curl_refresh_filename(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVCURLState *s = bs->opaque;
|
||||||
|
|
||||||
|
/* "readahead" and "timeout" do not change the guest-visible data,
|
||||||
|
* so ignore them */
|
||||||
|
if (s->sslverify != CURL_BLOCK_OPT_SSLVERIFY_DEFAULT ||
|
||||||
|
s->cookie || s->username || s->password || s->proxyusername ||
|
||||||
|
s->proxypassword)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), s->url);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char *const curl_strong_runtime_opts[] = {
|
||||||
|
CURL_BLOCK_OPT_URL,
|
||||||
|
CURL_BLOCK_OPT_SSLVERIFY,
|
||||||
|
CURL_BLOCK_OPT_COOKIE,
|
||||||
|
CURL_BLOCK_OPT_COOKIE_SECRET,
|
||||||
|
CURL_BLOCK_OPT_USERNAME,
|
||||||
|
CURL_BLOCK_OPT_PASSWORD_SECRET,
|
||||||
|
CURL_BLOCK_OPT_PROXY_USERNAME,
|
||||||
|
CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET,
|
||||||
|
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_http = {
|
static BlockDriver bdrv_http = {
|
||||||
.format_name = "http",
|
.format_name = "http",
|
||||||
.protocol_name = "http",
|
.protocol_name = "http",
|
||||||
|
@ -972,6 +994,9 @@ static BlockDriver bdrv_http = {
|
||||||
|
|
||||||
.bdrv_detach_aio_context = curl_detach_aio_context,
|
.bdrv_detach_aio_context = curl_detach_aio_context,
|
||||||
.bdrv_attach_aio_context = curl_attach_aio_context,
|
.bdrv_attach_aio_context = curl_attach_aio_context,
|
||||||
|
|
||||||
|
.bdrv_refresh_filename = curl_refresh_filename,
|
||||||
|
.strong_runtime_opts = curl_strong_runtime_opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_https = {
|
static BlockDriver bdrv_https = {
|
||||||
|
@ -988,6 +1013,9 @@ static BlockDriver bdrv_https = {
|
||||||
|
|
||||||
.bdrv_detach_aio_context = curl_detach_aio_context,
|
.bdrv_detach_aio_context = curl_detach_aio_context,
|
||||||
.bdrv_attach_aio_context = curl_attach_aio_context,
|
.bdrv_attach_aio_context = curl_attach_aio_context,
|
||||||
|
|
||||||
|
.bdrv_refresh_filename = curl_refresh_filename,
|
||||||
|
.strong_runtime_opts = curl_strong_runtime_opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_ftp = {
|
static BlockDriver bdrv_ftp = {
|
||||||
|
@ -1004,6 +1032,9 @@ static BlockDriver bdrv_ftp = {
|
||||||
|
|
||||||
.bdrv_detach_aio_context = curl_detach_aio_context,
|
.bdrv_detach_aio_context = curl_detach_aio_context,
|
||||||
.bdrv_attach_aio_context = curl_attach_aio_context,
|
.bdrv_attach_aio_context = curl_attach_aio_context,
|
||||||
|
|
||||||
|
.bdrv_refresh_filename = curl_refresh_filename,
|
||||||
|
.strong_runtime_opts = curl_strong_runtime_opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_ftps = {
|
static BlockDriver bdrv_ftps = {
|
||||||
|
@ -1020,6 +1051,9 @@ static BlockDriver bdrv_ftps = {
|
||||||
|
|
||||||
.bdrv_detach_aio_context = curl_detach_aio_context,
|
.bdrv_detach_aio_context = curl_detach_aio_context,
|
||||||
.bdrv_attach_aio_context = curl_attach_aio_context,
|
.bdrv_attach_aio_context = curl_attach_aio_context,
|
||||||
|
|
||||||
|
.bdrv_refresh_filename = curl_refresh_filename,
|
||||||
|
.strong_runtime_opts = curl_strong_runtime_opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void curl_block_init(void)
|
static void curl_block_init(void)
|
||||||
|
|
|
@ -28,21 +28,12 @@
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "block/blockjob.h"
|
#include "block/blockjob.h"
|
||||||
|
|
||||||
/**
|
|
||||||
* A BdrvDirtyBitmap can be in three possible states:
|
|
||||||
* (1) successor is NULL and disabled is false: full r/w mode
|
|
||||||
* (2) successor is NULL and disabled is true: read only mode ("disabled")
|
|
||||||
* (3) successor is set: frozen mode.
|
|
||||||
* A frozen bitmap cannot be renamed, deleted, anonymized, cleared, set,
|
|
||||||
* or enabled. A frozen bitmap can only abdicate() or reclaim().
|
|
||||||
*/
|
|
||||||
struct BdrvDirtyBitmap {
|
struct BdrvDirtyBitmap {
|
||||||
QemuMutex *mutex;
|
QemuMutex *mutex;
|
||||||
HBitmap *bitmap; /* Dirty bitmap implementation */
|
HBitmap *bitmap; /* Dirty bitmap implementation */
|
||||||
HBitmap *meta; /* Meta dirty bitmap */
|
HBitmap *meta; /* Meta dirty bitmap */
|
||||||
bool qmp_locked; /* Bitmap is locked, it can't be modified
|
bool busy; /* Bitmap is busy, it can't be used via QMP */
|
||||||
through QMP */
|
BdrvDirtyBitmap *successor; /* Anonymous child, if any. */
|
||||||
BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */
|
|
||||||
char *name; /* Optional non-empty unique ID */
|
char *name; /* Optional non-empty unique ID */
|
||||||
int64_t size; /* Size of the bitmap, in bytes */
|
int64_t size; /* Size of the bitmap, in bytes */
|
||||||
bool disabled; /* Bitmap is disabled. It ignores all writes to
|
bool disabled; /* Bitmap is disabled. It ignores all writes to
|
||||||
|
@ -55,6 +46,13 @@ struct BdrvDirtyBitmap {
|
||||||
and this bitmap must remain unchanged while
|
and this bitmap must remain unchanged while
|
||||||
this flag is set. */
|
this flag is set. */
|
||||||
bool persistent; /* bitmap must be saved to owner disk image */
|
bool persistent; /* bitmap must be saved to owner disk image */
|
||||||
|
bool inconsistent; /* bitmap is persistent, but inconsistent.
|
||||||
|
It cannot be used at all in any way, except
|
||||||
|
a QMP user can remove it. */
|
||||||
|
bool migration; /* Bitmap is selected for migration, it should
|
||||||
|
not be stored on the next inactivation
|
||||||
|
(persistent flag doesn't matter until next
|
||||||
|
invalidation).*/
|
||||||
QLIST_ENTRY(BdrvDirtyBitmap) list;
|
QLIST_ENTRY(BdrvDirtyBitmap) list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -171,35 +169,58 @@ const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called with BQL taken. */
|
/* Called with BQL taken. */
|
||||||
bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap)
|
bool bdrv_dirty_bitmap_has_successor(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
return bitmap->successor;
|
return bitmap->successor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked)
|
static bool bdrv_dirty_bitmap_busy(const BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
qemu_mutex_lock(bitmap->mutex);
|
return bitmap->busy;
|
||||||
bitmap->qmp_locked = qmp_locked;
|
|
||||||
qemu_mutex_unlock(bitmap->mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap)
|
void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy)
|
||||||
{
|
{
|
||||||
return bitmap->qmp_locked;
|
qemu_mutex_lock(bitmap->mutex);
|
||||||
|
bitmap->busy = busy;
|
||||||
|
qemu_mutex_unlock(bitmap->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called with BQL taken. */
|
/* Called with BQL taken. */
|
||||||
bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap)
|
bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
return !(bitmap->disabled || bitmap->successor);
|
return !bitmap->disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called with BQL taken. */
|
/**
|
||||||
|
* bdrv_dirty_bitmap_status: This API is now deprecated.
|
||||||
|
* Called with BQL taken.
|
||||||
|
*
|
||||||
|
* A BdrvDirtyBitmap can be in four possible user-visible states:
|
||||||
|
* (1) Active: successor is NULL, and disabled is false: full r/w mode
|
||||||
|
* (2) Disabled: successor is NULL, and disabled is true: qualified r/w mode,
|
||||||
|
* guest writes are dropped, but monitor writes are possible,
|
||||||
|
* through commands like merge and clear.
|
||||||
|
* (3) Frozen: successor is not NULL.
|
||||||
|
* A frozen bitmap cannot be renamed, deleted, cleared, set,
|
||||||
|
* enabled, merged to, etc. A frozen bitmap can only abdicate()
|
||||||
|
* or reclaim().
|
||||||
|
* In this state, the anonymous successor bitmap may be either
|
||||||
|
* Active and recording writes from the guest (e.g. backup jobs),
|
||||||
|
* or it can be Disabled and not recording writes.
|
||||||
|
* (4) Locked: Whether Active or Disabled, the user cannot modify this bitmap
|
||||||
|
* in any way from the monitor.
|
||||||
|
* (5) Inconsistent: This is a persistent bitmap whose "in use" bit is set, and
|
||||||
|
* is unusable by QEMU. It can be deleted to remove it from
|
||||||
|
* the qcow2.
|
||||||
|
*/
|
||||||
DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
|
DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
if (bdrv_dirty_bitmap_inconsistent(bitmap)) {
|
||||||
|
return DIRTY_BITMAP_STATUS_INCONSISTENT;
|
||||||
|
} else if (bdrv_dirty_bitmap_has_successor(bitmap)) {
|
||||||
return DIRTY_BITMAP_STATUS_FROZEN;
|
return DIRTY_BITMAP_STATUS_FROZEN;
|
||||||
} else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) {
|
} else if (bdrv_dirty_bitmap_busy(bitmap)) {
|
||||||
return DIRTY_BITMAP_STATUS_LOCKED;
|
return DIRTY_BITMAP_STATUS_LOCKED;
|
||||||
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
||||||
return DIRTY_BITMAP_STATUS_DISABLED;
|
return DIRTY_BITMAP_STATUS_DISABLED;
|
||||||
|
@ -208,9 +229,44 @@ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called with BQL taken. */
|
||||||
|
static bool bdrv_dirty_bitmap_recording(BdrvDirtyBitmap *bitmap)
|
||||||
|
{
|
||||||
|
return !bitmap->disabled || (bitmap->successor &&
|
||||||
|
!bitmap->successor->disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
if ((flags & BDRV_BITMAP_BUSY) && bdrv_dirty_bitmap_busy(bitmap)) {
|
||||||
|
error_setg(errp, "Bitmap '%s' is currently in use by another"
|
||||||
|
" operation and cannot be used", bitmap->name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flags & BDRV_BITMAP_RO) && bdrv_dirty_bitmap_readonly(bitmap)) {
|
||||||
|
error_setg(errp, "Bitmap '%s' is readonly and cannot be modified",
|
||||||
|
bitmap->name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flags & BDRV_BITMAP_INCONSISTENT) &&
|
||||||
|
bdrv_dirty_bitmap_inconsistent(bitmap)) {
|
||||||
|
error_setg(errp, "Bitmap '%s' is inconsistent and cannot be used",
|
||||||
|
bitmap->name);
|
||||||
|
error_append_hint(errp, "Try block-dirty-bitmap-remove to delete"
|
||||||
|
" this bitmap from disk");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a successor bitmap destined to replace this bitmap after an operation.
|
* Create a successor bitmap destined to replace this bitmap after an operation.
|
||||||
* Requires that the bitmap is not frozen and has no successor.
|
* Requires that the bitmap is not marked busy and has no successor.
|
||||||
|
* The successor will be enabled if the parent bitmap was.
|
||||||
* Called with BQL taken.
|
* Called with BQL taken.
|
||||||
*/
|
*/
|
||||||
int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
||||||
|
@ -219,12 +275,14 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
||||||
uint64_t granularity;
|
uint64_t granularity;
|
||||||
BdrvDirtyBitmap *child;
|
BdrvDirtyBitmap *child;
|
||||||
|
|
||||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) {
|
||||||
error_setg(errp, "Cannot create a successor for a bitmap that is "
|
return -1;
|
||||||
"currently frozen");
|
}
|
||||||
|
if (bdrv_dirty_bitmap_has_successor(bitmap)) {
|
||||||
|
error_setg(errp, "Cannot create a successor for a bitmap that already "
|
||||||
|
"has one");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
assert(!bitmap->successor);
|
|
||||||
|
|
||||||
/* Create an anonymous successor */
|
/* Create an anonymous successor */
|
||||||
granularity = bdrv_dirty_bitmap_granularity(bitmap);
|
granularity = bdrv_dirty_bitmap_granularity(bitmap);
|
||||||
|
@ -235,15 +293,16 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
||||||
|
|
||||||
/* Successor will be on or off based on our current state. */
|
/* Successor will be on or off based on our current state. */
|
||||||
child->disabled = bitmap->disabled;
|
child->disabled = bitmap->disabled;
|
||||||
|
bitmap->disabled = true;
|
||||||
|
|
||||||
/* Install the successor and freeze the parent */
|
/* Install the successor and mark the parent as busy */
|
||||||
bitmap->successor = child;
|
bitmap->successor = child;
|
||||||
|
bitmap->busy = true;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
|
void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
|
||||||
bitmap->disabled = false;
|
bitmap->disabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,7 +319,8 @@ void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap)
|
||||||
static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
|
static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
assert(!bitmap->active_iterators);
|
assert(!bitmap->active_iterators);
|
||||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
assert(!bdrv_dirty_bitmap_busy(bitmap));
|
||||||
|
assert(!bdrv_dirty_bitmap_has_successor(bitmap));
|
||||||
assert(!bitmap->meta);
|
assert(!bitmap->meta);
|
||||||
QLIST_REMOVE(bitmap, list);
|
QLIST_REMOVE(bitmap, list);
|
||||||
hbitmap_free(bitmap->bitmap);
|
hbitmap_free(bitmap->bitmap);
|
||||||
|
@ -292,6 +352,7 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
||||||
bitmap->successor = NULL;
|
bitmap->successor = NULL;
|
||||||
successor->persistent = bitmap->persistent;
|
successor->persistent = bitmap->persistent;
|
||||||
bitmap->persistent = false;
|
bitmap->persistent = false;
|
||||||
|
bitmap->busy = false;
|
||||||
bdrv_release_dirty_bitmap(bs, bitmap);
|
bdrv_release_dirty_bitmap(bs, bitmap);
|
||||||
|
|
||||||
return successor;
|
return successor;
|
||||||
|
@ -300,7 +361,8 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
||||||
/**
|
/**
|
||||||
* In cases of failure where we can no longer safely delete the parent,
|
* In cases of failure where we can no longer safely delete the parent,
|
||||||
* we may wish to re-join the parent and child/successor.
|
* we may wish to re-join the parent and child/successor.
|
||||||
* The merged parent will be un-frozen, but not explicitly re-enabled.
|
* The merged parent will be marked as not busy.
|
||||||
|
* The marged parent will be enabled if and only if the successor was enabled.
|
||||||
* Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken.
|
* Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken.
|
||||||
*/
|
*/
|
||||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
|
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
|
||||||
|
@ -314,10 +376,13 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hbitmap_merge(parent->bitmap, successor->bitmap)) {
|
if (!hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap)) {
|
||||||
error_setg(errp, "Merging of parent and successor bitmap failed");
|
error_setg(errp, "Merging of parent and successor bitmap failed");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parent->disabled = successor->disabled;
|
||||||
|
parent->busy = false;
|
||||||
bdrv_release_dirty_bitmap_locked(successor);
|
bdrv_release_dirty_bitmap_locked(successor);
|
||||||
parent->successor = NULL;
|
parent->successor = NULL;
|
||||||
|
|
||||||
|
@ -348,7 +413,8 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes)
|
||||||
|
|
||||||
bdrv_dirty_bitmaps_lock(bs);
|
bdrv_dirty_bitmaps_lock(bs);
|
||||||
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
||||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
assert(!bdrv_dirty_bitmap_busy(bitmap));
|
||||||
|
assert(!bdrv_dirty_bitmap_has_successor(bitmap));
|
||||||
assert(!bitmap->active_iterators);
|
assert(!bitmap->active_iterators);
|
||||||
hbitmap_truncate(bitmap->bitmap, bytes);
|
hbitmap_truncate(bitmap->bitmap, bytes);
|
||||||
bitmap->size = bytes;
|
bitmap->size = bytes;
|
||||||
|
@ -366,7 +432,7 @@ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()).
|
* Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()).
|
||||||
* There must not be any frozen bitmaps attached.
|
* There must not be any busy bitmaps attached.
|
||||||
* This function does not remove persistent bitmaps from the storage.
|
* This function does not remove persistent bitmaps from the storage.
|
||||||
* Called with BQL taken.
|
* Called with BQL taken.
|
||||||
*/
|
*/
|
||||||
|
@ -383,26 +449,6 @@ void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
|
||||||
bdrv_dirty_bitmaps_unlock(bs);
|
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.
|
|
||||||
* Called with BQL taken.
|
|
||||||
*/
|
|
||||||
void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs)
|
|
||||||
{
|
|
||||||
BdrvDirtyBitmap *bm, *next;
|
|
||||||
|
|
||||||
bdrv_dirty_bitmaps_lock(bs);
|
|
||||||
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
|
|
||||||
if (bdrv_dirty_bitmap_get_persistance(bm)) {
|
|
||||||
bdrv_release_dirty_bitmap_locked(bm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bdrv_dirty_bitmaps_unlock(bs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove persistent dirty bitmap from the storage if it exists.
|
* Remove persistent dirty bitmap from the storage if it exists.
|
||||||
* Absence of bitmap is not an error, because we have the following scenario:
|
* Absence of bitmap is not an error, because we have the following scenario:
|
||||||
|
@ -423,7 +469,6 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs,
|
||||||
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
bdrv_dirty_bitmap_lock(bitmap);
|
bdrv_dirty_bitmap_lock(bitmap);
|
||||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
|
||||||
bitmap->disabled = true;
|
bitmap->disabled = true;
|
||||||
bdrv_dirty_bitmap_unlock(bitmap);
|
bdrv_dirty_bitmap_unlock(bitmap);
|
||||||
}
|
}
|
||||||
|
@ -450,6 +495,11 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
|
||||||
info->has_name = !!bm->name;
|
info->has_name = !!bm->name;
|
||||||
info->name = g_strdup(bm->name);
|
info->name = g_strdup(bm->name);
|
||||||
info->status = bdrv_dirty_bitmap_status(bm);
|
info->status = bdrv_dirty_bitmap_status(bm);
|
||||||
|
info->recording = bdrv_dirty_bitmap_recording(bm);
|
||||||
|
info->busy = bdrv_dirty_bitmap_busy(bm);
|
||||||
|
info->persistent = bm->persistent;
|
||||||
|
info->has_inconsistent = bm->inconsistent;
|
||||||
|
info->inconsistent = bm->inconsistent;
|
||||||
entry->value = info;
|
entry->value = info;
|
||||||
*plist = entry;
|
*plist = entry;
|
||||||
plist = &entry->next;
|
plist = &entry->next;
|
||||||
|
@ -525,69 +575,13 @@ void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter)
|
||||||
|
|
||||||
int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter)
|
int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter)
|
||||||
{
|
{
|
||||||
return hbitmap_iter_next(&iter->hbi, true);
|
return hbitmap_iter_next(&iter->hbi);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the next consecutively dirty area in the dirty bitmap
|
|
||||||
* belonging to the given iterator @iter.
|
|
||||||
*
|
|
||||||
* @max_offset: Maximum value that may be returned for
|
|
||||||
* *offset + *bytes
|
|
||||||
* @offset: Will contain the start offset of the next dirty area
|
|
||||||
* @bytes: Will contain the length of the next dirty area
|
|
||||||
*
|
|
||||||
* Returns: True if a dirty area could be found before max_offset
|
|
||||||
* (which means that *offset and *bytes then contain valid
|
|
||||||
* values), false otherwise.
|
|
||||||
*
|
|
||||||
* Note that @iter is never advanced if false is returned. If an area
|
|
||||||
* is found (which means that true is returned), it will be advanced
|
|
||||||
* past that area.
|
|
||||||
*/
|
|
||||||
bool bdrv_dirty_iter_next_area(BdrvDirtyBitmapIter *iter, uint64_t max_offset,
|
|
||||||
uint64_t *offset, int *bytes)
|
|
||||||
{
|
|
||||||
uint32_t granularity = bdrv_dirty_bitmap_granularity(iter->bitmap);
|
|
||||||
uint64_t gran_max_offset;
|
|
||||||
int64_t ret;
|
|
||||||
int size;
|
|
||||||
|
|
||||||
if (max_offset == iter->bitmap->size) {
|
|
||||||
/* If max_offset points to the image end, round it up by the
|
|
||||||
* bitmap granularity */
|
|
||||||
gran_max_offset = ROUND_UP(max_offset, granularity);
|
|
||||||
} else {
|
|
||||||
gran_max_offset = max_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = hbitmap_iter_next(&iter->hbi, false);
|
|
||||||
if (ret < 0 || ret + granularity > gran_max_offset) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*offset = ret;
|
|
||||||
size = 0;
|
|
||||||
|
|
||||||
assert(granularity <= INT_MAX);
|
|
||||||
|
|
||||||
do {
|
|
||||||
/* Advance iterator */
|
|
||||||
ret = hbitmap_iter_next(&iter->hbi, true);
|
|
||||||
size += granularity;
|
|
||||||
} while (ret + granularity <= gran_max_offset &&
|
|
||||||
hbitmap_iter_next(&iter->hbi, false) == ret + granularity &&
|
|
||||||
size <= INT_MAX - granularity);
|
|
||||||
|
|
||||||
*bytes = MIN(size, max_offset - *offset);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called within bdrv_dirty_bitmap_lock..unlock */
|
/* Called within bdrv_dirty_bitmap_lock..unlock */
|
||||||
void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
||||||
int64_t offset, int64_t bytes)
|
int64_t offset, int64_t bytes)
|
||||||
{
|
{
|
||||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
|
||||||
assert(!bdrv_dirty_bitmap_readonly(bitmap));
|
assert(!bdrv_dirty_bitmap_readonly(bitmap));
|
||||||
hbitmap_set(bitmap->bitmap, offset, bytes);
|
hbitmap_set(bitmap->bitmap, offset, bytes);
|
||||||
}
|
}
|
||||||
|
@ -604,7 +598,6 @@ void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||||
void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
||||||
int64_t offset, int64_t bytes)
|
int64_t offset, int64_t bytes)
|
||||||
{
|
{
|
||||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
|
||||||
assert(!bdrv_dirty_bitmap_readonly(bitmap));
|
assert(!bdrv_dirty_bitmap_readonly(bitmap));
|
||||||
hbitmap_reset(bitmap->bitmap, offset, bytes);
|
hbitmap_reset(bitmap->bitmap, offset, bytes);
|
||||||
}
|
}
|
||||||
|
@ -619,7 +612,6 @@ void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||||
|
|
||||||
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
||||||
{
|
{
|
||||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
|
||||||
assert(!bdrv_dirty_bitmap_readonly(bitmap));
|
assert(!bdrv_dirty_bitmap_readonly(bitmap));
|
||||||
bdrv_dirty_bitmap_lock(bitmap);
|
bdrv_dirty_bitmap_lock(bitmap);
|
||||||
if (!out) {
|
if (!out) {
|
||||||
|
@ -633,12 +625,11 @@ void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
||||||
bdrv_dirty_bitmap_unlock(bitmap);
|
bdrv_dirty_bitmap_unlock(bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in)
|
void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup)
|
||||||
{
|
{
|
||||||
HBitmap *tmp = bitmap->bitmap;
|
HBitmap *tmp = bitmap->bitmap;
|
||||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
|
||||||
assert(!bdrv_dirty_bitmap_readonly(bitmap));
|
assert(!bdrv_dirty_bitmap_readonly(bitmap));
|
||||||
bitmap->bitmap = in;
|
bitmap->bitmap = backup;
|
||||||
hbitmap_free(tmp);
|
hbitmap_free(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -749,23 +740,46 @@ bool bdrv_has_readonly_bitmaps(BlockDriverState *bs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called with BQL taken. */
|
/* Called with BQL taken. */
|
||||||
void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent)
|
void bdrv_dirty_bitmap_set_persistence(BdrvDirtyBitmap *bitmap, bool persistent)
|
||||||
{
|
{
|
||||||
qemu_mutex_lock(bitmap->mutex);
|
qemu_mutex_lock(bitmap->mutex);
|
||||||
bitmap->persistent = persistent;
|
bitmap->persistent = persistent;
|
||||||
qemu_mutex_unlock(bitmap->mutex);
|
qemu_mutex_unlock(bitmap->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap)
|
/* Called with BQL taken. */
|
||||||
|
void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
return bitmap->persistent;
|
qemu_mutex_lock(bitmap->mutex);
|
||||||
|
assert(bitmap->persistent == true);
|
||||||
|
bitmap->inconsistent = true;
|
||||||
|
bitmap->disabled = true;
|
||||||
|
qemu_mutex_unlock(bitmap->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called with BQL taken. */
|
||||||
|
void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration)
|
||||||
|
{
|
||||||
|
qemu_mutex_lock(bitmap->mutex);
|
||||||
|
bitmap->migration = migration;
|
||||||
|
qemu_mutex_unlock(bitmap->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bdrv_dirty_bitmap_get_persistence(BdrvDirtyBitmap *bitmap)
|
||||||
|
{
|
||||||
|
return bitmap->persistent && !bitmap->migration;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bdrv_dirty_bitmap_inconsistent(const BdrvDirtyBitmap *bitmap)
|
||||||
|
{
|
||||||
|
return bitmap->inconsistent;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs)
|
bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BdrvDirtyBitmap *bm;
|
BdrvDirtyBitmap *bm;
|
||||||
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
|
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
|
||||||
if (bm->persistent && !bm->readonly) {
|
if (bm->persistent && !bm->readonly && !bm->migration) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -785,25 +799,50 @@ char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp)
|
||||||
return hbitmap_sha256(bitmap->bitmap, errp);
|
return hbitmap_sha256(bitmap->bitmap, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset)
|
int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset,
|
||||||
|
uint64_t bytes)
|
||||||
{
|
{
|
||||||
return hbitmap_next_zero(bitmap->bitmap, offset);
|
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,
|
void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
|
||||||
Error **errp)
|
HBitmap **backup, Error **errp)
|
||||||
{
|
{
|
||||||
|
bool ret;
|
||||||
|
|
||||||
/* only bitmaps from one bds are supported */
|
/* only bitmaps from one bds are supported */
|
||||||
assert(dest->mutex == src->mutex);
|
assert(dest->mutex == src->mutex);
|
||||||
|
|
||||||
qemu_mutex_lock(dest->mutex);
|
qemu_mutex_lock(dest->mutex);
|
||||||
|
|
||||||
assert(bdrv_dirty_bitmap_enabled(dest));
|
if (bdrv_dirty_bitmap_check(dest, BDRV_BITMAP_DEFAULT, errp)) {
|
||||||
assert(!bdrv_dirty_bitmap_readonly(dest));
|
goto out;
|
||||||
|
|
||||||
if (!hbitmap_merge(dest->bitmap, src->bitmap)) {
|
|
||||||
error_setg(errp, "Bitmaps are incompatible and can't be merged");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bdrv_dirty_bitmap_check(src, BDRV_BITMAP_ALLOW_RO, errp)) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hbitmap_can_merge(dest->bitmap, src->bitmap)) {
|
||||||
|
error_setg(errp, "Bitmaps are incompatible and can't be merged");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backup) {
|
||||||
|
*backup = dest->bitmap;
|
||||||
|
dest->bitmap = hbitmap_alloc(dest->size, hbitmap_granularity(*backup));
|
||||||
|
ret = hbitmap_merge(*backup, src->bitmap, dest->bitmap);
|
||||||
|
} else {
|
||||||
|
ret = hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap);
|
||||||
|
}
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
out:
|
||||||
qemu_mutex_unlock(dest->mutex);
|
qemu_mutex_unlock(dest->mutex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* 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,6 +33,9 @@
|
||||||
int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in,
|
int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in,
|
||||||
char *next_out, unsigned int avail_out);
|
char *next_out, unsigned int avail_out);
|
||||||
|
|
||||||
|
int (*dmg_uncompress_lzfse)(char *next_in, unsigned int avail_in,
|
||||||
|
char *next_out, unsigned int avail_out);
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
/* Limit chunk sizes to prevent unreasonable amounts of memory being used
|
/* Limit chunk sizes to prevent unreasonable amounts of memory being used
|
||||||
* or truncating when converting to 32-bit types
|
* or truncating when converting to 32-bit types
|
||||||
|
@ -41,6 +44,19 @@ enum {
|
||||||
DMG_SECTORCOUNTS_MAX = DMG_LENGTHS_MAX / 512,
|
DMG_SECTORCOUNTS_MAX = DMG_LENGTHS_MAX / 512,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
/* DMG Block Type */
|
||||||
|
UDZE = 0, /* Zeroes */
|
||||||
|
UDRW, /* RAW type */
|
||||||
|
UDIG, /* Ignore */
|
||||||
|
UDCO = 0x80000004,
|
||||||
|
UDZO,
|
||||||
|
UDBZ,
|
||||||
|
ULFO,
|
||||||
|
UDCM = 0x7ffffffe, /* Comments */
|
||||||
|
UDLE = 0xffffffff /* Last Entry */
|
||||||
|
};
|
||||||
|
|
||||||
static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename)
|
static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||||
{
|
{
|
||||||
int len;
|
int len;
|
||||||
|
@ -105,15 +121,17 @@ static void update_max_chunk_size(BDRVDMGState *s, uint32_t chunk,
|
||||||
uint32_t uncompressed_sectors = 0;
|
uint32_t uncompressed_sectors = 0;
|
||||||
|
|
||||||
switch (s->types[chunk]) {
|
switch (s->types[chunk]) {
|
||||||
case 0x80000005: /* zlib compressed */
|
case UDZO: /* zlib compressed */
|
||||||
case 0x80000006: /* bzip2 compressed */
|
case UDBZ: /* bzip2 compressed */
|
||||||
|
case ULFO: /* lzfse compressed */
|
||||||
compressed_size = s->lengths[chunk];
|
compressed_size = s->lengths[chunk];
|
||||||
uncompressed_sectors = s->sectorcounts[chunk];
|
uncompressed_sectors = s->sectorcounts[chunk];
|
||||||
break;
|
break;
|
||||||
case 1: /* copy */
|
case UDRW: /* copy */
|
||||||
uncompressed_sectors = DIV_ROUND_UP(s->lengths[chunk], 512);
|
uncompressed_sectors = DIV_ROUND_UP(s->lengths[chunk], 512);
|
||||||
break;
|
break;
|
||||||
case 2: /* zero */
|
case UDZE: /* zero */
|
||||||
|
case UDIG: /* ignore */
|
||||||
/* as the all-zeroes block may be large, it is treated specially: the
|
/* as the all-zeroes block may be large, it is treated specially: the
|
||||||
* sector is not copied from a large buffer, a simple memset is used
|
* sector is not copied from a large buffer, a simple memset is used
|
||||||
* instead. Therefore uncompressed_sectors does not need to be set. */
|
* instead. Therefore uncompressed_sectors does not need to be set. */
|
||||||
|
@ -182,12 +200,15 @@ typedef struct DmgHeaderState {
|
||||||
static bool dmg_is_known_block_type(uint32_t entry_type)
|
static bool dmg_is_known_block_type(uint32_t entry_type)
|
||||||
{
|
{
|
||||||
switch (entry_type) {
|
switch (entry_type) {
|
||||||
case 0x00000001: /* uncompressed */
|
case UDZE: /* zeros */
|
||||||
case 0x00000002: /* zeroes */
|
case UDRW: /* uncompressed */
|
||||||
case 0x80000005: /* zlib */
|
case UDIG: /* ignore */
|
||||||
|
case UDZO: /* zlib */
|
||||||
return true;
|
return true;
|
||||||
case 0x80000006: /* bzip2 */
|
case UDBZ: /* bzip2 */
|
||||||
return !!dmg_uncompress_bz2;
|
return !!dmg_uncompress_bz2;
|
||||||
|
case ULFO: /* lzfse */
|
||||||
|
return !!dmg_uncompress_lzfse;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -246,9 +267,10 @@ static int dmg_read_mish_block(BDRVDMGState *s, DmgHeaderState *ds,
|
||||||
/* sector count */
|
/* sector count */
|
||||||
s->sectorcounts[i] = buff_read_uint64(buffer, offset + 0x10);
|
s->sectorcounts[i] = buff_read_uint64(buffer, offset + 0x10);
|
||||||
|
|
||||||
/* all-zeroes sector (type 2) does not need to be "uncompressed" and can
|
/* all-zeroes sector (type UDZE and UDIG) does not need to be
|
||||||
* therefore be unbounded. */
|
* "uncompressed" and can therefore be unbounded. */
|
||||||
if (s->types[i] != 2 && s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) {
|
if (s->types[i] != UDZE && s->types[i] != UDIG
|
||||||
|
&& s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) {
|
||||||
error_report("sector count %" PRIu64 " for chunk %" PRIu32
|
error_report("sector count %" PRIu64 " for chunk %" PRIu32
|
||||||
" is larger than max (%u)",
|
" is larger than max (%u)",
|
||||||
s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX);
|
s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX);
|
||||||
|
@ -413,24 +435,19 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
int64_t offset;
|
int64_t offset;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
ret = bdrv_apply_auto_read_only(bs, NULL, errp);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||||
false, errp);
|
false, errp);
|
||||||
if (!bs->file) {
|
if (!bs->file) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bdrv_is_read_only(bs)) {
|
|
||||||
error_report("Opening dmg images without an explicit read-only=on "
|
|
||||||
"option is deprecated. Future versions will refuse to "
|
|
||||||
"open the image instead of automatically marking the "
|
|
||||||
"image read-only.");
|
|
||||||
ret = bdrv_set_read_only(bs, true, errp);
|
|
||||||
if (ret < 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
block_module_load_one("dmg-bz2");
|
block_module_load_one("dmg-bz2");
|
||||||
|
block_module_load_one("dmg-lzfse");
|
||||||
|
|
||||||
s->n_chunks = 0;
|
s->n_chunks = 0;
|
||||||
s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL;
|
s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL;
|
||||||
|
@ -558,16 +575,20 @@ static inline uint32_t search_chunk(BDRVDMGState *s, uint64_t sector_num)
|
||||||
{
|
{
|
||||||
/* binary search */
|
/* binary search */
|
||||||
uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3;
|
uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3;
|
||||||
while (chunk1 != chunk2) {
|
while (chunk1 <= chunk2) {
|
||||||
chunk3 = (chunk1 + chunk2) / 2;
|
chunk3 = (chunk1 + chunk2) / 2;
|
||||||
if (s->sectors[chunk3] > sector_num) {
|
if (s->sectors[chunk3] > sector_num) {
|
||||||
chunk2 = chunk3;
|
if (chunk3 == 0) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
chunk2 = chunk3 - 1;
|
||||||
} else if (s->sectors[chunk3] + s->sectorcounts[chunk3] > sector_num) {
|
} else if (s->sectors[chunk3] + s->sectorcounts[chunk3] > sector_num) {
|
||||||
return chunk3;
|
return chunk3;
|
||||||
} else {
|
} else {
|
||||||
chunk1 = chunk3;
|
chunk1 = chunk3 + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
err:
|
||||||
return s->n_chunks; /* error */
|
return s->n_chunks; /* error */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -585,7 +606,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num)
|
||||||
|
|
||||||
s->current_chunk = s->n_chunks;
|
s->current_chunk = s->n_chunks;
|
||||||
switch (s->types[chunk]) { /* block entry type */
|
switch (s->types[chunk]) { /* block entry type */
|
||||||
case 0x80000005: { /* zlib compressed */
|
case UDZO: { /* zlib compressed */
|
||||||
/* we need to buffer, because only the chunk as whole can be
|
/* we need to buffer, because only the chunk as whole can be
|
||||||
* inflated. */
|
* inflated. */
|
||||||
ret = bdrv_pread(bs->file, s->offsets[chunk],
|
ret = bdrv_pread(bs->file, s->offsets[chunk],
|
||||||
|
@ -608,7 +629,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
break; }
|
break; }
|
||||||
case 0x80000006: /* bzip2 compressed */
|
case UDBZ: /* bzip2 compressed */
|
||||||
if (!dmg_uncompress_bz2) {
|
if (!dmg_uncompress_bz2) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -629,14 +650,36 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1: /* copy */
|
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 */
|
||||||
ret = bdrv_pread(bs->file, s->offsets[chunk],
|
ret = bdrv_pread(bs->file, s->offsets[chunk],
|
||||||
s->uncompressed_chunk, s->lengths[chunk]);
|
s->uncompressed_chunk, s->lengths[chunk]);
|
||||||
if (ret != s->lengths[chunk]) {
|
if (ret != s->lengths[chunk]) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 2: /* zero */
|
case UDZE: /* zeros */
|
||||||
|
case UDIG: /* ignore */
|
||||||
/* see dmg_read, it is treated specially. No buffer needs to be
|
/* see dmg_read, it is treated specially. No buffer needs to be
|
||||||
* pre-filled, the zeroes can be set directly. */
|
* pre-filled, the zeroes can be set directly. */
|
||||||
break;
|
break;
|
||||||
|
@ -671,7 +714,8 @@ dmg_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||||
/* Special case: current chunk is all zeroes. Do not perform a memcpy as
|
/* Special case: current chunk is all zeroes. Do not perform a memcpy as
|
||||||
* s->uncompressed_chunk may be too small to cover the large all-zeroes
|
* s->uncompressed_chunk may be too small to cover the large all-zeroes
|
||||||
* section. dmg_read_chunk is called to find s->current_chunk */
|
* section. dmg_read_chunk is called to find s->current_chunk */
|
||||||
if (s->types[s->current_chunk] == 2) { /* all zeroes block entry */
|
if (s->types[s->current_chunk] == UDZE
|
||||||
|
|| s->types[s->current_chunk] == UDIG) { /* all zeroes block entry */
|
||||||
qemu_iovec_memset(qiov, i * 512, 0, 512);
|
qemu_iovec_memset(qiov, i * 512, 0, 512);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,4 +55,7 @@ typedef struct BDRVDMGState {
|
||||||
extern int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in,
|
extern int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in,
|
||||||
char *next_out, unsigned int avail_out);
|
char *next_out, unsigned int avail_out);
|
||||||
|
|
||||||
|
extern int (*dmg_uncompress_lzfse)(char *next_in, unsigned int avail_in,
|
||||||
|
char *next_out, unsigned int avail_out);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -176,7 +176,7 @@ int qemu_ftruncate64(int fd, int64_t length)
|
||||||
BOOL res;
|
BOOL res;
|
||||||
|
|
||||||
if ((GetVersion() & 0x80000000UL) && (length >> 32) != 0)
|
if ((GetVersion() & 0x80000000UL) && (length >> 32) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
h = (HANDLE)_get_osfhandle(fd);
|
h = (HANDLE)_get_osfhandle(fd);
|
||||||
|
|
||||||
|
@ -184,13 +184,13 @@ int qemu_ftruncate64(int fd, int64_t length)
|
||||||
li.HighPart = 0;
|
li.HighPart = 0;
|
||||||
li.LowPart = SetFilePointer (h, 0, &li.HighPart, FILE_CURRENT);
|
li.LowPart = SetFilePointer (h, 0, &li.HighPart, FILE_CURRENT);
|
||||||
if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
|
if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
high = length >> 32;
|
high = length >> 32;
|
||||||
dw = SetFilePointer(h, (DWORD) length, &high, FILE_BEGIN);
|
dw = SetFilePointer(h, (DWORD) length, &high, FILE_BEGIN);
|
||||||
if (dw == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
|
if (dw == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
res = SetEndOfFile(h);
|
res = SetEndOfFile(h);
|
||||||
|
|
||||||
|
@ -203,7 +203,7 @@ static int set_sparse(int fd)
|
||||||
{
|
{
|
||||||
DWORD returned;
|
DWORD returned;
|
||||||
return (int) DeviceIoControl((HANDLE)_get_osfhandle(fd), FSCTL_SET_SPARSE,
|
return (int) DeviceIoControl((HANDLE)_get_osfhandle(fd), FSCTL_SET_SPARSE,
|
||||||
NULL, 0, NULL, 0, &returned, NULL);
|
NULL, 0, NULL, 0, &returned, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void raw_detach_aio_context(BlockDriverState *bs)
|
static void raw_detach_aio_context(BlockDriverState *bs)
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu/units.h"
|
||||||
#include <glusterfs/api/glfs.h>
|
#include <glusterfs/api/glfs.h>
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "block/qdict.h"
|
#include "block/qdict.h"
|
||||||
|
@ -20,6 +21,10 @@
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_GLUSTERFS_FTRUNCATE_HAS_STAT
|
||||||
|
# define glfs_ftruncate(fd, offset) glfs_ftruncate(fd, offset, NULL, NULL)
|
||||||
|
#endif
|
||||||
|
|
||||||
#define GLUSTER_OPT_FILENAME "filename"
|
#define GLUSTER_OPT_FILENAME "filename"
|
||||||
#define GLUSTER_OPT_VOLUME "volume"
|
#define GLUSTER_OPT_VOLUME "volume"
|
||||||
#define GLUSTER_OPT_PATH "path"
|
#define GLUSTER_OPT_PATH "path"
|
||||||
|
@ -37,6 +42,12 @@
|
||||||
#define GLUSTER_DEBUG_MAX 9
|
#define GLUSTER_DEBUG_MAX 9
|
||||||
#define GLUSTER_OPT_LOGFILE "logfile"
|
#define GLUSTER_OPT_LOGFILE "logfile"
|
||||||
#define GLUSTER_LOGFILE_DEFAULT "-" /* handled in libgfapi as /dev/stderr */
|
#define GLUSTER_LOGFILE_DEFAULT "-" /* handled in libgfapi as /dev/stderr */
|
||||||
|
/*
|
||||||
|
* Several versions of GlusterFS (3.12? -> 6.0.1) fail when the transfer size
|
||||||
|
* is greater or equal to 1024 MiB, so we are limiting the transfer size to 512
|
||||||
|
* MiB to avoid this rare issue.
|
||||||
|
*/
|
||||||
|
#define GLUSTER_MAX_TRANSFER (512 * MiB)
|
||||||
|
|
||||||
#define GERR_INDEX_HINT "hint: check in 'server' array index '%d'\n"
|
#define GERR_INDEX_HINT "hint: check in 'server' array index '%d'\n"
|
||||||
|
|
||||||
|
@ -72,7 +83,7 @@ typedef struct ListElement {
|
||||||
GlfsPreopened saved;
|
GlfsPreopened saved;
|
||||||
} ListElement;
|
} ListElement;
|
||||||
|
|
||||||
static QLIST_HEAD(glfs_list, ListElement) glfs_list;
|
static QLIST_HEAD(, ListElement) glfs_list;
|
||||||
|
|
||||||
static QemuOptsList qemu_gluster_create_opts = {
|
static QemuOptsList qemu_gluster_create_opts = {
|
||||||
.name = "qemu-gluster-create-opts",
|
.name = "qemu-gluster-create-opts",
|
||||||
|
@ -725,7 +736,11 @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
|
||||||
/*
|
/*
|
||||||
* AIO callback routine called from GlusterFS thread.
|
* AIO callback routine called from GlusterFS thread.
|
||||||
*/
|
*/
|
||||||
static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
|
static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret,
|
||||||
|
#ifdef CONFIG_GLUSTERFS_IOCB_HAS_STAT
|
||||||
|
struct glfs_stat *pre, struct glfs_stat *post,
|
||||||
|
#endif
|
||||||
|
void *arg)
|
||||||
{
|
{
|
||||||
GlusterAIOCB *acb = (GlusterAIOCB *)arg;
|
GlusterAIOCB *acb = (GlusterAIOCB *)arg;
|
||||||
|
|
||||||
|
@ -849,8 +864,16 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
|
||||||
qemu_gluster_parse_flags(bdrv_flags, &open_flags);
|
qemu_gluster_parse_flags(bdrv_flags, &open_flags);
|
||||||
|
|
||||||
s->fd = glfs_open(s->glfs, gconf->path, open_flags);
|
s->fd = glfs_open(s->glfs, gconf->path, open_flags);
|
||||||
if (!s->fd) {
|
ret = s->fd ? 0 : -errno;
|
||||||
ret = -errno;
|
|
||||||
|
if (ret == -EACCES || ret == -EROFS) {
|
||||||
|
/* Try to degrade to read-only, but if it doesn't work, still use the
|
||||||
|
* normal error message. */
|
||||||
|
if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) {
|
||||||
|
open_flags = (open_flags & ~O_RDWR) | O_RDONLY;
|
||||||
|
s->fd = glfs_open(s->glfs, gconf->path, open_flags);
|
||||||
|
ret = s->fd ? 0 : -errno;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s->supports_seek_data = qemu_gluster_test_seek(s->fd);
|
s->supports_seek_data = qemu_gluster_test_seek(s->fd);
|
||||||
|
@ -871,6 +894,11 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qemu_gluster_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||||
|
{
|
||||||
|
bs->bl.max_transfer = GLUSTER_MAX_TRANSFER;
|
||||||
|
}
|
||||||
|
|
||||||
static int qemu_gluster_reopen_prepare(BDRVReopenState *state,
|
static int qemu_gluster_reopen_prepare(BDRVReopenState *state,
|
||||||
BlockReopenQueue *queue, Error **errp)
|
BlockReopenQueue *queue, Error **errp)
|
||||||
{
|
{
|
||||||
|
@ -1487,6 +1515,21 @@ static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char *const gluster_strong_open_opts[] = {
|
||||||
|
GLUSTER_OPT_VOLUME,
|
||||||
|
GLUSTER_OPT_PATH,
|
||||||
|
GLUSTER_OPT_TYPE,
|
||||||
|
GLUSTER_OPT_SERVER_PATTERN,
|
||||||
|
GLUSTER_OPT_HOST,
|
||||||
|
GLUSTER_OPT_PORT,
|
||||||
|
GLUSTER_OPT_TO,
|
||||||
|
GLUSTER_OPT_IPV4,
|
||||||
|
GLUSTER_OPT_IPV6,
|
||||||
|
GLUSTER_OPT_SOCKET,
|
||||||
|
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_gluster = {
|
static BlockDriver bdrv_gluster = {
|
||||||
.format_name = "gluster",
|
.format_name = "gluster",
|
||||||
.protocol_name = "gluster",
|
.protocol_name = "gluster",
|
||||||
|
@ -1513,7 +1556,9 @@ static BlockDriver bdrv_gluster = {
|
||||||
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
|
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
|
||||||
#endif
|
#endif
|
||||||
.bdrv_co_block_status = qemu_gluster_co_block_status,
|
.bdrv_co_block_status = qemu_gluster_co_block_status,
|
||||||
|
.bdrv_refresh_limits = qemu_gluster_refresh_limits,
|
||||||
.create_opts = &qemu_gluster_create_opts,
|
.create_opts = &qemu_gluster_create_opts,
|
||||||
|
.strong_runtime_opts = gluster_strong_open_opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_gluster_tcp = {
|
static BlockDriver bdrv_gluster_tcp = {
|
||||||
|
@ -1542,7 +1587,9 @@ static BlockDriver bdrv_gluster_tcp = {
|
||||||
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
|
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
|
||||||
#endif
|
#endif
|
||||||
.bdrv_co_block_status = qemu_gluster_co_block_status,
|
.bdrv_co_block_status = qemu_gluster_co_block_status,
|
||||||
|
.bdrv_refresh_limits = qemu_gluster_refresh_limits,
|
||||||
.create_opts = &qemu_gluster_create_opts,
|
.create_opts = &qemu_gluster_create_opts,
|
||||||
|
.strong_runtime_opts = gluster_strong_open_opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_gluster_unix = {
|
static BlockDriver bdrv_gluster_unix = {
|
||||||
|
@ -1571,7 +1618,9 @@ static BlockDriver bdrv_gluster_unix = {
|
||||||
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
|
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
|
||||||
#endif
|
#endif
|
||||||
.bdrv_co_block_status = qemu_gluster_co_block_status,
|
.bdrv_co_block_status = qemu_gluster_co_block_status,
|
||||||
|
.bdrv_refresh_limits = qemu_gluster_refresh_limits,
|
||||||
.create_opts = &qemu_gluster_create_opts,
|
.create_opts = &qemu_gluster_create_opts,
|
||||||
|
.strong_runtime_opts = gluster_strong_open_opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* rdma is deprecated (actually never supported for volfile fetch).
|
/* rdma is deprecated (actually never supported for volfile fetch).
|
||||||
|
@ -1606,7 +1655,9 @@ static BlockDriver bdrv_gluster_rdma = {
|
||||||
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
|
.bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes,
|
||||||
#endif
|
#endif
|
||||||
.bdrv_co_block_status = qemu_gluster_co_block_status,
|
.bdrv_co_block_status = qemu_gluster_co_block_status,
|
||||||
|
.bdrv_refresh_limits = qemu_gluster_refresh_limits,
|
||||||
.create_opts = &qemu_gluster_create_opts,
|
.create_opts = &qemu_gluster_create_opts,
|
||||||
|
.strong_runtime_opts = gluster_strong_open_opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void bdrv_gluster_init(void)
|
static void bdrv_gluster_init(void)
|
||||||
|
|
143
block/io.c
143
block/io.c
|
@ -38,8 +38,6 @@
|
||||||
/* Maximum bounce buffer for copy-on-read and write zeroes, in bytes */
|
/* Maximum bounce buffer for copy-on-read and write zeroes, in bytes */
|
||||||
#define MAX_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS)
|
#define MAX_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS)
|
||||||
|
|
||||||
static AioWait drain_all_aio_wait;
|
|
||||||
|
|
||||||
static void bdrv_parent_cb_resize(BlockDriverState *bs);
|
static void bdrv_parent_cb_resize(BlockDriverState *bs);
|
||||||
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
|
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
|
||||||
int64_t offset, int bytes, BdrvRequestFlags flags);
|
int64_t offset, int bytes, BdrvRequestFlags flags);
|
||||||
|
@ -268,10 +266,6 @@ bool bdrv_drain_poll(BlockDriverState *bs, bool recursive,
|
||||||
static bool bdrv_drain_poll_top_level(BlockDriverState *bs, bool recursive,
|
static bool bdrv_drain_poll_top_level(BlockDriverState *bs, bool recursive,
|
||||||
BdrvChild *ignore_parent)
|
BdrvChild *ignore_parent)
|
||||||
{
|
{
|
||||||
/* Execute pending BHs first and check everything else only after the BHs
|
|
||||||
* have executed. */
|
|
||||||
while (aio_poll(bs->aio_context, false));
|
|
||||||
|
|
||||||
return bdrv_drain_poll(bs, recursive, ignore_parent, false);
|
return bdrv_drain_poll(bs, recursive, ignore_parent, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,6 +282,18 @@ static void bdrv_co_drain_bh_cb(void *opaque)
|
||||||
BlockDriverState *bs = data->bs;
|
BlockDriverState *bs = data->bs;
|
||||||
|
|
||||||
if (bs) {
|
if (bs) {
|
||||||
|
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||||
|
AioContext *co_ctx = qemu_coroutine_get_aio_context(co);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the coroutine yielded, the lock for its home context was
|
||||||
|
* released, so we need to re-acquire it here. If it explicitly
|
||||||
|
* acquired a different context, the lock is still held and we don't
|
||||||
|
* want to lock it a second time (or AIO_WAIT_WHILE() would hang).
|
||||||
|
*/
|
||||||
|
if (ctx == co_ctx) {
|
||||||
|
aio_context_acquire(ctx);
|
||||||
|
}
|
||||||
bdrv_dec_in_flight(bs);
|
bdrv_dec_in_flight(bs);
|
||||||
if (data->begin) {
|
if (data->begin) {
|
||||||
bdrv_do_drained_begin(bs, data->recursive, data->parent,
|
bdrv_do_drained_begin(bs, data->recursive, data->parent,
|
||||||
|
@ -296,6 +302,9 @@ static void bdrv_co_drain_bh_cb(void *opaque)
|
||||||
bdrv_do_drained_end(bs, data->recursive, data->parent,
|
bdrv_do_drained_end(bs, data->recursive, data->parent,
|
||||||
data->ignore_bds_parents);
|
data->ignore_bds_parents);
|
||||||
}
|
}
|
||||||
|
if (ctx == co_ctx) {
|
||||||
|
aio_context_release(ctx);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(data->begin);
|
assert(data->begin);
|
||||||
bdrv_drain_all_begin();
|
bdrv_drain_all_begin();
|
||||||
|
@ -496,10 +505,6 @@ static bool bdrv_drain_all_poll(void)
|
||||||
BlockDriverState *bs = NULL;
|
BlockDriverState *bs = NULL;
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
/* Execute pending BHs first (may modify the graph) and check everything
|
|
||||||
* else only after the BHs have executed. */
|
|
||||||
while (aio_poll(qemu_get_aio_context(), false));
|
|
||||||
|
|
||||||
/* bdrv_drain_poll() can't make changes to the graph and we are holding the
|
/* bdrv_drain_poll() can't make changes to the graph and we are holding the
|
||||||
* main AioContext lock, so iterating bdrv_next_all_states() is safe. */
|
* main AioContext lock, so iterating bdrv_next_all_states() is safe. */
|
||||||
while ((bs = bdrv_next_all_states(bs))) {
|
while ((bs = bdrv_next_all_states(bs))) {
|
||||||
|
@ -550,7 +555,7 @@ void bdrv_drain_all_begin(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now poll the in-flight requests */
|
/* Now poll the in-flight requests */
|
||||||
AIO_WAIT_WHILE(&drain_all_aio_wait, NULL, bdrv_drain_all_poll());
|
AIO_WAIT_WHILE(NULL, bdrv_drain_all_poll());
|
||||||
|
|
||||||
while ((bs = bdrv_next_all_states(bs))) {
|
while ((bs = bdrv_next_all_states(bs))) {
|
||||||
bdrv_drain_assert_idle(bs);
|
bdrv_drain_assert_idle(bs);
|
||||||
|
@ -706,8 +711,7 @@ void bdrv_inc_in_flight(BlockDriverState *bs)
|
||||||
|
|
||||||
void bdrv_wakeup(BlockDriverState *bs)
|
void bdrv_wakeup(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
aio_wait_kick(bdrv_get_aio_wait(bs));
|
aio_wait_kick();
|
||||||
aio_wait_kick(&drain_all_aio_wait);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_dec_in_flight(BlockDriverState *bs)
|
void bdrv_dec_in_flight(BlockDriverState *bs)
|
||||||
|
@ -802,6 +806,7 @@ static void coroutine_fn bdrv_rw_co_entry(void *opaque)
|
||||||
rwco->qiov->size, rwco->qiov,
|
rwco->qiov->size, rwco->qiov,
|
||||||
rwco->flags);
|
rwco->flags);
|
||||||
}
|
}
|
||||||
|
aio_wait_kick();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -838,17 +843,13 @@ static int bdrv_prwv_co(BdrvChild *child, int64_t offset,
|
||||||
static int bdrv_rw_co(BdrvChild *child, int64_t sector_num, uint8_t *buf,
|
static int bdrv_rw_co(BdrvChild *child, int64_t sector_num, uint8_t *buf,
|
||||||
int nb_sectors, bool is_write, BdrvRequestFlags flags)
|
int nb_sectors, bool is_write, BdrvRequestFlags flags)
|
||||||
{
|
{
|
||||||
QEMUIOVector qiov;
|
QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf,
|
||||||
struct iovec iov = {
|
nb_sectors * BDRV_SECTOR_SIZE);
|
||||||
.iov_base = (void *)buf,
|
|
||||||
.iov_len = nb_sectors * BDRV_SECTOR_SIZE,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) {
|
if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
|
||||||
return bdrv_prwv_co(child, sector_num << BDRV_SECTOR_BITS,
|
return bdrv_prwv_co(child, sector_num << BDRV_SECTOR_BITS,
|
||||||
&qiov, is_write, flags);
|
&qiov, is_write, flags);
|
||||||
}
|
}
|
||||||
|
@ -875,13 +876,8 @@ int bdrv_write(BdrvChild *child, int64_t sector_num,
|
||||||
int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset,
|
int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset,
|
||||||
int bytes, BdrvRequestFlags flags)
|
int bytes, BdrvRequestFlags flags)
|
||||||
{
|
{
|
||||||
QEMUIOVector qiov;
|
QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, bytes);
|
||||||
struct iovec iov = {
|
|
||||||
.iov_base = NULL,
|
|
||||||
.iov_len = bytes,
|
|
||||||
};
|
|
||||||
|
|
||||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
|
||||||
return bdrv_prwv_co(child, offset, &qiov, true,
|
return bdrv_prwv_co(child, offset, &qiov, true,
|
||||||
BDRV_REQ_ZERO_WRITE | flags);
|
BDRV_REQ_ZERO_WRITE | flags);
|
||||||
}
|
}
|
||||||
|
@ -913,8 +909,6 @@ int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags)
|
||||||
}
|
}
|
||||||
ret = bdrv_block_status(bs, offset, bytes, &bytes, NULL, NULL);
|
ret = bdrv_block_status(bs, offset, bytes, &bytes, NULL, NULL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_report("error getting block status at offset %" PRId64 ": %s",
|
|
||||||
offset, strerror(-ret));
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
if (ret & BDRV_BLOCK_ZERO) {
|
if (ret & BDRV_BLOCK_ZERO) {
|
||||||
|
@ -923,8 +917,6 @@ int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags)
|
||||||
}
|
}
|
||||||
ret = bdrv_pwrite_zeroes(child, offset, bytes, flags);
|
ret = bdrv_pwrite_zeroes(child, offset, bytes, flags);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_report("error writing zeroes at offset %" PRId64 ": %s",
|
|
||||||
offset, strerror(-ret));
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
offset += bytes;
|
offset += bytes;
|
||||||
|
@ -945,17 +937,12 @@ int bdrv_preadv(BdrvChild *child, int64_t offset, QEMUIOVector *qiov)
|
||||||
|
|
||||||
int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int bytes)
|
int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int bytes)
|
||||||
{
|
{
|
||||||
QEMUIOVector qiov;
|
QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);
|
||||||
struct iovec iov = {
|
|
||||||
.iov_base = (void *)buf,
|
|
||||||
.iov_len = bytes,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (bytes < 0) {
|
if (bytes < 0) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
|
||||||
return bdrv_preadv(child, offset, &qiov);
|
return bdrv_preadv(child, offset, &qiov);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -973,17 +960,12 @@ int bdrv_pwritev(BdrvChild *child, int64_t offset, QEMUIOVector *qiov)
|
||||||
|
|
||||||
int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, int bytes)
|
int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, int bytes)
|
||||||
{
|
{
|
||||||
QEMUIOVector qiov;
|
QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);
|
||||||
struct iovec iov = {
|
|
||||||
.iov_base = (void *) buf,
|
|
||||||
.iov_len = bytes,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (bytes < 0) {
|
if (bytes < 0) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
|
||||||
return bdrv_pwritev(child, offset, &qiov);
|
return bdrv_pwritev(child, offset, &qiov);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1033,6 +1015,7 @@ static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs,
|
||||||
unsigned int nb_sectors;
|
unsigned int nb_sectors;
|
||||||
|
|
||||||
assert(!(flags & ~BDRV_REQ_MASK));
|
assert(!(flags & ~BDRV_REQ_MASK));
|
||||||
|
assert(!(flags & BDRV_REQ_NO_FALLBACK));
|
||||||
|
|
||||||
if (!drv) {
|
if (!drv) {
|
||||||
return -ENOMEDIUM;
|
return -ENOMEDIUM;
|
||||||
|
@ -1079,6 +1062,7 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs,
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
assert(!(flags & ~BDRV_REQ_MASK));
|
assert(!(flags & ~BDRV_REQ_MASK));
|
||||||
|
assert(!(flags & BDRV_REQ_NO_FALLBACK));
|
||||||
|
|
||||||
if (!drv) {
|
if (!drv) {
|
||||||
return -ENOMEDIUM;
|
return -ENOMEDIUM;
|
||||||
|
@ -1160,7 +1144,6 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
|
||||||
void *bounce_buffer;
|
void *bounce_buffer;
|
||||||
|
|
||||||
BlockDriver *drv = bs->drv;
|
BlockDriver *drv = bs->drv;
|
||||||
struct iovec iov;
|
|
||||||
QEMUIOVector local_qiov;
|
QEMUIOVector local_qiov;
|
||||||
int64_t cluster_offset;
|
int64_t cluster_offset;
|
||||||
int64_t cluster_bytes;
|
int64_t cluster_bytes;
|
||||||
|
@ -1225,9 +1208,8 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
|
||||||
|
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
/* Must copy-on-read; use the bounce buffer */
|
/* Must copy-on-read; use the bounce buffer */
|
||||||
iov.iov_base = bounce_buffer;
|
pnum = MIN(pnum, MAX_BOUNCE_BUFFER);
|
||||||
iov.iov_len = pnum = MIN(pnum, MAX_BOUNCE_BUFFER);
|
qemu_iovec_init_buf(&local_qiov, bounce_buffer, pnum);
|
||||||
qemu_iovec_init_external(&local_qiov, &iov, 1);
|
|
||||||
|
|
||||||
ret = bdrv_driver_preadv(bs, cluster_offset, pnum,
|
ret = bdrv_driver_preadv(bs, cluster_offset, pnum,
|
||||||
&local_qiov, 0);
|
&local_qiov, 0);
|
||||||
|
@ -1472,7 +1454,7 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
|
||||||
{
|
{
|
||||||
BlockDriver *drv = bs->drv;
|
BlockDriver *drv = bs->drv;
|
||||||
QEMUIOVector qiov;
|
QEMUIOVector qiov;
|
||||||
struct iovec iov = {0};
|
void *buf = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
bool need_flush = false;
|
bool need_flush = false;
|
||||||
int head = 0;
|
int head = 0;
|
||||||
|
@ -1487,6 +1469,10 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
|
||||||
return -ENOMEDIUM;
|
return -ENOMEDIUM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((flags & ~bs->supported_zero_flags) & BDRV_REQ_NO_FALLBACK) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
assert(alignment % bs->bl.request_alignment == 0);
|
assert(alignment % bs->bl.request_alignment == 0);
|
||||||
head = offset % alignment;
|
head = offset % alignment;
|
||||||
tail = (offset + bytes) % alignment;
|
tail = (offset + bytes) % alignment;
|
||||||
|
@ -1530,7 +1516,7 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
|
||||||
assert(!bs->supported_zero_flags);
|
assert(!bs->supported_zero_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == -ENOTSUP) {
|
if (ret == -ENOTSUP && !(flags & BDRV_REQ_NO_FALLBACK)) {
|
||||||
/* Fall back to bounce buffer if write zeroes is unsupported */
|
/* Fall back to bounce buffer if write zeroes is unsupported */
|
||||||
BdrvRequestFlags write_flags = flags & ~BDRV_REQ_ZERO_WRITE;
|
BdrvRequestFlags write_flags = flags & ~BDRV_REQ_ZERO_WRITE;
|
||||||
|
|
||||||
|
@ -1542,16 +1528,14 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
|
||||||
need_flush = true;
|
need_flush = true;
|
||||||
}
|
}
|
||||||
num = MIN(num, max_transfer);
|
num = MIN(num, max_transfer);
|
||||||
iov.iov_len = num;
|
if (buf == NULL) {
|
||||||
if (iov.iov_base == NULL) {
|
buf = qemu_try_blockalign0(bs, num);
|
||||||
iov.iov_base = qemu_try_blockalign(bs, num);
|
if (buf == NULL) {
|
||||||
if (iov.iov_base == NULL) {
|
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
memset(iov.iov_base, 0, num);
|
|
||||||
}
|
}
|
||||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
qemu_iovec_init_buf(&qiov, buf, num);
|
||||||
|
|
||||||
ret = bdrv_driver_pwritev(bs, offset, num, &qiov, write_flags);
|
ret = bdrv_driver_pwritev(bs, offset, num, &qiov, write_flags);
|
||||||
|
|
||||||
|
@ -1559,8 +1543,8 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
|
||||||
* all future requests.
|
* all future requests.
|
||||||
*/
|
*/
|
||||||
if (num < max_transfer) {
|
if (num < max_transfer) {
|
||||||
qemu_vfree(iov.iov_base);
|
qemu_vfree(buf);
|
||||||
iov.iov_base = NULL;
|
buf = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1572,7 +1556,7 @@ fail:
|
||||||
if (ret == 0 && need_flush) {
|
if (ret == 0 && need_flush) {
|
||||||
ret = bdrv_co_flush(bs);
|
ret = bdrv_co_flush(bs);
|
||||||
}
|
}
|
||||||
qemu_vfree(iov.iov_base);
|
qemu_vfree(buf);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1758,7 +1742,6 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child,
|
||||||
BlockDriverState *bs = child->bs;
|
BlockDriverState *bs = child->bs;
|
||||||
uint8_t *buf = NULL;
|
uint8_t *buf = NULL;
|
||||||
QEMUIOVector local_qiov;
|
QEMUIOVector local_qiov;
|
||||||
struct iovec iov;
|
|
||||||
uint64_t align = bs->bl.request_alignment;
|
uint64_t align = bs->bl.request_alignment;
|
||||||
unsigned int head_padding_bytes, tail_padding_bytes;
|
unsigned int head_padding_bytes, tail_padding_bytes;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
@ -1770,11 +1753,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child,
|
||||||
assert(flags & BDRV_REQ_ZERO_WRITE);
|
assert(flags & BDRV_REQ_ZERO_WRITE);
|
||||||
if (head_padding_bytes || tail_padding_bytes) {
|
if (head_padding_bytes || tail_padding_bytes) {
|
||||||
buf = qemu_blockalign(bs, align);
|
buf = qemu_blockalign(bs, align);
|
||||||
iov = (struct iovec) {
|
qemu_iovec_init_buf(&local_qiov, buf, align);
|
||||||
.iov_base = buf,
|
|
||||||
.iov_len = align,
|
|
||||||
};
|
|
||||||
qemu_iovec_init_external(&local_qiov, &iov, 1);
|
|
||||||
}
|
}
|
||||||
if (head_padding_bytes) {
|
if (head_padding_bytes) {
|
||||||
uint64_t zero_bytes = MIN(bytes, align - head_padding_bytes);
|
uint64_t zero_bytes = MIN(bytes, align - head_padding_bytes);
|
||||||
|
@ -1880,17 +1859,12 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
|
||||||
|
|
||||||
if (offset & (align - 1)) {
|
if (offset & (align - 1)) {
|
||||||
QEMUIOVector head_qiov;
|
QEMUIOVector head_qiov;
|
||||||
struct iovec head_iov;
|
|
||||||
|
|
||||||
mark_request_serialising(&req, align);
|
mark_request_serialising(&req, align);
|
||||||
wait_serialising_requests(&req);
|
wait_serialising_requests(&req);
|
||||||
|
|
||||||
head_buf = qemu_blockalign(bs, align);
|
head_buf = qemu_blockalign(bs, align);
|
||||||
head_iov = (struct iovec) {
|
qemu_iovec_init_buf(&head_qiov, head_buf, align);
|
||||||
.iov_base = head_buf,
|
|
||||||
.iov_len = align,
|
|
||||||
};
|
|
||||||
qemu_iovec_init_external(&head_qiov, &head_iov, 1);
|
|
||||||
|
|
||||||
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD);
|
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD);
|
||||||
ret = bdrv_aligned_preadv(child, &req, offset & ~(align - 1), align,
|
ret = bdrv_aligned_preadv(child, &req, offset & ~(align - 1), align,
|
||||||
|
@ -1919,7 +1893,6 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
|
||||||
|
|
||||||
if ((offset + bytes) & (align - 1)) {
|
if ((offset + bytes) & (align - 1)) {
|
||||||
QEMUIOVector tail_qiov;
|
QEMUIOVector tail_qiov;
|
||||||
struct iovec tail_iov;
|
|
||||||
size_t tail_bytes;
|
size_t tail_bytes;
|
||||||
bool waited;
|
bool waited;
|
||||||
|
|
||||||
|
@ -1928,11 +1901,7 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
|
||||||
assert(!waited || !use_local_qiov);
|
assert(!waited || !use_local_qiov);
|
||||||
|
|
||||||
tail_buf = qemu_blockalign(bs, align);
|
tail_buf = qemu_blockalign(bs, align);
|
||||||
tail_iov = (struct iovec) {
|
qemu_iovec_init_buf(&tail_qiov, tail_buf, align);
|
||||||
.iov_base = tail_buf,
|
|
||||||
.iov_len = align,
|
|
||||||
};
|
|
||||||
qemu_iovec_init_external(&tail_qiov, &tail_iov, 1);
|
|
||||||
|
|
||||||
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL);
|
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL);
|
||||||
ret = bdrv_aligned_preadv(child, &req, (offset + bytes) & ~(align - 1),
|
ret = bdrv_aligned_preadv(child, &req, (offset + bytes) & ~(align - 1),
|
||||||
|
@ -2275,6 +2244,7 @@ static void coroutine_fn bdrv_block_status_above_co_entry(void *opaque)
|
||||||
data->offset, data->bytes,
|
data->offset, data->bytes,
|
||||||
data->pnum, data->map, data->file);
|
data->pnum, data->map, data->file);
|
||||||
data->done = true;
|
data->done = true;
|
||||||
|
aio_wait_kick();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2434,6 +2404,7 @@ static void coroutine_fn bdrv_co_rw_vmstate_entry(void *opaque)
|
||||||
{
|
{
|
||||||
BdrvVmstateCo *co = opaque;
|
BdrvVmstateCo *co = opaque;
|
||||||
co->ret = bdrv_co_rw_vmstate(co->bs, co->qiov, co->pos, co->is_read);
|
co->ret = bdrv_co_rw_vmstate(co->bs, co->qiov, co->pos, co->is_read);
|
||||||
|
aio_wait_kick();
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
|
@ -2461,15 +2432,9 @@ bdrv_rw_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos,
|
||||||
int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
|
int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
|
||||||
int64_t pos, int size)
|
int64_t pos, int size)
|
||||||
{
|
{
|
||||||
QEMUIOVector qiov;
|
QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, size);
|
||||||
struct iovec iov = {
|
|
||||||
.iov_base = (void *) buf,
|
|
||||||
.iov_len = size,
|
|
||||||
};
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
|
||||||
|
|
||||||
ret = bdrv_writev_vmstate(bs, &qiov, pos);
|
ret = bdrv_writev_vmstate(bs, &qiov, pos);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -2486,14 +2451,9 @@ int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos)
|
||||||
int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf,
|
int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf,
|
||||||
int64_t pos, int size)
|
int64_t pos, int size)
|
||||||
{
|
{
|
||||||
QEMUIOVector qiov;
|
QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, size);
|
||||||
struct iovec iov = {
|
|
||||||
.iov_base = buf,
|
|
||||||
.iov_len = size,
|
|
||||||
};
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
|
||||||
ret = bdrv_readv_vmstate(bs, &qiov, pos);
|
ret = bdrv_readv_vmstate(bs, &qiov, pos);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -2555,6 +2515,7 @@ static void coroutine_fn bdrv_flush_co_entry(void *opaque)
|
||||||
FlushCo *rwco = opaque;
|
FlushCo *rwco = opaque;
|
||||||
|
|
||||||
rwco->ret = bdrv_co_flush(rwco->bs);
|
rwco->ret = bdrv_co_flush(rwco->bs);
|
||||||
|
aio_wait_kick();
|
||||||
}
|
}
|
||||||
|
|
||||||
int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
|
int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
|
||||||
|
@ -2700,6 +2661,7 @@ static void coroutine_fn bdrv_pdiscard_co_entry(void *opaque)
|
||||||
DiscardCo *rwco = opaque;
|
DiscardCo *rwco = opaque;
|
||||||
|
|
||||||
rwco->ret = bdrv_co_pdiscard(rwco->child, rwco->offset, rwco->bytes);
|
rwco->ret = bdrv_co_pdiscard(rwco->child, rwco->offset, rwco->bytes);
|
||||||
|
aio_wait_kick();
|
||||||
}
|
}
|
||||||
|
|
||||||
int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int bytes)
|
int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int bytes)
|
||||||
|
@ -2993,6 +2955,10 @@ static int coroutine_fn bdrv_co_copy_range_internal(
|
||||||
BdrvTrackedRequest req;
|
BdrvTrackedRequest req;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/* TODO We can support BDRV_REQ_NO_FALLBACK here */
|
||||||
|
assert(!(read_flags & BDRV_REQ_NO_FALLBACK));
|
||||||
|
assert(!(write_flags & BDRV_REQ_NO_FALLBACK));
|
||||||
|
|
||||||
if (!dst || !dst->bs) {
|
if (!dst || !dst->bs) {
|
||||||
return -ENOMEDIUM;
|
return -ENOMEDIUM;
|
||||||
}
|
}
|
||||||
|
@ -3213,6 +3179,7 @@ static void coroutine_fn bdrv_truncate_co_entry(void *opaque)
|
||||||
TruncateCo *tco = opaque;
|
TruncateCo *tco = opaque;
|
||||||
tco->ret = bdrv_co_truncate(tco->child, tco->offset, tco->prealloc,
|
tco->ret = bdrv_co_truncate(tco->child, tco->offset, tco->prealloc,
|
||||||
tco->errp);
|
tco->errp);
|
||||||
|
aio_wait_kick();
|
||||||
}
|
}
|
||||||
|
|
||||||
int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
|
int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
|
||||||
|
@ -3232,7 +3199,7 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
|
||||||
bdrv_truncate_co_entry(&tco);
|
bdrv_truncate_co_entry(&tco);
|
||||||
} else {
|
} else {
|
||||||
co = qemu_coroutine_create(bdrv_truncate_co_entry, &tco);
|
co = qemu_coroutine_create(bdrv_truncate_co_entry, &tco);
|
||||||
qemu_coroutine_enter(co);
|
bdrv_coroutine_enter(child->bs, co);
|
||||||
BDRV_POLL_WHILE(child->bs, tco.ret == NOT_DONE);
|
BDRV_POLL_WHILE(child->bs, tco.ret == NOT_DONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,9 @@
|
||||||
/* Conflict between scsi/utils.h and libiscsi! :( */
|
/* Conflict between scsi/utils.h and libiscsi! :( */
|
||||||
#define SCSI_XFER_NONE ISCSI_XFER_NONE
|
#define SCSI_XFER_NONE ISCSI_XFER_NONE
|
||||||
#include <iscsi/iscsi.h>
|
#include <iscsi/iscsi.h>
|
||||||
|
#define inline __attribute__((gnu_inline)) /* required for libiscsi v1.9.0 */
|
||||||
#include <iscsi/scsi-lowlevel.h>
|
#include <iscsi/scsi-lowlevel.h>
|
||||||
|
#undef inline
|
||||||
#undef SCSI_XFER_NONE
|
#undef SCSI_XFER_NONE
|
||||||
QEMU_BUILD_BUG_ON((int)SCSI_XFER_NONE != (int)ISCSI_XFER_NONE);
|
QEMU_BUILD_BUG_ON((int)SCSI_XFER_NONE != (int)ISCSI_XFER_NONE);
|
||||||
|
|
||||||
|
@ -117,7 +119,6 @@ typedef struct IscsiAIOCB {
|
||||||
QEMUBH *bh;
|
QEMUBH *bh;
|
||||||
IscsiLun *iscsilun;
|
IscsiLun *iscsilun;
|
||||||
struct scsi_task *task;
|
struct scsi_task *task;
|
||||||
uint8_t *buf;
|
|
||||||
int status;
|
int status;
|
||||||
int64_t sector_num;
|
int64_t sector_num;
|
||||||
int nb_sectors;
|
int nb_sectors;
|
||||||
|
@ -125,6 +126,7 @@ typedef struct IscsiAIOCB {
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
sg_io_hdr_t *ioh;
|
sg_io_hdr_t *ioh;
|
||||||
#endif
|
#endif
|
||||||
|
bool cancelled;
|
||||||
} IscsiAIOCB;
|
} IscsiAIOCB;
|
||||||
|
|
||||||
/* libiscsi uses time_t so its enough to process events every second */
|
/* libiscsi uses time_t so its enough to process events every second */
|
||||||
|
@ -143,6 +145,8 @@ static const unsigned iscsi_retry_times[] = {8, 32, 128, 512, 2048, 8192, 32768}
|
||||||
* unallocated. */
|
* unallocated. */
|
||||||
#define ISCSI_CHECKALLOC_THRES 64
|
#define ISCSI_CHECKALLOC_THRES 64
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
|
||||||
static void
|
static void
|
||||||
iscsi_bh_cb(void *p)
|
iscsi_bh_cb(void *p)
|
||||||
{
|
{
|
||||||
|
@ -150,9 +154,6 @@ iscsi_bh_cb(void *p)
|
||||||
|
|
||||||
qemu_bh_delete(acb->bh);
|
qemu_bh_delete(acb->bh);
|
||||||
|
|
||||||
g_free(acb->buf);
|
|
||||||
acb->buf = NULL;
|
|
||||||
|
|
||||||
acb->common.cb(acb->common.opaque, acb->status);
|
acb->common.cb(acb->common.opaque, acb->status);
|
||||||
|
|
||||||
if (acb->task != NULL) {
|
if (acb->task != NULL) {
|
||||||
|
@ -173,6 +174,8 @@ iscsi_schedule_bh(IscsiAIOCB *acb)
|
||||||
qemu_bh_schedule(acb->bh);
|
qemu_bh_schedule(acb->bh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
static void iscsi_co_generic_bh_cb(void *opaque)
|
static void iscsi_co_generic_bh_cb(void *opaque)
|
||||||
{
|
{
|
||||||
struct IscsiTask *iTask = opaque;
|
struct IscsiTask *iTask = opaque;
|
||||||
|
@ -291,14 +294,22 @@ static void iscsi_co_init_iscsitask(IscsiLun *iscsilun, struct IscsiTask *iTask)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
|
||||||
|
/* Called (via iscsi_service) with QemuMutex held. */
|
||||||
static void
|
static void
|
||||||
iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data,
|
iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data,
|
||||||
void *private_data)
|
void *private_data)
|
||||||
{
|
{
|
||||||
IscsiAIOCB *acb = private_data;
|
IscsiAIOCB *acb = private_data;
|
||||||
|
|
||||||
acb->status = -ECANCELED;
|
/* If the command callback hasn't been called yet, drop the task */
|
||||||
iscsi_schedule_bh(acb);
|
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() */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -307,14 +318,25 @@ iscsi_aio_cancel(BlockAIOCB *blockacb)
|
||||||
IscsiAIOCB *acb = (IscsiAIOCB *)blockacb;
|
IscsiAIOCB *acb = (IscsiAIOCB *)blockacb;
|
||||||
IscsiLun *iscsilun = acb->iscsilun;
|
IscsiLun *iscsilun = acb->iscsilun;
|
||||||
|
|
||||||
if (acb->status != -EINPROGRESS) {
|
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);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* send a task mgmt call to the target to cancel the task on the target */
|
acb->cancelled = true;
|
||||||
iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task,
|
|
||||||
iscsi_abort_task_cb, acb);
|
|
||||||
|
|
||||||
|
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 */
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_mutex_unlock(&iscsilun->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const AIOCBInfo iscsi_aiocb_info = {
|
static const AIOCBInfo iscsi_aiocb_info = {
|
||||||
|
@ -322,6 +344,7 @@ static const AIOCBInfo iscsi_aiocb_info = {
|
||||||
.cancel_async = iscsi_aio_cancel,
|
.cancel_async = iscsi_aio_cancel,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
static void iscsi_process_read(void *arg);
|
static void iscsi_process_read(void *arg);
|
||||||
static void iscsi_process_write(void *arg);
|
static void iscsi_process_write(void *arg);
|
||||||
|
@ -348,6 +371,8 @@ static void iscsi_timed_check_events(void *opaque)
|
||||||
{
|
{
|
||||||
IscsiLun *iscsilun = opaque;
|
IscsiLun *iscsilun = opaque;
|
||||||
|
|
||||||
|
qemu_mutex_lock(&iscsilun->mutex);
|
||||||
|
|
||||||
/* check for timed out requests */
|
/* check for timed out requests */
|
||||||
iscsi_service(iscsilun->iscsi, 0);
|
iscsi_service(iscsilun->iscsi, 0);
|
||||||
|
|
||||||
|
@ -360,6 +385,8 @@ static void iscsi_timed_check_events(void *opaque)
|
||||||
* to return to service once this situation changes. */
|
* to return to service once this situation changes. */
|
||||||
iscsi_set_events(iscsilun);
|
iscsi_set_events(iscsilun);
|
||||||
|
|
||||||
|
qemu_mutex_unlock(&iscsilun->mutex);
|
||||||
|
|
||||||
timer_mod(iscsilun->event_timer,
|
timer_mod(iscsilun->event_timer,
|
||||||
qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL);
|
qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL);
|
||||||
}
|
}
|
||||||
|
@ -933,8 +960,13 @@ iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status,
|
||||||
{
|
{
|
||||||
IscsiAIOCB *acb = opaque;
|
IscsiAIOCB *acb = opaque;
|
||||||
|
|
||||||
g_free(acb->buf);
|
if (status == SCSI_STATUS_CANCELLED) {
|
||||||
acb->buf = NULL;
|
if (!acb->bh) {
|
||||||
|
acb->status = -ECANCELED;
|
||||||
|
iscsi_schedule_bh(acb);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
acb->status = 0;
|
acb->status = 0;
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
|
@ -1010,8 +1042,8 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
|
||||||
acb->iscsilun = iscsilun;
|
acb->iscsilun = iscsilun;
|
||||||
acb->bh = NULL;
|
acb->bh = NULL;
|
||||||
acb->status = -EINPROGRESS;
|
acb->status = -EINPROGRESS;
|
||||||
acb->buf = NULL;
|
|
||||||
acb->ioh = buf;
|
acb->ioh = buf;
|
||||||
|
acb->cancelled = false;
|
||||||
|
|
||||||
if (req != SG_IO) {
|
if (req != SG_IO) {
|
||||||
iscsi_ioctl_handle_emulated(acb, req, buf);
|
iscsi_ioctl_handle_emulated(acb, req, buf);
|
||||||
|
@ -1844,7 +1876,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
iscsi_set_timeout(iscsi, timeout);
|
iscsi_set_timeout(iscsi, timeout);
|
||||||
#else
|
#else
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
error_report("iSCSI: ignoring timeout value for libiscsi <1.15.0");
|
warn_report("iSCSI: ignoring timeout value for libiscsi <1.15.0");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1878,9 +1910,11 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
/* Check the write protect flag of the LUN if we want to write */
|
/* Check the write protect flag of the LUN if we want to write */
|
||||||
if (iscsilun->type == TYPE_DISK && (flags & BDRV_O_RDWR) &&
|
if (iscsilun->type == TYPE_DISK && (flags & BDRV_O_RDWR) &&
|
||||||
iscsilun->write_protected) {
|
iscsilun->write_protected) {
|
||||||
error_setg(errp, "Cannot open a write protected LUN as read-write");
|
ret = bdrv_apply_auto_read_only(bs, "LUN is write protected", errp);
|
||||||
ret = -EACCES;
|
if (ret < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
|
flags &= ~BDRV_O_RDWR;
|
||||||
}
|
}
|
||||||
|
|
||||||
iscsi_readcapacity_sync(iscsilun, &local_err);
|
iscsi_readcapacity_sync(iscsilun, &local_err);
|
||||||
|
@ -2421,6 +2455,20 @@ static QemuOptsList iscsi_create_opts = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const char *const iscsi_strong_runtime_opts[] = {
|
||||||
|
"transport",
|
||||||
|
"portal",
|
||||||
|
"target",
|
||||||
|
"user",
|
||||||
|
"password",
|
||||||
|
"password-secret",
|
||||||
|
"lun",
|
||||||
|
"initiator-name",
|
||||||
|
"header-digest",
|
||||||
|
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_iscsi = {
|
static BlockDriver bdrv_iscsi = {
|
||||||
.format_name = "iscsi",
|
.format_name = "iscsi",
|
||||||
.protocol_name = "iscsi",
|
.protocol_name = "iscsi",
|
||||||
|
@ -2455,6 +2503,8 @@ static BlockDriver bdrv_iscsi = {
|
||||||
|
|
||||||
.bdrv_detach_aio_context = iscsi_detach_aio_context,
|
.bdrv_detach_aio_context = iscsi_detach_aio_context,
|
||||||
.bdrv_attach_aio_context = iscsi_attach_aio_context,
|
.bdrv_attach_aio_context = iscsi_attach_aio_context,
|
||||||
|
|
||||||
|
.strong_runtime_opts = iscsi_strong_runtime_opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
#if LIBISCSI_API_VERSION >= (20160603)
|
#if LIBISCSI_API_VERSION >= (20160603)
|
||||||
|
@ -2492,6 +2542,8 @@ static BlockDriver bdrv_iser = {
|
||||||
|
|
||||||
.bdrv_detach_aio_context = iscsi_detach_aio_context,
|
.bdrv_detach_aio_context = iscsi_detach_aio_context,
|
||||||
.bdrv_attach_aio_context = iscsi_attach_aio_context,
|
.bdrv_attach_aio_context = iscsi_attach_aio_context,
|
||||||
|
|
||||||
|
.strong_runtime_opts = iscsi_strong_runtime_opts,
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -234,9 +234,9 @@ static void qemu_laio_process_completions(LinuxAioState *s)
|
||||||
|
|
||||||
static void qemu_laio_process_completions_and_submit(LinuxAioState *s)
|
static void qemu_laio_process_completions_and_submit(LinuxAioState *s)
|
||||||
{
|
{
|
||||||
|
aio_context_acquire(s->aio_context);
|
||||||
qemu_laio_process_completions(s);
|
qemu_laio_process_completions(s);
|
||||||
|
|
||||||
aio_context_acquire(s->aio_context);
|
|
||||||
if (!s->io_q.plugged && !QSIMPLEQ_EMPTY(&s->io_q.pending)) {
|
if (!s->io_q.plugged && !QSIMPLEQ_EMPTY(&s->io_q.pending)) {
|
||||||
ioq_submit(s);
|
ioq_submit(s);
|
||||||
}
|
}
|
||||||
|
@ -384,10 +384,10 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset,
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case QEMU_AIO_WRITE:
|
case QEMU_AIO_WRITE:
|
||||||
io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset);
|
io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset);
|
||||||
break;
|
break;
|
||||||
case QEMU_AIO_READ:
|
case QEMU_AIO_READ:
|
||||||
io_prep_preadv(iocbs, fd, qiov->iov, qiov->niov, offset);
|
io_prep_preadv(iocbs, fd, qiov->iov, qiov->niov, offset);
|
||||||
break;
|
break;
|
||||||
/* Currently Linux kernel does not support other operations */
|
/* Currently Linux kernel does not support other operations */
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "%s: invalid AIO request type 0x%x.\n",
|
fprintf(stderr, "%s: invalid AIO request type 0x%x.\n",
|
||||||
|
|
197
block/mirror.c
197
block/mirror.c
|
@ -72,13 +72,15 @@ typedef struct MirrorBlockJob {
|
||||||
unsigned long *in_flight_bitmap;
|
unsigned long *in_flight_bitmap;
|
||||||
int in_flight;
|
int in_flight;
|
||||||
int64_t bytes_in_flight;
|
int64_t bytes_in_flight;
|
||||||
QTAILQ_HEAD(MirrorOpList, MirrorOp) ops_in_flight;
|
QTAILQ_HEAD(, MirrorOp) ops_in_flight;
|
||||||
int ret;
|
int ret;
|
||||||
bool unmap;
|
bool unmap;
|
||||||
int target_cluster_size;
|
int target_cluster_size;
|
||||||
int max_iov;
|
int max_iov;
|
||||||
bool initial_zeroing_ongoing;
|
bool initial_zeroing_ongoing;
|
||||||
int in_active_write_counter;
|
int in_active_write_counter;
|
||||||
|
bool prepared;
|
||||||
|
bool in_drain;
|
||||||
} MirrorBlockJob;
|
} MirrorBlockJob;
|
||||||
|
|
||||||
typedef struct MirrorBDSOpaque {
|
typedef struct MirrorBDSOpaque {
|
||||||
|
@ -198,7 +200,6 @@ static void coroutine_fn mirror_write_complete(MirrorOp *op, int ret)
|
||||||
{
|
{
|
||||||
MirrorBlockJob *s = op->s;
|
MirrorBlockJob *s = op->s;
|
||||||
|
|
||||||
aio_context_acquire(blk_get_aio_context(s->common.blk));
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
BlockErrorAction action;
|
BlockErrorAction action;
|
||||||
|
|
||||||
|
@ -208,15 +209,14 @@ static void coroutine_fn mirror_write_complete(MirrorOp *op, int ret)
|
||||||
s->ret = ret;
|
s->ret = ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mirror_iteration_done(op, ret);
|
mirror_iteration_done(op, ret);
|
||||||
aio_context_release(blk_get_aio_context(s->common.blk));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void coroutine_fn mirror_read_complete(MirrorOp *op, int ret)
|
static void coroutine_fn mirror_read_complete(MirrorOp *op, int ret)
|
||||||
{
|
{
|
||||||
MirrorBlockJob *s = op->s;
|
MirrorBlockJob *s = op->s;
|
||||||
|
|
||||||
aio_context_acquire(blk_get_aio_context(s->common.blk));
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
BlockErrorAction action;
|
BlockErrorAction action;
|
||||||
|
|
||||||
|
@ -227,12 +227,11 @@ static void coroutine_fn mirror_read_complete(MirrorOp *op, int ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
mirror_iteration_done(op, ret);
|
mirror_iteration_done(op, ret);
|
||||||
} else {
|
return;
|
||||||
ret = blk_co_pwritev(s->target, op->offset,
|
|
||||||
op->qiov.size, &op->qiov, 0);
|
|
||||||
mirror_write_complete(op, ret);
|
|
||||||
}
|
}
|
||||||
aio_context_release(blk_get_aio_context(s->common.blk));
|
|
||||||
|
ret = blk_co_pwritev(s->target, op->offset, op->qiov.size, &op->qiov, 0);
|
||||||
|
mirror_write_complete(op, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clip bytes relative to offset to not exceed end-of-file */
|
/* Clip bytes relative to offset to not exceed end-of-file */
|
||||||
|
@ -279,7 +278,8 @@ static int mirror_cow_align(MirrorBlockJob *s, int64_t *offset,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void mirror_wait_for_any_operation(MirrorBlockJob *s, bool active)
|
static inline void coroutine_fn
|
||||||
|
mirror_wait_for_any_operation(MirrorBlockJob *s, bool active)
|
||||||
{
|
{
|
||||||
MirrorOp *op;
|
MirrorOp *op;
|
||||||
|
|
||||||
|
@ -297,7 +297,8 @@ static inline void mirror_wait_for_any_operation(MirrorBlockJob *s, bool active)
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s)
|
static inline void coroutine_fn
|
||||||
|
mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s)
|
||||||
{
|
{
|
||||||
/* Only non-active operations use up in-flight slots */
|
/* Only non-active operations use up in-flight slots */
|
||||||
mirror_wait_for_any_operation(s, false);
|
mirror_wait_for_any_operation(s, false);
|
||||||
|
@ -600,33 +601,44 @@ static void mirror_free_init(MirrorBlockJob *s)
|
||||||
* mirror_resume() because mirror_run() will begin iterating again
|
* mirror_resume() because mirror_run() will begin iterating again
|
||||||
* when the job is resumed.
|
* when the job is resumed.
|
||||||
*/
|
*/
|
||||||
static void mirror_wait_for_all_io(MirrorBlockJob *s)
|
static void coroutine_fn mirror_wait_for_all_io(MirrorBlockJob *s)
|
||||||
{
|
{
|
||||||
while (s->in_flight > 0) {
|
while (s->in_flight > 0) {
|
||||||
mirror_wait_for_free_in_flight_slot(s);
|
mirror_wait_for_free_in_flight_slot(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
/**
|
||||||
int ret;
|
* mirror_exit_common: handle both abort() and prepare() cases.
|
||||||
} MirrorExitData;
|
* for .prepare, returns 0 on success and -errno on failure.
|
||||||
|
* for .abort cases, denoted by abort = true, MUST return 0.
|
||||||
static void mirror_exit(Job *job, void *opaque)
|
*/
|
||||||
|
static int mirror_exit_common(Job *job)
|
||||||
{
|
{
|
||||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
|
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
|
||||||
BlockJob *bjob = &s->common;
|
BlockJob *bjob = &s->common;
|
||||||
MirrorExitData *data = opaque;
|
|
||||||
MirrorBDSOpaque *bs_opaque = s->mirror_top_bs->opaque;
|
MirrorBDSOpaque *bs_opaque = s->mirror_top_bs->opaque;
|
||||||
AioContext *replace_aio_context = NULL;
|
AioContext *replace_aio_context = NULL;
|
||||||
BlockDriverState *src = s->mirror_top_bs->backing->bs;
|
BlockDriverState *src = s->mirror_top_bs->backing->bs;
|
||||||
BlockDriverState *target_bs = blk_bs(s->target);
|
BlockDriverState *target_bs = blk_bs(s->target);
|
||||||
BlockDriverState *mirror_top_bs = s->mirror_top_bs;
|
BlockDriverState *mirror_top_bs = s->mirror_top_bs;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
bool abort = job->ret < 0;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (s->prepared) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
s->prepared = true;
|
||||||
|
|
||||||
|
if (bdrv_chain_contains(src, target_bs)) {
|
||||||
|
bdrv_unfreeze_backing_chain(mirror_top_bs, target_bs);
|
||||||
|
}
|
||||||
|
|
||||||
bdrv_release_dirty_bitmap(src, s->dirty_bitmap);
|
bdrv_release_dirty_bitmap(src, s->dirty_bitmap);
|
||||||
|
|
||||||
/* Make sure that the source BDS doesn't go away before we called
|
/* Make sure that the source BDS doesn't go away during bdrv_replace_node,
|
||||||
* job_completed(). */
|
* before we can call bdrv_drained_end */
|
||||||
bdrv_ref(src);
|
bdrv_ref(src);
|
||||||
bdrv_ref(mirror_top_bs);
|
bdrv_ref(mirror_top_bs);
|
||||||
bdrv_ref(target_bs);
|
bdrv_ref(target_bs);
|
||||||
|
@ -646,13 +658,13 @@ static void mirror_exit(Job *job, void *opaque)
|
||||||
* required before it could become a backing file of target_bs. */
|
* required before it could become a backing file of target_bs. */
|
||||||
bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL,
|
bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL,
|
||||||
&error_abort);
|
&error_abort);
|
||||||
if (s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) {
|
if (!abort && s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) {
|
||||||
BlockDriverState *backing = s->is_none_mode ? src : s->base;
|
BlockDriverState *backing = s->is_none_mode ? src : s->base;
|
||||||
if (backing_bs(target_bs) != backing) {
|
if (backing_bs(target_bs) != backing) {
|
||||||
bdrv_set_backing_hd(target_bs, backing, &local_err);
|
bdrv_set_backing_hd(target_bs, backing, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_report_err(local_err);
|
error_report_err(local_err);
|
||||||
data->ret = -EPERM;
|
ret = -EPERM;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -662,24 +674,23 @@ static void mirror_exit(Job *job, void *opaque)
|
||||||
aio_context_acquire(replace_aio_context);
|
aio_context_acquire(replace_aio_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->should_complete && data->ret == 0) {
|
if (s->should_complete && !abort) {
|
||||||
BlockDriverState *to_replace = src;
|
BlockDriverState *to_replace = s->to_replace ?: src;
|
||||||
if (s->to_replace) {
|
bool ro = bdrv_is_read_only(to_replace);
|
||||||
to_replace = s->to_replace;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bdrv_get_flags(target_bs) != bdrv_get_flags(to_replace)) {
|
if (ro != bdrv_is_read_only(target_bs)) {
|
||||||
bdrv_reopen(target_bs, bdrv_get_flags(to_replace), NULL);
|
bdrv_reopen_set_read_only(target_bs, ro, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The mirror job has no requests in flight any more, but we need to
|
/* The mirror job has no requests in flight any more, but we need to
|
||||||
* drain potential other users of the BDS before changing the graph. */
|
* drain potential other users of the BDS before changing the graph. */
|
||||||
|
assert(s->in_drain);
|
||||||
bdrv_drained_begin(target_bs);
|
bdrv_drained_begin(target_bs);
|
||||||
bdrv_replace_node(to_replace, target_bs, &local_err);
|
bdrv_replace_node(to_replace, target_bs, &local_err);
|
||||||
bdrv_drained_end(target_bs);
|
bdrv_drained_end(target_bs);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_report_err(local_err);
|
error_report_err(local_err);
|
||||||
data->ret = -EPERM;
|
ret = -EPERM;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (s->to_replace) {
|
if (s->to_replace) {
|
||||||
|
@ -710,15 +721,27 @@ static void mirror_exit(Job *job, void *opaque)
|
||||||
blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort);
|
blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort);
|
||||||
|
|
||||||
bs_opaque->job = NULL;
|
bs_opaque->job = NULL;
|
||||||
job_completed(job, data->ret, NULL);
|
|
||||||
|
|
||||||
g_free(data);
|
|
||||||
bdrv_drained_end(src);
|
bdrv_drained_end(src);
|
||||||
|
s->in_drain = false;
|
||||||
bdrv_unref(mirror_top_bs);
|
bdrv_unref(mirror_top_bs);
|
||||||
bdrv_unref(src);
|
bdrv_unref(src);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mirror_throttle(MirrorBlockJob *s)
|
static int mirror_prepare(Job *job)
|
||||||
|
{
|
||||||
|
return mirror_exit_common(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mirror_abort(Job *job)
|
||||||
|
{
|
||||||
|
int ret = mirror_exit_common(job);
|
||||||
|
assert(ret == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void coroutine_fn mirror_throttle(MirrorBlockJob *s)
|
||||||
{
|
{
|
||||||
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
|
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
|
||||||
|
|
||||||
|
@ -812,10 +835,9 @@ static int mirror_flush(MirrorBlockJob *s)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void coroutine_fn mirror_run(void *opaque)
|
static int coroutine_fn mirror_run(Job *job, Error **errp)
|
||||||
{
|
{
|
||||||
MirrorBlockJob *s = opaque;
|
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
|
||||||
MirrorExitData *data;
|
|
||||||
BlockDriverState *bs = s->mirror_top_bs->backing->bs;
|
BlockDriverState *bs = s->mirror_top_bs->backing->bs;
|
||||||
BlockDriverState *target_bs = blk_bs(s->target);
|
BlockDriverState *target_bs = blk_bs(s->target);
|
||||||
bool need_drain = true;
|
bool need_drain = true;
|
||||||
|
@ -985,10 +1007,12 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||||
*/
|
*/
|
||||||
trace_mirror_before_drain(s, cnt);
|
trace_mirror_before_drain(s, cnt);
|
||||||
|
|
||||||
|
s->in_drain = true;
|
||||||
bdrv_drained_begin(bs);
|
bdrv_drained_begin(bs);
|
||||||
cnt = bdrv_get_dirty_count(s->dirty_bitmap);
|
cnt = bdrv_get_dirty_count(s->dirty_bitmap);
|
||||||
if (cnt > 0 || mirror_flush(s) < 0) {
|
if (cnt > 0 || mirror_flush(s) < 0) {
|
||||||
bdrv_drained_end(bs);
|
bdrv_drained_end(bs);
|
||||||
|
s->in_drain = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1035,13 +1059,12 @@ immediate_exit:
|
||||||
g_free(s->in_flight_bitmap);
|
g_free(s->in_flight_bitmap);
|
||||||
bdrv_dirty_iter_free(s->dbi);
|
bdrv_dirty_iter_free(s->dbi);
|
||||||
|
|
||||||
data = g_malloc(sizeof(*data));
|
|
||||||
data->ret = ret;
|
|
||||||
|
|
||||||
if (need_drain) {
|
if (need_drain) {
|
||||||
|
s->in_drain = true;
|
||||||
bdrv_drained_begin(bs);
|
bdrv_drained_begin(bs);
|
||||||
}
|
}
|
||||||
job_defer_to_main_loop(&s->common.job, mirror_exit, data);
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mirror_complete(Job *job, Error **errp)
|
static void mirror_complete(Job *job, Error **errp)
|
||||||
|
@ -1096,7 +1119,7 @@ static void mirror_complete(Job *job, Error **errp)
|
||||||
job_enter(job);
|
job_enter(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mirror_pause(Job *job)
|
static void coroutine_fn mirror_pause(Job *job)
|
||||||
{
|
{
|
||||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
|
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
|
||||||
|
|
||||||
|
@ -1106,6 +1129,16 @@ static void mirror_pause(Job *job)
|
||||||
static bool mirror_drained_poll(BlockJob *job)
|
static bool mirror_drained_poll(BlockJob *job)
|
||||||
{
|
{
|
||||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
|
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
|
||||||
|
|
||||||
|
/* If the job isn't paused nor cancelled, we can't be sure that it won't
|
||||||
|
* issue more requests. We make an exception if we've reached this point
|
||||||
|
* from one of our own drain sections, to avoid a deadlock waiting for
|
||||||
|
* ourselves.
|
||||||
|
*/
|
||||||
|
if (!s->common.job.paused && !s->common.job.cancelled && !s->in_drain) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return !!s->in_flight;
|
return !!s->in_flight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1138,7 +1171,9 @@ static const BlockJobDriver mirror_job_driver = {
|
||||||
.free = block_job_free,
|
.free = block_job_free,
|
||||||
.user_resume = block_job_user_resume,
|
.user_resume = block_job_user_resume,
|
||||||
.drain = block_job_drain,
|
.drain = block_job_drain,
|
||||||
.start = mirror_run,
|
.run = mirror_run,
|
||||||
|
.prepare = mirror_prepare,
|
||||||
|
.abort = mirror_abort,
|
||||||
.pause = mirror_pause,
|
.pause = mirror_pause,
|
||||||
.complete = mirror_complete,
|
.complete = mirror_complete,
|
||||||
},
|
},
|
||||||
|
@ -1154,7 +1189,9 @@ static const BlockJobDriver commit_active_job_driver = {
|
||||||
.free = block_job_free,
|
.free = block_job_free,
|
||||||
.user_resume = block_job_user_resume,
|
.user_resume = block_job_user_resume,
|
||||||
.drain = block_job_drain,
|
.drain = block_job_drain,
|
||||||
.start = mirror_run,
|
.run = mirror_run,
|
||||||
|
.prepare = mirror_prepare,
|
||||||
|
.abort = mirror_abort,
|
||||||
.pause = mirror_pause,
|
.pause = mirror_pause,
|
||||||
.complete = mirror_complete,
|
.complete = mirror_complete,
|
||||||
},
|
},
|
||||||
|
@ -1163,29 +1200,28 @@ static const BlockJobDriver commit_active_job_driver = {
|
||||||
.drain = mirror_drain,
|
.drain = mirror_drain,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void do_sync_target_write(MirrorBlockJob *job, MirrorMethod method,
|
static void coroutine_fn
|
||||||
uint64_t offset, uint64_t bytes,
|
do_sync_target_write(MirrorBlockJob *job, MirrorMethod method,
|
||||||
QEMUIOVector *qiov, int flags)
|
uint64_t offset, uint64_t bytes,
|
||||||
|
QEMUIOVector *qiov, int flags)
|
||||||
{
|
{
|
||||||
BdrvDirtyBitmapIter *iter;
|
|
||||||
QEMUIOVector target_qiov;
|
QEMUIOVector target_qiov;
|
||||||
uint64_t dirty_offset;
|
uint64_t dirty_offset = offset;
|
||||||
int dirty_bytes;
|
uint64_t dirty_bytes;
|
||||||
|
|
||||||
if (qiov) {
|
if (qiov) {
|
||||||
qemu_iovec_init(&target_qiov, qiov->niov);
|
qemu_iovec_init(&target_qiov, qiov->niov);
|
||||||
}
|
}
|
||||||
|
|
||||||
iter = bdrv_dirty_iter_new(job->dirty_bitmap);
|
|
||||||
bdrv_set_dirty_iter(iter, offset);
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
bool valid_area;
|
bool valid_area;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
bdrv_dirty_bitmap_lock(job->dirty_bitmap);
|
bdrv_dirty_bitmap_lock(job->dirty_bitmap);
|
||||||
valid_area = bdrv_dirty_iter_next_area(iter, offset + bytes,
|
dirty_bytes = MIN(offset + bytes - dirty_offset, INT_MAX);
|
||||||
&dirty_offset, &dirty_bytes);
|
valid_area = bdrv_dirty_bitmap_next_dirty_area(job->dirty_bitmap,
|
||||||
|
&dirty_offset,
|
||||||
|
&dirty_bytes);
|
||||||
if (!valid_area) {
|
if (!valid_area) {
|
||||||
bdrv_dirty_bitmap_unlock(job->dirty_bitmap);
|
bdrv_dirty_bitmap_unlock(job->dirty_bitmap);
|
||||||
break;
|
break;
|
||||||
|
@ -1241,9 +1277,10 @@ static void do_sync_target_write(MirrorBlockJob *job, MirrorMethod method,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dirty_offset += dirty_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
bdrv_dirty_iter_free(iter);
|
|
||||||
if (qiov) {
|
if (qiov) {
|
||||||
qemu_iovec_destroy(&target_qiov);
|
qemu_iovec_destroy(&target_qiov);
|
||||||
}
|
}
|
||||||
|
@ -1414,22 +1451,17 @@ static int coroutine_fn bdrv_mirror_top_pdiscard(BlockDriverState *bs,
|
||||||
NULL, 0);
|
NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bdrv_mirror_top_refresh_filename(BlockDriverState *bs, QDict *opts)
|
static void bdrv_mirror_top_refresh_filename(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
if (bs->backing == NULL) {
|
if (bs->backing == NULL) {
|
||||||
/* we can be here after failed bdrv_attach_child in
|
/* we can be here after failed bdrv_attach_child in
|
||||||
* bdrv_set_backing_hd */
|
* bdrv_set_backing_hd */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bdrv_refresh_filename(bs->backing->bs);
|
|
||||||
pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
|
pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
|
||||||
bs->backing->bs->filename);
|
bs->backing->bs->filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bdrv_mirror_top_close(BlockDriverState *bs)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bdrv_mirror_top_child_perm(BlockDriverState *bs, BdrvChild *c,
|
static void bdrv_mirror_top_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||||
const BdrvChildRole *role,
|
const BdrvChildRole *role,
|
||||||
BlockReopenQueue *reopen_queue,
|
BlockReopenQueue *reopen_queue,
|
||||||
|
@ -1456,7 +1488,6 @@ static BlockDriver bdrv_mirror_top = {
|
||||||
.bdrv_co_flush = bdrv_mirror_top_flush,
|
.bdrv_co_flush = bdrv_mirror_top_flush,
|
||||||
.bdrv_co_block_status = bdrv_co_block_status_from_backing,
|
.bdrv_co_block_status = bdrv_co_block_status_from_backing,
|
||||||
.bdrv_refresh_filename = bdrv_mirror_top_refresh_filename,
|
.bdrv_refresh_filename = bdrv_mirror_top_refresh_filename,
|
||||||
.bdrv_close = bdrv_mirror_top_close,
|
|
||||||
.bdrv_child_perm = bdrv_mirror_top_child_perm,
|
.bdrv_child_perm = bdrv_mirror_top_child_perm,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1499,6 +1530,11 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
|
||||||
buf_size = DEFAULT_MIRROR_BUF_SIZE;
|
buf_size = DEFAULT_MIRROR_BUF_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bs == target) {
|
||||||
|
error_setg(errp, "Can't mirror node into itself");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* In the case of active commit, add dummy driver to provide consistent
|
/* In the case of active commit, add dummy driver to provide consistent
|
||||||
* reads on the top, while disabling it in the intermediate nodes, and make
|
* reads on the top, while disabling it in the intermediate nodes, and make
|
||||||
* the backing chain writable. */
|
* the backing chain writable. */
|
||||||
|
@ -1512,7 +1548,8 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
|
||||||
}
|
}
|
||||||
mirror_top_bs->total_sectors = bs->total_sectors;
|
mirror_top_bs->total_sectors = bs->total_sectors;
|
||||||
mirror_top_bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
|
mirror_top_bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
|
||||||
mirror_top_bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED;
|
mirror_top_bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||||
|
BDRV_REQ_NO_FALLBACK;
|
||||||
bs_opaque = g_new0(MirrorBDSOpaque, 1);
|
bs_opaque = g_new0(MirrorBDSOpaque, 1);
|
||||||
mirror_top_bs->opaque = bs_opaque;
|
mirror_top_bs->opaque = bs_opaque;
|
||||||
bdrv_set_aio_context(mirror_top_bs, bdrv_get_aio_context(bs));
|
bdrv_set_aio_context(mirror_top_bs, bdrv_get_aio_context(bs));
|
||||||
|
@ -1595,6 +1632,14 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = block_job_add_bdrv(&s->common, "source", bs, 0,
|
||||||
|
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE |
|
||||||
|
BLK_PERM_CONSISTENT_READ,
|
||||||
|
errp);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
/* Required permissions are already taken with blk_new() */
|
/* Required permissions are already taken with blk_new() */
|
||||||
block_job_add_bdrv(&s->common, "target", target, 0, BLK_PERM_ALL,
|
block_job_add_bdrv(&s->common, "target", target, 0, BLK_PERM_ALL,
|
||||||
&error_abort);
|
&error_abort);
|
||||||
|
@ -1615,6 +1660,10 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bdrv_freeze_backing_chain(mirror_top_bs, target, errp) < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QTAILQ_INIT(&s->ops_in_flight);
|
QTAILQ_INIT(&s->ops_in_flight);
|
||||||
|
@ -1632,6 +1681,9 @@ fail:
|
||||||
g_free(s->replaces);
|
g_free(s->replaces);
|
||||||
blk_unref(s->target);
|
blk_unref(s->target);
|
||||||
bs_opaque->job = NULL;
|
bs_opaque->job = NULL;
|
||||||
|
if (s->dirty_bitmap) {
|
||||||
|
bdrv_release_dirty_bitmap(bs, s->dirty_bitmap);
|
||||||
|
}
|
||||||
job_early_fail(&s->common.job);
|
job_early_fail(&s->common.job);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1644,7 +1696,8 @@ fail:
|
||||||
|
|
||||||
void mirror_start(const char *job_id, BlockDriverState *bs,
|
void mirror_start(const char *job_id, BlockDriverState *bs,
|
||||||
BlockDriverState *target, const char *replaces,
|
BlockDriverState *target, const char *replaces,
|
||||||
int64_t speed, uint32_t granularity, int64_t buf_size,
|
int creation_flags, int64_t speed,
|
||||||
|
uint32_t granularity, int64_t buf_size,
|
||||||
MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
|
MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
|
||||||
BlockdevOnError on_source_error,
|
BlockdevOnError on_source_error,
|
||||||
BlockdevOnError on_target_error,
|
BlockdevOnError on_target_error,
|
||||||
|
@ -1660,7 +1713,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
|
||||||
}
|
}
|
||||||
is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
|
is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
|
||||||
base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL;
|
base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL;
|
||||||
mirror_start_job(job_id, bs, JOB_DEFAULT, target, replaces,
|
mirror_start_job(job_id, bs, creation_flags, target, replaces,
|
||||||
speed, granularity, buf_size, backing_mode,
|
speed, granularity, buf_size, backing_mode,
|
||||||
on_source_error, on_target_error, unmap, NULL, NULL,
|
on_source_error, on_target_error, unmap, NULL, NULL,
|
||||||
&mirror_job_driver, is_none_mode, base, false,
|
&mirror_job_driver, is_none_mode, base, false,
|
||||||
|
@ -1674,13 +1727,15 @@ void commit_active_start(const char *job_id, BlockDriverState *bs,
|
||||||
BlockCompletionFunc *cb, void *opaque,
|
BlockCompletionFunc *cb, void *opaque,
|
||||||
bool auto_complete, Error **errp)
|
bool auto_complete, Error **errp)
|
||||||
{
|
{
|
||||||
int orig_base_flags;
|
bool base_read_only;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
orig_base_flags = bdrv_get_flags(base);
|
base_read_only = bdrv_is_read_only(base);
|
||||||
|
|
||||||
if (bdrv_reopen(base, bs->open_flags, errp)) {
|
if (base_read_only) {
|
||||||
return;
|
if (bdrv_reopen_set_read_only(base, false, errp) < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mirror_start_job(job_id, bs, creation_flags, base, NULL, speed, 0, 0,
|
mirror_start_job(job_id, bs, creation_flags, base, NULL, speed, 0, 0,
|
||||||
|
@ -1699,6 +1754,8 @@ void commit_active_start(const char *job_id, BlockDriverState *bs,
|
||||||
error_restore_flags:
|
error_restore_flags:
|
||||||
/* ignore error and errp for bdrv_reopen, because we want to propagate
|
/* ignore error and errp for bdrv_reopen, because we want to propagate
|
||||||
* the original error */
|
* the original error */
|
||||||
bdrv_reopen(base, orig_base_flags, NULL);
|
if (base_read_only) {
|
||||||
|
bdrv_reopen_set_read_only(base, true, NULL);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
|
||||||
|
#include "trace.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "nbd-client.h"
|
#include "nbd-client.h"
|
||||||
|
|
||||||
|
@ -51,15 +53,13 @@ static void nbd_teardown_connection(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
NBDClientSession *client = nbd_get_client_session(bs);
|
NBDClientSession *client = nbd_get_client_session(bs);
|
||||||
|
|
||||||
if (!client->ioc) { /* Already closed */
|
assert(client->ioc);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* finish any pending coroutines */
|
/* finish any pending coroutines */
|
||||||
qio_channel_shutdown(client->ioc,
|
qio_channel_shutdown(client->ioc,
|
||||||
QIO_CHANNEL_SHUTDOWN_BOTH,
|
QIO_CHANNEL_SHUTDOWN_BOTH,
|
||||||
NULL);
|
NULL);
|
||||||
BDRV_POLL_WHILE(bs, client->read_reply_co);
|
BDRV_POLL_WHILE(bs, client->connection_co);
|
||||||
|
|
||||||
nbd_client_detach_aio_context(bs);
|
nbd_client_detach_aio_context(bs);
|
||||||
object_unref(OBJECT(client->sioc));
|
object_unref(OBJECT(client->sioc));
|
||||||
|
@ -68,7 +68,7 @@ static void nbd_teardown_connection(BlockDriverState *bs)
|
||||||
client->ioc = NULL;
|
client->ioc = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static coroutine_fn void nbd_read_reply_entry(void *opaque)
|
static coroutine_fn void nbd_connection_entry(void *opaque)
|
||||||
{
|
{
|
||||||
NBDClientSession *s = opaque;
|
NBDClientSession *s = opaque;
|
||||||
uint64_t i;
|
uint64_t i;
|
||||||
|
@ -76,10 +76,21 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
while (!s->quit) {
|
while (!s->quit) {
|
||||||
|
/*
|
||||||
|
* The NBD client can only really be considered idle when it has
|
||||||
|
* yielded from qio_channel_readv_all_eof(), waiting for data. This is
|
||||||
|
* the point where the additional scheduled coroutine entry happens
|
||||||
|
* after nbd_client_attach_aio_context().
|
||||||
|
*
|
||||||
|
* Therefore we keep an additional in_flight reference all the time and
|
||||||
|
* only drop it temporarily here.
|
||||||
|
*/
|
||||||
assert(s->reply.handle == 0);
|
assert(s->reply.handle == 0);
|
||||||
ret = nbd_receive_reply(s->ioc, &s->reply, &local_err);
|
ret = nbd_receive_reply(s->bs, s->ioc, &s->reply, &local_err);
|
||||||
|
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_report_err(local_err);
|
trace_nbd_read_reply_entry_fail(ret, error_get_pretty(local_err));
|
||||||
|
error_free(local_err);
|
||||||
}
|
}
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
break;
|
break;
|
||||||
|
@ -99,14 +110,14 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We're woken up again by the request itself. Note that there
|
/* We're woken up again by the request itself. Note that there
|
||||||
* is no race between yielding and reentering read_reply_co. This
|
* is no race between yielding and reentering connection_co. This
|
||||||
* is because:
|
* is because:
|
||||||
*
|
*
|
||||||
* - if the request runs on the same AioContext, it is only
|
* - if the request runs on the same AioContext, it is only
|
||||||
* entered after we yield
|
* entered after we yield
|
||||||
*
|
*
|
||||||
* - if the request runs on a different AioContext, reentering
|
* - if the request runs on a different AioContext, reentering
|
||||||
* read_reply_co happens through a bottom half, which can only
|
* connection_co happens through a bottom half, which can only
|
||||||
* run after we yield.
|
* run after we yield.
|
||||||
*/
|
*/
|
||||||
aio_co_wake(s->requests[i].coroutine);
|
aio_co_wake(s->requests[i].coroutine);
|
||||||
|
@ -115,7 +126,10 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)
|
||||||
|
|
||||||
s->quit = true;
|
s->quit = true;
|
||||||
nbd_recv_coroutines_wake_all(s);
|
nbd_recv_coroutines_wake_all(s);
|
||||||
s->read_reply_co = NULL;
|
bdrv_dec_in_flight(s->bs);
|
||||||
|
|
||||||
|
s->connection_co = NULL;
|
||||||
|
aio_wait_kick();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nbd_co_send_request(BlockDriverState *bs,
|
static int nbd_co_send_request(BlockDriverState *bs,
|
||||||
|
@ -150,10 +164,7 @@ static int nbd_co_send_request(BlockDriverState *bs,
|
||||||
rc = -EIO;
|
rc = -EIO;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
if (!s->ioc) {
|
assert(s->ioc);
|
||||||
rc = -EPIPE;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qiov) {
|
if (qiov) {
|
||||||
qio_channel_set_cork(s->ioc, true);
|
qio_channel_set_cork(s->ioc, true);
|
||||||
|
@ -200,7 +211,8 @@ static inline uint64_t payload_advance64(uint8_t **payload)
|
||||||
return ldq_be_p(*payload - 8);
|
return ldq_be_p(*payload - 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk,
|
static int nbd_parse_offset_hole_payload(NBDClientSession *client,
|
||||||
|
NBDStructuredReplyChunk *chunk,
|
||||||
uint8_t *payload, uint64_t orig_offset,
|
uint8_t *payload, uint64_t orig_offset,
|
||||||
QEMUIOVector *qiov, Error **errp)
|
QEMUIOVector *qiov, Error **errp)
|
||||||
{
|
{
|
||||||
|
@ -222,6 +234,10 @@ static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk,
|
||||||
" region");
|
" region");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
if (client->info.min_block &&
|
||||||
|
!QEMU_IS_ALIGNED(hole_size, client->info.min_block)) {
|
||||||
|
trace_nbd_structured_read_compliance("hole");
|
||||||
|
}
|
||||||
|
|
||||||
qemu_iovec_memset(qiov, offset - orig_offset, 0, hole_size);
|
qemu_iovec_memset(qiov, offset - orig_offset, 0, hole_size);
|
||||||
|
|
||||||
|
@ -229,8 +245,8 @@ static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* nbd_parse_blockstatus_payload
|
/* nbd_parse_blockstatus_payload
|
||||||
* support only one extent in reply and only for
|
* Based on our request, we expect only one extent in reply, for the
|
||||||
* base:allocation context
|
* base:allocation context.
|
||||||
*/
|
*/
|
||||||
static int nbd_parse_blockstatus_payload(NBDClientSession *client,
|
static int nbd_parse_blockstatus_payload(NBDClientSession *client,
|
||||||
NBDStructuredReplyChunk *chunk,
|
NBDStructuredReplyChunk *chunk,
|
||||||
|
@ -239,36 +255,69 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client,
|
||||||
{
|
{
|
||||||
uint32_t context_id;
|
uint32_t context_id;
|
||||||
|
|
||||||
if (chunk->length != sizeof(context_id) + sizeof(*extent)) {
|
/* The server succeeded, so it must have sent [at least] one extent */
|
||||||
|
if (chunk->length < sizeof(context_id) + sizeof(*extent)) {
|
||||||
error_setg(errp, "Protocol error: invalid payload for "
|
error_setg(errp, "Protocol error: invalid payload for "
|
||||||
"NBD_REPLY_TYPE_BLOCK_STATUS");
|
"NBD_REPLY_TYPE_BLOCK_STATUS");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
context_id = payload_advance32(&payload);
|
context_id = payload_advance32(&payload);
|
||||||
if (client->info.meta_base_allocation_id != context_id) {
|
if (client->info.context_id != context_id) {
|
||||||
error_setg(errp, "Protocol error: unexpected context id %d for "
|
error_setg(errp, "Protocol error: unexpected context id %d for "
|
||||||
"NBD_REPLY_TYPE_BLOCK_STATUS, when negotiated context "
|
"NBD_REPLY_TYPE_BLOCK_STATUS, when negotiated context "
|
||||||
"id is %d", context_id,
|
"id is %d", context_id,
|
||||||
client->info.meta_base_allocation_id);
|
client->info.context_id);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
extent->length = payload_advance32(&payload);
|
extent->length = payload_advance32(&payload);
|
||||||
extent->flags = payload_advance32(&payload);
|
extent->flags = payload_advance32(&payload);
|
||||||
|
|
||||||
if (extent->length == 0 ||
|
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 "
|
error_setg(errp, "Protocol error: server sent status chunk with "
|
||||||
"invalid length");
|
"zero length");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The server is allowed to send us extra information on the final
|
/*
|
||||||
* extent; just clamp it to the length we requested. */
|
* A server sending unaligned block status is in violation of the
|
||||||
|
* protocol, but as qemu-nbd 3.1 is such a server (at least for
|
||||||
|
* POSIX files that are not a multiple of 512 bytes, since qemu
|
||||||
|
* rounds files up to 512-byte multiples but lseek(SEEK_HOLE)
|
||||||
|
* still sees an implicit hole beyond the real EOF), it's nicer to
|
||||||
|
* work around the misbehaving server. If the request included
|
||||||
|
* more than the final unaligned block, truncate it back to an
|
||||||
|
* aligned result; if the request was only the final block, round
|
||||||
|
* up to the full block and change the status to fully-allocated
|
||||||
|
* (always a safe status, even if it loses information).
|
||||||
|
*/
|
||||||
|
if (client->info.min_block && !QEMU_IS_ALIGNED(extent->length,
|
||||||
|
client->info.min_block)) {
|
||||||
|
trace_nbd_parse_blockstatus_compliance("extent length is unaligned");
|
||||||
|
if (extent->length > client->info.min_block) {
|
||||||
|
extent->length = QEMU_ALIGN_DOWN(extent->length,
|
||||||
|
client->info.min_block);
|
||||||
|
} else {
|
||||||
|
extent->length = client->info.min_block;
|
||||||
|
extent->flags = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We used NBD_CMD_FLAG_REQ_ONE, so the server should not have
|
||||||
|
* sent us any more than one extent, nor should it have included
|
||||||
|
* status beyond our request in that extent. However, it's easy
|
||||||
|
* enough to ignore the server's noncompliance without killing the
|
||||||
|
* connection; just ignore trailing extents, and clamp things to
|
||||||
|
* the length of our request.
|
||||||
|
*/
|
||||||
|
if (chunk->length > sizeof(context_id) + sizeof(*extent)) {
|
||||||
|
trace_nbd_parse_blockstatus_compliance("more than one extent");
|
||||||
|
}
|
||||||
if (extent->length > orig_length) {
|
if (extent->length > orig_length) {
|
||||||
extent->length = orig_length;
|
extent->length = orig_length;
|
||||||
|
trace_nbd_parse_blockstatus_compliance("extent length too large");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -334,10 +383,9 @@ static int nbd_co_receive_offset_data_payload(NBDClientSession *s,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nbd_read(s->ioc, &offset, sizeof(offset), errp) < 0) {
|
if (nbd_read64(s->ioc, &offset, "OFFSET_DATA offset", errp) < 0) {
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
be64_to_cpus(&offset);
|
|
||||||
|
|
||||||
data_size = chunk->length - sizeof(offset);
|
data_size = chunk->length - sizeof(offset);
|
||||||
assert(data_size);
|
assert(data_size);
|
||||||
|
@ -347,6 +395,9 @@ static int nbd_co_receive_offset_data_payload(NBDClientSession *s,
|
||||||
" region");
|
" region");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
if (s->info.min_block && !QEMU_IS_ALIGNED(data_size, s->info.min_block)) {
|
||||||
|
trace_nbd_structured_read_compliance("data");
|
||||||
|
}
|
||||||
|
|
||||||
qemu_iovec_init(&sub_qiov, qiov->niov);
|
qemu_iovec_init(&sub_qiov, qiov->niov);
|
||||||
qemu_iovec_concat(&sub_qiov, qiov, offset - orig_offset, data_size);
|
qemu_iovec_concat(&sub_qiov, qiov, offset - orig_offset, data_size);
|
||||||
|
@ -384,7 +435,7 @@ static coroutine_fn int nbd_co_receive_structured_payload(
|
||||||
}
|
}
|
||||||
|
|
||||||
*payload = g_new(char, len);
|
*payload = g_new(char, len);
|
||||||
ret = nbd_read(s->ioc, *payload, len, errp);
|
ret = nbd_read(s->ioc, *payload, len, "structured payload", errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
g_free(*payload);
|
g_free(*payload);
|
||||||
*payload = NULL;
|
*payload = NULL;
|
||||||
|
@ -422,14 +473,15 @@ static coroutine_fn int nbd_co_do_receive_one_chunk(
|
||||||
}
|
}
|
||||||
*request_ret = 0;
|
*request_ret = 0;
|
||||||
|
|
||||||
/* Wait until we're woken up by nbd_read_reply_entry. */
|
/* Wait until we're woken up by nbd_connection_entry. */
|
||||||
s->requests[i].receiving = true;
|
s->requests[i].receiving = true;
|
||||||
qemu_coroutine_yield();
|
qemu_coroutine_yield();
|
||||||
s->requests[i].receiving = false;
|
s->requests[i].receiving = false;
|
||||||
if (!s->ioc || s->quit) {
|
if (s->quit) {
|
||||||
error_setg(errp, "Connection closed");
|
error_setg(errp, "Connection closed");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
assert(s->ioc);
|
||||||
|
|
||||||
assert(s->reply.handle == handle);
|
assert(s->reply.handle == handle);
|
||||||
|
|
||||||
|
@ -496,30 +548,29 @@ static coroutine_fn int nbd_co_do_receive_one_chunk(
|
||||||
}
|
}
|
||||||
|
|
||||||
/* nbd_co_receive_one_chunk
|
/* nbd_co_receive_one_chunk
|
||||||
* Read reply, wake up read_reply_co and set s->quit if needed.
|
* Read reply, wake up connection_co and set s->quit if needed.
|
||||||
* Return value is a fatal error code or normal nbd reply error code
|
* Return value is a fatal error code or normal nbd reply error code
|
||||||
*/
|
*/
|
||||||
static coroutine_fn int nbd_co_receive_one_chunk(
|
static coroutine_fn int nbd_co_receive_one_chunk(
|
||||||
NBDClientSession *s, uint64_t handle, bool only_structured,
|
NBDClientSession *s, uint64_t handle, bool only_structured,
|
||||||
QEMUIOVector *qiov, NBDReply *reply, void **payload, Error **errp)
|
int *request_ret, QEMUIOVector *qiov, NBDReply *reply, void **payload,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
int request_ret;
|
|
||||||
int ret = nbd_co_do_receive_one_chunk(s, handle, only_structured,
|
int ret = nbd_co_do_receive_one_chunk(s, handle, only_structured,
|
||||||
&request_ret, qiov, payload, errp);
|
request_ret, qiov, payload, errp);
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
s->quit = true;
|
s->quit = true;
|
||||||
} else {
|
} else {
|
||||||
/* For assert at loop start in nbd_read_reply_entry */
|
/* For assert at loop start in nbd_connection_entry */
|
||||||
if (reply) {
|
if (reply) {
|
||||||
*reply = s->reply;
|
*reply = s->reply;
|
||||||
}
|
}
|
||||||
s->reply.handle = 0;
|
s->reply.handle = 0;
|
||||||
ret = request_ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->read_reply_co) {
|
if (s->connection_co) {
|
||||||
aio_co_wake(s->read_reply_co);
|
aio_co_wake(s->connection_co);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -527,22 +578,17 @@ static coroutine_fn int nbd_co_receive_one_chunk(
|
||||||
|
|
||||||
typedef struct NBDReplyChunkIter {
|
typedef struct NBDReplyChunkIter {
|
||||||
int ret;
|
int ret;
|
||||||
bool fatal;
|
int request_ret;
|
||||||
Error *err;
|
Error *err;
|
||||||
bool done, only_structured;
|
bool done, only_structured;
|
||||||
} NBDReplyChunkIter;
|
} NBDReplyChunkIter;
|
||||||
|
|
||||||
static void nbd_iter_error(NBDReplyChunkIter *iter, bool fatal,
|
static void nbd_iter_channel_error(NBDReplyChunkIter *iter,
|
||||||
int ret, Error **local_err)
|
int ret, Error **local_err)
|
||||||
{
|
{
|
||||||
assert(ret < 0);
|
assert(ret < 0);
|
||||||
|
|
||||||
if ((fatal && !iter->fatal) || iter->ret == 0) {
|
if (!iter->ret) {
|
||||||
if (iter->ret != 0) {
|
|
||||||
error_free(iter->err);
|
|
||||||
iter->err = NULL;
|
|
||||||
}
|
|
||||||
iter->fatal = fatal;
|
|
||||||
iter->ret = ret;
|
iter->ret = ret;
|
||||||
error_propagate(&iter->err, *local_err);
|
error_propagate(&iter->err, *local_err);
|
||||||
} else {
|
} else {
|
||||||
|
@ -552,6 +598,15 @@ static void nbd_iter_error(NBDReplyChunkIter *iter, bool fatal,
|
||||||
*local_err = NULL;
|
*local_err = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void nbd_iter_request_error(NBDReplyChunkIter *iter, int ret)
|
||||||
|
{
|
||||||
|
assert(ret < 0);
|
||||||
|
|
||||||
|
if (!iter->request_ret) {
|
||||||
|
iter->request_ret = ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* NBD_FOREACH_REPLY_CHUNK
|
/* NBD_FOREACH_REPLY_CHUNK
|
||||||
*/
|
*/
|
||||||
#define NBD_FOREACH_REPLY_CHUNK(s, iter, handle, structured, \
|
#define NBD_FOREACH_REPLY_CHUNK(s, iter, handle, structured, \
|
||||||
|
@ -567,13 +622,13 @@ static bool nbd_reply_chunk_iter_receive(NBDClientSession *s,
|
||||||
QEMUIOVector *qiov, NBDReply *reply,
|
QEMUIOVector *qiov, NBDReply *reply,
|
||||||
void **payload)
|
void **payload)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret, request_ret;
|
||||||
NBDReply local_reply;
|
NBDReply local_reply;
|
||||||
NBDStructuredReplyChunk *chunk;
|
NBDStructuredReplyChunk *chunk;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
if (s->quit) {
|
if (s->quit) {
|
||||||
error_setg(&local_err, "Connection closed");
|
error_setg(&local_err, "Connection closed");
|
||||||
nbd_iter_error(iter, true, -EIO, &local_err);
|
nbd_iter_channel_error(iter, -EIO, &local_err);
|
||||||
goto break_loop;
|
goto break_loop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -587,14 +642,16 @@ static bool nbd_reply_chunk_iter_receive(NBDClientSession *s,
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = nbd_co_receive_one_chunk(s, handle, iter->only_structured,
|
ret = nbd_co_receive_one_chunk(s, handle, iter->only_structured,
|
||||||
qiov, reply, payload, &local_err);
|
&request_ret, qiov, reply, payload,
|
||||||
|
&local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
/* If it is a fatal error s->quit is set by nbd_co_receive_one_chunk */
|
nbd_iter_channel_error(iter, ret, &local_err);
|
||||||
nbd_iter_error(iter, s->quit, ret, &local_err);
|
} else if (request_ret < 0) {
|
||||||
|
nbd_iter_request_error(iter, request_ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do not execute the body of NBD_FOREACH_REPLY_CHUNK for simple reply. */
|
/* Do not execute the body of NBD_FOREACH_REPLY_CHUNK for simple reply. */
|
||||||
if (nbd_reply_is_simple(&s->reply) || s->quit) {
|
if (nbd_reply_is_simple(reply) || s->quit) {
|
||||||
goto break_loop;
|
goto break_loop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -627,7 +684,7 @@ break_loop:
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nbd_co_receive_return_code(NBDClientSession *s, uint64_t handle,
|
static int nbd_co_receive_return_code(NBDClientSession *s, uint64_t handle,
|
||||||
Error **errp)
|
int *request_ret, Error **errp)
|
||||||
{
|
{
|
||||||
NBDReplyChunkIter iter;
|
NBDReplyChunkIter iter;
|
||||||
|
|
||||||
|
@ -636,12 +693,13 @@ static int nbd_co_receive_return_code(NBDClientSession *s, uint64_t handle,
|
||||||
}
|
}
|
||||||
|
|
||||||
error_propagate(errp, iter.err);
|
error_propagate(errp, iter.err);
|
||||||
|
*request_ret = iter.request_ret;
|
||||||
return iter.ret;
|
return iter.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle,
|
static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle,
|
||||||
uint64_t offset, QEMUIOVector *qiov,
|
uint64_t offset, QEMUIOVector *qiov,
|
||||||
Error **errp)
|
int *request_ret, Error **errp)
|
||||||
{
|
{
|
||||||
NBDReplyChunkIter iter;
|
NBDReplyChunkIter iter;
|
||||||
NBDReply reply;
|
NBDReply reply;
|
||||||
|
@ -662,11 +720,11 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle,
|
||||||
* in qiov */
|
* in qiov */
|
||||||
break;
|
break;
|
||||||
case NBD_REPLY_TYPE_OFFSET_HOLE:
|
case NBD_REPLY_TYPE_OFFSET_HOLE:
|
||||||
ret = nbd_parse_offset_hole_payload(&reply.structured, payload,
|
ret = nbd_parse_offset_hole_payload(s, &reply.structured, payload,
|
||||||
offset, qiov, &local_err);
|
offset, qiov, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
s->quit = true;
|
s->quit = true;
|
||||||
nbd_iter_error(&iter, true, ret, &local_err);
|
nbd_iter_channel_error(&iter, ret, &local_err);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -676,7 +734,7 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle,
|
||||||
error_setg(&local_err,
|
error_setg(&local_err,
|
||||||
"Unexpected reply type: %d (%s) for CMD_READ",
|
"Unexpected reply type: %d (%s) for CMD_READ",
|
||||||
chunk->type, nbd_reply_type_lookup(chunk->type));
|
chunk->type, nbd_reply_type_lookup(chunk->type));
|
||||||
nbd_iter_error(&iter, true, -EINVAL, &local_err);
|
nbd_iter_channel_error(&iter, -EINVAL, &local_err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -685,12 +743,14 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle,
|
||||||
}
|
}
|
||||||
|
|
||||||
error_propagate(errp, iter.err);
|
error_propagate(errp, iter.err);
|
||||||
|
*request_ret = iter.request_ret;
|
||||||
return iter.ret;
|
return iter.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nbd_co_receive_blockstatus_reply(NBDClientSession *s,
|
static int nbd_co_receive_blockstatus_reply(NBDClientSession *s,
|
||||||
uint64_t handle, uint64_t length,
|
uint64_t handle, uint64_t length,
|
||||||
NBDExtent *extent, Error **errp)
|
NBDExtent *extent,
|
||||||
|
int *request_ret, Error **errp)
|
||||||
{
|
{
|
||||||
NBDReplyChunkIter iter;
|
NBDReplyChunkIter iter;
|
||||||
NBDReply reply;
|
NBDReply reply;
|
||||||
|
@ -699,9 +759,7 @@ static int nbd_co_receive_blockstatus_reply(NBDClientSession *s,
|
||||||
bool received = false;
|
bool received = false;
|
||||||
|
|
||||||
assert(!extent->length);
|
assert(!extent->length);
|
||||||
NBD_FOREACH_REPLY_CHUNK(s, iter, handle, s->info.structured_reply,
|
NBD_FOREACH_REPLY_CHUNK(s, iter, handle, false, NULL, &reply, &payload) {
|
||||||
NULL, &reply, &payload)
|
|
||||||
{
|
|
||||||
int ret;
|
int ret;
|
||||||
NBDStructuredReplyChunk *chunk = &reply.structured;
|
NBDStructuredReplyChunk *chunk = &reply.structured;
|
||||||
|
|
||||||
|
@ -712,7 +770,7 @@ static int nbd_co_receive_blockstatus_reply(NBDClientSession *s,
|
||||||
if (received) {
|
if (received) {
|
||||||
s->quit = true;
|
s->quit = true;
|
||||||
error_setg(&local_err, "Several BLOCK_STATUS chunks in reply");
|
error_setg(&local_err, "Several BLOCK_STATUS chunks in reply");
|
||||||
nbd_iter_error(&iter, true, -EINVAL, &local_err);
|
nbd_iter_channel_error(&iter, -EINVAL, &local_err);
|
||||||
}
|
}
|
||||||
received = true;
|
received = true;
|
||||||
|
|
||||||
|
@ -721,7 +779,7 @@ static int nbd_co_receive_blockstatus_reply(NBDClientSession *s,
|
||||||
&local_err);
|
&local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
s->quit = true;
|
s->quit = true;
|
||||||
nbd_iter_error(&iter, true, ret, &local_err);
|
nbd_iter_channel_error(&iter, ret, &local_err);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -731,7 +789,7 @@ static int nbd_co_receive_blockstatus_reply(NBDClientSession *s,
|
||||||
"Unexpected reply type: %d (%s) "
|
"Unexpected reply type: %d (%s) "
|
||||||
"for CMD_BLOCK_STATUS",
|
"for CMD_BLOCK_STATUS",
|
||||||
chunk->type, nbd_reply_type_lookup(chunk->type));
|
chunk->type, nbd_reply_type_lookup(chunk->type));
|
||||||
nbd_iter_error(&iter, true, -EINVAL, &local_err);
|
nbd_iter_channel_error(&iter, -EINVAL, &local_err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -739,21 +797,20 @@ static int nbd_co_receive_blockstatus_reply(NBDClientSession *s,
|
||||||
payload = NULL;
|
payload = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!extent->length && !iter.err) {
|
if (!extent->length && !iter.request_ret) {
|
||||||
error_setg(&iter.err,
|
error_setg(&local_err, "Server did not reply with any status extents");
|
||||||
"Server did not reply with any status extents");
|
nbd_iter_channel_error(&iter, -EIO, &local_err);
|
||||||
if (!iter.ret) {
|
|
||||||
iter.ret = -EIO;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
error_propagate(errp, iter.err);
|
error_propagate(errp, iter.err);
|
||||||
|
*request_ret = iter.request_ret;
|
||||||
return iter.ret;
|
return iter.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nbd_co_request(BlockDriverState *bs, NBDRequest *request,
|
static int nbd_co_request(BlockDriverState *bs, NBDRequest *request,
|
||||||
QEMUIOVector *write_qiov)
|
QEMUIOVector *write_qiov)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret, request_ret;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
NBDClientSession *client = nbd_get_client_session(bs);
|
NBDClientSession *client = nbd_get_client_session(bs);
|
||||||
|
|
||||||
|
@ -769,17 +826,22 @@ static int nbd_co_request(BlockDriverState *bs, NBDRequest *request,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = nbd_co_receive_return_code(client, request->handle, &local_err);
|
ret = nbd_co_receive_return_code(client, request->handle,
|
||||||
|
&request_ret, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_report_err(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);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret ? ret : request_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
|
int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||||
uint64_t bytes, QEMUIOVector *qiov, int flags)
|
uint64_t bytes, QEMUIOVector *qiov, int flags)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret, request_ret;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
NBDClientSession *client = nbd_get_client_session(bs);
|
NBDClientSession *client = nbd_get_client_session(bs);
|
||||||
NBDRequest request = {
|
NBDRequest request = {
|
||||||
|
@ -794,17 +856,40 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||||
if (!bytes) {
|
if (!bytes) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* Work around the fact that the block layer doesn't do
|
||||||
|
* byte-accurate sizing yet - if the read exceeds the server's
|
||||||
|
* advertised size because the block layer rounded size up, then
|
||||||
|
* truncate the request to the server and tail-pad with zero.
|
||||||
|
*/
|
||||||
|
if (offset >= client->info.size) {
|
||||||
|
assert(bytes < BDRV_SECTOR_SIZE);
|
||||||
|
qemu_iovec_memset(qiov, 0, 0, bytes);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (offset + bytes > client->info.size) {
|
||||||
|
uint64_t slop = offset + bytes - client->info.size;
|
||||||
|
|
||||||
|
assert(slop < BDRV_SECTOR_SIZE);
|
||||||
|
qemu_iovec_memset(qiov, bytes - slop, 0, slop);
|
||||||
|
request.len -= slop;
|
||||||
|
}
|
||||||
|
|
||||||
ret = nbd_co_send_request(bs, &request, NULL);
|
ret = nbd_co_send_request(bs, &request, NULL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = nbd_co_receive_cmdread_reply(client, request.handle, offset, qiov,
|
ret = nbd_co_receive_cmdread_reply(client, request.handle, offset, qiov,
|
||||||
&local_err);
|
&request_ret, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_report_err(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);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret ? ret : request_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
||||||
|
@ -898,7 +983,7 @@ int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs,
|
||||||
int64_t *pnum, int64_t *map,
|
int64_t *pnum, int64_t *map,
|
||||||
BlockDriverState **file)
|
BlockDriverState **file)
|
||||||
{
|
{
|
||||||
int64_t ret;
|
int ret, request_ret;
|
||||||
NBDExtent extent = { 0 };
|
NBDExtent extent = { 0 };
|
||||||
NBDClientSession *client = nbd_get_client_session(bs);
|
NBDClientSession *client = nbd_get_client_session(bs);
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
@ -908,33 +993,60 @@ int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs,
|
||||||
.from = offset,
|
.from = offset,
|
||||||
.len = MIN(MIN_NON_ZERO(QEMU_ALIGN_DOWN(INT_MAX,
|
.len = MIN(MIN_NON_ZERO(QEMU_ALIGN_DOWN(INT_MAX,
|
||||||
bs->bl.request_alignment),
|
bs->bl.request_alignment),
|
||||||
client->info.max_block), bytes),
|
client->info.max_block),
|
||||||
|
MIN(bytes, client->info.size - offset)),
|
||||||
.flags = NBD_CMD_FLAG_REQ_ONE,
|
.flags = NBD_CMD_FLAG_REQ_ONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!client->info.base_allocation) {
|
if (!client->info.base_allocation) {
|
||||||
*pnum = bytes;
|
*pnum = bytes;
|
||||||
return BDRV_BLOCK_DATA;
|
*map = offset;
|
||||||
|
*file = bs;
|
||||||
|
return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Work around the fact that the block layer doesn't do
|
||||||
|
* byte-accurate sizing yet - if the status request exceeds the
|
||||||
|
* server's advertised size because the block layer rounded size
|
||||||
|
* up, we truncated the request to the server (above), or are
|
||||||
|
* called on just the hole.
|
||||||
|
*/
|
||||||
|
if (offset >= client->info.size) {
|
||||||
|
*pnum = bytes;
|
||||||
|
assert(bytes < BDRV_SECTOR_SIZE);
|
||||||
|
/* Intentionally don't report offset_valid for the hole */
|
||||||
|
return BDRV_BLOCK_ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client->info.min_block) {
|
||||||
|
assert(QEMU_IS_ALIGNED(request.len, client->info.min_block));
|
||||||
|
}
|
||||||
ret = nbd_co_send_request(bs, &request, NULL);
|
ret = nbd_co_send_request(bs, &request, NULL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = nbd_co_receive_blockstatus_reply(client, request.handle, bytes,
|
ret = nbd_co_receive_blockstatus_reply(client, request.handle, bytes,
|
||||||
&extent, &local_err);
|
&extent, &request_ret, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_report_err(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) {
|
if (ret < 0 || request_ret < 0) {
|
||||||
return ret;
|
return ret ? ret : request_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(extent.length);
|
assert(extent.length);
|
||||||
*pnum = extent.length;
|
*pnum = extent.length;
|
||||||
|
*map = offset;
|
||||||
|
*file = bs;
|
||||||
return (extent.flags & NBD_STATE_HOLE ? 0 : BDRV_BLOCK_DATA) |
|
return (extent.flags & NBD_STATE_HOLE ? 0 : BDRV_BLOCK_DATA) |
|
||||||
(extent.flags & NBD_STATE_ZERO ? BDRV_BLOCK_ZERO : 0);
|
(extent.flags & NBD_STATE_ZERO ? BDRV_BLOCK_ZERO : 0) |
|
||||||
|
BDRV_BLOCK_OFFSET_VALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nbd_client_detach_aio_context(BlockDriverState *bs)
|
void nbd_client_detach_aio_context(BlockDriverState *bs)
|
||||||
|
@ -943,12 +1055,30 @@ void nbd_client_detach_aio_context(BlockDriverState *bs)
|
||||||
qio_channel_detach_aio_context(QIO_CHANNEL(client->ioc));
|
qio_channel_detach_aio_context(QIO_CHANNEL(client->ioc));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void nbd_client_attach_aio_context_bh(void *opaque)
|
||||||
|
{
|
||||||
|
BlockDriverState *bs = opaque;
|
||||||
|
NBDClientSession *client = nbd_get_client_session(bs);
|
||||||
|
|
||||||
|
/* The node is still drained, so we know the coroutine has yielded in
|
||||||
|
* nbd_read_eof(), the only place where bs->in_flight can reach 0, or it is
|
||||||
|
* entered for the first time. Both places are safe for entering the
|
||||||
|
* coroutine.*/
|
||||||
|
qemu_aio_coroutine_enter(bs->aio_context, client->connection_co);
|
||||||
|
bdrv_dec_in_flight(bs);
|
||||||
|
}
|
||||||
|
|
||||||
void nbd_client_attach_aio_context(BlockDriverState *bs,
|
void nbd_client_attach_aio_context(BlockDriverState *bs,
|
||||||
AioContext *new_context)
|
AioContext *new_context)
|
||||||
{
|
{
|
||||||
NBDClientSession *client = nbd_get_client_session(bs);
|
NBDClientSession *client = nbd_get_client_session(bs);
|
||||||
qio_channel_attach_aio_context(QIO_CHANNEL(client->ioc), new_context);
|
qio_channel_attach_aio_context(QIO_CHANNEL(client->ioc), new_context);
|
||||||
aio_co_schedule(new_context, client->read_reply_co);
|
|
||||||
|
bdrv_inc_in_flight(bs);
|
||||||
|
|
||||||
|
/* Need to wait here for the BH to run because the BH must run while the
|
||||||
|
* node is still drained. */
|
||||||
|
aio_wait_bh_oneshot(new_context, nbd_client_attach_aio_context_bh, bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nbd_client_close(BlockDriverState *bs)
|
void nbd_client_close(BlockDriverState *bs)
|
||||||
|
@ -956,26 +1086,55 @@ void nbd_client_close(BlockDriverState *bs)
|
||||||
NBDClientSession *client = nbd_get_client_session(bs);
|
NBDClientSession *client = nbd_get_client_session(bs);
|
||||||
NBDRequest request = { .type = NBD_CMD_DISC };
|
NBDRequest request = { .type = NBD_CMD_DISC };
|
||||||
|
|
||||||
if (client->ioc == NULL) {
|
assert(client->ioc);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nbd_send_request(client->ioc, &request);
|
nbd_send_request(client->ioc, &request);
|
||||||
|
|
||||||
nbd_teardown_connection(bs);
|
nbd_teardown_connection(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
int nbd_client_init(BlockDriverState *bs,
|
static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr,
|
||||||
QIOChannelSocket *sioc,
|
Error **errp)
|
||||||
const char *export,
|
{
|
||||||
QCryptoTLSCreds *tlscreds,
|
QIOChannelSocket *sioc;
|
||||||
const char *hostname,
|
Error *local_err = NULL;
|
||||||
const char *x_dirty_bitmap,
|
|
||||||
Error **errp)
|
sioc = qio_channel_socket_new();
|
||||||
|
qio_channel_set_name(QIO_CHANNEL(sioc), "nbd-client");
|
||||||
|
|
||||||
|
qio_channel_socket_connect_sync(sioc, saddr, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
object_unref(OBJECT(sioc));
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
qio_channel_set_delay(QIO_CHANNEL(sioc), false);
|
||||||
|
|
||||||
|
return sioc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nbd_client_connect(BlockDriverState *bs,
|
||||||
|
SocketAddress *saddr,
|
||||||
|
const char *export,
|
||||||
|
QCryptoTLSCreds *tlscreds,
|
||||||
|
const char *hostname,
|
||||||
|
const char *x_dirty_bitmap,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
NBDClientSession *client = nbd_get_client_session(bs);
|
NBDClientSession *client = nbd_get_client_session(bs);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* establish TCP connection, return error if it fails
|
||||||
|
* TODO: Configurable retry-until-timeout behaviour.
|
||||||
|
*/
|
||||||
|
QIOChannelSocket *sioc = nbd_establish_connection(saddr, errp);
|
||||||
|
|
||||||
|
if (!sioc) {
|
||||||
|
return -ECONNREFUSED;
|
||||||
|
}
|
||||||
|
|
||||||
/* NBD handshake */
|
/* NBD handshake */
|
||||||
logout("session init %s\n", export);
|
logout("session init %s\n", export);
|
||||||
qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL);
|
qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL);
|
||||||
|
@ -984,19 +1143,27 @@ int nbd_client_init(BlockDriverState *bs,
|
||||||
client->info.structured_reply = true;
|
client->info.structured_reply = true;
|
||||||
client->info.base_allocation = true;
|
client->info.base_allocation = true;
|
||||||
client->info.x_dirty_bitmap = g_strdup(x_dirty_bitmap);
|
client->info.x_dirty_bitmap = g_strdup(x_dirty_bitmap);
|
||||||
ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export,
|
client->info.name = g_strdup(export ?: "");
|
||||||
tlscreds, hostname,
|
ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), tlscreds, hostname,
|
||||||
&client->ioc, &client->info, errp);
|
&client->ioc, &client->info, errp);
|
||||||
g_free(client->info.x_dirty_bitmap);
|
g_free(client->info.x_dirty_bitmap);
|
||||||
|
g_free(client->info.name);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
logout("Failed to negotiate with the NBD server\n");
|
logout("Failed to negotiate with the NBD server\n");
|
||||||
|
object_unref(OBJECT(sioc));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
if (client->info.flags & NBD_FLAG_READ_ONLY &&
|
if (x_dirty_bitmap && !client->info.base_allocation) {
|
||||||
!bdrv_is_read_only(bs)) {
|
error_setg(errp, "requested x-dirty-bitmap %s not found",
|
||||||
error_setg(errp,
|
x_dirty_bitmap);
|
||||||
"request for write access conflicts with read-only export");
|
ret = -EINVAL;
|
||||||
return -EACCES;
|
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_SEND_FUA) {
|
if (client->info.flags & NBD_FLAG_SEND_FUA) {
|
||||||
bs->supported_write_flags = BDRV_REQ_FUA;
|
bs->supported_write_flags = BDRV_REQ_FUA;
|
||||||
|
@ -1006,10 +1173,7 @@ int nbd_client_init(BlockDriverState *bs,
|
||||||
bs->supported_zero_flags |= BDRV_REQ_MAY_UNMAP;
|
bs->supported_zero_flags |= BDRV_REQ_MAY_UNMAP;
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_co_mutex_init(&client->send_mutex);
|
|
||||||
qemu_co_queue_init(&client->free_sema);
|
|
||||||
client->sioc = sioc;
|
client->sioc = sioc;
|
||||||
object_ref(OBJECT(client->sioc));
|
|
||||||
|
|
||||||
if (!client->ioc) {
|
if (!client->ioc) {
|
||||||
client->ioc = QIO_CHANNEL(sioc);
|
client->ioc = QIO_CHANNEL(sioc);
|
||||||
|
@ -1019,9 +1183,44 @@ int nbd_client_init(BlockDriverState *bs,
|
||||||
/* Now that we're connected, set the socket to be non-blocking and
|
/* Now that we're connected, set the socket to be non-blocking and
|
||||||
* kick the reply mechanism. */
|
* kick the reply mechanism. */
|
||||||
qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL);
|
qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL);
|
||||||
client->read_reply_co = qemu_coroutine_create(nbd_read_reply_entry, client);
|
client->connection_co = qemu_coroutine_create(nbd_connection_entry, client);
|
||||||
|
bdrv_inc_in_flight(bs);
|
||||||
nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs));
|
nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs));
|
||||||
|
|
||||||
logout("Established connection with NBD server\n");
|
logout("Established connection with NBD server\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
/*
|
||||||
|
* We have connected, but must fail for other reasons. The
|
||||||
|
* connection is still blocking; send NBD_CMD_DISC as a courtesy
|
||||||
|
* to the server.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
NBDRequest request = { .type = NBD_CMD_DISC };
|
||||||
|
|
||||||
|
nbd_send_request(client->ioc ?: QIO_CHANNEL(sioc), &request);
|
||||||
|
|
||||||
|
object_unref(OBJECT(sioc));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int nbd_client_init(BlockDriverState *bs,
|
||||||
|
SocketAddress *saddr,
|
||||||
|
const char *export,
|
||||||
|
QCryptoTLSCreds *tlscreds,
|
||||||
|
const char *hostname,
|
||||||
|
const char *x_dirty_bitmap,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
NBDClientSession *client = nbd_get_client_session(bs);
|
||||||
|
|
||||||
|
client->bs = bs;
|
||||||
|
qemu_co_mutex_init(&client->send_mutex);
|
||||||
|
qemu_co_queue_init(&client->free_sema);
|
||||||
|
|
||||||
|
return nbd_client_connect(bs, saddr, export, tlscreds, hostname,
|
||||||
|
x_dirty_bitmap, errp);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Coroutine *coroutine;
|
Coroutine *coroutine;
|
||||||
uint64_t offset; /* original offset of the request */
|
uint64_t offset; /* original offset of the request */
|
||||||
bool receiving; /* waiting for read_reply_co? */
|
bool receiving; /* waiting for connection_co? */
|
||||||
} NBDClientRequest;
|
} NBDClientRequest;
|
||||||
|
|
||||||
typedef struct NBDClientSession {
|
typedef struct NBDClientSession {
|
||||||
|
@ -30,18 +30,19 @@ typedef struct NBDClientSession {
|
||||||
|
|
||||||
CoMutex send_mutex;
|
CoMutex send_mutex;
|
||||||
CoQueue free_sema;
|
CoQueue free_sema;
|
||||||
Coroutine *read_reply_co;
|
Coroutine *connection_co;
|
||||||
int in_flight;
|
int in_flight;
|
||||||
|
|
||||||
NBDClientRequest requests[MAX_NBD_REQUESTS];
|
NBDClientRequest requests[MAX_NBD_REQUESTS];
|
||||||
NBDReply reply;
|
NBDReply reply;
|
||||||
|
BlockDriverState *bs;
|
||||||
bool quit;
|
bool quit;
|
||||||
} NBDClientSession;
|
} NBDClientSession;
|
||||||
|
|
||||||
NBDClientSession *nbd_get_client_session(BlockDriverState *bs);
|
NBDClientSession *nbd_get_client_session(BlockDriverState *bs);
|
||||||
|
|
||||||
int nbd_client_init(BlockDriverState *bs,
|
int nbd_client_init(BlockDriverState *bs,
|
||||||
QIOChannelSocket *sock,
|
SocketAddress *saddr,
|
||||||
const char *export_name,
|
const char *export_name,
|
||||||
QCryptoTLSCreds *tlscreds,
|
QCryptoTLSCreds *tlscreds,
|
||||||
const char *hostname,
|
const char *hostname,
|
||||||
|
|
109
block/nbd.c
109
block/nbd.c
|
@ -295,30 +295,6 @@ NBDClientSession *nbd_get_client_session(BlockDriverState *bs)
|
||||||
return &s->client;
|
return &s->client;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr,
|
|
||||||
Error **errp)
|
|
||||||
{
|
|
||||||
QIOChannelSocket *sioc;
|
|
||||||
Error *local_err = NULL;
|
|
||||||
|
|
||||||
sioc = qio_channel_socket_new();
|
|
||||||
qio_channel_set_name(QIO_CHANNEL(sioc), "nbd-client");
|
|
||||||
|
|
||||||
qio_channel_socket_connect_sync(sioc,
|
|
||||||
saddr,
|
|
||||||
&local_err);
|
|
||||||
if (local_err) {
|
|
||||||
object_unref(OBJECT(sioc));
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
qio_channel_set_delay(QIO_CHANNEL(sioc), false);
|
|
||||||
|
|
||||||
return sioc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
|
static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
|
||||||
{
|
{
|
||||||
Object *obj;
|
Object *obj;
|
||||||
|
@ -394,7 +370,6 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
BDRVNBDState *s = bs->opaque;
|
BDRVNBDState *s = bs->opaque;
|
||||||
QemuOpts *opts = NULL;
|
QemuOpts *opts = NULL;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
QIOChannelSocket *sioc = NULL;
|
|
||||||
QCryptoTLSCreds *tlscreds = NULL;
|
QCryptoTLSCreds *tlscreds = NULL;
|
||||||
const char *hostname = NULL;
|
const char *hostname = NULL;
|
||||||
int ret = -EINVAL;
|
int ret = -EINVAL;
|
||||||
|
@ -434,22 +409,11 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
hostname = s->saddr->u.inet.host;
|
hostname = s->saddr->u.inet.host;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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 */
|
/* NBD handshake */
|
||||||
ret = nbd_client_init(bs, sioc, s->export, tlscreds, hostname,
|
ret = nbd_client_init(bs, s->saddr, s->export, tlscreds, hostname,
|
||||||
qemu_opt_get(opts, "x-dirty-bitmap"), errp);
|
qemu_opt_get(opts, "x-dirty-bitmap"), errp);
|
||||||
|
|
||||||
error:
|
error:
|
||||||
if (sioc) {
|
|
||||||
object_unref(OBJECT(sioc));
|
|
||||||
}
|
|
||||||
if (tlscreds) {
|
if (tlscreds) {
|
||||||
object_unref(OBJECT(tlscreds));
|
object_unref(OBJECT(tlscreds));
|
||||||
}
|
}
|
||||||
|
@ -473,7 +437,24 @@ static void nbd_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||||
uint32_t min = s->info.min_block;
|
uint32_t min = s->info.min_block;
|
||||||
uint32_t max = MIN_NON_ZERO(NBD_MAX_BUFFER_SIZE, s->info.max_block);
|
uint32_t max = MIN_NON_ZERO(NBD_MAX_BUFFER_SIZE, s->info.max_block);
|
||||||
|
|
||||||
bs->bl.request_alignment = min ? min : BDRV_SECTOR_SIZE;
|
/*
|
||||||
|
* If the server did not advertise an alignment:
|
||||||
|
* - a size that is not sector-aligned implies that an alignment
|
||||||
|
* of 1 can be used to access those tail bytes
|
||||||
|
* - advertisement of block status requires an alignment of 1, so
|
||||||
|
* that we don't violate block layer constraints that block
|
||||||
|
* status is always aligned (as we can't control whether the
|
||||||
|
* server will report sub-sector extents, such as a hole at EOF
|
||||||
|
* on an unaligned POSIX file)
|
||||||
|
* - otherwise, assume the server is so old that we are safer avoiding
|
||||||
|
* sub-sector requests
|
||||||
|
*/
|
||||||
|
if (!min) {
|
||||||
|
min = (!QEMU_IS_ALIGNED(s->info.size, BDRV_SECTOR_SIZE) ||
|
||||||
|
s->info.base_allocation) ? 1 : BDRV_SECTOR_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bs->bl.request_alignment = min;
|
||||||
bs->bl.max_pdiscard = max;
|
bs->bl.max_pdiscard = max;
|
||||||
bs->bl.max_pwrite_zeroes = max;
|
bs->bl.max_pwrite_zeroes = max;
|
||||||
bs->bl.max_transfer = max;
|
bs->bl.max_transfer = max;
|
||||||
|
@ -513,12 +494,9 @@ static void nbd_attach_aio_context(BlockDriverState *bs,
|
||||||
nbd_client_attach_aio_context(bs, new_context);
|
nbd_client_attach_aio_context(bs, new_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
|
static void nbd_refresh_filename(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVNBDState *s = bs->opaque;
|
BDRVNBDState *s = bs->opaque;
|
||||||
QDict *opts = qdict_new();
|
|
||||||
QObject *saddr_qdict;
|
|
||||||
Visitor *ov;
|
|
||||||
const char *host = NULL, *port = NULL, *path = NULL;
|
const char *host = NULL, *port = NULL, *path = NULL;
|
||||||
|
|
||||||
if (s->saddr->type == SOCKET_ADDRESS_TYPE_INET) {
|
if (s->saddr->type == SOCKET_ADDRESS_TYPE_INET) {
|
||||||
|
@ -531,8 +509,6 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||||
path = s->saddr->u.q_unix.path;
|
path = s->saddr->u.q_unix.path;
|
||||||
} /* else can't represent as pseudo-filename */
|
} /* else can't represent as pseudo-filename */
|
||||||
|
|
||||||
qdict_put_str(opts, "driver", "nbd");
|
|
||||||
|
|
||||||
if (path && s->export) {
|
if (path && s->export) {
|
||||||
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||||
"nbd+unix:///%s?socket=%s", s->export, path);
|
"nbd+unix:///%s?socket=%s", s->export, path);
|
||||||
|
@ -546,24 +522,29 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||||
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||||
"nbd://%s:%s", host, port);
|
"nbd://%s:%s", host, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
ov = qobject_output_visitor_new(&saddr_qdict);
|
|
||||||
visit_type_SocketAddress(ov, NULL, &s->saddr, &error_abort);
|
|
||||||
visit_complete(ov, &saddr_qdict);
|
|
||||||
visit_free(ov);
|
|
||||||
qdict_put_obj(opts, "server", saddr_qdict);
|
|
||||||
|
|
||||||
if (s->export) {
|
|
||||||
qdict_put_str(opts, "export", s->export);
|
|
||||||
}
|
|
||||||
if (s->tlscredsid) {
|
|
||||||
qdict_put_str(opts, "tls-creds", s->tlscredsid);
|
|
||||||
}
|
|
||||||
|
|
||||||
qdict_flatten(opts);
|
|
||||||
bs->full_open_options = opts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *nbd_dirname(BlockDriverState *bs, Error **errp)
|
||||||
|
{
|
||||||
|
/* The generic bdrv_dirname() implementation is able to work out some
|
||||||
|
* directory name for NBD nodes, but that would be wrong. So far there is no
|
||||||
|
* specification for how "export paths" would work, so NBD does not have
|
||||||
|
* directory names. */
|
||||||
|
error_setg(errp, "Cannot generate a base directory for NBD nodes");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *const nbd_strong_runtime_opts[] = {
|
||||||
|
"path",
|
||||||
|
"host",
|
||||||
|
"port",
|
||||||
|
"export",
|
||||||
|
"tls-creds",
|
||||||
|
"server.",
|
||||||
|
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_nbd = {
|
static BlockDriver bdrv_nbd = {
|
||||||
.format_name = "nbd",
|
.format_name = "nbd",
|
||||||
.protocol_name = "nbd",
|
.protocol_name = "nbd",
|
||||||
|
@ -582,6 +563,8 @@ static BlockDriver bdrv_nbd = {
|
||||||
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
||||||
.bdrv_refresh_filename = nbd_refresh_filename,
|
.bdrv_refresh_filename = nbd_refresh_filename,
|
||||||
.bdrv_co_block_status = nbd_client_co_block_status,
|
.bdrv_co_block_status = nbd_client_co_block_status,
|
||||||
|
.bdrv_dirname = nbd_dirname,
|
||||||
|
.strong_runtime_opts = nbd_strong_runtime_opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_nbd_tcp = {
|
static BlockDriver bdrv_nbd_tcp = {
|
||||||
|
@ -602,6 +585,8 @@ static BlockDriver bdrv_nbd_tcp = {
|
||||||
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
||||||
.bdrv_refresh_filename = nbd_refresh_filename,
|
.bdrv_refresh_filename = nbd_refresh_filename,
|
||||||
.bdrv_co_block_status = nbd_client_co_block_status,
|
.bdrv_co_block_status = nbd_client_co_block_status,
|
||||||
|
.bdrv_dirname = nbd_dirname,
|
||||||
|
.strong_runtime_opts = nbd_strong_runtime_opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_nbd_unix = {
|
static BlockDriver bdrv_nbd_unix = {
|
||||||
|
@ -622,6 +607,8 @@ static BlockDriver bdrv_nbd_unix = {
|
||||||
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
.bdrv_attach_aio_context = nbd_attach_aio_context,
|
||||||
.bdrv_refresh_filename = nbd_refresh_filename,
|
.bdrv_refresh_filename = nbd_refresh_filename,
|
||||||
.bdrv_co_block_status = nbd_client_co_block_status,
|
.bdrv_co_block_status = nbd_client_co_block_status,
|
||||||
|
.bdrv_dirname = nbd_dirname,
|
||||||
|
.strong_runtime_opts = nbd_strong_runtime_opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void bdrv_nbd_init(void)
|
static void bdrv_nbd_init(void)
|
||||||
|
|
54
block/nfs.c
54
block/nfs.c
|
@ -799,14 +799,9 @@ static int nfs_reopen_prepare(BDRVReopenState *state,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nfs_refresh_filename(BlockDriverState *bs, QDict *options)
|
static void nfs_refresh_filename(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
NFSClient *client = bs->opaque;
|
NFSClient *client = bs->opaque;
|
||||||
QDict *opts = qdict_new();
|
|
||||||
QObject *server_qdict;
|
|
||||||
Visitor *ov;
|
|
||||||
|
|
||||||
qdict_put_str(opts, "driver", "nfs");
|
|
||||||
|
|
||||||
if (client->uid && !client->gid) {
|
if (client->uid && !client->gid) {
|
||||||
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||||
|
@ -824,35 +819,20 @@ static void nfs_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||||
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||||
"nfs://%s%s", client->server->host, client->path);
|
"nfs://%s%s", client->server->host, client->path);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ov = qobject_output_visitor_new(&server_qdict);
|
static char *nfs_dirname(BlockDriverState *bs, Error **errp)
|
||||||
visit_type_NFSServer(ov, NULL, &client->server, &error_abort);
|
{
|
||||||
visit_complete(ov, &server_qdict);
|
NFSClient *client = bs->opaque;
|
||||||
qdict_put_obj(opts, "server", server_qdict);
|
|
||||||
qdict_put_str(opts, "path", client->path);
|
|
||||||
|
|
||||||
if (client->uid) {
|
if (client->uid || client->gid) {
|
||||||
qdict_put_int(opts, "user", client->uid);
|
bdrv_refresh_filename(bs);
|
||||||
}
|
error_setg(errp, "Cannot generate a base directory for NFS node '%s'",
|
||||||
if (client->gid) {
|
bs->filename);
|
||||||
qdict_put_int(opts, "group", client->gid);
|
return NULL;
|
||||||
}
|
|
||||||
if (client->tcp_syncnt) {
|
|
||||||
qdict_put_int(opts, "tcp-syn-cnt", client->tcp_syncnt);
|
|
||||||
}
|
|
||||||
if (client->readahead) {
|
|
||||||
qdict_put_int(opts, "readahead-size", client->readahead);
|
|
||||||
}
|
|
||||||
if (client->pagecache) {
|
|
||||||
qdict_put_int(opts, "page-cache-size", client->pagecache);
|
|
||||||
}
|
|
||||||
if (client->debug) {
|
|
||||||
qdict_put_int(opts, "debug", client->debug);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visit_free(ov);
|
return g_strdup_printf("nfs://%s%s/", client->server->host, client->path);
|
||||||
qdict_flatten(opts);
|
|
||||||
bs->full_open_options = opts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LIBNFS_FEATURE_PAGECACHE
|
#ifdef LIBNFS_FEATURE_PAGECACHE
|
||||||
|
@ -864,6 +844,15 @@ static void coroutine_fn nfs_co_invalidate_cache(BlockDriverState *bs,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static const char *nfs_strong_runtime_opts[] = {
|
||||||
|
"path",
|
||||||
|
"user",
|
||||||
|
"group",
|
||||||
|
"server.",
|
||||||
|
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_nfs = {
|
static BlockDriver bdrv_nfs = {
|
||||||
.format_name = "nfs",
|
.format_name = "nfs",
|
||||||
.protocol_name = "nfs",
|
.protocol_name = "nfs",
|
||||||
|
@ -889,6 +878,9 @@ static BlockDriver bdrv_nfs = {
|
||||||
.bdrv_detach_aio_context = nfs_detach_aio_context,
|
.bdrv_detach_aio_context = nfs_detach_aio_context,
|
||||||
.bdrv_attach_aio_context = nfs_attach_aio_context,
|
.bdrv_attach_aio_context = nfs_attach_aio_context,
|
||||||
.bdrv_refresh_filename = nfs_refresh_filename,
|
.bdrv_refresh_filename = nfs_refresh_filename,
|
||||||
|
.bdrv_dirname = nfs_dirname,
|
||||||
|
|
||||||
|
.strong_runtime_opts = nfs_strong_runtime_opts,
|
||||||
|
|
||||||
#ifdef LIBNFS_FEATURE_PAGECACHE
|
#ifdef LIBNFS_FEATURE_PAGECACHE
|
||||||
.bdrv_co_invalidate_cache = nfs_co_invalidate_cache,
|
.bdrv_co_invalidate_cache = nfs_co_invalidate_cache,
|
||||||
|
|
36
block/null.c
36
block/null.c
|
@ -97,10 +97,6 @@ static int null_file_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void null_close(BlockDriverState *bs)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static int64_t null_getlength(BlockDriverState *bs)
|
static int64_t null_getlength(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVNullState *s = bs->opaque;
|
BDRVNullState *s = bs->opaque;
|
||||||
|
@ -243,19 +239,33 @@ static int coroutine_fn null_co_block_status(BlockDriverState *bs,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void null_refresh_filename(BlockDriverState *bs, QDict *opts)
|
static void null_refresh_filename(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
qdict_del(opts, "filename");
|
const QDictEntry *e;
|
||||||
|
|
||||||
if (!qdict_size(opts)) {
|
for (e = qdict_first(bs->full_open_options); e;
|
||||||
snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s://",
|
e = qdict_next(bs->full_open_options, e))
|
||||||
bs->drv->format_name);
|
{
|
||||||
|
/* These options can be ignored */
|
||||||
|
if (strcmp(qdict_entry_key(e), "filename") &&
|
||||||
|
strcmp(qdict_entry_key(e), "driver") &&
|
||||||
|
strcmp(qdict_entry_key(e), NULL_OPT_LATENCY))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qdict_put_str(opts, "driver", bs->drv->format_name);
|
snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s://",
|
||||||
bs->full_open_options = qobject_ref(opts);
|
bs->drv->format_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *const null_strong_runtime_opts[] = {
|
||||||
|
BLOCK_OPT_SIZE,
|
||||||
|
NULL_OPT_ZEROES,
|
||||||
|
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_null_co = {
|
static BlockDriver bdrv_null_co = {
|
||||||
.format_name = "null-co",
|
.format_name = "null-co",
|
||||||
.protocol_name = "null-co",
|
.protocol_name = "null-co",
|
||||||
|
@ -263,7 +273,6 @@ static BlockDriver bdrv_null_co = {
|
||||||
|
|
||||||
.bdrv_file_open = null_file_open,
|
.bdrv_file_open = null_file_open,
|
||||||
.bdrv_parse_filename = null_co_parse_filename,
|
.bdrv_parse_filename = null_co_parse_filename,
|
||||||
.bdrv_close = null_close,
|
|
||||||
.bdrv_getlength = null_getlength,
|
.bdrv_getlength = null_getlength,
|
||||||
|
|
||||||
.bdrv_co_preadv = null_co_preadv,
|
.bdrv_co_preadv = null_co_preadv,
|
||||||
|
@ -274,6 +283,7 @@ static BlockDriver bdrv_null_co = {
|
||||||
.bdrv_co_block_status = null_co_block_status,
|
.bdrv_co_block_status = null_co_block_status,
|
||||||
|
|
||||||
.bdrv_refresh_filename = null_refresh_filename,
|
.bdrv_refresh_filename = null_refresh_filename,
|
||||||
|
.strong_runtime_opts = null_strong_runtime_opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_null_aio = {
|
static BlockDriver bdrv_null_aio = {
|
||||||
|
@ -283,7 +293,6 @@ static BlockDriver bdrv_null_aio = {
|
||||||
|
|
||||||
.bdrv_file_open = null_file_open,
|
.bdrv_file_open = null_file_open,
|
||||||
.bdrv_parse_filename = null_aio_parse_filename,
|
.bdrv_parse_filename = null_aio_parse_filename,
|
||||||
.bdrv_close = null_close,
|
|
||||||
.bdrv_getlength = null_getlength,
|
.bdrv_getlength = null_getlength,
|
||||||
|
|
||||||
.bdrv_aio_preadv = null_aio_preadv,
|
.bdrv_aio_preadv = null_aio_preadv,
|
||||||
|
@ -294,6 +303,7 @@ static BlockDriver bdrv_null_aio = {
|
||||||
.bdrv_co_block_status = null_co_block_status,
|
.bdrv_co_block_status = null_co_block_status,
|
||||||
|
|
||||||
.bdrv_refresh_filename = null_refresh_filename,
|
.bdrv_refresh_filename = null_refresh_filename,
|
||||||
|
.strong_runtime_opts = null_strong_runtime_opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void bdrv_null_init(void)
|
static void bdrv_null_init(void)
|
||||||
|
|
105
block/nvme.c
105
block/nvme.c
|
@ -82,7 +82,7 @@ typedef volatile struct {
|
||||||
uint8_t reserved1[0xec0];
|
uint8_t reserved1[0xec0];
|
||||||
uint8_t cmd_set_specfic[0x100];
|
uint8_t cmd_set_specfic[0x100];
|
||||||
uint32_t doorbells[];
|
uint32_t doorbells[];
|
||||||
} QEMU_PACKED NVMeRegs;
|
} NVMeRegs;
|
||||||
|
|
||||||
QEMU_BUILD_BUG_ON(offsetof(NVMeRegs, doorbells) != 0x1000);
|
QEMU_BUILD_BUG_ON(offsetof(NVMeRegs, doorbells) != 0x1000);
|
||||||
|
|
||||||
|
@ -104,13 +104,16 @@ typedef struct {
|
||||||
uint64_t nsze; /* Namespace size reported by identify command */
|
uint64_t nsze; /* Namespace size reported by identify command */
|
||||||
int nsid; /* The namespace id to read/write data. */
|
int nsid; /* The namespace id to read/write data. */
|
||||||
uint64_t max_transfer;
|
uint64_t max_transfer;
|
||||||
int plugged;
|
bool plugged;
|
||||||
|
|
||||||
CoMutex dma_map_lock;
|
CoMutex dma_map_lock;
|
||||||
CoQueue dma_flush_queue;
|
CoQueue dma_flush_queue;
|
||||||
|
|
||||||
/* Total size of mapped qiov, accessed under dma_map_lock */
|
/* Total size of mapped qiov, accessed under dma_map_lock */
|
||||||
int dma_map_count;
|
int dma_map_count;
|
||||||
|
|
||||||
|
/* PCI address (required for nvme_refresh_filename()) */
|
||||||
|
char *device;
|
||||||
} BDRVNVMeState;
|
} BDRVNVMeState;
|
||||||
|
|
||||||
#define NVME_BLOCK_OPT_DEVICE "device"
|
#define NVME_BLOCK_OPT_DEVICE "device"
|
||||||
|
@ -390,6 +393,7 @@ static void nvme_cmd_sync_cb(void *opaque, int ret)
|
||||||
{
|
{
|
||||||
int *pret = opaque;
|
int *pret = opaque;
|
||||||
*pret = ret;
|
*pret = ret;
|
||||||
|
aio_wait_kick();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nvme_cmd_sync(BlockDriverState *bs, NVMeQueuePair *q,
|
static int nvme_cmd_sync(BlockDriverState *bs, NVMeQueuePair *q,
|
||||||
|
@ -489,10 +493,8 @@ static void nvme_handle_event(EventNotifier *n)
|
||||||
BDRVNVMeState *s = container_of(n, BDRVNVMeState, irq_notifier);
|
BDRVNVMeState *s = container_of(n, BDRVNVMeState, irq_notifier);
|
||||||
|
|
||||||
trace_nvme_handle_event(s);
|
trace_nvme_handle_event(s);
|
||||||
aio_context_acquire(s->aio_context);
|
|
||||||
event_notifier_test_and_clear(n);
|
event_notifier_test_and_clear(n);
|
||||||
nvme_poll_queues(s);
|
nvme_poll_queues(s);
|
||||||
aio_context_release(s->aio_context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool nvme_add_io_queue(BlockDriverState *bs, Error **errp)
|
static bool nvme_add_io_queue(BlockDriverState *bs, Error **errp)
|
||||||
|
@ -558,6 +560,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
|
||||||
|
|
||||||
qemu_co_mutex_init(&s->dma_map_lock);
|
qemu_co_mutex_init(&s->dma_map_lock);
|
||||||
qemu_co_queue_init(&s->dma_flush_queue);
|
qemu_co_queue_init(&s->dma_flush_queue);
|
||||||
|
s->device = g_strdup(device);
|
||||||
s->nsid = namespace;
|
s->nsid = namespace;
|
||||||
s->aio_context = bdrv_get_aio_context(bs);
|
s->aio_context = bdrv_get_aio_context(bs);
|
||||||
ret = event_notifier_init(&s->irq_notifier, 0);
|
ret = event_notifier_init(&s->irq_notifier, 0);
|
||||||
|
@ -569,13 +572,13 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
|
||||||
s->vfio = qemu_vfio_open_pci(device, errp);
|
s->vfio = qemu_vfio_open_pci(device, errp);
|
||||||
if (!s->vfio) {
|
if (!s->vfio) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->regs = qemu_vfio_pci_map_bar(s->vfio, 0, 0, NVME_BAR_SIZE, errp);
|
s->regs = qemu_vfio_pci_map_bar(s->vfio, 0, 0, NVME_BAR_SIZE, errp);
|
||||||
if (!s->regs) {
|
if (!s->regs) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Perform initialize sequence as described in NVMe spec "7.6.1
|
/* Perform initialize sequence as described in NVMe spec "7.6.1
|
||||||
|
@ -585,7 +588,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
|
||||||
if (!(cap & (1ULL << 37))) {
|
if (!(cap & (1ULL << 37))) {
|
||||||
error_setg(errp, "Device doesn't support NVMe command set");
|
error_setg(errp, "Device doesn't support NVMe command set");
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->page_size = MAX(4096, 1 << (12 + ((cap >> 48) & 0xF)));
|
s->page_size = MAX(4096, 1 << (12 + ((cap >> 48) & 0xF)));
|
||||||
|
@ -603,7 +606,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
|
||||||
PRId64 " ms)",
|
PRId64 " ms)",
|
||||||
timeout_ms);
|
timeout_ms);
|
||||||
ret = -ETIMEDOUT;
|
ret = -ETIMEDOUT;
|
||||||
goto fail;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -613,7 +616,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
|
||||||
s->queues[0] = nvme_create_queue_pair(bs, 0, NVME_QUEUE_SIZE, errp);
|
s->queues[0] = nvme_create_queue_pair(bs, 0, NVME_QUEUE_SIZE, errp);
|
||||||
if (!s->queues[0]) {
|
if (!s->queues[0]) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto out;
|
||||||
}
|
}
|
||||||
QEMU_BUILD_BUG_ON(NVME_QUEUE_SIZE & 0xF000);
|
QEMU_BUILD_BUG_ON(NVME_QUEUE_SIZE & 0xF000);
|
||||||
s->regs->aqa = cpu_to_le32((NVME_QUEUE_SIZE << 16) | NVME_QUEUE_SIZE);
|
s->regs->aqa = cpu_to_le32((NVME_QUEUE_SIZE << 16) | NVME_QUEUE_SIZE);
|
||||||
|
@ -633,14 +636,14 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
|
||||||
PRId64 " ms)",
|
PRId64 " ms)",
|
||||||
timeout_ms);
|
timeout_ms);
|
||||||
ret = -ETIMEDOUT;
|
ret = -ETIMEDOUT;
|
||||||
goto fail_queue;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = qemu_vfio_pci_init_irq(s->vfio, &s->irq_notifier,
|
ret = qemu_vfio_pci_init_irq(s->vfio, &s->irq_notifier,
|
||||||
VFIO_PCI_MSIX_IRQ_INDEX, errp);
|
VFIO_PCI_MSIX_IRQ_INDEX, errp);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
goto fail_queue;
|
goto out;
|
||||||
}
|
}
|
||||||
aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier,
|
aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier,
|
||||||
false, nvme_handle_event, nvme_poll_cb);
|
false, nvme_handle_event, nvme_poll_cb);
|
||||||
|
@ -649,30 +652,15 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
goto fail_handler;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set up command queues. */
|
/* Set up command queues. */
|
||||||
if (!nvme_add_io_queue(bs, errp)) {
|
if (!nvme_add_io_queue(bs, errp)) {
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
goto fail_handler;
|
|
||||||
}
|
}
|
||||||
return 0;
|
out:
|
||||||
|
/* Cleaning up is done in nvme_file_open() upon error. */
|
||||||
fail_handler:
|
|
||||||
aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier,
|
|
||||||
false, NULL, NULL);
|
|
||||||
fail_queue:
|
|
||||||
nvme_free_queue_pair(bs, s->queues[0]);
|
|
||||||
fail:
|
|
||||||
g_free(s->queues);
|
|
||||||
if (s->regs) {
|
|
||||||
qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)s->regs, 0, NVME_BAR_SIZE);
|
|
||||||
}
|
|
||||||
if (s->vfio) {
|
|
||||||
qemu_vfio_close(s->vfio);
|
|
||||||
}
|
|
||||||
event_notifier_cleanup(&s->irq_notifier);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -739,10 +727,14 @@ static void nvme_close(BlockDriverState *bs)
|
||||||
for (i = 0; i < s->nr_queues; ++i) {
|
for (i = 0; i < s->nr_queues; ++i) {
|
||||||
nvme_free_queue_pair(bs, s->queues[i]);
|
nvme_free_queue_pair(bs, s->queues[i]);
|
||||||
}
|
}
|
||||||
|
g_free(s->queues);
|
||||||
aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier,
|
aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier,
|
||||||
false, NULL, NULL);
|
false, NULL, NULL);
|
||||||
|
event_notifier_cleanup(&s->irq_notifier);
|
||||||
qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)s->regs, 0, NVME_BAR_SIZE);
|
qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)s->regs, 0, NVME_BAR_SIZE);
|
||||||
qemu_vfio_close(s->vfio);
|
qemu_vfio_close(s->vfio);
|
||||||
|
|
||||||
|
g_free(s->device);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nvme_file_open(BlockDriverState *bs, QDict *options, int flags,
|
static int nvme_file_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
|
@ -852,7 +844,7 @@ try_map:
|
||||||
}
|
}
|
||||||
|
|
||||||
for (j = 0; j < qiov->iov[i].iov_len / s->page_size; j++) {
|
for (j = 0; j < qiov->iov[i].iov_len / s->page_size; j++) {
|
||||||
pagelist[entries++] = iova + j * s->page_size;
|
pagelist[entries++] = cpu_to_le64(iova + j * s->page_size);
|
||||||
}
|
}
|
||||||
trace_nvme_cmd_map_qiov_iov(s, i, qiov->iov[i].iov_base,
|
trace_nvme_cmd_map_qiov_iov(s, i, qiov->iov[i].iov_base,
|
||||||
qiov->iov[i].iov_len / s->page_size);
|
qiov->iov[i].iov_len / s->page_size);
|
||||||
|
@ -865,20 +857,16 @@ try_map:
|
||||||
case 0:
|
case 0:
|
||||||
abort();
|
abort();
|
||||||
case 1:
|
case 1:
|
||||||
cmd->prp1 = cpu_to_le64(pagelist[0]);
|
cmd->prp1 = pagelist[0];
|
||||||
cmd->prp2 = 0;
|
cmd->prp2 = 0;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
cmd->prp1 = cpu_to_le64(pagelist[0]);
|
cmd->prp1 = pagelist[0];
|
||||||
cmd->prp2 = cpu_to_le64(pagelist[1]);;
|
cmd->prp2 = pagelist[1];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
cmd->prp1 = cpu_to_le64(pagelist[0]);
|
cmd->prp1 = pagelist[0];
|
||||||
cmd->prp2 = cpu_to_le64(req->prp_list_iova);
|
cmd->prp2 = cpu_to_le64(req->prp_list_iova + sizeof(uint64_t));
|
||||||
for (i = 0; i < entries - 1; ++i) {
|
|
||||||
pagelist[i] = cpu_to_le64(pagelist[i + 1]);
|
|
||||||
}
|
|
||||||
pagelist[entries - 1] = 0;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
trace_nvme_cmd_map_qiov(s, cmd, req, qiov, entries);
|
trace_nvme_cmd_map_qiov(s, cmd, req, qiov, entries);
|
||||||
|
@ -1071,17 +1059,12 @@ static int nvme_reopen_prepare(BDRVReopenState *reopen_state,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts)
|
static void nvme_refresh_filename(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
qdict_del(opts, "filename");
|
BDRVNVMeState *s = bs->opaque;
|
||||||
|
|
||||||
if (!qdict_size(opts)) {
|
snprintf(bs->exact_filename, sizeof(bs->exact_filename), "nvme://%s/%i",
|
||||||
snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s://",
|
s->device, s->nsid);
|
||||||
bs->drv->format_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
qdict_put_str(opts, "driver", bs->drv->format_name);
|
|
||||||
bs->full_open_options = qobject_ref(opts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nvme_refresh_limits(BlockDriverState *bs, Error **errp)
|
static void nvme_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||||
|
@ -1114,7 +1097,8 @@ static void nvme_attach_aio_context(BlockDriverState *bs,
|
||||||
static void nvme_aio_plug(BlockDriverState *bs)
|
static void nvme_aio_plug(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVNVMeState *s = bs->opaque;
|
BDRVNVMeState *s = bs->opaque;
|
||||||
s->plugged++;
|
assert(!s->plugged);
|
||||||
|
s->plugged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nvme_aio_unplug(BlockDriverState *bs)
|
static void nvme_aio_unplug(BlockDriverState *bs)
|
||||||
|
@ -1122,14 +1106,13 @@ static void nvme_aio_unplug(BlockDriverState *bs)
|
||||||
int i;
|
int i;
|
||||||
BDRVNVMeState *s = bs->opaque;
|
BDRVNVMeState *s = bs->opaque;
|
||||||
assert(s->plugged);
|
assert(s->plugged);
|
||||||
if (!--s->plugged) {
|
s->plugged = false;
|
||||||
for (i = 1; i < s->nr_queues; i++) {
|
for (i = 1; i < s->nr_queues; i++) {
|
||||||
NVMeQueuePair *q = s->queues[i];
|
NVMeQueuePair *q = s->queues[i];
|
||||||
qemu_mutex_lock(&q->lock);
|
qemu_mutex_lock(&q->lock);
|
||||||
nvme_kick(s, q);
|
nvme_kick(s, q);
|
||||||
nvme_process_completion(s, q);
|
nvme_process_completion(s, q);
|
||||||
qemu_mutex_unlock(&q->lock);
|
qemu_mutex_unlock(&q->lock);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1154,6 +1137,13 @@ static void nvme_unregister_buf(BlockDriverState *bs, void *host)
|
||||||
qemu_vfio_dma_unmap(s->vfio, host);
|
qemu_vfio_dma_unmap(s->vfio, host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *const nvme_strong_runtime_opts[] = {
|
||||||
|
NVME_BLOCK_OPT_DEVICE,
|
||||||
|
NVME_BLOCK_OPT_NAMESPACE,
|
||||||
|
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_nvme = {
|
static BlockDriver bdrv_nvme = {
|
||||||
.format_name = "nvme",
|
.format_name = "nvme",
|
||||||
.protocol_name = "nvme",
|
.protocol_name = "nvme",
|
||||||
|
@ -1171,6 +1161,7 @@ static BlockDriver bdrv_nvme = {
|
||||||
|
|
||||||
.bdrv_refresh_filename = nvme_refresh_filename,
|
.bdrv_refresh_filename = nvme_refresh_filename,
|
||||||
.bdrv_refresh_limits = nvme_refresh_limits,
|
.bdrv_refresh_limits = nvme_refresh_limits,
|
||||||
|
.strong_runtime_opts = nvme_strong_runtime_opts,
|
||||||
|
|
||||||
.bdrv_detach_aio_context = nvme_detach_aio_context,
|
.bdrv_detach_aio_context = nvme_detach_aio_context,
|
||||||
.bdrv_attach_aio_context = nvme_attach_aio_context,
|
.bdrv_attach_aio_context = nvme_attach_aio_context,
|
||||||
|
|
|
@ -220,23 +220,20 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num,
|
||||||
if (bs->backing) {
|
if (bs->backing) {
|
||||||
int64_t nb_cow_sectors = to_allocate * s->tracks;
|
int64_t nb_cow_sectors = to_allocate * s->tracks;
|
||||||
int64_t nb_cow_bytes = nb_cow_sectors << BDRV_SECTOR_BITS;
|
int64_t nb_cow_bytes = nb_cow_sectors << BDRV_SECTOR_BITS;
|
||||||
QEMUIOVector qiov;
|
QEMUIOVector qiov =
|
||||||
struct iovec iov = {
|
QEMU_IOVEC_INIT_BUF(qiov, qemu_blockalign(bs, nb_cow_bytes),
|
||||||
.iov_len = nb_cow_bytes,
|
nb_cow_bytes);
|
||||||
.iov_base = qemu_blockalign(bs, nb_cow_bytes)
|
|
||||||
};
|
|
||||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
|
||||||
|
|
||||||
ret = bdrv_co_preadv(bs->backing, idx * s->tracks * BDRV_SECTOR_SIZE,
|
ret = bdrv_co_preadv(bs->backing, idx * s->tracks * BDRV_SECTOR_SIZE,
|
||||||
nb_cow_bytes, &qiov, 0);
|
nb_cow_bytes, &qiov, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
qemu_vfree(iov.iov_base);
|
qemu_vfree(qemu_iovec_buf(&qiov));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_co_pwritev(bs->file, s->data_end * BDRV_SECTOR_SIZE,
|
ret = bdrv_co_pwritev(bs->file, s->data_end * BDRV_SECTOR_SIZE,
|
||||||
nb_cow_bytes, &qiov, 0);
|
nb_cow_bytes, &qiov, 0);
|
||||||
qemu_vfree(iov.iov_base);
|
qemu_vfree(qemu_iovec_buf(&qiov));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
38
block/qapi.c
38
block/qapi.c
|
@ -51,6 +51,8 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bdrv_refresh_filename(bs);
|
||||||
|
|
||||||
info = g_malloc0(sizeof(*info));
|
info = g_malloc0(sizeof(*info));
|
||||||
info->file = g_strdup(bs->filename);
|
info->file = g_strdup(bs->filename);
|
||||||
info->ro = bs->read_only;
|
info->ro = bs->read_only;
|
||||||
|
@ -264,6 +266,8 @@ void bdrv_query_image_info(BlockDriverState *bs,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bdrv_refresh_filename(bs);
|
||||||
|
|
||||||
info = g_new0(ImageInfo, 1);
|
info = g_new0(ImageInfo, 1);
|
||||||
info->filename = g_strdup(bs->filename);
|
info->filename = g_strdup(bs->filename);
|
||||||
info->format = g_strdup(bdrv_get_format_name(bs));
|
info->format = g_strdup(bdrv_get_format_name(bs));
|
||||||
|
@ -282,23 +286,20 @@ void bdrv_query_image_info(BlockDriverState *bs,
|
||||||
info->dirty_flag = bdi.is_dirty;
|
info->dirty_flag = bdi.is_dirty;
|
||||||
info->has_dirty_flag = true;
|
info->has_dirty_flag = true;
|
||||||
}
|
}
|
||||||
info->format_specific = bdrv_get_specific_info(bs);
|
info->format_specific = bdrv_get_specific_info(bs, &err);
|
||||||
|
if (err) {
|
||||||
|
error_propagate(errp, err);
|
||||||
|
qapi_free_ImageInfo(info);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
info->has_format_specific = info->format_specific != NULL;
|
info->has_format_specific = info->format_specific != NULL;
|
||||||
|
|
||||||
backing_filename = bs->backing_file;
|
backing_filename = bs->backing_file;
|
||||||
if (backing_filename[0] != '\0') {
|
if (backing_filename[0] != '\0') {
|
||||||
char *backing_filename2 = g_malloc0(PATH_MAX);
|
char *backing_filename2;
|
||||||
info->backing_filename = g_strdup(backing_filename);
|
info->backing_filename = g_strdup(backing_filename);
|
||||||
info->has_backing_filename = true;
|
info->has_backing_filename = true;
|
||||||
bdrv_get_full_backing_filename(bs, backing_filename2, PATH_MAX, &err);
|
backing_filename2 = bdrv_get_full_backing_filename(bs, NULL);
|
||||||
if (err) {
|
|
||||||
/* Can't reconstruct the full backing filename, so we must omit
|
|
||||||
* this field and apply a Best Effort to this query. */
|
|
||||||
g_free(backing_filename2);
|
|
||||||
backing_filename2 = NULL;
|
|
||||||
error_free(err);
|
|
||||||
err = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Always report the full_backing_filename if present, even if it's the
|
/* Always report the full_backing_filename if present, even if it's the
|
||||||
* same as backing_filename. That they are same is useful info. */
|
* same as backing_filename. That they are same is useful info. */
|
||||||
|
@ -492,14 +493,14 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
|
||||||
}
|
}
|
||||||
|
|
||||||
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_READ],
|
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_READ],
|
||||||
&ds->has_x_rd_latency_histogram,
|
&ds->has_rd_latency_histogram,
|
||||||
&ds->x_rd_latency_histogram);
|
&ds->rd_latency_histogram);
|
||||||
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_WRITE],
|
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_WRITE],
|
||||||
&ds->has_x_wr_latency_histogram,
|
&ds->has_wr_latency_histogram,
|
||||||
&ds->x_wr_latency_histogram);
|
&ds->wr_latency_histogram);
|
||||||
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_FLUSH],
|
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_FLUSH],
|
||||||
&ds->has_x_flush_latency_histogram,
|
&ds->has_flush_latency_histogram,
|
||||||
&ds->x_flush_latency_histogram);
|
&ds->flush_latency_histogram);
|
||||||
}
|
}
|
||||||
|
|
||||||
static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs,
|
static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs,
|
||||||
|
@ -594,7 +595,7 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) {
|
for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) {
|
||||||
BlockStatsList *info = g_malloc0(sizeof(*info));
|
BlockStatsList *info;
|
||||||
AioContext *ctx = blk_get_aio_context(blk);
|
AioContext *ctx = blk_get_aio_context(blk);
|
||||||
BlockStats *s;
|
BlockStats *s;
|
||||||
char *qdev;
|
char *qdev;
|
||||||
|
@ -619,6 +620,7 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes,
|
||||||
bdrv_query_blk_stats(s->stats, blk);
|
bdrv_query_blk_stats(s->stats, blk);
|
||||||
aio_context_release(ctx);
|
aio_context_release(ctx);
|
||||||
|
|
||||||
|
info = g_malloc0(sizeof(*info));
|
||||||
info->value = s;
|
info->value = s;
|
||||||
*p_next = info;
|
*p_next = info;
|
||||||
p_next = &info->next;
|
p_next = &info->next;
|
||||||
|
|
55
block/qcow.c
55
block/qcow.c
|
@ -31,6 +31,7 @@
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
#include "qemu/bswap.h"
|
#include "qemu/bswap.h"
|
||||||
|
#include "qemu/cutils.h"
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
#include "qapi/qmp/qstring.h"
|
#include "qapi/qmp/qstring.h"
|
||||||
|
@ -140,14 +141,14 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
be32_to_cpus(&header.magic);
|
header.magic = be32_to_cpu(header.magic);
|
||||||
be32_to_cpus(&header.version);
|
header.version = be32_to_cpu(header.version);
|
||||||
be64_to_cpus(&header.backing_file_offset);
|
header.backing_file_offset = be64_to_cpu(header.backing_file_offset);
|
||||||
be32_to_cpus(&header.backing_file_size);
|
header.backing_file_size = be32_to_cpu(header.backing_file_size);
|
||||||
be32_to_cpus(&header.mtime);
|
header.mtime = be32_to_cpu(header.mtime);
|
||||||
be64_to_cpus(&header.size);
|
header.size = be64_to_cpu(header.size);
|
||||||
be32_to_cpus(&header.crypt_method);
|
header.crypt_method = be32_to_cpu(header.crypt_method);
|
||||||
be64_to_cpus(&header.l1_table_offset);
|
header.l1_table_offset = be64_to_cpu(header.l1_table_offset);
|
||||||
|
|
||||||
if (header.magic != QCOW_MAGIC) {
|
if (header.magic != QCOW_MAGIC) {
|
||||||
error_setg(errp, "Image not in qcow format");
|
error_setg(errp, "Image not in qcow format");
|
||||||
|
@ -213,7 +214,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
cflags |= QCRYPTO_BLOCK_OPEN_NO_IO;
|
cflags |= QCRYPTO_BLOCK_OPEN_NO_IO;
|
||||||
}
|
}
|
||||||
s->crypto = qcrypto_block_open(crypto_opts, "encrypt.",
|
s->crypto = qcrypto_block_open(crypto_opts, "encrypt.",
|
||||||
NULL, NULL, cflags, errp);
|
NULL, NULL, cflags, 1, errp);
|
||||||
if (!s->crypto) {
|
if (!s->crypto) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -270,7 +271,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
}
|
}
|
||||||
|
|
||||||
for(i = 0;i < s->l1_size; i++) {
|
for(i = 0;i < s->l1_size; i++) {
|
||||||
be64_to_cpus(&s->l1_table[i]);
|
s->l1_table[i] = be64_to_cpu(s->l1_table[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* alloc L2 cache (max. 64k * 16 * 8 = 8 MB) */
|
/* alloc L2 cache (max. 64k * 16 * 8 = 8 MB) */
|
||||||
|
@ -295,11 +296,13 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
ret = bdrv_pread(bs->file, header.backing_file_offset,
|
ret = bdrv_pread(bs->file, header.backing_file_offset,
|
||||||
bs->backing_file, len);
|
bs->auto_backing_file, len);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
bs->backing_file[len] = '\0';
|
bs->auto_backing_file[len] = '\0';
|
||||||
|
pstrcpy(bs->backing_file, sizeof(bs->backing_file),
|
||||||
|
bs->auto_backing_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Disable migration when qcow images are used */
|
/* Disable migration when qcow images are used */
|
||||||
|
@ -628,7 +631,6 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||||
int offset_in_cluster;
|
int offset_in_cluster;
|
||||||
int ret = 0, n;
|
int ret = 0, n;
|
||||||
uint64_t cluster_offset;
|
uint64_t cluster_offset;
|
||||||
struct iovec hd_iov;
|
|
||||||
QEMUIOVector hd_qiov;
|
QEMUIOVector hd_qiov;
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
void *orig_buf;
|
void *orig_buf;
|
||||||
|
@ -661,9 +663,7 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||||
if (!cluster_offset) {
|
if (!cluster_offset) {
|
||||||
if (bs->backing) {
|
if (bs->backing) {
|
||||||
/* read from the base image */
|
/* read from the base image */
|
||||||
hd_iov.iov_base = (void *)buf;
|
qemu_iovec_init_buf(&hd_qiov, buf, n);
|
||||||
hd_iov.iov_len = n;
|
|
||||||
qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
|
|
||||||
qemu_co_mutex_unlock(&s->lock);
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
/* qcow2 emits this on bs->file instead of bs->backing */
|
/* qcow2 emits this on bs->file instead of bs->backing */
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
|
BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
|
||||||
|
@ -688,9 +688,7 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
hd_iov.iov_base = (void *)buf;
|
qemu_iovec_init_buf(&hd_qiov, buf, n);
|
||||||
hd_iov.iov_len = n;
|
|
||||||
qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
|
|
||||||
qemu_co_mutex_unlock(&s->lock);
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
|
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
|
||||||
ret = bdrv_co_preadv(bs->file, cluster_offset + offset_in_cluster,
|
ret = bdrv_co_preadv(bs->file, cluster_offset + offset_in_cluster,
|
||||||
|
@ -733,7 +731,6 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
||||||
int offset_in_cluster;
|
int offset_in_cluster;
|
||||||
uint64_t cluster_offset;
|
uint64_t cluster_offset;
|
||||||
int ret = 0, n;
|
int ret = 0, n;
|
||||||
struct iovec hd_iov;
|
|
||||||
QEMUIOVector hd_qiov;
|
QEMUIOVector hd_qiov;
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
void *orig_buf;
|
void *orig_buf;
|
||||||
|
@ -779,9 +776,7 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hd_iov.iov_base = (void *)buf;
|
qemu_iovec_init_buf(&hd_qiov, buf, n);
|
||||||
hd_iov.iov_len = n;
|
|
||||||
qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
|
|
||||||
qemu_co_mutex_unlock(&s->lock);
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
||||||
ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster,
|
ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster,
|
||||||
|
@ -1062,7 +1057,6 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcowState *s = bs->opaque;
|
||||||
QEMUIOVector hd_qiov;
|
QEMUIOVector hd_qiov;
|
||||||
struct iovec iov;
|
|
||||||
z_stream strm;
|
z_stream strm;
|
||||||
int ret, out_len;
|
int ret, out_len;
|
||||||
uint8_t *buf, *out_buf;
|
uint8_t *buf, *out_buf;
|
||||||
|
@ -1128,11 +1122,7 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
|
||||||
}
|
}
|
||||||
cluster_offset &= s->cluster_offset_mask;
|
cluster_offset &= s->cluster_offset_mask;
|
||||||
|
|
||||||
iov = (struct iovec) {
|
qemu_iovec_init_buf(&hd_qiov, out_buf, out_len);
|
||||||
.iov_base = out_buf,
|
|
||||||
.iov_len = out_len,
|
|
||||||
};
|
|
||||||
qemu_iovec_init_external(&hd_qiov, &iov, 1);
|
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED);
|
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED);
|
||||||
ret = bdrv_co_pwritev(bs->file, cluster_offset, out_len, &hd_qiov, 0);
|
ret = bdrv_co_pwritev(bs->file, cluster_offset, out_len, &hd_qiov, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -1183,6 +1173,12 @@ static QemuOptsList qcow_create_opts = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const char *const qcow_strong_runtime_opts[] = {
|
||||||
|
"encrypt." BLOCK_CRYPTO_OPT_QCOW_KEY_SECRET,
|
||||||
|
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
static BlockDriver bdrv_qcow = {
|
static BlockDriver bdrv_qcow = {
|
||||||
.format_name = "qcow",
|
.format_name = "qcow",
|
||||||
.instance_size = sizeof(BDRVQcowState),
|
.instance_size = sizeof(BDRVQcowState),
|
||||||
|
@ -1206,6 +1202,7 @@ static BlockDriver bdrv_qcow = {
|
||||||
.bdrv_get_info = qcow_get_info,
|
.bdrv_get_info = qcow_get_info,
|
||||||
|
|
||||||
.create_opts = &qcow_create_opts,
|
.create_opts = &qcow_create_opts,
|
||||||
|
.strong_runtime_opts = qcow_strong_runtime_opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void bdrv_qcow_init(void)
|
static void bdrv_qcow_init(void)
|
||||||
|
|
|
@ -77,8 +77,6 @@ typedef struct Qcow2BitmapTable {
|
||||||
uint32_t size; /* number of 64bit entries */
|
uint32_t size; /* number of 64bit entries */
|
||||||
QSIMPLEQ_ENTRY(Qcow2BitmapTable) entry;
|
QSIMPLEQ_ENTRY(Qcow2BitmapTable) entry;
|
||||||
} Qcow2BitmapTable;
|
} Qcow2BitmapTable;
|
||||||
typedef QSIMPLEQ_HEAD(Qcow2BitmapTableList, Qcow2BitmapTable)
|
|
||||||
Qcow2BitmapTableList;
|
|
||||||
|
|
||||||
typedef struct Qcow2Bitmap {
|
typedef struct Qcow2Bitmap {
|
||||||
Qcow2BitmapTable table;
|
Qcow2BitmapTable table;
|
||||||
|
@ -118,7 +116,7 @@ static inline void bitmap_table_to_be(uint64_t *bitmap_table, size_t size)
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
for (i = 0; i < size; ++i) {
|
for (i = 0; i < size; ++i) {
|
||||||
cpu_to_be64s(&bitmap_table[i]);
|
bitmap_table[i] = cpu_to_be64(bitmap_table[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +229,7 @@ static int bitmap_table_load(BlockDriverState *bs, Qcow2BitmapTable *tb,
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < tb->size; ++i) {
|
for (i = 0; i < tb->size; ++i) {
|
||||||
be64_to_cpus(&table[i]);
|
table[i] = be64_to_cpu(table[i]);
|
||||||
ret = check_table_entry(table[i], s->cluster_size);
|
ret = check_table_entry(table[i], s->cluster_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -345,11 +343,17 @@ static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs,
|
||||||
uint32_t granularity;
|
uint32_t granularity;
|
||||||
BdrvDirtyBitmap *bitmap = NULL;
|
BdrvDirtyBitmap *bitmap = NULL;
|
||||||
|
|
||||||
if (bm->flags & BME_FLAG_IN_USE) {
|
granularity = 1U << bm->granularity_bits;
|
||||||
error_setg(errp, "Bitmap '%s' is in use", bm->name);
|
bitmap = bdrv_create_dirty_bitmap(bs, granularity, bm->name, errp);
|
||||||
|
if (bitmap == NULL) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bm->flags & BME_FLAG_IN_USE) {
|
||||||
|
/* Data is unusable, skip loading it */
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
ret = bitmap_table_load(bs, &bm->table, &bitmap_table);
|
ret = bitmap_table_load(bs, &bm->table, &bitmap_table);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret,
|
error_setg_errno(errp, -ret,
|
||||||
|
@ -358,12 +362,6 @@ static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
granularity = 1U << bm->granularity_bits;
|
|
||||||
bitmap = bdrv_create_dirty_bitmap(bs, granularity, bm->name, errp);
|
|
||||||
if (bitmap == NULL) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = load_bitmap_data(bs, bitmap_table, bm->table.size, bitmap);
|
ret = load_bitmap_data(bs, bitmap_table, bm->table.size, bitmap);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not read bitmap '%s' from image",
|
error_setg_errno(errp, -ret, "Could not read bitmap '%s' from image",
|
||||||
|
@ -394,20 +392,20 @@ fail:
|
||||||
|
|
||||||
static inline void bitmap_dir_entry_to_cpu(Qcow2BitmapDirEntry *entry)
|
static inline void bitmap_dir_entry_to_cpu(Qcow2BitmapDirEntry *entry)
|
||||||
{
|
{
|
||||||
be64_to_cpus(&entry->bitmap_table_offset);
|
entry->bitmap_table_offset = be64_to_cpu(entry->bitmap_table_offset);
|
||||||
be32_to_cpus(&entry->bitmap_table_size);
|
entry->bitmap_table_size = be32_to_cpu(entry->bitmap_table_size);
|
||||||
be32_to_cpus(&entry->flags);
|
entry->flags = be32_to_cpu(entry->flags);
|
||||||
be16_to_cpus(&entry->name_size);
|
entry->name_size = be16_to_cpu(entry->name_size);
|
||||||
be32_to_cpus(&entry->extra_data_size);
|
entry->extra_data_size = be32_to_cpu(entry->extra_data_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void bitmap_dir_entry_to_be(Qcow2BitmapDirEntry *entry)
|
static inline void bitmap_dir_entry_to_be(Qcow2BitmapDirEntry *entry)
|
||||||
{
|
{
|
||||||
cpu_to_be64s(&entry->bitmap_table_offset);
|
entry->bitmap_table_offset = cpu_to_be64(entry->bitmap_table_offset);
|
||||||
cpu_to_be32s(&entry->bitmap_table_size);
|
entry->bitmap_table_size = cpu_to_be32(entry->bitmap_table_size);
|
||||||
cpu_to_be32s(&entry->flags);
|
entry->flags = cpu_to_be32(entry->flags);
|
||||||
cpu_to_be16s(&entry->name_size);
|
entry->name_size = cpu_to_be16(entry->name_size);
|
||||||
cpu_to_be32s(&entry->extra_data_size);
|
entry->extra_data_size = cpu_to_be32(entry->extra_data_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int calc_dir_entry_size(size_t name_size, size_t extra_data_size)
|
static inline int calc_dir_entry_size(size_t name_size, size_t extra_data_size)
|
||||||
|
@ -464,10 +462,25 @@ static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry)
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
fail = (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) ||
|
if (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) {
|
||||||
(len > ((phys_bitmap_bytes * 8) << entry->granularity_bits));
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
return fail ? -EINVAL : 0;
|
if (!(entry->flags & BME_FLAG_IN_USE) &&
|
||||||
|
(len > ((phys_bitmap_bytes * 8) << entry->granularity_bits)))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We've loaded a valid bitmap (IN_USE not set) or we are going to
|
||||||
|
* store a valid bitmap, but the allocated bitmap table size is not
|
||||||
|
* enough to store this bitmap.
|
||||||
|
*
|
||||||
|
* Note, that it's OK to have an invalid bitmap with invalid size due
|
||||||
|
* to a bitmap that was not correctly saved after image resize.
|
||||||
|
*/
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void bitmap_directory_to_be(uint8_t *dir, size_t size)
|
static inline void bitmap_directory_to_be(uint8_t *dir, size_t size)
|
||||||
|
@ -780,7 +793,8 @@ static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list,
|
||||||
* directory in-place (actually, turn-off the extension), which is checked
|
* directory in-place (actually, turn-off the extension), which is checked
|
||||||
* in qcow2_check_metadata_overlap() */
|
* in qcow2_check_metadata_overlap() */
|
||||||
ret = qcow2_pre_write_overlap_check(
|
ret = qcow2_pre_write_overlap_check(
|
||||||
bs, in_place ? QCOW2_OL_BITMAP_DIRECTORY : 0, dir_offset, dir_size);
|
bs, in_place ? QCOW2_OL_BITMAP_DIRECTORY : 0, dir_offset, dir_size,
|
||||||
|
false);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -951,6 +965,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||||
Qcow2Bitmap *bm;
|
Qcow2Bitmap *bm;
|
||||||
GSList *created_dirty_bitmaps = NULL;
|
GSList *created_dirty_bitmaps = NULL;
|
||||||
bool header_updated = false;
|
bool header_updated = false;
|
||||||
|
bool needs_update = false;
|
||||||
|
|
||||||
if (s->nb_bitmaps == 0) {
|
if (s->nb_bitmaps == 0) {
|
||||||
/* No bitmaps - nothing to do */
|
/* No bitmaps - nothing to do */
|
||||||
|
@ -964,35 +979,39 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||||
}
|
}
|
||||||
|
|
||||||
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
||||||
if (!(bm->flags & BME_FLAG_IN_USE)) {
|
BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp);
|
||||||
BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp);
|
if (bitmap == NULL) {
|
||||||
if (bitmap == NULL) {
|
goto fail;
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(bm->flags & BME_FLAG_AUTO)) {
|
|
||||||
bdrv_disable_dirty_bitmap(bitmap);
|
|
||||||
}
|
|
||||||
bdrv_dirty_bitmap_set_persistance(bitmap, true);
|
|
||||||
bm->flags |= BME_FLAG_IN_USE;
|
|
||||||
created_dirty_bitmaps =
|
|
||||||
g_slist_append(created_dirty_bitmaps, bitmap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bdrv_dirty_bitmap_set_persistence(bitmap, true);
|
||||||
|
if (bm->flags & BME_FLAG_IN_USE) {
|
||||||
|
bdrv_dirty_bitmap_set_inconsistent(bitmap);
|
||||||
|
} else {
|
||||||
|
/* NB: updated flags only get written if can_write(bs) is true. */
|
||||||
|
bm->flags |= BME_FLAG_IN_USE;
|
||||||
|
needs_update = true;
|
||||||
|
}
|
||||||
|
if (!(bm->flags & BME_FLAG_AUTO)) {
|
||||||
|
bdrv_disable_dirty_bitmap(bitmap);
|
||||||
|
}
|
||||||
|
created_dirty_bitmaps =
|
||||||
|
g_slist_append(created_dirty_bitmaps, bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (created_dirty_bitmaps != NULL) {
|
if (needs_update && can_write(bs)) {
|
||||||
if (can_write(bs)) {
|
/* in_use flags must be updated */
|
||||||
/* in_use flags must be updated */
|
int ret = update_ext_header_and_dir_in_place(bs, bm_list);
|
||||||
int ret = update_ext_header_and_dir_in_place(bs, bm_list);
|
if (ret < 0) {
|
||||||
if (ret < 0) {
|
error_setg_errno(errp, -ret, "Can't update bitmap directory");
|
||||||
error_setg_errno(errp, -ret, "Can't update bitmap directory");
|
goto fail;
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
header_updated = true;
|
|
||||||
} else {
|
|
||||||
g_slist_foreach(created_dirty_bitmaps, set_readonly_helper,
|
|
||||||
(gpointer)true);
|
|
||||||
}
|
}
|
||||||
|
header_updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!can_write(bs)) {
|
||||||
|
g_slist_foreach(created_dirty_bitmaps, set_readonly_helper,
|
||||||
|
(gpointer)true);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_slist_free(created_dirty_bitmaps);
|
g_slist_free(created_dirty_bitmaps);
|
||||||
|
@ -1008,6 +1027,82 @@ fail:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Qcow2BitmapInfoFlagsList *get_bitmap_info_flags(uint32_t flags)
|
||||||
|
{
|
||||||
|
Qcow2BitmapInfoFlagsList *list = NULL;
|
||||||
|
Qcow2BitmapInfoFlagsList **plist = &list;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
int bme; /* Bitmap directory entry flags */
|
||||||
|
int info; /* The flags to report to the user */
|
||||||
|
} map[] = {
|
||||||
|
{ BME_FLAG_IN_USE, QCOW2_BITMAP_INFO_FLAGS_IN_USE },
|
||||||
|
{ BME_FLAG_AUTO, QCOW2_BITMAP_INFO_FLAGS_AUTO },
|
||||||
|
};
|
||||||
|
|
||||||
|
int map_size = ARRAY_SIZE(map);
|
||||||
|
|
||||||
|
for (i = 0; i < map_size; ++i) {
|
||||||
|
if (flags & map[i].bme) {
|
||||||
|
Qcow2BitmapInfoFlagsList *entry =
|
||||||
|
g_new0(Qcow2BitmapInfoFlagsList, 1);
|
||||||
|
entry->value = map[i].info;
|
||||||
|
*plist = entry;
|
||||||
|
plist = &entry->next;
|
||||||
|
flags &= ~map[i].bme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Check if the BME_* mapping above is complete */
|
||||||
|
assert(!flags);
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* qcow2_get_bitmap_info_list()
|
||||||
|
* Returns a list of QCOW2 bitmap details.
|
||||||
|
* In case of no bitmaps, the function returns NULL and
|
||||||
|
* the @errp parameter is not set.
|
||||||
|
* When bitmap information can not be obtained, the function returns
|
||||||
|
* NULL and the @errp parameter is set.
|
||||||
|
*/
|
||||||
|
Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
BDRVQcow2State *s = bs->opaque;
|
||||||
|
Qcow2BitmapList *bm_list;
|
||||||
|
Qcow2Bitmap *bm;
|
||||||
|
Qcow2BitmapInfoList *list = NULL;
|
||||||
|
Qcow2BitmapInfoList **plist = &list;
|
||||||
|
|
||||||
|
if (s->nb_bitmaps == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
|
||||||
|
s->bitmap_directory_size, errp);
|
||||||
|
if (bm_list == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
||||||
|
Qcow2BitmapInfo *info = g_new0(Qcow2BitmapInfo, 1);
|
||||||
|
Qcow2BitmapInfoList *obj = g_new0(Qcow2BitmapInfoList, 1);
|
||||||
|
info->granularity = 1U << bm->granularity_bits;
|
||||||
|
info->name = g_strdup(bm->name);
|
||||||
|
info->flags = get_bitmap_info_flags(bm->flags & ~BME_RESERVED_FLAGS);
|
||||||
|
obj->value = info;
|
||||||
|
*plist = obj;
|
||||||
|
plist = &obj->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitmap_list_free(bm_list);
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
|
int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
|
@ -1038,23 +1133,21 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
|
||||||
}
|
}
|
||||||
|
|
||||||
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
||||||
if (!(bm->flags & BME_FLAG_IN_USE)) {
|
BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name);
|
||||||
BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name);
|
if (bitmap == NULL) {
|
||||||
if (bitmap == NULL) {
|
continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bdrv_dirty_bitmap_readonly(bitmap)) {
|
|
||||||
error_setg(errp, "Bitmap %s is not readonly but not marked"
|
|
||||||
"'IN_USE' in the image. Something went wrong,"
|
|
||||||
"all the bitmaps may be corrupted", bm->name);
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
bm->flags |= BME_FLAG_IN_USE;
|
|
||||||
ro_dirty_bitmaps = g_slist_append(ro_dirty_bitmaps, bitmap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!bdrv_dirty_bitmap_readonly(bitmap)) {
|
||||||
|
error_setg(errp, "Bitmap %s was loaded prior to rw-reopen, but was "
|
||||||
|
"not marked as readonly. This is a bug, something went "
|
||||||
|
"wrong. All of the bitmaps may be corrupted", bm->name);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bm->flags |= BME_FLAG_IN_USE;
|
||||||
|
ro_dirty_bitmaps = g_slist_append(ro_dirty_bitmaps, bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ro_dirty_bitmaps != NULL) {
|
if (ro_dirty_bitmaps != NULL) {
|
||||||
|
@ -1082,6 +1175,52 @@ int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp)
|
||||||
return qcow2_reopen_bitmaps_rw_hint(bs, NULL, errp);
|
return qcow2_reopen_bitmaps_rw_hint(bs, NULL, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Checks to see if it's safe to resize bitmaps */
|
||||||
|
int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp)
|
||||||
|
{
|
||||||
|
BDRVQcow2State *s = bs->opaque;
|
||||||
|
Qcow2BitmapList *bm_list;
|
||||||
|
Qcow2Bitmap *bm;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (s->nb_bitmaps == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
|
||||||
|
s->bitmap_directory_size, errp);
|
||||||
|
if (bm_list == NULL) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
||||||
|
BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name);
|
||||||
|
if (bitmap == NULL) {
|
||||||
|
/*
|
||||||
|
* We rely on all bitmaps being in-memory to be able to resize them,
|
||||||
|
* Otherwise, we'd need to resize them on disk explicitly
|
||||||
|
*/
|
||||||
|
error_setg(errp, "Cannot resize qcow2 with persistent bitmaps that "
|
||||||
|
"were not loaded into memory");
|
||||||
|
ret = -ENOTSUP;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The checks against readonly and busy are redundant, but certainly
|
||||||
|
* do no harm. checks against inconsistent are crucial:
|
||||||
|
*/
|
||||||
|
if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) {
|
||||||
|
ret = -ENOTSUP;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
bitmap_list_free(bm_list);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* store_bitmap_data()
|
/* store_bitmap_data()
|
||||||
* Store bitmap to image, filling bitmap table accordingly.
|
* Store bitmap to image, filling bitmap table accordingly.
|
||||||
*/
|
*/
|
||||||
|
@ -1150,7 +1289,7 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs,
|
||||||
memset(buf + write_size, 0, s->cluster_size - write_size);
|
memset(buf + write_size, 0, s->cluster_size - write_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = qcow2_pre_write_overlap_check(bs, 0, off, s->cluster_size);
|
ret = qcow2_pre_write_overlap_check(bs, 0, off, s->cluster_size, false);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Qcow2 overlap check failed");
|
error_setg_errno(errp, -ret, "Qcow2 overlap check failed");
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -1218,7 +1357,7 @@ static int store_bitmap(BlockDriverState *bs, Qcow2Bitmap *bm, Error **errp)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = qcow2_pre_write_overlap_check(bs, 0, tb_offset,
|
ret = qcow2_pre_write_overlap_check(bs, 0, tb_offset,
|
||||||
tb_size * sizeof(tb[0]));
|
tb_size * sizeof(tb[0]), false);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Qcow2 overlap check failed");
|
error_setg_errno(errp, -ret, "Qcow2 overlap check failed");
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -1316,7 +1455,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||||
int ret;
|
int ret;
|
||||||
Qcow2BitmapList *bm_list;
|
Qcow2BitmapList *bm_list;
|
||||||
Qcow2Bitmap *bm;
|
Qcow2Bitmap *bm;
|
||||||
Qcow2BitmapTableList drop_tables;
|
QSIMPLEQ_HEAD(, Qcow2BitmapTable) drop_tables;
|
||||||
Qcow2BitmapTable *tb, *tb_next;
|
Qcow2BitmapTable *tb, *tb_next;
|
||||||
|
|
||||||
if (!bdrv_has_changed_persistent_bitmaps(bs)) {
|
if (!bdrv_has_changed_persistent_bitmaps(bs)) {
|
||||||
|
@ -1349,9 +1488,9 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||||
uint32_t granularity = bdrv_dirty_bitmap_granularity(bitmap);
|
uint32_t granularity = bdrv_dirty_bitmap_granularity(bitmap);
|
||||||
Qcow2Bitmap *bm;
|
Qcow2Bitmap *bm;
|
||||||
|
|
||||||
if (!bdrv_dirty_bitmap_get_persistance(bitmap) ||
|
if (!bdrv_dirty_bitmap_get_persistence(bitmap) ||
|
||||||
bdrv_dirty_bitmap_readonly(bitmap))
|
bdrv_dirty_bitmap_readonly(bitmap) ||
|
||||||
{
|
bdrv_dirty_bitmap_inconsistent(bitmap)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1418,6 +1557,22 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||||
g_free(tb);
|
g_free(tb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
||||||
|
/* For safety, we remove bitmap after storing.
|
||||||
|
* We may be here in two cases:
|
||||||
|
* 1. bdrv_close. It's ok to drop bitmap.
|
||||||
|
* 2. inactivation. It means migration without 'dirty-bitmaps'
|
||||||
|
* capability, so bitmaps are not marked with
|
||||||
|
* BdrvDirtyBitmap.migration flags. It's not bad to drop them too,
|
||||||
|
* and reload on invalidation.
|
||||||
|
*/
|
||||||
|
if (bm->dirty_bitmap == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bdrv_release_dirty_bitmap(bs, bm->dirty_bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
bitmap_list_free(bm_list);
|
bitmap_list_free(bm_list);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1451,7 +1606,7 @@ int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp)
|
||||||
for (bitmap = bdrv_dirty_bitmap_next(bs, NULL); bitmap != NULL;
|
for (bitmap = bdrv_dirty_bitmap_next(bs, NULL); bitmap != NULL;
|
||||||
bitmap = bdrv_dirty_bitmap_next(bs, bitmap))
|
bitmap = bdrv_dirty_bitmap_next(bs, bitmap))
|
||||||
{
|
{
|
||||||
if (bdrv_dirty_bitmap_get_persistance(bitmap)) {
|
if (bdrv_dirty_bitmap_get_persistence(bitmap)) {
|
||||||
bdrv_dirty_bitmap_set_readonly(bitmap, true);
|
bdrv_dirty_bitmap_set_readonly(bitmap, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue