From nobody Sun Mar 22 15:41:49 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=none dis=none) header.from=yandex-team.ru ARC-Seal: i=1; a=rsa-sha256; t=1773935726; cv=none; d=zohomail.com; s=zohoarc; b=CGme0cW0+PYMZldOnuYM2DFf1MfNXydpoxr6N+3cBs1vXqX52PdFS7I2AYWDVaW4b9WAJEX7cDAuwWPhIMl3T8qh/zDTjmyf/1TIMU0+6XGeyfQbu41dUa4Zfu/QETPz6POqJtmjsas766HBUhxG6jjQzPcNCX8EGYZvGANm5rs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773935726; 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=sEQxXYFaeeWO+82XezYBRhx9zIRP35FphdW1P849f8Y=; b=gys5jVC4EENxzxL87M10Krn5xyxdNLmkIFVckdet+tdEuJgB9kdTNa9MFXywxlJi0BHD+wTRg9+cDcS67oPPQ/8sRtcXzCGgkhT0OWf9OZPXDZWxs6daj/ERSa0NayqdZsm+S6YnjYuxhrqDWHIXrUyeFFVhpMu0ts2+jC8eN4o= 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=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773935726013486.5888580303074; Thu, 19 Mar 2026 08:55:26 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w3Fh8-0003aY-MC; Thu, 19 Mar 2026 11:54:00 -0400 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 1w3Fh1-0003Yy-PJ for qemu-devel@nongnu.org; Thu, 19 Mar 2026 11:53:51 -0400 Received: from forwardcorp1b.mail.yandex.net ([2a02:6b8:c02:900:1:45:d181:df01]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w3Fgx-0007gz-Ic for qemu-devel@nongnu.org; Thu, 19 Mar 2026 11:53:51 -0400 Received: from mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net [IPv6:2a02:6b8:c24:fa2:0:640:41ee:0]) by forwardcorp1b.mail.yandex.net (Yandex) with ESMTPS id 28BD280815; Thu, 19 Mar 2026 18:53:46 +0300 (MSK) Received: from vsementsov-lin (unknown [2a02:6bf:8080:d57::1:20]) by mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net (smtpcorp/Yandex) with ESMTPSA id arQgWZ2AA0U0-lkUOZNrT; Thu, 19 Mar 2026 18:53:45 +0300 Precedence: bulk X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.ru; s=default; t=1773935625; bh=sEQxXYFaeeWO+82XezYBRhx9zIRP35FphdW1P849f8Y=; h=Message-ID:Date:In-Reply-To:Cc:Subject:References:To:From; b=LpRGFVAPpLbHLBezb6qOpIiO+o6z3XLwyP8BOnF7HvzS/3nEkTBS308CSj3E8GtDF LXFxPA6blXSqGe3gEvhKfcCi3likaSgdaC7bFxucnQr1G45QhpGJTIavgZB7N//z5o 9Emm7MWkNfmCbvmASLcBNQx8nm0xOF8K7tYddR1g= Authentication-Results: mail-nwsmtp-smtp-corp-main-34.sas.yp-c.yandex.net; dkim=pass header.i=@yandex-team.ru From: Vladimir Sementsov-Ogievskiy To: jasowang@redhat.com, mst@redhat.com Cc: armbru@redhat.com, eblake@redhat.com, farosas@suse.de, peterx@redhat.com, zhao1.liu@intel.com, wangyanan55@huawei.com, philmd@linaro.org, marcel.apfelbaum@gmail.com, eduardo@habkost.net, davydov-max@yandex-team.ru, qemu-devel@nongnu.org, vsementsov@yandex-team.ru, yc-core@yandex-team.ru, leiyang@redhat.com, raphael.s.norwitz@gmail.com, bchaney@akamai.com, th.huth+qemu@posteo.eu, berrange@redhat.com, pbonzini@redhat.com Subject: [PATCH v13 6/8] net/tap: support local migration with virtio-net Date: Thu, 19 Mar 2026 18:53:30 +0300 Message-ID: <20260319155333.260341-7-vsementsov@yandex-team.ru> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260319155333.260341-1-vsementsov@yandex-team.ru> References: <20260319155333.260341-1-vsementsov@yandex-team.ru> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable 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=2a02:6b8:c02:900:1:45:d181:df01; envelope-from=vsementsov@yandex-team.ru; helo=forwardcorp1b.mail.yandex.net 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, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=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 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 @yandex-team.ru) X-ZM-MESSAGEID: 1773935727775154100 Content-Type: text/plain; charset="utf-8" Support transferring of TAP state (including open fd) through migration stream as part of viritio-net "local-migration". Add new option, incoming-fds, which should be set to true to trigger new logic. For new option require explicitly unset script and downscript, to keep possibility of implementing support for them in future. Note disabling read polling on source stop for TAP migration: otherwise, source process may steal packages from TAP fd even after source vm STOP. Signed-off-by: Vladimir Sementsov-Ogievskiy --- net/tap.c | 147 +++++++++++++++++++++++++++++++++++++++++++++++--- qapi/net.json | 7 ++- 2 files changed, 147 insertions(+), 7 deletions(-) diff --git a/net/tap.c b/net/tap.c index 9d6213fc3e5..2156b6cbb73 100644 --- a/net/tap.c +++ b/net/tap.c @@ -36,6 +36,7 @@ #include "net/net.h" #include "clients.h" #include "monitor/monitor.h" +#include "system/runstate.h" #include "system/system.h" #include "qapi/error.h" #include "qemu/cutils.h" @@ -86,6 +87,9 @@ typedef struct TAPState { VHostNetState *vhost_net; unsigned host_vnet_hdr_len; Notifier exit; + + bool read_poll_detached; + VMChangeStateEntry *vmstate; } TAPState; =20 static void launch_script(const char *setup_script, const char *ifname, @@ -94,19 +98,25 @@ static void launch_script(const char *setup_script, con= st char *ifname, static void tap_send(void *opaque); static void tap_writable(void *opaque); =20 +static bool tap_is_explicit_no_scirpt(const char *script_arg) +{ + return script_arg && + (script_arg[0] =3D=3D '\0' || strcmp(script_arg, "no") =3D=3D 0); +} + static char *tap_parse_script(const char *script_arg, const char *default_= path) { g_autofree char *res =3D g_strdup(script_arg); =20 - if (!res) { - res =3D get_relocated_path(default_path); + if (tap_is_explicit_no_scirpt(script_arg)) { + return NULL; } =20 - if (res[0] =3D=3D '\0' || strcmp(res, "no") =3D=3D 0) { - return NULL; + if (!script_arg) { + return get_relocated_path(default_path); } =20 - return g_steal_pointer(&res); + return g_strdup(script_arg); } =20 static void tap_update_fd_handler(TAPState *s) @@ -123,6 +133,23 @@ static void tap_read_poll(TAPState *s, bool enable) tap_update_fd_handler(s); } =20 +static void tap_vm_state_change(void *opaque, bool running, RunState state) +{ + TAPState *s =3D opaque; + + if (running) { + if (s->read_poll_detached) { + tap_read_poll(s, true); + s->read_poll_detached =3D false; + } + } else if (state =3D=3D RUN_STATE_FINISH_MIGRATE) { + if (s->read_poll) { + s->read_poll_detached =3D true; + tap_read_poll(s, false); + } + } +} + static void tap_write_poll(TAPState *s, bool enable) { s->write_poll =3D enable; @@ -353,6 +380,11 @@ static void tap_cleanup(NetClientState *nc) s->exit.notify =3D NULL; } =20 + if (s->vmstate) { + qemu_del_vm_change_state_handler(s->vmstate); + s->vmstate =3D NULL; + } + tap_read_poll(s, false); tap_write_poll(s, false); close(s->fd); @@ -393,6 +425,65 @@ static VHostNetState *tap_get_vhost_net(NetClientState= *nc) return s->vhost_net; } =20 +static bool tap_is_wait_incoming(NetClientState *nc) +{ + TAPState *s =3D DO_UPCAST(TAPState, nc, nc); + assert(nc->info->type =3D=3D NET_CLIENT_DRIVER_TAP); + return s->fd =3D=3D -1; +} + +static int tap_pre_load(void *opaque) +{ + TAPState *s =3D opaque; + + if (s->fd !=3D -1) { + error_report( + "TAP is already initialized and cannot receive incoming fd"); + return -EINVAL; + } + + return 0; +} + +static bool tap_setup_vhost(TAPState *s, Error **errp); + +static int tap_post_load(void *opaque, int version_id) +{ + TAPState *s =3D opaque; + Error *local_err =3D NULL; + + tap_read_poll(s, true); + + if (s->fd < 0) { + return -1; + } + + if (!tap_setup_vhost(s, &local_err)) { + error_prepend(&local_err, + "Failed to setup vhost during TAP post-load: "); + error_report_err(local_err); + return -1; + } + + return 0; +} + +static const VMStateDescription vmstate_tap =3D { + .name =3D "net-tap", + .pre_load =3D tap_pre_load, + .post_load =3D tap_post_load, + .fields =3D (const VMStateField[]) { + VMSTATE_FD(fd, TAPState), + VMSTATE_BOOL(using_vnet_hdr, TAPState), + VMSTATE_BOOL(has_ufo, TAPState), + VMSTATE_BOOL(has_uso, TAPState), + VMSTATE_BOOL(has_tunnel, TAPState), + VMSTATE_BOOL(enabled, TAPState), + VMSTATE_UINT32(host_vnet_hdr_len, TAPState), + VMSTATE_END_OF_LIST() + } +}; + /* fd support */ =20 static NetClientInfo net_tap_info =3D { @@ -412,7 +503,9 @@ static NetClientInfo net_tap_info =3D { .set_vnet_le =3D tap_set_vnet_le, .set_vnet_be =3D tap_set_vnet_be, .set_steering_ebpf =3D tap_set_steering_ebpf, + .is_wait_incoming =3D tap_is_wait_incoming, .get_vhost_net =3D tap_get_vhost_net, + .backend_vmsd =3D &vmstate_tap, }; =20 static TAPState *net_tap_fd_init(NetClientState *peer, @@ -748,6 +841,9 @@ static bool net_init_tap_one(const NetdevTapOptions *ta= p, NetClientState *peer, int sndbuf =3D (tap->has_sndbuf && tap->sndbuf) ? MIN(tap->sndbuf, INT_MAX) : INT= _MAX; =20 + s->read_poll_detached =3D false; + s->vmstate =3D qemu_add_vm_change_state_handler(tap_vm_state_change, s= ); + if (!tap_set_sndbuf(fd, sndbuf, sndbuf_required ? errp : NULL) && sndbuf_required) { goto failed; @@ -779,6 +875,8 @@ static bool net_init_tap_one(const NetdevTapOptions *ta= p, NetClientState *peer, return true; =20 failed: + qemu_del_vm_change_state_handler(s->vmstate); + s->vmstate =3D NULL; qemu_del_net_client(&s->nc); return false; } @@ -910,6 +1008,26 @@ int net_init_tap(const Netdev *netdev, const char *na= me, return -1; } =20 + if (tap->incoming_fds && + (tap->fd || tap->fds || tap->helper || tap->br || tap->ifname || + tap->has_sndbuf || tap->has_vnet_hdr)) { + error_setg(errp, "incoming-fds is incompatible with " + "fd=3D, fds=3D, helper=3D, br=3D, ifname=3D, sndbuf=3D = and vnet_hdr=3D"); + return -1; + } + + if (tap->incoming_fds && + !(tap_is_explicit_no_scirpt(tap->script) && + tap_is_explicit_no_scirpt(tap->downscript))) { + /* + * script=3D"" and downscript=3D"" are silently supported to be co= nsistent + * with cases without incoming_fds, but do not care to put this in= to + * error message. + */ + error_setg(errp, "incoming-fds requires script=3Dno and downscript= =3Dno"); + return -1; + } + queues =3D tap_parse_fds_and_queues(tap, &fds, errp); if (queues < 0) { return -1; @@ -928,7 +1046,24 @@ int net_init_tap(const Netdev *netdev, const char *na= me, goto fail; } =20 - if (fds) { + if (tap->incoming_fds) { + for (i =3D 0; i < queues; i++) { + NetClientState *nc; + TAPState *s; + + nc =3D qemu_new_net_client(&net_tap_info, peer, "tap", name); + qemu_set_info_str(nc, "incoming"); + + s =3D DO_UPCAST(TAPState, nc, nc); + s->fd =3D -1; + if (vhost_fds) { + s->vhostfd =3D vhost_fds[i]; + s->vhost_busyloop_timeout =3D tap->has_poll_us ? tap->poll= _us : 0; + } else { + s->vhostfd =3D -1; + } + } + } else if (fds) { for (i =3D 0; i < queues; i++) { if (i =3D=3D 0) { vnet_hdr =3D tap_probe_vnet_hdr(fds[i], errp); diff --git a/qapi/net.json b/qapi/net.json index 118bd349651..2240de7dbf6 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -355,6 +355,10 @@ # @poll-us: maximum number of microseconds that could be spent on busy # polling for tap (since 2.7) # +# @incoming-fds: do not open or create any TAP devices. Prepare for +# getting opened TAP file descriptors from incoming migration +# stream. (Since 11.0) +# # Since: 1.2 ## { 'struct': 'NetdevTapOptions', @@ -373,7 +377,8 @@ '*vhostfds': 'str', '*vhostforce': 'bool', '*queues': 'uint32', - '*poll-us': 'uint32'} } + '*poll-us': 'uint32', + '*incoming-fds': 'bool' } } =20 ## # @NetdevSocketOptions: --=20 2.52.0