diff options
-rw-r--r-- | exec.c | 65 | ||||
-rw-r--r-- | include/exec/memory-internal.h | 62 |
2 files changed, 106 insertions, 21 deletions
@@ -2354,18 +2354,55 @@ ram_addr_t qemu_ram_addr_from_host(void *ptr) return block->offset + offset; } -/* Called within RCU critical section. */ -static void notdirty_mem_write(void *opaque, hwaddr ram_addr, - uint64_t val, unsigned size) -{ - bool locked = false; +/* Called within RCU critical section. */ +void memory_notdirty_write_prepare(NotDirtyInfo *ndi, + CPUState *cpu, + vaddr mem_vaddr, + ram_addr_t ram_addr, + unsigned size) +{ + ndi->cpu = cpu; + ndi->ram_addr = ram_addr; + ndi->mem_vaddr = mem_vaddr; + ndi->size = size; + ndi->locked = false; assert(tcg_enabled()); if (!cpu_physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) { - locked = true; + ndi->locked = true; tb_lock(); tb_invalidate_phys_page_fast(ram_addr, size); } +} + +/* Called within RCU critical section. */ +void memory_notdirty_write_complete(NotDirtyInfo *ndi) +{ + if (ndi->locked) { + tb_unlock(); + } + + /* Set both VGA and migration bits for simplicity and to remove + * the notdirty callback faster. + */ + cpu_physical_memory_set_dirty_range(ndi->ram_addr, ndi->size, + DIRTY_CLIENTS_NOCODE); + /* we remove the notdirty callback only if the code has been + flushed */ + if (!cpu_physical_memory_is_clean(ndi->ram_addr)) { + tlb_set_dirty(ndi->cpu, ndi->mem_vaddr); + } +} + +/* Called within RCU critical section. */ +static void notdirty_mem_write(void *opaque, hwaddr ram_addr, + uint64_t val, unsigned size) +{ + NotDirtyInfo ndi; + + memory_notdirty_write_prepare(&ndi, current_cpu, current_cpu->mem_io_vaddr, + ram_addr, size); + switch (size) { case 1: stb_p(qemu_map_ram_ptr(NULL, ram_addr), val); @@ -2382,21 +2419,7 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr, default: abort(); } - - if (locked) { - tb_unlock(); - } - - /* Set both VGA and migration bits for simplicity and to remove - * the notdirty callback faster. - */ - cpu_physical_memory_set_dirty_range(ram_addr, size, - DIRTY_CLIENTS_NOCODE); - /* we remove the notdirty callback only if the code has been - flushed */ - if (!cpu_physical_memory_is_clean(ram_addr)) { - tlb_set_dirty(current_cpu, current_cpu->mem_io_vaddr); - } + memory_notdirty_write_complete(&ndi); } static bool notdirty_mem_accepts(void *opaque, hwaddr addr, diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 647e9bd5c4..98d82964cc 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -39,5 +39,67 @@ void mtree_print_dispatch(fprintf_function mon, void *f, struct AddressSpaceDispatch *d, MemoryRegion *root); +/* Opaque struct for passing info from memory_notdirty_write_prepare() + * to memory_notdirty_write_complete(). Callers should treat all fields + * as private, with the exception of @active. + * + * @active is a field which is not touched by either the prepare or + * complete functions, but which the caller can use if it wishes to + * track whether it has called prepare for this struct and so needs + * to later call the complete function. + */ +typedef struct { + CPUState *cpu; + ram_addr_t ram_addr; + vaddr mem_vaddr; + unsigned size; + bool locked; + bool active; +} NotDirtyInfo; + +/** + * memory_notdirty_write_prepare: call before writing to non-dirty memory + * @ndi: pointer to opaque NotDirtyInfo struct + * @cpu: CPU doing the write + * @mem_vaddr: virtual address of write + * @ram_addr: the ram address of the write + * @size: size of write in bytes + * + * Any code which writes to the host memory corresponding to + * guest RAM which has been marked as NOTDIRTY must wrap those + * writes in calls to memory_notdirty_write_prepare() and + * memory_notdirty_write_complete(): + * + * NotDirtyInfo ndi; + * memory_notdirty_write_prepare(&ndi, ....); + * ... perform write here ... + * memory_notdirty_write_complete(&ndi); + * + * These calls will ensure that we flush any TCG translated code for + * the memory being written, update the dirty bits and (if possible) + * remove the slowpath callback for writing to the memory. + * + * This must only be called if we are using TCG; it will assert otherwise. + * + * We may take a lock in the prepare call, so callers must ensure that + * they don't exit (via longjump or otherwise) without calling complete. + * + * This call must only be made inside an RCU critical section. + * (Note that while we're executing a TCG TB we're always in an + * RCU critical section, which is likely to be the case for callers + * of these functions.) + */ +void memory_notdirty_write_prepare(NotDirtyInfo *ndi, + CPUState *cpu, + vaddr mem_vaddr, + ram_addr_t ram_addr, + unsigned size); +/** + * memory_notdirty_write_complete: finish write to non-dirty memory + * @ndi: pointer to the opaque NotDirtyInfo struct which was initialized + * by memory_not_dirty_write_prepare(). + */ +void memory_notdirty_write_complete(NotDirtyInfo *ndi); + #endif #endif |