qemu-devel
[Top][All Lists]
Advanced

[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;



reply via email to

[Prev in Thread] Current Thread [Next in Thread]