GlobalProperty hw_compat_7_2[] = {
{ "e1000e", "migrate-timadj", "off" },
{ "virtio-mem", "x-early-migration", "false" },
+ { "migration", "x-preempt-pre-7-2", "true" },
};
const size_t hw_compat_7_2_len = G_N_ELEMENTS(hw_compat_7_2);
qemu_savevm_state_complete_postcopy(s->to_dst_file);
qemu_mutex_unlock_iothread();
- /* Shutdown the postcopy fast path thread */
- if (migrate_postcopy_preempt()) {
+ /*
+ * Shutdown the postcopy fast path thread. This is only needed
+ * when dest QEMU binary is old (7.1/7.2). QEMU 8.0+ doesn't need
+ * this.
+ */
+ if (migrate_postcopy_preempt() && s->preempt_pre_7_2) {
postcopy_preempt_shutdown_file(s);
}
decompress_error_check, true),
DEFINE_PROP_UINT8("x-clear-bitmap-shift", MigrationState,
clear_bitmap_shift, CLEAR_BITMAP_SHIFT_DEFAULT),
+ DEFINE_PROP_BOOL("x-preempt-pre-7-2", MigrationState,
+ preempt_pre_7_2, false),
/* Migration parameters */
DEFINE_PROP_UINT8("x-compress-level", MigrationState,
bool all_zero;
} PostcopyTmpPage;
+typedef enum {
+ PREEMPT_THREAD_NONE = 0,
+ PREEMPT_THREAD_CREATED,
+ PREEMPT_THREAD_QUIT,
+} PreemptThreadStatus;
+
/* State for the incoming migration */
struct MigrationIncomingState {
QEMUFile *from_src_file;
QemuSemaphore postcopy_qemufile_dst_done;
/* Postcopy priority thread is used to receive postcopy requested pages */
QemuThread postcopy_prio_thread;
- bool postcopy_prio_thread_created;
+ /*
+ * Always set by the main vm load thread only, but can be read by the
+ * postcopy preempt thread. "volatile" makes sure all reads will be
+ * uptodate across cores.
+ */
+ volatile PreemptThreadStatus preempt_thread_status;
/*
* Used to sync between the ram load main thread and the fast ram load
* thread. It protects postcopy_qemufile_dst, which is the postcopy
* do not trigger spurious decompression errors.
*/
bool decompress_error_check;
+ /*
+ * This variable only affects behavior when postcopy preempt mode is
+ * enabled.
+ *
+ * When set:
+ *
+ * - postcopy preempt src QEMU instance will generate an EOS message at
+ * the end of migration to shut the preempt channel on dest side.
+ *
+ * When clear:
+ *
+ * - postcopy preempt src QEMU instance will _not_ generate an EOS
+ * message at the end of migration, the dest qemu will shutdown the
+ * channel itself.
+ *
+ * NOTE: See message-id <ZBoShWArKDPpX/D7@work-vm> on qemu-devel
+ * mailing list for more information on the possible race. Everyone
+ * should probably just keep this value untouched after set by the
+ * machine type (or the default).
+ */
+ bool preempt_pre_7_2;
/*
* This decides the size of guest memory chunk that will be used
{
trace_postcopy_ram_incoming_cleanup_entry();
- if (mis->postcopy_prio_thread_created) {
+ if (mis->preempt_thread_status == PREEMPT_THREAD_CREATED) {
+ /* Notify the fast load thread to quit */
+ mis->preempt_thread_status = PREEMPT_THREAD_QUIT;
+ if (mis->postcopy_qemufile_dst) {
+ qemu_file_shutdown(mis->postcopy_qemufile_dst);
+ }
qemu_thread_join(&mis->postcopy_prio_thread);
- mis->postcopy_prio_thread_created = false;
+ mis->preempt_thread_status = PREEMPT_THREAD_NONE;
}
if (mis->have_fault_thread) {
*/
postcopy_thread_create(mis, &mis->postcopy_prio_thread, "fault-fast",
postcopy_preempt_thread, QEMU_THREAD_JOINABLE);
- mis->postcopy_prio_thread_created = true;
+ mis->preempt_thread_status = PREEMPT_THREAD_CREATED;
}
trace_postcopy_ram_enable_notify();
trace_postcopy_pause_fast_load_continued();
}
+static bool preempt_thread_should_run(MigrationIncomingState *mis)
+{
+ return mis->preempt_thread_status != PREEMPT_THREAD_QUIT;
+}
+
void *postcopy_preempt_thread(void *opaque)
{
MigrationIncomingState *mis = opaque;
/* Sending RAM_SAVE_FLAG_EOS to terminate this thread */
qemu_mutex_lock(&mis->postcopy_prio_thread_mutex);
- while (1) {
+ while (preempt_thread_should_run(mis)) {
ret = ram_load_postcopy(mis->postcopy_qemufile_dst,
RAM_CHANNEL_POSTCOPY);
/* If error happened, go into recovery routine */
- if (ret) {
+ if (ret && preempt_thread_should_run(mis)) {
postcopy_pause_ram_fast_load(mis);
} else {
/* We're done */