Patchwork [10/15] savevm: Migrate RAM based on name/offset

login
register
mail settings
Submitter Alex Williamson
Date June 24, 2010, 4:42 a.m.
Message ID <20100624044202.16168.45991.stgit@localhost.localdomain>
Download mbox | patch
Permalink /patch/56750/
State New
Headers show

Comments

Alex Williamson - June 24, 2010, 4:42 a.m.
Synchronize RAM blocks with the target and migrate using name/offset
pairs.  This ensures both source and target have the same view of
RAM and that we get the right bits into the right slot.

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
---

 arch_init.c |  118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 vl.c        |    2 +
 2 files changed, 108 insertions(+), 12 deletions(-)
Paolo Bonzini - June 24, 2010, 5:49 a.m.
On 06/24/2010 06:42 AM, Alex Williamson wrote:
>           if (flags&  RAM_SAVE_FLAG_COMPRESS) {
> -            uint8_t ch = qemu_get_byte(f);
> -            memset(qemu_get_ram_ptr(addr), ch, TARGET_PAGE_SIZE);
> +            void *host;
> +            uint8_t ch;
> +
> +            if (version_id == 3) {
> +                host = qemu_get_ram_ptr(addr);
> +            } else {
> +                RAMBlock *block;
> +                char id[256];
> +                uint8_t len;
> +
> +                len = qemu_get_byte(f);
> +                qemu_get_buffer(f, (uint8_t *)id, len);
> +                id[len] = 0;

Can you measure the amount of bytes transferred before and now?  It 
seems pretty expensive.  Maybe you could add another flag so that the 
memory name is passed on the wire only if it differs from the previous 
one.  Alternatively (perhaps better) define a new kind of RAM save data 
just for the RAM block, and assume all pages after it are for that 
block.  You can even encode the RAM length in the flags, so that you 
only have to pass "len | RAM_SAVE_FLAG_BLOCK_NAME" followed by the 
length+characters of the name.

Also, code like this:

+                len = qemu_get_byte(f);
+                qemu_get_buffer(f, (uint8_t *)id, len);
+                id[len] = 0;
+
+                QLIST_FOREACH(block, &ram_list.blocks, next) {
+                    if (!strncmp(id, block->idstr, sizeof(id)))
+                        break;
+                }
+                if (!block)
+                    return -EINVAL;

is present three times.  It should be split in two functions and these 
functions be called (the first function can also be used elsewhere, I 
think, though this of course can be done in a followup).

Paolo

Patch

diff --git a/arch_init.c b/arch_init.c
index 109dcef..37aad9d 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -113,20 +113,33 @@  static int ram_save_block(QEMUFile *f)
 
     while (addr < total_ram) {
         if (cpu_physical_memory_get_dirty(current_addr, MIGRATION_DIRTY_FLAG)) {
+            RAMBlock *block;
+            ram_addr_t offset;
             uint8_t *p;
 
             cpu_physical_memory_reset_dirty(current_addr,
                                             current_addr + TARGET_PAGE_SIZE,
                                             MIGRATION_DIRTY_FLAG);
 
-            p = qemu_get_ram_ptr(current_addr);
+            QLIST_FOREACH(block, &ram_list.blocks, next) {
+                if (current_addr - block->offset < block->length)
+                    break;
+            }
+            offset = current_addr - block->offset;
+            p = block->host + offset;
 
             if (is_dup_page(p, *p)) {
-                qemu_put_be64(f, current_addr | RAM_SAVE_FLAG_COMPRESS);
+                qemu_put_be64(f, offset | RAM_SAVE_FLAG_COMPRESS);
+                qemu_put_byte(f, strlen(block->idstr));
+                qemu_put_buffer(f, (uint8_t *)block->idstr,
+                                strlen(block->idstr));
                 qemu_put_byte(f, *p);
                 bytes_sent = 1;
             } else {
-                qemu_put_be64(f, current_addr | RAM_SAVE_FLAG_PAGE);
+                qemu_put_be64(f, offset | RAM_SAVE_FLAG_PAGE);
+                qemu_put_byte(f, strlen(block->idstr));
+                qemu_put_buffer(f, (uint8_t *)block->idstr,
+                                strlen(block->idstr));
                 qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
                 bytes_sent = TARGET_PAGE_SIZE;
             }
@@ -196,6 +209,7 @@  int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
     }
 
     if (stage == 1) {
+        RAMBlock *block;
         uint64_t total_ram = ram_bytes_total();
         bytes_transferred = 0;
 
@@ -210,6 +224,12 @@  int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
         cpu_physical_memory_set_dirty_tracking(1);
 
         qemu_put_be64(f, total_ram | RAM_SAVE_FLAG_MEM_SIZE);
+
+        QLIST_FOREACH(block, &ram_list.blocks, next) {
+            qemu_put_byte(f, strlen(block->idstr));
+            qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr));
+            qemu_put_be64(f, block->length);
+        }
     }
 
     bytes_transferred_last = bytes_transferred;
@@ -257,7 +277,7 @@  int ram_load(QEMUFile *f, void *opaque, int version_id)
     ram_addr_t addr;
     int flags;
 
-    if (version_id != 3) {
+    if (version_id < 3 || version_id > 4) {
         return -EINVAL;
     }
 
@@ -268,23 +288,99 @@  int ram_load(QEMUFile *f, void *opaque, int version_id)
         addr &= TARGET_PAGE_MASK;
 
         if (flags & RAM_SAVE_FLAG_MEM_SIZE) {
-            if (addr != ram_bytes_total()) {
-                return -EINVAL;
+            if (version_id == 3) {
+                if (addr != ram_bytes_total()) {
+                    return -EINVAL;
+                }
+            } else {
+                /* Synchronize RAM block list */
+                char id[256];
+                ram_addr_t length;
+                ram_addr_t total_ram_bytes = addr;
+
+                while (total_ram_bytes) {
+                    RAMBlock *block;
+                    uint8_t len;
+
+                    len = qemu_get_byte(f);
+                    qemu_get_buffer(f, (uint8_t *)id, len);
+                    id[len] = 0;
+                    length = qemu_get_be64(f);
+
+                    QLIST_FOREACH(block, &ram_list.blocks, next) {
+                        if (!strncmp(id, block->idstr, sizeof(id))) {
+                            if (block->length != length)
+                                return -EINVAL;
+                            break;
+                        }
+                    }
+
+                    if (!block) {
+                        if (!qemu_ram_alloc(NULL, id, length))
+                            return -ENOMEM;
+                    }
+
+                    total_ram_bytes -= length;
+                }
             }
         }
 
         if (flags & RAM_SAVE_FLAG_COMPRESS) {
-            uint8_t ch = qemu_get_byte(f);
-            memset(qemu_get_ram_ptr(addr), ch, TARGET_PAGE_SIZE);
+            void *host;
+            uint8_t ch;
+
+            if (version_id == 3) {
+                host = qemu_get_ram_ptr(addr);
+            } else {
+                RAMBlock *block;
+                char id[256];
+                uint8_t len;
+
+                len = qemu_get_byte(f);
+                qemu_get_buffer(f, (uint8_t *)id, len);
+                id[len] = 0;
+
+                QLIST_FOREACH(block, &ram_list.blocks, next) {
+                    if (!strncmp(id, block->idstr, sizeof(id)))
+                        break;
+                }
+                if (!block)
+                    return -EINVAL;
+
+                host = block->host + addr;
+            }
+            ch = qemu_get_byte(f);
+            memset(host, ch, TARGET_PAGE_SIZE);
 #ifndef _WIN32
             if (ch == 0 &&
                 (!kvm_enabled() || kvm_has_sync_mmu())) {
-                madvise(qemu_get_ram_ptr(addr), TARGET_PAGE_SIZE,
-                        MADV_DONTNEED);
+                madvise(host, TARGET_PAGE_SIZE, MADV_DONTNEED);
             }
 #endif
         } else if (flags & RAM_SAVE_FLAG_PAGE) {
-            qemu_get_buffer(f, qemu_get_ram_ptr(addr), TARGET_PAGE_SIZE);
+            void *host;
+
+            if (version_id == 3) {
+                host = qemu_get_ram_ptr(addr);
+            } else {
+                RAMBlock *block;
+                char id[256];
+                uint8_t len;
+
+                len = qemu_get_byte(f);
+                qemu_get_buffer(f, (uint8_t *)id, len);
+                id[len] = 0;
+
+                QLIST_FOREACH(block, &ram_list.blocks, next) {
+                    if (!strncmp(id, block->idstr, sizeof(id)))
+                        break;
+                }
+                if (!block)
+                    return -EINVAL;
+
+                host = block->host + addr;
+            }
+            qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
         }
         if (qemu_file_has_error(f)) {
             return -EIO;
diff --git a/vl.c b/vl.c
index 4a1c5f4..8a5de9f 100644
--- a/vl.c
+++ b/vl.c
@@ -2794,7 +2794,7 @@  int main(int argc, char **argv, char **envp)
     if (qemu_opts_foreach(&qemu_drive_opts, drive_init_func, &machine->use_scsi, 1) != 0)
         exit(1);
 
-    register_savevm_live(NULL, "ram", 0, 3, NULL, ram_save_live, NULL, 
+    register_savevm_live(NULL, "ram", 0, 4, NULL, ram_save_live, NULL,
                          ram_load, NULL);
 
     if (nb_numa_nodes > 0) {