mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/stefanha/tags/block-pull-request' into staging
Pull request This pull request contains a virtio-blk/scsi performance optimization, event loop scalability improvements, and a qtest-based device fuzzing framework. I am including the fuzzing patches because I have reviewed them and Thomas Huth is currently away on leave. # gpg: Signature made Sat 22 Feb 2020 08:50:05 GMT # gpg: using RSA key 8695A8BFD3F97CDAAC35775A9CA4ABB381AB73C8 # gpg: Good signature from "Stefan Hajnoczi <stefanha@redhat.com>" [full] # gpg: aka "Stefan Hajnoczi <stefanha@gmail.com>" [full] # Primary key fingerprint: 8695 A8BF D3F9 7CDA AC35 775A 9CA4 ABB3 81AB 73C8 * remotes/stefanha/tags/block-pull-request: (31 commits) fuzz: add documentation to docs/devel/ fuzz: add virtio-scsi fuzz target fuzz: add virtio-net fuzz target fuzz: add i440fx fuzz targets fuzz: add configure flag --enable-fuzzing fuzz: add target/fuzz makefile rules fuzz: add support for qos-assisted fuzz targets fuzz: support for fork-based fuzzing. main: keep rcu_atfork callback enabled for qtest exec: keep ram block across fork when using qtest fuzz: add fuzzer skeleton libqos: move useful qos-test funcs to qos_external libqos: split qos-test and libqos makefile vars libqos: rename i2c_send and i2c_recv qtest: add in-process incoming command handler libqtest: make bufwrite rely on the TransportOps libqtest: add a layer of abstraction to send/recv qtest: add qtest_server_send abstraction fuzz: add FUZZ_TARGET module type module: check module wasn't already initialized ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
+10
-1
@@ -2031,7 +2031,8 @@ F: include/qemu/main-loop.h
|
||||
F: include/sysemu/runstate.h
|
||||
F: util/main-loop.c
|
||||
F: util/qemu-timer.c
|
||||
F: vl.c
|
||||
F: softmmu/vl.c
|
||||
F: softmmu/main.c
|
||||
F: qapi/run-state.json
|
||||
|
||||
Human Monitor (HMP)
|
||||
@@ -2183,6 +2184,14 @@ F: qtest.c
|
||||
F: accel/qtest.c
|
||||
F: tests/qtest/
|
||||
|
||||
Device Fuzzing
|
||||
M: Alexander Bulekov <alxndr@bu.edu>
|
||||
R: Paolo Bonzini <pbonzini@redhat.com>
|
||||
R: Bandan Das <bsd@redhat.com>
|
||||
R: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
S: Maintained
|
||||
F: tests/qtest/fuzz/
|
||||
|
||||
Register API
|
||||
M: Alistair Francis <alistair@alistair23.me>
|
||||
S: Maintained
|
||||
|
||||
@@ -477,7 +477,7 @@ config-host.h-timestamp: config-host.mak
|
||||
qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
|
||||
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@")
|
||||
|
||||
TARGET_DIRS_RULES := $(foreach t, all clean install, $(addsuffix /$(t), $(TARGET_DIRS)))
|
||||
TARGET_DIRS_RULES := $(foreach t, all fuzz clean install, $(addsuffix /$(t), $(TARGET_DIRS)))
|
||||
|
||||
SOFTMMU_ALL_RULES=$(filter %-softmmu/all, $(TARGET_DIRS_RULES))
|
||||
$(SOFTMMU_ALL_RULES): $(authz-obj-y)
|
||||
@@ -490,6 +490,15 @@ ifdef DECOMPRESS_EDK2_BLOBS
|
||||
$(SOFTMMU_ALL_RULES): $(edk2-decompressed)
|
||||
endif
|
||||
|
||||
SOFTMMU_FUZZ_RULES=$(filter %-softmmu/fuzz, $(TARGET_DIRS_RULES))
|
||||
$(SOFTMMU_FUZZ_RULES): $(authz-obj-y)
|
||||
$(SOFTMMU_FUZZ_RULES): $(block-obj-y)
|
||||
$(SOFTMMU_FUZZ_RULES): $(chardev-obj-y)
|
||||
$(SOFTMMU_FUZZ_RULES): $(crypto-obj-y)
|
||||
$(SOFTMMU_FUZZ_RULES): $(io-obj-y)
|
||||
$(SOFTMMU_FUZZ_RULES): config-all-devices.mak
|
||||
$(SOFTMMU_FUZZ_RULES): $(edk2-decompressed)
|
||||
|
||||
.PHONY: $(TARGET_DIRS_RULES)
|
||||
# The $(TARGET_DIRS_RULES) are of the form SUBDIR/GOAL, so that
|
||||
# $(dir $@) yields the sub-directory, and $(notdir $@) yields the sub-goal
|
||||
@@ -540,6 +549,9 @@ subdir-slirp: slirp/all
|
||||
$(filter %/all, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \
|
||||
$(qom-obj-y)
|
||||
|
||||
$(filter %/fuzz, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \
|
||||
$(qom-obj-y) $(crypto-user-obj-$(CONFIG_USER_ONLY))
|
||||
|
||||
ROM_DIRS = $(addprefix pc-bios/, $(ROMS))
|
||||
ROM_DIRS_RULES=$(foreach t, all clean, $(addsuffix /$(t), $(ROM_DIRS)))
|
||||
# Only keep -O and -g cflags
|
||||
@@ -549,6 +561,7 @@ $(ROM_DIRS_RULES):
|
||||
|
||||
.PHONY: recurse-all recurse-clean recurse-install
|
||||
recurse-all: $(addsuffix /all, $(TARGET_DIRS) $(ROM_DIRS))
|
||||
recurse-fuzz: $(addsuffix /fuzz, $(TARGET_DIRS) $(ROM_DIRS))
|
||||
recurse-clean: $(addsuffix /clean, $(TARGET_DIRS) $(ROM_DIRS))
|
||||
recurse-install: $(addsuffix /install, $(TARGET_DIRS))
|
||||
$(addsuffix /install, $(TARGET_DIRS)): all
|
||||
|
||||
@@ -58,8 +58,6 @@ common-obj-y += ui/
|
||||
common-obj-m += ui/
|
||||
|
||||
common-obj-y += dma-helpers.o
|
||||
common-obj-y += vl.o
|
||||
vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS)
|
||||
common-obj-$(CONFIG_TPM) += tpm.o
|
||||
|
||||
common-obj-y += backends/
|
||||
|
||||
+18
-1
@@ -160,6 +160,7 @@ obj-y += qapi/
|
||||
obj-y += memory.o
|
||||
obj-y += memory_mapping.o
|
||||
obj-y += migration/ram.o
|
||||
obj-y += softmmu/
|
||||
LIBS := $(libs_softmmu) $(LIBS)
|
||||
|
||||
# Hardware support
|
||||
@@ -202,7 +203,7 @@ endif
|
||||
COMMON_LDADDS = ../libqemuutil.a
|
||||
|
||||
# build either PROG or PROGW
|
||||
$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS)
|
||||
$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS) $(softmmu-main-y)
|
||||
$(call LINK, $(filter-out %.mak, $^))
|
||||
ifdef CONFIG_DARWIN
|
||||
$(call quiet-command,Rez -append $(SRC_PATH)/pc-bios/qemu.rsrc -o $@,"REZ","$(TARGET_DIR)$@")
|
||||
@@ -227,6 +228,22 @@ ifdef CONFIG_TRACE_SYSTEMTAP
|
||||
rm -f *.stp
|
||||
endif
|
||||
|
||||
ifdef CONFIG_FUZZ
|
||||
include $(SRC_PATH)/tests/qtest/fuzz/Makefile.include
|
||||
include $(SRC_PATH)/tests/qtest/Makefile.include
|
||||
|
||||
fuzz: fuzz-vars
|
||||
fuzz-vars: QEMU_CFLAGS := $(FUZZ_CFLAGS) $(QEMU_CFLAGS)
|
||||
fuzz-vars: QEMU_LDFLAGS := $(FUZZ_LDFLAGS) $(QEMU_LDFLAGS)
|
||||
fuzz-vars: $(QEMU_PROG_FUZZ)
|
||||
dummy := $(call unnest-vars,, fuzz-obj-y)
|
||||
|
||||
|
||||
$(QEMU_PROG_FUZZ): config-devices.mak $(all-obj-y) $(COMMON_LDADDS) $(fuzz-obj-y)
|
||||
$(call LINK, $(filter-out %.mak, $^))
|
||||
|
||||
endif
|
||||
|
||||
install: all
|
||||
ifneq ($(PROGS),)
|
||||
$(call install-prog,$(PROGS),$(DESTDIR)$(bindir))
|
||||
|
||||
@@ -2636,10 +2636,7 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
|
||||
|
||||
static void bdrv_detach_child(BdrvChild *child)
|
||||
{
|
||||
if (child->next.le_prev) {
|
||||
QLIST_REMOVE(child, next);
|
||||
child->next.le_prev = NULL;
|
||||
}
|
||||
QLIST_SAFE_REMOVE(child, next);
|
||||
|
||||
bdrv_replace_child(child, NULL);
|
||||
|
||||
|
||||
+1
-3
@@ -216,9 +216,7 @@ static void char_spice_finalize(Object *obj)
|
||||
|
||||
vmc_unregister_interface(s);
|
||||
|
||||
if (s->next.le_prev) {
|
||||
QLIST_REMOVE(s, next);
|
||||
}
|
||||
QLIST_SAFE_REMOVE(s, next);
|
||||
|
||||
g_free((char *)s->sin.subtype);
|
||||
g_free((char *)s->sin.portname);
|
||||
|
||||
@@ -505,6 +505,7 @@ debug_mutex="no"
|
||||
libpmem=""
|
||||
default_devices="yes"
|
||||
plugins="no"
|
||||
fuzzing="no"
|
||||
|
||||
supported_cpu="no"
|
||||
supported_os="no"
|
||||
@@ -635,6 +636,15 @@ int main(void) { return 0; }
|
||||
EOF
|
||||
}
|
||||
|
||||
write_c_fuzzer_skeleton() {
|
||||
cat > $TMPC <<EOF
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { return 0; }
|
||||
EOF
|
||||
}
|
||||
|
||||
if check_define __linux__ ; then
|
||||
targetos="Linux"
|
||||
elif check_define _WIN32 ; then
|
||||
@@ -1558,6 +1568,10 @@ for opt do
|
||||
;;
|
||||
--disable-containers) use_containers="no"
|
||||
;;
|
||||
--enable-fuzzing) fuzzing=yes
|
||||
;;
|
||||
--disable-fuzzing) fuzzing=no
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: unknown option $opt"
|
||||
echo "Try '$0 --help' for more information"
|
||||
@@ -6072,6 +6086,15 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# checks for fuzzer
|
||||
if test "$fuzzing" = "yes" ; then
|
||||
write_c_fuzzer_skeleton
|
||||
if compile_prog "$CPU_CFLAGS -Werror -fsanitize=address,fuzzer" ""; then
|
||||
have_fuzzer=yes
|
||||
fi
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# check for libpmem
|
||||
|
||||
@@ -6666,6 +6689,7 @@ echo "libpmem support $libpmem"
|
||||
echo "libudev $libudev"
|
||||
echo "default devices $default_devices"
|
||||
echo "plugin support $plugins"
|
||||
echo "fuzzing support $fuzzing"
|
||||
|
||||
if test "$supported_cpu" = "no"; then
|
||||
echo
|
||||
@@ -7504,6 +7528,16 @@ fi
|
||||
if test "$sheepdog" = "yes" ; then
|
||||
echo "CONFIG_SHEEPDOG=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$fuzzing" = "yes" ; then
|
||||
if test "$have_fuzzer" = "yes"; then
|
||||
FUZZ_LDFLAGS=" -fsanitize=address,fuzzer"
|
||||
FUZZ_CFLAGS=" -fsanitize=address,fuzzer"
|
||||
CFLAGS=" -fsanitize=address,fuzzer-no-link"
|
||||
else
|
||||
error_exit "Your compiler doesn't support -fsanitize=address,fuzzer"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$plugins" = "yes" ; then
|
||||
echo "CONFIG_PLUGIN=y" >> $config_host_mak
|
||||
@@ -7605,6 +7639,11 @@ if test "$libudev" != "no"; then
|
||||
echo "CONFIG_LIBUDEV=y" >> $config_host_mak
|
||||
echo "LIBUDEV_LIBS=$libudev_libs" >> $config_host_mak
|
||||
fi
|
||||
if test "$fuzzing" != "no"; then
|
||||
echo "CONFIG_FUZZ=y" >> $config_host_mak
|
||||
echo "FUZZ_CFLAGS=$FUZZ_CFLAGS" >> $config_host_mak
|
||||
echo "FUZZ_LDFLAGS=$FUZZ_LDFLAGS" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$edk2_blobs" = "yes" ; then
|
||||
echo "DECOMPRESS_EDK2_BLOBS=y" >> $config_host_mak
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
= Fuzzing =
|
||||
|
||||
== Introduction ==
|
||||
|
||||
This document describes the virtual-device fuzzing infrastructure in QEMU and
|
||||
how to use it to implement additional fuzzers.
|
||||
|
||||
== Basics ==
|
||||
|
||||
Fuzzing operates by passing inputs to an entry point/target function. The
|
||||
fuzzer tracks the code coverage triggered by the input. Based on these
|
||||
findings, the fuzzer mutates the input and repeats the fuzzing.
|
||||
|
||||
To fuzz QEMU, we rely on libfuzzer. Unlike other fuzzers such as AFL, libfuzzer
|
||||
is an _in-process_ fuzzer. For the developer, this means that it is their
|
||||
responsibility to ensure that state is reset between fuzzing-runs.
|
||||
|
||||
== Building the fuzzers ==
|
||||
|
||||
NOTE: If possible, build a 32-bit binary. When forking, the 32-bit fuzzer is
|
||||
much faster, since the page-map has a smaller size. This is due to the fact that
|
||||
AddressSanitizer mmaps ~20TB of memory, as part of its detection. This results
|
||||
in a large page-map, and a much slower fork().
|
||||
|
||||
To build the fuzzers, install a recent version of clang:
|
||||
Configure with (substitute the clang binaries with the version you installed):
|
||||
|
||||
CC=clang-8 CXX=clang++-8 /path/to/configure --enable-fuzzing
|
||||
|
||||
Fuzz targets are built similarly to system/softmmu:
|
||||
|
||||
make i386-softmmu/fuzz
|
||||
|
||||
This builds ./i386-softmmu/qemu-fuzz-i386
|
||||
|
||||
The first option to this command is: --fuzz_taget=FUZZ_NAME
|
||||
To list all of the available fuzzers run qemu-fuzz-i386 with no arguments.
|
||||
|
||||
eg:
|
||||
./i386-softmmu/qemu-fuzz-i386 --fuzz-target=virtio-net-fork-fuzz
|
||||
|
||||
Internally, libfuzzer parses all arguments that do not begin with "--".
|
||||
Information about these is available by passing -help=1
|
||||
|
||||
Now the only thing left to do is wait for the fuzzer to trigger potential
|
||||
crashes.
|
||||
|
||||
== Adding a new fuzzer ==
|
||||
Coverage over virtual devices can be improved by adding additional fuzzers.
|
||||
Fuzzers are kept in tests/qtest/fuzz/ and should be added to
|
||||
tests/qtest/fuzz/Makefile.include
|
||||
|
||||
Fuzzers can rely on both qtest and libqos to communicate with virtual devices.
|
||||
|
||||
1. Create a new source file. For example ``tests/qtest/fuzz/foo-device-fuzz.c``.
|
||||
|
||||
2. Write the fuzzing code using the libqtest/libqos API. See existing fuzzers
|
||||
for reference.
|
||||
|
||||
3. Register the fuzzer in ``tests/fuzz/Makefile.include`` by appending the
|
||||
corresponding object to fuzz-obj-y
|
||||
|
||||
Fuzzers can be more-or-less thought of as special qtest programs which can
|
||||
modify the qtest commands and/or qtest command arguments based on inputs
|
||||
provided by libfuzzer. Libfuzzer passes a byte array and length. Commonly the
|
||||
fuzzer loops over the byte-array interpreting it as a list of qtest commands,
|
||||
addresses, or values.
|
||||
|
||||
= Implementation Details =
|
||||
|
||||
== The Fuzzer's Lifecycle ==
|
||||
|
||||
The fuzzer has two entrypoints that libfuzzer calls. libfuzzer provides it's
|
||||
own main(), which performs some setup, and calls the entrypoints:
|
||||
|
||||
LLVMFuzzerInitialize: called prior to fuzzing. Used to initialize all of the
|
||||
necessary state
|
||||
|
||||
LLVMFuzzerTestOneInput: called for each fuzzing run. Processes the input and
|
||||
resets the state at the end of each run.
|
||||
|
||||
In more detail:
|
||||
|
||||
LLVMFuzzerInitialize parses the arguments to the fuzzer (must start with two
|
||||
dashes, so they are ignored by libfuzzer main()). Currently, the arguments
|
||||
select the fuzz target. Then, the qtest client is initialized. If the target
|
||||
requires qos, qgraph is set up and the QOM/LIBQOS modules are initialized.
|
||||
Then the QGraph is walked and the QEMU cmd_line is determined and saved.
|
||||
|
||||
After this, the vl.c:qemu__main is called to set up the guest. There are
|
||||
target-specific hooks that can be called before and after qemu_main, for
|
||||
additional setup(e.g. PCI setup, or VM snapshotting).
|
||||
|
||||
LLVMFuzzerTestOneInput: Uses qtest/qos functions to act based on the fuzz
|
||||
input. It is also responsible for manually calling the main loop/main_loop_wait
|
||||
to ensure that bottom halves are executed and any cleanup required before the
|
||||
next input.
|
||||
|
||||
Since the same process is reused for many fuzzing runs, QEMU state needs to
|
||||
be reset at the end of each run. There are currently two implemented
|
||||
options for resetting state:
|
||||
1. Reboot the guest between runs.
|
||||
Pros: Straightforward and fast for simple fuzz targets.
|
||||
Cons: Depending on the device, does not reset all device state. If the
|
||||
device requires some initialization prior to being ready for fuzzing
|
||||
(common for QOS-based targets), this initialization needs to be done after
|
||||
each reboot.
|
||||
Example target: i440fx-qtest-reboot-fuzz
|
||||
2. Run each test case in a separate forked process and copy the coverage
|
||||
information back to the parent. This is fairly similar to AFL's "deferred"
|
||||
fork-server mode [3]
|
||||
Pros: Relatively fast. Devices only need to be initialized once. No need
|
||||
to do slow reboots or vmloads.
|
||||
Cons: Not officially supported by libfuzzer. Does not work well for devices
|
||||
that rely on dedicated threads.
|
||||
Example target: virtio-net-fork-fuzz
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/tcg.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/error-report.h"
|
||||
@@ -2305,8 +2306,15 @@ static void ram_block_add(RAMBlock *new_block, Error **errp, bool shared)
|
||||
if (new_block->host) {
|
||||
qemu_ram_setup_dump(new_block->host, new_block->max_length);
|
||||
qemu_madvise(new_block->host, new_block->max_length, QEMU_MADV_HUGEPAGE);
|
||||
/* MADV_DONTFORK is also needed by KVM in absence of synchronous MMU */
|
||||
qemu_madvise(new_block->host, new_block->max_length, QEMU_MADV_DONTFORK);
|
||||
/*
|
||||
* MADV_DONTFORK is also needed by KVM in absence of synchronous MMU
|
||||
* Configure it unless the machine is a qtest server, in which case
|
||||
* KVM is not used and it may be forked (eg for fuzzing purposes).
|
||||
*/
|
||||
if (!qtest_enabled()) {
|
||||
qemu_madvise(new_block->host, new_block->max_length,
|
||||
QEMU_MADV_DONTFORK);
|
||||
}
|
||||
ram_block_notify_add(new_block->host, new_block->max_length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1272,7 +1272,7 @@ static Property virtio_blk_properties[] = {
|
||||
DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0,
|
||||
true),
|
||||
DEFINE_PROP_UINT16("num-queues", VirtIOBlock, conf.num_queues, 1),
|
||||
DEFINE_PROP_UINT16("queue-size", VirtIOBlock, conf.queue_size, 128),
|
||||
DEFINE_PROP_UINT16("queue-size", VirtIOBlock, conf.queue_size, 256),
|
||||
DEFINE_PROP_BOOL("seg-max-adjust", VirtIOBlock, conf.seg_max_adjust, true),
|
||||
DEFINE_PROP_LINK("iothread", VirtIOBlock, conf.iothread, TYPE_IOTHREAD,
|
||||
IOThread *),
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include "hw/mem/nvdimm.h"
|
||||
|
||||
GlobalProperty hw_compat_4_2[] = {
|
||||
{ "virtio-blk-device", "queue-size", "128"},
|
||||
{ "virtio-scsi-device", "virtqueue_size", "128"},
|
||||
{ "virtio-blk-device", "x-enable-wce-if-config-wce", "off" },
|
||||
{ "virtio-blk-device", "seg-max-adjust", "off"},
|
||||
{ "virtio-scsi-device", "seg_max_adjust", "off"},
|
||||
|
||||
@@ -965,7 +965,7 @@ static void virtio_scsi_device_unrealize(DeviceState *dev, Error **errp)
|
||||
static Property virtio_scsi_properties[] = {
|
||||
DEFINE_PROP_UINT32("num_queues", VirtIOSCSI, parent_obj.conf.num_queues, 1),
|
||||
DEFINE_PROP_UINT32("virtqueue_size", VirtIOSCSI,
|
||||
parent_obj.conf.virtqueue_size, 128),
|
||||
parent_obj.conf.virtqueue_size, 256),
|
||||
DEFINE_PROP_BOOL("seg_max_adjust", VirtIOSCSI,
|
||||
parent_obj.conf.seg_max_adjust, true),
|
||||
DEFINE_PROP_UINT32("max_sectors", VirtIOSCSI, parent_obj.conf.max_sectors,
|
||||
|
||||
+23
-3
@@ -42,6 +42,7 @@ void qemu_aio_unref(void *p);
|
||||
void qemu_aio_ref(void *p);
|
||||
|
||||
typedef struct AioHandler AioHandler;
|
||||
typedef QLIST_HEAD(, AioHandler) AioHandlerList;
|
||||
typedef void QEMUBHFunc(void *opaque);
|
||||
typedef bool AioPollFn(void *opaque);
|
||||
typedef void IOHandler(void *opaque);
|
||||
@@ -51,6 +52,19 @@ struct ThreadPool;
|
||||
struct LinuxAioState;
|
||||
struct LuringState;
|
||||
|
||||
/*
|
||||
* Each aio_bh_poll() call carves off a slice of the BH list, so that newly
|
||||
* scheduled BHs are not processed until the next aio_bh_poll() call. All
|
||||
* active aio_bh_poll() calls chain their slices together in a list, so that
|
||||
* nested aio_bh_poll() calls process all scheduled bottom halves.
|
||||
*/
|
||||
typedef QSLIST_HEAD(, QEMUBH) BHList;
|
||||
typedef struct BHListSlice BHListSlice;
|
||||
struct BHListSlice {
|
||||
BHList bh_list;
|
||||
QSIMPLEQ_ENTRY(BHListSlice) next;
|
||||
};
|
||||
|
||||
struct AioContext {
|
||||
GSource source;
|
||||
|
||||
@@ -58,7 +72,10 @@ struct AioContext {
|
||||
QemuRecMutex lock;
|
||||
|
||||
/* The list of registered AIO handlers. Protected by ctx->list_lock. */
|
||||
QLIST_HEAD(, AioHandler) aio_handlers;
|
||||
AioHandlerList aio_handlers;
|
||||
|
||||
/* The list of AIO handlers to be deleted. Protected by ctx->list_lock. */
|
||||
AioHandlerList deleted_aio_handlers;
|
||||
|
||||
/* Used to avoid unnecessary event_notifier_set calls in aio_notify;
|
||||
* accessed with atomic primitives. If this field is 0, everything
|
||||
@@ -91,8 +108,11 @@ struct AioContext {
|
||||
*/
|
||||
QemuLockCnt list_lock;
|
||||
|
||||
/* Anchor of the list of Bottom Halves belonging to the context */
|
||||
struct QEMUBH *first_bh;
|
||||
/* Bottom Halves pending aio_bh_poll() processing */
|
||||
BHList bh_list;
|
||||
|
||||
/* Chained BH list slices for each nested aio_bh_poll() call */
|
||||
QSIMPLEQ_HEAD(, BHListSlice) bh_slice_list;
|
||||
|
||||
/* Used by aio_notify.
|
||||
*
|
||||
|
||||
@@ -46,6 +46,7 @@ typedef enum {
|
||||
MODULE_INIT_TRACE,
|
||||
MODULE_INIT_XEN_BACKEND,
|
||||
MODULE_INIT_LIBQOS,
|
||||
MODULE_INIT_FUZZ_TARGET,
|
||||
MODULE_INIT_MAX
|
||||
} module_init_type;
|
||||
|
||||
@@ -56,7 +57,8 @@ typedef enum {
|
||||
#define xen_backend_init(function) module_init(function, \
|
||||
MODULE_INIT_XEN_BACKEND)
|
||||
#define libqos_init(function) module_init(function, MODULE_INIT_LIBQOS)
|
||||
|
||||
#define fuzz_target_init(function) module_init(function, \
|
||||
MODULE_INIT_FUZZ_TARGET)
|
||||
#define block_module_load_one(lib) module_load_one("block-", lib)
|
||||
#define ui_module_load_one(lib) module_load_one("ui-", lib)
|
||||
#define audio_module_load_one(lib) module_load_one("audio-", lib)
|
||||
|
||||
+30
-2
@@ -144,6 +144,23 @@ struct { \
|
||||
*(elm)->field.le_prev = (elm)->field.le_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
/*
|
||||
* Like QLIST_REMOVE() but safe to call when elm is not in a list
|
||||
*/
|
||||
#define QLIST_SAFE_REMOVE(elm, field) do { \
|
||||
if ((elm)->field.le_prev != NULL) { \
|
||||
if ((elm)->field.le_next != NULL) \
|
||||
(elm)->field.le_next->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = (elm)->field.le_next; \
|
||||
(elm)->field.le_next = NULL; \
|
||||
(elm)->field.le_prev = NULL; \
|
||||
} \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
/* Is elm in a list? */
|
||||
#define QLIST_IS_INSERTED(elm, field) ((elm)->field.le_prev != NULL)
|
||||
|
||||
#define QLIST_FOREACH(var, head, field) \
|
||||
for ((var) = ((head)->lh_first); \
|
||||
(var); \
|
||||
@@ -211,9 +228,20 @@ struct { \
|
||||
(head)->slh_first = (head)->slh_first->field.sle_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QSLIST_REMOVE_AFTER(slistelm, field) do { \
|
||||
#define QSLIST_REMOVE_AFTER(slistelm, field) do { \
|
||||
(slistelm)->field.sle_next = \
|
||||
QSLIST_NEXT(QSLIST_NEXT((slistelm), field), field); \
|
||||
QSLIST_NEXT(QSLIST_NEXT((slistelm), field), field); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QSLIST_REMOVE(head, elm, type, field) do { \
|
||||
if ((head)->slh_first == (elm)) { \
|
||||
QSLIST_REMOVE_HEAD((head), field); \
|
||||
} else { \
|
||||
struct type *curelm = (head)->slh_first; \
|
||||
while (curelm->field.sle_next != (elm)) \
|
||||
curelm = curelm->field.sle_next; \
|
||||
curelm->field.sle_next = curelm->field.sle_next->field.sle_next; \
|
||||
} \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QSLIST_FOREACH(var, head, field) \
|
||||
|
||||
@@ -262,6 +262,53 @@ extern "C" {
|
||||
(var) && ((next) = atomic_rcu_read(&(var)->field.tqe_next), 1); \
|
||||
(var) = (next))
|
||||
|
||||
/*
|
||||
* RCU singly-linked list
|
||||
*/
|
||||
|
||||
/* Singly-linked list access methods */
|
||||
#define QSLIST_EMPTY_RCU(head) (atomic_read(&(head)->slh_first) == NULL)
|
||||
#define QSLIST_FIRST_RCU(head) atomic_rcu_read(&(head)->slh_first)
|
||||
#define QSLIST_NEXT_RCU(elm, field) atomic_rcu_read(&(elm)->field.sle_next)
|
||||
|
||||
/* Singly-linked list functions */
|
||||
#define QSLIST_INSERT_HEAD_RCU(head, elm, field) do { \
|
||||
(elm)->field.sle_next = (head)->slh_first; \
|
||||
atomic_rcu_set(&(head)->slh_first, (elm)); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QSLIST_INSERT_AFTER_RCU(head, listelm, elm, field) do { \
|
||||
(elm)->field.sle_next = (listelm)->field.sle_next; \
|
||||
atomic_rcu_set(&(listelm)->field.sle_next, (elm)); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QSLIST_REMOVE_HEAD_RCU(head, field) do { \
|
||||
atomic_set(&(head)->slh_first, (head)->slh_first->field.sle_next); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QSLIST_REMOVE_RCU(head, elm, type, field) do { \
|
||||
if ((head)->slh_first == (elm)) { \
|
||||
QSLIST_REMOVE_HEAD_RCU((head), field); \
|
||||
} else { \
|
||||
struct type *curr = (head)->slh_first; \
|
||||
while (curr->field.sle_next != (elm)) { \
|
||||
curr = curr->field.sle_next; \
|
||||
} \
|
||||
atomic_set(&curr->field.sle_next, \
|
||||
curr->field.sle_next->field.sle_next); \
|
||||
} \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QSLIST_FOREACH_RCU(var, head, field) \
|
||||
for ((var) = atomic_rcu_read(&(head)->slh_first); \
|
||||
(var); \
|
||||
(var) = atomic_rcu_read(&(var)->field.sle_next))
|
||||
|
||||
#define QSLIST_FOREACH_SAFE_RCU(var, head, field, next) \
|
||||
for ((var) = atomic_rcu_read(&(head)->slh_first); \
|
||||
(var) && ((next) = atomic_rcu_read(&(var)->field.sle_next), 1); \
|
||||
(var) = (next))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -26,4 +26,8 @@ bool qtest_driver(void);
|
||||
|
||||
void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **errp);
|
||||
|
||||
void qtest_server_set_send_handler(void (*send)(void *, const char *),
|
||||
void *opaque);
|
||||
void qtest_server_inproc_recv(void *opaque, const char *buf);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -115,6 +115,10 @@ QemuOpts *qemu_get_machine_opts(void);
|
||||
|
||||
bool defaults_enabled(void);
|
||||
|
||||
void qemu_init(int argc, char **argv, char **envp);
|
||||
void qemu_main_loop(void);
|
||||
void qemu_cleanup(void);
|
||||
|
||||
extern QemuOptsList qemu_legacy_drive_opts;
|
||||
extern QemuOptsList qemu_common_drive_opts;
|
||||
extern QemuOptsList qemu_drive_opts;
|
||||
|
||||
@@ -43,6 +43,8 @@ static GString *inbuf;
|
||||
static int irq_levels[MAX_IRQ];
|
||||
static qemu_timeval start_time;
|
||||
static bool qtest_opened;
|
||||
static void (*qtest_server_send)(void*, const char*);
|
||||
static void *qtest_server_send_opaque;
|
||||
|
||||
#define FMT_timeval "%ld.%06ld"
|
||||
|
||||
@@ -229,8 +231,10 @@ static void GCC_FMT_ATTR(1, 2) qtest_log_send(const char *fmt, ...)
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void do_qtest_send(CharBackend *chr, const char *str, size_t len)
|
||||
static void qtest_server_char_be_send(void *opaque, const char *str)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
CharBackend* chr = (CharBackend *)opaque;
|
||||
qemu_chr_fe_write_all(chr, (uint8_t *)str, len);
|
||||
if (qtest_log_fp && qtest_opened) {
|
||||
fprintf(qtest_log_fp, "%s", str);
|
||||
@@ -239,7 +243,7 @@ static void do_qtest_send(CharBackend *chr, const char *str, size_t len)
|
||||
|
||||
static void qtest_send(CharBackend *chr, const char *str)
|
||||
{
|
||||
do_qtest_send(chr, str, strlen(str));
|
||||
qtest_server_send(qtest_server_send_opaque, str);
|
||||
}
|
||||
|
||||
static void GCC_FMT_ATTR(2, 3) qtest_sendf(CharBackend *chr,
|
||||
@@ -784,9 +788,32 @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **
|
||||
qemu_chr_fe_set_echo(&qtest_chr, true);
|
||||
|
||||
inbuf = g_string_new("");
|
||||
|
||||
if (!qtest_server_send) {
|
||||
qtest_server_set_send_handler(qtest_server_char_be_send, &qtest_chr);
|
||||
}
|
||||
}
|
||||
|
||||
void qtest_server_set_send_handler(void (*send)(void*, const char*), void *opaque)
|
||||
{
|
||||
qtest_server_send = send;
|
||||
qtest_server_send_opaque = opaque;
|
||||
}
|
||||
|
||||
bool qtest_driver(void)
|
||||
{
|
||||
return qtest_chr.chr != NULL;
|
||||
}
|
||||
|
||||
void qtest_server_inproc_recv(void *dummy, const char *buf)
|
||||
{
|
||||
static GString *gstr;
|
||||
if (!gstr) {
|
||||
gstr = g_string_new(NULL);
|
||||
}
|
||||
g_string_append(gstr, buf);
|
||||
if (gstr->str[gstr->len - 1] == '\n') {
|
||||
qtest_process_inbuf(NULL, gstr);
|
||||
g_string_truncate(gstr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@ sub top_of_kernel_tree {
|
||||
my @tree_check = (
|
||||
"COPYING", "MAINTAINERS", "Makefile",
|
||||
"README.rst", "docs", "VERSION",
|
||||
"vl.c"
|
||||
"linux-user", "softmmu"
|
||||
);
|
||||
|
||||
foreach my $check (@tree_check) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user