qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH RFC 2/2] migration: use checkpoint during migration


From: Bohdan Trach
Subject: [Qemu-devel] [PATCH RFC 2/2] migration: use checkpoint during migration
Date: Tue, 24 Nov 2015 17:42:29 +0100

Extend memory page saving and loading functions to utilize information
available in checkpoints to avoid sending full pages over the network.

Signed-off-by: Bohdan Trach <address@hidden>
---
 migration/ram.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
 trace-events    |   5 +++
 2 files changed, 127 insertions(+), 9 deletions(-)

diff --git a/migration/ram.c b/migration/ram.c
index 379a381..79cb143 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -203,6 +203,16 @@ void init_checksum_lookup_table(const char 
*checkpoint_path)
           cmp_hash_offset_entry);
     g_free(pg);
 }
+
+static int is_outgoing_with_checkpoint(void) {
+    return (fd_checkpoint != -1);
+}
+
+static uint32_t get_page_nr(uint64_t addr) {
+    assert((addr % TARGET_PAGE_SIZE) == 0);
+    return (addr / TARGET_PAGE_SIZE);
+}
+
 static int dirty_rate_high_cnt;
 
 static uint64_t bitmap_sync_count;
@@ -219,6 +229,7 @@ static uint64_t bitmap_sync_count;
 #define RAM_SAVE_FLAG_XBZRLE   0x40
 /* 0x80 is reserved in migration.h start with 0x100 next */
 #define RAM_SAVE_FLAG_COMPRESS_PAGE    0x100
+#define RAM_SAVE_FLAG_HASH     0x200
 
 static const uint8_t ZERO_TARGET_PAGE[TARGET_PAGE_SIZE];
 
@@ -887,6 +898,7 @@ static int ram_save_page(QEMUFile *f, RAMBlock* block, 
ram_addr_t offset,
     uint8_t *p;
     int ret;
     bool send_async = true;
+    uint8_t hash[SHA256_DIGEST_LENGTH];
 
     p = block->host + offset;
 
@@ -935,16 +947,32 @@ static int ram_save_page(QEMUFile *f, RAMBlock* block, 
ram_addr_t offset,
 
     /* XBZRLE overflow or normal page */
     if (pages == -1) {
-        *bytes_transferred += save_page_header(f, block,
-                                               offset | RAM_SAVE_FLAG_PAGE);
-        if (send_async) {
-            qemu_put_buffer_async(f, p, TARGET_PAGE_SIZE);
-        } else {
-            qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
+        if (is_outgoing_with_checkpoint()) {
+            SHA256(p, TARGET_PAGE_SIZE, hash);
+
+            if (bsearch(hash, hashes, hashes_entries,
+                        SHA256_DIGEST_LENGTH, uint256_compare) != NULL) {
+
+                *bytes_transferred += save_page_header(f, block, offset | 
RAM_SAVE_FLAG_HASH);
+                qemu_put_buffer(f, hash, SHA256_DIGEST_LENGTH);
+                *bytes_transferred += SHA256_DIGEST_LENGTH;
+                pages = 1;
+                trace_ram_load_send_hash(offset&TARGET_PAGE_MASK, (offset | 
RAM_SAVE_FLAG_HASH)& ~TARGET_PAGE_MASK, sha256s(hash));
+            }
+        }
+        if (pages == -1) {
+            *bytes_transferred += save_page_header(f, block,
+                                                   offset | 
RAM_SAVE_FLAG_PAGE);
+            if (send_async) {
+                qemu_put_buffer_async(f, p, TARGET_PAGE_SIZE);
+            } else {
+                qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
+            }
+            *bytes_transferred += TARGET_PAGE_SIZE;
+            pages = 1;
+            acct_info.norm_pages++;
+            trace_ram_load_send_page(offset&TARGET_PAGE_MASK, (offset | 
RAM_SAVE_FLAG_PAGE)& ~TARGET_PAGE_MASK);
         }
-        *bytes_transferred += TARGET_PAGE_SIZE;
-        pages = 1;
-        acct_info.norm_pages++;
     }
 
     XBZRLE_cache_unlock();
@@ -2540,6 +2568,58 @@ static int ram_load_postcopy(QEMUFile *f)
     return ret;
 }
 
+/**
+ * If migration source determined we already have the chunk, it only
+ * sends a hash of the page's content. Read it from local storage,
+ * e.g., an old checkpoint.
+ * @param host Address which, after this function, should have a content 
matching the functions 2nd parameter.
+ * @param hash The hash value.
+ * @param size Size of the memory region in bytes. Typically, size is a single 
page, e.g., 4 KiB.
+ * @param fd file descriptor of checkpoint file
+ */
+static void ram_handle_hash(void *host, uint64_t guest_phy_addr, uint8_t 
*hash, uint64_t size)
+{
+    assert(fd_checkpoint != -1);
+
+    /* fprintf(stdout, "ram_handle_hash: incoming has %u!\n", hash); */
+    uint8_t local_page_hash[SHA256_DIGEST_LENGTH];
+    SHA256(host, TARGET_PAGE_SIZE, local_page_hash);
+
+    if (0 != memcmp(local_page_hash, hash, SHA256_DIGEST_LENGTH)) {
+        /* Computed hash does not match the hash the migration source
+           sent us for this page. */
+        hash_offset_entry* v = bsearch(hash, hash_offset_array, 
hash_offset_entries,
+                                       sizeof(hash_offset_entry), 
cmp_hash_offset_entry);
+        if (v == NULL) {
+            /* For some reason the source thought the destination
+               already has this block. But it doesn't. Hmmm ... */
+            trace_ram_handle_hash_unknown(sha256s(hash), guest_phy_addr);
+            assert(0);
+        }
+
+        trace_ram_handle_hash(guest_phy_addr, sha256s(hash), v->offset);
+
+        off_t offset_actual = lseek(fd_checkpoint, v->offset, SEEK_SET);
+        assert(offset_actual == v->offset);
+
+        ssize_t read_actual = read(fd_checkpoint, host, TARGET_PAGE_SIZE);
+        assert(read_actual == TARGET_PAGE_SIZE);
+        SHA256(host, TARGET_PAGE_SIZE, local_page_hash);
+        if (0 != memcmp(local_page_hash, hash, SHA256_DIGEST_LENGTH)) {
+            trace_ram_handle_hash_mismatch(sha256s(local_page_hash));
+            assert(0);
+        }
+    }
+}
+
+static void add_remote_hash(ram_addr_t addr, uint8_t *hash) {
+    uint64_t page_nr = get_page_nr(addr);
+    memcpy(&hashes[page_nr * SHA256_DIGEST_LENGTH],
+           hash,
+           SHA256_DIGEST_LENGTH);
+}
+
+
 static int ram_load(QEMUFile *f, void *opaque, int version_id)
 {
     int flags = 0, ret = 0;
@@ -2572,6 +2652,7 @@ static int ram_load(QEMUFile *f, void *opaque, int 
version_id)
         ram_addr_t addr, total_ram_bytes;
         void *host = NULL;
         uint8_t ch;
+        uint8_t hash[SHA256_DIGEST_LENGTH];
 
         addr = qemu_get_be64(f);
         flags = addr & ~TARGET_PAGE_MASK;
@@ -2627,10 +2708,34 @@ static int ram_load(QEMUFile *f, void *opaque, int 
version_id)
         case RAM_SAVE_FLAG_COMPRESS:
             ch = qemu_get_byte(f);
             ram_handle_compressed(host, ch, TARGET_PAGE_SIZE);
+            if (fd_checkpoint != -1) {
+                if (ch != 0) {
+                    SHA256(host, TARGET_PAGE_SIZE, hash);
+                    add_remote_hash(addr, hash);
+                } else {
+                    add_remote_hash(addr, all_zeroes_hash);
+                }
+            }
             break;
+        case RAM_SAVE_FLAG_HASH:
+            host = host_from_stream_offset(f, addr, flags);
+            if (!host) {
+                error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
+                ret = -EINVAL;
+                break;
+            }
 
+            qemu_get_buffer(f, hash, SHA256_DIGEST_LENGTH);
+
+            ram_handle_hash(host, addr, hash, TARGET_PAGE_SIZE);
+            add_remote_hash(addr, hash);
+            break;
         case RAM_SAVE_FLAG_PAGE:
             qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
+            if (fd_checkpoint != -1) {
+                SHA256(host, TARGET_PAGE_SIZE, hash);
+                add_remote_hash(addr, hash);
+            }
             break;
 
         case RAM_SAVE_FLAG_COMPRESS_PAGE:
@@ -2642,6 +2747,10 @@ static int ram_load(QEMUFile *f, void *opaque, int 
version_id)
             }
             qemu_get_buffer(f, compressed_data_buf, len);
             decompress_data_with_multi_threads(compressed_data_buf, host, len);
+            if (fd_checkpoint != -1) {
+                SHA256(host, TARGET_PAGE_SIZE, hash);
+                add_remote_hash(addr, hash);
+            }
             break;
 
         case RAM_SAVE_FLAG_XBZRLE:
@@ -2651,6 +2760,10 @@ static int ram_load(QEMUFile *f, void *opaque, int 
version_id)
                 ret = -EINVAL;
                 break;
             }
+            if (fd_checkpoint != -1) {
+                SHA256(host, TARGET_PAGE_SIZE, hash);
+                add_remote_hash(addr, hash);
+            }
             break;
         case RAM_SAVE_FLAG_EOS:
             /* normal exit */
diff --git a/trace-events b/trace-events
index eee060b..e543821 100644
--- a/trace-events
+++ b/trace-events
@@ -1267,6 +1267,11 @@ ram_save_queue_pages(const char *rbname, size_t start, 
size_t len) "%s: start: %
 allocate_checksum_table(uint64_t  npages) "pages=%" PRIu64
 init_checksum_lookup_table_start(uint64_t ram_size) "ram_size=%" PRIu64
 init_checksum_lookup_table_hash(const char* hash, uint64_t offset) "hash=%s 
offset=%" PRIu64
+ram_load_send_hash(uint64_t addr, uint64_t flags, const char *hash) "addr=%" 
PRIx64 " flags=%" PRIx64 " hash=%s"
+ram_load_send_page(uint64_t addr, uint64_t flags) "addr=%" PRIx64 " flags=%" 
PRIx64
+ram_handle_hash(uint64_t addr, const char *hash, uint64_t offset) "addr=%" 
PRIx64 " hash=%s offset=%" PRIx64
+ram_handle_hash_mismatch(const char *hash) "mismatch: hash=%s"
+ram_handle_hash_unknown(const char *hash, uint64_t offset) "unknown hash %s at 
guest addr %08" PRIx64
 
 # hw/display/qxl.c
 disable qxl_interface_set_mm_time(int qid, uint32_t mm_time) "%d %d"
-- 
2.4.10




reply via email to

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