Compare commits
5 Commits
v4.1.0-rc1
...
rm-protoco
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6321d78dc5 | ||
|
|
a0974f99aa | ||
|
|
42883c01bf | ||
|
|
7966c2b312 | ||
|
|
5888011244 |
27
.cirrus.yml
27
.cirrus.yml
@@ -1,27 +0,0 @@
|
||||
env:
|
||||
CIRRUS_CLONE_DEPTH: 1
|
||||
|
||||
freebsd_12_task:
|
||||
freebsd_instance:
|
||||
image: freebsd-12-0-release-amd64
|
||||
cpu: 8
|
||||
memory: 8G
|
||||
install_script: pkg install -y
|
||||
bash bison curl cyrus-sasl git glib gmake gnutls gsed
|
||||
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 gnu-sed glib pixman make sdl2
|
||||
script:
|
||||
- ./configure --python=/usr/local/bin/python3 || { cat config.log; exit 1; }
|
||||
- gmake -j$(sysctl -n hw.ncpu)
|
||||
- gmake check -j$(sysctl -n hw.ncpu)
|
||||
@@ -1,10 +1,4 @@
|
||||
# EditorConfig is a file format and collection of text editor plugins
|
||||
# for maintaining consistent coding styles between different editors
|
||||
# and IDEs. Most popular editors support this either natively or via
|
||||
# plugin.
|
||||
#
|
||||
# Check https://editorconfig.org for details.
|
||||
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
@@ -12,32 +6,10 @@ end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
|
||||
[*.mak]
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
file_type_emacs = makefile
|
||||
|
||||
[Makefile*]
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
file_type_emacs = makefile
|
||||
|
||||
[*.{c,h}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.sh]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{s,S}]
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
file_type_emacs = asm
|
||||
|
||||
[*.{vert,frag}]
|
||||
file_type_emacs = glsl
|
||||
|
||||
[*.json]
|
||||
indent_style = space
|
||||
file_type_emacs = python
|
||||
|
||||
80
.gitignore
vendored
80
.gitignore
vendored
@@ -1,4 +1,3 @@
|
||||
/.doctrees
|
||||
/config-devices.*
|
||||
/config-all-devices.*
|
||||
/config-all-disas.*
|
||||
@@ -6,7 +5,6 @@
|
||||
/config-target.*
|
||||
/config.status
|
||||
/config-temp
|
||||
/elf2dmp
|
||||
/trace-events-all
|
||||
/trace/generated-events.h
|
||||
/trace/generated-events.c
|
||||
@@ -32,21 +30,79 @@
|
||||
/qapi-gen-timestamp
|
||||
/qapi/qapi-builtin-types.[ch]
|
||||
/qapi/qapi-builtin-visit.[ch]
|
||||
/qapi/qapi-commands-*.[ch]
|
||||
/qapi/qapi-commands-block-core.[ch]
|
||||
/qapi/qapi-commands-block.[ch]
|
||||
/qapi/qapi-commands-char.[ch]
|
||||
/qapi/qapi-commands-common.[ch]
|
||||
/qapi/qapi-commands-crypto.[ch]
|
||||
/qapi/qapi-commands-introspect.[ch]
|
||||
/qapi/qapi-commands-migration.[ch]
|
||||
/qapi/qapi-commands-misc.[ch]
|
||||
/qapi/qapi-commands-net.[ch]
|
||||
/qapi/qapi-commands-rocker.[ch]
|
||||
/qapi/qapi-commands-run-state.[ch]
|
||||
/qapi/qapi-commands-sockets.[ch]
|
||||
/qapi/qapi-commands-tpm.[ch]
|
||||
/qapi/qapi-commands-trace.[ch]
|
||||
/qapi/qapi-commands-transaction.[ch]
|
||||
/qapi/qapi-commands-ui.[ch]
|
||||
/qapi/qapi-commands.[ch]
|
||||
/qapi/qapi-emit-events.[ch]
|
||||
/qapi/qapi-events-*.[ch]
|
||||
/qapi/qapi-events-block-core.[ch]
|
||||
/qapi/qapi-events-block.[ch]
|
||||
/qapi/qapi-events-char.[ch]
|
||||
/qapi/qapi-events-common.[ch]
|
||||
/qapi/qapi-events-crypto.[ch]
|
||||
/qapi/qapi-events-introspect.[ch]
|
||||
/qapi/qapi-events-migration.[ch]
|
||||
/qapi/qapi-events-misc.[ch]
|
||||
/qapi/qapi-events-net.[ch]
|
||||
/qapi/qapi-events-rocker.[ch]
|
||||
/qapi/qapi-events-run-state.[ch]
|
||||
/qapi/qapi-events-sockets.[ch]
|
||||
/qapi/qapi-events-tpm.[ch]
|
||||
/qapi/qapi-events-trace.[ch]
|
||||
/qapi/qapi-events-transaction.[ch]
|
||||
/qapi/qapi-events-ui.[ch]
|
||||
/qapi/qapi-events.[ch]
|
||||
/qapi/qapi-introspect.[ch]
|
||||
/qapi/qapi-types-*.[ch]
|
||||
/qapi/qapi-types-block-core.[ch]
|
||||
/qapi/qapi-types-block.[ch]
|
||||
/qapi/qapi-types-char.[ch]
|
||||
/qapi/qapi-types-common.[ch]
|
||||
/qapi/qapi-types-crypto.[ch]
|
||||
/qapi/qapi-types-introspect.[ch]
|
||||
/qapi/qapi-types-migration.[ch]
|
||||
/qapi/qapi-types-misc.[ch]
|
||||
/qapi/qapi-types-net.[ch]
|
||||
/qapi/qapi-types-rocker.[ch]
|
||||
/qapi/qapi-types-run-state.[ch]
|
||||
/qapi/qapi-types-sockets.[ch]
|
||||
/qapi/qapi-types-tpm.[ch]
|
||||
/qapi/qapi-types-trace.[ch]
|
||||
/qapi/qapi-types-transaction.[ch]
|
||||
/qapi/qapi-types-ui.[ch]
|
||||
/qapi/qapi-types.[ch]
|
||||
/qapi/qapi-visit-*.[ch]
|
||||
/qapi/qapi-visit-block-core.[ch]
|
||||
/qapi/qapi-visit-block.[ch]
|
||||
/qapi/qapi-visit-char.[ch]
|
||||
/qapi/qapi-visit-common.[ch]
|
||||
/qapi/qapi-visit-crypto.[ch]
|
||||
/qapi/qapi-visit-introspect.[ch]
|
||||
/qapi/qapi-visit-migration.[ch]
|
||||
/qapi/qapi-visit-misc.[ch]
|
||||
/qapi/qapi-visit-net.[ch]
|
||||
/qapi/qapi-visit-rocker.[ch]
|
||||
/qapi/qapi-visit-run-state.[ch]
|
||||
/qapi/qapi-visit-sockets.[ch]
|
||||
/qapi/qapi-visit-tpm.[ch]
|
||||
/qapi/qapi-visit-trace.[ch]
|
||||
/qapi/qapi-visit-transaction.[ch]
|
||||
/qapi/qapi-visit-ui.[ch]
|
||||
/qapi/qapi-visit.[ch]
|
||||
/qapi/qapi-doc.texi
|
||||
/qemu-doc.html
|
||||
/qemu-doc.info
|
||||
/qemu-doc.txt
|
||||
/qemu-edid
|
||||
/qemu-img
|
||||
/qemu-nbd
|
||||
/qemu-options.def
|
||||
@@ -95,9 +151,7 @@
|
||||
.sdk
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.gcov
|
||||
/pc-bios/bios-pq/status
|
||||
/pc-bios/edk2-*.fd
|
||||
/pc-bios/vgabios-pq/status
|
||||
/pc-bios/optionrom/linuxboot.asm
|
||||
/pc-bios/optionrom/linuxboot.bin
|
||||
@@ -107,10 +161,6 @@
|
||||
/pc-bios/optionrom/linuxboot_dma.bin
|
||||
/pc-bios/optionrom/linuxboot_dma.raw
|
||||
/pc-bios/optionrom/linuxboot_dma.img
|
||||
/pc-bios/optionrom/pvh.asm
|
||||
/pc-bios/optionrom/pvh.bin
|
||||
/pc-bios/optionrom/pvh.raw
|
||||
/pc-bios/optionrom/pvh.img
|
||||
/pc-bios/optionrom/multiboot.asm
|
||||
/pc-bios/optionrom/multiboot.bin
|
||||
/pc-bios/optionrom/multiboot.raw
|
||||
@@ -121,7 +171,6 @@
|
||||
/pc-bios/optionrom/kvmvapic.img
|
||||
/pc-bios/s390-ccw/s390-ccw.elf
|
||||
/pc-bios/s390-ccw/s390-ccw.img
|
||||
/docs/built
|
||||
/docs/interop/qemu-ga-qapi.texi
|
||||
/docs/interop/qemu-ga-ref.html
|
||||
/docs/interop/qemu-ga-ref.info*
|
||||
@@ -157,4 +206,3 @@ trace-dtrace-root.h
|
||||
trace-dtrace-root.dtrace
|
||||
trace-ust-all.h
|
||||
trace-ust-all.c
|
||||
/target/arm/decode-sve.inc.c
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
before_script:
|
||||
- apt-get update -qq
|
||||
- apt-get install -y -qq flex bison libglib2.0-dev libpixman-1-dev genisoimage
|
||||
|
||||
build-system1:
|
||||
script:
|
||||
- apt-get install -y -qq libgtk-3-dev libvte-dev nettle-dev libcacard-dev
|
||||
libusb-dev libvde-dev libspice-protocol-dev libgl1-mesa-dev
|
||||
- ./configure --enable-werror --target-list="aarch64-softmmu alpha-softmmu
|
||||
cris-softmmu hppa-softmmu lm32-softmmu moxie-softmmu microblazeel-softmmu
|
||||
mips64el-softmmu m68k-softmmu ppc-softmmu riscv64-softmmu sparc-softmmu"
|
||||
- make -j2
|
||||
- make -j2 check
|
||||
|
||||
build-system2:
|
||||
script:
|
||||
- apt-get install -y -qq libsdl2-dev libgcrypt-dev libbrlapi-dev libaio-dev
|
||||
libfdt-dev liblzo2-dev librdmacm-dev libibverbs-dev libibumad-dev
|
||||
- ./configure --enable-werror --target-list="tricore-softmmu unicore32-softmmu
|
||||
microblaze-softmmu mips-softmmu riscv32-softmmu s390x-softmmu sh4-softmmu
|
||||
sparc64-softmmu x86_64-softmmu xtensa-softmmu nios2-softmmu or1k-softmmu"
|
||||
- make -j2
|
||||
- make -j2 check
|
||||
|
||||
build-disabled:
|
||||
script:
|
||||
- ./configure --enable-werror --disable-rdma --disable-slirp --disable-curl
|
||||
--disable-capstone --disable-live-block-migration --disable-glusterfs
|
||||
--disable-replication --disable-coroutine-pool --disable-smartcard
|
||||
--disable-guest-agent --disable-curses --disable-libxml2 --disable-tpm
|
||||
--disable-qom-cast-debug --disable-spice --disable-vhost-vsock
|
||||
--disable-vhost-net --disable-vhost-crypto --disable-vhost-user
|
||||
--target-list="i386-softmmu ppc64-softmmu mips64-softmmu i386-linux-user"
|
||||
- make -j2
|
||||
- make -j2 check-qtest SPEED=slow
|
||||
|
||||
build-tcg-disabled:
|
||||
script:
|
||||
- apt-get install -y -qq clang libgtk-3-dev libbluetooth-dev libusb-dev
|
||||
- ./configure --cc=clang --enable-werror --disable-tcg --audio-drv-list=""
|
||||
- make -j2
|
||||
- make check-unit
|
||||
- make check-qapi-schema
|
||||
- cd tests/qemu-iotests/
|
||||
- ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048
|
||||
052 063 077 086 101 104 106 113 147 148 150 151 152 157 159 160
|
||||
163 170 171 183 184 192 194 197 205 208 215 221 222 226 227 236
|
||||
- ./check -qcow2 001 002 003 004 005 007 008 009 010 011 012 013 017 018 019
|
||||
020 021 022 024 025 027 028 029 031 032 033 034 035 036 037 038
|
||||
039 040 042 043 046 047 048 049 050 051 052 053 054 056 057 058
|
||||
060 061 062 063 065 066 067 068 069 071 072 073 074 079 080 082
|
||||
085 086 089 090 091 095 096 097 098 099 102 103 104 105 107 108
|
||||
110 111 114 117 120 122 124 126 127 129 130 132 133 134 137 138
|
||||
139 140 141 142 143 144 145 147 150 151 152 154 155 156 157 158
|
||||
161 165 170 172 174 176 177 179 184 186 187 190 192 194 195 196
|
||||
197 200 202 203 205 208 209 214 215 216 217 218 222 226 227 229 234
|
||||
|
||||
build-user:
|
||||
script:
|
||||
- ./configure --enable-werror --disable-system --disable-guest-agent
|
||||
--disable-capstone --disable-slirp --disable-fdt
|
||||
- make -j2
|
||||
- make run-tcg-tests-i386-linux-user run-tcg-tests-x86_64-linux-user
|
||||
|
||||
build-clang:
|
||||
script:
|
||||
- apt-get install -y -qq clang libsdl2-dev
|
||||
xfslibs-dev libiscsi-dev libnfs-dev libseccomp-dev gnutls-dev librbd-dev
|
||||
- ./configure --cc=clang --cxx=clang++ --enable-werror
|
||||
--target-list="alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu
|
||||
ppc-softmmu s390x-softmmu x86_64-softmmu arm-linux-user"
|
||||
- make -j2
|
||||
- make -j2 check
|
||||
|
||||
build-tci:
|
||||
script:
|
||||
- TARGETS="aarch64 alpha arm hppa m68k microblaze moxie ppc64 s390x x86_64"
|
||||
- ./configure --enable-tcg-interpreter
|
||||
--target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)"
|
||||
- make -j2
|
||||
- make tests/boot-serial-test tests/cdrom-test tests/pxe-test
|
||||
- for tg in $TARGETS ; do
|
||||
export QTEST_QEMU_BINARY="${tg}-softmmu/qemu-system-${tg}" ;
|
||||
./tests/boot-serial-test || exit 1 ;
|
||||
./tests/cdrom-test || exit 1 ;
|
||||
done
|
||||
- QTEST_QEMU_BINARY="x86_64-softmmu/qemu-system-x86_64" ./tests/pxe-test
|
||||
- QTEST_QEMU_BINARY="s390x-softmmu/qemu-system-s390x" ./tests/pxe-test -m slow
|
||||
45
.gitmodules
vendored
45
.gitmodules
vendored
@@ -1,57 +1,48 @@
|
||||
[submodule "roms/vgabios"]
|
||||
path = roms/vgabios
|
||||
url = git://git.qemu-project.org/vgabios.git/
|
||||
[submodule "roms/seabios"]
|
||||
path = roms/seabios
|
||||
url = https://git.qemu.org/git/seabios.git/
|
||||
url = git://git.qemu-project.org/seabios.git/
|
||||
[submodule "roms/SLOF"]
|
||||
path = roms/SLOF
|
||||
url = https://git.qemu.org/git/SLOF.git
|
||||
url = git://git.qemu-project.org/SLOF.git
|
||||
[submodule "roms/ipxe"]
|
||||
path = roms/ipxe
|
||||
url = https://git.qemu.org/git/ipxe.git
|
||||
url = git://git.qemu-project.org/ipxe.git
|
||||
[submodule "roms/openbios"]
|
||||
path = roms/openbios
|
||||
url = https://git.qemu.org/git/openbios.git
|
||||
url = git://git.qemu-project.org/openbios.git
|
||||
[submodule "roms/openhackware"]
|
||||
path = roms/openhackware
|
||||
url = https://git.qemu.org/git/openhackware.git
|
||||
url = git://git.qemu-project.org/openhackware.git
|
||||
[submodule "roms/qemu-palcode"]
|
||||
path = roms/qemu-palcode
|
||||
url = https://git.qemu.org/git/qemu-palcode.git
|
||||
url = git://github.com/rth7680/qemu-palcode.git
|
||||
[submodule "roms/sgabios"]
|
||||
path = roms/sgabios
|
||||
url = https://git.qemu.org/git/sgabios.git
|
||||
url = git://git.qemu-project.org/sgabios.git
|
||||
[submodule "dtc"]
|
||||
path = dtc
|
||||
url = https://git.qemu.org/git/dtc.git
|
||||
url = git://git.qemu-project.org/dtc.git
|
||||
[submodule "roms/u-boot"]
|
||||
path = roms/u-boot
|
||||
url = https://git.qemu.org/git/u-boot.git
|
||||
url = git://git.qemu-project.org/u-boot.git
|
||||
[submodule "roms/skiboot"]
|
||||
path = roms/skiboot
|
||||
url = https://git.qemu.org/git/skiboot.git
|
||||
url = git://git.qemu.org/skiboot.git
|
||||
[submodule "roms/QemuMacDrivers"]
|
||||
path = roms/QemuMacDrivers
|
||||
url = https://git.qemu.org/git/QemuMacDrivers.git
|
||||
url = git://git.qemu.org/QemuMacDrivers.git
|
||||
[submodule "ui/keycodemapdb"]
|
||||
path = ui/keycodemapdb
|
||||
url = https://git.qemu.org/git/keycodemapdb.git
|
||||
url = git://git.qemu.org/keycodemapdb.git
|
||||
[submodule "capstone"]
|
||||
path = capstone
|
||||
url = https://git.qemu.org/git/capstone.git
|
||||
url = git://git.qemu.org/capstone.git
|
||||
[submodule "roms/seabios-hppa"]
|
||||
path = roms/seabios-hppa
|
||||
url = https://git.qemu.org/git/seabios-hppa.git
|
||||
url = git://github.com/hdeller/seabios-hppa.git
|
||||
[submodule "roms/u-boot-sam460ex"]
|
||||
path = roms/u-boot-sam460ex
|
||||
url = https://git.qemu.org/git/u-boot-sam460ex.git
|
||||
[submodule "tests/fp/berkeley-testfloat-3"]
|
||||
path = tests/fp/berkeley-testfloat-3
|
||||
url = https://git.qemu.org/git/berkeley-testfloat-3.git
|
||||
[submodule "tests/fp/berkeley-softfloat-3"]
|
||||
path = tests/fp/berkeley-softfloat-3
|
||||
url = https://git.qemu.org/git/berkeley-softfloat-3.git
|
||||
[submodule "roms/edk2"]
|
||||
path = roms/edk2
|
||||
url = https://git.qemu.org/git/edk2.git
|
||||
[submodule "slirp"]
|
||||
path = slirp
|
||||
url = https://git.qemu.org/git/libslirp.git
|
||||
url = git://github.com/zbalaton/u-boot-sam460ex
|
||||
|
||||
29
.mailmap
29
.mailmap
@@ -1,7 +1,6 @@
|
||||
# This mailmap fixes up author names/addresses.
|
||||
|
||||
# The first section translates weird addresses from the original git import
|
||||
# into proper addresses so that they are counted properly by git shortlog.
|
||||
# This mailmap just translates the weird addresses from the original import into git
|
||||
# into proper addresses so that they are counted properly in git shortlog output.
|
||||
#
|
||||
Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Anthony Liguori <anthony@codemonkey.ws> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com>
|
||||
@@ -12,28 +11,14 @@ Fabrice Bellard <fabrice@bellard.org> bellard <bellard@c046a42c-6fe2-441c-8c8c-7
|
||||
James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com>
|
||||
Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Yongbok Kim <yongbok.kim@mips.com> <yongbok.kim@imgtec.com>
|
||||
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@mips.com>
|
||||
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@imgtec.com>
|
||||
Paul Burton <pburton@wavecomp.com> <paul.burton@mips.com>
|
||||
Paul Burton <pburton@wavecomp.com> <paul.burton@imgtec.com>
|
||||
Paul Burton <pburton@wavecomp.com> <paul@archlinuxmips.org>
|
||||
Paul Burton <paul.burton@mips.com> <paul.burton@imgtec.com>
|
||||
Paul Burton <paul.burton@mips.com> <paul@archlinuxmips.org>
|
||||
Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
|
||||
# There is also a:
|
||||
# (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
# for the cvs2svn initialization commit e63c3dc74bf.
|
||||
|
||||
# Next, translate a few commits where mailman rewrote the From: line due
|
||||
# to strict SPF, although we prefer to avoid adding more entries like that.
|
||||
Ed Swierk <eswierk@skyportsystems.com> Ed Swierk via Qemu-devel <qemu-devel@nongnu.org>
|
||||
Ian McKellar <ianloic@google.com> Ian McKellar via Qemu-devel <qemu-devel@nongnu.org>
|
||||
Julia Suvorova <jusual@mail.ru> Julia Suvorova via Qemu-devel <qemu-devel@nongnu.org>
|
||||
Justin Terry (VM) <juterry@microsoft.com> Justin Terry (VM) via Qemu-devel <qemu-devel@nongnu.org>
|
||||
|
||||
|
||||
#
|
||||
# Also list preferred name forms where people have changed their
|
||||
# git author config, or had utf8/latin1 encoding issues.
|
||||
# git author config
|
||||
Daniel P. Berrangé <berrange@redhat.com>
|
||||
Reimar Döffinger <Reimar.Doeffinger@gmx.de>
|
||||
|
||||
302
.patchew.yml
302
.patchew.yml
@@ -1,302 +0,0 @@
|
||||
---
|
||||
# Note: this file is still unused. It serves as a documentation for the
|
||||
# Patchew configuration in case patchew.org disappears or has to be
|
||||
# reinstalled.
|
||||
#
|
||||
# Patchew configuration is available to project administrators at
|
||||
# https://patchew.org/api/v1/projects/1/config/ and can be configured
|
||||
# to YAML using the following Python script:
|
||||
#
|
||||
# import json
|
||||
# import sys
|
||||
# import ruamel.yaml
|
||||
#
|
||||
# json_str = sys.stdin.read()
|
||||
# yaml = ruamel.yaml.YAML()
|
||||
# yaml.explicit_start = True
|
||||
# data = json.loads(json_str, object_pairs_hook=ruamel.yaml.comments.CommentedMap)
|
||||
# ruamel.yaml.scalarstring.walk_tree(data)
|
||||
# yaml.dump(data, sys.stdout)
|
||||
|
||||
email:
|
||||
notifications:
|
||||
timeouts:
|
||||
event: TestingReport
|
||||
enabled: true
|
||||
to_user: false
|
||||
reply_subject: true
|
||||
set_reply_to: true
|
||||
in_reply_to: true
|
||||
reply_to_all: false
|
||||
subject_template: none
|
||||
to: fam@euphon.net
|
||||
cc: ''
|
||||
body_template: |
|
||||
{% if not is_timeout %} {{ cancel }} {% endif %}
|
||||
|
||||
Test '{{ test }}' timeout, log:
|
||||
|
||||
{{ log }}
|
||||
ENOSPC:
|
||||
event: TestingReport
|
||||
enabled: true
|
||||
to_user: false
|
||||
reply_subject: false
|
||||
set_reply_to: false
|
||||
in_reply_to: true
|
||||
reply_to_all: false
|
||||
subject_template: Out of space error
|
||||
to: fam@euphon.net
|
||||
cc: ''
|
||||
body_template: |
|
||||
{% if passed %}
|
||||
{{ cancel }}
|
||||
{% endif %}
|
||||
|
||||
{% if 'No space left on device' in log %}
|
||||
Tester {{ tester }} out of space when running {{ test }}
|
||||
|
||||
{{ log }}
|
||||
{% else %}
|
||||
{{ cancel }}
|
||||
{% endif %}
|
||||
FailureShort:
|
||||
event: TestingReport
|
||||
enabled: true
|
||||
to_user: false
|
||||
reply_subject: true
|
||||
set_reply_to: true
|
||||
in_reply_to: true
|
||||
reply_to_all: true
|
||||
subject_template: Testing failed
|
||||
to: ''
|
||||
cc: ''
|
||||
body_template: |
|
||||
{% if passed or not obj.message_id or is_timeout %}
|
||||
{{ cancel }}
|
||||
{% endif %}
|
||||
{% if 'No space left on device' in log %}
|
||||
{{ cancel }}
|
||||
{% endif %}
|
||||
Patchew URL: https://patchew.org/QEMU/{{ obj.message_id }}/
|
||||
|
||||
{% ansi2text log as logtext %}
|
||||
{% if test == "checkpatch" %}
|
||||
Hi,
|
||||
|
||||
This series seems to have some coding style problems. See output below for
|
||||
more information:
|
||||
|
||||
{{ logtext }}
|
||||
{% elif test == "docker-mingw@fedora" or test == "docker-quick@centos7" or test == "asan" %}
|
||||
Hi,
|
||||
|
||||
This series failed the {{ test }} build test. Please find the testing commands and
|
||||
their output below. If you have Docker installed, you can probably reproduce it
|
||||
locally.
|
||||
|
||||
{% lines_between logtext start="^=== TEST SCRIPT BEGIN ===$" stop="^=== TEST SCRIPT END ===$" %}
|
||||
{% lines_between logtext start="^=== OUTPUT BEGIN ===$" stop="=== OUTPUT END ===$" as output %}
|
||||
{% grep_C output regex="\b(FAIL|XPASS|ERROR|WARN|error:|warning:)" n=3 %}
|
||||
{% elif test == "s390x" or test == "FreeBSD" or test == "ppcle" or test == "ppcbe" %}
|
||||
Hi,
|
||||
|
||||
This series failed build test on {{test}} host. Please find the details below.
|
||||
|
||||
{% lines_between logtext start="^=== TEST SCRIPT BEGIN ===$" stop="^=== TEST SCRIPT END ===$" %}
|
||||
{% lines_between logtext start="^=== OUTPUT BEGIN ===$" stop="=== OUTPUT END ===$" as output %}
|
||||
{% grep_C output regex="\b(FAIL|XPASS|ERROR|WARN|error:|warning:)" n=3 %}
|
||||
{% else %}
|
||||
{{ cancel }}
|
||||
{% endif %}
|
||||
|
||||
The full log is available at
|
||||
{{ log_url }}.
|
||||
---
|
||||
Email generated automatically by Patchew [https://patchew.org/].
|
||||
Please send your feedback to patchew-devel@redhat.com
|
||||
testing:
|
||||
tests:
|
||||
asan:
|
||||
enabled: true
|
||||
requirements: docker
|
||||
timeout: 3600
|
||||
script: |
|
||||
#!/bin/bash
|
||||
time make docker-test-debug@fedora TARGET_LIST=x86_64-softmmu J=14 NETWORK=1
|
||||
docker-quick@centos7:
|
||||
enabled: false
|
||||
requirements: docker,x86_64
|
||||
timeout: 3600
|
||||
script: |
|
||||
#!/bin/bash
|
||||
time make docker-test-quick@centos7 SHOW_ENV=1 J=14 NETWORK=1
|
||||
checkpatch:
|
||||
enabled: true
|
||||
requirements: ''
|
||||
timeout: 600
|
||||
script: |
|
||||
#!/bin/bash
|
||||
git rev-parse base > /dev/null || exit 0
|
||||
git config --local diff.renamelimit 0
|
||||
git config --local diff.renames True
|
||||
git config --local diff.algorithm histogram
|
||||
./scripts/checkpatch.pl --mailback base..
|
||||
docker-mingw@fedora:
|
||||
enabled: true
|
||||
requirements: docker,x86_64
|
||||
timeout: 3600
|
||||
script: |
|
||||
#! /bin/bash
|
||||
test "$(uname -m)" = "x86_64"
|
||||
ppcle:
|
||||
enabled: false
|
||||
requirements: ppcle
|
||||
timeout: 3600
|
||||
script: |
|
||||
#!/bin/bash
|
||||
# Testing script will be invoked under the git checkout with
|
||||
# HEAD pointing to a commit that has the patches applied on top of "base"
|
||||
# branch
|
||||
set -e
|
||||
CC=$HOME/bin/cc
|
||||
INSTALL=$PWD/install
|
||||
BUILD=$PWD/build
|
||||
mkdir -p $BUILD $INSTALL
|
||||
SRC=$PWD
|
||||
cd $BUILD
|
||||
$SRC/configure --cc=$CC --prefix=$INSTALL
|
||||
make -j4
|
||||
# XXX: we need reliable clean up
|
||||
# make check -j4 V=1
|
||||
make install
|
||||
|
||||
echo
|
||||
echo "=== ENV ==="
|
||||
env
|
||||
|
||||
echo
|
||||
echo "=== PACKAGES ==="
|
||||
rpm -qa
|
||||
ppcbe:
|
||||
enabled: false
|
||||
requirements: ppcbe
|
||||
timeout: 3600
|
||||
script: |
|
||||
#!/bin/bash
|
||||
# Testing script will be invoked under the git checkout with
|
||||
# HEAD pointing to a commit that has the patches applied on top of "base"
|
||||
# branch
|
||||
set -e
|
||||
CC=$HOME/bin/cc
|
||||
INSTALL=$PWD/install
|
||||
BUILD=$PWD/build
|
||||
mkdir -p $BUILD $INSTALL
|
||||
SRC=$PWD
|
||||
cd $BUILD
|
||||
$SRC/configure --cc=$CC --prefix=$INSTALL
|
||||
make -j4
|
||||
# XXX: we need reliable clean up
|
||||
# make check -j4 V=1
|
||||
make install
|
||||
|
||||
echo
|
||||
echo "=== ENV ==="
|
||||
env
|
||||
|
||||
echo
|
||||
echo "=== PACKAGES ==="
|
||||
rpm -qa
|
||||
FreeBSD:
|
||||
enabled: true
|
||||
requirements: qemu-x86,x86_64,git
|
||||
timeout: 3600
|
||||
script: |
|
||||
#!/bin/bash
|
||||
# Testing script will be invoked under the git checkout with
|
||||
# HEAD pointing to a commit that has the patches applied on top of "base"
|
||||
# branch
|
||||
if qemu-system-x86_64 --help >/dev/null 2>&1; then
|
||||
QEMU=qemu-system-x86_64
|
||||
elif /usr/libexec/qemu-kvm --help >/dev/null 2>&1; then
|
||||
QEMU=/usr/libexec/qemu-kvm
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
make vm-build-freebsd J=21 QEMU=$QEMU
|
||||
exit 0
|
||||
docker-clang@ubuntu:
|
||||
enabled: true
|
||||
requirements: docker,x86_64
|
||||
timeout: 3600
|
||||
script: |
|
||||
#!/bin/bash
|
||||
time make docker-test-clang@ubuntu SHOW_ENV=1 J=14 NETWORK=1
|
||||
s390x:
|
||||
enabled: true
|
||||
requirements: s390x
|
||||
timeout: 3600
|
||||
script: |
|
||||
#!/bin/bash
|
||||
# Testing script will be invoked under the git checkout with
|
||||
# HEAD pointing to a commit that has the patches applied on top of "base"
|
||||
# branch
|
||||
set -e
|
||||
CC=$HOME/bin/cc
|
||||
INSTALL=$PWD/install
|
||||
BUILD=$PWD/build
|
||||
mkdir -p $BUILD $INSTALL
|
||||
SRC=$PWD
|
||||
cd $BUILD
|
||||
$SRC/configure --cc=$CC --prefix=$INSTALL
|
||||
make -j4
|
||||
# XXX: we need reliable clean up
|
||||
# make check -j4 V=1
|
||||
make install
|
||||
|
||||
echo
|
||||
echo "=== ENV ==="
|
||||
env
|
||||
|
||||
echo
|
||||
echo "=== PACKAGES ==="
|
||||
rpm -qa
|
||||
requirements:
|
||||
x86_64:
|
||||
script: |
|
||||
#! /bin/bash
|
||||
test "$(uname -m)" = "x86_64"
|
||||
qemu-x86:
|
||||
script: |
|
||||
#!/bin/bash
|
||||
if qemu-system-x86_64 --help >/dev/null 2>&1; then
|
||||
:
|
||||
elif /usr/libexec/qemu-kvm --help >/dev/null 2>&1; then
|
||||
:
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
ppcle:
|
||||
script: |
|
||||
#!/bin/bash
|
||||
test "$(uname -m)" = "ppc64le"
|
||||
ppcbe:
|
||||
script: |
|
||||
#!/bin/bash
|
||||
test "$(uname -m)" = "ppc64"
|
||||
git:
|
||||
script: |
|
||||
#! /bin/bash
|
||||
git config user.name > /dev/null 2>&1
|
||||
docker:
|
||||
script: |
|
||||
#!/bin/bash
|
||||
docker ps || sudo -n docker ps
|
||||
s390x:
|
||||
script: |
|
||||
#!/bin/bash
|
||||
test "$(uname -m)" = "s390x"
|
||||
git:
|
||||
push_to: git@github.com:patchew-project/qemu
|
||||
public_repo: https://github.com/patchew-project/qemu
|
||||
url_template: https://github.com/patchew-project/qemu/tree/%t
|
||||
@@ -7,11 +7,10 @@ env:
|
||||
matrix:
|
||||
- IMAGE=debian-amd64
|
||||
TARGET_LIST=x86_64-softmmu,x86_64-linux-user
|
||||
# currently disabled as the mxe.cc repos are down
|
||||
# - IMAGE=debian-win32-cross
|
||||
# TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu
|
||||
# - IMAGE=debian-win64-cross
|
||||
# TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu
|
||||
- IMAGE=debian-win32-cross
|
||||
TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu
|
||||
- IMAGE=debian-win64-cross
|
||||
TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu
|
||||
- IMAGE=debian-armel-cross
|
||||
TARGET_LIST=arm-softmmu,arm-linux-user,armeb-linux-user
|
||||
- IMAGE=debian-armhf-cross
|
||||
@@ -36,5 +35,13 @@ build:
|
||||
options: "-e HOME=/root"
|
||||
ci:
|
||||
- unset CC
|
||||
# some targets require newer up to date packages, for example TARGET_LIST matching
|
||||
# aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu)
|
||||
# see the configure script:
|
||||
# error_exit "DTC (libfdt) version >= 1.4.2 not present. Your options:"
|
||||
# " (1) Preferred: Install the DTC (libfdt) devel package"
|
||||
# " (2) Fetch the DTC submodule, using:"
|
||||
# " git submodule update --init dtc"
|
||||
- dpkg --compare-versions `dpkg-query --showformat='${Version}' --show libfdt-dev` ge 1.4.2 || git submodule update --init dtc
|
||||
- ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST}
|
||||
- make -j$(($(getconf _NPROCESSORS_ONLN) + 1))
|
||||
|
||||
313
.travis.yml
313
.travis.yml
@@ -1,13 +1,10 @@
|
||||
# The current Travis default is a VM based 16.04 Xenial on GCE
|
||||
# Additional builds with specific requirements for a full VM need to
|
||||
# be added as additional matrix: entries later on
|
||||
dist: xenial
|
||||
sudo: false
|
||||
language: c
|
||||
python:
|
||||
- "2.6"
|
||||
compiler:
|
||||
- gcc
|
||||
cache: ccache
|
||||
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
@@ -31,20 +28,12 @@ addons:
|
||||
- libseccomp-dev
|
||||
- libspice-protocol-dev
|
||||
- libspice-server-dev
|
||||
- libssh-dev
|
||||
- libssh2-1-dev
|
||||
- liburcu-dev
|
||||
- libusb-1.0-0-dev
|
||||
- libvte-2.91-dev
|
||||
- libvte-2.90-dev
|
||||
- sparse
|
||||
- uuid-dev
|
||||
- gcovr
|
||||
homebrew:
|
||||
packages:
|
||||
- glib
|
||||
- pixman
|
||||
- gnu-sed
|
||||
update: true
|
||||
|
||||
|
||||
# The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu
|
||||
# to prevent IRC notifications from forks. This was created using:
|
||||
@@ -55,203 +44,130 @@ notifications:
|
||||
- secure: "F7GDRgjuOo5IUyRLqSkmDL7kvdU4UcH3Lm/W2db2JnDHTGCqgEdaYEYKciyCLZ57vOTsTsOgesN8iUT7hNHBd1KWKjZe9KDTZWppWRYVwAwQMzVeSOsbbU4tRoJ6Pp+3qhH1Z0eGYR9ZgKYAoTumDFgSAYRp4IscKS8jkoedOqM="
|
||||
on_success: change
|
||||
on_failure: always
|
||||
|
||||
|
||||
env:
|
||||
global:
|
||||
- SRC_DIR="."
|
||||
- BUILD_DIR="."
|
||||
- BASE_CONFIG="--disable-docs --disable-tools"
|
||||
- TEST_CMD="make check -j3 V=1"
|
||||
# This is broadly a list of "mainline" softmmu targets which have support across the major distros
|
||||
- MAIN_SOFTMMU_TARGETS="aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
|
||||
|
||||
- TEST_CMD="make check"
|
||||
- MAKEFLAGS="-j3"
|
||||
matrix:
|
||||
- CONFIG=""
|
||||
- CONFIG="--enable-debug --enable-debug-tcg --enable-trace-backends=log"
|
||||
- CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb"
|
||||
- CONFIG="--enable-modules --disable-linux-user"
|
||||
- CONFIG="--with-coroutine=ucontext --disable-linux-user"
|
||||
- CONFIG="--with-coroutine=sigaltstack --disable-linux-user"
|
||||
git:
|
||||
# we want to do this ourselves
|
||||
submodules: false
|
||||
|
||||
|
||||
before_install:
|
||||
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install libffi gettext glib pixman ; fi
|
||||
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
|
||||
- git submodule update --init --recursive
|
||||
before_script:
|
||||
- mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
|
||||
- ${SRC_DIR}/configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; }
|
||||
- ./configure ${CONFIG}
|
||||
script:
|
||||
- make -j3 && ${TEST_CMD}
|
||||
|
||||
|
||||
- make ${MAKEFLAGS} && ${TEST_CMD}
|
||||
matrix:
|
||||
include:
|
||||
- env:
|
||||
- CONFIG="--disable-system --static"
|
||||
|
||||
|
||||
# we split the system builds as it takes a while to build them all
|
||||
- env:
|
||||
- CONFIG="--disable-user --target-list=${MAIN_SOFTMMU_TARGETS}"
|
||||
|
||||
|
||||
- env:
|
||||
- CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
|
||||
|
||||
|
||||
# Just build tools and run minimal unit and softfloat checks
|
||||
- env:
|
||||
- BASE_CONFIG="--enable-tools"
|
||||
- CONFIG="--disable-user --disable-system"
|
||||
- TEST_CMD="make check-unit check-softfloat -j3"
|
||||
|
||||
- env:
|
||||
- CONFIG="--enable-debug --enable-debug-tcg --disable-user"
|
||||
|
||||
|
||||
# TCG debug can be run just on it's own and is mostly agnostic to user/softmmu distinctions
|
||||
- env:
|
||||
- CONFIG="--enable-debug-tcg --disable-system"
|
||||
|
||||
|
||||
- env:
|
||||
- CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-libusb --disable-replication --target-list=${MAIN_SOFTMMU_TARGETS}"
|
||||
|
||||
|
||||
# Module builds are mostly of interest to major distros
|
||||
- env:
|
||||
- CONFIG="--enable-modules --target-list=${MAIN_SOFTMMU_TARGETS}"
|
||||
|
||||
|
||||
# Alternate coroutines implementations are only really of interest to KVM users
|
||||
# However we can't test against KVM on Travis so we can only run unit tests
|
||||
- env:
|
||||
- CONFIG="--with-coroutine=ucontext --disable-tcg"
|
||||
- TEST_CMD="make check-unit -j3 V=1"
|
||||
|
||||
|
||||
- env:
|
||||
- CONFIG="--with-coroutine=sigaltstack --disable-tcg"
|
||||
- TEST_CMD="make check-unit -j3 V=1"
|
||||
|
||||
|
||||
# Check we can build docs and tools (out of tree)
|
||||
- env:
|
||||
- BUILD_DIR="out-of-tree/build/dir" SRC_DIR="../../.."
|
||||
- BASE_CONFIG="--enable-tools --enable-docs"
|
||||
- CONFIG="--target-list=x86_64-softmmu,aarch64-linux-user"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- python-sphinx
|
||||
- texinfo
|
||||
- perl
|
||||
|
||||
|
||||
# Test with Clang for compile portability (Travis uses clang-5.0)
|
||||
- env:
|
||||
- CONFIG="--disable-system"
|
||||
# Test with CLang for compile portability
|
||||
- env: CONFIG=""
|
||||
compiler: clang
|
||||
|
||||
|
||||
- env:
|
||||
- CONFIG="--disable-user --target-list=${MAIN_SOFTMMU_TARGETS}"
|
||||
compiler: clang
|
||||
|
||||
|
||||
- env:
|
||||
- CONFIG="--target-list=${MAIN_SOFTMMU_TARGETS} "
|
||||
compiler: clang
|
||||
before_script:
|
||||
- ./configure ${CONFIG} --extra-cflags="-fsanitize=undefined -Werror" || { cat config.log && exit 1; }
|
||||
|
||||
|
||||
- env:
|
||||
- CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
|
||||
compiler: clang
|
||||
|
||||
|
||||
# gprof/gcov are GCC features
|
||||
- env:
|
||||
- CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=${MAIN_SOFTMMU_TARGETS}"
|
||||
after_success:
|
||||
- ${SRC_DIR}/scripts/travis/coverage-summary.sh
|
||||
|
||||
|
||||
- env: CONFIG="--enable-gprof --enable-gcov --disable-pie"
|
||||
compiler: gcc
|
||||
# We manually include builds which we disable "make check" for
|
||||
- env:
|
||||
- CONFIG="--without-default-devices --disable-user"
|
||||
- TEST_CMD=""
|
||||
|
||||
|
||||
# We manually include builds which we disable "make check" for
|
||||
- env:
|
||||
- CONFIG="--enable-debug --enable-tcg-interpreter"
|
||||
- TEST_CMD=""
|
||||
|
||||
|
||||
# We don't need to exercise every backend with every front-end
|
||||
- env:
|
||||
- CONFIG="--enable-trace-backends=log,simple,syslog --disable-system"
|
||||
- TEST_CMD=""
|
||||
|
||||
|
||||
- env:
|
||||
- CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu"
|
||||
- TEST_CMD=""
|
||||
|
||||
|
||||
- env:
|
||||
- CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu"
|
||||
- TEST_CMD=""
|
||||
|
||||
|
||||
# MacOSX builds
|
||||
- env:
|
||||
- CONFIG="--target-list=${MAIN_SOFTMMU_TARGETS}"
|
||||
- env: CONFIG="--enable-debug --enable-tcg-interpreter"
|
||||
TEST_CMD=""
|
||||
compiler: gcc
|
||||
- env: CONFIG="--enable-trace-backends=simple"
|
||||
TEST_CMD=""
|
||||
compiler: gcc
|
||||
- env: CONFIG="--enable-trace-backends=ftrace"
|
||||
TEST_CMD=""
|
||||
compiler: gcc
|
||||
- env: CONFIG="--enable-trace-backends=ust"
|
||||
TEST_CMD=""
|
||||
compiler: gcc
|
||||
- env: CONFIG="--disable-tcg"
|
||||
TEST_CMD=""
|
||||
compiler: gcc
|
||||
- env: CONFIG=""
|
||||
os: osx
|
||||
osx_image: xcode9.4
|
||||
compiler: clang
|
||||
|
||||
|
||||
- env:
|
||||
- CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu"
|
||||
os: osx
|
||||
osx_image: xcode10.2
|
||||
compiler: clang
|
||||
|
||||
|
||||
# Python builds
|
||||
- env:
|
||||
- CONFIG="--target-list=x86_64-softmmu"
|
||||
language: python
|
||||
# Plain Trusty System Build
|
||||
- env: CONFIG="--disable-linux-user"
|
||||
sudo: required
|
||||
addons:
|
||||
dist: trusty
|
||||
compiler: gcc
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get build-dep -qq qemu
|
||||
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
|
||||
- git submodule update --init --recursive
|
||||
# Plain Trusty Linux User Build
|
||||
- env: CONFIG="--disable-system"
|
||||
sudo: required
|
||||
addons:
|
||||
dist: trusty
|
||||
compiler: gcc
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get build-dep -qq qemu
|
||||
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
|
||||
- git submodule update --init --recursive
|
||||
# Trusty System build with latest stable clang & python 3.0
|
||||
- sudo: required
|
||||
addons:
|
||||
dist: trusty
|
||||
language: generic
|
||||
compiler: none
|
||||
python:
|
||||
- "3.4"
|
||||
|
||||
|
||||
- env:
|
||||
- CONFIG="--target-list=x86_64-softmmu"
|
||||
language: python
|
||||
- "3.0"
|
||||
env:
|
||||
- COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9
|
||||
- CONFIG="--disable-linux-user --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3"
|
||||
before_install:
|
||||
- wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
- sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main'
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq -y clang-3.9
|
||||
- sudo apt-get build-dep -qq qemu
|
||||
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
|
||||
- git submodule update --init --recursive
|
||||
before_script:
|
||||
- ./configure ${CONFIG} || cat config.log
|
||||
# Trusty Linux User build with latest stable clang & python 3.6
|
||||
- sudo: required
|
||||
addons:
|
||||
dist: trusty
|
||||
language: generic
|
||||
compiler: none
|
||||
python:
|
||||
- "3.6"
|
||||
|
||||
|
||||
# Acceptance (Functional) tests
|
||||
- env:
|
||||
- CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu,mips-softmmu,mips64el-softmmu,aarch64-softmmu,arm-softmmu,s390x-softmmu,alpha-softmmu"
|
||||
- TEST_CMD="make check-acceptance"
|
||||
after_failure:
|
||||
- cat tests/results/latest/job.log
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- python3-pip
|
||||
- python3.5-venv
|
||||
env:
|
||||
- COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9
|
||||
- CONFIG="--disable-system --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3"
|
||||
before_install:
|
||||
- wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
- sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main'
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq -y clang-3.9
|
||||
- sudo apt-get build-dep -qq qemu
|
||||
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
|
||||
- git submodule update --init --recursive
|
||||
before_script:
|
||||
- ./configure ${CONFIG} || cat config.log
|
||||
# Using newer GCC with sanitizers
|
||||
- addons:
|
||||
apt:
|
||||
update: true
|
||||
sources:
|
||||
# PPAs for newer toolchains
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
# Extra toolchains
|
||||
- gcc-9
|
||||
- g++-9
|
||||
- gcc-5
|
||||
- g++-5
|
||||
# Build dependencies
|
||||
- libaio-dev
|
||||
- libattr1-dev
|
||||
@@ -271,28 +187,17 @@ matrix:
|
||||
- libseccomp-dev
|
||||
- libspice-protocol-dev
|
||||
- libspice-server-dev
|
||||
- libssh-dev
|
||||
- libssh2-1-dev
|
||||
- liburcu-dev
|
||||
- libusb-1.0-0-dev
|
||||
- libvte-2.91-dev
|
||||
- libvte-2.90-dev
|
||||
- sparse
|
||||
- uuid-dev
|
||||
language: generic
|
||||
compiler: none
|
||||
env:
|
||||
- COMPILER_NAME=gcc CXX=g++-9 CC=gcc-9
|
||||
- CONFIG="--cc=gcc-9 --cxx=g++-9 --disable-pie --disable-linux-user"
|
||||
- COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5
|
||||
- CONFIG="--cc=gcc-5 --cxx=g++-5 --disable-pie --disable-linux-user"
|
||||
- TEST_CMD=""
|
||||
before_script:
|
||||
- ./configure ${CONFIG} --extra-cflags="-g3 -O0 -Wno-error=stringop-truncation -fsanitize=thread -fuse-ld=gold" || { cat config.log && exit 1; }
|
||||
|
||||
|
||||
# Run check-tcg against linux-user
|
||||
- env:
|
||||
- CONFIG="--disable-system"
|
||||
- TEST_CMD="make -j3 check-tcg V=1"
|
||||
|
||||
# Run check-tcg against softmmu targets
|
||||
- env:
|
||||
- CONFIG="--target-list=xtensa-softmmu,arm-softmmu,aarch64-softmmu,alpha-softmmu"
|
||||
- TEST_CMD="make -j3 check-tcg V=1"
|
||||
- ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || cat config.log
|
||||
|
||||
64
CODING_STYLE
64
CODING_STYLE
@@ -29,45 +29,6 @@ Spaces of course are superior to tabs because:
|
||||
|
||||
Do not leave whitespace dangling off the ends of lines.
|
||||
|
||||
1.1 Multiline Indent
|
||||
|
||||
There are several places where indent is necessary:
|
||||
|
||||
- if/else
|
||||
- while/for
|
||||
- function definition & call
|
||||
|
||||
When breaking up a long line to fit within line width, we need a proper indent
|
||||
for the following lines.
|
||||
|
||||
In case of if/else, while/for, align the secondary lines just after the
|
||||
opening parenthesis of the first.
|
||||
|
||||
For example:
|
||||
|
||||
if (a == 1 &&
|
||||
b == 2) {
|
||||
|
||||
while (a == 1 &&
|
||||
b == 2) {
|
||||
|
||||
In case of function, there are several variants:
|
||||
|
||||
* 4 spaces indent from the beginning
|
||||
* align the secondary lines just after the opening parenthesis of the
|
||||
first
|
||||
|
||||
For example:
|
||||
|
||||
do_something(x, y,
|
||||
z);
|
||||
|
||||
do_something(x, y,
|
||||
z);
|
||||
|
||||
do_something(x, do_another(y,
|
||||
z));
|
||||
|
||||
2. Line width
|
||||
|
||||
Lines should be 80 characters; try not to make them longer.
|
||||
@@ -147,10 +108,10 @@ block to a separate function altogether.
|
||||
When comparing a variable for (in)equality with a constant, list the
|
||||
constant on the right, as in:
|
||||
|
||||
if (a == 1) {
|
||||
/* Reads like: "If a equals 1" */
|
||||
do_something();
|
||||
}
|
||||
if (a == 1) {
|
||||
/* Reads like: "If a equals 1" */
|
||||
do_something();
|
||||
}
|
||||
|
||||
Rationale: Yoda conditions (as in 'if (1 == a)') are awkward to read.
|
||||
Besides, good compilers already warn users when '==' is mis-typed as '=',
|
||||
@@ -163,23 +124,6 @@ We use traditional C-style /* */ comments and avoid // comments.
|
||||
Rationale: The // form is valid in C99, so this is purely a matter of
|
||||
consistency of style. The checkpatch script will warn you about this.
|
||||
|
||||
Multiline comment blocks should have a row of stars on the left,
|
||||
and the initial /* and terminating */ both on their own lines:
|
||||
/*
|
||||
* like
|
||||
* this
|
||||
*/
|
||||
This is the same format required by the Linux kernel coding style.
|
||||
|
||||
(Some of the existing comments in the codebase use the GNU Coding
|
||||
Standards form which does not have stars on the left, or other
|
||||
variations; avoid these when writing new comments, but don't worry
|
||||
about converting to the preferred form unless you're editing that
|
||||
comment anyway.)
|
||||
|
||||
Rationale: Consistency, and ease of visually picking out a multiline
|
||||
comment from the surrounding code.
|
||||
|
||||
8. trace-events style
|
||||
|
||||
8.1 0x prefix
|
||||
|
||||
22
COPYING.LIB
22
COPYING.LIB
@@ -1,8 +1,8 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
@@ -112,7 +112,7 @@ modification follow. Pay close attention to the difference between a
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
@@ -146,7 +146,7 @@ such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
@@ -432,7 +432,7 @@ decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
@@ -455,7 +455,7 @@ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
@@ -476,7 +476,7 @@ convey the exclusion of warranty; and each file should have at least the
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -485,7 +485,7 @@ convey the exclusion of warranty; and each file should have at least the
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
@@ -500,3 +500,5 @@ necessary. Here is a sample; alter the names:
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
||||
|
||||
270
COPYING.PYTHON
Normal file
270
COPYING.PYTHON
Normal file
@@ -0,0 +1,270 @@
|
||||
A. HISTORY OF THE SOFTWARE
|
||||
==========================
|
||||
|
||||
Python was created in the early 1990s by Guido van Rossum at Stichting
|
||||
Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
|
||||
as a successor of a language called ABC. Guido remains Python's
|
||||
principal author, although it includes many contributions from others.
|
||||
|
||||
In 1995, Guido continued his work on Python at the Corporation for
|
||||
National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
|
||||
in Reston, Virginia where he released several versions of the
|
||||
software.
|
||||
|
||||
In May 2000, Guido and the Python core development team moved to
|
||||
BeOpen.com to form the BeOpen PythonLabs team. In October of the same
|
||||
year, the PythonLabs team moved to Digital Creations (now Zope
|
||||
Corporation, see http://www.zope.com). In 2001, the Python Software
|
||||
Foundation (PSF, see http://www.python.org/psf/) was formed, a
|
||||
non-profit organization created specifically to own Python-related
|
||||
Intellectual Property. Zope Corporation is a sponsoring member of
|
||||
the PSF.
|
||||
|
||||
All Python releases are Open Source (see http://www.opensource.org for
|
||||
the Open Source Definition). Historically, most, but not all, Python
|
||||
releases have also been GPL-compatible; the table below summarizes
|
||||
the various releases.
|
||||
|
||||
Release Derived Year Owner GPL-
|
||||
from compatible? (1)
|
||||
|
||||
0.9.0 thru 1.2 1991-1995 CWI yes
|
||||
1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
|
||||
1.6 1.5.2 2000 CNRI no
|
||||
2.0 1.6 2000 BeOpen.com no
|
||||
1.6.1 1.6 2001 CNRI yes (2)
|
||||
2.1 2.0+1.6.1 2001 PSF no
|
||||
2.0.1 2.0+1.6.1 2001 PSF yes
|
||||
2.1.1 2.1+2.0.1 2001 PSF yes
|
||||
2.2 2.1.1 2001 PSF yes
|
||||
2.1.2 2.1.1 2002 PSF yes
|
||||
2.1.3 2.1.2 2002 PSF yes
|
||||
2.2.1 2.2 2002 PSF yes
|
||||
2.2.2 2.2.1 2002 PSF yes
|
||||
2.2.3 2.2.2 2003 PSF yes
|
||||
2.3 2.2.2 2002-2003 PSF yes
|
||||
2.3.1 2.3 2002-2003 PSF yes
|
||||
2.3.2 2.3.1 2002-2003 PSF yes
|
||||
2.3.3 2.3.2 2002-2003 PSF yes
|
||||
2.3.4 2.3.3 2004 PSF yes
|
||||
2.3.5 2.3.4 2005 PSF yes
|
||||
2.4 2.3 2004 PSF yes
|
||||
2.4.1 2.4 2005 PSF yes
|
||||
2.4.2 2.4.1 2005 PSF yes
|
||||
2.4.3 2.4.2 2006 PSF yes
|
||||
2.5 2.4 2006 PSF yes
|
||||
2.7 2.6 2010 PSF yes
|
||||
|
||||
Footnotes:
|
||||
|
||||
(1) GPL-compatible doesn't mean that we're distributing Python under
|
||||
the GPL. All Python licenses, unlike the GPL, let you distribute
|
||||
a modified version without making your changes open source. The
|
||||
GPL-compatible licenses make it possible to combine Python with
|
||||
other software that is released under the GPL; the others don't.
|
||||
|
||||
(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
|
||||
because its license has a choice of law clause. According to
|
||||
CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
|
||||
is "not incompatible" with the GPL.
|
||||
|
||||
Thanks to the many outside volunteers who have worked under Guido's
|
||||
direction to make these releases possible.
|
||||
|
||||
|
||||
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
|
||||
===============================================================
|
||||
|
||||
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
||||
--------------------------------------------
|
||||
|
||||
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
||||
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
||||
otherwise using this software ("Python") in source or binary form and
|
||||
its associated documentation.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, PSF
|
||||
hereby grants Licensee a nonexclusive, royalty-free, world-wide
|
||||
license to reproduce, analyze, test, perform and/or display publicly,
|
||||
prepare derivative works, distribute, and otherwise use Python
|
||||
alone or in any derivative version, provided, however, that PSF's
|
||||
License Agreement and PSF's notice of copyright, i.e., "Copyright (c)
|
||||
2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation; All Rights
|
||||
Reserved" are retained in Python alone or in any derivative version
|
||||
prepared by Licensee.
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on
|
||||
or incorporates Python or any part thereof, and wants to make
|
||||
the derivative work available to others as provided herein, then
|
||||
Licensee hereby agrees to include in any such work a brief summary of
|
||||
the changes made to Python.
|
||||
|
||||
4. PSF is making Python available to Licensee on an "AS IS"
|
||||
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
|
||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
6. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
7. Nothing in this License Agreement shall be deemed to create any
|
||||
relationship of agency, partnership, or joint venture between PSF and
|
||||
Licensee. This License Agreement does not grant permission to use PSF
|
||||
trademarks or trade name in a trademark sense to endorse or promote
|
||||
products or services of Licensee, or any third party.
|
||||
|
||||
8. By copying, installing or otherwise using Python, Licensee
|
||||
agrees to be bound by the terms and conditions of this License
|
||||
Agreement.
|
||||
|
||||
|
||||
BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
|
||||
-------------------------------------------
|
||||
|
||||
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
|
||||
|
||||
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
|
||||
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
|
||||
Individual or Organization ("Licensee") accessing and otherwise using
|
||||
this software in source or binary form and its associated
|
||||
documentation ("the Software").
|
||||
|
||||
2. Subject to the terms and conditions of this BeOpen Python License
|
||||
Agreement, BeOpen hereby grants Licensee a non-exclusive,
|
||||
royalty-free, world-wide license to reproduce, analyze, test, perform
|
||||
and/or display publicly, prepare derivative works, distribute, and
|
||||
otherwise use the Software alone or in any derivative version,
|
||||
provided, however, that the BeOpen Python License is retained in the
|
||||
Software, alone or in any derivative version prepared by Licensee.
|
||||
|
||||
3. BeOpen is making the Software available to Licensee on an "AS IS"
|
||||
basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
|
||||
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
|
||||
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
|
||||
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
5. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
6. This License Agreement shall be governed by and interpreted in all
|
||||
respects by the law of the State of California, excluding conflict of
|
||||
law provisions. Nothing in this License Agreement shall be deemed to
|
||||
create any relationship of agency, partnership, or joint venture
|
||||
between BeOpen and Licensee. This License Agreement does not grant
|
||||
permission to use BeOpen trademarks or trade names in a trademark
|
||||
sense to endorse or promote products or services of Licensee, or any
|
||||
third party. As an exception, the "BeOpen Python" logos available at
|
||||
http://www.pythonlabs.com/logos.html may be used according to the
|
||||
permissions granted on that web page.
|
||||
|
||||
7. By copying, installing or otherwise using the software, Licensee
|
||||
agrees to be bound by the terms and conditions of this License
|
||||
Agreement.
|
||||
|
||||
|
||||
CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
|
||||
---------------------------------------
|
||||
|
||||
1. This LICENSE AGREEMENT is between the Corporation for National
|
||||
Research Initiatives, having an office at 1895 Preston White Drive,
|
||||
Reston, VA 20191 ("CNRI"), and the Individual or Organization
|
||||
("Licensee") accessing and otherwise using Python 1.6.1 software in
|
||||
source or binary form and its associated documentation.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, CNRI
|
||||
hereby grants Licensee a nonexclusive, royalty-free, world-wide
|
||||
license to reproduce, analyze, test, perform and/or display publicly,
|
||||
prepare derivative works, distribute, and otherwise use Python 1.6.1
|
||||
alone or in any derivative version, provided, however, that CNRI's
|
||||
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
|
||||
1995-2001 Corporation for National Research Initiatives; All Rights
|
||||
Reserved" are retained in Python 1.6.1 alone or in any derivative
|
||||
version prepared by Licensee. Alternately, in lieu of CNRI's License
|
||||
Agreement, Licensee may substitute the following text (omitting the
|
||||
quotes): "Python 1.6.1 is made available subject to the terms and
|
||||
conditions in CNRI's License Agreement. This Agreement together with
|
||||
Python 1.6.1 may be located on the Internet using the following
|
||||
unique, persistent identifier (known as a handle): 1895.22/1013. This
|
||||
Agreement may also be obtained from a proxy server on the Internet
|
||||
using the following URL: http://hdl.handle.net/1895.22/1013".
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on
|
||||
or incorporates Python 1.6.1 or any part thereof, and wants to make
|
||||
the derivative work available to others as provided herein, then
|
||||
Licensee hereby agrees to include in any such work a brief summary of
|
||||
the changes made to Python 1.6.1.
|
||||
|
||||
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
|
||||
basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
|
||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
6. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
7. This License Agreement shall be governed by the federal
|
||||
intellectual property law of the United States, including without
|
||||
limitation the federal copyright law, and, to the extent such
|
||||
U.S. federal law does not apply, by the law of the Commonwealth of
|
||||
Virginia, excluding Virginia's conflict of law provisions.
|
||||
Notwithstanding the foregoing, with regard to derivative works based
|
||||
on Python 1.6.1 that incorporate non-separable material that was
|
||||
previously distributed under the GNU General Public License (GPL), the
|
||||
law of the Commonwealth of Virginia shall govern this License
|
||||
Agreement only as to issues arising under or with respect to
|
||||
Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
|
||||
License Agreement shall be deemed to create any relationship of
|
||||
agency, partnership, or joint venture between CNRI and Licensee. This
|
||||
License Agreement does not grant permission to use CNRI trademarks or
|
||||
trade name in a trademark sense to endorse or promote products or
|
||||
services of Licensee, or any third party.
|
||||
|
||||
8. By clicking on the "ACCEPT" button where indicated, or by copying,
|
||||
installing or otherwise using Python 1.6.1, Licensee agrees to be
|
||||
bound by the terms and conditions of this License Agreement.
|
||||
|
||||
ACCEPT
|
||||
|
||||
|
||||
CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
|
||||
--------------------------------------------------
|
||||
|
||||
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
|
||||
The Netherlands. All rights reserved.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software and its
|
||||
documentation for any purpose and without fee is hereby granted,
|
||||
provided that the above copyright notice appear in all copies and that
|
||||
both that copyright notice and this permission notice appear in
|
||||
supporting documentation, and that the name of Stichting Mathematisch
|
||||
Centrum or CWI not be used in advertising or publicity pertaining to
|
||||
distribution of the software without specific, written prior
|
||||
permission.
|
||||
|
||||
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
||||
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
|
||||
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
9
HACKING
9
HACKING
@@ -118,15 +118,6 @@ Please note that g_malloc will exit on allocation failure, so there
|
||||
is no need to test for failure (as you would have to with malloc).
|
||||
Calling g_malloc with a zero size is valid and will return NULL.
|
||||
|
||||
Prefer g_new(T, n) instead of g_malloc(sizeof(T) * n) for the following
|
||||
reasons:
|
||||
|
||||
a. It catches multiplication overflowing size_t;
|
||||
b. It returns T * instead of void *, letting compiler catch more type
|
||||
errors.
|
||||
|
||||
Declarations like T *v = g_malloc(sizeof(*v)) are acceptable, though.
|
||||
|
||||
Memory allocated by qemu_memalign or qemu_blockalign must be freed with
|
||||
qemu_vfree, since breaking this will cause problems on Win32.
|
||||
|
||||
|
||||
36
Kconfig.host
36
Kconfig.host
@@ -1,36 +0,0 @@
|
||||
# These are "proxy" symbols used to pass config-host.mak values
|
||||
# down to Kconfig. See also MINIKCONF_ARGS in the Makefile:
|
||||
# these two need to be kept in sync.
|
||||
|
||||
config KVM
|
||||
bool
|
||||
|
||||
config LINUX
|
||||
bool
|
||||
|
||||
config OPENGL
|
||||
bool
|
||||
|
||||
config X11
|
||||
bool
|
||||
|
||||
config SPICE
|
||||
bool
|
||||
|
||||
config IVSHMEM
|
||||
bool
|
||||
|
||||
config TPM
|
||||
bool
|
||||
|
||||
config VHOST_USER
|
||||
bool
|
||||
|
||||
config XEN
|
||||
bool
|
||||
|
||||
config VIRTFS
|
||||
bool
|
||||
|
||||
config PVRDMA
|
||||
bool
|
||||
1065
MAINTAINERS
1065
MAINTAINERS
File diff suppressed because it is too large
Load Diff
192
Makefile.objs
192
Makefile.objs
@@ -1,20 +1,69 @@
|
||||
#######################################################################
|
||||
# Common libraries for tools and emulators
|
||||
stub-obj-y = stubs/ util/ crypto/
|
||||
stub-obj-y = stubs/ crypto/
|
||||
util-obj-y = util/ qobject/ qapi/
|
||||
util-obj-y += qapi/qapi-builtin-types.o
|
||||
util-obj-y += qapi/qapi-types.o
|
||||
util-obj-y += qapi/qapi-types-block-core.o
|
||||
util-obj-y += qapi/qapi-types-block.o
|
||||
util-obj-y += qapi/qapi-types-char.o
|
||||
util-obj-y += qapi/qapi-types-common.o
|
||||
util-obj-y += qapi/qapi-types-crypto.o
|
||||
util-obj-y += qapi/qapi-types-introspect.o
|
||||
util-obj-y += qapi/qapi-types-migration.o
|
||||
util-obj-y += qapi/qapi-types-misc.o
|
||||
util-obj-y += qapi/qapi-types-net.o
|
||||
util-obj-y += qapi/qapi-types-rocker.o
|
||||
util-obj-y += qapi/qapi-types-run-state.o
|
||||
util-obj-y += qapi/qapi-types-sockets.o
|
||||
util-obj-y += qapi/qapi-types-tpm.o
|
||||
util-obj-y += qapi/qapi-types-trace.o
|
||||
util-obj-y += qapi/qapi-types-transaction.o
|
||||
util-obj-y += qapi/qapi-types-ui.o
|
||||
util-obj-y += qapi/qapi-builtin-visit.o
|
||||
util-obj-y += qapi/qapi-visit.o
|
||||
util-obj-y += qapi/qapi-visit-block-core.o
|
||||
util-obj-y += qapi/qapi-visit-block.o
|
||||
util-obj-y += qapi/qapi-visit-char.o
|
||||
util-obj-y += qapi/qapi-visit-common.o
|
||||
util-obj-y += qapi/qapi-visit-crypto.o
|
||||
util-obj-y += qapi/qapi-visit-introspect.o
|
||||
util-obj-y += qapi/qapi-visit-migration.o
|
||||
util-obj-y += qapi/qapi-visit-misc.o
|
||||
util-obj-y += qapi/qapi-visit-net.o
|
||||
util-obj-y += qapi/qapi-visit-rocker.o
|
||||
util-obj-y += qapi/qapi-visit-run-state.o
|
||||
util-obj-y += qapi/qapi-visit-sockets.o
|
||||
util-obj-y += qapi/qapi-visit-tpm.o
|
||||
util-obj-y += qapi/qapi-visit-trace.o
|
||||
util-obj-y += qapi/qapi-visit-transaction.o
|
||||
util-obj-y += qapi/qapi-visit-ui.o
|
||||
util-obj-y += qapi/qapi-events.o
|
||||
util-obj-y += qapi/qapi-events-block-core.o
|
||||
util-obj-y += qapi/qapi-events-block.o
|
||||
util-obj-y += qapi/qapi-events-char.o
|
||||
util-obj-y += qapi/qapi-events-common.o
|
||||
util-obj-y += qapi/qapi-events-crypto.o
|
||||
util-obj-y += qapi/qapi-events-introspect.o
|
||||
util-obj-y += qapi/qapi-events-migration.o
|
||||
util-obj-y += qapi/qapi-events-misc.o
|
||||
util-obj-y += qapi/qapi-events-net.o
|
||||
util-obj-y += qapi/qapi-events-rocker.o
|
||||
util-obj-y += qapi/qapi-events-run-state.o
|
||||
util-obj-y += qapi/qapi-events-sockets.o
|
||||
util-obj-y += qapi/qapi-events-tpm.o
|
||||
util-obj-y += qapi/qapi-events-trace.o
|
||||
util-obj-y += qapi/qapi-events-transaction.o
|
||||
util-obj-y += qapi/qapi-events-ui.o
|
||||
util-obj-y += qapi/qapi-introspect.o
|
||||
|
||||
chardev-obj-y = chardev/
|
||||
|
||||
#######################################################################
|
||||
# authz-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
authz-obj-y = authz/
|
||||
|
||||
#######################################################################
|
||||
# block-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
block-obj-y = nbd/
|
||||
block-obj-y += block.o blockjob.o job.o
|
||||
block-obj-y += nbd/
|
||||
block-obj-y += block.o blockjob.o
|
||||
block-obj-y += block/ scsi/
|
||||
block-obj-y += qemu-io-cmds.o
|
||||
block-obj-$(CONFIG_REPLICATION) += replication.o
|
||||
@@ -25,7 +74,7 @@ block-obj-m = block/
|
||||
# crypto-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
crypto-obj-y = crypto/
|
||||
crypto-user-obj-y = crypto/
|
||||
crypto-aes-obj-y = crypto/
|
||||
|
||||
#######################################################################
|
||||
# qom-obj-y is code used by both qemu system emulation and qemu-img
|
||||
@@ -45,9 +94,6 @@ io-obj-y = io/
|
||||
ifeq ($(CONFIG_SOFTMMU),y)
|
||||
common-obj-y = blockdev.o blockdev-nbd.o block/
|
||||
common-obj-y += bootdevice.o iothread.o
|
||||
common-obj-y += dump/
|
||||
common-obj-y += job-qmp.o
|
||||
common-obj-y += monitor/
|
||||
common-obj-y += net/
|
||||
common-obj-y += qdev-monitor.o device-hotplug.o
|
||||
common-obj-$(CONFIG_WIN32) += os-win32.o
|
||||
@@ -73,6 +119,8 @@ common-obj-y += vl.o
|
||||
vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS)
|
||||
common-obj-$(CONFIG_TPM) += tpm.o
|
||||
|
||||
common-obj-$(CONFIG_SLIRP) += slirp/
|
||||
|
||||
common-obj-y += backends/
|
||||
common-obj-y += chardev/
|
||||
|
||||
@@ -85,7 +133,25 @@ common-obj-$(CONFIG_FDT) += device_tree.o
|
||||
######################################################################
|
||||
# qapi
|
||||
|
||||
common-obj-y += qapi/
|
||||
common-obj-y += qapi/qapi-commands.o
|
||||
common-obj-y += qapi/qapi-commands-block-core.o
|
||||
common-obj-y += qapi/qapi-commands-block.o
|
||||
common-obj-y += qapi/qapi-commands-char.o
|
||||
common-obj-y += qapi/qapi-commands-common.o
|
||||
common-obj-y += qapi/qapi-commands-crypto.o
|
||||
common-obj-y += qapi/qapi-commands-introspect.o
|
||||
common-obj-y += qapi/qapi-commands-migration.o
|
||||
common-obj-y += qapi/qapi-commands-misc.o
|
||||
common-obj-y += qapi/qapi-commands-net.o
|
||||
common-obj-y += qapi/qapi-commands-rocker.o
|
||||
common-obj-y += qapi/qapi-commands-run-state.o
|
||||
common-obj-y += qapi/qapi-commands-sockets.o
|
||||
common-obj-y += qapi/qapi-commands-tpm.o
|
||||
common-obj-y += qapi/qapi-commands-trace.o
|
||||
common-obj-y += qapi/qapi-commands-transaction.o
|
||||
common-obj-y += qapi/qapi-commands-ui.o
|
||||
common-obj-y += qapi/qapi-introspect.o
|
||||
common-obj-y += qmp.o hmp.o
|
||||
endif
|
||||
|
||||
#######################################################################
|
||||
@@ -102,6 +168,7 @@ version-obj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.o
|
||||
######################################################################
|
||||
# tracing
|
||||
util-obj-y += trace/
|
||||
target-obj-y += trace/
|
||||
|
||||
######################################################################
|
||||
# guest agent
|
||||
@@ -114,7 +181,6 @@ qga-vss-dll-obj-y = qga/
|
||||
|
||||
######################################################################
|
||||
# contrib
|
||||
elf2dmp-obj-y = contrib/elf2dmp/
|
||||
ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/
|
||||
ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/
|
||||
libvhost-user-obj-y = contrib/libvhost-user/
|
||||
@@ -122,87 +188,69 @@ vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS)
|
||||
vhost-user-scsi.o-libs := $(LIBISCSI_LIBS)
|
||||
vhost-user-scsi-obj-y = contrib/vhost-user-scsi/
|
||||
vhost-user-blk-obj-y = contrib/vhost-user-blk/
|
||||
rdmacm-mux-obj-y = contrib/rdmacm-mux/
|
||||
vhost-user-input-obj-y = contrib/vhost-user-input/
|
||||
vhost-user-gpu-obj-y = contrib/vhost-user-gpu/
|
||||
|
||||
######################################################################
|
||||
trace-events-subdirs =
|
||||
trace-events-subdirs += accel/kvm
|
||||
trace-events-subdirs += accel/tcg
|
||||
trace-events-subdirs += util
|
||||
trace-events-subdirs += crypto
|
||||
trace-events-subdirs += monitor
|
||||
ifeq ($(CONFIG_USER_ONLY),y)
|
||||
trace-events-subdirs += linux-user
|
||||
endif
|
||||
ifeq ($(CONFIG_BLOCK),y)
|
||||
trace-events-subdirs += authz
|
||||
trace-events-subdirs += block
|
||||
trace-events-subdirs += io
|
||||
trace-events-subdirs += nbd
|
||||
trace-events-subdirs += scsi
|
||||
endif
|
||||
ifeq ($(CONFIG_SOFTMMU),y)
|
||||
trace-events-subdirs += migration
|
||||
trace-events-subdirs += block
|
||||
trace-events-subdirs += chardev
|
||||
trace-events-subdirs += audio
|
||||
trace-events-subdirs += hw/9pfs
|
||||
trace-events-subdirs += hw/acpi
|
||||
trace-events-subdirs += hw/alpha
|
||||
trace-events-subdirs += hw/arm
|
||||
trace-events-subdirs += hw/audio
|
||||
trace-events-subdirs += hw/block
|
||||
trace-events-subdirs += hw/block/dataplane
|
||||
trace-events-subdirs += hw/char
|
||||
trace-events-subdirs += hw/dma
|
||||
trace-events-subdirs += hw/hppa
|
||||
trace-events-subdirs += hw/i2c
|
||||
trace-events-subdirs += hw/i386
|
||||
trace-events-subdirs += hw/i386/xen
|
||||
trace-events-subdirs += hw/ide
|
||||
trace-events-subdirs += hw/input
|
||||
trace-events-subdirs += hw/intc
|
||||
trace-events-subdirs += hw/isa
|
||||
trace-events-subdirs += hw/mem
|
||||
trace-events-subdirs += hw/mips
|
||||
trace-events-subdirs += hw/misc
|
||||
trace-events-subdirs += hw/misc/macio
|
||||
trace-events-subdirs += hw/net
|
||||
trace-events-subdirs += hw/nvram
|
||||
trace-events-subdirs += hw/pci
|
||||
trace-events-subdirs += hw/pci-host
|
||||
trace-events-subdirs += hw/ppc
|
||||
trace-events-subdirs += hw/rdma
|
||||
trace-events-subdirs += hw/rdma/vmw
|
||||
trace-events-subdirs += hw/s390x
|
||||
trace-events-subdirs += hw/virtio
|
||||
trace-events-subdirs += hw/audio
|
||||
trace-events-subdirs += hw/misc
|
||||
trace-events-subdirs += hw/misc/macio
|
||||
trace-events-subdirs += hw/usb
|
||||
trace-events-subdirs += hw/scsi
|
||||
trace-events-subdirs += hw/sd
|
||||
trace-events-subdirs += hw/nvram
|
||||
trace-events-subdirs += hw/display
|
||||
trace-events-subdirs += hw/input
|
||||
trace-events-subdirs += hw/timer
|
||||
trace-events-subdirs += hw/dma
|
||||
trace-events-subdirs += hw/sparc
|
||||
trace-events-subdirs += hw/sparc64
|
||||
trace-events-subdirs += hw/timer
|
||||
trace-events-subdirs += hw/tpm
|
||||
trace-events-subdirs += hw/usb
|
||||
trace-events-subdirs += hw/sd
|
||||
trace-events-subdirs += hw/isa
|
||||
trace-events-subdirs += hw/mem
|
||||
trace-events-subdirs += hw/i386
|
||||
trace-events-subdirs += hw/i386/xen
|
||||
trace-events-subdirs += hw/9pfs
|
||||
trace-events-subdirs += hw/ppc
|
||||
trace-events-subdirs += hw/pci
|
||||
trace-events-subdirs += hw/pci-host
|
||||
trace-events-subdirs += hw/s390x
|
||||
trace-events-subdirs += hw/vfio
|
||||
trace-events-subdirs += hw/virtio
|
||||
trace-events-subdirs += hw/watchdog
|
||||
trace-events-subdirs += hw/acpi
|
||||
trace-events-subdirs += hw/arm
|
||||
trace-events-subdirs += hw/alpha
|
||||
trace-events-subdirs += hw/hppa
|
||||
trace-events-subdirs += hw/xen
|
||||
trace-events-subdirs += hw/gpio
|
||||
trace-events-subdirs += hw/riscv
|
||||
trace-events-subdirs += migration
|
||||
trace-events-subdirs += net
|
||||
trace-events-subdirs += hw/ide
|
||||
trace-events-subdirs += hw/tpm
|
||||
trace-events-subdirs += ui
|
||||
endif
|
||||
trace-events-subdirs += hw/display
|
||||
trace-events-subdirs += qapi
|
||||
trace-events-subdirs += qom
|
||||
trace-events-subdirs += audio
|
||||
trace-events-subdirs += net
|
||||
trace-events-subdirs += target/arm
|
||||
trace-events-subdirs += target/hppa
|
||||
trace-events-subdirs += target/i386
|
||||
trace-events-subdirs += target/mips
|
||||
trace-events-subdirs += target/ppc
|
||||
trace-events-subdirs += target/riscv
|
||||
trace-events-subdirs += target/s390x
|
||||
trace-events-subdirs += target/sparc
|
||||
trace-events-subdirs += util
|
||||
trace-events-subdirs += target/s390x
|
||||
trace-events-subdirs += target/ppc
|
||||
trace-events-subdirs += qom
|
||||
trace-events-subdirs += linux-user
|
||||
trace-events-subdirs += qapi
|
||||
trace-events-subdirs += accel/tcg
|
||||
trace-events-subdirs += accel/kvm
|
||||
trace-events-subdirs += nbd
|
||||
trace-events-subdirs += scsi
|
||||
|
||||
trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events)
|
||||
|
||||
|
||||
@@ -4,19 +4,16 @@ BUILD_DIR?=$(CURDIR)/..
|
||||
|
||||
include ../config-host.mak
|
||||
include config-target.mak
|
||||
include $(SRC_PATH)/rules.mak
|
||||
|
||||
ifdef CONFIG_SOFTMMU
|
||||
include config-devices.mak
|
||||
endif
|
||||
include $(SRC_PATH)/rules.mak
|
||||
|
||||
$(call set-vpath, $(SRC_PATH):$(BUILD_DIR))
|
||||
ifdef CONFIG_LINUX
|
||||
QEMU_CFLAGS += -I../linux-headers
|
||||
endif
|
||||
QEMU_CFLAGS += -iquote .. -iquote $(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H
|
||||
QEMU_CFLAGS += -I.. -I$(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H
|
||||
|
||||
QEMU_CFLAGS+=-iquote $(SRC_PATH)/include
|
||||
QEMU_CFLAGS+=-I$(SRC_PATH)/include
|
||||
|
||||
ifdef CONFIG_USER_ONLY
|
||||
# user emulator name
|
||||
@@ -39,17 +36,11 @@ endif
|
||||
PROGS=$(QEMU_PROG) $(QEMU_PROGW)
|
||||
STPFILES=
|
||||
|
||||
# Makefile Tests
|
||||
include $(SRC_PATH)/tests/tcg/Makefile.include
|
||||
|
||||
config-target.h: config-target.h-timestamp
|
||||
config-target.h-timestamp: config-target.mak
|
||||
|
||||
config-devices.h: config-devices.h-timestamp
|
||||
config-devices.h-timestamp: config-devices.mak
|
||||
|
||||
ifdef CONFIG_TRACE_SYSTEMTAP
|
||||
stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp $(QEMU_PROG)-log.stp
|
||||
stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp
|
||||
|
||||
ifdef CONFIG_USER_ONLY
|
||||
TARGET_TYPE=user
|
||||
@@ -88,14 +79,6 @@ $(QEMU_PROG)-simpletrace.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y)
|
||||
--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \
|
||||
$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp")
|
||||
|
||||
$(QEMU_PROG)-log.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y)
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--group=all \
|
||||
--format=log-stap \
|
||||
--backends=$(TRACE_BACKENDS) \
|
||||
--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \
|
||||
$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-log.stp")
|
||||
|
||||
else
|
||||
stap:
|
||||
endif
|
||||
@@ -106,8 +89,6 @@ all: $(PROGS) stap
|
||||
# Dummy command so that make thinks it has done something
|
||||
@true
|
||||
|
||||
obj-y += trace/
|
||||
|
||||
#########################################################
|
||||
# cpu emulator library
|
||||
obj-y += exec.o
|
||||
@@ -116,7 +97,7 @@ obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o
|
||||
obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o
|
||||
obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o
|
||||
obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o
|
||||
obj-$(CONFIG_TCG) += fpu/softfloat.o
|
||||
obj-y += fpu/softfloat.o
|
||||
obj-y += target/$(TARGET_BASE_ARCH)/
|
||||
obj-y += disas.o
|
||||
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
|
||||
@@ -151,14 +132,12 @@ endif #CONFIG_BSD_USER
|
||||
#########################################################
|
||||
# System emulator target
|
||||
ifdef CONFIG_SOFTMMU
|
||||
obj-y += arch_init.o cpus.o gdbstub.o balloon.o ioport.o
|
||||
obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
|
||||
obj-y += qtest.o
|
||||
obj-y += dump/
|
||||
obj-y += hw/
|
||||
obj-y += monitor/
|
||||
obj-y += qapi/
|
||||
obj-y += memory.o
|
||||
obj-y += memory_mapping.o
|
||||
obj-y += dump.o
|
||||
obj-y += migration/ram.o
|
||||
LIBS := $(libs_softmmu) $(LIBS)
|
||||
|
||||
@@ -169,37 +148,43 @@ else
|
||||
obj-y += hw/$(TARGET_BASE_ARCH)/
|
||||
endif
|
||||
|
||||
generated-files-y += hmp-commands.h hmp-commands-info.h
|
||||
generated-files-y += config-devices.h
|
||||
GENERATED_FILES += hmp-commands.h hmp-commands-info.h
|
||||
|
||||
endif # CONFIG_SOFTMMU
|
||||
|
||||
# Workaround for http://gcc.gnu.org/PR55489, see configure.
|
||||
%/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS)
|
||||
|
||||
dummy := $(call unnest-vars,,obj-y)
|
||||
all-obj-y := $(obj-y)
|
||||
|
||||
target-obj-y :=
|
||||
block-obj-y :=
|
||||
common-obj-y :=
|
||||
chardev-obj-y :=
|
||||
include $(SRC_PATH)/Makefile.objs
|
||||
dummy := $(call unnest-vars,,target-obj-y)
|
||||
target-obj-y-save := $(target-obj-y)
|
||||
dummy := $(call unnest-vars,.., \
|
||||
authz-obj-y \
|
||||
block-obj-y \
|
||||
block-obj-m \
|
||||
chardev-obj-y \
|
||||
crypto-obj-y \
|
||||
crypto-user-obj-y \
|
||||
crypto-aes-obj-y \
|
||||
qom-obj-y \
|
||||
io-obj-y \
|
||||
common-obj-y \
|
||||
common-obj-m)
|
||||
target-obj-y := $(target-obj-y-save)
|
||||
all-obj-y += $(common-obj-y)
|
||||
all-obj-y += $(target-obj-y)
|
||||
all-obj-y += $(qom-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(authz-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y)
|
||||
all-obj-$(CONFIG_USER_ONLY) += $(crypto-user-obj-y)
|
||||
all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
|
||||
|
||||
ifdef CONFIG_SOFTMMU
|
||||
$(QEMU_PROG_BUILD): config-devices.mak
|
||||
endif
|
||||
|
||||
COMMON_LDADDS = ../libqemuutil.a
|
||||
|
||||
@@ -224,7 +209,6 @@ clean: clean-target
|
||||
rm -f *.a *~ $(PROGS)
|
||||
rm -f $(shell find . -name '*.[od]')
|
||||
rm -f hmp-commands.h gdbstub-xml.c
|
||||
rm -f trace/generated-helpers.c trace/generated-helpers.c-timestamp
|
||||
ifdef CONFIG_TRACE_SYSTEMTAP
|
||||
rm -f *.stp
|
||||
endif
|
||||
@@ -237,24 +221,7 @@ ifdef CONFIG_TRACE_SYSTEMTAP
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset"
|
||||
$(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp"
|
||||
$(INSTALL_DATA) $(QEMU_PROG)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp"
|
||||
$(INSTALL_DATA) $(QEMU_PROG)-log.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-log.stp"
|
||||
endif
|
||||
|
||||
generated-files-y += config-target.h
|
||||
Makefile: $(generated-files-y)
|
||||
|
||||
# Reports/Analysis
|
||||
#
|
||||
# The target specific coverage report only cares about target specific
|
||||
# blobs and not the shared code.
|
||||
#
|
||||
|
||||
%/coverage-report.html:
|
||||
@mkdir -p $*
|
||||
$(call quiet-command,\
|
||||
gcovr -r $(SRC_PATH) --object-directory $(CURDIR) \
|
||||
-p --html --html-details -o $@, \
|
||||
"GEN", "coverage-report.html")
|
||||
|
||||
.PHONY: coverage-report
|
||||
coverage-report: $(CURDIR)/reports/coverage/coverage-report.html
|
||||
GENERATED_FILES += config-target.h
|
||||
Makefile: $(GENERATED_FILES)
|
||||
|
||||
4
README
4
README
@@ -54,7 +54,7 @@ Submitting patches
|
||||
|
||||
The QEMU source code is maintained under the GIT version control system.
|
||||
|
||||
git clone https://git.qemu.org/git/qemu.git
|
||||
git clone git://git.qemu.org/qemu.git
|
||||
|
||||
When submitting patches, one common approach is to use 'git
|
||||
format-patch' and/or 'git send-email' to format & send the mail to the
|
||||
@@ -70,7 +70,7 @@ the QEMU website
|
||||
|
||||
The QEMU website is also maintained under source control.
|
||||
|
||||
git clone https://git.qemu.org/git/qemu-web.git
|
||||
git clone git://git.qemu.org/qemu-web.git
|
||||
https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/
|
||||
|
||||
A 'git-publish' utility was created to make above process less
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
obj-$(CONFIG_SOFTMMU) += accel.o
|
||||
obj-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_POSIX)) += qtest.o
|
||||
obj-$(CONFIG_KVM) += kvm/
|
||||
obj-y += kvm/
|
||||
obj-$(CONFIG_TCG) += tcg/
|
||||
obj-y += stubs/
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
#include "qom/object.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
static const TypeInfo accel_type = {
|
||||
.name = TYPE_ACCEL,
|
||||
@@ -65,16 +64,14 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms)
|
||||
ms->accelerator = NULL;
|
||||
*(acc->allowed) = false;
|
||||
object_unref(OBJECT(accel));
|
||||
} else {
|
||||
object_set_accelerator_compat_props(acc->compat_props);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void configure_accelerator(MachineState *ms, const char *progname)
|
||||
void configure_accelerator(MachineState *ms)
|
||||
{
|
||||
const char *accel;
|
||||
char **accel_list, **tmp;
|
||||
const char *accel, *p;
|
||||
char buf[10];
|
||||
int ret;
|
||||
bool accel_initialised = false;
|
||||
bool init_failed = false;
|
||||
@@ -82,31 +79,25 @@ void configure_accelerator(MachineState *ms, const char *progname)
|
||||
|
||||
accel = qemu_opt_get(qemu_get_machine_opts(), "accel");
|
||||
if (accel == NULL) {
|
||||
/* Select the default accelerator */
|
||||
int pnlen = strlen(progname);
|
||||
if (pnlen >= 3 && g_str_equal(&progname[pnlen - 3], "kvm")) {
|
||||
/* If the program name ends with "kvm", we prefer KVM */
|
||||
accel = "kvm:tcg";
|
||||
} else {
|
||||
#if defined(CONFIG_TCG)
|
||||
accel = "tcg";
|
||||
#elif defined(CONFIG_KVM)
|
||||
accel = "kvm";
|
||||
#else
|
||||
error_report("No accelerator selected and"
|
||||
" no default accelerator available");
|
||||
exit(1);
|
||||
#endif
|
||||
}
|
||||
/* Use the default "accelerator", tcg */
|
||||
accel = "tcg";
|
||||
}
|
||||
|
||||
accel_list = g_strsplit(accel, ":", 0);
|
||||
|
||||
for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) {
|
||||
acc = accel_find(*tmp);
|
||||
p = accel;
|
||||
while (!accel_initialised && *p != '\0') {
|
||||
if (*p == ':') {
|
||||
p++;
|
||||
}
|
||||
p = get_opt_name(buf, sizeof(buf), p, ':');
|
||||
acc = accel_find(buf);
|
||||
if (!acc) {
|
||||
continue;
|
||||
}
|
||||
if (acc->available && !acc->available()) {
|
||||
printf("%s not supported for this target\n",
|
||||
acc->name);
|
||||
continue;
|
||||
}
|
||||
ret = accel_init_machine(acc, ms);
|
||||
if (ret < 0) {
|
||||
init_failed = true;
|
||||
@@ -116,7 +107,6 @@ void configure_accelerator(MachineState *ms, const char *progname)
|
||||
accel_initialised = true;
|
||||
}
|
||||
}
|
||||
g_strfreev(accel_list);
|
||||
|
||||
if (!accel_initialised) {
|
||||
if (!init_failed) {
|
||||
@@ -130,13 +120,10 @@ void configure_accelerator(MachineState *ms, const char *progname)
|
||||
}
|
||||
}
|
||||
|
||||
void accel_setup_post(MachineState *ms)
|
||||
void accel_register_compat_props(AccelState *accel)
|
||||
{
|
||||
AccelState *accel = ms->accelerator;
|
||||
AccelClass *acc = ACCEL_GET_CLASS(accel);
|
||||
if (acc->setup_post) {
|
||||
acc->setup_post(ms, accel);
|
||||
}
|
||||
AccelClass *class = ACCEL_GET_CLASS(accel);
|
||||
register_compat_props_array(class->global_props);
|
||||
}
|
||||
|
||||
static void register_accel_types(void)
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
obj-y += kvm-all.o
|
||||
obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o
|
||||
obj-$(CONFIG_KVM) += kvm-all.o
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <linux/kvm.h>
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/atomic.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/config-file.h"
|
||||
@@ -37,8 +38,6 @@
|
||||
#include "qemu/event_notifier.h"
|
||||
#include "trace.h"
|
||||
#include "hw/irq.h"
|
||||
#include "sysemu/sev.h"
|
||||
#include "sysemu/balloon.h"
|
||||
|
||||
#include "hw/boards.h"
|
||||
|
||||
@@ -78,20 +77,17 @@ struct KVMState
|
||||
int fd;
|
||||
int vmfd;
|
||||
int coalesced_mmio;
|
||||
int coalesced_pio;
|
||||
struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
|
||||
bool coalesced_flush_in_progress;
|
||||
int vcpu_events;
|
||||
int robust_singlestep;
|
||||
int debugregs;
|
||||
#ifdef KVM_CAP_SET_GUEST_DEBUG
|
||||
QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints;
|
||||
struct kvm_sw_breakpoint_head kvm_sw_breakpoints;
|
||||
#endif
|
||||
int max_nested_state_len;
|
||||
int many_ioeventfds;
|
||||
int intx_set_mask;
|
||||
bool sync_mmu;
|
||||
bool manual_dirty_log_protect;
|
||||
/* The man page (and posix) say ioctl numbers are signed int, but
|
||||
* they're not. Linux, glibc and *BSD all treat ioctl numbers as
|
||||
* unsigned, and treating them as signed here can break things */
|
||||
@@ -103,14 +99,10 @@ struct KVMState
|
||||
int nr_allocated_irq_routes;
|
||||
unsigned long *used_gsi_bitmap;
|
||||
unsigned int gsi_count;
|
||||
QTAILQ_HEAD(, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE];
|
||||
QTAILQ_HEAD(msi_hashtab, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE];
|
||||
#endif
|
||||
KVMMemoryListener memory_listener;
|
||||
QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus;
|
||||
|
||||
/* memory encryption */
|
||||
void *memcrypt_handle;
|
||||
int (*memcrypt_encrypt_data)(void *handle, uint8_t *ptr, uint64_t len);
|
||||
};
|
||||
|
||||
KVMState *kvm_state;
|
||||
@@ -139,9 +131,6 @@ static const KVMCapabilityInfo kvm_required_capabilites[] = {
|
||||
KVM_CAP_LAST_INFO
|
||||
};
|
||||
|
||||
#define kvm_slots_lock(kml) qemu_mutex_lock(&(kml)->slots_lock)
|
||||
#define kvm_slots_unlock(kml) qemu_mutex_unlock(&(kml)->slots_lock)
|
||||
|
||||
int kvm_get_max_memslots(void)
|
||||
{
|
||||
KVMState *s = KVM_STATE(current_machine->accelerator);
|
||||
@@ -149,27 +138,6 @@ int kvm_get_max_memslots(void)
|
||||
return s->nr_slots;
|
||||
}
|
||||
|
||||
bool kvm_memcrypt_enabled(void)
|
||||
{
|
||||
if (kvm_state && kvm_state->memcrypt_handle) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len)
|
||||
{
|
||||
if (kvm_state->memcrypt_handle &&
|
||||
kvm_state->memcrypt_encrypt_data) {
|
||||
return kvm_state->memcrypt_encrypt_data(kvm_state->memcrypt_handle,
|
||||
ptr, len);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Called with KVMMemoryListener.slots_lock held */
|
||||
static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml)
|
||||
{
|
||||
KVMState *s = kvm_state;
|
||||
@@ -187,17 +155,10 @@ static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml)
|
||||
bool kvm_has_free_slot(MachineState *ms)
|
||||
{
|
||||
KVMState *s = KVM_STATE(ms->accelerator);
|
||||
bool result;
|
||||
KVMMemoryListener *kml = &s->memory_listener;
|
||||
|
||||
kvm_slots_lock(kml);
|
||||
result = !!kvm_get_free_slot(kml);
|
||||
kvm_slots_unlock(kml);
|
||||
|
||||
return result;
|
||||
return kvm_get_free_slot(&s->memory_listener);
|
||||
}
|
||||
|
||||
/* Called with KVMMemoryListener.slots_lock held */
|
||||
static KVMSlot *kvm_alloc_slot(KVMMemoryListener *kml)
|
||||
{
|
||||
KVMSlot *slot = kvm_get_free_slot(kml);
|
||||
@@ -256,24 +217,21 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram,
|
||||
hwaddr *phys_addr)
|
||||
{
|
||||
KVMMemoryListener *kml = &s->memory_listener;
|
||||
int i, ret = 0;
|
||||
int i;
|
||||
|
||||
kvm_slots_lock(kml);
|
||||
for (i = 0; i < s->nr_slots; i++) {
|
||||
KVMSlot *mem = &kml->slots[i];
|
||||
|
||||
if (ram >= mem->ram && ram < mem->ram + mem->memory_size) {
|
||||
*phys_addr = mem->start_addr + (ram - mem->ram);
|
||||
ret = 1;
|
||||
break;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
kvm_slots_unlock(kml);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new)
|
||||
static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot)
|
||||
{
|
||||
KVMState *s = kvm_state;
|
||||
struct kvm_userspace_memory_region mem;
|
||||
@@ -284,7 +242,7 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo
|
||||
mem.userspace_addr = (unsigned long)slot->ram;
|
||||
mem.flags = slot->flags;
|
||||
|
||||
if (slot->memory_size && !new && (mem.flags ^ slot->old_flags) & KVM_MEM_READONLY) {
|
||||
if (slot->memory_size && mem.flags & KVM_MEM_READONLY) {
|
||||
/* Set the slot size to 0 before setting the slot to the desired
|
||||
* value. This is needed based on KVM commit 75d61fbc. */
|
||||
mem.memory_size = 0;
|
||||
@@ -292,7 +250,6 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo
|
||||
}
|
||||
mem.memory_size = slot->memory_size;
|
||||
ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem);
|
||||
slot->old_flags = mem.flags;
|
||||
trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr,
|
||||
mem.memory_size, mem.userspace_addr, ret);
|
||||
return ret;
|
||||
@@ -307,11 +264,6 @@ int kvm_destroy_vcpu(CPUState *cpu)
|
||||
|
||||
DPRINTF("kvm_destroy_vcpu\n");
|
||||
|
||||
ret = kvm_arch_destroy_vcpu(cpu);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0);
|
||||
if (mmap_size < 0) {
|
||||
ret = mmap_size;
|
||||
@@ -411,18 +363,20 @@ static int kvm_mem_flags(MemoryRegion *mr)
|
||||
return flags;
|
||||
}
|
||||
|
||||
/* Called with KVMMemoryListener.slots_lock held */
|
||||
static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem,
|
||||
MemoryRegion *mr)
|
||||
{
|
||||
int old_flags;
|
||||
|
||||
old_flags = mem->flags;
|
||||
mem->flags = kvm_mem_flags(mr);
|
||||
|
||||
/* If nothing changed effectively, no need to issue ioctl */
|
||||
if (mem->flags == mem->old_flags) {
|
||||
if (mem->flags == old_flags) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return kvm_set_user_memory_region(kml, mem, false);
|
||||
return kvm_set_user_memory_region(kml, mem);
|
||||
}
|
||||
|
||||
static int kvm_section_update_flags(KVMMemoryListener *kml,
|
||||
@@ -430,26 +384,19 @@ static int kvm_section_update_flags(KVMMemoryListener *kml,
|
||||
{
|
||||
hwaddr start_addr, size;
|
||||
KVMSlot *mem;
|
||||
int ret = 0;
|
||||
|
||||
size = kvm_align_section(section, &start_addr);
|
||||
if (!size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
kvm_slots_lock(kml);
|
||||
|
||||
mem = kvm_lookup_matching_slot(kml, start_addr, size);
|
||||
if (!mem) {
|
||||
/* We don't have a slot if we want to trap every access. */
|
||||
goto out;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = kvm_slot_update_flags(kml, mem, section->mr);
|
||||
|
||||
out:
|
||||
kvm_slots_unlock(kml);
|
||||
return ret;
|
||||
return kvm_slot_update_flags(kml, mem, section->mr);
|
||||
}
|
||||
|
||||
static void kvm_log_start(MemoryListener *listener,
|
||||
@@ -501,15 +448,13 @@ static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section,
|
||||
#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1))
|
||||
|
||||
/**
|
||||
* kvm_physical_sync_dirty_bitmap - Sync dirty bitmap from kernel space
|
||||
* kvm_physical_sync_dirty_bitmap - Grab dirty bitmap from kernel space
|
||||
* This function updates qemu's dirty bitmap using
|
||||
* memory_region_set_dirty(). This means all bits are set
|
||||
* to dirty.
|
||||
*
|
||||
* This function will first try to fetch dirty bitmap from the kernel,
|
||||
* and then updates qemu's dirty bitmap.
|
||||
*
|
||||
* NOTE: caller must be with kml->slots_lock held.
|
||||
*
|
||||
* @kml: the KVM memory listener object
|
||||
* @section: the memory section to sync the dirty bitmap with
|
||||
* @start_add: start of logged region.
|
||||
* @end_addr: end of logged region.
|
||||
*/
|
||||
static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
|
||||
MemoryRegionSection *section)
|
||||
@@ -518,14 +463,13 @@ static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
|
||||
struct kvm_dirty_log d = {};
|
||||
KVMSlot *mem;
|
||||
hwaddr start_addr, size;
|
||||
int ret = 0;
|
||||
|
||||
size = kvm_align_section(section, &start_addr);
|
||||
if (size) {
|
||||
mem = kvm_lookup_matching_slot(kml, start_addr, size);
|
||||
if (!mem) {
|
||||
/* We don't have a slot if we want to trap every access. */
|
||||
goto out;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX bad kernel interface alert
|
||||
@@ -542,176 +486,20 @@ static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
|
||||
*/
|
||||
size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS),
|
||||
/*HOST_LONG_BITS*/ 64) / 8;
|
||||
if (!mem->dirty_bmap) {
|
||||
/* Allocate on the first log_sync, once and for all */
|
||||
mem->dirty_bmap = g_malloc0(size);
|
||||
}
|
||||
d.dirty_bitmap = g_malloc0(size);
|
||||
|
||||
d.dirty_bitmap = mem->dirty_bmap;
|
||||
d.slot = mem->slot | (kml->as_id << 16);
|
||||
if (kvm_vm_ioctl(s, KVM_GET_DIRTY_LOG, &d) == -1) {
|
||||
DPRINTF("ioctl failed %d\n", errno);
|
||||
ret = -1;
|
||||
goto out;
|
||||
g_free(d.dirty_bitmap);
|
||||
return -1;
|
||||
}
|
||||
|
||||
kvm_get_dirty_pages_log_range(section, d.dirty_bitmap);
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Alignment requirement for KVM_CLEAR_DIRTY_LOG - 64 pages */
|
||||
#define KVM_CLEAR_LOG_SHIFT 6
|
||||
#define KVM_CLEAR_LOG_ALIGN (qemu_real_host_page_size << KVM_CLEAR_LOG_SHIFT)
|
||||
#define KVM_CLEAR_LOG_MASK (-KVM_CLEAR_LOG_ALIGN)
|
||||
|
||||
/**
|
||||
* kvm_physical_log_clear - Clear the kernel's dirty bitmap for range
|
||||
*
|
||||
* NOTE: this will be a no-op if we haven't enabled manual dirty log
|
||||
* protection in the host kernel because in that case this operation
|
||||
* will be done within log_sync().
|
||||
*
|
||||
* @kml: the kvm memory listener
|
||||
* @section: the memory range to clear dirty bitmap
|
||||
*/
|
||||
static int kvm_physical_log_clear(KVMMemoryListener *kml,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
KVMState *s = kvm_state;
|
||||
struct kvm_clear_dirty_log d;
|
||||
uint64_t start, end, bmap_start, start_delta, bmap_npages, size;
|
||||
unsigned long *bmap_clear = NULL, psize = qemu_real_host_page_size;
|
||||
KVMSlot *mem = NULL;
|
||||
int ret, i;
|
||||
|
||||
if (!s->manual_dirty_log_protect) {
|
||||
/* No need to do explicit clear */
|
||||
return 0;
|
||||
g_free(d.dirty_bitmap);
|
||||
}
|
||||
|
||||
start = section->offset_within_address_space;
|
||||
size = int128_get64(section->size);
|
||||
|
||||
if (!size) {
|
||||
/* Nothing more we can do... */
|
||||
return 0;
|
||||
}
|
||||
|
||||
kvm_slots_lock(kml);
|
||||
|
||||
/* Find any possible slot that covers the section */
|
||||
for (i = 0; i < s->nr_slots; i++) {
|
||||
mem = &kml->slots[i];
|
||||
if (mem->start_addr <= start &&
|
||||
start + size <= mem->start_addr + mem->memory_size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We should always find one memslot until this point, otherwise
|
||||
* there could be something wrong from the upper layer
|
||||
*/
|
||||
assert(mem && i != s->nr_slots);
|
||||
|
||||
/*
|
||||
* We need to extend either the start or the size or both to
|
||||
* satisfy the KVM interface requirement. Firstly, do the start
|
||||
* page alignment on 64 host pages
|
||||
*/
|
||||
bmap_start = (start - mem->start_addr) & KVM_CLEAR_LOG_MASK;
|
||||
start_delta = start - mem->start_addr - bmap_start;
|
||||
bmap_start /= psize;
|
||||
|
||||
/*
|
||||
* The kernel interface has restriction on the size too, that either:
|
||||
*
|
||||
* (1) the size is 64 host pages aligned (just like the start), or
|
||||
* (2) the size fills up until the end of the KVM memslot.
|
||||
*/
|
||||
bmap_npages = DIV_ROUND_UP(size + start_delta, KVM_CLEAR_LOG_ALIGN)
|
||||
<< KVM_CLEAR_LOG_SHIFT;
|
||||
end = mem->memory_size / psize;
|
||||
if (bmap_npages > end - bmap_start) {
|
||||
bmap_npages = end - bmap_start;
|
||||
}
|
||||
start_delta /= psize;
|
||||
|
||||
/*
|
||||
* Prepare the bitmap to clear dirty bits. Here we must guarantee
|
||||
* that we won't clear any unknown dirty bits otherwise we might
|
||||
* accidentally clear some set bits which are not yet synced from
|
||||
* the kernel into QEMU's bitmap, then we'll lose track of the
|
||||
* guest modifications upon those pages (which can directly lead
|
||||
* to guest data loss or panic after migration).
|
||||
*
|
||||
* Layout of the KVMSlot.dirty_bmap:
|
||||
*
|
||||
* |<-------- bmap_npages -----------..>|
|
||||
* [1]
|
||||
* start_delta size
|
||||
* |----------------|-------------|------------------|------------|
|
||||
* ^ ^ ^ ^
|
||||
* | | | |
|
||||
* start bmap_start (start) end
|
||||
* of memslot of memslot
|
||||
*
|
||||
* [1] bmap_npages can be aligned to either 64 pages or the end of slot
|
||||
*/
|
||||
|
||||
assert(bmap_start % BITS_PER_LONG == 0);
|
||||
/* We should never do log_clear before log_sync */
|
||||
assert(mem->dirty_bmap);
|
||||
if (start_delta) {
|
||||
/* Slow path - we need to manipulate a temp bitmap */
|
||||
bmap_clear = bitmap_new(bmap_npages);
|
||||
bitmap_copy_with_src_offset(bmap_clear, mem->dirty_bmap,
|
||||
bmap_start, start_delta + size / psize);
|
||||
/*
|
||||
* We need to fill the holes at start because that was not
|
||||
* specified by the caller and we extended the bitmap only for
|
||||
* 64 pages alignment
|
||||
*/
|
||||
bitmap_clear(bmap_clear, 0, start_delta);
|
||||
d.dirty_bitmap = bmap_clear;
|
||||
} else {
|
||||
/* Fast path - start address aligns well with BITS_PER_LONG */
|
||||
d.dirty_bitmap = mem->dirty_bmap + BIT_WORD(bmap_start);
|
||||
}
|
||||
|
||||
d.first_page = bmap_start;
|
||||
/* It should never overflow. If it happens, say something */
|
||||
assert(bmap_npages <= UINT32_MAX);
|
||||
d.num_pages = bmap_npages;
|
||||
d.slot = mem->slot | (kml->as_id << 16);
|
||||
|
||||
if (kvm_vm_ioctl(s, KVM_CLEAR_DIRTY_LOG, &d) == -1) {
|
||||
ret = -errno;
|
||||
error_report("%s: KVM_CLEAR_DIRTY_LOG failed, slot=%d, "
|
||||
"start=0x%"PRIx64", size=0x%"PRIx32", errno=%d",
|
||||
__func__, d.slot, (uint64_t)d.first_page,
|
||||
(uint32_t)d.num_pages, ret);
|
||||
} else {
|
||||
ret = 0;
|
||||
trace_kvm_clear_dirty_log(d.slot, d.first_page, d.num_pages);
|
||||
}
|
||||
|
||||
/*
|
||||
* After we have updated the remote dirty bitmap, we update the
|
||||
* cached bitmap as well for the memslot, then if another user
|
||||
* clears the same region we know we shouldn't clear it again on
|
||||
* the remote otherwise it's data loss as well.
|
||||
*/
|
||||
bitmap_clear(mem->dirty_bmap, bmap_start + start_delta,
|
||||
size / psize);
|
||||
/* This handles the NULL case well */
|
||||
g_free(bmap_clear);
|
||||
|
||||
kvm_slots_unlock(kml);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kvm_coalesce_mmio_region(MemoryListener *listener,
|
||||
@@ -748,45 +536,6 @@ static void kvm_uncoalesce_mmio_region(MemoryListener *listener,
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_coalesce_pio_add(MemoryListener *listener,
|
||||
MemoryRegionSection *section,
|
||||
hwaddr start, hwaddr size)
|
||||
{
|
||||
KVMState *s = kvm_state;
|
||||
|
||||
if (s->coalesced_pio) {
|
||||
struct kvm_coalesced_mmio_zone zone;
|
||||
|
||||
zone.addr = start;
|
||||
zone.size = size;
|
||||
zone.pio = 1;
|
||||
|
||||
(void)kvm_vm_ioctl(s, KVM_REGISTER_COALESCED_MMIO, &zone);
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_coalesce_pio_del(MemoryListener *listener,
|
||||
MemoryRegionSection *section,
|
||||
hwaddr start, hwaddr size)
|
||||
{
|
||||
KVMState *s = kvm_state;
|
||||
|
||||
if (s->coalesced_pio) {
|
||||
struct kvm_coalesced_mmio_zone zone;
|
||||
|
||||
zone.addr = start;
|
||||
zone.size = size;
|
||||
zone.pio = 1;
|
||||
|
||||
(void)kvm_vm_ioctl(s, KVM_UNREGISTER_COALESCED_MMIO, &zone);
|
||||
}
|
||||
}
|
||||
|
||||
static MemoryListener kvm_coalesced_pio_listener = {
|
||||
.coalesced_io_add = kvm_coalesce_pio_add,
|
||||
.coalesced_io_del = kvm_coalesce_pio_del,
|
||||
};
|
||||
|
||||
int kvm_check_extension(KVMState *s, unsigned int extension)
|
||||
{
|
||||
int ret;
|
||||
@@ -844,8 +593,6 @@ static int kvm_set_ioeventfd_mmio(int fd, hwaddr addr, uint32_t val,
|
||||
.fd = fd,
|
||||
};
|
||||
|
||||
trace_kvm_set_ioeventfd_mmio(fd, (uint64_t)addr, val, assign, size,
|
||||
datamatch);
|
||||
if (!kvm_enabled()) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
@@ -877,7 +624,6 @@ static int kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint16_t val,
|
||||
.fd = fd,
|
||||
};
|
||||
int r;
|
||||
trace_kvm_set_ioeventfd_pio(fd, addr, val, assign, size, datamatch);
|
||||
if (!kvm_enabled()) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
@@ -973,29 +719,24 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
||||
ram = memory_region_get_ram_ptr(mr) + section->offset_within_region +
|
||||
(start_addr - section->offset_within_address_space);
|
||||
|
||||
kvm_slots_lock(kml);
|
||||
|
||||
if (!add) {
|
||||
mem = kvm_lookup_matching_slot(kml, start_addr, size);
|
||||
if (!mem) {
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
|
||||
kvm_physical_sync_dirty_bitmap(kml, section);
|
||||
}
|
||||
|
||||
/* unregister the slot */
|
||||
g_free(mem->dirty_bmap);
|
||||
mem->dirty_bmap = NULL;
|
||||
mem->memory_size = 0;
|
||||
mem->flags = 0;
|
||||
err = kvm_set_user_memory_region(kml, mem, false);
|
||||
err = kvm_set_user_memory_region(kml, mem);
|
||||
if (err) {
|
||||
fprintf(stderr, "%s: error unregistering slot: %s\n",
|
||||
__func__, strerror(-err));
|
||||
abort();
|
||||
}
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
|
||||
/* register the new slot */
|
||||
@@ -1005,15 +746,12 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
||||
mem->ram = ram;
|
||||
mem->flags = kvm_mem_flags(mr);
|
||||
|
||||
err = kvm_set_user_memory_region(kml, mem, true);
|
||||
err = kvm_set_user_memory_region(kml, mem);
|
||||
if (err) {
|
||||
fprintf(stderr, "%s: error registering slot: %s\n", __func__,
|
||||
strerror(-err));
|
||||
abort();
|
||||
}
|
||||
|
||||
out:
|
||||
kvm_slots_unlock(kml);
|
||||
}
|
||||
|
||||
static void kvm_region_add(MemoryListener *listener,
|
||||
@@ -1040,30 +778,12 @@ static void kvm_log_sync(MemoryListener *listener,
|
||||
KVMMemoryListener *kml = container_of(listener, KVMMemoryListener, listener);
|
||||
int r;
|
||||
|
||||
kvm_slots_lock(kml);
|
||||
r = kvm_physical_sync_dirty_bitmap(kml, section);
|
||||
kvm_slots_unlock(kml);
|
||||
if (r < 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_log_clear(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
KVMMemoryListener *kml = container_of(listener, KVMMemoryListener, listener);
|
||||
int r;
|
||||
|
||||
r = kvm_physical_log_clear(kml, section);
|
||||
if (r < 0) {
|
||||
error_report_once("%s: kvm log clear failed: mr=%s "
|
||||
"offset=%"HWADDR_PRIx" size=%"PRIx64, __func__,
|
||||
section->mr->name, section->offset_within_region,
|
||||
int128_get64(section->size));
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_mem_ioeventfd_add(MemoryListener *listener,
|
||||
MemoryRegionSection *section,
|
||||
bool match_data, uint64_t data,
|
||||
@@ -1076,8 +796,8 @@ static void kvm_mem_ioeventfd_add(MemoryListener *listener,
|
||||
data, true, int128_get64(section->size),
|
||||
match_data);
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "%s: error adding ioeventfd: %s (%d)\n",
|
||||
__func__, strerror(-r), -r);
|
||||
fprintf(stderr, "%s: error adding ioeventfd: %s\n",
|
||||
__func__, strerror(-r));
|
||||
abort();
|
||||
}
|
||||
}
|
||||
@@ -1094,8 +814,6 @@ static void kvm_mem_ioeventfd_del(MemoryListener *listener,
|
||||
data, false, int128_get64(section->size),
|
||||
match_data);
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "%s: error deleting ioeventfd: %s (%d)\n",
|
||||
__func__, strerror(-r), -r);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
@@ -1112,8 +830,8 @@ static void kvm_io_ioeventfd_add(MemoryListener *listener,
|
||||
data, true, int128_get64(section->size),
|
||||
match_data);
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "%s: error adding ioeventfd: %s (%d)\n",
|
||||
__func__, strerror(-r), -r);
|
||||
fprintf(stderr, "%s: error adding ioeventfd: %s\n",
|
||||
__func__, strerror(-r));
|
||||
abort();
|
||||
}
|
||||
}
|
||||
@@ -1131,8 +849,6 @@ static void kvm_io_ioeventfd_del(MemoryListener *listener,
|
||||
data, false, int128_get64(section->size),
|
||||
match_data);
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "%s: error deleting ioeventfd: %s (%d)\n",
|
||||
__func__, strerror(-r), -r);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
@@ -1142,7 +858,6 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml,
|
||||
{
|
||||
int i;
|
||||
|
||||
qemu_mutex_init(&kml->slots_lock);
|
||||
kml->slots = g_malloc0(s->nr_slots * sizeof(KVMSlot));
|
||||
kml->as_id = as_id;
|
||||
|
||||
@@ -1155,7 +870,6 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml,
|
||||
kml->listener.log_start = kvm_log_start;
|
||||
kml->listener.log_stop = kvm_log_stop;
|
||||
kml->listener.log_sync = kvm_log_sync;
|
||||
kml->listener.log_clear = kvm_log_clear;
|
||||
kml->listener.priority = 10;
|
||||
|
||||
memory_listener_register(&kml->listener, as);
|
||||
@@ -1751,8 +1465,8 @@ static int kvm_init(MachineState *ms)
|
||||
const char *name;
|
||||
int num;
|
||||
} num_cpus[] = {
|
||||
{ "SMP", ms->smp.cpus },
|
||||
{ "hotpluggable", ms->smp.max_cpus },
|
||||
{ "SMP", smp_cpus },
|
||||
{ "hotpluggable", max_cpus },
|
||||
{ NULL, }
|
||||
}, *nc = num_cpus;
|
||||
int soft_vcpus_limit, hard_vcpus_limit;
|
||||
@@ -1811,7 +1525,7 @@ static int kvm_init(MachineState *ms)
|
||||
|
||||
kvm_type = qemu_opt_get(qemu_get_machine_opts(), "kvm-type");
|
||||
if (mc->kvm_type) {
|
||||
type = mc->kvm_type(ms, kvm_type);
|
||||
type = mc->kvm_type(kvm_type);
|
||||
} else if (kvm_type) {
|
||||
ret = -EINVAL;
|
||||
fprintf(stderr, "Invalid argument kvm-type=%s\n", kvm_type);
|
||||
@@ -1877,19 +1591,6 @@ static int kvm_init(MachineState *ms)
|
||||
}
|
||||
|
||||
s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO);
|
||||
s->coalesced_pio = s->coalesced_mmio &&
|
||||
kvm_check_extension(s, KVM_CAP_COALESCED_PIO);
|
||||
|
||||
s->manual_dirty_log_protect =
|
||||
kvm_check_extension(s, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2);
|
||||
if (s->manual_dirty_log_protect) {
|
||||
ret = kvm_vm_enable_cap(s, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2, 0, 1);
|
||||
if (ret) {
|
||||
warn_report("Trying to enable KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 "
|
||||
"but failed. Falling back to the legacy mode. ");
|
||||
s->manual_dirty_log_protect = false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef KVM_CAP_VCPU_EVENTS
|
||||
s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS);
|
||||
@@ -1902,8 +1603,6 @@ static int kvm_init(MachineState *ms)
|
||||
s->debugregs = kvm_check_extension(s, KVM_CAP_DEBUGREGS);
|
||||
#endif
|
||||
|
||||
s->max_nested_state_len = kvm_check_extension(s, KVM_CAP_NESTED_STATE);
|
||||
|
||||
#ifdef KVM_CAP_IRQ_ROUTING
|
||||
kvm_direct_msi_allowed = (kvm_check_extension(s, KVM_CAP_SIGNAL_MSI) > 0);
|
||||
#endif
|
||||
@@ -1915,8 +1614,10 @@ static int kvm_init(MachineState *ms)
|
||||
s->irq_set_ioctl = KVM_IRQ_LINE_STATUS;
|
||||
}
|
||||
|
||||
#ifdef KVM_CAP_READONLY_MEM
|
||||
kvm_readonly_mem_allowed =
|
||||
(kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0);
|
||||
#endif
|
||||
|
||||
kvm_eventfds_allowed =
|
||||
(kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0);
|
||||
@@ -1935,20 +1636,6 @@ static int kvm_init(MachineState *ms)
|
||||
|
||||
kvm_state = s;
|
||||
|
||||
/*
|
||||
* if memory encryption object is specified then initialize the memory
|
||||
* encryption context.
|
||||
*/
|
||||
if (ms->memory_encryption) {
|
||||
kvm_state->memcrypt_handle = sev_guest_init(ms->memory_encryption);
|
||||
if (!kvm_state->memcrypt_handle) {
|
||||
ret = -1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
kvm_state->memcrypt_encrypt_data = sev_encrypt_data;
|
||||
}
|
||||
|
||||
ret = kvm_arch_init(ms, s);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
@@ -1962,22 +1649,17 @@ static int kvm_init(MachineState *ms)
|
||||
s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add;
|
||||
s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del;
|
||||
}
|
||||
s->memory_listener.listener.coalesced_io_add = kvm_coalesce_mmio_region;
|
||||
s->memory_listener.listener.coalesced_io_del = kvm_uncoalesce_mmio_region;
|
||||
s->memory_listener.listener.coalesced_mmio_add = kvm_coalesce_mmio_region;
|
||||
s->memory_listener.listener.coalesced_mmio_del = kvm_uncoalesce_mmio_region;
|
||||
|
||||
kvm_memory_listener_register(s, &s->memory_listener,
|
||||
&address_space_memory, 0);
|
||||
memory_listener_register(&kvm_io_listener,
|
||||
&address_space_io);
|
||||
memory_listener_register(&kvm_coalesced_pio_listener,
|
||||
&address_space_io);
|
||||
|
||||
s->many_ioeventfds = kvm_check_many_ioeventfds();
|
||||
|
||||
s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU);
|
||||
if (!s->sync_mmu) {
|
||||
qemu_balloon_inhibit(true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -2029,7 +1711,7 @@ static int kvm_handle_internal_error(CPUState *cpu, struct kvm_run *run)
|
||||
if (run->internal.suberror == KVM_INTERNAL_ERROR_EMULATION) {
|
||||
fprintf(stderr, "emulation failure\n");
|
||||
if (!kvm_arch_stop_on_emulation_error(cpu)) {
|
||||
cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
|
||||
cpu_dump_state(cpu, stderr, fprintf, CPU_DUMP_CODE);
|
||||
return EXCP_INTERRUPT;
|
||||
}
|
||||
}
|
||||
@@ -2056,13 +1738,7 @@ void kvm_flush_coalesced_mmio_buffer(void)
|
||||
|
||||
ent = &ring->coalesced_mmio[ring->first];
|
||||
|
||||
if (ent->pio == 1) {
|
||||
address_space_rw(&address_space_io, ent->phys_addr,
|
||||
MEMTXATTRS_UNSPECIFIED, ent->data,
|
||||
ent->len, true);
|
||||
} else {
|
||||
cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len);
|
||||
}
|
||||
cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len);
|
||||
smp_wmb();
|
||||
ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX;
|
||||
}
|
||||
@@ -2320,7 +1996,7 @@ int kvm_cpu_exec(CPUState *cpu)
|
||||
qemu_mutex_lock_iothread();
|
||||
|
||||
if (ret < 0) {
|
||||
cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
|
||||
cpu_dump_state(cpu, stderr, fprintf, CPU_DUMP_CODE);
|
||||
vm_stop(RUN_STATE_INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
@@ -2471,11 +2147,6 @@ int kvm_has_debugregs(void)
|
||||
return kvm_state->debugregs;
|
||||
}
|
||||
|
||||
int kvm_max_nested_state_length(void)
|
||||
{
|
||||
return kvm_state->max_nested_state_len;
|
||||
}
|
||||
|
||||
int kvm_has_many_ioeventfds(void)
|
||||
{
|
||||
if (!kvm_enabled()) {
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* QEMU SEV stub
|
||||
*
|
||||
* Copyright Advanced Micro Devices 2018
|
||||
*
|
||||
* Authors:
|
||||
* Brijesh Singh <brijesh.singh@amd.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/sev.h"
|
||||
|
||||
int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
void *sev_guest_init(const char *id)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
# Trace events for debugging and performance instrumentation
|
||||
|
||||
# kvm-all.c
|
||||
kvm_ioctl(int type, void *arg) "type 0x%x, arg %p"
|
||||
@@ -12,8 +12,5 @@ kvm_irqchip_commit_routes(void) ""
|
||||
kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d"
|
||||
kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d"
|
||||
kvm_irqchip_release_virq(int virq) "virq %d"
|
||||
kvm_set_ioeventfd_mmio(int fd, uint64_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%" PRIx64 " val=0x%x assign: %d size: %d match: %d"
|
||||
kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%x val=0x%x assign: %d size: %d match: %d"
|
||||
kvm_set_user_memory(uint32_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d"
|
||||
kvm_clear_dirty_log(uint32_t slot, uint64_t start, uint32_t size) "slot#%"PRId32" start 0x%"PRIx64" size 0x%"PRIx32
|
||||
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* QTest accelerator code
|
||||
*
|
||||
* Copyright IBM, Corp. 2011
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "sysemu/accel.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "sysemu/cpus.h"
|
||||
|
||||
static int qtest_init_accel(MachineState *ms)
|
||||
{
|
||||
QemuOpts *opts = qemu_opts_create(qemu_find_opts("icount"), NULL, 0,
|
||||
&error_abort);
|
||||
qemu_opt_set(opts, "shift", "0", &error_abort);
|
||||
configure_icount(opts, &error_abort);
|
||||
qemu_opts_del(opts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qtest_accel_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
AccelClass *ac = ACCEL_CLASS(oc);
|
||||
ac->name = "QTest";
|
||||
ac->init_machine = qtest_init_accel;
|
||||
ac->allowed = &qtest_allowed;
|
||||
}
|
||||
|
||||
#define TYPE_QTEST_ACCEL ACCEL_CLASS_NAME("qtest")
|
||||
|
||||
static const TypeInfo qtest_accel_type = {
|
||||
.name = TYPE_QTEST_ACCEL,
|
||||
.parent = TYPE_ACCEL,
|
||||
.class_init = qtest_accel_class_init,
|
||||
};
|
||||
|
||||
static void qtest_type_init(void)
|
||||
{
|
||||
type_register_static(&qtest_accel_type);
|
||||
}
|
||||
|
||||
type_init(qtest_type_init);
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "cpu.h"
|
||||
#include "sysemu/hax.h"
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "cpu.h"
|
||||
#include "sysemu/hvf.h"
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "cpu.h"
|
||||
#include "sysemu/kvm.h"
|
||||
|
||||
@@ -104,16 +105,6 @@ int kvm_on_sigbus(int code, void *addr)
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool kvm_memcrypt_enabled(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev)
|
||||
{
|
||||
|
||||
@@ -21,6 +21,10 @@ void tb_flush(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
|
||||
void tb_unlock(void)
|
||||
{
|
||||
}
|
||||
|
||||
void tlb_set_dirty(CPUState *cpu, target_ulong vaddr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "cpu.h"
|
||||
#include "sysemu/whpx.h"
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -18,37 +18,26 @@
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "trace/mem.h"
|
||||
|
||||
#if DATA_SIZE == 16
|
||||
# define SUFFIX o
|
||||
# define DATA_TYPE Int128
|
||||
# define BSWAP bswap128
|
||||
# define SHIFT 4
|
||||
#elif DATA_SIZE == 8
|
||||
# define SUFFIX q
|
||||
# define DATA_TYPE uint64_t
|
||||
# define SDATA_TYPE int64_t
|
||||
# define BSWAP bswap64
|
||||
# define SHIFT 3
|
||||
#elif DATA_SIZE == 4
|
||||
# define SUFFIX l
|
||||
# define DATA_TYPE uint32_t
|
||||
# define SDATA_TYPE int32_t
|
||||
# define BSWAP bswap32
|
||||
# define SHIFT 2
|
||||
#elif DATA_SIZE == 2
|
||||
# define SUFFIX w
|
||||
# define DATA_TYPE uint16_t
|
||||
# define SDATA_TYPE int16_t
|
||||
# define BSWAP bswap16
|
||||
# define SHIFT 1
|
||||
#elif DATA_SIZE == 1
|
||||
# define SUFFIX b
|
||||
# define DATA_TYPE uint8_t
|
||||
# define SDATA_TYPE int8_t
|
||||
# define BSWAP
|
||||
# define SHIFT 0
|
||||
#else
|
||||
# error unsupported data size
|
||||
#endif
|
||||
@@ -59,37 +48,14 @@
|
||||
# define ABI_TYPE uint32_t
|
||||
#endif
|
||||
|
||||
#define ATOMIC_TRACE_RMW do { \
|
||||
uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
|
||||
\
|
||||
trace_guest_mem_before_exec(env_cpu(env), addr, info); \
|
||||
trace_guest_mem_before_exec(env_cpu(env), addr, \
|
||||
info | TRACE_MEM_ST); \
|
||||
} while (0)
|
||||
|
||||
#define ATOMIC_TRACE_LD do { \
|
||||
uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
|
||||
\
|
||||
trace_guest_mem_before_exec(env_cpu(env), addr, info); \
|
||||
} while (0)
|
||||
|
||||
# define ATOMIC_TRACE_ST do { \
|
||||
uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true); \
|
||||
\
|
||||
trace_guest_mem_before_exec(env_cpu(env), addr, info); \
|
||||
} while (0)
|
||||
|
||||
/* Define host-endian atomic operations. Note that END is used within
|
||||
the ATOMIC_NAME macro, and redefined below. */
|
||||
#if DATA_SIZE == 1
|
||||
# define END
|
||||
# define MEND _be /* either le or be would be fine */
|
||||
#elif defined(HOST_WORDS_BIGENDIAN)
|
||||
# define END _be
|
||||
# define MEND _be
|
||||
#else
|
||||
# define END _le
|
||||
# define MEND _le
|
||||
#endif
|
||||
|
||||
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
@@ -97,27 +63,17 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE ret;
|
||||
|
||||
ATOMIC_TRACE_RMW;
|
||||
#if DATA_SIZE == 16
|
||||
ret = atomic16_cmpxchg(haddr, cmpv, newv);
|
||||
#else
|
||||
ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv);
|
||||
#endif
|
||||
DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if DATA_SIZE >= 16
|
||||
#if HAVE_ATOMIC128
|
||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
||||
|
||||
ATOMIC_TRACE_LD;
|
||||
val = atomic16_read(haddr);
|
||||
__atomic_load(haddr, &val, __ATOMIC_RELAXED);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
return val;
|
||||
}
|
||||
@@ -127,22 +83,16 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
|
||||
ATOMIC_TRACE_ST;
|
||||
atomic16_set(haddr, val);
|
||||
__atomic_store(haddr, &val, __ATOMIC_RELAXED);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE ret;
|
||||
|
||||
ATOMIC_TRACE_RMW;
|
||||
ret = atomic_xchg__nocheck(haddr, val);
|
||||
DATA_TYPE ret = atomic_xchg__nocheck(haddr, val);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
return ret;
|
||||
}
|
||||
@@ -153,10 +103,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
{ \
|
||||
ATOMIC_MMU_DECLS; \
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||
DATA_TYPE ret; \
|
||||
\
|
||||
ATOMIC_TRACE_RMW; \
|
||||
ret = atomic_##X(haddr, val); \
|
||||
DATA_TYPE ret = atomic_##X(haddr, val); \
|
||||
ATOMIC_MMU_CLEANUP; \
|
||||
return ret; \
|
||||
}
|
||||
@@ -171,48 +118,9 @@ GEN_ATOMIC_HELPER(or_fetch)
|
||||
GEN_ATOMIC_HELPER(xor_fetch)
|
||||
|
||||
#undef GEN_ATOMIC_HELPER
|
||||
|
||||
/* These helpers are, as a whole, full barriers. Within the helper,
|
||||
* the leading barrier is explicit and the trailing barrier is within
|
||||
* cmpxchg primitive.
|
||||
*
|
||||
* Trace this load + RMW loop as a single RMW op. This way, regardless
|
||||
* of CF_PARALLEL's value, we'll trace just a read and a write.
|
||||
*/
|
||||
#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ABI_TYPE xval EXTRA_ARGS) \
|
||||
{ \
|
||||
ATOMIC_MMU_DECLS; \
|
||||
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||
XDATA_TYPE cmp, old, new, val = xval; \
|
||||
\
|
||||
ATOMIC_TRACE_RMW; \
|
||||
smp_mb(); \
|
||||
cmp = atomic_read__nocheck(haddr); \
|
||||
do { \
|
||||
old = cmp; new = FN(old, val); \
|
||||
cmp = atomic_cmpxchg__nocheck(haddr, old, new); \
|
||||
} while (cmp != old); \
|
||||
ATOMIC_MMU_CLEANUP; \
|
||||
return RET; \
|
||||
}
|
||||
|
||||
GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old)
|
||||
GEN_ATOMIC_HELPER_FN(fetch_umin, MIN, DATA_TYPE, old)
|
||||
GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old)
|
||||
GEN_ATOMIC_HELPER_FN(fetch_umax, MAX, DATA_TYPE, old)
|
||||
|
||||
GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new)
|
||||
GEN_ATOMIC_HELPER_FN(umin_fetch, MIN, DATA_TYPE, new)
|
||||
GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
|
||||
GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
|
||||
|
||||
#undef GEN_ATOMIC_HELPER_FN
|
||||
#endif /* DATA SIZE >= 16 */
|
||||
|
||||
#undef END
|
||||
#undef MEND
|
||||
|
||||
#if DATA_SIZE > 1
|
||||
|
||||
@@ -220,10 +128,8 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
|
||||
within the ATOMIC_NAME macro. */
|
||||
#ifdef HOST_WORDS_BIGENDIAN
|
||||
# define END _le
|
||||
# define MEND _le
|
||||
#else
|
||||
# define END _be
|
||||
# define MEND _be
|
||||
#endif
|
||||
|
||||
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
@@ -231,27 +137,17 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE ret;
|
||||
|
||||
ATOMIC_TRACE_RMW;
|
||||
#if DATA_SIZE == 16
|
||||
ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv));
|
||||
#else
|
||||
ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
|
||||
#endif
|
||||
DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
return BSWAP(ret);
|
||||
}
|
||||
|
||||
#if DATA_SIZE >= 16
|
||||
#if HAVE_ATOMIC128
|
||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
||||
|
||||
ATOMIC_TRACE_LD;
|
||||
val = atomic16_read(haddr);
|
||||
__atomic_load(haddr, &val, __ATOMIC_RELAXED);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
return BSWAP(val);
|
||||
}
|
||||
@@ -261,23 +157,17 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
|
||||
ATOMIC_TRACE_ST;
|
||||
val = BSWAP(val);
|
||||
atomic16_set(haddr, val);
|
||||
__atomic_store(haddr, &val, __ATOMIC_RELAXED);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
ABI_TYPE ret;
|
||||
|
||||
ATOMIC_TRACE_RMW;
|
||||
ret = atomic_xchg__nocheck(haddr, BSWAP(val));
|
||||
ABI_TYPE ret = atomic_xchg__nocheck(haddr, BSWAP(val));
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
return BSWAP(ret);
|
||||
}
|
||||
@@ -288,10 +178,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
{ \
|
||||
ATOMIC_MMU_DECLS; \
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||
DATA_TYPE ret; \
|
||||
\
|
||||
ATOMIC_TRACE_RMW; \
|
||||
ret = atomic_##X(haddr, BSWAP(val)); \
|
||||
DATA_TYPE ret = atomic_##X(haddr, BSWAP(val)); \
|
||||
ATOMIC_MMU_CLEANUP; \
|
||||
return BSWAP(ret); \
|
||||
}
|
||||
@@ -305,64 +192,54 @@ GEN_ATOMIC_HELPER(xor_fetch)
|
||||
|
||||
#undef GEN_ATOMIC_HELPER
|
||||
|
||||
/* These helpers are, as a whole, full barriers. Within the helper,
|
||||
* the leading barrier is explicit and the trailing barrier is within
|
||||
* cmpxchg primitive.
|
||||
*
|
||||
* Trace this load + RMW loop as a single RMW op. This way, regardless
|
||||
* of CF_PARALLEL's value, we'll trace just a read and a write.
|
||||
*/
|
||||
#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ABI_TYPE xval EXTRA_ARGS) \
|
||||
{ \
|
||||
ATOMIC_MMU_DECLS; \
|
||||
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||
XDATA_TYPE ldo, ldn, old, new, val = xval; \
|
||||
\
|
||||
ATOMIC_TRACE_RMW; \
|
||||
smp_mb(); \
|
||||
ldn = atomic_read__nocheck(haddr); \
|
||||
do { \
|
||||
ldo = ldn; old = BSWAP(ldo); new = FN(old, val); \
|
||||
ldn = atomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new)); \
|
||||
} while (ldo != ldn); \
|
||||
ATOMIC_MMU_CLEANUP; \
|
||||
return RET; \
|
||||
}
|
||||
|
||||
GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old)
|
||||
GEN_ATOMIC_HELPER_FN(fetch_umin, MIN, DATA_TYPE, old)
|
||||
GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old)
|
||||
GEN_ATOMIC_HELPER_FN(fetch_umax, MAX, DATA_TYPE, old)
|
||||
|
||||
GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new)
|
||||
GEN_ATOMIC_HELPER_FN(umin_fetch, MIN, DATA_TYPE, new)
|
||||
GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
|
||||
GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
|
||||
|
||||
/* Note that for addition, we need to use a separate cmpxchg loop instead
|
||||
of bswaps for the reverse-host-endian helpers. */
|
||||
#define ADD(X, Y) (X + Y)
|
||||
GEN_ATOMIC_HELPER_FN(fetch_add, ADD, DATA_TYPE, old)
|
||||
GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
|
||||
#undef ADD
|
||||
ABI_TYPE ATOMIC_NAME(fetch_add)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE ldo, ldn, ret, sto;
|
||||
|
||||
#undef GEN_ATOMIC_HELPER_FN
|
||||
ldo = atomic_read__nocheck(haddr);
|
||||
while (1) {
|
||||
ret = BSWAP(ldo);
|
||||
sto = BSWAP(ret + val);
|
||||
ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto);
|
||||
if (ldn == ldo) {
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
return ret;
|
||||
}
|
||||
ldo = ldn;
|
||||
}
|
||||
}
|
||||
|
||||
ABI_TYPE ATOMIC_NAME(add_fetch)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE ldo, ldn, ret, sto;
|
||||
|
||||
ldo = atomic_read__nocheck(haddr);
|
||||
while (1) {
|
||||
ret = BSWAP(ldo) + val;
|
||||
sto = BSWAP(ret);
|
||||
ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto);
|
||||
if (ldn == ldo) {
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
return ret;
|
||||
}
|
||||
ldo = ldn;
|
||||
}
|
||||
}
|
||||
#endif /* DATA_SIZE >= 16 */
|
||||
|
||||
#undef END
|
||||
#undef MEND
|
||||
#endif /* DATA_SIZE > 1 */
|
||||
|
||||
#undef ATOMIC_TRACE_ST
|
||||
#undef ATOMIC_TRACE_LD
|
||||
#undef ATOMIC_TRACE_RMW
|
||||
|
||||
#undef BSWAP
|
||||
#undef ABI_TYPE
|
||||
#undef DATA_TYPE
|
||||
#undef SDATA_TYPE
|
||||
#undef SUFFIX
|
||||
#undef DATA_SIZE
|
||||
#undef SHIFT
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/tcg.h"
|
||||
#include "exec/exec-all.h"
|
||||
|
||||
bool tcg_allowed;
|
||||
@@ -28,8 +27,10 @@ bool tcg_allowed;
|
||||
/* exit the current TB, but without causing any exception to be raised */
|
||||
void cpu_loop_exit_noexc(CPUState *cpu)
|
||||
{
|
||||
/* XXX: restore cpu registers saved in host registers */
|
||||
|
||||
cpu->exception_index = -1;
|
||||
cpu_loop_exit(cpu);
|
||||
siglongjmp(cpu->jmp_env, 1);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
@@ -64,17 +65,15 @@ void cpu_reloading_memory_map(void)
|
||||
|
||||
void cpu_loop_exit(CPUState *cpu)
|
||||
{
|
||||
/* Undo the setting in cpu_tb_exec. */
|
||||
cpu->can_do_io = 1;
|
||||
siglongjmp(cpu->jmp_env, 1);
|
||||
}
|
||||
|
||||
void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc)
|
||||
{
|
||||
if (pc) {
|
||||
cpu_restore_state(cpu, pc, true);
|
||||
cpu_restore_state(cpu, pc);
|
||||
}
|
||||
cpu_loop_exit(cpu);
|
||||
siglongjmp(cpu->jmp_env, 1);
|
||||
}
|
||||
|
||||
void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -16,9 +16,7 @@
|
||||
* 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 "qemu-common.h"
|
||||
#include "cpu.h"
|
||||
#include "trace.h"
|
||||
#include "disas/disas.h"
|
||||
@@ -27,6 +25,7 @@
|
||||
#include "qemu/atomic.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/rcu.h"
|
||||
#include "exec/tb-hash.h"
|
||||
#include "exec/tb-lookup.h"
|
||||
@@ -56,7 +55,7 @@ typedef struct SyncClocks {
|
||||
#define MAX_DELAY_PRINT_RATE 2000000000LL
|
||||
#define MAX_NB_PRINTS 100
|
||||
|
||||
static void align_clocks(SyncClocks *sc, CPUState *cpu)
|
||||
static void align_clocks(SyncClocks *sc, const CPUState *cpu)
|
||||
{
|
||||
int64_t cpu_icount;
|
||||
|
||||
@@ -64,7 +63,7 @@ static void align_clocks(SyncClocks *sc, CPUState *cpu)
|
||||
return;
|
||||
}
|
||||
|
||||
cpu_icount = cpu->icount_extra + cpu_neg(cpu)->icount_decr.u16.low;
|
||||
cpu_icount = cpu->icount_extra + cpu->icount_decr.u16.low;
|
||||
sc->diff_clk += cpu_icount_to_ns(sc->last_cpu_icount - cpu_icount);
|
||||
sc->last_cpu_icount = cpu_icount;
|
||||
|
||||
@@ -107,15 +106,15 @@ static void print_delay(const SyncClocks *sc)
|
||||
}
|
||||
}
|
||||
|
||||
static void init_delay_params(SyncClocks *sc, CPUState *cpu)
|
||||
static void init_delay_params(SyncClocks *sc,
|
||||
const CPUState *cpu)
|
||||
{
|
||||
if (!icount_align_option) {
|
||||
return;
|
||||
}
|
||||
sc->realtime_clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT);
|
||||
sc->diff_clk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - sc->realtime_clock;
|
||||
sc->last_cpu_icount
|
||||
= cpu->icount_extra + cpu_neg(cpu)->icount_decr.u16.low;
|
||||
sc->last_cpu_icount = cpu->icount_extra + cpu->icount_decr.u16.low;
|
||||
if (sc->diff_clk < max_delay) {
|
||||
max_delay = sc->diff_clk;
|
||||
}
|
||||
@@ -157,14 +156,11 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb)
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_CPU)
|
||||
&& qemu_log_in_addr_range(itb->pc)) {
|
||||
qemu_log_lock();
|
||||
int flags = 0;
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) {
|
||||
flags |= CPU_DUMP_FPU;
|
||||
}
|
||||
#if defined(TARGET_I386)
|
||||
flags |= CPU_DUMP_CCOP;
|
||||
log_cpu_state(cpu, CPU_DUMP_CCOP);
|
||||
#else
|
||||
log_cpu_state(cpu, 0);
|
||||
#endif
|
||||
log_cpu_state(cpu, flags);
|
||||
qemu_log_unlock();
|
||||
}
|
||||
#endif /* DEBUG_DISAS */
|
||||
@@ -214,20 +210,20 @@ static void cpu_exec_nocache(CPUState *cpu, int max_cycles,
|
||||
We only end up here when an existing TB is too long. */
|
||||
cflags |= MIN(max_cycles, CF_COUNT_MASK);
|
||||
|
||||
mmap_lock();
|
||||
tb_lock();
|
||||
tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base,
|
||||
orig_tb->flags, cflags);
|
||||
tb->orig_tb = orig_tb;
|
||||
mmap_unlock();
|
||||
tb_unlock();
|
||||
|
||||
/* execute the generated code */
|
||||
trace_exec_tb_nocache(tb, tb->pc);
|
||||
cpu_tb_exec(cpu, tb);
|
||||
|
||||
mmap_lock();
|
||||
tb_lock();
|
||||
tb_phys_invalidate(tb, -1);
|
||||
mmap_unlock();
|
||||
tcg_tb_remove(tb);
|
||||
tb_remove(tb);
|
||||
tb_unlock();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -246,7 +242,12 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
||||
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
|
||||
if (tb == NULL) {
|
||||
mmap_lock();
|
||||
tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
|
||||
tb_lock();
|
||||
tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask);
|
||||
if (likely(tb == NULL)) {
|
||||
tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
|
||||
}
|
||||
tb_unlock();
|
||||
mmap_unlock();
|
||||
}
|
||||
|
||||
@@ -261,17 +262,15 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
||||
cpu_tb_exec(cpu, tb);
|
||||
cc->cpu_exec_exit(cpu);
|
||||
} else {
|
||||
/*
|
||||
/* We may have exited due to another problem here, so we need
|
||||
* to reset any tb_locks we may have taken but didn't release.
|
||||
* The mmap_lock is dropped by tb_gen_code if it runs out of
|
||||
* memory.
|
||||
*/
|
||||
#ifndef CONFIG_SOFTMMU
|
||||
tcg_debug_assert(!have_mmap_lock());
|
||||
#endif
|
||||
if (qemu_mutex_iothread_locked()) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
assert_no_pages_locked();
|
||||
tb_lock_reset();
|
||||
}
|
||||
|
||||
if (in_exclusive_region) {
|
||||
@@ -294,7 +293,7 @@ struct tb_desc {
|
||||
uint32_t trace_vcpu_dstate;
|
||||
};
|
||||
|
||||
static bool tb_lookup_cmp(const void *p, const void *d)
|
||||
static bool tb_cmp(const void *p, const void *d)
|
||||
{
|
||||
const TranslationBlock *tb = p;
|
||||
const struct tb_desc *desc = d;
|
||||
@@ -337,12 +336,9 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
|
||||
desc.trace_vcpu_dstate = *cpu->trace_dstate;
|
||||
desc.pc = pc;
|
||||
phys_pc = get_page_addr_code(desc.env, pc);
|
||||
if (phys_pc == -1) {
|
||||
return NULL;
|
||||
}
|
||||
desc.phys_page1 = phys_pc & TARGET_PAGE_MASK;
|
||||
h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate);
|
||||
return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp);
|
||||
return qht_lookup(&tb_ctx.htable, tb_cmp, &desc, h);
|
||||
}
|
||||
|
||||
void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr)
|
||||
@@ -356,43 +352,28 @@ void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr)
|
||||
}
|
||||
}
|
||||
|
||||
/* Called with tb_lock held. */
|
||||
static inline void tb_add_jump(TranslationBlock *tb, int n,
|
||||
TranslationBlock *tb_next)
|
||||
{
|
||||
uintptr_t old;
|
||||
|
||||
assert(n < ARRAY_SIZE(tb->jmp_list_next));
|
||||
qemu_spin_lock(&tb_next->jmp_lock);
|
||||
|
||||
/* make sure the destination TB is valid */
|
||||
if (tb_next->cflags & CF_INVALID) {
|
||||
goto out_unlock_next;
|
||||
if (tb->jmp_list_next[n]) {
|
||||
/* Another thread has already done this while we were
|
||||
* outside of the lock; nothing to do in this case */
|
||||
return;
|
||||
}
|
||||
/* Atomically claim the jump destination slot only if it was NULL */
|
||||
old = atomic_cmpxchg(&tb->jmp_dest[n], (uintptr_t)NULL, (uintptr_t)tb_next);
|
||||
if (old) {
|
||||
goto out_unlock_next;
|
||||
}
|
||||
|
||||
/* patch the native jump address */
|
||||
tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr);
|
||||
|
||||
/* add in TB jmp list */
|
||||
tb->jmp_list_next[n] = tb_next->jmp_list_head;
|
||||
tb_next->jmp_list_head = (uintptr_t)tb | n;
|
||||
|
||||
qemu_spin_unlock(&tb_next->jmp_lock);
|
||||
|
||||
qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc,
|
||||
"Linking TBs %p [" TARGET_FMT_lx
|
||||
"] index %d -> %p [" TARGET_FMT_lx "]\n",
|
||||
tb->tc.ptr, tb->pc, n,
|
||||
tb_next->tc.ptr, tb_next->pc);
|
||||
return;
|
||||
|
||||
out_unlock_next:
|
||||
qemu_spin_unlock(&tb_next->jmp_lock);
|
||||
return;
|
||||
/* patch the native jump address */
|
||||
tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr);
|
||||
|
||||
/* add in TB jmp circular list */
|
||||
tb->jmp_list_next[n] = tb_next->jmp_list_first;
|
||||
tb_next->jmp_list_first = (uintptr_t)tb | n;
|
||||
}
|
||||
|
||||
static inline TranslationBlock *tb_find(CPUState *cpu,
|
||||
@@ -402,11 +383,27 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
|
||||
TranslationBlock *tb;
|
||||
target_ulong cs_base, pc;
|
||||
uint32_t flags;
|
||||
bool acquired_tb_lock = false;
|
||||
|
||||
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
|
||||
if (tb == NULL) {
|
||||
/* mmap_lock is needed by tb_gen_code, and mmap_lock must be
|
||||
* taken outside tb_lock. As system emulation is currently
|
||||
* single threaded the locks are NOPs.
|
||||
*/
|
||||
mmap_lock();
|
||||
tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask);
|
||||
tb_lock();
|
||||
acquired_tb_lock = true;
|
||||
|
||||
/* There's a chance that our desired tb has been translated while
|
||||
* taking the locks so we check again inside the lock.
|
||||
*/
|
||||
tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask);
|
||||
if (likely(tb == NULL)) {
|
||||
/* if no translated code available, then translate it now */
|
||||
tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask);
|
||||
}
|
||||
|
||||
mmap_unlock();
|
||||
/* We add the TB in the virtual pc hash table for the fast lookup */
|
||||
atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb);
|
||||
@@ -421,8 +418,17 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
|
||||
}
|
||||
#endif
|
||||
/* See if we can patch the calling TB. */
|
||||
if (last_tb) {
|
||||
tb_add_jump(last_tb, tb_exit, tb);
|
||||
if (last_tb && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) {
|
||||
if (!acquired_tb_lock) {
|
||||
tb_lock();
|
||||
acquired_tb_lock = true;
|
||||
}
|
||||
if (!(tb->cflags & CF_INVALID)) {
|
||||
tb_add_jump(last_tb, tb_exit, tb);
|
||||
}
|
||||
}
|
||||
if (acquired_tb_lock) {
|
||||
tb_unlock();
|
||||
}
|
||||
return tb;
|
||||
}
|
||||
@@ -469,7 +475,7 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
|
||||
if (cpu->exception_index < 0) {
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (replay_has_exception()
|
||||
&& cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra == 0) {
|
||||
&& cpu->icount_decr.u16.low + cpu->icount_extra == 0) {
|
||||
/* try to cause an exception pending in the log */
|
||||
cpu_exec_nocache(cpu, 1, tb_find(cpu, NULL, 0, curr_cflags()), true);
|
||||
}
|
||||
@@ -527,7 +533,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
* Ensure zeroing happens before reading cpu->exit_request or
|
||||
* cpu->interrupt_request (see also smp_wmb in cpu_exit())
|
||||
*/
|
||||
atomic_mb_set(&cpu_neg(cpu)->icount_decr.u16.high, 0);
|
||||
atomic_mb_set(&cpu->icount_decr.u16.high, 0);
|
||||
|
||||
if (unlikely(atomic_read(&cpu->interrupt_request))) {
|
||||
int interrupt_request;
|
||||
@@ -579,7 +585,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
else {
|
||||
if (cc->cpu_exec_interrupt(cpu, interrupt_request)) {
|
||||
replay_interrupt();
|
||||
cpu->exception_index = -1;
|
||||
*last_tb = NULL;
|
||||
}
|
||||
/* The target hook may have updated the 'cpu->interrupt_request';
|
||||
@@ -598,13 +603,10 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
}
|
||||
|
||||
/* Finally, check if we need to exit to the main loop. */
|
||||
if (unlikely(atomic_read(&cpu->exit_request))
|
||||
|| (use_icount
|
||||
&& cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra == 0)) {
|
||||
if (unlikely(atomic_read(&cpu->exit_request)
|
||||
|| (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) {
|
||||
atomic_set(&cpu->exit_request, 0);
|
||||
if (cpu->exception_index == -1) {
|
||||
cpu->exception_index = EXCP_INTERRUPT;
|
||||
}
|
||||
cpu->exception_index = EXCP_INTERRUPT;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -627,7 +629,7 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
|
||||
}
|
||||
|
||||
*last_tb = NULL;
|
||||
insns_left = atomic_read(&cpu_neg(cpu)->icount_decr.u32);
|
||||
insns_left = atomic_read(&cpu->icount_decr.u32);
|
||||
if (insns_left < 0) {
|
||||
/* Something asked us to stop executing chained TBs; just
|
||||
* continue round the main loop. Whatever requested the exit
|
||||
@@ -646,7 +648,7 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
|
||||
cpu_update_icount(cpu);
|
||||
/* Refill decrementer and continue execution. */
|
||||
insns_left = MIN(0xffff, cpu->icount_budget);
|
||||
cpu_neg(cpu)->icount_decr.u16.low = insns_left;
|
||||
cpu->icount_decr.u16.low = insns_left;
|
||||
cpu->icount_extra = cpu->icount_budget - insns_left;
|
||||
if (!cpu->icount_extra) {
|
||||
/* Execute any remaining instructions, then let the main loop
|
||||
@@ -699,13 +701,11 @@ int cpu_exec(CPUState *cpu)
|
||||
g_assert(cpu == current_cpu);
|
||||
g_assert(cc == CPU_GET_CLASS(cpu));
|
||||
#endif /* buggy compiler */
|
||||
#ifndef CONFIG_SOFTMMU
|
||||
tcg_debug_assert(!have_mmap_lock());
|
||||
#endif
|
||||
cpu->can_do_io = 1;
|
||||
tb_lock_reset();
|
||||
if (qemu_mutex_iothread_locked()) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
assert_no_pages_locked();
|
||||
}
|
||||
|
||||
/* if an exception is pending, we execute it here */
|
||||
|
||||
1590
accel/tcg/cputlb.c
1590
accel/tcg/cputlb.c
File diff suppressed because it is too large
Load Diff
435
accel/tcg/softmmu_template.h
Normal file
435
accel/tcg/softmmu_template.h
Normal file
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
* Software MMU support
|
||||
*
|
||||
* Generate helpers used by TCG for qemu_ld/st ops and code load
|
||||
* functions.
|
||||
*
|
||||
* Included from target op helpers and exec.c.
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
#if DATA_SIZE == 8
|
||||
#define SUFFIX q
|
||||
#define LSUFFIX q
|
||||
#define SDATA_TYPE int64_t
|
||||
#define DATA_TYPE uint64_t
|
||||
#elif DATA_SIZE == 4
|
||||
#define SUFFIX l
|
||||
#define LSUFFIX l
|
||||
#define SDATA_TYPE int32_t
|
||||
#define DATA_TYPE uint32_t
|
||||
#elif DATA_SIZE == 2
|
||||
#define SUFFIX w
|
||||
#define LSUFFIX uw
|
||||
#define SDATA_TYPE int16_t
|
||||
#define DATA_TYPE uint16_t
|
||||
#elif DATA_SIZE == 1
|
||||
#define SUFFIX b
|
||||
#define LSUFFIX ub
|
||||
#define SDATA_TYPE int8_t
|
||||
#define DATA_TYPE uint8_t
|
||||
#else
|
||||
#error unsupported data size
|
||||
#endif
|
||||
|
||||
|
||||
/* For the benefit of TCG generated code, we want to avoid the complication
|
||||
of ABI-specific return type promotion and always return a value extended
|
||||
to the register size of the host. This is tcg_target_long, except in the
|
||||
case of a 32-bit host and 64-bit data, and for that we always have
|
||||
uint64_t. Don't bother with this widened value for SOFTMMU_CODE_ACCESS. */
|
||||
#if defined(SOFTMMU_CODE_ACCESS) || DATA_SIZE == 8
|
||||
# define WORD_TYPE DATA_TYPE
|
||||
# define USUFFIX SUFFIX
|
||||
#else
|
||||
# define WORD_TYPE tcg_target_ulong
|
||||
# define USUFFIX glue(u, SUFFIX)
|
||||
# define SSUFFIX glue(s, SUFFIX)
|
||||
#endif
|
||||
|
||||
#ifdef SOFTMMU_CODE_ACCESS
|
||||
#define READ_ACCESS_TYPE MMU_INST_FETCH
|
||||
#define ADDR_READ addr_code
|
||||
#else
|
||||
#define READ_ACCESS_TYPE MMU_DATA_LOAD
|
||||
#define ADDR_READ addr_read
|
||||
#endif
|
||||
|
||||
#if DATA_SIZE == 8
|
||||
# define BSWAP(X) bswap64(X)
|
||||
#elif DATA_SIZE == 4
|
||||
# define BSWAP(X) bswap32(X)
|
||||
#elif DATA_SIZE == 2
|
||||
# define BSWAP(X) bswap16(X)
|
||||
#else
|
||||
# define BSWAP(X) (X)
|
||||
#endif
|
||||
|
||||
#if DATA_SIZE == 1
|
||||
# define helper_le_ld_name glue(glue(helper_ret_ld, USUFFIX), MMUSUFFIX)
|
||||
# define helper_be_ld_name helper_le_ld_name
|
||||
# define helper_le_lds_name glue(glue(helper_ret_ld, SSUFFIX), MMUSUFFIX)
|
||||
# define helper_be_lds_name helper_le_lds_name
|
||||
# define helper_le_st_name glue(glue(helper_ret_st, SUFFIX), MMUSUFFIX)
|
||||
# define helper_be_st_name helper_le_st_name
|
||||
#else
|
||||
# define helper_le_ld_name glue(glue(helper_le_ld, USUFFIX), MMUSUFFIX)
|
||||
# define helper_be_ld_name glue(glue(helper_be_ld, USUFFIX), MMUSUFFIX)
|
||||
# define helper_le_lds_name glue(glue(helper_le_ld, SSUFFIX), MMUSUFFIX)
|
||||
# define helper_be_lds_name glue(glue(helper_be_ld, SSUFFIX), MMUSUFFIX)
|
||||
# define helper_le_st_name glue(glue(helper_le_st, SUFFIX), MMUSUFFIX)
|
||||
# define helper_be_st_name glue(glue(helper_be_st, SUFFIX), MMUSUFFIX)
|
||||
#endif
|
||||
|
||||
#ifndef SOFTMMU_CODE_ACCESS
|
||||
static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env,
|
||||
size_t mmu_idx, size_t index,
|
||||
target_ulong addr,
|
||||
uintptr_t retaddr)
|
||||
{
|
||||
CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
|
||||
return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, DATA_SIZE);
|
||||
}
|
||||
#endif
|
||||
|
||||
WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
unsigned mmu_idx = get_mmuidx(oi);
|
||||
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
|
||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||
uintptr_t haddr;
|
||||
DATA_TYPE res;
|
||||
|
||||
if (addr & ((1 << a_bits) - 1)) {
|
||||
cpu_unaligned_access(ENV_GET_CPU(env), addr, READ_ACCESS_TYPE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
|
||||
/* If the TLB entry is for a different page, reload and try again. */
|
||||
if ((addr & TARGET_PAGE_MASK)
|
||||
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
|
||||
if (!VICTIM_TLB_HIT(ADDR_READ, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
|
||||
}
|
||||
|
||||
/* Handle an IO access. */
|
||||
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
|
||||
if ((addr & (DATA_SIZE - 1)) != 0) {
|
||||
goto do_unaligned_access;
|
||||
}
|
||||
|
||||
/* ??? Note that the io helpers always read data in the target
|
||||
byte ordering. We should push the LE/BE request down into io. */
|
||||
res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr);
|
||||
res = TGT_LE(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Handle slow unaligned access (it spans two pages or IO). */
|
||||
if (DATA_SIZE > 1
|
||||
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
|
||||
>= TARGET_PAGE_SIZE)) {
|
||||
target_ulong addr1, addr2;
|
||||
DATA_TYPE res1, res2;
|
||||
unsigned shift;
|
||||
do_unaligned_access:
|
||||
addr1 = addr & ~(DATA_SIZE - 1);
|
||||
addr2 = addr1 + DATA_SIZE;
|
||||
res1 = helper_le_ld_name(env, addr1, oi, retaddr);
|
||||
res2 = helper_le_ld_name(env, addr2, oi, retaddr);
|
||||
shift = (addr & (DATA_SIZE - 1)) * 8;
|
||||
|
||||
/* Little-endian combine. */
|
||||
res = (res1 >> shift) | (res2 << ((DATA_SIZE * 8) - shift));
|
||||
return res;
|
||||
}
|
||||
|
||||
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||
#if DATA_SIZE == 1
|
||||
res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr);
|
||||
#else
|
||||
res = glue(glue(ld, LSUFFIX), _le_p)((uint8_t *)haddr);
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
#if DATA_SIZE > 1
|
||||
WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
unsigned mmu_idx = get_mmuidx(oi);
|
||||
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
|
||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||
uintptr_t haddr;
|
||||
DATA_TYPE res;
|
||||
|
||||
if (addr & ((1 << a_bits) - 1)) {
|
||||
cpu_unaligned_access(ENV_GET_CPU(env), addr, READ_ACCESS_TYPE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
|
||||
/* If the TLB entry is for a different page, reload and try again. */
|
||||
if ((addr & TARGET_PAGE_MASK)
|
||||
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
|
||||
if (!VICTIM_TLB_HIT(ADDR_READ, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
|
||||
}
|
||||
|
||||
/* Handle an IO access. */
|
||||
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
|
||||
if ((addr & (DATA_SIZE - 1)) != 0) {
|
||||
goto do_unaligned_access;
|
||||
}
|
||||
|
||||
/* ??? Note that the io helpers always read data in the target
|
||||
byte ordering. We should push the LE/BE request down into io. */
|
||||
res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr);
|
||||
res = TGT_BE(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Handle slow unaligned access (it spans two pages or IO). */
|
||||
if (DATA_SIZE > 1
|
||||
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
|
||||
>= TARGET_PAGE_SIZE)) {
|
||||
target_ulong addr1, addr2;
|
||||
DATA_TYPE res1, res2;
|
||||
unsigned shift;
|
||||
do_unaligned_access:
|
||||
addr1 = addr & ~(DATA_SIZE - 1);
|
||||
addr2 = addr1 + DATA_SIZE;
|
||||
res1 = helper_be_ld_name(env, addr1, oi, retaddr);
|
||||
res2 = helper_be_ld_name(env, addr2, oi, retaddr);
|
||||
shift = (addr & (DATA_SIZE - 1)) * 8;
|
||||
|
||||
/* Big-endian combine. */
|
||||
res = (res1 << shift) | (res2 >> ((DATA_SIZE * 8) - shift));
|
||||
return res;
|
||||
}
|
||||
|
||||
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||
res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr);
|
||||
return res;
|
||||
}
|
||||
#endif /* DATA_SIZE > 1 */
|
||||
|
||||
#ifndef SOFTMMU_CODE_ACCESS
|
||||
|
||||
/* Provide signed versions of the load routines as well. We can of course
|
||||
avoid this for 64-bit data, or for 32-bit data on 32-bit host. */
|
||||
#if DATA_SIZE * 8 < TCG_TARGET_REG_BITS
|
||||
WORD_TYPE helper_le_lds_name(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return (SDATA_TYPE)helper_le_ld_name(env, addr, oi, retaddr);
|
||||
}
|
||||
|
||||
# if DATA_SIZE > 1
|
||||
WORD_TYPE helper_be_lds_name(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return (SDATA_TYPE)helper_be_ld_name(env, addr, oi, retaddr);
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static inline void glue(io_write, SUFFIX)(CPUArchState *env,
|
||||
size_t mmu_idx, size_t index,
|
||||
DATA_TYPE val,
|
||||
target_ulong addr,
|
||||
uintptr_t retaddr)
|
||||
{
|
||||
CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
|
||||
return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr, DATA_SIZE);
|
||||
}
|
||||
|
||||
void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
unsigned mmu_idx = get_mmuidx(oi);
|
||||
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||
uintptr_t haddr;
|
||||
|
||||
if (addr & ((1 << a_bits) - 1)) {
|
||||
cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
|
||||
/* If the TLB entry is for a different page, reload and try again. */
|
||||
if ((addr & TARGET_PAGE_MASK)
|
||||
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
|
||||
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK;
|
||||
}
|
||||
|
||||
/* Handle an IO access. */
|
||||
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
|
||||
if ((addr & (DATA_SIZE - 1)) != 0) {
|
||||
goto do_unaligned_access;
|
||||
}
|
||||
|
||||
/* ??? Note that the io helpers always read data in the target
|
||||
byte ordering. We should push the LE/BE request down into io. */
|
||||
val = TGT_LE(val);
|
||||
glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Handle slow unaligned access (it spans two pages or IO). */
|
||||
if (DATA_SIZE > 1
|
||||
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
|
||||
>= TARGET_PAGE_SIZE)) {
|
||||
int i, index2;
|
||||
target_ulong page2, tlb_addr2;
|
||||
do_unaligned_access:
|
||||
/* Ensure the second page is in the TLB. Note that the first page
|
||||
is already guaranteed to be filled, and that the second page
|
||||
cannot evict the first. */
|
||||
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
|
||||
index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write;
|
||||
if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK))
|
||||
&& !VICTIM_TLB_HIT(addr_write, page2)) {
|
||||
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
|
||||
/* XXX: not efficient, but simple. */
|
||||
/* This loop must go in the forward direction to avoid issues
|
||||
with self-modifying code in Windows 64-bit. */
|
||||
for (i = 0; i < DATA_SIZE; ++i) {
|
||||
/* Little-endian extract. */
|
||||
uint8_t val8 = val >> (i * 8);
|
||||
glue(helper_ret_stb, MMUSUFFIX)(env, addr + i, val8,
|
||||
oi, retaddr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||
#if DATA_SIZE == 1
|
||||
glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val);
|
||||
#else
|
||||
glue(glue(st, SUFFIX), _le_p)((uint8_t *)haddr, val);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if DATA_SIZE > 1
|
||||
void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
unsigned mmu_idx = get_mmuidx(oi);
|
||||
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||
uintptr_t haddr;
|
||||
|
||||
if (addr & ((1 << a_bits) - 1)) {
|
||||
cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
|
||||
/* If the TLB entry is for a different page, reload and try again. */
|
||||
if ((addr & TARGET_PAGE_MASK)
|
||||
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
|
||||
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK;
|
||||
}
|
||||
|
||||
/* Handle an IO access. */
|
||||
if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) {
|
||||
if ((addr & (DATA_SIZE - 1)) != 0) {
|
||||
goto do_unaligned_access;
|
||||
}
|
||||
|
||||
/* ??? Note that the io helpers always read data in the target
|
||||
byte ordering. We should push the LE/BE request down into io. */
|
||||
val = TGT_BE(val);
|
||||
glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Handle slow unaligned access (it spans two pages or IO). */
|
||||
if (DATA_SIZE > 1
|
||||
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
|
||||
>= TARGET_PAGE_SIZE)) {
|
||||
int i, index2;
|
||||
target_ulong page2, tlb_addr2;
|
||||
do_unaligned_access:
|
||||
/* Ensure the second page is in the TLB. Note that the first page
|
||||
is already guaranteed to be filled, and that the second page
|
||||
cannot evict the first. */
|
||||
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
|
||||
index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write;
|
||||
if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK))
|
||||
&& !VICTIM_TLB_HIT(addr_write, page2)) {
|
||||
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
|
||||
/* XXX: not efficient, but simple */
|
||||
/* This loop must go in the forward direction to avoid issues
|
||||
with self-modifying code. */
|
||||
for (i = 0; i < DATA_SIZE; ++i) {
|
||||
/* Big-endian extract. */
|
||||
uint8_t val8 = val >> (((DATA_SIZE - 1) * 8) - (i * 8));
|
||||
glue(helper_ret_stb, MMUSUFFIX)(env, addr + i, val8,
|
||||
oi, retaddr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||
glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val);
|
||||
}
|
||||
#endif /* DATA_SIZE > 1 */
|
||||
#endif /* !defined(SOFTMMU_CODE_ACCESS) */
|
||||
|
||||
#undef READ_ACCESS_TYPE
|
||||
#undef DATA_TYPE
|
||||
#undef SUFFIX
|
||||
#undef LSUFFIX
|
||||
#undef DATA_SIZE
|
||||
#undef ADDR_READ
|
||||
#undef WORD_TYPE
|
||||
#undef SDATA_TYPE
|
||||
#undef USUFFIX
|
||||
#undef SSUFFIX
|
||||
#undef BSWAP
|
||||
#undef helper_le_ld_name
|
||||
#undef helper_be_ld_name
|
||||
#undef helper_le_lds_name
|
||||
#undef helper_be_lds_name
|
||||
#undef helper_le_st_name
|
||||
#undef helper_be_st_name
|
||||
@@ -26,14 +26,15 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/accel.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/tcg.h"
|
||||
#include "qom/object.h"
|
||||
#include "cpu.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qom/cpu.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "qemu/main-loop.h"
|
||||
|
||||
unsigned long tcg_tb_size;
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
/* mask must never be zero, except for A20 change call */
|
||||
static void tcg_handle_interrupt(CPUState *cpu, int mask)
|
||||
{
|
||||
@@ -50,7 +51,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
|
||||
if (!qemu_cpu_is_self(cpu)) {
|
||||
qemu_cpu_kick(cpu);
|
||||
} else {
|
||||
atomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1);
|
||||
cpu->icount_decr.u16.high = -1;
|
||||
if (use_icount &&
|
||||
!cpu->can_do_io
|
||||
&& (mask & ~old_mask) != 0) {
|
||||
@@ -58,6 +59,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int tcg_init(MachineState *ms)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -398,54 +398,6 @@ void HELPER(gvec_neg64)(void *d, void *a, uint32_t desc)
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_abs8)(void *d, void *a, 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 *)(d + i) = aa < 0 ? -aa : aa;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_abs16)(void *d, void *a, 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 *)(d + i) = aa < 0 ? -aa : aa;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_abs32)(void *d, void *a, 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 *)(d + i) = aa < 0 ? -aa : aa;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_abs64)(void *d, void *a, 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 *)(d + i) = aa < 0 ? -aa : aa;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_mov)(void *d, void *a, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
@@ -560,39 +512,6 @@ void HELPER(gvec_orc)(void *d, void *a, void *b, uint32_t desc)
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_nand)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = ~(*(vec64 *)(a + i) & *(vec64 *)(b + i));
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_nor)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = ~(*(vec64 *)(a + i) | *(vec64 *)(b + i));
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_eqv)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = ~(*(vec64 *)(a + i) ^ *(vec64 *)(b + i));
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
@@ -773,150 +692,6 @@ void HELPER(gvec_sar64i)(void *d, void *a, uint32_t desc)
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_shl8v)(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 sh = *(uint8_t *)(b + i) & 7;
|
||||
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) << sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_shl16v)(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)) {
|
||||
uint8_t sh = *(uint16_t *)(b + i) & 15;
|
||||
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) << sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_shl32v)(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)) {
|
||||
uint8_t sh = *(uint32_t *)(b + i) & 31;
|
||||
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) << sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_shl64v)(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)) {
|
||||
uint8_t sh = *(uint64_t *)(b + i) & 63;
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) << sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_shr8v)(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 sh = *(uint8_t *)(b + i) & 7;
|
||||
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) >> sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_shr16v)(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)) {
|
||||
uint8_t sh = *(uint16_t *)(b + i) & 15;
|
||||
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) >> sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_shr32v)(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)) {
|
||||
uint8_t sh = *(uint32_t *)(b + i) & 31;
|
||||
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) >> sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_shr64v)(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)) {
|
||||
uint8_t sh = *(uint64_t *)(b + i) & 63;
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) >> sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_sar8v)(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)) {
|
||||
uint8_t sh = *(uint8_t *)(b + i) & 7;
|
||||
*(int8_t *)(d + i) = *(int8_t *)(a + i) >> sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_sar16v)(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)) {
|
||||
uint8_t sh = *(uint16_t *)(b + i) & 15;
|
||||
*(int16_t *)(d + i) = *(int16_t *)(a + i) >> sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_sar32v)(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)) {
|
||||
uint8_t sh = *(uint32_t *)(b + i) & 31;
|
||||
*(int32_t *)(d + i) = *(int32_t *)(a + i) >> sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_sar64v)(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)) {
|
||||
uint8_t sh = *(uint64_t *)(b + i) & 63;
|
||||
*(int64_t *)(d + i) = *(int64_t *)(a + i) >> sh;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
/* If vectors are enabled, the compiler fills in -1 for true.
|
||||
Otherwise, we must take care of this by hand. */
|
||||
#ifdef CONFIG_VECTOR16
|
||||
@@ -930,7 +705,7 @@ void HELPER(NAME)(void *d, void *a, void *b, uint32_t desc) \
|
||||
{ \
|
||||
intptr_t oprsz = simd_oprsz(desc); \
|
||||
intptr_t i; \
|
||||
for (i = 0; i < oprsz; i += sizeof(TYPE)) { \
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) { \
|
||||
*(TYPE *)(d + i) = DO_CMP0(*(TYPE *)(a + i) OP *(TYPE *)(b + i)); \
|
||||
} \
|
||||
clear_high(d, oprsz, desc); \
|
||||
@@ -1220,241 +995,3 @@ void HELPER(gvec_ussub64)(void *d, void *a, void *b, uint32_t desc)
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_smin8)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(int8_t)) {
|
||||
int8_t aa = *(int8_t *)(a + i);
|
||||
int8_t bb = *(int8_t *)(b + i);
|
||||
int8_t dd = aa < bb ? aa : bb;
|
||||
*(int8_t *)(d + i) = dd;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_smin16)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(int16_t)) {
|
||||
int16_t aa = *(int16_t *)(a + i);
|
||||
int16_t bb = *(int16_t *)(b + i);
|
||||
int16_t dd = aa < bb ? aa : bb;
|
||||
*(int16_t *)(d + i) = dd;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_smin32)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(int32_t)) {
|
||||
int32_t aa = *(int32_t *)(a + i);
|
||||
int32_t bb = *(int32_t *)(b + i);
|
||||
int32_t dd = aa < bb ? aa : bb;
|
||||
*(int32_t *)(d + i) = dd;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_smin64)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(int64_t)) {
|
||||
int64_t aa = *(int64_t *)(a + i);
|
||||
int64_t bb = *(int64_t *)(b + i);
|
||||
int64_t dd = aa < bb ? aa : bb;
|
||||
*(int64_t *)(d + i) = dd;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_smax8)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(int8_t)) {
|
||||
int8_t aa = *(int8_t *)(a + i);
|
||||
int8_t bb = *(int8_t *)(b + i);
|
||||
int8_t dd = aa > bb ? aa : bb;
|
||||
*(int8_t *)(d + i) = dd;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_smax16)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(int16_t)) {
|
||||
int16_t aa = *(int16_t *)(a + i);
|
||||
int16_t bb = *(int16_t *)(b + i);
|
||||
int16_t dd = aa > bb ? aa : bb;
|
||||
*(int16_t *)(d + i) = dd;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_smax32)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(int32_t)) {
|
||||
int32_t aa = *(int32_t *)(a + i);
|
||||
int32_t bb = *(int32_t *)(b + i);
|
||||
int32_t dd = aa > bb ? aa : bb;
|
||||
*(int32_t *)(d + i) = dd;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_smax64)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(int64_t)) {
|
||||
int64_t aa = *(int64_t *)(a + i);
|
||||
int64_t bb = *(int64_t *)(b + i);
|
||||
int64_t dd = aa > bb ? aa : bb;
|
||||
*(int64_t *)(d + i) = dd;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_umin8)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
uint8_t aa = *(uint8_t *)(a + i);
|
||||
uint8_t bb = *(uint8_t *)(b + i);
|
||||
uint8_t dd = aa < bb ? aa : bb;
|
||||
*(uint8_t *)(d + i) = dd;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_umin16)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
uint16_t aa = *(uint16_t *)(a + i);
|
||||
uint16_t bb = *(uint16_t *)(b + i);
|
||||
uint16_t dd = aa < bb ? aa : bb;
|
||||
*(uint16_t *)(d + i) = dd;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_umin32)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
uint32_t aa = *(uint32_t *)(a + i);
|
||||
uint32_t bb = *(uint32_t *)(b + i);
|
||||
uint32_t dd = aa < bb ? aa : bb;
|
||||
*(uint32_t *)(d + i) = dd;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_umin64)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
uint64_t aa = *(uint64_t *)(a + i);
|
||||
uint64_t bb = *(uint64_t *)(b + i);
|
||||
uint64_t dd = aa < bb ? aa : bb;
|
||||
*(uint64_t *)(d + i) = dd;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_umax8)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
uint8_t aa = *(uint8_t *)(a + i);
|
||||
uint8_t bb = *(uint8_t *)(b + i);
|
||||
uint8_t dd = aa > bb ? aa : bb;
|
||||
*(uint8_t *)(d + i) = dd;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_umax16)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
uint16_t aa = *(uint16_t *)(a + i);
|
||||
uint16_t bb = *(uint16_t *)(b + i);
|
||||
uint16_t dd = aa > bb ? aa : bb;
|
||||
*(uint16_t *)(d + i) = dd;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_umax32)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
uint32_t aa = *(uint32_t *)(a + i);
|
||||
uint32_t bb = *(uint32_t *)(b + i);
|
||||
uint32_t dd = aa > bb ? aa : bb;
|
||||
*(uint32_t *)(d + i) = dd;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_umax64)(void *d, void *a, void *b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
uint64_t aa = *(uint64_t *)(a + i);
|
||||
uint64_t bb = *(uint64_t *)(b + i);
|
||||
uint64_t dd = aa > bb ? aa : bb;
|
||||
*(uint64_t *)(d + i) = dd;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
void HELPER(gvec_bitsel)(void *d, void *a, void *b, void *c, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
vec64 aa = *(vec64 *)(a + i);
|
||||
vec64 bb = *(vec64 *)(b + i);
|
||||
vec64 cc = *(vec64 *)(c + i);
|
||||
*(vec64 *)(d + i) = (bb & aa) | (cc & ~aa);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ uint64_t HELPER(ctpop_i64)(uint64_t arg)
|
||||
|
||||
void *HELPER(lookup_tb_ptr)(CPUArchState *env)
|
||||
{
|
||||
CPUState *cpu = env_cpu(env);
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
TranslationBlock *tb;
|
||||
target_ulong cs_base, pc;
|
||||
uint32_t flags;
|
||||
@@ -165,5 +165,5 @@ void *HELPER(lookup_tb_ptr)(CPUArchState *env)
|
||||
|
||||
void HELPER(exit_atomic)(CPUArchState *env)
|
||||
{
|
||||
cpu_loop_exit_atomic(env_cpu(env), GETPC());
|
||||
cpu_loop_exit_atomic(ENV_GET_CPU(env), GETPC());
|
||||
}
|
||||
|
||||
@@ -125,19 +125,11 @@ GEN_ATOMIC_HELPERS(fetch_add)
|
||||
GEN_ATOMIC_HELPERS(fetch_and)
|
||||
GEN_ATOMIC_HELPERS(fetch_or)
|
||||
GEN_ATOMIC_HELPERS(fetch_xor)
|
||||
GEN_ATOMIC_HELPERS(fetch_smin)
|
||||
GEN_ATOMIC_HELPERS(fetch_umin)
|
||||
GEN_ATOMIC_HELPERS(fetch_smax)
|
||||
GEN_ATOMIC_HELPERS(fetch_umax)
|
||||
|
||||
GEN_ATOMIC_HELPERS(add_fetch)
|
||||
GEN_ATOMIC_HELPERS(and_fetch)
|
||||
GEN_ATOMIC_HELPERS(or_fetch)
|
||||
GEN_ATOMIC_HELPERS(xor_fetch)
|
||||
GEN_ATOMIC_HELPERS(smin_fetch)
|
||||
GEN_ATOMIC_HELPERS(umin_fetch)
|
||||
GEN_ATOMIC_HELPERS(smax_fetch)
|
||||
GEN_ATOMIC_HELPERS(umax_fetch)
|
||||
|
||||
GEN_ATOMIC_HELPERS(xchg)
|
||||
|
||||
@@ -200,45 +192,17 @@ DEF_HELPER_FLAGS_4(gvec_ussub16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_ussub32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_ussub64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(gvec_smin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_smin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_smin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_smin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(gvec_smax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_smax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_smax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_smax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(gvec_umin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_umin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_umin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_umin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(gvec_umax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_umax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_umax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_umax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_3(gvec_neg8, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_neg16, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_neg32, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_neg64, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_3(gvec_abs8, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_abs16, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_abs32, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_abs64, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_3(gvec_not, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_and, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_or, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_xor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_andc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_orc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_nand, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_nor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_eqv, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(gvec_ands, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_xors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
|
||||
@@ -259,21 +223,6 @@ DEF_HELPER_FLAGS_3(gvec_sar16i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_sar32i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_sar64i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(gvec_shl8v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_shl16v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_shl32v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_shl64v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(gvec_shr8v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_shr16v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_shr32v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_shr64v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(gvec_sar8v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_sar16v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_sar32v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_sar64v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(gvec_eq8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_eq16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_eq32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
@@ -303,5 +252,3 @@ DEF_HELPER_FLAGS_4(gvec_leu8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_leu16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_leu32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_leu64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_5(gvec_bitsel, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
# Trace events for debugging and performance instrumentation
|
||||
|
||||
# TCG related tracing (mostly disabled by default)
|
||||
# cpu-exec.c
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -23,13 +23,10 @@
|
||||
|
||||
|
||||
/* translate-all.c */
|
||||
struct page_collection *page_collection_lock(tb_page_addr_t start,
|
||||
tb_page_addr_t end);
|
||||
void page_collection_unlock(struct page_collection *set);
|
||||
void tb_invalidate_phys_page_fast(struct page_collection *pages,
|
||||
tb_page_addr_t start, int len);
|
||||
void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len);
|
||||
void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
|
||||
int is_cpu_write_access);
|
||||
void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end);
|
||||
void tb_check_watchpoint(CPUState *cpu);
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "cpu.h"
|
||||
#include "tcg/tcg.h"
|
||||
@@ -31,9 +32,9 @@ void translator_loop_temp_check(DisasContextBase *db)
|
||||
}
|
||||
|
||||
void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
CPUState *cpu, TranslationBlock *tb, int max_insns)
|
||||
CPUState *cpu, TranslationBlock *tb)
|
||||
{
|
||||
int bp_insn = 0;
|
||||
int max_insns;
|
||||
|
||||
/* Initialize DisasContext */
|
||||
db->tb = tb;
|
||||
@@ -41,10 +42,21 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
db->pc_next = db->pc_first;
|
||||
db->is_jmp = DISAS_NEXT;
|
||||
db->num_insns = 0;
|
||||
db->max_insns = max_insns;
|
||||
db->singlestep_enabled = cpu->singlestep_enabled;
|
||||
|
||||
ops->init_disas_context(db, cpu);
|
||||
/* Instruction counting */
|
||||
max_insns = tb_cflags(db->tb) & CF_COUNT_MASK;
|
||||
if (max_insns == 0) {
|
||||
max_insns = CF_COUNT_MASK;
|
||||
}
|
||||
if (max_insns > TCG_MAX_INSNS) {
|
||||
max_insns = TCG_MAX_INSNS;
|
||||
}
|
||||
if (db->singlestep_enabled || singlestep) {
|
||||
max_insns = 1;
|
||||
}
|
||||
|
||||
max_insns = ops->init_disas_context(db, cpu, max_insns);
|
||||
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
||||
|
||||
/* Reset the temp count so that we can identify leaks */
|
||||
@@ -61,13 +73,11 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
||||
|
||||
/* Pass breakpoint hits to target for further processing */
|
||||
if (!db->singlestep_enabled
|
||||
&& unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
|
||||
if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
|
||||
CPUBreakpoint *bp;
|
||||
QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
|
||||
if (bp->pc == db->pc_next) {
|
||||
if (ops->breakpoint_check(db, cpu, bp)) {
|
||||
bp_insn = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -85,8 +95,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
update db->pc_next and db->is_jmp to indicate what should be
|
||||
done next -- either exiting this loop or locate the start of
|
||||
the next instruction. */
|
||||
if (db->num_insns == db->max_insns
|
||||
&& (tb_cflags(db->tb) & CF_LAST_IO)) {
|
||||
if (db->num_insns == max_insns && (tb_cflags(db->tb) & CF_LAST_IO)) {
|
||||
/* Accept I/O on the last instruction. */
|
||||
gen_io_start();
|
||||
ops->translate_insn(db, cpu);
|
||||
@@ -102,7 +111,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
|
||||
/* Stop translation if the output buffer is full,
|
||||
or we have executed all of the allowed instructions. */
|
||||
if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
|
||||
if (tcg_op_buf_full() || db->num_insns >= max_insns) {
|
||||
db->is_jmp = DISAS_TOO_MANY;
|
||||
break;
|
||||
}
|
||||
@@ -110,7 +119,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
|
||||
/* Emit code to exit the TB, as indicated by db->is_jmp. */
|
||||
ops->tb_stop(db, cpu);
|
||||
gen_tb_end(db->tb, db->num_insns - bp_insn);
|
||||
gen_tb_end(db->tb, db->num_insns);
|
||||
|
||||
/* The disas_log hook may use these values rather than recompute. */
|
||||
db->tb->size = db->pc_next - db->pc_first;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qom/cpu.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
bool enable_cpu_pm = false;
|
||||
|
||||
void cpu_resume(CPUState *cpu)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "translate-all.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "qemu/atomic128.h"
|
||||
|
||||
#undef EAX
|
||||
#undef ECX
|
||||
@@ -63,57 +62,28 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
|
||||
{
|
||||
CPUState *cpu = current_cpu;
|
||||
CPUClass *cc;
|
||||
int ret;
|
||||
unsigned long address = (unsigned long)info->si_addr;
|
||||
MMUAccessType access_type = is_write ? MMU_DATA_STORE : MMU_DATA_LOAD;
|
||||
|
||||
switch (helper_retaddr) {
|
||||
default:
|
||||
/*
|
||||
* Fault during host memory operation within a helper function.
|
||||
* The helper's host return address, saved here, gives us a
|
||||
* pointer into the generated code that will unwind to the
|
||||
* correct guest pc.
|
||||
*/
|
||||
/* We must handle PC addresses from two different sources:
|
||||
* a call return address and a signal frame address.
|
||||
*
|
||||
* Within cpu_restore_state_from_tb we assume the former and adjust
|
||||
* the address by -GETPC_ADJ so that the address is within the call
|
||||
* insn so that addr does not accidentally match the beginning of the
|
||||
* next guest insn.
|
||||
*
|
||||
* However, when the PC comes from the signal frame, it points to
|
||||
* the actual faulting host insn and not a call insn. Subtracting
|
||||
* GETPC_ADJ in that case may accidentally match the previous guest insn.
|
||||
*
|
||||
* So for the later case, adjust forward to compensate for what
|
||||
* will be done later by cpu_restore_state_from_tb.
|
||||
*/
|
||||
if (helper_retaddr) {
|
||||
pc = helper_retaddr;
|
||||
break;
|
||||
|
||||
case 0:
|
||||
/*
|
||||
* Fault during host memory operation within generated code.
|
||||
* (Or, a unrelated bug within qemu, but we can't tell from here).
|
||||
*
|
||||
* We take the host pc from the signal frame. However, we cannot
|
||||
* use that value directly. Within cpu_restore_state_from_tb, we
|
||||
* assume PC comes from GETPC(), as used by the helper functions,
|
||||
* so we adjust the address by -GETPC_ADJ to form an address that
|
||||
* is within the call insn, so that the address does not accidentially
|
||||
* match the beginning of the next guest insn. However, when the
|
||||
* pc comes from the signal frame it points to the actual faulting
|
||||
* host memory insn and not the return from a call insn.
|
||||
*
|
||||
* Therefore, adjust to compensate for what will be done later
|
||||
* by cpu_restore_state_from_tb.
|
||||
*/
|
||||
} else {
|
||||
pc += GETPC_ADJ;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/*
|
||||
* Fault during host read for translation, or loosely, "execution".
|
||||
*
|
||||
* The guest pc is already pointing to the start of the TB for which
|
||||
* code is being generated. If the guest translator manages the
|
||||
* page crossings correctly, this is exactly the correct address
|
||||
* (and if the translator doesn't handle page boundaries correctly
|
||||
* there's little we can do about that here). Therefore, do not
|
||||
* trigger the unwinder.
|
||||
*
|
||||
* Like tb_gen_code, release the memory lock before cpu_loop_exit.
|
||||
*/
|
||||
pc = 0;
|
||||
access_type = MMU_INST_FETCH;
|
||||
mmap_unlock();
|
||||
break;
|
||||
}
|
||||
|
||||
/* For synchronous signals we expect to be coming from the vCPU
|
||||
@@ -163,7 +133,7 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
|
||||
* currently executing TB was modified and must be exited
|
||||
* immediately. Clear helper_retaddr for next execution.
|
||||
*/
|
||||
clear_helper_retaddr();
|
||||
helper_retaddr = 0;
|
||||
cpu_exit_tb_from_sighandler(cpu, old_set);
|
||||
/* NORETURN */
|
||||
|
||||
@@ -176,16 +146,35 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
|
||||
are still valid segv ones */
|
||||
address = h2g_nocheck(address);
|
||||
|
||||
/*
|
||||
* There is no way the target can handle this other than raising
|
||||
* an exception. Undo signal and retaddr state prior to longjmp.
|
||||
*/
|
||||
sigprocmask(SIG_SETMASK, old_set, NULL);
|
||||
clear_helper_retaddr();
|
||||
|
||||
cc = CPU_GET_CLASS(cpu);
|
||||
cc->tlb_fill(cpu, address, 0, access_type, MMU_USER_IDX, false, pc);
|
||||
g_assert_not_reached();
|
||||
/* see if it is an MMU fault */
|
||||
g_assert(cc->handle_mmu_fault);
|
||||
ret = cc->handle_mmu_fault(cpu, address, 0, is_write, MMU_USER_IDX);
|
||||
|
||||
if (ret == 0) {
|
||||
/* The MMU fault was handled without causing real CPU fault.
|
||||
* Retain helper_retaddr for a possible second fault.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* All other paths lead to cpu_exit; clear helper_retaddr
|
||||
* for next execution.
|
||||
*/
|
||||
helper_retaddr = 0;
|
||||
|
||||
if (ret < 0) {
|
||||
return 0; /* not an MMU fault */
|
||||
}
|
||||
|
||||
/* Now we have a real cpu fault. */
|
||||
cpu_restore_state(cpu, pc);
|
||||
|
||||
sigprocmask(SIG_SETMASK, old_set, NULL);
|
||||
cpu_loop_exit(cpu);
|
||||
|
||||
/* never comes here */
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if defined(__i386__)
|
||||
@@ -489,66 +478,28 @@ int cpu_signal_handler(int host_signum, void *pinfo,
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
#ifndef ESR_MAGIC
|
||||
/* Pre-3.16 kernel headers don't have these, so provide fallback definitions */
|
||||
#define ESR_MAGIC 0x45535201
|
||||
struct esr_context {
|
||||
struct _aarch64_ctx head;
|
||||
uint64_t esr;
|
||||
};
|
||||
#endif
|
||||
|
||||
static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc)
|
||||
{
|
||||
return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved;
|
||||
}
|
||||
|
||||
static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr)
|
||||
{
|
||||
return (struct _aarch64_ctx *)((char *)hdr + hdr->size);
|
||||
}
|
||||
|
||||
int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
|
||||
{
|
||||
siginfo_t *info = pinfo;
|
||||
ucontext_t *uc = puc;
|
||||
uintptr_t pc = uc->uc_mcontext.pc;
|
||||
uint32_t insn = *(uint32_t *)pc;
|
||||
bool is_write;
|
||||
struct _aarch64_ctx *hdr;
|
||||
struct esr_context const *esrctx = NULL;
|
||||
|
||||
/* Find the esr_context, which has the WnR bit in it */
|
||||
for (hdr = first_ctx(uc); hdr->magic; hdr = next_ctx(hdr)) {
|
||||
if (hdr->magic == ESR_MAGIC) {
|
||||
esrctx = (struct esr_context const *)hdr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* XXX: need kernel patch to get write flag faster. */
|
||||
is_write = ( (insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */
|
||||
|| (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */
|
||||
|| (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */
|
||||
|| (insn & 0xbfc00000) == 0x0d800000 /* C3.3.4 */
|
||||
|| (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */
|
||||
|| (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */
|
||||
|| (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */
|
||||
/* Ingore bits 10, 11 & 21, controlling indexing. */
|
||||
|| (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */
|
||||
|| (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */
|
||||
/* Ignore bits 23 & 24, controlling indexing. */
|
||||
|| (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */
|
||||
|
||||
if (esrctx) {
|
||||
/* For data aborts ESR.EC is 0b10010x: then bit 6 is the WnR bit */
|
||||
uint64_t esr = esrctx->esr;
|
||||
is_write = extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1;
|
||||
} else {
|
||||
/*
|
||||
* Fall back to parsing instructions; will only be needed
|
||||
* for really ancient (pre-3.16) kernels.
|
||||
*/
|
||||
uint32_t insn = *(uint32_t *)pc;
|
||||
|
||||
is_write = ((insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */
|
||||
|| (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */
|
||||
|| (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */
|
||||
|| (insn & 0xbfc00000) == 0x0d800000 /* C3.3.4 */
|
||||
|| (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */
|
||||
|| (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */
|
||||
|| (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */
|
||||
/* Ignore bits 10, 11 & 21, controlling indexing. */
|
||||
|| (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */
|
||||
|| (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */
|
||||
/* Ignore bits 23 & 24, controlling indexing. */
|
||||
|| (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */
|
||||
}
|
||||
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
|
||||
}
|
||||
|
||||
@@ -619,81 +570,6 @@ int cpu_signal_handler(int host_signum, void *pinfo,
|
||||
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
|
||||
}
|
||||
|
||||
#elif defined(__riscv)
|
||||
|
||||
int cpu_signal_handler(int host_signum, void *pinfo,
|
||||
void *puc)
|
||||
{
|
||||
siginfo_t *info = pinfo;
|
||||
ucontext_t *uc = puc;
|
||||
greg_t pc = uc->uc_mcontext.__gregs[REG_PC];
|
||||
uint32_t insn = *(uint32_t *)pc;
|
||||
int is_write = 0;
|
||||
|
||||
/* Detect store by reading the instruction at the program
|
||||
counter. Note: we currently only generate 32-bit
|
||||
instructions so we thus only detect 32-bit stores */
|
||||
switch (((insn >> 0) & 0b11)) {
|
||||
case 3:
|
||||
switch (((insn >> 2) & 0b11111)) {
|
||||
case 8:
|
||||
switch (((insn >> 12) & 0b111)) {
|
||||
case 0: /* sb */
|
||||
case 1: /* sh */
|
||||
case 2: /* sw */
|
||||
case 3: /* sd */
|
||||
case 4: /* sq */
|
||||
is_write = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
switch (((insn >> 12) & 0b111)) {
|
||||
case 2: /* fsw */
|
||||
case 3: /* fsd */
|
||||
case 4: /* fsq */
|
||||
is_write = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for compressed instructions */
|
||||
switch (((insn >> 13) & 0b111)) {
|
||||
case 7:
|
||||
switch (insn & 0b11) {
|
||||
case 0: /*c.sd */
|
||||
case 2: /* c.sdsp */
|
||||
is_write = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
switch (insn & 0b11) {
|
||||
case 0: /* c.sw */
|
||||
case 3: /* c.swsp */
|
||||
is_write = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#error host CPU specific signal handler needed
|
||||
@@ -708,17 +584,16 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
{
|
||||
/* Enforce qemu required alignment. */
|
||||
if (unlikely(addr & (size - 1))) {
|
||||
cpu_loop_exit_atomic(env_cpu(env), retaddr);
|
||||
cpu_loop_exit_atomic(ENV_GET_CPU(env), retaddr);
|
||||
}
|
||||
void *ret = g2h(addr);
|
||||
set_helper_retaddr(retaddr);
|
||||
return ret;
|
||||
helper_retaddr = retaddr;
|
||||
return g2h(addr);
|
||||
}
|
||||
|
||||
/* Macro to call the above, with local variables from the use context. */
|
||||
#define ATOMIC_MMU_DECLS do {} while (0)
|
||||
#define ATOMIC_MMU_LOOKUP atomic_mmu_lookup(env, addr, DATA_SIZE, GETPC())
|
||||
#define ATOMIC_MMU_CLEANUP do { clear_helper_retaddr(); } while (0)
|
||||
#define ATOMIC_MMU_CLEANUP do { helper_retaddr = 0; } while (0)
|
||||
|
||||
#define ATOMIC_NAME(X) HELPER(glue(glue(atomic_ ## X, SUFFIX), END))
|
||||
#define EXTRA_ARGS
|
||||
@@ -740,7 +615,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
/* The following is only callable from other helpers, and matches up
|
||||
with the softmmu version. */
|
||||
|
||||
#if HAVE_ATOMIC128 || HAVE_CMPXCHG128
|
||||
#ifdef CONFIG_ATOMIC128
|
||||
|
||||
#undef EXTRA_ARGS
|
||||
#undef ATOMIC_NAME
|
||||
@@ -753,4 +628,4 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
|
||||
#define DATA_SIZE 16
|
||||
#include "atomic_template.h"
|
||||
#endif
|
||||
#endif /* CONFIG_ATOMIC128 */
|
||||
|
||||
21
arch_init.c
21
arch_init.c
@@ -22,13 +22,13 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "cpu.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/audio/soundhw.h"
|
||||
#include "qapi/qapi-commands-misc.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/acpi/acpi.h"
|
||||
@@ -51,14 +51,14 @@ int graphic_depth = 32;
|
||||
#define QEMU_ARCH QEMU_ARCH_ARM
|
||||
#elif defined(TARGET_CRIS)
|
||||
#define QEMU_ARCH QEMU_ARCH_CRIS
|
||||
#elif defined(TARGET_HPPA)
|
||||
#define QEMU_ARCH QEMU_ARCH_HPPA
|
||||
#elif defined(TARGET_I386)
|
||||
#define QEMU_ARCH QEMU_ARCH_I386
|
||||
#elif defined(TARGET_LM32)
|
||||
#define QEMU_ARCH QEMU_ARCH_LM32
|
||||
#elif defined(TARGET_HPPA)
|
||||
#define QEMU_ARCH QEMU_ARCH_HPPA
|
||||
#elif defined(TARGET_M68K)
|
||||
#define QEMU_ARCH QEMU_ARCH_M68K
|
||||
#elif defined(TARGET_LM32)
|
||||
#define QEMU_ARCH QEMU_ARCH_LM32
|
||||
#elif defined(TARGET_MICROBLAZE)
|
||||
#define QEMU_ARCH QEMU_ARCH_MICROBLAZE
|
||||
#elif defined(TARGET_MIPS)
|
||||
@@ -79,12 +79,12 @@ int graphic_depth = 32;
|
||||
#define QEMU_ARCH QEMU_ARCH_SH4
|
||||
#elif defined(TARGET_SPARC)
|
||||
#define QEMU_ARCH QEMU_ARCH_SPARC
|
||||
#elif defined(TARGET_TRICORE)
|
||||
#define QEMU_ARCH QEMU_ARCH_TRICORE
|
||||
#elif defined(TARGET_UNICORE32)
|
||||
#define QEMU_ARCH QEMU_ARCH_UNICORE32
|
||||
#elif defined(TARGET_XTENSA)
|
||||
#define QEMU_ARCH QEMU_ARCH_XTENSA
|
||||
#elif defined(TARGET_UNICORE32)
|
||||
#define QEMU_ARCH QEMU_ARCH_UNICORE32
|
||||
#elif defined(TARGET_TRICORE)
|
||||
#define QEMU_ARCH QEMU_ARCH_TRICORE
|
||||
#endif
|
||||
|
||||
const uint32_t arch_type = QEMU_ARCH;
|
||||
@@ -112,8 +112,7 @@ TargetInfo *qmp_query_target(Error **errp)
|
||||
{
|
||||
TargetInfo *info = g_malloc0(sizeof(*info));
|
||||
|
||||
info->arch = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, -1,
|
||||
&error_abort);
|
||||
info->arch = g_strdup(TARGET_NAME);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o
|
||||
common-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
|
||||
common-obj-$(CONFIG_SPICE) += spiceaudio.o
|
||||
common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o
|
||||
common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o
|
||||
|
||||
@@ -21,22 +21,42 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <alsa/asoundlib.h>
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/module.h"
|
||||
#include "audio.h"
|
||||
#include "trace.h"
|
||||
|
||||
#if QEMU_GNUC_PREREQ(4, 3)
|
||||
#pragma GCC diagnostic ignored "-Waddress"
|
||||
#endif
|
||||
|
||||
#define AUDIO_CAP "alsa"
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct ALSAConf {
|
||||
int size_in_usec_in;
|
||||
int size_in_usec_out;
|
||||
const char *pcm_name_in;
|
||||
const char *pcm_name_out;
|
||||
unsigned int buffer_size_in;
|
||||
unsigned int period_size_in;
|
||||
unsigned int buffer_size_out;
|
||||
unsigned int period_size_out;
|
||||
unsigned int threshold;
|
||||
|
||||
int buffer_size_in_overridden;
|
||||
int period_size_in_overridden;
|
||||
|
||||
int buffer_size_out_overridden;
|
||||
int period_size_out_overridden;
|
||||
} ALSAConf;
|
||||
|
||||
struct pollhlp {
|
||||
snd_pcm_t *handle;
|
||||
struct pollfd *pfds;
|
||||
ALSAConf *conf;
|
||||
int count;
|
||||
int mask;
|
||||
};
|
||||
@@ -48,7 +68,6 @@ typedef struct ALSAVoiceOut {
|
||||
void *pcm_buf;
|
||||
snd_pcm_t *handle;
|
||||
struct pollhlp pollhlp;
|
||||
Audiodev *dev;
|
||||
} ALSAVoiceOut;
|
||||
|
||||
typedef struct ALSAVoiceIn {
|
||||
@@ -56,18 +75,21 @@ typedef struct ALSAVoiceIn {
|
||||
snd_pcm_t *handle;
|
||||
void *pcm_buf;
|
||||
struct pollhlp pollhlp;
|
||||
Audiodev *dev;
|
||||
} ALSAVoiceIn;
|
||||
|
||||
struct alsa_params_req {
|
||||
int freq;
|
||||
snd_pcm_format_t fmt;
|
||||
int nchannels;
|
||||
int size_in_usec;
|
||||
int override_mask;
|
||||
unsigned int buffer_size;
|
||||
unsigned int period_size;
|
||||
};
|
||||
|
||||
struct alsa_params_obt {
|
||||
int freq;
|
||||
AudioFormat fmt;
|
||||
audfmt_e fmt;
|
||||
int endianness;
|
||||
int nchannels;
|
||||
snd_pcm_uframes_t samples;
|
||||
@@ -274,16 +296,16 @@ static int alsa_write (SWVoiceOut *sw, void *buf, int len)
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
|
||||
static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
|
||||
{
|
||||
switch (fmt) {
|
||||
case AUDIO_FORMAT_S8:
|
||||
case AUD_FMT_S8:
|
||||
return SND_PCM_FORMAT_S8;
|
||||
|
||||
case AUDIO_FORMAT_U8:
|
||||
case AUD_FMT_U8:
|
||||
return SND_PCM_FORMAT_U8;
|
||||
|
||||
case AUDIO_FORMAT_S16:
|
||||
case AUD_FMT_S16:
|
||||
if (endianness) {
|
||||
return SND_PCM_FORMAT_S16_BE;
|
||||
}
|
||||
@@ -291,7 +313,7 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
|
||||
return SND_PCM_FORMAT_S16_LE;
|
||||
}
|
||||
|
||||
case AUDIO_FORMAT_U16:
|
||||
case AUD_FMT_U16:
|
||||
if (endianness) {
|
||||
return SND_PCM_FORMAT_U16_BE;
|
||||
}
|
||||
@@ -299,7 +321,7 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
|
||||
return SND_PCM_FORMAT_U16_LE;
|
||||
}
|
||||
|
||||
case AUDIO_FORMAT_S32:
|
||||
case AUD_FMT_S32:
|
||||
if (endianness) {
|
||||
return SND_PCM_FORMAT_S32_BE;
|
||||
}
|
||||
@@ -307,7 +329,7 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
|
||||
return SND_PCM_FORMAT_S32_LE;
|
||||
}
|
||||
|
||||
case AUDIO_FORMAT_U32:
|
||||
case AUD_FMT_U32:
|
||||
if (endianness) {
|
||||
return SND_PCM_FORMAT_U32_BE;
|
||||
}
|
||||
@@ -324,58 +346,58 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
|
||||
}
|
||||
}
|
||||
|
||||
static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt,
|
||||
static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt,
|
||||
int *endianness)
|
||||
{
|
||||
switch (alsafmt) {
|
||||
case SND_PCM_FORMAT_S8:
|
||||
*endianness = 0;
|
||||
*fmt = AUDIO_FORMAT_S8;
|
||||
*fmt = AUD_FMT_S8;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_U8:
|
||||
*endianness = 0;
|
||||
*fmt = AUDIO_FORMAT_U8;
|
||||
*fmt = AUD_FMT_U8;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_S16_LE:
|
||||
*endianness = 0;
|
||||
*fmt = AUDIO_FORMAT_S16;
|
||||
*fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_U16_LE:
|
||||
*endianness = 0;
|
||||
*fmt = AUDIO_FORMAT_U16;
|
||||
*fmt = AUD_FMT_U16;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_S16_BE:
|
||||
*endianness = 1;
|
||||
*fmt = AUDIO_FORMAT_S16;
|
||||
*fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_U16_BE:
|
||||
*endianness = 1;
|
||||
*fmt = AUDIO_FORMAT_U16;
|
||||
*fmt = AUD_FMT_U16;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_S32_LE:
|
||||
*endianness = 0;
|
||||
*fmt = AUDIO_FORMAT_S32;
|
||||
*fmt = AUD_FMT_S32;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_U32_LE:
|
||||
*endianness = 0;
|
||||
*fmt = AUDIO_FORMAT_U32;
|
||||
*fmt = AUD_FMT_U32;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_S32_BE:
|
||||
*endianness = 1;
|
||||
*fmt = AUDIO_FORMAT_S32;
|
||||
*fmt = AUD_FMT_S32;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_U32_BE:
|
||||
*endianness = 1;
|
||||
*fmt = AUDIO_FORMAT_U32;
|
||||
*fmt = AUD_FMT_U32;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -388,18 +410,17 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt,
|
||||
|
||||
static void alsa_dump_info (struct alsa_params_req *req,
|
||||
struct alsa_params_obt *obt,
|
||||
snd_pcm_format_t obtfmt,
|
||||
AudiodevAlsaPerDirectionOptions *apdo)
|
||||
snd_pcm_format_t obtfmt)
|
||||
{
|
||||
dolog("parameter | requested value | obtained value\n");
|
||||
dolog("format | %10d | %10d\n", req->fmt, obtfmt);
|
||||
dolog("channels | %10d | %10d\n",
|
||||
req->nchannels, obt->nchannels);
|
||||
dolog("frequency | %10d | %10d\n", req->freq, obt->freq);
|
||||
dolog("============================================\n");
|
||||
dolog("requested: buffer len %" PRId32 " period len %" PRId32 "\n",
|
||||
apdo->buffer_length, apdo->period_length);
|
||||
dolog("obtained: samples %ld\n", obt->samples);
|
||||
dolog ("parameter | requested value | obtained value\n");
|
||||
dolog ("format | %10d | %10d\n", req->fmt, obtfmt);
|
||||
dolog ("channels | %10d | %10d\n",
|
||||
req->nchannels, obt->nchannels);
|
||||
dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
|
||||
dolog ("============================================\n");
|
||||
dolog ("requested: buffer size %d period size %d\n",
|
||||
req->buffer_size, req->period_size);
|
||||
dolog ("obtained: samples %ld\n", obt->samples);
|
||||
}
|
||||
|
||||
static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
|
||||
@@ -432,23 +453,23 @@ static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
|
||||
}
|
||||
}
|
||||
|
||||
static int alsa_open(bool in, struct alsa_params_req *req,
|
||||
struct alsa_params_obt *obt, snd_pcm_t **handlep,
|
||||
Audiodev *dev)
|
||||
static int alsa_open (int in, struct alsa_params_req *req,
|
||||
struct alsa_params_obt *obt, snd_pcm_t **handlep,
|
||||
ALSAConf *conf)
|
||||
{
|
||||
AudiodevAlsaOptions *aopts = &dev->u.alsa;
|
||||
AudiodevAlsaPerDirectionOptions *apdo = in ? aopts->in : aopts->out;
|
||||
snd_pcm_t *handle;
|
||||
snd_pcm_hw_params_t *hw_params;
|
||||
int err;
|
||||
int size_in_usec;
|
||||
unsigned int freq, nchannels;
|
||||
const char *pcm_name = apdo->has_dev ? apdo->dev : "default";
|
||||
const char *pcm_name = in ? conf->pcm_name_in : conf->pcm_name_out;
|
||||
snd_pcm_uframes_t obt_buffer_size;
|
||||
const char *typ = in ? "ADC" : "DAC";
|
||||
snd_pcm_format_t obtfmt;
|
||||
|
||||
freq = req->freq;
|
||||
nchannels = req->nchannels;
|
||||
size_in_usec = req->size_in_usec;
|
||||
|
||||
snd_pcm_hw_params_alloca (&hw_params);
|
||||
|
||||
@@ -508,42 +529,79 @@ static int alsa_open(bool in, struct alsa_params_req *req,
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (apdo->buffer_length) {
|
||||
int dir = 0;
|
||||
unsigned int btime = apdo->buffer_length;
|
||||
if (req->buffer_size) {
|
||||
unsigned long obt;
|
||||
|
||||
err = snd_pcm_hw_params_set_buffer_time_near(
|
||||
handle, hw_params, &btime, &dir);
|
||||
if (size_in_usec) {
|
||||
int dir = 0;
|
||||
unsigned int btime = req->buffer_size;
|
||||
|
||||
err = snd_pcm_hw_params_set_buffer_time_near (
|
||||
handle,
|
||||
hw_params,
|
||||
&btime,
|
||||
&dir
|
||||
);
|
||||
obt = btime;
|
||||
}
|
||||
else {
|
||||
snd_pcm_uframes_t bsize = req->buffer_size;
|
||||
|
||||
err = snd_pcm_hw_params_set_buffer_size_near (
|
||||
handle,
|
||||
hw_params,
|
||||
&bsize
|
||||
);
|
||||
obt = bsize;
|
||||
}
|
||||
if (err < 0) {
|
||||
alsa_logerr2(err, typ, "Failed to set buffer time to %" PRId32 "\n",
|
||||
apdo->buffer_length);
|
||||
alsa_logerr2 (err, typ, "Failed to set buffer %s to %d\n",
|
||||
size_in_usec ? "time" : "size", req->buffer_size);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (apdo->has_buffer_length && btime != apdo->buffer_length) {
|
||||
dolog("Requested buffer time %" PRId32
|
||||
" was rejected, using %u\n", apdo->buffer_length, btime);
|
||||
}
|
||||
if ((req->override_mask & 2) && (obt - req->buffer_size))
|
||||
dolog ("Requested buffer %s %u was rejected, using %lu\n",
|
||||
size_in_usec ? "time" : "size", req->buffer_size, obt);
|
||||
}
|
||||
|
||||
if (apdo->period_length) {
|
||||
int dir = 0;
|
||||
unsigned int ptime = apdo->period_length;
|
||||
if (req->period_size) {
|
||||
unsigned long obt;
|
||||
|
||||
err = snd_pcm_hw_params_set_period_time_near(handle, hw_params, &ptime,
|
||||
&dir);
|
||||
if (size_in_usec) {
|
||||
int dir = 0;
|
||||
unsigned int ptime = req->period_size;
|
||||
|
||||
err = snd_pcm_hw_params_set_period_time_near (
|
||||
handle,
|
||||
hw_params,
|
||||
&ptime,
|
||||
&dir
|
||||
);
|
||||
obt = ptime;
|
||||
}
|
||||
else {
|
||||
int dir = 0;
|
||||
snd_pcm_uframes_t psize = req->period_size;
|
||||
|
||||
err = snd_pcm_hw_params_set_period_size_near (
|
||||
handle,
|
||||
hw_params,
|
||||
&psize,
|
||||
&dir
|
||||
);
|
||||
obt = psize;
|
||||
}
|
||||
|
||||
if (err < 0) {
|
||||
alsa_logerr2(err, typ, "Failed to set period time to %" PRId32 "\n",
|
||||
apdo->period_length);
|
||||
alsa_logerr2 (err, typ, "Failed to set period %s to %d\n",
|
||||
size_in_usec ? "time" : "size", req->period_size);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (apdo->has_period_length && ptime != apdo->period_length) {
|
||||
dolog("Requested period time %" PRId32 " was rejected, using %d\n",
|
||||
apdo->period_length, ptime);
|
||||
}
|
||||
if (((req->override_mask & 1) && (obt - req->period_size)))
|
||||
dolog ("Requested period %s %u was rejected, using %lu\n",
|
||||
size_in_usec ? "time" : "size", req->period_size, obt);
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params (handle, hw_params);
|
||||
@@ -575,12 +633,30 @@ static int alsa_open(bool in, struct alsa_params_req *req,
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!in && aopts->has_threshold && aopts->threshold) {
|
||||
struct audsettings as = { .freq = freq };
|
||||
alsa_set_threshold(
|
||||
handle,
|
||||
audio_buffer_frames(qapi_AudiodevAlsaPerDirectionOptions_base(apdo),
|
||||
&as, aopts->threshold));
|
||||
if (!in && conf->threshold) {
|
||||
snd_pcm_uframes_t threshold;
|
||||
int bytes_per_sec;
|
||||
|
||||
bytes_per_sec = freq << (nchannels == 2);
|
||||
|
||||
switch (obt->fmt) {
|
||||
case AUD_FMT_S8:
|
||||
case AUD_FMT_U8:
|
||||
break;
|
||||
|
||||
case AUD_FMT_S16:
|
||||
case AUD_FMT_U16:
|
||||
bytes_per_sec <<= 1;
|
||||
break;
|
||||
|
||||
case AUD_FMT_S32:
|
||||
case AUD_FMT_U32:
|
||||
bytes_per_sec <<= 2;
|
||||
break;
|
||||
}
|
||||
|
||||
threshold = (conf->threshold * bytes_per_sec) / 1000;
|
||||
alsa_set_threshold (handle, threshold);
|
||||
}
|
||||
|
||||
obt->nchannels = nchannels;
|
||||
@@ -593,11 +669,11 @@ static int alsa_open(bool in, struct alsa_params_req *req,
|
||||
obt->nchannels != req->nchannels ||
|
||||
obt->freq != req->freq) {
|
||||
dolog ("Audio parameters for %s\n", typ);
|
||||
alsa_dump_info(req, obt, obtfmt, apdo);
|
||||
alsa_dump_info (req, obt, obtfmt);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
alsa_dump_info(req, obt, obtfmt, pdo);
|
||||
alsa_dump_info (req, obt, obtfmt);
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
@@ -723,13 +799,19 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
struct alsa_params_obt obt;
|
||||
snd_pcm_t *handle;
|
||||
struct audsettings obt_as;
|
||||
Audiodev *dev = drv_opaque;
|
||||
ALSAConf *conf = drv_opaque;
|
||||
|
||||
req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
|
||||
req.freq = as->freq;
|
||||
req.nchannels = as->nchannels;
|
||||
req.period_size = conf->period_size_out;
|
||||
req.buffer_size = conf->buffer_size_out;
|
||||
req.size_in_usec = conf->size_in_usec_out;
|
||||
req.override_mask =
|
||||
(conf->period_size_out_overridden ? 1 : 0) |
|
||||
(conf->buffer_size_out_overridden ? 2 : 0);
|
||||
|
||||
if (alsa_open(0, &req, &obt, &handle, dev)) {
|
||||
if (alsa_open (0, &req, &obt, &handle, conf)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -750,7 +832,7 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
}
|
||||
|
||||
alsa->handle = handle;
|
||||
alsa->dev = dev;
|
||||
alsa->pollhlp.conf = conf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -790,12 +872,16 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl)
|
||||
static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
||||
AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.out;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
{
|
||||
bool poll_mode = apdo->try_poll;
|
||||
va_list ap;
|
||||
int poll_mode;
|
||||
|
||||
va_start (ap, cmd);
|
||||
poll_mode = va_arg (ap, int);
|
||||
va_end (ap);
|
||||
|
||||
ldebug ("enabling voice\n");
|
||||
if (poll_mode && alsa_poll_out (hw)) {
|
||||
@@ -824,13 +910,19 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
struct alsa_params_obt obt;
|
||||
snd_pcm_t *handle;
|
||||
struct audsettings obt_as;
|
||||
Audiodev *dev = drv_opaque;
|
||||
ALSAConf *conf = drv_opaque;
|
||||
|
||||
req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
|
||||
req.freq = as->freq;
|
||||
req.nchannels = as->nchannels;
|
||||
req.period_size = conf->period_size_in;
|
||||
req.buffer_size = conf->buffer_size_in;
|
||||
req.size_in_usec = conf->size_in_usec_in;
|
||||
req.override_mask =
|
||||
(conf->period_size_in_overridden ? 1 : 0) |
|
||||
(conf->buffer_size_in_overridden ? 2 : 0);
|
||||
|
||||
if (alsa_open(1, &req, &obt, &handle, dev)) {
|
||||
if (alsa_open (1, &req, &obt, &handle, conf)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -851,7 +943,7 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
}
|
||||
|
||||
alsa->handle = handle;
|
||||
alsa->dev = dev;
|
||||
alsa->pollhlp.conf = conf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -993,12 +1085,16 @@ static int alsa_read (SWVoiceIn *sw, void *buf, int size)
|
||||
static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
{
|
||||
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
||||
AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.in;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
{
|
||||
bool poll_mode = apdo->try_poll;
|
||||
va_list ap;
|
||||
int poll_mode;
|
||||
|
||||
va_start (ap, cmd);
|
||||
poll_mode = va_arg (ap, int);
|
||||
va_end (ap);
|
||||
|
||||
ldebug ("enabling voice\n");
|
||||
if (poll_mode && alsa_poll_in (hw)) {
|
||||
@@ -1021,54 +1117,88 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo)
|
||||
static ALSAConf glob_conf = {
|
||||
.buffer_size_out = 4096,
|
||||
.period_size_out = 1024,
|
||||
.pcm_name_out = "default",
|
||||
.pcm_name_in = "default",
|
||||
};
|
||||
|
||||
static void *alsa_audio_init (void)
|
||||
{
|
||||
if (!apdo->has_try_poll) {
|
||||
apdo->try_poll = true;
|
||||
apdo->has_try_poll = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void *alsa_audio_init(Audiodev *dev)
|
||||
{
|
||||
AudiodevAlsaOptions *aopts;
|
||||
assert(dev->driver == AUDIODEV_DRIVER_ALSA);
|
||||
|
||||
aopts = &dev->u.alsa;
|
||||
alsa_init_per_direction(aopts->in);
|
||||
alsa_init_per_direction(aopts->out);
|
||||
|
||||
/*
|
||||
* need to define them, as otherwise alsa produces no sound
|
||||
* doesn't set has_* so alsa_open can identify it wasn't set by the user
|
||||
*/
|
||||
if (!dev->u.alsa.out->has_period_length) {
|
||||
/* 1024 frames assuming 44100Hz */
|
||||
dev->u.alsa.out->period_length = 1024 * 1000000 / 44100;
|
||||
}
|
||||
if (!dev->u.alsa.out->has_buffer_length) {
|
||||
/* 4096 frames assuming 44100Hz */
|
||||
dev->u.alsa.out->buffer_length = 4096ll * 1000000 / 44100;
|
||||
}
|
||||
|
||||
/*
|
||||
* OptsVisitor sets unspecified optional fields to zero, but do not depend
|
||||
* on it...
|
||||
*/
|
||||
if (!dev->u.alsa.in->has_period_length) {
|
||||
dev->u.alsa.in->period_length = 0;
|
||||
}
|
||||
if (!dev->u.alsa.in->has_buffer_length) {
|
||||
dev->u.alsa.in->buffer_length = 0;
|
||||
}
|
||||
|
||||
return dev;
|
||||
ALSAConf *conf = g_malloc(sizeof(ALSAConf));
|
||||
*conf = glob_conf;
|
||||
return conf;
|
||||
}
|
||||
|
||||
static void alsa_audio_fini (void *opaque)
|
||||
{
|
||||
g_free(opaque);
|
||||
}
|
||||
|
||||
static struct audio_option alsa_options[] = {
|
||||
{
|
||||
.name = "DAC_SIZE_IN_USEC",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &glob_conf.size_in_usec_out,
|
||||
.descr = "DAC period/buffer size in microseconds (otherwise in frames)"
|
||||
},
|
||||
{
|
||||
.name = "DAC_PERIOD_SIZE",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &glob_conf.period_size_out,
|
||||
.descr = "DAC period size (0 to go with system default)",
|
||||
.overriddenp = &glob_conf.period_size_out_overridden
|
||||
},
|
||||
{
|
||||
.name = "DAC_BUFFER_SIZE",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &glob_conf.buffer_size_out,
|
||||
.descr = "DAC buffer size (0 to go with system default)",
|
||||
.overriddenp = &glob_conf.buffer_size_out_overridden
|
||||
},
|
||||
{
|
||||
.name = "ADC_SIZE_IN_USEC",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &glob_conf.size_in_usec_in,
|
||||
.descr =
|
||||
"ADC period/buffer size in microseconds (otherwise in frames)"
|
||||
},
|
||||
{
|
||||
.name = "ADC_PERIOD_SIZE",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &glob_conf.period_size_in,
|
||||
.descr = "ADC period size (0 to go with system default)",
|
||||
.overriddenp = &glob_conf.period_size_in_overridden
|
||||
},
|
||||
{
|
||||
.name = "ADC_BUFFER_SIZE",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &glob_conf.buffer_size_in,
|
||||
.descr = "ADC buffer size (0 to go with system default)",
|
||||
.overriddenp = &glob_conf.buffer_size_in_overridden
|
||||
},
|
||||
{
|
||||
.name = "THRESHOLD",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &glob_conf.threshold,
|
||||
.descr = "(undocumented)"
|
||||
},
|
||||
{
|
||||
.name = "DAC_DEV",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &glob_conf.pcm_name_out,
|
||||
.descr = "DAC device name (for instance dmix)"
|
||||
},
|
||||
{
|
||||
.name = "ADC_DEV",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &glob_conf.pcm_name_in,
|
||||
.descr = "ADC device name"
|
||||
},
|
||||
{ /* End of list */ }
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops alsa_pcm_ops = {
|
||||
.init_out = alsa_init_out,
|
||||
.fini_out = alsa_fini_out,
|
||||
@@ -1086,6 +1216,7 @@ static struct audio_pcm_ops alsa_pcm_ops = {
|
||||
static struct audio_driver alsa_audio_driver = {
|
||||
.name = "alsa",
|
||||
.descr = "ALSA http://www.alsa-project.org",
|
||||
.options = alsa_options,
|
||||
.init = alsa_audio_init,
|
||||
.fini = alsa_audio_fini,
|
||||
.pcm_ops = &alsa_pcm_ops,
|
||||
|
||||
953
audio/audio.c
953
audio/audio.c
File diff suppressed because it is too large
Load Diff
@@ -26,31 +26,30 @@
|
||||
#define QEMU_AUDIO_H
|
||||
|
||||
#include "qemu/queue.h"
|
||||
#include "qapi/qapi-types-audio.h"
|
||||
|
||||
typedef void (*audio_callback_fn) (void *opaque, int avail);
|
||||
|
||||
typedef enum {
|
||||
AUD_FMT_U8,
|
||||
AUD_FMT_S8,
|
||||
AUD_FMT_U16,
|
||||
AUD_FMT_S16,
|
||||
AUD_FMT_U32,
|
||||
AUD_FMT_S32
|
||||
} audfmt_e;
|
||||
|
||||
#ifdef HOST_WORDS_BIGENDIAN
|
||||
#define AUDIO_HOST_ENDIANNESS 1
|
||||
#else
|
||||
#define AUDIO_HOST_ENDIANNESS 0
|
||||
#endif
|
||||
|
||||
typedef struct audsettings {
|
||||
struct audsettings {
|
||||
int freq;
|
||||
int nchannels;
|
||||
AudioFormat fmt;
|
||||
audfmt_e fmt;
|
||||
int endianness;
|
||||
} audsettings;
|
||||
|
||||
audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo);
|
||||
int audioformat_bytes_per_sample(AudioFormat fmt);
|
||||
int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
|
||||
audsettings *as, int def_usecs);
|
||||
int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
|
||||
audsettings *as, int def_usecs);
|
||||
int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
|
||||
audsettings *as, int def_usecs);
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
AUD_CNOTIFY_ENABLE,
|
||||
@@ -90,6 +89,7 @@ typedef struct QEMUAudioTimeStamp {
|
||||
void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0);
|
||||
void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
|
||||
|
||||
void AUD_help (void);
|
||||
void AUD_register_card (const char *name, QEMUSoundCard *card);
|
||||
void AUD_remove_card (QEMUSoundCard *card);
|
||||
CaptureVoiceOut *AUD_add_capture (
|
||||
@@ -171,8 +171,4 @@ void audio_sample_to_uint64(void *samples, int pos,
|
||||
void audio_sample_from_uint64(void *samples, int pos,
|
||||
uint64_t left, uint64_t right);
|
||||
|
||||
void audio_parse_option(const char *opt);
|
||||
void audio_init_audiodevs(void);
|
||||
void audio_legacy_help(void);
|
||||
|
||||
#endif /* QEMU_AUDIO_H */
|
||||
|
||||
@@ -33,6 +33,22 @@
|
||||
|
||||
struct audio_pcm_ops;
|
||||
|
||||
typedef enum {
|
||||
AUD_OPT_INT,
|
||||
AUD_OPT_FMT,
|
||||
AUD_OPT_STR,
|
||||
AUD_OPT_BOOL
|
||||
} audio_option_tag_e;
|
||||
|
||||
struct audio_option {
|
||||
const char *name;
|
||||
audio_option_tag_e tag;
|
||||
void *valp;
|
||||
const char *descr;
|
||||
int *overriddenp;
|
||||
int overridden;
|
||||
};
|
||||
|
||||
struct audio_callback {
|
||||
void *opaque;
|
||||
audio_callback_fn fn;
|
||||
@@ -129,7 +145,8 @@ typedef struct audio_driver audio_driver;
|
||||
struct audio_driver {
|
||||
const char *name;
|
||||
const char *descr;
|
||||
void *(*init) (Audiodev *);
|
||||
struct audio_option *options;
|
||||
void *(*init) (void);
|
||||
void (*fini) (void *);
|
||||
struct audio_pcm_ops *pcm_ops;
|
||||
int can_be_default;
|
||||
@@ -174,9 +191,8 @@ struct SWVoiceCap {
|
||||
QLIST_ENTRY (SWVoiceCap) entries;
|
||||
};
|
||||
|
||||
typedef struct AudioState {
|
||||
struct AudioState {
|
||||
struct audio_driver *drv;
|
||||
Audiodev *dev;
|
||||
void *drv_opaque;
|
||||
|
||||
QEMUTimer *ts;
|
||||
@@ -187,13 +203,10 @@ typedef struct AudioState {
|
||||
int nb_hw_voices_out;
|
||||
int nb_hw_voices_in;
|
||||
int vm_running;
|
||||
int64_t period_ticks;
|
||||
} AudioState;
|
||||
};
|
||||
|
||||
extern const struct mixeng_volume nominal_volume;
|
||||
|
||||
extern const char *audio_prio_list[];
|
||||
|
||||
void audio_driver_register(audio_driver *drv);
|
||||
audio_driver *audio_driver_lookup(const char *name);
|
||||
|
||||
@@ -235,18 +248,4 @@ static inline int audio_ring_dist (int dst, int src, int len)
|
||||
#define AUDIO_STRINGIFY_(n) #n
|
||||
#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n)
|
||||
|
||||
typedef struct AudiodevListEntry {
|
||||
Audiodev *dev;
|
||||
QSIMPLEQ_ENTRY(AudiodevListEntry) next;
|
||||
} AudiodevListEntry;
|
||||
|
||||
typedef QSIMPLEQ_HEAD(, AudiodevListEntry) AudiodevListHead;
|
||||
AudiodevListHead audio_handle_legacy_opts(void);
|
||||
|
||||
void audio_free_audiodev_list(AudiodevListHead *head);
|
||||
|
||||
void audio_create_pdos(Audiodev *dev);
|
||||
AudiodevPerDirectionOptions *audio_get_pdo_in(Audiodev *dev);
|
||||
AudiodevPerDirectionOptions *audio_get_pdo_out(Audiodev *dev);
|
||||
|
||||
#endif /* QEMU_AUDIO_INT_H */
|
||||
|
||||
@@ -1,549 +0,0 @@
|
||||
/*
|
||||
* QEMU Audio subsystem: legacy configuration handling
|
||||
*
|
||||
* Copyright (c) 2015-2019 Zoltán Kővágó <DirtY.iCE.hu@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "audio.h"
|
||||
#include "audio_int.h"
|
||||
#include "qemu/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);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "audio.h"
|
||||
|
||||
#define AUDIO_CAP "audio-pt"
|
||||
|
||||
@@ -299,42 +299,11 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
|
||||
{
|
||||
switch (dev->driver) {
|
||||
case AUDIODEV_DRIVER_NONE:
|
||||
return dev->u.none.TYPE;
|
||||
case AUDIODEV_DRIVER_ALSA:
|
||||
return qapi_AudiodevAlsaPerDirectionOptions_base(dev->u.alsa.TYPE);
|
||||
case AUDIODEV_DRIVER_COREAUDIO:
|
||||
return qapi_AudiodevCoreaudioPerDirectionOptions_base(
|
||||
dev->u.coreaudio.TYPE);
|
||||
case AUDIODEV_DRIVER_DSOUND:
|
||||
return dev->u.dsound.TYPE;
|
||||
case AUDIODEV_DRIVER_OSS:
|
||||
return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.TYPE);
|
||||
case AUDIODEV_DRIVER_PA:
|
||||
return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE);
|
||||
case AUDIODEV_DRIVER_SDL:
|
||||
return dev->u.sdl.TYPE;
|
||||
case AUDIODEV_DRIVER_SPICE:
|
||||
return dev->u.spice.TYPE;
|
||||
case AUDIODEV_DRIVER_WAV:
|
||||
return dev->u.wav.TYPE;
|
||||
|
||||
case AUDIODEV_DRIVER__MAX:
|
||||
break;
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as)
|
||||
{
|
||||
HW *hw;
|
||||
AudioState *s = &glob_audio_state;
|
||||
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
|
||||
|
||||
if (pdo->fixed_settings) {
|
||||
if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) {
|
||||
hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
|
||||
if (hw) {
|
||||
return hw;
|
||||
@@ -362,11 +331,9 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
|
||||
SW *sw;
|
||||
HW *hw;
|
||||
struct audsettings hw_as;
|
||||
AudioState *s = &glob_audio_state;
|
||||
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
|
||||
|
||||
if (pdo->fixed_settings) {
|
||||
hw_as = audiodev_to_audsettings(pdo);
|
||||
if (glue (conf.fixed_, TYPE).enabled) {
|
||||
hw_as = glue (conf.fixed_, TYPE).settings;
|
||||
}
|
||||
else {
|
||||
hw_as = *as;
|
||||
@@ -431,7 +398,6 @@ SW *glue (AUD_open_, TYPE) (
|
||||
)
|
||||
{
|
||||
AudioState *s = &glob_audio_state;
|
||||
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
|
||||
|
||||
if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
|
||||
dolog ("card=%p name=%p callback_fn=%p as=%p\n",
|
||||
@@ -456,7 +422,7 @@ SW *glue (AUD_open_, TYPE) (
|
||||
return sw;
|
||||
}
|
||||
|
||||
if (!pdo->fixed_settings && sw) {
|
||||
if (!glue (conf.fixed_, TYPE).enabled && sw) {
|
||||
glue (AUD_close_, TYPE) (card, sw);
|
||||
sw = NULL;
|
||||
}
|
||||
|
||||
@@ -24,20 +24,20 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx,
|
||||
wfx->cbSize = 0;
|
||||
|
||||
switch (as->fmt) {
|
||||
case AUDIO_FORMAT_S8:
|
||||
case AUDIO_FORMAT_U8:
|
||||
case AUD_FMT_S8:
|
||||
case AUD_FMT_U8:
|
||||
wfx->wBitsPerSample = 8;
|
||||
break;
|
||||
|
||||
case AUDIO_FORMAT_S16:
|
||||
case AUDIO_FORMAT_U16:
|
||||
case AUD_FMT_S16:
|
||||
case AUD_FMT_U16:
|
||||
wfx->wBitsPerSample = 16;
|
||||
wfx->nAvgBytesPerSec <<= 1;
|
||||
wfx->nBlockAlign <<= 1;
|
||||
break;
|
||||
|
||||
case AUDIO_FORMAT_S32:
|
||||
case AUDIO_FORMAT_U32:
|
||||
case AUD_FMT_S32:
|
||||
case AUD_FMT_U32:
|
||||
wfx->wBitsPerSample = 32;
|
||||
wfx->nAvgBytesPerSec <<= 2;
|
||||
wfx->nBlockAlign <<= 2;
|
||||
@@ -85,15 +85,15 @@ int waveformat_to_audio_settings (WAVEFORMATEX *wfx,
|
||||
|
||||
switch (wfx->wBitsPerSample) {
|
||||
case 8:
|
||||
as->fmt = AUDIO_FORMAT_U8;
|
||||
as->fmt = AUD_FMT_U8;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
as->fmt = AUDIO_FORMAT_S16;
|
||||
as->fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
case 32:
|
||||
as->fmt = AUDIO_FORMAT_S32;
|
||||
as->fmt = AUD_FMT_S32;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#include <pthread.h> /* pthread_X */
|
||||
|
||||
#include "qemu/module.h"
|
||||
#include "qemu-common.h"
|
||||
#include "audio.h"
|
||||
|
||||
#define AUDIO_CAP "coreaudio"
|
||||
@@ -36,6 +36,11 @@
|
||||
#define MAC_OS_X_VERSION_10_6 1060
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int buffer_frames;
|
||||
int nbuffers;
|
||||
} CoreaudioConf;
|
||||
|
||||
typedef struct coreaudioVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
pthread_mutex_t mutex;
|
||||
@@ -502,9 +507,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
int err;
|
||||
const char *typ = "playback";
|
||||
AudioValueRange frameRange;
|
||||
Audiodev *dev = drv_opaque;
|
||||
AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
|
||||
int frames;
|
||||
CoreaudioConf *conf = drv_opaque;
|
||||
|
||||
/* create mutex */
|
||||
err = pthread_mutex_init(&core->mutex, NULL);
|
||||
@@ -535,17 +538,16 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
return -1;
|
||||
}
|
||||
|
||||
frames = audio_buffer_frames(
|
||||
qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
|
||||
if (frameRange.mMinimum > frames) {
|
||||
if (frameRange.mMinimum > conf->buffer_frames) {
|
||||
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
|
||||
dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
|
||||
} else if (frameRange.mMaximum < frames) {
|
||||
}
|
||||
else if (frameRange.mMaximum < conf->buffer_frames) {
|
||||
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
|
||||
dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
|
||||
}
|
||||
else {
|
||||
core->audioDevicePropertyBufferFrameSize = frames;
|
||||
core->audioDevicePropertyBufferFrameSize = conf->buffer_frames;
|
||||
}
|
||||
|
||||
/* set Buffer Frame Size */
|
||||
@@ -566,8 +568,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
"Could not get device buffer frame size\n");
|
||||
return -1;
|
||||
}
|
||||
hw->samples = (cpdo->has_buffer_count ? cpdo->buffer_count : 4) *
|
||||
core->audioDevicePropertyBufferFrameSize;
|
||||
hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
|
||||
|
||||
/* get StreamFormat */
|
||||
status = coreaudio_get_streamformat(core->outputDeviceID,
|
||||
@@ -679,15 +680,40 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *coreaudio_audio_init(Audiodev *dev)
|
||||
static CoreaudioConf glob_conf = {
|
||||
.buffer_frames = 512,
|
||||
.nbuffers = 4,
|
||||
};
|
||||
|
||||
static void *coreaudio_audio_init (void)
|
||||
{
|
||||
return dev;
|
||||
CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf));
|
||||
*conf = glob_conf;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
static void coreaudio_audio_fini (void *opaque)
|
||||
{
|
||||
g_free(opaque);
|
||||
}
|
||||
|
||||
static struct audio_option coreaudio_options[] = {
|
||||
{
|
||||
.name = "BUFFER_SIZE",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &glob_conf.buffer_frames,
|
||||
.descr = "Size of the buffer in frames"
|
||||
},
|
||||
{
|
||||
.name = "BUFFER_COUNT",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &glob_conf.nbuffers,
|
||||
.descr = "Number of buffers"
|
||||
},
|
||||
{ /* End of list */ }
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops coreaudio_pcm_ops = {
|
||||
.init_out = coreaudio_init_out,
|
||||
.fini_out = coreaudio_fini_out,
|
||||
@@ -699,6 +725,7 @@ static struct audio_pcm_ops coreaudio_pcm_ops = {
|
||||
static struct audio_driver coreaudio_audio_driver = {
|
||||
.name = "coreaudio",
|
||||
.descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
|
||||
.options = coreaudio_options,
|
||||
.init = coreaudio_audio_init,
|
||||
.fini = coreaudio_audio_fini,
|
||||
.pcm_ops = &coreaudio_pcm_ops,
|
||||
|
||||
@@ -167,18 +167,17 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
dsound *s = drv_opaque;
|
||||
WAVEFORMATEX wfx;
|
||||
struct audsettings obt_as;
|
||||
DSoundConf *conf = &s->conf;
|
||||
#ifdef DSBTYPE_IN
|
||||
const char *typ = "ADC";
|
||||
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
|
||||
DSCBUFFERDESC bd;
|
||||
DSCBCAPS bc;
|
||||
AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.in;
|
||||
#else
|
||||
const char *typ = "DAC";
|
||||
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
|
||||
DSBUFFERDESC bd;
|
||||
DSBCAPS bc;
|
||||
AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.out;
|
||||
#endif
|
||||
|
||||
if (!s->FIELD2) {
|
||||
@@ -194,8 +193,8 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
memset (&bd, 0, sizeof (bd));
|
||||
bd.dwSize = sizeof (bd);
|
||||
bd.lpwfxFormat = &wfx;
|
||||
bd.dwBufferBytes = audio_buffer_bytes(pdo, as, 92880);
|
||||
#ifdef DSBTYPE_IN
|
||||
bd.dwBufferBytes = conf->bufsize_in;
|
||||
hr = IDirectSoundCapture_CreateCaptureBuffer (
|
||||
s->dsound_capture,
|
||||
&bd,
|
||||
@@ -204,6 +203,7 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
);
|
||||
#else
|
||||
bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
|
||||
bd.dwBufferBytes = conf->bufsize_out;
|
||||
hr = IDirectSound_CreateSoundBuffer (
|
||||
s->dsound,
|
||||
&bd,
|
||||
|
||||
@@ -27,12 +27,11 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "audio.h"
|
||||
|
||||
#define AUDIO_CAP "dsound"
|
||||
#include "audio_int.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
@@ -43,11 +42,17 @@
|
||||
|
||||
/* #define DEBUG_DSOUND */
|
||||
|
||||
typedef struct {
|
||||
int bufsize_in;
|
||||
int bufsize_out;
|
||||
int latency_millis;
|
||||
} DSoundConf;
|
||||
|
||||
typedef struct {
|
||||
LPDIRECTSOUND dsound;
|
||||
LPDIRECTSOUNDCAPTURE dsound_capture;
|
||||
struct audsettings settings;
|
||||
Audiodev *dev;
|
||||
DSoundConf conf;
|
||||
} dsound;
|
||||
|
||||
typedef struct {
|
||||
@@ -243,9 +248,9 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
|
||||
dsound_log_hresult (hr);
|
||||
}
|
||||
|
||||
static uint64_t usecs_to_bytes(struct audio_pcm_info *info, uint32_t usecs)
|
||||
static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis)
|
||||
{
|
||||
return muldiv64(usecs, info->bytes_per_second, 1000000);
|
||||
return (millis * info->bytes_per_second) / 1000;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DSOUND
|
||||
@@ -473,7 +478,7 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
|
||||
LPVOID p1, p2;
|
||||
int bufsize;
|
||||
dsound *s = ds->s;
|
||||
AudiodevDsoundOptions *dso = &s->dev->u.dsound;
|
||||
DSoundConf *conf = &s->conf;
|
||||
|
||||
if (!dsb) {
|
||||
dolog ("Attempt to run empty with playback buffer\n");
|
||||
@@ -496,14 +501,14 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
|
||||
len = live << hwshift;
|
||||
|
||||
if (ds->first_time) {
|
||||
if (dso->latency) {
|
||||
if (conf->latency_millis) {
|
||||
DWORD cur_blat;
|
||||
|
||||
cur_blat = audio_ring_dist (wpos, ppos, bufsize);
|
||||
ds->first_time = 0;
|
||||
old_pos = wpos;
|
||||
old_pos +=
|
||||
usecs_to_bytes(&hw->info, dso->latency) - cur_blat;
|
||||
millis_to_bytes (&hw->info, conf->latency_millis) - cur_blat;
|
||||
old_pos %= bufsize;
|
||||
old_pos &= ~hw->info.align;
|
||||
}
|
||||
@@ -742,6 +747,12 @@ static int dsound_run_in (HWVoiceIn *hw)
|
||||
return decr;
|
||||
}
|
||||
|
||||
static DSoundConf glob_conf = {
|
||||
.bufsize_in = 16384,
|
||||
.bufsize_out = 16384,
|
||||
.latency_millis = 10
|
||||
};
|
||||
|
||||
static void dsound_audio_fini (void *opaque)
|
||||
{
|
||||
HRESULT hr;
|
||||
@@ -772,22 +783,13 @@ static void dsound_audio_fini (void *opaque)
|
||||
g_free(s);
|
||||
}
|
||||
|
||||
static void *dsound_audio_init(Audiodev *dev)
|
||||
static void *dsound_audio_init (void)
|
||||
{
|
||||
int err;
|
||||
HRESULT hr;
|
||||
dsound *s = g_malloc0(sizeof(dsound));
|
||||
AudiodevDsoundOptions *dso;
|
||||
|
||||
assert(dev->driver == AUDIODEV_DRIVER_DSOUND);
|
||||
s->dev = dev;
|
||||
dso = &dev->u.dsound;
|
||||
|
||||
if (!dso->has_latency) {
|
||||
dso->has_latency = true;
|
||||
dso->latency = 10000; /* 10 ms */
|
||||
}
|
||||
|
||||
s->conf = glob_conf;
|
||||
hr = CoInitialize (NULL);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not initialize COM\n");
|
||||
@@ -852,6 +854,28 @@ static void *dsound_audio_init(Audiodev *dev)
|
||||
return s;
|
||||
}
|
||||
|
||||
static struct audio_option dsound_options[] = {
|
||||
{
|
||||
.name = "LATENCY_MILLIS",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &glob_conf.latency_millis,
|
||||
.descr = "(undocumented)"
|
||||
},
|
||||
{
|
||||
.name = "BUFSIZE_OUT",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &glob_conf.bufsize_out,
|
||||
.descr = "(undocumented)"
|
||||
},
|
||||
{
|
||||
.name = "BUFSIZE_IN",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &glob_conf.bufsize_in,
|
||||
.descr = "(undocumented)"
|
||||
},
|
||||
{ /* End of list */ }
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops dsound_pcm_ops = {
|
||||
.init_out = dsound_init_out,
|
||||
.fini_out = dsound_fini_out,
|
||||
@@ -869,6 +893,7 @@ static struct audio_pcm_ops dsound_pcm_ops = {
|
||||
static struct audio_driver dsound_audio_driver = {
|
||||
.name = "dsound",
|
||||
.descr = "DirectSound http://wikipedia.org/wiki/DirectSound",
|
||||
.options = dsound_options,
|
||||
.init = dsound_audio_init,
|
||||
.fini = dsound_audio_fini,
|
||||
.pcm_ops = &dsound_pcm_ops,
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "audio.h"
|
||||
|
||||
@@ -21,10 +21,9 @@
|
||||
* 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 "qemu/host-utils.h"
|
||||
#include "qemu/module.h"
|
||||
#include "audio.h"
|
||||
#include "qemu/timer.h"
|
||||
|
||||
@@ -137,7 +136,7 @@ static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *no_audio_init(Audiodev *dev)
|
||||
static void *no_audio_init (void)
|
||||
{
|
||||
return &no_audio_init;
|
||||
}
|
||||
@@ -164,6 +163,7 @@ static struct audio_pcm_ops no_pcm_ops = {
|
||||
static struct audio_driver no_audio_driver = {
|
||||
.name = "none",
|
||||
.descr = "Timer based audio emulation",
|
||||
.options = NULL,
|
||||
.init = no_audio_init,
|
||||
.fini = no_audio_fini,
|
||||
.pcm_ops = &no_pcm_ops,
|
||||
|
||||
196
audio/ossaudio.c
196
audio/ossaudio.c
@@ -21,12 +21,11 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/soundcard.h>
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "audio.h"
|
||||
#include "trace.h"
|
||||
@@ -38,6 +37,16 @@
|
||||
#define USE_DSP_POLICY
|
||||
#endif
|
||||
|
||||
typedef struct OSSConf {
|
||||
int try_mmap;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
const char *devpath_out;
|
||||
const char *devpath_in;
|
||||
int exclusive;
|
||||
int policy;
|
||||
} OSSConf;
|
||||
|
||||
typedef struct OSSVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
void *pcm_buf;
|
||||
@@ -47,7 +56,7 @@ typedef struct OSSVoiceOut {
|
||||
int fragsize;
|
||||
int mmapped;
|
||||
int pending;
|
||||
Audiodev *dev;
|
||||
OSSConf *conf;
|
||||
} OSSVoiceOut;
|
||||
|
||||
typedef struct OSSVoiceIn {
|
||||
@@ -56,12 +65,12 @@ typedef struct OSSVoiceIn {
|
||||
int fd;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
Audiodev *dev;
|
||||
OSSConf *conf;
|
||||
} OSSVoiceIn;
|
||||
|
||||
struct oss_params {
|
||||
int freq;
|
||||
int fmt;
|
||||
audfmt_e fmt;
|
||||
int nchannels;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
@@ -139,16 +148,16 @@ static int oss_write (SWVoiceOut *sw, void *buf, int len)
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int aud_to_ossfmt (AudioFormat fmt, int endianness)
|
||||
static int aud_to_ossfmt (audfmt_e fmt, int endianness)
|
||||
{
|
||||
switch (fmt) {
|
||||
case AUDIO_FORMAT_S8:
|
||||
case AUD_FMT_S8:
|
||||
return AFMT_S8;
|
||||
|
||||
case AUDIO_FORMAT_U8:
|
||||
case AUD_FMT_U8:
|
||||
return AFMT_U8;
|
||||
|
||||
case AUDIO_FORMAT_S16:
|
||||
case AUD_FMT_S16:
|
||||
if (endianness) {
|
||||
return AFMT_S16_BE;
|
||||
}
|
||||
@@ -156,7 +165,7 @@ static int aud_to_ossfmt (AudioFormat fmt, int endianness)
|
||||
return AFMT_S16_LE;
|
||||
}
|
||||
|
||||
case AUDIO_FORMAT_U16:
|
||||
case AUD_FMT_U16:
|
||||
if (endianness) {
|
||||
return AFMT_U16_BE;
|
||||
}
|
||||
@@ -173,37 +182,37 @@ static int aud_to_ossfmt (AudioFormat fmt, int endianness)
|
||||
}
|
||||
}
|
||||
|
||||
static int oss_to_audfmt (int ossfmt, AudioFormat *fmt, int *endianness)
|
||||
static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
|
||||
{
|
||||
switch (ossfmt) {
|
||||
case AFMT_S8:
|
||||
*endianness = 0;
|
||||
*fmt = AUDIO_FORMAT_S8;
|
||||
*fmt = AUD_FMT_S8;
|
||||
break;
|
||||
|
||||
case AFMT_U8:
|
||||
*endianness = 0;
|
||||
*fmt = AUDIO_FORMAT_U8;
|
||||
*fmt = AUD_FMT_U8;
|
||||
break;
|
||||
|
||||
case AFMT_S16_LE:
|
||||
*endianness = 0;
|
||||
*fmt = AUDIO_FORMAT_S16;
|
||||
*fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
case AFMT_U16_LE:
|
||||
*endianness = 0;
|
||||
*fmt = AUDIO_FORMAT_U16;
|
||||
*fmt = AUD_FMT_U16;
|
||||
break;
|
||||
|
||||
case AFMT_S16_BE:
|
||||
*endianness = 1;
|
||||
*fmt = AUDIO_FORMAT_S16;
|
||||
*fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
case AFMT_U16_BE:
|
||||
*endianness = 1;
|
||||
*fmt = AUDIO_FORMAT_U16;
|
||||
*fmt = AUD_FMT_U16;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -253,25 +262,19 @@ static int oss_get_version (int fd, int *version, const char *typ)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int oss_open(int in, struct oss_params *req, audsettings *as,
|
||||
struct oss_params *obt, int *pfd, Audiodev *dev)
|
||||
static int oss_open (int in, struct oss_params *req,
|
||||
struct oss_params *obt, int *pfd, OSSConf* conf)
|
||||
{
|
||||
AudiodevOssOptions *oopts = &dev->u.oss;
|
||||
AudiodevOssPerDirectionOptions *opdo = in ? oopts->in : oopts->out;
|
||||
int fd;
|
||||
int oflags = (oopts->has_exclusive && oopts->exclusive) ? O_EXCL : 0;
|
||||
int oflags = conf->exclusive ? O_EXCL : 0;
|
||||
audio_buf_info abinfo;
|
||||
int fmt, freq, nchannels;
|
||||
int setfragment = 1;
|
||||
const char *dspname = opdo->has_dev ? opdo->dev : "/dev/dsp";
|
||||
const char *dspname = in ? conf->devpath_in : conf->devpath_out;
|
||||
const char *typ = in ? "ADC" : "DAC";
|
||||
#ifdef USE_DSP_POLICY
|
||||
int policy = oopts->has_dsp_policy ? oopts->dsp_policy : 5;
|
||||
#endif
|
||||
|
||||
/* Kludge needed to have working mmap on Linux */
|
||||
oflags |= (oopts->has_try_mmap && oopts->try_mmap) ?
|
||||
O_RDWR : (in ? O_RDONLY : O_WRONLY);
|
||||
oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
|
||||
|
||||
fd = open (dspname, oflags | O_NONBLOCK);
|
||||
if (-1 == fd) {
|
||||
@@ -282,9 +285,6 @@ static int oss_open(int in, struct oss_params *req, audsettings *as,
|
||||
freq = req->freq;
|
||||
nchannels = req->nchannels;
|
||||
fmt = req->fmt;
|
||||
req->nfrags = opdo->has_buffer_count ? opdo->buffer_count : 4;
|
||||
req->fragsize = audio_buffer_bytes(
|
||||
qapi_AudiodevOssPerDirectionOptions_base(opdo), as, 23220);
|
||||
|
||||
if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
|
||||
oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
|
||||
@@ -308,18 +308,18 @@ static int oss_open(int in, struct oss_params *req, audsettings *as,
|
||||
}
|
||||
|
||||
#ifdef USE_DSP_POLICY
|
||||
if (policy >= 0) {
|
||||
if (conf->policy >= 0) {
|
||||
int version;
|
||||
|
||||
if (!oss_get_version (fd, &version, typ)) {
|
||||
trace_oss_version(version);
|
||||
|
||||
if (version >= 0x040000) {
|
||||
int policy2 = policy;
|
||||
if (ioctl(fd, SNDCTL_DSP_POLICY, &policy2)) {
|
||||
int policy = conf->policy;
|
||||
if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
|
||||
oss_logerr2 (errno, typ,
|
||||
"Failed to set timing policy to %d\n",
|
||||
policy);
|
||||
conf->policy);
|
||||
goto err;
|
||||
}
|
||||
setfragment = 0;
|
||||
@@ -500,18 +500,19 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
int endianness;
|
||||
int err;
|
||||
int fd;
|
||||
AudioFormat effective_fmt;
|
||||
audfmt_e effective_fmt;
|
||||
struct audsettings obt_as;
|
||||
Audiodev *dev = drv_opaque;
|
||||
AudiodevOssOptions *oopts = &dev->u.oss;
|
||||
OSSConf *conf = drv_opaque;
|
||||
|
||||
oss->fd = -1;
|
||||
|
||||
req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
|
||||
req.freq = as->freq;
|
||||
req.nchannels = as->nchannels;
|
||||
req.fragsize = conf->fragsize;
|
||||
req.nfrags = conf->nfrags;
|
||||
|
||||
if (oss_open(0, &req, as, &obt, &fd, dev)) {
|
||||
if (oss_open (0, &req, &obt, &fd, conf)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -538,7 +539,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
|
||||
|
||||
oss->mmapped = 0;
|
||||
if (oopts->has_try_mmap && oopts->try_mmap) {
|
||||
if (conf->try_mmap) {
|
||||
oss->pcm_buf = mmap (
|
||||
NULL,
|
||||
hw->samples << hw->info.shift,
|
||||
@@ -596,7 +597,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
}
|
||||
|
||||
oss->fd = fd;
|
||||
oss->dev = dev;
|
||||
oss->conf = conf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -604,12 +605,16 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
int trig;
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
{
|
||||
bool poll_mode = opdo->try_poll;
|
||||
va_list ap;
|
||||
int poll_mode;
|
||||
|
||||
va_start (ap, cmd);
|
||||
poll_mode = va_arg (ap, int);
|
||||
va_end (ap);
|
||||
|
||||
ldebug ("enabling voice\n");
|
||||
if (poll_mode) {
|
||||
@@ -662,16 +667,18 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
int endianness;
|
||||
int err;
|
||||
int fd;
|
||||
AudioFormat effective_fmt;
|
||||
audfmt_e effective_fmt;
|
||||
struct audsettings obt_as;
|
||||
Audiodev *dev = drv_opaque;
|
||||
OSSConf *conf = drv_opaque;
|
||||
|
||||
oss->fd = -1;
|
||||
|
||||
req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
|
||||
req.freq = as->freq;
|
||||
req.nchannels = as->nchannels;
|
||||
if (oss_open(1, &req, as, &obt, &fd, dev)) {
|
||||
req.fragsize = conf->fragsize;
|
||||
req.nfrags = conf->nfrags;
|
||||
if (oss_open (1, &req, &obt, &fd, conf)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -705,7 +712,7 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
}
|
||||
|
||||
oss->fd = fd;
|
||||
oss->dev = dev;
|
||||
oss->conf = conf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -796,12 +803,16 @@ static int oss_read (SWVoiceIn *sw, void *buf, int size)
|
||||
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
{
|
||||
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
||||
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
{
|
||||
bool poll_mode = opdo->try_poll;
|
||||
va_list ap;
|
||||
int poll_mode;
|
||||
|
||||
va_start (ap, cmd);
|
||||
poll_mode = va_arg (ap, int);
|
||||
va_end (ap);
|
||||
|
||||
if (poll_mode) {
|
||||
oss_poll_in (hw);
|
||||
@@ -821,36 +832,82 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void oss_init_per_direction(AudiodevOssPerDirectionOptions *opdo)
|
||||
static OSSConf glob_conf = {
|
||||
.try_mmap = 0,
|
||||
.nfrags = 4,
|
||||
.fragsize = 4096,
|
||||
.devpath_out = "/dev/dsp",
|
||||
.devpath_in = "/dev/dsp",
|
||||
.exclusive = 0,
|
||||
.policy = 5
|
||||
};
|
||||
|
||||
static void *oss_audio_init (void)
|
||||
{
|
||||
if (!opdo->has_try_poll) {
|
||||
opdo->try_poll = true;
|
||||
opdo->has_try_poll = true;
|
||||
}
|
||||
}
|
||||
OSSConf *conf = g_malloc(sizeof(OSSConf));
|
||||
*conf = glob_conf;
|
||||
|
||||
static void *oss_audio_init(Audiodev *dev)
|
||||
{
|
||||
AudiodevOssOptions *oopts;
|
||||
assert(dev->driver == AUDIODEV_DRIVER_OSS);
|
||||
|
||||
oopts = &dev->u.oss;
|
||||
oss_init_per_direction(oopts->in);
|
||||
oss_init_per_direction(oopts->out);
|
||||
|
||||
if (access(oopts->in->has_dev ? oopts->in->dev : "/dev/dsp",
|
||||
R_OK | W_OK) < 0 ||
|
||||
access(oopts->out->has_dev ? oopts->out->dev : "/dev/dsp",
|
||||
R_OK | W_OK) < 0) {
|
||||
if (access(conf->devpath_in, R_OK | W_OK) < 0 ||
|
||||
access(conf->devpath_out, R_OK | W_OK) < 0) {
|
||||
g_free(conf);
|
||||
return NULL;
|
||||
}
|
||||
return dev;
|
||||
return conf;
|
||||
}
|
||||
|
||||
static void oss_audio_fini (void *opaque)
|
||||
{
|
||||
g_free(opaque);
|
||||
}
|
||||
|
||||
static struct audio_option oss_options[] = {
|
||||
{
|
||||
.name = "FRAGSIZE",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &glob_conf.fragsize,
|
||||
.descr = "Fragment size in bytes"
|
||||
},
|
||||
{
|
||||
.name = "NFRAGS",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &glob_conf.nfrags,
|
||||
.descr = "Number of fragments"
|
||||
},
|
||||
{
|
||||
.name = "MMAP",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &glob_conf.try_mmap,
|
||||
.descr = "Try using memory mapped access"
|
||||
},
|
||||
{
|
||||
.name = "DAC_DEV",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &glob_conf.devpath_out,
|
||||
.descr = "Path to DAC device"
|
||||
},
|
||||
{
|
||||
.name = "ADC_DEV",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &glob_conf.devpath_in,
|
||||
.descr = "Path to ADC device"
|
||||
},
|
||||
{
|
||||
.name = "EXCLUSIVE",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &glob_conf.exclusive,
|
||||
.descr = "Open device in exclusive mode (vmix won't work)"
|
||||
},
|
||||
#ifdef USE_DSP_POLICY
|
||||
{
|
||||
.name = "POLICY",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &glob_conf.policy,
|
||||
.descr = "Set the timing policy of the device, -1 to use fragment mode",
|
||||
},
|
||||
#endif
|
||||
{ /* End of list */ }
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops oss_pcm_ops = {
|
||||
.init_out = oss_init_out,
|
||||
.fini_out = oss_fini_out,
|
||||
@@ -868,6 +925,7 @@ static struct audio_pcm_ops oss_pcm_ops = {
|
||||
static struct audio_driver oss_audio_driver = {
|
||||
.name = "oss",
|
||||
.descr = "OSS http://www.opensound.com",
|
||||
.options = oss_options,
|
||||
.init = oss_audio_init,
|
||||
.fini = oss_audio_fini,
|
||||
.pcm_ops = &oss_pcm_ops,
|
||||
|
||||
167
audio/paaudio.c
167
audio/paaudio.c
@@ -1,9 +1,7 @@
|
||||
/* public domain */
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu-common.h"
|
||||
#include "audio.h"
|
||||
#include "qapi/opts-visitor.h"
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
@@ -12,7 +10,14 @@
|
||||
#include "audio_pt_int.h"
|
||||
|
||||
typedef struct {
|
||||
Audiodev *dev;
|
||||
int samples;
|
||||
char *server;
|
||||
char *sink;
|
||||
char *source;
|
||||
} PAConf;
|
||||
|
||||
typedef struct {
|
||||
PAConf conf;
|
||||
pa_threaded_mainloop *mainloop;
|
||||
pa_context *context;
|
||||
} paaudio;
|
||||
@@ -27,7 +32,6 @@ typedef struct {
|
||||
void *pcm_buf;
|
||||
struct audio_pt pt;
|
||||
paaudio *g;
|
||||
int samples;
|
||||
} PAVoiceOut;
|
||||
|
||||
typedef struct {
|
||||
@@ -42,7 +46,6 @@ typedef struct {
|
||||
const void *read_data;
|
||||
size_t read_index, read_length;
|
||||
paaudio *g;
|
||||
int samples;
|
||||
} PAVoiceIn;
|
||||
|
||||
static void qpa_audio_fini(void *opaque);
|
||||
@@ -224,7 +227,7 @@ static void *qpa_thread_out (void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
decr = to_mix = audio_MIN(pa->live, pa->samples >> 5);
|
||||
decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2);
|
||||
rpos = pa->rpos;
|
||||
|
||||
if (audio_pt_unlock(&pa->pt, __func__)) {
|
||||
@@ -316,7 +319,7 @@ static void *qpa_thread_in (void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
incr = to_grab = audio_MIN(pa->dead, pa->samples >> 5);
|
||||
incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2);
|
||||
wpos = pa->wpos;
|
||||
|
||||
if (audio_pt_unlock(&pa->pt, __func__)) {
|
||||
@@ -382,21 +385,21 @@ static int qpa_read (SWVoiceIn *sw, void *buf, int len)
|
||||
return audio_pcm_sw_read (sw, buf, len);
|
||||
}
|
||||
|
||||
static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
|
||||
static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
|
||||
{
|
||||
int format;
|
||||
|
||||
switch (afmt) {
|
||||
case AUDIO_FORMAT_S8:
|
||||
case AUDIO_FORMAT_U8:
|
||||
case AUD_FMT_S8:
|
||||
case AUD_FMT_U8:
|
||||
format = PA_SAMPLE_U8;
|
||||
break;
|
||||
case AUDIO_FORMAT_S16:
|
||||
case AUDIO_FORMAT_U16:
|
||||
case AUD_FMT_S16:
|
||||
case AUD_FMT_U16:
|
||||
format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
|
||||
break;
|
||||
case AUDIO_FORMAT_S32:
|
||||
case AUDIO_FORMAT_U32:
|
||||
case AUD_FMT_S32:
|
||||
case AUD_FMT_U32:
|
||||
format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
|
||||
break;
|
||||
default:
|
||||
@@ -407,26 +410,26 @@ static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
|
||||
return format;
|
||||
}
|
||||
|
||||
static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
|
||||
static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
|
||||
{
|
||||
switch (fmt) {
|
||||
case PA_SAMPLE_U8:
|
||||
return AUDIO_FORMAT_U8;
|
||||
return AUD_FMT_U8;
|
||||
case PA_SAMPLE_S16BE:
|
||||
*endianness = 1;
|
||||
return AUDIO_FORMAT_S16;
|
||||
return AUD_FMT_S16;
|
||||
case PA_SAMPLE_S16LE:
|
||||
*endianness = 0;
|
||||
return AUDIO_FORMAT_S16;
|
||||
return AUD_FMT_S16;
|
||||
case PA_SAMPLE_S32BE:
|
||||
*endianness = 1;
|
||||
return AUDIO_FORMAT_S32;
|
||||
return AUD_FMT_S32;
|
||||
case PA_SAMPLE_S32LE:
|
||||
*endianness = 0;
|
||||
return AUDIO_FORMAT_S32;
|
||||
return AUD_FMT_S32;
|
||||
default:
|
||||
dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
|
||||
return AUDIO_FORMAT_U8;
|
||||
return AUD_FMT_U8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,15 +546,17 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
struct audsettings obt_as = *as;
|
||||
PAVoiceOut *pa = (PAVoiceOut *) hw;
|
||||
paaudio *g = pa->g = drv_opaque;
|
||||
AudiodevPaOptions *popts = &g->dev->u.pa;
|
||||
AudiodevPaPerDirectionOptions *ppdo = popts->out;
|
||||
|
||||
ss.format = audfmt_to_pa (as->fmt, as->endianness);
|
||||
ss.channels = as->nchannels;
|
||||
ss.rate = as->freq;
|
||||
|
||||
ba.tlength = pa_usec_to_bytes(ppdo->latency, &ss);
|
||||
ba.minreq = -1;
|
||||
/*
|
||||
* qemu audio tick runs at 100 Hz (by default), so processing
|
||||
* data chunks worth 10 ms of sound should be a good fit.
|
||||
*/
|
||||
ba.tlength = pa_usec_to_bytes (10 * 1000, &ss);
|
||||
ba.minreq = pa_usec_to_bytes (5 * 1000, &ss);
|
||||
ba.maxlength = -1;
|
||||
ba.prebuf = -1;
|
||||
|
||||
@@ -561,7 +566,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
g,
|
||||
"qemu",
|
||||
PA_STREAM_PLAYBACK,
|
||||
ppdo->has_name ? ppdo->name : NULL,
|
||||
g->conf.sink,
|
||||
&ss,
|
||||
NULL, /* channel map */
|
||||
&ba, /* buffering attributes */
|
||||
@@ -573,9 +578,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
}
|
||||
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
hw->samples = pa->samples = audio_buffer_samples(
|
||||
qapi_AudiodevPaPerDirectionOptions_base(ppdo),
|
||||
&obt_as, ppdo->buffer_length);
|
||||
hw->samples = g->conf.samples;
|
||||
pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
|
||||
pa->rpos = hw->rpos;
|
||||
if (!pa->pcm_buf) {
|
||||
@@ -606,32 +609,24 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
{
|
||||
int error;
|
||||
pa_sample_spec ss;
|
||||
pa_buffer_attr ba;
|
||||
struct audsettings obt_as = *as;
|
||||
PAVoiceIn *pa = (PAVoiceIn *) hw;
|
||||
paaudio *g = pa->g = drv_opaque;
|
||||
AudiodevPaOptions *popts = &g->dev->u.pa;
|
||||
AudiodevPaPerDirectionOptions *ppdo = popts->in;
|
||||
|
||||
ss.format = audfmt_to_pa (as->fmt, as->endianness);
|
||||
ss.channels = as->nchannels;
|
||||
ss.rate = as->freq;
|
||||
|
||||
ba.fragsize = pa_usec_to_bytes(ppdo->latency, &ss);
|
||||
ba.maxlength = pa_usec_to_bytes(ppdo->latency * 2, &ss);
|
||||
ba.minreq = -1;
|
||||
ba.prebuf = -1;
|
||||
|
||||
obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
|
||||
|
||||
pa->stream = qpa_simple_new (
|
||||
g,
|
||||
"qemu",
|
||||
PA_STREAM_RECORD,
|
||||
ppdo->has_name ? ppdo->name : NULL,
|
||||
g->conf.source,
|
||||
&ss,
|
||||
NULL, /* channel map */
|
||||
&ba, /* buffering attributes */
|
||||
NULL, /* buffering attributes */
|
||||
&error
|
||||
);
|
||||
if (!pa->stream) {
|
||||
@@ -640,9 +635,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
}
|
||||
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
hw->samples = pa->samples = audio_buffer_samples(
|
||||
qapi_AudiodevPaPerDirectionOptions_base(ppdo),
|
||||
&obt_as, ppdo->buffer_length);
|
||||
hw->samples = g->conf.samples;
|
||||
pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
|
||||
pa->wpos = hw->wpos;
|
||||
if (!pa->pcm_buf) {
|
||||
@@ -814,54 +807,15 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qpa_validate_per_direction_opts(Audiodev *dev,
|
||||
AudiodevPaPerDirectionOptions *pdo)
|
||||
/* common */
|
||||
static PAConf glob_conf = {
|
||||
.samples = 4096,
|
||||
};
|
||||
|
||||
static void *qpa_audio_init (void)
|
||||
{
|
||||
if (!pdo->has_buffer_length) {
|
||||
pdo->has_buffer_length = true;
|
||||
pdo->buffer_length = 46440;
|
||||
}
|
||||
if (!pdo->has_latency) {
|
||||
pdo->has_latency = true;
|
||||
pdo->latency = 15000;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void *qpa_audio_init(Audiodev *dev)
|
||||
{
|
||||
paaudio *g;
|
||||
AudiodevPaOptions *popts = &dev->u.pa;
|
||||
const char *server;
|
||||
|
||||
if (!popts->has_server) {
|
||||
char pidfile[64];
|
||||
char *runtime;
|
||||
struct stat st;
|
||||
|
||||
runtime = getenv("XDG_RUNTIME_DIR");
|
||||
if (!runtime) {
|
||||
return NULL;
|
||||
}
|
||||
snprintf(pidfile, sizeof(pidfile), "%s/pulse/pid", runtime);
|
||||
if (stat(pidfile, &st) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
assert(dev->driver == AUDIODEV_DRIVER_PA);
|
||||
|
||||
g = g_malloc(sizeof(paaudio));
|
||||
server = popts->has_server ? popts->server : NULL;
|
||||
|
||||
if (!qpa_validate_per_direction_opts(dev, popts->in)) {
|
||||
goto fail;
|
||||
}
|
||||
if (!qpa_validate_per_direction_opts(dev, popts->out)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
g->dev = dev;
|
||||
paaudio *g = g_malloc(sizeof(paaudio));
|
||||
g->conf = glob_conf;
|
||||
g->mainloop = NULL;
|
||||
g->context = NULL;
|
||||
|
||||
@@ -871,14 +825,14 @@ static void *qpa_audio_init(Audiodev *dev)
|
||||
}
|
||||
|
||||
g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop),
|
||||
server);
|
||||
g->conf.server);
|
||||
if (!g->context) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pa_context_set_state_callback (g->context, context_state_cb, g);
|
||||
|
||||
if (pa_context_connect(g->context, server, 0, NULL) < 0) {
|
||||
if (pa_context_connect (g->context, g->conf.server, 0, NULL) < 0) {
|
||||
qpa_logerr (pa_context_errno (g->context),
|
||||
"pa_context_connect() failed\n");
|
||||
goto fail;
|
||||
@@ -941,6 +895,34 @@ static void qpa_audio_fini (void *opaque)
|
||||
g_free(g);
|
||||
}
|
||||
|
||||
struct audio_option qpa_options[] = {
|
||||
{
|
||||
.name = "SAMPLES",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &glob_conf.samples,
|
||||
.descr = "buffer size in samples"
|
||||
},
|
||||
{
|
||||
.name = "SERVER",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &glob_conf.server,
|
||||
.descr = "server address"
|
||||
},
|
||||
{
|
||||
.name = "SINK",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &glob_conf.sink,
|
||||
.descr = "sink device name"
|
||||
},
|
||||
{
|
||||
.name = "SOURCE",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &glob_conf.source,
|
||||
.descr = "source device name"
|
||||
},
|
||||
{ /* End of list */ }
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops qpa_pcm_ops = {
|
||||
.init_out = qpa_init_out,
|
||||
.fini_out = qpa_fini_out,
|
||||
@@ -958,6 +940,7 @@ static struct audio_pcm_ops qpa_pcm_ops = {
|
||||
static struct audio_driver pa_audio_driver = {
|
||||
.name = "pa",
|
||||
.descr = "http://www.pulseaudio.org/",
|
||||
.options = qpa_options,
|
||||
.init = qpa_audio_init,
|
||||
.fini = qpa_audio_fini,
|
||||
.pcm_ops = &qpa_pcm_ops,
|
||||
|
||||
239
audio/sdlaudio.c
239
audio/sdlaudio.c
@@ -21,11 +21,10 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <SDL.h>
|
||||
#include <SDL_thread.h>
|
||||
#include "qemu/module.h"
|
||||
#include "qemu-common.h"
|
||||
#include "audio.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
@@ -39,17 +38,31 @@
|
||||
#define AUDIO_CAP "sdl"
|
||||
#include "audio_int.h"
|
||||
|
||||
#define USE_SEMAPHORE (SDL_MAJOR_VERSION < 2)
|
||||
|
||||
typedef struct SDLVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
int live;
|
||||
#if USE_SEMAPHORE
|
||||
int rpos;
|
||||
#endif
|
||||
int decr;
|
||||
} SDLVoiceOut;
|
||||
|
||||
static struct {
|
||||
int nb_samples;
|
||||
} conf = {
|
||||
.nb_samples = 1024
|
||||
};
|
||||
|
||||
static struct SDLAudioState {
|
||||
int exit;
|
||||
#if USE_SEMAPHORE
|
||||
SDL_mutex *mutex;
|
||||
SDL_sem *sem;
|
||||
#endif
|
||||
int initialized;
|
||||
bool driver_created;
|
||||
Audiodev *dev;
|
||||
} glob_sdl;
|
||||
typedef struct SDLAudioState SDLAudioState;
|
||||
|
||||
@@ -64,19 +77,79 @@ static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
|
||||
}
|
||||
|
||||
static int aud_to_sdlfmt (AudioFormat fmt)
|
||||
static int sdl_lock (SDLAudioState *s, const char *forfn)
|
||||
{
|
||||
#if USE_SEMAPHORE
|
||||
if (SDL_LockMutex (s->mutex)) {
|
||||
sdl_logerr ("SDL_LockMutex for %s failed\n", forfn);
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
SDL_LockAudio();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_unlock (SDLAudioState *s, const char *forfn)
|
||||
{
|
||||
#if USE_SEMAPHORE
|
||||
if (SDL_UnlockMutex (s->mutex)) {
|
||||
sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn);
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
SDL_UnlockAudio();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_post (SDLAudioState *s, const char *forfn)
|
||||
{
|
||||
#if USE_SEMAPHORE
|
||||
if (SDL_SemPost (s->sem)) {
|
||||
sdl_logerr ("SDL_SemPost for %s failed\n", forfn);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if USE_SEMAPHORE
|
||||
static int sdl_wait (SDLAudioState *s, const char *forfn)
|
||||
{
|
||||
if (SDL_SemWait (s->sem)) {
|
||||
sdl_logerr ("SDL_SemWait for %s failed\n", forfn);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
|
||||
{
|
||||
if (sdl_unlock (s, forfn)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sdl_post (s, forfn);
|
||||
}
|
||||
|
||||
static int aud_to_sdlfmt (audfmt_e fmt)
|
||||
{
|
||||
switch (fmt) {
|
||||
case AUDIO_FORMAT_S8:
|
||||
case AUD_FMT_S8:
|
||||
return AUDIO_S8;
|
||||
|
||||
case AUDIO_FORMAT_U8:
|
||||
case AUD_FMT_U8:
|
||||
return AUDIO_U8;
|
||||
|
||||
case AUDIO_FORMAT_S16:
|
||||
case AUD_FMT_S16:
|
||||
return AUDIO_S16LSB;
|
||||
|
||||
case AUDIO_FORMAT_U16:
|
||||
case AUD_FMT_U16:
|
||||
return AUDIO_U16LSB;
|
||||
|
||||
default:
|
||||
@@ -88,37 +161,37 @@ static int aud_to_sdlfmt (AudioFormat fmt)
|
||||
}
|
||||
}
|
||||
|
||||
static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness)
|
||||
static int sdl_to_audfmt(int sdlfmt, audfmt_e *fmt, int *endianness)
|
||||
{
|
||||
switch (sdlfmt) {
|
||||
case AUDIO_S8:
|
||||
*endianness = 0;
|
||||
*fmt = AUDIO_FORMAT_S8;
|
||||
*fmt = AUD_FMT_S8;
|
||||
break;
|
||||
|
||||
case AUDIO_U8:
|
||||
*endianness = 0;
|
||||
*fmt = AUDIO_FORMAT_U8;
|
||||
*fmt = AUD_FMT_U8;
|
||||
break;
|
||||
|
||||
case AUDIO_S16LSB:
|
||||
*endianness = 0;
|
||||
*fmt = AUDIO_FORMAT_S16;
|
||||
*fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
case AUDIO_U16LSB:
|
||||
*endianness = 0;
|
||||
*fmt = AUDIO_FORMAT_U16;
|
||||
*fmt = AUD_FMT_U16;
|
||||
break;
|
||||
|
||||
case AUDIO_S16MSB:
|
||||
*endianness = 1;
|
||||
*fmt = AUDIO_FORMAT_S16;
|
||||
*fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
case AUDIO_U16MSB:
|
||||
*endianness = 1;
|
||||
*fmt = AUDIO_FORMAT_U16;
|
||||
*fmt = AUD_FMT_U16;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -170,9 +243,9 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
|
||||
static void sdl_close (SDLAudioState *s)
|
||||
{
|
||||
if (s->initialized) {
|
||||
SDL_LockAudio();
|
||||
sdl_lock (s, "sdl_close");
|
||||
s->exit = 1;
|
||||
SDL_UnlockAudio();
|
||||
sdl_unlock_and_post (s, "sdl_close");
|
||||
SDL_PauseAudio (1);
|
||||
SDL_CloseAudio ();
|
||||
s->initialized = 0;
|
||||
@@ -185,36 +258,76 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
HWVoiceOut *hw = &sdl->hw;
|
||||
int samples = len >> hw->info.shift;
|
||||
int to_mix, decr;
|
||||
|
||||
if (s->exit || !sdl->live) {
|
||||
if (s->exit) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* dolog ("in callback samples=%d live=%d\n", samples, sdl->live); */
|
||||
while (samples) {
|
||||
int to_mix, decr;
|
||||
|
||||
to_mix = audio_MIN(samples, sdl->live);
|
||||
decr = to_mix;
|
||||
while (to_mix) {
|
||||
int chunk = audio_MIN(to_mix, hw->samples - hw->rpos);
|
||||
struct st_sample *src = hw->mix_buf + hw->rpos;
|
||||
/* dolog ("in callback samples=%d\n", samples); */
|
||||
#if USE_SEMAPHORE
|
||||
sdl_wait (s, "sdl_callback");
|
||||
if (s->exit) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
|
||||
hw->clip(buf, src, chunk);
|
||||
hw->rpos = (hw->rpos + chunk) % hw->samples;
|
||||
to_mix -= chunk;
|
||||
buf += chunk << hw->info.shift;
|
||||
if (sdl_lock (s, "sdl_callback")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (audio_bug(__func__, sdl->live < 0 || sdl->live > hw->samples)) {
|
||||
dolog ("sdl->live=%d hw->samples=%d\n",
|
||||
sdl->live, hw->samples);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sdl->live) {
|
||||
goto again;
|
||||
}
|
||||
#else
|
||||
if (s->exit || !sdl->live) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* dolog ("in callback live=%d\n", live); */
|
||||
to_mix = audio_MIN (samples, sdl->live);
|
||||
decr = to_mix;
|
||||
while (to_mix) {
|
||||
int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
|
||||
struct st_sample *src = hw->mix_buf + hw->rpos;
|
||||
|
||||
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
|
||||
hw->clip (buf, src, chunk);
|
||||
#if USE_SEMAPHORE
|
||||
sdl->rpos = (sdl->rpos + chunk) % hw->samples;
|
||||
#else
|
||||
hw->rpos = (hw->rpos + chunk) % hw->samples;
|
||||
#endif
|
||||
to_mix -= chunk;
|
||||
buf += chunk << hw->info.shift;
|
||||
}
|
||||
samples -= decr;
|
||||
sdl->live -= decr;
|
||||
sdl->decr += decr;
|
||||
|
||||
#if USE_SEMAPHORE
|
||||
again:
|
||||
if (sdl_unlock (s, "sdl_callback")) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
samples -= decr;
|
||||
sdl->live -= decr;
|
||||
sdl->decr += decr;
|
||||
|
||||
/* dolog ("done len=%d\n", len); */
|
||||
|
||||
#if (SDL_MAJOR_VERSION >= 2)
|
||||
/* SDL2 does not clear the remaining buffer for us, so do it on our own */
|
||||
if (samples) {
|
||||
memset(buf, 0, samples << hw->info.shift);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
|
||||
@@ -226,8 +339,11 @@ static int sdl_run_out (HWVoiceOut *hw, int live)
|
||||
{
|
||||
int decr;
|
||||
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
|
||||
SDL_LockAudio();
|
||||
if (sdl_lock (s, "sdl_run_out")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sdl->decr > live) {
|
||||
ldebug ("sdl->decr %d live %d sdl->live %d\n",
|
||||
@@ -239,10 +355,19 @@ static int sdl_run_out (HWVoiceOut *hw, int live)
|
||||
decr = audio_MIN (sdl->decr, live);
|
||||
sdl->decr -= decr;
|
||||
|
||||
#if USE_SEMAPHORE
|
||||
sdl->live = live - decr;
|
||||
hw->rpos = sdl->rpos;
|
||||
#else
|
||||
sdl->live = live;
|
||||
#endif
|
||||
|
||||
SDL_UnlockAudio();
|
||||
|
||||
if (sdl->live > 0) {
|
||||
sdl_unlock_and_post (s, "sdl_run_out");
|
||||
}
|
||||
else {
|
||||
sdl_unlock (s, "sdl_run_out");
|
||||
}
|
||||
return decr;
|
||||
}
|
||||
|
||||
@@ -261,13 +386,13 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
SDL_AudioSpec req, obt;
|
||||
int endianness;
|
||||
int err;
|
||||
AudioFormat effective_fmt;
|
||||
audfmt_e effective_fmt;
|
||||
struct audsettings obt_as;
|
||||
|
||||
req.freq = as->freq;
|
||||
req.format = aud_to_sdlfmt (as->fmt);
|
||||
req.channels = as->nchannels;
|
||||
req.samples = audio_buffer_samples(s->dev->u.sdl.out, as, 11610);
|
||||
req.samples = conf.nb_samples;
|
||||
req.callback = sdl_callback;
|
||||
req.userdata = sdl;
|
||||
|
||||
@@ -311,7 +436,7 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *sdl_audio_init(Audiodev *dev)
|
||||
static void *sdl_audio_init (void)
|
||||
{
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
if (s->driver_created) {
|
||||
@@ -324,8 +449,24 @@ static void *sdl_audio_init(Audiodev *dev)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if USE_SEMAPHORE
|
||||
s->mutex = SDL_CreateMutex ();
|
||||
if (!s->mutex) {
|
||||
sdl_logerr ("Failed to create SDL mutex\n");
|
||||
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s->sem = SDL_CreateSemaphore (0);
|
||||
if (!s->sem) {
|
||||
sdl_logerr ("Failed to create SDL semaphore\n");
|
||||
SDL_DestroyMutex (s->mutex);
|
||||
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
s->driver_created = true;
|
||||
s->dev = dev;
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -333,11 +474,24 @@ static void sdl_audio_fini (void *opaque)
|
||||
{
|
||||
SDLAudioState *s = opaque;
|
||||
sdl_close (s);
|
||||
#if USE_SEMAPHORE
|
||||
SDL_DestroySemaphore (s->sem);
|
||||
SDL_DestroyMutex (s->mutex);
|
||||
#endif
|
||||
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
||||
s->driver_created = false;
|
||||
s->dev = NULL;
|
||||
}
|
||||
|
||||
static struct audio_option sdl_options[] = {
|
||||
{
|
||||
.name = "SAMPLES",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.nb_samples,
|
||||
.descr = "Size of SDL buffer in samples"
|
||||
},
|
||||
{ /* End of list */ }
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops sdl_pcm_ops = {
|
||||
.init_out = sdl_init_out,
|
||||
.fini_out = sdl_fini_out,
|
||||
@@ -349,6 +503,7 @@ static struct audio_pcm_ops sdl_pcm_ops = {
|
||||
static struct audio_driver sdl_audio_driver = {
|
||||
.name = "sdl",
|
||||
.descr = "SDL http://www.libsdl.org",
|
||||
.options = sdl_options,
|
||||
.init = sdl_audio_init,
|
||||
.fini = sdl_audio_fini,
|
||||
.pcm_ops = &sdl_pcm_ops,
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "ui/qemu-spice.h"
|
||||
@@ -78,7 +77,7 @@ static const SpiceRecordInterface record_sif = {
|
||||
.base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
|
||||
};
|
||||
|
||||
static void *spice_audio_init(Audiodev *dev)
|
||||
static void *spice_audio_init (void)
|
||||
{
|
||||
if (!using_spice) {
|
||||
return NULL;
|
||||
@@ -131,7 +130,7 @@ static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
|
||||
settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ;
|
||||
#endif
|
||||
settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
|
||||
settings.fmt = AUDIO_FORMAT_S16;
|
||||
settings.fmt = AUD_FMT_S16;
|
||||
settings.endianness = AUDIO_HOST_ENDIANNESS;
|
||||
|
||||
audio_pcm_init_info (&hw->info, &settings);
|
||||
@@ -259,7 +258,7 @@ static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
settings.freq = SPICE_INTERFACE_RECORD_FREQ;
|
||||
#endif
|
||||
settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
|
||||
settings.fmt = AUDIO_FORMAT_S16;
|
||||
settings.fmt = AUD_FMT_S16;
|
||||
settings.endianness = AUDIO_HOST_ENDIANNESS;
|
||||
|
||||
audio_pcm_init_info (&hw->info, &settings);
|
||||
@@ -374,6 +373,10 @@ static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct audio_option audio_options[] = {
|
||||
{ /* end of list */ },
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops audio_callbacks = {
|
||||
.init_out = line_out_init,
|
||||
.fini_out = line_out_fini,
|
||||
@@ -391,6 +394,7 @@ static struct audio_pcm_ops audio_callbacks = {
|
||||
static struct audio_driver spice_audio_driver = {
|
||||
.name = "spice",
|
||||
.descr = "spice audio driver",
|
||||
.options = audio_options,
|
||||
.init = spice_audio_init,
|
||||
.fini = spice_audio_fini,
|
||||
.pcm_ops = &audio_callbacks,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
|
||||
# alsaaudio.c
|
||||
# audio/alsaaudio.c
|
||||
alsa_revents(int revents) "revents = %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"
|
||||
@@ -12,11 +12,6 @@ alsa_resume_out(void) "Resuming suspended output stream"
|
||||
alsa_resume_in(void) "Resuming suspended input stream"
|
||||
alsa_no_frames(int state) "No frames available and ALSA state is %d"
|
||||
|
||||
# ossaudio.c
|
||||
# audio/ossaudio.c
|
||||
oss_version(int version) "OSS version = 0x%x"
|
||||
oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d"
|
||||
|
||||
# audio.c
|
||||
audio_timer_start(int interval) "interval %d ms"
|
||||
audio_timer_stop(void) ""
|
||||
audio_timer_delayed(int interval) "interval %d ms"
|
||||
|
||||
@@ -21,12 +21,9 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qapi/opts-visitor.h"
|
||||
#include "audio.h"
|
||||
|
||||
#define AUDIO_CAP "wav"
|
||||
@@ -40,6 +37,11 @@ typedef struct WAVVoiceOut {
|
||||
int total_samples;
|
||||
} WAVVoiceOut;
|
||||
|
||||
typedef struct {
|
||||
struct audsettings settings;
|
||||
const char *wav_path;
|
||||
} WAVConf;
|
||||
|
||||
static int wav_run_out (HWVoiceOut *hw, int live)
|
||||
{
|
||||
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
|
||||
@@ -110,30 +112,25 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
|
||||
0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
Audiodev *dev = drv_opaque;
|
||||
AudiodevWavOptions *wopts = &dev->u.wav;
|
||||
struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out);
|
||||
const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav";
|
||||
WAVConf *conf = drv_opaque;
|
||||
struct audsettings wav_as = conf->settings;
|
||||
|
||||
stereo = wav_as.nchannels == 2;
|
||||
switch (wav_as.fmt) {
|
||||
case AUDIO_FORMAT_S8:
|
||||
case AUDIO_FORMAT_U8:
|
||||
case AUD_FMT_S8:
|
||||
case AUD_FMT_U8:
|
||||
bits16 = 0;
|
||||
break;
|
||||
|
||||
case AUDIO_FORMAT_S16:
|
||||
case AUDIO_FORMAT_U16:
|
||||
case AUD_FMT_S16:
|
||||
case AUD_FMT_U16:
|
||||
bits16 = 1;
|
||||
break;
|
||||
|
||||
case AUDIO_FORMAT_S32:
|
||||
case AUDIO_FORMAT_U32:
|
||||
case AUD_FMT_S32:
|
||||
case AUD_FMT_U32:
|
||||
dolog ("WAVE files can not handle 32bit formats\n");
|
||||
return -1;
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
hdr[34] = bits16 ? 0x10 : 0x08;
|
||||
@@ -154,10 +151,10 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
|
||||
le_store (hdr + 32, 1 << (bits16 + stereo), 2);
|
||||
|
||||
wav->f = fopen(wav_path, "wb");
|
||||
wav->f = fopen (conf->wav_path, "wb");
|
||||
if (!wav->f) {
|
||||
dolog ("Failed to open wave file `%s'\nReason: %s\n",
|
||||
wav_path, strerror(errno));
|
||||
conf->wav_path, strerror (errno));
|
||||
g_free (wav->pcm_buf);
|
||||
wav->pcm_buf = NULL;
|
||||
return -1;
|
||||
@@ -225,17 +222,54 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *wav_audio_init(Audiodev *dev)
|
||||
static WAVConf glob_conf = {
|
||||
.settings.freq = 44100,
|
||||
.settings.nchannels = 2,
|
||||
.settings.fmt = AUD_FMT_S16,
|
||||
.wav_path = "qemu.wav"
|
||||
};
|
||||
|
||||
static void *wav_audio_init (void)
|
||||
{
|
||||
assert(dev->driver == AUDIODEV_DRIVER_WAV);
|
||||
return dev;
|
||||
WAVConf *conf = g_malloc(sizeof(WAVConf));
|
||||
*conf = glob_conf;
|
||||
return conf;
|
||||
}
|
||||
|
||||
static void wav_audio_fini (void *opaque)
|
||||
{
|
||||
ldebug ("wav_fini");
|
||||
g_free(opaque);
|
||||
}
|
||||
|
||||
static struct audio_option wav_options[] = {
|
||||
{
|
||||
.name = "FREQUENCY",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &glob_conf.settings.freq,
|
||||
.descr = "Frequency"
|
||||
},
|
||||
{
|
||||
.name = "FORMAT",
|
||||
.tag = AUD_OPT_FMT,
|
||||
.valp = &glob_conf.settings.fmt,
|
||||
.descr = "Format"
|
||||
},
|
||||
{
|
||||
.name = "DAC_FIXED_CHANNELS",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &glob_conf.settings.nchannels,
|
||||
.descr = "Number of channels (1 - mono, 2 - stereo)"
|
||||
},
|
||||
{
|
||||
.name = "PATH",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &glob_conf.wav_path,
|
||||
.descr = "Path to wave file"
|
||||
},
|
||||
{ /* End of list */ }
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops wav_pcm_ops = {
|
||||
.init_out = wav_init_out,
|
||||
.fini_out = wav_fini_out,
|
||||
@@ -247,6 +281,7 @@ static struct audio_pcm_ops wav_pcm_ops = {
|
||||
static struct audio_driver wav_audio_driver = {
|
||||
.name = "wav",
|
||||
.descr = "WAV renderer http://wikipedia.org/wiki/WAV",
|
||||
.options = wav_options,
|
||||
.init = wav_audio_init,
|
||||
.fini = wav_audio_fini,
|
||||
.pcm_ops = &wav_pcm_ops,
|
||||
|
||||
@@ -38,29 +38,30 @@ static void wav_destroy (void *opaque)
|
||||
uint8_t dlen[4];
|
||||
uint32_t datalen = wav->bytes;
|
||||
uint32_t rifflen = datalen + 36;
|
||||
Monitor *mon = cur_mon;
|
||||
|
||||
if (wav->f) {
|
||||
le_store (rlen, rifflen, 4);
|
||||
le_store (dlen, datalen, 4);
|
||||
|
||||
if (fseek (wav->f, 4, SEEK_SET)) {
|
||||
error_report("wav_destroy: rlen fseek failed: %s",
|
||||
strerror(errno));
|
||||
monitor_printf (mon, "wav_destroy: rlen fseek failed\nReason: %s\n",
|
||||
strerror (errno));
|
||||
goto doclose;
|
||||
}
|
||||
if (fwrite (rlen, 4, 1, wav->f) != 1) {
|
||||
error_report("wav_destroy: rlen fwrite failed: %s",
|
||||
strerror(errno));
|
||||
monitor_printf (mon, "wav_destroy: rlen fwrite failed\nReason %s\n",
|
||||
strerror (errno));
|
||||
goto doclose;
|
||||
}
|
||||
if (fseek (wav->f, 32, SEEK_CUR)) {
|
||||
error_report("wav_destroy: dlen fseek failed: %s",
|
||||
strerror(errno));
|
||||
monitor_printf (mon, "wav_destroy: dlen fseek failed\nReason %s\n",
|
||||
strerror (errno));
|
||||
goto doclose;
|
||||
}
|
||||
if (fwrite (dlen, 1, 4, wav->f) != 4) {
|
||||
error_report("wav_destroy: dlen fwrite failed: %s",
|
||||
strerror(errno));
|
||||
monitor_printf (mon, "wav_destroy: dlen fwrite failed\nReason %s\n",
|
||||
strerror (errno));
|
||||
goto doclose;
|
||||
}
|
||||
doclose:
|
||||
@@ -77,7 +78,8 @@ static void wav_capture (void *opaque, void *buf, int size)
|
||||
WAVState *wav = opaque;
|
||||
|
||||
if (fwrite (buf, size, 1, wav->f) != 1) {
|
||||
error_report("wav_capture: fwrite error: %s", strerror(errno));
|
||||
monitor_printf (cur_mon, "wav_capture: fwrite error\nReason: %s",
|
||||
strerror (errno));
|
||||
}
|
||||
wav->bytes += size;
|
||||
}
|
||||
@@ -108,6 +110,7 @@ static struct capture_ops wav_capture_ops = {
|
||||
int wav_start_capture (CaptureState *s, const char *path, int freq,
|
||||
int bits, int nchannels)
|
||||
{
|
||||
Monitor *mon = cur_mon;
|
||||
WAVState *wav;
|
||||
uint8_t hdr[] = {
|
||||
0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
|
||||
@@ -121,13 +124,13 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
|
||||
CaptureVoiceOut *cap;
|
||||
|
||||
if (bits != 8 && bits != 16) {
|
||||
error_report("incorrect bit count %d, must be 8 or 16", bits);
|
||||
monitor_printf (mon, "incorrect bit count %d, must be 8 or 16\n", bits);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (nchannels != 1 && nchannels != 2) {
|
||||
error_report("incorrect channel count %d, must be 1 or 2",
|
||||
nchannels);
|
||||
monitor_printf (mon, "incorrect channel count %d, must be 1 or 2\n",
|
||||
nchannels);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -136,7 +139,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
|
||||
|
||||
as.freq = freq;
|
||||
as.nchannels = 1 << stereo;
|
||||
as.fmt = bits16 ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8;
|
||||
as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
|
||||
as.endianness = 0;
|
||||
|
||||
ops.notify = wav_notify;
|
||||
@@ -155,8 +158,8 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
|
||||
|
||||
wav->f = fopen (path, "wb");
|
||||
if (!wav->f) {
|
||||
error_report("Failed to open wave file `%s': %s",
|
||||
path, strerror(errno));
|
||||
monitor_printf (mon, "Failed to open wave file `%s'\nReason: %s\n",
|
||||
path, strerror (errno));
|
||||
g_free (wav);
|
||||
return -1;
|
||||
}
|
||||
@@ -167,13 +170,14 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
|
||||
wav->freq = freq;
|
||||
|
||||
if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) {
|
||||
error_report("Failed to write header: %s", strerror(errno));
|
||||
monitor_printf (mon, "Failed to write header\nReason: %s\n",
|
||||
strerror (errno));
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
cap = AUD_add_capture (&as, &ops, wav);
|
||||
if (!cap) {
|
||||
error_report("Failed to add audio capture");
|
||||
monitor_printf (mon, "Failed to add audio capture\n");
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
@@ -185,7 +189,8 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
|
||||
error_free:
|
||||
g_free (wav->path);
|
||||
if (fclose (wav->f)) {
|
||||
error_report("Failed to close wave file: %s", strerror(errno));
|
||||
monitor_printf (mon, "Failed to close wave file\nReason: %s\n",
|
||||
strerror (errno));
|
||||
}
|
||||
g_free (wav);
|
||||
return -1;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
authz-obj-y += base.o
|
||||
authz-obj-y += simple.o
|
||||
authz-obj-y += list.o
|
||||
authz-obj-y += listfile.o
|
||||
authz-obj-$(CONFIG_AUTH_PAM) += pamacct.o
|
||||
|
||||
pamacct.o-libs = -lpam
|
||||
83
authz/base.c
83
authz/base.c
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* QEMU authorization framework base class
|
||||
*
|
||||
* Copyright (c) 2018 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "authz/base.h"
|
||||
#include "qemu/module.h"
|
||||
#include "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)
|
||||
|
||||
272
authz/list.c
272
authz/list.c
@@ -1,272 +0,0 @@
|
||||
/*
|
||||
* QEMU access control list authorization driver
|
||||
*
|
||||
* Copyright (c) 2018 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "authz/list.h"
|
||||
#include "trace.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "qapi/qapi-visit-authz.h"
|
||||
#include "qemu/module.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);
|
||||
284
authz/listfile.c
284
authz/listfile.c
@@ -1,284 +0,0 @@
|
||||
/*
|
||||
* QEMU access control list file authorization driver
|
||||
*
|
||||
* Copyright (c) 2018 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "authz/listfile.h"
|
||||
#include "trace.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/module.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);
|
||||
149
authz/pamacct.c
149
authz/pamacct.c
@@ -1,149 +0,0 @@
|
||||
/*
|
||||
* QEMU PAM authorization driver
|
||||
*
|
||||
* Copyright (c) 2018 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "authz/pamacct.h"
|
||||
#include "trace.h"
|
||||
#include "qemu/module.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);
|
||||
116
authz/simple.c
116
authz/simple.c
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* QEMU simple authorization driver
|
||||
*
|
||||
* Copyright (c) 2018 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "authz/simple.h"
|
||||
#include "trace.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
|
||||
static bool qauthz_simple_is_allowed(QAuthZ *authz,
|
||||
const char *identity,
|
||||
Error **errp)
|
||||
{
|
||||
QAuthZSimple *sauthz = QAUTHZ_SIMPLE(authz);
|
||||
|
||||
trace_qauthz_simple_is_allowed(authz, sauthz->identity, identity);
|
||||
return g_str_equal(identity, sauthz->identity);
|
||||
}
|
||||
|
||||
static void
|
||||
qauthz_simple_prop_set_identity(Object *obj,
|
||||
const char *value,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
|
||||
|
||||
g_free(sauthz->identity);
|
||||
sauthz->identity = g_strdup(value);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
qauthz_simple_prop_get_identity(Object *obj,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
|
||||
|
||||
return g_strdup(sauthz->identity);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qauthz_simple_finalize(Object *obj)
|
||||
{
|
||||
QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
|
||||
|
||||
g_free(sauthz->identity);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qauthz_simple_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
QAuthZClass *authz = QAUTHZ_CLASS(oc);
|
||||
|
||||
authz->is_allowed = qauthz_simple_is_allowed;
|
||||
|
||||
object_class_property_add_str(oc, "identity",
|
||||
qauthz_simple_prop_get_identity,
|
||||
qauthz_simple_prop_set_identity,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
QAuthZSimple *qauthz_simple_new(const char *id,
|
||||
const char *identity,
|
||||
Error **errp)
|
||||
{
|
||||
return QAUTHZ_SIMPLE(
|
||||
object_new_with_props(TYPE_QAUTHZ_SIMPLE,
|
||||
object_get_objects_root(),
|
||||
id, errp,
|
||||
"identity", identity,
|
||||
NULL));
|
||||
}
|
||||
|
||||
|
||||
static const TypeInfo qauthz_simple_info = {
|
||||
.parent = TYPE_QAUTHZ,
|
||||
.name = TYPE_QAUTHZ_SIMPLE,
|
||||
.instance_size = sizeof(QAuthZSimple),
|
||||
.instance_finalize = qauthz_simple_finalize,
|
||||
.class_size = sizeof(QAuthZSimpleClass),
|
||||
.class_init = qauthz_simple_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
qauthz_simple_register_types(void)
|
||||
{
|
||||
type_register_static(&qauthz_simple_info);
|
||||
}
|
||||
|
||||
|
||||
type_init(qauthz_simple_register_types);
|
||||
@@ -1,18 +0,0 @@
|
||||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
|
||||
# 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,16 +4,15 @@ common-obj-$(CONFIG_POSIX) += rng-random.o
|
||||
common-obj-$(CONFIG_TPM) += tpm.o
|
||||
|
||||
common-obj-y += hostmem.o hostmem-ram.o
|
||||
common-obj-$(CONFIG_POSIX) += hostmem-file.o
|
||||
common-obj-$(CONFIG_LINUX) += hostmem-file.o
|
||||
|
||||
common-obj-y += cryptodev.o
|
||||
common-obj-y += cryptodev-builtin.o
|
||||
|
||||
ifeq ($(CONFIG_VIRTIO_CRYPTO),y)
|
||||
ifeq ($(CONFIG_VIRTIO),y)
|
||||
common-obj-y += cryptodev-vhost.o
|
||||
common-obj-$(CONFIG_VHOST_CRYPTO) += cryptodev-vhost-user.o
|
||||
common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) += \
|
||||
cryptodev-vhost-user.o
|
||||
endif
|
||||
|
||||
common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_VIRTIO)) += vhost-user.o
|
||||
|
||||
common-obj-$(CONFIG_LINUX) += hostmem-memfd.o
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/virtio/vhost-user.h"
|
||||
#include "standard-headers/linux/virtio_crypto.h"
|
||||
#include "sysemu/cryptodev-vhost.h"
|
||||
#include "chardev/char-fe.h"
|
||||
@@ -47,7 +46,6 @@
|
||||
typedef struct CryptoDevBackendVhostUser {
|
||||
CryptoDevBackend parent_obj;
|
||||
|
||||
VhostUserState vhost_user;
|
||||
CharBackend chr;
|
||||
char *chr_name;
|
||||
bool opened;
|
||||
@@ -104,7 +102,7 @@ cryptodev_vhost_user_start(int queues,
|
||||
continue;
|
||||
}
|
||||
|
||||
options.opaque = &s->vhost_user;
|
||||
options.opaque = &s->chr;
|
||||
options.backend_type = VHOST_BACKEND_TYPE_USER;
|
||||
options.cc = b->conf.peers.ccs[i];
|
||||
s->vhost_crypto[i] = cryptodev_vhost_init(&options);
|
||||
@@ -157,6 +155,7 @@ static void cryptodev_vhost_user_event(void *opaque, int event)
|
||||
{
|
||||
CryptoDevBackendVhostUser *s = opaque;
|
||||
CryptoDevBackend *b = CRYPTODEV_BACKEND(s);
|
||||
Error *err = NULL;
|
||||
int queues = b->conf.peers.queues;
|
||||
|
||||
assert(queues < MAX_CRYPTO_QUEUE_NUM);
|
||||
@@ -173,6 +172,10 @@ static void cryptodev_vhost_user_event(void *opaque, int event)
|
||||
cryptodev_vhost_user_stop(queues, s);
|
||||
break;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
}
|
||||
}
|
||||
|
||||
static void cryptodev_vhost_user_init(
|
||||
@@ -212,10 +215,6 @@ static void cryptodev_vhost_user_init(
|
||||
}
|
||||
}
|
||||
|
||||
if (!vhost_user_init(&s->vhost_user, &s->chr, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
|
||||
cryptodev_vhost_user_event, NULL, s, NULL, true);
|
||||
|
||||
@@ -300,8 +299,6 @@ static void cryptodev_vhost_user_cleanup(
|
||||
backend->conf.peers.ccs[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
vhost_user_cleanup(&s->vhost_user);
|
||||
}
|
||||
|
||||
static void cryptodev_vhost_user_set_chardev(Object *obj,
|
||||
|
||||
@@ -9,11 +9,9 @@
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/hostmem.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
@@ -33,21 +31,15 @@ typedef struct HostMemoryBackendFile HostMemoryBackendFile;
|
||||
struct HostMemoryBackendFile {
|
||||
HostMemoryBackend parent_obj;
|
||||
|
||||
bool discard_data;
|
||||
char *mem_path;
|
||||
uint64_t align;
|
||||
bool discard_data;
|
||||
bool is_pmem;
|
||||
};
|
||||
|
||||
static void
|
||||
file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
{
|
||||
#ifndef CONFIG_POSIX
|
||||
error_setg(errp, "backend '%s' not supported on this host",
|
||||
object_get_typename(OBJECT(backend)));
|
||||
#else
|
||||
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend);
|
||||
gchar *name;
|
||||
|
||||
if (!backend->size) {
|
||||
error_setg(errp, "can't create backend with size 0");
|
||||
@@ -57,38 +49,19 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
error_setg(errp, "mem-path property not set");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify pmem file size since starting a guest with an incorrect size
|
||||
* leads to confusing failures inside the guest.
|
||||
*/
|
||||
if (fb->is_pmem) {
|
||||
Error *local_err = NULL;
|
||||
uint64_t size;
|
||||
|
||||
size = qemu_get_pmem_size(fb->mem_path, &local_err);
|
||||
if (!size) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (backend->size > size) {
|
||||
error_setg(errp, "size property %" PRIu64 " is larger than "
|
||||
"pmem file \"%s\" size %" PRIu64, backend->size,
|
||||
fb->mem_path, size);
|
||||
return;
|
||||
}
|
||||
#ifndef CONFIG_LINUX
|
||||
error_setg(errp, "-mem-path not supported on this host");
|
||||
#else
|
||||
if (!host_memory_backend_mr_inited(backend)) {
|
||||
gchar *path;
|
||||
backend->force_prealloc = mem_prealloc;
|
||||
path = object_get_canonical_path(OBJECT(backend));
|
||||
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
|
||||
path,
|
||||
backend->size, fb->align, backend->share,
|
||||
fb->mem_path, errp);
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
backend->force_prealloc = mem_prealloc;
|
||||
name = host_memory_backend_get_name(backend);
|
||||
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
|
||||
name,
|
||||
backend->size, fb->align,
|
||||
(backend->share ? RAM_SHARED : 0) |
|
||||
(fb->is_pmem ? RAM_PMEM : 0),
|
||||
fb->mem_path, errp);
|
||||
g_free(name);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -105,8 +78,7 @@ static void set_mem_path(Object *o, const char *str, Error **errp)
|
||||
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
|
||||
|
||||
if (host_memory_backend_mr_inited(backend)) {
|
||||
error_setg(errp, "cannot change property 'mem-path' of %s",
|
||||
object_get_typename(o));
|
||||
error_setg(errp, "cannot change property value");
|
||||
return;
|
||||
}
|
||||
g_free(fb->mem_path);
|
||||
@@ -144,8 +116,7 @@ static void file_memory_backend_set_align(Object *o, Visitor *v,
|
||||
uint64_t val;
|
||||
|
||||
if (host_memory_backend_mr_inited(backend)) {
|
||||
error_setg(&local_err, "cannot change property '%s' of %s",
|
||||
name, object_get_typename(o));
|
||||
error_setg(&local_err, "cannot change property value");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -159,39 +130,6 @@ static void file_memory_backend_set_align(Object *o, Visitor *v,
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
static bool file_memory_backend_get_pmem(Object *o, Error **errp)
|
||||
{
|
||||
return MEMORY_BACKEND_FILE(o)->is_pmem;
|
||||
}
|
||||
|
||||
static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
|
||||
|
||||
if (host_memory_backend_mr_inited(backend)) {
|
||||
|
||||
error_setg(errp, "cannot change property 'pmem' of %s.",
|
||||
object_get_typename(o));
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_LIBPMEM
|
||||
if (value) {
|
||||
Error *local_err = NULL;
|
||||
|
||||
error_setg(&local_err,
|
||||
"Lack of libpmem support while setting the 'pmem=on'"
|
||||
" of %s. We can't ensure data persistence.",
|
||||
object_get_typename(o));
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
fb->is_pmem = value;
|
||||
}
|
||||
|
||||
static void file_backend_unparent(Object *obj)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
@@ -223,9 +161,6 @@ file_backend_class_init(ObjectClass *oc, void *data)
|
||||
file_memory_backend_get_align,
|
||||
file_memory_backend_set_align,
|
||||
NULL, NULL, &error_abort);
|
||||
object_class_property_add_bool(oc, "pmem",
|
||||
file_memory_backend_get_pmem, file_memory_backend_set_pmem,
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
static void file_backend_instance_finalize(Object *o)
|
||||
|
||||
@@ -9,13 +9,12 @@
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/hostmem.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "qemu/memfd.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
#define TYPE_MEMORY_BACKEND_MEMFD "memory-backend-memfd"
|
||||
@@ -45,6 +44,10 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
if (host_memory_backend_mr_inited(backend)) {
|
||||
return;
|
||||
}
|
||||
|
||||
backend->force_prealloc = mem_prealloc;
|
||||
fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size,
|
||||
m->hugetlb, m->hugetlbsize, m->seal ?
|
||||
@@ -54,10 +57,9 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
name = host_memory_backend_get_name(backend);
|
||||
name = object_get_canonical_path(OBJECT(backend));
|
||||
memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend),
|
||||
name, backend->size,
|
||||
backend->share, fd, errp);
|
||||
name, backend->size, true, fd, errp);
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
@@ -129,7 +131,6 @@ memfd_backend_instance_init(Object *obj)
|
||||
|
||||
/* default to sealed file */
|
||||
m->seal = true;
|
||||
MEMORY_BACKEND(m)->share = true;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -139,29 +140,18 @@ memfd_backend_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
bc->alloc = memfd_backend_memory_alloc;
|
||||
|
||||
if (qemu_memfd_check(MFD_HUGETLB)) {
|
||||
object_class_property_add_bool(oc, "hugetlb",
|
||||
memfd_backend_get_hugetlb,
|
||||
memfd_backend_set_hugetlb,
|
||||
&error_abort);
|
||||
object_class_property_set_description(oc, "hugetlb",
|
||||
"Use huge pages",
|
||||
&error_abort);
|
||||
object_class_property_add(oc, "hugetlbsize", "int",
|
||||
memfd_backend_get_hugetlbsize,
|
||||
memfd_backend_set_hugetlbsize,
|
||||
NULL, NULL, &error_abort);
|
||||
object_class_property_set_description(oc, "hugetlbsize",
|
||||
"Huge pages size (ex: 2M, 1G)",
|
||||
&error_abort);
|
||||
}
|
||||
object_class_property_add_bool(oc, "hugetlb",
|
||||
memfd_backend_get_hugetlb,
|
||||
memfd_backend_set_hugetlb,
|
||||
&error_abort);
|
||||
object_class_property_add(oc, "hugetlbsize", "int",
|
||||
memfd_backend_get_hugetlbsize,
|
||||
memfd_backend_set_hugetlbsize,
|
||||
NULL, NULL, &error_abort);
|
||||
object_class_property_add_bool(oc, "seal",
|
||||
memfd_backend_get_seal,
|
||||
memfd_backend_set_seal,
|
||||
&error_abort);
|
||||
object_class_property_set_description(oc, "seal",
|
||||
"Seal growing & shrinking",
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
static const TypeInfo memfd_backend_info = {
|
||||
@@ -174,9 +164,7 @@ static const TypeInfo memfd_backend_info = {
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
|
||||
type_register_static(&memfd_backend_info);
|
||||
}
|
||||
type_register_static(&memfd_backend_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
|
||||
@@ -9,29 +9,28 @@
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/hostmem.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
|
||||
#define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram"
|
||||
|
||||
|
||||
static void
|
||||
ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
{
|
||||
char *name;
|
||||
char *path;
|
||||
|
||||
if (!backend->size) {
|
||||
error_setg(errp, "can't create backend with size 0");
|
||||
return;
|
||||
}
|
||||
|
||||
name = host_memory_backend_get_name(backend);
|
||||
memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), name,
|
||||
path = object_get_canonical_path_component(OBJECT(backend));
|
||||
memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), path,
|
||||
backend->size, backend->share, errp);
|
||||
g_free(name);
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "qapi/visitor.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "qemu/mmap-alloc.h"
|
||||
|
||||
#ifdef CONFIG_NUMA
|
||||
#include <numaif.h>
|
||||
@@ -28,16 +27,6 @@ QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_BIND != MPOL_BIND);
|
||||
QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE);
|
||||
#endif
|
||||
|
||||
char *
|
||||
host_memory_backend_get_name(HostMemoryBackend *backend)
|
||||
{
|
||||
if (!backend->use_canonical_path) {
|
||||
return object_get_canonical_path_component(OBJECT(backend));
|
||||
}
|
||||
|
||||
return object_get_canonical_path(OBJECT(backend));
|
||||
}
|
||||
|
||||
static void
|
||||
host_memory_backend_get_size(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
@@ -57,8 +46,7 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name,
|
||||
uint64_t value;
|
||||
|
||||
if (host_memory_backend_mr_inited(backend)) {
|
||||
error_setg(&local_err, "cannot change property %s of %s ",
|
||||
name, object_get_typename(obj));
|
||||
error_setg(&local_err, "cannot change property value");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -67,9 +55,8 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name,
|
||||
goto out;
|
||||
}
|
||||
if (!value) {
|
||||
error_setg(&local_err,
|
||||
"property '%s' of %s doesn't take value '%" PRIu64 "'",
|
||||
name, object_get_typename(obj), value);
|
||||
error_setg(&local_err, "Property '%s.%s' doesn't take value '%"
|
||||
PRIu64 "'", object_get_typename(obj), name, value);
|
||||
goto out;
|
||||
}
|
||||
backend->size = value;
|
||||
@@ -88,7 +75,7 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name,
|
||||
|
||||
value = find_first_bit(backend->host_nodes, MAX_NODES);
|
||||
if (value == MAX_NODES) {
|
||||
goto ret;
|
||||
return;
|
||||
}
|
||||
|
||||
*node = g_malloc0(sizeof(**node));
|
||||
@@ -106,7 +93,6 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name,
|
||||
node = &(*node)->next;
|
||||
} while (true);
|
||||
|
||||
ret:
|
||||
visit_type_uint16List(v, name, &host_nodes, errp);
|
||||
}
|
||||
|
||||
@@ -116,23 +102,14 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name,
|
||||
{
|
||||
#ifdef CONFIG_NUMA
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
uint16List *l, *host_nodes = NULL;
|
||||
uint16List *l = NULL;
|
||||
|
||||
visit_type_uint16List(v, name, &host_nodes, errp);
|
||||
visit_type_uint16List(v, name, &l, errp);
|
||||
|
||||
for (l = host_nodes; l; l = l->next) {
|
||||
if (l->value >= MAX_NODES) {
|
||||
error_setg(errp, "Invalid host-nodes value: %d", l->value);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
for (l = host_nodes; l; l = l->next) {
|
||||
while (l) {
|
||||
bitmap_set(backend->host_nodes, l->value, 1);
|
||||
l = l->next;
|
||||
}
|
||||
|
||||
out:
|
||||
qapi_free_uint16List(host_nodes);
|
||||
#else
|
||||
error_setg(errp, "NUMA node binding are not supported by this QEMU");
|
||||
#endif
|
||||
@@ -222,7 +199,6 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value,
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
|
||||
if (backend->force_prealloc) {
|
||||
if (value) {
|
||||
@@ -242,7 +218,7 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value,
|
||||
void *ptr = memory_region_get_ram_ptr(&backend->mr);
|
||||
uint64_t sz = memory_region_size(&backend->mr);
|
||||
|
||||
os_mem_prealloc(fd, ptr, sz, ms->smp.cpus, &local_err);
|
||||
os_mem_prealloc(fd, ptr, sz, smp_cpus, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
@@ -261,11 +237,6 @@ static void host_memory_backend_init(Object *obj)
|
||||
backend->prealloc = mem_prealloc;
|
||||
}
|
||||
|
||||
static void host_memory_backend_post_init(Object *obj)
|
||||
{
|
||||
object_apply_compat_props(obj);
|
||||
}
|
||||
|
||||
bool host_memory_backend_mr_inited(HostMemoryBackend *backend)
|
||||
{
|
||||
/*
|
||||
@@ -275,7 +246,8 @@ bool host_memory_backend_mr_inited(HostMemoryBackend *backend)
|
||||
return memory_region_size(&backend->mr) != 0;
|
||||
}
|
||||
|
||||
MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend)
|
||||
MemoryRegion *
|
||||
host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp)
|
||||
{
|
||||
return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL;
|
||||
}
|
||||
@@ -290,29 +262,11 @@ bool host_memory_backend_is_mapped(HostMemoryBackend *backend)
|
||||
return backend->is_mapped;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
size_t host_memory_backend_pagesize(HostMemoryBackend *memdev)
|
||||
{
|
||||
Object *obj = OBJECT(memdev);
|
||||
char *path = object_property_get_str(obj, "mem-path", NULL);
|
||||
size_t pagesize = qemu_mempath_getpagesize(path);
|
||||
|
||||
g_free(path);
|
||||
return pagesize;
|
||||
}
|
||||
#else
|
||||
size_t host_memory_backend_pagesize(HostMemoryBackend *memdev)
|
||||
{
|
||||
return getpagesize();
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(uc);
|
||||
HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(uc);
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
Error *local_err = NULL;
|
||||
void *ptr;
|
||||
uint64_t sz;
|
||||
@@ -377,7 +331,7 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
|
||||
*/
|
||||
if (backend->prealloc) {
|
||||
os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz,
|
||||
ms->smp.cpus, &local_err);
|
||||
smp_cpus, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
@@ -397,6 +351,24 @@ host_memory_backend_can_be_deleted(UserCreatable *uc)
|
||||
}
|
||||
}
|
||||
|
||||
static char *get_id(Object *o, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||
|
||||
return g_strdup(backend->id);
|
||||
}
|
||||
|
||||
static void set_id(Object *o, const char *str, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||
|
||||
if (backend->id) {
|
||||
error_setg(errp, "cannot change property value");
|
||||
return;
|
||||
}
|
||||
backend->id = g_strdup(str);
|
||||
}
|
||||
|
||||
static bool host_memory_backend_get_share(Object *o, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||
@@ -415,23 +387,6 @@ static void host_memory_backend_set_share(Object *o, bool value, Error **errp)
|
||||
backend->share = value;
|
||||
}
|
||||
|
||||
static bool
|
||||
host_memory_backend_get_use_canonical_path(Object *obj, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
|
||||
return backend->use_canonical_path;
|
||||
}
|
||||
|
||||
static void
|
||||
host_memory_backend_set_use_canonical_path(Object *obj, bool value,
|
||||
Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
|
||||
backend->use_canonical_path = value;
|
||||
}
|
||||
|
||||
static void
|
||||
host_memory_backend_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
@@ -443,44 +398,34 @@ host_memory_backend_class_init(ObjectClass *oc, void *data)
|
||||
object_class_property_add_bool(oc, "merge",
|
||||
host_memory_backend_get_merge,
|
||||
host_memory_backend_set_merge, &error_abort);
|
||||
object_class_property_set_description(oc, "merge",
|
||||
"Mark memory as mergeable", &error_abort);
|
||||
object_class_property_add_bool(oc, "dump",
|
||||
host_memory_backend_get_dump,
|
||||
host_memory_backend_set_dump, &error_abort);
|
||||
object_class_property_set_description(oc, "dump",
|
||||
"Set to 'off' to exclude from core dump", &error_abort);
|
||||
object_class_property_add_bool(oc, "prealloc",
|
||||
host_memory_backend_get_prealloc,
|
||||
host_memory_backend_set_prealloc, &error_abort);
|
||||
object_class_property_set_description(oc, "prealloc",
|
||||
"Preallocate memory", &error_abort);
|
||||
object_class_property_add(oc, "size", "int",
|
||||
host_memory_backend_get_size,
|
||||
host_memory_backend_set_size,
|
||||
NULL, NULL, &error_abort);
|
||||
object_class_property_set_description(oc, "size",
|
||||
"Size of the memory region (ex: 500M)", &error_abort);
|
||||
object_class_property_add(oc, "host-nodes", "int",
|
||||
host_memory_backend_get_host_nodes,
|
||||
host_memory_backend_set_host_nodes,
|
||||
NULL, NULL, &error_abort);
|
||||
object_class_property_set_description(oc, "host-nodes",
|
||||
"Binds memory to the list of NUMA host nodes", &error_abort);
|
||||
object_class_property_add_enum(oc, "policy", "HostMemPolicy",
|
||||
&HostMemPolicy_lookup,
|
||||
host_memory_backend_get_policy,
|
||||
host_memory_backend_set_policy, &error_abort);
|
||||
object_class_property_set_description(oc, "policy",
|
||||
"Set the NUMA policy", &error_abort);
|
||||
object_class_property_add_str(oc, "id", get_id, set_id, &error_abort);
|
||||
object_class_property_add_bool(oc, "share",
|
||||
host_memory_backend_get_share, host_memory_backend_set_share,
|
||||
&error_abort);
|
||||
object_class_property_set_description(oc, "share",
|
||||
"Mark the memory as private to QEMU or shared", &error_abort);
|
||||
object_class_property_add_bool(oc, "x-use-canonical-path-for-ramblock-id",
|
||||
host_memory_backend_get_use_canonical_path,
|
||||
host_memory_backend_set_use_canonical_path, &error_abort);
|
||||
}
|
||||
|
||||
static void host_memory_backend_finalize(Object *o)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||
g_free(backend->id);
|
||||
}
|
||||
|
||||
static const TypeInfo host_memory_backend_info = {
|
||||
@@ -491,7 +436,7 @@ static const TypeInfo host_memory_backend_info = {
|
||||
.class_init = host_memory_backend_class_init,
|
||||
.instance_size = sizeof(HostMemoryBackend),
|
||||
.instance_init = host_memory_backend_init,
|
||||
.instance_post_init = host_memory_backend_post_init,
|
||||
.instance_finalize = host_memory_backend_finalize,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "chardev/char-fe.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
#define TYPE_RNG_EGD "rng-egd"
|
||||
#define RNG_EGD(obj) OBJECT_CHECK(RngEgd, (obj), TYPE_RNG_EGD)
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
struct RngRandom
|
||||
{
|
||||
@@ -113,7 +112,7 @@ static void rng_random_init(Object *obj)
|
||||
rng_random_set_filename,
|
||||
NULL);
|
||||
|
||||
s->filename = g_strdup("/dev/urandom");
|
||||
s->filename = g_strdup("/dev/random");
|
||||
s->fd = -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "sysemu/rng.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
|
||||
void rng_backend_request_entropy(RngBackend *s, size_t size,
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "sysemu/tpm.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/module.h"
|
||||
#include "block/thread-pool.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
|
||||
@@ -1,209 +0,0 @@
|
||||
/*
|
||||
* QEMU vhost-user backend
|
||||
*
|
||||
* Copyright (C) 2018 Red Hat Inc
|
||||
*
|
||||
* Authors:
|
||||
* Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/qdev.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "sysemu/vhost-user-backend.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "io/channel-command.h"
|
||||
#include "hw/virtio/virtio-bus.h"
|
||||
|
||||
static bool
|
||||
ioeventfd_enabled(void)
|
||||
{
|
||||
return kvm_enabled() && kvm_eventfds_enabled();
|
||||
}
|
||||
|
||||
int
|
||||
vhost_user_backend_dev_init(VhostUserBackend *b, VirtIODevice *vdev,
|
||||
unsigned nvqs, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(!b->vdev && vdev);
|
||||
|
||||
if (!ioeventfd_enabled()) {
|
||||
error_setg(errp, "vhost initialization failed: requires kvm");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!vhost_user_init(&b->vhost_user, &b->chr, errp)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
b->vdev = vdev;
|
||||
b->dev.nvqs = nvqs;
|
||||
b->dev.vqs = g_new(struct vhost_virtqueue, nvqs);
|
||||
|
||||
ret = vhost_dev_init(&b->dev, &b->vhost_user, VHOST_BACKEND_TYPE_USER, 0);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "vhost initialization failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
vhost_user_backend_start(VhostUserBackend *b)
|
||||
{
|
||||
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(b->vdev)));
|
||||
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
|
||||
int ret, i ;
|
||||
|
||||
if (b->started) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!k->set_guest_notifiers) {
|
||||
error_report("binding does not support guest notifiers");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = vhost_dev_enable_notifiers(&b->dev, b->vdev);
|
||||
if (ret < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ret = k->set_guest_notifiers(qbus->parent, b->dev.nvqs, true);
|
||||
if (ret < 0) {
|
||||
error_report("Error binding guest notifier");
|
||||
goto err_host_notifiers;
|
||||
}
|
||||
|
||||
b->dev.acked_features = b->vdev->guest_features;
|
||||
ret = vhost_dev_start(&b->dev, b->vdev);
|
||||
if (ret < 0) {
|
||||
error_report("Error start vhost dev");
|
||||
goto err_guest_notifiers;
|
||||
}
|
||||
|
||||
/* guest_notifier_mask/pending not used yet, so just unmask
|
||||
* everything here. virtio-pci will do the right thing by
|
||||
* enabling/disabling irqfd.
|
||||
*/
|
||||
for (i = 0; i < b->dev.nvqs; i++) {
|
||||
vhost_virtqueue_mask(&b->dev, b->vdev,
|
||||
b->dev.vq_index + i, false);
|
||||
}
|
||||
|
||||
b->started = true;
|
||||
return;
|
||||
|
||||
err_guest_notifiers:
|
||||
k->set_guest_notifiers(qbus->parent, b->dev.nvqs, false);
|
||||
err_host_notifiers:
|
||||
vhost_dev_disable_notifiers(&b->dev, b->vdev);
|
||||
}
|
||||
|
||||
void
|
||||
vhost_user_backend_stop(VhostUserBackend *b)
|
||||
{
|
||||
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(b->vdev)));
|
||||
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
|
||||
int ret = 0;
|
||||
|
||||
if (!b->started) {
|
||||
return;
|
||||
}
|
||||
|
||||
vhost_dev_stop(&b->dev, b->vdev);
|
||||
|
||||
if (k->set_guest_notifiers) {
|
||||
ret = k->set_guest_notifiers(qbus->parent,
|
||||
b->dev.nvqs, false);
|
||||
if (ret < 0) {
|
||||
error_report("vhost guest notifier cleanup failed: %d", ret);
|
||||
}
|
||||
}
|
||||
assert(ret >= 0);
|
||||
|
||||
vhost_dev_disable_notifiers(&b->dev, b->vdev);
|
||||
b->started = false;
|
||||
}
|
||||
|
||||
static void set_chardev(Object *obj, const char *value, Error **errp)
|
||||
{
|
||||
VhostUserBackend *b = VHOST_USER_BACKEND(obj);
|
||||
Chardev *chr;
|
||||
|
||||
if (b->completed) {
|
||||
error_setg(errp, QERR_PERMISSION_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
g_free(b->chr_name);
|
||||
b->chr_name = g_strdup(value);
|
||||
|
||||
chr = qemu_chr_find(b->chr_name);
|
||||
if (chr == NULL) {
|
||||
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
|
||||
"Chardev '%s' not found", b->chr_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!qemu_chr_fe_init(&b->chr, chr, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
b->completed = true;
|
||||
/* could call vhost_dev_init() so early message can be exchanged */
|
||||
}
|
||||
|
||||
static char *get_chardev(Object *obj, Error **errp)
|
||||
{
|
||||
VhostUserBackend *b = VHOST_USER_BACKEND(obj);
|
||||
Chardev *chr = qemu_chr_fe_get_driver(&b->chr);
|
||||
|
||||
if (chr && chr->label) {
|
||||
return g_strdup(chr->label);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void vhost_user_backend_init(Object *obj)
|
||||
{
|
||||
object_property_add_str(obj, "chardev", get_chardev, set_chardev, NULL);
|
||||
}
|
||||
|
||||
static void vhost_user_backend_finalize(Object *obj)
|
||||
{
|
||||
VhostUserBackend *b = VHOST_USER_BACKEND(obj);
|
||||
|
||||
g_free(b->dev.vqs);
|
||||
g_free(b->chr_name);
|
||||
|
||||
vhost_user_cleanup(&b->vhost_user);
|
||||
qemu_chr_fe_deinit(&b->chr, true);
|
||||
}
|
||||
|
||||
static const TypeInfo vhost_user_backend_info = {
|
||||
.name = TYPE_VHOST_USER_BACKEND,
|
||||
.parent = TYPE_OBJECT,
|
||||
.instance_size = sizeof(VhostUserBackend),
|
||||
.instance_init = vhost_user_backend_init,
|
||||
.instance_finalize = vhost_user_backend_finalize,
|
||||
.class_size = sizeof(VhostUserBackendClass),
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&vhost_user_backend_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
14
balloon.c
14
balloon.c
@@ -25,7 +25,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/atomic.h"
|
||||
#include "qemu-common.h"
|
||||
#include "exec/cpu-common.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/balloon.h"
|
||||
@@ -37,22 +37,16 @@
|
||||
static QEMUBalloonEvent *balloon_event_fn;
|
||||
static QEMUBalloonStatus *balloon_stat_fn;
|
||||
static void *balloon_opaque;
|
||||
static int balloon_inhibit_count;
|
||||
static bool balloon_inhibited;
|
||||
|
||||
bool qemu_balloon_is_inhibited(void)
|
||||
{
|
||||
return atomic_read(&balloon_inhibit_count) > 0;
|
||||
return balloon_inhibited;
|
||||
}
|
||||
|
||||
void qemu_balloon_inhibit(bool state)
|
||||
{
|
||||
if (state) {
|
||||
atomic_inc(&balloon_inhibit_count);
|
||||
} else {
|
||||
atomic_dec(&balloon_inhibit_count);
|
||||
}
|
||||
|
||||
assert(atomic_read(&balloon_inhibit_count) >= 0);
|
||||
balloon_inhibited = state;
|
||||
}
|
||||
|
||||
static bool have_balloon(Error **errp)
|
||||
|
||||
@@ -1,19 +1,10 @@
|
||||
block-obj-y += raw-format.o vmdk.o vpc.o
|
||||
block-obj-$(CONFIG_QCOW1) += qcow.o
|
||||
block-obj-$(CONFIG_VDI) += vdi.o
|
||||
block-obj-$(CONFIG_CLOOP) += cloop.o
|
||||
block-obj-$(CONFIG_BOCHS) += bochs.o
|
||||
block-obj-$(CONFIG_VVFAT) += vvfat.o
|
||||
block-obj-$(CONFIG_DMG) += dmg.o
|
||||
|
||||
block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o qcow2-threads.o
|
||||
block-obj-$(CONFIG_QED) += qed.o qed-l2-cache.o qed-table.o qed-cluster.o
|
||||
block-obj-$(CONFIG_QED) += qed-check.o
|
||||
block-obj-y += raw-format.o qcow.o vdi.o vmdk.o cloop.o bochs.o vpc.o vvfat.o dmg.o
|
||||
block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o
|
||||
block-obj-y += qed.o qed-l2-cache.o qed-table.o qed-cluster.o
|
||||
block-obj-y += qed-check.o
|
||||
block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o
|
||||
block-obj-y += quorum.o
|
||||
block-obj-y += blkdebug.o blkverify.o blkreplay.o
|
||||
block-obj-$(CONFIG_PARALLELS) += parallels.o
|
||||
block-obj-y += blklogwrites.o
|
||||
block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o
|
||||
block-obj-y += block-backend.o snapshot.o qapi.o
|
||||
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
|
||||
block-obj-$(CONFIG_POSIX) += file-posix.o
|
||||
@@ -22,8 +13,7 @@ block-obj-y += null.o mirror.o commit.o io.o create.o
|
||||
block-obj-y += throttle-groups.o
|
||||
block-obj-$(CONFIG_LINUX) += nvme.o
|
||||
|
||||
block-obj-y += nbd.o
|
||||
block-obj-$(CONFIG_SHEEPDOG) += sheepdog.o
|
||||
block-obj-y += nbd.o nbd-client.o sheepdog.o
|
||||
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
|
||||
block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o
|
||||
block-obj-$(CONFIG_LIBNFS) += nfs.o
|
||||
@@ -31,12 +21,12 @@ block-obj-$(CONFIG_CURL) += curl.o
|
||||
block-obj-$(CONFIG_RBD) += rbd.o
|
||||
block-obj-$(CONFIG_GLUSTERFS) += gluster.o
|
||||
block-obj-$(CONFIG_VXHS) += vxhs.o
|
||||
block-obj-$(CONFIG_LIBSSH) += ssh.o
|
||||
block-obj-$(CONFIG_LIBSSH2) += ssh.o
|
||||
block-obj-y += accounting.o dirty-bitmap.o
|
||||
block-obj-y += write-threshold.o
|
||||
block-obj-y += backup.o
|
||||
block-obj-$(CONFIG_REPLICATION) += replication.o
|
||||
block-obj-y += throttle.o copy-on-read.o
|
||||
block-obj-y += throttle.o
|
||||
|
||||
block-obj-y += crypto.o
|
||||
|
||||
@@ -52,13 +42,10 @@ rbd.o-libs := $(RBD_LIBS)
|
||||
gluster.o-cflags := $(GLUSTERFS_CFLAGS)
|
||||
gluster.o-libs := $(GLUSTERFS_LIBS)
|
||||
vxhs.o-libs := $(VXHS_LIBS)
|
||||
ssh.o-cflags := $(LIBSSH_CFLAGS)
|
||||
ssh.o-libs := $(LIBSSH_LIBS)
|
||||
block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o
|
||||
block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y)
|
||||
ssh.o-cflags := $(LIBSSH2_CFLAGS)
|
||||
ssh.o-libs := $(LIBSSH2_LIBS)
|
||||
block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o
|
||||
dmg-bz2.o-libs := $(BZIP2_LIBS)
|
||||
block-obj-$(if $(CONFIG_LZFSE),m,n) += dmg-lzfse.o
|
||||
dmg-lzfse.o-libs := $(LZFSE_LIBS)
|
||||
qcow.o-libs := -lz
|
||||
linux-aio.o-libs := -laio
|
||||
parallels.o-cflags := $(LIBXML2_CFLAGS)
|
||||
|
||||
@@ -94,94 +94,6 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
cookie->type = type;
|
||||
}
|
||||
|
||||
/* block_latency_histogram_compare_func:
|
||||
* Compare @key with interval [@it[0], @it[1]).
|
||||
* Return: -1 if @key < @it[0]
|
||||
* 0 if @key in [@it[0], @it[1])
|
||||
* +1 if @key >= @it[1]
|
||||
*/
|
||||
static int block_latency_histogram_compare_func(const void *key, const void *it)
|
||||
{
|
||||
uint64_t k = *(uint64_t *)key;
|
||||
uint64_t a = ((uint64_t *)it)[0];
|
||||
uint64_t b = ((uint64_t *)it)[1];
|
||||
|
||||
return k < a ? -1 : (k < b ? 0 : 1);
|
||||
}
|
||||
|
||||
static void block_latency_histogram_account(BlockLatencyHistogram *hist,
|
||||
int64_t latency_ns)
|
||||
{
|
||||
uint64_t *pos;
|
||||
|
||||
if (hist->bins == NULL) {
|
||||
/* histogram disabled */
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (latency_ns < hist->boundaries[0]) {
|
||||
hist->bins[0]++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (latency_ns >= hist->boundaries[hist->nbins - 2]) {
|
||||
hist->bins[hist->nbins - 1]++;
|
||||
return;
|
||||
}
|
||||
|
||||
pos = bsearch(&latency_ns, hist->boundaries, hist->nbins - 2,
|
||||
sizeof(hist->boundaries[0]),
|
||||
block_latency_histogram_compare_func);
|
||||
assert(pos != NULL);
|
||||
|
||||
hist->bins[pos - hist->boundaries + 1]++;
|
||||
}
|
||||
|
||||
int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type,
|
||||
uint64List *boundaries)
|
||||
{
|
||||
BlockLatencyHistogram *hist = &stats->latency_histogram[type];
|
||||
uint64List *entry;
|
||||
uint64_t *ptr;
|
||||
uint64_t prev = 0;
|
||||
int new_nbins = 1;
|
||||
|
||||
for (entry = boundaries; entry; entry = entry->next) {
|
||||
if (entry->value <= prev) {
|
||||
return -EINVAL;
|
||||
}
|
||||
new_nbins++;
|
||||
prev = entry->value;
|
||||
}
|
||||
|
||||
hist->nbins = new_nbins;
|
||||
g_free(hist->boundaries);
|
||||
hist->boundaries = g_new(uint64_t, hist->nbins - 1);
|
||||
for (entry = boundaries, ptr = hist->boundaries; entry;
|
||||
entry = entry->next, ptr++)
|
||||
{
|
||||
*ptr = entry->value;
|
||||
}
|
||||
|
||||
g_free(hist->bins);
|
||||
hist->bins = g_new0(uint64_t, hist->nbins);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void block_latency_histograms_clear(BlockAcctStats *stats)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BLOCK_MAX_IOTYPE; i++) {
|
||||
BlockLatencyHistogram *hist = &stats->latency_histogram[i];
|
||||
g_free(hist->bins);
|
||||
g_free(hist->boundaries);
|
||||
memset(hist, 0, sizeof(*hist));
|
||||
}
|
||||
}
|
||||
|
||||
static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
bool failed)
|
||||
{
|
||||
@@ -204,9 +116,6 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
stats->nr_ops[cookie->type]++;
|
||||
}
|
||||
|
||||
block_latency_histogram_account(&stats->latency_histogram[cookie->type],
|
||||
latency_ns);
|
||||
|
||||
if (!failed || stats->account_failed) {
|
||||
stats->total_time_ns[cookie->type] += latency_ns;
|
||||
stats->last_access_time_ns = time_ns;
|
||||
|
||||
567
block/backup.c
567
block/backup.c
@@ -27,13 +27,7 @@
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
|
||||
|
||||
typedef struct CowRequest {
|
||||
int64_t start_byte;
|
||||
int64_t end_byte;
|
||||
QLIST_ENTRY(CowRequest) list;
|
||||
CoQueue wait_queue; /* coroutines blocked on this request */
|
||||
} CowRequest;
|
||||
#define SLICE_TIME 100000000ULL /* ns */
|
||||
|
||||
typedef struct BackupBlockJob {
|
||||
BlockJob common;
|
||||
@@ -41,10 +35,10 @@ typedef struct BackupBlockJob {
|
||||
/* bitmap for sync=incremental */
|
||||
BdrvDirtyBitmap *sync_bitmap;
|
||||
MirrorSyncMode sync_mode;
|
||||
RateLimit limit;
|
||||
BlockdevOnError on_source_error;
|
||||
BlockdevOnError on_target_error;
|
||||
CoRwlock flush_rwlock;
|
||||
uint64_t len;
|
||||
uint64_t bytes_read;
|
||||
int64_t cluster_size;
|
||||
bool compress;
|
||||
@@ -52,14 +46,8 @@ typedef struct BackupBlockJob {
|
||||
QLIST_HEAD(, CowRequest) inflight_reqs;
|
||||
|
||||
HBitmap *copy_bitmap;
|
||||
bool use_copy_range;
|
||||
int64_t copy_range_size;
|
||||
|
||||
bool serialize_target_writes;
|
||||
} BackupBlockJob;
|
||||
|
||||
static const BlockJobDriver backup_job_driver;
|
||||
|
||||
/* See if in-flight requests overlap and wait for them to complete */
|
||||
static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
|
||||
int64_t start,
|
||||
@@ -97,99 +85,19 @@ static void cow_request_end(CowRequest *req)
|
||||
qemu_co_queue_restart_all(&req->wait_queue);
|
||||
}
|
||||
|
||||
/* Copy range to target with a bounce buffer and return the bytes copied. If
|
||||
* error occurred, return a negative error number */
|
||||
static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
|
||||
int64_t start,
|
||||
int64_t end,
|
||||
bool is_write_notifier,
|
||||
bool *error_is_read,
|
||||
void **bounce_buffer)
|
||||
{
|
||||
int ret;
|
||||
BlockBackend *blk = job->common.blk;
|
||||
int nbytes;
|
||||
int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
|
||||
int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0;
|
||||
|
||||
assert(QEMU_IS_ALIGNED(start, job->cluster_size));
|
||||
hbitmap_reset(job->copy_bitmap, start, job->cluster_size);
|
||||
nbytes = MIN(job->cluster_size, job->len - start);
|
||||
if (!*bounce_buffer) {
|
||||
*bounce_buffer = blk_blockalign(blk, job->cluster_size);
|
||||
}
|
||||
|
||||
ret = blk_co_pread(blk, start, nbytes, *bounce_buffer, read_flags);
|
||||
if (ret < 0) {
|
||||
trace_backup_do_cow_read_fail(job, start, ret);
|
||||
if (error_is_read) {
|
||||
*error_is_read = true;
|
||||
}
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (buffer_is_zero(*bounce_buffer, nbytes)) {
|
||||
ret = blk_co_pwrite_zeroes(job->target, start,
|
||||
nbytes, write_flags | BDRV_REQ_MAY_UNMAP);
|
||||
} else {
|
||||
ret = blk_co_pwrite(job->target, start,
|
||||
nbytes, *bounce_buffer, write_flags |
|
||||
(job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0));
|
||||
}
|
||||
if (ret < 0) {
|
||||
trace_backup_do_cow_write_fail(job, start, ret);
|
||||
if (error_is_read) {
|
||||
*error_is_read = false;
|
||||
}
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return nbytes;
|
||||
fail:
|
||||
hbitmap_set(job->copy_bitmap, start, job->cluster_size);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/* Copy range to target and return the bytes copied. If error occurred, return a
|
||||
* negative error number. */
|
||||
static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job,
|
||||
int64_t start,
|
||||
int64_t end,
|
||||
bool is_write_notifier)
|
||||
{
|
||||
int ret;
|
||||
int nr_clusters;
|
||||
BlockBackend *blk = job->common.blk;
|
||||
int nbytes;
|
||||
int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
|
||||
int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0;
|
||||
|
||||
assert(QEMU_IS_ALIGNED(job->copy_range_size, job->cluster_size));
|
||||
assert(QEMU_IS_ALIGNED(start, job->cluster_size));
|
||||
nbytes = MIN(job->copy_range_size, end - start);
|
||||
nr_clusters = DIV_ROUND_UP(nbytes, job->cluster_size);
|
||||
hbitmap_reset(job->copy_bitmap, start, job->cluster_size * nr_clusters);
|
||||
ret = blk_co_copy_range(blk, start, job->target, start, nbytes,
|
||||
read_flags, write_flags);
|
||||
if (ret < 0) {
|
||||
trace_backup_do_cow_copy_range_fail(job, start, ret);
|
||||
hbitmap_set(job->copy_bitmap, start, job->cluster_size * nr_clusters);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
||||
int64_t offset, uint64_t bytes,
|
||||
bool *error_is_read,
|
||||
bool is_write_notifier)
|
||||
{
|
||||
BlockBackend *blk = job->common.blk;
|
||||
CowRequest cow_request;
|
||||
struct iovec iov;
|
||||
QEMUIOVector bounce_qiov;
|
||||
void *bounce_buffer = NULL;
|
||||
int ret = 0;
|
||||
int64_t start, end; /* bytes */
|
||||
void *bounce_buffer = NULL;
|
||||
int n; /* bytes */
|
||||
|
||||
qemu_co_rwlock_rdlock(&job->flush_rwlock);
|
||||
|
||||
@@ -201,38 +109,60 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
||||
wait_for_overlapping_requests(job, start, end);
|
||||
cow_request_begin(&cow_request, job, start, end);
|
||||
|
||||
while (start < end) {
|
||||
if (!hbitmap_get(job->copy_bitmap, start)) {
|
||||
for (; start < end; start += job->cluster_size) {
|
||||
if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) {
|
||||
trace_backup_do_cow_skip(job, start);
|
||||
start += job->cluster_size;
|
||||
continue; /* already copied */
|
||||
}
|
||||
hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1);
|
||||
|
||||
trace_backup_do_cow_process(job, start);
|
||||
|
||||
if (job->use_copy_range) {
|
||||
ret = backup_cow_with_offload(job, start, end, is_write_notifier);
|
||||
if (ret < 0) {
|
||||
job->use_copy_range = false;
|
||||
}
|
||||
n = MIN(job->cluster_size, job->common.len - start);
|
||||
|
||||
if (!bounce_buffer) {
|
||||
bounce_buffer = blk_blockalign(blk, job->cluster_size);
|
||||
}
|
||||
if (!job->use_copy_range) {
|
||||
ret = backup_cow_with_bounce_buffer(job, start, end, is_write_notifier,
|
||||
error_is_read, &bounce_buffer);
|
||||
iov.iov_base = bounce_buffer;
|
||||
iov.iov_len = n;
|
||||
qemu_iovec_init_external(&bounce_qiov, &iov, 1);
|
||||
|
||||
ret = blk_co_preadv(blk, start, bounce_qiov.size, &bounce_qiov,
|
||||
is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0);
|
||||
if (ret < 0) {
|
||||
trace_backup_do_cow_read_fail(job, start, ret);
|
||||
if (error_is_read) {
|
||||
*error_is_read = true;
|
||||
}
|
||||
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
|
||||
ret = blk_co_pwrite_zeroes(job->target, start,
|
||||
bounce_qiov.size, BDRV_REQ_MAY_UNMAP);
|
||||
} else {
|
||||
ret = blk_co_pwritev(job->target, start,
|
||||
bounce_qiov.size, &bounce_qiov,
|
||||
job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
|
||||
}
|
||||
if (ret < 0) {
|
||||
break;
|
||||
trace_backup_do_cow_write_fail(job, start, ret);
|
||||
if (error_is_read) {
|
||||
*error_is_read = false;
|
||||
}
|
||||
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Publish progress, guest I/O counts as progress too. Note that the
|
||||
* offset field is an opaque progress value, it is not a disk offset.
|
||||
*/
|
||||
start += ret;
|
||||
job->bytes_read += ret;
|
||||
job_progress_update(&job->common.job, ret);
|
||||
ret = 0;
|
||||
job->bytes_read += n;
|
||||
job->common.offset += n;
|
||||
}
|
||||
|
||||
out:
|
||||
if (bounce_buffer) {
|
||||
qemu_vfree(bounce_buffer);
|
||||
}
|
||||
@@ -260,12 +190,23 @@ static int coroutine_fn backup_before_write_notify(
|
||||
return backup_do_cow(job, req->offset, req->bytes, NULL, true);
|
||||
}
|
||||
|
||||
static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
|
||||
|
||||
if (speed < 0) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER, "speed");
|
||||
return;
|
||||
}
|
||||
ratelimit_set_speed(&s->limit, speed, SLICE_TIME);
|
||||
}
|
||||
|
||||
static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
|
||||
{
|
||||
BdrvDirtyBitmap *bm;
|
||||
BlockDriverState *bs = blk_bs(job->common.blk);
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret < 0 || block_job_is_cancelled(&job->common)) {
|
||||
/* Merge the successor back into the parent, delete nothing. */
|
||||
bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL);
|
||||
assert(bm);
|
||||
@@ -276,40 +217,43 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
|
||||
}
|
||||
}
|
||||
|
||||
static void backup_commit(Job *job)
|
||||
static void backup_commit(BlockJob *job)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
|
||||
if (s->sync_bitmap) {
|
||||
backup_cleanup_sync_bitmap(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void backup_abort(Job *job)
|
||||
static void backup_abort(BlockJob *job)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
|
||||
if (s->sync_bitmap) {
|
||||
backup_cleanup_sync_bitmap(s, -1);
|
||||
}
|
||||
}
|
||||
|
||||
static void backup_clean(Job *job)
|
||||
static void backup_clean(BlockJob *job)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
|
||||
assert(s->target);
|
||||
blk_unref(s->target);
|
||||
s->target = NULL;
|
||||
}
|
||||
|
||||
if (s->copy_bitmap) {
|
||||
hbitmap_free(s->copy_bitmap);
|
||||
s->copy_bitmap = NULL;
|
||||
}
|
||||
static void backup_attached_aio_context(BlockJob *job, AioContext *aio_context)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
|
||||
|
||||
blk_set_aio_context(s->target, aio_context);
|
||||
}
|
||||
|
||||
void backup_do_checkpoint(BlockJob *job, Error **errp)
|
||||
{
|
||||
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
|
||||
int64_t len;
|
||||
|
||||
assert(block_job_driver(job) == &backup_job_driver);
|
||||
assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP);
|
||||
|
||||
if (backup_job->sync_mode != MIRROR_SYNC_MODE_NONE) {
|
||||
error_setg(errp, "The backup job only supports block checkpoint in"
|
||||
@@ -317,7 +261,39 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
hbitmap_set(backup_job->copy_bitmap, 0, backup_job->len);
|
||||
len = DIV_ROUND_UP(backup_job->common.len, backup_job->cluster_size);
|
||||
hbitmap_set(backup_job->copy_bitmap, 0, len);
|
||||
}
|
||||
|
||||
void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset,
|
||||
uint64_t bytes)
|
||||
{
|
||||
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
|
||||
int64_t start, end;
|
||||
|
||||
assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP);
|
||||
|
||||
start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size);
|
||||
end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size);
|
||||
wait_for_overlapping_requests(backup_job, start, end);
|
||||
}
|
||||
|
||||
void backup_cow_request_begin(CowRequest *req, BlockJob *job,
|
||||
int64_t offset, uint64_t bytes)
|
||||
{
|
||||
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
|
||||
int64_t start, end;
|
||||
|
||||
assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP);
|
||||
|
||||
start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size);
|
||||
end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size);
|
||||
cow_request_begin(req, backup_job, start, end);
|
||||
}
|
||||
|
||||
void backup_cow_request_end(CowRequest *req)
|
||||
{
|
||||
cow_request_end(req);
|
||||
}
|
||||
|
||||
static void backup_drain(BlockJob *job)
|
||||
@@ -347,65 +323,57 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job,
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int ret;
|
||||
} BackupCompleteData;
|
||||
|
||||
static void backup_complete(BlockJob *job, void *opaque)
|
||||
{
|
||||
BackupCompleteData *data = opaque;
|
||||
|
||||
block_job_completed(job, data->ret);
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
static bool coroutine_fn yield_and_check(BackupBlockJob *job)
|
||||
{
|
||||
uint64_t delay_ns;
|
||||
|
||||
if (job_is_cancelled(&job->common.job)) {
|
||||
if (block_job_is_cancelled(&job->common)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* We need to yield even for delay_ns = 0 so that bdrv_drain_all() can
|
||||
* return. Without a yield, the VM would not reboot. */
|
||||
delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read);
|
||||
job->bytes_read = 0;
|
||||
job_sleep_ns(&job->common.job, delay_ns);
|
||||
/* we need to yield so that bdrv_drain_all() returns.
|
||||
* (without, VM does not reboot)
|
||||
*/
|
||||
if (job->common.speed) {
|
||||
uint64_t delay_ns = ratelimit_calculate_delay(&job->limit,
|
||||
job->bytes_read);
|
||||
job->bytes_read = 0;
|
||||
block_job_sleep_ns(&job->common, delay_ns);
|
||||
} else {
|
||||
block_job_sleep_ns(&job->common, 0);
|
||||
}
|
||||
|
||||
if (job_is_cancelled(&job->common.job)) {
|
||||
if (block_job_is_cancelled(&job->common)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool bdrv_is_unallocated_range(BlockDriverState *bs,
|
||||
int64_t offset, int64_t bytes)
|
||||
{
|
||||
int64_t end = offset + bytes;
|
||||
|
||||
while (offset < end && !bdrv_is_allocated(bs, offset, bytes, &bytes)) {
|
||||
if (bytes == 0) {
|
||||
return true;
|
||||
}
|
||||
offset += bytes;
|
||||
bytes = end - offset;
|
||||
}
|
||||
|
||||
return offset >= end;
|
||||
}
|
||||
|
||||
static int coroutine_fn backup_loop(BackupBlockJob *job)
|
||||
static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
||||
{
|
||||
int ret;
|
||||
bool error_is_read;
|
||||
int64_t offset;
|
||||
int64_t cluster;
|
||||
HBitmapIter hbi;
|
||||
BlockDriverState *bs = blk_bs(job->common.blk);
|
||||
|
||||
hbitmap_iter_init(&hbi, job->copy_bitmap, 0);
|
||||
while ((offset = hbitmap_iter_next(&hbi)) != -1) {
|
||||
if (job->sync_mode == MIRROR_SYNC_MODE_TOP &&
|
||||
bdrv_is_unallocated_range(bs, offset, job->cluster_size))
|
||||
{
|
||||
hbitmap_reset(job->copy_bitmap, offset, job->cluster_size);
|
||||
continue;
|
||||
}
|
||||
|
||||
while ((cluster = hbitmap_iter_next(&hbi)) != -1) {
|
||||
do {
|
||||
if (yield_and_check(job)) {
|
||||
return 0;
|
||||
}
|
||||
ret = backup_do_cow(job, offset,
|
||||
ret = backup_do_cow(job, cluster * job->cluster_size,
|
||||
job->cluster_size, &error_is_read, false);
|
||||
if (ret < 0 && backup_error_action(job, error_is_read, -ret) ==
|
||||
BLOCK_ERROR_ACTION_REPORT)
|
||||
@@ -421,118 +389,162 @@ static int coroutine_fn backup_loop(BackupBlockJob *job)
|
||||
/* init copy_bitmap from sync_bitmap */
|
||||
static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
|
||||
{
|
||||
uint64_t offset = 0;
|
||||
uint64_t bytes = job->len;
|
||||
BdrvDirtyBitmapIter *dbi;
|
||||
int64_t offset;
|
||||
int64_t end = DIV_ROUND_UP(bdrv_dirty_bitmap_size(job->sync_bitmap),
|
||||
job->cluster_size);
|
||||
|
||||
while (bdrv_dirty_bitmap_next_dirty_area(job->sync_bitmap,
|
||||
&offset, &bytes))
|
||||
{
|
||||
hbitmap_set(job->copy_bitmap, offset, bytes);
|
||||
dbi = bdrv_dirty_iter_new(job->sync_bitmap);
|
||||
while ((offset = bdrv_dirty_iter_next(dbi)) != -1) {
|
||||
int64_t cluster = offset / job->cluster_size;
|
||||
int64_t next_cluster;
|
||||
|
||||
offset += bytes;
|
||||
if (offset >= job->len) {
|
||||
offset += bdrv_dirty_bitmap_granularity(job->sync_bitmap);
|
||||
if (offset >= bdrv_dirty_bitmap_size(job->sync_bitmap)) {
|
||||
hbitmap_set(job->copy_bitmap, cluster, end - cluster);
|
||||
break;
|
||||
}
|
||||
bytes = job->len - offset;
|
||||
|
||||
offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset);
|
||||
if (offset == -1) {
|
||||
hbitmap_set(job->copy_bitmap, cluster, end - cluster);
|
||||
break;
|
||||
}
|
||||
|
||||
next_cluster = DIV_ROUND_UP(offset, job->cluster_size);
|
||||
hbitmap_set(job->copy_bitmap, cluster, next_cluster - cluster);
|
||||
if (next_cluster >= end) {
|
||||
break;
|
||||
}
|
||||
|
||||
bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size);
|
||||
}
|
||||
|
||||
/* TODO job_progress_set_remaining() would make more sense */
|
||||
job_progress_update(&job->common.job,
|
||||
job->len - hbitmap_count(job->copy_bitmap));
|
||||
job->common.offset = job->common.len -
|
||||
hbitmap_count(job->copy_bitmap) * job->cluster_size;
|
||||
|
||||
bdrv_dirty_iter_free(dbi);
|
||||
}
|
||||
|
||||
static int coroutine_fn backup_run(Job *job, Error **errp)
|
||||
static void coroutine_fn backup_run(void *opaque)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
|
||||
BlockDriverState *bs = blk_bs(s->common.blk);
|
||||
BackupBlockJob *job = opaque;
|
||||
BackupCompleteData *data;
|
||||
BlockDriverState *bs = blk_bs(job->common.blk);
|
||||
int64_t offset, nb_clusters;
|
||||
int ret = 0;
|
||||
|
||||
QLIST_INIT(&s->inflight_reqs);
|
||||
qemu_co_rwlock_init(&s->flush_rwlock);
|
||||
QLIST_INIT(&job->inflight_reqs);
|
||||
qemu_co_rwlock_init(&job->flush_rwlock);
|
||||
|
||||
job_progress_set_remaining(job, s->len);
|
||||
|
||||
if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
||||
backup_incremental_init_copy_bitmap(s);
|
||||
nb_clusters = DIV_ROUND_UP(job->common.len, job->cluster_size);
|
||||
job->copy_bitmap = hbitmap_alloc(nb_clusters, 0);
|
||||
if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
||||
backup_incremental_init_copy_bitmap(job);
|
||||
} else {
|
||||
hbitmap_set(s->copy_bitmap, 0, s->len);
|
||||
hbitmap_set(job->copy_bitmap, 0, nb_clusters);
|
||||
}
|
||||
|
||||
s->before_write.notify = backup_before_write_notify;
|
||||
bdrv_add_before_write_notifier(bs, &s->before_write);
|
||||
|
||||
if (s->sync_mode == MIRROR_SYNC_MODE_NONE) {
|
||||
job->before_write.notify = backup_before_write_notify;
|
||||
bdrv_add_before_write_notifier(bs, &job->before_write);
|
||||
|
||||
if (job->sync_mode == MIRROR_SYNC_MODE_NONE) {
|
||||
/* All bits are set in copy_bitmap to allow any cluster to be copied.
|
||||
* This does not actually require them to be copied. */
|
||||
while (!job_is_cancelled(job)) {
|
||||
while (!block_job_is_cancelled(&job->common)) {
|
||||
/* Yield until the job is cancelled. We just let our before_write
|
||||
* notify callback service CoW requests. */
|
||||
job_yield(job);
|
||||
block_job_yield(&job->common);
|
||||
}
|
||||
} else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
||||
ret = backup_run_incremental(job);
|
||||
} else {
|
||||
ret = backup_loop(s);
|
||||
/* Both FULL and TOP SYNC_MODE's require copying.. */
|
||||
for (offset = 0; offset < job->common.len;
|
||||
offset += job->cluster_size) {
|
||||
bool error_is_read;
|
||||
int alloced = 0;
|
||||
|
||||
if (yield_and_check(job)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (job->sync_mode == MIRROR_SYNC_MODE_TOP) {
|
||||
int i;
|
||||
int64_t n;
|
||||
|
||||
/* Check to see if these blocks are already in the
|
||||
* backing file. */
|
||||
|
||||
for (i = 0; i < job->cluster_size;) {
|
||||
/* bdrv_is_allocated() only returns true/false based
|
||||
* on the first set of sectors it comes across that
|
||||
* are are all in the same state.
|
||||
* For that reason we must verify each sector in the
|
||||
* backup cluster length. We end up copying more than
|
||||
* needed but at some point that is always the case. */
|
||||
alloced =
|
||||
bdrv_is_allocated(bs, offset + i,
|
||||
job->cluster_size - i, &n);
|
||||
i += n;
|
||||
|
||||
if (alloced || n == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the above loop never found any sectors that are in
|
||||
* the topmost image, skip this backup. */
|
||||
if (alloced == 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/* FULL sync mode we copy the whole drive. */
|
||||
if (alloced < 0) {
|
||||
ret = alloced;
|
||||
} else {
|
||||
ret = backup_do_cow(job, offset, job->cluster_size,
|
||||
&error_is_read, false);
|
||||
}
|
||||
if (ret < 0) {
|
||||
/* Depending on error action, fail now or retry cluster */
|
||||
BlockErrorAction action =
|
||||
backup_error_action(job, error_is_read, -ret);
|
||||
if (action == BLOCK_ERROR_ACTION_REPORT) {
|
||||
break;
|
||||
} else {
|
||||
offset -= job->cluster_size;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notifier_with_return_remove(&s->before_write);
|
||||
notifier_with_return_remove(&job->before_write);
|
||||
|
||||
/* wait until pending backup_do_cow() calls have completed */
|
||||
qemu_co_rwlock_wrlock(&s->flush_rwlock);
|
||||
qemu_co_rwlock_unlock(&s->flush_rwlock);
|
||||
qemu_co_rwlock_wrlock(&job->flush_rwlock);
|
||||
qemu_co_rwlock_unlock(&job->flush_rwlock);
|
||||
hbitmap_free(job->copy_bitmap);
|
||||
|
||||
return ret;
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
block_job_defer_to_main_loop(&job->common, backup_complete, data);
|
||||
}
|
||||
|
||||
static const BlockJobDriver backup_job_driver = {
|
||||
.job_driver = {
|
||||
.instance_size = sizeof(BackupBlockJob),
|
||||
.job_type = JOB_TYPE_BACKUP,
|
||||
.free = block_job_free,
|
||||
.user_resume = block_job_user_resume,
|
||||
.drain = block_job_drain,
|
||||
.run = backup_run,
|
||||
.commit = backup_commit,
|
||||
.abort = backup_abort,
|
||||
.clean = backup_clean,
|
||||
},
|
||||
.instance_size = sizeof(BackupBlockJob),
|
||||
.job_type = BLOCK_JOB_TYPE_BACKUP,
|
||||
.start = backup_run,
|
||||
.set_speed = backup_set_speed,
|
||||
.commit = backup_commit,
|
||||
.abort = backup_abort,
|
||||
.clean = backup_clean,
|
||||
.attached_aio_context = backup_attached_aio_context,
|
||||
.drain = backup_drain,
|
||||
};
|
||||
|
||||
static int64_t backup_calculate_cluster_size(BlockDriverState *target,
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
BlockDriverInfo bdi;
|
||||
|
||||
/*
|
||||
* If there is no backing file on the target, we cannot rely on COW if our
|
||||
* backup cluster size is smaller than the target cluster size. Even for
|
||||
* targets with a backing file, try to avoid COW if possible.
|
||||
*/
|
||||
ret = bdrv_get_info(target, &bdi);
|
||||
if (ret == -ENOTSUP && !target->backing) {
|
||||
/* Cluster size is not defined */
|
||||
warn_report("The target block device doesn't provide "
|
||||
"information about the block size and it doesn't have a "
|
||||
"backing file. The default block size of %u bytes is "
|
||||
"used. If the actual block size of the target exceeds "
|
||||
"this default, the backup may be unusable",
|
||||
BACKUP_CLUSTER_SIZE_DEFAULT);
|
||||
return BACKUP_CLUSTER_SIZE_DEFAULT;
|
||||
} else if (ret < 0 && !target->backing) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Couldn't determine the cluster size of the target image, "
|
||||
"which has no backing file");
|
||||
error_append_hint(errp,
|
||||
"Aborting, since this may create an unusable destination image\n");
|
||||
return ret;
|
||||
} else if (ret < 0 && target->backing) {
|
||||
/* Not fatal; just trudge on ahead. */
|
||||
return BACKUP_CLUSTER_SIZE_DEFAULT;
|
||||
}
|
||||
|
||||
return MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
|
||||
}
|
||||
|
||||
BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
BlockDriverState *target, int64_t speed,
|
||||
MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
|
||||
@@ -541,13 +553,12 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
BlockdevOnError on_target_error,
|
||||
int creation_flags,
|
||||
BlockCompletionFunc *cb, void *opaque,
|
||||
JobTxn *txn, Error **errp)
|
||||
BlockJobTxn *txn, Error **errp)
|
||||
{
|
||||
int64_t len;
|
||||
BlockDriverInfo bdi;
|
||||
BackupBlockJob *job = NULL;
|
||||
int ret;
|
||||
int64_t cluster_size;
|
||||
HBitmap *copy_bitmap = NULL;
|
||||
|
||||
assert(bs);
|
||||
assert(target);
|
||||
@@ -609,15 +620,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
goto error;
|
||||
}
|
||||
|
||||
cluster_size = backup_calculate_cluster_size(target, errp);
|
||||
if (cluster_size < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
copy_bitmap = hbitmap_alloc(len, ctz32(cluster_size));
|
||||
|
||||
/* job->len is fixed, so we can't allow resize */
|
||||
job = block_job_create(job_id, &backup_job_driver, txn, bs,
|
||||
/* job->common.len is fixed, so we can't allow resize */
|
||||
job = block_job_create(job_id, &backup_job_driver, bs,
|
||||
BLK_PERM_CONSISTENT_READ,
|
||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
|
||||
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD,
|
||||
@@ -627,8 +631,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
}
|
||||
|
||||
/* The target must match the source in size, so no resize here either */
|
||||
job->target = blk_new(job->common.job.aio_context,
|
||||
BLK_PERM_WRITE,
|
||||
job->target = blk_new(BLK_PERM_WRITE,
|
||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
|
||||
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD);
|
||||
ret = blk_insert_bs(job->target, target, errp);
|
||||
@@ -643,36 +646,48 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
sync_bitmap : NULL;
|
||||
job->compress = compress;
|
||||
|
||||
/* Detect image-fleecing (and similar) schemes */
|
||||
job->serialize_target_writes = bdrv_chain_contains(target, bs);
|
||||
job->cluster_size = cluster_size;
|
||||
job->copy_bitmap = copy_bitmap;
|
||||
copy_bitmap = NULL;
|
||||
job->use_copy_range = true;
|
||||
job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk),
|
||||
blk_get_max_transfer(job->target));
|
||||
job->copy_range_size = MAX(job->cluster_size,
|
||||
QEMU_ALIGN_UP(job->copy_range_size,
|
||||
job->cluster_size));
|
||||
/* If there is no backing file on the target, we cannot rely on COW if our
|
||||
* backup cluster size is smaller than the target cluster size. Even for
|
||||
* targets with a backing file, try to avoid COW if possible. */
|
||||
ret = bdrv_get_info(target, &bdi);
|
||||
if (ret == -ENOTSUP && !target->backing) {
|
||||
/* Cluster size is not defined */
|
||||
warn_report("The target block device doesn't provide "
|
||||
"information about the block size and it doesn't have a "
|
||||
"backing file. The default block size of %u bytes is "
|
||||
"used. If the actual block size of the target exceeds "
|
||||
"this default, the backup may be unusable",
|
||||
BACKUP_CLUSTER_SIZE_DEFAULT);
|
||||
job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
|
||||
} else if (ret < 0 && !target->backing) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Couldn't determine the cluster size of the target image, "
|
||||
"which has no backing file");
|
||||
error_append_hint(errp,
|
||||
"Aborting, since this may create an unusable destination image\n");
|
||||
goto error;
|
||||
} else if (ret < 0 && target->backing) {
|
||||
/* Not fatal; just trudge on ahead. */
|
||||
job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
|
||||
} else {
|
||||
job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
|
||||
}
|
||||
|
||||
/* Required permissions are already taken with target's blk_new() */
|
||||
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
|
||||
&error_abort);
|
||||
job->len = len;
|
||||
job->common.len = len;
|
||||
block_job_txn_add_job(txn, &job->common);
|
||||
|
||||
return &job->common;
|
||||
|
||||
error:
|
||||
if (copy_bitmap) {
|
||||
assert(!job || !job->copy_bitmap);
|
||||
hbitmap_free(copy_bitmap);
|
||||
}
|
||||
if (sync_bitmap) {
|
||||
bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL);
|
||||
}
|
||||
if (job) {
|
||||
backup_clean(&job->common.job);
|
||||
job_early_fail(&job->common.job);
|
||||
backup_clean(&job->common);
|
||||
block_job_early_fail(&job->common);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
146
block/blkdebug.c
146
block/blkdebug.c
@@ -75,7 +75,6 @@ typedef struct BlkdebugRule {
|
||||
int state;
|
||||
union {
|
||||
struct {
|
||||
uint64_t iotype_mask;
|
||||
int error;
|
||||
int immediately;
|
||||
int once;
|
||||
@@ -92,9 +91,6 @@ typedef struct BlkdebugRule {
|
||||
QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
|
||||
} BlkdebugRule;
|
||||
|
||||
QEMU_BUILD_BUG_MSG(BLKDEBUG_IO_TYPE__MAX > 64,
|
||||
"BlkdebugIOType mask does not fit into an uint64_t");
|
||||
|
||||
static QemuOptsList inject_error_opts = {
|
||||
.name = "inject-error",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head),
|
||||
@@ -107,10 +103,6 @@ static QemuOptsList inject_error_opts = {
|
||||
.name = "state",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
},
|
||||
{
|
||||
.name = "iotype",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},
|
||||
{
|
||||
.name = "errno",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
@@ -170,8 +162,6 @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
|
||||
int event;
|
||||
struct BlkdebugRule *rule;
|
||||
int64_t sector;
|
||||
BlkdebugIOType iotype;
|
||||
Error *local_error = NULL;
|
||||
|
||||
/* Find the right event for the rule */
|
||||
event_name = qemu_opt_get(opts, "event");
|
||||
@@ -202,26 +192,6 @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
|
||||
sector = qemu_opt_get_number(opts, "sector", -1);
|
||||
rule->options.inject.offset =
|
||||
sector == -1 ? -1 : sector * BDRV_SECTOR_SIZE;
|
||||
|
||||
iotype = qapi_enum_parse(&BlkdebugIOType_lookup,
|
||||
qemu_opt_get(opts, "iotype"),
|
||||
BLKDEBUG_IO_TYPE__MAX, &local_error);
|
||||
if (local_error) {
|
||||
error_propagate(errp, local_error);
|
||||
return -1;
|
||||
}
|
||||
if (iotype != BLKDEBUG_IO_TYPE__MAX) {
|
||||
rule->options.inject.iotype_mask = (1ull << iotype);
|
||||
} else {
|
||||
/* Apply the default */
|
||||
rule->options.inject.iotype_mask =
|
||||
(1ull << BLKDEBUG_IO_TYPE_READ)
|
||||
| (1ull << BLKDEBUG_IO_TYPE_WRITE)
|
||||
| (1ull << BLKDEBUG_IO_TYPE_WRITE_ZEROES)
|
||||
| (1ull << BLKDEBUG_IO_TYPE_DISCARD)
|
||||
| (1ull << BLKDEBUG_IO_TYPE_FLUSH);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ACTION_SET_STATE:
|
||||
@@ -335,7 +305,7 @@ static void blkdebug_parse_filename(const char *filename, QDict *options,
|
||||
|
||||
if (c != filename) {
|
||||
QString *config_path;
|
||||
config_path = qstring_from_substr(filename, 0, c - filename);
|
||||
config_path = qstring_from_substr(filename, 0, c - filename - 1);
|
||||
qdict_put(options, "config", config_path);
|
||||
}
|
||||
|
||||
@@ -428,11 +398,10 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto out;
|
||||
}
|
||||
|
||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||
(BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
|
||||
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
|
||||
bs->file->bs->supported_zero_flags);
|
||||
bs->supported_write_flags = BDRV_REQ_FUA &
|
||||
bs->file->bs->supported_write_flags;
|
||||
bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
|
||||
bs->file->bs->supported_zero_flags;
|
||||
ret = -EINVAL;
|
||||
|
||||
/* Set alignment overrides */
|
||||
@@ -491,8 +460,6 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto out;
|
||||
}
|
||||
|
||||
bdrv_debug_event(bs, BLKDBG_NONE);
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
if (ret < 0) {
|
||||
@@ -502,8 +469,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
BlkdebugIOType iotype)
|
||||
static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugRule *rule = NULL;
|
||||
@@ -513,10 +479,9 @@ static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
|
||||
uint64_t inject_offset = rule->options.inject.offset;
|
||||
|
||||
if ((inject_offset == -1 ||
|
||||
(bytes && inject_offset >= offset &&
|
||||
inject_offset < offset + bytes)) &&
|
||||
(rule->options.inject.iotype_mask & (1ull << iotype)))
|
||||
if (inject_offset == -1 ||
|
||||
(bytes && inject_offset >= offset &&
|
||||
inject_offset < offset + bytes))
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -555,7 +520,7 @@ blkdebug_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
assert(bytes <= bs->bl.max_transfer);
|
||||
}
|
||||
|
||||
err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_READ);
|
||||
err = rule_check(bs, offset, bytes);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
@@ -576,7 +541,7 @@ blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
assert(bytes <= bs->bl.max_transfer);
|
||||
}
|
||||
|
||||
err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_WRITE);
|
||||
err = rule_check(bs, offset, bytes);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
@@ -586,7 +551,7 @@ blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
|
||||
static int blkdebug_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
int err = rule_check(bs, 0, 0, BLKDEBUG_IO_TYPE_FLUSH);
|
||||
int err = rule_check(bs, 0, 0);
|
||||
|
||||
if (err) {
|
||||
return err;
|
||||
@@ -620,7 +585,7 @@ static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs,
|
||||
assert(bytes <= bs->bl.max_pwrite_zeroes);
|
||||
}
|
||||
|
||||
err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_WRITE_ZEROES);
|
||||
err = rule_check(bs, offset, bytes);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
@@ -654,12 +619,12 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
|
||||
assert(bytes <= bs->bl.max_pdiscard);
|
||||
}
|
||||
|
||||
err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_DISCARD);
|
||||
err = rule_check(bs, offset, bytes);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return bdrv_co_pdiscard(bs->file, offset, bytes);
|
||||
return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
|
||||
}
|
||||
|
||||
static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs,
|
||||
@@ -670,15 +635,7 @@ static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs,
|
||||
int64_t *map,
|
||||
BlockDriverState **file)
|
||||
{
|
||||
int err;
|
||||
|
||||
assert(QEMU_IS_ALIGNED(offset | bytes, bs->bl.request_alignment));
|
||||
|
||||
err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_BLOCK_STATUS);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return bdrv_co_block_status_from_file(bs, want_zero, offset, bytes,
|
||||
pnum, map, file);
|
||||
}
|
||||
@@ -853,37 +810,52 @@ static int64_t blkdebug_getlength(BlockDriverState *bs)
|
||||
return bdrv_getlength(bs->file->bs);
|
||||
}
|
||||
|
||||
static void blkdebug_refresh_filename(BlockDriverState *bs)
|
||||
static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
QDict *opts;
|
||||
const QDictEntry *e;
|
||||
int ret;
|
||||
bool force_json = false;
|
||||
|
||||
if (!bs->file->bs->exact_filename[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (e = qdict_first(bs->full_open_options); e;
|
||||
e = qdict_next(bs->full_open_options, e))
|
||||
{
|
||||
/* Real child options are under "image", but "x-image" may
|
||||
* contain a filename */
|
||||
for (e = qdict_first(options); e; e = qdict_next(options, e)) {
|
||||
if (strcmp(qdict_entry_key(e), "config") &&
|
||||
strcmp(qdict_entry_key(e), "image") &&
|
||||
strcmp(qdict_entry_key(e), "x-image") &&
|
||||
strcmp(qdict_entry_key(e), "driver"))
|
||||
strcmp(qdict_entry_key(e), "x-image"))
|
||||
{
|
||||
return;
|
||||
force_json = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||
"blkdebug:%s:%s",
|
||||
s->config_file ?: "", bs->file->bs->exact_filename);
|
||||
if (ret >= sizeof(bs->exact_filename)) {
|
||||
/* An overflow makes the filename unusable, so do not report any */
|
||||
bs->exact_filename[0] = 0;
|
||||
if (force_json && !bs->file->bs->full_open_options) {
|
||||
/* The config file cannot be recreated, so creating a plain filename
|
||||
* is impossible */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!force_json && bs->file->bs->exact_filename[0]) {
|
||||
int ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||
"blkdebug:%s:%s", s->config_file ?: "",
|
||||
bs->file->bs->exact_filename);
|
||||
if (ret >= sizeof(bs->exact_filename)) {
|
||||
/* An overflow makes the filename unusable, so do not report any */
|
||||
bs->exact_filename[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
opts = qdict_new();
|
||||
qdict_put_str(opts, "driver", "blkdebug");
|
||||
|
||||
QINCREF(bs->file->bs->full_open_options);
|
||||
qdict_put(opts, "image", bs->file->bs->full_open_options);
|
||||
|
||||
for (e = qdict_first(options); e; e = qdict_next(options, e)) {
|
||||
if (strcmp(qdict_entry_key(e), "x-image")) {
|
||||
qobject_incref(qdict_entry_value(e));
|
||||
qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e));
|
||||
}
|
||||
}
|
||||
|
||||
bs->full_open_options = opts;
|
||||
}
|
||||
|
||||
static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
@@ -916,20 +888,6 @@ static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *const blkdebug_strong_runtime_opts[] = {
|
||||
"config",
|
||||
"inject-error.",
|
||||
"set-state.",
|
||||
"align",
|
||||
"max-transfer",
|
||||
"opt-write-zero",
|
||||
"max-write-zero",
|
||||
"opt-discard",
|
||||
"max-discard",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_blkdebug = {
|
||||
.format_name = "blkdebug",
|
||||
.protocol_name = "blkdebug",
|
||||
@@ -959,8 +917,6 @@ static BlockDriver bdrv_blkdebug = {
|
||||
= blkdebug_debug_remove_breakpoint,
|
||||
.bdrv_debug_resume = blkdebug_debug_resume,
|
||||
.bdrv_debug_is_suspended = blkdebug_debug_is_suspended,
|
||||
|
||||
.strong_runtime_opts = blkdebug_strong_runtime_opts,
|
||||
};
|
||||
|
||||
static void bdrv_blkdebug_init(void)
|
||||
|
||||
@@ -1,533 +0,0 @@
|
||||
/*
|
||||
* Write logging blk driver based on blkverify and blkdebug.
|
||||
*
|
||||
* Copyright (c) 2017 Tuomas Tynkkynen <tuomas@tuxera.com>
|
||||
* Copyright (c) 2018 Aapo Vienamo <aapo@tuxera.com>
|
||||
* Copyright (c) 2018 Ari Sundholm <ari@tuxera.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/sockets.h" /* for EINPROGRESS on Windows */
|
||||
#include "block/block_int.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
|
||||
/* Disk format stuff - taken from Linux drivers/md/dm-log-writes.c */
|
||||
|
||||
#define LOG_FLUSH_FLAG (1 << 0)
|
||||
#define LOG_FUA_FLAG (1 << 1)
|
||||
#define LOG_DISCARD_FLAG (1 << 2)
|
||||
#define LOG_MARK_FLAG (1 << 3)
|
||||
#define LOG_FLAG_MASK (LOG_FLUSH_FLAG \
|
||||
| LOG_FUA_FLAG \
|
||||
| LOG_DISCARD_FLAG \
|
||||
| LOG_MARK_FLAG)
|
||||
|
||||
#define WRITE_LOG_VERSION 1ULL
|
||||
#define WRITE_LOG_MAGIC 0x6a736677736872ULL
|
||||
|
||||
/* All fields are little-endian. */
|
||||
struct log_write_super {
|
||||
uint64_t magic;
|
||||
uint64_t version;
|
||||
uint64_t nr_entries;
|
||||
uint32_t sectorsize;
|
||||
} QEMU_PACKED;
|
||||
|
||||
struct log_write_entry {
|
||||
uint64_t sector;
|
||||
uint64_t nr_sectors;
|
||||
uint64_t flags;
|
||||
uint64_t data_len;
|
||||
} QEMU_PACKED;
|
||||
|
||||
/* End of disk format structures. */
|
||||
|
||||
typedef struct {
|
||||
BdrvChild *log_file;
|
||||
uint32_t sectorsize;
|
||||
uint32_t sectorbits;
|
||||
uint64_t cur_log_sector;
|
||||
uint64_t nr_entries;
|
||||
uint64_t update_interval;
|
||||
} BDRVBlkLogWritesState;
|
||||
|
||||
static QemuOptsList runtime_opts = {
|
||||
.name = "blklogwrites",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = "log-append",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Append to an existing log",
|
||||
},
|
||||
{
|
||||
.name = "log-sector-size",
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Log sector size",
|
||||
},
|
||||
{
|
||||
.name = "log-super-update-interval",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "Log superblock update interval (# of write requests)",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
static inline uint32_t blk_log_writes_log2(uint32_t value)
|
||||
{
|
||||
assert(value > 0);
|
||||
return 31 - clz32(value);
|
||||
}
|
||||
|
||||
static inline bool blk_log_writes_sector_size_valid(uint32_t sector_size)
|
||||
{
|
||||
return is_power_of_2(sector_size) &&
|
||||
sector_size >= sizeof(struct log_write_super) &&
|
||||
sector_size >= sizeof(struct log_write_entry) &&
|
||||
sector_size < (1ull << 24);
|
||||
}
|
||||
|
||||
static uint64_t blk_log_writes_find_cur_log_sector(BdrvChild *log,
|
||||
uint32_t sector_size,
|
||||
uint64_t nr_entries,
|
||||
Error **errp)
|
||||
{
|
||||
uint64_t cur_sector = 1;
|
||||
uint64_t cur_idx = 0;
|
||||
uint32_t sector_bits = blk_log_writes_log2(sector_size);
|
||||
struct log_write_entry cur_entry;
|
||||
|
||||
while (cur_idx < nr_entries) {
|
||||
int read_ret = bdrv_pread(log, cur_sector << sector_bits, &cur_entry,
|
||||
sizeof(cur_entry));
|
||||
if (read_ret < 0) {
|
||||
error_setg_errno(errp, -read_ret,
|
||||
"Failed to read log entry %"PRIu64, cur_idx);
|
||||
return (uint64_t)-1ull;
|
||||
}
|
||||
|
||||
if (cur_entry.flags & ~cpu_to_le64(LOG_FLAG_MASK)) {
|
||||
error_setg(errp, "Invalid flags 0x%"PRIx64" in log entry %"PRIu64,
|
||||
le64_to_cpu(cur_entry.flags), cur_idx);
|
||||
return (uint64_t)-1ull;
|
||||
}
|
||||
|
||||
/* Account for the sector of the entry itself */
|
||||
++cur_sector;
|
||||
|
||||
/*
|
||||
* Account for the data of the write.
|
||||
* For discards, this data is not present.
|
||||
*/
|
||||
if (!(cur_entry.flags & cpu_to_le64(LOG_DISCARD_FLAG))) {
|
||||
cur_sector += le64_to_cpu(cur_entry.nr_sectors);
|
||||
}
|
||||
|
||||
++cur_idx;
|
||||
}
|
||||
|
||||
return cur_sector;
|
||||
}
|
||||
|
||||
static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVBlkLogWritesState *s = bs->opaque;
|
||||
QemuOpts *opts;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
uint64_t log_sector_size;
|
||||
bool log_append;
|
||||
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
ret = -EINVAL;
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Open the file */
|
||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
ret = -EINVAL;
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Open the log file */
|
||||
s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_file, false,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
ret = -EINVAL;
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
log_append = qemu_opt_get_bool(opts, "log-append", false);
|
||||
|
||||
if (log_append) {
|
||||
struct log_write_super log_sb = { 0, 0, 0, 0 };
|
||||
|
||||
if (qemu_opt_find(opts, "log-sector-size")) {
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "log-append and log-sector-size are mutually "
|
||||
"exclusive");
|
||||
goto fail_log;
|
||||
}
|
||||
|
||||
/* Read log superblock or fake one for an empty log */
|
||||
if (!bdrv_getlength(s->log_file->bs)) {
|
||||
log_sb.magic = cpu_to_le64(WRITE_LOG_MAGIC);
|
||||
log_sb.version = cpu_to_le64(WRITE_LOG_VERSION);
|
||||
log_sb.nr_entries = cpu_to_le64(0);
|
||||
log_sb.sectorsize = cpu_to_le32(BDRV_SECTOR_SIZE);
|
||||
} else {
|
||||
ret = bdrv_pread(s->log_file, 0, &log_sb, sizeof(log_sb));
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not read log superblock");
|
||||
goto fail_log;
|
||||
}
|
||||
}
|
||||
|
||||
if (log_sb.magic != cpu_to_le64(WRITE_LOG_MAGIC)) {
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "Invalid log superblock magic");
|
||||
goto fail_log;
|
||||
}
|
||||
|
||||
if (log_sb.version != cpu_to_le64(WRITE_LOG_VERSION)) {
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "Unsupported log version %"PRIu64,
|
||||
le64_to_cpu(log_sb.version));
|
||||
goto fail_log;
|
||||
}
|
||||
|
||||
log_sector_size = le32_to_cpu(log_sb.sectorsize);
|
||||
s->cur_log_sector = 1;
|
||||
s->nr_entries = 0;
|
||||
|
||||
if (blk_log_writes_sector_size_valid(log_sector_size)) {
|
||||
s->cur_log_sector =
|
||||
blk_log_writes_find_cur_log_sector(s->log_file, log_sector_size,
|
||||
le64_to_cpu(log_sb.nr_entries), &local_err);
|
||||
if (local_err) {
|
||||
ret = -EINVAL;
|
||||
error_propagate(errp, local_err);
|
||||
goto fail_log;
|
||||
}
|
||||
|
||||
s->nr_entries = le64_to_cpu(log_sb.nr_entries);
|
||||
}
|
||||
} else {
|
||||
log_sector_size = qemu_opt_get_size(opts, "log-sector-size",
|
||||
BDRV_SECTOR_SIZE);
|
||||
s->cur_log_sector = 1;
|
||||
s->nr_entries = 0;
|
||||
}
|
||||
|
||||
if (!blk_log_writes_sector_size_valid(log_sector_size)) {
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "Invalid log sector size %"PRIu64, log_sector_size);
|
||||
goto fail_log;
|
||||
}
|
||||
|
||||
s->sectorsize = log_sector_size;
|
||||
s->sectorbits = blk_log_writes_log2(log_sector_size);
|
||||
s->update_interval = qemu_opt_get_number(opts, "log-super-update-interval",
|
||||
4096);
|
||||
if (!s->update_interval) {
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "Invalid log superblock update interval %"PRIu64,
|
||||
s->update_interval);
|
||||
goto fail_log;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail_log:
|
||||
if (ret < 0) {
|
||||
bdrv_unref_child(bs, s->log_file);
|
||||
s->log_file = NULL;
|
||||
}
|
||||
fail:
|
||||
if (ret < 0) {
|
||||
bdrv_unref_child(bs, bs->file);
|
||||
bs->file = NULL;
|
||||
}
|
||||
qemu_opts_del(opts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void blk_log_writes_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBlkLogWritesState *s = bs->opaque;
|
||||
|
||||
bdrv_unref_child(bs, s->log_file);
|
||||
s->log_file = NULL;
|
||||
}
|
||||
|
||||
static int64_t blk_log_writes_getlength(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_getlength(bs->file->bs);
|
||||
}
|
||||
|
||||
static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||
const BdrvChildRole *role,
|
||||
BlockReopenQueue *ro_q,
|
||||
uint64_t perm, uint64_t shrd,
|
||||
uint64_t *nperm, uint64_t *nshrd)
|
||||
{
|
||||
if (!c) {
|
||||
*nperm = perm & DEFAULT_PERM_PASSTHROUGH;
|
||||
*nshrd = (shrd & DEFAULT_PERM_PASSTHROUGH) | DEFAULT_PERM_UNCHANGED;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strcmp(c->name, "log")) {
|
||||
bdrv_format_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd);
|
||||
} else {
|
||||
bdrv_filter_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd);
|
||||
}
|
||||
}
|
||||
|
||||
static void blk_log_writes_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BDRVBlkLogWritesState *s = bs->opaque;
|
||||
bs->bl.request_alignment = s->sectorsize;
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
blk_log_writes_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
|
||||
}
|
||||
|
||||
typedef struct BlkLogWritesFileReq {
|
||||
BlockDriverState *bs;
|
||||
uint64_t offset;
|
||||
uint64_t bytes;
|
||||
int file_flags;
|
||||
QEMUIOVector *qiov;
|
||||
int (*func)(struct BlkLogWritesFileReq *r);
|
||||
int file_ret;
|
||||
} BlkLogWritesFileReq;
|
||||
|
||||
typedef struct {
|
||||
BlockDriverState *bs;
|
||||
QEMUIOVector *qiov;
|
||||
struct log_write_entry entry;
|
||||
uint64_t zero_size;
|
||||
int log_ret;
|
||||
} BlkLogWritesLogReq;
|
||||
|
||||
static void coroutine_fn blk_log_writes_co_do_log(BlkLogWritesLogReq *lr)
|
||||
{
|
||||
BDRVBlkLogWritesState *s = lr->bs->opaque;
|
||||
uint64_t cur_log_offset = s->cur_log_sector << s->sectorbits;
|
||||
|
||||
s->nr_entries++;
|
||||
s->cur_log_sector +=
|
||||
ROUND_UP(lr->qiov->size, s->sectorsize) >> s->sectorbits;
|
||||
|
||||
lr->log_ret = bdrv_co_pwritev(s->log_file, cur_log_offset, lr->qiov->size,
|
||||
lr->qiov, 0);
|
||||
|
||||
/* Logging for the "write zeroes" operation */
|
||||
if (lr->log_ret == 0 && lr->zero_size) {
|
||||
cur_log_offset = s->cur_log_sector << s->sectorbits;
|
||||
s->cur_log_sector +=
|
||||
ROUND_UP(lr->zero_size, s->sectorsize) >> s->sectorbits;
|
||||
|
||||
lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, cur_log_offset,
|
||||
lr->zero_size, 0);
|
||||
}
|
||||
|
||||
/* Update super block on flush or every update interval */
|
||||
if (lr->log_ret == 0 && ((lr->entry.flags & LOG_FLUSH_FLAG)
|
||||
|| (s->nr_entries % s->update_interval == 0)))
|
||||
{
|
||||
struct log_write_super super = {
|
||||
.magic = cpu_to_le64(WRITE_LOG_MAGIC),
|
||||
.version = cpu_to_le64(WRITE_LOG_VERSION),
|
||||
.nr_entries = cpu_to_le64(s->nr_entries),
|
||||
.sectorsize = cpu_to_le32(s->sectorsize),
|
||||
};
|
||||
void *zeroes = g_malloc0(s->sectorsize - sizeof(super));
|
||||
QEMUIOVector qiov;
|
||||
|
||||
qemu_iovec_init(&qiov, 2);
|
||||
qemu_iovec_add(&qiov, &super, sizeof(super));
|
||||
qemu_iovec_add(&qiov, zeroes, s->sectorsize - sizeof(super));
|
||||
|
||||
lr->log_ret =
|
||||
bdrv_co_pwritev(s->log_file, 0, s->sectorsize, &qiov, 0);
|
||||
if (lr->log_ret == 0) {
|
||||
lr->log_ret = bdrv_co_flush(s->log_file->bs);
|
||||
}
|
||||
qemu_iovec_destroy(&qiov);
|
||||
g_free(zeroes);
|
||||
}
|
||||
}
|
||||
|
||||
static void coroutine_fn blk_log_writes_co_do_file(BlkLogWritesFileReq *fr)
|
||||
{
|
||||
fr->file_ret = fr->func(fr);
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
blk_log_writes_co_log(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags,
|
||||
int (*file_func)(BlkLogWritesFileReq *r),
|
||||
uint64_t entry_flags, bool is_zero_write)
|
||||
{
|
||||
QEMUIOVector log_qiov;
|
||||
size_t niov = qiov ? qiov->niov : 0;
|
||||
BDRVBlkLogWritesState *s = bs->opaque;
|
||||
BlkLogWritesFileReq fr = {
|
||||
.bs = bs,
|
||||
.offset = offset,
|
||||
.bytes = bytes,
|
||||
.file_flags = flags,
|
||||
.qiov = qiov,
|
||||
.func = file_func,
|
||||
};
|
||||
BlkLogWritesLogReq lr = {
|
||||
.bs = bs,
|
||||
.qiov = &log_qiov,
|
||||
.entry = {
|
||||
.sector = cpu_to_le64(offset >> s->sectorbits),
|
||||
.nr_sectors = cpu_to_le64(bytes >> s->sectorbits),
|
||||
.flags = cpu_to_le64(entry_flags),
|
||||
.data_len = 0,
|
||||
},
|
||||
.zero_size = is_zero_write ? bytes : 0,
|
||||
};
|
||||
void *zeroes = g_malloc0(s->sectorsize - sizeof(lr.entry));
|
||||
|
||||
assert((1 << s->sectorbits) == s->sectorsize);
|
||||
assert(bs->bl.request_alignment == s->sectorsize);
|
||||
assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
|
||||
assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
|
||||
|
||||
qemu_iovec_init(&log_qiov, niov + 2);
|
||||
qemu_iovec_add(&log_qiov, &lr.entry, sizeof(lr.entry));
|
||||
qemu_iovec_add(&log_qiov, zeroes, s->sectorsize - sizeof(lr.entry));
|
||||
if (qiov) {
|
||||
qemu_iovec_concat(&log_qiov, qiov, 0, qiov->size);
|
||||
}
|
||||
|
||||
blk_log_writes_co_do_file(&fr);
|
||||
blk_log_writes_co_do_log(&lr);
|
||||
|
||||
qemu_iovec_destroy(&log_qiov);
|
||||
g_free(zeroes);
|
||||
|
||||
if (lr.log_ret < 0) {
|
||||
return lr.log_ret;
|
||||
}
|
||||
|
||||
return fr.file_ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
blk_log_writes_co_do_file_pwritev(BlkLogWritesFileReq *fr)
|
||||
{
|
||||
return bdrv_co_pwritev(fr->bs->file, fr->offset, fr->bytes,
|
||||
fr->qiov, fr->file_flags);
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
blk_log_writes_co_do_file_pwrite_zeroes(BlkLogWritesFileReq *fr)
|
||||
{
|
||||
return bdrv_co_pwrite_zeroes(fr->bs->file, fr->offset, fr->bytes,
|
||||
fr->file_flags);
|
||||
}
|
||||
|
||||
static int coroutine_fn blk_log_writes_co_do_file_flush(BlkLogWritesFileReq *fr)
|
||||
{
|
||||
return bdrv_co_flush(fr->bs->file->bs);
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
blk_log_writes_co_do_file_pdiscard(BlkLogWritesFileReq *fr)
|
||||
{
|
||||
return bdrv_co_pdiscard(fr->bs->file, fr->offset, fr->bytes);
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
blk_log_writes_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
return blk_log_writes_co_log(bs, offset, bytes, qiov, flags,
|
||||
blk_log_writes_co_do_file_pwritev, 0, false);
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
blk_log_writes_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
return blk_log_writes_co_log(bs, offset, bytes, NULL, flags,
|
||||
blk_log_writes_co_do_file_pwrite_zeroes, 0,
|
||||
true);
|
||||
}
|
||||
|
||||
static int coroutine_fn blk_log_writes_co_flush_to_disk(BlockDriverState *bs)
|
||||
{
|
||||
return blk_log_writes_co_log(bs, 0, 0, NULL, 0,
|
||||
blk_log_writes_co_do_file_flush,
|
||||
LOG_FLUSH_FLAG, false);
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
blk_log_writes_co_pdiscard(BlockDriverState *bs, int64_t offset, int count)
|
||||
{
|
||||
return blk_log_writes_co_log(bs, offset, count, NULL, 0,
|
||||
blk_log_writes_co_do_file_pdiscard,
|
||||
LOG_DISCARD_FLAG, false);
|
||||
}
|
||||
|
||||
static const char *const blk_log_writes_strong_runtime_opts[] = {
|
||||
"log-append",
|
||||
"log-sector-size",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_blk_log_writes = {
|
||||
.format_name = "blklogwrites",
|
||||
.instance_size = sizeof(BDRVBlkLogWritesState),
|
||||
|
||||
.bdrv_open = blk_log_writes_open,
|
||||
.bdrv_close = blk_log_writes_close,
|
||||
.bdrv_getlength = blk_log_writes_getlength,
|
||||
.bdrv_child_perm = blk_log_writes_child_perm,
|
||||
.bdrv_refresh_limits = blk_log_writes_refresh_limits,
|
||||
|
||||
.bdrv_co_preadv = blk_log_writes_co_preadv,
|
||||
.bdrv_co_pwritev = blk_log_writes_co_pwritev,
|
||||
.bdrv_co_pwrite_zeroes = blk_log_writes_co_pwrite_zeroes,
|
||||
.bdrv_co_flush_to_disk = blk_log_writes_co_flush_to_disk,
|
||||
.bdrv_co_pdiscard = blk_log_writes_co_pdiscard,
|
||||
.bdrv_co_block_status = bdrv_co_block_status_from_file,
|
||||
|
||||
.is_filter = true,
|
||||
.strong_runtime_opts = blk_log_writes_strong_runtime_opts,
|
||||
};
|
||||
|
||||
static void bdrv_blk_log_writes_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_blk_log_writes);
|
||||
}
|
||||
|
||||
block_init(bdrv_blk_log_writes_init);
|
||||
12
block/blkreplay.c
Normal file → Executable file
12
block/blkreplay.c
Normal file → Executable file
@@ -10,7 +10,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "qapi/error.h"
|
||||
@@ -35,14 +35,15 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
|
||||
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED;
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void blkreplay_close(BlockDriverState *bs)
|
||||
{
|
||||
}
|
||||
|
||||
static int64_t blkreplay_getlength(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_getlength(bs->file->bs);
|
||||
@@ -109,7 +110,7 @@ static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs,
|
||||
int64_t offset, int bytes)
|
||||
{
|
||||
uint64_t reqid = blkreplay_next_id();
|
||||
int ret = bdrv_co_pdiscard(bs->file, offset, bytes);
|
||||
int ret = bdrv_co_pdiscard(bs->file->bs, offset, bytes);
|
||||
block_request_create(reqid, bs, qemu_coroutine_self());
|
||||
qemu_coroutine_yield();
|
||||
|
||||
@@ -131,6 +132,7 @@ static BlockDriver bdrv_blkreplay = {
|
||||
.instance_size = 0,
|
||||
|
||||
.bdrv_open = blkreplay_open,
|
||||
.bdrv_close = blkreplay_close,
|
||||
.bdrv_child_perm = bdrv_filter_default_perms,
|
||||
.bdrv_getlength = blkreplay_getlength,
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
|
||||
typedef struct {
|
||||
@@ -81,7 +80,7 @@ static void blkverify_parse_filename(const char *filename, QDict *options,
|
||||
}
|
||||
|
||||
/* TODO Implement option pass-through and set raw.filename here */
|
||||
raw_path = qstring_from_substr(filename, 0, c - filename);
|
||||
raw_path = qstring_from_substr(filename, 0, c - filename - 1);
|
||||
qdict_put(options, "x-raw", raw_path);
|
||||
|
||||
/* TODO Allow multi-level nesting and set file.filename here */
|
||||
@@ -142,9 +141,6 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
|
||||
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED;
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
qemu_opts_del(opts);
|
||||
@@ -282,10 +278,27 @@ static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||
return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate);
|
||||
}
|
||||
|
||||
static void blkverify_refresh_filename(BlockDriverState *bs)
|
||||
static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
|
||||
/* bs->file->bs has already been refreshed */
|
||||
bdrv_refresh_filename(s->test_file->bs);
|
||||
|
||||
if (bs->file->bs->full_open_options
|
||||
&& s->test_file->bs->full_open_options)
|
||||
{
|
||||
QDict *opts = qdict_new();
|
||||
qdict_put_str(opts, "driver", "blkverify");
|
||||
|
||||
QINCREF(bs->file->bs->full_open_options);
|
||||
qdict_put(opts, "raw", bs->file->bs->full_open_options);
|
||||
QINCREF(s->test_file->bs->full_open_options);
|
||||
qdict_put(opts, "test", s->test_file->bs->full_open_options);
|
||||
|
||||
bs->full_open_options = opts;
|
||||
}
|
||||
|
||||
if (bs->file->bs->exact_filename[0]
|
||||
&& s->test_file->bs->exact_filename[0])
|
||||
{
|
||||
@@ -300,15 +313,6 @@ static void blkverify_refresh_filename(BlockDriverState *bs)
|
||||
}
|
||||
}
|
||||
|
||||
static char *blkverify_dirname(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
/* In general, there are two BDSs with different dirnames below this one;
|
||||
* so there is no unique dirname we could return (unless both are equal by
|
||||
* chance). Therefore, to be consistent, just always return NULL. */
|
||||
error_setg(errp, "Cannot generate a base directory for blkverify nodes");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_blkverify = {
|
||||
.format_name = "blkverify",
|
||||
.protocol_name = "blkverify",
|
||||
@@ -320,7 +324,6 @@ static BlockDriver bdrv_blkverify = {
|
||||
.bdrv_child_perm = bdrv_filter_default_perms,
|
||||
.bdrv_getlength = blkverify_getlength,
|
||||
.bdrv_refresh_filename = blkverify_refresh_filename,
|
||||
.bdrv_dirname = blkverify_dirname,
|
||||
|
||||
.bdrv_co_preadv = blkverify_co_preadv,
|
||||
.bdrv_co_pwritev = blkverify_co_pwritev,
|
||||
|
||||
@@ -31,24 +31,18 @@
|
||||
|
||||
static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb);
|
||||
|
||||
typedef struct BlockBackendAioNotifier {
|
||||
void (*attached_aio_context)(AioContext *new_context, void *opaque);
|
||||
void (*detach_aio_context)(void *opaque);
|
||||
void *opaque;
|
||||
QLIST_ENTRY(BlockBackendAioNotifier) list;
|
||||
} BlockBackendAioNotifier;
|
||||
|
||||
struct BlockBackend {
|
||||
char *name;
|
||||
int refcnt;
|
||||
BdrvChild *root;
|
||||
AioContext *ctx;
|
||||
DriveInfo *legacy_dinfo; /* null unless created by drive_new() */
|
||||
QTAILQ_ENTRY(BlockBackend) link; /* for block_backends */
|
||||
QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */
|
||||
BlockBackendPublic public;
|
||||
|
||||
DeviceState *dev; /* attached device model, if any */
|
||||
void *dev; /* attached device model, if any */
|
||||
bool legacy_dev; /* true if dev is not a DeviceState */
|
||||
/* TODO change to DeviceState when all users are qdevified */
|
||||
const BlockDevOps *dev_ops;
|
||||
void *dev_opaque;
|
||||
|
||||
@@ -72,11 +66,9 @@ struct BlockBackend {
|
||||
uint64_t shared_perm;
|
||||
bool disable_perm;
|
||||
|
||||
bool allow_aio_context_change;
|
||||
bool allow_write_beyond_eof;
|
||||
|
||||
NotifierList remove_bs_notifiers, insert_bs_notifiers;
|
||||
QLIST_HEAD(, BlockBackendAioNotifier) aio_notifiers;
|
||||
|
||||
int quiesce_counter;
|
||||
VMChangeStateEntry *vmsh;
|
||||
@@ -88,6 +80,7 @@ struct BlockBackend {
|
||||
* Accessed with atomic ops.
|
||||
*/
|
||||
unsigned int in_flight;
|
||||
AioWait wait;
|
||||
};
|
||||
|
||||
typedef struct BlockBackendAIOCB {
|
||||
@@ -120,17 +113,11 @@ static void blk_root_inherit_options(int *child_flags, QDict *child_options,
|
||||
abort();
|
||||
}
|
||||
static void blk_root_drained_begin(BdrvChild *child);
|
||||
static bool blk_root_drained_poll(BdrvChild *child);
|
||||
static void blk_root_drained_end(BdrvChild *child);
|
||||
|
||||
static void blk_root_change_media(BdrvChild *child, bool load);
|
||||
static void blk_root_resize(BdrvChild *child);
|
||||
|
||||
static bool blk_root_can_set_aio_ctx(BdrvChild *child, AioContext *ctx,
|
||||
GSList **ignore, Error **errp);
|
||||
static void blk_root_set_aio_ctx(BdrvChild *child, AioContext *ctx,
|
||||
GSList **ignore);
|
||||
|
||||
static char *blk_root_get_parent_desc(BdrvChild *child)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
@@ -260,36 +247,6 @@ static int blk_root_inactivate(BdrvChild *child)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void blk_root_attach(BdrvChild *child)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
BlockBackendAioNotifier *notifier;
|
||||
|
||||
trace_blk_root_attach(child, blk, child->bs);
|
||||
|
||||
QLIST_FOREACH(notifier, &blk->aio_notifiers, list) {
|
||||
bdrv_add_aio_context_notifier(child->bs,
|
||||
notifier->attached_aio_context,
|
||||
notifier->detach_aio_context,
|
||||
notifier->opaque);
|
||||
}
|
||||
}
|
||||
|
||||
static void blk_root_detach(BdrvChild *child)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
BlockBackendAioNotifier *notifier;
|
||||
|
||||
trace_blk_root_detach(child, blk, child->bs);
|
||||
|
||||
QLIST_FOREACH(notifier, &blk->aio_notifiers, list) {
|
||||
bdrv_remove_aio_context_notifier(child->bs,
|
||||
notifier->attached_aio_context,
|
||||
notifier->detach_aio_context,
|
||||
notifier->opaque);
|
||||
}
|
||||
}
|
||||
|
||||
static const BdrvChildRole child_root = {
|
||||
.inherit_options = blk_root_inherit_options,
|
||||
|
||||
@@ -299,17 +256,10 @@ static const BdrvChildRole child_root = {
|
||||
.get_parent_desc = blk_root_get_parent_desc,
|
||||
|
||||
.drained_begin = blk_root_drained_begin,
|
||||
.drained_poll = blk_root_drained_poll,
|
||||
.drained_end = blk_root_drained_end,
|
||||
|
||||
.activate = blk_root_activate,
|
||||
.inactivate = blk_root_inactivate,
|
||||
|
||||
.attach = blk_root_attach,
|
||||
.detach = blk_root_detach,
|
||||
|
||||
.can_set_aio_ctx = blk_root_can_set_aio_ctx,
|
||||
.set_aio_ctx = blk_root_set_aio_ctx,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -323,25 +273,20 @@ static const BdrvChildRole child_root = {
|
||||
*
|
||||
* Return the new BlockBackend on success, null on failure.
|
||||
*/
|
||||
BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm)
|
||||
BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
|
||||
blk = g_new0(BlockBackend, 1);
|
||||
blk->refcnt = 1;
|
||||
blk->ctx = ctx;
|
||||
blk->perm = perm;
|
||||
blk->shared_perm = shared_perm;
|
||||
blk_set_enable_write_cache(blk, true);
|
||||
|
||||
blk->on_read_error = BLOCKDEV_ON_ERROR_REPORT;
|
||||
blk->on_write_error = BLOCKDEV_ON_ERROR_ENOSPC;
|
||||
|
||||
block_acct_init(&blk->stats);
|
||||
|
||||
notifier_list_init(&blk->remove_bs_notifiers);
|
||||
notifier_list_init(&blk->insert_bs_notifiers);
|
||||
QLIST_INIT(&blk->aio_notifiers);
|
||||
|
||||
QTAILQ_INSERT_TAIL(&block_backends, blk, link);
|
||||
return blk;
|
||||
@@ -349,7 +294,6 @@ BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm)
|
||||
|
||||
/*
|
||||
* Creates a new BlockBackend, opens a new BlockDriverState, and connects both.
|
||||
* The new BlockBackend is in the main AioContext.
|
||||
*
|
||||
* Just as with bdrv_open(), after having called this function the reference to
|
||||
* @options belongs to the block layer (even on failure).
|
||||
@@ -385,16 +329,17 @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
|
||||
perm |= BLK_PERM_RESIZE;
|
||||
}
|
||||
|
||||
blk = blk_new(qemu_get_aio_context(), perm, BLK_PERM_ALL);
|
||||
blk = blk_new(perm, BLK_PERM_ALL);
|
||||
bs = bdrv_open(filename, reference, options, flags, errp);
|
||||
if (!bs) {
|
||||
blk_unref(blk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
blk->root = bdrv_root_attach_child(bs, "root", &child_root, blk->ctx,
|
||||
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
|
||||
perm, BLK_PERM_ALL, blk, errp);
|
||||
if (!blk->root) {
|
||||
bdrv_unref(bs);
|
||||
blk_unref(blk);
|
||||
return NULL;
|
||||
}
|
||||
@@ -419,7 +364,6 @@ static void blk_delete(BlockBackend *blk)
|
||||
}
|
||||
assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers));
|
||||
assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers));
|
||||
assert(QLIST_EMPTY(&blk->aio_notifiers));
|
||||
QTAILQ_REMOVE(&block_backends, blk, link);
|
||||
drive_info_del(blk->legacy_dinfo);
|
||||
block_acct_cleanup(&blk->stats);
|
||||
@@ -432,6 +376,7 @@ static void drive_info_del(DriveInfo *dinfo)
|
||||
return;
|
||||
}
|
||||
qemu_opts_del(dinfo->opts);
|
||||
g_free(dinfo->serial);
|
||||
g_free(dinfo);
|
||||
}
|
||||
|
||||
@@ -446,7 +391,6 @@ int blk_get_refcnt(BlockBackend *blk)
|
||||
*/
|
||||
void blk_ref(BlockBackend *blk)
|
||||
{
|
||||
assert(blk->refcnt > 0);
|
||||
blk->refcnt++;
|
||||
}
|
||||
|
||||
@@ -459,13 +403,7 @@ void blk_unref(BlockBackend *blk)
|
||||
{
|
||||
if (blk) {
|
||||
assert(blk->refcnt > 0);
|
||||
if (blk->refcnt > 1) {
|
||||
blk->refcnt--;
|
||||
} else {
|
||||
blk_drain(blk);
|
||||
/* blk_drain() cannot resurrect blk, nobody held a reference */
|
||||
assert(blk->refcnt == 1);
|
||||
blk->refcnt = 0;
|
||||
if (!--blk->refcnt) {
|
||||
blk_delete(blk);
|
||||
}
|
||||
}
|
||||
@@ -787,11 +725,6 @@ void blk_remove_bs(BlockBackend *blk)
|
||||
|
||||
blk_update_root_state(blk);
|
||||
|
||||
/* bdrv_root_unref_child() will cause blk->root to become stale and may
|
||||
* switch to a completion coroutine later on. Let's drain all I/O here
|
||||
* to avoid that and a potential QEMU crash.
|
||||
*/
|
||||
blk_drain(blk);
|
||||
bdrv_root_unref_child(blk->root);
|
||||
blk->root = NULL;
|
||||
}
|
||||
@@ -802,12 +735,12 @@ void blk_remove_bs(BlockBackend *blk)
|
||||
int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
|
||||
bdrv_ref(bs);
|
||||
blk->root = bdrv_root_attach_child(bs, "root", &child_root, blk->ctx,
|
||||
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
|
||||
blk->perm, blk->shared_perm, blk, errp);
|
||||
if (blk->root == NULL) {
|
||||
return -EPERM;
|
||||
}
|
||||
bdrv_ref(bs);
|
||||
|
||||
notifier_list_notify(&blk->insert_bs_notifiers, blk);
|
||||
if (tgm->throttle_state) {
|
||||
@@ -845,11 +778,7 @@ void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm)
|
||||
*shared_perm = blk->shared_perm;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attach device model @dev to @blk.
|
||||
* Return 0 on success, -EBUSY when a device model is attached already.
|
||||
*/
|
||||
int blk_attach_dev(BlockBackend *blk, DeviceState *dev)
|
||||
static int blk_do_attach_dev(BlockBackend *blk, void *dev)
|
||||
{
|
||||
if (blk->dev) {
|
||||
return -EBUSY;
|
||||
@@ -864,16 +793,40 @@ int blk_attach_dev(BlockBackend *blk, DeviceState *dev)
|
||||
|
||||
blk_ref(blk);
|
||||
blk->dev = dev;
|
||||
blk->legacy_dev = false;
|
||||
blk_iostatus_reset(blk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attach device model @dev to @blk.
|
||||
* Return 0 on success, -EBUSY when a device model is attached already.
|
||||
*/
|
||||
int blk_attach_dev(BlockBackend *blk, DeviceState *dev)
|
||||
{
|
||||
return blk_do_attach_dev(blk, dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attach device model @dev to @blk.
|
||||
* @blk must not have a device model attached already.
|
||||
* TODO qdevified devices don't use this, remove when devices are qdevified
|
||||
*/
|
||||
void blk_attach_dev_legacy(BlockBackend *blk, void *dev)
|
||||
{
|
||||
if (blk_do_attach_dev(blk, dev) < 0) {
|
||||
abort();
|
||||
}
|
||||
blk->legacy_dev = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Detach device model @dev from @blk.
|
||||
* @dev must be currently attached to @blk.
|
||||
*/
|
||||
void blk_detach_dev(BlockBackend *blk, DeviceState *dev)
|
||||
void blk_detach_dev(BlockBackend *blk, void *dev)
|
||||
/* TODO change to DeviceState *dev when all users are qdevified */
|
||||
{
|
||||
assert(blk->dev == dev);
|
||||
blk->dev = NULL;
|
||||
@@ -887,7 +840,8 @@ void blk_detach_dev(BlockBackend *blk, DeviceState *dev)
|
||||
/*
|
||||
* Return the device model attached to @blk if any, else null.
|
||||
*/
|
||||
DeviceState *blk_get_attached_dev(BlockBackend *blk)
|
||||
void *blk_get_attached_dev(BlockBackend *blk)
|
||||
/* TODO change to return DeviceState * when all users are qdevified */
|
||||
{
|
||||
return blk->dev;
|
||||
}
|
||||
@@ -896,15 +850,17 @@ DeviceState *blk_get_attached_dev(BlockBackend *blk)
|
||||
* device attached to the BlockBackend. */
|
||||
char *blk_get_attached_dev_id(BlockBackend *blk)
|
||||
{
|
||||
DeviceState *dev = blk->dev;
|
||||
DeviceState *dev;
|
||||
|
||||
assert(!blk->legacy_dev);
|
||||
dev = blk->dev;
|
||||
|
||||
if (!dev) {
|
||||
return g_strdup("");
|
||||
} else if (dev->id) {
|
||||
return g_strdup(dev->id);
|
||||
}
|
||||
|
||||
return object_get_canonical_path(OBJECT(dev)) ?: g_strdup("");
|
||||
return object_get_canonical_path(OBJECT(dev));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -934,6 +890,11 @@ BlockBackend *blk_by_dev(void *dev)
|
||||
void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops,
|
||||
void *opaque)
|
||||
{
|
||||
/* All drivers that use blk_set_dev_ops() are qdevified and we want to keep
|
||||
* it that way, so we can assume blk->dev, if present, is a DeviceState if
|
||||
* blk->dev_ops is set. Non-device users may use dev_ops without device. */
|
||||
assert(!blk->legacy_dev);
|
||||
|
||||
blk->dev_ops = ops;
|
||||
blk->dev_opaque = opaque;
|
||||
|
||||
@@ -959,6 +920,8 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp)
|
||||
bool tray_was_open, tray_is_open;
|
||||
Error *local_err = NULL;
|
||||
|
||||
assert(!blk->legacy_dev);
|
||||
|
||||
tray_was_open = blk_dev_is_tray_open(blk);
|
||||
blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err);
|
||||
if (local_err) {
|
||||
@@ -970,7 +933,8 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp)
|
||||
|
||||
if (tray_was_open != tray_is_open) {
|
||||
char *id = blk_get_attached_dev_id(blk);
|
||||
qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open);
|
||||
qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open,
|
||||
&error_abort);
|
||||
g_free(id);
|
||||
}
|
||||
}
|
||||
@@ -1073,7 +1037,11 @@ void blk_iostatus_disable(BlockBackend *blk)
|
||||
void blk_iostatus_reset(BlockBackend *blk)
|
||||
{
|
||||
if (blk_iostatus_is_enabled(blk)) {
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
blk->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
|
||||
if (bs && bs->job) {
|
||||
block_job_iostatus_reset(bs->job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1091,11 +1059,6 @@ void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow)
|
||||
blk->allow_write_beyond_eof = allow;
|
||||
}
|
||||
|
||||
void blk_set_allow_aio_context_change(BlockBackend *blk, bool allow)
|
||||
{
|
||||
blk->allow_aio_context_change = allow;
|
||||
}
|
||||
|
||||
static int blk_check_byte_request(BlockBackend *blk, int64_t offset,
|
||||
size_t size)
|
||||
{
|
||||
@@ -1199,7 +1162,6 @@ static void blk_read_entry(void *opaque)
|
||||
|
||||
rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size,
|
||||
qiov, rwco->flags);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
static void blk_write_entry(void *opaque)
|
||||
@@ -1209,15 +1171,23 @@ static void blk_write_entry(void *opaque)
|
||||
|
||||
rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size,
|
||||
qiov, rwco->flags);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
|
||||
int64_t bytes, CoroutineEntry co_entry,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);
|
||||
BlkRwCo rwco = {
|
||||
QEMUIOVector qiov;
|
||||
struct iovec iov;
|
||||
BlkRwCo rwco;
|
||||
|
||||
iov = (struct iovec) {
|
||||
.iov_base = buf,
|
||||
.iov_len = bytes,
|
||||
};
|
||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
||||
|
||||
rwco = (BlkRwCo) {
|
||||
.blk = blk,
|
||||
.offset = offset,
|
||||
.iobuf = &qiov,
|
||||
@@ -1265,15 +1235,15 @@ int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags)
|
||||
return bdrv_make_zero(blk->root, flags);
|
||||
}
|
||||
|
||||
void blk_inc_in_flight(BlockBackend *blk)
|
||||
static void blk_inc_in_flight(BlockBackend *blk)
|
||||
{
|
||||
atomic_inc(&blk->in_flight);
|
||||
}
|
||||
|
||||
void blk_dec_in_flight(BlockBackend *blk)
|
||||
static void blk_dec_in_flight(BlockBackend *blk)
|
||||
{
|
||||
atomic_dec(&blk->in_flight);
|
||||
aio_wait_kick();
|
||||
aio_wait_kick(&blk->wait);
|
||||
}
|
||||
|
||||
static void error_callback_bh(void *opaque)
|
||||
@@ -1314,8 +1284,8 @@ static const AIOCBInfo blk_aio_em_aiocb_info = {
|
||||
static void blk_aio_complete(BlkAioEmAIOCB *acb)
|
||||
{
|
||||
if (acb->has_returned) {
|
||||
acb->common.cb(acb->common.opaque, acb->rwco.ret);
|
||||
blk_dec_in_flight(acb->rwco.blk);
|
||||
acb->common.cb(acb->common.opaque, acb->rwco.ret);
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
}
|
||||
@@ -1512,7 +1482,6 @@ static void blk_ioctl_entry(void *opaque)
|
||||
|
||||
rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset,
|
||||
qiov->iov[0].iov_base);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
|
||||
@@ -1543,7 +1512,7 @@ int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_co_pdiscard(blk->root, offset, bytes);
|
||||
return bdrv_co_pdiscard(blk_bs(blk), offset, bytes);
|
||||
}
|
||||
|
||||
int blk_co_flush(BlockBackend *blk)
|
||||
@@ -1559,7 +1528,6 @@ static void blk_flush_entry(void *opaque)
|
||||
{
|
||||
BlkRwCo *rwco = opaque;
|
||||
rwco->ret = blk_co_flush(rwco->blk);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
int blk_flush(BlockBackend *blk)
|
||||
@@ -1576,8 +1544,9 @@ void blk_drain(BlockBackend *blk)
|
||||
}
|
||||
|
||||
/* We may have -ENOMEDIUM completions in flight */
|
||||
AIO_WAIT_WHILE(blk_get_aio_context(blk),
|
||||
atomic_mb_read(&blk->in_flight) > 0);
|
||||
AIO_WAIT_WHILE(&blk->wait,
|
||||
blk_get_aio_context(blk),
|
||||
atomic_mb_read(&blk->in_flight) > 0);
|
||||
|
||||
if (bs) {
|
||||
bdrv_drained_end(bs);
|
||||
@@ -1596,7 +1565,8 @@ void blk_drain_all(void)
|
||||
aio_context_acquire(ctx);
|
||||
|
||||
/* We may have -ENOMEDIUM completions in flight */
|
||||
AIO_WAIT_WHILE(ctx, atomic_mb_read(&blk->in_flight) > 0);
|
||||
AIO_WAIT_WHILE(&blk->wait, ctx,
|
||||
atomic_mb_read(&blk->in_flight) > 0);
|
||||
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
@@ -1648,7 +1618,8 @@ static void send_qmp_error_event(BlockBackend *blk,
|
||||
qapi_event_send_block_io_error(blk_name(blk), !!bs,
|
||||
bs ? bdrv_get_node_name(bs) : NULL, optype,
|
||||
action, blk_iostatus_is_enabled(blk),
|
||||
error == ENOSPC, strerror(error));
|
||||
error == ENOSPC, strerror(error),
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
/* This is done by device models because, while the block layer knows
|
||||
@@ -1683,7 +1654,7 @@ void blk_error_action(BlockBackend *blk, BlockErrorAction action,
|
||||
}
|
||||
}
|
||||
|
||||
bool blk_is_read_only(BlockBackend *blk)
|
||||
int blk_is_read_only(BlockBackend *blk)
|
||||
{
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
|
||||
@@ -1694,18 +1665,18 @@ bool blk_is_read_only(BlockBackend *blk)
|
||||
}
|
||||
}
|
||||
|
||||
bool blk_is_sg(BlockBackend *blk)
|
||||
int blk_is_sg(BlockBackend *blk)
|
||||
{
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
|
||||
if (!bs) {
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return bdrv_is_sg(bs);
|
||||
}
|
||||
|
||||
bool blk_enable_write_cache(BlockBackend *blk)
|
||||
int blk_enable_write_cache(BlockBackend *blk)
|
||||
{
|
||||
return blk->enable_write_cache;
|
||||
}
|
||||
@@ -1753,6 +1724,9 @@ void blk_eject(BlockBackend *blk, bool eject_flag)
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
char *id;
|
||||
|
||||
/* blk_eject is only called by qdevified devices */
|
||||
assert(!blk->legacy_dev);
|
||||
|
||||
if (bs) {
|
||||
bdrv_eject(bs, eject_flag);
|
||||
}
|
||||
@@ -1761,7 +1735,7 @@ void blk_eject(BlockBackend *blk, bool eject_flag)
|
||||
* the frontend experienced a tray event. */
|
||||
id = blk_get_attached_dev_id(blk);
|
||||
qapi_event_send_device_tray_moved(blk_name(blk), id,
|
||||
eject_flag);
|
||||
eject_flag, &error_abort);
|
||||
g_free(id);
|
||||
}
|
||||
|
||||
@@ -1776,13 +1750,6 @@ 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 */
|
||||
uint32_t blk_get_max_transfer(BlockBackend *blk)
|
||||
{
|
||||
@@ -1858,11 +1825,10 @@ AioContext *blk_get_aio_context(BlockBackend *blk)
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
|
||||
if (bs) {
|
||||
AioContext *ctx = bdrv_get_aio_context(blk_bs(blk));
|
||||
assert(ctx == blk->ctx);
|
||||
return bdrv_get_aio_context(bs);
|
||||
} else {
|
||||
return qemu_get_aio_context();
|
||||
}
|
||||
|
||||
return blk->ctx;
|
||||
}
|
||||
|
||||
static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb)
|
||||
@@ -1871,79 +1837,28 @@ static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb)
|
||||
return blk_get_aio_context(blk_acb->blk);
|
||||
}
|
||||
|
||||
static int blk_do_set_aio_context(BlockBackend *blk, AioContext *new_context,
|
||||
bool update_root_node, Error **errp)
|
||||
void blk_set_aio_context(BlockBackend *blk, AioContext *new_context)
|
||||
{
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
|
||||
int ret;
|
||||
|
||||
if (bs) {
|
||||
if (update_root_node) {
|
||||
ret = bdrv_child_try_set_aio_context(bs, new_context, blk->root,
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if (tgm->throttle_state) {
|
||||
bdrv_drained_begin(bs);
|
||||
throttle_group_detach_aio_context(tgm);
|
||||
throttle_group_attach_aio_context(tgm, new_context);
|
||||
bdrv_drained_end(bs);
|
||||
}
|
||||
bdrv_set_aio_context(bs, new_context);
|
||||
}
|
||||
|
||||
blk->ctx = new_context;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blk_set_aio_context(BlockBackend *blk, AioContext *new_context,
|
||||
Error **errp)
|
||||
{
|
||||
return blk_do_set_aio_context(blk, new_context, true, errp);
|
||||
}
|
||||
|
||||
static bool blk_root_can_set_aio_ctx(BdrvChild *child, AioContext *ctx,
|
||||
GSList **ignore, Error **errp)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
|
||||
if (blk->allow_aio_context_change) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Only manually created BlockBackends that are not attached to anything
|
||||
* can change their AioContext without updating their user. */
|
||||
if (!blk->name || blk->dev) {
|
||||
/* TODO Add BB name/QOM path */
|
||||
error_setg(errp, "Cannot change iothread of active block backend");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void blk_root_set_aio_ctx(BdrvChild *child, AioContext *ctx,
|
||||
GSList **ignore)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
blk_do_set_aio_context(blk, ctx, false, &error_abort);
|
||||
}
|
||||
|
||||
void blk_add_aio_context_notifier(BlockBackend *blk,
|
||||
void (*attached_aio_context)(AioContext *new_context, void *opaque),
|
||||
void (*detach_aio_context)(void *opaque), void *opaque)
|
||||
{
|
||||
BlockBackendAioNotifier *notifier;
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
|
||||
notifier = g_new(BlockBackendAioNotifier, 1);
|
||||
notifier->attached_aio_context = attached_aio_context;
|
||||
notifier->detach_aio_context = detach_aio_context;
|
||||
notifier->opaque = opaque;
|
||||
QLIST_INSERT_HEAD(&blk->aio_notifiers, notifier, list);
|
||||
|
||||
if (bs) {
|
||||
bdrv_add_aio_context_notifier(bs, attached_aio_context,
|
||||
detach_aio_context, opaque);
|
||||
@@ -1956,25 +1871,12 @@ void blk_remove_aio_context_notifier(BlockBackend *blk,
|
||||
void (*detach_aio_context)(void *),
|
||||
void *opaque)
|
||||
{
|
||||
BlockBackendAioNotifier *notifier;
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
|
||||
if (bs) {
|
||||
bdrv_remove_aio_context_notifier(bs, attached_aio_context,
|
||||
detach_aio_context, opaque);
|
||||
}
|
||||
|
||||
QLIST_FOREACH(notifier, &blk->aio_notifiers, list) {
|
||||
if (notifier->attached_aio_context == attached_aio_context &&
|
||||
notifier->detach_aio_context == detach_aio_context &&
|
||||
notifier->opaque == opaque) {
|
||||
QLIST_REMOVE(notifier, list);
|
||||
g_free(notifier);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
abort();
|
||||
}
|
||||
|
||||
void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify)
|
||||
@@ -2047,7 +1949,6 @@ static void blk_pdiscard_entry(void *opaque)
|
||||
QEMUIOVector *qiov = rwco->iobuf;
|
||||
|
||||
rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
|
||||
@@ -2229,13 +2130,6 @@ static void blk_root_drained_begin(BdrvChild *child)
|
||||
}
|
||||
}
|
||||
|
||||
static bool blk_root_drained_poll(BdrvChild *child)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
assert(blk->quiesce_counter);
|
||||
return !!blk->in_flight;
|
||||
}
|
||||
|
||||
static void blk_root_drained_end(BdrvChild *child)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
@@ -2260,27 +2154,3 @@ void blk_unregister_buf(BlockBackend *blk, void *host)
|
||||
{
|
||||
bdrv_unregister_buf(blk_bs(blk), host);
|
||||
}
|
||||
|
||||
int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in,
|
||||
BlockBackend *blk_out, int64_t off_out,
|
||||
int bytes, BdrvRequestFlags read_flags,
|
||||
BdrvRequestFlags write_flags)
|
||||
{
|
||||
int r;
|
||||
r = blk_check_byte_request(blk_in, off_in, bytes);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
r = blk_check_byte_request(blk_out, off_out, bytes);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
return bdrv_co_copy_range(blk_in->root, off_in,
|
||||
blk_out->root, off_out,
|
||||
bytes, read_flags, write_flags);
|
||||
}
|
||||
|
||||
const BdrvChild *blk_root(BlockBackend *blk)
|
||||
{
|
||||
return blk->root;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/bswap.h"
|
||||
@@ -84,14 +85,14 @@ static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
const struct bochs_header *bochs = (const void *)buf;
|
||||
|
||||
if (buf_size < HEADER_SIZE)
|
||||
return 0;
|
||||
return 0;
|
||||
|
||||
if (!strcmp(bochs->magic, HEADER_MAGIC) &&
|
||||
!strcmp(bochs->type, REDOLOG_TYPE) &&
|
||||
!strcmp(bochs->subtype, GROWING_TYPE) &&
|
||||
((le32_to_cpu(bochs->version) == HEADER_VERSION) ||
|
||||
(le32_to_cpu(bochs->version) == HEADER_V1)))
|
||||
return 100;
|
||||
!strcmp(bochs->type, REDOLOG_TYPE) &&
|
||||
!strcmp(bochs->subtype, GROWING_TYPE) &&
|
||||
((le32_to_cpu(bochs->version) == HEADER_VERSION) ||
|
||||
(le32_to_cpu(bochs->version) == HEADER_V1)))
|
||||
return 100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -104,18 +105,23 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
struct bochs_header bochs;
|
||||
int ret;
|
||||
|
||||
/* No write support yet */
|
||||
ret = bdrv_apply_auto_read_only(bs, NULL, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
false, errp);
|
||||
if (!bs->file) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!bdrv_is_read_only(bs)) {
|
||||
error_report("Opening bochs images without an explicit read-only=on "
|
||||
"option is deprecated. Future versions will refuse to "
|
||||
"open the image instead of automatically marking the "
|
||||
"image read-only.");
|
||||
ret = bdrv_set_read_only(bs, true, errp); /* no write support yet */
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
@@ -124,8 +130,8 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
if (strcmp(bochs.magic, HEADER_MAGIC) ||
|
||||
strcmp(bochs.type, REDOLOG_TYPE) ||
|
||||
strcmp(bochs.subtype, GROWING_TYPE) ||
|
||||
((le32_to_cpu(bochs.version) != HEADER_VERSION) &&
|
||||
(le32_to_cpu(bochs.version) != HEADER_V1))) {
|
||||
((le32_to_cpu(bochs.version) != HEADER_VERSION) &&
|
||||
(le32_to_cpu(bochs.version) != HEADER_V1))) {
|
||||
error_setg(errp, "Image not in Bochs format");
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -157,7 +163,7 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
for (i = 0; i < s->catalog_size; i++)
|
||||
le32_to_cpus(&s->catalog_bitmap[i]);
|
||||
le32_to_cpus(&s->catalog_bitmap[i]);
|
||||
|
||||
s->data_offset = le32_to_cpu(bochs.header) + (s->catalog_size * 4);
|
||||
|
||||
@@ -216,7 +222,7 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||
extent_offset = (offset % s->extent_size) / 512;
|
||||
|
||||
if (s->catalog_bitmap[extent_index] == 0xffffffff) {
|
||||
return 0; /* not allocated */
|
||||
return 0; /* not allocated */
|
||||
}
|
||||
|
||||
bitmap_offset = s->data_offset +
|
||||
@@ -231,7 +237,7 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||
}
|
||||
|
||||
if (!((bitmap_entry >> (extent_offset % 8)) & 1)) {
|
||||
return 0; /* not allocated */
|
||||
return 0; /* not allocated */
|
||||
}
|
||||
|
||||
return bitmap_offset + (512 * (s->bitmap_blocks + extent_offset));
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/bswap.h"
|
||||
@@ -66,17 +67,23 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
uint32_t offsets_size, max_compressed_block_size = 1, i;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_apply_auto_read_only(bs, NULL, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
false, errp);
|
||||
if (!bs->file) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!bdrv_is_read_only(bs)) {
|
||||
error_report("Opening cloop images without an explicit read-only=on "
|
||||
"option is deprecated. Future versions will refuse to "
|
||||
"open the image instead of automatically marking the "
|
||||
"image read-only.");
|
||||
ret = bdrv_set_read_only(bs, true, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* read header */
|
||||
ret = bdrv_pread(bs->file, 128, &s->block_size, 4);
|
||||
if (ret < 0) {
|
||||
|
||||
259
block/commit.c
259
block/commit.c
@@ -31,15 +31,16 @@ enum {
|
||||
COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */
|
||||
};
|
||||
|
||||
#define SLICE_TIME 100000000ULL /* ns */
|
||||
|
||||
typedef struct CommitBlockJob {
|
||||
BlockJob common;
|
||||
RateLimit limit;
|
||||
BlockDriverState *commit_top_bs;
|
||||
BlockBackend *top;
|
||||
BlockBackend *base;
|
||||
BlockDriverState *base_bs;
|
||||
BlockdevOnError on_error;
|
||||
bool base_read_only;
|
||||
bool chain_frozen;
|
||||
int base_flags;
|
||||
char *backing_file_str;
|
||||
} CommitBlockJob;
|
||||
|
||||
@@ -48,15 +49,21 @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base,
|
||||
void *buf)
|
||||
{
|
||||
int ret = 0;
|
||||
QEMUIOVector qiov;
|
||||
struct iovec iov = {
|
||||
.iov_base = buf,
|
||||
.iov_len = bytes,
|
||||
};
|
||||
|
||||
assert(bytes < SIZE_MAX);
|
||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
||||
|
||||
ret = blk_co_pread(bs, offset, bytes, buf, 0);
|
||||
ret = blk_co_preadv(bs, offset, qiov.size, &qiov, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = blk_co_pwrite(base, offset, bytes, buf, 0);
|
||||
ret = blk_co_pwritev(base, offset, qiov.size, &qiov, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -64,98 +71,96 @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int commit_prepare(Job *job)
|
||||
{
|
||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
|
||||
typedef struct {
|
||||
int ret;
|
||||
} CommitCompleteData;
|
||||
|
||||
bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs);
|
||||
s->chain_frozen = false;
|
||||
static void commit_complete(BlockJob *job, void *opaque)
|
||||
{
|
||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common);
|
||||
CommitCompleteData *data = opaque;
|
||||
BlockDriverState *top = blk_bs(s->top);
|
||||
BlockDriverState *base = blk_bs(s->base);
|
||||
BlockDriverState *commit_top_bs = s->commit_top_bs;
|
||||
int ret = data->ret;
|
||||
bool remove_commit_top_bs = false;
|
||||
|
||||
/* Make sure commit_top_bs and top stay around until bdrv_replace_node() */
|
||||
bdrv_ref(top);
|
||||
bdrv_ref(commit_top_bs);
|
||||
|
||||
/* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before
|
||||
* the normal backing chain can be restored. */
|
||||
blk_unref(s->base);
|
||||
s->base = NULL;
|
||||
|
||||
/* FIXME: bdrv_drop_intermediate treats total failures and partial failures
|
||||
* identically. Further work is needed to disambiguate these cases. */
|
||||
return bdrv_drop_intermediate(s->commit_top_bs, s->base_bs,
|
||||
s->backing_file_str);
|
||||
}
|
||||
|
||||
static void commit_abort(Job *job)
|
||||
{
|
||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
|
||||
BlockDriverState *top_bs = blk_bs(s->top);
|
||||
|
||||
if (s->chain_frozen) {
|
||||
bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs);
|
||||
if (!block_job_is_cancelled(&s->common) && ret == 0) {
|
||||
/* success */
|
||||
ret = bdrv_drop_intermediate(s->commit_top_bs, base,
|
||||
s->backing_file_str);
|
||||
} else {
|
||||
/* XXX Can (or should) we somehow keep 'consistent read' blocked even
|
||||
* after the failed/cancelled commit job is gone? If we already wrote
|
||||
* something to base, the intermediate images aren't valid any more. */
|
||||
remove_commit_top_bs = true;
|
||||
}
|
||||
|
||||
/* Make sure commit_top_bs and top stay around until bdrv_replace_node() */
|
||||
bdrv_ref(top_bs);
|
||||
bdrv_ref(s->commit_top_bs);
|
||||
|
||||
if (s->base) {
|
||||
blk_unref(s->base);
|
||||
}
|
||||
|
||||
/* free the blockers on the intermediate nodes so that bdrv_replace_nodes
|
||||
* can succeed */
|
||||
block_job_remove_all_bdrv(&s->common);
|
||||
|
||||
/* If bdrv_drop_intermediate() failed (or was not invoked), remove the
|
||||
* commit filter driver from the backing chain now. Do this as the final
|
||||
* step so that the 'consistent read' permission can be granted.
|
||||
*
|
||||
* XXX Can (or should) we somehow keep 'consistent read' blocked even
|
||||
* after the failed/cancelled commit job is gone? If we already wrote
|
||||
* something to base, the intermediate images aren't valid any more. */
|
||||
bdrv_replace_node(s->commit_top_bs, backing_bs(s->commit_top_bs),
|
||||
&error_abort);
|
||||
|
||||
bdrv_unref(s->commit_top_bs);
|
||||
bdrv_unref(top_bs);
|
||||
}
|
||||
|
||||
static void commit_clean(Job *job)
|
||||
{
|
||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
|
||||
|
||||
/* restore base open flags here if appropriate (e.g., change the base back
|
||||
* to r/o). These reopens do not need to be atomic, since we won't abort
|
||||
* even on failure here */
|
||||
if (s->base_read_only) {
|
||||
bdrv_reopen_set_read_only(s->base_bs, true, NULL);
|
||||
if (s->base_flags != bdrv_get_flags(base)) {
|
||||
bdrv_reopen(base, s->base_flags, NULL);
|
||||
}
|
||||
|
||||
g_free(s->backing_file_str);
|
||||
blk_unref(s->top);
|
||||
|
||||
/* If there is more than one reference to the job (e.g. if called from
|
||||
* block_job_finish_sync()), block_job_completed() won't free it and
|
||||
* therefore the blockers on the intermediate nodes remain. This would
|
||||
* cause bdrv_set_backing_hd() to fail. */
|
||||
block_job_remove_all_bdrv(job);
|
||||
|
||||
block_job_completed(&s->common, ret);
|
||||
g_free(data);
|
||||
|
||||
/* If bdrv_drop_intermediate() didn't already do that, remove the commit
|
||||
* filter driver from the backing chain. Do this as the final step so that
|
||||
* the 'consistent read' permission can be granted. */
|
||||
if (remove_commit_top_bs) {
|
||||
bdrv_child_try_set_perm(commit_top_bs->backing, 0, BLK_PERM_ALL,
|
||||
&error_abort);
|
||||
bdrv_replace_node(commit_top_bs, backing_bs(commit_top_bs),
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
bdrv_unref(commit_top_bs);
|
||||
bdrv_unref(top);
|
||||
}
|
||||
|
||||
static int coroutine_fn commit_run(Job *job, Error **errp)
|
||||
static void coroutine_fn commit_run(void *opaque)
|
||||
{
|
||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
|
||||
CommitBlockJob *s = opaque;
|
||||
CommitCompleteData *data;
|
||||
int64_t offset;
|
||||
uint64_t delay_ns = 0;
|
||||
int ret = 0;
|
||||
int64_t n = 0; /* bytes */
|
||||
void *buf = NULL;
|
||||
int bytes_written = 0;
|
||||
int64_t len, base_len;
|
||||
int64_t base_len;
|
||||
|
||||
ret = len = blk_getlength(s->top);
|
||||
if (len < 0) {
|
||||
ret = s->common.len = blk_getlength(s->top);
|
||||
|
||||
if (s->common.len < 0) {
|
||||
goto out;
|
||||
}
|
||||
job_progress_set_remaining(&s->common.job, len);
|
||||
|
||||
ret = base_len = blk_getlength(s->base);
|
||||
if (base_len < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (base_len < len) {
|
||||
ret = blk_truncate(s->base, len, PREALLOC_MODE_OFF, NULL);
|
||||
if (base_len < s->common.len) {
|
||||
ret = blk_truncate(s->base, s->common.len, PREALLOC_MODE_OFF, NULL);
|
||||
if (ret) {
|
||||
goto out;
|
||||
}
|
||||
@@ -163,18 +168,18 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
|
||||
|
||||
buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE);
|
||||
|
||||
for (offset = 0; offset < len; offset += n) {
|
||||
for (offset = 0; offset < s->common.len; offset += n) {
|
||||
bool copy;
|
||||
|
||||
/* Note that even when no rate limit is applied we need to yield
|
||||
* with no pending I/O here so that bdrv_drain_all() returns.
|
||||
*/
|
||||
job_sleep_ns(&s->common.job, delay_ns);
|
||||
if (job_is_cancelled(&s->common.job)) {
|
||||
block_job_sleep_ns(&s->common, delay_ns);
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
break;
|
||||
}
|
||||
/* Copy if allocated above the base */
|
||||
ret = bdrv_is_allocated_above(blk_bs(s->top), blk_bs(s->base), false,
|
||||
ret = bdrv_is_allocated_above(blk_bs(s->top), blk_bs(s->base),
|
||||
offset, COMMIT_BUFFER_SIZE, &n);
|
||||
copy = (ret == 1);
|
||||
trace_commit_one_iteration(s, offset, n, ret);
|
||||
@@ -193,12 +198,10 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
|
||||
}
|
||||
}
|
||||
/* Publish progress */
|
||||
job_progress_update(&s->common.job, n);
|
||||
s->common.offset += n;
|
||||
|
||||
if (copy) {
|
||||
delay_ns = block_job_ratelimit_get_delay(&s->common, n);
|
||||
} else {
|
||||
delay_ns = 0;
|
||||
if (copy && s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, n);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,21 +210,27 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
|
||||
out:
|
||||
qemu_vfree(buf);
|
||||
|
||||
return ret;
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
block_job_defer_to_main_loop(&s->common, commit_complete, data);
|
||||
}
|
||||
|
||||
static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
{
|
||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common);
|
||||
|
||||
if (speed < 0) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER, "speed");
|
||||
return;
|
||||
}
|
||||
ratelimit_set_speed(&s->limit, speed, SLICE_TIME);
|
||||
}
|
||||
|
||||
static const BlockJobDriver commit_job_driver = {
|
||||
.job_driver = {
|
||||
.instance_size = sizeof(CommitBlockJob),
|
||||
.job_type = JOB_TYPE_COMMIT,
|
||||
.free = block_job_free,
|
||||
.user_resume = block_job_user_resume,
|
||||
.drain = block_job_drain,
|
||||
.run = commit_run,
|
||||
.prepare = commit_prepare,
|
||||
.abort = commit_abort,
|
||||
.clean = commit_clean
|
||||
},
|
||||
.instance_size = sizeof(CommitBlockJob),
|
||||
.job_type = BLOCK_JOB_TYPE_COMMIT,
|
||||
.set_speed = commit_set_speed,
|
||||
.start = commit_run,
|
||||
};
|
||||
|
||||
static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs,
|
||||
@@ -230,12 +239,17 @@ static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs,
|
||||
return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
|
||||
}
|
||||
|
||||
static void bdrv_commit_top_refresh_filename(BlockDriverState *bs)
|
||||
static void bdrv_commit_top_refresh_filename(BlockDriverState *bs, QDict *opts)
|
||||
{
|
||||
bdrv_refresh_filename(bs->backing->bs);
|
||||
pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
|
||||
bs->backing->bs->filename);
|
||||
}
|
||||
|
||||
static void bdrv_commit_top_close(BlockDriverState *bs)
|
||||
{
|
||||
}
|
||||
|
||||
static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||
const BdrvChildRole *role,
|
||||
BlockReopenQueue *reopen_queue,
|
||||
@@ -253,16 +267,17 @@ static BlockDriver bdrv_commit_top = {
|
||||
.bdrv_co_preadv = bdrv_commit_top_preadv,
|
||||
.bdrv_co_block_status = bdrv_co_block_status_from_backing,
|
||||
.bdrv_refresh_filename = bdrv_commit_top_refresh_filename,
|
||||
.bdrv_close = bdrv_commit_top_close,
|
||||
.bdrv_child_perm = bdrv_commit_top_child_perm,
|
||||
};
|
||||
|
||||
void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
BlockDriverState *base, BlockDriverState *top,
|
||||
int creation_flags, int64_t speed,
|
||||
BlockDriverState *base, BlockDriverState *top, int64_t speed,
|
||||
BlockdevOnError on_error, const char *backing_file_str,
|
||||
const char *filter_node_name, Error **errp)
|
||||
{
|
||||
CommitBlockJob *s;
|
||||
int orig_base_flags;
|
||||
BlockDriverState *iter;
|
||||
BlockDriverState *commit_top_bs = NULL;
|
||||
Error *local_err = NULL;
|
||||
@@ -274,16 +289,18 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
return;
|
||||
}
|
||||
|
||||
s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL,
|
||||
speed, creation_flags, NULL, NULL, errp);
|
||||
s = block_job_create(job_id, &commit_job_driver, bs, 0, BLK_PERM_ALL,
|
||||
speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* convert base to r/w, if necessary */
|
||||
s->base_read_only = bdrv_is_read_only(base);
|
||||
if (s->base_read_only) {
|
||||
if (bdrv_reopen_set_read_only(base, false, errp) != 0) {
|
||||
orig_base_flags = bdrv_get_flags(base);
|
||||
if (!(orig_base_flags & BDRV_O_RDWR)) {
|
||||
bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
@@ -298,20 +315,26 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
if (!filter_node_name) {
|
||||
commit_top_bs->implicit = true;
|
||||
}
|
||||
|
||||
/* So that we can always drop this node */
|
||||
commit_top_bs->never_freeze = true;
|
||||
|
||||
commit_top_bs->total_sectors = top->total_sectors;
|
||||
bdrv_set_aio_context(commit_top_bs, bdrv_get_aio_context(top));
|
||||
|
||||
bdrv_append(commit_top_bs, top, &local_err);
|
||||
bdrv_set_backing_hd(commit_top_bs, top, &local_err);
|
||||
if (local_err) {
|
||||
bdrv_unref(commit_top_bs);
|
||||
commit_top_bs = NULL;
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
bdrv_replace_node(top, commit_top_bs, &local_err);
|
||||
if (local_err) {
|
||||
bdrv_unref(commit_top_bs);
|
||||
commit_top_bs = NULL;
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->commit_top_bs = commit_top_bs;
|
||||
bdrv_unref(commit_top_bs);
|
||||
|
||||
/* Block all nodes between top and base, because they will
|
||||
* disappear from the chain after this operation. */
|
||||
@@ -329,18 +352,12 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
if (bdrv_freeze_backing_chain(commit_top_bs, base, errp) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
s->chain_frozen = true;
|
||||
|
||||
ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->base = blk_new(s->common.job.aio_context,
|
||||
BLK_PERM_CONSISTENT_READ
|
||||
s->base = blk_new(BLK_PERM_CONSISTENT_READ
|
||||
| BLK_PERM_WRITE
|
||||
| BLK_PERM_RESIZE,
|
||||
BLK_PERM_CONSISTENT_READ
|
||||
@@ -350,41 +367,33 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
s->base_bs = base;
|
||||
|
||||
/* Required permissions are already taken with block_job_add_bdrv() */
|
||||
s->top = blk_new(s->common.job.aio_context, 0, BLK_PERM_ALL);
|
||||
s->top = blk_new(0, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(s->top, top, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->base_flags = orig_base_flags;
|
||||
s->backing_file_str = g_strdup(backing_file_str);
|
||||
s->on_error = on_error;
|
||||
|
||||
trace_commit_start(bs, base, top, s);
|
||||
job_start(&s->common.job);
|
||||
block_job_start(&s->common);
|
||||
return;
|
||||
|
||||
fail:
|
||||
if (s->chain_frozen) {
|
||||
bdrv_unfreeze_backing_chain(commit_top_bs, base);
|
||||
}
|
||||
if (s->base) {
|
||||
blk_unref(s->base);
|
||||
}
|
||||
if (s->top) {
|
||||
blk_unref(s->top);
|
||||
}
|
||||
if (s->base_read_only) {
|
||||
bdrv_reopen_set_read_only(base, true, NULL);
|
||||
}
|
||||
job_early_fail(&s->common.job);
|
||||
/* commit_top_bs has to be replaced after deleting the block job,
|
||||
* otherwise this would fail because of lack of permissions. */
|
||||
if (commit_top_bs) {
|
||||
bdrv_replace_node(commit_top_bs, top, &error_abort);
|
||||
}
|
||||
block_job_early_fail(&s->common);
|
||||
}
|
||||
|
||||
|
||||
@@ -397,9 +406,8 @@ int bdrv_commit(BlockDriverState *bs)
|
||||
BlockDriverState *backing_file_bs = NULL;
|
||||
BlockDriverState *commit_top_bs = NULL;
|
||||
BlockDriver *drv = bs->drv;
|
||||
AioContext *ctx;
|
||||
int64_t offset, length, backing_length;
|
||||
int ro;
|
||||
int ro, open_flags;
|
||||
int64_t n;
|
||||
int ret = 0;
|
||||
uint8_t *buf = NULL;
|
||||
@@ -418,16 +426,16 @@ int bdrv_commit(BlockDriverState *bs)
|
||||
}
|
||||
|
||||
ro = bs->backing->bs->read_only;
|
||||
open_flags = bs->backing->bs->open_flags;
|
||||
|
||||
if (ro) {
|
||||
if (bdrv_reopen_set_read_only(bs->backing->bs, false, NULL)) {
|
||||
if (bdrv_reopen(bs->backing->bs, open_flags | BDRV_O_RDWR, NULL)) {
|
||||
return -EACCES;
|
||||
}
|
||||
}
|
||||
|
||||
ctx = bdrv_get_aio_context(bs);
|
||||
src = blk_new(ctx, BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL);
|
||||
backing = blk_new(ctx, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
src = blk_new(BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL);
|
||||
backing = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
|
||||
|
||||
ret = blk_insert_bs(src, bs, &local_err);
|
||||
if (ret < 0) {
|
||||
@@ -444,6 +452,7 @@ int bdrv_commit(BlockDriverState *bs)
|
||||
error_report_err(local_err);
|
||||
goto ro_cleanup;
|
||||
}
|
||||
bdrv_set_aio_context(commit_top_bs, bdrv_get_aio_context(backing_file_bs));
|
||||
|
||||
bdrv_set_backing_hd(commit_top_bs, backing_file_bs, &error_abort);
|
||||
bdrv_set_backing_hd(bs, commit_top_bs, &error_abort);
|
||||
@@ -530,7 +539,7 @@ ro_cleanup:
|
||||
|
||||
if (ro) {
|
||||
/* ignoring error return here */
|
||||
bdrv_reopen_set_read_only(bs->backing->bs, true, NULL);
|
||||
bdrv_reopen(bs->backing->bs, open_flags & ~BDRV_O_RDWR, NULL);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user