Let's introduce RAM_NORESERVE, allowing mmap'ing with MAP_NORESERVE. The new flag has the following semantics: RAM is mmap-ed with MAP_NORESERVE. When set, reserving swap space (or huge pages on Linux) is skipped: will bail out if not supported. When not set, the OS might reserve swap space (or huge pages on Linux), depending on OS support. Allow passing it into: - memory_region_init_ram_nomigrate() - memory_region_init_resizeable_ram() - memory_region_init_ram_from_file() ... and teach qemu_ram_mmap() and qemu_anon_ram_alloc() about the flag. Bail out if the flag is not supported, which is the case right now for both, POSIX and win32. We will add the POSIX mmap implementation next and allow specifying RAM_NORESERVE via memory backends. The target use case is virtio-mem, which dynamically exposes memory inside a large, sparse memory area to the VM. Cc: Juan Quintela <quintela@xxxxxxxxxx> Cc: Halil Pasic <pasic@xxxxxxxxxxxxx> Cc: Cornelia Huck <cohuck@xxxxxxxxxx> Cc: Christian Borntraeger <borntraeger@xxxxxxxxxx> Cc: Thomas Huth <thuth@xxxxxxxxxx> Cc: Stefan Weil <sw@xxxxxxxxxxx> Cc: kvm@xxxxxxxxxxxxxxx Cc: qemu-s390x@xxxxxxxxxx Signed-off-by: David Hildenbrand <david@xxxxxxxxxx> --- include/exec/cpu-common.h | 1 + include/exec/memory.h | 16 +++++++++++++--- include/exec/ram_addr.h | 3 ++- include/qemu/mmap-alloc.h | 3 +++ include/qemu/osdep.h | 3 ++- migration/ram.c | 3 +-- softmmu/physmem.c | 15 +++++++++++---- util/mmap-alloc.c | 6 ++++++ util/oslib-posix.c | 6 ++++-- util/oslib-win32.c | 13 ++++++++++++- 10 files changed, 55 insertions(+), 14 deletions(-) diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 5a0a2d93e0..38a47ad4ac 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -58,6 +58,7 @@ void *qemu_ram_get_host_addr(RAMBlock *rb); ram_addr_t qemu_ram_get_offset(RAMBlock *rb); ram_addr_t qemu_ram_get_used_length(RAMBlock *rb); bool qemu_ram_is_shared(RAMBlock *rb); +bool qemu_ram_is_noreserve(RAMBlock *rb); bool qemu_ram_is_uf_zeroable(RAMBlock *rb); void qemu_ram_set_uf_zeroable(RAMBlock *rb); bool qemu_ram_is_migratable(RAMBlock *rb); diff --git a/include/exec/memory.h b/include/exec/memory.h index 2d97bdf59c..1369497415 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -155,6 +155,14 @@ typedef struct IOMMUTLBEvent { */ #define RAM_UF_WRITEPROTECT (1 << 6) +/* + * RAM is mmap-ed with MAP_NORESERVE. When set, reserving swap space (or huge + * pages Linux) is skipped: will bail out if not supported. When not set, the + * OS might reserve swap space (or huge pages on Linux), depending on OS + * support. + */ +#define RAM_NORESERVE (1 << 7) + static inline void iommu_notifier_init(IOMMUNotifier *n, IOMMUNotify fn, IOMMUNotifierFlag flags, hwaddr start, hwaddr end, @@ -913,7 +921,7 @@ void memory_region_init_ram_nomigrate(MemoryRegion *mr, * @name: Region name, becomes part of RAMBlock name used in migration stream * must be unique within any device * @size: size of the region. - * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED. + * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_NORESERVE. * @errp: pointer to Error*, to store an error if it happens. * * Note that this function does not do anything to cause the data in the @@ -967,7 +975,8 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr, * @size: size of the region. * @align: alignment of the region base address; if 0, the default alignment * (getpagesize()) will be used. - * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM. + * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM, + * RAM_NORESERVE, * @path: the path in which to allocate the RAM. * @readonly: true to open @path for reading, false for read/write. * @errp: pointer to Error*, to store an error if it happens. @@ -993,7 +1002,8 @@ void memory_region_init_ram_from_file(MemoryRegion *mr, * @owner: the object that tracks the region's reference count * @name: the name of the region. * @size: size of the region. - * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM. + * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM, + * RAM_NORESERVE. * @fd: the fd to mmap. * @offset: offset within the file referenced by fd * @errp: pointer to Error*, to store an error if it happens. diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 6d4513f8e2..551876bed0 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -104,7 +104,8 @@ long qemu_maxrampagesize(void); * Parameters: * @size: the size in bytes of the ram block * @mr: the memory region where the ram block is - * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM. + * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM, + * RAM_NORESERVE. * @mem_path or @fd: specify the backing file or device * @readonly: true to open @path for reading, false for read/write. * @errp: pointer to Error*, to store an error if it happens diff --git a/include/qemu/mmap-alloc.h b/include/qemu/mmap-alloc.h index 55664ea9f3..6ac05d70d4 100644 --- a/include/qemu/mmap-alloc.h +++ b/include/qemu/mmap-alloc.h @@ -15,6 +15,9 @@ size_t qemu_mempath_getpagesize(const char *mem_path); /* Map MAP_SYNC|MAP_SHARED_VALIDATE if possible, fallback and warn otherwise. */ #define QEMU_RAM_MMAP_PMEM (1 << 2) +/* Map MAP_NORESERVE and fail if not effective. */ +#define QEMU_RAM_MMAP_NORESERVE (1 << 3) + /** * qemu_ram_mmap: mmap the specified file or device. * diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index ba15be9c56..d6d8ef0999 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -343,7 +343,8 @@ extern int daemon(int, int); int qemu_daemon(int nochdir, int noclose); void *qemu_try_memalign(size_t alignment, size_t size); void *qemu_memalign(size_t alignment, size_t size); -void *qemu_anon_ram_alloc(size_t size, uint64_t *align, bool shared); +void *qemu_anon_ram_alloc(size_t size, uint64_t *align, bool shared, + bool noreserve); void qemu_vfree(void *ptr); void qemu_anon_ram_free(void *ptr, size_t size); diff --git a/migration/ram.c b/migration/ram.c index 72143da0ac..dd8daad386 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3322,8 +3322,7 @@ int colo_init_ram_cache(void) WITH_RCU_READ_LOCK_GUARD() { RAMBLOCK_FOREACH_NOT_IGNORED(block) { block->colo_cache = qemu_anon_ram_alloc(block->used_length, - NULL, - false); + NULL, false, false); if (!block->colo_cache) { error_report("%s: Can't alloc memory for COLO cache of block %s," "size 0x" RAM_ADDR_FMT, __func__, block->idstr, diff --git a/softmmu/physmem.c b/softmmu/physmem.c index ec7a382ccd..dcc1fb74aa 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -1584,6 +1584,7 @@ static void *file_ram_alloc(RAMBlock *block, mmap_flags = readonly ? QEMU_RAM_MMAP_READONLY : 0; mmap_flags |= (block->flags & RAM_SHARED) ? QEMU_RAM_MMAP_SHARED : 0; mmap_flags |= (block->flags & RAM_PMEM) ? QEMU_RAM_MMAP_PMEM : 0; + mmap_flags |= (block->flags & RAM_NORESERVE) ? QEMU_RAM_MMAP_NORESERVE : 0; area = qemu_ram_mmap(fd, memory, block->mr->align, mmap_flags, offset); if (area == MAP_FAILED) { error_setg_errno(errp, errno, @@ -1704,6 +1705,11 @@ bool qemu_ram_is_shared(RAMBlock *rb) return rb->flags & RAM_SHARED; } +bool qemu_ram_is_noreserve(RAMBlock *rb) +{ + return rb->flags & RAM_NORESERVE; +} + /* Note: Only set at the start of postcopy */ bool qemu_ram_is_uf_zeroable(RAMBlock *rb) { @@ -1932,6 +1938,7 @@ static void dirty_memory_extend(ram_addr_t old_ram_size, static void ram_block_add(RAMBlock *new_block, Error **errp) { const bool shared = qemu_ram_is_shared(new_block); + const bool noreserve = qemu_ram_is_noreserve(new_block); RAMBlock *block; RAMBlock *last_block = NULL; ram_addr_t old_ram_size, new_ram_size; @@ -1954,7 +1961,7 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) } else { new_block->host = qemu_anon_ram_alloc(new_block->max_length, &new_block->mr->align, - shared); + shared, noreserve); if (!new_block->host) { error_setg_errno(errp, errno, "cannot set up guest memory '%s'", @@ -2025,7 +2032,7 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, int64_t file_size, file_align; /* Just support these ram flags by now. */ - assert((ram_flags & ~(RAM_SHARED | RAM_PMEM)) == 0); + assert((ram_flags & ~(RAM_SHARED | RAM_PMEM | RAM_NORESERVE)) == 0); if (xen_enabled()) { error_setg(errp, "-mem-path not supported with Xen"); @@ -2116,7 +2123,7 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, RAMBlock *new_block; Error *local_err = NULL; - assert((ram_flags & ~(RAM_SHARED | RAM_RESIZEABLE)) == 0); + assert((ram_flags & ~(RAM_SHARED | RAM_RESIZEABLE | RAM_NORESERVE)) == 0); size = HOST_PAGE_ALIGN(size); max_size = HOST_PAGE_ALIGN(max_size); @@ -2151,7 +2158,7 @@ RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, RAMBlock *qemu_ram_alloc(ram_addr_t size, uint32_t ram_flags, MemoryRegion *mr, Error **errp) { - assert((ram_flags & ~RAM_SHARED) == 0); + assert((ram_flags & ~(RAM_SHARED | RAM_NORESERVE)) == 0); return qemu_ram_alloc_internal(size, size, NULL, NULL, ram_flags, mr, errp); } diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c index bd8f7ab547..ecace41ad5 100644 --- a/util/mmap-alloc.c +++ b/util/mmap-alloc.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "qemu/mmap-alloc.h" #include "qemu/host-utils.h" +#include "qemu/error-report.h" #define HUGETLBFS_MAGIC 0x958458f6 @@ -183,6 +184,11 @@ void *qemu_ram_mmap(int fd, size_t offset, total; void *ptr, *guardptr; + if (mmap_flags & QEMU_RAM_MMAP_NORESERVE) { + error_report("Skipping reservation of swap space is not supported"); + return MAP_FAILED; + } + /* * Note: this always allocates at least one extra page of virtual address * space, even if size is already aligned. diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 1d250416f1..eab92fcafe 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -227,9 +227,11 @@ void *qemu_memalign(size_t alignment, size_t size) } /* alloc shared memory pages */ -void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared) +void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared, + bool noreserve) { - const uint32_t mmap_flags = shared ? QEMU_RAM_MMAP_SHARED : 0; + const uint32_t mmap_flags = (shared ? QEMU_RAM_MMAP_SHARED : 0) | + (noreserve ? QEMU_RAM_MMAP_NORESERVE : 0); size_t align = QEMU_VMALLOC_ALIGN; void *ptr = qemu_ram_mmap(-1, size, align, mmap_flags, 0); diff --git a/util/oslib-win32.c b/util/oslib-win32.c index f68b8012bb..8cafe44179 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -39,6 +39,7 @@ #include "trace.h" #include "qemu/sockets.h" #include "qemu/cutils.h" +#include "qemu/error-report.h" #include <malloc.h> /* this must come after including "trace.h" */ @@ -77,10 +78,20 @@ static int get_allocation_granularity(void) return system_info.dwAllocationGranularity; } -void *qemu_anon_ram_alloc(size_t size, uint64_t *align, bool shared) +void *qemu_anon_ram_alloc(size_t size, uint64_t *align, bool shared, + bool noreserve) { void *ptr; + if (noreserve) { + /* + * We need a MEM_COMMIT before accessing any memory in a MEM_RESERVE + * area; we cannot easily mimic POSIX MAP_NORESERVE semantics. + */ + error_report("Skipping reservation of swap space is not supported."); + return NULL; + } + ptr = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE); trace_qemu_anon_ram_alloc(size, ptr); -- 2.29.2