* thread-safe tb_flush (Fred, Alex, Sergey, me, Richard, Emilio,... :-)
* license clarification for compiler.h (Felipe) * glib cflags improvement (Marc-André) * checkpatch silencing (Paolo) * SMRAM migration fix (Paolo) * Replay improvements (Pavel) * IOMMU notifier improvements (Peter) * IOAPIC now defaults to version 0x20 (Peter) -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQExBAABCAAbBQJX6kKUFBxwYm9uemluaUByZWRoYXQuY29tAAoJEL/70l94x66D M1UIAKCQ7XfWDoClYd1TyGZ+Qj3K3TrjwLDIl/Z258euyeZ9p7PpqYQ64OCRsREJ fsGQOqkFYDe7gi4epJiJOuu4oAW7Xu8G6lB2RfBd7KWVMhsl3Che9AEom7amzyzh yoN+g9gwKfAmYwpKyjYWnlWOSjUvif6o0DaTCQCMTaAoEM3b4HKdgHfr6A2dA/E/ 47rtIVp/jNExmrZkaOjnCDS1DJ8XYT3aVeoTkuzRFQ3DBzrAiPABn6B4ExP8IBcJ YLFX/W8xG7F3qyXbKQOV/uYM25A55WS5B0G94ZfSlDtUGa/avzS7df9DFD/IWQT+ RpfiyDdeJueByiTw9R0ZYxFjhd8= =g7xm -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging * thread-safe tb_flush (Fred, Alex, Sergey, me, Richard, Emilio,... :-) * license clarification for compiler.h (Felipe) * glib cflags improvement (Marc-André) * checkpatch silencing (Paolo) * SMRAM migration fix (Paolo) * Replay improvements (Pavel) * IOMMU notifier improvements (Peter) * IOAPIC now defaults to version 0x20 (Peter) # gpg: Signature made Tue 27 Sep 2016 10:57:40 BST # gpg: using RSA key 0xBFFBD25F78C7AE83 # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * remotes/bonzini/tags/for-upstream: (28 commits) replay: allow replay stopping and restarting replay: vmstate for replay module replay: move internal data to the structure cpus-common: lock-free fast path for cpu_exec_start/end tcg: Make tb_flush() thread safe cpus-common: Introduce async_safe_run_on_cpu() cpus-common: simplify locking for start_exclusive/end_exclusive cpus-common: remove redundant call to exclusive_idle() cpus-common: always defer async_run_on_cpu work items docs: include formal model for TCG exclusive sections cpus-common: move exclusive work infrastructure from linux-user cpus-common: fix uninitialized variable use in run_on_cpu cpus-common: move CPU work item management to common code cpus-common: move CPU list management to common code linux-user: Add qemu_cpu_is_self() and qemu_cpu_kick() linux-user: Use QemuMutex and QemuCond cpus: Rename flush_queued_work() cpus: Move common code out of {async_, }run_on_cpu() cpus: pass CPUState to run_on_cpu helpers build-sys: put glib_cflags in QEMU_CFLAGS ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
c640f2849e
@ -89,7 +89,7 @@ endif
|
||||
|
||||
#######################################################################
|
||||
# Target-independent parts used in system and user emulation
|
||||
common-obj-y += tcg-runtime.o
|
||||
common-obj-y += tcg-runtime.o cpus-common.o
|
||||
common-obj-y += hw/
|
||||
common-obj-y += qom/
|
||||
common-obj-y += disas/
|
||||
|
@ -20,11 +20,6 @@ typedef struct Request {
|
||||
QEMUBH *bh;
|
||||
} Request;
|
||||
|
||||
/* Next request id.
|
||||
This counter is global, because requests from different
|
||||
block devices should not get overlapping ids. */
|
||||
static uint64_t request_id;
|
||||
|
||||
static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
@ -84,7 +79,7 @@ static void block_request_create(uint64_t reqid, BlockDriverState *bs,
|
||||
static int coroutine_fn blkreplay_co_preadv(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
uint64_t reqid = request_id++;
|
||||
uint64_t reqid = blkreplay_next_id();
|
||||
int ret = bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
|
||||
block_request_create(reqid, bs, qemu_coroutine_self());
|
||||
qemu_coroutine_yield();
|
||||
@ -95,7 +90,7 @@ static int coroutine_fn blkreplay_co_preadv(BlockDriverState *bs,
|
||||
static int coroutine_fn blkreplay_co_pwritev(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
uint64_t reqid = request_id++;
|
||||
uint64_t reqid = blkreplay_next_id();
|
||||
int ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
|
||||
block_request_create(reqid, bs, qemu_coroutine_self());
|
||||
qemu_coroutine_yield();
|
||||
@ -106,7 +101,7 @@ static int coroutine_fn blkreplay_co_pwritev(BlockDriverState *bs,
|
||||
static int coroutine_fn blkreplay_co_pwrite_zeroes(BlockDriverState *bs,
|
||||
int64_t offset, int count, BdrvRequestFlags flags)
|
||||
{
|
||||
uint64_t reqid = request_id++;
|
||||
uint64_t reqid = blkreplay_next_id();
|
||||
int ret = bdrv_co_pwrite_zeroes(bs->file, offset, count, flags);
|
||||
block_request_create(reqid, bs, qemu_coroutine_self());
|
||||
qemu_coroutine_yield();
|
||||
@ -117,7 +112,7 @@ static int coroutine_fn blkreplay_co_pwrite_zeroes(BlockDriverState *bs,
|
||||
static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs,
|
||||
int64_t offset, int count)
|
||||
{
|
||||
uint64_t reqid = request_id++;
|
||||
uint64_t reqid = blkreplay_next_id();
|
||||
int ret = bdrv_co_pdiscard(bs->file->bs, offset, count);
|
||||
block_request_create(reqid, bs, qemu_coroutine_self());
|
||||
qemu_coroutine_yield();
|
||||
@ -127,7 +122,7 @@ static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs,
|
||||
|
||||
static int coroutine_fn blkreplay_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
uint64_t reqid = request_id++;
|
||||
uint64_t reqid = blkreplay_next_id();
|
||||
int ret = bdrv_co_flush(bs->file->bs);
|
||||
block_request_create(reqid, bs, qemu_coroutine_self());
|
||||
qemu_coroutine_yield();
|
||||
|
@ -67,23 +67,6 @@ int cpu_get_pic_interrupt(CPUX86State *env)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* These are no-ops because we are not threadsafe. */
|
||||
static inline void cpu_exec_start(CPUArchState *env)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void cpu_exec_end(CPUArchState *env)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void start_exclusive(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void end_exclusive(void)
|
||||
{
|
||||
}
|
||||
|
||||
void fork_start(void)
|
||||
{
|
||||
}
|
||||
@ -95,14 +78,6 @@ void fork_end(int child)
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_list_lock(void)
|
||||
{
|
||||
}
|
||||
|
||||
void cpu_list_unlock(void)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef TARGET_I386
|
||||
/***********************************************************/
|
||||
/* CPUX86 core interface */
|
||||
@ -172,7 +147,11 @@ void cpu_loop(CPUX86State *env)
|
||||
//target_siginfo_t info;
|
||||
|
||||
for(;;) {
|
||||
cpu_exec_start(cs);
|
||||
trapnr = cpu_exec(cs);
|
||||
cpu_exec_end(cs);
|
||||
process_queued_cpu_work(cs);
|
||||
|
||||
switch(trapnr) {
|
||||
case 0x80:
|
||||
/* syscall from int $0x80 */
|
||||
@ -513,7 +492,10 @@ void cpu_loop(CPUSPARCState *env)
|
||||
//target_siginfo_t info;
|
||||
|
||||
while (1) {
|
||||
cpu_exec_start(cs);
|
||||
trapnr = cpu_exec(cs);
|
||||
cpu_exec_end(cs);
|
||||
process_queued_cpu_work(cs);
|
||||
|
||||
switch (trapnr) {
|
||||
#ifndef TARGET_SPARC64
|
||||
@ -748,6 +730,7 @@ int main(int argc, char **argv)
|
||||
if (argc <= 1)
|
||||
usage();
|
||||
|
||||
qemu_init_cpu_list();
|
||||
module_call_init(MODULE_INIT_QOM);
|
||||
|
||||
if ((envlist = envlist_create()) == NULL) {
|
||||
|
3
configure
vendored
3
configure
vendored
@ -2988,7 +2988,7 @@ for i in $glib_modules; do
|
||||
if $pkg_config --atleast-version=$glib_req_ver $i; then
|
||||
glib_cflags=$($pkg_config --cflags $i)
|
||||
glib_libs=$($pkg_config --libs $i)
|
||||
CFLAGS="$glib_cflags $CFLAGS"
|
||||
QEMU_CFLAGS="$glib_cflags $QEMU_CFLAGS"
|
||||
LIBS="$glib_libs $LIBS"
|
||||
libs_qga="$glib_libs $libs_qga"
|
||||
else
|
||||
@ -5195,7 +5195,6 @@ fi
|
||||
if test "$glib_subprocess" = "yes" ; then
|
||||
echo "CONFIG_HAS_GLIB_SUBPROCESS_TESTS=y" >> $config_host_mak
|
||||
fi
|
||||
echo "GLIB_CFLAGS=$glib_cflags" >> $config_host_mak
|
||||
if test "$gtk" = "yes" ; then
|
||||
echo "CONFIG_GTK=y" >> $config_host_mak
|
||||
echo "CONFIG_GTKABI=$gtkabi" >> $config_host_mak
|
||||
|
12
cpu-exec.c
12
cpu-exec.c
@ -204,20 +204,16 @@ static void cpu_exec_nocache(CPUState *cpu, int max_cycles,
|
||||
TranslationBlock *orig_tb, bool ignore_icount)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
bool old_tb_flushed;
|
||||
|
||||
/* Should never happen.
|
||||
We only end up here when an existing TB is too long. */
|
||||
if (max_cycles > CF_COUNT_MASK)
|
||||
max_cycles = CF_COUNT_MASK;
|
||||
|
||||
old_tb_flushed = cpu->tb_flushed;
|
||||
cpu->tb_flushed = false;
|
||||
tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, orig_tb->flags,
|
||||
max_cycles | CF_NOCACHE
|
||||
| (ignore_icount ? CF_IGNORE_ICOUNT : 0));
|
||||
tb->orig_tb = cpu->tb_flushed ? NULL : orig_tb;
|
||||
cpu->tb_flushed |= old_tb_flushed;
|
||||
tb->orig_tb = orig_tb;
|
||||
/* execute the generated code */
|
||||
trace_exec_tb_nocache(tb, tb->pc);
|
||||
cpu_tb_exec(cpu, tb);
|
||||
@ -338,10 +334,7 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
|
||||
tb_lock();
|
||||
have_tb_lock = true;
|
||||
}
|
||||
/* Check if translation buffer has been flushed */
|
||||
if (cpu->tb_flushed) {
|
||||
cpu->tb_flushed = false;
|
||||
} else if (!tb->invalid) {
|
||||
if (!tb->invalid) {
|
||||
tb_add_jump(last_tb, tb_exit, tb);
|
||||
}
|
||||
}
|
||||
@ -606,7 +599,6 @@ int cpu_exec(CPUState *cpu)
|
||||
break;
|
||||
}
|
||||
|
||||
atomic_mb_set(&cpu->tb_flushed, false); /* reset before first TB lookup */
|
||||
for(;;) {
|
||||
cpu_handle_interrupt(cpu, &last_tb);
|
||||
tb = tb_find(cpu, last_tb, tb_exit);
|
||||
|
352
cpus-common.c
Normal file
352
cpus-common.c
Normal file
@ -0,0 +1,352 @@
|
||||
/*
|
||||
* CPU thread main loop - common bits for user and system mode emulation
|
||||
*
|
||||
* Copyright (c) 2003-2005 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "exec/cpu-common.h"
|
||||
#include "qom/cpu.h"
|
||||
#include "sysemu/cpus.h"
|
||||
|
||||
static QemuMutex qemu_cpu_list_lock;
|
||||
static QemuCond exclusive_cond;
|
||||
static QemuCond exclusive_resume;
|
||||
static QemuCond qemu_work_cond;
|
||||
|
||||
/* >= 1 if a thread is inside start_exclusive/end_exclusive. Written
|
||||
* under qemu_cpu_list_lock, read with atomic operations.
|
||||
*/
|
||||
static int pending_cpus;
|
||||
|
||||
void qemu_init_cpu_list(void)
|
||||
{
|
||||
/* This is needed because qemu_init_cpu_list is also called by the
|
||||
* child process in a fork. */
|
||||
pending_cpus = 0;
|
||||
|
||||
qemu_mutex_init(&qemu_cpu_list_lock);
|
||||
qemu_cond_init(&exclusive_cond);
|
||||
qemu_cond_init(&exclusive_resume);
|
||||
qemu_cond_init(&qemu_work_cond);
|
||||
}
|
||||
|
||||
void cpu_list_lock(void)
|
||||
{
|
||||
qemu_mutex_lock(&qemu_cpu_list_lock);
|
||||
}
|
||||
|
||||
void cpu_list_unlock(void)
|
||||
{
|
||||
qemu_mutex_unlock(&qemu_cpu_list_lock);
|
||||
}
|
||||
|
||||
static bool cpu_index_auto_assigned;
|
||||
|
||||
static int cpu_get_free_index(void)
|
||||
{
|
||||
CPUState *some_cpu;
|
||||
int cpu_index = 0;
|
||||
|
||||
cpu_index_auto_assigned = true;
|
||||
CPU_FOREACH(some_cpu) {
|
||||
cpu_index++;
|
||||
}
|
||||
return cpu_index;
|
||||
}
|
||||
|
||||
static void finish_safe_work(CPUState *cpu)
|
||||
{
|
||||
cpu_exec_start(cpu);
|
||||
cpu_exec_end(cpu);
|
||||
}
|
||||
|
||||
void cpu_list_add(CPUState *cpu)
|
||||
{
|
||||
qemu_mutex_lock(&qemu_cpu_list_lock);
|
||||
if (cpu->cpu_index == UNASSIGNED_CPU_INDEX) {
|
||||
cpu->cpu_index = cpu_get_free_index();
|
||||
assert(cpu->cpu_index != UNASSIGNED_CPU_INDEX);
|
||||
} else {
|
||||
assert(!cpu_index_auto_assigned);
|
||||
}
|
||||
QTAILQ_INSERT_TAIL(&cpus, cpu, node);
|
||||
qemu_mutex_unlock(&qemu_cpu_list_lock);
|
||||
|
||||
finish_safe_work(cpu);
|
||||
}
|
||||
|
||||
void cpu_list_remove(CPUState *cpu)
|
||||
{
|
||||
qemu_mutex_lock(&qemu_cpu_list_lock);
|
||||
if (!QTAILQ_IN_USE(cpu, node)) {
|
||||
/* there is nothing to undo since cpu_exec_init() hasn't been called */
|
||||
qemu_mutex_unlock(&qemu_cpu_list_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(!(cpu_index_auto_assigned && cpu != QTAILQ_LAST(&cpus, CPUTailQ)));
|
||||
|
||||
QTAILQ_REMOVE(&cpus, cpu, node);
|
||||
cpu->cpu_index = UNASSIGNED_CPU_INDEX;
|
||||
qemu_mutex_unlock(&qemu_cpu_list_lock);
|
||||
}
|
||||
|
||||
struct qemu_work_item {
|
||||
struct qemu_work_item *next;
|
||||
run_on_cpu_func func;
|
||||
void *data;
|
||||
bool free, exclusive, done;
|
||||
};
|
||||
|
||||
static void queue_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi)
|
||||
{
|
||||
qemu_mutex_lock(&cpu->work_mutex);
|
||||
if (cpu->queued_work_first == NULL) {
|
||||
cpu->queued_work_first = wi;
|
||||
} else {
|
||||
cpu->queued_work_last->next = wi;
|
||||
}
|
||||
cpu->queued_work_last = wi;
|
||||
wi->next = NULL;
|
||||
wi->done = false;
|
||||
qemu_mutex_unlock(&cpu->work_mutex);
|
||||
|
||||
qemu_cpu_kick(cpu);
|
||||
}
|
||||
|
||||
void do_run_on_cpu(CPUState *cpu, run_on_cpu_func func, void *data,
|
||||
QemuMutex *mutex)
|
||||
{
|
||||
struct qemu_work_item wi;
|
||||
|
||||
if (qemu_cpu_is_self(cpu)) {
|
||||
func(cpu, data);
|
||||
return;
|
||||
}
|
||||
|
||||
wi.func = func;
|
||||
wi.data = data;
|
||||
wi.done = false;
|
||||
wi.free = false;
|
||||
wi.exclusive = false;
|
||||
|
||||
queue_work_on_cpu(cpu, &wi);
|
||||
while (!atomic_mb_read(&wi.done)) {
|
||||
CPUState *self_cpu = current_cpu;
|
||||
|
||||
qemu_cond_wait(&qemu_work_cond, mutex);
|
||||
current_cpu = self_cpu;
|
||||
}
|
||||
}
|
||||
|
||||
void async_run_on_cpu(CPUState *cpu, run_on_cpu_func func, void *data)
|
||||
{
|
||||
struct qemu_work_item *wi;
|
||||
|
||||
wi = g_malloc0(sizeof(struct qemu_work_item));
|
||||
wi->func = func;
|
||||
wi->data = data;
|
||||
wi->free = true;
|
||||
|
||||
queue_work_on_cpu(cpu, wi);
|
||||
}
|
||||
|
||||
/* Wait for pending exclusive operations to complete. The CPU list lock
|
||||
must be held. */
|
||||
static inline void exclusive_idle(void)
|
||||
{
|
||||
while (pending_cpus) {
|
||||
qemu_cond_wait(&exclusive_resume, &qemu_cpu_list_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* Start an exclusive operation.
|
||||
Must only be called from outside cpu_exec. */
|
||||
void start_exclusive(void)
|
||||
{
|
||||
CPUState *other_cpu;
|
||||
int running_cpus;
|
||||
|
||||
qemu_mutex_lock(&qemu_cpu_list_lock);
|
||||
exclusive_idle();
|
||||
|
||||
/* Make all other cpus stop executing. */
|
||||
atomic_set(&pending_cpus, 1);
|
||||
|
||||
/* Write pending_cpus before reading other_cpu->running. */
|
||||
smp_mb();
|
||||
running_cpus = 0;
|
||||
CPU_FOREACH(other_cpu) {
|
||||
if (atomic_read(&other_cpu->running)) {
|
||||
other_cpu->has_waiter = true;
|
||||
running_cpus++;
|
||||
qemu_cpu_kick(other_cpu);
|
||||
}
|
||||
}
|
||||
|
||||
atomic_set(&pending_cpus, running_cpus + 1);
|
||||
while (pending_cpus > 1) {
|
||||
qemu_cond_wait(&exclusive_cond, &qemu_cpu_list_lock);
|
||||
}
|
||||
|
||||
/* Can release mutex, no one will enter another exclusive
|
||||
* section until end_exclusive resets pending_cpus to 0.
|
||||
*/
|
||||
qemu_mutex_unlock(&qemu_cpu_list_lock);
|
||||
}
|
||||
|
||||
/* Finish an exclusive operation. */
|
||||
void end_exclusive(void)
|
||||
{
|
||||
qemu_mutex_lock(&qemu_cpu_list_lock);
|
||||
atomic_set(&pending_cpus, 0);
|
||||
qemu_cond_broadcast(&exclusive_resume);
|
||||
qemu_mutex_unlock(&qemu_cpu_list_lock);
|
||||
}
|
||||
|
||||
/* Wait for exclusive ops to finish, and begin cpu execution. */
|
||||
void cpu_exec_start(CPUState *cpu)
|
||||
{
|
||||
atomic_set(&cpu->running, true);
|
||||
|
||||
/* Write cpu->running before reading pending_cpus. */
|
||||
smp_mb();
|
||||
|
||||
/* 1. start_exclusive saw cpu->running == true and pending_cpus >= 1.
|
||||
* After taking the lock we'll see cpu->has_waiter == true and run---not
|
||||
* for long because start_exclusive kicked us. cpu_exec_end will
|
||||
* decrement pending_cpus and signal the waiter.
|
||||
*
|
||||
* 2. start_exclusive saw cpu->running == false but pending_cpus >= 1.
|
||||
* This includes the case when an exclusive item is running now.
|
||||
* Then we'll see cpu->has_waiter == false and wait for the item to
|
||||
* complete.
|
||||
*
|
||||
* 3. pending_cpus == 0. Then start_exclusive is definitely going to
|
||||
* see cpu->running == true, and it will kick the CPU.
|
||||
*/
|
||||
if (unlikely(atomic_read(&pending_cpus))) {
|
||||
qemu_mutex_lock(&qemu_cpu_list_lock);
|
||||
if (!cpu->has_waiter) {
|
||||
/* Not counted in pending_cpus, let the exclusive item
|
||||
* run. Since we have the lock, just set cpu->running to true
|
||||
* while holding it; no need to check pending_cpus again.
|
||||
*/
|
||||
atomic_set(&cpu->running, false);
|
||||
exclusive_idle();
|
||||
/* Now pending_cpus is zero. */
|
||||
atomic_set(&cpu->running, true);
|
||||
} else {
|
||||
/* Counted in pending_cpus, go ahead and release the
|
||||
* waiter at cpu_exec_end.
|
||||
*/
|
||||
}
|
||||
qemu_mutex_unlock(&qemu_cpu_list_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark cpu as not executing, and release pending exclusive ops. */
|
||||
void cpu_exec_end(CPUState *cpu)
|
||||
{
|
||||
atomic_set(&cpu->running, false);
|
||||
|
||||
/* Write cpu->running before reading pending_cpus. */
|
||||
smp_mb();
|
||||
|
||||
/* 1. start_exclusive saw cpu->running == true. Then it will increment
|
||||
* pending_cpus and wait for exclusive_cond. After taking the lock
|
||||
* we'll see cpu->has_waiter == true.
|
||||
*
|
||||
* 2. start_exclusive saw cpu->running == false but here pending_cpus >= 1.
|
||||
* This includes the case when an exclusive item started after setting
|
||||
* cpu->running to false and before we read pending_cpus. Then we'll see
|
||||
* cpu->has_waiter == false and not touch pending_cpus. The next call to
|
||||
* cpu_exec_start will run exclusive_idle if still necessary, thus waiting
|
||||
* for the item to complete.
|
||||
*
|
||||
* 3. pending_cpus == 0. Then start_exclusive is definitely going to
|
||||
* see cpu->running == false, and it can ignore this CPU until the
|
||||
* next cpu_exec_start.
|
||||
*/
|
||||
if (unlikely(atomic_read(&pending_cpus))) {
|
||||
qemu_mutex_lock(&qemu_cpu_list_lock);
|
||||
if (cpu->has_waiter) {
|
||||
cpu->has_waiter = false;
|
||||
atomic_set(&pending_cpus, pending_cpus - 1);
|
||||
if (pending_cpus == 1) {
|
||||
qemu_cond_signal(&exclusive_cond);
|
||||
}
|
||||
}
|
||||
qemu_mutex_unlock(&qemu_cpu_list_lock);
|
||||
}
|
||||
}
|
||||
|
||||
void async_safe_run_on_cpu(CPUState *cpu, run_on_cpu_func func, void *data)
|
||||
{
|
||||
struct qemu_work_item *wi;
|
||||
|
||||
wi = g_malloc0(sizeof(struct qemu_work_item));
|
||||
wi->func = func;
|
||||
wi->data = data;
|
||||
wi->free = true;
|
||||
wi->exclusive = true;
|
||||
|
||||
queue_work_on_cpu(cpu, wi);
|
||||
}
|
||||
|
||||
void process_queued_cpu_work(CPUState *cpu)
|
||||
{
|
||||
struct qemu_work_item *wi;
|
||||
|
||||
if (cpu->queued_work_first == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_mutex_lock(&cpu->work_mutex);
|
||||
while (cpu->queued_work_first != NULL) {
|
||||
wi = cpu->queued_work_first;
|
||||
cpu->queued_work_first = wi->next;
|
||||
if (!cpu->queued_work_first) {
|
||||
cpu->queued_work_last = NULL;
|
||||
}
|
||||
qemu_mutex_unlock(&cpu->work_mutex);
|
||||
if (wi->exclusive) {
|
||||
/* Running work items outside the BQL avoids the following deadlock:
|
||||
* 1) start_exclusive() is called with the BQL taken while another
|
||||
* CPU is running; 2) cpu_exec in the other CPU tries to takes the
|
||||
* BQL, so it goes to sleep; start_exclusive() is sleeping too, so
|
||||
* neither CPU can proceed.
|
||||
*/
|
||||
qemu_mutex_unlock_iothread();
|
||||
start_exclusive();
|
||||
wi->func(cpu, wi->data);
|
||||
end_exclusive();
|
||||
qemu_mutex_lock_iothread();
|
||||
} else {
|
||||
wi->func(cpu, wi->data);
|
||||
}
|
||||
qemu_mutex_lock(&cpu->work_mutex);
|
||||
if (wi->free) {
|
||||
g_free(wi);
|
||||
} else {
|
||||
atomic_mb_set(&wi->done, true);
|
||||
}
|
||||
}
|
||||
qemu_mutex_unlock(&cpu->work_mutex);
|
||||
qemu_cond_broadcast(&qemu_work_cond);
|
||||
}
|
100
cpus.c
100
cpus.c
@ -557,9 +557,8 @@ static const VMStateDescription vmstate_timers = {
|
||||
}
|
||||
};
|
||||
|
||||
static void cpu_throttle_thread(void *opaque)
|
||||
static void cpu_throttle_thread(CPUState *cpu, void *opaque)
|
||||
{
|
||||
CPUState *cpu = opaque;
|
||||
double pct;
|
||||
double throttle_ratio;
|
||||
long sleeptime_ns;
|
||||
@ -589,7 +588,7 @@ static void cpu_throttle_timer_tick(void *opaque)
|
||||
}
|
||||
CPU_FOREACH(cpu) {
|
||||
if (!atomic_xchg(&cpu->throttle_thread_scheduled, 1)) {
|
||||
async_run_on_cpu(cpu, cpu_throttle_thread, cpu);
|
||||
async_run_on_cpu(cpu, cpu_throttle_thread, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@ -751,6 +750,7 @@ static int do_vm_stop(RunState state)
|
||||
}
|
||||
|
||||
bdrv_drain_all();
|
||||
replay_disable_events();
|
||||
ret = blk_flush_all();
|
||||
|
||||
return ret;
|
||||
@ -903,79 +903,21 @@ static QemuThread io_thread;
|
||||
static QemuCond qemu_cpu_cond;
|
||||
/* system init */
|
||||
static QemuCond qemu_pause_cond;
|
||||
static QemuCond qemu_work_cond;
|
||||
|
||||
void qemu_init_cpu_loop(void)
|
||||
{
|
||||
qemu_init_sigbus();
|
||||
qemu_cond_init(&qemu_cpu_cond);
|
||||
qemu_cond_init(&qemu_pause_cond);
|
||||
qemu_cond_init(&qemu_work_cond);
|
||||
qemu_cond_init(&qemu_io_proceeded_cond);
|
||||
qemu_mutex_init(&qemu_global_mutex);
|
||||
|
||||
qemu_thread_get_self(&io_thread);
|
||||
}
|
||||
|
||||
void run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data)
|
||||
void run_on_cpu(CPUState *cpu, run_on_cpu_func func, void *data)
|
||||
{
|
||||
struct qemu_work_item wi;
|
||||
|
||||
if (qemu_cpu_is_self(cpu)) {
|
||||
func(data);
|
||||
return;
|
||||
}
|
||||
|
||||
wi.func = func;
|
||||
wi.data = data;
|
||||
wi.free = false;
|
||||
|
||||
qemu_mutex_lock(&cpu->work_mutex);
|
||||
if (cpu->queued_work_first == NULL) {
|
||||
cpu->queued_work_first = &wi;
|
||||
} else {
|
||||
cpu->queued_work_last->next = &wi;
|
||||
}
|
||||
cpu->queued_work_last = &wi;
|
||||
wi.next = NULL;
|
||||
wi.done = false;
|
||||
qemu_mutex_unlock(&cpu->work_mutex);
|
||||
|
||||
qemu_cpu_kick(cpu);
|
||||
while (!atomic_mb_read(&wi.done)) {
|
||||
CPUState *self_cpu = current_cpu;
|
||||
|
||||
qemu_cond_wait(&qemu_work_cond, &qemu_global_mutex);
|
||||
current_cpu = self_cpu;
|
||||
}
|
||||
}
|
||||
|
||||
void async_run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data)
|
||||
{
|
||||
struct qemu_work_item *wi;
|
||||
|
||||
if (qemu_cpu_is_self(cpu)) {
|
||||
func(data);
|
||||
return;
|
||||
}
|
||||
|
||||
wi = g_malloc0(sizeof(struct qemu_work_item));
|
||||
wi->func = func;
|
||||
wi->data = data;
|
||||
wi->free = true;
|
||||
|
||||
qemu_mutex_lock(&cpu->work_mutex);
|
||||
if (cpu->queued_work_first == NULL) {
|
||||
cpu->queued_work_first = wi;
|
||||
} else {
|
||||
cpu->queued_work_last->next = wi;
|
||||
}
|
||||
cpu->queued_work_last = wi;
|
||||
wi->next = NULL;
|
||||
wi->done = false;
|
||||
qemu_mutex_unlock(&cpu->work_mutex);
|
||||
|
||||
qemu_cpu_kick(cpu);
|
||||
do_run_on_cpu(cpu, func, data, &qemu_global_mutex);
|
||||
}
|
||||
|
||||
static void qemu_kvm_destroy_vcpu(CPUState *cpu)
|
||||
@ -990,34 +932,6 @@ static void qemu_tcg_destroy_vcpu(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
|
||||
static void flush_queued_work(CPUState *cpu)
|
||||
{
|
||||
struct qemu_work_item *wi;
|
||||
|
||||
if (cpu->queued_work_first == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_mutex_lock(&cpu->work_mutex);
|
||||
while (cpu->queued_work_first != NULL) {
|
||||
wi = cpu->queued_work_first;
|
||||
cpu->queued_work_first = wi->next;
|
||||
if (!cpu->queued_work_first) {
|
||||
cpu->queued_work_last = NULL;
|
||||
}
|
||||
qemu_mutex_unlock(&cpu->work_mutex);
|
||||
wi->func(wi->data);
|
||||
qemu_mutex_lock(&cpu->work_mutex);
|
||||
if (wi->free) {
|
||||
g_free(wi);
|
||||
} else {
|
||||
atomic_mb_set(&wi->done, true);
|
||||
}
|
||||
}
|
||||
qemu_mutex_unlock(&cpu->work_mutex);
|
||||
qemu_cond_broadcast(&qemu_work_cond);
|
||||
}
|
||||
|
||||
static void qemu_wait_io_event_common(CPUState *cpu)
|
||||
{
|
||||
if (cpu->stop) {
|
||||
@ -1025,7 +939,7 @@ static void qemu_wait_io_event_common(CPUState *cpu)
|
||||
cpu->stopped = true;
|
||||
qemu_cond_broadcast(&qemu_pause_cond);
|
||||
}
|
||||
flush_queued_work(cpu);
|
||||
process_queued_cpu_work(cpu);
|
||||
cpu->thread_kicked = false;
|
||||
}
|
||||
|
||||
@ -1544,7 +1458,9 @@ static int tcg_cpu_exec(CPUState *cpu)
|
||||
cpu->icount_decr.u16.low = decr;
|
||||
cpu->icount_extra = count;
|
||||
}
|
||||
cpu_exec_start(cpu);
|
||||
ret = cpu_exec(cpu);
|
||||
cpu_exec_end(cpu);
|
||||
#ifdef CONFIG_PROFILER
|
||||
tcg_time += profile_getclock() - ti;
|
||||
#endif
|
||||
|
225
docs/tcg-exclusive.promela
Normal file
225
docs/tcg-exclusive.promela
Normal file
@ -0,0 +1,225 @@
|
||||
/*
|
||||
* This model describes the implementation of exclusive sections in
|
||||
* cpus-common.c (start_exclusive, end_exclusive, cpu_exec_start,
|
||||
* cpu_exec_end).
|
||||
*
|
||||
* Author: Paolo Bonzini <pbonzini@redhat.com>
|
||||
*
|
||||
* This file is in the public domain. If you really want a license,
|
||||
* the WTFPL will do.
|
||||
*
|
||||
* To verify it:
|
||||
* spin -a docs/tcg-exclusive.promela
|
||||
* gcc pan.c -O2
|
||||
* ./a.out -a
|
||||
*
|
||||
* Tunable processor macros: N_CPUS, N_EXCLUSIVE, N_CYCLES, USE_MUTEX,
|
||||
* TEST_EXPENSIVE.
|
||||
*/
|
||||
|
||||
// Define the missing parameters for the model
|
||||
#ifndef N_CPUS
|
||||
#define N_CPUS 2
|
||||
#warning defaulting to 2 CPU processes
|
||||
#endif
|
||||
|
||||
// the expensive test is not so expensive for <= 2 CPUs
|
||||
// If the mutex is used, it's also cheap (300 MB / 4 seconds) for 3 CPUs
|
||||
// For 3 CPUs and the lock-free option it needs 1.5 GB of RAM
|
||||
#if N_CPUS <= 2 || (N_CPUS <= 3 && defined USE_MUTEX)
|
||||
#define TEST_EXPENSIVE
|
||||
#endif
|
||||
|
||||
#ifndef N_EXCLUSIVE
|
||||
# if !defined N_CYCLES || N_CYCLES <= 1 || defined TEST_EXPENSIVE
|
||||
# define N_EXCLUSIVE 2
|
||||
# warning defaulting to 2 concurrent exclusive sections
|
||||
# else
|
||||
# define N_EXCLUSIVE 1
|
||||
# warning defaulting to 1 concurrent exclusive sections
|
||||
# endif
|
||||
#endif
|
||||
#ifndef N_CYCLES
|
||||
# if N_EXCLUSIVE <= 1 || defined TEST_EXPENSIVE
|
||||
# define N_CYCLES 2
|
||||
# warning defaulting to 2 CPU cycles
|
||||
# else
|
||||
# define N_CYCLES 1
|
||||
# warning defaulting to 1 CPU cycles
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
// synchronization primitives. condition variables require a
|
||||
// process-local "cond_t saved;" variable.
|
||||
|
||||
#define mutex_t byte
|
||||
#define MUTEX_LOCK(m) atomic { m == 0 -> m = 1 }
|
||||
#define MUTEX_UNLOCK(m) m = 0
|
||||
|
||||
#define cond_t int
|
||||
#define COND_WAIT(c, m) { \
|
||||
saved = c; \
|
||||
MUTEX_UNLOCK(m); \
|
||||
c != saved -> MUTEX_LOCK(m); \
|
||||
}
|
||||
#define COND_BROADCAST(c) c++
|
||||
|
||||
// this is the logic from cpus-common.c
|
||||
|
||||
mutex_t mutex;
|
||||
cond_t exclusive_cond;
|
||||
cond_t exclusive_resume;
|
||||
byte pending_cpus;
|
||||
|
||||
byte running[N_CPUS];
|
||||
byte has_waiter[N_CPUS];
|
||||
|
||||
#define exclusive_idle() \
|
||||
do \
|
||||
:: pending_cpus -> COND_WAIT(exclusive_resume, mutex); \
|
||||
:: else -> break; \
|
||||
od
|
||||
|
||||
#define start_exclusive() \
|
||||
MUTEX_LOCK(mutex); \
|
||||
exclusive_idle(); \
|
||||
pending_cpus = 1; \
|
||||
\
|
||||
i = 0; \
|
||||
do \
|
||||
:: i < N_CPUS -> { \
|
||||
if \
|
||||
:: running[i] -> has_waiter[i] = 1; pending_cpus++; \
|
||||
:: else -> skip; \
|
||||
fi; \
|
||||
i++; \
|
||||
} \
|
||||
:: else -> break; \
|
||||
od; \
|
||||
\
|
||||
do \
|
||||
:: pending_cpus > 1 -> COND_WAIT(exclusive_cond, mutex); \
|
||||
:: else -> break; \
|
||||
od; \
|
||||
MUTEX_UNLOCK(mutex);
|
||||
|
||||
#define end_exclusive() \
|
||||
MUTEX_LOCK(mutex); \
|
||||
pending_cpus = 0; \
|
||||
COND_BROADCAST(exclusive_resume); \
|
||||
MUTEX_UNLOCK(mutex);
|
||||
|
||||
#ifdef USE_MUTEX
|
||||
// Simple version using mutexes
|
||||
#define cpu_exec_start(id) \
|
||||
MUTEX_LOCK(mutex); \
|
||||
exclusive_idle(); \
|
||||
running[id] = 1; \
|
||||
MUTEX_UNLOCK(mutex);
|
||||
|
||||
#define cpu_exec_end(id) \
|
||||
MUTEX_LOCK(mutex); \
|
||||
running[id] = 0; \
|
||||
if \
|
||||
:: pending_cpus -> { \
|
||||
pending_cpus--; \
|
||||
if \
|
||||
:: pending_cpus == 1 -> COND_BROADCAST(exclusive_cond); \
|
||||
:: else -> skip; \
|
||||
fi; \
|
||||
} \
|
||||
:: else -> skip; \
|
||||
fi; \
|
||||
MUTEX_UNLOCK(mutex);
|
||||
#else
|
||||
// Wait-free fast path, only needs mutex when concurrent with
|
||||
// an exclusive section
|
||||
#define cpu_exec_start(id) \
|
||||
running[id] = 1; \
|
||||
if \
|
||||
:: pending_cpus -> { \
|
||||
MUTEX_LOCK(mutex); \
|
||||
if \
|
||||
:: !has_waiter[id] -> { \
|
||||
running[id] = 0; \
|
||||
exclusive_idle(); \
|
||||
running[id] = 1; \
|
||||
} \
|
||||
:: else -> skip; \
|
||||
fi; \
|
||||
MUTEX_UNLOCK(mutex); \
|
||||
} \
|
||||
:: else -> skip; \
|
||||
fi;
|
||||
|
||||
#define cpu_exec_end(id) \
|
||||
running[id] = 0; \
|
||||
if \
|
||||
:: pending_cpus -> { \
|
||||
MUTEX_LOCK(mutex); \
|
||||
if \
|
||||
:: has_waiter[id] -> { \
|
||||
has_waiter[id] = 0; \
|
||||
pending_cpus--; \
|
||||
if \
|
||||
:: pending_cpus == 1 -> COND_BROADCAST(exclusive_cond); \
|
||||
:: else -> skip; \
|
||||
fi; \
|
||||
} \
|
||||
:: else -> skip; \
|
||||
fi; \
|
||||
MUTEX_UNLOCK(mutex); \
|
||||
} \
|
||||
:: else -> skip; \
|
||||
fi
|
||||
#endif
|
||||
|
||||
// Promela processes
|
||||
|
||||
byte done_cpu;
|
||||
byte in_cpu;
|
||||
active[N_CPUS] proctype cpu()
|
||||
{
|
||||
byte id = _pid % N_CPUS;
|
||||
byte cycles = 0;
|
||||
cond_t saved;
|
||||
|
||||
do
|
||||
:: cycles == N_CYCLES -> break;
|
||||
:: else -> {
|
||||
cycles++;
|
||||
cpu_exec_start(id)
|
||||
in_cpu++;
|
||||
done_cpu++;
|
||||
in_cpu--;
|
||||
cpu_exec_end(id)
|
||||
}
|
||||
od;
|
||||
}
|
||||
|
||||
byte done_exclusive;
|
||||
byte in_exclusive;
|
||||
active[N_EXCLUSIVE] proctype exclusive()
|
||||
{
|
||||
cond_t saved;
|
||||
byte i;
|
||||
|
||||
start_exclusive();
|
||||
in_exclusive = 1;
|
||||
done_exclusive++;
|
||||
in_exclusive = 0;
|
||||
end_exclusive();
|
||||
}
|
||||
|
||||
#define LIVENESS (done_cpu == N_CPUS * N_CYCLES && done_exclusive == N_EXCLUSIVE)
|
||||
#define SAFETY !(in_exclusive && in_cpu)
|
||||
|
||||
never { /* ! ([] SAFETY && <> [] LIVENESS) */
|
||||
do
|
||||
// once the liveness property is satisfied, this is not executable
|
||||
// and the never clause is not accepted
|
||||
:: ! LIVENESS -> accept_liveness: skip
|
||||
:: 1 -> assert(SAFETY)
|
||||
od;
|
||||
}
|
37
exec.c
37
exec.c
@ -598,36 +598,11 @@ AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx)
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool cpu_index_auto_assigned;
|
||||
|
||||
static int cpu_get_free_index(void)
|
||||
{
|
||||
CPUState *some_cpu;
|
||||
int cpu_index = 0;
|
||||
|
||||
cpu_index_auto_assigned = true;
|
||||
CPU_FOREACH(some_cpu) {
|
||||
cpu_index++;
|
||||
}
|
||||
return cpu_index;
|
||||
}
|
||||
|
||||
void cpu_exec_exit(CPUState *cpu)
|
||||
{
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
|
||||
cpu_list_lock();
|
||||
if (!QTAILQ_IN_USE(cpu, node)) {
|
||||
/* there is nothing to undo since cpu_exec_init() hasn't been called */
|
||||
cpu_list_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
assert(!(cpu_index_auto_assigned && cpu != QTAILQ_LAST(&cpus, CPUTailQ)));
|
||||
|
||||
QTAILQ_REMOVE(&cpus, cpu, node);
|
||||
cpu->cpu_index = UNASSIGNED_CPU_INDEX;
|
||||
cpu_list_unlock();
|
||||
cpu_list_remove(cpu);
|
||||
|
||||
if (cc->vmsd != NULL) {
|
||||
vmstate_unregister(NULL, cc->vmsd, cpu);
|
||||
@ -663,15 +638,7 @@ void cpu_exec_init(CPUState *cpu, Error **errp)
|
||||
object_ref(OBJECT(cpu->memory));
|
||||
#endif
|
||||
|
||||
cpu_list_lock();
|
||||
if (cpu->cpu_index == UNASSIGNED_CPU_INDEX) {
|
||||
cpu->cpu_index = cpu_get_free_index();
|
||||
assert(cpu->cpu_index != UNASSIGNED_CPU_INDEX);
|
||||
} else {
|
||||
assert(!cpu_index_auto_assigned);
|
||||
}
|
||||
QTAILQ_INSERT_TAIL(&cpus, cpu, node);
|
||||
cpu_list_unlock();
|
||||
cpu_list_add(cpu);
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (qdev_get_vmsd(DEVICE(cpu)) == NULL) {
|
||||
|
@ -21,6 +21,7 @@
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/i386/amd_iommu.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "trace.h"
|
||||
|
||||
/* used AMD-Vi MMIO registers */
|
||||
@ -1066,13 +1067,18 @@ static const MemoryRegionOps mmio_mem_ops = {
|
||||
}
|
||||
};
|
||||
|
||||
static void amdvi_iommu_notify_started(MemoryRegion *iommu)
|
||||
static void amdvi_iommu_notify_flag_changed(MemoryRegion *iommu,
|
||||
IOMMUNotifierFlag old,
|
||||
IOMMUNotifierFlag new)
|
||||
{
|
||||
AMDVIAddressSpace *as = container_of(iommu, AMDVIAddressSpace, iommu);
|
||||
|
||||
hw_error("device %02x.%02x.%x requires iommu notifier which is not "
|
||||
"currently supported", as->bus_num, PCI_SLOT(as->devfn),
|
||||
PCI_FUNC(as->devfn));
|
||||
if (new & IOMMU_NOTIFIER_MAP) {
|
||||
error_report("device %02x.%02x.%x requires iommu notifier which is not "
|
||||
"currently supported", as->bus_num, PCI_SLOT(as->devfn),
|
||||
PCI_FUNC(as->devfn));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void amdvi_init(AMDVIState *s)
|
||||
@ -1080,7 +1086,7 @@ static void amdvi_init(AMDVIState *s)
|
||||
amdvi_iotlb_reset(s);
|
||||
|
||||
s->iommu_ops.translate = amdvi_translate;
|
||||
s->iommu_ops.notify_started = amdvi_iommu_notify_started;
|
||||
s->iommu_ops.notify_flag_changed = amdvi_iommu_notify_flag_changed;
|
||||
s->devtab_len = 0;
|
||||
s->cmdbuf_len = 0;
|
||||
s->cmdbuf_head = 0;
|
||||
|
@ -1974,14 +1974,20 @@ static IOMMUTLBEntry vtd_iommu_translate(MemoryRegion *iommu, hwaddr addr,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vtd_iommu_notify_started(MemoryRegion *iommu)
|
||||
static void vtd_iommu_notify_flag_changed(MemoryRegion *iommu,
|
||||
IOMMUNotifierFlag old,
|
||||
IOMMUNotifierFlag new)
|
||||
{
|
||||
VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu);
|
||||
|
||||
hw_error("Device at bus %s addr %02x.%d requires iommu notifier which "
|
||||
"is currently not supported by intel-iommu emulation",
|
||||
vtd_as->bus->qbus.name, PCI_SLOT(vtd_as->devfn),
|
||||
PCI_FUNC(vtd_as->devfn));
|
||||
if (new & IOMMU_NOTIFIER_MAP) {
|
||||
error_report("Device at bus %s addr %02x.%d requires iommu "
|
||||
"notifier which is currently not supported by "
|
||||
"intel-iommu emulation",
|
||||
vtd_as->bus->qbus.name, PCI_SLOT(vtd_as->devfn),
|
||||
PCI_FUNC(vtd_as->devfn));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static const VMStateDescription vtd_vmstate = {
|
||||
@ -2348,7 +2354,7 @@ static void vtd_init(IntelIOMMUState *s)
|
||||
memset(s->womask, 0, DMAR_REG_SIZE);
|
||||
|
||||
s->iommu_ops.translate = vtd_iommu_translate;
|
||||
s->iommu_ops.notify_started = vtd_iommu_notify_started;
|
||||
s->iommu_ops.notify_flag_changed = vtd_iommu_notify_flag_changed;
|
||||
s->root = 0;
|
||||
s->root_extended = false;
|
||||
s->dmar_enabled = false;
|
||||
|
@ -125,7 +125,7 @@ static void kvm_apic_vapic_base_update(APICCommonState *s)
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_apic_put(void *data)
|
||||
static void kvm_apic_put(CPUState *cs, void *data)
|
||||
{
|
||||
APICCommonState *s = data;
|
||||
struct kvm_lapic_state kapic;
|
||||
@ -146,10 +146,9 @@ static void kvm_apic_post_load(APICCommonState *s)
|
||||
run_on_cpu(CPU(s->cpu), kvm_apic_put, s);
|
||||
}
|
||||
|
||||
static void do_inject_external_nmi(void *data)
|
||||
static void do_inject_external_nmi(CPUState *cpu, void *data)
|
||||
{
|
||||
APICCommonState *s = data;
|
||||
CPUState *cpu = CPU(s->cpu);
|
||||
uint32_t lvt;
|
||||
int ret;
|
||||
|
||||
|
@ -483,7 +483,7 @@ typedef struct VAPICEnableTPRReporting {
|
||||
bool enable;
|
||||
} VAPICEnableTPRReporting;
|
||||
|
||||
static void vapic_do_enable_tpr_reporting(void *data)
|
||||
static void vapic_do_enable_tpr_reporting(CPUState *cpu, void *data)
|
||||
{
|
||||
VAPICEnableTPRReporting *info = data;
|
||||
|
||||
@ -734,10 +734,10 @@ static void vapic_realize(DeviceState *dev, Error **errp)
|
||||
nb_option_roms++;
|
||||
}
|
||||
|
||||
static void do_vapic_enable(void *data)
|
||||
static void do_vapic_enable(CPUState *cs, void *data)
|
||||
{
|
||||
VAPICROMState *s = data;
|
||||
X86CPU *cpu = X86_CPU(first_cpu);
|
||||
X86CPU *cpu = X86_CPU(cs);
|
||||
|
||||
static const uint8_t enabled = 1;
|
||||
cpu_physical_memory_write(s->vapic_paddr + offsetof(VAPICState, enabled),
|
||||
|
@ -416,7 +416,7 @@ static void ioapic_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
|
||||
static Property ioapic_properties[] = {
|
||||
DEFINE_PROP_UINT8("version", IOAPICCommonState, version, 0x11),
|
||||
DEFINE_PROP_UINT8("version", IOAPICCommonState, version, 0x20),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
@ -54,11 +54,6 @@ typedef struct SpinState {
|
||||
SpinInfo spin[MAX_CPUS];
|
||||
} SpinState;
|
||||
|
||||
typedef struct spin_kick {
|
||||
PowerPCCPU *cpu;
|
||||
SpinInfo *spin;
|
||||
} SpinKick;
|
||||
|
||||
static void spin_reset(void *opaque)
|
||||
{
|
||||
SpinState *s = opaque;
|
||||
@ -89,16 +84,15 @@ static void mmubooke_create_initial_mapping(CPUPPCState *env,
|
||||
env->tlb_dirty = true;
|
||||
}
|
||||
|
||||
static void spin_kick(void *data)
|
||||
static void spin_kick(CPUState *cs, void *data)
|
||||
{
|
||||
SpinKick *kick = data;
|
||||
CPUState *cpu = CPU(kick->cpu);
|
||||
CPUPPCState *env = &kick->cpu->env;
|
||||
SpinInfo *curspin = kick->spin;
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
SpinInfo *curspin = data;
|
||||
hwaddr map_size = 64 * 1024 * 1024;
|
||||
hwaddr map_start;
|
||||
|
||||
cpu_synchronize_state(cpu);
|
||||
cpu_synchronize_state(cs);
|
||||
stl_p(&curspin->pir, env->spr[SPR_BOOKE_PIR]);
|
||||
env->nip = ldq_p(&curspin->addr) & (map_size - 1);
|
||||
env->gpr[3] = ldq_p(&curspin->r3);
|
||||
@ -112,10 +106,10 @@ static void spin_kick(void *data)
|
||||
map_start = ldq_p(&curspin->addr) & ~(map_size - 1);
|
||||
mmubooke_create_initial_mapping(env, 0, map_start, map_size);
|
||||
|
||||
cpu->halted = 0;
|
||||
cpu->exception_index = -1;
|
||||
cpu->stopped = false;
|
||||
qemu_cpu_kick(cpu);
|
||||
cs->halted = 0;
|
||||
cs->exception_index = -1;
|
||||
cs->stopped = false;
|
||||
qemu_cpu_kick(cs);
|
||||
}
|
||||
|
||||
static void spin_write(void *opaque, hwaddr addr, uint64_t value,
|
||||
@ -153,12 +147,7 @@ static void spin_write(void *opaque, hwaddr addr, uint64_t value,
|
||||
|
||||
if (!(ldq_p(&curspin->addr) & 1)) {
|
||||
/* run CPU */
|
||||
SpinKick kick = {
|
||||
.cpu = POWERPC_CPU(cpu),
|
||||
.spin = curspin,
|
||||
};
|
||||
|
||||
run_on_cpu(cpu, spin_kick, &kick);
|
||||
run_on_cpu(cpu, spin_kick, curspin);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2132,10 +2132,8 @@ static void spapr_machine_finalizefn(Object *obj)
|
||||
g_free(spapr->kvm_type);
|
||||
}
|
||||
|
||||
static void ppc_cpu_do_nmi_on_cpu(void *arg)
|
||||
static void ppc_cpu_do_nmi_on_cpu(CPUState *cs, void *arg)
|
||||
{
|
||||
CPUState *cs = arg;
|
||||
|
||||
cpu_synchronize_state(cs);
|
||||
ppc_cpu_do_system_reset(cs);
|
||||
}
|
||||
@ -2145,7 +2143,7 @@ static void spapr_nmi(NMIState *n, int cpu_index, Error **errp)
|
||||
CPUState *cs;
|
||||
|
||||
CPU_FOREACH(cs) {
|
||||
async_run_on_cpu(cs, ppc_cpu_do_nmi_on_cpu, cs);
|
||||
async_run_on_cpu(cs, ppc_cpu_do_nmi_on_cpu, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,19 +13,18 @@
|
||||
#include "kvm_ppc.h"
|
||||
|
||||
struct SPRSyncState {
|
||||
CPUState *cs;
|
||||
int spr;
|
||||
target_ulong value;
|
||||
target_ulong mask;
|
||||
};
|
||||
|
||||
static void do_spr_sync(void *arg)
|
||||
static void do_spr_sync(CPUState *cs, void *arg)
|
||||
{
|
||||
struct SPRSyncState *s = arg;
|
||||
PowerPCCPU *cpu = POWERPC_CPU(s->cs);
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
cpu_synchronize_state(s->cs);
|
||||
cpu_synchronize_state(cs);
|
||||
env->spr[s->spr] &= ~s->mask;
|
||||
env->spr[s->spr] |= s->value;
|
||||
}
|
||||
@ -34,7 +33,6 @@ static void set_spr(CPUState *cs, int spr, target_ulong value,
|
||||
target_ulong mask)
|
||||
{
|
||||
struct SPRSyncState s = {
|
||||
.cs = cs,
|
||||
.spr = spr,
|
||||
.value = value,
|
||||
.mask = mask
|
||||
@ -909,17 +907,17 @@ static target_ulong cas_get_option_vector(int vector, target_ulong table)
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
PowerPCCPU *cpu;
|
||||
uint32_t cpu_version;
|
||||
Error *err;
|
||||
} SetCompatState;
|
||||
|
||||
static void do_set_compat(void *arg)
|
||||
static void do_set_compat(CPUState *cs, void *arg)
|
||||
{
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
SetCompatState *s = arg;
|
||||
|
||||
cpu_synchronize_state(CPU(s->cpu));
|
||||
ppc_set_compat(s->cpu, s->cpu_version, &s->err);
|
||||
cpu_synchronize_state(cs);
|
||||
ppc_set_compat(cpu, s->cpu_version, &s->err);
|
||||
}
|
||||
|
||||
#define get_compat_level(cpuver) ( \
|
||||
@ -1015,7 +1013,6 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
|
||||
if (old_cpu_version != cpu_version) {
|
||||
CPU_FOREACH(cs) {
|
||||
SetCompatState s = {
|
||||
.cpu = POWERPC_CPU(cs),
|
||||
.cpu_version = cpu_version,
|
||||
.err = NULL,
|
||||
};
|
||||
|
@ -156,14 +156,17 @@ static uint64_t spapr_tce_get_min_page_size(MemoryRegion *iommu)
|
||||
return 1ULL << tcet->page_shift;
|
||||
}
|
||||
|
||||
static void spapr_tce_notify_started(MemoryRegion *iommu)
|
||||
static void spapr_tce_notify_flag_changed(MemoryRegion *iommu,
|
||||
IOMMUNotifierFlag old,
|
||||
IOMMUNotifierFlag new)
|
||||
{
|
||||
spapr_tce_set_need_vfio(container_of(iommu, sPAPRTCETable, iommu), true);
|
||||
}
|
||||
struct sPAPRTCETable *tbl = container_of(iommu, sPAPRTCETable, iommu);
|
||||
|
||||
static void spapr_tce_notify_stopped(MemoryRegion *iommu)
|
||||
{
|
||||
spapr_tce_set_need_vfio(container_of(iommu, sPAPRTCETable, iommu), false);
|
||||
if (old == IOMMU_NOTIFIER_NONE && new != IOMMU_NOTIFIER_NONE) {
|
||||
spapr_tce_set_need_vfio(tbl, true);
|
||||
} else if (old != IOMMU_NOTIFIER_NONE && new == IOMMU_NOTIFIER_NONE) {
|
||||
spapr_tce_set_need_vfio(tbl, false);
|
||||
}
|
||||
}
|
||||
|
||||
static int spapr_tce_table_post_load(void *opaque, int version_id)
|
||||
@ -246,8 +249,7 @@ static const VMStateDescription vmstate_spapr_tce_table = {
|
||||
static MemoryRegionIOMMUOps spapr_iommu_ops = {
|
||||
.translate = spapr_tce_translate_iommu,
|
||||
.get_min_page_size = spapr_tce_get_min_page_size,
|
||||
.notify_started = spapr_tce_notify_started,
|
||||
.notify_stopped = spapr_tce_notify_stopped,
|
||||
.notify_flag_changed = spapr_tce_notify_flag_changed,
|
||||
};
|
||||
|
||||
static int spapr_tce_table_realize(DeviceState *dev)
|
||||
|
@ -293,11 +293,10 @@ static bool vfio_listener_skipped_section(MemoryRegionSection *section)
|
||||
section->offset_within_address_space & (1ULL << 63);
|
||||
}
|
||||
|
||||
static void vfio_iommu_map_notify(Notifier *n, void *data)
|
||||
static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
|
||||
{
|
||||
VFIOGuestIOMMU *giommu = container_of(n, VFIOGuestIOMMU, n);
|
||||
VFIOContainer *container = giommu->container;
|
||||
IOMMUTLBEntry *iotlb = data;
|
||||
hwaddr iova = iotlb->iova + giommu->iommu_offset;
|
||||
MemoryRegion *mr;
|
||||
hwaddr xlat;
|
||||
@ -454,6 +453,7 @@ static void vfio_listener_region_add(MemoryListener *listener,
|
||||
section->offset_within_region;
|
||||
giommu->container = container;
|
||||
giommu->n.notify = vfio_iommu_map_notify;
|
||||
giommu->n.notifier_flags = IOMMU_NOTIFIER_ALL;
|
||||
QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next);
|
||||
|
||||
memory_region_register_iommu_notifier(giommu->iommu, &giommu->n);
|
||||
|
@ -23,6 +23,11 @@ typedef struct CPUListState {
|
||||
FILE *file;
|
||||
} CPUListState;
|
||||
|
||||
/* The CPU list lock nests outside tb_lock/tb_unlock. */
|
||||
void qemu_init_cpu_list(void);
|
||||
void cpu_list_lock(void);
|
||||
void cpu_list_unlock(void);
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
|
||||
enum device_endian {
|
||||
|
@ -56,17 +56,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
target_ulong pc, target_ulong cs_base,
|
||||
uint32_t flags,
|
||||
int cflags);
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
void cpu_list_lock(void);
|
||||
void cpu_list_unlock(void);
|
||||
#else
|
||||
static inline void cpu_list_unlock(void)
|
||||
{
|
||||
}
|
||||
static inline void cpu_list_lock(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
void cpu_exec_init(CPUState *cpu, Error **errp);
|
||||
void QEMU_NORETURN cpu_loop_exit(CPUState *cpu);
|
||||
|
@ -67,6 +67,27 @@ struct IOMMUTLBEntry {
|
||||
IOMMUAccessFlags perm;
|
||||
};
|
||||
|
||||
/*
|
||||
* Bitmap for different IOMMUNotifier capabilities. Each notifier can
|
||||
* register with one or multiple IOMMU Notifier capability bit(s).
|
||||
*/
|
||||
typedef enum {
|
||||
IOMMU_NOTIFIER_NONE = 0,
|
||||
/* Notify cache invalidations */
|
||||
IOMMU_NOTIFIER_UNMAP = 0x1,
|
||||
/* Notify entry changes (newly created entries) */
|
||||
IOMMU_NOTIFIER_MAP = 0x2,
|
||||
} IOMMUNotifierFlag;
|
||||
|
||||
#define IOMMU_NOTIFIER_ALL (IOMMU_NOTIFIER_MAP | IOMMU_NOTIFIER_UNMAP)
|
||||
|
||||
struct IOMMUNotifier {
|
||||
void (*notify)(struct IOMMUNotifier *notifier, IOMMUTLBEntry *data);
|
||||
IOMMUNotifierFlag notifier_flags;
|
||||
QLIST_ENTRY(IOMMUNotifier) node;
|
||||
};
|
||||
typedef struct IOMMUNotifier IOMMUNotifier;
|
||||
|
||||
/* New-style MMIO accessors can indicate that the transaction failed.
|
||||
* A zero (MEMTX_OK) response means success; anything else is a failure
|
||||
* of some kind. The memory subsystem will bitwise-OR together results
|
||||
@ -153,10 +174,10 @@ struct MemoryRegionIOMMUOps {
|
||||
IOMMUTLBEntry (*translate)(MemoryRegion *iommu, hwaddr addr, bool is_write);
|
||||
/* Returns minimum supported page size */
|
||||
uint64_t (*get_min_page_size)(MemoryRegion *iommu);
|
||||
/* Called when the first notifier is set */
|
||||
void (*notify_started)(MemoryRegion *iommu);
|
||||
/* Called when the last notifier is removed */
|
||||
void (*notify_stopped)(MemoryRegion *iommu);
|
||||
/* Called when IOMMU Notifier flag changed */
|
||||
void (*notify_flag_changed)(MemoryRegion *iommu,
|
||||
IOMMUNotifierFlag old_flags,
|
||||
IOMMUNotifierFlag new_flags);
|
||||
};
|
||||
|
||||
typedef struct CoalescedMemoryRange CoalescedMemoryRange;
|
||||
@ -201,7 +222,8 @@ struct MemoryRegion {
|
||||
const char *name;
|
||||
unsigned ioeventfd_nb;
|
||||
MemoryRegionIoeventfd *ioeventfds;
|
||||
NotifierList iommu_notify;
|
||||
QLIST_HEAD(, IOMMUNotifier) iommu_notify;
|
||||
IOMMUNotifierFlag iommu_notify_flags;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -607,6 +629,15 @@ uint64_t memory_region_iommu_get_min_page_size(MemoryRegion *mr);
|
||||
/**
|
||||
* memory_region_notify_iommu: notify a change in an IOMMU translation entry.
|
||||
*
|
||||
* The notification type will be decided by entry.perm bits:
|
||||
*
|
||||
* - For UNMAP (cache invalidation) notifies: set entry.perm to IOMMU_NONE.
|
||||
* - For MAP (newly added entry) notifies: set entry.perm to the
|
||||
* permission of the page (which is definitely !IOMMU_NONE).
|
||||
*
|
||||
* Note: for any IOMMU implementation, an in-place mapping change
|
||||
* should be notified with an UNMAP followed by a MAP.
|
||||
*
|
||||
* @mr: the memory region that was changed
|
||||
* @entry: the new entry in the IOMMU translation table. The entry
|
||||
* replaces all old entries for the same virtual I/O address range.
|
||||
@ -620,11 +651,12 @@ void memory_region_notify_iommu(MemoryRegion *mr,
|
||||
* IOMMU translation entries.
|
||||
*
|
||||
* @mr: the memory region to observe
|
||||
* @n: the notifier to be added; the notifier receives a pointer to an
|
||||
* #IOMMUTLBEntry as the opaque value; the pointer ceases to be
|
||||
* valid on exit from the notifier.
|
||||
* @n: the IOMMUNotifier to be added; the notify callback receives a
|
||||
* pointer to an #IOMMUTLBEntry as the opaque value; the pointer
|
||||
* ceases to be valid on exit from the notifier.
|
||||
*/
|
||||
void memory_region_register_iommu_notifier(MemoryRegion *mr, Notifier *n);
|
||||
void memory_region_register_iommu_notifier(MemoryRegion *mr,
|
||||
IOMMUNotifier *n);
|
||||
|
||||
/**
|
||||
* memory_region_iommu_replay: replay existing IOMMU translations to
|
||||
@ -636,7 +668,8 @@ void memory_region_register_iommu_notifier(MemoryRegion *mr, Notifier *n);
|
||||
* @is_write: Whether to treat the replay as a translate "write"
|
||||
* through the iommu
|
||||
*/
|
||||
void memory_region_iommu_replay(MemoryRegion *mr, Notifier *n, bool is_write);
|
||||
void memory_region_iommu_replay(MemoryRegion *mr, IOMMUNotifier *n,
|
||||
bool is_write);
|
||||
|
||||
/**
|
||||
* memory_region_unregister_iommu_notifier: unregister a notifier for
|
||||
@ -646,7 +679,8 @@ void memory_region_iommu_replay(MemoryRegion *mr, Notifier *n, bool is_write);
|
||||
* needs to be called
|
||||
* @n: the notifier to be removed.
|
||||
*/
|
||||
void memory_region_unregister_iommu_notifier(MemoryRegion *mr, Notifier *n);
|
||||
void memory_region_unregister_iommu_notifier(MemoryRegion *mr,
|
||||
IOMMUNotifier *n);
|
||||
|
||||
/**
|
||||
* memory_region_name: get a memory region's name
|
||||
@ -1154,12 +1188,11 @@ MemoryRegionSection memory_region_find(MemoryRegion *mr,
|
||||
hwaddr addr, uint64_t size);
|
||||
|
||||
/**
|
||||
* address_space_sync_dirty_bitmap: synchronize the dirty log for all memory
|
||||
* memory_global_dirty_log_sync: synchronize the dirty log for all memory
|
||||
*
|
||||
* Synchronizes the dirty page log for an entire address space.
|
||||
* @as: the address space that contains the memory being synchronized
|
||||
* Synchronizes the dirty page log for all address spaces.
|
||||
*/
|
||||
void address_space_sync_dirty_bitmap(AddressSpace *as);
|
||||
void memory_global_dirty_log_sync(void);
|
||||
|
||||
/**
|
||||
* memory_region_transaction_begin: Start a transaction.
|
||||
|
@ -38,7 +38,7 @@ struct TBContext {
|
||||
QemuMutex tb_lock;
|
||||
|
||||
/* statistics */
|
||||
int tb_flush_count;
|
||||
unsigned tb_flush_count;
|
||||
int tb_phys_invalidate_count;
|
||||
};
|
||||
|
||||
|
@ -6,6 +6,10 @@
|
||||
.driver = "virtio-pci",\
|
||||
.property = "page-per-vq",\
|
||||
.value = "on",\
|
||||
},{\
|
||||
.driver = "ioapic",\
|
||||
.property = "version",\
|
||||
.value = "0x11",\
|
||||
},
|
||||
|
||||
#define HW_COMPAT_2_6 \
|
||||
|
@ -93,7 +93,7 @@ typedef struct VFIOGuestIOMMU {
|
||||
VFIOContainer *container;
|
||||
MemoryRegion *iommu;
|
||||
hwaddr iommu_offset;
|
||||
Notifier n;
|
||||
IOMMUNotifier n;
|
||||
QLIST_ENTRY(VFIOGuestIOMMU) giommu_next;
|
||||
} VFIOGuestIOMMU;
|
||||
|
||||
|
@ -1,4 +1,8 @@
|
||||
/* public domain */
|
||||
/* compiler.h: macros to abstract away compiler specifics
|
||||
*
|
||||
* 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 COMPILER_H
|
||||
#define COMPILER_H
|
||||
|
@ -232,13 +232,8 @@ struct kvm_run;
|
||||
#define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS)
|
||||
|
||||
/* work queue */
|
||||
struct qemu_work_item {
|
||||
struct qemu_work_item *next;
|
||||
void (*func)(void *data);
|
||||
void *data;
|
||||
int done;
|
||||
bool free;
|
||||
};
|
||||
typedef void (*run_on_cpu_func)(CPUState *cpu, void *data);
|
||||
struct qemu_work_item;
|
||||
|
||||
/**
|
||||
* CPUState:
|
||||
@ -247,7 +242,9 @@ struct qemu_work_item {
|
||||
* @nr_threads: Number of threads within this CPU.
|
||||
* @numa_node: NUMA node this CPU is belonging to.
|
||||
* @host_tid: Host thread ID.
|
||||
* @running: #true if CPU is currently running (usermode).
|
||||
* @running: #true if CPU is currently running (lockless).
|
||||
* @has_waiter: #true if a CPU is currently waiting for the cpu_exec_end;
|
||||
* valid under cpu_list_lock.
|
||||
* @created: Indicates whether the CPU thread has been successfully created.
|
||||
* @interrupt_request: Indicates a pending interrupt request.
|
||||
* @halted: Nonzero if the CPU is in suspended state.
|
||||
@ -257,7 +254,6 @@ struct qemu_work_item {
|
||||
* @crash_occurred: Indicates the OS reported a crash (panic) for this CPU
|
||||
* @tcg_exit_req: Set to force TCG to stop executing linked TBs for this
|
||||
* CPU and return to its top level loop.
|
||||
* @tb_flushed: Indicates the translation buffer has been flushed.
|
||||
* @singlestep_enabled: Flags for single-stepping.
|
||||
* @icount_extra: Instructions until next timer event.
|
||||
* @icount_decr: Number of cycles left, with interrupt flag in high bit.
|
||||
@ -301,7 +297,7 @@ struct CPUState {
|
||||
#endif
|
||||
int thread_id;
|
||||
uint32_t host_tid;
|
||||
bool running;
|
||||
bool running, has_waiter;
|
||||
struct QemuCond *halt_cond;
|
||||
bool thread_kicked;
|
||||
bool created;
|
||||
@ -310,7 +306,6 @@ struct CPUState {
|
||||
bool unplug;
|
||||
bool crash_occurred;
|
||||
bool exit_request;
|
||||
bool tb_flushed;
|
||||
uint32_t interrupt_request;
|
||||
int singlestep_enabled;
|
||||
int64_t icount_extra;
|
||||
@ -542,6 +537,18 @@ static inline int cpu_asidx_from_attrs(CPUState *cpu, MemTxAttrs attrs)
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* cpu_list_add:
|
||||
* @cpu: The CPU to be added to the list of CPUs.
|
||||
*/
|
||||
void cpu_list_add(CPUState *cpu);
|
||||
|
||||
/**
|
||||
* cpu_list_remove:
|
||||
* @cpu: The CPU to be removed from the list of CPUs.
|
||||
*/
|
||||
void cpu_list_remove(CPUState *cpu);
|
||||
|
||||
/**
|
||||
* cpu_reset:
|
||||
* @cpu: The CPU whose state is to be reset.
|
||||
@ -615,6 +622,18 @@ void qemu_cpu_kick(CPUState *cpu);
|
||||
*/
|
||||
bool cpu_is_stopped(CPUState *cpu);
|
||||
|
||||
/**
|
||||
* do_run_on_cpu:
|
||||
* @cpu: The vCPU to run on.
|
||||
* @func: The function to be executed.
|
||||
* @data: Data to pass to the function.
|
||||
* @mutex: Mutex to release while waiting for @func to run.
|
||||
*
|
||||
* Used internally in the implementation of run_on_cpu.
|
||||
*/
|
||||
void do_run_on_cpu(CPUState *cpu, run_on_cpu_func func, void *data,
|
||||
QemuMutex *mutex);
|
||||
|
||||
/**
|
||||
* run_on_cpu:
|
||||
* @cpu: The vCPU to run on.
|
||||
@ -623,7 +642,7 @@ bool cpu_is_stopped(CPUState *cpu);
|
||||
*
|
||||
* Schedules the function @func for execution on the vCPU @cpu.
|
||||
*/
|
||||
void run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data);
|
||||
void run_on_cpu(CPUState *cpu, run_on_cpu_func func, void *data);
|
||||
|
||||
/**
|
||||
* async_run_on_cpu:
|
||||
@ -633,7 +652,21 @@ void run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data);
|
||||
*
|
||||
* Schedules the function @func for execution on the vCPU @cpu asynchronously.
|
||||
*/
|
||||
void async_run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data);
|
||||
void async_run_on_cpu(CPUState *cpu, run_on_cpu_func func, void *data);
|
||||
|
||||
/**
|
||||
* async_safe_run_on_cpu:
|
||||
* @cpu: The vCPU to run on.
|
||||
* @func: The function to be executed.
|
||||
* @data: Data to pass to the function.
|
||||
*
|
||||
* Schedules the function @func for execution on the vCPU @cpu asynchronously,
|
||||
* while all other vCPUs are sleeping.
|
||||
*
|
||||
* Unlike run_on_cpu and async_run_on_cpu, the function is run outside the
|
||||
* BQL.
|
||||
*/
|
||||
void async_safe_run_on_cpu(CPUState *cpu, run_on_cpu_func func, void *data);
|
||||
|
||||
/**
|
||||
* qemu_get_cpu:
|
||||
@ -793,6 +826,49 @@ void cpu_remove(CPUState *cpu);
|
||||
*/
|
||||
void cpu_remove_sync(CPUState *cpu);
|
||||
|
||||
/**
|
||||
* process_queued_cpu_work() - process all items on CPU work queue
|
||||
* @cpu: The CPU which work queue to process.
|
||||
*/
|
||||
void process_queued_cpu_work(CPUState *cpu);
|
||||
|
||||
/**
|
||||
* cpu_exec_start:
|
||||
* @cpu: The CPU for the current thread.
|
||||
*
|
||||
* Record that a CPU has started execution and can be interrupted with
|
||||
* cpu_exit.
|
||||
*/
|
||||
void cpu_exec_start(CPUState *cpu);
|
||||
|
||||
/**
|
||||
* cpu_exec_end:
|
||||
* @cpu: The CPU for the current thread.
|
||||
*
|
||||
* Record that a CPU has stopped execution and exclusive sections
|
||||
* can be executed without interrupting it.
|
||||
*/
|
||||
void cpu_exec_end(CPUState *cpu);
|
||||
|
||||
/**
|
||||
* start_exclusive:
|
||||
*
|
||||
* Wait for a concurrent exclusive section to end, and then start
|
||||
* a section of work that is run while other CPUs are not running
|
||||
* between cpu_exec_start and cpu_exec_end. CPUs that are running
|
||||
* cpu_exec are exited immediately. CPUs that call cpu_exec_start
|
||||
* during the exclusive section go to sleep until this CPU calls
|
||||
* end_exclusive.
|
||||
*/
|
||||
void start_exclusive(void);
|
||||
|
||||
/**
|
||||
* end_exclusive:
|
||||
*
|
||||
* Concludes an exclusive execution section started by start_exclusive.
|
||||
*/
|
||||
void end_exclusive(void);
|
||||
|
||||
/**
|
||||
* qemu_init_vcpu:
|
||||
* @cpu: The vCPU to initialize.
|
||||
|
@ -105,6 +105,8 @@ bool replay_checkpoint(ReplayCheckpoint checkpoint);
|
||||
|
||||
/*! Disables storing events in the queue */
|
||||
void replay_disable_events(void);
|
||||
/*! Enables storing events in the queue */
|
||||
void replay_enable_events(void);
|
||||
/*! Returns true when saving events is enabled */
|
||||
bool replay_events_enabled(void);
|
||||
/*! Adds bottom half event to the queue */
|
||||
@ -115,6 +117,8 @@ void replay_input_event(QemuConsole *src, InputEvent *evt);
|
||||
void replay_input_sync_event(void);
|
||||
/*! Adds block layer event to the queue */
|
||||
void replay_block_event(QEMUBH *bh, uint64_t id);
|
||||
/*! Returns ID for the next block event */
|
||||
uint64_t blkreplay_next_id(void);
|
||||
|
||||
/* Character device */
|
||||
|
||||
|
21
kvm-all.c
21
kvm-all.c
@ -1847,10 +1847,8 @@ void kvm_flush_coalesced_mmio_buffer(void)
|
||||
s->coalesced_flush_in_progress = false;
|
||||
}
|
||||
|
||||
static void do_kvm_cpu_synchronize_state(void *arg)
|
||||
static void do_kvm_cpu_synchronize_state(CPUState *cpu, void *arg)
|
||||
{
|
||||
CPUState *cpu = arg;
|
||||
|
||||
if (!cpu->kvm_vcpu_dirty) {
|
||||
kvm_arch_get_registers(cpu);
|
||||
cpu->kvm_vcpu_dirty = true;
|
||||
@ -1860,34 +1858,30 @@ static void do_kvm_cpu_synchronize_state(void *arg)
|
||||
void kvm_cpu_synchronize_state(CPUState *cpu)
|
||||
{
|
||||
if (!cpu->kvm_vcpu_dirty) {
|
||||
run_on_cpu(cpu, do_kvm_cpu_synchronize_state, cpu);
|
||||
run_on_cpu(cpu, do_kvm_cpu_synchronize_state, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void do_kvm_cpu_synchronize_post_reset(void *arg)
|
||||
static void do_kvm_cpu_synchronize_post_reset(CPUState *cpu, void *arg)
|
||||
{
|
||||
CPUState *cpu = arg;
|
||||
|
||||
kvm_arch_put_registers(cpu, KVM_PUT_RESET_STATE);
|
||||
cpu->kvm_vcpu_dirty = false;
|
||||
}
|
||||
|
||||
void kvm_cpu_synchronize_post_reset(CPUState *cpu)
|
||||
{
|
||||
run_on_cpu(cpu, do_kvm_cpu_synchronize_post_reset, cpu);
|
||||
run_on_cpu(cpu, do_kvm_cpu_synchronize_post_reset, NULL);
|
||||
}
|
||||
|
||||
static void do_kvm_cpu_synchronize_post_init(void *arg)
|
||||
static void do_kvm_cpu_synchronize_post_init(CPUState *cpu, void *arg)
|
||||
{
|
||||
CPUState *cpu = arg;
|
||||
|
||||
kvm_arch_put_registers(cpu, KVM_PUT_FULL_STATE);
|
||||
cpu->kvm_vcpu_dirty = false;
|
||||
}
|
||||
|
||||
void kvm_cpu_synchronize_post_init(CPUState *cpu)
|
||||
{
|
||||
run_on_cpu(cpu, do_kvm_cpu_synchronize_post_init, cpu);
|
||||
run_on_cpu(cpu, do_kvm_cpu_synchronize_post_init, NULL);
|
||||
}
|
||||
|
||||
int kvm_cpu_exec(CPUState *cpu)
|
||||
@ -2216,7 +2210,7 @@ struct kvm_set_guest_debug_data {
|
||||
int err;
|
||||
};
|
||||
|
||||
static void kvm_invoke_set_guest_debug(void *data)
|
||||
static void kvm_invoke_set_guest_debug(CPUState *unused_cpu, void *data)
|
||||
{
|
||||
struct kvm_set_guest_debug_data *dbg_data = data;
|
||||
|
||||
@ -2234,7 +2228,6 @@ int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap)
|
||||
data.dbg.control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP;
|
||||
}
|
||||
kvm_arch_update_guest_debug(cpu, &data.dbg);
|
||||
data.cpu = cpu;
|
||||
|
||||
run_on_cpu(cpu, kvm_invoke_set_guest_debug, &data);
|
||||
return data.err;
|
||||
|
@ -107,21 +107,11 @@ int cpu_get_pic_interrupt(CPUX86State *env)
|
||||
/***********************************************************/
|
||||
/* Helper routines for implementing atomic operations. */
|
||||
|
||||
/* To implement exclusive operations we force all cpus to syncronise.
|
||||
We don't require a full sync, only that no cpus are executing guest code.
|
||||
The alternative is to map target atomic ops onto host equivalents,
|
||||
which requires quite a lot of per host/target work. */
|
||||
static pthread_mutex_t cpu_list_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_mutex_t exclusive_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t exclusive_cond = PTHREAD_COND_INITIALIZER;
|
||||
static pthread_cond_t exclusive_resume = PTHREAD_COND_INITIALIZER;
|
||||
static int pending_cpus;
|
||||
|
||||
/* Make sure everything is in a consistent state for calling fork(). */
|
||||
void fork_start(void)
|
||||
{
|
||||
cpu_list_lock();
|
||||
qemu_mutex_lock(&tcg_ctx.tb_ctx.tb_lock);
|
||||
pthread_mutex_lock(&exclusive_lock);
|
||||
mmap_fork_start();
|
||||
}
|
||||
|
||||
@ -137,93 +127,15 @@ void fork_end(int child)
|
||||
QTAILQ_REMOVE(&cpus, cpu, node);
|
||||
}
|
||||
}
|
||||
pending_cpus = 0;
|
||||
pthread_mutex_init(&exclusive_lock, NULL);
|
||||
pthread_mutex_init(&cpu_list_mutex, NULL);
|
||||
pthread_cond_init(&exclusive_cond, NULL);
|
||||
pthread_cond_init(&exclusive_resume, NULL);
|
||||
qemu_mutex_init(&tcg_ctx.tb_ctx.tb_lock);
|
||||
qemu_init_cpu_list();
|
||||
gdbserver_fork(thread_cpu);
|
||||
} else {
|
||||
pthread_mutex_unlock(&exclusive_lock);
|
||||
qemu_mutex_unlock(&tcg_ctx.tb_ctx.tb_lock);
|
||||
cpu_list_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for pending exclusive operations to complete. The exclusive lock
|
||||
must be held. */
|
||||
static inline void exclusive_idle(void)
|
||||
{
|
||||
while (pending_cpus) {
|
||||
pthread_cond_wait(&exclusive_resume, &exclusive_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* Start an exclusive operation.
|
||||
Must only be called from outside cpu_exec. */
|
||||
static inline void start_exclusive(void)
|
||||
{
|
||||
CPUState *other_cpu;
|
||||
|
||||
pthread_mutex_lock(&exclusive_lock);
|
||||
exclusive_idle();
|
||||
|
||||
pending_cpus = 1;
|
||||
/* Make all other cpus stop executing. */
|
||||
CPU_FOREACH(other_cpu) {
|
||||
if (other_cpu->running) {
|
||||
pending_cpus++;
|
||||
cpu_exit(other_cpu);
|
||||
}
|
||||
}
|
||||
if (pending_cpus > 1) {
|
||||
pthread_cond_wait(&exclusive_cond, &exclusive_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* Finish an exclusive operation. */
|
||||
static inline void __attribute__((unused)) end_exclusive(void)
|
||||
{
|
||||
pending_cpus = 0;
|
||||
pthread_cond_broadcast(&exclusive_resume);
|
||||
pthread_mutex_unlock(&exclusive_lock);
|
||||
}
|
||||
|
||||
/* Wait for exclusive ops to finish, and begin cpu execution. */
|
||||
static inline void cpu_exec_start(CPUState *cpu)
|
||||
{
|
||||
pthread_mutex_lock(&exclusive_lock);
|
||||
exclusive_idle();
|
||||
cpu->running = true;
|
||||
pthread_mutex_unlock(&exclusive_lock);
|
||||
}
|
||||
|
||||
/* Mark cpu as not executing, and release pending exclusive ops. */
|
||||
static inline void cpu_exec_end(CPUState *cpu)
|
||||
{
|
||||
pthread_mutex_lock(&exclusive_lock);
|
||||
cpu->running = false;
|
||||
if (pending_cpus > 1) {
|
||||
pending_cpus--;
|
||||
if (pending_cpus == 1) {
|
||||
pthread_cond_signal(&exclusive_cond);
|
||||
}
|
||||
}
|
||||
exclusive_idle();
|
||||
pthread_mutex_unlock(&exclusive_lock);
|
||||
}
|
||||
|
||||
void cpu_list_lock(void)
|
||||
{
|
||||
pthread_mutex_lock(&cpu_list_mutex);
|
||||
}
|
||||
|
||||
void cpu_list_unlock(void)
|
||||
{
|
||||
pthread_mutex_unlock(&cpu_list_mutex);
|
||||
}
|
||||
|
||||
|
||||
#ifdef TARGET_I386
|
||||
/***********************************************************/
|
||||
/* CPUX86 core interface */
|
||||
@ -296,6 +208,8 @@ void cpu_loop(CPUX86State *env)
|
||||
cpu_exec_start(cs);
|
||||
trapnr = cpu_exec(cs);
|
||||
cpu_exec_end(cs);
|
||||
process_queued_cpu_work(cs);
|
||||
|
||||
switch(trapnr) {
|
||||
case 0x80:
|
||||
/* linux syscall from int $0x80 */
|
||||
@ -737,6 +651,8 @@ void cpu_loop(CPUARMState *env)
|
||||
cpu_exec_start(cs);
|
||||
trapnr = cpu_exec(cs);
|
||||
cpu_exec_end(cs);
|
||||
process_queued_cpu_work(cs);
|
||||
|
||||
switch(trapnr) {
|
||||
case EXCP_UDEF:
|
||||
{
|
||||
@ -1073,6 +989,7 @@ void cpu_loop(CPUARMState *env)
|
||||
cpu_exec_start(cs);
|
||||
trapnr = cpu_exec(cs);
|
||||
cpu_exec_end(cs);
|
||||
process_queued_cpu_work(cs);
|
||||
|
||||
switch (trapnr) {
|
||||
case EXCP_SWI:
|
||||
@ -1161,6 +1078,8 @@ void cpu_loop(CPUUniCore32State *env)
|
||||
cpu_exec_start(cs);
|
||||
trapnr = cpu_exec(cs);
|
||||
cpu_exec_end(cs);
|
||||
process_queued_cpu_work(cs);
|
||||
|
||||
switch (trapnr) {
|
||||
case UC32_EXCP_PRIV:
|
||||
{
|
||||
@ -1366,6 +1285,7 @@ void cpu_loop (CPUSPARCState *env)
|
||||
cpu_exec_start(cs);
|
||||
trapnr = cpu_exec(cs);
|
||||
cpu_exec_end(cs);
|
||||
process_queued_cpu_work(cs);
|
||||
|
||||
/* Compute PSR before exposing state. */
|
||||
if (env->cc_op != CC_OP_FLAGS) {
|
||||
@ -1638,6 +1558,8 @@ void cpu_loop(CPUPPCState *env)
|
||||
cpu_exec_start(cs);
|
||||
trapnr = cpu_exec(cs);
|
||||
cpu_exec_end(cs);
|
||||
process_queued_cpu_work(cs);
|
||||
|
||||
switch(trapnr) {
|
||||
case POWERPC_EXCP_NONE:
|
||||
/* Just go on */
|
||||
@ -2484,6 +2406,8 @@ void cpu_loop(CPUMIPSState *env)
|
||||
cpu_exec_start(cs);
|
||||
trapnr = cpu_exec(cs);
|
||||
cpu_exec_end(cs);
|
||||
process_queued_cpu_work(cs);
|
||||
|
||||
switch(trapnr) {
|
||||
case EXCP_SYSCALL:
|
||||
env->active_tc.PC += 4;
|
||||
@ -2724,6 +2648,7 @@ void cpu_loop(CPUOpenRISCState *env)
|
||||
cpu_exec_start(cs);
|
||||
trapnr = cpu_exec(cs);
|
||||
cpu_exec_end(cs);
|
||||
process_queued_cpu_work(cs);
|
||||
gdbsig = 0;
|
||||
|
||||
switch (trapnr) {
|
||||
@ -2818,6 +2743,7 @@ void cpu_loop(CPUSH4State *env)
|
||||
cpu_exec_start(cs);
|
||||
trapnr = cpu_exec(cs);
|
||||
cpu_exec_end(cs);
|
||||
process_queued_cpu_work(cs);
|
||||
|
||||
switch (trapnr) {
|
||||
case 0x160:
|
||||
@ -2884,6 +2810,8 @@ void cpu_loop(CPUCRISState *env)
|
||||
cpu_exec_start(cs);
|
||||
trapnr = cpu_exec(cs);
|
||||
cpu_exec_end(cs);
|
||||
process_queued_cpu_work(cs);
|
||||
|
||||
switch (trapnr) {
|
||||
case 0xaa:
|
||||
{
|
||||
@ -2949,6 +2877,8 @@ void cpu_loop(CPUMBState *env)
|
||||
cpu_exec_start(cs);
|
||||
trapnr = cpu_exec(cs);
|
||||
cpu_exec_end(cs);
|
||||
process_queued_cpu_work(cs);
|
||||
|
||||
switch (trapnr) {
|
||||
case 0xaa:
|
||||
{
|
||||
@ -3066,6 +2996,8 @@ void cpu_loop(CPUM68KState *env)
|
||||
cpu_exec_start(cs);
|
||||
trapnr = cpu_exec(cs);
|
||||
cpu_exec_end(cs);
|
||||
process_queued_cpu_work(cs);
|
||||
|
||||
switch(trapnr) {
|
||||
case EXCP_ILLEGAL:
|
||||
{
|
||||
@ -3209,6 +3141,7 @@ void cpu_loop(CPUAlphaState *env)
|
||||
cpu_exec_start(cs);
|
||||
trapnr = cpu_exec(cs);
|
||||
cpu_exec_end(cs);
|
||||
process_queued_cpu_work(cs);
|
||||
|
||||
/* All of the traps imply a transition through PALcode, which
|
||||
implies an REI instruction has been executed. Which means
|
||||
@ -3401,6 +3334,8 @@ void cpu_loop(CPUS390XState *env)
|
||||
cpu_exec_start(cs);
|
||||
trapnr = cpu_exec(cs);
|
||||
cpu_exec_end(cs);
|
||||
process_queued_cpu_work(cs);
|
||||
|
||||
switch (trapnr) {
|
||||
case EXCP_INTERRUPT:
|
||||
/* Just indicate that signals should be handled asap. */
|
||||
@ -3710,6 +3645,8 @@ void cpu_loop(CPUTLGState *env)
|
||||
cpu_exec_start(cs);
|
||||
trapnr = cpu_exec(cs);
|
||||
cpu_exec_end(cs);
|
||||
process_queued_cpu_work(cs);
|
||||
|
||||
switch (trapnr) {
|
||||
case TILEGX_EXCP_SYSCALL:
|
||||
{
|
||||
@ -3769,6 +3706,16 @@ void cpu_loop(CPUTLGState *env)
|
||||
|
||||
THREAD CPUState *thread_cpu;
|
||||
|
||||
bool qemu_cpu_is_self(CPUState *cpu)
|
||||
{
|
||||
return thread_cpu == cpu;
|
||||
}
|
||||
|
||||
void qemu_cpu_kick(CPUState *cpu)
|
||||
{
|
||||
cpu_exit(cpu);
|
||||
}
|
||||
|
||||
void task_settid(TaskState *ts)
|
||||
{
|
||||
if (ts->ts_tid == 0) {
|
||||
@ -4211,6 +4158,7 @@ int main(int argc, char **argv, char **envp)
|
||||
int ret;
|
||||
int execfd;
|
||||
|
||||
qemu_init_cpu_list();
|
||||
module_call_init(MODULE_INIT_QOM);
|
||||
|
||||
if ((envlist = envlist_create()) == NULL) {
|
||||
|
106
memory.c
106
memory.c
@ -158,14 +158,10 @@ static bool memory_listener_match(MemoryListener *listener,
|
||||
|
||||
/* No need to ref/unref .mr, the FlatRange keeps it alive. */
|
||||
#define MEMORY_LISTENER_UPDATE_REGION(fr, as, dir, callback, _args...) \
|
||||
MEMORY_LISTENER_CALL(callback, dir, (&(MemoryRegionSection) { \
|
||||
.mr = (fr)->mr, \
|
||||
.address_space = (as), \
|
||||
.offset_within_region = (fr)->offset_in_region, \
|
||||
.size = (fr)->addr.size, \
|
||||
.offset_within_address_space = int128_get64((fr)->addr.start), \
|
||||
.readonly = (fr)->readonly, \
|
||||
}), ##_args)
|
||||
do { \
|
||||
MemoryRegionSection mrs = section_from_flat_range(fr, as); \
|
||||
MEMORY_LISTENER_CALL(callback, dir, &mrs, ##_args); \
|
||||
} while(0)
|
||||
|
||||
struct CoalescedMemoryRange {
|
||||
AddrRange addr;
|
||||
@ -245,6 +241,19 @@ typedef struct AddressSpaceOps AddressSpaceOps;
|
||||
#define FOR_EACH_FLAT_RANGE(var, view) \
|
||||
for (var = (view)->ranges; var < (view)->ranges + (view)->nr; ++var)
|
||||
|
||||
static inline MemoryRegionSection
|
||||
section_from_flat_range(FlatRange *fr, AddressSpace *as)
|
||||
{
|
||||
return (MemoryRegionSection) {
|
||||
.mr = fr->mr,
|
||||
.address_space = as,
|
||||
.offset_within_region = fr->offset_in_region,
|
||||
.size = fr->addr.size,
|
||||
.offset_within_address_space = int128_get64(fr->addr.start),
|
||||
.readonly = fr->readonly,
|
||||
};
|
||||
}
|
||||
|
||||
static bool flatrange_equal(FlatRange *a, FlatRange *b)
|
||||
{
|
||||
return a->mr == b->mr
|
||||
@ -1413,7 +1422,8 @@ void memory_region_init_iommu(MemoryRegion *mr,
|
||||
memory_region_init(mr, owner, name, size);
|
||||
mr->iommu_ops = ops,
|
||||
mr->terminates = true; /* then re-forwards */
|
||||
notifier_list_init(&mr->iommu_notify);
|
||||
QLIST_INIT(&mr->iommu_notify);
|
||||
mr->iommu_notify_flags = IOMMU_NOTIFIER_NONE;
|
||||
}
|
||||
|
||||
static void memory_region_finalize(Object *obj)
|
||||
@ -1508,13 +1518,31 @@ bool memory_region_is_logging(MemoryRegion *mr, uint8_t client)
|
||||
return memory_region_get_dirty_log_mask(mr) & (1 << client);
|
||||
}
|
||||
|
||||
void memory_region_register_iommu_notifier(MemoryRegion *mr, Notifier *n)
|
||||
static void memory_region_update_iommu_notify_flags(MemoryRegion *mr)
|
||||
{
|
||||
if (mr->iommu_ops->notify_started &&
|
||||
QLIST_EMPTY(&mr->iommu_notify.notifiers)) {
|
||||
mr->iommu_ops->notify_started(mr);
|
||||
IOMMUNotifierFlag flags = IOMMU_NOTIFIER_NONE;
|
||||
IOMMUNotifier *iommu_notifier;
|
||||
|
||||
QLIST_FOREACH(iommu_notifier, &mr->iommu_notify, node) {
|
||||
flags |= iommu_notifier->notifier_flags;
|
||||
}
|
||||
notifier_list_add(&mr->iommu_notify, n);
|
||||
|
||||
if (flags != mr->iommu_notify_flags &&
|
||||
mr->iommu_ops->notify_flag_changed) {
|
||||
mr->iommu_ops->notify_flag_changed(mr, mr->iommu_notify_flags,
|
||||
flags);
|
||||
}
|
||||
|
||||
mr->iommu_notify_flags = flags;
|
||||
}
|
||||
|
||||
void memory_region_register_iommu_notifier(MemoryRegion *mr,
|
||||
IOMMUNotifier *n)
|
||||
{
|
||||
/* We need to register for at least one bitfield */
|
||||
assert(n->notifier_flags != IOMMU_NOTIFIER_NONE);
|
||||
QLIST_INSERT_HEAD(&mr->iommu_notify, n, node);
|
||||
memory_region_update_iommu_notify_flags(mr);
|
||||
}
|
||||
|
||||
uint64_t memory_region_iommu_get_min_page_size(MemoryRegion *mr)
|
||||
@ -1526,7 +1554,8 @@ uint64_t memory_region_iommu_get_min_page_size(MemoryRegion *mr)
|
||||
return TARGET_PAGE_SIZE;
|
||||
}
|
||||
|
||||
void memory_region_iommu_replay(MemoryRegion *mr, Notifier *n, bool is_write)
|
||||
void memory_region_iommu_replay(MemoryRegion *mr, IOMMUNotifier *n,
|
||||
bool is_write)
|
||||
{
|
||||
hwaddr addr, granularity;
|
||||
IOMMUTLBEntry iotlb;
|
||||
@ -1547,20 +1576,32 @@ void memory_region_iommu_replay(MemoryRegion *mr, Notifier *n, bool is_write)
|
||||
}
|
||||
}
|
||||
|
||||
void memory_region_unregister_iommu_notifier(MemoryRegion *mr, Notifier *n)
|
||||
void memory_region_unregister_iommu_notifier(MemoryRegion *mr,
|
||||
IOMMUNotifier *n)
|
||||
{
|
||||
notifier_remove(n);
|
||||
if (mr->iommu_ops->notify_stopped &&
|
||||
QLIST_EMPTY(&mr->iommu_notify.notifiers)) {
|
||||
mr->iommu_ops->notify_stopped(mr);
|
||||
}
|
||||
QLIST_REMOVE(n, node);
|
||||
memory_region_update_iommu_notify_flags(mr);
|
||||
}
|
||||
|
||||
void memory_region_notify_iommu(MemoryRegion *mr,
|
||||
IOMMUTLBEntry entry)
|
||||
{
|
||||
IOMMUNotifier *iommu_notifier;
|
||||
IOMMUNotifierFlag request_flags;
|
||||
|
||||
assert(memory_region_is_iommu(mr));
|
||||
notifier_list_notify(&mr->iommu_notify, &entry);
|
||||
|
||||
if (entry.perm & IOMMU_RW) {
|
||||
request_flags = IOMMU_NOTIFIER_MAP;
|
||||
} else {
|
||||
request_flags = IOMMU_NOTIFIER_UNMAP;
|
||||
}
|
||||
|
||||
QLIST_FOREACH(iommu_notifier, &mr->iommu_notify, node) {
|
||||
if (iommu_notifier->notifier_flags & request_flags) {
|
||||
iommu_notifier->notify(iommu_notifier, &entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client)
|
||||
@ -2124,16 +2165,27 @@ bool memory_region_present(MemoryRegion *container, hwaddr addr)
|
||||
return mr && mr != container;
|
||||
}
|
||||
|
||||
void address_space_sync_dirty_bitmap(AddressSpace *as)
|
||||
void memory_global_dirty_log_sync(void)
|
||||
{
|
||||
MemoryListener *listener;
|
||||
AddressSpace *as;
|
||||
FlatView *view;
|
||||
FlatRange *fr;
|
||||
|
||||
view = address_space_get_flatview(as);
|
||||
FOR_EACH_FLAT_RANGE(fr, view) {
|
||||
MEMORY_LISTENER_UPDATE_REGION(fr, as, Forward, log_sync);
|
||||
QTAILQ_FOREACH(listener, &memory_listeners, link) {
|
||||
if (!listener->log_sync) {
|
||||
continue;
|
||||
}
|
||||
/* Global listeners are being phased out. */
|
||||
assert(listener->address_space_filter);
|
||||
as = listener->address_space_filter;
|
||||
view = address_space_get_flatview(as);
|
||||
FOR_EACH_FLAT_RANGE(fr, view) {
|
||||
MemoryRegionSection mrs = section_from_flat_range(fr, as);
|
||||
listener->log_sync(listener, &mrs);
|
||||
}
|
||||
flatview_unref(view);
|
||||
}
|
||||
flatview_unref(view);
|
||||
}
|
||||
|
||||
void memory_global_dirty_log_start(void)
|
||||
|
@ -626,7 +626,7 @@ static void migration_bitmap_sync(void)
|
||||
}
|
||||
|
||||
trace_migration_bitmap_sync_start();
|
||||
address_space_sync_dirty_bitmap(&address_space_memory);
|
||||
memory_global_dirty_log_sync();
|
||||
|
||||
qemu_mutex_lock(&migration_bitmap_mutex);
|
||||
rcu_read_lock();
|
||||
|
@ -4,3 +4,4 @@ common-obj-y += replay-events.o
|
||||
common-obj-y += replay-time.o
|
||||
common-obj-y += replay-input.o
|
||||
common-obj-y += replay-char.o
|
||||
common-obj-y += replay-snapshot.o
|
||||
|
@ -279,7 +279,7 @@ static Event *replay_read_event(int checkpoint)
|
||||
/* Called with replay mutex locked */
|
||||
void replay_read_events(int checkpoint)
|
||||
{
|
||||
while (replay_data_kind == EVENT_ASYNC) {
|
||||
while (replay_state.data_kind == EVENT_ASYNC) {
|
||||
Event *event = replay_read_event(checkpoint);
|
||||
if (!event) {
|
||||
break;
|
||||
@ -309,3 +309,11 @@ bool replay_events_enabled(void)
|
||||
{
|
||||
return events_enabled;
|
||||
}
|
||||
|
||||
uint64_t blkreplay_next_id(void)
|
||||
{
|
||||
if (replay_events_enabled()) {
|
||||
return replay_state.block_request_id++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -16,11 +16,8 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
unsigned int replay_data_kind = -1;
|
||||
static unsigned int replay_has_unread_data;
|
||||
|
||||
/* Mutex to protect reading and writing events to the log.
|
||||
replay_data_kind and replay_has_unread_data are also protected
|
||||
data_kind and has_unread_data are also protected
|
||||
by this mutex.
|
||||
It also protects replay events queue which stores events to be
|
||||
written or read to the log. */
|
||||
@ -150,15 +147,16 @@ void replay_check_error(void)
|
||||
void replay_fetch_data_kind(void)
|
||||
{
|
||||
if (replay_file) {
|
||||
if (!replay_has_unread_data) {
|
||||
replay_data_kind = replay_get_byte();
|
||||
if (replay_data_kind == EVENT_INSTRUCTION) {
|
||||
if (!replay_state.has_unread_data) {
|
||||
replay_state.data_kind = replay_get_byte();
|
||||
if (replay_state.data_kind == EVENT_INSTRUCTION) {
|
||||
replay_state.instructions_count = replay_get_dword();
|
||||
}
|
||||
replay_check_error();
|
||||
replay_has_unread_data = 1;
|
||||
if (replay_data_kind >= EVENT_COUNT) {
|
||||
error_report("Replay: unknown event kind %d", replay_data_kind);
|
||||
replay_state.has_unread_data = 1;
|
||||
if (replay_state.data_kind >= EVENT_COUNT) {
|
||||
error_report("Replay: unknown event kind %d",
|
||||
replay_state.data_kind);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@ -167,7 +165,7 @@ void replay_fetch_data_kind(void)
|
||||
|
||||
void replay_finish_event(void)
|
||||
{
|
||||
replay_has_unread_data = 0;
|
||||
replay_state.has_unread_data = 0;
|
||||
replay_fetch_data_kind();
|
||||
}
|
||||
|
||||
|
@ -62,11 +62,19 @@ typedef struct ReplayState {
|
||||
uint64_t current_step;
|
||||
/*! Number of instructions to be executed before other events happen. */
|
||||
int instructions_count;
|
||||
/*! Type of the currently executed event. */
|
||||
unsigned int data_kind;
|
||||
/*! Flag which indicates that event is not processed yet. */
|
||||
unsigned int has_unread_data;
|
||||
/*! Temporary variable for saving current log offset. */
|
||||
uint64_t file_offset;
|
||||
/*! Next block operation id.
|
||||
This counter is global, because requests from different
|
||||
block devices should not get overlapping ids. */
|
||||
uint64_t block_request_id;
|
||||
} ReplayState;
|
||||
extern ReplayState replay_state;
|
||||
|
||||
extern unsigned int replay_data_kind;
|
||||
|
||||
/* File for replay writing */
|
||||
extern FILE *replay_file;
|
||||
|
||||
@ -98,7 +106,7 @@ void replay_check_error(void);
|
||||
the next event from the log. */
|
||||
void replay_finish_event(void);
|
||||
/*! Reads data type from the file and stores it in the
|
||||
replay_data_kind variable. */
|
||||
data_kind variable. */
|
||||
void replay_fetch_data_kind(void);
|
||||
|
||||
/*! Saves queued events (like instructions and sound). */
|
||||
@ -119,8 +127,6 @@ void replay_read_next_clock(unsigned int kind);
|
||||
void replay_init_events(void);
|
||||
/*! Clears internal data structures for events handling */
|
||||
void replay_finish_events(void);
|
||||
/*! Enables storing events in the queue */
|
||||
void replay_enable_events(void);
|
||||
/*! Flushes events queue */
|
||||
void replay_flush_events(void);
|
||||
/*! Clears events list before loading new VM state */
|
||||
@ -155,4 +161,11 @@ void replay_event_char_read_save(void *opaque);
|
||||
/*! Reads char event read from the file. */
|
||||
void *replay_event_char_read_load(void);
|
||||
|
||||
/* VMState-related functions */
|
||||
|
||||
/* Registers replay VMState.
|
||||
Should be called before virtual devices initialization
|
||||
to make cached timers available for post_load functions. */
|
||||
void replay_vmstate_register(void);
|
||||
|
||||
#endif
|
||||
|
61
replay/replay-snapshot.c
Normal file
61
replay/replay-snapshot.c
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* replay-snapshot.c
|
||||
*
|
||||
* Copyright (c) 2010-2016 Institute for System Programming
|
||||
* of the Russian Academy of Sciences.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "replay-internal.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
static void replay_pre_save(void *opaque)
|
||||
{
|
||||
ReplayState *state = opaque;
|
||||
state->file_offset = ftell(replay_file);
|
||||
}
|
||||
|
||||
static int replay_post_load(void *opaque, int version_id)
|
||||
{
|
||||
ReplayState *state = opaque;
|
||||
fseek(replay_file, state->file_offset, SEEK_SET);
|
||||
/* If this was a vmstate, saved in recording mode,
|
||||
we need to initialize replay data fields. */
|
||||
replay_fetch_data_kind();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_replay = {
|
||||
.name = "replay",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.pre_save = replay_pre_save,
|
||||
.post_load = replay_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT64_ARRAY(cached_clock, ReplayState, REPLAY_CLOCK_COUNT),
|
||||
VMSTATE_UINT64(current_step, ReplayState),
|
||||
VMSTATE_INT32(instructions_count, ReplayState),
|
||||
VMSTATE_UINT32(data_kind, ReplayState),
|
||||
VMSTATE_UINT32(has_unread_data, ReplayState),
|
||||
VMSTATE_UINT64(file_offset, ReplayState),
|
||||
VMSTATE_UINT64(block_request_id, ReplayState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
};
|
||||
|
||||
void replay_vmstate_register(void)
|
||||
{
|
||||
vmstate_register(NULL, 0, &vmstate_replay, &replay_state);
|
||||
}
|
@ -31,7 +31,7 @@ int64_t replay_save_clock(ReplayClockKind kind, int64_t clock)
|
||||
|
||||
void replay_read_next_clock(ReplayClockKind kind)
|
||||
{
|
||||
unsigned int read_kind = replay_data_kind - EVENT_CLOCK;
|
||||
unsigned int read_kind = replay_state.data_kind - EVENT_CLOCK;
|
||||
|
||||
assert(read_kind == kind);
|
||||
|
||||
|
@ -38,15 +38,15 @@ bool replay_next_event_is(int event)
|
||||
|
||||
/* nothing to skip - not all instructions used */
|
||||
if (replay_state.instructions_count != 0) {
|
||||
assert(replay_data_kind == EVENT_INSTRUCTION);
|
||||
assert(replay_state.data_kind == EVENT_INSTRUCTION);
|
||||
return event == EVENT_INSTRUCTION;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (event == replay_data_kind) {
|
||||
if (event == replay_state.data_kind) {
|
||||
res = true;
|
||||
}
|
||||
switch (replay_data_kind) {
|
||||
switch (replay_state.data_kind) {
|
||||
case EVENT_SHUTDOWN:
|
||||
replay_finish_event();
|
||||
qemu_system_shutdown_request();
|
||||
@ -85,7 +85,7 @@ void replay_account_executed_instructions(void)
|
||||
replay_state.instructions_count -= count;
|
||||
replay_state.current_step += count;
|
||||
if (replay_state.instructions_count == 0) {
|
||||
assert(replay_data_kind == EVENT_INSTRUCTION);
|
||||
assert(replay_state.data_kind == EVENT_INSTRUCTION);
|
||||
replay_finish_event();
|
||||
/* Wake up iothread. This is required because
|
||||
timers will not expire until clock counters
|
||||
@ -188,7 +188,7 @@ bool replay_checkpoint(ReplayCheckpoint checkpoint)
|
||||
if (replay_mode == REPLAY_MODE_PLAY) {
|
||||
if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
|
||||
replay_finish_event();
|
||||
} else if (replay_data_kind != EVENT_ASYNC) {
|
||||
} else if (replay_state.data_kind != EVENT_ASYNC) {
|
||||
res = false;
|
||||
goto out;
|
||||
}
|
||||
@ -196,7 +196,7 @@ bool replay_checkpoint(ReplayCheckpoint checkpoint)
|
||||
/* replay_read_events may leave some unread events.
|
||||
Return false if not all of the events associated with
|
||||
checkpoint were processed */
|
||||
res = replay_data_kind != EVENT_ASYNC;
|
||||
res = replay_state.data_kind != EVENT_ASYNC;
|
||||
} else if (replay_mode == REPLAY_MODE_RECORD) {
|
||||
replay_put_event(EVENT_CHECKPOINT + checkpoint);
|
||||
replay_save_events(checkpoint);
|
||||
@ -237,9 +237,10 @@ static void replay_enable(const char *fname, int mode)
|
||||
replay_filename = g_strdup(fname);
|
||||
|
||||
replay_mode = mode;
|
||||
replay_data_kind = -1;
|
||||
replay_state.data_kind = -1;
|
||||
replay_state.instructions_count = 0;
|
||||
replay_state.current_step = 0;
|
||||
replay_state.has_unread_data = 0;
|
||||
|
||||
/* skip file header for RECORD and check it for PLAY */
|
||||
if (replay_mode == REPLAY_MODE_RECORD) {
|
||||
@ -291,6 +292,7 @@ void replay_configure(QemuOpts *opts)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
replay_vmstate_register();
|
||||
replay_enable(fname, mode);
|
||||
|
||||
out:
|
||||
|
@ -2407,7 +2407,7 @@ sub process {
|
||||
# we have e.g. CONFIG_LINUX and CONFIG_WIN32 for common cases
|
||||
# where they might be necessary.
|
||||
if ($line =~ m@^.\s*\#\s*if.*\b__@) {
|
||||
ERROR("architecture specific defines should be avoided\n" . $herecurr);
|
||||
WARN("architecture specific defines should be avoided\n" . $herecurr);
|
||||
}
|
||||
|
||||
# Check that the storage class is at the beginning of a declaration
|
||||
|
@ -67,3 +67,8 @@ void replay_char_read_all_save_buf(uint8_t *buf, int offset)
|
||||
void replay_block_event(QEMUBH *bh, uint64_t id)
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t blkreplay_next_id(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -1113,7 +1113,6 @@ out:
|
||||
|
||||
typedef struct MCEInjectionParams {
|
||||
Monitor *mon;
|
||||
X86CPU *cpu;
|
||||
int bank;
|
||||
uint64_t status;
|
||||
uint64_t mcg_status;
|
||||
@ -1122,14 +1121,14 @@ typedef struct MCEInjectionParams {
|
||||
int flags;
|
||||
} MCEInjectionParams;
|
||||
|
||||
static void do_inject_x86_mce(void *data)
|
||||
static void do_inject_x86_mce(CPUState *cs, void *data)
|
||||
{
|
||||
MCEInjectionParams *params = data;
|
||||
CPUX86State *cenv = ¶ms->cpu->env;
|
||||
CPUState *cpu = CPU(params->cpu);
|
||||
X86CPU *cpu = X86_CPU(cs);
|
||||
CPUX86State *cenv = &cpu->env;
|
||||
uint64_t *banks = cenv->mce_banks + 4 * params->bank;
|
||||
|
||||
cpu_synchronize_state(cpu);
|
||||
cpu_synchronize_state(cs);
|
||||
|
||||
/*
|
||||
* If there is an MCE exception being processed, ignore this SRAO MCE
|
||||
@ -1149,7 +1148,7 @@ static void do_inject_x86_mce(void *data)
|
||||
if ((cenv->mcg_cap & MCG_CTL_P) && cenv->mcg_ctl != ~(uint64_t)0) {
|
||||
monitor_printf(params->mon,
|
||||
"CPU %d: Uncorrected error reporting disabled\n",
|
||||
cpu->cpu_index);
|
||||
cs->cpu_index);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1161,7 +1160,7 @@ static void do_inject_x86_mce(void *data)
|
||||
monitor_printf(params->mon,
|
||||
"CPU %d: Uncorrected error reporting disabled for"
|
||||
" bank %d\n",
|
||||
cpu->cpu_index, params->bank);
|
||||
cs->cpu_index, params->bank);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1170,7 +1169,7 @@ static void do_inject_x86_mce(void *data)
|
||||
monitor_printf(params->mon,
|
||||
"CPU %d: Previous MCE still in progress, raising"
|
||||
" triple fault\n",
|
||||
cpu->cpu_index);
|
||||
cs->cpu_index);
|
||||
qemu_log_mask(CPU_LOG_RESET, "Triple fault\n");
|
||||
qemu_system_reset_request();
|
||||
return;
|
||||
@ -1182,7 +1181,7 @@ static void do_inject_x86_mce(void *data)
|
||||
banks[3] = params->misc;
|
||||
cenv->mcg_status = params->mcg_status;
|
||||
banks[1] = params->status;
|
||||
cpu_interrupt(cpu, CPU_INTERRUPT_MCE);
|
||||
cpu_interrupt(cs, CPU_INTERRUPT_MCE);
|
||||
} else if (!(banks[1] & MCI_STATUS_VAL)
|
||||
|| !(banks[1] & MCI_STATUS_UC)) {
|
||||
if (banks[1] & MCI_STATUS_VAL) {
|
||||
@ -1204,7 +1203,6 @@ void cpu_x86_inject_mce(Monitor *mon, X86CPU *cpu, int bank,
|
||||
CPUX86State *cenv = &cpu->env;
|
||||
MCEInjectionParams params = {
|
||||
.mon = mon,
|
||||
.cpu = cpu,
|
||||
.bank = bank,
|
||||
.status = status,
|
||||
.mcg_status = mcg_status,
|
||||
@ -1245,7 +1243,6 @@ void cpu_x86_inject_mce(Monitor *mon, X86CPU *cpu, int bank,
|
||||
if (other_cs == cs) {
|
||||
continue;
|
||||
}
|
||||
params.cpu = X86_CPU(other_cs);
|
||||
run_on_cpu(other_cs, do_inject_x86_mce, ¶ms);
|
||||
}
|
||||
}
|
||||
|
@ -150,10 +150,8 @@ static int kvm_get_tsc(CPUState *cs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void do_kvm_synchronize_tsc(void *arg)
|
||||
static inline void do_kvm_synchronize_tsc(CPUState *cpu, void *arg)
|
||||
{
|
||||
CPUState *cpu = arg;
|
||||
|
||||
kvm_get_tsc(cpu);
|
||||
}
|
||||
|
||||
@ -163,7 +161,7 @@ void kvm_synchronize_all_tsc(void)
|
||||
|
||||
if (kvm_enabled()) {
|
||||
CPU_FOREACH(cpu) {
|
||||
run_on_cpu(cpu, do_kvm_synchronize_tsc, cpu);
|
||||
run_on_cpu(cpu, do_kvm_synchronize_tsc, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ static void s390_cpu_machine_reset_cb(void *opaque)
|
||||
{
|
||||
S390CPU *cpu = opaque;
|
||||
|
||||
run_on_cpu(CPU(cpu), s390_do_cpu_full_reset, CPU(cpu));
|
||||
run_on_cpu(CPU(cpu), s390_do_cpu_full_reset, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -220,7 +220,7 @@ static void s390_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||
s390_cpu_gdb_init(cs);
|
||||
qemu_init_vcpu(cs);
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
run_on_cpu(cs, s390_do_cpu_full_reset, cs);
|
||||
run_on_cpu(cs, s390_do_cpu_full_reset, NULL);
|
||||
#else
|
||||
cpu_reset(cs);
|
||||
#endif
|
||||
|
@ -502,17 +502,14 @@ static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb,
|
||||
#define decode_basedisp_rs decode_basedisp_s
|
||||
|
||||
/* helper functions for run_on_cpu() */
|
||||
static inline void s390_do_cpu_reset(void *arg)
|
||||
static inline void s390_do_cpu_reset(CPUState *cs, void *arg)
|
||||
{
|
||||
CPUState *cs = arg;
|
||||
S390CPUClass *scc = S390_CPU_GET_CLASS(cs);
|
||||
|
||||
scc->cpu_reset(cs);
|
||||
}
|
||||
static inline void s390_do_cpu_full_reset(void *arg)
|
||||
static inline void s390_do_cpu_full_reset(CPUState *cs, void *arg)
|
||||
{
|
||||
CPUState *cs = arg;
|
||||
|
||||
cpu_reset(cs);
|
||||
}
|
||||
|
||||
|
@ -1385,7 +1385,6 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
|
||||
}
|
||||
|
||||
typedef struct SigpInfo {
|
||||
S390CPU *cpu;
|
||||
uint64_t param;
|
||||
int cc;
|
||||
uint64_t *status_reg;
|
||||
@ -1398,38 +1397,40 @@ static void set_sigp_status(SigpInfo *si, uint64_t status)
|
||||
si->cc = SIGP_CC_STATUS_STORED;
|
||||
}
|
||||
|
||||
static void sigp_start(void *arg)
|
||||
static void sigp_start(CPUState *cs, void *arg)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
SigpInfo *si = arg;
|
||||
|
||||
if (s390_cpu_get_state(si->cpu) != CPU_STATE_STOPPED) {
|
||||
if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
|
||||
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
||||
return;
|
||||
}
|
||||
|
||||
s390_cpu_set_state(CPU_STATE_OPERATING, si->cpu);
|
||||
s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
|
||||
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
||||
}
|
||||
|
||||
static void sigp_stop(void *arg)
|
||||
static void sigp_stop(CPUState *cs, void *arg)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
SigpInfo *si = arg;
|
||||
struct kvm_s390_irq irq = {
|
||||
.type = KVM_S390_SIGP_STOP,
|
||||
};
|
||||
|
||||
if (s390_cpu_get_state(si->cpu) != CPU_STATE_OPERATING) {
|
||||
if (s390_cpu_get_state(cpu) != CPU_STATE_OPERATING) {
|
||||
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
||||
return;
|
||||
}
|
||||
|
||||
/* disabled wait - sleeping in user space */
|
||||
if (CPU(si->cpu)->halted) {
|
||||
s390_cpu_set_state(CPU_STATE_STOPPED, si->cpu);
|
||||
if (cs->halted) {
|
||||
s390_cpu_set_state(CPU_STATE_STOPPED, cpu);
|
||||
} else {
|
||||
/* execute the stop function */
|
||||
si->cpu->env.sigp_order = SIGP_STOP;
|
||||
kvm_s390_vcpu_interrupt(si->cpu, &irq);
|
||||
cpu->env.sigp_order = SIGP_STOP;
|
||||
kvm_s390_vcpu_interrupt(cpu, &irq);
|
||||
}
|
||||
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
||||
}
|
||||
@ -1496,56 +1497,58 @@ static int kvm_s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sigp_stop_and_store_status(void *arg)
|
||||
static void sigp_stop_and_store_status(CPUState *cs, void *arg)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
SigpInfo *si = arg;
|
||||
struct kvm_s390_irq irq = {
|
||||
.type = KVM_S390_SIGP_STOP,
|
||||
};
|
||||
|
||||
/* disabled wait - sleeping in user space */
|
||||
if (s390_cpu_get_state(si->cpu) == CPU_STATE_OPERATING &&
|
||||
CPU(si->cpu)->halted) {
|
||||
s390_cpu_set_state(CPU_STATE_STOPPED, si->cpu);
|
||||
if (s390_cpu_get_state(cpu) == CPU_STATE_OPERATING && cs->halted) {
|
||||
s390_cpu_set_state(CPU_STATE_STOPPED, cpu);
|
||||
}
|
||||
|
||||
switch (s390_cpu_get_state(si->cpu)) {
|
||||
switch (s390_cpu_get_state(cpu)) {
|
||||
case CPU_STATE_OPERATING:
|
||||
si->cpu->env.sigp_order = SIGP_STOP_STORE_STATUS;
|
||||
kvm_s390_vcpu_interrupt(si->cpu, &irq);
|
||||
cpu->env.sigp_order = SIGP_STOP_STORE_STATUS;
|
||||
kvm_s390_vcpu_interrupt(cpu, &irq);
|
||||
/* store will be performed when handling the stop intercept */
|
||||
break;
|
||||
case CPU_STATE_STOPPED:
|
||||
/* already stopped, just store the status */
|
||||
cpu_synchronize_state(CPU(si->cpu));
|
||||
kvm_s390_store_status(si->cpu, KVM_S390_STORE_STATUS_DEF_ADDR, true);
|
||||
cpu_synchronize_state(cs);
|
||||
kvm_s390_store_status(cpu, KVM_S390_STORE_STATUS_DEF_ADDR, true);
|
||||
break;
|
||||
}
|
||||
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
||||
}
|
||||
|
||||
static void sigp_store_status_at_address(void *arg)
|
||||
static void sigp_store_status_at_address(CPUState *cs, void *arg)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
SigpInfo *si = arg;
|
||||
uint32_t address = si->param & 0x7ffffe00u;
|
||||
|
||||
/* cpu has to be stopped */
|
||||
if (s390_cpu_get_state(si->cpu) != CPU_STATE_STOPPED) {
|
||||
if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
|
||||
set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
|
||||
return;
|
||||
}
|
||||
|
||||
cpu_synchronize_state(CPU(si->cpu));
|
||||
cpu_synchronize_state(cs);
|
||||
|
||||
if (kvm_s390_store_status(si->cpu, address, false)) {
|
||||
if (kvm_s390_store_status(cpu, address, false)) {
|
||||
set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
|
||||
return;
|
||||
}
|
||||
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
||||
}
|
||||
|
||||
static void sigp_store_adtl_status(void *arg)
|
||||
static void sigp_store_adtl_status(CPUState *cs, void *arg)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
SigpInfo *si = arg;
|
||||
|
||||
if (!s390_has_feat(S390_FEAT_VECTOR)) {
|
||||
@ -1554,7 +1557,7 @@ static void sigp_store_adtl_status(void *arg)
|
||||
}
|
||||
|
||||
/* cpu has to be stopped */
|
||||
if (s390_cpu_get_state(si->cpu) != CPU_STATE_STOPPED) {
|
||||
if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
|
||||
set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
|
||||
return;
|
||||
}
|
||||
@ -1565,31 +1568,32 @@ static void sigp_store_adtl_status(void *arg)
|
||||
return;
|
||||
}
|
||||
|
||||
cpu_synchronize_state(CPU(si->cpu));
|
||||
cpu_synchronize_state(cs);
|
||||
|
||||
if (kvm_s390_store_adtl_status(si->cpu, si->param)) {
|
||||
if (kvm_s390_store_adtl_status(cpu, si->param)) {
|
||||
set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
|
||||
return;
|
||||
}
|
||||
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
||||
}
|
||||
|
||||
static void sigp_restart(void *arg)
|
||||
static void sigp_restart(CPUState *cs, void *arg)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
SigpInfo *si = arg;
|
||||
struct kvm_s390_irq irq = {
|
||||
.type = KVM_S390_RESTART,
|
||||
};
|
||||
|
||||
switch (s390_cpu_get_state(si->cpu)) {
|
||||
switch (s390_cpu_get_state(cpu)) {
|
||||
case CPU_STATE_STOPPED:
|
||||
/* the restart irq has to be delivered prior to any other pending irq */
|
||||
cpu_synchronize_state(CPU(si->cpu));
|
||||
do_restart_interrupt(&si->cpu->env);
|
||||
s390_cpu_set_state(CPU_STATE_OPERATING, si->cpu);
|
||||
cpu_synchronize_state(cs);
|
||||
do_restart_interrupt(&cpu->env);
|
||||
s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
|
||||
break;
|
||||
case CPU_STATE_OPERATING:
|
||||
kvm_s390_vcpu_interrupt(si->cpu, &irq);
|
||||
kvm_s390_vcpu_interrupt(cpu, &irq);
|
||||
break;
|
||||
}
|
||||
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
||||
@ -1597,20 +1601,18 @@ static void sigp_restart(void *arg)
|
||||
|
||||
int kvm_s390_cpu_restart(S390CPU *cpu)
|
||||
{
|
||||
SigpInfo si = {
|
||||
.cpu = cpu,
|
||||
};
|
||||
SigpInfo si = {};
|
||||
|
||||
run_on_cpu(CPU(cpu), sigp_restart, &si);
|
||||
DPRINTF("DONE: KVM cpu restart: %p\n", &cpu->env);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sigp_initial_cpu_reset(void *arg)
|
||||
static void sigp_initial_cpu_reset(CPUState *cs, void *arg)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
|
||||
SigpInfo *si = arg;
|
||||
CPUState *cs = CPU(si->cpu);
|
||||
S390CPUClass *scc = S390_CPU_GET_CLASS(si->cpu);
|
||||
|
||||
cpu_synchronize_state(cs);
|
||||
scc->initial_cpu_reset(cs);
|
||||
@ -1618,11 +1620,11 @@ static void sigp_initial_cpu_reset(void *arg)
|
||||
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
||||
}
|
||||
|
||||
static void sigp_cpu_reset(void *arg)
|
||||
static void sigp_cpu_reset(CPUState *cs, void *arg)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
|
||||
SigpInfo *si = arg;
|
||||
CPUState *cs = CPU(si->cpu);
|
||||
S390CPUClass *scc = S390_CPU_GET_CLASS(si->cpu);
|
||||
|
||||
cpu_synchronize_state(cs);
|
||||
scc->cpu_reset(cs);
|
||||
@ -1630,12 +1632,13 @@ static void sigp_cpu_reset(void *arg)
|
||||
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
||||
}
|
||||
|
||||
static void sigp_set_prefix(void *arg)
|
||||
static void sigp_set_prefix(CPUState *cs, void *arg)
|
||||
{
|
||||
S390CPU *cpu = S390_CPU(cs);
|
||||
SigpInfo *si = arg;
|
||||
uint32_t addr = si->param & 0x7fffe000u;
|
||||
|
||||
cpu_synchronize_state(CPU(si->cpu));
|
||||
cpu_synchronize_state(cs);
|
||||
|
||||
if (!address_space_access_valid(&address_space_memory, addr,
|
||||
sizeof(struct LowCore), false)) {
|
||||
@ -1644,13 +1647,13 @@ static void sigp_set_prefix(void *arg)
|
||||
}
|
||||
|
||||
/* cpu has to be stopped */
|
||||
if (s390_cpu_get_state(si->cpu) != CPU_STATE_STOPPED) {
|
||||
if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
|
||||
set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
|
||||
return;
|
||||
}
|
||||
|
||||
si->cpu->env.psa = addr;
|
||||
cpu_synchronize_post_init(CPU(si->cpu));
|
||||
cpu->env.psa = addr;
|
||||
cpu_synchronize_post_init(cs);
|
||||
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
||||
}
|
||||
|
||||
@ -1658,7 +1661,6 @@ static int handle_sigp_single_dst(S390CPU *dst_cpu, uint8_t order,
|
||||
uint64_t param, uint64_t *status_reg)
|
||||
{
|
||||
SigpInfo si = {
|
||||
.cpu = dst_cpu,
|
||||
.param = param,
|
||||
.status_reg = status_reg,
|
||||
};
|
||||
|
@ -126,7 +126,7 @@ static int modified_clear_reset(S390CPU *cpu)
|
||||
pause_all_vcpus();
|
||||
cpu_synchronize_all_states();
|
||||
CPU_FOREACH(t) {
|
||||
run_on_cpu(t, s390_do_cpu_full_reset, t);
|
||||
run_on_cpu(t, s390_do_cpu_full_reset, NULL);
|
||||
}
|
||||
s390_cmma_reset();
|
||||
subsystem_reset();
|
||||
@ -145,7 +145,7 @@ static int load_normal_reset(S390CPU *cpu)
|
||||
pause_all_vcpus();
|
||||
cpu_synchronize_all_states();
|
||||
CPU_FOREACH(t) {
|
||||
run_on_cpu(t, s390_do_cpu_reset, t);
|
||||
run_on_cpu(t, s390_do_cpu_reset, NULL);
|
||||
}
|
||||
s390_cmma_reset();
|
||||
subsystem_reset();
|
||||
|
@ -834,12 +834,19 @@ static void page_flush_tb(void)
|
||||
}
|
||||
|
||||
/* flush all the translation blocks */
|
||||
/* XXX: tb_flush is currently not thread safe */
|
||||
void tb_flush(CPUState *cpu)
|
||||
static void do_tb_flush(CPUState *cpu, void *data)
|
||||
{
|
||||
if (!tcg_enabled()) {
|
||||
return;
|
||||
unsigned tb_flush_req = (unsigned) (uintptr_t) data;
|
||||
|
||||
tb_lock();
|
||||
|
||||
/* If it's already been done on request of another CPU,
|
||||
* just retry.
|
||||
*/
|
||||
if (tcg_ctx.tb_ctx.tb_flush_count != tb_flush_req) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
#if defined(DEBUG_FLUSH)
|
||||
printf("qemu: flush code_size=%ld nb_tbs=%d avg_tb_size=%ld\n",
|
||||
(unsigned long)(tcg_ctx.code_gen_ptr - tcg_ctx.code_gen_buffer),
|
||||
@ -858,7 +865,6 @@ void tb_flush(CPUState *cpu)
|
||||
for (i = 0; i < TB_JMP_CACHE_SIZE; ++i) {
|
||||
atomic_set(&cpu->tb_jmp_cache[i], NULL);
|
||||
}
|
||||
atomic_mb_set(&cpu->tb_flushed, true);
|
||||
}
|
||||
|
||||
tcg_ctx.tb_ctx.nb_tbs = 0;
|
||||
@ -868,7 +874,19 @@ void tb_flush(CPUState *cpu)
|
||||
tcg_ctx.code_gen_ptr = tcg_ctx.code_gen_buffer;
|
||||
/* XXX: flush processor icache at this point if cache flush is
|
||||
expensive */
|
||||
tcg_ctx.tb_ctx.tb_flush_count++;
|
||||
atomic_mb_set(&tcg_ctx.tb_ctx.tb_flush_count,
|
||||
tcg_ctx.tb_ctx.tb_flush_count + 1);
|
||||
|
||||
done:
|
||||
tb_unlock();
|
||||
}
|
||||
|
||||
void tb_flush(CPUState *cpu)
|
||||
{
|
||||
if (tcg_enabled()) {
|
||||
uintptr_t tb_flush_req = atomic_mb_read(&tcg_ctx.tb_ctx.tb_flush_count);
|
||||
async_safe_run_on_cpu(cpu, do_tb_flush, (void *) tb_flush_req);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TB_CHECK
|
||||
@ -1175,9 +1193,8 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
buffer_overflow:
|
||||
/* flush must be done */
|
||||
tb_flush(cpu);
|
||||
/* cannot fail at this point */
|
||||
tb = tb_alloc(pc);
|
||||
assert(tb != NULL);
|
||||
mmap_unlock();
|
||||
cpu_loop_exit(cpu);
|
||||
}
|
||||
|
||||
gen_code_buf = tcg_ctx.code_gen_ptr;
|
||||
@ -1775,7 +1792,8 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf)
|
||||
qht_statistics_destroy(&hst);
|
||||
|
||||
cpu_fprintf(f, "\nStatistics:\n");
|
||||
cpu_fprintf(f, "TB flush count %d\n", tcg_ctx.tb_ctx.tb_flush_count);
|
||||
cpu_fprintf(f, "TB flush count %u\n",
|
||||
atomic_read(&tcg_ctx.tb_ctx.tb_flush_count));
|
||||
cpu_fprintf(f, "TB invalidate count %d\n",
|
||||
tcg_ctx.tb_ctx.tb_phys_invalidate_count);
|
||||
cpu_fprintf(f, "TLB flush count %d\n", tlb_flush_count);
|
||||
|
2
vl.c
2
vl.c
@ -784,6 +784,7 @@ void vm_start(void)
|
||||
if (runstate_is_running()) {
|
||||
qapi_event_send_stop(&error_abort);
|
||||
} else {
|
||||
replay_enable_events();
|
||||
cpu_enable_ticks();
|
||||
runstate_set(RUN_STATE_RUNNING);
|
||||
vm_state_notify(1, RUN_STATE_RUNNING);
|
||||
@ -3019,6 +3020,7 @@ int main(int argc, char **argv, char **envp)
|
||||
Error *err = NULL;
|
||||
bool list_data_dirs = false;
|
||||
|
||||
qemu_init_cpu_list();
|
||||
qemu_init_cpu_loop();
|
||||
qemu_mutex_lock_iothread();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user