mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
+1
-1
@@ -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/
|
||||
|
||||
+5
-10
@@ -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();
|
||||
|
||||
+8
-25
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
+2
-10
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
+11
-5
@@ -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;
|
||||
|
||||
+12
-6
@@ -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;
|
||||
|
||||
+2
-3
@@ -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;
|
||||
|
||||
|
||||
+3
-3
@@ -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),
|
||||
|
||||
+1
-1
@@ -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(),
|
||||
};
|
||||
|
||||
|
||||
+10
-21
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-10
@@ -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,
|
||||
};
|
||||
|
||||
+10
-8
@@ -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)
|
||||
|
||||
+2
-2
@@ -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 {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user