Skip to content

Commit

Permalink
kerndat: add test for availability of PR_SET_THP_DISABLE prctl
Browse files Browse the repository at this point in the history
The PR_SET_THP_DISABLE prctl allows control of transparent huge pages on
per-process basis. It is available since Linux 3.15, but until recently it
set VM_NOHUGEPAGE for all VMAs created after prctl() call, which prevents
proper restore for combination of pre- and post-copy. A recent change to
prctl(PR_SET_THP_DISABLE) behavior eliminates the use of per-VMA flags and
we can use the new version of the prctl() to disable THP.

Acked-by: Pavel Emelyanov <[email protected]>
Signed-off-by: Mike Rapoport <[email protected]>
Signed-off-by: Andrei Vagin <[email protected]>
  • Loading branch information
rppt authored and avagin committed Sep 16, 2017
1 parent 9f61de8 commit 5480a2a
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 0 deletions.
1 change: 1 addition & 0 deletions criu/include/kerndat.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ struct kerndat_s {
int lsm;
bool has_uffd;
unsigned long uffd_features;
bool has_thp_disable;
};

extern struct kerndat_s kdat;
Expand Down
8 changes: 8 additions & 0 deletions criu/include/prctl.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,12 @@ struct prctl_mm_map {
# define PR_GET_TID_ADDRESS 40
#endif

#ifndef PR_SET_THP_DISABLE
# define PR_SET_THP_DISABLE 41
#endif

#ifndef PR_GET_THP_DISABLE
# define PR_GET_THP_DISABLE 42
#endif

#endif /* __CR_PRCTL_H__ */
73 changes: 73 additions & 0 deletions criu/kerndat.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <stdint.h>
#include <sys/socket.h>
#include <arpa/inet.h> /* for sockaddr_in and inet_ntoa() */
#include <sys/prctl.h>

#include "int.h"
#include "log.h"
Expand All @@ -31,6 +32,7 @@
#include <compel/compel.h>
#include "netfilter.h"
#include "linux/userfaultfd.h"
#include "prctl.h"

struct kerndat_s kdat = {
};
Expand Down Expand Up @@ -755,6 +757,75 @@ int kerndat_uffd(void)
return 0;
}

int kerndat_has_thp_disable(void)
{
struct bfd f;
void *addr;
char *str;
int ret = -1;
bool vma_match = false;

if (prctl(PR_SET_THP_DISABLE, 1, 0, 0, 0)) {
if (errno != EINVAL)
return -1;
pr_info("PR_SET_THP_DISABLE is not available\n");
return 0;
}

addr = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
if (addr == MAP_FAILED) {
pr_perror("Can't mmap memory for THP disable test");
return -1;
}

if (prctl(PR_SET_THP_DISABLE, 0, 0, 0, 0))
return -1;

f.fd = open("/proc/self/smaps", O_RDONLY);
if (f.fd < 0) {
pr_perror("Can't open /proc/self/smaps");
goto out;
}
if (bfdopenr(&f))
goto out;

while ((str = breadline(&f)) != NULL) {
if (IS_ERR(str))
goto out;

if (is_vma_range_fmt(str)) {
unsigned long vma_addr;

if (sscanf(str, "%lx-", &vma_addr) != 1) {
pr_err("Can't parse: %s\n", str);
goto out;
}

if (vma_addr == (unsigned long)addr)
vma_match = true;
}

if (vma_match && !strncmp(str, "VmFlags: ", 9)) {
u32 flags = 0;
u64 madv = 0;
int io_pf = 0;

parse_vmflags(str, &flags, &madv, &io_pf);
kdat.has_thp_disable = !(madv & (1 << MADV_NOHUGEPAGE));
break;
}
}

ret = 0;

out:
bclose(&f);
munmap(addr, PAGE_SIZE);

return ret;
}

int kerndat_init(void)
{
int ret;
Expand Down Expand Up @@ -795,6 +866,8 @@ int kerndat_init(void)
ret = kerndat_detect_stack_guard_gap();
if (!ret)
ret = kerndat_uffd();
if (!ret)
ret = kerndat_has_thp_disable();

kerndat_lsm();
kerndat_mmap_min_addr();
Expand Down

0 comments on commit 5480a2a

Please sign in to comment.