swap: wake the faulting process on conflict
userfaultfd does not wake the thread if zero operation fails with
EEXIST.
minor change: An unexpected page fault event is critical error because
it blocks the faulting thread forever.
BUG=b:215093219
TEST=none
Change-Id: I5326878018f825024c7a5ceaf8daec5792547cb6
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/4125076
Commit-Queue: Shin Kawamura <kawasin@google.com>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Reviewed-by: David Stevens <stevensd@chromium.org>
diff --git a/swap/src/lib.rs b/swap/src/lib.rs
index c9bbdc4..388f0c3 100644
--- a/swap/src/lib.rs
+++ b/swap/src/lib.rs
@@ -448,7 +448,7 @@
.handle_page_fault(uffd, addr as usize)
.context("handle fault")?;
} else {
- warn!("page fault event while handler is none");
+ bail!("page fault event while handler is none");
}
}
UffdEvent::Fork { uffd } => {
diff --git a/swap/src/page_handler.rs b/swap/src/page_handler.rs
index 77338b2..4d65a4d 100644
--- a/swap/src/page_handler.rs
+++ b/swap/src/page_handler.rs
@@ -245,6 +245,7 @@
Err(UffdError::ZeropageFailed(errno)) if errno as i32 == libc::EEXIST => {
// zeroing fails with EEXIST if the page is already filled. This case can
// happen if page faults on the same page happen on different processes.
+ uffd.wake(page_addr, page_size)?;
Ok(())
}
Err(e) => Err(e.into()),
diff --git a/swap/src/userfaultfd.rs b/swap/src/userfaultfd.rs
index dd627f7..410bc26 100644
--- a/swap/src/userfaultfd.rs
+++ b/swap/src/userfaultfd.rs
@@ -129,6 +129,18 @@
}
}
+ /// Wake the faulting thread blocked by the page(s).
+ ///
+ /// If the page is not initialized, the thread causes a page fault again.
+ ///
+ /// # Arguments
+ ///
+ /// * `addr` - the starting address of the page(s).
+ /// * `len` - the length in bytes of the page(s).
+ pub fn wake(&self, addr: usize, len: usize) -> Result<()> {
+ self.uffd.wake(addr as *mut libc::c_void, len)
+ }
+
/// Read an event from the userfaultfd.
///
/// Return `None` immediately if no events is ready to read.