From 5832d1f2f51e1a1991c53ea98c535a619cf03001 Mon Sep 17 00:00:00 2001 From: aliguori Date: Mon, 24 Nov 2008 19:36:26 +0000 Subject: [PATCH] kvm: Introduce kvm logging interface (Glauber Costa) Introduce functions to control logging of memory regions. We select regions based on its start address, a guest_physical_addr (target_phys_addr_t, in qemu nomenclature). The main user of this interface right now is VGA optimization (a way of reducing the number of mmio exits). Signed-off-by: Glauber Costa Signed-off-by: Anthony Liguori git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5792 c046a42c-6fe2-441c-8c8c-71466251a162 --- kvm-all.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++------ kvm.h | 4 ++ 2 files changed, 113 insertions(+), 13 deletions(-) diff --git a/kvm-all.c b/kvm-all.c index e44f296b01..39f826bbeb 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -2,9 +2,11 @@ * QEMU KVM support * * Copyright IBM, Corp. 2008 + * Red Hat, Inc. 2008 * * Authors: * Anthony Liguori + * Glauber Costa * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -41,6 +43,8 @@ typedef struct KVMSlot int flags; } KVMSlot; +typedef struct kvm_dirty_log KVMDirtyLog; + int kvm_allowed = 0; struct KVMState @@ -82,6 +86,20 @@ static KVMSlot *kvm_lookup_slot(KVMState *s, target_phys_addr_t start_addr) return NULL; } +static int kvm_set_user_memory_region(KVMState *s, KVMSlot *slot) +{ + struct kvm_userspace_memory_region mem; + + mem.slot = slot->slot; + mem.guest_phys_addr = slot->start_addr; + mem.memory_size = slot->memory_size; + mem.userspace_addr = (unsigned long)phys_ram_base + slot->phys_offset; + mem.flags = slot->flags; + + return kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); +} + + int kvm_init_vcpu(CPUState *env) { KVMState *s = kvm_state; @@ -119,6 +137,97 @@ err: return ret; } +/* + * dirty pages logging control + */ +static int kvm_dirty_pages_log_change(target_phys_addr_t phys_addr, target_phys_addr_t end_addr, + unsigned flags, + unsigned mask) +{ + KVMState *s = kvm_state; + KVMSlot *mem = kvm_lookup_slot(s, phys_addr); + if (mem == NULL) { + dprintf("invalid parameters %llx-%llx\n", phys_addr, end_addr); + return -EINVAL; + } + + flags = (mem->flags & ~mask) | flags; + /* Nothing changed, no need to issue ioctl */ + if (flags == mem->flags) + return 0; + + mem->flags = flags; + + return kvm_set_user_memory_region(s, mem); +} + +int kvm_log_start(target_phys_addr_t phys_addr, target_phys_addr_t end_addr) +{ + return kvm_dirty_pages_log_change(phys_addr, end_addr, + KVM_MEM_LOG_DIRTY_PAGES, + KVM_MEM_LOG_DIRTY_PAGES); +} + +int kvm_log_stop(target_phys_addr_t phys_addr, target_phys_addr_t end_addr) +{ + return kvm_dirty_pages_log_change(phys_addr, end_addr, + 0, + KVM_MEM_LOG_DIRTY_PAGES); +} + +/** + * kvm_physical_sync_dirty_bitmap - Grab dirty bitmap from kernel space + * This function updates qemu's dirty bitmap using cpu_physical_memory_set_dirty(). + * This means all bits are set to dirty. + * + * @start_add: start of logged region. This is what we use to search the memslot + * @end_addr: end of logged region. + */ +void kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, target_phys_addr_t end_addr) +{ + KVMState *s = kvm_state; + KVMDirtyLog d; + KVMSlot *mem = kvm_lookup_slot(s, start_addr); + unsigned long alloc_size; + ram_addr_t addr; + target_phys_addr_t phys_addr = start_addr; + + dprintf("sync addr: %llx into %lx\n", start_addr, mem->phys_offset); + if (mem == NULL) { + fprintf(stderr, "BUG: %s: invalid parameters\n", __func__); + return; + } + + alloc_size = mem->memory_size >> TARGET_PAGE_BITS / sizeof(d.dirty_bitmap); + d.dirty_bitmap = qemu_mallocz(alloc_size); + + if (d.dirty_bitmap == NULL) { + dprintf("Could not allocate dirty bitmap\n"); + return; + } + + d.slot = mem->slot; + dprintf("slot %d, phys_addr %llx, uaddr: %llx\n", + d.slot, mem->start_addr, mem->phys_offset); + + if (kvm_vm_ioctl(s, KVM_GET_DIRTY_LOG, &d) == -1) { + dprintf("ioctl failed %d\n", errno); + goto out; + } + + phys_addr = start_addr; + for (addr = mem->phys_offset; phys_addr < end_addr; phys_addr+= TARGET_PAGE_SIZE, addr += TARGET_PAGE_SIZE) { + unsigned long *bitmap = (unsigned long *)d.dirty_bitmap; + unsigned nr = (phys_addr - start_addr) >> TARGET_PAGE_BITS; + unsigned word = nr / (sizeof(*bitmap) * 8); + unsigned bit = nr % (sizeof(*bitmap) * 8); + if ((bitmap[word] >> bit) & 1) + cpu_physical_memory_set_dirty(addr); + } +out: + qemu_free(d.dirty_bitmap); +} + int kvm_init(int smp_cpus) { KVMState *s; @@ -316,19 +425,6 @@ int kvm_cpu_exec(CPUState *env) return ret; } -static int kvm_set_user_memory_region(KVMState *s, KVMSlot *slot) -{ - struct kvm_userspace_memory_region mem; - - mem.slot = slot->slot; - mem.guest_phys_addr = slot->start_addr; - mem.memory_size = slot->memory_size; - mem.userspace_addr = (unsigned long)phys_ram_base + slot->phys_offset; - mem.flags = slot->flags; - - return kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); -} - void kvm_set_phys_mem(target_phys_addr_t start_addr, ram_addr_t size, ram_addr_t phys_offset) diff --git a/kvm.h b/kvm.h index 304de272a7..fb952a3fb1 100644 --- a/kvm.h +++ b/kvm.h @@ -38,6 +38,10 @@ void kvm_set_phys_mem(target_phys_addr_t start_addr, ram_addr_t size, ram_addr_t phys_offset); +void kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, target_phys_addr_t end_addr); + +int kvm_log_start(target_phys_addr_t phys_addr, target_phys_addr_t len); +int kvm_log_stop(target_phys_addr_t phys_addr, target_phys_addr_t len); /* internal API */ struct KVMState;