mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/stsquad/tags/pull-mttcg-240217-1' into staging
This is the MTTCG pull-request as posted yesterday. # gpg: Signature made Fri 24 Feb 2017 11:17:51 GMT # gpg: using RSA key 0xFBD0DB095A9E2A44 # gpg: Good signature from "Alex Bennée (Master Work Key) <alex.bennee@linaro.org>" # Primary key fingerprint: 6685 AE99 E751 67BC AFC8 DF35 FBD0 DB09 5A9E 2A44 * remotes/stsquad/tags/pull-mttcg-240217-1: (24 commits) tcg: enable MTTCG by default for ARM on x86 hosts hw/misc/imx6_src: defer clearing of SRC_SCR reset bits target-arm: ensure all cross vCPUs TLB flushes complete target-arm: don't generate WFE/YIELD calls for MTTCG target-arm/powerctl: defer cpu reset work to CPU context cputlb: introduce tlb_flush_*_all_cpus[_synced] cputlb: atomically update tlb fields used by tlb_reset_dirty cputlb: add tlb_flush_by_mmuidx async routines cputlb and arm/sparc targets: convert mmuidx flushes from varg to bitmap cputlb: introduce tlb_flush_* async work. cputlb: tweak qemu_ram_addr_from_host_nofail reporting cputlb: add assert_cpu_is_self checks tcg: handle EXCP_ATOMIC exception for system emulation tcg: enable thread-per-vCPU tcg: enable tb_lock() for SoftMMU tcg: remove global exit_request tcg: drop global lock during TCG code execution tcg: rename tcg_current_cpu to tcg_current_rr_cpu tcg: add kick timer for single-threaded vCPU emulation tcg: add options for enabling MTTCG ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
@@ -5879,6 +5879,7 @@ mkdir -p $target_dir
|
||||
echo "# Automatically generated by configure - do not modify" > $config_target_mak
|
||||
|
||||
bflt="no"
|
||||
mttcg="no"
|
||||
interp_prefix1=$(echo "$interp_prefix" | sed "s/%M/$target_name/g")
|
||||
gdb_xml_files=""
|
||||
|
||||
@@ -5897,11 +5898,13 @@ case "$target_name" in
|
||||
arm|armeb)
|
||||
TARGET_ARCH=arm
|
||||
bflt="yes"
|
||||
mttcg="yes"
|
||||
gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml"
|
||||
;;
|
||||
aarch64)
|
||||
TARGET_BASE_ARCH=arm
|
||||
bflt="yes"
|
||||
mttcg="yes"
|
||||
gdb_xml_files="aarch64-core.xml aarch64-fpu.xml arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml"
|
||||
;;
|
||||
cris)
|
||||
@@ -6066,6 +6069,9 @@ if test "$target_bigendian" = "yes" ; then
|
||||
fi
|
||||
if test "$target_softmmu" = "yes" ; then
|
||||
echo "CONFIG_SOFTMMU=y" >> $config_target_mak
|
||||
if test "$mttcg" = "yes" ; then
|
||||
echo "TARGET_SUPPORTS_MTTCG=y" >> $config_target_mak
|
||||
fi
|
||||
fi
|
||||
if test "$target_user_only" = "yes" ; then
|
||||
echo "CONFIG_USER_ONLY=y" >> $config_target_mak
|
||||
|
||||
@@ -23,9 +23,6 @@
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/memory-internal.h"
|
||||
|
||||
bool exit_request;
|
||||
CPUState *tcg_current_cpu;
|
||||
|
||||
/* exit the current TB, but without causing any exception to be raised */
|
||||
void cpu_loop_exit_noexc(CPUState *cpu)
|
||||
{
|
||||
|
||||
+59
-30
@@ -29,6 +29,7 @@
|
||||
#include "qemu/rcu.h"
|
||||
#include "exec/tb-hash.h"
|
||||
#include "exec/log.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY)
|
||||
#include "hw/i386/apic.h"
|
||||
#endif
|
||||
@@ -227,20 +228,43 @@ static void cpu_exec_nocache(CPUState *cpu, int max_cycles,
|
||||
|
||||
static void cpu_exec_step(CPUState *cpu)
|
||||
{
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
CPUArchState *env = (CPUArchState *)cpu->env_ptr;
|
||||
TranslationBlock *tb;
|
||||
target_ulong cs_base, pc;
|
||||
uint32_t flags;
|
||||
|
||||
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
|
||||
tb = tb_gen_code(cpu, pc, cs_base, flags,
|
||||
1 | CF_NOCACHE | CF_IGNORE_ICOUNT);
|
||||
tb->orig_tb = NULL;
|
||||
/* execute the generated code */
|
||||
trace_exec_tb_nocache(tb, pc);
|
||||
cpu_tb_exec(cpu, tb);
|
||||
tb_phys_invalidate(tb, -1);
|
||||
tb_free(tb);
|
||||
if (sigsetjmp(cpu->jmp_env, 0) == 0) {
|
||||
mmap_lock();
|
||||
tb_lock();
|
||||
tb = tb_gen_code(cpu, pc, cs_base, flags,
|
||||
1 | CF_NOCACHE | CF_IGNORE_ICOUNT);
|
||||
tb->orig_tb = NULL;
|
||||
tb_unlock();
|
||||
mmap_unlock();
|
||||
|
||||
cc->cpu_exec_enter(cpu);
|
||||
/* execute the generated code */
|
||||
trace_exec_tb_nocache(tb, pc);
|
||||
cpu_tb_exec(cpu, tb);
|
||||
cc->cpu_exec_exit(cpu);
|
||||
|
||||
tb_lock();
|
||||
tb_phys_invalidate(tb, -1);
|
||||
tb_free(tb);
|
||||
tb_unlock();
|
||||
} else {
|
||||
/* We may have exited due to another problem here, so we need
|
||||
* to reset any tb_locks we may have taken but didn't release.
|
||||
* The mmap_lock is dropped by tb_gen_code if it runs out of
|
||||
* memory.
|
||||
*/
|
||||
#ifndef CONFIG_SOFTMMU
|
||||
tcg_debug_assert(!have_mmap_lock());
|
||||
#endif
|
||||
tb_lock_reset();
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_exec_step_atomic(CPUState *cpu)
|
||||
@@ -384,12 +408,13 @@ static inline bool cpu_handle_halt(CPUState *cpu)
|
||||
if ((cpu->interrupt_request & CPU_INTERRUPT_POLL)
|
||||
&& replay_interrupt()) {
|
||||
X86CPU *x86_cpu = X86_CPU(cpu);
|
||||
qemu_mutex_lock_iothread();
|
||||
apic_poll_irq(x86_cpu->apic_state);
|
||||
cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL);
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
#endif
|
||||
if (!cpu_has_work(cpu)) {
|
||||
current_cpu = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -439,7 +464,9 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
|
||||
#else
|
||||
if (replay_exception()) {
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
qemu_mutex_lock_iothread();
|
||||
cc->do_interrupt(cpu);
|
||||
qemu_mutex_unlock_iothread();
|
||||
cpu->exception_index = -1;
|
||||
} else if (!replay_has_interrupt()) {
|
||||
/* give a chance to iothread in replay mode */
|
||||
@@ -465,9 +492,11 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
TranslationBlock **last_tb)
|
||||
{
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
int interrupt_request = cpu->interrupt_request;
|
||||
|
||||
if (unlikely(interrupt_request)) {
|
||||
if (unlikely(atomic_read(&cpu->interrupt_request))) {
|
||||
int interrupt_request;
|
||||
qemu_mutex_lock_iothread();
|
||||
interrupt_request = cpu->interrupt_request;
|
||||
if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) {
|
||||
/* Mask out external interrupts for this step. */
|
||||
interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK;
|
||||
@@ -475,6 +504,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
if (interrupt_request & CPU_INTERRUPT_DEBUG) {
|
||||
cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
|
||||
cpu->exception_index = EXCP_DEBUG;
|
||||
qemu_mutex_unlock_iothread();
|
||||
return true;
|
||||
}
|
||||
if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) {
|
||||
@@ -484,6 +514,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
cpu->interrupt_request &= ~CPU_INTERRUPT_HALT;
|
||||
cpu->halted = 1;
|
||||
cpu->exception_index = EXCP_HLT;
|
||||
qemu_mutex_unlock_iothread();
|
||||
return true;
|
||||
}
|
||||
#if defined(TARGET_I386)
|
||||
@@ -494,12 +525,14 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0, 0);
|
||||
do_cpu_init(x86_cpu);
|
||||
cpu->exception_index = EXCP_HALTED;
|
||||
qemu_mutex_unlock_iothread();
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
else if (interrupt_request & CPU_INTERRUPT_RESET) {
|
||||
replay_interrupt();
|
||||
cpu_reset(cpu);
|
||||
qemu_mutex_unlock_iothread();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -522,7 +555,12 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
the program flow was changed */
|
||||
*last_tb = NULL;
|
||||
}
|
||||
|
||||
/* If we exit via cpu_loop_exit/longjmp it is reset in cpu_exec */
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
|
||||
|
||||
if (unlikely(atomic_read(&cpu->exit_request) || replay_has_interrupt())) {
|
||||
atomic_set(&cpu->exit_request, 0);
|
||||
cpu->exception_index = EXCP_INTERRUPT;
|
||||
@@ -548,15 +586,13 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
|
||||
*tb_exit = ret & TB_EXIT_MASK;
|
||||
switch (*tb_exit) {
|
||||
case TB_EXIT_REQUESTED:
|
||||
/* Something asked us to stop executing
|
||||
* chained TBs; just continue round the main
|
||||
* loop. Whatever requested the exit will also
|
||||
* have set something else (eg exit_request or
|
||||
* interrupt_request) which we will handle
|
||||
* next time around the loop. But we need to
|
||||
* ensure the zeroing of tcg_exit_req (see cpu_tb_exec)
|
||||
* comes before the next read of cpu->exit_request
|
||||
* or cpu->interrupt_request.
|
||||
/* Something asked us to stop executing chained TBs; just
|
||||
* continue round the main loop. Whatever requested the exit
|
||||
* will also have set something else (eg interrupt_request)
|
||||
* which we will handle next time around the loop. But we
|
||||
* need to ensure the tcg_exit_req read in generated code
|
||||
* comes before the next read of cpu->exit_request or
|
||||
* cpu->interrupt_request.
|
||||
*/
|
||||
smp_mb();
|
||||
*last_tb = NULL;
|
||||
@@ -608,13 +644,8 @@ int cpu_exec(CPUState *cpu)
|
||||
return EXCP_HALTED;
|
||||
}
|
||||
|
||||
atomic_mb_set(&tcg_current_cpu, cpu);
|
||||
rcu_read_lock();
|
||||
|
||||
if (unlikely(atomic_mb_read(&exit_request))) {
|
||||
cpu->exit_request = 1;
|
||||
}
|
||||
|
||||
cc->cpu_exec_enter(cpu);
|
||||
|
||||
/* Calculate difference between guest clock and host clock.
|
||||
@@ -640,6 +671,9 @@ int cpu_exec(CPUState *cpu)
|
||||
#endif /* buggy compiler */
|
||||
cpu->can_do_io = 1;
|
||||
tb_lock_reset();
|
||||
if (qemu_mutex_iothread_locked()) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
}
|
||||
|
||||
/* if an exception is pending, we execute it here */
|
||||
@@ -659,10 +693,5 @@ int cpu_exec(CPUState *cpu)
|
||||
cc->cpu_exec_exit(cpu);
|
||||
rcu_read_unlock();
|
||||
|
||||
/* fail safe : never use current_cpu outside cpu_exec() */
|
||||
current_cpu = NULL;
|
||||
|
||||
/* Does not need atomic_mb_set because a spurious wakeup is okay. */
|
||||
atomic_set(&tcg_current_cpu, NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,350 @@
|
||||
Copyright (c) 2015-2016 Linaro Ltd.
|
||||
|
||||
This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
later. See the COPYING file in the top-level directory.
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
This document outlines the design for multi-threaded TCG system-mode
|
||||
emulation. The current user-mode emulation mirrors the thread
|
||||
structure of the translated executable. Some of the work will be
|
||||
applicable to both system and linux-user emulation.
|
||||
|
||||
The original system-mode TCG implementation was single threaded and
|
||||
dealt with multiple CPUs with simple round-robin scheduling. This
|
||||
simplified a lot of things but became increasingly limited as systems
|
||||
being emulated gained additional cores and per-core performance gains
|
||||
for host systems started to level off.
|
||||
|
||||
vCPU Scheduling
|
||||
===============
|
||||
|
||||
We introduce a new running mode where each vCPU will run on its own
|
||||
user-space thread. This will be enabled by default for all FE/BE
|
||||
combinations that have had the required work done to support this
|
||||
safely.
|
||||
|
||||
In the general case of running translated code there should be no
|
||||
inter-vCPU dependencies and all vCPUs should be able to run at full
|
||||
speed. Synchronisation will only be required while accessing internal
|
||||
shared data structures or when the emulated architecture requires a
|
||||
coherent representation of the emulated machine state.
|
||||
|
||||
Shared Data Structures
|
||||
======================
|
||||
|
||||
Main Run Loop
|
||||
-------------
|
||||
|
||||
Even when there is no code being generated there are a number of
|
||||
structures associated with the hot-path through the main run-loop.
|
||||
These are associated with looking up the next translation block to
|
||||
execute. These include:
|
||||
|
||||
tb_jmp_cache (per-vCPU, cache of recent jumps)
|
||||
tb_ctx.htable (global hash table, phys address->tb lookup)
|
||||
|
||||
As TB linking only occurs when blocks are in the same page this code
|
||||
is critical to performance as looking up the next TB to execute is the
|
||||
most common reason to exit the generated code.
|
||||
|
||||
DESIGN REQUIREMENT: Make access to lookup structures safe with
|
||||
multiple reader/writer threads. Minimise any lock contention to do it.
|
||||
|
||||
The hot-path avoids using locks where possible. The tb_jmp_cache is
|
||||
updated with atomic accesses to ensure consistent results. The fall
|
||||
back QHT based hash table is also designed for lockless lookups. Locks
|
||||
are only taken when code generation is required or TranslationBlocks
|
||||
have their block-to-block jumps patched.
|
||||
|
||||
Global TCG State
|
||||
----------------
|
||||
|
||||
We need to protect the entire code generation cycle including any post
|
||||
generation patching of the translated code. This also implies a shared
|
||||
translation buffer which contains code running on all cores. Any
|
||||
execution path that comes to the main run loop will need to hold a
|
||||
mutex for code generation. This also includes times when we need flush
|
||||
code or entries from any shared lookups/caches. Structures held on a
|
||||
per-vCPU basis won't need locking unless other vCPUs will need to
|
||||
modify them.
|
||||
|
||||
DESIGN REQUIREMENT: Add locking around all code generation and TB
|
||||
patching.
|
||||
|
||||
(Current solution)
|
||||
|
||||
Mainly as part of the linux-user work all code generation is
|
||||
serialised with a tb_lock(). For the SoftMMU tb_lock() also takes the
|
||||
place of mmap_lock() in linux-user.
|
||||
|
||||
Translation Blocks
|
||||
------------------
|
||||
|
||||
Currently the whole system shares a single code generation buffer
|
||||
which when full will force a flush of all translations and start from
|
||||
scratch again. Some operations also force a full flush of translations
|
||||
including:
|
||||
|
||||
- debugging operations (breakpoint insertion/removal)
|
||||
- some CPU helper functions
|
||||
|
||||
This is done with the async_safe_run_on_cpu() mechanism to ensure all
|
||||
vCPUs are quiescent when changes are being made to shared global
|
||||
structures.
|
||||
|
||||
More granular translation invalidation events are typically due
|
||||
to a change of the state of a physical page:
|
||||
|
||||
- code modification (self modify code, patching code)
|
||||
- page changes (new page mapping in linux-user mode)
|
||||
|
||||
While setting the invalid flag in a TranslationBlock will stop it
|
||||
being used when looked up in the hot-path there are a number of other
|
||||
book-keeping structures that need to be safely cleared.
|
||||
|
||||
Any TranslationBlocks which have been patched to jump directly to the
|
||||
now invalid blocks need the jump patches reversing so they will return
|
||||
to the C code.
|
||||
|
||||
There are a number of look-up caches that need to be properly updated
|
||||
including the:
|
||||
|
||||
- jump lookup cache
|
||||
- the physical-to-tb lookup hash table
|
||||
- the global page table
|
||||
|
||||
The global page table (l1_map) which provides a multi-level look-up
|
||||
for PageDesc structures which contain pointers to the start of a
|
||||
linked list of all Translation Blocks in that page (see page_next).
|
||||
|
||||
Both the jump patching and the page cache involve linked lists that
|
||||
the invalidated TranslationBlock needs to be removed from.
|
||||
|
||||
DESIGN REQUIREMENT: Safely handle invalidation of TBs
|
||||
- safely patch/revert direct jumps
|
||||
- remove central PageDesc lookup entries
|
||||
- ensure lookup caches/hashes are safely updated
|
||||
|
||||
(Current solution)
|
||||
|
||||
The direct jump themselves are updated atomically by the TCG
|
||||
tb_set_jmp_target() code. Modification to the linked lists that allow
|
||||
searching for linked pages are done under the protect of the
|
||||
tb_lock().
|
||||
|
||||
The global page table is protected by the tb_lock() in system-mode and
|
||||
mmap_lock() in linux-user mode.
|
||||
|
||||
The lookup caches are updated atomically and the lookup hash uses QHT
|
||||
which is designed for concurrent safe lookup.
|
||||
|
||||
|
||||
Memory maps and TLBs
|
||||
--------------------
|
||||
|
||||
The memory handling code is fairly critical to the speed of memory
|
||||
access in the emulated system. The SoftMMU code is designed so the
|
||||
hot-path can be handled entirely within translated code. This is
|
||||
handled with a per-vCPU TLB structure which once populated will allow
|
||||
a series of accesses to the page to occur without exiting the
|
||||
translated code. It is possible to set flags in the TLB address which
|
||||
will ensure the slow-path is taken for each access. This can be done
|
||||
to support:
|
||||
|
||||
- Memory regions (dividing up access to PIO, MMIO and RAM)
|
||||
- Dirty page tracking (for code gen, SMC detection, migration and display)
|
||||
- Virtual TLB (for translating guest address->real address)
|
||||
|
||||
When the TLB tables are updated by a vCPU thread other than their own
|
||||
we need to ensure it is done in a safe way so no inconsistent state is
|
||||
seen by the vCPU thread.
|
||||
|
||||
Some operations require updating a number of vCPUs TLBs at the same
|
||||
time in a synchronised manner.
|
||||
|
||||
DESIGN REQUIREMENTS:
|
||||
|
||||
- TLB Flush All/Page
|
||||
- can be across-vCPUs
|
||||
- cross vCPU TLB flush may need other vCPU brought to halt
|
||||
- change may need to be visible to the calling vCPU immediately
|
||||
- TLB Flag Update
|
||||
- usually cross-vCPU
|
||||
- want change to be visible as soon as possible
|
||||
- TLB Update (update a CPUTLBEntry, via tlb_set_page_with_attrs)
|
||||
- This is a per-vCPU table - by definition can't race
|
||||
- updated by its own thread when the slow-path is forced
|
||||
|
||||
(Current solution)
|
||||
|
||||
We have updated cputlb.c to defer operations when a cross-vCPU
|
||||
operation with async_run_on_cpu() which ensures each vCPU sees a
|
||||
coherent state when it next runs its work (in a few instructions
|
||||
time).
|
||||
|
||||
A new set up operations (tlb_flush_*_all_cpus) take an additional flag
|
||||
which when set will force synchronisation by setting the source vCPUs
|
||||
work as "safe work" and exiting the cpu run loop. This ensure by the
|
||||
time execution restarts all flush operations have completed.
|
||||
|
||||
TLB flag updates are all done atomically and are also protected by the
|
||||
tb_lock() which is used by the functions that update the TLB in bulk.
|
||||
|
||||
(Known limitation)
|
||||
|
||||
Not really a limitation but the wait mechanism is overly strict for
|
||||
some architectures which only need flushes completed by a barrier
|
||||
instruction. This could be a future optimisation.
|
||||
|
||||
Emulated hardware state
|
||||
-----------------------
|
||||
|
||||
Currently thanks to KVM work any access to IO memory is automatically
|
||||
protected by the global iothread mutex, also known as the BQL (Big
|
||||
Qemu Lock). Any IO region that doesn't use global mutex is expected to
|
||||
do its own locking.
|
||||
|
||||
However IO memory isn't the only way emulated hardware state can be
|
||||
modified. Some architectures have model specific registers that
|
||||
trigger hardware emulation features. Generally any translation helper
|
||||
that needs to update more than a single vCPUs of state should take the
|
||||
BQL.
|
||||
|
||||
As the BQL, or global iothread mutex is shared across the system we
|
||||
push the use of the lock as far down into the TCG code as possible to
|
||||
minimise contention.
|
||||
|
||||
(Current solution)
|
||||
|
||||
MMIO access automatically serialises hardware emulation by way of the
|
||||
BQL. Currently ARM targets serialise all ARM_CP_IO register accesses
|
||||
and also defer the reset/startup of vCPUs to the vCPU context by way
|
||||
of async_run_on_cpu().
|
||||
|
||||
Updates to interrupt state are also protected by the BQL as they can
|
||||
often be cross vCPU.
|
||||
|
||||
Memory Consistency
|
||||
==================
|
||||
|
||||
Between emulated guests and host systems there are a range of memory
|
||||
consistency models. Even emulating weakly ordered systems on strongly
|
||||
ordered hosts needs to ensure things like store-after-load re-ordering
|
||||
can be prevented when the guest wants to.
|
||||
|
||||
Memory Barriers
|
||||
---------------
|
||||
|
||||
Barriers (sometimes known as fences) provide a mechanism for software
|
||||
to enforce a particular ordering of memory operations from the point
|
||||
of view of external observers (e.g. another processor core). They can
|
||||
apply to any memory operations as well as just loads or stores.
|
||||
|
||||
The Linux kernel has an excellent write-up on the various forms of
|
||||
memory barrier and the guarantees they can provide [1].
|
||||
|
||||
Barriers are often wrapped around synchronisation primitives to
|
||||
provide explicit memory ordering semantics. However they can be used
|
||||
by themselves to provide safe lockless access by ensuring for example
|
||||
a change to a signal flag will only be visible once the changes to
|
||||
payload are.
|
||||
|
||||
DESIGN REQUIREMENT: Add a new tcg_memory_barrier op
|
||||
|
||||
This would enforce a strong load/store ordering so all loads/stores
|
||||
complete at the memory barrier. On single-core non-SMP strongly
|
||||
ordered backends this could become a NOP.
|
||||
|
||||
Aside from explicit standalone memory barrier instructions there are
|
||||
also implicit memory ordering semantics which comes with each guest
|
||||
memory access instruction. For example all x86 load/stores come with
|
||||
fairly strong guarantees of sequential consistency where as ARM has
|
||||
special variants of load/store instructions that imply acquire/release
|
||||
semantics.
|
||||
|
||||
In the case of a strongly ordered guest architecture being emulated on
|
||||
a weakly ordered host the scope for a heavy performance impact is
|
||||
quite high.
|
||||
|
||||
DESIGN REQUIREMENTS: Be efficient with use of memory barriers
|
||||
- host systems with stronger implied guarantees can skip some barriers
|
||||
- merge consecutive barriers to the strongest one
|
||||
|
||||
(Current solution)
|
||||
|
||||
The system currently has a tcg_gen_mb() which will add memory barrier
|
||||
operations if code generation is being done in a parallel context. The
|
||||
tcg_optimize() function attempts to merge barriers up to their
|
||||
strongest form before any load/store operations. The solution was
|
||||
originally developed and tested for linux-user based systems. All
|
||||
backends have been converted to emit fences when required. So far the
|
||||
following front-ends have been updated to emit fences when required:
|
||||
|
||||
- target-i386
|
||||
- target-arm
|
||||
- target-aarch64
|
||||
- target-alpha
|
||||
- target-mips
|
||||
|
||||
Memory Control and Maintenance
|
||||
------------------------------
|
||||
|
||||
This includes a class of instructions for controlling system cache
|
||||
behaviour. While QEMU doesn't model cache behaviour these instructions
|
||||
are often seen when code modification has taken place to ensure the
|
||||
changes take effect.
|
||||
|
||||
Synchronisation Primitives
|
||||
--------------------------
|
||||
|
||||
There are two broad types of synchronisation primitives found in
|
||||
modern ISAs: atomic instructions and exclusive regions.
|
||||
|
||||
The first type offer a simple atomic instruction which will guarantee
|
||||
some sort of test and conditional store will be truly atomic w.r.t.
|
||||
other cores sharing access to the memory. The classic example is the
|
||||
x86 cmpxchg instruction.
|
||||
|
||||
The second type offer a pair of load/store instructions which offer a
|
||||
guarantee that an region of memory has not been touched between the
|
||||
load and store instructions. An example of this is ARM's ldrex/strex
|
||||
pair where the strex instruction will return a flag indicating a
|
||||
successful store only if no other CPU has accessed the memory region
|
||||
since the ldrex.
|
||||
|
||||
Traditionally TCG has generated a series of operations that work
|
||||
because they are within the context of a single translation block so
|
||||
will have completed before another CPU is scheduled. However with
|
||||
the ability to have multiple threads running to emulate multiple CPUs
|
||||
we will need to explicitly expose these semantics.
|
||||
|
||||
DESIGN REQUIREMENTS:
|
||||
- Support classic atomic instructions
|
||||
- Support load/store exclusive (or load link/store conditional) pairs
|
||||
- Generic enough infrastructure to support all guest architectures
|
||||
CURRENT OPEN QUESTIONS:
|
||||
- How problematic is the ABA problem in general?
|
||||
|
||||
(Current solution)
|
||||
|
||||
The TCG provides a number of atomic helpers (tcg_gen_atomic_*) which
|
||||
can be used directly or combined to emulate other instructions like
|
||||
ARM's ldrex/strex instructions. While they are susceptible to the ABA
|
||||
problem so far common guests have not implemented patterns where
|
||||
this may be a problem - typically presenting a locking ABI which
|
||||
assumes cmpxchg like semantics.
|
||||
|
||||
The code also includes a fall-back for cases where multi-threaded TCG
|
||||
ops can't work (e.g. guest atomic width > host atomic width). In this
|
||||
case an EXCP_ATOMIC exit occurs and the instruction is emulated with
|
||||
an exclusive lock which ensures all emulation is serialised.
|
||||
|
||||
While the atomic helpers look good enough for now there may be a need
|
||||
to look at solutions that can more closely model the guest
|
||||
architectures semantics.
|
||||
|
||||
==========
|
||||
|
||||
[1] https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/plain/Documentation/memory-barriers.txt
|
||||
@@ -2134,9 +2134,9 @@ static void check_watchpoint(int offset, int len, MemTxAttrs attrs, int flags)
|
||||
}
|
||||
cpu->watchpoint_hit = wp;
|
||||
|
||||
/* The tb_lock will be reset when cpu_loop_exit or
|
||||
* cpu_loop_exit_noexc longjmp back into the cpu_exec
|
||||
* main loop.
|
||||
/* Both tb_lock and iothread_mutex will be reset when
|
||||
* cpu_loop_exit or cpu_loop_exit_noexc longjmp
|
||||
* back into the cpu_exec main loop.
|
||||
*/
|
||||
tb_lock();
|
||||
tb_check_watchpoint(cpu);
|
||||
@@ -2371,8 +2371,14 @@ static void io_mem_init(void)
|
||||
memory_region_init_io(&io_mem_rom, NULL, &unassigned_mem_ops, NULL, NULL, UINT64_MAX);
|
||||
memory_region_init_io(&io_mem_unassigned, NULL, &unassigned_mem_ops, NULL,
|
||||
NULL, UINT64_MAX);
|
||||
|
||||
/* io_mem_notdirty calls tb_invalidate_phys_page_fast,
|
||||
* which can be called without the iothread mutex.
|
||||
*/
|
||||
memory_region_init_io(&io_mem_notdirty, NULL, ¬dirty_mem_ops, NULL,
|
||||
NULL, UINT64_MAX);
|
||||
memory_region_clear_global_locking(&io_mem_notdirty);
|
||||
|
||||
memory_region_init_io(&io_mem_watch, NULL, &watch_mem_ops, NULL,
|
||||
NULL, UINT64_MAX);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/irq.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
+2
-2
@@ -457,8 +457,8 @@ static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip)
|
||||
resume_all_vcpus();
|
||||
|
||||
if (!kvm_enabled()) {
|
||||
/* tb_lock will be reset when cpu_loop_exit_noexc longjmps
|
||||
* back into the cpu_exec loop. */
|
||||
/* Both tb_lock and iothread_mutex will be reset when
|
||||
* longjmps back into the cpu_exec loop. */
|
||||
tb_lock();
|
||||
tb_gen_code(cs, current_pc, current_cs_base, current_flags, 1);
|
||||
cpu_loop_exit_noexc(cs);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "trace.h"
|
||||
#include "gicv3_internal.h"
|
||||
#include "cpu.h"
|
||||
@@ -733,6 +734,8 @@ void gicv3_cpuif_update(GICv3CPUState *cs)
|
||||
ARMCPU *cpu = ARM_CPU(cs->cpu);
|
||||
CPUARMState *env = &cpu->env;
|
||||
|
||||
g_assert(qemu_mutex_iothread_locked());
|
||||
|
||||
trace_gicv3_cpuif_update(gicv3_redist_affid(cs), cs->hppi.irq,
|
||||
cs->hppi.grp, cs->hppi.prio);
|
||||
|
||||
|
||||
+49
-9
@@ -14,6 +14,7 @@
|
||||
#include "qemu/bitops.h"
|
||||
#include "qemu/log.h"
|
||||
#include "arm-powerctl.h"
|
||||
#include "qom/cpu.h"
|
||||
|
||||
#ifndef DEBUG_IMX6_SRC
|
||||
#define DEBUG_IMX6_SRC 0
|
||||
@@ -113,6 +114,45 @@ static uint64_t imx6_src_read(void *opaque, hwaddr offset, unsigned size)
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/* The reset is asynchronous so we need to defer clearing the reset
|
||||
* bit until the work is completed.
|
||||
*/
|
||||
|
||||
struct SRCSCRResetInfo {
|
||||
IMX6SRCState *s;
|
||||
int reset_bit;
|
||||
};
|
||||
|
||||
static void imx6_clear_reset_bit(CPUState *cpu, run_on_cpu_data data)
|
||||
{
|
||||
struct SRCSCRResetInfo *ri = data.host_ptr;
|
||||
IMX6SRCState *s = ri->s;
|
||||
|
||||
assert(qemu_mutex_iothread_locked());
|
||||
|
||||
s->regs[SRC_SCR] = deposit32(s->regs[SRC_SCR], ri->reset_bit, 1, 0);
|
||||
DPRINTF("reg[%s] <= 0x%" PRIx32 "\n",
|
||||
imx6_src_reg_name(SRC_SCR), s->regs[SRC_SCR]);
|
||||
|
||||
g_free(ri);
|
||||
}
|
||||
|
||||
static void imx6_defer_clear_reset_bit(int cpuid,
|
||||
IMX6SRCState *s,
|
||||
unsigned long reset_shift)
|
||||
{
|
||||
struct SRCSCRResetInfo *ri;
|
||||
|
||||
ri = g_malloc(sizeof(struct SRCSCRResetInfo));
|
||||
ri->s = s;
|
||||
ri->reset_bit = reset_shift;
|
||||
|
||||
async_run_on_cpu(arm_get_cpu_by_id(cpuid), imx6_clear_reset_bit,
|
||||
RUN_ON_CPU_HOST_PTR(ri));
|
||||
}
|
||||
|
||||
|
||||
static void imx6_src_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
@@ -153,7 +193,7 @@ static void imx6_src_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
arm_set_cpu_off(3);
|
||||
}
|
||||
/* We clear the reset bits as the processor changed state */
|
||||
clear_bit(CORE3_RST_SHIFT, ¤t_value);
|
||||
imx6_defer_clear_reset_bit(3, s, CORE3_RST_SHIFT);
|
||||
clear_bit(CORE3_RST_SHIFT, &change_mask);
|
||||
}
|
||||
if (EXTRACT(change_mask, CORE2_ENABLE)) {
|
||||
@@ -162,11 +202,11 @@ static void imx6_src_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
arm_set_cpu_on(2, s->regs[SRC_GPR5], s->regs[SRC_GPR6],
|
||||
3, false);
|
||||
} else {
|
||||
/* CORE 3 is shut down */
|
||||
/* CORE 2 is shut down */
|
||||
arm_set_cpu_off(2);
|
||||
}
|
||||
/* We clear the reset bits as the processor changed state */
|
||||
clear_bit(CORE2_RST_SHIFT, ¤t_value);
|
||||
imx6_defer_clear_reset_bit(2, s, CORE2_RST_SHIFT);
|
||||
clear_bit(CORE2_RST_SHIFT, &change_mask);
|
||||
}
|
||||
if (EXTRACT(change_mask, CORE1_ENABLE)) {
|
||||
@@ -175,28 +215,28 @@ static void imx6_src_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
arm_set_cpu_on(1, s->regs[SRC_GPR3], s->regs[SRC_GPR4],
|
||||
3, false);
|
||||
} else {
|
||||
/* CORE 3 is shut down */
|
||||
/* CORE 1 is shut down */
|
||||
arm_set_cpu_off(1);
|
||||
}
|
||||
/* We clear the reset bits as the processor changed state */
|
||||
clear_bit(CORE1_RST_SHIFT, ¤t_value);
|
||||
imx6_defer_clear_reset_bit(1, s, CORE1_RST_SHIFT);
|
||||
clear_bit(CORE1_RST_SHIFT, &change_mask);
|
||||
}
|
||||
if (EXTRACT(change_mask, CORE0_RST)) {
|
||||
arm_reset_cpu(0);
|
||||
clear_bit(CORE0_RST_SHIFT, ¤t_value);
|
||||
imx6_defer_clear_reset_bit(0, s, CORE0_RST_SHIFT);
|
||||
}
|
||||
if (EXTRACT(change_mask, CORE1_RST)) {
|
||||
arm_reset_cpu(1);
|
||||
clear_bit(CORE1_RST_SHIFT, ¤t_value);
|
||||
imx6_defer_clear_reset_bit(1, s, CORE1_RST_SHIFT);
|
||||
}
|
||||
if (EXTRACT(change_mask, CORE2_RST)) {
|
||||
arm_reset_cpu(2);
|
||||
clear_bit(CORE2_RST_SHIFT, ¤t_value);
|
||||
imx6_defer_clear_reset_bit(2, s, CORE2_RST_SHIFT);
|
||||
}
|
||||
if (EXTRACT(change_mask, CORE3_RST)) {
|
||||
arm_reset_cpu(3);
|
||||
clear_bit(CORE3_RST_SHIFT, ¤t_value);
|
||||
imx6_defer_clear_reset_bit(3, s, CORE3_RST_SHIFT);
|
||||
}
|
||||
if (EXTRACT(change_mask, SW_IPU2_RST)) {
|
||||
/* We pretend the IPU2 is reset */
|
||||
|
||||
+15
-1
@@ -62,7 +62,16 @@ void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
unsigned int old_pending = env->pending_interrupts;
|
||||
unsigned int old_pending;
|
||||
bool locked = false;
|
||||
|
||||
/* We may already have the BQL if coming from the reset path */
|
||||
if (!qemu_mutex_iothread_locked()) {
|
||||
locked = true;
|
||||
qemu_mutex_lock_iothread();
|
||||
}
|
||||
|
||||
old_pending = env->pending_interrupts;
|
||||
|
||||
if (level) {
|
||||
env->pending_interrupts |= 1 << n_IRQ;
|
||||
@@ -80,9 +89,14 @@ void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
LOG_IRQ("%s: %p n_IRQ %d level %d => pending %08" PRIx32
|
||||
"req %08x\n", __func__, env, n_IRQ, level,
|
||||
env->pending_interrupts, CPU(cpu)->interrupt_request);
|
||||
|
||||
if (locked) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
}
|
||||
|
||||
/* PowerPC 6xx / 7xx internal IRQ controller */
|
||||
|
||||
@@ -1010,6 +1010,9 @@ static void emulate_spapr_hypercall(PPCVirtualHypervisor *vhyp,
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
/* The TCG path should also be holding the BQL at this point */
|
||||
g_assert(qemu_mutex_iothread_locked());
|
||||
|
||||
if (msr_pr) {
|
||||
hcall_dprintf("Hypercall made with MSR[PR]=1\n");
|
||||
env->gpr[3] = H_PRIVILEGE;
|
||||
|
||||
@@ -23,8 +23,6 @@
|
||||
/* cputlb.c */
|
||||
void tlb_protect_code(ram_addr_t ram_addr);
|
||||
void tlb_unprotect_code(ram_addr_t ram_addr);
|
||||
void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, uintptr_t start,
|
||||
uintptr_t length);
|
||||
extern int tlb_flush_count;
|
||||
|
||||
#endif
|
||||
|
||||
+120
-12
@@ -92,6 +92,27 @@ void cpu_address_space_init(CPUState *cpu, AddressSpace *as, int asidx);
|
||||
* MMU indexes.
|
||||
*/
|
||||
void tlb_flush_page(CPUState *cpu, target_ulong addr);
|
||||
/**
|
||||
* tlb_flush_page_all_cpus:
|
||||
* @cpu: src CPU of the flush
|
||||
* @addr: virtual address of page to be flushed
|
||||
*
|
||||
* Flush one page from the TLB of the specified CPU, for all
|
||||
* MMU indexes.
|
||||
*/
|
||||
void tlb_flush_page_all_cpus(CPUState *src, target_ulong addr);
|
||||
/**
|
||||
* tlb_flush_page_all_cpus_synced:
|
||||
* @cpu: src CPU of the flush
|
||||
* @addr: virtual address of page to be flushed
|
||||
*
|
||||
* Flush one page from the TLB of the specified CPU, for all MMU
|
||||
* indexes like tlb_flush_page_all_cpus except the source vCPUs work
|
||||
* is scheduled as safe work meaning all flushes will be complete once
|
||||
* the source vCPUs safe work is complete. This will depend on when
|
||||
* the guests translation ends the TB.
|
||||
*/
|
||||
void tlb_flush_page_all_cpus_synced(CPUState *src, target_ulong addr);
|
||||
/**
|
||||
* tlb_flush:
|
||||
* @cpu: CPU whose TLB should be flushed
|
||||
@@ -102,25 +123,88 @@ void tlb_flush_page(CPUState *cpu, target_ulong addr);
|
||||
* use one of the other functions for efficiency.
|
||||
*/
|
||||
void tlb_flush(CPUState *cpu);
|
||||
/**
|
||||
* tlb_flush_all_cpus:
|
||||
* @cpu: src CPU of the flush
|
||||
*/
|
||||
void tlb_flush_all_cpus(CPUState *src_cpu);
|
||||
/**
|
||||
* tlb_flush_all_cpus_synced:
|
||||
* @cpu: src CPU of the flush
|
||||
*
|
||||
* Like tlb_flush_all_cpus except this except the source vCPUs work is
|
||||
* scheduled as safe work meaning all flushes will be complete once
|
||||
* the source vCPUs safe work is complete. This will depend on when
|
||||
* the guests translation ends the TB.
|
||||
*/
|
||||
void tlb_flush_all_cpus_synced(CPUState *src_cpu);
|
||||
/**
|
||||
* tlb_flush_page_by_mmuidx:
|
||||
* @cpu: CPU whose TLB should be flushed
|
||||
* @addr: virtual address of page to be flushed
|
||||
* @...: list of MMU indexes to flush, terminated by a negative value
|
||||
* @idxmap: bitmap of MMU indexes to flush
|
||||
*
|
||||
* Flush one page from the TLB of the specified CPU, for the specified
|
||||
* MMU indexes.
|
||||
*/
|
||||
void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, ...);
|
||||
void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr,
|
||||
uint16_t idxmap);
|
||||
/**
|
||||
* tlb_flush_page_by_mmuidx_all_cpus:
|
||||
* @cpu: Originating CPU of the flush
|
||||
* @addr: virtual address of page to be flushed
|
||||
* @idxmap: bitmap of MMU indexes to flush
|
||||
*
|
||||
* Flush one page from the TLB of all CPUs, for the specified
|
||||
* MMU indexes.
|
||||
*/
|
||||
void tlb_flush_page_by_mmuidx_all_cpus(CPUState *cpu, target_ulong addr,
|
||||
uint16_t idxmap);
|
||||
/**
|
||||
* tlb_flush_page_by_mmuidx_all_cpus_synced:
|
||||
* @cpu: Originating CPU of the flush
|
||||
* @addr: virtual address of page to be flushed
|
||||
* @idxmap: bitmap of MMU indexes to flush
|
||||
*
|
||||
* Flush one page from the TLB of all CPUs, for the specified MMU
|
||||
* indexes like tlb_flush_page_by_mmuidx_all_cpus except the source
|
||||
* vCPUs work is scheduled as safe work meaning all flushes will be
|
||||
* complete once the source vCPUs safe work is complete. This will
|
||||
* depend on when the guests translation ends the TB.
|
||||
*/
|
||||
void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, target_ulong addr,
|
||||
uint16_t idxmap);
|
||||
/**
|
||||
* tlb_flush_by_mmuidx:
|
||||
* @cpu: CPU whose TLB should be flushed
|
||||
* @...: list of MMU indexes to flush, terminated by a negative value
|
||||
* @wait: If true ensure synchronisation by exiting the cpu_loop
|
||||
* @idxmap: bitmap of MMU indexes to flush
|
||||
*
|
||||
* Flush all entries from the TLB of the specified CPU, for the specified
|
||||
* MMU indexes.
|
||||
*/
|
||||
void tlb_flush_by_mmuidx(CPUState *cpu, ...);
|
||||
void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap);
|
||||
/**
|
||||
* tlb_flush_by_mmuidx_all_cpus:
|
||||
* @cpu: Originating CPU of the flush
|
||||
* @idxmap: bitmap of MMU indexes to flush
|
||||
*
|
||||
* Flush all entries from all TLBs of all CPUs, for the specified
|
||||
* MMU indexes.
|
||||
*/
|
||||
void tlb_flush_by_mmuidx_all_cpus(CPUState *cpu, uint16_t idxmap);
|
||||
/**
|
||||
* tlb_flush_by_mmuidx_all_cpus_synced:
|
||||
* @cpu: Originating CPU of the flush
|
||||
* @idxmap: bitmap of MMU indexes to flush
|
||||
*
|
||||
* Flush all entries from all TLBs of all CPUs, for the specified
|
||||
* MMU indexes like tlb_flush_by_mmuidx_all_cpus except except the source
|
||||
* vCPUs work is scheduled as safe work meaning all flushes will be
|
||||
* complete once the source vCPUs safe work is complete. This will
|
||||
* depend on when the guests translation ends the TB.
|
||||
*/
|
||||
void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap);
|
||||
/**
|
||||
* tlb_set_page_with_attrs:
|
||||
* @cpu: CPU to add this TLB entry for
|
||||
@@ -162,17 +246,45 @@ void probe_write(CPUArchState *env, target_ulong addr, int mmu_idx,
|
||||
static inline void tlb_flush_page(CPUState *cpu, target_ulong addr)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void tlb_flush_page_all_cpus(CPUState *src, target_ulong addr)
|
||||
{
|
||||
}
|
||||
static inline void tlb_flush_page_all_cpus_synced(CPUState *src,
|
||||
target_ulong addr)
|
||||
{
|
||||
}
|
||||
static inline void tlb_flush(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void tlb_flush_all_cpus(CPUState *src_cpu)
|
||||
{
|
||||
}
|
||||
static inline void tlb_flush_all_cpus_synced(CPUState *src_cpu)
|
||||
{
|
||||
}
|
||||
static inline void tlb_flush_page_by_mmuidx(CPUState *cpu,
|
||||
target_ulong addr, ...)
|
||||
target_ulong addr, uint16_t idxmap)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void tlb_flush_by_mmuidx(CPUState *cpu, ...)
|
||||
static inline void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap)
|
||||
{
|
||||
}
|
||||
static inline void tlb_flush_page_by_mmuidx_all_cpus(CPUState *cpu,
|
||||
target_ulong addr,
|
||||
uint16_t idxmap)
|
||||
{
|
||||
}
|
||||
static inline void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu,
|
||||
target_ulong addr,
|
||||
uint16_t idxmap)
|
||||
{
|
||||
}
|
||||
static inline void tlb_flush_by_mmuidx_all_cpus(CPUState *cpu, uint16_t idxmap)
|
||||
{
|
||||
}
|
||||
static inline void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu,
|
||||
uint16_t idxmap)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
@@ -404,8 +516,4 @@ bool memory_region_is_unassigned(MemoryRegion *mr);
|
||||
/* vl.c */
|
||||
extern int singlestep;
|
||||
|
||||
/* cpu-exec.c, accessed with atomic_mb_read/atomic_mb_set */
|
||||
extern CPUState *tcg_current_cpu;
|
||||
extern bool exit_request;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -329,6 +329,7 @@ struct CPUState {
|
||||
bool unplug;
|
||||
bool crash_occurred;
|
||||
bool exit_request;
|
||||
/* updates protected by BQL */
|
||||
uint32_t interrupt_request;
|
||||
int singlestep_enabled;
|
||||
int64_t icount_extra;
|
||||
@@ -401,6 +402,12 @@ struct CPUState {
|
||||
|
||||
bool hax_vcpu_dirty;
|
||||
struct hax_vcpu_state *hax_vcpu;
|
||||
|
||||
/* The pending_tlb_flush flag is set and cleared atomically to
|
||||
* avoid potential races. The aim of the flag is to avoid
|
||||
* unnecessary flushes.
|
||||
*/
|
||||
uint16_t pending_tlb_flush;
|
||||
};
|
||||
|
||||
QTAILQ_HEAD(CPUTailQ, CPUState);
|
||||
@@ -415,6 +422,15 @@ extern struct CPUTailQ cpus;
|
||||
|
||||
extern __thread CPUState *current_cpu;
|
||||
|
||||
/**
|
||||
* qemu_tcg_mttcg_enabled:
|
||||
* Check whether we are running MultiThread TCG or not.
|
||||
*
|
||||
* Returns: %true if we are in MTTCG mode %false otherwise.
|
||||
*/
|
||||
extern bool mttcg_enabled;
|
||||
#define qemu_tcg_mttcg_enabled() (mttcg_enabled)
|
||||
|
||||
/**
|
||||
* cpu_paging_enabled:
|
||||
* @cpu: The CPU whose state is to be inspected.
|
||||
|
||||
@@ -36,4 +36,6 @@ extern int smp_threads;
|
||||
|
||||
void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg);
|
||||
|
||||
void qemu_tcg_configure(QemuOpts *opts, Error **errp);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -917,6 +917,8 @@ void memory_region_transaction_commit(void)
|
||||
AddressSpace *as;
|
||||
|
||||
assert(memory_region_transaction_depth);
|
||||
assert(qemu_mutex_iothread_locked());
|
||||
|
||||
--memory_region_transaction_depth;
|
||||
if (!memory_region_transaction_depth) {
|
||||
if (memory_region_update_pending) {
|
||||
|
||||
@@ -95,6 +95,26 @@ STEXI
|
||||
Select CPU model (@code{-cpu help} for list and additional feature selection)
|
||||
ETEXI
|
||||
|
||||
DEF("accel", HAS_ARG, QEMU_OPTION_accel,
|
||||
"-accel [accel=]accelerator[,thread=single|multi]\n"
|
||||
" select accelerator ('-accel help for list')\n"
|
||||
" thread=single|multi (enable multi-threaded TCG)", QEMU_ARCH_ALL)
|
||||
STEXI
|
||||
@item -accel @var{name}[,prop=@var{value}[,...]]
|
||||
@findex -accel
|
||||
This is used to enable an accelerator. Depending on the target architecture,
|
||||
kvm, xen, or tcg can be available. By default, tcg is used. If there is more
|
||||
than one accelerator specified, the next one is used if the previous one fails
|
||||
to initialize.
|
||||
@table @option
|
||||
@item thread=single|multi
|
||||
Controls number of TCG threads. When the TCG is multi-threaded there will be one
|
||||
thread per vCPU therefor taking advantage of additional host cores. The default
|
||||
is to enable multi-threading where both the back-end and front-ends support it and
|
||||
no incompatible TCG features have been enabled (e.g. icount/replay).
|
||||
@end table
|
||||
ETEXI
|
||||
|
||||
DEF("smp", HAS_ARG, QEMU_OPTION_smp,
|
||||
"-smp [cpus=]n[,maxcpus=cpus][,cores=cores][,threads=threads][,sockets=sockets]\n"
|
||||
" set the number of CPUs to 'n' [default=1]\n"
|
||||
|
||||
@@ -113,9 +113,19 @@ static void cpu_common_get_memory_mapping(CPUState *cpu,
|
||||
error_setg(errp, "Obtaining memory mappings is unsupported on this CPU.");
|
||||
}
|
||||
|
||||
/* Resetting the IRQ comes from across the code base so we take the
|
||||
* BQL here if we need to. cpu_interrupt assumes it is held.*/
|
||||
void cpu_reset_interrupt(CPUState *cpu, int mask)
|
||||
{
|
||||
bool need_lock = !qemu_mutex_iothread_locked();
|
||||
|
||||
if (need_lock) {
|
||||
qemu_mutex_lock_iothread();
|
||||
}
|
||||
cpu->interrupt_request &= ~mask;
|
||||
if (need_lock) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_exit(CPUState *cpu)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user