diff mbox

[RFC,11/13] snapshot/migration: Save VM's RAM into snapshot file

Message ID 1452169208-840-12-git-send-email-zhang.zhanghailiang@huawei.com
State New
Headers show

Commit Message

Zhanghailiang Jan. 7, 2016, 12:20 p.m. UTC
For live memory snapshot, we should capture the VM's state
just at the time of getting snapshot command. For VM's RAM,
they may be dirtied during the process of creating snapshot,
Because VM is still running while we create snapshot.
To catch all the action of writing page, we have remove all pages'
write permission by using userfaultfd, and we also have a thread
to deal with the write-protect notification.

Here, we will read the address of page that will be dirtied, and
then save it into a queue. For snapshot thread, it will save the
page into snapshot file and then remove the write-protect.
In this way, we can ensure, the content of the page in snapshot file is
same with the time we got snapshot command.

Signed-off-by: zhanghailiang <zhang.zhanghailiang@huawei.com>
---
 include/migration/postcopy-ram.h |  4 ++++
 migration/migration.c            | 17 +++++++++++++++--
 migration/postcopy-ram.c         | 19 +++++++++++++++----
 migration/ram.c                  | 24 +++++++++++++++++++++---
 4 files changed, 55 insertions(+), 9 deletions(-)
diff mbox

Patch

diff --git a/include/migration/postcopy-ram.h b/include/migration/postcopy-ram.h
index 978a8d7..bc9ce41 100644
--- a/include/migration/postcopy-ram.h
+++ b/include/migration/postcopy-ram.h
@@ -96,6 +96,10 @@  int postcopy_place_page_zero(MigrationIncomingState *mis, void *host);
  */
 void *postcopy_get_tmp_page(MigrationIncomingState *mis);
 
+int ram_set_pages_wp(uint64_t page_addr,
+                     uint64_t size,
+                     bool remove,
+                     int uffd);
 int postcopy_ram_disable_notify(UserfaultState *us);
 
 void qemu_mlock_all_memory(void);
diff --git a/migration/migration.c b/migration/migration.c
index 2001490..3765c3b 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -34,6 +34,7 @@ 
 #include "exec/memory.h"
 #include "exec/address-spaces.h"
 #include "hw/boards.h" /* Fix me: Remove this if we support snapshot for KVM */
+#include <linux/userfaultfd.h>
 
 #define MAX_THROTTLE  (32 << 20)      /* Migration transfer speed throttling */
 
@@ -1780,7 +1781,19 @@  static void *snapshot_thread(void *opaque)
 
     trace_snapshot_thread_setup_complete();
 
-    /* Save VM's Live state, such as RAM */
+    while (qemu_file_get_error(ms->file) == 0) {
+        if (qemu_savevm_state_iterate(ms->file, false) > 0) {
+            break;
+        }
+    }
+
+    ret = qemu_file_get_error(ms->file);
+    if (ret == 0) {
+        qemu_savevm_state_complete_precopy(ms->file, true);
+    } else {
+        migrate_set_state(ms, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_FAILED);
+        goto out;
+    }
 
     qemu_save_buffer_file(ms, buffer);
     ret = qemu_file_get_error(ms->file);
@@ -1790,7 +1803,7 @@  static void *snapshot_thread(void *opaque)
         migrate_set_state(ms, MIGRATION_STATUS_ACTIVE,
                           MIGRATION_STATUS_COMPLETED);
     }
-
+out:
     postcopy_ram_disable_notify(&ms->userfault_state);
 
     qemu_mutex_lock_iothread();
diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c
index 9cd854b..61392d3 100644
--- a/migration/postcopy-ram.c
+++ b/migration/postcopy-ram.c
@@ -377,10 +377,10 @@  int postcopy_ram_prepare_discard(MigrationIncomingState *mis)
     return 0;
 }
 
-static int ram_set_pages_wp(uint64_t page_addr,
-                            uint64_t size,
-                            bool remove,
-                            int uffd)
+int ram_set_pages_wp(uint64_t page_addr,
+                     uint64_t size,
+                     bool remove,
+                     int uffd)
 {
     struct uffdio_writeprotect wp_struct;
 
@@ -539,6 +539,17 @@  static void *postcopy_ram_fault_thread(void *opaque)
                 migrate_send_rp_req_pages(mis, NULL,
                                           rb_offset, hostpagesize);
             }
+        } else { /* UFFDIO_REGISTER_MODE_WP */
+            MigrationState *ms = container_of(us, MigrationState,
+                                              userfault_state);
+            ret = ram_save_queue_pages(ms, qemu_ram_get_idstr(rb), rb_offset,
+                                       hostpagesize);
+
+            if (ret < 0) {
+                error_report("%s: Save: %"PRIx64 " failed!",
+                             __func__, (uint64_t)msg.arg.pagefault.address);
+                break;
+            }
         }
     }
     trace_postcopy_ram_fault_thread_exit();
diff --git a/migration/ram.c b/migration/ram.c
index c87663f..fc4c788 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -734,7 +734,11 @@  static int ram_save_page(QEMUFile *f, RAMBlock* block, ram_addr_t offset,
     ram_addr_t current_addr;
     uint8_t *p;
     int ret;
-    bool send_async = true;
+    /*
+    * For snapshot, we should not be async, or the content of page may be
+    * changed before it is really been saved.
+    */
+    bool send_async = !migration_in_snapshot(migrate_get_current());
 
     p = block->host + offset;
 
@@ -1087,7 +1091,7 @@  static bool get_queued_page(MigrationState *ms, PageSearchStatus *pss,
          * even if this queue request was received after the background
          * search already sent it.
          */
-        if (block) {
+        if (block && !migration_in_snapshot(ms)) {
             unsigned long *bitmap;
             bitmap = atomic_rcu_read(&migration_bitmap_rcu)->bmap;
             dirty = test_bit(*ram_addr_abs >> TARGET_PAGE_BITS, bitmap);
@@ -1351,6 +1355,19 @@  static int ram_find_and_save_block(QEMUFile *f, bool last_stage,
             pages = ram_save_host_page(ms, f, pss.block, &pss.offset,
                                        last_stage, bytes_transferred,
                                        dirty_ram_abs);
+            /* For snapshot, we will remove the page write-protect here */
+            if (migration_in_snapshot(ms)) {
+                int ret;
+                uint64_t host_addr = (uint64_t)(pss.block->host + pss.offset);
+
+                ret = ram_set_pages_wp(host_addr, getpagesize(), true,
+                                       ms->userfault_state.userfault_fd);
+                if (ret < 0) {
+                    error_report("Failed to remove the write-protect for page:"
+                                 "%"PRIx64 " length: %d, block: %s", host_addr,
+                                 getpagesize(), pss.block->idstr);
+                }
+            }
         }
     } while (!pages && again);
 
@@ -2031,7 +2048,8 @@  static int ram_save_complete(QEMUFile *f, void *opaque)
 {
     rcu_read_lock();
 
-    if (!migration_in_postcopy(migrate_get_current())) {
+    if (!migration_in_postcopy(migrate_get_current()) &&
+        !migration_in_snapshot(migrate_get_current())) {
         migration_bitmap_sync();
     }