]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
criu: add feature check capability
authorAdrian Reber <areber@redhat.com>
Wed, 13 Dec 2017 11:04:02 +0000 (12:04 +0100)
committerAdrian Reber <areber@redhat.com>
Thu, 14 Dec 2017 19:34:51 +0000 (20:34 +0100)
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>
src/lxc/criu.c
src/lxc/criu.h
src/lxc/lxccontainer.c
src/lxc/lxccontainer.h

index 245328ab17c2ae95b75ee65037dd3c2fa34ed6b5..0341ba2f46901aa707cc98427135d20a9411a3b5 100644 (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
index ce94b31777aa26f87a6cfef0f459b5242994641a..9f842a90e912f6946f048afed3338af5d7e040ab 100644 (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
index 934754b6e5341810305a57a153ecb064837da722..a7c10b9269bdd770396b2381fede3a3ea558fa22 100644 (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;
index 5938fa3d1510897b220f242667873f30a59c33e2..da709cc909f83920d2adc3400fdd30899f1fbf88 100644 (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 {