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