Skip to content

Commit

Permalink
kerndat: check the PAGEMAP_SCAN ioctl
Browse files Browse the repository at this point in the history
PAGEMAP_SCAN is a new ioctl that allows to get page attributes in a more
effeciant way than reading pagemap files.

Signed-off-by: Andrei Vagin <[email protected]>
  • Loading branch information
avagin committed Oct 25, 2023
1 parent c474816 commit e635fe1
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 0 deletions.
10 changes: 10 additions & 0 deletions criu/cr-check.c
Original file line number Diff line number Diff line change
Expand Up @@ -1382,6 +1382,14 @@ static int check_ipv6_freebind(void)
return 0;
}

static int check_pagemap_scan(void)
{
if (!kdat.has_pagemap_scan)
return -1;

return 0;
}

static int (*chk_feature)(void);

/*
Expand Down Expand Up @@ -1502,6 +1510,7 @@ int cr_check(void)
ret |= check_openat2();
ret |= check_ptrace_get_rseq_conf();
ret |= check_ipv6_freebind();
ret |= check_pagemap_scan();

if (kdat.lsm == LSMTYPE__APPARMOR)
ret |= check_apparmor_stacking();
Expand Down Expand Up @@ -1623,6 +1632,7 @@ static struct feature_list feature_list[] = {
{ "openat2", check_openat2 },
{ "get_rseq_conf", check_ptrace_get_rseq_conf },
{ "ipv6_freebind", check_ipv6_freebind },
{ "pagemap_scan", check_pagemap_scan},
{ NULL, NULL },
};

Expand Down
1 change: 1 addition & 0 deletions criu/include/kerndat.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ struct kerndat_s {
struct __ptrace_rseq_configuration libc_rseq_conf;
bool has_ipv6_freebind;
bool has_membarrier_get_registrations;
bool has_pagemap_scan;
};

extern struct kerndat_s kdat;
Expand Down
65 changes: 65 additions & 0 deletions criu/include/pagemap_scan.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#ifndef __CR_PAGEMAP_SCAN_H__
#define __CR_PAGEMAP_SCAN_H__

#include <sys/types.h>
#include "int.h"

/* Bitmasks provided in pm_scan_args masks and reported in page_region.categories. */
#define PAGE_IS_WPALLOWED (1 << 0)
#define PAGE_IS_WRITTEN (1 << 1)
#define PAGE_IS_FILE (1 << 2)
#define PAGE_IS_PRESENT (1 << 3)
#define PAGE_IS_SWAPPED (1 << 4)
#define PAGE_IS_PFNZERO (1 << 5)
#define PAGE_IS_HUGE (1 << 6)

/*
* struct page_region - Page region with flags
* @start: Start of the region
* @end: End of the region (exclusive)
* @categories: PAGE_IS_* category bitmask for the region
*/
struct page_region {
u64 start;
u64 end;
u64 categories;
};

#define PAGEMAP_SCAN _IOWR('f', 16, struct pm_scan_arg)

/* Flags for PAGEMAP_SCAN ioctl */
#define PM_SCAN_WP_MATCHING (1 << 0) /* Write protect the pages matched. */
#define PM_SCAN_CHECK_WPASYNC (1 << 1) /* Abort the scan when a non-WP-enabled page is found. */

/*
* struct pm_scan_arg - Pagemap ioctl argument
* @size: Size of the structure
* @flags: Flags for the IOCTL
* @start: Starting address of the region
* @end: Ending address of the region
* @walk_end Address where the scan stopped (written by kernel).
* walk_end == end (address tags cleared) informs that the scan completed on entire range.
* @vec: Address of page_region struct array for output
* @vec_len: Length of the page_region struct array
* @max_pages: Optional limit for number of returned pages (0 = disabled)
* @category_inverted: PAGE_IS_* categories which values match if 0 instead of 1
* @category_mask: Skip pages for which any category doesn't match
* @category_anyof_mask: Skip pages for which no category matches
* @return_mask: PAGE_IS_* categories that are to be reported in `page_region`s returned
*/
struct pm_scan_arg {
u64 size;
u64 flags;
u64 start;
u64 end;
u64 walk_end;
u64 vec;
u64 vec_len;
u64 max_pages;
u64 category_inverted;
u64 category_mask;
u64 category_anyof_mask;
u64 return_mask;
};

#endif /* __CR_PAGEMAP_SCAN_H__ */
19 changes: 19 additions & 0 deletions criu/kerndat.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#include "memfd.h"
#include "mount-v2.h"
#include "util-caps.h"
#include "pagemap_scan.h"

struct kerndat_s kdat = {};
volatile int dummy_var;
Expand All @@ -74,6 +75,24 @@ static int check_pagemap(void)
return -1;
}

if (ioctl(fd, PAGEMAP_SCAN, NULL) == 0) {
pr_err("PAGEMAP_SCAN succeeded unexpectedly\n");
return -1;
} else {
switch (errno) {
case EFAULT:
pr_debug("PAGEMAP_SCAN is supported\n");
kdat.has_pagemap_scan = true;
break;
case EINVAL:
pr_debug("PAGEMAP_SCAN isn't supported\n");
break;
default:
pr_perror("PAGEMAP_SCAN failed with unexpcted errno");
return -1;
}
}

retry = 3;
while (retry--) {
++dummy_var;
Expand Down

0 comments on commit e635fe1

Please sign in to comment.