Unverified Commit 12401528 by Christian Brauner Committed by GitHub

Merge pull request #2035 from adrianreber/master

criu: add feature check capability
parents 81b10e37 739ef90c
......@@ -653,6 +653,104 @@ err:
}
/*
* 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 = wait_for_pid(pid);
if (ret == -1) {
/* It is not known why CRIU failed. Either
* CRIU is not available, the feature check
* does not exist or the feature is not
* supported. */
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
* CRIU_GITID_PATCHLEVEL) to work, enabling users building from git to c/r
......
......@@ -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
......@@ -4510,6 +4510,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
......@@ -4563,6 +4564,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;
......
......@@ -904,9 +904,16 @@ 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.
*/
struct migrate_opts {
......@@ -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 {
......
......@@ -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 \
......
/* liblxcapi
*
* Copyright © 2017 Adrian Reber <areber@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);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment