[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