// 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 "util.h" #include "ip.h" #include "passt.h" #include "inany.h" #include "flow.h" #include "flow_table.h" #include "migrate.h" /* Current version of migration data */ #define MIGRATE_VERSION 1 /* Magic as we see it and as seen with reverse endianness */ #define MIGRATE_MAGIC 0xB1BB1D1B0BB1D1B0 #define MIGRATE_MAGIC_SWAPPED 0xB0D1B1B01B1DBBB1 /* Migration header to send from source */ static union migrate_header header = { .magic = MIGRATE_MAGIC, .version = htonl_constant(MIGRATE_VERSION), .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 *)), }; /* Data sections for version 1 */ static struct iovec sections_v1[] = { { &header, sizeof(header) }, }; /* Set of data versions */ static struct migrate_data data_versions[] = { { 1, sections_v1, }, { 0 }, }; /* 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 }, }; /* Versioned sets of migration handlers */ struct migrate_target_handlers target_handlers[] = { { 1, handlers_target_pre_v1, handlers_target_post_v1, }, { 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) { static struct migrate_data *d; int count, rc; (void)m; for (d = data_versions; d->v != MIGRATE_VERSION; d++); for (count = 0; d->sections[count].iov_len; count++); debug("Writing %u migration sections", count - 1 /* minus header */); rc = write_remainder(fd, d->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 */ int migrate_source(struct ctx *c, int fd) { struct migrate_meta m; int 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_target_read_header() - Set metadata in target from source header * @fd: Descriptor for state transfer * @m: Migration metadata, filled on return * * Return: 0 on success, error code on failure */ static int migrate_target_read_header(int fd, struct migrate_meta *m) { static struct migrate_data *d; union migrate_header h; if (read_all_buf(fd, &h, sizeof(h))) return errno; debug("Source magic: 0x%016" PRIx64 ", sizeof(void *): %u, version: %u", h.magic, ntohl(h.voidp_size), ntohl(h.version)); for (d = data_versions; d->v != ntohl(h.version) && d->v; d++); if (!d->v) return ENOTSUP; m->v = d->v; if (h.magic == MIGRATE_MAGIC) m->bswap = false; else if (h.magic == MIGRATE_MAGIC_SWAPPED) 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; } /** * 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_target_handlers *th; struct migrate_handler *h; for (th = target_handlers; th->v != m->v && th->v; th++); for (h = th->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) { static struct migrate_data *d; unsigned cnt; int rc; for (d = data_versions; d->v != m->v && d->v; d++); for (cnt = 0; d->sections[cnt + 1 /* skip header */].iov_len; cnt++); debug("Reading %u migration sections", cnt); rc = read_remainder(fd, d->sections + 1, 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_target_handlers *th; struct migrate_handler *h; for (th = target_handlers; th->v != m->v && th->v; th++); for (h = th->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 */ int migrate_target(struct ctx *c, int fd) { struct migrate_meta m; int rc; rc = migrate_target_read_header(fd, &m); if (rc) { err("Migration header check failed: %s, abort", 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; }