提交 f65ed4c1 编写于 作者: A aliguori

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: NAnthony Liguori <aliguori@us.ibm.com>



git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5961 c046a42c-6fe2-441c-8c8c-71466251a162
上级 d85dc283
......@@ -973,6 +973,15 @@ void cpu_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, target_phys_a
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) */
......
......@@ -2344,6 +2344,18 @@ ram_addr_t cpu_get_physical_page_desc(target_phys_addr_t addr)
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)
{
......
......@@ -3220,6 +3220,7 @@ static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci)
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) {
......
......@@ -1001,10 +1001,22 @@ e1000_mmio_map(PCIDevice *pci_dev, int region_num,
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
......
......@@ -279,6 +279,7 @@ static void pci_update_mappings(PCIDevice *d)
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;
......
......@@ -2256,6 +2256,7 @@ void vga_init(VGAState *s)
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 @@ static void vga_mm_init(VGAState *s, target_phys_addr_t vram_base,
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,
......
......@@ -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 @@ struct KVMState
KVMSlot slots[32];
int fd;
int vmfd;
int coalesced_mmio;
};
static KVMState *kvm_state;
......@@ -228,6 +232,44 @@ out:
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 @@ int kvm_init(int smp_cpus)
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 @@ static int kvm_handle_io(CPUState *env, uint16_t port, void *data,
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 @@ int kvm_cpu_exec(CPUState *env)
abort();
}
kvm_run_coalesced_mmio(env, run);
ret = 0; /* exit loop */
switch (run->exit_reason) {
case KVM_EXIT_IO:
......
......@@ -45,6 +45,9 @@ int kvm_log_stop(target_phys_addr_t phys_addr, target_phys_addr_t len);
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;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册