:p
atchew
Login
From: "Edgar E. Iglesias" <edgar.iglesias@amd.com> This adds a mapcache option to the PVH machines allowing users to disable the mapcache. To remain backwards compatible, the option defaults to "on". When disabled, all guest memory is mapped at startup rather than on demand. Starting QEMU may take longer in this mode, but runtime overhead is reduced and it also enables virtio vhost backends to work. As a trade-off, grants are unavailable without the mapcache and guests with large amounts of RAM can take quite a while to start. Cheers, Edgar Edgar E. Iglesias (5): xen: mapcache: Assert mapcache existance xen: mapcache: Add function to check if the mapcache is enabled physmem: xen: Conditionalize use of the mapcache hw/xen-hvm: Add a mapcache arg to xen_register_ioreq() hw/xen: xenpvh: Add prop to enable/disable the mapcache hw/i386/xen/xen-hvm.c | 2 +- hw/xen/xen-hvm-common.c | 18 +++-- hw/xen/xen-mapcache.c | 16 ++++ hw/xen/xen-pvh-common.c | 134 +++++++++++++++++++++++++++++--- hw/xen/xen_stubs.c | 5 ++ include/hw/xen/xen-hvm-common.h | 3 +- include/hw/xen/xen-pvh-common.h | 2 + include/system/xen-mapcache.h | 1 + system/physmem.c | 13 ++-- 9 files changed, 169 insertions(+), 25 deletions(-) -- 2.43.0
From: "Edgar E. Iglesias" <edgar.iglesias@amd.com> Assert that the mapcache was created prior to being used. Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com> --- hw/xen/xen-mapcache.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index XXXXXXX..XXXXXXX 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -XXX,XX +XXX,XX @@ uint8_t *xen_map_cache(MemoryRegion *mr, MapCache *mc = mapcache; uint8_t *p; + assert(mapcache); + if (grant) { mc = is_write ? mapcache_grants_rw : mapcache_grants_ro; } @@ -XXX,XX +XXX,XX @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr) { ram_addr_t addr; + assert(mapcache); + addr = xen_ram_addr_from_mapcache_single(mapcache, ptr); if (addr == RAM_ADDR_INVALID) { addr = xen_ram_addr_from_mapcache_single(mapcache_grants_ro, ptr); @@ -XXX,XX +XXX,XX @@ static void xen_invalidate_map_cache_entry_bh(void *opaque) void coroutine_mixed_fn xen_invalidate_map_cache_entry(uint8_t *buffer) { + assert(mapcache); + if (qemu_in_coroutine()) { XenMapCacheData data = { .co = qemu_coroutine_self(), @@ -XXX,XX +XXX,XX @@ static void xen_invalidate_map_cache_single(MapCache *mc) void xen_invalidate_map_cache(void) { + assert(mapcache); + /* Flush pending AIO before destroying the mapcache */ bdrv_drain_all(); @@ -XXX,XX +XXX,XX @@ uint8_t *xen_replace_cache_entry(hwaddr old_phys_addr, { uint8_t *p; + assert(mapcache); + mapcache_lock(mapcache); p = xen_replace_cache_entry_unlocked(mapcache, old_phys_addr, new_phys_addr, size); -- 2.43.0
From: "Edgar E. Iglesias" <edgar.iglesias@amd.com> Add function to check if the mapcache is enabled. No functional change. Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com> --- hw/xen/xen-mapcache.c | 6 ++++++ hw/xen/xen_stubs.c | 5 +++++ include/system/xen-mapcache.h | 1 + 3 files changed, 12 insertions(+) diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index XXXXXXX..XXXXXXX 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -XXX,XX +XXX,XX @@ static MapCache *mapcache_grants_ro; static MapCache *mapcache_grants_rw; static xengnttab_handle *xen_region_gnttabdev; +bool xen_map_cache_enabled(void) +{ + /* Map cache enabled implies xen_enabled(). */ + return xen_enabled() && mapcache; +} + static inline void mapcache_lock(MapCache *mc) { qemu_mutex_lock(&mc->lock); diff --git a/hw/xen/xen_stubs.c b/hw/xen/xen_stubs.c index XXXXXXX..XXXXXXX 100644 --- a/hw/xen/xen_stubs.c +++ b/hw/xen/xen_stubs.c @@ -XXX,XX +XXX,XX @@ bool xen_mr_is_memory(MemoryRegion *mr) g_assert_not_reached(); } +bool xen_map_cache_enabled(void) +{ + return false; +} + void xen_invalidate_map_cache_entry(uint8_t *buffer) { g_assert_not_reached(); diff --git a/include/system/xen-mapcache.h b/include/system/xen-mapcache.h index XXXXXXX..XXXXXXX 100644 --- a/include/system/xen-mapcache.h +++ b/include/system/xen-mapcache.h @@ -XXX,XX +XXX,XX @@ typedef hwaddr (*phys_offset_to_gaddr_t)(hwaddr phys_offset, ram_addr_t size); +bool xen_map_cache_enabled(void); void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque); uint8_t *xen_map_cache(MemoryRegion *mr, hwaddr phys_addr, hwaddr size, -- 2.43.0
From: "Edgar E. Iglesias" <edgar.iglesias@amd.com> Conditionalize use of the mapcache. This is in preparation to optionally disable the mapcache at runtime. Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com> --- system/physmem.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index XXXXXXX..XXXXXXX 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -XXX,XX +XXX,XX @@ MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat, is_write, true, &as, attrs); mr = section.mr; - if (xen_enabled() && memory_access_is_direct(mr, is_write, attrs)) { + if (xen_map_cache_enabled() && + memory_access_is_direct(mr, is_write, attrs)) { hwaddr page = ((addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE) - addr; *plen = MIN(page, *plen); } @@ -XXX,XX +XXX,XX @@ static void reclaim_ramblock(RAMBlock *block) { if (block->flags & RAM_PREALLOC) { ; - } else if (xen_enabled()) { + } else if (xen_map_cache_enabled()) { xen_invalidate_map_cache_entry(block->host); #if !defined(_WIN32) && !defined(EMSCRIPTEN) } else if (block->fd >= 0) { @@ -XXX,XX +XXX,XX @@ static void *qemu_ram_ptr_length(RAMBlock *block, ram_addr_t addr, len = *size; } - if (xen_enabled() && block->host == NULL) { + if (xen_map_cache_enabled() && block->host == NULL) { /* We need to check if the requested address is in the RAM * because we don't want to map the entire memory in QEMU. * In that case just map the requested area. @@ -XXX,XX +XXX,XX @@ RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset, RAMBlock *block; uint8_t *host = ptr; - if (xen_enabled()) { + if (xen_map_cache_enabled()) { ram_addr_t ram_addr; RCU_READ_LOCK_GUARD(); ram_addr = xen_ram_addr_from_mapcache(ptr); @@ -XXX,XX +XXX,XX @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, if (is_write) { invalidate_and_set_dirty(mr, addr1, access_len); } - if (xen_enabled()) { + if (xen_map_cache_enabled()) { xen_invalidate_map_cache_entry(buffer); } memory_region_unref(mr); @@ -XXX,XX +XXX,XX @@ void address_space_cache_destroy(MemoryRegionCache *cache) return; } - if (xen_enabled()) { + if (xen_map_cache_enabled()) { xen_invalidate_map_cache_entry(cache->ptr); } memory_region_unref(cache->mrs.mr); -- 2.43.0
From: "Edgar E. Iglesias" <edgar.iglesias@amd.com> Add a mapcache argument to xen_register_ioreq() allowing the caller to optionally disable the mapcache. All callers still call it with mapcache = true so there's no functional change yet. Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com> --- hw/i386/xen/xen-hvm.c | 2 +- hw/xen/xen-hvm-common.c | 18 ++++++++++++------ hw/xen/xen-pvh-common.c | 3 ++- include/hw/xen/xen-hvm-common.h | 3 ++- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -XXX,XX +XXX,XX @@ void xen_hvm_init_pc(PCMachineState *pcms, MemoryRegion **ram_memory) xen_register_ioreq(state, max_cpus, HVM_IOREQSRV_BUFIOREQ_ATOMIC, - &xen_memory_listener); + &xen_memory_listener, true); xen_is_stubdomain = xen_check_stubdomain(state->xenstore); diff --git a/hw/xen/xen-hvm-common.c b/hw/xen/xen-hvm-common.c index XXXXXXX..XXXXXXX 100644 --- a/hw/xen/xen-hvm-common.c +++ b/hw/xen/xen-hvm-common.c @@ -XXX,XX +XXX,XX @@ static void handle_ioreq(XenIOState *state, ioreq_t *req) case IOREQ_TYPE_TIMEOFFSET: break; case IOREQ_TYPE_INVALIDATE: - xen_invalidate_map_cache(); + if (xen_map_cache_enabled()) { + xen_invalidate_map_cache(); + } break; case IOREQ_TYPE_PCI_CONFIG: cpu_ioreq_config(state, req); @@ -XXX,XX +XXX,XX @@ void xen_shutdown_fatal_error(const char *fmt, ...) static void xen_do_ioreq_register(XenIOState *state, unsigned int max_cpus, - const MemoryListener *xen_memory_listener) + const MemoryListener *xen_memory_listener, + bool mapcache) { int i, rc; @@ -XXX,XX +XXX,XX @@ static void xen_do_ioreq_register(XenIOState *state, state->bufioreq_local_port = rc; } /* Init RAM management */ + if (mapcache) { #ifdef XEN_COMPAT_PHYSMAP - xen_map_cache_init(xen_phys_offset_to_gaddr, state); + xen_map_cache_init(xen_phys_offset_to_gaddr, state); #else - xen_map_cache_init(NULL, state); + xen_map_cache_init(NULL, state); #endif + } qemu_add_vm_change_state_handler(xen_hvm_change_state_handler, state); @@ -XXX,XX +XXX,XX @@ err: void xen_register_ioreq(XenIOState *state, unsigned int max_cpus, uint8_t handle_bufioreq, - const MemoryListener *xen_memory_listener) + const MemoryListener *xen_memory_listener, + bool mapcache) { int rc; @@ -XXX,XX +XXX,XX @@ void xen_register_ioreq(XenIOState *state, unsigned int max_cpus, state->has_bufioreq = handle_bufioreq != HVM_IOREQSRV_BUFIOREQ_OFF; rc = xen_create_ioreq_server(xen_domid, handle_bufioreq, &state->ioservid); if (!rc) { - xen_do_ioreq_register(state, max_cpus, xen_memory_listener); + xen_do_ioreq_register(state, max_cpus, xen_memory_listener, mapcache); } else { warn_report("xen: failed to create ioreq server"); } diff --git a/hw/xen/xen-pvh-common.c b/hw/xen/xen-pvh-common.c index XXXXXXX..XXXXXXX 100644 --- a/hw/xen/xen-pvh-common.c +++ b/hw/xen/xen-pvh-common.c @@ -XXX,XX +XXX,XX @@ static void xen_pvh_init(MachineState *ms) xen_pvh_init_ram(s, sysmem); xen_register_ioreq(&s->ioreq, ms->smp.max_cpus, xpc->handle_bufioreq, - &xen_memory_listener); + &xen_memory_listener, + true); if (s->cfg.virtio_mmio_num) { xen_create_virtio_mmio_devices(s); diff --git a/include/hw/xen/xen-hvm-common.h b/include/hw/xen/xen-hvm-common.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/xen/xen-hvm-common.h +++ b/include/hw/xen/xen-hvm-common.h @@ -XXX,XX +XXX,XX @@ void xen_device_unrealize(DeviceListener *listener, DeviceState *dev); void xen_hvm_change_state_handler(void *opaque, bool running, RunState rstate); void xen_register_ioreq(XenIOState *state, unsigned int max_cpus, uint8_t handle_bufioreq, - const MemoryListener *xen_memory_listener); + const MemoryListener *xen_memory_listener, + bool mapcache); void cpu_ioreq_pio(ioreq_t *req); #endif /* HW_XEN_HVM_COMMON_H */ -- 2.43.0
From: "Edgar E. Iglesias" <edgar.iglesias@amd.com> Add a machine property to enable/disable the mapcache. Default it to enabled for backwards compatibility. Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com> --- hw/xen/xen-pvh-common.c | 133 +++++++++++++++++++++++++++++--- include/hw/xen/xen-pvh-common.h | 2 + 2 files changed, 124 insertions(+), 11 deletions(-) diff --git a/hw/xen/xen-pvh-common.c b/hw/xen/xen-pvh-common.c index XXXXXXX..XXXXXXX 100644 --- a/hw/xen/xen-pvh-common.c +++ b/hw/xen/xen-pvh-common.c @@ -XXX,XX +XXX,XX @@ static const MemoryListener xen_memory_listener = { .priority = MEMORY_LISTENER_PRIORITY_ACCEL, }; +/* + * Map foreign RAM in bounded chunks so we don't build a PFN array for the + * entire guest size (which can be huge for large guests). We reserve a VA + * range once and then MAP_FIXED each chunk into place. + */ +#define XEN_PVH_MAP_CHUNK_PAGES 65535 + +static void *xen_map_guest_ram(XenPVHMachineState *s, + uint64_t addr, uint64_t size) +{ + size_t total_pages = size >> XC_PAGE_SHIFT; + size_t chunk_pages = MIN(XEN_PVH_MAP_CHUNK_PAGES, total_pages); + g_autofree xen_pfn_t *pfns = NULL; + void *base = NULL; + size_t offset; + + if (!total_pages) { + goto done; + } + + base = mmap(NULL, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (base == MAP_FAILED) { + base = NULL; + goto done; + } + + pfns = g_new0(xen_pfn_t, chunk_pages); + if (!pfns) { + munmap(base, size); + base = NULL; + goto done; + } + + for (offset = 0; offset < total_pages; offset += chunk_pages) { + size_t num_pages = MIN(chunk_pages, total_pages - offset); + void *mapped; + size_t i; + + for (i = 0; i < num_pages; i++) { + pfns[i] = (addr >> XC_PAGE_SHIFT) + offset + i; + } + + mapped = xenforeignmemory_map2( + xen_fmem, xen_domid, + (uint8_t *)base + (offset << XC_PAGE_SHIFT), + PROT_READ | PROT_WRITE, MAP_FIXED, + num_pages, pfns, NULL); + if (!mapped) { + munmap(base, size); + base = NULL; + goto done; + } + } +done: + if (!base) { + /* We can't recover from this. */ + error_report("FATAL: Failed to foreign-map %" PRIx64 " - %" PRIx64, + addr, addr + size); + exit(EXIT_FAILURE); + } + return base; +} + static void xen_pvh_init_ram(XenPVHMachineState *s, MemoryRegion *sysmem) { @@ -XXX,XX +XXX,XX @@ static void xen_pvh_init_ram(XenPVHMachineState *s, block_len = s->cfg.ram_high.base + ram_size[1]; } - memory_region_init_ram(&xen_memory, NULL, "xen.ram", block_len, - &error_fatal); + if (s->cfg.mapcache) { + memory_region_init_ram(&xen_memory, NULL, "xen.ram", + block_len, &error_fatal); + memory_region_init_alias(&s->ram.low, NULL, "xen.ram.lo", &xen_memory, + s->cfg.ram_low.base, ram_size[0]); + if (ram_size[1] > 0) { + memory_region_init_alias(&s->ram.high, NULL, "xen.ram.hi", + &xen_memory, + s->cfg.ram_high.base, ram_size[1]); + } + } else { + void *p; + + p = xen_map_guest_ram(s, s->cfg.ram_low.base, ram_size[0]); + memory_region_init_ram_ptr(&s->ram.low, NULL, "xen.ram.lo", + ram_size[0], p); + if (ram_size[1] > 0) { + p = xen_map_guest_ram(s, s->cfg.ram_high.base, ram_size[1]); + memory_region_init_ram_ptr(&s->ram.high, NULL, "xen.ram.hi", + ram_size[1], p); + } + } - memory_region_init_alias(&s->ram.low, NULL, "xen.ram.lo", &xen_memory, - s->cfg.ram_low.base, ram_size[0]); + /* Map them onto QEMU's address-space. */ memory_region_add_subregion(sysmem, s->cfg.ram_low.base, &s->ram.low); if (ram_size[1] > 0) { - memory_region_init_alias(&s->ram.high, NULL, "xen.ram.hi", &xen_memory, - s->cfg.ram_high.base, ram_size[1]); memory_region_add_subregion(sysmem, s->cfg.ram_high.base, &s->ram.high); } - /* Setup support for grants. */ - memory_region_init_ram(&xen_grants, NULL, "xen.grants", block_len, - &error_fatal); - memory_region_add_subregion(sysmem, XEN_GRANT_ADDR_OFF, &xen_grants); + /* Grants are only supported when the mapcache is on. */ + if (s->cfg.mapcache) { + /* Setup support for grants. */ + memory_region_init_ram(&xen_grants, NULL, "xen.grants", block_len, + &error_fatal); + memory_region_add_subregion(sysmem, XEN_GRANT_ADDR_OFF, &xen_grants); + } } static void xen_set_irq(void *opaque, int irq, int level) @@ -XXX,XX +XXX,XX @@ static void xen_pvh_init(MachineState *ms) xen_register_ioreq(&s->ioreq, ms->smp.max_cpus, xpc->handle_bufioreq, &xen_memory_listener, - true); + s->cfg.mapcache); if (s->cfg.virtio_mmio_num) { xen_create_virtio_mmio_devices(s); @@ -XXX,XX +XXX,XX @@ XEN_PVH_PROP_MEMMAP(pci_ecam) XEN_PVH_PROP_MEMMAP(pci_mmio) XEN_PVH_PROP_MEMMAP(pci_mmio_high) +static void xen_pvh_set_mapcache(Object *obj, bool value, Error **errp) +{ + XenPVHMachineState *xp = XEN_PVH_MACHINE(obj); + + xp->cfg.mapcache = value; +} + +static bool xen_pvh_get_mapcache(Object *obj, Error **errp) +{ + XenPVHMachineState *xp = XEN_PVH_MACHINE(obj); + + return xp->cfg.mapcache; +} + static void xen_pvh_set_pci_intx_irq_base(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) @@ -XXX,XX +XXX,XX @@ do { \ OC_MEMMAP_PROP_SIZE(c, prop_name, name); \ } while (0) + object_class_property_add_bool(oc, "mapcache", xen_pvh_get_mapcache, + xen_pvh_set_mapcache); + object_class_property_set_description(oc, "mapcache", + "Set on/off to enable/disable the " + "mapcache"); + /* * We provide memmap properties to allow Xen to move things to other * addresses for example when users need to accomodate the memory-map @@ -XXX,XX +XXX,XX @@ do { \ #endif } +static void xen_pvh_instance_init(Object *obj) +{ + XenPVHMachineState *xp = XEN_PVH_MACHINE(obj); + + xp->cfg.mapcache = true; +} + static void xen_pvh_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -XXX,XX +XXX,XX @@ static const TypeInfo xen_pvh_info = { .parent = TYPE_MACHINE, .abstract = true, .instance_size = sizeof(XenPVHMachineState), + .instance_init = xen_pvh_instance_init, .class_size = sizeof(XenPVHMachineClass), .class_init = xen_pvh_class_init, }; diff --git a/include/hw/xen/xen-pvh-common.h b/include/hw/xen/xen-pvh-common.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/xen/xen-pvh-common.h +++ b/include/hw/xen/xen-pvh-common.h @@ -XXX,XX +XXX,XX @@ struct XenPVHMachineState { /* PCI */ MemMapEntry pci_ecam, pci_mmio, pci_mmio_high; uint32_t pci_intx_irq_base; + + bool mapcache; } cfg; }; -- 2.43.0
From: "Edgar E. Iglesias" <edgar.iglesias@amd.com> Merge tag 'pull-11.0-virtio-gpu-updates-060326-1' of https://gitlab.com/stsquad/qemu into staging (2026-03-07 11:22:16 +0000) are available in the Git repository at: https://gitlab.com/edgar.iglesias/qemu.git tags/edgar/edgars-xen-queue-2026-02-09 for you to fetch changes up to 83d1d9a49e8eb970f87508ae4ce0f222084df365: hw/xen: xenpvh: Add prop to enable/disable the mapcache (2026-03-09 03:24:02 +0100) ---------------------------------------------------------------- Edgars Xen queue ---------------------------------------------------------------- Edgar E. Iglesias (5): xen: mapcache: Assert mapcache existance xen: mapcache: Add function to check if the mapcache is enabled physmem: xen: Conditionalize use of the mapcache hw/xen-hvm: Add a mapcache arg to xen_register_ioreq() hw/xen: xenpvh: Add prop to enable/disable the mapcache hw/i386/xen/xen-hvm.c | 2 +- hw/xen/xen-hvm-common.c | 18 +++-- hw/xen/xen-mapcache.c | 16 ++++ hw/xen/xen-pvh-common.c | 134 +++++++++++++++++++++++++++++--- hw/xen/xen_stubs.c | 5 ++ include/hw/xen/xen-hvm-common.h | 3 +- include/hw/xen/xen-pvh-common.h | 2 + include/system/xen-mapcache.h | 1 + system/physmem.c | 14 ++-- 9 files changed, 170 insertions(+), 25 deletions(-) -- 2.43.0
From: "Edgar E. Iglesias" <edgar.iglesias@amd.com> Assert that the mapcache was created prior to being used. Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> --- hw/xen/xen-mapcache.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index XXXXXXX..XXXXXXX 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -XXX,XX +XXX,XX @@ uint8_t *xen_map_cache(MemoryRegion *mr, MapCache *mc = mapcache; uint8_t *p; + assert(mapcache); + if (grant) { mc = is_write ? mapcache_grants_rw : mapcache_grants_ro; } @@ -XXX,XX +XXX,XX @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr) { ram_addr_t addr; + assert(mapcache); + addr = xen_ram_addr_from_mapcache_single(mapcache, ptr); if (addr == RAM_ADDR_INVALID) { addr = xen_ram_addr_from_mapcache_single(mapcache_grants_ro, ptr); @@ -XXX,XX +XXX,XX @@ static void xen_invalidate_map_cache_entry_bh(void *opaque) void coroutine_mixed_fn xen_invalidate_map_cache_entry(uint8_t *buffer) { + assert(mapcache); + if (qemu_in_coroutine()) { XenMapCacheData data = { .co = qemu_coroutine_self(), @@ -XXX,XX +XXX,XX @@ static void xen_invalidate_map_cache_single(MapCache *mc) void xen_invalidate_map_cache(void) { + assert(mapcache); + /* Flush pending AIO before destroying the mapcache */ bdrv_drain_all(); @@ -XXX,XX +XXX,XX @@ uint8_t *xen_replace_cache_entry(hwaddr old_phys_addr, { uint8_t *p; + assert(mapcache); + mapcache_lock(mapcache); p = xen_replace_cache_entry_unlocked(mapcache, old_phys_addr, new_phys_addr, size); -- 2.43.0
From: "Edgar E. Iglesias" <edgar.iglesias@amd.com> Add function to check if the mapcache is enabled. No functional change. Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> --- hw/xen/xen-mapcache.c | 6 ++++++ hw/xen/xen_stubs.c | 5 +++++ include/system/xen-mapcache.h | 1 + 3 files changed, 12 insertions(+) diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index XXXXXXX..XXXXXXX 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -XXX,XX +XXX,XX @@ static MapCache *mapcache_grants_ro; static MapCache *mapcache_grants_rw; static xengnttab_handle *xen_region_gnttabdev; +bool xen_map_cache_enabled(void) +{ + /* Map cache enabled implies xen_enabled(). */ + return xen_enabled() && mapcache; +} + static inline void mapcache_lock(MapCache *mc) { qemu_mutex_lock(&mc->lock); diff --git a/hw/xen/xen_stubs.c b/hw/xen/xen_stubs.c index XXXXXXX..XXXXXXX 100644 --- a/hw/xen/xen_stubs.c +++ b/hw/xen/xen_stubs.c @@ -XXX,XX +XXX,XX @@ bool xen_mr_is_memory(MemoryRegion *mr) g_assert_not_reached(); } +bool xen_map_cache_enabled(void) +{ + return false; +} + void xen_invalidate_map_cache_entry(uint8_t *buffer) { g_assert_not_reached(); diff --git a/include/system/xen-mapcache.h b/include/system/xen-mapcache.h index XXXXXXX..XXXXXXX 100644 --- a/include/system/xen-mapcache.h +++ b/include/system/xen-mapcache.h @@ -XXX,XX +XXX,XX @@ typedef hwaddr (*phys_offset_to_gaddr_t)(hwaddr phys_offset, ram_addr_t size); +bool xen_map_cache_enabled(void); void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque); uint8_t *xen_map_cache(MemoryRegion *mr, hwaddr phys_addr, hwaddr size, -- 2.43.0
From: "Edgar E. Iglesias" <edgar.iglesias@amd.com> Conditionalize use of the mapcache. This is in preparation to optionally disable the mapcache at runtime. Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com> Acked-by: Peter Xu <peterx@redhat.com> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> --- system/physmem.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index XXXXXXX..XXXXXXX 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -XXX,XX +XXX,XX @@ MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat, is_write, true, &as, attrs); mr = section.mr; - if (xen_enabled() && memory_access_is_direct(mr, is_write, attrs)) { + if (xen_map_cache_enabled() && + memory_access_is_direct(mr, is_write, attrs)) { + /* mapcache: Next page may be unmapped or in a different bucket/VA. */ hwaddr page = ((addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE) - addr; *plen = MIN(page, *plen); } @@ -XXX,XX +XXX,XX @@ static void reclaim_ramblock(RAMBlock *block) { if (block->flags & RAM_PREALLOC) { ; - } else if (xen_enabled()) { + } else if (xen_map_cache_enabled()) { xen_invalidate_map_cache_entry(block->host); #if !defined(_WIN32) && !defined(EMSCRIPTEN) } else if (block->fd >= 0) { @@ -XXX,XX +XXX,XX @@ static void *qemu_ram_ptr_length(RAMBlock *block, ram_addr_t addr, len = *size; } - if (xen_enabled() && block->host == NULL) { + if (xen_map_cache_enabled() && block->host == NULL) { /* We need to check if the requested address is in the RAM * because we don't want to map the entire memory in QEMU. * In that case just map the requested area. @@ -XXX,XX +XXX,XX @@ RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset, RAMBlock *block; uint8_t *host = ptr; - if (xen_enabled()) { + if (xen_map_cache_enabled()) { ram_addr_t ram_addr; RCU_READ_LOCK_GUARD(); ram_addr = xen_ram_addr_from_mapcache(ptr); @@ -XXX,XX +XXX,XX @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, if (is_write) { invalidate_and_set_dirty(mr, addr1, access_len); } - if (xen_enabled()) { + if (xen_map_cache_enabled()) { xen_invalidate_map_cache_entry(buffer); } memory_region_unref(mr); @@ -XXX,XX +XXX,XX @@ void address_space_cache_destroy(MemoryRegionCache *cache) return; } - if (xen_enabled()) { + if (xen_map_cache_enabled()) { xen_invalidate_map_cache_entry(cache->ptr); } memory_region_unref(cache->mrs.mr); -- 2.43.0
From: "Edgar E. Iglesias" <edgar.iglesias@amd.com> Add a mapcache argument to xen_register_ioreq() allowing the caller to optionally disable the mapcache. All callers still call it with mapcache = true so there's no functional change yet. Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> --- hw/i386/xen/xen-hvm.c | 2 +- hw/xen/xen-hvm-common.c | 18 ++++++++++++------ hw/xen/xen-pvh-common.c | 3 ++- include/hw/xen/xen-hvm-common.h | 3 ++- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -XXX,XX +XXX,XX @@ void xen_hvm_init_pc(PCMachineState *pcms, MemoryRegion **ram_memory) xen_register_ioreq(state, max_cpus, HVM_IOREQSRV_BUFIOREQ_ATOMIC, - &xen_memory_listener); + &xen_memory_listener, true); xen_is_stubdomain = xen_check_stubdomain(state->xenstore); diff --git a/hw/xen/xen-hvm-common.c b/hw/xen/xen-hvm-common.c index XXXXXXX..XXXXXXX 100644 --- a/hw/xen/xen-hvm-common.c +++ b/hw/xen/xen-hvm-common.c @@ -XXX,XX +XXX,XX @@ static void handle_ioreq(XenIOState *state, ioreq_t *req) case IOREQ_TYPE_TIMEOFFSET: break; case IOREQ_TYPE_INVALIDATE: - xen_invalidate_map_cache(); + if (xen_map_cache_enabled()) { + xen_invalidate_map_cache(); + } break; case IOREQ_TYPE_PCI_CONFIG: cpu_ioreq_config(state, req); @@ -XXX,XX +XXX,XX @@ void xen_shutdown_fatal_error(const char *fmt, ...) static void xen_do_ioreq_register(XenIOState *state, unsigned int max_cpus, - const MemoryListener *xen_memory_listener) + const MemoryListener *xen_memory_listener, + bool mapcache) { int i, rc; @@ -XXX,XX +XXX,XX @@ static void xen_do_ioreq_register(XenIOState *state, state->bufioreq_local_port = rc; } /* Init RAM management */ + if (mapcache) { #ifdef XEN_COMPAT_PHYSMAP - xen_map_cache_init(xen_phys_offset_to_gaddr, state); + xen_map_cache_init(xen_phys_offset_to_gaddr, state); #else - xen_map_cache_init(NULL, state); + xen_map_cache_init(NULL, state); #endif + } qemu_add_vm_change_state_handler(xen_hvm_change_state_handler, state); @@ -XXX,XX +XXX,XX @@ err: void xen_register_ioreq(XenIOState *state, unsigned int max_cpus, uint8_t handle_bufioreq, - const MemoryListener *xen_memory_listener) + const MemoryListener *xen_memory_listener, + bool mapcache) { int rc; @@ -XXX,XX +XXX,XX @@ void xen_register_ioreq(XenIOState *state, unsigned int max_cpus, state->has_bufioreq = handle_bufioreq != HVM_IOREQSRV_BUFIOREQ_OFF; rc = xen_create_ioreq_server(xen_domid, handle_bufioreq, &state->ioservid); if (!rc) { - xen_do_ioreq_register(state, max_cpus, xen_memory_listener); + xen_do_ioreq_register(state, max_cpus, xen_memory_listener, mapcache); } else { warn_report("xen: failed to create ioreq server"); } diff --git a/hw/xen/xen-pvh-common.c b/hw/xen/xen-pvh-common.c index XXXXXXX..XXXXXXX 100644 --- a/hw/xen/xen-pvh-common.c +++ b/hw/xen/xen-pvh-common.c @@ -XXX,XX +XXX,XX @@ static void xen_pvh_init(MachineState *ms) xen_pvh_init_ram(s, sysmem); xen_register_ioreq(&s->ioreq, ms->smp.max_cpus, xpc->handle_bufioreq, - &xen_memory_listener); + &xen_memory_listener, + true); if (s->cfg.virtio_mmio_num) { xen_create_virtio_mmio_devices(s); diff --git a/include/hw/xen/xen-hvm-common.h b/include/hw/xen/xen-hvm-common.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/xen/xen-hvm-common.h +++ b/include/hw/xen/xen-hvm-common.h @@ -XXX,XX +XXX,XX @@ void xen_device_unrealize(DeviceListener *listener, DeviceState *dev); void xen_hvm_change_state_handler(void *opaque, bool running, RunState rstate); void xen_register_ioreq(XenIOState *state, unsigned int max_cpus, uint8_t handle_bufioreq, - const MemoryListener *xen_memory_listener); + const MemoryListener *xen_memory_listener, + bool mapcache); void cpu_ioreq_pio(ioreq_t *req); #endif /* HW_XEN_HVM_COMMON_H */ -- 2.43.0
From: "Edgar E. Iglesias" <edgar.iglesias@amd.com> Add a machine property to enable/disable the mapcache. Default it to enabled for backwards compatibility. Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> --- hw/xen/xen-pvh-common.c | 133 +++++++++++++++++++++++++++++--- include/hw/xen/xen-pvh-common.h | 2 + 2 files changed, 124 insertions(+), 11 deletions(-) diff --git a/hw/xen/xen-pvh-common.c b/hw/xen/xen-pvh-common.c index XXXXXXX..XXXXXXX 100644 --- a/hw/xen/xen-pvh-common.c +++ b/hw/xen/xen-pvh-common.c @@ -XXX,XX +XXX,XX @@ static const MemoryListener xen_memory_listener = { .priority = MEMORY_LISTENER_PRIORITY_ACCEL, }; +/* + * Map foreign RAM in bounded chunks so we don't build a PFN array for the + * entire guest size (which can be huge for large guests). We reserve a VA + * range once and then MAP_FIXED each chunk into place. + */ +#define XEN_PVH_MAP_CHUNK_PAGES 65535 + +static void *xen_map_guest_ram(XenPVHMachineState *s, + uint64_t addr, uint64_t size) +{ + size_t total_pages = size >> XC_PAGE_SHIFT; + size_t chunk_pages = MIN(XEN_PVH_MAP_CHUNK_PAGES, total_pages); + g_autofree xen_pfn_t *pfns = NULL; + void *base = NULL; + size_t offset; + + if (!total_pages) { + goto done; + } + + base = mmap(NULL, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (base == MAP_FAILED) { + base = NULL; + goto done; + } + + pfns = g_new0(xen_pfn_t, chunk_pages); + if (!pfns) { + munmap(base, size); + base = NULL; + goto done; + } + + for (offset = 0; offset < total_pages; offset += chunk_pages) { + size_t num_pages = MIN(chunk_pages, total_pages - offset); + void *mapped; + size_t i; + + for (i = 0; i < num_pages; i++) { + pfns[i] = (addr >> XC_PAGE_SHIFT) + offset + i; + } + + mapped = xenforeignmemory_map2( + xen_fmem, xen_domid, + (uint8_t *)base + (offset << XC_PAGE_SHIFT), + PROT_READ | PROT_WRITE, MAP_FIXED, + num_pages, pfns, NULL); + if (!mapped) { + munmap(base, size); + base = NULL; + goto done; + } + } +done: + if (!base) { + /* We can't recover from this. */ + error_report("FATAL: Failed to foreign-map %" PRIx64 " - %" PRIx64, + addr, addr + size); + exit(EXIT_FAILURE); + } + return base; +} + static void xen_pvh_init_ram(XenPVHMachineState *s, MemoryRegion *sysmem) { @@ -XXX,XX +XXX,XX @@ static void xen_pvh_init_ram(XenPVHMachineState *s, block_len = s->cfg.ram_high.base + ram_size[1]; } - memory_region_init_ram(&xen_memory, NULL, "xen.ram", block_len, - &error_fatal); + if (s->cfg.mapcache) { + memory_region_init_ram(&xen_memory, NULL, "xen.ram", + block_len, &error_fatal); + memory_region_init_alias(&s->ram.low, NULL, "xen.ram.lo", &xen_memory, + s->cfg.ram_low.base, ram_size[0]); + if (ram_size[1] > 0) { + memory_region_init_alias(&s->ram.high, NULL, "xen.ram.hi", + &xen_memory, + s->cfg.ram_high.base, ram_size[1]); + } + } else { + void *p; + + p = xen_map_guest_ram(s, s->cfg.ram_low.base, ram_size[0]); + memory_region_init_ram_ptr(&s->ram.low, NULL, "xen.ram.lo", + ram_size[0], p); + if (ram_size[1] > 0) { + p = xen_map_guest_ram(s, s->cfg.ram_high.base, ram_size[1]); + memory_region_init_ram_ptr(&s->ram.high, NULL, "xen.ram.hi", + ram_size[1], p); + } + } - memory_region_init_alias(&s->ram.low, NULL, "xen.ram.lo", &xen_memory, - s->cfg.ram_low.base, ram_size[0]); + /* Map them onto QEMU's address-space. */ memory_region_add_subregion(sysmem, s->cfg.ram_low.base, &s->ram.low); if (ram_size[1] > 0) { - memory_region_init_alias(&s->ram.high, NULL, "xen.ram.hi", &xen_memory, - s->cfg.ram_high.base, ram_size[1]); memory_region_add_subregion(sysmem, s->cfg.ram_high.base, &s->ram.high); } - /* Setup support for grants. */ - memory_region_init_ram(&xen_grants, NULL, "xen.grants", block_len, - &error_fatal); - memory_region_add_subregion(sysmem, XEN_GRANT_ADDR_OFF, &xen_grants); + /* Grants are only supported when the mapcache is on. */ + if (s->cfg.mapcache) { + /* Setup support for grants. */ + memory_region_init_ram(&xen_grants, NULL, "xen.grants", block_len, + &error_fatal); + memory_region_add_subregion(sysmem, XEN_GRANT_ADDR_OFF, &xen_grants); + } } static void xen_set_irq(void *opaque, int irq, int level) @@ -XXX,XX +XXX,XX @@ static void xen_pvh_init(MachineState *ms) xen_register_ioreq(&s->ioreq, ms->smp.max_cpus, xpc->handle_bufioreq, &xen_memory_listener, - true); + s->cfg.mapcache); if (s->cfg.virtio_mmio_num) { xen_create_virtio_mmio_devices(s); @@ -XXX,XX +XXX,XX @@ XEN_PVH_PROP_MEMMAP(pci_ecam) XEN_PVH_PROP_MEMMAP(pci_mmio) XEN_PVH_PROP_MEMMAP(pci_mmio_high) +static void xen_pvh_set_mapcache(Object *obj, bool value, Error **errp) +{ + XenPVHMachineState *xp = XEN_PVH_MACHINE(obj); + + xp->cfg.mapcache = value; +} + +static bool xen_pvh_get_mapcache(Object *obj, Error **errp) +{ + XenPVHMachineState *xp = XEN_PVH_MACHINE(obj); + + return xp->cfg.mapcache; +} + static void xen_pvh_set_pci_intx_irq_base(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) @@ -XXX,XX +XXX,XX @@ do { \ OC_MEMMAP_PROP_SIZE(c, prop_name, name); \ } while (0) + object_class_property_add_bool(oc, "mapcache", xen_pvh_get_mapcache, + xen_pvh_set_mapcache); + object_class_property_set_description(oc, "mapcache", + "Set on/off to enable/disable the " + "mapcache"); + /* * We provide memmap properties to allow Xen to move things to other * addresses for example when users need to accomodate the memory-map @@ -XXX,XX +XXX,XX @@ do { \ #endif } +static void xen_pvh_instance_init(Object *obj) +{ + XenPVHMachineState *xp = XEN_PVH_MACHINE(obj); + + xp->cfg.mapcache = true; +} + static void xen_pvh_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -XXX,XX +XXX,XX @@ static const TypeInfo xen_pvh_info = { .parent = TYPE_MACHINE, .abstract = true, .instance_size = sizeof(XenPVHMachineState), + .instance_init = xen_pvh_instance_init, .class_size = sizeof(XenPVHMachineClass), .class_init = xen_pvh_class_init, }; diff --git a/include/hw/xen/xen-pvh-common.h b/include/hw/xen/xen-pvh-common.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/xen/xen-pvh-common.h +++ b/include/hw/xen/xen-pvh-common.h @@ -XXX,XX +XXX,XX @@ struct XenPVHMachineState { /* PCI */ MemMapEntry pci_ecam, pci_mmio, pci_mmio_high; uint32_t pci_intx_irq_base; + + bool mapcache; } cfg; }; -- 2.43.0