<div dir="ltr">Pavel and Adrian,<div><br></div><div>Please take a look at this patch and let me know what you think.</div><div><br></div><div>As you will see, I have changed some pr_error() to pr_warn() when calling &quot;criu check --extra&quot; because we&#39;re saying that the extra features do not necessarily cause dump/restore failures.  But we can&#39;t change all pr_error() calls to pr_warn() indiscriminately in the functions that are called by &quot;criu check --extra&quot; because some of them are real errors.</div><div><br></div><div>Another item of discussion is the exit code.  Obviously, if there are no errors or warnings the exit code has to be zero.  But what if there&#39;s a warning on an extra or specific feature?  Currently, the exit code is non-zero but one can argue that for warnings it should still be zero.</div><div><br></div><div>The current patch is a definite improvement (because it was broken) but to make it &quot;perfect&quot; will require more time and iterations.  It&#39;s also very difficult to test all permutations.</div><div><br></div><div>--Saied</div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Mar 10, 2016 at 12:31 PM, Saied Kazemi <span dir="ltr">&lt;<a href="mailto:saied@google.com" target="_blank">saied@google.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">The &quot;criu check&quot; command to check if the kernel is properly configured<br>
to run criu is broken.<br>
<br>
The &quot;criu check --ms&quot; command used to be the way to tell criu to check<br>
only for features that have been merged upstream.  But recent kernels<br>
</span>have a set of features whose presence doesn&#39;t necessarily mean that<br>
dump or restore will fail but rather *may* fail depending on whether<br>
the process tree uses those features.<br>
<br>
This patch deprecates --ms and introduces --extra, --experimental,<br>
and --all.  See &quot;criu --help&quot; or &quot;man criu&quot; for more info.<br>
<span class=""><br>
Typical use cases are:<br>
<br>
        $ sudo criu check<br>
</span>        Looks good.<br>
        $ echo $?<br>
        0<br>
<br>
        $ sudo criu check --extra<br>
        Looks good.<br>
<br>
        $ sudo criu check --extra<br>
        &lt;one or more warnings...&gt;<br>
<span class="">        Looks good but some kernel features are missing<br>
</span>        which, depending on your process tree, may cause<br>
        dump or restore failure.<br>
        $ echo $?<br>
        1<br>
<span class=""><br>
        $ sudo criu check --feature list<br>
        mnt_id aio_remap timerfd tun userns fdinfo_lock seccomp_suspend \<br>
                seccomp_filters loginuid cgroupns<br>
<br>
</span><span class="">        $ sudo criu check --feature mnt_id<br>
</span>        Warn  (cr-check.c:283): fdinfo doesn&#39;t contain the mnt_id field<br>
        $ echo $?<br>
        1<br>
<br>
        $ sudo criu check --feature tun<br>
        tun is supported<br>
        $ echo $?<br>
        0<br>
<span class=""><br>
Signed-off-by: Saied Kazemi &lt;<a href="mailto:saied@google.com">saied@google.com</a>&gt;<br>
---<br>
</span> Documentation/criu.txt    |  48 ++++++++--<br>
 criu/cr-check.c           | 217 ++++++++++++++++++++++++++--------------------<br>
 criu/cr-service.c         |   3 -<br>
 criu/crtools.c            |  40 +++++++--<br>
 criu/include/cr_options.h |   3 +-<br>
 criu/timerfd.c            |   2 +-<br>
 6 files changed, 196 insertions(+), 117 deletions(-)<br>
<br>
diff --git a/Documentation/criu.txt b/Documentation/criu.txt<br>
index e7ba6dd..bd246bf 100644<br>
--- a/Documentation/criu.txt<br>
+++ b/Documentation/criu.txt<br>
@@ -194,7 +194,7 @@ In other words, do not use it until really needed.<br>
<br>
 *-j*, *--shell-job*::<br>
     Allow one to dump shell jobs. This implies the restored task will inherit session and<br>
-    process group ID from the criu itself. Also this option allows one to migrate a<br>
+    process group ID from the *criu* itself. Also this option allows one to migrate a<br>
     single external tty connection, in other words this option allows one to migrate<br>
     such application as &quot;top&quot; and friends. If passed on *dump* it must be<br>
     specified on *restore* as well.<br>
@@ -267,7 +267,7 @@ The &#39;&lt;mode&gt;&#39; may be one of below.<br>
 *--ext-mount-map* *auto*::<br>
     This is a special case. If this flag is passed, when an external<br>
     mount is missing from the command line &#39;*--ext-mount-map* &lt;KEY&gt;:&lt;VAL&gt;&#39; syntax,<br>
-    criu attempts to automatically resolve this mount from its namespace.<br>
+    *criu* attempts to automatically resolve this mount from its namespace.<br>
<br>
 *--enable-external-sharing*, *--enable-external-masters*::<br>
     These flags enable external shared or slave mounts to be resolved<br>
@@ -278,7 +278,7 @@ The &#39;&lt;mode&gt;&#39; may be one of below.<br>
<br>
 *-j*, *--shell-job*::<br>
     Restore shell jobs, in other words inherit session and process group<br>
-    ID from the criu itself.<br>
+    ID from the *criu* itself.<br>
<br>
 *--cpu-cap* [&#39;&lt;cap&gt;&#39;,&#39;&lt;cap&gt;&#39;]::<br>
     Specify &#39;&lt;cap&gt;&#39; CPU capability to be present on the CPU the process is<br>
@@ -314,15 +314,45 @@ relaxing capability with *--cpu-cap*=*none* parameter.<br>
<br>
 *check*<br>
 ~~~~~~~<br>
-Tests whether the kernel support is up to date.<br>
+Checks whether the kernel supports the features that *criu* needs to<br>
+successfully dump and restore a process tree.<br>
<br>
-*--ms*::<br>
-    Do not check not yet merged features.<br>
+There are three categories of kernel support as described below.  *criu<br>
+check* always checks Category 1 features unless *--feature* is specified<br>
+which only checks the specified feature.<br>
+<br>
+If there are no errors or warnings, *criu* prints &quot;Looks good.&quot; and its<br>
+exit code is 0.  Otherwise, *criu* prints appropriate error and warning<br>
+messages and its exit code is non-zero.<br>
+<br>
+- *Category 1*.     Absolutely required. These are features like<br>
+                    &#39;/proc/&lt;pid&gt;/map_files&#39;, &#39;NETLINK_SOCK_DIAG&#39; socket<br>
+                    monitoring, &#39;/proc/sys/kernel/ns_last_pid&#39;, etc.<br>
+<br>
+- *Category 2*.     Required only for specific cases. These are features<br>
+                    like aio remap, &#39;/dev/net/tun&#39;, etc. that are<br>
+                    required if the process being dumped or restored<br>
+                    is using them.<br>
+<br>
+- *Category 3*.     Experimental. These are features like task-diag that<br>
+                    are used for experimental purposes (mostly<br>
+                    during development).<br>
+<br>
+Without an argument, *criu check* checks Category 1 features. This behavior<br>
+can change with the following options:<br>
+<br>
+*--extra*::<br>
+    Check kernel support for Category 2 features.<br>
+<br>
+*--experimental*::<br>
+    Check kernel support for Category 3 features.<br>
+<br>
+*--all*::<br>
+    Check kernel support for Category 1, 2, and 3 features.<br>
<br>
 *--feature* &#39;&lt;name&gt;&#39;::<br>
-    Check a particular feature. Instead of checking everything one may specify<br>
-    which exactly feature is to be tested. The &#39;&lt;name&gt;&#39; may be: *mnt_id*,<br>
-    *aio_remap*, *timerfd*, *tun*, *userns*.<br>
+    Check a specific feature.  If &#39;&lt;name&gt;&#39; is &#39;list&#39;, a list of valid<br>
+    kernel feature names that can be checked will be printed.<br>
<br>
 *page-server*<br>
 ~~~~~~~~~~~~~<br>
diff --git a/criu/cr-check.c b/criu/cr-check.c<br>
index 2896b02..f3f2021 100644<br>
--- a/criu/cr-check.c<br>
+++ b/criu/cr-check.c<br>
@@ -41,6 +41,8 @@<br>
 #include &quot;pstree.h&quot;<br>
 #include &quot;cr_options.h&quot;<br>
<br>
+static char *feature_name(int (*func)());<br>
+<br>
 static int check_tty(void)<br>
 {<br>
        int master = -1, slave = -1;<br>
@@ -169,9 +171,8 @@ static int check_kcmp(void)<br>
        return -1;<br>
 }<br>
<br>
-static int check_prctl(void)<br>
+static int check_prctl(int pr_set_mm_map)<br>
<span class=""> {<br>
-       unsigned long user_auxv = 0;<br>
        unsigned int *tid_addr;<br>
        unsigned int size = 0;<br>
        int ret;<br>
</span>@@ -182,38 +183,13 @@ static int check_prctl(void)<br>
                return -1;<br>
        }<br>
<br>
-       /*<br>
<span class="">-        * Either new or old interface must be supported in the kernel.<br>
</span>-        */<br>
-       ret = prctl(PR_SET_MM, PR_SET_MM_MAP_SIZE, (unsigned long)&amp;size, 0, 0);<br>
<div><div class="h5">-       if (ret) {<br>
-               if (!opts.check_ms_kernel) {<br>
-                       pr_msg(&quot;prctl: PR_SET_MM_MAP is not supported, which &quot;<br>
-                              &quot;is required for restoring user namespaces\n&quot;);<br>
-                       return -1;<br>
-               } else<br>
-                       pr_warn(&quot;Skipping unssuported PR_SET_MM_MAP\n&quot;);<br>
-<br>
-               ret = prctl(PR_SET_MM, PR_SET_MM_BRK, brk(0), 0, 0);<br>
-               if (ret) {<br>
-                       if (ret == -EPERM)<br>
-                               pr_msg(&quot;prctl: One needs CAP_SYS_RESOURCE capability to perform testing\n&quot;);<br>
-                       else<br>
-                               pr_msg(&quot;prctl: PR_SET_MM is not supported\n&quot;);<br>
-                       return -1;<br>
-               }<br>
-<br>
-               ret = prctl(PR_SET_MM, PR_SET_MM_EXE_FILE, -1, 0, 0);<br>
-               if (ret != -EBADF) {<br>
-                       pr_msg(&quot;prctl: PR_SET_MM_EXE_FILE is not supported (%d)\n&quot;, ret);<br>
-                       return -1;<br>
-               }<br>
-<br>
-               ret = prctl(PR_SET_MM, PR_SET_MM_AUXV, (long)&amp;user_auxv, sizeof(user_auxv), 0);<br>
-               if (ret) {<br>
-                       pr_msg(&quot;prctl: PR_SET_MM_AUXV is not supported\n&quot;);<br>
-                       return -1;<br>
-               }<br>
</div></div>+       if (pr_set_mm_map) {<br>
+               /*<br>
+                * PR_SET_MM_MAP is optional because criu/pie/restorer.c<br>
+                * has proper fall-back in case this API is not supported.<br>
+                */<br>
+               if (prctl(PR_SET_MM, PR_SET_MM_MAP_SIZE, (unsigned long)&amp;size, 0, 0))<br>
+                       pr_warn(&quot;prctl: PR_SET_MM_MAP is not supported\n&quot;);<br>
        }<br>
<br>
        return 0;<br>
@@ -304,7 +280,7 @@ int check_mnt_id(void)<br>
                return -1;<br>
<br>
        if (fdinfo.mnt_id == -1) {<br>
-               pr_err(&quot;fdinfo doesn&#39;t contain the mnt_id field\n&quot;);<br>
+               pr_warn(&quot;fdinfo doesn&#39;t contain the mnt_id field\n&quot;);<br>
                return -1;<br>
        }<br>
<br>
@@ -626,7 +602,7 @@ static int check_ptrace_suspend_seccomp(void)<br>
<br>
        if (ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_SUSPEND_SECCOMP) &lt; 0) {<br>
                if (errno == EINVAL) {<br>
-                       pr_err(&quot;Kernel doesn&#39;t support PTRACE_O_SUSPEND_SECCOMP\n&quot;);<br>
+                       pr_warn(&quot;Kernel doesn&#39;t support PTRACE_O_SUSPEND_SECCOMP\n&quot;);<br>
                } else {<br>
                        pr_perror(&quot;couldn&#39;t suspend seccomp&quot;);<br>
                }<br>
@@ -670,7 +646,7 @@ static int check_ptrace_dump_seccomp_filters(void)<br>
        len = ptrace(PTRACE_SECCOMP_GET_FILTER, pid, 0, NULL);<br>
        if (len &lt; 0) {<br>
                ret = -1;<br>
-               pr_perror(&quot;Dumping seccomp filters not supported&quot;);<br>
+               pr_warn(&quot;Dumping seccomp filters not supported&quot;);<br>
        }<br>
<br>
        kill(pid, SIGKILL);<br>
@@ -766,11 +742,8 @@ static int check_aio_remap(void)<br>
<span class="">        ctx = (aio_context_t)naddr;<br>
        r = syscall(SYS_io_getevents, ctx, 0, 1, NULL, NULL);<br>
        if (r &lt; 0) {<br>
-               if (!opts.check_ms_kernel) {<br>
-                       pr_err(&quot;AIO remap doesn&#39;t work properly\n&quot;);<br>
-                       return -1;<br>
-               } else<br>
-                       pr_warn(&quot;Skipping unsupported AIO remap\n&quot;);<br>
</span>+               pr_warn(&quot;AIO remap doesn&#39;t work properly\n&quot;);<br>
<span class="">+               return -1;<br>
        }<br>
<br>
        return 0;<br>
</span>@@ -782,12 +755,8 @@ static int check_fdinfo_lock(void)<br>
<span class="">                return -1;<br>
<br>
        if (!kdat.has_fdinfo_lock) {<br>
-               if (!opts.check_ms_kernel) {<br>
-                       pr_err(&quot;fdinfo doesn&#39;t contain the lock field\n&quot;);<br>
-                       return -1;<br>
-               } else {<br>
-                       pr_warn(&quot;fdinfo doesn&#39;t contain the lock field\n&quot;);<br>
-               }<br>
</span>+               pr_warn(&quot;fdinfo doesn&#39;t contain the lock field\n&quot;);<br>
<span class="">+               return -1;<br>
        }<br>
<br>
        return 0;<br>
</span>@@ -823,14 +792,10 @@ static int check_clone_parent_vs_pid()<br>
<span class=""> static int check_cgroupns(void)<br>
 {<br>
        int ret;<br>
-       if (opts.check_ms_kernel) {<br>
-               pr_warn(&quot;Skipping cgroup namespaces check\n&quot;);<br>
-               return 0;<br>
-       }<br>
<br>
        ret = access(&quot;/proc/self/ns/cgroup&quot;, F_OK);<br>
        if (ret &lt; 0) {<br>
</span>-               pr_err(&quot;cgroupns not supported. This is not fatal.\n&quot;);<br>
+               pr_warn(&quot;cgroupns not supported. This is not fatal.\n&quot;);<br>
                return -1;<br>
        }<br>
<br>
@@ -839,6 +804,22 @@ static int check_cgroupns(void)<br>
<span class=""><br>
 static int (*chk_feature)(void);<br>
<br>
+/*<br>
+ * There are three categories of kernel features:<br>
+ *<br>
</span>+ * 1. Absolutely required (/proc/pid/map_files, ptrace PEEKSIGINFO, etc.).<br>
+ * 2. Required only for specific cases (aio remap, tun, etc.).<br>
+ *    Checked when --extra or --all is specified.<br>
+ * 3. Experimental (task-diag).<br>
+ *    Checked when --experimental or --all is specified.<br>
<span class="">+ *<br>
+ * We fail if any feature in category 1 is missing but tolerate failures<br>
+ * in the other categories.  Currently, there is nothing in category 3.<br>
+ */<br>
+#define GOOD           &quot;Looks good.&quot;<br>
</span>+#define GOOD_BUT       &quot;Looks good but some kernel features are missing\n&quot; \<br>
+                       &quot;which, depending on your process tree, may cause\n&quot; \<br>
+                       &quot;dump or restore failure.&quot;<br>
<span class=""> int cr_check(void)<br>
 {<br>
        struct ns_id ns = { .type = NS_CRIU, .ns_pid = PROC_SELF, .nd = &amp;mnt_ns_desc };<br>
</span>@@ -863,16 +844,22 @@ int cr_check(void)<br>
<span class="">                return -1;<br>
<br>
        if (chk_feature) {<br>
-               ret = chk_feature();<br>
</span>-               goto out;<br>
<span class="">+               if (chk_feature())<br>
+                       return -1;<br>
</span>+               print_on_level(DEFAULT_LOGLEVEL, &quot;%s is supported\n&quot;,<br>
+                       feature_name(chk_feature));<br>
+               return 0;<br>
<span class="">        }<br>
<br>
+       /*<br>
+        * Category 1 - absolutely required.<br>
+        */<br>
</span>        ret |= check_map_files();<br>
        ret |= check_sock_diag();<br>
        ret |= check_ns_last_pid();<br>
        ret |= check_sock_peek_off();<br>
<span class="">        ret |= check_kcmp();<br>
-       ret |= check_prctl();<br>
</span>+       ret |= check_prctl(0);<br>
        ret |= check_fcntl();<br>
        ret |= check_proc_stat();<br>
        ret |= check_tcp();<br>
@@ -883,22 +870,39 @@ int cr_check(void)<br>
        ret |= check_ipc();<br>
        ret |= check_sigqueuinfo();<br>
        ret |= check_ptrace_peeksiginfo();<br>
-       ret |= check_ptrace_suspend_seccomp();<br>
-       ret |= check_ptrace_dump_seccomp_filters();<br>
-       ret |= check_mem_dirty_track();<br>
-       ret |= check_posix_timers();<br>
-       ret |= check_tun_cr(0);<br>
-       ret |= check_timerfd();<br>
-       ret |= check_mnt_id();<br>
-       ret |= check_aio_remap();<br>
-       ret |= check_fdinfo_lock();<br>
-       ret |= check_clone_parent_vs_pid();<br>
-       ret |= check_cgroupns();<br>
+       if (ret)<br>
+               return ret;<br>
<br>
-out:<br>
<span class="">-       if (!ret)<br>
-               print_on_level(DEFAULT_LOGLEVEL, &quot;Looks good.\n&quot;);<br>
</span><span class="">+       /*<br>
+        * Category 2 - required for specific cases.<br>
+        */<br>
</span>+       if (opts.check_extra_features) {<br>
+               ret |= check_prctl(1);<br>
+               ret |= check_ptrace_suspend_seccomp();<br>
+               ret |= check_ptrace_dump_seccomp_filters();<br>
+               ret |= check_mem_dirty_track();<br>
+               ret |= check_posix_timers();<br>
+               ret |= check_tun_cr(0);<br>
+               ret |= check_timerfd();<br>
+               ret |= check_mnt_id();<br>
+               ret |= check_aio_remap();<br>
+               ret |= check_fdinfo_lock();<br>
+               ret |= check_clone_parent_vs_pid();<br>
+               ret |= check_cgroupns();<br>
+       }<br>
<br>
+       /*<br>
+        * Category 3 - experimental.<br>
+        */<br>
+       if (opts.check_experimental_features) {<br>
+               /*<br>
+                * Place holder as currently there are no checks.<br>
+                * Remove this comment when adding checks for<br>
+                * experimental features.<br>
+                */<br>
+       }<br>
+<br>
<span class="">+       print_on_level(DEFAULT_LOGLEVEL, &quot;%s\n&quot;, ret ? GOOD_BUT : GOOD);<br>
</span>        return ret;<br>
 }<br>
<br>
@@ -920,14 +924,14 @@ static int check_userns(void)<br>
<br>
        ret = access(&quot;/proc/self/ns/user&quot;, F_OK);<br>
        if (ret) {<br>
-               pr_perror(&quot;No userns proc file&quot;);<br>
+               pr_warn(&quot;No userns proc file&quot;);<br>
                return -1;<br>
<span class="">        }<br>
<br>
        ret = prctl(PR_SET_MM, PR_SET_MM_MAP_SIZE, (unsigned long)&amp;size, 0, 0);<br>
        if (ret) {<br>
</span>                errno = -ret;<br>
-               pr_perror(&quot;No new prctl API&quot;);<br>
+               pr_warn(&quot;No new prctl API&quot;);<br>
                return -1;<br>
        }<br>
<br>
@@ -947,32 +951,53 @@ static int check_loginuid(void)<br>
<div><div class="h5">        return 0;<br>
 }<br>
<br>
+struct feature_list {<br>
+       char *name;<br>
+       int (*func)();<br>
+};<br>
+<br>
+static struct feature_list feature_list[] = {<br>
+       { &quot;mnt_id&quot;, check_mnt_id },<br>
+       { &quot;aio_remap&quot;, check_aio_remap },<br>
+       { &quot;timerfd&quot;, check_timerfd },<br>
+       { &quot;tun&quot;, check_tun },<br>
+       { &quot;userns&quot;, check_userns },<br>
+       { &quot;fdinfo_lock&quot;, check_fdinfo_lock },<br>
+       { &quot;seccomp_suspend&quot;, check_ptrace_suspend_seccomp },<br>
+       { &quot;seccomp_filters&quot;, check_ptrace_dump_seccomp_filters },<br>
+       { &quot;loginuid&quot;, check_loginuid },<br>
+       { &quot;cgroupns&quot;, check_cgroupns },<br>
+       { NULL, NULL },<br>
+};<br>
+<br>
 int check_add_feature(char *feat)<br>
 {<br>
-       if (!strcmp(feat, &quot;mnt_id&quot;))<br>
-               chk_feature = check_mnt_id;<br>
-       else if (!strcmp(feat, &quot;aio_remap&quot;))<br>
-               chk_feature = check_aio_remap;<br>
-       else if (!strcmp(feat, &quot;timerfd&quot;))<br>
-               chk_feature = check_timerfd;<br>
-       else if (!strcmp(feat, &quot;tun&quot;))<br>
-               chk_feature = check_tun;<br>
-       else if (!strcmp(feat, &quot;userns&quot;))<br>
-               chk_feature = check_userns;<br>
-       else if (!strcmp(feat, &quot;fdinfo_lock&quot;))<br>
-               chk_feature = check_fdinfo_lock;<br>
-       else if (!strcmp(feat, &quot;seccomp_suspend&quot;))<br>
-               chk_feature = check_ptrace_suspend_seccomp;<br>
-       else if (!strcmp(feat, &quot;seccomp_filters&quot;))<br>
-               chk_feature = check_ptrace_dump_seccomp_filters;<br>
-       else if (!strcmp(feat, &quot;loginuid&quot;))<br>
-               chk_feature = check_loginuid;<br>
-       else if (!strcmp(feat, &quot;cgroupns&quot;))<br>
-               chk_feature = check_cgroupns;<br>
-       else {<br>
-               pr_err(&quot;Unknown feature %s\n&quot;, feat);<br>
-               return -1;<br>
+       struct feature_list *fl;<br>
+<br>
+       if (!strcmp(feat, &quot;list&quot;)) {<br>
+               for (fl = feature_list; fl-&gt;name; fl++)<br>
+                       pr_msg(&quot;%s &quot;, fl-&gt;name);<br>
+               pr_msg(&quot;\n&quot;);<br>
+               return 1;<br>
        }<br>
<br>
-       return 0;<br>
+       for (fl = feature_list; fl-&gt;name; fl++) {<br>
+               if (!strcmp(feat, fl-&gt;name)) {<br>
+                       chk_feature = fl-&gt;func;<br>
+                       return 0;<br>
+               }<br>
+       }<br>
+       pr_err(&quot;Unknown feature %s\n&quot;, feat);<br>
+       return -1;<br>
</div></div>+}<br>
+<br>
+static char *feature_name(int (*func)())<br>
+{<br>
<span class="">+       struct feature_list *fl;<br>
+<br>
</span>+       for (fl = feature_list; fl-&gt;func; fl++) {<br>
+               if (fl-&gt;func == func)<br>
+                       return fl-&gt;name;<br>
+       }<br>
+       return NULL;<br>
 }<br>
diff --git a/criu/cr-service.c b/criu/cr-service.c<br>
index 88d4af7..032c763 100644<br>
--- a/criu/cr-service.c<br>
+++ b/criu/cr-service.c<br>
@@ -567,9 +567,6 @@ static int check(int sk)<br>
<span class=""><br>
        setproctitle(&quot;check --rpc&quot;);<br>
<br>
-       /* Check only minimal kernel support */<br>
-       opts.check_ms_kernel = true;<br>
</span>-<br>
<span class="">        if (!cr_check())<br>
                resp.success = true;<br>
<br>
diff --git a/criu/crtools.c b/criu/crtools.c<br>
</span>index d6e8672..db4c2d5 100644<br>
--- a/criu/crtools.c<br>
+++ b/criu/crtools.c<br>
@@ -275,6 +275,9 @@ int main(int argc, char *argv[], char *envp[])<br>
<span class="">                { &quot;timeout&quot;,                    required_argument,      0, 1072 },<br>
                { &quot;external&quot;,                   required_argument,      0, 1073 },<br>
                { &quot;empty-ns&quot;,                   required_argument,      0, 1074 },<br>
</span>+               { &quot;extra&quot;,                      no_argument,            0, 1075 },<br>
+               { &quot;experimental&quot;,               no_argument,            0, 1076 },<br>
+               { &quot;all&quot;,                        no_argument,            0, 1077 },<br>
                { },<br>
        };<br>
<br>
@@ -455,8 +458,8 @@ int main(int argc, char *argv[], char *envp[])<br>
<span class="">                        opts.force_irmap = true;<br>
                        break;<br>
                case 1054:<br>
-                       opts.check_ms_kernel = true;<br>
-                       break;<br>
</span>+                       pr_err(&quot;--ms is deprecated; see \&quot;Check options\&quot; of criu --help\n&quot;);<br>
<span class="">+                       return 1;<br>
                case &#39;L&#39;:<br>
                        opts.libdir = optarg;<br>
                        break;<br>
</span>@@ -490,8 +493,11 @@ int main(int argc, char *argv[], char *envp[])<br>
<span class="">                                return 1;<br>
                        break;<br>
                case 1063:<br>
-                       if (check_add_feature(optarg) &lt; 0)<br>
+                       ret = check_add_feature(optarg);<br>
+                       if (ret &lt; 0)    /* invalid kernel feature name */<br>
                                return 1;<br>
+                       if (ret &gt; 0)    /* list kernel features and exit */<br>
+                               return 0;<br>
                        break;<br>
                case 1064:<br>
                        if (!add_skip_mount(optarg))<br>
</span>@@ -554,6 +560,16 @@ int main(int argc, char *argv[], char *envp[])<br>
<span class="">                                return 1;<br>
                        }<br>
                        break;<br>
+               case 1075:<br>
</span>+                       opts.check_extra_features = true;<br>
+                       break;<br>
+               case 1076:<br>
+                       opts.check_experimental_features = true;<br>
+                       break;<br>
+               case 1077:<br>
+                       opts.check_extra_features = true;<br>
+                       opts.check_experimental_features = true;<br>
<span class="">+                       break;<br>
                case &#39;V&#39;:<br>
                        pr_msg(&quot;Version: %s\n&quot;, CRIU_VERSION);<br>
                        if (strcmp(CRIU_GITID, &quot;0&quot;))<br>
</span>@@ -715,7 +731,7 @@ usage:<br>
<span class=""> &quot;Usage:\n&quot;<br>
 &quot;  criu dump|pre-dump -t PID [&lt;options&gt;]\n&quot;<br>
 &quot;  criu restore [&lt;options&gt;]\n&quot;<br>
-&quot;  criu check [--ms]\n&quot;<br>
+&quot;  criu check [--feature FEAT]\n&quot;<br>
 &quot;  criu exec -p PID &lt;syscall-string&gt;\n&quot;<br>
 &quot;  criu page-server\n&quot;<br>
 &quot;  criu service [&lt;options&gt;]\n&quot;<br>
</span>@@ -808,8 +824,19 @@ usage:<br>
<span class=""> &quot;                            socket[inode]\n&quot;<br>
 &quot;                            file[mnt_id:inode]\n&quot;<br>
 &quot;  --empty-ns {net}\n&quot;<br>
-&quot;                      Create a namespace, but don&#39;t restore its properies.\n&quot;<br>
-&quot;                      An user will retore them from action scripts.\n&quot;<br>
+&quot;                        Create a namespace, but don&#39;t restore its properies.\n&quot;<br>
+&quot;                        An user will retore them from action scripts.\n&quot;<br>
+&quot;Check options:\n&quot;<br>
</span>+&quot;  without any arguments, \&quot;criu check\&quot; checks availability of absolutely required\n&quot;<br>
+&quot;  kernel features; if any of these features is missing dump and restore will fail\n&quot;<br>
+&quot;  --extra               also check availability of extra kernel features\n&quot;<br>
+&quot;  --experimental        also check availability of experimental kernel features\n&quot;<br>
+&quot;  --all                 also check availability of extra and experimental kernel features\n&quot;<br>
+&quot;  --feature FEAT        only check availability of one of the following kernel features\n&quot;<br>
<span class="">+&quot;                        &quot;<br>
+       );<br>
+       check_add_feature(&quot;list&quot;);<br>
+       pr_msg(<br>
 &quot;\n&quot;<br>
 &quot;* Logging:\n&quot;<br>
 &quot;  -o|--log-file FILE    log file name\n&quot;<br>
</span>@@ -837,7 +864,6 @@ usage:<br>
<span class=""> &quot;Other options:\n&quot;<br>
 &quot;  -h|--help             show this text\n&quot;<br>
 &quot;  -V|--version          show version\n&quot;<br>
-&quot;     --ms               don&#39;t check not yet merged kernel features\n&quot;<br>
        );<br>
<br>
        return 0;<br>
diff --git a/criu/include/cr_options.h b/criu/include/cr_options.h<br>
</span>index a6f0b3e..5e1cc21 100644<br>
--- a/criu/include/cr_options.h<br>
+++ b/criu/include/cr_options.h<br>
@@ -56,7 +56,8 @@ struct cr_options {<br>
<span class="">        int                     final_state;<br>
        char                    *show_dump_file;<br>
        char                    *show_fmt;<br>
-       bool                    check_ms_kernel;<br>
</span>+       bool                    check_extra_features;<br>
+       bool                    check_experimental_features;<br>
<span class="">        bool                    show_pages_content;<br>
        union {<br>
                bool            restore_detach;<br>
</span>diff --git a/criu/timerfd.c b/criu/timerfd.c<br>
index 942e5e6..13a6570 100644<br>
--- a/criu/timerfd.c<br>
+++ b/criu/timerfd.c<br>
@@ -52,7 +52,7 @@ int check_timerfd(void)<br>
                ret = ioctl(fd, TFD_IOC_SET_TICKS, NULL);<br>
                if (ret &lt; 0) {<br>
                        if (errno != EFAULT)<br>
-                               pr_perror(&quot;No timerfd support for c/r&quot;);<br>
+                               pr_warn(&quot;No timerfd support for c/r&quot;);<br>
                        else<br>
                                ret = 0;<br>
                }<br>
<span class="HOEnZb"><font color="#888888">--<br>
2.7.0.rc3.207.g0ac5344<br>
<br>
</font></span></blockquote></div><br></div>