//! Port of the example from the `userfaultfd` manpage. use libc::{self, c_void}; use nix::poll::{poll, PollFd, PollFlags}; use nix::sys::mman::{mmap, MapFlags, ProtFlags}; use nix::unistd::{sysconf, SysconfVar}; use std::{convert::TryInto, env}; use userfaultfd::{Event, Uffd, UffdBuilder}; fn fault_handler_thread(uffd: Uffd) { let page_size = sysconf(SysconfVar::PAGE_SIZE).unwrap().unwrap() as usize; // Create a page that will be copied into the faulting region let page = unsafe { mmap( None, page_size.try_into().unwrap(), ProtFlags::PROT_READ | ProtFlags::PROT_WRITE, MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS, None::, 0, ) .expect("mmap") }; // Loop, handling incoming events on the userfaultfd file descriptor let mut fault_cnt = 0; loop { // See what poll() tells us about the userfaultfd let pollfd = PollFd::new(&uffd, PollFlags::POLLIN); let nready = poll(&mut [pollfd], -1).expect("poll"); println!("\nfault_handler_thread():"); let revents = pollfd.revents().unwrap(); println!( " poll() returns: nready = {}; POLLIN = {}; POLLERR = {}", nready, revents.contains(PollFlags::POLLIN), revents.contains(PollFlags::POLLERR), ); // Read an event from the userfaultfd let event = uffd .read_event() .expect("read uffd_msg") .expect("uffd_msg ready"); // We expect only one kind of event; verify that assumption if let Event::Pagefault { addr, .. } = event { // Display info about the page-fault event println!(" UFFD_EVENT_PAGEFAULT event: {:?}", event); // Copy the page pointed to by 'page' into the faulting region. Vary the contents that are // copied in, so that it is more obvious that each fault is handled separately. for c in unsafe { std::slice::from_raw_parts_mut(page as *mut u8, page_size) } { *c = b'A' + fault_cnt % 20; } fault_cnt += 1; let dst = (addr as usize & !(page_size - 1)) as *mut c_void; let copy = unsafe { uffd.copy(page, dst, page_size, true).expect("uffd copy") }; println!(" (uffdio_copy.copy returned {})", copy); } else { panic!("Unexpected event on userfaultfd"); } } } fn main() { let num_pages = env::args() .nth(1) .expect("Usage: manpage ") .parse::() .unwrap(); let page_size = sysconf(SysconfVar::PAGE_SIZE).unwrap().unwrap() as usize; let len = num_pages * page_size; // Create and enable userfaultfd object let uffd = UffdBuilder::new() .close_on_exec(true) .non_blocking(true) .user_mode_only(true) .create() .expect("uffd creation"); // Create a private anonymous mapping. The memory will be demand-zero paged--that is, not yet // allocated. When we actually touch the memory, it will be allocated via the userfaultfd. let addr = unsafe { mmap( None, len.try_into().unwrap(), ProtFlags::PROT_READ | ProtFlags::PROT_WRITE, MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS, None::, 0, ) .expect("mmap") }; println!("Address returned by mmap() = {:p}", addr); // Register the memory range of the mapping we just created for handling by the userfaultfd // object. In mode, we request to track missing pages (i.e., pages that have not yet been // faulted in). uffd.register(addr, len).expect("uffd.register()"); // Create a thread that will process the userfaultfd events let _s = std::thread::spawn(move || fault_handler_thread(uffd)); // Main thread now touches memory in the mapping, touching locations 1024 bytes apart. This will // trigger userfaultfd events for all pages in the region. // Ensure that faulting address is not on a page boundary, in order to test that we correctly // handle that case in fault_handling_thread() let mut l = 0xf; while l < len { let ptr = (addr as usize + l) as *mut u8; let c = unsafe { *ptr }; println!("Read address {:p} in main(): {:?}", ptr, c as char); l += 1024; std::thread::sleep(std::time::Duration::from_micros(100000)); } }