From: "Dr. David Alan Gilbert" <dgilb...@redhat.com> The PMI holds the state of each page on the incoming side, so that we can tell if the page is missing, already received or there is a request outstanding for it.
Signed-off-by: Dr. David Alan Gilbert <dgilb...@redhat.com> --- include/migration/migration.h | 18 ++++++ include/migration/postcopy-ram.h | 4 ++ include/qemu/typedefs.h | 1 + postcopy-ram.c | 118 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+) diff --git a/include/migration/migration.h b/include/migration/migration.h index 2289254..722c846 100644 --- a/include/migration/migration.h +++ b/include/migration/migration.h @@ -57,6 +57,23 @@ struct MigrationRetPathState { typedef struct MigrationState MigrationState; +/* Postcopy page-map-incoming - data about each page on the inbound side */ + +typedef enum { + POSTCOPY_PMI_MISSING, /* page hasn't yet been received */ + POSTCOPY_PMI_REQUESTED, /* Kernel asked for a page, but we've not got it */ + POSTCOPY_PMI_RECEIVED /* We've got the page */ +} PostcopyPMIState; + +struct PostcopyPMI { + /* TODO: I'm expecting to rework this using some atomic compare-exchange + * thing, which will require merging the maps together + */ + QemuMutex mutex; + unsigned long *received_map; /* Pages that we have received */ + unsigned long *requested_map; /* Pages that we're sending a request for */ +}; + /* State for the incoming migration */ struct MigrationIncomingState { QEMUFile *file; @@ -71,6 +88,7 @@ struct MigrationIncomingState { QEMUFile *return_path; QemuMutex rp_mutex; /* We send replies from multiple threads */ + PostcopyPMI postcopy_pmi; }; MigrationIncomingState *migration_incoming_state_init(QEMUFile *f); diff --git a/include/migration/postcopy-ram.h b/include/migration/postcopy-ram.h index fe89a3c..75ca0fd 100644 --- a/include/migration/postcopy-ram.h +++ b/include/migration/postcopy-ram.h @@ -36,4 +36,8 @@ int postcopy_ram_discard_range(MigrationIncomingState *mis, uint8_t *start, int postcopy_send_discard_bm_ram(MigrationState *ms, const char *name, unsigned long start, unsigned long end); +void postcopy_pmi_destroy(MigrationIncomingState *mis); +void postcopy_pmi_discard_range(MigrationIncomingState *mis, + size_t start, size_t npages); + #endif diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 8539de6..61b330c 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -77,6 +77,7 @@ typedef struct QEMUSGList QEMUSGList; typedef struct SHPCDevice SHPCDevice; typedef struct FWCfgState FWCfgState; typedef struct PcGuestInfo PcGuestInfo; +typedef struct PostcopyPMI PostcopyPMI; typedef struct Range Range; typedef struct AdapterInfo AdapterInfo; diff --git a/postcopy-ram.c b/postcopy-ram.c index ff6bdd6..f92f516 100644 --- a/postcopy-ram.c +++ b/postcopy-ram.c @@ -23,6 +23,8 @@ #include "qemu-common.h" #include "migration/migration.h" #include "migration/postcopy-ram.h" +#include "qemu/bitmap.h" +#include "qemu/error-report.h" #include "sysemu/sysemu.h" //#define DEBUG_POSTCOPY @@ -66,6 +68,122 @@ #define __NR_remap_anon_pages 317 #endif +/* ---------------------------------------------------------------------- */ +/* Postcopy pagemap-inbound (pmi) - data structures that record the */ +/* state of each page used by the inbound postcopy */ + +static void postcopy_pmi_init(MigrationIncomingState *mis, size_t ram_pages) +{ + qemu_mutex_init(&mis->postcopy_pmi.mutex); + mis->postcopy_pmi.received_map = bitmap_new(ram_pages); + mis->postcopy_pmi.requested_map = bitmap_new(ram_pages); + bitmap_clear(mis->postcopy_pmi.received_map, 0, ram_pages); + bitmap_clear(mis->postcopy_pmi.requested_map, 0, ram_pages); +} + +void postcopy_pmi_destroy(MigrationIncomingState *mis) +{ + if (mis->postcopy_pmi.received_map) { + g_free(mis->postcopy_pmi.received_map); + mis->postcopy_pmi.received_map = NULL; + } + if (mis->postcopy_pmi.requested_map) { + g_free(mis->postcopy_pmi.requested_map); + mis->postcopy_pmi.requested_map = NULL; + } + qemu_mutex_destroy(&mis->postcopy_pmi.mutex); +} + +/* + * Mark a set of pages in the PMI as being clear; this is used by the discard + * at the start of postcopy, and before the postcopy stream starts. + */ +void postcopy_pmi_discard_range(MigrationIncomingState *mis, + size_t start, size_t npages) +{ + bitmap_clear(mis->postcopy_pmi.received_map, start, npages); +} + +/* + * Retrieve the state of the given page + * Note: This version for use by callers already holding the lock + */ +static PostcopyPMIState postcopy_pmi_get_state_nolock( + MigrationIncomingState *mis, + size_t bitmap_index) +{ + bool received, requested; + + received = test_bit(bitmap_index, mis->postcopy_pmi.received_map); + requested = test_bit(bitmap_index, mis->postcopy_pmi.requested_map); + + if (received) { + assert(!requested); + return POSTCOPY_PMI_RECEIVED; + } else { + return requested ? POSTCOPY_PMI_REQUESTED : POSTCOPY_PMI_MISSING; + } +} + +/* Retrieve the state of the given page */ +static PostcopyPMIState postcopy_pmi_get_state(MigrationIncomingState *mis, + size_t bitmap_index) +{ + PostcopyPMIState ret; + qemu_mutex_lock(&mis->postcopy_pmi.mutex); + ret = postcopy_pmi_get_state_nolock(mis, bitmap_index); + qemu_mutex_unlock(&mis->postcopy_pmi.mutex); + + return ret; +} + +/* + * Set the page state to the given state if the previous state was as expected + * Return the actual previous state. + */ +static PostcopyPMIState postcopy_pmi_change_state(MigrationIncomingState *mis, + size_t bitmap_index, + PostcopyPMIState expected_state, + PostcopyPMIState new_state) +{ + PostcopyPMIState old_state; + + qemu_mutex_lock(&mis->postcopy_pmi.mutex); + old_state = postcopy_pmi_get_state_nolock(mis, bitmap_index); + + if (old_state == expected_state) { + switch (new_state) { + case POSTCOPY_PMI_MISSING: + assert(0); /* This shouldn't actually happen - use discard_range */ + break; + + case POSTCOPY_PMI_REQUESTED: + assert(old_state == POSTCOPY_PMI_MISSING); + set_bit(bitmap_index, mis->postcopy_pmi.requested_map); + break; + + case POSTCOPY_PMI_RECEIVED: + assert(old_state == POSTCOPY_PMI_MISSING || + old_state == POSTCOPY_PMI_REQUESTED); + set_bit(bitmap_index, mis->postcopy_pmi.received_map); + clear_bit(bitmap_index, mis->postcopy_pmi.requested_map); + break; + } + } + + qemu_mutex_unlock(&mis->postcopy_pmi.mutex); + return old_state; +} + +static void postcopy_pmi_dump(MigrationIncomingState *mis) +{ + fprintf(stderr, "postcopy_pmi_dump: requested\n"); + ram_debug_dump_bitmap(mis->postcopy_pmi.requested_map, false); + fprintf(stderr, "postcopy_pmi_dump: received\n"); + ram_debug_dump_bitmap(mis->postcopy_pmi.received_map, true); +} + +/* ---------------------------------------------------------------------- */ int postcopy_ram_hosttest(void) { /* TODO: Needs guarding with CONFIG_ once we have libc's that have the defs -- 1.9.3