Support transferring of TAP state (including open fd) through migration stream.
Add new option, incoming-fds, which should be set to true to trigger new logic. Signed-off-by: Vladimir Sementsov-Ogievskiy <[email protected]> --- net/tap.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++- qapi/net.json | 6 +++- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/net/tap.c b/net/tap.c index bd19c71c42..57d4d2d9f8 100644 --- a/net/tap.c +++ b/net/tap.c @@ -393,6 +393,65 @@ static VHostNetState *tap_get_vhost_net(NetClientState *nc) return s->vhost_net; } +static bool tap_is_wait_incoming(NetClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + assert(nc->info->type == NET_CLIENT_DRIVER_TAP); + return s->fd == -1; +} + +static int tap_pre_load(void *opaque) +{ + TAPState *s = opaque; + + if (s->fd != -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 = opaque; + Error *local_err = 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 = { + .name = "net-tap", + .pre_load = tap_pre_load, + .post_load = tap_post_load, + .fields = (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 */ static NetClientInfo net_tap_info = { @@ -412,7 +471,9 @@ static NetClientInfo net_tap_info = { .set_vnet_le = tap_set_vnet_le, .set_vnet_be = tap_set_vnet_be, .set_steering_ebpf = tap_set_steering_ebpf, + .is_wait_incoming = tap_is_wait_incoming, .get_vhost_net = tap_get_vhost_net, + .backend_vmsd = &vmstate_tap, }; static TAPState *net_tap_fd_init(NetClientState *peer, @@ -907,6 +968,14 @@ int net_init_tap(const Netdev *netdev, const char *name, return -1; } + if (tap->incoming_fds && + (tap->fd || tap->fds || tap->helper || tap->script || + tap->downscript)) { + error_setg(errp, "incoming-fds is incompatible with " + "fd=, fds=, helper=, script=, downscript="); + return -1; + } + queues = tap_parse_fds_and_queues(tap, &fds, errp); if (queues < 0) { return -1; @@ -925,7 +994,24 @@ int net_init_tap(const Netdev *netdev, const char *name, goto fail; } - if (fds) { + if (tap->incoming_fds) { + for (i = 0; i < queues; i++) { + NetClientState *nc; + TAPState *s; + + nc = qemu_new_net_client(&net_tap_info, peer, "tap", name); + qemu_set_info_str(nc, "incoming"); + + s = DO_UPCAST(TAPState, nc, nc); + s->fd = -1; + if (vhost_fds) { + s->vhostfd = vhost_fds[i]; + s->vhost_busyloop_timeout = tap->has_poll_us ? tap->poll_us : 0; + } else { + s->vhostfd = -1; + } + } + } else if (fds) { for (i = 0; i < queues; i++) { if (i == 0) { vnet_hdr = tap_probe_vnet_hdr(fds[i], errp); diff --git a/qapi/net.json b/qapi/net.json index 118bd34965..79f5ce9f43 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -355,6 +355,9 @@ # @poll-us: maximum number of microseconds that could be spent on busy # polling for tap (since 2.7) # +# @incoming-fds: do not open/connnect any resources, instead wait for +# TAP state from incoming migration stream. (Since 11.0) +# # Since: 1.2 ## { 'struct': 'NetdevTapOptions', @@ -373,7 +376,8 @@ '*vhostfds': 'str', '*vhostforce': 'bool', '*queues': 'uint32', - '*poll-us': 'uint32'} } + '*poll-us': 'uint32', + '*incoming-fds': 'bool' } } ## # @NetdevSocketOptions: -- 2.52.0
