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:
Peter Maydell
2016-09-28 23:02:56 +01:00
49 changed files with 1157 additions and 521 deletions
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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) {
Vendored
+1 -2
View File
@@ -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
View File
@@ -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
View 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);
}
+8 -92
View File
@@ -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
View 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;
}
+2 -35
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+5
View File
@@ -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