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