[lxc-devel] [lxc/master] criu: add feature check capability

adrianreber on Github lxc-bot at linuxcontainers.org
Thu Dec 14 07:39:33 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 1156 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20171214/2ead1675/attachment.bin>
-------------- next part --------------
From be2c083206699978f65827463a6372d84debef61 Mon Sep 17 00:00:00 2001
From: Adrian Reber <areber at redhat.com>
Date: Wed, 13 Dec 2017 12:04:02 +0100
Subject: [PATCH 1/2] criu: add feature check capability

For migration optimization features like pre-copy or post-copy migration
the support cannot be determined by simply looking at the CRIU version.
Features like that depend on the architecture/kernel/criu combination
and CRIU offers a feature checking interface to query if it is
supported.

This adds a LXC interface to query CRIU for those feature via the
migrate() API call. For the recent pre-copy migration support in LXD
this can be used to automatically detect if pre-copy migration should be
used.

In addition to the existing migrate() API commands this adds a new
command: 'MIGRATE_FEATURE_CHECK'.

The migrate_opts{} structure is extended by the member features_to_check
which is a bitmask defining which CRIU features should be queried.

Currently only the querying of the features FEATURE_MEM_TRACK and
FEATURE_LAZY_PAGES is supported.

Signed-off-by: Adrian Reber <areber at redhat.com>
---
 src/lxc/criu.c         | 100 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/lxc/criu.h         |   1 +
 src/lxc/lxccontainer.c |  10 +++++
 src/lxc/lxccontainer.h |  14 +++++++
 4 files changed, 125 insertions(+)

diff --git a/src/lxc/criu.c b/src/lxc/criu.c
index 245328ab1..92e2c6f64 100644
--- a/src/lxc/criu.c
+++ b/src/lxc/criu.c
@@ -652,6 +652,106 @@ static void exec_criu(struct criu_opts *opts)
 	free(argv);
 }
 
+/*
+ * Function to check if the checks activated in 'features_to_check' are
+ * available with the current architecture/kernel/criu combination.
+ *
+ * Parameter features_to_check is a bit mask of all features that should be
+ * checked (see feature check defines in lxc/lxccontainer.h).
+ *
+ * If the return value is true, all requested features are supported. If
+ * the return value is false the features_to_check parameter is updated
+ * to reflect which features are available. '0' means no feature but
+ * also that something went totally wrong.
+ *
+ * Some of the code flow of criu_version_ok() is duplicated and maybe it
+ * is a good candidate for refactoring.
+ */
+bool __criu_check_feature(uint64_t *features_to_check)
+{
+	pid_t pid;
+	uint64_t current_bit = 0;
+	int ret;
+	int features = *features_to_check;
+	/* Feature checking is currently always like
+	 * criu check --feature <feature-name>
+	 */
+	char *args[] = { "criu", "check", "--feature", NULL, NULL };
+
+	if ((features & ~FEATURE_MEM_TRACK & ~FEATURE_LAZY_PAGES) != 0) {
+		/* There are feature bits activated we do not understand.
+		 * Refusing to answer at all */
+		*features_to_check = 0;
+		return false;
+	}
+
+	while (current_bit < sizeof(uint64_t) * 8) {
+		/* only test requested features */
+		if (!(features & (1ULL << current_bit))) {
+			/* skip this */
+			current_bit++;
+			continue;
+		}
+
+		pid = fork();
+		if (pid < 0) {
+			SYSERROR("fork() failed");
+			*features_to_check = 0;
+			return false;
+		}
+
+		if (pid == 0) {
+			if ((1ULL << current_bit) == FEATURE_MEM_TRACK)
+				/* This is needed for pre-dump support, which
+				 * enables pre-copy migration. */
+				args[3] = "mem_dirty_track";
+			else if ((1ULL << current_bit) == FEATURE_LAZY_PAGES)
+				/* CRIU has two checks for userfaultfd support.
+				 *
+				 * The simpler check is only for 'uffd'. If the
+				 * kernel supports userfaultfd without noncoop
+				 * then only process can be lazily restored
+				 * which do not fork. With 'uffd-noncoop'
+				 * it is also possible to lazily restore processes
+				 * which do fork. For a container runtime like
+				 * LXC checking only for 'uffd' makes not much sense. */
+				args[3] = "uffd-noncoop";
+			else
+				exit(1);
+
+			null_stdfds();
+
+			execvp("criu", args);
+			SYSERROR("Failed to exec \"criu\"");
+			exit(1);
+		}
+
+		ret = lxc_wait_for_pid_status(pid);
+
+		if (ret < 0) {
+			SYSERROR("execing criu failed, is it installed?");
+			*features_to_check = 0;
+			return false;
+		}
+
+		if (ret > 0) {
+			INFO("feature not supported");
+			/* Clear not supported feature bit */
+			features &= ~(1ULL << current_bit);
+		}
+
+		current_bit++;
+		/* no more checks requested; exit check loop */
+		if (!(features & ~((1ULL << current_bit)-1)))
+			break;
+	}
+	if (features != *features_to_check) {
+		*features_to_check = features;
+		return false;
+	}
+	return true;
+}
+
 /*
  * Check to see if the criu version is recent enough for all the features we
  * use. This version allows either CRIU_VERSION or (CRIU_GITID_VERSION and
diff --git a/src/lxc/criu.h b/src/lxc/criu.h
index ce94b3177..9f842a90e 100644
--- a/src/lxc/criu.h
+++ b/src/lxc/criu.h
@@ -30,5 +30,6 @@
 bool __criu_pre_dump(struct lxc_container *c, struct migrate_opts *opts);
 bool __criu_dump(struct lxc_container *c, struct migrate_opts *opts);
 bool __criu_restore(struct lxc_container *c, struct migrate_opts *opts);
+bool __criu_check_feature(uint64_t *features_to_check);
 
 #endif
diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c
index 934754b6e..a7c10b926 100644
--- a/src/lxc/lxccontainer.c
+++ b/src/lxc/lxccontainer.c
@@ -4474,6 +4474,7 @@ static int do_lxcapi_migrate(struct lxc_container *c, unsigned int cmd,
 {
 	int ret = -1;
 	struct migrate_opts *valid_opts = opts;
+	uint64_t features_to_check = 0;
 
 	/* If the caller has a bigger (newer) struct migrate_opts, let's make
 	 * sure that the stuff on the end is zero, i.e. that they didn't ask us
@@ -4527,6 +4528,15 @@ static int do_lxcapi_migrate(struct lxc_container *c, unsigned int cmd,
 		}
 		ret = !__criu_restore(c, valid_opts);
 		break;
+	case MIGRATE_FEATURE_CHECK:
+		features_to_check = valid_opts->features_to_check;
+		ret = !__criu_check_feature(&features_to_check);
+		if (ret) {
+			/* Something went wrong. Let's let the caller
+			 * know which feature checks failed. */
+			valid_opts->features_to_check = features_to_check;
+		}
+		break;
 	default:
 		ERROR("invalid migrate command %u", cmd);
 		ret = -EINVAL;
diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h
index 5938fa3d1..da709cc90 100644
--- a/src/lxc/lxccontainer.h
+++ b/src/lxc/lxccontainer.h
@@ -904,8 +904,15 @@ enum {
 	MIGRATE_PRE_DUMP,
 	MIGRATE_DUMP,
 	MIGRATE_RESTORE,
+	MIGRATE_FEATURE_CHECK,
 };
 
+/*!
+ * \brief Available feature checks.
+ */
+#define FEATURE_MEM_TRACK    (1ULL << 0)
+#define FEATURE_LAZY_PAGES   (1ULL << 1)
+
 /*!
  * \brief Options for the migrate API call.
  */
@@ -942,6 +949,13 @@ struct migrate_opts {
 	 * which at this time is 1MB.
 	 */
 	uint64_t ghost_limit;
+
+	/* Some features cannot be checked by comparing the CRIU version.
+	 * Features like dirty page tracking or userfaultfd depend on
+	 * the architecture/kernel/criu combination. This is a bitmask
+	 * in which the desired feature checks can be encoded.
+	 */
+	uint64_t features_to_check;
 };
 
 struct lxc_console_log {

From 5ec272589903a52316ee18c86b3bfbc9ea145369 Mon Sep 17 00:00:00 2001
From: Adrian Reber <areber at redhat.com>
Date: Wed, 13 Dec 2017 12:14:58 +0100
Subject: [PATCH 2/2] criu: add a test case for the criu feature check support

This adds a simple test case which verifies that the new migrate() API
command 'MIGRATE_FEATURE_CHECK' works as expected.

If a feature does not exist on the currently running
architecture/kernel/criu combination it does not report an error as this
is a valid scenario.

Signed-off-by: Adrian Reber <areber at redhat.com>
---
 src/tests/Makefile.am          |  5 ++-
 src/tests/criu_check_feature.c | 90 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 94 insertions(+), 1 deletion(-)
 create mode 100644 src/tests/criu_check_feature.c

diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
index b38c93c67..0525ca909 100644
--- a/src/tests/Makefile.am
+++ b/src/tests/Makefile.am
@@ -32,6 +32,7 @@ lxc_test_shortlived_SOURCES = shortlived.c
 lxc_test_livepatch_SOURCES = livepatch.c lxctest.h
 lxc_test_state_server_SOURCES = state_server.c lxctest.h
 lxc_test_share_ns_SOURCES = share_ns.c lxctest.h
+lxc_test_criu_check_feature_SOURCES = criu_check_feature.c lxctest.h
 
 AM_CFLAGS=-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
 	-DLXCPATH=\"$(LXCPATH)\" \
@@ -61,7 +62,8 @@ bin_PROGRAMS = lxc-test-containertests lxc-test-locktests lxc-test-startone \
 	lxc-test-reboot lxc-test-list lxc-test-attach lxc-test-device-add-remove \
 	lxc-test-apparmor lxc-test-utils lxc-test-parse-config-file \
 	lxc-test-config-jump-table lxc-test-shortlived lxc-test-livepatch \
-	lxc-test-api-reboot lxc-test-state-server lxc-test-share-ns
+	lxc-test-api-reboot lxc-test-state-server lxc-test-share-ns \
+	lxc-test-criu-check-feature
 
 bin_SCRIPTS = lxc-test-automount \
 	      lxc-test-autostart \
@@ -93,6 +95,7 @@ EXTRA_DIST = \
 	console_log.c \
 	containertests.c \
 	createtest.c \
+	criu_check_feature.c \
 	destroytest.c \
 	device_add_remove.c \
 	get_item.c \
diff --git a/src/tests/criu_check_feature.c b/src/tests/criu_check_feature.c
new file mode 100644
index 000000000..a5e0c34eb
--- /dev/null
+++ b/src/tests/criu_check_feature.c
@@ -0,0 +1,90 @@
+/* liblxcapi
+ *
+ * Copyright © 2017 Adrian Reber <areber at redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <string.h>
+#include <limits.h>
+
+#include "lxc/lxccontainer.h"
+#include "lxctest.h"
+
+int main(int argc, char *argv[])
+{
+	struct lxc_container *c;
+	struct migrate_opts m_opts;
+	int ret = EXIT_FAILURE;
+
+	/* Test the feature check interface,
+	 * we actually do not need a container. */
+	c = lxc_container_new("check_feature", NULL);
+	if (!c) {
+		lxc_error("%s", "Failed to create container \"check_feature\"");
+		exit(ret);
+	}
+
+	if (c->is_defined(c)) {
+		lxc_error("%s\n", "Container \"check_feature\" is defined");
+		goto on_error_put;
+	}
+
+	/* check the migrate API call with wrong 'cmd' */
+	if (!c->migrate(c, UINT_MAX, &m_opts, sizeof(struct migrate_opts))) {
+		/* This should failed */
+		lxc_error("%s\n", "Migrate API calls with command UINT_MAX did not fail");
+		goto on_error_put;
+	}
+
+	/* do the actual fature check for memory tracking */
+	m_opts.features_to_check = FEATURE_MEM_TRACK;
+	if (c->migrate(c, MIGRATE_FEATURE_CHECK, &m_opts, sizeof(struct migrate_opts))) {
+		lxc_debug("%s\n", "System does not support \"FEATURE_MEM_TRACK\".");
+	}
+
+	/* check for lazy pages */
+	m_opts.features_to_check = FEATURE_LAZY_PAGES;
+	if (c->migrate(c, MIGRATE_FEATURE_CHECK, &m_opts, sizeof(struct migrate_opts))) {
+		lxc_debug("%s\n", "System does not support \"FEATURE_LAZY_PAGES\".");
+	}
+
+	/* check for lazy pages and memory tracking */
+	m_opts.features_to_check = FEATURE_LAZY_PAGES | FEATURE_MEM_TRACK;
+	if (c->migrate(c, MIGRATE_FEATURE_CHECK, &m_opts, sizeof(struct migrate_opts))) {
+		if (m_opts.features_to_check == FEATURE_LAZY_PAGES)
+			lxc_debug("%s\n", "System does not support \"FEATURE_MEM_TRACK\"");
+		else if (m_opts.features_to_check == FEATURE_MEM_TRACK)
+			lxc_debug("%s\n", "System does not support \"FEATURE_LAZY_PAGES\"");
+		else
+			lxc_debug("%s\n", "System does not support \"FEATURE_MEM_TRACK\" "
+					"and \"FEATURE_LAZY_PAGES\"");
+	}
+
+	/* test for unknown feature; once there are 64 features to test
+	 * this will be valid... */
+	m_opts.features_to_check = -1ULL;
+	if (!c->migrate(c, MIGRATE_FEATURE_CHECK, &m_opts, sizeof(struct migrate_opts))) {
+		lxc_error("%s\n", "Unsupported feature supported, which is strange.");
+		goto on_error_put;
+	}
+
+	ret = EXIT_SUCCESS;
+
+on_error_put:
+	lxc_container_put(c);
+	if (ret == EXIT_SUCCESS)
+		lxc_debug("%s\n", "All criu feature check tests passed");
+	exit(ret);
+}


More information about the lxc-devel mailing list