[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH] pc: map pc ram from user-specified file
From: |
Peter Feiner |
Subject: |
[Qemu-devel] [PATCH] pc: map pc ram from user-specified file |
Date: |
Fri, 25 Nov 2011 14:02:39 -0500 |
Enables providing a backing file for the PC's ram. The file is specified by the
new -pcram-file option. The file is mmap'd shared, so the RAMBlock that it backs
doesn't need to be saved by vm_save / migration.
Signed-off-by: Peter Feiner <address@hidden>
---
We have found this small feature very useful for experimenting with memory
migration techniques. By exposing PC memory through a simple interface (i.e.,
the filesystem), we can implement various memory migration techniques
independently of QEMU. For example, one can map a VM's ram to a file being
served over a network, thus implementing on-demand fetching.
In the future, RAMBlocks could be mmap'd privately to implement memory sharing.
Note that unlike the existing -mem-path option, which specifies a (hugetlbfs)
directory in which files for all RAMBlocks are to be created, -pcram-file
specifies a file to be mapped for the "pc.ram" RAMBlock
arch_init.c | 11 +++++++
cpu-all.h | 12 ++++++++
exec.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
qemu-common.h | 2 +
qemu-options.hx | 13 ++++++++
vl.c | 3 ++
6 files changed, 124 insertions(+), 0 deletions(-)
diff --git a/arch_init.c b/arch_init.c
index a411fdf..96e8a28 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -122,6 +122,14 @@ static int ram_save_block(QEMUFile *f)
if (!block)
block = QLIST_FIRST(&ram_list.blocks);
+ while (block->do_not_save) {
+ last_block = block;
+ block = QLIST_NEXT(block, next);
+ if (!block) {
+ return 0;
+ }
+ }
+
current_addr = block->offset + offset;
do {
@@ -185,6 +193,9 @@ static ram_addr_t ram_save_remaining(void)
QLIST_FOREACH(block, &ram_list.blocks, next) {
ram_addr_t addr;
+ if (block->do_not_save) {
+ continue;
+ }
for (addr = block->offset; addr < block->offset + block->length;
addr += TARGET_PAGE_SIZE) {
if (cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG)) {
diff --git a/cpu-all.h b/cpu-all.h
index 5f47ab8..a78f38c 100644
--- a/cpu-all.h
+++ b/cpu-all.h
@@ -482,6 +482,7 @@ typedef struct RAMBlock {
uint32_t flags;
char idstr[256];
QLIST_ENTRY(RAMBlock) next;
+ int do_not_save;
#if defined(__linux__) && !defined(TARGET_S390X)
int fd;
#endif
@@ -493,6 +494,17 @@ typedef struct RAMList {
} RAMList;
extern RAMList ram_list;
+typedef struct MemFile {
+ const char *idstr;
+ const char *path;
+ QLIST_ENTRY(MemFile) next;
+} MemFile;
+
+typedef struct MemFileList {
+ QLIST_HEAD(files, MemFile) files;
+} MemFileList;
+extern MemFileList mem_file_list;
+
extern const char *mem_path;
extern int mem_prealloc;
diff --git a/exec.c b/exec.c
index 6b92198..9a1cbca 100644
--- a/exec.c
+++ b/exec.c
@@ -117,6 +117,8 @@ static MemoryRegion *system_io;
#endif
+MemFileList mem_file_list = { .files = QLIST_HEAD_INITIALIZER(mem_file_list) };
+
CPUState *first_cpu;
/* current CPU in the current thread. It is only valid inside
cpu_exec() */
@@ -2774,6 +2776,59 @@ void qemu_flush_coalesced_mmio_buffer(void)
kvm_flush_coalesced_mmio_buffer();
}
+#ifdef __linux__
+static void *mem_file_ram_alloc(RAMBlock *block,
+ ram_addr_t memory)
+{
+ void *host;
+ MemFile *mf;
+ struct stat buf;
+ int ret;
+
+ QLIST_FOREACH(mf, &mem_file_list.files, next) {
+ if (strcmp(mf->idstr, block->idstr)) {
+ continue;
+ }
+
+ if (kvm_enabled() && !kvm_has_sync_mmu()) {
+ fprintf(stderr, "host lacks kvm mmu notifiers, "
+ "MemFile unsupported, abort!\n");
+ abort();
+ }
+
+ block->fd = open(mf->path, O_RDWR);
+ if (block->fd == -1) {
+ fprintf(stderr, "Could not open %s for RAMBlock %s, abort!\n",
+ mf->path, mf->idstr);
+ abort();
+ }
+ ret = fstat(block->fd, &buf);
+ if (ret != 0) {
+ fprintf(stderr, "Could not stat %s for RAMBlock %s, abort!\n",
+ mf->path, mf->idstr);
+ abort();
+ }
+ if (buf.st_size != memory) {
+ fprintf(stderr,
+ "File %s has size %luB. RAMBlock %s expects %luB.
Abort!\n",
+ mf->path, buf.st_size, block->idstr, memory);
+ abort();
+ }
+
+ host = mmap(NULL, memory, PROT_READ | PROT_WRITE, MAP_SHARED,
+ block->fd, 0);
+ if (host == MAP_FAILED) {
+ fprintf(stderr, "Failed to mmap %s for RAMBlock %s, abort!\n",
+ mf->path, mf->idstr);
+ abort();
+ }
+ block->do_not_save = 1;
+ return host;
+ }
+ return NULL;
+}
+#endif
+
#if defined(__linux__) && !defined(TARGET_S390X)
#include <sys/vfs.h>
@@ -2914,6 +2969,28 @@ static ram_addr_t last_ram_offset(void)
return last;
}
+void add_memory_file(const char *idstr, const char *path)
+{
+#ifndef __linux__
+ fprintf(stderr, "MemFile only supported on Linux, abort!\n");
+ abort();
+#else
+ MemFile *mf;
+
+ QLIST_FOREACH(mf, &mem_file_list.files, next) {
+ if (!strcmp(mf->idstr, idstr)) {
+ fprintf(stderr, "MemFile for \"%s\" already specified, abort!\n",
+ idstr);
+ abort();
+ }
+ }
+ mf = g_malloc0(sizeof(*mf));
+ mf->idstr = idstr;
+ mf->path = path;
+ QLIST_INSERT_HEAD(&mem_file_list.files, mf, next);
+#endif
+}
+
ram_addr_t qemu_ram_alloc_from_ptr(DeviceState *dev, const char *name,
ram_addr_t size, void *host)
{
@@ -2940,6 +3017,12 @@ ram_addr_t qemu_ram_alloc_from_ptr(DeviceState
*dev, const char *name,
}
new_block->offset = find_ram_offset(size);
+#ifdef __linux__
+ new_block->host = mem_file_ram_alloc(new_block, size);
+ if (new_block->host) {
+ assert(!host);
+ } else
+#endif
if (host) {
new_block->host = host;
new_block->flags |= RAM_PREALLOC_MASK;
diff --git a/qemu-common.h b/qemu-common.h
index 2ce47aa..41adbac 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -306,6 +306,8 @@ char *os_find_datadir(const char *argv0);
void os_parse_cmd_args(int index, const char *optarg);
void os_pidfile_error(void);
+void add_memory_file(const char *idstr, const char *path);
+
/* Convert a byte between binary and BCD. */
static inline uint8_t to_bcd(uint8_t val)
{
diff --git a/qemu-options.hx b/qemu-options.hx
index 681eaf1..25b7c38 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -387,6 +387,19 @@ Preallocate memory when using -mem-path.
ETEXI
#endif
+#ifdef __linux__
+DEF("pcram-file", HAS_ARG, QEMU_OPTION_pcram_file,
+ "-pcram-file FILE provide backing storage for PC RAM\n", QEMU_ARCH_I386)
+STEXI
address@hidden -pcram-file @var{path}
+Populate guest PC RAM with memory mapped file @var{path}. All changes to guest
+ram are reflected in the file (i.e., it is a @code{MAP_SHARED} mapping).
+
+PC RAM is neither migrated nor saved.
+ETEXI
+#endif
+
+
DEF("k", HAS_ARG, QEMU_OPTION_k,
"-k language use keyboard layout (for example 'fr' for French)\n",
QEMU_ARCH_ALL)
diff --git a/vl.c b/vl.c
index f5afed4..2d28797 100644
--- a/vl.c
+++ b/vl.c
@@ -2549,6 +2549,9 @@ int main(int argc, char **argv, char **envp)
ram_size = value;
break;
}
+ case QEMU_OPTION_pcram_file:
+ add_memory_file("pc.ram", optarg);
+ break;
case QEMU_OPTION_mempath:
mem_path = optarg;
break;
- [Qemu-devel] [PATCH] pc: map pc ram from user-specified file,
Peter Feiner <=