// 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 identifier for migration data */ #define MIGRATE_MAGIC 0xB1BB1D1B0BB1D1B0 /* Migration header to send from source */ static struct migrate_header header = { .magic = htonll_constant(MIGRATE_MAGIC), .version = htonl_constant(MIGRATE_VERSION), }; /** * migrate_send_block() - Migration stage handler to send verbatim data * @c: Execution context * @stage: Migration stage * @fd: Migration fd * * Sends the buffer in @stage->iov over the migration channel. */ __attribute__((__unused__)) static int migrate_send_block(struct ctx *c, const struct migrate_stage *stage, int fd) { (void)c; if (write_remainder(fd, &stage->iov, 1, 0) < 0) return errno; return 0; } /** * migrate_recv_block() - Migration stage handler to receive verbatim data * @c: Execution context * @stage: Migration stage * @fd: Migration fd * * Reads the buffer in @stage->iov from the migration channel. * * #syscalls:vu readv */ __attribute__((__unused__)) static int migrate_recv_block(struct ctx *c, const struct migrate_stage *stage, int fd) { (void)c; if (read_remainder(fd, &stage->iov, 1, 0) < 0) return errno; return 0; } #define DATA_STAGE(v) \ { \ .name = #v, \ .source = migrate_send_block, \ .target = migrate_recv_block, \ .iov = { &(v), sizeof(v) }, \ } /* Stages for version 1 */ static const struct migrate_stage stages_v1[] = { { .name = "flow pre", .target = NULL, }, { .name = "flow post", .source = NULL, }, { 0 }, }; /* Set of data versions */ static const struct migrate_version versions[] = { { 1, stages_v1, }, { 0 }, }; /** * 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) { const struct migrate_version *v = versions + ARRAY_SIZE(versions) - 1; const struct migrate_stage *s; int ret; ret = write_all_buf(fd, &header, sizeof(header)); if (ret) { err("Can't send migration header: %s, abort", strerror_(ret)); return ret; } for (s = v->s; *s->name; s++) { if (!s->source) continue; debug("Source side migration: %s", s->name); if ((ret = s->source(c, s, fd))) { err("Source migration stage %s: %s, abort", s->name, strerror_(ret)); return ret; } } return 0; } /** * migrate_target_read_header() - Read header in target * @fd: Descriptor for state transfer * * Return: version number on success, 0 on failure with errno set */ static uint32_t migrate_target_read_header(int fd) { struct migrate_header h; if (read_all_buf(fd, &h, sizeof(h))) return 0; debug("Source magic: 0x%016" PRIx64 ", version: %u", be64toh(h.magic), ntohl_constant(h.version)); if (ntohll_constant(h.magic) != MIGRATE_MAGIC || !ntohl(h.version)) { errno = EINVAL; return 0; } return ntohl(h.version); } /** * 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) { const struct migrate_version *v; const struct migrate_stage *s; uint32_t id; int ret; id = migrate_target_read_header(fd); if (!id) { ret = errno; err("Migration header check failed: %s, abort", strerror_(ret)); return ret; } for (v = versions; v->id && v->id == id; v++); if (!v->id) { err("Unsupported version: %u", id); return -ENOTSUP; } for (s = v->s; *s->name; s++) { if (!s->target) continue; debug("Target side migration: %s", s->name); if ((ret = s->target(c, s, fd))) { err("Target migration stage %s: %s, abort", s->name, strerror_(ret)); return ret; } } return 0; }