mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-02 23:33:54 -06:00
memory: fix race between TCG and accesses to dirty bitmap
There is a race between TCG and accesses to the dirty log: vCPU thread reader thread ----------------------- ----------------------- TLB check -> slow path notdirty_mem_write write to RAM set dirty flag clear dirty flag TLB check -> fast path read memory write to RAM Fortunately, in order to fix it, no change is required to the vCPU thread. However, the reader thread must delay the read after the vCPU thread has finished the write. This can be approximated conservatively by run_on_cpu, which waits for the end of the current translation block. A similar technique is used by KVM, which has to do a synchronous TLB flush after doing a test-and-clear of the dirty-page flags. Reported-by: Dr. David Alan Gilbert <dgilbert@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
1e8a98b538
commit
9458a9a1df
4 changed files with 53 additions and 1 deletions
31
exec.c
31
exec.c
|
@ -197,6 +197,7 @@ typedef struct subpage_t {
|
|||
|
||||
static void io_mem_init(void);
|
||||
static void memory_map_init(void);
|
||||
static void tcg_log_global_after_sync(MemoryListener *listener);
|
||||
static void tcg_commit(MemoryListener *listener);
|
||||
|
||||
static MemoryRegion io_mem_watch;
|
||||
|
@ -905,6 +906,7 @@ void cpu_address_space_init(CPUState *cpu, int asidx,
|
|||
newas->cpu = cpu;
|
||||
newas->as = as;
|
||||
if (tcg_enabled()) {
|
||||
newas->tcg_as_listener.log_global_after_sync = tcg_log_global_after_sync;
|
||||
newas->tcg_as_listener.commit = tcg_commit;
|
||||
memory_listener_register(&newas->tcg_as_listener, as);
|
||||
}
|
||||
|
@ -3142,6 +3144,35 @@ void address_space_dispatch_free(AddressSpaceDispatch *d)
|
|||
g_free(d);
|
||||
}
|
||||
|
||||
static void do_nothing(CPUState *cpu, run_on_cpu_data d)
|
||||
{
|
||||
}
|
||||
|
||||
static void tcg_log_global_after_sync(MemoryListener *listener)
|
||||
{
|
||||
CPUAddressSpace *cpuas;
|
||||
|
||||
/* Wait for the CPU to end the current TB. This avoids the following
|
||||
* incorrect race:
|
||||
*
|
||||
* vCPU migration
|
||||
* ---------------------- -------------------------
|
||||
* TLB check -> slow path
|
||||
* notdirty_mem_write
|
||||
* write to RAM
|
||||
* mark dirty
|
||||
* clear dirty flag
|
||||
* TLB check -> fast path
|
||||
* read memory
|
||||
* write to RAM
|
||||
*
|
||||
* by pushing the migration thread's memory read after the vCPU thread has
|
||||
* written the memory.
|
||||
*/
|
||||
cpuas = container_of(listener, CPUAddressSpace, tcg_as_listener);
|
||||
run_on_cpu(cpuas->cpu, do_nothing, RUN_ON_CPU_NULL);
|
||||
}
|
||||
|
||||
static void tcg_commit(MemoryListener *listener)
|
||||
{
|
||||
CPUAddressSpace *cpuas;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue