PAGEMAP_SCAN(2const)
β¨ NAME β¨
PAGEMAP_SCAN β get and/or clear page flags π
So this is a 2const page, bestie β a little const-flavored slice of the ioctl(2) family, living its best life under <pink>PAGEMAP_SCAN(2const)</pink>. It's the one that reads page flags and optionally wipes some of them clean. π§Ή
Also, library check: it's the Standard C library (libc, -lc), don't forget to link it. π
π LIBRARY π
Standard C library (libc, -lc)
π¦ SYNOPSIS π¦
#include <linux/fs.h> /* Definition of PAGE* and PM_* constants */
#include <sys/ioctl.h>
int ioctl(int pagemap_fd, PAGEMAP_SCAN, struct pm_scan_arg *arg);
#include <linux/fs.h>
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;
};
struct page_region {
__u64 start;
__u64 end;
__u64 categories;
};
Like. That's the whole vibe. Two headers, one ioctl call, two structs. Clean. Iconic. π
π DESCRIPTION π
Okay so this ioctl(2) is the one that gets and optionally clears some very specific flags from page table entries. And when it reports back? It does it with PAGE_SIZE granularity, no in-between, periodt. β¨
Now here's the setup part that lowkey trips people up β listen:
To start tracking the written state (that's the flag) of a page or a whole range of memory, you need UFFD_FEATURE_WP_ASYNC to be enabled by the UFFDIO_API ioctl(2) on userfaultfd, and the memory range has to be registered with the UFFDIO_REGISTER ioctl(2) in UFFDIO_REGISTER_MODE_WP mode.
Both of those. Not one. Both. π
β Supported page flags β
These are the page table entry flags that get support:
PAGE_IS_WPALLOWEDβ the page has asynchronous write-protection enabled. It's allowed to be write-protected, basically. π‘οΈPAGE_IS_WRITTENβ the page has been written to from the time it was write protected. Somebody touched it. πPAGE_IS_FILEβ the page is file backed. πPAGE_IS_PRESENTβ the page is present in the memory. She's here. πPAGE_IS_SWAPPEDβ the page is swapped. πPAGE_IS_PFNZEROβ the page has zero PFN. π«§PAGE_IS_HUGEβ the page is THP or Hugetlb backed. Big girl energy. π
π₯ Supported operations π₯
The get operation is always performed if the output buffer is specified β it doesn't ask, it just does it. π
The other operations are as follows:
PM_SCAN_WP_MATCHINGβ write protect the matched pages. βοΈPM_SCAN_CHECK_WPASYNCβ abort the scan when a page is found which doesn't have the Userfaultfd Asynchronous Write protection enabled. π¨ Like, she will stop the whole thing. Dramatic but valid.
π The struct pm_scan_arg argument π
Field-by-field, let's go:
sizeβ this field should be set to the size of the structure in bytes, as insizeof(struct pm_scan_arg). Measure yourself before you go out, bestie. πflagsβ the operations to be performed are specified in it. π©startβ the starting address of the scan is specified in it. πendβ the ending address of the scan is specified in it. πwalk_endβ the kernel returns the scan's ending address in it. And awalk_endequal toendmeans that the scan has completed on the entire range. If they match, you're done, slay. βvecβ the address of thepage_regionarray for output. ποΈvec_lenβ the length of thepage_regionstruct array. πmax_pagesβ it is the optional limit for the number of output pages required. Capped. π§’category_invertedβPAGE_IS_*categories which values match if0instead of1. Contrarian queen. πcategory_maskβ skip pages for which anyPAGE_IS_*category doesn't match. βοΈcategory_anyof_maskβ skip pages for which noPAGE_IS_*category matches. βοΈreturn_maskβPAGE_IS_*categories that are to be reported inpage_region. π’
π RETURN VALUE π
On error, -1 is returned, and errno is set to indicate the error. That's it. That's the whole scene. π
π¨ ERRORS π¨
Error codes can be one of, but are not limited to, the following β so don't think this list is the entire lore, iykyk:
EINVALβ invalid arguments i.e., invalid size of the argument, invalid flags, invalid categories, thestartaddress isn't aligned withPAGE_SIZE, orvec_lenis specified whenvecisNULL. So many ways to be wrong bestie, align your addresses. π«EFAULTβ invalidargpointer, invalidvecpointer, or invalid address range specified bystartandend. π§ENOMEMβ no memory is available. πΈEINTRβ fatal signal is pending. β‘
Fr fr, EINVAL is doing a lot of heavy lifting here. Read your errors, don't guess. π
π STANDARDS π
Linux.
πΈ HISTORY πΈ
Linux 6.7. Newer than you think. π±
π¦ SEE ALSO π¦
π Okay that's the whole page, bestie. Go forth and scan those page maps. This little love letter is from Linux man-pages 6.20, dated 2026-05-16 β shoutout to the PAGEMAP_SCAN(2const) girlies, you ate and left no crumbs πβ¨π«Ά
π¦ where this page comes from
- arch rolling: man-pages 6.20-1 Β· BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
- nixpkgs unstable: man-pages 6.20
- π the page's license: Linux-man-pages-copyleft
- Copyright 2023, Collabora
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms