diff --git a/criu/include/uffd.h b/criu/include/uffd.h index 4d790cebf..e2b9be693 100644 --- a/criu/include/uffd.h +++ b/criu/include/uffd.h @@ -2,6 +2,8 @@ #define __CR_UFFD_H_ struct task_restore_args; + +extern int uffd_open(int flags, unsigned long *features); extern int setup_uffd(int pid, struct task_restore_args *task_args); extern int lazy_pages_setup_zombie(int pid); extern int prepare_lazy_pages_socket(void); diff --git a/criu/kerndat.c b/criu/kerndat.c index 156b5b630..62b62613d 100644 --- a/criu/kerndat.c +++ b/criu/kerndat.c @@ -33,6 +33,7 @@ #include "netfilter.h" #include "linux/userfaultfd.h" #include "prctl.h" +#include "uffd.h" struct kerndat_s kdat = { }; @@ -719,18 +720,18 @@ unl: int kerndat_uffd(void) { - struct uffdio_api uffdio_api; int uffd; - uffd = syscall(SYS_userfaultfd, 0); + uffd = uffd_open(0, &kdat.uffd_features); /* - * uffd == -1 is probably enough to not use lazy-restore - * on this system. Additionally checking for ENOSYS - * makes sure it is actually not implemented. + * uffd == -ENOSYS means userfaultfd is not supported on this + * system and we just happily return with kdat.has_uffd = false. + * Error other than -ENOSYS would mean "Houston, Houston, we + * have a problem!" */ - if (uffd == -1) { - if (errno == ENOSYS) + if (uffd < 0) { + if (uffd == -ENOSYS) return 0; pr_err("Lazy pages are not available\n"); @@ -739,21 +740,12 @@ int kerndat_uffd(void) kdat.has_uffd = true; - 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; - + /* + * we have to close the uffd and reopen in later in restorer + * to enable non-cooperative features + */ close(uffd); + return 0; } diff --git a/criu/uffd.c b/criu/uffd.c index c5ad78503..d3142ae8b 100644 --- a/criu/uffd.c +++ b/criu/uffd.c @@ -211,10 +211,46 @@ int lazy_pages_setup_zombie(int pid) return 0; } +int uffd_open(int flags, unsigned long *features) +{ + struct uffdio_api uffdio_api = { 0 }; + int uffd; + + uffd = syscall(SYS_userfaultfd, flags); + if (uffd == -1) { + pr_perror("Lazy pages are not available"); + return -errno; + } + + uffdio_api.api = UFFD_API; + if (features) + uffdio_api.features = *features; + + if (ioctl(uffd, UFFDIO_API, &uffdio_api)) { + pr_perror("Failed to get uffd API"); + goto err; + } + + if (uffdio_api.api != UFFD_API) { + pr_err("Incompatible uffd API: expected %Lu, got %Lu\n", + UFFD_API, uffdio_api.api); + goto err; + } + + if (features) + *features = uffdio_api.features; + + return uffd; + +err: + close(uffd); + return -1; +} + /* This function is used by 'criu restore --lazy-pages' */ int setup_uffd(int pid, struct task_restore_args *task_args) { - struct uffdio_api uffdio_api; + unsigned long features = kdat.uffd_features & NEED_UFFD_API_FEATURES; if (!opts.lazy_pages) { task_args->uffd = -1; @@ -225,26 +261,12 @@ int setup_uffd(int pid, struct task_restore_args *task_args) * Open userfaulfd FD which is passed to the restorer blob and * to a second process handling the userfaultfd page faults. */ - task_args->uffd = syscall(SYS_userfaultfd, O_CLOEXEC | O_NONBLOCK); + task_args->uffd = uffd_open(O_CLOEXEC | O_NONBLOCK, &features); if (task_args->uffd < 0) { pr_perror("Unable to open an userfaultfd descriptor"); return -1; } - /* - * Check if the UFFD_API is the one which is expected - */ - uffdio_api.api = UFFD_API; - uffdio_api.features = kdat.uffd_features & NEED_UFFD_API_FEATURES; - if (ioctl(task_args->uffd, UFFDIO_API, &uffdio_api)) { - pr_err("Checking for UFFDIO_API failed.\n"); - goto err; - } - if (uffdio_api.api != UFFD_API) { - pr_err("Result of looking up UFFDIO_API does not match: %Lu\n", uffdio_api.api); - goto err; - } - if (send_uffd(task_args->uffd, pid) < 0) goto err;