[CRIU] [PATCH 2/2] Make userfaultfd detection a part of kerndat

Mike Rapoport rppt at linux.vnet.ibm.com
Thu Dec 8 05:10:12 PST 2016


Instead of checking for availability of userfaultfd late during the restore
process, make the detection of supported userfaultfd functionality part of
kerndat. As a bonus, I've extended criu check with ability to verify
presence of userfaultfd.

Signed-off-by: Mike Rapoport <rppt at linux.vnet.ibm.com>
---
 criu/cr-check.c        | 20 ++++++++++++++++++++
 criu/include/kerndat.h |  2 ++
 criu/kerndat.c         | 41 +++++++++++++++++++++++++++++++++++++++++
 criu/uffd.c            | 25 +------------------------
 4 files changed, 64 insertions(+), 24 deletions(-)

diff --git a/criu/cr-check.c b/criu/cr-check.c
index fa5c2eb..60279e1 100644
--- a/criu/cr-check.c
+++ b/criu/cr-check.c
@@ -47,6 +47,7 @@
 #include "cr_options.h"
 #include "libnetlink.h"
 #include "net.h"
+#include "linux/userfaultfd.h"
 
 static char *feature_name(int (*func)());
 
@@ -953,6 +954,23 @@ static int check_tcp_window(void)
 	return 0;
 }
 
+static int check_uffd(void)
+{
+	unsigned long features = UFFD_FEATURE_EVENT_FORK |
+		UFFD_FEATURE_EVENT_REMAP |
+		UFFD_FEATURE_EVENT_MADVDONTNEED;
+
+	if (kerndat_uffd(true))
+		return -1;
+
+	if ((kdat.uffd_features & features) != features) {
+		pr_err("Userfaultfd missing essential features\n");
+		return -1;
+	}
+
+	return 0;
+}
+
 static int (*chk_feature)(void);
 
 /*
@@ -1061,6 +1079,7 @@ int cr_check(void)
 	 */
 	if (opts.check_experimental_features) {
 		ret |= check_autofs();
+		ret |= check_uffd();
 	}
 
 	print_on_level(DEFAULT_LOGLEVEL, "%s\n", ret ? CHECK_MAYBE : CHECK_GOOD);
@@ -1134,6 +1153,7 @@ static struct feature_list feature_list[] = {
 	{ "cgroupns", check_cgroupns },
 	{ "autofs", check_autofs },
 	{ "tcp_half_closed", check_tcp_halt_closed },
+	{ "lazy_pages", check_uffd },
 	{ NULL, NULL },
 };
 
diff --git a/criu/include/kerndat.h b/criu/include/kerndat.h
index 7021c18..ab6ef3f 100644
--- a/criu/include/kerndat.h
+++ b/criu/include/kerndat.h
@@ -39,6 +39,7 @@ struct kerndat_s {
 	enum pagemap_func pmap;
 	unsigned int has_xtlocks;
 	bool has_tcp_half_closed;
+	unsigned long uffd_features;
 };
 
 extern struct kerndat_s kdat;
@@ -61,5 +62,6 @@ extern int kerndat_fs_virtualized(unsigned int which, u32 kdev);
 
 extern int kerndat_tcp_repair_window();
 extern int kerndat_tcp_repair();
+extern int kerndat_uffd(bool need_uffd);
 
 #endif /* __CR_KERNDAT_H__ */
diff --git a/criu/kerndat.c b/criu/kerndat.c
index c30e18e..82599ec 100644
--- a/criu/kerndat.c
+++ b/criu/kerndat.c
@@ -26,6 +26,7 @@
 #include "sk-inet.h"
 #include <compel/plugins/std/syscall-codes.h>
 #include <compel/compel.h>
+#include "linux/userfaultfd.h"
 
 struct kerndat_s kdat = {
 };
@@ -532,6 +533,44 @@ static int kerndat_compat_restore(void)
 	return 0;
 }
 
+int kerndat_uffd(bool need_uffd)
+{
+	struct uffdio_api uffdio_api;
+	int uffd;
+
+	uffd = syscall(SYS_userfaultfd, 0);
+
+	/*
+	 * uffd == -1 is probably enough to not use lazy-restore
+	 * on this system. Additionally checking for ENOSYS
+	 * makes sure it is actually not implemented.
+	 */
+	if (uffd == -1 && errno == ENOSYS) {
+		if (!need_uffd)
+			return 0;
+
+		pr_err("Lazy pages are not available\n");
+		return -1;
+	}
+
+	uffdio_api.api = UFFD_API;
+	uffdio_api.features = 0;
+	if (ioctl(uffd, UFFDIO_API, &uffdio_api)) {
+		pr_perror("Failed to get uffd API");
+		return -1;
+	}
+	if (uffdio_api.api != UFFD_API) {
+		pr_err("Incompatible uffd API: expected %Lu, got %Lu\n",
+		       UFFD_API, uffdio_api.api);
+		return -1;
+	}
+
+	kdat.uffd_features = uffdio_api.features;
+
+	close(uffd);
+	return 0;
+}
+
 int kerndat_init(void)
 {
 	int ret;
@@ -596,6 +635,8 @@ int kerndat_init_rst(void)
 		ret = kerndat_compat_restore();
 	if (!ret)
 		ret = kerndat_tcp_repair();
+	if (!ret)
+		ret = kerndat_uffd(opts.lazy_pages);
 
 	kerndat_lsm();
 
diff --git a/criu/uffd.c b/criu/uffd.c
index d71a83a..0575d3b 100644
--- a/criu/uffd.c
+++ b/criu/uffd.c
@@ -166,27 +166,6 @@ out:
 	return ret;
 }
 
-/* Runtime detection if userfaultfd can be used */
-
-static int check_for_uffd()
-{
-	int uffd;
-
-	uffd = syscall(SYS_userfaultfd, 0);
-	/*
-	 * uffd == -1 is probably enough to not use lazy-restore
-	 * on this system. Additionally checking for ENOSYS
-	 * makes sure it is actually not implemented.
-	 */
-	if ((uffd == -1) && (errno == ENOSYS)) {
-		pr_err("Runtime detection of userfaultfd failed on this system.\n");
-		pr_err("Processes cannot be lazy-restored on this system.\n");
-		return -1;
-	}
-	close(uffd);
-	return 0;
-}
-
 int lazy_pages_setup_zombie(int pid)
 {
 	if (!opts.lazy_pages)
@@ -208,8 +187,6 @@ int setup_uffd(int pid, struct task_restore_args *task_args)
 		return 0;
 	}
 
-	if (check_for_uffd())
-		return -1;
 	/*
 	 * Open userfaulfd FD which is passed to the restorer blob and
 	 * to a second process handling the userfaultfd page faults.
@@ -812,7 +789,7 @@ int cr_lazy_pages(bool daemon)
 	int lazy_sk;
 	int ret;
 
-	if (check_for_uffd())
+	if (kerndat_uffd(true))
 		return -1;
 
 	if (prepare_dummy_pstree())
-- 
1.9.1



More information about the CRIU mailing list