Commit 830dd301 authored by Mike Rapoport's avatar Mike Rapoport Committed by Andrei Vagin

lazy-pages: introduce uffd_open

kdat and lazy-pages use nearly the same sequence to open userfault. This
code can definitely live in a dedicated function.
Signed-off-by: 's avatarMike Rapoport <rppt@linux.vnet.ibm.com>
Signed-off-by: 's avatarPavel Emelyanov <xemul@virtuozzo.com>
parent 1f2cc31f
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
#define __CR_UFFD_H_ #define __CR_UFFD_H_
struct task_restore_args; 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 setup_uffd(int pid, struct task_restore_args *task_args);
extern int lazy_pages_setup_zombie(int pid); extern int lazy_pages_setup_zombie(int pid);
extern int prepare_lazy_pages_socket(void); extern int prepare_lazy_pages_socket(void);
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "netfilter.h" #include "netfilter.h"
#include "linux/userfaultfd.h" #include "linux/userfaultfd.h"
#include "prctl.h" #include "prctl.h"
#include "uffd.h"
struct kerndat_s kdat = { struct kerndat_s kdat = {
}; };
...@@ -719,18 +720,18 @@ unl: ...@@ -719,18 +720,18 @@ unl:
int kerndat_uffd(void) int kerndat_uffd(void)
{ {
struct uffdio_api uffdio_api;
int uffd; int uffd;
uffd = syscall(SYS_userfaultfd, 0); uffd = uffd_open(0, &kdat.uffd_features);
/* /*
* uffd == -1 is probably enough to not use lazy-restore * uffd == -ENOSYS means userfaultfd is not supported on this
* on this system. Additionally checking for ENOSYS * system and we just happily return with kdat.has_uffd = false.
* makes sure it is actually not implemented. * Error other than -ENOSYS would mean "Houston, Houston, we
* have a problem!"
*/ */
if (uffd == -1) { if (uffd < 0) {
if (errno == ENOSYS) if (uffd == -ENOSYS)
return 0; return 0;
pr_err("Lazy pages are not available\n"); pr_err("Lazy pages are not available\n");
...@@ -739,21 +740,12 @@ int kerndat_uffd(void) ...@@ -739,21 +740,12 @@ int kerndat_uffd(void)
kdat.has_uffd = true; kdat.has_uffd = true;
uffdio_api.api = UFFD_API; /*
uffdio_api.features = 0; * we have to close the uffd and reopen in later in restorer
if (ioctl(uffd, UFFDIO_API, &uffdio_api)) { * to enable non-cooperative features
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); close(uffd);
return 0; return 0;
} }
......
...@@ -211,10 +211,46 @@ int lazy_pages_setup_zombie(int pid) ...@@ -211,10 +211,46 @@ int lazy_pages_setup_zombie(int pid)
return 0; 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' */ /* This function is used by 'criu restore --lazy-pages' */
int setup_uffd(int pid, struct task_restore_args *task_args) 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) { if (!opts.lazy_pages) {
task_args->uffd = -1; task_args->uffd = -1;
...@@ -225,26 +261,12 @@ int setup_uffd(int pid, struct task_restore_args *task_args) ...@@ -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 * Open userfaulfd FD which is passed to the restorer blob and
* to a second process handling the userfaultfd page faults. * 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) { if (task_args->uffd < 0) {
pr_perror("Unable to open an userfaultfd descriptor"); pr_perror("Unable to open an userfaultfd descriptor");
return -1; 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) if (send_uffd(task_args->uffd, pid) < 0)
goto err; goto err;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment