• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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