// SPDX-License-Identifier: GPL-2.0-or-later /* PASST - Plug A Simple Socket Transport * for qemu/UNIX domain socket mode * * PASTA - Pack A Subtle Tap Abstraction * for network namespace/tap device mode * * migrate.c - Migration sections, layout, and routines * * Copyright (c) 2025 Red Hat GmbH * Author: Stefano Brivio */ #include #include #include #include "util.h" #include "ip.h" #include "passt.h" #include "inany.h" #include "flow.h" #include "flow_table.h" #include "repair.h" #include "migrate.h" /* Current version of migration data */ #define MIGRATE_VERSION 1 #define MAGIC_INIT { '{', 'p', 'a', 's', 's', 't', 'y', '}' } static const struct migrate_header header = { .magic = MAGIC_INIT, .version = htonl_constant(MIGRATE_VERSION), .compat_version = htonl_constant(MIGRATE_VERSION), }; /* Magic as we see it and as seen with reverse endianness */ #define MIGRATE_ENDIAN_TAG 0x12345678 /* Migration header to send from source */ static union migrate_header_v1 header_v1 = { .endian = MIGRATE_ENDIAN_TAG, .time_t_size = htonl_constant(sizeof(time_t)), .flow_size = htonl_constant(sizeof(union flow)), .flow_sidx_size = htonl_constant(sizeof(struct flow_sidx)), .voidp_size = htonl_constant(sizeof(void *)), }; /* Handlers to call in source before sending data */ struct migrate_handler handlers_source_pre[] = { { flow_migrate_source_pre }, { 0 }, }; /* Handlers to call in source after sending data */ struct migrate_handler handlers_source_post[] = { { 0 }, }; /* Handlers to call in target before receiving data with version 1 */ struct migrate_handler handlers_target_pre_v1[] = { { 0 }, }; /* Handlers to call in target after receiving data with version 1 */ struct migrate_handler handlers_target_post_v1[] = { { 0 }, }; /* Data sections for version 1 */ static struct iovec sections_v1[] = { { NULL, 0 }, }; /** * migrate_write_header_v1() - Send v1 ABI information from source * @fd: Descriptor for state transfer * * Return: 0 on success, error code on failure */ static int migrate_write_header_v1(int fd) { if (write_all_buf(fd, &header_v1, sizeof(header_v1))) return errno; return 0; } /** * migrate_read_header_v1() - Read and check ABI information sent by source * @fd: Descriptor for state transfer * @m: Migration metadata, updated on return * * Return: 0 on success, error code on failure */ static int migrate_read_header_v1(int fd, struct migrate_meta *m) { union migrate_header_v1 h; if (read_all_buf(fd, &h, sizeof(h))) return errno; if (h.endian == MIGRATE_ENDIAN_TAG) m->bswap = false; else if (bswap_32(h.endian) == MIGRATE_ENDIAN_TAG) m->bswap = true; else return ENOTSUP; if (ntohl(h.voidp_size) == 4) m->source_64b = false; else if (ntohl(h.voidp_size) == 8) m->source_64b = true; else return ENOTSUP; if (ntohl(h.time_t_size) == 4) m->time_64b = false; else if (ntohl(h.time_t_size) == 8) m->time_64b = true; else return ENOTSUP; m->flow_size = ntohl(h.flow_size); m->flow_sidx_size = ntohl(h.flow_sidx_size); return 0; } /* Supported protocol versions */ static const struct migrate_version versions[] = { { .v = 1, .read_header_v = migrate_read_header_v1, .pre = handlers_target_pre_v1, .sections = sections_v1, .post = handlers_target_post_v1, }, { 0 }, }; /** * migrate_write_header() - Send basic version information from source * @fd: Descriptor for state transfer * * Return: 0 on success, error code on failure */ static int migrate_write_header(int fd) { if (write_all_buf(fd, &header, sizeof(header))) return errno; return 0; } /** * migrate_source_pre() - Pre-migration tasks as source * @c: Execution context * @m: Migration metadata * * Return: 0 on success, error code on failure */ static int migrate_source_pre(struct ctx *c, struct migrate_meta *m) { struct migrate_handler *h; for (h = handlers_source_pre; h->fn; h++) { int rc; if ((rc = h->fn(c, m))) return rc; } return 0; } /** * migrate_source_state() - Send device state as migration source * @fd: Descriptor for state transfer * @m: Migration metadata * * Return: 0 on success, error code on failure */ static int migrate_source_state(int fd, const struct migrate_meta *m) { int count, rc; for (count = 0; m->v->sections[count].iov_len; count++); debug("Writing %u migration sections", count - 1 /* minus header */); rc = write_remainder(fd, m->v->sections, count, 0); if (rc < 0) return errno; return 0; } /** * migrate_source_post() - Post-migration tasks as source * @c: Execution context * @m: Migration metadata * * Return: 0 on success, error code on failure */ static void migrate_source_post(struct ctx *c, struct migrate_meta *m) { struct migrate_handler *h; for (h = handlers_source_post; h->fn; h++) h->fn(c, m); } /** * migrate_source() - Migration as source, send state to hypervisor * @c: Execution context * @fd: File descriptor for state transfer * * Return: 0 on success, positive error code on failure */ static int migrate_source(struct ctx *c, int fd) { struct migrate_meta m; int rc; m.version = MIGRATE_VERSION; for (m.v = versions; m.v->v != m.version; m.v++); rc = migrate_write_header(fd); if (rc) { err("Source migration header failed: %s", strerror_(rc)); return rc; } rc = migrate_write_header_v1(fd); if (rc) { err("Source migration header v1 failed: %s", strerror_(rc)); return rc; } if ((rc = migrate_source_pre(c, &m))) { err("Source pre-migration failed: %s, abort", strerror_(rc)); return rc; } debug("Saving backend state"); rc = migrate_source_state(fd, &m); if (rc) err("Source migration failed: %s", strerror_(rc)); else migrate_source_post(c, &m); return rc; } /** * migrate_read_header() - Read header and check versions from source * @fd: Descriptor for state transfer * @m: Migration metadata, filled on return * * Return: 0 on success, error code on failure */ static int migrate_read_header(int fd, struct migrate_meta *m) { struct migrate_header h; uint32_t compat_version; if (read_all_buf(fd, &h, sizeof(h))) return errno; m->version = ntohl(h.version); compat_version = ntohl(h.compat_version); debug("Source magic: '%c%c%c%c%c%c%c%c version: %u compat_version: %u", h.magic[0], h.magic[1], h.magic[2], h.magic[3], h.magic[4], h.magic[5], h.magic[6], h.magic[7], m->version, compat_version); for (m->v = versions; m->v->v > m->version && m->v->v < compat_version; m->v++) ; if (!m->v->v) return ENOTSUP; return 0; } /** * migrate_target_pre() - Pre-migration tasks as target * @c: Execution context * @m: Migration metadata * * Return: 0 on success, error code on failure */ static int migrate_target_pre(struct ctx *c, struct migrate_meta *m) { struct migrate_handler *h; for (h = m->v->pre; h->fn; h++) { int rc; if ((rc = h->fn(c, m))) return rc; } return 0; } /** * migrate_target_state() - Receive device state as migration target * @fd: Descriptor for state transfer * @m: Migration metadata * * Return: 0 on success, error code on failure * * #syscalls:vu readv */ static int migrate_target_state(int fd, const struct migrate_meta *m) { unsigned cnt; int rc; for (cnt = 0; m->v->sections[cnt].iov_len; cnt++); debug("Reading %u migration sections", cnt); rc = read_remainder(fd, m->v->sections, cnt, 0); if (rc < 0) return errno; return 0; } /** * migrate_target_post() - Post-migration tasks as target * @c: Execution context * @m: Migration metadata */ static void migrate_target_post(struct ctx *c, struct migrate_meta *m) { struct migrate_handler *h; for (h = m->v->post; h->fn; h++) h->fn(c, m); } /** * migrate_target() - Migration as target, receive state from hypervisor * @c: Execution context * @fd: File descriptor for state transfer * * Return: 0 on success, positive error code on failure */ static int migrate_target(struct ctx *c, int fd) { struct migrate_meta m; int rc; rc = migrate_read_header(fd, &m); if (rc) { err("Migration header check failed: %s, abort", strerror_(rc)); return rc; } rc = m.v->read_header_v(fd, &m); if (rc) { err("Migration header v%d check failed: %s, abort", m.v->v, strerror_(rc)); return rc; } if ((rc = migrate_target_pre(c, &m))) { err("Target pre-migration failed: %s, abort", strerror_(rc)); return rc; } debug("Loading backend state"); rc = migrate_target_state(fd, &m); if (rc) err("Target migration failed: %s", strerror_(rc)); else migrate_target_post(c, &m); return rc; } /** * migrate_init() - Set up things necessary for migration * @c: Execution context */ void migrate_init(struct ctx *c) { c->device_state_fd = -1; c->device_state_result = -1; repair_sock_init(c); } /** * migrate_close() - Close migration channel * @c: Execution context */ void migrate_close(struct ctx *c) { if (c->device_state_fd != -1) { debug("Closing migration channel, fd: %d", c->device_state_fd); epoll_del(c, c->device_state_fd); close(c->device_state_fd); c->device_state_fd = -1; c->device_state_result = -1; } } /** * migrate_request() - Request a migration of device state * @c: Execution context * @fd: fd to transfer state * @target: Are we the target of the migration? */ void migrate_request(struct ctx *c, int fd, bool target) { debug("Migration requested, fd: %d", c->device_state_fd); if (c->device_state_fd != -1) migrate_close(c); c->device_state_fd = fd; c->migrate_target = target; } /** * migrate_handler() - Send/receive passt internal state to/from QEMU * @c: Execution context */ void migrate_handler(struct ctx *c) { int rc; if (c->device_state_fd < 0) return; debug("migrate_handler fd %d target %d", c->device_state_fd, c->migrate_target); if (c->migrate_target) rc = migrate_target(c, c->device_state_fd); else rc = migrate_source(c, c->device_state_fd); migrate_close(c); c->device_state_result = rc; }