Compare commits
110 Commits
pull-ui-20
...
v2.8.1.1
Author | SHA1 | Date | |
---|---|---|---|
|
d1b725ee12 | ||
|
96bae145e2 | ||
|
7124ccf8b3 | ||
|
08c48c731a | ||
|
12110bf70e | ||
|
07672ab003 | ||
|
877e2b016e | ||
|
2f8ab9b1dd | ||
|
c15c6d2594 | ||
|
dd39c544f4 | ||
|
879b6454be | ||
|
ce37df91f4 | ||
|
dc35a13747 | ||
|
a290442234 | ||
|
031700e452 | ||
|
2f51fd1f73 | ||
|
63fdb09491 | ||
|
3328c14e63 | ||
|
a99fd943c4 | ||
|
670ddcc0aa | ||
|
8db38049c6 | ||
|
205a619563 | ||
|
5d26f91c89 | ||
|
1a184c3af3 | ||
|
7f515a96ab | ||
|
d437262fa8 | ||
|
74b13f92c2 | ||
|
4bcb497c7e | ||
|
8029d55a93 | ||
|
a3aeb9f09d | ||
|
34e9c09d03 | ||
|
9e9483dea9 | ||
|
ba9c51d225 | ||
|
495756ef9d | ||
|
9ad26963bf | ||
|
15ad066065 | ||
|
7cfd9c114b | ||
|
bddf2232fc | ||
|
fc8e94c3e5 | ||
|
5e4641777c | ||
|
d5506b3128 | ||
|
823fb688eb | ||
|
270a46ea0d | ||
|
f61f76cb3f | ||
|
8ac427cdd0 | ||
|
1d1d9226d8 | ||
|
adf2c47aa2 | ||
|
1a156ae5d1 | ||
|
3b8f27fe0b | ||
|
44d24c7b4a | ||
|
5f4b9013e6 | ||
|
5e40f283e5 | ||
|
d2b9063eb8 | ||
|
d8dea6fbcb | ||
|
f054cead44 | ||
|
5fb07a7b6c | ||
|
3fb4b3c371 | ||
|
a626117f6a | ||
|
3b33cba69c | ||
|
50b468d421 | ||
|
028fbea477 | ||
|
6e8052f967 | ||
|
5c60c6ef61 | ||
|
2ab8276a1c | ||
|
662a97d74f | ||
|
d6f119475d | ||
|
f47bf0823b | ||
|
8a6562592f | ||
|
9f6cb916f2 | ||
|
dc659e3aea | ||
|
87ede19db3 | ||
|
da95bfe06b | ||
|
7830be742a | ||
|
620a65dc44 | ||
|
9d14f0cd65 | ||
|
04cde530be | ||
|
a15785cfbd | ||
|
3731a25a62 | ||
|
7e9a1c4914 | ||
|
059f751ec2 | ||
|
bb07a379b5 | ||
|
719e6dd171 | ||
|
05a92c2005 | ||
|
9c5cb58970 | ||
|
c8c9aab173 | ||
|
5b24a96cd2 | ||
|
9f4ba82b06 | ||
|
62d1dbbfce | ||
|
ea9e59bdf6 | ||
|
e314b1b1fb | ||
|
d7322e13c6 | ||
|
d93d06a2fc | ||
|
0f3490faa8 | ||
|
cfe40e1485 | ||
|
3439290f9e | ||
|
ec10eada04 | ||
|
d3c54bf9e7 | ||
|
91225c670a | ||
|
4286f58bfc | ||
|
0bb99557e1 | ||
|
a9f46b8b65 | ||
|
ed6083afc2 | ||
|
d10142c11b | ||
|
6c1e3a16bc | ||
|
acf22d2264 | ||
|
54f951d634 | ||
|
984bd0a1b8 | ||
|
52d43ff72e | ||
|
e103f9e7b4 | ||
|
2c4f0f6c11 |
37
.gitignore
vendored
37
.gitignore
vendored
@@ -6,12 +6,18 @@
|
||||
/config.status
|
||||
/config-temp
|
||||
/trace-events-all
|
||||
/trace/generated-tracers.h
|
||||
/trace/generated-tracers.c
|
||||
/trace/generated-tracers-dtrace.h
|
||||
/trace/generated-tracers.dtrace
|
||||
/trace/generated-events.h
|
||||
/trace/generated-events.c
|
||||
/trace/generated-helpers-wrappers.h
|
||||
/trace/generated-helpers.h
|
||||
/trace/generated-helpers.c
|
||||
/trace/generated-tcg-tracers.h
|
||||
/trace/generated-ust-provider.h
|
||||
/trace/generated-ust.c
|
||||
/ui/shader/texture-blit-frag.h
|
||||
/ui/shader/texture-blit-vert.h
|
||||
*-timestamp
|
||||
@@ -34,7 +40,6 @@
|
||||
/qmp-marshal.c
|
||||
/qemu-doc.html
|
||||
/qemu-doc.info
|
||||
/qemu-doc.txt
|
||||
/qemu-img
|
||||
/qemu-nbd
|
||||
/qemu-options.def
|
||||
@@ -55,6 +60,7 @@
|
||||
*.a
|
||||
*.aux
|
||||
*.cp
|
||||
*.dvi
|
||||
*.exe
|
||||
*.msi
|
||||
*.dll
|
||||
@@ -76,6 +82,10 @@
|
||||
*.d
|
||||
!/scripts/qemu-guest-agent/fsfreeze-hook.d
|
||||
*.o
|
||||
*.lo
|
||||
*.la
|
||||
*.pc
|
||||
.libs
|
||||
.sdk
|
||||
*.gcda
|
||||
*.gcno
|
||||
@@ -99,34 +109,9 @@
|
||||
/pc-bios/optionrom/kvmvapic.img
|
||||
/pc-bios/s390-ccw/s390-ccw.elf
|
||||
/pc-bios/s390-ccw/s390-ccw.img
|
||||
/docs/qemu-ga-ref.html
|
||||
/docs/qemu-ga-ref.txt
|
||||
/docs/qemu-qmp-ref.html
|
||||
/docs/qemu-qmp-ref.txt
|
||||
docs/qemu-ga-ref.info*
|
||||
docs/qemu-qmp-ref.info*
|
||||
/qemu-ga-qapi.texi
|
||||
/qemu-qapi.texi
|
||||
*.tps
|
||||
.stgit-*
|
||||
cscope.*
|
||||
tags
|
||||
TAGS
|
||||
docker-src.*
|
||||
*~
|
||||
trace.h
|
||||
trace.c
|
||||
trace-ust.h
|
||||
trace-ust.h
|
||||
trace-dtrace.h
|
||||
trace-dtrace.dtrace
|
||||
trace-root.h
|
||||
trace-root.c
|
||||
trace-ust-root.h
|
||||
trace-ust-root.h
|
||||
trace-ust-all.h
|
||||
trace-ust-all.c
|
||||
trace-dtrace-root.h
|
||||
trace-dtrace-root.dtrace
|
||||
trace-ust-all.h
|
||||
trace-ust-all.c
|
||||
|
57
.travis.yml
57
.travis.yml
@@ -4,6 +4,7 @@ python:
|
||||
- "2.4"
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
cache: ccache
|
||||
addons:
|
||||
apt:
|
||||
@@ -67,9 +68,6 @@ script:
|
||||
- make -j3 && ${TEST_CMD}
|
||||
matrix:
|
||||
include:
|
||||
# Test with CLang for compile portability
|
||||
- env: CONFIG=""
|
||||
compiler: clang
|
||||
# gprof/gcov are GCC features
|
||||
- env: CONFIG="--enable-gprof --enable-gcov --disable-pie"
|
||||
compiler: gcc
|
||||
@@ -92,8 +90,8 @@ matrix:
|
||||
- env: CONFIG=""
|
||||
os: osx
|
||||
compiler: clang
|
||||
# Plain Trusty System Build
|
||||
- env: CONFIG="--disable-linux-user"
|
||||
# Plain Trusty Build
|
||||
- env: CONFIG=""
|
||||
sudo: required
|
||||
addons:
|
||||
dist: trusty
|
||||
@@ -103,55 +101,6 @@ matrix:
|
||||
- sudo apt-get build-dep -qq qemu
|
||||
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
|
||||
- git submodule update --init --recursive
|
||||
# Plain Trusty Linux User Build
|
||||
- env: CONFIG="--disable-system"
|
||||
sudo: required
|
||||
addons:
|
||||
dist: trusty
|
||||
compiler: gcc
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get build-dep -qq qemu
|
||||
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
|
||||
- git submodule update --init --recursive
|
||||
# Trusty System build with latest stable clang
|
||||
- sudo: required
|
||||
addons:
|
||||
dist: trusty
|
||||
language: generic
|
||||
compiler: none
|
||||
env:
|
||||
- COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9
|
||||
- CONFIG="--disable-linux-user --cc=clang-3.9 --cxx=clang++-3.9"
|
||||
before_install:
|
||||
- wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
- sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main'
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq -y clang-3.9
|
||||
- sudo apt-get build-dep -qq qemu
|
||||
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
|
||||
- git submodule update --init --recursive
|
||||
before_script:
|
||||
- ./configure ${CONFIG} || cat config.log
|
||||
# Trusty Linux User build with latest stable clang
|
||||
- sudo: required
|
||||
addons:
|
||||
dist: trusty
|
||||
language: generic
|
||||
compiler: none
|
||||
env:
|
||||
- COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9
|
||||
- CONFIG="--disable-system --cc=clang-3.9 --cxx=clang++-3.9"
|
||||
before_install:
|
||||
- wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
- sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main'
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq -y clang-3.9
|
||||
- sudo apt-get build-dep -qq qemu
|
||||
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
|
||||
- git submodule update --init --recursive
|
||||
before_script:
|
||||
- ./configure ${CONFIG} || cat config.log
|
||||
# Using newer GCC with sanitizers
|
||||
- addons:
|
||||
apt:
|
||||
|
18
HACKING
18
HACKING
@@ -1,28 +1,10 @@
|
||||
1. Preprocessor
|
||||
|
||||
1.1. Variadic macros
|
||||
|
||||
For variadic macros, stick with this C99-like syntax:
|
||||
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { printf("IRQ: " fmt, ## __VA_ARGS__); } while (0)
|
||||
|
||||
1.2. Include directives
|
||||
|
||||
Order include directives as follows:
|
||||
|
||||
#include "qemu/osdep.h" /* Always first... */
|
||||
#include <...> /* then system headers... */
|
||||
#include "..." /* and finally QEMU headers. */
|
||||
|
||||
The "qemu/osdep.h" header contains preprocessor macros that affect the behavior
|
||||
of core system headers like <stdint.h>. It must be the first include so that
|
||||
core system headers included by external libraries get the preprocessor macros
|
||||
that QEMU depends on.
|
||||
|
||||
Do not include "qemu/osdep.h" from header files since the .c file will have
|
||||
already included it.
|
||||
|
||||
2. C types
|
||||
|
||||
It should be common sense to use the right type, but we have collected
|
||||
|
96
MAINTAINERS
96
MAINTAINERS
@@ -106,7 +106,7 @@ F: include/fpu/
|
||||
Alpha
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
S: Maintained
|
||||
F: target/alpha/
|
||||
F: target-alpha/
|
||||
F: hw/alpha/
|
||||
F: tests/tcg/alpha/
|
||||
F: disas/alpha.c
|
||||
@@ -115,7 +115,7 @@ ARM
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: target/arm/
|
||||
F: target-arm/
|
||||
F: hw/arm/
|
||||
F: hw/cpu/a*mpcore.c
|
||||
F: include/hw/cpu/a*mpcore.h
|
||||
@@ -126,22 +126,16 @@ F: disas/libvixl/
|
||||
CRIS
|
||||
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
||||
S: Maintained
|
||||
F: target/cris/
|
||||
F: target-cris/
|
||||
F: hw/cris/
|
||||
F: include/hw/cris/
|
||||
F: tests/tcg/cris/
|
||||
F: disas/cris.c
|
||||
|
||||
HPPA (PA-RISC)
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
S: Maintained
|
||||
F: target/hppa/
|
||||
F: disas/hppa.c
|
||||
|
||||
LM32
|
||||
M: Michael Walle <michael@walle.cc>
|
||||
S: Maintained
|
||||
F: target/lm32/
|
||||
F: target-lm32/
|
||||
F: disas/lm32.c
|
||||
F: hw/lm32/
|
||||
F: hw/*/lm32_*
|
||||
@@ -153,13 +147,13 @@ F: tests/tcg/lm32/
|
||||
M68K
|
||||
M: Laurent Vivier <laurent@vivier.eu>
|
||||
S: Maintained
|
||||
F: target/m68k/
|
||||
F: target-m68k/
|
||||
F: disas/m68k.c
|
||||
|
||||
MicroBlaze
|
||||
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
||||
S: Maintained
|
||||
F: target/microblaze/
|
||||
F: target-microblaze/
|
||||
F: hw/microblaze/
|
||||
F: disas/microblaze.c
|
||||
|
||||
@@ -167,7 +161,7 @@ MIPS
|
||||
M: Aurelien Jarno <aurelien@aurel32.net>
|
||||
M: Yongbok Kim <yongbok.kim@imgtec.com>
|
||||
S: Maintained
|
||||
F: target/mips/
|
||||
F: target-mips/
|
||||
F: hw/mips/
|
||||
F: hw/misc/mips_*
|
||||
F: hw/intc/mips_gic.c
|
||||
@@ -182,23 +176,15 @@ F: disas/mips.c
|
||||
Moxie
|
||||
M: Anthony Green <green@moxielogic.com>
|
||||
S: Maintained
|
||||
F: target/moxie/
|
||||
F: target-moxie/
|
||||
F: disas/moxie.c
|
||||
F: hw/moxie/
|
||||
F: default-configs/moxie-softmmu.mak
|
||||
|
||||
NiosII
|
||||
M: Chris Wulff <crwulff@gmail.com>
|
||||
M: Marek Vasut <marex@denx.de>
|
||||
S: Maintained
|
||||
F: target/nios2/
|
||||
F: hw/nios2/
|
||||
F: disas/nios2.c
|
||||
|
||||
OpenRISC
|
||||
M: Jia Liu <proljc@gmail.com>
|
||||
S: Maintained
|
||||
F: target/openrisc/
|
||||
F: target-openrisc/
|
||||
F: hw/openrisc/
|
||||
F: tests/tcg/openrisc/
|
||||
|
||||
@@ -207,7 +193,7 @@ M: David Gibson <david@gibson.dropbear.id.au>
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Maintained
|
||||
F: target/ppc/
|
||||
F: target-ppc/
|
||||
F: hw/ppc/
|
||||
F: include/hw/ppc/
|
||||
F: disas/ppc.c
|
||||
@@ -216,14 +202,14 @@ S390
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
S: Maintained
|
||||
F: target/s390x/
|
||||
F: target-s390x/
|
||||
F: hw/s390x/
|
||||
F: disas/s390.c
|
||||
|
||||
SH4
|
||||
M: Aurelien Jarno <aurelien@aurel32.net>
|
||||
S: Odd Fixes
|
||||
F: target/sh4/
|
||||
F: target-sh4/
|
||||
F: hw/sh4/
|
||||
F: disas/sh4.c
|
||||
F: include/hw/sh4/
|
||||
@@ -232,7 +218,7 @@ SPARC
|
||||
M: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
|
||||
M: Artyom Tarasenko <atar4qemu@gmail.com>
|
||||
S: Maintained
|
||||
F: target/sparc/
|
||||
F: target-sparc/
|
||||
F: hw/sparc/
|
||||
F: hw/sparc64/
|
||||
F: disas/sparc.c
|
||||
@@ -240,7 +226,7 @@ F: disas/sparc.c
|
||||
UniCore32
|
||||
M: Guan Xuetao <gxt@mprc.pku.edu.cn>
|
||||
S: Maintained
|
||||
F: target/unicore32/
|
||||
F: target-unicore32/
|
||||
F: hw/unicore32/
|
||||
F: include/hw/unicore32/
|
||||
|
||||
@@ -249,7 +235,7 @@ M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
M: Eduardo Habkost <ehabkost@redhat.com>
|
||||
S: Maintained
|
||||
F: target/i386/
|
||||
F: target-i386/
|
||||
F: hw/i386/
|
||||
F: disas/i386.c
|
||||
|
||||
@@ -257,14 +243,14 @@ Xtensa
|
||||
M: Max Filippov <jcmvbkbc@gmail.com>
|
||||
W: http://wiki.osll.spb.ru/doku.php?id=etc:users:jcmvbkbc:qemu-target-xtensa
|
||||
S: Maintained
|
||||
F: target/xtensa/
|
||||
F: target-xtensa/
|
||||
F: hw/xtensa/
|
||||
F: tests/tcg/xtensa/
|
||||
|
||||
TriCore
|
||||
M: Bastian Koppelmann <kbastian@mail.uni-paderborn.de>
|
||||
S: Maintained
|
||||
F: target/tricore/
|
||||
F: target-tricore/
|
||||
F: hw/tricore/
|
||||
F: include/hw/tricore/
|
||||
|
||||
@@ -283,26 +269,26 @@ ARM
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: target/arm/kvm.c
|
||||
F: target-arm/kvm.c
|
||||
|
||||
MIPS
|
||||
M: James Hogan <james.hogan@imgtec.com>
|
||||
S: Maintained
|
||||
F: target/mips/kvm.c
|
||||
F: target-mips/kvm.c
|
||||
|
||||
PPC
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
S: Maintained
|
||||
F: target/ppc/kvm.c
|
||||
F: target-ppc/kvm.c
|
||||
|
||||
S390
|
||||
M: Christian Borntraeger <borntraeger@de.ibm.com>
|
||||
M: Cornelia Huck <cornelia.huck@de.ibm.com>
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
S: Maintained
|
||||
F: target/s390x/kvm.c
|
||||
F: target/s390x/ioinst.[ch]
|
||||
F: target/s390x/machine.c
|
||||
F: target-s390x/kvm.c
|
||||
F: target-s390x/ioinst.[ch]
|
||||
F: target-s390x/machine.c
|
||||
F: hw/intc/s390_flic.c
|
||||
F: hw/intc/s390_flic_kvm.c
|
||||
F: include/hw/s390x/s390_flic.h
|
||||
@@ -315,7 +301,7 @@ M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
M: Marcelo Tosatti <mtosatti@redhat.com>
|
||||
L: kvm@vger.kernel.org
|
||||
S: Supported
|
||||
F: target/i386/kvm.c
|
||||
F: target-i386/kvm.c
|
||||
|
||||
Guest CPU Cores (Xen):
|
||||
----------------------
|
||||
@@ -323,7 +309,7 @@ Guest CPU Cores (Xen):
|
||||
X86
|
||||
M: Stefano Stabellini <sstabellini@kernel.org>
|
||||
M: Anthony Perard <anthony.perard@citrix.com>
|
||||
L: xen-devel@lists.xenproject.org
|
||||
L: xen-devel@lists.xensource.com
|
||||
S: Supported
|
||||
F: xen-*
|
||||
F: */xen*
|
||||
@@ -522,6 +508,7 @@ M: Shannon Zhao <shannon.zhao@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/virt-acpi-build.c
|
||||
F: include/hw/arm/virt-acpi-build.h
|
||||
|
||||
STM32F205
|
||||
M: Alistair Francis <alistair@alistair23.me>
|
||||
@@ -671,13 +658,10 @@ F: hw/misc/macio/
|
||||
F: hw/intc/heathrow_pic.c
|
||||
|
||||
PReP
|
||||
M: Hervé Poussineau <hpoussin@reactos.org>
|
||||
L: qemu-devel@nongnu.org
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Maintained
|
||||
S: Odd Fixes
|
||||
F: hw/ppc/prep.c
|
||||
F: hw/ppc/prep_systemio.c
|
||||
F: hw/ppc/rs6000_mc.c
|
||||
F: hw/pci-host/prep.[hc]
|
||||
F: hw/isa/pc87312.[hc]
|
||||
F: pc-bios/ppc_rom.bin
|
||||
@@ -742,13 +726,6 @@ S: Maintained
|
||||
F: hw/sparc64/sun4u.c
|
||||
F: pc-bios/openbios-sparc64
|
||||
|
||||
Sun4v
|
||||
M: Artyom Tarasenko <atar4qemu@gmail.com>
|
||||
S: Maintained
|
||||
F: hw/sparc64/sun4v.c
|
||||
F: hw/timer/sun4v-rtc.c
|
||||
F: include/hw/timer/sun4v-rtc.h
|
||||
|
||||
Leon3
|
||||
M: Fabien Chouteau <chouteau@adacore.com>
|
||||
S: Maintained
|
||||
@@ -830,7 +807,6 @@ M: Eduardo Habkost <ehabkost@redhat.com>
|
||||
M: Marcel Apfelbaum <marcel@redhat.com>
|
||||
S: Supported
|
||||
F: hw/core/machine.c
|
||||
F: hw/core/null-machine.c
|
||||
F: include/hw/boards.h
|
||||
|
||||
Xtensa Machines
|
||||
@@ -909,6 +885,7 @@ F: hw/acpi/*
|
||||
F: hw/smbios/*
|
||||
F: hw/i386/acpi-build.[hc]
|
||||
F: hw/arm/virt-acpi-build.c
|
||||
F: include/hw/arm/virt-acpi-build.h
|
||||
|
||||
ppc4xx
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
@@ -1034,7 +1011,7 @@ F: hw/input/virtio-input*.c
|
||||
F: include/hw/virtio/virtio-input.h
|
||||
|
||||
virtio-serial
|
||||
M: Amit Shah <amit@kernel.org>
|
||||
M: Amit Shah <amit.shah@redhat.com>
|
||||
S: Supported
|
||||
F: hw/char/virtio-serial-bus.c
|
||||
F: hw/char/virtio-console.c
|
||||
@@ -1043,7 +1020,7 @@ F: tests/virtio-console-test.c
|
||||
F: tests/virtio-serial-test.c
|
||||
|
||||
virtio-rng
|
||||
M: Amit Shah <amit@kernel.org>
|
||||
M: Amit Shah <amit.shah@redhat.com>
|
||||
S: Supported
|
||||
F: hw/virtio/virtio-rng.c
|
||||
F: include/hw/virtio/virtio-rng.h
|
||||
@@ -1197,9 +1174,8 @@ T: git git://github.com/jnsnow/qemu.git bitmaps
|
||||
|
||||
Character device backends
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
M: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
S: Maintained
|
||||
F: chardev/
|
||||
F: qemu-char.c
|
||||
F: backends/msmouse.c
|
||||
F: backends/testdev.c
|
||||
|
||||
@@ -1431,7 +1407,7 @@ F: scripts/checkpatch.pl
|
||||
|
||||
Migration
|
||||
M: Juan Quintela <quintela@redhat.com>
|
||||
M: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
||||
M: Amit Shah <amit.shah@redhat.com>
|
||||
S: Maintained
|
||||
F: include/migration/
|
||||
F: migration/
|
||||
@@ -1520,7 +1496,6 @@ M: Riku Voipio <riku.voipio@iki.fi>
|
||||
S: Maintained
|
||||
F: thunk.c
|
||||
F: user-exec.c
|
||||
F: user-exec-stub.c
|
||||
|
||||
BSD user
|
||||
S: Orphan
|
||||
@@ -1645,7 +1620,6 @@ M: Peter Lieven <pl@kamp.de>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: block/iscsi.c
|
||||
F: block/iscsi-opts.c
|
||||
|
||||
NFS
|
||||
M: Jeff Cody <jcody@redhat.com>
|
||||
@@ -1746,9 +1720,9 @@ L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: block/linux-aio.c
|
||||
F: include/block/raw-aio.h
|
||||
F: block/raw-format.c
|
||||
F: block/file-posix.c
|
||||
F: block/file-win32.c
|
||||
F: block/raw-posix.c
|
||||
F: block/raw-win32.c
|
||||
F: block/raw_bsd.c
|
||||
F: block/win32-aio.c
|
||||
|
||||
qcow2
|
||||
|
270
Makefile
270
Makefile
@@ -56,143 +56,32 @@ GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c qapi-event.c
|
||||
GENERATED_HEADERS += qmp-introspect.h
|
||||
GENERATED_SOURCES += qmp-introspect.c
|
||||
|
||||
GENERATED_HEADERS += trace/generated-tracers.h
|
||||
ifeq ($(findstring dtrace,$(TRACE_BACKENDS)),dtrace)
|
||||
GENERATED_HEADERS += trace/generated-tracers-dtrace.h
|
||||
endif
|
||||
GENERATED_SOURCES += trace/generated-tracers.c
|
||||
|
||||
GENERATED_HEADERS += trace/generated-tcg-tracers.h
|
||||
|
||||
GENERATED_HEADERS += trace/generated-helpers-wrappers.h
|
||||
GENERATED_HEADERS += trace/generated-helpers.h
|
||||
GENERATED_SOURCES += trace/generated-helpers.c
|
||||
|
||||
ifdef CONFIG_TRACE_UST
|
||||
GENERATED_HEADERS += trace-ust-all.h
|
||||
GENERATED_SOURCES += trace-ust-all.c
|
||||
ifeq ($(findstring ust,$(TRACE_BACKENDS)),ust)
|
||||
GENERATED_HEADERS += trace/generated-ust-provider.h
|
||||
GENERATED_SOURCES += trace/generated-ust.c
|
||||
endif
|
||||
|
||||
GENERATED_HEADERS += module_block.h
|
||||
|
||||
TRACE_HEADERS = trace-root.h $(trace-events-subdirs:%=%/trace.h)
|
||||
TRACE_SOURCES = trace-root.c $(trace-events-subdirs:%=%/trace.c)
|
||||
TRACE_DTRACE =
|
||||
ifdef CONFIG_TRACE_DTRACE
|
||||
TRACE_HEADERS += trace-dtrace-root.h $(trace-events-subdirs:%=%/trace-dtrace.h)
|
||||
TRACE_DTRACE += trace-dtrace-root.dtrace $(trace-events-subdirs:%=%/trace-dtrace.dtrace)
|
||||
endif
|
||||
ifdef CONFIG_TRACE_UST
|
||||
TRACE_HEADERS += trace-ust-root.h $(trace-events-subdirs:%=%/trace-ust.h)
|
||||
endif
|
||||
|
||||
GENERATED_HEADERS += $(TRACE_HEADERS)
|
||||
GENERATED_SOURCES += $(TRACE_SOURCES)
|
||||
|
||||
trace-group-name = $(shell dirname $1 | sed -e 's/[^a-zA-Z0-9]/_/g')
|
||||
|
||||
%/trace.h: %/trace.h-timestamp
|
||||
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
||||
%/trace.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y)
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--group=$(call trace-group-name,$@) \
|
||||
--format=h \
|
||||
--backends=$(TRACE_BACKENDS) \
|
||||
$< > $@,"GEN","$(@:%-timestamp=%)")
|
||||
|
||||
%/trace.c: %/trace.c-timestamp
|
||||
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
||||
%/trace.c-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y)
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--group=$(call trace-group-name,$@) \
|
||||
--format=c \
|
||||
--backends=$(TRACE_BACKENDS) \
|
||||
$< > $@,"GEN","$(@:%-timestamp=%)")
|
||||
|
||||
%/trace-ust.h: %/trace-ust.h-timestamp
|
||||
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
||||
%/trace-ust.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y)
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--group=$(call trace-group-name,$@) \
|
||||
--format=ust-events-h \
|
||||
--backends=$(TRACE_BACKENDS) \
|
||||
$< > $@,"GEN","$(@:%-timestamp=%)")
|
||||
|
||||
%/trace-dtrace.dtrace: %/trace-dtrace.dtrace-timestamp
|
||||
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
||||
%/trace-dtrace.dtrace-timestamp: $(SRC_PATH)/%/trace-events $(BUILD_DIR)/config-host.mak $(tracetool-y)
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--group=$(call trace-group-name,$@) \
|
||||
--format=d \
|
||||
--backends=$(TRACE_BACKENDS) \
|
||||
$< > $@,"GEN","$(@:%-timestamp=%)")
|
||||
|
||||
%/trace-dtrace.h: %/trace-dtrace.dtrace $(tracetool-y)
|
||||
$(call quiet-command,dtrace -o $@ -h -s $<, "GEN","$@")
|
||||
|
||||
%/trace-dtrace.o: %/trace-dtrace.dtrace $(tracetool-y)
|
||||
|
||||
|
||||
trace-root.h: trace-root.h-timestamp
|
||||
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
||||
trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--group=root \
|
||||
--format=h \
|
||||
--backends=$(TRACE_BACKENDS) \
|
||||
$< > $@,"GEN","$(@:%-timestamp=%)")
|
||||
|
||||
trace-root.c: trace-root.c-timestamp
|
||||
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
||||
trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--group=root \
|
||||
--format=c \
|
||||
--backends=$(TRACE_BACKENDS) \
|
||||
$< > $@,"GEN","$(@:%-timestamp=%)")
|
||||
|
||||
trace-ust-root.h: trace-ust-root.h-timestamp
|
||||
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
||||
trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y)
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--group=root \
|
||||
--format=ust-events-h \
|
||||
--backends=$(TRACE_BACKENDS) \
|
||||
$< > $@,"GEN","$(@:%-timestamp=%)")
|
||||
|
||||
trace-ust-all.h: trace-ust-all.h-timestamp
|
||||
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
||||
trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y)
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--group=all \
|
||||
--format=ust-events-h \
|
||||
--backends=$(TRACE_BACKENDS) \
|
||||
$(trace-events-files) > $@,"GEN","$(@:%-timestamp=%)")
|
||||
|
||||
trace-ust-all.c: trace-ust-all.c-timestamp
|
||||
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
||||
trace-ust-all.c-timestamp: $(trace-events-files) $(tracetool-y)
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--group=all \
|
||||
--format=ust-events-c \
|
||||
--backends=$(TRACE_BACKENDS) \
|
||||
$(trace-events-files) > $@,"GEN","$(@:%-timestamp=%)")
|
||||
|
||||
trace-dtrace-root.dtrace: trace-dtrace-root.dtrace-timestamp
|
||||
@cmp $< $@ >/dev/null 2>&1 || cp $< $@
|
||||
trace-dtrace-root.dtrace-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak $(tracetool-y)
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--group=root \
|
||||
--format=d \
|
||||
--backends=$(TRACE_BACKENDS) \
|
||||
$< > $@,"GEN","$(@:%-timestamp=%)")
|
||||
|
||||
trace-dtrace-root.h: trace-dtrace-root.dtrace
|
||||
$(call quiet-command,dtrace -o $@ -h -s $<, "GEN","$@")
|
||||
|
||||
trace-dtrace-root.o: trace-dtrace-root.dtrace
|
||||
|
||||
# Don't try to regenerate Makefile or configure
|
||||
# We don't generate any of them
|
||||
Makefile: ;
|
||||
configure: ;
|
||||
|
||||
.PHONY: all clean cscope distclean html info install install-doc \
|
||||
pdf txt recurse-all speed test dist msi FORCE
|
||||
.PHONY: all clean cscope distclean dvi html info install install-doc \
|
||||
pdf recurse-all speed test dist msi FORCE
|
||||
|
||||
$(call set-vpath, $(SRC_PATH))
|
||||
|
||||
@@ -201,9 +90,7 @@ LIBS+=-lz $(LIBS_TOOLS)
|
||||
HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF)
|
||||
|
||||
ifdef BUILD_DOCS
|
||||
DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
|
||||
DOCS+=docs/qemu-qmp-ref.html docs/qemu-qmp-ref.txt docs/qemu-qmp-ref.7
|
||||
DOCS+=docs/qemu-ga-ref.html docs/qemu-ga-ref.txt docs/qemu-ga-ref.7
|
||||
DOCS=qemu-doc.html qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
|
||||
ifdef CONFIG_VIRTFS
|
||||
DOCS+=fsdev/virtfs-proxy-helper.1
|
||||
endif
|
||||
@@ -258,12 +145,10 @@ endif
|
||||
|
||||
dummy := $(call unnest-vars,, \
|
||||
stub-obj-y \
|
||||
chardev-obj-y \
|
||||
util-obj-y \
|
||||
qga-obj-y \
|
||||
ivshmem-client-obj-y \
|
||||
ivshmem-server-obj-y \
|
||||
libvhost-user-obj-y \
|
||||
qga-vss-dll-obj-y \
|
||||
block-obj-y \
|
||||
block-obj-m \
|
||||
@@ -272,8 +157,7 @@ dummy := $(call unnest-vars,, \
|
||||
qom-obj-y \
|
||||
io-obj-y \
|
||||
common-obj-y \
|
||||
common-obj-m \
|
||||
trace-obj-y)
|
||||
common-obj-m)
|
||||
|
||||
ifneq ($(wildcard config-host.mak),)
|
||||
include $(SRC_PATH)/tests/Makefile.include
|
||||
@@ -299,11 +183,7 @@ qemu-version.h: FORCE
|
||||
printf '""\n'; \
|
||||
fi; \
|
||||
fi) > $@.tmp)
|
||||
$(call quiet-command, if ! cmp -s $@ $@.tmp; then \
|
||||
mv $@.tmp $@; \
|
||||
else \
|
||||
rm $@.tmp; \
|
||||
fi)
|
||||
$(call quiet-command, cmp -s $@ $@.tmp || mv $@.tmp $@)
|
||||
|
||||
config-host.h: config-host.h-timestamp
|
||||
config-host.h-timestamp: config-host.mak
|
||||
@@ -340,8 +220,7 @@ subdir-dtc:dtc/libfdt dtc/tests
|
||||
dtc/%:
|
||||
mkdir -p $@
|
||||
|
||||
$(SUBDIR_RULES): libqemuutil.a libqemustub.a $(common-obj-y) $(chardev-obj-y) \
|
||||
$(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY)) $(trace-obj-y)
|
||||
$(SUBDIR_RULES): libqemuutil.a libqemustub.a $(common-obj-y) $(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY))
|
||||
|
||||
ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
|
||||
# Only keep -O and -g cflags
|
||||
@@ -352,10 +231,12 @@ ALL_SUBDIRS=$(TARGET_DIRS) $(patsubst %,pc-bios/%, $(ROMS))
|
||||
|
||||
recurse-all: $(SUBDIR_RULES) $(ROMSUBDIR_RULES)
|
||||
|
||||
$(BUILD_DIR)/version.o: $(SRC_PATH)/version.rc config-host.h
|
||||
$(BUILD_DIR)/version.o: $(SRC_PATH)/version.rc config-host.h | $(BUILD_DIR)/version.lo
|
||||
$(call quiet-command,$(WINDRES) -I$(BUILD_DIR) -o $@ $<,"RC","version.o")
|
||||
$(BUILD_DIR)/version.lo: $(SRC_PATH)/version.rc config-host.h
|
||||
$(call quiet-command,$(WINDRES) -I$(BUILD_DIR) -o $@ $<,"RC","version.lo")
|
||||
|
||||
Makefile: $(version-obj-y)
|
||||
Makefile: $(version-obj-y) $(version-lobj-y)
|
||||
|
||||
######################################################################
|
||||
# Build libraries
|
||||
@@ -365,17 +246,15 @@ libqemuutil.a: $(util-obj-y)
|
||||
|
||||
######################################################################
|
||||
|
||||
COMMON_LDADDS = $(trace-obj-y) libqemuutil.a libqemustub.a
|
||||
|
||||
qemu-img.o: qemu-img-cmds.h
|
||||
|
||||
qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
|
||||
qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
|
||||
qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
|
||||
qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
|
||||
qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
|
||||
qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
|
||||
|
||||
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
|
||||
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o libqemuutil.a libqemustub.a
|
||||
|
||||
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS)
|
||||
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o libqemuutil.a libqemustub.a
|
||||
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
|
||||
|
||||
qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
|
||||
@@ -387,7 +266,6 @@ qemu-ga$(EXESUF): QEMU_CFLAGS += -I qga/qapi-generated
|
||||
gen-out-type = $(subst .,-,$(suffix $@))
|
||||
|
||||
qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py
|
||||
qapi-py += $(SRC_PATH)/scripts/qapi2texi.py
|
||||
|
||||
qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h :\
|
||||
$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
|
||||
@@ -440,7 +318,7 @@ $(qapi-modules) $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py)
|
||||
QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h)
|
||||
$(qga-obj-y) qemu-ga.o: $(QGALIB_GEN)
|
||||
|
||||
qemu-ga$(EXESUF): $(qga-obj-y) $(COMMON_LDADDS)
|
||||
qemu-ga$(EXESUF): $(qga-obj-y) libqemuutil.a libqemustub.a
|
||||
$(call LINK, $^)
|
||||
|
||||
ifdef QEMU_GA_MSI_ENABLED
|
||||
@@ -465,9 +343,9 @@ ifneq ($(EXESUF),)
|
||||
qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI)
|
||||
endif
|
||||
|
||||
ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS)
|
||||
ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) libqemuutil.a libqemustub.a
|
||||
$(call LINK, $^)
|
||||
ivshmem-server$(EXESUF): $(ivshmem-server-obj-y) $(COMMON_LDADDS)
|
||||
ivshmem-server$(EXESUF): $(ivshmem-server-obj-y) libqemuutil.a libqemustub.a
|
||||
$(call LINK, $^)
|
||||
|
||||
module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak
|
||||
@@ -480,9 +358,10 @@ clean:
|
||||
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
|
||||
rm -f qemu-options.def
|
||||
rm -f *.msi
|
||||
find . \( -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f -exec rm {} +
|
||||
find . \( -name '*.l[oa]' -o -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f -exec rm {} +
|
||||
rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
|
||||
rm -f fsdev/*.pod
|
||||
rm -rf .libs */.libs
|
||||
rm -f qemu-img-cmds.h
|
||||
rm -f ui/shader/*-vert.h ui/shader/*-frag.h
|
||||
@# May not be present in GENERATED_HEADERS
|
||||
@@ -510,17 +389,12 @@ distclean: clean
|
||||
rm -f config-all-devices.mak config-all-disas.mak config.status
|
||||
rm -f po/*.mo tests/qemu-iotests/common.env
|
||||
rm -f roms/seabios/config.mak roms/vgabios/config.mak
|
||||
rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps
|
||||
rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps qemu-doc.dvi
|
||||
rm -f qemu-doc.fn qemu-doc.fns qemu-doc.info qemu-doc.ky qemu-doc.kys
|
||||
rm -f qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp
|
||||
rm -f qemu-doc.vr qemu-doc.txt
|
||||
rm -f qemu-doc.vr
|
||||
rm -f config.log
|
||||
rm -f linux-headers/asm
|
||||
rm -f qemu-ga-qapi.texi qemu-qapi.texi
|
||||
rm -f docs/qemu-qmp-ref.7 docs/qemu-ga-ref.7
|
||||
rm -f docs/qemu-qmp-ref.txt docs/qemu-ga-ref.txt
|
||||
rm -f docs/qemu-qmp-ref.pdf docs/qemu-ga-ref.pdf
|
||||
rm -f docs/qemu-qmp-ref.html docs/qemu-ga-ref.html
|
||||
for d in $(TARGET_DIRS); do \
|
||||
rm -rf $$d || exit 1 ; \
|
||||
done
|
||||
@@ -557,14 +431,10 @@ endif
|
||||
install-doc: $(DOCS)
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) docs/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) docs/qemu-qmp-ref.txt "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) $(SRC_PATH)/docs/qmp-commands.txt "$(DESTDIR)$(qemu_docdir)"
|
||||
ifdef CONFIG_POSIX
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7"
|
||||
$(INSTALL_DATA) docs/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
|
||||
ifneq ($(TOOLS),)
|
||||
$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
|
||||
@@ -572,9 +442,6 @@ ifneq ($(TOOLS),)
|
||||
endif
|
||||
ifneq (,$(findstring qemu-ga,$(TOOLS)))
|
||||
$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
|
||||
$(INSTALL_DATA) docs/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) docs/qemu-ga-ref.txt "$(DESTDIR)$(qemu_docdir)"
|
||||
$(INSTALL_DATA) docs/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7"
|
||||
endif
|
||||
endif
|
||||
ifdef CONFIG_VIRTFS
|
||||
@@ -593,7 +460,7 @@ endif
|
||||
endif
|
||||
|
||||
|
||||
install: all $(if $(BUILD_DOCS),install-doc) $(BUILD_DIR)/trace-events-all \
|
||||
install: all $(if $(BUILD_DOCS),install-doc) \
|
||||
install-datadir install-localstatedir
|
||||
ifneq ($(TOOLS),)
|
||||
$(call install-prog,$(subst qemu-ga,qemu-ga$(EXESUF),$(TOOLS)),$(DESTDIR)$(bindir))
|
||||
@@ -663,22 +530,20 @@ ui/console-gl.o: $(SRC_PATH)/ui/console-gl.c \
|
||||
|
||||
# documentation
|
||||
MAKEINFO=makeinfo
|
||||
MAKEINFOFLAGS=--no-split --number-sections -D 'VERSION $(VERSION)'
|
||||
TEXIFLAG=$(if $(V),,--quiet) --command='@set VERSION $(VERSION)'
|
||||
MAKEINFOFLAGS=--no-headers --no-split --number-sections
|
||||
TEXIFLAG=$(if $(V),,--quiet)
|
||||
%.dvi: %.texi
|
||||
$(call quiet-command,texi2dvi $(TEXIFLAG) -I . $<,"GEN","$@")
|
||||
|
||||
%.html: %.texi
|
||||
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \
|
||||
--html $< -o $@,"GEN","$@")
|
||||
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --html $< -o $@, \
|
||||
"GEN","$@")
|
||||
|
||||
%.info: %.texi
|
||||
$(call quiet-command,$(MAKEINFO) $(MAKEINFOFLAGS) $< -o $@,"GEN","$@")
|
||||
|
||||
%.txt: %.texi
|
||||
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \
|
||||
--plaintext $< -o $@,"GEN","$@")
|
||||
$(call quiet-command,$(MAKEINFO) $< -o $@,"GEN","$@")
|
||||
|
||||
%.pdf: %.texi
|
||||
$(call quiet-command,texi2pdf $(TEXIFLAG) -I $(SRC_PATH) -I . $< -o $@,"GEN","$@")
|
||||
$(call quiet-command,texi2pdf $(TEXIFLAG) -I . $<,"GEN","$@")
|
||||
|
||||
qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
|
||||
@@ -692,36 +557,47 @@ qemu-monitor-info.texi: $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxt
|
||||
qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
|
||||
|
||||
qemu-qapi.texi: $(qapi-modules) $(qapi-py)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN","$@")
|
||||
|
||||
qemu-ga-qapi.texi: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN","$@")
|
||||
|
||||
qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi qemu-monitor-info.texi
|
||||
$(call quiet-command, \
|
||||
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu.pod && \
|
||||
$(POD2MAN) --section=1 --center=" " --release=" " qemu.pod > $@, \
|
||||
"GEN","$@")
|
||||
qemu.1: qemu-option-trace.texi
|
||||
|
||||
qemu-img.1: qemu-img.texi qemu-option-trace.texi qemu-img-cmds.texi
|
||||
$(call quiet-command, \
|
||||
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-img.pod && \
|
||||
$(POD2MAN) --section=1 --center=" " --release=" " qemu-img.pod > $@, \
|
||||
"GEN","$@")
|
||||
|
||||
fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi
|
||||
$(call quiet-command, \
|
||||
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< fsdev/virtfs-proxy-helper.pod && \
|
||||
$(POD2MAN) --section=1 --center=" " --release=" " fsdev/virtfs-proxy-helper.pod > $@, \
|
||||
"GEN","$@")
|
||||
|
||||
qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi
|
||||
$(call quiet-command, \
|
||||
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-nbd.pod && \
|
||||
$(POD2MAN) --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \
|
||||
"GEN","$@")
|
||||
|
||||
qemu-ga.8: qemu-ga.texi
|
||||
$(call quiet-command, \
|
||||
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-ga.pod && \
|
||||
$(POD2MAN) --section=8 --center=" " --release=" " qemu-ga.pod > $@, \
|
||||
"GEN","$@")
|
||||
|
||||
html: qemu-doc.html docs/qemu-qmp-ref.html docs/qemu-ga-ref.html
|
||||
info: qemu-doc.info docs/qemu-qmp-ref.info docs/qemu-ga-ref.info
|
||||
pdf: qemu-doc.pdf docs/qemu-qmp-ref.pdf docs/qemu-ga-ref.pdf
|
||||
txt: qemu-doc.txt docs/qemu-qmp-ref.txt docs/qemu-ga-ref.txt
|
||||
dvi: qemu-doc.dvi
|
||||
html: qemu-doc.html
|
||||
info: qemu-doc.info
|
||||
pdf: qemu-doc.pdf
|
||||
|
||||
qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \
|
||||
qemu-doc.dvi qemu-doc.html qemu-doc.info qemu-doc.pdf: \
|
||||
qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \
|
||||
qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
|
||||
qemu-monitor-info.texi
|
||||
|
||||
docs/qemu-ga-ref.dvi docs/qemu-ga-ref.html docs/qemu-ga-ref.info docs/qemu-ga-ref.pdf docs/qemu-ga-ref.txt docs/qemu-ga-ref.7: \
|
||||
docs/qemu-ga-ref.texi qemu-ga-qapi.texi
|
||||
|
||||
docs/qemu-qmp-ref.dvi docs/qemu-qmp-ref.html docs/qemu-qmp-ref.info docs/qemu-qmp-ref.pdf docs/qemu-qmp-ref.txt docs/qemu-qmp-ref.7: \
|
||||
docs/qemu-qmp-ref.texi qemu-qapi.texi
|
||||
|
||||
|
||||
ifdef CONFIG_WIN32
|
||||
|
||||
INSTALLER = qemu-setup-$(VERSION)$(EXESUF)
|
||||
@@ -784,10 +660,6 @@ ifneq ($(filter-out $(UNCHECKED_GOALS),$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fa
|
||||
Makefile: $(GENERATED_HEADERS)
|
||||
endif
|
||||
|
||||
.SECONDARY: $(TRACE_HEADERS) $(TRACE_HEADERS:%=%-timestamp) \
|
||||
$(TRACE_SOURCES) $(TRACE_SOURCES:%=%-timestamp) \
|
||||
$(TRACE_DTRACE) $(TRACE_DTRACE:%=%-timestamp)
|
||||
|
||||
# Include automatically generated dependency files
|
||||
# Dependencies in Makefile.objs files come from our recursive subdir rules
|
||||
-include $(wildcard *.d tests/*.d)
|
||||
@@ -818,7 +690,7 @@ help:
|
||||
@echo ' docker - Help about targets running tests inside Docker containers'
|
||||
@echo ''
|
||||
@echo 'Documentation targets:'
|
||||
@echo ' html info pdf txt'
|
||||
@echo ' dvi html info pdf'
|
||||
@echo ' - Build documentation in specified format'
|
||||
@echo ''
|
||||
ifdef CONFIG_WIN32
|
||||
|
107
Makefile.objs
107
Makefile.objs
@@ -4,8 +4,6 @@ stub-obj-y = stubs/ crypto/
|
||||
util-obj-y = util/ qobject/ qapi/
|
||||
util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o
|
||||
|
||||
chardev-obj-y = chardev/
|
||||
|
||||
#######################################################################
|
||||
# block-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
@@ -53,7 +51,8 @@ common-obj-$(CONFIG_POSIX) += os-posix.o
|
||||
common-obj-$(CONFIG_LINUX) += fsdev/
|
||||
|
||||
common-obj-y += migration/
|
||||
common-obj-y += page_cache.o #aio.o
|
||||
common-obj-y += qemu-char.o #aio.o
|
||||
common-obj-y += page_cache.o
|
||||
|
||||
common-obj-$(CONFIG_SPICE) += spice-qemu-char.o
|
||||
|
||||
@@ -98,6 +97,7 @@ common-obj-y += disas/
|
||||
######################################################################
|
||||
# Resource file for Windows executables
|
||||
version-obj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.o
|
||||
version-lobj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.lo
|
||||
|
||||
######################################################################
|
||||
# tracing
|
||||
@@ -116,61 +116,50 @@ qga-vss-dll-obj-y = qga/
|
||||
# contrib
|
||||
ivshmem-client-obj-y = contrib/ivshmem-client/
|
||||
ivshmem-server-obj-y = contrib/ivshmem-server/
|
||||
libvhost-user-obj-y = contrib/libvhost-user/
|
||||
|
||||
|
||||
######################################################################
|
||||
trace-events-subdirs =
|
||||
trace-events-subdirs += util
|
||||
trace-events-subdirs += crypto
|
||||
trace-events-subdirs += io
|
||||
trace-events-subdirs += migration
|
||||
trace-events-subdirs += block
|
||||
trace-events-subdirs += hw/block
|
||||
trace-events-subdirs += hw/block/dataplane
|
||||
trace-events-subdirs += hw/char
|
||||
trace-events-subdirs += hw/intc
|
||||
trace-events-subdirs += hw/net
|
||||
trace-events-subdirs += hw/virtio
|
||||
trace-events-subdirs += hw/audio
|
||||
trace-events-subdirs += hw/misc
|
||||
trace-events-subdirs += hw/usb
|
||||
trace-events-subdirs += hw/scsi
|
||||
trace-events-subdirs += hw/nvram
|
||||
trace-events-subdirs += hw/display
|
||||
trace-events-subdirs += hw/input
|
||||
trace-events-subdirs += hw/timer
|
||||
trace-events-subdirs += hw/dma
|
||||
trace-events-subdirs += hw/sparc
|
||||
trace-events-subdirs += hw/sd
|
||||
trace-events-subdirs += hw/isa
|
||||
trace-events-subdirs += hw/mem
|
||||
trace-events-subdirs += hw/i386
|
||||
trace-events-subdirs += hw/i386/xen
|
||||
trace-events-subdirs += hw/9pfs
|
||||
trace-events-subdirs += hw/ppc
|
||||
trace-events-subdirs += hw/pci
|
||||
trace-events-subdirs += hw/s390x
|
||||
trace-events-subdirs += hw/vfio
|
||||
trace-events-subdirs += hw/acpi
|
||||
trace-events-subdirs += hw/arm
|
||||
trace-events-subdirs += hw/alpha
|
||||
trace-events-subdirs += hw/xen
|
||||
trace-events-subdirs += ui
|
||||
trace-events-subdirs += audio
|
||||
trace-events-subdirs += net
|
||||
trace-events-subdirs += target/arm
|
||||
trace-events-subdirs += target/i386
|
||||
trace-events-subdirs += target/sparc
|
||||
trace-events-subdirs += target/s390x
|
||||
trace-events-subdirs += target/ppc
|
||||
trace-events-subdirs += qom
|
||||
trace-events-subdirs += linux-user
|
||||
trace-events-subdirs += qapi
|
||||
|
||||
trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events)
|
||||
|
||||
trace-obj-y = trace-root.o
|
||||
trace-obj-y += $(trace-events-subdirs:%=%/trace.o)
|
||||
trace-obj-$(CONFIG_TRACE_UST) += trace-ust-all.o
|
||||
trace-obj-$(CONFIG_TRACE_DTRACE) += trace-dtrace-root.o
|
||||
trace-obj-$(CONFIG_TRACE_DTRACE) += $(trace-events-subdirs:%=%/trace-dtrace.o)
|
||||
trace-events-y = trace-events
|
||||
trace-events-y += util/trace-events
|
||||
trace-events-y += crypto/trace-events
|
||||
trace-events-y += io/trace-events
|
||||
trace-events-y += migration/trace-events
|
||||
trace-events-y += block/trace-events
|
||||
trace-events-y += hw/block/trace-events
|
||||
trace-events-y += hw/char/trace-events
|
||||
trace-events-y += hw/intc/trace-events
|
||||
trace-events-y += hw/net/trace-events
|
||||
trace-events-y += hw/virtio/trace-events
|
||||
trace-events-y += hw/audio/trace-events
|
||||
trace-events-y += hw/misc/trace-events
|
||||
trace-events-y += hw/usb/trace-events
|
||||
trace-events-y += hw/scsi/trace-events
|
||||
trace-events-y += hw/nvram/trace-events
|
||||
trace-events-y += hw/display/trace-events
|
||||
trace-events-y += hw/input/trace-events
|
||||
trace-events-y += hw/timer/trace-events
|
||||
trace-events-y += hw/dma/trace-events
|
||||
trace-events-y += hw/sparc/trace-events
|
||||
trace-events-y += hw/sd/trace-events
|
||||
trace-events-y += hw/isa/trace-events
|
||||
trace-events-y += hw/mem/trace-events
|
||||
trace-events-y += hw/i386/trace-events
|
||||
trace-events-y += hw/9pfs/trace-events
|
||||
trace-events-y += hw/ppc/trace-events
|
||||
trace-events-y += hw/pci/trace-events
|
||||
trace-events-y += hw/s390x/trace-events
|
||||
trace-events-y += hw/vfio/trace-events
|
||||
trace-events-y += hw/acpi/trace-events
|
||||
trace-events-y += hw/arm/trace-events
|
||||
trace-events-y += hw/alpha/trace-events
|
||||
trace-events-y += ui/trace-events
|
||||
trace-events-y += audio/trace-events
|
||||
trace-events-y += net/trace-events
|
||||
trace-events-y += target-arm/trace-events
|
||||
trace-events-y += target-i386/trace-events
|
||||
trace-events-y += target-sparc/trace-events
|
||||
trace-events-y += target-s390x/trace-events
|
||||
trace-events-y += target-ppc/trace-events
|
||||
trace-events-y += qom/trace-events
|
||||
trace-events-y += linux-user/trace-events
|
||||
trace-events-y += qapi/trace-events
|
||||
|
@@ -11,7 +11,7 @@ $(call set-vpath, $(SRC_PATH):$(BUILD_DIR))
|
||||
ifdef CONFIG_LINUX
|
||||
QEMU_CFLAGS += -I../linux-headers
|
||||
endif
|
||||
QEMU_CFLAGS += -I.. -I$(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H
|
||||
QEMU_CFLAGS += -I.. -I$(SRC_PATH)/target-$(TARGET_BASE_ARCH) -DNEED_CPU_H
|
||||
|
||||
QEMU_CFLAGS+=-I$(SRC_PATH)/include
|
||||
|
||||
@@ -50,7 +50,6 @@ endif
|
||||
|
||||
$(QEMU_PROG).stp-installed: $(BUILD_DIR)/trace-events-all
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--group=all \
|
||||
--format=stap \
|
||||
--backends=$(TRACE_BACKENDS) \
|
||||
--binary=$(bindir)/$(QEMU_PROG) \
|
||||
@@ -60,7 +59,6 @@ $(QEMU_PROG).stp-installed: $(BUILD_DIR)/trace-events-all
|
||||
|
||||
$(QEMU_PROG).stp: $(BUILD_DIR)/trace-events-all
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--group=all \
|
||||
--format=stap \
|
||||
--backends=$(TRACE_BACKENDS) \
|
||||
--binary=$(realpath .)/$(QEMU_PROG) \
|
||||
@@ -70,7 +68,6 @@ $(QEMU_PROG).stp: $(BUILD_DIR)/trace-events-all
|
||||
|
||||
$(QEMU_PROG)-simpletrace.stp: $(BUILD_DIR)/trace-events-all
|
||||
$(call quiet-command,$(TRACETOOL) \
|
||||
--group=all \
|
||||
--format=simpletrace-stap \
|
||||
--backends=$(TRACE_BACKENDS) \
|
||||
--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \
|
||||
@@ -79,7 +76,6 @@ $(QEMU_PROG)-simpletrace.stp: $(BUILD_DIR)/trace-events-all
|
||||
else
|
||||
stap:
|
||||
endif
|
||||
.PHONY: stap
|
||||
|
||||
all: $(PROGS) stap
|
||||
|
||||
@@ -96,11 +92,10 @@ obj-$(CONFIG_TCG_INTERPRETER) += tci.o
|
||||
obj-y += tcg/tcg-common.o
|
||||
obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o
|
||||
obj-y += fpu/softfloat.o
|
||||
obj-y += target/$(TARGET_BASE_ARCH)/
|
||||
obj-y += target-$(TARGET_BASE_ARCH)/
|
||||
obj-y += disas.o
|
||||
obj-y += tcg-runtime.o
|
||||
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
|
||||
obj-$(call lnot,$(CONFIG_HAX)) += hax-stub.o
|
||||
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
|
||||
|
||||
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/decContext.o
|
||||
@@ -119,7 +114,7 @@ QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) \
|
||||
-I$(SRC_PATH)/linux-user
|
||||
|
||||
obj-y += linux-user/
|
||||
obj-y += gdbstub.o thunk.o user-exec.o user-exec-stub.o
|
||||
obj-y += gdbstub.o thunk.o user-exec.o
|
||||
|
||||
endif #CONFIG_LINUX_USER
|
||||
|
||||
@@ -132,7 +127,7 @@ QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ABI_DIR) \
|
||||
-I$(SRC_PATH)/bsd-user/$(HOST_VARIANT_DIR)
|
||||
|
||||
obj-y += bsd-user/
|
||||
obj-y += gdbstub.o user-exec.o user-exec-stub.o
|
||||
obj-y += gdbstub.o user-exec.o
|
||||
|
||||
endif #CONFIG_BSD_USER
|
||||
|
||||
@@ -175,36 +170,31 @@ all-obj-y := $(obj-y)
|
||||
target-obj-y :=
|
||||
block-obj-y :=
|
||||
common-obj-y :=
|
||||
chardev-obj-y :=
|
||||
include $(SRC_PATH)/Makefile.objs
|
||||
dummy := $(call unnest-vars,,target-obj-y)
|
||||
target-obj-y-save := $(target-obj-y)
|
||||
dummy := $(call unnest-vars,.., \
|
||||
block-obj-y \
|
||||
block-obj-m \
|
||||
chardev-obj-y \
|
||||
crypto-obj-y \
|
||||
crypto-aes-obj-y \
|
||||
qom-obj-y \
|
||||
io-obj-y \
|
||||
common-obj-y \
|
||||
common-obj-m \
|
||||
trace-obj-y)
|
||||
common-obj-m)
|
||||
target-obj-y := $(target-obj-y-save)
|
||||
all-obj-y += $(common-obj-y)
|
||||
all-obj-y += $(target-obj-y)
|
||||
all-obj-y += $(qom-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y)
|
||||
all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
|
||||
|
||||
$(QEMU_PROG_BUILD): config-devices.mak
|
||||
|
||||
COMMON_LDADDS = $(trace-obj-y) ../libqemuutil.a ../libqemustub.a
|
||||
|
||||
# build either PROG or PROGW
|
||||
$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS)
|
||||
$(QEMU_PROG_BUILD): $(all-obj-y) ../libqemuutil.a ../libqemustub.a
|
||||
$(call LINK, $(filter-out %.mak, $^))
|
||||
ifdef CONFIG_DARWIN
|
||||
$(call quiet-command,Rez -append $(SRC_PATH)/pc-bios/qemu.rsrc -o $@,"REZ","$(TARGET_DIR)$@")
|
||||
|
1
README
1
README
@@ -45,7 +45,6 @@ of other UNIX targets. The simple steps to build QEMU are:
|
||||
Additional information can also be found online via the QEMU website:
|
||||
|
||||
http://qemu-project.org/Hosts/Linux
|
||||
http://qemu-project.org/Hosts/Mac
|
||||
http://qemu-project.org/Hosts/W32
|
||||
|
||||
|
||||
|
397
aio-posix.c
397
aio-posix.c
@@ -16,10 +16,8 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block.h"
|
||||
#include "qemu/rcu_queue.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "trace-root.h"
|
||||
#ifdef CONFIG_EPOLL_CREATE1
|
||||
#include <sys/epoll.h>
|
||||
#endif
|
||||
@@ -29,9 +27,6 @@ struct AioHandler
|
||||
GPollFD pfd;
|
||||
IOHandler *io_read;
|
||||
IOHandler *io_write;
|
||||
AioPollFn *io_poll;
|
||||
IOHandler *io_poll_begin;
|
||||
IOHandler *io_poll_end;
|
||||
int deleted;
|
||||
void *opaque;
|
||||
bool is_external;
|
||||
@@ -66,7 +61,7 @@ static bool aio_epoll_try_enable(AioContext *ctx)
|
||||
AioHandler *node;
|
||||
struct epoll_event event;
|
||||
|
||||
QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) {
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
int r;
|
||||
if (node->deleted || !node->pfd.events) {
|
||||
continue;
|
||||
@@ -205,61 +200,47 @@ void aio_set_fd_handler(AioContext *ctx,
|
||||
bool is_external,
|
||||
IOHandler *io_read,
|
||||
IOHandler *io_write,
|
||||
AioPollFn *io_poll,
|
||||
void *opaque)
|
||||
{
|
||||
AioHandler *node;
|
||||
bool is_new = false;
|
||||
bool deleted = false;
|
||||
|
||||
qemu_lockcnt_lock(&ctx->list_lock);
|
||||
|
||||
node = find_aio_handler(ctx, fd);
|
||||
|
||||
/* Are we deleting the fd handler? */
|
||||
if (!io_read && !io_write && !io_poll) {
|
||||
if (!io_read && !io_write) {
|
||||
if (node == NULL) {
|
||||
qemu_lockcnt_unlock(&ctx->list_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
g_source_remove_poll(&ctx->source, &node->pfd);
|
||||
|
||||
/* If the lock is held, just mark the node as deleted */
|
||||
if (qemu_lockcnt_count(&ctx->list_lock)) {
|
||||
if (ctx->walking_handlers) {
|
||||
node->deleted = 1;
|
||||
node->pfd.revents = 0;
|
||||
} else {
|
||||
/* Otherwise, delete it for real. We can't just mark it as
|
||||
* deleted because deleted nodes are only cleaned up while
|
||||
* no one is walking the handlers list.
|
||||
* deleted because deleted nodes are only cleaned up after
|
||||
* releasing the walking_handlers lock.
|
||||
*/
|
||||
QLIST_REMOVE(node, node);
|
||||
deleted = true;
|
||||
}
|
||||
|
||||
if (!node->io_poll) {
|
||||
ctx->poll_disable_cnt--;
|
||||
}
|
||||
} else {
|
||||
if (node == NULL) {
|
||||
/* Alloc and insert if it's not already there */
|
||||
node = g_new0(AioHandler, 1);
|
||||
node->pfd.fd = fd;
|
||||
QLIST_INSERT_HEAD_RCU(&ctx->aio_handlers, node, node);
|
||||
QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node);
|
||||
|
||||
g_source_add_poll(&ctx->source, &node->pfd);
|
||||
is_new = true;
|
||||
|
||||
ctx->poll_disable_cnt += !io_poll;
|
||||
} else {
|
||||
ctx->poll_disable_cnt += !io_poll - !node->io_poll;
|
||||
}
|
||||
|
||||
/* Update handler with latest information */
|
||||
node->io_read = io_read;
|
||||
node->io_write = io_write;
|
||||
node->io_poll = io_poll;
|
||||
node->opaque = opaque;
|
||||
node->is_external = is_external;
|
||||
|
||||
@@ -268,133 +249,72 @@ void aio_set_fd_handler(AioContext *ctx,
|
||||
}
|
||||
|
||||
aio_epoll_update(ctx, node, is_new);
|
||||
qemu_lockcnt_unlock(&ctx->list_lock);
|
||||
aio_notify(ctx);
|
||||
|
||||
if (deleted) {
|
||||
g_free(node);
|
||||
}
|
||||
}
|
||||
|
||||
void aio_set_fd_poll(AioContext *ctx, int fd,
|
||||
IOHandler *io_poll_begin,
|
||||
IOHandler *io_poll_end)
|
||||
{
|
||||
AioHandler *node = find_aio_handler(ctx, fd);
|
||||
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
node->io_poll_begin = io_poll_begin;
|
||||
node->io_poll_end = io_poll_end;
|
||||
}
|
||||
|
||||
void aio_set_event_notifier(AioContext *ctx,
|
||||
EventNotifier *notifier,
|
||||
bool is_external,
|
||||
EventNotifierHandler *io_read,
|
||||
AioPollFn *io_poll)
|
||||
EventNotifierHandler *io_read)
|
||||
{
|
||||
aio_set_fd_handler(ctx, event_notifier_get_fd(notifier), is_external,
|
||||
(IOHandler *)io_read, NULL, io_poll, notifier);
|
||||
aio_set_fd_handler(ctx, event_notifier_get_fd(notifier),
|
||||
is_external, (IOHandler *)io_read, NULL, notifier);
|
||||
}
|
||||
|
||||
void aio_set_event_notifier_poll(AioContext *ctx,
|
||||
EventNotifier *notifier,
|
||||
EventNotifierHandler *io_poll_begin,
|
||||
EventNotifierHandler *io_poll_end)
|
||||
{
|
||||
aio_set_fd_poll(ctx, event_notifier_get_fd(notifier),
|
||||
(IOHandler *)io_poll_begin,
|
||||
(IOHandler *)io_poll_end);
|
||||
}
|
||||
|
||||
static void poll_set_started(AioContext *ctx, bool started)
|
||||
{
|
||||
AioHandler *node;
|
||||
|
||||
if (started == ctx->poll_started) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->poll_started = started;
|
||||
|
||||
qemu_lockcnt_inc(&ctx->list_lock);
|
||||
QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) {
|
||||
IOHandler *fn;
|
||||
|
||||
if (node->deleted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (started) {
|
||||
fn = node->io_poll_begin;
|
||||
} else {
|
||||
fn = node->io_poll_end;
|
||||
}
|
||||
|
||||
if (fn) {
|
||||
fn(node->opaque);
|
||||
}
|
||||
}
|
||||
qemu_lockcnt_dec(&ctx->list_lock);
|
||||
}
|
||||
|
||||
|
||||
bool aio_prepare(AioContext *ctx)
|
||||
{
|
||||
/* Poll mode cannot be used with glib's event loop, disable it. */
|
||||
poll_set_started(ctx, false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool aio_pending(AioContext *ctx)
|
||||
{
|
||||
AioHandler *node;
|
||||
bool result = false;
|
||||
|
||||
/*
|
||||
* We have to walk very carefully in case aio_set_fd_handler is
|
||||
* called while we're walking.
|
||||
*/
|
||||
qemu_lockcnt_inc(&ctx->list_lock);
|
||||
|
||||
QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) {
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
int revents;
|
||||
|
||||
revents = node->pfd.revents & node->pfd.events;
|
||||
if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR) && node->io_read &&
|
||||
aio_node_check(ctx, node->is_external)) {
|
||||
result = true;
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
if (revents & (G_IO_OUT | G_IO_ERR) && node->io_write &&
|
||||
aio_node_check(ctx, node->is_external)) {
|
||||
result = true;
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
qemu_lockcnt_dec(&ctx->list_lock);
|
||||
|
||||
return result;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool aio_dispatch_handlers(AioContext *ctx)
|
||||
bool aio_dispatch(AioContext *ctx)
|
||||
{
|
||||
AioHandler *node, *tmp;
|
||||
AioHandler *node;
|
||||
bool progress = false;
|
||||
|
||||
/*
|
||||
* If there are callbacks left that have been queued, we need to call them.
|
||||
* Do not call select in this case, because it is possible that the caller
|
||||
* does not need a complete flush (as is the case for aio_poll loops).
|
||||
*/
|
||||
if (aio_bh_poll(ctx)) {
|
||||
progress = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to walk very carefully in case aio_set_fd_handler is
|
||||
* called while we're walking.
|
||||
*/
|
||||
qemu_lockcnt_inc(&ctx->list_lock);
|
||||
|
||||
QLIST_FOREACH_SAFE_RCU(node, &ctx->aio_handlers, node, tmp) {
|
||||
node = QLIST_FIRST(&ctx->aio_handlers);
|
||||
while (node) {
|
||||
AioHandler *tmp;
|
||||
int revents;
|
||||
|
||||
ctx->walking_handlers++;
|
||||
|
||||
revents = node->pfd.revents & node->pfd.events;
|
||||
node->pfd.revents = 0;
|
||||
|
||||
@@ -417,38 +337,17 @@ static bool aio_dispatch_handlers(AioContext *ctx)
|
||||
progress = true;
|
||||
}
|
||||
|
||||
if (node->deleted) {
|
||||
if (qemu_lockcnt_dec_if_lock(&ctx->list_lock)) {
|
||||
QLIST_REMOVE(node, node);
|
||||
g_free(node);
|
||||
qemu_lockcnt_inc_and_unlock(&ctx->list_lock);
|
||||
}
|
||||
tmp = node;
|
||||
node = QLIST_NEXT(node, node);
|
||||
|
||||
ctx->walking_handlers--;
|
||||
|
||||
if (!ctx->walking_handlers && tmp->deleted) {
|
||||
QLIST_REMOVE(tmp, node);
|
||||
g_free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
qemu_lockcnt_dec(&ctx->list_lock);
|
||||
return progress;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that dispatch_fds == false has the side-effect of post-poning the
|
||||
* freeing of deleted handlers.
|
||||
*/
|
||||
bool aio_dispatch(AioContext *ctx, bool dispatch_fds)
|
||||
{
|
||||
bool progress;
|
||||
|
||||
/*
|
||||
* If there are callbacks left that have been queued, we need to call them.
|
||||
* Do not call select in this case, because it is possible that the caller
|
||||
* does not need a complete flush (as is the case for aio_poll loops).
|
||||
*/
|
||||
progress = aio_bh_poll(ctx);
|
||||
|
||||
if (dispatch_fds) {
|
||||
progress |= aio_dispatch_handlers(ctx);
|
||||
}
|
||||
|
||||
/* Run our timers */
|
||||
progress |= timerlistgroup_run_timers(&ctx->tlg);
|
||||
|
||||
@@ -501,101 +400,12 @@ static void add_pollfd(AioHandler *node)
|
||||
npfd++;
|
||||
}
|
||||
|
||||
static bool run_poll_handlers_once(AioContext *ctx)
|
||||
{
|
||||
bool progress = false;
|
||||
AioHandler *node;
|
||||
|
||||
QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) {
|
||||
if (!node->deleted && node->io_poll &&
|
||||
aio_node_check(ctx, node->is_external) &&
|
||||
node->io_poll(node->opaque)) {
|
||||
progress = true;
|
||||
}
|
||||
|
||||
/* Caller handles freeing deleted nodes. Don't do it here. */
|
||||
}
|
||||
|
||||
return progress;
|
||||
}
|
||||
|
||||
/* run_poll_handlers:
|
||||
* @ctx: the AioContext
|
||||
* @max_ns: maximum time to poll for, in nanoseconds
|
||||
*
|
||||
* Polls for a given time.
|
||||
*
|
||||
* Note that ctx->notify_me must be non-zero so this function can detect
|
||||
* aio_notify().
|
||||
*
|
||||
* Note that the caller must have incremented ctx->list_lock.
|
||||
*
|
||||
* Returns: true if progress was made, false otherwise
|
||||
*/
|
||||
static bool run_poll_handlers(AioContext *ctx, int64_t max_ns)
|
||||
{
|
||||
bool progress;
|
||||
int64_t end_time;
|
||||
|
||||
assert(ctx->notify_me);
|
||||
assert(qemu_lockcnt_count(&ctx->list_lock) > 0);
|
||||
assert(ctx->poll_disable_cnt == 0);
|
||||
|
||||
trace_run_poll_handlers_begin(ctx, max_ns);
|
||||
|
||||
end_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + max_ns;
|
||||
|
||||
do {
|
||||
progress = run_poll_handlers_once(ctx);
|
||||
} while (!progress && qemu_clock_get_ns(QEMU_CLOCK_REALTIME) < end_time);
|
||||
|
||||
trace_run_poll_handlers_end(ctx, progress);
|
||||
|
||||
return progress;
|
||||
}
|
||||
|
||||
/* try_poll_mode:
|
||||
* @ctx: the AioContext
|
||||
* @blocking: busy polling is only attempted when blocking is true
|
||||
*
|
||||
* ctx->notify_me must be non-zero so this function can detect aio_notify().
|
||||
*
|
||||
* Note that the caller must have incremented ctx->list_lock.
|
||||
*
|
||||
* Returns: true if progress was made, false otherwise
|
||||
*/
|
||||
static bool try_poll_mode(AioContext *ctx, bool blocking)
|
||||
{
|
||||
if (blocking && ctx->poll_max_ns && ctx->poll_disable_cnt == 0) {
|
||||
/* See qemu_soonest_timeout() uint64_t hack */
|
||||
int64_t max_ns = MIN((uint64_t)aio_compute_timeout(ctx),
|
||||
(uint64_t)ctx->poll_ns);
|
||||
|
||||
if (max_ns) {
|
||||
poll_set_started(ctx, true);
|
||||
|
||||
if (run_poll_handlers(ctx, max_ns)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
poll_set_started(ctx, false);
|
||||
|
||||
/* Even if we don't run busy polling, try polling once in case it can make
|
||||
* progress and the caller will be able to avoid ppoll(2)/epoll_wait(2).
|
||||
*/
|
||||
return run_poll_handlers_once(ctx);
|
||||
}
|
||||
|
||||
bool aio_poll(AioContext *ctx, bool blocking)
|
||||
{
|
||||
AioHandler *node;
|
||||
int i;
|
||||
int ret = 0;
|
||||
int i, ret;
|
||||
bool progress;
|
||||
int64_t timeout;
|
||||
int64_t start = 0;
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
progress = false;
|
||||
@@ -611,93 +421,43 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
||||
atomic_add(&ctx->notify_me, 2);
|
||||
}
|
||||
|
||||
qemu_lockcnt_inc(&ctx->list_lock);
|
||||
ctx->walking_handlers++;
|
||||
|
||||
if (ctx->poll_max_ns) {
|
||||
start = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
|
||||
}
|
||||
assert(npfd == 0);
|
||||
|
||||
if (try_poll_mode(ctx, blocking)) {
|
||||
progress = true;
|
||||
} else {
|
||||
assert(npfd == 0);
|
||||
/* fill pollfds */
|
||||
|
||||
/* fill pollfds */
|
||||
|
||||
if (!aio_epoll_enabled(ctx)) {
|
||||
QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) {
|
||||
if (!node->deleted && node->pfd.events
|
||||
&& aio_node_check(ctx, node->is_external)) {
|
||||
add_pollfd(node);
|
||||
}
|
||||
if (!aio_epoll_enabled(ctx)) {
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
if (!node->deleted && node->pfd.events
|
||||
&& aio_node_check(ctx, node->is_external)) {
|
||||
add_pollfd(node);
|
||||
}
|
||||
}
|
||||
|
||||
timeout = blocking ? aio_compute_timeout(ctx) : 0;
|
||||
|
||||
/* wait until next event */
|
||||
if (timeout) {
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
if (aio_epoll_check_poll(ctx, pollfds, npfd, timeout)) {
|
||||
AioHandler epoll_handler;
|
||||
|
||||
epoll_handler.pfd.fd = ctx->epollfd;
|
||||
epoll_handler.pfd.events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR;
|
||||
npfd = 0;
|
||||
add_pollfd(&epoll_handler);
|
||||
ret = aio_epoll(ctx, pollfds, npfd, timeout);
|
||||
} else {
|
||||
ret = qemu_poll_ns(pollfds, npfd, timeout);
|
||||
}
|
||||
if (timeout) {
|
||||
aio_context_acquire(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
timeout = blocking ? aio_compute_timeout(ctx) : 0;
|
||||
|
||||
/* wait until next event */
|
||||
if (timeout) {
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
if (aio_epoll_check_poll(ctx, pollfds, npfd, timeout)) {
|
||||
AioHandler epoll_handler;
|
||||
|
||||
epoll_handler.pfd.fd = ctx->epollfd;
|
||||
epoll_handler.pfd.events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR;
|
||||
npfd = 0;
|
||||
add_pollfd(&epoll_handler);
|
||||
ret = aio_epoll(ctx, pollfds, npfd, timeout);
|
||||
} else {
|
||||
ret = qemu_poll_ns(pollfds, npfd, timeout);
|
||||
}
|
||||
if (blocking) {
|
||||
atomic_sub(&ctx->notify_me, 2);
|
||||
}
|
||||
|
||||
/* Adjust polling time */
|
||||
if (ctx->poll_max_ns) {
|
||||
int64_t block_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start;
|
||||
|
||||
if (block_ns <= ctx->poll_ns) {
|
||||
/* This is the sweet spot, no adjustment needed */
|
||||
} else if (block_ns > ctx->poll_max_ns) {
|
||||
/* We'd have to poll for too long, poll less */
|
||||
int64_t old = ctx->poll_ns;
|
||||
|
||||
if (ctx->poll_shrink) {
|
||||
ctx->poll_ns /= ctx->poll_shrink;
|
||||
} else {
|
||||
ctx->poll_ns = 0;
|
||||
}
|
||||
|
||||
trace_poll_shrink(ctx, old, ctx->poll_ns);
|
||||
} else if (ctx->poll_ns < ctx->poll_max_ns &&
|
||||
block_ns < ctx->poll_max_ns) {
|
||||
/* There is room to grow, poll longer */
|
||||
int64_t old = ctx->poll_ns;
|
||||
int64_t grow = ctx->poll_grow;
|
||||
|
||||
if (grow == 0) {
|
||||
grow = 2;
|
||||
}
|
||||
|
||||
if (ctx->poll_ns) {
|
||||
ctx->poll_ns *= grow;
|
||||
} else {
|
||||
ctx->poll_ns = 4000; /* start polling at 4 microseconds */
|
||||
}
|
||||
|
||||
if (ctx->poll_ns > ctx->poll_max_ns) {
|
||||
ctx->poll_ns = ctx->poll_max_ns;
|
||||
}
|
||||
|
||||
trace_poll_grow(ctx, old, ctx->poll_ns);
|
||||
}
|
||||
if (timeout) {
|
||||
aio_context_acquire(ctx);
|
||||
}
|
||||
|
||||
aio_notify_accept(ctx);
|
||||
@@ -710,10 +470,10 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
||||
}
|
||||
|
||||
npfd = 0;
|
||||
qemu_lockcnt_dec(&ctx->list_lock);
|
||||
ctx->walking_handlers--;
|
||||
|
||||
/* Run dispatch even if there were no readable fds to run timers */
|
||||
if (aio_dispatch(ctx, ret > 0)) {
|
||||
if (aio_dispatch(ctx)) {
|
||||
progress = true;
|
||||
}
|
||||
|
||||
@@ -724,13 +484,6 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
||||
|
||||
void aio_context_setup(AioContext *ctx)
|
||||
{
|
||||
/* TODO remove this in final patch submission */
|
||||
if (getenv("QEMU_AIO_POLL_MAX_NS")) {
|
||||
fprintf(stderr, "The QEMU_AIO_POLL_MAX_NS environment variable has "
|
||||
"been replaced with -object iothread,poll-max-ns=NUM\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EPOLL_CREATE1
|
||||
assert(!ctx->epollfd);
|
||||
ctx->epollfd = epoll_create1(EPOLL_CLOEXEC);
|
||||
@@ -742,17 +495,3 @@ void aio_context_setup(AioContext *ctx)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns,
|
||||
int64_t grow, int64_t shrink, Error **errp)
|
||||
{
|
||||
/* No thread synchronization here, it doesn't matter if an incorrect value
|
||||
* is used once.
|
||||
*/
|
||||
ctx->poll_max_ns = max_ns;
|
||||
ctx->poll_ns = 0;
|
||||
ctx->poll_grow = grow;
|
||||
ctx->poll_shrink = shrink;
|
||||
|
||||
aio_notify(ctx);
|
||||
}
|
||||
|
115
aio-win32.c
115
aio-win32.c
@@ -20,8 +20,6 @@
|
||||
#include "block/block.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/rcu_queue.h"
|
||||
|
||||
struct AioHandler {
|
||||
EventNotifier *e;
|
||||
@@ -40,13 +38,11 @@ void aio_set_fd_handler(AioContext *ctx,
|
||||
bool is_external,
|
||||
IOHandler *io_read,
|
||||
IOHandler *io_write,
|
||||
AioPollFn *io_poll,
|
||||
void *opaque)
|
||||
{
|
||||
/* fd is a SOCKET in our case */
|
||||
AioHandler *node;
|
||||
|
||||
qemu_lockcnt_lock(&ctx->list_lock);
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
if (node->pfd.fd == fd && !node->deleted) {
|
||||
break;
|
||||
@@ -56,14 +52,14 @@ void aio_set_fd_handler(AioContext *ctx,
|
||||
/* Are we deleting the fd handler? */
|
||||
if (!io_read && !io_write) {
|
||||
if (node) {
|
||||
/* If aio_poll is in progress, just mark the node as deleted */
|
||||
if (qemu_lockcnt_count(&ctx->list_lock)) {
|
||||
/* If the lock is held, just mark the node as deleted */
|
||||
if (ctx->walking_handlers) {
|
||||
node->deleted = 1;
|
||||
node->pfd.revents = 0;
|
||||
} else {
|
||||
/* Otherwise, delete it for real. We can't just mark it as
|
||||
* deleted because deleted nodes are only cleaned up after
|
||||
* releasing the list_lock.
|
||||
* releasing the walking_handlers lock.
|
||||
*/
|
||||
QLIST_REMOVE(node, node);
|
||||
g_free(node);
|
||||
@@ -76,7 +72,7 @@ void aio_set_fd_handler(AioContext *ctx,
|
||||
/* Alloc and insert if it's not already there */
|
||||
node = g_new0(AioHandler, 1);
|
||||
node->pfd.fd = fd;
|
||||
QLIST_INSERT_HEAD_RCU(&ctx->aio_handlers, node, node);
|
||||
QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node);
|
||||
}
|
||||
|
||||
node->pfd.events = 0;
|
||||
@@ -101,26 +97,16 @@ void aio_set_fd_handler(AioContext *ctx,
|
||||
FD_CONNECT | FD_WRITE | FD_OOB);
|
||||
}
|
||||
|
||||
qemu_lockcnt_unlock(&ctx->list_lock);
|
||||
aio_notify(ctx);
|
||||
}
|
||||
|
||||
void aio_set_fd_poll(AioContext *ctx, int fd,
|
||||
IOHandler *io_poll_begin,
|
||||
IOHandler *io_poll_end)
|
||||
{
|
||||
/* Not implemented */
|
||||
}
|
||||
|
||||
void aio_set_event_notifier(AioContext *ctx,
|
||||
EventNotifier *e,
|
||||
bool is_external,
|
||||
EventNotifierHandler *io_notify,
|
||||
AioPollFn *io_poll)
|
||||
EventNotifierHandler *io_notify)
|
||||
{
|
||||
AioHandler *node;
|
||||
|
||||
qemu_lockcnt_lock(&ctx->list_lock);
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
if (node->e == e && !node->deleted) {
|
||||
break;
|
||||
@@ -132,14 +118,14 @@ void aio_set_event_notifier(AioContext *ctx,
|
||||
if (node) {
|
||||
g_source_remove_poll(&ctx->source, &node->pfd);
|
||||
|
||||
/* aio_poll is in progress, just mark the node as deleted */
|
||||
if (qemu_lockcnt_count(&ctx->list_lock)) {
|
||||
/* If the lock is held, just mark the node as deleted */
|
||||
if (ctx->walking_handlers) {
|
||||
node->deleted = 1;
|
||||
node->pfd.revents = 0;
|
||||
} else {
|
||||
/* Otherwise, delete it for real. We can't just mark it as
|
||||
* deleted because deleted nodes are only cleaned up after
|
||||
* releasing the list_lock.
|
||||
* releasing the walking_handlers lock.
|
||||
*/
|
||||
QLIST_REMOVE(node, node);
|
||||
g_free(node);
|
||||
@@ -153,7 +139,7 @@ void aio_set_event_notifier(AioContext *ctx,
|
||||
node->pfd.fd = (uintptr_t)event_notifier_get_handle(e);
|
||||
node->pfd.events = G_IO_IN;
|
||||
node->is_external = is_external;
|
||||
QLIST_INSERT_HEAD_RCU(&ctx->aio_handlers, node, node);
|
||||
QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node);
|
||||
|
||||
g_source_add_poll(&ctx->source, &node->pfd);
|
||||
}
|
||||
@@ -161,18 +147,9 @@ void aio_set_event_notifier(AioContext *ctx,
|
||||
node->io_notify = io_notify;
|
||||
}
|
||||
|
||||
qemu_lockcnt_unlock(&ctx->list_lock);
|
||||
aio_notify(ctx);
|
||||
}
|
||||
|
||||
void aio_set_event_notifier_poll(AioContext *ctx,
|
||||
EventNotifier *notifier,
|
||||
EventNotifierHandler *io_poll_begin,
|
||||
EventNotifierHandler *io_poll_end)
|
||||
{
|
||||
/* Not implemented */
|
||||
}
|
||||
|
||||
bool aio_prepare(AioContext *ctx)
|
||||
{
|
||||
static struct timeval tv0;
|
||||
@@ -180,16 +157,10 @@ bool aio_prepare(AioContext *ctx)
|
||||
bool have_select_revents = false;
|
||||
fd_set rfds, wfds;
|
||||
|
||||
/*
|
||||
* We have to walk very carefully in case aio_set_fd_handler is
|
||||
* called while we're walking.
|
||||
*/
|
||||
qemu_lockcnt_inc(&ctx->list_lock);
|
||||
|
||||
/* fill fd sets */
|
||||
FD_ZERO(&rfds);
|
||||
FD_ZERO(&wfds);
|
||||
QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) {
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
if (node->io_read) {
|
||||
FD_SET ((SOCKET)node->pfd.fd, &rfds);
|
||||
}
|
||||
@@ -199,7 +170,7 @@ bool aio_prepare(AioContext *ctx)
|
||||
}
|
||||
|
||||
if (select(0, &rfds, &wfds, NULL, &tv0) > 0) {
|
||||
QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) {
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
node->pfd.revents = 0;
|
||||
if (FD_ISSET(node->pfd.fd, &rfds)) {
|
||||
node->pfd.revents |= G_IO_IN;
|
||||
@@ -213,55 +184,45 @@ bool aio_prepare(AioContext *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
qemu_lockcnt_dec(&ctx->list_lock);
|
||||
return have_select_revents;
|
||||
}
|
||||
|
||||
bool aio_pending(AioContext *ctx)
|
||||
{
|
||||
AioHandler *node;
|
||||
bool result = false;
|
||||
|
||||
/*
|
||||
* We have to walk very carefully in case aio_set_fd_handler is
|
||||
* called while we're walking.
|
||||
*/
|
||||
qemu_lockcnt_inc(&ctx->list_lock);
|
||||
QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) {
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
if (node->pfd.revents && node->io_notify) {
|
||||
result = true;
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((node->pfd.revents & G_IO_IN) && node->io_read) {
|
||||
result = true;
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
if ((node->pfd.revents & G_IO_OUT) && node->io_write) {
|
||||
result = true;
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
qemu_lockcnt_dec(&ctx->list_lock);
|
||||
return result;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool aio_dispatch_handlers(AioContext *ctx, HANDLE event)
|
||||
{
|
||||
AioHandler *node;
|
||||
bool progress = false;
|
||||
AioHandler *tmp;
|
||||
|
||||
qemu_lockcnt_inc(&ctx->list_lock);
|
||||
|
||||
/*
|
||||
* We have to walk very carefully in case aio_set_fd_handler is
|
||||
* called while we're walking.
|
||||
*/
|
||||
QLIST_FOREACH_SAFE_RCU(node, &ctx->aio_handlers, node, tmp) {
|
||||
node = QLIST_FIRST(&ctx->aio_handlers);
|
||||
while (node) {
|
||||
AioHandler *tmp;
|
||||
int revents = node->pfd.revents;
|
||||
|
||||
ctx->walking_handlers++;
|
||||
|
||||
if (!node->deleted &&
|
||||
(revents || event_notifier_get_handle(node->e) == event) &&
|
||||
node->io_notify) {
|
||||
@@ -296,27 +257,26 @@ static bool aio_dispatch_handlers(AioContext *ctx, HANDLE event)
|
||||
}
|
||||
}
|
||||
|
||||
if (node->deleted) {
|
||||
if (qemu_lockcnt_dec_if_lock(&ctx->list_lock)) {
|
||||
QLIST_REMOVE(node, node);
|
||||
g_free(node);
|
||||
qemu_lockcnt_inc_and_unlock(&ctx->list_lock);
|
||||
}
|
||||
tmp = node;
|
||||
node = QLIST_NEXT(node, node);
|
||||
|
||||
ctx->walking_handlers--;
|
||||
|
||||
if (!ctx->walking_handlers && tmp->deleted) {
|
||||
QLIST_REMOVE(tmp, node);
|
||||
g_free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
qemu_lockcnt_dec(&ctx->list_lock);
|
||||
return progress;
|
||||
}
|
||||
|
||||
bool aio_dispatch(AioContext *ctx, bool dispatch_fds)
|
||||
bool aio_dispatch(AioContext *ctx)
|
||||
{
|
||||
bool progress;
|
||||
|
||||
progress = aio_bh_poll(ctx);
|
||||
if (dispatch_fds) {
|
||||
progress |= aio_dispatch_handlers(ctx, INVALID_HANDLE_VALUE);
|
||||
}
|
||||
progress |= aio_dispatch_handlers(ctx, INVALID_HANDLE_VALUE);
|
||||
progress |= timerlistgroup_run_timers(&ctx->tlg);
|
||||
return progress;
|
||||
}
|
||||
@@ -343,19 +303,20 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
||||
atomic_add(&ctx->notify_me, 2);
|
||||
}
|
||||
|
||||
qemu_lockcnt_inc(&ctx->list_lock);
|
||||
have_select_revents = aio_prepare(ctx);
|
||||
|
||||
ctx->walking_handlers++;
|
||||
|
||||
/* fill fd sets */
|
||||
count = 0;
|
||||
QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) {
|
||||
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
|
||||
if (!node->deleted && node->io_notify
|
||||
&& aio_node_check(ctx, node->is_external)) {
|
||||
events[count++] = event_notifier_get_handle(node->e);
|
||||
}
|
||||
}
|
||||
|
||||
qemu_lockcnt_dec(&ctx->list_lock);
|
||||
ctx->walking_handlers--;
|
||||
first = true;
|
||||
|
||||
/* ctx->notifier is always registered. */
|
||||
@@ -413,9 +374,3 @@ bool aio_poll(AioContext *ctx, bool blocking)
|
||||
void aio_context_setup(AioContext *ctx)
|
||||
{
|
||||
}
|
||||
|
||||
void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns,
|
||||
int64_t grow, int64_t shrink, Error **errp)
|
||||
{
|
||||
error_setg(errp, "AioContext polling is not implemented on Windows");
|
||||
}
|
||||
|
50
arch_init.c
50
arch_init.c
@@ -28,6 +28,7 @@
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/audio/audio.h"
|
||||
#include "hw/smbios/smbios.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qmp-commands.h"
|
||||
@@ -63,8 +64,6 @@ int graphic_depth = 32;
|
||||
#define QEMU_ARCH QEMU_ARCH_MIPS
|
||||
#elif defined(TARGET_MOXIE)
|
||||
#define QEMU_ARCH QEMU_ARCH_MOXIE
|
||||
#elif defined(TARGET_NIOS2)
|
||||
#define QEMU_ARCH QEMU_ARCH_NIOS2
|
||||
#elif defined(TARGET_OPENRISC)
|
||||
#define QEMU_ARCH QEMU_ARCH_OPENRISC
|
||||
#elif defined(TARGET_PPC)
|
||||
@@ -85,6 +84,33 @@ int graphic_depth = 32;
|
||||
|
||||
const uint32_t arch_type = QEMU_ARCH;
|
||||
|
||||
static struct defconfig_file {
|
||||
const char *filename;
|
||||
/* Indicates it is an user config file (disabled by -no-user-config) */
|
||||
bool userconfig;
|
||||
} default_config_files[] = {
|
||||
{ CONFIG_QEMU_CONFDIR "/qemu.conf", true },
|
||||
{ NULL }, /* end of list */
|
||||
};
|
||||
|
||||
int qemu_read_default_config_files(bool userconfig)
|
||||
{
|
||||
int ret;
|
||||
struct defconfig_file *f;
|
||||
|
||||
for (f = default_config_files; f->filename; f++) {
|
||||
if (!userconfig && f->userconfig) {
|
||||
continue;
|
||||
}
|
||||
ret = qemu_read_config_file(f->filename);
|
||||
if (ret < 0 && ret != -ENOENT) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct soundhw {
|
||||
const char *name;
|
||||
const char *descr;
|
||||
@@ -209,6 +235,26 @@ void audio_init(void)
|
||||
}
|
||||
}
|
||||
|
||||
void do_acpitable_option(const QemuOpts *opts)
|
||||
{
|
||||
#ifdef TARGET_I386
|
||||
Error *err = NULL;
|
||||
|
||||
acpi_table_add(opts, &err);
|
||||
if (err) {
|
||||
error_reportf_err(err, "Wrong acpi table provided: ");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void do_smbios_option(QemuOpts *opts)
|
||||
{
|
||||
#ifdef TARGET_I386
|
||||
smbios_entry_add(opts);
|
||||
#endif
|
||||
}
|
||||
|
||||
int kvm_available(void)
|
||||
{
|
||||
#ifdef CONFIG_KVM
|
||||
|
66
async.c
66
async.c
@@ -53,14 +53,14 @@ void aio_bh_schedule_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
|
||||
.cb = cb,
|
||||
.opaque = opaque,
|
||||
};
|
||||
qemu_lockcnt_lock(&ctx->list_lock);
|
||||
qemu_mutex_lock(&ctx->bh_lock);
|
||||
bh->next = ctx->first_bh;
|
||||
bh->scheduled = 1;
|
||||
bh->deleted = 1;
|
||||
/* Make sure that the members are ready before putting bh into list */
|
||||
smp_wmb();
|
||||
ctx->first_bh = bh;
|
||||
qemu_lockcnt_unlock(&ctx->list_lock);
|
||||
qemu_mutex_unlock(&ctx->bh_lock);
|
||||
aio_notify(ctx);
|
||||
}
|
||||
|
||||
@@ -73,12 +73,12 @@ QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
|
||||
.cb = cb,
|
||||
.opaque = opaque,
|
||||
};
|
||||
qemu_lockcnt_lock(&ctx->list_lock);
|
||||
qemu_mutex_lock(&ctx->bh_lock);
|
||||
bh->next = ctx->first_bh;
|
||||
/* Make sure that the members are ready before putting bh into list */
|
||||
smp_wmb();
|
||||
ctx->first_bh = bh;
|
||||
qemu_lockcnt_unlock(&ctx->list_lock);
|
||||
qemu_mutex_unlock(&ctx->bh_lock);
|
||||
return bh;
|
||||
}
|
||||
|
||||
@@ -92,13 +92,14 @@ int aio_bh_poll(AioContext *ctx)
|
||||
{
|
||||
QEMUBH *bh, **bhp, *next;
|
||||
int ret;
|
||||
bool deleted = false;
|
||||
|
||||
qemu_lockcnt_inc(&ctx->list_lock);
|
||||
ctx->walking_bh++;
|
||||
|
||||
ret = 0;
|
||||
for (bh = atomic_rcu_read(&ctx->first_bh); bh; bh = next) {
|
||||
next = atomic_rcu_read(&bh->next);
|
||||
for (bh = ctx->first_bh; bh; bh = next) {
|
||||
/* Make sure that fetching bh happens before accessing its members */
|
||||
smp_read_barrier_depends();
|
||||
next = bh->next;
|
||||
/* The atomic_xchg is paired with the one in qemu_bh_schedule. The
|
||||
* implicit memory barrier ensures that the callback sees all writes
|
||||
* done by the scheduling thread. It also ensures that the scheduling
|
||||
@@ -113,18 +114,13 @@ int aio_bh_poll(AioContext *ctx)
|
||||
bh->idle = 0;
|
||||
aio_bh_call(bh);
|
||||
}
|
||||
if (bh->deleted) {
|
||||
deleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->walking_bh--;
|
||||
|
||||
/* remove deleted bhs */
|
||||
if (!deleted) {
|
||||
qemu_lockcnt_dec(&ctx->list_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (qemu_lockcnt_dec_and_lock(&ctx->list_lock)) {
|
||||
if (!ctx->walking_bh) {
|
||||
qemu_mutex_lock(&ctx->bh_lock);
|
||||
bhp = &ctx->first_bh;
|
||||
while (*bhp) {
|
||||
bh = *bhp;
|
||||
@@ -135,8 +131,9 @@ int aio_bh_poll(AioContext *ctx)
|
||||
bhp = &bh->next;
|
||||
}
|
||||
}
|
||||
qemu_lockcnt_unlock(&ctx->list_lock);
|
||||
qemu_mutex_unlock(&ctx->bh_lock);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -190,8 +187,7 @@ aio_compute_timeout(AioContext *ctx)
|
||||
int timeout = -1;
|
||||
QEMUBH *bh;
|
||||
|
||||
for (bh = atomic_rcu_read(&ctx->first_bh); bh;
|
||||
bh = atomic_rcu_read(&bh->next)) {
|
||||
for (bh = ctx->first_bh; bh; bh = bh->next) {
|
||||
if (bh->scheduled) {
|
||||
if (bh->idle) {
|
||||
/* idle bottom halves will be polled at least
|
||||
@@ -255,7 +251,7 @@ aio_ctx_dispatch(GSource *source,
|
||||
AioContext *ctx = (AioContext *) source;
|
||||
|
||||
assert(callback == NULL);
|
||||
aio_dispatch(ctx, true);
|
||||
aio_dispatch(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -274,8 +270,7 @@ aio_ctx_finalize(GSource *source)
|
||||
}
|
||||
#endif
|
||||
|
||||
qemu_lockcnt_lock(&ctx->list_lock);
|
||||
assert(!qemu_lockcnt_count(&ctx->list_lock));
|
||||
qemu_mutex_lock(&ctx->bh_lock);
|
||||
while (ctx->first_bh) {
|
||||
QEMUBH *next = ctx->first_bh->next;
|
||||
|
||||
@@ -285,12 +280,12 @@ aio_ctx_finalize(GSource *source)
|
||||
g_free(ctx->first_bh);
|
||||
ctx->first_bh = next;
|
||||
}
|
||||
qemu_lockcnt_unlock(&ctx->list_lock);
|
||||
qemu_mutex_unlock(&ctx->bh_lock);
|
||||
|
||||
aio_set_event_notifier(ctx, &ctx->notifier, false, NULL, NULL);
|
||||
aio_set_event_notifier(ctx, &ctx->notifier, false, NULL);
|
||||
event_notifier_cleanup(&ctx->notifier);
|
||||
qemu_rec_mutex_destroy(&ctx->lock);
|
||||
qemu_lockcnt_destroy(&ctx->list_lock);
|
||||
qemu_mutex_destroy(&ctx->bh_lock);
|
||||
timerlistgroup_deinit(&ctx->tlg);
|
||||
}
|
||||
|
||||
@@ -354,15 +349,6 @@ static void event_notifier_dummy_cb(EventNotifier *e)
|
||||
{
|
||||
}
|
||||
|
||||
/* Returns true if aio_notify() was called (e.g. a BH was scheduled) */
|
||||
static bool event_notifier_poll(void *opaque)
|
||||
{
|
||||
EventNotifier *e = opaque;
|
||||
AioContext *ctx = container_of(e, AioContext, notifier);
|
||||
|
||||
return atomic_read(&ctx->notified);
|
||||
}
|
||||
|
||||
AioContext *aio_context_new(Error **errp)
|
||||
{
|
||||
int ret;
|
||||
@@ -377,24 +363,18 @@ AioContext *aio_context_new(Error **errp)
|
||||
goto fail;
|
||||
}
|
||||
g_source_set_can_recurse(&ctx->source, true);
|
||||
qemu_lockcnt_init(&ctx->list_lock);
|
||||
aio_set_event_notifier(ctx, &ctx->notifier,
|
||||
false,
|
||||
(EventNotifierHandler *)
|
||||
event_notifier_dummy_cb,
|
||||
event_notifier_poll);
|
||||
event_notifier_dummy_cb);
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
ctx->linux_aio = NULL;
|
||||
#endif
|
||||
ctx->thread_pool = NULL;
|
||||
qemu_mutex_init(&ctx->bh_lock);
|
||||
qemu_rec_mutex_init(&ctx->lock);
|
||||
timerlistgroup_init(&ctx->tlg, aio_timerlist_notify, ctx);
|
||||
|
||||
ctx->poll_ns = 0;
|
||||
ctx->poll_max_ns = 0;
|
||||
ctx->poll_grow = 0;
|
||||
ctx->poll_shrink = 0;
|
||||
|
||||
return ctx;
|
||||
fail:
|
||||
g_source_destroy(&ctx->source);
|
||||
|
132
backends/baum.c
132
backends/baum.c
@@ -27,10 +27,12 @@
|
||||
#include "sysemu/char.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/usb.h"
|
||||
#include "ui/console.h"
|
||||
#include <brlapi.h>
|
||||
#include <brlapi_constants.h>
|
||||
#include <brlapi_keycodes.h>
|
||||
#ifdef CONFIG_SDL
|
||||
#include <SDL_syswm.h>
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#define DPRINTF(fmt, ...) \
|
||||
@@ -85,7 +87,7 @@
|
||||
#define BUF_SIZE 256
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
CharDriverState *chr;
|
||||
|
||||
brlapi_handle_t *brlapi;
|
||||
int brlapi_fd;
|
||||
@@ -98,10 +100,7 @@ typedef struct {
|
||||
uint8_t out_buf_used, out_buf_ptr;
|
||||
|
||||
QEMUTimer *cellCount_timer;
|
||||
} BaumChardev;
|
||||
|
||||
#define TYPE_CHARDEV_BRAILLE "chardev-braille"
|
||||
#define BAUM_CHARDEV(obj) OBJECT_CHECK(BaumChardev, (obj), TYPE_CHARDEV_BRAILLE)
|
||||
} BaumDriverState;
|
||||
|
||||
/* Let's assume NABCC by default */
|
||||
enum way {
|
||||
@@ -226,10 +225,14 @@ static const uint8_t nabcc_translation[2][256] = {
|
||||
};
|
||||
|
||||
/* The guest OS has started discussing with us, finish initializing BrlAPI */
|
||||
static int baum_deferred_init(BaumChardev *baum)
|
||||
static int baum_deferred_init(BaumDriverState *baum)
|
||||
{
|
||||
int tty = BRLAPI_TTY_DEFAULT;
|
||||
QemuConsole *con;
|
||||
#if defined(CONFIG_SDL)
|
||||
#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0)
|
||||
SDL_SysWMinfo info;
|
||||
#endif
|
||||
#endif
|
||||
int tty;
|
||||
|
||||
if (baum->deferred_init) {
|
||||
return 1;
|
||||
@@ -240,12 +243,21 @@ static int baum_deferred_init(BaumChardev *baum)
|
||||
return 0;
|
||||
}
|
||||
|
||||
con = qemu_console_lookup_by_index(0);
|
||||
if (con && qemu_console_is_graphic(con)) {
|
||||
tty = qemu_console_get_window_id(con);
|
||||
if (tty == -1)
|
||||
tty = BRLAPI_TTY_DEFAULT;
|
||||
#if defined(CONFIG_SDL)
|
||||
#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0)
|
||||
memset(&info, 0, sizeof(info));
|
||||
SDL_VERSION(&info.version);
|
||||
if (SDL_GetWMInfo(&info)) {
|
||||
tty = info.info.x11.wmwindow;
|
||||
} else {
|
||||
#endif
|
||||
#endif
|
||||
tty = BRLAPI_TTY_DEFAULT;
|
||||
#if defined(CONFIG_SDL)
|
||||
#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0)
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (brlapi__enterTtyMode(baum->brlapi, tty, NULL) == -1) {
|
||||
brlapi_perror("baum: brlapi__enterTtyMode");
|
||||
@@ -256,9 +268,9 @@ static int baum_deferred_init(BaumChardev *baum)
|
||||
}
|
||||
|
||||
/* The serial port can receive more of our data */
|
||||
static void baum_chr_accept_input(struct Chardev *chr)
|
||||
static void baum_accept_input(struct CharDriverState *chr)
|
||||
{
|
||||
BaumChardev *baum = BAUM_CHARDEV(chr);
|
||||
BaumDriverState *baum = chr->opaque;
|
||||
int room, first;
|
||||
|
||||
if (!baum->out_buf_used)
|
||||
@@ -282,25 +294,24 @@ static void baum_chr_accept_input(struct Chardev *chr)
|
||||
}
|
||||
|
||||
/* We want to send a packet */
|
||||
static void baum_write_packet(BaumChardev *baum, const uint8_t *buf, int len)
|
||||
static void baum_write_packet(BaumDriverState *baum, const uint8_t *buf, int len)
|
||||
{
|
||||
Chardev *chr = CHARDEV(baum);
|
||||
uint8_t io_buf[1 + 2 * len], *cur = io_buf;
|
||||
int room;
|
||||
*cur++ = ESC;
|
||||
while (len--)
|
||||
if ((*cur++ = *buf++) == ESC)
|
||||
*cur++ = ESC;
|
||||
room = qemu_chr_be_can_write(chr);
|
||||
room = qemu_chr_be_can_write(baum->chr);
|
||||
len = cur - io_buf;
|
||||
if (len <= room) {
|
||||
/* Fits */
|
||||
qemu_chr_be_write(chr, io_buf, len);
|
||||
qemu_chr_be_write(baum->chr, io_buf, len);
|
||||
} else {
|
||||
int first;
|
||||
uint8_t out;
|
||||
/* Can't fit all, send what can be, and store the rest. */
|
||||
qemu_chr_be_write(chr, io_buf, room);
|
||||
qemu_chr_be_write(baum->chr, io_buf, room);
|
||||
len -= room;
|
||||
cur = io_buf + room;
|
||||
if (len > BUF_SIZE - baum->out_buf_used) {
|
||||
@@ -325,14 +336,14 @@ static void baum_write_packet(BaumChardev *baum, const uint8_t *buf, int len)
|
||||
/* Called when the other end seems to have a wrong idea of our display size */
|
||||
static void baum_cellCount_timer_cb(void *opaque)
|
||||
{
|
||||
BaumChardev *baum = BAUM_CHARDEV(opaque);
|
||||
BaumDriverState *baum = opaque;
|
||||
uint8_t cell_count[] = { BAUM_RSP_CellCount, baum->x * baum->y };
|
||||
DPRINTF("Timeout waiting for DisplayData, sending cell count\n");
|
||||
baum_write_packet(baum, cell_count, sizeof(cell_count));
|
||||
}
|
||||
|
||||
/* Try to interpret a whole incoming packet */
|
||||
static int baum_eat_packet(BaumChardev *baum, const uint8_t *buf, int len)
|
||||
static int baum_eat_packet(BaumDriverState *baum, const uint8_t *buf, int len)
|
||||
{
|
||||
const uint8_t *cur = buf;
|
||||
uint8_t req = 0;
|
||||
@@ -473,9 +484,9 @@ static int baum_eat_packet(BaumChardev *baum, const uint8_t *buf, int len)
|
||||
}
|
||||
|
||||
/* The other end is writing some data. Store it and try to interpret */
|
||||
static int baum_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
||||
static int baum_write(CharDriverState *chr, const uint8_t *buf, int len)
|
||||
{
|
||||
BaumChardev *baum = BAUM_CHARDEV(chr);
|
||||
BaumDriverState *baum = chr->opaque;
|
||||
int tocopy, cur, eaten, orig_len = len;
|
||||
|
||||
if (!len)
|
||||
@@ -514,16 +525,14 @@ static int baum_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
||||
}
|
||||
|
||||
/* Send the key code to the other end */
|
||||
static void baum_send_key(BaumChardev *baum, uint8_t type, uint8_t value)
|
||||
{
|
||||
static void baum_send_key(BaumDriverState *baum, uint8_t type, uint8_t value) {
|
||||
uint8_t packet[] = { type, value };
|
||||
DPRINTF("writing key %x %x\n", type, value);
|
||||
baum_write_packet(baum, packet, sizeof(packet));
|
||||
}
|
||||
|
||||
static void baum_send_key2(BaumChardev *baum, uint8_t type, uint8_t value,
|
||||
uint8_t value2)
|
||||
{
|
||||
static void baum_send_key2(BaumDriverState *baum, uint8_t type, uint8_t value,
|
||||
uint8_t value2) {
|
||||
uint8_t packet[] = { type, value, value2 };
|
||||
DPRINTF("writing key %x %x\n", type, value);
|
||||
baum_write_packet(baum, packet, sizeof(packet));
|
||||
@@ -532,7 +541,7 @@ static void baum_send_key2(BaumChardev *baum, uint8_t type, uint8_t value,
|
||||
/* We got some data on the BrlAPI socket */
|
||||
static void baum_chr_read(void *opaque)
|
||||
{
|
||||
BaumChardev *baum = BAUM_CHARDEV(opaque);
|
||||
BaumDriverState *baum = opaque;
|
||||
brlapi_keyCode_t code;
|
||||
int ret;
|
||||
if (!baum->brlapi)
|
||||
@@ -616,25 +625,41 @@ static void baum_chr_read(void *opaque)
|
||||
}
|
||||
}
|
||||
|
||||
static void char_braille_finalize(Object *obj)
|
||||
static void baum_free(struct CharDriverState *chr)
|
||||
{
|
||||
BaumChardev *baum = BAUM_CHARDEV(obj);
|
||||
BaumDriverState *baum = chr->opaque;
|
||||
|
||||
timer_free(baum->cellCount_timer);
|
||||
if (baum->brlapi) {
|
||||
brlapi__closeConnection(baum->brlapi);
|
||||
g_free(baum->brlapi);
|
||||
}
|
||||
g_free(baum);
|
||||
}
|
||||
|
||||
static void baum_chr_open(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
static CharDriverState *chr_baum_init(const char *id,
|
||||
ChardevBackend *backend,
|
||||
ChardevReturn *ret,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
BaumChardev *baum = BAUM_CHARDEV(chr);
|
||||
ChardevCommon *common = backend->u.braille.data;
|
||||
BaumDriverState *baum;
|
||||
CharDriverState *chr;
|
||||
brlapi_handle_t *handle;
|
||||
|
||||
chr = qemu_chr_alloc(common, errp);
|
||||
if (!chr) {
|
||||
return NULL;
|
||||
}
|
||||
baum = g_malloc0(sizeof(BaumDriverState));
|
||||
baum->chr = chr;
|
||||
|
||||
chr->opaque = baum;
|
||||
chr->chr_write = baum_write;
|
||||
chr->chr_accept_input = baum_accept_input;
|
||||
chr->chr_free = baum_free;
|
||||
|
||||
handle = g_malloc0(brlapi_getHandleSize());
|
||||
baum->brlapi = handle;
|
||||
|
||||
@@ -642,36 +667,27 @@ static void baum_chr_open(Chardev *chr,
|
||||
if (baum->brlapi_fd == -1) {
|
||||
error_setg(errp, "brlapi__openConnection: %s",
|
||||
brlapi_strerror(brlapi_error_location()));
|
||||
g_free(handle);
|
||||
return;
|
||||
goto fail_handle;
|
||||
}
|
||||
baum->deferred_init = 0;
|
||||
|
||||
baum->cellCount_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, baum_cellCount_timer_cb, baum);
|
||||
|
||||
qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum);
|
||||
|
||||
return chr;
|
||||
|
||||
fail_handle:
|
||||
g_free(handle);
|
||||
g_free(chr);
|
||||
g_free(baum);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void char_braille_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->open = baum_chr_open;
|
||||
cc->chr_write = baum_chr_write;
|
||||
cc->chr_accept_input = baum_chr_accept_input;
|
||||
}
|
||||
|
||||
static const TypeInfo char_braille_type_info = {
|
||||
.name = TYPE_CHARDEV_BRAILLE,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.instance_size = sizeof(BaumChardev),
|
||||
.instance_finalize = char_braille_finalize,
|
||||
.class_init = char_braille_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_braille_type_info);
|
||||
register_char_driver("braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL,
|
||||
chr_baum_init);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
|
@@ -94,8 +94,6 @@ static void cryptodev_builtin_init(
|
||||
backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendSymOpInfo);
|
||||
backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN;
|
||||
backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN;
|
||||
|
||||
cryptodev_backend_set_ready(backend, true);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -113,42 +111,23 @@ cryptodev_builtin_get_unused_session_index(
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define AES_KEYSIZE_128 16
|
||||
#define AES_KEYSIZE_192 24
|
||||
#define AES_KEYSIZE_256 32
|
||||
#define AES_KEYSIZE_128_XTS AES_KEYSIZE_256
|
||||
#define AES_KEYSIZE_256_XTS 64
|
||||
|
||||
static int
|
||||
cryptodev_builtin_get_aes_algo(uint32_t key_len, int mode, Error **errp)
|
||||
cryptodev_builtin_get_aes_algo(uint32_t key_len, Error **errp)
|
||||
{
|
||||
int algo;
|
||||
|
||||
if (key_len == AES_KEYSIZE_128) {
|
||||
if (key_len == 128 / 8) {
|
||||
algo = QCRYPTO_CIPHER_ALG_AES_128;
|
||||
} else if (key_len == AES_KEYSIZE_192) {
|
||||
} else if (key_len == 192 / 8) {
|
||||
algo = QCRYPTO_CIPHER_ALG_AES_192;
|
||||
} else if (key_len == AES_KEYSIZE_256) { /* equals AES_KEYSIZE_128_XTS */
|
||||
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
|
||||
algo = QCRYPTO_CIPHER_ALG_AES_128;
|
||||
} else {
|
||||
algo = QCRYPTO_CIPHER_ALG_AES_256;
|
||||
}
|
||||
} else if (key_len == AES_KEYSIZE_256_XTS) {
|
||||
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
|
||||
algo = QCRYPTO_CIPHER_ALG_AES_256;
|
||||
} else {
|
||||
goto err;
|
||||
}
|
||||
} else if (key_len == 256 / 8) {
|
||||
algo = QCRYPTO_CIPHER_ALG_AES_256;
|
||||
} else {
|
||||
goto err;
|
||||
error_setg(errp, "Unsupported key length :%u", key_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return algo;
|
||||
|
||||
err:
|
||||
error_setg(errp, "Unsupported key length :%u", key_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int cryptodev_builtin_create_cipher_session(
|
||||
@@ -176,48 +155,32 @@ static int cryptodev_builtin_create_cipher_session(
|
||||
|
||||
switch (sess_info->cipher_alg) {
|
||||
case VIRTIO_CRYPTO_CIPHER_AES_ECB:
|
||||
mode = QCRYPTO_CIPHER_MODE_ECB;
|
||||
algo = cryptodev_builtin_get_aes_algo(sess_info->key_len,
|
||||
mode, errp);
|
||||
errp);
|
||||
if (algo < 0) {
|
||||
return -1;
|
||||
}
|
||||
mode = QCRYPTO_CIPHER_MODE_ECB;
|
||||
break;
|
||||
case VIRTIO_CRYPTO_CIPHER_AES_CBC:
|
||||
mode = QCRYPTO_CIPHER_MODE_CBC;
|
||||
algo = cryptodev_builtin_get_aes_algo(sess_info->key_len,
|
||||
mode, errp);
|
||||
errp);
|
||||
if (algo < 0) {
|
||||
return -1;
|
||||
}
|
||||
mode = QCRYPTO_CIPHER_MODE_CBC;
|
||||
break;
|
||||
case VIRTIO_CRYPTO_CIPHER_AES_CTR:
|
||||
algo = cryptodev_builtin_get_aes_algo(sess_info->key_len,
|
||||
errp);
|
||||
if (algo < 0) {
|
||||
return -1;
|
||||
}
|
||||
mode = QCRYPTO_CIPHER_MODE_CTR;
|
||||
algo = cryptodev_builtin_get_aes_algo(sess_info->key_len,
|
||||
mode, errp);
|
||||
if (algo < 0) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case VIRTIO_CRYPTO_CIPHER_AES_XTS:
|
||||
mode = QCRYPTO_CIPHER_MODE_XTS;
|
||||
algo = cryptodev_builtin_get_aes_algo(sess_info->key_len,
|
||||
mode, errp);
|
||||
if (algo < 0) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case VIRTIO_CRYPTO_CIPHER_3DES_ECB:
|
||||
case VIRTIO_CRYPTO_CIPHER_DES_ECB:
|
||||
algo = QCRYPTO_CIPHER_ALG_DES_RFB;
|
||||
mode = QCRYPTO_CIPHER_MODE_ECB;
|
||||
algo = QCRYPTO_CIPHER_ALG_3DES;
|
||||
break;
|
||||
case VIRTIO_CRYPTO_CIPHER_3DES_CBC:
|
||||
mode = QCRYPTO_CIPHER_MODE_CBC;
|
||||
algo = QCRYPTO_CIPHER_ALG_3DES;
|
||||
break;
|
||||
case VIRTIO_CRYPTO_CIPHER_3DES_CTR:
|
||||
mode = QCRYPTO_CIPHER_MODE_CTR;
|
||||
algo = QCRYPTO_CIPHER_ALG_3DES;
|
||||
break;
|
||||
default:
|
||||
error_setg(errp, "Unsupported cipher alg :%u",
|
||||
@@ -368,8 +331,6 @@ static void cryptodev_builtin_cleanup(
|
||||
backend->conf.peers.ccs[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
cryptodev_backend_set_ready(backend, false);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@@ -73,6 +73,8 @@ void cryptodev_backend_cleanup(
|
||||
if (bc->cleanup) {
|
||||
bc->cleanup(backend, errp);
|
||||
}
|
||||
|
||||
backend->ready = false;
|
||||
}
|
||||
|
||||
int64_t cryptodev_backend_sym_create_session(
|
||||
@@ -187,39 +189,14 @@ cryptodev_backend_complete(UserCreatable *uc, Error **errp)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
backend->ready = true;
|
||||
return;
|
||||
|
||||
out:
|
||||
backend->ready = false;
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
void cryptodev_backend_set_used(CryptoDevBackend *backend, bool used)
|
||||
{
|
||||
backend->is_used = used;
|
||||
}
|
||||
|
||||
bool cryptodev_backend_is_used(CryptoDevBackend *backend)
|
||||
{
|
||||
return backend->is_used;
|
||||
}
|
||||
|
||||
void cryptodev_backend_set_ready(CryptoDevBackend *backend, bool ready)
|
||||
{
|
||||
backend->ready = ready;
|
||||
}
|
||||
|
||||
bool cryptodev_backend_is_ready(CryptoDevBackend *backend)
|
||||
{
|
||||
return backend->ready;
|
||||
}
|
||||
|
||||
static bool
|
||||
cryptodev_backend_can_be_deleted(UserCreatable *uc, Error **errp)
|
||||
{
|
||||
return !cryptodev_backend_is_used(CRYPTODEV_BACKEND(uc));
|
||||
}
|
||||
|
||||
static void cryptodev_backend_instance_init(Object *obj)
|
||||
{
|
||||
object_property_add(obj, "queues", "int",
|
||||
@@ -232,9 +209,7 @@ static void cryptodev_backend_instance_init(Object *obj)
|
||||
|
||||
static void cryptodev_backend_finalize(Object *obj)
|
||||
{
|
||||
CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj);
|
||||
|
||||
cryptodev_backend_cleanup(backend, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -243,7 +218,6 @@ cryptodev_backend_class_init(ObjectClass *oc, void *data)
|
||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||
|
||||
ucc->complete = cryptodev_backend_complete;
|
||||
ucc->can_be_deleted = cryptodev_backend_can_be_deleted;
|
||||
|
||||
QTAILQ_INIT(&crypto_clients);
|
||||
}
|
||||
|
@@ -348,24 +348,6 @@ host_memory_backend_can_be_deleted(UserCreatable *uc, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
static char *get_id(Object *o, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||
|
||||
return g_strdup(backend->id);
|
||||
}
|
||||
|
||||
static void set_id(Object *o, const char *str, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||
|
||||
if (backend->id) {
|
||||
error_setg(errp, "cannot change property value");
|
||||
return;
|
||||
}
|
||||
backend->id = g_strdup(str);
|
||||
}
|
||||
|
||||
static void
|
||||
host_memory_backend_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
@@ -395,13 +377,6 @@ host_memory_backend_class_init(ObjectClass *oc, void *data)
|
||||
HostMemPolicy_lookup,
|
||||
host_memory_backend_get_policy,
|
||||
host_memory_backend_set_policy, &error_abort);
|
||||
object_class_property_add_str(oc, "id", get_id, set_id, &error_abort);
|
||||
}
|
||||
|
||||
static void host_memory_backend_finalize(Object *o)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||
g_free(backend->id);
|
||||
}
|
||||
|
||||
static const TypeInfo host_memory_backend_info = {
|
||||
@@ -412,7 +387,6 @@ static const TypeInfo host_memory_backend_info = {
|
||||
.class_init = host_memory_backend_class_init,
|
||||
.instance_size = sizeof(HostMemoryBackend),
|
||||
.instance_init = host_memory_backend_init,
|
||||
.instance_finalize = host_memory_backend_finalize,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
|
@@ -31,23 +31,18 @@
|
||||
#define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6)
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
|
||||
CharDriverState *chr;
|
||||
QemuInputHandlerState *hs;
|
||||
int axis[INPUT_AXIS__MAX];
|
||||
bool btns[INPUT_BUTTON__MAX];
|
||||
bool btnc[INPUT_BUTTON__MAX];
|
||||
uint8_t outbuf[32];
|
||||
int outlen;
|
||||
} MouseChardev;
|
||||
} MouseState;
|
||||
|
||||
#define TYPE_CHARDEV_MSMOUSE "chardev-msmouse"
|
||||
#define MOUSE_CHARDEV(obj) \
|
||||
OBJECT_CHECK(MouseChardev, (obj), TYPE_CHARDEV_MSMOUSE)
|
||||
|
||||
static void msmouse_chr_accept_input(Chardev *chr)
|
||||
static void msmouse_chr_accept_input(CharDriverState *chr)
|
||||
{
|
||||
MouseChardev *mouse = MOUSE_CHARDEV(chr);
|
||||
MouseState *mouse = chr->opaque;
|
||||
int len;
|
||||
|
||||
len = qemu_chr_be_can_write(chr);
|
||||
@@ -65,7 +60,7 @@ static void msmouse_chr_accept_input(Chardev *chr)
|
||||
}
|
||||
}
|
||||
|
||||
static void msmouse_queue_event(MouseChardev *mouse)
|
||||
static void msmouse_queue_event(MouseState *mouse)
|
||||
{
|
||||
unsigned char bytes[4] = { 0x40, 0x00, 0x00, 0x00 };
|
||||
int dx, dy, count = 3;
|
||||
@@ -102,7 +97,7 @@ static void msmouse_queue_event(MouseChardev *mouse)
|
||||
static void msmouse_input_event(DeviceState *dev, QemuConsole *src,
|
||||
InputEvent *evt)
|
||||
{
|
||||
MouseChardev *mouse = MOUSE_CHARDEV(dev);
|
||||
MouseState *mouse = (MouseState *)dev;
|
||||
InputMoveEvent *move;
|
||||
InputBtnEvent *btn;
|
||||
|
||||
@@ -126,24 +121,24 @@ static void msmouse_input_event(DeviceState *dev, QemuConsole *src,
|
||||
|
||||
static void msmouse_input_sync(DeviceState *dev)
|
||||
{
|
||||
MouseChardev *mouse = MOUSE_CHARDEV(dev);
|
||||
Chardev *chr = CHARDEV(dev);
|
||||
MouseState *mouse = (MouseState *)dev;
|
||||
|
||||
msmouse_queue_event(mouse);
|
||||
msmouse_chr_accept_input(chr);
|
||||
msmouse_chr_accept_input(mouse->chr);
|
||||
}
|
||||
|
||||
static int msmouse_chr_write(struct Chardev *s, const uint8_t *buf, int len)
|
||||
static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int len)
|
||||
{
|
||||
/* Ignore writes to mouse port */
|
||||
return len;
|
||||
}
|
||||
|
||||
static void char_msmouse_finalize(Object *obj)
|
||||
static void msmouse_chr_free(struct CharDriverState *chr)
|
||||
{
|
||||
MouseChardev *mouse = MOUSE_CHARDEV(obj);
|
||||
MouseState *mouse = chr->opaque;
|
||||
|
||||
qemu_input_handler_unregister(mouse->hs);
|
||||
g_free(mouse);
|
||||
}
|
||||
|
||||
static QemuInputHandler msmouse_handler = {
|
||||
@@ -153,38 +148,39 @@ static QemuInputHandler msmouse_handler = {
|
||||
.sync = msmouse_input_sync,
|
||||
};
|
||||
|
||||
static void msmouse_chr_open(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
static CharDriverState *qemu_chr_open_msmouse(const char *id,
|
||||
ChardevBackend *backend,
|
||||
ChardevReturn *ret,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
MouseChardev *mouse = MOUSE_CHARDEV(chr);
|
||||
ChardevCommon *common = backend->u.msmouse.data;
|
||||
MouseState *mouse;
|
||||
CharDriverState *chr;
|
||||
|
||||
chr = qemu_chr_alloc(common, errp);
|
||||
if (!chr) {
|
||||
return NULL;
|
||||
}
|
||||
chr->chr_write = msmouse_chr_write;
|
||||
chr->chr_free = msmouse_chr_free;
|
||||
chr->chr_accept_input = msmouse_chr_accept_input;
|
||||
*be_opened = false;
|
||||
|
||||
mouse = g_new0(MouseState, 1);
|
||||
mouse->hs = qemu_input_handler_register((DeviceState *)mouse,
|
||||
&msmouse_handler);
|
||||
|
||||
mouse->chr = chr;
|
||||
chr->opaque = mouse;
|
||||
|
||||
return chr;
|
||||
}
|
||||
|
||||
static void char_msmouse_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->open = msmouse_chr_open;
|
||||
cc->chr_write = msmouse_chr_write;
|
||||
cc->chr_accept_input = msmouse_chr_accept_input;
|
||||
}
|
||||
|
||||
static const TypeInfo char_msmouse_type_info = {
|
||||
.name = TYPE_CHARDEV_MSMOUSE,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.instance_size = sizeof(MouseChardev),
|
||||
.instance_finalize = char_msmouse_finalize,
|
||||
.class_init = char_msmouse_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_msmouse_type_info);
|
||||
register_char_driver("msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL,
|
||||
qemu_chr_open_msmouse);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
|
@@ -86,7 +86,7 @@ static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size)
|
||||
static void rng_egd_opened(RngBackend *b, Error **errp)
|
||||
{
|
||||
RngEgd *s = RNG_EGD(b);
|
||||
Chardev *chr;
|
||||
CharDriverState *chr;
|
||||
|
||||
if (s->chr_name == NULL) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
|
||||
@@ -125,7 +125,7 @@ static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp)
|
||||
static char *rng_egd_get_chardev(Object *obj, Error **errp)
|
||||
{
|
||||
RngEgd *s = RNG_EGD(obj);
|
||||
Chardev *chr = qemu_chr_fe_get_driver(&s->chr);
|
||||
CharDriverState *chr = qemu_chr_fe_get_driver(&s->chr);
|
||||
|
||||
if (chr && chr->label) {
|
||||
return g_strdup(chr->label);
|
||||
|
@@ -30,18 +30,13 @@
|
||||
#define BUF_SIZE 32
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
|
||||
CharDriverState *chr;
|
||||
uint8_t in_buf[32];
|
||||
int in_buf_used;
|
||||
} TestdevChardev;
|
||||
|
||||
#define TYPE_CHARDEV_TESTDEV "chardev-testdev"
|
||||
#define TESTDEV_CHARDEV(obj) \
|
||||
OBJECT_CHECK(TestdevChardev, (obj), TYPE_CHARDEV_TESTDEV)
|
||||
} TestdevCharState;
|
||||
|
||||
/* Try to interpret a whole incoming packet */
|
||||
static int testdev_eat_packet(TestdevChardev *testdev)
|
||||
static int testdev_eat_packet(TestdevCharState *testdev)
|
||||
{
|
||||
const uint8_t *cur = testdev->in_buf;
|
||||
int len = testdev->in_buf_used;
|
||||
@@ -82,9 +77,9 @@ static int testdev_eat_packet(TestdevChardev *testdev)
|
||||
}
|
||||
|
||||
/* The other end is writing some data. Store it and try to interpret */
|
||||
static int testdev_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
||||
static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len)
|
||||
{
|
||||
TestdevChardev *testdev = TESTDEV_CHARDEV(chr);
|
||||
TestdevCharState *testdev = chr->opaque;
|
||||
int tocopy, eaten, orig_len = len;
|
||||
|
||||
while (len) {
|
||||
@@ -107,23 +102,36 @@ static int testdev_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
||||
return orig_len;
|
||||
}
|
||||
|
||||
static void char_testdev_class_init(ObjectClass *oc, void *data)
|
||||
static void testdev_free(struct CharDriverState *chr)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
TestdevCharState *testdev = chr->opaque;
|
||||
|
||||
cc->chr_write = testdev_chr_write;
|
||||
g_free(testdev);
|
||||
}
|
||||
|
||||
static const TypeInfo char_testdev_type_info = {
|
||||
.name = TYPE_CHARDEV_TESTDEV,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.instance_size = sizeof(TestdevChardev),
|
||||
.class_init = char_testdev_class_init,
|
||||
};
|
||||
static CharDriverState *chr_testdev_init(const char *id,
|
||||
ChardevBackend *backend,
|
||||
ChardevReturn *ret,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
TestdevCharState *testdev;
|
||||
CharDriverState *chr;
|
||||
|
||||
testdev = g_new0(TestdevCharState, 1);
|
||||
testdev->chr = chr = g_new0(CharDriverState, 1);
|
||||
|
||||
chr->opaque = testdev;
|
||||
chr->chr_write = testdev_write;
|
||||
chr->chr_free = testdev_free;
|
||||
|
||||
return chr;
|
||||
}
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_testdev_type_info);
|
||||
register_char_driver("testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL,
|
||||
chr_testdev_init);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
|
@@ -29,7 +29,7 @@
|
||||
#include "exec/cpu-common.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/balloon.h"
|
||||
#include "trace-root.h"
|
||||
#include "trace.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qapi/qmp/qjson.h"
|
||||
|
28
block.c
28
block.c
@@ -22,7 +22,7 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/trace.h"
|
||||
#include "trace.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/blockjob.h"
|
||||
#include "block/nbd.h"
|
||||
@@ -1851,7 +1851,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
|
||||
bdrv_refresh_filename(bs);
|
||||
|
||||
/* Check if any unknown options were used */
|
||||
if (qdict_size(options) != 0) {
|
||||
if (options && (qdict_size(options) != 0)) {
|
||||
const QDictEntry *entry = qdict_first(options);
|
||||
if (flags & BDRV_O_PROTOCOL) {
|
||||
error_setg(errp, "Block protocol '%s' doesn't support the option "
|
||||
@@ -3145,7 +3145,6 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
|
||||
int is_protocol = 0;
|
||||
BlockDriverState *curr_bs = NULL;
|
||||
BlockDriverState *retval = NULL;
|
||||
Error *local_error = NULL;
|
||||
|
||||
if (!bs || !bs->drv || !backing_file) {
|
||||
return NULL;
|
||||
@@ -3166,18 +3165,6 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
|
||||
retval = curr_bs->backing->bs;
|
||||
break;
|
||||
}
|
||||
/* Also check against the full backing filename for the image */
|
||||
bdrv_get_full_backing_filename(curr_bs, backing_file_full, PATH_MAX,
|
||||
&local_error);
|
||||
if (local_error == NULL) {
|
||||
if (strcmp(backing_file, backing_file_full) == 0) {
|
||||
retval = curr_bs->backing->bs;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
error_free(local_error);
|
||||
local_error = NULL;
|
||||
}
|
||||
} else {
|
||||
/* If not an absolute filename path, make it relative to the current
|
||||
* image's filename path */
|
||||
@@ -3248,18 +3235,19 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
if (!(bs->open_flags & BDRV_O_INACTIVE)) {
|
||||
return;
|
||||
}
|
||||
bs->open_flags &= ~BDRV_O_INACTIVE;
|
||||
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
bdrv_invalidate_cache(child->bs, &local_err);
|
||||
if (bs->drv->bdrv_invalidate_cache) {
|
||||
bs->drv->bdrv_invalidate_cache(bs, &local_err);
|
||||
if (local_err) {
|
||||
bs->open_flags |= BDRV_O_INACTIVE;
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bs->open_flags &= ~BDRV_O_INACTIVE;
|
||||
if (bs->drv->bdrv_invalidate_cache) {
|
||||
bs->drv->bdrv_invalidate_cache(bs, &local_err);
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
bdrv_invalidate_cache(child->bs, &local_err);
|
||||
if (local_err) {
|
||||
bs->open_flags |= BDRV_O_INACTIVE;
|
||||
error_propagate(errp, local_err);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
block-obj-y += raw-format.o qcow.o vdi.o vmdk.o cloop.o bochs.o vpc.o vvfat.o dmg.o
|
||||
block-obj-y += raw_bsd.o qcow.o vdi.o vmdk.o cloop.o bochs.o vpc.o vvfat.o dmg.o
|
||||
block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o
|
||||
block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
|
||||
block-obj-y += qed-check.o
|
||||
@@ -6,15 +6,14 @@ block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o
|
||||
block-obj-y += quorum.o
|
||||
block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o
|
||||
block-obj-y += block-backend.o snapshot.o qapi.o
|
||||
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
|
||||
block-obj-$(CONFIG_POSIX) += file-posix.o
|
||||
block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
|
||||
block-obj-$(CONFIG_POSIX) += raw-posix.o
|
||||
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
|
||||
block-obj-y += null.o mirror.o commit.o io.o
|
||||
block-obj-y += throttle-groups.o
|
||||
|
||||
block-obj-y += nbd.o nbd-client.o sheepdog.o
|
||||
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
|
||||
block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o
|
||||
block-obj-$(CONFIG_LIBNFS) += nfs.o
|
||||
block-obj-$(CONFIG_CURL) += curl.o
|
||||
block-obj-$(CONFIG_RBD) += rbd.o
|
||||
|
@@ -58,6 +58,10 @@ typedef struct BlkdebugSuspendedReq {
|
||||
QLIST_ENTRY(BlkdebugSuspendedReq) next;
|
||||
} BlkdebugSuspendedReq;
|
||||
|
||||
static const AIOCBInfo blkdebug_aiocb_info = {
|
||||
.aiocb_size = sizeof(BlkdebugAIOCB),
|
||||
};
|
||||
|
||||
enum {
|
||||
ACTION_INJECT_ERROR,
|
||||
ACTION_SET_STATE,
|
||||
@@ -73,7 +77,7 @@ typedef struct BlkdebugRule {
|
||||
int error;
|
||||
int immediately;
|
||||
int once;
|
||||
int64_t offset;
|
||||
int64_t sector;
|
||||
} inject;
|
||||
struct {
|
||||
int new_state;
|
||||
@@ -170,7 +174,6 @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
|
||||
const char* event_name;
|
||||
BlkdebugEvent event;
|
||||
struct BlkdebugRule *rule;
|
||||
int64_t sector;
|
||||
|
||||
/* Find the right event for the rule */
|
||||
event_name = qemu_opt_get(opts, "event");
|
||||
@@ -197,9 +200,7 @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
|
||||
rule->options.inject.once = qemu_opt_get_bool(opts, "once", 0);
|
||||
rule->options.inject.immediately =
|
||||
qemu_opt_get_bool(opts, "immediately", 0);
|
||||
sector = qemu_opt_get_number(opts, "sector", -1);
|
||||
rule->options.inject.offset =
|
||||
sector == -1 ? -1 : sector * BDRV_SECTOR_SIZE;
|
||||
rule->options.inject.sector = qemu_opt_get_number(opts, "sector", -1);
|
||||
break;
|
||||
|
||||
case ACTION_SET_STATE:
|
||||
@@ -407,14 +408,17 @@ out:
|
||||
|
||||
static void error_callback_bh(void *opaque)
|
||||
{
|
||||
Coroutine *co = opaque;
|
||||
qemu_coroutine_enter(co);
|
||||
struct BlkdebugAIOCB *acb = opaque;
|
||||
acb->common.cb(acb->common.opaque, acb->ret);
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
static int inject_error(BlockDriverState *bs, BlkdebugRule *rule)
|
||||
static BlockAIOCB *inject_error(BlockDriverState *bs,
|
||||
BlockCompletionFunc *cb, void *opaque, BlkdebugRule *rule)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
int error = rule->options.inject.error;
|
||||
struct BlkdebugAIOCB *acb;
|
||||
bool immediately = rule->options.inject.immediately;
|
||||
|
||||
if (rule->options.inject.once) {
|
||||
@@ -422,79 +426,81 @@ static int inject_error(BlockDriverState *bs, BlkdebugRule *rule)
|
||||
remove_rule(rule);
|
||||
}
|
||||
|
||||
if (!immediately) {
|
||||
aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), error_callback_bh,
|
||||
qemu_coroutine_self());
|
||||
qemu_coroutine_yield();
|
||||
if (immediately) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return -error;
|
||||
acb = qemu_aio_get(&blkdebug_aiocb_info, bs, cb, opaque);
|
||||
acb->ret = -error;
|
||||
|
||||
aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), error_callback_bh, acb);
|
||||
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
blkdebug_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
static BlockAIOCB *blkdebug_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugRule *rule = NULL;
|
||||
|
||||
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
|
||||
uint64_t inject_offset = rule->options.inject.offset;
|
||||
|
||||
if (inject_offset == -1 ||
|
||||
(inject_offset >= offset && inject_offset < offset + bytes))
|
||||
{
|
||||
if (rule->options.inject.sector == -1 ||
|
||||
(rule->options.inject.sector >= sector_num &&
|
||||
rule->options.inject.sector < sector_num + nb_sectors)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rule && rule->options.inject.error) {
|
||||
return inject_error(bs, rule);
|
||||
return inject_error(bs, cb, opaque, rule);
|
||||
}
|
||||
|
||||
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
|
||||
return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors,
|
||||
cb, opaque);
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
static BlockAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugRule *rule = NULL;
|
||||
|
||||
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
|
||||
uint64_t inject_offset = rule->options.inject.offset;
|
||||
|
||||
if (inject_offset == -1 ||
|
||||
(inject_offset >= offset && inject_offset < offset + bytes))
|
||||
{
|
||||
if (rule->options.inject.sector == -1 ||
|
||||
(rule->options.inject.sector >= sector_num &&
|
||||
rule->options.inject.sector < sector_num + nb_sectors)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rule && rule->options.inject.error) {
|
||||
return inject_error(bs, rule);
|
||||
return inject_error(bs, cb, opaque, rule);
|
||||
}
|
||||
|
||||
return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
|
||||
return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors,
|
||||
cb, opaque);
|
||||
}
|
||||
|
||||
static int blkdebug_co_flush(BlockDriverState *bs)
|
||||
static BlockAIOCB *blkdebug_aio_flush(BlockDriverState *bs,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugRule *rule = NULL;
|
||||
|
||||
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
|
||||
if (rule->options.inject.offset == -1) {
|
||||
if (rule->options.inject.sector == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rule && rule->options.inject.error) {
|
||||
return inject_error(bs, rule);
|
||||
return inject_error(bs, cb, opaque, rule);
|
||||
}
|
||||
|
||||
return bdrv_co_flush(bs->file->bs);
|
||||
return bdrv_aio_flush(bs->file->bs, cb, opaque);
|
||||
}
|
||||
|
||||
|
||||
@@ -746,9 +752,9 @@ static BlockDriver bdrv_blkdebug = {
|
||||
.bdrv_refresh_filename = blkdebug_refresh_filename,
|
||||
.bdrv_refresh_limits = blkdebug_refresh_limits,
|
||||
|
||||
.bdrv_co_preadv = blkdebug_co_preadv,
|
||||
.bdrv_co_pwritev = blkdebug_co_pwritev,
|
||||
.bdrv_co_flush_to_disk = blkdebug_co_flush,
|
||||
.bdrv_aio_readv = blkdebug_aio_readv,
|
||||
.bdrv_aio_writev = blkdebug_aio_writev,
|
||||
.bdrv_aio_flush = blkdebug_aio_flush,
|
||||
|
||||
.bdrv_debug_event = blkdebug_debug_event,
|
||||
.bdrv_debug_breakpoint = blkdebug_debug_breakpoint,
|
||||
|
@@ -19,36 +19,38 @@ typedef struct {
|
||||
BdrvChild *test_file;
|
||||
} BDRVBlkverifyState;
|
||||
|
||||
typedef struct BlkverifyRequest {
|
||||
Coroutine *co;
|
||||
BlockDriverState *bs;
|
||||
typedef struct BlkverifyAIOCB BlkverifyAIOCB;
|
||||
struct BlkverifyAIOCB {
|
||||
BlockAIOCB common;
|
||||
|
||||
/* Request metadata */
|
||||
bool is_write;
|
||||
uint64_t offset;
|
||||
uint64_t bytes;
|
||||
int flags;
|
||||
|
||||
int (*request_fn)(BdrvChild *, int64_t, unsigned int, QEMUIOVector *,
|
||||
BdrvRequestFlags);
|
||||
|
||||
int ret; /* test image result */
|
||||
int raw_ret; /* raw image result */
|
||||
int64_t sector_num;
|
||||
int nb_sectors;
|
||||
|
||||
int ret; /* first completed request's result */
|
||||
unsigned int done; /* completion counter */
|
||||
|
||||
QEMUIOVector *qiov; /* user I/O vector */
|
||||
QEMUIOVector *raw_qiov; /* cloned I/O vector for raw file */
|
||||
} BlkverifyRequest;
|
||||
QEMUIOVector raw_qiov; /* cloned I/O vector for raw file */
|
||||
void *buf; /* buffer for raw file I/O */
|
||||
|
||||
static void GCC_FMT_ATTR(2, 3) blkverify_err(BlkverifyRequest *r,
|
||||
void (*verify)(BlkverifyAIOCB *acb);
|
||||
};
|
||||
|
||||
static const AIOCBInfo blkverify_aiocb_info = {
|
||||
.aiocb_size = sizeof(BlkverifyAIOCB),
|
||||
};
|
||||
|
||||
static void GCC_FMT_ATTR(2, 3) blkverify_err(BlkverifyAIOCB *acb,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "blkverify: %s offset=%" PRId64 " bytes=%" PRId64 " ",
|
||||
r->is_write ? "write" : "read", r->offset, r->bytes);
|
||||
fprintf(stderr, "blkverify: %s sector_num=%" PRId64 " nb_sectors=%d ",
|
||||
acb->is_write ? "write" : "read", acb->sector_num,
|
||||
acb->nb_sectors);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(ap);
|
||||
@@ -164,106 +166,113 @@ static int64_t blkverify_getlength(BlockDriverState *bs)
|
||||
return bdrv_getlength(s->test_file->bs);
|
||||
}
|
||||
|
||||
static void coroutine_fn blkverify_do_test_req(void *opaque)
|
||||
static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write,
|
||||
int64_t sector_num, QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
BlkverifyRequest *r = opaque;
|
||||
BDRVBlkverifyState *s = r->bs->opaque;
|
||||
BlkverifyAIOCB *acb = qemu_aio_get(&blkverify_aiocb_info, bs, cb, opaque);
|
||||
|
||||
r->ret = r->request_fn(s->test_file, r->offset, r->bytes, r->qiov,
|
||||
r->flags);
|
||||
r->done++;
|
||||
qemu_coroutine_enter_if_inactive(r->co);
|
||||
acb->is_write = is_write;
|
||||
acb->sector_num = sector_num;
|
||||
acb->nb_sectors = nb_sectors;
|
||||
acb->ret = -EINPROGRESS;
|
||||
acb->done = 0;
|
||||
acb->qiov = qiov;
|
||||
acb->buf = NULL;
|
||||
acb->verify = NULL;
|
||||
return acb;
|
||||
}
|
||||
|
||||
static void coroutine_fn blkverify_do_raw_req(void *opaque)
|
||||
static void blkverify_aio_bh(void *opaque)
|
||||
{
|
||||
BlkverifyRequest *r = opaque;
|
||||
BlkverifyAIOCB *acb = opaque;
|
||||
|
||||
r->raw_ret = r->request_fn(r->bs->file, r->offset, r->bytes, r->raw_qiov,
|
||||
r->flags);
|
||||
r->done++;
|
||||
qemu_coroutine_enter_if_inactive(r->co);
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
blkverify_co_prwv(BlockDriverState *bs, BlkverifyRequest *r, uint64_t offset,
|
||||
uint64_t bytes, QEMUIOVector *qiov, QEMUIOVector *raw_qiov,
|
||||
int flags, bool is_write)
|
||||
{
|
||||
Coroutine *co_a, *co_b;
|
||||
|
||||
*r = (BlkverifyRequest) {
|
||||
.co = qemu_coroutine_self(),
|
||||
.bs = bs,
|
||||
.offset = offset,
|
||||
.bytes = bytes,
|
||||
.qiov = qiov,
|
||||
.raw_qiov = raw_qiov,
|
||||
.flags = flags,
|
||||
.is_write = is_write,
|
||||
.request_fn = is_write ? bdrv_co_pwritev : bdrv_co_preadv,
|
||||
};
|
||||
|
||||
co_a = qemu_coroutine_create(blkverify_do_test_req, r);
|
||||
co_b = qemu_coroutine_create(blkverify_do_raw_req, r);
|
||||
|
||||
qemu_coroutine_enter(co_a);
|
||||
qemu_coroutine_enter(co_b);
|
||||
|
||||
while (r->done < 2) {
|
||||
qemu_coroutine_yield();
|
||||
if (acb->buf) {
|
||||
qemu_iovec_destroy(&acb->raw_qiov);
|
||||
qemu_vfree(acb->buf);
|
||||
}
|
||||
|
||||
if (r->ret != r->raw_ret) {
|
||||
blkverify_err(r, "return value mismatch %d != %d", r->ret, r->raw_ret);
|
||||
}
|
||||
|
||||
return r->ret;
|
||||
acb->common.cb(acb->common.opaque, acb->ret);
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
blkverify_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
static void blkverify_aio_cb(void *opaque, int ret)
|
||||
{
|
||||
BlkverifyRequest r;
|
||||
QEMUIOVector raw_qiov;
|
||||
void *buf;
|
||||
ssize_t cmp_offset;
|
||||
int ret;
|
||||
BlkverifyAIOCB *acb = opaque;
|
||||
|
||||
buf = qemu_blockalign(bs->file->bs, qiov->size);
|
||||
qemu_iovec_init(&raw_qiov, qiov->niov);
|
||||
qemu_iovec_clone(&raw_qiov, qiov, buf);
|
||||
switch (++acb->done) {
|
||||
case 1:
|
||||
acb->ret = ret;
|
||||
break;
|
||||
|
||||
ret = blkverify_co_prwv(bs, &r, offset, bytes, qiov, &raw_qiov, flags,
|
||||
false);
|
||||
case 2:
|
||||
if (acb->ret != ret) {
|
||||
blkverify_err(acb, "return value mismatch %d != %d", acb->ret, ret);
|
||||
}
|
||||
|
||||
cmp_offset = qemu_iovec_compare(qiov, &raw_qiov);
|
||||
if (cmp_offset != -1) {
|
||||
blkverify_err(&r, "contents mismatch at offset %" PRId64,
|
||||
offset + cmp_offset);
|
||||
if (acb->verify) {
|
||||
acb->verify(acb);
|
||||
}
|
||||
|
||||
aio_bh_schedule_oneshot(bdrv_get_aio_context(acb->common.bs),
|
||||
blkverify_aio_bh, acb);
|
||||
break;
|
||||
}
|
||||
|
||||
qemu_iovec_destroy(&raw_qiov);
|
||||
qemu_vfree(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
blkverify_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
static void blkverify_verify_readv(BlkverifyAIOCB *acb)
|
||||
{
|
||||
BlkverifyRequest r;
|
||||
return blkverify_co_prwv(bs, &r, offset, bytes, qiov, qiov, flags, true);
|
||||
ssize_t offset = qemu_iovec_compare(acb->qiov, &acb->raw_qiov);
|
||||
if (offset != -1) {
|
||||
blkverify_err(acb, "contents mismatch in sector %" PRId64,
|
||||
acb->sector_num + (int64_t)(offset / BDRV_SECTOR_SIZE));
|
||||
}
|
||||
}
|
||||
|
||||
static int blkverify_co_flush(BlockDriverState *bs)
|
||||
static BlockAIOCB *blkverify_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
BlkverifyAIOCB *acb = blkverify_aio_get(bs, false, sector_num, qiov,
|
||||
nb_sectors, cb, opaque);
|
||||
|
||||
acb->verify = blkverify_verify_readv;
|
||||
acb->buf = qemu_blockalign(bs->file->bs, qiov->size);
|
||||
qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov);
|
||||
qemu_iovec_clone(&acb->raw_qiov, qiov, acb->buf);
|
||||
|
||||
bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors,
|
||||
blkverify_aio_cb, acb);
|
||||
bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors,
|
||||
blkverify_aio_cb, acb);
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static BlockAIOCB *blkverify_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
BlkverifyAIOCB *acb = blkverify_aio_get(bs, true, sector_num, qiov,
|
||||
nb_sectors, cb, opaque);
|
||||
|
||||
bdrv_aio_writev(s->test_file, sector_num, qiov, nb_sectors,
|
||||
blkverify_aio_cb, acb);
|
||||
bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors,
|
||||
blkverify_aio_cb, acb);
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static BlockAIOCB *blkverify_aio_flush(BlockDriverState *bs,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
|
||||
/* Only flush test file, the raw file is not important */
|
||||
return bdrv_co_flush(s->test_file->bs);
|
||||
return bdrv_aio_flush(s->test_file->bs, cb, opaque);
|
||||
}
|
||||
|
||||
static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||
@@ -323,9 +332,9 @@ static BlockDriver bdrv_blkverify = {
|
||||
.bdrv_getlength = blkverify_getlength,
|
||||
.bdrv_refresh_filename = blkverify_refresh_filename,
|
||||
|
||||
.bdrv_co_preadv = blkverify_co_preadv,
|
||||
.bdrv_co_pwritev = blkverify_co_pwritev,
|
||||
.bdrv_co_flush = blkverify_co_flush,
|
||||
.bdrv_aio_readv = blkverify_aio_readv,
|
||||
.bdrv_aio_writev = blkverify_aio_writev,
|
||||
.bdrv_aio_flush = blkverify_aio_flush,
|
||||
|
||||
.is_filter = true,
|
||||
.bdrv_recurse_is_first_non_filter = blkverify_recurse_is_first_non_filter,
|
||||
|
@@ -192,19 +192,19 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
|
||||
switch (action) {
|
||||
case CURL_POLL_IN:
|
||||
aio_set_fd_handler(s->aio_context, fd, false,
|
||||
curl_multi_read, NULL, NULL, state);
|
||||
curl_multi_read, NULL, state);
|
||||
break;
|
||||
case CURL_POLL_OUT:
|
||||
aio_set_fd_handler(s->aio_context, fd, false,
|
||||
NULL, curl_multi_do, NULL, state);
|
||||
NULL, curl_multi_do, state);
|
||||
break;
|
||||
case CURL_POLL_INOUT:
|
||||
aio_set_fd_handler(s->aio_context, fd, false,
|
||||
curl_multi_read, curl_multi_do, NULL, state);
|
||||
curl_multi_read, curl_multi_do, state);
|
||||
break;
|
||||
case CURL_POLL_REMOVE:
|
||||
aio_set_fd_handler(s->aio_context, fd, false,
|
||||
NULL, NULL, NULL, NULL);
|
||||
NULL, NULL, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@@ -1253,7 +1253,7 @@ static int qemu_gluster_has_zero_init(BlockDriverState *bs)
|
||||
* If @start is in a trailing hole or beyond EOF, return -ENXIO.
|
||||
* If we can't find out, return a negative errno other than -ENXIO.
|
||||
*
|
||||
* (Shamefully copied from file-posix.c, only miniscule adaptions.)
|
||||
* (Shamefully copied from raw-posix.c, only miniscule adaptions.)
|
||||
*/
|
||||
static int find_allocation(BlockDriverState *bs, off_t start,
|
||||
off_t *data, off_t *hole)
|
||||
@@ -1349,7 +1349,7 @@ exit:
|
||||
* 'nb_sectors' is the max value 'pnum' should be set to. If nb_sectors goes
|
||||
* beyond the end of the disk image it will be clamped.
|
||||
*
|
||||
* (Based on raw_co_get_block_status() from file-posix.c.)
|
||||
* (Based on raw_co_get_block_status() from raw-posix.c.)
|
||||
*/
|
||||
static int64_t coroutine_fn qemu_gluster_co_get_block_status(
|
||||
BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum,
|
||||
|
41
block/io.c
41
block/io.c
@@ -228,7 +228,9 @@ void bdrv_drained_begin(BlockDriverState *bs)
|
||||
bdrv_parent_drained_begin(bs);
|
||||
}
|
||||
|
||||
bdrv_io_unplugged_begin(bs);
|
||||
bdrv_drain_recurse(bs);
|
||||
bdrv_io_unplugged_end(bs);
|
||||
}
|
||||
|
||||
void bdrv_drained_end(BlockDriverState *bs)
|
||||
@@ -300,6 +302,7 @@ void bdrv_drain_all_begin(void)
|
||||
|
||||
aio_context_acquire(aio_context);
|
||||
bdrv_parent_drained_begin(bs);
|
||||
bdrv_io_unplugged_begin(bs);
|
||||
aio_disable_external(aio_context);
|
||||
aio_context_release(aio_context);
|
||||
|
||||
@@ -344,6 +347,7 @@ void bdrv_drain_all_end(void)
|
||||
|
||||
aio_context_acquire(aio_context);
|
||||
aio_enable_external(aio_context);
|
||||
bdrv_io_unplugged_end(bs);
|
||||
bdrv_parent_drained_end(bs);
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
@@ -2646,7 +2650,7 @@ void bdrv_io_plug(BlockDriverState *bs)
|
||||
bdrv_io_plug(child->bs);
|
||||
}
|
||||
|
||||
if (bs->io_plugged++ == 0) {
|
||||
if (bs->io_plugged++ == 0 && bs->io_plug_disabled == 0) {
|
||||
BlockDriver *drv = bs->drv;
|
||||
if (drv && drv->bdrv_io_plug) {
|
||||
drv->bdrv_io_plug(bs);
|
||||
@@ -2659,7 +2663,7 @@ void bdrv_io_unplug(BlockDriverState *bs)
|
||||
BdrvChild *child;
|
||||
|
||||
assert(bs->io_plugged);
|
||||
if (--bs->io_plugged == 0) {
|
||||
if (--bs->io_plugged == 0 && bs->io_plug_disabled == 0) {
|
||||
BlockDriver *drv = bs->drv;
|
||||
if (drv && drv->bdrv_io_unplug) {
|
||||
drv->bdrv_io_unplug(bs);
|
||||
@@ -2670,3 +2674,36 @@ void bdrv_io_unplug(BlockDriverState *bs)
|
||||
bdrv_io_unplug(child->bs);
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_io_unplugged_begin(BlockDriverState *bs)
|
||||
{
|
||||
BdrvChild *child;
|
||||
|
||||
if (bs->io_plug_disabled++ == 0 && bs->io_plugged > 0) {
|
||||
BlockDriver *drv = bs->drv;
|
||||
if (drv && drv->bdrv_io_unplug) {
|
||||
drv->bdrv_io_unplug(bs);
|
||||
}
|
||||
}
|
||||
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
bdrv_io_unplugged_begin(child->bs);
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_io_unplugged_end(BlockDriverState *bs)
|
||||
{
|
||||
BdrvChild *child;
|
||||
|
||||
assert(bs->io_plug_disabled);
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
bdrv_io_unplugged_end(child->bs);
|
||||
}
|
||||
|
||||
if (--bs->io_plug_disabled == 0 && bs->io_plugged > 0) {
|
||||
BlockDriver *drv = bs->drv;
|
||||
if (drv && drv->bdrv_io_plug) {
|
||||
drv->bdrv_io_plug(bs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* QEMU Block driver for iSCSI images (static options)
|
||||
*
|
||||
* Copyright (c) 2017 Peter Lieven <pl@kamp.de>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/config-file.h"
|
||||
|
||||
static QemuOptsList qemu_iscsi_opts = {
|
||||
.name = "iscsi",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(qemu_iscsi_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = "user",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "username for CHAP authentication to target",
|
||||
},{
|
||||
.name = "password",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "password for CHAP authentication to target",
|
||||
},{
|
||||
.name = "password-secret",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "ID of the secret providing password for CHAP "
|
||||
"authentication to target",
|
||||
},{
|
||||
.name = "header-digest",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "HeaderDigest setting. "
|
||||
"{CRC32C|CRC32C-NONE|NONE-CRC32C|NONE}",
|
||||
},{
|
||||
.name = "initiator-name",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Initiator iqn name to use when connecting",
|
||||
},{
|
||||
.name = "timeout",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "Request timeout in seconds (default 0 = no timeout)",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
static void iscsi_block_opts_init(void)
|
||||
{
|
||||
qemu_add_opts(&qemu_iscsi_opts);
|
||||
}
|
||||
|
||||
block_init(iscsi_block_opts_init);
|
@@ -362,7 +362,6 @@ iscsi_set_events(IscsiLun *iscsilun)
|
||||
false,
|
||||
(ev & POLLIN) ? iscsi_process_read : NULL,
|
||||
(ev & POLLOUT) ? iscsi_process_write : NULL,
|
||||
NULL,
|
||||
iscsilun);
|
||||
iscsilun->events = ev;
|
||||
}
|
||||
@@ -1531,7 +1530,7 @@ static void iscsi_detach_aio_context(BlockDriverState *bs)
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
|
||||
aio_set_fd_handler(iscsilun->aio_context, iscsi_get_fd(iscsilun->iscsi),
|
||||
false, NULL, NULL, NULL, NULL);
|
||||
false, NULL, NULL, NULL);
|
||||
iscsilun->events = 0;
|
||||
|
||||
if (iscsilun->nop_timer) {
|
||||
|
@@ -255,20 +255,6 @@ static void qemu_laio_completion_cb(EventNotifier *e)
|
||||
}
|
||||
}
|
||||
|
||||
static bool qemu_laio_poll_cb(void *opaque)
|
||||
{
|
||||
EventNotifier *e = opaque;
|
||||
LinuxAioState *s = container_of(e, LinuxAioState, e);
|
||||
struct io_event *events;
|
||||
|
||||
if (!io_getevents_peek(s->ctx, &events)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
qemu_laio_process_completions_and_submit(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void laio_cancel(BlockAIOCB *blockacb)
|
||||
{
|
||||
struct qemu_laiocb *laiocb = (struct qemu_laiocb *)blockacb;
|
||||
@@ -453,7 +439,7 @@ BlockAIOCB *laio_submit(BlockDriverState *bs, LinuxAioState *s, int fd,
|
||||
|
||||
void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context)
|
||||
{
|
||||
aio_set_event_notifier(old_context, &s->e, false, NULL, NULL);
|
||||
aio_set_event_notifier(old_context, &s->e, false, NULL);
|
||||
qemu_bh_delete(s->completion_bh);
|
||||
}
|
||||
|
||||
@@ -462,8 +448,7 @@ void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context)
|
||||
s->aio_context = new_context;
|
||||
s->completion_bh = aio_bh_new(new_context, qemu_laio_completion_bh, s);
|
||||
aio_set_event_notifier(new_context, &s->e, false,
|
||||
qemu_laio_completion_cb,
|
||||
qemu_laio_poll_cb);
|
||||
qemu_laio_completion_cb);
|
||||
}
|
||||
|
||||
LinuxAioState *laio_init(void)
|
||||
|
@@ -145,7 +145,7 @@ static int nbd_co_send_request(BlockDriverState *bs,
|
||||
aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_set_fd_handler(aio_context, s->sioc->fd, false,
|
||||
nbd_reply_ready, nbd_restart_write, NULL, bs);
|
||||
nbd_reply_ready, nbd_restart_write, bs);
|
||||
if (qiov) {
|
||||
qio_channel_set_cork(s->ioc, true);
|
||||
rc = nbd_send_request(s->ioc, request);
|
||||
@@ -161,7 +161,7 @@ static int nbd_co_send_request(BlockDriverState *bs,
|
||||
rc = nbd_send_request(s->ioc, request);
|
||||
}
|
||||
aio_set_fd_handler(aio_context, s->sioc->fd, false,
|
||||
nbd_reply_ready, NULL, NULL, bs);
|
||||
nbd_reply_ready, NULL, bs);
|
||||
s->send_coroutine = NULL;
|
||||
qemu_co_mutex_unlock(&s->send_mutex);
|
||||
return rc;
|
||||
@@ -366,14 +366,14 @@ void nbd_client_detach_aio_context(BlockDriverState *bs)
|
||||
{
|
||||
aio_set_fd_handler(bdrv_get_aio_context(bs),
|
||||
nbd_get_client_session(bs)->sioc->fd,
|
||||
false, NULL, NULL, NULL, NULL);
|
||||
false, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void nbd_client_attach_aio_context(BlockDriverState *bs,
|
||||
AioContext *new_context)
|
||||
{
|
||||
aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sioc->fd,
|
||||
false, nbd_reply_ready, NULL, NULL, bs);
|
||||
false, nbd_reply_ready, NULL, bs);
|
||||
}
|
||||
|
||||
void nbd_client_close(BlockDriverState *bs)
|
||||
|
@@ -198,8 +198,7 @@ static void nfs_set_events(NFSClient *client)
|
||||
aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
|
||||
false,
|
||||
(ev & POLLIN) ? nfs_process_read : NULL,
|
||||
(ev & POLLOUT) ? nfs_process_write : NULL,
|
||||
NULL, client);
|
||||
(ev & POLLOUT) ? nfs_process_write : NULL, client);
|
||||
|
||||
}
|
||||
client->events = ev;
|
||||
@@ -397,7 +396,7 @@ static void nfs_detach_aio_context(BlockDriverState *bs)
|
||||
NFSClient *client = bs->opaque;
|
||||
|
||||
aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
|
||||
false, NULL, NULL, NULL, NULL);
|
||||
false, NULL, NULL, NULL);
|
||||
client->events = 0;
|
||||
}
|
||||
|
||||
@@ -417,7 +416,7 @@ static void nfs_client_close(NFSClient *client)
|
||||
nfs_close(client->context, client->fh);
|
||||
}
|
||||
aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
|
||||
false, NULL, NULL, NULL, NULL);
|
||||
false, NULL, NULL, NULL);
|
||||
nfs_destroy_context(client->context);
|
||||
}
|
||||
memset(client, 0, sizeof(NFSClient));
|
||||
|
97
block/qapi.c
97
block/qapi.c
@@ -237,8 +237,8 @@ void bdrv_query_image_info(BlockDriverState *bs,
|
||||
|
||||
size = bdrv_getlength(bs);
|
||||
if (size < 0) {
|
||||
error_setg_errno(errp, -size, "Can't get image size '%s'",
|
||||
bs->exact_filename);
|
||||
error_setg_errno(errp, -size, "Can't get size of device '%s'",
|
||||
bdrv_get_device_name(bs));
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -357,6 +357,10 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
|
||||
qapi_free_BlockInfo(info);
|
||||
}
|
||||
|
||||
static BlockStats *bdrv_query_stats(BlockBackend *blk,
|
||||
const BlockDriverState *bs,
|
||||
bool query_backing);
|
||||
|
||||
static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
|
||||
{
|
||||
BlockAcctStats *stats = blk_get_stats(blk);
|
||||
@@ -424,18 +428,9 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
|
||||
}
|
||||
}
|
||||
|
||||
static BlockStats *bdrv_query_bds_stats(const BlockDriverState *bs,
|
||||
static void bdrv_query_bds_stats(BlockStats *s, const BlockDriverState *bs,
|
||||
bool query_backing)
|
||||
{
|
||||
BlockStats *s = NULL;
|
||||
|
||||
s = g_malloc0(sizeof(*s));
|
||||
s->stats = g_malloc0(sizeof(*s->stats));
|
||||
|
||||
if (!bs) {
|
||||
return s;
|
||||
}
|
||||
|
||||
if (bdrv_get_node_name(bs)[0]) {
|
||||
s->has_node_name = true;
|
||||
s->node_name = g_strdup(bdrv_get_node_name(bs));
|
||||
@@ -445,12 +440,32 @@ static BlockStats *bdrv_query_bds_stats(const BlockDriverState *bs,
|
||||
|
||||
if (bs->file) {
|
||||
s->has_parent = true;
|
||||
s->parent = bdrv_query_bds_stats(bs->file->bs, query_backing);
|
||||
s->parent = bdrv_query_stats(NULL, bs->file->bs, query_backing);
|
||||
}
|
||||
|
||||
if (query_backing && bs->backing) {
|
||||
s->has_backing = true;
|
||||
s->backing = bdrv_query_bds_stats(bs->backing->bs, query_backing);
|
||||
s->backing = bdrv_query_stats(NULL, bs->backing->bs, query_backing);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static BlockStats *bdrv_query_stats(BlockBackend *blk,
|
||||
const BlockDriverState *bs,
|
||||
bool query_backing)
|
||||
{
|
||||
BlockStats *s;
|
||||
|
||||
s = g_malloc0(sizeof(*s));
|
||||
s->stats = g_malloc0(sizeof(*s->stats));
|
||||
|
||||
if (blk) {
|
||||
s->has_device = true;
|
||||
s->device = g_strdup(blk_name(blk));
|
||||
bdrv_query_blk_stats(s->stats, blk);
|
||||
}
|
||||
if (bs) {
|
||||
bdrv_query_bds_stats(s, bs, query_backing);
|
||||
}
|
||||
|
||||
return s;
|
||||
@@ -479,44 +494,42 @@ BlockInfoList *qmp_query_block(Error **errp)
|
||||
return head;
|
||||
}
|
||||
|
||||
static bool next_query_bds(BlockBackend **blk, BlockDriverState **bs,
|
||||
bool query_nodes)
|
||||
{
|
||||
if (query_nodes) {
|
||||
*bs = bdrv_next_node(*bs);
|
||||
return !!*bs;
|
||||
}
|
||||
|
||||
*blk = blk_next(*blk);
|
||||
*bs = *blk ? blk_bs(*blk) : NULL;
|
||||
|
||||
return !!*blk;
|
||||
}
|
||||
|
||||
BlockStatsList *qmp_query_blockstats(bool has_query_nodes,
|
||||
bool query_nodes,
|
||||
Error **errp)
|
||||
{
|
||||
BlockStatsList *head = NULL, **p_next = &head;
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *bs;
|
||||
BlockBackend *blk = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
|
||||
/* Just to be safe if query_nodes is not always initialized */
|
||||
if (has_query_nodes && query_nodes) {
|
||||
for (bs = bdrv_next_node(NULL); bs; bs = bdrv_next_node(bs)) {
|
||||
BlockStatsList *info = g_malloc0(sizeof(*info));
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
query_nodes = has_query_nodes && query_nodes;
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
info->value = bdrv_query_bds_stats(bs, false);
|
||||
aio_context_release(ctx);
|
||||
while (next_query_bds(&blk, &bs, query_nodes)) {
|
||||
BlockStatsList *info = g_malloc0(sizeof(*info));
|
||||
AioContext *ctx = blk ? blk_get_aio_context(blk)
|
||||
: bdrv_get_aio_context(bs);
|
||||
|
||||
*p_next = info;
|
||||
p_next = &info->next;
|
||||
}
|
||||
} else {
|
||||
for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
|
||||
BlockStatsList *info = g_malloc0(sizeof(*info));
|
||||
AioContext *ctx = blk_get_aio_context(blk);
|
||||
BlockStats *s;
|
||||
aio_context_acquire(ctx);
|
||||
info->value = bdrv_query_stats(blk, bs, !query_nodes);
|
||||
aio_context_release(ctx);
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
s = bdrv_query_bds_stats(blk_bs(blk), true);
|
||||
s->has_device = true;
|
||||
s->device = g_strdup(blk_name(blk));
|
||||
bdrv_query_blk_stats(s->stats, blk);
|
||||
aio_context_release(ctx);
|
||||
|
||||
info->value = s;
|
||||
*p_next = info;
|
||||
p_next = &info->next;
|
||||
}
|
||||
*p_next = info;
|
||||
p_next = &info->next;
|
||||
}
|
||||
|
||||
return head;
|
||||
|
@@ -104,7 +104,6 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
unsigned int len, i, shift;
|
||||
int ret;
|
||||
QCowHeader header;
|
||||
Error *local_err = NULL;
|
||||
|
||||
ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
|
||||
if (ret < 0) {
|
||||
@@ -253,12 +252,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
error_setg(&s->migration_blocker, "The qcow format used by node '%s' "
|
||||
"does not support live migration",
|
||||
bdrv_get_device_or_node_name(bs));
|
||||
ret = migrate_add_blocker(s->migration_blocker, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
error_free(s->migration_blocker);
|
||||
goto fail;
|
||||
}
|
||||
migrate_add_blocker(s->migration_blocker);
|
||||
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
return 0;
|
||||
|
@@ -83,16 +83,6 @@ static Qcow2SetRefcountFunc *const set_refcount_funcs[] = {
|
||||
/*********************************************************/
|
||||
/* refcount handling */
|
||||
|
||||
static void update_max_refcount_table_index(BDRVQcow2State *s)
|
||||
{
|
||||
unsigned i = s->refcount_table_size - 1;
|
||||
while (i > 0 && (s->refcount_table[i] & REFT_OFFSET_MASK) == 0) {
|
||||
i--;
|
||||
}
|
||||
/* Set s->max_refcount_table_index to the index of the last used entry */
|
||||
s->max_refcount_table_index = i;
|
||||
}
|
||||
|
||||
int qcow2_refcount_init(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
@@ -121,7 +111,6 @@ int qcow2_refcount_init(BlockDriverState *bs)
|
||||
}
|
||||
for(i = 0; i < s->refcount_table_size; i++)
|
||||
be64_to_cpus(&s->refcount_table[i]);
|
||||
update_max_refcount_table_index(s);
|
||||
}
|
||||
return 0;
|
||||
fail:
|
||||
@@ -450,10 +439,6 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
s->refcount_table[refcount_table_index] = new_block;
|
||||
/* If there's a hole in s->refcount_table then it can happen
|
||||
* that refcount_table_index < s->max_refcount_table_index */
|
||||
s->max_refcount_table_index =
|
||||
MAX(s->max_refcount_table_index, refcount_table_index);
|
||||
|
||||
/* The new refcount block may be where the caller intended to put its
|
||||
* data, so let it restart the search. */
|
||||
@@ -595,7 +580,6 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
||||
s->refcount_table = new_table;
|
||||
s->refcount_table_size = table_size;
|
||||
s->refcount_table_offset = table_offset;
|
||||
update_max_refcount_table_index(s);
|
||||
|
||||
/* Free old table. */
|
||||
qcow2_free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t),
|
||||
@@ -2187,7 +2171,6 @@ write_refblocks:
|
||||
s->refcount_table = on_disk_reftable;
|
||||
s->refcount_table_offset = reftable_offset;
|
||||
s->refcount_table_size = reftable_size;
|
||||
update_max_refcount_table_index(s);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -2400,11 +2383,7 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
|
||||
}
|
||||
|
||||
if ((chk & QCOW2_OL_REFCOUNT_BLOCK) && s->refcount_table) {
|
||||
unsigned last_entry = s->max_refcount_table_index;
|
||||
assert(last_entry < s->refcount_table_size);
|
||||
assert(last_entry + 1 == s->refcount_table_size ||
|
||||
(s->refcount_table[last_entry + 1] & REFT_OFFSET_MASK) == 0);
|
||||
for (i = 0; i <= last_entry; i++) {
|
||||
for (i = 0; i < s->refcount_table_size; i++) {
|
||||
if ((s->refcount_table[i] & REFT_OFFSET_MASK) &&
|
||||
overlaps_with(s->refcount_table[i] & REFT_OFFSET_MASK,
|
||||
s->cluster_size)) {
|
||||
@@ -2892,7 +2871,6 @@ int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order,
|
||||
/* Now update the rest of the in-memory information */
|
||||
old_reftable = s->refcount_table;
|
||||
s->refcount_table = new_reftable;
|
||||
update_max_refcount_table_index(s);
|
||||
|
||||
s->refcount_bits = 1 << refcount_order;
|
||||
s->refcount_max = UINT64_C(1) << (s->refcount_bits - 1);
|
||||
|
@@ -2743,7 +2743,6 @@ static int make_completely_empty(BlockDriverState *bs)
|
||||
|
||||
s->refcount_table_offset = s->cluster_size;
|
||||
s->refcount_table_size = s->cluster_size / sizeof(uint64_t);
|
||||
s->max_refcount_table_index = 0;
|
||||
|
||||
g_free(s->refcount_table);
|
||||
s->refcount_table = new_reftable;
|
||||
|
@@ -251,7 +251,6 @@ typedef struct BDRVQcow2State {
|
||||
uint64_t *refcount_table;
|
||||
uint64_t refcount_table_offset;
|
||||
uint32_t refcount_table_size;
|
||||
uint32_t max_refcount_table_index; /* Last used entry in refcount_table */
|
||||
uint64_t free_cluster_index;
|
||||
uint64_t free_byte_offset;
|
||||
|
||||
|
410
block/quorum.c
410
block/quorum.c
@@ -97,7 +97,7 @@ typedef struct QuorumAIOCB QuorumAIOCB;
|
||||
* $children_count QuorumChildRequest.
|
||||
*/
|
||||
typedef struct QuorumChildRequest {
|
||||
BlockDriverState *bs;
|
||||
BlockAIOCB *aiocb;
|
||||
QEMUIOVector qiov;
|
||||
uint8_t *buf;
|
||||
int ret;
|
||||
@@ -110,12 +110,11 @@ typedef struct QuorumChildRequest {
|
||||
* used to do operations on each children and track overall progress.
|
||||
*/
|
||||
struct QuorumAIOCB {
|
||||
BlockDriverState *bs;
|
||||
Coroutine *co;
|
||||
BlockAIOCB common;
|
||||
|
||||
/* Request metadata */
|
||||
uint64_t offset;
|
||||
uint64_t bytes;
|
||||
uint64_t sector_num;
|
||||
int nb_sectors;
|
||||
|
||||
QEMUIOVector *qiov; /* calling IOV */
|
||||
|
||||
@@ -134,15 +133,32 @@ struct QuorumAIOCB {
|
||||
int children_read; /* how many children have been read from */
|
||||
};
|
||||
|
||||
typedef struct QuorumCo {
|
||||
QuorumAIOCB *acb;
|
||||
int idx;
|
||||
} QuorumCo;
|
||||
static bool quorum_vote(QuorumAIOCB *acb);
|
||||
|
||||
static void quorum_aio_cancel(BlockAIOCB *blockacb)
|
||||
{
|
||||
QuorumAIOCB *acb = container_of(blockacb, QuorumAIOCB, common);
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
int i;
|
||||
|
||||
/* cancel all callbacks */
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
if (acb->qcrs[i].aiocb) {
|
||||
bdrv_aio_cancel_async(acb->qcrs[i].aiocb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static AIOCBInfo quorum_aiocb_info = {
|
||||
.aiocb_size = sizeof(QuorumAIOCB),
|
||||
.cancel_async = quorum_aio_cancel,
|
||||
};
|
||||
|
||||
static void quorum_aio_finalize(QuorumAIOCB *acb)
|
||||
{
|
||||
acb->common.cb(acb->common.opaque, acb->vote_ret);
|
||||
g_free(acb->qcrs);
|
||||
g_free(acb);
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
static bool quorum_sha256_compare(QuorumVoteValue *a, QuorumVoteValue *b)
|
||||
@@ -155,26 +171,30 @@ static bool quorum_64bits_compare(QuorumVoteValue *a, QuorumVoteValue *b)
|
||||
return a->l == b->l;
|
||||
}
|
||||
|
||||
static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs,
|
||||
static QuorumAIOCB *quorum_aio_get(BDRVQuorumState *s,
|
||||
BlockDriverState *bs,
|
||||
QEMUIOVector *qiov,
|
||||
uint64_t offset,
|
||||
uint64_t bytes)
|
||||
uint64_t sector_num,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
QuorumAIOCB *acb = g_new(QuorumAIOCB, 1);
|
||||
QuorumAIOCB *acb = qemu_aio_get(&quorum_aiocb_info, bs, cb, opaque);
|
||||
int i;
|
||||
|
||||
*acb = (QuorumAIOCB) {
|
||||
.co = qemu_coroutine_self(),
|
||||
.bs = bs,
|
||||
.offset = offset,
|
||||
.bytes = bytes,
|
||||
.qiov = qiov,
|
||||
.votes.compare = quorum_sha256_compare,
|
||||
.votes.vote_list = QLIST_HEAD_INITIALIZER(acb.votes.vote_list),
|
||||
};
|
||||
|
||||
acb->common.bs->opaque = s;
|
||||
acb->sector_num = sector_num;
|
||||
acb->nb_sectors = nb_sectors;
|
||||
acb->qiov = qiov;
|
||||
acb->qcrs = g_new0(QuorumChildRequest, s->num_children);
|
||||
acb->count = 0;
|
||||
acb->success_count = 0;
|
||||
acb->rewrite_count = 0;
|
||||
acb->votes.compare = quorum_sha256_compare;
|
||||
QLIST_INIT(&acb->votes.vote_list);
|
||||
acb->is_read = false;
|
||||
acb->vote_ret = 0;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
acb->qcrs[i].buf = NULL;
|
||||
acb->qcrs[i].ret = 0;
|
||||
@@ -184,37 +204,30 @@ static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs,
|
||||
return acb;
|
||||
}
|
||||
|
||||
static void quorum_report_bad(QuorumOpType type, uint64_t offset,
|
||||
uint64_t bytes, char *node_name, int ret)
|
||||
static void quorum_report_bad(QuorumOpType type, uint64_t sector_num,
|
||||
int nb_sectors, char *node_name, int ret)
|
||||
{
|
||||
const char *msg = NULL;
|
||||
int64_t start_sector = offset / BDRV_SECTOR_SIZE;
|
||||
int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE);
|
||||
|
||||
if (ret < 0) {
|
||||
msg = strerror(-ret);
|
||||
}
|
||||
|
||||
qapi_event_send_quorum_report_bad(type, !!msg, msg, node_name, start_sector,
|
||||
end_sector - start_sector, &error_abort);
|
||||
qapi_event_send_quorum_report_bad(type, !!msg, msg, node_name,
|
||||
sector_num, nb_sectors, &error_abort);
|
||||
}
|
||||
|
||||
static void quorum_report_failure(QuorumAIOCB *acb)
|
||||
{
|
||||
const char *reference = bdrv_get_device_or_node_name(acb->bs);
|
||||
int64_t start_sector = acb->offset / BDRV_SECTOR_SIZE;
|
||||
int64_t end_sector = DIV_ROUND_UP(acb->offset + acb->bytes,
|
||||
BDRV_SECTOR_SIZE);
|
||||
|
||||
qapi_event_send_quorum_failure(reference, start_sector,
|
||||
end_sector - start_sector, &error_abort);
|
||||
const char *reference = bdrv_get_device_or_node_name(acb->common.bs);
|
||||
qapi_event_send_quorum_failure(reference, acb->sector_num,
|
||||
acb->nb_sectors, &error_abort);
|
||||
}
|
||||
|
||||
static int quorum_vote_error(QuorumAIOCB *acb);
|
||||
|
||||
static bool quorum_has_too_much_io_failed(QuorumAIOCB *acb)
|
||||
{
|
||||
BDRVQuorumState *s = acb->bs->opaque;
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
|
||||
if (acb->success_count < s->threshold) {
|
||||
acb->vote_ret = quorum_vote_error(acb);
|
||||
@@ -225,7 +238,22 @@ static bool quorum_has_too_much_io_failed(QuorumAIOCB *acb)
|
||||
return false;
|
||||
}
|
||||
|
||||
static int read_fifo_child(QuorumAIOCB *acb);
|
||||
static void quorum_rewrite_aio_cb(void *opaque, int ret)
|
||||
{
|
||||
QuorumAIOCB *acb = opaque;
|
||||
|
||||
/* one less rewrite to do */
|
||||
acb->rewrite_count--;
|
||||
|
||||
/* wait until all rewrite callbacks have completed */
|
||||
if (acb->rewrite_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
quorum_aio_finalize(acb);
|
||||
}
|
||||
|
||||
static BlockAIOCB *read_fifo_child(QuorumAIOCB *acb);
|
||||
|
||||
static void quorum_copy_qiov(QEMUIOVector *dest, QEMUIOVector *source)
|
||||
{
|
||||
@@ -244,7 +272,70 @@ static void quorum_report_bad_acb(QuorumChildRequest *sacb, int ret)
|
||||
{
|
||||
QuorumAIOCB *acb = sacb->parent;
|
||||
QuorumOpType type = acb->is_read ? QUORUM_OP_TYPE_READ : QUORUM_OP_TYPE_WRITE;
|
||||
quorum_report_bad(type, acb->offset, acb->bytes, sacb->bs->node_name, ret);
|
||||
quorum_report_bad(type, acb->sector_num, acb->nb_sectors,
|
||||
sacb->aiocb->bs->node_name, ret);
|
||||
}
|
||||
|
||||
static void quorum_fifo_aio_cb(void *opaque, int ret)
|
||||
{
|
||||
QuorumChildRequest *sacb = opaque;
|
||||
QuorumAIOCB *acb = sacb->parent;
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
|
||||
assert(acb->is_read && s->read_pattern == QUORUM_READ_PATTERN_FIFO);
|
||||
|
||||
if (ret < 0) {
|
||||
quorum_report_bad_acb(sacb, ret);
|
||||
|
||||
/* We try to read next child in FIFO order if we fail to read */
|
||||
if (acb->children_read < s->num_children) {
|
||||
read_fifo_child(acb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
acb->vote_ret = ret;
|
||||
|
||||
/* FIXME: rewrite failed children if acb->children_read > 1? */
|
||||
quorum_aio_finalize(acb);
|
||||
}
|
||||
|
||||
static void quorum_aio_cb(void *opaque, int ret)
|
||||
{
|
||||
QuorumChildRequest *sacb = opaque;
|
||||
QuorumAIOCB *acb = sacb->parent;
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
bool rewrite = false;
|
||||
int i;
|
||||
|
||||
sacb->ret = ret;
|
||||
if (ret == 0) {
|
||||
acb->success_count++;
|
||||
} else {
|
||||
quorum_report_bad_acb(sacb, ret);
|
||||
}
|
||||
acb->count++;
|
||||
assert(acb->count <= s->num_children);
|
||||
assert(acb->success_count <= s->num_children);
|
||||
if (acb->count < s->num_children) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Do the vote on read */
|
||||
if (acb->is_read) {
|
||||
rewrite = quorum_vote(acb);
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
qemu_vfree(acb->qcrs[i].buf);
|
||||
qemu_iovec_destroy(&acb->qcrs[i].qiov);
|
||||
}
|
||||
} else {
|
||||
quorum_has_too_much_io_failed(acb);
|
||||
}
|
||||
|
||||
/* if no rewrite is done the code will finish right away */
|
||||
if (!rewrite) {
|
||||
quorum_aio_finalize(acb);
|
||||
}
|
||||
}
|
||||
|
||||
static void quorum_report_bad_versions(BDRVQuorumState *s,
|
||||
@@ -259,31 +350,14 @@ static void quorum_report_bad_versions(BDRVQuorumState *s,
|
||||
continue;
|
||||
}
|
||||
QLIST_FOREACH(item, &version->items, next) {
|
||||
quorum_report_bad(QUORUM_OP_TYPE_READ, acb->offset, acb->bytes,
|
||||
quorum_report_bad(QUORUM_OP_TYPE_READ, acb->sector_num,
|
||||
acb->nb_sectors,
|
||||
s->children[item->index]->bs->node_name, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void quorum_rewrite_entry(void *opaque)
|
||||
{
|
||||
QuorumCo *co = opaque;
|
||||
QuorumAIOCB *acb = co->acb;
|
||||
BDRVQuorumState *s = acb->bs->opaque;
|
||||
|
||||
/* Ignore any errors, it's just a correction attempt for already
|
||||
* corrupted data. */
|
||||
bdrv_co_pwritev(s->children[co->idx], acb->offset, acb->bytes,
|
||||
acb->qiov, 0);
|
||||
|
||||
/* Wake up the caller after the last rewrite */
|
||||
acb->rewrite_count--;
|
||||
if (!acb->rewrite_count) {
|
||||
qemu_coroutine_enter_if_inactive(acb->co);
|
||||
}
|
||||
}
|
||||
|
||||
static bool quorum_rewrite_bad_versions(QuorumAIOCB *acb,
|
||||
static bool quorum_rewrite_bad_versions(BDRVQuorumState *s, QuorumAIOCB *acb,
|
||||
QuorumVoteValue *value)
|
||||
{
|
||||
QuorumVoteVersion *version;
|
||||
@@ -302,7 +376,7 @@ static bool quorum_rewrite_bad_versions(QuorumAIOCB *acb,
|
||||
}
|
||||
}
|
||||
|
||||
/* quorum_rewrite_entry will count down this to zero */
|
||||
/* quorum_rewrite_aio_cb will count down this to zero */
|
||||
acb->rewrite_count = count;
|
||||
|
||||
/* now fire the correcting rewrites */
|
||||
@@ -311,14 +385,9 @@ static bool quorum_rewrite_bad_versions(QuorumAIOCB *acb,
|
||||
continue;
|
||||
}
|
||||
QLIST_FOREACH(item, &version->items, next) {
|
||||
Coroutine *co;
|
||||
QuorumCo data = {
|
||||
.acb = acb,
|
||||
.idx = item->index,
|
||||
};
|
||||
|
||||
co = qemu_coroutine_create(quorum_rewrite_entry, &data);
|
||||
qemu_coroutine_enter(co);
|
||||
bdrv_aio_writev(s->children[item->index], acb->sector_num,
|
||||
acb->qiov, acb->nb_sectors, quorum_rewrite_aio_cb,
|
||||
acb);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,8 +507,8 @@ static void GCC_FMT_ATTR(2, 3) quorum_err(QuorumAIOCB *acb,
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "quorum: offset=%" PRIu64 " bytes=%" PRIu64 " ",
|
||||
acb->offset, acb->bytes);
|
||||
fprintf(stderr, "quorum: sector_num=%" PRId64 " nb_sectors=%d ",
|
||||
acb->sector_num, acb->nb_sectors);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(ap);
|
||||
@@ -450,15 +519,16 @@ static bool quorum_compare(QuorumAIOCB *acb,
|
||||
QEMUIOVector *a,
|
||||
QEMUIOVector *b)
|
||||
{
|
||||
BDRVQuorumState *s = acb->bs->opaque;
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
ssize_t offset;
|
||||
|
||||
/* This driver will replace blkverify in this particular case */
|
||||
if (s->is_blkverify) {
|
||||
offset = qemu_iovec_compare(a, b);
|
||||
if (offset != -1) {
|
||||
quorum_err(acb, "contents mismatch at offset %" PRIu64,
|
||||
acb->offset + offset);
|
||||
quorum_err(acb, "contents mismatch in sector %" PRId64,
|
||||
acb->sector_num +
|
||||
(uint64_t)(offset / BDRV_SECTOR_SIZE));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -469,7 +539,7 @@ static bool quorum_compare(QuorumAIOCB *acb,
|
||||
/* Do a vote to get the error code */
|
||||
static int quorum_vote_error(QuorumAIOCB *acb)
|
||||
{
|
||||
BDRVQuorumState *s = acb->bs->opaque;
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
QuorumVoteVersion *winner = NULL;
|
||||
QuorumVotes error_votes;
|
||||
QuorumVoteValue result_value;
|
||||
@@ -498,16 +568,17 @@ static int quorum_vote_error(QuorumAIOCB *acb)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void quorum_vote(QuorumAIOCB *acb)
|
||||
static bool quorum_vote(QuorumAIOCB *acb)
|
||||
{
|
||||
bool quorum = true;
|
||||
bool rewrite = false;
|
||||
int i, j, ret;
|
||||
QuorumVoteValue hash;
|
||||
BDRVQuorumState *s = acb->bs->opaque;
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
QuorumVoteVersion *winner;
|
||||
|
||||
if (quorum_has_too_much_io_failed(acb)) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* get the index of the first successful read */
|
||||
@@ -535,7 +606,7 @@ static void quorum_vote(QuorumAIOCB *acb)
|
||||
/* Every successful read agrees */
|
||||
if (quorum) {
|
||||
quorum_copy_qiov(acb->qiov, &acb->qcrs[i].qiov);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* compute hashes for each successful read, also store indexes */
|
||||
@@ -570,46 +641,19 @@ static void quorum_vote(QuorumAIOCB *acb)
|
||||
|
||||
/* corruption correction is enabled */
|
||||
if (s->rewrite_corrupted) {
|
||||
quorum_rewrite_bad_versions(acb, &winner->value);
|
||||
rewrite = quorum_rewrite_bad_versions(s, acb, &winner->value);
|
||||
}
|
||||
|
||||
free_exit:
|
||||
/* free lists */
|
||||
quorum_free_vote_list(&acb->votes);
|
||||
return rewrite;
|
||||
}
|
||||
|
||||
static void read_quorum_children_entry(void *opaque)
|
||||
static BlockAIOCB *read_quorum_children(QuorumAIOCB *acb)
|
||||
{
|
||||
QuorumCo *co = opaque;
|
||||
QuorumAIOCB *acb = co->acb;
|
||||
BDRVQuorumState *s = acb->bs->opaque;
|
||||
int i = co->idx;
|
||||
QuorumChildRequest *sacb = &acb->qcrs[i];
|
||||
|
||||
sacb->bs = s->children[i]->bs;
|
||||
sacb->ret = bdrv_co_preadv(s->children[i], acb->offset, acb->bytes,
|
||||
&acb->qcrs[i].qiov, 0);
|
||||
|
||||
if (sacb->ret == 0) {
|
||||
acb->success_count++;
|
||||
} else {
|
||||
quorum_report_bad_acb(sacb, sacb->ret);
|
||||
}
|
||||
|
||||
acb->count++;
|
||||
assert(acb->count <= s->num_children);
|
||||
assert(acb->success_count <= s->num_children);
|
||||
|
||||
/* Wake up the caller after the last read */
|
||||
if (acb->count == s->num_children) {
|
||||
qemu_coroutine_enter_if_inactive(acb->co);
|
||||
}
|
||||
}
|
||||
|
||||
static int read_quorum_children(QuorumAIOCB *acb)
|
||||
{
|
||||
BDRVQuorumState *s = acb->bs->opaque;
|
||||
int i, ret;
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
int i;
|
||||
|
||||
acb->children_read = s->num_children;
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
@@ -619,131 +663,65 @@ static int read_quorum_children(QuorumAIOCB *acb)
|
||||
}
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
Coroutine *co;
|
||||
QuorumCo data = {
|
||||
.acb = acb,
|
||||
.idx = i,
|
||||
};
|
||||
|
||||
co = qemu_coroutine_create(read_quorum_children_entry, &data);
|
||||
qemu_coroutine_enter(co);
|
||||
acb->qcrs[i].aiocb = bdrv_aio_readv(s->children[i], acb->sector_num,
|
||||
&acb->qcrs[i].qiov, acb->nb_sectors,
|
||||
quorum_aio_cb, &acb->qcrs[i]);
|
||||
}
|
||||
|
||||
while (acb->count < s->num_children) {
|
||||
qemu_coroutine_yield();
|
||||
}
|
||||
|
||||
/* Do the vote on read */
|
||||
quorum_vote(acb);
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
qemu_vfree(acb->qcrs[i].buf);
|
||||
qemu_iovec_destroy(&acb->qcrs[i].qiov);
|
||||
}
|
||||
|
||||
while (acb->rewrite_count) {
|
||||
qemu_coroutine_yield();
|
||||
}
|
||||
|
||||
ret = acb->vote_ret;
|
||||
|
||||
return ret;
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static int read_fifo_child(QuorumAIOCB *acb)
|
||||
static BlockAIOCB *read_fifo_child(QuorumAIOCB *acb)
|
||||
{
|
||||
BDRVQuorumState *s = acb->bs->opaque;
|
||||
int n, ret;
|
||||
BDRVQuorumState *s = acb->common.bs->opaque;
|
||||
int n = acb->children_read++;
|
||||
|
||||
/* We try to read the next child in FIFO order if we failed to read */
|
||||
do {
|
||||
n = acb->children_read++;
|
||||
acb->qcrs[n].bs = s->children[n]->bs;
|
||||
ret = bdrv_co_preadv(s->children[n], acb->offset, acb->bytes,
|
||||
acb->qiov, 0);
|
||||
if (ret < 0) {
|
||||
quorum_report_bad_acb(&acb->qcrs[n], ret);
|
||||
}
|
||||
} while (ret < 0 && acb->children_read < s->num_children);
|
||||
acb->qcrs[n].aiocb = bdrv_aio_readv(s->children[n], acb->sector_num,
|
||||
acb->qiov, acb->nb_sectors,
|
||||
quorum_fifo_aio_cb, &acb->qcrs[n]);
|
||||
|
||||
/* FIXME: rewrite failed children if acb->children_read > 1? */
|
||||
|
||||
return ret;
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static int quorum_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t bytes, QEMUIOVector *qiov, int flags)
|
||||
static BlockAIOCB *quorum_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes);
|
||||
int ret;
|
||||
|
||||
QuorumAIOCB *acb = quorum_aio_get(s, bs, qiov, sector_num,
|
||||
nb_sectors, cb, opaque);
|
||||
acb->is_read = true;
|
||||
acb->children_read = 0;
|
||||
|
||||
if (s->read_pattern == QUORUM_READ_PATTERN_QUORUM) {
|
||||
ret = read_quorum_children(acb);
|
||||
} else {
|
||||
ret = read_fifo_child(acb);
|
||||
return read_quorum_children(acb);
|
||||
}
|
||||
quorum_aio_finalize(acb);
|
||||
|
||||
return ret;
|
||||
return read_fifo_child(acb);
|
||||
}
|
||||
|
||||
static void write_quorum_entry(void *opaque)
|
||||
{
|
||||
QuorumCo *co = opaque;
|
||||
QuorumAIOCB *acb = co->acb;
|
||||
BDRVQuorumState *s = acb->bs->opaque;
|
||||
int i = co->idx;
|
||||
QuorumChildRequest *sacb = &acb->qcrs[i];
|
||||
|
||||
sacb->bs = s->children[i]->bs;
|
||||
sacb->ret = bdrv_co_pwritev(s->children[i], acb->offset, acb->bytes,
|
||||
acb->qiov, 0);
|
||||
if (sacb->ret == 0) {
|
||||
acb->success_count++;
|
||||
} else {
|
||||
quorum_report_bad_acb(sacb, sacb->ret);
|
||||
}
|
||||
acb->count++;
|
||||
assert(acb->count <= s->num_children);
|
||||
assert(acb->success_count <= s->num_children);
|
||||
|
||||
/* Wake up the caller after the last write */
|
||||
if (acb->count == s->num_children) {
|
||||
qemu_coroutine_enter_if_inactive(acb->co);
|
||||
}
|
||||
}
|
||||
|
||||
static int quorum_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t bytes, QEMUIOVector *qiov, int flags)
|
||||
static BlockAIOCB *quorum_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes);
|
||||
int i, ret;
|
||||
QuorumAIOCB *acb = quorum_aio_get(s, bs, qiov, sector_num, nb_sectors,
|
||||
cb, opaque);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
Coroutine *co;
|
||||
QuorumCo data = {
|
||||
.acb = acb,
|
||||
.idx = i,
|
||||
};
|
||||
|
||||
co = qemu_coroutine_create(write_quorum_entry, &data);
|
||||
qemu_coroutine_enter(co);
|
||||
acb->qcrs[i].aiocb = bdrv_aio_writev(s->children[i], sector_num,
|
||||
qiov, nb_sectors, &quorum_aio_cb,
|
||||
&acb->qcrs[i]);
|
||||
}
|
||||
|
||||
while (acb->count < s->num_children) {
|
||||
qemu_coroutine_yield();
|
||||
}
|
||||
|
||||
quorum_has_too_much_io_failed(acb);
|
||||
|
||||
ret = acb->vote_ret;
|
||||
quorum_aio_finalize(acb);
|
||||
|
||||
return ret;
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
static int64_t quorum_getlength(BlockDriverState *bs)
|
||||
@@ -787,7 +765,7 @@ static coroutine_fn int quorum_co_flush(BlockDriverState *bs)
|
||||
result = bdrv_co_flush(s->children[i]->bs);
|
||||
if (result) {
|
||||
quorum_report_bad(QUORUM_OP_TYPE_FLUSH, 0,
|
||||
bdrv_getlength(s->children[i]->bs),
|
||||
bdrv_nb_sectors(s->children[i]->bs),
|
||||
s->children[i]->bs->node_name, result);
|
||||
result_value.l = result;
|
||||
quorum_count_vote(&error_votes, &result_value, i);
|
||||
@@ -1120,8 +1098,8 @@ static BlockDriver bdrv_quorum = {
|
||||
|
||||
.bdrv_getlength = quorum_getlength,
|
||||
|
||||
.bdrv_co_preadv = quorum_co_preadv,
|
||||
.bdrv_co_pwritev = quorum_co_pwritev,
|
||||
.bdrv_aio_readv = quorum_aio_readv,
|
||||
.bdrv_aio_writev = quorum_aio_writev,
|
||||
|
||||
.bdrv_add_child = quorum_add_child,
|
||||
.bdrv_del_child = quorum_del_child,
|
||||
|
@@ -651,15 +651,12 @@ static void raw_reopen_abort(BDRVReopenState *state)
|
||||
state->opaque = NULL;
|
||||
}
|
||||
|
||||
static int hdev_get_max_transfer_length(BlockDriverState *bs, int fd)
|
||||
static int hdev_get_max_transfer_length(int fd)
|
||||
{
|
||||
#ifdef BLKSECTGET
|
||||
int max_bytes = 0;
|
||||
short max_sectors = 0;
|
||||
if (bs->sg && ioctl(fd, BLKSECTGET, &max_bytes) == 0) {
|
||||
return max_bytes;
|
||||
} else if (!bs->sg && ioctl(fd, BLKSECTGET, &max_sectors) == 0) {
|
||||
return max_sectors << BDRV_SECTOR_BITS;
|
||||
int max_sectors = 0;
|
||||
if (ioctl(fd, BLKSECTGET, &max_sectors) == 0) {
|
||||
return max_sectors;
|
||||
} else {
|
||||
return -errno;
|
||||
}
|
||||
@@ -674,10 +671,10 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
struct stat st;
|
||||
|
||||
if (!fstat(s->fd, &st)) {
|
||||
if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) {
|
||||
int ret = hdev_get_max_transfer_length(bs, s->fd);
|
||||
if (ret > 0 && ret <= BDRV_REQUEST_MAX_BYTES) {
|
||||
bs->bl.max_transfer = pow2floor(ret);
|
||||
if (S_ISBLK(st.st_mode)) {
|
||||
int ret = hdev_get_max_transfer_length(s->fd);
|
||||
if (ret > 0 && ret <= BDRV_REQUEST_MAX_SECTORS) {
|
||||
bs->bl.max_transfer = pow2floor(ret << BDRV_SECTOR_BITS);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
/* BlockDriver implementation for "raw" format driver
|
||||
/* BlockDriver implementation for "raw"
|
||||
*
|
||||
* Copyright (C) 2010-2016 Red Hat, Inc.
|
||||
* Copyright (C) 2010, Blue Swirl <blauwirbel@gmail.com>
|
315
block/sheepdog.c
315
block/sheepdog.c
@@ -306,7 +306,6 @@ static inline size_t count_data_objs(const struct SheepdogInode *inode)
|
||||
} while (0)
|
||||
|
||||
typedef struct SheepdogAIOCB SheepdogAIOCB;
|
||||
typedef struct BDRVSheepdogState BDRVSheepdogState;
|
||||
|
||||
typedef struct AIOReq {
|
||||
SheepdogAIOCB *aiocb;
|
||||
@@ -335,7 +334,7 @@ enum AIOCBState {
|
||||
|| y->max_affect_data_idx < x->min_affect_data_idx))
|
||||
|
||||
struct SheepdogAIOCB {
|
||||
BDRVSheepdogState *s;
|
||||
BlockAIOCB common;
|
||||
|
||||
QEMUIOVector *qiov;
|
||||
|
||||
@@ -346,6 +345,9 @@ struct SheepdogAIOCB {
|
||||
enum AIOCBState aiocb_type;
|
||||
|
||||
Coroutine *coroutine;
|
||||
void (*aio_done_func)(SheepdogAIOCB *);
|
||||
|
||||
bool cancelable;
|
||||
int nr_pending;
|
||||
|
||||
uint32_t min_affect_data_idx;
|
||||
@@ -363,7 +365,7 @@ struct SheepdogAIOCB {
|
||||
QLIST_ENTRY(SheepdogAIOCB) aiocb_siblings;
|
||||
};
|
||||
|
||||
struct BDRVSheepdogState {
|
||||
typedef struct BDRVSheepdogState {
|
||||
BlockDriverState *bs;
|
||||
AioContext *aio_context;
|
||||
|
||||
@@ -390,7 +392,7 @@ struct BDRVSheepdogState {
|
||||
|
||||
CoQueue overlapping_queue;
|
||||
QLIST_HEAD(inflight_aiocb_head, SheepdogAIOCB) inflight_aiocb_head;
|
||||
};
|
||||
} BDRVSheepdogState;
|
||||
|
||||
typedef struct BDRVSheepdogReopenState {
|
||||
int fd;
|
||||
@@ -448,13 +450,14 @@ static const char * sd_strerror(int err)
|
||||
*
|
||||
* 1. In sd_co_rw_vector, we send the I/O requests to the server and
|
||||
* link the requests to the inflight_list in the
|
||||
* BDRVSheepdogState. The function yields while waiting for
|
||||
* BDRVSheepdogState. The function exits without waiting for
|
||||
* receiving the response.
|
||||
*
|
||||
* 2. We receive the response in aio_read_response, the fd handler to
|
||||
* the sheepdog connection. We switch back to sd_co_readv/sd_writev
|
||||
* after all the requests belonging to the AIOCB are finished. If
|
||||
* needed, sd_co_writev will send another requests for the vdi object.
|
||||
* the sheepdog connection. If metadata update is needed, we send
|
||||
* the write request to the vdi object in sd_write_done, the write
|
||||
* completion function. We switch back to sd_co_readv/writev after
|
||||
* all the requests belonging to the AIOCB are finished.
|
||||
*/
|
||||
|
||||
static inline AIOReq *alloc_aio_req(BDRVSheepdogState *s, SheepdogAIOCB *acb,
|
||||
@@ -479,34 +482,94 @@ static inline AIOReq *alloc_aio_req(BDRVSheepdogState *s, SheepdogAIOCB *acb,
|
||||
return aio_req;
|
||||
}
|
||||
|
||||
static void wait_for_overlapping_aiocb(BDRVSheepdogState *s, SheepdogAIOCB *acb)
|
||||
static inline void free_aio_req(BDRVSheepdogState *s, AIOReq *aio_req)
|
||||
{
|
||||
SheepdogAIOCB *cb;
|
||||
SheepdogAIOCB *acb = aio_req->aiocb;
|
||||
|
||||
retry:
|
||||
QLIST_FOREACH(cb, &s->inflight_aiocb_head, aiocb_siblings) {
|
||||
if (AIOCBOverlapping(acb, cb)) {
|
||||
qemu_co_queue_wait(&s->overlapping_queue);
|
||||
goto retry;
|
||||
acb->cancelable = false;
|
||||
QLIST_REMOVE(aio_req, aio_siblings);
|
||||
g_free(aio_req);
|
||||
|
||||
acb->nr_pending--;
|
||||
}
|
||||
|
||||
static void coroutine_fn sd_finish_aiocb(SheepdogAIOCB *acb)
|
||||
{
|
||||
qemu_coroutine_enter(acb->coroutine);
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether the specified acb can be canceled
|
||||
*
|
||||
* We can cancel aio when any request belonging to the acb is:
|
||||
* - Not processed by the sheepdog server.
|
||||
* - Not linked to the inflight queue.
|
||||
*/
|
||||
static bool sd_acb_cancelable(const SheepdogAIOCB *acb)
|
||||
{
|
||||
BDRVSheepdogState *s = acb->common.bs->opaque;
|
||||
AIOReq *aioreq;
|
||||
|
||||
if (!acb->cancelable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QLIST_FOREACH(aioreq, &s->inflight_aio_head, aio_siblings) {
|
||||
if (aioreq->aiocb == acb) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void sd_aio_cancel(BlockAIOCB *blockacb)
|
||||
{
|
||||
SheepdogAIOCB *acb = (SheepdogAIOCB *)blockacb;
|
||||
BDRVSheepdogState *s = acb->common.bs->opaque;
|
||||
AIOReq *aioreq, *next;
|
||||
|
||||
if (sd_acb_cancelable(acb)) {
|
||||
/* Remove outstanding requests from failed queue. */
|
||||
QLIST_FOREACH_SAFE(aioreq, &s->failed_aio_head, aio_siblings,
|
||||
next) {
|
||||
if (aioreq->aiocb == acb) {
|
||||
free_aio_req(s, aioreq);
|
||||
}
|
||||
}
|
||||
|
||||
assert(acb->nr_pending == 0);
|
||||
if (acb->common.cb) {
|
||||
acb->common.cb(acb->common.opaque, -ECANCELED);
|
||||
}
|
||||
sd_finish_aiocb(acb);
|
||||
}
|
||||
}
|
||||
|
||||
static void sd_aio_setup(SheepdogAIOCB *acb, BDRVSheepdogState *s,
|
||||
QEMUIOVector *qiov, int64_t sector_num, int nb_sectors,
|
||||
int type)
|
||||
static const AIOCBInfo sd_aiocb_info = {
|
||||
.aiocb_size = sizeof(SheepdogAIOCB),
|
||||
.cancel_async = sd_aio_cancel,
|
||||
};
|
||||
|
||||
static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||
int64_t sector_num, int nb_sectors)
|
||||
{
|
||||
SheepdogAIOCB *acb;
|
||||
uint32_t object_size;
|
||||
BDRVSheepdogState *s = bs->opaque;
|
||||
|
||||
object_size = (UINT32_C(1) << s->inode.block_size_shift);
|
||||
|
||||
acb->s = s;
|
||||
acb = qemu_aio_get(&sd_aiocb_info, bs, NULL, NULL);
|
||||
|
||||
acb->qiov = qiov;
|
||||
|
||||
acb->sector_num = sector_num;
|
||||
acb->nb_sectors = nb_sectors;
|
||||
|
||||
acb->aio_done_func = NULL;
|
||||
acb->cancelable = true;
|
||||
acb->coroutine = qemu_coroutine_self();
|
||||
acb->ret = 0;
|
||||
acb->nr_pending = 0;
|
||||
@@ -517,14 +580,8 @@ static void sd_aio_setup(SheepdogAIOCB *acb, BDRVSheepdogState *s,
|
||||
|
||||
acb->min_dirty_data_idx = UINT32_MAX;
|
||||
acb->max_dirty_data_idx = 0;
|
||||
acb->aiocb_type = type;
|
||||
|
||||
if (type == AIOCB_FLUSH_CACHE) {
|
||||
return;
|
||||
}
|
||||
|
||||
wait_for_overlapping_aiocb(s, acb);
|
||||
QLIST_INSERT_HEAD(&s->inflight_aiocb_head, acb, aiocb_siblings);
|
||||
return acb;
|
||||
}
|
||||
|
||||
/* Return -EIO in case of error, file descriptor on success */
|
||||
@@ -607,7 +664,7 @@ static coroutine_fn void do_co_req(void *opaque)
|
||||
|
||||
co = qemu_coroutine_self();
|
||||
aio_set_fd_handler(srco->aio_context, sockfd, false,
|
||||
NULL, restart_co_req, NULL, co);
|
||||
NULL, restart_co_req, co);
|
||||
|
||||
ret = send_co_req(sockfd, hdr, data, wlen);
|
||||
if (ret < 0) {
|
||||
@@ -615,7 +672,7 @@ static coroutine_fn void do_co_req(void *opaque)
|
||||
}
|
||||
|
||||
aio_set_fd_handler(srco->aio_context, sockfd, false,
|
||||
restart_co_req, NULL, NULL, co);
|
||||
restart_co_req, NULL, co);
|
||||
|
||||
ret = qemu_co_recv(sockfd, hdr, sizeof(*hdr));
|
||||
if (ret != sizeof(*hdr)) {
|
||||
@@ -641,7 +698,7 @@ out:
|
||||
/* there is at most one request for this sockfd, so it is safe to
|
||||
* set each handler to NULL. */
|
||||
aio_set_fd_handler(srco->aio_context, sockfd, false,
|
||||
NULL, NULL, NULL, NULL);
|
||||
NULL, NULL, NULL);
|
||||
|
||||
srco->ret = ret;
|
||||
srco->finished = true;
|
||||
@@ -703,7 +760,7 @@ static coroutine_fn void reconnect_to_sdog(void *opaque)
|
||||
AIOReq *aio_req, *next;
|
||||
|
||||
aio_set_fd_handler(s->aio_context, s->fd, false, NULL,
|
||||
NULL, NULL, NULL);
|
||||
NULL, NULL);
|
||||
close(s->fd);
|
||||
s->fd = -1;
|
||||
|
||||
@@ -740,6 +797,7 @@ static coroutine_fn void reconnect_to_sdog(void *opaque)
|
||||
while (!QLIST_EMPTY(&s->failed_aio_head)) {
|
||||
aio_req = QLIST_FIRST(&s->failed_aio_head);
|
||||
QLIST_REMOVE(aio_req, aio_siblings);
|
||||
QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
|
||||
resend_aioreq(s, aio_req);
|
||||
}
|
||||
}
|
||||
@@ -782,6 +840,9 @@ static void coroutine_fn aio_read_response(void *opaque)
|
||||
|
||||
switch (acb->aiocb_type) {
|
||||
case AIOCB_WRITE_UDATA:
|
||||
/* this coroutine context is no longer suitable for co_recv
|
||||
* because we may send data to update vdi objects */
|
||||
s->co_recv = NULL;
|
||||
if (!is_data_obj(aio_req->oid)) {
|
||||
break;
|
||||
}
|
||||
@@ -829,12 +890,6 @@ static void coroutine_fn aio_read_response(void *opaque)
|
||||
}
|
||||
}
|
||||
|
||||
/* No more data for this aio_req (reload_inode below uses its own file
|
||||
* descriptor handler which doesn't use co_recv).
|
||||
*/
|
||||
s->co_recv = NULL;
|
||||
|
||||
QLIST_REMOVE(aio_req, aio_siblings);
|
||||
switch (rsp.result) {
|
||||
case SD_RES_SUCCESS:
|
||||
break;
|
||||
@@ -852,26 +907,26 @@ static void coroutine_fn aio_read_response(void *opaque)
|
||||
aio_req->oid = vid_to_vdi_oid(s->inode.vdi_id);
|
||||
}
|
||||
resend_aioreq(s, aio_req);
|
||||
return;
|
||||
goto out;
|
||||
default:
|
||||
acb->ret = -EIO;
|
||||
error_report("%s", sd_strerror(rsp.result));
|
||||
break;
|
||||
}
|
||||
|
||||
g_free(aio_req);
|
||||
|
||||
if (!--acb->nr_pending) {
|
||||
free_aio_req(s, aio_req);
|
||||
if (!acb->nr_pending) {
|
||||
/*
|
||||
* We've finished all requests which belong to the AIOCB, so
|
||||
* we can switch back to sd_co_readv/writev now.
|
||||
*/
|
||||
qemu_coroutine_enter(acb->coroutine);
|
||||
acb->aio_done_func(acb);
|
||||
}
|
||||
|
||||
out:
|
||||
s->co_recv = NULL;
|
||||
return;
|
||||
|
||||
err:
|
||||
s->co_recv = NULL;
|
||||
reconnect_to_sdog(opaque);
|
||||
}
|
||||
|
||||
@@ -909,7 +964,7 @@ static int get_sheep_fd(BDRVSheepdogState *s, Error **errp)
|
||||
}
|
||||
|
||||
aio_set_fd_handler(s->aio_context, fd, false,
|
||||
co_read_response, NULL, NULL, s);
|
||||
co_read_response, NULL, s);
|
||||
return fd;
|
||||
}
|
||||
|
||||
@@ -1121,8 +1176,6 @@ static void coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
|
||||
uint64_t old_oid = aio_req->base_oid;
|
||||
bool create = aio_req->create;
|
||||
|
||||
QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
|
||||
|
||||
if (!nr_copies) {
|
||||
error_report("bug");
|
||||
}
|
||||
@@ -1173,7 +1226,7 @@ static void coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
s->co_send = qemu_coroutine_self();
|
||||
aio_set_fd_handler(s->aio_context, s->fd, false,
|
||||
co_read_response, co_write_request, NULL, s);
|
||||
co_read_response, co_write_request, s);
|
||||
socket_set_cork(s->fd, 1);
|
||||
|
||||
/* send a header */
|
||||
@@ -1192,7 +1245,7 @@ static void coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
|
||||
out:
|
||||
socket_set_cork(s->fd, 0);
|
||||
aio_set_fd_handler(s->aio_context, s->fd, false,
|
||||
co_read_response, NULL, NULL, s);
|
||||
co_read_response, NULL, s);
|
||||
s->co_send = NULL;
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
}
|
||||
@@ -1343,7 +1396,7 @@ static void sd_detach_aio_context(BlockDriverState *bs)
|
||||
BDRVSheepdogState *s = bs->opaque;
|
||||
|
||||
aio_set_fd_handler(s->aio_context, s->fd, false, NULL,
|
||||
NULL, NULL, NULL);
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
static void sd_attach_aio_context(BlockDriverState *bs,
|
||||
@@ -1353,7 +1406,7 @@ static void sd_attach_aio_context(BlockDriverState *bs,
|
||||
|
||||
s->aio_context = new_context;
|
||||
aio_set_fd_handler(new_context, s->fd, false,
|
||||
co_read_response, NULL, NULL, s);
|
||||
co_read_response, NULL, s);
|
||||
}
|
||||
|
||||
/* TODO Convert to fine grained options */
|
||||
@@ -1467,7 +1520,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
return 0;
|
||||
out:
|
||||
aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd,
|
||||
false, NULL, NULL, NULL, NULL);
|
||||
false, NULL, NULL, NULL);
|
||||
if (s->fd >= 0) {
|
||||
closesocket(s->fd);
|
||||
}
|
||||
@@ -1506,7 +1559,7 @@ static void sd_reopen_commit(BDRVReopenState *state)
|
||||
|
||||
if (s->fd) {
|
||||
aio_set_fd_handler(s->aio_context, s->fd, false,
|
||||
NULL, NULL, NULL, NULL);
|
||||
NULL, NULL, NULL);
|
||||
closesocket(s->fd);
|
||||
}
|
||||
|
||||
@@ -1530,7 +1583,7 @@ static void sd_reopen_abort(BDRVReopenState *state)
|
||||
|
||||
if (re_s->fd) {
|
||||
aio_set_fd_handler(s->aio_context, re_s->fd, false,
|
||||
NULL, NULL, NULL, NULL);
|
||||
NULL, NULL, NULL);
|
||||
closesocket(re_s->fd);
|
||||
}
|
||||
|
||||
@@ -1919,7 +1972,7 @@ static void sd_close(BlockDriverState *bs)
|
||||
}
|
||||
|
||||
aio_set_fd_handler(bdrv_get_aio_context(bs), s->fd,
|
||||
false, NULL, NULL, NULL, NULL);
|
||||
false, NULL, NULL, NULL);
|
||||
closesocket(s->fd);
|
||||
g_free(s->host_spec);
|
||||
}
|
||||
@@ -1972,10 +2025,11 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset)
|
||||
/*
|
||||
* This function is called after writing data objects. If we need to
|
||||
* update metadata, this sends a write request to the vdi object.
|
||||
* Otherwise, this switches back to sd_co_readv/writev.
|
||||
*/
|
||||
static void coroutine_fn sd_write_done(SheepdogAIOCB *acb)
|
||||
{
|
||||
BDRVSheepdogState *s = acb->s;
|
||||
BDRVSheepdogState *s = acb->common.bs->opaque;
|
||||
struct iovec iov;
|
||||
AIOReq *aio_req;
|
||||
uint32_t offset, data_len, mn, mx;
|
||||
@@ -1984,7 +2038,6 @@ static void coroutine_fn sd_write_done(SheepdogAIOCB *acb)
|
||||
mx = acb->max_dirty_data_idx;
|
||||
if (mn <= mx) {
|
||||
/* we need to update the vdi object. */
|
||||
++acb->nr_pending;
|
||||
offset = sizeof(s->inode) - sizeof(s->inode.data_vdi_id) +
|
||||
mn * sizeof(s->inode.data_vdi_id[0]);
|
||||
data_len = (mx - mn + 1) * sizeof(s->inode.data_vdi_id[0]);
|
||||
@@ -1996,11 +2049,15 @@ static void coroutine_fn sd_write_done(SheepdogAIOCB *acb)
|
||||
iov.iov_len = sizeof(s->inode);
|
||||
aio_req = alloc_aio_req(s, acb, vid_to_vdi_oid(s->inode.vdi_id),
|
||||
data_len, offset, 0, false, 0, offset);
|
||||
QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
|
||||
add_aio_request(s, aio_req, &iov, 1, AIOCB_WRITE_UDATA);
|
||||
if (--acb->nr_pending) {
|
||||
qemu_coroutine_yield();
|
||||
}
|
||||
|
||||
acb->aio_done_func = sd_finish_aiocb;
|
||||
acb->aiocb_type = AIOCB_WRITE_UDATA;
|
||||
return;
|
||||
}
|
||||
|
||||
sd_finish_aiocb(acb);
|
||||
}
|
||||
|
||||
/* Delete current working VDI on the snapshot chain */
|
||||
@@ -2112,15 +2169,16 @@ out:
|
||||
* Returns 1 when we need to wait a response, 0 when there is no sent
|
||||
* request and -errno in error cases.
|
||||
*/
|
||||
static void coroutine_fn sd_co_rw_vector(SheepdogAIOCB *acb)
|
||||
static int coroutine_fn sd_co_rw_vector(void *p)
|
||||
{
|
||||
SheepdogAIOCB *acb = p;
|
||||
int ret = 0;
|
||||
unsigned long len, done = 0, total = acb->nb_sectors * BDRV_SECTOR_SIZE;
|
||||
unsigned long idx;
|
||||
uint32_t object_size;
|
||||
uint64_t oid;
|
||||
uint64_t offset;
|
||||
BDRVSheepdogState *s = acb->s;
|
||||
BDRVSheepdogState *s = acb->common.bs->opaque;
|
||||
SheepdogInode *inode = &s->inode;
|
||||
AIOReq *aio_req;
|
||||
|
||||
@@ -2132,7 +2190,7 @@ static void coroutine_fn sd_co_rw_vector(SheepdogAIOCB *acb)
|
||||
ret = sd_create_branch(s);
|
||||
if (ret) {
|
||||
acb->ret = -EIO;
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2197,6 +2255,8 @@ static void coroutine_fn sd_co_rw_vector(SheepdogAIOCB *acb)
|
||||
old_oid,
|
||||
acb->aiocb_type == AIOCB_DISCARD_OBJ ?
|
||||
0 : done);
|
||||
QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
|
||||
|
||||
add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov,
|
||||
acb->aiocb_type);
|
||||
done:
|
||||
@@ -2204,25 +2264,31 @@ static void coroutine_fn sd_co_rw_vector(SheepdogAIOCB *acb)
|
||||
idx++;
|
||||
done += len;
|
||||
}
|
||||
if (--acb->nr_pending) {
|
||||
qemu_coroutine_yield();
|
||||
out:
|
||||
if (!--acb->nr_pending) {
|
||||
return acb->ret;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void sd_aio_complete(SheepdogAIOCB *acb)
|
||||
static bool check_overlapping_aiocb(BDRVSheepdogState *s, SheepdogAIOCB *aiocb)
|
||||
{
|
||||
if (acb->aiocb_type == AIOCB_FLUSH_CACHE) {
|
||||
return;
|
||||
SheepdogAIOCB *cb;
|
||||
|
||||
QLIST_FOREACH(cb, &s->inflight_aiocb_head, aiocb_siblings) {
|
||||
if (AIOCBOverlapping(aiocb, cb)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
QLIST_REMOVE(acb, aiocb_siblings);
|
||||
qemu_co_queue_restart_all(&acb->s->overlapping_queue);
|
||||
QLIST_INSERT_HEAD(&s->inflight_aiocb_head, aiocb, aiocb_siblings);
|
||||
return false;
|
||||
}
|
||||
|
||||
static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
SheepdogAIOCB acb;
|
||||
SheepdogAIOCB *acb;
|
||||
int ret;
|
||||
int64_t offset = (sector_num + nb_sectors) * BDRV_SECTOR_SIZE;
|
||||
BDRVSheepdogState *s = bs->opaque;
|
||||
@@ -2234,50 +2300,85 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
}
|
||||
}
|
||||
|
||||
sd_aio_setup(&acb, s, qiov, sector_num, nb_sectors, AIOCB_WRITE_UDATA);
|
||||
sd_co_rw_vector(&acb);
|
||||
sd_write_done(&acb);
|
||||
sd_aio_complete(&acb);
|
||||
acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors);
|
||||
acb->aio_done_func = sd_write_done;
|
||||
acb->aiocb_type = AIOCB_WRITE_UDATA;
|
||||
|
||||
return acb.ret;
|
||||
retry:
|
||||
if (check_overlapping_aiocb(s, acb)) {
|
||||
qemu_co_queue_wait(&s->overlapping_queue);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
ret = sd_co_rw_vector(acb);
|
||||
if (ret <= 0) {
|
||||
QLIST_REMOVE(acb, aiocb_siblings);
|
||||
qemu_co_queue_restart_all(&s->overlapping_queue);
|
||||
qemu_aio_unref(acb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
qemu_coroutine_yield();
|
||||
|
||||
QLIST_REMOVE(acb, aiocb_siblings);
|
||||
qemu_co_queue_restart_all(&s->overlapping_queue);
|
||||
|
||||
return acb->ret;
|
||||
}
|
||||
|
||||
static coroutine_fn int sd_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
{
|
||||
SheepdogAIOCB acb;
|
||||
SheepdogAIOCB *acb;
|
||||
int ret;
|
||||
BDRVSheepdogState *s = bs->opaque;
|
||||
|
||||
sd_aio_setup(&acb, s, qiov, sector_num, nb_sectors, AIOCB_READ_UDATA);
|
||||
sd_co_rw_vector(&acb);
|
||||
sd_aio_complete(&acb);
|
||||
acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors);
|
||||
acb->aiocb_type = AIOCB_READ_UDATA;
|
||||
acb->aio_done_func = sd_finish_aiocb;
|
||||
|
||||
return acb.ret;
|
||||
retry:
|
||||
if (check_overlapping_aiocb(s, acb)) {
|
||||
qemu_co_queue_wait(&s->overlapping_queue);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
ret = sd_co_rw_vector(acb);
|
||||
if (ret <= 0) {
|
||||
QLIST_REMOVE(acb, aiocb_siblings);
|
||||
qemu_co_queue_restart_all(&s->overlapping_queue);
|
||||
qemu_aio_unref(acb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
qemu_coroutine_yield();
|
||||
|
||||
QLIST_REMOVE(acb, aiocb_siblings);
|
||||
qemu_co_queue_restart_all(&s->overlapping_queue);
|
||||
return acb->ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn sd_co_flush_to_disk(BlockDriverState *bs)
|
||||
{
|
||||
BDRVSheepdogState *s = bs->opaque;
|
||||
SheepdogAIOCB acb;
|
||||
SheepdogAIOCB *acb;
|
||||
AIOReq *aio_req;
|
||||
|
||||
if (s->cache_flags != SD_FLAG_CMD_CACHE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sd_aio_setup(&acb, s, NULL, 0, 0, AIOCB_FLUSH_CACHE);
|
||||
acb = sd_aio_setup(bs, NULL, 0, 0);
|
||||
acb->aiocb_type = AIOCB_FLUSH_CACHE;
|
||||
acb->aio_done_func = sd_finish_aiocb;
|
||||
|
||||
acb.nr_pending++;
|
||||
aio_req = alloc_aio_req(s, &acb, vid_to_vdi_oid(s->inode.vdi_id),
|
||||
aio_req = alloc_aio_req(s, acb, vid_to_vdi_oid(s->inode.vdi_id),
|
||||
0, 0, 0, false, 0, 0);
|
||||
add_aio_request(s, aio_req, NULL, 0, acb.aiocb_type);
|
||||
QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings);
|
||||
add_aio_request(s, aio_req, NULL, 0, acb->aiocb_type);
|
||||
|
||||
if (--acb.nr_pending) {
|
||||
qemu_coroutine_yield();
|
||||
}
|
||||
|
||||
sd_aio_complete(&acb);
|
||||
return acb.ret;
|
||||
qemu_coroutine_yield();
|
||||
return acb->ret;
|
||||
}
|
||||
|
||||
static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
@@ -2711,8 +2812,9 @@ static int sd_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||
static coroutine_fn int sd_co_pdiscard(BlockDriverState *bs, int64_t offset,
|
||||
int count)
|
||||
{
|
||||
SheepdogAIOCB acb;
|
||||
SheepdogAIOCB *acb;
|
||||
BDRVSheepdogState *s = bs->opaque;
|
||||
int ret;
|
||||
QEMUIOVector discard_iov;
|
||||
struct iovec iov;
|
||||
uint32_t zero = 0;
|
||||
@@ -2730,12 +2832,31 @@ static coroutine_fn int sd_co_pdiscard(BlockDriverState *bs, int64_t offset,
|
||||
if (!QEMU_IS_ALIGNED(offset | count, BDRV_SECTOR_SIZE)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
sd_aio_setup(&acb, s, &discard_iov, offset >> BDRV_SECTOR_BITS,
|
||||
count >> BDRV_SECTOR_BITS, AIOCB_DISCARD_OBJ);
|
||||
sd_co_rw_vector(&acb);
|
||||
sd_aio_complete(&acb);
|
||||
acb = sd_aio_setup(bs, &discard_iov, offset >> BDRV_SECTOR_BITS,
|
||||
count >> BDRV_SECTOR_BITS);
|
||||
acb->aiocb_type = AIOCB_DISCARD_OBJ;
|
||||
acb->aio_done_func = sd_finish_aiocb;
|
||||
|
||||
return acb.ret;
|
||||
retry:
|
||||
if (check_overlapping_aiocb(s, acb)) {
|
||||
qemu_co_queue_wait(&s->overlapping_queue);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
ret = sd_co_rw_vector(acb);
|
||||
if (ret <= 0) {
|
||||
QLIST_REMOVE(acb, aiocb_siblings);
|
||||
qemu_co_queue_restart_all(&s->overlapping_queue);
|
||||
qemu_aio_unref(acb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
qemu_coroutine_yield();
|
||||
|
||||
QLIST_REMOVE(acb, aiocb_siblings);
|
||||
qemu_co_queue_restart_all(&s->overlapping_queue);
|
||||
|
||||
return acb->ret;
|
||||
}
|
||||
|
||||
static coroutine_fn int64_t
|
||||
|
@@ -911,7 +911,7 @@ static coroutine_fn void set_fd_handler(BDRVSSHState *s, BlockDriverState *bs)
|
||||
rd_handler, wr_handler);
|
||||
|
||||
aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock,
|
||||
false, rd_handler, wr_handler, NULL, co);
|
||||
false, rd_handler, wr_handler, co);
|
||||
}
|
||||
|
||||
static coroutine_fn void clear_fd_handler(BDRVSSHState *s,
|
||||
@@ -919,7 +919,7 @@ static coroutine_fn void clear_fd_handler(BDRVSSHState *s,
|
||||
{
|
||||
DPRINTF("s->sock=%d", s->sock);
|
||||
aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock,
|
||||
false, NULL, NULL, NULL, NULL);
|
||||
false, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
/* A non-blocking call returned EAGAIN, so yield, ensuring the
|
||||
|
@@ -35,6 +35,8 @@ mirror_one_iteration(void *s, int64_t sector_num, int nb_sectors) "s %p sector_n
|
||||
mirror_iteration_done(void *s, int64_t sector_num, int nb_sectors, int ret) "s %p sector_num %"PRId64" nb_sectors %d ret %d"
|
||||
mirror_yield(void *s, int64_t cnt, int buf_free_count, int in_flight) "s %p dirty count %"PRId64" free buffers %d in_flight %d"
|
||||
mirror_yield_in_flight(void *s, int64_t sector_num, int in_flight) "s %p sector_num %"PRId64" in_flight %d"
|
||||
mirror_yield_buf_busy(void *s, int nb_chunks, int in_flight) "s %p requested chunks %d in_flight %d"
|
||||
mirror_break_buf_busy(void *s, int nb_chunks, int in_flight) "s %p requested chunks %d in_flight %d"
|
||||
|
||||
# block/backup.c
|
||||
backup_do_cow_enter(void *job, int64_t start, int64_t sector_num, int nb_sectors) "job %p start %"PRId64" sector_num %"PRId64" nb_sectors %d"
|
||||
@@ -51,8 +53,8 @@ qmp_block_job_resume(void *job) "job %p"
|
||||
qmp_block_job_complete(void *job) "job %p"
|
||||
qmp_block_stream(void *bs, void *job) "bs %p job %p"
|
||||
|
||||
# block/file-win32.c
|
||||
# block/file-posix.c
|
||||
# block/raw-win32.c
|
||||
# block/raw-posix.c
|
||||
paio_submit_co(int64_t offset, int count, int type) "offset %"PRId64" count %d type %d"
|
||||
paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d"
|
||||
|
||||
|
@@ -361,7 +361,6 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
VdiHeader header;
|
||||
size_t bmap_size;
|
||||
int ret;
|
||||
Error *local_err = NULL;
|
||||
|
||||
logout("\n");
|
||||
|
||||
@@ -472,12 +471,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
error_setg(&s->migration_blocker, "The vdi format used by node '%s' "
|
||||
"does not support live migration",
|
||||
bdrv_get_device_or_node_name(bs));
|
||||
ret = migrate_add_blocker(s->migration_blocker, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
error_free(s->migration_blocker);
|
||||
goto fail_free_bmap;
|
||||
}
|
||||
migrate_add_blocker(s->migration_blocker);
|
||||
|
||||
qemu_co_mutex_init(&s->write_lock);
|
||||
|
||||
|
17
block/vhdx.c
17
block/vhdx.c
@@ -991,17 +991,6 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable migration when VHDX images are used */
|
||||
error_setg(&s->migration_blocker, "The vhdx format used by node '%s' "
|
||||
"does not support live migration",
|
||||
bdrv_get_device_or_node_name(bs));
|
||||
ret = migrate_add_blocker(s->migration_blocker, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
error_free(s->migration_blocker);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (flags & BDRV_O_RDWR) {
|
||||
ret = vhdx_update_headers(bs, s, false, NULL);
|
||||
if (ret < 0) {
|
||||
@@ -1011,6 +1000,12 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
/* TODO: differencing files */
|
||||
|
||||
/* Disable migration when VHDX images are used */
|
||||
error_setg(&s->migration_blocker, "The vhdx format used by node '%s' "
|
||||
"does not support live migration",
|
||||
bdrv_get_device_or_node_name(bs));
|
||||
migrate_add_blocker(s->migration_blocker);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
vhdx_close(bs);
|
||||
|
@@ -941,7 +941,6 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
int ret;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
uint32_t magic;
|
||||
Error *local_err = NULL;
|
||||
|
||||
buf = vmdk_read_desc(bs->file, 0, errp);
|
||||
if (!buf) {
|
||||
@@ -977,13 +976,7 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
error_setg(&s->migration_blocker, "The vmdk format used by node '%s' "
|
||||
"does not support live migration",
|
||||
bdrv_get_device_or_node_name(bs));
|
||||
ret = migrate_add_blocker(s->migration_blocker, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
error_free(s->migration_blocker);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
migrate_add_blocker(s->migration_blocker);
|
||||
g_free(buf);
|
||||
return 0;
|
||||
|
||||
|
11
block/vpc.c
11
block/vpc.c
@@ -422,18 +422,13 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
#endif
|
||||
}
|
||||
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
|
||||
/* Disable migration when VHD images are used */
|
||||
error_setg(&s->migration_blocker, "The vpc format used by node '%s' "
|
||||
"does not support live migration",
|
||||
bdrv_get_device_or_node_name(bs));
|
||||
ret = migrate_add_blocker(s->migration_blocker, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
error_free(s->migration_blocker);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
migrate_add_blocker(s->migration_blocker);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@@ -1185,26 +1185,22 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count;
|
||||
|
||||
if (s->first_sectors_number == 0x40) {
|
||||
init_mbr(s, cyls, heads, secs);
|
||||
}
|
||||
|
||||
// assert(is_consistent(s));
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
|
||||
/* Disable migration when vvfat is used rw */
|
||||
if (s->qcow) {
|
||||
error_setg(&s->migration_blocker,
|
||||
"The vvfat (rw) format used by node '%s' "
|
||||
"does not support live migration",
|
||||
bdrv_get_device_or_node_name(bs));
|
||||
ret = migrate_add_blocker(s->migration_blocker, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
error_free(s->migration_blocker);
|
||||
goto fail;
|
||||
}
|
||||
migrate_add_blocker(s->migration_blocker);
|
||||
}
|
||||
|
||||
if (s->first_sectors_number == 0x40) {
|
||||
init_mbr(s, cyls, heads, secs);
|
||||
}
|
||||
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
qemu_opts_del(opts);
|
||||
|
@@ -175,7 +175,7 @@ int win32_aio_attach(QEMUWin32AIOState *aio, HANDLE hfile)
|
||||
void win32_aio_detach_aio_context(QEMUWin32AIOState *aio,
|
||||
AioContext *old_context)
|
||||
{
|
||||
aio_set_event_notifier(old_context, &aio->e, false, NULL, NULL);
|
||||
aio_set_event_notifier(old_context, &aio->e, false, NULL);
|
||||
aio->is_aio_context_attached = false;
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ void win32_aio_attach_aio_context(QEMUWin32AIOState *aio,
|
||||
{
|
||||
aio->is_aio_context_attached = true;
|
||||
aio_set_event_notifier(new_context, &aio->e, false,
|
||||
win32_aio_completion_cb, NULL);
|
||||
win32_aio_completion_cb);
|
||||
}
|
||||
|
||||
QEMUWin32AIOState *win32_aio_init(void)
|
||||
|
@@ -16,6 +16,7 @@
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "trace.h"
|
||||
#include "block/nbd.h"
|
||||
#include "io/channel-socket.h"
|
||||
|
||||
|
@@ -48,7 +48,7 @@
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "block/trace.h"
|
||||
#include "trace.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/help_option.h"
|
||||
|
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "trace.h"
|
||||
#include "block/block.h"
|
||||
#include "block/blockjob_int.h"
|
||||
#include "block/block_int.h"
|
||||
|
@@ -1,17 +0,0 @@
|
||||
chardev-obj-y += char.o
|
||||
chardev-obj-$(CONFIG_WIN32) += char-console.o
|
||||
chardev-obj-$(CONFIG_POSIX) += char-fd.o
|
||||
chardev-obj-y += char-file.o
|
||||
chardev-obj-y += char-io.o
|
||||
chardev-obj-y += char-mux.o
|
||||
chardev-obj-y += char-null.o
|
||||
chardev-obj-$(CONFIG_POSIX) += char-parallel.o
|
||||
chardev-obj-y += char-pipe.o
|
||||
chardev-obj-$(CONFIG_POSIX) += char-pty.o
|
||||
chardev-obj-y += char-ringbuf.o
|
||||
chardev-obj-y += char-serial.o
|
||||
chardev-obj-y += char-socket.o
|
||||
chardev-obj-y += char-stdio.o
|
||||
chardev-obj-y += char-udp.o
|
||||
chardev-obj-$(CONFIG_WIN32) += char-win.o
|
||||
chardev-obj-$(CONFIG_WIN32) += char-win-stdio.o
|
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "char-win.h"
|
||||
|
||||
static void qemu_chr_open_win_con(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
qemu_chr_open_win_file(chr, GetStdHandle(STD_OUTPUT_HANDLE));
|
||||
}
|
||||
|
||||
static void char_console_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->open = qemu_chr_open_win_con;
|
||||
}
|
||||
|
||||
static const TypeInfo char_console_type_info = {
|
||||
.name = TYPE_CHARDEV_CONSOLE,
|
||||
.parent = TYPE_CHARDEV_WIN,
|
||||
.class_init = char_console_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_console_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "io/channel-file.h"
|
||||
|
||||
#include "char-fd.h"
|
||||
#include "char-io.h"
|
||||
|
||||
/* Called with chr_write_lock held. */
|
||||
static int fd_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
||||
{
|
||||
FDChardev *s = FD_CHARDEV(chr);
|
||||
|
||||
return io_channel_send(s->ioc_out, buf, len);
|
||||
}
|
||||
|
||||
static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
FDChardev *s = FD_CHARDEV(opaque);
|
||||
int len;
|
||||
uint8_t buf[CHR_READ_BUF_LEN];
|
||||
ssize_t ret;
|
||||
|
||||
len = sizeof(buf);
|
||||
if (len > s->max_size) {
|
||||
len = s->max_size;
|
||||
}
|
||||
if (len == 0) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ret = qio_channel_read(
|
||||
chan, (gchar *)buf, len, NULL);
|
||||
if (ret == 0) {
|
||||
remove_fd_in_watch(chr);
|
||||
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
||||
return FALSE;
|
||||
}
|
||||
if (ret > 0) {
|
||||
qemu_chr_be_write(chr, buf, ret);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int fd_chr_read_poll(void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
FDChardev *s = FD_CHARDEV(opaque);
|
||||
|
||||
s->max_size = qemu_chr_be_can_write(chr);
|
||||
return s->max_size;
|
||||
}
|
||||
|
||||
static GSource *fd_chr_add_watch(Chardev *chr, GIOCondition cond)
|
||||
{
|
||||
FDChardev *s = FD_CHARDEV(chr);
|
||||
return qio_channel_create_watch(s->ioc_out, cond);
|
||||
}
|
||||
|
||||
static void fd_chr_update_read_handler(Chardev *chr,
|
||||
GMainContext *context)
|
||||
{
|
||||
FDChardev *s = FD_CHARDEV(chr);
|
||||
|
||||
remove_fd_in_watch(chr);
|
||||
if (s->ioc_in) {
|
||||
chr->fd_in_tag = io_add_watch_poll(chr, s->ioc_in,
|
||||
fd_chr_read_poll,
|
||||
fd_chr_read, chr,
|
||||
context);
|
||||
}
|
||||
}
|
||||
|
||||
static void char_fd_finalize(Object *obj)
|
||||
{
|
||||
Chardev *chr = CHARDEV(obj);
|
||||
FDChardev *s = FD_CHARDEV(obj);
|
||||
|
||||
remove_fd_in_watch(chr);
|
||||
if (s->ioc_in) {
|
||||
object_unref(OBJECT(s->ioc_in));
|
||||
}
|
||||
if (s->ioc_out) {
|
||||
object_unref(OBJECT(s->ioc_out));
|
||||
}
|
||||
|
||||
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
||||
}
|
||||
|
||||
int qmp_chardev_open_file_source(char *src, int flags, Error **errp)
|
||||
{
|
||||
int fd = -1;
|
||||
|
||||
TFR(fd = qemu_open(src, flags, 0666));
|
||||
if (fd == -1) {
|
||||
error_setg_file_open(errp, errno, src);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* open a character device to a unix fd */
|
||||
void qemu_chr_open_fd(Chardev *chr,
|
||||
int fd_in, int fd_out)
|
||||
{
|
||||
FDChardev *s = FD_CHARDEV(chr);
|
||||
char *name;
|
||||
|
||||
s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in));
|
||||
name = g_strdup_printf("chardev-file-in-%s", chr->label);
|
||||
qio_channel_set_name(QIO_CHANNEL(s->ioc_in), name);
|
||||
g_free(name);
|
||||
s->ioc_out = QIO_CHANNEL(qio_channel_file_new_fd(fd_out));
|
||||
name = g_strdup_printf("chardev-file-out-%s", chr->label);
|
||||
qio_channel_set_name(QIO_CHANNEL(s->ioc_out), name);
|
||||
g_free(name);
|
||||
qemu_set_nonblock(fd_out);
|
||||
s->chr = chr;
|
||||
}
|
||||
|
||||
static void char_fd_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->chr_add_watch = fd_chr_add_watch;
|
||||
cc->chr_write = fd_chr_write;
|
||||
cc->chr_update_read_handler = fd_chr_update_read_handler;
|
||||
}
|
||||
|
||||
static const TypeInfo char_fd_type_info = {
|
||||
.name = TYPE_CHARDEV_FD,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.instance_size = sizeof(FDChardev),
|
||||
.instance_finalize = char_fd_finalize,
|
||||
.class_init = char_fd_class_init,
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_fd_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef CHAR_FD_H
|
||||
#define CHAR_FD_H
|
||||
|
||||
#include "io/channel.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
typedef struct FDChardev {
|
||||
Chardev parent;
|
||||
Chardev *chr;
|
||||
QIOChannel *ioc_in, *ioc_out;
|
||||
int max_size;
|
||||
} FDChardev;
|
||||
|
||||
#define TYPE_CHARDEV_FD "chardev-fd"
|
||||
|
||||
#define FD_CHARDEV(obj) OBJECT_CHECK(FDChardev, (obj), TYPE_CHARDEV_FD)
|
||||
|
||||
void qemu_chr_open_fd(Chardev *chr, int fd_in, int fd_out);
|
||||
int qmp_chardev_open_file_source(char *src, int flags, Error **errp);
|
||||
|
||||
#endif /* CHAR_FD_H */
|
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "char-win.h"
|
||||
#else
|
||||
#include "char-fd.h"
|
||||
#endif
|
||||
|
||||
static void qmp_chardev_open_file(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevFile *file = backend->u.file.data;
|
||||
#ifdef _WIN32
|
||||
HANDLE out;
|
||||
DWORD accessmode;
|
||||
DWORD flags;
|
||||
|
||||
if (file->has_in) {
|
||||
error_setg(errp, "input file not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
if (file->has_append && file->append) {
|
||||
/* Append to file if it already exists. */
|
||||
accessmode = FILE_GENERIC_WRITE & ~FILE_WRITE_DATA;
|
||||
flags = OPEN_ALWAYS;
|
||||
} else {
|
||||
/* Truncate file if it already exists. */
|
||||
accessmode = GENERIC_WRITE;
|
||||
flags = CREATE_ALWAYS;
|
||||
}
|
||||
|
||||
out = CreateFile(file->out, accessmode, FILE_SHARE_READ, NULL, flags,
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (out == INVALID_HANDLE_VALUE) {
|
||||
error_setg(errp, "open %s failed", file->out);
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_chr_open_win_file(chr, out);
|
||||
#else
|
||||
int flags, in = -1, out;
|
||||
|
||||
flags = O_WRONLY | O_CREAT | O_BINARY;
|
||||
if (file->has_append && file->append) {
|
||||
flags |= O_APPEND;
|
||||
} else {
|
||||
flags |= O_TRUNC;
|
||||
}
|
||||
|
||||
out = qmp_chardev_open_file_source(file->out, flags, errp);
|
||||
if (out < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (file->has_in) {
|
||||
flags = O_RDONLY;
|
||||
in = qmp_chardev_open_file_source(file->in, flags, errp);
|
||||
if (in < 0) {
|
||||
qemu_close(out);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
qemu_chr_open_fd(chr, in, out);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *backend,
|
||||
Error **errp)
|
||||
{
|
||||
const char *path = qemu_opt_get(opts, "path");
|
||||
ChardevFile *file;
|
||||
|
||||
backend->type = CHARDEV_BACKEND_KIND_FILE;
|
||||
if (path == NULL) {
|
||||
error_setg(errp, "chardev: file: no filename given");
|
||||
return;
|
||||
}
|
||||
file = backend->u.file.data = g_new0(ChardevFile, 1);
|
||||
qemu_chr_parse_common(opts, qapi_ChardevFile_base(file));
|
||||
file->out = g_strdup(path);
|
||||
|
||||
file->has_append = true;
|
||||
file->append = qemu_opt_get_bool(opts, "append", false);
|
||||
}
|
||||
|
||||
static void char_file_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_file_out;
|
||||
cc->open = qmp_chardev_open_file;
|
||||
}
|
||||
|
||||
static const TypeInfo char_file_type_info = {
|
||||
.name = TYPE_CHARDEV_FILE,
|
||||
#ifdef _WIN32
|
||||
.parent = TYPE_CHARDEV_WIN,
|
||||
#else
|
||||
.parent = TYPE_CHARDEV_FD,
|
||||
#endif
|
||||
.class_init = char_file_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_file_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
@@ -1,192 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "char-io.h"
|
||||
|
||||
typedef struct IOWatchPoll {
|
||||
GSource parent;
|
||||
|
||||
QIOChannel *ioc;
|
||||
GSource *src;
|
||||
|
||||
IOCanReadHandler *fd_can_read;
|
||||
GSourceFunc fd_read;
|
||||
void *opaque;
|
||||
GMainContext *context;
|
||||
} IOWatchPoll;
|
||||
|
||||
static IOWatchPoll *io_watch_poll_from_source(GSource *source)
|
||||
{
|
||||
return container_of(source, IOWatchPoll, parent);
|
||||
}
|
||||
|
||||
static gboolean io_watch_poll_prepare(GSource *source,
|
||||
gint *timeout)
|
||||
{
|
||||
IOWatchPoll *iwp = io_watch_poll_from_source(source);
|
||||
bool now_active = iwp->fd_can_read(iwp->opaque) > 0;
|
||||
bool was_active = iwp->src != NULL;
|
||||
if (was_active == now_active) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (now_active) {
|
||||
iwp->src = qio_channel_create_watch(
|
||||
iwp->ioc, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL);
|
||||
g_source_set_callback(iwp->src, iwp->fd_read, iwp->opaque, NULL);
|
||||
g_source_attach(iwp->src, iwp->context);
|
||||
} else {
|
||||
g_source_destroy(iwp->src);
|
||||
g_source_unref(iwp->src);
|
||||
iwp->src = NULL;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean io_watch_poll_check(GSource *source)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean io_watch_poll_dispatch(GSource *source, GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
static void io_watch_poll_finalize(GSource *source)
|
||||
{
|
||||
/* Due to a glib bug, removing the last reference to a source
|
||||
* inside a finalize callback causes recursive locking (and a
|
||||
* deadlock). This is not a problem inside other callbacks,
|
||||
* including dispatch callbacks, so we call io_remove_watch_poll
|
||||
* to remove this source. At this point, iwp->src must
|
||||
* be NULL, or we would leak it.
|
||||
*
|
||||
* This would be solved much more elegantly by child sources,
|
||||
* but we support older glib versions that do not have them.
|
||||
*/
|
||||
IOWatchPoll *iwp = io_watch_poll_from_source(source);
|
||||
assert(iwp->src == NULL);
|
||||
}
|
||||
|
||||
static GSourceFuncs io_watch_poll_funcs = {
|
||||
.prepare = io_watch_poll_prepare,
|
||||
.check = io_watch_poll_check,
|
||||
.dispatch = io_watch_poll_dispatch,
|
||||
.finalize = io_watch_poll_finalize,
|
||||
};
|
||||
|
||||
guint io_add_watch_poll(Chardev *chr,
|
||||
QIOChannel *ioc,
|
||||
IOCanReadHandler *fd_can_read,
|
||||
QIOChannelFunc fd_read,
|
||||
gpointer user_data,
|
||||
GMainContext *context)
|
||||
{
|
||||
IOWatchPoll *iwp;
|
||||
int tag;
|
||||
char *name;
|
||||
|
||||
iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs,
|
||||
sizeof(IOWatchPoll));
|
||||
iwp->fd_can_read = fd_can_read;
|
||||
iwp->opaque = user_data;
|
||||
iwp->ioc = ioc;
|
||||
iwp->fd_read = (GSourceFunc) fd_read;
|
||||
iwp->src = NULL;
|
||||
iwp->context = context;
|
||||
|
||||
name = g_strdup_printf("chardev-iowatch-%s", chr->label);
|
||||
g_source_set_name((GSource *)iwp, name);
|
||||
g_free(name);
|
||||
|
||||
tag = g_source_attach(&iwp->parent, context);
|
||||
g_source_unref(&iwp->parent);
|
||||
return tag;
|
||||
}
|
||||
|
||||
static void io_remove_watch_poll(guint tag)
|
||||
{
|
||||
GSource *source;
|
||||
IOWatchPoll *iwp;
|
||||
|
||||
g_return_if_fail(tag > 0);
|
||||
|
||||
source = g_main_context_find_source_by_id(NULL, tag);
|
||||
g_return_if_fail(source != NULL);
|
||||
|
||||
iwp = io_watch_poll_from_source(source);
|
||||
if (iwp->src) {
|
||||
g_source_destroy(iwp->src);
|
||||
g_source_unref(iwp->src);
|
||||
iwp->src = NULL;
|
||||
}
|
||||
g_source_destroy(&iwp->parent);
|
||||
}
|
||||
|
||||
void remove_fd_in_watch(Chardev *chr)
|
||||
{
|
||||
if (chr->fd_in_tag) {
|
||||
io_remove_watch_poll(chr->fd_in_tag);
|
||||
chr->fd_in_tag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int io_channel_send_full(QIOChannel *ioc,
|
||||
const void *buf, size_t len,
|
||||
int *fds, size_t nfds)
|
||||
{
|
||||
size_t offset = 0;
|
||||
|
||||
while (offset < len) {
|
||||
ssize_t ret = 0;
|
||||
struct iovec iov = { .iov_base = (char *)buf + offset,
|
||||
.iov_len = len - offset };
|
||||
|
||||
ret = qio_channel_writev_full(
|
||||
ioc, &iov, 1,
|
||||
fds, nfds, NULL);
|
||||
if (ret == QIO_CHANNEL_ERR_BLOCK) {
|
||||
if (offset) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
errno = EAGAIN;
|
||||
return -1;
|
||||
} else if (ret < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
offset += ret;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
int io_channel_send(QIOChannel *ioc, const void *buf, size_t len)
|
||||
{
|
||||
return io_channel_send_full(ioc, buf, len, NULL, 0);
|
||||
}
|
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef CHAR_IO_H
|
||||
#define CHAR_IO_H
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "io/channel.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
/* Can only be used for read */
|
||||
guint io_add_watch_poll(Chardev *chr,
|
||||
QIOChannel *ioc,
|
||||
IOCanReadHandler *fd_can_read,
|
||||
QIOChannelFunc fd_read,
|
||||
gpointer user_data,
|
||||
GMainContext *context);
|
||||
|
||||
void remove_fd_in_watch(Chardev *chr);
|
||||
|
||||
int io_channel_send(QIOChannel *ioc, const void *buf, size_t len);
|
||||
|
||||
int io_channel_send_full(QIOChannel *ioc, const void *buf, size_t len,
|
||||
int *fds, size_t nfds);
|
||||
|
||||
#endif /* CHAR_IO_H */
|
@@ -1,358 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "char-mux.h"
|
||||
|
||||
/* MUX driver for serial I/O splitting */
|
||||
|
||||
/* Called with chr_write_lock held. */
|
||||
static int mux_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
||||
{
|
||||
MuxChardev *d = MUX_CHARDEV(chr);
|
||||
int ret;
|
||||
if (!d->timestamps) {
|
||||
ret = qemu_chr_fe_write(&d->chr, buf, len);
|
||||
} else {
|
||||
int i;
|
||||
|
||||
ret = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (d->linestart) {
|
||||
char buf1[64];
|
||||
int64_t ti;
|
||||
int secs;
|
||||
|
||||
ti = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
|
||||
if (d->timestamps_start == -1) {
|
||||
d->timestamps_start = ti;
|
||||
}
|
||||
ti -= d->timestamps_start;
|
||||
secs = ti / 1000;
|
||||
snprintf(buf1, sizeof(buf1),
|
||||
"[%02d:%02d:%02d.%03d] ",
|
||||
secs / 3600,
|
||||
(secs / 60) % 60,
|
||||
secs % 60,
|
||||
(int)(ti % 1000));
|
||||
/* XXX this blocks entire thread. Rewrite to use
|
||||
* qemu_chr_fe_write and background I/O callbacks */
|
||||
qemu_chr_fe_write_all(&d->chr,
|
||||
(uint8_t *)buf1, strlen(buf1));
|
||||
d->linestart = 0;
|
||||
}
|
||||
ret += qemu_chr_fe_write(&d->chr, buf + i, 1);
|
||||
if (buf[i] == '\n') {
|
||||
d->linestart = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char * const mux_help[] = {
|
||||
"% h print this help\n\r",
|
||||
"% x exit emulator\n\r",
|
||||
"% s save disk data back to file (if -snapshot)\n\r",
|
||||
"% t toggle console timestamps\n\r",
|
||||
"% b send break (magic sysrq)\n\r",
|
||||
"% c switch between console and monitor\n\r",
|
||||
"% % sends %\n\r",
|
||||
NULL
|
||||
};
|
||||
|
||||
int term_escape_char = 0x01; /* ctrl-a is used for escape */
|
||||
static void mux_print_help(Chardev *chr)
|
||||
{
|
||||
int i, j;
|
||||
char ebuf[15] = "Escape-Char";
|
||||
char cbuf[50] = "\n\r";
|
||||
|
||||
if (term_escape_char > 0 && term_escape_char < 26) {
|
||||
snprintf(cbuf, sizeof(cbuf), "\n\r");
|
||||
snprintf(ebuf, sizeof(ebuf), "C-%c", term_escape_char - 1 + 'a');
|
||||
} else {
|
||||
snprintf(cbuf, sizeof(cbuf),
|
||||
"\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r",
|
||||
term_escape_char);
|
||||
}
|
||||
/* XXX this blocks entire thread. Rewrite to use
|
||||
* qemu_chr_fe_write and background I/O callbacks */
|
||||
qemu_chr_write_all(chr, (uint8_t *)cbuf, strlen(cbuf));
|
||||
for (i = 0; mux_help[i] != NULL; i++) {
|
||||
for (j = 0; mux_help[i][j] != '\0'; j++) {
|
||||
if (mux_help[i][j] == '%') {
|
||||
qemu_chr_write_all(chr, (uint8_t *)ebuf, strlen(ebuf));
|
||||
} else {
|
||||
qemu_chr_write_all(chr, (uint8_t *)&mux_help[i][j], 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mux_chr_send_event(MuxChardev *d, int mux_nr, int event)
|
||||
{
|
||||
CharBackend *be = d->backends[mux_nr];
|
||||
|
||||
if (be && be->chr_event) {
|
||||
be->chr_event(be->opaque, event);
|
||||
}
|
||||
}
|
||||
|
||||
static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch)
|
||||
{
|
||||
if (d->term_got_escape) {
|
||||
d->term_got_escape = 0;
|
||||
if (ch == term_escape_char) {
|
||||
goto send_char;
|
||||
}
|
||||
switch (ch) {
|
||||
case '?':
|
||||
case 'h':
|
||||
mux_print_help(chr);
|
||||
break;
|
||||
case 'x':
|
||||
{
|
||||
const char *term = "QEMU: Terminated\n\r";
|
||||
qemu_chr_write_all(chr, (uint8_t *)term, strlen(term));
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
blk_commit_all();
|
||||
break;
|
||||
case 'b':
|
||||
qemu_chr_be_event(chr, CHR_EVENT_BREAK);
|
||||
break;
|
||||
case 'c':
|
||||
assert(d->mux_cnt > 0); /* handler registered with first fe */
|
||||
/* Switch to the next registered device */
|
||||
mux_set_focus(chr, (d->focus + 1) % d->mux_cnt);
|
||||
break;
|
||||
case 't':
|
||||
d->timestamps = !d->timestamps;
|
||||
d->timestamps_start = -1;
|
||||
d->linestart = 0;
|
||||
break;
|
||||
}
|
||||
} else if (ch == term_escape_char) {
|
||||
d->term_got_escape = 1;
|
||||
} else {
|
||||
send_char:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mux_chr_accept_input(Chardev *chr)
|
||||
{
|
||||
MuxChardev *d = MUX_CHARDEV(chr);
|
||||
int m = d->focus;
|
||||
CharBackend *be = d->backends[m];
|
||||
|
||||
while (be && d->prod[m] != d->cons[m] &&
|
||||
be->chr_can_read && be->chr_can_read(be->opaque)) {
|
||||
be->chr_read(be->opaque,
|
||||
&d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int mux_chr_can_read(void *opaque)
|
||||
{
|
||||
MuxChardev *d = MUX_CHARDEV(opaque);
|
||||
int m = d->focus;
|
||||
CharBackend *be = d->backends[m];
|
||||
|
||||
if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (be && be->chr_can_read) {
|
||||
return be->chr_can_read(be->opaque);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
MuxChardev *d = MUX_CHARDEV(opaque);
|
||||
int m = d->focus;
|
||||
CharBackend *be = d->backends[m];
|
||||
int i;
|
||||
|
||||
mux_chr_accept_input(opaque);
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
if (mux_proc_byte(chr, d, buf[i])) {
|
||||
if (d->prod[m] == d->cons[m] &&
|
||||
be && be->chr_can_read &&
|
||||
be->chr_can_read(be->opaque)) {
|
||||
be->chr_read(be->opaque, &buf[i], 1);
|
||||
} else {
|
||||
d->buffer[m][d->prod[m]++ & MUX_BUFFER_MASK] = buf[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool muxes_realized;
|
||||
|
||||
static void mux_chr_event(void *opaque, int event)
|
||||
{
|
||||
MuxChardev *d = MUX_CHARDEV(opaque);
|
||||
int i;
|
||||
|
||||
if (!muxes_realized) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Send the event to all registered listeners */
|
||||
for (i = 0; i < d->mux_cnt; i++) {
|
||||
mux_chr_send_event(d, i, event);
|
||||
}
|
||||
}
|
||||
|
||||
static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond)
|
||||
{
|
||||
MuxChardev *d = MUX_CHARDEV(s);
|
||||
Chardev *chr = qemu_chr_fe_get_driver(&d->chr);
|
||||
ChardevClass *cc = CHARDEV_GET_CLASS(chr);
|
||||
|
||||
if (!cc->chr_add_watch) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return cc->chr_add_watch(chr, cond);
|
||||
}
|
||||
|
||||
static void char_mux_finalize(Object *obj)
|
||||
{
|
||||
MuxChardev *d = MUX_CHARDEV(obj);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < d->mux_cnt; i++) {
|
||||
CharBackend *be = d->backends[i];
|
||||
if (be) {
|
||||
be->chr = NULL;
|
||||
}
|
||||
}
|
||||
qemu_chr_fe_deinit(&d->chr);
|
||||
}
|
||||
|
||||
void mux_chr_set_handlers(Chardev *chr, GMainContext *context)
|
||||
{
|
||||
MuxChardev *d = MUX_CHARDEV(chr);
|
||||
|
||||
/* Fix up the real driver with mux routines */
|
||||
qemu_chr_fe_set_handlers(&d->chr,
|
||||
mux_chr_can_read,
|
||||
mux_chr_read,
|
||||
mux_chr_event,
|
||||
chr,
|
||||
context, true);
|
||||
}
|
||||
|
||||
void mux_set_focus(Chardev *chr, int focus)
|
||||
{
|
||||
MuxChardev *d = MUX_CHARDEV(chr);
|
||||
|
||||
assert(focus >= 0);
|
||||
assert(focus < d->mux_cnt);
|
||||
|
||||
if (d->focus != -1) {
|
||||
mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
|
||||
}
|
||||
|
||||
d->focus = focus;
|
||||
mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
|
||||
}
|
||||
|
||||
static void qemu_chr_open_mux(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevMux *mux = backend->u.mux.data;
|
||||
Chardev *drv;
|
||||
MuxChardev *d = MUX_CHARDEV(chr);
|
||||
|
||||
drv = qemu_chr_find(mux->chardev);
|
||||
if (drv == NULL) {
|
||||
error_setg(errp, "mux: base chardev %s not found", mux->chardev);
|
||||
return;
|
||||
}
|
||||
|
||||
d->focus = -1;
|
||||
/* only default to opened state if we've realized the initial
|
||||
* set of muxes
|
||||
*/
|
||||
*be_opened = muxes_realized;
|
||||
qemu_chr_fe_init(&d->chr, drv, errp);
|
||||
}
|
||||
|
||||
static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
|
||||
Error **errp)
|
||||
{
|
||||
const char *chardev = qemu_opt_get(opts, "chardev");
|
||||
ChardevMux *mux;
|
||||
|
||||
if (chardev == NULL) {
|
||||
error_setg(errp, "chardev: mux: no chardev given");
|
||||
return;
|
||||
}
|
||||
backend->type = CHARDEV_BACKEND_KIND_MUX;
|
||||
mux = backend->u.mux.data = g_new0(ChardevMux, 1);
|
||||
qemu_chr_parse_common(opts, qapi_ChardevMux_base(mux));
|
||||
mux->chardev = g_strdup(chardev);
|
||||
}
|
||||
|
||||
static void char_mux_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_mux;
|
||||
cc->open = qemu_chr_open_mux;
|
||||
cc->chr_write = mux_chr_write;
|
||||
cc->chr_accept_input = mux_chr_accept_input;
|
||||
cc->chr_add_watch = mux_chr_add_watch;
|
||||
}
|
||||
|
||||
static const TypeInfo char_mux_type_info = {
|
||||
.name = TYPE_CHARDEV_MUX,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.class_init = char_mux_class_init,
|
||||
.instance_size = sizeof(MuxChardev),
|
||||
.instance_finalize = char_mux_finalize,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_mux_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef CHAR_MUX_H
|
||||
#define CHAR_MUX_H
|
||||
|
||||
#include "sysemu/char.h"
|
||||
|
||||
extern bool muxes_realized;
|
||||
|
||||
#define MAX_MUX 4
|
||||
#define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */
|
||||
#define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
|
||||
typedef struct MuxChardev {
|
||||
Chardev parent;
|
||||
CharBackend *backends[MAX_MUX];
|
||||
CharBackend chr;
|
||||
int focus;
|
||||
int mux_cnt;
|
||||
int term_got_escape;
|
||||
int max_size;
|
||||
/* Intermediate input buffer catches escape sequences even if the
|
||||
currently active device is not accepting any input - but only until it
|
||||
is full as well. */
|
||||
unsigned char buffer[MAX_MUX][MUX_BUFFER_SIZE];
|
||||
int prod[MAX_MUX];
|
||||
int cons[MAX_MUX];
|
||||
int timestamps;
|
||||
|
||||
/* Protected by the Chardev chr_write_lock. */
|
||||
int linestart;
|
||||
int64_t timestamps_start;
|
||||
} MuxChardev;
|
||||
|
||||
#define MUX_CHARDEV(obj) OBJECT_CHECK(MuxChardev, (obj), TYPE_CHARDEV_MUX)
|
||||
#define CHARDEV_IS_MUX(chr) \
|
||||
object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX)
|
||||
|
||||
void mux_chr_set_handlers(Chardev *chr, GMainContext *context);
|
||||
void mux_set_focus(Chardev *chr, int focus);
|
||||
void mux_chr_send_event(MuxChardev *d, int mux_nr, int event);
|
||||
|
||||
#endif /* CHAR_MUX_H */
|
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
static void null_chr_open(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
*be_opened = false;
|
||||
}
|
||||
|
||||
static void char_null_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->open = null_chr_open;
|
||||
}
|
||||
|
||||
static const TypeInfo char_null_type_info = {
|
||||
.name = TYPE_CHARDEV_NULL,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.instance_size = sizeof(Chardev),
|
||||
.class_init = char_null_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_null_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
@@ -1,316 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "qapi/error.h"
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#ifdef CONFIG_BSD
|
||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
#include <dev/ppbus/ppi.h>
|
||||
#include <dev/ppbus/ppbconf.h>
|
||||
#elif defined(__DragonFly__)
|
||||
#include <dev/misc/ppi/ppi.h>
|
||||
#include <bus/ppbus/ppbconf.h>
|
||||
#endif
|
||||
#else
|
||||
#ifdef __linux__
|
||||
#include <linux/ppdev.h>
|
||||
#include <linux/parport.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "char-fd.h"
|
||||
#include "char-parallel.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
int fd;
|
||||
int mode;
|
||||
} ParallelChardev;
|
||||
|
||||
#define PARALLEL_CHARDEV(obj) \
|
||||
OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
|
||||
|
||||
static int pp_hw_mode(ParallelChardev *s, uint16_t mode)
|
||||
{
|
||||
if (s->mode != mode) {
|
||||
int m = mode;
|
||||
if (ioctl(s->fd, PPSETMODE, &m) < 0) {
|
||||
return 0;
|
||||
}
|
||||
s->mode = mode;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pp_ioctl(Chardev *chr, int cmd, void *arg)
|
||||
{
|
||||
ParallelChardev *drv = PARALLEL_CHARDEV(chr);
|
||||
int fd = drv->fd;
|
||||
uint8_t b;
|
||||
|
||||
switch (cmd) {
|
||||
case CHR_IOCTL_PP_READ_DATA:
|
||||
if (ioctl(fd, PPRDATA, &b) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
*(uint8_t *)arg = b;
|
||||
break;
|
||||
case CHR_IOCTL_PP_WRITE_DATA:
|
||||
b = *(uint8_t *)arg;
|
||||
if (ioctl(fd, PPWDATA, &b) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_PP_READ_CONTROL:
|
||||
if (ioctl(fd, PPRCONTROL, &b) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
/* Linux gives only the lowest bits, and no way to know data
|
||||
direction! For better compatibility set the fixed upper
|
||||
bits. */
|
||||
*(uint8_t *)arg = b | 0xc0;
|
||||
break;
|
||||
case CHR_IOCTL_PP_WRITE_CONTROL:
|
||||
b = *(uint8_t *)arg;
|
||||
if (ioctl(fd, PPWCONTROL, &b) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_PP_READ_STATUS:
|
||||
if (ioctl(fd, PPRSTATUS, &b) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
*(uint8_t *)arg = b;
|
||||
break;
|
||||
case CHR_IOCTL_PP_DATA_DIR:
|
||||
if (ioctl(fd, PPDATADIR, (int *)arg) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_PP_EPP_READ_ADDR:
|
||||
if (pp_hw_mode(drv, IEEE1284_MODE_EPP | IEEE1284_ADDR)) {
|
||||
struct ParallelIOArg *parg = arg;
|
||||
int n = read(fd, parg->buffer, parg->count);
|
||||
if (n != parg->count) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_PP_EPP_READ:
|
||||
if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
|
||||
struct ParallelIOArg *parg = arg;
|
||||
int n = read(fd, parg->buffer, parg->count);
|
||||
if (n != parg->count) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_PP_EPP_WRITE_ADDR:
|
||||
if (pp_hw_mode(drv, IEEE1284_MODE_EPP | IEEE1284_ADDR)) {
|
||||
struct ParallelIOArg *parg = arg;
|
||||
int n = write(fd, parg->buffer, parg->count);
|
||||
if (n != parg->count) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_PP_EPP_WRITE:
|
||||
if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
|
||||
struct ParallelIOArg *parg = arg;
|
||||
int n = write(fd, parg->buffer, parg->count);
|
||||
if (n != parg->count) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qemu_chr_open_pp_fd(Chardev *chr,
|
||||
int fd,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ParallelChardev *drv = PARALLEL_CHARDEV(chr);
|
||||
|
||||
if (ioctl(fd, PPCLAIM) < 0) {
|
||||
error_setg_errno(errp, errno, "not a parallel port");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
drv->fd = fd;
|
||||
drv->mode = IEEE1284_MODE_COMPAT;
|
||||
}
|
||||
#endif /* __linux__ */
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
int fd;
|
||||
} ParallelChardev;
|
||||
|
||||
#define PARALLEL_CHARDEV(obj) \
|
||||
OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
|
||||
|
||||
static int pp_ioctl(Chardev *chr, int cmd, void *arg)
|
||||
{
|
||||
ParallelChardev *drv = PARALLEL_CHARDEV(chr);
|
||||
uint8_t b;
|
||||
|
||||
switch (cmd) {
|
||||
case CHR_IOCTL_PP_READ_DATA:
|
||||
if (ioctl(drv->fd, PPIGDATA, &b) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
*(uint8_t *)arg = b;
|
||||
break;
|
||||
case CHR_IOCTL_PP_WRITE_DATA:
|
||||
b = *(uint8_t *)arg;
|
||||
if (ioctl(drv->fd, PPISDATA, &b) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_PP_READ_CONTROL:
|
||||
if (ioctl(drv->fd, PPIGCTRL, &b) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
*(uint8_t *)arg = b;
|
||||
break;
|
||||
case CHR_IOCTL_PP_WRITE_CONTROL:
|
||||
b = *(uint8_t *)arg;
|
||||
if (ioctl(drv->fd, PPISCTRL, &b) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_PP_READ_STATUS:
|
||||
if (ioctl(drv->fd, PPIGSTATUS, &b) < 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
*(uint8_t *)arg = b;
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qemu_chr_open_pp_fd(Chardev *chr,
|
||||
int fd,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ParallelChardev *drv = PARALLEL_CHARDEV(chr);
|
||||
drv->fd = fd;
|
||||
*be_opened = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CHARDEV_PARPORT
|
||||
static void qmp_chardev_open_parallel(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevHostdev *parallel = backend->u.parallel.data;
|
||||
int fd;
|
||||
|
||||
fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
|
||||
if (fd < 0) {
|
||||
return;
|
||||
}
|
||||
qemu_chr_open_pp_fd(chr, fd, be_opened, errp);
|
||||
}
|
||||
|
||||
static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend,
|
||||
Error **errp)
|
||||
{
|
||||
const char *device = qemu_opt_get(opts, "path");
|
||||
ChardevHostdev *parallel;
|
||||
|
||||
if (device == NULL) {
|
||||
error_setg(errp, "chardev: parallel: no device path given");
|
||||
return;
|
||||
}
|
||||
backend->type = CHARDEV_BACKEND_KIND_PARALLEL;
|
||||
parallel = backend->u.parallel.data = g_new0(ChardevHostdev, 1);
|
||||
qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(parallel));
|
||||
parallel->device = g_strdup(device);
|
||||
}
|
||||
|
||||
static void char_parallel_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_parallel;
|
||||
cc->open = qmp_chardev_open_parallel;
|
||||
#if defined(__linux__)
|
||||
cc->chr_ioctl = pp_ioctl;
|
||||
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
|
||||
defined(__DragonFly__)
|
||||
cc->chr_ioctl = pp_ioctl;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void char_parallel_finalize(Object *obj)
|
||||
{
|
||||
#if defined(__linux__)
|
||||
Chardev *chr = CHARDEV(obj);
|
||||
ParallelChardev *drv = PARALLEL_CHARDEV(chr);
|
||||
int fd = drv->fd;
|
||||
|
||||
pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
|
||||
ioctl(fd, PPRELEASE);
|
||||
close(fd);
|
||||
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
||||
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
|
||||
defined(__DragonFly__)
|
||||
/* FIXME: close fd? */
|
||||
#endif
|
||||
}
|
||||
|
||||
static const TypeInfo char_parallel_type_info = {
|
||||
.name = TYPE_CHARDEV_PARALLEL,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.instance_size = sizeof(ParallelChardev),
|
||||
.instance_finalize = char_parallel_finalize,
|
||||
.class_init = char_parallel_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_parallel_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
|
||||
#endif
|
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef CHAR_PARALLEL_H
|
||||
#define CHAR_PARALLEL_H
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || \
|
||||
defined(__FreeBSD_kernel__) || defined(__DragonFly__)
|
||||
#define HAVE_CHARDEV_PARPORT 1
|
||||
#endif
|
||||
|
||||
#endif /* CHAR_PARALLEL_H */
|
@@ -1,191 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "char-win.h"
|
||||
#else
|
||||
#include "char-fd.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define MAXCONNECT 1
|
||||
#define NTIMEOUT 5000
|
||||
|
||||
static int win_chr_pipe_init(Chardev *chr, const char *filename,
|
||||
Error **errp)
|
||||
{
|
||||
WinChardev *s = WIN_CHARDEV(chr);
|
||||
OVERLAPPED ov;
|
||||
int ret;
|
||||
DWORD size;
|
||||
char *openname;
|
||||
|
||||
s->fpipe = TRUE;
|
||||
|
||||
s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!s->hsend) {
|
||||
error_setg(errp, "Failed CreateEvent");
|
||||
goto fail;
|
||||
}
|
||||
s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!s->hrecv) {
|
||||
error_setg(errp, "Failed CreateEvent");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
openname = g_strdup_printf("\\\\.\\pipe\\%s", filename);
|
||||
s->hcom = CreateNamedPipe(openname,
|
||||
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
||||
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |
|
||||
PIPE_WAIT,
|
||||
MAXCONNECT, NSENDBUF, NRECVBUF, NTIMEOUT, NULL);
|
||||
g_free(openname);
|
||||
if (s->hcom == INVALID_HANDLE_VALUE) {
|
||||
error_setg(errp, "Failed CreateNamedPipe (%lu)", GetLastError());
|
||||
s->hcom = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ZeroMemory(&ov, sizeof(ov));
|
||||
ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
ret = ConnectNamedPipe(s->hcom, &ov);
|
||||
if (ret) {
|
||||
error_setg(errp, "Failed ConnectNamedPipe");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = GetOverlappedResult(s->hcom, &ov, &size, TRUE);
|
||||
if (!ret) {
|
||||
error_setg(errp, "Failed GetOverlappedResult");
|
||||
if (ov.hEvent) {
|
||||
CloseHandle(ov.hEvent);
|
||||
ov.hEvent = NULL;
|
||||
}
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ov.hEvent) {
|
||||
CloseHandle(ov.hEvent);
|
||||
ov.hEvent = NULL;
|
||||
}
|
||||
qemu_add_polling_cb(win_chr_pipe_poll, chr);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void qemu_chr_open_pipe(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevHostdev *opts = backend->u.pipe.data;
|
||||
const char *filename = opts->device;
|
||||
|
||||
if (win_chr_pipe_init(chr, filename, errp) < 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void qemu_chr_open_pipe(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevHostdev *opts = backend->u.pipe.data;
|
||||
int fd_in, fd_out;
|
||||
char *filename_in;
|
||||
char *filename_out;
|
||||
const char *filename = opts->device;
|
||||
|
||||
filename_in = g_strdup_printf("%s.in", filename);
|
||||
filename_out = g_strdup_printf("%s.out", filename);
|
||||
TFR(fd_in = qemu_open(filename_in, O_RDWR | O_BINARY));
|
||||
TFR(fd_out = qemu_open(filename_out, O_RDWR | O_BINARY));
|
||||
g_free(filename_in);
|
||||
g_free(filename_out);
|
||||
if (fd_in < 0 || fd_out < 0) {
|
||||
if (fd_in >= 0) {
|
||||
close(fd_in);
|
||||
}
|
||||
if (fd_out >= 0) {
|
||||
close(fd_out);
|
||||
}
|
||||
TFR(fd_in = fd_out = qemu_open(filename, O_RDWR | O_BINARY));
|
||||
if (fd_in < 0) {
|
||||
error_setg_file_open(errp, errno, filename);
|
||||
return;
|
||||
}
|
||||
}
|
||||
qemu_chr_open_fd(chr, fd_in, fd_out);
|
||||
}
|
||||
|
||||
#endif /* !_WIN32 */
|
||||
|
||||
static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend,
|
||||
Error **errp)
|
||||
{
|
||||
const char *device = qemu_opt_get(opts, "path");
|
||||
ChardevHostdev *dev;
|
||||
|
||||
if (device == NULL) {
|
||||
error_setg(errp, "chardev: pipe: no device path given");
|
||||
return;
|
||||
}
|
||||
backend->type = CHARDEV_BACKEND_KIND_PIPE;
|
||||
dev = backend->u.pipe.data = g_new0(ChardevHostdev, 1);
|
||||
qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(dev));
|
||||
dev->device = g_strdup(device);
|
||||
}
|
||||
|
||||
static void char_pipe_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_pipe;
|
||||
cc->open = qemu_chr_open_pipe;
|
||||
}
|
||||
|
||||
static const TypeInfo char_pipe_type_info = {
|
||||
.name = TYPE_CHARDEV_PIPE,
|
||||
#ifdef _WIN32
|
||||
.parent = TYPE_CHARDEV_WIN,
|
||||
#else
|
||||
.parent = TYPE_CHARDEV_FD,
|
||||
#endif
|
||||
.class_init = char_pipe_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_pipe_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
@@ -1,300 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "io/channel-file.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#include "char-io.h"
|
||||
|
||||
#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
|
||||
|| defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
|
||||
|| defined(__GLIBC__)
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
QIOChannel *ioc;
|
||||
int read_bytes;
|
||||
|
||||
/* Protected by the Chardev chr_write_lock. */
|
||||
int connected;
|
||||
guint timer_tag;
|
||||
guint open_tag;
|
||||
} PtyChardev;
|
||||
|
||||
#define PTY_CHARDEV(obj) OBJECT_CHECK(PtyChardev, (obj), TYPE_CHARDEV_PTY)
|
||||
|
||||
static void pty_chr_update_read_handler_locked(Chardev *chr);
|
||||
static void pty_chr_state(Chardev *chr, int connected);
|
||||
|
||||
static gboolean pty_chr_timer(gpointer opaque)
|
||||
{
|
||||
struct Chardev *chr = CHARDEV(opaque);
|
||||
PtyChardev *s = PTY_CHARDEV(opaque);
|
||||
|
||||
qemu_mutex_lock(&chr->chr_write_lock);
|
||||
s->timer_tag = 0;
|
||||
s->open_tag = 0;
|
||||
if (!s->connected) {
|
||||
/* Next poll ... */
|
||||
pty_chr_update_read_handler_locked(chr);
|
||||
}
|
||||
qemu_mutex_unlock(&chr->chr_write_lock);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Called with chr_write_lock held. */
|
||||
static void pty_chr_rearm_timer(Chardev *chr, int ms)
|
||||
{
|
||||
PtyChardev *s = PTY_CHARDEV(chr);
|
||||
char *name;
|
||||
|
||||
if (s->timer_tag) {
|
||||
g_source_remove(s->timer_tag);
|
||||
s->timer_tag = 0;
|
||||
}
|
||||
|
||||
if (ms == 1000) {
|
||||
name = g_strdup_printf("pty-timer-secs-%s", chr->label);
|
||||
s->timer_tag = g_timeout_add_seconds(1, pty_chr_timer, chr);
|
||||
} else {
|
||||
name = g_strdup_printf("pty-timer-ms-%s", chr->label);
|
||||
s->timer_tag = g_timeout_add(ms, pty_chr_timer, chr);
|
||||
}
|
||||
g_source_set_name_by_id(s->timer_tag, name);
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
/* Called with chr_write_lock held. */
|
||||
static void pty_chr_update_read_handler_locked(Chardev *chr)
|
||||
{
|
||||
PtyChardev *s = PTY_CHARDEV(chr);
|
||||
GPollFD pfd;
|
||||
int rc;
|
||||
QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc);
|
||||
|
||||
pfd.fd = fioc->fd;
|
||||
pfd.events = G_IO_OUT;
|
||||
pfd.revents = 0;
|
||||
do {
|
||||
rc = g_poll(&pfd, 1, 0);
|
||||
} while (rc == -1 && errno == EINTR);
|
||||
assert(rc >= 0);
|
||||
|
||||
if (pfd.revents & G_IO_HUP) {
|
||||
pty_chr_state(chr, 0);
|
||||
} else {
|
||||
pty_chr_state(chr, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void pty_chr_update_read_handler(Chardev *chr,
|
||||
GMainContext *context)
|
||||
{
|
||||
qemu_mutex_lock(&chr->chr_write_lock);
|
||||
pty_chr_update_read_handler_locked(chr);
|
||||
qemu_mutex_unlock(&chr->chr_write_lock);
|
||||
}
|
||||
|
||||
/* Called with chr_write_lock held. */
|
||||
static int char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
||||
{
|
||||
PtyChardev *s = PTY_CHARDEV(chr);
|
||||
|
||||
if (!s->connected) {
|
||||
/* guest sends data, check for (re-)connect */
|
||||
pty_chr_update_read_handler_locked(chr);
|
||||
if (!s->connected) {
|
||||
return len;
|
||||
}
|
||||
}
|
||||
return io_channel_send(s->ioc, buf, len);
|
||||
}
|
||||
|
||||
static GSource *pty_chr_add_watch(Chardev *chr, GIOCondition cond)
|
||||
{
|
||||
PtyChardev *s = PTY_CHARDEV(chr);
|
||||
if (!s->connected) {
|
||||
return NULL;
|
||||
}
|
||||
return qio_channel_create_watch(s->ioc, cond);
|
||||
}
|
||||
|
||||
static int pty_chr_read_poll(void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
PtyChardev *s = PTY_CHARDEV(opaque);
|
||||
|
||||
s->read_bytes = qemu_chr_be_can_write(chr);
|
||||
return s->read_bytes;
|
||||
}
|
||||
|
||||
static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
PtyChardev *s = PTY_CHARDEV(opaque);
|
||||
gsize len;
|
||||
uint8_t buf[CHR_READ_BUF_LEN];
|
||||
ssize_t ret;
|
||||
|
||||
len = sizeof(buf);
|
||||
if (len > s->read_bytes) {
|
||||
len = s->read_bytes;
|
||||
}
|
||||
if (len == 0) {
|
||||
return TRUE;
|
||||
}
|
||||
ret = qio_channel_read(s->ioc, (char *)buf, len, NULL);
|
||||
if (ret <= 0) {
|
||||
pty_chr_state(chr, 0);
|
||||
return FALSE;
|
||||
} else {
|
||||
pty_chr_state(chr, 1);
|
||||
qemu_chr_be_write(chr, buf, ret);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
PtyChardev *s = PTY_CHARDEV(opaque);
|
||||
|
||||
s->open_tag = 0;
|
||||
qemu_chr_be_generic_open(chr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Called with chr_write_lock held. */
|
||||
static void pty_chr_state(Chardev *chr, int connected)
|
||||
{
|
||||
PtyChardev *s = PTY_CHARDEV(chr);
|
||||
|
||||
if (!connected) {
|
||||
if (s->open_tag) {
|
||||
g_source_remove(s->open_tag);
|
||||
s->open_tag = 0;
|
||||
}
|
||||
remove_fd_in_watch(chr);
|
||||
s->connected = 0;
|
||||
/* (re-)connect poll interval for idle guests: once per second.
|
||||
* We check more frequently in case the guests sends data to
|
||||
* the virtual device linked to our pty. */
|
||||
pty_chr_rearm_timer(chr, 1000);
|
||||
} else {
|
||||
if (s->timer_tag) {
|
||||
g_source_remove(s->timer_tag);
|
||||
s->timer_tag = 0;
|
||||
}
|
||||
if (!s->connected) {
|
||||
g_assert(s->open_tag == 0);
|
||||
s->connected = 1;
|
||||
s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr);
|
||||
}
|
||||
if (!chr->fd_in_tag) {
|
||||
chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
|
||||
pty_chr_read_poll,
|
||||
pty_chr_read,
|
||||
chr, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void char_pty_finalize(Object *obj)
|
||||
{
|
||||
Chardev *chr = CHARDEV(obj);
|
||||
PtyChardev *s = PTY_CHARDEV(obj);
|
||||
|
||||
qemu_mutex_lock(&chr->chr_write_lock);
|
||||
pty_chr_state(chr, 0);
|
||||
object_unref(OBJECT(s->ioc));
|
||||
if (s->timer_tag) {
|
||||
g_source_remove(s->timer_tag);
|
||||
s->timer_tag = 0;
|
||||
}
|
||||
qemu_mutex_unlock(&chr->chr_write_lock);
|
||||
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
||||
}
|
||||
|
||||
static void char_pty_open(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
PtyChardev *s;
|
||||
int master_fd, slave_fd;
|
||||
char pty_name[PATH_MAX];
|
||||
char *name;
|
||||
|
||||
master_fd = qemu_openpty_raw(&slave_fd, pty_name);
|
||||
if (master_fd < 0) {
|
||||
error_setg_errno(errp, errno, "Failed to create PTY");
|
||||
return;
|
||||
}
|
||||
|
||||
close(slave_fd);
|
||||
qemu_set_nonblock(master_fd);
|
||||
|
||||
chr->filename = g_strdup_printf("pty:%s", pty_name);
|
||||
error_report("char device redirected to %s (label %s)",
|
||||
pty_name, chr->label);
|
||||
|
||||
s = PTY_CHARDEV(chr);
|
||||
s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
|
||||
name = g_strdup_printf("chardev-pty-%s", chr->label);
|
||||
qio_channel_set_name(QIO_CHANNEL(s->ioc), name);
|
||||
g_free(name);
|
||||
s->timer_tag = 0;
|
||||
*be_opened = false;
|
||||
}
|
||||
|
||||
static void char_pty_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->open = char_pty_open;
|
||||
cc->chr_write = char_pty_chr_write;
|
||||
cc->chr_update_read_handler = pty_chr_update_read_handler;
|
||||
cc->chr_add_watch = pty_chr_add_watch;
|
||||
}
|
||||
|
||||
static const TypeInfo char_pty_type_info = {
|
||||
.name = TYPE_CHARDEV_PTY,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.instance_size = sizeof(PtyChardev),
|
||||
.instance_finalize = char_pty_finalize,
|
||||
.class_init = char_pty_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_pty_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
|
||||
#endif
|
@@ -1,249 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "qemu/base64.h"
|
||||
|
||||
/* Ring buffer chardev */
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
size_t size;
|
||||
size_t prod;
|
||||
size_t cons;
|
||||
uint8_t *cbuf;
|
||||
} RingBufChardev;
|
||||
|
||||
#define RINGBUF_CHARDEV(obj) \
|
||||
OBJECT_CHECK(RingBufChardev, (obj), TYPE_CHARDEV_RINGBUF)
|
||||
|
||||
static size_t ringbuf_count(const Chardev *chr)
|
||||
{
|
||||
const RingBufChardev *d = RINGBUF_CHARDEV(chr);
|
||||
|
||||
return d->prod - d->cons;
|
||||
}
|
||||
|
||||
static int ringbuf_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
||||
{
|
||||
RingBufChardev *d = RINGBUF_CHARDEV(chr);
|
||||
int i;
|
||||
|
||||
if (!buf || (len < 0)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
d->cbuf[d->prod++ & (d->size - 1)] = buf[i];
|
||||
if (d->prod - d->cons > d->size) {
|
||||
d->cons = d->prod - d->size;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int ringbuf_chr_read(Chardev *chr, uint8_t *buf, int len)
|
||||
{
|
||||
RingBufChardev *d = RINGBUF_CHARDEV(chr);
|
||||
int i;
|
||||
|
||||
qemu_mutex_lock(&chr->chr_write_lock);
|
||||
for (i = 0; i < len && d->cons != d->prod; i++) {
|
||||
buf[i] = d->cbuf[d->cons++ & (d->size - 1)];
|
||||
}
|
||||
qemu_mutex_unlock(&chr->chr_write_lock);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static void char_ringbuf_finalize(Object *obj)
|
||||
{
|
||||
RingBufChardev *d = RINGBUF_CHARDEV(obj);
|
||||
|
||||
g_free(d->cbuf);
|
||||
}
|
||||
|
||||
static void qemu_chr_open_ringbuf(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevRingbuf *opts = backend->u.ringbuf.data;
|
||||
RingBufChardev *d = RINGBUF_CHARDEV(chr);
|
||||
|
||||
d->size = opts->has_size ? opts->size : 65536;
|
||||
|
||||
/* The size must be power of 2 */
|
||||
if (d->size & (d->size - 1)) {
|
||||
error_setg(errp, "size of ringbuf chardev must be power of two");
|
||||
return;
|
||||
}
|
||||
|
||||
d->prod = 0;
|
||||
d->cons = 0;
|
||||
d->cbuf = g_malloc0(d->size);
|
||||
}
|
||||
|
||||
void qmp_ringbuf_write(const char *device, const char *data,
|
||||
bool has_format, enum DataFormat format,
|
||||
Error **errp)
|
||||
{
|
||||
Chardev *chr;
|
||||
const uint8_t *write_data;
|
||||
int ret;
|
||||
gsize write_count;
|
||||
|
||||
chr = qemu_chr_find(device);
|
||||
if (!chr) {
|
||||
error_setg(errp, "Device '%s' not found", device);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CHARDEV_IS_RINGBUF(chr)) {
|
||||
error_setg(errp, "%s is not a ringbuf device", device);
|
||||
return;
|
||||
}
|
||||
|
||||
if (has_format && (format == DATA_FORMAT_BASE64)) {
|
||||
write_data = qbase64_decode(data, -1,
|
||||
&write_count,
|
||||
errp);
|
||||
if (!write_data) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
write_data = (uint8_t *)data;
|
||||
write_count = strlen(data);
|
||||
}
|
||||
|
||||
ret = ringbuf_chr_write(chr, write_data, write_count);
|
||||
|
||||
if (write_data != (uint8_t *)data) {
|
||||
g_free((void *)write_data);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Failed to write to device %s", device);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
char *qmp_ringbuf_read(const char *device, int64_t size,
|
||||
bool has_format, enum DataFormat format,
|
||||
Error **errp)
|
||||
{
|
||||
Chardev *chr;
|
||||
uint8_t *read_data;
|
||||
size_t count;
|
||||
char *data;
|
||||
|
||||
chr = qemu_chr_find(device);
|
||||
if (!chr) {
|
||||
error_setg(errp, "Device '%s' not found", device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!CHARDEV_IS_RINGBUF(chr)) {
|
||||
error_setg(errp, "%s is not a ringbuf device", device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (size <= 0) {
|
||||
error_setg(errp, "size must be greater than zero");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
count = ringbuf_count(chr);
|
||||
size = size > count ? count : size;
|
||||
read_data = g_malloc(size + 1);
|
||||
|
||||
ringbuf_chr_read(chr, read_data, size);
|
||||
|
||||
if (has_format && (format == DATA_FORMAT_BASE64)) {
|
||||
data = g_base64_encode(read_data, size);
|
||||
g_free(read_data);
|
||||
} else {
|
||||
/*
|
||||
* FIXME should read only complete, valid UTF-8 characters up
|
||||
* to @size bytes. Invalid sequences should be replaced by a
|
||||
* suitable replacement character. Except when (and only
|
||||
* when) ring buffer lost characters since last read, initial
|
||||
* continuation characters should be dropped.
|
||||
*/
|
||||
read_data[size] = 0;
|
||||
data = (char *)read_data;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
|
||||
Error **errp)
|
||||
{
|
||||
int val;
|
||||
ChardevRingbuf *ringbuf;
|
||||
|
||||
backend->type = CHARDEV_BACKEND_KIND_RINGBUF;
|
||||
ringbuf = backend->u.ringbuf.data = g_new0(ChardevRingbuf, 1);
|
||||
qemu_chr_parse_common(opts, qapi_ChardevRingbuf_base(ringbuf));
|
||||
|
||||
val = qemu_opt_get_size(opts, "size", 0);
|
||||
if (val != 0) {
|
||||
ringbuf->has_size = true;
|
||||
ringbuf->size = val;
|
||||
}
|
||||
}
|
||||
|
||||
static void char_ringbuf_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_ringbuf;
|
||||
cc->open = qemu_chr_open_ringbuf;
|
||||
cc->chr_write = ringbuf_chr_write;
|
||||
}
|
||||
|
||||
static const TypeInfo char_ringbuf_type_info = {
|
||||
.name = TYPE_CHARDEV_RINGBUF,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.class_init = char_ringbuf_class_init,
|
||||
.instance_size = sizeof(RingBufChardev),
|
||||
.instance_finalize = char_ringbuf_finalize,
|
||||
};
|
||||
|
||||
/* Bug-compatibility: */
|
||||
static const TypeInfo char_memory_type_info = {
|
||||
.name = TYPE_CHARDEV_MEMORY,
|
||||
.parent = TYPE_CHARDEV_RINGBUF,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_ringbuf_type_info);
|
||||
type_register_static(&char_memory_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
@@ -1,318 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "io/channel-file.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "char-win.h"
|
||||
#else
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
#include "char-fd.h"
|
||||
#endif
|
||||
|
||||
#include "char-serial.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
static void qmp_chardev_open_serial(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevHostdev *serial = backend->u.serial.data;
|
||||
|
||||
win_chr_init(chr, serial->device, errp);
|
||||
}
|
||||
|
||||
#elif defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
|
||||
|| defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
|
||||
|| defined(__GLIBC__)
|
||||
|
||||
static void tty_serial_init(int fd, int speed,
|
||||
int parity, int data_bits, int stop_bits)
|
||||
{
|
||||
struct termios tty;
|
||||
speed_t spd;
|
||||
|
||||
#if 0
|
||||
printf("tty_serial_init: speed=%d parity=%c data=%d stop=%d\n",
|
||||
speed, parity, data_bits, stop_bits);
|
||||
#endif
|
||||
tcgetattr(fd, &tty);
|
||||
|
||||
#define check_speed(val) if (speed <= val) { spd = B##val; break; }
|
||||
speed = speed * 10 / 11;
|
||||
do {
|
||||
check_speed(50);
|
||||
check_speed(75);
|
||||
check_speed(110);
|
||||
check_speed(134);
|
||||
check_speed(150);
|
||||
check_speed(200);
|
||||
check_speed(300);
|
||||
check_speed(600);
|
||||
check_speed(1200);
|
||||
check_speed(1800);
|
||||
check_speed(2400);
|
||||
check_speed(4800);
|
||||
check_speed(9600);
|
||||
check_speed(19200);
|
||||
check_speed(38400);
|
||||
/* Non-Posix values follow. They may be unsupported on some systems. */
|
||||
check_speed(57600);
|
||||
check_speed(115200);
|
||||
#ifdef B230400
|
||||
check_speed(230400);
|
||||
#endif
|
||||
#ifdef B460800
|
||||
check_speed(460800);
|
||||
#endif
|
||||
#ifdef B500000
|
||||
check_speed(500000);
|
||||
#endif
|
||||
#ifdef B576000
|
||||
check_speed(576000);
|
||||
#endif
|
||||
#ifdef B921600
|
||||
check_speed(921600);
|
||||
#endif
|
||||
#ifdef B1000000
|
||||
check_speed(1000000);
|
||||
#endif
|
||||
#ifdef B1152000
|
||||
check_speed(1152000);
|
||||
#endif
|
||||
#ifdef B1500000
|
||||
check_speed(1500000);
|
||||
#endif
|
||||
#ifdef B2000000
|
||||
check_speed(2000000);
|
||||
#endif
|
||||
#ifdef B2500000
|
||||
check_speed(2500000);
|
||||
#endif
|
||||
#ifdef B3000000
|
||||
check_speed(3000000);
|
||||
#endif
|
||||
#ifdef B3500000
|
||||
check_speed(3500000);
|
||||
#endif
|
||||
#ifdef B4000000
|
||||
check_speed(4000000);
|
||||
#endif
|
||||
spd = B115200;
|
||||
} while (0);
|
||||
|
||||
cfsetispeed(&tty, spd);
|
||||
cfsetospeed(&tty, spd);
|
||||
|
||||
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
|
||||
| INLCR | IGNCR | ICRNL | IXON);
|
||||
tty.c_oflag |= OPOST;
|
||||
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
|
||||
tty.c_cflag &= ~(CSIZE | PARENB | PARODD | CRTSCTS | CSTOPB);
|
||||
switch (data_bits) {
|
||||
default:
|
||||
case 8:
|
||||
tty.c_cflag |= CS8;
|
||||
break;
|
||||
case 7:
|
||||
tty.c_cflag |= CS7;
|
||||
break;
|
||||
case 6:
|
||||
tty.c_cflag |= CS6;
|
||||
break;
|
||||
case 5:
|
||||
tty.c_cflag |= CS5;
|
||||
break;
|
||||
}
|
||||
switch (parity) {
|
||||
default:
|
||||
case 'N':
|
||||
break;
|
||||
case 'E':
|
||||
tty.c_cflag |= PARENB;
|
||||
break;
|
||||
case 'O':
|
||||
tty.c_cflag |= PARENB | PARODD;
|
||||
break;
|
||||
}
|
||||
if (stop_bits == 2) {
|
||||
tty.c_cflag |= CSTOPB;
|
||||
}
|
||||
|
||||
tcsetattr(fd, TCSANOW, &tty);
|
||||
}
|
||||
|
||||
static int tty_serial_ioctl(Chardev *chr, int cmd, void *arg)
|
||||
{
|
||||
FDChardev *s = FD_CHARDEV(chr);
|
||||
QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc_in);
|
||||
|
||||
switch (cmd) {
|
||||
case CHR_IOCTL_SERIAL_SET_PARAMS:
|
||||
{
|
||||
QEMUSerialSetParams *ssp = arg;
|
||||
tty_serial_init(fioc->fd,
|
||||
ssp->speed, ssp->parity,
|
||||
ssp->data_bits, ssp->stop_bits);
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_SERIAL_SET_BREAK:
|
||||
{
|
||||
int enable = *(int *)arg;
|
||||
if (enable) {
|
||||
tcsendbreak(fioc->fd, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_SERIAL_GET_TIOCM:
|
||||
{
|
||||
int sarg = 0;
|
||||
int *targ = (int *)arg;
|
||||
ioctl(fioc->fd, TIOCMGET, &sarg);
|
||||
*targ = 0;
|
||||
if (sarg & TIOCM_CTS) {
|
||||
*targ |= CHR_TIOCM_CTS;
|
||||
}
|
||||
if (sarg & TIOCM_CAR) {
|
||||
*targ |= CHR_TIOCM_CAR;
|
||||
}
|
||||
if (sarg & TIOCM_DSR) {
|
||||
*targ |= CHR_TIOCM_DSR;
|
||||
}
|
||||
if (sarg & TIOCM_RI) {
|
||||
*targ |= CHR_TIOCM_RI;
|
||||
}
|
||||
if (sarg & TIOCM_DTR) {
|
||||
*targ |= CHR_TIOCM_DTR;
|
||||
}
|
||||
if (sarg & TIOCM_RTS) {
|
||||
*targ |= CHR_TIOCM_RTS;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CHR_IOCTL_SERIAL_SET_TIOCM:
|
||||
{
|
||||
int sarg = *(int *)arg;
|
||||
int targ = 0;
|
||||
ioctl(fioc->fd, TIOCMGET, &targ);
|
||||
targ &= ~(CHR_TIOCM_CTS | CHR_TIOCM_CAR | CHR_TIOCM_DSR
|
||||
| CHR_TIOCM_RI | CHR_TIOCM_DTR | CHR_TIOCM_RTS);
|
||||
if (sarg & CHR_TIOCM_CTS) {
|
||||
targ |= TIOCM_CTS;
|
||||
}
|
||||
if (sarg & CHR_TIOCM_CAR) {
|
||||
targ |= TIOCM_CAR;
|
||||
}
|
||||
if (sarg & CHR_TIOCM_DSR) {
|
||||
targ |= TIOCM_DSR;
|
||||
}
|
||||
if (sarg & CHR_TIOCM_RI) {
|
||||
targ |= TIOCM_RI;
|
||||
}
|
||||
if (sarg & CHR_TIOCM_DTR) {
|
||||
targ |= TIOCM_DTR;
|
||||
}
|
||||
if (sarg & CHR_TIOCM_RTS) {
|
||||
targ |= TIOCM_RTS;
|
||||
}
|
||||
ioctl(fioc->fd, TIOCMSET, &targ);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qmp_chardev_open_serial(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevHostdev *serial = backend->u.serial.data;
|
||||
int fd;
|
||||
|
||||
fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp);
|
||||
if (fd < 0) {
|
||||
return;
|
||||
}
|
||||
qemu_set_nonblock(fd);
|
||||
tty_serial_init(fd, 115200, 'N', 8, 1);
|
||||
|
||||
qemu_chr_open_fd(chr, fd, fd);
|
||||
}
|
||||
#endif /* __linux__ || __sun__ */
|
||||
|
||||
#ifdef HAVE_CHARDEV_SERIAL
|
||||
static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend,
|
||||
Error **errp)
|
||||
{
|
||||
const char *device = qemu_opt_get(opts, "path");
|
||||
ChardevHostdev *serial;
|
||||
|
||||
if (device == NULL) {
|
||||
error_setg(errp, "chardev: serial/tty: no device path given");
|
||||
return;
|
||||
}
|
||||
backend->type = CHARDEV_BACKEND_KIND_SERIAL;
|
||||
serial = backend->u.serial.data = g_new0(ChardevHostdev, 1);
|
||||
qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(serial));
|
||||
serial->device = g_strdup(device);
|
||||
}
|
||||
|
||||
static void char_serial_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_serial;
|
||||
cc->open = qmp_chardev_open_serial;
|
||||
#ifndef _WIN32
|
||||
cc->chr_ioctl = tty_serial_ioctl;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static const TypeInfo char_serial_type_info = {
|
||||
.name = TYPE_CHARDEV_SERIAL,
|
||||
#ifdef _WIN32
|
||||
.parent = TYPE_CHARDEV_WIN,
|
||||
#else
|
||||
.parent = TYPE_CHARDEV_FD,
|
||||
#endif
|
||||
.class_init = char_serial_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_serial_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
|
||||
#endif
|
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef CHAR_SERIAL_H
|
||||
#define CHAR_SERIAL_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#define HAVE_CHARDEV_SERIAL 1
|
||||
#elif defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
|
||||
|| defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
|
||||
|| defined(__GLIBC__)
|
||||
#define HAVE_CHARDEV_SERIAL 1
|
||||
#endif
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "char-win.h"
|
||||
#include "char-win-stdio.h"
|
||||
#else
|
||||
#include <termios.h>
|
||||
#include "char-fd.h"
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
/* init terminal so that we can grab keys */
|
||||
static struct termios oldtty;
|
||||
static int old_fd0_flags;
|
||||
static bool stdio_in_use;
|
||||
static bool stdio_allow_signal;
|
||||
static bool stdio_echo_state;
|
||||
|
||||
static void term_exit(void)
|
||||
{
|
||||
tcsetattr(0, TCSANOW, &oldtty);
|
||||
fcntl(0, F_SETFL, old_fd0_flags);
|
||||
}
|
||||
|
||||
static void qemu_chr_set_echo_stdio(Chardev *chr, bool echo)
|
||||
{
|
||||
struct termios tty;
|
||||
|
||||
stdio_echo_state = echo;
|
||||
tty = oldtty;
|
||||
if (!echo) {
|
||||
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
|
||||
| INLCR | IGNCR | ICRNL | IXON);
|
||||
tty.c_oflag |= OPOST;
|
||||
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN);
|
||||
tty.c_cflag &= ~(CSIZE | PARENB);
|
||||
tty.c_cflag |= CS8;
|
||||
tty.c_cc[VMIN] = 1;
|
||||
tty.c_cc[VTIME] = 0;
|
||||
}
|
||||
if (!stdio_allow_signal) {
|
||||
tty.c_lflag &= ~ISIG;
|
||||
}
|
||||
|
||||
tcsetattr(0, TCSANOW, &tty);
|
||||
}
|
||||
|
||||
static void term_stdio_handler(int sig)
|
||||
{
|
||||
/* restore echo after resume from suspend. */
|
||||
qemu_chr_set_echo_stdio(NULL, stdio_echo_state);
|
||||
}
|
||||
|
||||
static void qemu_chr_open_stdio(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevStdio *opts = backend->u.stdio.data;
|
||||
struct sigaction act;
|
||||
|
||||
if (is_daemonized()) {
|
||||
error_setg(errp, "cannot use stdio with -daemonize");
|
||||
return;
|
||||
}
|
||||
|
||||
if (stdio_in_use) {
|
||||
error_setg(errp, "cannot use stdio by multiple character devices");
|
||||
return;
|
||||
}
|
||||
|
||||
stdio_in_use = true;
|
||||
old_fd0_flags = fcntl(0, F_GETFL);
|
||||
tcgetattr(0, &oldtty);
|
||||
qemu_set_nonblock(0);
|
||||
atexit(term_exit);
|
||||
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_handler = term_stdio_handler;
|
||||
sigaction(SIGCONT, &act, NULL);
|
||||
|
||||
qemu_chr_open_fd(chr, 0, 1);
|
||||
|
||||
if (opts->has_signal) {
|
||||
stdio_allow_signal = opts->signal;
|
||||
}
|
||||
qemu_chr_set_echo_stdio(chr, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevStdio *stdio;
|
||||
|
||||
backend->type = CHARDEV_BACKEND_KIND_STDIO;
|
||||
stdio = backend->u.stdio.data = g_new0(ChardevStdio, 1);
|
||||
qemu_chr_parse_common(opts, qapi_ChardevStdio_base(stdio));
|
||||
stdio->has_signal = true;
|
||||
stdio->signal = qemu_opt_get_bool(opts, "signal", true);
|
||||
}
|
||||
|
||||
static void char_stdio_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_stdio;
|
||||
#ifndef _WIN32
|
||||
cc->open = qemu_chr_open_stdio;
|
||||
cc->chr_set_echo = qemu_chr_set_echo_stdio;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void char_stdio_finalize(Object *obj)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
term_exit();
|
||||
#endif
|
||||
}
|
||||
|
||||
static const TypeInfo char_stdio_type_info = {
|
||||
.name = TYPE_CHARDEV_STDIO,
|
||||
#ifdef _WIN32
|
||||
.parent = TYPE_CHARDEV_WIN_STDIO,
|
||||
#else
|
||||
.parent = TYPE_CHARDEV_FD,
|
||||
#endif
|
||||
.instance_finalize = char_stdio_finalize,
|
||||
.class_init = char_stdio_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_stdio_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
@@ -1,233 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "io/channel-socket.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
#include "char-io.h"
|
||||
|
||||
/***********************************************************/
|
||||
/* UDP Net console */
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
QIOChannel *ioc;
|
||||
uint8_t buf[CHR_READ_BUF_LEN];
|
||||
int bufcnt;
|
||||
int bufptr;
|
||||
int max_size;
|
||||
} UdpChardev;
|
||||
|
||||
#define UDP_CHARDEV(obj) OBJECT_CHECK(UdpChardev, (obj), TYPE_CHARDEV_UDP)
|
||||
|
||||
/* Called with chr_write_lock held. */
|
||||
static int udp_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
||||
{
|
||||
UdpChardev *s = UDP_CHARDEV(chr);
|
||||
|
||||
return qio_channel_write(
|
||||
s->ioc, (const char *)buf, len, NULL);
|
||||
}
|
||||
|
||||
static int udp_chr_read_poll(void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
UdpChardev *s = UDP_CHARDEV(opaque);
|
||||
|
||||
s->max_size = qemu_chr_be_can_write(chr);
|
||||
|
||||
/* If there were any stray characters in the queue process them
|
||||
* first
|
||||
*/
|
||||
while (s->max_size > 0 && s->bufptr < s->bufcnt) {
|
||||
qemu_chr_be_write(chr, &s->buf[s->bufptr], 1);
|
||||
s->bufptr++;
|
||||
s->max_size = qemu_chr_be_can_write(chr);
|
||||
}
|
||||
return s->max_size;
|
||||
}
|
||||
|
||||
static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
UdpChardev *s = UDP_CHARDEV(opaque);
|
||||
ssize_t ret;
|
||||
|
||||
if (s->max_size == 0) {
|
||||
return TRUE;
|
||||
}
|
||||
ret = qio_channel_read(
|
||||
s->ioc, (char *)s->buf, sizeof(s->buf), NULL);
|
||||
if (ret <= 0) {
|
||||
remove_fd_in_watch(chr);
|
||||
return FALSE;
|
||||
}
|
||||
s->bufcnt = ret;
|
||||
|
||||
s->bufptr = 0;
|
||||
while (s->max_size > 0 && s->bufptr < s->bufcnt) {
|
||||
qemu_chr_be_write(chr, &s->buf[s->bufptr], 1);
|
||||
s->bufptr++;
|
||||
s->max_size = qemu_chr_be_can_write(chr);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void udp_chr_update_read_handler(Chardev *chr,
|
||||
GMainContext *context)
|
||||
{
|
||||
UdpChardev *s = UDP_CHARDEV(chr);
|
||||
|
||||
remove_fd_in_watch(chr);
|
||||
if (s->ioc) {
|
||||
chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
|
||||
udp_chr_read_poll,
|
||||
udp_chr_read, chr,
|
||||
context);
|
||||
}
|
||||
}
|
||||
|
||||
static void char_udp_finalize(Object *obj)
|
||||
{
|
||||
Chardev *chr = CHARDEV(obj);
|
||||
UdpChardev *s = UDP_CHARDEV(obj);
|
||||
|
||||
remove_fd_in_watch(chr);
|
||||
if (s->ioc) {
|
||||
object_unref(OBJECT(s->ioc));
|
||||
}
|
||||
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
||||
}
|
||||
|
||||
static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
|
||||
Error **errp)
|
||||
{
|
||||
const char *host = qemu_opt_get(opts, "host");
|
||||
const char *port = qemu_opt_get(opts, "port");
|
||||
const char *localaddr = qemu_opt_get(opts, "localaddr");
|
||||
const char *localport = qemu_opt_get(opts, "localport");
|
||||
bool has_local = false;
|
||||
SocketAddress *addr;
|
||||
ChardevUdp *udp;
|
||||
|
||||
backend->type = CHARDEV_BACKEND_KIND_UDP;
|
||||
if (host == NULL || strlen(host) == 0) {
|
||||
host = "localhost";
|
||||
}
|
||||
if (port == NULL || strlen(port) == 0) {
|
||||
error_setg(errp, "chardev: udp: remote port not specified");
|
||||
return;
|
||||
}
|
||||
if (localport == NULL || strlen(localport) == 0) {
|
||||
localport = "0";
|
||||
} else {
|
||||
has_local = true;
|
||||
}
|
||||
if (localaddr == NULL || strlen(localaddr) == 0) {
|
||||
localaddr = "";
|
||||
} else {
|
||||
has_local = true;
|
||||
}
|
||||
|
||||
udp = backend->u.udp.data = g_new0(ChardevUdp, 1);
|
||||
qemu_chr_parse_common(opts, qapi_ChardevUdp_base(udp));
|
||||
|
||||
addr = g_new0(SocketAddress, 1);
|
||||
addr->type = SOCKET_ADDRESS_KIND_INET;
|
||||
addr->u.inet.data = g_new(InetSocketAddress, 1);
|
||||
*addr->u.inet.data = (InetSocketAddress) {
|
||||
.host = g_strdup(host),
|
||||
.port = g_strdup(port),
|
||||
.has_ipv4 = qemu_opt_get(opts, "ipv4"),
|
||||
.ipv4 = qemu_opt_get_bool(opts, "ipv4", 0),
|
||||
.has_ipv6 = qemu_opt_get(opts, "ipv6"),
|
||||
.ipv6 = qemu_opt_get_bool(opts, "ipv6", 0),
|
||||
};
|
||||
udp->remote = addr;
|
||||
|
||||
if (has_local) {
|
||||
udp->has_local = true;
|
||||
addr = g_new0(SocketAddress, 1);
|
||||
addr->type = SOCKET_ADDRESS_KIND_INET;
|
||||
addr->u.inet.data = g_new(InetSocketAddress, 1);
|
||||
*addr->u.inet.data = (InetSocketAddress) {
|
||||
.host = g_strdup(localaddr),
|
||||
.port = g_strdup(localport),
|
||||
};
|
||||
udp->local = addr;
|
||||
}
|
||||
}
|
||||
|
||||
static void qmp_chardev_open_udp(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
ChardevUdp *udp = backend->u.udp.data;
|
||||
QIOChannelSocket *sioc = qio_channel_socket_new();
|
||||
char *name;
|
||||
UdpChardev *s = UDP_CHARDEV(chr);
|
||||
|
||||
if (qio_channel_socket_dgram_sync(sioc,
|
||||
udp->local, udp->remote,
|
||||
errp) < 0) {
|
||||
object_unref(OBJECT(sioc));
|
||||
return;
|
||||
}
|
||||
|
||||
name = g_strdup_printf("chardev-udp-%s", chr->label);
|
||||
qio_channel_set_name(QIO_CHANNEL(sioc), name);
|
||||
g_free(name);
|
||||
|
||||
s->ioc = QIO_CHANNEL(sioc);
|
||||
/* be isn't opened until we get a connection */
|
||||
*be_opened = false;
|
||||
}
|
||||
|
||||
static void char_udp_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->parse = qemu_chr_parse_udp;
|
||||
cc->open = qmp_chardev_open_udp;
|
||||
cc->chr_write = udp_chr_write;
|
||||
cc->chr_update_read_handler = udp_chr_update_read_handler;
|
||||
}
|
||||
|
||||
static const TypeInfo char_udp_type_info = {
|
||||
.name = TYPE_CHARDEV_UDP,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.instance_size = sizeof(UdpChardev),
|
||||
.instance_finalize = char_udp_finalize,
|
||||
.class_init = char_udp_class_init,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_udp_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
@@ -1,266 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "char-win.h"
|
||||
#include "char-win-stdio.h"
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
HANDLE hStdIn;
|
||||
HANDLE hInputReadyEvent;
|
||||
HANDLE hInputDoneEvent;
|
||||
HANDLE hInputThread;
|
||||
uint8_t win_stdio_buf;
|
||||
} WinStdioChardev;
|
||||
|
||||
#define WIN_STDIO_CHARDEV(obj) \
|
||||
OBJECT_CHECK(WinStdioChardev, (obj), TYPE_CHARDEV_WIN_STDIO)
|
||||
|
||||
static void win_stdio_wait_func(void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque);
|
||||
INPUT_RECORD buf[4];
|
||||
int ret;
|
||||
DWORD dwSize;
|
||||
int i;
|
||||
|
||||
ret = ReadConsoleInput(stdio->hStdIn, buf, ARRAY_SIZE(buf), &dwSize);
|
||||
|
||||
if (!ret) {
|
||||
/* Avoid error storm */
|
||||
qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < dwSize; i++) {
|
||||
KEY_EVENT_RECORD *kev = &buf[i].Event.KeyEvent;
|
||||
|
||||
if (buf[i].EventType == KEY_EVENT && kev->bKeyDown) {
|
||||
int j;
|
||||
if (kev->uChar.AsciiChar != 0) {
|
||||
for (j = 0; j < kev->wRepeatCount; j++) {
|
||||
if (qemu_chr_be_can_write(chr)) {
|
||||
uint8_t c = kev->uChar.AsciiChar;
|
||||
qemu_chr_be_write(chr, &c, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static DWORD WINAPI win_stdio_thread(LPVOID param)
|
||||
{
|
||||
WinStdioChardev *stdio = WIN_STDIO_CHARDEV(param);
|
||||
int ret;
|
||||
DWORD dwSize;
|
||||
|
||||
while (1) {
|
||||
|
||||
/* Wait for one byte */
|
||||
ret = ReadFile(stdio->hStdIn, &stdio->win_stdio_buf, 1, &dwSize, NULL);
|
||||
|
||||
/* Exit in case of error, continue if nothing read */
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
if (!dwSize) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Some terminal emulator returns \r\n for Enter, just pass \n */
|
||||
if (stdio->win_stdio_buf == '\r') {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Signal the main thread and wait until the byte was eaten */
|
||||
if (!SetEvent(stdio->hInputReadyEvent)) {
|
||||
break;
|
||||
}
|
||||
if (WaitForSingleObject(stdio->hInputDoneEvent, INFINITE)
|
||||
!= WAIT_OBJECT_0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void win_stdio_thread_wait_func(void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque);
|
||||
|
||||
if (qemu_chr_be_can_write(chr)) {
|
||||
qemu_chr_be_write(chr, &stdio->win_stdio_buf, 1);
|
||||
}
|
||||
|
||||
SetEvent(stdio->hInputDoneEvent);
|
||||
}
|
||||
|
||||
static void qemu_chr_set_echo_win_stdio(Chardev *chr, bool echo)
|
||||
{
|
||||
WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
|
||||
DWORD dwMode = 0;
|
||||
|
||||
GetConsoleMode(stdio->hStdIn, &dwMode);
|
||||
|
||||
if (echo) {
|
||||
SetConsoleMode(stdio->hStdIn, dwMode | ENABLE_ECHO_INPUT);
|
||||
} else {
|
||||
SetConsoleMode(stdio->hStdIn, dwMode & ~ENABLE_ECHO_INPUT);
|
||||
}
|
||||
}
|
||||
|
||||
static void qemu_chr_open_stdio(Chardev *chr,
|
||||
ChardevBackend *backend,
|
||||
bool *be_opened,
|
||||
Error **errp)
|
||||
{
|
||||
WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
|
||||
DWORD dwMode;
|
||||
int is_console = 0;
|
||||
|
||||
stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
if (stdio->hStdIn == INVALID_HANDLE_VALUE) {
|
||||
error_setg(errp, "cannot open stdio: invalid handle");
|
||||
return;
|
||||
}
|
||||
|
||||
is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
|
||||
|
||||
if (is_console) {
|
||||
if (qemu_add_wait_object(stdio->hStdIn,
|
||||
win_stdio_wait_func, chr)) {
|
||||
error_setg(errp, "qemu_add_wait_object: failed");
|
||||
goto err1;
|
||||
}
|
||||
} else {
|
||||
DWORD dwId;
|
||||
|
||||
stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
stdio->hInputDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
if (stdio->hInputReadyEvent == INVALID_HANDLE_VALUE
|
||||
|| stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) {
|
||||
error_setg(errp, "cannot create event");
|
||||
goto err2;
|
||||
}
|
||||
if (qemu_add_wait_object(stdio->hInputReadyEvent,
|
||||
win_stdio_thread_wait_func, chr)) {
|
||||
error_setg(errp, "qemu_add_wait_object: failed");
|
||||
goto err2;
|
||||
}
|
||||
stdio->hInputThread = CreateThread(NULL, 0, win_stdio_thread,
|
||||
chr, 0, &dwId);
|
||||
|
||||
if (stdio->hInputThread == INVALID_HANDLE_VALUE) {
|
||||
error_setg(errp, "cannot create stdio thread");
|
||||
goto err3;
|
||||
}
|
||||
}
|
||||
|
||||
dwMode |= ENABLE_LINE_INPUT;
|
||||
|
||||
if (is_console) {
|
||||
/* set the terminal in raw mode */
|
||||
/* ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS */
|
||||
dwMode |= ENABLE_PROCESSED_INPUT;
|
||||
}
|
||||
|
||||
SetConsoleMode(stdio->hStdIn, dwMode);
|
||||
|
||||
qemu_chr_set_echo_win_stdio(chr, false);
|
||||
|
||||
return;
|
||||
|
||||
err3:
|
||||
qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
|
||||
err2:
|
||||
CloseHandle(stdio->hInputReadyEvent);
|
||||
CloseHandle(stdio->hInputDoneEvent);
|
||||
err1:
|
||||
qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
|
||||
}
|
||||
|
||||
static void char_win_stdio_finalize(Object *obj)
|
||||
{
|
||||
WinStdioChardev *stdio = WIN_STDIO_CHARDEV(obj);
|
||||
|
||||
if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(stdio->hInputReadyEvent);
|
||||
}
|
||||
if (stdio->hInputDoneEvent != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(stdio->hInputDoneEvent);
|
||||
}
|
||||
if (stdio->hInputThread != INVALID_HANDLE_VALUE) {
|
||||
TerminateThread(stdio->hInputThread, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int win_stdio_write(Chardev *chr, const uint8_t *buf, int len)
|
||||
{
|
||||
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD dwSize;
|
||||
int len1;
|
||||
|
||||
len1 = len;
|
||||
|
||||
while (len1 > 0) {
|
||||
if (!WriteFile(hStdOut, buf, len1, &dwSize, NULL)) {
|
||||
break;
|
||||
}
|
||||
buf += dwSize;
|
||||
len1 -= dwSize;
|
||||
}
|
||||
|
||||
return len - len1;
|
||||
}
|
||||
|
||||
static void char_win_stdio_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->open = qemu_chr_open_stdio;
|
||||
cc->chr_write = win_stdio_write;
|
||||
cc->chr_set_echo = qemu_chr_set_echo_win_stdio;
|
||||
}
|
||||
|
||||
static const TypeInfo char_win_stdio_type_info = {
|
||||
.name = TYPE_CHARDEV_WIN_STDIO,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.instance_size = sizeof(WinStdioChardev),
|
||||
.instance_finalize = char_win_stdio_finalize,
|
||||
.class_init = char_win_stdio_class_init,
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_win_stdio_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef CHAR_WIN_STDIO_H
|
||||
#define CHAR_WIN_STDIO_H
|
||||
|
||||
#define TYPE_CHARDEV_WIN_STDIO "chardev-win-stdio"
|
||||
|
||||
#endif /* CHAR_WIN_STDIO_H */
|
@@ -1,265 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qapi/error.h"
|
||||
#include "char-win.h"
|
||||
|
||||
static void win_chr_readfile(Chardev *chr)
|
||||
{
|
||||
WinChardev *s = WIN_CHARDEV(chr);
|
||||
|
||||
int ret, err;
|
||||
uint8_t buf[CHR_READ_BUF_LEN];
|
||||
DWORD size;
|
||||
|
||||
ZeroMemory(&s->orecv, sizeof(s->orecv));
|
||||
s->orecv.hEvent = s->hrecv;
|
||||
ret = ReadFile(s->hcom, buf, s->len, &size, &s->orecv);
|
||||
if (!ret) {
|
||||
err = GetLastError();
|
||||
if (err == ERROR_IO_PENDING) {
|
||||
ret = GetOverlappedResult(s->hcom, &s->orecv, &size, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
if (size > 0) {
|
||||
qemu_chr_be_write(chr, buf, size);
|
||||
}
|
||||
}
|
||||
|
||||
static void win_chr_read(Chardev *chr)
|
||||
{
|
||||
WinChardev *s = WIN_CHARDEV(chr);
|
||||
|
||||
if (s->len > s->max_size) {
|
||||
s->len = s->max_size;
|
||||
}
|
||||
if (s->len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
win_chr_readfile(chr);
|
||||
}
|
||||
|
||||
static int win_chr_read_poll(Chardev *chr)
|
||||
{
|
||||
WinChardev *s = WIN_CHARDEV(chr);
|
||||
|
||||
s->max_size = qemu_chr_be_can_write(chr);
|
||||
return s->max_size;
|
||||
}
|
||||
|
||||
static int win_chr_poll(void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
WinChardev *s = WIN_CHARDEV(opaque);
|
||||
COMSTAT status;
|
||||
DWORD comerr;
|
||||
|
||||
ClearCommError(s->hcom, &comerr, &status);
|
||||
if (status.cbInQue > 0) {
|
||||
s->len = status.cbInQue;
|
||||
win_chr_read_poll(chr);
|
||||
win_chr_read(chr);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int win_chr_init(Chardev *chr, const char *filename, Error **errp)
|
||||
{
|
||||
WinChardev *s = WIN_CHARDEV(chr);
|
||||
COMMCONFIG comcfg;
|
||||
COMMTIMEOUTS cto = { 0, 0, 0, 0, 0};
|
||||
COMSTAT comstat;
|
||||
DWORD size;
|
||||
DWORD err;
|
||||
|
||||
s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!s->hsend) {
|
||||
error_setg(errp, "Failed CreateEvent");
|
||||
goto fail;
|
||||
}
|
||||
s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!s->hrecv) {
|
||||
error_setg(errp, "Failed CreateEvent");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->hcom = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
|
||||
if (s->hcom == INVALID_HANDLE_VALUE) {
|
||||
error_setg(errp, "Failed CreateFile (%lu)", GetLastError());
|
||||
s->hcom = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!SetupComm(s->hcom, NRECVBUF, NSENDBUF)) {
|
||||
error_setg(errp, "Failed SetupComm");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ZeroMemory(&comcfg, sizeof(COMMCONFIG));
|
||||
size = sizeof(COMMCONFIG);
|
||||
GetDefaultCommConfig(filename, &comcfg, &size);
|
||||
comcfg.dcb.DCBlength = sizeof(DCB);
|
||||
CommConfigDialog(filename, NULL, &comcfg);
|
||||
|
||||
if (!SetCommState(s->hcom, &comcfg.dcb)) {
|
||||
error_setg(errp, "Failed SetCommState");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!SetCommMask(s->hcom, EV_ERR)) {
|
||||
error_setg(errp, "Failed SetCommMask");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cto.ReadIntervalTimeout = MAXDWORD;
|
||||
if (!SetCommTimeouts(s->hcom, &cto)) {
|
||||
error_setg(errp, "Failed SetCommTimeouts");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!ClearCommError(s->hcom, &err, &comstat)) {
|
||||
error_setg(errp, "Failed ClearCommError");
|
||||
goto fail;
|
||||
}
|
||||
qemu_add_polling_cb(win_chr_poll, chr);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int win_chr_pipe_poll(void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
WinChardev *s = WIN_CHARDEV(opaque);
|
||||
DWORD size;
|
||||
|
||||
PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL);
|
||||
if (size > 0) {
|
||||
s->len = size;
|
||||
win_chr_read_poll(chr);
|
||||
win_chr_read(chr);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called with chr_write_lock held. */
|
||||
static int win_chr_write(Chardev *chr, const uint8_t *buf, int len1)
|
||||
{
|
||||
WinChardev *s = WIN_CHARDEV(chr);
|
||||
DWORD len, ret, size, err;
|
||||
|
||||
len = len1;
|
||||
ZeroMemory(&s->osend, sizeof(s->osend));
|
||||
s->osend.hEvent = s->hsend;
|
||||
while (len > 0) {
|
||||
if (s->hsend) {
|
||||
ret = WriteFile(s->hcom, buf, len, &size, &s->osend);
|
||||
} else {
|
||||
ret = WriteFile(s->hcom, buf, len, &size, NULL);
|
||||
}
|
||||
if (!ret) {
|
||||
err = GetLastError();
|
||||
if (err == ERROR_IO_PENDING) {
|
||||
ret = GetOverlappedResult(s->hcom, &s->osend, &size, TRUE);
|
||||
if (ret) {
|
||||
buf += size;
|
||||
len -= size;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
buf += size;
|
||||
len -= size;
|
||||
}
|
||||
}
|
||||
return len1 - len;
|
||||
}
|
||||
|
||||
static void char_win_finalize(Object *obj)
|
||||
{
|
||||
Chardev *chr = CHARDEV(obj);
|
||||
WinChardev *s = WIN_CHARDEV(chr);
|
||||
|
||||
if (s->skip_free) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->hsend) {
|
||||
CloseHandle(s->hsend);
|
||||
}
|
||||
if (s->hrecv) {
|
||||
CloseHandle(s->hrecv);
|
||||
}
|
||||
if (s->hcom) {
|
||||
CloseHandle(s->hcom);
|
||||
}
|
||||
if (s->fpipe) {
|
||||
qemu_del_polling_cb(win_chr_pipe_poll, chr);
|
||||
} else {
|
||||
qemu_del_polling_cb(win_chr_poll, chr);
|
||||
}
|
||||
|
||||
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
||||
}
|
||||
|
||||
void qemu_chr_open_win_file(Chardev *chr, HANDLE fd_out)
|
||||
{
|
||||
WinChardev *s = WIN_CHARDEV(chr);
|
||||
|
||||
s->skip_free = true;
|
||||
s->hcom = fd_out;
|
||||
}
|
||||
|
||||
static void char_win_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->chr_write = win_chr_write;
|
||||
}
|
||||
|
||||
static const TypeInfo char_win_type_info = {
|
||||
.name = TYPE_CHARDEV_WIN,
|
||||
.parent = TYPE_CHARDEV,
|
||||
.instance_size = sizeof(WinChardev),
|
||||
.instance_finalize = char_win_finalize,
|
||||
.class_init = char_win_class_init,
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&char_win_type_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* QEMU System Emulator
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef CHAR_WIN_H
|
||||
#define CHAR_WIN_H
|
||||
|
||||
#include "sysemu/char.h"
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
int max_size;
|
||||
HANDLE hcom, hrecv, hsend;
|
||||
OVERLAPPED orecv;
|
||||
BOOL fpipe;
|
||||
DWORD len;
|
||||
|
||||
/* Protected by the Chardev chr_write_lock. */
|
||||
OVERLAPPED osend;
|
||||
/* FIXME: file/console do not finalize */
|
||||
bool skip_free;
|
||||
} WinChardev;
|
||||
|
||||
#define NSENDBUF 2048
|
||||
#define NRECVBUF 2048
|
||||
|
||||
#define TYPE_CHARDEV_WIN "chardev-win"
|
||||
#define WIN_CHARDEV(obj) OBJECT_CHECK(WinChardev, (obj), TYPE_CHARDEV_WIN)
|
||||
|
||||
void qemu_chr_open_win_file(Chardev *chr, HANDLE fd_out);
|
||||
int win_chr_init(Chardev *chr, const char *filename, Error **errp);
|
||||
int win_chr_pipe_poll(void *opaque);
|
||||
|
||||
#endif /* CHAR_WIN_H */
|
1334
chardev/char.c
1334
chardev/char.c
File diff suppressed because it is too large
Load Diff
68
configure
vendored
68
configure
vendored
@@ -28,6 +28,8 @@ TMPB="qemu-conf"
|
||||
TMPC="${TMPDIR1}/${TMPB}.c"
|
||||
TMPO="${TMPDIR1}/${TMPB}.o"
|
||||
TMPCXX="${TMPDIR1}/${TMPB}.cxx"
|
||||
TMPL="${TMPDIR1}/${TMPB}.lo"
|
||||
TMPA="${TMPDIR1}/lib${TMPB}.la"
|
||||
TMPE="${TMPDIR1}/${TMPB}.exe"
|
||||
TMPMO="${TMPDIR1}/${TMPB}.mo"
|
||||
|
||||
@@ -228,7 +230,7 @@ vhost_net="no"
|
||||
vhost_scsi="no"
|
||||
vhost_vsock="no"
|
||||
kvm="no"
|
||||
hax="no"
|
||||
colo="yes"
|
||||
rdma=""
|
||||
gprof="no"
|
||||
debug_tcg="no"
|
||||
@@ -311,7 +313,6 @@ gnutls_rnd=""
|
||||
nettle=""
|
||||
nettle_kdf="no"
|
||||
gcrypt=""
|
||||
gcrypt_hmac="no"
|
||||
gcrypt_kdf="no"
|
||||
vte=""
|
||||
virglrenderer=""
|
||||
@@ -562,7 +563,6 @@ CYGWIN*)
|
||||
;;
|
||||
MINGW32*)
|
||||
mingw32="yes"
|
||||
hax="yes"
|
||||
audio_possible_drivers="dsound sdl"
|
||||
if check_include dsound.h; then
|
||||
audio_drv_list="dsound"
|
||||
@@ -612,7 +612,6 @@ OpenBSD)
|
||||
Darwin)
|
||||
bsd="yes"
|
||||
darwin="yes"
|
||||
hax="yes"
|
||||
LDFLAGS_SHARED="-bundle -undefined dynamic_lookup"
|
||||
if [ "$cpu" = "x86_64" ] ; then
|
||||
QEMU_CFLAGS="-arch x86_64 $QEMU_CFLAGS"
|
||||
@@ -922,9 +921,9 @@ for opt do
|
||||
;;
|
||||
--enable-kvm) kvm="yes"
|
||||
;;
|
||||
--disable-hax) hax="no"
|
||||
--disable-colo) colo="no"
|
||||
;;
|
||||
--enable-hax) hax="yes"
|
||||
--enable-colo) colo="yes"
|
||||
;;
|
||||
--disable-tcg-interpreter) tcg_interpreter="no"
|
||||
;;
|
||||
@@ -1374,7 +1373,7 @@ disabled with --disable-FEATURE, default is enabled if available:
|
||||
fdt fdt device tree
|
||||
bluez bluez stack connectivity
|
||||
kvm KVM acceleration support
|
||||
hax HAX acceleration support
|
||||
colo COarse-grain LOck-stepping VM for Non-stop Service
|
||||
rdma RDMA-based migration support
|
||||
vde support for vde network
|
||||
netmap support for netmap network
|
||||
@@ -1474,7 +1473,7 @@ fi
|
||||
|
||||
gcc_flags="-Wold-style-declaration -Wold-style-definition -Wtype-limits"
|
||||
gcc_flags="-Wformat-security -Wformat-y2k -Winit-self -Wignored-qualifiers $gcc_flags"
|
||||
gcc_flags="-Wno-missing-include-dirs -Wempty-body -Wnested-externs $gcc_flags"
|
||||
gcc_flags="-Wmissing-include-dirs -Wempty-body -Wnested-externs $gcc_flags"
|
||||
gcc_flags="-Wendif-labels -Wno-shift-negative-value $gcc_flags"
|
||||
gcc_flags="-Wno-initializer-overrides $gcc_flags"
|
||||
gcc_flags="-Wno-string-plus-int $gcc_flags"
|
||||
@@ -2418,19 +2417,6 @@ EOF
|
||||
if compile_prog "$gcrypt_cflags" "$gcrypt_libs" ; then
|
||||
gcrypt_kdf=yes
|
||||
fi
|
||||
|
||||
cat > $TMPC << EOF
|
||||
#include <gcrypt.h>
|
||||
int main(void) {
|
||||
gcry_mac_hd_t handle;
|
||||
gcry_mac_open(&handle, GCRY_MAC_HMAC_MD5,
|
||||
GCRY_MAC_FLAG_SECURE, NULL);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if compile_prog "$gcrypt_cflags" "$gcrypt_libs" ; then
|
||||
gcrypt_hmac=yes
|
||||
fi
|
||||
else
|
||||
if test "$gcrypt" = "yes"; then
|
||||
feature_not_found "gcrypt" "Install gcrypt devel"
|
||||
@@ -2752,7 +2738,7 @@ if compile_prog "" "" ; then
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# xfsctl() probe, used for file-posix.c
|
||||
# xfsctl() probe, used for raw-posix
|
||||
if test "$xfs" != "no" ; then
|
||||
cat > $TMPC << EOF
|
||||
#include <stddef.h> /* NULL */
|
||||
@@ -3079,7 +3065,7 @@ fi
|
||||
|
||||
# g_test_trap_subprocess added in 2.38. Used by some tests.
|
||||
glib_subprocess=yes
|
||||
if ! $pkg_config --atleast-version=2.38 glib-2.0; then
|
||||
if test "$mingw32" = "yes" || ! $pkg_config --atleast-version=2.38 glib-2.0; then
|
||||
glib_subprocess=no
|
||||
fi
|
||||
|
||||
@@ -5064,7 +5050,7 @@ echo "Linux AIO support $linux_aio"
|
||||
echo "ATTR/XATTR support $attr"
|
||||
echo "Install blobs $blobs"
|
||||
echo "KVM support $kvm"
|
||||
echo "HAX support $hax"
|
||||
echo "COLO support $colo"
|
||||
echo "RDMA support $rdma"
|
||||
echo "TCG interpreter $tcg_interpreter"
|
||||
echo "fdt support $fdt"
|
||||
@@ -5401,9 +5387,6 @@ if test "$gnutls_rnd" = "yes" ; then
|
||||
fi
|
||||
if test "$gcrypt" = "yes" ; then
|
||||
echo "CONFIG_GCRYPT=y" >> $config_host_mak
|
||||
if test "$gcrypt_hmac" = "yes" ; then
|
||||
echo "CONFIG_GCRYPT_HMAC=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$gcrypt_kdf" = "yes" ; then
|
||||
echo "CONFIG_GCRYPT_KDF=y" >> $config_host_mak
|
||||
fi
|
||||
@@ -5703,6 +5686,10 @@ if have_backend "syslog"; then
|
||||
fi
|
||||
echo "CONFIG_TRACE_FILE=$trace_file" >> $config_host_mak
|
||||
|
||||
if test "$colo" = "yes"; then
|
||||
echo "CONFIG_COLO=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$rdma" = "yes" ; then
|
||||
echo "CONFIG_RDMA=y" >> $config_host_mak
|
||||
fi
|
||||
@@ -5843,7 +5830,7 @@ target_name=$(echo $target | cut -d '-' -f 1)
|
||||
target_bigendian="no"
|
||||
|
||||
case "$target_name" in
|
||||
armeb|hppa|lm32|m68k|microblaze|mips|mipsn32|mips64|moxie|or1k|ppc|ppcemb|ppc64|ppc64abi32|s390x|sh4eb|sparc|sparc64|sparc32plus|xtensaeb)
|
||||
armeb|lm32|m68k|microblaze|mips|mipsn32|mips64|moxie|or32|ppc|ppcemb|ppc64|ppc64abi32|s390x|sh4eb|sparc|sparc64|sparc32plus|xtensaeb)
|
||||
target_bigendian=yes
|
||||
;;
|
||||
esac
|
||||
@@ -5906,8 +5893,6 @@ case "$target_name" in
|
||||
;;
|
||||
cris)
|
||||
;;
|
||||
hppa)
|
||||
;;
|
||||
lm32)
|
||||
;;
|
||||
m68k)
|
||||
@@ -5935,9 +5920,7 @@ case "$target_name" in
|
||||
;;
|
||||
moxie)
|
||||
;;
|
||||
nios2)
|
||||
;;
|
||||
or1k)
|
||||
or32)
|
||||
TARGET_ARCH=openrisc
|
||||
TARGET_BASE_ARCH=openrisc
|
||||
;;
|
||||
@@ -6052,15 +6035,6 @@ case "$target_name" in
|
||||
fi
|
||||
fi
|
||||
esac
|
||||
if test "$hax" = "yes" ; then
|
||||
if test "$target_softmmu" = "yes" ; then
|
||||
case "$target_name" in
|
||||
i386|x86_64)
|
||||
echo "CONFIG_HAX=y" >> $config_target_mak
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
if test "$target_bigendian" = "yes" ; then
|
||||
echo "TARGET_WORDS_BIGENDIAN=y" >> $config_target_mak
|
||||
fi
|
||||
@@ -6118,9 +6092,6 @@ for i in $ARCH $TARGET_BASE_ARCH ; do
|
||||
cris)
|
||||
disas_config "CRIS"
|
||||
;;
|
||||
hppa)
|
||||
disas_config "HPPA"
|
||||
;;
|
||||
i386|x86_64|x32)
|
||||
disas_config "I386"
|
||||
;;
|
||||
@@ -6142,10 +6113,7 @@ for i in $ARCH $TARGET_BASE_ARCH ; do
|
||||
moxie*)
|
||||
disas_config "MOXIE"
|
||||
;;
|
||||
nios2)
|
||||
disas_config "NIOS2"
|
||||
;;
|
||||
or1k)
|
||||
or32)
|
||||
disas_config "OPENRISC"
|
||||
;;
|
||||
ppc*)
|
||||
@@ -6215,7 +6183,7 @@ fi
|
||||
|
||||
# build tree in object directory in case the source is not in the current directory
|
||||
DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests"
|
||||
DIRS="$DIRS docs fsdev"
|
||||
DIRS="$DIRS fsdev"
|
||||
DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw"
|
||||
DIRS="$DIRS roms/seabios roms/vgabios"
|
||||
DIRS="$DIRS qapi-generated"
|
||||
|
@@ -1 +0,0 @@
|
||||
libvhost-user-obj-y = libvhost-user.o
|
File diff suppressed because it is too large
Load Diff
@@ -1,435 +0,0 @@
|
||||
/*
|
||||
* Vhost User library
|
||||
*
|
||||
* Copyright (c) 2016 Red Hat, Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Victor Kaplansky <victork@redhat.com>
|
||||
* Marc-André Lureau <mlureau@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* later. See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef LIBVHOST_USER_H
|
||||
#define LIBVHOST_USER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <linux/vhost.h>
|
||||
#include "standard-headers/linux/virtio_ring.h"
|
||||
|
||||
/* Based on qemu/hw/virtio/vhost-user.c */
|
||||
#define VHOST_USER_F_PROTOCOL_FEATURES 30
|
||||
#define VHOST_LOG_PAGE 4096
|
||||
|
||||
#define VHOST_MAX_NR_VIRTQUEUE 8
|
||||
#define VIRTQUEUE_MAX_SIZE 1024
|
||||
|
||||
#define VHOST_MEMORY_MAX_NREGIONS 8
|
||||
|
||||
enum VhostUserProtocolFeature {
|
||||
VHOST_USER_PROTOCOL_F_MQ = 0,
|
||||
VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1,
|
||||
VHOST_USER_PROTOCOL_F_RARP = 2,
|
||||
|
||||
VHOST_USER_PROTOCOL_F_MAX
|
||||
};
|
||||
|
||||
#define VHOST_USER_PROTOCOL_FEATURE_MASK ((1 << VHOST_USER_PROTOCOL_F_MAX) - 1)
|
||||
|
||||
typedef enum VhostUserRequest {
|
||||
VHOST_USER_NONE = 0,
|
||||
VHOST_USER_GET_FEATURES = 1,
|
||||
VHOST_USER_SET_FEATURES = 2,
|
||||
VHOST_USER_SET_OWNER = 3,
|
||||
VHOST_USER_RESET_OWNER = 4,
|
||||
VHOST_USER_SET_MEM_TABLE = 5,
|
||||
VHOST_USER_SET_LOG_BASE = 6,
|
||||
VHOST_USER_SET_LOG_FD = 7,
|
||||
VHOST_USER_SET_VRING_NUM = 8,
|
||||
VHOST_USER_SET_VRING_ADDR = 9,
|
||||
VHOST_USER_SET_VRING_BASE = 10,
|
||||
VHOST_USER_GET_VRING_BASE = 11,
|
||||
VHOST_USER_SET_VRING_KICK = 12,
|
||||
VHOST_USER_SET_VRING_CALL = 13,
|
||||
VHOST_USER_SET_VRING_ERR = 14,
|
||||
VHOST_USER_GET_PROTOCOL_FEATURES = 15,
|
||||
VHOST_USER_SET_PROTOCOL_FEATURES = 16,
|
||||
VHOST_USER_GET_QUEUE_NUM = 17,
|
||||
VHOST_USER_SET_VRING_ENABLE = 18,
|
||||
VHOST_USER_SEND_RARP = 19,
|
||||
VHOST_USER_INPUT_GET_CONFIG = 20,
|
||||
VHOST_USER_MAX
|
||||
} VhostUserRequest;
|
||||
|
||||
typedef struct VhostUserMemoryRegion {
|
||||
uint64_t guest_phys_addr;
|
||||
uint64_t memory_size;
|
||||
uint64_t userspace_addr;
|
||||
uint64_t mmap_offset;
|
||||
} VhostUserMemoryRegion;
|
||||
|
||||
typedef struct VhostUserMemory {
|
||||
uint32_t nregions;
|
||||
uint32_t padding;
|
||||
VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS];
|
||||
} VhostUserMemory;
|
||||
|
||||
typedef struct VhostUserLog {
|
||||
uint64_t mmap_size;
|
||||
uint64_t mmap_offset;
|
||||
} VhostUserLog;
|
||||
|
||||
#if defined(_WIN32)
|
||||
# define VU_PACKED __attribute__((gcc_struct, packed))
|
||||
#else
|
||||
# define VU_PACKED __attribute__((packed))
|
||||
#endif
|
||||
|
||||
typedef struct VhostUserMsg {
|
||||
VhostUserRequest request;
|
||||
|
||||
#define VHOST_USER_VERSION_MASK (0x3)
|
||||
#define VHOST_USER_REPLY_MASK (0x1 << 2)
|
||||
uint32_t flags;
|
||||
uint32_t size; /* the following payload size */
|
||||
|
||||
union {
|
||||
#define VHOST_USER_VRING_IDX_MASK (0xff)
|
||||
#define VHOST_USER_VRING_NOFD_MASK (0x1 << 8)
|
||||
uint64_t u64;
|
||||
struct vhost_vring_state state;
|
||||
struct vhost_vring_addr addr;
|
||||
VhostUserMemory memory;
|
||||
VhostUserLog log;
|
||||
} payload;
|
||||
|
||||
int fds[VHOST_MEMORY_MAX_NREGIONS];
|
||||
int fd_num;
|
||||
uint8_t *data;
|
||||
} VU_PACKED VhostUserMsg;
|
||||
|
||||
typedef struct VuDevRegion {
|
||||
/* Guest Physical address. */
|
||||
uint64_t gpa;
|
||||
/* Memory region size. */
|
||||
uint64_t size;
|
||||
/* QEMU virtual address (userspace). */
|
||||
uint64_t qva;
|
||||
/* Starting offset in our mmaped space. */
|
||||
uint64_t mmap_offset;
|
||||
/* Start address of mmaped space. */
|
||||
uint64_t mmap_addr;
|
||||
} VuDevRegion;
|
||||
|
||||
typedef struct VuDev VuDev;
|
||||
|
||||
typedef uint64_t (*vu_get_features_cb) (VuDev *dev);
|
||||
typedef void (*vu_set_features_cb) (VuDev *dev, uint64_t features);
|
||||
typedef int (*vu_process_msg_cb) (VuDev *dev, VhostUserMsg *vmsg,
|
||||
int *do_reply);
|
||||
typedef void (*vu_queue_set_started_cb) (VuDev *dev, int qidx, bool started);
|
||||
|
||||
typedef struct VuDevIface {
|
||||
/* called by VHOST_USER_GET_FEATURES to get the features bitmask */
|
||||
vu_get_features_cb get_features;
|
||||
/* enable vhost implementation features */
|
||||
vu_set_features_cb set_features;
|
||||
/* get the protocol feature bitmask from the underlying vhost
|
||||
* implementation */
|
||||
vu_get_features_cb get_protocol_features;
|
||||
/* enable protocol features in the underlying vhost implementation. */
|
||||
vu_set_features_cb set_protocol_features;
|
||||
/* process_msg is called for each vhost-user message received */
|
||||
/* skip libvhost-user processing if return value != 0 */
|
||||
vu_process_msg_cb process_msg;
|
||||
/* tells when queues can be processed */
|
||||
vu_queue_set_started_cb queue_set_started;
|
||||
} VuDevIface;
|
||||
|
||||
typedef void (*vu_queue_handler_cb) (VuDev *dev, int qidx);
|
||||
|
||||
typedef struct VuRing {
|
||||
unsigned int num;
|
||||
struct vring_desc *desc;
|
||||
struct vring_avail *avail;
|
||||
struct vring_used *used;
|
||||
uint64_t log_guest_addr;
|
||||
uint32_t flags;
|
||||
} VuRing;
|
||||
|
||||
typedef struct VuVirtq {
|
||||
VuRing vring;
|
||||
|
||||
/* Next head to pop */
|
||||
uint16_t last_avail_idx;
|
||||
|
||||
/* Last avail_idx read from VQ. */
|
||||
uint16_t shadow_avail_idx;
|
||||
|
||||
uint16_t used_idx;
|
||||
|
||||
/* Last used index value we have signalled on */
|
||||
uint16_t signalled_used;
|
||||
|
||||
/* Last used index value we have signalled on */
|
||||
bool signalled_used_valid;
|
||||
|
||||
/* Notification enabled? */
|
||||
bool notification;
|
||||
|
||||
int inuse;
|
||||
|
||||
vu_queue_handler_cb handler;
|
||||
|
||||
int call_fd;
|
||||
int kick_fd;
|
||||
int err_fd;
|
||||
unsigned int enable;
|
||||
bool started;
|
||||
} VuVirtq;
|
||||
|
||||
enum VuWatchCondtion {
|
||||
VU_WATCH_IN = 1 << 0,
|
||||
VU_WATCH_OUT = 1 << 1,
|
||||
VU_WATCH_PRI = 1 << 2,
|
||||
VU_WATCH_ERR = 1 << 3,
|
||||
VU_WATCH_HUP = 1 << 4,
|
||||
};
|
||||
|
||||
typedef void (*vu_panic_cb) (VuDev *dev, const char *err);
|
||||
typedef void (*vu_watch_cb) (VuDev *dev, int condition, void *data);
|
||||
typedef void (*vu_set_watch_cb) (VuDev *dev, int fd, int condition,
|
||||
vu_watch_cb cb, void *data);
|
||||
typedef void (*vu_remove_watch_cb) (VuDev *dev, int fd);
|
||||
|
||||
struct VuDev {
|
||||
int sock;
|
||||
uint32_t nregions;
|
||||
VuDevRegion regions[VHOST_MEMORY_MAX_NREGIONS];
|
||||
VuVirtq vq[VHOST_MAX_NR_VIRTQUEUE];
|
||||
int log_call_fd;
|
||||
uint64_t log_size;
|
||||
uint8_t *log_table;
|
||||
uint64_t features;
|
||||
uint64_t protocol_features;
|
||||
bool broken;
|
||||
|
||||
/* @set_watch: add or update the given fd to the watch set,
|
||||
* call cb when condition is met */
|
||||
vu_set_watch_cb set_watch;
|
||||
|
||||
/* @remove_watch: remove the given fd from the watch set */
|
||||
vu_remove_watch_cb remove_watch;
|
||||
|
||||
/* @panic: encountered an unrecoverable error, you may try to
|
||||
* re-initialize */
|
||||
vu_panic_cb panic;
|
||||
const VuDevIface *iface;
|
||||
};
|
||||
|
||||
typedef struct VuVirtqElement {
|
||||
unsigned int index;
|
||||
unsigned int out_num;
|
||||
unsigned int in_num;
|
||||
struct iovec *in_sg;
|
||||
struct iovec *out_sg;
|
||||
} VuVirtqElement;
|
||||
|
||||
/**
|
||||
* vu_init:
|
||||
* @dev: a VuDev context
|
||||
* @socket: the socket connected to vhost-user master
|
||||
* @panic: a panic callback
|
||||
* @set_watch: a set_watch callback
|
||||
* @remove_watch: a remove_watch callback
|
||||
* @iface: a VuDevIface structure with vhost-user device callbacks
|
||||
*
|
||||
* Intializes a VuDev vhost-user context.
|
||||
**/
|
||||
void vu_init(VuDev *dev,
|
||||
int socket,
|
||||
vu_panic_cb panic,
|
||||
vu_set_watch_cb set_watch,
|
||||
vu_remove_watch_cb remove_watch,
|
||||
const VuDevIface *iface);
|
||||
|
||||
|
||||
/**
|
||||
* vu_deinit:
|
||||
* @dev: a VuDev context
|
||||
*
|
||||
* Cleans up the VuDev context
|
||||
*/
|
||||
void vu_deinit(VuDev *dev);
|
||||
|
||||
/**
|
||||
* vu_dispatch:
|
||||
* @dev: a VuDev context
|
||||
*
|
||||
* Process one vhost-user message.
|
||||
*
|
||||
* Returns: TRUE on success, FALSE on failure.
|
||||
*/
|
||||
bool vu_dispatch(VuDev *dev);
|
||||
|
||||
/**
|
||||
* vu_gpa_to_va:
|
||||
* @dev: a VuDev context
|
||||
* @guest_addr: guest address
|
||||
*
|
||||
* Translate a guest address to a pointer. Returns NULL on failure.
|
||||
*/
|
||||
void *vu_gpa_to_va(VuDev *dev, uint64_t guest_addr);
|
||||
|
||||
/**
|
||||
* vu_get_queue:
|
||||
* @dev: a VuDev context
|
||||
* @qidx: queue index
|
||||
*
|
||||
* Returns the queue number @qidx.
|
||||
*/
|
||||
VuVirtq *vu_get_queue(VuDev *dev, int qidx);
|
||||
|
||||
/**
|
||||
* vu_set_queue_handler:
|
||||
* @dev: a VuDev context
|
||||
* @vq: a VuVirtq queue
|
||||
* @handler: the queue handler callback
|
||||
*
|
||||
* Set the queue handler. This function may be called several times
|
||||
* for the same queue. If called with NULL @handler, the handler is
|
||||
* removed.
|
||||
*/
|
||||
void vu_set_queue_handler(VuDev *dev, VuVirtq *vq,
|
||||
vu_queue_handler_cb handler);
|
||||
|
||||
|
||||
/**
|
||||
* vu_queue_set_notification:
|
||||
* @dev: a VuDev context
|
||||
* @vq: a VuVirtq queue
|
||||
* @enable: state
|
||||
*
|
||||
* Set whether the queue notifies (via event index or interrupt)
|
||||
*/
|
||||
void vu_queue_set_notification(VuDev *dev, VuVirtq *vq, int enable);
|
||||
|
||||
/**
|
||||
* vu_queue_enabled:
|
||||
* @dev: a VuDev context
|
||||
* @vq: a VuVirtq queue
|
||||
*
|
||||
* Returns: whether the queue is enabled.
|
||||
*/
|
||||
bool vu_queue_enabled(VuDev *dev, VuVirtq *vq);
|
||||
|
||||
/**
|
||||
* vu_queue_enabled:
|
||||
* @dev: a VuDev context
|
||||
* @vq: a VuVirtq queue
|
||||
*
|
||||
* Returns: whether the queue is empty.
|
||||
*/
|
||||
int vu_queue_empty(VuDev *dev, VuVirtq *vq);
|
||||
|
||||
/**
|
||||
* vu_queue_notify:
|
||||
* @dev: a VuDev context
|
||||
* @vq: a VuVirtq queue
|
||||
*
|
||||
* Request to notify the queue via callfd (skipped if unnecessary)
|
||||
*/
|
||||
void vu_queue_notify(VuDev *dev, VuVirtq *vq);
|
||||
|
||||
/**
|
||||
* vu_queue_pop:
|
||||
* @dev: a VuDev context
|
||||
* @vq: a VuVirtq queue
|
||||
* @sz: the size of struct to return (must be >= VuVirtqElement)
|
||||
*
|
||||
* Returns: a VuVirtqElement filled from the queue or NULL.
|
||||
*/
|
||||
void *vu_queue_pop(VuDev *dev, VuVirtq *vq, size_t sz);
|
||||
|
||||
/**
|
||||
* vu_queue_rewind:
|
||||
* @dev: a VuDev context
|
||||
* @vq: a VuVirtq queue
|
||||
* @num: number of elements to push back
|
||||
*
|
||||
* Pretend that elements weren't popped from the virtqueue. The next
|
||||
* virtqueue_pop() will refetch the oldest element.
|
||||
*
|
||||
* Returns: true on success, false if @num is greater than the number of in use
|
||||
* elements.
|
||||
*/
|
||||
bool vu_queue_rewind(VuDev *dev, VuVirtq *vq, unsigned int num);
|
||||
|
||||
/**
|
||||
* vu_queue_fill:
|
||||
* @dev: a VuDev context
|
||||
* @vq: a VuVirtq queue
|
||||
* @elem: a VuVirtqElement
|
||||
* @len: length in bytes to write
|
||||
* @idx: optional offset for the used ring index (0 in general)
|
||||
*
|
||||
* Fill the used ring with @elem element.
|
||||
*/
|
||||
void vu_queue_fill(VuDev *dev, VuVirtq *vq,
|
||||
const VuVirtqElement *elem,
|
||||
unsigned int len, unsigned int idx);
|
||||
|
||||
/**
|
||||
* vu_queue_push:
|
||||
* @dev: a VuDev context
|
||||
* @vq: a VuVirtq queue
|
||||
* @elem: a VuVirtqElement
|
||||
* @len: length in bytes to write
|
||||
*
|
||||
* Helper that combines vu_queue_fill() with a vu_queue_flush().
|
||||
*/
|
||||
void vu_queue_push(VuDev *dev, VuVirtq *vq,
|
||||
const VuVirtqElement *elem, unsigned int len);
|
||||
|
||||
/**
|
||||
* vu_queue_flush:
|
||||
* @dev: a VuDev context
|
||||
* @vq: a VuVirtq queue
|
||||
* @num: number of elements to flush
|
||||
*
|
||||
* Mark the last number of elements as done (used.idx is updated by
|
||||
* num elements).
|
||||
*/
|
||||
void vu_queue_flush(VuDev *dev, VuVirtq *vq, unsigned int num);
|
||||
|
||||
/**
|
||||
* vu_queue_get_avail_bytes:
|
||||
* @dev: a VuDev context
|
||||
* @vq: a VuVirtq queue
|
||||
* @in_bytes: in bytes
|
||||
* @out_bytes: out bytes
|
||||
* @max_in_bytes: stop counting after max_in_bytes
|
||||
* @max_out_bytes: stop counting after max_out_bytes
|
||||
*
|
||||
* Count the number of available bytes, up to max_in_bytes/max_out_bytes.
|
||||
*/
|
||||
void vu_queue_get_avail_bytes(VuDev *vdev, VuVirtq *vq, unsigned int *in_bytes,
|
||||
unsigned int *out_bytes,
|
||||
unsigned max_in_bytes, unsigned max_out_bytes);
|
||||
|
||||
/**
|
||||
* vu_queue_avail_bytes:
|
||||
* @dev: a VuDev context
|
||||
* @vq: a VuVirtq queue
|
||||
* @in_bytes: expected in bytes
|
||||
* @out_bytes: expected out bytes
|
||||
*
|
||||
* Returns: true if in_bytes <= in_total && out_bytes <= out_total
|
||||
*/
|
||||
bool vu_queue_avail_bytes(VuDev *dev, VuVirtq *vq, unsigned int in_bytes,
|
||||
unsigned int out_bytes);
|
||||
|
||||
#endif /* LIBVHOST_USER_H */
|
81
cpu-exec.c
81
cpu-exec.c
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "trace-root.h"
|
||||
#include "trace.h"
|
||||
#include "disas/disas.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "tcg.h"
|
||||
@@ -461,7 +461,7 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
static inline void cpu_handle_interrupt(CPUState *cpu,
|
||||
TranslationBlock **last_tb)
|
||||
{
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
@@ -475,7 +475,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
if (interrupt_request & CPU_INTERRUPT_DEBUG) {
|
||||
cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
|
||||
cpu->exception_index = EXCP_DEBUG;
|
||||
return true;
|
||||
cpu_loop_exit(cpu);
|
||||
}
|
||||
if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) {
|
||||
/* Do nothing */
|
||||
@@ -484,7 +484,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
cpu->interrupt_request &= ~CPU_INTERRUPT_HALT;
|
||||
cpu->halted = 1;
|
||||
cpu->exception_index = EXCP_HLT;
|
||||
return true;
|
||||
cpu_loop_exit(cpu);
|
||||
}
|
||||
#if defined(TARGET_I386)
|
||||
else if (interrupt_request & CPU_INTERRUPT_INIT) {
|
||||
@@ -494,13 +494,13 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0, 0);
|
||||
do_cpu_init(x86_cpu);
|
||||
cpu->exception_index = EXCP_HALTED;
|
||||
return true;
|
||||
cpu_loop_exit(cpu);
|
||||
}
|
||||
#else
|
||||
else if (interrupt_request & CPU_INTERRUPT_RESET) {
|
||||
replay_interrupt();
|
||||
cpu_reset(cpu);
|
||||
return true;
|
||||
cpu_loop_exit(cpu);
|
||||
}
|
||||
#endif
|
||||
/* The target hook has 3 exit conditions:
|
||||
@@ -508,8 +508,8 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
True when it is, and we should restart on a new TB,
|
||||
and via longjmp via cpu_loop_exit. */
|
||||
else {
|
||||
replay_interrupt();
|
||||
if (cc->cpu_exec_interrupt(cpu, interrupt_request)) {
|
||||
replay_interrupt();
|
||||
*last_tb = NULL;
|
||||
}
|
||||
/* The target hook may have updated the 'cpu->interrupt_request';
|
||||
@@ -526,10 +526,8 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
if (unlikely(atomic_read(&cpu->exit_request) || replay_has_interrupt())) {
|
||||
atomic_set(&cpu->exit_request, 0);
|
||||
cpu->exception_index = EXCP_INTERRUPT;
|
||||
return true;
|
||||
cpu_loop_exit(cpu);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
|
||||
@@ -554,11 +552,11 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
|
||||
* have set something else (eg exit_request or
|
||||
* interrupt_request) which we will handle
|
||||
* next time around the loop. But we need to
|
||||
* ensure the zeroing of tcg_exit_req (see cpu_tb_exec)
|
||||
* ensure the tcg_exit_req read in generated code
|
||||
* comes before the next read of cpu->exit_request
|
||||
* or cpu->interrupt_request.
|
||||
*/
|
||||
smp_mb();
|
||||
smp_rmb();
|
||||
*last_tb = NULL;
|
||||
break;
|
||||
case TB_EXIT_ICOUNT_EXPIRED:
|
||||
@@ -624,37 +622,42 @@ int cpu_exec(CPUState *cpu)
|
||||
*/
|
||||
init_delay_params(&sc, cpu);
|
||||
|
||||
/* prepare setjmp context for exception handling */
|
||||
if (sigsetjmp(cpu->jmp_env, 0) != 0) {
|
||||
for(;;) {
|
||||
/* prepare setjmp context for exception handling */
|
||||
if (sigsetjmp(cpu->jmp_env, 0) == 0) {
|
||||
TranslationBlock *tb, *last_tb = NULL;
|
||||
int tb_exit = 0;
|
||||
|
||||
/* if an exception is pending, we execute it here */
|
||||
if (cpu_handle_exception(cpu, &ret)) {
|
||||
break;
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
cpu_handle_interrupt(cpu, &last_tb);
|
||||
tb = tb_find(cpu, last_tb, tb_exit);
|
||||
cpu_loop_exec_tb(cpu, tb, &last_tb, &tb_exit, &sc);
|
||||
/* Try to align the host and virtual clocks
|
||||
if the guest is in advance */
|
||||
align_clocks(&sc, cpu);
|
||||
} /* for(;;) */
|
||||
} else {
|
||||
#if defined(__clang__) || !QEMU_GNUC_PREREQ(4, 6)
|
||||
/* Some compilers wrongly smash all local variables after
|
||||
* siglongjmp. There were bug reports for gcc 4.5.0 and clang.
|
||||
* Reload essential local variables here for those compilers.
|
||||
* Newer versions of gcc would complain about this code (-Wclobbered). */
|
||||
cpu = current_cpu;
|
||||
cc = CPU_GET_CLASS(cpu);
|
||||
/* Some compilers wrongly smash all local variables after
|
||||
* siglongjmp. There were bug reports for gcc 4.5.0 and clang.
|
||||
* Reload essential local variables here for those compilers.
|
||||
* Newer versions of gcc would complain about this code (-Wclobbered). */
|
||||
cpu = current_cpu;
|
||||
cc = CPU_GET_CLASS(cpu);
|
||||
#else /* buggy compiler */
|
||||
/* Assert that the compiler does not smash local variables. */
|
||||
g_assert(cpu == current_cpu);
|
||||
g_assert(cc == CPU_GET_CLASS(cpu));
|
||||
/* Assert that the compiler does not smash local variables. */
|
||||
g_assert(cpu == current_cpu);
|
||||
g_assert(cc == CPU_GET_CLASS(cpu));
|
||||
#endif /* buggy compiler */
|
||||
cpu->can_do_io = 1;
|
||||
tb_lock_reset();
|
||||
}
|
||||
|
||||
/* if an exception is pending, we execute it here */
|
||||
while (!cpu_handle_exception(cpu, &ret)) {
|
||||
TranslationBlock *last_tb = NULL;
|
||||
int tb_exit = 0;
|
||||
|
||||
while (!cpu_handle_interrupt(cpu, &last_tb)) {
|
||||
TranslationBlock *tb = tb_find(cpu, last_tb, tb_exit);
|
||||
cpu_loop_exec_tb(cpu, tb, &last_tb, &tb_exit, &sc);
|
||||
/* Try to align the host and virtual clocks
|
||||
if the guest is in advance */
|
||||
align_clocks(&sc, cpu);
|
||||
cpu->can_do_io = 1;
|
||||
tb_lock_reset();
|
||||
}
|
||||
}
|
||||
} /* for(;;) */
|
||||
|
||||
cc->cpu_exec_exit(cpu);
|
||||
rcu_read_unlock();
|
||||
|
121
cpus.c
121
cpus.c
@@ -33,9 +33,7 @@
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "exec/gdbstub.h"
|
||||
#include "sysemu/dma.h"
|
||||
#include "sysemu/hw_accel.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/hax.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "exec/exec-all.h"
|
||||
|
||||
@@ -1222,46 +1220,6 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *qemu_hax_cpu_thread_fn(void *arg)
|
||||
{
|
||||
CPUState *cpu = arg;
|
||||
int r;
|
||||
qemu_thread_get_self(cpu->thread);
|
||||
qemu_mutex_lock(&qemu_global_mutex);
|
||||
|
||||
cpu->thread_id = qemu_get_thread_id();
|
||||
cpu->created = true;
|
||||
cpu->halted = 0;
|
||||
current_cpu = cpu;
|
||||
|
||||
hax_init_vcpu(cpu);
|
||||
qemu_cond_signal(&qemu_cpu_cond);
|
||||
|
||||
while (1) {
|
||||
if (cpu_can_run(cpu)) {
|
||||
r = hax_smp_cpu_exec(cpu);
|
||||
if (r == EXCP_DEBUG) {
|
||||
cpu_handle_guest_debug(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
while (cpu_thread_is_idle(cpu)) {
|
||||
qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
SleepEx(0, TRUE);
|
||||
#endif
|
||||
qemu_wait_io_event_common(cpu);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static void CALLBACK dummy_apc_func(ULONG_PTR unused)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static void qemu_cpu_kick_thread(CPUState *cpu)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
@@ -1277,13 +1235,7 @@ static void qemu_cpu_kick_thread(CPUState *cpu)
|
||||
exit(1);
|
||||
}
|
||||
#else /* _WIN32 */
|
||||
if (!qemu_cpu_is_self(cpu)) {
|
||||
if (!QueueUserAPC(dummy_apc_func, cpu->hThread, 0)) {
|
||||
fprintf(stderr, "%s: QueueUserAPC failed with error %lu\n",
|
||||
__func__, GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1306,13 +1258,6 @@ void qemu_cpu_kick(CPUState *cpu)
|
||||
if (tcg_enabled()) {
|
||||
qemu_cpu_kick_no_halt();
|
||||
} else {
|
||||
if (hax_enabled()) {
|
||||
/*
|
||||
* FIXME: race condition with the exit_request check in
|
||||
* hax_vcpu_hax_exec
|
||||
*/
|
||||
cpu->exit_request = 1;
|
||||
}
|
||||
qemu_cpu_kick_thread(cpu);
|
||||
}
|
||||
}
|
||||
@@ -1473,26 +1418,6 @@ static void qemu_tcg_init_vcpu(CPUState *cpu)
|
||||
}
|
||||
}
|
||||
|
||||
static void qemu_hax_start_vcpu(CPUState *cpu)
|
||||
{
|
||||
char thread_name[VCPU_THREAD_NAME_SIZE];
|
||||
|
||||
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/HAX",
|
||||
cpu->cpu_index);
|
||||
qemu_thread_create(cpu->thread, thread_name, qemu_hax_cpu_thread_fn,
|
||||
cpu, QEMU_THREAD_JOINABLE);
|
||||
#ifdef _WIN32
|
||||
cpu->hThread = qemu_thread_get_handle(cpu->thread);
|
||||
#endif
|
||||
while (!cpu->created) {
|
||||
qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void qemu_kvm_start_vcpu(CPUState *cpu)
|
||||
{
|
||||
char thread_name[VCPU_THREAD_NAME_SIZE];
|
||||
@@ -1543,8 +1468,6 @@ void qemu_init_vcpu(CPUState *cpu)
|
||||
|
||||
if (kvm_enabled()) {
|
||||
qemu_kvm_start_vcpu(cpu);
|
||||
} else if (hax_enabled()) {
|
||||
qemu_hax_start_vcpu(cpu);
|
||||
} else if (tcg_enabled()) {
|
||||
qemu_tcg_init_vcpu(cpu);
|
||||
} else {
|
||||
@@ -1578,48 +1501,6 @@ int vm_stop(RunState state)
|
||||
return do_vm_stop(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare for (re)starting the VM.
|
||||
* Returns -1 if the vCPUs are not to be restarted (e.g. if they are already
|
||||
* running or in case of an error condition), 0 otherwise.
|
||||
*/
|
||||
int vm_prepare_start(void)
|
||||
{
|
||||
RunState requested;
|
||||
int res = 0;
|
||||
|
||||
qemu_vmstop_requested(&requested);
|
||||
if (runstate_is_running() && requested == RUN_STATE__MAX) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Ensure that a STOP/RESUME pair of events is emitted if a
|
||||
* vmstop request was pending. The BLOCK_IO_ERROR event, for
|
||||
* example, according to documentation is always followed by
|
||||
* the STOP event.
|
||||
*/
|
||||
if (runstate_is_running()) {
|
||||
qapi_event_send_stop(&error_abort);
|
||||
res = -1;
|
||||
} else {
|
||||
replay_enable_events();
|
||||
cpu_enable_ticks();
|
||||
runstate_set(RUN_STATE_RUNNING);
|
||||
vm_state_notify(1, RUN_STATE_RUNNING);
|
||||
}
|
||||
|
||||
/* We are sending this now, but the CPUs will be resumed shortly later */
|
||||
qapi_event_send_resume(&error_abort);
|
||||
return res;
|
||||
}
|
||||
|
||||
void vm_start(void)
|
||||
{
|
||||
if (!vm_prepare_start()) {
|
||||
resume_all_vcpus();
|
||||
}
|
||||
}
|
||||
|
||||
/* does a state transition even if the VM is already stopped,
|
||||
current state is forgotten forever */
|
||||
int vm_stop_force_state(RunState state)
|
||||
|
21
cputlb.c
21
cputlb.c
@@ -60,15 +60,24 @@
|
||||
/* statistics */
|
||||
int tlb_flush_count;
|
||||
|
||||
/* This is OK because CPU architectures generally permit an
|
||||
* implementation to drop entries from the TLB at any time, so
|
||||
* flushing more entries than required is only an efficiency issue,
|
||||
* not a correctness issue.
|
||||
/* NOTE:
|
||||
* If flush_global is true (the usual case), flush all tlb entries.
|
||||
* If flush_global is false, flush (at least) all tlb entries not
|
||||
* marked global.
|
||||
*
|
||||
* Since QEMU doesn't currently implement a global/not-global flag
|
||||
* for tlb entries, at the moment tlb_flush() will also flush all
|
||||
* tlb entries in the flush_global == false case. This is OK because
|
||||
* CPU architectures generally permit an implementation to drop
|
||||
* entries from the TLB at any time, so flushing more entries than
|
||||
* required is only an efficiency issue, not a correctness issue.
|
||||
*/
|
||||
void tlb_flush(CPUState *cpu)
|
||||
void tlb_flush(CPUState *cpu, int flush_global)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
|
||||
tlb_debug("(%d)\n", flush_global);
|
||||
|
||||
memset(env->tlb_table, -1, sizeof(env->tlb_table));
|
||||
memset(env->tlb_v_table, -1, sizeof(env->tlb_v_table));
|
||||
memset(cpu->tb_jmp_cache, 0, sizeof(cpu->tb_jmp_cache));
|
||||
@@ -135,7 +144,7 @@ void tlb_flush_page(CPUState *cpu, target_ulong addr)
|
||||
TARGET_FMT_lx "/" TARGET_FMT_lx ")\n",
|
||||
env->tlb_flush_addr, env->tlb_flush_mask);
|
||||
|
||||
tlb_flush(cpu);
|
||||
tlb_flush(cpu, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -3,10 +3,6 @@ crypto-obj-y += hash.o
|
||||
crypto-obj-$(CONFIG_NETTLE) += hash-nettle.o
|
||||
crypto-obj-$(if $(CONFIG_NETTLE),n,$(CONFIG_GCRYPT)) += hash-gcrypt.o
|
||||
crypto-obj-$(if $(CONFIG_NETTLE),n,$(if $(CONFIG_GCRYPT),n,y)) += hash-glib.o
|
||||
crypto-obj-y += hmac.o
|
||||
crypto-obj-$(CONFIG_NETTLE) += hmac-nettle.o
|
||||
crypto-obj-$(CONFIG_GCRYPT_HMAC) += hmac-gcrypt.o
|
||||
crypto-obj-$(if $(CONFIG_NETTLE),n,$(if $(CONFIG_GCRYPT_HMAC),n,y)) += hmac-glib.o
|
||||
crypto-obj-y += aes.o
|
||||
crypto-obj-y += desrfb.o
|
||||
crypto-obj-y += cipher.o
|
||||
|
@@ -29,7 +29,6 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg,
|
||||
{
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_DES_RFB:
|
||||
case QCRYPTO_CIPHER_ALG_3DES:
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
case QCRYPTO_CIPHER_ALG_AES_192:
|
||||
case QCRYPTO_CIPHER_ALG_AES_256:
|
||||
@@ -100,10 +99,6 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
|
||||
gcryalg = GCRY_CIPHER_DES;
|
||||
break;
|
||||
|
||||
case QCRYPTO_CIPHER_ALG_3DES:
|
||||
gcryalg = GCRY_CIPHER_3DES;
|
||||
break;
|
||||
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
gcryalg = GCRY_CIPHER_AES128;
|
||||
break;
|
||||
@@ -205,7 +200,6 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
|
||||
case QCRYPTO_CIPHER_ALG_TWOFISH_256:
|
||||
ctx->blocksize = 16;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_3DES:
|
||||
case QCRYPTO_CIPHER_ALG_CAST5_128:
|
||||
ctx->blocksize = 8;
|
||||
break;
|
||||
|
@@ -78,18 +78,6 @@ static void des_decrypt_native(cipher_ctx_t ctx, cipher_length_t length,
|
||||
des_decrypt(ctx, length, dst, src);
|
||||
}
|
||||
|
||||
static void des3_encrypt_native(cipher_ctx_t ctx, cipher_length_t length,
|
||||
uint8_t *dst, const uint8_t *src)
|
||||
{
|
||||
des3_encrypt(ctx, length, dst, src);
|
||||
}
|
||||
|
||||
static void des3_decrypt_native(cipher_ctx_t ctx, cipher_length_t length,
|
||||
uint8_t *dst, const uint8_t *src)
|
||||
{
|
||||
des3_decrypt(ctx, length, dst, src);
|
||||
}
|
||||
|
||||
static void cast128_encrypt_native(cipher_ctx_t ctx, cipher_length_t length,
|
||||
uint8_t *dst, const uint8_t *src)
|
||||
{
|
||||
@@ -152,18 +140,6 @@ static void des_decrypt_wrapper(const void *ctx, size_t length,
|
||||
des_decrypt(ctx, length, dst, src);
|
||||
}
|
||||
|
||||
static void des3_encrypt_wrapper(const void *ctx, size_t length,
|
||||
uint8_t *dst, const uint8_t *src)
|
||||
{
|
||||
des3_encrypt(ctx, length, dst, src);
|
||||
}
|
||||
|
||||
static void des3_decrypt_wrapper(const void *ctx, size_t length,
|
||||
uint8_t *dst, const uint8_t *src)
|
||||
{
|
||||
des3_decrypt(ctx, length, dst, src);
|
||||
}
|
||||
|
||||
static void cast128_encrypt_wrapper(const void *ctx, size_t length,
|
||||
uint8_t *dst, const uint8_t *src)
|
||||
{
|
||||
@@ -221,7 +197,6 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg,
|
||||
{
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_DES_RFB:
|
||||
case QCRYPTO_CIPHER_ALG_3DES:
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
case QCRYPTO_CIPHER_ALG_AES_192:
|
||||
case QCRYPTO_CIPHER_ALG_AES_256:
|
||||
@@ -279,7 +254,6 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
|
||||
cipher->mode = mode;
|
||||
|
||||
ctx = g_new0(QCryptoCipherNettle, 1);
|
||||
cipher->opaque = ctx;
|
||||
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_DES_RFB:
|
||||
@@ -296,18 +270,6 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
|
||||
ctx->blocksize = DES_BLOCK_SIZE;
|
||||
break;
|
||||
|
||||
case QCRYPTO_CIPHER_ALG_3DES:
|
||||
ctx->ctx = g_new0(struct des3_ctx, 1);
|
||||
des3_set_key(ctx->ctx, key);
|
||||
|
||||
ctx->alg_encrypt_native = des3_encrypt_native;
|
||||
ctx->alg_decrypt_native = des3_decrypt_native;
|
||||
ctx->alg_encrypt_wrapper = des3_encrypt_wrapper;
|
||||
ctx->alg_decrypt_wrapper = des3_decrypt_wrapper;
|
||||
|
||||
ctx->blocksize = DES3_BLOCK_SIZE;
|
||||
break;
|
||||
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
case QCRYPTO_CIPHER_ALG_AES_192:
|
||||
case QCRYPTO_CIPHER_ALG_AES_256:
|
||||
@@ -422,11 +384,13 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
|
||||
}
|
||||
|
||||
ctx->iv = g_new0(uint8_t, ctx->blocksize);
|
||||
cipher->opaque = ctx;
|
||||
|
||||
return cipher;
|
||||
|
||||
error:
|
||||
qcrypto_cipher_free(cipher);
|
||||
g_free(cipher);
|
||||
g_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@@ -28,7 +28,6 @@ static size_t alg_key_len[QCRYPTO_CIPHER_ALG__MAX] = {
|
||||
[QCRYPTO_CIPHER_ALG_AES_192] = 24,
|
||||
[QCRYPTO_CIPHER_ALG_AES_256] = 32,
|
||||
[QCRYPTO_CIPHER_ALG_DES_RFB] = 8,
|
||||
[QCRYPTO_CIPHER_ALG_3DES] = 24,
|
||||
[QCRYPTO_CIPHER_ALG_CAST5_128] = 16,
|
||||
[QCRYPTO_CIPHER_ALG_SERPENT_128] = 16,
|
||||
[QCRYPTO_CIPHER_ALG_SERPENT_192] = 24,
|
||||
@@ -43,7 +42,6 @@ static size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX] = {
|
||||
[QCRYPTO_CIPHER_ALG_AES_192] = 16,
|
||||
[QCRYPTO_CIPHER_ALG_AES_256] = 16,
|
||||
[QCRYPTO_CIPHER_ALG_DES_RFB] = 8,
|
||||
[QCRYPTO_CIPHER_ALG_3DES] = 8,
|
||||
[QCRYPTO_CIPHER_ALG_CAST5_128] = 8,
|
||||
[QCRYPTO_CIPHER_ALG_SERPENT_128] = 16,
|
||||
[QCRYPTO_CIPHER_ALG_SERPENT_192] = 16,
|
||||
@@ -109,9 +107,8 @@ qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg,
|
||||
}
|
||||
|
||||
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
|
||||
if (alg == QCRYPTO_CIPHER_ALG_DES_RFB
|
||||
|| alg == QCRYPTO_CIPHER_ALG_3DES) {
|
||||
error_setg(errp, "XTS mode not compatible with DES-RFB/3DES");
|
||||
if (alg == QCRYPTO_CIPHER_ALG_DES_RFB) {
|
||||
error_setg(errp, "XTS mode not compatible with DES-RFB");
|
||||
return false;
|
||||
}
|
||||
if (nkey % 2) {
|
||||
|
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
* QEMU Crypto hmac algorithms (based on libgcrypt)
|
||||
*
|
||||
* Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD.
|
||||
*
|
||||
* Authors:
|
||||
* Longpeng(Mike) <longpeng2@huawei.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* (at your option) any later version. See the COPYING file in the
|
||||
* top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "crypto/hmac.h"
|
||||
#include <gcrypt.h>
|
||||
|
||||
static int qcrypto_hmac_alg_map[QCRYPTO_HASH_ALG__MAX] = {
|
||||
[QCRYPTO_HASH_ALG_MD5] = GCRY_MAC_HMAC_MD5,
|
||||
[QCRYPTO_HASH_ALG_SHA1] = GCRY_MAC_HMAC_SHA1,
|
||||
[QCRYPTO_HASH_ALG_SHA224] = GCRY_MAC_HMAC_SHA224,
|
||||
[QCRYPTO_HASH_ALG_SHA256] = GCRY_MAC_HMAC_SHA256,
|
||||
[QCRYPTO_HASH_ALG_SHA384] = GCRY_MAC_HMAC_SHA384,
|
||||
[QCRYPTO_HASH_ALG_SHA512] = GCRY_MAC_HMAC_SHA512,
|
||||
[QCRYPTO_HASH_ALG_RIPEMD160] = GCRY_MAC_HMAC_RMD160,
|
||||
};
|
||||
|
||||
typedef struct QCryptoHmacGcrypt QCryptoHmacGcrypt;
|
||||
struct QCryptoHmacGcrypt {
|
||||
gcry_mac_hd_t handle;
|
||||
};
|
||||
|
||||
bool qcrypto_hmac_supports(QCryptoHashAlgorithm alg)
|
||||
{
|
||||
if (alg < G_N_ELEMENTS(qcrypto_hmac_alg_map) &&
|
||||
qcrypto_hmac_alg_map[alg] != GCRY_MAC_NONE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QCryptoHmac *qcrypto_hmac_new(QCryptoHashAlgorithm alg,
|
||||
const uint8_t *key, size_t nkey,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoHmac *hmac;
|
||||
QCryptoHmacGcrypt *ctx;
|
||||
gcry_error_t err;
|
||||
|
||||
if (!qcrypto_hmac_supports(alg)) {
|
||||
error_setg(errp, "Unsupported hmac algorithm %s",
|
||||
QCryptoHashAlgorithm_lookup[alg]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hmac = g_new0(QCryptoHmac, 1);
|
||||
hmac->alg = alg;
|
||||
|
||||
ctx = g_new0(QCryptoHmacGcrypt, 1);
|
||||
|
||||
err = gcry_mac_open(&ctx->handle, qcrypto_hmac_alg_map[alg],
|
||||
GCRY_MAC_FLAG_SECURE, NULL);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot initialize hmac: %s",
|
||||
gcry_strerror(err));
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = gcry_mac_setkey(ctx->handle, (const void *)key, nkey);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot set key: %s",
|
||||
gcry_strerror(err));
|
||||
goto error;
|
||||
}
|
||||
|
||||
hmac->opaque = ctx;
|
||||
return hmac;
|
||||
|
||||
error:
|
||||
g_free(ctx);
|
||||
g_free(hmac);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void qcrypto_hmac_free(QCryptoHmac *hmac)
|
||||
{
|
||||
QCryptoHmacGcrypt *ctx;
|
||||
|
||||
if (!hmac) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx = hmac->opaque;
|
||||
gcry_mac_close(ctx->handle);
|
||||
|
||||
g_free(ctx);
|
||||
g_free(hmac);
|
||||
}
|
||||
|
||||
int qcrypto_hmac_bytesv(QCryptoHmac *hmac,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
uint8_t **result,
|
||||
size_t *resultlen,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoHmacGcrypt *ctx;
|
||||
gcry_error_t err;
|
||||
uint32_t ret;
|
||||
int i;
|
||||
|
||||
ctx = hmac->opaque;
|
||||
|
||||
for (i = 0; i < niov; i++) {
|
||||
gcry_mac_write(ctx->handle, iov[i].iov_base, iov[i].iov_len);
|
||||
}
|
||||
|
||||
ret = gcry_mac_get_algo_maclen(qcrypto_hmac_alg_map[hmac->alg]);
|
||||
if (ret <= 0) {
|
||||
error_setg(errp, "Unable to get hmac length: %s",
|
||||
gcry_strerror(ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*resultlen == 0) {
|
||||
*resultlen = ret;
|
||||
*result = g_new0(uint8_t, *resultlen);
|
||||
} else if (*resultlen != ret) {
|
||||
error_setg(errp, "Result buffer size %zu is smaller than hmac %d",
|
||||
*resultlen, ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = gcry_mac_read(ctx->handle, *result, resultlen);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot get result: %s",
|
||||
gcry_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = gcry_mac_reset(ctx->handle);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot reset hmac context: %s",
|
||||
gcry_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@@ -1,166 +0,0 @@
|
||||
/*
|
||||
* QEMU Crypto hmac algorithms (based on glib)
|
||||
*
|
||||
* Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD.
|
||||
*
|
||||
* Authors:
|
||||
* Longpeng(Mike) <longpeng2@huawei.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* (at your option) any later version. See the COPYING file in the
|
||||
* top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "crypto/hmac.h"
|
||||
|
||||
/* Support for HMAC Algos has been added in GLib 2.30 */
|
||||
#if GLIB_CHECK_VERSION(2, 30, 0)
|
||||
|
||||
static int qcrypto_hmac_alg_map[QCRYPTO_HASH_ALG__MAX] = {
|
||||
[QCRYPTO_HASH_ALG_MD5] = G_CHECKSUM_MD5,
|
||||
[QCRYPTO_HASH_ALG_SHA1] = G_CHECKSUM_SHA1,
|
||||
[QCRYPTO_HASH_ALG_SHA256] = G_CHECKSUM_SHA256,
|
||||
/* Support for HMAC SHA-512 in GLib 2.42 */
|
||||
#if GLIB_CHECK_VERSION(2, 42, 0)
|
||||
[QCRYPTO_HASH_ALG_SHA512] = G_CHECKSUM_SHA512,
|
||||
#else
|
||||
[QCRYPTO_HASH_ALG_SHA512] = -1,
|
||||
#endif
|
||||
[QCRYPTO_HASH_ALG_SHA224] = -1,
|
||||
[QCRYPTO_HASH_ALG_SHA384] = -1,
|
||||
[QCRYPTO_HASH_ALG_RIPEMD160] = -1,
|
||||
};
|
||||
|
||||
typedef struct QCryptoHmacGlib QCryptoHmacGlib;
|
||||
struct QCryptoHmacGlib {
|
||||
GHmac *ghmac;
|
||||
};
|
||||
|
||||
bool qcrypto_hmac_supports(QCryptoHashAlgorithm alg)
|
||||
{
|
||||
if (alg < G_N_ELEMENTS(qcrypto_hmac_alg_map) &&
|
||||
qcrypto_hmac_alg_map[alg] != -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QCryptoHmac *qcrypto_hmac_new(QCryptoHashAlgorithm alg,
|
||||
const uint8_t *key, size_t nkey,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoHmac *hmac;
|
||||
QCryptoHmacGlib *ctx;
|
||||
|
||||
if (!qcrypto_hmac_supports(alg)) {
|
||||
error_setg(errp, "Unsupported hmac algorithm %s",
|
||||
QCryptoHashAlgorithm_lookup[alg]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hmac = g_new0(QCryptoHmac, 1);
|
||||
hmac->alg = alg;
|
||||
|
||||
ctx = g_new0(QCryptoHmacGlib, 1);
|
||||
|
||||
ctx->ghmac = g_hmac_new(qcrypto_hmac_alg_map[alg],
|
||||
(const uint8_t *)key, nkey);
|
||||
if (!ctx->ghmac) {
|
||||
error_setg(errp, "Cannot initialize hmac and set key");
|
||||
goto error;
|
||||
}
|
||||
|
||||
hmac->opaque = ctx;
|
||||
return hmac;
|
||||
|
||||
error:
|
||||
g_free(ctx);
|
||||
g_free(hmac);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void qcrypto_hmac_free(QCryptoHmac *hmac)
|
||||
{
|
||||
QCryptoHmacGlib *ctx;
|
||||
|
||||
if (!hmac) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx = hmac->opaque;
|
||||
g_hmac_unref(ctx->ghmac);
|
||||
|
||||
g_free(ctx);
|
||||
g_free(hmac);
|
||||
}
|
||||
|
||||
int qcrypto_hmac_bytesv(QCryptoHmac *hmac,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
uint8_t **result,
|
||||
size_t *resultlen,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoHmacGlib *ctx;
|
||||
int i, ret;
|
||||
|
||||
ctx = hmac->opaque;
|
||||
|
||||
for (i = 0; i < niov; i++) {
|
||||
g_hmac_update(ctx->ghmac, iov[i].iov_base, iov[i].iov_len);
|
||||
}
|
||||
|
||||
ret = g_checksum_type_get_length(qcrypto_hmac_alg_map[hmac->alg]);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Unable to get hmac length");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*resultlen == 0) {
|
||||
*resultlen = ret;
|
||||
*result = g_new0(uint8_t, *resultlen);
|
||||
} else if (*resultlen != ret) {
|
||||
error_setg(errp, "Result buffer size %zu is smaller than hmac %d",
|
||||
*resultlen, ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_hmac_get_digest(ctx->ghmac, *result, resultlen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
bool qcrypto_hmac_supports(QCryptoHashAlgorithm alg)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QCryptoHmac *qcrypto_hmac_new(QCryptoHashAlgorithm alg,
|
||||
const uint8_t *key, size_t nkey,
|
||||
Error **errp)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void qcrypto_hmac_free(QCryptoHmac *hmac)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int qcrypto_hmac_bytesv(QCryptoHmac *hmac,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
uint8_t **result,
|
||||
size_t *resultlen,
|
||||
Error **errp)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,175 +0,0 @@
|
||||
/*
|
||||
* QEMU Crypto hmac algorithms (based on nettle)
|
||||
*
|
||||
* Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD.
|
||||
*
|
||||
* Authors:
|
||||
* Longpeng(Mike) <longpeng2@huawei.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* (at your option) any later version. See the COPYING file in the
|
||||
* top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "crypto/hmac.h"
|
||||
#include <nettle/hmac.h>
|
||||
|
||||
typedef void (*qcrypto_nettle_hmac_setkey)(void *ctx,
|
||||
size_t key_length, const uint8_t *key);
|
||||
|
||||
typedef void (*qcrypto_nettle_hmac_update)(void *ctx,
|
||||
size_t length, const uint8_t *data);
|
||||
|
||||
typedef void (*qcrypto_nettle_hmac_digest)(void *ctx,
|
||||
size_t length, uint8_t *digest);
|
||||
|
||||
typedef struct QCryptoHmacNettle QCryptoHmacNettle;
|
||||
struct QCryptoHmacNettle {
|
||||
union qcrypto_nettle_hmac_ctx {
|
||||
struct hmac_md5_ctx md5_ctx;
|
||||
struct hmac_sha1_ctx sha1_ctx;
|
||||
struct hmac_sha256_ctx sha256_ctx; /* equals hmac_sha224_ctx */
|
||||
struct hmac_sha512_ctx sha512_ctx; /* equals hmac_sha384_ctx */
|
||||
struct hmac_ripemd160_ctx ripemd160_ctx;
|
||||
} u;
|
||||
};
|
||||
|
||||
struct qcrypto_nettle_hmac_alg {
|
||||
qcrypto_nettle_hmac_setkey setkey;
|
||||
qcrypto_nettle_hmac_update update;
|
||||
qcrypto_nettle_hmac_digest digest;
|
||||
size_t len;
|
||||
} qcrypto_hmac_alg_map[QCRYPTO_HASH_ALG__MAX] = {
|
||||
[QCRYPTO_HASH_ALG_MD5] = {
|
||||
.setkey = (qcrypto_nettle_hmac_setkey)hmac_md5_set_key,
|
||||
.update = (qcrypto_nettle_hmac_update)hmac_md5_update,
|
||||
.digest = (qcrypto_nettle_hmac_digest)hmac_md5_digest,
|
||||
.len = MD5_DIGEST_SIZE,
|
||||
},
|
||||
[QCRYPTO_HASH_ALG_SHA1] = {
|
||||
.setkey = (qcrypto_nettle_hmac_setkey)hmac_sha1_set_key,
|
||||
.update = (qcrypto_nettle_hmac_update)hmac_sha1_update,
|
||||
.digest = (qcrypto_nettle_hmac_digest)hmac_sha1_digest,
|
||||
.len = SHA1_DIGEST_SIZE,
|
||||
},
|
||||
[QCRYPTO_HASH_ALG_SHA224] = {
|
||||
.setkey = (qcrypto_nettle_hmac_setkey)hmac_sha224_set_key,
|
||||
.update = (qcrypto_nettle_hmac_update)hmac_sha224_update,
|
||||
.digest = (qcrypto_nettle_hmac_digest)hmac_sha224_digest,
|
||||
.len = SHA224_DIGEST_SIZE,
|
||||
},
|
||||
[QCRYPTO_HASH_ALG_SHA256] = {
|
||||
.setkey = (qcrypto_nettle_hmac_setkey)hmac_sha256_set_key,
|
||||
.update = (qcrypto_nettle_hmac_update)hmac_sha256_update,
|
||||
.digest = (qcrypto_nettle_hmac_digest)hmac_sha256_digest,
|
||||
.len = SHA256_DIGEST_SIZE,
|
||||
},
|
||||
[QCRYPTO_HASH_ALG_SHA384] = {
|
||||
.setkey = (qcrypto_nettle_hmac_setkey)hmac_sha384_set_key,
|
||||
.update = (qcrypto_nettle_hmac_update)hmac_sha384_update,
|
||||
.digest = (qcrypto_nettle_hmac_digest)hmac_sha384_digest,
|
||||
.len = SHA384_DIGEST_SIZE,
|
||||
},
|
||||
[QCRYPTO_HASH_ALG_SHA512] = {
|
||||
.setkey = (qcrypto_nettle_hmac_setkey)hmac_sha512_set_key,
|
||||
.update = (qcrypto_nettle_hmac_update)hmac_sha512_update,
|
||||
.digest = (qcrypto_nettle_hmac_digest)hmac_sha512_digest,
|
||||
.len = SHA512_DIGEST_SIZE,
|
||||
},
|
||||
[QCRYPTO_HASH_ALG_RIPEMD160] = {
|
||||
.setkey = (qcrypto_nettle_hmac_setkey)hmac_ripemd160_set_key,
|
||||
.update = (qcrypto_nettle_hmac_update)hmac_ripemd160_update,
|
||||
.digest = (qcrypto_nettle_hmac_digest)hmac_ripemd160_digest,
|
||||
.len = RIPEMD160_DIGEST_SIZE,
|
||||
},
|
||||
};
|
||||
|
||||
bool qcrypto_hmac_supports(QCryptoHashAlgorithm alg)
|
||||
{
|
||||
if (alg < G_N_ELEMENTS(qcrypto_hmac_alg_map) &&
|
||||
qcrypto_hmac_alg_map[alg].setkey != NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QCryptoHmac *qcrypto_hmac_new(QCryptoHashAlgorithm alg,
|
||||
const uint8_t *key, size_t nkey,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoHmac *hmac;
|
||||
QCryptoHmacNettle *ctx;
|
||||
|
||||
if (!qcrypto_hmac_supports(alg)) {
|
||||
error_setg(errp, "Unsupported hmac algorithm %s",
|
||||
QCryptoHashAlgorithm_lookup[alg]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hmac = g_new0(QCryptoHmac, 1);
|
||||
hmac->alg = alg;
|
||||
|
||||
ctx = g_new0(QCryptoHmacNettle, 1);
|
||||
|
||||
qcrypto_hmac_alg_map[alg].setkey(&ctx->u, nkey, key);
|
||||
|
||||
hmac->opaque = ctx;
|
||||
|
||||
return hmac;
|
||||
}
|
||||
|
||||
void qcrypto_hmac_free(QCryptoHmac *hmac)
|
||||
{
|
||||
QCryptoHmacNettle *ctx;
|
||||
|
||||
if (!hmac) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx = hmac->opaque;
|
||||
|
||||
g_free(ctx);
|
||||
g_free(hmac);
|
||||
}
|
||||
|
||||
int qcrypto_hmac_bytesv(QCryptoHmac *hmac,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
uint8_t **result,
|
||||
size_t *resultlen,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoHmacNettle *ctx;
|
||||
int i;
|
||||
|
||||
ctx = (QCryptoHmacNettle *)hmac->opaque;
|
||||
|
||||
for (i = 0; i < niov; ++i) {
|
||||
size_t len = iov[i].iov_len;
|
||||
uint8_t *base = iov[i].iov_base;
|
||||
while (len) {
|
||||
size_t shortlen = MIN(len, UINT_MAX);
|
||||
qcrypto_hmac_alg_map[hmac->alg].update(&ctx->u, len, base);
|
||||
len -= shortlen;
|
||||
base += len;
|
||||
}
|
||||
}
|
||||
|
||||
if (*resultlen == 0) {
|
||||
*resultlen = qcrypto_hmac_alg_map[hmac->alg].len;
|
||||
*result = g_new0(uint8_t, *resultlen);
|
||||
} else if (*resultlen != qcrypto_hmac_alg_map[hmac->alg].len) {
|
||||
error_setg(errp,
|
||||
"Result buffer size %zu is smaller than hash %zu",
|
||||
*resultlen, qcrypto_hmac_alg_map[hmac->alg].len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
qcrypto_hmac_alg_map[hmac->alg].digest(&ctx->u, *resultlen, *result);
|
||||
|
||||
return 0;
|
||||
}
|
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* QEMU Crypto hmac algorithms
|
||||
*
|
||||
* Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* (at your option) any later version. See the COPYING file in the
|
||||
* top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "crypto/hmac.h"
|
||||
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
|
||||
int qcrypto_hmac_bytes(QCryptoHmac *hmac,
|
||||
const char *buf,
|
||||
size_t len,
|
||||
uint8_t **result,
|
||||
size_t *resultlen,
|
||||
Error **errp)
|
||||
{
|
||||
struct iovec iov = {
|
||||
.iov_base = (char *)buf,
|
||||
.iov_len = len
|
||||
};
|
||||
|
||||
return qcrypto_hmac_bytesv(hmac, &iov, 1, result, resultlen, errp);
|
||||
}
|
||||
|
||||
int qcrypto_hmac_digestv(QCryptoHmac *hmac,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
char **digest,
|
||||
Error **errp)
|
||||
{
|
||||
uint8_t *result = NULL;
|
||||
size_t resultlen = 0;
|
||||
size_t i;
|
||||
|
||||
if (qcrypto_hmac_bytesv(hmac, iov, niov, &result, &resultlen, errp) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*digest = g_new0(char, (resultlen * 2) + 1);
|
||||
|
||||
for (i = 0 ; i < resultlen ; i++) {
|
||||
(*digest)[(i * 2)] = hex[(result[i] >> 4) & 0xf];
|
||||
(*digest)[(i * 2) + 1] = hex[result[i] & 0xf];
|
||||
}
|
||||
|
||||
(*digest)[resultlen * 2] = '\0';
|
||||
|
||||
g_free(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qcrypto_hmac_digest(QCryptoHmac *hmac,
|
||||
const char *buf,
|
||||
size_t len,
|
||||
char **digest,
|
||||
Error **errp)
|
||||
{
|
||||
struct iovec iov = {
|
||||
.iov_base = (char *)buf,
|
||||
.iov_len = len
|
||||
};
|
||||
|
||||
return qcrypto_hmac_digestv(hmac, &iov, 1, digest, errp);
|
||||
}
|
166
crypto/hmac.h
166
crypto/hmac.h
@@ -1,166 +0,0 @@
|
||||
/*
|
||||
* QEMU Crypto hmac algorithms
|
||||
*
|
||||
* Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* (at your option) any later version. See the COPYING file in the
|
||||
* top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef QCRYPTO_HMAC_H
|
||||
#define QCRYPTO_HMAC_H
|
||||
|
||||
#include "qapi-types.h"
|
||||
|
||||
typedef struct QCryptoHmac QCryptoHmac;
|
||||
struct QCryptoHmac {
|
||||
QCryptoHashAlgorithm alg;
|
||||
void *opaque;
|
||||
};
|
||||
|
||||
/**
|
||||
* qcrypto_hmac_supports:
|
||||
* @alg: the hmac algorithm
|
||||
*
|
||||
* Determine if @alg hmac algorithm is supported by
|
||||
* the current configured build
|
||||
*
|
||||
* Returns:
|
||||
* true if the algorithm is supported, false otherwise
|
||||
*/
|
||||
bool qcrypto_hmac_supports(QCryptoHashAlgorithm alg);
|
||||
|
||||
/**
|
||||
* qcrypto_hmac_new:
|
||||
* @alg: the hmac algorithm
|
||||
* @key: the key bytes
|
||||
* @nkey: the length of @key
|
||||
* @errp: pointer to a NULL-initialized error object
|
||||
*
|
||||
* Creates a new hmac object with the algorithm @alg
|
||||
*
|
||||
* The @key parameter provides the bytes representing
|
||||
* the secret key to use. The @nkey parameter specifies
|
||||
* the length of @key in bytes
|
||||
*
|
||||
* Note: must use qcrypto_hmac_free() to release the
|
||||
* returned hmac object when no longer required
|
||||
*
|
||||
* Returns:
|
||||
* a new hmac object, or NULL on error
|
||||
*/
|
||||
QCryptoHmac *qcrypto_hmac_new(QCryptoHashAlgorithm alg,
|
||||
const uint8_t *key, size_t nkey,
|
||||
Error **errp);
|
||||
|
||||
/**
|
||||
* qcrypto_hmac_free:
|
||||
* @hmac: the hmac object
|
||||
*
|
||||
* Release the memory associated with @hmac that was
|
||||
* previously allocated by qcrypto_hmac_new()
|
||||
*/
|
||||
void qcrypto_hmac_free(QCryptoHmac *hmac);
|
||||
|
||||
/**
|
||||
* qcrypto_hmac_bytesv:
|
||||
* @hmac: the hmac object
|
||||
* @iov: the array of memory regions to hmac
|
||||
* @niov: the length of @iov
|
||||
* @result: pointer to hold output hmac
|
||||
* @resultlen: pointer to hold length of @result
|
||||
* @errp: pointer to a NULL-initialized error object
|
||||
*
|
||||
* Computes the hmac across all the memory regions
|
||||
* present in @iov. The @result pointer will be
|
||||
* filled with raw bytes representing the computed
|
||||
* hmac, which will have length @resultlen. The
|
||||
* memory pointer in @result must be released
|
||||
* with a call to g_free() when no longer required.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, -1 on error
|
||||
*/
|
||||
int qcrypto_hmac_bytesv(QCryptoHmac *hmac,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
uint8_t **result,
|
||||
size_t *resultlen,
|
||||
Error **errp);
|
||||
|
||||
/**
|
||||
* qcrypto_hmac_bytes:
|
||||
* @hmac: the hmac object
|
||||
* @buf: the memory region to hmac
|
||||
* @len: the length of @buf
|
||||
* @result: pointer to hold output hmac
|
||||
* @resultlen: pointer to hold length of @result
|
||||
* @errp: pointer to a NULL-initialized error object
|
||||
*
|
||||
* Computes the hmac across all the memory region
|
||||
* @buf of length @len. The @result pointer will be
|
||||
* filled with raw bytes representing the computed
|
||||
* hmac, which will have length @resultlen. The
|
||||
* memory pointer in @result must be released
|
||||
* with a call to g_free() when no longer required.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, -1 on error
|
||||
*/
|
||||
int qcrypto_hmac_bytes(QCryptoHmac *hmac,
|
||||
const char *buf,
|
||||
size_t len,
|
||||
uint8_t **result,
|
||||
size_t *resultlen,
|
||||
Error **errp);
|
||||
|
||||
/**
|
||||
* qcrypto_hmac_digestv:
|
||||
* @hmac: the hmac object
|
||||
* @iov: the array of memory regions to hmac
|
||||
* @niov: the length of @iov
|
||||
* @digest: pointer to hold output hmac
|
||||
* @errp: pointer to a NULL-initialized error object
|
||||
*
|
||||
* Computes the hmac across all the memory regions
|
||||
* present in @iov. The @digest pointer will be
|
||||
* filled with the printable hex digest of the computed
|
||||
* hmac, which will be terminated by '\0'. The
|
||||
* memory pointer in @digest must be released
|
||||
* with a call to g_free() when no longer required.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, -1 on error
|
||||
*/
|
||||
int qcrypto_hmac_digestv(QCryptoHmac *hmac,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
char **digest,
|
||||
Error **errp);
|
||||
|
||||
/**
|
||||
* qcrypto_hmac_digest:
|
||||
* @hmac: the hmac object
|
||||
* @buf: the memory region to hmac
|
||||
* @len: the length of @buf
|
||||
* @digest: pointer to hold output hmac
|
||||
* @errp: pointer to a NULL-initialized error object
|
||||
*
|
||||
* Computes the hmac across all the memory region
|
||||
* @buf of length @len. The @digest pointer will be
|
||||
* filled with the printable hex digest of the computed
|
||||
* hmac, which will be terminated by '\0'. The
|
||||
* memory pointer in @digest must be released
|
||||
* with a call to g_free() when no longer required.
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
int qcrypto_hmac_digest(QCryptoHmac *hmac,
|
||||
const char *buf,
|
||||
size_t len,
|
||||
char **digest,
|
||||
Error **errp);
|
||||
|
||||
#endif
|
@@ -3,7 +3,6 @@
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_SERIAL_ISA=y
|
||||
CONFIG_I8254=y
|
||||
CONFIG_PCKBD=y
|
||||
CONFIG_VGA_CIRRUS=y
|
||||
|
@@ -6,7 +6,6 @@ CONFIG_VGA=y
|
||||
CONFIG_NAND=y
|
||||
CONFIG_ECC=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_SERIAL_ISA=y
|
||||
CONFIG_PTIMER=y
|
||||
CONFIG_SD=y
|
||||
CONFIG_MAX7310=y
|
||||
@@ -95,8 +94,6 @@ CONFIG_VERSATILE_PCI=y
|
||||
CONFIG_VERSATILE_I2C=y
|
||||
|
||||
CONFIG_PCI_GENERIC=y
|
||||
CONFIG_VFIO_XGMAC=y
|
||||
CONFIG_VFIO_AMD_XGBE=y
|
||||
|
||||
CONFIG_SDHCI=y
|
||||
CONFIG_INTEGRATOR_DEBUG=y
|
||||
@@ -111,7 +108,6 @@ CONFIG_FSL_IMX25=y
|
||||
|
||||
CONFIG_IMX_I2C=y
|
||||
|
||||
CONFIG_PCIE_PORT=y
|
||||
CONFIG_XIO3130=y
|
||||
CONFIG_IOH3420=y
|
||||
CONFIG_I82801B11=y
|
||||
|
@@ -1 +0,0 @@
|
||||
# Default configuration for hppa-linux-user
|
@@ -15,7 +15,6 @@ CONFIG_IPMI_EXTERN=y
|
||||
CONFIG_ISA_IPMI_KCS=y
|
||||
CONFIG_ISA_IPMI_BT=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_SERIAL_ISA=y
|
||||
CONFIG_PARALLEL=y
|
||||
CONFIG_I8254=y
|
||||
CONFIG_PCSPK=y
|
||||
@@ -52,10 +51,8 @@ CONFIG_PVPANIC=y
|
||||
CONFIG_MEM_HOTPLUG=y
|
||||
CONFIG_NVDIMM=y
|
||||
CONFIG_ACPI_NVDIMM=y
|
||||
CONFIG_PCIE_PORT=y
|
||||
CONFIG_XIO3130=y
|
||||
CONFIG_IOH3420=y
|
||||
CONFIG_I82801B11=y
|
||||
CONFIG_SMBIOS=y
|
||||
CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
|
||||
CONFIG_PXB=y
|
||||
|
@@ -1,4 +1,6 @@
|
||||
# Default configuration for m68k-softmmu
|
||||
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_COLDFIRE=y
|
||||
CONFIG_PTIMER=y
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user