At present the header for our device state migration stream is sent as a buffer in the sections array, like anything else. It contains both version information, and details on the source ABI which are specific to v1 of the migration protocol. Alter this for greater flexibility: * We separate out a minimal fixed header, which we will need to keep for every future version, from a version specific header, containing (for v1) the ABI data * Handle both the headers separately from the data sections making for better symmetry between the source and target sides * Add a "compat_version" field. This will allow us to make future protocol extensions which are backwards compatible with older targets, while retaining the ability to also make breaking protocol extensions. This establishes a minimal header with fixed representation to maintain for all future versions. Signed-off-by: David Gibson <david(a)gibson.dropbear.id.au> --- migrate.c | 157 ++++++++++++++++++++++++++++++++++++++++-------------- migrate.h | 26 ++++++--- 2 files changed, 138 insertions(+), 45 deletions(-) diff --git a/migrate.c b/migrate.c index f9e967cf..3cad9892 100644 --- a/migrate.c +++ b/migrate.c @@ -12,6 +12,7 @@ * Author: Stefano Brivio <sbrivio(a)redhat.com> */ +#include <byteswap.h> #include <errno.h> #include <sys/uio.h> @@ -28,14 +29,20 @@ /* 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_MAGIC 0xB1BB1D1B0BB1D1B0 -#define MIGRATE_MAGIC_SWAPPED 0xB0D1B1B01B1DBBB1 +#define MIGRATE_ENDIAN_TAG 0x12345678 /* Migration header to send from source */ -static union migrate_header header = { - .magic = MIGRATE_MAGIC, - .version = htonl_constant(MIGRATE_VERSION), +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)), @@ -65,13 +72,69 @@ struct migrate_handler handlers_target_post_v1[] = { /* Data sections for version 1 */ static struct iovec sections_v1[] = { - { &header, sizeof(header) }, + { 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, @@ -79,6 +142,20 @@ static const struct migrate_version versions[] = { { 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 @@ -151,6 +228,18 @@ static int migrate_source(struct ctx *c, int fd) 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; @@ -168,52 +257,35 @@ static int migrate_source(struct ctx *c, int fd) } /** - * migrate_target_read_header() - Set metadata in target from source header + * 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_target_read_header(int fd, struct migrate_meta *m) +static int migrate_read_header(int fd, struct migrate_meta *m) { - union migrate_header h; + 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: 0x%016" PRIx64 ", sizeof(void *): %u, version: %u", - h.magic, ntohl(h.voidp_size), m->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++); + for (m->v = versions; + m->v->v > m->version && m->v->v < compat_version; + m->v++) + ; if (!m->v->v) return ENOTSUP; - 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; } @@ -252,10 +324,10 @@ static int migrate_target_state(int fd, const struct migrate_meta *m) unsigned cnt; int rc; - for (cnt = 0; m->v->sections[cnt + 1 /* skip header */].iov_len; cnt++); + for (cnt = 0; m->v->sections[cnt].iov_len; cnt++); debug("Reading %u migration sections", cnt); - rc = read_remainder(fd, m->v->sections + 1, cnt, 0); + rc = read_remainder(fd, m->v->sections, cnt, 0); if (rc < 0) return errno; @@ -287,12 +359,19 @@ static int migrate_target(struct ctx *c, int fd) struct migrate_meta m; int rc; - rc = migrate_target_read_header(fd, &m); + 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; diff --git a/migrate.h b/migrate.h index b532bde0..343448c8 100644 --- a/migrate.h +++ b/migrate.h @@ -6,20 +6,32 @@ #ifndef MIGRATE_H #define MIGRATE_H +typedef char migrate_magic_t[8]; + +/** + * struct migrate_header - Initial header used for all protocol versions + * @magic: '{passty}' as a byte array + * @version: Version of the protocol used by sender, network order + * @compat_version: Lowest compatible protocol version, network order + */ +struct migrate_header { + migrate_magic_t magic; + uint32_t version; + uint32_t compat_version; +}; + /** - * union migrate_header - Migration header from source - * @magic: 0xB1BB1D1B0BB1D1B0, host order - * @version: Source sends highest known, target aborts if unsupported + * union migrate_header_v1 - Migration header from source + * @endian: 0x12345678, source host order * @voidp_size: sizeof(void *), network order * @time_t_size: sizeof(time_t), network order * @flow_size: sizeof(union flow), network order * @flow_sidx_size: sizeof(struct flow_sidx_t), network order * @unused: Go figure */ -union migrate_header { +union migrate_header_v1 { struct { - uint64_t magic; - uint32_t version; + uint32_t endian; uint32_t voidp_size; uint32_t time_t_size; uint32_t flow_size; @@ -41,12 +53,14 @@ struct migrate_handler { /** * struct migrate_version - Handlers and data sections for a protocol version * @v: Source version this applies to, host order + * @read_header_v: Read version specific header(s) * @pre: Set of functions to execute in target before data copy * @sections: Array of data sections, NULL-terminated * @post: Set of functions to execute in target after data copy */ struct migrate_version { uint32_t v; + int (*read_header_v)(int fd, struct migrate_meta *m); struct migrate_handler *pre; struct iovec *sections; struct migrate_handler *post; -- 2.48.1