Commit ce5e1916 authored by Mike Rapoport's avatar Mike Rapoport Committed by Andrei Vagin

Make userfaultfd detection a part of kerndat

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: 's avatarMike Rapoport <rppt@linux.vnet.ibm.com>
Signed-off-by: 's avatarPavel Emelyanov <xemul@virtuozzo.com>
parent 8dcb4e07
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
#include "libnetlink.h" #include "libnetlink.h"
#include "net.h" #include "net.h"
#include "restorer.h" #include "restorer.h"
#include "linux/userfaultfd.h"
static char *feature_name(int (*func)()); static char *feature_name(int (*func)());
...@@ -1045,6 +1046,23 @@ static int check_compat_cr(void) ...@@ -1045,6 +1046,23 @@ static int check_compat_cr(void)
return -1; return -1;
} }
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); static int (*chk_feature)(void);
/* /*
...@@ -1156,6 +1174,7 @@ int cr_check(void) ...@@ -1156,6 +1174,7 @@ int cr_check(void)
if (opts.check_experimental_features) { if (opts.check_experimental_features) {
ret |= check_autofs(); ret |= check_autofs();
ret |= check_compat_cr(); ret |= check_compat_cr();
ret |= check_uffd();
} }
print_on_level(DEFAULT_LOGLEVEL, "%s\n", ret ? CHECK_MAYBE : CHECK_GOOD); print_on_level(DEFAULT_LOGLEVEL, "%s\n", ret ? CHECK_MAYBE : CHECK_GOOD);
...@@ -1197,6 +1216,7 @@ static struct feature_list feature_list[] = { ...@@ -1197,6 +1216,7 @@ static struct feature_list feature_list[] = {
{ "autofs", check_autofs }, { "autofs", check_autofs },
{ "tcp_half_closed", check_tcp_halt_closed }, { "tcp_half_closed", check_tcp_halt_closed },
{ "compat_cr", check_compat_cr }, { "compat_cr", check_compat_cr },
{ "lazy_pages", check_uffd },
{ NULL, NULL }, { NULL, NULL },
}; };
......
...@@ -49,6 +49,7 @@ struct kerndat_s { ...@@ -49,6 +49,7 @@ struct kerndat_s {
bool has_tcp_half_closed; bool has_tcp_half_closed;
bool stack_guard_gap_hidden; bool stack_guard_gap_hidden;
int lsm; int lsm;
unsigned long uffd_features;
}; };
extern struct kerndat_s kdat; extern struct kerndat_s kdat;
...@@ -70,5 +71,6 @@ enum { ...@@ -70,5 +71,6 @@ enum {
extern int kerndat_fs_virtualized(unsigned int which, u32 kdev); extern int kerndat_fs_virtualized(unsigned int which, u32 kdev);
extern int kerndat_tcp_repair(); extern int kerndat_tcp_repair();
extern int kerndat_uffd(bool need_uffd);
#endif /* __CR_KERNDAT_H__ */ #endif /* __CR_KERNDAT_H__ */
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <compel/plugins/std/syscall-codes.h> #include <compel/plugins/std/syscall-codes.h>
#include <compel/compel.h> #include <compel/compel.h>
#include "netfilter.h" #include "netfilter.h"
#include "linux/userfaultfd.h"
struct kerndat_s kdat = { struct kerndat_s kdat = {
}; };
...@@ -714,6 +715,44 @@ unl: ...@@ -714,6 +715,44 @@ unl:
} }
} }
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 kerndat_init(void)
{ {
int ret; int ret;
...@@ -752,6 +791,8 @@ int kerndat_init(void) ...@@ -752,6 +791,8 @@ int kerndat_init(void)
ret = kerndat_has_memfd_create(); ret = kerndat_has_memfd_create();
if (!ret) if (!ret)
ret = kerndat_detect_stack_guard_gap(); ret = kerndat_detect_stack_guard_gap();
if (!ret)
ret = kerndat_uffd(opts.lazy_pages);
kerndat_lsm(); kerndat_lsm();
kerndat_mmap_min_addr(); kerndat_mmap_min_addr();
......
...@@ -166,27 +166,6 @@ out: ...@@ -166,27 +166,6 @@ out:
return ret; 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) int lazy_pages_setup_zombie(int pid)
{ {
if (!opts.lazy_pages) if (!opts.lazy_pages)
...@@ -208,8 +187,6 @@ int setup_uffd(int pid, struct task_restore_args *task_args) ...@@ -208,8 +187,6 @@ int setup_uffd(int pid, struct task_restore_args *task_args)
return 0; return 0;
} }
if (check_for_uffd())
return -1;
/* /*
* 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.
...@@ -812,7 +789,7 @@ int cr_lazy_pages(bool daemon) ...@@ -812,7 +789,7 @@ int cr_lazy_pages(bool daemon)
int lazy_sk; int lazy_sk;
int ret; int ret;
if (check_for_uffd()) if (kerndat_uffd(true))
return -1; return -1;
if (prepare_dummy_pstree()) if (prepare_dummy_pstree())
......
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