mirror of https://github.com/xemu-project/xemu.git
v5.0.0 release
-----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAl6oXgEZHHBldGVyLm1h eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3tbxEACuWVSycXZ+tejIbSf7KdtK c8MUD6nop70xfq8UObzkNw6lxJkBJo4eBWOlTLvCHChb+aQnpniSvkG6aTro5Q2i LpdtQ3rZR93gyfpnUVhyD1kFMg6f3dVrtQqnJOX0VdWjo634UcXOcccFaW41b59W b3860PqqBUlmx8tdi0RDZ4VZ2kq7HOlYwl9+GU2IfmNYoQAYnYQOiTmO6nFIOOCc vTdcFk50nPaZNS4oT60vJ+/+bMs31XWrxXPIXivRics7OYKY8qfxGKw+IpnVaoNq BSSqGooo4BPSlm9U698AP81iC62vnmyEG8uhJLEGfkKF8x4P2ctcyvlaLXuuNie9 SNKscal7EFbyvooe7mYTlPB9enubp/Q1VCsqpcmGH+8WL5W356wIV0PK0qiCeoPX yFPlMKHjxZEp2NkCvZlyxz2BrsNEIUDcIK1x2zy80+wFYZNrj/tAfR86eBG/rGP3 N/zCkHPADb5qddKKa5hD24SF6Q2f5Ef6tv6+2DNmGOifunS/ea7M4qxc+SiVBuwD bYyRHyQCr7WEcKHVqbSsdn6VBYyFpsecudxtItjKXbwuqLfPx4p4CokowFDDJiXI LpJruDiL7pQKU3t+1/P6Q3Qwdqf3YTt+IA5/PhOa/surLbhuXkteBt379avdJ21n EbpKGu3UV4UTwMyJlzlT0A== =wWkC -----END PGP SIGNATURE----- Merge tag 'v5.0.0' into merge-v5.0.0 v5.0.0 release
This commit is contained in:
commit
987aa99e3f
|
@ -22,7 +22,9 @@ macos_task:
|
|||
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; }
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --python=/usr/local/bin/python3 || { cat config.log; exit 1; }
|
||||
- gmake -j$(sysctl -n hw.ncpu)
|
||||
- gmake check -j$(sysctl -n hw.ncpu)
|
||||
|
||||
|
@ -33,6 +35,8 @@ macos_xcode_task:
|
|||
install_script:
|
||||
- brew install pkg-config gnu-sed glib pixman make sdl2
|
||||
script:
|
||||
- ./configure --cc=clang || { cat config.log; exit 1; }
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --cc=clang || { cat config.log; exit 1; }
|
||||
- gmake -j$(sysctl -n hw.ncpu)
|
||||
- gmake check -j$(sysctl -n hw.ncpu)
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
# Configuration for Repo Lockdown - https://github.com/dessant/repo-lockdown
|
||||
|
||||
# Close issues and pull requests
|
||||
close: true
|
||||
|
||||
# Lock issues and pull requests
|
||||
lock: true
|
||||
|
||||
issues:
|
||||
comment: |
|
||||
Thank you for your interest in the QEMU project.
|
||||
|
||||
This repository is a read-only mirror of the project's master
|
||||
repostories hosted on https://git.qemu.org/git/qemu.git.
|
||||
The project does not process issues filed on GitHub.
|
||||
|
||||
The project issues are tracked on Launchpad:
|
||||
https://bugs.launchpad.net/qemu
|
||||
|
||||
QEMU welcomes bug report contributions. You can file new ones on:
|
||||
https://bugs.launchpad.net/qemu/+filebug
|
||||
|
||||
pulls:
|
||||
comment: |
|
||||
Thank you for your interest in the QEMU project.
|
||||
|
||||
This repository is a read-only mirror of the project's master
|
||||
repostories hosted on https://git.qemu.org/git/qemu.git.
|
||||
The project does not process merge requests filed on GitHub.
|
||||
|
||||
QEMU welcomes contributions of code (either fixing bugs or adding new
|
||||
functionality). However, we get a lot of patches, and so we have some
|
||||
guidelines about contributing on the project website:
|
||||
https://www.qemu.org/contribute/
|
|
@ -6,6 +6,7 @@
|
|||
/config-target.*
|
||||
/config.status
|
||||
/config-temp
|
||||
/tools/virtiofsd/50-qemu-virtiofsd.json
|
||||
/elf2dmp
|
||||
/trace-events-all
|
||||
/trace/generated-events.h
|
||||
|
@ -37,6 +38,7 @@
|
|||
/qapi/qapi-emit-events.[ch]
|
||||
/qapi/qapi-events-*.[ch]
|
||||
/qapi/qapi-events.[ch]
|
||||
/qapi/qapi-init-commands.[ch]
|
||||
/qapi/qapi-introspect.[ch]
|
||||
/qapi/qapi-types-*.[ch]
|
||||
/qapi/qapi-types.[ch]
|
||||
|
@ -44,9 +46,6 @@
|
|||
!/qapi/qapi-visit-core.c
|
||||
/qapi/qapi-visit.[ch]
|
||||
/qapi/qapi-doc.texi
|
||||
/qemu-doc.html
|
||||
/qemu-doc.info
|
||||
/qemu-doc.txt
|
||||
/qemu-edid
|
||||
/qemu-img
|
||||
/qemu-nbd
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
docker-edk2:
|
||||
stage: build
|
||||
rules: # Only run this job when the Dockerfile is modified
|
||||
- changes:
|
||||
- .gitlab-ci-edk2.yml
|
||||
- .gitlab-ci.d/edk2/Dockerfile
|
||||
when: always
|
||||
image: docker:19.03.1
|
||||
services:
|
||||
- docker:19.03.1-dind
|
||||
variables:
|
||||
GIT_DEPTH: 3
|
||||
IMAGE_TAG: $CI_REGISTRY_IMAGE:edk2-cross-build
|
||||
# We don't use TLS
|
||||
DOCKER_HOST: tcp://docker:2375
|
||||
DOCKER_TLS_CERTDIR: ""
|
||||
before_script:
|
||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||
script:
|
||||
- docker pull $IMAGE_TAG || true
|
||||
- docker build --cache-from $IMAGE_TAG --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
|
||||
--tag $IMAGE_TAG .gitlab-ci.d/edk2
|
||||
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
|
||||
- docker push $IMAGE_TAG
|
||||
|
||||
build-edk2:
|
||||
rules: # Only run this job when ...
|
||||
- changes: # ... roms/edk2/ is modified (submodule updated)
|
||||
- roms/edk2/*
|
||||
when: always
|
||||
- if: '$CI_COMMIT_REF_NAME =~ /^edk2/' # or the branch/tag starts with 'edk2'
|
||||
when: always
|
||||
- if: '$CI_COMMIT_MESSAGE =~ /edk2/i' # or last commit description contains 'EDK2'
|
||||
when: always
|
||||
artifacts:
|
||||
paths: # 'artifacts.zip' will contains the following files:
|
||||
- pc-bios/edk2*bz2
|
||||
- pc-bios/edk2-licenses.txt
|
||||
- edk2-stdout.log
|
||||
- edk2-stderr.log
|
||||
image: $CI_REGISTRY_IMAGE:edk2-cross-build
|
||||
variables:
|
||||
GIT_DEPTH: 3
|
||||
script: # Clone the required submodules and build EDK2
|
||||
- git submodule update --init roms/edk2
|
||||
- git -C roms/edk2 submodule update --init
|
||||
- export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1))
|
||||
- echo "=== Using ${JOBS} simultaneous jobs ==="
|
||||
- make -j${JOBS} -C roms efi 2>&1 1>edk2-stdout.log | tee -a edk2-stderr.log >&2
|
|
@ -0,0 +1,63 @@
|
|||
docker-opensbi:
|
||||
stage: build
|
||||
rules: # Only run this job when the Dockerfile is modified
|
||||
- changes:
|
||||
- .gitlab-ci-opensbi.yml
|
||||
- .gitlab-ci.d/opensbi/Dockerfile
|
||||
when: always
|
||||
image: docker:19.03.1
|
||||
services:
|
||||
- docker:19.03.1-dind
|
||||
variables:
|
||||
GIT_DEPTH: 3
|
||||
IMAGE_TAG: $CI_REGISTRY_IMAGE:opensbi-cross-build
|
||||
# We don't use TLS
|
||||
DOCKER_HOST: tcp://docker:2375
|
||||
DOCKER_TLS_CERTDIR: ""
|
||||
before_script:
|
||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||
script:
|
||||
- docker pull $IMAGE_TAG || true
|
||||
- docker build --cache-from $IMAGE_TAG --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
|
||||
--tag $IMAGE_TAG .gitlab-ci.d/opensbi
|
||||
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
|
||||
- docker push $IMAGE_TAG
|
||||
|
||||
build-opensbi:
|
||||
rules: # Only run this job when ...
|
||||
- changes: # ... roms/opensbi/ is modified (submodule updated)
|
||||
- roms/opensbi/*
|
||||
when: always
|
||||
- if: '$CI_COMMIT_REF_NAME =~ /^opensbi/' # or the branch/tag starts with 'opensbi'
|
||||
when: always
|
||||
- if: '$CI_COMMIT_MESSAGE =~ /opensbi/i' # or last commit description contains 'OpenSBI'
|
||||
when: always
|
||||
artifacts:
|
||||
paths: # 'artifacts.zip' will contains the following files:
|
||||
- pc-bios/opensbi-riscv32-sifive_u-fw_jump.bin
|
||||
- pc-bios/opensbi-riscv32-virt-fw_jump.bin
|
||||
- pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin
|
||||
- pc-bios/opensbi-riscv64-virt-fw_jump.bin
|
||||
- opensbi32-virt-stdout.log
|
||||
- opensbi32-virt-stderr.log
|
||||
- opensbi64-virt-stdout.log
|
||||
- opensbi64-virt-stderr.log
|
||||
- opensbi32-sifive_u-stdout.log
|
||||
- opensbi32-sifive_u-stderr.log
|
||||
- opensbi64-sifive_u-stdout.log
|
||||
- opensbi64-sifive_u-stderr.log
|
||||
image: $CI_REGISTRY_IMAGE:opensbi-cross-build
|
||||
variables:
|
||||
GIT_DEPTH: 3
|
||||
script: # Clone the required submodules and build OpenSBI
|
||||
- git submodule update --init roms/opensbi
|
||||
- export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1))
|
||||
- echo "=== Using ${JOBS} simultaneous jobs ==="
|
||||
- make -j${JOBS} -C roms/opensbi clean
|
||||
- make -j${JOBS} -C roms opensbi32-virt 2>&1 1>opensbi32-virt-stdout.log | tee -a opensbi32-virt-stderr.log >&2
|
||||
- make -j${JOBS} -C roms/opensbi clean
|
||||
- make -j${JOBS} -C roms opensbi64-virt 2>&1 1>opensbi64-virt-stdout.log | tee -a opensbi64-virt-stderr.log >&2
|
||||
- make -j${JOBS} -C roms/opensbi clean
|
||||
- make -j${JOBS} -C roms opensbi32-sifive_u 2>&1 1>opensbi32-sifive_u-stdout.log | tee -a opensbi32-sifive_u-stderr.log >&2
|
||||
- make -j${JOBS} -C roms/opensbi clean
|
||||
- make -j${JOBS} -C roms opensbi64-sifive_u 2>&1 1>opensbi64-sifive_u-stdout.log | tee -a opensbi64-sifive_u-stderr.log >&2
|
|
@ -0,0 +1,27 @@
|
|||
#
|
||||
# Docker image to cross-compile EDK2 firmware binaries
|
||||
#
|
||||
FROM ubuntu:16.04
|
||||
|
||||
MAINTAINER Philippe Mathieu-Daudé <philmd@redhat.com>
|
||||
|
||||
# Install packages required to build EDK2
|
||||
RUN apt update \
|
||||
&& \
|
||||
\
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
apt install --assume-yes --no-install-recommends \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
dos2unix \
|
||||
gcc-aarch64-linux-gnu \
|
||||
gcc-arm-linux-gnueabi \
|
||||
git \
|
||||
iasl \
|
||||
make \
|
||||
nasm \
|
||||
python \
|
||||
uuid-dev \
|
||||
&& \
|
||||
\
|
||||
rm -rf /var/lib/apt/lists/*
|
|
@ -0,0 +1,33 @@
|
|||
#
|
||||
# Docker image to cross-compile OpenSBI firmware binaries
|
||||
#
|
||||
FROM ubuntu:18.04
|
||||
|
||||
MAINTAINER Bin Meng <bmeng.cn@gmail.com>
|
||||
|
||||
# Install packages required to build OpenSBI
|
||||
RUN apt update \
|
||||
&& \
|
||||
\
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
apt install --assume-yes --no-install-recommends \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
git \
|
||||
make \
|
||||
wget \
|
||||
&& \
|
||||
\
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Manually install the kernel.org "Crosstool" based toolchains for gcc-8.3
|
||||
RUN wget -O - \
|
||||
https://mirrors.edge.kernel.org/pub/tools/crosstool/files/bin/x86_64/8.3.0/x86_64-gcc-8.3.0-nolibc-riscv32-linux.tar.xz \
|
||||
| tar -C /opt -xJ
|
||||
RUN wget -O - \
|
||||
https://mirrors.edge.kernel.org/pub/tools/crosstool/files/bin/x86_64/8.3.0/x86_64-gcc-8.3.0-nolibc-riscv64-linux.tar.xz \
|
||||
| tar -C /opt -xJ
|
||||
|
||||
# Export the toolchains to the system path
|
||||
ENV PATH="/opt/gcc-8.3.0-nolibc/riscv32-linux/bin:${PATH}"
|
||||
ENV PATH="/opt/gcc-8.3.0-nolibc/riscv64-linux/bin:${PATH}"
|
|
@ -1,3 +1,7 @@
|
|||
include:
|
||||
- local: '/.gitlab-ci-edk2.yml'
|
||||
- local: '/.gitlab-ci-opensbi.yml'
|
||||
|
||||
before_script:
|
||||
- apt-get update -qq
|
||||
- apt-get install -y -qq flex bison libglib2.0-dev libpixman-1-dev genisoimage
|
||||
|
@ -6,7 +10,9 @@ 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 libvdeplug-dev
|
||||
- ./configure --enable-werror --target-list="aarch64-softmmu alpha-softmmu
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../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
|
||||
|
@ -16,7 +22,10 @@ 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
|
||||
libzstd-dev
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../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
|
||||
|
@ -24,7 +33,9 @@ build-system2:
|
|||
|
||||
build-disabled:
|
||||
script:
|
||||
- ./configure --enable-werror --disable-rdma --disable-slirp --disable-curl
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../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
|
||||
|
@ -36,32 +47,38 @@ build-disabled:
|
|||
|
||||
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=""
|
||||
- apt-get install -y -qq clang libgtk-3-dev libusb-dev
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../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 028 040 051 056 057 058 065 067 068 082 085 091 095 096 102
|
||||
122 124 127 129 132 139 142 144 145 147 151 152 155 157 165 194
|
||||
196 197 200 202 203 205 208 209 215 216 218 222 227 234 246 247
|
||||
248 250 254 255 256
|
||||
052 063 077 086 101 104 106 113 148 150 151 152 157 159 160 163
|
||||
170 171 183 184 192 194 197 208 215 221 222 226 227 236 253 277
|
||||
- ./check -qcow2 028 051 056 057 058 065 067 068 082 085 091 095 096 102 122
|
||||
124 132 139 142 144 145 151 152 155 157 165 194 196 197 200 202
|
||||
208 209 215 216 218 222 227 234 246 247 248 250 254 255 257 258
|
||||
260 261 262 263 264 270 272 273 277 279
|
||||
|
||||
build-user:
|
||||
script:
|
||||
- ./configure --enable-werror --disable-system --disable-guest-agent
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../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 libattr1-dev libcap-dev
|
||||
- apt-get install -y -qq clang libsdl2-dev libattr1-dev libcap-ng-dev
|
||||
xfslibs-dev libiscsi-dev libnfs-dev libseccomp-dev gnutls-dev librbd-dev
|
||||
- ./configure --cc=clang --cxx=clang++ --enable-werror
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../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
|
||||
|
@ -70,14 +87,18 @@ build-clang:
|
|||
build-tci:
|
||||
script:
|
||||
- TARGETS="aarch64 alpha arm hppa m68k microblaze moxie ppc64 s390x x86_64"
|
||||
- ./configure --enable-tcg-interpreter
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../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
|
||||
- make run-tcg-tests-x86_64-softmmu
|
||||
- make tests/qtest/boot-serial-test tests/qtest/cdrom-test tests/qtest/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 ;
|
||||
./tests/qtest/boot-serial-test || exit 1 ;
|
||||
./tests/qtest/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
|
||||
- QTEST_QEMU_BINARY="x86_64-softmmu/qemu-system-x86_64" ./tests/qtest/pxe-test
|
||||
- QTEST_QEMU_BINARY="s390x-softmmu/qemu-system-s390x"
|
||||
./tests/qtest/pxe-test -m slow
|
||||
|
|
|
@ -10,9 +10,6 @@
|
|||
[submodule "roms/openbios"]
|
||||
path = roms/openbios
|
||||
url = https://git.qemu.org/git/openbios.git
|
||||
[submodule "roms/openhackware"]
|
||||
path = roms/openhackware
|
||||
url = https://git.qemu.org/git/openhackware.git
|
||||
[submodule "roms/qemu-palcode"]
|
||||
path = roms/qemu-palcode
|
||||
url = https://git.qemu.org/git/qemu-palcode.git
|
||||
|
|
9
.mailmap
9
.mailmap
|
@ -39,11 +39,13 @@ Julia Suvorova <jusual@mail.ru> Julia Suvorova via Qemu-devel <qemu-devel@nongnu
|
|||
Justin Terry (VM) <juterry@microsoft.com> Justin Terry (VM) via Qemu-devel <qemu-devel@nongnu.org>
|
||||
|
||||
# Next, replace old addresses by a more recent one.
|
||||
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@mips.com>
|
||||
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@imgtec.com>
|
||||
Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <aleksandar.markovic@mips.com>
|
||||
Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <aleksandar.markovic@imgtec.com>
|
||||
Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <amarkovic@wavecomp.com>
|
||||
Aleksandar Rikalo <aleksandar.rikalo@rt-rk.com> <arikalo@wavecomp.com>
|
||||
Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com>
|
||||
James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com>
|
||||
Leif Lindholm <leif@nuviainc.com> <leif.lindholm@linaro.org>
|
||||
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>
|
||||
|
@ -151,7 +153,8 @@ Xiaoqiang Zhao <zxq_yx_007@163.com>
|
|||
Xinhua Cao <caoxinhua@huawei.com>
|
||||
Xiong Zhang <xiong.y.zhang@intel.com>
|
||||
Yin Yin <yin.yin@cs2c.com.cn>
|
||||
yuchenlin <npes87184@gmail.com>
|
||||
Yu-Chen Lin <npes87184@gmail.com>
|
||||
Yu-Chen Lin <npes87184@gmail.com> <yuchenlin@synology.com>
|
||||
YunQiang Su <syq@debian.org>
|
||||
YunQiang Su <ysu@wavecomp.com>
|
||||
Yuri Pudgorodskiy <yur@virtuozzo.com>
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# .readthedocs.yml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
|
||||
# We want all the document formats
|
||||
formats: all
|
||||
|
||||
# For consistency, we require that QEMU's Sphinx extensions
|
||||
# run with at least the same minimum version of Python that
|
||||
# we require for other Python in our codebase (our conf.py
|
||||
# enforces this, and some code needs it.)
|
||||
python:
|
||||
version: 3.5
|
|
@ -35,5 +35,7 @@ build:
|
|||
options: "-e HOME=/root"
|
||||
ci:
|
||||
- unset CC
|
||||
- ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST}
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-docs ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST}
|
||||
- make -j$(($(getconf _NPROCESSORS_ONLN) + 1))
|
||||
|
|
371
.travis.yml
371
.travis.yml
|
@ -1,6 +1,7 @@
|
|||
# The current Travis default is a VM based 16.04 Xenial on GCE
|
||||
# Additional builds with specific requirements for a full VM need to
|
||||
# be added as additional matrix: entries later on
|
||||
os: linux
|
||||
dist: xenial
|
||||
language: c
|
||||
compiler:
|
||||
|
@ -26,7 +27,6 @@ addons:
|
|||
- libaio-dev
|
||||
- libattr1-dev
|
||||
- libbrlapi-dev
|
||||
- libcap-dev
|
||||
- libcap-ng-dev
|
||||
- libgcc-4.8-dev
|
||||
- libgnutls28-dev
|
||||
|
@ -49,16 +49,12 @@ addons:
|
|||
- libusb-1.0-0-dev
|
||||
- libvdeplug-dev
|
||||
- libvte-2.91-dev
|
||||
- libzstd-dev
|
||||
- sparse
|
||||
- uuid-dev
|
||||
- gcovr
|
||||
homebrew:
|
||||
packages:
|
||||
- ccache
|
||||
- glib
|
||||
- pixman
|
||||
- gnu-sed
|
||||
update: true
|
||||
# Tests dependencies
|
||||
- genisoimage
|
||||
|
||||
|
||||
# The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu
|
||||
|
@ -74,94 +70,126 @@ notifications:
|
|||
|
||||
env:
|
||||
global:
|
||||
- SRC_DIR="."
|
||||
- BUILD_DIR="."
|
||||
- SRC_DIR=".."
|
||||
- BUILD_DIR="build"
|
||||
- BASE_CONFIG="--disable-docs --disable-tools"
|
||||
- TEST_BUILD_CMD=""
|
||||
- TEST_CMD="make check V=1"
|
||||
# This is broadly a list of "mainline" softmmu targets which have support across the major distros
|
||||
- MAIN_SOFTMMU_TARGETS="aarch64-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
|
||||
- CCACHE_SLOPPINESS="include_file_ctime,include_file_mtime"
|
||||
- CCACHE_MAXSIZE=1G
|
||||
- G_MESSAGES_DEBUG=error
|
||||
|
||||
|
||||
git:
|
||||
# we want to do this ourselves
|
||||
submodules: false
|
||||
|
||||
|
||||
before_script:
|
||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then export PATH="/usr/local/opt/ccache/libexec:$PATH" ; fi
|
||||
# Common first phase for all steps
|
||||
before_install:
|
||||
- if command -v ccache ; then ccache --zero-stats ; fi
|
||||
- export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1))
|
||||
- echo "=== Using ${JOBS} simultaneous jobs ==="
|
||||
|
||||
# Configure step - may be overridden
|
||||
before_script:
|
||||
- mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
|
||||
- ${SRC_DIR}/configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; }
|
||||
|
||||
# Main build & test - rarely overridden - controlled by TEST_CMD
|
||||
script:
|
||||
- make -j3 && travis_retry ${TEST_CMD}
|
||||
- BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$?
|
||||
- |
|
||||
if [ "$BUILD_RC" -eq 0 ] && [ -n "$TEST_BUILD_CMD" ]; then
|
||||
${TEST_BUILD_CMD} || BUILD_RC=$?
|
||||
else
|
||||
$(exit $BUILD_RC);
|
||||
fi
|
||||
- |
|
||||
if [ "$BUILD_RC" -eq 0 ] ; then
|
||||
${TEST_CMD} ;
|
||||
else
|
||||
$(exit $BUILD_RC);
|
||||
fi
|
||||
after_script:
|
||||
- if command -v ccache ; then ccache --show-stats ; fi
|
||||
|
||||
|
||||
matrix:
|
||||
jobs:
|
||||
include:
|
||||
- env:
|
||||
- name: "GCC static (user)"
|
||||
env:
|
||||
- CONFIG="--disable-system --static"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
|
||||
|
||||
|
||||
# we split the system builds as it takes a while to build them all
|
||||
- env:
|
||||
- name: "GCC (main-softmmu)"
|
||||
env:
|
||||
- CONFIG="--disable-user --target-list=${MAIN_SOFTMMU_TARGETS}"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
|
||||
|
||||
|
||||
- env:
|
||||
- CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
|
||||
- name: "GCC (other-softmmu)"
|
||||
env:
|
||||
- CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
|
||||
|
||||
|
||||
# Just build tools and run minimal unit and softfloat checks
|
||||
- env:
|
||||
- name: "GCC check-softfloat (user)"
|
||||
env:
|
||||
- BASE_CONFIG="--enable-tools"
|
||||
- CONFIG="--disable-user --disable-system"
|
||||
- TEST_CMD="make check-unit check-softfloat -j3"
|
||||
- TEST_CMD="make check-unit check-softfloat -j${JOBS}"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
|
||||
|
||||
|
||||
# --enable-debug implies --enable-debug-tcg, also runs quite a bit slower
|
||||
- env:
|
||||
- name: "GCC debug (main-softmmu)"
|
||||
env:
|
||||
- CONFIG="--enable-debug --target-list=${MAIN_SOFTMMU_TARGETS}"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug"
|
||||
|
||||
|
||||
# TCG debug can be run just on its own and is mostly agnostic to user/softmmu distinctions
|
||||
- env:
|
||||
- name: "GCC debug (user)"
|
||||
env:
|
||||
- CONFIG="--enable-debug-tcg --disable-system"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg"
|
||||
|
||||
|
||||
- env:
|
||||
- name: "GCC some libs disabled (main-softmmu)"
|
||||
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:
|
||||
- name: "GCC modules (main-softmmu)"
|
||||
env:
|
||||
- CONFIG="--enable-modules --target-list=${MAIN_SOFTMMU_TARGETS}"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
|
||||
|
||||
|
||||
# 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:
|
||||
- name: "check-unit coroutine=ucontext"
|
||||
env:
|
||||
- CONFIG="--with-coroutine=ucontext --disable-tcg"
|
||||
- TEST_CMD="make check-unit -j3 V=1"
|
||||
- TEST_CMD="make check-unit -j${JOBS} V=1"
|
||||
|
||||
|
||||
- env:
|
||||
- name: "check-unit coroutine=sigaltstack"
|
||||
env:
|
||||
- CONFIG="--with-coroutine=sigaltstack --disable-tcg"
|
||||
- TEST_CMD="make check-unit -j3 V=1"
|
||||
- TEST_CMD="make check-unit -j${JOBS} V=1"
|
||||
|
||||
|
||||
# Check we can build docs and tools (out of tree)
|
||||
- env:
|
||||
- name: "tools and docs (bionic)"
|
||||
dist: bionic
|
||||
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"
|
||||
|
@ -169,118 +197,147 @@ matrix:
|
|||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- python-sphinx
|
||||
- python3-sphinx
|
||||
- texinfo
|
||||
- perl
|
||||
|
||||
|
||||
# Test with Clang for compile portability (Travis uses clang-5.0)
|
||||
- env:
|
||||
- name: "Clang (user)"
|
||||
env:
|
||||
- CONFIG="--disable-system"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-default"
|
||||
compiler: clang
|
||||
|
||||
|
||||
- env:
|
||||
- CONFIG="--disable-user --target-list=${MAIN_SOFTMMU_TARGETS}"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-default"
|
||||
compiler: clang
|
||||
|
||||
|
||||
- env:
|
||||
- name: "Clang (main-softmmu)"
|
||||
env:
|
||||
- CONFIG="--target-list=${MAIN_SOFTMMU_TARGETS} "
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-sanitize"
|
||||
compiler: clang
|
||||
before_script:
|
||||
- ./configure ${CONFIG} --extra-cflags="-fsanitize=undefined -Werror" || { cat config.log && exit 1; }
|
||||
- mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
|
||||
- ${SRC_DIR}/configure ${CONFIG} --extra-cflags="-fsanitize=undefined -Werror" || { cat config.log && exit 1; }
|
||||
|
||||
|
||||
- env:
|
||||
- name: "Clang (other-softmmu)"
|
||||
env:
|
||||
- CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-default"
|
||||
compiler: clang
|
||||
|
||||
|
||||
# gprof/gcov are GCC features
|
||||
- env:
|
||||
- name: "GCC gprof/gcov"
|
||||
env:
|
||||
- CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=${MAIN_SOFTMMU_TARGETS}"
|
||||
after_success:
|
||||
- ${SRC_DIR}/scripts/travis/coverage-summary.sh
|
||||
|
||||
|
||||
# We manually include builds which we disable "make check" for
|
||||
- env:
|
||||
- name: "GCC without-default-devices (softmmu)"
|
||||
env:
|
||||
- CONFIG="--without-default-devices --disable-user"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
|
||||
- TEST_CMD=""
|
||||
|
||||
|
||||
# We manually include builds which we disable "make check" for
|
||||
- env:
|
||||
- CONFIG="--enable-debug --enable-tcg-interpreter"
|
||||
- TEST_CMD=""
|
||||
# Check the TCG interpreter (TCI)
|
||||
- name: "GCC TCI"
|
||||
env:
|
||||
- CONFIG="--enable-debug-tcg --enable-tcg-interpreter --disable-kvm --disable-containers
|
||||
--target-list=alpha-softmmu,arm-softmmu,hppa-softmmu,m68k-softmmu,microblaze-softmmu,moxie-softmmu,ppc-softmmu,s390x-softmmu,x86_64-softmmu"
|
||||
- TEST_CMD="make check-qtest check-tcg V=1"
|
||||
|
||||
|
||||
# We don't need to exercise every backend with every front-end
|
||||
- env:
|
||||
- name: "GCC trace log,simple,syslog (user)"
|
||||
env:
|
||||
- CONFIG="--enable-trace-backends=log,simple,syslog --disable-system"
|
||||
- TEST_CMD=""
|
||||
|
||||
|
||||
- env:
|
||||
- name: "GCC trace ftrace (x86_64-softmmu)"
|
||||
env:
|
||||
- CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu"
|
||||
- TEST_CMD=""
|
||||
|
||||
|
||||
- env:
|
||||
- name: "GCC trace ust (x86_64-softmmu)"
|
||||
env:
|
||||
- CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu"
|
||||
- TEST_CMD=""
|
||||
|
||||
|
||||
# MacOSX builds - cirrus.yml also tests some MacOS builds including latest Xcode
|
||||
|
||||
- env:
|
||||
- name: "OSX Xcode 10.3"
|
||||
env:
|
||||
- BASE_CONFIG="--disable-docs --enable-tools"
|
||||
- CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu"
|
||||
os: osx
|
||||
osx_image: xcode10.3
|
||||
compiler: clang
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
- ccache
|
||||
- glib
|
||||
- pixman
|
||||
- gnu-sed
|
||||
- python
|
||||
update: true
|
||||
before_script:
|
||||
- brew link --overwrite python
|
||||
- export PATH="/usr/local/opt/ccache/libexec:$PATH"
|
||||
- mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
|
||||
- ${SRC_DIR}/configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; }
|
||||
|
||||
|
||||
# Python builds
|
||||
- env:
|
||||
- name: "GCC Python 3.5 (x86_64-softmmu)"
|
||||
env:
|
||||
- CONFIG="--target-list=x86_64-softmmu"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
|
||||
language: python
|
||||
python:
|
||||
- "3.4"
|
||||
python: 3.5
|
||||
|
||||
|
||||
- env:
|
||||
- name: "GCC Python 3.6 (x86_64-softmmu)"
|
||||
env:
|
||||
- CONFIG="--target-list=x86_64-softmmu"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
|
||||
language: python
|
||||
python:
|
||||
- "3.6"
|
||||
python: 3.6
|
||||
|
||||
|
||||
# Acceptance (Functional) tests
|
||||
- env:
|
||||
- CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu,mips-softmmu,mips64el-softmmu,aarch64-softmmu,arm-softmmu,s390x-softmmu,alpha-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,sparc-softmmu"
|
||||
- name: "GCC check-acceptance"
|
||||
dist: bionic
|
||||
env:
|
||||
- CONFIG="--enable-tools --target-list=aarch64-softmmu,alpha-softmmu,arm-softmmu,m68k-softmmu,microblaze-softmmu,mips-softmmu,mips64el-softmmu,nios2-softmmu,or1k-softmmu,ppc-softmmu,ppc64-softmmu,s390x-softmmu,sparc-softmmu,x86_64-softmmu,xtensa-softmmu"
|
||||
- TEST_CMD="make check-acceptance"
|
||||
after_failure:
|
||||
- cat tests/results/latest/job.log
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-acceptance"
|
||||
after_script:
|
||||
- python3 -c 'import json; r = json.load(open("tests/results/latest/results.json")); [print(t["logfile"]) for t in r["tests"] if t["status"] not in ("PASS", "SKIP")]' | xargs cat
|
||||
- du -chs $HOME/avocado/data/cache
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- python3-pil
|
||||
- python3-pip
|
||||
- python3.5-venv
|
||||
- python3-numpy
|
||||
- python3-opencv
|
||||
- python3-venv
|
||||
- rpm2cpio
|
||||
- tesseract-ocr
|
||||
- tesseract-ocr-eng
|
||||
|
||||
|
||||
# Using newer GCC with sanitizers
|
||||
- addons:
|
||||
- name: "GCC9 with sanitizers (softmmu)"
|
||||
addons:
|
||||
apt:
|
||||
update: true
|
||||
sources:
|
||||
|
@ -323,56 +380,212 @@ matrix:
|
|||
- CONFIG="--cc=gcc-9 --cxx=g++-9 --disable-pie --disable-linux-user"
|
||||
- TEST_CMD=""
|
||||
before_script:
|
||||
- ./configure ${CONFIG} --extra-cflags="-g3 -O0 -Wno-error=stringop-truncation -fsanitize=thread -fuse-ld=gold" || { cat config.log && exit 1; }
|
||||
- mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
|
||||
- ${SRC_DIR}/configure ${CONFIG} --extra-cflags="-g3 -O0 -Wno-error=stringop-truncation -fsanitize=thread" --extra-ldflags="-fuse-ld=gold" || { cat config.log && exit 1; }
|
||||
|
||||
|
||||
# Run check-tcg against linux-user
|
||||
- env:
|
||||
- name: "GCC check-tcg (user)"
|
||||
env:
|
||||
- CONFIG="--disable-system --enable-debug-tcg"
|
||||
- TEST_CMD="make -j3 check-tcg V=1"
|
||||
- TEST_BUILD_CMD="make build-tcg"
|
||||
- TEST_CMD="make check-tcg"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg"
|
||||
|
||||
|
||||
# Run check-tcg against linux-user (with plugins)
|
||||
# we skip sparc64-linux-user until it has been fixed somewhat
|
||||
- env:
|
||||
- CONFIG="--disable-system --enable-plugins --enable-debug-tcg --target-list-exclude=sparc64-linux-user"
|
||||
- TEST_CMD="make -j3 check-tcg V=1"
|
||||
# we skip cris-linux-user as it doesn't use the common run loop
|
||||
- name: "GCC plugins check-tcg (user)"
|
||||
env:
|
||||
- CONFIG="--disable-system --enable-plugins --enable-debug-tcg --target-list-exclude=sparc64-linux-user,cris-linux-user"
|
||||
- TEST_BUILD_CMD="make build-tcg"
|
||||
- TEST_CMD="make check-tcg"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg"
|
||||
|
||||
|
||||
# Run check-tcg against softmmu targets
|
||||
- env:
|
||||
- name: "GCC check-tcg (some-softmmu)"
|
||||
env:
|
||||
- CONFIG="--enable-debug-tcg --target-list=xtensa-softmmu,arm-softmmu,aarch64-softmmu,alpha-softmmu"
|
||||
- TEST_CMD="make -j3 check-tcg V=1"
|
||||
- TEST_BUILD_CMD="make build-tcg"
|
||||
- TEST_CMD="make check-tcg"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg"
|
||||
|
||||
|
||||
# Run check-tcg against softmmu targets (with plugins)
|
||||
- env:
|
||||
- name: "GCC plugins check-tcg (some-softmmu)"
|
||||
env:
|
||||
- CONFIG="--enable-plugins --enable-debug-tcg --target-list=xtensa-softmmu,arm-softmmu,aarch64-softmmu,alpha-softmmu"
|
||||
- TEST_CMD="make -j3 check-tcg V=1"
|
||||
- TEST_BUILD_CMD="make build-tcg"
|
||||
- TEST_CMD="make check-tcg"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg"
|
||||
|
||||
- name: "[aarch64] GCC check-tcg"
|
||||
arch: arm64
|
||||
dist: xenial
|
||||
addons:
|
||||
apt_packages:
|
||||
- libaio-dev
|
||||
- libattr1-dev
|
||||
- libbrlapi-dev
|
||||
- libcap-ng-dev
|
||||
- libgcrypt20-dev
|
||||
- libgnutls28-dev
|
||||
- libgtk-3-dev
|
||||
- libiscsi-dev
|
||||
- liblttng-ust-dev
|
||||
- libncurses5-dev
|
||||
- libnfs-dev
|
||||
- libnss3-dev
|
||||
- libpixman-1-dev
|
||||
- libpng-dev
|
||||
- librados-dev
|
||||
- libsdl2-dev
|
||||
- libseccomp-dev
|
||||
- liburcu-dev
|
||||
- libusb-1.0-0-dev
|
||||
- libvdeplug-dev
|
||||
- libvte-2.91-dev
|
||||
# Tests dependencies
|
||||
- genisoimage
|
||||
env:
|
||||
- TEST_CMD="make check check-tcg V=1"
|
||||
- CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS}"
|
||||
|
||||
- name: "[ppc64] GCC check-tcg"
|
||||
arch: ppc64le
|
||||
dist: xenial
|
||||
addons:
|
||||
apt_packages:
|
||||
- libaio-dev
|
||||
- libattr1-dev
|
||||
- libbrlapi-dev
|
||||
- libcap-ng-dev
|
||||
- libgcrypt20-dev
|
||||
- libgnutls28-dev
|
||||
- libgtk-3-dev
|
||||
- libiscsi-dev
|
||||
- liblttng-ust-dev
|
||||
- libncurses5-dev
|
||||
- libnfs-dev
|
||||
- libnss3-dev
|
||||
- libpixman-1-dev
|
||||
- libpng-dev
|
||||
- librados-dev
|
||||
- libsdl2-dev
|
||||
- libseccomp-dev
|
||||
- liburcu-dev
|
||||
- libusb-1.0-0-dev
|
||||
- libvdeplug-dev
|
||||
- libvte-2.91-dev
|
||||
# Tests dependencies
|
||||
- genisoimage
|
||||
env:
|
||||
- TEST_CMD="make check check-tcg V=1"
|
||||
- CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS},ppc64le-linux-user"
|
||||
|
||||
- name: "[s390x] GCC check-tcg"
|
||||
arch: s390x
|
||||
dist: bionic
|
||||
addons:
|
||||
apt_packages:
|
||||
- libaio-dev
|
||||
- libattr1-dev
|
||||
- libbrlapi-dev
|
||||
- libcap-ng-dev
|
||||
- libgcrypt20-dev
|
||||
- libgnutls28-dev
|
||||
- libgtk-3-dev
|
||||
- libiscsi-dev
|
||||
- liblttng-ust-dev
|
||||
- libncurses5-dev
|
||||
- libnfs-dev
|
||||
- libnss3-dev
|
||||
- libpixman-1-dev
|
||||
- libpng-dev
|
||||
- librados-dev
|
||||
- libsdl2-dev
|
||||
- libseccomp-dev
|
||||
- liburcu-dev
|
||||
- libusb-1.0-0-dev
|
||||
- libvdeplug-dev
|
||||
- libvte-2.91-dev
|
||||
# Tests dependencies
|
||||
- genisoimage
|
||||
env:
|
||||
- TEST_CMD="make check check-tcg V=1"
|
||||
- CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS},s390x-linux-user"
|
||||
script:
|
||||
- ( cd ${SRC_DIR} ; git submodule update --init roms/SLOF )
|
||||
- BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$?
|
||||
- |
|
||||
if [ "$BUILD_RC" -eq 0 ] ; then
|
||||
mv pc-bios/s390-ccw/*.img pc-bios/ ;
|
||||
${TEST_CMD} ;
|
||||
else
|
||||
$(exit $BUILD_RC);
|
||||
fi
|
||||
|
||||
- name: "[s390x] GCC check (KVM)"
|
||||
arch: s390x
|
||||
dist: bionic
|
||||
addons:
|
||||
apt_packages:
|
||||
- libaio-dev
|
||||
- libattr1-dev
|
||||
- libbrlapi-dev
|
||||
- libcap-ng-dev
|
||||
- libgcrypt20-dev
|
||||
- libgnutls28-dev
|
||||
- libgtk-3-dev
|
||||
- libiscsi-dev
|
||||
- liblttng-ust-dev
|
||||
- libncurses5-dev
|
||||
- libnfs-dev
|
||||
- libnss3-dev
|
||||
- libpixman-1-dev
|
||||
- libpng-dev
|
||||
- librados-dev
|
||||
- libsdl2-dev
|
||||
- libseccomp-dev
|
||||
- liburcu-dev
|
||||
- libusb-1.0-0-dev
|
||||
- libvdeplug-dev
|
||||
- libvte-2.91-dev
|
||||
# Tests dependencies
|
||||
- genisoimage
|
||||
env:
|
||||
- TEST_CMD="make check-unit"
|
||||
- CONFIG="--disable-containers --disable-tcg --enable-kvm --disable-tools"
|
||||
script:
|
||||
- ( cd ${SRC_DIR} ; git submodule update --init roms/SLOF )
|
||||
- BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$?
|
||||
- |
|
||||
if [ "$BUILD_RC" -eq 0 ] ; then
|
||||
mv pc-bios/s390-ccw/*.img pc-bios/ ;
|
||||
${TEST_CMD} ;
|
||||
else
|
||||
$(exit $BUILD_RC);
|
||||
fi
|
||||
|
||||
# Release builds
|
||||
# The make-release script expect a QEMU version, so our tag must start with a 'v'.
|
||||
# This is the case when release candidate tags are created.
|
||||
- if: tag IS present AND tag =~ /^v\d+\.\d+(\.\d+)?(-\S*)?$/
|
||||
- name: "Release tarball"
|
||||
if: tag IS present AND tag =~ /^v\d+\.\d+(\.\d+)?(-\S*)?$/
|
||||
env:
|
||||
# We want to build from the release tarball
|
||||
- BUILD_DIR="release/build/dir" SRC_DIR="../../.."
|
||||
- BASE_CONFIG="--prefix=$PWD/dist"
|
||||
- CONFIG="--target-list=x86_64-softmmu,aarch64-softmmu,armeb-linux-user,ppc-linux-user"
|
||||
- TEST_CMD="make install -j3"
|
||||
- TEST_CMD="make install -j${JOBS}"
|
||||
- QEMU_VERSION="${TRAVIS_TAG:1}"
|
||||
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
|
||||
before_script:
|
||||
- command -v ccache && ccache --zero-stats
|
||||
- mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
|
||||
script:
|
||||
- make -C ${SRC_DIR} qemu-${QEMU_VERSION}.tar.bz2
|
||||
- ls -l ${SRC_DIR}/qemu-${QEMU_VERSION}.tar.bz2
|
||||
- tar -xf ${SRC_DIR}/qemu-${QEMU_VERSION}.tar.bz2 && cd qemu-${QEMU_VERSION}
|
||||
- ./configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; }
|
||||
- mkdir -p release-build && cd release-build
|
||||
- ../configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; }
|
||||
- make install
|
||||
|
|
|
@ -25,6 +25,11 @@ config TPM
|
|||
|
||||
config VHOST_USER
|
||||
bool
|
||||
select VHOST
|
||||
|
||||
config VHOST_KERNEL
|
||||
bool
|
||||
select VHOST
|
||||
|
||||
config XEN
|
||||
bool
|
||||
|
|
344
MAINTAINERS
344
MAINTAINERS
|
@ -101,7 +101,8 @@ F: include/hw/watchdog/wdt_diag288.h
|
|||
F: pc-bios/s390-ccw/
|
||||
F: pc-bios/s390-ccw.img
|
||||
F: target/s390x/
|
||||
F: docs/vfio-ap.txt
|
||||
F: docs/system/target-s390x.rst
|
||||
F: docs/system/s390x/
|
||||
F: tests/migration/s390x/
|
||||
K: ^Subject:.*(?i)s390x?
|
||||
T: git https://github.com/cohuck/qemu.git s390-next
|
||||
|
@ -155,6 +156,7 @@ F: include/hw/cpu/a*mpcore.h
|
|||
F: disas/arm.c
|
||||
F: disas/arm-a64.cc
|
||||
F: disas/libvixl/
|
||||
F: docs/system/target-arm.rst
|
||||
|
||||
ARM SMMU
|
||||
M: Eric Auger <eric.auger@redhat.com>
|
||||
|
@ -178,6 +180,8 @@ S: Maintained
|
|||
F: target/hppa/
|
||||
F: hw/hppa/
|
||||
F: disas/hppa.c
|
||||
F: hw/net/*i82596*
|
||||
F: include/hw/net/lasi_82596.h
|
||||
|
||||
LM32 TCG CPUs
|
||||
M: Michael Walle <michael@walle.cc>
|
||||
|
@ -206,13 +210,14 @@ F: hw/microblaze/
|
|||
F: disas/microblaze.c
|
||||
|
||||
MIPS TCG CPUs
|
||||
M: Aurelien Jarno <aurelien@aurel32.net>
|
||||
M: Aleksandar Markovic <amarkovic@wavecomp.com>
|
||||
M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
|
||||
R: Aurelien Jarno <aurelien@aurel32.net>
|
||||
R: Aleksandar Rikalo <aleksandar.rikalo@rt-rk.com>
|
||||
S: Maintained
|
||||
F: target/mips/
|
||||
F: default-configs/*mips*
|
||||
F: disas/*mips*
|
||||
F: docs/system/cpu-models-mips.rst.inc
|
||||
F: hw/intc/mips_gic.c
|
||||
F: hw/mips/
|
||||
F: hw/misc/mips_*
|
||||
|
@ -221,6 +226,8 @@ F: include/hw/intc/mips_gic.h
|
|||
F: include/hw/mips/
|
||||
F: include/hw/misc/mips_*
|
||||
F: include/hw/timer/mips_gictimer.h
|
||||
F: tests/acceptance/linux_ssh_mips_malta.py
|
||||
F: tests/acceptance/machine_mips_malta.py
|
||||
F: tests/tcg/mips/
|
||||
K: ^Subject:.*(?i)mips
|
||||
|
||||
|
@ -271,6 +278,11 @@ F: include/hw/riscv/
|
|||
F: linux-user/host/riscv32/
|
||||
F: linux-user/host/riscv64/
|
||||
|
||||
RENESAS RX CPUs
|
||||
M: Yoshinori Sato <ysato@users.sourceforge.jp>
|
||||
S: Maintained
|
||||
F: target/rx/
|
||||
|
||||
S390 TCG CPUs
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
M: David Hildenbrand <david@redhat.com>
|
||||
|
@ -316,7 +328,7 @@ F: tests/tcg/i386/
|
|||
F: tests/tcg/x86_64/
|
||||
F: hw/i386/
|
||||
F: disas/i386.c
|
||||
F: docs/qemu-cpu-models.texi
|
||||
F: docs/system/cpu-models-x86.rst.inc
|
||||
T: git https://github.com/ehabkost/qemu.git x86-next
|
||||
|
||||
Xtensa TCG CPUs
|
||||
|
@ -362,9 +374,8 @@ S: Maintained
|
|||
F: target/arm/kvm.c
|
||||
|
||||
MIPS KVM CPUs
|
||||
M: James Hogan <jhogan@kernel.org>
|
||||
R: Aleksandar Rikalo <aleksandar.rikalo@rt-rk.com>
|
||||
S: Maintained
|
||||
M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
|
||||
S: Odd Fixes
|
||||
F: target/mips/kvm.c
|
||||
|
||||
PPC KVM CPUs
|
||||
|
@ -401,6 +412,21 @@ S: Supported
|
|||
F: target/i386/kvm.c
|
||||
F: scripts/kvm/vmxcap
|
||||
|
||||
X86 HVF CPUs
|
||||
M: Roman Bolshakov <r.bolshakov@yadro.com>
|
||||
S: Maintained
|
||||
F: accel/stubs/hvf-stub.c
|
||||
F: target/i386/hvf/
|
||||
F: include/sysemu/hvf.h
|
||||
|
||||
WHPX CPUs
|
||||
M: Sunil Muthuswamy <sunilmut@microsoft.com>
|
||||
S: Supported
|
||||
F: target/i386/whpx-all.c
|
||||
F: target/i386/whp-dispatch.h
|
||||
F: accel/stubs/whpx-stub.c
|
||||
F: include/sysemu/whpx.h
|
||||
|
||||
Guest CPU Cores (Xen)
|
||||
---------------------
|
||||
X86 Xen CPUs
|
||||
|
@ -414,15 +440,28 @@ F: hw/9pfs/xen-9p*
|
|||
F: hw/char/xen_console.c
|
||||
F: hw/display/xenfb.c
|
||||
F: hw/net/xen_nic.c
|
||||
F: hw/usb/xen-usb.c
|
||||
F: hw/block/xen*
|
||||
F: hw/block/dataplane/xen*
|
||||
F: hw/xen/
|
||||
F: hw/xenpv/
|
||||
F: hw/i386/xen/
|
||||
F: hw/pci-host/xen_igd_pt.c
|
||||
F: include/hw/block/dataplane/xen*
|
||||
F: include/hw/xen/
|
||||
F: include/sysemu/xen-mapcache.h
|
||||
|
||||
Guest CPU Cores (HAXM)
|
||||
---------------------
|
||||
X86 HAXM CPUs
|
||||
M: Wenchao Wang <wenchao.wang@intel.com>
|
||||
M: Colin Xu <colin.xu@intel.com>
|
||||
L: haxm-team@intel.com
|
||||
W: https://github.com/intel/haxm/issues
|
||||
S: Maintained
|
||||
F: include/sysemu/hax.h
|
||||
F: target/i386/hax-*
|
||||
|
||||
Hosts
|
||||
-----
|
||||
LINUX
|
||||
|
@ -479,6 +518,15 @@ F: hw/*/allwinner*
|
|||
F: include/hw/*/allwinner*
|
||||
F: hw/arm/cubieboard.c
|
||||
|
||||
Allwinner-h3
|
||||
M: Niek Linnenbank <nieklinnenbank@gmail.com>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/*/allwinner-h3*
|
||||
F: include/hw/*/allwinner-h3*
|
||||
F: hw/arm/orangepi.c
|
||||
F: docs/system/orangepi.rst
|
||||
|
||||
ARM PrimeCell and CMSDK devices
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
|
@ -531,7 +579,7 @@ F: include/hw/misc/arm11scu.h
|
|||
F: include/hw/timer/a9gtimer.h
|
||||
F: include/hw/timer/arm_mptimer.h
|
||||
F: include/hw/timer/armv7m_systick.h
|
||||
F: tests/test-arm-mptimer.c
|
||||
F: tests/qtest/test-arm-mptimer.c
|
||||
|
||||
Exynos
|
||||
M: Igor Mitsyanko <i.mitsyanko@gmail.com>
|
||||
|
@ -558,6 +606,14 @@ F: include/hw/arm/digic.h
|
|||
F: hw/*/digic*
|
||||
F: include/hw/*/digic*
|
||||
|
||||
Goldfish RTC
|
||||
M: Anup Patel <anup.patel@wdc.com>
|
||||
M: Alistair Francis <Alistair.Francis@wdc.com>
|
||||
L: qemu-riscv@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/rtc/goldfish_rtc.c
|
||||
F: include/hw/rtc/goldfish_rtc.h
|
||||
|
||||
Gumstix
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
R: Philippe Mathieu-Daudé <f4bug@amsat.org>
|
||||
|
@ -594,6 +650,8 @@ S: Maintained
|
|||
F: hw/arm/integratorcp.c
|
||||
F: hw/misc/arm_integrator_debug.c
|
||||
F: include/hw/misc/arm_integrator_debug.h
|
||||
F: tests/acceptance/machine_arm_integratorcp.py
|
||||
F: docs/system/arm/integratorcp.rst
|
||||
|
||||
MCIMX6UL EVK / i.MX6ul
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
|
@ -652,6 +710,7 @@ M: Peter Maydell <peter.maydell@linaro.org>
|
|||
L: qemu-arm@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: hw/arm/musicpal.c
|
||||
F: docs/system/arm/musicpal.rst
|
||||
|
||||
nSeries
|
||||
M: Andrzej Zaborowski <balrogg@gmail.com>
|
||||
|
@ -667,6 +726,8 @@ F: hw/rtc/twl92230.c
|
|||
F: include/hw/display/blizzard.h
|
||||
F: include/hw/input/tsc2xxx.h
|
||||
F: include/hw/misc/cbus.h
|
||||
F: tests/acceptance/machine_arm_n8x0.py
|
||||
F: docs/system/arm/nseries.rst
|
||||
|
||||
Palm
|
||||
M: Andrzej Zaborowski <balrogg@gmail.com>
|
||||
|
@ -676,6 +737,7 @@ S: Odd Fixes
|
|||
F: hw/arm/palm.c
|
||||
F: hw/input/tsc210x.c
|
||||
F: include/hw/input/tsc2xxx.h
|
||||
F: docs/system/arm/palm.rst
|
||||
|
||||
Raspberry Pi
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
|
@ -697,6 +759,7 @@ F: hw/arm/realview*
|
|||
F: hw/cpu/realview_mpcore.c
|
||||
F: hw/intc/realview_gic.c
|
||||
F: include/hw/intc/realview_gic.h
|
||||
F: docs/system/arm/realview.rst
|
||||
|
||||
PXA2XX
|
||||
M: Andrzej Zaborowski <balrogg@gmail.com>
|
||||
|
@ -716,6 +779,7 @@ F: hw/misc/max111x.c
|
|||
F: include/hw/arm/pxa.h
|
||||
F: include/hw/arm/sharpsl.h
|
||||
F: include/hw/display/tc6393xb.h
|
||||
F: docs/system/arm/xscale.rst
|
||||
|
||||
SABRELITE / i.MX6
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
|
@ -726,6 +790,8 @@ F: hw/arm/sabrelite.c
|
|||
F: hw/arm/fsl-imx6.c
|
||||
F: hw/misc/imx6_*.c
|
||||
F: hw/ssi/imx_spi.c
|
||||
F: hw/usb/imx-usb-phy.c
|
||||
F: include/hw/usb/imx-usb-phy.h
|
||||
F: include/hw/arm/fsl-imx6.h
|
||||
F: include/hw/misc/imx6_*.h
|
||||
F: include/hw/ssi/imx_spi.h
|
||||
|
@ -733,7 +799,7 @@ F: include/hw/ssi/imx_spi.h
|
|||
SBSA-REF
|
||||
M: Radoslaw Biernacki <radoslaw.biernacki@linaro.org>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
R: Leif Lindholm <leif.lindholm@linaro.org>
|
||||
R: Leif Lindholm <leif@nuviainc.com>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/sbsa-ref.c
|
||||
|
@ -751,6 +817,7 @@ L: qemu-arm@nongnu.org
|
|||
S: Maintained
|
||||
F: hw/*/stellaris*
|
||||
F: include/hw/input/gamepad.h
|
||||
F: docs/system/arm/stellaris.rst
|
||||
|
||||
Versatile Express
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
|
@ -764,6 +831,7 @@ L: qemu-arm@nongnu.org
|
|||
S: Maintained
|
||||
F: hw/*/versatile*
|
||||
F: hw/misc/arm_sysctl.c
|
||||
F: docs/system/arm/versatile.rst
|
||||
|
||||
Virt
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
|
@ -805,6 +873,7 @@ F: hw/arm/virt-acpi-build.c
|
|||
STM32F205
|
||||
M: Alistair Francis <alistair@alistair23.me>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/stm32f205_soc.c
|
||||
F: hw/misc/stm32f2xx_syscfg.c
|
||||
|
@ -814,15 +883,33 @@ F: hw/adc/*
|
|||
F: hw/ssi/stm32f2xx_spi.c
|
||||
F: include/hw/*/stm32*.h
|
||||
|
||||
STM32F405
|
||||
M: Alistair Francis <alistair@alistair23.me>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/stm32f405_soc.c
|
||||
F: hw/misc/stm32f4xx_syscfg.c
|
||||
F: hw/misc/stm32f4xx_exti.c
|
||||
|
||||
Netduino 2
|
||||
M: Alistair Francis <alistair@alistair23.me>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/netduino2.c
|
||||
|
||||
Netduino Plus 2
|
||||
M: Alistair Francis <alistair@alistair23.me>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/netduinoplus2.c
|
||||
|
||||
SmartFusion2
|
||||
M: Subbaraya Sundeep <sundeep.lkml@gmail.com>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/msf2-soc.c
|
||||
F: hw/misc/msf2-sysreg.c
|
||||
|
@ -836,6 +923,7 @@ F: include/hw/ssi/mss-spi.h
|
|||
Emcraft M2S-FG484
|
||||
M: Subbaraya Sundeep <sundeep.lkml@gmail.com>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/msf2-som.c
|
||||
|
||||
|
@ -862,7 +950,7 @@ F: hw/*/nrf51*.c
|
|||
F: hw/*/microbit*.c
|
||||
F: include/hw/*/nrf51*.h
|
||||
F: include/hw/*/microbit*.h
|
||||
F: tests/microbit-test.c
|
||||
F: tests/qtest/microbit-test.c
|
||||
|
||||
CRIS Machines
|
||||
-------------
|
||||
|
@ -874,10 +962,11 @@ F: hw/*/etraxfs_*.c
|
|||
|
||||
HP-PARISC Machines
|
||||
------------------
|
||||
Dino
|
||||
HP B160L
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
R: Helge Deller <deller@gmx.de>
|
||||
S: Odd Fixes
|
||||
F: default-configs/hppa-softmmu.mak
|
||||
F: hw/hppa/
|
||||
F: pc-bios/hppa-firmware.img
|
||||
|
||||
|
@ -955,29 +1044,35 @@ F: hw/display/jazz_led.c
|
|||
F: hw/dma/rc4030.c
|
||||
|
||||
Malta
|
||||
M: Aurelien Jarno <aurelien@aurel32.net>
|
||||
R: Aleksandar Rikalo <aleksandar.rikalo@rt-rk.com>
|
||||
M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
|
||||
M: Philippe Mathieu-Daudé <f4bug@amsat.org>
|
||||
R: Aurelien Jarno <aurelien@aurel32.net>
|
||||
S: Maintained
|
||||
F: hw/isa/piix4.c
|
||||
F: hw/acpi/piix4.c
|
||||
F: hw/mips/mips_malta.c
|
||||
F: hw/mips/gt64xxx_pci.c
|
||||
F: include/hw/southbridge/piix.h
|
||||
F: tests/acceptance/linux_ssh_mips_malta.py
|
||||
F: tests/acceptance/machine_mips_malta.py
|
||||
|
||||
Mipssim
|
||||
M: Aleksandar Markovic <amarkovic@wavecomp.com>
|
||||
M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
|
||||
R: Aleksandar Rikalo <aleksandar.rikalo@rt-rk.com>
|
||||
S: Odd Fixes
|
||||
F: hw/mips/mips_mipssim.c
|
||||
F: hw/net/mipsnet.c
|
||||
|
||||
R4000
|
||||
M: Aurelien Jarno <aurelien@aurel32.net>
|
||||
M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
|
||||
R: Aurelien Jarno <aurelien@aurel32.net>
|
||||
R: Aleksandar Rikalo <aleksandar.rikalo@rt-rk.com>
|
||||
S: Maintained
|
||||
S: Obsolete
|
||||
F: hw/mips/mips_r4k.c
|
||||
|
||||
Fulong 2E
|
||||
M: Aleksandar Markovic <amarkovic@wavecomp.com>
|
||||
R: Aleksandar Rikalo <aleksandar.rikalo@rt-rk.com>
|
||||
M: Philippe Mathieu-Daudé <f4bug@amsat.org>
|
||||
M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
|
||||
S: Odd Fixes
|
||||
F: hw/mips/mips_fulong2e.c
|
||||
F: hw/isa/vt82c686.c
|
||||
|
@ -1081,7 +1176,6 @@ F: hw/dma/i82374.c
|
|||
F: hw/rtc/m48t59-isa.c
|
||||
F: include/hw/isa/pc87312.h
|
||||
F: include/hw/rtc/m48t59.h
|
||||
F: pc-bios/ppc_rom.bin
|
||||
F: tests/acceptance/ppc_prep_40p.py
|
||||
|
||||
sPAPR
|
||||
|
@ -1095,10 +1189,10 @@ F: include/hw/*/xics*
|
|||
F: pc-bios/slof.bin
|
||||
F: docs/specs/ppc-spapr-hcalls.txt
|
||||
F: docs/specs/ppc-spapr-hotplug.txt
|
||||
F: tests/spapr*
|
||||
F: tests/libqos/*spapr*
|
||||
F: tests/rtas*
|
||||
F: tests/libqos/rtas*
|
||||
F: tests/qtest/spapr*
|
||||
F: tests/qtest/libqos/*spapr*
|
||||
F: tests/qtest/rtas*
|
||||
F: tests/qtest/libqos/rtas*
|
||||
|
||||
PowerNV (Non-Virtualized)
|
||||
M: Cédric Le Goater <clg@kaod.org>
|
||||
|
@ -1110,7 +1204,7 @@ F: hw/intc/pnv*
|
|||
F: hw/intc/xics_pnv.c
|
||||
F: include/hw/ppc/pnv*
|
||||
F: pc-bios/skiboot.lid
|
||||
F: tests/pnv*
|
||||
F: tests/qtest/pnv*
|
||||
|
||||
virtex_ml507
|
||||
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
||||
|
@ -1215,7 +1309,7 @@ S: Supported
|
|||
F: hw/s390x/ipl.*
|
||||
F: pc-bios/s390-ccw/
|
||||
F: pc-bios/s390-ccw.img
|
||||
F: docs/devel/s390-dasd-ipl.txt
|
||||
F: docs/devel/s390-dasd-ipl.rst
|
||||
T: git https://github.com/borntraeger/qemu.git s390-next
|
||||
L: qemu-s390x@nongnu.org
|
||||
|
||||
|
@ -1258,7 +1352,7 @@ F: hw/misc/sga.c
|
|||
F: hw/isa/apm.c
|
||||
F: include/hw/isa/apm.h
|
||||
F: tests/test-x86-cpuid.c
|
||||
F: tests/test-x86-cpuid-compat.c
|
||||
F: tests/qtest/test-x86-cpuid-compat.c
|
||||
|
||||
PC Chipset
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
|
@ -1354,10 +1448,10 @@ F: hw/ide/
|
|||
F: hw/block/block.c
|
||||
F: hw/block/cdrom.c
|
||||
F: hw/block/hd-geometry.c
|
||||
F: tests/ide-test.c
|
||||
F: tests/ahci-test.c
|
||||
F: tests/cdrom-test.c
|
||||
F: tests/libqos/ahci*
|
||||
F: tests/qtest/ide-test.c
|
||||
F: tests/qtest/ahci-test.c
|
||||
F: tests/qtest/cdrom-test.c
|
||||
F: tests/qtest/libqos/ahci*
|
||||
T: git https://github.com/jnsnow/qemu.git ide
|
||||
|
||||
IPMI
|
||||
|
@ -1366,7 +1460,7 @@ S: Maintained
|
|||
F: include/hw/ipmi/*
|
||||
F: hw/ipmi/*
|
||||
F: hw/smbios/smbios_type_38.c
|
||||
F: tests/ipmi*
|
||||
F: tests/qtest/ipmi*
|
||||
T: git https://github.com/cminyard/qemu.git master-ipmi-rebase
|
||||
|
||||
Floppy
|
||||
|
@ -1375,11 +1469,12 @@ L: qemu-block@nongnu.org
|
|||
S: Supported
|
||||
F: hw/block/fdc.c
|
||||
F: include/hw/block/fdc.h
|
||||
F: tests/fdc-test.c
|
||||
F: tests/qtest/fdc-test.c
|
||||
T: git https://github.com/jnsnow/qemu.git ide
|
||||
|
||||
OMAP
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/*/omap*
|
||||
F: include/hw/arm/omap.h
|
||||
|
@ -1413,8 +1508,8 @@ F: hw/acpi/*
|
|||
F: hw/smbios/*
|
||||
F: hw/i386/acpi-build.[hc]
|
||||
F: hw/arm/virt-acpi-build.c
|
||||
F: tests/bios-tables-test.c
|
||||
F: tests/acpi-utils.[hc]
|
||||
F: tests/qtest/bios-tables-test.c
|
||||
F: tests/qtest/acpi-utils.[hc]
|
||||
F: tests/data/acpi/
|
||||
|
||||
ppc4xx
|
||||
|
@ -1437,7 +1532,7 @@ M: Jason Wang <jasowang@redhat.com>
|
|||
S: Odd Fixes
|
||||
F: hw/net/
|
||||
F: include/hw/net/
|
||||
F: tests/virtio-net-test.c
|
||||
F: tests/qtest/virtio-net-test.c
|
||||
F: docs/virtio-net-failover.rst
|
||||
T: git https://github.com/jasowang/qemu.git net
|
||||
|
||||
|
@ -1454,7 +1549,7 @@ R: Fam Zheng <fam@euphon.net>
|
|||
S: Supported
|
||||
F: include/hw/scsi/*
|
||||
F: hw/scsi/*
|
||||
F: tests/virtio-scsi-test.c
|
||||
F: tests/qtest/virtio-scsi-test.c
|
||||
T: git https://github.com/bonzini/qemu.git scsi-next
|
||||
|
||||
SSI
|
||||
|
@ -1464,7 +1559,7 @@ F: hw/ssi/*
|
|||
F: hw/block/m25p80.c
|
||||
F: include/hw/ssi/ssi.h
|
||||
X: hw/ssi/xilinx_*
|
||||
F: tests/m25p80-test.c
|
||||
F: tests/qtest/m25p80-test.c
|
||||
|
||||
Xilinx SPI
|
||||
M: Alistair Francis <alistair@alistair23.me>
|
||||
|
@ -1477,13 +1572,14 @@ S: Odd Fixes
|
|||
F: include/hw/sd/sd*
|
||||
F: hw/sd/core.c
|
||||
F: hw/sd/sd*
|
||||
F: tests/sd*
|
||||
F: hw/sd/ssi-sd.c
|
||||
F: tests/qtest/sd*
|
||||
|
||||
USB
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
F: hw/usb/*
|
||||
F: tests/usb-*-test.c
|
||||
F: tests/qtest/usb-*-test.c
|
||||
F: docs/usb2.txt
|
||||
F: docs/usb-storage.txt
|
||||
F: include/hw/usb.h
|
||||
|
@ -1524,7 +1620,7 @@ F: hw/s390x/ap-bridge.c
|
|||
F: include/hw/s390x/ap-device.h
|
||||
F: include/hw/s390x/ap-bridge.h
|
||||
F: hw/vfio/ap.c
|
||||
F: docs/vfio-ap.txt
|
||||
F: docs/system/s390x/vfio-ap.rst
|
||||
L: qemu-s390x@nongnu.org
|
||||
|
||||
vhost
|
||||
|
@ -1545,15 +1641,25 @@ F: hw/virtio/Makefile.objs
|
|||
F: hw/virtio/trace-events
|
||||
F: net/vhost-user.c
|
||||
F: include/hw/virtio/
|
||||
F: tests/virtio-balloon-test.c
|
||||
|
||||
virtio-balloon
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
M: David Hildenbrand <david@redhat.com>
|
||||
S: Maintained
|
||||
F: hw/virtio/virtio-balloon*.c
|
||||
F: include/hw/virtio/virtio-balloon.h
|
||||
F: balloon.c
|
||||
F: include/sysemu/balloon.h
|
||||
|
||||
virtio-9p
|
||||
M: Greg Kurz <groug@kaod.org>
|
||||
R: Christian Schoenebeck <qemu_oss@crudebyte.com>
|
||||
S: Odd Fixes
|
||||
F: hw/9pfs/
|
||||
X: hw/9pfs/xen-9p*
|
||||
F: fsdev/
|
||||
F: tests/virtio-9p-test.c
|
||||
F: docs/interop/virtfs-proxy-helper.rst
|
||||
F: tests/qtest/virtio-9p-test.c
|
||||
T: git https://github.com/gkurz/qemu.git 9p-next
|
||||
|
||||
virtio-blk
|
||||
|
@ -1562,7 +1668,7 @@ L: qemu-block@nongnu.org
|
|||
S: Supported
|
||||
F: hw/block/virtio-blk.c
|
||||
F: hw/block/dataplane/*
|
||||
F: tests/virtio-blk-test.c
|
||||
F: tests/qtest/virtio-blk-test.c
|
||||
T: git https://github.com/stefanha/qemu.git block
|
||||
|
||||
virtio-ccw
|
||||
|
@ -1575,6 +1681,15 @@ T: git https://github.com/cohuck/qemu.git s390-next
|
|||
T: git https://github.com/borntraeger/qemu.git s390-next
|
||||
L: qemu-s390x@nongnu.org
|
||||
|
||||
virtiofs
|
||||
M: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
S: Supported
|
||||
F: tools/virtiofsd/*
|
||||
F: hw/virtio/vhost-user-fs*
|
||||
F: include/hw/virtio/vhost-user-fs.h
|
||||
F: docs/interop/virtiofsd.rst
|
||||
|
||||
virtio-input
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
|
@ -1583,6 +1698,12 @@ F: hw/input/virtio-input*.c
|
|||
F: include/hw/virtio/virtio-input.h
|
||||
F: contrib/vhost-user-input/*
|
||||
|
||||
virtio-iommu
|
||||
M: Eric Auger <eric.auger@redhat.com>
|
||||
S: Maintained
|
||||
F: hw/virtio/virtio-iommu*.c
|
||||
F: include/hw/virtio/virtio-iommu.h
|
||||
|
||||
virtio-serial
|
||||
M: Laurent Vivier <lvivier@redhat.com>
|
||||
R: Amit Shah <amit@kernel.org>
|
||||
|
@ -1590,8 +1711,7 @@ S: Supported
|
|||
F: hw/char/virtio-serial-bus.c
|
||||
F: hw/char/virtio-console.c
|
||||
F: include/hw/virtio/virtio-serial.h
|
||||
F: tests/virtio-console-test.c
|
||||
F: tests/virtio-serial-test.c
|
||||
F: tests/qtest/virtio-serial-test.c
|
||||
|
||||
virtio-rng
|
||||
M: Laurent Vivier <lvivier@redhat.com>
|
||||
|
@ -1601,7 +1721,7 @@ F: hw/virtio/virtio-rng.c
|
|||
F: include/hw/virtio/virtio-rng.h
|
||||
F: include/sysemu/rng*.h
|
||||
F: backends/rng*.c
|
||||
F: tests/virtio-rng-test.c
|
||||
F: tests/qtest/virtio-rng-test.c
|
||||
|
||||
virtio-crypto
|
||||
M: Gonglei <arei.gonglei@huawei.com>
|
||||
|
@ -1615,7 +1735,7 @@ M: Keith Busch <keith.busch@intel.com>
|
|||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: hw/block/nvme*
|
||||
F: tests/nvme-test.c
|
||||
F: tests/qtest/nvme-test.c
|
||||
|
||||
megasas
|
||||
M: Hannes Reinecke <hare@suse.com>
|
||||
|
@ -1623,7 +1743,7 @@ L: qemu-block@nongnu.org
|
|||
S: Supported
|
||||
F: hw/scsi/megasas.c
|
||||
F: hw/scsi/mfi.h
|
||||
F: tests/megasas-test.c
|
||||
F: tests/qtest/megasas-test.c
|
||||
|
||||
Network packet abstractions
|
||||
M: Dmitry Fleytman <dmitry.fleytman@gmail.com>
|
||||
|
@ -1638,7 +1758,7 @@ M: Dmitry Fleytman <dmitry.fleytman@gmail.com>
|
|||
S: Maintained
|
||||
F: hw/net/vmxnet*
|
||||
F: hw/scsi/vmw_pvscsi*
|
||||
F: tests/vmxnet3-test.c
|
||||
F: tests/qtest/vmxnet3-test.c
|
||||
|
||||
Rocker
|
||||
M: Jiri Pirko <jiri@resnulli.us>
|
||||
|
@ -1686,7 +1806,7 @@ F: docs/generic-loader.txt
|
|||
Intel Hexadecimal Object File Loader
|
||||
M: Su Hang <suhang16@mails.ucas.ac.cn>
|
||||
S: Maintained
|
||||
F: tests/hexloader-test.c
|
||||
F: tests/qtest/hexloader-test.c
|
||||
F: tests/data/hex-loader/test.hex
|
||||
|
||||
CHRP NVRAM
|
||||
|
@ -1694,7 +1814,7 @@ M: Thomas Huth <thuth@redhat.com>
|
|||
S: Maintained
|
||||
F: hw/nvram/chrp_nvram.c
|
||||
F: include/hw/nvram/chrp_nvram.h
|
||||
F: tests/prom-env-test.c
|
||||
F: tests/qtest/prom-env-test.c
|
||||
|
||||
VM Generation ID
|
||||
M: Ben Warren <ben@skyportsystems.com>
|
||||
|
@ -1702,7 +1822,7 @@ S: Maintained
|
|||
F: hw/acpi/vmgenid.c
|
||||
F: include/hw/acpi/vmgenid.h
|
||||
F: docs/specs/vmgenid.txt
|
||||
F: tests/vmgenid-test.c
|
||||
F: tests/qtest/vmgenid-test.c
|
||||
F: stubs/vmgenid.c
|
||||
|
||||
Unimplemented device
|
||||
|
@ -1733,6 +1853,18 @@ F: hw/display/virtio-gpu*
|
|||
F: hw/display/virtio-vga.*
|
||||
F: include/hw/virtio/virtio-gpu.h
|
||||
|
||||
vhost-user-blk
|
||||
M: Raphael Norwitz <raphael.norwitz@nutanix.com>
|
||||
S: Maintained
|
||||
F: contrib/vhost-user-blk/
|
||||
F: contrib/vhost-user-scsi/
|
||||
F: hw/block/vhost-user-blk.c
|
||||
F: hw/scsi/vhost-user-scsi.c
|
||||
F: hw/virtio/vhost-user-blk-pci.c
|
||||
F: hw/virtio/vhost-user-scsi-pci.c
|
||||
F: include/hw/virtio/vhost-user-blk.h
|
||||
F: include/hw/virtio/vhost-user-scsi.h
|
||||
|
||||
vhost-user-gpu
|
||||
M: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
|
@ -1771,8 +1903,8 @@ F: hw/nvram/fw_cfg.c
|
|||
F: stubs/fw_cfg.c
|
||||
F: include/hw/nvram/fw_cfg.h
|
||||
F: include/standard-headers/linux/qemu_fw_cfg.h
|
||||
F: tests/libqos/fw_cfg.c
|
||||
F: tests/fw_cfg-test.c
|
||||
F: tests/qtest/libqos/fw_cfg.c
|
||||
F: tests/qtest/fw_cfg-test.c
|
||||
T: git https://github.com/philmd/qemu.git fw_cfg-next
|
||||
|
||||
XIVE
|
||||
|
@ -1792,9 +1924,9 @@ S: Maintained
|
|||
F: audio/
|
||||
F: hw/audio/
|
||||
F: include/hw/audio/
|
||||
F: tests/ac97-test.c
|
||||
F: tests/es1370-test.c
|
||||
F: tests/intel-hda-test.c
|
||||
F: tests/qtest/ac97-test.c
|
||||
F: tests/qtest/es1370-test.c
|
||||
F: tests/qtest/intel-hda-test.c
|
||||
|
||||
Block layer core
|
||||
M: Kevin Wolf <kwolf@redhat.com>
|
||||
|
@ -1806,6 +1938,7 @@ F: block/
|
|||
F: hw/block/
|
||||
F: include/block/
|
||||
F: qemu-img*
|
||||
F: docs/interop/qemu-img.rst
|
||||
F: qemu-io*
|
||||
F: tests/qemu-iotests/
|
||||
F: util/qemu-progress.c
|
||||
|
@ -1820,6 +1953,8 @@ L: qemu-block@nongnu.org
|
|||
S: Supported
|
||||
F: util/async.c
|
||||
F: util/aio-*.c
|
||||
F: util/aio-*.h
|
||||
F: util/fdmon-*.c
|
||||
F: block/io.c
|
||||
F: migration/block*
|
||||
F: include/block/aio.h
|
||||
|
@ -1855,6 +1990,7 @@ Block QAPI, monitor, command line
|
|||
M: Markus Armbruster <armbru@redhat.com>
|
||||
S: Supported
|
||||
F: blockdev.c
|
||||
F: blockdev-hmp-cmds.c
|
||||
F: block/qapi.c
|
||||
F: qapi/block*.json
|
||||
F: qapi/transaction.json
|
||||
|
@ -1865,12 +2001,12 @@ M: John Snow <jsnow@redhat.com>
|
|||
R: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: util/hbitmap.c
|
||||
F: block/dirty-bitmap.c
|
||||
F: include/qemu/hbitmap.h
|
||||
F: include/block/dirty-bitmap.h
|
||||
F: qcow2-bitmap.c
|
||||
F: block/dirty-bitmap.c
|
||||
F: block/qcow2-bitmap.c
|
||||
F: migration/block-dirty-bitmap.c
|
||||
F: util/hbitmap.c
|
||||
F: tests/test-hbitmap.c
|
||||
F: docs/interop/bitmaps.rst
|
||||
T: git https://github.com/jnsnow/qemu.git bitmaps
|
||||
|
@ -1902,6 +2038,11 @@ M: Markus Armbruster <armbru@redhat.com>
|
|||
S: Supported
|
||||
F: scripts/coverity-model.c
|
||||
|
||||
Coverity Scan integration
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
S: Maintained
|
||||
F: scripts/coverity-scan/
|
||||
|
||||
Device Tree
|
||||
M: Alistair Francis <alistair.francis@wdc.com>
|
||||
R: David Gibson <david@gibson.dropbear.id.au>
|
||||
|
@ -1930,6 +2071,11 @@ F: include/qemu/error-report.h
|
|||
F: qapi/error.json
|
||||
F: util/error.c
|
||||
F: util/qemu-error.c
|
||||
F: scripts/coccinelle/err-bad-newline.cocci
|
||||
F: scripts/coccinelle/error-use-after-free.cocci
|
||||
F: scripts/coccinelle/error_propagate_null.cocci
|
||||
F: scripts/coccinelle/remove_local_err.cocci
|
||||
F: scripts/coccinelle/use-error_fatal.cocci
|
||||
|
||||
GDB stub
|
||||
M: Alex Bennée <alex.bennee@linaro.org>
|
||||
|
@ -1946,9 +2092,11 @@ F: ioport.c
|
|||
F: include/exec/memop.h
|
||||
F: include/exec/memory.h
|
||||
F: include/exec/ram_addr.h
|
||||
F: include/exec/ramblock.h
|
||||
F: memory.c
|
||||
F: include/exec/memory-internal.h
|
||||
F: exec.c
|
||||
F: scripts/coccinelle/memory-region-housekeeping.cocci
|
||||
|
||||
SPICE
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
|
@ -1982,7 +2130,8 @@ F: include/qemu/main-loop.h
|
|||
F: include/sysemu/runstate.h
|
||||
F: util/main-loop.c
|
||||
F: util/qemu-timer.c
|
||||
F: vl.c
|
||||
F: softmmu/vl.c
|
||||
F: softmmu/main.c
|
||||
F: qapi/run-state.json
|
||||
|
||||
Human Monitor (HMP)
|
||||
|
@ -1995,7 +2144,7 @@ F: monitor/hmp*
|
|||
F: hmp.h
|
||||
F: hmp-commands*.hx
|
||||
F: include/monitor/hmp-target.h
|
||||
F: tests/test-hmp.c
|
||||
F: tests/qtest/test-hmp.c
|
||||
F: include/qemu/qemu-print.h
|
||||
F: util/qemu-print.c
|
||||
|
||||
|
@ -2038,6 +2187,11 @@ F: python/qemu/*py
|
|||
F: scripts/*.py
|
||||
F: tests/*.py
|
||||
|
||||
Benchmark util
|
||||
M: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
|
||||
S: Maintained
|
||||
F: scripts/simplebench/
|
||||
|
||||
QAPI
|
||||
M: Markus Armbruster <armbru@redhat.com>
|
||||
M: Michael Roth <mdroth@linux.vnet.ibm.com>
|
||||
|
@ -2121,8 +2275,8 @@ F: qapi/error.json
|
|||
F: docs/devel/*qmp-*
|
||||
F: docs/interop/*qmp-*
|
||||
F: scripts/qmp/
|
||||
F: tests/qmp-test.c
|
||||
F: tests/qmp-cmd-test.c
|
||||
F: tests/qtest/qmp-test.c
|
||||
F: tests/qtest/qmp-cmd-test.c
|
||||
T: git https://repo.or.cz/qemu/armbru.git qapi-next
|
||||
|
||||
qtest
|
||||
|
@ -2132,9 +2286,15 @@ R: Paolo Bonzini <pbonzini@redhat.com>
|
|||
S: Maintained
|
||||
F: qtest.c
|
||||
F: accel/qtest.c
|
||||
F: tests/libqtest*
|
||||
F: tests/libqos/
|
||||
F: tests/*-test.c
|
||||
F: tests/qtest/
|
||||
|
||||
Device Fuzzing
|
||||
M: Alexander Bulekov <alxndr@bu.edu>
|
||||
R: Paolo Bonzini <pbonzini@redhat.com>
|
||||
R: Bandan Das <bsd@redhat.com>
|
||||
R: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
S: Maintained
|
||||
F: tests/qtest/fuzz/
|
||||
|
||||
Register API
|
||||
M: Alistair Francis <alistair@alistair23.me>
|
||||
|
@ -2161,10 +2321,11 @@ M: Stefan Hajnoczi <stefanha@redhat.com>
|
|||
S: Maintained
|
||||
F: trace/
|
||||
F: trace-events
|
||||
F: qemu-option-trace.texi
|
||||
F: docs/qemu-option-trace.rst.inc
|
||||
F: scripts/tracetool.py
|
||||
F: scripts/tracetool/
|
||||
F: scripts/qemu-trace-stap*
|
||||
F: docs/interop/qemu-trace-stap.rst
|
||||
F: docs/devel/tracing.txt
|
||||
T: git https://github.com/stefanha/qemu.git tracing
|
||||
|
||||
|
@ -2178,7 +2339,7 @@ F: include/hw/acpi/tpm.h
|
|||
F: include/sysemu/tpm*
|
||||
F: qapi/tpm.json
|
||||
F: backends/tpm.c
|
||||
F: tests/*tpm*
|
||||
F: tests/qtest/*tpm*
|
||||
T: git https://github.com/stefanberger/qemu-tpm.git tpm-next
|
||||
|
||||
Checkpatch
|
||||
|
@ -2189,14 +2350,26 @@ Migration
|
|||
M: Juan Quintela <quintela@redhat.com>
|
||||
M: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
||||
S: Maintained
|
||||
F: hw/core/vmstate-if.c
|
||||
F: include/hw/vmstate-if.h
|
||||
F: include/migration/
|
||||
F: migration/
|
||||
F: scripts/vmstate-static-checker.py
|
||||
F: tests/vmstate-static-checker-data/
|
||||
F: tests/migration-test.c
|
||||
F: tests/qtest/migration-test.c
|
||||
F: docs/devel/migration.rst
|
||||
F: qapi/migration.json
|
||||
|
||||
D-Bus
|
||||
M: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
S: Maintained
|
||||
F: backends/dbus-vmstate.c
|
||||
F: tests/dbus-vmstate*
|
||||
F: util/dbus.c
|
||||
F: include/qemu/dbus.h
|
||||
F: docs/interop/dbus.rst
|
||||
F: docs/interop/dbus-vmstate.rst
|
||||
|
||||
Seccomp
|
||||
M: Eduardo Otubo <otubo@redhat.com>
|
||||
S: Supported
|
||||
|
@ -2339,6 +2512,8 @@ F: roms/edk2
|
|||
F: roms/edk2-*
|
||||
F: tests/data/uefi-boot-images/
|
||||
F: tests/uefi-test-tools/
|
||||
F: .gitlab-ci-edk2.yml
|
||||
F: .gitlab-ci.d/edk2/
|
||||
|
||||
Usermode Emulation
|
||||
------------------
|
||||
|
@ -2360,6 +2535,9 @@ S: Maintained
|
|||
F: linux-user/
|
||||
F: default-configs/*-linux-user.mak
|
||||
F: scripts/qemu-binfmt-conf.sh
|
||||
F: scripts/update-syscalltbl.sh
|
||||
F: scripts/update-mips-syscall-args.sh
|
||||
F: scripts/gensyscalls.sh
|
||||
|
||||
Tiny Code Generator (TCG)
|
||||
-------------------------
|
||||
|
@ -2367,6 +2545,7 @@ Common TCG code
|
|||
M: Richard Henderson <rth@twiddle.net>
|
||||
S: Maintained
|
||||
F: tcg/
|
||||
F: include/tcg/
|
||||
|
||||
TCG Plugins
|
||||
M: Alex Bennée <alex.bennee@linaro.org>
|
||||
|
@ -2376,8 +2555,7 @@ F: plugins/
|
|||
F: tests/plugin
|
||||
|
||||
AArch64 TCG target
|
||||
M: Claudio Fontana <claudio.fontana@huawei.com>
|
||||
M: Claudio Fontana <claudio.fontana@gmail.com>
|
||||
M: Richard Henderson <richard.henderson@linaro.org>
|
||||
S: Maintained
|
||||
L: qemu-arm@nongnu.org
|
||||
F: tcg/aarch64/
|
||||
|
@ -2398,7 +2576,8 @@ F: tcg/i386/
|
|||
F: disas/i386.c
|
||||
|
||||
MIPS TCG target
|
||||
M: Aurelien Jarno <aurelien@aurel32.net>
|
||||
M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
|
||||
R: Aurelien Jarno <aurelien@aurel32.net>
|
||||
R: Aleksandar Rikalo <aleksandar.rikalo@rt-rk.com>
|
||||
S: Maintained
|
||||
F: tcg/mips/
|
||||
|
@ -2488,6 +2667,7 @@ F: include/block/nbd*
|
|||
F: qemu-nbd.*
|
||||
F: blockdev-nbd.c
|
||||
F: docs/interop/nbd.txt
|
||||
F: docs/interop/qemu-nbd.rst
|
||||
T: git https://repo.or.cz/qemu/ericb.git nbd
|
||||
|
||||
NFS
|
||||
|
@ -2591,6 +2771,15 @@ F: block/file-posix.c
|
|||
F: block/file-win32.c
|
||||
F: block/win32-aio.c
|
||||
|
||||
Linux io_uring
|
||||
M: Aarushi Mehta <mehta.aaru20@gmail.com>
|
||||
M: Julia Suvorova <jusual@redhat.com>
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Maintained
|
||||
F: block/io_uring.c
|
||||
F: stubs/io_uring.c
|
||||
|
||||
qcow2
|
||||
M: Kevin Wolf <kwolf@redhat.com>
|
||||
M: Max Reitz <mreitz@redhat.com>
|
||||
|
@ -2640,7 +2829,7 @@ F: tests/test-replication.c
|
|||
F: docs/block-replication.txt
|
||||
|
||||
PVRDMA
|
||||
M: Yuval Shaia <yuval.shaia@oracle.com>
|
||||
M: Yuval Shaia <yuval.shaia.ml@gmail.com>
|
||||
M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
|
||||
S: Maintained
|
||||
F: hw/rdma/*
|
||||
|
@ -2662,6 +2851,7 @@ M: Alex Bennée <alex.bennee@linaro.org>
|
|||
M: Fam Zheng <fam@euphon.net>
|
||||
R: Philippe Mathieu-Daudé <philmd@redhat.com>
|
||||
S: Maintained
|
||||
F: .github/lockdown.yml
|
||||
F: .travis.yml
|
||||
F: scripts/travis/
|
||||
F: .shippable.yml
|
||||
|
@ -2706,7 +2896,7 @@ F: contrib/gitdm/*
|
|||
|
||||
Incompatible changes
|
||||
R: libvir-list@redhat.com
|
||||
F: qemu-deprecated.texi
|
||||
F: docs/system/deprecated.rst
|
||||
|
||||
Build System
|
||||
------------
|
||||
|
@ -2715,6 +2905,10 @@ M: Daniel P. Berrange <berrange@redhat.com>
|
|||
S: Odd Fixes
|
||||
F: scripts/git-submodule.sh
|
||||
|
||||
UI translations
|
||||
M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
|
||||
F: po/*.po
|
||||
|
||||
Sphinx documentation configuration and build machinery
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
S: Maintained
|
||||
|
|
270
Makefile
270
Makefile
|
@ -18,9 +18,6 @@ UNCHECKED_GOALS := %clean TAGS cscope ctags dist \
|
|||
help check-help print-% \
|
||||
docker docker-% vm-help vm-test vm-build-%
|
||||
|
||||
print-%:
|
||||
@echo '$*=$($*)'
|
||||
|
||||
# All following code might depend on configuration variables
|
||||
ifneq ($(wildcard config-host.mak),)
|
||||
# Put the all: rule here so that config-host.mak can contain dependencies.
|
||||
|
@ -120,6 +117,7 @@ GENERATED_QAPI_FILES += qapi/qapi-builtin-visit.h qapi/qapi-builtin-visit.c
|
|||
GENERATED_QAPI_FILES += qapi/qapi-visit.h qapi/qapi-visit.c
|
||||
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.h)
|
||||
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.c)
|
||||
GENERATED_QAPI_FILES += qapi/qapi-init-commands.h qapi/qapi-init-commands.c
|
||||
GENERATED_QAPI_FILES += qapi/qapi-commands.h qapi/qapi-commands.c
|
||||
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.h)
|
||||
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.c)
|
||||
|
@ -130,7 +128,28 @@ GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.c)
|
|||
GENERATED_QAPI_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h
|
||||
GENERATED_QAPI_FILES += qapi/qapi-doc.texi
|
||||
|
||||
# The following list considers only the storage daemon main module. All other
|
||||
# modules are currently shared with the main schema, so we don't actually
|
||||
# generate additional files.
|
||||
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES = storage-daemon/qapi/qapi-commands.h
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-commands.c
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-emit-events.h
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-emit-events.c
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-events.h
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-events.c
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-init-commands.h
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-init-commands.c
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-introspect.h
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-introspect.c
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-types.h
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-types.c
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-visit.h
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-visit.c
|
||||
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-doc.texi
|
||||
|
||||
generated-files-y += $(GENERATED_QAPI_FILES)
|
||||
generated-files-y += $(GENERATED_STORAGE_DAEMON_QAPI_FILES)
|
||||
|
||||
generated-files-y += trace/generated-tcg-tracers.h
|
||||
|
||||
|
@ -324,13 +343,14 @@ HELPERS-y =
|
|||
|
||||
HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) = qemu-bridge-helper$(EXESUF)
|
||||
|
||||
ifdef CONFIG_LINUX
|
||||
ifdef CONFIG_VIRGL
|
||||
ifdef CONFIG_GBM
|
||||
ifeq ($(CONFIG_LINUX)$(CONFIG_VIRGL)$(CONFIG_GBM)$(CONFIG_TOOLS),yyyy)
|
||||
HELPERS-y += vhost-user-gpu$(EXESUF)
|
||||
vhost-user-json-y += contrib/vhost-user-gpu/50-qemu-gpu.json
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy)
|
||||
HELPERS-y += virtiofsd$(EXESUF)
|
||||
vhost-user-json-y += tools/virtiofsd/50-qemu-virtiofsd.json
|
||||
endif
|
||||
|
||||
# Sphinx does not allow building manuals into the same directory as
|
||||
|
@ -345,16 +365,23 @@ MANUAL_BUILDDIR := docs
|
|||
endif
|
||||
|
||||
ifdef BUILD_DOCS
|
||||
DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 $(MANUAL_BUILDDIR)/interop/qemu-ga.8
|
||||
DOCS+=$(MANUAL_BUILDDIR)/system/qemu.1
|
||||
DOCS+=$(MANUAL_BUILDDIR)/tools/qemu-img.1
|
||||
DOCS+=$(MANUAL_BUILDDIR)/tools/qemu-nbd.8
|
||||
DOCS+=$(MANUAL_BUILDDIR)/interop/qemu-ga.8
|
||||
ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy)
|
||||
DOCS+=$(MANUAL_BUILDDIR)/tools/virtiofsd.1
|
||||
endif
|
||||
DOCS+=$(MANUAL_BUILDDIR)/system/qemu-block-drivers.7
|
||||
DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7
|
||||
DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7
|
||||
DOCS+=docs/qemu-block-drivers.7
|
||||
DOCS+=docs/qemu-cpu-models.7
|
||||
DOCS+=$(MANUAL_BUILDDIR)/system/qemu-cpu-models.7
|
||||
DOCS+=$(MANUAL_BUILDDIR)/index.html
|
||||
ifdef CONFIG_VIRTFS
|
||||
DOCS+=fsdev/virtfs-proxy-helper.1
|
||||
DOCS+=$(MANUAL_BUILDDIR)/tools/virtfs-proxy-helper.1
|
||||
endif
|
||||
ifdef CONFIG_TRACE_SYSTEMTAP
|
||||
DOCS+=scripts/qemu-trace-stap.1
|
||||
DOCS+=$(MANUAL_BUILDDIR)/tools/qemu-trace-stap.1
|
||||
endif
|
||||
else
|
||||
DOCS=
|
||||
|
@ -389,15 +416,16 @@ MINIKCONF_ARGS = \
|
|||
CONFIG_OPENGL=$(CONFIG_OPENGL) \
|
||||
CONFIG_X11=$(CONFIG_X11) \
|
||||
CONFIG_VHOST_USER=$(CONFIG_VHOST_USER) \
|
||||
CONFIG_VHOST_KERNEL=$(CONFIG_VHOST_KERNEL) \
|
||||
CONFIG_VIRTFS=$(CONFIG_VIRTFS) \
|
||||
CONFIG_LINUX=$(CONFIG_LINUX) \
|
||||
CONFIG_PVRDMA=$(CONFIG_PVRDMA)
|
||||
|
||||
MINIKCONF_INPUTS = $(SRC_PATH)/Kconfig.host $(SRC_PATH)/hw/Kconfig \
|
||||
$(wildcard $(SRC_PATH)/hw/*/Kconfig)
|
||||
MINIKCONF_INPUTS = $(SRC_PATH)/Kconfig.host $(SRC_PATH)/hw/Kconfig
|
||||
MINIKCONF_DEPS = $(MINIKCONF_INPUTS) $(wildcard $(SRC_PATH)/hw/*/Kconfig)
|
||||
MINIKCONF = $(PYTHON) $(SRC_PATH)/scripts/minikconf.py \
|
||||
|
||||
$(SUBDIR_DEVICES_MAK): %/config-devices.mak: default-configs/%.mak $(MINIKCONF_INPUTS) $(BUILD_DIR)/config-host.mak
|
||||
$(SUBDIR_DEVICES_MAK): %/config-devices.mak: default-configs/%.mak $(MINIKCONF_DEPS) $(BUILD_DIR)/config-host.mak
|
||||
$(call quiet-command, $(MINIKCONF) $(MINIKCONF_ARGS) > $@.tmp, "GEN", "$@.tmp")
|
||||
$(call quiet-command, if test -f $@; then \
|
||||
if cmp -s $@.old $@; then \
|
||||
|
@ -433,6 +461,7 @@ dummy := $(call unnest-vars,, \
|
|||
elf2dmp-obj-y \
|
||||
ivshmem-client-obj-y \
|
||||
ivshmem-server-obj-y \
|
||||
virtiofsd-obj-y \
|
||||
rdmacm-mux-obj-y \
|
||||
libvhost-user-obj-y \
|
||||
vhost-user-scsi-obj-y \
|
||||
|
@ -442,16 +471,13 @@ dummy := $(call unnest-vars,, \
|
|||
qga-vss-dll-obj-y \
|
||||
block-obj-y \
|
||||
block-obj-m \
|
||||
storage-daemon-obj-y \
|
||||
storage-daemon-obj-m \
|
||||
crypto-obj-y \
|
||||
crypto-user-obj-y \
|
||||
qom-obj-y \
|
||||
io-obj-y \
|
||||
common-obj-y \
|
||||
common-obj-m \
|
||||
ui-obj-y \
|
||||
ui-obj-m \
|
||||
audio-obj-y \
|
||||
audio-obj-m \
|
||||
trace-obj-y)
|
||||
|
||||
include $(SRC_PATH)/tests/Makefile.include
|
||||
|
@ -474,11 +500,12 @@ config-host.h-timestamp: config-host.mak
|
|||
qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@")
|
||||
|
||||
TARGET_DIRS_RULES := $(foreach t, all clean install, $(addsuffix /$(t), $(TARGET_DIRS)))
|
||||
TARGET_DIRS_RULES := $(foreach t, all fuzz clean install, $(addsuffix /$(t), $(TARGET_DIRS)))
|
||||
|
||||
SOFTMMU_ALL_RULES=$(filter %-softmmu/all, $(TARGET_DIRS_RULES))
|
||||
$(SOFTMMU_ALL_RULES): $(authz-obj-y)
|
||||
$(SOFTMMU_ALL_RULES): $(block-obj-y)
|
||||
$(SOFTMMU_ALL_RULES): $(storage-daemon-obj-y)
|
||||
$(SOFTMMU_ALL_RULES): $(chardev-obj-y)
|
||||
$(SOFTMMU_ALL_RULES): $(crypto-obj-y)
|
||||
$(SOFTMMU_ALL_RULES): $(io-obj-y)
|
||||
|
@ -487,6 +514,15 @@ ifdef DECOMPRESS_EDK2_BLOBS
|
|||
$(SOFTMMU_ALL_RULES): $(edk2-decompressed)
|
||||
endif
|
||||
|
||||
SOFTMMU_FUZZ_RULES=$(filter %-softmmu/fuzz, $(TARGET_DIRS_RULES))
|
||||
$(SOFTMMU_FUZZ_RULES): $(authz-obj-y)
|
||||
$(SOFTMMU_FUZZ_RULES): $(block-obj-y)
|
||||
$(SOFTMMU_FUZZ_RULES): $(chardev-obj-y)
|
||||
$(SOFTMMU_FUZZ_RULES): $(crypto-obj-y)
|
||||
$(SOFTMMU_FUZZ_RULES): $(io-obj-y)
|
||||
$(SOFTMMU_FUZZ_RULES): config-all-devices.mak
|
||||
$(SOFTMMU_FUZZ_RULES): $(edk2-decompressed)
|
||||
|
||||
.PHONY: $(TARGET_DIRS_RULES)
|
||||
# The $(TARGET_DIRS_RULES) are of the form SUBDIR/GOAL, so that
|
||||
# $(dir $@) yields the sub-directory, and $(notdir $@) yields the sub-goal
|
||||
|
@ -499,7 +535,7 @@ DTC_CPPFLAGS=-I$(BUILD_DIR)/dtc -I$(SRC_PATH)/dtc -I$(SRC_PATH)/dtc/libfdt
|
|||
|
||||
.PHONY: dtc/all
|
||||
dtc/all: .git-submodule-status dtc/libfdt dtc/tests
|
||||
$(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt/libfdt.a,)
|
||||
$(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(QEMU_LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt/libfdt.a,)
|
||||
|
||||
dtc/%: .git-submodule-status
|
||||
@mkdir -p $@
|
||||
|
@ -526,7 +562,7 @@ slirp/all: .git-submodule-status
|
|||
BUILD_DIR="$(BUILD_DIR)/slirp" \
|
||||
PKG_CONFIG="$(PKG_CONFIG)" \
|
||||
CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" \
|
||||
CFLAGS="$(QEMU_CFLAGS) $(CFLAGS)" LDFLAGS="$(LDFLAGS)")
|
||||
CFLAGS="$(QEMU_CFLAGS) $(CFLAGS)" LDFLAGS="$(QEMU_LDFLAGS)")
|
||||
|
||||
# Compatibility gunk to keep make working across the rename of targets
|
||||
# for recursion, to be removed some time after 4.1.
|
||||
|
@ -535,6 +571,9 @@ subdir-capstone: capstone/all
|
|||
subdir-slirp: slirp/all
|
||||
|
||||
$(filter %/all, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \
|
||||
$(qom-obj-y)
|
||||
|
||||
$(filter %/fuzz, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \
|
||||
$(qom-obj-y) $(crypto-user-obj-$(CONFIG_USER_ONLY))
|
||||
|
||||
ROM_DIRS = $(addprefix pc-bios/, $(ROMS))
|
||||
|
@ -546,6 +585,7 @@ $(ROM_DIRS_RULES):
|
|||
|
||||
.PHONY: recurse-all recurse-clean recurse-install
|
||||
recurse-all: $(addsuffix /all, $(TARGET_DIRS) $(ROM_DIRS))
|
||||
recurse-fuzz: $(addsuffix /fuzz, $(TARGET_DIRS) $(ROM_DIRS))
|
||||
recurse-clean: $(addsuffix /clean, $(TARGET_DIRS) $(ROM_DIRS))
|
||||
recurse-install: $(addsuffix /install, $(TARGET_DIRS))
|
||||
$(addsuffix /install, $(TARGET_DIRS)): all
|
||||
|
@ -570,6 +610,7 @@ qemu-img.o: qemu-img-cmds.h
|
|||
qemu-img$(EXESUF): qemu-img.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
|
||||
qemu-nbd$(EXESUF): qemu-nbd.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
|
||||
qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
|
||||
qemu-storage-daemon$(EXESUF): qemu-storage-daemon.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(chardev-obj-y) $(io-obj-y) $(qom-obj-y) $(storage-daemon-obj-y) $(COMMON_LDADDS)
|
||||
|
||||
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
|
||||
|
||||
|
@ -578,7 +619,6 @@ qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $(COMMON_LDADDS)
|
|||
qemu-edid$(EXESUF): qemu-edid.o hw/display/edid-generate.o $(COMMON_LDADDS)
|
||||
|
||||
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS)
|
||||
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
|
||||
|
||||
scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(authz-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
|
||||
ifdef CONFIG_MPATH
|
||||
|
@ -613,6 +653,7 @@ $(SRC_PATH)/scripts/qapi-gen.py
|
|||
qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \
|
||||
qga/qapi-generated/qga-qapi-visit.c qga/qapi-generated/qga-qapi-visit.h \
|
||||
qga/qapi-generated/qga-qapi-commands.h qga/qapi-generated/qga-qapi-commands.c \
|
||||
qga/qapi-generated/qga-qapi-init-commands.h qga/qapi-generated/qga-qapi-init-commands.c \
|
||||
qga/qapi-generated/qga-qapi-doc.texi: \
|
||||
qga/qapi-generated/qapi-gen-timestamp ;
|
||||
qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py)
|
||||
|
@ -631,7 +672,18 @@ qapi-gen-timestamp: $(qapi-modules) $(qapi-py)
|
|||
"GEN","$(@:%-timestamp=%)")
|
||||
@>$@
|
||||
|
||||
QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qapi-commands.h)
|
||||
qapi-modules-storage-daemon = \
|
||||
$(SRC_PATH)/storage-daemon/qapi/qapi-schema.json \
|
||||
$(QAPI_MODULES_STORAGE_DAEMON:%=$(SRC_PATH)/qapi/%.json)
|
||||
|
||||
$(GENERATED_STORAGE_DAEMON_QAPI_FILES): storage-daemon/qapi/qapi-gen-timestamp ;
|
||||
storage-daemon/qapi/qapi-gen-timestamp: $(qapi-modules-storage-daemon) $(qapi-py)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
|
||||
-o "storage-daemon/qapi" $<, \
|
||||
"GEN","$(@:%-timestamp=%)")
|
||||
@>$@
|
||||
|
||||
QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qapi-commands.h qga-qapi-init-commands.h)
|
||||
$(qga-obj-y): $(QGALIB_GEN)
|
||||
|
||||
qemu-ga$(EXESUF): $(qga-obj-y) $(COMMON_LDADDS)
|
||||
|
@ -677,6 +729,12 @@ rdmacm-mux$(EXESUF): LIBS += "-libumad"
|
|||
rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS)
|
||||
$(call LINK, $^)
|
||||
|
||||
# relies on Linux-specific syscalls
|
||||
ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy)
|
||||
virtiofsd$(EXESUF): $(virtiofsd-obj-y) libvhost-user.a $(COMMON_LDADDS)
|
||||
$(call LINK, $^)
|
||||
endif
|
||||
|
||||
vhost-user-gpu$(EXESUF): $(vhost-user-gpu-obj-y) $(libvhost-user-obj-y) libqemuutil.a libqemustub.a
|
||||
$(call LINK, $^)
|
||||
|
||||
|
@ -723,6 +781,7 @@ clean: recurse-clean
|
|||
rm -f trace/generated-tracers-dtrace.h*
|
||||
rm -f $(foreach f,$(generated-files-y),$(f) $(f)-timestamp)
|
||||
rm -f qapi-gen-timestamp
|
||||
rm -f storage-daemon/qapi/qapi-gen-timestamp
|
||||
rm -rf qga/qapi-generated
|
||||
rm -f config-all-devices.mak
|
||||
|
||||
|
@ -739,16 +798,12 @@ rm -f $(MANUAL_BUILDDIR)/$1/objects.inv $(MANUAL_BUILDDIR)/$1/searchindex.js $(M
|
|||
endef
|
||||
|
||||
distclean: clean
|
||||
rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi
|
||||
rm -f config-host.mak config-host.h* $(DOCS)
|
||||
rm -f tests/tcg/config-*.mak
|
||||
rm -f config-all-devices.mak config-all-disas.mak config.status
|
||||
rm -f $(SUBDIR_DEVICES_MAK)
|
||||
rm -f po/*.mo tests/qemu-iotests/common.env
|
||||
rm -f roms/seabios/config.mak roms/vgabios/config.mak
|
||||
rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps
|
||||
rm -f qemu-doc.fn qemu-doc.fns qemu-doc.info qemu-doc.ky qemu-doc.kys
|
||||
rm -f qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp
|
||||
rm -f qemu-doc.vr qemu-doc.txt
|
||||
rm -f qemu-plugins-ld.symbols qemu-plugins-ld64.symbols
|
||||
rm -f config.log
|
||||
rm -f linux-headers/asm
|
||||
|
@ -758,12 +813,13 @@ distclean: clean
|
|||
rm -f docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
|
||||
rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
|
||||
rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
|
||||
rm -f docs/qemu-block-drivers.7
|
||||
rm -f docs/qemu-cpu-models.7
|
||||
rm -rf .doctrees
|
||||
$(call clean-manual,devel)
|
||||
$(call clean-manual,interop)
|
||||
$(call clean-manual,specs)
|
||||
$(call clean-manual,system)
|
||||
$(call clean-manual,tools)
|
||||
$(call clean-manual,user)
|
||||
for d in $(TARGET_DIRS); do \
|
||||
rm -rf $$d || exit 1 ; \
|
||||
done
|
||||
|
@ -779,7 +835,7 @@ ifdef INSTALL_BLOBS
|
|||
BLOBS=bios.bin bios-256k.bin bios-microvm.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
||||
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
|
||||
vgabios-ramfb.bin vgabios-bochs-display.bin vgabios-ati.bin \
|
||||
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
|
||||
openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
|
||||
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
|
||||
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
|
||||
efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \
|
||||
|
@ -795,7 +851,7 @@ u-boot.e500 u-boot-sam460-20100605.bin \
|
|||
qemu_vga.ndrv \
|
||||
edk2-licenses.txt \
|
||||
hppa-firmware.img \
|
||||
opensbi-riscv32-virt-fw_jump.bin \
|
||||
opensbi-riscv32-sifive_u-fw_jump.bin opensbi-riscv32-virt-fw_jump.bin \
|
||||
opensbi-riscv64-sifive_u-fw_jump.bin opensbi-riscv64-virt-fw_jump.bin
|
||||
|
||||
|
||||
|
@ -820,27 +876,29 @@ endef
|
|||
install-sphinxdocs: sphinxdocs
|
||||
$(call install-manual,interop)
|
||||
$(call install-manual,specs)
|
||||
$(call install-manual,system)
|
||||
$(call install-manual,tools)
|
||||
$(call install-manual,user)
|
||||
|
||||
install-doc: $(DOCS) install-sphinxdocs
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/index.html "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.txt "$(DESTDIR)$(qemu_docdir)"
|
||||
ifdef CONFIG_POSIX
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/system/qemu.1 "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7"
|
||||
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
|
||||
$(INSTALL_DATA) docs/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7"
|
||||
$(INSTALL_DATA) docs/qemu-cpu-models.7 "$(DESTDIR)$(mandir)/man7"
|
||||
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/system/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7"
|
||||
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/system/qemu-cpu-models.7 "$(DESTDIR)$(mandir)/man7"
|
||||
ifeq ($(CONFIG_TOOLS),y)
|
||||
$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/qemu-img.1 "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
|
||||
$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8"
|
||||
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/qemu-nbd.8 "$(DESTDIR)$(mandir)/man8"
|
||||
endif
|
||||
ifdef CONFIG_TRACE_SYSTEMTAP
|
||||
$(INSTALL_DATA) scripts/qemu-trace-stap.1 "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/qemu-trace-stap.1 "$(DESTDIR)$(mandir)/man1"
|
||||
endif
|
||||
ifneq (,$(findstring qemu-ga,$(TOOLS)))
|
||||
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/interop/qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
|
||||
|
@ -851,7 +909,10 @@ endif
|
|||
endif
|
||||
ifdef CONFIG_VIRTFS
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DATA) fsdev/virtfs-proxy-helper.1 "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/virtfs-proxy-helper.1 "$(DESTDIR)$(mandir)/man1"
|
||||
endif
|
||||
ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy)
|
||||
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/virtiofsd.1 "$(DESTDIR)$(mandir)/man1"
|
||||
endif
|
||||
|
||||
install-datadir:
|
||||
|
@ -1006,16 +1067,32 @@ docs/version.texi: $(SRC_PATH)/VERSION config-host.mak
|
|||
# and handles "don't rebuild things unless necessary" itself.
|
||||
# The '.doctrees' files are cached information to speed this up.
|
||||
.PHONY: sphinxdocs
|
||||
sphinxdocs: $(MANUAL_BUILDDIR)/devel/index.html $(MANUAL_BUILDDIR)/interop/index.html $(MANUAL_BUILDDIR)/specs/index.html
|
||||
sphinxdocs: $(MANUAL_BUILDDIR)/devel/index.html \
|
||||
$(MANUAL_BUILDDIR)/interop/index.html \
|
||||
$(MANUAL_BUILDDIR)/specs/index.html \
|
||||
$(MANUAL_BUILDDIR)/system/index.html \
|
||||
$(MANUAL_BUILDDIR)/tools/index.html \
|
||||
$(MANUAL_BUILDDIR)/user/index.html
|
||||
|
||||
# Canned command to build a single manual
|
||||
# Arguments: $1 = manual name, $2 = Sphinx builder ('html' or 'man')
|
||||
# Note the use of different doctree for each (manual, builder) tuple;
|
||||
# this works around Sphinx not handling parallel invocation on
|
||||
# a single doctree: https://github.com/sphinx-doc/sphinx/issues/2946
|
||||
build-manual = $(call quiet-command,CONFDIR="$(qemu_confdir)" sphinx-build $(if $(V),,-q) -W -n -b $2 -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1-$2 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1")
|
||||
build-manual = $(call quiet-command,CONFDIR="$(qemu_confdir)" $(SPHINX_BUILD) $(if $(V),,-q) $(SPHINX_WERROR) -b $2 -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1-$2 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1")
|
||||
# We assume all RST files in the manual's directory are used in it
|
||||
manual-deps = $(wildcard $(SRC_PATH)/docs/$1/*.rst) $(SRC_PATH)/docs/$1/conf.py $(SRC_PATH)/docs/conf.py
|
||||
manual-deps = $(wildcard $(SRC_PATH)/docs/$1/*.rst $(SRC_PATH)/docs/$1/*/*.rst) \
|
||||
$(SRC_PATH)/docs/defs.rst.inc \
|
||||
$(SRC_PATH)/docs/$1/conf.py $(SRC_PATH)/docs/conf.py \
|
||||
$(SRC_PATH)/docs/sphinx/*.py
|
||||
# Macro to write out the rule and dependencies for building manpages
|
||||
# Usage: $(call define-manpage-rule,manualname,manpage1 manpage2...[,extradeps])
|
||||
# 'extradeps' is optional, and specifies extra files (eg .hx files) that
|
||||
# the manual page depends on.
|
||||
define define-manpage-rule
|
||||
$(call atomic,$(foreach manpage,$2,$(MANUAL_BUILDDIR)/$1/$(manpage)),$(call manual-deps,$1) $3)
|
||||
$(call build-manual,$1,man)
|
||||
endef
|
||||
|
||||
$(MANUAL_BUILDDIR)/devel/index.html: $(call manual-deps,devel)
|
||||
$(call build-manual,devel,html)
|
||||
|
@ -1026,20 +1103,28 @@ $(MANUAL_BUILDDIR)/interop/index.html: $(call manual-deps,interop)
|
|||
$(MANUAL_BUILDDIR)/specs/index.html: $(call manual-deps,specs)
|
||||
$(call build-manual,specs,html)
|
||||
|
||||
$(MANUAL_BUILDDIR)/interop/qemu-ga.8: $(call manual-deps,interop)
|
||||
$(call build-manual,interop,man)
|
||||
$(MANUAL_BUILDDIR)/system/index.html: $(call manual-deps,system) $(SRC_PATH)/hmp-commands.hx $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/qemu-options.hx
|
||||
$(call build-manual,system,html)
|
||||
|
||||
qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
|
||||
$(MANUAL_BUILDDIR)/tools/index.html: $(call manual-deps,tools) $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/docs/qemu-option-trace.rst.inc
|
||||
$(call build-manual,tools,html)
|
||||
|
||||
qemu-monitor.texi: $(SRC_PATH)/hmp-commands.hx $(SRC_PATH)/scripts/hxtool
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
|
||||
$(MANUAL_BUILDDIR)/user/index.html: $(call manual-deps,user)
|
||||
$(call build-manual,user,html)
|
||||
|
||||
qemu-monitor-info.texi: $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxtool
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
|
||||
$(call define-manpage-rule,interop,qemu-ga.8)
|
||||
|
||||
qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
|
||||
$(call define-manpage-rule,system,qemu.1 qemu-block-drivers.7 qemu-cpu-models.7)
|
||||
|
||||
$(call define-manpage-rule,tools,\
|
||||
qemu-img.1 qemu-nbd.8 qemu-trace-stap.1\
|
||||
virtiofsd.1 virtfs-proxy-helper.1,\
|
||||
$(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/docs/qemu-option-trace.rst.inc)
|
||||
|
||||
$(MANUAL_BUILDDIR)/index.html: $(SRC_PATH)/docs/index.html.in qemu-version.h
|
||||
@mkdir -p "$(MANUAL_BUILDDIR)"
|
||||
$(call quiet-command, sed "s|@@VERSION@@|${VERSION}|g" $< >$@, \
|
||||
"GEN","$@")
|
||||
|
||||
docs/interop/qemu-qmp-qapi.texi: qapi/qapi-doc.texi
|
||||
@cp -p $< $@
|
||||
|
@ -1047,26 +1132,10 @@ docs/interop/qemu-qmp-qapi.texi: qapi/qapi-doc.texi
|
|||
docs/interop/qemu-ga-qapi.texi: qga/qapi-generated/qga-qapi-doc.texi
|
||||
@cp -p $< $@
|
||||
|
||||
qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi qemu-monitor-info.texi
|
||||
qemu.1: qemu-option-trace.texi
|
||||
qemu-img.1: qemu-img.texi qemu-option-trace.texi qemu-img-cmds.texi
|
||||
fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi
|
||||
qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi
|
||||
docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi
|
||||
docs/qemu-cpu-models.7: docs/qemu-cpu-models.texi
|
||||
scripts/qemu-trace-stap.1: scripts/qemu-trace-stap.texi
|
||||
|
||||
html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html sphinxdocs
|
||||
info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info
|
||||
pdf: qemu-doc.pdf docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
|
||||
txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
|
||||
|
||||
qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \
|
||||
qemu-img.texi qemu-nbd.texi qemu-options.texi \
|
||||
qemu-tech.texi qemu-option-trace.texi \
|
||||
qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi \
|
||||
qemu-monitor-info.texi docs/qemu-block-drivers.texi \
|
||||
docs/qemu-cpu-models.texi docs/security.texi
|
||||
html: docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html sphinxdocs
|
||||
info: docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info
|
||||
pdf: docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
|
||||
txt: docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
|
||||
|
||||
docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \
|
||||
docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \
|
||||
|
@ -1169,50 +1238,57 @@ endif
|
|||
include $(SRC_PATH)/tests/docker/Makefile.include
|
||||
include $(SRC_PATH)/tests/vm/Makefile.include
|
||||
|
||||
print-help-run = printf " %-30s - %s\\n" "$1" "$2"
|
||||
print-help = $(quiet-@)$(call print-help-run,$1,$2)
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo 'Generic targets:'
|
||||
@echo ' all - Build all'
|
||||
$(call print-help,all,Build all)
|
||||
ifdef CONFIG_MODULES
|
||||
@echo ' modules - Build all modules'
|
||||
$(call print-help,modules,Build all modules)
|
||||
endif
|
||||
@echo ' dir/file.o - Build specified target only'
|
||||
@echo ' install - Install QEMU, documentation and tools'
|
||||
@echo ' ctags/TAGS - Generate tags file for editors'
|
||||
@echo ' cscope - Generate cscope index'
|
||||
$(call print-help,dir/file.o,Build specified target only)
|
||||
$(call print-help,install,Install QEMU, documentation and tools)
|
||||
$(call print-help,ctags/TAGS,Generate tags file for editors)
|
||||
$(call print-help,cscope,Generate cscope index)
|
||||
@echo ''
|
||||
@$(if $(TARGET_DIRS), \
|
||||
echo 'Architecture specific targets:'; \
|
||||
$(foreach t, $(TARGET_DIRS), \
|
||||
printf " %-30s - Build for %s\\n" $(t)/all $(t);) \
|
||||
$(call print-help-run,$(t)/all,Build for $(t));) \
|
||||
echo '')
|
||||
@$(if $(TOOLS), \
|
||||
echo 'Tools targets:'; \
|
||||
$(foreach t, $(TOOLS), \
|
||||
$(call print-help-run,$(t),Build $(shell basename $(t)) tool);) \
|
||||
echo '')
|
||||
@echo 'Cleaning targets:'
|
||||
@echo ' clean - Remove most generated files but keep the config'
|
||||
$(call print-help,clean,Remove most generated files but keep the config)
|
||||
ifdef CONFIG_GCOV
|
||||
@echo ' clean-coverage - Remove coverage files'
|
||||
$(call print-help,clean-coverage,Remove coverage files)
|
||||
endif
|
||||
@echo ' distclean - Remove all generated files'
|
||||
@echo ' dist - Build a distributable tarball'
|
||||
$(call print-help,distclean,Remove all generated files)
|
||||
$(call print-help,dist,Build a distributable tarball)
|
||||
@echo ''
|
||||
@echo 'Test targets:'
|
||||
@echo ' check - Run all tests (check-help for details)'
|
||||
@echo ' docker - Help about targets running tests inside containers'
|
||||
@echo ' vm-help - Help about targets running tests inside VM'
|
||||
$(call print-help,check,Run all tests (check-help for details))
|
||||
$(call print-help,docker,Help about targets running tests inside containers)
|
||||
$(call print-help,vm-help,Help about targets running tests inside VM)
|
||||
@echo ''
|
||||
@echo 'Documentation targets:'
|
||||
@echo ' html info pdf txt'
|
||||
@echo ' - Build documentation in specified format'
|
||||
$(call print-help,html info pdf txt,Build documentation in specified format)
|
||||
ifdef CONFIG_GCOV
|
||||
@echo ' coverage-report - Create code coverage report'
|
||||
$(call print-help,coverage-report,Create code coverage report)
|
||||
endif
|
||||
@echo ''
|
||||
ifdef CONFIG_WIN32
|
||||
@echo 'Windows targets:'
|
||||
@echo ' installer - Build NSIS-based installer for QEMU'
|
||||
$(call print-help,installer,Build NSIS-based installer for QEMU)
|
||||
ifdef QEMU_GA_MSI_ENABLED
|
||||
@echo ' msi - Build MSI-based installer for qemu-ga'
|
||||
$(call print-help,msi,Build MSI-based installer for qemu-ga)
|
||||
endif
|
||||
@echo ''
|
||||
endif
|
||||
@echo ' $(MAKE) [targets] (quiet build, default)'
|
||||
@echo ' $(MAKE) V=1 [targets] (verbose build)'
|
||||
$(call print-help,$(MAKE) [targets],(quiet build, default))
|
||||
$(call print-help,$(MAKE) V=1 [targets],(verbose build))
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
#######################################################################
|
||||
# Common libraries for tools and emulators
|
||||
stub-obj-y = stubs/ util/ crypto/
|
||||
util-obj-y = util/ qobject/ qapi/
|
||||
stub-obj-y = stubs/
|
||||
util-obj-y = crypto/ util/ qobject/ qapi/
|
||||
qom-obj-y = qom/
|
||||
|
||||
#######################################################################
|
||||
# code used by both qemu system emulation and qemu-img
|
||||
|
||||
ifeq ($(call lor,$(CONFIG_SOFTMMU),$(CONFIG_TOOLS)),y)
|
||||
|
||||
chardev-obj-y = chardev/
|
||||
|
||||
#######################################################################
|
||||
# authz-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
authz-obj-y = authz/
|
||||
|
||||
#######################################################################
|
||||
# block-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
block-obj-y = nbd/
|
||||
block-obj-y += block.o blockjob.o job.o
|
||||
block-obj-y += block/ scsi/
|
||||
|
@ -21,22 +21,21 @@ block-obj-$(CONFIG_REPLICATION) += replication.o
|
|||
|
||||
block-obj-m = block/
|
||||
|
||||
#######################################################################
|
||||
# crypto-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
crypto-obj-y = crypto/
|
||||
crypto-user-obj-y = crypto/
|
||||
|
||||
#######################################################################
|
||||
# qom-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
qom-obj-y = qom/
|
||||
|
||||
#######################################################################
|
||||
# io-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
io-obj-y = io/
|
||||
|
||||
endif # CONFIG_SOFTMMU or CONFIG_TOOLS
|
||||
|
||||
#######################################################################
|
||||
# storage-daemon-obj-y is code used by qemu-storage-daemon (these objects are
|
||||
# used for system emulation, too, but specified separately there)
|
||||
|
||||
storage-daemon-obj-y = block/ monitor/ qapi/ qom/ storage-daemon/
|
||||
storage-daemon-obj-y += blockdev.o blockdev-nbd.o iothread.o job-qmp.o
|
||||
storage-daemon-obj-$(CONFIG_WIN32) += os-win32.o
|
||||
storage-daemon-obj-$(CONFIG_POSIX) += os-posix.o
|
||||
|
||||
######################################################################
|
||||
# Target independent part of system emulation. The long term path is to
|
||||
# suppress *all* target specific code in case of system emulation, i.e. a
|
||||
|
@ -49,12 +48,13 @@ common-obj-y += dump/
|
|||
common-obj-y += job-qmp.o
|
||||
common-obj-y += monitor/
|
||||
common-obj-y += net/
|
||||
common-obj-y += qdev-monitor.o device-hotplug.o
|
||||
common-obj-y += qdev-monitor.o
|
||||
common-obj-$(CONFIG_WIN32) += os-win32.o
|
||||
common-obj-$(CONFIG_POSIX) += os-posix.o
|
||||
|
||||
common-obj-$(CONFIG_LINUX) += fsdev/
|
||||
|
||||
common-obj-y += accel/
|
||||
common-obj-y += migration/
|
||||
|
||||
common-obj-y += audio/
|
||||
|
@ -65,12 +65,8 @@ common-obj-y += replay/
|
|||
|
||||
common-obj-y += ui/
|
||||
common-obj-m += ui/
|
||||
common-obj-y += bt-host.o bt-vhci.o
|
||||
bt-host.o-cflags := $(BLUEZ_CFLAGS)
|
||||
|
||||
common-obj-y += dma-helpers.o
|
||||
common-obj-y += vl.o
|
||||
vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS)
|
||||
common-obj-$(CONFIG_TPM) += tpm.o
|
||||
|
||||
common-obj-y += backends/
|
||||
|
@ -82,11 +78,9 @@ qemu-seccomp.o-libs := $(SECCOMP_LIBS)
|
|||
|
||||
common-obj-$(CONFIG_FDT) += device_tree.o
|
||||
|
||||
######################################################################
|
||||
# qapi
|
||||
|
||||
common-obj-y += qapi/
|
||||
endif
|
||||
|
||||
endif # CONFIG_SOFTMMU
|
||||
|
||||
#######################################################################
|
||||
# Target-independent parts used in system and user emulation
|
||||
|
@ -125,11 +119,13 @@ 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/
|
||||
virtiofsd-obj-y = tools/virtiofsd/
|
||||
|
||||
######################################################################
|
||||
trace-events-subdirs =
|
||||
trace-events-subdirs += accel/kvm
|
||||
trace-events-subdirs += accel/tcg
|
||||
trace-events-subdirs += backends
|
||||
trace-events-subdirs += crypto
|
||||
trace-events-subdirs += monitor
|
||||
ifeq ($(CONFIG_USER_ONLY),y)
|
||||
|
@ -143,8 +139,8 @@ trace-events-subdirs += nbd
|
|||
trace-events-subdirs += scsi
|
||||
endif
|
||||
ifeq ($(CONFIG_SOFTMMU),y)
|
||||
trace-events-subdirs += chardev
|
||||
trace-events-subdirs += audio
|
||||
trace-events-subdirs += chardev
|
||||
trace-events-subdirs += hw/9pfs
|
||||
trace-events-subdirs += hw/acpi
|
||||
trace-events-subdirs += hw/alpha
|
||||
|
@ -179,6 +175,7 @@ trace-events-subdirs += hw/scsi
|
|||
trace-events-subdirs += hw/sd
|
||||
trace-events-subdirs += hw/sparc
|
||||
trace-events-subdirs += hw/sparc64
|
||||
trace-events-subdirs += hw/ssi
|
||||
trace-events-subdirs += hw/timer
|
||||
trace-events-subdirs += hw/tpm
|
||||
trace-events-subdirs += hw/usb
|
||||
|
@ -192,6 +189,7 @@ trace-events-subdirs += migration
|
|||
trace-events-subdirs += net
|
||||
trace-events-subdirs += ui
|
||||
endif
|
||||
trace-events-subdirs += hw/core
|
||||
trace-events-subdirs += hw/display
|
||||
trace-events-subdirs += qapi
|
||||
trace-events-subdirs += qom
|
||||
|
@ -204,7 +202,6 @@ trace-events-subdirs += target/riscv
|
|||
trace-events-subdirs += target/s390x
|
||||
trace-events-subdirs += target/sparc
|
||||
trace-events-subdirs += util
|
||||
trace-events-subdirs += hw/core
|
||||
|
||||
trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events)
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ endif
|
|||
|
||||
$(call set-vpath, $(SRC_PATH):$(BUILD_DIR))
|
||||
ifdef CONFIG_LINUX
|
||||
QEMU_CFLAGS += -I../linux-headers
|
||||
QEMU_CFLAGS += -isystem ../linux-headers
|
||||
endif
|
||||
QEMU_CFLAGS += -iquote .. -iquote $(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H
|
||||
|
||||
|
@ -128,7 +128,8 @@ ifdef CONFIG_LINUX_USER
|
|||
|
||||
QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) \
|
||||
-I$(SRC_PATH)/linux-user/host/$(ARCH) \
|
||||
-I$(SRC_PATH)/linux-user
|
||||
-I$(SRC_PATH)/linux-user \
|
||||
-Ilinux-user/$(TARGET_ABI_DIR)
|
||||
|
||||
obj-y += linux-user/
|
||||
obj-y += gdbstub.o thunk.o
|
||||
|
@ -160,6 +161,7 @@ obj-y += qapi/
|
|||
obj-y += memory.o
|
||||
obj-y += memory_mapping.o
|
||||
obj-y += migration/ram.o
|
||||
obj-y += softmmu/
|
||||
LIBS := $(libs_softmmu) $(LIBS)
|
||||
|
||||
# Hardware support
|
||||
|
@ -184,7 +186,6 @@ dummy := $(call unnest-vars,.., \
|
|||
block-obj-m \
|
||||
chardev-obj-y \
|
||||
crypto-obj-y \
|
||||
crypto-user-obj-y \
|
||||
qom-obj-y \
|
||||
io-obj-y \
|
||||
common-obj-y \
|
||||
|
@ -193,7 +194,6 @@ all-obj-y += $(common-obj-y)
|
|||
all-obj-y += $(qom-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(authz-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y)
|
||||
all-obj-$(CONFIG_USER_ONLY) += $(crypto-user-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
|
||||
|
||||
|
@ -204,7 +204,7 @@ endif
|
|||
COMMON_LDADDS = ../libqemuutil.a
|
||||
|
||||
# build either PROG or PROGW
|
||||
$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS)
|
||||
$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS) $(softmmu-main-y)
|
||||
$(call LINK, $(filter-out %.mak, $^))
|
||||
ifdef CONFIG_DARWIN
|
||||
$(call quiet-command,Rez -append $(SRC_PATH)/pc-bios/qemu.rsrc -o $@,"REZ","$(TARGET_DIR)$@")
|
||||
|
@ -229,6 +229,22 @@ ifdef CONFIG_TRACE_SYSTEMTAP
|
|||
rm -f *.stp
|
||||
endif
|
||||
|
||||
ifdef CONFIG_FUZZ
|
||||
include $(SRC_PATH)/tests/qtest/fuzz/Makefile.include
|
||||
include $(SRC_PATH)/tests/qtest/Makefile.include
|
||||
|
||||
fuzz: fuzz-vars
|
||||
fuzz-vars: QEMU_CFLAGS := $(FUZZ_CFLAGS) $(QEMU_CFLAGS)
|
||||
fuzz-vars: QEMU_LDFLAGS := $(FUZZ_LDFLAGS) $(QEMU_LDFLAGS)
|
||||
fuzz-vars: $(QEMU_PROG_FUZZ)
|
||||
dummy := $(call unnest-vars,, fuzz-obj-y)
|
||||
|
||||
|
||||
$(QEMU_PROG_FUZZ): config-devices.mak $(all-obj-y) $(COMMON_LDADDS) $(fuzz-obj-y)
|
||||
$(call LINK, $(filter-out %.mak, $^))
|
||||
|
||||
endif
|
||||
|
||||
install: all
|
||||
ifneq ($(PROGS),)
|
||||
$(call install-prog,$(PROGS),$(DESTDIR)$(bindir))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
obj-$(CONFIG_SOFTMMU) += accel.o
|
||||
common-obj-$(CONFIG_SOFTMMU) += accel.o
|
||||
obj-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_POSIX)) += qtest.o
|
||||
obj-$(CONFIG_KVM) += kvm/
|
||||
obj-$(CONFIG_TCG) += tcg/
|
||||
|
|
|
@ -28,13 +28,7 @@
|
|||
#include "hw/boards.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "hw/xen/xen.h"
|
||||
#include "qom/object.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
static const TypeInfo accel_type = {
|
||||
.name = TYPE_ACCEL,
|
||||
|
@ -44,7 +38,7 @@ static const TypeInfo accel_type = {
|
|||
};
|
||||
|
||||
/* Lookup AccelClass from opt_name. Returns NULL if not found */
|
||||
static AccelClass *accel_find(const char *opt_name)
|
||||
AccelClass *accel_find(const char *opt_name)
|
||||
{
|
||||
char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name);
|
||||
AccelClass *ac = ACCEL_CLASS(object_class_by_name(class_name));
|
||||
|
@ -52,11 +46,9 @@ static AccelClass *accel_find(const char *opt_name)
|
|||
return ac;
|
||||
}
|
||||
|
||||
static int accel_init_machine(AccelClass *acc, MachineState *ms)
|
||||
int accel_init_machine(AccelState *accel, MachineState *ms)
|
||||
{
|
||||
ObjectClass *oc = OBJECT_CLASS(acc);
|
||||
const char *cname = object_class_get_name(oc);
|
||||
AccelState *accel = ACCEL(object_new(cname));
|
||||
AccelClass *acc = ACCEL_GET_CLASS(accel);
|
||||
int ret;
|
||||
ms->accelerator = accel;
|
||||
*(acc->allowed) = true;
|
||||
|
@ -71,63 +63,9 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void configure_accelerator(MachineState *ms, const char *progname)
|
||||
AccelState *current_accel(void)
|
||||
{
|
||||
const char *accel;
|
||||
char **accel_list, **tmp;
|
||||
int ret;
|
||||
bool accel_initialised = false;
|
||||
bool init_failed = false;
|
||||
AccelClass *acc = NULL;
|
||||
|
||||
accel = qemu_opt_get(qemu_get_machine_opts(), "accel");
|
||||
if (accel == NULL) {
|
||||
/* Select the default accelerator */
|
||||
int pnlen = strlen(progname);
|
||||
if (pnlen >= 3 && g_str_equal(&progname[pnlen - 3], "kvm")) {
|
||||
/* If the program name ends with "kvm", we prefer KVM */
|
||||
accel = "kvm:tcg";
|
||||
} else {
|
||||
#if defined(CONFIG_TCG)
|
||||
accel = "tcg";
|
||||
#elif defined(CONFIG_KVM)
|
||||
accel = "kvm";
|
||||
#else
|
||||
error_report("No accelerator selected and"
|
||||
" no default accelerator available");
|
||||
exit(1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
accel_list = g_strsplit(accel, ":", 0);
|
||||
|
||||
for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) {
|
||||
acc = accel_find(*tmp);
|
||||
if (!acc) {
|
||||
continue;
|
||||
}
|
||||
ret = accel_init_machine(acc, ms);
|
||||
if (ret < 0) {
|
||||
init_failed = true;
|
||||
error_report("failed to initialize %s: %s",
|
||||
acc->name, strerror(-ret));
|
||||
} else {
|
||||
accel_initialised = true;
|
||||
}
|
||||
}
|
||||
g_strfreev(accel_list);
|
||||
|
||||
if (!accel_initialised) {
|
||||
if (!init_failed) {
|
||||
error_report("-machine accel=%s: No accelerator found", accel);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (init_failed) {
|
||||
error_report("Back to %s accelerator", acc->name);
|
||||
}
|
||||
return current_machine->accelerator;
|
||||
}
|
||||
|
||||
void accel_setup_post(MachineState *ms)
|
||||
|
|
|
@ -41,6 +41,9 @@
|
|||
#include "hw/irq.h"
|
||||
#include "sysemu/sev.h"
|
||||
#include "sysemu/balloon.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qapi/qapi-types-common.h"
|
||||
#include "qapi/qapi-visit-common.h"
|
||||
|
||||
#include "hw/boards.h"
|
||||
|
||||
|
@ -92,6 +95,10 @@ struct KVMState
|
|||
int max_nested_state_len;
|
||||
int many_ioeventfds;
|
||||
int intx_set_mask;
|
||||
int kvm_shadow_mem;
|
||||
bool kernel_irqchip_allowed;
|
||||
bool kernel_irqchip_required;
|
||||
OnOffAuto kernel_irqchip_split;
|
||||
bool sync_mmu;
|
||||
bool manual_dirty_log_protect;
|
||||
/* The man page (and posix) say ioctl numbers are signed int, but
|
||||
|
@ -157,7 +164,7 @@ static NotifierList kvm_irqchip_change_notifiers =
|
|||
|
||||
int kvm_get_max_memslots(void)
|
||||
{
|
||||
KVMState *s = KVM_STATE(current_machine->accelerator);
|
||||
KVMState *s = KVM_STATE(current_accel());
|
||||
|
||||
return s->nr_slots;
|
||||
}
|
||||
|
@ -301,13 +308,23 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo
|
|||
/* Set the slot size to 0 before setting the slot to the desired
|
||||
* value. This is needed based on KVM commit 75d61fbc. */
|
||||
mem.memory_size = 0;
|
||||
kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem);
|
||||
ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
mem.memory_size = slot->memory_size;
|
||||
ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem);
|
||||
slot->old_flags = mem.flags;
|
||||
err:
|
||||
trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr,
|
||||
mem.memory_size, mem.userspace_addr, ret);
|
||||
if (ret < 0) {
|
||||
error_report("%s: KVM_SET_USER_MEMORY_REGION failed, slot=%d,"
|
||||
" start=0x%" PRIx64 ", size=0x%" PRIx64 ": %s",
|
||||
__func__, mem.slot, slot->start_addr,
|
||||
(uint64_t)mem.memory_size, strerror(errno));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -518,6 +535,27 @@ static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section,
|
|||
|
||||
#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1))
|
||||
|
||||
/* Allocate the dirty bitmap for a slot */
|
||||
static void kvm_memslot_init_dirty_bitmap(KVMSlot *mem)
|
||||
{
|
||||
/*
|
||||
* XXX bad kernel interface alert
|
||||
* For dirty bitmap, kernel allocates array of size aligned to
|
||||
* bits-per-long. But for case when the kernel is 64bits and
|
||||
* the userspace is 32bits, userspace can't align to the same
|
||||
* bits-per-long, since sizeof(long) is different between kernel
|
||||
* and user space. This way, userspace will provide buffer which
|
||||
* may be 4 bytes less than the kernel will use, resulting in
|
||||
* userspace memory corruption (which is not detectable by valgrind
|
||||
* too, in most cases).
|
||||
* So for now, let's align to 64 instead of HOST_LONG_BITS here, in
|
||||
* a hope that sizeof(long) won't become >8 any time soon.
|
||||
*/
|
||||
hwaddr bitmap_size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS),
|
||||
/*HOST_LONG_BITS*/ 64) / 8;
|
||||
mem->dirty_bmap = g_malloc0(bitmap_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_physical_sync_dirty_bitmap - Sync dirty bitmap from kernel space
|
||||
*
|
||||
|
@ -550,23 +588,9 @@ static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
|
|||
goto out;
|
||||
}
|
||||
|
||||
/* XXX bad kernel interface alert
|
||||
* For dirty bitmap, kernel allocates array of size aligned to
|
||||
* bits-per-long. But for case when the kernel is 64bits and
|
||||
* the userspace is 32bits, userspace can't align to the same
|
||||
* bits-per-long, since sizeof(long) is different between kernel
|
||||
* and user space. This way, userspace will provide buffer which
|
||||
* may be 4 bytes less than the kernel will use, resulting in
|
||||
* userspace memory corruption (which is not detectable by valgrind
|
||||
* too, in most cases).
|
||||
* So for now, let's align to 64 instead of HOST_LONG_BITS here, in
|
||||
* a hope that sizeof(long) won't become >8 any time soon.
|
||||
*/
|
||||
if (!mem->dirty_bmap) {
|
||||
hwaddr bitmap_size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS),
|
||||
/*HOST_LONG_BITS*/ 64) / 8;
|
||||
/* Allocate on the first log_sync, once and for all */
|
||||
mem->dirty_bmap = g_malloc0(bitmap_size);
|
||||
kvm_memslot_init_dirty_bitmap(mem);
|
||||
}
|
||||
|
||||
d.dirty_bitmap = mem->dirty_bmap;
|
||||
|
@ -1067,6 +1091,13 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
|||
mem->ram = ram;
|
||||
mem->flags = kvm_mem_flags(mr);
|
||||
|
||||
if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
|
||||
/*
|
||||
* Reallocate the bmap; it means it doesn't disappear in
|
||||
* middle of a migrate.
|
||||
*/
|
||||
kvm_memslot_init_dirty_bitmap(mem);
|
||||
}
|
||||
err = kvm_set_user_memory_region(kml, mem, true);
|
||||
if (err) {
|
||||
fprintf(stderr, "%s: error registering slot: %s\n", __func__,
|
||||
|
@ -1758,10 +1789,11 @@ void kvm_irqchip_set_qemuirq_gsi(KVMState *s, qemu_irq irq, int gsi)
|
|||
g_hash_table_insert(s->gsimap, irq, GINT_TO_POINTER(gsi));
|
||||
}
|
||||
|
||||
static void kvm_irqchip_create(MachineState *machine, KVMState *s)
|
||||
static void kvm_irqchip_create(KVMState *s)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(s->kernel_irqchip_split != ON_OFF_AUTO_AUTO);
|
||||
if (kvm_check_extension(s, KVM_CAP_IRQCHIP)) {
|
||||
;
|
||||
} else if (kvm_check_extension(s, KVM_CAP_S390_IRQCHIP)) {
|
||||
|
@ -1776,9 +1808,9 @@ static void kvm_irqchip_create(MachineState *machine, KVMState *s)
|
|||
|
||||
/* First probe and see if there's a arch-specific hook to create the
|
||||
* in-kernel irqchip for us */
|
||||
ret = kvm_arch_irqchip_create(machine, s);
|
||||
ret = kvm_arch_irqchip_create(s);
|
||||
if (ret == 0) {
|
||||
if (machine_kernel_irqchip_split(machine)) {
|
||||
if (s->kernel_irqchip_split == ON_OFF_AUTO_ON) {
|
||||
perror("Split IRQ chip mode not supported.");
|
||||
exit(1);
|
||||
} else {
|
||||
|
@ -1826,7 +1858,7 @@ static int kvm_max_vcpu_id(KVMState *s)
|
|||
|
||||
bool kvm_vcpu_id_is_valid(int vcpu_id)
|
||||
{
|
||||
KVMState *s = KVM_STATE(current_machine->accelerator);
|
||||
KVMState *s = KVM_STATE(current_accel());
|
||||
return vcpu_id >= 0 && vcpu_id < kvm_max_vcpu_id(s);
|
||||
}
|
||||
|
||||
|
@ -2049,8 +2081,12 @@ static int kvm_init(MachineState *ms)
|
|||
goto err;
|
||||
}
|
||||
|
||||
if (machine_kernel_irqchip_allowed(ms)) {
|
||||
kvm_irqchip_create(ms, s);
|
||||
if (s->kernel_irqchip_split == ON_OFF_AUTO_AUTO) {
|
||||
s->kernel_irqchip_split = mc->default_kernel_irqchip_split ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
|
||||
}
|
||||
|
||||
if (s->kernel_irqchip_allowed) {
|
||||
kvm_irqchip_create(s);
|
||||
}
|
||||
|
||||
if (kvm_eventfds_allowed) {
|
||||
|
@ -2152,9 +2188,9 @@ void kvm_flush_coalesced_mmio_buffer(void)
|
|||
ent = &ring->coalesced_mmio[ring->first];
|
||||
|
||||
if (ent->pio == 1) {
|
||||
address_space_rw(&address_space_io, ent->phys_addr,
|
||||
MEMTXATTRS_UNSPECIFIED, ent->data,
|
||||
ent->len, true);
|
||||
address_space_write(&address_space_io, ent->phys_addr,
|
||||
MEMTXATTRS_UNSPECIFIED, ent->data,
|
||||
ent->len);
|
||||
} else {
|
||||
cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len);
|
||||
}
|
||||
|
@ -2940,6 +2976,95 @@ static bool kvm_accel_has_memory(MachineState *ms, AddressSpace *as,
|
|||
return false;
|
||||
}
|
||||
|
||||
static void kvm_get_kvm_shadow_mem(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
KVMState *s = KVM_STATE(obj);
|
||||
int64_t value = s->kvm_shadow_mem;
|
||||
|
||||
visit_type_int(v, name, &value, errp);
|
||||
}
|
||||
|
||||
static void kvm_set_kvm_shadow_mem(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
KVMState *s = KVM_STATE(obj);
|
||||
Error *error = NULL;
|
||||
int64_t value;
|
||||
|
||||
visit_type_int(v, name, &value, &error);
|
||||
if (error) {
|
||||
error_propagate(errp, error);
|
||||
return;
|
||||
}
|
||||
|
||||
s->kvm_shadow_mem = value;
|
||||
}
|
||||
|
||||
static void kvm_set_kernel_irqchip(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
Error *err = NULL;
|
||||
KVMState *s = KVM_STATE(obj);
|
||||
OnOffSplit mode;
|
||||
|
||||
visit_type_OnOffSplit(v, name, &mode, &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
} else {
|
||||
switch (mode) {
|
||||
case ON_OFF_SPLIT_ON:
|
||||
s->kernel_irqchip_allowed = true;
|
||||
s->kernel_irqchip_required = true;
|
||||
s->kernel_irqchip_split = ON_OFF_AUTO_OFF;
|
||||
break;
|
||||
case ON_OFF_SPLIT_OFF:
|
||||
s->kernel_irqchip_allowed = false;
|
||||
s->kernel_irqchip_required = false;
|
||||
s->kernel_irqchip_split = ON_OFF_AUTO_OFF;
|
||||
break;
|
||||
case ON_OFF_SPLIT_SPLIT:
|
||||
s->kernel_irqchip_allowed = true;
|
||||
s->kernel_irqchip_required = true;
|
||||
s->kernel_irqchip_split = ON_OFF_AUTO_ON;
|
||||
break;
|
||||
default:
|
||||
/* The value was checked in visit_type_OnOffSplit() above. If
|
||||
* we get here, then something is wrong in QEMU.
|
||||
*/
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool kvm_kernel_irqchip_allowed(void)
|
||||
{
|
||||
return kvm_state->kernel_irqchip_allowed;
|
||||
}
|
||||
|
||||
bool kvm_kernel_irqchip_required(void)
|
||||
{
|
||||
return kvm_state->kernel_irqchip_required;
|
||||
}
|
||||
|
||||
bool kvm_kernel_irqchip_split(void)
|
||||
{
|
||||
return kvm_state->kernel_irqchip_split == ON_OFF_AUTO_ON;
|
||||
}
|
||||
|
||||
static void kvm_accel_instance_init(Object *obj)
|
||||
{
|
||||
KVMState *s = KVM_STATE(obj);
|
||||
|
||||
s->kvm_shadow_mem = -1;
|
||||
s->kernel_irqchip_allowed = true;
|
||||
s->kernel_irqchip_split = ON_OFF_AUTO_AUTO;
|
||||
}
|
||||
|
||||
static void kvm_accel_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
AccelClass *ac = ACCEL_CLASS(oc);
|
||||
|
@ -2947,11 +3072,24 @@ static void kvm_accel_class_init(ObjectClass *oc, void *data)
|
|||
ac->init_machine = kvm_init;
|
||||
ac->has_memory = kvm_accel_has_memory;
|
||||
ac->allowed = &kvm_allowed;
|
||||
|
||||
object_class_property_add(oc, "kernel-irqchip", "on|off|split",
|
||||
NULL, kvm_set_kernel_irqchip,
|
||||
NULL, NULL, &error_abort);
|
||||
object_class_property_set_description(oc, "kernel-irqchip",
|
||||
"Configure KVM in-kernel irqchip", &error_abort);
|
||||
|
||||
object_class_property_add(oc, "kvm-shadow-mem", "int",
|
||||
kvm_get_kvm_shadow_mem, kvm_set_kvm_shadow_mem,
|
||||
NULL, NULL, &error_abort);
|
||||
object_class_property_set_description(oc, "kvm-shadow-mem",
|
||||
"KVM shadow MMU size", &error_abort);
|
||||
}
|
||||
|
||||
static const TypeInfo kvm_accel_type = {
|
||||
.name = TYPE_KVM_ACCEL,
|
||||
.parent = TYPE_ACCEL,
|
||||
.instance_init = kvm_accel_instance_init,
|
||||
.class_init = kvm_accel_class_init,
|
||||
.instance_size = sizeof(KVMState),
|
||||
};
|
||||
|
|
|
@ -64,13 +64,10 @@
|
|||
the ATOMIC_NAME macro, and redefined below. */
|
||||
#if DATA_SIZE == 1
|
||||
# define END
|
||||
# define MEND _be /* either le or be would be fine */
|
||||
#elif defined(HOST_WORDS_BIGENDIAN)
|
||||
# define END _be
|
||||
# define MEND _be
|
||||
#else
|
||||
# define END _le
|
||||
# define MEND _le
|
||||
#endif
|
||||
|
||||
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
|
@ -79,8 +76,8 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
|||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE ret;
|
||||
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
|
||||
atomic_trace_rmw_pre(env, addr, info);
|
||||
#if DATA_SIZE == 16
|
||||
|
@ -99,8 +96,8 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
|||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
||||
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
|
||||
atomic_trace_ld_pre(env, addr, info);
|
||||
val = atomic16_read(haddr);
|
||||
|
@ -114,8 +111,8 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
|||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true,
|
||||
ATOMIC_MMU_IDX);
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, true,
|
||||
ATOMIC_MMU_IDX);
|
||||
|
||||
atomic_trace_st_pre(env, addr, info);
|
||||
atomic16_set(haddr, val);
|
||||
|
@ -130,8 +127,8 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
|||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE ret;
|
||||
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
|
||||
atomic_trace_rmw_pre(env, addr, info);
|
||||
ret = atomic_xchg__nocheck(haddr, val);
|
||||
|
@ -147,10 +144,8 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
|||
ATOMIC_MMU_DECLS; \
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||
DATA_TYPE ret; \
|
||||
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, \
|
||||
false, \
|
||||
ATOMIC_MMU_IDX); \
|
||||
\
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false, \
|
||||
ATOMIC_MMU_IDX); \
|
||||
atomic_trace_rmw_pre(env, addr, info); \
|
||||
ret = atomic_##X(haddr, val); \
|
||||
ATOMIC_MMU_CLEANUP; \
|
||||
|
@ -183,10 +178,8 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
|||
ATOMIC_MMU_DECLS; \
|
||||
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||
XDATA_TYPE cmp, old, new, val = xval; \
|
||||
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, \
|
||||
false, \
|
||||
ATOMIC_MMU_IDX); \
|
||||
\
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false, \
|
||||
ATOMIC_MMU_IDX); \
|
||||
atomic_trace_rmw_pre(env, addr, info); \
|
||||
smp_mb(); \
|
||||
cmp = atomic_read__nocheck(haddr); \
|
||||
|
@ -213,7 +206,6 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
|
|||
#endif /* DATA SIZE >= 16 */
|
||||
|
||||
#undef END
|
||||
#undef MEND
|
||||
|
||||
#if DATA_SIZE > 1
|
||||
|
||||
|
@ -221,10 +213,8 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
|
|||
within the ATOMIC_NAME macro. */
|
||||
#ifdef HOST_WORDS_BIGENDIAN
|
||||
# define END _le
|
||||
# define MEND _le
|
||||
#else
|
||||
# define END _be
|
||||
# define MEND _be
|
||||
#endif
|
||||
|
||||
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
|
@ -233,9 +223,8 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
|||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE ret;
|
||||
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT,
|
||||
false,
|
||||
ATOMIC_MMU_IDX);
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
|
||||
atomic_trace_rmw_pre(env, addr, info);
|
||||
#if DATA_SIZE == 16
|
||||
|
@ -254,9 +243,8 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
|||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
||||
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT,
|
||||
false,
|
||||
ATOMIC_MMU_IDX);
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
|
||||
atomic_trace_ld_pre(env, addr, info);
|
||||
val = atomic16_read(haddr);
|
||||
|
@ -270,9 +258,8 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
|||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT,
|
||||
true,
|
||||
ATOMIC_MMU_IDX);
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, true,
|
||||
ATOMIC_MMU_IDX);
|
||||
|
||||
val = BSWAP(val);
|
||||
atomic_trace_st_pre(env, addr, info);
|
||||
|
@ -289,9 +276,8 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
|||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
ABI_TYPE ret;
|
||||
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT,
|
||||
false,
|
||||
ATOMIC_MMU_IDX);
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
|
||||
atomic_trace_rmw_pre(env, addr, info);
|
||||
ret = atomic_xchg__nocheck(haddr, BSWAP(val));
|
||||
|
@ -307,10 +293,8 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
|||
ATOMIC_MMU_DECLS; \
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||
DATA_TYPE ret; \
|
||||
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, \
|
||||
false, \
|
||||
ATOMIC_MMU_IDX); \
|
||||
\
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, \
|
||||
false, ATOMIC_MMU_IDX); \
|
||||
atomic_trace_rmw_pre(env, addr, info); \
|
||||
ret = atomic_##X(haddr, BSWAP(val)); \
|
||||
ATOMIC_MMU_CLEANUP; \
|
||||
|
@ -341,10 +325,8 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
|||
ATOMIC_MMU_DECLS; \
|
||||
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||
XDATA_TYPE ldo, ldn, old, new, val = xval; \
|
||||
uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, \
|
||||
false, \
|
||||
ATOMIC_MMU_IDX); \
|
||||
\
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, \
|
||||
false, ATOMIC_MMU_IDX); \
|
||||
atomic_trace_rmw_pre(env, addr, info); \
|
||||
smp_mb(); \
|
||||
ldn = atomic_read__nocheck(haddr); \
|
||||
|
@ -378,7 +360,6 @@ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
|
|||
#endif /* DATA_SIZE >= 16 */
|
||||
|
||||
#undef END
|
||||
#undef MEND
|
||||
#endif /* DATA_SIZE > 1 */
|
||||
|
||||
#undef BSWAP
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include "trace.h"
|
||||
#include "disas/disas.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "tcg.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "qemu/atomic.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "qemu/timer.h"
|
||||
|
@ -156,7 +156,7 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb)
|
|||
#if defined(DEBUG_DISAS)
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_CPU)
|
||||
&& qemu_log_in_addr_range(itb->pc)) {
|
||||
qemu_log_lock();
|
||||
FILE *logfile = qemu_log_lock();
|
||||
int flags = 0;
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) {
|
||||
flags |= CPU_DUMP_FPU;
|
||||
|
@ -165,7 +165,7 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb)
|
|||
flags |= CPU_DUMP_CCOP;
|
||||
#endif
|
||||
log_cpu_state(cpu, flags);
|
||||
qemu_log_unlock();
|
||||
qemu_log_unlock(logfile);
|
||||
}
|
||||
#endif /* DEBUG_DISAS */
|
||||
|
||||
|
@ -240,6 +240,8 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
|||
uint32_t cf_mask = cflags & CF_HASH_MASK;
|
||||
|
||||
if (sigsetjmp(cpu->jmp_env, 0) == 0) {
|
||||
start_exclusive();
|
||||
|
||||
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
|
||||
if (tb == NULL) {
|
||||
mmap_lock();
|
||||
|
@ -247,8 +249,6 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
|||
mmap_unlock();
|
||||
}
|
||||
|
||||
start_exclusive();
|
||||
|
||||
/* Since we got here, we know that parallel_cpus must be true. */
|
||||
parallel_cpus = false;
|
||||
cc->cpu_exec_enter(cpu);
|
||||
|
@ -271,14 +271,15 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
|||
qemu_plugin_disable_mem_helpers(cpu);
|
||||
}
|
||||
|
||||
if (cpu_in_exclusive_context(cpu)) {
|
||||
/* We might longjump out of either the codegen or the
|
||||
* execution, so must make sure we only end the exclusive
|
||||
* region if we started it.
|
||||
*/
|
||||
parallel_cpus = true;
|
||||
end_exclusive();
|
||||
}
|
||||
|
||||
/*
|
||||
* As we start the exclusive region before codegen we must still
|
||||
* be in the region if we longjump out of either the codegen or
|
||||
* the execution.
|
||||
*/
|
||||
g_assert(cpu_in_exclusive_context(cpu));
|
||||
parallel_cpus = true;
|
||||
end_exclusive();
|
||||
}
|
||||
|
||||
struct tb_desc {
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
#include "qemu/atomic.h"
|
||||
#include "qemu/atomic128.h"
|
||||
#include "translate-all.h"
|
||||
#include "trace-root.h"
|
||||
#include "trace/mem.h"
|
||||
#ifdef CONFIG_PLUGIN
|
||||
#include "qemu/plugin-memory.h"
|
||||
#endif
|
||||
|
@ -78,9 +80,14 @@ QEMU_BUILD_BUG_ON(sizeof(target_ulong) > sizeof(run_on_cpu_data));
|
|||
QEMU_BUILD_BUG_ON(NB_MMU_MODES > 16);
|
||||
#define ALL_MMUIDX_BITS ((1 << NB_MMU_MODES) - 1)
|
||||
|
||||
static inline size_t sizeof_tlb(CPUArchState *env, uintptr_t mmu_idx)
|
||||
static inline size_t tlb_n_entries(CPUTLBDescFast *fast)
|
||||
{
|
||||
return env_tlb(env)->f[mmu_idx].mask + (1 << CPU_TLB_ENTRY_BITS);
|
||||
return (fast->mask >> CPU_TLB_ENTRY_BITS) + 1;
|
||||
}
|
||||
|
||||
static inline size_t sizeof_tlb(CPUTLBDescFast *fast)
|
||||
{
|
||||
return fast->mask + (1 << CPU_TLB_ENTRY_BITS);
|
||||
}
|
||||
|
||||
static void tlb_window_reset(CPUTLBDesc *desc, int64_t ns,
|
||||
|
@ -90,26 +97,10 @@ static void tlb_window_reset(CPUTLBDesc *desc, int64_t ns,
|
|||
desc->window_max_entries = max_entries;
|
||||
}
|
||||
|
||||
static void tlb_dyn_init(CPUArchState *env)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NB_MMU_MODES; i++) {
|
||||
CPUTLBDesc *desc = &env_tlb(env)->d[i];
|
||||
size_t n_entries = 1 << CPU_TLB_DYN_DEFAULT_BITS;
|
||||
|
||||
tlb_window_reset(desc, get_clock_realtime(), 0);
|
||||
desc->n_used_entries = 0;
|
||||
env_tlb(env)->f[i].mask = (n_entries - 1) << CPU_TLB_ENTRY_BITS;
|
||||
env_tlb(env)->f[i].table = g_new(CPUTLBEntry, n_entries);
|
||||
env_tlb(env)->d[i].iotlb = g_new(CPUIOTLBEntry, n_entries);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tlb_mmu_resize_locked() - perform TLB resize bookkeeping; resize if necessary
|
||||
* @env: CPU that owns the TLB
|
||||
* @mmu_idx: MMU index of the TLB
|
||||
* @desc: The CPUTLBDesc portion of the TLB
|
||||
* @fast: The CPUTLBDescFast portion of the same TLB
|
||||
*
|
||||
* Called with tlb_lock_held.
|
||||
*
|
||||
|
@ -146,13 +137,12 @@ static void tlb_dyn_init(CPUArchState *env)
|
|||
* high), since otherwise we are likely to have a significant amount of
|
||||
* conflict misses.
|
||||
*/
|
||||
static void tlb_mmu_resize_locked(CPUArchState *env, int mmu_idx)
|
||||
static void tlb_mmu_resize_locked(CPUTLBDesc *desc, CPUTLBDescFast *fast,
|
||||
int64_t now)
|
||||
{
|
||||
CPUTLBDesc *desc = &env_tlb(env)->d[mmu_idx];
|
||||
size_t old_size = tlb_n_entries(env, mmu_idx);
|
||||
size_t old_size = tlb_n_entries(fast);
|
||||
size_t rate;
|
||||
size_t new_size = old_size;
|
||||
int64_t now = get_clock_realtime();
|
||||
int64_t window_len_ms = 100;
|
||||
int64_t window_len_ns = window_len_ms * 1000 * 1000;
|
||||
bool window_expired = now > desc->window_begin_ns + window_len_ns;
|
||||
|
@ -191,14 +181,15 @@ static void tlb_mmu_resize_locked(CPUArchState *env, int mmu_idx)
|
|||
return;
|
||||
}
|
||||
|
||||
g_free(env_tlb(env)->f[mmu_idx].table);
|
||||
g_free(env_tlb(env)->d[mmu_idx].iotlb);
|
||||
g_free(fast->table);
|
||||
g_free(desc->iotlb);
|
||||
|
||||
tlb_window_reset(desc, now, 0);
|
||||
/* desc->n_used_entries is cleared by the caller */
|
||||
env_tlb(env)->f[mmu_idx].mask = (new_size - 1) << CPU_TLB_ENTRY_BITS;
|
||||
env_tlb(env)->f[mmu_idx].table = g_try_new(CPUTLBEntry, new_size);
|
||||
env_tlb(env)->d[mmu_idx].iotlb = g_try_new(CPUIOTLBEntry, new_size);
|
||||
fast->mask = (new_size - 1) << CPU_TLB_ENTRY_BITS;
|
||||
fast->table = g_try_new(CPUTLBEntry, new_size);
|
||||
desc->iotlb = g_try_new(CPUIOTLBEntry, new_size);
|
||||
|
||||
/*
|
||||
* If the allocations fail, try smaller sizes. We just freed some
|
||||
* memory, so going back to half of new_size has a good chance of working.
|
||||
|
@ -206,27 +197,51 @@ static void tlb_mmu_resize_locked(CPUArchState *env, int mmu_idx)
|
|||
* allocations to fail though, so we progressively reduce the allocation
|
||||
* size, aborting if we cannot even allocate the smallest TLB we support.
|
||||
*/
|
||||
while (env_tlb(env)->f[mmu_idx].table == NULL ||
|
||||
env_tlb(env)->d[mmu_idx].iotlb == NULL) {
|
||||
while (fast->table == NULL || desc->iotlb == NULL) {
|
||||
if (new_size == (1 << CPU_TLB_DYN_MIN_BITS)) {
|
||||
error_report("%s: %s", __func__, strerror(errno));
|
||||
abort();
|
||||
}
|
||||
new_size = MAX(new_size >> 1, 1 << CPU_TLB_DYN_MIN_BITS);
|
||||
env_tlb(env)->f[mmu_idx].mask = (new_size - 1) << CPU_TLB_ENTRY_BITS;
|
||||
fast->mask = (new_size - 1) << CPU_TLB_ENTRY_BITS;
|
||||
|
||||
g_free(env_tlb(env)->f[mmu_idx].table);
|
||||
g_free(env_tlb(env)->d[mmu_idx].iotlb);
|
||||
env_tlb(env)->f[mmu_idx].table = g_try_new(CPUTLBEntry, new_size);
|
||||
env_tlb(env)->d[mmu_idx].iotlb = g_try_new(CPUIOTLBEntry, new_size);
|
||||
g_free(fast->table);
|
||||
g_free(desc->iotlb);
|
||||
fast->table = g_try_new(CPUTLBEntry, new_size);
|
||||
desc->iotlb = g_try_new(CPUIOTLBEntry, new_size);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void tlb_table_flush_by_mmuidx(CPUArchState *env, int mmu_idx)
|
||||
static void tlb_mmu_flush_locked(CPUTLBDesc *desc, CPUTLBDescFast *fast)
|
||||
{
|
||||
tlb_mmu_resize_locked(env, mmu_idx);
|
||||
memset(env_tlb(env)->f[mmu_idx].table, -1, sizeof_tlb(env, mmu_idx));
|
||||
env_tlb(env)->d[mmu_idx].n_used_entries = 0;
|
||||
desc->n_used_entries = 0;
|
||||
desc->large_page_addr = -1;
|
||||
desc->large_page_mask = -1;
|
||||
desc->vindex = 0;
|
||||
memset(fast->table, -1, sizeof_tlb(fast));
|
||||
memset(desc->vtable, -1, sizeof(desc->vtable));
|
||||
}
|
||||
|
||||
static void tlb_flush_one_mmuidx_locked(CPUArchState *env, int mmu_idx,
|
||||
int64_t now)
|
||||
{
|
||||
CPUTLBDesc *desc = &env_tlb(env)->d[mmu_idx];
|
||||
CPUTLBDescFast *fast = &env_tlb(env)->f[mmu_idx];
|
||||
|
||||
tlb_mmu_resize_locked(desc, fast, now);
|
||||
tlb_mmu_flush_locked(desc, fast);
|
||||
}
|
||||
|
||||
static void tlb_mmu_init(CPUTLBDesc *desc, CPUTLBDescFast *fast, int64_t now)
|
||||
{
|
||||
size_t n_entries = 1 << CPU_TLB_DYN_DEFAULT_BITS;
|
||||
|
||||
tlb_window_reset(desc, now, 0);
|
||||
desc->n_used_entries = 0;
|
||||
fast->mask = (n_entries - 1) << CPU_TLB_ENTRY_BITS;
|
||||
fast->table = g_new(CPUTLBEntry, n_entries);
|
||||
desc->iotlb = g_new(CPUIOTLBEntry, n_entries);
|
||||
tlb_mmu_flush_locked(desc, fast);
|
||||
}
|
||||
|
||||
static inline void tlb_n_used_entries_inc(CPUArchState *env, uintptr_t mmu_idx)
|
||||
|
@ -242,13 +257,17 @@ static inline void tlb_n_used_entries_dec(CPUArchState *env, uintptr_t mmu_idx)
|
|||
void tlb_init(CPUState *cpu)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
int64_t now = get_clock_realtime();
|
||||
int i;
|
||||
|
||||
qemu_spin_init(&env_tlb(env)->c.lock);
|
||||
|
||||
/* Ensure that cpu_reset performs a full flush. */
|
||||
env_tlb(env)->c.dirty = ALL_MMUIDX_BITS;
|
||||
/* All tlbs are initialized flushed. */
|
||||
env_tlb(env)->c.dirty = 0;
|
||||
|
||||
tlb_dyn_init(env);
|
||||
for (i = 0; i < NB_MMU_MODES; i++) {
|
||||
tlb_mmu_init(&env_tlb(env)->d[i], &env_tlb(env)->f[i], now);
|
||||
}
|
||||
}
|
||||
|
||||
/* flush_all_helper: run fn across all cpus
|
||||
|
@ -287,21 +306,12 @@ void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide)
|
|||
*pelide = elide;
|
||||
}
|
||||
|
||||
static void tlb_flush_one_mmuidx_locked(CPUArchState *env, int mmu_idx)
|
||||
{
|
||||
tlb_table_flush_by_mmuidx(env, mmu_idx);
|
||||
env_tlb(env)->d[mmu_idx].large_page_addr = -1;
|
||||
env_tlb(env)->d[mmu_idx].large_page_mask = -1;
|
||||
env_tlb(env)->d[mmu_idx].vindex = 0;
|
||||
memset(env_tlb(env)->d[mmu_idx].vtable, -1,
|
||||
sizeof(env_tlb(env)->d[0].vtable));
|
||||
}
|
||||
|
||||
static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
uint16_t asked = data.host_int;
|
||||
uint16_t all_dirty, work, to_clean;
|
||||
int64_t now = get_clock_realtime();
|
||||
|
||||
assert_cpu_is_self(cpu);
|
||||
|
||||
|
@ -316,7 +326,7 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data)
|
|||
|
||||
for (work = to_clean; work != 0; work &= work - 1) {
|
||||
int mmu_idx = ctz32(work);
|
||||
tlb_flush_one_mmuidx_locked(env, mmu_idx);
|
||||
tlb_flush_one_mmuidx_locked(env, mmu_idx, now);
|
||||
}
|
||||
|
||||
qemu_spin_unlock(&env_tlb(env)->c.lock);
|
||||
|
@ -438,7 +448,7 @@ static void tlb_flush_page_locked(CPUArchState *env, int midx,
|
|||
tlb_debug("forcing full flush midx %d ("
|
||||
TARGET_FMT_lx "/" TARGET_FMT_lx ")\n",
|
||||
midx, lp_addr, lp_mask);
|
||||
tlb_flush_one_mmuidx_locked(env, midx);
|
||||
tlb_flush_one_mmuidx_locked(env, midx, get_clock_realtime());
|
||||
} else {
|
||||
if (tlb_flush_entry_locked(tlb_entry(env, midx, page), page)) {
|
||||
tlb_n_used_entries_dec(env, midx);
|
||||
|
@ -447,28 +457,29 @@ static void tlb_flush_page_locked(CPUArchState *env, int midx,
|
|||
}
|
||||
}
|
||||
|
||||
/* As we are going to hijack the bottom bits of the page address for a
|
||||
* mmuidx bit mask we need to fail to build if we can't do that
|
||||
/**
|
||||
* tlb_flush_page_by_mmuidx_async_0:
|
||||
* @cpu: cpu on which to flush
|
||||
* @addr: page of virtual address to flush
|
||||
* @idxmap: set of mmu_idx to flush
|
||||
*
|
||||
* Helper for tlb_flush_page_by_mmuidx and friends, flush one page
|
||||
* at @addr from the tlbs indicated by @idxmap from @cpu.
|
||||
*/
|
||||
QEMU_BUILD_BUG_ON(NB_MMU_MODES > TARGET_PAGE_BITS_MIN);
|
||||
|
||||
static void tlb_flush_page_by_mmuidx_async_work(CPUState *cpu,
|
||||
run_on_cpu_data data)
|
||||
static void tlb_flush_page_by_mmuidx_async_0(CPUState *cpu,
|
||||
target_ulong addr,
|
||||
uint16_t idxmap)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
target_ulong addr_and_mmuidx = (target_ulong) data.target_ptr;
|
||||
target_ulong addr = addr_and_mmuidx & TARGET_PAGE_MASK;
|
||||
unsigned long mmu_idx_bitmap = addr_and_mmuidx & ALL_MMUIDX_BITS;
|
||||
int mmu_idx;
|
||||
|
||||
assert_cpu_is_self(cpu);
|
||||
|
||||
tlb_debug("page addr:" TARGET_FMT_lx " mmu_map:0x%lx\n",
|
||||
addr, mmu_idx_bitmap);
|
||||
tlb_debug("page addr:" TARGET_FMT_lx " mmu_map:0x%x\n", addr, idxmap);
|
||||
|
||||
qemu_spin_lock(&env_tlb(env)->c.lock);
|
||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||
if (test_bit(mmu_idx, &mmu_idx_bitmap)) {
|
||||
if ((idxmap >> mmu_idx) & 1) {
|
||||
tlb_flush_page_locked(env, mmu_idx, addr);
|
||||
}
|
||||
}
|
||||
|
@ -477,22 +488,75 @@ static void tlb_flush_page_by_mmuidx_async_work(CPUState *cpu,
|
|||
tb_flush_jmp_cache(cpu, addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* tlb_flush_page_by_mmuidx_async_1:
|
||||
* @cpu: cpu on which to flush
|
||||
* @data: encoded addr + idxmap
|
||||
*
|
||||
* Helper for tlb_flush_page_by_mmuidx and friends, called through
|
||||
* async_run_on_cpu. The idxmap parameter is encoded in the page
|
||||
* offset of the target_ptr field. This limits the set of mmu_idx
|
||||
* that can be passed via this method.
|
||||
*/
|
||||
static void tlb_flush_page_by_mmuidx_async_1(CPUState *cpu,
|
||||
run_on_cpu_data data)
|
||||
{
|
||||
target_ulong addr_and_idxmap = (target_ulong) data.target_ptr;
|
||||
target_ulong addr = addr_and_idxmap & TARGET_PAGE_MASK;
|
||||
uint16_t idxmap = addr_and_idxmap & ~TARGET_PAGE_MASK;
|
||||
|
||||
tlb_flush_page_by_mmuidx_async_0(cpu, addr, idxmap);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
target_ulong addr;
|
||||
uint16_t idxmap;
|
||||
} TLBFlushPageByMMUIdxData;
|
||||
|
||||
/**
|
||||
* tlb_flush_page_by_mmuidx_async_2:
|
||||
* @cpu: cpu on which to flush
|
||||
* @data: allocated addr + idxmap
|
||||
*
|
||||
* Helper for tlb_flush_page_by_mmuidx and friends, called through
|
||||
* async_run_on_cpu. The addr+idxmap parameters are stored in a
|
||||
* TLBFlushPageByMMUIdxData structure that has been allocated
|
||||
* specifically for this helper. Free the structure when done.
|
||||
*/
|
||||
static void tlb_flush_page_by_mmuidx_async_2(CPUState *cpu,
|
||||
run_on_cpu_data data)
|
||||
{
|
||||
TLBFlushPageByMMUIdxData *d = data.host_ptr;
|
||||
|
||||
tlb_flush_page_by_mmuidx_async_0(cpu, d->addr, d->idxmap);
|
||||
g_free(d);
|
||||
}
|
||||
|
||||
void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, uint16_t idxmap)
|
||||
{
|
||||
target_ulong addr_and_mmu_idx;
|
||||
|
||||
tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%" PRIx16 "\n", addr, idxmap);
|
||||
|
||||
/* This should already be page aligned */
|
||||
addr_and_mmu_idx = addr & TARGET_PAGE_MASK;
|
||||
addr_and_mmu_idx |= idxmap;
|
||||
addr &= TARGET_PAGE_MASK;
|
||||
|
||||
if (!qemu_cpu_is_self(cpu)) {
|
||||
async_run_on_cpu(cpu, tlb_flush_page_by_mmuidx_async_work,
|
||||
RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx));
|
||||
if (qemu_cpu_is_self(cpu)) {
|
||||
tlb_flush_page_by_mmuidx_async_0(cpu, addr, idxmap);
|
||||
} else if (idxmap < TARGET_PAGE_SIZE) {
|
||||
/*
|
||||
* Most targets have only a few mmu_idx. In the case where
|
||||
* we can stuff idxmap into the low TARGET_PAGE_BITS, avoid
|
||||
* allocating memory for this operation.
|
||||
*/
|
||||
async_run_on_cpu(cpu, tlb_flush_page_by_mmuidx_async_1,
|
||||
RUN_ON_CPU_TARGET_PTR(addr | idxmap));
|
||||
} else {
|
||||
tlb_flush_page_by_mmuidx_async_work(
|
||||
cpu, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx));
|
||||
TLBFlushPageByMMUIdxData *d = g_new(TLBFlushPageByMMUIdxData, 1);
|
||||
|
||||
/* Otherwise allocate a structure, freed by the worker. */
|
||||
d->addr = addr;
|
||||
d->idxmap = idxmap;
|
||||
async_run_on_cpu(cpu, tlb_flush_page_by_mmuidx_async_2,
|
||||
RUN_ON_CPU_HOST_PTR(d));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -504,17 +568,36 @@ void tlb_flush_page(CPUState *cpu, target_ulong addr)
|
|||
void tlb_flush_page_by_mmuidx_all_cpus(CPUState *src_cpu, target_ulong addr,
|
||||
uint16_t idxmap)
|
||||
{
|
||||
const run_on_cpu_func fn = tlb_flush_page_by_mmuidx_async_work;
|
||||
target_ulong addr_and_mmu_idx;
|
||||
|
||||
tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%"PRIx16"\n", addr, idxmap);
|
||||
|
||||
/* This should already be page aligned */
|
||||
addr_and_mmu_idx = addr & TARGET_PAGE_MASK;
|
||||
addr_and_mmu_idx |= idxmap;
|
||||
addr &= TARGET_PAGE_MASK;
|
||||
|
||||
flush_all_helper(src_cpu, fn, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx));
|
||||
fn(src_cpu, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx));
|
||||
/*
|
||||
* Allocate memory to hold addr+idxmap only when needed.
|
||||
* See tlb_flush_page_by_mmuidx for details.
|
||||
*/
|
||||
if (idxmap < TARGET_PAGE_SIZE) {
|
||||
flush_all_helper(src_cpu, tlb_flush_page_by_mmuidx_async_1,
|
||||
RUN_ON_CPU_TARGET_PTR(addr | idxmap));
|
||||
} else {
|
||||
CPUState *dst_cpu;
|
||||
|
||||
/* Allocate a separate data block for each destination cpu. */
|
||||
CPU_FOREACH(dst_cpu) {
|
||||
if (dst_cpu != src_cpu) {
|
||||
TLBFlushPageByMMUIdxData *d
|
||||
= g_new(TLBFlushPageByMMUIdxData, 1);
|
||||
|
||||
d->addr = addr;
|
||||
d->idxmap = idxmap;
|
||||
async_run_on_cpu(dst_cpu, tlb_flush_page_by_mmuidx_async_2,
|
||||
RUN_ON_CPU_HOST_PTR(d));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tlb_flush_page_by_mmuidx_async_0(src_cpu, addr, idxmap);
|
||||
}
|
||||
|
||||
void tlb_flush_page_all_cpus(CPUState *src, target_ulong addr)
|
||||
|
@ -526,17 +609,41 @@ void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
|
|||
target_ulong addr,
|
||||
uint16_t idxmap)
|
||||
{
|
||||
const run_on_cpu_func fn = tlb_flush_page_by_mmuidx_async_work;
|
||||
target_ulong addr_and_mmu_idx;
|
||||
|
||||
tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%"PRIx16"\n", addr, idxmap);
|
||||
|
||||
/* This should already be page aligned */
|
||||
addr_and_mmu_idx = addr & TARGET_PAGE_MASK;
|
||||
addr_and_mmu_idx |= idxmap;
|
||||
addr &= TARGET_PAGE_MASK;
|
||||
|
||||
flush_all_helper(src_cpu, fn, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx));
|
||||
async_safe_run_on_cpu(src_cpu, fn, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx));
|
||||
/*
|
||||
* Allocate memory to hold addr+idxmap only when needed.
|
||||
* See tlb_flush_page_by_mmuidx for details.
|
||||
*/
|
||||
if (idxmap < TARGET_PAGE_SIZE) {
|
||||
flush_all_helper(src_cpu, tlb_flush_page_by_mmuidx_async_1,
|
||||
RUN_ON_CPU_TARGET_PTR(addr | idxmap));
|
||||
async_safe_run_on_cpu(src_cpu, tlb_flush_page_by_mmuidx_async_1,
|
||||
RUN_ON_CPU_TARGET_PTR(addr | idxmap));
|
||||
} else {
|
||||
CPUState *dst_cpu;
|
||||
TLBFlushPageByMMUIdxData *d;
|
||||
|
||||
/* Allocate a separate data block for each destination cpu. */
|
||||
CPU_FOREACH(dst_cpu) {
|
||||
if (dst_cpu != src_cpu) {
|
||||
d = g_new(TLBFlushPageByMMUIdxData, 1);
|
||||
d->addr = addr;
|
||||
d->idxmap = idxmap;
|
||||
async_run_on_cpu(dst_cpu, tlb_flush_page_by_mmuidx_async_2,
|
||||
RUN_ON_CPU_HOST_PTR(d));
|
||||
}
|
||||
}
|
||||
|
||||
d = g_new(TLBFlushPageByMMUIdxData, 1);
|
||||
d->addr = addr;
|
||||
d->idxmap = idxmap;
|
||||
async_safe_run_on_cpu(src_cpu, tlb_flush_page_by_mmuidx_async_2,
|
||||
RUN_ON_CPU_HOST_PTR(d));
|
||||
}
|
||||
}
|
||||
|
||||
void tlb_flush_page_all_cpus_synced(CPUState *src, target_ulong addr)
|
||||
|
@ -620,7 +727,7 @@ void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length)
|
|||
qemu_spin_lock(&env_tlb(env)->c.lock);
|
||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||
unsigned int i;
|
||||
unsigned int n = tlb_n_entries(env, mmu_idx);
|
||||
unsigned int n = tlb_n_entries(&env_tlb(env)->f[mmu_idx]);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
tlb_reset_dirty_range_locked(&env_tlb(env)->f[mmu_idx].table[i],
|
||||
|
@ -1625,6 +1732,137 @@ tcg_target_ulong helper_be_ldsl_mmu(CPUArchState *env, target_ulong addr,
|
|||
return (int32_t)helper_be_ldul_mmu(env, addr, oi, retaddr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Load helpers for cpu_ldst.h.
|
||||
*/
|
||||
|
||||
static inline uint64_t cpu_load_helper(CPUArchState *env, abi_ptr addr,
|
||||
int mmu_idx, uintptr_t retaddr,
|
||||
MemOp op, FullLoadHelper *full_load)
|
||||
{
|
||||
uint16_t meminfo;
|
||||
TCGMemOpIdx oi;
|
||||
uint64_t ret;
|
||||
|
||||
meminfo = trace_mem_get_info(op, mmu_idx, false);
|
||||
trace_guest_mem_before_exec(env_cpu(env), addr, meminfo);
|
||||
|
||||
op &= ~MO_SIGN;
|
||||
oi = make_memop_idx(op, mmu_idx);
|
||||
ret = full_load(env, addr, oi, retaddr);
|
||||
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, meminfo);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr addr,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
return cpu_load_helper(env, addr, mmu_idx, ra, MO_UB, full_ldub_mmu);
|
||||
}
|
||||
|
||||
int cpu_ldsb_mmuidx_ra(CPUArchState *env, abi_ptr addr,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
return (int8_t)cpu_load_helper(env, addr, mmu_idx, ra, MO_SB,
|
||||
full_ldub_mmu);
|
||||
}
|
||||
|
||||
uint32_t cpu_lduw_mmuidx_ra(CPUArchState *env, abi_ptr addr,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
return cpu_load_helper(env, addr, mmu_idx, ra, MO_TEUW,
|
||||
MO_TE == MO_LE
|
||||
? full_le_lduw_mmu : full_be_lduw_mmu);
|
||||
}
|
||||
|
||||
int cpu_ldsw_mmuidx_ra(CPUArchState *env, abi_ptr addr,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
return (int16_t)cpu_load_helper(env, addr, mmu_idx, ra, MO_TESW,
|
||||
MO_TE == MO_LE
|
||||
? full_le_lduw_mmu : full_be_lduw_mmu);
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_mmuidx_ra(CPUArchState *env, abi_ptr addr,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
return cpu_load_helper(env, addr, mmu_idx, ra, MO_TEUL,
|
||||
MO_TE == MO_LE
|
||||
? full_le_ldul_mmu : full_be_ldul_mmu);
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_mmuidx_ra(CPUArchState *env, abi_ptr addr,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
return cpu_load_helper(env, addr, mmu_idx, ra, MO_TEQ,
|
||||
MO_TE == MO_LE
|
||||
? helper_le_ldq_mmu : helper_be_ldq_mmu);
|
||||
}
|
||||
|
||||
uint32_t cpu_ldub_data_ra(CPUArchState *env, target_ulong ptr,
|
||||
uintptr_t retaddr)
|
||||
{
|
||||
return cpu_ldub_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr);
|
||||
}
|
||||
|
||||
int cpu_ldsb_data_ra(CPUArchState *env, target_ulong ptr, uintptr_t retaddr)
|
||||
{
|
||||
return cpu_ldsb_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr);
|
||||
}
|
||||
|
||||
uint32_t cpu_lduw_data_ra(CPUArchState *env, target_ulong ptr,
|
||||
uintptr_t retaddr)
|
||||
{
|
||||
return cpu_lduw_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr);
|
||||
}
|
||||
|
||||
int cpu_ldsw_data_ra(CPUArchState *env, target_ulong ptr, uintptr_t retaddr)
|
||||
{
|
||||
return cpu_ldsw_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr);
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_data_ra(CPUArchState *env, target_ulong ptr, uintptr_t retaddr)
|
||||
{
|
||||
return cpu_ldl_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr);
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_data_ra(CPUArchState *env, target_ulong ptr, uintptr_t retaddr)
|
||||
{
|
||||
return cpu_ldq_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr);
|
||||
}
|
||||
|
||||
uint32_t cpu_ldub_data(CPUArchState *env, target_ulong ptr)
|
||||
{
|
||||
return cpu_ldub_data_ra(env, ptr, 0);
|
||||
}
|
||||
|
||||
int cpu_ldsb_data(CPUArchState *env, target_ulong ptr)
|
||||
{
|
||||
return cpu_ldsb_data_ra(env, ptr, 0);
|
||||
}
|
||||
|
||||
uint32_t cpu_lduw_data(CPUArchState *env, target_ulong ptr)
|
||||
{
|
||||
return cpu_lduw_data_ra(env, ptr, 0);
|
||||
}
|
||||
|
||||
int cpu_ldsw_data(CPUArchState *env, target_ulong ptr)
|
||||
{
|
||||
return cpu_ldsw_data_ra(env, ptr, 0);
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_data(CPUArchState *env, target_ulong ptr)
|
||||
{
|
||||
return cpu_ldl_data_ra(env, ptr, 0);
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_data(CPUArchState *env, target_ulong ptr)
|
||||
{
|
||||
return cpu_ldq_data_ra(env, ptr, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Store Helpers
|
||||
*/
|
||||
|
@ -1854,6 +2092,94 @@ void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val,
|
|||
store_helper(env, addr, val, oi, retaddr, MO_BEQ);
|
||||
}
|
||||
|
||||
/*
|
||||
* Store Helpers for cpu_ldst.h
|
||||
*/
|
||||
|
||||
static inline void QEMU_ALWAYS_INLINE
|
||||
cpu_store_helper(CPUArchState *env, target_ulong addr, uint64_t val,
|
||||
int mmu_idx, uintptr_t retaddr, MemOp op)
|
||||
{
|
||||
TCGMemOpIdx oi;
|
||||
uint16_t meminfo;
|
||||
|
||||
meminfo = trace_mem_get_info(op, mmu_idx, true);
|
||||
trace_guest_mem_before_exec(env_cpu(env), addr, meminfo);
|
||||
|
||||
oi = make_memop_idx(op, mmu_idx);
|
||||
store_helper(env, addr, val, oi, retaddr, op);
|
||||
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, meminfo);
|
||||
}
|
||||
|
||||
void cpu_stb_mmuidx_ra(CPUArchState *env, target_ulong addr, uint32_t val,
|
||||
int mmu_idx, uintptr_t retaddr)
|
||||
{
|
||||
cpu_store_helper(env, addr, val, mmu_idx, retaddr, MO_UB);
|
||||
}
|
||||
|
||||
void cpu_stw_mmuidx_ra(CPUArchState *env, target_ulong addr, uint32_t val,
|
||||
int mmu_idx, uintptr_t retaddr)
|
||||
{
|
||||
cpu_store_helper(env, addr, val, mmu_idx, retaddr, MO_TEUW);
|
||||
}
|
||||
|
||||
void cpu_stl_mmuidx_ra(CPUArchState *env, target_ulong addr, uint32_t val,
|
||||
int mmu_idx, uintptr_t retaddr)
|
||||
{
|
||||
cpu_store_helper(env, addr, val, mmu_idx, retaddr, MO_TEUL);
|
||||
}
|
||||
|
||||
void cpu_stq_mmuidx_ra(CPUArchState *env, target_ulong addr, uint64_t val,
|
||||
int mmu_idx, uintptr_t retaddr)
|
||||
{
|
||||
cpu_store_helper(env, addr, val, mmu_idx, retaddr, MO_TEQ);
|
||||
}
|
||||
|
||||
void cpu_stb_data_ra(CPUArchState *env, target_ulong ptr,
|
||||
uint32_t val, uintptr_t retaddr)
|
||||
{
|
||||
cpu_stb_mmuidx_ra(env, ptr, val, cpu_mmu_index(env, false), retaddr);
|
||||
}
|
||||
|
||||
void cpu_stw_data_ra(CPUArchState *env, target_ulong ptr,
|
||||
uint32_t val, uintptr_t retaddr)
|
||||
{
|
||||
cpu_stw_mmuidx_ra(env, ptr, val, cpu_mmu_index(env, false), retaddr);
|
||||
}
|
||||
|
||||
void cpu_stl_data_ra(CPUArchState *env, target_ulong ptr,
|
||||
uint32_t val, uintptr_t retaddr)
|
||||
{
|
||||
cpu_stl_mmuidx_ra(env, ptr, val, cpu_mmu_index(env, false), retaddr);
|
||||
}
|
||||
|
||||
void cpu_stq_data_ra(CPUArchState *env, target_ulong ptr,
|
||||
uint64_t val, uintptr_t retaddr)
|
||||
{
|
||||
cpu_stq_mmuidx_ra(env, ptr, val, cpu_mmu_index(env, false), retaddr);
|
||||
}
|
||||
|
||||
void cpu_stb_data(CPUArchState *env, target_ulong ptr, uint32_t val)
|
||||
{
|
||||
cpu_stb_data_ra(env, ptr, val, 0);
|
||||
}
|
||||
|
||||
void cpu_stw_data(CPUArchState *env, target_ulong ptr, uint32_t val)
|
||||
{
|
||||
cpu_stw_data_ra(env, ptr, val, 0);
|
||||
}
|
||||
|
||||
void cpu_stl_data(CPUArchState *env, target_ulong ptr, uint32_t val)
|
||||
{
|
||||
cpu_stl_data_ra(env, ptr, val, 0);
|
||||
}
|
||||
|
||||
void cpu_stq_data(CPUArchState *env, target_ulong ptr, uint64_t val)
|
||||
{
|
||||
cpu_stq_data_ra(env, ptr, val, 0);
|
||||
}
|
||||
|
||||
/* First set of helpers allows passing in of OI and RETADDR. This makes
|
||||
them callable from other helpers. */
|
||||
|
||||
|
@ -1912,98 +2238,50 @@ void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val,
|
|||
|
||||
/* Code access functions. */
|
||||
|
||||
static uint64_t full_ldub_cmmu(CPUArchState *env, target_ulong addr,
|
||||
static uint64_t full_ldub_code(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, MO_8, true, full_ldub_cmmu);
|
||||
return load_helper(env, addr, oi, retaddr, MO_8, true, full_ldub_code);
|
||||
}
|
||||
|
||||
uint8_t helper_ret_ldub_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr addr)
|
||||
{
|
||||
return full_ldub_cmmu(env, addr, oi, retaddr);
|
||||
TCGMemOpIdx oi = make_memop_idx(MO_UB, cpu_mmu_index(env, true));
|
||||
return full_ldub_code(env, addr, oi, 0);
|
||||
}
|
||||
|
||||
int8_t helper_ret_ldsb_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
static uint64_t full_lduw_code(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return (int8_t) full_ldub_cmmu(env, addr, oi, retaddr);
|
||||
return load_helper(env, addr, oi, retaddr, MO_TEUW, true, full_lduw_code);
|
||||
}
|
||||
|
||||
static uint64_t full_le_lduw_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr addr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, MO_LEUW, true,
|
||||
full_le_lduw_cmmu);
|
||||
TCGMemOpIdx oi = make_memop_idx(MO_TEUW, cpu_mmu_index(env, true));
|
||||
return full_lduw_code(env, addr, oi, 0);
|
||||
}
|
||||
|
||||
uint16_t helper_le_lduw_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
static uint64_t full_ldl_code(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return full_le_lduw_cmmu(env, addr, oi, retaddr);
|
||||
return load_helper(env, addr, oi, retaddr, MO_TEUL, true, full_ldl_code);
|
||||
}
|
||||
|
||||
int16_t helper_le_ldsw_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr addr)
|
||||
{
|
||||
return (int16_t) full_le_lduw_cmmu(env, addr, oi, retaddr);
|
||||
TCGMemOpIdx oi = make_memop_idx(MO_TEUL, cpu_mmu_index(env, true));
|
||||
return full_ldl_code(env, addr, oi, 0);
|
||||
}
|
||||
|
||||
static uint64_t full_be_lduw_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
static uint64_t full_ldq_code(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, MO_BEUW, true,
|
||||
full_be_lduw_cmmu);
|
||||
return load_helper(env, addr, oi, retaddr, MO_TEQ, true, full_ldq_code);
|
||||
}
|
||||
|
||||
uint16_t helper_be_lduw_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr addr)
|
||||
{
|
||||
return full_be_lduw_cmmu(env, addr, oi, retaddr);
|
||||
}
|
||||
|
||||
int16_t helper_be_ldsw_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return (int16_t) full_be_lduw_cmmu(env, addr, oi, retaddr);
|
||||
}
|
||||
|
||||
static uint64_t full_le_ldul_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, MO_LEUL, true,
|
||||
full_le_ldul_cmmu);
|
||||
}
|
||||
|
||||
uint32_t helper_le_ldl_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return full_le_ldul_cmmu(env, addr, oi, retaddr);
|
||||
}
|
||||
|
||||
static uint64_t full_be_ldul_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, MO_BEUL, true,
|
||||
full_be_ldul_cmmu);
|
||||
}
|
||||
|
||||
uint32_t helper_be_ldl_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return full_be_ldul_cmmu(env, addr, oi, retaddr);
|
||||
}
|
||||
|
||||
uint64_t helper_le_ldq_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, MO_LEQ, true,
|
||||
helper_le_ldq_cmmu);
|
||||
}
|
||||
|
||||
uint64_t helper_be_ldq_cmmu(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return load_helper(env, addr, oi, retaddr, MO_BEQ, true,
|
||||
helper_be_ldq_cmmu);
|
||||
TCGMemOpIdx oi = make_memop_idx(MO_TEQ, cpu_mmu_index(env, true));
|
||||
return full_ldq_code(env, addr, oi, 0);
|
||||
}
|
||||
|
|
|
@ -30,8 +30,23 @@
|
|||
#include "cpu.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/boards.h"
|
||||
#include "qapi/qapi-builtin-visit.h"
|
||||
|
||||
unsigned long tcg_tb_size;
|
||||
typedef struct TCGState {
|
||||
AccelState parent_obj;
|
||||
|
||||
bool mttcg_enabled;
|
||||
unsigned long tb_size;
|
||||
} TCGState;
|
||||
|
||||
#define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg")
|
||||
|
||||
#define TCG_STATE(obj) \
|
||||
OBJECT_CHECK(TCGState, (obj), TYPE_TCG_ACCEL)
|
||||
|
||||
/* mask must never be zero, except for A20 change call */
|
||||
static void tcg_handle_interrupt(CPUState *cpu, int mask)
|
||||
|
@ -58,27 +73,153 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We default to false if we know other options have been enabled
|
||||
* which are currently incompatible with MTTCG. Otherwise when each
|
||||
* guest (target) has been updated to support:
|
||||
* - atomic instructions
|
||||
* - memory ordering primitives (barriers)
|
||||
* they can set the appropriate CONFIG flags in ${target}-softmmu.mak
|
||||
*
|
||||
* Once a guest architecture has been converted to the new primitives
|
||||
* there are two remaining limitations to check.
|
||||
*
|
||||
* - The guest can't be oversized (e.g. 64 bit guest on 32 bit host)
|
||||
* - The host must have a stronger memory order than the guest
|
||||
*
|
||||
* It may be possible in future to support strong guests on weak hosts
|
||||
* but that will require tagging all load/stores in a guest with their
|
||||
* implicit memory order requirements which would likely slow things
|
||||
* down a lot.
|
||||
*/
|
||||
|
||||
static bool check_tcg_memory_orders_compatible(void)
|
||||
{
|
||||
#if defined(TCG_GUEST_DEFAULT_MO) && defined(TCG_TARGET_DEFAULT_MO)
|
||||
return (TCG_GUEST_DEFAULT_MO & ~TCG_TARGET_DEFAULT_MO) == 0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool default_mttcg_enabled(void)
|
||||
{
|
||||
if (use_icount || TCG_OVERSIZED_GUEST) {
|
||||
return false;
|
||||
} else {
|
||||
#ifdef TARGET_SUPPORTS_MTTCG
|
||||
return check_tcg_memory_orders_compatible();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void tcg_accel_instance_init(Object *obj)
|
||||
{
|
||||
TCGState *s = TCG_STATE(obj);
|
||||
|
||||
s->mttcg_enabled = default_mttcg_enabled();
|
||||
}
|
||||
|
||||
static int tcg_init(MachineState *ms)
|
||||
{
|
||||
tcg_exec_init(tcg_tb_size * 1024 * 1024);
|
||||
TCGState *s = TCG_STATE(current_accel());
|
||||
|
||||
tcg_exec_init(s->tb_size * 1024 * 1024);
|
||||
cpu_interrupt_handler = tcg_handle_interrupt;
|
||||
mttcg_enabled = s->mttcg_enabled;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *tcg_get_thread(Object *obj, Error **errp)
|
||||
{
|
||||
TCGState *s = TCG_STATE(obj);
|
||||
|
||||
return g_strdup(s->mttcg_enabled ? "multi" : "single");
|
||||
}
|
||||
|
||||
static void tcg_set_thread(Object *obj, const char *value, Error **errp)
|
||||
{
|
||||
TCGState *s = TCG_STATE(obj);
|
||||
|
||||
if (strcmp(value, "multi") == 0) {
|
||||
if (TCG_OVERSIZED_GUEST) {
|
||||
error_setg(errp, "No MTTCG when guest word size > hosts");
|
||||
} else if (use_icount) {
|
||||
error_setg(errp, "No MTTCG when icount is enabled");
|
||||
} else {
|
||||
#ifndef TARGET_SUPPORTS_MTTCG
|
||||
warn_report("Guest not yet converted to MTTCG - "
|
||||
"you may get unexpected results");
|
||||
#endif
|
||||
if (!check_tcg_memory_orders_compatible()) {
|
||||
warn_report("Guest expects a stronger memory ordering "
|
||||
"than the host provides");
|
||||
error_printf("This may cause strange/hard to debug errors\n");
|
||||
}
|
||||
s->mttcg_enabled = true;
|
||||
}
|
||||
} else if (strcmp(value, "single") == 0) {
|
||||
s->mttcg_enabled = false;
|
||||
} else {
|
||||
error_setg(errp, "Invalid 'thread' setting %s", value);
|
||||
}
|
||||
}
|
||||
|
||||
static void tcg_get_tb_size(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
TCGState *s = TCG_STATE(obj);
|
||||
uint32_t value = s->tb_size;
|
||||
|
||||
visit_type_uint32(v, name, &value, errp);
|
||||
}
|
||||
|
||||
static void tcg_set_tb_size(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
TCGState *s = TCG_STATE(obj);
|
||||
Error *error = NULL;
|
||||
uint32_t value;
|
||||
|
||||
visit_type_uint32(v, name, &value, &error);
|
||||
if (error) {
|
||||
error_propagate(errp, error);
|
||||
return;
|
||||
}
|
||||
|
||||
s->tb_size = value;
|
||||
}
|
||||
|
||||
static void tcg_accel_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
AccelClass *ac = ACCEL_CLASS(oc);
|
||||
ac->name = "tcg";
|
||||
ac->init_machine = tcg_init;
|
||||
ac->allowed = &tcg_allowed;
|
||||
}
|
||||
|
||||
#define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg")
|
||||
object_class_property_add_str(oc, "thread",
|
||||
tcg_get_thread,
|
||||
tcg_set_thread,
|
||||
NULL);
|
||||
|
||||
object_class_property_add(oc, "tb-size", "int",
|
||||
tcg_get_tb_size, tcg_set_tb_size,
|
||||
NULL, NULL, &error_abort);
|
||||
object_class_property_set_description(oc, "tb-size",
|
||||
"TCG translation block cache size", &error_abort);
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo tcg_accel_type = {
|
||||
.name = TYPE_TCG_ACCEL,
|
||||
.parent = TYPE_ACCEL,
|
||||
.instance_init = tcg_accel_instance_init,
|
||||
.class_init = tcg_accel_class_init,
|
||||
.instance_size = sizeof(TCGState),
|
||||
};
|
||||
|
||||
static void register_accel_types(void)
|
||||
|
|
|
@ -21,51 +21,9 @@
|
|||
#include "qemu/host-utils.h"
|
||||
#include "cpu.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "tcg-gvec-desc.h"
|
||||
#include "tcg/tcg-gvec-desc.h"
|
||||
|
||||
|
||||
/* Virtually all hosts support 16-byte vectors. Those that don't can emulate
|
||||
* them via GCC's generic vector extension. This turns out to be simpler and
|
||||
* more reliable than getting the compiler to autovectorize.
|
||||
*
|
||||
* In tcg-op-gvec.c, we asserted that both the size and alignment of the data
|
||||
* are multiples of 16.
|
||||
*
|
||||
* When the compiler does not support all of the operations we require, the
|
||||
* loops are written so that we can always fall back on the base types.
|
||||
*/
|
||||
#ifdef CONFIG_VECTOR16
|
||||
typedef uint8_t vec8 __attribute__((vector_size(16)));
|
||||
typedef uint16_t vec16 __attribute__((vector_size(16)));
|
||||
typedef uint32_t vec32 __attribute__((vector_size(16)));
|
||||
typedef uint64_t vec64 __attribute__((vector_size(16)));
|
||||
|
||||
typedef int8_t svec8 __attribute__((vector_size(16)));
|
||||
typedef int16_t svec16 __attribute__((vector_size(16)));
|
||||
typedef int32_t svec32 __attribute__((vector_size(16)));
|
||||
typedef int64_t svec64 __attribute__((vector_size(16)));
|
||||
|
||||
#define DUP16(X) { X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X }
|
||||
#define DUP8(X) { X, X, X, X, X, X, X, X }
|
||||
#define DUP4(X) { X, X, X, X }
|
||||
#define DUP2(X) { X, X }
|
||||
#else
|
||||
typedef uint8_t vec8;
|
||||
typedef uint16_t vec16;
|
||||
typedef uint32_t vec32;
|
||||
typedef uint64_t vec64;
|
||||
|
||||
typedef int8_t svec8;
|
||||
typedef int16_t svec16;
|
||||
typedef int32_t svec32;
|
||||
typedef int64_t svec64;
|
||||
|
||||
#define DUP16(X) X
|
||||
#define DUP8(X) X
|
||||
#define DUP4(X) X
|
||||
#define DUP2(X) X
|
||||
#endif /* CONFIG_VECTOR16 */
|
||||
|
||||
static inline void clear_high(void *d, intptr_t oprsz, uint32_t desc)
|
||||
{
|
||||
intptr_t maxsz = simd_maxsz(desc);
|
||||
|
@ -83,8 +41,8 @@ void HELPER(gvec_add8)(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(vec8)) {
|
||||
*(vec8 *)(d + i) = *(vec8 *)(a + i) + *(vec8 *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) + *(uint8_t *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -94,8 +52,8 @@ void HELPER(gvec_add16)(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(vec16)) {
|
||||
*(vec16 *)(d + i) = *(vec16 *)(a + i) + *(vec16 *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) + *(uint16_t *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -105,8 +63,8 @@ void HELPER(gvec_add32)(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(vec32)) {
|
||||
*(vec32 *)(d + i) = *(vec32 *)(a + i) + *(vec32 *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) + *(uint32_t *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -116,8 +74,8 @@ void HELPER(gvec_add64)(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);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) + *(uint64_t *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -125,11 +83,10 @@ void HELPER(gvec_add64)(void *d, void *a, void *b, uint32_t desc)
|
|||
void HELPER(gvec_adds8)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec8 vecb = (vec8)DUP16(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec8)) {
|
||||
*(vec8 *)(d + i) = *(vec8 *)(a + i) + vecb;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) + (uint8_t)b;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -137,11 +94,10 @@ void HELPER(gvec_adds8)(void *d, void *a, uint64_t b, uint32_t desc)
|
|||
void HELPER(gvec_adds16)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec16 vecb = (vec16)DUP8(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec16)) {
|
||||
*(vec16 *)(d + i) = *(vec16 *)(a + i) + vecb;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) + (uint16_t)b;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -149,11 +105,10 @@ void HELPER(gvec_adds16)(void *d, void *a, uint64_t b, uint32_t desc)
|
|||
void HELPER(gvec_adds32)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec32 vecb = (vec32)DUP4(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec32)) {
|
||||
*(vec32 *)(d + i) = *(vec32 *)(a + i) + vecb;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) + (uint32_t)b;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -161,11 +116,10 @@ void HELPER(gvec_adds32)(void *d, void *a, uint64_t b, uint32_t desc)
|
|||
void HELPER(gvec_adds64)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec64 vecb = (vec64)DUP2(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) + vecb;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) + b;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -175,8 +129,8 @@ void HELPER(gvec_sub8)(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(vec8)) {
|
||||
*(vec8 *)(d + i) = *(vec8 *)(a + i) - *(vec8 *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) - *(uint8_t *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -186,8 +140,8 @@ void HELPER(gvec_sub16)(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(vec16)) {
|
||||
*(vec16 *)(d + i) = *(vec16 *)(a + i) - *(vec16 *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) - *(uint16_t *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -197,8 +151,8 @@ void HELPER(gvec_sub32)(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(vec32)) {
|
||||
*(vec32 *)(d + i) = *(vec32 *)(a + i) - *(vec32 *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) - *(uint32_t *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -208,8 +162,8 @@ void HELPER(gvec_sub64)(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);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) - *(uint64_t *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -217,11 +171,10 @@ void HELPER(gvec_sub64)(void *d, void *a, void *b, uint32_t desc)
|
|||
void HELPER(gvec_subs8)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec8 vecb = (vec8)DUP16(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec8)) {
|
||||
*(vec8 *)(d + i) = *(vec8 *)(a + i) - vecb;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) - (uint8_t)b;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -229,11 +182,10 @@ void HELPER(gvec_subs8)(void *d, void *a, uint64_t b, uint32_t desc)
|
|||
void HELPER(gvec_subs16)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec16 vecb = (vec16)DUP8(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec16)) {
|
||||
*(vec16 *)(d + i) = *(vec16 *)(a + i) - vecb;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) - (uint16_t)b;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -241,11 +193,10 @@ void HELPER(gvec_subs16)(void *d, void *a, uint64_t b, uint32_t desc)
|
|||
void HELPER(gvec_subs32)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec32 vecb = (vec32)DUP4(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec32)) {
|
||||
*(vec32 *)(d + i) = *(vec32 *)(a + i) - vecb;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) - (uint32_t)b;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -253,11 +204,10 @@ void HELPER(gvec_subs32)(void *d, void *a, uint64_t b, uint32_t desc)
|
|||
void HELPER(gvec_subs64)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec64 vecb = (vec64)DUP2(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) - vecb;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) - b;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -267,8 +217,8 @@ void HELPER(gvec_mul8)(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(vec8)) {
|
||||
*(vec8 *)(d + i) = *(vec8 *)(a + i) * *(vec8 *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) * *(uint8_t *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -278,8 +228,8 @@ void HELPER(gvec_mul16)(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(vec16)) {
|
||||
*(vec16 *)(d + i) = *(vec16 *)(a + i) * *(vec16 *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) * *(uint16_t *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -289,8 +239,8 @@ void HELPER(gvec_mul32)(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(vec32)) {
|
||||
*(vec32 *)(d + i) = *(vec32 *)(a + i) * *(vec32 *)(b + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) * *(uint32_t *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -300,8 +250,8 @@ void HELPER(gvec_mul64)(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);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) * *(uint64_t *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -309,11 +259,10 @@ void HELPER(gvec_mul64)(void *d, void *a, void *b, uint32_t desc)
|
|||
void HELPER(gvec_muls8)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec8 vecb = (vec8)DUP16(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec8)) {
|
||||
*(vec8 *)(d + i) = *(vec8 *)(a + i) * vecb;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) * (uint8_t)b;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -321,11 +270,10 @@ void HELPER(gvec_muls8)(void *d, void *a, uint64_t b, uint32_t desc)
|
|||
void HELPER(gvec_muls16)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec16 vecb = (vec16)DUP8(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec16)) {
|
||||
*(vec16 *)(d + i) = *(vec16 *)(a + i) * vecb;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) * (uint16_t)b;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -333,11 +281,10 @@ void HELPER(gvec_muls16)(void *d, void *a, uint64_t b, uint32_t desc)
|
|||
void HELPER(gvec_muls32)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec32 vecb = (vec32)DUP4(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec32)) {
|
||||
*(vec32 *)(d + i) = *(vec32 *)(a + i) * vecb;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) * (uint32_t)b;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -345,11 +292,10 @@ void HELPER(gvec_muls32)(void *d, void *a, uint64_t b, uint32_t desc)
|
|||
void HELPER(gvec_muls64)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec64 vecb = (vec64)DUP2(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) * vecb;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) * b;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -359,8 +305,8 @@ void HELPER(gvec_neg8)(void *d, void *a, uint32_t desc)
|
|||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec8)) {
|
||||
*(vec8 *)(d + i) = -*(vec8 *)(a + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(uint8_t *)(d + i) = -*(uint8_t *)(a + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -370,8 +316,8 @@ void HELPER(gvec_neg16)(void *d, void *a, uint32_t desc)
|
|||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec16)) {
|
||||
*(vec16 *)(d + i) = -*(vec16 *)(a + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(uint16_t *)(d + i) = -*(uint16_t *)(a + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -381,8 +327,8 @@ void HELPER(gvec_neg32)(void *d, void *a, uint32_t desc)
|
|||
intptr_t oprsz = simd_oprsz(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec32)) {
|
||||
*(vec32 *)(d + i) = -*(vec32 *)(a + i);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(uint32_t *)(d + i) = -*(uint32_t *)(a + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -392,8 +338,8 @@ void HELPER(gvec_neg64)(void *d, void *a, 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);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = -*(uint64_t *)(a + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -499,8 +445,8 @@ void HELPER(gvec_not)(void *d, void *a, 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);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = ~*(uint64_t *)(a + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -510,8 +456,8 @@ void HELPER(gvec_and)(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);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) & *(uint64_t *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -521,8 +467,8 @@ void HELPER(gvec_or)(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);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) | *(uint64_t *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -532,8 +478,8 @@ void HELPER(gvec_xor)(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);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) ^ *(uint64_t *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -543,8 +489,8 @@ void HELPER(gvec_andc)(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);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) &~ *(uint64_t *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -554,8 +500,8 @@ void HELPER(gvec_orc)(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);
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) |~ *(uint64_t *)(b + i);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -565,8 +511,8 @@ 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));
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = ~(*(uint64_t *)(a + i) & *(uint64_t *)(b + i));
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -576,8 +522,8 @@ 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));
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = ~(*(uint64_t *)(a + i) | *(uint64_t *)(b + i));
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -587,8 +533,8 @@ 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));
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = ~(*(uint64_t *)(a + i) ^ *(uint64_t *)(b + i));
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -596,11 +542,10 @@ void HELPER(gvec_eqv)(void *d, void *a, void *b, uint32_t desc)
|
|||
void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec64 vecb = (vec64)DUP2(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) & vecb;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) & b;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -608,11 +553,10 @@ void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc)
|
|||
void HELPER(gvec_xors)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec64 vecb = (vec64)DUP2(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) ^ vecb;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) ^ b;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -620,11 +564,10 @@ void HELPER(gvec_xors)(void *d, void *a, uint64_t b, uint32_t desc)
|
|||
void HELPER(gvec_ors)(void *d, void *a, uint64_t b, uint32_t desc)
|
||||
{
|
||||
intptr_t oprsz = simd_oprsz(desc);
|
||||
vec64 vecb = (vec64)DUP2(b);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) | vecb;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) | b;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -635,8 +578,8 @@ void HELPER(gvec_shl8i)(void *d, void *a, uint32_t desc)
|
|||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec8)) {
|
||||
*(vec8 *)(d + i) = *(vec8 *)(a + i) << shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) << shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -647,8 +590,8 @@ void HELPER(gvec_shl16i)(void *d, void *a, uint32_t desc)
|
|||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec16)) {
|
||||
*(vec16 *)(d + i) = *(vec16 *)(a + i) << shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) << shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -659,8 +602,8 @@ void HELPER(gvec_shl32i)(void *d, void *a, uint32_t desc)
|
|||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec32)) {
|
||||
*(vec32 *)(d + i) = *(vec32 *)(a + i) << shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) << shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -671,8 +614,8 @@ void HELPER(gvec_shl64i)(void *d, void *a, uint32_t desc)
|
|||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) << shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) << shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -683,8 +626,8 @@ void HELPER(gvec_shr8i)(void *d, void *a, uint32_t desc)
|
|||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec8)) {
|
||||
*(vec8 *)(d + i) = *(vec8 *)(a + i) >> shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) >> shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -695,8 +638,8 @@ void HELPER(gvec_shr16i)(void *d, void *a, uint32_t desc)
|
|||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec16)) {
|
||||
*(vec16 *)(d + i) = *(vec16 *)(a + i) >> shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) >> shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -707,8 +650,8 @@ void HELPER(gvec_shr32i)(void *d, void *a, uint32_t desc)
|
|||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec32)) {
|
||||
*(vec32 *)(d + i) = *(vec32 *)(a + i) >> shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) >> shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -719,8 +662,8 @@ void HELPER(gvec_shr64i)(void *d, void *a, uint32_t desc)
|
|||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(vec64 *)(d + i) = *(vec64 *)(a + i) >> shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) >> shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -731,8 +674,8 @@ void HELPER(gvec_sar8i)(void *d, void *a, uint32_t desc)
|
|||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec8)) {
|
||||
*(svec8 *)(d + i) = *(svec8 *)(a + i) >> shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
|
||||
*(int8_t *)(d + i) = *(int8_t *)(a + i) >> shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -743,8 +686,8 @@ void HELPER(gvec_sar16i)(void *d, void *a, uint32_t desc)
|
|||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec16)) {
|
||||
*(svec16 *)(d + i) = *(svec16 *)(a + i) >> shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
|
||||
*(int16_t *)(d + i) = *(int16_t *)(a + i) >> shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -755,8 +698,8 @@ void HELPER(gvec_sar32i)(void *d, void *a, uint32_t desc)
|
|||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec32)) {
|
||||
*(svec32 *)(d + i) = *(svec32 *)(a + i) >> shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
*(int32_t *)(d + i) = *(int32_t *)(a + i) >> shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -767,8 +710,8 @@ void HELPER(gvec_sar64i)(void *d, void *a, uint32_t desc)
|
|||
int shift = simd_data(desc);
|
||||
intptr_t i;
|
||||
|
||||
for (i = 0; i < oprsz; i += sizeof(vec64)) {
|
||||
*(svec64 *)(d + i) = *(svec64 *)(a + i) >> shift;
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
*(int64_t *)(d + i) = *(int64_t *)(a + i) >> shift;
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
@ -917,39 +860,30 @@ void HELPER(gvec_sar64v)(void *d, void *a, void *b, uint32_t desc)
|
|||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
||||
/* If vectors are enabled, the compiler fills in -1 for true.
|
||||
Otherwise, we must take care of this by hand. */
|
||||
#ifdef CONFIG_VECTOR16
|
||||
# define DO_CMP0(X) X
|
||||
#else
|
||||
# define DO_CMP0(X) -(X)
|
||||
#endif
|
||||
|
||||
#define DO_CMP1(NAME, TYPE, OP) \
|
||||
void HELPER(NAME)(void *d, void *a, void *b, uint32_t desc) \
|
||||
{ \
|
||||
intptr_t oprsz = simd_oprsz(desc); \
|
||||
intptr_t i; \
|
||||
for (i = 0; i < oprsz; i += sizeof(TYPE)) { \
|
||||
*(TYPE *)(d + i) = DO_CMP0(*(TYPE *)(a + i) OP *(TYPE *)(b + i)); \
|
||||
*(TYPE *)(d + i) = -(*(TYPE *)(a + i) OP *(TYPE *)(b + i)); \
|
||||
} \
|
||||
clear_high(d, oprsz, desc); \
|
||||
}
|
||||
|
||||
#define DO_CMP2(SZ) \
|
||||
DO_CMP1(gvec_eq##SZ, vec##SZ, ==) \
|
||||
DO_CMP1(gvec_ne##SZ, vec##SZ, !=) \
|
||||
DO_CMP1(gvec_lt##SZ, svec##SZ, <) \
|
||||
DO_CMP1(gvec_le##SZ, svec##SZ, <=) \
|
||||
DO_CMP1(gvec_ltu##SZ, vec##SZ, <) \
|
||||
DO_CMP1(gvec_leu##SZ, vec##SZ, <=)
|
||||
DO_CMP1(gvec_eq##SZ, uint##SZ##_t, ==) \
|
||||
DO_CMP1(gvec_ne##SZ, uint##SZ##_t, !=) \
|
||||
DO_CMP1(gvec_lt##SZ, int##SZ##_t, <) \
|
||||
DO_CMP1(gvec_le##SZ, int##SZ##_t, <=) \
|
||||
DO_CMP1(gvec_ltu##SZ, uint##SZ##_t, <) \
|
||||
DO_CMP1(gvec_leu##SZ, uint##SZ##_t, <=)
|
||||
|
||||
DO_CMP2(8)
|
||||
DO_CMP2(16)
|
||||
DO_CMP2(32)
|
||||
DO_CMP2(64)
|
||||
|
||||
#undef DO_CMP0
|
||||
#undef DO_CMP1
|
||||
#undef DO_CMP2
|
||||
|
||||
|
@ -1450,11 +1384,11 @@ 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);
|
||||
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 cc = *(uint64_t *)(c + i);
|
||||
*(uint64_t *)(d + i) = (bb & aa) | (cc & ~aa);
|
||||
}
|
||||
clear_high(d, oprsz, desc);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "exec/tb-lookup.h"
|
||||
#include "disas/disas.h"
|
||||
#include "exec/log.h"
|
||||
#include "tcg/tcg.h"
|
||||
|
||||
/* 32-bit helpers */
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qemu-common.h"
|
||||
|
||||
#define NO_CPU_IO_DEFS
|
||||
|
@ -25,7 +26,7 @@
|
|||
#include "trace.h"
|
||||
#include "disas/disas.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "tcg.h"
|
||||
#include "tcg/tcg.h"
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
#include "qemu.h"
|
||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
|
@ -891,43 +892,61 @@ static void page_lock_pair(PageDesc **ret_p1, tb_page_addr_t phys1,
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
/* Currently it is not recommended to allocate big chunks of data in
|
||||
user mode. It will change when a dedicated libc will be used. */
|
||||
/* ??? 64-bit hosts ought to have no problem mmaping data outside the
|
||||
region in which the guest needs to run. Revisit this. */
|
||||
#define USE_STATIC_CODE_GEN_BUFFER
|
||||
#endif
|
||||
|
||||
/* Minimum size of the code gen buffer. This number is randomly chosen,
|
||||
but not so small that we can't have a fair number of TB's live. */
|
||||
#define MIN_CODE_GEN_BUFFER_SIZE (1024u * 1024)
|
||||
#define MIN_CODE_GEN_BUFFER_SIZE (1 * MiB)
|
||||
|
||||
/* Maximum size of the code gen buffer we'd like to use. Unless otherwise
|
||||
indicated, this is constrained by the range of direct branches on the
|
||||
host cpu, as used by the TCG implementation of goto_tb. */
|
||||
#if defined(__x86_64__)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
|
||||
#elif defined(__sparc__)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
|
||||
#elif defined(__powerpc64__)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
|
||||
#elif defined(__powerpc__)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (32u * 1024 * 1024)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (32 * MiB)
|
||||
#elif defined(__aarch64__)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
|
||||
#elif defined(__s390x__)
|
||||
/* We have a +- 4GB range on the branches; leave some slop. */
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (3ul * 1024 * 1024 * 1024)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (3 * GiB)
|
||||
#elif defined(__mips__)
|
||||
/* We have a 256MB branch region, but leave room to make sure the
|
||||
main executable is also within that region. */
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (128ul * 1024 * 1024)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (128 * MiB)
|
||||
#else
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1)
|
||||
#endif
|
||||
|
||||
#define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (32u * 1024 * 1024)
|
||||
#if TCG_TARGET_REG_BITS == 32
|
||||
#define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (32 * MiB)
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/*
|
||||
* For user mode on smaller 32 bit systems we may run into trouble
|
||||
* allocating big chunks of data in the right place. On these systems
|
||||
* we utilise a static code generation buffer directly in the binary.
|
||||
*/
|
||||
#define USE_STATIC_CODE_GEN_BUFFER
|
||||
#endif
|
||||
#else /* TCG_TARGET_REG_BITS == 64 */
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/*
|
||||
* As user-mode emulation typically means running multiple instances
|
||||
* of the translator don't go too nuts with our default code gen
|
||||
* buffer lest we make things too hard for the OS.
|
||||
*/
|
||||
#define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (128 * MiB)
|
||||
#else
|
||||
/*
|
||||
* We expect most system emulation to run one or two guests per host.
|
||||
* Users running large scale system emulation may want to tweak their
|
||||
* runtime setup via the tb-size control on the command line.
|
||||
*/
|
||||
#define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (1 * GiB)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define DEFAULT_CODE_GEN_BUFFER_SIZE \
|
||||
(DEFAULT_CODE_GEN_BUFFER_SIZE_1 < MAX_CODE_GEN_BUFFER_SIZE \
|
||||
|
@ -937,15 +956,7 @@ static inline size_t size_code_gen_buffer(size_t tb_size)
|
|||
{
|
||||
/* Size the buffer. */
|
||||
if (tb_size == 0) {
|
||||
#ifdef USE_STATIC_CODE_GEN_BUFFER
|
||||
tb_size = DEFAULT_CODE_GEN_BUFFER_SIZE;
|
||||
#else
|
||||
/* ??? Needs adjustments. */
|
||||
/* ??? If we relax the requirement that CONFIG_USER_ONLY use the
|
||||
static buffer, we could size this on RESERVED_VA, on the text
|
||||
segment size of the executable, or continue to use the default. */
|
||||
tb_size = (unsigned long)(ram_size / 4);
|
||||
#endif
|
||||
}
|
||||
if (tb_size < MIN_CODE_GEN_BUFFER_SIZE) {
|
||||
tb_size = MIN_CODE_GEN_BUFFER_SIZE;
|
||||
|
@ -1032,47 +1043,20 @@ static inline void *alloc_code_gen_buffer(void)
|
|||
{
|
||||
int prot = PROT_WRITE | PROT_READ | PROT_EXEC;
|
||||
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
|
||||
uintptr_t start = 0;
|
||||
size_t size = tcg_ctx->code_gen_buffer_size;
|
||||
void *buf;
|
||||
|
||||
/* Constrain the position of the buffer based on the host cpu.
|
||||
Note that these addresses are chosen in concert with the
|
||||
addresses assigned in the relevant linker script file. */
|
||||
# if defined(__PIE__) || defined(__PIC__)
|
||||
/* Don't bother setting a preferred location if we're building
|
||||
a position-independent executable. We're more likely to get
|
||||
an address near the main executable if we let the kernel
|
||||
choose the address. */
|
||||
# elif defined(__x86_64__) && defined(MAP_32BIT)
|
||||
/* Force the memory down into low memory with the executable.
|
||||
Leave the choice of exact location with the kernel. */
|
||||
flags |= MAP_32BIT;
|
||||
/* Cannot expect to map more than 800MB in low memory. */
|
||||
if (size > 800u * 1024 * 1024) {
|
||||
tcg_ctx->code_gen_buffer_size = size = 800u * 1024 * 1024;
|
||||
}
|
||||
# elif defined(__sparc__)
|
||||
start = 0x40000000ul;
|
||||
# elif defined(__s390x__)
|
||||
start = 0x90000000ul;
|
||||
# elif defined(__mips__)
|
||||
# if _MIPS_SIM == _ABI64
|
||||
start = 0x128000000ul;
|
||||
# else
|
||||
start = 0x08000000ul;
|
||||
# endif
|
||||
# endif
|
||||
|
||||
buf = mmap((void *)start, size, prot, flags, -1, 0);
|
||||
buf = mmap(NULL, size, prot, flags, -1, 0);
|
||||
if (buf == MAP_FAILED) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef __mips__
|
||||
if (cross_256mb(buf, size)) {
|
||||
/* Try again, with the original still mapped, to avoid re-acquiring
|
||||
that 256mb crossing. This time don't specify an address. */
|
||||
/*
|
||||
* Try again, with the original still mapped, to avoid re-acquiring
|
||||
* the same 256mb crossing.
|
||||
*/
|
||||
size_t size2;
|
||||
void *buf2 = mmap(NULL, size, prot, flags, -1, 0);
|
||||
switch ((int)(buf2 != MAP_FAILED)) {
|
||||
|
@ -1804,7 +1788,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
|||
#ifdef DEBUG_DISAS
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM) &&
|
||||
qemu_log_in_addr_range(tb->pc)) {
|
||||
qemu_log_lock();
|
||||
FILE *logfile = qemu_log_lock();
|
||||
qemu_log("OUT: [size=%d]\n", gen_code_size);
|
||||
if (tcg_ctx->data_gen_ptr) {
|
||||
size_t code_size = tcg_ctx->data_gen_ptr - tb->tc.ptr;
|
||||
|
@ -1829,7 +1813,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
|||
}
|
||||
qemu_log("\n");
|
||||
qemu_log_flush();
|
||||
qemu_log_unlock();
|
||||
qemu_log_unlock(logfile);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -138,11 +138,11 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
|||
#ifdef DEBUG_DISAS
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
|
||||
&& qemu_log_in_addr_range(db->pc_first)) {
|
||||
qemu_log_lock();
|
||||
FILE *logfile = qemu_log_lock();
|
||||
qemu_log("----------------\n");
|
||||
ops->disas_log(db, cpu);
|
||||
qemu_log("\n");
|
||||
qemu_log_unlock();
|
||||
qemu_log_unlock(logfile);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -20,12 +20,14 @@
|
|||
#include "cpu.h"
|
||||
#include "disas/disas.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "tcg.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "translate-all.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "qemu/atomic128.h"
|
||||
#include "trace-root.h"
|
||||
#include "trace/mem.h"
|
||||
|
||||
#undef EAX
|
||||
#undef ECX
|
||||
|
@ -734,6 +736,240 @@ int cpu_signal_handler(int host_signum, void *pinfo,
|
|||
|
||||
/* The softmmu versions of these helpers are in cputlb.c. */
|
||||
|
||||
uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint32_t ret;
|
||||
uint16_t meminfo = trace_mem_get_info(MO_UB, MMU_USER_IDX, false);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
ret = ldub_p(g2h(ptr));
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cpu_ldsb_data(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
int ret;
|
||||
uint16_t meminfo = trace_mem_get_info(MO_SB, MMU_USER_IDX, false);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
ret = ldsb_p(g2h(ptr));
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_lduw_data(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint32_t ret;
|
||||
uint16_t meminfo = trace_mem_get_info(MO_TEUW, MMU_USER_IDX, false);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
ret = lduw_p(g2h(ptr));
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cpu_ldsw_data(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
int ret;
|
||||
uint16_t meminfo = trace_mem_get_info(MO_TESW, MMU_USER_IDX, false);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
ret = ldsw_p(g2h(ptr));
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_data(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint32_t ret;
|
||||
uint16_t meminfo = trace_mem_get_info(MO_TEUL, MMU_USER_IDX, false);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
ret = ldl_p(g2h(ptr));
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_data(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint64_t ret;
|
||||
uint16_t meminfo = trace_mem_get_info(MO_TEQ, MMU_USER_IDX, false);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
ret = ldq_p(g2h(ptr));
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_ldub_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
set_helper_retaddr(retaddr);
|
||||
ret = cpu_ldub_data(env, ptr);
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cpu_ldsb_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
set_helper_retaddr(retaddr);
|
||||
ret = cpu_ldsb_data(env, ptr);
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_lduw_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
set_helper_retaddr(retaddr);
|
||||
ret = cpu_lduw_data(env, ptr);
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cpu_ldsw_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
set_helper_retaddr(retaddr);
|
||||
ret = cpu_ldsw_data(env, ptr);
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
set_helper_retaddr(retaddr);
|
||||
ret = cpu_ldl_data(env, ptr);
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
|
||||
{
|
||||
uint64_t ret;
|
||||
|
||||
set_helper_retaddr(retaddr);
|
||||
ret = cpu_ldq_data(env, ptr);
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cpu_stb_data(CPUArchState *env, abi_ptr ptr, uint32_t val)
|
||||
{
|
||||
uint16_t meminfo = trace_mem_get_info(MO_UB, MMU_USER_IDX, true);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
stb_p(g2h(ptr), val);
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
}
|
||||
|
||||
void cpu_stw_data(CPUArchState *env, abi_ptr ptr, uint32_t val)
|
||||
{
|
||||
uint16_t meminfo = trace_mem_get_info(MO_TEUW, MMU_USER_IDX, true);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
stw_p(g2h(ptr), val);
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
}
|
||||
|
||||
void cpu_stl_data(CPUArchState *env, abi_ptr ptr, uint32_t val)
|
||||
{
|
||||
uint16_t meminfo = trace_mem_get_info(MO_TEUL, MMU_USER_IDX, true);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
stl_p(g2h(ptr), val);
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
}
|
||||
|
||||
void cpu_stq_data(CPUArchState *env, abi_ptr ptr, uint64_t val)
|
||||
{
|
||||
uint16_t meminfo = trace_mem_get_info(MO_TEQ, MMU_USER_IDX, true);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
|
||||
stq_p(g2h(ptr), val);
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
|
||||
}
|
||||
|
||||
void cpu_stb_data_ra(CPUArchState *env, abi_ptr ptr,
|
||||
uint32_t val, uintptr_t retaddr)
|
||||
{
|
||||
set_helper_retaddr(retaddr);
|
||||
cpu_stb_data(env, ptr, val);
|
||||
clear_helper_retaddr();
|
||||
}
|
||||
|
||||
void cpu_stw_data_ra(CPUArchState *env, abi_ptr ptr,
|
||||
uint32_t val, uintptr_t retaddr)
|
||||
{
|
||||
set_helper_retaddr(retaddr);
|
||||
cpu_stw_data(env, ptr, val);
|
||||
clear_helper_retaddr();
|
||||
}
|
||||
|
||||
void cpu_stl_data_ra(CPUArchState *env, abi_ptr ptr,
|
||||
uint32_t val, uintptr_t retaddr)
|
||||
{
|
||||
set_helper_retaddr(retaddr);
|
||||
cpu_stl_data(env, ptr, val);
|
||||
clear_helper_retaddr();
|
||||
}
|
||||
|
||||
void cpu_stq_data_ra(CPUArchState *env, abi_ptr ptr,
|
||||
uint64_t val, uintptr_t retaddr)
|
||||
{
|
||||
set_helper_retaddr(retaddr);
|
||||
cpu_stq_data(env, ptr, val);
|
||||
clear_helper_retaddr();
|
||||
}
|
||||
|
||||
uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
set_helper_retaddr(1);
|
||||
ret = ldub_p(g2h(ptr));
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
set_helper_retaddr(1);
|
||||
ret = lduw_p(g2h(ptr));
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
set_helper_retaddr(1);
|
||||
ret = ldl_p(g2h(ptr));
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint64_t ret;
|
||||
|
||||
set_helper_retaddr(1);
|
||||
ret = ldq_p(g2h(ptr));
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Do not allow unaligned operations to proceed. Return the host address. */
|
||||
static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
int size, uintptr_t retaddr)
|
||||
|
|
|
@ -77,6 +77,8 @@ int graphic_depth = 32;
|
|||
#define QEMU_ARCH QEMU_ARCH_PPC
|
||||
#elif defined(TARGET_RISCV)
|
||||
#define QEMU_ARCH QEMU_ARCH_RISCV
|
||||
#elif defined(TARGET_RX)
|
||||
#define QEMU_ARCH QEMU_ARCH_RX
|
||||
#elif defined(TARGET_S390X)
|
||||
#define QEMU_ARCH QEMU_ARCH_S390X
|
||||
#elif defined(TARGET_SH4)
|
||||
|
|
|
@ -307,6 +307,13 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
|
|||
return SND_PCM_FORMAT_U32_LE;
|
||||
}
|
||||
|
||||
case AUDIO_FORMAT_F32:
|
||||
if (endianness) {
|
||||
return SND_PCM_FORMAT_FLOAT_BE;
|
||||
} else {
|
||||
return SND_PCM_FORMAT_FLOAT_LE;
|
||||
}
|
||||
|
||||
default:
|
||||
dolog ("Internal logic error: Bad audio format %d\n", fmt);
|
||||
#ifdef DEBUG_AUDIO
|
||||
|
@ -370,6 +377,16 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt,
|
|||
*fmt = AUDIO_FORMAT_U32;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_FLOAT_LE:
|
||||
*endianness = 0;
|
||||
*fmt = AUDIO_FORMAT_F32;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_FLOAT_BE:
|
||||
*endianness = 1;
|
||||
*fmt = AUDIO_FORMAT_F32;
|
||||
break;
|
||||
|
||||
default:
|
||||
dolog ("Unrecognized audio format %d\n", alsafmt);
|
||||
return -1;
|
||||
|
@ -802,7 +819,7 @@ static size_t alsa_read(HWVoiceIn *hw, void *buf, size_t len)
|
|||
switch (nread) {
|
||||
case 0:
|
||||
trace_alsa_read_zero(len);
|
||||
return pos;;
|
||||
return pos;
|
||||
|
||||
case -EPIPE:
|
||||
if (alsa_recover(alsa->handle)) {
|
||||
|
@ -818,7 +835,7 @@ static size_t alsa_read(HWVoiceIn *hw, void *buf, size_t len)
|
|||
default:
|
||||
alsa_logerr(nread, "Failed to read %zu frames to %p\n",
|
||||
len, dst);
|
||||
return pos;;
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -906,6 +923,7 @@ static struct audio_pcm_ops alsa_pcm_ops = {
|
|||
.init_out = alsa_init_out,
|
||||
.fini_out = alsa_fini_out,
|
||||
.write = alsa_write,
|
||||
.run_buffer_out = audio_generic_run_buffer_out,
|
||||
.enable_out = alsa_enable_out,
|
||||
|
||||
.init_in = alsa_init_in,
|
||||
|
|
144
audio/audio.c
144
audio/audio.c
|
@ -218,6 +218,9 @@ static void audio_print_settings (struct audsettings *as)
|
|||
case AUDIO_FORMAT_U32:
|
||||
AUD_log (NULL, "U32");
|
||||
break;
|
||||
case AUDIO_FORMAT_F32:
|
||||
AUD_log (NULL, "F32");
|
||||
break;
|
||||
default:
|
||||
AUD_log (NULL, "invalid(%d)", as->fmt);
|
||||
break;
|
||||
|
@ -252,6 +255,7 @@ static int audio_validate_settings (struct audsettings *as)
|
|||
case AUDIO_FORMAT_U16:
|
||||
case AUDIO_FORMAT_S32:
|
||||
case AUDIO_FORMAT_U32:
|
||||
case AUDIO_FORMAT_F32:
|
||||
break;
|
||||
default:
|
||||
invalid = 1;
|
||||
|
@ -264,24 +268,28 @@ static int audio_validate_settings (struct audsettings *as)
|
|||
|
||||
static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *as)
|
||||
{
|
||||
int bits = 8, sign = 0;
|
||||
int bits = 8;
|
||||
bool is_signed = false, is_float = false;
|
||||
|
||||
switch (as->fmt) {
|
||||
case AUDIO_FORMAT_S8:
|
||||
sign = 1;
|
||||
is_signed = true;
|
||||
/* fall through */
|
||||
case AUDIO_FORMAT_U8:
|
||||
break;
|
||||
|
||||
case AUDIO_FORMAT_S16:
|
||||
sign = 1;
|
||||
is_signed = true;
|
||||
/* fall through */
|
||||
case AUDIO_FORMAT_U16:
|
||||
bits = 16;
|
||||
break;
|
||||
|
||||
case AUDIO_FORMAT_F32:
|
||||
is_float = true;
|
||||
/* fall through */
|
||||
case AUDIO_FORMAT_S32:
|
||||
sign = 1;
|
||||
is_signed = true;
|
||||
/* fall through */
|
||||
case AUDIO_FORMAT_U32:
|
||||
bits = 32;
|
||||
|
@ -292,32 +300,38 @@ static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *a
|
|||
}
|
||||
return info->freq == as->freq
|
||||
&& info->nchannels == as->nchannels
|
||||
&& info->sign == sign
|
||||
&& info->is_signed == is_signed
|
||||
&& info->is_float == is_float
|
||||
&& info->bits == bits
|
||||
&& info->swap_endianness == (as->endianness != AUDIO_HOST_ENDIANNESS);
|
||||
}
|
||||
|
||||
void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
|
||||
{
|
||||
int bits = 8, sign = 0, mul;
|
||||
int bits = 8, mul;
|
||||
bool is_signed = false, is_float = false;
|
||||
|
||||
switch (as->fmt) {
|
||||
case AUDIO_FORMAT_S8:
|
||||
sign = 1;
|
||||
is_signed = true;
|
||||
/* fall through */
|
||||
case AUDIO_FORMAT_U8:
|
||||
mul = 1;
|
||||
break;
|
||||
|
||||
case AUDIO_FORMAT_S16:
|
||||
sign = 1;
|
||||
is_signed = true;
|
||||
/* fall through */
|
||||
case AUDIO_FORMAT_U16:
|
||||
bits = 16;
|
||||
mul = 2;
|
||||
break;
|
||||
|
||||
case AUDIO_FORMAT_F32:
|
||||
is_float = true;
|
||||
/* fall through */
|
||||
case AUDIO_FORMAT_S32:
|
||||
sign = 1;
|
||||
is_signed = true;
|
||||
/* fall through */
|
||||
case AUDIO_FORMAT_U32:
|
||||
bits = 32;
|
||||
|
@ -330,7 +344,8 @@ void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
|
|||
|
||||
info->freq = as->freq;
|
||||
info->bits = bits;
|
||||
info->sign = sign;
|
||||
info->is_signed = is_signed;
|
||||
info->is_float = is_float;
|
||||
info->nchannels = as->nchannels;
|
||||
info->bytes_per_frame = as->nchannels * mul;
|
||||
info->bytes_per_second = info->freq * info->bytes_per_frame;
|
||||
|
@ -343,7 +358,7 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
|
|||
return;
|
||||
}
|
||||
|
||||
if (info->sign) {
|
||||
if (info->is_signed || info->is_float) {
|
||||
memset(buf, 0x00, len * info->bytes_per_frame);
|
||||
}
|
||||
else {
|
||||
|
@ -769,8 +784,9 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
|
|||
#ifdef DEBUG_AUDIO
|
||||
static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
|
||||
{
|
||||
dolog ("%s: bits %d, sign %d, freq %d, nchan %d\n",
|
||||
cap, info->bits, info->sign, info->freq, info->nchannels);
|
||||
dolog("%s: bits %d, sign %d, float %d, freq %d, nchan %d\n",
|
||||
cap, info->bits, info->is_signed, info->is_float, info->freq,
|
||||
info->nchannels);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -878,9 +894,9 @@ size_t AUD_read(SWVoiceIn *sw, void *buf, size_t size)
|
|||
}
|
||||
}
|
||||
|
||||
int AUD_get_buffer_size_out (SWVoiceOut *sw)
|
||||
int AUD_get_buffer_size_out(SWVoiceOut *sw)
|
||||
{
|
||||
return sw->hw->mix_buf->size * sw->hw->info.bytes_per_frame;
|
||||
return sw->hw->samples * sw->hw->info.bytes_per_frame;
|
||||
}
|
||||
|
||||
void AUD_set_active_out (SWVoiceOut *sw, int on)
|
||||
|
@ -1075,10 +1091,8 @@ static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live)
|
|||
while (live) {
|
||||
size_t size, decr, proc;
|
||||
void *buf = hw->pcm_ops->get_buffer_out(hw, &size);
|
||||
if (!buf) {
|
||||
/* retrying will likely won't help, drop everything. */
|
||||
hw->mix_buf->pos = (hw->mix_buf->pos + live) % hw->mix_buf->size;
|
||||
return clipped + live;
|
||||
if (!buf || size == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
decr = MIN(size / hw->info.bytes_per_frame, live);
|
||||
|
@ -1096,6 +1110,10 @@ static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live)
|
|||
}
|
||||
}
|
||||
|
||||
if (hw->pcm_ops->run_buffer_out) {
|
||||
hw->pcm_ops->run_buffer_out(hw);
|
||||
}
|
||||
|
||||
return clipped;
|
||||
}
|
||||
|
||||
|
@ -1402,7 +1420,8 @@ void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size)
|
|||
}
|
||||
assert(start >= 0 && start < hw->size_emul);
|
||||
|
||||
*size = MIN(hw->pending_emul, hw->size_emul - start);
|
||||
*size = MIN(*size, hw->pending_emul);
|
||||
*size = MIN(*size, hw->size_emul - start);
|
||||
return hw->buf_emul + start;
|
||||
}
|
||||
|
||||
|
@ -1412,6 +1431,28 @@ void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
|
|||
hw->pending_emul -= size;
|
||||
}
|
||||
|
||||
void audio_generic_run_buffer_out(HWVoiceOut *hw)
|
||||
{
|
||||
while (hw->pending_emul) {
|
||||
size_t write_len, written;
|
||||
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
|
||||
|
||||
if (start < 0) {
|
||||
start += hw->size_emul;
|
||||
}
|
||||
assert(start >= 0 && start < hw->size_emul);
|
||||
|
||||
write_len = MIN(hw->pending_emul, hw->size_emul - start);
|
||||
|
||||
written = hw->pcm_ops->write(hw, hw->buf_emul + start, write_len);
|
||||
hw->pending_emul -= written;
|
||||
|
||||
if (written < write_len) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
||||
{
|
||||
if (unlikely(!hw->buf_emul)) {
|
||||
|
@ -1427,8 +1468,7 @@ void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
|||
return hw->buf_emul + hw->pos_emul;
|
||||
}
|
||||
|
||||
size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf,
|
||||
size_t size)
|
||||
size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
|
||||
{
|
||||
assert(buf == hw->buf_emul + hw->pos_emul &&
|
||||
size + hw->pending_emul <= hw->size_emul);
|
||||
|
@ -1439,35 +1479,6 @@ size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf,
|
|||
return size;
|
||||
}
|
||||
|
||||
size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
|
||||
{
|
||||
audio_generic_put_buffer_out_nowrite(hw, buf, size);
|
||||
|
||||
while (hw->pending_emul) {
|
||||
size_t write_len, written;
|
||||
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
|
||||
if (start < 0) {
|
||||
start += hw->size_emul;
|
||||
}
|
||||
assert(start >= 0 && start < hw->size_emul);
|
||||
|
||||
write_len = MIN(hw->pending_emul, hw->size_emul - start);
|
||||
|
||||
written = hw->pcm_ops->write(hw, hw->buf_emul + start, write_len);
|
||||
hw->pending_emul -= written;
|
||||
|
||||
if (written < write_len) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* fake we have written everything. non-written data remain in pending_emul,
|
||||
* so we do not have to clip them multiple times
|
||||
*/
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size)
|
||||
{
|
||||
size_t dst_size, copy_size;
|
||||
|
@ -1475,21 +1486,19 @@ size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size)
|
|||
copy_size = MIN(size, dst_size);
|
||||
|
||||
memcpy(dst, buf, copy_size);
|
||||
return hw->pcm_ops->put_buffer_out(hw, buf, copy_size);
|
||||
return hw->pcm_ops->put_buffer_out(hw, dst, copy_size);
|
||||
}
|
||||
|
||||
size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size)
|
||||
{
|
||||
size_t dst_size, copy_size;
|
||||
void *dst = hw->pcm_ops->get_buffer_in(hw, &dst_size);
|
||||
copy_size = MIN(size, dst_size);
|
||||
void *src = hw->pcm_ops->get_buffer_in(hw, &size);
|
||||
|
||||
memcpy(dst, buf, copy_size);
|
||||
hw->pcm_ops->put_buffer_in(hw, buf, copy_size);
|
||||
return copy_size;
|
||||
memcpy(buf, src, size);
|
||||
hw->pcm_ops->put_buffer_in(hw, src, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
static int audio_driver_init(AudioState *s, struct audio_driver *drv,
|
||||
bool msg, Audiodev *dev)
|
||||
{
|
||||
|
@ -1738,7 +1747,7 @@ static AudioState *audio_init(Audiodev *dev, const char *name)
|
|||
if (dev->timer_period <= 0) {
|
||||
s->period_ticks = 1;
|
||||
} else {
|
||||
s->period_ticks = dev->timer_period * SCALE_US;
|
||||
s->period_ticks = dev->timer_period * (int64_t)SCALE_US;
|
||||
}
|
||||
|
||||
e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s);
|
||||
|
@ -1836,11 +1845,15 @@ CaptureVoiceOut *AUD_add_capture(
|
|||
|
||||
cap->buf = g_malloc0_n(hw->mix_buf->size, hw->info.bytes_per_frame);
|
||||
|
||||
hw->clip = mixeng_clip
|
||||
[hw->info.nchannels == 2]
|
||||
[hw->info.sign]
|
||||
[hw->info.swap_endianness]
|
||||
[audio_bits_to_index (hw->info.bits)];
|
||||
if (hw->info.is_float) {
|
||||
hw->clip = mixeng_clip_float[hw->info.nchannels == 2];
|
||||
} else {
|
||||
hw->clip = mixeng_clip
|
||||
[hw->info.nchannels == 2]
|
||||
[hw->info.is_signed]
|
||||
[hw->info.swap_endianness]
|
||||
[audio_bits_to_index(hw->info.bits)];
|
||||
}
|
||||
|
||||
QLIST_INSERT_HEAD (&s->cap_head, cap, entries);
|
||||
QLIST_INSERT_HEAD (&cap->cb_head, cb, entries);
|
||||
|
@ -2079,6 +2092,7 @@ int audioformat_bytes_per_sample(AudioFormat fmt)
|
|||
|
||||
case AUDIO_FORMAT_U32:
|
||||
case AUDIO_FORMAT_S32:
|
||||
case AUDIO_FORMAT_F32:
|
||||
return 4;
|
||||
|
||||
case AUDIO_FORMAT__MAX:
|
||||
|
|
|
@ -40,7 +40,8 @@ struct audio_callback {
|
|||
|
||||
struct audio_pcm_info {
|
||||
int bits;
|
||||
int sign;
|
||||
bool is_signed;
|
||||
bool is_float;
|
||||
int freq;
|
||||
int nchannels;
|
||||
int bytes_per_frame;
|
||||
|
@ -152,6 +153,7 @@ struct audio_pcm_ops {
|
|||
int (*init_out)(HWVoiceOut *hw, audsettings *as, void *drv_opaque);
|
||||
void (*fini_out)(HWVoiceOut *hw);
|
||||
size_t (*write) (HWVoiceOut *hw, void *buf, size_t size);
|
||||
void (*run_buffer_out)(HWVoiceOut *hw);
|
||||
/*
|
||||
* get a buffer that after later can be passed to put_buffer_out; optional
|
||||
* returns the buffer, and writes it's size to size (in bytes)
|
||||
|
@ -178,10 +180,9 @@ struct audio_pcm_ops {
|
|||
|
||||
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
|
||||
void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
|
||||
void audio_generic_run_buffer_out(HWVoiceOut *hw);
|
||||
void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size);
|
||||
size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size);
|
||||
size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf,
|
||||
size_t size);
|
||||
size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size);
|
||||
size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size);
|
||||
|
||||
|
|
|
@ -153,15 +153,23 @@ static int glue (audio_pcm_sw_init_, TYPE) (
|
|||
sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq;
|
||||
#endif
|
||||
|
||||
if (sw->info.is_float) {
|
||||
#ifdef DAC
|
||||
sw->conv = mixeng_conv
|
||||
sw->conv = mixeng_conv_float[sw->info.nchannels == 2];
|
||||
#else
|
||||
sw->clip = mixeng_clip
|
||||
sw->clip = mixeng_clip_float[sw->info.nchannels == 2];
|
||||
#endif
|
||||
[sw->info.nchannels == 2]
|
||||
[sw->info.sign]
|
||||
[sw->info.swap_endianness]
|
||||
[audio_bits_to_index (sw->info.bits)];
|
||||
} else {
|
||||
#ifdef DAC
|
||||
sw->conv = mixeng_conv
|
||||
#else
|
||||
sw->clip = mixeng_clip
|
||||
#endif
|
||||
[sw->info.nchannels == 2]
|
||||
[sw->info.is_signed]
|
||||
[sw->info.swap_endianness]
|
||||
[audio_bits_to_index(sw->info.bits)];
|
||||
}
|
||||
|
||||
sw->name = g_strdup (name);
|
||||
err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw);
|
||||
|
@ -276,15 +284,23 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
|
|||
goto err1;
|
||||
}
|
||||
|
||||
if (hw->info.is_float) {
|
||||
#ifdef DAC
|
||||
hw->clip = mixeng_clip
|
||||
hw->clip = mixeng_clip_float[hw->info.nchannels == 2];
|
||||
#else
|
||||
hw->conv = mixeng_conv
|
||||
hw->conv = mixeng_conv_float[hw->info.nchannels == 2];
|
||||
#endif
|
||||
[hw->info.nchannels == 2]
|
||||
[hw->info.sign]
|
||||
[hw->info.swap_endianness]
|
||||
[audio_bits_to_index (hw->info.bits)];
|
||||
} else {
|
||||
#ifdef DAC
|
||||
hw->clip = mixeng_clip
|
||||
#else
|
||||
hw->conv = mixeng_conv
|
||||
#endif
|
||||
[hw->info.nchannels == 2]
|
||||
[hw->info.is_signed]
|
||||
[hw->info.swap_endianness]
|
||||
[audio_bits_to_index(hw->info.bits)];
|
||||
}
|
||||
|
||||
glue(audio_pcm_hw_alloc_resources_, TYPE)(hw);
|
||||
|
||||
|
|
|
@ -411,7 +411,7 @@ static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
|
|||
}
|
||||
COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
|
||||
(hw, size))
|
||||
COREAUDIO_WRAPPER_FUNC(put_buffer_out_nowrite, size_t,
|
||||
COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
|
||||
(HWVoiceOut *hw, void *buf, size_t size),
|
||||
(hw, buf, size))
|
||||
COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
|
||||
|
@ -471,20 +471,6 @@ static OSStatus audioDeviceIOProc(
|
|||
return 0;
|
||||
}
|
||||
|
||||
static UInt32 coreaudio_get_flags(struct audio_pcm_info *info,
|
||||
struct audsettings *as)
|
||||
{
|
||||
UInt32 flags = info->sign ? kAudioFormatFlagIsSignedInteger : 0;
|
||||
if (as->endianness) { /* 0 = little, 1 = big */
|
||||
flags |= kAudioFormatFlagIsBigEndian;
|
||||
}
|
||||
|
||||
if (flags == 0) { /* must not be 0 */
|
||||
flags = kAudioFormatFlagsAreAllClear;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
void *drv_opaque)
|
||||
{
|
||||
|
@ -496,6 +482,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
|||
Audiodev *dev = drv_opaque;
|
||||
AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
|
||||
int frames;
|
||||
struct audsettings fake_as;
|
||||
|
||||
/* create mutex */
|
||||
err = pthread_mutex_init(&core->mutex, NULL);
|
||||
|
@ -504,6 +491,9 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
|||
return -1;
|
||||
}
|
||||
|
||||
fake_as = *as;
|
||||
as = &fake_as;
|
||||
as->fmt = AUDIO_FORMAT_F32;
|
||||
audio_pcm_init_info (&hw->info, as);
|
||||
|
||||
status = coreaudio_get_voice(&core->outputDeviceID);
|
||||
|
@ -572,15 +562,6 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
|||
|
||||
/* set Samplerate */
|
||||
core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
|
||||
core->outputStreamBasicDescription.mFormatID = kAudioFormatLinearPCM;
|
||||
core->outputStreamBasicDescription.mFormatFlags =
|
||||
coreaudio_get_flags(&hw->info, as);
|
||||
core->outputStreamBasicDescription.mBytesPerPacket =
|
||||
core->outputStreamBasicDescription.mBytesPerFrame =
|
||||
hw->info.nchannels * hw->info.bits / 8;
|
||||
core->outputStreamBasicDescription.mFramesPerPacket = 1;
|
||||
core->outputStreamBasicDescription.mChannelsPerFrame = hw->info.nchannels;
|
||||
core->outputStreamBasicDescription.mBitsPerChannel = hw->info.bits;
|
||||
|
||||
status = coreaudio_set_streamformat(core->outputDeviceID,
|
||||
&core->outputStreamBasicDescription);
|
||||
|
@ -687,9 +668,12 @@ static void coreaudio_audio_fini (void *opaque)
|
|||
static struct audio_pcm_ops coreaudio_pcm_ops = {
|
||||
.init_out = coreaudio_init_out,
|
||||
.fini_out = coreaudio_fini_out,
|
||||
/* wrapper for audio_generic_write */
|
||||
.write = coreaudio_write,
|
||||
/* wrapper for audio_generic_get_buffer_out */
|
||||
.get_buffer_out = coreaudio_get_buffer_out,
|
||||
.put_buffer_out = coreaudio_put_buffer_out_nowrite,
|
||||
/* wrapper for audio_generic_put_buffer_out */
|
||||
.put_buffer_out = coreaudio_put_buffer_out,
|
||||
.enable_out = coreaudio_enable_out
|
||||
};
|
||||
|
||||
|
|
|
@ -244,6 +244,7 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
|
|||
goto fail0;
|
||||
}
|
||||
|
||||
ds->first_time = true;
|
||||
obt_as.endianness = 0;
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
|
||||
|
|
|
@ -53,12 +53,14 @@ typedef struct {
|
|||
typedef struct {
|
||||
HWVoiceOut hw;
|
||||
LPDIRECTSOUNDBUFFER dsound_buffer;
|
||||
bool first_time;
|
||||
dsound *s;
|
||||
} DSoundVoiceOut;
|
||||
|
||||
typedef struct {
|
||||
HWVoiceIn hw;
|
||||
LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
|
||||
bool first_time;
|
||||
dsound *s;
|
||||
} DSoundVoiceIn;
|
||||
|
||||
|
@ -277,7 +279,7 @@ static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp,
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (*statusp & DSERR_BUFFERLOST) {
|
||||
if (*statusp & DSBSTATUS_BUFFERLOST) {
|
||||
dsound_restore_out(dsb, s);
|
||||
return -1;
|
||||
}
|
||||
|
@ -414,21 +416,32 @@ static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
|||
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
|
||||
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
|
||||
HRESULT hr;
|
||||
DWORD ppos, act_size;
|
||||
DWORD ppos, wpos, act_size;
|
||||
size_t req_size;
|
||||
int err;
|
||||
void *ret;
|
||||
|
||||
hr = IDirectSoundBuffer_GetCurrentPosition(dsb, &ppos, NULL);
|
||||
hr = IDirectSoundBuffer_GetCurrentPosition(
|
||||
dsb, &ppos, ds->first_time ? &wpos : NULL);
|
||||
if (FAILED(hr)) {
|
||||
dsound_logerr(hr, "Could not get playback buffer position\n");
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ds->first_time) {
|
||||
hw->pos_emul = wpos;
|
||||
ds->first_time = false;
|
||||
}
|
||||
|
||||
req_size = audio_ring_dist(ppos, hw->pos_emul, hw->size_emul);
|
||||
req_size = MIN(req_size, hw->size_emul - hw->pos_emul);
|
||||
|
||||
if (req_size == 0) {
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
|
||||
&act_size, NULL, false, ds->s);
|
||||
if (err) {
|
||||
|
@ -508,20 +521,31 @@ static void *dsound_get_buffer_in(HWVoiceIn *hw, size_t *size)
|
|||
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
|
||||
LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
|
||||
HRESULT hr;
|
||||
DWORD cpos, act_size;
|
||||
DWORD cpos, rpos, act_size;
|
||||
size_t req_size;
|
||||
int err;
|
||||
void *ret;
|
||||
|
||||
hr = IDirectSoundCaptureBuffer_GetCurrentPosition(dscb, &cpos, NULL);
|
||||
hr = IDirectSoundCaptureBuffer_GetCurrentPosition(
|
||||
dscb, &cpos, ds->first_time ? &rpos : NULL);
|
||||
if (FAILED(hr)) {
|
||||
dsound_logerr(hr, "Could not get capture buffer position\n");
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ds->first_time) {
|
||||
hw->pos_emul = rpos;
|
||||
ds->first_time = false;
|
||||
}
|
||||
|
||||
req_size = audio_ring_dist(cpos, hw->pos_emul, hw->size_emul);
|
||||
req_size = MIN(req_size, hw->size_emul - hw->pos_emul);
|
||||
req_size = MIN(*size, MIN(req_size, hw->size_emul - hw->pos_emul));
|
||||
|
||||
if (req_size == 0) {
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
err = dsound_lock_in(dscb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
|
||||
&act_size, NULL, false, ds->s);
|
||||
|
|
|
@ -267,6 +267,76 @@ f_sample *mixeng_clip[2][2][2][3] = {
|
|||
}
|
||||
};
|
||||
|
||||
#ifdef FLOAT_MIXENG
|
||||
#define CONV_NATURAL_FLOAT(x) (x)
|
||||
#define CLIP_NATURAL_FLOAT(x) (x)
|
||||
#else
|
||||
static const float float_scale = UINT_MAX / 2.f;
|
||||
#define CONV_NATURAL_FLOAT(x) ((x) * float_scale)
|
||||
|
||||
#ifdef RECIPROCAL
|
||||
static const float float_scale_reciprocal = 2.f / UINT_MAX;
|
||||
#define CLIP_NATURAL_FLOAT(x) ((x) * float_scale_reciprocal)
|
||||
#else
|
||||
#define CLIP_NATURAL_FLOAT(x) ((x) / float_scale)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static void conv_natural_float_to_mono(struct st_sample *dst, const void *src,
|
||||
int samples)
|
||||
{
|
||||
float *in = (float *)src;
|
||||
|
||||
while (samples--) {
|
||||
dst->r = dst->l = CONV_NATURAL_FLOAT(*in++);
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
static void conv_natural_float_to_stereo(struct st_sample *dst, const void *src,
|
||||
int samples)
|
||||
{
|
||||
float *in = (float *)src;
|
||||
|
||||
while (samples--) {
|
||||
dst->l = CONV_NATURAL_FLOAT(*in++);
|
||||
dst->r = CONV_NATURAL_FLOAT(*in++);
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
t_sample *mixeng_conv_float[2] = {
|
||||
conv_natural_float_to_mono,
|
||||
conv_natural_float_to_stereo,
|
||||
};
|
||||
|
||||
static void clip_natural_float_from_mono(void *dst, const struct st_sample *src,
|
||||
int samples)
|
||||
{
|
||||
float *out = (float *)dst;
|
||||
|
||||
while (samples--) {
|
||||
*out++ = CLIP_NATURAL_FLOAT(src->l + src->r);
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
static void clip_natural_float_from_stereo(
|
||||
void *dst, const struct st_sample *src, int samples)
|
||||
{
|
||||
float *out = (float *)dst;
|
||||
|
||||
while (samples--) {
|
||||
*out++ = CLIP_NATURAL_FLOAT(src->l);
|
||||
*out++ = CLIP_NATURAL_FLOAT(src->r);
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
f_sample *mixeng_clip_float[2] = {
|
||||
clip_natural_float_from_mono,
|
||||
clip_natural_float_from_stereo,
|
||||
};
|
||||
|
||||
void audio_sample_to_uint64(void *samples, int pos,
|
||||
uint64_t *left, uint64_t *right)
|
||||
|
|
|
@ -38,9 +38,14 @@ typedef struct st_sample st_sample;
|
|||
typedef void (t_sample) (struct st_sample *dst, const void *src, int samples);
|
||||
typedef void (f_sample) (void *dst, const struct st_sample *src, int samples);
|
||||
|
||||
/* indices: [stereo][signed][swap endiannes][8, 16 or 32-bits] */
|
||||
extern t_sample *mixeng_conv[2][2][2][3];
|
||||
extern f_sample *mixeng_clip[2][2][2][3];
|
||||
|
||||
/* indices: [stereo] */
|
||||
extern t_sample *mixeng_conv_float[2];
|
||||
extern f_sample *mixeng_clip_float[2];
|
||||
|
||||
void *st_rate_start (int inrate, int outrate);
|
||||
void st_rate_flow(void *opaque, st_sample *ibuf, st_sample *obuf,
|
||||
size_t *isamp, size_t *osamp);
|
||||
|
|
|
@ -41,32 +41,31 @@ static inline mixeng_real glue (conv_, ET) (IN_T v)
|
|||
|
||||
#ifdef RECIPROCAL
|
||||
#ifdef SIGNED
|
||||
return nv * (1.f / (mixeng_real) (IN_MAX - IN_MIN));
|
||||
return nv * (2.f / ((mixeng_real)IN_MAX - IN_MIN));
|
||||
#else
|
||||
return (nv - HALF) * (1.f / (mixeng_real) IN_MAX);
|
||||
return (nv - HALF) * (2.f / (mixeng_real)IN_MAX);
|
||||
#endif
|
||||
#else /* !RECIPROCAL */
|
||||
#ifdef SIGNED
|
||||
return nv / (mixeng_real) ((mixeng_real) IN_MAX - IN_MIN);
|
||||
return nv / (((mixeng_real)IN_MAX - IN_MIN) / 2.f);
|
||||
#else
|
||||
return (nv - HALF) / (mixeng_real) IN_MAX;
|
||||
return (nv - HALF) / ((mixeng_real)IN_MAX / 2.f);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline IN_T glue (clip_, ET) (mixeng_real v)
|
||||
{
|
||||
if (v >= 0.5) {
|
||||
if (v >= 1.f) {
|
||||
return IN_MAX;
|
||||
}
|
||||
else if (v < -0.5) {
|
||||
} else if (v < -1.f) {
|
||||
return IN_MIN;
|
||||
}
|
||||
|
||||
#ifdef SIGNED
|
||||
return ENDIAN_CONVERT ((IN_T) (v * ((mixeng_real) IN_MAX - IN_MIN)));
|
||||
return ENDIAN_CONVERT((IN_T)(v * (((mixeng_real)IN_MAX - IN_MIN) / 2.f)));
|
||||
#else
|
||||
return ENDIAN_CONVERT ((IN_T) ((v * IN_MAX) + HALF));
|
||||
return ENDIAN_CONVERT((IN_T)((v * ((mixeng_real)IN_MAX / 2.f)) + HALF));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -84,10 +83,9 @@ static inline int64_t glue (conv_, ET) (IN_T v)
|
|||
|
||||
static inline IN_T glue (clip_, ET) (int64_t v)
|
||||
{
|
||||
if (v >= 0x7f000000) {
|
||||
if (v >= 0x7fffffffLL) {
|
||||
return IN_MAX;
|
||||
}
|
||||
else if (v < -2147483648LL) {
|
||||
} else if (v < -2147483648LL) {
|
||||
return IN_MIN;
|
||||
}
|
||||
|
||||
|
|
|
@ -118,6 +118,7 @@ static struct audio_pcm_ops no_pcm_ops = {
|
|||
.init_out = no_init_out,
|
||||
.fini_out = no_fini_out,
|
||||
.write = no_write,
|
||||
.run_buffer_out = audio_generic_run_buffer_out,
|
||||
.enable_out = no_enable_out,
|
||||
|
||||
.init_in = no_init_in,
|
||||
|
|
|
@ -382,6 +382,15 @@ static size_t oss_get_available_bytes(OSSVoiceOut *oss)
|
|||
return audio_ring_dist(cntinfo.ptr, oss->hw.pos_emul, oss->hw.size_emul);
|
||||
}
|
||||
|
||||
static void oss_run_buffer_out(HWVoiceOut *hw)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *)hw;
|
||||
|
||||
if (!oss->mmapped) {
|
||||
audio_generic_run_buffer_out(hw);
|
||||
}
|
||||
}
|
||||
|
||||
static void *oss_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
|
@ -420,7 +429,7 @@ static size_t oss_write(HWVoiceOut *hw, void *buf, size_t len)
|
|||
size_t to_copy = MIN(len, hw->size_emul - hw->pos_emul);
|
||||
memcpy(hw->buf_emul + hw->pos_emul, buf, to_copy);
|
||||
|
||||
hw->pos_emul = (hw->pos_emul + to_copy) % hw->pos_emul;
|
||||
hw->pos_emul = (hw->pos_emul + to_copy) % hw->size_emul;
|
||||
buf += to_copy;
|
||||
len -= to_copy;
|
||||
}
|
||||
|
@ -570,20 +579,18 @@ static void oss_enable_out(HWVoiceOut *hw, bool enable)
|
|||
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
|
||||
|
||||
if (enable) {
|
||||
bool poll_mode = opdo->try_poll;
|
||||
hw->poll_mode = opdo->try_poll;
|
||||
|
||||
ldebug("enabling voice\n");
|
||||
if (poll_mode) {
|
||||
if (hw->poll_mode) {
|
||||
oss_poll_out(hw);
|
||||
poll_mode = 0;
|
||||
}
|
||||
hw->poll_mode = poll_mode;
|
||||
|
||||
if (!oss->mmapped) {
|
||||
return;
|
||||
}
|
||||
|
||||
audio_pcm_info_clear_buf(&hw->info, hw->buf_emul, hw->mix_buf->size);
|
||||
audio_pcm_info_clear_buf(&hw->info, hw->buf_emul, hw->samples);
|
||||
trig = PCM_ENABLE_OUTPUT;
|
||||
if (ioctl(oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
oss_logerr(errno,
|
||||
|
@ -699,17 +706,15 @@ static void oss_enable_in(HWVoiceIn *hw, bool enable)
|
|||
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
|
||||
|
||||
if (enable) {
|
||||
bool poll_mode = opdo->try_poll;
|
||||
hw->poll_mode = opdo->try_poll;
|
||||
|
||||
if (poll_mode) {
|
||||
if (hw->poll_mode) {
|
||||
oss_poll_in(hw);
|
||||
poll_mode = 0;
|
||||
}
|
||||
hw->poll_mode = poll_mode;
|
||||
} else {
|
||||
if (hw->poll_mode) {
|
||||
hw->poll_mode = 0;
|
||||
qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
|
||||
hw->poll_mode = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -748,6 +753,7 @@ static struct audio_pcm_ops oss_pcm_ops = {
|
|||
.init_out = oss_init_out,
|
||||
.fini_out = oss_fini_out,
|
||||
.write = oss_write,
|
||||
.run_buffer_out = oss_run_buffer_out,
|
||||
.get_buffer_out = oss_get_buffer_out,
|
||||
.put_buffer_out = oss_put_buffer_out,
|
||||
.enable_out = oss_enable_out,
|
||||
|
|
|
@ -32,7 +32,6 @@ typedef struct {
|
|||
HWVoiceOut hw;
|
||||
pa_stream *stream;
|
||||
paaudio *g;
|
||||
size_t samples;
|
||||
} PAVoiceOut;
|
||||
|
||||
typedef struct {
|
||||
|
@ -41,7 +40,6 @@ typedef struct {
|
|||
const void *read_data;
|
||||
size_t read_length;
|
||||
paaudio *g;
|
||||
size_t samples;
|
||||
} PAVoiceIn;
|
||||
|
||||
static void qpa_conn_fini(PAConnection *c);
|
||||
|
@ -156,34 +154,48 @@ static size_t qpa_read(HWVoiceIn *hw, void *data, size_t length)
|
|||
{
|
||||
PAVoiceIn *p = (PAVoiceIn *) hw;
|
||||
PAConnection *c = p->g->conn;
|
||||
size_t l;
|
||||
int r;
|
||||
size_t total = 0;
|
||||
|
||||
pa_threaded_mainloop_lock(c->mainloop);
|
||||
|
||||
CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
|
||||
"pa_threaded_mainloop_lock failed\n");
|
||||
|
||||
if (!p->read_length) {
|
||||
r = pa_stream_peek(p->stream, &p->read_data, &p->read_length);
|
||||
CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail,
|
||||
"pa_stream_peek failed\n");
|
||||
if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
|
||||
/* wait for stream to become ready */
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
l = MIN(p->read_length, length);
|
||||
memcpy(data, p->read_data, l);
|
||||
while (total < length) {
|
||||
size_t l;
|
||||
int r;
|
||||
|
||||
p->read_data += l;
|
||||
p->read_length -= l;
|
||||
if (!p->read_length) {
|
||||
r = pa_stream_peek(p->stream, &p->read_data, &p->read_length);
|
||||
CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail,
|
||||
"pa_stream_peek failed\n");
|
||||
if (!p->read_length) {
|
||||
/* buffer is empty */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!p->read_length) {
|
||||
r = pa_stream_drop(p->stream);
|
||||
CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail,
|
||||
"pa_stream_drop failed\n");
|
||||
l = MIN(p->read_length, length - total);
|
||||
memcpy((char *)data + total, p->read_data, l);
|
||||
|
||||
p->read_data += l;
|
||||
p->read_length -= l;
|
||||
total += l;
|
||||
|
||||
if (!p->read_length) {
|
||||
r = pa_stream_drop(p->stream);
|
||||
CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail,
|
||||
"pa_stream_drop failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
unlock:
|
||||
pa_threaded_mainloop_unlock(c->mainloop);
|
||||
return l;
|
||||
return total;
|
||||
|
||||
unlock_and_fail:
|
||||
pa_threaded_mainloop_unlock(c->mainloop);
|
||||
|
@ -265,6 +277,9 @@ static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
|
|||
case AUDIO_FORMAT_U32:
|
||||
format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
|
||||
break;
|
||||
case AUDIO_FORMAT_F32:
|
||||
format = endianness ? PA_SAMPLE_FLOAT32BE : PA_SAMPLE_FLOAT32LE;
|
||||
break;
|
||||
default:
|
||||
dolog ("Internal logic error: Bad audio format %d\n", afmt);
|
||||
format = PA_SAMPLE_U8;
|
||||
|
@ -290,6 +305,12 @@ static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
|
|||
case PA_SAMPLE_S32LE:
|
||||
*endianness = 0;
|
||||
return AUDIO_FORMAT_S32;
|
||||
case PA_SAMPLE_FLOAT32BE:
|
||||
*endianness = 1;
|
||||
return AUDIO_FORMAT_F32;
|
||||
case PA_SAMPLE_FLOAT32LE:
|
||||
*endianness = 0;
|
||||
return AUDIO_FORMAT_F32;
|
||||
default:
|
||||
dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
|
||||
return AUDIO_FORMAT_U8;
|
||||
|
@ -474,7 +495,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
|||
}
|
||||
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
hw->samples = pa->samples = audio_buffer_samples(
|
||||
hw->samples = audio_buffer_samples(
|
||||
qapi_AudiodevPaPerDirectionOptions_base(ppdo),
|
||||
&obt_as, ppdo->buffer_length);
|
||||
|
||||
|
@ -522,7 +543,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
|||
}
|
||||
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
hw->samples = pa->samples = audio_buffer_samples(
|
||||
hw->samples = audio_buffer_samples(
|
||||
qapi_AudiodevPaPerDirectionOptions_base(ppdo),
|
||||
&obt_as, ppdo->buffer_length);
|
||||
|
||||
|
@ -536,7 +557,6 @@ static void qpa_simple_disconnect(PAConnection *c, pa_stream *stream)
|
|||
{
|
||||
int err;
|
||||
|
||||
pa_threaded_mainloop_lock(c->mainloop);
|
||||
/*
|
||||
* wait until actually connects. workaround pa bug #247
|
||||
* https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/247
|
||||
|
@ -550,7 +570,6 @@ static void qpa_simple_disconnect(PAConnection *c, pa_stream *stream)
|
|||
dolog("Failed to disconnect! err=%d\n", err);
|
||||
}
|
||||
pa_stream_unref(stream);
|
||||
pa_threaded_mainloop_unlock(c->mainloop);
|
||||
}
|
||||
|
||||
static void qpa_fini_out (HWVoiceOut *hw)
|
||||
|
@ -558,8 +577,12 @@ static void qpa_fini_out (HWVoiceOut *hw)
|
|||
PAVoiceOut *pa = (PAVoiceOut *) hw;
|
||||
|
||||
if (pa->stream) {
|
||||
qpa_simple_disconnect(pa->g->conn, pa->stream);
|
||||
PAConnection *c = pa->g->conn;
|
||||
|
||||
pa_threaded_mainloop_lock(c->mainloop);
|
||||
qpa_simple_disconnect(c, pa->stream);
|
||||
pa->stream = NULL;
|
||||
pa_threaded_mainloop_unlock(c->mainloop);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -568,8 +591,20 @@ static void qpa_fini_in (HWVoiceIn *hw)
|
|||
PAVoiceIn *pa = (PAVoiceIn *) hw;
|
||||
|
||||
if (pa->stream) {
|
||||
qpa_simple_disconnect(pa->g->conn, pa->stream);
|
||||
PAConnection *c = pa->g->conn;
|
||||
|
||||
pa_threaded_mainloop_lock(c->mainloop);
|
||||
if (pa->read_length) {
|
||||
int r = pa_stream_drop(pa->stream);
|
||||
if (r) {
|
||||
qpa_logerr(pa_context_errno(c->context),
|
||||
"pa_stream_drop failed\n");
|
||||
}
|
||||
pa->read_length = 0;
|
||||
}
|
||||
qpa_simple_disconnect(c, pa->stream);
|
||||
pa->stream = NULL;
|
||||
pa_threaded_mainloop_unlock(c->mainloop);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -77,6 +77,14 @@ static int aud_to_sdlfmt (AudioFormat fmt)
|
|||
case AUDIO_FORMAT_U16:
|
||||
return AUDIO_U16LSB;
|
||||
|
||||
case AUDIO_FORMAT_S32:
|
||||
return AUDIO_S32LSB;
|
||||
|
||||
/* no unsigned 32-bit support in SDL */
|
||||
|
||||
case AUDIO_FORMAT_F32:
|
||||
return AUDIO_F32LSB;
|
||||
|
||||
default:
|
||||
dolog ("Internal logic error: Bad audio format %d\n", fmt);
|
||||
#ifdef DEBUG_AUDIO
|
||||
|
@ -119,6 +127,26 @@ static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness)
|
|||
*fmt = AUDIO_FORMAT_U16;
|
||||
break;
|
||||
|
||||
case AUDIO_S32LSB:
|
||||
*endianness = 0;
|
||||
*fmt = AUDIO_FORMAT_S32;
|
||||
break;
|
||||
|
||||
case AUDIO_S32MSB:
|
||||
*endianness = 1;
|
||||
*fmt = AUDIO_FORMAT_S32;
|
||||
break;
|
||||
|
||||
case AUDIO_F32LSB:
|
||||
*endianness = 0;
|
||||
*fmt = AUDIO_FORMAT_F32;
|
||||
break;
|
||||
|
||||
case AUDIO_F32MSB:
|
||||
*endianness = 1;
|
||||
*fmt = AUDIO_FORMAT_F32;
|
||||
break;
|
||||
|
||||
default:
|
||||
dolog ("Unrecognized SDL audio format %d\n", sdlfmt);
|
||||
return -1;
|
||||
|
@ -227,7 +255,7 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
|
|||
|
||||
SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
|
||||
(hw, size), *size = 0, sdl_unlock)
|
||||
SDL_WRAPPER_FUNC(put_buffer_out_nowrite, size_t,
|
||||
SDL_WRAPPER_FUNC(put_buffer_out, size_t,
|
||||
(HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size),
|
||||
/*nothing*/, sdl_unlock_and_post)
|
||||
SDL_WRAPPER_FUNC(write, size_t,
|
||||
|
@ -320,9 +348,12 @@ static void sdl_audio_fini (void *opaque)
|
|||
static struct audio_pcm_ops sdl_pcm_ops = {
|
||||
.init_out = sdl_init_out,
|
||||
.fini_out = sdl_fini_out,
|
||||
/* wrapper for audio_generic_write */
|
||||
.write = sdl_write,
|
||||
/* wrapper for audio_generic_get_buffer_out */
|
||||
.get_buffer_out = sdl_get_buffer_out,
|
||||
.put_buffer_out = sdl_put_buffer_out_nowrite,
|
||||
/* wrapper for audio_generic_put_buffer_out */
|
||||
.put_buffer_out = sdl_put_buffer_out,
|
||||
.enable_out = sdl_enable_out,
|
||||
};
|
||||
|
||||
|
|
|
@ -197,6 +197,7 @@ static struct audio_pcm_ops wav_pcm_ops = {
|
|||
.init_out = wav_init_out,
|
||||
.fini_out = wav_fini_out,
|
||||
.write = wav_write_out,
|
||||
.run_buffer_out = audio_generic_run_buffer_out,
|
||||
.enable_out = wav_enable_out,
|
||||
};
|
||||
|
||||
|
|
|
@ -239,7 +239,7 @@ qauthz_list_file_init(Object *obj)
|
|||
|
||||
authz->file_watch = -1;
|
||||
#ifdef CONFIG_INOTIFY1
|
||||
authz->refresh = TRUE;
|
||||
authz->refresh = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -17,3 +17,7 @@ endif
|
|||
common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_VIRTIO)) += vhost-user.o
|
||||
|
||||
common-obj-$(CONFIG_LINUX) += hostmem-memfd.o
|
||||
|
||||
common-obj-$(CONFIG_GIO) += dbus-vmstate.o
|
||||
dbus-vmstate.o-cflags = $(GIO_CFLAGS)
|
||||
dbus-vmstate.o-libs = $(GIO_LIBS)
|
||||
|
|
|
@ -152,7 +152,7 @@ cryptodev_vhost_claim_chardev(CryptoDevBackendVhostUser *s,
|
|||
return chr;
|
||||
}
|
||||
|
||||
static void cryptodev_vhost_user_event(void *opaque, int event)
|
||||
static void cryptodev_vhost_user_event(void *opaque, QEMUChrEvent event)
|
||||
{
|
||||
CryptoDevBackendVhostUser *s = opaque;
|
||||
CryptoDevBackend *b = CRYPTODEV_BACKEND(s);
|
||||
|
@ -171,6 +171,11 @@ static void cryptodev_vhost_user_event(void *opaque, int event)
|
|||
b->ready = false;
|
||||
cryptodev_vhost_user_stop(queues, s);
|
||||
break;
|
||||
case CHR_EVENT_BREAK:
|
||||
case CHR_EVENT_MUX_IN:
|
||||
case CHR_EVENT_MUX_OUT:
|
||||
/* Ignore */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -176,19 +176,10 @@ cryptodev_backend_complete(UserCreatable *uc, Error **errp)
|
|||
{
|
||||
CryptoDevBackend *backend = CRYPTODEV_BACKEND(uc);
|
||||
CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_GET_CLASS(uc);
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (bc->init) {
|
||||
bc->init(backend, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
bc->init(backend, errp);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
out:
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
void cryptodev_backend_set_used(CryptoDevBackend *backend, bool used)
|
||||
|
|
|
@ -0,0 +1,511 @@
|
|||
/*
|
||||
* QEMU dbus-vmstate
|
||||
*
|
||||
* Copyright (C) 2019 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 "qemu/units.h"
|
||||
#include "qemu/dbus.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "trace.h"
|
||||
|
||||
typedef struct DBusVMState DBusVMState;
|
||||
typedef struct DBusVMStateClass DBusVMStateClass;
|
||||
|
||||
#define TYPE_DBUS_VMSTATE "dbus-vmstate"
|
||||
#define DBUS_VMSTATE(obj) \
|
||||
OBJECT_CHECK(DBusVMState, (obj), TYPE_DBUS_VMSTATE)
|
||||
#define DBUS_VMSTATE_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(DBusVMStateClass, (obj), TYPE_DBUS_VMSTATE)
|
||||
#define DBUS_VMSTATE_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(DBusVMStateClass, (klass), TYPE_DBUS_VMSTATE)
|
||||
|
||||
struct DBusVMStateClass {
|
||||
ObjectClass parent_class;
|
||||
};
|
||||
|
||||
struct DBusVMState {
|
||||
Object parent;
|
||||
|
||||
GDBusConnection *bus;
|
||||
char *dbus_addr;
|
||||
char *id_list;
|
||||
|
||||
uint32_t data_size;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
static const GDBusPropertyInfo vmstate_property_info[] = {
|
||||
{ -1, (char *) "Id", (char *) "s",
|
||||
G_DBUS_PROPERTY_INFO_FLAGS_READABLE, NULL },
|
||||
};
|
||||
|
||||
static const GDBusPropertyInfo * const vmstate_property_info_pointers[] = {
|
||||
&vmstate_property_info[0],
|
||||
NULL
|
||||
};
|
||||
|
||||
static const GDBusInterfaceInfo vmstate1_interface_info = {
|
||||
-1,
|
||||
(char *) "org.qemu.VMState1",
|
||||
(GDBusMethodInfo **) NULL,
|
||||
(GDBusSignalInfo **) NULL,
|
||||
(GDBusPropertyInfo **) &vmstate_property_info_pointers,
|
||||
NULL,
|
||||
};
|
||||
|
||||
#define DBUS_VMSTATE_SIZE_LIMIT (1 * MiB)
|
||||
|
||||
static GHashTable *
|
||||
get_id_list_set(DBusVMState *self)
|
||||
{
|
||||
g_auto(GStrv) ids = NULL;
|
||||
g_autoptr(GHashTable) set = NULL;
|
||||
int i;
|
||||
|
||||
if (!self->id_list) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ids = g_strsplit(self->id_list, ",", -1);
|
||||
set = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
|
||||
for (i = 0; ids[i]; i++) {
|
||||
g_hash_table_add(set, ids[i]);
|
||||
ids[i] = NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer(&set);
|
||||
}
|
||||
|
||||
static GHashTable *
|
||||
dbus_get_proxies(DBusVMState *self, GError **err)
|
||||
{
|
||||
g_autoptr(GHashTable) proxies = NULL;
|
||||
g_autoptr(GHashTable) ids = NULL;
|
||||
g_auto(GStrv) names = NULL;
|
||||
Error *error = NULL;
|
||||
size_t i;
|
||||
|
||||
ids = get_id_list_set(self);
|
||||
proxies = g_hash_table_new_full(g_str_hash, g_str_equal,
|
||||
g_free, g_object_unref);
|
||||
|
||||
names = qemu_dbus_get_queued_owners(self->bus, "org.qemu.VMState1", &error);
|
||||
if (!names) {
|
||||
g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
|
||||
error_get_pretty(error));
|
||||
error_free(error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; names[i]; i++) {
|
||||
g_autoptr(GDBusProxy) proxy = NULL;
|
||||
g_autoptr(GVariant) result = NULL;
|
||||
g_autofree char *id = NULL;
|
||||
size_t size;
|
||||
|
||||
proxy = g_dbus_proxy_new_sync(self->bus, G_DBUS_PROXY_FLAGS_NONE,
|
||||
(GDBusInterfaceInfo *) &vmstate1_interface_info,
|
||||
names[i],
|
||||
"/org/qemu/VMState1",
|
||||
"org.qemu.VMState1",
|
||||
NULL, err);
|
||||
if (!proxy) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = g_dbus_proxy_get_cached_property(proxy, "Id");
|
||||
if (!result) {
|
||||
g_set_error_literal(err, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"VMState Id property is missing.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
id = g_variant_dup_string(result, &size);
|
||||
if (ids && !g_hash_table_remove(ids, id)) {
|
||||
g_clear_pointer(&id, g_free);
|
||||
g_clear_object(&proxy);
|
||||
continue;
|
||||
}
|
||||
if (size == 0 || size >= 256) {
|
||||
g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"VMState Id '%s' is invalid.", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!g_hash_table_insert(proxies, id, proxy)) {
|
||||
g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Duplicated VMState Id '%s'", id);
|
||||
return NULL;
|
||||
}
|
||||
id = NULL;
|
||||
proxy = NULL;
|
||||
|
||||
g_clear_pointer(&result, g_variant_unref);
|
||||
}
|
||||
|
||||
if (ids) {
|
||||
g_autofree char **left = NULL;
|
||||
|
||||
left = (char **)g_hash_table_get_keys_as_array(ids, NULL);
|
||||
if (*left) {
|
||||
g_autofree char *leftids = g_strjoinv(",", left);
|
||||
g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Required VMState Id are missing: %s", leftids);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return g_steal_pointer(&proxies);
|
||||
}
|
||||
|
||||
static int
|
||||
dbus_load_state_proxy(GDBusProxy *proxy, const uint8_t *data, size_t size)
|
||||
{
|
||||
g_autoptr(GError) err = NULL;
|
||||
g_autoptr(GVariant) result = NULL;
|
||||
g_autoptr(GVariant) value = NULL;
|
||||
|
||||
value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
|
||||
data, size, sizeof(char));
|
||||
result = g_dbus_proxy_call_sync(proxy, "Load",
|
||||
g_variant_new("(@ay)",
|
||||
g_steal_pointer(&value)),
|
||||
G_DBUS_CALL_FLAGS_NO_AUTO_START,
|
||||
-1, NULL, &err);
|
||||
if (!result) {
|
||||
error_report("%s: Failed to Load: %s", __func__, err->message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dbus_vmstate_post_load(void *opaque, int version_id)
|
||||
{
|
||||
DBusVMState *self = DBUS_VMSTATE(opaque);
|
||||
g_autoptr(GInputStream) m = NULL;
|
||||
g_autoptr(GDataInputStream) s = NULL;
|
||||
g_autoptr(GError) err = NULL;
|
||||
g_autoptr(GHashTable) proxies = NULL;
|
||||
uint32_t nelem;
|
||||
|
||||
trace_dbus_vmstate_post_load(version_id);
|
||||
|
||||
proxies = dbus_get_proxies(self, &err);
|
||||
if (!proxies) {
|
||||
error_report("%s: Failed to get proxies: %s", __func__, err->message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
m = g_memory_input_stream_new_from_data(self->data, self->data_size, NULL);
|
||||
s = g_data_input_stream_new(m);
|
||||
g_data_input_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
|
||||
|
||||
nelem = g_data_input_stream_read_uint32(s, NULL, &err);
|
||||
if (err) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
while (nelem > 0) {
|
||||
GDBusProxy *proxy = NULL;
|
||||
uint32_t len;
|
||||
gsize bytes_read, avail;
|
||||
char id[256];
|
||||
|
||||
len = g_data_input_stream_read_uint32(s, NULL, &err);
|
||||
if (err) {
|
||||
goto error;
|
||||
}
|
||||
if (len >= 256) {
|
||||
error_report("%s: Invalid DBus vmstate proxy name %u",
|
||||
__func__, len);
|
||||
return -1;
|
||||
}
|
||||
if (!g_input_stream_read_all(G_INPUT_STREAM(s), id, len,
|
||||
&bytes_read, NULL, &err)) {
|
||||
goto error;
|
||||
}
|
||||
g_return_val_if_fail(bytes_read == len, -1);
|
||||
id[len] = 0;
|
||||
|
||||
trace_dbus_vmstate_loading(id);
|
||||
|
||||
proxy = g_hash_table_lookup(proxies, id);
|
||||
if (!proxy) {
|
||||
error_report("%s: Failed to find proxy Id '%s'", __func__, id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = g_data_input_stream_read_uint32(s, NULL, &err);
|
||||
avail = g_buffered_input_stream_get_available(
|
||||
G_BUFFERED_INPUT_STREAM(s));
|
||||
|
||||
if (len > DBUS_VMSTATE_SIZE_LIMIT || len > avail) {
|
||||
error_report("%s: Invalid vmstate size: %u", __func__, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dbus_load_state_proxy(proxy,
|
||||
g_buffered_input_stream_peek_buffer(G_BUFFERED_INPUT_STREAM(s),
|
||||
NULL),
|
||||
len) < 0) {
|
||||
error_report("%s: Failed to restore Id '%s'", __func__, id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!g_seekable_seek(G_SEEKABLE(s), len, G_SEEK_CUR, NULL, &err)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
nelem -= 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
error_report("%s: Failed to read from stream: %s", __func__, err->message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
dbus_save_state_proxy(gpointer key,
|
||||
gpointer value,
|
||||
gpointer user_data)
|
||||
{
|
||||
GDataOutputStream *s = user_data;
|
||||
const char *id = key;
|
||||
GDBusProxy *proxy = value;
|
||||
g_autoptr(GVariant) result = NULL;
|
||||
g_autoptr(GVariant) child = NULL;
|
||||
g_autoptr(GError) err = NULL;
|
||||
const uint8_t *data;
|
||||
gsize size;
|
||||
|
||||
trace_dbus_vmstate_saving(id);
|
||||
|
||||
result = g_dbus_proxy_call_sync(proxy, "Save",
|
||||
NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START,
|
||||
-1, NULL, &err);
|
||||
if (!result) {
|
||||
error_report("%s: Failed to Save: %s", __func__, err->message);
|
||||
return;
|
||||
}
|
||||
|
||||
child = g_variant_get_child_value(result, 0);
|
||||
data = g_variant_get_fixed_array(child, &size, sizeof(char));
|
||||
if (!data) {
|
||||
error_report("%s: Failed to Save: not a byte array", __func__);
|
||||
return;
|
||||
}
|
||||
if (size > DBUS_VMSTATE_SIZE_LIMIT) {
|
||||
error_report("%s: Too large vmstate data to save: %zu",
|
||||
__func__, (size_t)size);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_data_output_stream_put_uint32(s, strlen(id), NULL, &err) ||
|
||||
!g_data_output_stream_put_string(s, id, NULL, &err) ||
|
||||
!g_data_output_stream_put_uint32(s, size, NULL, &err) ||
|
||||
!g_output_stream_write_all(G_OUTPUT_STREAM(s),
|
||||
data, size, NULL, NULL, &err)) {
|
||||
error_report("%s: Failed to write to stream: %s",
|
||||
__func__, err->message);
|
||||
}
|
||||
}
|
||||
|
||||
static int dbus_vmstate_pre_save(void *opaque)
|
||||
{
|
||||
DBusVMState *self = DBUS_VMSTATE(opaque);
|
||||
g_autoptr(GOutputStream) m = NULL;
|
||||
g_autoptr(GDataOutputStream) s = NULL;
|
||||
g_autoptr(GHashTable) proxies = NULL;
|
||||
g_autoptr(GError) err = NULL;
|
||||
|
||||
trace_dbus_vmstate_pre_save();
|
||||
|
||||
proxies = dbus_get_proxies(self, &err);
|
||||
if (!proxies) {
|
||||
error_report("%s: Failed to get proxies: %s", __func__, err->message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
m = g_memory_output_stream_new_resizable();
|
||||
s = g_data_output_stream_new(m);
|
||||
g_data_output_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
|
||||
|
||||
if (!g_data_output_stream_put_uint32(s, g_hash_table_size(proxies),
|
||||
NULL, &err)) {
|
||||
error_report("%s: Failed to write to stream: %s",
|
||||
__func__, err->message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_hash_table_foreach(proxies, dbus_save_state_proxy, s);
|
||||
|
||||
if (g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m))
|
||||
> UINT32_MAX) {
|
||||
error_report("%s: DBus vmstate buffer is too large", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!g_output_stream_close(G_OUTPUT_STREAM(m), NULL, &err)) {
|
||||
error_report("%s: Failed to close stream: %s", __func__, err->message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_free(self->data);
|
||||
self->data_size =
|
||||
g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m));
|
||||
self->data =
|
||||
g_memory_output_stream_steal_data(G_MEMORY_OUTPUT_STREAM(m));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription dbus_vmstate = {
|
||||
.name = TYPE_DBUS_VMSTATE,
|
||||
.version_id = 0,
|
||||
.pre_save = dbus_vmstate_pre_save,
|
||||
.post_load = dbus_vmstate_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(data_size, DBusVMState),
|
||||
VMSTATE_VBUFFER_ALLOC_UINT32(data, DBusVMState, 0, 0, data_size),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
dbus_vmstate_complete(UserCreatable *uc, Error **errp)
|
||||
{
|
||||
DBusVMState *self = DBUS_VMSTATE(uc);
|
||||
g_autoptr(GError) err = NULL;
|
||||
|
||||
if (!object_resolve_path_type("", TYPE_DBUS_VMSTATE, NULL)) {
|
||||
error_setg(errp, "There is already an instance of %s",
|
||||
TYPE_DBUS_VMSTATE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->dbus_addr) {
|
||||
error_setg(errp, QERR_MISSING_PARAMETER, "addr");
|
||||
return;
|
||||
}
|
||||
|
||||
self->bus = g_dbus_connection_new_for_address_sync(self->dbus_addr,
|
||||
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
|
||||
G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
|
||||
NULL, NULL, &err);
|
||||
if (err) {
|
||||
error_setg(errp, "failed to connect to DBus: '%s'", err->message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vmstate_register(VMSTATE_IF(self), VMSTATE_INSTANCE_ID_ANY,
|
||||
&dbus_vmstate, self) < 0) {
|
||||
error_setg(errp, "Failed to register vmstate");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dbus_vmstate_finalize(Object *o)
|
||||
{
|
||||
DBusVMState *self = DBUS_VMSTATE(o);
|
||||
|
||||
vmstate_unregister(VMSTATE_IF(self), &dbus_vmstate, self);
|
||||
|
||||
g_clear_object(&self->bus);
|
||||
g_free(self->dbus_addr);
|
||||
g_free(self->id_list);
|
||||
g_free(self->data);
|
||||
}
|
||||
|
||||
static char *
|
||||
get_dbus_addr(Object *o, Error **errp)
|
||||
{
|
||||
DBusVMState *self = DBUS_VMSTATE(o);
|
||||
|
||||
return g_strdup(self->dbus_addr);
|
||||
}
|
||||
|
||||
static void
|
||||
set_dbus_addr(Object *o, const char *str, Error **errp)
|
||||
{
|
||||
DBusVMState *self = DBUS_VMSTATE(o);
|
||||
|
||||
g_free(self->dbus_addr);
|
||||
self->dbus_addr = g_strdup(str);
|
||||
}
|
||||
|
||||
static char *
|
||||
get_id_list(Object *o, Error **errp)
|
||||
{
|
||||
DBusVMState *self = DBUS_VMSTATE(o);
|
||||
|
||||
return g_strdup(self->id_list);
|
||||
}
|
||||
|
||||
static void
|
||||
set_id_list(Object *o, const char *str, Error **errp)
|
||||
{
|
||||
DBusVMState *self = DBUS_VMSTATE(o);
|
||||
|
||||
g_free(self->id_list);
|
||||
self->id_list = g_strdup(str);
|
||||
}
|
||||
|
||||
static char *
|
||||
dbus_vmstate_get_id(VMStateIf *vmif)
|
||||
{
|
||||
return g_strdup(TYPE_DBUS_VMSTATE);
|
||||
}
|
||||
|
||||
static void
|
||||
dbus_vmstate_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||
VMStateIfClass *vc = VMSTATE_IF_CLASS(oc);
|
||||
|
||||
ucc->complete = dbus_vmstate_complete;
|
||||
vc->get_id = dbus_vmstate_get_id;
|
||||
|
||||
object_class_property_add_str(oc, "addr",
|
||||
get_dbus_addr, set_dbus_addr,
|
||||
&error_abort);
|
||||
object_class_property_add_str(oc, "id-list",
|
||||
get_id_list, set_id_list,
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
static const TypeInfo dbus_vmstate_info = {
|
||||
.name = TYPE_DBUS_VMSTATE,
|
||||
.parent = TYPE_OBJECT,
|
||||
.instance_size = sizeof(DBusVMState),
|
||||
.instance_finalize = dbus_vmstate_finalize,
|
||||
.class_size = sizeof(DBusVMStateClass),
|
||||
.class_init = dbus_vmstate_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ TYPE_VMSTATE_IF },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
register_types(void)
|
||||
{
|
||||
type_register_static(&dbus_vmstate_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
|
@ -18,13 +18,6 @@
|
|||
#include "sysemu/sysemu.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
|
||||
/* hostmem-file.c */
|
||||
/**
|
||||
* @TYPE_MEMORY_BACKEND_FILE:
|
||||
* name of backend that uses mmap on a file descriptor
|
||||
*/
|
||||
#define TYPE_MEMORY_BACKEND_FILE "memory-backend-file"
|
||||
|
||||
#define MEMORY_BACKEND_FILE(obj) \
|
||||
OBJECT_CHECK(HostMemoryBackendFile, (obj), TYPE_MEMORY_BACKEND_FILE)
|
||||
|
||||
|
@ -58,7 +51,6 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
|||
return;
|
||||
}
|
||||
|
||||
backend->force_prealloc = mem_prealloc;
|
||||
name = host_memory_backend_get_name(backend);
|
||||
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
|
||||
name,
|
||||
|
|
|
@ -45,7 +45,6 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
|||
return;
|
||||
}
|
||||
|
||||
backend->force_prealloc = mem_prealloc;
|
||||
fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size,
|
||||
m->hugetlb, m->hugetlbsize, m->seal ?
|
||||
F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL : 0,
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
#include "qemu/module.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
|
||||
#define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram"
|
||||
|
||||
static void
|
||||
ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
{
|
||||
|
|
|
@ -215,7 +215,7 @@ static bool host_memory_backend_get_prealloc(Object *obj, Error **errp)
|
|||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
|
||||
return backend->prealloc || backend->force_prealloc;
|
||||
return backend->prealloc;
|
||||
}
|
||||
|
||||
static void host_memory_backend_set_prealloc(Object *obj, bool value,
|
||||
|
@ -223,15 +223,6 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value,
|
|||
{
|
||||
Error *local_err = NULL;
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
|
||||
if (backend->force_prealloc) {
|
||||
if (value) {
|
||||
error_setg(errp,
|
||||
"remove -mem-prealloc to use the prealloc property");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!host_memory_backend_mr_inited(backend)) {
|
||||
backend->prealloc = value;
|
||||
|
@ -243,7 +234,7 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value,
|
|||
void *ptr = memory_region_get_ram_ptr(&backend->mr);
|
||||
uint64_t sz = memory_region_size(&backend->mr);
|
||||
|
||||
os_mem_prealloc(fd, ptr, sz, ms->smp.cpus, &local_err);
|
||||
os_mem_prealloc(fd, ptr, sz, backend->prealloc_threads, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
|
@ -252,14 +243,44 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value,
|
|||
}
|
||||
}
|
||||
|
||||
static void host_memory_backend_get_prealloc_threads(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
visit_type_uint32(v, name, &backend->prealloc_threads, errp);
|
||||
}
|
||||
|
||||
static void host_memory_backend_set_prealloc_threads(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
Error *local_err = NULL;
|
||||
uint32_t value;
|
||||
|
||||
visit_type_uint32(v, name, &value, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
if (value <= 0) {
|
||||
error_setg(&local_err,
|
||||
"property '%s' of %s doesn't take value '%d'",
|
||||
name, object_get_typename(obj), value);
|
||||
goto out;
|
||||
}
|
||||
backend->prealloc_threads = value;
|
||||
out:
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
static void host_memory_backend_init(Object *obj)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
MachineState *machine = MACHINE(qdev_get_machine());
|
||||
|
||||
/* TODO: convert access to globals to compat properties */
|
||||
backend->merge = machine_mem_merge(machine);
|
||||
backend->dump = machine_dump_guest_core(machine);
|
||||
backend->prealloc = mem_prealloc;
|
||||
backend->prealloc_threads = 1;
|
||||
}
|
||||
|
||||
static void host_memory_backend_post_init(Object *obj)
|
||||
|
@ -313,7 +334,6 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
|
|||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(uc);
|
||||
HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(uc);
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
Error *local_err = NULL;
|
||||
void *ptr;
|
||||
uint64_t sz;
|
||||
|
@ -378,7 +398,7 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
|
|||
*/
|
||||
if (backend->prealloc) {
|
||||
os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz,
|
||||
ms->smp.cpus, &local_err);
|
||||
backend->prealloc_threads, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
|
@ -456,6 +476,12 @@ host_memory_backend_class_init(ObjectClass *oc, void *data)
|
|||
host_memory_backend_set_prealloc, &error_abort);
|
||||
object_class_property_set_description(oc, "prealloc",
|
||||
"Preallocate memory", &error_abort);
|
||||
object_class_property_add(oc, "prealloc-threads", "int",
|
||||
host_memory_backend_get_prealloc_threads,
|
||||
host_memory_backend_set_prealloc_threads,
|
||||
NULL, NULL, &error_abort);
|
||||
object_class_property_set_description(oc, "prealloc-threads",
|
||||
"Number of CPU threads to use for prealloc", &error_abort);
|
||||
object_class_property_add(oc, "size", "int",
|
||||
host_memory_backend_get_size,
|
||||
host_memory_backend_set_size,
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
|
||||
# dbus-vmstate.c
|
||||
dbus_vmstate_pre_save(void)
|
||||
dbus_vmstate_post_load(int version_id) "version_id: %d"
|
||||
dbus_vmstate_loading(const char *id) "id: %s"
|
||||
dbus_vmstate_saving(const char *id) "id: %s"
|
476
block.c
476
block.c
|
@ -363,6 +363,7 @@ char *bdrv_get_full_backing_filename(BlockDriverState *bs, Error **errp)
|
|||
|
||||
void bdrv_register(BlockDriver *bdrv)
|
||||
{
|
||||
assert(bdrv->format_name);
|
||||
QLIST_INSERT_HEAD(&bdrv_drivers, bdrv, list);
|
||||
}
|
||||
|
||||
|
@ -482,7 +483,8 @@ static void coroutine_fn bdrv_create_co_entry(void *opaque)
|
|||
CreateCo *cco = opaque;
|
||||
assert(cco->drv);
|
||||
|
||||
ret = cco->drv->bdrv_co_create_opts(cco->filename, cco->opts, &local_err);
|
||||
ret = cco->drv->bdrv_co_create_opts(cco->drv,
|
||||
cco->filename, cco->opts, &local_err);
|
||||
error_propagate(&cco->err, local_err);
|
||||
cco->ret = ret;
|
||||
}
|
||||
|
@ -532,19 +534,168 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for bdrv_create_file_fallback(): Resize @blk to at
|
||||
* least the given @minimum_size.
|
||||
*
|
||||
* On success, return @blk's actual length.
|
||||
* Otherwise, return -errno.
|
||||
*/
|
||||
static int64_t create_file_fallback_truncate(BlockBackend *blk,
|
||||
int64_t minimum_size, Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
int64_t size;
|
||||
int ret;
|
||||
|
||||
ret = blk_truncate(blk, minimum_size, false, PREALLOC_MODE_OFF, &local_err);
|
||||
if (ret < 0 && ret != -ENOTSUP) {
|
||||
error_propagate(errp, local_err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size = blk_getlength(blk);
|
||||
if (size < 0) {
|
||||
error_free(local_err);
|
||||
error_setg_errno(errp, -size,
|
||||
"Failed to inquire the new image file's length");
|
||||
return size;
|
||||
}
|
||||
|
||||
if (size < minimum_size) {
|
||||
/* Need to grow the image, but we failed to do that */
|
||||
error_propagate(errp, local_err);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
error_free(local_err);
|
||||
local_err = NULL;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for bdrv_create_file_fallback(): Zero the first
|
||||
* sector to remove any potentially pre-existing image header.
|
||||
*/
|
||||
static int create_file_fallback_zero_first_sector(BlockBackend *blk,
|
||||
int64_t current_size,
|
||||
Error **errp)
|
||||
{
|
||||
int64_t bytes_to_clear;
|
||||
int ret;
|
||||
|
||||
bytes_to_clear = MIN(current_size, BDRV_SECTOR_SIZE);
|
||||
if (bytes_to_clear) {
|
||||
ret = blk_pwrite_zeroes(blk, 0, bytes_to_clear, BDRV_REQ_MAY_UNMAP);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Failed to clear the new image's first sector");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple implementation of bdrv_co_create_opts for protocol drivers
|
||||
* which only support creation via opening a file
|
||||
* (usually existing raw storage device)
|
||||
*/
|
||||
int coroutine_fn bdrv_co_create_opts_simple(BlockDriver *drv,
|
||||
const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
QDict *options;
|
||||
int64_t size = 0;
|
||||
char *buf = NULL;
|
||||
PreallocMode prealloc;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
|
||||
buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
|
||||
prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
|
||||
PREALLOC_MODE_OFF, &local_err);
|
||||
g_free(buf);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (prealloc != PREALLOC_MODE_OFF) {
|
||||
error_setg(errp, "Unsupported preallocation mode '%s'",
|
||||
PreallocMode_str(prealloc));
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
options = qdict_new();
|
||||
qdict_put_str(options, "driver", drv->format_name);
|
||||
|
||||
blk = blk_new_open(filename, NULL, options,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE, errp);
|
||||
if (!blk) {
|
||||
error_prepend(errp, "Protocol driver '%s' does not support image "
|
||||
"creation, and opening the image failed: ",
|
||||
drv->format_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
size = create_file_fallback_truncate(blk, size, errp);
|
||||
if (size < 0) {
|
||||
ret = size;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = create_file_fallback_zero_first_sector(blk, size, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
blk_unref(blk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp)
|
||||
{
|
||||
BlockDriver *drv;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
drv = bdrv_find_protocol(filename, true, errp);
|
||||
if (drv == NULL) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ret = bdrv_create(drv, filename, opts, &local_err);
|
||||
error_propagate(errp, local_err);
|
||||
return bdrv_create(drv, filename, opts, errp);
|
||||
}
|
||||
|
||||
int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
assert(bs != NULL);
|
||||
|
||||
if (!bs->drv) {
|
||||
error_setg(errp, "Block node '%s' is not opened", bs->filename);
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
if (!bs->drv->bdrv_co_delete_file) {
|
||||
error_setg(errp, "Driver '%s' does not support image deletion",
|
||||
bs->drv->format_name);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
ret = bs->drv->bdrv_co_delete_file(bs, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -845,6 +996,28 @@ static BlockdevDetectZeroesOptions bdrv_parse_detect_zeroes(QemuOpts *opts,
|
|||
return detect_zeroes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set open flags for aio engine
|
||||
*
|
||||
* Return 0 on success, -1 if the engine specified is invalid
|
||||
*/
|
||||
int bdrv_parse_aio(const char *mode, int *flags)
|
||||
{
|
||||
if (!strcmp(mode, "threads")) {
|
||||
/* do nothing, default */
|
||||
} else if (!strcmp(mode, "native")) {
|
||||
*flags |= BDRV_O_NATIVE_AIO;
|
||||
#ifdef CONFIG_LINUX_IO_URING
|
||||
} else if (!strcmp(mode, "io_uring")) {
|
||||
*flags |= BDRV_O_IO_URING;
|
||||
#endif
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set open flags for a given discard mode
|
||||
*
|
||||
|
@ -1422,6 +1595,24 @@ QemuOptsList bdrv_runtime_opts = {
|
|||
},
|
||||
};
|
||||
|
||||
QemuOptsList bdrv_create_opts_simple = {
|
||||
.name = "simple-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(bdrv_create_opts_simple.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{
|
||||
.name = BLOCK_OPT_PREALLOC,
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Preallocation mode (allowed values: off)"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Common part for opening disk images and files
|
||||
*
|
||||
|
@ -1712,8 +1903,6 @@ static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q,
|
|||
bool *tighten_restrictions, Error **errp);
|
||||
static void bdrv_child_abort_perm_update(BdrvChild *c);
|
||||
static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
|
||||
static void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
|
||||
uint64_t *shared_perm);
|
||||
|
||||
typedef struct BlockReopenQueueEntry {
|
||||
bool prepared;
|
||||
|
@ -1937,8 +2126,8 @@ static void bdrv_set_perm(BlockDriverState *bs, uint64_t cumulative_perms,
|
|||
}
|
||||
}
|
||||
|
||||
static void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
|
||||
uint64_t *shared_perm)
|
||||
void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
|
||||
uint64_t *shared_perm)
|
||||
{
|
||||
BdrvChild *c;
|
||||
uint64_t cumulative_perms = 0;
|
||||
|
@ -1976,18 +2165,19 @@ char *bdrv_perm_names(uint64_t perm)
|
|||
{ 0, NULL }
|
||||
};
|
||||
|
||||
char *result = g_strdup("");
|
||||
GString *result = g_string_sized_new(30);
|
||||
struct perm_name *p;
|
||||
|
||||
for (p = permissions; p->name; p++) {
|
||||
if (perm & p->perm) {
|
||||
char *old = result;
|
||||
result = g_strdup_printf("%s%s%s", old, *old ? ", " : "", p->name);
|
||||
g_free(old);
|
||||
if (result->len > 0) {
|
||||
g_string_append(result, ", ");
|
||||
}
|
||||
g_string_append(result, p->name);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return g_string_free(result, FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2227,6 +2417,24 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
|
|||
*nshared = shared;
|
||||
}
|
||||
|
||||
uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm)
|
||||
{
|
||||
static const uint64_t permissions[] = {
|
||||
[BLOCK_PERMISSION_CONSISTENT_READ] = BLK_PERM_CONSISTENT_READ,
|
||||
[BLOCK_PERMISSION_WRITE] = BLK_PERM_WRITE,
|
||||
[BLOCK_PERMISSION_WRITE_UNCHANGED] = BLK_PERM_WRITE_UNCHANGED,
|
||||
[BLOCK_PERMISSION_RESIZE] = BLK_PERM_RESIZE,
|
||||
[BLOCK_PERMISSION_GRAPH_MOD] = BLK_PERM_GRAPH_MOD,
|
||||
};
|
||||
|
||||
QEMU_BUILD_BUG_ON(ARRAY_SIZE(permissions) != BLOCK_PERMISSION__MAX);
|
||||
QEMU_BUILD_BUG_ON(1UL << ARRAY_SIZE(permissions) != BLK_PERM_ALL + 1);
|
||||
|
||||
assert(qapi_perm < BLOCK_PERMISSION__MAX);
|
||||
|
||||
return permissions[qapi_perm];
|
||||
}
|
||||
|
||||
static void bdrv_replace_child_noperm(BdrvChild *child,
|
||||
BlockDriverState *new_bs)
|
||||
{
|
||||
|
@ -2394,13 +2602,13 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
|
|||
if (bdrv_get_aio_context(child_bs) != ctx) {
|
||||
ret = bdrv_try_set_aio_context(child_bs, ctx, &local_err);
|
||||
if (ret < 0 && child_role->can_set_aio_ctx) {
|
||||
GSList *ignore = g_slist_prepend(NULL, child);;
|
||||
GSList *ignore = g_slist_prepend(NULL, child);
|
||||
ctx = bdrv_get_aio_context(child_bs);
|
||||
if (child_role->can_set_aio_ctx(child, ctx, &ignore, NULL)) {
|
||||
error_free(local_err);
|
||||
ret = 0;
|
||||
g_slist_free(ignore);
|
||||
ignore = g_slist_prepend(NULL, child);;
|
||||
ignore = g_slist_prepend(NULL, child);
|
||||
child_role->set_aio_ctx(child, ctx, &ignore);
|
||||
}
|
||||
g_slist_free(ignore);
|
||||
|
@ -2409,6 +2617,7 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
|
|||
error_propagate(errp, local_err);
|
||||
g_free(child);
|
||||
bdrv_abort_perm_update(child_bs);
|
||||
bdrv_unref(child_bs);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -2458,10 +2667,7 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
|
|||
|
||||
static void bdrv_detach_child(BdrvChild *child)
|
||||
{
|
||||
if (child->next.le_prev) {
|
||||
QLIST_REMOVE(child, next);
|
||||
child->next.le_prev = NULL;
|
||||
}
|
||||
QLIST_SAFE_REMOVE(child, next);
|
||||
|
||||
bdrv_replace_child(child, NULL);
|
||||
|
||||
|
@ -2559,10 +2765,10 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
|
|||
|
||||
if (bs->backing) {
|
||||
bdrv_unref_child(bs, bs->backing);
|
||||
bs->backing = NULL;
|
||||
}
|
||||
|
||||
if (!backing_hd) {
|
||||
bs->backing = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -3519,6 +3725,15 @@ cleanup_perm:
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) {
|
||||
BlockDriverState *bs = bs_entry->state.bs;
|
||||
|
||||
if (bs->drv->bdrv_reopen_commit_post)
|
||||
bs->drv->bdrv_reopen_commit_post(&bs_entry->state);
|
||||
}
|
||||
}
|
||||
cleanup:
|
||||
QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
|
||||
if (ret) {
|
||||
|
@ -3602,6 +3817,29 @@ static void bdrv_reopen_perm(BlockReopenQueue *q, BlockDriverState *bs,
|
|||
*shared = cumulative_shared_perms;
|
||||
}
|
||||
|
||||
static bool bdrv_reopen_can_attach(BlockDriverState *parent,
|
||||
BdrvChild *child,
|
||||
BlockDriverState *new_child,
|
||||
Error **errp)
|
||||
{
|
||||
AioContext *parent_ctx = bdrv_get_aio_context(parent);
|
||||
AioContext *child_ctx = bdrv_get_aio_context(new_child);
|
||||
GSList *ignore;
|
||||
bool ret;
|
||||
|
||||
ignore = g_slist_prepend(NULL, child);
|
||||
ret = bdrv_can_set_aio_context(new_child, parent_ctx, &ignore, NULL);
|
||||
g_slist_free(ignore);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ignore = g_slist_prepend(NULL, child);
|
||||
ret = bdrv_can_set_aio_context(parent, child_ctx, &ignore, errp);
|
||||
g_slist_free(ignore);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Take a BDRVReopenState and check if the value of 'backing' in the
|
||||
* reopen_state->options QDict is valid or not.
|
||||
|
@ -3653,14 +3891,11 @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
|
|||
}
|
||||
|
||||
/*
|
||||
* TODO: before removing the x- prefix from x-blockdev-reopen we
|
||||
* should move the new backing file into the right AioContext
|
||||
* instead of returning an error.
|
||||
* Check AioContext compatibility so that the bdrv_set_backing_hd() call in
|
||||
* bdrv_reopen_commit() won't fail.
|
||||
*/
|
||||
if (new_backing_bs) {
|
||||
if (bdrv_get_aio_context(new_backing_bs) != bdrv_get_aio_context(bs)) {
|
||||
error_setg(errp, "Cannot use a new backing file "
|
||||
"with a different AioContext");
|
||||
if (!bdrv_reopen_can_attach(bs, bs->backing, new_backing_bs, errp)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
@ -4162,6 +4397,7 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
|
|||
bdrv_ref(from);
|
||||
|
||||
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
|
||||
assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to));
|
||||
bdrv_drained_begin(from);
|
||||
|
||||
/* Put all parents into @list and calculate their cumulative permissions */
|
||||
|
@ -4766,14 +5002,15 @@ BlockDriverState *bdrv_find_node(const char *node_name)
|
|||
}
|
||||
|
||||
/* Put this QMP function here so it can access the static graph_bdrv_states. */
|
||||
BlockDeviceInfoList *bdrv_named_nodes_list(Error **errp)
|
||||
BlockDeviceInfoList *bdrv_named_nodes_list(bool flat,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDeviceInfoList *list, *entry;
|
||||
BlockDriverState *bs;
|
||||
|
||||
list = NULL;
|
||||
QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) {
|
||||
BlockDeviceInfo *info = bdrv_block_device_info(NULL, bs, errp);
|
||||
BlockDeviceInfo *info = bdrv_block_device_info(NULL, bs, flat, errp);
|
||||
if (!info) {
|
||||
qapi_free_BlockDeviceInfoList(list);
|
||||
return NULL;
|
||||
|
@ -4854,36 +5091,23 @@ static void xdbg_graph_add_node(XDbgBlockGraphConstructor *gr, void *node,
|
|||
static void xdbg_graph_add_edge(XDbgBlockGraphConstructor *gr, void *parent,
|
||||
const BdrvChild *child)
|
||||
{
|
||||
typedef struct {
|
||||
unsigned int flag;
|
||||
BlockPermission num;
|
||||
} PermissionMap;
|
||||
|
||||
static const PermissionMap permissions[] = {
|
||||
{ BLK_PERM_CONSISTENT_READ, BLOCK_PERMISSION_CONSISTENT_READ },
|
||||
{ BLK_PERM_WRITE, BLOCK_PERMISSION_WRITE },
|
||||
{ BLK_PERM_WRITE_UNCHANGED, BLOCK_PERMISSION_WRITE_UNCHANGED },
|
||||
{ BLK_PERM_RESIZE, BLOCK_PERMISSION_RESIZE },
|
||||
{ BLK_PERM_GRAPH_MOD, BLOCK_PERMISSION_GRAPH_MOD },
|
||||
{ 0, 0 }
|
||||
};
|
||||
const PermissionMap *p;
|
||||
BlockPermission qapi_perm;
|
||||
XDbgBlockGraphEdge *edge;
|
||||
|
||||
QEMU_BUILD_BUG_ON(1UL << (ARRAY_SIZE(permissions) - 1) != BLK_PERM_ALL + 1);
|
||||
|
||||
edge = g_new0(XDbgBlockGraphEdge, 1);
|
||||
|
||||
edge->parent = xdbg_graph_node_num(gr, parent);
|
||||
edge->child = xdbg_graph_node_num(gr, child->bs);
|
||||
edge->name = g_strdup(child->name);
|
||||
|
||||
for (p = permissions; p->flag; p++) {
|
||||
if (p->flag & child->perm) {
|
||||
QAPI_LIST_ADD(edge->perm, p->num);
|
||||
for (qapi_perm = 0; qapi_perm < BLOCK_PERMISSION__MAX; qapi_perm++) {
|
||||
uint64_t flag = bdrv_qapi_perm_to_blk_perm(qapi_perm);
|
||||
|
||||
if (flag & child->perm) {
|
||||
QAPI_LIST_ADD(edge->perm, qapi_perm);
|
||||
}
|
||||
if (p->flag & child->shared_perm) {
|
||||
QAPI_LIST_ADD(edge->shared_perm, p->num);
|
||||
if (flag & child->shared_perm) {
|
||||
QAPI_LIST_ADD(edge->shared_perm, qapi_perm);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5335,10 +5559,6 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs,
|
|||
return;
|
||||
}
|
||||
|
||||
if (!(bs->open_flags & BDRV_O_INACTIVE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
bdrv_co_invalidate_cache(child->bs, &local_err);
|
||||
if (local_err) {
|
||||
|
@ -5360,34 +5580,36 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs,
|
|||
* just keep the extended permissions for the next time that an activation
|
||||
* of the image is tried.
|
||||
*/
|
||||
bs->open_flags &= ~BDRV_O_INACTIVE;
|
||||
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
|
||||
ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
bs->open_flags |= BDRV_O_INACTIVE;
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
bdrv_set_perm(bs, perm, shared_perm);
|
||||
|
||||
if (bs->drv->bdrv_co_invalidate_cache) {
|
||||
bs->drv->bdrv_co_invalidate_cache(bs, &local_err);
|
||||
if (local_err) {
|
||||
if (bs->open_flags & BDRV_O_INACTIVE) {
|
||||
bs->open_flags &= ~BDRV_O_INACTIVE;
|
||||
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
|
||||
ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
bs->open_flags |= BDRV_O_INACTIVE;
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
bdrv_set_perm(bs, perm, shared_perm);
|
||||
|
||||
FOR_EACH_DIRTY_BITMAP(bs, bm) {
|
||||
bdrv_dirty_bitmap_skip_store(bm, false);
|
||||
}
|
||||
if (bs->drv->bdrv_co_invalidate_cache) {
|
||||
bs->drv->bdrv_co_invalidate_cache(bs, &local_err);
|
||||
if (local_err) {
|
||||
bs->open_flags |= BDRV_O_INACTIVE;
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ret = refresh_total_sectors(bs, bs->total_sectors);
|
||||
if (ret < 0) {
|
||||
bs->open_flags |= BDRV_O_INACTIVE;
|
||||
error_setg_errno(errp, -ret, "Could not refresh total sector count");
|
||||
return;
|
||||
FOR_EACH_DIRTY_BITMAP(bs, bm) {
|
||||
bdrv_dirty_bitmap_skip_store(bm, false);
|
||||
}
|
||||
|
||||
ret = refresh_total_sectors(bs, bs->total_sectors);
|
||||
if (ret < 0) {
|
||||
bs->open_flags |= BDRV_O_INACTIVE;
|
||||
error_setg_errno(errp, -ret, "Could not refresh total sector count");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QLIST_FOREACH(parent, &bs->parents, next_parent) {
|
||||
|
@ -5751,12 +5973,11 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
|||
return;
|
||||
}
|
||||
|
||||
/* Create parameter list */
|
||||
create_opts = qemu_opts_append(create_opts, drv->create_opts);
|
||||
create_opts = qemu_opts_append(create_opts, proto_drv->create_opts);
|
||||
|
||||
/* Create parameter list with default values */
|
||||
opts = qemu_opts_create(create_opts, NULL, 0, &error_abort);
|
||||
qemu_opt_set_number(opts, BLOCK_OPT_SIZE, img_size, &error_abort);
|
||||
|
||||
/* Parse -o options */
|
||||
if (options) {
|
||||
|
@ -5766,6 +5987,13 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
|||
}
|
||||
}
|
||||
|
||||
if (!qemu_opt_get(opts, BLOCK_OPT_SIZE)) {
|
||||
qemu_opt_set_number(opts, BLOCK_OPT_SIZE, img_size, &error_abort);
|
||||
} else if (img_size != UINT64_C(-1)) {
|
||||
error_setg(errp, "The image size must be specified only once");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (base_filename) {
|
||||
qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, base_filename, &local_err);
|
||||
if (local_err) {
|
||||
|
@ -6169,65 +6397,55 @@ int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
|||
return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque, errp);
|
||||
}
|
||||
|
||||
/* This function will be called by the bdrv_recurse_is_first_non_filter method
|
||||
* of block filter and by bdrv_is_first_non_filter.
|
||||
* It is used to test if the given bs is the candidate or recurse more in the
|
||||
* node graph.
|
||||
/*
|
||||
* This function checks whether the given @to_replace is allowed to be
|
||||
* replaced by a node that always shows the same data as @bs. This is
|
||||
* used for example to verify whether the mirror job can replace
|
||||
* @to_replace by the target mirrored from @bs.
|
||||
* To be replaceable, @bs and @to_replace may either be guaranteed to
|
||||
* always show the same data (because they are only connected through
|
||||
* filters), or some driver may allow replacing one of its children
|
||||
* because it can guarantee that this child's data is not visible at
|
||||
* all (for example, for dissenting quorum children that have no other
|
||||
* parents).
|
||||
*/
|
||||
bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||
BlockDriverState *candidate)
|
||||
bool bdrv_recurse_can_replace(BlockDriverState *bs,
|
||||
BlockDriverState *to_replace)
|
||||
{
|
||||
/* return false if basic checks fails */
|
||||
if (!bs || !bs->drv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* the code reached a non block filter driver -> check if the bs is
|
||||
* the same as the candidate. It's the recursion termination condition.
|
||||
*/
|
||||
if (!bs->drv->is_filter) {
|
||||
return bs == candidate;
|
||||
}
|
||||
/* Down this path the driver is a block filter driver */
|
||||
|
||||
/* If the block filter recursion method is defined use it to recurse down
|
||||
* the node graph.
|
||||
*/
|
||||
if (bs->drv->bdrv_recurse_is_first_non_filter) {
|
||||
return bs->drv->bdrv_recurse_is_first_non_filter(bs, candidate);
|
||||
if (bs == to_replace) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* the driver is a block filter but don't allow to recurse -> return false
|
||||
*/
|
||||
/* See what the driver can do */
|
||||
if (bs->drv->bdrv_recurse_can_replace) {
|
||||
return bs->drv->bdrv_recurse_can_replace(bs, to_replace);
|
||||
}
|
||||
|
||||
/* For filters without an own implementation, we can recurse on our own */
|
||||
if (bs->drv->is_filter) {
|
||||
BdrvChild *child = bs->file ?: bs->backing;
|
||||
return bdrv_recurse_can_replace(child->bs, to_replace);
|
||||
}
|
||||
|
||||
/* Safe default */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* This function checks if the candidate is the first non filter bs down it's
|
||||
* bs chain. Since we don't have pointers to parents it explore all bs chains
|
||||
* from the top. Some filters can choose not to pass down the recursion.
|
||||
/*
|
||||
* Check whether the given @node_name can be replaced by a node that
|
||||
* has the same data as @parent_bs. If so, return @node_name's BDS;
|
||||
* NULL otherwise.
|
||||
*
|
||||
* @node_name must be a (recursive) *child of @parent_bs (or this
|
||||
* function will return NULL).
|
||||
*
|
||||
* The result (whether the node can be replaced or not) is only valid
|
||||
* for as long as no graph or permission changes occur.
|
||||
*/
|
||||
bool bdrv_is_first_non_filter(BlockDriverState *candidate)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
BdrvNextIterator it;
|
||||
|
||||
/* walk down the bs forest recursively */
|
||||
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
|
||||
bool perm;
|
||||
|
||||
/* try to recurse in this top level bs */
|
||||
perm = bdrv_recurse_is_first_non_filter(bs, candidate);
|
||||
|
||||
/* candidate is the first non filter */
|
||||
if (perm) {
|
||||
bdrv_next_cleanup(&it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs,
|
||||
const char *node_name, Error **errp)
|
||||
{
|
||||
|
@ -6252,8 +6470,11 @@ BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs,
|
|||
* Another benefit is that this tests exclude backing files which are
|
||||
* blocked by the backing blockers.
|
||||
*/
|
||||
if (!bdrv_recurse_is_first_non_filter(parent_bs, to_replace_bs)) {
|
||||
error_setg(errp, "Only top most non filter can be replaced");
|
||||
if (!bdrv_recurse_can_replace(parent_bs, to_replace_bs)) {
|
||||
error_setg(errp, "Cannot replace '%s' by a node mirrored from '%s', "
|
||||
"because it cannot be guaranteed that doing so would not "
|
||||
"lead to an abrupt change of visible data",
|
||||
node_name, parent_bs->node_name);
|
||||
to_replace_bs = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
@ -6410,6 +6631,7 @@ void bdrv_refresh_filename(BlockDriverState *bs)
|
|||
child->bs->exact_filename);
|
||||
pstrcpy(bs->filename, sizeof(bs->filename), child->bs->filename);
|
||||
|
||||
qobject_unref(bs->full_open_options);
|
||||
bs->full_open_options = qobject_ref(child->bs->full_open_options);
|
||||
|
||||
return;
|
||||
|
|
|
@ -18,6 +18,7 @@ block-obj-y += block-backend.o snapshot.o qapi.o
|
|||
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
|
||||
block-obj-$(CONFIG_POSIX) += file-posix.o
|
||||
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
|
||||
block-obj-$(CONFIG_LINUX_IO_URING) += io_uring.o
|
||||
block-obj-y += null.o mirror.o commit.o io.o create.o
|
||||
block-obj-y += throttle-groups.o
|
||||
block-obj-$(CONFIG_LINUX) += nvme.o
|
||||
|
@ -43,8 +44,12 @@ block-obj-y += crypto.o
|
|||
|
||||
block-obj-y += aio_task.o
|
||||
block-obj-y += backup-top.o
|
||||
block-obj-y += filter-compress.o
|
||||
common-obj-y += monitor/
|
||||
|
||||
common-obj-y += stream.o
|
||||
block-obj-y += stream.o
|
||||
|
||||
common-obj-y += qapi-sysemu.o
|
||||
|
||||
nfs.o-libs := $(LIBNFS_LIBS)
|
||||
iscsi.o-cflags := $(LIBISCSI_CFLAGS)
|
||||
|
@ -65,5 +70,7 @@ block-obj-$(if $(CONFIG_LZFSE),m,n) += dmg-lzfse.o
|
|||
dmg-lzfse.o-libs := $(LZFSE_LIBS)
|
||||
qcow.o-libs := -lz
|
||||
linux-aio.o-libs := -laio
|
||||
io_uring.o-cflags := $(LINUX_IO_URING_CFLAGS)
|
||||
io_uring.o-libs := $(LINUX_IO_URING_LIBS)
|
||||
parallels.o-cflags := $(LIBXML2_CFLAGS)
|
||||
parallels.o-libs := $(LIBXML2_LIBS)
|
||||
|
|
|
@ -38,6 +38,7 @@ typedef struct BDRVBackupTopState {
|
|||
BlockCopyState *bcs;
|
||||
BdrvChild *target;
|
||||
bool active;
|
||||
int64_t cluster_size;
|
||||
} BDRVBackupTopState;
|
||||
|
||||
static coroutine_fn int backup_top_co_preadv(
|
||||
|
@ -48,11 +49,17 @@ static coroutine_fn int backup_top_co_preadv(
|
|||
}
|
||||
|
||||
static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t bytes)
|
||||
uint64_t bytes, BdrvRequestFlags flags)
|
||||
{
|
||||
BDRVBackupTopState *s = bs->opaque;
|
||||
uint64_t end = QEMU_ALIGN_UP(offset + bytes, s->bcs->cluster_size);
|
||||
uint64_t off = QEMU_ALIGN_DOWN(offset, s->bcs->cluster_size);
|
||||
uint64_t off, end;
|
||||
|
||||
if (flags & BDRV_REQ_WRITE_UNCHANGED) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
off = QEMU_ALIGN_DOWN(offset, s->cluster_size);
|
||||
end = QEMU_ALIGN_UP(offset + bytes, s->cluster_size);
|
||||
|
||||
return block_copy(s->bcs, off, end - off, NULL);
|
||||
}
|
||||
|
@ -60,7 +67,7 @@ static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
|
|||
static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs,
|
||||
int64_t offset, int bytes)
|
||||
{
|
||||
int ret = backup_top_cbw(bs, offset, bytes);
|
||||
int ret = backup_top_cbw(bs, offset, bytes, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -71,7 +78,7 @@ static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs,
|
|||
static int coroutine_fn backup_top_co_pwrite_zeroes(BlockDriverState *bs,
|
||||
int64_t offset, int bytes, BdrvRequestFlags flags)
|
||||
{
|
||||
int ret = backup_top_cbw(bs, offset, bytes);
|
||||
int ret = backup_top_cbw(bs, offset, bytes, flags);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -84,11 +91,9 @@ static coroutine_fn int backup_top_co_pwritev(BlockDriverState *bs,
|
|||
uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
if (!(flags & BDRV_REQ_WRITE_UNCHANGED)) {
|
||||
int ret = backup_top_cbw(bs, offset, bytes);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
int ret = backup_top_cbw(bs, offset, bytes, flags);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags);
|
||||
|
@ -190,13 +195,19 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
|
|||
BlockDriverState *top = bdrv_new_open_driver(&bdrv_backup_top_filter,
|
||||
filter_node_name,
|
||||
BDRV_O_RDWR, errp);
|
||||
bool appended = false;
|
||||
|
||||
if (!top) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
state = top->opaque;
|
||||
top->total_sectors = source->total_sectors;
|
||||
top->opaque = state = g_new0(BDRVBackupTopState, 1);
|
||||
top->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||
(BDRV_REQ_FUA & source->supported_write_flags);
|
||||
top->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
|
||||
source->supported_zero_flags);
|
||||
|
||||
bdrv_ref(target);
|
||||
state->target = bdrv_attach_child(top, target, "target", &child_file, errp);
|
||||
|
@ -212,8 +223,9 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
|
|||
bdrv_append(top, source, &local_err);
|
||||
if (local_err) {
|
||||
error_prepend(&local_err, "Cannot append backup-top filter: ");
|
||||
goto append_failed;
|
||||
goto fail;
|
||||
}
|
||||
appended = true;
|
||||
|
||||
/*
|
||||
* bdrv_append() finished successfully, now we can require permissions
|
||||
|
@ -224,14 +236,15 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
|
|||
if (local_err) {
|
||||
error_prepend(&local_err,
|
||||
"Cannot set permissions for backup-top filter: ");
|
||||
goto failed_after_append;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
state->cluster_size = cluster_size;
|
||||
state->bcs = block_copy_state_new(top->backing, state->target,
|
||||
cluster_size, write_flags, &local_err);
|
||||
if (local_err) {
|
||||
error_prepend(&local_err, "Cannot create block-copy-state: ");
|
||||
goto failed_after_append;
|
||||
goto fail;
|
||||
}
|
||||
*bcs = state->bcs;
|
||||
|
||||
|
@ -239,14 +252,15 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
|
|||
|
||||
return top;
|
||||
|
||||
failed_after_append:
|
||||
state->active = false;
|
||||
bdrv_backup_top_drop(top);
|
||||
fail:
|
||||
if (appended) {
|
||||
state->active = false;
|
||||
bdrv_backup_top_drop(top);
|
||||
} else {
|
||||
bdrv_unref(top);
|
||||
}
|
||||
|
||||
append_failed:
|
||||
bdrv_drained_end(source);
|
||||
bdrv_unref_child(top, state->target);
|
||||
bdrv_unref(top);
|
||||
error_propagate(errp, local_err);
|
||||
|
||||
return NULL;
|
||||
|
@ -255,14 +269,11 @@ append_failed:
|
|||
void bdrv_backup_top_drop(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBackupTopState *s = bs->opaque;
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
block_copy_state_free(s->bcs);
|
||||
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
bdrv_drained_begin(bs);
|
||||
|
||||
block_copy_state_free(s->bcs);
|
||||
|
||||
s->active = false;
|
||||
bdrv_child_refresh_perms(bs, bs->backing, &error_abort);
|
||||
bdrv_replace_node(bs, backing_bs(bs), &error_abort);
|
||||
|
@ -271,6 +282,4 @@ void bdrv_backup_top_drop(BlockDriverState *bs)
|
|||
bdrv_drained_end(bs);
|
||||
|
||||
bdrv_unref(bs);
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
|
|
@ -57,15 +57,6 @@ static void backup_progress_bytes_callback(int64_t bytes, void *opaque)
|
|||
BackupBlockJob *s = opaque;
|
||||
|
||||
s->bytes_read += bytes;
|
||||
job_progress_update(&s->common.job, bytes);
|
||||
}
|
||||
|
||||
static void backup_progress_reset_callback(void *opaque)
|
||||
{
|
||||
BackupBlockJob *s = opaque;
|
||||
uint64_t estimate = bdrv_get_dirty_count(s->bcs->copy_bitmap);
|
||||
|
||||
job_progress_set_remaining(&s->common.job, estimate);
|
||||
}
|
||||
|
||||
static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
||||
|
@ -111,7 +102,7 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
|
|||
|
||||
if (ret < 0 && job->bitmap_mode == BITMAP_SYNC_MODE_ALWAYS) {
|
||||
/* If we failed and synced, merge in the bits we didn't copy: */
|
||||
bdrv_dirty_bitmap_merge_internal(bm, job->bcs->copy_bitmap,
|
||||
bdrv_dirty_bitmap_merge_internal(bm, block_copy_dirty_bitmap(job->bcs),
|
||||
NULL, true);
|
||||
}
|
||||
}
|
||||
|
@ -135,7 +126,6 @@ static void backup_abort(Job *job)
|
|||
static void backup_clean(Job *job)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
|
||||
|
||||
bdrv_backup_top_drop(s->backup_top);
|
||||
}
|
||||
|
||||
|
@ -151,7 +141,8 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
|
|||
return;
|
||||
}
|
||||
|
||||
bdrv_set_dirty_bitmap(backup_job->bcs->copy_bitmap, 0, backup_job->len);
|
||||
bdrv_set_dirty_bitmap(block_copy_dirty_bitmap(backup_job->bcs), 0,
|
||||
backup_job->len);
|
||||
}
|
||||
|
||||
static BlockErrorAction backup_error_action(BackupBlockJob *job,
|
||||
|
@ -196,7 +187,7 @@ static int coroutine_fn backup_loop(BackupBlockJob *job)
|
|||
BdrvDirtyBitmapIter *bdbi;
|
||||
int ret = 0;
|
||||
|
||||
bdbi = bdrv_dirty_iter_new(job->bcs->copy_bitmap);
|
||||
bdbi = bdrv_dirty_iter_new(block_copy_dirty_bitmap(job->bcs));
|
||||
while ((offset = bdrv_dirty_iter_next(bdbi)) != -1) {
|
||||
do {
|
||||
if (yield_and_check(job)) {
|
||||
|
@ -216,14 +207,14 @@ static int coroutine_fn backup_loop(BackupBlockJob *job)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void backup_init_copy_bitmap(BackupBlockJob *job)
|
||||
static void backup_init_bcs_bitmap(BackupBlockJob *job)
|
||||
{
|
||||
bool ret;
|
||||
uint64_t estimate;
|
||||
BdrvDirtyBitmap *bcs_bitmap = block_copy_dirty_bitmap(job->bcs);
|
||||
|
||||
if (job->sync_mode == MIRROR_SYNC_MODE_BITMAP) {
|
||||
ret = bdrv_dirty_bitmap_merge_internal(job->bcs->copy_bitmap,
|
||||
job->sync_bitmap,
|
||||
ret = bdrv_dirty_bitmap_merge_internal(bcs_bitmap, job->sync_bitmap,
|
||||
NULL, true);
|
||||
assert(ret);
|
||||
} else {
|
||||
|
@ -232,12 +223,12 @@ static void backup_init_copy_bitmap(BackupBlockJob *job)
|
|||
* We can't hog the coroutine to initialize this thoroughly.
|
||||
* Set a flag and resume work when we are able to yield safely.
|
||||
*/
|
||||
job->bcs->skip_unallocated = true;
|
||||
block_copy_set_skip_unallocated(job->bcs, true);
|
||||
}
|
||||
bdrv_set_dirty_bitmap(job->bcs->copy_bitmap, 0, job->len);
|
||||
bdrv_set_dirty_bitmap(bcs_bitmap, 0, job->len);
|
||||
}
|
||||
|
||||
estimate = bdrv_get_dirty_count(job->bcs->copy_bitmap);
|
||||
estimate = bdrv_get_dirty_count(bcs_bitmap);
|
||||
job_progress_set_remaining(&job->common.job, estimate);
|
||||
}
|
||||
|
||||
|
@ -246,7 +237,7 @@ static int coroutine_fn backup_run(Job *job, Error **errp)
|
|||
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
|
||||
int ret = 0;
|
||||
|
||||
backup_init_copy_bitmap(s);
|
||||
backup_init_bcs_bitmap(s);
|
||||
|
||||
if (s->sync_mode == MIRROR_SYNC_MODE_TOP) {
|
||||
int64_t offset = 0;
|
||||
|
@ -265,12 +256,12 @@ static int coroutine_fn backup_run(Job *job, Error **errp)
|
|||
|
||||
offset += count;
|
||||
}
|
||||
s->bcs->skip_unallocated = false;
|
||||
block_copy_set_skip_unallocated(s->bcs, false);
|
||||
}
|
||||
|
||||
if (s->sync_mode == MIRROR_SYNC_MODE_NONE) {
|
||||
/*
|
||||
* All bits are set in copy_bitmap to allow any cluster to be copied.
|
||||
* All bits are set in bcs bitmap to allow any cluster to be copied.
|
||||
* This does not actually require them to be copied.
|
||||
*/
|
||||
while (!job_is_cancelled(job)) {
|
||||
|
@ -461,8 +452,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
|||
job->cluster_size = cluster_size;
|
||||
job->len = len;
|
||||
|
||||
block_copy_set_callbacks(bcs, backup_progress_bytes_callback,
|
||||
backup_progress_reset_callback, job);
|
||||
block_copy_set_progress_callback(bcs, backup_progress_bytes_callback, job);
|
||||
block_copy_set_progress_meter(bcs, &job->common.job.progress);
|
||||
|
||||
/* Required permissions are already taken by backup-top target */
|
||||
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
|
||||
|
|
|
@ -28,10 +28,14 @@
|
|||
#include "qemu/cutils.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qlist.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "sysemu/qtest.h"
|
||||
|
||||
typedef struct BDRVBlkdebugState {
|
||||
|
@ -44,6 +48,9 @@ typedef struct BDRVBlkdebugState {
|
|||
uint64_t opt_discard;
|
||||
uint64_t max_discard;
|
||||
|
||||
uint64_t take_child_perms;
|
||||
uint64_t unshare_child_perms;
|
||||
|
||||
/* For blkdebug_refresh_filename() */
|
||||
char *config_file;
|
||||
|
||||
|
@ -344,6 +351,69 @@ static void blkdebug_parse_filename(const char *filename, QDict *options,
|
|||
qdict_put_str(options, "x-image", filename);
|
||||
}
|
||||
|
||||
static int blkdebug_parse_perm_list(uint64_t *dest, QDict *options,
|
||||
const char *prefix, Error **errp)
|
||||
{
|
||||
int ret = 0;
|
||||
QDict *subqdict = NULL;
|
||||
QObject *crumpled_subqdict = NULL;
|
||||
Visitor *v = NULL;
|
||||
BlockPermissionList *perm_list = NULL, *element;
|
||||
Error *local_err = NULL;
|
||||
|
||||
*dest = 0;
|
||||
|
||||
qdict_extract_subqdict(options, &subqdict, prefix);
|
||||
if (!qdict_size(subqdict)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
crumpled_subqdict = qdict_crumple(subqdict, errp);
|
||||
if (!crumpled_subqdict) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new(crumpled_subqdict);
|
||||
visit_type_BlockPermissionList(v, NULL, &perm_list, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (element = perm_list; element; element = element->next) {
|
||||
*dest |= bdrv_qapi_perm_to_blk_perm(element->value);
|
||||
}
|
||||
|
||||
out:
|
||||
qapi_free_BlockPermissionList(perm_list);
|
||||
visit_free(v);
|
||||
qobject_unref(subqdict);
|
||||
qobject_unref(crumpled_subqdict);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int blkdebug_parse_perms(BDRVBlkdebugState *s, QDict *options,
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = blkdebug_parse_perm_list(&s->take_child_perms, options,
|
||||
"take-child-perms.", errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = blkdebug_parse_perm_list(&s->unshare_child_perms, options,
|
||||
"unshare-child-perms.", errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static QemuOptsList runtime_opts = {
|
||||
.name = "blkdebug",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
|
||||
|
@ -419,6 +489,12 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
/* Set initial state */
|
||||
s->state = 1;
|
||||
|
||||
/* Parse permissions modifiers before opening the image file */
|
||||
ret = blkdebug_parse_perms(s, options, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Open the image file */
|
||||
bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image",
|
||||
bs, &child_file, false, &local_err);
|
||||
|
@ -916,6 +992,21 @@ static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void blkdebug_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||
const BdrvChildRole *role,
|
||||
BlockReopenQueue *reopen_queue,
|
||||
uint64_t perm, uint64_t shared,
|
||||
uint64_t *nperm, uint64_t *nshared)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
|
||||
bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
|
||||
nperm, nshared);
|
||||
|
||||
*nperm |= s->take_child_perms;
|
||||
*nshared &= ~s->unshare_child_perms;
|
||||
}
|
||||
|
||||
static const char *const blkdebug_strong_runtime_opts[] = {
|
||||
"config",
|
||||
"inject-error.",
|
||||
|
@ -940,7 +1031,7 @@ static BlockDriver bdrv_blkdebug = {
|
|||
.bdrv_file_open = blkdebug_open,
|
||||
.bdrv_close = blkdebug_close,
|
||||
.bdrv_reopen_prepare = blkdebug_reopen_prepare,
|
||||
.bdrv_child_perm = bdrv_filter_default_perms,
|
||||
.bdrv_child_perm = blkdebug_child_perm,
|
||||
|
||||
.bdrv_getlength = blkdebug_getlength,
|
||||
.bdrv_refresh_filename = blkdebug_refresh_filename,
|
||||
|
|
|
@ -268,18 +268,18 @@ static int blkverify_co_flush(BlockDriverState *bs)
|
|||
return bdrv_co_flush(s->test_file->bs);
|
||||
}
|
||||
|
||||
static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||
BlockDriverState *candidate)
|
||||
static bool blkverify_recurse_can_replace(BlockDriverState *bs,
|
||||
BlockDriverState *to_replace)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
|
||||
bool perm = bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
|
||||
|
||||
if (perm) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate);
|
||||
/*
|
||||
* blkverify quits the whole qemu process if there is a mismatch
|
||||
* between bs->file->bs and s->test_file->bs. Therefore, we know
|
||||
* know that both must match bs and we can recurse down to either.
|
||||
*/
|
||||
return bdrv_recurse_can_replace(bs->file->bs, to_replace) ||
|
||||
bdrv_recurse_can_replace(s->test_file->bs, to_replace);
|
||||
}
|
||||
|
||||
static void blkverify_refresh_filename(BlockDriverState *bs)
|
||||
|
@ -327,7 +327,7 @@ static BlockDriver bdrv_blkverify = {
|
|||
.bdrv_co_flush = blkverify_co_flush,
|
||||
|
||||
.is_filter = true,
|
||||
.bdrv_recurse_is_first_non_filter = blkverify_recurse_is_first_non_filter,
|
||||
.bdrv_recurse_can_replace = blkverify_recurse_can_replace,
|
||||
};
|
||||
|
||||
static void bdrv_blkverify_init(void)
|
||||
|
|
|
@ -1140,16 +1140,22 @@ static int blk_check_byte_request(BlockBackend *blk, int64_t offset,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* To be called between exactly one pair of blk_inc/dec_in_flight() */
|
||||
static void coroutine_fn blk_wait_while_drained(BlockBackend *blk)
|
||||
{
|
||||
assert(blk->in_flight > 0);
|
||||
|
||||
if (blk->quiesce_counter && !blk->disable_request_queuing) {
|
||||
blk_dec_in_flight(blk);
|
||||
qemu_co_queue_wait(&blk->queued_requests, NULL);
|
||||
blk_inc_in_flight(blk);
|
||||
}
|
||||
}
|
||||
|
||||
int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset,
|
||||
unsigned int bytes, QEMUIOVector *qiov,
|
||||
BdrvRequestFlags flags)
|
||||
/* To be called between exactly one pair of blk_inc/dec_in_flight() */
|
||||
static int coroutine_fn
|
||||
blk_do_preadv(BlockBackend *blk, int64_t offset, unsigned int bytes,
|
||||
QEMUIOVector *qiov, BdrvRequestFlags flags)
|
||||
{
|
||||
int ret;
|
||||
BlockDriverState *bs;
|
||||
|
@ -1178,10 +1184,24 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset,
|
||||
unsigned int bytes,
|
||||
QEMUIOVector *qiov, size_t qiov_offset,
|
||||
BdrvRequestFlags flags)
|
||||
int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset,
|
||||
unsigned int bytes, QEMUIOVector *qiov,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
blk_inc_in_flight(blk);
|
||||
ret = blk_do_preadv(blk, offset, bytes, qiov, flags);
|
||||
blk_dec_in_flight(blk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* To be called between exactly one pair of blk_inc/dec_in_flight() */
|
||||
static int coroutine_fn
|
||||
blk_do_pwritev_part(BlockBackend *blk, int64_t offset, unsigned int bytes,
|
||||
QEMUIOVector *qiov, size_t qiov_offset,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
int ret;
|
||||
BlockDriverState *bs;
|
||||
|
@ -1214,6 +1234,20 @@ int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset,
|
||||
unsigned int bytes,
|
||||
QEMUIOVector *qiov, size_t qiov_offset,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
blk_inc_in_flight(blk);
|
||||
ret = blk_do_pwritev_part(blk, offset, bytes, qiov, qiov_offset, flags);
|
||||
blk_dec_in_flight(blk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
|
||||
unsigned int bytes, QEMUIOVector *qiov,
|
||||
BdrvRequestFlags flags)
|
||||
|
@ -1234,7 +1268,7 @@ static void blk_read_entry(void *opaque)
|
|||
BlkRwCo *rwco = opaque;
|
||||
QEMUIOVector *qiov = rwco->iobuf;
|
||||
|
||||
rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size,
|
||||
rwco->ret = blk_do_preadv(rwco->blk, rwco->offset, qiov->size,
|
||||
qiov, rwco->flags);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
@ -1244,8 +1278,8 @@ static void blk_write_entry(void *opaque)
|
|||
BlkRwCo *rwco = opaque;
|
||||
QEMUIOVector *qiov = rwco->iobuf;
|
||||
|
||||
rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size,
|
||||
qiov, rwco->flags);
|
||||
rwco->ret = blk_do_pwritev_part(rwco->blk, rwco->offset, qiov->size,
|
||||
qiov, 0, rwco->flags);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
|
@ -1262,6 +1296,7 @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
|
|||
.ret = NOT_DONE,
|
||||
};
|
||||
|
||||
blk_inc_in_flight(blk);
|
||||
if (qemu_in_coroutine()) {
|
||||
/* Fast-path if already in coroutine context */
|
||||
co_entry(&rwco);
|
||||
|
@ -1270,6 +1305,7 @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
|
|||
bdrv_coroutine_enter(blk_bs(blk), co);
|
||||
BDRV_POLL_WHILE(blk_bs(blk), rwco.ret == NOT_DONE);
|
||||
}
|
||||
blk_dec_in_flight(blk);
|
||||
|
||||
return rwco.ret;
|
||||
}
|
||||
|
@ -1387,14 +1423,8 @@ static void blk_aio_read_entry(void *opaque)
|
|||
BlkRwCo *rwco = &acb->rwco;
|
||||
QEMUIOVector *qiov = rwco->iobuf;
|
||||
|
||||
if (rwco->blk->quiesce_counter) {
|
||||
blk_dec_in_flight(rwco->blk);
|
||||
blk_wait_while_drained(rwco->blk);
|
||||
blk_inc_in_flight(rwco->blk);
|
||||
}
|
||||
|
||||
assert(qiov->size == acb->bytes);
|
||||
rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, acb->bytes,
|
||||
rwco->ret = blk_do_preadv(rwco->blk, rwco->offset, acb->bytes,
|
||||
qiov, rwco->flags);
|
||||
blk_aio_complete(acb);
|
||||
}
|
||||
|
@ -1405,15 +1435,9 @@ static void blk_aio_write_entry(void *opaque)
|
|||
BlkRwCo *rwco = &acb->rwco;
|
||||
QEMUIOVector *qiov = rwco->iobuf;
|
||||
|
||||
if (rwco->blk->quiesce_counter) {
|
||||
blk_dec_in_flight(rwco->blk);
|
||||
blk_wait_while_drained(rwco->blk);
|
||||
blk_inc_in_flight(rwco->blk);
|
||||
}
|
||||
|
||||
assert(!qiov || qiov->size == acb->bytes);
|
||||
rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, acb->bytes,
|
||||
qiov, rwco->flags);
|
||||
rwco->ret = blk_do_pwritev_part(rwco->blk, rwco->offset, acb->bytes,
|
||||
qiov, 0, rwco->flags);
|
||||
blk_aio_complete(acb);
|
||||
}
|
||||
|
||||
|
@ -1488,38 +1512,6 @@ BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset,
|
|||
blk_aio_write_entry, flags, cb, opaque);
|
||||
}
|
||||
|
||||
static void blk_aio_flush_entry(void *opaque)
|
||||
{
|
||||
BlkAioEmAIOCB *acb = opaque;
|
||||
BlkRwCo *rwco = &acb->rwco;
|
||||
|
||||
rwco->ret = blk_co_flush(rwco->blk);
|
||||
blk_aio_complete(acb);
|
||||
}
|
||||
|
||||
BlockAIOCB *blk_aio_flush(BlockBackend *blk,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
return blk_aio_prwv(blk, 0, 0, NULL, blk_aio_flush_entry, 0, cb, opaque);
|
||||
}
|
||||
|
||||
static void blk_aio_pdiscard_entry(void *opaque)
|
||||
{
|
||||
BlkAioEmAIOCB *acb = opaque;
|
||||
BlkRwCo *rwco = &acb->rwco;
|
||||
|
||||
rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, acb->bytes);
|
||||
blk_aio_complete(acb);
|
||||
}
|
||||
|
||||
BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk,
|
||||
int64_t offset, int bytes,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
return blk_aio_prwv(blk, offset, bytes, NULL, blk_aio_pdiscard_entry, 0,
|
||||
cb, opaque);
|
||||
}
|
||||
|
||||
void blk_aio_cancel(BlockAIOCB *acb)
|
||||
{
|
||||
bdrv_aio_cancel(acb);
|
||||
|
@ -1530,7 +1522,9 @@ void blk_aio_cancel_async(BlockAIOCB *acb)
|
|||
bdrv_aio_cancel_async(acb);
|
||||
}
|
||||
|
||||
int blk_co_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
|
||||
/* To be called between exactly one pair of blk_inc/dec_in_flight() */
|
||||
static int coroutine_fn
|
||||
blk_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
|
||||
{
|
||||
blk_wait_while_drained(blk);
|
||||
|
||||
|
@ -1546,8 +1540,7 @@ static void blk_ioctl_entry(void *opaque)
|
|||
BlkRwCo *rwco = opaque;
|
||||
QEMUIOVector *qiov = rwco->iobuf;
|
||||
|
||||
rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset,
|
||||
qiov->iov[0].iov_base);
|
||||
rwco->ret = blk_do_ioctl(rwco->blk, rwco->offset, qiov->iov[0].iov_base);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
|
@ -1561,7 +1554,7 @@ static void blk_aio_ioctl_entry(void *opaque)
|
|||
BlkAioEmAIOCB *acb = opaque;
|
||||
BlkRwCo *rwco = &acb->rwco;
|
||||
|
||||
rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, rwco->iobuf);
|
||||
rwco->ret = blk_do_ioctl(rwco->blk, rwco->offset, rwco->iobuf);
|
||||
|
||||
blk_aio_complete(acb);
|
||||
}
|
||||
|
@ -1572,7 +1565,9 @@ BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf,
|
|||
return blk_aio_prwv(blk, req, 0, buf, blk_aio_ioctl_entry, 0, cb, opaque);
|
||||
}
|
||||
|
||||
int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
|
||||
/* To be called between exactly one pair of blk_inc/dec_in_flight() */
|
||||
static int coroutine_fn
|
||||
blk_do_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -1586,7 +1581,50 @@ int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
|
|||
return bdrv_co_pdiscard(blk->root, offset, bytes);
|
||||
}
|
||||
|
||||
int blk_co_flush(BlockBackend *blk)
|
||||
static void blk_aio_pdiscard_entry(void *opaque)
|
||||
{
|
||||
BlkAioEmAIOCB *acb = opaque;
|
||||
BlkRwCo *rwco = &acb->rwco;
|
||||
|
||||
rwco->ret = blk_do_pdiscard(rwco->blk, rwco->offset, acb->bytes);
|
||||
blk_aio_complete(acb);
|
||||
}
|
||||
|
||||
BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk,
|
||||
int64_t offset, int bytes,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
return blk_aio_prwv(blk, offset, bytes, NULL, blk_aio_pdiscard_entry, 0,
|
||||
cb, opaque);
|
||||
}
|
||||
|
||||
int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
|
||||
{
|
||||
int ret;
|
||||
|
||||
blk_inc_in_flight(blk);
|
||||
ret = blk_do_pdiscard(blk, offset, bytes);
|
||||
blk_dec_in_flight(blk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void blk_pdiscard_entry(void *opaque)
|
||||
{
|
||||
BlkRwCo *rwco = opaque;
|
||||
QEMUIOVector *qiov = rwco->iobuf;
|
||||
|
||||
rwco->ret = blk_do_pdiscard(rwco->blk, rwco->offset, qiov->size);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
|
||||
{
|
||||
return blk_prw(blk, offset, NULL, bytes, blk_pdiscard_entry, 0);
|
||||
}
|
||||
|
||||
/* To be called between exactly one pair of blk_inc/dec_in_flight() */
|
||||
static int coroutine_fn blk_do_flush(BlockBackend *blk)
|
||||
{
|
||||
blk_wait_while_drained(blk);
|
||||
|
||||
|
@ -1597,10 +1635,36 @@ int blk_co_flush(BlockBackend *blk)
|
|||
return bdrv_co_flush(blk_bs(blk));
|
||||
}
|
||||
|
||||
static void blk_aio_flush_entry(void *opaque)
|
||||
{
|
||||
BlkAioEmAIOCB *acb = opaque;
|
||||
BlkRwCo *rwco = &acb->rwco;
|
||||
|
||||
rwco->ret = blk_do_flush(rwco->blk);
|
||||
blk_aio_complete(acb);
|
||||
}
|
||||
|
||||
BlockAIOCB *blk_aio_flush(BlockBackend *blk,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
return blk_aio_prwv(blk, 0, 0, NULL, blk_aio_flush_entry, 0, cb, opaque);
|
||||
}
|
||||
|
||||
int coroutine_fn blk_co_flush(BlockBackend *blk)
|
||||
{
|
||||
int ret;
|
||||
|
||||
blk_inc_in_flight(blk);
|
||||
ret = blk_do_flush(blk);
|
||||
blk_dec_in_flight(blk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void blk_flush_entry(void *opaque)
|
||||
{
|
||||
BlkRwCo *rwco = opaque;
|
||||
rwco->ret = blk_co_flush(rwco->blk);
|
||||
rwco->ret = blk_do_flush(rwco->blk);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
|
@ -2083,20 +2147,6 @@ int blk_truncate(BlockBackend *blk, int64_t offset, bool exact,
|
|||
return bdrv_truncate(blk->root, offset, exact, prealloc, errp);
|
||||
}
|
||||
|
||||
static void blk_pdiscard_entry(void *opaque)
|
||||
{
|
||||
BlkRwCo *rwco = opaque;
|
||||
QEMUIOVector *qiov = rwco->iobuf;
|
||||
|
||||
rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
|
||||
{
|
||||
return blk_prw(blk, offset, NULL, bytes, blk_pdiscard_entry, 0);
|
||||
}
|
||||
|
||||
int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf,
|
||||
int64_t pos, int size)
|
||||
{
|
||||
|
|
|
@ -24,37 +24,136 @@
|
|||
#define BLOCK_COPY_MAX_BUFFER (1 * MiB)
|
||||
#define BLOCK_COPY_MAX_MEM (128 * MiB)
|
||||
|
||||
static void coroutine_fn block_copy_wait_inflight_reqs(BlockCopyState *s,
|
||||
int64_t start,
|
||||
int64_t end)
|
||||
typedef struct BlockCopyInFlightReq {
|
||||
int64_t offset;
|
||||
int64_t bytes;
|
||||
QLIST_ENTRY(BlockCopyInFlightReq) list;
|
||||
CoQueue wait_queue; /* coroutines blocked on this request */
|
||||
} BlockCopyInFlightReq;
|
||||
|
||||
typedef struct BlockCopyState {
|
||||
/*
|
||||
* BdrvChild objects are not owned or managed by block-copy. They are
|
||||
* provided by block-copy user and user is responsible for appropriate
|
||||
* permissions on these children.
|
||||
*/
|
||||
BdrvChild *source;
|
||||
BdrvChild *target;
|
||||
BdrvDirtyBitmap *copy_bitmap;
|
||||
int64_t in_flight_bytes;
|
||||
int64_t cluster_size;
|
||||
bool use_copy_range;
|
||||
int64_t copy_size;
|
||||
uint64_t len;
|
||||
QLIST_HEAD(, BlockCopyInFlightReq) inflight_reqs;
|
||||
|
||||
BdrvRequestFlags write_flags;
|
||||
|
||||
/*
|
||||
* skip_unallocated:
|
||||
*
|
||||
* Used by sync=top jobs, which first scan the source node for unallocated
|
||||
* areas and clear them in the copy_bitmap. During this process, the bitmap
|
||||
* is thus not fully initialized: It may still have bits set for areas that
|
||||
* are unallocated and should actually not be copied.
|
||||
*
|
||||
* This is indicated by skip_unallocated.
|
||||
*
|
||||
* In this case, block_copy() will query the source’s allocation status,
|
||||
* skip unallocated regions, clear them in the copy_bitmap, and invoke
|
||||
* block_copy_reset_unallocated() every time it does.
|
||||
*/
|
||||
bool skip_unallocated;
|
||||
|
||||
ProgressMeter *progress;
|
||||
/* progress_bytes_callback: called when some copying progress is done. */
|
||||
ProgressBytesCallbackFunc progress_bytes_callback;
|
||||
void *progress_opaque;
|
||||
|
||||
SharedResource *mem;
|
||||
} BlockCopyState;
|
||||
|
||||
static BlockCopyInFlightReq *find_conflicting_inflight_req(BlockCopyState *s,
|
||||
int64_t offset,
|
||||
int64_t bytes)
|
||||
{
|
||||
BlockCopyInFlightReq *req;
|
||||
bool waited;
|
||||
|
||||
do {
|
||||
waited = false;
|
||||
QLIST_FOREACH(req, &s->inflight_reqs, list) {
|
||||
if (end > req->start_byte && start < req->end_byte) {
|
||||
qemu_co_queue_wait(&req->wait_queue, NULL);
|
||||
waited = true;
|
||||
break;
|
||||
}
|
||||
QLIST_FOREACH(req, &s->inflight_reqs, list) {
|
||||
if (offset + bytes > req->offset && offset < req->offset + req->bytes) {
|
||||
return req;
|
||||
}
|
||||
} while (waited);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there are no intersecting requests return false. Otherwise, wait for the
|
||||
* first found intersecting request to finish and return true.
|
||||
*/
|
||||
static bool coroutine_fn block_copy_wait_one(BlockCopyState *s, int64_t offset,
|
||||
int64_t bytes)
|
||||
{
|
||||
BlockCopyInFlightReq *req = find_conflicting_inflight_req(s, offset, bytes);
|
||||
|
||||
if (!req) {
|
||||
return false;
|
||||
}
|
||||
|
||||
qemu_co_queue_wait(&req->wait_queue, NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Called only on full-dirty region */
|
||||
static void block_copy_inflight_req_begin(BlockCopyState *s,
|
||||
BlockCopyInFlightReq *req,
|
||||
int64_t start, int64_t end)
|
||||
int64_t offset, int64_t bytes)
|
||||
{
|
||||
req->start_byte = start;
|
||||
req->end_byte = end;
|
||||
assert(!find_conflicting_inflight_req(s, offset, bytes));
|
||||
|
||||
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
|
||||
s->in_flight_bytes += bytes;
|
||||
|
||||
req->offset = offset;
|
||||
req->bytes = bytes;
|
||||
qemu_co_queue_init(&req->wait_queue);
|
||||
QLIST_INSERT_HEAD(&s->inflight_reqs, req, list);
|
||||
}
|
||||
|
||||
static void coroutine_fn block_copy_inflight_req_end(BlockCopyInFlightReq *req)
|
||||
/*
|
||||
* block_copy_inflight_req_shrink
|
||||
*
|
||||
* Drop the tail of the request to be handled later. Set dirty bits back and
|
||||
* wake up all requests waiting for us (may be some of them are not intersecting
|
||||
* with shrunk request)
|
||||
*/
|
||||
static void coroutine_fn block_copy_inflight_req_shrink(BlockCopyState *s,
|
||||
BlockCopyInFlightReq *req, int64_t new_bytes)
|
||||
{
|
||||
if (new_bytes == req->bytes) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(new_bytes > 0 && new_bytes < req->bytes);
|
||||
|
||||
s->in_flight_bytes -= req->bytes - new_bytes;
|
||||
bdrv_set_dirty_bitmap(s->copy_bitmap,
|
||||
req->offset + new_bytes, req->bytes - new_bytes);
|
||||
|
||||
req->bytes = new_bytes;
|
||||
qemu_co_queue_restart_all(&req->wait_queue);
|
||||
}
|
||||
|
||||
static void coroutine_fn block_copy_inflight_req_end(BlockCopyState *s,
|
||||
BlockCopyInFlightReq *req,
|
||||
int ret)
|
||||
{
|
||||
s->in_flight_bytes -= req->bytes;
|
||||
if (ret < 0) {
|
||||
bdrv_set_dirty_bitmap(s->copy_bitmap, req->offset, req->bytes);
|
||||
}
|
||||
QLIST_REMOVE(req, list);
|
||||
qemu_co_queue_restart_all(&req->wait_queue);
|
||||
}
|
||||
|
@ -70,16 +169,19 @@ void block_copy_state_free(BlockCopyState *s)
|
|||
g_free(s);
|
||||
}
|
||||
|
||||
static uint32_t block_copy_max_transfer(BdrvChild *source, BdrvChild *target)
|
||||
{
|
||||
return MIN_NON_ZERO(INT_MAX,
|
||||
MIN_NON_ZERO(source->bs->bl.max_transfer,
|
||||
target->bs->bl.max_transfer));
|
||||
}
|
||||
|
||||
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
||||
int64_t cluster_size,
|
||||
BdrvRequestFlags write_flags, Error **errp)
|
||||
{
|
||||
BlockCopyState *s;
|
||||
BdrvDirtyBitmap *copy_bitmap;
|
||||
uint32_t max_transfer =
|
||||
MIN_NON_ZERO(INT_MAX,
|
||||
MIN_NON_ZERO(source->bs->bl.max_transfer,
|
||||
target->bs->bl.max_transfer));
|
||||
|
||||
copy_bitmap = bdrv_create_dirty_bitmap(source->bs, cluster_size, NULL,
|
||||
errp);
|
||||
|
@ -99,7 +201,7 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
|||
.mem = shres_create(BLOCK_COPY_MAX_MEM),
|
||||
};
|
||||
|
||||
if (max_transfer < cluster_size) {
|
||||
if (block_copy_max_transfer(source, target) < cluster_size) {
|
||||
/*
|
||||
* copy_range does not respect max_transfer. We don't want to bother
|
||||
* with requests smaller than block-copy cluster size, so fallback to
|
||||
|
@ -114,12 +216,11 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
|||
s->copy_size = cluster_size;
|
||||
} else {
|
||||
/*
|
||||
* copy_range does not respect max_transfer (it's a TODO), so we factor
|
||||
* that in here.
|
||||
* We enable copy-range, but keep small copy_size, until first
|
||||
* successful copy_range (look at block_copy_do_copy).
|
||||
*/
|
||||
s->use_copy_range = true;
|
||||
s->copy_size = MIN(MAX(cluster_size, BLOCK_COPY_MAX_COPY_RANGE),
|
||||
QEMU_ALIGN_DOWN(max_transfer, cluster_size));
|
||||
s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER);
|
||||
}
|
||||
|
||||
QLIST_INIT(&s->inflight_reqs);
|
||||
|
@ -127,48 +228,83 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
|||
return s;
|
||||
}
|
||||
|
||||
void block_copy_set_callbacks(
|
||||
void block_copy_set_progress_callback(
|
||||
BlockCopyState *s,
|
||||
ProgressBytesCallbackFunc progress_bytes_callback,
|
||||
ProgressResetCallbackFunc progress_reset_callback,
|
||||
void *progress_opaque)
|
||||
{
|
||||
s->progress_bytes_callback = progress_bytes_callback;
|
||||
s->progress_reset_callback = progress_reset_callback;
|
||||
s->progress_opaque = progress_opaque;
|
||||
}
|
||||
|
||||
void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm)
|
||||
{
|
||||
s->progress = pm;
|
||||
}
|
||||
|
||||
/*
|
||||
* block_copy_do_copy
|
||||
*
|
||||
* Do copy of cluser-aligned chunk. @end is allowed to exceed s->len only to
|
||||
* cover last cluster when s->len is not aligned to clusters.
|
||||
* Do copy of cluster-aligned chunk. Requested region is allowed to exceed
|
||||
* s->len only to cover last cluster when s->len is not aligned to clusters.
|
||||
*
|
||||
* No sync here: nor bitmap neighter intersecting requests handling, only copy.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
static int coroutine_fn block_copy_do_copy(BlockCopyState *s,
|
||||
int64_t start, int64_t end,
|
||||
bool *error_is_read)
|
||||
int64_t offset, int64_t bytes,
|
||||
bool zeroes, bool *error_is_read)
|
||||
{
|
||||
int ret;
|
||||
int nbytes = MIN(end, s->len) - start;
|
||||
int64_t nbytes = MIN(offset + bytes, s->len) - offset;
|
||||
void *bounce_buffer = NULL;
|
||||
|
||||
assert(QEMU_IS_ALIGNED(start, s->cluster_size));
|
||||
assert(QEMU_IS_ALIGNED(end, s->cluster_size));
|
||||
assert(end < s->len || end == QEMU_ALIGN_UP(s->len, s->cluster_size));
|
||||
assert(offset >= 0 && bytes > 0 && INT64_MAX - offset >= bytes);
|
||||
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
|
||||
assert(QEMU_IS_ALIGNED(bytes, s->cluster_size));
|
||||
assert(offset < s->len);
|
||||
assert(offset + bytes <= s->len ||
|
||||
offset + bytes == QEMU_ALIGN_UP(s->len, s->cluster_size));
|
||||
assert(nbytes < INT_MAX);
|
||||
|
||||
if (zeroes) {
|
||||
ret = bdrv_co_pwrite_zeroes(s->target, offset, nbytes, s->write_flags &
|
||||
~BDRV_REQ_WRITE_COMPRESSED);
|
||||
if (ret < 0) {
|
||||
trace_block_copy_write_zeroes_fail(s, offset, ret);
|
||||
if (error_is_read) {
|
||||
*error_is_read = false;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (s->use_copy_range) {
|
||||
ret = bdrv_co_copy_range(s->source, start, s->target, start, nbytes,
|
||||
ret = bdrv_co_copy_range(s->source, offset, s->target, offset, nbytes,
|
||||
0, s->write_flags);
|
||||
if (ret < 0) {
|
||||
trace_block_copy_copy_range_fail(s, start, ret);
|
||||
trace_block_copy_copy_range_fail(s, offset, ret);
|
||||
s->use_copy_range = false;
|
||||
s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER);
|
||||
/* Fallback to read+write with allocated buffer */
|
||||
} else {
|
||||
if (s->use_copy_range) {
|
||||
/*
|
||||
* Successful copy-range. Now increase copy_size. copy_range
|
||||
* does not respect max_transfer (it's a TODO), so we factor
|
||||
* that in here.
|
||||
*
|
||||
* Note: we double-check s->use_copy_range for the case when
|
||||
* parallel block-copy request unsets it during previous
|
||||
* bdrv_co_copy_range call.
|
||||
*/
|
||||
s->copy_size =
|
||||
MIN(MAX(s->cluster_size, BLOCK_COPY_MAX_COPY_RANGE),
|
||||
QEMU_ALIGN_DOWN(block_copy_max_transfer(s->source,
|
||||
s->target),
|
||||
s->cluster_size));
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
@ -176,24 +312,27 @@ static int coroutine_fn block_copy_do_copy(BlockCopyState *s,
|
|||
/*
|
||||
* In case of failed copy_range request above, we may proceed with buffered
|
||||
* request larger than BLOCK_COPY_MAX_BUFFER. Still, further requests will
|
||||
* be properly limited, so don't care too much.
|
||||
* be properly limited, so don't care too much. Moreover the most likely
|
||||
* case (copy_range is unsupported for the configuration, so the very first
|
||||
* copy_range request fails) is handled by setting large copy_size only
|
||||
* after first successful copy_range.
|
||||
*/
|
||||
|
||||
bounce_buffer = qemu_blockalign(s->source->bs, nbytes);
|
||||
|
||||
ret = bdrv_co_pread(s->source, start, nbytes, bounce_buffer, 0);
|
||||
ret = bdrv_co_pread(s->source, offset, nbytes, bounce_buffer, 0);
|
||||
if (ret < 0) {
|
||||
trace_block_copy_read_fail(s, start, ret);
|
||||
trace_block_copy_read_fail(s, offset, ret);
|
||||
if (error_is_read) {
|
||||
*error_is_read = true;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = bdrv_co_pwrite(s->target, start, nbytes, bounce_buffer,
|
||||
ret = bdrv_co_pwrite(s->target, offset, nbytes, bounce_buffer,
|
||||
s->write_flags);
|
||||
if (ret < 0) {
|
||||
trace_block_copy_write_fail(s, start, ret);
|
||||
trace_block_copy_write_fail(s, offset, ret);
|
||||
if (error_is_read) {
|
||||
*error_is_read = false;
|
||||
}
|
||||
|
@ -206,6 +345,38 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int block_copy_block_status(BlockCopyState *s, int64_t offset,
|
||||
int64_t bytes, int64_t *pnum)
|
||||
{
|
||||
int64_t num;
|
||||
BlockDriverState *base;
|
||||
int ret;
|
||||
|
||||
if (s->skip_unallocated && s->source->bs->backing) {
|
||||
base = s->source->bs->backing->bs;
|
||||
} else {
|
||||
base = NULL;
|
||||
}
|
||||
|
||||
ret = bdrv_block_status_above(s->source->bs, base, offset, bytes, &num,
|
||||
NULL, NULL);
|
||||
if (ret < 0 || num < s->cluster_size) {
|
||||
/*
|
||||
* On error or if failed to obtain large enough chunk just fallback to
|
||||
* copy one cluster.
|
||||
*/
|
||||
num = s->cluster_size;
|
||||
ret = BDRV_BLOCK_ALLOCATED | BDRV_BLOCK_DATA;
|
||||
} else if (offset + num == s->len) {
|
||||
num = QEMU_ALIGN_UP(num, s->cluster_size);
|
||||
} else {
|
||||
num = QEMU_ALIGN_DOWN(num, s->cluster_size);
|
||||
}
|
||||
|
||||
*pnum = num;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the cluster starting at offset is allocated or not.
|
||||
* return via pnum the number of contiguous clusters sharing this allocation.
|
||||
|
@ -269,21 +440,28 @@ int64_t block_copy_reset_unallocated(BlockCopyState *s,
|
|||
|
||||
if (!ret) {
|
||||
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
|
||||
s->progress_reset_callback(s->progress_opaque);
|
||||
progress_set_remaining(s->progress,
|
||||
bdrv_get_dirty_count(s->copy_bitmap) +
|
||||
s->in_flight_bytes);
|
||||
}
|
||||
|
||||
*count = bytes;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int coroutine_fn block_copy(BlockCopyState *s,
|
||||
int64_t start, uint64_t bytes,
|
||||
bool *error_is_read)
|
||||
/*
|
||||
* block_copy_dirty_clusters
|
||||
*
|
||||
* Copy dirty clusters in @offset/@bytes range.
|
||||
* Returns 1 if dirty clusters found and successfully copied, 0 if no dirty
|
||||
* clusters found and -errno on failure.
|
||||
*/
|
||||
static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
|
||||
int64_t offset, int64_t bytes,
|
||||
bool *error_is_read)
|
||||
{
|
||||
int ret = 0;
|
||||
int64_t end = bytes + start; /* bytes */
|
||||
int64_t status_bytes;
|
||||
BlockCopyInFlightReq req;
|
||||
bool found_dirty = false;
|
||||
|
||||
/*
|
||||
* block_copy() user is responsible for keeping source and target in same
|
||||
|
@ -292,60 +470,109 @@ int coroutine_fn block_copy(BlockCopyState *s,
|
|||
assert(bdrv_get_aio_context(s->source->bs) ==
|
||||
bdrv_get_aio_context(s->target->bs));
|
||||
|
||||
assert(QEMU_IS_ALIGNED(start, s->cluster_size));
|
||||
assert(QEMU_IS_ALIGNED(end, s->cluster_size));
|
||||
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
|
||||
assert(QEMU_IS_ALIGNED(bytes, s->cluster_size));
|
||||
|
||||
block_copy_wait_inflight_reqs(s, start, bytes);
|
||||
block_copy_inflight_req_begin(s, &req, start, end);
|
||||
while (bytes) {
|
||||
BlockCopyInFlightReq req;
|
||||
int64_t next_zero, cur_bytes, status_bytes;
|
||||
|
||||
while (start < end) {
|
||||
int64_t next_zero, chunk_end;
|
||||
|
||||
if (!bdrv_dirty_bitmap_get(s->copy_bitmap, start)) {
|
||||
trace_block_copy_skip(s, start);
|
||||
start += s->cluster_size;
|
||||
if (!bdrv_dirty_bitmap_get(s->copy_bitmap, offset)) {
|
||||
trace_block_copy_skip(s, offset);
|
||||
offset += s->cluster_size;
|
||||
bytes -= s->cluster_size;
|
||||
continue; /* already copied */
|
||||
}
|
||||
|
||||
chunk_end = MIN(end, start + s->copy_size);
|
||||
found_dirty = true;
|
||||
|
||||
next_zero = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, start,
|
||||
chunk_end - start);
|
||||
cur_bytes = MIN(bytes, s->copy_size);
|
||||
|
||||
next_zero = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, offset,
|
||||
cur_bytes);
|
||||
if (next_zero >= 0) {
|
||||
assert(next_zero > start); /* start is dirty */
|
||||
assert(next_zero < chunk_end); /* no need to do MIN() */
|
||||
chunk_end = next_zero;
|
||||
assert(next_zero > offset); /* offset is dirty */
|
||||
assert(next_zero < offset + cur_bytes); /* no need to do MIN() */
|
||||
cur_bytes = next_zero - offset;
|
||||
}
|
||||
block_copy_inflight_req_begin(s, &req, offset, cur_bytes);
|
||||
|
||||
ret = block_copy_block_status(s, offset, cur_bytes, &status_bytes);
|
||||
assert(ret >= 0); /* never fail */
|
||||
cur_bytes = MIN(cur_bytes, status_bytes);
|
||||
block_copy_inflight_req_shrink(s, &req, cur_bytes);
|
||||
if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) {
|
||||
block_copy_inflight_req_end(s, &req, 0);
|
||||
progress_set_remaining(s->progress,
|
||||
bdrv_get_dirty_count(s->copy_bitmap) +
|
||||
s->in_flight_bytes);
|
||||
trace_block_copy_skip_range(s, offset, status_bytes);
|
||||
offset += status_bytes;
|
||||
bytes -= status_bytes;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (s->skip_unallocated) {
|
||||
ret = block_copy_reset_unallocated(s, start, &status_bytes);
|
||||
if (ret == 0) {
|
||||
trace_block_copy_skip_range(s, start, status_bytes);
|
||||
start += status_bytes;
|
||||
continue;
|
||||
}
|
||||
/* Clamp to known allocated region */
|
||||
chunk_end = MIN(chunk_end, start + status_bytes);
|
||||
}
|
||||
trace_block_copy_process(s, offset);
|
||||
|
||||
trace_block_copy_process(s, start);
|
||||
|
||||
bdrv_reset_dirty_bitmap(s->copy_bitmap, start, chunk_end - start);
|
||||
|
||||
co_get_from_shres(s->mem, chunk_end - start);
|
||||
ret = block_copy_do_copy(s, start, chunk_end, error_is_read);
|
||||
co_put_to_shres(s->mem, chunk_end - start);
|
||||
co_get_from_shres(s->mem, cur_bytes);
|
||||
ret = block_copy_do_copy(s, offset, cur_bytes, ret & BDRV_BLOCK_ZERO,
|
||||
error_is_read);
|
||||
co_put_to_shres(s->mem, cur_bytes);
|
||||
block_copy_inflight_req_end(s, &req, ret);
|
||||
if (ret < 0) {
|
||||
bdrv_set_dirty_bitmap(s->copy_bitmap, start, chunk_end - start);
|
||||
break;
|
||||
return ret;
|
||||
}
|
||||
|
||||
s->progress_bytes_callback(chunk_end - start, s->progress_opaque);
|
||||
start = chunk_end;
|
||||
ret = 0;
|
||||
progress_work_done(s->progress, cur_bytes);
|
||||
s->progress_bytes_callback(cur_bytes, s->progress_opaque);
|
||||
offset += cur_bytes;
|
||||
bytes -= cur_bytes;
|
||||
}
|
||||
|
||||
block_copy_inflight_req_end(&req);
|
||||
return found_dirty;
|
||||
}
|
||||
|
||||
/*
|
||||
* block_copy
|
||||
*
|
||||
* Copy requested region, accordingly to dirty bitmap.
|
||||
* Collaborate with parallel block_copy requests: if they succeed it will help
|
||||
* us. If they fail, we will retry not-copied regions. So, if we return error,
|
||||
* it means that some I/O operation failed in context of _this_ block_copy call,
|
||||
* not some parallel operation.
|
||||
*/
|
||||
int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes,
|
||||
bool *error_is_read)
|
||||
{
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = block_copy_dirty_clusters(s, offset, bytes, error_is_read);
|
||||
|
||||
if (ret == 0) {
|
||||
ret = block_copy_wait_one(s, offset, bytes);
|
||||
}
|
||||
|
||||
/*
|
||||
* We retry in two cases:
|
||||
* 1. Some progress done
|
||||
* Something was copied, which means that there were yield points
|
||||
* and some new dirty bits may have appeared (due to failed parallel
|
||||
* block-copy requests).
|
||||
* 2. We have waited for some intersecting block-copy request
|
||||
* It may have failed and produced new dirty bits.
|
||||
*/
|
||||
} while (ret > 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s)
|
||||
{
|
||||
return s->copy_bitmap;
|
||||
}
|
||||
|
||||
void block_copy_set_skip_unallocated(BlockCopyState *s, bool skip)
|
||||
{
|
||||
s->skip_unallocated = skip;
|
||||
}
|
||||
|
|
|
@ -43,27 +43,6 @@ typedef struct CommitBlockJob {
|
|||
char *backing_file_str;
|
||||
} CommitBlockJob;
|
||||
|
||||
static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base,
|
||||
int64_t offset, uint64_t bytes,
|
||||
void *buf)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
assert(bytes < SIZE_MAX);
|
||||
|
||||
ret = blk_co_pread(bs, offset, bytes, buf, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = blk_co_pwrite(base, offset, bytes, buf, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int commit_prepare(Job *job)
|
||||
{
|
||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
|
||||
|
@ -140,7 +119,6 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
|
|||
int ret = 0;
|
||||
int64_t n = 0; /* bytes */
|
||||
void *buf = NULL;
|
||||
int bytes_written = 0;
|
||||
int64_t len, base_len;
|
||||
|
||||
ret = len = blk_getlength(s->top);
|
||||
|
@ -165,6 +143,7 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
|
|||
|
||||
for (offset = 0; offset < len; offset += n) {
|
||||
bool copy;
|
||||
bool error_in_source = true;
|
||||
|
||||
/* Note that even when no rate limit is applied we need to yield
|
||||
* with no pending I/O here so that bdrv_drain_all() returns.
|
||||
|
@ -179,12 +158,20 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
|
|||
copy = (ret == 1);
|
||||
trace_commit_one_iteration(s, offset, n, ret);
|
||||
if (copy) {
|
||||
ret = commit_populate(s->top, s->base, offset, n, buf);
|
||||
bytes_written += n;
|
||||
assert(n < SIZE_MAX);
|
||||
|
||||
ret = blk_co_pread(s->top, offset, n, buf, 0);
|
||||
if (ret >= 0) {
|
||||
ret = blk_co_pwrite(s->base, offset, n, buf, 0);
|
||||
if (ret < 0) {
|
||||
error_in_source = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret < 0) {
|
||||
BlockErrorAction action =
|
||||
block_job_error_action(&s->common, false, s->on_error, -ret);
|
||||
block_job_error_action(&s->common, s->on_error,
|
||||
error_in_source, -ret);
|
||||
if (action == BLOCK_ERROR_ACTION_REPORT) {
|
||||
goto out;
|
||||
} else {
|
||||
|
|
|
@ -118,13 +118,6 @@ static void cor_lock_medium(BlockDriverState *bs, bool locked)
|
|||
}
|
||||
|
||||
|
||||
static bool cor_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||
BlockDriverState *candidate)
|
||||
{
|
||||
return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
|
||||
}
|
||||
|
||||
|
||||
static BlockDriver bdrv_copy_on_read = {
|
||||
.format_name = "copy-on-read",
|
||||
|
||||
|
@ -143,8 +136,6 @@ static BlockDriver bdrv_copy_on_read = {
|
|||
|
||||
.bdrv_co_block_status = bdrv_co_block_status_from_file,
|
||||
|
||||
.bdrv_recurse_is_first_non_filter = cor_recurse_is_first_non_filter,
|
||||
|
||||
.has_variable_length = true,
|
||||
.is_filter = true,
|
||||
};
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "qapi/error.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "crypto.h"
|
||||
|
||||
typedef struct BlockCrypto BlockCrypto;
|
||||
|
@ -484,6 +485,67 @@ static int64_t block_crypto_getlength(BlockDriverState *bs)
|
|||
}
|
||||
|
||||
|
||||
static BlockMeasureInfo *block_crypto_measure(QemuOpts *opts,
|
||||
BlockDriverState *in_bs,
|
||||
Error **errp)
|
||||
{
|
||||
g_autoptr(QCryptoBlockCreateOptions) create_opts = NULL;
|
||||
Error *local_err = NULL;
|
||||
BlockMeasureInfo *info;
|
||||
uint64_t size;
|
||||
size_t luks_payload_size;
|
||||
QDict *cryptoopts;
|
||||
|
||||
/*
|
||||
* Preallocation mode doesn't affect size requirements but we must consume
|
||||
* the option.
|
||||
*/
|
||||
g_free(qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC));
|
||||
|
||||
size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
|
||||
|
||||
if (in_bs) {
|
||||
int64_t ssize = bdrv_getlength(in_bs);
|
||||
|
||||
if (ssize < 0) {
|
||||
error_setg_errno(&local_err, -ssize,
|
||||
"Unable to get image virtual_size");
|
||||
goto err;
|
||||
}
|
||||
|
||||
size = ssize;
|
||||
}
|
||||
|
||||
cryptoopts = qemu_opts_to_qdict_filtered(opts, NULL,
|
||||
&block_crypto_create_opts_luks, true);
|
||||
qdict_put_str(cryptoopts, "format", "luks");
|
||||
create_opts = block_crypto_create_opts_init(cryptoopts, &local_err);
|
||||
qobject_unref(cryptoopts);
|
||||
if (!create_opts) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!qcrypto_block_calculate_payload_offset(create_opts, NULL,
|
||||
&luks_payload_size,
|
||||
&local_err)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unallocated blocks are still encrypted so allocation status makes no
|
||||
* difference to the file size.
|
||||
*/
|
||||
info = g_new(BlockMeasureInfo, 1);
|
||||
info->fully_allocated = luks_payload_size + size;
|
||||
info->required = luks_payload_size + size;
|
||||
return info;
|
||||
|
||||
err:
|
||||
error_propagate(errp, local_err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int block_crypto_probe_luks(const uint8_t *buf,
|
||||
int buf_size,
|
||||
const char *filename) {
|
||||
|
@ -539,7 +601,8 @@ fail:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
|
||||
static int coroutine_fn block_crypto_co_create_opts_luks(BlockDriver *drv,
|
||||
const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
|
@ -596,6 +659,23 @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
|
|||
|
||||
ret = 0;
|
||||
fail:
|
||||
/*
|
||||
* If an error occurred, delete 'filename'. Even if the file existed
|
||||
* beforehand, it has been truncated and corrupted in the process.
|
||||
*/
|
||||
if (ret && bs) {
|
||||
Error *local_delete_err = NULL;
|
||||
int r_del = bdrv_co_delete_file(bs, &local_delete_err);
|
||||
/*
|
||||
* ENOTSUP will happen if the block driver doesn't support
|
||||
* the 'bdrv_co_delete_file' interface. This is a predictable
|
||||
* scenario and shouldn't be reported back to the user.
|
||||
*/
|
||||
if ((r_del < 0) && (r_del != -ENOTSUP)) {
|
||||
error_report_err(local_delete_err);
|
||||
}
|
||||
}
|
||||
|
||||
bdrv_unref(bs);
|
||||
qapi_free_QCryptoBlockCreateOptions(create_opts);
|
||||
qobject_unref(cryptoopts);
|
||||
|
@ -670,6 +750,7 @@ static BlockDriver bdrv_crypto_luks = {
|
|||
.bdrv_co_preadv = block_crypto_co_preadv,
|
||||
.bdrv_co_pwritev = block_crypto_co_pwritev,
|
||||
.bdrv_getlength = block_crypto_getlength,
|
||||
.bdrv_measure = block_crypto_measure,
|
||||
.bdrv_get_info = block_crypto_get_info_luks,
|
||||
.bdrv_get_specific_info = block_crypto_get_specific_info_luks,
|
||||
|
||||
|
|
32
block/curl.c
32
block/curl.c
|
@ -214,11 +214,35 @@ static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
|
|||
{
|
||||
BDRVCURLState *s = opaque;
|
||||
size_t realsize = size * nmemb;
|
||||
const char *accept_line = "Accept-Ranges: bytes";
|
||||
const char *header = (char *)ptr;
|
||||
const char *end = header + realsize;
|
||||
const char *accept_ranges = "accept-ranges:";
|
||||
const char *bytes = "bytes";
|
||||
|
||||
if (realsize >= strlen(accept_line)
|
||||
&& strncmp((char *)ptr, accept_line, strlen(accept_line)) == 0) {
|
||||
s->accept_range = true;
|
||||
if (realsize >= strlen(accept_ranges)
|
||||
&& g_ascii_strncasecmp(header, accept_ranges,
|
||||
strlen(accept_ranges)) == 0) {
|
||||
|
||||
char *p = strchr(header, ':') + 1;
|
||||
|
||||
/* Skip whitespace between the header name and value. */
|
||||
while (p < end && *p && g_ascii_isspace(*p)) {
|
||||
p++;
|
||||
}
|
||||
|
||||
if (end - p >= strlen(bytes)
|
||||
&& strncmp(p, bytes, strlen(bytes)) == 0) {
|
||||
|
||||
/* Check that there is nothing but whitespace after the value. */
|
||||
p += strlen(bytes);
|
||||
while (p < end && *p && g_ascii_isspace(*p)) {
|
||||
p++;
|
||||
}
|
||||
|
||||
if (p == end || !*p) {
|
||||
s->accept_range = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return realsize;
|
||||
|
|
|
@ -860,16 +860,24 @@ char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp)
|
|||
return hbitmap_sha256(bitmap->bitmap, errp);
|
||||
}
|
||||
|
||||
int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset,
|
||||
uint64_t bytes)
|
||||
int64_t bdrv_dirty_bitmap_next_dirty(BdrvDirtyBitmap *bitmap, int64_t offset,
|
||||
int64_t bytes)
|
||||
{
|
||||
return hbitmap_next_dirty(bitmap->bitmap, offset, bytes);
|
||||
}
|
||||
|
||||
int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, int64_t offset,
|
||||
int64_t bytes)
|
||||
{
|
||||
return hbitmap_next_zero(bitmap->bitmap, offset, bytes);
|
||||
}
|
||||
|
||||
bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap,
|
||||
uint64_t *offset, uint64_t *bytes)
|
||||
int64_t start, int64_t end, int64_t max_dirty_count,
|
||||
int64_t *dirty_start, int64_t *dirty_count)
|
||||
{
|
||||
return hbitmap_next_dirty_area(bitmap->bitmap, offset, bytes);
|
||||
return hbitmap_next_dirty_area(bitmap->bitmap, start, end, max_dirty_count,
|
||||
dirty_start, dirty_count);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -156,6 +156,7 @@ typedef struct BDRVRawState {
|
|||
bool has_write_zeroes:1;
|
||||
bool discard_zeroes:1;
|
||||
bool use_linux_aio:1;
|
||||
bool use_linux_io_uring:1;
|
||||
bool page_cache_inconsistent:1;
|
||||
bool has_fallocate;
|
||||
bool needs_alignment;
|
||||
|
@ -444,7 +445,7 @@ static QemuOptsList raw_runtime_opts = {
|
|||
{
|
||||
.name = "aio",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "host AIO implementation (threads, native)",
|
||||
.help = "host AIO implementation (threads, native, io_uring)",
|
||||
},
|
||||
{
|
||||
.name = "locking",
|
||||
|
@ -503,9 +504,16 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
aio_default = (bdrv_flags & BDRV_O_NATIVE_AIO)
|
||||
? BLOCKDEV_AIO_OPTIONS_NATIVE
|
||||
: BLOCKDEV_AIO_OPTIONS_THREADS;
|
||||
if (bdrv_flags & BDRV_O_NATIVE_AIO) {
|
||||
aio_default = BLOCKDEV_AIO_OPTIONS_NATIVE;
|
||||
#ifdef CONFIG_LINUX_IO_URING
|
||||
} else if (bdrv_flags & BDRV_O_IO_URING) {
|
||||
aio_default = BLOCKDEV_AIO_OPTIONS_IO_URING;
|
||||
#endif
|
||||
} else {
|
||||
aio_default = BLOCKDEV_AIO_OPTIONS_THREADS;
|
||||
}
|
||||
|
||||
aio = qapi_enum_parse(&BlockdevAioOptions_lookup,
|
||||
qemu_opt_get(opts, "aio"),
|
||||
aio_default, &local_err);
|
||||
|
@ -514,7 +522,11 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
|||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->use_linux_aio = (aio == BLOCKDEV_AIO_OPTIONS_NATIVE);
|
||||
#ifdef CONFIG_LINUX_IO_URING
|
||||
s->use_linux_io_uring = (aio == BLOCKDEV_AIO_OPTIONS_IO_URING);
|
||||
#endif
|
||||
|
||||
locking = qapi_enum_parse(&OnOffAuto_lookup,
|
||||
qemu_opt_get(opts, "locking"),
|
||||
|
@ -600,6 +612,22 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
|||
}
|
||||
#endif /* !defined(CONFIG_LINUX_AIO) */
|
||||
|
||||
#ifdef CONFIG_LINUX_IO_URING
|
||||
if (s->use_linux_io_uring) {
|
||||
if (!aio_setup_linux_io_uring(bdrv_get_aio_context(bs), errp)) {
|
||||
error_prepend(errp, "Unable to use io_uring: ");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (s->use_linux_io_uring) {
|
||||
error_setg(errp, "aio=io_uring was specified, but is not supported "
|
||||
"in this build.");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
#endif /* !defined(CONFIG_LINUX_IO_URING) */
|
||||
|
||||
s->has_discard = true;
|
||||
s->has_write_zeroes = true;
|
||||
if ((bs->open_flags & BDRV_O_NOCACHE) != 0) {
|
||||
|
@ -860,7 +888,6 @@ static int raw_handle_perm_lock(BlockDriverState *bs,
|
|||
"Is another process using the image [%s]?\n",
|
||||
bs->filename);
|
||||
}
|
||||
op = RAW_PL_ABORT;
|
||||
/* fall through to unlock bytes. */
|
||||
case RAW_PL_ABORT:
|
||||
raw_apply_lock_bytes(s, s->fd, s->perm, ~s->shared_perm,
|
||||
|
@ -1877,21 +1904,25 @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset,
|
|||
return -EIO;
|
||||
|
||||
/*
|
||||
* Check if the underlying device requires requests to be aligned,
|
||||
* and if the request we are trying to submit is aligned or not.
|
||||
* If this is the case tell the low-level driver that it needs
|
||||
* to copy the buffer.
|
||||
* When using O_DIRECT, the request must be aligned to be able to use
|
||||
* either libaio or io_uring interface. If not fail back to regular thread
|
||||
* pool read/write code which emulates this for us if we
|
||||
* set QEMU_AIO_MISALIGNED.
|
||||
*/
|
||||
if (s->needs_alignment) {
|
||||
if (!bdrv_qiov_is_aligned(bs, qiov)) {
|
||||
type |= QEMU_AIO_MISALIGNED;
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
} else if (s->use_linux_aio) {
|
||||
LinuxAioState *aio = aio_get_linux_aio(bdrv_get_aio_context(bs));
|
||||
assert(qiov->size == bytes);
|
||||
return laio_co_submit(bs, aio, s->fd, offset, qiov, type);
|
||||
if (s->needs_alignment && !bdrv_qiov_is_aligned(bs, qiov)) {
|
||||
type |= QEMU_AIO_MISALIGNED;
|
||||
#ifdef CONFIG_LINUX_IO_URING
|
||||
} else if (s->use_linux_io_uring) {
|
||||
LuringState *aio = aio_get_linux_io_uring(bdrv_get_aio_context(bs));
|
||||
assert(qiov->size == bytes);
|
||||
return luring_co_submit(bs, aio, s->fd, offset, qiov, type);
|
||||
#endif
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
} else if (s->use_linux_aio) {
|
||||
LinuxAioState *aio = aio_get_linux_aio(bdrv_get_aio_context(bs));
|
||||
assert(qiov->size == bytes);
|
||||
return laio_co_submit(bs, aio, s->fd, offset, qiov, type);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
acb = (RawPosixAIOData) {
|
||||
|
@ -1927,24 +1958,36 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
|||
|
||||
static void raw_aio_plug(BlockDriverState *bs)
|
||||
{
|
||||
BDRVRawState __attribute__((unused)) *s = bs->opaque;
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
BDRVRawState *s = bs->opaque;
|
||||
if (s->use_linux_aio) {
|
||||
LinuxAioState *aio = aio_get_linux_aio(bdrv_get_aio_context(bs));
|
||||
laio_io_plug(bs, aio);
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_LINUX_IO_URING
|
||||
if (s->use_linux_io_uring) {
|
||||
LuringState *aio = aio_get_linux_io_uring(bdrv_get_aio_context(bs));
|
||||
luring_io_plug(bs, aio);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void raw_aio_unplug(BlockDriverState *bs)
|
||||
{
|
||||
BDRVRawState __attribute__((unused)) *s = bs->opaque;
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
BDRVRawState *s = bs->opaque;
|
||||
if (s->use_linux_aio) {
|
||||
LinuxAioState *aio = aio_get_linux_aio(bdrv_get_aio_context(bs));
|
||||
laio_io_unplug(bs, aio);
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_LINUX_IO_URING
|
||||
if (s->use_linux_io_uring) {
|
||||
LuringState *aio = aio_get_linux_io_uring(bdrv_get_aio_context(bs));
|
||||
luring_io_unplug(bs, aio);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int raw_co_flush_to_disk(BlockDriverState *bs)
|
||||
|
@ -1964,14 +2007,20 @@ static int raw_co_flush_to_disk(BlockDriverState *bs)
|
|||
.aio_type = QEMU_AIO_FLUSH,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_LINUX_IO_URING
|
||||
if (s->use_linux_io_uring) {
|
||||
LuringState *aio = aio_get_linux_io_uring(bdrv_get_aio_context(bs));
|
||||
return luring_co_submit(bs, aio, s->fd, 0, NULL, QEMU_AIO_FLUSH);
|
||||
}
|
||||
#endif
|
||||
return raw_thread_pool_submit(bs, handle_aiocb_flush, &acb);
|
||||
}
|
||||
|
||||
static void raw_aio_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context)
|
||||
{
|
||||
BDRVRawState __attribute__((unused)) *s = bs->opaque;
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
BDRVRawState *s = bs->opaque;
|
||||
if (s->use_linux_aio) {
|
||||
Error *local_err = NULL;
|
||||
if (!aio_setup_linux_aio(new_context, &local_err)) {
|
||||
|
@ -1981,6 +2030,16 @@ static void raw_aio_attach_aio_context(BlockDriverState *bs,
|
|||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_LINUX_IO_URING
|
||||
if (s->use_linux_io_uring) {
|
||||
Error *local_err;
|
||||
if (!aio_setup_linux_io_uring(new_context, &local_err)) {
|
||||
error_reportf_err(local_err, "Unable to use linux io_uring, "
|
||||
"falling back to thread pool: ");
|
||||
s->use_linux_io_uring = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void raw_close(BlockDriverState *bs)
|
||||
|
@ -2346,7 +2405,9 @@ out:
|
|||
return result;
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
static int coroutine_fn raw_co_create_opts(BlockDriver *drv,
|
||||
const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions options;
|
||||
|
@ -2386,6 +2447,28 @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
|
|||
return raw_co_create(&options, errp);
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_delete_file(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
{
|
||||
struct stat st;
|
||||
int ret;
|
||||
|
||||
if (!(stat(bs->filename, &st) == 0) || !S_ISREG(st.st_mode)) {
|
||||
error_setg_errno(errp, ENOENT, "%s is not a regular file",
|
||||
bs->filename);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ret = unlink(bs->filename);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
error_setg_errno(errp, -ret, "Error when deleting file %s",
|
||||
bs->filename);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find allocation range in @bs around offset @start.
|
||||
* May change underlying file descriptor's file offset.
|
||||
|
@ -2753,7 +2836,6 @@ raw_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes,
|
|||
req->overlap_bytes = req->bytes;
|
||||
|
||||
bdrv_mark_request_serialising(req, bs->bl.request_alignment);
|
||||
bdrv_wait_serialising_requests(req);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -3017,6 +3099,7 @@ BlockDriver bdrv_file = {
|
|||
.bdrv_co_block_status = raw_co_block_status,
|
||||
.bdrv_co_invalidate_cache = raw_co_invalidate_cache,
|
||||
.bdrv_co_pwrite_zeroes = raw_co_pwrite_zeroes,
|
||||
.bdrv_co_delete_file = raw_co_delete_file,
|
||||
|
||||
.bdrv_co_preadv = raw_co_preadv,
|
||||
.bdrv_co_pwritev = raw_co_pwritev,
|
||||
|
@ -3418,67 +3501,6 @@ static coroutine_fn int hdev_co_pwrite_zeroes(BlockDriverState *bs,
|
|||
return raw_do_pwrite_zeroes(bs, offset, bytes, flags, true);
|
||||
}
|
||||
|
||||
static int coroutine_fn hdev_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
int fd;
|
||||
int ret = 0;
|
||||
struct stat stat_buf;
|
||||
int64_t total_size = 0;
|
||||
bool has_prefix;
|
||||
|
||||
/* This function is used by both protocol block drivers and therefore either
|
||||
* of these prefixes may be given.
|
||||
* The return value has to be stored somewhere, otherwise this is an error
|
||||
* due to -Werror=unused-value. */
|
||||
has_prefix =
|
||||
strstart(filename, "host_device:", &filename) ||
|
||||
strstart(filename, "host_cdrom:" , &filename);
|
||||
|
||||
(void)has_prefix;
|
||||
|
||||
ret = raw_normalize_devicepath(&filename, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read out options */
|
||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
|
||||
fd = qemu_open(filename, O_WRONLY | O_BINARY);
|
||||
if (fd < 0) {
|
||||
ret = -errno;
|
||||
error_setg_errno(errp, -ret, "Could not open device");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (fstat(fd, &stat_buf) < 0) {
|
||||
ret = -errno;
|
||||
error_setg_errno(errp, -ret, "Could not stat device");
|
||||
} else if (!S_ISBLK(stat_buf.st_mode) && !S_ISCHR(stat_buf.st_mode)) {
|
||||
error_setg(errp,
|
||||
"The given file is neither a block nor a character device");
|
||||
ret = -ENODEV;
|
||||
} else if (lseek(fd, 0, SEEK_END) < total_size) {
|
||||
error_setg(errp, "Device is too small");
|
||||
ret = -ENOSPC;
|
||||
}
|
||||
|
||||
if (!ret && total_size) {
|
||||
uint8_t buf[BDRV_SECTOR_SIZE] = { 0 };
|
||||
int64_t zero_size = MIN(BDRV_SECTOR_SIZE, total_size);
|
||||
if (lseek(fd, 0, SEEK_SET) == -1) {
|
||||
ret = -errno;
|
||||
} else {
|
||||
ret = qemu_write_full(fd, buf, zero_size);
|
||||
ret = ret == zero_size ? 0 : -errno;
|
||||
}
|
||||
}
|
||||
qemu_close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_host_device = {
|
||||
.format_name = "host_device",
|
||||
.protocol_name = "host_device",
|
||||
|
@ -3491,8 +3513,8 @@ static BlockDriver bdrv_host_device = {
|
|||
.bdrv_reopen_prepare = raw_reopen_prepare,
|
||||
.bdrv_reopen_commit = raw_reopen_commit,
|
||||
.bdrv_reopen_abort = raw_reopen_abort,
|
||||
.bdrv_co_create_opts = hdev_co_create_opts,
|
||||
.create_opts = &raw_create_opts,
|
||||
.bdrv_co_create_opts = bdrv_co_create_opts_simple,
|
||||
.create_opts = &bdrv_create_opts_simple,
|
||||
.mutable_opts = mutable_opts,
|
||||
.bdrv_co_invalidate_cache = raw_co_invalidate_cache,
|
||||
.bdrv_co_pwrite_zeroes = hdev_co_pwrite_zeroes,
|
||||
|
@ -3619,12 +3641,11 @@ static BlockDriver bdrv_host_cdrom = {
|
|||
.bdrv_reopen_prepare = raw_reopen_prepare,
|
||||
.bdrv_reopen_commit = raw_reopen_commit,
|
||||
.bdrv_reopen_abort = raw_reopen_abort,
|
||||
.bdrv_co_create_opts = hdev_co_create_opts,
|
||||
.create_opts = &raw_create_opts,
|
||||
.bdrv_co_create_opts = bdrv_co_create_opts_simple,
|
||||
.create_opts = &bdrv_create_opts_simple,
|
||||
.mutable_opts = mutable_opts,
|
||||
.bdrv_co_invalidate_cache = raw_co_invalidate_cache,
|
||||
|
||||
|
||||
.bdrv_co_preadv = raw_co_preadv,
|
||||
.bdrv_co_pwritev = raw_co_pwritev,
|
||||
.bdrv_co_flush_to_disk = raw_co_flush_to_disk,
|
||||
|
@ -3753,8 +3774,8 @@ static BlockDriver bdrv_host_cdrom = {
|
|||
.bdrv_reopen_prepare = raw_reopen_prepare,
|
||||
.bdrv_reopen_commit = raw_reopen_commit,
|
||||
.bdrv_reopen_abort = raw_reopen_abort,
|
||||
.bdrv_co_create_opts = hdev_co_create_opts,
|
||||
.create_opts = &raw_create_opts,
|
||||
.bdrv_co_create_opts = bdrv_co_create_opts_simple,
|
||||
.create_opts = &bdrv_create_opts_simple,
|
||||
.mutable_opts = mutable_opts,
|
||||
|
||||
.bdrv_co_preadv = raw_co_preadv,
|
||||
|
|
|
@ -588,7 +588,9 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
static int coroutine_fn raw_co_create_opts(BlockDriver *drv,
|
||||
const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions options;
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Compress filter block driver
|
||||
*
|
||||
* Copyright (c) 2019 Virtuozzo International GmbH
|
||||
*
|
||||
* Author:
|
||||
* Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
|
||||
* (based on block/copy-on-read.c by Max Reitz)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 or
|
||||
* (at your option) any later version of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
|
||||
static int compress_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false,
|
||||
errp);
|
||||
if (!bs->file) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!bs->file->bs->drv || !block_driver_can_compress(bs->file->bs->drv)) {
|
||||
error_setg(errp,
|
||||
"Compression is not supported for underlying format: %s",
|
||||
bdrv_get_format_name(bs->file->bs) ?: "(no format)");
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||
(BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
|
||||
|
||||
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
|
||||
bs->file->bs->supported_zero_flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int64_t compress_getlength(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_getlength(bs->file->bs);
|
||||
}
|
||||
|
||||
|
||||
static int coroutine_fn compress_co_preadv_part(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov,
|
||||
size_t qiov_offset,
|
||||
int flags)
|
||||
{
|
||||
return bdrv_co_preadv_part(bs->file, offset, bytes, qiov, qiov_offset,
|
||||
flags);
|
||||
}
|
||||
|
||||
|
||||
static int coroutine_fn compress_co_pwritev_part(BlockDriverState *bs,
|
||||
uint64_t offset,
|
||||
uint64_t bytes,
|
||||
QEMUIOVector *qiov,
|
||||
size_t qiov_offset, int flags)
|
||||
{
|
||||
return bdrv_co_pwritev_part(bs->file, offset, bytes, qiov, qiov_offset,
|
||||
flags | BDRV_REQ_WRITE_COMPRESSED);
|
||||
}
|
||||
|
||||
|
||||
static int coroutine_fn compress_co_pwrite_zeroes(BlockDriverState *bs,
|
||||
int64_t offset, int bytes,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
|
||||
}
|
||||
|
||||
|
||||
static int coroutine_fn compress_co_pdiscard(BlockDriverState *bs,
|
||||
int64_t offset, int bytes)
|
||||
{
|
||||
return bdrv_co_pdiscard(bs->file, offset, bytes);
|
||||
}
|
||||
|
||||
|
||||
static void compress_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BlockDriverInfo bdi;
|
||||
int ret;
|
||||
|
||||
if (!bs->file) {
|
||||
return;
|
||||
}
|
||||
|
||||
ret = bdrv_get_info(bs->file->bs, &bdi);
|
||||
if (ret < 0 || bdi.cluster_size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bs->bl.request_alignment = bdi.cluster_size;
|
||||
}
|
||||
|
||||
|
||||
static void compress_eject(BlockDriverState *bs, bool eject_flag)
|
||||
{
|
||||
bdrv_eject(bs->file->bs, eject_flag);
|
||||
}
|
||||
|
||||
|
||||
static void compress_lock_medium(BlockDriverState *bs, bool locked)
|
||||
{
|
||||
bdrv_lock_medium(bs->file->bs, locked);
|
||||
}
|
||||
|
||||
|
||||
static BlockDriver bdrv_compress = {
|
||||
.format_name = "compress",
|
||||
|
||||
.bdrv_open = compress_open,
|
||||
.bdrv_child_perm = bdrv_filter_default_perms,
|
||||
|
||||
.bdrv_getlength = compress_getlength,
|
||||
|
||||
.bdrv_co_preadv_part = compress_co_preadv_part,
|
||||
.bdrv_co_pwritev_part = compress_co_pwritev_part,
|
||||
.bdrv_co_pwrite_zeroes = compress_co_pwrite_zeroes,
|
||||
.bdrv_co_pdiscard = compress_co_pdiscard,
|
||||
.bdrv_refresh_limits = compress_refresh_limits,
|
||||
|
||||
.bdrv_eject = compress_eject,
|
||||
.bdrv_lock_medium = compress_lock_medium,
|
||||
|
||||
.bdrv_co_block_status = bdrv_co_block_status_from_file,
|
||||
|
||||
.has_variable_length = true,
|
||||
.is_filter = true,
|
||||
};
|
||||
|
||||
static void bdrv_compress_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_compress);
|
||||
}
|
||||
|
||||
block_init(bdrv_compress_init);
|
|
@ -1130,7 +1130,8 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn qemu_gluster_co_create_opts(const char *filename,
|
||||
static int coroutine_fn qemu_gluster_co_create_opts(BlockDriver *drv,
|
||||
const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
|
|
192
block/io.c
192
block/io.c
|
@ -715,12 +715,69 @@ static void tracked_request_begin(BdrvTrackedRequest *req,
|
|||
qemu_co_mutex_unlock(&bs->reqs_lock);
|
||||
}
|
||||
|
||||
void bdrv_mark_request_serialising(BdrvTrackedRequest *req, uint64_t align)
|
||||
static bool tracked_request_overlaps(BdrvTrackedRequest *req,
|
||||
int64_t offset, uint64_t bytes)
|
||||
{
|
||||
/* aaaa bbbb */
|
||||
if (offset >= req->overlap_offset + req->overlap_bytes) {
|
||||
return false;
|
||||
}
|
||||
/* bbbb aaaa */
|
||||
if (req->overlap_offset >= offset + bytes) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool coroutine_fn
|
||||
bdrv_wait_serialising_requests_locked(BlockDriverState *bs,
|
||||
BdrvTrackedRequest *self)
|
||||
{
|
||||
BdrvTrackedRequest *req;
|
||||
bool retry;
|
||||
bool waited = false;
|
||||
|
||||
do {
|
||||
retry = false;
|
||||
QLIST_FOREACH(req, &bs->tracked_requests, list) {
|
||||
if (req == self || (!req->serialising && !self->serialising)) {
|
||||
continue;
|
||||
}
|
||||
if (tracked_request_overlaps(req, self->overlap_offset,
|
||||
self->overlap_bytes))
|
||||
{
|
||||
/* Hitting this means there was a reentrant request, for
|
||||
* example, a block driver issuing nested requests. This must
|
||||
* never happen since it means deadlock.
|
||||
*/
|
||||
assert(qemu_coroutine_self() != req->co);
|
||||
|
||||
/* If the request is already (indirectly) waiting for us, or
|
||||
* will wait for us as soon as it wakes up, then just go on
|
||||
* (instead of producing a deadlock in the former case). */
|
||||
if (!req->waiting_for) {
|
||||
self->waiting_for = req;
|
||||
qemu_co_queue_wait(&req->wait_queue, &bs->reqs_lock);
|
||||
self->waiting_for = NULL;
|
||||
retry = true;
|
||||
waited = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (retry);
|
||||
return waited;
|
||||
}
|
||||
|
||||
bool bdrv_mark_request_serialising(BdrvTrackedRequest *req, uint64_t align)
|
||||
{
|
||||
BlockDriverState *bs = req->bs;
|
||||
int64_t overlap_offset = req->offset & ~(align - 1);
|
||||
uint64_t overlap_bytes = ROUND_UP(req->offset + req->bytes, align)
|
||||
- overlap_offset;
|
||||
bool waited;
|
||||
|
||||
qemu_co_mutex_lock(&bs->reqs_lock);
|
||||
if (!req->serialising) {
|
||||
atomic_inc(&req->bs->serialising_in_flight);
|
||||
req->serialising = true;
|
||||
|
@ -728,18 +785,9 @@ void bdrv_mark_request_serialising(BdrvTrackedRequest *req, uint64_t align)
|
|||
|
||||
req->overlap_offset = MIN(req->overlap_offset, overlap_offset);
|
||||
req->overlap_bytes = MAX(req->overlap_bytes, overlap_bytes);
|
||||
}
|
||||
|
||||
static bool is_request_serialising_and_aligned(BdrvTrackedRequest *req)
|
||||
{
|
||||
/*
|
||||
* If the request is serialising, overlap_offset and overlap_bytes are set,
|
||||
* so we can check if the request is aligned. Otherwise, don't care and
|
||||
* return false.
|
||||
*/
|
||||
|
||||
return req->serialising && (req->offset == req->overlap_offset) &&
|
||||
(req->bytes == req->overlap_bytes);
|
||||
waited = bdrv_wait_serialising_requests_locked(bs, req);
|
||||
qemu_co_mutex_unlock(&bs->reqs_lock);
|
||||
return waited;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -793,20 +841,6 @@ static int bdrv_get_cluster_size(BlockDriverState *bs)
|
|||
}
|
||||
}
|
||||
|
||||
static bool tracked_request_overlaps(BdrvTrackedRequest *req,
|
||||
int64_t offset, uint64_t bytes)
|
||||
{
|
||||
/* aaaa bbbb */
|
||||
if (offset >= req->overlap_offset + req->overlap_bytes) {
|
||||
return false;
|
||||
}
|
||||
/* bbbb aaaa */
|
||||
if (req->overlap_offset >= offset + bytes) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void bdrv_inc_in_flight(BlockDriverState *bs)
|
||||
{
|
||||
atomic_inc(&bs->in_flight);
|
||||
|
@ -823,48 +857,18 @@ void bdrv_dec_in_flight(BlockDriverState *bs)
|
|||
bdrv_wakeup(bs);
|
||||
}
|
||||
|
||||
bool coroutine_fn bdrv_wait_serialising_requests(BdrvTrackedRequest *self)
|
||||
static bool coroutine_fn bdrv_wait_serialising_requests(BdrvTrackedRequest *self)
|
||||
{
|
||||
BlockDriverState *bs = self->bs;
|
||||
BdrvTrackedRequest *req;
|
||||
bool retry;
|
||||
bool waited = false;
|
||||
|
||||
if (!atomic_read(&bs->serialising_in_flight)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
retry = false;
|
||||
qemu_co_mutex_lock(&bs->reqs_lock);
|
||||
QLIST_FOREACH(req, &bs->tracked_requests, list) {
|
||||
if (req == self || (!req->serialising && !self->serialising)) {
|
||||
continue;
|
||||
}
|
||||
if (tracked_request_overlaps(req, self->overlap_offset,
|
||||
self->overlap_bytes))
|
||||
{
|
||||
/* Hitting this means there was a reentrant request, for
|
||||
* example, a block driver issuing nested requests. This must
|
||||
* never happen since it means deadlock.
|
||||
*/
|
||||
assert(qemu_coroutine_self() != req->co);
|
||||
|
||||
/* If the request is already (indirectly) waiting for us, or
|
||||
* will wait for us as soon as it wakes up, then just go on
|
||||
* (instead of producing a deadlock in the former case). */
|
||||
if (!req->waiting_for) {
|
||||
self->waiting_for = req;
|
||||
qemu_co_queue_wait(&req->wait_queue, &bs->reqs_lock);
|
||||
self->waiting_for = NULL;
|
||||
retry = true;
|
||||
waited = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
qemu_co_mutex_unlock(&bs->reqs_lock);
|
||||
} while (retry);
|
||||
qemu_co_mutex_lock(&bs->reqs_lock);
|
||||
waited = bdrv_wait_serialising_requests_locked(bs, self);
|
||||
qemu_co_mutex_unlock(&bs->reqs_lock);
|
||||
|
||||
return waited;
|
||||
}
|
||||
|
@ -1395,7 +1399,7 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
|
|||
if (!(flags & BDRV_REQ_PREFETCH)) {
|
||||
qemu_iovec_from_buf(qiov, qiov_offset + progress,
|
||||
bounce_buffer + skip_bytes,
|
||||
pnum - skip_bytes);
|
||||
MIN(pnum - skip_bytes, bytes - progress));
|
||||
}
|
||||
} else if (!(flags & BDRV_REQ_PREFETCH)) {
|
||||
/* Read directly into the destination */
|
||||
|
@ -1445,8 +1449,7 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
|
|||
* potential fallback support, if we ever implement any read flags
|
||||
* to pass through to drivers. For now, there aren't any
|
||||
* passthrough flags. */
|
||||
assert(!(flags & ~(BDRV_REQ_NO_SERIALISING | BDRV_REQ_COPY_ON_READ |
|
||||
BDRV_REQ_PREFETCH)));
|
||||
assert(!(flags & ~(BDRV_REQ_COPY_ON_READ | BDRV_REQ_PREFETCH)));
|
||||
|
||||
/* Handle Copy on Read and associated serialisation */
|
||||
if (flags & BDRV_REQ_COPY_ON_READ) {
|
||||
|
@ -1456,12 +1459,7 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
|
|||
* it ensures that the CoR read and write operations are atomic and
|
||||
* guest writes cannot interleave between them. */
|
||||
bdrv_mark_request_serialising(req, bdrv_get_cluster_size(bs));
|
||||
}
|
||||
|
||||
/* BDRV_REQ_SERIALISING is only for write operation */
|
||||
assert(!(flags & BDRV_REQ_SERIALISING));
|
||||
|
||||
if (!(flags & BDRV_REQ_NO_SERIALISING)) {
|
||||
} else {
|
||||
bdrv_wait_serialising_requests(req);
|
||||
}
|
||||
|
||||
|
@ -1567,10 +1565,12 @@ static bool bdrv_init_padding(BlockDriverState *bs,
|
|||
pad->tail = align - pad->tail;
|
||||
}
|
||||
|
||||
if ((!pad->head && !pad->tail) || !bytes) {
|
||||
if (!pad->head && !pad->tail) {
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(bytes); /* Nothing good in aligning zero-length requests */
|
||||
|
||||
sum = pad->head + bytes + pad->tail;
|
||||
pad->buf_len = (sum > align && pad->head && pad->tail) ? 2 * align : align;
|
||||
pad->buf = qemu_blockalign(bs, pad->buf_len);
|
||||
|
@ -1708,10 +1708,22 @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (bytes == 0 && !QEMU_IS_ALIGNED(offset, bs->bl.request_alignment)) {
|
||||
/*
|
||||
* Aligning zero request is nonsense. Even if driver has special meaning
|
||||
* of zero-length (like qcow2_co_pwritev_compressed_part), we can't pass
|
||||
* it to driver due to request_alignment.
|
||||
*
|
||||
* Still, no reason to return an error if someone do unaligned
|
||||
* zero-length read occasionally.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
bdrv_inc_in_flight(bs);
|
||||
|
||||
/* Don't do copy-on-read if we read data before write operation */
|
||||
if (atomic_read(&bs->copy_on_read) && !(flags & BDRV_REQ_NO_SERIALISING)) {
|
||||
if (atomic_read(&bs->copy_on_read)) {
|
||||
flags |= BDRV_REQ_COPY_ON_READ;
|
||||
}
|
||||
|
||||
|
@ -1852,20 +1864,24 @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, uint64_t bytes,
|
|||
return -EPERM;
|
||||
}
|
||||
|
||||
/* BDRV_REQ_NO_SERIALISING is only for read operation */
|
||||
assert(!(flags & BDRV_REQ_NO_SERIALISING));
|
||||
assert(!(bs->open_flags & BDRV_O_INACTIVE));
|
||||
assert((bs->open_flags & BDRV_O_NO_IO) == 0);
|
||||
assert(!(flags & ~BDRV_REQ_MASK));
|
||||
|
||||
if (flags & BDRV_REQ_SERIALISING) {
|
||||
bdrv_mark_request_serialising(req, bdrv_get_cluster_size(bs));
|
||||
waited = bdrv_mark_request_serialising(req, bdrv_get_cluster_size(bs));
|
||||
/*
|
||||
* For a misaligned request we should have already waited earlier,
|
||||
* because we come after bdrv_padding_rmw_read which must be called
|
||||
* with the request already marked as serialising.
|
||||
*/
|
||||
assert(!waited ||
|
||||
(req->offset == req->overlap_offset &&
|
||||
req->bytes == req->overlap_bytes));
|
||||
} else {
|
||||
bdrv_wait_serialising_requests(req);
|
||||
}
|
||||
|
||||
waited = bdrv_wait_serialising_requests(req);
|
||||
|
||||
assert(!waited || !req->serialising ||
|
||||
is_request_serialising_and_aligned(req));
|
||||
assert(req->overlap_offset <= offset);
|
||||
assert(offset + bytes <= req->overlap_offset + req->overlap_bytes);
|
||||
assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE);
|
||||
|
@ -2027,7 +2043,6 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child,
|
|||
padding = bdrv_init_padding(bs, offset, bytes, &pad);
|
||||
if (padding) {
|
||||
bdrv_mark_request_serialising(req, align);
|
||||
bdrv_wait_serialising_requests(req);
|
||||
|
||||
bdrv_padding_rmw_read(child, req, &pad, true);
|
||||
|
||||
|
@ -2115,6 +2130,18 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child,
|
|||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (bytes == 0 && !QEMU_IS_ALIGNED(offset, bs->bl.request_alignment)) {
|
||||
/*
|
||||
* Aligning zero request is nonsense. Even if driver has special meaning
|
||||
* of zero-length (like qcow2_co_pwritev_compressed_part), we can't pass
|
||||
* it to driver due to request_alignment.
|
||||
*
|
||||
* Still, no reason to return an error if someone do unaligned
|
||||
* zero-length write occasionally.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
bdrv_inc_in_flight(bs);
|
||||
/*
|
||||
* Align write if necessary by performing a read-modify-write cycle.
|
||||
|
@ -2130,7 +2157,6 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child,
|
|||
|
||||
if (bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, &pad)) {
|
||||
bdrv_mark_request_serialising(&req, align);
|
||||
bdrv_wait_serialising_requests(&req);
|
||||
bdrv_padding_rmw_read(child, &req, &pad, false);
|
||||
}
|
||||
|
||||
|
@ -3222,9 +3248,7 @@ static int coroutine_fn bdrv_co_copy_range_internal(
|
|||
|
||||
/* BDRV_REQ_SERIALISING is only for write operation */
|
||||
assert(!(read_flags & BDRV_REQ_SERIALISING));
|
||||
if (!(read_flags & BDRV_REQ_NO_SERIALISING)) {
|
||||
bdrv_wait_serialising_requests(&req);
|
||||
}
|
||||
bdrv_wait_serialising_requests(&req);
|
||||
|
||||
ret = src->bs->drv->bdrv_co_copy_range_from(src->bs,
|
||||
src, src_offset,
|
||||
|
|
|
@ -0,0 +1,433 @@
|
|||
/*
|
||||
* Linux io_uring support.
|
||||
*
|
||||
* Copyright (C) 2009 IBM, Corp.
|
||||
* Copyright (C) 2009 Red Hat, Inc.
|
||||
* Copyright (C) 2019 Aarushi Mehta
|
||||
*
|
||||
* 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 <liburing.h>
|
||||
#include "qemu-common.h"
|
||||
#include "block/aio.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "block/block.h"
|
||||
#include "block/raw-aio.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "qapi/error.h"
|
||||
#include "trace.h"
|
||||
|
||||
/* io_uring ring size */
|
||||
#define MAX_ENTRIES 128
|
||||
|
||||
typedef struct LuringAIOCB {
|
||||
Coroutine *co;
|
||||
struct io_uring_sqe sqeq;
|
||||
ssize_t ret;
|
||||
QEMUIOVector *qiov;
|
||||
bool is_read;
|
||||
QSIMPLEQ_ENTRY(LuringAIOCB) next;
|
||||
|
||||
/*
|
||||
* Buffered reads may require resubmission, see
|
||||
* luring_resubmit_short_read().
|
||||
*/
|
||||
int total_read;
|
||||
QEMUIOVector resubmit_qiov;
|
||||
} LuringAIOCB;
|
||||
|
||||
typedef struct LuringQueue {
|
||||
int plugged;
|
||||
unsigned int in_queue;
|
||||
unsigned int in_flight;
|
||||
bool blocked;
|
||||
QSIMPLEQ_HEAD(, LuringAIOCB) submit_queue;
|
||||
} LuringQueue;
|
||||
|
||||
typedef struct LuringState {
|
||||
AioContext *aio_context;
|
||||
|
||||
struct io_uring ring;
|
||||
|
||||
/* io queue for submit at batch. Protected by AioContext lock. */
|
||||
LuringQueue io_q;
|
||||
|
||||
/* I/O completion processing. Only runs in I/O thread. */
|
||||
QEMUBH *completion_bh;
|
||||
} LuringState;
|
||||
|
||||
/**
|
||||
* luring_resubmit:
|
||||
*
|
||||
* Resubmit a request by appending it to submit_queue. The caller must ensure
|
||||
* that ioq_submit() is called later so that submit_queue requests are started.
|
||||
*/
|
||||
static void luring_resubmit(LuringState *s, LuringAIOCB *luringcb)
|
||||
{
|
||||
QSIMPLEQ_INSERT_TAIL(&s->io_q.submit_queue, luringcb, next);
|
||||
s->io_q.in_queue++;
|
||||
}
|
||||
|
||||
/**
|
||||
* luring_resubmit_short_read:
|
||||
*
|
||||
* Before Linux commit 9d93a3f5a0c ("io_uring: punt short reads to async
|
||||
* context") a buffered I/O request with the start of the file range in the
|
||||
* page cache could result in a short read. Applications need to resubmit the
|
||||
* remaining read request.
|
||||
*
|
||||
* This is a slow path but recent kernels never take it.
|
||||
*/
|
||||
static void luring_resubmit_short_read(LuringState *s, LuringAIOCB *luringcb,
|
||||
int nread)
|
||||
{
|
||||
QEMUIOVector *resubmit_qiov;
|
||||
size_t remaining;
|
||||
|
||||
trace_luring_resubmit_short_read(s, luringcb, nread);
|
||||
|
||||
/* Update read position */
|
||||
luringcb->total_read = nread;
|
||||
remaining = luringcb->qiov->size - luringcb->total_read;
|
||||
|
||||
/* Shorten qiov */
|
||||
resubmit_qiov = &luringcb->resubmit_qiov;
|
||||
if (resubmit_qiov->iov == NULL) {
|
||||
qemu_iovec_init(resubmit_qiov, luringcb->qiov->niov);
|
||||
} else {
|
||||
qemu_iovec_reset(resubmit_qiov);
|
||||
}
|
||||
qemu_iovec_concat(resubmit_qiov, luringcb->qiov, luringcb->total_read,
|
||||
remaining);
|
||||
|
||||
/* Update sqe */
|
||||
luringcb->sqeq.off = nread;
|
||||
luringcb->sqeq.addr = (__u64)(uintptr_t)luringcb->resubmit_qiov.iov;
|
||||
luringcb->sqeq.len = luringcb->resubmit_qiov.niov;
|
||||
|
||||
luring_resubmit(s, luringcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* luring_process_completions:
|
||||
* @s: AIO state
|
||||
*
|
||||
* Fetches completed I/O requests, consumes cqes and invokes their callbacks
|
||||
* The function is somewhat tricky because it supports nested event loops, for
|
||||
* example when a request callback invokes aio_poll().
|
||||
*
|
||||
* Function schedules BH completion so it can be called again in a nested
|
||||
* event loop. When there are no events left to complete the BH is being
|
||||
* canceled.
|
||||
*
|
||||
*/
|
||||
static void luring_process_completions(LuringState *s)
|
||||
{
|
||||
struct io_uring_cqe *cqes;
|
||||
int total_bytes;
|
||||
/*
|
||||
* Request completion callbacks can run the nested event loop.
|
||||
* Schedule ourselves so the nested event loop will "see" remaining
|
||||
* completed requests and process them. Without this, completion
|
||||
* callbacks that wait for other requests using a nested event loop
|
||||
* would hang forever.
|
||||
*
|
||||
* This workaround is needed because io_uring uses poll_wait, which
|
||||
* is woken up when new events are added to the uring, thus polling on
|
||||
* the same uring fd will block unless more events are received.
|
||||
*
|
||||
* Other leaf block drivers (drivers that access the data themselves)
|
||||
* are networking based, so they poll sockets for data and run the
|
||||
* correct coroutine.
|
||||
*/
|
||||
qemu_bh_schedule(s->completion_bh);
|
||||
|
||||
while (io_uring_peek_cqe(&s->ring, &cqes) == 0) {
|
||||
LuringAIOCB *luringcb;
|
||||
int ret;
|
||||
|
||||
if (!cqes) {
|
||||
break;
|
||||
}
|
||||
|
||||
luringcb = io_uring_cqe_get_data(cqes);
|
||||
ret = cqes->res;
|
||||
io_uring_cqe_seen(&s->ring, cqes);
|
||||
cqes = NULL;
|
||||
|
||||
/* Change counters one-by-one because we can be nested. */
|
||||
s->io_q.in_flight--;
|
||||
trace_luring_process_completion(s, luringcb, ret);
|
||||
|
||||
/* total_read is non-zero only for resubmitted read requests */
|
||||
total_bytes = ret + luringcb->total_read;
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret == -EINTR) {
|
||||
luring_resubmit(s, luringcb);
|
||||
continue;
|
||||
}
|
||||
} else if (!luringcb->qiov) {
|
||||
goto end;
|
||||
} else if (total_bytes == luringcb->qiov->size) {
|
||||
ret = 0;
|
||||
/* Only read/write */
|
||||
} else {
|
||||
/* Short Read/Write */
|
||||
if (luringcb->is_read) {
|
||||
if (ret > 0) {
|
||||
luring_resubmit_short_read(s, luringcb, ret);
|
||||
continue;
|
||||
} else {
|
||||
/* Pad with zeroes */
|
||||
qemu_iovec_memset(luringcb->qiov, total_bytes, 0,
|
||||
luringcb->qiov->size - total_bytes);
|
||||
ret = 0;
|
||||
}
|
||||
} else {
|
||||
ret = -ENOSPC;
|
||||
}
|
||||
}
|
||||
end:
|
||||
luringcb->ret = ret;
|
||||
qemu_iovec_destroy(&luringcb->resubmit_qiov);
|
||||
|
||||
/*
|
||||
* If the coroutine is already entered it must be in ioq_submit()
|
||||
* and will notice luringcb->ret has been filled in when it
|
||||
* eventually runs later. Coroutines cannot be entered recursively
|
||||
* so avoid doing that!
|
||||
*/
|
||||
if (!qemu_coroutine_entered(luringcb->co)) {
|
||||
aio_co_wake(luringcb->co);
|
||||
}
|
||||
}
|
||||
qemu_bh_cancel(s->completion_bh);
|
||||
}
|
||||
|
||||
static int ioq_submit(LuringState *s)
|
||||
{
|
||||
int ret = 0;
|
||||
LuringAIOCB *luringcb, *luringcb_next;
|
||||
|
||||
while (s->io_q.in_queue > 0) {
|
||||
/*
|
||||
* Try to fetch sqes from the ring for requests waiting in
|
||||
* the overflow queue
|
||||
*/
|
||||
QSIMPLEQ_FOREACH_SAFE(luringcb, &s->io_q.submit_queue, next,
|
||||
luringcb_next) {
|
||||
struct io_uring_sqe *sqes = io_uring_get_sqe(&s->ring);
|
||||
if (!sqes) {
|
||||
break;
|
||||
}
|
||||
/* Prep sqe for submission */
|
||||
*sqes = luringcb->sqeq;
|
||||
QSIMPLEQ_REMOVE_HEAD(&s->io_q.submit_queue, next);
|
||||
}
|
||||
ret = io_uring_submit(&s->ring);
|
||||
trace_luring_io_uring_submit(s, ret);
|
||||
/* Prevent infinite loop if submission is refused */
|
||||
if (ret <= 0) {
|
||||
if (ret == -EAGAIN) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
s->io_q.in_flight += ret;
|
||||
s->io_q.in_queue -= ret;
|
||||
}
|
||||
s->io_q.blocked = (s->io_q.in_queue > 0);
|
||||
|
||||
if (s->io_q.in_flight) {
|
||||
/*
|
||||
* We can try to complete something just right away if there are
|
||||
* still requests in-flight.
|
||||
*/
|
||||
luring_process_completions(s);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void luring_process_completions_and_submit(LuringState *s)
|
||||
{
|
||||
aio_context_acquire(s->aio_context);
|
||||
luring_process_completions(s);
|
||||
|
||||
if (!s->io_q.plugged && s->io_q.in_queue > 0) {
|
||||
ioq_submit(s);
|
||||
}
|
||||
aio_context_release(s->aio_context);
|
||||
}
|
||||
|
||||
static void qemu_luring_completion_bh(void *opaque)
|
||||
{
|
||||
LuringState *s = opaque;
|
||||
luring_process_completions_and_submit(s);
|
||||
}
|
||||
|
||||
static void qemu_luring_completion_cb(void *opaque)
|
||||
{
|
||||
LuringState *s = opaque;
|
||||
luring_process_completions_and_submit(s);
|
||||
}
|
||||
|
||||
static bool qemu_luring_poll_cb(void *opaque)
|
||||
{
|
||||
LuringState *s = opaque;
|
||||
struct io_uring_cqe *cqes;
|
||||
|
||||
if (io_uring_peek_cqe(&s->ring, &cqes) == 0) {
|
||||
if (cqes) {
|
||||
luring_process_completions_and_submit(s);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ioq_init(LuringQueue *io_q)
|
||||
{
|
||||
QSIMPLEQ_INIT(&io_q->submit_queue);
|
||||
io_q->plugged = 0;
|
||||
io_q->in_queue = 0;
|
||||
io_q->in_flight = 0;
|
||||
io_q->blocked = false;
|
||||
}
|
||||
|
||||
void luring_io_plug(BlockDriverState *bs, LuringState *s)
|
||||
{
|
||||
trace_luring_io_plug(s);
|
||||
s->io_q.plugged++;
|
||||
}
|
||||
|
||||
void luring_io_unplug(BlockDriverState *bs, LuringState *s)
|
||||
{
|
||||
assert(s->io_q.plugged);
|
||||
trace_luring_io_unplug(s, s->io_q.blocked, s->io_q.plugged,
|
||||
s->io_q.in_queue, s->io_q.in_flight);
|
||||
if (--s->io_q.plugged == 0 &&
|
||||
!s->io_q.blocked && s->io_q.in_queue > 0) {
|
||||
ioq_submit(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* luring_do_submit:
|
||||
* @fd: file descriptor for I/O
|
||||
* @luringcb: AIO control block
|
||||
* @s: AIO state
|
||||
* @offset: offset for request
|
||||
* @type: type of request
|
||||
*
|
||||
* Fetches sqes from ring, adds to pending queue and preps them
|
||||
*
|
||||
*/
|
||||
static int luring_do_submit(int fd, LuringAIOCB *luringcb, LuringState *s,
|
||||
uint64_t offset, int type)
|
||||
{
|
||||
int ret;
|
||||
struct io_uring_sqe *sqes = &luringcb->sqeq;
|
||||
|
||||
switch (type) {
|
||||
case QEMU_AIO_WRITE:
|
||||
io_uring_prep_writev(sqes, fd, luringcb->qiov->iov,
|
||||
luringcb->qiov->niov, offset);
|
||||
break;
|
||||
case QEMU_AIO_READ:
|
||||
io_uring_prep_readv(sqes, fd, luringcb->qiov->iov,
|
||||
luringcb->qiov->niov, offset);
|
||||
break;
|
||||
case QEMU_AIO_FLUSH:
|
||||
io_uring_prep_fsync(sqes, fd, IORING_FSYNC_DATASYNC);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: invalid AIO request type, aborting 0x%x.\n",
|
||||
__func__, type);
|
||||
abort();
|
||||
}
|
||||
io_uring_sqe_set_data(sqes, luringcb);
|
||||
|
||||
QSIMPLEQ_INSERT_TAIL(&s->io_q.submit_queue, luringcb, next);
|
||||
s->io_q.in_queue++;
|
||||
trace_luring_do_submit(s, s->io_q.blocked, s->io_q.plugged,
|
||||
s->io_q.in_queue, s->io_q.in_flight);
|
||||
if (!s->io_q.blocked &&
|
||||
(!s->io_q.plugged ||
|
||||
s->io_q.in_flight + s->io_q.in_queue >= MAX_ENTRIES)) {
|
||||
ret = ioq_submit(s);
|
||||
trace_luring_do_submit_done(s, ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int coroutine_fn luring_co_submit(BlockDriverState *bs, LuringState *s, int fd,
|
||||
uint64_t offset, QEMUIOVector *qiov, int type)
|
||||
{
|
||||
int ret;
|
||||
LuringAIOCB luringcb = {
|
||||
.co = qemu_coroutine_self(),
|
||||
.ret = -EINPROGRESS,
|
||||
.qiov = qiov,
|
||||
.is_read = (type == QEMU_AIO_READ),
|
||||
};
|
||||
trace_luring_co_submit(bs, s, &luringcb, fd, offset, qiov ? qiov->size : 0,
|
||||
type);
|
||||
ret = luring_do_submit(fd, &luringcb, s, offset, type);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (luringcb.ret == -EINPROGRESS) {
|
||||
qemu_coroutine_yield();
|
||||
}
|
||||
return luringcb.ret;
|
||||
}
|
||||
|
||||
void luring_detach_aio_context(LuringState *s, AioContext *old_context)
|
||||
{
|
||||
aio_set_fd_handler(old_context, s->ring.ring_fd, false, NULL, NULL, NULL,
|
||||
s);
|
||||
qemu_bh_delete(s->completion_bh);
|
||||
s->aio_context = NULL;
|
||||
}
|
||||
|
||||
void luring_attach_aio_context(LuringState *s, AioContext *new_context)
|
||||
{
|
||||
s->aio_context = new_context;
|
||||
s->completion_bh = aio_bh_new(new_context, qemu_luring_completion_bh, s);
|
||||
aio_set_fd_handler(s->aio_context, s->ring.ring_fd, false,
|
||||
qemu_luring_completion_cb, NULL, qemu_luring_poll_cb, s);
|
||||
}
|
||||
|
||||
LuringState *luring_init(Error **errp)
|
||||
{
|
||||
int rc;
|
||||
LuringState *s = g_new0(LuringState, 1);
|
||||
struct io_uring *ring = &s->ring;
|
||||
|
||||
trace_luring_init_state(s, sizeof(*s));
|
||||
|
||||
rc = io_uring_queue_init(MAX_ENTRIES, ring, 0);
|
||||
if (rc < 0) {
|
||||
error_setg_errno(errp, errno, "failed to init linux io_uring ring");
|
||||
g_free(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ioq_init(&s->io_q);
|
||||
return s;
|
||||
|
||||
}
|
||||
|
||||
void luring_cleanup(LuringState *s)
|
||||
{
|
||||
io_uring_queue_exit(&s->ring);
|
||||
g_free(s);
|
||||
trace_luring_cleanup_state(s);
|
||||
}
|
|
@ -701,7 +701,7 @@ static int coroutine_fn iscsi_co_block_status(BlockDriverState *bs,
|
|||
struct scsi_get_lba_status *lbas = NULL;
|
||||
struct scsi_lba_status_descriptor *lbasd = NULL;
|
||||
struct IscsiTask iTask;
|
||||
uint64_t lba;
|
||||
uint64_t lba, max_bytes;
|
||||
int ret;
|
||||
|
||||
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
||||
|
@ -721,6 +721,7 @@ static int coroutine_fn iscsi_co_block_status(BlockDriverState *bs,
|
|||
}
|
||||
|
||||
lba = offset / iscsilun->block_size;
|
||||
max_bytes = (iscsilun->num_blocks - lba) * iscsilun->block_size;
|
||||
|
||||
qemu_mutex_lock(&iscsilun->mutex);
|
||||
retry:
|
||||
|
@ -752,7 +753,7 @@ retry:
|
|||
}
|
||||
|
||||
lbas = scsi_datain_unmarshall(iTask.task);
|
||||
if (lbas == NULL) {
|
||||
if (lbas == NULL || lbas->num_descriptors == 0) {
|
||||
ret = -EIO;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
@ -764,7 +765,7 @@ retry:
|
|||
goto out_unlock;
|
||||
}
|
||||
|
||||
*pnum = (int64_t) lbasd->num_blocks * iscsilun->block_size;
|
||||
*pnum = MIN((int64_t) lbasd->num_blocks * iscsilun->block_size, max_bytes);
|
||||
|
||||
if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED ||
|
||||
lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) {
|
||||
|
@ -990,8 +991,7 @@ iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status,
|
|||
acb->ioh->driver_status |= SG_ERR_DRIVER_SENSE;
|
||||
|
||||
acb->ioh->sb_len_wr = acb->task->datain.size - 2;
|
||||
ss = (acb->ioh->mx_sb_len >= acb->ioh->sb_len_wr) ?
|
||||
acb->ioh->mx_sb_len : acb->ioh->sb_len_wr;
|
||||
ss = MIN(acb->ioh->mx_sb_len, acb->ioh->sb_len_wr);
|
||||
memcpy(acb->ioh->sbp, &acb->task->datain.data[2], ss);
|
||||
}
|
||||
|
||||
|
@ -2001,7 +2001,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
iscsilun->cluster_size = iscsilun->bl.opt_unmap_gran *
|
||||
iscsilun->block_size;
|
||||
if (iscsilun->lbprz) {
|
||||
ret = iscsi_allocmap_init(iscsilun, bs->open_flags);
|
||||
ret = iscsi_allocmap_init(iscsilun, flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2163,58 +2163,6 @@ static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int coroutine_fn iscsi_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
int ret = 0;
|
||||
int64_t total_size = 0;
|
||||
BlockDriverState *bs;
|
||||
IscsiLun *iscsilun = NULL;
|
||||
QDict *bs_options;
|
||||
Error *local_err = NULL;
|
||||
|
||||
bs = bdrv_new();
|
||||
|
||||
/* Read out options */
|
||||
total_size = DIV_ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||
BDRV_SECTOR_SIZE);
|
||||
bs->opaque = g_new0(struct IscsiLun, 1);
|
||||
iscsilun = bs->opaque;
|
||||
|
||||
bs_options = qdict_new();
|
||||
iscsi_parse_filename(filename, bs_options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
ret = iscsi_open(bs, bs_options, 0, NULL);
|
||||
}
|
||||
qobject_unref(bs_options);
|
||||
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
iscsi_detach_aio_context(bs);
|
||||
if (iscsilun->type != TYPE_DISK) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (bs->total_sectors < total_size) {
|
||||
ret = -ENOSPC;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
if (iscsilun->iscsi != NULL) {
|
||||
iscsi_destroy_context(iscsilun->iscsi);
|
||||
}
|
||||
g_free(bs->opaque);
|
||||
bs->opaque = NULL;
|
||||
bdrv_unref(bs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
|
@ -2450,18 +2398,6 @@ out_unlock:
|
|||
return r;
|
||||
}
|
||||
|
||||
static QemuOptsList iscsi_create_opts = {
|
||||
.name = "iscsi-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = BLOCK_OPT_SIZE,
|
||||
.type = QEMU_OPT_SIZE,
|
||||
.help = "Virtual disk size"
|
||||
},
|
||||
{ /* end of list */ }
|
||||
}
|
||||
};
|
||||
|
||||
static const char *const iscsi_strong_runtime_opts[] = {
|
||||
"transport",
|
||||
|
@ -2485,8 +2421,8 @@ static BlockDriver bdrv_iscsi = {
|
|||
.bdrv_parse_filename = iscsi_parse_filename,
|
||||
.bdrv_file_open = iscsi_open,
|
||||
.bdrv_close = iscsi_close,
|
||||
.bdrv_co_create_opts = iscsi_co_create_opts,
|
||||
.create_opts = &iscsi_create_opts,
|
||||
.bdrv_co_create_opts = bdrv_co_create_opts_simple,
|
||||
.create_opts = &bdrv_create_opts_simple,
|
||||
.bdrv_reopen_prepare = iscsi_reopen_prepare,
|
||||
.bdrv_reopen_commit = iscsi_reopen_commit,
|
||||
.bdrv_co_invalidate_cache = iscsi_co_invalidate_cache,
|
||||
|
@ -2524,8 +2460,8 @@ static BlockDriver bdrv_iser = {
|
|||
.bdrv_parse_filename = iscsi_parse_filename,
|
||||
.bdrv_file_open = iscsi_open,
|
||||
.bdrv_close = iscsi_close,
|
||||
.bdrv_co_create_opts = iscsi_co_create_opts,
|
||||
.create_opts = &iscsi_create_opts,
|
||||
.bdrv_co_create_opts = bdrv_co_create_opts_simple,
|
||||
.create_opts = &bdrv_create_opts_simple,
|
||||
.bdrv_reopen_prepare = iscsi_reopen_prepare,
|
||||
.bdrv_reopen_commit = iscsi_reopen_commit,
|
||||
.bdrv_co_invalidate_cache = iscsi_co_invalidate_cache,
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
* than this we will get EAGAIN from io_submit which is communicated to
|
||||
* the guest as an I/O error.
|
||||
*/
|
||||
#define MAX_EVENTS 128
|
||||
#define MAX_EVENTS 1024
|
||||
|
||||
struct qemu_laiocb {
|
||||
Coroutine *co;
|
||||
|
@ -121,7 +121,7 @@ struct aio_ring {
|
|||
unsigned incompat_features;
|
||||
unsigned header_length; /* size of aio_ring */
|
||||
|
||||
struct io_event io_events[0];
|
||||
struct io_event io_events[];
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -102,7 +102,9 @@ struct MirrorOp {
|
|||
|
||||
bool is_pseudo_op;
|
||||
bool is_active_write;
|
||||
bool is_in_flight;
|
||||
CoQueue waiting_requests;
|
||||
Coroutine *co;
|
||||
|
||||
QTAILQ_ENTRY(MirrorOp) next;
|
||||
};
|
||||
|
@ -292,7 +294,9 @@ mirror_wait_for_any_operation(MirrorBlockJob *s, bool active)
|
|||
* caller of this function. Since there is only one pseudo op
|
||||
* at any given time, we will always find some real operation
|
||||
* to wait on. */
|
||||
if (!op->is_pseudo_op && op->is_active_write == active) {
|
||||
if (!op->is_pseudo_op && op->is_in_flight &&
|
||||
op->is_active_write == active)
|
||||
{
|
||||
qemu_co_queue_wait(&op->waiting_requests, NULL);
|
||||
return;
|
||||
}
|
||||
|
@ -366,6 +370,7 @@ static void coroutine_fn mirror_co_read(void *opaque)
|
|||
/* Copy the dirty cluster. */
|
||||
s->in_flight++;
|
||||
s->bytes_in_flight += op->bytes;
|
||||
op->is_in_flight = true;
|
||||
trace_mirror_one_iteration(s, op->offset, op->bytes);
|
||||
|
||||
ret = bdrv_co_preadv(s->mirror_top_bs->backing, op->offset, op->bytes,
|
||||
|
@ -381,6 +386,7 @@ static void coroutine_fn mirror_co_zero(void *opaque)
|
|||
op->s->in_flight++;
|
||||
op->s->bytes_in_flight += op->bytes;
|
||||
*op->bytes_handled = op->bytes;
|
||||
op->is_in_flight = true;
|
||||
|
||||
ret = blk_co_pwrite_zeroes(op->s->target, op->offset, op->bytes,
|
||||
op->s->unmap ? BDRV_REQ_MAY_UNMAP : 0);
|
||||
|
@ -395,6 +401,7 @@ static void coroutine_fn mirror_co_discard(void *opaque)
|
|||
op->s->in_flight++;
|
||||
op->s->bytes_in_flight += op->bytes;
|
||||
*op->bytes_handled = op->bytes;
|
||||
op->is_in_flight = true;
|
||||
|
||||
ret = blk_co_pdiscard(op->s->target, op->offset, op->bytes);
|
||||
mirror_write_complete(op, ret);
|
||||
|
@ -429,6 +436,7 @@ static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset,
|
|||
default:
|
||||
abort();
|
||||
}
|
||||
op->co = co;
|
||||
|
||||
QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next);
|
||||
qemu_coroutine_enter(co);
|
||||
|
@ -673,6 +681,7 @@ static int mirror_exit_common(Job *job)
|
|||
bdrv_set_backing_hd(target_bs, backing, &local_err);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
local_err = NULL;
|
||||
ret = -EPERM;
|
||||
}
|
||||
}
|
||||
|
@ -695,7 +704,19 @@ static int mirror_exit_common(Job *job)
|
|||
* drain potential other users of the BDS before changing the graph. */
|
||||
assert(s->in_drain);
|
||||
bdrv_drained_begin(target_bs);
|
||||
bdrv_replace_node(to_replace, target_bs, &local_err);
|
||||
/*
|
||||
* Cannot use check_to_replace_node() here, because that would
|
||||
* check for an op blocker on @to_replace, and we have our own
|
||||
* there.
|
||||
*/
|
||||
if (bdrv_recurse_can_replace(src, to_replace)) {
|
||||
bdrv_replace_node(to_replace, target_bs, &local_err);
|
||||
} else {
|
||||
error_setg(&local_err, "Can no longer replace '%s' by '%s', "
|
||||
"because it can no longer be guaranteed that doing so "
|
||||
"would not lead to an abrupt change of visible data",
|
||||
to_replace->node_name, target_bs->node_name);
|
||||
}
|
||||
bdrv_drained_end(target_bs);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
|
@ -1304,6 +1325,7 @@ static MirrorOp *coroutine_fn active_write_prepare(MirrorBlockJob *s,
|
|||
.offset = offset,
|
||||
.bytes = bytes,
|
||||
.is_active_write = true,
|
||||
.is_in_flight = true,
|
||||
};
|
||||
qemu_co_queue_init(&op->waiting_requests);
|
||||
QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next);
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
common-obj-y += block-hmp-cmds.o
|
File diff suppressed because it is too large
Load Diff
54
block/nbd.c
54
block/nbd.c
|
@ -70,6 +70,7 @@ typedef struct BDRVNBDState {
|
|||
CoMutex send_mutex;
|
||||
CoQueue free_sema;
|
||||
Coroutine *connection_co;
|
||||
Coroutine *teardown_co;
|
||||
QemuCoSleepState *connection_co_sleep_ns_state;
|
||||
bool drained;
|
||||
bool wait_drained_end;
|
||||
|
@ -94,6 +95,19 @@ typedef struct BDRVNBDState {
|
|||
|
||||
static int nbd_client_connect(BlockDriverState *bs, Error **errp);
|
||||
|
||||
static void nbd_clear_bdrvstate(BDRVNBDState *s)
|
||||
{
|
||||
object_unref(OBJECT(s->tlscreds));
|
||||
qapi_free_SocketAddress(s->saddr);
|
||||
s->saddr = NULL;
|
||||
g_free(s->export);
|
||||
s->export = NULL;
|
||||
g_free(s->tlscredsid);
|
||||
s->tlscredsid = NULL;
|
||||
g_free(s->x_dirty_bitmap);
|
||||
s->x_dirty_bitmap = NULL;
|
||||
}
|
||||
|
||||
static void nbd_channel_error(BDRVNBDState *s, int ret)
|
||||
{
|
||||
if (ret == -EIO) {
|
||||
|
@ -203,7 +217,15 @@ static void nbd_teardown_connection(BlockDriverState *bs)
|
|||
qemu_co_sleep_wake(s->connection_co_sleep_ns_state);
|
||||
}
|
||||
}
|
||||
BDRV_POLL_WHILE(bs, s->connection_co);
|
||||
if (qemu_in_coroutine()) {
|
||||
s->teardown_co = qemu_coroutine_self();
|
||||
/* connection_co resumes us when it terminates */
|
||||
qemu_coroutine_yield();
|
||||
s->teardown_co = NULL;
|
||||
} else {
|
||||
BDRV_POLL_WHILE(bs, s->connection_co);
|
||||
}
|
||||
assert(!s->connection_co);
|
||||
}
|
||||
|
||||
static bool nbd_client_connecting(BDRVNBDState *s)
|
||||
|
@ -395,6 +417,9 @@ static coroutine_fn void nbd_connection_entry(void *opaque)
|
|||
s->ioc = NULL;
|
||||
}
|
||||
|
||||
if (s->teardown_co) {
|
||||
aio_co_wake(s->teardown_co);
|
||||
}
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
|
@ -866,6 +891,7 @@ typedef struct NBDReplyChunkIter {
|
|||
static void nbd_iter_channel_error(NBDReplyChunkIter *iter,
|
||||
int ret, Error **local_err)
|
||||
{
|
||||
assert(local_err && *local_err);
|
||||
assert(ret < 0);
|
||||
|
||||
if (!iter->ret) {
|
||||
|
@ -1515,8 +1541,10 @@ static int nbd_parse_uri(const char *filename, QDict *options)
|
|||
goto out;
|
||||
}
|
||||
|
||||
p = uri->path ? uri->path : "/";
|
||||
p += strspn(p, "/");
|
||||
p = uri->path ? uri->path : "";
|
||||
if (p[0] == '/') {
|
||||
p++;
|
||||
}
|
||||
if (p[0]) {
|
||||
qdict_put_str(options, "export", p);
|
||||
}
|
||||
|
@ -1864,11 +1892,7 @@ static int nbd_process_options(BlockDriverState *bs, QDict *options,
|
|||
|
||||
error:
|
||||
if (ret < 0) {
|
||||
object_unref(OBJECT(s->tlscreds));
|
||||
qapi_free_SocketAddress(s->saddr);
|
||||
g_free(s->export);
|
||||
g_free(s->tlscredsid);
|
||||
g_free(s->x_dirty_bitmap);
|
||||
nbd_clear_bdrvstate(s);
|
||||
}
|
||||
qemu_opts_del(opts);
|
||||
return ret;
|
||||
|
@ -1891,6 +1915,7 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
|
||||
ret = nbd_client_connect(bs, errp);
|
||||
if (ret < 0) {
|
||||
nbd_clear_bdrvstate(s);
|
||||
return ret;
|
||||
}
|
||||
/* successfully connected */
|
||||
|
@ -1947,12 +1972,7 @@ static void nbd_close(BlockDriverState *bs)
|
|||
BDRVNBDState *s = bs->opaque;
|
||||
|
||||
nbd_client_close(bs);
|
||||
|
||||
object_unref(OBJECT(s->tlscreds));
|
||||
qapi_free_SocketAddress(s->saddr);
|
||||
g_free(s->export);
|
||||
g_free(s->tlscredsid);
|
||||
g_free(s->x_dirty_bitmap);
|
||||
nbd_clear_bdrvstate(s);
|
||||
}
|
||||
|
||||
static int64_t nbd_getlength(BlockDriverState *bs)
|
||||
|
@ -2018,6 +2038,8 @@ static BlockDriver bdrv_nbd = {
|
|||
.protocol_name = "nbd",
|
||||
.instance_size = sizeof(BDRVNBDState),
|
||||
.bdrv_parse_filename = nbd_parse_filename,
|
||||
.bdrv_co_create_opts = bdrv_co_create_opts_simple,
|
||||
.create_opts = &bdrv_create_opts_simple,
|
||||
.bdrv_file_open = nbd_open,
|
||||
.bdrv_reopen_prepare = nbd_client_reopen_prepare,
|
||||
.bdrv_co_preadv = nbd_client_co_preadv,
|
||||
|
@ -2043,6 +2065,8 @@ static BlockDriver bdrv_nbd_tcp = {
|
|||
.protocol_name = "nbd+tcp",
|
||||
.instance_size = sizeof(BDRVNBDState),
|
||||
.bdrv_parse_filename = nbd_parse_filename,
|
||||
.bdrv_co_create_opts = bdrv_co_create_opts_simple,
|
||||
.create_opts = &bdrv_create_opts_simple,
|
||||
.bdrv_file_open = nbd_open,
|
||||
.bdrv_reopen_prepare = nbd_client_reopen_prepare,
|
||||
.bdrv_co_preadv = nbd_client_co_preadv,
|
||||
|
@ -2068,6 +2092,8 @@ static BlockDriver bdrv_nbd_unix = {
|
|||
.protocol_name = "nbd+unix",
|
||||
.instance_size = sizeof(BDRVNBDState),
|
||||
.bdrv_parse_filename = nbd_parse_filename,
|
||||
.bdrv_co_create_opts = bdrv_co_create_opts_simple,
|
||||
.create_opts = &bdrv_create_opts_simple,
|
||||
.bdrv_file_open = nbd_open,
|
||||
.bdrv_reopen_prepare = nbd_client_reopen_prepare,
|
||||
.bdrv_co_preadv = nbd_client_co_preadv,
|
||||
|
|
|
@ -662,7 +662,9 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
|
||||
static int coroutine_fn nfs_file_co_create_opts(BlockDriver *drv,
|
||||
const char *url,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options;
|
||||
|
|
|
@ -1333,6 +1333,9 @@ static BlockDriver bdrv_nvme = {
|
|||
.protocol_name = "nvme",
|
||||
.instance_size = sizeof(BDRVNVMeState),
|
||||
|
||||
.bdrv_co_create_opts = bdrv_co_create_opts_simple,
|
||||
.create_opts = &bdrv_create_opts_simple,
|
||||
|
||||
.bdrv_parse_filename = nvme_parse_filename,
|
||||
.bdrv_file_open = nvme_file_open,
|
||||
.bdrv_close = nvme_close,
|
||||
|
|
|
@ -609,7 +609,8 @@ exit:
|
|||
goto out;
|
||||
}
|
||||
|
||||
static int coroutine_fn parallels_co_create_opts(const char *filename,
|
||||
static int coroutine_fn parallels_co_create_opts(BlockDriver *drv,
|
||||
const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,590 @@
|
|||
/*
|
||||
* QMP command handlers specific to the system emulators
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* later. See the COPYING file in the top-level directory.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* 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 "qapi/error.h"
|
||||
#include "qapi/qapi-commands-block.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "sysemu/blockdev.h"
|
||||
|
||||
static BlockBackend *qmp_get_blk(const char *blk_name, const char *qdev_id,
|
||||
Error **errp)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
|
||||
if (!blk_name == !qdev_id) {
|
||||
error_setg(errp, "Need exactly one of 'device' and 'id'");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (qdev_id) {
|
||||
blk = blk_by_qdev_id(qdev_id, errp);
|
||||
} else {
|
||||
blk = blk_by_name(blk_name);
|
||||
if (blk == NULL) {
|
||||
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
|
||||
"Device '%s' not found", blk_name);
|
||||
}
|
||||
}
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to open the tray of @device.
|
||||
* If @force, ignore its tray lock.
|
||||
* Else, if the tray is locked, don't open it, but ask the guest to open it.
|
||||
* On error, store an error through @errp and return -errno.
|
||||
* If @device does not exist, return -ENODEV.
|
||||
* If it has no removable media, return -ENOTSUP.
|
||||
* If it has no tray, return -ENOSYS.
|
||||
* If the guest was asked to open the tray, return -EINPROGRESS.
|
||||
* Else, return 0.
|
||||
*/
|
||||
static int do_open_tray(const char *blk_name, const char *qdev_id,
|
||||
bool force, Error **errp)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
const char *device = qdev_id ?: blk_name;
|
||||
bool locked;
|
||||
|
||||
blk = qmp_get_blk(blk_name, qdev_id, errp);
|
||||
if (!blk) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!blk_dev_has_removable_media(blk)) {
|
||||
error_setg(errp, "Device '%s' is not removable", device);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (!blk_dev_has_tray(blk)) {
|
||||
error_setg(errp, "Device '%s' does not have a tray", device);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
if (blk_dev_is_tray_open(blk)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
locked = blk_dev_is_medium_locked(blk);
|
||||
if (locked) {
|
||||
blk_dev_eject_request(blk, force);
|
||||
}
|
||||
|
||||
if (!locked || force) {
|
||||
blk_dev_change_media_cb(blk, false, &error_abort);
|
||||
}
|
||||
|
||||
if (locked && !force) {
|
||||
error_setg(errp, "Device '%s' is locked and force was not specified, "
|
||||
"wait for tray to open and try again", device);
|
||||
return -EINPROGRESS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void qmp_blockdev_open_tray(bool has_device, const char *device,
|
||||
bool has_id, const char *id,
|
||||
bool has_force, bool force,
|
||||
Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
int rc;
|
||||
|
||||
if (!has_force) {
|
||||
force = false;
|
||||
}
|
||||
rc = do_open_tray(has_device ? device : NULL,
|
||||
has_id ? id : NULL,
|
||||
force, &local_err);
|
||||
if (rc && rc != -ENOSYS && rc != -EINPROGRESS) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
error_free(local_err);
|
||||
}
|
||||
|
||||
void qmp_blockdev_close_tray(bool has_device, const char *device,
|
||||
bool has_id, const char *id,
|
||||
Error **errp)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
Error *local_err = NULL;
|
||||
|
||||
device = has_device ? device : NULL;
|
||||
id = has_id ? id : NULL;
|
||||
|
||||
blk = qmp_get_blk(device, id, errp);
|
||||
if (!blk) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!blk_dev_has_removable_media(blk)) {
|
||||
error_setg(errp, "Device '%s' is not removable", device ?: id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!blk_dev_has_tray(blk)) {
|
||||
/* Ignore this command on tray-less devices */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!blk_dev_is_tray_open(blk)) {
|
||||
return;
|
||||
}
|
||||
|
||||
blk_dev_change_media_cb(blk, true, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void blockdev_remove_medium(bool has_device, const char *device,
|
||||
bool has_id, const char *id, Error **errp)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *bs;
|
||||
AioContext *aio_context;
|
||||
bool has_attached_device;
|
||||
|
||||
device = has_device ? device : NULL;
|
||||
id = has_id ? id : NULL;
|
||||
|
||||
blk = qmp_get_blk(device, id, errp);
|
||||
if (!blk) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* For BBs without a device, we can exchange the BDS tree at will */
|
||||
has_attached_device = blk_get_attached_dev(blk);
|
||||
|
||||
if (has_attached_device && !blk_dev_has_removable_media(blk)) {
|
||||
error_setg(errp, "Device '%s' is not removable", device ?: id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (has_attached_device && blk_dev_has_tray(blk) &&
|
||||
!blk_dev_is_tray_open(blk))
|
||||
{
|
||||
error_setg(errp, "Tray of device '%s' is not open", device ?: id);
|
||||
return;
|
||||
}
|
||||
|
||||
bs = blk_bs(blk);
|
||||
if (!bs) {
|
||||
return;
|
||||
}
|
||||
|
||||
aio_context = bdrv_get_aio_context(bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
blk_remove_bs(blk);
|
||||
|
||||
if (!blk_dev_has_tray(blk)) {
|
||||
/* For tray-less devices, blockdev-open-tray is a no-op (or may not be
|
||||
* called at all); therefore, the medium needs to be ejected here.
|
||||
* Do it after blk_remove_bs() so blk_is_inserted(blk) returns the @load
|
||||
* value passed here (i.e. false). */
|
||||
blk_dev_change_media_cb(blk, false, &error_abort);
|
||||
}
|
||||
|
||||
out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
void qmp_blockdev_remove_medium(const char *id, Error **errp)
|
||||
{
|
||||
blockdev_remove_medium(false, NULL, true, id, errp);
|
||||
}
|
||||
|
||||
static void qmp_blockdev_insert_anon_medium(BlockBackend *blk,
|
||||
BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
bool has_device;
|
||||
int ret;
|
||||
|
||||
/* For BBs without a device, we can exchange the BDS tree at will */
|
||||
has_device = blk_get_attached_dev(blk);
|
||||
|
||||
if (has_device && !blk_dev_has_removable_media(blk)) {
|
||||
error_setg(errp, "Device is not removable");
|
||||
return;
|
||||
}
|
||||
|
||||
if (has_device && blk_dev_has_tray(blk) && !blk_dev_is_tray_open(blk)) {
|
||||
error_setg(errp, "Tray of the device is not open");
|
||||
return;
|
||||
}
|
||||
|
||||
if (blk_bs(blk)) {
|
||||
error_setg(errp, "There already is a medium in the device");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = blk_insert_bs(blk, bs, errp);
|
||||
if (ret < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!blk_dev_has_tray(blk)) {
|
||||
/* For tray-less devices, blockdev-close-tray is a no-op (or may not be
|
||||
* called at all); therefore, the medium needs to be pushed into the
|
||||
* slot here.
|
||||
* Do it after blk_insert_bs() so blk_is_inserted(blk) returns the @load
|
||||
* value passed here (i.e. true). */
|
||||
blk_dev_change_media_cb(blk, true, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
blk_remove_bs(blk);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void blockdev_insert_medium(bool has_device, const char *device,
|
||||
bool has_id, const char *id,
|
||||
const char *node_name, Error **errp)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *bs;
|
||||
|
||||
blk = qmp_get_blk(has_device ? device : NULL,
|
||||
has_id ? id : NULL,
|
||||
errp);
|
||||
if (!blk) {
|
||||
return;
|
||||
}
|
||||
|
||||
bs = bdrv_find_node(node_name);
|
||||
if (!bs) {
|
||||
error_setg(errp, "Node '%s' not found", node_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_has_blk(bs)) {
|
||||
error_setg(errp, "Node '%s' is already in use", node_name);
|
||||
return;
|
||||
}
|
||||
|
||||
qmp_blockdev_insert_anon_medium(blk, bs, errp);
|
||||
}
|
||||
|
||||
void qmp_blockdev_insert_medium(const char *id, const char *node_name,
|
||||
Error **errp)
|
||||
{
|
||||
blockdev_insert_medium(false, NULL, true, id, node_name, errp);
|
||||
}
|
||||
|
||||
void qmp_blockdev_change_medium(bool has_device, const char *device,
|
||||
bool has_id, const char *id,
|
||||
const char *filename,
|
||||
bool has_format, const char *format,
|
||||
bool has_read_only,
|
||||
BlockdevChangeReadOnlyMode read_only,
|
||||
Error **errp)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *medium_bs = NULL;
|
||||
int bdrv_flags;
|
||||
bool detect_zeroes;
|
||||
int rc;
|
||||
QDict *options = NULL;
|
||||
Error *err = NULL;
|
||||
|
||||
blk = qmp_get_blk(has_device ? device : NULL,
|
||||
has_id ? id : NULL,
|
||||
errp);
|
||||
if (!blk) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (blk_bs(blk)) {
|
||||
blk_update_root_state(blk);
|
||||
}
|
||||
|
||||
bdrv_flags = blk_get_open_flags_from_root_state(blk);
|
||||
bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING |
|
||||
BDRV_O_PROTOCOL | BDRV_O_AUTO_RDONLY);
|
||||
|
||||
if (!has_read_only) {
|
||||
read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN;
|
||||
}
|
||||
|
||||
switch (read_only) {
|
||||
case BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN:
|
||||
break;
|
||||
|
||||
case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_ONLY:
|
||||
bdrv_flags &= ~BDRV_O_RDWR;
|
||||
break;
|
||||
|
||||
case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_WRITE:
|
||||
bdrv_flags |= BDRV_O_RDWR;
|
||||
break;
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
options = qdict_new();
|
||||
detect_zeroes = blk_get_detect_zeroes_from_root_state(blk);
|
||||
qdict_put_str(options, "detect-zeroes", detect_zeroes ? "on" : "off");
|
||||
|
||||
if (has_format) {
|
||||
qdict_put_str(options, "driver", format);
|
||||
}
|
||||
|
||||
medium_bs = bdrv_open(filename, NULL, options, bdrv_flags, errp);
|
||||
if (!medium_bs) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = do_open_tray(has_device ? device : NULL,
|
||||
has_id ? id : NULL,
|
||||
false, &err);
|
||||
if (rc && rc != -ENOSYS) {
|
||||
error_propagate(errp, err);
|
||||
goto fail;
|
||||
}
|
||||
error_free(err);
|
||||
err = NULL;
|
||||
|
||||
blockdev_remove_medium(has_device, device, has_id, id, &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
qmp_blockdev_insert_anon_medium(blk, medium_bs, &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
qmp_blockdev_close_tray(has_device, device, has_id, id, errp);
|
||||
|
||||
fail:
|
||||
/* If the medium has been inserted, the device has its own reference, so
|
||||
* ours must be relinquished; and if it has not been inserted successfully,
|
||||
* the reference must be relinquished anyway */
|
||||
bdrv_unref(medium_bs);
|
||||
}
|
||||
|
||||
void qmp_eject(bool has_device, const char *device,
|
||||
bool has_id, const char *id,
|
||||
bool has_force, bool force, Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
int rc;
|
||||
|
||||
if (!has_force) {
|
||||
force = false;
|
||||
}
|
||||
|
||||
rc = do_open_tray(has_device ? device : NULL,
|
||||
has_id ? id : NULL,
|
||||
force, &local_err);
|
||||
if (rc && rc != -ENOSYS) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
error_free(local_err);
|
||||
|
||||
blockdev_remove_medium(has_device, device, has_id, id, errp);
|
||||
}
|
||||
|
||||
/* throttling disk I/O limits */
|
||||
void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
|
||||
{
|
||||
ThrottleConfig cfg;
|
||||
BlockDriverState *bs;
|
||||
BlockBackend *blk;
|
||||
AioContext *aio_context;
|
||||
|
||||
blk = qmp_get_blk(arg->has_device ? arg->device : NULL,
|
||||
arg->has_id ? arg->id : NULL,
|
||||
errp);
|
||||
if (!blk) {
|
||||
return;
|
||||
}
|
||||
|
||||
aio_context = blk_get_aio_context(blk);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
bs = blk_bs(blk);
|
||||
if (!bs) {
|
||||
error_setg(errp, "Device has no medium");
|
||||
goto out;
|
||||
}
|
||||
|
||||
throttle_config_init(&cfg);
|
||||
cfg.buckets[THROTTLE_BPS_TOTAL].avg = arg->bps;
|
||||
cfg.buckets[THROTTLE_BPS_READ].avg = arg->bps_rd;
|
||||
cfg.buckets[THROTTLE_BPS_WRITE].avg = arg->bps_wr;
|
||||
|
||||
cfg.buckets[THROTTLE_OPS_TOTAL].avg = arg->iops;
|
||||
cfg.buckets[THROTTLE_OPS_READ].avg = arg->iops_rd;
|
||||
cfg.buckets[THROTTLE_OPS_WRITE].avg = arg->iops_wr;
|
||||
|
||||
if (arg->has_bps_max) {
|
||||
cfg.buckets[THROTTLE_BPS_TOTAL].max = arg->bps_max;
|
||||
}
|
||||
if (arg->has_bps_rd_max) {
|
||||
cfg.buckets[THROTTLE_BPS_READ].max = arg->bps_rd_max;
|
||||
}
|
||||
if (arg->has_bps_wr_max) {
|
||||
cfg.buckets[THROTTLE_BPS_WRITE].max = arg->bps_wr_max;
|
||||
}
|
||||
if (arg->has_iops_max) {
|
||||
cfg.buckets[THROTTLE_OPS_TOTAL].max = arg->iops_max;
|
||||
}
|
||||
if (arg->has_iops_rd_max) {
|
||||
cfg.buckets[THROTTLE_OPS_READ].max = arg->iops_rd_max;
|
||||
}
|
||||
if (arg->has_iops_wr_max) {
|
||||
cfg.buckets[THROTTLE_OPS_WRITE].max = arg->iops_wr_max;
|
||||
}
|
||||
|
||||
if (arg->has_bps_max_length) {
|
||||
cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_max_length;
|
||||
}
|
||||
if (arg->has_bps_rd_max_length) {
|
||||
cfg.buckets[THROTTLE_BPS_READ].burst_length = arg->bps_rd_max_length;
|
||||
}
|
||||
if (arg->has_bps_wr_max_length) {
|
||||
cfg.buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_wr_max_length;
|
||||
}
|
||||
if (arg->has_iops_max_length) {
|
||||
cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_max_length;
|
||||
}
|
||||
if (arg->has_iops_rd_max_length) {
|
||||
cfg.buckets[THROTTLE_OPS_READ].burst_length = arg->iops_rd_max_length;
|
||||
}
|
||||
if (arg->has_iops_wr_max_length) {
|
||||
cfg.buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_wr_max_length;
|
||||
}
|
||||
|
||||
if (arg->has_iops_size) {
|
||||
cfg.op_size = arg->iops_size;
|
||||
}
|
||||
|
||||
if (!throttle_is_valid(&cfg, errp)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (throttle_enabled(&cfg)) {
|
||||
/* Enable I/O limits if they're not enabled yet, otherwise
|
||||
* just update the throttling group. */
|
||||
if (!blk_get_public(blk)->throttle_group_member.throttle_state) {
|
||||
blk_io_limits_enable(blk,
|
||||
arg->has_group ? arg->group :
|
||||
arg->has_device ? arg->device :
|
||||
arg->id);
|
||||
} else if (arg->has_group) {
|
||||
blk_io_limits_update_group(blk, arg->group);
|
||||
}
|
||||
/* Set the new throttling configuration */
|
||||
blk_set_io_limits(blk, &cfg);
|
||||
} else if (blk_get_public(blk)->throttle_group_member.throttle_state) {
|
||||
/* If all throttling settings are set to 0, disable I/O limits */
|
||||
blk_io_limits_disable(blk);
|
||||
}
|
||||
|
||||
out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
void qmp_block_latency_histogram_set(
|
||||
const char *id,
|
||||
bool has_boundaries, uint64List *boundaries,
|
||||
bool has_boundaries_read, uint64List *boundaries_read,
|
||||
bool has_boundaries_write, uint64List *boundaries_write,
|
||||
bool has_boundaries_flush, uint64List *boundaries_flush,
|
||||
Error **errp)
|
||||
{
|
||||
BlockBackend *blk = qmp_get_blk(NULL, id, errp);
|
||||
BlockAcctStats *stats;
|
||||
int ret;
|
||||
|
||||
if (!blk) {
|
||||
return;
|
||||
}
|
||||
|
||||
stats = blk_get_stats(blk);
|
||||
|
||||
if (!has_boundaries && !has_boundaries_read && !has_boundaries_write &&
|
||||
!has_boundaries_flush)
|
||||
{
|
||||
block_latency_histograms_clear(stats);
|
||||
return;
|
||||
}
|
||||
|
||||
if (has_boundaries || has_boundaries_read) {
|
||||
ret = block_latency_histogram_set(
|
||||
stats, BLOCK_ACCT_READ,
|
||||
has_boundaries_read ? boundaries_read : boundaries);
|
||||
if (ret) {
|
||||
error_setg(errp, "Device '%s' set read boundaries fail", id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_boundaries || has_boundaries_write) {
|
||||
ret = block_latency_histogram_set(
|
||||
stats, BLOCK_ACCT_WRITE,
|
||||
has_boundaries_write ? boundaries_write : boundaries);
|
||||
if (ret) {
|
||||
error_setg(errp, "Device '%s' set write boundaries fail", id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_boundaries || has_boundaries_flush) {
|
||||
ret = block_latency_histogram_set(
|
||||
stats, BLOCK_ACCT_FLUSH,
|
||||
has_boundaries_flush ? boundaries_flush : boundaries);
|
||||
if (ret) {
|
||||
error_setg(errp, "Device '%s' set flush boundaries fail", id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
15
block/qapi.c
15
block/qapi.c
|
@ -42,7 +42,9 @@
|
|||
#include "qemu/cutils.h"
|
||||
|
||||
BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
|
||||
BlockDriverState *bs, Error **errp)
|
||||
BlockDriverState *bs,
|
||||
bool flat,
|
||||
Error **errp)
|
||||
{
|
||||
ImageInfo **p_image_info;
|
||||
BlockDriverState *bs0;
|
||||
|
@ -156,6 +158,11 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* stop gathering data for flat output */
|
||||
if (flat) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (bs0->drv && bs0->backing) {
|
||||
info->backing_file_depth++;
|
||||
bs0 = bs0->backing->bs;
|
||||
|
@ -389,7 +396,7 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
|
|||
|
||||
if (bs && bs->drv) {
|
||||
info->has_inserted = true;
|
||||
info->inserted = bdrv_block_device_info(blk, bs, errp);
|
||||
info->inserted = bdrv_block_device_info(blk, bs, false, errp);
|
||||
if (info->inserted == NULL) {
|
||||
goto err;
|
||||
}
|
||||
|
@ -657,7 +664,7 @@ void bdrv_snapshot_dump(QEMUSnapshotInfo *sn)
|
|||
char *sizing = NULL;
|
||||
|
||||
if (!sn) {
|
||||
qemu_printf("%-10s%-20s%7s%20s%15s",
|
||||
qemu_printf("%-10s%-20s%11s%20s%15s",
|
||||
"ID", "TAG", "VM SIZE", "DATE", "VM CLOCK");
|
||||
} else {
|
||||
ti = sn->date_sec;
|
||||
|
@ -672,7 +679,7 @@ void bdrv_snapshot_dump(QEMUSnapshotInfo *sn)
|
|||
(int)(secs % 60),
|
||||
(int)((sn->vm_clock_nsec / 1000000) % 1000));
|
||||
sizing = size_to_str(sn->vm_state_size);
|
||||
qemu_printf("%-10s%-20s%7s%20s%15s",
|
||||
qemu_printf("%-10s%-20s%11s%20s%15s",
|
||||
sn->id_str, sn->name,
|
||||
sizing,
|
||||
date_buf,
|
||||
|
|
|
@ -934,7 +934,8 @@ exit:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn qcow_co_create_opts(const char *filename,
|
||||
static int coroutine_fn qcow_co_create_opts(BlockDriver *drv,
|
||||
const char *filename,
|
||||
QemuOpts *opts, Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
|
|
|
@ -647,7 +647,6 @@ static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset,
|
|||
return bm_list;
|
||||
|
||||
broken_dir:
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "Broken bitmap directory");
|
||||
|
||||
fail:
|
||||
|
@ -1289,7 +1288,6 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs,
|
|||
uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap);
|
||||
const char *bm_name = bdrv_dirty_bitmap_name(bitmap);
|
||||
uint8_t *buf = NULL;
|
||||
BdrvDirtyBitmapIter *dbi;
|
||||
uint64_t *tb;
|
||||
uint64_t tb_size =
|
||||
size_to_clusters(s,
|
||||
|
@ -1308,12 +1306,14 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
dbi = bdrv_dirty_iter_new(bitmap);
|
||||
buf = g_malloc(s->cluster_size);
|
||||
limit = bytes_covered_by_bitmap_cluster(s, bitmap);
|
||||
assert(DIV_ROUND_UP(bm_size, limit) == tb_size);
|
||||
|
||||
while ((offset = bdrv_dirty_iter_next(dbi)) >= 0) {
|
||||
offset = 0;
|
||||
while ((offset = bdrv_dirty_bitmap_next_dirty(bitmap, offset, INT64_MAX))
|
||||
>= 0)
|
||||
{
|
||||
uint64_t cluster = offset / limit;
|
||||
uint64_t end, write_size;
|
||||
int64_t off;
|
||||
|
@ -1356,23 +1356,17 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (end >= bm_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
bdrv_set_dirty_iter(dbi, end);
|
||||
offset = end;
|
||||
}
|
||||
|
||||
*bitmap_table_size = tb_size;
|
||||
g_free(buf);
|
||||
bdrv_dirty_iter_free(dbi);
|
||||
|
||||
return tb;
|
||||
|
||||
fail:
|
||||
clear_bitmap_table(bs, tb, tb_size);
|
||||
g_free(buf);
|
||||
bdrv_dirty_iter_free(dbi);
|
||||
g_free(tb);
|
||||
|
||||
return NULL;
|
||||
|
@ -1703,8 +1697,14 @@ bool coroutine_fn qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs,
|
|||
Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
bool found;
|
||||
Qcow2BitmapList *bm_list;
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
uint64_t bitmap_directory_size = 0;
|
||||
uint32_t nb_bitmaps = 0;
|
||||
|
||||
if (bdrv_find_dirty_bitmap(bs, name)) {
|
||||
error_setg(errp, "Bitmap already exists: %s", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s->qcow_version < 3) {
|
||||
/* Without autoclear_features, we would always have to assume
|
||||
|
@ -1720,38 +1720,27 @@ bool coroutine_fn qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (s->nb_bitmaps == 0) {
|
||||
return true;
|
||||
FOR_EACH_DIRTY_BITMAP(bs, bitmap) {
|
||||
if (bdrv_dirty_bitmap_get_persistence(bitmap)) {
|
||||
nb_bitmaps++;
|
||||
bitmap_directory_size +=
|
||||
calc_dir_entry_size(strlen(bdrv_dirty_bitmap_name(bitmap)), 0);
|
||||
}
|
||||
}
|
||||
nb_bitmaps++;
|
||||
bitmap_directory_size += calc_dir_entry_size(strlen(name), 0);
|
||||
|
||||
if (s->nb_bitmaps >= QCOW2_MAX_BITMAPS) {
|
||||
if (nb_bitmaps > QCOW2_MAX_BITMAPS) {
|
||||
error_setg(errp,
|
||||
"Maximum number of persistent bitmaps is already reached");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (s->bitmap_directory_size + calc_dir_entry_size(strlen(name), 0) >
|
||||
QCOW2_MAX_BITMAP_DIRECTORY_SIZE)
|
||||
{
|
||||
if (bitmap_directory_size > QCOW2_MAX_BITMAP_DIRECTORY_SIZE) {
|
||||
error_setg(errp, "Not enough space in the bitmap directory");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
|
||||
s->bitmap_directory_size, errp);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
if (bm_list == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
found = find_bitmap_by_name(bm_list, name);
|
||||
bitmap_list_free(bm_list);
|
||||
if (found) {
|
||||
error_setg(errp, "Bitmap with the same name is already stored");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
fail:
|
||||
|
|
|
@ -124,12 +124,11 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
|||
#endif
|
||||
|
||||
new_l1_size2 = sizeof(uint64_t) * new_l1_size;
|
||||
new_l1_table = qemu_try_blockalign(bs->file->bs,
|
||||
ROUND_UP(new_l1_size2, 512));
|
||||
new_l1_table = qemu_try_blockalign(bs->file->bs, new_l1_size2);
|
||||
if (new_l1_table == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(new_l1_table, 0, ROUND_UP(new_l1_size2, 512));
|
||||
memset(new_l1_table, 0, new_l1_size2);
|
||||
|
||||
if (s->l1_size) {
|
||||
memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t));
|
||||
|
@ -217,26 +216,31 @@ static int l2_load(BlockDriverState *bs, uint64_t offset,
|
|||
}
|
||||
|
||||
/*
|
||||
* Writes one sector of the L1 table to the disk (can't update single entries
|
||||
* and we really don't want bdrv_pread to perform a read-modify-write)
|
||||
* Writes an L1 entry to disk (note that depending on the alignment
|
||||
* requirements this function may write more that just one entry in
|
||||
* order to prevent bdrv_pwrite from performing a read-modify-write)
|
||||
*/
|
||||
#define L1_ENTRIES_PER_SECTOR (512 / 8)
|
||||
int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t buf[L1_ENTRIES_PER_SECTOR] = { 0 };
|
||||
int l1_start_index;
|
||||
int i, ret;
|
||||
int bufsize = MAX(sizeof(uint64_t),
|
||||
MIN(bs->file->bs->bl.request_alignment, s->cluster_size));
|
||||
int nentries = bufsize / sizeof(uint64_t);
|
||||
g_autofree uint64_t *buf = g_try_new0(uint64_t, nentries);
|
||||
|
||||
l1_start_index = l1_index & ~(L1_ENTRIES_PER_SECTOR - 1);
|
||||
for (i = 0; i < L1_ENTRIES_PER_SECTOR && l1_start_index + i < s->l1_size;
|
||||
i++)
|
||||
{
|
||||
if (buf == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
l1_start_index = QEMU_ALIGN_DOWN(l1_index, nentries);
|
||||
for (i = 0; i < MIN(nentries, s->l1_size - l1_start_index); i++) {
|
||||
buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]);
|
||||
}
|
||||
|
||||
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1,
|
||||
s->l1_table_offset + 8 * l1_start_index, sizeof(buf), false);
|
||||
s->l1_table_offset + 8 * l1_start_index, bufsize, false);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -244,7 +248,7 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index)
|
|||
BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE);
|
||||
ret = bdrv_pwrite_sync(bs->file,
|
||||
s->l1_table_offset + 8 * l1_start_index,
|
||||
buf, sizeof(buf));
|
||||
buf, bufsize);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -777,6 +781,10 @@ int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
|||
(cluster_offset + compressed_size - 1) / QCOW2_COMPRESSED_SECTOR_SIZE -
|
||||
(cluster_offset / QCOW2_COMPRESSED_SECTOR_SIZE);
|
||||
|
||||
/* The offset and size must fit in their fields of the L2 table entry */
|
||||
assert((cluster_offset & s->cluster_offset_mask) == cluster_offset);
|
||||
assert((nb_csectors & s->csize_mask) == nb_csectors);
|
||||
|
||||
cluster_offset |= QCOW_OFLAG_COMPRESSED |
|
||||
((uint64_t)nb_csectors << s->csize_shift);
|
||||
|
||||
|
@ -972,6 +980,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
|||
|
||||
assert(l2_index + m->nb_clusters <= s->l2_slice_size);
|
||||
for (i = 0; i < m->nb_clusters; i++) {
|
||||
uint64_t offset = cluster_offset + (i << s->cluster_bits);
|
||||
/* if two concurrent writes happen to the same unallocated cluster
|
||||
* each write allocates separate cluster and writes data concurrently.
|
||||
* The first one to complete updates l2 table with pointer to its
|
||||
|
@ -982,8 +991,10 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
|||
old_cluster[j++] = l2_slice[l2_index + i];
|
||||
}
|
||||
|
||||
l2_slice[l2_index + i] = cpu_to_be64((cluster_offset +
|
||||
(i << s->cluster_bits)) | QCOW_OFLAG_COPIED);
|
||||
/* The offset must fit in the offset field of the L2 table entry */
|
||||
assert((offset & L2E_OFFSET_MASK) == offset);
|
||||
|
||||
l2_slice[l2_index + i] = cpu_to_be64(offset | QCOW_OFLAG_COPIED);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1015,8 +1026,11 @@ err:
|
|||
void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
qcow2_free_clusters(bs, m->alloc_offset, m->nb_clusters << s->cluster_bits,
|
||||
QCOW2_DISCARD_NEVER);
|
||||
if (!has_data_file(bs) && !m->keep_old_clusters) {
|
||||
qcow2_free_clusters(bs, m->alloc_offset,
|
||||
m->nb_clusters << s->cluster_bits,
|
||||
QCOW2_DISCARD_NEVER);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1913,6 +1927,9 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
/* The offset must fit in the offset field */
|
||||
assert((offset & L2E_OFFSET_MASK) == offset);
|
||||
|
||||
if (l2_refcount > 1) {
|
||||
/* For shared L2 tables, set the refcount accordingly
|
||||
* (it is already 1 and needs to be l2_refcount) */
|
||||
|
|
|
@ -889,6 +889,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
|
|||
offset);
|
||||
if (table != NULL) {
|
||||
qcow2_cache_put(s->refcount_block_cache, &refcount_block);
|
||||
old_table_index = -1;
|
||||
qcow2_cache_discard(s->refcount_block_cache, table);
|
||||
}
|
||||
|
||||
|
@ -1262,7 +1263,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
|||
* l1_table_offset when it is the current s->l1_table_offset! Be careful
|
||||
* when changing this! */
|
||||
if (l1_table_offset != s->l1_table_offset) {
|
||||
l1_table = g_try_malloc0(ROUND_UP(l1_size2, 512));
|
||||
l1_table = g_try_malloc0(l1_size2);
|
||||
if (l1_size2 && l1_table == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
|
|
|
@ -1024,8 +1024,7 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs,
|
|||
return ret;
|
||||
}
|
||||
new_l1_bytes = sn->l1_size * sizeof(uint64_t);
|
||||
new_l1_table = qemu_try_blockalign(bs->file->bs,
|
||||
ROUND_UP(new_l1_bytes, 512));
|
||||
new_l1_table = qemu_try_blockalign(bs->file->bs, new_l1_bytes);
|
||||
if (new_l1_table == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
|
@ -128,12 +128,12 @@ static ssize_t qcow2_compress(void *dest, size_t dest_size,
|
|||
* @src - source buffer, @src_size bytes
|
||||
*
|
||||
* Returns: 0 on success
|
||||
* -1 on fail
|
||||
* -EIO on fail
|
||||
*/
|
||||
static ssize_t qcow2_decompress(void *dest, size_t dest_size,
|
||||
const void *src, size_t src_size)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret;
|
||||
z_stream strm;
|
||||
|
||||
memset(&strm, 0, sizeof(strm));
|
||||
|
@ -144,17 +144,19 @@ static ssize_t qcow2_decompress(void *dest, size_t dest_size,
|
|||
|
||||
ret = inflateInit2(&strm, -12);
|
||||
if (ret != Z_OK) {
|
||||
return -1;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = inflate(&strm, Z_FINISH);
|
||||
if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) || strm.avail_out != 0) {
|
||||
if ((ret == Z_STREAM_END || ret == Z_BUF_ERROR) && strm.avail_out == 0) {
|
||||
/*
|
||||
* We approve Z_BUF_ERROR because we need @dest buffer to be filled, but
|
||||
* @src buffer may be processed partly (because in qcow2 we know size of
|
||||
* compressed data with precision of one sector)
|
||||
*/
|
||||
ret = -1;
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
inflateEnd(&strm);
|
||||
|
@ -246,12 +248,15 @@ qcow2_co_encdec(BlockDriverState *bs, uint64_t host_offset,
|
|||
.len = len,
|
||||
.func = func,
|
||||
};
|
||||
uint64_t sector_size;
|
||||
|
||||
assert(QEMU_IS_ALIGNED(guest_offset, BDRV_SECTOR_SIZE));
|
||||
assert(QEMU_IS_ALIGNED(host_offset, BDRV_SECTOR_SIZE));
|
||||
assert(QEMU_IS_ALIGNED(len, BDRV_SECTOR_SIZE));
|
||||
assert(s->crypto);
|
||||
|
||||
sector_size = qcrypto_block_get_sector_size(s->crypto);
|
||||
assert(QEMU_IS_ALIGNED(guest_offset, sector_size));
|
||||
assert(QEMU_IS_ALIGNED(host_offset, sector_size));
|
||||
assert(QEMU_IS_ALIGNED(len, sector_size));
|
||||
|
||||
return len == 0 ? 0 : qcow2_co_process(bs, qcow2_encdec_pool_func, &arg);
|
||||
}
|
||||
|
||||
|
@ -270,7 +275,8 @@ qcow2_co_encdec(BlockDriverState *bs, uint64_t host_offset,
|
|||
* will be written to the underlying storage device at
|
||||
* @host_offset
|
||||
*
|
||||
* @len - length of the buffer (must be a BDRV_SECTOR_SIZE multiple)
|
||||
* @len - length of the buffer (must be a multiple of the encryption
|
||||
* sector size)
|
||||
*
|
||||
* Depending on the encryption method, @host_offset and/or @guest_offset
|
||||
* may be used for generating the initialization vector for
|
||||
|
|
311
block/qcow2.c
311
block/qcow2.c
|
@ -135,13 +135,16 @@ static ssize_t qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen,
|
|||
s->crypto_header.length = headerlen;
|
||||
s->crypto_header.offset = ret;
|
||||
|
||||
/* Zero fill remaining space in cluster so it has predictable
|
||||
* content in case of future spec changes */
|
||||
/*
|
||||
* Zero fill all space in cluster so it has predictable
|
||||
* content, as we may not initialize some regions of the
|
||||
* header (eg only 1 out of 8 key slots will be initialized)
|
||||
*/
|
||||
clusterlen = size_to_clusters(s, headerlen) * s->cluster_size;
|
||||
assert(qcow2_pre_write_overlap_check(bs, 0, ret, clusterlen, false) == 0);
|
||||
ret = bdrv_pwrite_zeroes(bs->file,
|
||||
ret + headerlen,
|
||||
clusterlen - headerlen, 0);
|
||||
ret,
|
||||
clusterlen, 0);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not zero fill encryption header");
|
||||
return -1;
|
||||
|
@ -174,7 +177,7 @@ static ssize_t qcow2_crypto_hdr_write_func(QCryptoBlock *block, size_t offset,
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
/*
|
||||
* read qcow2 extension and fill bs
|
||||
* start reading from start_offset
|
||||
* finish reading upon magic of value 0 or when end_offset reached
|
||||
|
@ -367,7 +370,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (bitmaps_ext.bitmap_directory_offset & (s->cluster_size - 1)) {
|
||||
if (offset_into_cluster(s, bitmaps_ext.bitmap_directory_offset)) {
|
||||
error_setg(errp, "bitmaps_ext: "
|
||||
"invalid bitmap directory offset");
|
||||
return -EINVAL;
|
||||
|
@ -453,16 +456,15 @@ static void cleanup_unknown_header_ext(BlockDriverState *bs)
|
|||
static void report_unsupported_feature(Error **errp, Qcow2Feature *table,
|
||||
uint64_t mask)
|
||||
{
|
||||
char *features = g_strdup("");
|
||||
char *old;
|
||||
g_autoptr(GString) features = g_string_sized_new(60);
|
||||
|
||||
while (table && table->name[0] != '\0') {
|
||||
if (table->type == QCOW2_FEAT_TYPE_INCOMPATIBLE) {
|
||||
if (mask & (1ULL << table->bit)) {
|
||||
old = features;
|
||||
features = g_strdup_printf("%s%s%.46s", old, *old ? ", " : "",
|
||||
table->name);
|
||||
g_free(old);
|
||||
if (features->len > 0) {
|
||||
g_string_append(features, ", ");
|
||||
}
|
||||
g_string_append_printf(features, "%.46s", table->name);
|
||||
mask &= ~(1ULL << table->bit);
|
||||
}
|
||||
}
|
||||
|
@ -470,14 +472,14 @@ static void report_unsupported_feature(Error **errp, Qcow2Feature *table,
|
|||
}
|
||||
|
||||
if (mask) {
|
||||
old = features;
|
||||
features = g_strdup_printf("%s%sUnknown incompatible feature: %" PRIx64,
|
||||
old, *old ? ", " : "", mask);
|
||||
g_free(old);
|
||||
if (features->len > 0) {
|
||||
g_string_append(features, ", ");
|
||||
}
|
||||
g_string_append_printf(features,
|
||||
"Unknown incompatible feature: %" PRIx64, mask);
|
||||
}
|
||||
|
||||
error_setg(errp, "Unsupported qcow2 feature(s): %s", features);
|
||||
g_free(features);
|
||||
error_setg(errp, "Unsupported qcow2 feature(s): %s", features->str);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1492,7 +1494,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
|||
|
||||
if (s->l1_size > 0) {
|
||||
s->l1_table = qemu_try_blockalign(bs->file->bs,
|
||||
ROUND_UP(s->l1_size * sizeof(uint64_t), 512));
|
||||
s->l1_size * sizeof(uint64_t));
|
||||
if (s->l1_table == NULL) {
|
||||
error_setg(errp, "Could not allocate L1 table");
|
||||
ret = -ENOMEM;
|
||||
|
@ -1705,14 +1707,14 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
|||
if (!(bdrv_get_flags(bs) & BDRV_O_INACTIVE)) {
|
||||
/* It's case 1, 2 or 3.2. Or 3.1 which is BUG in management layer. */
|
||||
bool header_updated = qcow2_load_dirty_bitmaps(bs, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
update_header = update_header && !header_updated;
|
||||
}
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (update_header) {
|
||||
ret = qcow2_update_header(bs);
|
||||
|
@ -1722,7 +1724,8 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
|||
}
|
||||
}
|
||||
|
||||
bs->supported_zero_flags = header.version >= 3 ? BDRV_REQ_MAY_UNMAP : 0;
|
||||
bs->supported_zero_flags = header.version >= 3 ?
|
||||
BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK : 0;
|
||||
|
||||
/* Repair image if dirty */
|
||||
if (!(flags & (BDRV_O_CHECK | BDRV_O_INACTIVE)) && !bs->read_only &&
|
||||
|
@ -1755,6 +1758,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
|||
g_free(s->image_data_file);
|
||||
if (has_data_file(bs)) {
|
||||
bdrv_unref_child(bs, s->data_file);
|
||||
s->data_file = NULL;
|
||||
}
|
||||
g_free(s->unknown_header_fields);
|
||||
cleanup_unknown_header_ext(bs);
|
||||
|
@ -1881,6 +1885,11 @@ fail:
|
|||
static void qcow2_reopen_commit(BDRVReopenState *state)
|
||||
{
|
||||
qcow2_update_options_commit(state->bs, state->opaque);
|
||||
g_free(state->opaque);
|
||||
}
|
||||
|
||||
static void qcow2_reopen_commit_post(BDRVReopenState *state)
|
||||
{
|
||||
if (state->flags & BDRV_O_RDWR) {
|
||||
Error *local_err = NULL;
|
||||
|
||||
|
@ -1895,7 +1904,6 @@ static void qcow2_reopen_commit(BDRVReopenState *state)
|
|||
bdrv_get_node_name(state->bs));
|
||||
}
|
||||
}
|
||||
g_free(state->opaque);
|
||||
}
|
||||
|
||||
static void qcow2_reopen_abort(BDRVReopenState *state)
|
||||
|
@ -1958,9 +1966,8 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs,
|
|||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t cluster_offset;
|
||||
int index_in_cluster, ret;
|
||||
unsigned int bytes;
|
||||
int status = 0;
|
||||
int ret, status = 0;
|
||||
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
|
||||
|
@ -1981,8 +1988,7 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs,
|
|||
|
||||
if ((ret == QCOW2_CLUSTER_NORMAL || ret == QCOW2_CLUSTER_ZERO_ALLOC) &&
|
||||
!s->crypto) {
|
||||
index_in_cluster = offset & (s->cluster_size - 1);
|
||||
*map = cluster_offset | index_in_cluster;
|
||||
*map = cluster_offset | offset_into_cluster(s, offset);
|
||||
*file = s->data_file->bs;
|
||||
status |= BDRV_BLOCK_OFFSET_VALID;
|
||||
}
|
||||
|
@ -2070,8 +2076,6 @@ qcow2_co_preadv_encrypted(BlockDriverState *bs,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE));
|
||||
assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE));
|
||||
if (qcow2_co_decrypt(bs,
|
||||
file_cluster_offset + offset_into_cluster(s, offset),
|
||||
offset, buf, bytes) < 0)
|
||||
|
@ -2169,10 +2173,7 @@ static coroutine_fn int qcow2_co_preadv_task(BlockDriverState *bs,
|
|||
offset, bytes, qiov, qiov_offset);
|
||||
|
||||
case QCOW2_CLUSTER_NORMAL:
|
||||
if ((file_cluster_offset & 511) != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
assert(offset_into_cluster(s, file_cluster_offset) == 0);
|
||||
if (bs->encrypted) {
|
||||
return qcow2_co_preadv_encrypted(bs, file_cluster_offset,
|
||||
offset, bytes, qiov, qiov_offset);
|
||||
|
@ -2508,7 +2509,7 @@ static coroutine_fn int qcow2_co_pwritev_part(
|
|||
goto out_locked;
|
||||
}
|
||||
|
||||
assert((cluster_offset & 511) == 0);
|
||||
assert(offset_into_cluster(s, cluster_offset) == 0);
|
||||
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0,
|
||||
cluster_offset + offset_in_cluster,
|
||||
|
@ -2610,6 +2611,7 @@ static void qcow2_close(BlockDriverState *bs)
|
|||
|
||||
qcrypto_block_free(s->crypto);
|
||||
s->crypto = NULL;
|
||||
qapi_free_QCryptoBlockOpenOptions(s->crypto_opts);
|
||||
|
||||
g_free(s->unknown_header_fields);
|
||||
cleanup_unknown_header_ext(bs);
|
||||
|
@ -2620,6 +2622,7 @@ static void qcow2_close(BlockDriverState *bs)
|
|||
|
||||
if (has_data_file(bs)) {
|
||||
bdrv_unref_child(bs, s->data_file);
|
||||
s->data_file = NULL;
|
||||
}
|
||||
|
||||
qcow2_refcount_close(bs);
|
||||
|
@ -2820,9 +2823,16 @@ int qcow2_update_header(BlockDriverState *bs)
|
|||
buflen -= ret;
|
||||
}
|
||||
|
||||
/* Feature table */
|
||||
if (s->qcow_version >= 3) {
|
||||
Qcow2Feature features[] = {
|
||||
/*
|
||||
* Feature table. A mere 8 feature names occupies 392 bytes, and
|
||||
* when coupled with the v3 minimum header of 104 bytes plus the
|
||||
* 8-byte end-of-extension marker, that would leave only 8 bytes
|
||||
* for a backing file name in an image with 512-byte clusters.
|
||||
* Thus, we choose to omit this header for cluster sizes 4k and
|
||||
* smaller.
|
||||
*/
|
||||
if (s->qcow_version >= 3 && s->cluster_size > 4096) {
|
||||
static const Qcow2Feature features[] = {
|
||||
{
|
||||
.type = QCOW2_FEAT_TYPE_INCOMPATIBLE,
|
||||
.bit = QCOW2_INCOMPAT_DIRTY_BITNR,
|
||||
|
@ -2843,6 +2853,16 @@ int qcow2_update_header(BlockDriverState *bs)
|
|||
.bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR,
|
||||
.name = "lazy refcounts",
|
||||
},
|
||||
{
|
||||
.type = QCOW2_FEAT_TYPE_AUTOCLEAR,
|
||||
.bit = QCOW2_AUTOCLEAR_BITMAPS_BITNR,
|
||||
.name = "bitmaps",
|
||||
},
|
||||
{
|
||||
.type = QCOW2_FEAT_TYPE_AUTOCLEAR,
|
||||
.bit = QCOW2_AUTOCLEAR_DATA_FILE_RAW_BITNR,
|
||||
.name = "raw external data",
|
||||
},
|
||||
};
|
||||
|
||||
ret = header_ext_add(buf, QCOW2_EXT_MAGIC_FEATURE_TABLE,
|
||||
|
@ -3252,7 +3272,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
|
|||
* inconsistency later.
|
||||
*
|
||||
* We do need a refcount table because growing the refcount table means
|
||||
* allocating two new refcount blocks - the seconds of which would be at
|
||||
* allocating two new refcount blocks - the second of which would be at
|
||||
* 2 GB for 64k clusters, and we don't want to have a 2 GB initial file
|
||||
* size for any qcow2 image.
|
||||
*/
|
||||
|
@ -3277,7 +3297,8 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
|
|||
|
||||
/* Validate options and set default values */
|
||||
if (!QEMU_IS_ALIGNED(qcow2_opts->size, BDRV_SECTOR_SIZE)) {
|
||||
error_setg(errp, "Image size must be a multiple of 512 bytes");
|
||||
error_setg(errp, "Image size must be a multiple of %u bytes",
|
||||
(unsigned) BDRV_SECTOR_SIZE);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
@ -3496,7 +3517,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
|
|||
goto out;
|
||||
}
|
||||
|
||||
/* Want a backing file? There you go.*/
|
||||
/* Want a backing file? There you go. */
|
||||
if (qcow2_opts->has_backing_file) {
|
||||
const char *backing_format = NULL;
|
||||
|
||||
|
@ -3554,7 +3575,9 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
static int coroutine_fn qcow2_co_create_opts(BlockDriver *drv,
|
||||
const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
|
@ -3761,6 +3784,12 @@ static coroutine_fn int qcow2_co_pdiscard(BlockDriverState *bs,
|
|||
int ret;
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
|
||||
/* If the image does not support QCOW_OFLAG_ZERO then discarding
|
||||
* clusters could expose stale data from the backing file. */
|
||||
if (s->qcow_version < 3 && bs->backing) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (!QEMU_IS_ALIGNED(offset | bytes, s->cluster_size)) {
|
||||
assert(bytes < s->cluster_size);
|
||||
/* Ignore partial clusters, except for the special case of the
|
||||
|
@ -3833,10 +3862,6 @@ qcow2_co_copy_range_from(BlockDriverState *bs,
|
|||
case QCOW2_CLUSTER_NORMAL:
|
||||
child = s->data_file;
|
||||
copy_offset += offset_into_cluster(s, src_offset);
|
||||
if ((copy_offset & 511) != 0) {
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -3898,7 +3923,7 @@ qcow2_co_copy_range_to(BlockDriverState *bs,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
assert((cluster_offset & 511) == 0);
|
||||
assert(offset_into_cluster(s, cluster_offset) == 0);
|
||||
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0,
|
||||
cluster_offset + offset_in_cluster, cur_bytes, true);
|
||||
|
@ -3955,8 +3980,9 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
|
|||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (offset & 511) {
|
||||
error_setg(errp, "The new size must be a multiple of 512");
|
||||
if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) {
|
||||
error_setg(errp, "The new size must be a multiple of %u",
|
||||
(unsigned) BDRV_SECTOR_SIZE);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -4222,10 +4248,8 @@ fail:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* XXX: put compressed sectors first, then all the cluster aligned
|
||||
tables to avoid losing bytes in alignment */
|
||||
static coroutine_fn int
|
||||
qcow2_co_pwritev_compressed_part(BlockDriverState *bs,
|
||||
qcow2_co_pwritev_compressed_task(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, size_t qiov_offset)
|
||||
{
|
||||
|
@ -4235,32 +4259,11 @@ qcow2_co_pwritev_compressed_part(BlockDriverState *bs,
|
|||
uint8_t *buf, *out_buf;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
if (has_data_file(bs)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (bytes == 0) {
|
||||
/* align end of file to a sector boundary to ease reading with
|
||||
sector based I/Os */
|
||||
int64_t len = bdrv_getlength(bs->file->bs);
|
||||
if (len < 0) {
|
||||
return len;
|
||||
}
|
||||
return bdrv_co_truncate(bs->file, len, false, PREALLOC_MODE_OFF, NULL);
|
||||
}
|
||||
|
||||
if (offset_into_cluster(s, offset)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
assert(bytes == s->cluster_size || (bytes < s->cluster_size &&
|
||||
(offset + bytes == bs->total_sectors << BDRV_SECTOR_BITS)));
|
||||
|
||||
buf = qemu_blockalign(bs, s->cluster_size);
|
||||
if (bytes != s->cluster_size) {
|
||||
if (bytes > s->cluster_size ||
|
||||
offset + bytes != bs->total_sectors << BDRV_SECTOR_BITS)
|
||||
{
|
||||
qemu_vfree(buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (bytes < s->cluster_size) {
|
||||
/* Zero-pad last write if image size is not cluster aligned */
|
||||
memset(buf + bytes, 0, s->cluster_size - bytes);
|
||||
}
|
||||
|
@ -4309,6 +4312,82 @@ fail:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow2_co_pwritev_compressed_task_entry(AioTask *task)
|
||||
{
|
||||
Qcow2AioTask *t = container_of(task, Qcow2AioTask, task);
|
||||
|
||||
assert(!t->cluster_type && !t->l2meta);
|
||||
|
||||
return qcow2_co_pwritev_compressed_task(t->bs, t->offset, t->bytes, t->qiov,
|
||||
t->qiov_offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: put compressed sectors first, then all the cluster aligned
|
||||
* tables to avoid losing bytes in alignment
|
||||
*/
|
||||
static coroutine_fn int
|
||||
qcow2_co_pwritev_compressed_part(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, size_t qiov_offset)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
AioTaskPool *aio = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (has_data_file(bs)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (bytes == 0) {
|
||||
/*
|
||||
* align end of file to a sector boundary to ease reading with
|
||||
* sector based I/Os
|
||||
*/
|
||||
int64_t len = bdrv_getlength(bs->file->bs);
|
||||
if (len < 0) {
|
||||
return len;
|
||||
}
|
||||
return bdrv_co_truncate(bs->file, len, false, PREALLOC_MODE_OFF, NULL);
|
||||
}
|
||||
|
||||
if (offset_into_cluster(s, offset)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (offset_into_cluster(s, bytes) &&
|
||||
(offset + bytes) != (bs->total_sectors << BDRV_SECTOR_BITS)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
while (bytes && aio_task_pool_status(aio) == 0) {
|
||||
uint64_t chunk_size = MIN(bytes, s->cluster_size);
|
||||
|
||||
if (!aio && chunk_size != bytes) {
|
||||
aio = aio_task_pool_new(QCOW2_MAX_WORKERS);
|
||||
}
|
||||
|
||||
ret = qcow2_add_task(bs, aio, qcow2_co_pwritev_compressed_task_entry,
|
||||
0, 0, offset, chunk_size, qiov, qiov_offset, NULL);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
qiov_offset += chunk_size;
|
||||
offset += chunk_size;
|
||||
bytes -= chunk_size;
|
||||
}
|
||||
|
||||
if (aio) {
|
||||
aio_task_pool_wait_all(aio);
|
||||
if (ret == 0) {
|
||||
ret = aio_task_pool_status(aio);
|
||||
}
|
||||
g_free(aio);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
qcow2_co_preadv_compressed(BlockDriverState *bs,
|
||||
uint64_t file_cluster_offset,
|
||||
|
@ -4562,60 +4641,6 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t qcow2_measure_crypto_hdr_init_func(QCryptoBlock *block,
|
||||
size_t headerlen, void *opaque, Error **errp)
|
||||
{
|
||||
size_t *headerlenp = opaque;
|
||||
|
||||
/* Stash away the payload size */
|
||||
*headerlenp = headerlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t qcow2_measure_crypto_hdr_write_func(QCryptoBlock *block,
|
||||
size_t offset, const uint8_t *buf, size_t buflen,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
/* Discard the bytes, we're not actually writing to an image */
|
||||
return buflen;
|
||||
}
|
||||
|
||||
/* Determine the number of bytes for the LUKS payload */
|
||||
static bool qcow2_measure_luks_headerlen(QemuOpts *opts, size_t *len,
|
||||
Error **errp)
|
||||
{
|
||||
QDict *opts_qdict;
|
||||
QDict *cryptoopts_qdict;
|
||||
QCryptoBlockCreateOptions *cryptoopts;
|
||||
QCryptoBlock *crypto;
|
||||
|
||||
/* Extract "encrypt." options into a qdict */
|
||||
opts_qdict = qemu_opts_to_qdict(opts, NULL);
|
||||
qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt.");
|
||||
qobject_unref(opts_qdict);
|
||||
|
||||
/* Build QCryptoBlockCreateOptions object from qdict */
|
||||
qdict_put_str(cryptoopts_qdict, "format", "luks");
|
||||
cryptoopts = block_crypto_create_opts_init(cryptoopts_qdict, errp);
|
||||
qobject_unref(cryptoopts_qdict);
|
||||
if (!cryptoopts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Fake LUKS creation in order to determine the payload size */
|
||||
crypto = qcrypto_block_create(cryptoopts, "encrypt.",
|
||||
qcow2_measure_crypto_hdr_init_func,
|
||||
qcow2_measure_crypto_hdr_write_func,
|
||||
len, errp);
|
||||
qapi_free_QCryptoBlockCreateOptions(cryptoopts);
|
||||
if (!crypto) {
|
||||
return false;
|
||||
}
|
||||
|
||||
qcrypto_block_free(crypto);
|
||||
return true;
|
||||
}
|
||||
|
||||
static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs,
|
||||
Error **errp)
|
||||
{
|
||||
|
@ -4666,9 +4691,27 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs,
|
|||
g_free(optstr);
|
||||
|
||||
if (has_luks) {
|
||||
g_autoptr(QCryptoBlockCreateOptions) create_opts = NULL;
|
||||
QDict *opts_qdict;
|
||||
QDict *cryptoopts;
|
||||
size_t headerlen;
|
||||
|
||||
if (!qcow2_measure_luks_headerlen(opts, &headerlen, &local_err)) {
|
||||
opts_qdict = qemu_opts_to_qdict(opts, NULL);
|
||||
qdict_extract_subqdict(opts_qdict, &cryptoopts, "encrypt.");
|
||||
qobject_unref(opts_qdict);
|
||||
|
||||
qdict_put_str(cryptoopts, "format", "luks");
|
||||
|
||||
create_opts = block_crypto_create_opts_init(cryptoopts, errp);
|
||||
qobject_unref(cryptoopts);
|
||||
if (!create_opts) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!qcrypto_block_calculate_payload_offset(create_opts,
|
||||
"encrypt.",
|
||||
&headerlen,
|
||||
&local_err)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
@ -4800,6 +4843,7 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs,
|
|||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
qapi_free_ImageInfoSpecific(spec_info);
|
||||
qapi_free_QCryptoBlockInfo(encrypt_info);
|
||||
return NULL;
|
||||
}
|
||||
*spec_info->u.qcow2.data = (ImageInfoSpecificQCow2){
|
||||
|
@ -5492,6 +5536,7 @@ BlockDriver bdrv_qcow2 = {
|
|||
.bdrv_close = qcow2_close,
|
||||
.bdrv_reopen_prepare = qcow2_reopen_prepare,
|
||||
.bdrv_reopen_commit = qcow2_reopen_commit,
|
||||
.bdrv_reopen_commit_post = qcow2_reopen_commit_post,
|
||||
.bdrv_reopen_abort = qcow2_reopen_abort,
|
||||
.bdrv_join_options = qcow2_join_options,
|
||||
.bdrv_child_perm = bdrv_format_default_perms,
|
||||
|
|
|
@ -301,9 +301,6 @@ typedef struct BDRVQcow2State {
|
|||
QEMUTimer *cache_clean_timer;
|
||||
unsigned cache_clean_interval;
|
||||
|
||||
uint8_t *cluster_cache;
|
||||
uint8_t *cluster_data;
|
||||
uint64_t cluster_cache_offset;
|
||||
QLIST_HEAD(, QCowL2Meta) cluster_allocs;
|
||||
|
||||
uint64_t *refcount_table;
|
||||
|
|
|
@ -720,7 +720,8 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn bdrv_qed_co_create_opts(const char *filename,
|
||||
static int coroutine_fn bdrv_qed_co_create_opts(BlockDriver *drv,
|
||||
const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
|
|
|
@ -796,17 +796,53 @@ static coroutine_fn int quorum_co_flush(BlockDriverState *bs)
|
|||
return result;
|
||||
}
|
||||
|
||||
static bool quorum_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||
BlockDriverState *candidate)
|
||||
static bool quorum_recurse_can_replace(BlockDriverState *bs,
|
||||
BlockDriverState *to_replace)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
bool perm = bdrv_recurse_is_first_non_filter(s->children[i]->bs,
|
||||
candidate);
|
||||
if (perm) {
|
||||
return true;
|
||||
/*
|
||||
* We have no idea whether our children show the same data as
|
||||
* this node (@bs). It is actually highly likely that
|
||||
* @to_replace does not, because replacing a broken child is
|
||||
* one of the main use cases here.
|
||||
*
|
||||
* We do know that the new BDS will match @bs, so replacing
|
||||
* any of our children by it will be safe. It cannot change
|
||||
* the data this quorum node presents to its parents.
|
||||
*
|
||||
* However, replacing @to_replace by @bs in any of our
|
||||
* children's chains may change visible data somewhere in
|
||||
* there. We therefore cannot recurse down those chains with
|
||||
* bdrv_recurse_can_replace().
|
||||
* (More formally, bdrv_recurse_can_replace() requires that
|
||||
* @to_replace will be replaced by something matching the @bs
|
||||
* passed to it. We cannot guarantee that.)
|
||||
*
|
||||
* Thus, we can only check whether any of our immediate
|
||||
* children matches @to_replace.
|
||||
*
|
||||
* (In the future, we might add a function to recurse down a
|
||||
* chain that checks that nothing there cares about a change
|
||||
* in data from the respective child in question. For
|
||||
* example, most filters do not care when their child's data
|
||||
* suddenly changes, as long as their parents do not care.)
|
||||
*/
|
||||
if (s->children[i]->bs == to_replace) {
|
||||
/*
|
||||
* We now have to ensure that there is no other parent
|
||||
* that cares about replacing this child by a node with
|
||||
* potentially different data.
|
||||
* We do so by checking whether there are any other parents
|
||||
* at all, which is stricter than necessary, but also very
|
||||
* simple. (We may decide to implement something more
|
||||
* complex and permissive when there is an actual need for
|
||||
* it.)
|
||||
*/
|
||||
return QLIST_FIRST(&to_replace->parents) == s->children[i] &&
|
||||
QLIST_NEXT(s->children[i], next_parent) == NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1114,6 +1150,23 @@ static char *quorum_dirname(BlockDriverState *bs, Error **errp)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void quorum_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||
const BdrvChildRole *role,
|
||||
BlockReopenQueue *reopen_queue,
|
||||
uint64_t perm, uint64_t shared,
|
||||
uint64_t *nperm, uint64_t *nshared)
|
||||
{
|
||||
*nperm = perm & DEFAULT_PERM_PASSTHROUGH;
|
||||
|
||||
/*
|
||||
* We cannot share RESIZE or WRITE, as this would make the
|
||||
* children differ from each other.
|
||||
*/
|
||||
*nshared = (shared & (BLK_PERM_CONSISTENT_READ |
|
||||
BLK_PERM_WRITE_UNCHANGED))
|
||||
| DEFAULT_PERM_UNCHANGED;
|
||||
}
|
||||
|
||||
static const char *const quorum_strong_runtime_opts[] = {
|
||||
QUORUM_OPT_VOTE_THRESHOLD,
|
||||
QUORUM_OPT_BLKVERIFY,
|
||||
|
@ -1143,10 +1196,9 @@ static BlockDriver bdrv_quorum = {
|
|||
.bdrv_add_child = quorum_add_child,
|
||||
.bdrv_del_child = quorum_del_child,
|
||||
|
||||
.bdrv_child_perm = bdrv_filter_default_perms,
|
||||
.bdrv_child_perm = quorum_child_perm,
|
||||
|
||||
.is_filter = true,
|
||||
.bdrv_recurse_is_first_non_filter = quorum_recurse_is_first_non_filter,
|
||||
.bdrv_recurse_can_replace = quorum_recurse_can_replace,
|
||||
|
||||
.strong_runtime_opts = quorum_strong_runtime_opts,
|
||||
};
|
||||
|
|
|
@ -419,7 +419,9 @@ static int raw_has_zero_init_truncate(BlockDriverState *bs)
|
|||
return bdrv_has_zero_init_truncate(bs->file->bs);
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
|
||||
static int coroutine_fn raw_co_create_opts(BlockDriver *drv,
|
||||
const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
return bdrv_create_file(filename, opts, errp);
|
||||
|
|
49
block/rbd.c
49
block/rbd.c
|
@ -104,6 +104,7 @@ typedef struct BDRVRBDState {
|
|||
rbd_image_t image;
|
||||
char *image_name;
|
||||
char *snap;
|
||||
char *namespace;
|
||||
uint64_t image_size;
|
||||
} BDRVRBDState;
|
||||
|
||||
|
@ -152,7 +153,7 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options,
|
|||
const char *start;
|
||||
char *p, *buf;
|
||||
QList *keypairs = NULL;
|
||||
char *found_str;
|
||||
char *found_str, *image_name;
|
||||
|
||||
if (!strstart(filename, "rbd:", &start)) {
|
||||
error_setg(errp, "File name must start with 'rbd:'");
|
||||
|
@ -171,18 +172,24 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options,
|
|||
qdict_put_str(options, "pool", found_str);
|
||||
|
||||
if (strchr(p, '@')) {
|
||||
found_str = qemu_rbd_next_tok(p, '@', &p);
|
||||
qemu_rbd_unescape(found_str);
|
||||
qdict_put_str(options, "image", found_str);
|
||||
image_name = qemu_rbd_next_tok(p, '@', &p);
|
||||
|
||||
found_str = qemu_rbd_next_tok(p, ':', &p);
|
||||
qemu_rbd_unescape(found_str);
|
||||
qdict_put_str(options, "snapshot", found_str);
|
||||
} else {
|
||||
found_str = qemu_rbd_next_tok(p, ':', &p);
|
||||
qemu_rbd_unescape(found_str);
|
||||
qdict_put_str(options, "image", found_str);
|
||||
image_name = qemu_rbd_next_tok(p, ':', &p);
|
||||
}
|
||||
/* Check for namespace in the image_name */
|
||||
if (strchr(image_name, '/')) {
|
||||
found_str = qemu_rbd_next_tok(image_name, '/', &image_name);
|
||||
qemu_rbd_unescape(found_str);
|
||||
qdict_put_str(options, "namespace", found_str);
|
||||
} else {
|
||||
qdict_put_str(options, "namespace", "");
|
||||
}
|
||||
qemu_rbd_unescape(image_name);
|
||||
qdict_put_str(options, "image", image_name);
|
||||
if (!p) {
|
||||
goto done;
|
||||
}
|
||||
|
@ -343,6 +350,11 @@ static QemuOptsList runtime_opts = {
|
|||
.type = QEMU_OPT_STRING,
|
||||
.help = "Rados pool name",
|
||||
},
|
||||
{
|
||||
.name = "namespace",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Rados namespace name in the pool",
|
||||
},
|
||||
{
|
||||
.name = "image",
|
||||
.type = QEMU_OPT_STRING,
|
||||
|
@ -425,7 +437,8 @@ static int qemu_rbd_co_create(BlockdevCreateOptions *options, Error **errp)
|
|||
return qemu_rbd_do_create(options, NULL, NULL, errp);
|
||||
}
|
||||
|
||||
static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
|
||||
static int coroutine_fn qemu_rbd_co_create_opts(BlockDriver *drv,
|
||||
const char *filename,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
|
@ -467,13 +480,14 @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
|
|||
* schema, but when they come from -drive, they're all QString.
|
||||
*/
|
||||
loc = rbd_opts->location;
|
||||
loc->pool = g_strdup(qdict_get_try_str(options, "pool"));
|
||||
loc->conf = g_strdup(qdict_get_try_str(options, "conf"));
|
||||
loc->has_conf = !!loc->conf;
|
||||
loc->user = g_strdup(qdict_get_try_str(options, "user"));
|
||||
loc->has_user = !!loc->user;
|
||||
loc->image = g_strdup(qdict_get_try_str(options, "image"));
|
||||
keypairs = qdict_get_try_str(options, "=keyvalue-pairs");
|
||||
loc->pool = g_strdup(qdict_get_try_str(options, "pool"));
|
||||
loc->conf = g_strdup(qdict_get_try_str(options, "conf"));
|
||||
loc->has_conf = !!loc->conf;
|
||||
loc->user = g_strdup(qdict_get_try_str(options, "user"));
|
||||
loc->has_user = !!loc->user;
|
||||
loc->q_namespace = g_strdup(qdict_get_try_str(options, "namespace"));
|
||||
loc->image = g_strdup(qdict_get_try_str(options, "image"));
|
||||
keypairs = qdict_get_try_str(options, "=keyvalue-pairs");
|
||||
|
||||
ret = qemu_rbd_do_create(create_options, keypairs, password_secret, errp);
|
||||
if (ret < 0) {
|
||||
|
@ -648,6 +662,11 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
|
|||
error_setg_errno(errp, -r, "error opening pool %s", opts->pool);
|
||||
goto failed_shutdown;
|
||||
}
|
||||
/*
|
||||
* Set the namespace after opening the io context on the pool,
|
||||
* if nspace == NULL or if nspace == "", it is just as we did nothing
|
||||
*/
|
||||
rados_ioctx_set_namespace(*io_ctx, opts->q_namespace);
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -144,12 +144,15 @@ fail:
|
|||
static void replication_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVReplicationState *s = bs->opaque;
|
||||
Job *commit_job;
|
||||
|
||||
if (s->stage == BLOCK_REPLICATION_RUNNING) {
|
||||
replication_stop(s->rs, false, NULL);
|
||||
}
|
||||
if (s->stage == BLOCK_REPLICATION_FAILOVER) {
|
||||
job_cancel_sync(&s->commit_job->job);
|
||||
commit_job = &s->commit_job->job;
|
||||
assert(commit_job->aio_context == qemu_get_current_aio_context());
|
||||
job_cancel_sync(commit_job);
|
||||
}
|
||||
|
||||
if (s->mode == REPLICATION_MODE_SECONDARY) {
|
||||
|
@ -306,12 +309,6 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool replication_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||
BlockDriverState *candidate)
|
||||
{
|
||||
return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
|
||||
}
|
||||
|
||||
static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
|
@ -456,6 +453,17 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
|||
aio_context_acquire(aio_context);
|
||||
s = bs->opaque;
|
||||
|
||||
if (s->stage == BLOCK_REPLICATION_DONE ||
|
||||
s->stage == BLOCK_REPLICATION_FAILOVER) {
|
||||
/*
|
||||
* This case happens when a secondary is promoted to primary.
|
||||
* Ignore the request because the secondary side of replication
|
||||
* doesn't have to do anything anymore.
|
||||
*/
|
||||
aio_context_release(aio_context);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->stage != BLOCK_REPLICATION_NONE) {
|
||||
error_setg(errp, "Block replication is running or done");
|
||||
aio_context_release(aio_context);
|
||||
|
@ -580,6 +588,17 @@ static void replication_do_checkpoint(ReplicationState *rs, Error **errp)
|
|||
aio_context_acquire(aio_context);
|
||||
s = bs->opaque;
|
||||
|
||||
if (s->stage == BLOCK_REPLICATION_DONE ||
|
||||
s->stage == BLOCK_REPLICATION_FAILOVER) {
|
||||
/*
|
||||
* This case happens when a secondary was promoted to primary.
|
||||
* Ignore the request because the secondary side of replication
|
||||
* doesn't have to do anything anymore.
|
||||
*/
|
||||
aio_context_release(aio_context);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->mode == REPLICATION_MODE_SECONDARY) {
|
||||
secondary_do_checkpoint(s, errp);
|
||||
}
|
||||
|
@ -596,7 +615,7 @@ static void replication_get_error(ReplicationState *rs, Error **errp)
|
|||
aio_context_acquire(aio_context);
|
||||
s = bs->opaque;
|
||||
|
||||
if (s->stage != BLOCK_REPLICATION_RUNNING) {
|
||||
if (s->stage == BLOCK_REPLICATION_NONE) {
|
||||
error_setg(errp, "Block replication is not running");
|
||||
aio_context_release(aio_context);
|
||||
return;
|
||||
|
@ -638,6 +657,17 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
|
|||
aio_context_acquire(aio_context);
|
||||
s = bs->opaque;
|
||||
|
||||
if (s->stage == BLOCK_REPLICATION_DONE ||
|
||||
s->stage == BLOCK_REPLICATION_FAILOVER) {
|
||||
/*
|
||||
* This case happens when a secondary was promoted to primary.
|
||||
* Ignore the request because the secondary side of replication
|
||||
* doesn't have to do anything anymore.
|
||||
*/
|
||||
aio_context_release(aio_context);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->stage != BLOCK_REPLICATION_RUNNING) {
|
||||
error_setg(errp, "Block replication is not running");
|
||||
aio_context_release(aio_context);
|
||||
|
@ -699,7 +729,6 @@ static BlockDriver bdrv_replication = {
|
|||
.bdrv_co_writev = replication_co_writev,
|
||||
|
||||
.is_filter = true,
|
||||
.bdrv_recurse_is_first_non_filter = replication_recurse_is_first_non_filter,
|
||||
|
||||
.has_variable_length = true,
|
||||
.strong_runtime_opts = replication_strong_runtime_opts,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue