1 //! Port of the example from the `userfaultfd` manpage.
2 use libc::{self, c_void};
3 use nix::poll::{poll, PollFd, PollFlags};
4 use nix::sys::mman::{mmap, MapFlags, ProtFlags};
5 use nix::unistd::{sysconf, SysconfVar};
6 use std::{convert::TryInto, env};
7 use userfaultfd::{Event, Uffd, UffdBuilder};
8 
fault_handler_thread(uffd: Uffd)9 fn fault_handler_thread(uffd: Uffd) {
10     let page_size = sysconf(SysconfVar::PAGE_SIZE).unwrap().unwrap() as usize;
11 
12     // Create a page that will be copied into the faulting region
13 
14     let page = unsafe {
15         mmap(
16             None,
17             page_size.try_into().unwrap(),
18             ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
19             MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS,
20             None::<std::os::fd::BorrowedFd>,
21             0,
22         )
23         .expect("mmap")
24     };
25 
26     // Loop, handling incoming events on the userfaultfd file descriptor
27 
28     let mut fault_cnt = 0;
29     loop {
30         // See what poll() tells us about the userfaultfd
31 
32         let pollfd = PollFd::new(&uffd, PollFlags::POLLIN);
33         let nready = poll(&mut [pollfd], -1).expect("poll");
34 
35         println!("\nfault_handler_thread():");
36         let revents = pollfd.revents().unwrap();
37         println!(
38             "    poll() returns: nready = {}; POLLIN = {}; POLLERR = {}",
39             nready,
40             revents.contains(PollFlags::POLLIN),
41             revents.contains(PollFlags::POLLERR),
42         );
43 
44         // Read an event from the userfaultfd
45         let event = uffd
46             .read_event()
47             .expect("read uffd_msg")
48             .expect("uffd_msg ready");
49 
50         // We expect only one kind of event; verify that assumption
51 
52         if let Event::Pagefault { addr, .. } = event {
53             // Display info about the page-fault event
54 
55             println!("    UFFD_EVENT_PAGEFAULT event: {:?}", event);
56 
57             // Copy the page pointed to by 'page' into the faulting region. Vary the contents that are
58             // copied in, so that it is more obvious that each fault is handled separately.
59 
60             for c in unsafe { std::slice::from_raw_parts_mut(page as *mut u8, page_size) } {
61                 *c = b'A' + fault_cnt % 20;
62             }
63             fault_cnt += 1;
64 
65             let dst = (addr as usize & !(page_size - 1)) as *mut c_void;
66             let copy = unsafe { uffd.copy(page, dst, page_size, true).expect("uffd copy") };
67 
68             println!("        (uffdio_copy.copy returned {})", copy);
69         } else {
70             panic!("Unexpected event on userfaultfd");
71         }
72     }
73 }
74 
main()75 fn main() {
76     let num_pages = env::args()
77         .nth(1)
78         .expect("Usage: manpage <num_pages>")
79         .parse::<usize>()
80         .unwrap();
81 
82     let page_size = sysconf(SysconfVar::PAGE_SIZE).unwrap().unwrap() as usize;
83     let len = num_pages * page_size;
84 
85     // Create and enable userfaultfd object
86 
87     let uffd = UffdBuilder::new()
88         .close_on_exec(true)
89         .non_blocking(true)
90         .user_mode_only(true)
91         .create()
92         .expect("uffd creation");
93 
94     // Create a private anonymous mapping. The memory will be demand-zero paged--that is, not yet
95     // allocated. When we actually touch the memory, it will be allocated via the userfaultfd.
96 
97     let addr = unsafe {
98         mmap(
99             None,
100             len.try_into().unwrap(),
101             ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
102             MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS,
103             None::<std::os::fd::BorrowedFd>,
104             0,
105         )
106         .expect("mmap")
107     };
108 
109     println!("Address returned by mmap() = {:p}", addr);
110 
111     // Register the memory range of the mapping we just created for handling by the userfaultfd
112     // object. In mode, we request to track missing pages (i.e., pages that have not yet been
113     // faulted in).
114 
115     uffd.register(addr, len).expect("uffd.register()");
116 
117     // Create a thread that will process the userfaultfd events
118     let _s = std::thread::spawn(move || fault_handler_thread(uffd));
119 
120     // Main thread now touches memory in the mapping, touching locations 1024 bytes apart. This will
121     // trigger userfaultfd events for all pages in the region.
122 
123     // Ensure that faulting address is not on a page boundary, in order to test that we correctly
124     // handle that case in fault_handling_thread()
125     let mut l = 0xf;
126 
127     while l < len {
128         let ptr = (addr as usize + l) as *mut u8;
129         let c = unsafe { *ptr };
130         println!("Read address {:p} in main(): {:?}", ptr, c as char);
131         l += 1024;
132         std::thread::sleep(std::time::Duration::from_micros(100000));
133     }
134 }
135