qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] vga optmization


From: Glauber Costa
Subject: [Qemu-devel] vga optmization
Date: Mon, 3 Nov 2008 15:31:11 -0200
User-agent: Mutt/1.5.18 (2008-05-17)

Hi guys,

this is a port of current kvm vga memory optimization to our new
infrastructure proposed by anthony. It's goal is to use as few
kvm specific hooks as possible. In fact, the only one I'm relying
on is enabling/disabling of logging. The rest, is pretty much general.

We map the linear frame buffer area as RAM, and then use dirty tracking
to decide whether or not to update it. To be consistent with qemu,
this version, differently from upstream kvm, tracks memory based on its
physical address, represented by vram_offset, instead of vram_ptr, or
any other construct.

Let me know what you think

diff --git a/cpu-all.h b/cpu-all.h
index cdd79bc..9118f4d 100644
--- a/cpu-all.h
+++ b/cpu-all.h
@@ -46,6 +46,8 @@
 
 #ifdef BSWAP_NEEDED
 
+#include "kvm.h"
+
 static inline uint16_t tswap16(uint16_t s)
 {
     return bswap16(s);
@@ -909,17 +911,10 @@ int cpu_memory_rw_debug(CPUState *env, target_ulong addr,
 #define KQEMU_DIRTY_FLAG     0x04
 #define MIGRATION_DIRTY_FLAG 0x08
 
-/* read dirty bit (return 0 or 1) */
-static inline int cpu_physical_memory_is_dirty(ram_addr_t addr)
-{
-    return phys_ram_dirty[addr >> TARGET_PAGE_BITS] == 0xff;
-}
+int cpu_physical_memory_get_dirty(ram_addr_t addr, int dirty_flags);
+int cpu_physical_memory_is_dirty(ram_addr_t addr);
 
-static inline int cpu_physical_memory_get_dirty(ram_addr_t addr,
-                                                int dirty_flags)
-{
-    return phys_ram_dirty[addr >> TARGET_PAGE_BITS] & dirty_flags;
-}
+void qemu_physical_sync_dirty_bitmap(ram_addr_t start_addr);
 
 static inline void cpu_physical_memory_set_dirty(ram_addr_t addr)
 {
diff --git a/exec.c b/exec.c
index ef1072b..4ea145e 100644
--- a/exec.c
+++ b/exec.c
@@ -1818,11 +1818,40 @@ int cpu_physical_memory_set_dirty_tracking(int enable)
     return 0;
 }
 
+int cpu_physical_memory_get_dirty(ram_addr_t addr,
+                                                int dirty_flags)
+{
+    int is_dirty = 0;
+    is_dirty = phys_ram_dirty[addr >> TARGET_PAGE_BITS] & dirty_flags;
+    if (is_dirty)
+        goto out;
+#ifdef CONFIG_KVM
+    if (kvm_enabled())
+        is_dirty = kvm_physical_memory_get_dirty(addr);
+        /* to make it usable below */
+        is_dirty = !!is_dirty * 0xff;
+#endif
+out:
+    return is_dirty;
+}
+
+/* read dirty bit (return 0 or 1) */
+int cpu_physical_memory_is_dirty(ram_addr_t addr)
+{
+    return cpu_physical_memory_get_dirty(addr, 0xff) == 0xff;
+}
+
 int cpu_physical_memory_get_dirty_tracking(void)
 {
     return in_migration;
 }
 
+void qemu_physical_sync_dirty_bitmap(ram_addr_t start_addr)
+{
+    if (kvm_enabled())
+        kvm_physical_sync_dirty_bitmap(start_addr);
+}
+
 static inline void tlb_update_dirty(CPUTLBEntry *tlb_entry)
 {
     ram_addr_t ram_addr;
diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c
index af9c9e6..27be4a7 100644
--- a/hw/cirrus_vga.c
+++ b/hw/cirrus_vga.c
@@ -31,6 +31,7 @@
 #include "pci.h"
 #include "console.h"
 #include "vga_int.h"
+#include "kvm.h"
 
 /*
  * TODO:
@@ -248,6 +249,9 @@ typedef struct CirrusVGAState {
     int cirrus_linear_io_addr;
     int cirrus_linear_bitblt_io_addr;
     int cirrus_mmio_io_addr;
+    uint32_t cirrus_lfb_addr;
+    uint32_t cirrus_lfb_end;
+    uint32_t cirrus_lfb_mapped;
     uint32_t cirrus_addr_mask;
     uint32_t linear_mmio_mask;
     uint8_t cirrus_shadow_gr0;
@@ -2618,6 +2622,69 @@ static CPUWriteMemoryFunc *cirrus_linear_bitblt_write[3] 
= {
     cirrus_linear_bitblt_writel,
 };
 
+void set_vram_mapping(target_phys_addr_t begin, target_phys_addr_t end, 
ram_addr_t target)
+{
+    /* align begin and end address */
+    begin = begin & TARGET_PAGE_MASK;
+    end = begin + VGA_RAM_SIZE;
+    end = (end + TARGET_PAGE_SIZE -1 ) & TARGET_PAGE_MASK;
+
+    cpu_register_physical_memory(begin, end - begin, target);
+
+    if (kvm_enabled()) {
+        /* XXX: per-slot dirty tracking in qemu may get rid of it */
+        kvm_log_start(begin, end - begin);
+    }
+}
+
+void unset_vram_mapping(target_phys_addr_t begin, target_phys_addr_t end)
+{
+    /* align begin and end address */
+    end = begin + VGA_RAM_SIZE;
+    begin = begin & TARGET_PAGE_MASK;
+    end = (end + TARGET_PAGE_SIZE -1 ) & TARGET_PAGE_MASK;
+
+    if (kvm_enabled()) {
+        /* XXX: per-slot dirty tracking in qemu may get rid of it */
+        kvm_log_stop(begin, end - begin);
+    }
+    cpu_register_physical_memory(begin, end - begin, IO_MEM_UNASSIGNED);
+
+}
+
+static void map_linear_vram(CirrusVGAState *s)
+{
+
+    if (!s->cirrus_lfb_mapped && s->cirrus_lfb_addr && s->cirrus_lfb_end) {
+        set_vram_mapping(s->cirrus_lfb_addr, s->cirrus_lfb_end, 
s->vram_offset);
+        s->cirrus_lfb_mapped = 1;
+    }
+    
+    if(!(s->cirrus_srcptr != s->cirrus_srcptr_end)
+        && !((s->sr[0x07] & 0x01) == 0)
+        && !((s->gr[0x0B] & 0x14) == 0x14)
+        && !(s->gr[0x0B] & 0x02)) {
+        cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x20000, 
s->vram_offset | IO_MEM_RAM);
+    }
+    else {
+        cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x20000, 
s->vga_io_memory);
+    }
+}
+
+void unmap_linear_vram(CirrusVGAState *s)
+{
+
+    if (s->cirrus_lfb_mapped && s->cirrus_lfb_addr && s->cirrus_lfb_end) {
+        unset_vram_mapping(s->cirrus_lfb_addr,
+                           s->cirrus_lfb_end);
+        s->cirrus_lfb_mapped = 0;
+    }
+
+    cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000,
+                                 s->vga_io_memory);
+
+}
+
 /* Compute the memory access functions */
 static void cirrus_update_memory_access(CirrusVGAState *s)
 {
@@ -2636,11 +2703,13 @@ static void cirrus_update_memory_access(CirrusVGAState 
*s)
 
        mode = s->gr[0x05] & 0x7;
        if (mode < 4 || mode > 5 || ((s->gr[0x0B] & 0x4) == 0)) {
+            map_linear_vram(s);
             s->cirrus_linear_write[0] = cirrus_linear_mem_writeb;
             s->cirrus_linear_write[1] = cirrus_linear_mem_writew;
             s->cirrus_linear_write[2] = cirrus_linear_mem_writel;
         } else {
         generic_io:
+            unmap_linear_vram(s);
             s->cirrus_linear_write[0] = cirrus_linear_writeb;
             s->cirrus_linear_write[1] = cirrus_linear_writew;
             s->cirrus_linear_write[2] = cirrus_linear_writel;
@@ -3117,7 +3186,7 @@ static int cirrus_vga_load(QEMUFile *f, void *opaque, int 
version_id)
 
 static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci)
 {
-    int vga_io_memory, i;
+    int i;
     static int inited;
 
     if (!inited) {
@@ -3156,10 +3225,10 @@ static void cirrus_init_common(CirrusVGAState * s, int 
device_id, int is_pci)
     register_ioport_read(0x3ba, 1, 1, vga_ioport_read, s);
     register_ioport_read(0x3da, 1, 1, vga_ioport_read, s);
 
-    vga_io_memory = cpu_register_io_memory(0, cirrus_vga_mem_read,
+    s->vga_io_memory = cpu_register_io_memory(0, cirrus_vga_mem_read,
                                            cirrus_vga_mem_write, s);
     cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000,
-                                 vga_io_memory);
+                                 s->vga_io_memory);
 
     s->sr[0x06] = 0x0f;
     if (device_id == CIRRUS_ID_CLGD5446) {
@@ -3258,9 +3327,13 @@ static void cirrus_pci_lfb_map(PCIDevice *d, int 
region_num,
 
     /* XXX: add byte swapping apertures */
     cpu_register_physical_memory(addr, s->vram_size,
-                                s->cirrus_linear_io_addr);
+                                s->cirrus_linear_io_addr | s->vram_offset);
     cpu_register_physical_memory(addr + 0x1000000, 0x400000,
                                 s->cirrus_linear_bitblt_io_addr);
+
+    s->cirrus_lfb_mapped = 0;
+    s->cirrus_lfb_addr = addr;
+    s->cirrus_lfb_end = addr + VGA_RAM_SIZE;
 }
 
 static void cirrus_pci_mmio_map(PCIDevice *d, int region_num,
diff --git a/hw/vga.c b/hw/vga.c
index 9540db0..275ef0a 100644
--- a/hw/vga.c
+++ b/hw/vga.c
@@ -28,6 +28,7 @@
 #include "vga_int.h"
 #include "pixel_ops.h"
 #include "qemu-timer.h"
+#include "kvm.h"
 
 //#define DEBUG_VGA
 //#define DEBUG_VGA_MEM
@@ -1568,6 +1569,8 @@ static void vga_draw_graphic(VGAState *s, int full_update)
     uint32_t v, addr1, addr;
     vga_draw_line_func *vga_draw_line;
 
+    qemu_physical_sync_dirty_bitmap(s->vram_offset);
+
     full_update |= update_basic_params(s);
 
     s->get_resolution(s, &width, &height);
@@ -2102,6 +2105,9 @@ static void vga_map(PCIDevice *pci_dev, int region_num,
     } else {
         cpu_register_physical_memory(addr, s->vram_size, s->vram_offset);
     }
+
+    if (kvm_enabled())
+        kvm_log_start(addr, VGA_RAM_SIZE);
 }
 
 void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base,
diff --git a/hw/vga_int.h b/hw/vga_int.h
index 82a755e..7d3c011 100644
--- a/hw/vga_int.h
+++ b/hw/vga_int.h
@@ -100,7 +100,7 @@ typedef void (* vga_update_retrace_info_fn)(struct VGAState 
*s);
 
 #define VGA_STATE_COMMON                                                \
     uint8_t *vram_ptr;                                                  \
-    unsigned long vram_offset;                                          \
+    ram_addr_t vram_offset;                                             \
     unsigned int vram_size;                                             \
     unsigned long bios_offset;                                          \
     unsigned int bios_size;                                             \
@@ -129,6 +129,7 @@ typedef void (* vga_update_retrace_info_fn)(struct VGAState 
*s);
     int dac_8bit;                                                       \
     uint8_t palette[768];                                               \
     int32_t bank_offset;                                                \
+    uint32_t vga_io_memory;                                             \
     int (*get_bpp)(struct VGAState *s);                                 \
     void (*get_offsets)(struct VGAState *s,                             \
                         uint32_t *pline_offset,                         \
diff --git a/kvm-all.c b/kvm-all.c
index 4379071..4916865 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -31,7 +31,18 @@
     do { } while (0)
 #endif
 
-typedef struct kvm_userspace_memory_region KVMSlot;
+#define warning(fmt, ...) \
+    do { fprintf(stderr, "%s:%d" fmt, __func__, __LINE__, ## __VA_ARGS__); } 
while (0)
+
+typedef struct KVMSlot {
+        struct kvm_userspace_memory_region region;
+        int logging_count;
+        uint64_t *dirty_bitmap;
+} KVMSlot;
+
+#define kvm_uaddr(addr) ((addr) + (ram_addr_t)phys_ram_base)
+
+typedef struct kvm_dirty_log KVMDirty;
 
 int kvm_allowed = 0;
 
@@ -49,7 +60,7 @@ static KVMSlot *kvm_alloc_slot(KVMState *s)
     int i;
 
     for (i = 0; i < ARRAY_SIZE(s->slots); i++) {
-        if (s->slots[i].memory_size == 0)
+        if (s->slots[i].region.memory_size == 0)
             return &s->slots[i];
     }
 
@@ -63,8 +74,26 @@ static KVMSlot *kvm_lookup_slot(KVMState *s, 
target_phys_addr_t start_addr)
     for (i = 0; i < ARRAY_SIZE(s->slots); i++) {
         KVMSlot *mem = &s->slots[i];
 
-        if (start_addr >= mem->guest_phys_addr &&
-            start_addr < (mem->guest_phys_addr + mem->memory_size))
+        if (start_addr >= mem->region.guest_phys_addr &&
+            start_addr < (mem->region.guest_phys_addr + 
mem->region.memory_size))
+            return mem;
+    }
+
+    return NULL;
+}
+
+/* find the slot correspondence using userspace_addr as a key */
+static KVMSlot *kvm_lookup_slot_uaddr(KVMState *s, ram_addr_t addr)
+{
+    int i;
+    
+    uint64_t uaddr = (uint64_t)kvm_uaddr(addr);
+
+    for (i = 0; i < ARRAY_SIZE(s->slots); i++) {
+        KVMSlot *mem = &s->slots[i];
+
+        if (uaddr >= mem->region.userspace_addr &&
+            uaddr < (mem->region.userspace_addr + mem->region.memory_size))
             return mem;
     }
 
@@ -109,6 +138,133 @@ err:
     return ret;
 }
 
+/* 
+ *  * dirty pages logging control 
+ *   */
+static int kvm_dirty_pages_log_change(KVMSlot *mem,
+                                      unsigned flags,
+                                      unsigned mask)
+{
+    int r = -1;
+    KVMState *s = kvm_state;
+
+    flags = (mem->region.flags & ~mask) | flags;
+    if (flags == mem->region.flags)
+            return 0;
+
+    mem->region.flags = flags;
+
+    r = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem->region);
+    if (r == -1)
+        fprintf(stderr, "%s: %m\n", __FUNCTION__);
+
+    return r;
+}
+
+#define BMAP_SIZE(mem) ((mem->region.memory_size >> TARGET_PAGE_BITS)/ 
sizeof(*mem->dirty_bitmap))
+
+int kvm_log_start(target_phys_addr_t phys_addr, target_phys_addr_t len)
+{
+        KVMState *s = kvm_state;
+        KVMSlot *mem = kvm_lookup_slot(s, phys_addr);
+
+        dprintf("try log start %llx len %llx\n", phys_addr, len);
+
+        if (mem == NULL)  {
+                fprintf(stderr, "BUG: %s: invalid parameters\n", __func__);
+                return -EINVAL;
+        }
+
+        if (mem->logging_count++)
+                return 0;
+
+        mem->dirty_bitmap = malloc(BMAP_SIZE(mem));  
+
+        if (mem->dirty_bitmap == NULL)
+            return -ENOMEM;
+
+        dprintf("slot %d: enable logging (phys %llx, uaddr %llx)\n",
+                 mem->region.slot, mem->region.guest_phys_addr, 
mem->region.userspace_addr);
+
+        return kvm_dirty_pages_log_change(mem,
+                                          KVM_MEM_LOG_DIRTY_PAGES,
+                                          KVM_MEM_LOG_DIRTY_PAGES);
+}
+
+int kvm_log_stop(target_phys_addr_t phys_addr, target_phys_addr_t len)
+{
+
+        KVMState *s = kvm_state;
+        KVMSlot *mem = kvm_lookup_slot(s, phys_addr);
+
+        if (mem == NULL) {
+                fprintf(stderr, "BUG: %s: invalid parameters\n", __func__);
+                return -EINVAL;
+        }
+        dprintf("slot %d: disabling logging\n", mem->region.slot);
+
+        if (mem->logging_count--)
+                return 0;
+
+        return kvm_dirty_pages_log_change(mem,
+                                          0,
+                                          KVM_MEM_LOG_DIRTY_PAGES);
+}
+
+#define BITMAP_WORD(mem) (sizeof(*(mem)->dirty_bitmap) * 8)
+static inline int lookup_bitmap_phys(KVMSlot *mem, ram_addr_t addr)
+{
+    unsigned nr = (uint32_t)((uint64_t)kvm_uaddr(addr) - 
mem->region.userspace_addr) >> TARGET_PAGE_BITS;
+    unsigned word = nr / BITMAP_WORD(mem);
+    unsigned bit = nr % BITMAP_WORD(mem);
+    int ret;
+
+    ret = (mem->dirty_bitmap[word] >> bit) & 1;
+    return ret;
+}
+
+int kvm_physical_memory_get_dirty(ram_addr_t addr)
+{
+    KVMState *s = kvm_state;
+    KVMSlot *mem = kvm_lookup_slot_uaddr(s, addr);
+
+    if (mem == NULL) {
+            fprintf(stderr, "BUG: %s: invalid parameters\n", __func__);
+            return -EINVAL;
+    }
+
+    if (mem->dirty_bitmap == NULL) {
+        return 0;
+    }
+    return lookup_bitmap_phys(mem, addr);
+}
+
+void kvm_physical_sync_dirty_bitmap(ram_addr_t start_addr)
+{
+    KVMState *s = kvm_state;
+    KVMSlot *mem = kvm_lookup_slot_uaddr(s, start_addr);
+    
+    KVMDirty d;
+
+    dprintf("sync addr: %lx %llx %llx\n", start_addr, 
mem->region.guest_phys_addr, kvm_uaddr(start_addr));
+    if (mem == NULL) {
+            fprintf(stderr, "BUG: %s: invalid parameters\n", __func__);
+            return;
+    }
+
+    if (mem->dirty_bitmap == NULL)
+        warning("Asked to sync dirty bitmap for region with logging 
disabled\n");
+
+    d.slot = mem->region.slot;
+    d.dirty_bitmap = mem->dirty_bitmap;
+    dprintf("slot %d, phys_addr %llx, uaddr: %llx\n",
+            d.slot, mem->region.guest_phys_addr, mem->region.userspace_addr);
+
+    if (kvm_vm_ioctl(s, KVM_GET_DIRTY_LOG, &d) == -1)
+        warning("ioctl failed %d\n", errno);
+    
+}
+
 int kvm_init(int smp_cpus)
 {
     KVMState *s;
@@ -123,7 +279,7 @@ int kvm_init(int smp_cpus)
         return -ENOMEM;
 
     for (i = 0; i < ARRAY_SIZE(s->slots); i++)
-        s->slots[i].slot = i;
+        s->slots[i].region.slot = i;
 
     s->vmfd = -1;
     s->fd = open("/dev/kvm", O_RDWR);
@@ -317,15 +473,18 @@ void kvm_set_phys_mem(target_phys_addr_t start_addr,
 
     mem = kvm_lookup_slot(s, start_addr);
     if (mem) {
-        if (flags == IO_MEM_UNASSIGNED) {
-            mem->memory_size = 0;
-            mem->guest_phys_addr = start_addr;
-            mem->userspace_addr = 0;
-            mem->flags = 0;
-
-            kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, mem);
-        } else if (start_addr >= mem->guest_phys_addr &&
-                   (start_addr + size) <= (mem->guest_phys_addr + 
mem->memory_size))
+        if ((flags == IO_MEM_UNASSIGNED) || (flags >= TLB_MMIO)) {
+            if (mem->region.flags == KVM_MEM_LOG_DIRTY_PAGES)
+                return;
+            dprintf("deleting memory %llx with flags %d\n", 
mem->region.guest_phys_addr, mem->region.flags);
+            mem->region.memory_size = 0;
+            mem->region.guest_phys_addr = start_addr;
+            mem->region.userspace_addr = 0;
+            mem->region.flags = 0;
+
+            kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem->region);
+        } else if (start_addr >= mem->region.guest_phys_addr &&
+                   (start_addr + size) <= (mem->region.guest_phys_addr + 
mem->region.memory_size))
             return;
     }
 
@@ -334,12 +493,12 @@ void kvm_set_phys_mem(target_phys_addr_t start_addr,
         return;
 
     mem = kvm_alloc_slot(s);
-    mem->memory_size = size;
-    mem->guest_phys_addr = start_addr;
-    mem->userspace_addr = (unsigned long)(phys_ram_base + phys_offset);
-    mem->flags = 0;
+    mem->region.memory_size = size;
+    mem->region.guest_phys_addr = start_addr;
+    mem->region.userspace_addr = (unsigned long)(phys_ram_base + phys_offset);
+    mem->region.flags = 0;
 
-    kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, mem);
+    kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem->region);
     /* FIXME deal with errors */
 }
 
diff --git a/kvm.h b/kvm.h
index 37102b4..6e2d9d2 100644
--- a/kvm.h
+++ b/kvm.h
@@ -38,6 +38,9 @@ void kvm_set_phys_mem(target_phys_addr_t start_addr,
                       ram_addr_t size,
                       ram_addr_t phys_offset);
 
+int kvm_physical_memory_get_dirty(ram_addr_t addr);
+void kvm_physical_sync_dirty_bitmap(ram_addr_t start_addr);
+
 /* internal API */
 
 struct KVMState;

reply via email to

[Prev in Thread] Current Thread [Next in Thread]