From 2bec46dc97571a3c34b18fe4ca198e7bfbdca41f Mon Sep 17 00:00:00 2001 From: aliguori Date: Mon, 24 Nov 2008 20:21:41 +0000 Subject: [PATCH] vga optimization (Glauber Costa) Hypervisors like KVM perform badly while doing mmio on a loop, because it'll generate an exit on each access. This is the case with VGA, which results in very bad performance. In this patch, we map the linear frame buffer as RAM, make sure it has dirty region tracking enabled, and then just let the region to be written. Cleanups suggestions by: Stefano Stabellini Signed-off-by: Glauber Costa Signed-off-by: Anthony Liguori git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5793 c046a42c-6fe2-441c-8c8c-71466251a162 --- cpu-all.h | 2 ++ exec.c | 6 +++++ hw/cirrus_vga.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/vga.c | 47 +++++++++++++++++++++++++++++++++++ hw/vga_int.h | 9 +++++++ 5 files changed, 129 insertions(+) diff --git a/cpu-all.h b/cpu-all.h index aa4b770fe2..5fa1e69372 100644 --- a/cpu-all.h +++ b/cpu-all.h @@ -948,6 +948,8 @@ int cpu_physical_memory_set_dirty_tracking(int enable); int cpu_physical_memory_get_dirty_tracking(void); +void cpu_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, target_phys_addr_t end_addr); + void dump_exec_info(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)); diff --git a/exec.c b/exec.c index 4d39eaaf20..43a1b78f46 100644 --- a/exec.c +++ b/exec.c @@ -1887,6 +1887,12 @@ int cpu_physical_memory_get_dirty_tracking(void) return in_migration; } +void cpu_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, target_phys_addr_t end_addr) +{ + if (kvm_enabled()) + kvm_physical_sync_dirty_bitmap(start_addr, end_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 01f3b6a631..e0cf458d76 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: @@ -1228,6 +1229,12 @@ static void cirrus_update_bank_ptr(CirrusVGAState * s, unsigned bank_index) } if (limit > 0) { + /* Thinking about changing bank base? First, drop the dirty bitmap information + * on the current location, otherwise we lose this pointer forever */ + if (s->lfb_vram_mapped) { + target_phys_addr_t base_addr = isa_mem_base + 0xa0000 + bank_index * 0x8000; + cpu_physical_sync_dirty_bitmap(base_addr, base_addr + 0x8000); + } s->cirrus_bank_base[bank_index] = offset; s->cirrus_bank_limit[bank_index] = limit; } else { @@ -1356,6 +1363,7 @@ cirrus_hook_write_sr(CirrusVGAState * s, unsigned reg_index, int reg_value) s->hw_cursor_y = (reg_value << 3) | (reg_index >> 5); break; case 0x07: // Extended Sequencer Mode + cirrus_update_memory_access(s); case 0x08: // EEPROM Control case 0x09: // Scratch Register 0 case 0x0a: // Scratch Register 1 @@ -1528,6 +1536,7 @@ cirrus_hook_write_gr(CirrusVGAState * s, unsigned reg_index, int reg_value) s->gr[reg_index] = reg_value; cirrus_update_bank_ptr(s, 0); cirrus_update_bank_ptr(s, 1); + cirrus_update_memory_access(s); break; case 0x0B: s->gr[reg_index] = reg_value; @@ -2618,6 +2627,52 @@ static CPUWriteMemoryFunc *cirrus_linear_bitblt_write[3] = { cirrus_linear_bitblt_writel, }; +static void map_linear_vram(CirrusVGAState *s) +{ + + if (!s->map_addr && s->lfb_addr && s->lfb_end) { + s->map_addr = s->lfb_addr; + s->map_end = s->lfb_end; + cpu_register_physical_memory(s->map_addr, s->map_end - s->map_addr, s->vram_offset); + vga_dirty_log_start((VGAState *)s); + } + + if (!s->map_addr) + return; + + s->lfb_vram_mapped = 0; + + 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, 0x8000, + (s->vram_offset + s->cirrus_bank_base[0]) | IO_MEM_RAM); + cpu_register_physical_memory(isa_mem_base + 0xa8000, 0x8000, + (s->vram_offset + s->cirrus_bank_base[1]) | IO_MEM_RAM); + + s->lfb_vram_mapped = 1; + vga_dirty_log_start((VGAState *)s); + } + else { + cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x8000, s->vga_io_memory); + cpu_register_physical_memory(isa_mem_base + 0xa8000, 0x8000, s->vga_io_memory); + } + +} + +static void unmap_linear_vram(CirrusVGAState *s) +{ + if (s->map_addr && s->lfb_addr && s->lfb_end) { + vga_dirty_log_stop((VGAState *)s); + s->map_addr = s->map_end = 0; + } + + cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x20000, + s->vga_io_memory); +} + /* Compute the memory access functions */ static void cirrus_update_memory_access(CirrusVGAState *s) { @@ -2636,11 +2691,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; @@ -3102,6 +3159,7 @@ static int cirrus_vga_load(QEMUFile *f, void *opaque, int version_id) qemu_get_be32s(f, &s->hw_cursor_x); qemu_get_be32s(f, &s->hw_cursor_y); + cirrus_update_memory_access(s); /* force refresh */ s->graphic_mode = -1; cirrus_update_bank_ptr(s, 0); @@ -3261,6 +3319,13 @@ static void cirrus_pci_lfb_map(PCIDevice *d, int region_num, s->cirrus_linear_io_addr); cpu_register_physical_memory(addr + 0x1000000, 0x400000, s->cirrus_linear_bitblt_io_addr); + + s->map_addr = s->map_end = 0; + s->lfb_addr = addr & TARGET_PAGE_MASK; + s->lfb_end = ((addr + VGA_RAM_SIZE) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; + /* account for overflow */ + if (s->lfb_end < addr + VGA_RAM_SIZE) + s->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 d2b995d5b2..492d3f9ace 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 @@ -1243,6 +1244,8 @@ static void vga_draw_text(VGAState *s, int full_update) vga_draw_glyph8_func *vga_draw_glyph8; vga_draw_glyph9_func *vga_draw_glyph9; + vga_dirty_log_stop(s); + full_update |= update_palette16(s); palette = s->last_palette; @@ -1556,6 +1559,18 @@ void vga_invalidate_scanlines(VGAState *s, int y1, int y2) } } +static void vga_sync_dirty_bitmap(VGAState *s) +{ + if (s->map_addr) + cpu_physical_sync_dirty_bitmap(s->map_addr, s->map_end); + + if (s->lfb_vram_mapped) { + cpu_physical_sync_dirty_bitmap(isa_mem_base + 0xa0000, 0xa8000); + cpu_physical_sync_dirty_bitmap(isa_mem_base + 0xa8000, 0xb0000); + } + vga_dirty_log_start(s); +} + /* * graphic modes */ @@ -1570,6 +1585,9 @@ static void vga_draw_graphic(VGAState *s, int full_update) full_update |= update_basic_params(s); + if (!full_update) + vga_sync_dirty_bitmap(s); + s->get_resolution(s, &width, &height); disp_width = width; @@ -1743,6 +1761,8 @@ static void vga_draw_blank(VGAState *s, int full_update) return; if (s->last_scr_width <= 0 || s->last_scr_height <= 0) return; + vga_dirty_log_stop(s); + if (ds_get_bits_per_pixel(s->ds) == 8) val = s->rgb_to_pixel(0, 0, 0); else @@ -2092,6 +2112,28 @@ typedef struct PCIVGAState { VGAState vga_state; } PCIVGAState; +void vga_dirty_log_start(VGAState *s) +{ + if (kvm_enabled() && s->map_addr) + kvm_log_start(s->map_addr, s->map_end - s->map_addr); + + if (kvm_enabled() && s->lfb_vram_mapped) { + kvm_log_start(isa_mem_base + 0xa0000, 0x8000); + kvm_log_start(isa_mem_base + 0xa8000, 0x8000); + } +} + +void vga_dirty_log_stop(VGAState *s) +{ + if (kvm_enabled() && s->map_addr) + kvm_log_stop(s->map_addr, s->map_end - s->map_addr); + + if (kvm_enabled() && s->lfb_vram_mapped) { + kvm_log_stop(isa_mem_base + 0xa0000, 0x8000); + kvm_log_stop(isa_mem_base + 0xa8000, 0x8000); + } +} + static void vga_map(PCIDevice *pci_dev, int region_num, uint32_t addr, uint32_t size, int type) { @@ -2102,6 +2144,11 @@ static void vga_map(PCIDevice *pci_dev, int region_num, } else { cpu_register_physical_memory(addr, s->vram_size, s->vram_offset); } + + s->map_addr = addr; + s->map_end = addr + VGA_RAM_SIZE; + + vga_dirty_log_start(s); } 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 4fa619accd..b360d3e013 100644 --- a/hw/vga_int.h +++ b/hw/vga_int.h @@ -102,6 +102,11 @@ typedef void (* vga_update_retrace_info_fn)(struct VGAState *s); uint8_t *vram_ptr; \ ram_addr_t vram_offset; \ unsigned int vram_size; \ + uint32_t lfb_addr; \ + uint32_t lfb_end; \ + uint32_t map_addr; \ + uint32_t map_end; \ + uint32_t lfb_vram_mapped; /* whether 0xa0000 is mapped as ram */ \ unsigned long bios_offset; \ unsigned int bios_size; \ target_phys_addr_t base_ctrl; \ @@ -189,6 +194,10 @@ static inline int c6_to_8(int v) void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base, ram_addr_t vga_ram_offset, int vga_ram_size); void vga_init(VGAState *s); + +void vga_dirty_log_start(VGAState *s); +void vga_dirty_log_stop(VGAState *s); + uint32_t vga_mem_readb(void *opaque, target_phys_addr_t addr); void vga_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val); void vga_invalidate_scanlines(VGAState *s, int y1, int y2);