Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
308c6ab905
@ -67,7 +67,7 @@ windows_msys2_task:
|
||||
CIRRUS_SHELL: powershell
|
||||
MSYS: winsymlinks:nativestrict
|
||||
MSYSTEM: MINGW64
|
||||
MSYS2_URL: https://github.com/msys2/msys2-installer/releases/download/2021-01-05/msys2-base-x86_64-20210105.sfx.exe
|
||||
MSYS2_URL: https://github.com/msys2/msys2-installer/releases/download/2021-04-19/msys2-base-x86_64-20210419.sfx.exe
|
||||
MSYS2_FINGERPRINT: 0
|
||||
MSYS2_PACKAGES: "
|
||||
diffutils git grep make pkg-config sed
|
||||
@ -130,7 +130,7 @@ windows_msys2_task:
|
||||
taskkill /F /FI "MODULES eq msys-2.0.dll"
|
||||
tasklist
|
||||
C:\tools\msys64\usr\bin\bash.exe -lc "mv -f /etc/pacman.conf.pacnew /etc/pacman.conf || true"
|
||||
C:\tools\msys64\usr\bin\bash.exe -lc "pacman --noconfirm -Suu --overwrite=*"
|
||||
C:\tools\msys64\usr\bin\bash.exe -lc "pacman --noconfirm -Syuu --overwrite=*"
|
||||
Write-Output "Core install time taken: $((Get-Date).Subtract($start_time))"
|
||||
$start_time = Get-Date
|
||||
|
||||
|
6
.github/lockdown.yml
vendored
6
.github/lockdown.yml
vendored
@ -14,11 +14,11 @@ issues:
|
||||
at https://gitlab.com/qemu-project/qemu.git.
|
||||
The project does not process issues filed on GitHub.
|
||||
|
||||
The project issues are tracked on Launchpad:
|
||||
https://bugs.launchpad.net/qemu
|
||||
The project issues are tracked on GitLab:
|
||||
https://gitlab.com/qemu-project/qemu/-/issues
|
||||
|
||||
QEMU welcomes bug report contributions. You can file new ones on:
|
||||
https://bugs.launchpad.net/qemu/+filebug
|
||||
https://gitlab.com/qemu-project/qemu/-/issues/new
|
||||
|
||||
pulls:
|
||||
comment: |
|
||||
|
81
.gitlab-ci.d/buildtest-template.yml
Normal file
81
.gitlab-ci.d/buildtest-template.yml
Normal file
@ -0,0 +1,81 @@
|
||||
.native_build_job_template:
|
||||
stage: build
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
|
||||
before_script:
|
||||
- JOBS=$(expr $(nproc) + 1)
|
||||
script:
|
||||
- if test -n "$LD_JOBS";
|
||||
then
|
||||
scripts/git-submodule.sh update meson ;
|
||||
fi
|
||||
- mkdir build
|
||||
- cd build
|
||||
- if test -n "$TARGETS";
|
||||
then
|
||||
../configure --enable-werror --disable-docs ${LD_JOBS:+--meson=git} $CONFIGURE_ARGS --target-list="$TARGETS" ;
|
||||
else
|
||||
../configure --enable-werror --disable-docs ${LD_JOBS:+--meson=git} $CONFIGURE_ARGS ;
|
||||
fi || { cat config.log meson-logs/meson-log.txt && exit 1; }
|
||||
- if test -n "$LD_JOBS";
|
||||
then
|
||||
../meson/meson.py configure . -Dbackend_max_links="$LD_JOBS" ;
|
||||
fi || exit 1;
|
||||
- make -j"$JOBS"
|
||||
- if test -n "$MAKE_CHECK_ARGS";
|
||||
then
|
||||
make -j"$JOBS" $MAKE_CHECK_ARGS ;
|
||||
fi
|
||||
|
||||
.native_test_job_template:
|
||||
stage: test
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
|
||||
script:
|
||||
- scripts/git-submodule.sh update
|
||||
$(sed -n '/GIT_SUBMODULES=/ s/.*=// p' build/config-host.mak)
|
||||
- cd build
|
||||
- find . -type f -exec touch {} +
|
||||
# Avoid recompiling by hiding ninja with NINJA=":"
|
||||
- make NINJA=":" $MAKE_CHECK_ARGS
|
||||
|
||||
.acceptance_test_job_template:
|
||||
extends: .native_test_job_template
|
||||
cache:
|
||||
key: "${CI_JOB_NAME}-cache"
|
||||
paths:
|
||||
- ${CI_PROJECT_DIR}/avocado-cache
|
||||
policy: pull-push
|
||||
artifacts:
|
||||
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
|
||||
when: on_failure
|
||||
expire_in: 7 days
|
||||
paths:
|
||||
- build/tests/results/latest/results.xml
|
||||
- build/tests/results/latest/test-results
|
||||
reports:
|
||||
junit: build/tests/results/latest/results.xml
|
||||
before_script:
|
||||
- mkdir -p ~/.config/avocado
|
||||
- echo "[datadir.paths]" > ~/.config/avocado/avocado.conf
|
||||
- echo "cache_dirs = ['${CI_PROJECT_DIR}/avocado-cache']"
|
||||
>> ~/.config/avocado/avocado.conf
|
||||
- echo -e '[job.output.testlogs]\nstatuses = ["FAIL", "INTERRUPT"]'
|
||||
>> ~/.config/avocado/avocado.conf
|
||||
- if [ -d ${CI_PROJECT_DIR}/avocado-cache ]; then
|
||||
du -chs ${CI_PROJECT_DIR}/avocado-cache ;
|
||||
fi
|
||||
- export AVOCADO_ALLOW_UNTRUSTED_CODE=1
|
||||
after_script:
|
||||
- cd build
|
||||
- du -chs ${CI_PROJECT_DIR}/avocado-cache
|
||||
rules:
|
||||
# Only run these jobs if running on the mainstream namespace,
|
||||
# or if the user set the QEMU_CI_AVOCADO_TESTING variable (either
|
||||
# in its namespace setting or via git-push option, see documentation
|
||||
# in /.gitlab-ci.yml of this repository).
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project"'
|
||||
when: always
|
||||
- if: '$QEMU_CI_AVOCADO_TESTING'
|
||||
when: always
|
||||
# Otherwise, set to manual (the jobs are created but not run).
|
||||
- when: manual
|
||||
allow_failure: true
|
706
.gitlab-ci.d/buildtest.yml
Normal file
706
.gitlab-ci.d/buildtest.yml
Normal file
@ -0,0 +1,706 @@
|
||||
include:
|
||||
- local: '/.gitlab-ci.d/buildtest-template.yml'
|
||||
|
||||
build-system-alpine:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
- job: amd64-alpine-container
|
||||
variables:
|
||||
IMAGE: alpine
|
||||
TARGETS: aarch64-softmmu alpha-softmmu cris-softmmu hppa-softmmu
|
||||
microblazeel-softmmu mips64el-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
CONFIGURE_ARGS: --enable-docs --enable-trace-backends=log,simple,syslog
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- .git-submodule-status
|
||||
- build
|
||||
|
||||
check-system-alpine:
|
||||
extends: .native_test_job_template
|
||||
needs:
|
||||
- job: build-system-alpine
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: alpine
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-system-alpine:
|
||||
extends: .acceptance_test_job_template
|
||||
needs:
|
||||
- job: build-system-alpine
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: alpine
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
|
||||
build-system-ubuntu:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-ubuntu2004-container
|
||||
variables:
|
||||
IMAGE: ubuntu2004
|
||||
CONFIGURE_ARGS: --enable-docs --enable-fdt=system --enable-slirp=system
|
||||
TARGETS: aarch64-softmmu alpha-softmmu cris-softmmu hppa-softmmu
|
||||
microblazeel-softmmu mips64el-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-system-ubuntu:
|
||||
extends: .native_test_job_template
|
||||
needs:
|
||||
- job: build-system-ubuntu
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: ubuntu2004
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-system-ubuntu:
|
||||
extends: .acceptance_test_job_template
|
||||
needs:
|
||||
- job: build-system-ubuntu
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: ubuntu2004
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
|
||||
build-system-debian:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-debian-container
|
||||
variables:
|
||||
IMAGE: debian-amd64
|
||||
CONFIGURE_ARGS: --enable-fdt=system
|
||||
TARGETS: arm-softmmu avr-softmmu i386-softmmu mipsel-softmmu
|
||||
riscv64-softmmu sh4eb-softmmu sparc-softmmu xtensaeb-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-system-debian:
|
||||
extends: .native_test_job_template
|
||||
needs:
|
||||
- job: build-system-debian
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: debian-amd64
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-system-debian:
|
||||
extends: .acceptance_test_job_template
|
||||
needs:
|
||||
- job: build-system-debian
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: debian-amd64
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
|
||||
build-system-fedora:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-fedora-container
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs
|
||||
--enable-fdt=system --enable-slirp=system --enable-capstone=system
|
||||
TARGETS: tricore-softmmu microblaze-softmmu mips-softmmu
|
||||
xtensa-softmmu m68k-softmmu riscv32-softmmu ppc-softmmu sparc64-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-system-fedora:
|
||||
extends: .native_test_job_template
|
||||
needs:
|
||||
- job: build-system-fedora
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-system-fedora:
|
||||
extends: .acceptance_test_job_template
|
||||
needs:
|
||||
- job: build-system-fedora
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
|
||||
build-system-centos:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-centos8-container
|
||||
variables:
|
||||
IMAGE: centos8
|
||||
CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-fdt=system
|
||||
--enable-modules --enable-trace-backends=dtrace
|
||||
TARGETS: ppc64-softmmu or1k-softmmu s390x-softmmu
|
||||
x86_64-softmmu rx-softmmu sh4-softmmu nios2-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-system-centos:
|
||||
extends: .native_test_job_template
|
||||
needs:
|
||||
- job: build-system-centos
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: centos8
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-system-centos:
|
||||
extends: .acceptance_test_job_template
|
||||
needs:
|
||||
- job: build-system-centos
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: centos8
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
|
||||
build-system-opensuse:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-opensuse-leap-container
|
||||
variables:
|
||||
IMAGE: opensuse-leap
|
||||
CONFIGURE_ARGS: --enable-fdt=system
|
||||
TARGETS: s390x-softmmu x86_64-softmmu aarch64-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-system-opensuse:
|
||||
extends: .native_test_job_template
|
||||
needs:
|
||||
- job: build-system-opensuse
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: opensuse-leap
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-system-opensuse:
|
||||
extends: .acceptance_test_job_template
|
||||
needs:
|
||||
- job: build-system-opensuse
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: opensuse-leap
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
|
||||
|
||||
build-disabled:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-fedora-container
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
CONFIGURE_ARGS:
|
||||
--disable-attr
|
||||
--disable-auth-pam
|
||||
--disable-avx2
|
||||
--disable-bochs
|
||||
--disable-brlapi
|
||||
--disable-bzip2
|
||||
--disable-cap-ng
|
||||
--disable-capstone
|
||||
--disable-cloop
|
||||
--disable-coroutine-pool
|
||||
--disable-curl
|
||||
--disable-curses
|
||||
--disable-dmg
|
||||
--disable-docs
|
||||
--disable-gcrypt
|
||||
--disable-glusterfs
|
||||
--disable-gnutls
|
||||
--disable-gtk
|
||||
--disable-guest-agent
|
||||
--disable-iconv
|
||||
--disable-keyring
|
||||
--disable-kvm
|
||||
--disable-libiscsi
|
||||
--disable-libpmem
|
||||
--disable-libssh
|
||||
--disable-libudev
|
||||
--disable-libusb
|
||||
--disable-libxml2
|
||||
--disable-linux-aio
|
||||
--disable-live-block-migration
|
||||
--disable-lzo
|
||||
--disable-malloc-trim
|
||||
--disable-mpath
|
||||
--disable-nettle
|
||||
--disable-numa
|
||||
--disable-opengl
|
||||
--disable-parallels
|
||||
--disable-pie
|
||||
--disable-qcow1
|
||||
--disable-qed
|
||||
--disable-qom-cast-debug
|
||||
--disable-rbd
|
||||
--disable-rdma
|
||||
--disable-replication
|
||||
--disable-sdl
|
||||
--disable-seccomp
|
||||
--disable-slirp
|
||||
--disable-smartcard
|
||||
--disable-snappy
|
||||
--disable-sparse
|
||||
--disable-spice
|
||||
--disable-strip
|
||||
--disable-tpm
|
||||
--disable-usb-redir
|
||||
--disable-vdi
|
||||
--disable-vhost-crypto
|
||||
--disable-vhost-net
|
||||
--disable-vhost-scsi
|
||||
--disable-vhost-kernel
|
||||
--disable-vhost-user
|
||||
--disable-vhost-vdpa
|
||||
--disable-vhost-vsock
|
||||
--disable-virglrenderer
|
||||
--disable-vnc
|
||||
--disable-vte
|
||||
--disable-vvfat
|
||||
--disable-xen
|
||||
--disable-zstd
|
||||
TARGETS: arm-softmmu i386-softmmu ppc64-softmmu mips64-softmmu
|
||||
s390x-softmmu i386-linux-user
|
||||
MAKE_CHECK_ARGS: check-qtest SPEED=slow
|
||||
|
||||
# This jobs explicitly disable TCG (--disable-tcg), KVM is detected by
|
||||
# the configure script. The container doesn't contain Xen headers so
|
||||
# Xen accelerator is not detected / selected. As result it build the
|
||||
# i386-softmmu and x86_64-softmmu with KVM being the single accelerator
|
||||
# available.
|
||||
# Also use a different coroutine implementation (which is only really of
|
||||
# interest to KVM users, i.e. with TCG disabled)
|
||||
build-tcg-disabled:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-centos8-container
|
||||
variables:
|
||||
IMAGE: centos8
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-tcg --audio-drv-list="" --with-coroutine=ucontext
|
||||
|| { cat config.log meson-logs/meson-log.txt && exit 1; }
|
||||
- make -j"$JOBS"
|
||||
- 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 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 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:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-debian-user-cross-container
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
CONFIGURE_ARGS: --disable-tools --disable-system
|
||||
MAKE_CHECK_ARGS: check-tcg
|
||||
|
||||
build-user-static:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-debian-user-cross-container
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
CONFIGURE_ARGS: --disable-tools --disable-system --static
|
||||
MAKE_CHECK_ARGS: check-tcg
|
||||
|
||||
# Because the hexagon cross-compiler takes so long to build we don't rely
|
||||
# on the CI system to build it and hence this job has an optional dependency
|
||||
# declared. The image is manually uploaded.
|
||||
build-user-hexagon:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: hexagon-cross-container
|
||||
optional: true
|
||||
variables:
|
||||
IMAGE: debian-hexagon-cross
|
||||
TARGETS: hexagon-linux-user
|
||||
CONFIGURE_ARGS: --disable-tools --disable-docs --enable-debug-tcg
|
||||
MAKE_CHECK_ARGS: check-tcg
|
||||
|
||||
# Only build the softmmu targets we have check-tcg tests for
|
||||
build-some-softmmu:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-debian-user-cross-container
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
CONFIGURE_ARGS: --disable-tools --enable-debug
|
||||
TARGETS: xtensa-softmmu arm-softmmu aarch64-softmmu alpha-softmmu
|
||||
MAKE_CHECK_ARGS: check-tcg
|
||||
|
||||
# Run check-tcg against linux-user (with plugins)
|
||||
# we skip sparc64-linux-user until it has been fixed somewhat
|
||||
# we skip cris-linux-user as it doesn't use the common run loop
|
||||
build-user-plugins:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-debian-user-cross-container
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
CONFIGURE_ARGS: --disable-tools --disable-system --enable-plugins --enable-debug-tcg --target-list-exclude=sparc64-linux-user,cris-linux-user
|
||||
MAKE_CHECK_ARGS: check-tcg
|
||||
timeout: 1h 30m
|
||||
|
||||
build-some-softmmu-plugins:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-debian-user-cross-container
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
CONFIGURE_ARGS: --disable-tools --disable-user --enable-plugins --enable-debug-tcg
|
||||
TARGETS: xtensa-softmmu arm-softmmu aarch64-softmmu alpha-softmmu
|
||||
MAKE_CHECK_ARGS: check-tcg
|
||||
|
||||
clang-system:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-fedora-container
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
CONFIGURE_ARGS: --cc=clang --cxx=clang++
|
||||
--extra-cflags=-fsanitize=undefined --extra-cflags=-fno-sanitize-recover=undefined
|
||||
TARGETS: alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu
|
||||
ppc-softmmu s390x-softmmu
|
||||
MAKE_CHECK_ARGS: check-qtest check-tcg
|
||||
|
||||
clang-user:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-debian-user-cross-container
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --disable-system
|
||||
--target-list-exclude=microblazeel-linux-user,aarch64_be-linux-user,i386-linux-user,m68k-linux-user,mipsn32el-linux-user,xtensaeb-linux-user
|
||||
--extra-cflags=-fsanitize=undefined --extra-cflags=-fno-sanitize-recover=undefined
|
||||
MAKE_CHECK_ARGS: check-unit check-tcg
|
||||
|
||||
# Set LD_JOBS=1 because this requires LTO and ld consumes a large amount of memory.
|
||||
# On gitlab runners, default value sometimes end up calling 2 lds concurrently and
|
||||
# triggers an Out-Of-Memory error
|
||||
#
|
||||
# Since slirp callbacks are used in QEMU Timers, slirp needs to be compiled together
|
||||
# with QEMU and linked as a static library to avoid false positives in CFI checks.
|
||||
# This can be accomplished by using -enable-slirp=git, which avoids the use of
|
||||
# a system-wide version of the library
|
||||
#
|
||||
# Split in three sets of build/check/acceptance to limit the execution time of each
|
||||
# job
|
||||
build-cfi-aarch64:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
- job: amd64-fedora-container
|
||||
variables:
|
||||
LD_JOBS: 1
|
||||
AR: llvm-ar
|
||||
IMAGE: fedora
|
||||
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug
|
||||
--enable-safe-stack --enable-slirp=git
|
||||
TARGETS: aarch64-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
timeout: 70m
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-cfi-aarch64:
|
||||
extends: .native_test_job_template
|
||||
needs:
|
||||
- job: build-cfi-aarch64
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-cfi-aarch64:
|
||||
extends: .acceptance_test_job_template
|
||||
needs:
|
||||
- job: build-cfi-aarch64
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
|
||||
build-cfi-ppc64-s390x:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
- job: amd64-fedora-container
|
||||
variables:
|
||||
LD_JOBS: 1
|
||||
AR: llvm-ar
|
||||
IMAGE: fedora
|
||||
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug
|
||||
--enable-safe-stack --enable-slirp=git
|
||||
TARGETS: ppc64-softmmu s390x-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
timeout: 70m
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-cfi-ppc64-s390x:
|
||||
extends: .native_test_job_template
|
||||
needs:
|
||||
- job: build-cfi-ppc64-s390x
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-cfi-ppc64-s390x:
|
||||
extends: .acceptance_test_job_template
|
||||
needs:
|
||||
- job: build-cfi-ppc64-s390x
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
|
||||
build-cfi-x86_64:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
- job: amd64-fedora-container
|
||||
variables:
|
||||
LD_JOBS: 1
|
||||
AR: llvm-ar
|
||||
IMAGE: fedora
|
||||
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug
|
||||
--enable-safe-stack --enable-slirp=git
|
||||
TARGETS: x86_64-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
timeout: 70m
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-cfi-x86_64:
|
||||
extends: .native_test_job_template
|
||||
needs:
|
||||
- job: build-cfi-x86_64
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-cfi-x86_64:
|
||||
extends: .acceptance_test_job_template
|
||||
needs:
|
||||
- job: build-cfi-x86_64
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
|
||||
tsan-build:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-ubuntu2004-container
|
||||
variables:
|
||||
IMAGE: ubuntu2004
|
||||
CONFIGURE_ARGS: --enable-tsan --cc=clang-10 --cxx=clang++-10
|
||||
--enable-trace-backends=ust --enable-fdt=system --enable-slirp=system
|
||||
TARGETS: x86_64-softmmu ppc64-softmmu riscv64-softmmu x86_64-linux-user
|
||||
MAKE_CHECK_ARGS: bench V=1
|
||||
|
||||
# These targets are on the way out
|
||||
build-deprecated:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-debian-user-cross-container
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
CONFIGURE_ARGS: --disable-tools
|
||||
MAKE_CHECK_ARGS: build-tcg
|
||||
TARGETS: ppc64abi32-linux-user
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
# We split the check-tcg step as test failures are expected but we still
|
||||
# want to catch the build breaking.
|
||||
check-deprecated:
|
||||
extends: .native_test_job_template
|
||||
needs:
|
||||
- job: build-deprecated
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
MAKE_CHECK_ARGS: check-tcg
|
||||
allow_failure: true
|
||||
|
||||
# gprof/gcov are GCC features
|
||||
build-gprof-gcov:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-ubuntu2004-container
|
||||
variables:
|
||||
IMAGE: ubuntu2004
|
||||
CONFIGURE_ARGS: --enable-gprof --enable-gcov
|
||||
TARGETS: aarch64-softmmu ppc64-softmmu s390x-softmmu x86_64-softmmu
|
||||
artifacts:
|
||||
expire_in: 1 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-gprof-gcov:
|
||||
extends: .native_test_job_template
|
||||
needs:
|
||||
- job: build-gprof-gcov
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: ubuntu2004
|
||||
MAKE_CHECK_ARGS: check
|
||||
after_script:
|
||||
- ${CI_PROJECT_DIR}/scripts/ci/coverage-summary.sh
|
||||
|
||||
build-oss-fuzz:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-fedora-container
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
script:
|
||||
- mkdir build-oss-fuzz
|
||||
- CC="clang" CXX="clang++" CFLAGS="-fsanitize=address"
|
||||
./scripts/oss-fuzz/build.sh
|
||||
- export ASAN_OPTIONS="fast_unwind_on_malloc=0"
|
||||
- for fuzzer in $(find ./build-oss-fuzz/DEST_DIR/ -executable -type f
|
||||
| grep -v slirp); do
|
||||
grep "LLVMFuzzerTestOneInput" ${fuzzer} > /dev/null 2>&1 || continue ;
|
||||
echo Testing ${fuzzer} ... ;
|
||||
"${fuzzer}" -runs=1 -seed=1 || exit 1 ;
|
||||
done
|
||||
# Unrelated to fuzzer: run some tests with -fsanitize=address
|
||||
- cd build-oss-fuzz && make check-qtest-i386 check-unit
|
||||
|
||||
build-tci:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-debian-user-cross-container
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
script:
|
||||
- TARGETS="aarch64 alpha arm hppa m68k microblaze ppc64 s390x x86_64"
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --enable-tcg-interpreter
|
||||
--target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)" || { cat config.log meson-logs/meson-log.txt && exit 1; }
|
||||
- make -j"$JOBS"
|
||||
- make tests/qtest/boot-serial-test tests/qtest/cdrom-test tests/qtest/pxe-test
|
||||
- for tg in $TARGETS ; do
|
||||
export QTEST_QEMU_BINARY="./qemu-system-${tg}" ;
|
||||
./tests/qtest/boot-serial-test || exit 1 ;
|
||||
./tests/qtest/cdrom-test || exit 1 ;
|
||||
done
|
||||
- QTEST_QEMU_BINARY="./qemu-system-x86_64" ./tests/qtest/pxe-test
|
||||
- QTEST_QEMU_BINARY="./qemu-system-s390x" ./tests/qtest/pxe-test -m slow
|
||||
- make check-tcg
|
||||
|
||||
# Alternate coroutines implementations are only really of interest to KVM users
|
||||
# However we can't test against KVM on Gitlab-CI so we can only run unit tests
|
||||
build-coroutine-sigaltstack:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-ubuntu2004-container
|
||||
variables:
|
||||
IMAGE: ubuntu2004
|
||||
CONFIGURE_ARGS: --with-coroutine=sigaltstack --disable-tcg
|
||||
--enable-trace-backends=ftrace
|
||||
MAKE_CHECK_ARGS: check-unit
|
||||
|
||||
# Check our reduced build configurations
|
||||
build-without-default-devices:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-centos8-container
|
||||
variables:
|
||||
IMAGE: centos8
|
||||
CONFIGURE_ARGS: --without-default-devices --disable-user
|
||||
|
||||
build-without-default-features:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-debian-container
|
||||
variables:
|
||||
IMAGE: debian-amd64
|
||||
CONFIGURE_ARGS: --without-default-features --disable-user
|
||||
--target-list-exclude=arm-softmmu,i386-softmmu,mipsel-softmmu,mips64-softmmu,ppc-softmmu
|
||||
MAKE_CHECK_ARGS: check-unit
|
||||
|
||||
build-libvhost-user:
|
||||
stage: build
|
||||
image: $CI_REGISTRY_IMAGE/qemu/fedora:latest
|
||||
needs:
|
||||
job: amd64-fedora-container
|
||||
before_script:
|
||||
- dnf install -y meson ninja-build
|
||||
script:
|
||||
- mkdir subprojects/libvhost-user/build
|
||||
- cd subprojects/libvhost-user/build
|
||||
- meson
|
||||
- ninja
|
||||
|
||||
# No targets are built here, just tools, docs, and unit tests. This
|
||||
# also feeds into the eventual documentation deployment steps later
|
||||
build-tools-and-docs-debian:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-debian-container
|
||||
variables:
|
||||
IMAGE: debian-amd64
|
||||
MAKE_CHECK_ARGS: check-unit check-softfloat ctags TAGS cscope
|
||||
CONFIGURE_ARGS: --disable-system --disable-user --enable-docs --enable-tools
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
# Prepare for GitLab pages deployment. Anything copied into the
|
||||
# "public" directory will be deployed to $USER.gitlab.io/$PROJECT
|
||||
pages:
|
||||
image: $CI_REGISTRY_IMAGE/qemu/debian-amd64:latest
|
||||
stage: test
|
||||
needs:
|
||||
- job: build-tools-and-docs-debian
|
||||
script:
|
||||
- mkdir -p public
|
||||
# HTML-ised source tree
|
||||
- make gtags
|
||||
- htags -anT --tree-view=filetree -m qemu_init
|
||||
-t "Welcome to the QEMU sourcecode"
|
||||
- mv HTML public/src
|
||||
# Project documentation
|
||||
- make -C build install DESTDIR=$(pwd)/temp-install
|
||||
- mv temp-install/usr/local/share/doc/qemu/* public/
|
||||
artifacts:
|
||||
paths:
|
||||
- public
|
17
.gitlab-ci.d/container-core.yml
Normal file
17
.gitlab-ci.d/container-core.yml
Normal file
@ -0,0 +1,17 @@
|
||||
include:
|
||||
- local: '/.gitlab-ci.d/container-template.yml'
|
||||
|
||||
amd64-centos8-container:
|
||||
extends: .container_job_template
|
||||
variables:
|
||||
NAME: centos8
|
||||
|
||||
amd64-fedora-container:
|
||||
extends: .container_job_template
|
||||
variables:
|
||||
NAME: fedora
|
||||
|
||||
amd64-debian10-container:
|
||||
extends: .container_job_template
|
||||
variables:
|
||||
NAME: debian10
|
192
.gitlab-ci.d/container-cross.yml
Normal file
192
.gitlab-ci.d/container-cross.yml
Normal file
@ -0,0 +1,192 @@
|
||||
alpha-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-alpha-cross
|
||||
|
||||
amd64-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-amd64-cross
|
||||
|
||||
amd64-debian-user-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-all-test-cross
|
||||
|
||||
arm64-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-arm64-cross
|
||||
|
||||
arm64-test-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian11-container']
|
||||
variables:
|
||||
NAME: debian-arm64-test-cross
|
||||
|
||||
armel-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-armel-cross
|
||||
|
||||
armhf-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-armhf-cross
|
||||
|
||||
# We never want to build hexagon in the CI system and by default we
|
||||
# always want to refer to the master registry where it lives.
|
||||
hexagon-cross-container:
|
||||
image: docker:stable
|
||||
stage: containers
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project"'
|
||||
when: never
|
||||
- when: always
|
||||
variables:
|
||||
NAME: debian-hexagon-cross
|
||||
GIT_DEPTH: 1
|
||||
services:
|
||||
- docker:dind
|
||||
before_script:
|
||||
- export TAG="$CI_REGISTRY_IMAGE/qemu/$NAME:latest"
|
||||
- export COMMON_TAG="$CI_REGISTRY/qemu-project/qemu/qemu/$NAME:latest"
|
||||
- docker info
|
||||
- docker login $CI_REGISTRY -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
|
||||
script:
|
||||
- echo "TAG:$TAG"
|
||||
- echo "COMMON_TAG:$COMMON_TAG"
|
||||
- docker pull $COMMON_TAG
|
||||
- docker tag $COMMON_TAG $TAG
|
||||
- docker push "$TAG"
|
||||
after_script:
|
||||
- docker logout
|
||||
|
||||
hppa-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-hppa-cross
|
||||
|
||||
m68k-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-m68k-cross
|
||||
|
||||
mips64-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-mips64-cross
|
||||
|
||||
mips64el-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-mips64el-cross
|
||||
|
||||
mips-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-mips-cross
|
||||
|
||||
mipsel-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-mipsel-cross
|
||||
|
||||
powerpc-test-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian11-container']
|
||||
variables:
|
||||
NAME: debian-powerpc-test-cross
|
||||
|
||||
ppc64el-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-ppc64el-cross
|
||||
|
||||
riscv64-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-riscv64-cross
|
||||
|
||||
s390x-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-s390x-cross
|
||||
|
||||
sh4-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-sh4-cross
|
||||
|
||||
sparc64-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-sparc64-cross
|
||||
|
||||
tricore-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-tricore-cross
|
||||
|
||||
xtensa-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
variables:
|
||||
NAME: debian-xtensa-cross
|
||||
|
||||
cris-fedora-cross-container:
|
||||
extends: .container_job_template
|
||||
variables:
|
||||
NAME: fedora-cris-cross
|
||||
|
||||
i386-fedora-cross-container:
|
||||
extends: .container_job_template
|
||||
variables:
|
||||
NAME: fedora-i386-cross
|
||||
|
||||
win32-fedora-cross-container:
|
||||
extends: .container_job_template
|
||||
variables:
|
||||
NAME: fedora-win32-cross
|
||||
|
||||
win64-fedora-cross-container:
|
||||
extends: .container_job_template
|
||||
variables:
|
||||
NAME: fedora-win64-cross
|
21
.gitlab-ci.d/container-template.yml
Normal file
21
.gitlab-ci.d/container-template.yml
Normal file
@ -0,0 +1,21 @@
|
||||
.container_job_template:
|
||||
image: docker:stable
|
||||
stage: containers
|
||||
services:
|
||||
- docker:dind
|
||||
before_script:
|
||||
- export TAG="$CI_REGISTRY_IMAGE/qemu/$NAME:latest"
|
||||
- export COMMON_TAG="$CI_REGISTRY/qemu-project/qemu/$NAME:latest"
|
||||
- apk add python3
|
||||
- docker info
|
||||
- docker login $CI_REGISTRY -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
|
||||
script:
|
||||
- echo "TAG:$TAG"
|
||||
- echo "COMMON_TAG:$COMMON_TAG"
|
||||
- ./tests/docker/docker.py --engine docker build
|
||||
-t "qemu/$NAME" -f "tests/docker/dockerfiles/$NAME.docker"
|
||||
-r $CI_REGISTRY/qemu-project/qemu
|
||||
- docker tag "qemu/$NAME" "$TAG"
|
||||
- docker push "$TAG"
|
||||
after_script:
|
||||
- docker logout
|
@ -1,251 +1,45 @@
|
||||
.container_job_template: &container_job_definition
|
||||
image: docker:stable
|
||||
stage: containers
|
||||
services:
|
||||
- docker:dind
|
||||
before_script:
|
||||
- export TAG="$CI_REGISTRY_IMAGE/qemu/$NAME:latest"
|
||||
- export COMMON_TAG="$CI_REGISTRY/qemu-project/qemu/$NAME:latest"
|
||||
- apk add python3
|
||||
- docker info
|
||||
- docker login $CI_REGISTRY -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
|
||||
script:
|
||||
- echo "TAG:$TAG"
|
||||
- echo "COMMON_TAG:$COMMON_TAG"
|
||||
- docker pull "$TAG" || docker pull "$COMMON_TAG" || true
|
||||
- ./tests/docker/docker.py --engine docker build
|
||||
-t "qemu/$NAME" -f "tests/docker/dockerfiles/$NAME.docker"
|
||||
-r $CI_REGISTRY_IMAGE
|
||||
- docker tag "qemu/$NAME" "$TAG"
|
||||
- docker push "$TAG"
|
||||
after_script:
|
||||
- docker logout
|
||||
include:
|
||||
- local: '/.gitlab-ci.d/container-core.yml'
|
||||
- local: '/.gitlab-ci.d/container-cross.yml'
|
||||
|
||||
amd64-alpine-container:
|
||||
<<: *container_job_definition
|
||||
extends: .container_job_template
|
||||
variables:
|
||||
NAME: alpine
|
||||
|
||||
amd64-centos7-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: centos7
|
||||
|
||||
amd64-centos8-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: centos8
|
||||
|
||||
amd64-debian10-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: debian10
|
||||
|
||||
amd64-debian11-container:
|
||||
<<: *container_job_definition
|
||||
extends: .container_job_template
|
||||
variables:
|
||||
NAME: debian11
|
||||
|
||||
alpha-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-alpha-cross
|
||||
|
||||
amd64-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-amd64-cross
|
||||
|
||||
amd64-debian-user-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-all-test-cross
|
||||
|
||||
amd64-debian-container:
|
||||
<<: *container_job_definition
|
||||
extends: .container_job_template
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-amd64
|
||||
|
||||
arm64-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-arm64-cross
|
||||
|
||||
arm64-test-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian11-container']
|
||||
variables:
|
||||
NAME: debian-arm64-test-cross
|
||||
|
||||
armel-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-armel-cross
|
||||
|
||||
armhf-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-armhf-cross
|
||||
|
||||
hppa-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-hppa-cross
|
||||
|
||||
m68k-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-m68k-cross
|
||||
|
||||
mips64-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-mips64-cross
|
||||
|
||||
mips64el-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-mips64el-cross
|
||||
|
||||
mips-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-mips-cross
|
||||
|
||||
mipsel-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-mipsel-cross
|
||||
|
||||
powerpc-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-powerpc-cross
|
||||
|
||||
ppc64-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-ppc64-cross
|
||||
|
||||
ppc64el-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-ppc64el-cross
|
||||
|
||||
riscv64-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-riscv64-cross
|
||||
|
||||
s390x-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-s390x-cross
|
||||
|
||||
sh4-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-sh4-cross
|
||||
|
||||
sparc64-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-sparc64-cross
|
||||
|
||||
tricore-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
stage: containers-layer2
|
||||
needs: ['amd64-debian10-container']
|
||||
variables:
|
||||
NAME: debian-tricore-cross
|
||||
|
||||
xtensa-debian-cross-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: debian-xtensa-cross
|
||||
|
||||
cris-fedora-cross-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: fedora-cris-cross
|
||||
|
||||
amd64-fedora-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: fedora
|
||||
|
||||
i386-fedora-cross-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: fedora-i386-cross
|
||||
|
||||
win32-fedora-cross-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: fedora-win32-cross
|
||||
|
||||
win64-fedora-cross-container:
|
||||
<<: *container_job_definition
|
||||
variables:
|
||||
NAME: fedora-win64-cross
|
||||
|
||||
amd64-ubuntu1804-container:
|
||||
<<: *container_job_definition
|
||||
extends: .container_job_template
|
||||
variables:
|
||||
NAME: ubuntu1804
|
||||
|
||||
amd64-ubuntu2004-container:
|
||||
<<: *container_job_definition
|
||||
extends: .container_job_template
|
||||
variables:
|
||||
NAME: ubuntu2004
|
||||
|
||||
amd64-ubuntu-container:
|
||||
<<: *container_job_definition
|
||||
extends: .container_job_template
|
||||
variables:
|
||||
NAME: ubuntu
|
||||
|
||||
amd64-opensuse-leap-container:
|
||||
<<: *container_job_definition
|
||||
extends: .container_job_template
|
||||
variables:
|
||||
NAME: opensuse-leap
|
||||
|
||||
python-container:
|
||||
extends: .container_job_template
|
||||
variables:
|
||||
NAME: python
|
||||
|
41
.gitlab-ci.d/crossbuild-template.yml
Normal file
41
.gitlab-ci.d/crossbuild-template.yml
Normal file
@ -0,0 +1,41 @@
|
||||
.cross_system_build_job:
|
||||
stage: build
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
|
||||
timeout: 80m
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- PKG_CONFIG_PATH=$PKG_CONFIG_PATH
|
||||
../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS
|
||||
--disable-user --target-list-exclude="arm-softmmu cris-softmmu
|
||||
i386-softmmu microblaze-softmmu mips-softmmu mipsel-softmmu
|
||||
mips64-softmmu ppc-softmmu sh4-softmmu xtensa-softmmu"
|
||||
- make -j$(expr $(nproc) + 1) all check-build $MAKE_CHECK_ARGS
|
||||
|
||||
# Job to cross-build specific accelerators.
|
||||
#
|
||||
# Set the $ACCEL variable to select the specific accelerator (default to
|
||||
# KVM), and set extra options (such disabling other accelerators) via the
|
||||
# $EXTRA_CONFIGURE_OPTS variable.
|
||||
.cross_accel_build_job:
|
||||
stage: build
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
|
||||
timeout: 30m
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- PKG_CONFIG_PATH=$PKG_CONFIG_PATH
|
||||
../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS
|
||||
--disable-tools --enable-${ACCEL:-kvm} $EXTRA_CONFIGURE_OPTS
|
||||
- make -j$(expr $(nproc) + 1) all check-build $MAKE_CHECK_ARGS
|
||||
|
||||
.cross_user_build_job:
|
||||
stage: build
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- PKG_CONFIG_PATH=$PKG_CONFIG_PATH
|
||||
../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS
|
||||
--disable-system
|
||||
- make -j$(expr $(nproc) + 1) all check-build $MAKE_CHECK_ARGS
|
@ -1,44 +1,5 @@
|
||||
.cross_system_build_job:
|
||||
stage: build
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
|
||||
timeout: 80m
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- PKG_CONFIG_PATH=$PKG_CONFIG_PATH
|
||||
../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS
|
||||
--disable-user --target-list-exclude="arm-softmmu cris-softmmu
|
||||
i386-softmmu microblaze-softmmu mips-softmmu mipsel-softmmu
|
||||
mips64-softmmu ppc-softmmu sh4-softmmu xtensa-softmmu"
|
||||
- make -j$(expr $(nproc) + 1) all check-build $MAKE_CHECK_ARGS
|
||||
|
||||
# Job to cross-build specific accelerators.
|
||||
#
|
||||
# Set the $ACCEL variable to select the specific accelerator (default to
|
||||
# KVM), and set extra options (such disabling other accelerators) via the
|
||||
# $ACCEL_CONFIGURE_OPTS variable.
|
||||
.cross_accel_build_job:
|
||||
stage: build
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
|
||||
timeout: 30m
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- PKG_CONFIG_PATH=$PKG_CONFIG_PATH
|
||||
../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS
|
||||
--disable-tools --enable-${ACCEL:-kvm} $ACCEL_CONFIGURE_OPTS
|
||||
- make -j$(expr $(nproc) + 1) all check-build
|
||||
|
||||
.cross_user_build_job:
|
||||
stage: build
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- PKG_CONFIG_PATH=$PKG_CONFIG_PATH
|
||||
../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS
|
||||
--disable-system
|
||||
- make -j$(expr $(nproc) + 1) all check-build $MAKE_CHECK_ARGS
|
||||
include:
|
||||
- local: '/.gitlab-ci.d/crossbuild-template.yml'
|
||||
|
||||
cross-armel-system:
|
||||
extends: .cross_system_build_job
|
||||
@ -98,6 +59,15 @@ cross-i386-user:
|
||||
IMAGE: fedora-i386-cross
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
cross-i386-tci:
|
||||
extends: .cross_accel_build_job
|
||||
timeout: 60m
|
||||
variables:
|
||||
IMAGE: fedora-i386-cross
|
||||
ACCEL: tcg-interpreter
|
||||
EXTRA_CONFIGURE_OPTS: --target-list=i386-softmmu,i386-linux-user,aarch64-softmmu,aarch64-linux-user,ppc-softmmu,ppc-linux-user
|
||||
MAKE_CHECK_ARGS: check check-tcg
|
||||
|
||||
cross-mips-system:
|
||||
extends: .cross_system_build_job
|
||||
needs:
|
||||
@ -174,7 +144,15 @@ cross-s390x-kvm-only:
|
||||
job: s390x-debian-cross-container
|
||||
variables:
|
||||
IMAGE: debian-s390x-cross
|
||||
ACCEL_CONFIGURE_OPTS: --disable-tcg
|
||||
EXTRA_CONFIGURE_OPTS: --disable-tcg
|
||||
|
||||
cross-mips64el-kvm-only:
|
||||
extends: .cross_accel_build_job
|
||||
needs:
|
||||
job: mips64el-debian-cross-container
|
||||
variables:
|
||||
IMAGE: debian-mips64el-cross
|
||||
EXTRA_CONFIGURE_OPTS: --disable-tcg --target-list=mips64el-softmmu
|
||||
|
||||
cross-win32-system:
|
||||
extends: .cross_system_build_job
|
||||
@ -197,7 +175,7 @@ cross-amd64-xen-only:
|
||||
variables:
|
||||
IMAGE: debian-amd64-cross
|
||||
ACCEL: xen
|
||||
ACCEL_CONFIGURE_OPTS: --disable-tcg --disable-kvm
|
||||
EXTRA_CONFIGURE_OPTS: --disable-tcg --disable-kvm
|
||||
|
||||
cross-arm64-xen-only:
|
||||
extends: .cross_accel_build_job
|
||||
@ -206,4 +184,4 @@ cross-arm64-xen-only:
|
||||
variables:
|
||||
IMAGE: debian-arm64-cross
|
||||
ACCEL: xen
|
||||
ACCEL_CONFIGURE_OPTS: --disable-tcg --disable-kvm
|
||||
EXTRA_CONFIGURE_OPTS: --disable-tcg --disable-kvm
|
||||
|
11
.gitlab-ci.d/qemu-project.yml
Normal file
11
.gitlab-ci.d/qemu-project.yml
Normal file
@ -0,0 +1,11 @@
|
||||
# This file contains the set of jobs run by the QEMU project:
|
||||
# https://gitlab.com/qemu-project/qemu/-/pipelines
|
||||
|
||||
include:
|
||||
- local: '/.gitlab-ci.d/stages.yml'
|
||||
- local: '/.gitlab-ci.d/edk2.yml'
|
||||
- local: '/.gitlab-ci.d/opensbi.yml'
|
||||
- local: '/.gitlab-ci.d/containers.yml'
|
||||
- local: '/.gitlab-ci.d/crossbuilds.yml'
|
||||
- local: '/.gitlab-ci.d/buildtest.yml'
|
||||
- local: '/.gitlab-ci.d/static_checks.yml'
|
8
.gitlab-ci.d/stages.yml
Normal file
8
.gitlab-ci.d/stages.yml
Normal file
@ -0,0 +1,8 @@
|
||||
# Currently we have two build stages after our containers are built:
|
||||
# - build (for traditional build and test or first stage build)
|
||||
# - test (for test stages, using build artefacts from a build stage)
|
||||
stages:
|
||||
- containers
|
||||
- containers-layer2
|
||||
- build
|
||||
- test
|
48
.gitlab-ci.d/static_checks.yml
Normal file
48
.gitlab-ci.d/static_checks.yml
Normal file
@ -0,0 +1,48 @@
|
||||
check-patch:
|
||||
stage: build
|
||||
image: $CI_REGISTRY_IMAGE/qemu/centos8:latest
|
||||
needs:
|
||||
job: amd64-centos8-container
|
||||
script:
|
||||
- .gitlab-ci.d/check-patch.py
|
||||
variables:
|
||||
GIT_DEPTH: 1000
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
||||
when: never
|
||||
- when: on_success
|
||||
allow_failure: true
|
||||
|
||||
check-dco:
|
||||
stage: build
|
||||
image: $CI_REGISTRY_IMAGE/qemu/centos8:latest
|
||||
needs:
|
||||
job: amd64-centos8-container
|
||||
script: .gitlab-ci.d/check-dco.py
|
||||
variables:
|
||||
GIT_DEPTH: 1000
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
||||
when: never
|
||||
- when: on_success
|
||||
|
||||
check-python-pipenv:
|
||||
stage: test
|
||||
image: $CI_REGISTRY_IMAGE/qemu/python:latest
|
||||
script:
|
||||
- make -C python check-pipenv
|
||||
variables:
|
||||
GIT_DEPTH: 1
|
||||
needs:
|
||||
job: python-container
|
||||
|
||||
check-python-tox:
|
||||
stage: test
|
||||
image: $CI_REGISTRY_IMAGE/qemu/python:latest
|
||||
script:
|
||||
- make -C python check-tox
|
||||
variables:
|
||||
GIT_DEPTH: 1
|
||||
needs:
|
||||
job: python-container
|
||||
allow_failure: true
|
872
.gitlab-ci.yml
872
.gitlab-ci.yml
@ -1,837 +1,39 @@
|
||||
# Currently we have two build stages after our containers are built:
|
||||
# - build (for traditional build and test or first stage build)
|
||||
# - test (for test stages, using build artefacts from a build stage)
|
||||
stages:
|
||||
- containers
|
||||
- containers-layer2
|
||||
- build
|
||||
- test
|
||||
#
|
||||
# This is the GitLab CI configuration file for the mainstream QEMU
|
||||
# project: https://gitlab.com/qemu-project/qemu/-/pipelines
|
||||
#
|
||||
# !!! DO NOT ADD ANY NEW CONFIGURATION TO THIS FILE !!!
|
||||
#
|
||||
# Only documentation or comments is accepted.
|
||||
#
|
||||
# To use a different set of jobs than the mainstream QEMU project,
|
||||
# you need to set the location of your custom yml file at "custom CI/CD
|
||||
# configuration path", on your GitLab CI namespace:
|
||||
# https://docs.gitlab.com/ee/ci/pipelines/settings.html#custom-cicd-configuration-path
|
||||
#
|
||||
# ----------------------------------------------------------------------
|
||||
#
|
||||
# QEMU CI jobs are based on templates. Some templates provide
|
||||
# user-configurable options, modifiable via configuration variables.
|
||||
#
|
||||
# These variables can be set globally in the user's CI namespace
|
||||
# setting:
|
||||
# https://docs.gitlab.com/ee/ci/variables/#create-a-custom-variable-in-the-ui
|
||||
# or set manually each time a branch/tag is pushed, as a git-push
|
||||
# command line argument:
|
||||
# https://docs.gitlab.com/ee/user/project/push_options.html#push-options-for-gitlab-cicd
|
||||
#
|
||||
# Example setting the QEMU_CI_EXAMPLE_VAR variable:
|
||||
#
|
||||
# git push -o ci.variable="QEMU_CI_EXAMPLE_VAR=value" myrepo mybranch
|
||||
#
|
||||
# ----------------------------------------------------------------------
|
||||
#
|
||||
# List of environment variables that can be use to modify the set
|
||||
# of jobs selected:
|
||||
#
|
||||
# - QEMU_CI_AVOCADO_TESTING
|
||||
# If set, tests using the Avocado framework will be run
|
||||
|
||||
include:
|
||||
- local: '/.gitlab-ci.d/edk2.yml'
|
||||
- local: '/.gitlab-ci.d/opensbi.yml'
|
||||
- local: '/.gitlab-ci.d/containers.yml'
|
||||
- local: '/.gitlab-ci.d/crossbuilds.yml'
|
||||
|
||||
.native_build_job_template: &native_build_job_definition
|
||||
stage: build
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
|
||||
before_script:
|
||||
- JOBS=$(expr $(nproc) + 1)
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- if test -n "$TARGETS";
|
||||
then
|
||||
../configure --enable-werror --disable-docs $CONFIGURE_ARGS --target-list="$TARGETS" ;
|
||||
else
|
||||
../configure --enable-werror --disable-docs $CONFIGURE_ARGS ;
|
||||
fi || { cat config.log meson-logs/meson-log.txt && exit 1; }
|
||||
- if test -n "$LD_JOBS";
|
||||
then
|
||||
meson configure . -Dbackend_max_links="$LD_JOBS" ;
|
||||
fi || exit 1;
|
||||
- make -j"$JOBS"
|
||||
- if test -n "$MAKE_CHECK_ARGS";
|
||||
then
|
||||
make -j"$JOBS" $MAKE_CHECK_ARGS ;
|
||||
fi
|
||||
|
||||
.native_test_job_template: &native_test_job_definition
|
||||
stage: test
|
||||
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
|
||||
script:
|
||||
- scripts/git-submodule.sh update
|
||||
$(sed -n '/GIT_SUBMODULES=/ s/.*=// p' build/config-host.mak)
|
||||
- cd build
|
||||
- find . -type f -exec touch {} +
|
||||
# Avoid recompiling by hiding ninja with NINJA=":"
|
||||
- make NINJA=":" $MAKE_CHECK_ARGS
|
||||
|
||||
.acceptance_template: &acceptance_definition
|
||||
cache:
|
||||
key: "${CI_JOB_NAME}-cache"
|
||||
paths:
|
||||
- ${CI_PROJECT_DIR}/avocado-cache
|
||||
policy: pull-push
|
||||
artifacts:
|
||||
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
|
||||
when: always
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build/tests/results/latest/results.xml
|
||||
- build/tests/results/latest/test-results
|
||||
reports:
|
||||
junit: build/tests/results/latest/results.xml
|
||||
before_script:
|
||||
- mkdir -p ~/.config/avocado
|
||||
- echo "[datadir.paths]" > ~/.config/avocado/avocado.conf
|
||||
- echo "cache_dirs = ['${CI_PROJECT_DIR}/avocado-cache']"
|
||||
>> ~/.config/avocado/avocado.conf
|
||||
- echo -e '[job.output.testlogs]\nstatuses = ["FAIL", "INTERRUPT"]'
|
||||
>> ~/.config/avocado/avocado.conf
|
||||
- if [ -d ${CI_PROJECT_DIR}/avocado-cache ]; then
|
||||
du -chs ${CI_PROJECT_DIR}/avocado-cache ;
|
||||
fi
|
||||
- export AVOCADO_ALLOW_UNTRUSTED_CODE=1
|
||||
after_script:
|
||||
- cd build
|
||||
- du -chs ${CI_PROJECT_DIR}/avocado-cache
|
||||
|
||||
build-system-alpine:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
- job: amd64-alpine-container
|
||||
variables:
|
||||
IMAGE: alpine
|
||||
TARGETS: aarch64-softmmu alpha-softmmu cris-softmmu hppa-softmmu
|
||||
moxie-softmmu microblazeel-softmmu mips64el-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
CONFIGURE_ARGS: --enable-docs --enable-trace-backends=log,simple,syslog
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- .git-submodule-status
|
||||
- build
|
||||
|
||||
check-system-alpine:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-system-alpine
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: alpine
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-system-alpine:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-system-alpine
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: alpine
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
<<: *acceptance_definition
|
||||
|
||||
build-system-ubuntu:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-ubuntu2004-container
|
||||
variables:
|
||||
IMAGE: ubuntu2004
|
||||
CONFIGURE_ARGS: --enable-docs --enable-fdt=system --enable-slirp=system
|
||||
TARGETS: aarch64-softmmu alpha-softmmu cris-softmmu hppa-softmmu
|
||||
moxie-softmmu microblazeel-softmmu mips64el-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-system-ubuntu:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-system-ubuntu
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: ubuntu2004
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-system-ubuntu:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-system-ubuntu
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: ubuntu2004
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
<<: *acceptance_definition
|
||||
|
||||
build-system-debian:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-debian-container
|
||||
variables:
|
||||
IMAGE: debian-amd64
|
||||
CONFIGURE_ARGS: --enable-fdt=system
|
||||
TARGETS: arm-softmmu avr-softmmu i386-softmmu mipsel-softmmu
|
||||
riscv64-softmmu sh4eb-softmmu sparc-softmmu xtensaeb-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-system-debian:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-system-debian
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: debian-amd64
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-system-debian:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-system-debian
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: debian-amd64
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
<<: *acceptance_definition
|
||||
|
||||
build-system-fedora:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-fedora-container
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs
|
||||
--enable-fdt=system --enable-slirp=system --enable-capstone=system
|
||||
TARGETS: tricore-softmmu microblaze-softmmu mips-softmmu
|
||||
xtensa-softmmu m68k-softmmu riscv32-softmmu ppc-softmmu sparc64-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-system-fedora:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-system-fedora
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-system-fedora:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-system-fedora
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
<<: *acceptance_definition
|
||||
|
||||
build-system-centos:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-centos8-container
|
||||
variables:
|
||||
IMAGE: centos8
|
||||
CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-fdt=system
|
||||
--enable-modules --enable-trace-backends=dtrace
|
||||
TARGETS: ppc64-softmmu or1k-softmmu s390x-softmmu
|
||||
x86_64-softmmu rx-softmmu sh4-softmmu nios2-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-system-centos:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-system-centos
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: centos8
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-system-centos:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-system-centos
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: centos8
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
<<: *acceptance_definition
|
||||
|
||||
build-system-opensuse:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-opensuse-leap-container
|
||||
variables:
|
||||
IMAGE: opensuse-leap
|
||||
CONFIGURE_ARGS: --enable-fdt=system
|
||||
TARGETS: s390x-softmmu x86_64-softmmu aarch64-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-system-opensuse:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-system-opensuse
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: opensuse-leap
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-system-opensuse:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-system-opensuse
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: opensuse-leap
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
<<: *acceptance_definition
|
||||
|
||||
|
||||
build-disabled:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-fedora-container
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
CONFIGURE_ARGS:
|
||||
--disable-attr
|
||||
--disable-auth-pam
|
||||
--disable-avx2
|
||||
--disable-bochs
|
||||
--disable-brlapi
|
||||
--disable-bzip2
|
||||
--disable-cap-ng
|
||||
--disable-capstone
|
||||
--disable-cloop
|
||||
--disable-coroutine-pool
|
||||
--disable-curl
|
||||
--disable-curses
|
||||
--disable-dmg
|
||||
--disable-docs
|
||||
--disable-gcrypt
|
||||
--disable-glusterfs
|
||||
--disable-gnutls
|
||||
--disable-gtk
|
||||
--disable-guest-agent
|
||||
--disable-iconv
|
||||
--disable-keyring
|
||||
--disable-kvm
|
||||
--disable-libiscsi
|
||||
--disable-libpmem
|
||||
--disable-libssh
|
||||
--disable-libudev
|
||||
--disable-libusb
|
||||
--disable-libxml2
|
||||
--disable-linux-aio
|
||||
--disable-live-block-migration
|
||||
--disable-lzo
|
||||
--disable-malloc-trim
|
||||
--disable-mpath
|
||||
--disable-nettle
|
||||
--disable-numa
|
||||
--disable-opengl
|
||||
--disable-parallels
|
||||
--disable-pie
|
||||
--disable-qcow1
|
||||
--disable-qed
|
||||
--disable-qom-cast-debug
|
||||
--disable-rbd
|
||||
--disable-rdma
|
||||
--disable-replication
|
||||
--disable-sdl
|
||||
--disable-seccomp
|
||||
--disable-sheepdog
|
||||
--disable-slirp
|
||||
--disable-smartcard
|
||||
--disable-snappy
|
||||
--disable-sparse
|
||||
--disable-spice
|
||||
--disable-strip
|
||||
--disable-tpm
|
||||
--disable-usb-redir
|
||||
--disable-vdi
|
||||
--disable-vhost-crypto
|
||||
--disable-vhost-net
|
||||
--disable-vhost-scsi
|
||||
--disable-vhost-kernel
|
||||
--disable-vhost-user
|
||||
--disable-vhost-vdpa
|
||||
--disable-vhost-vsock
|
||||
--disable-virglrenderer
|
||||
--disable-vnc
|
||||
--disable-vte
|
||||
--disable-vvfat
|
||||
--disable-xen
|
||||
--disable-zstd
|
||||
TARGETS: arm-softmmu i386-softmmu ppc64-softmmu mips64-softmmu
|
||||
s390x-softmmu i386-linux-user
|
||||
MAKE_CHECK_ARGS: check-qtest SPEED=slow
|
||||
|
||||
# This jobs explicitly disable TCG (--disable-tcg), KVM is detected by
|
||||
# the configure script. The container doesn't contain Xen headers so
|
||||
# Xen accelerator is not detected / selected. As result it build the
|
||||
# i386-softmmu and x86_64-softmmu with KVM being the single accelerator
|
||||
# available.
|
||||
# Also use a different coroutine implementation (which is only really of
|
||||
# interest to KVM users, i.e. with TCG disabled)
|
||||
build-tcg-disabled:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-centos8-container
|
||||
variables:
|
||||
IMAGE: centos8
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-tcg --audio-drv-list="" --with-coroutine=ucontext
|
||||
|| { cat config.log meson-logs/meson-log.txt && exit 1; }
|
||||
- make -j"$JOBS"
|
||||
- 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 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 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:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-debian-user-cross-container
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
CONFIGURE_ARGS: --disable-tools --disable-system
|
||||
MAKE_CHECK_ARGS: check-tcg
|
||||
|
||||
build-user-static:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-debian-user-cross-container
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
CONFIGURE_ARGS: --disable-tools --disable-system --static
|
||||
MAKE_CHECK_ARGS: check-tcg
|
||||
|
||||
# Only build the softmmu targets we have check-tcg tests for
|
||||
build-some-softmmu:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-debian-user-cross-container
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
CONFIGURE_ARGS: --disable-tools --enable-debug
|
||||
TARGETS: xtensa-softmmu arm-softmmu aarch64-softmmu alpha-softmmu
|
||||
MAKE_CHECK_ARGS: check-tcg
|
||||
|
||||
# Run check-tcg against linux-user (with plugins)
|
||||
# we skip sparc64-linux-user until it has been fixed somewhat
|
||||
# we skip cris-linux-user as it doesn't use the common run loop
|
||||
build-user-plugins:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-debian-user-cross-container
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
CONFIGURE_ARGS: --disable-tools --disable-system --enable-plugins --enable-debug-tcg --target-list-exclude=sparc64-linux-user,cris-linux-user
|
||||
MAKE_CHECK_ARGS: check-tcg
|
||||
timeout: 1h 30m
|
||||
|
||||
build-user-centos7:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-centos7-container
|
||||
variables:
|
||||
IMAGE: centos7
|
||||
CONFIGURE_ARGS: --disable-system --disable-tools --disable-docs
|
||||
MAKE_CHECK_ARGS: check-tcg
|
||||
|
||||
build-some-softmmu-plugins:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-debian-user-cross-container
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
CONFIGURE_ARGS: --disable-tools --disable-user --enable-plugins --enable-debug-tcg
|
||||
TARGETS: xtensa-softmmu arm-softmmu aarch64-softmmu alpha-softmmu
|
||||
MAKE_CHECK_ARGS: check-tcg
|
||||
|
||||
clang-system:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-fedora-container
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
CONFIGURE_ARGS: --cc=clang --cxx=clang++
|
||||
--extra-cflags=-fsanitize=undefined --extra-cflags=-fno-sanitize-recover=undefined
|
||||
TARGETS: alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu
|
||||
ppc-softmmu s390x-softmmu
|
||||
MAKE_CHECK_ARGS: check-qtest check-tcg
|
||||
|
||||
clang-user:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-debian-user-cross-container
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --disable-system
|
||||
--target-list-exclude=microblazeel-linux-user,aarch64_be-linux-user,i386-linux-user,m68k-linux-user,mipsn32el-linux-user,xtensaeb-linux-user
|
||||
--extra-cflags=-fsanitize=undefined --extra-cflags=-fno-sanitize-recover=undefined
|
||||
MAKE_CHECK_ARGS: check-unit check-tcg
|
||||
|
||||
# Set LD_JOBS=1 because this requires LTO and ld consumes a large amount of memory.
|
||||
# On gitlab runners, default value sometimes end up calling 2 lds concurrently and
|
||||
# triggers an Out-Of-Memory error
|
||||
#
|
||||
# Since slirp callbacks are used in QEMU Timers, slirp needs to be compiled together
|
||||
# with QEMU and linked as a static library to avoid false positives in CFI checks.
|
||||
# This can be accomplished by using -enable-slirp=git, which avoids the use of
|
||||
# a system-wide version of the library
|
||||
#
|
||||
# Split in three sets of build/check/acceptance to limit the execution time of each
|
||||
# job
|
||||
build-cfi-aarch64:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
- job: amd64-fedora-container
|
||||
variables:
|
||||
LD_JOBS: 1
|
||||
AR: llvm-ar
|
||||
IMAGE: fedora
|
||||
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug
|
||||
--enable-safe-stack --enable-slirp=git
|
||||
TARGETS: aarch64-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
timeout: 70m
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-cfi-aarch64:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-cfi-aarch64
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-cfi-aarch64:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-cfi-aarch64
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
<<: *acceptance_definition
|
||||
|
||||
build-cfi-ppc64-s390x:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
- job: amd64-fedora-container
|
||||
variables:
|
||||
LD_JOBS: 1
|
||||
AR: llvm-ar
|
||||
IMAGE: fedora
|
||||
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug
|
||||
--enable-safe-stack --enable-slirp=git
|
||||
TARGETS: ppc64-softmmu s390x-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
timeout: 70m
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-cfi-ppc64-s390x:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-cfi-ppc64-s390x
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-cfi-ppc64-s390x:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-cfi-ppc64-s390x
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
<<: *acceptance_definition
|
||||
|
||||
build-cfi-x86_64:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
- job: amd64-fedora-container
|
||||
variables:
|
||||
LD_JOBS: 1
|
||||
AR: llvm-ar
|
||||
IMAGE: fedora
|
||||
CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug
|
||||
--enable-safe-stack --enable-slirp=git
|
||||
TARGETS: x86_64-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
timeout: 70m
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-cfi-x86_64:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-cfi-x86_64
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
acceptance-cfi-x86_64:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-cfi-x86_64
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
<<: *acceptance_definition
|
||||
|
||||
tsan-build:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-ubuntu2004-container
|
||||
variables:
|
||||
IMAGE: ubuntu2004
|
||||
CONFIGURE_ARGS: --enable-tsan --cc=clang-10 --cxx=clang++-10
|
||||
--enable-trace-backends=ust --enable-fdt=system --enable-slirp=system
|
||||
TARGETS: x86_64-softmmu ppc64-softmmu riscv64-softmmu x86_64-linux-user
|
||||
MAKE_CHECK_ARGS: bench V=1
|
||||
|
||||
# These targets are on the way out
|
||||
build-deprecated:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-debian-user-cross-container
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
CONFIGURE_ARGS: --disable-tools
|
||||
MAKE_CHECK_ARGS: build-tcg
|
||||
TARGETS: ppc64abi32-linux-user lm32-softmmu unicore32-softmmu
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
# We split the check-tcg step as test failures are expected but we still
|
||||
# want to catch the build breaking.
|
||||
check-deprecated:
|
||||
<<: *native_test_job_definition
|
||||
needs:
|
||||
- job: build-deprecated
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
MAKE_CHECK_ARGS: check-tcg
|
||||
allow_failure: true
|
||||
|
||||
# gprof/gcov are GCC features
|
||||
gprof-gcov:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-ubuntu2004-container
|
||||
variables:
|
||||
IMAGE: ubuntu2004
|
||||
CONFIGURE_ARGS: --enable-gprof --enable-gcov
|
||||
MAKE_CHECK_ARGS: check
|
||||
TARGETS: aarch64-softmmu ppc64-softmmu s390x-softmmu x86_64-softmmu
|
||||
timeout: 70m
|
||||
after_script:
|
||||
- ${CI_PROJECT_DIR}/scripts/ci/coverage-summary.sh
|
||||
|
||||
build-oss-fuzz:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-fedora-container
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
script:
|
||||
- mkdir build-oss-fuzz
|
||||
- CC="clang" CXX="clang++" CFLAGS="-fsanitize=address"
|
||||
./scripts/oss-fuzz/build.sh
|
||||
- export ASAN_OPTIONS="fast_unwind_on_malloc=0"
|
||||
- for fuzzer in $(find ./build-oss-fuzz/DEST_DIR/ -executable -type f
|
||||
| grep -v slirp); do
|
||||
grep "LLVMFuzzerTestOneInput" ${fuzzer} > /dev/null 2>&1 || continue ;
|
||||
echo Testing ${fuzzer} ... ;
|
||||
"${fuzzer}" -runs=1 -seed=1 || exit 1 ;
|
||||
done
|
||||
# Unrelated to fuzzer: run some tests with -fsanitize=address
|
||||
- cd build-oss-fuzz && make check-qtest-i386 check-unit
|
||||
|
||||
build-tci:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-debian-user-cross-container
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
script:
|
||||
- TARGETS="aarch64 alpha arm hppa m68k microblaze moxie ppc64 s390x x86_64"
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --enable-tcg-interpreter
|
||||
--target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)" || { cat config.log meson-logs/meson-log.txt && exit 1; }
|
||||
- make -j"$JOBS"
|
||||
- make tests/qtest/boot-serial-test tests/qtest/cdrom-test tests/qtest/pxe-test
|
||||
- for tg in $TARGETS ; do
|
||||
export QTEST_QEMU_BINARY="./qemu-system-${tg}" ;
|
||||
./tests/qtest/boot-serial-test || exit 1 ;
|
||||
./tests/qtest/cdrom-test || exit 1 ;
|
||||
done
|
||||
- QTEST_QEMU_BINARY="./qemu-system-x86_64" ./tests/qtest/pxe-test
|
||||
- QTEST_QEMU_BINARY="./qemu-system-s390x" ./tests/qtest/pxe-test -m slow
|
||||
- make check-tcg
|
||||
|
||||
# Alternate coroutines implementations are only really of interest to KVM users
|
||||
# However we can't test against KVM on Gitlab-CI so we can only run unit tests
|
||||
build-coroutine-sigaltstack:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-ubuntu2004-container
|
||||
variables:
|
||||
IMAGE: ubuntu2004
|
||||
CONFIGURE_ARGS: --with-coroutine=sigaltstack --disable-tcg
|
||||
--enable-trace-backends=ftrace
|
||||
MAKE_CHECK_ARGS: check-unit
|
||||
|
||||
# Most jobs test latest gcrypt or nettle builds
|
||||
#
|
||||
# These jobs test old gcrypt and nettle from RHEL7
|
||||
# which had some API differences.
|
||||
crypto-old-nettle:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-centos7-container
|
||||
variables:
|
||||
IMAGE: centos7
|
||||
TARGETS: x86_64-softmmu x86_64-linux-user
|
||||
CONFIGURE_ARGS: --disable-gcrypt --enable-nettle
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
crypto-old-gcrypt:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-centos7-container
|
||||
variables:
|
||||
IMAGE: centos7
|
||||
TARGETS: x86_64-softmmu x86_64-linux-user
|
||||
CONFIGURE_ARGS: --disable-nettle --enable-gcrypt
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
crypto-only-gnutls:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-centos7-container
|
||||
variables:
|
||||
IMAGE: centos7
|
||||
TARGETS: x86_64-softmmu x86_64-linux-user
|
||||
CONFIGURE_ARGS: --disable-nettle --disable-gcrypt --enable-gnutls
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
|
||||
# Check our reduced build configurations
|
||||
build-without-default-devices:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-centos8-container
|
||||
variables:
|
||||
IMAGE: centos8
|
||||
CONFIGURE_ARGS: --without-default-devices --disable-user
|
||||
|
||||
build-without-default-features:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-debian-container
|
||||
variables:
|
||||
IMAGE: debian-amd64
|
||||
CONFIGURE_ARGS: --without-default-features --disable-user
|
||||
--target-list-exclude=arm-softmmu,i386-softmmu,mipsel-softmmu,mips64-softmmu,ppc-softmmu
|
||||
MAKE_CHECK_ARGS: check-unit
|
||||
|
||||
check-patch:
|
||||
stage: build
|
||||
image: $CI_REGISTRY_IMAGE/qemu/centos8:latest
|
||||
needs:
|
||||
job: amd64-centos8-container
|
||||
script: .gitlab-ci.d/check-patch.py
|
||||
except:
|
||||
variables:
|
||||
- $CI_PROJECT_NAMESPACE == 'qemu-project' && $CI_COMMIT_BRANCH == 'master'
|
||||
variables:
|
||||
GIT_DEPTH: 1000
|
||||
allow_failure: true
|
||||
|
||||
check-dco:
|
||||
stage: build
|
||||
image: $CI_REGISTRY_IMAGE/qemu/centos8:latest
|
||||
needs:
|
||||
job: amd64-centos8-container
|
||||
script: .gitlab-ci.d/check-dco.py
|
||||
except:
|
||||
variables:
|
||||
- $CI_PROJECT_NAMESPACE == 'qemu-project' && $CI_COMMIT_BRANCH == 'master'
|
||||
variables:
|
||||
GIT_DEPTH: 1000
|
||||
|
||||
build-libvhost-user:
|
||||
stage: build
|
||||
image: $CI_REGISTRY_IMAGE/qemu/fedora:latest
|
||||
needs:
|
||||
job: amd64-fedora-container
|
||||
before_script:
|
||||
- dnf install -y meson ninja-build
|
||||
script:
|
||||
- mkdir subprojects/libvhost-user/build
|
||||
- cd subprojects/libvhost-user/build
|
||||
- meson
|
||||
- ninja
|
||||
|
||||
# No targets are built here, just tools, docs, and unit tests. This
|
||||
# also feeds into the eventual documentation deployment steps later
|
||||
build-tools-and-docs-debian:
|
||||
<<: *native_build_job_definition
|
||||
needs:
|
||||
job: amd64-debian-container
|
||||
variables:
|
||||
IMAGE: debian-amd64
|
||||
MAKE_CHECK_ARGS: check-unit check-softfloat ctags TAGS cscope
|
||||
CONFIGURE_ARGS: --disable-system --disable-user --enable-docs --enable-tools
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
# Prepare for GitLab pages deployment. Anything copied into the
|
||||
# "public" directory will be deployed to $USER.gitlab.io/$PROJECT
|
||||
pages:
|
||||
image: $CI_REGISTRY_IMAGE/qemu/debian-amd64:latest
|
||||
stage: test
|
||||
needs:
|
||||
- job: build-tools-and-docs-debian
|
||||
script:
|
||||
- mkdir -p public
|
||||
# HTML-ised source tree
|
||||
- make gtags
|
||||
- htags -anT --tree-view=filetree -m qemu_init
|
||||
-t "Welcome to the QEMU sourcecode"
|
||||
- mv HTML public/src
|
||||
# Project documentation
|
||||
- make -C build install DESTDIR=$(pwd)/temp-install
|
||||
- mv temp-install/usr/local/share/doc/qemu/* public/
|
||||
artifacts:
|
||||
paths:
|
||||
- public
|
||||
- local: '/.gitlab-ci.d/qemu-project.yml'
|
||||
|
64
.gitlab/issue_templates/bug.md
Normal file
64
.gitlab/issue_templates/bug.md
Normal file
@ -0,0 +1,64 @@
|
||||
<!--
|
||||
This is the upstream QEMU issue tracker.
|
||||
|
||||
If you are able to, it will greatly facilitate bug triage if you attempt
|
||||
to reproduce the problem with the latest qemu.git master built from
|
||||
source. See https://www.qemu.org/download/#source for instructions on
|
||||
how to do this.
|
||||
|
||||
QEMU generally supports the last two releases advertised on
|
||||
https://www.qemu.org/. Problems with distro-packaged versions of QEMU
|
||||
older than this should be reported to the distribution instead.
|
||||
|
||||
See https://www.qemu.org/contribute/report-a-bug/ for additional
|
||||
guidance.
|
||||
|
||||
If this is a security issue, please consult
|
||||
https://www.qemu.org/contribute/security-process/
|
||||
-->
|
||||
|
||||
## Host environment
|
||||
- Operating system: (Windows 10 21H1, Fedora 34, etc.)
|
||||
- OS/kernel version: (For POSIX hosts, use `uname -a`)
|
||||
- Architecture: (x86, ARM, s390x, etc.)
|
||||
- QEMU flavor: (qemu-system-x86_64, qemu-aarch64, qemu-img, etc.)
|
||||
- QEMU version: (e.g. `qemu-system-x86_64 --version`)
|
||||
- QEMU command line:
|
||||
<!--
|
||||
Give the smallest, complete command line that exhibits the problem.
|
||||
|
||||
If you are using libvirt, virsh, or vmm, you can likely find the QEMU
|
||||
command line arguments in /var/log/libvirt/qemu/$GUEST.log.
|
||||
-->
|
||||
```
|
||||
./qemu-system-x86_64 -M q35 -m 4096 -enable-kvm -hda fedora32.qcow2
|
||||
```
|
||||
|
||||
## Emulated/Virtualized environment
|
||||
- Operating system: (Windows 10 21H1, Fedora 34, etc.)
|
||||
- OS/kernel version: (For POSIX guests, use `uname -a`.)
|
||||
- Architecture: (x86, ARM, s390x, etc.)
|
||||
|
||||
|
||||
## Description of problem
|
||||
<!-- Describe the problem, including any error/crash messages seen. -->
|
||||
|
||||
|
||||
## Steps to reproduce
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
|
||||
## Additional information
|
||||
|
||||
<!--
|
||||
Attach logs, stack traces, screenshots, etc. Compress the files if necessary.
|
||||
If using libvirt, libvirt logs and XML domain information may be relevant.
|
||||
-->
|
||||
|
||||
<!--
|
||||
The line below ensures that proper tags are added to the issue.
|
||||
Please do not remove it.
|
||||
-->
|
||||
/label ~"kind::Bug"
|
32
.gitlab/issue_templates/feature_request.md
Normal file
32
.gitlab/issue_templates/feature_request.md
Normal file
@ -0,0 +1,32 @@
|
||||
<!--
|
||||
This is the upstream QEMU issue tracker.
|
||||
|
||||
Please note that QEMU, like most open source projects, relies on
|
||||
contributors who have motivation, skills and available time to work on
|
||||
implementing particular features.
|
||||
|
||||
Feature requests can be helpful for determining demand and interest, but
|
||||
they are not a guarantee that a contributor will volunteer to implement
|
||||
it. We welcome and encourage even draft patches to implement a feature
|
||||
be sent to the mailing list where it can be discussed and developed
|
||||
further by the community.
|
||||
|
||||
Thank you for your interest in helping us to make QEMU better!
|
||||
-->
|
||||
|
||||
## Goal
|
||||
<!-- Describe the final result you want to achieve. Avoid design specifics. -->
|
||||
|
||||
|
||||
## Technical details
|
||||
<!-- Describe technical details, design specifics, suggestions, versions, etc. -->
|
||||
|
||||
|
||||
## Additional information
|
||||
<!-- Patch or branch references, any other useful information -->
|
||||
|
||||
<!--
|
||||
The line below ensures that proper tags are added to the issue.
|
||||
Please do not remove it.
|
||||
-->
|
||||
/label ~"kind::Feature Request"
|
@ -88,7 +88,7 @@ email:
|
||||
more information:
|
||||
|
||||
{{ logtext }}
|
||||
{% elif test == "docker-mingw@fedora" or test == "docker-quick@centos7" or test == "asan" %}
|
||||
{% elif test == "docker-mingw@fedora" or test == "docker-quick@centos8" or test == "asan" %}
|
||||
Hi,
|
||||
|
||||
This series failed the {{ test }} build test. Please find the testing commands and
|
||||
@ -124,13 +124,13 @@ testing:
|
||||
script: |
|
||||
#!/bin/bash
|
||||
time make docker-test-debug@fedora TARGET_LIST=x86_64-softmmu J=14 NETWORK=1
|
||||
docker-quick@centos7:
|
||||
docker-quick@centos8:
|
||||
enabled: false
|
||||
requirements: docker,x86_64
|
||||
timeout: 3600
|
||||
script: |
|
||||
#!/bin/bash
|
||||
time make docker-test-quick@centos7 SHOW_ENV=1 J=14 NETWORK=1
|
||||
time make docker-test-quick@centos8 SHOW_ENV=1 J=14 NETWORK=1
|
||||
checkpatch:
|
||||
enabled: true
|
||||
requirements: ''
|
||||
@ -138,9 +138,6 @@ testing:
|
||||
script: |
|
||||
#!/bin/bash
|
||||
git rev-parse base > /dev/null || exit 0
|
||||
git config --local diff.renamelimit 0
|
||||
git config --local diff.renames True
|
||||
git config --local diff.algorithm histogram
|
||||
./scripts/checkpatch.pl --mailback base..
|
||||
docker-mingw@fedora:
|
||||
enabled: true
|
||||
|
150
MAINTAINERS
150
MAINTAINERS
@ -128,7 +128,6 @@ F: docs/devel/decodetree.rst
|
||||
F: include/exec/cpu*.h
|
||||
F: include/exec/exec-all.h
|
||||
F: include/exec/helper*.h
|
||||
F: include/exec/tb-hash.h
|
||||
F: include/sysemu/cpus.h
|
||||
F: include/sysemu/tcg.h
|
||||
F: include/hw/core/tcg-cpu-ops.h
|
||||
@ -156,6 +155,7 @@ S: Maintained
|
||||
F: target/arm/
|
||||
F: tests/tcg/arm/
|
||||
F: tests/tcg/aarch64/
|
||||
F: tests/qtest/arm-cpu-features.c
|
||||
F: hw/arm/
|
||||
F: hw/cpu/a*mpcore.c
|
||||
F: include/hw/cpu/a*mpcore.h
|
||||
@ -197,6 +197,8 @@ F: linux-user/hexagon/
|
||||
F: tests/tcg/hexagon/
|
||||
F: disas/hexagon.c
|
||||
F: default-configs/targets/hexagon-linux-user.mak
|
||||
F: docker/dockerfiles/debian-hexagon-cross.docker
|
||||
F: docker/dockerfiles/debian-hexagon-cross.docker.d/build-toolchain.sh
|
||||
|
||||
HPPA (PA-RISC) TCG CPUs
|
||||
M: Richard Henderson <richard.henderson@linaro.org>
|
||||
@ -207,19 +209,6 @@ F: disas/hppa.c
|
||||
F: hw/net/*i82596*
|
||||
F: include/hw/net/lasi_82596.h
|
||||
|
||||
LM32 TCG CPUs
|
||||
R: Michael Walle <michael@walle.cc>
|
||||
S: Orphan
|
||||
F: target/lm32/
|
||||
F: disas/lm32.c
|
||||
F: hw/lm32/
|
||||
F: hw/*/lm32_*
|
||||
F: hw/*/milkymist-*
|
||||
F: include/hw/display/milkymist_tmu2.h
|
||||
F: include/hw/char/lm32_juart.h
|
||||
F: include/hw/lm32/
|
||||
F: tests/tcg/lm32/
|
||||
|
||||
M68K TCG CPUs
|
||||
M: Laurent Vivier <laurent@vivier.eu>
|
||||
S: Maintained
|
||||
@ -257,14 +246,7 @@ K: ^Subject:.*(?i)mips
|
||||
MIPS TCG CPUs (nanoMIPS ISA)
|
||||
S: Orphan
|
||||
F: disas/nanomips.*
|
||||
|
||||
Moxie TCG CPUs
|
||||
M: Anthony Green <green@moxielogic.com>
|
||||
S: Maintained
|
||||
F: target/moxie/
|
||||
F: disas/moxie.c
|
||||
F: hw/moxie/
|
||||
F: default-configs/*/moxie-softmmu.mak
|
||||
F: target/mips/tcg/*nanomips*
|
||||
|
||||
NiosII TCG CPUs
|
||||
M: Chris Wulff <crwulff@gmail.com>
|
||||
@ -295,9 +277,8 @@ F: tests/acceptance/machine_ppc.py
|
||||
|
||||
RISC-V TCG CPUs
|
||||
M: Palmer Dabbelt <palmer@dabbelt.com>
|
||||
M: Alistair Francis <Alistair.Francis@wdc.com>
|
||||
M: Sagar Karandikar <sagark@eecs.berkeley.edu>
|
||||
M: Bastian Koppelmann <kbastian@mail.uni-paderborn.de>
|
||||
M: Alistair Francis <alistair.francis@wdc.com>
|
||||
M: Bin Meng <bin.meng@windriver.com>
|
||||
L: qemu-riscv@nongnu.org
|
||||
S: Supported
|
||||
F: target/riscv/
|
||||
@ -339,24 +320,17 @@ F: hw/sparc64/
|
||||
F: include/hw/sparc/sparc64.h
|
||||
F: disas/sparc.c
|
||||
|
||||
UniCore32 TCG CPUs
|
||||
M: Guan Xuetao <gxt@mprc.pku.edu.cn>
|
||||
S: Maintained
|
||||
F: target/unicore32/
|
||||
F: hw/unicore32/
|
||||
F: include/hw/unicore32/
|
||||
|
||||
X86 TCG CPUs
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
M: Richard Henderson <richard.henderson@linaro.org>
|
||||
M: Eduardo Habkost <ehabkost@redhat.com>
|
||||
S: Maintained
|
||||
F: target/i386/
|
||||
F: target/i386/tcg/
|
||||
F: tests/tcg/i386/
|
||||
F: tests/tcg/x86_64/
|
||||
F: hw/i386/
|
||||
F: disas/i386.c
|
||||
F: docs/system/cpu-models-x86.rst.inc
|
||||
F: docs/system/cpu-models-x86*
|
||||
T: git https://gitlab.com/ehabkost/qemu.git x86-next
|
||||
|
||||
Xtensa TCG CPUs
|
||||
@ -376,6 +350,7 @@ S: Maintained
|
||||
F: target/tricore/
|
||||
F: hw/tricore/
|
||||
F: include/hw/tricore/
|
||||
F: tests/tcg/tricore/
|
||||
|
||||
Multiarch Linux User Tests
|
||||
M: Alex Bennée <alex.bennee@linaro.org>
|
||||
@ -404,7 +379,8 @@ F: target/arm/kvm.c
|
||||
MIPS KVM CPUs
|
||||
M: Huacai Chen <chenhuacai@kernel.org>
|
||||
S: Odd Fixes
|
||||
F: target/mips/kvm.c
|
||||
F: target/mips/kvm*
|
||||
F: target/mips/sysemu/
|
||||
|
||||
PPC KVM CPUs
|
||||
M: David Gibson <david@gibson.dropbear.id.au>
|
||||
@ -462,7 +438,15 @@ M: Roman Bolshakov <r.bolshakov@yadro.com>
|
||||
W: https://wiki.qemu.org/Features/HVF
|
||||
S: Maintained
|
||||
F: target/i386/hvf/
|
||||
|
||||
HVF
|
||||
M: Cameron Esfahani <dirty@apple.com>
|
||||
M: Roman Bolshakov <r.bolshakov@yadro.com>
|
||||
W: https://wiki.qemu.org/Features/HVF
|
||||
S: Maintained
|
||||
F: accel/hvf/
|
||||
F: include/sysemu/hvf.h
|
||||
F: include/sysemu/hvf_int.h
|
||||
|
||||
WHPX CPUs
|
||||
M: Sunil Muthuswamy <sunilmut@microsoft.com>
|
||||
@ -509,6 +493,15 @@ F: accel/stubs/hax-stub.c
|
||||
F: include/sysemu/hax.h
|
||||
F: target/i386/hax/
|
||||
|
||||
Guest CPU Cores (NVMM)
|
||||
----------------------
|
||||
NetBSD Virtual Machine Monitor (NVMM) CPU support
|
||||
M: Kamil Rytarowski <kamil@netbsd.org>
|
||||
M: Reinoud Zandijk <reinoud@netbsd.org>
|
||||
S: Maintained
|
||||
F: include/sysemu/nvmm.h
|
||||
F: target/i386/nvmm/
|
||||
|
||||
Hosts
|
||||
-----
|
||||
LINUX
|
||||
@ -529,6 +522,8 @@ F: include/qemu/*posix*.h
|
||||
|
||||
NETBSD
|
||||
M: Kamil Rytarowski <kamil@netbsd.org>
|
||||
M: Reinoud Zandijk <reinoud@netbsd.org>
|
||||
M: Ryo ONODERA <ryoon@netbsd.org>
|
||||
S: Maintained
|
||||
K: ^Subject:.*(?i)NetBSD
|
||||
|
||||
@ -1026,6 +1021,7 @@ F: include/hw/misc/pca9552*.h
|
||||
F: hw/net/ftgmac100.c
|
||||
F: include/hw/net/ftgmac100.h
|
||||
F: docs/system/arm/aspeed.rst
|
||||
F: tests/qtest/*aspeed*
|
||||
|
||||
NRF51
|
||||
M: Joel Stanley <joel@jms.id.au>
|
||||
@ -1037,6 +1033,7 @@ F: hw/*/microbit*.c
|
||||
F: include/hw/*/nrf51*.h
|
||||
F: include/hw/*/microbit*.h
|
||||
F: tests/qtest/microbit-test.c
|
||||
F: docs/system/arm/nrf.rst
|
||||
|
||||
AVR Machines
|
||||
-------------
|
||||
@ -1076,18 +1073,6 @@ F: default-configs/*/hppa-softmmu.mak
|
||||
F: hw/hppa/
|
||||
F: pc-bios/hppa-firmware.img
|
||||
|
||||
LM32 Machines
|
||||
-------------
|
||||
EVR32 and uclinux BSP
|
||||
R: Michael Walle <michael@walle.cc>
|
||||
S: Orphan
|
||||
F: hw/lm32/lm32_boards.c
|
||||
|
||||
milkymist
|
||||
R: Michael Walle <michael@walle.cc>
|
||||
S: Orphan
|
||||
F: hw/lm32/milkymist.c
|
||||
|
||||
M68K Machines
|
||||
-------------
|
||||
an5206
|
||||
@ -1193,6 +1178,7 @@ F: hw/isa/vt82c686.c
|
||||
F: hw/pci-host/bonito.c
|
||||
F: hw/usb/vt82c686-uhci-pci.c
|
||||
F: include/hw/isa/vt82c686.h
|
||||
F: tests/acceptance/machine_mips_fuloong2e.py
|
||||
|
||||
Loongson-3 virtual platforms
|
||||
M: Huacai Chen <chenhuacai@kernel.org>
|
||||
@ -1364,6 +1350,16 @@ F: pc-bios/canyonlands.dt[sb]
|
||||
F: pc-bios/u-boot-sam460ex-20100605.bin
|
||||
F: roms/u-boot-sam460ex
|
||||
|
||||
pegasos2
|
||||
M: BALATON Zoltan <balaton@eik.bme.hu>
|
||||
R: David Gibson <david@gibson.dropbear.id.au>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/ppc/pegasos2.c
|
||||
F: hw/pci-host/mv64361.c
|
||||
F: hw/pci-host/mv643xx.h
|
||||
F: include/hw/pci-host/mv64361.h
|
||||
|
||||
RISC-V Machines
|
||||
---------------
|
||||
OpenTitan
|
||||
@ -1371,11 +1367,9 @@ M: Alistair Francis <Alistair.Francis@wdc.com>
|
||||
L: qemu-riscv@nongnu.org
|
||||
S: Supported
|
||||
F: hw/riscv/opentitan.c
|
||||
F: hw/char/ibex_uart.c
|
||||
F: hw/intc/ibex_plic.c
|
||||
F: hw/*/ibex_*.c
|
||||
F: include/hw/riscv/opentitan.h
|
||||
F: include/hw/char/ibex_uart.h
|
||||
F: include/hw/intc/ibex_plic.h
|
||||
F: include/hw/*/ibex_*.h
|
||||
|
||||
Microchip PolarFire SoC Icicle Kit
|
||||
M: Bin Meng <bin.meng@windriver.com>
|
||||
@ -1392,6 +1386,15 @@ F: include/hw/misc/mchp_pfsoc_dmc.h
|
||||
F: include/hw/misc/mchp_pfsoc_ioscb.h
|
||||
F: include/hw/misc/mchp_pfsoc_sysreg.h
|
||||
|
||||
Shakti C class SoC
|
||||
M: Vijai Kumar K <vijai@behindbytes.com>
|
||||
L: qemu-riscv@nongnu.org
|
||||
S: Supported
|
||||
F: hw/riscv/shakti_c.c
|
||||
F: hw/char/shakti_uart.c
|
||||
F: include/hw/riscv/shakti_c.h
|
||||
F: include/hw/char/shakti_uart.h
|
||||
|
||||
SiFive Machines
|
||||
M: Alistair Francis <Alistair.Francis@wdc.com>
|
||||
M: Bin Meng <bin.meng@windriver.com>
|
||||
@ -1518,14 +1521,6 @@ F: hw/s390x/s390-pci*
|
||||
F: include/hw/s390x/s390-pci*
|
||||
L: qemu-s390x@nongnu.org
|
||||
|
||||
UniCore32 Machines
|
||||
------------------
|
||||
PKUnity-3 SoC initramfs-with-busybox
|
||||
M: Guan Xuetao <gxt@mprc.pku.edu.cn>
|
||||
S: Maintained
|
||||
F: hw/*/puv3*
|
||||
F: hw/unicore32/
|
||||
|
||||
X86 Machines
|
||||
------------
|
||||
PC
|
||||
@ -1676,6 +1671,9 @@ M: John Snow <jsnow@redhat.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: hw/block/fdc.c
|
||||
F: hw/block/fdc-internal.h
|
||||
F: hw/block/fdc-isa.c
|
||||
F: hw/block/fdc-sysbus.c
|
||||
F: include/hw/block/fdc.h
|
||||
F: tests/qtest/fdc-test.c
|
||||
T: git https://gitlab.com/jsnow/qemu.git ide
|
||||
@ -1804,6 +1802,7 @@ USB
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
F: hw/usb/*
|
||||
F: stubs/usb-dev-stub.c
|
||||
F: tests/qtest/usb-*-test.c
|
||||
F: docs/usb2.txt
|
||||
F: docs/usb-storage.txt
|
||||
@ -1823,6 +1822,7 @@ S: Supported
|
||||
F: hw/vfio/*
|
||||
F: include/hw/vfio/
|
||||
F: docs/igd-assign.txt
|
||||
F: docs/devel/vfio-migration.rst
|
||||
|
||||
vfio-ccw
|
||||
M: Cornelia Huck <cohuck@redhat.com>
|
||||
@ -1972,7 +1972,7 @@ M: Keith Busch <kbusch@kernel.org>
|
||||
M: Klaus Jensen <its@irrelevant.dk>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: hw/block/nvme*
|
||||
F: hw/nvme/*
|
||||
F: include/block/nvme.h
|
||||
F: tests/qtest/nvme-test.c
|
||||
F: docs/system/nvme.rst
|
||||
@ -2040,6 +2040,12 @@ S: Maintained
|
||||
F: hw/net/tulip.c
|
||||
F: hw/net/tulip.h
|
||||
|
||||
pca954x
|
||||
M: Patrick Venture <venture@google.com>
|
||||
S: Maintained
|
||||
F: hw/i2c/i2c_mux_pca954x.c
|
||||
F: include/hw/i2c/i2c_mux_pca954x.h
|
||||
|
||||
Generic Loader
|
||||
M: Alistair Francis <alistair@alistair23.me>
|
||||
S: Maintained
|
||||
@ -2221,6 +2227,7 @@ F: qapi/audio.json
|
||||
F: tests/qtest/ac97-test.c
|
||||
F: tests/qtest/es1370-test.c
|
||||
F: tests/qtest/intel-hda-test.c
|
||||
F: tests/qtest/fuzz-sb16-test.c
|
||||
|
||||
Block layer core
|
||||
M: Kevin Wolf <kwolf@redhat.com>
|
||||
@ -2445,6 +2452,7 @@ F: ui/cocoa.m
|
||||
Main loop
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Maintained
|
||||
F: include/exec/gen-icount.h
|
||||
F: include/qemu/main-loop.h
|
||||
F: include/sysemu/runstate.h
|
||||
F: include/sysemu/runstate-action.h
|
||||
@ -2531,6 +2539,7 @@ Benchmark util
|
||||
M: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
|
||||
S: Maintained
|
||||
F: scripts/simplebench/
|
||||
T: git https://src.openvz.org/scm/~vsementsov/qemu.git simplebench
|
||||
|
||||
Transactions helper
|
||||
M: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
|
||||
@ -2698,14 +2707,13 @@ F: scripts/tracetool.py
|
||||
F: scripts/tracetool/
|
||||
F: scripts/qemu-trace-stap*
|
||||
F: docs/tools/qemu-trace-stap.rst
|
||||
F: docs/devel/tracing.txt
|
||||
F: docs/devel/tracing.rst
|
||||
T: git https://github.com/stefanha/qemu.git tracing
|
||||
|
||||
TPM
|
||||
M: Stefan Berger <stefanb@linux.ibm.com>
|
||||
S: Maintained
|
||||
F: tpm.c
|
||||
F: stubs/tpm.c
|
||||
F: hw/tpm/*
|
||||
F: include/hw/acpi/tpm.h
|
||||
F: include/sysemu/tpm*
|
||||
@ -3056,12 +3064,6 @@ L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: block/rbd.c
|
||||
|
||||
Sheepdog
|
||||
M: Liu Yuan <namei.unix@gmail.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: block/sheepdog.c
|
||||
|
||||
VHDX
|
||||
M: Jeff Cody <codyprime@gmail.com>
|
||||
L: qemu-block@nongnu.org
|
||||
@ -3260,6 +3262,8 @@ F: block/export/vhost-user-blk-server.c
|
||||
F: block/export/vhost-user-blk-server.h
|
||||
F: include/qemu/vhost-user-server.h
|
||||
F: tests/qtest/libqos/vhost-user-blk.c
|
||||
F: tests/qtest/libqos/vhost-user-blk.h
|
||||
F: tests/qtest/vhost-user-blk-test.c
|
||||
F: util/vhost-user-server.c
|
||||
|
||||
FUSE block device exports
|
||||
@ -3318,6 +3322,14 @@ F: include/hw/remote/proxy-memory-listener.h
|
||||
F: hw/remote/iohub.c
|
||||
F: include/hw/remote/iohub.h
|
||||
|
||||
EBPF:
|
||||
M: Jason Wang <jasowang@redhat.com>
|
||||
R: Andrew Melnychenko <andrew@daynix.com>
|
||||
R: Yuri Benditovich <yuri.benditovich@daynix.com>
|
||||
S: Maintained
|
||||
F: ebpf/*
|
||||
F: tools/ebpf/*
|
||||
|
||||
Build and test automation
|
||||
-------------------------
|
||||
Build and test automation, general continuous integration
|
||||
@ -3371,7 +3383,7 @@ Documentation
|
||||
Build system architecture
|
||||
M: Daniel P. Berrange <berrange@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: docs/devel/build-system.txt
|
||||
F: docs/devel/build-system.rst
|
||||
|
||||
GIT Data Mining Config
|
||||
M: Alex Bennée <alex.bennee@linaro.org>
|
||||
|
4
Makefile
4
Makefile
@ -48,9 +48,11 @@ Makefile: .git-submodule-status
|
||||
|
||||
.PHONY: git-submodule-update
|
||||
git-submodule-update:
|
||||
ifneq ($(GIT_SUBMODULES_ACTION),ignore)
|
||||
$(call quiet-command, \
|
||||
(GIT="$(GIT)" "$(SRC_PATH)/scripts/git-submodule.sh" $(GIT_SUBMODULES_ACTION) $(GIT_SUBMODULES)), \
|
||||
"GIT","$(GIT_SUBMODULES)")
|
||||
endif
|
||||
|
||||
# 0. ensure the build tree is okay
|
||||
|
||||
@ -213,7 +215,7 @@ qemu-%.tar.bz2:
|
||||
|
||||
distclean: clean
|
||||
-$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) -t clean -g || :
|
||||
rm -f config-host.mak config-host.h*
|
||||
rm -f config-host.mak config-host.h* config-poison.h
|
||||
rm -f tests/tcg/config-*.mak
|
||||
rm -f config-all-disas.mak config.status
|
||||
rm -f roms/seabios/config.mak roms/vgabios/config.mak
|
||||
|
@ -131,16 +131,16 @@ will be tagged as my-feature-v2.
|
||||
Bug reporting
|
||||
=============
|
||||
|
||||
The QEMU project uses Launchpad as its primary upstream bug tracker. Bugs
|
||||
The QEMU project uses GitLab issues to track bugs. Bugs
|
||||
found when running code built from QEMU git or upstream released sources
|
||||
should be reported via:
|
||||
|
||||
* `<https://bugs.launchpad.net/qemu/>`_
|
||||
* `<https://gitlab.com/qemu-project/qemu/-/issues>`_
|
||||
|
||||
If using QEMU via an operating system vendor pre-built binary package, it
|
||||
is preferable to report bugs to the vendor's own bug tracker first. If
|
||||
the bug is also known to affect latest upstream code, it can also be
|
||||
reported via launchpad.
|
||||
reported via GitLab.
|
||||
|
||||
For additional information on bug reporting consult:
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
config WHPX
|
||||
bool
|
||||
|
||||
config NVMM
|
||||
bool
|
||||
|
||||
config HAX
|
||||
bool
|
||||
|
||||
|
@ -54,10 +54,23 @@ static void accel_init_cpu_int_aux(ObjectClass *klass, void *opaque)
|
||||
CPUClass *cc = CPU_CLASS(klass);
|
||||
AccelCPUClass *accel_cpu = opaque;
|
||||
|
||||
/*
|
||||
* The first callback allows accel-cpu to run initializations
|
||||
* for the CPU, customizing CPU behavior according to the accelerator.
|
||||
*
|
||||
* The second one allows the CPU to customize the accel-cpu
|
||||
* behavior according to the CPU.
|
||||
*
|
||||
* The second is currently only used by TCG, to specialize the
|
||||
* TCGCPUOps depending on the CPU type.
|
||||
*/
|
||||
cc->accel_cpu = accel_cpu;
|
||||
if (accel_cpu->cpu_class_init) {
|
||||
accel_cpu->cpu_class_init(cc);
|
||||
}
|
||||
if (cc->init_accel_cpu) {
|
||||
cc->init_accel_cpu(accel_cpu, cc);
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize the arch-specific accel CpuClass interfaces */
|
||||
@ -89,6 +102,25 @@ void accel_init_interfaces(AccelClass *ac)
|
||||
accel_init_cpu_interfaces(ac);
|
||||
}
|
||||
|
||||
void accel_cpu_instance_init(CPUState *cpu)
|
||||
{
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
|
||||
if (cc->accel_cpu && cc->accel_cpu->cpu_instance_init) {
|
||||
cc->accel_cpu->cpu_instance_init(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
bool accel_cpu_realizefn(CPUState *cpu, Error **errp)
|
||||
{
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
|
||||
if (cc->accel_cpu && cc->accel_cpu->cpu_realizefn) {
|
||||
return cc->accel_cpu->cpu_realizefn(cpu, errp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static const TypeInfo accel_cpu_type = {
|
||||
.name = TYPE_ACCEL_CPU,
|
||||
.parent = TYPE_OBJECT,
|
||||
|
471
accel/hvf/hvf-accel-ops.c
Normal file
471
accel/hvf/hvf-accel-ops.c
Normal file
@ -0,0 +1,471 @@
|
||||
/*
|
||||
* Copyright 2008 IBM Corporation
|
||||
* 2008 Red Hat, Inc.
|
||||
* Copyright 2011 Intel Corporation
|
||||
* Copyright 2016 Veertu, Inc.
|
||||
* Copyright 2017 The Android Open Source Project
|
||||
*
|
||||
* QEMU Hypervisor.framework support
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
* This file contain code under public domain from the hvdos project:
|
||||
* https://github.com/mist64/hvdos
|
||||
*
|
||||
* Parts Copyright (c) 2011 NetApp, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/hvf.h"
|
||||
#include "sysemu/hvf_int.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "qemu/guest-random.h"
|
||||
|
||||
HVFState *hvf_state;
|
||||
|
||||
/* Memory slots */
|
||||
|
||||
hvf_slot *hvf_find_overlap_slot(uint64_t start, uint64_t size)
|
||||
{
|
||||
hvf_slot *slot;
|
||||
int x;
|
||||
for (x = 0; x < hvf_state->num_slots; ++x) {
|
||||
slot = &hvf_state->slots[x];
|
||||
if (slot->size && start < (slot->start + slot->size) &&
|
||||
(start + size) > slot->start) {
|
||||
return slot;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mac_slot {
|
||||
int present;
|
||||
uint64_t size;
|
||||
uint64_t gpa_start;
|
||||
uint64_t gva;
|
||||
};
|
||||
|
||||
struct mac_slot mac_slots[32];
|
||||
|
||||
static int do_hvf_set_memory(hvf_slot *slot, hv_memory_flags_t flags)
|
||||
{
|
||||
struct mac_slot *macslot;
|
||||
hv_return_t ret;
|
||||
|
||||
macslot = &mac_slots[slot->slot_id];
|
||||
|
||||
if (macslot->present) {
|
||||
if (macslot->size != slot->size) {
|
||||
macslot->present = 0;
|
||||
ret = hv_vm_unmap(macslot->gpa_start, macslot->size);
|
||||
assert_hvf_ok(ret);
|
||||
}
|
||||
}
|
||||
|
||||
if (!slot->size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
macslot->present = 1;
|
||||
macslot->gpa_start = slot->start;
|
||||
macslot->size = slot->size;
|
||||
ret = hv_vm_map(slot->mem, slot->start, slot->size, flags);
|
||||
assert_hvf_ok(ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
|
||||
{
|
||||
hvf_slot *mem;
|
||||
MemoryRegion *area = section->mr;
|
||||
bool writeable = !area->readonly && !area->rom_device;
|
||||
hv_memory_flags_t flags;
|
||||
|
||||
if (!memory_region_is_ram(area)) {
|
||||
if (writeable) {
|
||||
return;
|
||||
} else if (!memory_region_is_romd(area)) {
|
||||
/*
|
||||
* If the memory device is not in romd_mode, then we actually want
|
||||
* to remove the hvf memory slot so all accesses will trap.
|
||||
*/
|
||||
add = false;
|
||||
}
|
||||
}
|
||||
|
||||
mem = hvf_find_overlap_slot(
|
||||
section->offset_within_address_space,
|
||||
int128_get64(section->size));
|
||||
|
||||
if (mem && add) {
|
||||
if (mem->size == int128_get64(section->size) &&
|
||||
mem->start == section->offset_within_address_space &&
|
||||
mem->mem == (memory_region_get_ram_ptr(area) +
|
||||
section->offset_within_region)) {
|
||||
return; /* Same region was attempted to register, go away. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Region needs to be reset. set the size to 0 and remap it. */
|
||||
if (mem) {
|
||||
mem->size = 0;
|
||||
if (do_hvf_set_memory(mem, 0)) {
|
||||
error_report("Failed to reset overlapping slot");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (!add) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (area->readonly ||
|
||||
(!memory_region_is_ram(area) && memory_region_is_romd(area))) {
|
||||
flags = HV_MEMORY_READ | HV_MEMORY_EXEC;
|
||||
} else {
|
||||
flags = HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC;
|
||||
}
|
||||
|
||||
/* Now make a new slot. */
|
||||
int x;
|
||||
|
||||
for (x = 0; x < hvf_state->num_slots; ++x) {
|
||||
mem = &hvf_state->slots[x];
|
||||
if (!mem->size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (x == hvf_state->num_slots) {
|
||||
error_report("No free slots");
|
||||
abort();
|
||||
}
|
||||
|
||||
mem->size = int128_get64(section->size);
|
||||
mem->mem = memory_region_get_ram_ptr(area) + section->offset_within_region;
|
||||
mem->start = section->offset_within_address_space;
|
||||
mem->region = area;
|
||||
|
||||
if (do_hvf_set_memory(mem, flags)) {
|
||||
error_report("Error registering new memory slot");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void do_hvf_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
|
||||
{
|
||||
if (!cpu->vcpu_dirty) {
|
||||
hvf_get_registers(cpu);
|
||||
cpu->vcpu_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void hvf_cpu_synchronize_state(CPUState *cpu)
|
||||
{
|
||||
if (!cpu->vcpu_dirty) {
|
||||
run_on_cpu(cpu, do_hvf_cpu_synchronize_state, RUN_ON_CPU_NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void do_hvf_cpu_synchronize_set_dirty(CPUState *cpu,
|
||||
run_on_cpu_data arg)
|
||||
{
|
||||
/* QEMU state is the reference, push it to HVF now and on next entry */
|
||||
cpu->vcpu_dirty = true;
|
||||
}
|
||||
|
||||
static void hvf_cpu_synchronize_post_reset(CPUState *cpu)
|
||||
{
|
||||
run_on_cpu(cpu, do_hvf_cpu_synchronize_set_dirty, RUN_ON_CPU_NULL);
|
||||
}
|
||||
|
||||
static void hvf_cpu_synchronize_post_init(CPUState *cpu)
|
||||
{
|
||||
run_on_cpu(cpu, do_hvf_cpu_synchronize_set_dirty, RUN_ON_CPU_NULL);
|
||||
}
|
||||
|
||||
static void hvf_cpu_synchronize_pre_loadvm(CPUState *cpu)
|
||||
{
|
||||
run_on_cpu(cpu, do_hvf_cpu_synchronize_set_dirty, RUN_ON_CPU_NULL);
|
||||
}
|
||||
|
||||
static void hvf_set_dirty_tracking(MemoryRegionSection *section, bool on)
|
||||
{
|
||||
hvf_slot *slot;
|
||||
|
||||
slot = hvf_find_overlap_slot(
|
||||
section->offset_within_address_space,
|
||||
int128_get64(section->size));
|
||||
|
||||
/* protect region against writes; begin tracking it */
|
||||
if (on) {
|
||||
slot->flags |= HVF_SLOT_LOG;
|
||||
hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size,
|
||||
HV_MEMORY_READ);
|
||||
/* stop tracking region*/
|
||||
} else {
|
||||
slot->flags &= ~HVF_SLOT_LOG;
|
||||
hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size,
|
||||
HV_MEMORY_READ | HV_MEMORY_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
static void hvf_log_start(MemoryListener *listener,
|
||||
MemoryRegionSection *section, int old, int new)
|
||||
{
|
||||
if (old != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
hvf_set_dirty_tracking(section, 1);
|
||||
}
|
||||
|
||||
static void hvf_log_stop(MemoryListener *listener,
|
||||
MemoryRegionSection *section, int old, int new)
|
||||
{
|
||||
if (new != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
hvf_set_dirty_tracking(section, 0);
|
||||
}
|
||||
|
||||
static void hvf_log_sync(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
/*
|
||||
* sync of dirty pages is handled elsewhere; just make sure we keep
|
||||
* tracking the region.
|
||||
*/
|
||||
hvf_set_dirty_tracking(section, 1);
|
||||
}
|
||||
|
||||
static void hvf_region_add(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
hvf_set_phys_mem(section, true);
|
||||
}
|
||||
|
||||
static void hvf_region_del(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
hvf_set_phys_mem(section, false);
|
||||
}
|
||||
|
||||
static MemoryListener hvf_memory_listener = {
|
||||
.priority = 10,
|
||||
.region_add = hvf_region_add,
|
||||
.region_del = hvf_region_del,
|
||||
.log_start = hvf_log_start,
|
||||
.log_stop = hvf_log_stop,
|
||||
.log_sync = hvf_log_sync,
|
||||
};
|
||||
|
||||
static void dummy_signal(int sig)
|
||||
{
|
||||
}
|
||||
|
||||
bool hvf_allowed;
|
||||
|
||||
static int hvf_accel_init(MachineState *ms)
|
||||
{
|
||||
int x;
|
||||
hv_return_t ret;
|
||||
HVFState *s;
|
||||
|
||||
ret = hv_vm_create(HV_VM_DEFAULT);
|
||||
assert_hvf_ok(ret);
|
||||
|
||||
s = g_new0(HVFState, 1);
|
||||
|
||||
s->num_slots = 32;
|
||||
for (x = 0; x < s->num_slots; ++x) {
|
||||
s->slots[x].size = 0;
|
||||
s->slots[x].slot_id = x;
|
||||
}
|
||||
|
||||
hvf_state = s;
|
||||
memory_listener_register(&hvf_memory_listener, &address_space_memory);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hvf_accel_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
AccelClass *ac = ACCEL_CLASS(oc);
|
||||
ac->name = "HVF";
|
||||
ac->init_machine = hvf_accel_init;
|
||||
ac->allowed = &hvf_allowed;
|
||||
}
|
||||
|
||||
static const TypeInfo hvf_accel_type = {
|
||||
.name = TYPE_HVF_ACCEL,
|
||||
.parent = TYPE_ACCEL,
|
||||
.class_init = hvf_accel_class_init,
|
||||
};
|
||||
|
||||
static void hvf_type_init(void)
|
||||
{
|
||||
type_register_static(&hvf_accel_type);
|
||||
}
|
||||
|
||||
type_init(hvf_type_init);
|
||||
|
||||
static void hvf_vcpu_destroy(CPUState *cpu)
|
||||
{
|
||||
hv_return_t ret = hv_vcpu_destroy(cpu->hvf->fd);
|
||||
assert_hvf_ok(ret);
|
||||
|
||||
hvf_arch_vcpu_destroy(cpu);
|
||||
g_free(cpu->hvf);
|
||||
cpu->hvf = NULL;
|
||||
}
|
||||
|
||||
static int hvf_init_vcpu(CPUState *cpu)
|
||||
{
|
||||
int r;
|
||||
|
||||
cpu->hvf = g_malloc0(sizeof(*cpu->hvf));
|
||||
|
||||
/* init cpu signals */
|
||||
sigset_t set;
|
||||
struct sigaction sigact;
|
||||
|
||||
memset(&sigact, 0, sizeof(sigact));
|
||||
sigact.sa_handler = dummy_signal;
|
||||
sigaction(SIG_IPI, &sigact, NULL);
|
||||
|
||||
pthread_sigmask(SIG_BLOCK, NULL, &set);
|
||||
sigdelset(&set, SIG_IPI);
|
||||
|
||||
r = hv_vcpu_create((hv_vcpuid_t *)&cpu->hvf->fd, HV_VCPU_DEFAULT);
|
||||
cpu->vcpu_dirty = 1;
|
||||
assert_hvf_ok(r);
|
||||
|
||||
return hvf_arch_init_vcpu(cpu);
|
||||
}
|
||||
|
||||
/*
|
||||
* The HVF-specific vCPU thread function. This one should only run when the host
|
||||
* CPU supports the VMX "unrestricted guest" feature.
|
||||
*/
|
||||
static void *hvf_cpu_thread_fn(void *arg)
|
||||
{
|
||||
CPUState *cpu = arg;
|
||||
|
||||
int r;
|
||||
|
||||
assert(hvf_enabled());
|
||||
|
||||
rcu_register_thread();
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
qemu_thread_get_self(cpu->thread);
|
||||
|
||||
cpu->thread_id = qemu_get_thread_id();
|
||||
cpu->can_do_io = 1;
|
||||
current_cpu = cpu;
|
||||
|
||||
hvf_init_vcpu(cpu);
|
||||
|
||||
/* signal CPU creation */
|
||||
cpu_thread_signal_created(cpu);
|
||||
qemu_guest_random_seed_thread_part2(cpu->random_seed);
|
||||
|
||||
do {
|
||||
if (cpu_can_run(cpu)) {
|
||||
r = hvf_vcpu_exec(cpu);
|
||||
if (r == EXCP_DEBUG) {
|
||||
cpu_handle_guest_debug(cpu);
|
||||
}
|
||||
}
|
||||
qemu_wait_io_event(cpu);
|
||||
} while (!cpu->unplug || cpu_can_run(cpu));
|
||||
|
||||
hvf_vcpu_destroy(cpu);
|
||||
cpu_thread_signal_destroyed(cpu);
|
||||
qemu_mutex_unlock_iothread();
|
||||
rcu_unregister_thread();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void hvf_start_vcpu_thread(CPUState *cpu)
|
||||
{
|
||||
char thread_name[VCPU_THREAD_NAME_SIZE];
|
||||
|
||||
/*
|
||||
* HVF currently does not support TCG, and only runs in
|
||||
* unrestricted-guest mode.
|
||||
*/
|
||||
assert(hvf_enabled());
|
||||
|
||||
cpu->thread = g_malloc0(sizeof(QemuThread));
|
||||
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
|
||||
qemu_cond_init(cpu->halt_cond);
|
||||
|
||||
snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/HVF",
|
||||
cpu->cpu_index);
|
||||
qemu_thread_create(cpu->thread, thread_name, hvf_cpu_thread_fn,
|
||||
cpu, QEMU_THREAD_JOINABLE);
|
||||
}
|
||||
|
||||
static void hvf_accel_ops_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
|
||||
|
||||
ops->create_vcpu_thread = hvf_start_vcpu_thread;
|
||||
|
||||
ops->synchronize_post_reset = hvf_cpu_synchronize_post_reset;
|
||||
ops->synchronize_post_init = hvf_cpu_synchronize_post_init;
|
||||
ops->synchronize_state = hvf_cpu_synchronize_state;
|
||||
ops->synchronize_pre_loadvm = hvf_cpu_synchronize_pre_loadvm;
|
||||
};
|
||||
static const TypeInfo hvf_accel_ops_type = {
|
||||
.name = ACCEL_OPS_NAME("hvf"),
|
||||
|
||||
.parent = TYPE_ACCEL_OPS,
|
||||
.class_init = hvf_accel_ops_class_init,
|
||||
.abstract = true,
|
||||
};
|
||||
static void hvf_accel_ops_register_types(void)
|
||||
{
|
||||
type_register_static(&hvf_accel_ops_type);
|
||||
}
|
||||
type_init(hvf_accel_ops_register_types);
|
47
accel/hvf/hvf-all.c
Normal file
47
accel/hvf/hvf-all.c
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* QEMU Hypervisor.framework support
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
* Contributions after 2012-01-13 are licensed under the terms of the
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "sysemu/hvf.h"
|
||||
#include "sysemu/hvf_int.h"
|
||||
|
||||
void assert_hvf_ok(hv_return_t ret)
|
||||
{
|
||||
if (ret == HV_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ret) {
|
||||
case HV_ERROR:
|
||||
error_report("Error: HV_ERROR");
|
||||
break;
|
||||
case HV_BUSY:
|
||||
error_report("Error: HV_BUSY");
|
||||
break;
|
||||
case HV_BAD_ARGUMENT:
|
||||
error_report("Error: HV_BAD_ARGUMENT");
|
||||
break;
|
||||
case HV_NO_RESOURCES:
|
||||
error_report("Error: HV_NO_RESOURCES");
|
||||
break;
|
||||
case HV_NO_DEVICE:
|
||||
error_report("Error: HV_NO_DEVICE");
|
||||
break;
|
||||
case HV_UNSUPPORTED:
|
||||
error_report("Error: HV_UNSUPPORTED");
|
||||
break;
|
||||
default:
|
||||
error_report("Unknown Error");
|
||||
}
|
||||
|
||||
abort();
|
||||
}
|
7
accel/hvf/meson.build
Normal file
7
accel/hvf/meson.build
Normal file
@ -0,0 +1,7 @@
|
||||
hvf_ss = ss.source_set()
|
||||
hvf_ss.add(files(
|
||||
'hvf-all.c',
|
||||
'hvf-accel-ops.c',
|
||||
))
|
||||
|
||||
specific_ss.add_all(when: 'CONFIG_HVF', if_true: hvf_ss)
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <sys/ioctl.h>
|
||||
#include <poll.h>
|
||||
|
||||
#include <linux/kvm.h>
|
||||
|
||||
@ -30,11 +31,9 @@
|
||||
#include "sysemu/kvm_int.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "exec/memory.h"
|
||||
#include "exec/ram_addr.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/event_notifier.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "trace.h"
|
||||
@ -80,6 +79,25 @@ struct KVMParkedVcpu {
|
||||
QLIST_ENTRY(KVMParkedVcpu) node;
|
||||
};
|
||||
|
||||
enum KVMDirtyRingReaperState {
|
||||
KVM_DIRTY_RING_REAPER_NONE = 0,
|
||||
/* The reaper is sleeping */
|
||||
KVM_DIRTY_RING_REAPER_WAIT,
|
||||
/* The reaper is reaping for dirty pages */
|
||||
KVM_DIRTY_RING_REAPER_REAPING,
|
||||
};
|
||||
|
||||
/*
|
||||
* KVM reaper instance, responsible for collecting the KVM dirty bits
|
||||
* via the dirty ring.
|
||||
*/
|
||||
struct KVMDirtyRingReaper {
|
||||
/* The reaper thread */
|
||||
QemuThread reaper_thr;
|
||||
volatile uint64_t reaper_iteration; /* iteration number of reaper thr */
|
||||
volatile enum KVMDirtyRingReaperState reaper_state; /* reap thr state */
|
||||
};
|
||||
|
||||
struct KVMState
|
||||
{
|
||||
AccelState parent_obj;
|
||||
@ -128,6 +146,9 @@ struct KVMState
|
||||
KVMMemoryListener *ml;
|
||||
AddressSpace *as;
|
||||
} *as;
|
||||
uint64_t kvm_dirty_ring_bytes; /* Size of the per-vcpu dirty ring */
|
||||
uint32_t kvm_dirty_ring_size; /* Number of dirty GFNs per ring */
|
||||
struct KVMDirtyRingReaper reaper;
|
||||
};
|
||||
|
||||
KVMState *kvm_state;
|
||||
@ -174,8 +195,12 @@ typedef struct KVMResampleFd KVMResampleFd;
|
||||
static QLIST_HEAD(, KVMResampleFd) kvm_resample_fd_list =
|
||||
QLIST_HEAD_INITIALIZER(kvm_resample_fd_list);
|
||||
|
||||
#define kvm_slots_lock(kml) qemu_mutex_lock(&(kml)->slots_lock)
|
||||
#define kvm_slots_unlock(kml) qemu_mutex_unlock(&(kml)->slots_lock)
|
||||
static QemuMutex kml_slots_lock;
|
||||
|
||||
#define kvm_slots_lock() qemu_mutex_lock(&kml_slots_lock)
|
||||
#define kvm_slots_unlock() qemu_mutex_unlock(&kml_slots_lock)
|
||||
|
||||
static void kvm_slot_init_dirty_bitmap(KVMSlot *mem);
|
||||
|
||||
static inline void kvm_resample_fd_remove(int gsi)
|
||||
{
|
||||
@ -241,9 +266,9 @@ bool kvm_has_free_slot(MachineState *ms)
|
||||
bool result;
|
||||
KVMMemoryListener *kml = &s->memory_listener;
|
||||
|
||||
kvm_slots_lock(kml);
|
||||
kvm_slots_lock();
|
||||
result = !!kvm_get_free_slot(kml);
|
||||
kvm_slots_unlock(kml);
|
||||
kvm_slots_unlock();
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -309,7 +334,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram,
|
||||
KVMMemoryListener *kml = &s->memory_listener;
|
||||
int i, ret = 0;
|
||||
|
||||
kvm_slots_lock(kml);
|
||||
kvm_slots_lock();
|
||||
for (i = 0; i < s->nr_slots; i++) {
|
||||
KVMSlot *mem = &kml->slots[i];
|
||||
|
||||
@ -319,7 +344,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram,
|
||||
break;
|
||||
}
|
||||
}
|
||||
kvm_slots_unlock(kml);
|
||||
kvm_slots_unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -385,6 +410,13 @@ static int do_kvm_destroy_vcpu(CPUState *cpu)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (cpu->kvm_dirty_gfns) {
|
||||
ret = munmap(cpu->kvm_dirty_gfns, s->kvm_dirty_ring_bytes);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
vcpu = g_malloc0(sizeof(*vcpu));
|
||||
vcpu->vcpu_id = kvm_arch_vcpu_id(cpu);
|
||||
vcpu->kvm_fd = cpu->kvm_fd;
|
||||
@ -461,6 +493,19 @@ int kvm_init_vcpu(CPUState *cpu, Error **errp)
|
||||
(void *)cpu->kvm_run + s->coalesced_mmio * PAGE_SIZE;
|
||||
}
|
||||
|
||||
if (s->kvm_dirty_ring_size) {
|
||||
/* Use MAP_SHARED to share pages with the kernel */
|
||||
cpu->kvm_dirty_gfns = mmap(NULL, s->kvm_dirty_ring_bytes,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||
cpu->kvm_fd,
|
||||
PAGE_SIZE * KVM_DIRTY_LOG_PAGE_OFFSET);
|
||||
if (cpu->kvm_dirty_gfns == MAP_FAILED) {
|
||||
ret = -errno;
|
||||
DPRINTF("mmap'ing vcpu dirty gfns failed: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
ret = kvm_arch_init_vcpu(cpu);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
@ -500,6 +545,7 @@ static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem,
|
||||
return 0;
|
||||
}
|
||||
|
||||
kvm_slot_init_dirty_bitmap(mem);
|
||||
return kvm_set_user_memory_region(kml, mem, false);
|
||||
}
|
||||
|
||||
@ -515,7 +561,7 @@ static int kvm_section_update_flags(KVMMemoryListener *kml,
|
||||
return 0;
|
||||
}
|
||||
|
||||
kvm_slots_lock(kml);
|
||||
kvm_slots_lock();
|
||||
|
||||
while (size && !ret) {
|
||||
slot_size = MIN(kvm_max_slot_size, size);
|
||||
@ -531,7 +577,7 @@ static int kvm_section_update_flags(KVMMemoryListener *kml,
|
||||
}
|
||||
|
||||
out:
|
||||
kvm_slots_unlock(kml);
|
||||
kvm_slots_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -570,22 +616,28 @@ static void kvm_log_stop(MemoryListener *listener,
|
||||
}
|
||||
|
||||
/* get kvm's dirty pages bitmap and update qemu's */
|
||||
static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section,
|
||||
unsigned long *bitmap)
|
||||
static void kvm_slot_sync_dirty_pages(KVMSlot *slot)
|
||||
{
|
||||
ram_addr_t start = section->offset_within_region +
|
||||
memory_region_get_ram_addr(section->mr);
|
||||
ram_addr_t pages = int128_get64(section->size) / qemu_real_host_page_size;
|
||||
ram_addr_t start = slot->ram_start_offset;
|
||||
ram_addr_t pages = slot->memory_size / qemu_real_host_page_size;
|
||||
|
||||
cpu_physical_memory_set_dirty_lebitmap(bitmap, start, pages);
|
||||
return 0;
|
||||
cpu_physical_memory_set_dirty_lebitmap(slot->dirty_bmap, start, pages);
|
||||
}
|
||||
|
||||
static void kvm_slot_reset_dirty_pages(KVMSlot *slot)
|
||||
{
|
||||
memset(slot->dirty_bmap, 0, slot->dirty_bmap_size);
|
||||
}
|
||||
|
||||
#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)
|
||||
static void kvm_slot_init_dirty_bitmap(KVMSlot *mem)
|
||||
{
|
||||
if (!(mem->flags & KVM_MEM_LOG_DIRTY_PAGES) || mem->dirty_bmap) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX bad kernel interface alert
|
||||
* For dirty bitmap, kernel allocates array of size aligned to
|
||||
@ -606,6 +658,196 @@ static void kvm_memslot_init_dirty_bitmap(KVMSlot *mem)
|
||||
hwaddr bitmap_size = ALIGN(mem->memory_size / qemu_real_host_page_size,
|
||||
/*HOST_LONG_BITS*/ 64) / 8;
|
||||
mem->dirty_bmap = g_malloc0(bitmap_size);
|
||||
mem->dirty_bmap_size = bitmap_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sync dirty bitmap from kernel to KVMSlot.dirty_bmap, return true if
|
||||
* succeeded, false otherwise
|
||||
*/
|
||||
static bool kvm_slot_get_dirty_log(KVMState *s, KVMSlot *slot)
|
||||
{
|
||||
struct kvm_dirty_log d = {};
|
||||
int ret;
|
||||
|
||||
d.dirty_bitmap = slot->dirty_bmap;
|
||||
d.slot = slot->slot | (slot->as_id << 16);
|
||||
ret = kvm_vm_ioctl(s, KVM_GET_DIRTY_LOG, &d);
|
||||
|
||||
if (ret == -ENOENT) {
|
||||
/* kernel does not have dirty bitmap in this slot */
|
||||
ret = 0;
|
||||
}
|
||||
if (ret) {
|
||||
error_report_once("%s: KVM_GET_DIRTY_LOG failed with %d",
|
||||
__func__, ret);
|
||||
}
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
/* Should be with all slots_lock held for the address spaces. */
|
||||
static void kvm_dirty_ring_mark_page(KVMState *s, uint32_t as_id,
|
||||
uint32_t slot_id, uint64_t offset)
|
||||
{
|
||||
KVMMemoryListener *kml;
|
||||
KVMSlot *mem;
|
||||
|
||||
if (as_id >= s->nr_as) {
|
||||
return;
|
||||
}
|
||||
|
||||
kml = s->as[as_id].ml;
|
||||
mem = &kml->slots[slot_id];
|
||||
|
||||
if (!mem->memory_size || offset >=
|
||||
(mem->memory_size / qemu_real_host_page_size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
set_bit(offset, mem->dirty_bmap);
|
||||
}
|
||||
|
||||
static bool dirty_gfn_is_dirtied(struct kvm_dirty_gfn *gfn)
|
||||
{
|
||||
return gfn->flags == KVM_DIRTY_GFN_F_DIRTY;
|
||||
}
|
||||
|
||||
static void dirty_gfn_set_collected(struct kvm_dirty_gfn *gfn)
|
||||
{
|
||||
gfn->flags = KVM_DIRTY_GFN_F_RESET;
|
||||
}
|
||||
|
||||
/*
|
||||
* Should be with all slots_lock held for the address spaces. It returns the
|
||||
* dirty page we've collected on this dirty ring.
|
||||
*/
|
||||
static uint32_t kvm_dirty_ring_reap_one(KVMState *s, CPUState *cpu)
|
||||
{
|
||||
struct kvm_dirty_gfn *dirty_gfns = cpu->kvm_dirty_gfns, *cur;
|
||||
uint32_t ring_size = s->kvm_dirty_ring_size;
|
||||
uint32_t count = 0, fetch = cpu->kvm_fetch_index;
|
||||
|
||||
assert(dirty_gfns && ring_size);
|
||||
trace_kvm_dirty_ring_reap_vcpu(cpu->cpu_index);
|
||||
|
||||
while (true) {
|
||||
cur = &dirty_gfns[fetch % ring_size];
|
||||
if (!dirty_gfn_is_dirtied(cur)) {
|
||||
break;
|
||||
}
|
||||
kvm_dirty_ring_mark_page(s, cur->slot >> 16, cur->slot & 0xffff,
|
||||
cur->offset);
|
||||
dirty_gfn_set_collected(cur);
|
||||
trace_kvm_dirty_ring_page(cpu->cpu_index, fetch, cur->offset);
|
||||
fetch++;
|
||||
count++;
|
||||
}
|
||||
cpu->kvm_fetch_index = fetch;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Must be with slots_lock held */
|
||||
static uint64_t kvm_dirty_ring_reap_locked(KVMState *s)
|
||||
{
|
||||
int ret;
|
||||
CPUState *cpu;
|
||||
uint64_t total = 0;
|
||||
int64_t stamp;
|
||||
|
||||
stamp = get_clock();
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
total += kvm_dirty_ring_reap_one(s, cpu);
|
||||
}
|
||||
|
||||
if (total) {
|
||||
ret = kvm_vm_ioctl(s, KVM_RESET_DIRTY_RINGS);
|
||||
assert(ret == total);
|
||||
}
|
||||
|
||||
stamp = get_clock() - stamp;
|
||||
|
||||
if (total) {
|
||||
trace_kvm_dirty_ring_reap(total, stamp / 1000);
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/*
|
||||
* Currently for simplicity, we must hold BQL before calling this. We can
|
||||
* consider to drop the BQL if we're clear with all the race conditions.
|
||||
*/
|
||||
static uint64_t kvm_dirty_ring_reap(KVMState *s)
|
||||
{
|
||||
uint64_t total;
|
||||
|
||||
/*
|
||||
* We need to lock all kvm slots for all address spaces here,
|
||||
* because:
|
||||
*
|
||||
* (1) We need to mark dirty for dirty bitmaps in multiple slots
|
||||
* and for tons of pages, so it's better to take the lock here
|
||||
* once rather than once per page. And more importantly,
|
||||
*
|
||||
* (2) We must _NOT_ publish dirty bits to the other threads
|
||||
* (e.g., the migration thread) via the kvm memory slot dirty
|
||||
* bitmaps before correctly re-protect those dirtied pages.
|
||||
* Otherwise we can have potential risk of data corruption if
|
||||
* the page data is read in the other thread before we do
|
||||
* reset below.
|
||||
*/
|
||||
kvm_slots_lock();
|
||||
total = kvm_dirty_ring_reap_locked(s);
|
||||
kvm_slots_unlock();
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static void do_kvm_cpu_synchronize_kick(CPUState *cpu, run_on_cpu_data arg)
|
||||
{
|
||||
/* No need to do anything */
|
||||
}
|
||||
|
||||
/*
|
||||
* Kick all vcpus out in a synchronized way. When returned, we
|
||||
* guarantee that every vcpu has been kicked and at least returned to
|
||||
* userspace once.
|
||||
*/
|
||||
static void kvm_cpu_synchronize_kick_all(void)
|
||||
{
|
||||
CPUState *cpu;
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
run_on_cpu(cpu, do_kvm_cpu_synchronize_kick, RUN_ON_CPU_NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush all the existing dirty pages to the KVM slot buffers. When
|
||||
* this call returns, we guarantee that all the touched dirty pages
|
||||
* before calling this function have been put into the per-kvmslot
|
||||
* dirty bitmap.
|
||||
*
|
||||
* This function must be called with BQL held.
|
||||
*/
|
||||
static void kvm_dirty_ring_flush(void)
|
||||
{
|
||||
trace_kvm_dirty_ring_flush(0);
|
||||
/*
|
||||
* The function needs to be serialized. Since this function
|
||||
* should always be with BQL held, serialization is guaranteed.
|
||||
* However, let's be sure of it.
|
||||
*/
|
||||
assert(qemu_mutex_iothread_locked());
|
||||
/*
|
||||
* First make sure to flush the hardware buffers by kicking all
|
||||
* vcpus out in a synchronous way.
|
||||
*/
|
||||
kvm_cpu_synchronize_kick_all();
|
||||
kvm_dirty_ring_reap(kvm_state);
|
||||
trace_kvm_dirty_ring_flush(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -619,53 +861,28 @@ static void kvm_memslot_init_dirty_bitmap(KVMSlot *mem)
|
||||
* @kml: the KVM memory listener object
|
||||
* @section: the memory section to sync the dirty bitmap with
|
||||
*/
|
||||
static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
|
||||
MemoryRegionSection *section)
|
||||
static void kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
KVMState *s = kvm_state;
|
||||
struct kvm_dirty_log d = {};
|
||||
KVMSlot *mem;
|
||||
hwaddr start_addr, size;
|
||||
hwaddr slot_size, slot_offset = 0;
|
||||
int ret = 0;
|
||||
hwaddr slot_size;
|
||||
|
||||
size = kvm_align_section(section, &start_addr);
|
||||
while (size) {
|
||||
MemoryRegionSection subsection = *section;
|
||||
|
||||
slot_size = MIN(kvm_max_slot_size, size);
|
||||
mem = kvm_lookup_matching_slot(kml, start_addr, slot_size);
|
||||
if (!mem) {
|
||||
/* We don't have a slot if we want to trap every access. */
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mem->dirty_bmap) {
|
||||
/* Allocate on the first log_sync, once and for all */
|
||||
kvm_memslot_init_dirty_bitmap(mem);
|
||||
if (kvm_slot_get_dirty_log(s, mem)) {
|
||||
kvm_slot_sync_dirty_pages(mem);
|
||||
}
|
||||
|
||||
d.dirty_bitmap = mem->dirty_bmap;
|
||||
d.slot = mem->slot | (kml->as_id << 16);
|
||||
ret = kvm_vm_ioctl(s, KVM_GET_DIRTY_LOG, &d);
|
||||
if (ret == -ENOENT) {
|
||||
/* kernel does not have dirty bitmap in this slot */
|
||||
ret = 0;
|
||||
} else if (ret < 0) {
|
||||
error_report("ioctl KVM_GET_DIRTY_LOG failed: %d", errno);
|
||||
goto out;
|
||||
} else {
|
||||
subsection.offset_within_region += slot_offset;
|
||||
subsection.size = int128_make64(slot_size);
|
||||
kvm_get_dirty_pages_log_range(&subsection, d.dirty_bitmap);
|
||||
}
|
||||
|
||||
slot_offset += slot_size;
|
||||
start_addr += slot_size;
|
||||
size -= slot_size;
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Alignment requirement for KVM_CLEAR_DIRTY_LOG - 64 pages */
|
||||
@ -812,7 +1029,7 @@ static int kvm_physical_log_clear(KVMMemoryListener *kml,
|
||||
return ret;
|
||||
}
|
||||
|
||||
kvm_slots_lock(kml);
|
||||
kvm_slots_lock();
|
||||
|
||||
for (i = 0; i < s->nr_slots; i++) {
|
||||
mem = &kml->slots[i];
|
||||
@ -838,7 +1055,7 @@ static int kvm_physical_log_clear(KVMMemoryListener *kml,
|
||||
}
|
||||
}
|
||||
|
||||
kvm_slots_unlock(kml);
|
||||
kvm_slots_unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1121,7 +1338,8 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
||||
int err;
|
||||
MemoryRegion *mr = section->mr;
|
||||
bool writeable = !mr->readonly && !mr->rom_device;
|
||||
hwaddr start_addr, size, slot_size;
|
||||
hwaddr start_addr, size, slot_size, mr_offset;
|
||||
ram_addr_t ram_start_offset;
|
||||
void *ram;
|
||||
|
||||
if (!memory_region_is_ram(mr)) {
|
||||
@ -1139,11 +1357,15 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
||||
return;
|
||||
}
|
||||
|
||||
/* use aligned delta to align the ram address */
|
||||
ram = memory_region_get_ram_ptr(mr) + section->offset_within_region +
|
||||
(start_addr - section->offset_within_address_space);
|
||||
/* The offset of the kvmslot within the memory region */
|
||||
mr_offset = section->offset_within_region + start_addr -
|
||||
section->offset_within_address_space;
|
||||
|
||||
kvm_slots_lock(kml);
|
||||
/* use aligned delta to align the ram address and offset */
|
||||
ram = memory_region_get_ram_ptr(mr) + mr_offset;
|
||||
ram_start_offset = memory_region_get_ram_addr(mr) + mr_offset;
|
||||
|
||||
kvm_slots_lock();
|
||||
|
||||
if (!add) {
|
||||
do {
|
||||
@ -1153,7 +1375,25 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
||||
goto out;
|
||||
}
|
||||
if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
|
||||
kvm_physical_sync_dirty_bitmap(kml, section);
|
||||
/*
|
||||
* NOTE: We should be aware of the fact that here we're only
|
||||
* doing a best effort to sync dirty bits. No matter whether
|
||||
* we're using dirty log or dirty ring, we ignored two facts:
|
||||
*
|
||||
* (1) dirty bits can reside in hardware buffers (PML)
|
||||
*
|
||||
* (2) after we collected dirty bits here, pages can be dirtied
|
||||
* again before we do the final KVM_SET_USER_MEMORY_REGION to
|
||||
* remove the slot.
|
||||
*
|
||||
* Not easy. Let's cross the fingers until it's fixed.
|
||||
*/
|
||||
if (kvm_state->kvm_dirty_ring_size) {
|
||||
kvm_dirty_ring_reap_locked(kvm_state);
|
||||
} else {
|
||||
kvm_slot_get_dirty_log(kvm_state, mem);
|
||||
}
|
||||
kvm_slot_sync_dirty_pages(mem);
|
||||
}
|
||||
|
||||
/* unregister the slot */
|
||||
@ -1177,18 +1417,13 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
||||
do {
|
||||
slot_size = MIN(kvm_max_slot_size, size);
|
||||
mem = kvm_alloc_slot(kml);
|
||||
mem->as_id = kml->as_id;
|
||||
mem->memory_size = slot_size;
|
||||
mem->start_addr = start_addr;
|
||||
mem->ram_start_offset = ram_start_offset;
|
||||
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);
|
||||
}
|
||||
kvm_slot_init_dirty_bitmap(mem);
|
||||
err = kvm_set_user_memory_region(kml, mem, true);
|
||||
if (err) {
|
||||
fprintf(stderr, "%s: error registering slot: %s\n", __func__,
|
||||
@ -1196,12 +1431,58 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
||||
abort();
|
||||
}
|
||||
start_addr += slot_size;
|
||||
ram_start_offset += slot_size;
|
||||
ram += slot_size;
|
||||
size -= slot_size;
|
||||
} while (size);
|
||||
|
||||
out:
|
||||
kvm_slots_unlock(kml);
|
||||
kvm_slots_unlock();
|
||||
}
|
||||
|
||||
static void *kvm_dirty_ring_reaper_thread(void *data)
|
||||
{
|
||||
KVMState *s = data;
|
||||
struct KVMDirtyRingReaper *r = &s->reaper;
|
||||
|
||||
rcu_register_thread();
|
||||
|
||||
trace_kvm_dirty_ring_reaper("init");
|
||||
|
||||
while (true) {
|
||||
r->reaper_state = KVM_DIRTY_RING_REAPER_WAIT;
|
||||
trace_kvm_dirty_ring_reaper("wait");
|
||||
/*
|
||||
* TODO: provide a smarter timeout rather than a constant?
|
||||
*/
|
||||
sleep(1);
|
||||
|
||||
trace_kvm_dirty_ring_reaper("wakeup");
|
||||
r->reaper_state = KVM_DIRTY_RING_REAPER_REAPING;
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
kvm_dirty_ring_reap(s);
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
||||
r->reaper_iteration++;
|
||||
}
|
||||
|
||||
trace_kvm_dirty_ring_reaper("exit");
|
||||
|
||||
rcu_unregister_thread();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int kvm_dirty_ring_reaper_init(KVMState *s)
|
||||
{
|
||||
struct KVMDirtyRingReaper *r = &s->reaper;
|
||||
|
||||
qemu_thread_create(&r->reaper_thr, "kvm-reaper",
|
||||
kvm_dirty_ring_reaper_thread,
|
||||
s, QEMU_THREAD_JOINABLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kvm_region_add(MemoryListener *listener,
|
||||
@ -1226,14 +1507,40 @@ static void kvm_log_sync(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
KVMMemoryListener *kml = container_of(listener, KVMMemoryListener, listener);
|
||||
int r;
|
||||
|
||||
kvm_slots_lock(kml);
|
||||
r = kvm_physical_sync_dirty_bitmap(kml, section);
|
||||
kvm_slots_unlock(kml);
|
||||
if (r < 0) {
|
||||
abort();
|
||||
kvm_slots_lock();
|
||||
kvm_physical_sync_dirty_bitmap(kml, section);
|
||||
kvm_slots_unlock();
|
||||
}
|
||||
|
||||
static void kvm_log_sync_global(MemoryListener *l)
|
||||
{
|
||||
KVMMemoryListener *kml = container_of(l, KVMMemoryListener, listener);
|
||||
KVMState *s = kvm_state;
|
||||
KVMSlot *mem;
|
||||
int i;
|
||||
|
||||
/* Flush all kernel dirty addresses into KVMSlot dirty bitmap */
|
||||
kvm_dirty_ring_flush();
|
||||
|
||||
/*
|
||||
* TODO: make this faster when nr_slots is big while there are
|
||||
* only a few used slots (small VMs).
|
||||
*/
|
||||
kvm_slots_lock();
|
||||
for (i = 0; i < s->nr_slots; i++) {
|
||||
mem = &kml->slots[i];
|
||||
if (mem->memory_size && mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
|
||||
kvm_slot_sync_dirty_pages(mem);
|
||||
/*
|
||||
* This is not needed by KVM_GET_DIRTY_LOG because the
|
||||
* ioctl will unconditionally overwrite the whole region.
|
||||
* However kvm dirty ring has no such side effect.
|
||||
*/
|
||||
kvm_slot_reset_dirty_pages(mem);
|
||||
}
|
||||
}
|
||||
kvm_slots_unlock();
|
||||
}
|
||||
|
||||
static void kvm_log_clear(MemoryListener *listener,
|
||||
@ -1330,7 +1637,6 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml,
|
||||
{
|
||||
int i;
|
||||
|
||||
qemu_mutex_init(&kml->slots_lock);
|
||||
kml->slots = g_malloc0(s->nr_slots * sizeof(KVMSlot));
|
||||
kml->as_id = as_id;
|
||||
|
||||
@ -1342,10 +1648,15 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml,
|
||||
kml->listener.region_del = kvm_region_del;
|
||||
kml->listener.log_start = kvm_log_start;
|
||||
kml->listener.log_stop = kvm_log_stop;
|
||||
kml->listener.log_sync = kvm_log_sync;
|
||||
kml->listener.log_clear = kvm_log_clear;
|
||||
kml->listener.priority = 10;
|
||||
|
||||
if (s->kvm_dirty_ring_size) {
|
||||
kml->listener.log_sync_global = kvm_log_sync_global;
|
||||
} else {
|
||||
kml->listener.log_sync = kvm_log_sync;
|
||||
kml->listener.log_clear = kvm_log_clear;
|
||||
}
|
||||
|
||||
memory_listener_register(&kml->listener, as);
|
||||
|
||||
for (i = 0; i < s->nr_as; ++i) {
|
||||
@ -2003,6 +2314,8 @@ static int kvm_init(MachineState *ms)
|
||||
int type = 0;
|
||||
uint64_t dirty_log_manual_caps;
|
||||
|
||||
qemu_mutex_init(&kml_slots_lock);
|
||||
|
||||
s = KVM_STATE(ms->accelerator);
|
||||
|
||||
/*
|
||||
@ -2019,7 +2332,6 @@ static int kvm_init(MachineState *ms)
|
||||
QTAILQ_INIT(&s->kvm_sw_breakpoints);
|
||||
#endif
|
||||
QLIST_INIT(&s->kvm_parked_vcpus);
|
||||
s->vmfd = -1;
|
||||
s->fd = qemu_open_old("/dev/kvm", O_RDWR);
|
||||
if (s->fd == -1) {
|
||||
fprintf(stderr, "Could not access KVM kernel module: %m\n");
|
||||
@ -2127,20 +2439,70 @@ static int kvm_init(MachineState *ms)
|
||||
s->coalesced_pio = s->coalesced_mmio &&
|
||||
kvm_check_extension(s, KVM_CAP_COALESCED_PIO);
|
||||
|
||||
dirty_log_manual_caps =
|
||||
kvm_check_extension(s, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2);
|
||||
dirty_log_manual_caps &= (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE |
|
||||
KVM_DIRTY_LOG_INITIALLY_SET);
|
||||
s->manual_dirty_log_protect = dirty_log_manual_caps;
|
||||
if (dirty_log_manual_caps) {
|
||||
ret = kvm_vm_enable_cap(s, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2, 0,
|
||||
dirty_log_manual_caps);
|
||||
if (ret) {
|
||||
warn_report("Trying to enable capability %"PRIu64" of "
|
||||
"KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 but failed. "
|
||||
"Falling back to the legacy mode. ",
|
||||
dirty_log_manual_caps);
|
||||
s->manual_dirty_log_protect = 0;
|
||||
/*
|
||||
* Enable KVM dirty ring if supported, otherwise fall back to
|
||||
* dirty logging mode
|
||||
*/
|
||||
if (s->kvm_dirty_ring_size > 0) {
|
||||
uint64_t ring_bytes;
|
||||
|
||||
ring_bytes = s->kvm_dirty_ring_size * sizeof(struct kvm_dirty_gfn);
|
||||
|
||||
/* Read the max supported pages */
|
||||
ret = kvm_vm_check_extension(s, KVM_CAP_DIRTY_LOG_RING);
|
||||
if (ret > 0) {
|
||||
if (ring_bytes > ret) {
|
||||
error_report("KVM dirty ring size %" PRIu32 " too big "
|
||||
"(maximum is %ld). Please use a smaller value.",
|
||||
s->kvm_dirty_ring_size,
|
||||
(long)ret / sizeof(struct kvm_dirty_gfn));
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = kvm_vm_enable_cap(s, KVM_CAP_DIRTY_LOG_RING, 0, ring_bytes);
|
||||
if (ret) {
|
||||
error_report("Enabling of KVM dirty ring failed: %s. "
|
||||
"Suggested mininum value is 1024.", strerror(-ret));
|
||||
goto err;
|
||||
}
|
||||
|
||||
s->kvm_dirty_ring_bytes = ring_bytes;
|
||||
} else {
|
||||
warn_report("KVM dirty ring not available, using bitmap method");
|
||||
s->kvm_dirty_ring_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 is not needed when dirty ring is
|
||||
* enabled. More importantly, KVM_DIRTY_LOG_INITIALLY_SET will assume no
|
||||
* page is wr-protected initially, which is against how kvm dirty ring is
|
||||
* usage - kvm dirty ring requires all pages are wr-protected at the very
|
||||
* beginning. Enabling this feature for dirty ring causes data corruption.
|
||||
*
|
||||
* TODO: Without KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 and kvm clear dirty log,
|
||||
* we may expect a higher stall time when starting the migration. In the
|
||||
* future we can enable KVM_CLEAR_DIRTY_LOG to work with dirty ring too:
|
||||
* instead of clearing dirty bit, it can be a way to explicitly wr-protect
|
||||
* guest pages.
|
||||
*/
|
||||
if (!s->kvm_dirty_ring_size) {
|
||||
dirty_log_manual_caps =
|
||||
kvm_check_extension(s, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2);
|
||||
dirty_log_manual_caps &= (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE |
|
||||
KVM_DIRTY_LOG_INITIALLY_SET);
|
||||
s->manual_dirty_log_protect = dirty_log_manual_caps;
|
||||
if (dirty_log_manual_caps) {
|
||||
ret = kvm_vm_enable_cap(s, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2, 0,
|
||||
dirty_log_manual_caps);
|
||||
if (ret) {
|
||||
warn_report("Trying to enable capability %"PRIu64" of "
|
||||
"KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 but failed. "
|
||||
"Falling back to the legacy mode. ",
|
||||
dirty_log_manual_caps);
|
||||
s->manual_dirty_log_protect = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2226,6 +2588,14 @@ static int kvm_init(MachineState *ms)
|
||||
ret = ram_block_discard_disable(true);
|
||||
assert(!ret);
|
||||
}
|
||||
|
||||
if (s->kvm_dirty_ring_size) {
|
||||
ret = kvm_dirty_ring_reaper_init(s);
|
||||
if (ret) {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
@ -2269,7 +2639,7 @@ static int kvm_handle_internal_error(CPUState *cpu, struct kvm_run *run)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < run->internal.ndata; ++i) {
|
||||
fprintf(stderr, "extra data[%d]: %"PRIx64"\n",
|
||||
fprintf(stderr, "extra data[%d]: 0x%016"PRIx64"\n",
|
||||
i, (uint64_t)run->internal.data[i]);
|
||||
}
|
||||
}
|
||||
@ -2538,6 +2908,17 @@ int kvm_cpu_exec(CPUState *cpu)
|
||||
case KVM_EXIT_INTERNAL_ERROR:
|
||||
ret = kvm_handle_internal_error(cpu, run);
|
||||
break;
|
||||
case KVM_EXIT_DIRTY_RING_FULL:
|
||||
/*
|
||||
* We shouldn't continue if the dirty ring of this vcpu is
|
||||
* still full. Got kicked by KVM_RESET_DIRTY_RINGS.
|
||||
*/
|
||||
trace_kvm_dirty_ring_full(cpu->cpu_index);
|
||||
qemu_mutex_lock_iothread();
|
||||
kvm_dirty_ring_reap(kvm_state);
|
||||
qemu_mutex_unlock_iothread();
|
||||
ret = 0;
|
||||
break;
|
||||
case KVM_EXIT_SYSTEM_EVENT:
|
||||
switch (run->system_event.type) {
|
||||
case KVM_SYSTEM_EVENT_SHUTDOWN:
|
||||
@ -3114,6 +3495,11 @@ static void kvm_set_kvm_shadow_mem(Object *obj, Visitor *v,
|
||||
KVMState *s = KVM_STATE(obj);
|
||||
int64_t value;
|
||||
|
||||
if (s->fd != -1) {
|
||||
error_setg(errp, "Cannot set properties after the accelerator has been initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!visit_type_int(v, name, &value, errp)) {
|
||||
return;
|
||||
}
|
||||
@ -3128,6 +3514,11 @@ static void kvm_set_kernel_irqchip(Object *obj, Visitor *v,
|
||||
KVMState *s = KVM_STATE(obj);
|
||||
OnOffSplit mode;
|
||||
|
||||
if (s->fd != -1) {
|
||||
error_setg(errp, "Cannot set properties after the accelerator has been initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!visit_type_OnOffSplit(v, name, &mode, errp)) {
|
||||
return;
|
||||
}
|
||||
@ -3170,13 +3561,53 @@ bool kvm_kernel_irqchip_split(void)
|
||||
return kvm_state->kernel_irqchip_split == ON_OFF_AUTO_ON;
|
||||
}
|
||||
|
||||
static void kvm_get_dirty_ring_size(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
KVMState *s = KVM_STATE(obj);
|
||||
uint32_t value = s->kvm_dirty_ring_size;
|
||||
|
||||
visit_type_uint32(v, name, &value, errp);
|
||||
}
|
||||
|
||||
static void kvm_set_dirty_ring_size(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
KVMState *s = KVM_STATE(obj);
|
||||
Error *error = NULL;
|
||||
uint32_t value;
|
||||
|
||||
if (s->fd != -1) {
|
||||
error_setg(errp, "Cannot set properties after the accelerator has been initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
visit_type_uint32(v, name, &value, &error);
|
||||
if (error) {
|
||||
error_propagate(errp, error);
|
||||
return;
|
||||
}
|
||||
if (value & (value - 1)) {
|
||||
error_setg(errp, "dirty-ring-size must be a power of two.");
|
||||
return;
|
||||
}
|
||||
|
||||
s->kvm_dirty_ring_size = value;
|
||||
}
|
||||
|
||||
static void kvm_accel_instance_init(Object *obj)
|
||||
{
|
||||
KVMState *s = KVM_STATE(obj);
|
||||
|
||||
s->fd = -1;
|
||||
s->vmfd = -1;
|
||||
s->kvm_shadow_mem = -1;
|
||||
s->kernel_irqchip_allowed = true;
|
||||
s->kernel_irqchip_split = ON_OFF_AUTO_AUTO;
|
||||
/* KVM dirty ring is by default off */
|
||||
s->kvm_dirty_ring_size = 0;
|
||||
}
|
||||
|
||||
static void kvm_accel_class_init(ObjectClass *oc, void *data)
|
||||
@ -3198,6 +3629,12 @@ static void kvm_accel_class_init(ObjectClass *oc, void *data)
|
||||
NULL, NULL);
|
||||
object_class_property_set_description(oc, "kvm-shadow-mem",
|
||||
"KVM shadow MMU size");
|
||||
|
||||
object_class_property_add(oc, "dirty-ring-size", "uint32",
|
||||
kvm_get_dirty_ring_size, kvm_set_dirty_ring_size,
|
||||
NULL, NULL);
|
||||
object_class_property_set_description(oc, "dirty-ring-size",
|
||||
"Size of KVM dirty page ring buffer (default: 0, i.e. use bitmap)");
|
||||
}
|
||||
|
||||
static const TypeInfo kvm_accel_type = {
|
||||
|
@ -1,4 +1,4 @@
|
||||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
# See docs/devel/tracing.rst for syntax documentation.
|
||||
|
||||
# kvm-all.c
|
||||
kvm_ioctl(int type, void *arg) "type 0x%x, arg %p"
|
||||
@ -18,4 +18,11 @@ kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint32_t val, bool assign, uint32_t
|
||||
kvm_set_user_memory(uint32_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d"
|
||||
kvm_clear_dirty_log(uint32_t slot, uint64_t start, uint32_t size) "slot#%"PRId32" start 0x%"PRIx64" size 0x%"PRIx32
|
||||
kvm_resample_fd_notify(int gsi) "gsi %d"
|
||||
kvm_dirty_ring_full(int id) "vcpu %d"
|
||||
kvm_dirty_ring_reap_vcpu(int id) "vcpu %d"
|
||||
kvm_dirty_ring_page(int vcpu, uint32_t slot, uint64_t offset) "vcpu %d fetch %"PRIu32" offset 0x%"PRIx64
|
||||
kvm_dirty_ring_reaper(const char *s) "%s"
|
||||
kvm_dirty_ring_reap(uint64_t count, int64_t t) "reaped %"PRIu64" pages (took %"PRIi64" us)"
|
||||
kvm_dirty_ring_reaper_kick(const char *reason) "%s"
|
||||
kvm_dirty_ring_flush(int finished) "%d"
|
||||
|
||||
|
@ -2,6 +2,7 @@ specific_ss.add(files('accel-common.c'))
|
||||
softmmu_ss.add(files('accel-softmmu.c'))
|
||||
user_ss.add(files('accel-user.c'))
|
||||
|
||||
subdir('hvf')
|
||||
subdir('qtest')
|
||||
subdir('kvm')
|
||||
subdir('tcg')
|
||||
|
@ -11,7 +11,6 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "sysemu/kvm.h"
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
|
@ -11,7 +11,6 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "exec/exec-all.h"
|
||||
|
||||
void tb_flush(CPUState *cpu)
|
||||
|
@ -74,7 +74,7 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE cmpv, ABI_TYPE newv EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP_RW;
|
||||
DATA_TYPE ret;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
@ -95,7 +95,7 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP_R;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
|
||||
@ -110,7 +110,7 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP_W;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, true,
|
||||
ATOMIC_MMU_IDX);
|
||||
|
||||
@ -125,7 +125,7 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP_RW;
|
||||
DATA_TYPE ret;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
@ -142,7 +142,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ABI_TYPE val EXTRA_ARGS) \
|
||||
{ \
|
||||
ATOMIC_MMU_DECLS; \
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP_RW; \
|
||||
DATA_TYPE ret; \
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false, \
|
||||
ATOMIC_MMU_IDX); \
|
||||
@ -176,7 +176,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ABI_TYPE xval EXTRA_ARGS) \
|
||||
{ \
|
||||
ATOMIC_MMU_DECLS; \
|
||||
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP_RW; \
|
||||
XDATA_TYPE cmp, old, new, val = xval; \
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false, \
|
||||
ATOMIC_MMU_IDX); \
|
||||
@ -221,7 +221,7 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE cmpv, ABI_TYPE newv EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP_RW;
|
||||
DATA_TYPE ret;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
@ -242,7 +242,7 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP_R;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
|
||||
@ -257,7 +257,7 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP_W;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, true,
|
||||
ATOMIC_MMU_IDX);
|
||||
|
||||
@ -274,7 +274,7 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP_RW;
|
||||
ABI_TYPE ret;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
@ -291,7 +291,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ABI_TYPE val EXTRA_ARGS) \
|
||||
{ \
|
||||
ATOMIC_MMU_DECLS; \
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP_RW; \
|
||||
DATA_TYPE ret; \
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, \
|
||||
false, ATOMIC_MMU_IDX); \
|
||||
@ -323,7 +323,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ABI_TYPE xval EXTRA_ARGS) \
|
||||
{ \
|
||||
ATOMIC_MMU_DECLS; \
|
||||
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
|
||||
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP_RW; \
|
||||
XDATA_TYPE ldo, ldn, old, new, val = xval; \
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, \
|
||||
false, ATOMIC_MMU_IDX); \
|
||||
|
@ -18,7 +18,6 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/tcg.h"
|
||||
#include "exec/exec-all.h"
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/qemu-print.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/core/tcg-cpu-ops.h"
|
||||
#include "trace.h"
|
||||
#include "disas/disas.h"
|
||||
@ -30,8 +29,6 @@
|
||||
#include "qemu/compiler.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/rcu.h"
|
||||
#include "exec/tb-hash.h"
|
||||
#include "exec/tb-lookup.h"
|
||||
#include "exec/log.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY)
|
||||
@ -41,6 +38,9 @@
|
||||
#include "exec/cpu-all.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "tb-hash.h"
|
||||
#include "tb-lookup.h"
|
||||
#include "tb-context.h"
|
||||
#include "internal.h"
|
||||
|
||||
/* -icount align implementation. */
|
||||
|
@ -19,14 +19,11 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/core/tcg-cpu-ops.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/memory.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "exec/cputlb.h"
|
||||
#include "exec/tb-hash.h"
|
||||
#include "exec/memory-internal.h"
|
||||
#include "exec/ram_addr.h"
|
||||
#include "tcg/tcg.h"
|
||||
@ -38,6 +35,7 @@
|
||||
#include "exec/translate-all.h"
|
||||
#include "trace/trace-root.h"
|
||||
#include "trace/mem.h"
|
||||
#include "tb-hash.h"
|
||||
#include "internal.h"
|
||||
#ifdef CONFIG_PLUGIN
|
||||
#include "qemu/plugin-memory.h"
|
||||
@ -709,8 +707,9 @@ void tlb_flush_page_all_cpus_synced(CPUState *src, target_ulong addr)
|
||||
tlb_flush_page_by_mmuidx_all_cpus_synced(src, addr, ALL_MMUIDX_BITS);
|
||||
}
|
||||
|
||||
static void tlb_flush_page_bits_locked(CPUArchState *env, int midx,
|
||||
target_ulong page, unsigned bits)
|
||||
static void tlb_flush_range_locked(CPUArchState *env, int midx,
|
||||
target_ulong addr, target_ulong len,
|
||||
unsigned bits)
|
||||
{
|
||||
CPUTLBDesc *d = &env_tlb(env)->d[midx];
|
||||
CPUTLBDescFast *f = &env_tlb(env)->f[midx];
|
||||
@ -720,20 +719,26 @@ static void tlb_flush_page_bits_locked(CPUArchState *env, int midx,
|
||||
* If @bits is smaller than the tlb size, there may be multiple entries
|
||||
* within the TLB; otherwise all addresses that match under @mask hit
|
||||
* the same TLB entry.
|
||||
*
|
||||
* TODO: Perhaps allow bits to be a few bits less than the size.
|
||||
* For now, just flush the entire TLB.
|
||||
*
|
||||
* If @len is larger than the tlb size, then it will take longer to
|
||||
* test all of the entries in the TLB than it will to flush it all.
|
||||
*/
|
||||
if (mask < f->mask) {
|
||||
if (mask < f->mask || len > f->mask) {
|
||||
tlb_debug("forcing full flush midx %d ("
|
||||
TARGET_FMT_lx "/" TARGET_FMT_lx ")\n",
|
||||
midx, page, mask);
|
||||
TARGET_FMT_lx "/" TARGET_FMT_lx "+" TARGET_FMT_lx ")\n",
|
||||
midx, addr, mask, len);
|
||||
tlb_flush_one_mmuidx_locked(env, midx, get_clock_realtime());
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if we need to flush due to large pages. */
|
||||
if ((page & d->large_page_mask) == d->large_page_addr) {
|
||||
/*
|
||||
* Check if we need to flush due to large pages.
|
||||
* Because large_page_mask contains all 1's from the msb,
|
||||
* we only need to test the end of the range.
|
||||
*/
|
||||
if (((addr + len - 1) & d->large_page_mask) == d->large_page_addr) {
|
||||
tlb_debug("forcing full flush midx %d ("
|
||||
TARGET_FMT_lx "/" TARGET_FMT_lx ")\n",
|
||||
midx, d->large_page_addr, d->large_page_mask);
|
||||
@ -741,85 +746,67 @@ static void tlb_flush_page_bits_locked(CPUArchState *env, int midx,
|
||||
return;
|
||||
}
|
||||
|
||||
if (tlb_flush_entry_mask_locked(tlb_entry(env, midx, page), page, mask)) {
|
||||
tlb_n_used_entries_dec(env, midx);
|
||||
for (target_ulong i = 0; i < len; i += TARGET_PAGE_SIZE) {
|
||||
target_ulong page = addr + i;
|
||||
CPUTLBEntry *entry = tlb_entry(env, midx, page);
|
||||
|
||||
if (tlb_flush_entry_mask_locked(entry, page, mask)) {
|
||||
tlb_n_used_entries_dec(env, midx);
|
||||
}
|
||||
tlb_flush_vtlb_page_mask_locked(env, midx, page, mask);
|
||||
}
|
||||
tlb_flush_vtlb_page_mask_locked(env, midx, page, mask);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
target_ulong addr;
|
||||
target_ulong len;
|
||||
uint16_t idxmap;
|
||||
uint16_t bits;
|
||||
} TLBFlushPageBitsByMMUIdxData;
|
||||
} TLBFlushRangeData;
|
||||
|
||||
static void
|
||||
tlb_flush_page_bits_by_mmuidx_async_0(CPUState *cpu,
|
||||
TLBFlushPageBitsByMMUIdxData d)
|
||||
static void tlb_flush_range_by_mmuidx_async_0(CPUState *cpu,
|
||||
TLBFlushRangeData d)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
int mmu_idx;
|
||||
|
||||
assert_cpu_is_self(cpu);
|
||||
|
||||
tlb_debug("page addr:" TARGET_FMT_lx "/%u mmu_map:0x%x\n",
|
||||
d.addr, d.bits, d.idxmap);
|
||||
tlb_debug("range:" TARGET_FMT_lx "/%u+" TARGET_FMT_lx " mmu_map:0x%x\n",
|
||||
d.addr, d.bits, d.len, d.idxmap);
|
||||
|
||||
qemu_spin_lock(&env_tlb(env)->c.lock);
|
||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||
if ((d.idxmap >> mmu_idx) & 1) {
|
||||
tlb_flush_page_bits_locked(env, mmu_idx, d.addr, d.bits);
|
||||
tlb_flush_range_locked(env, mmu_idx, d.addr, d.len, d.bits);
|
||||
}
|
||||
}
|
||||
qemu_spin_unlock(&env_tlb(env)->c.lock);
|
||||
|
||||
tb_flush_jmp_cache(cpu, d.addr);
|
||||
}
|
||||
|
||||
static bool encode_pbm_to_runon(run_on_cpu_data *out,
|
||||
TLBFlushPageBitsByMMUIdxData d)
|
||||
{
|
||||
/* We need 6 bits to hold to hold @bits up to 63. */
|
||||
if (d.idxmap <= MAKE_64BIT_MASK(0, TARGET_PAGE_BITS - 6)) {
|
||||
*out = RUN_ON_CPU_TARGET_PTR(d.addr | (d.idxmap << 6) | d.bits);
|
||||
return true;
|
||||
for (target_ulong i = 0; i < d.len; i += TARGET_PAGE_SIZE) {
|
||||
tb_flush_jmp_cache(cpu, d.addr + i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static TLBFlushPageBitsByMMUIdxData
|
||||
decode_runon_to_pbm(run_on_cpu_data data)
|
||||
static void tlb_flush_range_by_mmuidx_async_1(CPUState *cpu,
|
||||
run_on_cpu_data data)
|
||||
{
|
||||
target_ulong addr_map_bits = (target_ulong) data.target_ptr;
|
||||
return (TLBFlushPageBitsByMMUIdxData){
|
||||
.addr = addr_map_bits & TARGET_PAGE_MASK,
|
||||
.idxmap = (addr_map_bits & ~TARGET_PAGE_MASK) >> 6,
|
||||
.bits = addr_map_bits & 0x3f
|
||||
};
|
||||
}
|
||||
|
||||
static void tlb_flush_page_bits_by_mmuidx_async_1(CPUState *cpu,
|
||||
run_on_cpu_data runon)
|
||||
{
|
||||
tlb_flush_page_bits_by_mmuidx_async_0(cpu, decode_runon_to_pbm(runon));
|
||||
}
|
||||
|
||||
static void tlb_flush_page_bits_by_mmuidx_async_2(CPUState *cpu,
|
||||
run_on_cpu_data data)
|
||||
{
|
||||
TLBFlushPageBitsByMMUIdxData *d = data.host_ptr;
|
||||
tlb_flush_page_bits_by_mmuidx_async_0(cpu, *d);
|
||||
TLBFlushRangeData *d = data.host_ptr;
|
||||
tlb_flush_range_by_mmuidx_async_0(cpu, *d);
|
||||
g_free(d);
|
||||
}
|
||||
|
||||
void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, target_ulong addr,
|
||||
uint16_t idxmap, unsigned bits)
|
||||
void tlb_flush_range_by_mmuidx(CPUState *cpu, target_ulong addr,
|
||||
target_ulong len, uint16_t idxmap,
|
||||
unsigned bits)
|
||||
{
|
||||
TLBFlushPageBitsByMMUIdxData d;
|
||||
run_on_cpu_data runon;
|
||||
TLBFlushRangeData d;
|
||||
|
||||
/* If all bits are significant, this devolves to tlb_flush_page. */
|
||||
if (bits >= TARGET_LONG_BITS) {
|
||||
/*
|
||||
* If all bits are significant, and len is small,
|
||||
* this devolves to tlb_flush_page.
|
||||
*/
|
||||
if (bits >= TARGET_LONG_BITS && len <= TARGET_PAGE_SIZE) {
|
||||
tlb_flush_page_by_mmuidx(cpu, addr, idxmap);
|
||||
return;
|
||||
}
|
||||
@ -831,34 +818,38 @@ void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, target_ulong addr,
|
||||
|
||||
/* This should already be page aligned */
|
||||
d.addr = addr & TARGET_PAGE_MASK;
|
||||
d.len = len;
|
||||
d.idxmap = idxmap;
|
||||
d.bits = bits;
|
||||
|
||||
if (qemu_cpu_is_self(cpu)) {
|
||||
tlb_flush_page_bits_by_mmuidx_async_0(cpu, d);
|
||||
} else if (encode_pbm_to_runon(&runon, d)) {
|
||||
async_run_on_cpu(cpu, tlb_flush_page_bits_by_mmuidx_async_1, runon);
|
||||
tlb_flush_range_by_mmuidx_async_0(cpu, d);
|
||||
} else {
|
||||
TLBFlushPageBitsByMMUIdxData *p
|
||||
= g_new(TLBFlushPageBitsByMMUIdxData, 1);
|
||||
|
||||
/* Otherwise allocate a structure, freed by the worker. */
|
||||
*p = d;
|
||||
async_run_on_cpu(cpu, tlb_flush_page_bits_by_mmuidx_async_2,
|
||||
TLBFlushRangeData *p = g_memdup(&d, sizeof(d));
|
||||
async_run_on_cpu(cpu, tlb_flush_range_by_mmuidx_async_1,
|
||||
RUN_ON_CPU_HOST_PTR(p));
|
||||
}
|
||||
}
|
||||
|
||||
void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *src_cpu,
|
||||
target_ulong addr,
|
||||
uint16_t idxmap,
|
||||
unsigned bits)
|
||||
void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, target_ulong addr,
|
||||
uint16_t idxmap, unsigned bits)
|
||||
{
|
||||
TLBFlushPageBitsByMMUIdxData d;
|
||||
run_on_cpu_data runon;
|
||||
tlb_flush_range_by_mmuidx(cpu, addr, TARGET_PAGE_SIZE, idxmap, bits);
|
||||
}
|
||||
|
||||
/* If all bits are significant, this devolves to tlb_flush_page. */
|
||||
if (bits >= TARGET_LONG_BITS) {
|
||||
void tlb_flush_range_by_mmuidx_all_cpus(CPUState *src_cpu,
|
||||
target_ulong addr, target_ulong len,
|
||||
uint16_t idxmap, unsigned bits)
|
||||
{
|
||||
TLBFlushRangeData d;
|
||||
CPUState *dst_cpu;
|
||||
|
||||
/*
|
||||
* If all bits are significant, and len is small,
|
||||
* this devolves to tlb_flush_page.
|
||||
*/
|
||||
if (bits >= TARGET_LONG_BITS && len <= TARGET_PAGE_SIZE) {
|
||||
tlb_flush_page_by_mmuidx_all_cpus(src_cpu, addr, idxmap);
|
||||
return;
|
||||
}
|
||||
@ -870,40 +861,45 @@ void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *src_cpu,
|
||||
|
||||
/* This should already be page aligned */
|
||||
d.addr = addr & TARGET_PAGE_MASK;
|
||||
d.len = len;
|
||||
d.idxmap = idxmap;
|
||||
d.bits = bits;
|
||||
|
||||
if (encode_pbm_to_runon(&runon, d)) {
|
||||
flush_all_helper(src_cpu, tlb_flush_page_bits_by_mmuidx_async_1, runon);
|
||||
} else {
|
||||
CPUState *dst_cpu;
|
||||
TLBFlushPageBitsByMMUIdxData *p;
|
||||
|
||||
/* Allocate a separate data block for each destination cpu. */
|
||||
CPU_FOREACH(dst_cpu) {
|
||||
if (dst_cpu != src_cpu) {
|
||||
p = g_new(TLBFlushPageBitsByMMUIdxData, 1);
|
||||
*p = d;
|
||||
async_run_on_cpu(dst_cpu,
|
||||
tlb_flush_page_bits_by_mmuidx_async_2,
|
||||
RUN_ON_CPU_HOST_PTR(p));
|
||||
}
|
||||
/* Allocate a separate data block for each destination cpu. */
|
||||
CPU_FOREACH(dst_cpu) {
|
||||
if (dst_cpu != src_cpu) {
|
||||
TLBFlushRangeData *p = g_memdup(&d, sizeof(d));
|
||||
async_run_on_cpu(dst_cpu,
|
||||
tlb_flush_range_by_mmuidx_async_1,
|
||||
RUN_ON_CPU_HOST_PTR(p));
|
||||
}
|
||||
}
|
||||
|
||||
tlb_flush_page_bits_by_mmuidx_async_0(src_cpu, d);
|
||||
tlb_flush_range_by_mmuidx_async_0(src_cpu, d);
|
||||
}
|
||||
|
||||
void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
|
||||
target_ulong addr,
|
||||
uint16_t idxmap,
|
||||
unsigned bits)
|
||||
void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *src_cpu,
|
||||
target_ulong addr,
|
||||
uint16_t idxmap, unsigned bits)
|
||||
{
|
||||
TLBFlushPageBitsByMMUIdxData d;
|
||||
run_on_cpu_data runon;
|
||||
tlb_flush_range_by_mmuidx_all_cpus(src_cpu, addr, TARGET_PAGE_SIZE,
|
||||
idxmap, bits);
|
||||
}
|
||||
|
||||
/* If all bits are significant, this devolves to tlb_flush_page. */
|
||||
if (bits >= TARGET_LONG_BITS) {
|
||||
void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
|
||||
target_ulong addr,
|
||||
target_ulong len,
|
||||
uint16_t idxmap,
|
||||
unsigned bits)
|
||||
{
|
||||
TLBFlushRangeData d, *p;
|
||||
CPUState *dst_cpu;
|
||||
|
||||
/*
|
||||
* If all bits are significant, and len is small,
|
||||
* this devolves to tlb_flush_page.
|
||||
*/
|
||||
if (bits >= TARGET_LONG_BITS && len <= TARGET_PAGE_SIZE) {
|
||||
tlb_flush_page_by_mmuidx_all_cpus_synced(src_cpu, addr, idxmap);
|
||||
return;
|
||||
}
|
||||
@ -915,32 +911,31 @@ void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
|
||||
|
||||
/* This should already be page aligned */
|
||||
d.addr = addr & TARGET_PAGE_MASK;
|
||||
d.len = len;
|
||||
d.idxmap = idxmap;
|
||||
d.bits = bits;
|
||||
|
||||
if (encode_pbm_to_runon(&runon, d)) {
|
||||
flush_all_helper(src_cpu, tlb_flush_page_bits_by_mmuidx_async_1, runon);
|
||||
async_safe_run_on_cpu(src_cpu, tlb_flush_page_bits_by_mmuidx_async_1,
|
||||
runon);
|
||||
} else {
|
||||
CPUState *dst_cpu;
|
||||
TLBFlushPageBitsByMMUIdxData *p;
|
||||
|
||||
/* Allocate a separate data block for each destination cpu. */
|
||||
CPU_FOREACH(dst_cpu) {
|
||||
if (dst_cpu != src_cpu) {
|
||||
p = g_new(TLBFlushPageBitsByMMUIdxData, 1);
|
||||
*p = d;
|
||||
async_run_on_cpu(dst_cpu, tlb_flush_page_bits_by_mmuidx_async_2,
|
||||
RUN_ON_CPU_HOST_PTR(p));
|
||||
}
|
||||
/* Allocate a separate data block for each destination cpu. */
|
||||
CPU_FOREACH(dst_cpu) {
|
||||
if (dst_cpu != src_cpu) {
|
||||
p = g_memdup(&d, sizeof(d));
|
||||
async_run_on_cpu(dst_cpu, tlb_flush_range_by_mmuidx_async_1,
|
||||
RUN_ON_CPU_HOST_PTR(p));
|
||||
}
|
||||
|
||||
p = g_new(TLBFlushPageBitsByMMUIdxData, 1);
|
||||
*p = d;
|
||||
async_safe_run_on_cpu(src_cpu, tlb_flush_page_bits_by_mmuidx_async_2,
|
||||
RUN_ON_CPU_HOST_PTR(p));
|
||||
}
|
||||
|
||||
p = g_memdup(&d, sizeof(d));
|
||||
async_safe_run_on_cpu(src_cpu, tlb_flush_range_by_mmuidx_async_1,
|
||||
RUN_ON_CPU_HOST_PTR(p));
|
||||
}
|
||||
|
||||
void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
|
||||
target_ulong addr,
|
||||
uint16_t idxmap,
|
||||
unsigned bits)
|
||||
{
|
||||
tlb_flush_range_by_mmuidx_all_cpus_synced(src_cpu, addr, TARGET_PAGE_SIZE,
|
||||
idxmap, bits);
|
||||
}
|
||||
|
||||
/* update the TLBs so that writes to code in the virtual page 'addr'
|
||||
@ -1747,18 +1742,22 @@ bool tlb_plugin_lookup(CPUState *cpu, target_ulong addr, int mmu_idx,
|
||||
|
||||
#endif
|
||||
|
||||
/* Probe for a read-modify-write atomic operation. Do not allow unaligned
|
||||
* operations, or io operations to proceed. Return the host address. */
|
||||
/*
|
||||
* Probe for an atomic operation. Do not allow unaligned operations,
|
||||
* or io operations to proceed. Return the host address.
|
||||
*
|
||||
* @prot may be PAGE_READ, PAGE_WRITE, or PAGE_READ|PAGE_WRITE.
|
||||
*/
|
||||
static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
TCGMemOpIdx oi, int size, int prot,
|
||||
uintptr_t retaddr)
|
||||
{
|
||||
size_t mmu_idx = get_mmuidx(oi);
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *tlbe = tlb_entry(env, mmu_idx, addr);
|
||||
target_ulong tlb_addr = tlb_addr_write(tlbe);
|
||||
MemOp mop = get_memop(oi);
|
||||
int a_bits = get_alignment_bits(mop);
|
||||
int s_bits = mop & MO_SIZE;
|
||||
uintptr_t index;
|
||||
CPUTLBEntry *tlbe;
|
||||
target_ulong tlb_addr;
|
||||
void *hostaddr;
|
||||
|
||||
/* Adjust the given return address. */
|
||||
@ -1772,7 +1771,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
}
|
||||
|
||||
/* Enforce qemu required alignment. */
|
||||
if (unlikely(addr & ((1 << s_bits) - 1))) {
|
||||
if (unlikely(addr & (size - 1))) {
|
||||
/* We get here if guest alignment was not requested,
|
||||
or was not enforced by cpu_unaligned_access above.
|
||||
We might widen the access and emulate, but for now
|
||||
@ -1780,15 +1779,45 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
goto stop_the_world;
|
||||
}
|
||||
|
||||
index = tlb_index(env, mmu_idx, addr);
|
||||
tlbe = tlb_entry(env, mmu_idx, addr);
|
||||
|
||||
/* Check TLB entry and enforce page permissions. */
|
||||
if (!tlb_hit(tlb_addr, addr)) {
|
||||
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
||||
tlb_fill(env_cpu(env), addr, 1 << s_bits, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
index = tlb_index(env, mmu_idx, addr);
|
||||
tlbe = tlb_entry(env, mmu_idx, addr);
|
||||
if (prot & PAGE_WRITE) {
|
||||
tlb_addr = tlb_addr_write(tlbe);
|
||||
if (!tlb_hit(tlb_addr, addr)) {
|
||||
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
||||
tlb_fill(env_cpu(env), addr, size,
|
||||
MMU_DATA_STORE, mmu_idx, retaddr);
|
||||
index = tlb_index(env, mmu_idx, addr);
|
||||
tlbe = tlb_entry(env, mmu_idx, addr);
|
||||
}
|
||||
tlb_addr = tlb_addr_write(tlbe) & ~TLB_INVALID_MASK;
|
||||
}
|
||||
|
||||
/* Let the guest notice RMW on a write-only page. */
|
||||
if ((prot & PAGE_READ) &&
|
||||
unlikely(tlbe->addr_read != (tlb_addr & ~TLB_NOTDIRTY))) {
|
||||
tlb_fill(env_cpu(env), addr, size,
|
||||
MMU_DATA_LOAD, mmu_idx, retaddr);
|
||||
/*
|
||||
* Since we don't support reads and writes to different addresses,
|
||||
* and we do have the proper page loaded for write, this shouldn't
|
||||
* ever return. But just in case, handle via stop-the-world.
|
||||
*/
|
||||
goto stop_the_world;
|
||||
}
|
||||
} else /* if (prot & PAGE_READ) */ {
|
||||
tlb_addr = tlbe->addr_read;
|
||||
if (!tlb_hit(tlb_addr, addr)) {
|
||||
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
||||
tlb_fill(env_cpu(env), addr, size,
|
||||
MMU_DATA_LOAD, mmu_idx, retaddr);
|
||||
index = tlb_index(env, mmu_idx, addr);
|
||||
tlbe = tlb_entry(env, mmu_idx, addr);
|
||||
}
|
||||
tlb_addr = tlbe->addr_read & ~TLB_INVALID_MASK;
|
||||
}
|
||||
tlb_addr = tlb_addr_write(tlbe) & ~TLB_INVALID_MASK;
|
||||
}
|
||||
|
||||
/* Notice an IO access or a needs-MMU-lookup access */
|
||||
@ -1798,20 +1827,10 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
goto stop_the_world;
|
||||
}
|
||||
|
||||
/* Let the guest notice RMW on a write-only page. */
|
||||
if (unlikely(tlbe->addr_read != (tlb_addr & ~TLB_NOTDIRTY))) {
|
||||
tlb_fill(env_cpu(env), addr, 1 << s_bits, MMU_DATA_LOAD,
|
||||
mmu_idx, retaddr);
|
||||
/* Since we don't support reads and writes to different addresses,
|
||||
and we do have the proper page loaded for write, this shouldn't
|
||||
ever return. But just in case, handle via stop-the-world. */
|
||||
goto stop_the_world;
|
||||
}
|
||||
|
||||
hostaddr = (void *)((uintptr_t)addr + tlbe->addend);
|
||||
|
||||
if (unlikely(tlb_addr & TLB_NOTDIRTY)) {
|
||||
notdirty_write(env_cpu(env), addr, 1 << s_bits,
|
||||
notdirty_write(env_cpu(env), addr, size,
|
||||
&env_tlb(env)->d[mmu_idx].iotlb[index], retaddr);
|
||||
}
|
||||
|
||||
@ -2674,7 +2693,12 @@ void cpu_stq_le_data(CPUArchState *env, target_ulong ptr, uint64_t val)
|
||||
#define ATOMIC_NAME(X) \
|
||||
HELPER(glue(glue(glue(atomic_ ## X, SUFFIX), END), _mmu))
|
||||
#define ATOMIC_MMU_DECLS
|
||||
#define ATOMIC_MMU_LOOKUP atomic_mmu_lookup(env, addr, oi, retaddr)
|
||||
#define ATOMIC_MMU_LOOKUP_RW \
|
||||
atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_READ | PAGE_WRITE, retaddr)
|
||||
#define ATOMIC_MMU_LOOKUP_R \
|
||||
atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_READ, retaddr)
|
||||
#define ATOMIC_MMU_LOOKUP_W \
|
||||
atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_WRITE, retaddr)
|
||||
#define ATOMIC_MMU_CLEANUP
|
||||
#define ATOMIC_MMU_IDX get_mmuidx(oi)
|
||||
|
||||
@ -2703,10 +2727,18 @@ void cpu_stq_le_data(CPUArchState *env, target_ulong ptr, uint64_t val)
|
||||
|
||||
#undef EXTRA_ARGS
|
||||
#undef ATOMIC_NAME
|
||||
#undef ATOMIC_MMU_LOOKUP
|
||||
#undef ATOMIC_MMU_LOOKUP_RW
|
||||
#undef ATOMIC_MMU_LOOKUP_R
|
||||
#undef ATOMIC_MMU_LOOKUP_W
|
||||
|
||||
#define EXTRA_ARGS , TCGMemOpIdx oi
|
||||
#define ATOMIC_NAME(X) HELPER(glue(glue(atomic_ ## X, SUFFIX), END))
|
||||
#define ATOMIC_MMU_LOOKUP atomic_mmu_lookup(env, addr, oi, GETPC())
|
||||
#define ATOMIC_MMU_LOOKUP_RW \
|
||||
atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_READ | PAGE_WRITE, GETPC())
|
||||
#define ATOMIC_MMU_LOOKUP_R \
|
||||
atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_READ, GETPC())
|
||||
#define ATOMIC_MMU_LOOKUP_W \
|
||||
atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_WRITE, GETPC())
|
||||
|
||||
#define DATA_SIZE 1
|
||||
#include "atomic_template.h"
|
||||
|
@ -16,5 +16,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, target_ulong pc,
|
||||
int cflags);
|
||||
|
||||
void QEMU_NORETURN cpu_io_recompile(CPUState *cpu, uintptr_t retaddr);
|
||||
void page_init(void);
|
||||
void tb_htable_init(void);
|
||||
|
||||
#endif /* ACCEL_TCG_INTERNAL_H */
|
||||
|
@ -43,7 +43,6 @@
|
||||
* CPU's index into a TCG temp, since the first callback did it already.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "tcg/tcg-op.h"
|
||||
#include "trace/mem.h"
|
||||
@ -161,9 +160,8 @@ static void gen_empty_mem_helper(void)
|
||||
tcg_temp_free_ptr(ptr);
|
||||
}
|
||||
|
||||
static inline
|
||||
void gen_plugin_cb_start(enum plugin_gen_from from,
|
||||
enum plugin_gen_cb type, unsigned wr)
|
||||
static void gen_plugin_cb_start(enum plugin_gen_from from,
|
||||
enum plugin_gen_cb type, unsigned wr)
|
||||
{
|
||||
TCGOp *op;
|
||||
|
||||
@ -180,7 +178,7 @@ static void gen_wrapped(enum plugin_gen_from from,
|
||||
tcg_gen_plugin_cb_end();
|
||||
}
|
||||
|
||||
static inline void plugin_gen_empty_callback(enum plugin_gen_from from)
|
||||
static void plugin_gen_empty_callback(enum plugin_gen_from from)
|
||||
{
|
||||
switch (from) {
|
||||
case PLUGIN_GEN_AFTER_INSN:
|
||||
@ -386,7 +384,7 @@ static TCGOp *copy_st_ptr(TCGOp **begin_op, TCGOp *op)
|
||||
}
|
||||
|
||||
static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *empty_func,
|
||||
void *func, unsigned tcg_flags, int *cb_idx)
|
||||
void *func, int *cb_idx)
|
||||
{
|
||||
/* copy all ops until the call */
|
||||
do {
|
||||
@ -413,7 +411,7 @@ static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *empty_func,
|
||||
tcg_debug_assert(i < MAX_OPC_PARAM_ARGS);
|
||||
}
|
||||
op->args[*cb_idx] = (uintptr_t)func;
|
||||
op->args[*cb_idx + 1] = tcg_flags;
|
||||
op->args[*cb_idx + 1] = (*begin_op)->args[*cb_idx + 1];
|
||||
|
||||
return op;
|
||||
}
|
||||
@ -440,7 +438,7 @@ static TCGOp *append_udata_cb(const struct qemu_plugin_dyn_cb *cb,
|
||||
|
||||
/* call */
|
||||
op = copy_call(&begin_op, op, HELPER(plugin_vcpu_udata_cb),
|
||||
cb->f.vcpu_udata, cb->tcg_flags, cb_idx);
|
||||
cb->f.vcpu_udata, cb_idx);
|
||||
|
||||
return op;
|
||||
}
|
||||
@ -491,7 +489,7 @@ static TCGOp *append_mem_cb(const struct qemu_plugin_dyn_cb *cb,
|
||||
if (type == PLUGIN_GEN_CB_MEM) {
|
||||
/* call */
|
||||
op = copy_call(&begin_op, op, HELPER(plugin_vcpu_mem_cb),
|
||||
cb->f.vcpu_udata, cb->tcg_flags, cb_idx);
|
||||
cb->f.vcpu_udata, cb_idx);
|
||||
}
|
||||
|
||||
return op;
|
||||
@ -514,9 +512,8 @@ static bool op_rw(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb)
|
||||
return !!(cb->rw & (w + 1));
|
||||
}
|
||||
|
||||
static inline
|
||||
void inject_cb_type(const GArray *cbs, TCGOp *begin_op, inject_fn inject,
|
||||
op_ok_fn ok)
|
||||
static void inject_cb_type(const GArray *cbs, TCGOp *begin_op,
|
||||
inject_fn inject, op_ok_fn ok)
|
||||
{
|
||||
TCGOp *end_op;
|
||||
TCGOp *op;
|
||||
|
@ -1,5 +1,4 @@
|
||||
#ifdef CONFIG_PLUGIN
|
||||
/* Note: no TCG flags because those are overwritten later */
|
||||
DEF_HELPER_2(plugin_vcpu_udata_cb, void, i32, ptr)
|
||||
DEF_HELPER_4(plugin_vcpu_mem_cb, void, i32, i32, i64, ptr)
|
||||
DEF_HELPER_FLAGS_2(plugin_vcpu_udata_cb, TCG_CALL_NO_RWG, void, i32, ptr)
|
||||
DEF_HELPER_FLAGS_4(plugin_vcpu_mem_cb, TCG_CALL_NO_RWG, void, i32, i32, i64, ptr)
|
||||
#endif
|
||||
|
@ -14,7 +14,7 @@
|
||||
#endif
|
||||
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/tb-hash.h"
|
||||
#include "tb-hash.h"
|
||||
|
||||
/* Might cause an exception, so have a longjmp destination ready */
|
||||
static inline TranslationBlock *tb_lookup(CPUState *cpu, target_ulong pc,
|
@ -30,7 +30,6 @@
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/guest-random.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "hw/boards.h"
|
||||
|
||||
#include "tcg-accel-ops.h"
|
||||
#include "tcg-accel-ops-icount.h"
|
||||
|
@ -30,7 +30,6 @@
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/guest-random.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "hw/boards.h"
|
||||
|
||||
#include "tcg-accel-ops.h"
|
||||
#include "tcg-accel-ops-rr.h"
|
||||
|
@ -32,7 +32,6 @@
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/guest-random.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "hw/boards.h"
|
||||
|
||||
#include "tcg-accel-ops.h"
|
||||
#include "tcg-accel-ops-mttcg.h"
|
||||
|
@ -32,6 +32,11 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/accel.h"
|
||||
#include "qapi/qapi-builtin-visit.h"
|
||||
#include "qemu/units.h"
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
#include "hw/boards.h"
|
||||
#endif
|
||||
#include "internal.h"
|
||||
|
||||
struct TCGState {
|
||||
AccelState parent_obj;
|
||||
@ -105,22 +110,29 @@ static void tcg_accel_instance_init(Object *obj)
|
||||
|
||||
bool mttcg_enabled;
|
||||
|
||||
static int tcg_init(MachineState *ms)
|
||||
static int tcg_init_machine(MachineState *ms)
|
||||
{
|
||||
TCGState *s = TCG_STATE(current_accel());
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
unsigned max_cpus = 1;
|
||||
#else
|
||||
unsigned max_cpus = ms->smp.max_cpus;
|
||||
#endif
|
||||
|
||||
tcg_exec_init(s->tb_size * 1024 * 1024, s->splitwx_enabled);
|
||||
tcg_allowed = true;
|
||||
mttcg_enabled = s->mttcg_enabled;
|
||||
|
||||
page_init();
|
||||
tb_htable_init();
|
||||
tcg_init(s->tb_size * MiB, s->splitwx_enabled, max_cpus);
|
||||
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
/*
|
||||
* Initialize TCG regions only for softmmu.
|
||||
*
|
||||
* This needs to be done later for user mode, because the prologue
|
||||
* generation needs to be delayed so that GUEST_BASE is already set.
|
||||
* There's no guest base to take into account, so go ahead and
|
||||
* initialize the prologue now.
|
||||
*/
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
tcg_region_init();
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
tcg_prologue_init(tcg_ctx);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -200,7 +212,7 @@ static void tcg_accel_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
AccelClass *ac = ACCEL_CLASS(oc);
|
||||
ac->name = "tcg";
|
||||
ac->init_machine = tcg_init;
|
||||
ac->init_machine = tcg_init_machine;
|
||||
ac->allowed = &tcg_allowed;
|
||||
|
||||
object_class_property_add_str(oc, "thread",
|
||||
|
@ -1073,9 +1073,8 @@ void HELPER(gvec_ssadd32)(void *d, void *a, void *b, uint32_t desc)
|
||||
for (i = 0; i < oprsz; i += sizeof(int32_t)) {
|
||||
int32_t ai = *(int32_t *)(a + i);
|
||||
int32_t bi = *(int32_t *)(b + i);
|
||||
int32_t di = ai + bi;
|
||||
if (((di ^ ai) &~ (ai ^ bi)) < 0) {
|
||||
/* Signed overflow. */
|
||||
int32_t di;
|
||||
if (sadd32_overflow(ai, bi, &di)) {
|
||||
di = (di < 0 ? INT32_MAX : INT32_MIN);
|
||||
}
|
||||
*(int32_t *)(d + i) = di;
|
||||
@ -1091,9 +1090,8 @@ void HELPER(gvec_ssadd64)(void *d, void *a, void *b, uint32_t desc)
|
||||
for (i = 0; i < oprsz; i += sizeof(int64_t)) {
|
||||
int64_t ai = *(int64_t *)(a + i);
|
||||
int64_t bi = *(int64_t *)(b + i);
|
||||
int64_t di = ai + bi;
|
||||
if (((di ^ ai) &~ (ai ^ bi)) < 0) {
|
||||
/* Signed overflow. */
|
||||
int64_t di;
|
||||
if (sadd64_overflow(ai, bi, &di)) {
|
||||
di = (di < 0 ? INT64_MAX : INT64_MIN);
|
||||
}
|
||||
*(int64_t *)(d + i) = di;
|
||||
@ -1143,9 +1141,8 @@ void HELPER(gvec_sssub32)(void *d, void *a, void *b, uint32_t desc)
|
||||
for (i = 0; i < oprsz; i += sizeof(int32_t)) {
|
||||
int32_t ai = *(int32_t *)(a + i);
|
||||
int32_t bi = *(int32_t *)(b + i);
|
||||
int32_t di = ai - bi;
|
||||
if (((di ^ ai) & (ai ^ bi)) < 0) {
|
||||
/* Signed overflow. */
|
||||
int32_t di;
|
||||
if (ssub32_overflow(ai, bi, &di)) {
|
||||
di = (di < 0 ? INT32_MAX : INT32_MIN);
|
||||
}
|
||||
*(int32_t *)(d + i) = di;
|
||||
@ -1161,9 +1158,8 @@ void HELPER(gvec_sssub64)(void *d, void *a, void *b, uint32_t desc)
|
||||
for (i = 0; i < oprsz; i += sizeof(int64_t)) {
|
||||
int64_t ai = *(int64_t *)(a + i);
|
||||
int64_t bi = *(int64_t *)(b + i);
|
||||
int64_t di = ai - bi;
|
||||
if (((di ^ ai) & (ai ^ bi)) < 0) {
|
||||
/* Signed overflow. */
|
||||
int64_t di;
|
||||
if (ssub64_overflow(ai, bi, &di)) {
|
||||
di = (di < 0 ? INT64_MAX : INT64_MIN);
|
||||
}
|
||||
*(int64_t *)(d + i) = di;
|
||||
@ -1209,8 +1205,8 @@ void HELPER(gvec_usadd32)(void *d, void *a, void *b, uint32_t desc)
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
uint32_t ai = *(uint32_t *)(a + i);
|
||||
uint32_t bi = *(uint32_t *)(b + i);
|
||||
uint32_t di = ai + bi;
|
||||
if (di < ai) {
|
||||
uint32_t di;
|
||||
if (uadd32_overflow(ai, bi, &di)) {
|
||||
di = UINT32_MAX;
|
||||
}
|
||||
*(uint32_t *)(d + i) = di;
|
||||
@ -1226,8 +1222,8 @@ void HELPER(gvec_usadd64)(void *d, void *a, void *b, uint32_t desc)
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
uint64_t ai = *(uint64_t *)(a + i);
|
||||
uint64_t bi = *(uint64_t *)(b + i);
|
||||
uint64_t di = ai + bi;
|
||||
if (di < ai) {
|
||||
uint64_t di;
|
||||
if (uadd64_overflow(ai, bi, &di)) {
|
||||
di = UINT64_MAX;
|
||||
}
|
||||
*(uint64_t *)(d + i) = di;
|
||||
@ -1273,8 +1269,8 @@ void HELPER(gvec_ussub32)(void *d, void *a, void *b, uint32_t desc)
|
||||
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
|
||||
uint32_t ai = *(uint32_t *)(a + i);
|
||||
uint32_t bi = *(uint32_t *)(b + i);
|
||||
uint32_t di = ai - bi;
|
||||
if (ai < bi) {
|
||||
uint32_t di;
|
||||
if (usub32_overflow(ai, bi, &di)) {
|
||||
di = 0;
|
||||
}
|
||||
*(uint32_t *)(d + i) = di;
|
||||
@ -1290,8 +1286,8 @@ void HELPER(gvec_ussub64)(void *d, void *a, void *b, uint32_t desc)
|
||||
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
|
||||
uint64_t ai = *(uint64_t *)(a + i);
|
||||
uint64_t bi = *(uint64_t *)(b + i);
|
||||
uint64_t di = ai - bi;
|
||||
if (ai < bi) {
|
||||
uint64_t di;
|
||||
if (usub64_overflow(ai, bi, &di)) {
|
||||
di = 0;
|
||||
}
|
||||
*(uint64_t *)(d + i) = di;
|
||||
|
@ -30,7 +30,7 @@
|
||||
#include "disas/disas.h"
|
||||
#include "exec/log.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "exec/tb-lookup.h"
|
||||
#include "tb-lookup.h"
|
||||
|
||||
//// --- Begin LibAFL code ---
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
# See docs/devel/tracing.rst for syntax documentation.
|
||||
|
||||
# TCG related tracing
|
||||
# cpu-exec.c
|
||||
|
@ -18,11 +18,9 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qemu-common.h"
|
||||
|
||||
#define NO_CPU_IO_DEFS
|
||||
#include "cpu.h"
|
||||
#include "trace.h"
|
||||
#include "disas/disas.h"
|
||||
#include "exec/exec-all.h"
|
||||
@ -48,10 +46,8 @@
|
||||
#endif
|
||||
|
||||
#include "exec/cputlb.h"
|
||||
#include "exec/tb-hash.h"
|
||||
#include "exec/translate-all.h"
|
||||
#include "qemu/bitmap.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/qemu-print.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/main-loop.h"
|
||||
@ -61,6 +57,8 @@
|
||||
#include "sysemu/tcg.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/core/tcg-cpu-ops.h"
|
||||
#include "tb-hash.h"
|
||||
#include "tb-context.h"
|
||||
#include "internal.h"
|
||||
|
||||
/* #define DEBUG_TB_INVALIDATE */
|
||||
@ -220,9 +218,6 @@ static int v_l2_levels;
|
||||
|
||||
static void *l1_map[V_L1_MAX_SIZE];
|
||||
|
||||
/* code generation context */
|
||||
TCGContext tcg_init_ctx;
|
||||
__thread TCGContext *tcg_ctx;
|
||||
TBContext tb_ctx;
|
||||
|
||||
static void page_table_config_init(void)
|
||||
@ -245,11 +240,6 @@ static void page_table_config_init(void)
|
||||
assert(v_l2_levels >= 0);
|
||||
}
|
||||
|
||||
static void cpu_gen_init(void)
|
||||
{
|
||||
tcg_context_init(&tcg_init_ctx);
|
||||
}
|
||||
|
||||
/* Encode VAL as a signed leb128 sequence at P.
|
||||
Return P incremented past the encoded value. */
|
||||
static uint8_t *encode_sleb128(uint8_t *p, target_long val)
|
||||
@ -415,7 +405,7 @@ bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void page_init(void)
|
||||
void page_init(void)
|
||||
{
|
||||
page_size_init();
|
||||
page_table_config_init();
|
||||
@ -900,408 +890,6 @@ static void page_lock_pair(PageDesc **ret_p1, tb_page_addr_t phys1,
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 (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 (2 * GiB)
|
||||
#elif defined(__sparc__)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
|
||||
#elif defined(__powerpc64__)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
|
||||
#elif defined(__powerpc__)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (32 * MiB)
|
||||
#elif defined(__aarch64__)
|
||||
# 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 (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 (128 * MiB)
|
||||
#else
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1)
|
||||
#endif
|
||||
|
||||
#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 \
|
||||
? DEFAULT_CODE_GEN_BUFFER_SIZE_1 : MAX_CODE_GEN_BUFFER_SIZE)
|
||||
|
||||
static size_t size_code_gen_buffer(size_t tb_size)
|
||||
{
|
||||
/* Size the buffer. */
|
||||
if (tb_size == 0) {
|
||||
size_t phys_mem = qemu_get_host_physmem();
|
||||
if (phys_mem == 0) {
|
||||
tb_size = DEFAULT_CODE_GEN_BUFFER_SIZE;
|
||||
} else {
|
||||
tb_size = MIN(DEFAULT_CODE_GEN_BUFFER_SIZE, phys_mem / 8);
|
||||
}
|
||||
}
|
||||
if (tb_size < MIN_CODE_GEN_BUFFER_SIZE) {
|
||||
tb_size = MIN_CODE_GEN_BUFFER_SIZE;
|
||||
}
|
||||
if (tb_size > MAX_CODE_GEN_BUFFER_SIZE) {
|
||||
tb_size = MAX_CODE_GEN_BUFFER_SIZE;
|
||||
}
|
||||
return tb_size;
|
||||
}
|
||||
|
||||
#ifdef __mips__
|
||||
/* In order to use J and JAL within the code_gen_buffer, we require
|
||||
that the buffer not cross a 256MB boundary. */
|
||||
static inline bool cross_256mb(void *addr, size_t size)
|
||||
{
|
||||
return ((uintptr_t)addr ^ ((uintptr_t)addr + size)) & ~0x0ffffffful;
|
||||
}
|
||||
|
||||
/* We weren't able to allocate a buffer without crossing that boundary,
|
||||
so make do with the larger portion of the buffer that doesn't cross.
|
||||
Returns the new base of the buffer, and adjusts code_gen_buffer_size. */
|
||||
static inline void *split_cross_256mb(void *buf1, size_t size1)
|
||||
{
|
||||
void *buf2 = (void *)(((uintptr_t)buf1 + size1) & ~0x0ffffffful);
|
||||
size_t size2 = buf1 + size1 - buf2;
|
||||
|
||||
size1 = buf2 - buf1;
|
||||
if (size1 < size2) {
|
||||
size1 = size2;
|
||||
buf1 = buf2;
|
||||
}
|
||||
|
||||
tcg_ctx->code_gen_buffer_size = size1;
|
||||
return buf1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_STATIC_CODE_GEN_BUFFER
|
||||
static uint8_t static_code_gen_buffer[DEFAULT_CODE_GEN_BUFFER_SIZE]
|
||||
__attribute__((aligned(CODE_GEN_ALIGN)));
|
||||
|
||||
static bool alloc_code_gen_buffer(size_t tb_size, int splitwx, Error **errp)
|
||||
{
|
||||
void *buf, *end;
|
||||
size_t size;
|
||||
|
||||
if (splitwx > 0) {
|
||||
error_setg(errp, "jit split-wx not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* page-align the beginning and end of the buffer */
|
||||
buf = static_code_gen_buffer;
|
||||
end = static_code_gen_buffer + sizeof(static_code_gen_buffer);
|
||||
buf = QEMU_ALIGN_PTR_UP(buf, qemu_real_host_page_size);
|
||||
end = QEMU_ALIGN_PTR_DOWN(end, qemu_real_host_page_size);
|
||||
|
||||
size = end - buf;
|
||||
|
||||
/* Honor a command-line option limiting the size of the buffer. */
|
||||
if (size > tb_size) {
|
||||
size = QEMU_ALIGN_DOWN(tb_size, qemu_real_host_page_size);
|
||||
}
|
||||
tcg_ctx->code_gen_buffer_size = size;
|
||||
|
||||
#ifdef __mips__
|
||||
if (cross_256mb(buf, size)) {
|
||||
buf = split_cross_256mb(buf, size);
|
||||
size = tcg_ctx->code_gen_buffer_size;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (qemu_mprotect_rwx(buf, size)) {
|
||||
error_setg_errno(errp, errno, "mprotect of jit buffer");
|
||||
return false;
|
||||
}
|
||||
qemu_madvise(buf, size, QEMU_MADV_HUGEPAGE);
|
||||
|
||||
tcg_ctx->code_gen_buffer = buf;
|
||||
return true;
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
static bool alloc_code_gen_buffer(size_t size, int splitwx, Error **errp)
|
||||
{
|
||||
void *buf;
|
||||
|
||||
if (splitwx > 0) {
|
||||
error_setg(errp, "jit split-wx not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
buf = VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT,
|
||||
PAGE_EXECUTE_READWRITE);
|
||||
if (buf == NULL) {
|
||||
error_setg_win32(errp, GetLastError(),
|
||||
"allocate %zu bytes for jit buffer", size);
|
||||
return false;
|
||||
}
|
||||
|
||||
tcg_ctx->code_gen_buffer = buf;
|
||||
tcg_ctx->code_gen_buffer_size = size;
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
static bool alloc_code_gen_buffer_anon(size_t size, int prot,
|
||||
int flags, Error **errp)
|
||||
{
|
||||
void *buf;
|
||||
|
||||
buf = mmap(NULL, size, prot, flags, -1, 0);
|
||||
if (buf == MAP_FAILED) {
|
||||
error_setg_errno(errp, errno,
|
||||
"allocate %zu bytes for jit buffer", size);
|
||||
return false;
|
||||
}
|
||||
tcg_ctx->code_gen_buffer_size = size;
|
||||
|
||||
#ifdef __mips__
|
||||
if (cross_256mb(buf, size)) {
|
||||
/*
|
||||
* 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)) {
|
||||
case 1:
|
||||
if (!cross_256mb(buf2, size)) {
|
||||
/* Success! Use the new buffer. */
|
||||
munmap(buf, size);
|
||||
break;
|
||||
}
|
||||
/* Failure. Work with what we had. */
|
||||
munmap(buf2, size);
|
||||
/* fallthru */
|
||||
default:
|
||||
/* Split the original buffer. Free the smaller half. */
|
||||
buf2 = split_cross_256mb(buf, size);
|
||||
size2 = tcg_ctx->code_gen_buffer_size;
|
||||
if (buf == buf2) {
|
||||
munmap(buf + size2, size - size2);
|
||||
} else {
|
||||
munmap(buf, size - size2);
|
||||
}
|
||||
size = size2;
|
||||
break;
|
||||
}
|
||||
buf = buf2;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Request large pages for the buffer. */
|
||||
qemu_madvise(buf, size, QEMU_MADV_HUGEPAGE);
|
||||
|
||||
tcg_ctx->code_gen_buffer = buf;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_TCG_INTERPRETER
|
||||
#ifdef CONFIG_POSIX
|
||||
#include "qemu/memfd.h"
|
||||
|
||||
static bool alloc_code_gen_buffer_splitwx_memfd(size_t size, Error **errp)
|
||||
{
|
||||
void *buf_rw = NULL, *buf_rx = MAP_FAILED;
|
||||
int fd = -1;
|
||||
|
||||
#ifdef __mips__
|
||||
/* Find space for the RX mapping, vs the 256MiB regions. */
|
||||
if (!alloc_code_gen_buffer_anon(size, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS |
|
||||
MAP_NORESERVE, errp)) {
|
||||
return false;
|
||||
}
|
||||
/* The size of the mapping may have been adjusted. */
|
||||
size = tcg_ctx->code_gen_buffer_size;
|
||||
buf_rx = tcg_ctx->code_gen_buffer;
|
||||
#endif
|
||||
|
||||
buf_rw = qemu_memfd_alloc("tcg-jit", size, 0, &fd, errp);
|
||||
if (buf_rw == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#ifdef __mips__
|
||||
void *tmp = mmap(buf_rx, size, PROT_READ | PROT_EXEC,
|
||||
MAP_SHARED | MAP_FIXED, fd, 0);
|
||||
if (tmp != buf_rx) {
|
||||
goto fail_rx;
|
||||
}
|
||||
#else
|
||||
buf_rx = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0);
|
||||
if (buf_rx == MAP_FAILED) {
|
||||
goto fail_rx;
|
||||
}
|
||||
#endif
|
||||
|
||||
close(fd);
|
||||
tcg_ctx->code_gen_buffer = buf_rw;
|
||||
tcg_ctx->code_gen_buffer_size = size;
|
||||
tcg_splitwx_diff = buf_rx - buf_rw;
|
||||
|
||||
/* Request large pages for the buffer and the splitwx. */
|
||||
qemu_madvise(buf_rw, size, QEMU_MADV_HUGEPAGE);
|
||||
qemu_madvise(buf_rx, size, QEMU_MADV_HUGEPAGE);
|
||||
return true;
|
||||
|
||||
fail_rx:
|
||||
error_setg_errno(errp, errno, "failed to map shared memory for execute");
|
||||
fail:
|
||||
if (buf_rx != MAP_FAILED) {
|
||||
munmap(buf_rx, size);
|
||||
}
|
||||
if (buf_rw) {
|
||||
munmap(buf_rw, size);
|
||||
}
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_POSIX */
|
||||
|
||||
#ifdef CONFIG_DARWIN
|
||||
#include <mach/mach.h>
|
||||
|
||||
extern kern_return_t mach_vm_remap(vm_map_t target_task,
|
||||
mach_vm_address_t *target_address,
|
||||
mach_vm_size_t size,
|
||||
mach_vm_offset_t mask,
|
||||
int flags,
|
||||
vm_map_t src_task,
|
||||
mach_vm_address_t src_address,
|
||||
boolean_t copy,
|
||||
vm_prot_t *cur_protection,
|
||||
vm_prot_t *max_protection,
|
||||
vm_inherit_t inheritance);
|
||||
|
||||
static bool alloc_code_gen_buffer_splitwx_vmremap(size_t size, Error **errp)
|
||||
{
|
||||
kern_return_t ret;
|
||||
mach_vm_address_t buf_rw, buf_rx;
|
||||
vm_prot_t cur_prot, max_prot;
|
||||
|
||||
/* Map the read-write portion via normal anon memory. */
|
||||
if (!alloc_code_gen_buffer_anon(size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, errp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
buf_rw = (mach_vm_address_t)tcg_ctx->code_gen_buffer;
|
||||
buf_rx = 0;
|
||||
ret = mach_vm_remap(mach_task_self(),
|
||||
&buf_rx,
|
||||
size,
|
||||
0,
|
||||
VM_FLAGS_ANYWHERE,
|
||||
mach_task_self(),
|
||||
buf_rw,
|
||||
false,
|
||||
&cur_prot,
|
||||
&max_prot,
|
||||
VM_INHERIT_NONE);
|
||||
if (ret != KERN_SUCCESS) {
|
||||
/* TODO: Convert "ret" to a human readable error message. */
|
||||
error_setg(errp, "vm_remap for jit splitwx failed");
|
||||
munmap((void *)buf_rw, size);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mprotect((void *)buf_rx, size, PROT_READ | PROT_EXEC) != 0) {
|
||||
error_setg_errno(errp, errno, "mprotect for jit splitwx");
|
||||
munmap((void *)buf_rx, size);
|
||||
munmap((void *)buf_rw, size);
|
||||
return false;
|
||||
}
|
||||
|
||||
tcg_splitwx_diff = buf_rx - buf_rw;
|
||||
return true;
|
||||
}
|
||||
#endif /* CONFIG_DARWIN */
|
||||
#endif /* CONFIG_TCG_INTERPRETER */
|
||||
|
||||
static bool alloc_code_gen_buffer_splitwx(size_t size, Error **errp)
|
||||
{
|
||||
#ifndef CONFIG_TCG_INTERPRETER
|
||||
# ifdef CONFIG_DARWIN
|
||||
return alloc_code_gen_buffer_splitwx_vmremap(size, errp);
|
||||
# endif
|
||||
# ifdef CONFIG_POSIX
|
||||
return alloc_code_gen_buffer_splitwx_memfd(size, errp);
|
||||
# endif
|
||||
#endif
|
||||
error_setg(errp, "jit split-wx not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool alloc_code_gen_buffer(size_t size, int splitwx, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
int prot, flags;
|
||||
|
||||
if (splitwx) {
|
||||
if (alloc_code_gen_buffer_splitwx(size, errp)) {
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
* If splitwx force-on (1), fail;
|
||||
* if splitwx default-on (-1), fall through to splitwx off.
|
||||
*/
|
||||
if (splitwx > 0) {
|
||||
return false;
|
||||
}
|
||||
error_free_or_abort(errp);
|
||||
}
|
||||
|
||||
prot = PROT_READ | PROT_WRITE | PROT_EXEC;
|
||||
flags = MAP_PRIVATE | MAP_ANONYMOUS;
|
||||
#ifdef CONFIG_TCG_INTERPRETER
|
||||
/* The tcg interpreter does not need execute permission. */
|
||||
prot = PROT_READ | PROT_WRITE;
|
||||
#elif defined(CONFIG_DARWIN)
|
||||
/* Applicable to both iOS and macOS (Apple Silicon). */
|
||||
if (!splitwx) {
|
||||
flags |= MAP_JIT;
|
||||
}
|
||||
#endif
|
||||
|
||||
return alloc_code_gen_buffer_anon(size, prot, flags, errp);
|
||||
}
|
||||
#endif /* USE_STATIC_CODE_GEN_BUFFER, WIN32, POSIX */
|
||||
|
||||
static bool tb_cmp(const void *ap, const void *bp)
|
||||
{
|
||||
const TranslationBlock *a = ap;
|
||||
@ -1316,36 +904,13 @@ static bool tb_cmp(const void *ap, const void *bp)
|
||||
a->page_addr[1] == b->page_addr[1];
|
||||
}
|
||||
|
||||
static void tb_htable_init(void)
|
||||
void tb_htable_init(void)
|
||||
{
|
||||
unsigned int mode = QHT_MODE_AUTO_RESIZE;
|
||||
|
||||
qht_init(&tb_ctx.htable, tb_cmp, CODE_GEN_HTABLE_SIZE, mode);
|
||||
}
|
||||
|
||||
/* Must be called before using the QEMU cpus. 'tb_size' is the size
|
||||
(in bytes) allocated to the translation buffer. Zero means default
|
||||
size. */
|
||||
void tcg_exec_init(unsigned long tb_size, int splitwx)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
tcg_allowed = true;
|
||||
cpu_gen_init();
|
||||
page_init();
|
||||
tb_htable_init();
|
||||
|
||||
ok = alloc_code_gen_buffer(size_code_gen_buffer(tb_size),
|
||||
splitwx, &error_fatal);
|
||||
assert(ok);
|
||||
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
/* There's no guest base to take into account, so go ahead and
|
||||
initialize the prologue now. */
|
||||
tcg_prologue_init(tcg_ctx);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* call with @p->lock held */
|
||||
static inline void invalidate_page_bitmap(PageDesc *p)
|
||||
{
|
||||
@ -1913,6 +1478,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
|
||||
tcg_ctx->cpu = env_cpu(env);
|
||||
gen_intermediate_code(cpu, tb, max_insns);
|
||||
assert(tb->size != 0);
|
||||
tcg_ctx->cpu = NULL;
|
||||
max_insns = tb->icount;
|
||||
|
||||
@ -2043,8 +1609,15 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
int i;
|
||||
qemu_log(" data: [size=%d]\n", data_size);
|
||||
for (i = 0; i < data_size / sizeof(tcg_target_ulong); i++) {
|
||||
qemu_log("0x%08" PRIxPTR ": .quad 0x%" TCG_PRIlx "\n",
|
||||
(uintptr_t)&rx_data_gen_ptr[i], rx_data_gen_ptr[i]);
|
||||
if (sizeof(tcg_target_ulong) == 8) {
|
||||
qemu_log("0x%08" PRIxPTR ": .quad 0x%016" TCG_PRIlx "\n",
|
||||
(uintptr_t)&rx_data_gen_ptr[i], rx_data_gen_ptr[i]);
|
||||
} else if (sizeof(tcg_target_ulong) == 4) {
|
||||
qemu_log("0x%08" PRIxPTR ": .long 0x%08" TCG_PRIlx "\n",
|
||||
(uintptr_t)&rx_data_gen_ptr[i], rx_data_gen_ptr[i]);
|
||||
} else {
|
||||
qemu_build_not_reached();
|
||||
}
|
||||
}
|
||||
}
|
||||
qemu_log("\n");
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "cpu.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "tcg/tcg-op.h"
|
||||
#include "exec/exec-all.h"
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
bool enable_cpu_pm = false;
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/core/tcg-cpu-ops.h"
|
||||
#include "disas/disas.h"
|
||||
#include "exec/exec-all.h"
|
||||
@ -255,28 +254,35 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size,
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
#include <ucontext.h>
|
||||
#include <machine/trap.h>
|
||||
|
||||
#define EIP_sig(context) ((context)->uc_mcontext.__gregs[_REG_EIP])
|
||||
#define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO])
|
||||
#define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR])
|
||||
#define MASK_sig(context) ((context)->uc_sigmask)
|
||||
#define PAGE_FAULT_TRAP T_PAGEFLT
|
||||
#elif defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
#include <ucontext.h>
|
||||
#include <machine/trap.h>
|
||||
|
||||
#define EIP_sig(context) (*((unsigned long *)&(context)->uc_mcontext.mc_eip))
|
||||
#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno)
|
||||
#define ERROR_sig(context) ((context)->uc_mcontext.mc_err)
|
||||
#define MASK_sig(context) ((context)->uc_sigmask)
|
||||
#define PAGE_FAULT_TRAP T_PAGEFLT
|
||||
#elif defined(__OpenBSD__)
|
||||
#include <machine/trap.h>
|
||||
#define EIP_sig(context) ((context)->sc_eip)
|
||||
#define TRAP_sig(context) ((context)->sc_trapno)
|
||||
#define ERROR_sig(context) ((context)->sc_err)
|
||||
#define MASK_sig(context) ((context)->sc_mask)
|
||||
#define PAGE_FAULT_TRAP T_PAGEFLT
|
||||
#else
|
||||
#define EIP_sig(context) ((context)->uc_mcontext.gregs[REG_EIP])
|
||||
#define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO])
|
||||
#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR])
|
||||
#define MASK_sig(context) ((context)->uc_sigmask)
|
||||
#define PAGE_FAULT_TRAP 0xe
|
||||
#endif
|
||||
|
||||
int cpu_signal_handler(int host_signum, void *pinfo,
|
||||
@ -302,34 +308,42 @@ int cpu_signal_handler(int host_signum, void *pinfo,
|
||||
pc = EIP_sig(uc);
|
||||
trapno = TRAP_sig(uc);
|
||||
return handle_cpu_signal(pc, info,
|
||||
trapno == 0xe ? (ERROR_sig(uc) >> 1) & 1 : 0,
|
||||
trapno == PAGE_FAULT_TRAP ?
|
||||
(ERROR_sig(uc) >> 1) & 1 : 0,
|
||||
&MASK_sig(uc));
|
||||
}
|
||||
|
||||
#elif defined(__x86_64__)
|
||||
|
||||
#ifdef __NetBSD__
|
||||
#include <machine/trap.h>
|
||||
#define PC_sig(context) _UC_MACHINE_PC(context)
|
||||
#define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO])
|
||||
#define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR])
|
||||
#define MASK_sig(context) ((context)->uc_sigmask)
|
||||
#define PAGE_FAULT_TRAP T_PAGEFLT
|
||||
#elif defined(__OpenBSD__)
|
||||
#include <machine/trap.h>
|
||||
#define PC_sig(context) ((context)->sc_rip)
|
||||
#define TRAP_sig(context) ((context)->sc_trapno)
|
||||
#define ERROR_sig(context) ((context)->sc_err)
|
||||
#define MASK_sig(context) ((context)->sc_mask)
|
||||
#define PAGE_FAULT_TRAP T_PAGEFLT
|
||||
#elif defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
#include <ucontext.h>
|
||||
#include <machine/trap.h>
|
||||
|
||||
#define PC_sig(context) (*((unsigned long *)&(context)->uc_mcontext.mc_rip))
|
||||
#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno)
|
||||
#define ERROR_sig(context) ((context)->uc_mcontext.mc_err)
|
||||
#define MASK_sig(context) ((context)->uc_sigmask)
|
||||
#define PAGE_FAULT_TRAP T_PAGEFLT
|
||||
#else
|
||||
#define PC_sig(context) ((context)->uc_mcontext.gregs[REG_RIP])
|
||||
#define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO])
|
||||
#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR])
|
||||
#define MASK_sig(context) ((context)->uc_sigmask)
|
||||
#define PAGE_FAULT_TRAP 0xe
|
||||
#endif
|
||||
|
||||
int cpu_signal_handler(int host_signum, void *pinfo,
|
||||
@ -347,7 +361,8 @@ int cpu_signal_handler(int host_signum, void *pinfo,
|
||||
|
||||
pc = PC_sig(uc);
|
||||
return handle_cpu_signal(pc, info,
|
||||
TRAP_sig(uc) == 0xe ? (ERROR_sig(uc) >> 1) & 1 : 0,
|
||||
TRAP_sig(uc) == PAGE_FAULT_TRAP ?
|
||||
(ERROR_sig(uc) >> 1) & 1 : 0,
|
||||
&MASK_sig(uc));
|
||||
}
|
||||
|
||||
@ -1221,7 +1236,9 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
|
||||
/* Macro to call the above, with local variables from the use context. */
|
||||
#define ATOMIC_MMU_DECLS do {} while (0)
|
||||
#define ATOMIC_MMU_LOOKUP atomic_mmu_lookup(env, addr, DATA_SIZE, GETPC())
|
||||
#define ATOMIC_MMU_LOOKUP_RW atomic_mmu_lookup(env, addr, DATA_SIZE, GETPC())
|
||||
#define ATOMIC_MMU_LOOKUP_R ATOMIC_MMU_LOOKUP_RW
|
||||
#define ATOMIC_MMU_LOOKUP_W ATOMIC_MMU_LOOKUP_RW
|
||||
#define ATOMIC_MMU_CLEANUP do { clear_helper_retaddr(); } while (0)
|
||||
#define ATOMIC_MMU_IDX MMU_USER_IDX
|
||||
|
||||
@ -1251,12 +1268,12 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
|
||||
#undef EXTRA_ARGS
|
||||
#undef ATOMIC_NAME
|
||||
#undef ATOMIC_MMU_LOOKUP
|
||||
#undef ATOMIC_MMU_LOOKUP_RW
|
||||
|
||||
#define EXTRA_ARGS , TCGMemOpIdx oi, uintptr_t retaddr
|
||||
#define ATOMIC_NAME(X) \
|
||||
HELPER(glue(glue(glue(atomic_ ## X, SUFFIX), END), _mmu))
|
||||
#define ATOMIC_MMU_LOOKUP atomic_mmu_lookup(env, addr, DATA_SIZE, retaddr)
|
||||
#define ATOMIC_MMU_LOOKUP_RW atomic_mmu_lookup(env, addr, DATA_SIZE, retaddr)
|
||||
|
||||
#define DATA_SIZE 16
|
||||
#include "atomic_template.h"
|
||||
|
@ -34,6 +34,8 @@
|
||||
#define AUDIO_CAP "alsa"
|
||||
#include "audio_int.h"
|
||||
|
||||
#define DEBUG_ALSA 0
|
||||
|
||||
struct pollhlp {
|
||||
snd_pcm_t *handle;
|
||||
struct pollfd *pfds;
|
||||
@ -587,16 +589,12 @@ static int alsa_open(bool in, struct alsa_params_req *req,
|
||||
|
||||
*handlep = handle;
|
||||
|
||||
if (obtfmt != req->fmt ||
|
||||
obt->nchannels != req->nchannels ||
|
||||
obt->freq != req->freq) {
|
||||
if (DEBUG_ALSA || obtfmt != req->fmt ||
|
||||
obt->nchannels != req->nchannels || obt->freq != req->freq) {
|
||||
dolog ("Audio parameters for %s\n", typ);
|
||||
alsa_dump_info(req, obt, obtfmt, apdo);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
alsa_dump_info(req, obt, obtfmt, apdo);
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
err:
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "qapi/qapi-visit-audio.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "ui/qemu-spice.h"
|
||||
@ -704,7 +705,7 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
|
||||
|
||||
if (live == hwsamples) {
|
||||
#ifdef DEBUG_OUT
|
||||
dolog ("%s is full %d\n", sw->name, live);
|
||||
dolog ("%s is full %zu\n", sw->name, live);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
@ -994,7 +995,7 @@ static size_t audio_get_avail (SWVoiceIn *sw)
|
||||
}
|
||||
|
||||
ldebug (
|
||||
"%s: get_avail live %d ret %" PRId64 "\n",
|
||||
"%s: get_avail live %zu ret %" PRId64 "\n",
|
||||
SW_NAME (sw),
|
||||
live, (((int64_t) live << 32) / sw->ratio) * sw->info.bytes_per_frame
|
||||
);
|
||||
@ -1021,7 +1022,7 @@ static size_t audio_get_free(SWVoiceOut *sw)
|
||||
dead = sw->hw->mix_buf->size - live;
|
||||
|
||||
#ifdef DEBUG_OUT
|
||||
dolog ("%s: get_free live %d dead %d ret %" PRId64 "\n",
|
||||
dolog ("%s: get_free live %zu dead %zu ret %" PRId64 "\n",
|
||||
SW_NAME (sw),
|
||||
live, dead, (((int64_t) dead << 32) / sw->ratio) *
|
||||
sw->info.bytes_per_frame);
|
||||
@ -2172,6 +2173,14 @@ const char *audio_get_id(QEMUSoundCard *card)
|
||||
}
|
||||
}
|
||||
|
||||
const char *audio_application_name(void)
|
||||
{
|
||||
const char *vm_name;
|
||||
|
||||
vm_name = qemu_get_vm_name();
|
||||
return vm_name ? vm_name : "qemu";
|
||||
}
|
||||
|
||||
void audio_rate_start(RateCtl *rate)
|
||||
{
|
||||
memset(rate, 0, sizeof(RateCtl));
|
||||
|
@ -243,6 +243,8 @@ void *audio_calloc (const char *funcname, int nmemb, size_t size);
|
||||
|
||||
void audio_run(AudioState *s, const char *msg);
|
||||
|
||||
const char *audio_application_name(void);
|
||||
|
||||
typedef struct RateCtl {
|
||||
int64_t start_ticks;
|
||||
int64_t bytes_sent;
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#include <pthread.h> /* pthread_X */
|
||||
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/module.h"
|
||||
#include "audio.h"
|
||||
|
||||
@ -34,12 +35,11 @@
|
||||
|
||||
typedef struct coreaudioVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
pthread_mutex_t mutex;
|
||||
pthread_mutex_t buf_mutex;
|
||||
AudioDeviceID outputDeviceID;
|
||||
int frameSizeSetting;
|
||||
uint32_t bufferCount;
|
||||
UInt32 audioDevicePropertyBufferFrameSize;
|
||||
AudioStreamBasicDescription outputStreamBasicDescription;
|
||||
AudioDeviceIOProcID ioprocid;
|
||||
bool enabled;
|
||||
} coreaudioVoiceOut;
|
||||
@ -114,24 +114,6 @@ static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
|
||||
framesize);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_get_streamformat(AudioDeviceID id,
|
||||
AudioStreamBasicDescription *d)
|
||||
{
|
||||
UInt32 size = sizeof(*d);
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioDevicePropertyStreamFormat,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
return AudioObjectGetPropertyData(id,
|
||||
&addr,
|
||||
0,
|
||||
NULL,
|
||||
&size,
|
||||
d);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
|
||||
AudioStreamBasicDescription *d)
|
||||
{
|
||||
@ -260,11 +242,11 @@ static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
|
||||
#define coreaudio_playback_logerr(status, ...) \
|
||||
coreaudio_logerr2(status, "playback", __VA_ARGS__)
|
||||
|
||||
static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
|
||||
static int coreaudio_buf_lock (coreaudioVoiceOut *core, const char *fn_name)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pthread_mutex_lock (&core->mutex);
|
||||
err = pthread_mutex_lock (&core->buf_mutex);
|
||||
if (err) {
|
||||
dolog ("Could not lock voice for %s\nReason: %s\n",
|
||||
fn_name, strerror (err));
|
||||
@ -273,11 +255,11 @@ static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
|
||||
static int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pthread_mutex_unlock (&core->mutex);
|
||||
err = pthread_mutex_unlock (&core->buf_mutex);
|
||||
if (err) {
|
||||
dolog ("Could not unlock voice for %s\nReason: %s\n",
|
||||
fn_name, strerror (err));
|
||||
@ -292,13 +274,13 @@ static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; \
|
||||
ret_type ret; \
|
||||
\
|
||||
if (coreaudio_lock(core, "coreaudio_" #name)) { \
|
||||
if (coreaudio_buf_lock(core, "coreaudio_" #name)) { \
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
ret = glue(audio_generic_, name)args; \
|
||||
\
|
||||
coreaudio_unlock(core, "coreaudio_" #name); \
|
||||
coreaudio_buf_unlock(core, "coreaudio_" #name); \
|
||||
return ret; \
|
||||
}
|
||||
COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
|
||||
@ -310,7 +292,10 @@ COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
|
||||
(hw, buf, size))
|
||||
#undef COREAUDIO_WRAPPER_FUNC
|
||||
|
||||
/* callback to feed audiooutput buffer */
|
||||
/*
|
||||
* callback to feed audiooutput buffer. called without iothread lock.
|
||||
* allowed to lock "buf_mutex", but disallowed to have any other locks.
|
||||
*/
|
||||
static OSStatus audioDeviceIOProc(
|
||||
AudioDeviceID inDevice,
|
||||
const AudioTimeStamp *inNow,
|
||||
@ -326,13 +311,13 @@ static OSStatus audioDeviceIOProc(
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
|
||||
size_t len;
|
||||
|
||||
if (coreaudio_lock (core, "audioDeviceIOProc")) {
|
||||
if (coreaudio_buf_lock (core, "audioDeviceIOProc")) {
|
||||
inInputTime = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (inDevice != core->outputDeviceID) {
|
||||
coreaudio_unlock (core, "audioDeviceIOProc(old device)");
|
||||
coreaudio_buf_unlock (core, "audioDeviceIOProc(old device)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -342,7 +327,7 @@ static OSStatus audioDeviceIOProc(
|
||||
/* if there are not enough samples, set signal and return */
|
||||
if (pending_frames < frameCount) {
|
||||
inInputTime = 0;
|
||||
coreaudio_unlock (core, "audioDeviceIOProc(empty)");
|
||||
coreaudio_buf_unlock (core, "audioDeviceIOProc(empty)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -364,7 +349,7 @@ static OSStatus audioDeviceIOProc(
|
||||
out += write_len;
|
||||
}
|
||||
|
||||
coreaudio_unlock (core, "audioDeviceIOProc");
|
||||
coreaudio_buf_unlock (core, "audioDeviceIOProc");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -373,6 +358,17 @@ static OSStatus init_out_device(coreaudioVoiceOut *core)
|
||||
OSStatus status;
|
||||
AudioValueRange frameRange;
|
||||
|
||||
AudioStreamBasicDescription streamBasicDescription = {
|
||||
.mBitsPerChannel = core->hw.info.bits,
|
||||
.mBytesPerFrame = core->hw.info.bytes_per_frame,
|
||||
.mBytesPerPacket = core->hw.info.bytes_per_frame,
|
||||
.mChannelsPerFrame = core->hw.info.nchannels,
|
||||
.mFormatFlags = kLinearPCMFormatFlagIsFloat,
|
||||
.mFormatID = kAudioFormatLinearPCM,
|
||||
.mFramesPerPacket = 1,
|
||||
.mSampleRate = core->hw.info.freq
|
||||
};
|
||||
|
||||
status = coreaudio_get_voice(&core->outputDeviceID);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_playback_logerr (status,
|
||||
@ -432,34 +428,30 @@ static OSStatus init_out_device(coreaudioVoiceOut *core)
|
||||
}
|
||||
core->hw.samples = core->bufferCount * core->audioDevicePropertyBufferFrameSize;
|
||||
|
||||
/* get StreamFormat */
|
||||
status = coreaudio_get_streamformat(core->outputDeviceID,
|
||||
&core->outputStreamBasicDescription);
|
||||
if (status == kAudioHardwareBadObjectError) {
|
||||
return 0;
|
||||
}
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_playback_logerr (status,
|
||||
"Could not get Device Stream properties\n");
|
||||
core->outputDeviceID = kAudioDeviceUnknown;
|
||||
return status;
|
||||
}
|
||||
|
||||
/* set Samplerate */
|
||||
status = coreaudio_set_streamformat(core->outputDeviceID,
|
||||
&core->outputStreamBasicDescription);
|
||||
&streamBasicDescription);
|
||||
if (status == kAudioHardwareBadObjectError) {
|
||||
return 0;
|
||||
}
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_playback_logerr (status,
|
||||
"Could not set samplerate %lf\n",
|
||||
core->outputStreamBasicDescription.mSampleRate);
|
||||
streamBasicDescription.mSampleRate);
|
||||
core->outputDeviceID = kAudioDeviceUnknown;
|
||||
return status;
|
||||
}
|
||||
|
||||
/* set Callback */
|
||||
/*
|
||||
* set Callback.
|
||||
*
|
||||
* On macOS 11.3.1, Core Audio calls AudioDeviceIOProc after calling an
|
||||
* internal function named HALB_Mutex::Lock(), which locks a mutex in
|
||||
* HALB_IOThread::Entry(void*). HALB_Mutex::Lock() is also called in
|
||||
* AudioObjectGetPropertyData, which is called by coreaudio driver.
|
||||
* Therefore, the specified callback must be designed to avoid a deadlock
|
||||
* with the callers of AudioObjectGetPropertyData.
|
||||
*/
|
||||
core->ioprocid = NULL;
|
||||
status = AudioDeviceCreateIOProcID(core->outputDeviceID,
|
||||
audioDeviceIOProc,
|
||||
@ -542,6 +534,7 @@ static void update_device_playback_state(coreaudioVoiceOut *core)
|
||||
}
|
||||
}
|
||||
|
||||
/* called without iothread lock. */
|
||||
static OSStatus handle_voice_change(
|
||||
AudioObjectID in_object_id,
|
||||
UInt32 in_number_addresses,
|
||||
@ -551,9 +544,7 @@ static OSStatus handle_voice_change(
|
||||
OSStatus status;
|
||||
coreaudioVoiceOut *core = in_client_data;
|
||||
|
||||
if (coreaudio_lock(core, __func__)) {
|
||||
abort();
|
||||
}
|
||||
qemu_mutex_lock_iothread();
|
||||
|
||||
if (core->outputDeviceID) {
|
||||
fini_out_device(core);
|
||||
@ -564,7 +555,7 @@ static OSStatus handle_voice_change(
|
||||
update_device_playback_state(core);
|
||||
}
|
||||
|
||||
coreaudio_unlock (core, __func__);
|
||||
qemu_mutex_unlock_iothread();
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -579,14 +570,10 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
struct audsettings obt_as;
|
||||
|
||||
/* create mutex */
|
||||
err = pthread_mutex_init(&core->mutex, NULL);
|
||||
err = pthread_mutex_init(&core->buf_mutex, NULL);
|
||||
if (err) {
|
||||
dolog("Could not create mutex\nReason: %s\n", strerror (err));
|
||||
goto mutex_error;
|
||||
}
|
||||
|
||||
if (coreaudio_lock(core, __func__)) {
|
||||
goto lock_error;
|
||||
return -1;
|
||||
}
|
||||
|
||||
obt_as = *as;
|
||||
@ -598,7 +585,6 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
|
||||
|
||||
core->bufferCount = cpdo->has_buffer_count ? cpdo->buffer_count : 4;
|
||||
core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
|
||||
|
||||
status = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
|
||||
&voice_addr, handle_voice_change,
|
||||
@ -606,37 +592,21 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_playback_logerr (status,
|
||||
"Could not listen to voice property change\n");
|
||||
goto listener_error;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (init_out_device(core)) {
|
||||
goto device_error;
|
||||
status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
|
||||
&voice_addr,
|
||||
handle_voice_change,
|
||||
core);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_playback_logerr(status,
|
||||
"Could not remove voice property change listener\n");
|
||||
}
|
||||
}
|
||||
|
||||
coreaudio_unlock(core, __func__);
|
||||
return 0;
|
||||
|
||||
device_error:
|
||||
status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
|
||||
&voice_addr,
|
||||
handle_voice_change,
|
||||
core);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_playback_logerr(status,
|
||||
"Could not remove voice property change listener\n");
|
||||
}
|
||||
|
||||
listener_error:
|
||||
coreaudio_unlock(core, __func__);
|
||||
|
||||
lock_error:
|
||||
err = pthread_mutex_destroy(&core->mutex);
|
||||
if (err) {
|
||||
dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
|
||||
}
|
||||
|
||||
mutex_error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void coreaudio_fini_out (HWVoiceOut *hw)
|
||||
@ -645,10 +615,6 @@ static void coreaudio_fini_out (HWVoiceOut *hw)
|
||||
int err;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
|
||||
|
||||
if (coreaudio_lock(core, __func__)) {
|
||||
abort();
|
||||
}
|
||||
|
||||
status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
|
||||
&voice_addr,
|
||||
handle_voice_change,
|
||||
@ -659,10 +625,8 @@ static void coreaudio_fini_out (HWVoiceOut *hw)
|
||||
|
||||
fini_out_device(core);
|
||||
|
||||
coreaudio_unlock(core, __func__);
|
||||
|
||||
/* destroy mutex */
|
||||
err = pthread_mutex_destroy(&core->mutex);
|
||||
err = pthread_mutex_destroy(&core->buf_mutex);
|
||||
if (err) {
|
||||
dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
|
||||
}
|
||||
@ -672,14 +636,8 @@ static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
|
||||
{
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
|
||||
|
||||
if (coreaudio_lock(core, __func__)) {
|
||||
abort();
|
||||
}
|
||||
|
||||
core->enabled = enable;
|
||||
update_device_playback_state(core);
|
||||
|
||||
coreaudio_unlock(core, __func__);
|
||||
}
|
||||
|
||||
static void *coreaudio_audio_init(Audiodev *dev)
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/atomic.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu-common.h"
|
||||
#include "audio.h"
|
||||
|
||||
#define AUDIO_CAP "jack"
|
||||
@ -412,7 +411,7 @@ static int qjack_client_init(QJackClient *c)
|
||||
|
||||
snprintf(client_name, sizeof(client_name), "%s-%s",
|
||||
c->out ? "out" : "in",
|
||||
c->opt->client_name ? c->opt->client_name : qemu_get_vm_name());
|
||||
c->opt->client_name ? c->opt->client_name : audio_application_name());
|
||||
|
||||
if (c->opt->exact_name) {
|
||||
options |= JackUseExactName;
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu-common.h"
|
||||
#include "audio.h"
|
||||
#include "qapi/opts-visitor.h"
|
||||
|
||||
@ -463,10 +462,7 @@ static pa_stream *qpa_simple_new (
|
||||
|
||||
pa_stream_set_state_callback(stream, stream_state_cb, c);
|
||||
|
||||
flags =
|
||||
PA_STREAM_INTERPOLATE_TIMING
|
||||
| PA_STREAM_AUTO_TIMING_UPDATE
|
||||
| PA_STREAM_EARLY_REQUESTS;
|
||||
flags = PA_STREAM_EARLY_REQUESTS;
|
||||
|
||||
if (dev) {
|
||||
/* don't move the stream if the user specified a sink/source */
|
||||
@ -756,7 +752,6 @@ static int qpa_validate_per_direction_opts(Audiodev *dev,
|
||||
/* common */
|
||||
static void *qpa_conn_init(const char *server)
|
||||
{
|
||||
const char *vm_name;
|
||||
PAConnection *c = g_malloc0(sizeof(PAConnection));
|
||||
QTAILQ_INSERT_TAIL(&pa_conns, c, list);
|
||||
|
||||
@ -765,9 +760,8 @@ static void *qpa_conn_init(const char *server)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vm_name = qemu_get_vm_name();
|
||||
c->context = pa_context_new(pa_threaded_mainloop_get_api(c->mainloop),
|
||||
vm_name ? vm_name : "qemu");
|
||||
audio_application_name());
|
||||
if (!c->context) {
|
||||
goto fail;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
# See docs/devel/tracing.rst for syntax documentation.
|
||||
|
||||
# alsaaudio.c
|
||||
alsa_revents(int revents) "revents = %d"
|
||||
|
@ -6,4 +6,4 @@ authz_ss.add(files(
|
||||
'simple.c',
|
||||
))
|
||||
|
||||
authz_ss.add(when: ['CONFIG_AUTH_PAM', pam], if_true: files('pamacct.c'))
|
||||
authz_ss.add(when: pam, if_true: files('pamacct.c'))
|
||||
|
@ -1,4 +1,4 @@
|
||||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
# See docs/devel/tracing.rst for syntax documentation.
|
||||
|
||||
# base.c
|
||||
qauthz_is_allowed(void *authz, const char *identity, bool allowed) "AuthZ %p check identity=%s allowed=%d"
|
||||
|
@ -52,6 +52,7 @@ cryptodev_vhost_init(
|
||||
{
|
||||
int r;
|
||||
CryptoDevBackendVhost *crypto;
|
||||
Error *local_err = NULL;
|
||||
|
||||
crypto = g_new(CryptoDevBackendVhost, 1);
|
||||
crypto->dev.max_queues = 1;
|
||||
@ -66,8 +67,10 @@ cryptodev_vhost_init(
|
||||
/* vhost-user needs vq_index to initiate a specific queue pair */
|
||||
crypto->dev.vq_index = crypto->cc->queue_index * crypto->dev.nvqs;
|
||||
|
||||
r = vhost_dev_init(&crypto->dev, options->opaque, options->backend_type, 0);
|
||||
r = vhost_dev_init(&crypto->dev, options->opaque, options->backend_type, 0,
|
||||
&local_err);
|
||||
if (r < 0) {
|
||||
error_report_err(local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/module.h"
|
||||
#include "sysemu/hostmem.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
@ -40,6 +39,7 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
object_get_typename(OBJECT(backend)));
|
||||
#else
|
||||
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend);
|
||||
uint32_t ram_flags;
|
||||
gchar *name;
|
||||
|
||||
if (!backend->size) {
|
||||
@ -52,11 +52,11 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
}
|
||||
|
||||
name = host_memory_backend_get_name(backend);
|
||||
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
|
||||
name,
|
||||
backend->size, fb->align,
|
||||
(backend->share ? RAM_SHARED : 0) |
|
||||
(fb->is_pmem ? RAM_PMEM : 0),
|
||||
ram_flags = backend->share ? RAM_SHARED : 0;
|
||||
ram_flags |= backend->reserve ? 0 : RAM_NORESERVE;
|
||||
ram_flags |= fb->is_pmem ? RAM_PMEM : 0;
|
||||
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), name,
|
||||
backend->size, fb->align, ram_flags,
|
||||
fb->mem_path, fb->readonly, errp);
|
||||
g_free(name);
|
||||
#endif
|
||||
|
@ -12,7 +12,6 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/hostmem.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "qemu/memfd.h"
|
||||
#include "qemu/module.h"
|
||||
@ -36,6 +35,7 @@ static void
|
||||
memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
{
|
||||
HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(backend);
|
||||
uint32_t ram_flags;
|
||||
char *name;
|
||||
int fd;
|
||||
|
||||
@ -53,9 +53,10 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
}
|
||||
|
||||
name = host_memory_backend_get_name(backend);
|
||||
memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend),
|
||||
name, backend->size,
|
||||
backend->share, fd, 0, errp);
|
||||
ram_flags = backend->share ? RAM_SHARED : 0;
|
||||
ram_flags |= backend->reserve ? 0 : RAM_NORESERVE;
|
||||
memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), name,
|
||||
backend->size, ram_flags, fd, 0, errp);
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
static void
|
||||
ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
{
|
||||
uint32_t ram_flags;
|
||||
char *name;
|
||||
|
||||
if (!backend->size) {
|
||||
@ -27,8 +28,10 @@ ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
}
|
||||
|
||||
name = host_memory_backend_get_name(backend);
|
||||
memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), name,
|
||||
backend->size, backend->share, errp);
|
||||
ram_flags = backend->share ? RAM_SHARED : 0;
|
||||
ram_flags |= backend->reserve ? 0 : RAM_NORESERVE;
|
||||
memory_region_init_ram_flags_nomigrate(&backend->mr, OBJECT(backend), name,
|
||||
backend->size, ram_flags, errp);
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/hostmem.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/boards.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-builtin-visit.h"
|
||||
@ -217,6 +216,11 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value,
|
||||
Error *local_err = NULL;
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
|
||||
if (!backend->reserve && value) {
|
||||
error_setg(errp, "'prealloc=on' and 'reserve=off' are incompatible");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!host_memory_backend_mr_inited(backend)) {
|
||||
backend->prealloc = value;
|
||||
return;
|
||||
@ -268,6 +272,7 @@ static void host_memory_backend_init(Object *obj)
|
||||
/* TODO: convert access to globals to compat properties */
|
||||
backend->merge = machine_mem_merge(machine);
|
||||
backend->dump = machine_dump_guest_core(machine);
|
||||
backend->reserve = true;
|
||||
backend->prealloc_threads = 1;
|
||||
}
|
||||
|
||||
@ -426,6 +431,30 @@ static void host_memory_backend_set_share(Object *o, bool value, Error **errp)
|
||||
backend->share = value;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LINUX
|
||||
static bool host_memory_backend_get_reserve(Object *o, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||
|
||||
return backend->reserve;
|
||||
}
|
||||
|
||||
static void host_memory_backend_set_reserve(Object *o, bool value, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||
|
||||
if (host_memory_backend_mr_inited(backend)) {
|
||||
error_setg(errp, "cannot change property value");
|
||||
return;
|
||||
}
|
||||
if (backend->prealloc && !value) {
|
||||
error_setg(errp, "'prealloc=on' and 'reserve=off' are incompatible");
|
||||
return;
|
||||
}
|
||||
backend->reserve = value;
|
||||
}
|
||||
#endif /* CONFIG_LINUX */
|
||||
|
||||
static bool
|
||||
host_memory_backend_get_use_canonical_path(Object *obj, Error **errp)
|
||||
{
|
||||
@ -494,6 +523,12 @@ host_memory_backend_class_init(ObjectClass *oc, void *data)
|
||||
host_memory_backend_get_share, host_memory_backend_set_share);
|
||||
object_class_property_set_description(oc, "share",
|
||||
"Mark the memory as private to QEMU or shared");
|
||||
#ifdef CONFIG_LINUX
|
||||
object_class_property_add_bool(oc, "reserve",
|
||||
host_memory_backend_get_reserve, host_memory_backend_set_reserve);
|
||||
object_class_property_set_description(oc, "reserve",
|
||||
"Reserve swap space (or huge pages) if applicable");
|
||||
#endif /* CONFIG_LINUX */
|
||||
/*
|
||||
* Do not delete/rename option. This option must be considered stable
|
||||
* (as if it didn't have the 'x-' prefix including deprecation period) as
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qemu/lockable.h"
|
||||
#include "io/channel-socket.h"
|
||||
#include "sysemu/tpm_backend.h"
|
||||
#include "sysemu/tpm_util.h"
|
||||
@ -124,31 +125,26 @@ static int tpm_emulator_ctrlcmd(TPMEmulator *tpm, unsigned long cmd, void *msg,
|
||||
uint32_t cmd_no = cpu_to_be32(cmd);
|
||||
ssize_t n = sizeof(uint32_t) + msg_len_in;
|
||||
uint8_t *buf = NULL;
|
||||
int ret = -1;
|
||||
|
||||
qemu_mutex_lock(&tpm->mutex);
|
||||
WITH_QEMU_LOCK_GUARD(&tpm->mutex) {
|
||||
buf = g_alloca(n);
|
||||
memcpy(buf, &cmd_no, sizeof(cmd_no));
|
||||
memcpy(buf + sizeof(cmd_no), msg, msg_len_in);
|
||||
|
||||
buf = g_alloca(n);
|
||||
memcpy(buf, &cmd_no, sizeof(cmd_no));
|
||||
memcpy(buf + sizeof(cmd_no), msg, msg_len_in);
|
||||
|
||||
n = qemu_chr_fe_write_all(dev, buf, n);
|
||||
if (n <= 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (msg_len_out != 0) {
|
||||
n = qemu_chr_fe_read_all(dev, msg, msg_len_out);
|
||||
n = qemu_chr_fe_write_all(dev, buf, n);
|
||||
if (n <= 0) {
|
||||
goto end;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msg_len_out != 0) {
|
||||
n = qemu_chr_fe_read_all(dev, msg, msg_len_out);
|
||||
if (n <= 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
end:
|
||||
qemu_mutex_unlock(&tpm->mutex);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu,
|
||||
|
@ -1,4 +1,4 @@
|
||||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
# See docs/devel/tracing.rst for syntax documentation.
|
||||
|
||||
# tpm_passthrough.c
|
||||
tpm_passthrough_handle_request(void *cmd) "processing command %p"
|
||||
|
@ -1,4 +1,4 @@
|
||||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
# See docs/devel/tracing.rst for syntax documentation.
|
||||
|
||||
# dbus-vmstate.c
|
||||
dbus_vmstate_pre_save(void)
|
||||
|
@ -48,9 +48,9 @@ vhost_user_backend_dev_init(VhostUserBackend *b, VirtIODevice *vdev,
|
||||
b->dev.nvqs = nvqs;
|
||||
b->dev.vqs = g_new0(struct vhost_virtqueue, nvqs);
|
||||
|
||||
ret = vhost_dev_init(&b->dev, &b->vhost_user, VHOST_BACKEND_TYPE_USER, 0);
|
||||
ret = vhost_dev_init(&b->dev, &b->vhost_user, VHOST_BACKEND_TYPE_USER, 0,
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "vhost initialization failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
409
block.c
409
block.c
@ -42,7 +42,6 @@
|
||||
#include "qapi/qobject-output-visitor.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qemu/notify.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/coroutine.h"
|
||||
@ -55,7 +54,7 @@
|
||||
#ifdef CONFIG_BSD
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/queue.h>
|
||||
#ifndef __DragonFly__
|
||||
#if defined(HAVE_SYS_DISK_H)
|
||||
#include <sys/disk.h>
|
||||
#endif
|
||||
#endif
|
||||
@ -85,20 +84,15 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
|
||||
|
||||
static void bdrv_replace_child_noperm(BdrvChild *child,
|
||||
BlockDriverState *new_bs);
|
||||
static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
|
||||
BlockDriverState *child_bs,
|
||||
const char *child_name,
|
||||
const BdrvChildClass *child_class,
|
||||
BdrvChildRole child_role,
|
||||
BdrvChild **child,
|
||||
Transaction *tran,
|
||||
Error **errp);
|
||||
static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
|
||||
BdrvChild *child,
|
||||
Transaction *tran);
|
||||
static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
|
||||
Transaction *tran);
|
||||
|
||||
static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
|
||||
BlockReopenQueue *queue,
|
||||
Transaction *set_backings_tran, Error **errp);
|
||||
Transaction *change_child_tran, Error **errp);
|
||||
static void bdrv_reopen_commit(BDRVReopenState *reopen_state);
|
||||
static void bdrv_reopen_abort(BDRVReopenState *reopen_state);
|
||||
|
||||
@ -266,7 +260,7 @@ void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix,
|
||||
* image is inactivated. */
|
||||
bool bdrv_is_read_only(BlockDriverState *bs)
|
||||
{
|
||||
return bs->read_only;
|
||||
return !(bs->open_flags & BDRV_O_RDWR);
|
||||
}
|
||||
|
||||
int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only,
|
||||
@ -318,7 +312,6 @@ int bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs->read_only = true;
|
||||
bs->open_flags &= ~BDRV_O_RDWR;
|
||||
|
||||
return 0;
|
||||
@ -401,7 +394,6 @@ BlockDriverState *bdrv_new(void)
|
||||
for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) {
|
||||
QLIST_INIT(&bs->op_blockers[i]);
|
||||
}
|
||||
notifier_with_return_list_init(&bs->before_write_notifiers);
|
||||
qemu_co_mutex_init(&bs->reqs_lock);
|
||||
qemu_mutex_init(&bs->dirty_bitmap_mutex);
|
||||
bs->refcnt = 1;
|
||||
@ -1160,7 +1152,7 @@ int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough)
|
||||
static char *bdrv_child_get_parent_desc(BdrvChild *c)
|
||||
{
|
||||
BlockDriverState *parent = c->opaque;
|
||||
return g_strdup(bdrv_get_device_or_node_name(parent));
|
||||
return g_strdup_printf("node '%s'", bdrv_get_node_name(parent));
|
||||
}
|
||||
|
||||
static void bdrv_child_cb_drained_begin(BdrvChild *child)
|
||||
@ -1414,7 +1406,7 @@ static int bdrv_child_cb_update_filename(BdrvChild *c, BlockDriverState *base,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AioContext *bdrv_child_cb_get_parent_aio_context(BdrvChild *c)
|
||||
AioContext *child_of_bds_get_parent_aio_context(BdrvChild *c)
|
||||
{
|
||||
BlockDriverState *bs = c->opaque;
|
||||
|
||||
@ -1434,7 +1426,7 @@ const BdrvChildClass child_of_bds = {
|
||||
.can_set_aio_ctx = bdrv_child_cb_can_set_aio_ctx,
|
||||
.set_aio_ctx = bdrv_child_cb_set_aio_ctx,
|
||||
.update_filename = bdrv_child_cb_update_filename,
|
||||
.get_parent_aio_context = bdrv_child_cb_get_parent_aio_context,
|
||||
.get_parent_aio_context = child_of_bds_get_parent_aio_context,
|
||||
};
|
||||
|
||||
AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c)
|
||||
@ -1551,7 +1543,6 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv,
|
||||
}
|
||||
|
||||
bs->drv = drv;
|
||||
bs->read_only = !(bs->open_flags & BDRV_O_RDWR);
|
||||
bs->opaque = g_malloc0(drv->instance_size);
|
||||
|
||||
if (drv->bdrv_file_open) {
|
||||
@ -1722,6 +1713,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file,
|
||||
QemuOpts *opts;
|
||||
BlockDriver *drv;
|
||||
Error *local_err = NULL;
|
||||
bool ro;
|
||||
|
||||
assert(bs->file == NULL);
|
||||
assert(options != NULL && bs->options != options);
|
||||
@ -1772,17 +1764,17 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file,
|
||||
trace_bdrv_open_common(bs, filename ?: "", bs->open_flags,
|
||||
drv->format_name);
|
||||
|
||||
bs->read_only = !(bs->open_flags & BDRV_O_RDWR);
|
||||
ro = bdrv_is_read_only(bs);
|
||||
|
||||
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) {
|
||||
if (!bs->read_only && bdrv_is_whitelisted(drv, true)) {
|
||||
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, ro)) {
|
||||
if (!ro && bdrv_is_whitelisted(drv, true)) {
|
||||
ret = bdrv_apply_auto_read_only(bs, NULL, NULL);
|
||||
} else {
|
||||
ret = -ENOTSUP;
|
||||
}
|
||||
if (ret < 0) {
|
||||
error_setg(errp,
|
||||
!bs->read_only && bdrv_is_whitelisted(drv, true)
|
||||
!ro && bdrv_is_whitelisted(drv, true)
|
||||
? "Driver '%s' can only be used for read-only devices"
|
||||
: "Driver '%s' is not whitelisted",
|
||||
drv->format_name);
|
||||
@ -1794,7 +1786,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file,
|
||||
assert(qatomic_read(&bs->copy_on_read) == 0);
|
||||
|
||||
if (bs->open_flags & BDRV_O_COPY_ON_READ) {
|
||||
if (!bs->read_only) {
|
||||
if (!ro) {
|
||||
bdrv_enable_copy_on_read(bs);
|
||||
} else {
|
||||
error_setg(errp, "Can't use copy-on-read on read-only device");
|
||||
@ -2037,27 +2029,38 @@ bool bdrv_is_writable(BlockDriverState *bs)
|
||||
|
||||
static char *bdrv_child_user_desc(BdrvChild *c)
|
||||
{
|
||||
if (c->klass->get_parent_desc) {
|
||||
return c->klass->get_parent_desc(c);
|
||||
}
|
||||
|
||||
return g_strdup("another user");
|
||||
return c->klass->get_parent_desc(c);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that @a allows everything that @b needs. @a and @b must reference same
|
||||
* child node.
|
||||
*/
|
||||
static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp)
|
||||
{
|
||||
g_autofree char *user = NULL;
|
||||
g_autofree char *perm_names = NULL;
|
||||
const char *child_bs_name;
|
||||
g_autofree char *a_user = NULL;
|
||||
g_autofree char *b_user = NULL;
|
||||
g_autofree char *perms = NULL;
|
||||
|
||||
assert(a->bs);
|
||||
assert(a->bs == b->bs);
|
||||
|
||||
if ((b->perm & a->shared_perm) == b->perm) {
|
||||
return true;
|
||||
}
|
||||
|
||||
perm_names = bdrv_perm_names(b->perm & ~a->shared_perm);
|
||||
user = bdrv_child_user_desc(a);
|
||||
error_setg(errp, "Conflicts with use by %s as '%s', which does not "
|
||||
"allow '%s' on %s",
|
||||
user, a->name, perm_names, bdrv_get_node_name(b->bs));
|
||||
child_bs_name = bdrv_get_node_name(b->bs);
|
||||
a_user = bdrv_child_user_desc(a);
|
||||
b_user = bdrv_child_user_desc(b);
|
||||
perms = bdrv_perm_names(b->perm & ~a->shared_perm);
|
||||
|
||||
error_setg(errp, "Permission conflict on node '%s': permissions '%s' are "
|
||||
"both required by %s (uses node '%s' as '%s' child) and "
|
||||
"unshared by %s (uses node '%s' as '%s' child).",
|
||||
child_bs_name, perms,
|
||||
b_user, child_bs_name, b->name,
|
||||
a_user, child_bs_name, a->name);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -2249,12 +2252,14 @@ static TransactionActionDrv bdrv_replace_child_drv = {
|
||||
};
|
||||
|
||||
/*
|
||||
* bdrv_replace_child
|
||||
* bdrv_replace_child_tran
|
||||
*
|
||||
* Note: real unref of old_bs is done only on commit.
|
||||
*
|
||||
* The function doesn't update permissions, caller is responsible for this.
|
||||
*/
|
||||
static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
|
||||
Transaction *tran)
|
||||
static void bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs,
|
||||
Transaction *tran)
|
||||
{
|
||||
BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1);
|
||||
*s = (BdrvReplaceChildState) {
|
||||
@ -2762,6 +2767,14 @@ static TransactionActionDrv bdrv_attach_child_common_drv = {
|
||||
|
||||
/*
|
||||
* Common part of attaching bdrv child to bs or to blk or to job
|
||||
*
|
||||
* Resulting new child is returned through @child.
|
||||
* At start *@child must be NULL.
|
||||
* @child is saved to a new entry of @tran, so that *@child could be reverted to
|
||||
* NULL on abort(). So referenced variable must live at least until transaction
|
||||
* end.
|
||||
*
|
||||
* Function doesn't update permissions, caller is responsible for this.
|
||||
*/
|
||||
static int bdrv_attach_child_common(BlockDriverState *child_bs,
|
||||
const char *child_name,
|
||||
@ -2777,6 +2790,7 @@ static int bdrv_attach_child_common(BlockDriverState *child_bs,
|
||||
|
||||
assert(child);
|
||||
assert(*child == NULL);
|
||||
assert(child_class->get_parent_desc);
|
||||
|
||||
new_child = g_new(BdrvChild, 1);
|
||||
*new_child = (BdrvChild) {
|
||||
@ -2836,6 +2850,12 @@ static int bdrv_attach_child_common(BlockDriverState *child_bs,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Variable referenced by @child must live at least until transaction end.
|
||||
* (see bdrv_attach_child_common() doc for details)
|
||||
*
|
||||
* Function doesn't update permissions, caller is responsible for this.
|
||||
*/
|
||||
static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
|
||||
BlockDriverState *child_bs,
|
||||
const char *child_name,
|
||||
@ -2918,12 +2938,15 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
|
||||
child_role, perm, shared_perm, opaque,
|
||||
&child, tran, errp);
|
||||
if (ret < 0) {
|
||||
bdrv_unref(child_bs);
|
||||
return NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = bdrv_refresh_perms(child_bs, errp);
|
||||
|
||||
out:
|
||||
tran_finalize(tran, ret);
|
||||
/* child is unset on failure by bdrv_attach_child_common_abort() */
|
||||
assert((ret < 0) == !child);
|
||||
|
||||
bdrv_unref(child_bs);
|
||||
return child;
|
||||
@ -2964,6 +2987,8 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
|
||||
|
||||
out:
|
||||
tran_finalize(tran, ret);
|
||||
/* child is unset on failure by bdrv_attach_child_common_abort() */
|
||||
assert((ret < 0) == !child);
|
||||
|
||||
bdrv_unref(child_bs);
|
||||
|
||||
@ -3095,54 +3120,104 @@ static BdrvChildRole bdrv_backing_role(BlockDriverState *bs)
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the bs->backing link of a BDS. A new reference is created; callers
|
||||
* which don't need their own reference any more must call bdrv_unref().
|
||||
* Sets the bs->backing or bs->file link of a BDS. A new reference is created;
|
||||
* callers which don't need their own reference any more must call bdrv_unref().
|
||||
*
|
||||
* Function doesn't update permissions, caller is responsible for this.
|
||||
*/
|
||||
static int bdrv_set_backing_noperm(BlockDriverState *bs,
|
||||
BlockDriverState *backing_hd,
|
||||
Transaction *tran, Error **errp)
|
||||
static int bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs,
|
||||
BlockDriverState *child_bs,
|
||||
bool is_backing,
|
||||
Transaction *tran, Error **errp)
|
||||
{
|
||||
int ret = 0;
|
||||
bool update_inherits_from = bdrv_chain_contains(bs, backing_hd) &&
|
||||
bdrv_inherits_from_recursive(backing_hd, bs);
|
||||
bool update_inherits_from =
|
||||
bdrv_inherits_from_recursive(child_bs, parent_bs);
|
||||
BdrvChild *child = is_backing ? parent_bs->backing : parent_bs->file;
|
||||
BdrvChildRole role;
|
||||
|
||||
if (bdrv_is_backing_chain_frozen(bs, child_bs(bs->backing), errp)) {
|
||||
if (!parent_bs->drv) {
|
||||
/*
|
||||
* Node without drv is an object without a class :/. TODO: finally fix
|
||||
* qcow2 driver to never clear bs->drv and implement format corruption
|
||||
* handling in other way.
|
||||
*/
|
||||
error_setg(errp, "Node corrupted");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (child && child->frozen) {
|
||||
error_setg(errp, "Cannot change frozen '%s' link from '%s' to '%s'",
|
||||
child->name, parent_bs->node_name, child->bs->node_name);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (bs->backing) {
|
||||
/* Cannot be frozen, we checked that above */
|
||||
bdrv_unset_inherits_from(bs, bs->backing, tran);
|
||||
bdrv_remove_filter_or_cow_child(bs, tran);
|
||||
if (is_backing && !parent_bs->drv->is_filter &&
|
||||
!parent_bs->drv->supports_backing)
|
||||
{
|
||||
error_setg(errp, "Driver '%s' of node '%s' does not support backing "
|
||||
"files", parent_bs->drv->format_name, parent_bs->node_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!backing_hd) {
|
||||
if (parent_bs->drv->is_filter) {
|
||||
role = BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY;
|
||||
} else if (is_backing) {
|
||||
role = BDRV_CHILD_COW;
|
||||
} else {
|
||||
/*
|
||||
* We only can use same role as it is in existing child. We don't have
|
||||
* infrastructure to determine role of file child in generic way
|
||||
*/
|
||||
if (!child) {
|
||||
error_setg(errp, "Cannot set file child to format node without "
|
||||
"file child");
|
||||
return -EINVAL;
|
||||
}
|
||||
role = child->role;
|
||||
}
|
||||
|
||||
if (child) {
|
||||
bdrv_unset_inherits_from(parent_bs, child, tran);
|
||||
bdrv_remove_file_or_backing_child(parent_bs, child, tran);
|
||||
}
|
||||
|
||||
if (!child_bs) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = bdrv_attach_child_noperm(bs, backing_hd, "backing",
|
||||
&child_of_bds, bdrv_backing_role(bs),
|
||||
&bs->backing, tran, errp);
|
||||
ret = bdrv_attach_child_noperm(parent_bs, child_bs,
|
||||
is_backing ? "backing" : "file",
|
||||
&child_of_bds, role,
|
||||
is_backing ? &parent_bs->backing :
|
||||
&parent_bs->file,
|
||||
tran, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* If backing_hd was already part of bs's backing chain, and
|
||||
* inherits_from pointed recursively to bs then let's update it to
|
||||
* If inherits_from pointed recursively to bs then let's update it to
|
||||
* point directly to bs (else it will become NULL).
|
||||
*/
|
||||
if (update_inherits_from) {
|
||||
bdrv_set_inherits_from(backing_hd, bs, tran);
|
||||
bdrv_set_inherits_from(child_bs, parent_bs, tran);
|
||||
}
|
||||
|
||||
out:
|
||||
bdrv_refresh_limits(bs, tran, NULL);
|
||||
bdrv_refresh_limits(parent_bs, tran, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bdrv_set_backing_noperm(BlockDriverState *bs,
|
||||
BlockDriverState *backing_hd,
|
||||
Transaction *tran, Error **errp)
|
||||
{
|
||||
return bdrv_set_file_or_backing_noperm(bs, backing_hd, true, tran, errp);
|
||||
}
|
||||
|
||||
int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
|
||||
Error **errp)
|
||||
{
|
||||
@ -4051,7 +4126,7 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
|
||||
ret = bdrv_flush(bs_entry->state.bs);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Error flushing drive");
|
||||
goto cleanup;
|
||||
goto abort;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4073,6 +4148,10 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
|
||||
refresh_list = bdrv_topological_dfs(refresh_list, found,
|
||||
state->old_backing_bs);
|
||||
}
|
||||
if (state->old_file_bs) {
|
||||
refresh_list = bdrv_topological_dfs(refresh_list, found,
|
||||
state->old_file_bs);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4148,29 +4227,6 @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
|
||||
return ret;
|
||||
}
|
||||
|
||||
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.
|
||||
@ -4188,115 +4244,81 @@ static bool bdrv_reopen_can_attach(BlockDriverState *parent,
|
||||
*
|
||||
* Return 0 on success, otherwise return < 0 and set @errp.
|
||||
*/
|
||||
static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
|
||||
Transaction *set_backings_tran,
|
||||
Error **errp)
|
||||
static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state,
|
||||
bool is_backing, Transaction *tran,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs = reopen_state->bs;
|
||||
BlockDriverState *overlay_bs, *below_bs, *new_backing_bs;
|
||||
BlockDriverState *new_child_bs;
|
||||
BlockDriverState *old_child_bs = is_backing ? child_bs(bs->backing) :
|
||||
child_bs(bs->file);
|
||||
const char *child_name = is_backing ? "backing" : "file";
|
||||
QObject *value;
|
||||
const char *str;
|
||||
|
||||
value = qdict_get(reopen_state->options, "backing");
|
||||
value = qdict_get(reopen_state->options, child_name);
|
||||
if (value == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (qobject_type(value)) {
|
||||
case QTYPE_QNULL:
|
||||
new_backing_bs = NULL;
|
||||
assert(is_backing); /* The 'file' option does not allow a null value */
|
||||
new_child_bs = NULL;
|
||||
break;
|
||||
case QTYPE_QSTRING:
|
||||
str = qstring_get_str(qobject_to(QString, value));
|
||||
new_backing_bs = bdrv_lookup_bs(NULL, str, errp);
|
||||
if (new_backing_bs == NULL) {
|
||||
new_child_bs = bdrv_lookup_bs(NULL, str, errp);
|
||||
if (new_child_bs == NULL) {
|
||||
return -EINVAL;
|
||||
} else if (bdrv_recurse_has_child(new_backing_bs, bs)) {
|
||||
error_setg(errp, "Making '%s' a backing file of '%s' "
|
||||
"would create a cycle", str, bs->node_name);
|
||||
} else if (bdrv_recurse_has_child(new_child_bs, bs)) {
|
||||
error_setg(errp, "Making '%s' a %s child of '%s' would create a "
|
||||
"cycle", str, child_name, bs->node_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* 'backing' does not allow any other data type */
|
||||
/*
|
||||
* The options QDict has been flattened, so 'backing' and 'file'
|
||||
* do not allow any other data type here.
|
||||
*/
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
/*
|
||||
* Check AioContext compatibility so that the bdrv_set_backing_hd() call in
|
||||
* bdrv_reopen_commit() won't fail.
|
||||
*/
|
||||
if (new_backing_bs) {
|
||||
if (!bdrv_reopen_can_attach(bs, bs->backing, new_backing_bs, errp)) {
|
||||
return -EINVAL;
|
||||
if (old_child_bs == new_child_bs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (old_child_bs) {
|
||||
if (bdrv_skip_implicit_filters(old_child_bs) == new_child_bs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (old_child_bs->implicit) {
|
||||
error_setg(errp, "Cannot replace implicit %s child of %s",
|
||||
child_name, bs->node_name);
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that @bs can really handle backing files, because we are
|
||||
* about to give it one (or swap the existing one)
|
||||
*/
|
||||
if (bs->drv->is_filter) {
|
||||
/* Filters always have a file or a backing child */
|
||||
if (!bs->backing) {
|
||||
error_setg(errp, "'%s' is a %s filter node that does not support a "
|
||||
"backing child", bs->node_name, bs->drv->format_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (!bs->drv->supports_backing) {
|
||||
error_setg(errp, "Driver '%s' of node '%s' does not support backing "
|
||||
"files", bs->drv->format_name, bs->node_name);
|
||||
if (bs->drv->is_filter && !old_child_bs) {
|
||||
/*
|
||||
* Filters always have a file or a backing child, so we are trying to
|
||||
* change wrong child
|
||||
*/
|
||||
error_setg(errp, "'%s' is a %s filter node that does not support a "
|
||||
"%s child", bs->node_name, bs->drv->format_name, child_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the "actual" backing file by skipping all links that point
|
||||
* to an implicit node, if any (e.g. a commit filter node).
|
||||
* We cannot use any of the bdrv_skip_*() functions here because
|
||||
* those return the first explicit node, while we are looking for
|
||||
* its overlay here.
|
||||
*/
|
||||
overlay_bs = bs;
|
||||
for (below_bs = bdrv_filter_or_cow_bs(overlay_bs);
|
||||
below_bs && below_bs->implicit;
|
||||
below_bs = bdrv_filter_or_cow_bs(overlay_bs))
|
||||
{
|
||||
overlay_bs = below_bs;
|
||||
if (is_backing) {
|
||||
reopen_state->old_backing_bs = old_child_bs;
|
||||
} else {
|
||||
reopen_state->old_file_bs = old_child_bs;
|
||||
}
|
||||
|
||||
/* If we want to replace the backing file we need some extra checks */
|
||||
if (new_backing_bs != bdrv_filter_or_cow_bs(overlay_bs)) {
|
||||
int ret;
|
||||
|
||||
/* Check for implicit nodes between bs and its backing file */
|
||||
if (bs != overlay_bs) {
|
||||
error_setg(errp, "Cannot change backing link if '%s' has "
|
||||
"an implicit backing file", bs->node_name);
|
||||
return -EPERM;
|
||||
}
|
||||
/*
|
||||
* Check if the backing link that we want to replace is frozen.
|
||||
* Note that
|
||||
* bdrv_filter_or_cow_child(overlay_bs) == overlay_bs->backing,
|
||||
* because we know that overlay_bs == bs, and that @bs
|
||||
* either is a filter that uses ->backing or a COW format BDS
|
||||
* with bs->drv->supports_backing == true.
|
||||
*/
|
||||
if (bdrv_is_backing_chain_frozen(overlay_bs,
|
||||
child_bs(overlay_bs->backing), errp))
|
||||
{
|
||||
return -EPERM;
|
||||
}
|
||||
reopen_state->replace_backing_bs = true;
|
||||
reopen_state->old_backing_bs = bs->backing ? bs->backing->bs : NULL;
|
||||
ret = bdrv_set_backing_noperm(bs, new_backing_bs, set_backings_tran,
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return bdrv_set_file_or_backing_noperm(bs, new_child_bs, is_backing,
|
||||
tran, errp);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4318,7 +4340,7 @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
|
||||
*/
|
||||
static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
|
||||
BlockReopenQueue *queue,
|
||||
Transaction *set_backings_tran, Error **errp)
|
||||
Transaction *change_child_tran, Error **errp)
|
||||
{
|
||||
int ret = -1;
|
||||
int old_flags;
|
||||
@ -4438,12 +4460,21 @@ static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
|
||||
* either a reference to an existing node (using its node name)
|
||||
* or NULL to simply detach the current backing file.
|
||||
*/
|
||||
ret = bdrv_reopen_parse_backing(reopen_state, set_backings_tran, errp);
|
||||
ret = bdrv_reopen_parse_file_or_backing(reopen_state, true,
|
||||
change_child_tran, errp);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
qdict_del(reopen_state->options, "backing");
|
||||
|
||||
/* Allow changing the 'file' option. In this case NULL is not allowed */
|
||||
ret = bdrv_reopen_parse_file_or_backing(reopen_state, false,
|
||||
change_child_tran, errp);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
qdict_del(reopen_state->options, "file");
|
||||
|
||||
/* Options that are not handled are only okay if they are unchanged
|
||||
* compared to the old state. It is expected that some options are only
|
||||
* used for the initial open, but not reopen (e.g. filename) */
|
||||
@ -4546,20 +4577,18 @@ static void bdrv_reopen_commit(BDRVReopenState *reopen_state)
|
||||
bs->explicit_options = reopen_state->explicit_options;
|
||||
bs->options = reopen_state->options;
|
||||
bs->open_flags = reopen_state->flags;
|
||||
bs->read_only = !(reopen_state->flags & BDRV_O_RDWR);
|
||||
bs->detect_zeroes = reopen_state->detect_zeroes;
|
||||
|
||||
if (reopen_state->replace_backing_bs) {
|
||||
qdict_del(bs->explicit_options, "backing");
|
||||
qdict_del(bs->options, "backing");
|
||||
}
|
||||
|
||||
/* Remove child references from bs->options and bs->explicit_options.
|
||||
* Child options were already removed in bdrv_reopen_queue_child() */
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
qdict_del(bs->explicit_options, child->name);
|
||||
qdict_del(bs->options, child->name);
|
||||
}
|
||||
/* backing is probably removed, so it's not handled by previous loop */
|
||||
qdict_del(bs->explicit_options, "backing");
|
||||
qdict_del(bs->options, "backing");
|
||||
|
||||
bdrv_refresh_limits(bs, NULL, NULL);
|
||||
}
|
||||
|
||||
@ -4751,7 +4780,7 @@ static void bdrv_remove_filter_or_cow_child_abort(void *opaque)
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't have to restore child->bs here to undo bdrv_replace_child()
|
||||
* We don't have to restore child->bs here to undo bdrv_replace_child_tran()
|
||||
* because that function is transactionable and it registered own completion
|
||||
* entries in @tran, so .abort() for bdrv_replace_child_safe() will be
|
||||
* called automatically.
|
||||
@ -4772,22 +4801,23 @@ static TransactionActionDrv bdrv_remove_filter_or_cow_child_drv = {
|
||||
};
|
||||
|
||||
/*
|
||||
* A function to remove backing-chain child of @bs if exists: cow child for
|
||||
* format nodes (always .backing) and filter child for filters (may be .file or
|
||||
* .backing)
|
||||
* A function to remove backing or file child of @bs.
|
||||
* Function doesn't update permissions, caller is responsible for this.
|
||||
*/
|
||||
static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
|
||||
Transaction *tran)
|
||||
static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
|
||||
BdrvChild *child,
|
||||
Transaction *tran)
|
||||
{
|
||||
BdrvRemoveFilterOrCowChild *s;
|
||||
BdrvChild *child = bdrv_filter_or_cow_child(bs);
|
||||
|
||||
assert(child == bs->backing || child == bs->file);
|
||||
|
||||
if (!child) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (child->bs) {
|
||||
bdrv_replace_child(child, NULL, tran);
|
||||
bdrv_replace_child_tran(child, NULL, tran);
|
||||
}
|
||||
|
||||
s = g_new(BdrvRemoveFilterOrCowChild, 1);
|
||||
@ -4805,6 +4835,17 @@ static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A function to remove backing-chain child of @bs if exists: cow child for
|
||||
* format nodes (always .backing) and filter child for filters (may be .file or
|
||||
* .backing)
|
||||
*/
|
||||
static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
|
||||
Transaction *tran)
|
||||
{
|
||||
bdrv_remove_file_or_backing_child(bs, bdrv_filter_or_cow_child(bs), tran);
|
||||
}
|
||||
|
||||
static int bdrv_replace_node_noperm(BlockDriverState *from,
|
||||
BlockDriverState *to,
|
||||
bool auto_skip, Transaction *tran,
|
||||
@ -4827,7 +4868,7 @@ static int bdrv_replace_node_noperm(BlockDriverState *from,
|
||||
c->name, from->node_name);
|
||||
return -EPERM;
|
||||
}
|
||||
bdrv_replace_child(c, to, tran);
|
||||
bdrv_replace_child_tran(c, to, tran);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -4851,7 +4892,7 @@ static int bdrv_replace_node_common(BlockDriverState *from,
|
||||
Transaction *tran = tran_new();
|
||||
g_autoptr(GHashTable) found = NULL;
|
||||
g_autoptr(GSList) refresh_list = NULL;
|
||||
BlockDriverState *to_cow_parent;
|
||||
BlockDriverState *to_cow_parent = NULL;
|
||||
int ret;
|
||||
|
||||
if (detach_subchain) {
|
||||
@ -6538,9 +6579,13 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
||||
}
|
||||
assert(full_backing);
|
||||
|
||||
/* backing files always opened read-only */
|
||||
/*
|
||||
* No need to do I/O here, which allows us to open encrypted
|
||||
* backing images without needing the secret
|
||||
*/
|
||||
back_flags = flags;
|
||||
back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
|
||||
back_flags |= BDRV_O_NO_IO;
|
||||
|
||||
backing_options = qdict_new();
|
||||
if (backing_fmt) {
|
||||
|
@ -331,7 +331,7 @@ static void coroutine_fn backup_set_speed(BlockJob *job, int64_t speed)
|
||||
}
|
||||
}
|
||||
|
||||
static void backup_cancel(Job *job)
|
||||
static void backup_cancel(Job *job, bool force)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
|
||||
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include "hw/qdev-core.h"
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-events-block.h"
|
||||
@ -142,19 +141,18 @@ static void blk_root_set_aio_ctx(BdrvChild *child, AioContext *ctx,
|
||||
static char *blk_root_get_parent_desc(BdrvChild *child)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
char *dev_id;
|
||||
g_autofree char *dev_id = NULL;
|
||||
|
||||
if (blk->name) {
|
||||
return g_strdup(blk->name);
|
||||
return g_strdup_printf("block device '%s'", blk->name);
|
||||
}
|
||||
|
||||
dev_id = blk_get_attached_dev_id(blk);
|
||||
if (*dev_id) {
|
||||
return dev_id;
|
||||
return g_strdup_printf("block device '%s'", dev_id);
|
||||
} else {
|
||||
/* TODO Callback into the BB owner for something more detailed */
|
||||
g_free(dev_id);
|
||||
return g_strdup("a block device");
|
||||
return g_strdup("an unnamed block device");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1853,7 +1851,7 @@ bool blk_supports_write_perm(BlockBackend *blk)
|
||||
if (bs) {
|
||||
return !bdrv_is_read_only(bs);
|
||||
} else {
|
||||
return !blk->root_state.read_only;
|
||||
return blk->root_state.open_flags & BDRV_O_RDWR;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1955,16 +1953,29 @@ uint32_t blk_get_request_alignment(BlockBackend *blk)
|
||||
return bs ? bs->bl.request_alignment : BDRV_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
/* Returns the maximum hardware transfer length, in bytes; guaranteed nonzero */
|
||||
uint64_t blk_get_max_hw_transfer(BlockBackend *blk)
|
||||
{
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
uint64_t max = INT_MAX;
|
||||
|
||||
if (bs) {
|
||||
max = MIN_NON_ZERO(max, bs->bl.max_hw_transfer);
|
||||
max = MIN_NON_ZERO(max, bs->bl.max_transfer);
|
||||
}
|
||||
return ROUND_DOWN(max, blk_get_request_alignment(blk));
|
||||
}
|
||||
|
||||
/* Returns the maximum transfer length, in bytes; guaranteed nonzero */
|
||||
uint32_t blk_get_max_transfer(BlockBackend *blk)
|
||||
{
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
uint32_t max = 0;
|
||||
uint32_t max = INT_MAX;
|
||||
|
||||
if (bs) {
|
||||
max = bs->bl.max_transfer;
|
||||
max = MIN_NON_ZERO(max, bs->bl.max_transfer);
|
||||
}
|
||||
return MIN_NON_ZERO(max, INT_MAX);
|
||||
return ROUND_DOWN(max, blk_get_request_alignment(blk));
|
||||
}
|
||||
|
||||
int blk_get_max_iov(BlockBackend *blk)
|
||||
@ -2270,7 +2281,6 @@ void blk_update_root_state(BlockBackend *blk)
|
||||
assert(blk->root);
|
||||
|
||||
blk->root_state.open_flags = blk->root->bs->open_flags;
|
||||
blk->root_state.read_only = blk->root->bs->read_only;
|
||||
blk->root_state.detect_zeroes = blk->root->bs->detect_zeroes;
|
||||
}
|
||||
|
||||
@ -2289,12 +2299,7 @@ bool blk_get_detect_zeroes_from_root_state(BlockBackend *blk)
|
||||
*/
|
||||
int blk_get_open_flags_from_root_state(BlockBackend *blk)
|
||||
{
|
||||
int bs_flags;
|
||||
|
||||
bs_flags = blk->root_state.read_only ? 0 : BDRV_O_RDWR;
|
||||
bs_flags |= blk->root_state.open_flags & ~BDRV_O_RDWR;
|
||||
|
||||
return bs_flags;
|
||||
return blk->root_state.open_flags;
|
||||
}
|
||||
|
||||
BlockBackendRootState *blk_get_root_state(BlockBackend *blk)
|
||||
@ -2394,8 +2399,13 @@ static void blk_root_drained_begin(BdrvChild *child)
|
||||
static bool blk_root_drained_poll(BdrvChild *child)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
bool busy = false;
|
||||
assert(blk->quiesce_counter);
|
||||
return !!blk->in_flight;
|
||||
|
||||
if (blk->dev_ops && blk->dev_ops->drained_poll) {
|
||||
busy = blk->dev_ops->drained_poll(blk->dev_opaque);
|
||||
}
|
||||
return busy || !!blk->in_flight;
|
||||
}
|
||||
|
||||
static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter)
|
||||
|
@ -28,10 +28,18 @@
|
||||
#define BLOCK_COPY_MAX_WORKERS 64
|
||||
#define BLOCK_COPY_SLICE_TIME 100000000ULL /* ns */
|
||||
|
||||
typedef enum {
|
||||
COPY_READ_WRITE_CLUSTER,
|
||||
COPY_READ_WRITE,
|
||||
COPY_WRITE_ZEROES,
|
||||
COPY_RANGE_SMALL,
|
||||
COPY_RANGE_FULL
|
||||
} BlockCopyMethod;
|
||||
|
||||
static coroutine_fn int block_copy_task_entry(AioTask *task);
|
||||
|
||||
typedef struct BlockCopyCallState {
|
||||
/* IN parameters. Initialized in block_copy_async() and never changed. */
|
||||
/* Fields initialized in block_copy_async() and never changed. */
|
||||
BlockCopyState *s;
|
||||
int64_t offset;
|
||||
int64_t bytes;
|
||||
@ -40,33 +48,60 @@ typedef struct BlockCopyCallState {
|
||||
bool ignore_ratelimit;
|
||||
BlockCopyAsyncCallbackFunc cb;
|
||||
void *cb_opaque;
|
||||
|
||||
/* Coroutine where async block-copy is running */
|
||||
Coroutine *co;
|
||||
|
||||
/* Fields whose state changes throughout the execution */
|
||||
bool finished; /* atomic */
|
||||
QemuCoSleep sleep; /* TODO: protect API with a lock */
|
||||
bool cancelled; /* atomic */
|
||||
/* To reference all call states from BlockCopyState */
|
||||
QLIST_ENTRY(BlockCopyCallState) list;
|
||||
|
||||
/* State */
|
||||
int ret;
|
||||
bool finished;
|
||||
QemuCoSleepState *sleep_state;
|
||||
bool cancelled;
|
||||
|
||||
/* OUT parameters */
|
||||
/*
|
||||
* Fields that report information about return values and erros.
|
||||
* Protected by lock in BlockCopyState.
|
||||
*/
|
||||
bool error_is_read;
|
||||
/*
|
||||
* @ret is set concurrently by tasks under mutex. Only set once by first
|
||||
* failed task (and untouched if no task failed).
|
||||
* After finishing (call_state->finished is true), it is not modified
|
||||
* anymore and may be safely read without mutex.
|
||||
*/
|
||||
int ret;
|
||||
} BlockCopyCallState;
|
||||
|
||||
typedef struct BlockCopyTask {
|
||||
AioTask task;
|
||||
|
||||
/*
|
||||
* Fields initialized in block_copy_task_create()
|
||||
* and never changed.
|
||||
*/
|
||||
BlockCopyState *s;
|
||||
BlockCopyCallState *call_state;
|
||||
int64_t offset;
|
||||
int64_t bytes;
|
||||
bool zeroes;
|
||||
QLIST_ENTRY(BlockCopyTask) list;
|
||||
/*
|
||||
* @method can also be set again in the while loop of
|
||||
* block_copy_dirty_clusters(), but it is never accessed concurrently
|
||||
* because the only other function that reads it is
|
||||
* block_copy_task_entry() and it is invoked afterwards in the same
|
||||
* iteration.
|
||||
*/
|
||||
BlockCopyMethod method;
|
||||
|
||||
/*
|
||||
* Fields whose state changes throughout the execution
|
||||
* Protected by lock in BlockCopyState.
|
||||
*/
|
||||
CoQueue wait_queue; /* coroutines blocked on this task */
|
||||
/*
|
||||
* Only protect the case of parallel read while updating @bytes
|
||||
* value in block_copy_task_shrink().
|
||||
*/
|
||||
int64_t bytes;
|
||||
QLIST_ENTRY(BlockCopyTask) list;
|
||||
} BlockCopyTask;
|
||||
|
||||
static int64_t task_end(BlockCopyTask *task)
|
||||
@ -82,17 +117,25 @@ typedef struct BlockCopyState {
|
||||
*/
|
||||
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(, BlockCopyTask) tasks; /* All tasks from all block-copy calls */
|
||||
QLIST_HEAD(, BlockCopyCallState) calls;
|
||||
|
||||
/*
|
||||
* Fields initialized in block_copy_state_new()
|
||||
* and never changed.
|
||||
*/
|
||||
int64_t cluster_size;
|
||||
int64_t max_transfer;
|
||||
uint64_t len;
|
||||
BdrvRequestFlags write_flags;
|
||||
|
||||
/*
|
||||
* Fields whose state changes throughout the execution
|
||||
* Protected by lock.
|
||||
*/
|
||||
CoMutex lock;
|
||||
int64_t in_flight_bytes;
|
||||
BlockCopyMethod method;
|
||||
QLIST_HEAD(, BlockCopyTask) tasks; /* All tasks from all block-copy calls */
|
||||
QLIST_HEAD(, BlockCopyCallState) calls;
|
||||
/*
|
||||
* skip_unallocated:
|
||||
*
|
||||
@ -107,16 +150,15 @@ typedef struct BlockCopyState {
|
||||
* skip unallocated regions, clear them in the copy_bitmap, and invoke
|
||||
* block_copy_reset_unallocated() every time it does.
|
||||
*/
|
||||
bool skip_unallocated;
|
||||
|
||||
bool skip_unallocated; /* atomic */
|
||||
/* State fields that use a thread-safe API */
|
||||
BdrvDirtyBitmap *copy_bitmap;
|
||||
ProgressMeter *progress;
|
||||
|
||||
SharedResource *mem;
|
||||
|
||||
uint64_t speed;
|
||||
RateLimit rate_limit;
|
||||
} BlockCopyState;
|
||||
|
||||
/* Called with lock held */
|
||||
static BlockCopyTask *find_conflicting_task(BlockCopyState *s,
|
||||
int64_t offset, int64_t bytes)
|
||||
{
|
||||
@ -134,6 +176,9 @@ static BlockCopyTask *find_conflicting_task(BlockCopyState *s,
|
||||
/*
|
||||
* If there are no intersecting tasks return false. Otherwise, wait for the
|
||||
* first found intersecting tasks to finish and return true.
|
||||
*
|
||||
* Called with lock held. May temporary release the lock.
|
||||
* Return value of 0 proves that lock was NOT released.
|
||||
*/
|
||||
static bool coroutine_fn block_copy_wait_one(BlockCopyState *s, int64_t offset,
|
||||
int64_t bytes)
|
||||
@ -144,22 +189,43 @@ static bool coroutine_fn block_copy_wait_one(BlockCopyState *s, int64_t offset,
|
||||
return false;
|
||||
}
|
||||
|
||||
qemu_co_queue_wait(&task->wait_queue, NULL);
|
||||
qemu_co_queue_wait(&task->wait_queue, &s->lock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Called with lock held */
|
||||
static int64_t block_copy_chunk_size(BlockCopyState *s)
|
||||
{
|
||||
switch (s->method) {
|
||||
case COPY_READ_WRITE_CLUSTER:
|
||||
return s->cluster_size;
|
||||
case COPY_READ_WRITE:
|
||||
case COPY_RANGE_SMALL:
|
||||
return MIN(MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER),
|
||||
s->max_transfer);
|
||||
case COPY_RANGE_FULL:
|
||||
return MIN(MAX(s->cluster_size, BLOCK_COPY_MAX_COPY_RANGE),
|
||||
s->max_transfer);
|
||||
default:
|
||||
/* Cannot have COPY_WRITE_ZEROES here. */
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for the first dirty area in offset/bytes range and create task at
|
||||
* the beginning of it.
|
||||
*/
|
||||
static BlockCopyTask *block_copy_task_create(BlockCopyState *s,
|
||||
BlockCopyCallState *call_state,
|
||||
int64_t offset, int64_t bytes)
|
||||
static coroutine_fn BlockCopyTask *
|
||||
block_copy_task_create(BlockCopyState *s, BlockCopyCallState *call_state,
|
||||
int64_t offset, int64_t bytes)
|
||||
{
|
||||
BlockCopyTask *task;
|
||||
int64_t max_chunk = MIN_NON_ZERO(s->copy_size, call_state->max_chunk);
|
||||
int64_t max_chunk;
|
||||
|
||||
QEMU_LOCK_GUARD(&s->lock);
|
||||
max_chunk = MIN_NON_ZERO(block_copy_chunk_size(s), call_state->max_chunk);
|
||||
if (!bdrv_dirty_bitmap_next_dirty_area(s->copy_bitmap,
|
||||
offset, offset + bytes,
|
||||
max_chunk, &offset, &bytes))
|
||||
@ -183,6 +249,7 @@ static BlockCopyTask *block_copy_task_create(BlockCopyState *s,
|
||||
.call_state = call_state,
|
||||
.offset = offset,
|
||||
.bytes = bytes,
|
||||
.method = s->method,
|
||||
};
|
||||
qemu_co_queue_init(&task->wait_queue);
|
||||
QLIST_INSERT_HEAD(&s->tasks, task, list);
|
||||
@ -200,6 +267,7 @@ static BlockCopyTask *block_copy_task_create(BlockCopyState *s,
|
||||
static void coroutine_fn block_copy_task_shrink(BlockCopyTask *task,
|
||||
int64_t new_bytes)
|
||||
{
|
||||
QEMU_LOCK_GUARD(&task->s->lock);
|
||||
if (new_bytes == task->bytes) {
|
||||
return;
|
||||
}
|
||||
@ -216,11 +284,15 @@ static void coroutine_fn block_copy_task_shrink(BlockCopyTask *task,
|
||||
|
||||
static void coroutine_fn block_copy_task_end(BlockCopyTask *task, int ret)
|
||||
{
|
||||
QEMU_LOCK_GUARD(&task->s->lock);
|
||||
task->s->in_flight_bytes -= task->bytes;
|
||||
if (ret < 0) {
|
||||
bdrv_set_dirty_bitmap(task->s->copy_bitmap, task->offset, task->bytes);
|
||||
}
|
||||
QLIST_REMOVE(task, list);
|
||||
progress_set_remaining(task->s->progress,
|
||||
bdrv_get_dirty_count(task->s->copy_bitmap) +
|
||||
task->s->in_flight_bytes);
|
||||
qemu_co_queue_restart_all(&task->wait_queue);
|
||||
}
|
||||
|
||||
@ -230,6 +302,7 @@ void block_copy_state_free(BlockCopyState *s)
|
||||
return;
|
||||
}
|
||||
|
||||
ratelimit_destroy(&s->rate_limit);
|
||||
bdrv_release_dirty_bitmap(s->copy_bitmap);
|
||||
shres_destroy(s->mem);
|
||||
g_free(s);
|
||||
@ -265,36 +338,39 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
||||
.len = bdrv_dirty_bitmap_size(copy_bitmap),
|
||||
.write_flags = write_flags,
|
||||
.mem = shres_create(BLOCK_COPY_MAX_MEM),
|
||||
.max_transfer = QEMU_ALIGN_DOWN(
|
||||
block_copy_max_transfer(source, target),
|
||||
cluster_size),
|
||||
};
|
||||
|
||||
if (block_copy_max_transfer(source, target) < cluster_size) {
|
||||
if (s->max_transfer < 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
|
||||
* buffered copying (read and write respect max_transfer on their
|
||||
* behalf).
|
||||
*/
|
||||
s->use_copy_range = false;
|
||||
s->copy_size = cluster_size;
|
||||
s->method = COPY_READ_WRITE_CLUSTER;
|
||||
} else if (write_flags & BDRV_REQ_WRITE_COMPRESSED) {
|
||||
/* Compression supports only cluster-size writes and no copy-range. */
|
||||
s->use_copy_range = false;
|
||||
s->copy_size = cluster_size;
|
||||
s->method = COPY_READ_WRITE_CLUSTER;
|
||||
} else {
|
||||
/*
|
||||
* We enable copy-range, but keep small copy_size, until first
|
||||
* If copy range enabled, start with COPY_RANGE_SMALL, until first
|
||||
* successful copy_range (look at block_copy_do_copy).
|
||||
*/
|
||||
s->use_copy_range = use_copy_range;
|
||||
s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER);
|
||||
s->method = use_copy_range ? COPY_RANGE_SMALL : COPY_READ_WRITE;
|
||||
}
|
||||
|
||||
ratelimit_init(&s->rate_limit);
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
QLIST_INIT(&s->tasks);
|
||||
QLIST_INIT(&s->calls);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Only set before running the job, no need for locking. */
|
||||
void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm)
|
||||
{
|
||||
s->progress = pm;
|
||||
@ -340,11 +416,15 @@ static coroutine_fn int block_copy_task_run(AioTaskPool *pool,
|
||||
*
|
||||
* No sync here: nor bitmap neighter intersecting requests handling, only copy.
|
||||
*
|
||||
* @method is an in-out argument, so that copy_range can be either extended to
|
||||
* a full-size buffer or disabled if the copy_range attempt fails. The output
|
||||
* value of @method should be used for subsequent tasks.
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
static int coroutine_fn block_copy_do_copy(BlockCopyState *s,
|
||||
int64_t offset, int64_t bytes,
|
||||
bool zeroes, bool *error_is_read)
|
||||
BlockCopyMethod *method,
|
||||
bool *error_is_read)
|
||||
{
|
||||
int ret;
|
||||
int64_t nbytes = MIN(offset + bytes, s->len) - offset;
|
||||
@ -358,7 +438,8 @@ static int coroutine_fn block_copy_do_copy(BlockCopyState *s,
|
||||
offset + bytes == QEMU_ALIGN_UP(s->len, s->cluster_size));
|
||||
assert(nbytes < INT_MAX);
|
||||
|
||||
if (zeroes) {
|
||||
switch (*method) {
|
||||
case COPY_WRITE_ZEROES:
|
||||
ret = bdrv_co_pwrite_zeroes(s->target, offset, nbytes, s->write_flags &
|
||||
~BDRV_REQ_WRITE_COMPRESSED);
|
||||
if (ret < 0) {
|
||||
@ -366,84 +447,86 @@ static int coroutine_fn block_copy_do_copy(BlockCopyState *s,
|
||||
*error_is_read = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (s->use_copy_range) {
|
||||
case COPY_RANGE_SMALL:
|
||||
case COPY_RANGE_FULL:
|
||||
ret = bdrv_co_copy_range(s->source, offset, s->target, offset, nbytes,
|
||||
0, s->write_flags);
|
||||
if (ret >= 0) {
|
||||
/* Successful copy-range, increase chunk size. */
|
||||
*method = COPY_RANGE_FULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
trace_block_copy_copy_range_fail(s, offset, ret);
|
||||
*method = COPY_READ_WRITE;
|
||||
/* Fall through to read+write with allocated buffer */
|
||||
|
||||
case COPY_READ_WRITE_CLUSTER:
|
||||
case COPY_READ_WRITE:
|
||||
/*
|
||||
* 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. 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, offset, nbytes, bounce_buffer, 0);
|
||||
if (ret < 0) {
|
||||
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));
|
||||
}
|
||||
trace_block_copy_read_fail(s, offset, ret);
|
||||
*error_is_read = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = bdrv_co_pwrite(s->target, offset, nbytes, bounce_buffer,
|
||||
s->write_flags);
|
||||
if (ret < 0) {
|
||||
trace_block_copy_write_fail(s, offset, ret);
|
||||
*error_is_read = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
qemu_vfree(bounce_buffer);
|
||||
break;
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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. 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, offset, nbytes, bounce_buffer, 0);
|
||||
if (ret < 0) {
|
||||
trace_block_copy_read_fail(s, offset, ret);
|
||||
*error_is_read = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = bdrv_co_pwrite(s->target, offset, nbytes, bounce_buffer,
|
||||
s->write_flags);
|
||||
if (ret < 0) {
|
||||
trace_block_copy_write_fail(s, offset, ret);
|
||||
*error_is_read = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
qemu_vfree(bounce_buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static coroutine_fn int block_copy_task_entry(AioTask *task)
|
||||
{
|
||||
BlockCopyTask *t = container_of(task, BlockCopyTask, task);
|
||||
BlockCopyState *s = t->s;
|
||||
bool error_is_read = false;
|
||||
BlockCopyMethod method = t->method;
|
||||
int ret;
|
||||
|
||||
ret = block_copy_do_copy(t->s, t->offset, t->bytes, t->zeroes,
|
||||
&error_is_read);
|
||||
if (ret < 0 && !t->call_state->ret) {
|
||||
t->call_state->ret = ret;
|
||||
t->call_state->error_is_read = error_is_read;
|
||||
} else {
|
||||
progress_work_done(t->s->progress, t->bytes);
|
||||
ret = block_copy_do_copy(s, t->offset, t->bytes, &method, &error_is_read);
|
||||
|
||||
WITH_QEMU_LOCK_GUARD(&s->lock) {
|
||||
if (s->method == t->method) {
|
||||
s->method = method;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
if (!t->call_state->ret) {
|
||||
t->call_state->ret = ret;
|
||||
t->call_state->error_is_read = error_is_read;
|
||||
}
|
||||
} else {
|
||||
progress_work_done(s->progress, t->bytes);
|
||||
}
|
||||
}
|
||||
co_put_to_shres(t->s->mem, t->bytes);
|
||||
co_put_to_shres(s->mem, t->bytes);
|
||||
block_copy_task_end(t, ret);
|
||||
|
||||
return ret;
|
||||
@ -456,7 +539,7 @@ static int block_copy_block_status(BlockCopyState *s, int64_t offset,
|
||||
BlockDriverState *base;
|
||||
int ret;
|
||||
|
||||
if (s->skip_unallocated) {
|
||||
if (qatomic_read(&s->skip_unallocated)) {
|
||||
base = bdrv_backing_chain_next(s->source->bs);
|
||||
} else {
|
||||
base = NULL;
|
||||
@ -543,10 +626,12 @@ int64_t block_copy_reset_unallocated(BlockCopyState *s,
|
||||
bytes = clusters * s->cluster_size;
|
||||
|
||||
if (!ret) {
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
|
||||
progress_set_remaining(s->progress,
|
||||
bdrv_get_dirty_count(s->copy_bitmap) +
|
||||
s->in_flight_bytes);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
}
|
||||
|
||||
*count = bytes;
|
||||
@ -582,7 +667,8 @@ block_copy_dirty_clusters(BlockCopyCallState *call_state)
|
||||
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
|
||||
assert(QEMU_IS_ALIGNED(bytes, s->cluster_size));
|
||||
|
||||
while (bytes && aio_task_pool_status(aio) == 0 && !call_state->cancelled) {
|
||||
while (bytes && aio_task_pool_status(aio) == 0 &&
|
||||
!qatomic_read(&call_state->cancelled)) {
|
||||
BlockCopyTask *task;
|
||||
int64_t status_bytes;
|
||||
|
||||
@ -604,34 +690,32 @@ block_copy_dirty_clusters(BlockCopyCallState *call_state)
|
||||
if (status_bytes < task->bytes) {
|
||||
block_copy_task_shrink(task, status_bytes);
|
||||
}
|
||||
if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) {
|
||||
if (qatomic_read(&s->skip_unallocated) &&
|
||||
!(ret & BDRV_BLOCK_ALLOCATED)) {
|
||||
block_copy_task_end(task, 0);
|
||||
progress_set_remaining(s->progress,
|
||||
bdrv_get_dirty_count(s->copy_bitmap) +
|
||||
s->in_flight_bytes);
|
||||
trace_block_copy_skip_range(s, task->offset, task->bytes);
|
||||
offset = task_end(task);
|
||||
bytes = end - offset;
|
||||
g_free(task);
|
||||
continue;
|
||||
}
|
||||
task->zeroes = ret & BDRV_BLOCK_ZERO;
|
||||
|
||||
if (s->speed) {
|
||||
if (!call_state->ignore_ratelimit) {
|
||||
uint64_t ns = ratelimit_calculate_delay(&s->rate_limit, 0);
|
||||
if (ns > 0) {
|
||||
block_copy_task_end(task, -EAGAIN);
|
||||
g_free(task);
|
||||
qemu_co_sleep_ns_wakeable(QEMU_CLOCK_REALTIME, ns,
|
||||
&call_state->sleep_state);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ratelimit_calculate_delay(&s->rate_limit, task->bytes);
|
||||
if (ret & BDRV_BLOCK_ZERO) {
|
||||
task->method = COPY_WRITE_ZEROES;
|
||||
}
|
||||
|
||||
if (!call_state->ignore_ratelimit) {
|
||||
uint64_t ns = ratelimit_calculate_delay(&s->rate_limit, 0);
|
||||
if (ns > 0) {
|
||||
block_copy_task_end(task, -EAGAIN);
|
||||
g_free(task);
|
||||
qemu_co_sleep_ns_wakeable(&call_state->sleep,
|
||||
QEMU_CLOCK_REALTIME, ns);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ratelimit_calculate_delay(&s->rate_limit, task->bytes);
|
||||
|
||||
trace_block_copy_process(s, task->offset);
|
||||
|
||||
co_get_from_shres(s->mem, task->bytes);
|
||||
@ -672,9 +756,7 @@ out:
|
||||
|
||||
void block_copy_kick(BlockCopyCallState *call_state)
|
||||
{
|
||||
if (call_state->sleep_state) {
|
||||
qemu_co_sleep_wake(call_state->sleep_state);
|
||||
}
|
||||
qemu_co_sleep_wake(&call_state->sleep);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -689,15 +771,40 @@ void block_copy_kick(BlockCopyCallState *call_state)
|
||||
static int coroutine_fn block_copy_common(BlockCopyCallState *call_state)
|
||||
{
|
||||
int ret;
|
||||
BlockCopyState *s = call_state->s;
|
||||
|
||||
QLIST_INSERT_HEAD(&call_state->s->calls, call_state, list);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
QLIST_INSERT_HEAD(&s->calls, call_state, list);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
do {
|
||||
ret = block_copy_dirty_clusters(call_state);
|
||||
|
||||
if (ret == 0 && !call_state->cancelled) {
|
||||
ret = block_copy_wait_one(call_state->s, call_state->offset,
|
||||
call_state->bytes);
|
||||
if (ret == 0 && !qatomic_read(&call_state->cancelled)) {
|
||||
WITH_QEMU_LOCK_GUARD(&s->lock) {
|
||||
/*
|
||||
* Check that there is no task we still need to
|
||||
* wait to complete
|
||||
*/
|
||||
ret = block_copy_wait_one(s, call_state->offset,
|
||||
call_state->bytes);
|
||||
if (ret == 0) {
|
||||
/*
|
||||
* No pending tasks, but check again the bitmap in this
|
||||
* same critical section, since a task might have failed
|
||||
* between this and the critical section in
|
||||
* block_copy_dirty_clusters().
|
||||
*
|
||||
* block_copy_wait_one return value 0 also means that it
|
||||
* didn't release the lock. So, we are still in the same
|
||||
* critical section, not interrupted by any concurrent
|
||||
* access to state.
|
||||
*/
|
||||
ret = bdrv_dirty_bitmap_next_dirty(s->copy_bitmap,
|
||||
call_state->offset,
|
||||
call_state->bytes) >= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -709,15 +816,17 @@ static int coroutine_fn block_copy_common(BlockCopyCallState *call_state)
|
||||
* 2. We have waited for some intersecting block-copy request
|
||||
* It may have failed and produced new dirty bits.
|
||||
*/
|
||||
} while (ret > 0 && !call_state->cancelled);
|
||||
} while (ret > 0 && !qatomic_read(&call_state->cancelled));
|
||||
|
||||
call_state->finished = true;
|
||||
qatomic_store_release(&call_state->finished, true);
|
||||
|
||||
if (call_state->cb) {
|
||||
call_state->cb(call_state->cb_opaque);
|
||||
}
|
||||
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
QLIST_REMOVE(call_state, list);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -772,44 +881,50 @@ void block_copy_call_free(BlockCopyCallState *call_state)
|
||||
return;
|
||||
}
|
||||
|
||||
assert(call_state->finished);
|
||||
assert(qatomic_read(&call_state->finished));
|
||||
g_free(call_state);
|
||||
}
|
||||
|
||||
bool block_copy_call_finished(BlockCopyCallState *call_state)
|
||||
{
|
||||
return call_state->finished;
|
||||
return qatomic_read(&call_state->finished);
|
||||
}
|
||||
|
||||
bool block_copy_call_succeeded(BlockCopyCallState *call_state)
|
||||
{
|
||||
return call_state->finished && !call_state->cancelled &&
|
||||
call_state->ret == 0;
|
||||
return qatomic_load_acquire(&call_state->finished) &&
|
||||
!qatomic_read(&call_state->cancelled) &&
|
||||
call_state->ret == 0;
|
||||
}
|
||||
|
||||
bool block_copy_call_failed(BlockCopyCallState *call_state)
|
||||
{
|
||||
return call_state->finished && !call_state->cancelled &&
|
||||
call_state->ret < 0;
|
||||
return qatomic_load_acquire(&call_state->finished) &&
|
||||
!qatomic_read(&call_state->cancelled) &&
|
||||
call_state->ret < 0;
|
||||
}
|
||||
|
||||
bool block_copy_call_cancelled(BlockCopyCallState *call_state)
|
||||
{
|
||||
return call_state->cancelled;
|
||||
return qatomic_read(&call_state->cancelled);
|
||||
}
|
||||
|
||||
int block_copy_call_status(BlockCopyCallState *call_state, bool *error_is_read)
|
||||
{
|
||||
assert(call_state->finished);
|
||||
assert(qatomic_load_acquire(&call_state->finished));
|
||||
if (error_is_read) {
|
||||
*error_is_read = call_state->error_is_read;
|
||||
}
|
||||
return call_state->ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that cancelling and finishing are racy.
|
||||
* User can cancel a block-copy that is already finished.
|
||||
*/
|
||||
void block_copy_call_cancel(BlockCopyCallState *call_state)
|
||||
{
|
||||
call_state->cancelled = true;
|
||||
qatomic_set(&call_state->cancelled, true);
|
||||
block_copy_kick(call_state);
|
||||
}
|
||||
|
||||
@ -820,15 +935,12 @@ BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s)
|
||||
|
||||
void block_copy_set_skip_unallocated(BlockCopyState *s, bool skip)
|
||||
{
|
||||
s->skip_unallocated = skip;
|
||||
qatomic_set(&s->skip_unallocated, skip);
|
||||
}
|
||||
|
||||
void block_copy_set_speed(BlockCopyState *s, uint64_t speed)
|
||||
{
|
||||
s->speed = speed;
|
||||
if (speed > 0) {
|
||||
ratelimit_set_speed(&s->rate_limit, speed, BLOCK_COPY_SLICE_TIME);
|
||||
}
|
||||
ratelimit_set_speed(&s->rate_limit, speed, BLOCK_COPY_SLICE_TIME);
|
||||
|
||||
/*
|
||||
* Note: it's good to kick all call states from here, but it should be done
|
||||
|
@ -119,24 +119,24 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
|
||||
uint64_t delay_ns = 0;
|
||||
int ret = 0;
|
||||
int64_t n = 0; /* bytes */
|
||||
void *buf = NULL;
|
||||
QEMU_AUTO_VFREE void *buf = NULL;
|
||||
int64_t len, base_len;
|
||||
|
||||
ret = len = blk_getlength(s->top);
|
||||
len = blk_getlength(s->top);
|
||||
if (len < 0) {
|
||||
goto out;
|
||||
return len;
|
||||
}
|
||||
job_progress_set_remaining(&s->common.job, len);
|
||||
|
||||
ret = base_len = blk_getlength(s->base);
|
||||
base_len = blk_getlength(s->base);
|
||||
if (base_len < 0) {
|
||||
goto out;
|
||||
return base_len;
|
||||
}
|
||||
|
||||
if (base_len < len) {
|
||||
ret = blk_truncate(s->base, len, false, PREALLOC_MODE_OFF, 0, NULL);
|
||||
if (ret) {
|
||||
goto out;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,7 +174,7 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
|
||||
block_job_error_action(&s->common, s->on_error,
|
||||
error_in_source, -ret);
|
||||
if (action == BLOCK_ERROR_ACTION_REPORT) {
|
||||
goto out;
|
||||
return ret;
|
||||
} else {
|
||||
n = 0;
|
||||
continue;
|
||||
@ -190,12 +190,7 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
qemu_vfree(buf);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const BlockJobDriver commit_job_driver = {
|
||||
@ -435,7 +430,7 @@ int bdrv_commit(BlockDriverState *bs)
|
||||
int ro;
|
||||
int64_t n;
|
||||
int ret = 0;
|
||||
uint8_t *buf = NULL;
|
||||
QEMU_AUTO_VFREE uint8_t *buf = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (!drv)
|
||||
@ -453,7 +448,7 @@ int bdrv_commit(BlockDriverState *bs)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ro = backing_file_bs->read_only;
|
||||
ro = bdrv_is_read_only(backing_file_bs);
|
||||
|
||||
if (ro) {
|
||||
if (bdrv_reopen_set_read_only(backing_file_bs, false, NULL)) {
|
||||
@ -556,8 +551,6 @@ int bdrv_commit(BlockDriverState *bs)
|
||||
|
||||
ret = 0;
|
||||
ro_cleanup:
|
||||
qemu_vfree(buf);
|
||||
|
||||
blk_unref(backing);
|
||||
if (bdrv_cow_bs(bs) != backing_file_bs) {
|
||||
bdrv_set_backing_hd(bs, backing_file_bs, &error_abort);
|
||||
|
@ -29,7 +29,6 @@
|
||||
|
||||
|
||||
typedef struct BDRVStateCOR {
|
||||
bool active;
|
||||
BlockDriverState *bottom_bs;
|
||||
bool chain_frozen;
|
||||
} BDRVStateCOR;
|
||||
@ -89,7 +88,6 @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
*/
|
||||
bdrv_ref(bottom_bs);
|
||||
}
|
||||
state->active = true;
|
||||
state->bottom_bs = bottom_bs;
|
||||
|
||||
/*
|
||||
@ -112,17 +110,6 @@ static void cor_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||
uint64_t perm, uint64_t shared,
|
||||
uint64_t *nperm, uint64_t *nshared)
|
||||
{
|
||||
BDRVStateCOR *s = bs->opaque;
|
||||
|
||||
if (!s->active) {
|
||||
/*
|
||||
* While the filter is being removed
|
||||
*/
|
||||
*nperm = 0;
|
||||
*nshared = BLK_PERM_ALL;
|
||||
return;
|
||||
}
|
||||
|
||||
*nperm = perm & PERM_PASSTHROUGH;
|
||||
*nshared = (shared & PERM_PASSTHROUGH) | PERM_UNCHANGED;
|
||||
|
||||
@ -280,32 +267,14 @@ static BlockDriver bdrv_copy_on_read = {
|
||||
|
||||
void bdrv_cor_filter_drop(BlockDriverState *cor_filter_bs)
|
||||
{
|
||||
BdrvChild *child;
|
||||
BlockDriverState *bs;
|
||||
BDRVStateCOR *s = cor_filter_bs->opaque;
|
||||
|
||||
child = bdrv_filter_child(cor_filter_bs);
|
||||
if (!child) {
|
||||
return;
|
||||
}
|
||||
bs = child->bs;
|
||||
|
||||
/* Retain the BDS until we complete the graph change. */
|
||||
bdrv_ref(bs);
|
||||
/* Hold a guest back from writing while permissions are being reset. */
|
||||
bdrv_drained_begin(bs);
|
||||
/* Drop permissions before the graph change. */
|
||||
s->active = false;
|
||||
/* unfreeze, as otherwise bdrv_replace_node() will fail */
|
||||
if (s->chain_frozen) {
|
||||
s->chain_frozen = false;
|
||||
bdrv_unfreeze_backing_chain(cor_filter_bs, s->bottom_bs);
|
||||
}
|
||||
bdrv_child_refresh_perms(cor_filter_bs, child, &error_abort);
|
||||
bdrv_replace_node(cor_filter_bs, bs, &error_abort);
|
||||
|
||||
bdrv_drained_end(bs);
|
||||
bdrv_unref(bs);
|
||||
bdrv_drop_filter(cor_filter_bs, &error_abort);
|
||||
bdrv_unref(cor_filter_bs);
|
||||
}
|
||||
|
||||
|
@ -66,4 +66,10 @@ int coroutine_fn bdrv_co_readv_vmstate(BlockDriverState *bs,
|
||||
int coroutine_fn bdrv_co_writev_vmstate(BlockDriverState *bs,
|
||||
QEMUIOVector *qiov, int64_t pos);
|
||||
|
||||
int generated_co_wrapper
|
||||
nbd_do_establish_connection(BlockDriverState *bs, Error **errp);
|
||||
int coroutine_fn
|
||||
nbd_co_do_establish_connection(BlockDriverState *bs, Error **errp);
|
||||
|
||||
|
||||
#endif /* BLOCK_COROUTINES_INT_H */
|
||||
|
@ -70,9 +70,16 @@ static void vu_blk_req_complete(VuBlkReq *req)
|
||||
static bool vu_blk_sect_range_ok(VuBlkExport *vexp, uint64_t sector,
|
||||
size_t size)
|
||||
{
|
||||
uint64_t nb_sectors = size >> BDRV_SECTOR_BITS;
|
||||
uint64_t nb_sectors;
|
||||
uint64_t total_sectors;
|
||||
|
||||
if (size % VIRTIO_BLK_SECTOR_SIZE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nb_sectors = size >> VIRTIO_BLK_SECTOR_BITS;
|
||||
|
||||
QEMU_BUILD_BUG_ON(BDRV_SECTOR_SIZE != VIRTIO_BLK_SECTOR_SIZE);
|
||||
if (nb_sectors > BDRV_REQUEST_MAX_SECTORS) {
|
||||
return false;
|
||||
}
|
||||
|
@ -42,6 +42,8 @@
|
||||
#include "scsi/constants.h"
|
||||
|
||||
#if defined(__APPLE__) && (__MACH__)
|
||||
#include <sys/ioctl.h>
|
||||
#if defined(HAVE_HOST_BLOCK_DEVICE)
|
||||
#include <paths.h>
|
||||
#include <sys/param.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
@ -52,6 +54,7 @@
|
||||
//#include <IOKit/storage/IOCDTypes.h>
|
||||
#include <IOKit/storage/IODVDMedia.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */
|
||||
#endif
|
||||
|
||||
#ifdef __sun__
|
||||
@ -106,8 +109,6 @@
|
||||
#include <xfs/xfs.h>
|
||||
#endif
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
/* OS X does not have O_DSYNC */
|
||||
#ifndef O_DSYNC
|
||||
#ifdef O_SYNC
|
||||
@ -160,7 +161,7 @@ typedef struct BDRVRawState {
|
||||
bool discard_zeroes:1;
|
||||
bool use_linux_aio:1;
|
||||
bool use_linux_io_uring:1;
|
||||
bool page_cache_inconsistent:1;
|
||||
int page_cache_inconsistent; /* errno from fdatasync failure */
|
||||
bool has_fallocate;
|
||||
bool needs_alignment;
|
||||
bool drop_cache;
|
||||
@ -180,7 +181,17 @@ typedef struct BDRVRawReopenState {
|
||||
bool check_cache_dropped;
|
||||
} BDRVRawReopenState;
|
||||
|
||||
static int fd_open(BlockDriverState *bs);
|
||||
static int fd_open(BlockDriverState *bs)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
/* this is just to ensure s->fd is sane (its called by io ops) */
|
||||
if (s->fd >= 0) {
|
||||
return 0;
|
||||
}
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int64_t raw_getlength(BlockDriverState *bs);
|
||||
|
||||
typedef struct RawPosixAIOData {
|
||||
@ -1149,22 +1160,27 @@ static void raw_reopen_abort(BDRVReopenState *state)
|
||||
s->reopen_state = NULL;
|
||||
}
|
||||
|
||||
static int sg_get_max_transfer_length(int fd)
|
||||
static int hdev_get_max_hw_transfer(int fd, struct stat *st)
|
||||
{
|
||||
#ifdef BLKSECTGET
|
||||
int max_bytes = 0;
|
||||
|
||||
if (ioctl(fd, BLKSECTGET, &max_bytes) == 0) {
|
||||
return max_bytes;
|
||||
if (S_ISBLK(st->st_mode)) {
|
||||
unsigned short max_sectors = 0;
|
||||
if (ioctl(fd, BLKSECTGET, &max_sectors) == 0) {
|
||||
return max_sectors * 512;
|
||||
}
|
||||
} else {
|
||||
return -errno;
|
||||
int max_bytes = 0;
|
||||
if (ioctl(fd, BLKSECTGET, &max_bytes) == 0) {
|
||||
return max_bytes;
|
||||
}
|
||||
}
|
||||
return -errno;
|
||||
#else
|
||||
return -ENOSYS;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int sg_get_max_segments(int fd)
|
||||
static int hdev_get_max_segments(int fd, struct stat *st)
|
||||
{
|
||||
#ifdef CONFIG_LINUX
|
||||
char buf[32];
|
||||
@ -1173,15 +1189,20 @@ static int sg_get_max_segments(int fd)
|
||||
int ret;
|
||||
int sysfd = -1;
|
||||
long max_segments;
|
||||
struct stat st;
|
||||
|
||||
if (fstat(fd, &st)) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
if (S_ISCHR(st->st_mode)) {
|
||||
if (ioctl(fd, SG_GET_SG_TABLESIZE, &ret) == 0) {
|
||||
return ret;
|
||||
}
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (!S_ISBLK(st->st_mode)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
sysfspath = g_strdup_printf("/sys/dev/block/%u:%u/queue/max_segments",
|
||||
major(st.st_rdev), minor(st.st_rdev));
|
||||
major(st->st_rdev), minor(st->st_rdev));
|
||||
sysfd = open(sysfspath, O_RDONLY);
|
||||
if (sysfd == -1) {
|
||||
ret = -errno;
|
||||
@ -1218,24 +1239,33 @@ out:
|
||||
static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
if (bs->sg) {
|
||||
int ret = sg_get_max_transfer_length(s->fd);
|
||||
|
||||
if (ret > 0 && ret <= BDRV_REQUEST_MAX_BYTES) {
|
||||
bs->bl.max_transfer = pow2floor(ret);
|
||||
}
|
||||
|
||||
ret = sg_get_max_segments(s->fd);
|
||||
if (ret > 0) {
|
||||
bs->bl.max_transfer = MIN(bs->bl.max_transfer,
|
||||
ret * qemu_real_host_page_size);
|
||||
}
|
||||
}
|
||||
struct stat st;
|
||||
|
||||
raw_probe_alignment(bs, s->fd, errp);
|
||||
bs->bl.min_mem_alignment = s->buf_align;
|
||||
bs->bl.opt_mem_alignment = MAX(s->buf_align, qemu_real_host_page_size);
|
||||
|
||||
/*
|
||||
* Maximum transfers are best effort, so it is okay to ignore any
|
||||
* errors. That said, based on the man page errors in fstat would be
|
||||
* very much unexpected; the only possible case seems to be ENOMEM.
|
||||
*/
|
||||
if (fstat(s->fd, &st)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bs->sg || S_ISBLK(st.st_mode)) {
|
||||
int ret = hdev_get_max_hw_transfer(s->fd, &st);
|
||||
|
||||
if (ret > 0 && ret <= BDRV_REQUEST_MAX_BYTES) {
|
||||
bs->bl.max_hw_transfer = ret;
|
||||
}
|
||||
|
||||
ret = hdev_get_max_segments(s->fd, &st);
|
||||
if (ret > 0) {
|
||||
bs->bl.max_iov = ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int check_for_dasd(int fd)
|
||||
@ -1317,7 +1347,9 @@ static int handle_aiocb_ioctl(void *opaque)
|
||||
RawPosixAIOData *aiocb = opaque;
|
||||
int ret;
|
||||
|
||||
ret = ioctl(aiocb->aio_fildes, aiocb->ioctl.cmd, aiocb->ioctl.buf);
|
||||
do {
|
||||
ret = ioctl(aiocb->aio_fildes, aiocb->ioctl.cmd, aiocb->ioctl.buf);
|
||||
} while (ret == -1 && errno == EINTR);
|
||||
if (ret == -1) {
|
||||
return -errno;
|
||||
}
|
||||
@ -1333,11 +1365,13 @@ static int handle_aiocb_flush(void *opaque)
|
||||
int ret;
|
||||
|
||||
if (s->page_cache_inconsistent) {
|
||||
return -EIO;
|
||||
return -s->page_cache_inconsistent;
|
||||
}
|
||||
|
||||
ret = qemu_fdatasync(aiocb->aio_fildes);
|
||||
if (ret == -1) {
|
||||
trace_file_flush_fdatasync_failed(errno);
|
||||
|
||||
/* There is no clear definition of the semantics of a failing fsync(),
|
||||
* so we may have to assume the worst. The sad truth is that this
|
||||
* assumption is correct for Linux. Some pages are now probably marked
|
||||
@ -1352,7 +1386,7 @@ static int handle_aiocb_flush(void *opaque)
|
||||
* Obviously, this doesn't affect O_DIRECT, which bypasses the page
|
||||
* cache. */
|
||||
if ((s->open_flags & O_DIRECT) == 0) {
|
||||
s->page_cache_inconsistent = true;
|
||||
s->page_cache_inconsistent = errno;
|
||||
}
|
||||
return -errno;
|
||||
}
|
||||
@ -1625,17 +1659,17 @@ static int handle_aiocb_write_zeroes(void *opaque)
|
||||
if (s->has_write_zeroes) {
|
||||
int ret = do_fallocate(s->fd, FALLOC_FL_ZERO_RANGE,
|
||||
aiocb->aio_offset, aiocb->aio_nbytes);
|
||||
if (ret == -EINVAL) {
|
||||
/*
|
||||
* Allow falling back to pwrite for file systems that
|
||||
* do not support fallocate() for an unaligned byte range.
|
||||
*/
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (ret == 0 || ret != -ENOTSUP) {
|
||||
if (ret == -ENOTSUP) {
|
||||
s->has_write_zeroes = false;
|
||||
} else if (ret == 0 || ret != -EINVAL) {
|
||||
return ret;
|
||||
}
|
||||
s->has_write_zeroes = false;
|
||||
/*
|
||||
* Note: Some file systems do not like unaligned byte ranges, and
|
||||
* return EINVAL in such a case, though they should not do it according
|
||||
* to the man-page of fallocate(). Thus we simply ignore this return
|
||||
* value and try the other fallbacks instead.
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1650,6 +1684,17 @@ static int handle_aiocb_write_zeroes(void *opaque)
|
||||
return ret;
|
||||
}
|
||||
s->has_fallocate = false;
|
||||
} else if (ret == -EINVAL) {
|
||||
/*
|
||||
* Some file systems like older versions of GPFS do not like un-
|
||||
* aligned byte ranges, and return EINVAL in such a case, though
|
||||
* they should not do it according to the man-page of fallocate().
|
||||
* Warn about the bad filesystem and try the final fallback instead.
|
||||
*/
|
||||
warn_report_once("Your file system is misbehaving: "
|
||||
"fallocate(FALLOC_FL_PUNCH_HOLE) returned EINVAL. "
|
||||
"Please report this bug to your file sytem "
|
||||
"vendor.");
|
||||
} else if (ret != -ENOTSUP) {
|
||||
return ret;
|
||||
} else {
|
||||
@ -2284,39 +2329,37 @@ static int64_t raw_getlength(BlockDriverState *bs)
|
||||
again:
|
||||
#endif
|
||||
if (!fstat(fd, &sb) && (S_IFCHR & sb.st_mode)) {
|
||||
size = 0;
|
||||
#ifdef DIOCGMEDIASIZE
|
||||
if (ioctl(fd, DIOCGMEDIASIZE, (off_t *)&size))
|
||||
#elif defined(DIOCGPART)
|
||||
{
|
||||
struct partinfo pi;
|
||||
if (ioctl(fd, DIOCGPART, &pi) == 0)
|
||||
size = pi.media_size;
|
||||
else
|
||||
size = 0;
|
||||
if (ioctl(fd, DIOCGMEDIASIZE, (off_t *)&size)) {
|
||||
size = 0;
|
||||
}
|
||||
if (size == 0)
|
||||
#endif
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
{
|
||||
#ifdef DIOCGPART
|
||||
if (size == 0) {
|
||||
struct partinfo pi;
|
||||
if (ioctl(fd, DIOCGPART, &pi) == 0) {
|
||||
size = pi.media_size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(DKIOCGETBLOCKCOUNT) && defined(DKIOCGETBLOCKSIZE)
|
||||
if (size == 0) {
|
||||
uint64_t sectors = 0;
|
||||
uint32_t sector_size = 0;
|
||||
|
||||
if (ioctl(fd, DKIOCGETBLOCKCOUNT, §ors) == 0
|
||||
&& ioctl(fd, DKIOCGETBLOCKSIZE, §or_size) == 0) {
|
||||
size = sectors * sector_size;
|
||||
} else {
|
||||
size = lseek(fd, 0LL, SEEK_END);
|
||||
if (size < 0) {
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
size = lseek(fd, 0LL, SEEK_END);
|
||||
#endif
|
||||
if (size == 0) {
|
||||
size = lseek(fd, 0LL, SEEK_END);
|
||||
}
|
||||
if (size < 0) {
|
||||
return -errno;
|
||||
}
|
||||
#endif
|
||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
switch(s->type) {
|
||||
case FTYPE_CD:
|
||||
@ -3003,6 +3046,7 @@ static BlockStatsSpecific *raw_get_specific_stats(BlockDriverState *bs)
|
||||
return stats;
|
||||
}
|
||||
|
||||
#if defined(HAVE_HOST_BLOCK_DEVICE)
|
||||
static BlockStatsSpecific *hdev_get_specific_stats(BlockDriverState *bs)
|
||||
{
|
||||
BlockStatsSpecific *stats = g_new(BlockStatsSpecific, 1);
|
||||
@ -3012,6 +3056,7 @@ static BlockStatsSpecific *hdev_get_specific_stats(BlockDriverState *bs)
|
||||
|
||||
return stats;
|
||||
}
|
||||
#endif /* HAVE_HOST_BLOCK_DEVICE */
|
||||
|
||||
static QemuOptsList raw_create_opts = {
|
||||
.name = "raw-create-opts",
|
||||
@ -3227,6 +3272,8 @@ BlockDriver bdrv_file = {
|
||||
/***********************************************/
|
||||
/* host device */
|
||||
|
||||
#if defined(HAVE_HOST_BLOCK_DEVICE)
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
static kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath,
|
||||
CFIndex maxPathSize, int flags);
|
||||
@ -3519,16 +3566,6 @@ hdev_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
|
||||
}
|
||||
#endif /* linux */
|
||||
|
||||
static int fd_open(BlockDriverState *bs)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
/* this is just to ensure s->fd is sane (its called by io ops) */
|
||||
if (s->fd >= 0)
|
||||
return 0;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static coroutine_fn int
|
||||
hdev_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes)
|
||||
{
|
||||
@ -3852,6 +3889,8 @@ static BlockDriver bdrv_host_cdrom = {
|
||||
};
|
||||
#endif /* __FreeBSD__ */
|
||||
|
||||
#endif /* HAVE_HOST_BLOCK_DEVICE */
|
||||
|
||||
static void bdrv_file_init(void)
|
||||
{
|
||||
/*
|
||||
@ -3859,6 +3898,7 @@ static void bdrv_file_init(void)
|
||||
* registered last will get probed first.
|
||||
*/
|
||||
bdrv_register(&bdrv_file);
|
||||
#if defined(HAVE_HOST_BLOCK_DEVICE)
|
||||
bdrv_register(&bdrv_host_device);
|
||||
#ifdef __linux__
|
||||
bdrv_register(&bdrv_host_cdrom);
|
||||
@ -3866,6 +3906,7 @@ static void bdrv_file_init(void)
|
||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
bdrv_register(&bdrv_host_cdrom);
|
||||
#endif
|
||||
#endif /* HAVE_HOST_BLOCK_DEVICE */
|
||||
}
|
||||
|
||||
block_init(bdrv_file_init);
|
||||
|
25
block/io.c
25
block/io.c
@ -30,6 +30,7 @@
|
||||
#include "block/blockjob_int.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/coroutines.h"
|
||||
#include "block/write-threshold.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
@ -126,6 +127,8 @@ static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src)
|
||||
{
|
||||
dst->opt_transfer = MAX(dst->opt_transfer, src->opt_transfer);
|
||||
dst->max_transfer = MIN_NON_ZERO(dst->max_transfer, src->max_transfer);
|
||||
dst->max_hw_transfer = MIN_NON_ZERO(dst->max_hw_transfer,
|
||||
src->max_hw_transfer);
|
||||
dst->opt_mem_alignment = MAX(dst->opt_mem_alignment,
|
||||
src->opt_mem_alignment);
|
||||
dst->min_mem_alignment = MAX(dst->min_mem_alignment,
|
||||
@ -1972,7 +1975,7 @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, int64_t bytes,
|
||||
|
||||
bdrv_check_request(offset, bytes, &error_abort);
|
||||
|
||||
if (bs->read_only) {
|
||||
if (bdrv_is_read_only(bs)) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
@ -2008,8 +2011,8 @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, int64_t bytes,
|
||||
} else {
|
||||
assert(child->perm & BLK_PERM_WRITE);
|
||||
}
|
||||
return notifier_with_return_list_notify(&bs->before_write_notifiers,
|
||||
req);
|
||||
bdrv_write_threshold_check_write(bs, offset, bytes);
|
||||
return 0;
|
||||
case BDRV_TRACKED_TRUNCATE:
|
||||
assert(child->perm & BLK_PERM_RESIZE);
|
||||
return 0;
|
||||
@ -3164,12 +3167,6 @@ bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov)
|
||||
return true;
|
||||
}
|
||||
|
||||
void bdrv_add_before_write_notifier(BlockDriverState *bs,
|
||||
NotifierWithReturn *notifier)
|
||||
{
|
||||
notifier_with_return_list_add(&bs->before_write_notifiers, notifier);
|
||||
}
|
||||
|
||||
void bdrv_io_plug(BlockDriverState *bs)
|
||||
{
|
||||
BdrvChild *child;
|
||||
@ -3395,6 +3392,11 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact,
|
||||
return old_size;
|
||||
}
|
||||
|
||||
if (bdrv_is_read_only(bs)) {
|
||||
error_setg(errp, "Image is read-only");
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (offset > old_size) {
|
||||
new_bytes = offset - old_size;
|
||||
} else {
|
||||
@ -3411,11 +3413,6 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact,
|
||||
if (new_bytes) {
|
||||
bdrv_make_request_serialising(&req, 1);
|
||||
}
|
||||
if (bs->read_only) {
|
||||
error_setg(errp, "Image is read-only");
|
||||
ret = -EACCES;
|
||||
goto out;
|
||||
}
|
||||
ret = bdrv_co_write_req_prepare(child, offset - new_bytes, new_bytes, &req,
|
||||
0);
|
||||
if (ret < 0) {
|
||||
|
@ -13,6 +13,7 @@ block_ss.add(files(
|
||||
'commit.c',
|
||||
'copy-on-read.c',
|
||||
'preallocate.c',
|
||||
'progress_meter.c',
|
||||
'create.c',
|
||||
'crypto.c',
|
||||
'dirty-bitmap.c',
|
||||
@ -64,7 +65,6 @@ block_ss.add(when: 'CONFIG_POSIX', if_true: [files('file-posix.c'), coref, iokit
|
||||
block_ss.add(when: libiscsi, if_true: files('iscsi-opts.c'))
|
||||
block_ss.add(when: 'CONFIG_LINUX', if_true: files('nvme.c'))
|
||||
block_ss.add(when: 'CONFIG_REPLICATION', if_true: files('replication.c'))
|
||||
block_ss.add(when: 'CONFIG_SHEEPDOG', if_true: files('sheepdog.c'))
|
||||
block_ss.add(when: ['CONFIG_LINUX_AIO', libaio], if_true: files('linux-aio.c'))
|
||||
block_ss.add(when: ['CONFIG_LINUX_IO_URING', linux_io_uring], if_true: files('io_uring.c'))
|
||||
|
||||
@ -72,19 +72,19 @@ block_modules = {}
|
||||
|
||||
modsrc = []
|
||||
foreach m : [
|
||||
[curl, 'curl', [curl, glib], 'curl.c'],
|
||||
[glusterfs, 'gluster', glusterfs, 'gluster.c'],
|
||||
[libiscsi, 'iscsi', libiscsi, 'iscsi.c'],
|
||||
[libnfs, 'nfs', libnfs, 'nfs.c'],
|
||||
[libssh, 'ssh', libssh, 'ssh.c'],
|
||||
[rbd, 'rbd', rbd, 'rbd.c'],
|
||||
[curl, 'curl', files('curl.c')],
|
||||
[glusterfs, 'gluster', files('gluster.c')],
|
||||
[libiscsi, 'iscsi', [files('iscsi.c'), libm]],
|
||||
[libnfs, 'nfs', files('nfs.c')],
|
||||
[libssh, 'ssh', files('ssh.c')],
|
||||
[rbd, 'rbd', files('rbd.c')],
|
||||
]
|
||||
if m[0].found()
|
||||
if enable_modules
|
||||
modsrc += files(m[3])
|
||||
endif
|
||||
module_ss = ss.source_set()
|
||||
module_ss.add(when: m[2], if_true: files(m[3]))
|
||||
module_ss.add(when: m[0], if_true: m[2])
|
||||
if enable_modules
|
||||
modsrc += module_ss.all_sources()
|
||||
endif
|
||||
block_modules += {m[1] : module_ss}
|
||||
endif
|
||||
endforeach
|
||||
|
@ -1178,12 +1178,14 @@ static bool mirror_drained_poll(BlockJob *job)
|
||||
return !!s->in_flight;
|
||||
}
|
||||
|
||||
static void mirror_cancel(Job *job)
|
||||
static void mirror_cancel(Job *job, bool force)
|
||||
{
|
||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
|
||||
BlockDriverState *target = blk_bs(s->target);
|
||||
|
||||
bdrv_cancel_in_flight(target);
|
||||
if (force || !job_is_ready(job)) {
|
||||
bdrv_cancel_in_flight(target);
|
||||
}
|
||||
}
|
||||
|
||||
static const BlockJobDriver mirror_job_driver = {
|
||||
|
@ -557,8 +557,10 @@ void hmp_eject(Monitor *mon, const QDict *qdict)
|
||||
|
||||
void hmp_qemu_io(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
BlockBackend *blk = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
BlockBackend *local_blk = NULL;
|
||||
AioContext *ctx = NULL;
|
||||
bool qdev = qdict_get_try_bool(qdict, "qdev", false);
|
||||
const char *device = qdict_get_str(qdict, "device");
|
||||
const char *command = qdict_get_str(qdict, "command");
|
||||
@ -573,20 +575,24 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
|
||||
} else {
|
||||
blk = blk_by_name(device);
|
||||
if (!blk) {
|
||||
BlockDriverState *bs = bdrv_lookup_bs(NULL, device, &err);
|
||||
if (bs) {
|
||||
blk = local_blk = blk_new(bdrv_get_aio_context(bs),
|
||||
0, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs, &err);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
bs = bdrv_lookup_bs(NULL, device, &err);
|
||||
if (!bs) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx = blk ? blk_get_aio_context(blk) : bdrv_get_aio_context(bs);
|
||||
aio_context_acquire(ctx);
|
||||
|
||||
if (bs) {
|
||||
blk = local_blk = blk_new(bdrv_get_aio_context(bs), 0, BLK_PERM_ALL);
|
||||
ret = blk_insert_bs(blk, bs, &err);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Notably absent: Proper permission management. This is sad, but it seems
|
||||
* almost impossible to achieve without changing the semantics and thereby
|
||||
@ -616,6 +622,11 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
|
||||
|
||||
fail:
|
||||
blk_unref(local_blk);
|
||||
|
||||
if (ctx) {
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
|
||||
hmp_handle_error(mon, err);
|
||||
}
|
||||
|
||||
|
607
block/nbd.c
607
block/nbd.c
@ -44,6 +44,7 @@
|
||||
#include "block/qdict.h"
|
||||
#include "block/nbd.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/coroutines.h"
|
||||
|
||||
#include "qemu/yank.h"
|
||||
|
||||
@ -66,63 +67,19 @@ typedef enum NBDClientState {
|
||||
NBD_CLIENT_QUIT
|
||||
} NBDClientState;
|
||||
|
||||
typedef enum NBDConnectThreadState {
|
||||
/* No thread, no pending results */
|
||||
CONNECT_THREAD_NONE,
|
||||
|
||||
/* Thread is running, no results for now */
|
||||
CONNECT_THREAD_RUNNING,
|
||||
|
||||
/*
|
||||
* Thread is running, but requestor exited. Thread should close
|
||||
* the new socket and free the connect state on exit.
|
||||
*/
|
||||
CONNECT_THREAD_RUNNING_DETACHED,
|
||||
|
||||
/* Thread finished, results are stored in a state */
|
||||
CONNECT_THREAD_FAIL,
|
||||
CONNECT_THREAD_SUCCESS
|
||||
} NBDConnectThreadState;
|
||||
|
||||
typedef struct NBDConnectThread {
|
||||
/* Initialization constants */
|
||||
SocketAddress *saddr; /* address to connect to */
|
||||
/*
|
||||
* Bottom half to schedule on completion. Scheduled only if bh_ctx is not
|
||||
* NULL
|
||||
*/
|
||||
QEMUBHFunc *bh_func;
|
||||
void *bh_opaque;
|
||||
|
||||
/*
|
||||
* Result of last attempt. Valid in FAIL and SUCCESS states.
|
||||
* If you want to steal error, don't forget to set pointer to NULL.
|
||||
*/
|
||||
QIOChannelSocket *sioc;
|
||||
Error *err;
|
||||
|
||||
/* state and bh_ctx are protected by mutex */
|
||||
QemuMutex mutex;
|
||||
NBDConnectThreadState state; /* current state of the thread */
|
||||
AioContext *bh_ctx; /* where to schedule bh (NULL means don't schedule) */
|
||||
} NBDConnectThread;
|
||||
|
||||
typedef struct BDRVNBDState {
|
||||
QIOChannelSocket *sioc; /* The master data channel */
|
||||
QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */
|
||||
QIOChannel *ioc; /* The current I/O channel */
|
||||
NBDExportInfo info;
|
||||
|
||||
CoMutex send_mutex;
|
||||
CoQueue free_sema;
|
||||
Coroutine *connection_co;
|
||||
Coroutine *teardown_co;
|
||||
QemuCoSleepState *connection_co_sleep_ns_state;
|
||||
QemuCoSleep reconnect_sleep;
|
||||
bool drained;
|
||||
bool wait_drained_end;
|
||||
int in_flight;
|
||||
NBDClientState state;
|
||||
int connect_status;
|
||||
Error *connect_err;
|
||||
bool wait_in_flight;
|
||||
|
||||
QEMUTimer *reconnect_delay_timer;
|
||||
@ -140,20 +97,20 @@ typedef struct BDRVNBDState {
|
||||
char *x_dirty_bitmap;
|
||||
bool alloc_depth;
|
||||
|
||||
bool wait_connect;
|
||||
NBDConnectThread *connect_thread;
|
||||
NBDClientConnection *conn;
|
||||
} BDRVNBDState;
|
||||
|
||||
static int nbd_establish_connection(BlockDriverState *bs, SocketAddress *saddr,
|
||||
Error **errp);
|
||||
static int nbd_co_establish_connection(BlockDriverState *bs, Error **errp);
|
||||
static void nbd_co_establish_connection_cancel(BlockDriverState *bs,
|
||||
bool detach);
|
||||
static int nbd_client_handshake(BlockDriverState *bs, Error **errp);
|
||||
static void nbd_yank(void *opaque);
|
||||
|
||||
static void nbd_clear_bdrvstate(BDRVNBDState *s)
|
||||
static void nbd_clear_bdrvstate(BlockDriverState *bs)
|
||||
{
|
||||
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
|
||||
|
||||
nbd_client_connection_release(s->conn);
|
||||
s->conn = NULL;
|
||||
|
||||
yank_unregister_instance(BLOCKDEV_YANK_INSTANCE(bs->node_name));
|
||||
|
||||
object_unref(OBJECT(s->tlscreds));
|
||||
qapi_free_SocketAddress(s->saddr);
|
||||
s->saddr = NULL;
|
||||
@ -165,15 +122,20 @@ static void nbd_clear_bdrvstate(BDRVNBDState *s)
|
||||
s->x_dirty_bitmap = NULL;
|
||||
}
|
||||
|
||||
static bool nbd_client_connected(BDRVNBDState *s)
|
||||
{
|
||||
return qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTED;
|
||||
}
|
||||
|
||||
static void nbd_channel_error(BDRVNBDState *s, int ret)
|
||||
{
|
||||
if (ret == -EIO) {
|
||||
if (qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTED) {
|
||||
if (nbd_client_connected(s)) {
|
||||
s->state = s->reconnect_delay ? NBD_CLIENT_CONNECTING_WAIT :
|
||||
NBD_CLIENT_CONNECTING_NOWAIT;
|
||||
}
|
||||
} else {
|
||||
if (qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTED) {
|
||||
if (nbd_client_connected(s)) {
|
||||
qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
|
||||
}
|
||||
s->state = NBD_CLIENT_QUIT;
|
||||
@ -188,6 +150,7 @@ static void nbd_recv_coroutines_wake_all(BDRVNBDState *s)
|
||||
NBDClientRequest *req = &s->requests[i];
|
||||
|
||||
if (req->coroutine && req->receiving) {
|
||||
req->receiving = false;
|
||||
aio_co_wake(req->coroutine);
|
||||
}
|
||||
}
|
||||
@ -271,7 +234,7 @@ static void nbd_client_attach_aio_context(BlockDriverState *bs,
|
||||
* s->connection_co is either yielded from nbd_receive_reply or from
|
||||
* nbd_co_reconnect_loop()
|
||||
*/
|
||||
if (qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTED) {
|
||||
if (nbd_client_connected(s)) {
|
||||
qio_channel_attach_aio_context(QIO_CHANNEL(s->ioc), new_context);
|
||||
}
|
||||
|
||||
@ -289,11 +252,9 @@ static void coroutine_fn nbd_client_co_drain_begin(BlockDriverState *bs)
|
||||
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
|
||||
|
||||
s->drained = true;
|
||||
if (s->connection_co_sleep_ns_state) {
|
||||
qemu_co_sleep_wake(s->connection_co_sleep_ns_state);
|
||||
}
|
||||
qemu_co_sleep_wake(&s->reconnect_sleep);
|
||||
|
||||
nbd_co_establish_connection_cancel(bs, false);
|
||||
nbd_co_establish_connection_cancel(s->conn);
|
||||
|
||||
reconnect_delay_timer_del(s);
|
||||
|
||||
@ -322,18 +283,12 @@ static void nbd_teardown_connection(BlockDriverState *bs)
|
||||
if (s->ioc) {
|
||||
/* finish any pending coroutines */
|
||||
qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
|
||||
} else if (s->sioc) {
|
||||
/* abort negotiation */
|
||||
qio_channel_shutdown(QIO_CHANNEL(s->sioc), QIO_CHANNEL_SHUTDOWN_BOTH,
|
||||
NULL);
|
||||
}
|
||||
|
||||
s->state = NBD_CLIENT_QUIT;
|
||||
if (s->connection_co) {
|
||||
if (s->connection_co_sleep_ns_state) {
|
||||
qemu_co_sleep_wake(s->connection_co_sleep_ns_state);
|
||||
}
|
||||
nbd_co_establish_connection_cancel(bs, true);
|
||||
qemu_co_sleep_wake(&s->reconnect_sleep);
|
||||
nbd_co_establish_connection_cancel(s->conn);
|
||||
}
|
||||
if (qemu_in_coroutine()) {
|
||||
s->teardown_co = qemu_coroutine_self();
|
||||
@ -358,239 +313,95 @@ static bool nbd_client_connecting_wait(BDRVNBDState *s)
|
||||
return qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTING_WAIT;
|
||||
}
|
||||
|
||||
static void connect_bh(void *opaque)
|
||||
{
|
||||
BDRVNBDState *state = opaque;
|
||||
|
||||
assert(state->wait_connect);
|
||||
state->wait_connect = false;
|
||||
aio_co_wake(state->connection_co);
|
||||
}
|
||||
|
||||
static void nbd_init_connect_thread(BDRVNBDState *s)
|
||||
{
|
||||
s->connect_thread = g_new(NBDConnectThread, 1);
|
||||
|
||||
*s->connect_thread = (NBDConnectThread) {
|
||||
.saddr = QAPI_CLONE(SocketAddress, s->saddr),
|
||||
.state = CONNECT_THREAD_NONE,
|
||||
.bh_func = connect_bh,
|
||||
.bh_opaque = s,
|
||||
};
|
||||
|
||||
qemu_mutex_init(&s->connect_thread->mutex);
|
||||
}
|
||||
|
||||
static void nbd_free_connect_thread(NBDConnectThread *thr)
|
||||
{
|
||||
if (thr->sioc) {
|
||||
qio_channel_close(QIO_CHANNEL(thr->sioc), NULL);
|
||||
}
|
||||
error_free(thr->err);
|
||||
qapi_free_SocketAddress(thr->saddr);
|
||||
g_free(thr);
|
||||
}
|
||||
|
||||
static void *connect_thread_func(void *opaque)
|
||||
{
|
||||
NBDConnectThread *thr = opaque;
|
||||
int ret;
|
||||
bool do_free = false;
|
||||
|
||||
thr->sioc = qio_channel_socket_new();
|
||||
|
||||
error_free(thr->err);
|
||||
thr->err = NULL;
|
||||
ret = qio_channel_socket_connect_sync(thr->sioc, thr->saddr, &thr->err);
|
||||
if (ret < 0) {
|
||||
object_unref(OBJECT(thr->sioc));
|
||||
thr->sioc = NULL;
|
||||
}
|
||||
|
||||
qemu_mutex_lock(&thr->mutex);
|
||||
|
||||
switch (thr->state) {
|
||||
case CONNECT_THREAD_RUNNING:
|
||||
thr->state = ret < 0 ? CONNECT_THREAD_FAIL : CONNECT_THREAD_SUCCESS;
|
||||
if (thr->bh_ctx) {
|
||||
aio_bh_schedule_oneshot(thr->bh_ctx, thr->bh_func, thr->bh_opaque);
|
||||
|
||||
/* play safe, don't reuse bh_ctx on further connection attempts */
|
||||
thr->bh_ctx = NULL;
|
||||
}
|
||||
break;
|
||||
case CONNECT_THREAD_RUNNING_DETACHED:
|
||||
do_free = true;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
qemu_mutex_unlock(&thr->mutex);
|
||||
|
||||
if (do_free) {
|
||||
nbd_free_connect_thread(thr);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
nbd_co_establish_connection(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
QemuThread thread;
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
NBDConnectThread *thr = s->connect_thread;
|
||||
|
||||
if (!thr) {
|
||||
/* detached */
|
||||
return -1;
|
||||
}
|
||||
|
||||
qemu_mutex_lock(&thr->mutex);
|
||||
|
||||
switch (thr->state) {
|
||||
case CONNECT_THREAD_FAIL:
|
||||
case CONNECT_THREAD_NONE:
|
||||
error_free(thr->err);
|
||||
thr->err = NULL;
|
||||
thr->state = CONNECT_THREAD_RUNNING;
|
||||
qemu_thread_create(&thread, "nbd-connect",
|
||||
connect_thread_func, thr, QEMU_THREAD_DETACHED);
|
||||
break;
|
||||
case CONNECT_THREAD_SUCCESS:
|
||||
/* Previous attempt finally succeeded in background */
|
||||
thr->state = CONNECT_THREAD_NONE;
|
||||
s->sioc = thr->sioc;
|
||||
thr->sioc = NULL;
|
||||
yank_register_function(BLOCKDEV_YANK_INSTANCE(bs->node_name),
|
||||
nbd_yank, bs);
|
||||
qemu_mutex_unlock(&thr->mutex);
|
||||
return 0;
|
||||
case CONNECT_THREAD_RUNNING:
|
||||
/* Already running, will wait */
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
thr->bh_ctx = qemu_get_current_aio_context();
|
||||
|
||||
qemu_mutex_unlock(&thr->mutex);
|
||||
|
||||
|
||||
/*
|
||||
* We are going to wait for connect-thread finish, but
|
||||
* nbd_client_co_drain_begin() can interrupt.
|
||||
*
|
||||
* Note that wait_connect variable is not visible for connect-thread. It
|
||||
* doesn't need mutex protection, it used only inside home aio context of
|
||||
* bs.
|
||||
*/
|
||||
s->wait_connect = true;
|
||||
qemu_coroutine_yield();
|
||||
|
||||
if (!s->connect_thread) {
|
||||
/* detached */
|
||||
return -1;
|
||||
}
|
||||
assert(thr == s->connect_thread);
|
||||
|
||||
qemu_mutex_lock(&thr->mutex);
|
||||
|
||||
switch (thr->state) {
|
||||
case CONNECT_THREAD_SUCCESS:
|
||||
case CONNECT_THREAD_FAIL:
|
||||
thr->state = CONNECT_THREAD_NONE;
|
||||
error_propagate(errp, thr->err);
|
||||
thr->err = NULL;
|
||||
s->sioc = thr->sioc;
|
||||
thr->sioc = NULL;
|
||||
if (s->sioc) {
|
||||
yank_register_function(BLOCKDEV_YANK_INSTANCE(bs->node_name),
|
||||
nbd_yank, bs);
|
||||
}
|
||||
ret = (s->sioc ? 0 : -1);
|
||||
break;
|
||||
case CONNECT_THREAD_RUNNING:
|
||||
case CONNECT_THREAD_RUNNING_DETACHED:
|
||||
/*
|
||||
* Obviously, drained section wants to start. Report the attempt as
|
||||
* failed. Still connect thread is executing in background, and its
|
||||
* result may be used for next connection attempt.
|
||||
*/
|
||||
ret = -1;
|
||||
error_setg(errp, "Connection attempt cancelled by other operation");
|
||||
break;
|
||||
|
||||
case CONNECT_THREAD_NONE:
|
||||
/*
|
||||
* Impossible. We've seen this thread running. So it should be
|
||||
* running or at least give some results.
|
||||
*/
|
||||
abort();
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
qemu_mutex_unlock(&thr->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* nbd_co_establish_connection_cancel
|
||||
* Cancel nbd_co_establish_connection asynchronously: it will finish soon, to
|
||||
* allow drained section to begin.
|
||||
*
|
||||
* If detach is true, also cleanup the state (or if thread is running, move it
|
||||
* to CONNECT_THREAD_RUNNING_DETACHED state). s->connect_thread becomes NULL if
|
||||
* detach is true.
|
||||
* Update @bs with information learned during a completed negotiation process.
|
||||
* Return failure if the server's advertised options are incompatible with the
|
||||
* client's needs.
|
||||
*/
|
||||
static void nbd_co_establish_connection_cancel(BlockDriverState *bs,
|
||||
bool detach)
|
||||
static int nbd_handle_updated_info(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
NBDConnectThread *thr = s->connect_thread;
|
||||
bool wake = false;
|
||||
bool do_free = false;
|
||||
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
|
||||
int ret;
|
||||
|
||||
qemu_mutex_lock(&thr->mutex);
|
||||
|
||||
if (thr->state == CONNECT_THREAD_RUNNING) {
|
||||
/* We can cancel only in running state, when bh is not yet scheduled */
|
||||
thr->bh_ctx = NULL;
|
||||
if (s->wait_connect) {
|
||||
s->wait_connect = false;
|
||||
wake = true;
|
||||
if (s->x_dirty_bitmap) {
|
||||
if (!s->info.base_allocation) {
|
||||
error_setg(errp, "requested x-dirty-bitmap %s not found",
|
||||
s->x_dirty_bitmap);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (detach) {
|
||||
thr->state = CONNECT_THREAD_RUNNING_DETACHED;
|
||||
s->connect_thread = NULL;
|
||||
if (strcmp(s->x_dirty_bitmap, "qemu:allocation-depth") == 0) {
|
||||
s->alloc_depth = true;
|
||||
}
|
||||
} else if (detach) {
|
||||
do_free = true;
|
||||
}
|
||||
|
||||
qemu_mutex_unlock(&thr->mutex);
|
||||
|
||||
if (do_free) {
|
||||
nbd_free_connect_thread(thr);
|
||||
s->connect_thread = NULL;
|
||||
if (s->info.flags & NBD_FLAG_READ_ONLY) {
|
||||
ret = bdrv_apply_auto_read_only(bs, "NBD export is read-only", errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (wake) {
|
||||
aio_co_wake(s->connection_co);
|
||||
if (s->info.flags & NBD_FLAG_SEND_FUA) {
|
||||
bs->supported_write_flags = BDRV_REQ_FUA;
|
||||
bs->supported_zero_flags |= BDRV_REQ_FUA;
|
||||
}
|
||||
|
||||
if (s->info.flags & NBD_FLAG_SEND_WRITE_ZEROES) {
|
||||
bs->supported_zero_flags |= BDRV_REQ_MAY_UNMAP;
|
||||
if (s->info.flags & NBD_FLAG_SEND_FAST_ZERO) {
|
||||
bs->supported_zero_flags |= BDRV_REQ_NO_FALLBACK;
|
||||
}
|
||||
}
|
||||
|
||||
trace_nbd_client_handshake_success(s->export);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
|
||||
int ret;
|
||||
|
||||
assert(!s->ioc);
|
||||
|
||||
s->ioc = nbd_co_establish_connection(s->conn, &s->info, true, errp);
|
||||
if (!s->ioc) {
|
||||
return -ECONNREFUSED;
|
||||
}
|
||||
|
||||
ret = nbd_handle_updated_info(s->bs, NULL);
|
||||
if (ret < 0) {
|
||||
/*
|
||||
* We have connected, but must fail for other reasons.
|
||||
* Send NBD_CMD_DISC as a courtesy to the server.
|
||||
*/
|
||||
NBDRequest request = { .type = NBD_CMD_DISC };
|
||||
|
||||
nbd_send_request(s->ioc, &request);
|
||||
|
||||
object_unref(OBJECT(s->ioc));
|
||||
s->ioc = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
qio_channel_set_blocking(s->ioc, false, NULL);
|
||||
qio_channel_attach_aio_context(s->ioc, bdrv_get_aio_context(bs));
|
||||
|
||||
yank_register_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name), nbd_yank,
|
||||
bs);
|
||||
|
||||
/* successfully connected */
|
||||
s->state = NBD_CLIENT_CONNECTED;
|
||||
qemu_co_queue_restart_all(&s->free_sema);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s)
|
||||
{
|
||||
int ret;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (!nbd_client_connecting(s)) {
|
||||
return;
|
||||
}
|
||||
@ -624,44 +435,11 @@ static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s)
|
||||
qio_channel_detach_aio_context(QIO_CHANNEL(s->ioc));
|
||||
yank_unregister_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name),
|
||||
nbd_yank, s->bs);
|
||||
object_unref(OBJECT(s->sioc));
|
||||
s->sioc = NULL;
|
||||
object_unref(OBJECT(s->ioc));
|
||||
s->ioc = NULL;
|
||||
}
|
||||
|
||||
if (nbd_co_establish_connection(s->bs, &local_err) < 0) {
|
||||
ret = -ECONNREFUSED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bdrv_dec_in_flight(s->bs);
|
||||
|
||||
ret = nbd_client_handshake(s->bs, &local_err);
|
||||
|
||||
if (s->drained) {
|
||||
s->wait_drained_end = true;
|
||||
while (s->drained) {
|
||||
/*
|
||||
* We may be entered once from nbd_client_attach_aio_context_bh
|
||||
* and then from nbd_client_co_drain_end. So here is a loop.
|
||||
*/
|
||||
qemu_coroutine_yield();
|
||||
}
|
||||
}
|
||||
bdrv_inc_in_flight(s->bs);
|
||||
|
||||
out:
|
||||
s->connect_status = ret;
|
||||
error_free(s->connect_err);
|
||||
s->connect_err = NULL;
|
||||
error_propagate(&s->connect_err, local_err);
|
||||
|
||||
if (ret >= 0) {
|
||||
/* successfully connected */
|
||||
s->state = NBD_CLIENT_CONNECTED;
|
||||
qemu_co_queue_restart_all(&s->free_sema);
|
||||
}
|
||||
nbd_co_do_establish_connection(s->bs, NULL);
|
||||
}
|
||||
|
||||
static coroutine_fn void nbd_co_reconnect_loop(BDRVNBDState *s)
|
||||
@ -689,8 +467,8 @@ static coroutine_fn void nbd_co_reconnect_loop(BDRVNBDState *s)
|
||||
}
|
||||
bdrv_inc_in_flight(s->bs);
|
||||
} else {
|
||||
qemu_co_sleep_ns_wakeable(QEMU_CLOCK_REALTIME, timeout,
|
||||
&s->connection_co_sleep_ns_state);
|
||||
qemu_co_sleep_ns_wakeable(&s->reconnect_sleep,
|
||||
QEMU_CLOCK_REALTIME, timeout);
|
||||
if (s->drained) {
|
||||
continue;
|
||||
}
|
||||
@ -727,7 +505,7 @@ static coroutine_fn void nbd_connection_entry(void *opaque)
|
||||
nbd_co_reconnect_loop(s);
|
||||
}
|
||||
|
||||
if (qatomic_load_acquire(&s->state) != NBD_CLIENT_CONNECTED) {
|
||||
if (!nbd_client_connected(s)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -771,6 +549,7 @@ static coroutine_fn void nbd_connection_entry(void *opaque)
|
||||
* connection_co happens through a bottom half, which can only
|
||||
* run after we yield.
|
||||
*/
|
||||
s->requests[i].receiving = false;
|
||||
aio_co_wake(s->requests[i].coroutine);
|
||||
qemu_coroutine_yield();
|
||||
}
|
||||
@ -784,8 +563,6 @@ static coroutine_fn void nbd_connection_entry(void *opaque)
|
||||
qio_channel_detach_aio_context(QIO_CHANNEL(s->ioc));
|
||||
yank_unregister_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name),
|
||||
nbd_yank, s->bs);
|
||||
object_unref(OBJECT(s->sioc));
|
||||
s->sioc = NULL;
|
||||
object_unref(OBJECT(s->ioc));
|
||||
s->ioc = NULL;
|
||||
}
|
||||
@ -808,7 +585,7 @@ static int nbd_co_send_request(BlockDriverState *bs,
|
||||
qemu_co_queue_wait(&s->free_sema, &s->send_mutex);
|
||||
}
|
||||
|
||||
if (qatomic_load_acquire(&s->state) != NBD_CLIENT_CONNECTED) {
|
||||
if (!nbd_client_connected(s)) {
|
||||
rc = -EIO;
|
||||
goto err;
|
||||
}
|
||||
@ -835,8 +612,7 @@ static int nbd_co_send_request(BlockDriverState *bs,
|
||||
if (qiov) {
|
||||
qio_channel_set_cork(s->ioc, true);
|
||||
rc = nbd_send_request(s->ioc, request);
|
||||
if (qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTED &&
|
||||
rc >= 0) {
|
||||
if (nbd_client_connected(s) && rc >= 0) {
|
||||
if (qio_channel_writev_all(s->ioc, qiov->iov, qiov->niov,
|
||||
NULL) < 0) {
|
||||
rc = -EIO;
|
||||
@ -1160,8 +936,8 @@ static coroutine_fn int nbd_co_do_receive_one_chunk(
|
||||
/* Wait until we're woken up by nbd_connection_entry. */
|
||||
s->requests[i].receiving = true;
|
||||
qemu_coroutine_yield();
|
||||
s->requests[i].receiving = false;
|
||||
if (qatomic_load_acquire(&s->state) != NBD_CLIENT_CONNECTED) {
|
||||
assert(!s->requests[i].receiving);
|
||||
if (!nbd_client_connected(s)) {
|
||||
error_setg(errp, "Connection closed");
|
||||
return -EIO;
|
||||
}
|
||||
@ -1320,7 +1096,7 @@ static bool nbd_reply_chunk_iter_receive(BDRVNBDState *s,
|
||||
NBDReply local_reply;
|
||||
NBDStructuredReplyChunk *chunk;
|
||||
Error *local_err = NULL;
|
||||
if (qatomic_load_acquire(&s->state) != NBD_CLIENT_CONNECTED) {
|
||||
if (!nbd_client_connected(s)) {
|
||||
error_setg(&local_err, "Connection closed");
|
||||
nbd_iter_channel_error(iter, -EIO, &local_err);
|
||||
goto break_loop;
|
||||
@ -1345,8 +1121,7 @@ static bool nbd_reply_chunk_iter_receive(BDRVNBDState *s,
|
||||
}
|
||||
|
||||
/* Do not execute the body of NBD_FOREACH_REPLY_CHUNK for simple reply. */
|
||||
if (nbd_reply_is_simple(reply) ||
|
||||
qatomic_load_acquire(&s->state) != NBD_CLIENT_CONNECTED) {
|
||||
if (nbd_reply_is_simple(reply) || !nbd_client_connected(s)) {
|
||||
goto break_loop;
|
||||
}
|
||||
|
||||
@ -1784,7 +1559,7 @@ static void nbd_yank(void *opaque)
|
||||
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
|
||||
|
||||
qatomic_store_release(&s->state, NBD_CLIENT_QUIT);
|
||||
qio_channel_shutdown(QIO_CHANNEL(s->sioc), QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
|
||||
qio_channel_shutdown(QIO_CHANNEL(s->ioc), QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
|
||||
}
|
||||
|
||||
static void nbd_client_close(BlockDriverState *bs)
|
||||
@ -1799,111 +1574,6 @@ static void nbd_client_close(BlockDriverState *bs)
|
||||
nbd_teardown_connection(bs);
|
||||
}
|
||||
|
||||
static int nbd_establish_connection(BlockDriverState *bs,
|
||||
SocketAddress *saddr,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
|
||||
|
||||
s->sioc = qio_channel_socket_new();
|
||||
qio_channel_set_name(QIO_CHANNEL(s->sioc), "nbd-client");
|
||||
|
||||
qio_channel_socket_connect_sync(s->sioc, saddr, errp);
|
||||
if (*errp) {
|
||||
object_unref(OBJECT(s->sioc));
|
||||
s->sioc = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
yank_register_function(BLOCKDEV_YANK_INSTANCE(bs->node_name), nbd_yank, bs);
|
||||
qio_channel_set_delay(QIO_CHANNEL(s->sioc), false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* nbd_client_handshake takes ownership on s->sioc. On failure it's unref'ed. */
|
||||
static int nbd_client_handshake(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
int ret;
|
||||
|
||||
trace_nbd_client_handshake(s->export);
|
||||
qio_channel_set_blocking(QIO_CHANNEL(s->sioc), false, NULL);
|
||||
qio_channel_attach_aio_context(QIO_CHANNEL(s->sioc), aio_context);
|
||||
|
||||
s->info.request_sizes = true;
|
||||
s->info.structured_reply = true;
|
||||
s->info.base_allocation = true;
|
||||
s->info.x_dirty_bitmap = g_strdup(s->x_dirty_bitmap);
|
||||
s->info.name = g_strdup(s->export ?: "");
|
||||
ret = nbd_receive_negotiate(aio_context, QIO_CHANNEL(s->sioc), s->tlscreds,
|
||||
s->hostname, &s->ioc, &s->info, errp);
|
||||
g_free(s->info.x_dirty_bitmap);
|
||||
g_free(s->info.name);
|
||||
if (ret < 0) {
|
||||
yank_unregister_function(BLOCKDEV_YANK_INSTANCE(bs->node_name),
|
||||
nbd_yank, bs);
|
||||
object_unref(OBJECT(s->sioc));
|
||||
s->sioc = NULL;
|
||||
return ret;
|
||||
}
|
||||
if (s->x_dirty_bitmap) {
|
||||
if (!s->info.base_allocation) {
|
||||
error_setg(errp, "requested x-dirty-bitmap %s not found",
|
||||
s->x_dirty_bitmap);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
if (strcmp(s->x_dirty_bitmap, "qemu:allocation-depth") == 0) {
|
||||
s->alloc_depth = true;
|
||||
}
|
||||
}
|
||||
if (s->info.flags & NBD_FLAG_READ_ONLY) {
|
||||
ret = bdrv_apply_auto_read_only(bs, "NBD export is read-only", errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (s->info.flags & NBD_FLAG_SEND_FUA) {
|
||||
bs->supported_write_flags = BDRV_REQ_FUA;
|
||||
bs->supported_zero_flags |= BDRV_REQ_FUA;
|
||||
}
|
||||
if (s->info.flags & NBD_FLAG_SEND_WRITE_ZEROES) {
|
||||
bs->supported_zero_flags |= BDRV_REQ_MAY_UNMAP;
|
||||
if (s->info.flags & NBD_FLAG_SEND_FAST_ZERO) {
|
||||
bs->supported_zero_flags |= BDRV_REQ_NO_FALLBACK;
|
||||
}
|
||||
}
|
||||
|
||||
if (!s->ioc) {
|
||||
s->ioc = QIO_CHANNEL(s->sioc);
|
||||
object_ref(OBJECT(s->ioc));
|
||||
}
|
||||
|
||||
trace_nbd_client_handshake_success(s->export);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
/*
|
||||
* We have connected, but must fail for other reasons.
|
||||
* Send NBD_CMD_DISC as a courtesy to the server.
|
||||
*/
|
||||
{
|
||||
NBDRequest request = { .type = NBD_CMD_DISC };
|
||||
|
||||
nbd_send_request(s->ioc ?: QIO_CHANNEL(s->sioc), &request);
|
||||
|
||||
yank_unregister_function(BLOCKDEV_YANK_INSTANCE(bs->node_name),
|
||||
nbd_yank, bs);
|
||||
object_unref(OBJECT(s->sioc));
|
||||
s->sioc = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse nbd_open options
|
||||
@ -2137,6 +1807,12 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (socket_address_parse_named_fd(saddr, errp) < 0) {
|
||||
qapi_free_SocketAddress(saddr);
|
||||
saddr = NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
qobject_unref(addr);
|
||||
visit_free(iv);
|
||||
@ -2163,9 +1839,9 @@ static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
|
||||
error_setg(errp,
|
||||
"Expecting TLS credentials with a client endpoint");
|
||||
if (!qcrypto_tls_creds_check_endpoint(creds,
|
||||
QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT,
|
||||
errp)) {
|
||||
return NULL;
|
||||
}
|
||||
object_ref(obj);
|
||||
@ -2278,9 +1954,6 @@ static int nbd_process_options(BlockDriverState *bs, QDict *options,
|
||||
ret = 0;
|
||||
|
||||
error:
|
||||
if (ret < 0) {
|
||||
nbd_clear_bdrvstate(s);
|
||||
}
|
||||
qemu_opts_del(opts);
|
||||
return ret;
|
||||
}
|
||||
@ -2291,11 +1964,6 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
int ret;
|
||||
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
|
||||
|
||||
ret = nbd_process_options(bs, options, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
s->bs = bs;
|
||||
qemu_co_mutex_init(&s->send_mutex);
|
||||
qemu_co_queue_init(&s->free_sema);
|
||||
@ -2304,31 +1972,29 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
/*
|
||||
* establish TCP connection, return error if it fails
|
||||
* TODO: Configurable retry-until-timeout behaviour.
|
||||
*/
|
||||
if (nbd_establish_connection(bs, s->saddr, errp) < 0) {
|
||||
yank_unregister_instance(BLOCKDEV_YANK_INSTANCE(bs->node_name));
|
||||
return -ECONNREFUSED;
|
||||
}
|
||||
|
||||
ret = nbd_client_handshake(bs, errp);
|
||||
ret = nbd_process_options(bs, options, errp);
|
||||
if (ret < 0) {
|
||||
yank_unregister_instance(BLOCKDEV_YANK_INSTANCE(bs->node_name));
|
||||
nbd_clear_bdrvstate(s);
|
||||
return ret;
|
||||
goto fail;
|
||||
}
|
||||
/* successfully connected */
|
||||
s->state = NBD_CLIENT_CONNECTED;
|
||||
|
||||
nbd_init_connect_thread(s);
|
||||
s->conn = nbd_client_connection_new(s->saddr, true, s->export,
|
||||
s->x_dirty_bitmap, s->tlscreds);
|
||||
|
||||
/* TODO: Configurable retry-until-timeout behaviour. */
|
||||
ret = nbd_do_establish_connection(bs, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->connection_co = qemu_coroutine_create(nbd_connection_entry, s);
|
||||
bdrv_inc_in_flight(bs);
|
||||
aio_co_schedule(bdrv_get_aio_context(bs), s->connection_co);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
nbd_clear_bdrvstate(bs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nbd_co_flush(BlockDriverState *bs)
|
||||
@ -2372,11 +2038,8 @@ static void nbd_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
|
||||
static void nbd_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
|
||||
nbd_client_close(bs);
|
||||
yank_unregister_instance(BLOCKDEV_YANK_INSTANCE(bs->node_name));
|
||||
nbd_clear_bdrvstate(s);
|
||||
nbd_clear_bdrvstate(bs);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -39,7 +39,6 @@
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/uri.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
|
64
block/progress_meter.c
Normal file
64
block/progress_meter.c
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Helper functionality for some process progress tracking.
|
||||
*
|
||||
* Copyright (c) 2011 IBM Corp.
|
||||
* Copyright (c) 2012, 2018 Red Hat, Inc.
|
||||
* Copyright (c) 2020 Virtuozzo International GmbH
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/progress_meter.h"
|
||||
|
||||
void progress_init(ProgressMeter *pm)
|
||||
{
|
||||
qemu_mutex_init(&pm->lock);
|
||||
}
|
||||
|
||||
void progress_destroy(ProgressMeter *pm)
|
||||
{
|
||||
qemu_mutex_destroy(&pm->lock);
|
||||
}
|
||||
|
||||
void progress_get_snapshot(ProgressMeter *pm, uint64_t *current,
|
||||
uint64_t *total)
|
||||
{
|
||||
QEMU_LOCK_GUARD(&pm->lock);
|
||||
|
||||
*current = pm->current;
|
||||
*total = pm->total;
|
||||
}
|
||||
|
||||
void progress_work_done(ProgressMeter *pm, uint64_t done)
|
||||
{
|
||||
QEMU_LOCK_GUARD(&pm->lock);
|
||||
pm->current += done;
|
||||
}
|
||||
|
||||
void progress_set_remaining(ProgressMeter *pm, uint64_t remaining)
|
||||
{
|
||||
QEMU_LOCK_GUARD(&pm->lock);
|
||||
pm->total = pm->current + remaining;
|
||||
}
|
||||
|
||||
void progress_increase_remaining(ProgressMeter *pm, uint64_t delta)
|
||||
{
|
||||
QEMU_LOCK_GUARD(&pm->lock);
|
||||
pm->total += delta;
|
||||
}
|
13
block/qapi.c
13
block/qapi.c
@ -59,7 +59,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
|
||||
|
||||
info = g_malloc0(sizeof(*info));
|
||||
info->file = g_strdup(bs->filename);
|
||||
info->ro = bs->read_only;
|
||||
info->ro = bdrv_is_read_only(bs);
|
||||
info->drv = g_strdup(bs->drv->format_name);
|
||||
info->encrypted = bs->encrypted;
|
||||
|
||||
@ -663,10 +663,8 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes,
|
||||
|
||||
void bdrv_snapshot_dump(QEMUSnapshotInfo *sn)
|
||||
{
|
||||
char date_buf[128], clock_buf[128];
|
||||
char clock_buf[128];
|
||||
char icount_buf[128] = {0};
|
||||
struct tm tm;
|
||||
time_t ti;
|
||||
int64_t secs;
|
||||
char *sizing = NULL;
|
||||
|
||||
@ -674,10 +672,9 @@ void bdrv_snapshot_dump(QEMUSnapshotInfo *sn)
|
||||
qemu_printf("%-10s%-17s%8s%20s%13s%11s",
|
||||
"ID", "TAG", "VM SIZE", "DATE", "VM CLOCK", "ICOUNT");
|
||||
} else {
|
||||
ti = sn->date_sec;
|
||||
localtime_r(&ti, &tm);
|
||||
strftime(date_buf, sizeof(date_buf),
|
||||
"%Y-%m-%d %H:%M:%S", &tm);
|
||||
g_autoptr(GDateTime) date = g_date_time_new_from_unix_local(sn->date_sec);
|
||||
g_autofree char *date_buf = g_date_time_format(date, "%Y-%m-%d %H:%M:%S");
|
||||
|
||||
secs = sn->vm_clock_nsec / 1000000000;
|
||||
snprintf(clock_buf, sizeof(clock_buf),
|
||||
"%02d:%02d:%02d.%03d",
|
||||
|
@ -1026,7 +1026,7 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs,
|
||||
int new_l1_bytes;
|
||||
int ret;
|
||||
|
||||
assert(bs->read_only);
|
||||
assert(bdrv_is_read_only(bs));
|
||||
|
||||
/* Search the snapshot */
|
||||
snapshot_index = find_snapshot_by_id_and_name(bs, snapshot_id, name);
|
||||
|
@ -1723,8 +1723,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
||||
|
||||
/* Clear unknown autoclear feature bits */
|
||||
update_header |= s->autoclear_features & ~QCOW2_AUTOCLEAR_MASK;
|
||||
update_header =
|
||||
update_header && !bs->read_only && !(flags & BDRV_O_INACTIVE);
|
||||
update_header = update_header && bdrv_is_writable(bs);
|
||||
if (update_header) {
|
||||
s->autoclear_features &= QCOW2_AUTOCLEAR_MASK;
|
||||
}
|
||||
@ -1811,7 +1810,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
||||
bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE;
|
||||
|
||||
/* Repair image if dirty */
|
||||
if (!(flags & (BDRV_O_CHECK | BDRV_O_INACTIVE)) && !bs->read_only &&
|
||||
if (!(flags & BDRV_O_CHECK) && bdrv_is_writable(bs) &&
|
||||
(s->incompatible_features & QCOW2_INCOMPAT_DIRTY)) {
|
||||
BdrvCheckResult result = {0};
|
||||
|
||||
@ -5089,6 +5088,7 @@ static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
bdi->cluster_size = s->cluster_size;
|
||||
bdi->vm_state_offset = qcow2_vm_state_offset(s);
|
||||
bdi->is_dirty = s->incompatible_features & QCOW2_INCOMPAT_DIRTY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1279,7 +1279,7 @@ static BlockDriver bdrv_quorum = {
|
||||
.bdrv_dirname = quorum_dirname,
|
||||
.bdrv_co_block_status = quorum_co_block_status,
|
||||
|
||||
.bdrv_co_flush_to_disk = quorum_co_flush,
|
||||
.bdrv_co_flush = quorum_co_flush,
|
||||
|
||||
.bdrv_getlength = quorum_getlength,
|
||||
|
||||
|
32
block/rbd.c
32
block/rbd.c
@ -113,21 +113,31 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
|
||||
const char *keypairs, const char *secretid,
|
||||
Error **errp);
|
||||
|
||||
static char *qemu_rbd_strchr(char *src, char delim)
|
||||
{
|
||||
char *p;
|
||||
|
||||
for (p = src; *p; ++p) {
|
||||
if (*p == delim) {
|
||||
return p;
|
||||
}
|
||||
if (*p == '\\' && p[1] != '\0') {
|
||||
++p;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static char *qemu_rbd_next_tok(char *src, char delim, char **p)
|
||||
{
|
||||
char *end;
|
||||
|
||||
*p = NULL;
|
||||
|
||||
for (end = src; *end; ++end) {
|
||||
if (*end == delim) {
|
||||
break;
|
||||
}
|
||||
if (*end == '\\' && end[1] != '\0') {
|
||||
end++;
|
||||
}
|
||||
}
|
||||
if (*end == delim) {
|
||||
end = qemu_rbd_strchr(src, delim);
|
||||
if (end) {
|
||||
*p = end + 1;
|
||||
*end = '\0';
|
||||
}
|
||||
@ -171,7 +181,7 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options,
|
||||
qemu_rbd_unescape(found_str);
|
||||
qdict_put_str(options, "pool", found_str);
|
||||
|
||||
if (strchr(p, '@')) {
|
||||
if (qemu_rbd_strchr(p, '@')) {
|
||||
image_name = qemu_rbd_next_tok(p, '@', &p);
|
||||
|
||||
found_str = qemu_rbd_next_tok(p, ':', &p);
|
||||
@ -181,7 +191,7 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options,
|
||||
image_name = qemu_rbd_next_tok(p, ':', &p);
|
||||
}
|
||||
/* Check for namespace in the image_name */
|
||||
if (strchr(image_name, '/')) {
|
||||
if (qemu_rbd_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);
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "replication.h"
|
||||
#include "block/replication.h"
|
||||
|
||||
typedef enum {
|
||||
BLOCK_REPLICATION_NONE, /* block replication is not started */
|
||||
|
3356
block/sheepdog.c
3356
block/sheepdog.c
File diff suppressed because it is too large
Load Diff
@ -275,13 +275,16 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
|
||||
qobject_unref(file_options);
|
||||
g_free(subqdict_prefix);
|
||||
|
||||
/* Force .bdrv_open() below to re-attach fallback_bs on *fallback_ptr */
|
||||
qdict_put_str(options, (*fallback_ptr)->name,
|
||||
bdrv_get_node_name(fallback_bs));
|
||||
|
||||
/* Now close bs, apply the snapshot on fallback_bs, and re-open bs */
|
||||
if (drv->bdrv_close) {
|
||||
drv->bdrv_close(bs);
|
||||
}
|
||||
|
||||
/* .bdrv_open() will re-attach it */
|
||||
bdrv_unref_child(bs, *fallback_ptr);
|
||||
*fallback_ptr = NULL;
|
||||
|
||||
@ -296,7 +299,16 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
|
||||
return ret < 0 ? ret : open_ret;
|
||||
}
|
||||
|
||||
assert(fallback_bs == (*fallback_ptr)->bs);
|
||||
/*
|
||||
* fallback_ptr is &bs->file or &bs->backing. *fallback_ptr
|
||||
* was closed above and set to NULL, but the .bdrv_open() call
|
||||
* has opened it again, because we set the respective option
|
||||
* (with the qdict_put_str() call above).
|
||||
* Assert that .bdrv_open() has attached some child on
|
||||
* *fallback_ptr, and that it has attached the one we wanted
|
||||
* it to (i.e., fallback_bs).
|
||||
*/
|
||||
assert(*fallback_ptr && fallback_bs == (*fallback_ptr)->bs);
|
||||
bdrv_unref(fallback_bs);
|
||||
return ret;
|
||||
}
|
||||
@ -415,7 +427,7 @@ int bdrv_snapshot_load_tmp(BlockDriverState *bs,
|
||||
error_setg(errp, "snapshot_id and name are both NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!bs->read_only) {
|
||||
if (!bdrv_is_read_only(bs)) {
|
||||
error_setg(errp, "Device is not readonly");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
62
block/ssh.c
62
block/ssh.c
@ -277,7 +277,6 @@ static void ssh_parse_filename(const char *filename, QDict *options,
|
||||
static int check_host_key_knownhosts(BDRVSSHState *s, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
#ifdef HAVE_LIBSSH_0_8
|
||||
enum ssh_known_hosts_e state;
|
||||
int r;
|
||||
ssh_key pubkey;
|
||||
@ -343,46 +342,6 @@ static int check_host_key_knownhosts(BDRVSSHState *s, Error **errp)
|
||||
error_setg(errp, "error while checking for known server (%d)", state);
|
||||
goto out;
|
||||
}
|
||||
#else /* !HAVE_LIBSSH_0_8 */
|
||||
int state;
|
||||
|
||||
state = ssh_is_server_known(s->session);
|
||||
trace_ssh_server_status(state);
|
||||
|
||||
switch (state) {
|
||||
case SSH_SERVER_KNOWN_OK:
|
||||
/* OK */
|
||||
trace_ssh_check_host_key_knownhosts();
|
||||
break;
|
||||
case SSH_SERVER_KNOWN_CHANGED:
|
||||
ret = -EINVAL;
|
||||
error_setg(errp,
|
||||
"host key does not match the one in known_hosts; this "
|
||||
"may be a possible attack");
|
||||
goto out;
|
||||
case SSH_SERVER_FOUND_OTHER:
|
||||
ret = -EINVAL;
|
||||
error_setg(errp,
|
||||
"host key for this server not found, another type exists");
|
||||
goto out;
|
||||
case SSH_SERVER_FILE_NOT_FOUND:
|
||||
ret = -ENOENT;
|
||||
error_setg(errp, "known_hosts file not found");
|
||||
goto out;
|
||||
case SSH_SERVER_NOT_KNOWN:
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "no host key was found in known_hosts");
|
||||
goto out;
|
||||
case SSH_SERVER_ERROR:
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "server error");
|
||||
goto out;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "error while checking for known server (%d)", state);
|
||||
goto out;
|
||||
}
|
||||
#endif /* !HAVE_LIBSSH_0_8 */
|
||||
|
||||
/* known_hosts checking successful. */
|
||||
ret = 0;
|
||||
@ -438,11 +397,7 @@ check_host_key_hash(BDRVSSHState *s, const char *hash,
|
||||
unsigned char *server_hash;
|
||||
size_t server_hash_len;
|
||||
|
||||
#ifdef HAVE_LIBSSH_0_8
|
||||
r = ssh_get_server_publickey(s->session, &pubkey);
|
||||
#else
|
||||
r = ssh_get_publickey(s->session, &pubkey);
|
||||
#endif
|
||||
if (r != SSH_OK) {
|
||||
session_error_setg(errp, s, "failed to read remote host key");
|
||||
return -EINVAL;
|
||||
@ -487,6 +442,9 @@ static int check_host_key(BDRVSSHState *s, SshHostKeyCheck *hkc, Error **errp)
|
||||
} else if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_SHA1) {
|
||||
return check_host_key_hash(s, hkc->u.hash.hash,
|
||||
SSH_PUBLICKEY_HASH_SHA1, errp);
|
||||
} else if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_SHA256) {
|
||||
return check_host_key_hash(s, hkc->u.hash.hash,
|
||||
SSH_PUBLICKEY_HASH_SHA256, errp);
|
||||
}
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
@ -1233,8 +1191,6 @@ static void unsafe_flush_warning(BDRVSSHState *s, const char *what)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBSSH_0_8
|
||||
|
||||
static coroutine_fn int ssh_flush(BDRVSSHState *s, BlockDriverState *bs)
|
||||
{
|
||||
int r;
|
||||
@ -1271,18 +1227,6 @@ static coroutine_fn int ssh_co_flush(BlockDriverState *bs)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else /* !HAVE_LIBSSH_0_8 */
|
||||
|
||||
static coroutine_fn int ssh_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
BDRVSSHState *s = bs->opaque;
|
||||
|
||||
unsafe_flush_warning(s, "libssh >= 0.8.0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* !HAVE_LIBSSH_0_8 */
|
||||
|
||||
static int64_t ssh_getlength(BlockDriverState *bs)
|
||||
{
|
||||
BDRVSSHState *s = bs->opaque;
|
||||
|
@ -1,4 +1,4 @@
|
||||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
# See docs/devel/tracing.rst for syntax documentation.
|
||||
|
||||
# ../block.c
|
||||
bdrv_open_common(void *bs, const char *filename, int flags, const char *format_name) "bs %p filename \"%s\" flags 0x%x format_name \"%s\""
|
||||
@ -206,20 +206,7 @@ file_copy_file_range(void *bs, int src, int64_t src_off, int dst, int64_t dst_of
|
||||
file_FindEjectableOpticalMedia(const char *media) "Matching using %s"
|
||||
file_setup_cdrom(const char *partition) "Using %s as optical disc"
|
||||
file_hdev_is_sg(int type, int version) "SG device found: type=%d, version=%d"
|
||||
|
||||
# sheepdog.c
|
||||
sheepdog_reconnect_to_sdog(void) "Wait for connection to be established"
|
||||
sheepdog_aio_read_response(void) "disable cache since the server doesn't support it"
|
||||
sheepdog_open(uint32_t vid) "0x%" PRIx32 " snapshot inode was open"
|
||||
sheepdog_close(const char *name) "%s"
|
||||
sheepdog_create_branch_snapshot(uint32_t vdi) "0x%" PRIx32 " is snapshot"
|
||||
sheepdog_create_branch_created(uint32_t vdi) "0x%" PRIx32 " is created"
|
||||
sheepdog_create_branch_new(uint32_t vdi) "0x%" PRIx32 " was newly created"
|
||||
sheepdog_co_rw_vector_update(uint32_t vdi, uint64_t oid, uint64_t data, long idx) "update ino (%" PRIu32 ") %" PRIu64 " %" PRIu64 " %ld"
|
||||
sheepdog_co_rw_vector_new(uint64_t oid) "new oid 0x%" PRIx64
|
||||
sheepdog_snapshot_create_info(const char *sn_name, const char *id, const char *name, int64_t size, int is_snapshot) "sn_info: name %s id_str %s s: name %s vm_state_size %" PRId64 " " "is_snapshot %d"
|
||||
sheepdog_snapshot_create(const char *sn_name, const char *id) "%s %s"
|
||||
sheepdog_snapshot_create_inode(const char *name, uint32_t snap, uint32_t vdi) "s->inode: name %s snap_id 0x%" PRIx32 " vdi 0x%" PRIx32
|
||||
file_flush_fdatasync_failed(int err) "errno %d"
|
||||
|
||||
# ssh.c
|
||||
sftp_error(const char *op, const char *ssh_err, int ssh_err_code, int sftp_err_code) "%s failed: %s (libssh error code: %d, sftp error code: %d)"
|
||||
|
@ -801,7 +801,7 @@ int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed,
|
||||
}
|
||||
|
||||
if (logs.valid) {
|
||||
if (bs->read_only) {
|
||||
if (bdrv_is_read_only(bs)) {
|
||||
bdrv_refresh_filename(bs);
|
||||
ret = -EPERM;
|
||||
error_setg(errp,
|
||||
|
@ -3127,10 +3127,7 @@ static void vvfat_qcow_options(BdrvChildRole role, bool parent_is_format,
|
||||
qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on");
|
||||
}
|
||||
|
||||
static const BdrvChildClass child_vvfat_qcow = {
|
||||
.parent_is_bds = true,
|
||||
.inherit_options = vvfat_qcow_options,
|
||||
};
|
||||
static BdrvChildClass child_vvfat_qcow;
|
||||
|
||||
static int enable_write_target(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
@ -3208,15 +3205,12 @@ static void vvfat_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||
uint64_t perm, uint64_t shared,
|
||||
uint64_t *nperm, uint64_t *nshared)
|
||||
{
|
||||
BDRVVVFATState *s = bs->opaque;
|
||||
|
||||
assert(c == s->qcow || (role & BDRV_CHILD_COW));
|
||||
|
||||
if (c == s->qcow) {
|
||||
if (role & BDRV_CHILD_DATA) {
|
||||
/* This is a private node, nobody should try to attach to it */
|
||||
*nperm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE;
|
||||
*nshared = BLK_PERM_WRITE_UNCHANGED;
|
||||
} else {
|
||||
assert(role & BDRV_CHILD_COW);
|
||||
/* The backing file is there so 'commit' can use it. vvfat doesn't
|
||||
* access it in any way. */
|
||||
*nperm = 0;
|
||||
@ -3270,6 +3264,8 @@ static BlockDriver bdrv_vvfat = {
|
||||
|
||||
static void bdrv_vvfat_init(void)
|
||||
{
|
||||
child_vvfat_qcow = child_of_bds;
|
||||
child_vvfat_qcow.inherit_options = vvfat_qcow_options;
|
||||
bdrv_register(&bdrv_vvfat);
|
||||
}
|
||||
|
||||
|
@ -12,9 +12,7 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "block/write-threshold.h"
|
||||
#include "qemu/notify.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-commands-block-core.h"
|
||||
#include "qapi/qapi-events-block-core.h"
|
||||
@ -24,82 +22,9 @@ uint64_t bdrv_write_threshold_get(const BlockDriverState *bs)
|
||||
return bs->write_threshold_offset;
|
||||
}
|
||||
|
||||
bool bdrv_write_threshold_is_set(const BlockDriverState *bs)
|
||||
{
|
||||
return bs->write_threshold_offset > 0;
|
||||
}
|
||||
|
||||
static void write_threshold_disable(BlockDriverState *bs)
|
||||
{
|
||||
if (bdrv_write_threshold_is_set(bs)) {
|
||||
notifier_with_return_remove(&bs->write_threshold_notifier);
|
||||
bs->write_threshold_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t bdrv_write_threshold_exceeded(const BlockDriverState *bs,
|
||||
const BdrvTrackedRequest *req)
|
||||
{
|
||||
if (bdrv_write_threshold_is_set(bs)) {
|
||||
if (req->offset > bs->write_threshold_offset) {
|
||||
return (req->offset - bs->write_threshold_offset) + req->bytes;
|
||||
}
|
||||
if ((req->offset + req->bytes) > bs->write_threshold_offset) {
|
||||
return (req->offset + req->bytes) - bs->write_threshold_offset;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coroutine_fn before_write_notify(NotifierWithReturn *notifier,
|
||||
void *opaque)
|
||||
{
|
||||
BdrvTrackedRequest *req = opaque;
|
||||
BlockDriverState *bs = req->bs;
|
||||
uint64_t amount = 0;
|
||||
|
||||
amount = bdrv_write_threshold_exceeded(bs, req);
|
||||
if (amount > 0) {
|
||||
qapi_event_send_block_write_threshold(
|
||||
bs->node_name,
|
||||
amount,
|
||||
bs->write_threshold_offset);
|
||||
|
||||
/* autodisable to avoid flooding the monitor */
|
||||
write_threshold_disable(bs);
|
||||
}
|
||||
|
||||
return 0; /* should always let other notifiers run */
|
||||
}
|
||||
|
||||
static void write_threshold_register_notifier(BlockDriverState *bs)
|
||||
{
|
||||
bs->write_threshold_notifier.notify = before_write_notify;
|
||||
bdrv_add_before_write_notifier(bs, &bs->write_threshold_notifier);
|
||||
}
|
||||
|
||||
static void write_threshold_update(BlockDriverState *bs,
|
||||
int64_t threshold_bytes)
|
||||
{
|
||||
bs->write_threshold_offset = threshold_bytes;
|
||||
}
|
||||
|
||||
void bdrv_write_threshold_set(BlockDriverState *bs, uint64_t threshold_bytes)
|
||||
{
|
||||
if (bdrv_write_threshold_is_set(bs)) {
|
||||
if (threshold_bytes > 0) {
|
||||
write_threshold_update(bs, threshold_bytes);
|
||||
} else {
|
||||
write_threshold_disable(bs);
|
||||
}
|
||||
} else {
|
||||
if (threshold_bytes > 0) {
|
||||
/* avoid multiple registration */
|
||||
write_threshold_register_notifier(bs);
|
||||
write_threshold_update(bs, threshold_bytes);
|
||||
}
|
||||
/* discard bogus disable request */
|
||||
}
|
||||
bs->write_threshold_offset = threshold_bytes;
|
||||
}
|
||||
|
||||
void qmp_block_set_write_threshold(const char *node_name,
|
||||
@ -122,3 +47,17 @@ void qmp_block_set_write_threshold(const char *node_name,
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
void bdrv_write_threshold_check_write(BlockDriverState *bs, int64_t offset,
|
||||
int64_t bytes)
|
||||
{
|
||||
int64_t end = offset + bytes;
|
||||
uint64_t wtr = bs->write_threshold_offset;
|
||||
|
||||
if (wtr > 0 && end > wtr) {
|
||||
qapi_event_send_block_write_threshold(bs->node_name, end - wtr, wtr);
|
||||
|
||||
/* autodisable to avoid flooding the monitor */
|
||||
bdrv_write_threshold_set(bs, 0);
|
||||
}
|
||||
}
|
||||
|
@ -108,9 +108,9 @@ static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
|
||||
error_setg(errp,
|
||||
"Expecting TLS credentials with a server endpoint");
|
||||
if (!qcrypto_tls_creds_check_endpoint(creds,
|
||||
QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
|
||||
errp)) {
|
||||
return NULL;
|
||||
}
|
||||
object_ref(obj);
|
||||
|
@ -583,8 +583,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
|
||||
blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
|
||||
blk_rs = blk_get_root_state(blk);
|
||||
blk_rs->open_flags = bdrv_flags;
|
||||
blk_rs->read_only = read_only;
|
||||
blk_rs->open_flags = bdrv_flags | (read_only ? 0 : BDRV_O_RDWR);
|
||||
blk_rs->detect_zeroes = detect_zeroes;
|
||||
|
||||
qobject_unref(bs_opts);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user