public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
From: David Gibson <david@gibson.dropbear.id.au>
To: passt-dev@passt.top, Stefano Brivio <sbrivio@redhat.com>
Cc: David Gibson <david@gibson.dropbear.id.au>
Subject: [PATCH 2/2] migrate: More sophisticated versioning
Date: Fri, 31 Jan 2025 15:52:15 +1100	[thread overview]
Message-ID: <20250131045215.1368543-3-david@gibson.dropbear.id.au> (raw)
In-Reply-To: <20250131045215.1368543-1-david@gibson.dropbear.id.au>

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@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@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;
-- 
@@ -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


  parent reply	other threads:[~2025-01-31  4:52 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-01-31  4:52 [PATCH 0/2] Fancier version handling for migration David Gibson
2025-01-31  4:52 ` [PATCH 1/2] migrate: Merge protocol version information into one table David Gibson
2025-01-31  5:53   ` Stefano Brivio
2025-01-31  6:17     ` David Gibson
2025-01-31  4:52 ` David Gibson [this message]
2025-01-31  5:53   ` [PATCH 2/2] migrate: More sophisticated versioning Stefano Brivio
2025-01-31  6:21     ` David Gibson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250131045215.1368543-3-david@gibson.dropbear.id.au \
    --to=david@gibson.dropbear.id.au \
    --cc=passt-dev@passt.top \
    --cc=sbrivio@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://passt.top/passt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).