From a2e778d77ff20d4cf600d45b91f4b181cd550399 Mon Sep 17 00:00:00 2001 Message-Id: In-Reply-To: <4f8efce613a639a3c1e3022c521d6c70b7154de8.1357726992.git.minovotn@redhat.com> References: <4f8efce613a639a3c1e3022c521d6c70b7154de8.1357726992.git.minovotn@redhat.com> From: Stefan Hajnoczi Date: Wed, 2 Jan 2013 15:02:27 +0100 Subject: [PATCH 04/16] dataplane: add host memory mapping code RH-Author: Stefan Hajnoczi Message-id: <1357138959-1918-5-git-send-email-stefanha@redhat.com> Patchwork-id: 45517 O-Subject: [RHEL6.4 qemu-kvm PATCH v5 04/16] dataplane: add host memory mapping code Bugzilla: 877836 RH-Acked-by: Laszlo Ersek RH-Acked-by: Michael S. Tsirkin RH-Acked-by: Paolo Bonzini The data plane thread needs to map guest physical addresses to host pointers. Normally this is done with cpu_physical_memory_map() but the function assumes the global mutex is held. The data plane thread does not touch the global mutex and therefore needs a thread-safe memory mapping mechanism. Hostmem registers a CPUPhysMemoryClient similar to how vhost collects and pushes memory region information into the kernel. There is a fine-grained lock on the regions list which is held during lookup and when updating the regions list. When the physical memory map changes the .set_memory() callback is invoked. It updates the regions list in-place by removing, merging, splitting, or adding regions. This patch implements Hostmem for RHEL differently from upstream. Since MemoryListener and the memory API is not available in RHEL we use the vhost's CPUPhysMemoryClient approach. Note that the VGA region is skipped since dirty logging is not implemented by Hostmem and we don't expect to DMA into VGA memory. Signed-off-by: Stefan Hajnoczi --- Makefile.target | 3 +- configure | 1 + hw/dataplane/hostmem.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/dataplane/hostmem.h | 41 ++++++++++++++++ 4 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 hw/dataplane/hostmem.c create mode 100644 hw/dataplane/hostmem.h Signed-off-by: Michal Novotny --- Makefile.target | 3 +- configure | 1 + hw/dataplane/hostmem.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/dataplane/hostmem.h | 41 ++++++++++++++++ 4 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 hw/dataplane/hostmem.c create mode 100644 hw/dataplane/hostmem.h diff --git a/Makefile.target b/Makefile.target index a094bbc..3390b97 100644 --- a/Makefile.target +++ b/Makefile.target @@ -209,6 +209,7 @@ obj-y += virtio-scsi.o event_notifier.o obj-y += vhost_net.o obj-$(CONFIG_VHOST_NET) += vhost.o obj-$(CONFIG_KVM) += kvm.o kvm-all.o hyperv.o +obj-$(CONFIG_VIRTIO_BLK_DATA_PLANE) += dataplane/hostmem.o # MSI-X depends on kvm for interrupt injection, # so moved it from Makefile.hw to Makefile.target for now obj-y += msix.o @@ -423,7 +424,7 @@ qemu-monitor.h: $(SRC_PATH)/qemu-monitor.hx clean: rm -f *.o *.a *~ $(PROGS) nwfpe/*.o fpu/*.o - rm -f *.d */*.d tcg/*.o ide/*.o + rm -f *.d */*.d tcg/*.o ide/*.o dataplane/*.o rm -f qemu-options.def qemu-monitor.h gdbstub-xml.c ifdef CONFIG_SYSTEMTAP_TRACE rm -f *.stp diff --git a/configure b/configure index f8c15bb..a82cfd5 100755 --- a/configure +++ b/configure @@ -2756,6 +2756,7 @@ mkdir -p $target_dir mkdir -p $target_dir/fpu mkdir -p $target_dir/tcg mkdir -p $target_dir/ide +mkdir -p $target_dir/dataplane if test "$target" = "arm-linux-user" -o "$target" = "armeb-linux-user" -o "$target" = "arm-bsd-user" -o "$target" = "armeb-bsd-user" ; then mkdir -p $target_dir/nwfpe fi diff --git a/hw/dataplane/hostmem.c b/hw/dataplane/hostmem.c new file mode 100644 index 0000000..844db97 --- /dev/null +++ b/hw/dataplane/hostmem.c @@ -0,0 +1,124 @@ +/* + * Thread-safe guest to host memory mapping + * + * Copyright 2012 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include +#include "hw/pci.h" /* for range_*() helper functions */ +#include "hw/vhost.h" +#include "hostmem.h" + +/** + * Map guest physical address to host pointer + */ +void *hostmem_lookup(HostMem *hostmem, uint64_t phys, uint64_t len, + bool is_write) +{ + struct vhost_memory_region *found = NULL; + void *host_addr = NULL; + uint64_t offset_within_region; + unsigned int i; + + is_write = is_write; /*r/w information is currently not tracked */ + + qemu_mutex_lock(&hostmem->mem_lock); + for (i = 0; i < hostmem->mem->nregions; i++) { + struct vhost_memory_region *region = &hostmem->mem->regions[i]; + + if (range_covers_byte(region->guest_phys_addr, + region->memory_size, + phys)) { + found = region; + break; + } + } + if (!found) { + goto out; + } + offset_within_region = phys - found->guest_phys_addr; + if (len <= found->memory_size - offset_within_region) { + host_addr = (void*)(uintptr_t)(found->userspace_addr + + offset_within_region); + } +out: + qemu_mutex_unlock(&hostmem->mem_lock); + + return host_addr; +} + +static void hostmem_client_set_memory(CPUPhysMemoryClient *client, + target_phys_addr_t start_addr, + ram_addr_t size, + ram_addr_t phys_offset) +{ + HostMem *hostmem = container_of(client, HostMem, client); + ram_addr_t flags = phys_offset & ~TARGET_PAGE_MASK; + size_t s = offsetof(struct vhost_memory, regions) + + (hostmem->mem->nregions + 1) * sizeof hostmem->mem->regions[0]; + + /* TODO: this is a hack. + * At least one vga card (cirrus) changes the gpa to hva + * memory maps on data path, which slows us down. + * Since we should never need to DMA into VGA memory + * anyway, lets just skip these regions. */ + if (ranges_overlap(start_addr, size, 0xa0000, 0x10000)) { + return; + } + + qemu_mutex_lock(&hostmem->mem_lock); + + hostmem->mem = qemu_realloc(hostmem->mem, s); + + assert(size); + + vhost_mem_unassign_memory(hostmem->mem, start_addr, size); + if (flags == IO_MEM_RAM) { + /* Add given mapping, merging adjacent regions if any */ + vhost_mem_assign_memory(hostmem->mem, start_addr, size, + (uintptr_t)qemu_get_ram_ptr(phys_offset)); + } + + qemu_mutex_unlock(&hostmem->mem_lock); +} + +static int hostmem_client_sync_dirty_bitmap(struct CPUPhysMemoryClient *client, + target_phys_addr_t start_addr, + target_phys_addr_t end_addr) +{ + return 0; +} + +static int hostmem_client_migration_log(struct CPUPhysMemoryClient *client, + int enable) +{ + return 0; +} + +void hostmem_init(HostMem *hostmem) +{ + memset(hostmem, 0, sizeof(*hostmem)); + + qemu_mutex_init(&hostmem->mem_lock); + + hostmem->mem = qemu_mallocz(sizeof(*hostmem->mem)); + + hostmem->client.set_memory = hostmem_client_set_memory; + hostmem->client.sync_dirty_bitmap = hostmem_client_sync_dirty_bitmap; + hostmem->client.migration_log = hostmem_client_migration_log; + cpu_register_phys_memory_client(&hostmem->client); +} + +void hostmem_finalize(HostMem *hostmem) +{ + cpu_unregister_phys_memory_client(&hostmem->client); + qemu_mutex_destroy(&hostmem->mem_lock); + qemu_free(hostmem->mem); +} diff --git a/hw/dataplane/hostmem.h b/hw/dataplane/hostmem.h new file mode 100644 index 0000000..6a1aa05 --- /dev/null +++ b/hw/dataplane/hostmem.h @@ -0,0 +1,41 @@ +/* + * Thread-safe guest to host memory mapping + * + * Copyright 2012 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef HOSTMEM_H +#define HOSTMEM_H + +#include "hw/hw.h" +#include "qemu-thread.h" + +struct vhost_memory; +typedef struct { + CPUPhysMemoryClient client; + QemuMutex mem_lock; + struct vhost_memory *mem; +} HostMem; + +void hostmem_init(HostMem *hostmem); +void hostmem_finalize(HostMem *hostmem); + +/** + * Map a guest physical address to a pointer + * + * Note that there is no map/unmap mechanism here. The caller must ensure that + * mapped memory is no longer used across events like hot memory unplug. This + * can be done with other mechanisms like bdrv_drain_all() that quiesce + * in-flight I/O. + */ +void *hostmem_lookup(HostMem *hostmem, uint64_t phys, uint64_t len, + bool is_write); + +#endif /* HOSTMEM_H */ -- 1.7.11.7