Compare commits

..

5 Commits

Author SHA1 Message Date
Fabiano Rosas
6321d78dc5 include/block/block_int: Document protocol related functions
Clarify that:

- for protocols the brdv_file_open function is used instead
of bdrv_open;

- when protocol_name is set, a driver should expect
to be given only a filename and no other options.

Signed-off-by: Fabiano Rosas <farosas@linux.vnet.ibm.com>
2018-03-12 18:11:01 -03:00
Fabiano Rosas
a0974f99aa block/blkreplay: Remove protocol-related fields
The blkreplay driver is not a protocol so it should implement bdrv_open
instead of bdrv_file_open and not provide a protocol_name.

Attempts to invoke this driver using protocol syntax
(i.e. blkreplay:<filename:options:...>) will now fail gracefully:

  $ qemu-img info blkreplay:foo
  qemu-img: Could not open 'blkreplay:foo': Unknown protocol 'blkreplay'

Signed-off-by: Fabiano Rosas <farosas@linux.vnet.ibm.com>
Reviewed-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>
Reviewed-by: Max Reitz <mreitz@redhat.com>
2018-03-12 18:11:01 -03:00
Fabiano Rosas
42883c01bf block/throttle: Remove protocol-related fields
The throttle driver is not a protocol so it should implement bdrv_open
instead of bdrv_file_open and not provide a protocol_name.

Attempts to invoke this driver using protocol syntax
(i.e. throttle:<filename:options:...>) will now fail gracefully:

  $ qemu-img info throttle:foo
  qemu-img: Could not open 'throttle:foo': Unknown protocol 'throttle'

Signed-off-by: Fabiano Rosas <farosas@linux.vnet.ibm.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
2018-03-12 18:11:01 -03:00
Fabiano Rosas
7966c2b312 block/quorum: Remove protocol-related fields
The quorum driver is not a protocol so it should implement bdrv_open
instead of bdrv_file_open and not provide a protocol_name.

Attempts to invoke this driver using protocol syntax
(i.e. quorum:<filename:options:...>) will now fail gracefully:

  $ qemu-img info quorum:foo
  qemu-img: Could not open 'quorum:foo': Unknown protocol 'quorum'

Signed-off-by: Fabiano Rosas <farosas@linux.vnet.ibm.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
2018-03-12 18:11:01 -03:00
Fabiano Rosas
5888011244 block/replication: Remove protocol_name field
The protocol_name field is used when selecting a driver via protocol
syntax (i.e. <protocol_name>:<filename:options:...>). Drivers that are
only selected explicitly (e.g. driver=replication,mode=primary,...)
should not have a protocol_name.

This patch removes the protocol_name field from the brdv_replication
structure so that attempts to invoke this driver using protocol syntax
will fail gracefully:

  $ qemu-img info replication:foo
  qemu-img: Could not open 'replication:': Unknown protocol 'replication'

Buglink: https://bugs.launchpad.net/qemu/+bug/1726733
Signed-off-by: Fabiano Rosas <farosas@linux.vnet.ibm.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
2018-03-12 18:11:01 -03:00
5594 changed files with 176438 additions and 560051 deletions

View File

@@ -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)

View File

@@ -1,10 +1,4 @@
# EditorConfig is a file format and collection of text editor plugins # http://editorconfig.org
# for maintaining consistent coding styles between different editors
# and IDEs. Most popular editors support this either natively or via
# plugin.
#
# Check https://editorconfig.org for details.
root = true root = true
[*] [*]
@@ -12,32 +6,10 @@ end_of_line = lf
insert_final_newline = true insert_final_newline = true
charset = utf-8 charset = utf-8
[*.mak]
indent_style = tab
indent_size = 8
file_type_emacs = makefile
[Makefile*] [Makefile*]
indent_style = tab indent_style = tab
indent_size = 8 indent_size = 8
file_type_emacs = makefile
[*.{c,h}] [*.{c,h}]
indent_style = space indent_style = space
indent_size = 4 indent_size = 4
[*.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
View File

@@ -1,4 +1,3 @@
/.doctrees
/config-devices.* /config-devices.*
/config-all-devices.* /config-all-devices.*
/config-all-disas.* /config-all-disas.*
@@ -6,7 +5,6 @@
/config-target.* /config-target.*
/config.status /config.status
/config-temp /config-temp
/elf2dmp
/trace-events-all /trace-events-all
/trace/generated-events.h /trace/generated-events.h
/trace/generated-events.c /trace/generated-events.c
@@ -32,21 +30,79 @@
/qapi-gen-timestamp /qapi-gen-timestamp
/qapi/qapi-builtin-types.[ch] /qapi/qapi-builtin-types.[ch]
/qapi/qapi-builtin-visit.[ch] /qapi/qapi-builtin-visit.[ch]
/qapi/qapi-commands-*.[ch] /qapi/qapi-commands-block-core.[ch]
/qapi/qapi-commands-block.[ch]
/qapi/qapi-commands-char.[ch]
/qapi/qapi-commands-common.[ch]
/qapi/qapi-commands-crypto.[ch]
/qapi/qapi-commands-introspect.[ch]
/qapi/qapi-commands-migration.[ch]
/qapi/qapi-commands-misc.[ch]
/qapi/qapi-commands-net.[ch]
/qapi/qapi-commands-rocker.[ch]
/qapi/qapi-commands-run-state.[ch]
/qapi/qapi-commands-sockets.[ch]
/qapi/qapi-commands-tpm.[ch]
/qapi/qapi-commands-trace.[ch]
/qapi/qapi-commands-transaction.[ch]
/qapi/qapi-commands-ui.[ch]
/qapi/qapi-commands.[ch] /qapi/qapi-commands.[ch]
/qapi/qapi-emit-events.[ch] /qapi/qapi-events-block-core.[ch]
/qapi/qapi-events-*.[ch] /qapi/qapi-events-block.[ch]
/qapi/qapi-events-char.[ch]
/qapi/qapi-events-common.[ch]
/qapi/qapi-events-crypto.[ch]
/qapi/qapi-events-introspect.[ch]
/qapi/qapi-events-migration.[ch]
/qapi/qapi-events-misc.[ch]
/qapi/qapi-events-net.[ch]
/qapi/qapi-events-rocker.[ch]
/qapi/qapi-events-run-state.[ch]
/qapi/qapi-events-sockets.[ch]
/qapi/qapi-events-tpm.[ch]
/qapi/qapi-events-trace.[ch]
/qapi/qapi-events-transaction.[ch]
/qapi/qapi-events-ui.[ch]
/qapi/qapi-events.[ch] /qapi/qapi-events.[ch]
/qapi/qapi-introspect.[ch] /qapi/qapi-introspect.[ch]
/qapi/qapi-types-*.[ch] /qapi/qapi-types-block-core.[ch]
/qapi/qapi-types-block.[ch]
/qapi/qapi-types-char.[ch]
/qapi/qapi-types-common.[ch]
/qapi/qapi-types-crypto.[ch]
/qapi/qapi-types-introspect.[ch]
/qapi/qapi-types-migration.[ch]
/qapi/qapi-types-misc.[ch]
/qapi/qapi-types-net.[ch]
/qapi/qapi-types-rocker.[ch]
/qapi/qapi-types-run-state.[ch]
/qapi/qapi-types-sockets.[ch]
/qapi/qapi-types-tpm.[ch]
/qapi/qapi-types-trace.[ch]
/qapi/qapi-types-transaction.[ch]
/qapi/qapi-types-ui.[ch]
/qapi/qapi-types.[ch] /qapi/qapi-types.[ch]
/qapi/qapi-visit-*.[ch] /qapi/qapi-visit-block-core.[ch]
/qapi/qapi-visit-block.[ch]
/qapi/qapi-visit-char.[ch]
/qapi/qapi-visit-common.[ch]
/qapi/qapi-visit-crypto.[ch]
/qapi/qapi-visit-introspect.[ch]
/qapi/qapi-visit-migration.[ch]
/qapi/qapi-visit-misc.[ch]
/qapi/qapi-visit-net.[ch]
/qapi/qapi-visit-rocker.[ch]
/qapi/qapi-visit-run-state.[ch]
/qapi/qapi-visit-sockets.[ch]
/qapi/qapi-visit-tpm.[ch]
/qapi/qapi-visit-trace.[ch]
/qapi/qapi-visit-transaction.[ch]
/qapi/qapi-visit-ui.[ch]
/qapi/qapi-visit.[ch] /qapi/qapi-visit.[ch]
/qapi/qapi-doc.texi /qapi/qapi-doc.texi
/qemu-doc.html /qemu-doc.html
/qemu-doc.info /qemu-doc.info
/qemu-doc.txt /qemu-doc.txt
/qemu-edid
/qemu-img /qemu-img
/qemu-nbd /qemu-nbd
/qemu-options.def /qemu-options.def
@@ -95,9 +151,7 @@
.sdk .sdk
*.gcda *.gcda
*.gcno *.gcno
*.gcov
/pc-bios/bios-pq/status /pc-bios/bios-pq/status
/pc-bios/edk2-*.fd
/pc-bios/vgabios-pq/status /pc-bios/vgabios-pq/status
/pc-bios/optionrom/linuxboot.asm /pc-bios/optionrom/linuxboot.asm
/pc-bios/optionrom/linuxboot.bin /pc-bios/optionrom/linuxboot.bin
@@ -107,10 +161,6 @@
/pc-bios/optionrom/linuxboot_dma.bin /pc-bios/optionrom/linuxboot_dma.bin
/pc-bios/optionrom/linuxboot_dma.raw /pc-bios/optionrom/linuxboot_dma.raw
/pc-bios/optionrom/linuxboot_dma.img /pc-bios/optionrom/linuxboot_dma.img
/pc-bios/optionrom/pvh.asm
/pc-bios/optionrom/pvh.bin
/pc-bios/optionrom/pvh.raw
/pc-bios/optionrom/pvh.img
/pc-bios/optionrom/multiboot.asm /pc-bios/optionrom/multiboot.asm
/pc-bios/optionrom/multiboot.bin /pc-bios/optionrom/multiboot.bin
/pc-bios/optionrom/multiboot.raw /pc-bios/optionrom/multiboot.raw
@@ -121,7 +171,6 @@
/pc-bios/optionrom/kvmvapic.img /pc-bios/optionrom/kvmvapic.img
/pc-bios/s390-ccw/s390-ccw.elf /pc-bios/s390-ccw/s390-ccw.elf
/pc-bios/s390-ccw/s390-ccw.img /pc-bios/s390-ccw/s390-ccw.img
/docs/built
/docs/interop/qemu-ga-qapi.texi /docs/interop/qemu-ga-qapi.texi
/docs/interop/qemu-ga-ref.html /docs/interop/qemu-ga-ref.html
/docs/interop/qemu-ga-ref.info* /docs/interop/qemu-ga-ref.info*
@@ -157,4 +206,3 @@ trace-dtrace-root.h
trace-dtrace-root.dtrace trace-dtrace-root.dtrace
trace-ust-all.h trace-ust-all.h
trace-ust-all.c trace-ust-all.c
/target/arm/decode-sve.inc.c

View File

@@ -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

48
.gitmodules vendored
View File

@@ -1,60 +1,48 @@
[submodule "roms/vgabios"]
path = roms/vgabios
url = git://git.qemu-project.org/vgabios.git/
[submodule "roms/seabios"] [submodule "roms/seabios"]
path = roms/seabios path = roms/seabios
url = https://git.qemu.org/git/seabios.git/ url = git://git.qemu-project.org/seabios.git/
[submodule "roms/SLOF"] [submodule "roms/SLOF"]
path = roms/SLOF path = roms/SLOF
url = https://git.qemu.org/git/SLOF.git url = git://git.qemu-project.org/SLOF.git
[submodule "roms/ipxe"] [submodule "roms/ipxe"]
path = roms/ipxe path = roms/ipxe
url = https://git.qemu.org/git/ipxe.git url = git://git.qemu-project.org/ipxe.git
[submodule "roms/openbios"] [submodule "roms/openbios"]
path = roms/openbios path = roms/openbios
url = https://git.qemu.org/git/openbios.git url = git://git.qemu-project.org/openbios.git
[submodule "roms/openhackware"] [submodule "roms/openhackware"]
path = roms/openhackware path = roms/openhackware
url = https://git.qemu.org/git/openhackware.git url = git://git.qemu-project.org/openhackware.git
[submodule "roms/qemu-palcode"] [submodule "roms/qemu-palcode"]
path = roms/qemu-palcode path = roms/qemu-palcode
url = https://git.qemu.org/git/qemu-palcode.git url = git://github.com/rth7680/qemu-palcode.git
[submodule "roms/sgabios"] [submodule "roms/sgabios"]
path = roms/sgabios path = roms/sgabios
url = https://git.qemu.org/git/sgabios.git url = git://git.qemu-project.org/sgabios.git
[submodule "dtc"] [submodule "dtc"]
path = dtc path = dtc
url = https://git.qemu.org/git/dtc.git url = git://git.qemu-project.org/dtc.git
[submodule "roms/u-boot"] [submodule "roms/u-boot"]
path = roms/u-boot path = roms/u-boot
url = https://git.qemu.org/git/u-boot.git url = git://git.qemu-project.org/u-boot.git
[submodule "roms/skiboot"] [submodule "roms/skiboot"]
path = roms/skiboot path = roms/skiboot
url = https://git.qemu.org/git/skiboot.git url = git://git.qemu.org/skiboot.git
[submodule "roms/QemuMacDrivers"] [submodule "roms/QemuMacDrivers"]
path = roms/QemuMacDrivers path = roms/QemuMacDrivers
url = https://git.qemu.org/git/QemuMacDrivers.git url = git://git.qemu.org/QemuMacDrivers.git
[submodule "ui/keycodemapdb"] [submodule "ui/keycodemapdb"]
path = ui/keycodemapdb path = ui/keycodemapdb
url = https://git.qemu.org/git/keycodemapdb.git url = git://git.qemu.org/keycodemapdb.git
[submodule "capstone"] [submodule "capstone"]
path = capstone path = capstone
url = https://git.qemu.org/git/capstone.git url = git://git.qemu.org/capstone.git
[submodule "roms/seabios-hppa"] [submodule "roms/seabios-hppa"]
path = roms/seabios-hppa path = roms/seabios-hppa
url = https://git.qemu.org/git/seabios-hppa.git url = git://github.com/hdeller/seabios-hppa.git
[submodule "roms/u-boot-sam460ex"] [submodule "roms/u-boot-sam460ex"]
path = roms/u-boot-sam460ex path = roms/u-boot-sam460ex
url = https://git.qemu.org/git/u-boot-sam460ex.git url = git://github.com/zbalaton/u-boot-sam460ex
[submodule "tests/fp/berkeley-testfloat-3"]
path = tests/fp/berkeley-testfloat-3
url = https://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
[submodule "roms/opensbi"]
path = roms/opensbi
url = https://git.qemu.org/git/opensbi.git

View File

@@ -1,7 +1,6 @@
# This mailmap fixes up author names/addresses. # This mailmap just translates the weird addresses from the original import into git
# into proper addresses so that they are counted properly in git shortlog output.
# The first section translates weird addresses from the original git import #
# into proper addresses so that they are counted properly by git shortlog.
Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162> Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
Anthony Liguori <anthony@codemonkey.ws> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162> Anthony Liguori <anthony@codemonkey.ws> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162>
Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com> Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com>
@@ -12,28 +11,14 @@ Fabrice Bellard <fabrice@bellard.org> bellard <bellard@c046a42c-6fe2-441c-8c8c-7
James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com> James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com>
Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162> Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162> Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
Yongbok Kim <yongbok.kim@mips.com> <yongbok.kim@imgtec.com> Paul Burton <paul.burton@mips.com> <paul.burton@imgtec.com>
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@mips.com> Paul Burton <paul.burton@mips.com> <paul@archlinuxmips.org>
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@imgtec.com>
Paul Burton <pburton@wavecomp.com> <paul.burton@mips.com>
Paul Burton <pburton@wavecomp.com> <paul.burton@imgtec.com>
Paul Burton <pburton@wavecomp.com> <paul@archlinuxmips.org>
Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162> Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162> malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162>
# There is also a: # There is also a:
# (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162> # (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162>
# for the cvs2svn initialization commit e63c3dc74bf. # for the cvs2svn initialization commit e63c3dc74bf.
#
# Next, translate a few commits where mailman rewrote the From: line due
# to strict SPF, although we prefer to avoid adding more entries like that.
Ed Swierk <eswierk@skyportsystems.com> Ed Swierk via Qemu-devel <qemu-devel@nongnu.org>
Ian McKellar <ianloic@google.com> Ian McKellar via Qemu-devel <qemu-devel@nongnu.org>
Julia Suvorova <jusual@mail.ru> Julia Suvorova via Qemu-devel <qemu-devel@nongnu.org>
Justin Terry (VM) <juterry@microsoft.com> Justin Terry (VM) via Qemu-devel <qemu-devel@nongnu.org>
# Also list preferred name forms where people have changed their # Also list preferred name forms where people have changed their
# git author config, or had utf8/latin1 encoding issues. # git author config
Daniel P. Berrangé <berrange@redhat.com> Daniel P. Berrangé <berrange@redhat.com>
Reimar Döffinger <Reimar.Doeffinger@gmx.de>

View File

@@ -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

View File

@@ -35,5 +35,13 @@ build:
options: "-e HOME=/root" options: "-e HOME=/root"
ci: ci:
- unset CC - unset CC
# some targets require newer up to date packages, for example TARGET_LIST matching
# aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu)
# see the configure script:
# error_exit "DTC (libfdt) version >= 1.4.2 not present. Your options:"
# " (1) Preferred: Install the DTC (libfdt) devel package"
# " (2) Fetch the DTC submodule, using:"
# " git submodule update --init dtc"
- dpkg --compare-versions `dpkg-query --showformat='${Version}' --show libfdt-dev` ge 1.4.2 || git submodule update --init dtc
- ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST} - ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST}
- make -j$(($(getconf _NPROCESSORS_ONLN) + 1)) - make -j$(($(getconf _NPROCESSORS_ONLN) + 1))

View File

@@ -1,13 +1,10 @@
# The current Travis default is a VM based 16.04 Xenial on GCE sudo: false
# Additional builds with specific requirements for a full VM need to
# be added as additional matrix: entries later on
dist: xenial
language: c language: c
python:
- "2.6"
compiler: compiler:
- gcc - gcc
cache: ccache cache: ccache
addons: addons:
apt: apt:
packages: packages:
@@ -31,20 +28,12 @@ addons:
- libseccomp-dev - libseccomp-dev
- libspice-protocol-dev - libspice-protocol-dev
- libspice-server-dev - libspice-server-dev
- libssh-dev - libssh2-1-dev
- liburcu-dev - liburcu-dev
- libusb-1.0-0-dev - libusb-1.0-0-dev
- libvte-2.91-dev - libvte-2.90-dev
- sparse - sparse
- uuid-dev - uuid-dev
- gcovr
homebrew:
packages:
- glib
- pixman
- gnu-sed
update: true
# The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu # The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu
# to prevent IRC notifications from forks. This was created using: # to prevent IRC notifications from forks. This was created using:
@@ -55,203 +44,130 @@ notifications:
- secure: "F7GDRgjuOo5IUyRLqSkmDL7kvdU4UcH3Lm/W2db2JnDHTGCqgEdaYEYKciyCLZ57vOTsTsOgesN8iUT7hNHBd1KWKjZe9KDTZWppWRYVwAwQMzVeSOsbbU4tRoJ6Pp+3qhH1Z0eGYR9ZgKYAoTumDFgSAYRp4IscKS8jkoedOqM=" - secure: "F7GDRgjuOo5IUyRLqSkmDL7kvdU4UcH3Lm/W2db2JnDHTGCqgEdaYEYKciyCLZ57vOTsTsOgesN8iUT7hNHBd1KWKjZe9KDTZWppWRYVwAwQMzVeSOsbbU4tRoJ6Pp+3qhH1Z0eGYR9ZgKYAoTumDFgSAYRp4IscKS8jkoedOqM="
on_success: change on_success: change
on_failure: always on_failure: always
env: env:
global: global:
- SRC_DIR="." - TEST_CMD="make check"
- BUILD_DIR="." - MAKEFLAGS="-j3"
- BASE_CONFIG="--disable-docs --disable-tools" matrix:
- TEST_CMD="make check -j3 V=1" - CONFIG=""
# This is broadly a list of "mainline" softmmu targets which have support across the major distros - CONFIG="--enable-debug --enable-debug-tcg --enable-trace-backends=log"
- MAIN_SOFTMMU_TARGETS="aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu" - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb"
- CONFIG="--enable-modules --disable-linux-user"
- CONFIG="--with-coroutine=ucontext --disable-linux-user"
- CONFIG="--with-coroutine=sigaltstack --disable-linux-user"
git: git:
# we want to do this ourselves # we want to do this ourselves
submodules: false submodules: false
before_install:
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update ; fi
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install libffi gettext glib pixman ; fi
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
- git submodule update --init --recursive
before_script: before_script:
- mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR} - ./configure ${CONFIG}
- ${SRC_DIR}/configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; }
script: script:
- make -j3 && travis_retry ${TEST_CMD} - make ${MAKEFLAGS} && ${TEST_CMD}
matrix: matrix:
include: include:
- env: # Test with CLang for compile portability
- CONFIG="--disable-system --static" - env: CONFIG=""
# 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"
compiler: clang 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 # gprof/gcov are GCC features
- env: - env: CONFIG="--enable-gprof --enable-gcov --disable-pie"
- CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=${MAIN_SOFTMMU_TARGETS}" compiler: gcc
after_success:
- ${SRC_DIR}/scripts/travis/coverage-summary.sh
# We manually include builds which we disable "make check" for # We manually include builds which we disable "make check" for
- env: - env: CONFIG="--enable-debug --enable-tcg-interpreter"
- CONFIG="--without-default-devices --disable-user" TEST_CMD=""
- TEST_CMD="" compiler: gcc
- env: CONFIG="--enable-trace-backends=simple"
TEST_CMD=""
# We manually include builds which we disable "make check" for compiler: gcc
- env: - env: CONFIG="--enable-trace-backends=ftrace"
- CONFIG="--enable-debug --enable-tcg-interpreter" TEST_CMD=""
- TEST_CMD="" compiler: gcc
- env: CONFIG="--enable-trace-backends=ust"
TEST_CMD=""
# We don't need to exercise every backend with every front-end compiler: gcc
- env: - env: CONFIG="--disable-tcg"
- CONFIG="--enable-trace-backends=log,simple,syslog --disable-system" TEST_CMD=""
- TEST_CMD="" compiler: gcc
- env: CONFIG=""
- env:
- CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu"
- TEST_CMD=""
- env:
- CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu"
- TEST_CMD=""
# MacOSX builds
- env:
- CONFIG="--target-list=${MAIN_SOFTMMU_TARGETS}"
os: osx os: osx
osx_image: xcode9.4
compiler: clang compiler: clang
# Plain Trusty System Build
- env: CONFIG="--disable-linux-user"
- env: sudo: required
- CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu" addons:
os: osx dist: trusty
osx_image: xcode10.2 compiler: gcc
compiler: clang before_install:
- sudo apt-get update -qq
- sudo apt-get build-dep -qq qemu
# Python builds - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
- env: - git submodule update --init --recursive
- CONFIG="--target-list=x86_64-softmmu" # Plain Trusty Linux User Build
language: python - env: CONFIG="--disable-system"
sudo: required
addons:
dist: trusty
compiler: gcc
before_install:
- sudo apt-get update -qq
- sudo apt-get build-dep -qq qemu
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
- git submodule update --init --recursive
# Trusty System build with latest stable clang & python 3.0
- sudo: required
addons:
dist: trusty
language: generic
compiler: none
python: python:
- "3.4" - "3.0"
env:
- COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9
- env: - CONFIG="--disable-linux-user --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3"
- CONFIG="--target-list=x86_64-softmmu" before_install:
language: python - wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
- sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main'
- sudo apt-get update -qq
- sudo apt-get install -qq -y clang-3.9
- sudo apt-get build-dep -qq qemu
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
- git submodule update --init --recursive
before_script:
- ./configure ${CONFIG} || cat config.log
# Trusty Linux User build with latest stable clang & python 3.6
- sudo: required
addons:
dist: trusty
language: generic
compiler: none
python: python:
- "3.6" - "3.6"
env:
- COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9
# Acceptance (Functional) tests - CONFIG="--disable-system --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3"
- env: before_install:
- CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu,mips-softmmu,mips64el-softmmu,aarch64-softmmu,arm-softmmu,s390x-softmmu,alpha-softmmu" - wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
- TEST_CMD="make check-acceptance" - sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main'
after_failure: - sudo apt-get update -qq
- cat tests/results/latest/job.log - sudo apt-get install -qq -y clang-3.9
addons: - sudo apt-get build-dep -qq qemu
apt: - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
packages: - git submodule update --init --recursive
- python3-pip before_script:
- python3.5-venv - ./configure ${CONFIG} || cat config.log
# Using newer GCC with sanitizers # Using newer GCC with sanitizers
- addons: - addons:
apt: apt:
update: true
sources: sources:
# PPAs for newer toolchains # PPAs for newer toolchains
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
packages: packages:
# Extra toolchains # Extra toolchains
- gcc-9 - gcc-5
- g++-9 - g++-5
# Build dependencies # Build dependencies
- libaio-dev - libaio-dev
- libattr1-dev - libattr1-dev
@@ -271,28 +187,17 @@ matrix:
- libseccomp-dev - libseccomp-dev
- libspice-protocol-dev - libspice-protocol-dev
- libspice-server-dev - libspice-server-dev
- libssh-dev - libssh2-1-dev
- liburcu-dev - liburcu-dev
- libusb-1.0-0-dev - libusb-1.0-0-dev
- libvte-2.91-dev - libvte-2.90-dev
- sparse - sparse
- uuid-dev - uuid-dev
language: generic language: generic
compiler: none compiler: none
env: env:
- COMPILER_NAME=gcc CXX=g++-9 CC=gcc-9 - COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5
- CONFIG="--cc=gcc-9 --cxx=g++-9 --disable-pie --disable-linux-user" - CONFIG="--cc=gcc-5 --cxx=g++-5 --disable-pie --disable-linux-user"
- TEST_CMD="" - TEST_CMD=""
before_script: before_script:
- ./configure ${CONFIG} --extra-cflags="-g3 -O0 -Wno-error=stringop-truncation -fsanitize=thread -fuse-ld=gold" || { cat config.log && exit 1; } - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || cat config.log
# Run check-tcg against linux-user
- env:
- CONFIG="--disable-system"
- TEST_CMD="make -j3 check-tcg V=1"
# Run check-tcg against softmmu targets
- env:
- CONFIG="--target-list=xtensa-softmmu,arm-softmmu,aarch64-softmmu,alpha-softmmu"
- TEST_CMD="make -j3 check-tcg V=1"

View File

@@ -29,45 +29,6 @@ Spaces of course are superior to tabs because:
Do not leave whitespace dangling off the ends of lines. 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 2. Line width
Lines should be 80 characters; try not to make them longer. 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 When comparing a variable for (in)equality with a constant, list the
constant on the right, as in: constant on the right, as in:
if (a == 1) { if (a == 1) {
/* Reads like: "If a equals 1" */ /* Reads like: "If a equals 1" */
do_something(); do_something();
} }
Rationale: Yoda conditions (as in 'if (1 == a)') are awkward to read. Rationale: Yoda conditions (as in 'if (1 == a)') are awkward to read.
Besides, good compilers already warn users when '==' is mis-typed as '=', 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 Rationale: The // form is valid in C99, so this is purely a matter of
consistency of style. The checkpatch script will warn you about this. consistency of style. The checkpatch script will warn you about this.
Multiline comment blocks should have a row of stars on the left,
and the initial /* and terminating */ both on their own lines:
/*
* like
* this
*/
This is the same format required by the Linux kernel coding style.
(Some of the existing comments in the codebase use the GNU Coding
Standards form which does not have stars on the left, or other
variations; avoid these when writing new comments, but don't worry
about converting to the preferred form unless you're editing that
comment anyway.)
Rationale: Consistency, and ease of visually picking out a multiline
comment from the surrounding code.
8. trace-events style 8. trace-events style
8.1 0x prefix 8.1 0x prefix

View File

@@ -1,8 +1,8 @@
GNU LESSER GENERAL PUBLIC LICENSE GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999 Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc. Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed. of this license document, but changing it is not allowed.
@@ -10,7 +10,7 @@
as the successor of the GNU Library Public License, version 2, hence as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.] the version number 2.1.]
Preamble Preamble
The licenses for most software are designed to take away your The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public freedom to share and change it. By contrast, the GNU General Public
@@ -112,7 +112,7 @@ modification follow. Pay close attention to the difference between a
former contains code derived from the library, whereas the latter must former contains code derived from the library, whereas the latter must
be combined with the library in order to run. be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other 0. This License Agreement applies to any software library or other
@@ -146,7 +146,7 @@ such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does. and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's 1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an you conspicuously and appropriately publish on each copy an
@@ -432,7 +432,7 @@ decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing of all derivatives of our free software and of promoting the sharing
and reuse of software generally. and reuse of software generally.
NO WARRANTY NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
@@ -455,7 +455,7 @@ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES. DAMAGES.
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries How to Apply These Terms to Your New Libraries
@@ -476,7 +476,7 @@ convey the exclusion of warranty; and each file should have at least the
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -485,7 +485,7 @@ convey the exclusion of warranty; and each file should have at least the
You should have received a copy of the GNU Lesser General Public You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail. Also add information on how to contact you by electronic and paper mail.
@@ -500,3 +500,5 @@ necessary. Here is a sample; alter the names:
Ty Coon, President of Vice Ty Coon, President of Vice
That's all there is to it! That's all there is to it!

270
COPYING.PYTHON Normal file
View 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.

View File

@@ -118,15 +118,6 @@ Please note that g_malloc will exit on allocation failure, so there
is no need to test for failure (as you would have to with malloc). is no need to test for failure (as you would have to with malloc).
Calling g_malloc with a zero size is valid and will return NULL. Calling g_malloc with a zero size is valid and will return NULL.
Prefer g_new(T, n) instead of g_malloc(sizeof(T) * n) for the following
reasons:
a. It catches multiplication overflowing size_t;
b. It returns T * instead of void *, letting compiler catch more type
errors.
Declarations like T *v = g_malloc(sizeof(*v)) are acceptable, though.
Memory allocated by qemu_memalign or qemu_blockalign must be freed with Memory allocated by qemu_memalign or qemu_blockalign must be freed with
qemu_vfree, since breaking this will cause problems on Win32. qemu_vfree, since breaking this will cause problems on Win32.

View File

@@ -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

21
LICENSE
View File

@@ -1,18 +1,13 @@
The QEMU distribution includes both the QEMU emulator and The following points clarify the QEMU license:
various firmware files. These are separate programs that are
distributed together for our users' convenience, and they have
separate licenses.
The following points clarify the license of the QEMU emulator: 1) QEMU as a whole is released under the GNU General Public License,
version 2.
1) The QEMU emulator as a whole is released under the GNU General 2) Parts of QEMU have specific licenses which are compatible with the
Public License, version 2. GNU General Public License, version 2. Hence each source file contains
its own licensing information. Source files with no licensing information
2) Parts of the QEMU emulator have specific licenses which are compatible are released under the GNU General Public License, version 2 or (at your
with the GNU General Public License, version 2. Hence each source file option) any later version.
contains its own licensing information. Source files with no licensing
information are released under the GNU General Public License, version
2 or (at your option) any later version.
As of July 2013, contributions under version 2 of the GNU General Public As of July 2013, contributions under version 2 of the GNU General Public
License (and no later version) are only accepted for the following files License (and no later version) are only accepted for the following files

File diff suppressed because it is too large Load Diff

656
Makefile

File diff suppressed because it is too large Load Diff

View File

@@ -1,20 +1,69 @@
####################################################################### #######################################################################
# Common libraries for tools and emulators # Common libraries for tools and emulators
stub-obj-y = stubs/ util/ crypto/ stub-obj-y = stubs/ crypto/
util-obj-y = util/ qobject/ qapi/ util-obj-y = util/ qobject/ qapi/
util-obj-y += qapi/qapi-builtin-types.o
util-obj-y += qapi/qapi-types.o
util-obj-y += qapi/qapi-types-block-core.o
util-obj-y += qapi/qapi-types-block.o
util-obj-y += qapi/qapi-types-char.o
util-obj-y += qapi/qapi-types-common.o
util-obj-y += qapi/qapi-types-crypto.o
util-obj-y += qapi/qapi-types-introspect.o
util-obj-y += qapi/qapi-types-migration.o
util-obj-y += qapi/qapi-types-misc.o
util-obj-y += qapi/qapi-types-net.o
util-obj-y += qapi/qapi-types-rocker.o
util-obj-y += qapi/qapi-types-run-state.o
util-obj-y += qapi/qapi-types-sockets.o
util-obj-y += qapi/qapi-types-tpm.o
util-obj-y += qapi/qapi-types-trace.o
util-obj-y += qapi/qapi-types-transaction.o
util-obj-y += qapi/qapi-types-ui.o
util-obj-y += qapi/qapi-builtin-visit.o
util-obj-y += qapi/qapi-visit.o
util-obj-y += qapi/qapi-visit-block-core.o
util-obj-y += qapi/qapi-visit-block.o
util-obj-y += qapi/qapi-visit-char.o
util-obj-y += qapi/qapi-visit-common.o
util-obj-y += qapi/qapi-visit-crypto.o
util-obj-y += qapi/qapi-visit-introspect.o
util-obj-y += qapi/qapi-visit-migration.o
util-obj-y += qapi/qapi-visit-misc.o
util-obj-y += qapi/qapi-visit-net.o
util-obj-y += qapi/qapi-visit-rocker.o
util-obj-y += qapi/qapi-visit-run-state.o
util-obj-y += qapi/qapi-visit-sockets.o
util-obj-y += qapi/qapi-visit-tpm.o
util-obj-y += qapi/qapi-visit-trace.o
util-obj-y += qapi/qapi-visit-transaction.o
util-obj-y += qapi/qapi-visit-ui.o
util-obj-y += qapi/qapi-events.o
util-obj-y += qapi/qapi-events-block-core.o
util-obj-y += qapi/qapi-events-block.o
util-obj-y += qapi/qapi-events-char.o
util-obj-y += qapi/qapi-events-common.o
util-obj-y += qapi/qapi-events-crypto.o
util-obj-y += qapi/qapi-events-introspect.o
util-obj-y += qapi/qapi-events-migration.o
util-obj-y += qapi/qapi-events-misc.o
util-obj-y += qapi/qapi-events-net.o
util-obj-y += qapi/qapi-events-rocker.o
util-obj-y += qapi/qapi-events-run-state.o
util-obj-y += qapi/qapi-events-sockets.o
util-obj-y += qapi/qapi-events-tpm.o
util-obj-y += qapi/qapi-events-trace.o
util-obj-y += qapi/qapi-events-transaction.o
util-obj-y += qapi/qapi-events-ui.o
util-obj-y += qapi/qapi-introspect.o
chardev-obj-y = chardev/ chardev-obj-y = chardev/
#######################################################################
# authz-obj-y is code used by both qemu system emulation and qemu-img
authz-obj-y = authz/
####################################################################### #######################################################################
# block-obj-y is code used by both qemu system emulation and qemu-img # block-obj-y is code used by both qemu system emulation and qemu-img
block-obj-y = nbd/ block-obj-y += nbd/
block-obj-y += block.o blockjob.o job.o block-obj-y += block.o blockjob.o
block-obj-y += block/ scsi/ block-obj-y += block/ scsi/
block-obj-y += qemu-io-cmds.o block-obj-y += qemu-io-cmds.o
block-obj-$(CONFIG_REPLICATION) += replication.o block-obj-$(CONFIG_REPLICATION) += replication.o
@@ -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 is code used by both qemu system emulation and qemu-img
crypto-obj-y = crypto/ 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 # 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) ifeq ($(CONFIG_SOFTMMU),y)
common-obj-y = blockdev.o blockdev-nbd.o block/ common-obj-y = blockdev.o blockdev-nbd.o block/
common-obj-y += bootdevice.o iothread.o common-obj-y += bootdevice.o iothread.o
common-obj-y += dump/
common-obj-y += job-qmp.o
common-obj-y += monitor/
common-obj-y += net/ common-obj-y += net/
common-obj-y += qdev-monitor.o device-hotplug.o common-obj-y += qdev-monitor.o device-hotplug.o
common-obj-$(CONFIG_WIN32) += os-win32.o common-obj-$(CONFIG_WIN32) += os-win32.o
@@ -73,6 +119,8 @@ common-obj-y += vl.o
vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS) vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS)
common-obj-$(CONFIG_TPM) += tpm.o common-obj-$(CONFIG_TPM) += tpm.o
common-obj-$(CONFIG_SLIRP) += slirp/
common-obj-y += backends/ common-obj-y += backends/
common-obj-y += chardev/ common-obj-y += chardev/
@@ -85,7 +133,25 @@ common-obj-$(CONFIG_FDT) += device_tree.o
###################################################################### ######################################################################
# qapi # 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 endif
####################################################################### #######################################################################
@@ -102,6 +168,7 @@ version-obj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.o
###################################################################### ######################################################################
# tracing # tracing
util-obj-y += trace/ util-obj-y += trace/
target-obj-y += trace/
###################################################################### ######################################################################
# guest agent # guest agent
@@ -114,7 +181,6 @@ qga-vss-dll-obj-y = qga/
###################################################################### ######################################################################
# contrib # contrib
elf2dmp-obj-y = contrib/elf2dmp/
ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/ ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/
ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/ ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/
libvhost-user-obj-y = contrib/libvhost-user/ libvhost-user-obj-y = contrib/libvhost-user/
@@ -122,87 +188,69 @@ vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS)
vhost-user-scsi.o-libs := $(LIBISCSI_LIBS) vhost-user-scsi.o-libs := $(LIBISCSI_LIBS)
vhost-user-scsi-obj-y = contrib/vhost-user-scsi/ vhost-user-scsi-obj-y = contrib/vhost-user-scsi/
vhost-user-blk-obj-y = contrib/vhost-user-blk/ vhost-user-blk-obj-y = contrib/vhost-user-blk/
rdmacm-mux-obj-y = contrib/rdmacm-mux/
vhost-user-input-obj-y = contrib/vhost-user-input/
vhost-user-gpu-obj-y = contrib/vhost-user-gpu/
###################################################################### ######################################################################
trace-events-subdirs = trace-events-subdirs =
trace-events-subdirs += accel/kvm trace-events-subdirs += util
trace-events-subdirs += accel/tcg
trace-events-subdirs += crypto trace-events-subdirs += 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 += io
trace-events-subdirs += nbd trace-events-subdirs += migration
trace-events-subdirs += scsi trace-events-subdirs += block
endif
ifeq ($(CONFIG_SOFTMMU),y)
trace-events-subdirs += chardev 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
trace-events-subdirs += hw/block/dataplane trace-events-subdirs += hw/block/dataplane
trace-events-subdirs += hw/char trace-events-subdirs += hw/char
trace-events-subdirs += hw/dma
trace-events-subdirs += hw/hppa
trace-events-subdirs += hw/i2c
trace-events-subdirs += hw/i386
trace-events-subdirs += hw/i386/xen
trace-events-subdirs += hw/ide
trace-events-subdirs += hw/input
trace-events-subdirs += hw/intc trace-events-subdirs += hw/intc
trace-events-subdirs += hw/isa
trace-events-subdirs += hw/mem
trace-events-subdirs += hw/mips
trace-events-subdirs += hw/misc
trace-events-subdirs += hw/misc/macio
trace-events-subdirs += hw/net trace-events-subdirs += hw/net
trace-events-subdirs += hw/nvram
trace-events-subdirs += hw/pci
trace-events-subdirs += hw/pci-host
trace-events-subdirs += hw/ppc
trace-events-subdirs += hw/rdma trace-events-subdirs += hw/rdma
trace-events-subdirs += hw/rdma/vmw trace-events-subdirs += hw/rdma/vmw
trace-events-subdirs += hw/s390x trace-events-subdirs += hw/virtio
trace-events-subdirs += hw/audio
trace-events-subdirs += hw/misc
trace-events-subdirs += hw/misc/macio
trace-events-subdirs += hw/usb
trace-events-subdirs += hw/scsi trace-events-subdirs += hw/scsi
trace-events-subdirs += hw/sd trace-events-subdirs += hw/nvram
trace-events-subdirs += hw/display
trace-events-subdirs += hw/input
trace-events-subdirs += hw/timer
trace-events-subdirs += hw/dma
trace-events-subdirs += hw/sparc trace-events-subdirs += hw/sparc
trace-events-subdirs += hw/sparc64 trace-events-subdirs += hw/sparc64
trace-events-subdirs += hw/timer trace-events-subdirs += hw/sd
trace-events-subdirs += hw/tpm trace-events-subdirs += hw/isa
trace-events-subdirs += hw/usb trace-events-subdirs += hw/mem
trace-events-subdirs += hw/i386
trace-events-subdirs += hw/i386/xen
trace-events-subdirs += hw/9pfs
trace-events-subdirs += hw/ppc
trace-events-subdirs += hw/pci
trace-events-subdirs += hw/pci-host
trace-events-subdirs += hw/s390x
trace-events-subdirs += hw/vfio trace-events-subdirs += hw/vfio
trace-events-subdirs += hw/virtio trace-events-subdirs += hw/acpi
trace-events-subdirs += hw/watchdog trace-events-subdirs += hw/arm
trace-events-subdirs += hw/alpha
trace-events-subdirs += hw/hppa
trace-events-subdirs += hw/xen trace-events-subdirs += hw/xen
trace-events-subdirs += hw/gpio trace-events-subdirs += hw/ide
trace-events-subdirs += hw/riscv trace-events-subdirs += hw/tpm
trace-events-subdirs += migration
trace-events-subdirs += net
trace-events-subdirs += ui trace-events-subdirs += ui
endif trace-events-subdirs += audio
trace-events-subdirs += hw/display trace-events-subdirs += net
trace-events-subdirs += qapi
trace-events-subdirs += qom
trace-events-subdirs += target/arm trace-events-subdirs += target/arm
trace-events-subdirs += target/hppa
trace-events-subdirs += target/i386 trace-events-subdirs += target/i386
trace-events-subdirs += target/mips trace-events-subdirs += target/mips
trace-events-subdirs += target/ppc
trace-events-subdirs += target/riscv
trace-events-subdirs += target/s390x
trace-events-subdirs += target/sparc trace-events-subdirs += target/sparc
trace-events-subdirs += 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) trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events)

View File

@@ -4,19 +4,16 @@ BUILD_DIR?=$(CURDIR)/..
include ../config-host.mak include ../config-host.mak
include config-target.mak include config-target.mak
include $(SRC_PATH)/rules.mak
ifdef CONFIG_SOFTMMU
include config-devices.mak include config-devices.mak
endif include $(SRC_PATH)/rules.mak
$(call set-vpath, $(SRC_PATH):$(BUILD_DIR)) $(call set-vpath, $(SRC_PATH):$(BUILD_DIR))
ifdef CONFIG_LINUX ifdef CONFIG_LINUX
QEMU_CFLAGS += -I../linux-headers QEMU_CFLAGS += -I../linux-headers
endif endif
QEMU_CFLAGS += -iquote .. -iquote $(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H QEMU_CFLAGS += -I.. -I$(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H
QEMU_CFLAGS+=-iquote $(SRC_PATH)/include QEMU_CFLAGS+=-I$(SRC_PATH)/include
ifdef CONFIG_USER_ONLY ifdef CONFIG_USER_ONLY
# user emulator name # user emulator name
@@ -39,17 +36,11 @@ endif
PROGS=$(QEMU_PROG) $(QEMU_PROGW) PROGS=$(QEMU_PROG) $(QEMU_PROGW)
STPFILES= STPFILES=
# Makefile Tests
include $(SRC_PATH)/tests/tcg/Makefile.include
config-target.h: config-target.h-timestamp config-target.h: config-target.h-timestamp
config-target.h-timestamp: config-target.mak config-target.h-timestamp: config-target.mak
config-devices.h: config-devices.h-timestamp
config-devices.h-timestamp: config-devices.mak
ifdef CONFIG_TRACE_SYSTEMTAP ifdef CONFIG_TRACE_SYSTEMTAP
stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp $(QEMU_PROG)-log.stp stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp
ifdef CONFIG_USER_ONLY ifdef CONFIG_USER_ONLY
TARGET_TYPE=user TARGET_TYPE=user
@@ -88,14 +79,6 @@ $(QEMU_PROG)-simpletrace.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y)
--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \ --probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \
$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp") $< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp")
$(QEMU_PROG)-log.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y)
$(call quiet-command,$(TRACETOOL) \
--group=all \
--format=log-stap \
--backends=$(TRACE_BACKENDS) \
--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \
$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-log.stp")
else else
stap: stap:
endif endif
@@ -106,8 +89,6 @@ all: $(PROGS) stap
# Dummy command so that make thinks it has done something # Dummy command so that make thinks it has done something
@true @true
obj-y += trace/
######################################################### #########################################################
# cpu emulator library # cpu emulator library
obj-y += exec.o obj-y += exec.o
@@ -116,11 +97,10 @@ obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o
obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o
obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o
obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o
obj-$(CONFIG_TCG) += fpu/softfloat.o obj-y += fpu/softfloat.o
obj-y += target/$(TARGET_BASE_ARCH)/ obj-y += target/$(TARGET_BASE_ARCH)/
obj-y += disas.o obj-y += disas.o
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
LIBS := $(libs_cpu) $(LIBS)
######################################################### #########################################################
# Linux user emulator target # Linux user emulator target
@@ -152,14 +132,12 @@ endif #CONFIG_BSD_USER
######################################################### #########################################################
# System emulator target # System emulator target
ifdef CONFIG_SOFTMMU 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 += qtest.o
obj-y += dump/
obj-y += hw/ obj-y += hw/
obj-y += monitor/
obj-y += qapi/
obj-y += memory.o obj-y += memory.o
obj-y += memory_mapping.o obj-y += memory_mapping.o
obj-y += dump.o
obj-y += migration/ram.o obj-y += migration/ram.o
LIBS := $(libs_softmmu) $(LIBS) LIBS := $(libs_softmmu) $(LIBS)
@@ -170,37 +148,43 @@ else
obj-y += hw/$(TARGET_BASE_ARCH)/ obj-y += hw/$(TARGET_BASE_ARCH)/
endif endif
generated-files-y += hmp-commands.h hmp-commands-info.h GENERATED_FILES += hmp-commands.h hmp-commands-info.h
generated-files-y += config-devices.h
endif # CONFIG_SOFTMMU endif # CONFIG_SOFTMMU
# Workaround for http://gcc.gnu.org/PR55489, see configure.
%/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS)
dummy := $(call unnest-vars,,obj-y) dummy := $(call unnest-vars,,obj-y)
all-obj-y := $(obj-y) all-obj-y := $(obj-y)
target-obj-y :=
block-obj-y :=
common-obj-y :=
chardev-obj-y :=
include $(SRC_PATH)/Makefile.objs include $(SRC_PATH)/Makefile.objs
dummy := $(call unnest-vars,,target-obj-y)
target-obj-y-save := $(target-obj-y)
dummy := $(call unnest-vars,.., \ dummy := $(call unnest-vars,.., \
authz-obj-y \
block-obj-y \ block-obj-y \
block-obj-m \ block-obj-m \
chardev-obj-y \ chardev-obj-y \
crypto-obj-y \ crypto-obj-y \
crypto-user-obj-y \ crypto-aes-obj-y \
qom-obj-y \ qom-obj-y \
io-obj-y \ io-obj-y \
common-obj-y \ common-obj-y \
common-obj-m) common-obj-m)
target-obj-y := $(target-obj-y-save)
all-obj-y += $(common-obj-y) all-obj-y += $(common-obj-y)
all-obj-y += $(target-obj-y)
all-obj-y += $(qom-obj-y) all-obj-y += $(qom-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(authz-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y) all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y)
all-obj-$(CONFIG_USER_ONLY) += $(crypto-user-obj-y) all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y) all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y) all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
ifdef CONFIG_SOFTMMU
$(QEMU_PROG_BUILD): config-devices.mak $(QEMU_PROG_BUILD): config-devices.mak
endif
COMMON_LDADDS = ../libqemuutil.a COMMON_LDADDS = ../libqemuutil.a
@@ -225,7 +209,6 @@ clean: clean-target
rm -f *.a *~ $(PROGS) rm -f *.a *~ $(PROGS)
rm -f $(shell find . -name '*.[od]') rm -f $(shell find . -name '*.[od]')
rm -f hmp-commands.h gdbstub-xml.c rm -f hmp-commands.h gdbstub-xml.c
rm -f trace/generated-helpers.c trace/generated-helpers.c-timestamp
ifdef CONFIG_TRACE_SYSTEMTAP ifdef CONFIG_TRACE_SYSTEMTAP
rm -f *.stp rm -f *.stp
endif endif
@@ -238,24 +221,7 @@ ifdef CONFIG_TRACE_SYSTEMTAP
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset" $(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset"
$(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp" $(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp"
$(INSTALL_DATA) $(QEMU_PROG)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp" $(INSTALL_DATA) $(QEMU_PROG)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp"
$(INSTALL_DATA) $(QEMU_PROG)-log.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-log.stp"
endif endif
generated-files-y += config-target.h GENERATED_FILES += config-target.h
Makefile: $(generated-files-y) Makefile: $(GENERATED_FILES)
# 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

4
README
View File

@@ -54,7 +54,7 @@ Submitting patches
The QEMU source code is maintained under the GIT version control system. The QEMU source code is maintained under the GIT version control system.
git clone https://git.qemu.org/git/qemu.git git clone git://git.qemu.org/qemu.git
When submitting patches, one common approach is to use 'git When submitting patches, one common approach is to use 'git
format-patch' and/or 'git send-email' to format & send the mail to the format-patch' and/or 'git send-email' to format & send the mail to the
@@ -70,7 +70,7 @@ the QEMU website
The QEMU website is also maintained under source control. The QEMU website is also maintained under source control.
git clone https://git.qemu.org/git/qemu-web.git git clone git://git.qemu.org/qemu-web.git
https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/ https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/
A 'git-publish' utility was created to make above process less A 'git-publish' utility was created to make above process less

View File

@@ -1 +1 @@
4.0.92 2.11.50

View File

@@ -1,5 +1,4 @@
obj-$(CONFIG_SOFTMMU) += accel.o obj-$(CONFIG_SOFTMMU) += accel.o
obj-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_POSIX)) += qtest.o obj-y += kvm/
obj-$(CONFIG_KVM) += kvm/
obj-$(CONFIG_TCG) += tcg/ obj-$(CONFIG_TCG) += tcg/
obj-y += stubs/ obj-y += stubs/

View File

@@ -34,7 +34,6 @@
#include "qom/object.h" #include "qom/object.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qemu/option.h" #include "qemu/option.h"
#include "qapi/error.h"
static const TypeInfo accel_type = { static const TypeInfo accel_type = {
.name = TYPE_ACCEL, .name = TYPE_ACCEL,
@@ -65,16 +64,14 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms)
ms->accelerator = NULL; ms->accelerator = NULL;
*(acc->allowed) = false; *(acc->allowed) = false;
object_unref(OBJECT(accel)); object_unref(OBJECT(accel));
} else {
object_set_accelerator_compat_props(acc->compat_props);
} }
return ret; return ret;
} }
void configure_accelerator(MachineState *ms, const char *progname) void configure_accelerator(MachineState *ms)
{ {
const char *accel; const char *accel, *p;
char **accel_list, **tmp; char buf[10];
int ret; int ret;
bool accel_initialised = false; bool accel_initialised = false;
bool init_failed = false; bool init_failed = false;
@@ -82,31 +79,25 @@ void configure_accelerator(MachineState *ms, const char *progname)
accel = qemu_opt_get(qemu_get_machine_opts(), "accel"); accel = qemu_opt_get(qemu_get_machine_opts(), "accel");
if (accel == NULL) { if (accel == NULL) {
/* Select the default accelerator */ /* Use the default "accelerator", tcg */
int pnlen = strlen(progname); accel = "tcg";
if (pnlen >= 3 && g_str_equal(&progname[pnlen - 3], "kvm")) {
/* If the program name ends with "kvm", we prefer KVM */
accel = "kvm:tcg";
} else {
#if defined(CONFIG_TCG)
accel = "tcg";
#elif defined(CONFIG_KVM)
accel = "kvm";
#else
error_report("No accelerator selected and"
" no default accelerator available");
exit(1);
#endif
}
} }
accel_list = g_strsplit(accel, ":", 0); p = accel;
while (!accel_initialised && *p != '\0') {
for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) { if (*p == ':') {
acc = accel_find(*tmp); p++;
}
p = get_opt_name(buf, sizeof(buf), p, ':');
acc = accel_find(buf);
if (!acc) { if (!acc) {
continue; continue;
} }
if (acc->available && !acc->available()) {
printf("%s not supported for this target\n",
acc->name);
continue;
}
ret = accel_init_machine(acc, ms); ret = accel_init_machine(acc, ms);
if (ret < 0) { if (ret < 0) {
init_failed = true; init_failed = true;
@@ -116,7 +107,6 @@ void configure_accelerator(MachineState *ms, const char *progname)
accel_initialised = true; accel_initialised = true;
} }
} }
g_strfreev(accel_list);
if (!accel_initialised) { if (!accel_initialised) {
if (!init_failed) { if (!init_failed) {
@@ -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 *class = ACCEL_GET_CLASS(accel);
AccelClass *acc = ACCEL_GET_CLASS(accel); register_compat_props_array(class->global_props);
if (acc->setup_post) {
acc->setup_post(ms, accel);
}
} }
static void register_accel_types(void) static void register_accel_types(void)

View File

@@ -1,2 +1 @@
obj-y += kvm-all.o obj-$(CONFIG_KVM) += kvm-all.o
obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o

View File

@@ -18,6 +18,7 @@
#include <linux/kvm.h> #include <linux/kvm.h>
#include "qemu-common.h"
#include "qemu/atomic.h" #include "qemu/atomic.h"
#include "qemu/option.h" #include "qemu/option.h"
#include "qemu/config-file.h" #include "qemu/config-file.h"
@@ -37,8 +38,6 @@
#include "qemu/event_notifier.h" #include "qemu/event_notifier.h"
#include "trace.h" #include "trace.h"
#include "hw/irq.h" #include "hw/irq.h"
#include "sysemu/sev.h"
#include "sysemu/balloon.h"
#include "hw/boards.h" #include "hw/boards.h"
@@ -78,20 +77,17 @@ struct KVMState
int fd; int fd;
int vmfd; int vmfd;
int coalesced_mmio; int coalesced_mmio;
int coalesced_pio;
struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
bool coalesced_flush_in_progress; bool coalesced_flush_in_progress;
int vcpu_events; int vcpu_events;
int robust_singlestep; int robust_singlestep;
int debugregs; int debugregs;
#ifdef KVM_CAP_SET_GUEST_DEBUG #ifdef KVM_CAP_SET_GUEST_DEBUG
QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints; struct kvm_sw_breakpoint_head kvm_sw_breakpoints;
#endif #endif
int max_nested_state_len;
int many_ioeventfds; int many_ioeventfds;
int intx_set_mask; int intx_set_mask;
bool sync_mmu; bool sync_mmu;
bool manual_dirty_log_protect;
/* The man page (and posix) say ioctl numbers are signed int, but /* The man page (and posix) say ioctl numbers are signed int, but
* they're not. Linux, glibc and *BSD all treat ioctl numbers as * they're not. Linux, glibc and *BSD all treat ioctl numbers as
* unsigned, and treating them as signed here can break things */ * unsigned, and treating them as signed here can break things */
@@ -103,21 +99,10 @@ struct KVMState
int nr_allocated_irq_routes; int nr_allocated_irq_routes;
unsigned long *used_gsi_bitmap; unsigned long *used_gsi_bitmap;
unsigned int gsi_count; unsigned int gsi_count;
QTAILQ_HEAD(, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE]; QTAILQ_HEAD(msi_hashtab, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE];
#endif #endif
KVMMemoryListener memory_listener; KVMMemoryListener memory_listener;
QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus; QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus;
/* memory encryption */
void *memcrypt_handle;
int (*memcrypt_encrypt_data)(void *handle, uint8_t *ptr, uint64_t len);
/* For "info mtree -f" to tell if an MR is registered in KVM */
int nr_as;
struct KVMAs {
KVMMemoryListener *ml;
AddressSpace *as;
} *as;
}; };
KVMState *kvm_state; KVMState *kvm_state;
@@ -146,9 +131,6 @@ static const KVMCapabilityInfo kvm_required_capabilites[] = {
KVM_CAP_LAST_INFO 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) int kvm_get_max_memslots(void)
{ {
KVMState *s = KVM_STATE(current_machine->accelerator); KVMState *s = KVM_STATE(current_machine->accelerator);
@@ -156,27 +138,6 @@ int kvm_get_max_memslots(void)
return s->nr_slots; return s->nr_slots;
} }
bool kvm_memcrypt_enabled(void)
{
if (kvm_state && kvm_state->memcrypt_handle) {
return true;
}
return false;
}
int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len)
{
if (kvm_state->memcrypt_handle &&
kvm_state->memcrypt_encrypt_data) {
return kvm_state->memcrypt_encrypt_data(kvm_state->memcrypt_handle,
ptr, len);
}
return 1;
}
/* Called with KVMMemoryListener.slots_lock held */
static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml) static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml)
{ {
KVMState *s = kvm_state; KVMState *s = kvm_state;
@@ -194,17 +155,10 @@ static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml)
bool kvm_has_free_slot(MachineState *ms) bool kvm_has_free_slot(MachineState *ms)
{ {
KVMState *s = KVM_STATE(ms->accelerator); KVMState *s = KVM_STATE(ms->accelerator);
bool result;
KVMMemoryListener *kml = &s->memory_listener;
kvm_slots_lock(kml); return kvm_get_free_slot(&s->memory_listener);
result = !!kvm_get_free_slot(kml);
kvm_slots_unlock(kml);
return result;
} }
/* Called with KVMMemoryListener.slots_lock held */
static KVMSlot *kvm_alloc_slot(KVMMemoryListener *kml) static KVMSlot *kvm_alloc_slot(KVMMemoryListener *kml)
{ {
KVMSlot *slot = kvm_get_free_slot(kml); KVMSlot *slot = kvm_get_free_slot(kml);
@@ -263,24 +217,21 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram,
hwaddr *phys_addr) hwaddr *phys_addr)
{ {
KVMMemoryListener *kml = &s->memory_listener; KVMMemoryListener *kml = &s->memory_listener;
int i, ret = 0; int i;
kvm_slots_lock(kml);
for (i = 0; i < s->nr_slots; i++) { for (i = 0; i < s->nr_slots; i++) {
KVMSlot *mem = &kml->slots[i]; KVMSlot *mem = &kml->slots[i];
if (ram >= mem->ram && ram < mem->ram + mem->memory_size) { if (ram >= mem->ram && ram < mem->ram + mem->memory_size) {
*phys_addr = mem->start_addr + (ram - mem->ram); *phys_addr = mem->start_addr + (ram - mem->ram);
ret = 1; return 1;
break;
} }
} }
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; KVMState *s = kvm_state;
struct kvm_userspace_memory_region mem; struct kvm_userspace_memory_region mem;
@@ -291,7 +242,7 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo
mem.userspace_addr = (unsigned long)slot->ram; mem.userspace_addr = (unsigned long)slot->ram;
mem.flags = slot->flags; mem.flags = slot->flags;
if (slot->memory_size && !new && (mem.flags ^ slot->old_flags) & KVM_MEM_READONLY) { if (slot->memory_size && mem.flags & KVM_MEM_READONLY) {
/* Set the slot size to 0 before setting the slot to the desired /* Set the slot size to 0 before setting the slot to the desired
* value. This is needed based on KVM commit 75d61fbc. */ * value. This is needed based on KVM commit 75d61fbc. */
mem.memory_size = 0; mem.memory_size = 0;
@@ -299,7 +250,6 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo
} }
mem.memory_size = slot->memory_size; mem.memory_size = slot->memory_size;
ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem);
slot->old_flags = mem.flags;
trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr, trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr,
mem.memory_size, mem.userspace_addr, ret); mem.memory_size, mem.userspace_addr, ret);
return ret; return ret;
@@ -314,11 +264,6 @@ int kvm_destroy_vcpu(CPUState *cpu)
DPRINTF("kvm_destroy_vcpu\n"); 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); mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0);
if (mmap_size < 0) { if (mmap_size < 0) {
ret = mmap_size; ret = mmap_size;
@@ -418,18 +363,20 @@ static int kvm_mem_flags(MemoryRegion *mr)
return flags; return flags;
} }
/* Called with KVMMemoryListener.slots_lock held */
static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem, static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem,
MemoryRegion *mr) MemoryRegion *mr)
{ {
int old_flags;
old_flags = mem->flags;
mem->flags = kvm_mem_flags(mr); mem->flags = kvm_mem_flags(mr);
/* If nothing changed effectively, no need to issue ioctl */ /* If nothing changed effectively, no need to issue ioctl */
if (mem->flags == mem->old_flags) { if (mem->flags == old_flags) {
return 0; return 0;
} }
return kvm_set_user_memory_region(kml, mem, false); return kvm_set_user_memory_region(kml, mem);
} }
static int kvm_section_update_flags(KVMMemoryListener *kml, static int kvm_section_update_flags(KVMMemoryListener *kml,
@@ -437,26 +384,19 @@ static int kvm_section_update_flags(KVMMemoryListener *kml,
{ {
hwaddr start_addr, size; hwaddr start_addr, size;
KVMSlot *mem; KVMSlot *mem;
int ret = 0;
size = kvm_align_section(section, &start_addr); size = kvm_align_section(section, &start_addr);
if (!size) { if (!size) {
return 0; return 0;
} }
kvm_slots_lock(kml);
mem = kvm_lookup_matching_slot(kml, start_addr, size); mem = kvm_lookup_matching_slot(kml, start_addr, size);
if (!mem) { if (!mem) {
/* We don't have a slot if we want to trap every access. */ /* 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); return kvm_slot_update_flags(kml, mem, section->mr);
out:
kvm_slots_unlock(kml);
return ret;
} }
static void kvm_log_start(MemoryListener *listener, static void kvm_log_start(MemoryListener *listener,
@@ -508,15 +448,13 @@ static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section,
#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1)) #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, * @start_add: start of logged region.
* and then updates qemu's dirty bitmap. * @end_addr: end of logged region.
*
* 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
*/ */
static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml, static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
MemoryRegionSection *section) MemoryRegionSection *section)
@@ -525,14 +463,13 @@ static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
struct kvm_dirty_log d = {}; struct kvm_dirty_log d = {};
KVMSlot *mem; KVMSlot *mem;
hwaddr start_addr, size; hwaddr start_addr, size;
int ret = 0;
size = kvm_align_section(section, &start_addr); size = kvm_align_section(section, &start_addr);
if (size) { if (size) {
mem = kvm_lookup_matching_slot(kml, start_addr, size); mem = kvm_lookup_matching_slot(kml, start_addr, size);
if (!mem) { if (!mem) {
/* We don't have a slot if we want to trap every access. */ /* We don't have a slot if we want to trap every access. */
goto out; return 0;
} }
/* XXX bad kernel interface alert /* XXX bad kernel interface alert
@@ -549,176 +486,20 @@ static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
*/ */
size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS), size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS),
/*HOST_LONG_BITS*/ 64) / 8; /*HOST_LONG_BITS*/ 64) / 8;
if (!mem->dirty_bmap) { d.dirty_bitmap = g_malloc0(size);
/* Allocate on the first log_sync, once and for all */
mem->dirty_bmap = g_malloc0(size);
}
d.dirty_bitmap = mem->dirty_bmap;
d.slot = mem->slot | (kml->as_id << 16); d.slot = mem->slot | (kml->as_id << 16);
if (kvm_vm_ioctl(s, KVM_GET_DIRTY_LOG, &d) == -1) { if (kvm_vm_ioctl(s, KVM_GET_DIRTY_LOG, &d) == -1) {
DPRINTF("ioctl failed %d\n", errno); DPRINTF("ioctl failed %d\n", errno);
ret = -1; g_free(d.dirty_bitmap);
goto out; return -1;
} }
kvm_get_dirty_pages_log_range(section, d.dirty_bitmap); kvm_get_dirty_pages_log_range(section, d.dirty_bitmap);
} g_free(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;
} }
start = section->offset_within_address_space; return 0;
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;
} }
static void kvm_coalesce_mmio_region(MemoryListener *listener, static void kvm_coalesce_mmio_region(MemoryListener *listener,
@@ -755,45 +536,6 @@ static void kvm_uncoalesce_mmio_region(MemoryListener *listener,
} }
} }
static void kvm_coalesce_pio_add(MemoryListener *listener,
MemoryRegionSection *section,
hwaddr start, hwaddr size)
{
KVMState *s = kvm_state;
if (s->coalesced_pio) {
struct kvm_coalesced_mmio_zone zone;
zone.addr = start;
zone.size = size;
zone.pio = 1;
(void)kvm_vm_ioctl(s, KVM_REGISTER_COALESCED_MMIO, &zone);
}
}
static void kvm_coalesce_pio_del(MemoryListener *listener,
MemoryRegionSection *section,
hwaddr start, hwaddr size)
{
KVMState *s = kvm_state;
if (s->coalesced_pio) {
struct kvm_coalesced_mmio_zone zone;
zone.addr = start;
zone.size = size;
zone.pio = 1;
(void)kvm_vm_ioctl(s, KVM_UNREGISTER_COALESCED_MMIO, &zone);
}
}
static MemoryListener kvm_coalesced_pio_listener = {
.coalesced_io_add = kvm_coalesce_pio_add,
.coalesced_io_del = kvm_coalesce_pio_del,
};
int kvm_check_extension(KVMState *s, unsigned int extension) int kvm_check_extension(KVMState *s, unsigned int extension)
{ {
int ret; int ret;
@@ -851,8 +593,6 @@ static int kvm_set_ioeventfd_mmio(int fd, hwaddr addr, uint32_t val,
.fd = fd, .fd = fd,
}; };
trace_kvm_set_ioeventfd_mmio(fd, (uint64_t)addr, val, assign, size,
datamatch);
if (!kvm_enabled()) { if (!kvm_enabled()) {
return -ENOSYS; return -ENOSYS;
} }
@@ -884,7 +624,6 @@ static int kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint16_t val,
.fd = fd, .fd = fd,
}; };
int r; int r;
trace_kvm_set_ioeventfd_pio(fd, addr, val, assign, size, datamatch);
if (!kvm_enabled()) { if (!kvm_enabled()) {
return -ENOSYS; return -ENOSYS;
} }
@@ -980,29 +719,24 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
ram = memory_region_get_ram_ptr(mr) + section->offset_within_region + ram = memory_region_get_ram_ptr(mr) + section->offset_within_region +
(start_addr - section->offset_within_address_space); (start_addr - section->offset_within_address_space);
kvm_slots_lock(kml);
if (!add) { if (!add) {
mem = kvm_lookup_matching_slot(kml, start_addr, size); mem = kvm_lookup_matching_slot(kml, start_addr, size);
if (!mem) { if (!mem) {
goto out; return;
} }
if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) { if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
kvm_physical_sync_dirty_bitmap(kml, section); kvm_physical_sync_dirty_bitmap(kml, section);
} }
/* unregister the slot */ /* unregister the slot */
g_free(mem->dirty_bmap);
mem->dirty_bmap = NULL;
mem->memory_size = 0; mem->memory_size = 0;
mem->flags = 0; err = kvm_set_user_memory_region(kml, mem);
err = kvm_set_user_memory_region(kml, mem, false);
if (err) { if (err) {
fprintf(stderr, "%s: error unregistering slot: %s\n", fprintf(stderr, "%s: error unregistering slot: %s\n",
__func__, strerror(-err)); __func__, strerror(-err));
abort(); abort();
} }
goto out; return;
} }
/* register the new slot */ /* register the new slot */
@@ -1012,15 +746,12 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
mem->ram = ram; mem->ram = ram;
mem->flags = kvm_mem_flags(mr); mem->flags = kvm_mem_flags(mr);
err = kvm_set_user_memory_region(kml, mem, true); err = kvm_set_user_memory_region(kml, mem);
if (err) { if (err) {
fprintf(stderr, "%s: error registering slot: %s\n", __func__, fprintf(stderr, "%s: error registering slot: %s\n", __func__,
strerror(-err)); strerror(-err));
abort(); abort();
} }
out:
kvm_slots_unlock(kml);
} }
static void kvm_region_add(MemoryListener *listener, static void kvm_region_add(MemoryListener *listener,
@@ -1047,30 +778,12 @@ static void kvm_log_sync(MemoryListener *listener,
KVMMemoryListener *kml = container_of(listener, KVMMemoryListener, listener); KVMMemoryListener *kml = container_of(listener, KVMMemoryListener, listener);
int r; int r;
kvm_slots_lock(kml);
r = kvm_physical_sync_dirty_bitmap(kml, section); r = kvm_physical_sync_dirty_bitmap(kml, section);
kvm_slots_unlock(kml);
if (r < 0) { if (r < 0) {
abort(); 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, static void kvm_mem_ioeventfd_add(MemoryListener *listener,
MemoryRegionSection *section, MemoryRegionSection *section,
bool match_data, uint64_t data, bool match_data, uint64_t data,
@@ -1083,8 +796,8 @@ static void kvm_mem_ioeventfd_add(MemoryListener *listener,
data, true, int128_get64(section->size), data, true, int128_get64(section->size),
match_data); match_data);
if (r < 0) { if (r < 0) {
fprintf(stderr, "%s: error adding ioeventfd: %s (%d)\n", fprintf(stderr, "%s: error adding ioeventfd: %s\n",
__func__, strerror(-r), -r); __func__, strerror(-r));
abort(); abort();
} }
} }
@@ -1101,8 +814,6 @@ static void kvm_mem_ioeventfd_del(MemoryListener *listener,
data, false, int128_get64(section->size), data, false, int128_get64(section->size),
match_data); match_data);
if (r < 0) { if (r < 0) {
fprintf(stderr, "%s: error deleting ioeventfd: %s (%d)\n",
__func__, strerror(-r), -r);
abort(); abort();
} }
} }
@@ -1119,8 +830,8 @@ static void kvm_io_ioeventfd_add(MemoryListener *listener,
data, true, int128_get64(section->size), data, true, int128_get64(section->size),
match_data); match_data);
if (r < 0) { if (r < 0) {
fprintf(stderr, "%s: error adding ioeventfd: %s (%d)\n", fprintf(stderr, "%s: error adding ioeventfd: %s\n",
__func__, strerror(-r), -r); __func__, strerror(-r));
abort(); abort();
} }
} }
@@ -1138,8 +849,6 @@ static void kvm_io_ioeventfd_del(MemoryListener *listener,
data, false, int128_get64(section->size), data, false, int128_get64(section->size),
match_data); match_data);
if (r < 0) { if (r < 0) {
fprintf(stderr, "%s: error deleting ioeventfd: %s (%d)\n",
__func__, strerror(-r), -r);
abort(); abort();
} }
} }
@@ -1149,7 +858,6 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml,
{ {
int i; int i;
qemu_mutex_init(&kml->slots_lock);
kml->slots = g_malloc0(s->nr_slots * sizeof(KVMSlot)); kml->slots = g_malloc0(s->nr_slots * sizeof(KVMSlot));
kml->as_id = as_id; kml->as_id = as_id;
@@ -1162,18 +870,9 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml,
kml->listener.log_start = kvm_log_start; kml->listener.log_start = kvm_log_start;
kml->listener.log_stop = kvm_log_stop; kml->listener.log_stop = kvm_log_stop;
kml->listener.log_sync = kvm_log_sync; kml->listener.log_sync = kvm_log_sync;
kml->listener.log_clear = kvm_log_clear;
kml->listener.priority = 10; kml->listener.priority = 10;
memory_listener_register(&kml->listener, as); memory_listener_register(&kml->listener, as);
for (i = 0; i < s->nr_as; ++i) {
if (!s->as[i].as) {
s->as[i].as = as;
s->as[i].ml = kml;
break;
}
}
} }
static MemoryListener kvm_io_listener = { static MemoryListener kvm_io_listener = {
@@ -1766,8 +1465,8 @@ static int kvm_init(MachineState *ms)
const char *name; const char *name;
int num; int num;
} num_cpus[] = { } num_cpus[] = {
{ "SMP", ms->smp.cpus }, { "SMP", smp_cpus },
{ "hotpluggable", ms->smp.max_cpus }, { "hotpluggable", max_cpus },
{ NULL, } { NULL, }
}, *nc = num_cpus; }, *nc = num_cpus;
int soft_vcpus_limit, hard_vcpus_limit; int soft_vcpus_limit, hard_vcpus_limit;
@@ -1824,15 +1523,9 @@ static int kvm_init(MachineState *ms)
s->nr_slots = 32; s->nr_slots = 32;
} }
s->nr_as = kvm_check_extension(s, KVM_CAP_MULTI_ADDRESS_SPACE);
if (s->nr_as <= 1) {
s->nr_as = 1;
}
s->as = g_new0(struct KVMAs, s->nr_as);
kvm_type = qemu_opt_get(qemu_get_machine_opts(), "kvm-type"); kvm_type = qemu_opt_get(qemu_get_machine_opts(), "kvm-type");
if (mc->kvm_type) { if (mc->kvm_type) {
type = mc->kvm_type(ms, kvm_type); type = mc->kvm_type(kvm_type);
} else if (kvm_type) { } else if (kvm_type) {
ret = -EINVAL; ret = -EINVAL;
fprintf(stderr, "Invalid argument kvm-type=%s\n", kvm_type); fprintf(stderr, "Invalid argument kvm-type=%s\n", kvm_type);
@@ -1898,19 +1591,6 @@ static int kvm_init(MachineState *ms)
} }
s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO); s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO);
s->coalesced_pio = s->coalesced_mmio &&
kvm_check_extension(s, KVM_CAP_COALESCED_PIO);
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 #ifdef KVM_CAP_VCPU_EVENTS
s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS); s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS);
@@ -1923,8 +1603,6 @@ static int kvm_init(MachineState *ms)
s->debugregs = kvm_check_extension(s, KVM_CAP_DEBUGREGS); s->debugregs = kvm_check_extension(s, KVM_CAP_DEBUGREGS);
#endif #endif
s->max_nested_state_len = kvm_check_extension(s, KVM_CAP_NESTED_STATE);
#ifdef KVM_CAP_IRQ_ROUTING #ifdef KVM_CAP_IRQ_ROUTING
kvm_direct_msi_allowed = (kvm_check_extension(s, KVM_CAP_SIGNAL_MSI) > 0); kvm_direct_msi_allowed = (kvm_check_extension(s, KVM_CAP_SIGNAL_MSI) > 0);
#endif #endif
@@ -1936,8 +1614,10 @@ static int kvm_init(MachineState *ms)
s->irq_set_ioctl = KVM_IRQ_LINE_STATUS; s->irq_set_ioctl = KVM_IRQ_LINE_STATUS;
} }
#ifdef KVM_CAP_READONLY_MEM
kvm_readonly_mem_allowed = kvm_readonly_mem_allowed =
(kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0); (kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0);
#endif
kvm_eventfds_allowed = kvm_eventfds_allowed =
(kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0); (kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0);
@@ -1956,20 +1636,6 @@ static int kvm_init(MachineState *ms)
kvm_state = s; kvm_state = s;
/*
* if memory encryption object is specified then initialize the memory
* encryption context.
*/
if (ms->memory_encryption) {
kvm_state->memcrypt_handle = sev_guest_init(ms->memory_encryption);
if (!kvm_state->memcrypt_handle) {
ret = -1;
goto err;
}
kvm_state->memcrypt_encrypt_data = sev_encrypt_data;
}
ret = kvm_arch_init(ms, s); ret = kvm_arch_init(ms, s);
if (ret < 0) { if (ret < 0) {
goto err; goto err;
@@ -1983,22 +1649,17 @@ static int kvm_init(MachineState *ms)
s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add; s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add;
s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del; s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del;
} }
s->memory_listener.listener.coalesced_io_add = kvm_coalesce_mmio_region; s->memory_listener.listener.coalesced_mmio_add = kvm_coalesce_mmio_region;
s->memory_listener.listener.coalesced_io_del = kvm_uncoalesce_mmio_region; s->memory_listener.listener.coalesced_mmio_del = kvm_uncoalesce_mmio_region;
kvm_memory_listener_register(s, &s->memory_listener, kvm_memory_listener_register(s, &s->memory_listener,
&address_space_memory, 0); &address_space_memory, 0);
memory_listener_register(&kvm_io_listener, memory_listener_register(&kvm_io_listener,
&address_space_io); &address_space_io);
memory_listener_register(&kvm_coalesced_pio_listener,
&address_space_io);
s->many_ioeventfds = kvm_check_many_ioeventfds(); s->many_ioeventfds = kvm_check_many_ioeventfds();
s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU); s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU);
if (!s->sync_mmu) {
qemu_balloon_inhibit(true);
}
return 0; return 0;
@@ -2050,7 +1711,7 @@ static int kvm_handle_internal_error(CPUState *cpu, struct kvm_run *run)
if (run->internal.suberror == KVM_INTERNAL_ERROR_EMULATION) { if (run->internal.suberror == KVM_INTERNAL_ERROR_EMULATION) {
fprintf(stderr, "emulation failure\n"); fprintf(stderr, "emulation failure\n");
if (!kvm_arch_stop_on_emulation_error(cpu)) { 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; return EXCP_INTERRUPT;
} }
} }
@@ -2077,13 +1738,7 @@ void kvm_flush_coalesced_mmio_buffer(void)
ent = &ring->coalesced_mmio[ring->first]; ent = &ring->coalesced_mmio[ring->first];
if (ent->pio == 1) { cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len);
address_space_rw(&address_space_io, ent->phys_addr,
MEMTXATTRS_UNSPECIFIED, ent->data,
ent->len, true);
} else {
cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len);
}
smp_wmb(); smp_wmb();
ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX; ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX;
} }
@@ -2341,7 +1996,7 @@ int kvm_cpu_exec(CPUState *cpu)
qemu_mutex_lock_iothread(); qemu_mutex_lock_iothread();
if (ret < 0) { 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); vm_stop(RUN_STATE_INTERNAL_ERROR);
} }
@@ -2492,11 +2147,6 @@ int kvm_has_debugregs(void)
return kvm_state->debugregs; return kvm_state->debugregs;
} }
int kvm_max_nested_state_length(void)
{
return kvm_state->max_nested_state_len;
}
int kvm_has_many_ioeventfds(void) int kvm_has_many_ioeventfds(void)
{ {
if (!kvm_enabled()) { if (!kvm_enabled()) {
@@ -2849,28 +2499,11 @@ int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target)
return r; return r;
} }
static bool kvm_accel_has_memory(MachineState *ms, AddressSpace *as,
hwaddr start_addr, hwaddr size)
{
KVMState *kvm = KVM_STATE(ms->accelerator);
int i;
for (i = 0; i < kvm->nr_as; ++i) {
if (kvm->as[i].as == as && kvm->as[i].ml) {
return NULL != kvm_lookup_matching_slot(kvm->as[i].ml,
start_addr, size);
}
}
return false;
}
static void kvm_accel_class_init(ObjectClass *oc, void *data) static void kvm_accel_class_init(ObjectClass *oc, void *data)
{ {
AccelClass *ac = ACCEL_CLASS(oc); AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "KVM"; ac->name = "KVM";
ac->init_machine = kvm_init; ac->init_machine = kvm_init;
ac->has_memory = kvm_accel_has_memory;
ac->allowed = &kvm_allowed; ac->allowed = &kvm_allowed;
} }

View File

@@ -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;
}

View File

@@ -1,4 +1,4 @@
# See docs/devel/tracing.txt for syntax documentation. # Trace events for debugging and performance instrumentation
# kvm-all.c # kvm-all.c
kvm_ioctl(int type, void *arg) "type 0x%x, arg %p" kvm_ioctl(int type, void *arg) "type 0x%x, arg %p"
@@ -12,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_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d"
kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d" kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d"
kvm_irqchip_release_virq(int virq) "virq %d" kvm_irqchip_release_virq(int virq) "virq %d"
kvm_set_ioeventfd_mmio(int fd, uint64_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%" PRIx64 " val=0x%x assign: %d size: %d match: %d"
kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%x val=0x%x assign: %d size: %d match: %d"
kvm_set_user_memory(uint32_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d" kvm_set_user_memory(uint32_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d"
kvm_clear_dirty_log(uint32_t slot, uint64_t start, uint32_t size) "slot#%"PRId32" start 0x%"PRIx64" size 0x%"PRIx32

View File

@@ -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);

View File

@@ -14,6 +14,7 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h" #include "cpu.h"
#include "sysemu/hax.h" #include "sysemu/hax.h"

View File

@@ -12,6 +12,7 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h" #include "cpu.h"
#include "sysemu/hvf.h" #include "sysemu/hvf.h"

View File

@@ -11,6 +11,7 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h" #include "cpu.h"
#include "sysemu/kvm.h" #include "sysemu/kvm.h"
@@ -104,16 +105,6 @@ int kvm_on_sigbus(int code, void *addr)
return 1; return 1;
} }
bool kvm_memcrypt_enabled(void)
{
return false;
}
int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len)
{
return 1;
}
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev) int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev)
{ {

View File

@@ -21,6 +21,10 @@ void tb_flush(CPUState *cpu)
{ {
} }
void tb_unlock(void)
{
}
void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) void tlb_set_dirty(CPUState *cpu, target_ulong vaddr)
{ {
} }

View File

@@ -9,6 +9,7 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h" #include "cpu.h"
#include "sysemu/whpx.h" #include "sysemu/whpx.h"

View File

@@ -7,7 +7,7 @@
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either * License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version. * version 2 of the License, or (at your option) any later version.
* *
* This library is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -18,37 +18,26 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/ */
#include "trace/mem.h"
#if DATA_SIZE == 16 #if DATA_SIZE == 16
# define SUFFIX o # define SUFFIX o
# define DATA_TYPE Int128 # define DATA_TYPE Int128
# define BSWAP bswap128 # define BSWAP bswap128
# define SHIFT 4
#elif DATA_SIZE == 8 #elif DATA_SIZE == 8
# define SUFFIX q # define SUFFIX q
# define DATA_TYPE uint64_t # define DATA_TYPE uint64_t
# define SDATA_TYPE int64_t
# define BSWAP bswap64 # define BSWAP bswap64
# define SHIFT 3
#elif DATA_SIZE == 4 #elif DATA_SIZE == 4
# define SUFFIX l # define SUFFIX l
# define DATA_TYPE uint32_t # define DATA_TYPE uint32_t
# define SDATA_TYPE int32_t
# define BSWAP bswap32 # define BSWAP bswap32
# define SHIFT 2
#elif DATA_SIZE == 2 #elif DATA_SIZE == 2
# define SUFFIX w # define SUFFIX w
# define DATA_TYPE uint16_t # define DATA_TYPE uint16_t
# define SDATA_TYPE int16_t
# define BSWAP bswap16 # define BSWAP bswap16
# define SHIFT 1
#elif DATA_SIZE == 1 #elif DATA_SIZE == 1
# define SUFFIX b # define SUFFIX b
# define DATA_TYPE uint8_t # define DATA_TYPE uint8_t
# define SDATA_TYPE int8_t
# define BSWAP # define BSWAP
# define SHIFT 0
#else #else
# error unsupported data size # error unsupported data size
#endif #endif
@@ -59,37 +48,14 @@
# define ABI_TYPE uint32_t # define ABI_TYPE uint32_t
#endif #endif
#define ATOMIC_TRACE_RMW do { \
uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
\
trace_guest_mem_before_exec(env_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 /* Define host-endian atomic operations. Note that END is used within
the ATOMIC_NAME macro, and redefined below. */ the ATOMIC_NAME macro, and redefined below. */
#if DATA_SIZE == 1 #if DATA_SIZE == 1
# define END # define END
# define MEND _be /* either le or be would be fine */
#elif defined(HOST_WORDS_BIGENDIAN) #elif defined(HOST_WORDS_BIGENDIAN)
# define END _be # define END _be
# define MEND _be
#else #else
# define END _le # define END _le
# define MEND _le
#endif #endif
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
@@ -97,27 +63,17 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
{ {
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ret; DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv);
ATOMIC_TRACE_RMW;
#if DATA_SIZE == 16
ret = atomic16_cmpxchg(haddr, cmpv, newv);
#else
ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv);
#endif
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
return ret; return ret;
} }
#if DATA_SIZE >= 16 #if DATA_SIZE >= 16
#if HAVE_ATOMIC128
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
{ {
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
__atomic_load(haddr, &val, __ATOMIC_RELAXED);
ATOMIC_TRACE_LD;
val = atomic16_read(haddr);
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
return val; return val;
} }
@@ -127,22 +83,16 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
{ {
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
__atomic_store(haddr, &val, __ATOMIC_RELAXED);
ATOMIC_TRACE_ST;
atomic16_set(haddr, val);
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
} }
#endif
#else #else
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
ABI_TYPE val EXTRA_ARGS) ABI_TYPE val EXTRA_ARGS)
{ {
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ret; DATA_TYPE ret = atomic_xchg__nocheck(haddr, val);
ATOMIC_TRACE_RMW;
ret = atomic_xchg__nocheck(haddr, val);
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
return ret; return ret;
} }
@@ -153,10 +103,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
{ \ { \
ATOMIC_MMU_DECLS; \ ATOMIC_MMU_DECLS; \
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
DATA_TYPE ret; \ DATA_TYPE ret = atomic_##X(haddr, val); \
\
ATOMIC_TRACE_RMW; \
ret = atomic_##X(haddr, val); \
ATOMIC_MMU_CLEANUP; \ ATOMIC_MMU_CLEANUP; \
return ret; \ return ret; \
} }
@@ -171,48 +118,9 @@ GEN_ATOMIC_HELPER(or_fetch)
GEN_ATOMIC_HELPER(xor_fetch) GEN_ATOMIC_HELPER(xor_fetch)
#undef GEN_ATOMIC_HELPER #undef GEN_ATOMIC_HELPER
/* These helpers are, as a whole, full barriers. Within the helper,
* the leading barrier is explicit and the trailing barrier is within
* cmpxchg primitive.
*
* Trace this load + RMW loop as a single RMW op. This way, regardless
* of CF_PARALLEL's value, we'll trace just a read and a write.
*/
#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ABI_TYPE xval EXTRA_ARGS) \
{ \
ATOMIC_MMU_DECLS; \
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
XDATA_TYPE cmp, old, new, val = xval; \
\
ATOMIC_TRACE_RMW; \
smp_mb(); \
cmp = atomic_read__nocheck(haddr); \
do { \
old = cmp; new = FN(old, val); \
cmp = atomic_cmpxchg__nocheck(haddr, old, new); \
} while (cmp != old); \
ATOMIC_MMU_CLEANUP; \
return RET; \
}
GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(fetch_umin, MIN, DATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(fetch_umax, MAX, DATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(umin_fetch, MIN, DATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
#undef GEN_ATOMIC_HELPER_FN
#endif /* DATA SIZE >= 16 */ #endif /* DATA SIZE >= 16 */
#undef END #undef END
#undef MEND
#if DATA_SIZE > 1 #if DATA_SIZE > 1
@@ -220,10 +128,8 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
within the ATOMIC_NAME macro. */ within the ATOMIC_NAME macro. */
#ifdef HOST_WORDS_BIGENDIAN #ifdef HOST_WORDS_BIGENDIAN
# define END _le # define END _le
# define MEND _le
#else #else
# define END _be # define END _be
# define MEND _be
#endif #endif
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
@@ -231,27 +137,17 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
{ {
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ret; DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
ATOMIC_TRACE_RMW;
#if DATA_SIZE == 16
ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv));
#else
ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
#endif
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
return BSWAP(ret); return BSWAP(ret);
} }
#if DATA_SIZE >= 16 #if DATA_SIZE >= 16
#if HAVE_ATOMIC128
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
{ {
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
__atomic_load(haddr, &val, __ATOMIC_RELAXED);
ATOMIC_TRACE_LD;
val = atomic16_read(haddr);
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
return BSWAP(val); return BSWAP(val);
} }
@@ -261,23 +157,17 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
{ {
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
ATOMIC_TRACE_ST;
val = BSWAP(val); val = BSWAP(val);
atomic16_set(haddr, val); __atomic_store(haddr, &val, __ATOMIC_RELAXED);
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
} }
#endif
#else #else
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
ABI_TYPE val EXTRA_ARGS) ABI_TYPE val EXTRA_ARGS)
{ {
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
ABI_TYPE ret; ABI_TYPE ret = atomic_xchg__nocheck(haddr, BSWAP(val));
ATOMIC_TRACE_RMW;
ret = atomic_xchg__nocheck(haddr, BSWAP(val));
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
return BSWAP(ret); return BSWAP(ret);
} }
@@ -288,10 +178,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
{ \ { \
ATOMIC_MMU_DECLS; \ ATOMIC_MMU_DECLS; \
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
DATA_TYPE ret; \ DATA_TYPE ret = atomic_##X(haddr, BSWAP(val)); \
\
ATOMIC_TRACE_RMW; \
ret = atomic_##X(haddr, BSWAP(val)); \
ATOMIC_MMU_CLEANUP; \ ATOMIC_MMU_CLEANUP; \
return BSWAP(ret); \ return BSWAP(ret); \
} }
@@ -305,64 +192,54 @@ GEN_ATOMIC_HELPER(xor_fetch)
#undef GEN_ATOMIC_HELPER #undef GEN_ATOMIC_HELPER
/* These helpers are, as a whole, full barriers. Within the helper,
* the leading barrier is explicit and the trailing barrier is within
* cmpxchg primitive.
*
* Trace this load + RMW loop as a single RMW op. This way, regardless
* of CF_PARALLEL's value, we'll trace just a read and a write.
*/
#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ABI_TYPE xval EXTRA_ARGS) \
{ \
ATOMIC_MMU_DECLS; \
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
XDATA_TYPE ldo, ldn, old, new, val = xval; \
\
ATOMIC_TRACE_RMW; \
smp_mb(); \
ldn = atomic_read__nocheck(haddr); \
do { \
ldo = ldn; old = BSWAP(ldo); new = FN(old, val); \
ldn = atomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new)); \
} while (ldo != ldn); \
ATOMIC_MMU_CLEANUP; \
return RET; \
}
GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(fetch_umin, MIN, DATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(fetch_umax, MAX, DATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(umin_fetch, MIN, DATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
/* Note that for addition, we need to use a separate cmpxchg loop instead /* Note that for addition, we need to use a separate cmpxchg loop instead
of bswaps for the reverse-host-endian helpers. */ of bswaps for the reverse-host-endian helpers. */
#define ADD(X, Y) (X + Y) ABI_TYPE ATOMIC_NAME(fetch_add)(CPUArchState *env, target_ulong addr,
GEN_ATOMIC_HELPER_FN(fetch_add, ADD, DATA_TYPE, old) ABI_TYPE val EXTRA_ARGS)
GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new) {
#undef ADD ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ldo, ldn, ret, sto;
#undef GEN_ATOMIC_HELPER_FN ldo = atomic_read__nocheck(haddr);
while (1) {
ret = BSWAP(ldo);
sto = BSWAP(ret + val);
ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto);
if (ldn == ldo) {
ATOMIC_MMU_CLEANUP;
return ret;
}
ldo = ldn;
}
}
ABI_TYPE ATOMIC_NAME(add_fetch)(CPUArchState *env, target_ulong addr,
ABI_TYPE val EXTRA_ARGS)
{
ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ldo, ldn, ret, sto;
ldo = atomic_read__nocheck(haddr);
while (1) {
ret = BSWAP(ldo) + val;
sto = BSWAP(ret);
ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto);
if (ldn == ldo) {
ATOMIC_MMU_CLEANUP;
return ret;
}
ldo = ldn;
}
}
#endif /* DATA_SIZE >= 16 */ #endif /* DATA_SIZE >= 16 */
#undef END #undef END
#undef MEND
#endif /* DATA_SIZE > 1 */ #endif /* DATA_SIZE > 1 */
#undef ATOMIC_TRACE_ST
#undef ATOMIC_TRACE_LD
#undef ATOMIC_TRACE_RMW
#undef BSWAP #undef BSWAP
#undef ABI_TYPE #undef ABI_TYPE
#undef DATA_TYPE #undef DATA_TYPE
#undef SDATA_TYPE
#undef SUFFIX #undef SUFFIX
#undef DATA_SIZE #undef DATA_SIZE
#undef SHIFT

View File

@@ -6,7 +6,7 @@
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either * License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version. * version 2 of the License, or (at your option) any later version.
* *
* This library is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -20,7 +20,6 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "cpu.h" #include "cpu.h"
#include "sysemu/cpus.h" #include "sysemu/cpus.h"
#include "sysemu/tcg.h"
#include "exec/exec-all.h" #include "exec/exec-all.h"
bool tcg_allowed; bool tcg_allowed;
@@ -28,8 +27,10 @@ bool tcg_allowed;
/* exit the current TB, but without causing any exception to be raised */ /* exit the current TB, but without causing any exception to be raised */
void cpu_loop_exit_noexc(CPUState *cpu) void cpu_loop_exit_noexc(CPUState *cpu)
{ {
/* XXX: restore cpu registers saved in host registers */
cpu->exception_index = -1; cpu->exception_index = -1;
cpu_loop_exit(cpu); siglongjmp(cpu->jmp_env, 1);
} }
#if defined(CONFIG_SOFTMMU) #if defined(CONFIG_SOFTMMU)
@@ -64,17 +65,15 @@ void cpu_reloading_memory_map(void)
void cpu_loop_exit(CPUState *cpu) void cpu_loop_exit(CPUState *cpu)
{ {
/* Undo the setting in cpu_tb_exec. */
cpu->can_do_io = 1;
siglongjmp(cpu->jmp_env, 1); siglongjmp(cpu->jmp_env, 1);
} }
void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc)
{ {
if (pc) { if (pc) {
cpu_restore_state(cpu, pc, true); cpu_restore_state(cpu, pc);
} }
cpu_loop_exit(cpu); siglongjmp(cpu->jmp_env, 1);
} }
void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc) void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc)

View File

@@ -6,7 +6,7 @@
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either * License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version. * version 2 of the License, or (at your option) any later version.
* *
* This library is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -16,9 +16,7 @@
* You should have received a copy of the GNU Lesser General Public * You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h" #include "cpu.h"
#include "trace.h" #include "trace.h"
#include "disas/disas.h" #include "disas/disas.h"
@@ -27,6 +25,7 @@
#include "qemu/atomic.h" #include "qemu/atomic.h"
#include "sysemu/qtest.h" #include "sysemu/qtest.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#include "exec/address-spaces.h"
#include "qemu/rcu.h" #include "qemu/rcu.h"
#include "exec/tb-hash.h" #include "exec/tb-hash.h"
#include "exec/tb-lookup.h" #include "exec/tb-lookup.h"
@@ -56,7 +55,7 @@ typedef struct SyncClocks {
#define MAX_DELAY_PRINT_RATE 2000000000LL #define MAX_DELAY_PRINT_RATE 2000000000LL
#define MAX_NB_PRINTS 100 #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; int64_t cpu_icount;
@@ -64,7 +63,7 @@ static void align_clocks(SyncClocks *sc, CPUState *cpu)
return; 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->diff_clk += cpu_icount_to_ns(sc->last_cpu_icount - cpu_icount);
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) { if (!icount_align_option) {
return; return;
} }
sc->realtime_clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT); 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->diff_clk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - sc->realtime_clock;
sc->last_cpu_icount sc->last_cpu_icount = cpu->icount_extra + cpu->icount_decr.u16.low;
= cpu->icount_extra + cpu_neg(cpu)->icount_decr.u16.low;
if (sc->diff_clk < max_delay) { if (sc->diff_clk < max_delay) {
max_delay = sc->diff_clk; 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) if (qemu_loglevel_mask(CPU_LOG_TB_CPU)
&& qemu_log_in_addr_range(itb->pc)) { && qemu_log_in_addr_range(itb->pc)) {
qemu_log_lock(); qemu_log_lock();
int flags = 0;
if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) {
flags |= CPU_DUMP_FPU;
}
#if defined(TARGET_I386) #if defined(TARGET_I386)
flags |= CPU_DUMP_CCOP; log_cpu_state(cpu, CPU_DUMP_CCOP);
#else
log_cpu_state(cpu, 0);
#endif #endif
log_cpu_state(cpu, flags);
qemu_log_unlock(); qemu_log_unlock();
} }
#endif /* DEBUG_DISAS */ #endif /* DEBUG_DISAS */
@@ -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. */ We only end up here when an existing TB is too long. */
cflags |= MIN(max_cycles, CF_COUNT_MASK); cflags |= MIN(max_cycles, CF_COUNT_MASK);
mmap_lock(); tb_lock();
tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base,
orig_tb->flags, cflags); orig_tb->flags, cflags);
tb->orig_tb = orig_tb; tb->orig_tb = orig_tb;
mmap_unlock(); tb_unlock();
/* execute the generated code */ /* execute the generated code */
trace_exec_tb_nocache(tb, tb->pc); trace_exec_tb_nocache(tb, tb->pc);
cpu_tb_exec(cpu, tb); cpu_tb_exec(cpu, tb);
mmap_lock(); tb_lock();
tb_phys_invalidate(tb, -1); tb_phys_invalidate(tb, -1);
mmap_unlock(); tb_remove(tb);
tcg_tb_remove(tb); tb_unlock();
} }
#endif #endif
@@ -246,7 +242,12 @@ void cpu_exec_step_atomic(CPUState *cpu)
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
if (tb == NULL) { if (tb == NULL) {
mmap_lock(); mmap_lock();
tb = tb_gen_code(cpu, pc, cs_base, flags, cflags); tb_lock();
tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask);
if (likely(tb == NULL)) {
tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
}
tb_unlock();
mmap_unlock(); mmap_unlock();
} }
@@ -261,17 +262,15 @@ void cpu_exec_step_atomic(CPUState *cpu)
cpu_tb_exec(cpu, tb); cpu_tb_exec(cpu, tb);
cc->cpu_exec_exit(cpu); cc->cpu_exec_exit(cpu);
} else { } else {
/* /* We may have exited due to another problem here, so we need
* to reset any tb_locks we may have taken but didn't release.
* The mmap_lock is dropped by tb_gen_code if it runs out of * The mmap_lock is dropped by tb_gen_code if it runs out of
* memory. * memory.
*/ */
#ifndef CONFIG_SOFTMMU #ifndef CONFIG_SOFTMMU
tcg_debug_assert(!have_mmap_lock()); tcg_debug_assert(!have_mmap_lock());
#endif #endif
if (qemu_mutex_iothread_locked()) { tb_lock_reset();
qemu_mutex_unlock_iothread();
}
assert_no_pages_locked();
} }
if (in_exclusive_region) { if (in_exclusive_region) {
@@ -294,7 +293,7 @@ struct tb_desc {
uint32_t trace_vcpu_dstate; uint32_t trace_vcpu_dstate;
}; };
static bool tb_lookup_cmp(const void *p, const void *d) static bool tb_cmp(const void *p, const void *d)
{ {
const TranslationBlock *tb = p; const TranslationBlock *tb = p;
const struct tb_desc *desc = d; const struct tb_desc *desc = d;
@@ -337,12 +336,9 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
desc.trace_vcpu_dstate = *cpu->trace_dstate; desc.trace_vcpu_dstate = *cpu->trace_dstate;
desc.pc = pc; desc.pc = pc;
phys_pc = get_page_addr_code(desc.env, pc); phys_pc = get_page_addr_code(desc.env, pc);
if (phys_pc == -1) {
return NULL;
}
desc.phys_page1 = phys_pc & TARGET_PAGE_MASK; desc.phys_page1 = phys_pc & TARGET_PAGE_MASK;
h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate); h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate);
return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp); return qht_lookup(&tb_ctx.htable, tb_cmp, &desc, h);
} }
void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr)
@@ -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, static inline void tb_add_jump(TranslationBlock *tb, int n,
TranslationBlock *tb_next) TranslationBlock *tb_next)
{ {
uintptr_t old;
assert(n < ARRAY_SIZE(tb->jmp_list_next)); assert(n < ARRAY_SIZE(tb->jmp_list_next));
qemu_spin_lock(&tb_next->jmp_lock); if (tb->jmp_list_next[n]) {
/* Another thread has already done this while we were
/* make sure the destination TB is valid */ * outside of the lock; nothing to do in this case */
if (tb_next->cflags & CF_INVALID) { return;
goto out_unlock_next;
} }
/* Atomically claim the jump destination slot only if it was NULL */
old = atomic_cmpxchg(&tb->jmp_dest[n], (uintptr_t)NULL, (uintptr_t)tb_next);
if (old) {
goto out_unlock_next;
}
/* patch the native jump address */
tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr);
/* add in TB jmp list */
tb->jmp_list_next[n] = tb_next->jmp_list_head;
tb_next->jmp_list_head = (uintptr_t)tb | n;
qemu_spin_unlock(&tb_next->jmp_lock);
qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc, qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc,
"Linking TBs %p [" TARGET_FMT_lx "Linking TBs %p [" TARGET_FMT_lx
"] index %d -> %p [" TARGET_FMT_lx "]\n", "] index %d -> %p [" TARGET_FMT_lx "]\n",
tb->tc.ptr, tb->pc, n, tb->tc.ptr, tb->pc, n,
tb_next->tc.ptr, tb_next->pc); tb_next->tc.ptr, tb_next->pc);
return;
out_unlock_next: /* patch the native jump address */
qemu_spin_unlock(&tb_next->jmp_lock); tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr);
return;
/* add in TB jmp circular list */
tb->jmp_list_next[n] = tb_next->jmp_list_first;
tb_next->jmp_list_first = (uintptr_t)tb | n;
} }
static inline TranslationBlock *tb_find(CPUState *cpu, static inline TranslationBlock *tb_find(CPUState *cpu,
@@ -402,11 +383,27 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
TranslationBlock *tb; TranslationBlock *tb;
target_ulong cs_base, pc; target_ulong cs_base, pc;
uint32_t flags; uint32_t flags;
bool acquired_tb_lock = false;
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
if (tb == NULL) { if (tb == NULL) {
/* mmap_lock is needed by tb_gen_code, and mmap_lock must be
* taken outside tb_lock. As system emulation is currently
* single threaded the locks are NOPs.
*/
mmap_lock(); mmap_lock();
tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask); tb_lock();
acquired_tb_lock = true;
/* There's a chance that our desired tb has been translated while
* taking the locks so we check again inside the lock.
*/
tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask);
if (likely(tb == NULL)) {
/* if no translated code available, then translate it now */
tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask);
}
mmap_unlock(); mmap_unlock();
/* We add the TB in the virtual pc hash table for the fast lookup */ /* We add the TB in the virtual pc hash table for the fast lookup */
atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb); atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb);
@@ -421,8 +418,17 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
} }
#endif #endif
/* See if we can patch the calling TB. */ /* See if we can patch the calling TB. */
if (last_tb) { if (last_tb && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) {
tb_add_jump(last_tb, tb_exit, tb); if (!acquired_tb_lock) {
tb_lock();
acquired_tb_lock = true;
}
if (!(tb->cflags & CF_INVALID)) {
tb_add_jump(last_tb, tb_exit, tb);
}
}
if (acquired_tb_lock) {
tb_unlock();
} }
return tb; return tb;
} }
@@ -469,7 +475,7 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
if (cpu->exception_index < 0) { if (cpu->exception_index < 0) {
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
if (replay_has_exception() 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 */ /* try to cause an exception pending in the log */
cpu_exec_nocache(cpu, 1, tb_find(cpu, NULL, 0, curr_cflags()), true); 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 * Ensure zeroing happens before reading cpu->exit_request or
* cpu->interrupt_request (see also smp_wmb in cpu_exit()) * 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))) { if (unlikely(atomic_read(&cpu->interrupt_request))) {
int interrupt_request; int interrupt_request;
@@ -579,7 +585,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
else { else {
if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { if (cc->cpu_exec_interrupt(cpu, interrupt_request)) {
replay_interrupt(); replay_interrupt();
cpu->exception_index = -1;
*last_tb = NULL; *last_tb = NULL;
} }
/* The target hook may have updated the 'cpu->interrupt_request'; /* The target hook may have updated the 'cpu->interrupt_request';
@@ -598,13 +603,10 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
} }
/* Finally, check if we need to exit to the main loop. */ /* Finally, check if we need to exit to the main loop. */
if (unlikely(atomic_read(&cpu->exit_request)) if (unlikely(atomic_read(&cpu->exit_request)
|| (use_icount || (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) {
&& cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra == 0)) {
atomic_set(&cpu->exit_request, 0); atomic_set(&cpu->exit_request, 0);
if (cpu->exception_index == -1) { cpu->exception_index = EXCP_INTERRUPT;
cpu->exception_index = EXCP_INTERRUPT;
}
return true; return true;
} }
@@ -627,7 +629,7 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
} }
*last_tb = NULL; *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) { if (insns_left < 0) {
/* Something asked us to stop executing chained TBs; just /* Something asked us to stop executing chained TBs; just
* continue round the main loop. Whatever requested the exit * 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); cpu_update_icount(cpu);
/* Refill decrementer and continue execution. */ /* Refill decrementer and continue execution. */
insns_left = MIN(0xffff, cpu->icount_budget); 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; cpu->icount_extra = cpu->icount_budget - insns_left;
if (!cpu->icount_extra) { if (!cpu->icount_extra) {
/* Execute any remaining instructions, then let the main loop /* 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(cpu == current_cpu);
g_assert(cc == CPU_GET_CLASS(cpu)); g_assert(cc == CPU_GET_CLASS(cpu));
#endif /* buggy compiler */ #endif /* buggy compiler */
#ifndef CONFIG_SOFTMMU cpu->can_do_io = 1;
tcg_debug_assert(!have_mmap_lock()); tb_lock_reset();
#endif
if (qemu_mutex_iothread_locked()) { if (qemu_mutex_iothread_locked()) {
qemu_mutex_unlock_iothread(); qemu_mutex_unlock_iothread();
} }
assert_no_pages_locked();
} }
/* if an exception is pending, we execute it here */ /* if an exception is pending, we execute it here */

File diff suppressed because it is too large Load Diff

View 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

View File

@@ -26,14 +26,15 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "sysemu/accel.h" #include "sysemu/accel.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "sysemu/tcg.h"
#include "qom/object.h" #include "qom/object.h"
#include "cpu.h" #include "qemu-common.h"
#include "qom/cpu.h"
#include "sysemu/cpus.h" #include "sysemu/cpus.h"
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
unsigned long tcg_tb_size; unsigned long tcg_tb_size;
#ifndef CONFIG_USER_ONLY
/* mask must never be zero, except for A20 change call */ /* mask must never be zero, except for A20 change call */
static void tcg_handle_interrupt(CPUState *cpu, int mask) 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)) { if (!qemu_cpu_is_self(cpu)) {
qemu_cpu_kick(cpu); qemu_cpu_kick(cpu);
} else { } else {
atomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1); cpu->icount_decr.u16.high = -1;
if (use_icount && if (use_icount &&
!cpu->can_do_io !cpu->can_do_io
&& (mask & ~old_mask) != 0) { && (mask & ~old_mask) != 0) {
@@ -58,6 +59,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
} }
} }
} }
#endif
static int tcg_init(MachineState *ms) static int tcg_init(MachineState *ms)
{ {

View File

@@ -6,7 +6,7 @@
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either * License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version. * version 2 of the License, or (at your option) any later version.
* *
* This library is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -398,54 +398,6 @@ void HELPER(gvec_neg64)(void *d, void *a, uint32_t desc)
clear_high(d, oprsz, 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) void HELPER(gvec_mov)(void *d, void *a, uint32_t desc)
{ {
intptr_t oprsz = simd_oprsz(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); clear_high(d, oprsz, desc);
} }
void HELPER(gvec_nand)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = ~(*(vec64 *)(a + i) & *(vec64 *)(b + i));
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_nor)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = ~(*(vec64 *)(a + i) | *(vec64 *)(b + i));
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_eqv)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = ~(*(vec64 *)(a + i) ^ *(vec64 *)(b + i));
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc)
{ {
intptr_t oprsz = simd_oprsz(desc); intptr_t oprsz = simd_oprsz(desc);
@@ -773,150 +692,6 @@ void HELPER(gvec_sar64i)(void *d, void *a, uint32_t desc)
clear_high(d, oprsz, 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. /* If vectors are enabled, the compiler fills in -1 for true.
Otherwise, we must take care of this by hand. */ Otherwise, we must take care of this by hand. */
#ifdef CONFIG_VECTOR16 #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 oprsz = simd_oprsz(desc); \
intptr_t i; \ intptr_t i; \
for (i = 0; i < oprsz; i += sizeof(TYPE)) { \ for (i = 0; i < oprsz; i += sizeof(vec64)) { \
*(TYPE *)(d + i) = DO_CMP0(*(TYPE *)(a + i) OP *(TYPE *)(b + i)); \ *(TYPE *)(d + i) = DO_CMP0(*(TYPE *)(a + i) OP *(TYPE *)(b + i)); \
} \ } \
clear_high(d, oprsz, desc); \ clear_high(d, oprsz, desc); \
@@ -1220,241 +995,3 @@ void HELPER(gvec_ussub64)(void *d, void *a, void *b, uint32_t desc)
} }
clear_high(d, oprsz, desc); clear_high(d, oprsz, desc);
} }
void HELPER(gvec_smin8)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(int8_t)) {
int8_t aa = *(int8_t *)(a + i);
int8_t bb = *(int8_t *)(b + i);
int8_t dd = aa < bb ? aa : bb;
*(int8_t *)(d + i) = dd;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_smin16)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(int16_t)) {
int16_t aa = *(int16_t *)(a + i);
int16_t bb = *(int16_t *)(b + i);
int16_t dd = aa < bb ? aa : bb;
*(int16_t *)(d + i) = dd;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_smin32)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(int32_t)) {
int32_t aa = *(int32_t *)(a + i);
int32_t bb = *(int32_t *)(b + i);
int32_t dd = aa < bb ? aa : bb;
*(int32_t *)(d + i) = dd;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_smin64)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(int64_t)) {
int64_t aa = *(int64_t *)(a + i);
int64_t bb = *(int64_t *)(b + i);
int64_t dd = aa < bb ? aa : bb;
*(int64_t *)(d + i) = dd;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_smax8)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(int8_t)) {
int8_t aa = *(int8_t *)(a + i);
int8_t bb = *(int8_t *)(b + i);
int8_t dd = aa > bb ? aa : bb;
*(int8_t *)(d + i) = dd;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_smax16)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(int16_t)) {
int16_t aa = *(int16_t *)(a + i);
int16_t bb = *(int16_t *)(b + i);
int16_t dd = aa > bb ? aa : bb;
*(int16_t *)(d + i) = dd;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_smax32)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(int32_t)) {
int32_t aa = *(int32_t *)(a + i);
int32_t bb = *(int32_t *)(b + i);
int32_t dd = aa > bb ? aa : bb;
*(int32_t *)(d + i) = dd;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_smax64)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(int64_t)) {
int64_t aa = *(int64_t *)(a + i);
int64_t bb = *(int64_t *)(b + i);
int64_t dd = aa > bb ? aa : bb;
*(int64_t *)(d + i) = dd;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_umin8)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
uint8_t aa = *(uint8_t *)(a + i);
uint8_t bb = *(uint8_t *)(b + i);
uint8_t dd = aa < bb ? aa : bb;
*(uint8_t *)(d + i) = dd;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_umin16)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
uint16_t aa = *(uint16_t *)(a + i);
uint16_t bb = *(uint16_t *)(b + i);
uint16_t dd = aa < bb ? aa : bb;
*(uint16_t *)(d + i) = dd;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_umin32)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
uint32_t aa = *(uint32_t *)(a + i);
uint32_t bb = *(uint32_t *)(b + i);
uint32_t dd = aa < bb ? aa : bb;
*(uint32_t *)(d + i) = dd;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_umin64)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
uint64_t aa = *(uint64_t *)(a + i);
uint64_t bb = *(uint64_t *)(b + i);
uint64_t dd = aa < bb ? aa : bb;
*(uint64_t *)(d + i) = dd;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_umax8)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
uint8_t aa = *(uint8_t *)(a + i);
uint8_t bb = *(uint8_t *)(b + i);
uint8_t dd = aa > bb ? aa : bb;
*(uint8_t *)(d + i) = dd;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_umax16)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
uint16_t aa = *(uint16_t *)(a + i);
uint16_t bb = *(uint16_t *)(b + i);
uint16_t dd = aa > bb ? aa : bb;
*(uint16_t *)(d + i) = dd;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_umax32)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
uint32_t aa = *(uint32_t *)(a + i);
uint32_t bb = *(uint32_t *)(b + i);
uint32_t dd = aa > bb ? aa : bb;
*(uint32_t *)(d + i) = dd;
}
clear_high(d, oprsz, desc);
}
void HELPER(gvec_umax64)(void *d, void *a, void *b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
uint64_t aa = *(uint64_t *)(a + i);
uint64_t bb = *(uint64_t *)(b + i);
uint64_t dd = aa > bb ? aa : bb;
*(uint64_t *)(d + i) = dd;
}
clear_high(d, oprsz, desc);
}
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);
}

View File

@@ -146,7 +146,7 @@ uint64_t HELPER(ctpop_i64)(uint64_t arg)
void *HELPER(lookup_tb_ptr)(CPUArchState *env) void *HELPER(lookup_tb_ptr)(CPUArchState *env)
{ {
CPUState *cpu = env_cpu(env); CPUState *cpu = ENV_GET_CPU(env);
TranslationBlock *tb; TranslationBlock *tb;
target_ulong cs_base, pc; target_ulong cs_base, pc;
uint32_t flags; uint32_t flags;
@@ -165,5 +165,5 @@ void *HELPER(lookup_tb_ptr)(CPUArchState *env)
void HELPER(exit_atomic)(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());
} }

View File

@@ -125,19 +125,11 @@ GEN_ATOMIC_HELPERS(fetch_add)
GEN_ATOMIC_HELPERS(fetch_and) GEN_ATOMIC_HELPERS(fetch_and)
GEN_ATOMIC_HELPERS(fetch_or) GEN_ATOMIC_HELPERS(fetch_or)
GEN_ATOMIC_HELPERS(fetch_xor) GEN_ATOMIC_HELPERS(fetch_xor)
GEN_ATOMIC_HELPERS(fetch_smin)
GEN_ATOMIC_HELPERS(fetch_umin)
GEN_ATOMIC_HELPERS(fetch_smax)
GEN_ATOMIC_HELPERS(fetch_umax)
GEN_ATOMIC_HELPERS(add_fetch) GEN_ATOMIC_HELPERS(add_fetch)
GEN_ATOMIC_HELPERS(and_fetch) GEN_ATOMIC_HELPERS(and_fetch)
GEN_ATOMIC_HELPERS(or_fetch) GEN_ATOMIC_HELPERS(or_fetch)
GEN_ATOMIC_HELPERS(xor_fetch) GEN_ATOMIC_HELPERS(xor_fetch)
GEN_ATOMIC_HELPERS(smin_fetch)
GEN_ATOMIC_HELPERS(umin_fetch)
GEN_ATOMIC_HELPERS(smax_fetch)
GEN_ATOMIC_HELPERS(umax_fetch)
GEN_ATOMIC_HELPERS(xchg) GEN_ATOMIC_HELPERS(xchg)
@@ -200,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_ussub32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_ussub64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_ussub64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_smin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_smin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_smin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_smin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_smax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_smax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_smax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_smax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_umin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_umin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_umin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_umin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_umax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_umax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_umax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_umax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_neg8, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(gvec_neg8, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_neg16, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(gvec_neg16, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_neg32, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(gvec_neg32, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_neg64, 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_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_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_or, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_xor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_xor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_andc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_andc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_orc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_orc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_nand, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_nor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_eqv, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_ands, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_4(gvec_ands, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
DEF_HELPER_FLAGS_4(gvec_xors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_4(gvec_xors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
@@ -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_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_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_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_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) 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_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_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_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)

View File

@@ -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) # TCG related tracing (mostly disabled by default)
# cpu-exec.c # cpu-exec.c

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either * License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version. * version 2 of the License, or (at your option) any later version.
* *
* This library is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -23,13 +23,10 @@
/* translate-all.c */ /* translate-all.c */
struct page_collection *page_collection_lock(tb_page_addr_t start, void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len);
tb_page_addr_t end);
void page_collection_unlock(struct page_collection *set);
void tb_invalidate_phys_page_fast(struct page_collection *pages,
tb_page_addr_t start, int len);
void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
int is_cpu_write_access); int is_cpu_write_access);
void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end);
void tb_check_watchpoint(CPUState *cpu); void tb_check_watchpoint(CPUState *cpu);
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY

View File

@@ -8,6 +8,7 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "cpu.h" #include "cpu.h"
#include "tcg/tcg.h" #include "tcg/tcg.h"
@@ -31,9 +32,9 @@ void translator_loop_temp_check(DisasContextBase *db)
} }
void translator_loop(const TranslatorOps *ops, DisasContextBase *db, void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
CPUState *cpu, TranslationBlock *tb, int max_insns) CPUState *cpu, TranslationBlock *tb)
{ {
int bp_insn = 0; int max_insns;
/* Initialize DisasContext */ /* Initialize DisasContext */
db->tb = tb; db->tb = tb;
@@ -41,10 +42,21 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
db->pc_next = db->pc_first; db->pc_next = db->pc_first;
db->is_jmp = DISAS_NEXT; db->is_jmp = DISAS_NEXT;
db->num_insns = 0; db->num_insns = 0;
db->max_insns = max_insns;
db->singlestep_enabled = cpu->singlestep_enabled; 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 */ tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
/* Reset the temp count so that we can identify leaks */ /* Reset the temp count so that we can identify leaks */
@@ -61,13 +73,11 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
/* Pass breakpoint hits to target for further processing */ /* Pass breakpoint hits to target for further processing */
if (!db->singlestep_enabled if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
&& unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
CPUBreakpoint *bp; CPUBreakpoint *bp;
QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
if (bp->pc == db->pc_next) { if (bp->pc == db->pc_next) {
if (ops->breakpoint_check(db, cpu, bp)) { if (ops->breakpoint_check(db, cpu, bp)) {
bp_insn = 1;
break; break;
} }
} }
@@ -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 update db->pc_next and db->is_jmp to indicate what should be
done next -- either exiting this loop or locate the start of done next -- either exiting this loop or locate the start of
the next instruction. */ the next instruction. */
if (db->num_insns == db->max_insns if (db->num_insns == max_insns && (tb_cflags(db->tb) & CF_LAST_IO)) {
&& (tb_cflags(db->tb) & CF_LAST_IO)) {
/* Accept I/O on the last instruction. */ /* Accept I/O on the last instruction. */
gen_io_start(); gen_io_start();
ops->translate_insn(db, cpu); ops->translate_insn(db, cpu);
@@ -102,7 +111,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
/* Stop translation if the output buffer is full, /* Stop translation if the output buffer is full,
or we have executed all of the allowed instructions. */ or we have executed all of the allowed instructions. */
if (tcg_op_buf_full() || db->num_insns >= db->max_insns) { if (tcg_op_buf_full() || db->num_insns >= max_insns) {
db->is_jmp = DISAS_TOO_MANY; db->is_jmp = DISAS_TOO_MANY;
break; break;
} }
@@ -110,7 +119,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
/* Emit code to exit the TB, as indicated by db->is_jmp. */ /* Emit code to exit the TB, as indicated by db->is_jmp. */
ops->tb_stop(db, cpu); ops->tb_stop(db, cpu);
gen_tb_end(db->tb, db->num_insns - bp_insn); gen_tb_end(db->tb, db->num_insns);
/* The disas_log hook may use these values rather than recompute. */ /* The disas_log hook may use these values rather than recompute. */
db->tb->size = db->pc_next - db->pc_first; db->tb->size = db->pc_next - db->pc_first;

View File

@@ -1,9 +1,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "qom/cpu.h" #include "qom/cpu.h"
#include "sysemu/replay.h" #include "sysemu/replay.h"
#include "sysemu/sysemu.h"
bool enable_cpu_pm = false;
void cpu_resume(CPUState *cpu) void cpu_resume(CPUState *cpu)
{ {

View File

@@ -6,7 +6,7 @@
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either * License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version. * version 2 of the License, or (at your option) any later version.
* *
* This library is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -25,7 +25,6 @@
#include "exec/cpu_ldst.h" #include "exec/cpu_ldst.h"
#include "translate-all.h" #include "translate-all.h"
#include "exec/helper-proto.h" #include "exec/helper-proto.h"
#include "qemu/atomic128.h"
#undef EAX #undef EAX
#undef ECX #undef ECX
@@ -63,57 +62,28 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
{ {
CPUState *cpu = current_cpu; CPUState *cpu = current_cpu;
CPUClass *cc; CPUClass *cc;
int ret;
unsigned long address = (unsigned long)info->si_addr; unsigned long address = (unsigned long)info->si_addr;
MMUAccessType access_type = is_write ? MMU_DATA_STORE : MMU_DATA_LOAD;
switch (helper_retaddr) { /* We must handle PC addresses from two different sources:
default: * a call return address and a signal frame address.
/* *
* Fault during host memory operation within a helper function. * Within cpu_restore_state_from_tb we assume the former and adjust
* The helper's host return address, saved here, gives us a * the address by -GETPC_ADJ so that the address is within the call
* pointer into the generated code that will unwind to the * insn so that addr does not accidentally match the beginning of the
* correct guest pc. * 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; pc = helper_retaddr;
break; } else {
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.
*/
pc += GETPC_ADJ; 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 /* 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 * currently executing TB was modified and must be exited
* immediately. Clear helper_retaddr for next execution. * immediately. Clear helper_retaddr for next execution.
*/ */
clear_helper_retaddr(); helper_retaddr = 0;
cpu_exit_tb_from_sighandler(cpu, old_set); cpu_exit_tb_from_sighandler(cpu, old_set);
/* NORETURN */ /* NORETURN */
@@ -176,16 +146,35 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
are still valid segv ones */ are still valid segv ones */
address = h2g_nocheck(address); 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 = CPU_GET_CLASS(cpu);
cc->tlb_fill(cpu, address, 0, access_type, MMU_USER_IDX, false, pc); /* see if it is an MMU fault */
g_assert_not_reached(); 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__) #if defined(__i386__)
@@ -489,66 +478,28 @@ int cpu_signal_handler(int host_signum, void *pinfo,
#elif defined(__aarch64__) #elif defined(__aarch64__)
#ifndef ESR_MAGIC
/* Pre-3.16 kernel headers don't have these, so provide fallback definitions */
#define ESR_MAGIC 0x45535201
struct esr_context {
struct _aarch64_ctx head;
uint64_t esr;
};
#endif
static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc)
{
return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved;
}
static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr)
{
return (struct _aarch64_ctx *)((char *)hdr + hdr->size);
}
int cpu_signal_handler(int host_signum, void *pinfo, void *puc) int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
{ {
siginfo_t *info = pinfo; siginfo_t *info = pinfo;
ucontext_t *uc = puc; ucontext_t *uc = puc;
uintptr_t pc = uc->uc_mcontext.pc; uintptr_t pc = uc->uc_mcontext.pc;
uint32_t insn = *(uint32_t *)pc;
bool is_write; bool is_write;
struct _aarch64_ctx *hdr;
struct esr_context const *esrctx = NULL;
/* Find the esr_context, which has the WnR bit in it */ /* XXX: need kernel patch to get write flag faster. */
for (hdr = first_ctx(uc); hdr->magic; hdr = next_ctx(hdr)) { is_write = ( (insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */
if (hdr->magic == ESR_MAGIC) { || (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */
esrctx = (struct esr_context const *)hdr; || (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */
break; || (insn & 0xbfc00000) == 0x0d800000 /* C3.3.4 */
} || (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */
} || (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */
|| (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */
/* Ingore bits 10, 11 & 21, controlling indexing. */
|| (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */
|| (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */
/* Ignore bits 23 & 24, controlling indexing. */
|| (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */
if (esrctx) {
/* For data aborts ESR.EC is 0b10010x: then bit 6 is the WnR bit */
uint64_t esr = esrctx->esr;
is_write = extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1;
} else {
/*
* Fall back to parsing instructions; will only be needed
* for really ancient (pre-3.16) kernels.
*/
uint32_t insn = *(uint32_t *)pc;
is_write = ((insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */
|| (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */
|| (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */
|| (insn & 0xbfc00000) == 0x0d800000 /* C3.3.4 */
|| (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */
|| (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */
|| (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */
/* Ignore bits 10, 11 & 21, controlling indexing. */
|| (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */
|| (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */
/* Ignore bits 23 & 24, controlling indexing. */
|| (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */
}
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
} }
@@ -619,81 +570,6 @@ int cpu_signal_handler(int host_signum, void *pinfo,
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
} }
#elif defined(__riscv)
int cpu_signal_handler(int host_signum, void *pinfo,
void *puc)
{
siginfo_t *info = pinfo;
ucontext_t *uc = puc;
greg_t pc = uc->uc_mcontext.__gregs[REG_PC];
uint32_t insn = *(uint32_t *)pc;
int is_write = 0;
/* Detect store by reading the instruction at the program
counter. Note: we currently only generate 32-bit
instructions so we thus only detect 32-bit stores */
switch (((insn >> 0) & 0b11)) {
case 3:
switch (((insn >> 2) & 0b11111)) {
case 8:
switch (((insn >> 12) & 0b111)) {
case 0: /* sb */
case 1: /* sh */
case 2: /* sw */
case 3: /* sd */
case 4: /* sq */
is_write = 1;
break;
default:
break;
}
break;
case 9:
switch (((insn >> 12) & 0b111)) {
case 2: /* fsw */
case 3: /* fsd */
case 4: /* fsq */
is_write = 1;
break;
default:
break;
}
break;
default:
break;
}
}
/* Check for compressed instructions */
switch (((insn >> 13) & 0b111)) {
case 7:
switch (insn & 0b11) {
case 0: /*c.sd */
case 2: /* c.sdsp */
is_write = 1;
break;
default:
break;
}
break;
case 6:
switch (insn & 0b11) {
case 0: /* c.sw */
case 3: /* c.swsp */
is_write = 1;
break;
default:
break;
}
break;
default:
break;
}
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
}
#else #else
#error host CPU specific signal handler needed #error host CPU specific signal handler needed
@@ -708,17 +584,16 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
{ {
/* Enforce qemu required alignment. */ /* Enforce qemu required alignment. */
if (unlikely(addr & (size - 1))) { 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); helper_retaddr = retaddr;
set_helper_retaddr(retaddr); return g2h(addr);
return ret;
} }
/* Macro to call the above, with local variables from the use context. */ /* Macro to call the above, with local variables from the use context. */
#define ATOMIC_MMU_DECLS do {} while (0) #define ATOMIC_MMU_DECLS do {} while (0)
#define ATOMIC_MMU_LOOKUP atomic_mmu_lookup(env, addr, DATA_SIZE, GETPC()) #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 ATOMIC_NAME(X) HELPER(glue(glue(atomic_ ## X, SUFFIX), END))
#define EXTRA_ARGS #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 /* The following is only callable from other helpers, and matches up
with the softmmu version. */ with the softmmu version. */
#if HAVE_ATOMIC128 || HAVE_CMPXCHG128 #ifdef CONFIG_ATOMIC128
#undef EXTRA_ARGS #undef EXTRA_ARGS
#undef ATOMIC_NAME #undef ATOMIC_NAME
@@ -753,4 +628,4 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
#define DATA_SIZE 16 #define DATA_SIZE 16
#include "atomic_template.h" #include "atomic_template.h"
#endif #endif /* CONFIG_ATOMIC128 */

View File

@@ -22,13 +22,13 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h" #include "cpu.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "sysemu/arch_init.h" #include "sysemu/arch_init.h"
#include "hw/pci/pci.h" #include "hw/pci/pci.h"
#include "hw/audio/soundhw.h" #include "hw/audio/soundhw.h"
#include "qapi/qapi-commands-misc.h" #include "qapi/qapi-commands-misc.h"
#include "qapi/error.h"
#include "qemu/config-file.h" #include "qemu/config-file.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "hw/acpi/acpi.h" #include "hw/acpi/acpi.h"
@@ -51,14 +51,14 @@ int graphic_depth = 32;
#define QEMU_ARCH QEMU_ARCH_ARM #define QEMU_ARCH QEMU_ARCH_ARM
#elif defined(TARGET_CRIS) #elif defined(TARGET_CRIS)
#define QEMU_ARCH QEMU_ARCH_CRIS #define QEMU_ARCH QEMU_ARCH_CRIS
#elif defined(TARGET_HPPA)
#define QEMU_ARCH QEMU_ARCH_HPPA
#elif defined(TARGET_I386) #elif defined(TARGET_I386)
#define QEMU_ARCH QEMU_ARCH_I386 #define QEMU_ARCH QEMU_ARCH_I386
#elif defined(TARGET_LM32) #elif defined(TARGET_HPPA)
#define QEMU_ARCH QEMU_ARCH_LM32 #define QEMU_ARCH QEMU_ARCH_HPPA
#elif defined(TARGET_M68K) #elif defined(TARGET_M68K)
#define QEMU_ARCH QEMU_ARCH_M68K #define QEMU_ARCH QEMU_ARCH_M68K
#elif defined(TARGET_LM32)
#define QEMU_ARCH QEMU_ARCH_LM32
#elif defined(TARGET_MICROBLAZE) #elif defined(TARGET_MICROBLAZE)
#define QEMU_ARCH QEMU_ARCH_MICROBLAZE #define QEMU_ARCH QEMU_ARCH_MICROBLAZE
#elif defined(TARGET_MIPS) #elif defined(TARGET_MIPS)
@@ -79,12 +79,12 @@ int graphic_depth = 32;
#define QEMU_ARCH QEMU_ARCH_SH4 #define QEMU_ARCH QEMU_ARCH_SH4
#elif defined(TARGET_SPARC) #elif defined(TARGET_SPARC)
#define QEMU_ARCH QEMU_ARCH_SPARC #define QEMU_ARCH QEMU_ARCH_SPARC
#elif defined(TARGET_TRICORE)
#define QEMU_ARCH QEMU_ARCH_TRICORE
#elif defined(TARGET_UNICORE32)
#define QEMU_ARCH QEMU_ARCH_UNICORE32
#elif defined(TARGET_XTENSA) #elif defined(TARGET_XTENSA)
#define QEMU_ARCH QEMU_ARCH_XTENSA #define QEMU_ARCH QEMU_ARCH_XTENSA
#elif defined(TARGET_UNICORE32)
#define QEMU_ARCH QEMU_ARCH_UNICORE32
#elif defined(TARGET_TRICORE)
#define QEMU_ARCH QEMU_ARCH_TRICORE
#endif #endif
const uint32_t arch_type = QEMU_ARCH; const uint32_t arch_type = QEMU_ARCH;
@@ -112,8 +112,7 @@ TargetInfo *qmp_query_target(Error **errp)
{ {
TargetInfo *info = g_malloc0(sizeof(*info)); TargetInfo *info = g_malloc0(sizeof(*info));
info->arch = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, -1, info->arch = g_strdup(TARGET_NAME);
&error_abort);
return info; return info;
} }

View File

@@ -1,4 +1,4 @@
common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o common-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
common-obj-$(CONFIG_SPICE) += spiceaudio.o common-obj-$(CONFIG_SPICE) += spiceaudio.o
common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o
common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o

View File

@@ -21,22 +21,42 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include "qemu-common.h"
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#include "qemu/module.h"
#include "audio.h" #include "audio.h"
#include "trace.h" #include "trace.h"
#if QEMU_GNUC_PREREQ(4, 3)
#pragma GCC diagnostic ignored "-Waddress" #pragma GCC diagnostic ignored "-Waddress"
#endif
#define AUDIO_CAP "alsa" #define AUDIO_CAP "alsa"
#include "audio_int.h" #include "audio_int.h"
typedef struct ALSAConf {
int size_in_usec_in;
int size_in_usec_out;
const char *pcm_name_in;
const char *pcm_name_out;
unsigned int buffer_size_in;
unsigned int period_size_in;
unsigned int buffer_size_out;
unsigned int period_size_out;
unsigned int threshold;
int buffer_size_in_overridden;
int period_size_in_overridden;
int buffer_size_out_overridden;
int period_size_out_overridden;
} ALSAConf;
struct pollhlp { struct pollhlp {
snd_pcm_t *handle; snd_pcm_t *handle;
struct pollfd *pfds; struct pollfd *pfds;
ALSAConf *conf;
int count; int count;
int mask; int mask;
}; };
@@ -48,7 +68,6 @@ typedef struct ALSAVoiceOut {
void *pcm_buf; void *pcm_buf;
snd_pcm_t *handle; snd_pcm_t *handle;
struct pollhlp pollhlp; struct pollhlp pollhlp;
Audiodev *dev;
} ALSAVoiceOut; } ALSAVoiceOut;
typedef struct ALSAVoiceIn { typedef struct ALSAVoiceIn {
@@ -56,18 +75,21 @@ typedef struct ALSAVoiceIn {
snd_pcm_t *handle; snd_pcm_t *handle;
void *pcm_buf; void *pcm_buf;
struct pollhlp pollhlp; struct pollhlp pollhlp;
Audiodev *dev;
} ALSAVoiceIn; } ALSAVoiceIn;
struct alsa_params_req { struct alsa_params_req {
int freq; int freq;
snd_pcm_format_t fmt; snd_pcm_format_t fmt;
int nchannels; int nchannels;
int size_in_usec;
int override_mask;
unsigned int buffer_size;
unsigned int period_size;
}; };
struct alsa_params_obt { struct alsa_params_obt {
int freq; int freq;
AudioFormat fmt; audfmt_e fmt;
int endianness; int endianness;
int nchannels; int nchannels;
snd_pcm_uframes_t samples; snd_pcm_uframes_t samples;
@@ -274,16 +296,16 @@ static int alsa_write (SWVoiceOut *sw, void *buf, int len)
return audio_pcm_sw_write (sw, buf, len); return audio_pcm_sw_write (sw, buf, len);
} }
static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness) static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
{ {
switch (fmt) { switch (fmt) {
case AUDIO_FORMAT_S8: case AUD_FMT_S8:
return SND_PCM_FORMAT_S8; return SND_PCM_FORMAT_S8;
case AUDIO_FORMAT_U8: case AUD_FMT_U8:
return SND_PCM_FORMAT_U8; return SND_PCM_FORMAT_U8;
case AUDIO_FORMAT_S16: case AUD_FMT_S16:
if (endianness) { if (endianness) {
return SND_PCM_FORMAT_S16_BE; return SND_PCM_FORMAT_S16_BE;
} }
@@ -291,7 +313,7 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
return SND_PCM_FORMAT_S16_LE; return SND_PCM_FORMAT_S16_LE;
} }
case AUDIO_FORMAT_U16: case AUD_FMT_U16:
if (endianness) { if (endianness) {
return SND_PCM_FORMAT_U16_BE; return SND_PCM_FORMAT_U16_BE;
} }
@@ -299,7 +321,7 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
return SND_PCM_FORMAT_U16_LE; return SND_PCM_FORMAT_U16_LE;
} }
case AUDIO_FORMAT_S32: case AUD_FMT_S32:
if (endianness) { if (endianness) {
return SND_PCM_FORMAT_S32_BE; return SND_PCM_FORMAT_S32_BE;
} }
@@ -307,7 +329,7 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
return SND_PCM_FORMAT_S32_LE; return SND_PCM_FORMAT_S32_LE;
} }
case AUDIO_FORMAT_U32: case AUD_FMT_U32:
if (endianness) { if (endianness) {
return SND_PCM_FORMAT_U32_BE; return SND_PCM_FORMAT_U32_BE;
} }
@@ -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) int *endianness)
{ {
switch (alsafmt) { switch (alsafmt) {
case SND_PCM_FORMAT_S8: case SND_PCM_FORMAT_S8:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_S8; *fmt = AUD_FMT_S8;
break; break;
case SND_PCM_FORMAT_U8: case SND_PCM_FORMAT_U8:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_U8; *fmt = AUD_FMT_U8;
break; break;
case SND_PCM_FORMAT_S16_LE: case SND_PCM_FORMAT_S16_LE:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_S16; *fmt = AUD_FMT_S16;
break; break;
case SND_PCM_FORMAT_U16_LE: case SND_PCM_FORMAT_U16_LE:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_U16; *fmt = AUD_FMT_U16;
break; break;
case SND_PCM_FORMAT_S16_BE: case SND_PCM_FORMAT_S16_BE:
*endianness = 1; *endianness = 1;
*fmt = AUDIO_FORMAT_S16; *fmt = AUD_FMT_S16;
break; break;
case SND_PCM_FORMAT_U16_BE: case SND_PCM_FORMAT_U16_BE:
*endianness = 1; *endianness = 1;
*fmt = AUDIO_FORMAT_U16; *fmt = AUD_FMT_U16;
break; break;
case SND_PCM_FORMAT_S32_LE: case SND_PCM_FORMAT_S32_LE:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_S32; *fmt = AUD_FMT_S32;
break; break;
case SND_PCM_FORMAT_U32_LE: case SND_PCM_FORMAT_U32_LE:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_U32; *fmt = AUD_FMT_U32;
break; break;
case SND_PCM_FORMAT_S32_BE: case SND_PCM_FORMAT_S32_BE:
*endianness = 1; *endianness = 1;
*fmt = AUDIO_FORMAT_S32; *fmt = AUD_FMT_S32;
break; break;
case SND_PCM_FORMAT_U32_BE: case SND_PCM_FORMAT_U32_BE:
*endianness = 1; *endianness = 1;
*fmt = AUDIO_FORMAT_U32; *fmt = AUD_FMT_U32;
break; break;
default: default:
@@ -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, static void alsa_dump_info (struct alsa_params_req *req,
struct alsa_params_obt *obt, struct alsa_params_obt *obt,
snd_pcm_format_t obtfmt, snd_pcm_format_t obtfmt)
AudiodevAlsaPerDirectionOptions *apdo)
{ {
dolog("parameter | requested value | obtained value\n"); dolog ("parameter | requested value | obtained value\n");
dolog("format | %10d | %10d\n", req->fmt, obtfmt); dolog ("format | %10d | %10d\n", req->fmt, obtfmt);
dolog("channels | %10d | %10d\n", dolog ("channels | %10d | %10d\n",
req->nchannels, obt->nchannels); req->nchannels, obt->nchannels);
dolog("frequency | %10d | %10d\n", req->freq, obt->freq); dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
dolog("============================================\n"); dolog ("============================================\n");
dolog("requested: buffer len %" PRId32 " period len %" PRId32 "\n", dolog ("requested: buffer size %d period size %d\n",
apdo->buffer_length, apdo->period_length); req->buffer_size, req->period_size);
dolog("obtained: samples %ld\n", obt->samples); dolog ("obtained: samples %ld\n", obt->samples);
} }
static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold) static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
@@ -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, static int alsa_open (int in, struct alsa_params_req *req,
struct alsa_params_obt *obt, snd_pcm_t **handlep, struct alsa_params_obt *obt, snd_pcm_t **handlep,
Audiodev *dev) ALSAConf *conf)
{ {
AudiodevAlsaOptions *aopts = &dev->u.alsa;
AudiodevAlsaPerDirectionOptions *apdo = in ? aopts->in : aopts->out;
snd_pcm_t *handle; snd_pcm_t *handle;
snd_pcm_hw_params_t *hw_params; snd_pcm_hw_params_t *hw_params;
int err; int err;
int size_in_usec;
unsigned int freq, nchannels; unsigned int freq, nchannels;
const char *pcm_name = apdo->has_dev ? apdo->dev : "default"; const char *pcm_name = in ? conf->pcm_name_in : conf->pcm_name_out;
snd_pcm_uframes_t obt_buffer_size; snd_pcm_uframes_t obt_buffer_size;
const char *typ = in ? "ADC" : "DAC"; const char *typ = in ? "ADC" : "DAC";
snd_pcm_format_t obtfmt; snd_pcm_format_t obtfmt;
freq = req->freq; freq = req->freq;
nchannels = req->nchannels; nchannels = req->nchannels;
size_in_usec = req->size_in_usec;
snd_pcm_hw_params_alloca (&hw_params); snd_pcm_hw_params_alloca (&hw_params);
@@ -508,42 +529,79 @@ static int alsa_open(bool in, struct alsa_params_req *req,
goto err; goto err;
} }
if (apdo->buffer_length) { if (req->buffer_size) {
int dir = 0; unsigned long obt;
unsigned int btime = apdo->buffer_length;
err = snd_pcm_hw_params_set_buffer_time_near( if (size_in_usec) {
handle, hw_params, &btime, &dir); int dir = 0;
unsigned int btime = req->buffer_size;
err = snd_pcm_hw_params_set_buffer_time_near (
handle,
hw_params,
&btime,
&dir
);
obt = btime;
}
else {
snd_pcm_uframes_t bsize = req->buffer_size;
err = snd_pcm_hw_params_set_buffer_size_near (
handle,
hw_params,
&bsize
);
obt = bsize;
}
if (err < 0) { if (err < 0) {
alsa_logerr2(err, typ, "Failed to set buffer time to %" PRId32 "\n", alsa_logerr2 (err, typ, "Failed to set buffer %s to %d\n",
apdo->buffer_length); size_in_usec ? "time" : "size", req->buffer_size);
goto err; goto err;
} }
if (apdo->has_buffer_length && btime != apdo->buffer_length) { if ((req->override_mask & 2) && (obt - req->buffer_size))
dolog("Requested buffer time %" PRId32 dolog ("Requested buffer %s %u was rejected, using %lu\n",
" was rejected, using %u\n", apdo->buffer_length, btime); size_in_usec ? "time" : "size", req->buffer_size, obt);
}
} }
if (apdo->period_length) { if (req->period_size) {
int dir = 0; unsigned long obt;
unsigned int ptime = apdo->period_length;
err = snd_pcm_hw_params_set_period_time_near(handle, hw_params, &ptime, if (size_in_usec) {
&dir); int dir = 0;
unsigned int ptime = req->period_size;
err = snd_pcm_hw_params_set_period_time_near (
handle,
hw_params,
&ptime,
&dir
);
obt = ptime;
}
else {
int dir = 0;
snd_pcm_uframes_t psize = req->period_size;
err = snd_pcm_hw_params_set_period_size_near (
handle,
hw_params,
&psize,
&dir
);
obt = psize;
}
if (err < 0) { if (err < 0) {
alsa_logerr2(err, typ, "Failed to set period time to %" PRId32 "\n", alsa_logerr2 (err, typ, "Failed to set period %s to %d\n",
apdo->period_length); size_in_usec ? "time" : "size", req->period_size);
goto err; goto err;
} }
if (apdo->has_period_length && ptime != apdo->period_length) { if (((req->override_mask & 1) && (obt - req->period_size)))
dolog("Requested period time %" PRId32 " was rejected, using %d\n", dolog ("Requested period %s %u was rejected, using %lu\n",
apdo->period_length, ptime); size_in_usec ? "time" : "size", req->period_size, obt);
}
} }
err = snd_pcm_hw_params (handle, hw_params); err = snd_pcm_hw_params (handle, hw_params);
@@ -575,12 +633,30 @@ static int alsa_open(bool in, struct alsa_params_req *req,
goto err; goto err;
} }
if (!in && aopts->has_threshold && aopts->threshold) { if (!in && conf->threshold) {
struct audsettings as = { .freq = freq }; snd_pcm_uframes_t threshold;
alsa_set_threshold( int bytes_per_sec;
handle,
audio_buffer_frames(qapi_AudiodevAlsaPerDirectionOptions_base(apdo), bytes_per_sec = freq << (nchannels == 2);
&as, aopts->threshold));
switch (obt->fmt) {
case AUD_FMT_S8:
case AUD_FMT_U8:
break;
case AUD_FMT_S16:
case AUD_FMT_U16:
bytes_per_sec <<= 1;
break;
case AUD_FMT_S32:
case AUD_FMT_U32:
bytes_per_sec <<= 2;
break;
}
threshold = (conf->threshold * bytes_per_sec) / 1000;
alsa_set_threshold (handle, threshold);
} }
obt->nchannels = nchannels; obt->nchannels = nchannels;
@@ -593,11 +669,11 @@ static int alsa_open(bool in, struct alsa_params_req *req,
obt->nchannels != req->nchannels || obt->nchannels != req->nchannels ||
obt->freq != req->freq) { obt->freq != req->freq) {
dolog ("Audio parameters for %s\n", typ); dolog ("Audio parameters for %s\n", typ);
alsa_dump_info(req, obt, obtfmt, apdo); alsa_dump_info (req, obt, obtfmt);
} }
#ifdef DEBUG #ifdef DEBUG
alsa_dump_info(req, obt, obtfmt, pdo); alsa_dump_info (req, obt, obtfmt);
#endif #endif
return 0; return 0;
@@ -723,13 +799,19 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
struct alsa_params_obt obt; struct alsa_params_obt obt;
snd_pcm_t *handle; snd_pcm_t *handle;
struct audsettings obt_as; struct audsettings obt_as;
Audiodev *dev = drv_opaque; ALSAConf *conf = drv_opaque;
req.fmt = aud_to_alsafmt (as->fmt, as->endianness); req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
req.freq = as->freq; req.freq = as->freq;
req.nchannels = as->nchannels; req.nchannels = as->nchannels;
req.period_size = conf->period_size_out;
req.buffer_size = conf->buffer_size_out;
req.size_in_usec = conf->size_in_usec_out;
req.override_mask =
(conf->period_size_out_overridden ? 1 : 0) |
(conf->buffer_size_out_overridden ? 2 : 0);
if (alsa_open(0, &req, &obt, &handle, dev)) { if (alsa_open (0, &req, &obt, &handle, conf)) {
return -1; return -1;
} }
@@ -750,7 +832,7 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
} }
alsa->handle = handle; alsa->handle = handle;
alsa->dev = dev; alsa->pollhlp.conf = conf;
return 0; return 0;
} }
@@ -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, ...) static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
{ {
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.out;
switch (cmd) { switch (cmd) {
case VOICE_ENABLE: case VOICE_ENABLE:
{ {
bool poll_mode = apdo->try_poll; va_list ap;
int poll_mode;
va_start (ap, cmd);
poll_mode = va_arg (ap, int);
va_end (ap);
ldebug ("enabling voice\n"); ldebug ("enabling voice\n");
if (poll_mode && alsa_poll_out (hw)) { if (poll_mode && alsa_poll_out (hw)) {
@@ -824,13 +910,19 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
struct alsa_params_obt obt; struct alsa_params_obt obt;
snd_pcm_t *handle; snd_pcm_t *handle;
struct audsettings obt_as; struct audsettings obt_as;
Audiodev *dev = drv_opaque; ALSAConf *conf = drv_opaque;
req.fmt = aud_to_alsafmt (as->fmt, as->endianness); req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
req.freq = as->freq; req.freq = as->freq;
req.nchannels = as->nchannels; req.nchannels = as->nchannels;
req.period_size = conf->period_size_in;
req.buffer_size = conf->buffer_size_in;
req.size_in_usec = conf->size_in_usec_in;
req.override_mask =
(conf->period_size_in_overridden ? 1 : 0) |
(conf->buffer_size_in_overridden ? 2 : 0);
if (alsa_open(1, &req, &obt, &handle, dev)) { if (alsa_open (1, &req, &obt, &handle, conf)) {
return -1; return -1;
} }
@@ -851,7 +943,7 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
} }
alsa->handle = handle; alsa->handle = handle;
alsa->dev = dev; alsa->pollhlp.conf = conf;
return 0; return 0;
} }
@@ -993,12 +1085,16 @@ static int alsa_read (SWVoiceIn *sw, void *buf, int size)
static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
{ {
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.in;
switch (cmd) { switch (cmd) {
case VOICE_ENABLE: case VOICE_ENABLE:
{ {
bool poll_mode = apdo->try_poll; va_list ap;
int poll_mode;
va_start (ap, cmd);
poll_mode = va_arg (ap, int);
va_end (ap);
ldebug ("enabling voice\n"); ldebug ("enabling voice\n");
if (poll_mode && alsa_poll_in (hw)) { if (poll_mode && alsa_poll_in (hw)) {
@@ -1021,54 +1117,88 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
return -1; return -1;
} }
static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo) static ALSAConf glob_conf = {
.buffer_size_out = 4096,
.period_size_out = 1024,
.pcm_name_out = "default",
.pcm_name_in = "default",
};
static void *alsa_audio_init (void)
{ {
if (!apdo->has_try_poll) { ALSAConf *conf = g_malloc(sizeof(ALSAConf));
apdo->try_poll = true; *conf = glob_conf;
apdo->has_try_poll = true; return conf;
}
}
static void *alsa_audio_init(Audiodev *dev)
{
AudiodevAlsaOptions *aopts;
assert(dev->driver == AUDIODEV_DRIVER_ALSA);
aopts = &dev->u.alsa;
alsa_init_per_direction(aopts->in);
alsa_init_per_direction(aopts->out);
/*
* need to define them, as otherwise alsa produces no sound
* doesn't set has_* so alsa_open can identify it wasn't set by the user
*/
if (!dev->u.alsa.out->has_period_length) {
/* 1024 frames assuming 44100Hz */
dev->u.alsa.out->period_length = 1024 * 1000000 / 44100;
}
if (!dev->u.alsa.out->has_buffer_length) {
/* 4096 frames assuming 44100Hz */
dev->u.alsa.out->buffer_length = 4096ll * 1000000 / 44100;
}
/*
* OptsVisitor sets unspecified optional fields to zero, but do not depend
* on it...
*/
if (!dev->u.alsa.in->has_period_length) {
dev->u.alsa.in->period_length = 0;
}
if (!dev->u.alsa.in->has_buffer_length) {
dev->u.alsa.in->buffer_length = 0;
}
return dev;
} }
static void alsa_audio_fini (void *opaque) static void alsa_audio_fini (void *opaque)
{ {
g_free(opaque);
} }
static struct audio_option alsa_options[] = {
{
.name = "DAC_SIZE_IN_USEC",
.tag = AUD_OPT_BOOL,
.valp = &glob_conf.size_in_usec_out,
.descr = "DAC period/buffer size in microseconds (otherwise in frames)"
},
{
.name = "DAC_PERIOD_SIZE",
.tag = AUD_OPT_INT,
.valp = &glob_conf.period_size_out,
.descr = "DAC period size (0 to go with system default)",
.overriddenp = &glob_conf.period_size_out_overridden
},
{
.name = "DAC_BUFFER_SIZE",
.tag = AUD_OPT_INT,
.valp = &glob_conf.buffer_size_out,
.descr = "DAC buffer size (0 to go with system default)",
.overriddenp = &glob_conf.buffer_size_out_overridden
},
{
.name = "ADC_SIZE_IN_USEC",
.tag = AUD_OPT_BOOL,
.valp = &glob_conf.size_in_usec_in,
.descr =
"ADC period/buffer size in microseconds (otherwise in frames)"
},
{
.name = "ADC_PERIOD_SIZE",
.tag = AUD_OPT_INT,
.valp = &glob_conf.period_size_in,
.descr = "ADC period size (0 to go with system default)",
.overriddenp = &glob_conf.period_size_in_overridden
},
{
.name = "ADC_BUFFER_SIZE",
.tag = AUD_OPT_INT,
.valp = &glob_conf.buffer_size_in,
.descr = "ADC buffer size (0 to go with system default)",
.overriddenp = &glob_conf.buffer_size_in_overridden
},
{
.name = "THRESHOLD",
.tag = AUD_OPT_INT,
.valp = &glob_conf.threshold,
.descr = "(undocumented)"
},
{
.name = "DAC_DEV",
.tag = AUD_OPT_STR,
.valp = &glob_conf.pcm_name_out,
.descr = "DAC device name (for instance dmix)"
},
{
.name = "ADC_DEV",
.tag = AUD_OPT_STR,
.valp = &glob_conf.pcm_name_in,
.descr = "ADC device name"
},
{ /* End of list */ }
};
static struct audio_pcm_ops alsa_pcm_ops = { static struct audio_pcm_ops alsa_pcm_ops = {
.init_out = alsa_init_out, .init_out = alsa_init_out,
.fini_out = alsa_fini_out, .fini_out = alsa_fini_out,
@@ -1086,6 +1216,7 @@ static struct audio_pcm_ops alsa_pcm_ops = {
static struct audio_driver alsa_audio_driver = { static struct audio_driver alsa_audio_driver = {
.name = "alsa", .name = "alsa",
.descr = "ALSA http://www.alsa-project.org", .descr = "ALSA http://www.alsa-project.org",
.options = alsa_options,
.init = alsa_audio_init, .init = alsa_audio_init,
.fini = alsa_audio_fini, .fini = alsa_audio_fini,
.pcm_ops = &alsa_pcm_ops, .pcm_ops = &alsa_pcm_ops,

File diff suppressed because it is too large Load Diff

View File

@@ -26,31 +26,30 @@
#define QEMU_AUDIO_H #define QEMU_AUDIO_H
#include "qemu/queue.h" #include "qemu/queue.h"
#include "qapi/qapi-types-audio.h"
typedef void (*audio_callback_fn) (void *opaque, int avail); typedef void (*audio_callback_fn) (void *opaque, int avail);
typedef enum {
AUD_FMT_U8,
AUD_FMT_S8,
AUD_FMT_U16,
AUD_FMT_S16,
AUD_FMT_U32,
AUD_FMT_S32
} audfmt_e;
#ifdef HOST_WORDS_BIGENDIAN #ifdef HOST_WORDS_BIGENDIAN
#define AUDIO_HOST_ENDIANNESS 1 #define AUDIO_HOST_ENDIANNESS 1
#else #else
#define AUDIO_HOST_ENDIANNESS 0 #define AUDIO_HOST_ENDIANNESS 0
#endif #endif
typedef struct audsettings { struct audsettings {
int freq; int freq;
int nchannels; int nchannels;
AudioFormat fmt; audfmt_e fmt;
int endianness; int endianness;
} audsettings; };
audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo);
int audioformat_bytes_per_sample(AudioFormat fmt);
int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
typedef enum { typedef enum {
AUD_CNOTIFY_ENABLE, AUD_CNOTIFY_ENABLE,
@@ -90,6 +89,7 @@ typedef struct QEMUAudioTimeStamp {
void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0);
void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3); void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
void AUD_help (void);
void AUD_register_card (const char *name, QEMUSoundCard *card); void AUD_register_card (const char *name, QEMUSoundCard *card);
void AUD_remove_card (QEMUSoundCard *card); void AUD_remove_card (QEMUSoundCard *card);
CaptureVoiceOut *AUD_add_capture ( CaptureVoiceOut *AUD_add_capture (
@@ -171,8 +171,4 @@ void audio_sample_to_uint64(void *samples, int pos,
void audio_sample_from_uint64(void *samples, int pos, void audio_sample_from_uint64(void *samples, int pos,
uint64_t left, uint64_t right); uint64_t left, uint64_t right);
void audio_parse_option(const char *opt);
void audio_init_audiodevs(void);
void audio_legacy_help(void);
#endif /* QEMU_AUDIO_H */ #endif /* QEMU_AUDIO_H */

View File

@@ -33,6 +33,22 @@
struct audio_pcm_ops; struct audio_pcm_ops;
typedef enum {
AUD_OPT_INT,
AUD_OPT_FMT,
AUD_OPT_STR,
AUD_OPT_BOOL
} audio_option_tag_e;
struct audio_option {
const char *name;
audio_option_tag_e tag;
void *valp;
const char *descr;
int *overriddenp;
int overridden;
};
struct audio_callback { struct audio_callback {
void *opaque; void *opaque;
audio_callback_fn fn; audio_callback_fn fn;
@@ -129,7 +145,8 @@ typedef struct audio_driver audio_driver;
struct audio_driver { struct audio_driver {
const char *name; const char *name;
const char *descr; const char *descr;
void *(*init) (Audiodev *); struct audio_option *options;
void *(*init) (void);
void (*fini) (void *); void (*fini) (void *);
struct audio_pcm_ops *pcm_ops; struct audio_pcm_ops *pcm_ops;
int can_be_default; int can_be_default;
@@ -174,9 +191,8 @@ struct SWVoiceCap {
QLIST_ENTRY (SWVoiceCap) entries; QLIST_ENTRY (SWVoiceCap) entries;
}; };
typedef struct AudioState { struct AudioState {
struct audio_driver *drv; struct audio_driver *drv;
Audiodev *dev;
void *drv_opaque; void *drv_opaque;
QEMUTimer *ts; QEMUTimer *ts;
@@ -187,13 +203,10 @@ typedef struct AudioState {
int nb_hw_voices_out; int nb_hw_voices_out;
int nb_hw_voices_in; int nb_hw_voices_in;
int vm_running; int vm_running;
int64_t period_ticks; };
} AudioState;
extern const struct mixeng_volume nominal_volume; extern const struct mixeng_volume nominal_volume;
extern const char *audio_prio_list[];
void audio_driver_register(audio_driver *drv); void audio_driver_register(audio_driver *drv);
audio_driver *audio_driver_lookup(const char *name); audio_driver *audio_driver_lookup(const char *name);
@@ -235,18 +248,4 @@ static inline int audio_ring_dist (int dst, int src, int len)
#define AUDIO_STRINGIFY_(n) #n #define AUDIO_STRINGIFY_(n) #n
#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n) #define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n)
typedef struct AudiodevListEntry {
Audiodev *dev;
QSIMPLEQ_ENTRY(AudiodevListEntry) next;
} AudiodevListEntry;
typedef QSIMPLEQ_HEAD(, AudiodevListEntry) AudiodevListHead;
AudiodevListHead audio_handle_legacy_opts(void);
void audio_free_audiodev_list(AudiodevListHead *head);
void audio_create_pdos(Audiodev *dev);
AudiodevPerDirectionOptions *audio_get_pdo_in(Audiodev *dev);
AudiodevPerDirectionOptions *audio_get_pdo_out(Audiodev *dev);
#endif /* QEMU_AUDIO_INT_H */ #endif /* QEMU_AUDIO_INT_H */

View File

@@ -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);
}

View File

@@ -1,4 +1,5 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "audio.h" #include "audio.h"
#define AUDIO_CAP "audio-pt" #define AUDIO_CAP "audio-pt"

View File

@@ -299,42 +299,11 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
return NULL; return NULL;
} }
AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
{
switch (dev->driver) {
case AUDIODEV_DRIVER_NONE:
return dev->u.none.TYPE;
case AUDIODEV_DRIVER_ALSA:
return qapi_AudiodevAlsaPerDirectionOptions_base(dev->u.alsa.TYPE);
case AUDIODEV_DRIVER_COREAUDIO:
return qapi_AudiodevCoreaudioPerDirectionOptions_base(
dev->u.coreaudio.TYPE);
case AUDIODEV_DRIVER_DSOUND:
return dev->u.dsound.TYPE;
case AUDIODEV_DRIVER_OSS:
return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.TYPE);
case AUDIODEV_DRIVER_PA:
return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE);
case AUDIODEV_DRIVER_SDL:
return dev->u.sdl.TYPE;
case AUDIODEV_DRIVER_SPICE:
return dev->u.spice.TYPE;
case AUDIODEV_DRIVER_WAV:
return dev->u.wav.TYPE;
case AUDIODEV_DRIVER__MAX:
break;
}
abort();
}
static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as) static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as)
{ {
HW *hw; HW *hw;
AudioState *s = &glob_audio_state;
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
if (pdo->fixed_settings) { if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) {
hw = glue (audio_pcm_hw_add_new_, TYPE) (as); hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
if (hw) { if (hw) {
return hw; return hw;
@@ -362,11 +331,9 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
SW *sw; SW *sw;
HW *hw; HW *hw;
struct audsettings hw_as; struct audsettings hw_as;
AudioState *s = &glob_audio_state;
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
if (pdo->fixed_settings) { if (glue (conf.fixed_, TYPE).enabled) {
hw_as = audiodev_to_audsettings(pdo); hw_as = glue (conf.fixed_, TYPE).settings;
} }
else { else {
hw_as = *as; hw_as = *as;
@@ -431,7 +398,6 @@ SW *glue (AUD_open_, TYPE) (
) )
{ {
AudioState *s = &glob_audio_state; AudioState *s = &glob_audio_state;
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
if (audio_bug(__func__, !card || !name || !callback_fn || !as)) { if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
dolog ("card=%p name=%p callback_fn=%p as=%p\n", dolog ("card=%p name=%p callback_fn=%p as=%p\n",
@@ -456,7 +422,7 @@ SW *glue (AUD_open_, TYPE) (
return sw; return sw;
} }
if (!pdo->fixed_settings && sw) { if (!glue (conf.fixed_, TYPE).enabled && sw) {
glue (AUD_close_, TYPE) (card, sw); glue (AUD_close_, TYPE) (card, sw);
sw = NULL; sw = NULL;
} }

View File

@@ -24,20 +24,20 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx,
wfx->cbSize = 0; wfx->cbSize = 0;
switch (as->fmt) { switch (as->fmt) {
case AUDIO_FORMAT_S8: case AUD_FMT_S8:
case AUDIO_FORMAT_U8: case AUD_FMT_U8:
wfx->wBitsPerSample = 8; wfx->wBitsPerSample = 8;
break; break;
case AUDIO_FORMAT_S16: case AUD_FMT_S16:
case AUDIO_FORMAT_U16: case AUD_FMT_U16:
wfx->wBitsPerSample = 16; wfx->wBitsPerSample = 16;
wfx->nAvgBytesPerSec <<= 1; wfx->nAvgBytesPerSec <<= 1;
wfx->nBlockAlign <<= 1; wfx->nBlockAlign <<= 1;
break; break;
case AUDIO_FORMAT_S32: case AUD_FMT_S32:
case AUDIO_FORMAT_U32: case AUD_FMT_U32:
wfx->wBitsPerSample = 32; wfx->wBitsPerSample = 32;
wfx->nAvgBytesPerSec <<= 2; wfx->nAvgBytesPerSec <<= 2;
wfx->nBlockAlign <<= 2; wfx->nBlockAlign <<= 2;
@@ -85,15 +85,15 @@ int waveformat_to_audio_settings (WAVEFORMATEX *wfx,
switch (wfx->wBitsPerSample) { switch (wfx->wBitsPerSample) {
case 8: case 8:
as->fmt = AUDIO_FORMAT_U8; as->fmt = AUD_FMT_U8;
break; break;
case 16: case 16:
as->fmt = AUDIO_FORMAT_S16; as->fmt = AUD_FMT_S16;
break; break;
case 32: case 32:
as->fmt = AUDIO_FORMAT_S32; as->fmt = AUD_FMT_S32;
break; break;
default: default:

View File

@@ -26,7 +26,7 @@
#include <CoreAudio/CoreAudio.h> #include <CoreAudio/CoreAudio.h>
#include <pthread.h> /* pthread_X */ #include <pthread.h> /* pthread_X */
#include "qemu/module.h" #include "qemu-common.h"
#include "audio.h" #include "audio.h"
#define AUDIO_CAP "coreaudio" #define AUDIO_CAP "coreaudio"
@@ -36,6 +36,11 @@
#define MAC_OS_X_VERSION_10_6 1060 #define MAC_OS_X_VERSION_10_6 1060
#endif #endif
typedef struct {
int buffer_frames;
int nbuffers;
} CoreaudioConf;
typedef struct coreaudioVoiceOut { typedef struct coreaudioVoiceOut {
HWVoiceOut hw; HWVoiceOut hw;
pthread_mutex_t mutex; pthread_mutex_t mutex;
@@ -502,9 +507,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
int err; int err;
const char *typ = "playback"; const char *typ = "playback";
AudioValueRange frameRange; AudioValueRange frameRange;
Audiodev *dev = drv_opaque; CoreaudioConf *conf = drv_opaque;
AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
int frames;
/* create mutex */ /* create mutex */
err = pthread_mutex_init(&core->mutex, NULL); err = pthread_mutex_init(&core->mutex, NULL);
@@ -535,17 +538,16 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
return -1; return -1;
} }
frames = audio_buffer_frames( if (frameRange.mMinimum > conf->buffer_frames) {
qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
if (frameRange.mMinimum > frames) {
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum); dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
} else if (frameRange.mMaximum < frames) { }
else if (frameRange.mMaximum < conf->buffer_frames) {
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
} }
else { else {
core->audioDevicePropertyBufferFrameSize = frames; core->audioDevicePropertyBufferFrameSize = conf->buffer_frames;
} }
/* set Buffer Frame Size */ /* set Buffer Frame Size */
@@ -566,8 +568,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
"Could not get device buffer frame size\n"); "Could not get device buffer frame size\n");
return -1; return -1;
} }
hw->samples = (cpdo->has_buffer_count ? cpdo->buffer_count : 4) * hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
core->audioDevicePropertyBufferFrameSize;
/* get StreamFormat */ /* get StreamFormat */
status = coreaudio_get_streamformat(core->outputDeviceID, status = coreaudio_get_streamformat(core->outputDeviceID,
@@ -679,15 +680,40 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
return 0; return 0;
} }
static void *coreaudio_audio_init(Audiodev *dev) static CoreaudioConf glob_conf = {
.buffer_frames = 512,
.nbuffers = 4,
};
static void *coreaudio_audio_init (void)
{ {
return dev; CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf));
*conf = glob_conf;
return conf;
} }
static void coreaudio_audio_fini (void *opaque) static void coreaudio_audio_fini (void *opaque)
{ {
g_free(opaque);
} }
static struct audio_option coreaudio_options[] = {
{
.name = "BUFFER_SIZE",
.tag = AUD_OPT_INT,
.valp = &glob_conf.buffer_frames,
.descr = "Size of the buffer in frames"
},
{
.name = "BUFFER_COUNT",
.tag = AUD_OPT_INT,
.valp = &glob_conf.nbuffers,
.descr = "Number of buffers"
},
{ /* End of list */ }
};
static struct audio_pcm_ops coreaudio_pcm_ops = { static struct audio_pcm_ops coreaudio_pcm_ops = {
.init_out = coreaudio_init_out, .init_out = coreaudio_init_out,
.fini_out = coreaudio_fini_out, .fini_out = coreaudio_fini_out,
@@ -699,6 +725,7 @@ static struct audio_pcm_ops coreaudio_pcm_ops = {
static struct audio_driver coreaudio_audio_driver = { static struct audio_driver coreaudio_audio_driver = {
.name = "coreaudio", .name = "coreaudio",
.descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html", .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
.options = coreaudio_options,
.init = coreaudio_audio_init, .init = coreaudio_audio_init,
.fini = coreaudio_audio_fini, .fini = coreaudio_audio_fini,
.pcm_ops = &coreaudio_pcm_ops, .pcm_ops = &coreaudio_pcm_ops,

View File

@@ -167,18 +167,17 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
dsound *s = drv_opaque; dsound *s = drv_opaque;
WAVEFORMATEX wfx; WAVEFORMATEX wfx;
struct audsettings obt_as; struct audsettings obt_as;
DSoundConf *conf = &s->conf;
#ifdef DSBTYPE_IN #ifdef DSBTYPE_IN
const char *typ = "ADC"; const char *typ = "ADC";
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
DSCBUFFERDESC bd; DSCBUFFERDESC bd;
DSCBCAPS bc; DSCBCAPS bc;
AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.in;
#else #else
const char *typ = "DAC"; const char *typ = "DAC";
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
DSBUFFERDESC bd; DSBUFFERDESC bd;
DSBCAPS bc; DSBCAPS bc;
AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.out;
#endif #endif
if (!s->FIELD2) { if (!s->FIELD2) {
@@ -194,8 +193,8 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
memset (&bd, 0, sizeof (bd)); memset (&bd, 0, sizeof (bd));
bd.dwSize = sizeof (bd); bd.dwSize = sizeof (bd);
bd.lpwfxFormat = &wfx; bd.lpwfxFormat = &wfx;
bd.dwBufferBytes = audio_buffer_bytes(pdo, as, 92880);
#ifdef DSBTYPE_IN #ifdef DSBTYPE_IN
bd.dwBufferBytes = conf->bufsize_in;
hr = IDirectSoundCapture_CreateCaptureBuffer ( hr = IDirectSoundCapture_CreateCaptureBuffer (
s->dsound_capture, s->dsound_capture,
&bd, &bd,
@@ -204,6 +203,7 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
); );
#else #else
bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
bd.dwBufferBytes = conf->bufsize_out;
hr = IDirectSound_CreateSoundBuffer ( hr = IDirectSound_CreateSoundBuffer (
s->dsound, s->dsound,
&bd, &bd,

View File

@@ -27,12 +27,11 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "audio.h" #include "audio.h"
#define AUDIO_CAP "dsound" #define AUDIO_CAP "dsound"
#include "audio_int.h" #include "audio_int.h"
#include "qemu/host-utils.h"
#include "qemu/module.h"
#include <windows.h> #include <windows.h>
#include <mmsystem.h> #include <mmsystem.h>
@@ -43,11 +42,17 @@
/* #define DEBUG_DSOUND */ /* #define DEBUG_DSOUND */
typedef struct {
int bufsize_in;
int bufsize_out;
int latency_millis;
} DSoundConf;
typedef struct { typedef struct {
LPDIRECTSOUND dsound; LPDIRECTSOUND dsound;
LPDIRECTSOUNDCAPTURE dsound_capture; LPDIRECTSOUNDCAPTURE dsound_capture;
struct audsettings settings; struct audsettings settings;
Audiodev *dev; DSoundConf conf;
} dsound; } dsound;
typedef struct { typedef struct {
@@ -243,9 +248,9 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
dsound_log_hresult (hr); dsound_log_hresult (hr);
} }
static uint64_t usecs_to_bytes(struct audio_pcm_info *info, uint32_t usecs) static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis)
{ {
return muldiv64(usecs, info->bytes_per_second, 1000000); return (millis * info->bytes_per_second) / 1000;
} }
#ifdef DEBUG_DSOUND #ifdef DEBUG_DSOUND
@@ -473,7 +478,7 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
LPVOID p1, p2; LPVOID p1, p2;
int bufsize; int bufsize;
dsound *s = ds->s; dsound *s = ds->s;
AudiodevDsoundOptions *dso = &s->dev->u.dsound; DSoundConf *conf = &s->conf;
if (!dsb) { if (!dsb) {
dolog ("Attempt to run empty with playback buffer\n"); dolog ("Attempt to run empty with playback buffer\n");
@@ -496,14 +501,14 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
len = live << hwshift; len = live << hwshift;
if (ds->first_time) { if (ds->first_time) {
if (dso->latency) { if (conf->latency_millis) {
DWORD cur_blat; DWORD cur_blat;
cur_blat = audio_ring_dist (wpos, ppos, bufsize); cur_blat = audio_ring_dist (wpos, ppos, bufsize);
ds->first_time = 0; ds->first_time = 0;
old_pos = wpos; old_pos = wpos;
old_pos += old_pos +=
usecs_to_bytes(&hw->info, dso->latency) - cur_blat; millis_to_bytes (&hw->info, conf->latency_millis) - cur_blat;
old_pos %= bufsize; old_pos %= bufsize;
old_pos &= ~hw->info.align; old_pos &= ~hw->info.align;
} }
@@ -742,6 +747,12 @@ static int dsound_run_in (HWVoiceIn *hw)
return decr; return decr;
} }
static DSoundConf glob_conf = {
.bufsize_in = 16384,
.bufsize_out = 16384,
.latency_millis = 10
};
static void dsound_audio_fini (void *opaque) static void dsound_audio_fini (void *opaque)
{ {
HRESULT hr; HRESULT hr;
@@ -772,22 +783,13 @@ static void dsound_audio_fini (void *opaque)
g_free(s); g_free(s);
} }
static void *dsound_audio_init(Audiodev *dev) static void *dsound_audio_init (void)
{ {
int err; int err;
HRESULT hr; HRESULT hr;
dsound *s = g_malloc0(sizeof(dsound)); dsound *s = g_malloc0(sizeof(dsound));
AudiodevDsoundOptions *dso;
assert(dev->driver == AUDIODEV_DRIVER_DSOUND);
s->dev = dev;
dso = &dev->u.dsound;
if (!dso->has_latency) {
dso->has_latency = true;
dso->latency = 10000; /* 10 ms */
}
s->conf = glob_conf;
hr = CoInitialize (NULL); hr = CoInitialize (NULL);
if (FAILED (hr)) { if (FAILED (hr)) {
dsound_logerr (hr, "Could not initialize COM\n"); dsound_logerr (hr, "Could not initialize COM\n");
@@ -852,6 +854,28 @@ static void *dsound_audio_init(Audiodev *dev)
return s; return s;
} }
static struct audio_option dsound_options[] = {
{
.name = "LATENCY_MILLIS",
.tag = AUD_OPT_INT,
.valp = &glob_conf.latency_millis,
.descr = "(undocumented)"
},
{
.name = "BUFSIZE_OUT",
.tag = AUD_OPT_INT,
.valp = &glob_conf.bufsize_out,
.descr = "(undocumented)"
},
{
.name = "BUFSIZE_IN",
.tag = AUD_OPT_INT,
.valp = &glob_conf.bufsize_in,
.descr = "(undocumented)"
},
{ /* End of list */ }
};
static struct audio_pcm_ops dsound_pcm_ops = { static struct audio_pcm_ops dsound_pcm_ops = {
.init_out = dsound_init_out, .init_out = dsound_init_out,
.fini_out = dsound_fini_out, .fini_out = dsound_fini_out,
@@ -869,6 +893,7 @@ static struct audio_pcm_ops dsound_pcm_ops = {
static struct audio_driver dsound_audio_driver = { static struct audio_driver dsound_audio_driver = {
.name = "dsound", .name = "dsound",
.descr = "DirectSound http://wikipedia.org/wiki/DirectSound", .descr = "DirectSound http://wikipedia.org/wiki/DirectSound",
.options = dsound_options,
.init = dsound_audio_init, .init = dsound_audio_init,
.fini = dsound_audio_fini, .fini = dsound_audio_fini,
.pcm_ops = &dsound_pcm_ops, .pcm_ops = &dsound_pcm_ops,

View File

@@ -23,6 +23,7 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/bswap.h" #include "qemu/bswap.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "audio.h" #include "audio.h"

View File

@@ -21,10 +21,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/host-utils.h" #include "qemu/host-utils.h"
#include "qemu/module.h"
#include "audio.h" #include "audio.h"
#include "qemu/timer.h" #include "qemu/timer.h"
@@ -137,7 +136,7 @@ static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
return 0; return 0;
} }
static void *no_audio_init(Audiodev *dev) static void *no_audio_init (void)
{ {
return &no_audio_init; return &no_audio_init;
} }
@@ -164,6 +163,7 @@ static struct audio_pcm_ops no_pcm_ops = {
static struct audio_driver no_audio_driver = { static struct audio_driver no_audio_driver = {
.name = "none", .name = "none",
.descr = "Timer based audio emulation", .descr = "Timer based audio emulation",
.options = NULL,
.init = no_audio_init, .init = no_audio_init,
.fini = no_audio_fini, .fini = no_audio_fini,
.pcm_ops = &no_pcm_ops, .pcm_ops = &no_pcm_ops,

View File

@@ -21,12 +21,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/soundcard.h> #include <sys/soundcard.h>
#include "qemu-common.h"
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qemu/host-utils.h" #include "qemu/host-utils.h"
#include "audio.h" #include "audio.h"
#include "trace.h" #include "trace.h"
@@ -38,6 +37,16 @@
#define USE_DSP_POLICY #define USE_DSP_POLICY
#endif #endif
typedef struct OSSConf {
int try_mmap;
int nfrags;
int fragsize;
const char *devpath_out;
const char *devpath_in;
int exclusive;
int policy;
} OSSConf;
typedef struct OSSVoiceOut { typedef struct OSSVoiceOut {
HWVoiceOut hw; HWVoiceOut hw;
void *pcm_buf; void *pcm_buf;
@@ -47,7 +56,7 @@ typedef struct OSSVoiceOut {
int fragsize; int fragsize;
int mmapped; int mmapped;
int pending; int pending;
Audiodev *dev; OSSConf *conf;
} OSSVoiceOut; } OSSVoiceOut;
typedef struct OSSVoiceIn { typedef struct OSSVoiceIn {
@@ -56,12 +65,12 @@ typedef struct OSSVoiceIn {
int fd; int fd;
int nfrags; int nfrags;
int fragsize; int fragsize;
Audiodev *dev; OSSConf *conf;
} OSSVoiceIn; } OSSVoiceIn;
struct oss_params { struct oss_params {
int freq; int freq;
int fmt; audfmt_e fmt;
int nchannels; int nchannels;
int nfrags; int nfrags;
int fragsize; int fragsize;
@@ -139,16 +148,16 @@ static int oss_write (SWVoiceOut *sw, void *buf, int len)
return audio_pcm_sw_write (sw, buf, len); return audio_pcm_sw_write (sw, buf, len);
} }
static int aud_to_ossfmt (AudioFormat fmt, int endianness) static int aud_to_ossfmt (audfmt_e fmt, int endianness)
{ {
switch (fmt) { switch (fmt) {
case AUDIO_FORMAT_S8: case AUD_FMT_S8:
return AFMT_S8; return AFMT_S8;
case AUDIO_FORMAT_U8: case AUD_FMT_U8:
return AFMT_U8; return AFMT_U8;
case AUDIO_FORMAT_S16: case AUD_FMT_S16:
if (endianness) { if (endianness) {
return AFMT_S16_BE; return AFMT_S16_BE;
} }
@@ -156,7 +165,7 @@ static int aud_to_ossfmt (AudioFormat fmt, int endianness)
return AFMT_S16_LE; return AFMT_S16_LE;
} }
case AUDIO_FORMAT_U16: case AUD_FMT_U16:
if (endianness) { if (endianness) {
return AFMT_U16_BE; return AFMT_U16_BE;
} }
@@ -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) { switch (ossfmt) {
case AFMT_S8: case AFMT_S8:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_S8; *fmt = AUD_FMT_S8;
break; break;
case AFMT_U8: case AFMT_U8:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_U8; *fmt = AUD_FMT_U8;
break; break;
case AFMT_S16_LE: case AFMT_S16_LE:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_S16; *fmt = AUD_FMT_S16;
break; break;
case AFMT_U16_LE: case AFMT_U16_LE:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_U16; *fmt = AUD_FMT_U16;
break; break;
case AFMT_S16_BE: case AFMT_S16_BE:
*endianness = 1; *endianness = 1;
*fmt = AUDIO_FORMAT_S16; *fmt = AUD_FMT_S16;
break; break;
case AFMT_U16_BE: case AFMT_U16_BE:
*endianness = 1; *endianness = 1;
*fmt = AUDIO_FORMAT_U16; *fmt = AUD_FMT_U16;
break; break;
default: default:
@@ -253,25 +262,19 @@ static int oss_get_version (int fd, int *version, const char *typ)
} }
#endif #endif
static int oss_open(int in, struct oss_params *req, audsettings *as, static int oss_open (int in, struct oss_params *req,
struct oss_params *obt, int *pfd, Audiodev *dev) struct oss_params *obt, int *pfd, OSSConf* conf)
{ {
AudiodevOssOptions *oopts = &dev->u.oss;
AudiodevOssPerDirectionOptions *opdo = in ? oopts->in : oopts->out;
int fd; int fd;
int oflags = (oopts->has_exclusive && oopts->exclusive) ? O_EXCL : 0; int oflags = conf->exclusive ? O_EXCL : 0;
audio_buf_info abinfo; audio_buf_info abinfo;
int fmt, freq, nchannels; int fmt, freq, nchannels;
int setfragment = 1; int setfragment = 1;
const char *dspname = opdo->has_dev ? opdo->dev : "/dev/dsp"; const char *dspname = in ? conf->devpath_in : conf->devpath_out;
const char *typ = in ? "ADC" : "DAC"; const char *typ = in ? "ADC" : "DAC";
#ifdef USE_DSP_POLICY
int policy = oopts->has_dsp_policy ? oopts->dsp_policy : 5;
#endif
/* Kludge needed to have working mmap on Linux */ /* Kludge needed to have working mmap on Linux */
oflags |= (oopts->has_try_mmap && oopts->try_mmap) ? oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
O_RDWR : (in ? O_RDONLY : O_WRONLY);
fd = open (dspname, oflags | O_NONBLOCK); fd = open (dspname, oflags | O_NONBLOCK);
if (-1 == fd) { if (-1 == fd) {
@@ -282,9 +285,6 @@ static int oss_open(int in, struct oss_params *req, audsettings *as,
freq = req->freq; freq = req->freq;
nchannels = req->nchannels; nchannels = req->nchannels;
fmt = req->fmt; fmt = req->fmt;
req->nfrags = opdo->has_buffer_count ? opdo->buffer_count : 4;
req->fragsize = audio_buffer_bytes(
qapi_AudiodevOssPerDirectionOptions_base(opdo), as, 23220);
if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) { if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt); oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
@@ -308,18 +308,18 @@ static int oss_open(int in, struct oss_params *req, audsettings *as,
} }
#ifdef USE_DSP_POLICY #ifdef USE_DSP_POLICY
if (policy >= 0) { if (conf->policy >= 0) {
int version; int version;
if (!oss_get_version (fd, &version, typ)) { if (!oss_get_version (fd, &version, typ)) {
trace_oss_version(version); trace_oss_version(version);
if (version >= 0x040000) { if (version >= 0x040000) {
int policy2 = policy; int policy = conf->policy;
if (ioctl(fd, SNDCTL_DSP_POLICY, &policy2)) { if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
oss_logerr2 (errno, typ, oss_logerr2 (errno, typ,
"Failed to set timing policy to %d\n", "Failed to set timing policy to %d\n",
policy); conf->policy);
goto err; goto err;
} }
setfragment = 0; setfragment = 0;
@@ -500,18 +500,19 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
int endianness; int endianness;
int err; int err;
int fd; int fd;
AudioFormat effective_fmt; audfmt_e effective_fmt;
struct audsettings obt_as; struct audsettings obt_as;
Audiodev *dev = drv_opaque; OSSConf *conf = drv_opaque;
AudiodevOssOptions *oopts = &dev->u.oss;
oss->fd = -1; oss->fd = -1;
req.fmt = aud_to_ossfmt (as->fmt, as->endianness); req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
req.freq = as->freq; req.freq = as->freq;
req.nchannels = as->nchannels; req.nchannels = as->nchannels;
req.fragsize = conf->fragsize;
req.nfrags = conf->nfrags;
if (oss_open(0, &req, as, &obt, &fd, dev)) { if (oss_open (0, &req, &obt, &fd, conf)) {
return -1; return -1;
} }
@@ -538,7 +539,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
oss->mmapped = 0; oss->mmapped = 0;
if (oopts->has_try_mmap && oopts->try_mmap) { if (conf->try_mmap) {
oss->pcm_buf = mmap ( oss->pcm_buf = mmap (
NULL, NULL,
hw->samples << hw->info.shift, hw->samples << hw->info.shift,
@@ -596,7 +597,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
} }
oss->fd = fd; oss->fd = fd;
oss->dev = dev; oss->conf = conf;
return 0; return 0;
} }
@@ -604,12 +605,16 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
{ {
int trig; int trig;
OSSVoiceOut *oss = (OSSVoiceOut *) hw; OSSVoiceOut *oss = (OSSVoiceOut *) hw;
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
switch (cmd) { switch (cmd) {
case VOICE_ENABLE: case VOICE_ENABLE:
{ {
bool poll_mode = opdo->try_poll; va_list ap;
int poll_mode;
va_start (ap, cmd);
poll_mode = va_arg (ap, int);
va_end (ap);
ldebug ("enabling voice\n"); ldebug ("enabling voice\n");
if (poll_mode) { if (poll_mode) {
@@ -662,16 +667,18 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
int endianness; int endianness;
int err; int err;
int fd; int fd;
AudioFormat effective_fmt; audfmt_e effective_fmt;
struct audsettings obt_as; struct audsettings obt_as;
Audiodev *dev = drv_opaque; OSSConf *conf = drv_opaque;
oss->fd = -1; oss->fd = -1;
req.fmt = aud_to_ossfmt (as->fmt, as->endianness); req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
req.freq = as->freq; req.freq = as->freq;
req.nchannels = as->nchannels; req.nchannels = as->nchannels;
if (oss_open(1, &req, as, &obt, &fd, dev)) { req.fragsize = conf->fragsize;
req.nfrags = conf->nfrags;
if (oss_open (1, &req, &obt, &fd, conf)) {
return -1; return -1;
} }
@@ -705,7 +712,7 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
} }
oss->fd = fd; oss->fd = fd;
oss->dev = dev; oss->conf = conf;
return 0; return 0;
} }
@@ -796,12 +803,16 @@ static int oss_read (SWVoiceIn *sw, void *buf, int size)
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...) static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
{ {
OSSVoiceIn *oss = (OSSVoiceIn *) hw; OSSVoiceIn *oss = (OSSVoiceIn *) hw;
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
switch (cmd) { switch (cmd) {
case VOICE_ENABLE: case VOICE_ENABLE:
{ {
bool poll_mode = opdo->try_poll; va_list ap;
int poll_mode;
va_start (ap, cmd);
poll_mode = va_arg (ap, int);
va_end (ap);
if (poll_mode) { if (poll_mode) {
oss_poll_in (hw); oss_poll_in (hw);
@@ -821,36 +832,82 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
return 0; return 0;
} }
static void oss_init_per_direction(AudiodevOssPerDirectionOptions *opdo) static OSSConf glob_conf = {
.try_mmap = 0,
.nfrags = 4,
.fragsize = 4096,
.devpath_out = "/dev/dsp",
.devpath_in = "/dev/dsp",
.exclusive = 0,
.policy = 5
};
static void *oss_audio_init (void)
{ {
if (!opdo->has_try_poll) { OSSConf *conf = g_malloc(sizeof(OSSConf));
opdo->try_poll = true; *conf = glob_conf;
opdo->has_try_poll = true;
}
}
static void *oss_audio_init(Audiodev *dev) if (access(conf->devpath_in, R_OK | W_OK) < 0 ||
{ access(conf->devpath_out, R_OK | W_OK) < 0) {
AudiodevOssOptions *oopts; g_free(conf);
assert(dev->driver == AUDIODEV_DRIVER_OSS);
oopts = &dev->u.oss;
oss_init_per_direction(oopts->in);
oss_init_per_direction(oopts->out);
if (access(oopts->in->has_dev ? oopts->in->dev : "/dev/dsp",
R_OK | W_OK) < 0 ||
access(oopts->out->has_dev ? oopts->out->dev : "/dev/dsp",
R_OK | W_OK) < 0) {
return NULL; return NULL;
} }
return dev; return conf;
} }
static void oss_audio_fini (void *opaque) static void oss_audio_fini (void *opaque)
{ {
g_free(opaque);
} }
static struct audio_option oss_options[] = {
{
.name = "FRAGSIZE",
.tag = AUD_OPT_INT,
.valp = &glob_conf.fragsize,
.descr = "Fragment size in bytes"
},
{
.name = "NFRAGS",
.tag = AUD_OPT_INT,
.valp = &glob_conf.nfrags,
.descr = "Number of fragments"
},
{
.name = "MMAP",
.tag = AUD_OPT_BOOL,
.valp = &glob_conf.try_mmap,
.descr = "Try using memory mapped access"
},
{
.name = "DAC_DEV",
.tag = AUD_OPT_STR,
.valp = &glob_conf.devpath_out,
.descr = "Path to DAC device"
},
{
.name = "ADC_DEV",
.tag = AUD_OPT_STR,
.valp = &glob_conf.devpath_in,
.descr = "Path to ADC device"
},
{
.name = "EXCLUSIVE",
.tag = AUD_OPT_BOOL,
.valp = &glob_conf.exclusive,
.descr = "Open device in exclusive mode (vmix won't work)"
},
#ifdef USE_DSP_POLICY
{
.name = "POLICY",
.tag = AUD_OPT_INT,
.valp = &glob_conf.policy,
.descr = "Set the timing policy of the device, -1 to use fragment mode",
},
#endif
{ /* End of list */ }
};
static struct audio_pcm_ops oss_pcm_ops = { static struct audio_pcm_ops oss_pcm_ops = {
.init_out = oss_init_out, .init_out = oss_init_out,
.fini_out = oss_fini_out, .fini_out = oss_fini_out,
@@ -868,6 +925,7 @@ static struct audio_pcm_ops oss_pcm_ops = {
static struct audio_driver oss_audio_driver = { static struct audio_driver oss_audio_driver = {
.name = "oss", .name = "oss",
.descr = "OSS http://www.opensound.com", .descr = "OSS http://www.opensound.com",
.options = oss_options,
.init = oss_audio_init, .init = oss_audio_init,
.fini = oss_audio_fini, .fini = oss_audio_fini,
.pcm_ops = &oss_pcm_ops, .pcm_ops = &oss_pcm_ops,

View File

@@ -1,9 +1,7 @@
/* public domain */ /* public domain */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/module.h" #include "qemu-common.h"
#include "audio.h" #include "audio.h"
#include "qapi/opts-visitor.h"
#include <pulse/pulseaudio.h> #include <pulse/pulseaudio.h>
@@ -12,7 +10,14 @@
#include "audio_pt_int.h" #include "audio_pt_int.h"
typedef struct { typedef struct {
Audiodev *dev; int samples;
char *server;
char *sink;
char *source;
} PAConf;
typedef struct {
PAConf conf;
pa_threaded_mainloop *mainloop; pa_threaded_mainloop *mainloop;
pa_context *context; pa_context *context;
} paaudio; } paaudio;
@@ -27,7 +32,6 @@ typedef struct {
void *pcm_buf; void *pcm_buf;
struct audio_pt pt; struct audio_pt pt;
paaudio *g; paaudio *g;
int samples;
} PAVoiceOut; } PAVoiceOut;
typedef struct { typedef struct {
@@ -42,7 +46,6 @@ typedef struct {
const void *read_data; const void *read_data;
size_t read_index, read_length; size_t read_index, read_length;
paaudio *g; paaudio *g;
int samples;
} PAVoiceIn; } PAVoiceIn;
static void qpa_audio_fini(void *opaque); static void qpa_audio_fini(void *opaque);
@@ -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; rpos = pa->rpos;
if (audio_pt_unlock(&pa->pt, __func__)) { 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; wpos = pa->wpos;
if (audio_pt_unlock(&pa->pt, __func__)) { 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); return audio_pcm_sw_read (sw, buf, len);
} }
static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness) static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
{ {
int format; int format;
switch (afmt) { switch (afmt) {
case AUDIO_FORMAT_S8: case AUD_FMT_S8:
case AUDIO_FORMAT_U8: case AUD_FMT_U8:
format = PA_SAMPLE_U8; format = PA_SAMPLE_U8;
break; break;
case AUDIO_FORMAT_S16: case AUD_FMT_S16:
case AUDIO_FORMAT_U16: case AUD_FMT_U16:
format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
break; break;
case AUDIO_FORMAT_S32: case AUD_FMT_S32:
case AUDIO_FORMAT_U32: case AUD_FMT_U32:
format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
break; break;
default: default:
@@ -407,26 +410,26 @@ static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
return format; return format;
} }
static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness) static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
{ {
switch (fmt) { switch (fmt) {
case PA_SAMPLE_U8: case PA_SAMPLE_U8:
return AUDIO_FORMAT_U8; return AUD_FMT_U8;
case PA_SAMPLE_S16BE: case PA_SAMPLE_S16BE:
*endianness = 1; *endianness = 1;
return AUDIO_FORMAT_S16; return AUD_FMT_S16;
case PA_SAMPLE_S16LE: case PA_SAMPLE_S16LE:
*endianness = 0; *endianness = 0;
return AUDIO_FORMAT_S16; return AUD_FMT_S16;
case PA_SAMPLE_S32BE: case PA_SAMPLE_S32BE:
*endianness = 1; *endianness = 1;
return AUDIO_FORMAT_S32; return AUD_FMT_S32;
case PA_SAMPLE_S32LE: case PA_SAMPLE_S32LE:
*endianness = 0; *endianness = 0;
return AUDIO_FORMAT_S32; return AUD_FMT_S32;
default: default:
dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt); dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
return AUDIO_FORMAT_U8; return AUD_FMT_U8;
} }
} }
@@ -543,15 +546,17 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
struct audsettings obt_as = *as; struct audsettings obt_as = *as;
PAVoiceOut *pa = (PAVoiceOut *) hw; PAVoiceOut *pa = (PAVoiceOut *) hw;
paaudio *g = pa->g = drv_opaque; paaudio *g = pa->g = drv_opaque;
AudiodevPaOptions *popts = &g->dev->u.pa;
AudiodevPaPerDirectionOptions *ppdo = popts->out;
ss.format = audfmt_to_pa (as->fmt, as->endianness); ss.format = audfmt_to_pa (as->fmt, as->endianness);
ss.channels = as->nchannels; ss.channels = as->nchannels;
ss.rate = as->freq; ss.rate = as->freq;
ba.tlength = pa_usec_to_bytes(ppdo->latency, &ss); /*
ba.minreq = -1; * qemu audio tick runs at 100 Hz (by default), so processing
* data chunks worth 10 ms of sound should be a good fit.
*/
ba.tlength = pa_usec_to_bytes (10 * 1000, &ss);
ba.minreq = pa_usec_to_bytes (5 * 1000, &ss);
ba.maxlength = -1; ba.maxlength = -1;
ba.prebuf = -1; ba.prebuf = -1;
@@ -561,7 +566,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
g, g,
"qemu", "qemu",
PA_STREAM_PLAYBACK, PA_STREAM_PLAYBACK,
ppdo->has_name ? ppdo->name : NULL, g->conf.sink,
&ss, &ss,
NULL, /* channel map */ NULL, /* channel map */
&ba, /* buffering attributes */ &ba, /* buffering attributes */
@@ -573,9 +578,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
} }
audio_pcm_init_info (&hw->info, &obt_as); audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = pa->samples = audio_buffer_samples( hw->samples = g->conf.samples;
qapi_AudiodevPaPerDirectionOptions_base(ppdo),
&obt_as, ppdo->buffer_length);
pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
pa->rpos = hw->rpos; pa->rpos = hw->rpos;
if (!pa->pcm_buf) { if (!pa->pcm_buf) {
@@ -606,32 +609,24 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
{ {
int error; int error;
pa_sample_spec ss; pa_sample_spec ss;
pa_buffer_attr ba;
struct audsettings obt_as = *as; struct audsettings obt_as = *as;
PAVoiceIn *pa = (PAVoiceIn *) hw; PAVoiceIn *pa = (PAVoiceIn *) hw;
paaudio *g = pa->g = drv_opaque; paaudio *g = pa->g = drv_opaque;
AudiodevPaOptions *popts = &g->dev->u.pa;
AudiodevPaPerDirectionOptions *ppdo = popts->in;
ss.format = audfmt_to_pa (as->fmt, as->endianness); ss.format = audfmt_to_pa (as->fmt, as->endianness);
ss.channels = as->nchannels; ss.channels = as->nchannels;
ss.rate = as->freq; ss.rate = as->freq;
ba.fragsize = pa_usec_to_bytes(ppdo->latency, &ss);
ba.maxlength = 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); obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
pa->stream = qpa_simple_new ( pa->stream = qpa_simple_new (
g, g,
"qemu", "qemu",
PA_STREAM_RECORD, PA_STREAM_RECORD,
ppdo->has_name ? ppdo->name : NULL, g->conf.source,
&ss, &ss,
NULL, /* channel map */ NULL, /* channel map */
&ba, /* buffering attributes */ NULL, /* buffering attributes */
&error &error
); );
if (!pa->stream) { if (!pa->stream) {
@@ -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); audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = pa->samples = audio_buffer_samples( hw->samples = g->conf.samples;
qapi_AudiodevPaPerDirectionOptions_base(ppdo),
&obt_as, ppdo->buffer_length);
pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
pa->wpos = hw->wpos; pa->wpos = hw->wpos;
if (!pa->pcm_buf) { if (!pa->pcm_buf) {
@@ -814,54 +807,15 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
return 0; return 0;
} }
static int qpa_validate_per_direction_opts(Audiodev *dev, /* common */
AudiodevPaPerDirectionOptions *pdo) static PAConf glob_conf = {
.samples = 4096,
};
static void *qpa_audio_init (void)
{ {
if (!pdo->has_buffer_length) { paaudio *g = g_malloc(sizeof(paaudio));
pdo->has_buffer_length = true; g->conf = glob_conf;
pdo->buffer_length = 46440;
}
if (!pdo->has_latency) {
pdo->has_latency = true;
pdo->latency = 15000;
}
return 1;
}
static void *qpa_audio_init(Audiodev *dev)
{
paaudio *g;
AudiodevPaOptions *popts = &dev->u.pa;
const char *server;
if (!popts->has_server) {
char pidfile[64];
char *runtime;
struct stat st;
runtime = getenv("XDG_RUNTIME_DIR");
if (!runtime) {
return NULL;
}
snprintf(pidfile, sizeof(pidfile), "%s/pulse/pid", runtime);
if (stat(pidfile, &st) != 0) {
return NULL;
}
}
assert(dev->driver == AUDIODEV_DRIVER_PA);
g = g_malloc(sizeof(paaudio));
server = popts->has_server ? popts->server : NULL;
if (!qpa_validate_per_direction_opts(dev, popts->in)) {
goto fail;
}
if (!qpa_validate_per_direction_opts(dev, popts->out)) {
goto fail;
}
g->dev = dev;
g->mainloop = NULL; g->mainloop = NULL;
g->context = NULL; g->context = NULL;
@@ -871,14 +825,14 @@ static void *qpa_audio_init(Audiodev *dev)
} }
g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop),
server); g->conf.server);
if (!g->context) { if (!g->context) {
goto fail; goto fail;
} }
pa_context_set_state_callback (g->context, context_state_cb, g); pa_context_set_state_callback (g->context, context_state_cb, g);
if (pa_context_connect(g->context, server, 0, NULL) < 0) { if (pa_context_connect (g->context, g->conf.server, 0, NULL) < 0) {
qpa_logerr (pa_context_errno (g->context), qpa_logerr (pa_context_errno (g->context),
"pa_context_connect() failed\n"); "pa_context_connect() failed\n");
goto fail; goto fail;
@@ -941,6 +895,34 @@ static void qpa_audio_fini (void *opaque)
g_free(g); g_free(g);
} }
struct audio_option qpa_options[] = {
{
.name = "SAMPLES",
.tag = AUD_OPT_INT,
.valp = &glob_conf.samples,
.descr = "buffer size in samples"
},
{
.name = "SERVER",
.tag = AUD_OPT_STR,
.valp = &glob_conf.server,
.descr = "server address"
},
{
.name = "SINK",
.tag = AUD_OPT_STR,
.valp = &glob_conf.sink,
.descr = "sink device name"
},
{
.name = "SOURCE",
.tag = AUD_OPT_STR,
.valp = &glob_conf.source,
.descr = "source device name"
},
{ /* End of list */ }
};
static struct audio_pcm_ops qpa_pcm_ops = { static struct audio_pcm_ops qpa_pcm_ops = {
.init_out = qpa_init_out, .init_out = qpa_init_out,
.fini_out = qpa_fini_out, .fini_out = qpa_fini_out,
@@ -958,6 +940,7 @@ static struct audio_pcm_ops qpa_pcm_ops = {
static struct audio_driver pa_audio_driver = { static struct audio_driver pa_audio_driver = {
.name = "pa", .name = "pa",
.descr = "http://www.pulseaudio.org/", .descr = "http://www.pulseaudio.org/",
.options = qpa_options,
.init = qpa_audio_init, .init = qpa_audio_init,
.fini = qpa_audio_fini, .fini = qpa_audio_fini,
.pcm_ops = &qpa_pcm_ops, .pcm_ops = &qpa_pcm_ops,

View File

@@ -21,11 +21,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include <SDL.h> #include <SDL.h>
#include <SDL_thread.h> #include <SDL_thread.h>
#include "qemu/module.h" #include "qemu-common.h"
#include "audio.h" #include "audio.h"
#ifndef _WIN32 #ifndef _WIN32
@@ -39,17 +38,31 @@
#define AUDIO_CAP "sdl" #define AUDIO_CAP "sdl"
#include "audio_int.h" #include "audio_int.h"
#define USE_SEMAPHORE (SDL_MAJOR_VERSION < 2)
typedef struct SDLVoiceOut { typedef struct SDLVoiceOut {
HWVoiceOut hw; HWVoiceOut hw;
int live; int live;
#if USE_SEMAPHORE
int rpos;
#endif
int decr; int decr;
} SDLVoiceOut; } SDLVoiceOut;
static struct {
int nb_samples;
} conf = {
.nb_samples = 1024
};
static struct SDLAudioState { static struct SDLAudioState {
int exit; int exit;
#if USE_SEMAPHORE
SDL_mutex *mutex;
SDL_sem *sem;
#endif
int initialized; int initialized;
bool driver_created; bool driver_created;
Audiodev *dev;
} glob_sdl; } glob_sdl;
typedef struct SDLAudioState SDLAudioState; typedef struct SDLAudioState SDLAudioState;
@@ -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 ()); AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
} }
static int aud_to_sdlfmt (AudioFormat fmt) static int sdl_lock (SDLAudioState *s, const char *forfn)
{
#if USE_SEMAPHORE
if (SDL_LockMutex (s->mutex)) {
sdl_logerr ("SDL_LockMutex for %s failed\n", forfn);
return -1;
}
#else
SDL_LockAudio();
#endif
return 0;
}
static int sdl_unlock (SDLAudioState *s, const char *forfn)
{
#if USE_SEMAPHORE
if (SDL_UnlockMutex (s->mutex)) {
sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn);
return -1;
}
#else
SDL_UnlockAudio();
#endif
return 0;
}
static int sdl_post (SDLAudioState *s, const char *forfn)
{
#if USE_SEMAPHORE
if (SDL_SemPost (s->sem)) {
sdl_logerr ("SDL_SemPost for %s failed\n", forfn);
return -1;
}
#endif
return 0;
}
#if USE_SEMAPHORE
static int sdl_wait (SDLAudioState *s, const char *forfn)
{
if (SDL_SemWait (s->sem)) {
sdl_logerr ("SDL_SemWait for %s failed\n", forfn);
return -1;
}
return 0;
}
#endif
static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
{
if (sdl_unlock (s, forfn)) {
return -1;
}
return sdl_post (s, forfn);
}
static int aud_to_sdlfmt (audfmt_e fmt)
{ {
switch (fmt) { switch (fmt) {
case AUDIO_FORMAT_S8: case AUD_FMT_S8:
return AUDIO_S8; return AUDIO_S8;
case AUDIO_FORMAT_U8: case AUD_FMT_U8:
return AUDIO_U8; return AUDIO_U8;
case AUDIO_FORMAT_S16: case AUD_FMT_S16:
return AUDIO_S16LSB; return AUDIO_S16LSB;
case AUDIO_FORMAT_U16: case AUD_FMT_U16:
return AUDIO_U16LSB; return AUDIO_U16LSB;
default: default:
@@ -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) { switch (sdlfmt) {
case AUDIO_S8: case AUDIO_S8:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_S8; *fmt = AUD_FMT_S8;
break; break;
case AUDIO_U8: case AUDIO_U8:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_U8; *fmt = AUD_FMT_U8;
break; break;
case AUDIO_S16LSB: case AUDIO_S16LSB:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_S16; *fmt = AUD_FMT_S16;
break; break;
case AUDIO_U16LSB: case AUDIO_U16LSB:
*endianness = 0; *endianness = 0;
*fmt = AUDIO_FORMAT_U16; *fmt = AUD_FMT_U16;
break; break;
case AUDIO_S16MSB: case AUDIO_S16MSB:
*endianness = 1; *endianness = 1;
*fmt = AUDIO_FORMAT_S16; *fmt = AUD_FMT_S16;
break; break;
case AUDIO_U16MSB: case AUDIO_U16MSB:
*endianness = 1; *endianness = 1;
*fmt = AUDIO_FORMAT_U16; *fmt = AUD_FMT_U16;
break; break;
default: default:
@@ -170,9 +243,9 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
static void sdl_close (SDLAudioState *s) static void sdl_close (SDLAudioState *s)
{ {
if (s->initialized) { if (s->initialized) {
SDL_LockAudio(); sdl_lock (s, "sdl_close");
s->exit = 1; s->exit = 1;
SDL_UnlockAudio(); sdl_unlock_and_post (s, "sdl_close");
SDL_PauseAudio (1); SDL_PauseAudio (1);
SDL_CloseAudio (); SDL_CloseAudio ();
s->initialized = 0; s->initialized = 0;
@@ -185,36 +258,76 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
SDLAudioState *s = &glob_sdl; SDLAudioState *s = &glob_sdl;
HWVoiceOut *hw = &sdl->hw; HWVoiceOut *hw = &sdl->hw;
int samples = len >> hw->info.shift; int samples = len >> hw->info.shift;
int to_mix, decr;
if (s->exit || !sdl->live) { if (s->exit) {
return; return;
} }
/* dolog ("in callback samples=%d live=%d\n", samples, sdl->live); */ while (samples) {
int to_mix, decr;
to_mix = audio_MIN(samples, sdl->live); /* dolog ("in callback samples=%d\n", samples); */
decr = to_mix; #if USE_SEMAPHORE
while (to_mix) { sdl_wait (s, "sdl_callback");
int chunk = audio_MIN(to_mix, hw->samples - hw->rpos); if (s->exit) {
struct st_sample *src = hw->mix_buf + hw->rpos; return;
}
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ if (sdl_lock (s, "sdl_callback")) {
hw->clip(buf, src, chunk); return;
hw->rpos = (hw->rpos + chunk) % hw->samples; }
to_mix -= chunk;
buf += chunk << hw->info.shift; if (audio_bug(__func__, sdl->live < 0 || sdl->live > hw->samples)) {
dolog ("sdl->live=%d hw->samples=%d\n",
sdl->live, hw->samples);
return;
}
if (!sdl->live) {
goto again;
}
#else
if (s->exit || !sdl->live) {
break;
}
#endif
/* dolog ("in callback live=%d\n", live); */
to_mix = audio_MIN (samples, sdl->live);
decr = to_mix;
while (to_mix) {
int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
struct st_sample *src = hw->mix_buf + hw->rpos;
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
hw->clip (buf, src, chunk);
#if USE_SEMAPHORE
sdl->rpos = (sdl->rpos + chunk) % hw->samples;
#else
hw->rpos = (hw->rpos + chunk) % hw->samples;
#endif
to_mix -= chunk;
buf += chunk << hw->info.shift;
}
samples -= decr;
sdl->live -= decr;
sdl->decr += decr;
#if USE_SEMAPHORE
again:
if (sdl_unlock (s, "sdl_callback")) {
return;
}
#endif
} }
samples -= decr;
sdl->live -= decr;
sdl->decr += decr;
/* dolog ("done len=%d\n", len); */ /* dolog ("done len=%d\n", len); */
#if (SDL_MAJOR_VERSION >= 2)
/* SDL2 does not clear the remaining buffer for us, so do it on our own */ /* SDL2 does not clear the remaining buffer for us, so do it on our own */
if (samples) { if (samples) {
memset(buf, 0, samples << hw->info.shift); memset(buf, 0, samples << hw->info.shift);
} }
#endif
} }
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
@@ -226,8 +339,11 @@ static int sdl_run_out (HWVoiceOut *hw, int live)
{ {
int decr; int decr;
SDLVoiceOut *sdl = (SDLVoiceOut *) hw; SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
SDLAudioState *s = &glob_sdl;
SDL_LockAudio(); if (sdl_lock (s, "sdl_run_out")) {
return 0;
}
if (sdl->decr > live) { if (sdl->decr > live) {
ldebug ("sdl->decr %d live %d sdl->live %d\n", ldebug ("sdl->decr %d live %d sdl->live %d\n",
@@ -239,10 +355,19 @@ static int sdl_run_out (HWVoiceOut *hw, int live)
decr = audio_MIN (sdl->decr, live); decr = audio_MIN (sdl->decr, live);
sdl->decr -= decr; sdl->decr -= decr;
#if USE_SEMAPHORE
sdl->live = live - decr;
hw->rpos = sdl->rpos;
#else
sdl->live = live; sdl->live = live;
#endif
SDL_UnlockAudio(); if (sdl->live > 0) {
sdl_unlock_and_post (s, "sdl_run_out");
}
else {
sdl_unlock (s, "sdl_run_out");
}
return decr; return decr;
} }
@@ -261,13 +386,13 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
SDL_AudioSpec req, obt; SDL_AudioSpec req, obt;
int endianness; int endianness;
int err; int err;
AudioFormat effective_fmt; audfmt_e effective_fmt;
struct audsettings obt_as; struct audsettings obt_as;
req.freq = as->freq; req.freq = as->freq;
req.format = aud_to_sdlfmt (as->fmt); req.format = aud_to_sdlfmt (as->fmt);
req.channels = as->nchannels; req.channels = as->nchannels;
req.samples = audio_buffer_samples(s->dev->u.sdl.out, as, 11610); req.samples = conf.nb_samples;
req.callback = sdl_callback; req.callback = sdl_callback;
req.userdata = sdl; req.userdata = sdl;
@@ -311,7 +436,7 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
return 0; return 0;
} }
static void *sdl_audio_init(Audiodev *dev) static void *sdl_audio_init (void)
{ {
SDLAudioState *s = &glob_sdl; SDLAudioState *s = &glob_sdl;
if (s->driver_created) { if (s->driver_created) {
@@ -324,8 +449,24 @@ static void *sdl_audio_init(Audiodev *dev)
return NULL; return NULL;
} }
#if USE_SEMAPHORE
s->mutex = SDL_CreateMutex ();
if (!s->mutex) {
sdl_logerr ("Failed to create SDL mutex\n");
SDL_QuitSubSystem (SDL_INIT_AUDIO);
return NULL;
}
s->sem = SDL_CreateSemaphore (0);
if (!s->sem) {
sdl_logerr ("Failed to create SDL semaphore\n");
SDL_DestroyMutex (s->mutex);
SDL_QuitSubSystem (SDL_INIT_AUDIO);
return NULL;
}
#endif
s->driver_created = true; s->driver_created = true;
s->dev = dev;
return s; return s;
} }
@@ -333,11 +474,24 @@ static void sdl_audio_fini (void *opaque)
{ {
SDLAudioState *s = opaque; SDLAudioState *s = opaque;
sdl_close (s); sdl_close (s);
#if USE_SEMAPHORE
SDL_DestroySemaphore (s->sem);
SDL_DestroyMutex (s->mutex);
#endif
SDL_QuitSubSystem (SDL_INIT_AUDIO); SDL_QuitSubSystem (SDL_INIT_AUDIO);
s->driver_created = false; s->driver_created = false;
s->dev = NULL;
} }
static struct audio_option sdl_options[] = {
{
.name = "SAMPLES",
.tag = AUD_OPT_INT,
.valp = &conf.nb_samples,
.descr = "Size of SDL buffer in samples"
},
{ /* End of list */ }
};
static struct audio_pcm_ops sdl_pcm_ops = { static struct audio_pcm_ops sdl_pcm_ops = {
.init_out = sdl_init_out, .init_out = sdl_init_out,
.fini_out = sdl_fini_out, .fini_out = sdl_fini_out,
@@ -349,6 +503,7 @@ static struct audio_pcm_ops sdl_pcm_ops = {
static struct audio_driver sdl_audio_driver = { static struct audio_driver sdl_audio_driver = {
.name = "sdl", .name = "sdl",
.descr = "SDL http://www.libsdl.org", .descr = "SDL http://www.libsdl.org",
.options = sdl_options,
.init = sdl_audio_init, .init = sdl_audio_init,
.fini = sdl_audio_fini, .fini = sdl_audio_fini,
.pcm_ops = &sdl_pcm_ops, .pcm_ops = &sdl_pcm_ops,

View File

@@ -20,7 +20,6 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "hw/hw.h" #include "hw/hw.h"
#include "qemu/host-utils.h" #include "qemu/host-utils.h"
#include "qemu/module.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#include "ui/qemu-spice.h" #include "ui/qemu-spice.h"
@@ -78,7 +77,7 @@ static const SpiceRecordInterface record_sif = {
.base.minor_version = SPICE_INTERFACE_RECORD_MINOR, .base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
}; };
static void *spice_audio_init(Audiodev *dev) static void *spice_audio_init (void)
{ {
if (!using_spice) { if (!using_spice) {
return NULL; return NULL;
@@ -131,7 +130,7 @@ static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ; settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ;
#endif #endif
settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN; settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
settings.fmt = AUDIO_FORMAT_S16; settings.fmt = AUD_FMT_S16;
settings.endianness = AUDIO_HOST_ENDIANNESS; settings.endianness = AUDIO_HOST_ENDIANNESS;
audio_pcm_init_info (&hw->info, &settings); audio_pcm_init_info (&hw->info, &settings);
@@ -259,7 +258,7 @@ static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
settings.freq = SPICE_INTERFACE_RECORD_FREQ; settings.freq = SPICE_INTERFACE_RECORD_FREQ;
#endif #endif
settings.nchannels = SPICE_INTERFACE_RECORD_CHAN; settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
settings.fmt = AUDIO_FORMAT_S16; settings.fmt = AUD_FMT_S16;
settings.endianness = AUDIO_HOST_ENDIANNESS; settings.endianness = AUDIO_HOST_ENDIANNESS;
audio_pcm_init_info (&hw->info, &settings); audio_pcm_init_info (&hw->info, &settings);
@@ -374,6 +373,10 @@ static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
return 0; return 0;
} }
static struct audio_option audio_options[] = {
{ /* end of list */ },
};
static struct audio_pcm_ops audio_callbacks = { static struct audio_pcm_ops audio_callbacks = {
.init_out = line_out_init, .init_out = line_out_init,
.fini_out = line_out_fini, .fini_out = line_out_fini,
@@ -391,6 +394,7 @@ static struct audio_pcm_ops audio_callbacks = {
static struct audio_driver spice_audio_driver = { static struct audio_driver spice_audio_driver = {
.name = "spice", .name = "spice",
.descr = "spice audio driver", .descr = "spice audio driver",
.options = audio_options,
.init = spice_audio_init, .init = spice_audio_init,
.fini = spice_audio_fini, .fini = spice_audio_fini,
.pcm_ops = &audio_callbacks, .pcm_ops = &audio_callbacks,

View File

@@ -1,6 +1,6 @@
# See docs/devel/tracing.txt for syntax documentation. # See docs/devel/tracing.txt for syntax documentation.
# alsaaudio.c # audio/alsaaudio.c
alsa_revents(int revents) "revents = %d" alsa_revents(int revents) "revents = %d"
alsa_pollout(int i, int fd) "i = %d fd = %d" alsa_pollout(int i, int fd) "i = %d fd = %d"
alsa_set_handler(int events, int index, int fd, int err) "events=0x%x index=%d fd=%d err=%d" alsa_set_handler(int events, int index, int fd, int err) "events=0x%x index=%d fd=%d err=%d"
@@ -12,11 +12,6 @@ alsa_resume_out(void) "Resuming suspended output stream"
alsa_resume_in(void) "Resuming suspended input stream" alsa_resume_in(void) "Resuming suspended input stream"
alsa_no_frames(int state) "No frames available and ALSA state is %d" alsa_no_frames(int state) "No frames available and ALSA state is %d"
# ossaudio.c # audio/ossaudio.c
oss_version(int version) "OSS version = 0x%x" oss_version(int version) "OSS version = 0x%x"
oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d" oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d"
# audio.c
audio_timer_start(int interval) "interval %d ms"
audio_timer_stop(void) ""
audio_timer_delayed(int interval) "interval %d ms"

View File

@@ -21,12 +21,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/host-utils.h" #include "qemu/host-utils.h"
#include "qemu/module.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#include "qapi/opts-visitor.h"
#include "audio.h" #include "audio.h"
#define AUDIO_CAP "wav" #define AUDIO_CAP "wav"
@@ -40,6 +37,11 @@ typedef struct WAVVoiceOut {
int total_samples; int total_samples;
} WAVVoiceOut; } WAVVoiceOut;
typedef struct {
struct audsettings settings;
const char *wav_path;
} WAVConf;
static int wav_run_out (HWVoiceOut *hw, int live) static int wav_run_out (HWVoiceOut *hw, int live)
{ {
WAVVoiceOut *wav = (WAVVoiceOut *) hw; WAVVoiceOut *wav = (WAVVoiceOut *) hw;
@@ -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, 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
}; };
Audiodev *dev = drv_opaque; WAVConf *conf = drv_opaque;
AudiodevWavOptions *wopts = &dev->u.wav; struct audsettings wav_as = conf->settings;
struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out);
const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav";
stereo = wav_as.nchannels == 2; stereo = wav_as.nchannels == 2;
switch (wav_as.fmt) { switch (wav_as.fmt) {
case AUDIO_FORMAT_S8: case AUD_FMT_S8:
case AUDIO_FORMAT_U8: case AUD_FMT_U8:
bits16 = 0; bits16 = 0;
break; break;
case AUDIO_FORMAT_S16: case AUD_FMT_S16:
case AUDIO_FORMAT_U16: case AUD_FMT_U16:
bits16 = 1; bits16 = 1;
break; break;
case AUDIO_FORMAT_S32: case AUD_FMT_S32:
case AUDIO_FORMAT_U32: case AUD_FMT_U32:
dolog ("WAVE files can not handle 32bit formats\n"); dolog ("WAVE files can not handle 32bit formats\n");
return -1; return -1;
default:
abort();
} }
hdr[34] = bits16 ? 0x10 : 0x08; hdr[34] = bits16 ? 0x10 : 0x08;
@@ -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 + 28, hw->info.freq << (bits16 + stereo), 4);
le_store (hdr + 32, 1 << (bits16 + stereo), 2); le_store (hdr + 32, 1 << (bits16 + stereo), 2);
wav->f = fopen(wav_path, "wb"); wav->f = fopen (conf->wav_path, "wb");
if (!wav->f) { if (!wav->f) {
dolog ("Failed to open wave file `%s'\nReason: %s\n", dolog ("Failed to open wave file `%s'\nReason: %s\n",
wav_path, strerror(errno)); conf->wav_path, strerror (errno));
g_free (wav->pcm_buf); g_free (wav->pcm_buf);
wav->pcm_buf = NULL; wav->pcm_buf = NULL;
return -1; return -1;
@@ -225,17 +222,54 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
return 0; return 0;
} }
static void *wav_audio_init(Audiodev *dev) static WAVConf glob_conf = {
.settings.freq = 44100,
.settings.nchannels = 2,
.settings.fmt = AUD_FMT_S16,
.wav_path = "qemu.wav"
};
static void *wav_audio_init (void)
{ {
assert(dev->driver == AUDIODEV_DRIVER_WAV); WAVConf *conf = g_malloc(sizeof(WAVConf));
return dev; *conf = glob_conf;
return conf;
} }
static void wav_audio_fini (void *opaque) static void wav_audio_fini (void *opaque)
{ {
ldebug ("wav_fini"); ldebug ("wav_fini");
g_free(opaque);
} }
static struct audio_option wav_options[] = {
{
.name = "FREQUENCY",
.tag = AUD_OPT_INT,
.valp = &glob_conf.settings.freq,
.descr = "Frequency"
},
{
.name = "FORMAT",
.tag = AUD_OPT_FMT,
.valp = &glob_conf.settings.fmt,
.descr = "Format"
},
{
.name = "DAC_FIXED_CHANNELS",
.tag = AUD_OPT_INT,
.valp = &glob_conf.settings.nchannels,
.descr = "Number of channels (1 - mono, 2 - stereo)"
},
{
.name = "PATH",
.tag = AUD_OPT_STR,
.valp = &glob_conf.wav_path,
.descr = "Path to wave file"
},
{ /* End of list */ }
};
static struct audio_pcm_ops wav_pcm_ops = { static struct audio_pcm_ops wav_pcm_ops = {
.init_out = wav_init_out, .init_out = wav_init_out,
.fini_out = wav_fini_out, .fini_out = wav_fini_out,
@@ -247,6 +281,7 @@ static struct audio_pcm_ops wav_pcm_ops = {
static struct audio_driver wav_audio_driver = { static struct audio_driver wav_audio_driver = {
.name = "wav", .name = "wav",
.descr = "WAV renderer http://wikipedia.org/wiki/WAV", .descr = "WAV renderer http://wikipedia.org/wiki/WAV",
.options = wav_options,
.init = wav_audio_init, .init = wav_audio_init,
.fini = wav_audio_fini, .fini = wav_audio_fini,
.pcm_ops = &wav_pcm_ops, .pcm_ops = &wav_pcm_ops,

View File

@@ -38,29 +38,30 @@ static void wav_destroy (void *opaque)
uint8_t dlen[4]; uint8_t dlen[4];
uint32_t datalen = wav->bytes; uint32_t datalen = wav->bytes;
uint32_t rifflen = datalen + 36; uint32_t rifflen = datalen + 36;
Monitor *mon = cur_mon;
if (wav->f) { if (wav->f) {
le_store (rlen, rifflen, 4); le_store (rlen, rifflen, 4);
le_store (dlen, datalen, 4); le_store (dlen, datalen, 4);
if (fseek (wav->f, 4, SEEK_SET)) { if (fseek (wav->f, 4, SEEK_SET)) {
error_report("wav_destroy: rlen fseek failed: %s", monitor_printf (mon, "wav_destroy: rlen fseek failed\nReason: %s\n",
strerror(errno)); strerror (errno));
goto doclose; goto doclose;
} }
if (fwrite (rlen, 4, 1, wav->f) != 1) { if (fwrite (rlen, 4, 1, wav->f) != 1) {
error_report("wav_destroy: rlen fwrite failed: %s", monitor_printf (mon, "wav_destroy: rlen fwrite failed\nReason %s\n",
strerror(errno)); strerror (errno));
goto doclose; goto doclose;
} }
if (fseek (wav->f, 32, SEEK_CUR)) { if (fseek (wav->f, 32, SEEK_CUR)) {
error_report("wav_destroy: dlen fseek failed: %s", monitor_printf (mon, "wav_destroy: dlen fseek failed\nReason %s\n",
strerror(errno)); strerror (errno));
goto doclose; goto doclose;
} }
if (fwrite (dlen, 1, 4, wav->f) != 4) { if (fwrite (dlen, 1, 4, wav->f) != 4) {
error_report("wav_destroy: dlen fwrite failed: %s", monitor_printf (mon, "wav_destroy: dlen fwrite failed\nReason %s\n",
strerror(errno)); strerror (errno));
goto doclose; goto doclose;
} }
doclose: doclose:
@@ -77,7 +78,8 @@ static void wav_capture (void *opaque, void *buf, int size)
WAVState *wav = opaque; WAVState *wav = opaque;
if (fwrite (buf, size, 1, wav->f) != 1) { if (fwrite (buf, size, 1, wav->f) != 1) {
error_report("wav_capture: fwrite error: %s", strerror(errno)); monitor_printf (cur_mon, "wav_capture: fwrite error\nReason: %s",
strerror (errno));
} }
wav->bytes += size; wav->bytes += size;
} }
@@ -108,6 +110,7 @@ static struct capture_ops wav_capture_ops = {
int wav_start_capture (CaptureState *s, const char *path, int freq, int wav_start_capture (CaptureState *s, const char *path, int freq,
int bits, int nchannels) int bits, int nchannels)
{ {
Monitor *mon = cur_mon;
WAVState *wav; WAVState *wav;
uint8_t hdr[] = { uint8_t hdr[] = {
0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
@@ -121,13 +124,13 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
CaptureVoiceOut *cap; CaptureVoiceOut *cap;
if (bits != 8 && bits != 16) { if (bits != 8 && bits != 16) {
error_report("incorrect bit count %d, must be 8 or 16", bits); monitor_printf (mon, "incorrect bit count %d, must be 8 or 16\n", bits);
return -1; return -1;
} }
if (nchannels != 1 && nchannels != 2) { if (nchannels != 1 && nchannels != 2) {
error_report("incorrect channel count %d, must be 1 or 2", monitor_printf (mon, "incorrect channel count %d, must be 1 or 2\n",
nchannels); nchannels);
return -1; return -1;
} }
@@ -136,7 +139,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
as.freq = freq; as.freq = freq;
as.nchannels = 1 << stereo; as.nchannels = 1 << stereo;
as.fmt = bits16 ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8; as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
as.endianness = 0; as.endianness = 0;
ops.notify = wav_notify; ops.notify = wav_notify;
@@ -155,8 +158,8 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
wav->f = fopen (path, "wb"); wav->f = fopen (path, "wb");
if (!wav->f) { if (!wav->f) {
error_report("Failed to open wave file `%s': %s", monitor_printf (mon, "Failed to open wave file `%s'\nReason: %s\n",
path, strerror(errno)); path, strerror (errno));
g_free (wav); g_free (wav);
return -1; return -1;
} }
@@ -167,13 +170,14 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
wav->freq = freq; wav->freq = freq;
if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) { if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) {
error_report("Failed to write header: %s", strerror(errno)); monitor_printf (mon, "Failed to write header\nReason: %s\n",
strerror (errno));
goto error_free; goto error_free;
} }
cap = AUD_add_capture (&as, &ops, wav); cap = AUD_add_capture (&as, &ops, wav);
if (!cap) { if (!cap) {
error_report("Failed to add audio capture"); monitor_printf (mon, "Failed to add audio capture\n");
goto error_free; goto error_free;
} }
@@ -185,7 +189,8 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
error_free: error_free:
g_free (wav->path); g_free (wav->path);
if (fclose (wav->f)) { if (fclose (wav->f)) {
error_report("Failed to close wave file: %s", strerror(errno)); monitor_printf (mon, "Failed to close wave file\nReason: %s\n",
strerror (errno));
} }
g_free (wav); g_free (wav);
return -1; return -1;

View File

@@ -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

View File

@@ -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)

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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"

View File

@@ -4,16 +4,15 @@ common-obj-$(CONFIG_POSIX) += rng-random.o
common-obj-$(CONFIG_TPM) += tpm.o common-obj-$(CONFIG_TPM) += tpm.o
common-obj-y += hostmem.o hostmem-ram.o common-obj-y += hostmem.o hostmem-ram.o
common-obj-$(CONFIG_POSIX) += hostmem-file.o common-obj-$(CONFIG_LINUX) += hostmem-file.o
common-obj-y += cryptodev.o common-obj-y += cryptodev.o
common-obj-y += cryptodev-builtin.o common-obj-y += cryptodev-builtin.o
ifeq ($(CONFIG_VIRTIO_CRYPTO),y) ifeq ($(CONFIG_VIRTIO),y)
common-obj-y += cryptodev-vhost.o common-obj-y += cryptodev-vhost.o
common-obj-$(CONFIG_VHOST_CRYPTO) += cryptodev-vhost-user.o common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) += \
cryptodev-vhost-user.o
endif endif
common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_VIRTIO)) += vhost-user.o
common-obj-$(CONFIG_LINUX) += hostmem-memfd.o common-obj-$(CONFIG_LINUX) += hostmem-memfd.o

View File

@@ -26,7 +26,6 @@
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "hw/virtio/vhost-user.h"
#include "standard-headers/linux/virtio_crypto.h" #include "standard-headers/linux/virtio_crypto.h"
#include "sysemu/cryptodev-vhost.h" #include "sysemu/cryptodev-vhost.h"
#include "chardev/char-fe.h" #include "chardev/char-fe.h"
@@ -47,7 +46,6 @@
typedef struct CryptoDevBackendVhostUser { typedef struct CryptoDevBackendVhostUser {
CryptoDevBackend parent_obj; CryptoDevBackend parent_obj;
VhostUserState vhost_user;
CharBackend chr; CharBackend chr;
char *chr_name; char *chr_name;
bool opened; bool opened;
@@ -104,7 +102,7 @@ cryptodev_vhost_user_start(int queues,
continue; continue;
} }
options.opaque = &s->vhost_user; options.opaque = &s->chr;
options.backend_type = VHOST_BACKEND_TYPE_USER; options.backend_type = VHOST_BACKEND_TYPE_USER;
options.cc = b->conf.peers.ccs[i]; options.cc = b->conf.peers.ccs[i];
s->vhost_crypto[i] = cryptodev_vhost_init(&options); s->vhost_crypto[i] = cryptodev_vhost_init(&options);
@@ -157,6 +155,7 @@ static void cryptodev_vhost_user_event(void *opaque, int event)
{ {
CryptoDevBackendVhostUser *s = opaque; CryptoDevBackendVhostUser *s = opaque;
CryptoDevBackend *b = CRYPTODEV_BACKEND(s); CryptoDevBackend *b = CRYPTODEV_BACKEND(s);
Error *err = NULL;
int queues = b->conf.peers.queues; int queues = b->conf.peers.queues;
assert(queues < MAX_CRYPTO_QUEUE_NUM); assert(queues < MAX_CRYPTO_QUEUE_NUM);
@@ -173,6 +172,10 @@ static void cryptodev_vhost_user_event(void *opaque, int event)
cryptodev_vhost_user_stop(queues, s); cryptodev_vhost_user_stop(queues, s);
break; break;
} }
if (err) {
error_report_err(err);
}
} }
static void cryptodev_vhost_user_init( static void cryptodev_vhost_user_init(
@@ -212,10 +215,6 @@ static void cryptodev_vhost_user_init(
} }
} }
if (!vhost_user_init(&s->vhost_user, &s->chr, errp)) {
return;
}
qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
cryptodev_vhost_user_event, NULL, s, NULL, true); cryptodev_vhost_user_event, NULL, s, NULL, true);
@@ -300,8 +299,6 @@ static void cryptodev_vhost_user_cleanup(
backend->conf.peers.ccs[i] = NULL; backend->conf.peers.ccs[i] = NULL;
} }
} }
vhost_user_cleanup(&s->vhost_user);
} }
static void cryptodev_vhost_user_set_chardev(Object *obj, static void cryptodev_vhost_user_set_chardev(Object *obj,

View File

@@ -9,11 +9,9 @@
* This work is licensed under the terms of the GNU GPL, version 2 or later. * This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory. * See the COPYING file in the top-level directory.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/error-report.h" #include "qemu-common.h"
#include "qemu/module.h"
#include "sysemu/hostmem.h" #include "sysemu/hostmem.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "qom/object_interfaces.h" #include "qom/object_interfaces.h"
@@ -33,21 +31,15 @@ typedef struct HostMemoryBackendFile HostMemoryBackendFile;
struct HostMemoryBackendFile { struct HostMemoryBackendFile {
HostMemoryBackend parent_obj; HostMemoryBackend parent_obj;
bool discard_data;
char *mem_path; char *mem_path;
uint64_t align; uint64_t align;
bool discard_data;
bool is_pmem;
}; };
static void static void
file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
{ {
#ifndef CONFIG_POSIX
error_setg(errp, "backend '%s' not supported on this host",
object_get_typename(OBJECT(backend)));
#else
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend); HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend);
gchar *name;
if (!backend->size) { if (!backend->size) {
error_setg(errp, "can't create backend with size 0"); error_setg(errp, "can't create backend with size 0");
@@ -57,38 +49,19 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
error_setg(errp, "mem-path property not set"); error_setg(errp, "mem-path property not set");
return; return;
} }
#ifndef CONFIG_LINUX
/* error_setg(errp, "-mem-path not supported on this host");
* Verify pmem file size since starting a guest with an incorrect size #else
* leads to confusing failures inside the guest. if (!host_memory_backend_mr_inited(backend)) {
*/ gchar *path;
if (fb->is_pmem) { backend->force_prealloc = mem_prealloc;
Error *local_err = NULL; path = object_get_canonical_path(OBJECT(backend));
uint64_t size; memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
path,
size = qemu_get_pmem_size(fb->mem_path, &local_err); backend->size, fb->align, backend->share,
if (!size) { fb->mem_path, errp);
error_propagate(errp, local_err); g_free(path);
return;
}
if (backend->size > size) {
error_setg(errp, "size property %" PRIu64 " is larger than "
"pmem file \"%s\" size %" PRIu64, backend->size,
fb->mem_path, size);
return;
}
} }
backend->force_prealloc = mem_prealloc;
name = host_memory_backend_get_name(backend);
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
name,
backend->size, fb->align,
(backend->share ? RAM_SHARED : 0) |
(fb->is_pmem ? RAM_PMEM : 0),
fb->mem_path, errp);
g_free(name);
#endif #endif
} }
@@ -105,8 +78,7 @@ static void set_mem_path(Object *o, const char *str, Error **errp)
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
if (host_memory_backend_mr_inited(backend)) { if (host_memory_backend_mr_inited(backend)) {
error_setg(errp, "cannot change property 'mem-path' of %s", error_setg(errp, "cannot change property value");
object_get_typename(o));
return; return;
} }
g_free(fb->mem_path); g_free(fb->mem_path);
@@ -144,8 +116,7 @@ static void file_memory_backend_set_align(Object *o, Visitor *v,
uint64_t val; uint64_t val;
if (host_memory_backend_mr_inited(backend)) { if (host_memory_backend_mr_inited(backend)) {
error_setg(&local_err, "cannot change property '%s' of %s", error_setg(&local_err, "cannot change property value");
name, object_get_typename(o));
goto out; goto out;
} }
@@ -159,39 +130,6 @@ static void file_memory_backend_set_align(Object *o, Visitor *v,
error_propagate(errp, local_err); error_propagate(errp, local_err);
} }
static bool file_memory_backend_get_pmem(Object *o, Error **errp)
{
return MEMORY_BACKEND_FILE(o)->is_pmem;
}
static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(o);
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
if (host_memory_backend_mr_inited(backend)) {
error_setg(errp, "cannot change property 'pmem' of %s.",
object_get_typename(o));
return;
}
#ifndef CONFIG_LIBPMEM
if (value) {
Error *local_err = NULL;
error_setg(&local_err,
"Lack of libpmem support while setting the 'pmem=on'"
" of %s. We can't ensure data persistence.",
object_get_typename(o));
error_propagate(errp, local_err);
return;
}
#endif
fb->is_pmem = value;
}
static void file_backend_unparent(Object *obj) static void file_backend_unparent(Object *obj)
{ {
HostMemoryBackend *backend = MEMORY_BACKEND(obj); HostMemoryBackend *backend = MEMORY_BACKEND(obj);
@@ -223,9 +161,6 @@ file_backend_class_init(ObjectClass *oc, void *data)
file_memory_backend_get_align, file_memory_backend_get_align,
file_memory_backend_set_align, file_memory_backend_set_align,
NULL, NULL, &error_abort); NULL, NULL, &error_abort);
object_class_property_add_bool(oc, "pmem",
file_memory_backend_get_pmem, file_memory_backend_set_pmem,
&error_abort);
} }
static void file_backend_instance_finalize(Object *o) static void file_backend_instance_finalize(Object *o)

View File

@@ -9,13 +9,12 @@
* This work is licensed under the terms of the GNU GPL, version 2 or later. * This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory. * See the COPYING file in the top-level directory.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h"
#include "sysemu/hostmem.h" #include "sysemu/hostmem.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "qom/object_interfaces.h" #include "qom/object_interfaces.h"
#include "qemu/memfd.h" #include "qemu/memfd.h"
#include "qemu/module.h"
#include "qapi/error.h" #include "qapi/error.h"
#define TYPE_MEMORY_BACKEND_MEMFD "memory-backend-memfd" #define TYPE_MEMORY_BACKEND_MEMFD "memory-backend-memfd"
@@ -45,6 +44,10 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
return; return;
} }
if (host_memory_backend_mr_inited(backend)) {
return;
}
backend->force_prealloc = mem_prealloc; backend->force_prealloc = mem_prealloc;
fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size, fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size,
m->hugetlb, m->hugetlbsize, m->seal ? m->hugetlb, m->hugetlbsize, m->seal ?
@@ -54,10 +57,9 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
return; return;
} }
name = host_memory_backend_get_name(backend); name = object_get_canonical_path(OBJECT(backend));
memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend),
name, backend->size, name, backend->size, true, fd, errp);
backend->share, fd, errp);
g_free(name); g_free(name);
} }
@@ -129,7 +131,6 @@ memfd_backend_instance_init(Object *obj)
/* default to sealed file */ /* default to sealed file */
m->seal = true; m->seal = true;
MEMORY_BACKEND(m)->share = true;
} }
static void static void
@@ -139,29 +140,18 @@ memfd_backend_class_init(ObjectClass *oc, void *data)
bc->alloc = memfd_backend_memory_alloc; bc->alloc = memfd_backend_memory_alloc;
if (qemu_memfd_check(MFD_HUGETLB)) { object_class_property_add_bool(oc, "hugetlb",
object_class_property_add_bool(oc, "hugetlb", memfd_backend_get_hugetlb,
memfd_backend_get_hugetlb, memfd_backend_set_hugetlb,
memfd_backend_set_hugetlb, &error_abort);
&error_abort); object_class_property_add(oc, "hugetlbsize", "int",
object_class_property_set_description(oc, "hugetlb", memfd_backend_get_hugetlbsize,
"Use huge pages", memfd_backend_set_hugetlbsize,
&error_abort); NULL, NULL, &error_abort);
object_class_property_add(oc, "hugetlbsize", "int",
memfd_backend_get_hugetlbsize,
memfd_backend_set_hugetlbsize,
NULL, NULL, &error_abort);
object_class_property_set_description(oc, "hugetlbsize",
"Huge pages size (ex: 2M, 1G)",
&error_abort);
}
object_class_property_add_bool(oc, "seal", object_class_property_add_bool(oc, "seal",
memfd_backend_get_seal, memfd_backend_get_seal,
memfd_backend_set_seal, memfd_backend_set_seal,
&error_abort); &error_abort);
object_class_property_set_description(oc, "seal",
"Seal growing & shrinking",
&error_abort);
} }
static const TypeInfo memfd_backend_info = { static const TypeInfo memfd_backend_info = {
@@ -174,9 +164,7 @@ static const TypeInfo memfd_backend_info = {
static void register_types(void) static void register_types(void)
{ {
if (qemu_memfd_check(MFD_ALLOW_SEALING)) { type_register_static(&memfd_backend_info);
type_register_static(&memfd_backend_info);
}
} }
type_init(register_types); type_init(register_types);

View File

@@ -9,29 +9,28 @@
* This work is licensed under the terms of the GNU GPL, version 2 or later. * This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory. * See the COPYING file in the top-level directory.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "sysemu/hostmem.h" #include "sysemu/hostmem.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/module.h"
#include "qom/object_interfaces.h" #include "qom/object_interfaces.h"
#define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram" #define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram"
static void static void
ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
{ {
char *name; char *path;
if (!backend->size) { if (!backend->size) {
error_setg(errp, "can't create backend with size 0"); error_setg(errp, "can't create backend with size 0");
return; return;
} }
name = host_memory_backend_get_name(backend); path = object_get_canonical_path_component(OBJECT(backend));
memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), name, memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), path,
backend->size, backend->share, errp); backend->size, backend->share, errp);
g_free(name); g_free(path);
} }
static void static void

View File

@@ -18,7 +18,6 @@
#include "qapi/visitor.h" #include "qapi/visitor.h"
#include "qemu/config-file.h" #include "qemu/config-file.h"
#include "qom/object_interfaces.h" #include "qom/object_interfaces.h"
#include "qemu/mmap-alloc.h"
#ifdef CONFIG_NUMA #ifdef CONFIG_NUMA
#include <numaif.h> #include <numaif.h>
@@ -28,16 +27,6 @@ QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_BIND != MPOL_BIND);
QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE); QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE);
#endif #endif
char *
host_memory_backend_get_name(HostMemoryBackend *backend)
{
if (!backend->use_canonical_path) {
return object_get_canonical_path_component(OBJECT(backend));
}
return object_get_canonical_path(OBJECT(backend));
}
static void static void
host_memory_backend_get_size(Object *obj, Visitor *v, const char *name, host_memory_backend_get_size(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp) void *opaque, Error **errp)
@@ -57,8 +46,7 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name,
uint64_t value; uint64_t value;
if (host_memory_backend_mr_inited(backend)) { if (host_memory_backend_mr_inited(backend)) {
error_setg(&local_err, "cannot change property %s of %s ", error_setg(&local_err, "cannot change property value");
name, object_get_typename(obj));
goto out; goto out;
} }
@@ -67,9 +55,8 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name,
goto out; goto out;
} }
if (!value) { if (!value) {
error_setg(&local_err, error_setg(&local_err, "Property '%s.%s' doesn't take value '%"
"property '%s' of %s doesn't take value '%" PRIu64 "'", PRIu64 "'", object_get_typename(obj), name, value);
name, object_get_typename(obj), value);
goto out; goto out;
} }
backend->size = value; backend->size = value;
@@ -88,7 +75,7 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name,
value = find_first_bit(backend->host_nodes, MAX_NODES); value = find_first_bit(backend->host_nodes, MAX_NODES);
if (value == MAX_NODES) { if (value == MAX_NODES) {
goto ret; return;
} }
*node = g_malloc0(sizeof(**node)); *node = g_malloc0(sizeof(**node));
@@ -106,7 +93,6 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name,
node = &(*node)->next; node = &(*node)->next;
} while (true); } while (true);
ret:
visit_type_uint16List(v, name, &host_nodes, errp); visit_type_uint16List(v, name, &host_nodes, errp);
} }
@@ -116,23 +102,14 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name,
{ {
#ifdef CONFIG_NUMA #ifdef CONFIG_NUMA
HostMemoryBackend *backend = MEMORY_BACKEND(obj); HostMemoryBackend *backend = MEMORY_BACKEND(obj);
uint16List *l, *host_nodes = NULL; uint16List *l = NULL;
visit_type_uint16List(v, name, &host_nodes, errp); visit_type_uint16List(v, name, &l, errp);
for (l = host_nodes; l; l = l->next) { while (l) {
if (l->value >= MAX_NODES) {
error_setg(errp, "Invalid host-nodes value: %d", l->value);
goto out;
}
}
for (l = host_nodes; l; l = l->next) {
bitmap_set(backend->host_nodes, l->value, 1); bitmap_set(backend->host_nodes, l->value, 1);
l = l->next;
} }
out:
qapi_free_uint16List(host_nodes);
#else #else
error_setg(errp, "NUMA node binding are not supported by this QEMU"); error_setg(errp, "NUMA node binding are not supported by this QEMU");
#endif #endif
@@ -222,7 +199,6 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value,
{ {
Error *local_err = NULL; Error *local_err = NULL;
HostMemoryBackend *backend = MEMORY_BACKEND(obj); HostMemoryBackend *backend = MEMORY_BACKEND(obj);
MachineState *ms = MACHINE(qdev_get_machine());
if (backend->force_prealloc) { if (backend->force_prealloc) {
if (value) { 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); void *ptr = memory_region_get_ram_ptr(&backend->mr);
uint64_t sz = memory_region_size(&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) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; return;
@@ -261,11 +237,6 @@ static void host_memory_backend_init(Object *obj)
backend->prealloc = mem_prealloc; backend->prealloc = mem_prealloc;
} }
static void host_memory_backend_post_init(Object *obj)
{
object_apply_compat_props(obj);
}
bool host_memory_backend_mr_inited(HostMemoryBackend *backend) bool host_memory_backend_mr_inited(HostMemoryBackend *backend)
{ {
/* /*
@@ -275,7 +246,8 @@ bool host_memory_backend_mr_inited(HostMemoryBackend *backend)
return memory_region_size(&backend->mr) != 0; return memory_region_size(&backend->mr) != 0;
} }
MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend) MemoryRegion *
host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp)
{ {
return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL; return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL;
} }
@@ -290,29 +262,11 @@ bool host_memory_backend_is_mapped(HostMemoryBackend *backend)
return backend->is_mapped; return backend->is_mapped;
} }
#ifdef __linux__
size_t host_memory_backend_pagesize(HostMemoryBackend *memdev)
{
Object *obj = OBJECT(memdev);
char *path = object_property_get_str(obj, "mem-path", NULL);
size_t pagesize = qemu_mempath_getpagesize(path);
g_free(path);
return pagesize;
}
#else
size_t host_memory_backend_pagesize(HostMemoryBackend *memdev)
{
return getpagesize();
}
#endif
static void static void
host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
{ {
HostMemoryBackend *backend = MEMORY_BACKEND(uc); HostMemoryBackend *backend = MEMORY_BACKEND(uc);
HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(uc); HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(uc);
MachineState *ms = MACHINE(qdev_get_machine());
Error *local_err = NULL; Error *local_err = NULL;
void *ptr; void *ptr;
uint64_t sz; uint64_t sz;
@@ -377,7 +331,7 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
*/ */
if (backend->prealloc) { if (backend->prealloc) {
os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz, os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz,
ms->smp.cpus, &local_err); smp_cpus, &local_err);
if (local_err) { if (local_err) {
goto out; 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) static bool host_memory_backend_get_share(Object *o, Error **errp)
{ {
HostMemoryBackend *backend = MEMORY_BACKEND(o); 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; backend->share = value;
} }
static bool
host_memory_backend_get_use_canonical_path(Object *obj, Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
return backend->use_canonical_path;
}
static void
host_memory_backend_set_use_canonical_path(Object *obj, bool value,
Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
backend->use_canonical_path = value;
}
static void static void
host_memory_backend_class_init(ObjectClass *oc, void *data) host_memory_backend_class_init(ObjectClass *oc, void *data)
{ {
@@ -443,44 +398,34 @@ host_memory_backend_class_init(ObjectClass *oc, void *data)
object_class_property_add_bool(oc, "merge", object_class_property_add_bool(oc, "merge",
host_memory_backend_get_merge, host_memory_backend_get_merge,
host_memory_backend_set_merge, &error_abort); host_memory_backend_set_merge, &error_abort);
object_class_property_set_description(oc, "merge",
"Mark memory as mergeable", &error_abort);
object_class_property_add_bool(oc, "dump", object_class_property_add_bool(oc, "dump",
host_memory_backend_get_dump, host_memory_backend_get_dump,
host_memory_backend_set_dump, &error_abort); host_memory_backend_set_dump, &error_abort);
object_class_property_set_description(oc, "dump",
"Set to 'off' to exclude from core dump", &error_abort);
object_class_property_add_bool(oc, "prealloc", object_class_property_add_bool(oc, "prealloc",
host_memory_backend_get_prealloc, host_memory_backend_get_prealloc,
host_memory_backend_set_prealloc, &error_abort); host_memory_backend_set_prealloc, &error_abort);
object_class_property_set_description(oc, "prealloc",
"Preallocate memory", &error_abort);
object_class_property_add(oc, "size", "int", object_class_property_add(oc, "size", "int",
host_memory_backend_get_size, host_memory_backend_get_size,
host_memory_backend_set_size, host_memory_backend_set_size,
NULL, NULL, &error_abort); NULL, NULL, &error_abort);
object_class_property_set_description(oc, "size",
"Size of the memory region (ex: 500M)", &error_abort);
object_class_property_add(oc, "host-nodes", "int", object_class_property_add(oc, "host-nodes", "int",
host_memory_backend_get_host_nodes, host_memory_backend_get_host_nodes,
host_memory_backend_set_host_nodes, host_memory_backend_set_host_nodes,
NULL, NULL, &error_abort); NULL, NULL, &error_abort);
object_class_property_set_description(oc, "host-nodes",
"Binds memory to the list of NUMA host nodes", &error_abort);
object_class_property_add_enum(oc, "policy", "HostMemPolicy", object_class_property_add_enum(oc, "policy", "HostMemPolicy",
&HostMemPolicy_lookup, &HostMemPolicy_lookup,
host_memory_backend_get_policy, host_memory_backend_get_policy,
host_memory_backend_set_policy, &error_abort); host_memory_backend_set_policy, &error_abort);
object_class_property_set_description(oc, "policy", object_class_property_add_str(oc, "id", get_id, set_id, &error_abort);
"Set the NUMA policy", &error_abort);
object_class_property_add_bool(oc, "share", object_class_property_add_bool(oc, "share",
host_memory_backend_get_share, host_memory_backend_set_share, host_memory_backend_get_share, host_memory_backend_set_share,
&error_abort); &error_abort);
object_class_property_set_description(oc, "share", }
"Mark the memory as private to QEMU or shared", &error_abort);
object_class_property_add_bool(oc, "x-use-canonical-path-for-ramblock-id", static void host_memory_backend_finalize(Object *o)
host_memory_backend_get_use_canonical_path, {
host_memory_backend_set_use_canonical_path, &error_abort); HostMemoryBackend *backend = MEMORY_BACKEND(o);
g_free(backend->id);
} }
static const TypeInfo host_memory_backend_info = { static const TypeInfo host_memory_backend_info = {
@@ -491,7 +436,7 @@ static const TypeInfo host_memory_backend_info = {
.class_init = host_memory_backend_class_init, .class_init = host_memory_backend_class_init,
.instance_size = sizeof(HostMemoryBackend), .instance_size = sizeof(HostMemoryBackend),
.instance_init = host_memory_backend_init, .instance_init = host_memory_backend_init,
.instance_post_init = host_memory_backend_post_init, .instance_finalize = host_memory_backend_finalize,
.interfaces = (InterfaceInfo[]) { .interfaces = (InterfaceInfo[]) {
{ TYPE_USER_CREATABLE }, { TYPE_USER_CREATABLE },
{ } { }

View File

@@ -15,7 +15,6 @@
#include "chardev/char-fe.h" #include "chardev/char-fe.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
#include "qemu/module.h"
#define TYPE_RNG_EGD "rng-egd" #define TYPE_RNG_EGD "rng-egd"
#define RNG_EGD(obj) OBJECT_CHECK(RngEgd, (obj), TYPE_RNG_EGD) #define RNG_EGD(obj) OBJECT_CHECK(RngEgd, (obj), TYPE_RNG_EGD)

View File

@@ -16,7 +16,6 @@
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#include "qemu/module.h"
struct RngRandom struct RngRandom
{ {
@@ -113,7 +112,7 @@ static void rng_random_init(Object *obj)
rng_random_set_filename, rng_random_set_filename,
NULL); NULL);
s->filename = g_strdup("/dev/urandom"); s->filename = g_strdup("/dev/random");
s->fd = -1; s->fd = -1;
} }

View File

@@ -14,7 +14,6 @@
#include "sysemu/rng.h" #include "sysemu/rng.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
#include "qemu/module.h"
#include "qom/object_interfaces.h" #include "qom/object_interfaces.h"
void rng_backend_request_entropy(RngBackend *s, size_t size, void rng_backend_request_entropy(RngBackend *s, size_t size,

View File

@@ -18,7 +18,6 @@
#include "sysemu/tpm.h" #include "sysemu/tpm.h"
#include "qemu/thread.h" #include "qemu/thread.h"
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#include "qemu/module.h"
#include "block/thread-pool.h" #include "block/thread-pool.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"

View File

@@ -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);

View File

@@ -25,7 +25,7 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/atomic.h" #include "qemu-common.h"
#include "exec/cpu-common.h" #include "exec/cpu-common.h"
#include "sysemu/kvm.h" #include "sysemu/kvm.h"
#include "sysemu/balloon.h" #include "sysemu/balloon.h"
@@ -37,22 +37,16 @@
static QEMUBalloonEvent *balloon_event_fn; static QEMUBalloonEvent *balloon_event_fn;
static QEMUBalloonStatus *balloon_stat_fn; static QEMUBalloonStatus *balloon_stat_fn;
static void *balloon_opaque; static void *balloon_opaque;
static int balloon_inhibit_count; static bool balloon_inhibited;
bool qemu_balloon_is_inhibited(void) bool qemu_balloon_is_inhibited(void)
{ {
return atomic_read(&balloon_inhibit_count) > 0; return balloon_inhibited;
} }
void qemu_balloon_inhibit(bool state) void qemu_balloon_inhibit(bool state)
{ {
if (state) { balloon_inhibited = state;
atomic_inc(&balloon_inhibit_count);
} else {
atomic_dec(&balloon_inhibit_count);
}
assert(atomic_read(&balloon_inhibit_count) >= 0);
} }
static bool have_balloon(Error **errp) static bool have_balloon(Error **errp)

2484
block.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,10 @@
block-obj-y += raw-format.o vmdk.o vpc.o block-obj-y += raw-format.o qcow.o vdi.o vmdk.o cloop.o bochs.o vpc.o vvfat.o dmg.o
block-obj-$(CONFIG_QCOW1) += qcow.o block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o
block-obj-$(CONFIG_VDI) += vdi.o block-obj-y += qed.o qed-l2-cache.o qed-table.o qed-cluster.o
block-obj-$(CONFIG_CLOOP) += cloop.o block-obj-y += qed-check.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 += vhdx.o vhdx-endian.o vhdx-log.o block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o
block-obj-y += quorum.o block-obj-y += quorum.o
block-obj-y += blkdebug.o blkverify.o blkreplay.o block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o
block-obj-$(CONFIG_PARALLELS) += parallels.o
block-obj-y += blklogwrites.o
block-obj-y += block-backend.o snapshot.o qapi.o block-obj-y += block-backend.o snapshot.o qapi.o
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
block-obj-$(CONFIG_POSIX) += file-posix.o block-obj-$(CONFIG_POSIX) += file-posix.o
@@ -22,8 +13,7 @@ block-obj-y += null.o mirror.o commit.o io.o create.o
block-obj-y += throttle-groups.o block-obj-y += throttle-groups.o
block-obj-$(CONFIG_LINUX) += nvme.o block-obj-$(CONFIG_LINUX) += nvme.o
block-obj-y += nbd.o block-obj-y += nbd.o nbd-client.o sheepdog.o
block-obj-$(CONFIG_SHEEPDOG) += sheepdog.o
block-obj-$(CONFIG_LIBISCSI) += iscsi.o block-obj-$(CONFIG_LIBISCSI) += iscsi.o
block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o
block-obj-$(CONFIG_LIBNFS) += nfs.o block-obj-$(CONFIG_LIBNFS) += nfs.o
@@ -31,12 +21,12 @@ block-obj-$(CONFIG_CURL) += curl.o
block-obj-$(CONFIG_RBD) += rbd.o block-obj-$(CONFIG_RBD) += rbd.o
block-obj-$(CONFIG_GLUSTERFS) += gluster.o block-obj-$(CONFIG_GLUSTERFS) += gluster.o
block-obj-$(CONFIG_VXHS) += vxhs.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 += accounting.o dirty-bitmap.o
block-obj-y += write-threshold.o block-obj-y += write-threshold.o
block-obj-y += backup.o block-obj-y += backup.o
block-obj-$(CONFIG_REPLICATION) += replication.o block-obj-$(CONFIG_REPLICATION) += replication.o
block-obj-y += throttle.o copy-on-read.o block-obj-y += throttle.o
block-obj-y += crypto.o block-obj-y += crypto.o
@@ -52,13 +42,10 @@ rbd.o-libs := $(RBD_LIBS)
gluster.o-cflags := $(GLUSTERFS_CFLAGS) gluster.o-cflags := $(GLUSTERFS_CFLAGS)
gluster.o-libs := $(GLUSTERFS_LIBS) gluster.o-libs := $(GLUSTERFS_LIBS)
vxhs.o-libs := $(VXHS_LIBS) vxhs.o-libs := $(VXHS_LIBS)
ssh.o-cflags := $(LIBSSH_CFLAGS) ssh.o-cflags := $(LIBSSH2_CFLAGS)
ssh.o-libs := $(LIBSSH_LIBS) ssh.o-libs := $(LIBSSH2_LIBS)
block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o
block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y)
dmg-bz2.o-libs := $(BZIP2_LIBS) dmg-bz2.o-libs := $(BZIP2_LIBS)
block-obj-$(if $(CONFIG_LZFSE),m,n) += dmg-lzfse.o
dmg-lzfse.o-libs := $(LZFSE_LIBS)
qcow.o-libs := -lz qcow.o-libs := -lz
linux-aio.o-libs := -laio linux-aio.o-libs := -laio
parallels.o-cflags := $(LIBXML2_CFLAGS) parallels.o-cflags := $(LIBXML2_CFLAGS)

View File

@@ -94,94 +94,6 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
cookie->type = type; cookie->type = type;
} }
/* block_latency_histogram_compare_func:
* Compare @key with interval [@it[0], @it[1]).
* Return: -1 if @key < @it[0]
* 0 if @key in [@it[0], @it[1])
* +1 if @key >= @it[1]
*/
static int block_latency_histogram_compare_func(const void *key, const void *it)
{
uint64_t k = *(uint64_t *)key;
uint64_t a = ((uint64_t *)it)[0];
uint64_t b = ((uint64_t *)it)[1];
return k < a ? -1 : (k < b ? 0 : 1);
}
static void block_latency_histogram_account(BlockLatencyHistogram *hist,
int64_t latency_ns)
{
uint64_t *pos;
if (hist->bins == NULL) {
/* histogram disabled */
return;
}
if (latency_ns < hist->boundaries[0]) {
hist->bins[0]++;
return;
}
if (latency_ns >= hist->boundaries[hist->nbins - 2]) {
hist->bins[hist->nbins - 1]++;
return;
}
pos = bsearch(&latency_ns, hist->boundaries, hist->nbins - 2,
sizeof(hist->boundaries[0]),
block_latency_histogram_compare_func);
assert(pos != NULL);
hist->bins[pos - hist->boundaries + 1]++;
}
int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type,
uint64List *boundaries)
{
BlockLatencyHistogram *hist = &stats->latency_histogram[type];
uint64List *entry;
uint64_t *ptr;
uint64_t prev = 0;
int new_nbins = 1;
for (entry = boundaries; entry; entry = entry->next) {
if (entry->value <= prev) {
return -EINVAL;
}
new_nbins++;
prev = entry->value;
}
hist->nbins = new_nbins;
g_free(hist->boundaries);
hist->boundaries = g_new(uint64_t, hist->nbins - 1);
for (entry = boundaries, ptr = hist->boundaries; entry;
entry = entry->next, ptr++)
{
*ptr = entry->value;
}
g_free(hist->bins);
hist->bins = g_new0(uint64_t, hist->nbins);
return 0;
}
void block_latency_histograms_clear(BlockAcctStats *stats)
{
int i;
for (i = 0; i < BLOCK_MAX_IOTYPE; i++) {
BlockLatencyHistogram *hist = &stats->latency_histogram[i];
g_free(hist->bins);
g_free(hist->boundaries);
memset(hist, 0, sizeof(*hist));
}
}
static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
bool failed) bool failed)
{ {
@@ -204,9 +116,6 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
stats->nr_ops[cookie->type]++; stats->nr_ops[cookie->type]++;
} }
block_latency_histogram_account(&stats->latency_histogram[cookie->type],
latency_ns);
if (!failed || stats->account_failed) { if (!failed || stats->account_failed) {
stats->total_time_ns[cookie->type] += latency_ns; stats->total_time_ns[cookie->type] += latency_ns;
stats->last_access_time_ns = time_ns; stats->last_access_time_ns = time_ns;

View File

@@ -27,13 +27,7 @@
#include "qemu/error-report.h" #include "qemu/error-report.h"
#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16) #define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
#define SLICE_TIME 100000000ULL /* ns */
typedef struct CowRequest {
int64_t start_byte;
int64_t end_byte;
QLIST_ENTRY(CowRequest) list;
CoQueue wait_queue; /* coroutines blocked on this request */
} CowRequest;
typedef struct BackupBlockJob { typedef struct BackupBlockJob {
BlockJob common; BlockJob common;
@@ -41,10 +35,10 @@ typedef struct BackupBlockJob {
/* bitmap for sync=incremental */ /* bitmap for sync=incremental */
BdrvDirtyBitmap *sync_bitmap; BdrvDirtyBitmap *sync_bitmap;
MirrorSyncMode sync_mode; MirrorSyncMode sync_mode;
RateLimit limit;
BlockdevOnError on_source_error; BlockdevOnError on_source_error;
BlockdevOnError on_target_error; BlockdevOnError on_target_error;
CoRwlock flush_rwlock; CoRwlock flush_rwlock;
uint64_t len;
uint64_t bytes_read; uint64_t bytes_read;
int64_t cluster_size; int64_t cluster_size;
bool compress; bool compress;
@@ -52,14 +46,8 @@ typedef struct BackupBlockJob {
QLIST_HEAD(, CowRequest) inflight_reqs; QLIST_HEAD(, CowRequest) inflight_reqs;
HBitmap *copy_bitmap; HBitmap *copy_bitmap;
bool use_copy_range;
int64_t copy_range_size;
bool serialize_target_writes;
} BackupBlockJob; } BackupBlockJob;
static const BlockJobDriver backup_job_driver;
/* See if in-flight requests overlap and wait for them to complete */ /* See if in-flight requests overlap and wait for them to complete */
static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job, static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
int64_t start, int64_t start,
@@ -97,99 +85,19 @@ static void cow_request_end(CowRequest *req)
qemu_co_queue_restart_all(&req->wait_queue); qemu_co_queue_restart_all(&req->wait_queue);
} }
/* Copy range to target with a bounce buffer and return the bytes copied. If
* error occurred, return a negative error number */
static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
int64_t start,
int64_t end,
bool is_write_notifier,
bool *error_is_read,
void **bounce_buffer)
{
int ret;
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, static int coroutine_fn backup_do_cow(BackupBlockJob *job,
int64_t offset, uint64_t bytes, int64_t offset, uint64_t bytes,
bool *error_is_read, bool *error_is_read,
bool is_write_notifier) bool is_write_notifier)
{ {
BlockBackend *blk = job->common.blk;
CowRequest cow_request; CowRequest cow_request;
struct iovec iov;
QEMUIOVector bounce_qiov;
void *bounce_buffer = NULL;
int ret = 0; int ret = 0;
int64_t start, end; /* bytes */ int64_t start, end; /* bytes */
void *bounce_buffer = NULL; int n; /* bytes */
qemu_co_rwlock_rdlock(&job->flush_rwlock); qemu_co_rwlock_rdlock(&job->flush_rwlock);
@@ -201,38 +109,60 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
wait_for_overlapping_requests(job, start, end); wait_for_overlapping_requests(job, start, end);
cow_request_begin(&cow_request, job, start, end); cow_request_begin(&cow_request, job, start, end);
while (start < end) { for (; start < end; start += job->cluster_size) {
if (!hbitmap_get(job->copy_bitmap, start)) { if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) {
trace_backup_do_cow_skip(job, start); trace_backup_do_cow_skip(job, start);
start += job->cluster_size;
continue; /* already copied */ continue; /* already copied */
} }
hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1);
trace_backup_do_cow_process(job, start); trace_backup_do_cow_process(job, start);
if (job->use_copy_range) { n = MIN(job->cluster_size, job->common.len - start);
ret = backup_cow_with_offload(job, start, end, is_write_notifier);
if (ret < 0) { if (!bounce_buffer) {
job->use_copy_range = false; bounce_buffer = blk_blockalign(blk, job->cluster_size);
}
} }
if (!job->use_copy_range) { iov.iov_base = bounce_buffer;
ret = backup_cow_with_bounce_buffer(job, start, end, is_write_notifier, iov.iov_len = n;
error_is_read, &bounce_buffer); qemu_iovec_init_external(&bounce_qiov, &iov, 1);
ret = blk_co_preadv(blk, start, bounce_qiov.size, &bounce_qiov,
is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0);
if (ret < 0) {
trace_backup_do_cow_read_fail(job, start, ret);
if (error_is_read) {
*error_is_read = true;
}
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
goto out;
}
if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
ret = blk_co_pwrite_zeroes(job->target, start,
bounce_qiov.size, BDRV_REQ_MAY_UNMAP);
} else {
ret = blk_co_pwritev(job->target, start,
bounce_qiov.size, &bounce_qiov,
job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
} }
if (ret < 0) { if (ret < 0) {
break; trace_backup_do_cow_write_fail(job, start, ret);
if (error_is_read) {
*error_is_read = false;
}
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
goto out;
} }
/* Publish progress, guest I/O counts as progress too. Note that the /* Publish progress, guest I/O counts as progress too. Note that the
* offset field is an opaque progress value, it is not a disk offset. * offset field is an opaque progress value, it is not a disk offset.
*/ */
start += ret; job->bytes_read += n;
job->bytes_read += ret; job->common.offset += n;
job_progress_update(&job->common.job, ret);
ret = 0;
} }
out:
if (bounce_buffer) { if (bounce_buffer) {
qemu_vfree(bounce_buffer); qemu_vfree(bounce_buffer);
} }
@@ -260,12 +190,23 @@ static int coroutine_fn backup_before_write_notify(
return backup_do_cow(job, req->offset, req->bytes, NULL, true); return backup_do_cow(job, req->offset, req->bytes, NULL, true);
} }
static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
if (speed < 0) {
error_setg(errp, QERR_INVALID_PARAMETER, "speed");
return;
}
ratelimit_set_speed(&s->limit, speed, SLICE_TIME);
}
static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
{ {
BdrvDirtyBitmap *bm; BdrvDirtyBitmap *bm;
BlockDriverState *bs = blk_bs(job->common.blk); BlockDriverState *bs = blk_bs(job->common.blk);
if (ret < 0) { if (ret < 0 || block_job_is_cancelled(&job->common)) {
/* Merge the successor back into the parent, delete nothing. */ /* Merge the successor back into the parent, delete nothing. */
bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL); bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL);
assert(bm); assert(bm);
@@ -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) { if (s->sync_bitmap) {
backup_cleanup_sync_bitmap(s, 0); backup_cleanup_sync_bitmap(s, 0);
} }
} }
static void backup_abort(Job *job) static void backup_abort(BlockJob *job)
{ {
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); BackupBlockJob *s = container_of(job, BackupBlockJob, common);
if (s->sync_bitmap) { if (s->sync_bitmap) {
backup_cleanup_sync_bitmap(s, -1); backup_cleanup_sync_bitmap(s, -1);
} }
} }
static void backup_clean(Job *job) static void backup_clean(BlockJob *job)
{ {
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); BackupBlockJob *s = container_of(job, BackupBlockJob, common);
assert(s->target); assert(s->target);
blk_unref(s->target); blk_unref(s->target);
s->target = NULL; s->target = NULL;
}
if (s->copy_bitmap) { static void backup_attached_aio_context(BlockJob *job, AioContext *aio_context)
hbitmap_free(s->copy_bitmap); {
s->copy_bitmap = NULL; BackupBlockJob *s = container_of(job, BackupBlockJob, common);
}
blk_set_aio_context(s->target, aio_context);
} }
void backup_do_checkpoint(BlockJob *job, Error **errp) void backup_do_checkpoint(BlockJob *job, Error **errp)
{ {
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
int64_t len;
assert(block_job_driver(job) == &backup_job_driver); assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP);
if (backup_job->sync_mode != MIRROR_SYNC_MODE_NONE) { if (backup_job->sync_mode != MIRROR_SYNC_MODE_NONE) {
error_setg(errp, "The backup job only supports block checkpoint in" error_setg(errp, "The backup job only supports block checkpoint in"
@@ -317,7 +261,39 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
return; 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) 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) static bool coroutine_fn yield_and_check(BackupBlockJob *job)
{ {
uint64_t delay_ns; if (block_job_is_cancelled(&job->common)) {
if (job_is_cancelled(&job->common.job)) {
return true; return true;
} }
/* We need to yield even for delay_ns = 0 so that bdrv_drain_all() can /* we need to yield so that bdrv_drain_all() returns.
* return. Without a yield, the VM would not reboot. */ * (without, VM does not reboot)
delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read); */
job->bytes_read = 0; if (job->common.speed) {
job_sleep_ns(&job->common.job, delay_ns); uint64_t delay_ns = ratelimit_calculate_delay(&job->limit,
job->bytes_read);
job->bytes_read = 0;
block_job_sleep_ns(&job->common, delay_ns);
} else {
block_job_sleep_ns(&job->common, 0);
}
if (job_is_cancelled(&job->common.job)) { if (block_job_is_cancelled(&job->common)) {
return true; return true;
} }
return false; return false;
} }
static bool bdrv_is_unallocated_range(BlockDriverState *bs, static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
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)
{ {
int ret; int ret;
bool error_is_read; bool error_is_read;
int64_t offset; int64_t cluster;
HBitmapIter hbi; HBitmapIter hbi;
BlockDriverState *bs = blk_bs(job->common.blk);
hbitmap_iter_init(&hbi, job->copy_bitmap, 0); hbitmap_iter_init(&hbi, job->copy_bitmap, 0);
while ((offset = hbitmap_iter_next(&hbi)) != -1) { while ((cluster = 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;
}
do { do {
if (yield_and_check(job)) { if (yield_and_check(job)) {
return 0; return 0;
} }
ret = backup_do_cow(job, offset, ret = backup_do_cow(job, cluster * job->cluster_size,
job->cluster_size, &error_is_read, false); job->cluster_size, &error_is_read, false);
if (ret < 0 && backup_error_action(job, error_is_read, -ret) == if (ret < 0 && backup_error_action(job, error_is_read, -ret) ==
BLOCK_ERROR_ACTION_REPORT) BLOCK_ERROR_ACTION_REPORT)
@@ -421,118 +389,162 @@ static int coroutine_fn backup_loop(BackupBlockJob *job)
/* init copy_bitmap from sync_bitmap */ /* init copy_bitmap from sync_bitmap */
static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
{ {
uint64_t offset = 0; BdrvDirtyBitmapIter *dbi;
uint64_t bytes = job->len; 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, dbi = bdrv_dirty_iter_new(job->sync_bitmap);
&offset, &bytes)) while ((offset = bdrv_dirty_iter_next(dbi)) != -1) {
{ int64_t cluster = offset / job->cluster_size;
hbitmap_set(job->copy_bitmap, offset, bytes); int64_t next_cluster;
offset += bytes; offset += bdrv_dirty_bitmap_granularity(job->sync_bitmap);
if (offset >= job->len) { if (offset >= bdrv_dirty_bitmap_size(job->sync_bitmap)) {
hbitmap_set(job->copy_bitmap, cluster, end - cluster);
break; 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->common.offset = job->common.len -
job_progress_update(&job->common.job, hbitmap_count(job->copy_bitmap) * job->cluster_size;
job->len - hbitmap_count(job->copy_bitmap));
bdrv_dirty_iter_free(dbi);
} }
static int coroutine_fn backup_run(Job *job, Error **errp) static void coroutine_fn backup_run(void *opaque)
{ {
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); BackupBlockJob *job = opaque;
BlockDriverState *bs = blk_bs(s->common.blk); BackupCompleteData *data;
BlockDriverState *bs = blk_bs(job->common.blk);
int64_t offset, nb_clusters;
int ret = 0; int ret = 0;
QLIST_INIT(&s->inflight_reqs); QLIST_INIT(&job->inflight_reqs);
qemu_co_rwlock_init(&s->flush_rwlock); qemu_co_rwlock_init(&job->flush_rwlock);
job_progress_set_remaining(job, s->len); nb_clusters = DIV_ROUND_UP(job->common.len, job->cluster_size);
job->copy_bitmap = hbitmap_alloc(nb_clusters, 0);
if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
backup_incremental_init_copy_bitmap(s); backup_incremental_init_copy_bitmap(job);
} else { } 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. /* All bits are set in copy_bitmap to allow any cluster to be copied.
* This does not actually require them to be copied. */ * This does not actually require them to be copied. */
while (!job_is_cancelled(job)) { while (!block_job_is_cancelled(&job->common)) {
/* Yield until the job is cancelled. We just let our before_write /* Yield until the job is cancelled. We just let our before_write
* notify callback service CoW requests. */ * notify callback service CoW requests. */
job_yield(job); block_job_yield(&job->common);
} }
} else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
ret = backup_run_incremental(job);
} else { } 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 */ /* wait until pending backup_do_cow() calls have completed */
qemu_co_rwlock_wrlock(&s->flush_rwlock); qemu_co_rwlock_wrlock(&job->flush_rwlock);
qemu_co_rwlock_unlock(&s->flush_rwlock); qemu_co_rwlock_unlock(&job->flush_rwlock);
hbitmap_free(job->copy_bitmap);
return ret; data = g_malloc(sizeof(*data));
data->ret = ret;
block_job_defer_to_main_loop(&job->common, backup_complete, data);
} }
static const BlockJobDriver backup_job_driver = { static const BlockJobDriver backup_job_driver = {
.job_driver = { .instance_size = sizeof(BackupBlockJob),
.instance_size = sizeof(BackupBlockJob), .job_type = BLOCK_JOB_TYPE_BACKUP,
.job_type = JOB_TYPE_BACKUP, .start = backup_run,
.free = block_job_free, .set_speed = backup_set_speed,
.user_resume = block_job_user_resume, .commit = backup_commit,
.drain = block_job_drain, .abort = backup_abort,
.run = backup_run, .clean = backup_clean,
.commit = backup_commit, .attached_aio_context = backup_attached_aio_context,
.abort = backup_abort,
.clean = backup_clean,
},
.drain = backup_drain, .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, BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
BlockDriverState *target, int64_t speed, BlockDriverState *target, int64_t speed,
MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap, MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
@@ -541,13 +553,12 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
BlockdevOnError on_target_error, BlockdevOnError on_target_error,
int creation_flags, int creation_flags,
BlockCompletionFunc *cb, void *opaque, BlockCompletionFunc *cb, void *opaque,
JobTxn *txn, Error **errp) BlockJobTxn *txn, Error **errp)
{ {
int64_t len; int64_t len;
BlockDriverInfo bdi;
BackupBlockJob *job = NULL; BackupBlockJob *job = NULL;
int ret; int ret;
int64_t cluster_size;
HBitmap *copy_bitmap = NULL;
assert(bs); assert(bs);
assert(target); assert(target);
@@ -609,15 +620,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
goto error; goto error;
} }
cluster_size = backup_calculate_cluster_size(target, errp); /* job->common.len is fixed, so we can't allow resize */
if (cluster_size < 0) { job = block_job_create(job_id, &backup_job_driver, bs,
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,
BLK_PERM_CONSISTENT_READ, BLK_PERM_CONSISTENT_READ,
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD, BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD,
@@ -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 */ /* The target must match the source in size, so no resize here either */
job->target = blk_new(job->common.job.aio_context, job->target = blk_new(BLK_PERM_WRITE,
BLK_PERM_WRITE,
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD); BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD);
ret = blk_insert_bs(job->target, target, errp); 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; sync_bitmap : NULL;
job->compress = compress; job->compress = compress;
/* Detect image-fleecing (and similar) schemes */ /* If there is no backing file on the target, we cannot rely on COW if our
job->serialize_target_writes = bdrv_chain_contains(target, bs); * backup cluster size is smaller than the target cluster size. Even for
job->cluster_size = cluster_size; * targets with a backing file, try to avoid COW if possible. */
job->copy_bitmap = copy_bitmap; ret = bdrv_get_info(target, &bdi);
copy_bitmap = NULL; if (ret == -ENOTSUP && !target->backing) {
job->use_copy_range = true; /* Cluster size is not defined */
job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk), warn_report("The target block device doesn't provide "
blk_get_max_transfer(job->target)); "information about the block size and it doesn't have a "
job->copy_range_size = MAX(job->cluster_size, "backing file. The default block size of %u bytes is "
QEMU_ALIGN_UP(job->copy_range_size, "used. If the actual block size of the target exceeds "
job->cluster_size)); "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() */ /* Required permissions are already taken with target's blk_new() */
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
&error_abort); &error_abort);
job->len = len; job->common.len = len;
block_job_txn_add_job(txn, &job->common);
return &job->common; return &job->common;
error: error:
if (copy_bitmap) {
assert(!job || !job->copy_bitmap);
hbitmap_free(copy_bitmap);
}
if (sync_bitmap) { if (sync_bitmap) {
bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL); bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL);
} }
if (job) { if (job) {
backup_clean(&job->common.job); backup_clean(&job->common);
job_early_fail(&job->common.job); block_job_early_fail(&job->common);
} }
return NULL; return NULL;

View File

@@ -75,7 +75,6 @@ typedef struct BlkdebugRule {
int state; int state;
union { union {
struct { struct {
uint64_t iotype_mask;
int error; int error;
int immediately; int immediately;
int once; int once;
@@ -92,9 +91,6 @@ typedef struct BlkdebugRule {
QSIMPLEQ_ENTRY(BlkdebugRule) active_next; QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
} BlkdebugRule; } BlkdebugRule;
QEMU_BUILD_BUG_MSG(BLKDEBUG_IO_TYPE__MAX > 64,
"BlkdebugIOType mask does not fit into an uint64_t");
static QemuOptsList inject_error_opts = { static QemuOptsList inject_error_opts = {
.name = "inject-error", .name = "inject-error",
.head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head), .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head),
@@ -107,10 +103,6 @@ static QemuOptsList inject_error_opts = {
.name = "state", .name = "state",
.type = QEMU_OPT_NUMBER, .type = QEMU_OPT_NUMBER,
}, },
{
.name = "iotype",
.type = QEMU_OPT_STRING,
},
{ {
.name = "errno", .name = "errno",
.type = QEMU_OPT_NUMBER, .type = QEMU_OPT_NUMBER,
@@ -170,8 +162,6 @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
int event; int event;
struct BlkdebugRule *rule; struct BlkdebugRule *rule;
int64_t sector; int64_t sector;
BlkdebugIOType iotype;
Error *local_error = NULL;
/* Find the right event for the rule */ /* Find the right event for the rule */
event_name = qemu_opt_get(opts, "event"); 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); sector = qemu_opt_get_number(opts, "sector", -1);
rule->options.inject.offset = rule->options.inject.offset =
sector == -1 ? -1 : sector * BDRV_SECTOR_SIZE; 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; break;
case ACTION_SET_STATE: case ACTION_SET_STATE:
@@ -335,7 +305,7 @@ static void blkdebug_parse_filename(const char *filename, QDict *options,
if (c != filename) { if (c != filename) {
QString *config_path; QString *config_path;
config_path = qstring_from_substr(filename, 0, c - filename); config_path = qstring_from_substr(filename, 0, c - filename - 1);
qdict_put(options, "config", config_path); qdict_put(options, "config", config_path);
} }
@@ -428,11 +398,10 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
goto out; goto out;
} }
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | bs->supported_write_flags = BDRV_REQ_FUA &
(BDRV_REQ_FUA & bs->file->bs->supported_write_flags); bs->file->bs->supported_write_flags;
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & bs->file->bs->supported_zero_flags;
bs->file->bs->supported_zero_flags);
ret = -EINVAL; ret = -EINVAL;
/* Set alignment overrides */ /* Set alignment overrides */
@@ -491,8 +460,6 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
goto out; goto out;
} }
bdrv_debug_event(bs, BLKDBG_NONE);
ret = 0; ret = 0;
out: out:
if (ret < 0) { if (ret < 0) {
@@ -502,8 +469,7 @@ out:
return ret; return ret;
} }
static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes, static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes)
BlkdebugIOType iotype)
{ {
BDRVBlkdebugState *s = bs->opaque; BDRVBlkdebugState *s = bs->opaque;
BlkdebugRule *rule = NULL; 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) { QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
uint64_t inject_offset = rule->options.inject.offset; uint64_t inject_offset = rule->options.inject.offset;
if ((inject_offset == -1 || if (inject_offset == -1 ||
(bytes && inject_offset >= offset && (bytes && inject_offset >= offset &&
inject_offset < offset + bytes)) && inject_offset < offset + bytes))
(rule->options.inject.iotype_mask & (1ull << iotype)))
{ {
break; break;
} }
@@ -555,7 +520,7 @@ blkdebug_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
assert(bytes <= bs->bl.max_transfer); assert(bytes <= bs->bl.max_transfer);
} }
err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_READ); err = rule_check(bs, offset, bytes);
if (err) { if (err) {
return err; return err;
} }
@@ -576,7 +541,7 @@ blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
assert(bytes <= bs->bl.max_transfer); assert(bytes <= bs->bl.max_transfer);
} }
err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_WRITE); err = rule_check(bs, offset, bytes);
if (err) { if (err) {
return 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) 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) { if (err) {
return err; return err;
@@ -620,7 +585,7 @@ static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs,
assert(bytes <= bs->bl.max_pwrite_zeroes); 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) { if (err) {
return err; return err;
} }
@@ -654,12 +619,12 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
assert(bytes <= bs->bl.max_pdiscard); assert(bytes <= bs->bl.max_pdiscard);
} }
err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_DISCARD); err = rule_check(bs, offset, bytes);
if (err) { if (err) {
return err; return err;
} }
return bdrv_co_pdiscard(bs->file, offset, bytes); return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
} }
static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs, static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs,
@@ -670,15 +635,7 @@ static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs,
int64_t *map, int64_t *map,
BlockDriverState **file) BlockDriverState **file)
{ {
int err;
assert(QEMU_IS_ALIGNED(offset | bytes, bs->bl.request_alignment)); 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, return bdrv_co_block_status_from_file(bs, want_zero, offset, bytes,
pnum, map, file); pnum, map, file);
} }
@@ -853,37 +810,52 @@ static int64_t blkdebug_getlength(BlockDriverState *bs)
return bdrv_getlength(bs->file->bs); return bdrv_getlength(bs->file->bs);
} }
static void blkdebug_refresh_filename(BlockDriverState *bs) static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
{ {
BDRVBlkdebugState *s = bs->opaque; BDRVBlkdebugState *s = bs->opaque;
QDict *opts;
const QDictEntry *e; const QDictEntry *e;
int ret; bool force_json = false;
if (!bs->file->bs->exact_filename[0]) { for (e = qdict_first(options); e; e = qdict_next(options, e)) {
return;
}
for (e = qdict_first(bs->full_open_options); e;
e = qdict_next(bs->full_open_options, e))
{
/* Real child options are under "image", but "x-image" may
* contain a filename */
if (strcmp(qdict_entry_key(e), "config") && if (strcmp(qdict_entry_key(e), "config") &&
strcmp(qdict_entry_key(e), "image") && strcmp(qdict_entry_key(e), "x-image"))
strcmp(qdict_entry_key(e), "x-image") &&
strcmp(qdict_entry_key(e), "driver"))
{ {
return; force_json = true;
break;
} }
} }
ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename), if (force_json && !bs->file->bs->full_open_options) {
"blkdebug:%s:%s", /* The config file cannot be recreated, so creating a plain filename
s->config_file ?: "", bs->file->bs->exact_filename); * is impossible */
if (ret >= sizeof(bs->exact_filename)) { return;
/* An overflow makes the filename unusable, so do not report any */
bs->exact_filename[0] = 0;
} }
if (!force_json && bs->file->bs->exact_filename[0]) {
int ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
"blkdebug:%s:%s", s->config_file ?: "",
bs->file->bs->exact_filename);
if (ret >= sizeof(bs->exact_filename)) {
/* An overflow makes the filename unusable, so do not report any */
bs->exact_filename[0] = 0;
}
}
opts = qdict_new();
qdict_put_str(opts, "driver", "blkdebug");
QINCREF(bs->file->bs->full_open_options);
qdict_put(opts, "image", bs->file->bs->full_open_options);
for (e = qdict_first(options); e; e = qdict_next(options, e)) {
if (strcmp(qdict_entry_key(e), "x-image")) {
qobject_incref(qdict_entry_value(e));
qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e));
}
}
bs->full_open_options = opts;
} }
static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp) static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp)
@@ -916,20 +888,6 @@ static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
return 0; return 0;
} }
static const char *const blkdebug_strong_runtime_opts[] = {
"config",
"inject-error.",
"set-state.",
"align",
"max-transfer",
"opt-write-zero",
"max-write-zero",
"opt-discard",
"max-discard",
NULL
};
static BlockDriver bdrv_blkdebug = { static BlockDriver bdrv_blkdebug = {
.format_name = "blkdebug", .format_name = "blkdebug",
.protocol_name = "blkdebug", .protocol_name = "blkdebug",
@@ -959,8 +917,6 @@ static BlockDriver bdrv_blkdebug = {
= blkdebug_debug_remove_breakpoint, = blkdebug_debug_remove_breakpoint,
.bdrv_debug_resume = blkdebug_debug_resume, .bdrv_debug_resume = blkdebug_debug_resume,
.bdrv_debug_is_suspended = blkdebug_debug_is_suspended, .bdrv_debug_is_suspended = blkdebug_debug_is_suspended,
.strong_runtime_opts = blkdebug_strong_runtime_opts,
}; };
static void bdrv_blkdebug_init(void) static void bdrv_blkdebug_init(void)

View File

@@ -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
View File

@@ -10,7 +10,7 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/module.h" #include "qemu-common.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "sysemu/replay.h" #include "sysemu/replay.h"
#include "qapi/error.h" #include "qapi/error.h"
@@ -35,14 +35,15 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags,
goto fail; goto fail;
} }
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED;
ret = 0; ret = 0;
fail: fail:
return ret; return ret;
} }
static void blkreplay_close(BlockDriverState *bs)
{
}
static int64_t blkreplay_getlength(BlockDriverState *bs) static int64_t blkreplay_getlength(BlockDriverState *bs)
{ {
return bdrv_getlength(bs->file->bs); return bdrv_getlength(bs->file->bs);
@@ -109,7 +110,7 @@ static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs,
int64_t offset, int bytes) int64_t offset, int bytes)
{ {
uint64_t reqid = blkreplay_next_id(); uint64_t reqid = blkreplay_next_id();
int ret = bdrv_co_pdiscard(bs->file, offset, bytes); int ret = bdrv_co_pdiscard(bs->file->bs, offset, bytes);
block_request_create(reqid, bs, qemu_coroutine_self()); block_request_create(reqid, bs, qemu_coroutine_self());
qemu_coroutine_yield(); qemu_coroutine_yield();
@@ -131,6 +132,7 @@ static BlockDriver bdrv_blkreplay = {
.instance_size = 0, .instance_size = 0,
.bdrv_open = blkreplay_open, .bdrv_open = blkreplay_open,
.bdrv_close = blkreplay_close,
.bdrv_child_perm = bdrv_filter_default_perms, .bdrv_child_perm = bdrv_filter_default_perms,
.bdrv_getlength = blkreplay_getlength, .bdrv_getlength = blkreplay_getlength,

View File

@@ -14,7 +14,6 @@
#include "qapi/qmp/qdict.h" #include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h" #include "qapi/qmp/qstring.h"
#include "qemu/cutils.h" #include "qemu/cutils.h"
#include "qemu/module.h"
#include "qemu/option.h" #include "qemu/option.h"
typedef struct { 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 */ /* TODO Implement option pass-through and set raw.filename here */
raw_path = qstring_from_substr(filename, 0, c - filename); raw_path = qstring_from_substr(filename, 0, c - filename - 1);
qdict_put(options, "x-raw", raw_path); qdict_put(options, "x-raw", raw_path);
/* TODO Allow multi-level nesting and set file.filename here */ /* TODO Allow multi-level nesting and set file.filename here */
@@ -142,9 +141,6 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
goto fail; goto fail;
} }
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED;
ret = 0; ret = 0;
fail: fail:
qemu_opts_del(opts); qemu_opts_del(opts);
@@ -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); return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate);
} }
static void blkverify_refresh_filename(BlockDriverState *bs) static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
{ {
BDRVBlkverifyState *s = bs->opaque; BDRVBlkverifyState *s = bs->opaque;
/* bs->file->bs has already been refreshed */
bdrv_refresh_filename(s->test_file->bs);
if (bs->file->bs->full_open_options
&& s->test_file->bs->full_open_options)
{
QDict *opts = qdict_new();
qdict_put_str(opts, "driver", "blkverify");
QINCREF(bs->file->bs->full_open_options);
qdict_put(opts, "raw", bs->file->bs->full_open_options);
QINCREF(s->test_file->bs->full_open_options);
qdict_put(opts, "test", s->test_file->bs->full_open_options);
bs->full_open_options = opts;
}
if (bs->file->bs->exact_filename[0] if (bs->file->bs->exact_filename[0]
&& s->test_file->bs->exact_filename[0]) && s->test_file->bs->exact_filename[0])
{ {
@@ -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 = { static BlockDriver bdrv_blkverify = {
.format_name = "blkverify", .format_name = "blkverify",
.protocol_name = "blkverify", .protocol_name = "blkverify",
@@ -320,7 +324,6 @@ static BlockDriver bdrv_blkverify = {
.bdrv_child_perm = bdrv_filter_default_perms, .bdrv_child_perm = bdrv_filter_default_perms,
.bdrv_getlength = blkverify_getlength, .bdrv_getlength = blkverify_getlength,
.bdrv_refresh_filename = blkverify_refresh_filename, .bdrv_refresh_filename = blkverify_refresh_filename,
.bdrv_dirname = blkverify_dirname,
.bdrv_co_preadv = blkverify_co_preadv, .bdrv_co_preadv = blkverify_co_preadv,
.bdrv_co_pwritev = blkverify_co_pwritev, .bdrv_co_pwritev = blkverify_co_pwritev,

View File

@@ -31,24 +31,18 @@
static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb); static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb);
typedef struct BlockBackendAioNotifier {
void (*attached_aio_context)(AioContext *new_context, void *opaque);
void (*detach_aio_context)(void *opaque);
void *opaque;
QLIST_ENTRY(BlockBackendAioNotifier) list;
} BlockBackendAioNotifier;
struct BlockBackend { struct BlockBackend {
char *name; char *name;
int refcnt; int refcnt;
BdrvChild *root; BdrvChild *root;
AioContext *ctx;
DriveInfo *legacy_dinfo; /* null unless created by drive_new() */ DriveInfo *legacy_dinfo; /* null unless created by drive_new() */
QTAILQ_ENTRY(BlockBackend) link; /* for block_backends */ QTAILQ_ENTRY(BlockBackend) link; /* for block_backends */
QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */ QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */
BlockBackendPublic public; BlockBackendPublic public;
DeviceState *dev; /* attached device model, if any */ void *dev; /* attached device model, if any */
bool legacy_dev; /* true if dev is not a DeviceState */
/* TODO change to DeviceState when all users are qdevified */
const BlockDevOps *dev_ops; const BlockDevOps *dev_ops;
void *dev_opaque; void *dev_opaque;
@@ -72,11 +66,9 @@ struct BlockBackend {
uint64_t shared_perm; uint64_t shared_perm;
bool disable_perm; bool disable_perm;
bool allow_aio_context_change;
bool allow_write_beyond_eof; bool allow_write_beyond_eof;
NotifierList remove_bs_notifiers, insert_bs_notifiers; NotifierList remove_bs_notifiers, insert_bs_notifiers;
QLIST_HEAD(, BlockBackendAioNotifier) aio_notifiers;
int quiesce_counter; int quiesce_counter;
VMChangeStateEntry *vmsh; VMChangeStateEntry *vmsh;
@@ -88,6 +80,7 @@ struct BlockBackend {
* Accessed with atomic ops. * Accessed with atomic ops.
*/ */
unsigned int in_flight; unsigned int in_flight;
AioWait wait;
}; };
typedef struct BlockBackendAIOCB { typedef struct BlockBackendAIOCB {
@@ -120,17 +113,11 @@ static void blk_root_inherit_options(int *child_flags, QDict *child_options,
abort(); abort();
} }
static void blk_root_drained_begin(BdrvChild *child); static void blk_root_drained_begin(BdrvChild *child);
static bool blk_root_drained_poll(BdrvChild *child); static void blk_root_drained_end(BdrvChild *child);
static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter);
static void blk_root_change_media(BdrvChild *child, bool load); static void blk_root_change_media(BdrvChild *child, bool load);
static void blk_root_resize(BdrvChild *child); 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) static char *blk_root_get_parent_desc(BdrvChild *child)
{ {
BlockBackend *blk = child->opaque; BlockBackend *blk = child->opaque;
@@ -260,36 +247,6 @@ static int blk_root_inactivate(BdrvChild *child)
return 0; return 0;
} }
static void blk_root_attach(BdrvChild *child)
{
BlockBackend *blk = child->opaque;
BlockBackendAioNotifier *notifier;
trace_blk_root_attach(child, blk, child->bs);
QLIST_FOREACH(notifier, &blk->aio_notifiers, list) {
bdrv_add_aio_context_notifier(child->bs,
notifier->attached_aio_context,
notifier->detach_aio_context,
notifier->opaque);
}
}
static void blk_root_detach(BdrvChild *child)
{
BlockBackend *blk = child->opaque;
BlockBackendAioNotifier *notifier;
trace_blk_root_detach(child, blk, child->bs);
QLIST_FOREACH(notifier, &blk->aio_notifiers, list) {
bdrv_remove_aio_context_notifier(child->bs,
notifier->attached_aio_context,
notifier->detach_aio_context,
notifier->opaque);
}
}
static const BdrvChildRole child_root = { static const BdrvChildRole child_root = {
.inherit_options = blk_root_inherit_options, .inherit_options = blk_root_inherit_options,
@@ -299,17 +256,10 @@ static const BdrvChildRole child_root = {
.get_parent_desc = blk_root_get_parent_desc, .get_parent_desc = blk_root_get_parent_desc,
.drained_begin = blk_root_drained_begin, .drained_begin = blk_root_drained_begin,
.drained_poll = blk_root_drained_poll,
.drained_end = blk_root_drained_end, .drained_end = blk_root_drained_end,
.activate = blk_root_activate, .activate = blk_root_activate,
.inactivate = blk_root_inactivate, .inactivate = blk_root_inactivate,
.attach = blk_root_attach,
.detach = blk_root_detach,
.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. * 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; BlockBackend *blk;
blk = g_new0(BlockBackend, 1); blk = g_new0(BlockBackend, 1);
blk->refcnt = 1; blk->refcnt = 1;
blk->ctx = ctx;
blk->perm = perm; blk->perm = perm;
blk->shared_perm = shared_perm; blk->shared_perm = shared_perm;
blk_set_enable_write_cache(blk, true); blk_set_enable_write_cache(blk, true);
blk->on_read_error = BLOCKDEV_ON_ERROR_REPORT;
blk->on_write_error = BLOCKDEV_ON_ERROR_ENOSPC;
block_acct_init(&blk->stats); block_acct_init(&blk->stats);
notifier_list_init(&blk->remove_bs_notifiers); notifier_list_init(&blk->remove_bs_notifiers);
notifier_list_init(&blk->insert_bs_notifiers); notifier_list_init(&blk->insert_bs_notifiers);
QLIST_INIT(&blk->aio_notifiers);
QTAILQ_INSERT_TAIL(&block_backends, blk, link); QTAILQ_INSERT_TAIL(&block_backends, blk, link);
return blk; return blk;
@@ -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. * 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 * Just as with bdrv_open(), after having called this function the reference to
* @options belongs to the block layer (even on failure). * @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; 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); bs = bdrv_open(filename, reference, options, flags, errp);
if (!bs) { if (!bs) {
blk_unref(blk); blk_unref(blk);
return NULL; 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); perm, BLK_PERM_ALL, blk, errp);
if (!blk->root) { if (!blk->root) {
bdrv_unref(bs);
blk_unref(blk); blk_unref(blk);
return NULL; return NULL;
} }
@@ -419,7 +364,6 @@ static void blk_delete(BlockBackend *blk)
} }
assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers)); assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers));
assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers)); assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers));
assert(QLIST_EMPTY(&blk->aio_notifiers));
QTAILQ_REMOVE(&block_backends, blk, link); QTAILQ_REMOVE(&block_backends, blk, link);
drive_info_del(blk->legacy_dinfo); drive_info_del(blk->legacy_dinfo);
block_acct_cleanup(&blk->stats); block_acct_cleanup(&blk->stats);
@@ -432,6 +376,7 @@ static void drive_info_del(DriveInfo *dinfo)
return; return;
} }
qemu_opts_del(dinfo->opts); qemu_opts_del(dinfo->opts);
g_free(dinfo->serial);
g_free(dinfo); g_free(dinfo);
} }
@@ -446,7 +391,6 @@ int blk_get_refcnt(BlockBackend *blk)
*/ */
void blk_ref(BlockBackend *blk) void blk_ref(BlockBackend *blk)
{ {
assert(blk->refcnt > 0);
blk->refcnt++; blk->refcnt++;
} }
@@ -459,13 +403,7 @@ void blk_unref(BlockBackend *blk)
{ {
if (blk) { if (blk) {
assert(blk->refcnt > 0); assert(blk->refcnt > 0);
if (blk->refcnt > 1) { if (!--blk->refcnt) {
blk->refcnt--;
} else {
blk_drain(blk);
/* blk_drain() cannot resurrect blk, nobody held a reference */
assert(blk->refcnt == 1);
blk->refcnt = 0;
blk_delete(blk); blk_delete(blk);
} }
} }
@@ -787,11 +725,6 @@ void blk_remove_bs(BlockBackend *blk)
blk_update_root_state(blk); blk_update_root_state(blk);
/* bdrv_root_unref_child() will cause blk->root to become stale and may
* switch to a completion coroutine later on. Let's drain all I/O here
* to avoid that and a potential QEMU crash.
*/
blk_drain(blk);
bdrv_root_unref_child(blk->root); bdrv_root_unref_child(blk->root);
blk->root = NULL; blk->root = NULL;
} }
@@ -802,12 +735,12 @@ void blk_remove_bs(BlockBackend *blk)
int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
{ {
ThrottleGroupMember *tgm = &blk->public.throttle_group_member; ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
bdrv_ref(bs); blk->root = bdrv_root_attach_child(bs, "root", &child_root,
blk->root = bdrv_root_attach_child(bs, "root", &child_root, blk->ctx,
blk->perm, blk->shared_perm, blk, errp); blk->perm, blk->shared_perm, blk, errp);
if (blk->root == NULL) { if (blk->root == NULL) {
return -EPERM; return -EPERM;
} }
bdrv_ref(bs);
notifier_list_notify(&blk->insert_bs_notifiers, blk); notifier_list_notify(&blk->insert_bs_notifiers, blk);
if (tgm->throttle_state) { 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; *shared_perm = blk->shared_perm;
} }
/* static int blk_do_attach_dev(BlockBackend *blk, void *dev)
* Attach device model @dev to @blk.
* Return 0 on success, -EBUSY when a device model is attached already.
*/
int blk_attach_dev(BlockBackend *blk, DeviceState *dev)
{ {
if (blk->dev) { if (blk->dev) {
return -EBUSY; return -EBUSY;
@@ -864,16 +793,40 @@ int blk_attach_dev(BlockBackend *blk, DeviceState *dev)
blk_ref(blk); blk_ref(blk);
blk->dev = dev; blk->dev = dev;
blk->legacy_dev = false;
blk_iostatus_reset(blk); blk_iostatus_reset(blk);
return 0; return 0;
} }
/*
* Attach device model @dev to @blk.
* Return 0 on success, -EBUSY when a device model is attached already.
*/
int blk_attach_dev(BlockBackend *blk, DeviceState *dev)
{
return blk_do_attach_dev(blk, dev);
}
/*
* Attach device model @dev to @blk.
* @blk must not have a device model attached already.
* TODO qdevified devices don't use this, remove when devices are qdevified
*/
void blk_attach_dev_legacy(BlockBackend *blk, void *dev)
{
if (blk_do_attach_dev(blk, dev) < 0) {
abort();
}
blk->legacy_dev = true;
}
/* /*
* Detach device model @dev from @blk. * Detach device model @dev from @blk.
* @dev must be currently attached to @blk. * @dev must be currently attached to @blk.
*/ */
void blk_detach_dev(BlockBackend *blk, DeviceState *dev) void blk_detach_dev(BlockBackend *blk, void *dev)
/* TODO change to DeviceState *dev when all users are qdevified */
{ {
assert(blk->dev == dev); assert(blk->dev == dev);
blk->dev = NULL; blk->dev = NULL;
@@ -887,7 +840,8 @@ void blk_detach_dev(BlockBackend *blk, DeviceState *dev)
/* /*
* Return the device model attached to @blk if any, else null. * Return the device model attached to @blk if any, else null.
*/ */
DeviceState *blk_get_attached_dev(BlockBackend *blk) void *blk_get_attached_dev(BlockBackend *blk)
/* TODO change to return DeviceState * when all users are qdevified */
{ {
return blk->dev; return blk->dev;
} }
@@ -896,15 +850,17 @@ DeviceState *blk_get_attached_dev(BlockBackend *blk)
* device attached to the BlockBackend. */ * device attached to the BlockBackend. */
char *blk_get_attached_dev_id(BlockBackend *blk) char *blk_get_attached_dev_id(BlockBackend *blk)
{ {
DeviceState *dev = blk->dev; DeviceState *dev;
assert(!blk->legacy_dev);
dev = blk->dev;
if (!dev) { if (!dev) {
return g_strdup(""); return g_strdup("");
} else if (dev->id) { } else if (dev->id) {
return g_strdup(dev->id); return g_strdup(dev->id);
} }
return object_get_canonical_path(OBJECT(dev));
return object_get_canonical_path(OBJECT(dev)) ?: g_strdup("");
} }
/* /*
@@ -934,6 +890,11 @@ BlockBackend *blk_by_dev(void *dev)
void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops,
void *opaque) void *opaque)
{ {
/* All drivers that use blk_set_dev_ops() are qdevified and we want to keep
* it that way, so we can assume blk->dev, if present, is a DeviceState if
* blk->dev_ops is set. Non-device users may use dev_ops without device. */
assert(!blk->legacy_dev);
blk->dev_ops = ops; blk->dev_ops = ops;
blk->dev_opaque = opaque; blk->dev_opaque = opaque;
@@ -959,6 +920,8 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp)
bool tray_was_open, tray_is_open; bool tray_was_open, tray_is_open;
Error *local_err = NULL; Error *local_err = NULL;
assert(!blk->legacy_dev);
tray_was_open = blk_dev_is_tray_open(blk); tray_was_open = blk_dev_is_tray_open(blk);
blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err); blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err);
if (local_err) { if (local_err) {
@@ -970,7 +933,8 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp)
if (tray_was_open != tray_is_open) { if (tray_was_open != tray_is_open) {
char *id = blk_get_attached_dev_id(blk); char *id = blk_get_attached_dev_id(blk);
qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open); qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open,
&error_abort);
g_free(id); g_free(id);
} }
} }
@@ -1073,7 +1037,11 @@ void blk_iostatus_disable(BlockBackend *blk)
void blk_iostatus_reset(BlockBackend *blk) void blk_iostatus_reset(BlockBackend *blk)
{ {
if (blk_iostatus_is_enabled(blk)) { if (blk_iostatus_is_enabled(blk)) {
BlockDriverState *bs = blk_bs(blk);
blk->iostatus = BLOCK_DEVICE_IO_STATUS_OK; 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; 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, static int blk_check_byte_request(BlockBackend *blk, int64_t offset,
size_t size) 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, rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size,
qiov, rwco->flags); qiov, rwco->flags);
aio_wait_kick();
} }
static void blk_write_entry(void *opaque) static void blk_write_entry(void *opaque)
@@ -1209,15 +1171,23 @@ static void blk_write_entry(void *opaque)
rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size, rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size,
qiov, rwco->flags); qiov, rwco->flags);
aio_wait_kick();
} }
static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
int64_t bytes, CoroutineEntry co_entry, int64_t bytes, CoroutineEntry co_entry,
BdrvRequestFlags flags) BdrvRequestFlags flags)
{ {
QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); QEMUIOVector qiov;
BlkRwCo rwco = { struct iovec iov;
BlkRwCo rwco;
iov = (struct iovec) {
.iov_base = buf,
.iov_len = bytes,
};
qemu_iovec_init_external(&qiov, &iov, 1);
rwco = (BlkRwCo) {
.blk = blk, .blk = blk,
.offset = offset, .offset = offset,
.iobuf = &qiov, .iobuf = &qiov,
@@ -1249,7 +1219,7 @@ int blk_pread_unthrottled(BlockBackend *blk, int64_t offset, uint8_t *buf,
blk_root_drained_begin(blk->root); blk_root_drained_begin(blk->root);
ret = blk_pread(blk, offset, buf, count); ret = blk_pread(blk, offset, buf, count);
blk_root_drained_end(blk->root, NULL); blk_root_drained_end(blk->root);
return ret; return ret;
} }
@@ -1265,15 +1235,15 @@ int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags)
return bdrv_make_zero(blk->root, flags); return bdrv_make_zero(blk->root, flags);
} }
void blk_inc_in_flight(BlockBackend *blk) static void blk_inc_in_flight(BlockBackend *blk)
{ {
atomic_inc(&blk->in_flight); atomic_inc(&blk->in_flight);
} }
void blk_dec_in_flight(BlockBackend *blk) static void blk_dec_in_flight(BlockBackend *blk)
{ {
atomic_dec(&blk->in_flight); atomic_dec(&blk->in_flight);
aio_wait_kick(); aio_wait_kick(&blk->wait);
} }
static void error_callback_bh(void *opaque) static void error_callback_bh(void *opaque)
@@ -1314,8 +1284,8 @@ static const AIOCBInfo blk_aio_em_aiocb_info = {
static void blk_aio_complete(BlkAioEmAIOCB *acb) static void blk_aio_complete(BlkAioEmAIOCB *acb)
{ {
if (acb->has_returned) { if (acb->has_returned) {
acb->common.cb(acb->common.opaque, acb->rwco.ret);
blk_dec_in_flight(acb->rwco.blk); blk_dec_in_flight(acb->rwco.blk);
acb->common.cb(acb->common.opaque, acb->rwco.ret);
qemu_aio_unref(acb); qemu_aio_unref(acb);
} }
} }
@@ -1512,7 +1482,6 @@ static void blk_ioctl_entry(void *opaque)
rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset,
qiov->iov[0].iov_base); qiov->iov[0].iov_base);
aio_wait_kick();
} }
int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
@@ -1543,7 +1512,7 @@ int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
return ret; return ret;
} }
return bdrv_co_pdiscard(blk->root, offset, bytes); return bdrv_co_pdiscard(blk_bs(blk), offset, bytes);
} }
int blk_co_flush(BlockBackend *blk) int blk_co_flush(BlockBackend *blk)
@@ -1559,7 +1528,6 @@ static void blk_flush_entry(void *opaque)
{ {
BlkRwCo *rwco = opaque; BlkRwCo *rwco = opaque;
rwco->ret = blk_co_flush(rwco->blk); rwco->ret = blk_co_flush(rwco->blk);
aio_wait_kick();
} }
int blk_flush(BlockBackend *blk) int blk_flush(BlockBackend *blk)
@@ -1576,8 +1544,9 @@ void blk_drain(BlockBackend *blk)
} }
/* We may have -ENOMEDIUM completions in flight */ /* We may have -ENOMEDIUM completions in flight */
AIO_WAIT_WHILE(blk_get_aio_context(blk), AIO_WAIT_WHILE(&blk->wait,
atomic_mb_read(&blk->in_flight) > 0); blk_get_aio_context(blk),
atomic_mb_read(&blk->in_flight) > 0);
if (bs) { if (bs) {
bdrv_drained_end(bs); bdrv_drained_end(bs);
@@ -1596,7 +1565,8 @@ void blk_drain_all(void)
aio_context_acquire(ctx); aio_context_acquire(ctx);
/* We may have -ENOMEDIUM completions in flight */ /* We may have -ENOMEDIUM completions in flight */
AIO_WAIT_WHILE(ctx, atomic_mb_read(&blk->in_flight) > 0); AIO_WAIT_WHILE(&blk->wait, ctx,
atomic_mb_read(&blk->in_flight) > 0);
aio_context_release(ctx); aio_context_release(ctx);
} }
@@ -1648,7 +1618,8 @@ static void send_qmp_error_event(BlockBackend *blk,
qapi_event_send_block_io_error(blk_name(blk), !!bs, qapi_event_send_block_io_error(blk_name(blk), !!bs,
bs ? bdrv_get_node_name(bs) : NULL, optype, bs ? bdrv_get_node_name(bs) : NULL, optype,
action, blk_iostatus_is_enabled(blk), action, blk_iostatus_is_enabled(blk),
error == ENOSPC, strerror(error)); error == ENOSPC, strerror(error),
&error_abort);
} }
/* This is done by device models because, while the block layer knows /* This is done by device models because, while the block layer knows
@@ -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); 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); BlockDriverState *bs = blk_bs(blk);
if (!bs) { if (!bs) {
return false; return 0;
} }
return bdrv_is_sg(bs); return bdrv_is_sg(bs);
} }
bool blk_enable_write_cache(BlockBackend *blk) int blk_enable_write_cache(BlockBackend *blk)
{ {
return blk->enable_write_cache; return blk->enable_write_cache;
} }
@@ -1753,6 +1724,9 @@ void blk_eject(BlockBackend *blk, bool eject_flag)
BlockDriverState *bs = blk_bs(blk); BlockDriverState *bs = blk_bs(blk);
char *id; char *id;
/* blk_eject is only called by qdevified devices */
assert(!blk->legacy_dev);
if (bs) { if (bs) {
bdrv_eject(bs, eject_flag); bdrv_eject(bs, eject_flag);
} }
@@ -1761,7 +1735,7 @@ void blk_eject(BlockBackend *blk, bool eject_flag)
* the frontend experienced a tray event. */ * the frontend experienced a tray event. */
id = blk_get_attached_dev_id(blk); id = blk_get_attached_dev_id(blk);
qapi_event_send_device_tray_moved(blk_name(blk), id, qapi_event_send_device_tray_moved(blk_name(blk), id,
eject_flag); eject_flag, &error_abort);
g_free(id); g_free(id);
} }
@@ -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 */ /* Returns the maximum transfer length, in bytes; guaranteed nonzero */
uint32_t blk_get_max_transfer(BlockBackend *blk) uint32_t blk_get_max_transfer(BlockBackend *blk)
{ {
@@ -1858,11 +1825,10 @@ AioContext *blk_get_aio_context(BlockBackend *blk)
BlockDriverState *bs = blk_bs(blk); BlockDriverState *bs = blk_bs(blk);
if (bs) { if (bs) {
AioContext *ctx = bdrv_get_aio_context(blk_bs(blk)); return bdrv_get_aio_context(bs);
assert(ctx == blk->ctx); } else {
return qemu_get_aio_context();
} }
return blk->ctx;
} }
static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb) 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); return blk_get_aio_context(blk_acb->blk);
} }
static int blk_do_set_aio_context(BlockBackend *blk, AioContext *new_context, void blk_set_aio_context(BlockBackend *blk, AioContext *new_context)
bool update_root_node, Error **errp)
{ {
BlockDriverState *bs = blk_bs(blk); BlockDriverState *bs = blk_bs(blk);
ThrottleGroupMember *tgm = &blk->public.throttle_group_member; ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
int ret;
if (bs) { 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) { if (tgm->throttle_state) {
bdrv_drained_begin(bs); bdrv_drained_begin(bs);
throttle_group_detach_aio_context(tgm); throttle_group_detach_aio_context(tgm);
throttle_group_attach_aio_context(tgm, new_context); throttle_group_attach_aio_context(tgm, new_context);
bdrv_drained_end(bs); 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 blk_add_aio_context_notifier(BlockBackend *blk,
void (*attached_aio_context)(AioContext *new_context, void *opaque), void (*attached_aio_context)(AioContext *new_context, void *opaque),
void (*detach_aio_context)(void *opaque), void *opaque) void (*detach_aio_context)(void *opaque), void *opaque)
{ {
BlockBackendAioNotifier *notifier;
BlockDriverState *bs = blk_bs(blk); BlockDriverState *bs = blk_bs(blk);
notifier = g_new(BlockBackendAioNotifier, 1);
notifier->attached_aio_context = attached_aio_context;
notifier->detach_aio_context = detach_aio_context;
notifier->opaque = opaque;
QLIST_INSERT_HEAD(&blk->aio_notifiers, notifier, list);
if (bs) { if (bs) {
bdrv_add_aio_context_notifier(bs, attached_aio_context, bdrv_add_aio_context_notifier(bs, attached_aio_context,
detach_aio_context, opaque); detach_aio_context, opaque);
@@ -1956,25 +1871,12 @@ void blk_remove_aio_context_notifier(BlockBackend *blk,
void (*detach_aio_context)(void *), void (*detach_aio_context)(void *),
void *opaque) void *opaque)
{ {
BlockBackendAioNotifier *notifier;
BlockDriverState *bs = blk_bs(blk); BlockDriverState *bs = blk_bs(blk);
if (bs) { if (bs) {
bdrv_remove_aio_context_notifier(bs, attached_aio_context, bdrv_remove_aio_context_notifier(bs, attached_aio_context,
detach_aio_context, opaque); detach_aio_context, opaque);
} }
QLIST_FOREACH(notifier, &blk->aio_notifiers, list) {
if (notifier->attached_aio_context == attached_aio_context &&
notifier->detach_aio_context == detach_aio_context &&
notifier->opaque == opaque) {
QLIST_REMOVE(notifier, list);
g_free(notifier);
return;
}
}
abort();
} }
void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify) void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify)
@@ -2047,7 +1949,6 @@ static void blk_pdiscard_entry(void *opaque)
QEMUIOVector *qiov = rwco->iobuf; QEMUIOVector *qiov = rwco->iobuf;
rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size); rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size);
aio_wait_kick();
} }
int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes) int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
@@ -2229,14 +2130,7 @@ static void blk_root_drained_begin(BdrvChild *child)
} }
} }
static bool blk_root_drained_poll(BdrvChild *child) static void blk_root_drained_end(BdrvChild *child)
{
BlockBackend *blk = child->opaque;
assert(blk->quiesce_counter);
return !!blk->in_flight;
}
static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter)
{ {
BlockBackend *blk = child->opaque; BlockBackend *blk = child->opaque;
assert(blk->quiesce_counter); assert(blk->quiesce_counter);
@@ -2260,27 +2154,3 @@ void blk_unregister_buf(BlockBackend *blk, void *host)
{ {
bdrv_unregister_buf(blk_bs(blk), host); bdrv_unregister_buf(blk_bs(blk), host);
} }
int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in,
BlockBackend *blk_out, int64_t off_out,
int bytes, BdrvRequestFlags read_flags,
BdrvRequestFlags write_flags)
{
int r;
r = blk_check_byte_request(blk_in, off_in, bytes);
if (r) {
return r;
}
r = blk_check_byte_request(blk_out, off_out, bytes);
if (r) {
return r;
}
return bdrv_co_copy_range(blk_in->root, off_in,
blk_out->root, off_out,
bytes, read_flags, write_flags);
}
const BdrvChild *blk_root(BlockBackend *blk)
{
return blk->root;
}

View File

@@ -24,6 +24,7 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu-common.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "qemu/bswap.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; const struct bochs_header *bochs = (const void *)buf;
if (buf_size < HEADER_SIZE) if (buf_size < HEADER_SIZE)
return 0; return 0;
if (!strcmp(bochs->magic, HEADER_MAGIC) && if (!strcmp(bochs->magic, HEADER_MAGIC) &&
!strcmp(bochs->type, REDOLOG_TYPE) && !strcmp(bochs->type, REDOLOG_TYPE) &&
!strcmp(bochs->subtype, GROWING_TYPE) && !strcmp(bochs->subtype, GROWING_TYPE) &&
((le32_to_cpu(bochs->version) == HEADER_VERSION) || ((le32_to_cpu(bochs->version) == HEADER_VERSION) ||
(le32_to_cpu(bochs->version) == HEADER_V1))) (le32_to_cpu(bochs->version) == HEADER_V1)))
return 100; return 100;
return 0; return 0;
} }
@@ -104,18 +105,23 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
struct bochs_header bochs; struct bochs_header bochs;
int ret; int ret;
/* No write support yet */
ret = bdrv_apply_auto_read_only(bs, NULL, errp);
if (ret < 0) {
return ret;
}
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
false, errp); false, errp);
if (!bs->file) { if (!bs->file) {
return -EINVAL; return -EINVAL;
} }
if (!bdrv_is_read_only(bs)) {
error_report("Opening bochs images without an explicit read-only=on "
"option is deprecated. Future versions will refuse to "
"open the image instead of automatically marking the "
"image read-only.");
ret = bdrv_set_read_only(bs, true, errp); /* no write support yet */
if (ret < 0) {
return ret;
}
}
ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs));
if (ret < 0) { if (ret < 0) {
return ret; return ret;
@@ -124,8 +130,8 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
if (strcmp(bochs.magic, HEADER_MAGIC) || if (strcmp(bochs.magic, HEADER_MAGIC) ||
strcmp(bochs.type, REDOLOG_TYPE) || strcmp(bochs.type, REDOLOG_TYPE) ||
strcmp(bochs.subtype, GROWING_TYPE) || strcmp(bochs.subtype, GROWING_TYPE) ||
((le32_to_cpu(bochs.version) != HEADER_VERSION) && ((le32_to_cpu(bochs.version) != HEADER_VERSION) &&
(le32_to_cpu(bochs.version) != HEADER_V1))) { (le32_to_cpu(bochs.version) != HEADER_V1))) {
error_setg(errp, "Image not in Bochs format"); error_setg(errp, "Image not in Bochs format");
return -EINVAL; return -EINVAL;
} }
@@ -157,7 +163,7 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
} }
for (i = 0; i < s->catalog_size; i++) for (i = 0; i < s->catalog_size; i++)
le32_to_cpus(&s->catalog_bitmap[i]); le32_to_cpus(&s->catalog_bitmap[i]);
s->data_offset = le32_to_cpu(bochs.header) + (s->catalog_size * 4); s->data_offset = le32_to_cpu(bochs.header) + (s->catalog_size * 4);
@@ -216,7 +222,7 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
extent_offset = (offset % s->extent_size) / 512; extent_offset = (offset % s->extent_size) / 512;
if (s->catalog_bitmap[extent_index] == 0xffffffff) { if (s->catalog_bitmap[extent_index] == 0xffffffff) {
return 0; /* not allocated */ return 0; /* not allocated */
} }
bitmap_offset = s->data_offset + bitmap_offset = s->data_offset +
@@ -231,7 +237,7 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
} }
if (!((bitmap_entry >> (extent_offset % 8)) & 1)) { if (!((bitmap_entry >> (extent_offset % 8)) & 1)) {
return 0; /* not allocated */ return 0; /* not allocated */
} }
return bitmap_offset + (512 * (s->bitmap_blocks + extent_offset)); return bitmap_offset + (512 * (s->bitmap_blocks + extent_offset));

View File

@@ -24,6 +24,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qemu-common.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "qemu/bswap.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; uint32_t offsets_size, max_compressed_block_size = 1, i;
int ret; int ret;
ret = bdrv_apply_auto_read_only(bs, NULL, errp);
if (ret < 0) {
return ret;
}
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
false, errp); false, errp);
if (!bs->file) { if (!bs->file) {
return -EINVAL; return -EINVAL;
} }
if (!bdrv_is_read_only(bs)) {
error_report("Opening cloop images without an explicit read-only=on "
"option is deprecated. Future versions will refuse to "
"open the image instead of automatically marking the "
"image read-only.");
ret = bdrv_set_read_only(bs, true, errp);
if (ret < 0) {
return ret;
}
}
/* read header */ /* read header */
ret = bdrv_pread(bs->file, 128, &s->block_size, 4); ret = bdrv_pread(bs->file, 128, &s->block_size, 4);
if (ret < 0) { if (ret < 0) {

Some files were not shown because too many files have changed in this diff Show More