diff options
-rw-r--r-- | include/migration/migration.h | 6 | ||||
-rw-r--r-- | migration/migration.c | 2 | ||||
-rw-r--r-- | migration/savevm.c | 141 | ||||
-rw-r--r-- | trace-events | 4 |
4 files changed, 86 insertions, 67 deletions
diff --git a/include/migration/migration.h b/include/migration/migration.h index 571466beb2..3dc95f49da 100644 --- a/include/migration/migration.h +++ b/include/migration/migration.h @@ -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 */ diff --git a/migration/migration.c b/migration/migration.c index 295deb8a2d..e4f91a98f4 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -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; diff --git a/migration/savevm.c b/migration/savevm.c index 1ffe7b0a57..6b04f46bbc 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -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; + } + + 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; } } - file_error_after_eof = qemu_file_get_error(f); + 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; } diff --git a/trace-events b/trace-events index d61fcf9619..fbb099d8ed 100644 --- a/trace-events +++ b/trace-events @@ -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" |