From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1LA8u7-0005VT-LU for qemu-devel@nongnu.org; Tue, 09 Dec 2008 15:09:59 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1LA8u6-0005UF-Sm for qemu-devel@nongnu.org; Tue, 09 Dec 2008 15:09:59 -0500 Received: from [199.232.76.173] (port=55773 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1LA8u6-0005Tz-Mk for qemu-devel@nongnu.org; Tue, 09 Dec 2008 15:09:58 -0500 Received: from savannah.gnu.org ([199.232.41.3]:50205 helo=sv.gnu.org) by monty-python.gnu.org with esmtps (TLS-1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1LA8u6-0007fd-9k for qemu-devel@nongnu.org; Tue, 09 Dec 2008 15:09:58 -0500 Received: from cvs.savannah.gnu.org ([199.232.41.69]) by sv.gnu.org with esmtp (Exim 4.63) (envelope-from ) id 1LA8u5-0002nJ-Vq for qemu-devel@nongnu.org; Tue, 09 Dec 2008 20:09:58 +0000 Received: from aliguori by cvs.savannah.gnu.org with local (Exim 4.63) (envelope-from ) id 1LA8u5-0002nF-M1 for qemu-devel@nongnu.org; Tue, 09 Dec 2008 20:09:57 +0000 MIME-Version: 1.0 Errors-To: aliguori Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: Anthony Liguori Message-Id: Date: Tue, 09 Dec 2008 20:09:57 +0000 Subject: [Qemu-devel] [5961] KVM: Coalesced MMIO support Reply-To: qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Revision: 5961 http://svn.sv.gnu.org/viewvc/?view=rev&root=qemu&revision=5961 Author: aliguori Date: 2008-12-09 20:09:57 +0000 (Tue, 09 Dec 2008) Log Message: ----------- KVM: Coalesced MMIO support MMIO exits are more expensive in KVM or Xen than in QEMU because they involve, at least, privilege transitions. However, MMIO write operations can be effectively batched if those writes do not have side effects. Good examples of this include VGA pixel operations when in a planar mode. As it turns out, we can get a nice boost in other areas too. Laurent mentioned a 9.7% performance boost in iperf with the coalesced MMIO changes for the e1000 when he originally posted this work for KVM. Signed-off-by: Anthony Liguori Modified Paths: -------------- trunk/cpu-all.h trunk/exec.c trunk/hw/cirrus_vga.c trunk/hw/e1000.c trunk/hw/pci.c trunk/hw/vga.c trunk/kvm-all.c trunk/kvm.h Modified: trunk/cpu-all.h =================================================================== --- trunk/cpu-all.h 2008-12-09 19:59:09 UTC (rev 5960) +++ trunk/cpu-all.h 2008-12-09 20:09:57 UTC (rev 5961) @@ -973,6 +973,15 @@ void dump_exec_info(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)); +/* Coalesced MMIO regions are areas where write operations can be reordered. + * This usually implies that write operations are side-effect free. This allows + * batching which can make a major impact on performance when using + * virtualization. + */ +void qemu_register_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size); + +void qemu_unregister_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size); + /*******************************************/ /* host CPU ticks (if available) */ Modified: trunk/exec.c =================================================================== --- trunk/exec.c 2008-12-09 19:59:09 UTC (rev 5960) +++ trunk/exec.c 2008-12-09 20:09:57 UTC (rev 5961) @@ -2344,6 +2344,18 @@ return p->phys_offset; } +void qemu_register_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size) +{ + if (kvm_enabled()) + kvm_coalesce_mmio_region(addr, size); +} + +void qemu_unregister_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size) +{ + if (kvm_enabled()) + kvm_uncoalesce_mmio_region(addr, size); +} + /* XXX: better than nothing */ ram_addr_t qemu_ram_alloc(ram_addr_t size) { Modified: trunk/hw/cirrus_vga.c =================================================================== --- trunk/hw/cirrus_vga.c 2008-12-09 19:59:09 UTC (rev 5960) +++ trunk/hw/cirrus_vga.c 2008-12-09 20:09:57 UTC (rev 5961) @@ -3220,6 +3220,7 @@ cirrus_vga_mem_write, s); cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000, s->vga_io_memory); + qemu_register_coalesced_mmio(isa_mem_base + 0x000a0000, 0x20000); s->sr[0x06] = 0x0f; if (device_id == CIRRUS_ID_CLGD5446) { Modified: trunk/hw/e1000.c =================================================================== --- trunk/hw/e1000.c 2008-12-09 19:59:09 UTC (rev 5960) +++ trunk/hw/e1000.c 2008-12-09 20:09:57 UTC (rev 5961) @@ -1001,10 +1001,22 @@ uint32_t addr, uint32_t size, int type) { E1000State *d = (E1000State *)pci_dev; + int i; + const uint32_t excluded_regs[] = { + E1000_MDIC, E1000_ICR, E1000_ICS, E1000_IMS, + E1000_IMC, E1000_TCTL, E1000_TDT, PNPMMIO_SIZE + }; + DBGOUT(MMIO, "e1000_mmio_map addr=0x%08x 0x%08x\n", addr, size); cpu_register_physical_memory(addr, PNPMMIO_SIZE, d->mmio_index); + qemu_register_coalesced_mmio(addr, excluded_regs[0]); + + for (i = 0; excluded_regs[i] != PNPMMIO_SIZE; i++) + qemu_register_coalesced_mmio(addr + excluded_regs[i] + 4, + excluded_regs[i + 1] - + excluded_regs[i] - 4); } void Modified: trunk/hw/pci.c =================================================================== --- trunk/hw/pci.c 2008-12-09 19:59:09 UTC (rev 5960) +++ trunk/hw/pci.c 2008-12-09 20:09:57 UTC (rev 5961) @@ -279,6 +279,7 @@ cpu_register_physical_memory(pci_to_cpu_addr(r->addr), r->size, IO_MEM_UNASSIGNED); + qemu_unregister_coalesced_mmio(r->addr, r->size); } } r->addr = new_addr; Modified: trunk/hw/vga.c =================================================================== --- trunk/hw/vga.c 2008-12-09 19:59:09 UTC (rev 5960) +++ trunk/hw/vga.c 2008-12-09 20:09:57 UTC (rev 5961) @@ -2256,6 +2256,7 @@ vga_io_memory = cpu_register_io_memory(0, vga_mem_read, vga_mem_write, s); cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000, vga_io_memory); + qemu_register_coalesced_mmio(isa_mem_base + 0x000a0000, 0x20000); } /* Memory mapped interface */ @@ -2330,6 +2331,7 @@ cpu_register_physical_memory(ctrl_base, 0x100000, s_ioport_ctrl); s->bank_offset = 0; cpu_register_physical_memory(vram_base + 0x000a0000, 0x20000, vga_io_memory); + qemu_register_coalesced_mmio(vram_base + 0x000a0000, 0x20000); } int isa_vga_init(DisplayState *ds, uint8_t *vga_ram_base, Modified: trunk/kvm-all.c =================================================================== --- trunk/kvm-all.c 2008-12-09 19:59:09 UTC (rev 5960) +++ trunk/kvm-all.c 2008-12-09 20:09:57 UTC (rev 5961) @@ -24,6 +24,9 @@ #include "sysemu.h" #include "kvm.h" +/* KVM uses PAGE_SIZE in it's definition of COALESCED_MMIO_MAX */ +#define PAGE_SIZE TARGET_PAGE_SIZE + //#define DEBUG_KVM #ifdef DEBUG_KVM @@ -52,6 +55,7 @@ KVMSlot slots[32]; int fd; int vmfd; + int coalesced_mmio; }; static KVMState *kvm_state; @@ -228,6 +232,44 @@ qemu_free(d.dirty_bitmap); } +int kvm_coalesce_mmio_region(target_phys_addr_t start, ram_addr_t size) +{ + int ret = -ENOSYS; +#ifdef KVM_CAP_COALESCED_MMIO + KVMState *s = kvm_state; + + if (s->coalesced_mmio) { + struct kvm_coalesced_mmio_zone zone; + + zone.addr = start; + zone.size = size; + + ret = kvm_vm_ioctl(s, KVM_REGISTER_COALESCED_MMIO, &zone); + } +#endif + + return ret; +} + +int kvm_uncoalesce_mmio_region(target_phys_addr_t start, ram_addr_t size) +{ + int ret = -ENOSYS; +#ifdef KVM_CAP_COALESCED_MMIO + KVMState *s = kvm_state; + + if (s->coalesced_mmio) { + struct kvm_coalesced_mmio_zone zone; + + zone.addr = start; + zone.size = size; + + ret = kvm_vm_ioctl(s, KVM_UNREGISTER_COALESCED_MMIO, &zone); + } +#endif + + return ret; +} + int kvm_init(int smp_cpus) { KVMState *s; @@ -298,6 +340,13 @@ goto err; } + s->coalesced_mmio = 0; +#ifdef KVM_CAP_COALESCED_MMIO + ret = kvm_ioctl(s, KVM_CHECK_EXTENSION, KVM_CAP_COALESCED_MMIO); + if (ret > 0) + s->coalesced_mmio = ret; +#endif + ret = kvm_arch_init(s, smp_cpus); if (ret < 0) goto err; @@ -357,6 +406,27 @@ return 1; } +static void kvm_run_coalesced_mmio(CPUState *env, struct kvm_run *run) +{ +#ifdef KVM_CAP_COALESCED_MMIO + KVMState *s = kvm_state; + if (s->coalesced_mmio) { + struct kvm_coalesced_mmio_ring *ring; + + ring = (void *)run + (s->coalesced_mmio * TARGET_PAGE_SIZE); + while (ring->first != ring->last) { + struct kvm_coalesced_mmio *ent; + + ent = &ring->coalesced_mmio[ring->first]; + + cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len); + /* FIXME smp_wmb() */ + ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX; + } + } +#endif +} + int kvm_cpu_exec(CPUState *env) { struct kvm_run *run = env->kvm_run; @@ -387,6 +457,8 @@ abort(); } + kvm_run_coalesced_mmio(env, run); + ret = 0; /* exit loop */ switch (run->exit_reason) { case KVM_EXIT_IO: Modified: trunk/kvm.h =================================================================== --- trunk/kvm.h 2008-12-09 19:59:09 UTC (rev 5960) +++ trunk/kvm.h 2008-12-09 20:09:57 UTC (rev 5961) @@ -45,6 +45,9 @@ int kvm_has_sync_mmu(void); +int kvm_coalesce_mmio_region(target_phys_addr_t start, ram_addr_t size); +int kvm_uncoalesce_mmio_region(target_phys_addr_t start, ram_addr_t size); + /* internal API */ struct KVMState;