// 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[] = { { 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 */ 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() - Perform migration as source: send state to hypervisor * @fd: Descriptor for state transfer * @m: Migration metadata * * Return: 0 on success, error code on failure */ int migrate_source(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 */ 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_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 */ 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 */ 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() - Perform migration as target: receive state from hypervisor * @fd: Descriptor for state transfer * @m: Migration metadata * * Return: 0 on success, error code on failure * * #syscalls:vu readv */ int migrate_target(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 */ 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); }