memory: Introduce RAM_NORESERVE and wire it up in qemu_ram_mmap()
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 if applicable) is skipped: will bail out if not supported. When not set, the OS will do the reservation, if supported for the memory type. " 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 Linux support 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. Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> Reviewed-by: Peter Xu <peterx@redhat.com> Acked-by: Eduardo Habkost <ehabkost@redhat.com> for memory backend and machine core Signed-off-by: David Hildenbrand <david@redhat.com> Message-Id: <20210510114328.21835-9-david@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
b444f5c079
commit
8dbe22c686
@ -59,6 +59,7 @@ ram_addr_t qemu_ram_get_offset(RAMBlock *rb);
|
||||
ram_addr_t qemu_ram_get_used_length(RAMBlock *rb);
|
||||
ram_addr_t qemu_ram_get_max_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);
|
||||
|
@ -155,6 +155,13 @@ typedef struct IOMMUTLBEvent {
|
||||
*/
|
||||
#define RAM_UF_WRITEPROTECT (1 << 6)
|
||||
|
||||
/*
|
||||
* RAM is mmap-ed with MAP_NORESERVE. When set, reserving swap space (or huge
|
||||
* pages if applicable) is skipped: will bail out if not supported. When not
|
||||
* set, the OS will do the reservation, if supported for the memory type.
|
||||
*/
|
||||
#define RAM_NORESERVE (1 << 7)
|
||||
|
||||
static inline void iommu_notifier_init(IOMMUNotifier *n, IOMMUNotify fn,
|
||||
IOMMUNotifierFlag flags,
|
||||
hwaddr start, hwaddr end,
|
||||
@ -949,7 +956,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
|
||||
@ -1005,7 +1012,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.
|
||||
@ -1031,7 +1039,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.
|
||||
|
@ -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
|
||||
|
@ -362,7 +362,8 @@ extern "C" {
|
||||
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);
|
||||
|
||||
@ -383,6 +384,12 @@ void qemu_anon_ram_free(void *ptr, size_t size);
|
||||
*/
|
||||
#define QEMU_MAP_SYNC (1 << 2)
|
||||
|
||||
/*
|
||||
* Use MAP_NORESERVE to skip reservation of swap space (or huge pages if
|
||||
* applicable). Bail out if not supported/effective.
|
||||
*/
|
||||
#define QEMU_MAP_NORESERVE (1 << 3)
|
||||
|
||||
|
||||
#define QEMU_MADV_INVALID -1
|
||||
|
||||
|
@ -3343,8 +3343,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,
|
||||
|
@ -1591,6 +1591,7 @@ static void *file_ram_alloc(RAMBlock *block,
|
||||
qemu_map_flags = readonly ? QEMU_MAP_READONLY : 0;
|
||||
qemu_map_flags |= (block->flags & RAM_SHARED) ? QEMU_MAP_SHARED : 0;
|
||||
qemu_map_flags |= (block->flags & RAM_PMEM) ? QEMU_MAP_SYNC : 0;
|
||||
qemu_map_flags |= (block->flags & RAM_NORESERVE) ? QEMU_MAP_NORESERVE : 0;
|
||||
area = qemu_ram_mmap(fd, memory, block->mr->align, qemu_map_flags, offset);
|
||||
if (area == MAP_FAILED) {
|
||||
error_setg_errno(errp, errno,
|
||||
@ -1716,6 +1717,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)
|
||||
{
|
||||
@ -1950,6 +1956,7 @@ static void dirty_memory_extend(ram_addr_t old_ram_size,
|
||||
|
||||
static void ram_block_add(RAMBlock *new_block, Error **errp)
|
||||
{
|
||||
const bool noreserve = qemu_ram_is_noreserve(new_block);
|
||||
const bool shared = qemu_ram_is_shared(new_block);
|
||||
RAMBlock *block;
|
||||
RAMBlock *last_block = NULL;
|
||||
@ -1973,7 +1980,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'",
|
||||
@ -2045,7 +2052,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");
|
||||
@ -2137,6 +2144,8 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size,
|
||||
Error *local_err = NULL;
|
||||
|
||||
assert((ram_flags & ~(RAM_SHARED | RAM_RESIZEABLE | RAM_PREALLOC)) == 0);
|
||||
assert((ram_flags & ~(RAM_SHARED | RAM_RESIZEABLE | RAM_PREALLOC |
|
||||
RAM_NORESERVE)) == 0);
|
||||
assert(!host ^ (ram_flags & RAM_PREALLOC));
|
||||
|
||||
size = HOST_PAGE_ALIGN(size);
|
||||
@ -2170,7 +2179,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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
@ -121,6 +122,7 @@ static void *mmap_reserve(size_t size, int fd)
|
||||
static void *mmap_activate(void *ptr, size_t size, int fd,
|
||||
uint32_t qemu_map_flags, off_t map_offset)
|
||||
{
|
||||
const bool noreserve = qemu_map_flags & QEMU_MAP_NORESERVE;
|
||||
const bool readonly = qemu_map_flags & QEMU_MAP_READONLY;
|
||||
const bool shared = qemu_map_flags & QEMU_MAP_SHARED;
|
||||
const bool sync = qemu_map_flags & QEMU_MAP_SYNC;
|
||||
@ -129,6 +131,11 @@ static void *mmap_activate(void *ptr, size_t size, int fd,
|
||||
int flags = MAP_FIXED;
|
||||
void *activated_ptr;
|
||||
|
||||
if (noreserve) {
|
||||
error_report("Skipping reservation of swap space is not supported");
|
||||
return MAP_FAILED;
|
||||
}
|
||||
|
||||
flags |= fd == -1 ? MAP_ANONYMOUS : 0;
|
||||
flags |= shared ? MAP_SHARED : MAP_PRIVATE;
|
||||
if (shared && sync) {
|
||||
|
@ -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 qemu_map_flags = shared ? QEMU_MAP_SHARED : 0;
|
||||
const uint32_t qemu_map_flags = (shared ? QEMU_MAP_SHARED : 0) |
|
||||
(noreserve ? QEMU_MAP_NORESERVE : 0);
|
||||
size_t align = QEMU_VMALLOC_ALIGN;
|
||||
void *ptr = qemu_ram_mmap(-1, size, align, qemu_map_flags, 0);
|
||||
|
||||
|
@ -38,6 +38,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" */
|
||||
@ -76,10 +77,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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user