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: 's avatarAdrian Reber <areber@redhat.com>
parent f449521c
......@@ -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
......@@ -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;
......
......@@ -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 {
......
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