From dbddac6da01a13c9d5d162994a0a265173acecab Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 23 Mar 2015 10:31:53 +0100 Subject: memory: the only dirty memory flag for users is DIRTY_MEMORY_VGA DIRTY_MEMORY_MIGRATION is triggered by memory_global_dirty_log_start and memory_global_dirty_log_stop, so it cannot be used with memory_region_set_log. Specify this in the documentation and assert it. Reviewed-by: Fam Zheng Signed-off-by: Paolo Bonzini --- include/exec/memory.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include/exec') diff --git a/include/exec/memory.h b/include/exec/memory.h index b61c84f62a..7b0929d54f 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -647,8 +647,7 @@ void memory_region_ram_resize(MemoryRegion *mr, ram_addr_t newsize, * * @mr: the memory region being updated. * @log: whether dirty logging is to be enabled or disabled. - * @client: the user of the logging information; %DIRTY_MEMORY_MIGRATION or - * %DIRTY_MEMORY_VGA. + * @client: the user of the logging information; %DIRTY_MEMORY_VGA only. */ void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client); -- cgit v1.2.3-55-g7522 From 2d1a35bef0ed96b3f23535e459c552414ccdbafd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 23 Mar 2015 10:50:57 +0100 Subject: memory: differentiate memory_region_is_logging and memory_region_get_dirty_log_mask For now memory regions only track DIRTY_MEMORY_VGA individually, but this will change soon. To support this, split memory_region_is_logging in two functions: one that returns a given bit from dirty_log_mask, and one that returns the entire mask. memory_region_is_logging gets an extra parameter so that the compiler flags misuse. While VGA-specific users (including the Xen listener!) will want to keep checking that bit, KVM and vhost check for "any bit except migration" (because migration is handled via the global start/stop listener callbacks). Reviewed-by: Fam Zheng Signed-off-by: Paolo Bonzini --- hw/display/vmware_vga.c | 2 +- hw/virtio/dataplane/vring.c | 2 +- hw/virtio/vhost.c | 3 ++- include/exec/memory.h | 16 ++++++++++++++-- kvm-all.c | 3 ++- memory.c | 7 ++++++- xen-hvm.c | 2 +- 7 files changed, 27 insertions(+), 8 deletions(-) (limited to 'include/exec') diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index c17ddd1fcd..7f397d3c2e 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1124,7 +1124,7 @@ static void vmsvga_update_display(void *opaque) * Is it more efficient to look at vram VGA-dirty bits or wait * for the driver to issue SVGA_CMD_UPDATE? */ - if (memory_region_is_logging(&s->vga.vram)) { + if (memory_region_is_logging(&s->vga.vram, DIRTY_MEMORY_VGA)) { vga_sync_dirty_bitmap(&s->vga); dirty = memory_region_get_dirty(&s->vga.vram, 0, surface_stride(surface) * surface_height(surface), diff --git a/hw/virtio/dataplane/vring.c b/hw/virtio/dataplane/vring.c index 5c7b8c20fa..e37873384f 100644 --- a/hw/virtio/dataplane/vring.c +++ b/hw/virtio/dataplane/vring.c @@ -42,7 +42,7 @@ static void *vring_map(MemoryRegion **mr, hwaddr phys, hwaddr len, } /* Ignore regions with dirty logging, we cannot mark them dirty */ - if (memory_region_is_logging(section.mr)) { + if (memory_region_get_dirty_log_mask(section.mr)) { goto out; } diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 01f1e0490f..53d23d281d 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -416,7 +416,8 @@ static void vhost_set_memory(MemoryListener *listener, memory_listener); hwaddr start_addr = section->offset_within_address_space; ram_addr_t size = int128_get64(section->size); - bool log_dirty = memory_region_is_logging(section->mr); + bool log_dirty = + memory_region_get_dirty_log_mask(section->mr) & ~(1 << DIRTY_MEMORY_MIGRATION); int s = offsetof(struct vhost_memory, regions) + (dev->mem->nregions + 1) * sizeof dev->mem->regions[0]; void *ram; diff --git a/include/exec/memory.h b/include/exec/memory.h index 7b0929d54f..156d506063 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -591,11 +591,23 @@ const char *memory_region_name(const MemoryRegion *mr); /** * memory_region_is_logging: return whether a memory region is logging writes * - * Returns %true if the memory region is logging writes + * Returns %true if the memory region is logging writes for the given client * * @mr: the memory region being queried + * @client: the client being queried */ -bool memory_region_is_logging(MemoryRegion *mr); +bool memory_region_is_logging(MemoryRegion *mr, uint8_t client); + +/** + * memory_region_get_dirty_log_mask: return the clients for which a + * memory region is logging writes. + * + * Returns a bitmap of clients, which right now will be either 0 or + * (1 << DIRTY_MEMORY_VGA). + * + * @mr: the memory region being queried + */ +uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr); /** * memory_region_is_rom: check whether a memory region is ROM diff --git a/kvm-all.c b/kvm-all.c index 44a34a52d8..d5416bbd74 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -663,7 +663,8 @@ static void kvm_set_phys_mem(MemoryRegionSection *section, bool add) KVMSlot *mem, old; int err; MemoryRegion *mr = section->mr; - bool log_dirty = memory_region_is_logging(mr); + bool log_dirty = + memory_region_get_dirty_log_mask(mr) & ~(1 << DIRTY_MEMORY_MIGRATION); bool writeable = !mr->readonly && !mr->rom_device; bool readonly_flag = mr->readonly || memory_region_is_romd(mr); hwaddr start_addr = section->offset_within_address_space; diff --git a/memory.c b/memory.c index a8f8599079..72e33e853b 100644 --- a/memory.c +++ b/memory.c @@ -1389,11 +1389,16 @@ bool memory_region_is_skip_dump(MemoryRegion *mr) return mr->skip_dump; } -bool memory_region_is_logging(MemoryRegion *mr) +uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr) { return mr->dirty_log_mask; } +bool memory_region_is_logging(MemoryRegion *mr, uint8_t client) +{ + return memory_region_get_dirty_log_mask(mr) & (1 << client); +} + bool memory_region_is_rom(MemoryRegion *mr) { return mr->ram && mr->readonly; diff --git a/xen-hvm.c b/xen-hvm.c index 315864ca70..338ab291f8 100644 --- a/xen-hvm.c +++ b/xen-hvm.c @@ -488,7 +488,7 @@ static void xen_set_memory(struct MemoryListener *listener, XenIOState *state = container_of(listener, XenIOState, memory_listener); hwaddr start_addr = section->offset_within_address_space; ram_addr_t size = int128_get64(section->size); - bool log_dirty = memory_region_is_logging(section->mr); + bool log_dirty = memory_region_is_logging(section->mr, DIRTY_MEMORY_VGA); hvmmem_type_t mem_type; if (section->mr == &ram_memory) { -- cgit v1.2.3-55-g7522 From b2dfd71c4843a762f2befe702adb249cf55baf66 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 25 Apr 2015 14:38:30 +0200 Subject: memory: prepare for multiple bits in the dirty log mask When the dirty log mask will also cover other bits than DIRTY_MEMORY_VGA, some listeners may be interested in the overall zero/non-zero value of the dirty log mask; others may be interested in the value of single bits. For this reason, always call log_start/log_stop if bits have respectively appeared or disappeared, and pass the old and new values of the dirty log mask so that listeners can distinguish the kinds of change. For example, KVM checks if dirty logging used to be completely disabled (in log_start) or is now completely disabled (in log_stop). On the other hand, Xen has to check manually if DIRTY_MEMORY_VGA changed, since that is the only bit it cares about. Reviewed-by: Fam Zheng Signed-off-by: Paolo Bonzini --- hw/virtio/vhost.c | 6 ++++-- include/exec/memory.h | 6 ++++-- kvm-all.c | 14 ++++++++++++-- memory.c | 17 +++++++++++------ xen-hvm.c | 20 +++++++++++++------- 5 files changed, 44 insertions(+), 19 deletions(-) (limited to 'include/exec') diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 53d23d281d..a7fe3c5104 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -676,13 +676,15 @@ static void vhost_log_global_stop(MemoryListener *listener) } static void vhost_log_start(MemoryListener *listener, - MemoryRegionSection *section) + MemoryRegionSection *section, + int old, int new) { /* FIXME: implement */ } static void vhost_log_stop(MemoryListener *listener, - MemoryRegionSection *section) + MemoryRegionSection *section, + int old, int new) { /* FIXME: implement */ } diff --git a/include/exec/memory.h b/include/exec/memory.h index 156d506063..55dc11d55c 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -206,8 +206,10 @@ struct MemoryListener { void (*region_add)(MemoryListener *listener, MemoryRegionSection *section); void (*region_del)(MemoryListener *listener, MemoryRegionSection *section); void (*region_nop)(MemoryListener *listener, MemoryRegionSection *section); - void (*log_start)(MemoryListener *listener, MemoryRegionSection *section); - void (*log_stop)(MemoryListener *listener, MemoryRegionSection *section); + void (*log_start)(MemoryListener *listener, MemoryRegionSection *section, + int old, int new); + void (*log_stop)(MemoryListener *listener, MemoryRegionSection *section, + int old, int new); void (*log_sync)(MemoryListener *listener, MemoryRegionSection *section); void (*log_global_start)(MemoryListener *listener); void (*log_global_stop)(MemoryListener *listener); diff --git a/kvm-all.c b/kvm-all.c index d5416bbd74..c713b22f8c 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -344,10 +344,15 @@ static int kvm_dirty_pages_log_change(hwaddr phys_addr, } static void kvm_log_start(MemoryListener *listener, - MemoryRegionSection *section) + MemoryRegionSection *section, + int old, int new) { int r; + if (old != 0) { + return; + } + r = kvm_dirty_pages_log_change(section->offset_within_address_space, int128_get64(section->size), true); if (r < 0) { @@ -356,10 +361,15 @@ static void kvm_log_start(MemoryListener *listener, } static void kvm_log_stop(MemoryListener *listener, - MemoryRegionSection *section) + MemoryRegionSection *section, + int old, int new) { int r; + if (new != 0) { + return; + } + r = kvm_dirty_pages_log_change(section->offset_within_address_space, int128_get64(section->size), false); if (r < 0) { diff --git a/memory.c b/memory.c index 72e33e853b..3e3d5b8ad6 100644 --- a/memory.c +++ b/memory.c @@ -152,7 +152,7 @@ static bool memory_listener_match(MemoryListener *listener, } while (0) /* No need to ref/unref .mr, the FlatRange keeps it alive. */ -#define MEMORY_LISTENER_UPDATE_REGION(fr, as, dir, callback) \ +#define MEMORY_LISTENER_UPDATE_REGION(fr, as, dir, callback, _args...) \ MEMORY_LISTENER_CALL(callback, dir, (&(MemoryRegionSection) { \ .mr = (fr)->mr, \ .address_space = (as), \ @@ -160,7 +160,7 @@ static bool memory_listener_match(MemoryListener *listener, .size = (fr)->addr.size, \ .offset_within_address_space = int128_get64((fr)->addr.start), \ .readonly = (fr)->readonly, \ - })) + }), ##_args) struct CoalescedMemoryRange { AddrRange addr; @@ -774,10 +774,15 @@ static void address_space_update_topology_pass(AddressSpace *as, if (adding) { MEMORY_LISTENER_UPDATE_REGION(frnew, as, Forward, region_nop); - if (frold->dirty_log_mask && !frnew->dirty_log_mask) { - MEMORY_LISTENER_UPDATE_REGION(frnew, as, Reverse, log_stop); - } else if (frnew->dirty_log_mask && !frold->dirty_log_mask) { - MEMORY_LISTENER_UPDATE_REGION(frnew, as, Forward, log_start); + if (frnew->dirty_log_mask & ~frold->dirty_log_mask) { + MEMORY_LISTENER_UPDATE_REGION(frnew, as, Forward, log_start, + frold->dirty_log_mask, + frnew->dirty_log_mask); + } + if (frold->dirty_log_mask & ~frnew->dirty_log_mask) { + MEMORY_LISTENER_UPDATE_REGION(frnew, as, Reverse, log_stop, + frold->dirty_log_mask, + frnew->dirty_log_mask); } } diff --git a/xen-hvm.c b/xen-hvm.c index 338ab291f8..42356b836a 100644 --- a/xen-hvm.c +++ b/xen-hvm.c @@ -646,21 +646,27 @@ static void xen_sync_dirty_bitmap(XenIOState *state, } static void xen_log_start(MemoryListener *listener, - MemoryRegionSection *section) + MemoryRegionSection *section, + int old, int new) { XenIOState *state = container_of(listener, XenIOState, memory_listener); - xen_sync_dirty_bitmap(state, section->offset_within_address_space, - int128_get64(section->size)); + if (new & ~old & (1 << DIRTY_MEMORY_VGA)) { + xen_sync_dirty_bitmap(state, section->offset_within_address_space, + int128_get64(section->size)); + } } -static void xen_log_stop(MemoryListener *listener, MemoryRegionSection *section) +static void xen_log_stop(MemoryListener *listener, MemoryRegionSection *section, + int old, int new) { XenIOState *state = container_of(listener, XenIOState, memory_listener); - state->log_for_dirtybit = NULL; - /* Disable dirty bit tracking */ - xc_hvm_track_dirty_vram(xen_xc, xen_domid, 0, 0, NULL); + if (old & ~new & (1 << DIRTY_MEMORY_VGA)) { + state->log_for_dirtybit = NULL; + /* Disable dirty bit tracking */ + xc_hvm_track_dirty_vram(xen_xc, xen_domid, 0, 0, NULL); + } } static void xen_log_sync(MemoryListener *listener, MemoryRegionSection *section) -- cgit v1.2.3-55-g7522 From 677e7805cf95f3b2bca8baf0888d1ebed7f0c606 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 23 Mar 2015 10:53:21 +0100 Subject: memory: track DIRTY_MEMORY_CODE in mr->dirty_log_mask DIRTY_MEMORY_CODE is only needed for TCG. By adding it directly to mr->dirty_log_mask, we avoid testing for TCG everywhere a region is checked for the enabled/disabled state of dirty logging. Reviewed-by: Fam Zheng Signed-off-by: Paolo Bonzini --- include/exec/memory.h | 4 ++-- memory.c | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include/exec') diff --git a/include/exec/memory.h b/include/exec/memory.h index 55dc11d55c..8ae004eb06 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -604,8 +604,8 @@ bool memory_region_is_logging(MemoryRegion *mr, uint8_t client); * memory_region_get_dirty_log_mask: return the clients for which a * memory region is logging writes. * - * Returns a bitmap of clients, which right now will be either 0 or - * (1 << DIRTY_MEMORY_VGA). + * Returns a bitmap of clients, in which the DIRTY_MEMORY_* constants + * are the bit indices. * * @mr: the memory region being queried */ diff --git a/memory.c b/memory.c index 3e3d5b8ad6..47cc181f66 100644 --- a/memory.c +++ b/memory.c @@ -1212,6 +1212,7 @@ void memory_region_init_ram(MemoryRegion *mr, mr->terminates = true; mr->destructor = memory_region_destructor_ram; mr->ram_addr = qemu_ram_alloc(size, mr, errp); + mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; } void memory_region_init_resizeable_ram(MemoryRegion *mr, @@ -1229,6 +1230,7 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr, mr->terminates = true; mr->destructor = memory_region_destructor_ram; mr->ram_addr = qemu_ram_alloc_resizeable(size, max_size, resized, mr, errp); + mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; } #ifdef __linux__ @@ -1245,6 +1247,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr, mr->terminates = true; mr->destructor = memory_region_destructor_ram; mr->ram_addr = qemu_ram_alloc_from_file(size, mr, share, path, errp); + mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; } #endif @@ -1258,6 +1261,7 @@ void memory_region_init_ram_ptr(MemoryRegion *mr, mr->ram = true; mr->terminates = true; mr->destructor = memory_region_destructor_ram_from_ptr; + mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; /* qemu_ram_alloc_from_ptr cannot fail with ptr != NULL. */ assert(ptr != NULL); -- cgit v1.2.3-55-g7522 From 49dfcec40349245ad365964468b67e132c3cedc7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 23 Mar 2015 11:35:19 +0100 Subject: ram_addr: tweaks to xen_modified_memory Invoke xen_modified_memory from cpu_physical_memory_set_dirty_range_nocode; it is akin to DIRTY_MEMORY_MIGRATION, so set it together with that bitmap. The remaining call from invalidate_and_set_dirty's "else" branch will go away soon. Second, fix the second argument to the function in the cpu_physical_memory_set_dirty_lebitmap call site. That function is only used by KVM, but it is better to be clean anyway. Acked-by: Stefano Stabellini Reviewed-by: Fam Zheng Signed-off-by: Paolo Bonzini --- exec.c | 3 ++- include/exec/ram_addr.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include/exec') diff --git a/exec.c b/exec.c index fc8d05d50a..6f6dbc8e60 100644 --- a/exec.c +++ b/exec.c @@ -2281,8 +2281,9 @@ static void invalidate_and_set_dirty(hwaddr addr, if (cpu_physical_memory_range_includes_clean(addr, length)) { tb_invalidate_phys_range(addr, addr + length, 0); cpu_physical_memory_set_dirty_range_nocode(addr, length); + } else { + xen_modified_memory(addr, length); } - xen_modified_memory(addr, length); } static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr) diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index ff558a4734..7f6e928a7f 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -112,6 +112,7 @@ static inline void cpu_physical_memory_set_dirty_range_nocode(ram_addr_t start, page = start >> TARGET_PAGE_BITS; bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION], page, end - page); bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_VGA], page, end - page); + xen_modified_memory(start, length); } static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, @@ -155,7 +156,7 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, ram_list.dirty_memory[DIRTY_MEMORY_CODE][page + k] |= temp; } } - xen_modified_memory(start, pages); + xen_modified_memory(start, pages << TARGET_PAGE_BITS); } else { /* * bitmap-traveling is faster than memory-traveling (for addr...) -- cgit v1.2.3-55-g7522 From 1652b974766401743879d78f796f44b8929b0787 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 22 Apr 2015 14:15:48 +0200 Subject: exec: move functions to translate-all.h Remove them from the sundry exec-all.h header, since they are only used by the TCG runtime in exec.c and user-exec.c. Reviewed-by: Fam Zheng Signed-off-by: Paolo Bonzini --- include/exec/exec-all.h | 6 +----- linux-user/mmap.c | 1 + translate-all.h | 8 ++++++++ user-exec.c | 1 + 4 files changed, 11 insertions(+), 5 deletions(-) (limited to 'include/exec') diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index b58cd47ced..2f7a4f1700 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -90,11 +90,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, int cflags); void cpu_exec_init(CPUArchState *env); void QEMU_NORETURN cpu_loop_exit(CPUState *cpu); -int page_unprotect(target_ulong address, uintptr_t pc, void *puc); -void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, - int is_cpu_write_access); -void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end, - int is_cpu_write_access); + #if !defined(CONFIG_USER_ONLY) bool qemu_in_vcpu_thread(void); void cpu_reload_memory_map(CPUState *cpu); diff --git a/linux-user/mmap.c b/linux-user/mmap.c index a249f0ceb6..959ff4d003 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -30,6 +30,7 @@ #include "qemu.h" #include "qemu-common.h" +#include "translate-all.h" //#define DEBUG_MMAP diff --git a/translate-all.h b/translate-all.h index b6a07bd5d3..f1a40a53c2 100644 --- a/translate-all.h +++ b/translate-all.h @@ -21,6 +21,14 @@ /* translate-all.c */ void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len); +void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, + int is_cpu_write_access); +void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end, + int is_cpu_write_access); void tb_check_watchpoint(CPUState *cpu); +#ifdef CONFIG_USER_ONLY +int page_unprotect(target_ulong address, uintptr_t pc, void *puc); +#endif + #endif /* TRANSLATE_ALL_H */ diff --git a/user-exec.c b/user-exec.c index 8f57e8acb8..ed9a07f159 100644 --- a/user-exec.c +++ b/user-exec.c @@ -22,6 +22,7 @@ #include "tcg.h" #include "qemu/bitops.h" #include "exec/cpu_ldst.h" +#include "translate-all.h" #undef EAX #undef ECX -- cgit v1.2.3-55-g7522 From 9564f52da7eb061326956ed9a468935e3352512d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 22 Apr 2015 14:24:54 +0200 Subject: cputlb: remove useless arguments to tlb_unprotect_code_phys, rename These days modification of the TLB is done in notdirty_mem_write, so the virtual address and env pointer as unnecessary. The new name of the function, tlb_unprotect_code, is consistent with tlb_protect_code. Reviewed-by: Fam Zheng Signed-off-by: Paolo Bonzini --- cputlb.c | 3 +-- include/exec/cputlb.h | 3 +-- translate-all.c | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) (limited to 'include/exec') diff --git a/cputlb.c b/cputlb.c index 7606548200..dd1203b938 100644 --- a/cputlb.c +++ b/cputlb.c @@ -131,8 +131,7 @@ void tlb_protect_code(ram_addr_t ram_addr) /* update the TLB so that writes in physical page 'phys_addr' are no longer tested for self modifying code */ -void tlb_unprotect_code_phys(CPUState *cpu, ram_addr_t ram_addr, - target_ulong vaddr) +void tlb_unprotect_code(ram_addr_t ram_addr) { cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE); } diff --git a/include/exec/cputlb.h b/include/exec/cputlb.h index e0da9d7ad3..360815e1b4 100644 --- a/include/exec/cputlb.h +++ b/include/exec/cputlb.h @@ -22,8 +22,7 @@ #if !defined(CONFIG_USER_ONLY) /* cputlb.c */ void tlb_protect_code(ram_addr_t ram_addr); -void tlb_unprotect_code_phys(CPUState *cpu, ram_addr_t ram_addr, - target_ulong vaddr); +void tlb_unprotect_code(ram_addr_t ram_addr); void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, uintptr_t start, uintptr_t length); void cpu_tlb_reset_dirty_all(ram_addr_t start1, ram_addr_t length); diff --git a/translate-all.c b/translate-all.c index d118c6c2af..0e2ad8ac64 100644 --- a/translate-all.c +++ b/translate-all.c @@ -1158,7 +1158,7 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, if (!p->first_tb) { invalidate_page_bitmap(p); if (is_cpu_write_access) { - tlb_unprotect_code_phys(cpu, start, cpu->mem_io_vaddr); + tlb_unprotect_code(start); } } #endif -- cgit v1.2.3-55-g7522 From 58d2707e8713ef17b89b8b4c9ce586c76655a385 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 23 Mar 2015 11:56:01 +0100 Subject: exec: pass client mask to cpu_physical_memory_set_dirty_range This cuts in half the cost of bitmap operations (which will become more expensive when made atomic) during migration on non-VRAM regions. Reviewed-by: Fam Zheng Signed-off-by: Paolo Bonzini --- exec.c | 20 +++++++++++--------- include/exec/ram_addr.h | 33 ++++++++++++++++----------------- memory.c | 3 ++- 3 files changed, 29 insertions(+), 27 deletions(-) (limited to 'include/exec') diff --git a/exec.c b/exec.c index 650cfa8cb8..fe137bd725 100644 --- a/exec.c +++ b/exec.c @@ -1351,7 +1351,8 @@ int qemu_ram_resize(ram_addr_t base, ram_addr_t newsize, Error **errp) cpu_physical_memory_clear_dirty_range(block->offset, block->used_length); block->used_length = newsize; - cpu_physical_memory_set_dirty_range(block->offset, block->used_length); + cpu_physical_memory_set_dirty_range(block->offset, block->used_length, + DIRTY_CLIENTS_ALL); memory_region_set_size(block->mr, newsize); if (block->resized) { block->resized(block->idstr, newsize, block->host); @@ -1425,7 +1426,8 @@ static ram_addr_t ram_block_add(RAMBlock *new_block, Error **errp) } } cpu_physical_memory_set_dirty_range(new_block->offset, - new_block->used_length); + new_block->used_length, + DIRTY_CLIENTS_ALL); if (new_block->host) { qemu_ram_setup_dump(new_block->host, new_block->max_length); @@ -1813,7 +1815,11 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr, default: abort(); } - cpu_physical_memory_set_dirty_range_nocode(ram_addr, size); + /* 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)) { @@ -2259,9 +2265,7 @@ static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr, tb_invalidate_phys_range(addr, addr + length); dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE); } - if (dirty_log_mask) { - cpu_physical_memory_set_dirty_range_nocode(addr, length); - } + cpu_physical_memory_set_dirty_range(addr, length, dirty_log_mask); } else { xen_modified_memory(addr, length); } @@ -3014,9 +3018,7 @@ void address_space_stl_notdirty(AddressSpace *as, hwaddr addr, uint32_t val, dirty_log_mask = memory_region_get_dirty_log_mask(mr); dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE); - if (dirty_log_mask) { - cpu_physical_memory_set_dirty_range_nocode(addr1, 4); - } + cpu_physical_memory_set_dirty_range(addr1, 4, dirty_log_mask); r = MEMTX_OK; } if (result) { diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 7f6e928a7f..5bbc7bb41d 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -41,6 +41,9 @@ void qemu_ram_free_from_ptr(ram_addr_t addr); int qemu_ram_resize(ram_addr_t base, ram_addr_t newsize, Error **errp); +#define DIRTY_CLIENTS_ALL ((1 << DIRTY_MEMORY_NUM) - 1) +#define DIRTY_CLIENTS_NOCODE (DIRTY_CLIENTS_ALL & ~(1 << DIRTY_MEMORY_CODE)) + static inline bool cpu_physical_memory_get_dirty(ram_addr_t start, ram_addr_t length, unsigned client) @@ -103,28 +106,23 @@ static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, set_bit(addr >> TARGET_PAGE_BITS, ram_list.dirty_memory[client]); } -static inline void cpu_physical_memory_set_dirty_range_nocode(ram_addr_t start, - ram_addr_t length) -{ - unsigned long end, page; - - end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; - page = start >> TARGET_PAGE_BITS; - bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION], page, end - page); - bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_VGA], page, end - page); - xen_modified_memory(start, length); -} - static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, - ram_addr_t length) + ram_addr_t length, + uint8_t mask) { unsigned long end, page; end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; page = start >> TARGET_PAGE_BITS; - bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION], page, end - page); - bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_VGA], page, end - page); - bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_CODE], page, end - page); + if (likely(mask & (1 << DIRTY_MEMORY_MIGRATION))) { + bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION], page, end - page); + } + if (unlikely(mask & (1 << DIRTY_MEMORY_VGA))) { + bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_VGA], page, end - page); + } + if (unlikely(mask & (1 << DIRTY_MEMORY_CODE))) { + bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_CODE], page, end - page); + } xen_modified_memory(start, length); } @@ -172,7 +170,8 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, addr = page_number * TARGET_PAGE_SIZE; ram_addr = start + addr; cpu_physical_memory_set_dirty_range(ram_addr, - TARGET_PAGE_SIZE * hpratio); + TARGET_PAGE_SIZE * hpratio, + DIRTY_CLIENTS_ALL); } while (c != 0); } } diff --git a/memory.c b/memory.c index b7ca987c07..418cac7ce1 100644 --- a/memory.c +++ b/memory.c @@ -1461,7 +1461,8 @@ void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size) { assert(mr->terminates); - cpu_physical_memory_set_dirty_range(mr->ram_addr + addr, size); + cpu_physical_memory_set_dirty_range(mr->ram_addr + addr, size, + memory_region_get_dirty_log_mask(mr)); } bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr, -- cgit v1.2.3-55-g7522 From 72b47e79cef36ed6ffc718f10e21001d7ec2a66f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 22 Apr 2015 13:48:25 +0200 Subject: exec: invert return value of cpu_physical_memory_get_clean, rename While it is obvious that cpu_physical_memory_get_dirty returns true even if a single page is dirty, the same is not true for cpu_physical_memory_get_clean; one would expect that it returns true only if all the pages are clean, but it actually looks for even one clean page. (By contrast, the caller of that function, cpu_physical_memory_range_includes_clean, has a good name). To clarify, rename the function to cpu_physical_memory_all_dirty and return true if _all_ the pages are dirty. This is the opposite of the previous meaning, because "all are 1" is the same as "not (any is 0)", so we have to modify cpu_physical_memory_range_includes_clean as well. Reviewed-by: Fam Zheng Signed-off-by: Paolo Bonzini --- include/exec/ram_addr.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include/exec') diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 5bbc7bb41d..c221bd7dd0 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -59,7 +59,7 @@ static inline bool cpu_physical_memory_get_dirty(ram_addr_t start, return next < end; } -static inline bool cpu_physical_memory_get_clean(ram_addr_t start, +static inline bool cpu_physical_memory_all_dirty(ram_addr_t start, ram_addr_t length, unsigned client) { @@ -71,7 +71,7 @@ static inline bool cpu_physical_memory_get_clean(ram_addr_t start, page = start >> TARGET_PAGE_BITS; next = find_next_zero_bit(ram_list.dirty_memory[client], end, page); - return next < end; + return next >= end; } static inline bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr, @@ -92,10 +92,10 @@ static inline bool cpu_physical_memory_is_clean(ram_addr_t addr) static inline bool cpu_physical_memory_range_includes_clean(ram_addr_t start, ram_addr_t length) { - bool vga = cpu_physical_memory_get_clean(start, length, DIRTY_MEMORY_VGA); - bool code = cpu_physical_memory_get_clean(start, length, DIRTY_MEMORY_CODE); + bool vga = !cpu_physical_memory_all_dirty(start, length, DIRTY_MEMORY_VGA); + bool code = !cpu_physical_memory_all_dirty(start, length, DIRTY_MEMORY_CODE); bool migration = - cpu_physical_memory_get_clean(start, length, DIRTY_MEMORY_MIGRATION); + !cpu_physical_memory_all_dirty(start, length, DIRTY_MEMORY_MIGRATION); return vga || code || migration; } -- cgit v1.2.3-55-g7522 From e87f7778b64d4a6a78e16c288c7fdc6c15317d5f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 25 Mar 2015 15:21:39 +0100 Subject: exec: only check relevant bitmaps for cleanliness Most of the time, not all bitmaps have to be marked as dirty; do not do anything if the interesting ones are already dirty. Previously, any clean bitmap would have cause all the bitmaps to be marked dirty. In fact, unless running TCG most of the time bitmap operations need not be done at all, because memory_region_is_logging returns zero. In this case, skip the call to cpu_physical_memory_range_includes_clean altogether as well. With this patch, cpu_physical_memory_set_dirty_range is called unconditionally, so there need not be anymore a separate call to xen_modified_memory. Reviewed-by: Fam Zheng Signed-off-by: Paolo Bonzini --- exec.c | 22 +++++++++++++--------- include/exec/ram_addr.h | 25 ++++++++++++++++++------- 2 files changed, 31 insertions(+), 16 deletions(-) (limited to 'include/exec') diff --git a/exec.c b/exec.c index fe137bd725..162c57908e 100644 --- a/exec.c +++ b/exec.c @@ -2259,16 +2259,20 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr, hwaddr length) { - if (cpu_physical_memory_range_includes_clean(addr, length)) { - uint8_t dirty_log_mask = memory_region_get_dirty_log_mask(mr); - if (dirty_log_mask & (1 << DIRTY_MEMORY_CODE)) { - tb_invalidate_phys_range(addr, addr + length); - dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE); - } - cpu_physical_memory_set_dirty_range(addr, length, dirty_log_mask); - } else { - xen_modified_memory(addr, length); + uint8_t dirty_log_mask = memory_region_get_dirty_log_mask(mr); + /* No early return if dirty_log_mask is or becomes 0, because + * cpu_physical_memory_set_dirty_range will still call + * xen_modified_memory. + */ + if (dirty_log_mask) { + dirty_log_mask = + cpu_physical_memory_range_includes_clean(addr, length, dirty_log_mask); + } + if (dirty_log_mask & (1 << DIRTY_MEMORY_CODE)) { + tb_invalidate_phys_range(addr, addr + length); + dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE); } + cpu_physical_memory_set_dirty_range(addr, length, dirty_log_mask); } static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr) diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index c221bd7dd0..3e42b4f602 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -89,14 +89,25 @@ static inline bool cpu_physical_memory_is_clean(ram_addr_t addr) return !(vga && code && migration); } -static inline bool cpu_physical_memory_range_includes_clean(ram_addr_t start, - ram_addr_t length) +static inline uint8_t cpu_physical_memory_range_includes_clean(ram_addr_t start, + ram_addr_t length, + uint8_t mask) { - bool vga = !cpu_physical_memory_all_dirty(start, length, DIRTY_MEMORY_VGA); - bool code = !cpu_physical_memory_all_dirty(start, length, DIRTY_MEMORY_CODE); - bool migration = - !cpu_physical_memory_all_dirty(start, length, DIRTY_MEMORY_MIGRATION); - return vga || code || migration; + uint8_t ret = 0; + + if (mask & (1 << DIRTY_MEMORY_VGA) && + !cpu_physical_memory_all_dirty(start, length, DIRTY_MEMORY_VGA)) { + ret |= (1 << DIRTY_MEMORY_VGA); + } + if (mask & (1 << DIRTY_MEMORY_CODE) && + !cpu_physical_memory_all_dirty(start, length, DIRTY_MEMORY_CODE)) { + ret |= (1 << DIRTY_MEMORY_CODE); + } + if (mask & (1 << DIRTY_MEMORY_MIGRATION) && + !cpu_physical_memory_all_dirty(start, length, DIRTY_MEMORY_MIGRATION)) { + ret |= (1 << DIRTY_MEMORY_MIGRATION); + } + return ret; } static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, -- cgit v1.2.3-55-g7522 From 9460dee4b2258e3990906fb34099481c8334c267 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 23 Mar 2015 11:41:32 +0100 Subject: memory: do not touch code dirty bitmap unless TCG is enabled cpu_physical_memory_set_dirty_lebitmap unconditionally syncs the DIRTY_MEMORY_CODE bitmap. This however is unused unless TCG is enabled. Reviewed-by: Fam Zheng Signed-off-by: Paolo Bonzini --- include/exec/ram_addr.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'include/exec') diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 3e42b4f602..98110108b7 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -162,11 +162,14 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION][page + k] |= temp; ram_list.dirty_memory[DIRTY_MEMORY_VGA][page + k] |= temp; - ram_list.dirty_memory[DIRTY_MEMORY_CODE][page + k] |= temp; + if (tcg_enabled()) { + ram_list.dirty_memory[DIRTY_MEMORY_CODE][page + k] |= temp; + } } } xen_modified_memory(start, pages << TARGET_PAGE_BITS); } else { + uint8_t clients = tcg_enabled() ? DIRTY_CLIENTS_ALL : DIRTY_CLIENTS_NOCODE; /* * bitmap-traveling is faster than memory-traveling (for addr...) * especially when most of the memory is not dirty. @@ -181,8 +184,7 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, addr = page_number * TARGET_PAGE_SIZE; ram_addr = start + addr; cpu_physical_memory_set_dirty_range(ram_addr, - TARGET_PAGE_SIZE * hpratio, - DIRTY_CLIENTS_ALL); + TARGET_PAGE_SIZE * hpratio, clients); } while (c != 0); } } -- cgit v1.2.3-55-g7522 From d114875b9a1c21162f69a12d72f69a22e7bab376 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 2 Dec 2014 11:23:16 +0000 Subject: memory: use atomic ops for setting dirty memory bits Use set_bit_atomic() and bitmap_set_atomic() so that multiple threads can dirty memory without race conditions. Signed-off-by: Stefan Hajnoczi Message-Id: <1417519399-3166-4-git-send-email-stefanha@redhat.com> Reviewed-by: Fam Zheng Signed-off-by: Paolo Bonzini --- include/exec/ram_addr.h | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'include/exec') diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 98110108b7..9f73076044 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -114,7 +114,7 @@ static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr, unsigned client) { assert(client < DIRTY_MEMORY_NUM); - set_bit(addr >> TARGET_PAGE_BITS, ram_list.dirty_memory[client]); + set_bit_atomic(addr >> TARGET_PAGE_BITS, ram_list.dirty_memory[client]); } static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, @@ -122,17 +122,18 @@ static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, uint8_t mask) { unsigned long end, page; + unsigned long **d = ram_list.dirty_memory; end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; page = start >> TARGET_PAGE_BITS; if (likely(mask & (1 << DIRTY_MEMORY_MIGRATION))) { - bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION], page, end - page); + bitmap_set_atomic(d[DIRTY_MEMORY_MIGRATION], page, end - page); } if (unlikely(mask & (1 << DIRTY_MEMORY_VGA))) { - bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_VGA], page, end - page); + bitmap_set_atomic(d[DIRTY_MEMORY_VGA], page, end - page); } if (unlikely(mask & (1 << DIRTY_MEMORY_CODE))) { - bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_CODE], page, end - page); + bitmap_set_atomic(d[DIRTY_MEMORY_CODE], page, end - page); } xen_modified_memory(start, length); } @@ -159,11 +160,12 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, for (k = 0; k < nr; k++) { if (bitmap[k]) { unsigned long temp = leul_to_cpu(bitmap[k]); + unsigned long **d = ram_list.dirty_memory; - ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION][page + k] |= temp; - ram_list.dirty_memory[DIRTY_MEMORY_VGA][page + k] |= temp; + atomic_or(&d[DIRTY_MEMORY_MIGRATION][page + k], temp); + atomic_or(&d[DIRTY_MEMORY_VGA][page + k], temp); if (tcg_enabled()) { - ram_list.dirty_memory[DIRTY_MEMORY_CODE][page + k] |= temp; + atomic_or(&d[DIRTY_MEMORY_CODE][page + k], temp); } } } -- cgit v1.2.3-55-g7522 From 20015f72bda7d2f356c43580a5542a659afedf83 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 2 Dec 2014 11:23:17 +0000 Subject: migration: move dirty bitmap sync to ram_addr.h The dirty memory bitmap is managed by ram_addr.h and copied to migration_bitmap[] periodically during live migration. Move the code to sync the bitmap to ram_addr.h where related code lives. Signed-off-by: Stefan Hajnoczi Message-Id: <1417519399-3166-5-git-send-email-stefanha@redhat.com> Reviewed-by: Fam Zheng Signed-off-by: Paolo Bonzini --- arch_init.c | 46 ++-------------------------------------------- include/exec/ram_addr.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 44 deletions(-) (limited to 'include/exec') diff --git a/arch_init.c b/arch_init.c index b5d90a41fa..d29447497b 100644 --- a/arch_init.c +++ b/arch_init.c @@ -609,52 +609,10 @@ ram_addr_t migration_bitmap_find_and_reset_dirty(MemoryRegion *mr, return (next - base) << TARGET_PAGE_BITS; } -static inline bool migration_bitmap_set_dirty(ram_addr_t addr) -{ - bool ret; - int nr = addr >> TARGET_PAGE_BITS; - - ret = test_and_set_bit(nr, migration_bitmap); - - if (!ret) { - migration_dirty_pages++; - } - return ret; -} - static void migration_bitmap_sync_range(ram_addr_t start, ram_addr_t length) { - ram_addr_t addr; - unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS); - - /* start address is aligned at the start of a word? */ - if (((page * BITS_PER_LONG) << TARGET_PAGE_BITS) == start) { - int k; - int nr = BITS_TO_LONGS(length >> TARGET_PAGE_BITS); - unsigned long *src = ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION]; - - for (k = page; k < page + nr; k++) { - if (src[k]) { - unsigned long new_dirty; - new_dirty = ~migration_bitmap[k]; - migration_bitmap[k] |= src[k]; - new_dirty &= src[k]; - migration_dirty_pages += ctpopl(new_dirty); - src[k] = 0; - } - } - } else { - for (addr = 0; addr < length; addr += TARGET_PAGE_SIZE) { - if (cpu_physical_memory_get_dirty(start + addr, - TARGET_PAGE_SIZE, - DIRTY_MEMORY_MIGRATION)) { - cpu_physical_memory_reset_dirty(start + addr, - TARGET_PAGE_SIZE, - DIRTY_MEMORY_MIGRATION); - migration_bitmap_set_dirty(start + addr); - } - } - } + migration_dirty_pages += + cpu_physical_memory_sync_dirty_bitmap(migration_bitmap, start, length); } diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 9f73076044..63db371850 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -218,5 +218,49 @@ static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start, void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t length, unsigned client); +static inline +uint64_t cpu_physical_memory_sync_dirty_bitmap(unsigned long *dest, + ram_addr_t start, + ram_addr_t length) +{ + ram_addr_t addr; + unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS); + uint64_t num_dirty = 0; + + /* start address is aligned at the start of a word? */ + if (((page * BITS_PER_LONG) << TARGET_PAGE_BITS) == start) { + int k; + int nr = BITS_TO_LONGS(length >> TARGET_PAGE_BITS); + unsigned long *src = ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION]; + + for (k = page; k < page + nr; k++) { + if (src[k]) { + unsigned long new_dirty; + new_dirty = ~dest[k]; + dest[k] |= src[k]; + new_dirty &= src[k]; + num_dirty += ctpopl(new_dirty); + src[k] = 0; + } + } + } else { + for (addr = 0; addr < length; addr += TARGET_PAGE_SIZE) { + if (cpu_physical_memory_get_dirty(start + addr, + TARGET_PAGE_SIZE, + DIRTY_MEMORY_MIGRATION)) { + long k = (start + addr) >> TARGET_PAGE_BITS; + if (!test_and_set_bit(k, dest)) { + num_dirty++; + } + cpu_physical_memory_reset_dirty(start + addr, + TARGET_PAGE_SIZE, + DIRTY_MEMORY_MIGRATION); + } + } + } + + return num_dirty; +} + #endif #endif -- cgit v1.2.3-55-g7522 From 03eebc9e3246b9b3f5925aa41f7dfd7c1e467875 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 2 Dec 2014 11:23:18 +0000 Subject: memory: replace cpu_physical_memory_reset_dirty() with test-and-clear The cpu_physical_memory_reset_dirty() function is sometimes used together with cpu_physical_memory_get_dirty(). This is not atomic since two separate accesses to the dirty memory bitmap are made. Turn cpu_physical_memory_reset_dirty() and cpu_physical_memory_clear_dirty_range_type() into the atomic cpu_physical_memory_test_and_clear_dirty(). Signed-off-by: Stefan Hajnoczi Message-Id: <1417519399-3166-6-git-send-email-stefanha@redhat.com> Reviewed-by: Fam Zheng Signed-off-by: Paolo Bonzini --- cputlb.c | 4 ++-- exec.c | 23 +++++++++++++++++------ include/exec/ram_addr.h | 33 ++++++++++----------------------- memory.c | 11 ++++------- 4 files changed, 33 insertions(+), 38 deletions(-) (limited to 'include/exec') diff --git a/cputlb.c b/cputlb.c index dd1203b938..a50608676c 100644 --- a/cputlb.c +++ b/cputlb.c @@ -125,8 +125,8 @@ void tlb_flush_page(CPUState *cpu, target_ulong addr) can be detected */ void tlb_protect_code(ram_addr_t ram_addr) { - cpu_physical_memory_reset_dirty(ram_addr, TARGET_PAGE_SIZE, - DIRTY_MEMORY_CODE); + cpu_physical_memory_test_and_clear_dirty(ram_addr, TARGET_PAGE_SIZE, + DIRTY_MEMORY_CODE); } /* update the TLB so that writes in physical page 'phys_addr' are no longer diff --git a/exec.c b/exec.c index 162c57908e..487583b1bd 100644 --- a/exec.c +++ b/exec.c @@ -852,16 +852,27 @@ static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length) } /* Note: start and end must be within the same ram block. */ -void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t length, - unsigned client) +bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, + ram_addr_t length, + unsigned client) { - if (length == 0) - return; - cpu_physical_memory_clear_dirty_range_type(start, length, client); + unsigned long end, page; + bool dirty; - if (tcg_enabled()) { + if (length == 0) { + return false; + } + + end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; + page = start >> TARGET_PAGE_BITS; + dirty = bitmap_test_and_clear_atomic(ram_list.dirty_memory[client], + page, end - page); + + if (dirty && tcg_enabled()) { tlb_reset_dirty_range_all(start, length); } + + return dirty; } /* Called from RCU critical section */ diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 63db371850..a2beea7925 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -194,30 +194,19 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, } #endif /* not _WIN32 */ -static inline void cpu_physical_memory_clear_dirty_range_type(ram_addr_t start, - ram_addr_t length, - unsigned client) -{ - unsigned long end, page; - - assert(client < DIRTY_MEMORY_NUM); - end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; - page = start >> TARGET_PAGE_BITS; - bitmap_clear(ram_list.dirty_memory[client], page, end - page); -} +bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, + ram_addr_t length, + unsigned client); static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start, ram_addr_t length) { - cpu_physical_memory_clear_dirty_range_type(start, length, DIRTY_MEMORY_MIGRATION); - cpu_physical_memory_clear_dirty_range_type(start, length, DIRTY_MEMORY_VGA); - cpu_physical_memory_clear_dirty_range_type(start, length, DIRTY_MEMORY_CODE); + cpu_physical_memory_test_and_clear_dirty(start, length, DIRTY_MEMORY_MIGRATION); + cpu_physical_memory_test_and_clear_dirty(start, length, DIRTY_MEMORY_VGA); + cpu_physical_memory_test_and_clear_dirty(start, length, DIRTY_MEMORY_CODE); } -void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t length, - unsigned client); - static inline uint64_t cpu_physical_memory_sync_dirty_bitmap(unsigned long *dest, ram_addr_t start, @@ -245,16 +234,14 @@ uint64_t cpu_physical_memory_sync_dirty_bitmap(unsigned long *dest, } } else { for (addr = 0; addr < length; addr += TARGET_PAGE_SIZE) { - if (cpu_physical_memory_get_dirty(start + addr, - TARGET_PAGE_SIZE, - DIRTY_MEMORY_MIGRATION)) { + if (cpu_physical_memory_test_and_clear_dirty( + start + addr, + TARGET_PAGE_SIZE, + DIRTY_MEMORY_MIGRATION)) { long k = (start + addr) >> TARGET_PAGE_BITS; if (!test_and_set_bit(k, dest)) { num_dirty++; } - cpu_physical_memory_reset_dirty(start + addr, - TARGET_PAGE_SIZE, - DIRTY_MEMORY_MIGRATION); } } } diff --git a/memory.c b/memory.c index 418cac7ce1..0c5d32807e 100644 --- a/memory.c +++ b/memory.c @@ -1468,13 +1468,9 @@ void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr, bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size, unsigned client) { - bool ret; assert(mr->terminates); - ret = cpu_physical_memory_get_dirty(mr->ram_addr + addr, size, client); - if (ret) { - cpu_physical_memory_reset_dirty(mr->ram_addr + addr, size, client); - } - return ret; + return cpu_physical_memory_test_and_clear_dirty(mr->ram_addr + addr, + size, client); } @@ -1518,7 +1514,8 @@ void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size, unsigned client) { assert(mr->terminates); - cpu_physical_memory_reset_dirty(mr->ram_addr + addr, size, client); + cpu_physical_memory_test_and_clear_dirty(mr->ram_addr + addr, size, + client); } int memory_region_get_fd(MemoryRegion *mr) -- cgit v1.2.3-55-g7522 From 5f2cb94688bd0b2c88e0fc1ac3c4582965b7b106 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 2 Dec 2014 11:23:19 +0000 Subject: memory: make cpu_physical_memory_sync_dirty_bitmap() fully atomic The fast path of cpu_physical_memory_sync_dirty_bitmap() directly manipulates the dirty bitmap. Use atomic_xchg() to make the test-and-clear atomic. Signed-off-by: Stefan Hajnoczi Message-Id: <1417519399-3166-7-git-send-email-stefanha@redhat.com> [Only do xchg on nonzero words. - Paolo] Reviewed-by: Fam Zheng Signed-off-by: Paolo Bonzini --- include/exec/ram_addr.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/exec') diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index a2beea7925..c113f21140 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -224,12 +224,12 @@ uint64_t cpu_physical_memory_sync_dirty_bitmap(unsigned long *dest, for (k = page; k < page + nr; k++) { if (src[k]) { + unsigned long bits = atomic_xchg(&src[k], 0); unsigned long new_dirty; new_dirty = ~dest[k]; - dest[k] |= src[k]; - new_dirty &= src[k]; + dest[k] |= bits; + new_dirty &= bits; num_dirty += ctpopl(new_dirty); - src[k] = 0; } } } else { -- cgit v1.2.3-55-g7522 From f794aa4a2fd772a3ec413c4e478cc23857cfee98 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Apr 2015 14:52:04 +0200 Subject: target-i386: introduce cpu_get_mem_attrs Signed-off-by: Paolo Bonzini --- include/exec/memattrs.h | 4 +++- target-i386/cpu.h | 5 +++++ target-i386/helper.c | 3 ++- target-i386/kvm.c | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) (limited to 'include/exec') diff --git a/include/exec/memattrs.h b/include/exec/memattrs.h index 96dc440423..f8537a8d91 100644 --- a/include/exec/memattrs.h +++ b/include/exec/memattrs.h @@ -29,7 +29,9 @@ typedef struct MemTxAttrs { * "didn't specify" if necessary. */ unsigned int unspecified:1; - /* ARM/AMBA TrustZone Secure access */ + /* ARM/AMBA: TrustZone Secure access + * x86: System Management Mode access + */ unsigned int secure:1; /* Memory access is usermode (unprivileged) */ unsigned int user:1; diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 26182bdc7e..74e8819dba 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -1292,6 +1292,11 @@ static inline void cpu_load_efer(CPUX86State *env, uint64_t val) } } +static inline MemTxAttrs cpu_get_mem_attrs(CPUX86State *env) +{ + return ((MemTxAttrs) { .secure = (env->hflags & HF_SMM_MASK) != 0 }); +} + /* fpu_helper.c */ void cpu_set_mxcsr(CPUX86State *env, uint32_t val); void cpu_set_fpuc(CPUX86State *env, uint16_t val); diff --git a/target-i386/helper.c b/target-i386/helper.c index 4f1ddf701e..62e801b28e 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -771,7 +771,8 @@ do_check_protect_pse36: page_offset = vaddr & (page_size - 1); paddr = pte + page_offset; - tlb_set_page(cs, vaddr, paddr, prot, mmu_idx, page_size); + tlb_set_page_with_attrs(cs, vaddr, paddr, cpu_get_mem_attrs(env), + prot, mmu_idx, page_size); return 0; do_fault_rsvd: error_code |= PG_ERROR_RSVD_MASK; diff --git a/target-i386/kvm.c b/target-i386/kvm.c index ca2da84501..5a236e3103 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -2259,7 +2259,7 @@ MemTxAttrs kvm_arch_post_run(CPUState *cpu, struct kvm_run *run) } cpu_set_apic_tpr(x86_cpu->apic_state, run->cr8); cpu_set_apic_base(x86_cpu->apic_state, run->apic_base); - return MEMTXATTRS_UNSPECIFIED; + return cpu_get_mem_attrs(env); } int kvm_arch_process_async_events(CPUState *cs) -- cgit v1.2.3-55-g7522