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@redhat.com>
This commit is contained in:
Adrian Reber 2017-12-13 12:04:02 +01:00
parent f449521ce6
commit b5b12b9e75
Failed to extract signature
4 changed files with 123 additions and 0 deletions

View File

@ -652,6 +652,104 @@ err:
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 = 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

View File

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

View File

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

View File

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