Commit 7b89bf27 authored by Dr. David Alan Gilbert's avatar Dr. David Alan Gilbert Committed by Juan Quintela
Browse files

Rework loadvm path for subloops



Postcopy needs to have two migration streams loading concurrently;
one from memory (with the device state) and the other from the fd
with the memory transactions.

Split the core of qemu_loadvm_state out so we can use it for both.

Allow the inner loadvm loop to quit and cause the parent loops to
exit as well.

Signed-off-by: default avatarDr. David Alan Gilbert <dgilbert@redhat.com>
Reviewed-by: default avatarJuan Quintela <quintela@redhat.com>
Reviewed-by: default avatarAmit Shah <amit.shah@redhat.com>
Signed-off-by: default avatarJuan Quintela <quintela@redhat.com>
parent 70b20477
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -57,6 +57,12 @@ typedef QLIST_HEAD(, LoadStateEntry) LoadStateEntry_Head;
struct MigrationIncomingState {
    QEMUFile *from_src_file;

    /*
     * Free at the start of the main state load, set as the main thread finishes
     * loading state.
     */
    QemuEvent main_thread_load_event;

    QEMUFile *to_src_file;
    QemuMutex rp_mutex;    /* We send replies from multiple threads */

+2 −0
Original line number Diff line number Diff line
@@ -98,12 +98,14 @@ MigrationIncomingState *migration_incoming_state_new(QEMUFile* f)
    mis_current->from_src_file = f;
    QLIST_INIT(&mis_current->loadvm_handlers);
    qemu_mutex_init(&mis_current->rp_mutex);
    qemu_event_init(&mis_current->main_thread_load_event, false);

    return mis_current;
}

void migration_incoming_state_destroy(void)
{
    qemu_event_destroy(&mis_current->main_thread_load_event);
    loadvm_free_handlers(mis_current);
    g_free(mis_current);
    mis_current = NULL;
+74 −67
Original line number Diff line number Diff line
@@ -1051,11 +1051,18 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id)
    return NULL;
}

enum LoadVMExitCodes {
    /* Allow a command to quit all layers of nested loadvm loops */
    LOADVM_QUIT     =  1,
};

static int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis);
/**
 * loadvm_process_command: Process an incoming 'QEMU_VM_COMMAND'
 *
 * Returns: 0 on success, negative on error (in which case it will issue an
 *          error message).
 * Returns: 0 on just a normal return
 *          LOADVM_QUIT All good, but exit the loop
 *          <0 error (in which case it will issue an error message).
 * @f: The stream to read the command data from.
 */
static int loadvm_process_command(QEMUFile *f)
@@ -1162,47 +1169,10 @@ void loadvm_free_handlers(MigrationIncomingState *mis)
    }
}

int qemu_loadvm_state(QEMUFile *f)
static int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis)
{
    MigrationIncomingState *mis = migration_incoming_get_current();
    Error *local_err = NULL;
    uint8_t section_type;
    unsigned int v;
    int ret;
    int file_error_after_eof = -1;

    if (qemu_savevm_state_blocked(&local_err)) {
        error_report_err(local_err);
        return -EINVAL;
    }

    v = qemu_get_be32(f);
    if (v != QEMU_VM_FILE_MAGIC) {
        error_report("Not a migration stream");
        return -EINVAL;
    }

    v = qemu_get_be32(f);
    if (v == QEMU_VM_FILE_VERSION_COMPAT) {
        error_report("SaveVM v2 format is obsolete and don't work anymore");
        return -ENOTSUP;
    }
    if (v != QEMU_VM_FILE_VERSION) {
        error_report("Unsupported migration stream version");
        return -ENOTSUP;
    }

    if (!savevm_state.skip_configuration) {
        if (qemu_get_byte(f) != QEMU_VM_CONFIGURATION) {
            error_report("Configuration section missing");
            return -EINVAL;
        }
        ret = vmstate_load_state(f, &vmstate_configuration, &savevm_state, 0);

        if (ret) {
            return ret;
        }
    }

    while ((section_type = qemu_get_byte(f)) != QEMU_VM_EOF) {
        uint32_t instance_id, version_id, section_id;
@@ -1231,16 +1201,14 @@ int qemu_loadvm_state(QEMUFile *f)
            if (se == NULL) {
                error_report("Unknown savevm section or instance '%s' %d",
                             idstr, instance_id);
                ret = -EINVAL;
                goto out;
                return -EINVAL;
            }

            /* Validate version */
            if (version_id > se->version_id) {
                error_report("savevm: unsupported version %d for '%s' v%d",
                             version_id, idstr, se->version_id);
                ret = -EINVAL;
                goto out;
                return -EINVAL;
            }

            /* Add entry */
@@ -1255,11 +1223,10 @@ int qemu_loadvm_state(QEMUFile *f)
            if (ret < 0) {
                error_report("error while loading state for instance 0x%x of"
                             " device '%s'", instance_id, idstr);
                goto out;
                return ret;
            }
            if (!check_section_footer(f, le)) {
                ret = -EINVAL;
                goto out;
                return -EINVAL;
            }
            break;
        case QEMU_VM_SECTION_PART:
@@ -1274,35 +1241,83 @@ int qemu_loadvm_state(QEMUFile *f)
            }
            if (le == NULL) {
                error_report("Unknown savevm section %d", section_id);
                ret = -EINVAL;
                goto out;
                return -EINVAL;
            }

            ret = vmstate_load(f, le->se, le->version_id);
            if (ret < 0) {
                error_report("error while loading state section id %d(%s)",
                             section_id, le->se->idstr);
                goto out;
                return ret;
            }
            if (!check_section_footer(f, le)) {
                ret = -EINVAL;
                goto out;
                return -EINVAL;
            }
            break;
        case QEMU_VM_COMMAND:
            ret = loadvm_process_command(f);
            if (ret < 0) {
                goto out;
            trace_qemu_loadvm_state_section_command(ret);
            if ((ret < 0) || (ret & LOADVM_QUIT)) {
                return ret;
            }
            break;
        default:
            error_report("Unknown savevm section type %d", section_type);
            ret = -EINVAL;
            goto out;
            return -EINVAL;
        }
    }

    return 0;
}

int qemu_loadvm_state(QEMUFile *f)
{
    MigrationIncomingState *mis = migration_incoming_get_current();
    Error *local_err = NULL;
    unsigned int v;
    int ret;

    if (qemu_savevm_state_blocked(&local_err)) {
        error_report_err(local_err);
        return -EINVAL;
    }

    file_error_after_eof = qemu_file_get_error(f);
    v = qemu_get_be32(f);
    if (v != QEMU_VM_FILE_MAGIC) {
        error_report("Not a migration stream");
        return -EINVAL;
    }

    v = qemu_get_be32(f);
    if (v == QEMU_VM_FILE_VERSION_COMPAT) {
        error_report("SaveVM v2 format is obsolete and don't work anymore");
        return -ENOTSUP;
    }
    if (v != QEMU_VM_FILE_VERSION) {
        error_report("Unsupported migration stream version");
        return -ENOTSUP;
    }

    if (!savevm_state.skip_configuration) {
        if (qemu_get_byte(f) != QEMU_VM_CONFIGURATION) {
            error_report("Configuration section missing");
            return -EINVAL;
        }
        ret = vmstate_load_state(f, &vmstate_configuration, &savevm_state, 0);

        if (ret) {
            return ret;
        }
    }

    ret = qemu_loadvm_state_main(f, mis);
    qemu_event_set(&mis->main_thread_load_event);

    trace_qemu_loadvm_state_post_main(ret);

    if (ret == 0) {
        ret = qemu_file_get_error(f);
    }

    /*
     * Try to read in the VMDESC section as well, so that dumping tools that
@@ -1314,10 +1329,10 @@ int qemu_loadvm_state(QEMUFile *f)
     * We also mustn't read data that isn't there; some transports (RDMA)
     * will stall waiting for that data when the source has already closed.
     */
    if (should_send_vmdesc()) {
    if (ret == 0 && should_send_vmdesc()) {
        uint8_t *buf;
        uint32_t size;
        section_type = qemu_get_byte(f);
        uint8_t  section_type = qemu_get_byte(f);

        if (section_type != QEMU_VM_VMDESCRIPTION) {
            error_report("Expected vmdescription section, but got %d",
@@ -1341,14 +1356,6 @@ int qemu_loadvm_state(QEMUFile *f)

    cpu_synchronize_all_post_init();

    ret = 0;

out:
    if (ret == 0) {
        /* We may not have a VMDESC section, so ignore relative errors */
        ret = file_error_after_eof;
    }

    return ret;
}

+4 −0
Original line number Diff line number Diff line
@@ -1202,7 +1202,11 @@ virtio_gpu_fence_resp(uint64_t fence) "fence 0x%" PRIx64

# migration/savevm.c
qemu_loadvm_state_section(unsigned int section_type) "%d"
qemu_loadvm_state_section_command(int ret) "%d"
qemu_loadvm_state_section_partend(uint32_t section_id) "%u"
qemu_loadvm_state_main(void) ""
qemu_loadvm_state_main_quit_parent(void) ""
qemu_loadvm_state_post_main(int ret) "%d"
qemu_loadvm_state_section_startfull(uint32_t section_id, const char *idstr, uint32_t instance_id, uint32_t version_id) "%u(%s) %u %u"
loadvm_process_command(uint16_t com, uint16_t len) "com=0x%x len=%d"
loadvm_process_command_ping(uint32_t val) "%x"