From nobody Sat Feb 7 05:49:09 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1769523104; cv=none; d=zohomail.com; s=zohoarc; b=VxpQA+tq1aEVQf/MBtNwHkZvAvZpZymA9//Ztz2r+voJ3GSgj+U+L9aY9dCk746sFLtaC/QXc4arUVn7b5KkY2qBujL2sp+tVvPCwBnyhJWPPBB6s8+0n44JmF0DL6FnUY7qU4PqnMD2yVKioekIWNl9hQtuBYys2eZ0dxlZycs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1769523104; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=2Rg3iQadC32mqph8pX7W1m8PM4HTN1EGy3Hw3m4jXNM=; b=FWmwVFHWG8KsXDYCr5Wk/QFS/K3DJuqXIsrnf+Z2dQeuDybswc86sx1M6i/f495jfKmCBK2oo0g2wmAxpOsRevNkerJyb6LiBV5eDLg5dbZVR50JnSnAjwWAhS7ntD4/pCtCLwchSCQV+jM/MlkMxoKsSEBwZI5G1fYkZv/5dqs= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1769523104652617.5215163167308; Tue, 27 Jan 2026 06:11:44 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vkjgq-0007tH-Hc; Tue, 27 Jan 2026 09:05:08 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vkjgm-0007rO-7e for qemu-devel@nongnu.org; Tue, 27 Jan 2026 09:05:04 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vkjgk-0007BQ-76 for qemu-devel@nongnu.org; Tue, 27 Jan 2026 09:05:03 -0500 Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-121-HOPFirinPvWZuHUq3JuBSw-1; Tue, 27 Jan 2026 09:03:46 -0500 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 5186A19775DE; Tue, 27 Jan 2026 14:03:44 +0000 (UTC) Received: from fedora (unknown [10.43.3.182]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 368F518008FF; Tue, 27 Jan 2026 14:03:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769522701; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=2Rg3iQadC32mqph8pX7W1m8PM4HTN1EGy3Hw3m4jXNM=; b=QC3rTEYl4wgjWWmCLdIYxkPVfx2m6LIHUdCPjzDWq1vV8NUdCbGJ8De01QJpfzf91uE+/7 GN0dhoQ6JICH6XVGcrMbRONFjMPqb8+BRvgRawF6LRmoRBGTnxOMVrU7IsAFE1RLSLbHGy Kfmp+rAmUQYp4WsUjrN7iAe8qL+drgo= X-MC-Unique: HOPFirinPvWZuHUq3JuBSw-1 X-Mimecast-MFC-AGG-ID: HOPFirinPvWZuHUq3JuBSw_1769522624 From: Juraj Marcin To: qemu-devel@nongnu.org Cc: Juraj Marcin , Fabiano Rosas , "Michael S. Tsirkin" , Peter Xu , Jason Wang , Vladimir Sementsov-Ogievskiy Subject: [PATCH 1/4] migration/qemu-file: Add ability to clear error Date: Tue, 27 Jan 2026 15:03:07 +0100 Message-ID: <20260127140316.4187221-2-jmarcin@redhat.com> In-Reply-To: <20260127140316.4187221-1-jmarcin@redhat.com> References: <20260127140316.4187221-1-jmarcin@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=jmarcin@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1769523106122154100 Content-Type: text/plain; charset="utf-8" From: Juraj Marcin Signed-off-by: Juraj Marcin --- migration/qemu-file.c | 6 ++++++ migration/qemu-file.h | 1 + 2 files changed, 7 insertions(+) diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 9cf7dc3bd5..bdf6c73d3d 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -227,6 +227,12 @@ void qemu_file_set_error(QEMUFile *f, int ret) qemu_file_set_error_obj(f, ret, NULL); } =20 +void qemu_file_clear_error(QEMUFile *f) +{ + f->last_error =3D 0; + error_free(f->last_error_obj); +} + static bool qemu_file_is_writable(QEMUFile *f) { return f->is_writable; diff --git a/migration/qemu-file.h b/migration/qemu-file.h index a8e9bb2ccb..aa24196ffb 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -68,6 +68,7 @@ int qemu_file_get_error_obj_any(QEMUFile *f1, QEMUFile *f= 2, Error **errp); void qemu_file_set_error_obj(QEMUFile *f, int ret, Error *err); int qemu_file_get_error_obj(QEMUFile *f, Error **errp); void qemu_file_set_error(QEMUFile *f, int ret); +void qemu_file_clear_error(QEMUFile *f); int qemu_file_shutdown(QEMUFile *f); QEMUFile *qemu_file_get_return_path(QEMUFile *f); int qemu_fflush(QEMUFile *f); --=20 2.52.0 From nobody Sat Feb 7 05:49:09 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1769523136; cv=none; d=zohomail.com; s=zohoarc; b=IqRubzTbpgQaotDtMVaD/Bkl08dJYjnJOcjOlj8PU/X7hUoVB/yoQ0WsRLXnYyJd7y0uYGp/3DSl6ar3qgOzclZYZ37t7ICIyXiza0uZnw0kRN5auoTWW2K5VojBy/oxbBSX+0u8H6oYfRfOTpc0xaIZKn93QXGFAAd1r43b3tE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1769523136; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=EgI2iIGYTMzBG+3WYg2+H0hPIP9D8VvWshMQU6D9iDk=; b=gI4+ixyQlp+cNoV5krRtCB5DptrFKl8ObOpLJL0AqpfGSWR5Lsvhgx3bNYBEfumD9DybdhmFAAL734JwnXx2Uv0UlUc2aj+uP4tjzkTAZ/31GHMoSzhruqpiq+wL2PhvzSMJB5qPpIbdivx0VwhLvyR7tq8cxj/zT5jA0AMXjTs= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1769523136539415.93060120087637; Tue, 27 Jan 2026 06:12:16 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vkjhA-00089O-PR; Tue, 27 Jan 2026 09:05:28 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vkjh5-00083o-Up for qemu-devel@nongnu.org; Tue, 27 Jan 2026 09:05:26 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vkjh3-0007QG-Rv for qemu-devel@nongnu.org; Tue, 27 Jan 2026 09:05:23 -0500 Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-206-K7dgzMoRNoKRp5Zg_X5cvA-1; Tue, 27 Jan 2026 09:03:50 -0500 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 9E7011955D93; Tue, 27 Jan 2026 14:03:49 +0000 (UTC) Received: from fedora (unknown [10.43.3.182]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 88B9F18007D2; Tue, 27 Jan 2026 14:03:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769522719; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=EgI2iIGYTMzBG+3WYg2+H0hPIP9D8VvWshMQU6D9iDk=; b=UWpctH7vXbidlsu8cxcw+/nui/qu0wPwZ/TMGXeE7sjF0rZEhnIS3WBnBMSFq3RWegCWH8 rPoYxI3KIPC/dZFUS0GA6YFg96JG0acl/JejBJ6yLxepW9cEZyFs5ql6j0QFsaOpxMagwi y6D2QMXsI9bme/oVfhlXJRu3jeIUmoM= X-MC-Unique: K7dgzMoRNoKRp5Zg_X5cvA-1 X-Mimecast-MFC-AGG-ID: K7dgzMoRNoKRp5Zg_X5cvA_1769522630 From: Juraj Marcin To: qemu-devel@nongnu.org Cc: Juraj Marcin , Fabiano Rosas , "Michael S. Tsirkin" , Peter Xu , Jason Wang , Vladimir Sementsov-Ogievskiy Subject: [PATCH 2/4] migration: Introduce VM_STARTED return-path message Date: Tue, 27 Jan 2026 15:03:08 +0100 Message-ID: <20260127140316.4187221-3-jmarcin@redhat.com> In-Reply-To: <20260127140316.4187221-1-jmarcin@redhat.com> References: <20260127140316.4187221-1-jmarcin@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=jmarcin@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1769523138321154100 Content-Type: text/plain; charset="utf-8" From: Juraj Marcin Currently there is no universal way for the destination to tell the source it has started. In precopy it could be deduced from the RP_SHUT message and in postcopy from the response to the ping just before the POSTCOPY_RUN command, but neither method is precise. Moreover, there is no way to send more data after the destination has started with precopy migration. This patch adds new message type to the return-path which tells the source that the destination VM has just started (or can be started if autostart is false). Source VM can use this message to precisely calculate the downtime regardless of if postcopy is used and can also send more data, for example network packets. Signed-off-by: Juraj Marcin --- hw/core/machine.c | 4 +++- migration/migration.c | 34 ++++++++++++++++++++++++++++++---- migration/migration.h | 9 +++++++++ migration/options.c | 8 ++++++++ migration/options.h | 1 + migration/savevm.c | 3 +++ 6 files changed, 54 insertions(+), 5 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 6411e68856..dc73217a5f 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -38,7 +38,9 @@ #include "hw/acpi/generic_event_device.h" #include "qemu/audio.h" =20 -GlobalProperty hw_compat_10_2[] =3D {}; +GlobalProperty hw_compat_10_2[] =3D { + { "migration", "send-vm-started", "off" }, +}; const size_t hw_compat_10_2_len =3D G_N_ELEMENTS(hw_compat_10_2); =20 GlobalProperty hw_compat_10_1[] =3D { diff --git a/migration/migration.c b/migration/migration.c index b103a82fc0..4871db2365 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -82,6 +82,7 @@ enum mig_rp_message_type { MIG_RP_MSG_RECV_BITMAP, /* send recved_bitmap back to source */ MIG_RP_MSG_RESUME_ACK, /* tell source that we are ready to resume */ MIG_RP_MSG_SWITCHOVER_ACK, /* Tell source it's OK to do switchover */ + MIG_RP_MSG_VM_STARTED, /* tell source destination has started */ =20 MIG_RP_MSG_MAX }; @@ -750,6 +751,10 @@ static void process_incoming_migration_bh(void *opaque) runstate_set(global_state_get_runstate()); } trace_vmstate_downtime_checkpoint("dst-precopy-bh-vm-started"); + if (mis->to_src_file && migrate_send_vm_started()) { + migrate_send_rp_vm_started(mis); + } + /* * This must happen after any state changes since as soon as an extern= al * observer sees this event they might start to prod at the VM assuming @@ -996,6 +1001,11 @@ void migrate_send_rp_resume_ack(MigrationIncomingStat= e *mis, uint32_t value) migrate_send_rp_message(mis, MIG_RP_MSG_RESUME_ACK, sizeof(buf), &buf); } =20 +void migrate_send_rp_vm_started(MigrationIncomingState *mis) +{ + migrate_send_rp_message(mis, MIG_RP_MSG_VM_STARTED, 0, NULL); +} + bool migration_is_running(void) { MigrationState *s =3D current_migration; @@ -1660,6 +1670,9 @@ int migrate_init(MigrationState *s, Error **errp) s->postcopy_package_loaded =3D false; qemu_event_reset(&s->postcopy_package_loaded_event); =20 + s->dest_vm_started =3D false; + qemu_event_reset(&s->dest_vm_started_event); + return 0; } =20 @@ -2368,6 +2381,12 @@ static void *source_return_path_thread(void *opaque) trace_source_return_path_thread_switchover_acked(); break; =20 + case MIG_RP_MSG_VM_STARTED: + migration_downtime_end(ms); + ms->dest_vm_started =3D true; + qemu_event_set(&ms->dest_vm_started_event); + break; + default: break; } @@ -2591,7 +2610,9 @@ static int postcopy_start(MigrationState *ms, Error *= *errp) */ migration_call_notifiers(MIG_EVENT_PRECOPY_DONE, NULL); =20 - migration_downtime_end(ms); + if (!ms->rp_state.rp_thread_created || !migrate_send_vm_started()) { + migration_downtime_end(ms); + } =20 if (migrate_postcopy_ram()) { /* @@ -3086,7 +3107,9 @@ static void migration_completion_end(MigrationState *= s) * - correct ordering of s->mbps update vs. s->state; */ bql_lock(); - migration_downtime_end(s); + if (!s->rp_state.rp_thread_created || !migrate_send_vm_started()) { + migration_downtime_end(s); + } s->total_time =3D end_time - s->start_time; transfer_time =3D s->total_time - s->setup_time; if (transfer_time) { @@ -3300,9 +3323,10 @@ static void migration_iteration_finish(MigrationStat= e *s) case MIGRATION_STATUS_FAILED: case MIGRATION_STATUS_CANCELLED: case MIGRATION_STATUS_CANCELLING: - if (!migration_block_activate(&local_err)) { + if (s->dest_vm_started || !migration_block_activate(&local_err)) { /* - * Re-activate the block drives if they're inactivated. + * Re-activate the block drives if they're inactivated and the = dest + * vm has not reported that it has started. * * If it fails (e.g. in case of a split brain, where dest QEMU * might have taken some of the drive locks and running!), do @@ -3853,6 +3877,7 @@ static void migration_instance_finalize(Object *obj) qemu_sem_destroy(&ms->postcopy_qemufile_src_sem); error_free(ms->error); qemu_event_destroy(&ms->postcopy_package_loaded_event); + qemu_event_destroy(&ms->dest_vm_started_event); } =20 static void migration_instance_init(Object *obj) @@ -3875,6 +3900,7 @@ static void migration_instance_init(Object *obj) qemu_sem_init(&ms->postcopy_qemufile_src_sem, 0); qemu_mutex_init(&ms->qemu_file_lock); qemu_event_init(&ms->postcopy_package_loaded_event, 0); + qemu_event_init(&ms->dest_vm_started_event, false); } =20 /* diff --git a/migration/migration.h b/migration/migration.h index b6888daced..a3fab4f27e 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -522,6 +522,14 @@ struct MigrationState { * anything as input. */ bool has_block_bitmap_mapping; + + /* + * Do send VM_START message on the return-path when dest VM finishes + * loading device state and switches out of INMIGRATE run state. + */ + bool send_vm_started; + bool dest_vm_started; + QemuEvent dest_vm_started_event; }; =20 void migrate_set_state(MigrationStatus *state, MigrationStatus old_state, @@ -564,6 +572,7 @@ void migrate_send_rp_recv_bitmap(MigrationIncomingState= *mis, char *block_name); void migrate_send_rp_resume_ack(MigrationIncomingState *mis, uint32_t valu= e); int migrate_send_rp_switchover_ack(MigrationIncomingState *mis); +void migrate_send_rp_vm_started(MigrationIncomingState *mis); =20 void dirty_bitmap_mig_before_vm_start(void); void dirty_bitmap_mig_cancel_outgoing(void); diff --git a/migration/options.c b/migration/options.c index 1ffe85a2d8..a5a233183b 100644 --- a/migration/options.c +++ b/migration/options.c @@ -108,6 +108,7 @@ const Property migration_properties[] =3D { preempt_pre_7_2, false), DEFINE_PROP_BOOL("multifd-clean-tls-termination", MigrationState, multifd_clean_tls_termination, true), + DEFINE_PROP_BOOL("send-vm-started", MigrationState, send_vm_started, t= rue), =20 /* Migration parameters */ DEFINE_PROP_UINT8("x-throttle-trigger-threshold", MigrationState, @@ -434,6 +435,13 @@ bool migrate_zero_copy_send(void) return s->capabilities[MIGRATION_CAPABILITY_ZERO_COPY_SEND]; } =20 +bool migrate_send_vm_started(void) +{ + MigrationState *s =3D migrate_get_current(); + + return s->send_vm_started; +} + /* pseudo capabilities */ =20 bool migrate_multifd_flush_after_each_section(void) diff --git a/migration/options.h b/migration/options.h index b502871097..5fdc8fc6fe 100644 --- a/migration/options.h +++ b/migration/options.h @@ -42,6 +42,7 @@ bool migrate_return_path(void); bool migrate_validate_uuid(void); bool migrate_xbzrle(void); bool migrate_zero_copy_send(void); +bool migrate_send_vm_started(void); =20 /* * pseudo capabilities diff --git a/migration/savevm.c b/migration/savevm.c index 3dc812a7bb..1020094fc8 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2157,6 +2157,9 @@ static void loadvm_postcopy_handle_run_bh(void *opaqu= e) } =20 trace_vmstate_downtime_checkpoint("dst-postcopy-bh-vm-started"); + if (mis->to_src_file && migrate_send_vm_started()) { + migrate_send_rp_vm_started(mis); + } } =20 /* After all discards we can start running and asking for pages */ --=20 2.52.0 From nobody Sat Feb 7 05:49:09 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1769523048; cv=none; d=zohomail.com; s=zohoarc; b=UQqgvI6w9zovwo0Xo1zn7UFLcapFgdBRdXoMn98J/i2uxvyM2BLhCoNd2mLCHcxwyNprpdnnQZmK4L2QsnS5cTqCbUzADufHtuBwNtliEEscboNhQ3PR5Qr7ZiZ2S1J8fXoiWTYDLOJ9sf1D8rs6u0pnz91K19aQFnkMMxpLyj4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1769523048; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=RhnzShHRyi4TuqfDez8LmHbT/EVu54Nc0/hZC7DJTzM=; b=N5pG0X04e8K0DP1hOPaM2UtBQlHPnn4G8MkQV6JilQ18yBhPGk+PEZoxj9B6vo/96B+xcmcv2YPjQ/x5GGHcxUcH1Kjwj0Mfh9HICd65GMVG/mIn571orzpFXQOEqtMCM8Xn7a5k7ibEo0WKfT1F05dibaeet3lN1Sf1NVUk6ZU= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1769523048626998.7467132610626; Tue, 27 Jan 2026 06:10:48 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vkjh0-0007xo-1T; Tue, 27 Jan 2026 09:05:18 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vkjgw-0007wc-KD for qemu-devel@nongnu.org; Tue, 27 Jan 2026 09:05:15 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vkjgu-0007NS-Gt for qemu-devel@nongnu.org; Tue, 27 Jan 2026 09:05:14 -0500 Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-511-ZeS_96mLNSqIsBSWF6L0Rg-1; Tue, 27 Jan 2026 09:03:55 -0500 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id F22A81956096; Tue, 27 Jan 2026 14:03:53 +0000 (UTC) Received: from fedora (unknown [10.43.3.182]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id B5EA81800665; Tue, 27 Jan 2026 14:03:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769522710; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=RhnzShHRyi4TuqfDez8LmHbT/EVu54Nc0/hZC7DJTzM=; b=W+tx2scFWDDC6JhXw2X7mabyF+kiYvkdZzDM0kYDz9fmtmFbQq9WwtbpF9PjhI5/J/XBYC cc5bEXw8w7Mesub4mztyf7D/h530kzVuaTt1/c6eNlEyFtZdazgotDZg60nrg0zGs+keYG J+/PlauwPBoWhIrxHBJuKDsl5+pSLY0= X-MC-Unique: ZeS_96mLNSqIsBSWF6L0Rg-1 X-Mimecast-MFC-AGG-ID: ZeS_96mLNSqIsBSWF6L0Rg_1769522634 From: Juraj Marcin To: qemu-devel@nongnu.org Cc: Juraj Marcin , Fabiano Rosas , "Michael S. Tsirkin" , Peter Xu , Jason Wang , Vladimir Sementsov-Ogievskiy Subject: [PATCH 3/4] migration: Convert VMSD early_setup into VMStateSavePhase enum Date: Tue, 27 Jan 2026 15:03:09 +0100 Message-ID: <20260127140316.4187221-4-jmarcin@redhat.com> In-Reply-To: <20260127140316.4187221-1-jmarcin@redhat.com> References: <20260127140316.4187221-1-jmarcin@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=jmarcin@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1769523049763158500 Content-Type: text/plain; charset="utf-8" From: Juraj Marcin This allows devices to specify when during migration their fields should be saved. For now there are two Save Phases defined. EARLY_START These devices are saved during qemu_savevm_state_setup(), and corresponds to migration SETUP state, same behavior as the former early_setup flag. COMPLETE These devices are saved during migration completion or switch-over with qemu_savevm_state_complete_precopy_non_iterable(), this corresponds to the migration DEVICE state. This is the default phase if none is specified explicitly. This also allows introduction of other phases in the future, for example ITERATE_LIVE and POSTCOPY once support for iterative devices and postcopy is implemented in VMSD, and for the NETPASS phase implemented in this series. Signed-off-by: Juraj Marcin --- hw/virtio/virtio-mem.c | 2 +- include/migration/vmstate.h | 27 +++++++++++++++++++-------- migration/savevm.c | 4 ++-- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index c1e2defb68..6d3e3746e5 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -1438,7 +1438,7 @@ static const VMStateDescription vmstate_virtio_mem_de= vice_early =3D { .name =3D "virtio-mem-device-early", .minimum_version_id =3D 1, .version_id =3D 1, - .early_setup =3D true, + .phase =3D VMS_PHASE_EARLY_SETUP, .post_load =3D virtio_mem_post_load_early, .fields =3D (const VMStateField[]) { VMSTATE_WITH_TMP(VirtIOMEM, VirtIOMEMMigSanityChecks, diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index ed9095a466..62d7e9fe38 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -186,18 +186,29 @@ struct VMStateField { bool (*field_exists)(void *opaque, int version_id); }; =20 -struct VMStateDescription { - const char *name; - bool unmigratable; +typedef enum { /* - * This VMSD describes something that should be sent during setup phase - * of migration. It plays similar role as save_setup() for explicitly + * Specifies a VMSD of a device that should be migrated during the mig= ration + * completion phase (switch-over). (Default behavior, same behavior as + * before the introduction of save phase.) + */ + VMS_PHASE_COMPLETE =3D 0, + /* + * Specifies a VMSD of a device that should be saved during setup phas= e of + * migration. It plays similar role as save_setup() for explicitly * registered vmstate entries, so it can be seen as a way to describe * save_setup() in VMSD structures. - * + */ + VMS_PHASE_EARLY_SETUP, +} VMStateSavePhase; + +struct VMStateDescription { + const char *name; + bool unmigratable; + /* * Note that for now, a SaveStateEntry cannot have a VMSD and * operations (e.g., save_setup()) set at the same time. Consequently, - * save_setup() and a VMSD with early_setup set to true are mutually + * save_setup() and a VMSD with phase set to EARLY_SETUP are mutually * exclusive. For this reason, also early_setup VMSDs are migrated in a * QEMU_VM_SECTION_FULL section, while save_setup() data is migrated in * a QEMU_VM_SECTION_START section. @@ -213,7 +224,7 @@ struct VMStateDescription { * <0 on error where -value is an error number from errno.h */ =20 - bool early_setup; + VMStateSavePhase phase; int version_id; int minimum_version_id; MigrationPriority priority; diff --git a/migration/savevm.c b/migration/savevm.c index 1020094fc8..78eb1d6165 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1370,7 +1370,7 @@ int qemu_savevm_state_setup(QEMUFile *f, Error **errp) =20 trace_savevm_state_setup(); QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { - if (se->vmsd && se->vmsd->early_setup) { + if (se->vmsd && se->vmsd->phase =3D=3D VMS_PHASE_EARLY_SETUP) { ret =3D vmstate_save(f, se, vmdesc, errp); if (ret) { migrate_error_propagate(ms, error_copy(*errp)); @@ -1672,7 +1672,7 @@ int qemu_savevm_state_complete_precopy_non_iterable(Q= EMUFile *f, cpu_synchronize_all_states(); =20 QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { - if (se->vmsd && se->vmsd->early_setup) { + if (se->vmsd && se->vmsd->phase !=3D VMS_PHASE_COMPLETE) { /* Already saved during qemu_savevm_state_setup(). */ continue; } --=20 2.52.0 From nobody Sat Feb 7 05:49:09 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1769523087; cv=none; d=zohomail.com; s=zohoarc; b=oDTWvhGVInwpJ8RN5Y5a83ZJpdMXCZtrvg27Y1e9GvWgDSTZLgLOXKIJ26bbwBw6dSzUdI7Hg4jwgYC1TMhcWF8CzHqzqDT0XLy+gGYITiSmEshP1y0mKoxyJcg4BXYMkNTMzHTaQRfZjug2o0/DVYhT7dD+5hU+SVpKYGFIoWQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1769523087; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=dvZDmWiQHNZKmPxm+TJ9daDwZa26WXuC/MX6WVOUAyc=; b=NGS0QS4UgkQBjczGu3ki8NCcmBfzjPZusGIBJy0szNYk2MSi2/3j0XdeUkgDgNc6h+SI5kkbOON6ZQK1hEWElQ0KN07IMbgXMwYqzXAiBvB4qpyYBBDuGgPMSmAm4u/Jrbm22O5Jgl25xU3+TRRoubbKDVoxd3h5QUMGF52Ldu4= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1769523087442255.28721116864688; Tue, 27 Jan 2026 06:11:27 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vkjh8-00083I-4O; Tue, 27 Jan 2026 09:05:26 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vkjgz-0007xx-3Q for qemu-devel@nongnu.org; Tue, 27 Jan 2026 09:05:17 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vkjgv-0007Nd-Nv for qemu-devel@nongnu.org; Tue, 27 Jan 2026 09:05:16 -0500 Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-180-fZiy8HUEN0-P2Naxu5Rpsw-1; Tue, 27 Jan 2026 09:03:58 -0500 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id E390C1955D9A; Tue, 27 Jan 2026 14:03:57 +0000 (UTC) Received: from fedora (unknown [10.43.3.182]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id CE5ED18007D2; Tue, 27 Jan 2026 14:03:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769522713; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=dvZDmWiQHNZKmPxm+TJ9daDwZa26WXuC/MX6WVOUAyc=; b=WL6ZF20FVn/M6tu45LSK/Ol24Kb9W6mnGLm/8JL9K6hWpBAa9hMaetoKwG3nHGu6w7YoCz 8z6EODZZQbmYgmNrLeLh67q0q83BfJLfEaHvMq8ghwi6TTjd7caLq9BxerN6JoetTY2GQ7 HuiTGFDqY8DgxP2opI9nyzKGA4s1TPU= X-MC-Unique: fZiy8HUEN0-P2Naxu5Rpsw-1 X-Mimecast-MFC-AGG-ID: fZiy8HUEN0-P2Naxu5Rpsw_1769522638 From: Juraj Marcin To: qemu-devel@nongnu.org Cc: Juraj Marcin , Fabiano Rosas , "Michael S. Tsirkin" , Peter Xu , Jason Wang , Vladimir Sementsov-Ogievskiy Subject: [PATCH 4/4] migration: Pass network packets received during switchover to dest VM Date: Tue, 27 Jan 2026 15:03:10 +0100 Message-ID: <20260127140316.4187221-5-jmarcin@redhat.com> In-Reply-To: <20260127140316.4187221-1-jmarcin@redhat.com> References: <20260127140316.4187221-1-jmarcin@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=jmarcin@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1769523090152154100 Content-Type: text/plain; charset="utf-8" From: Juraj Marcin During migration switchover both the source and the destination machines are paused (compute downtime). During this period network still routes network packets to the source machine, as this is the last place where the recipient MAC address has been seen. Once the destination side starts and sends network announcement, all subsequent frames are routed correctly. However, frames delivered to the source machine are never processed and lost. This causes also a network downtime with roughly the same duration as compute downtime. This can cause problems not only for protocols that cannot handle packet loss, but can also introduce delays in protocols that can handle them. To resolve this, this feature instantiates a network filter for each network backend present during migration setup on both migration sides. On the source side, this filter caches all packets received from the backend during switchover. Once the destination machine starts, all cached packets are sent through the migration channel and the respective filter object on the destination side injects them to the NIC attached to the backend. Signed-off-by: Juraj Marcin --- include/migration/vmstate.h | 6 + include/net/net.h | 5 + migration/meson.build | 1 + migration/migration.c | 49 ++++++- migration/migration.h | 2 + migration/netpass.c | 246 ++++++++++++++++++++++++++++++++++++ migration/netpass.h | 14 ++ migration/options.c | 21 +++ migration/options.h | 1 + migration/savevm.c | 37 ++++++ migration/savevm.h | 2 + migration/trace-events | 9 ++ net/net.c | 11 ++ net/tap.c | 11 +- qapi/migration.json | 7 +- 15 files changed, 418 insertions(+), 4 deletions(-) create mode 100644 migration/netpass.c create mode 100644 migration/netpass.h diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 62d7e9fe38..7987e6c85a 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -200,6 +200,12 @@ typedef enum { * save_setup() in VMSD structures. */ VMS_PHASE_EARLY_SETUP, + /* + * Specifies a netpass VMSD, these devices are copied right after the + * destination is started regardless of precopy/postcopy. Failure in t= his + * phase does not fail the migration in case of precopy. + */ + VMS_PHASE_NETPASS, } VMStateSavePhase; =20 struct VMStateDescription { diff --git a/include/net/net.h b/include/net/net.h index 45bc86fc86..510908845b 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -82,6 +82,7 @@ typedef void (NetAnnounce)(NetClientState *); typedef bool (SetSteeringEBPF)(NetClientState *, int); typedef bool (NetCheckPeerType)(NetClientState *, ObjectClass *, Error **); typedef struct vhost_net *(GetVHostNet)(NetClientState *nc); +typedef void (NetpassEnabledNotify)(NetClientState *nc, void *opaque); =20 typedef struct NetClientInfo { NetClientDriver type; @@ -130,6 +131,9 @@ struct NetClientState { bool is_netdev; bool do_not_pad; /* do not pad to the minimum ethernet frame length */ bool is_datapath; + bool netpass_enabled; + NetpassEnabledNotify *netpass_enabled_notify; + void *netpass_enabled_notify_opaque; QTAILQ_HEAD(, NetFilterState) filters; }; =20 @@ -198,6 +202,7 @@ void qemu_flush_queued_packets(NetClientState *nc); void qemu_flush_or_purge_queued_packets(NetClientState *nc, bool purge); void qemu_set_info_str(NetClientState *nc, const char *fmt, ...) G_GNUC_PRINTF(2, 3); +void qemu_set_netpass_enabled(NetClientState *nc, bool enabled); void qemu_format_nic_info_str(NetClientState *nc, uint8_t macaddr[6]); bool qemu_has_ufo(NetClientState *nc); bool qemu_has_uso(NetClientState *nc); diff --git a/migration/meson.build b/migration/meson.build index c7f39bdb55..a501256979 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -30,6 +30,7 @@ system_ss.add(files( 'multifd-nocomp.c', 'multifd-zlib.c', 'multifd-zero-page.c', + 'netpass.c', 'options.c', 'postcopy-ram.c', 'ram.c', diff --git a/migration/migration.c b/migration/migration.c index 4871db2365..959719dd61 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -63,6 +63,7 @@ #include "system/dirtylimit.h" #include "qemu/sockets.h" #include "system/kvm.h" +#include "netpass.h" =20 #define NOTIFIER_ELEM_INIT(array, elem) \ [elem] =3D NOTIFIER_WITH_RETURN_LIST_INITIALIZER((array)[elem]) @@ -488,6 +489,10 @@ void migration_incoming_state_destroy(void) mis->postcopy_qemufile_dst =3D NULL; } =20 + if (migrate_netpass()) { + migration_netpass_cleanup(); + } + cpr_set_incoming_mode(MIG_MODE_NONE); yank_unregister_instance(MIGRATION_YANK_INSTANCE); } @@ -755,6 +760,10 @@ static void process_incoming_migration_bh(void *opaque) migrate_send_rp_vm_started(mis); } =20 + if (migrate_netpass()) { + qemu_loadvm_state_netpass(mis->from_src_file, mis); + } + /* * This must happen after any state changes since as soon as an extern= al * observer sees this event they might start to prod at the VM assuming @@ -775,6 +784,13 @@ process_incoming_migration_co(void *opaque) =20 assert(mis->from_src_file); =20 + if (migrate_netpass()) { + ret =3D migration_netpass_setup(&local_err); + if (ret < 0) { + goto fail; + } + } + mis->largest_page_size =3D qemu_ram_pagesize_largest(); postcopy_state_set(POSTCOPY_INCOMING_NONE); migrate_set_state(&mis->state, MIGRATION_STATUS_SETUP, @@ -811,8 +827,7 @@ process_incoming_migration_co(void *opaque) goto out; =20 fail: - migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, - MIGRATION_STATUS_FAILED); + migrate_set_state(&mis->state, mis->state, MIGRATION_STATUS_FAILED); migrate_error_propagate(s, local_err); migration_incoming_state_destroy(); =20 @@ -1336,6 +1351,10 @@ static void migration_cleanup(MigrationState *s) qemu_fclose(tmp); } =20 + if (migrate_netpass()) { + migration_netpass_cleanup(); + } + assert(!migration_is_active()); =20 if (s->state =3D=3D MIGRATION_STATUS_CANCELLING) { @@ -1673,6 +1692,8 @@ int migrate_init(MigrationState *s, Error **errp) s->dest_vm_started =3D false; qemu_event_reset(&s->dest_vm_started_event); =20 + s->netpass_state_sent =3D false; + return 0; } =20 @@ -2729,6 +2750,10 @@ static bool migration_switchover_start(MigrationStat= e *s, Error **errp) { ERRP_GUARD(); =20 + if (migrate_netpass()) { + migration_netpass_activate(); + } + if (!migration_switchover_prepare(s)) { error_setg(errp, "Switchover is interrupted"); return false; @@ -2821,6 +2846,14 @@ static void migration_completion(MigrationState *s) goto fail; } =20 + if (migrate_netpass() && !s->netpass_state_sent) { + qemu_event_wait(&s->dest_vm_started_event); + qemu_savevm_state_netpass(s->to_dst_file); + s->netpass_state_sent =3D true; + qemu_put_byte(s->to_dst_file, QEMU_VM_EOF); + qemu_fflush(s->to_dst_file); + } + if (close_return_path_on_source(s)) { goto fail; } @@ -3251,6 +3284,11 @@ static MigIterateState migration_iteration_run(Migra= tionState *s) migrate_set_state(&s->state, MIGRATION_STATUS_POSTCOPY_DEVICE, MIGRATION_STATUS_POSTCOPY_ACTIVE); } + + if (s->dest_vm_started && migrate_netpass() && !s->netpass_state_s= ent) { + qemu_savevm_state_netpass(s->to_dst_file); + s->netpass_state_sent =3D true; + } } else { /* * Exact pending reporting is only needed for precopy. Taking RAM @@ -3774,6 +3812,13 @@ void migration_start_outgoing(MigrationState *s) =20 s->expected_downtime =3D migrate_downtime_limit(); =20 + if (migrate_netpass()) { + ret =3D migration_netpass_setup(&local_err); + if (ret < 0) { + goto fail; + } + } + if (resume) { /* This is a resumed migration */ rate_limit =3D migrate_max_postcopy_bandwidth(); diff --git a/migration/migration.h b/migration/migration.h index a3fab4f27e..a0d9560254 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -530,6 +530,8 @@ struct MigrationState { bool send_vm_started; bool dest_vm_started; QemuEvent dest_vm_started_event; + + bool netpass_state_sent; }; =20 void migrate_set_state(MigrationStatus *state, MigrationStatus old_state, diff --git a/migration/netpass.c b/migration/netpass.c new file mode 100644 index 0000000000..92b2522c83 --- /dev/null +++ b/migration/netpass.c @@ -0,0 +1,246 @@ +#include "qemu/osdep.h" +#include "netpass.h" + +#include "migration/migration.h" +#include "migration/vmstate.h" +#include "net/queue.h" +#include "net/filter.h" +#include "net/net.h" +#include "net/vhost_net.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/iov.h" +#include "qemu/typedefs.h" +#include "qom/object.h" +#include "trace.h" + +struct NetPassState { + NetFilterState parent_obj; + bool active; + size_t packet_count; + uint32_t qlength; + uint32_t qcapacity; + uint8_t *qbuffer; + SocketReadState rs; + QTAILQ_ENTRY(NetPassState) next; +}; + +static void netpass_queue_clear(NetPassState *s) +{ + g_free(s->qbuffer); + s->qbuffer =3D NULL; + s->qcapacity =3D 0; + s->qlength =3D 0; + s->packet_count =3D 0; +} + +OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(NetPassState, filter_netpass, + FILTER_NETPASS, NETFILTER, + { TYPE_VMSTATE_IF }, { } ) + +static bool netpass_vmstate_pre_save(void *opaque, Error **errp) +{ + NetPassState *s =3D opaque; + s->active =3D false; + return true; +} + +static int netpass_vmstate_post_save(void *opaque) +{ + NetPassState *s =3D opaque; + trace_migration_netpass_passed_packet_count(NETFILTER(s)->netdev_id, s= ->packet_count); + netpass_queue_clear(s); + return 0; +} + +static void netpass_vmstate_post_load_bh(void *opaque) +{ + NetPassState *s =3D opaque; + + int ret =3D net_fill_rstate(&s->rs, s->qbuffer, s->qlength); + if (ret =3D=3D -1) { + warn_report("migration: Failed to fill netpass rstate during load"= ); + } + trace_migration_netpass_received_packet_count(NETFILTER(s)->netdev_id,= s->packet_count); + netpass_queue_clear(s); +} + +static bool netpass_vmstate_post_load(void *opaque, int version_id, Error = **errp) +{ + /* + * Schedule on the main thread in case this function is running on the + * postcopy listen thread and there is a fault during packet injection. + */ + migration_bh_schedule(netpass_vmstate_post_load_bh, opaque); + return true; +} + +static char *filter_netpass_vmstate_if_get_id(VMStateIf *obj) +{ + NetFilterState *nf =3D NETFILTER(obj); + return g_strconcat("filter-netpass/", nf->netdev_id, NULL); +} + +static const VMStateDescription vmstate_netpass =3D { + .name =3D "filter-netpass", + .version_id =3D 1, + .minimum_version_id =3D 1, + .phase =3D VMS_PHASE_NETPASS, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT32(qlength, NetPassState), + VMSTATE_UINT32(qcapacity, NetPassState), + VMSTATE_VBUFFER_ALLOC_UINT32(qbuffer, NetPassState, 0, NULL, qcapa= city), + VMSTATE_END_OF_LIST(), + }, + .pre_save_errp =3D netpass_vmstate_pre_save, + .post_save =3D netpass_vmstate_post_save, + .post_load_errp =3D netpass_vmstate_post_load, +}; + +QTAILQ_HEAD(, NetPassState) filters =3D QTAILQ_HEAD_INITIALIZER(filters); + +static void netpass_rs_finalize(SocketReadState *rs) +{ + NetPassState *s =3D container_of(rs, NetPassState, rs); + NetFilterState *nf =3D NETFILTER(s); + + struct iovec iov =3D { + .iov_len =3D rs->packet_len, + .iov_base =3D rs->buf, + }; + qemu_netfilter_pass_to_next(nf->netdev, 0, &iov, 1, nf); + s->packet_count++; +} + +static void filter_netpass_setup(NetFilterState *nf, Error **errp) +{ + NetPassState *s =3D FILTER_NETPASS(nf); + + s->active =3D false; + s->qbuffer =3D NULL; + s->qcapacity =3D 0; + s->qlength =3D 0; + s->packet_count =3D 0; + net_socket_rs_init(&s->rs, netpass_rs_finalize, true); +} + +static void filter_netpass_cleanup(NetFilterState *nf) +{ + NetPassState *s =3D FILTER_NETPASS(nf); + + s->active =3D false; + netpass_queue_clear(s); + if (nf->netdev) { + qemu_set_netpass_enabled(nf->netdev, false); + } +} + +static ssize_t filter_netpass_receive_iov(NetFilterState *nf, + NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + NetPacketSent *sent_cb) +{ + NetPassState *s =3D FILTER_NETPASS(nf); + + if (!s->active) { + return 0; + } + + uint32_t total_size =3D iov_size(iov, iovcnt); + size_t req_cap =3D sizeof(uint32_t) + sizeof(uint32_t) + total_size; + if (s->qcapacity - s->qlength < req_cap) { + size_t new_capacity =3D s->qcapacity; + while (new_capacity - s->qlength < req_cap) { + new_capacity +=3D 4096; + } + s->qbuffer =3D g_realloc(s->qbuffer, new_capacity); + s->qcapacity =3D new_capacity; + } + uint32_t total_size_be =3D htonl(total_size); + memcpy(&s->qbuffer[s->qlength], &total_size_be, sizeof(uint32_t)); + s->qlength +=3D sizeof(uint32_t); + uint32_t vnet_hdr_len_be =3D htonl(sender->vnet_hdr_len); + memcpy(&s->qbuffer[s->qlength], &vnet_hdr_len_be, sizeof(uint32_t)); + s->qlength +=3D sizeof(uint32_t); + iov_to_buf_full(iov, iovcnt, 0, &s->qbuffer[s->qlength], total_size); + s->qlength +=3D total_size; + s->packet_count++; + + return 0; +} + +static void filter_netpass_class_init(ObjectClass *oc, const void *data) +{ + NetFilterClass *nfc =3D NETFILTER_CLASS(oc); + VMStateIfClass *vc =3D VMSTATE_IF_CLASS(oc); + + nfc->setup =3D filter_netpass_setup; + nfc->cleanup =3D filter_netpass_cleanup; + nfc->receive_iov =3D filter_netpass_receive_iov; + + vc->get_id =3D filter_netpass_vmstate_if_get_id; +} + +static void filter_netpass_init(Object *obj) +{ +} + +static void filter_netpass_finalize(Object *obj) +{ + NetPassState *s =3D FILTER_NETPASS(obj); + (void)s; +} + +int migration_netpass_setup(Error **errp) +{ + NetClientState *nc; + + QTAILQ_FOREACH(nc, &net_clients, next) { + if (!nc->is_netdev) { + continue; + } + if (get_vhost_net(nc)) { + warn_report("migration: netpass is not supported with vhost=3D= on"); + continue; + } + g_autofree char *filter_id =3D g_strconcat("netpass-", nc->name, N= ULL); + Object *obj =3D object_new_with_props(TYPE_FILTER_NETPASS, + object_get_objects_root(), + filter_id, errp, + "netdev", nc->name, + "queue", "tx", + NULL); + if (!obj) { + error_prepend(errp, "Failed to setup migration netpass"); + return -1; + } + trace_migration_netpass_setup_created_filter(nc->name); + object_ref(obj); + QTAILQ_INSERT_TAIL(&filters, FILTER_NETPASS(obj), next); + vmstate_register(VMSTATE_IF(obj), VMSTATE_INSTANCE_ID_ANY, + &vmstate_netpass, obj); + } + return 0; +} + +void migration_netpass_activate(void) +{ + NetPassState *s; + QTAILQ_FOREACH(s, &filters, next) { + s->packet_count =3D 0; + s->active =3D true; + qemu_set_netpass_enabled(NETFILTER(s)->netdev, true); + } +} + +void migration_netpass_cleanup(void) +{ + NetPassState *s, *ns; + QTAILQ_FOREACH_SAFE(s, &filters, next, ns) { + QTAILQ_REMOVE(&filters, s, next); + vmstate_unregister(VMSTATE_IF(s), &vmstate_netpass, s); + object_unref(s); + } +} diff --git a/migration/netpass.h b/migration/netpass.h new file mode 100644 index 0000000000..8618cf4c73 --- /dev/null +++ b/migration/netpass.h @@ -0,0 +1,14 @@ +#ifndef QEMU_MIGRATION_NETPASS_H +#define QEMU_MIGRATION_NETPASS_H + +#include "qemu/typedefs.h" +#include "qom/object.h" + +#define TYPE_FILTER_NETPASS "filter-netpass" +OBJECT_DECLARE_SIMPLE_TYPE(NetPassState, FILTER_NETPASS) + +int migration_netpass_setup(Error **errp); +void migration_netpass_activate(void); +void migration_netpass_cleanup(void); + +#endif diff --git a/migration/options.c b/migration/options.c index a5a233183b..e6e2d441b0 100644 --- a/migration/options.c +++ b/migration/options.c @@ -211,6 +211,7 @@ const Property migration_properties[] =3D { DEFINE_PROP_MIG_CAP("mapped-ram", MIGRATION_CAPABILITY_MAPPED_RAM), DEFINE_PROP_MIG_CAP("x-ignore-shared", MIGRATION_CAPABILITY_X_IGNORE_SHARED), + DEFINE_PROP_MIG_CAP("netpass", MIGRATION_CAPABILITY_NETPASS), }; const size_t migration_properties_count =3D ARRAY_SIZE(migration_propertie= s); =20 @@ -442,6 +443,13 @@ bool migrate_send_vm_started(void) return s->send_vm_started; } =20 +bool migrate_netpass(void) +{ + MigrationState *s =3D migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_NETPASS]; +} + /* pseudo capabilities */ =20 bool migrate_multifd_flush_after_each_section(void) @@ -723,6 +731,19 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps= , Error **errp) } } =20 + if (new_caps[MIGRATION_CAPABILITY_NETPASS]) { + if (!new_caps[MIGRATION_CAPABILITY_RETURN_PATH]) { + error_setg(errp, "Capability 'netpass' requires capability " + "'return-path'"); + return false; + } + if (!migrate_send_vm_started()) { + error_setg(errp, "Capability 'netpass' requires support for VM= _STARTED " + "return-path message"); + return false; + } + } + /* * On destination side, check the cases that capability is being set * after incoming thread has started. diff --git a/migration/options.h b/migration/options.h index 5fdc8fc6fe..151eaef86c 100644 --- a/migration/options.h +++ b/migration/options.h @@ -43,6 +43,7 @@ bool migrate_validate_uuid(void); bool migrate_xbzrle(void); bool migrate_zero_copy_send(void); bool migrate_send_vm_started(void); +bool migrate_netpass(void); =20 /* * pseudo capabilities diff --git a/migration/savevm.c b/migration/savevm.c index 78eb1d6165..b930f27fa9 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -279,6 +279,7 @@ static bool should_validate_capability(int capability) switch (capability) { case MIGRATION_CAPABILITY_X_IGNORE_SHARED: case MIGRATION_CAPABILITY_MAPPED_RAM: + case MIGRATION_CAPABILITY_NETPASS: return true; default: return false; @@ -1731,6 +1732,29 @@ int qemu_savevm_state_complete_precopy(QEMUFile *f, = bool iterable_only) return qemu_fflush(f); } =20 +void qemu_savevm_state_netpass(QEMUFile *f) +{ + MigrationState *ms =3D migrate_get_current(); + JSONWriter *vmdesc =3D ms->vmdesc; + SaveStateEntry *se; + Error *local_err =3D NULL; + int ret; + + trace_savevm_state_netpass_begin(); + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { + if (!se->vmsd || se->vmsd->phase !=3D VMS_PHASE_NETPASS) { + continue; + } + ret =3D vmstate_save(f, se, vmdesc, &local_err); + if (ret) { + warn_report_err(local_err); + qemu_file_clear_error(f); + break; + } + } + trace_savevm_state_netpass_end(ret); +} + /* Give an estimate of the amount left to be transferred, * the result is split into the amount for units that can and * for units that can't do postcopy. @@ -3148,6 +3172,19 @@ int qemu_load_device_state(QEMUFile *f, Error **errp) return 0; } =20 +void qemu_loadvm_state_netpass(QEMUFile *f, MigrationIncomingState *mis) +{ + Error *local_errp; + trace_loadvm_state_netpass_begin(); + int ret =3D qemu_loadvm_state_main(mis->from_src_file, mis, &local_err= p); + trace_loadvm_state_netpass_end(ret); + if (ret < 0) { + warn_reportf_err(local_errp, + "Error while loading netpass data, this error wil= l be ignored"); + qemu_file_clear_error(f); + } +} + int qemu_loadvm_approve_switchover(void) { MigrationIncomingState *mis =3D migration_incoming_get_current(); diff --git a/migration/savevm.h b/migration/savevm.h index 125a2507b7..53220c40cf 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -42,6 +42,7 @@ int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy); void qemu_savevm_state_cleanup(void); void qemu_savevm_state_complete_postcopy(QEMUFile *f); int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only); +void qemu_savevm_state_netpass(QEMUFile *f); void qemu_savevm_state_pending_exact(uint64_t *must_precopy, uint64_t *can_postcopy); void qemu_savevm_state_pending_estimate(uint64_t *must_precopy, @@ -71,6 +72,7 @@ void qemu_loadvm_state_cleanup(MigrationIncomingState *mi= s); int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis, Error **errp); int qemu_load_device_state(QEMUFile *f, Error **errp); +void qemu_loadvm_state_netpass(QEMUFile *f, MigrationIncomingState *mis); int qemu_loadvm_approve_switchover(void); int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, bool in_postcopy); diff --git a/migration/trace-events b/migration/trace-events index 91d7506634..eb25944d1b 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -10,6 +10,8 @@ qemu_savevm_send_packaged(void) "" loadvm_state_switchover_ack_needed(unsigned int switchover_ack_pending_num= ) "Switchover ack pending num=3D%u" loadvm_state_setup(void) "" loadvm_state_cleanup(void) "" +loadvm_state_netpass_begin(void) "" +loadvm_state_netpass_end(int ret) "ret=3D%d" loadvm_handle_cmd_packaged(unsigned int length) "%u" loadvm_handle_cmd_packaged_main(int ret) "%d" loadvm_handle_cmd_packaged_received(int ret) "%d" @@ -45,6 +47,8 @@ savevm_state_resume_prepare(void) "" savevm_state_header(void) "" savevm_state_iterate(void) "" savevm_state_cleanup(void) "" +savevm_state_netpass_begin(void) "" +savevm_state_netpass_end(int ret) "ret=3D%d" vmstate_save(const char *idstr, const char *vmsd_name) "%s, %s" vmstate_load(const char *idstr, const char *vmsd_name) "%s, %s" vmstate_downtime_save(const char *type, const char *idstr, uint32_t instan= ce_id, int64_t downtime) "type=3D%s idstr=3D%s instance_id=3D%d downtime=3D= %"PRIi64 @@ -401,3 +405,8 @@ cpu_throttle_dirty_sync(void) "" =20 # block-active.c migration_block_activation(const char *name) "%s" + +# netpass.c +migration_netpass_setup_created_filter(const char *netdev) "netdev=3D%s" +migration_netpass_passed_packet_count(const char *netdev, size_t count) "n= etdev=3D%s count=3D%zu" +migration_netpass_received_packet_count(const char *netdev, size_t count) = "netdev=3D%s count=3D%zu" diff --git a/net/net.c b/net/net.c index a176936f9b..81540fefc1 100644 --- a/net/net.c +++ b/net/net.c @@ -158,6 +158,14 @@ void qemu_set_info_str(NetClientState *nc, const char = *fmt, ...) va_end(ap); } =20 +void qemu_set_netpass_enabled(NetClientState *nc, bool enabled) +{ + nc->netpass_enabled =3D enabled; + if (nc->netpass_enabled_notify) { + nc->netpass_enabled_notify(nc, nc->netpass_enabled_notify_opaque); + } +} + void qemu_format_nic_info_str(NetClientState *nc, uint8_t macaddr[6]) { qemu_set_info_str(nc, "model=3D%s,macaddr=3D%02x:%02x:%02x:%02x:%02x:%= 02x", @@ -287,6 +295,9 @@ static void qemu_net_client_setup(NetClientState *nc, nc->incoming_queue =3D qemu_new_net_queue(qemu_deliver_packet_iov, nc); nc->destructor =3D destructor; nc->is_datapath =3D is_datapath; + nc->netpass_enabled =3D false; + nc->netpass_enabled_notify =3D NULL; + nc->netpass_enabled_notify_opaque =3D NULL; QTAILQ_INIT(&nc->filters); } =20 diff --git a/net/tap.c b/net/tap.c index 8d7ab6ba6f..dcc03a3f03 100644 --- a/net/tap.c +++ b/net/tap.c @@ -109,7 +109,8 @@ static char *tap_parse_script(const char *script_arg, c= onst char *default_path) static void tap_update_fd_handler(TAPState *s) { qemu_set_fd_handler(s->fd, - s->read_poll && s->enabled ? tap_send : NULL, + (s->read_poll || s->nc.netpass_enabled) && s->enab= led ? + tap_send : NULL, s->write_poll && s->enabled ? tap_writable : NULL, s); } @@ -412,6 +413,11 @@ static NetClientInfo net_tap_info =3D { .get_vhost_net =3D tap_get_vhost_net, }; =20 +static void tap_netpass_enabled_nofity(NetClientState *nc, void *opaque) +{ + tap_update_fd_handler(opaque); +} + static TAPState *net_tap_fd_init(NetClientState *peer, const char *model, const char *name, @@ -444,6 +450,9 @@ static TAPState *net_tap_fd_init(NetClientState *peer, tap_read_poll(s, true); s->vhost_net =3D NULL; =20 + nc->netpass_enabled_notify =3D &tap_netpass_enabled_nofity; + nc->netpass_enabled_notify_opaque =3D s; + return s; } =20 diff --git a/qapi/migration.json b/qapi/migration.json index f925e5541b..d637b22c80 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -520,6 +520,11 @@ # each RAM page. Requires a migration URI that supports seeking, # such as a file. (since 9.0) # +# @netpass: Collect packets received by network backedns after source +# VM is paused and send them to the destination once it resumes. +# This (almost) completely eliminates packet loss caused by +# switchover. (since 11.0) +# # Features: # # @unstable: Members @x-colo and @x-ignore-shared are experimental. @@ -536,7 +541,7 @@ { 'name': 'x-ignore-shared', 'features': [ 'unstable' ] }, 'validate-uuid', 'background-snapshot', 'zero-copy-send', 'postcopy-preempt', 'switchover-ack', - 'dirty-limit', 'mapped-ram'] } + 'dirty-limit', 'mapped-ram', 'netpass'] } =20 ## # @MigrationCapabilityStatus: --=20 2.52.0