From b5b12b9e75935836213b7637d018c93763183945 Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 13 Dec 2017 12:04:02 +0100 Subject: [PATCH] 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 --- src/lxc/criu.c | 98 ++++++++++++++++++++++++++++++++++++++++++ src/lxc/criu.h | 1 + src/lxc/lxccontainer.c | 10 +++++ src/lxc/lxccontainer.h | 14 ++++++ 4 files changed, 123 insertions(+) diff --git a/src/lxc/criu.c b/src/lxc/criu.c index 245328ab1..0341ba2f4 100644 --- a/src/lxc/criu.c +++ b/src/lxc/criu.c @@ -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 + */ + 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 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 {