Skip to content

Add support for UFFDIO_CONTINUE and UFFDIO_REGISTER_MODE_MINOR #72

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ nix = { version = "0.27", features = ["poll", "mman", "feature"] }
default = []
linux4_14 = ["userfaultfd-sys/linux4_14", "nix/process"]
linux5_7 = ["userfaultfd-sys/linux5_7"]
linux5_13 = ["userfaultfd-sys/linux5_13"]
30 changes: 19 additions & 11 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ pub enum FaultKind {
/// The fault was a write on a write-protected page.
#[cfg(feature = "linux5_7")]
WriteProtected,
// The fault was a minor page fault, meaning the page was present in the page cache,
// but the userspace page table entry was missing.
#[cfg(feature = "linux5_13")]
Minor,
}

/// Events from the userfaultfd object that are read by `Uffd::read_event()`.
Expand Down Expand Up @@ -82,17 +86,21 @@ impl Event {
match msg.event {
raw::UFFD_EVENT_PAGEFAULT => {
let pagefault = unsafe { msg.arg.pagefault };
cfg_if::cfg_if!(
if #[cfg(feature = "linux5_7")] {
let kind = if pagefault.flags & raw::UFFD_PAGEFAULT_FLAG_WP != 0 {
FaultKind::WriteProtected
} else {
FaultKind::Missing
};
} else {
let kind = FaultKind::Missing;
}
);

#[allow(unused_mut)]
let mut kind = FaultKind::Missing;

// The below two flags are mutually exclusive (it does not make sense
// to have a minor fault that is a write-protect fault at the same time.
#[cfg(feature = "linux5_7")]
if pagefault.flags & raw::UFFD_PAGEFAULT_FLAG_WP != 0 {
kind = FaultKind::WriteProtected;
}

#[cfg(feature = "linux5_13")]
if pagefault.flags & raw::UFFD_PAGEFAULT_FLAG_MINOR != 0 {
kind = FaultKind::Minor
}

let rw = if pagefault.flags & raw::UFFD_PAGEFAULT_FLAG_WRITE == 0 {
ReadWrite::Read
Expand Down
35 changes: 35 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ bitflags! {
/// Registers the range for write faults.
#[cfg(feature = "linux5_7")]
const WRITE_PROTECT = raw::UFFDIO_REGISTER_MODE_WP;
// Registers the range for minor faults.
#[cfg(feature = "linux5_13")]
const MINOR = raw::UFFDIO_REGISTER_MODE_MINOR;
}
}

Expand Down Expand Up @@ -269,6 +272,38 @@ impl Uffd {
Ok(())
}

/// Resolves minor faults for a range.
///
/// If `wake` is `true`, wake up the thread waiting for page fault resolution on the memory
/// address range.
///
/// Returns the number of bytes actually mapped. If this differs from `len`, then the ioctl
/// returned EAGAIN.
#[cfg(feature = "linux5_13")]
pub fn r#continue(&self, start: *mut c_void, len: usize, wake: bool) -> Result<u64> {
let mut ioctl = raw::uffdio_continue {
range: raw::uffdio_range {
start: start as u64,
len: len as u64,
},
mode: if wake {
0
} else {
raw::UFFDIO_CONTINUE_MODE_DONTWAKE
},
mapped: 0,
};

let r =
unsafe { raw::r#continue(self.as_raw_fd(), &mut ioctl as *mut raw::uffdio_continue) };

match r {
Err(Errno::EAGAIN) if ioctl.mapped > 0 => Ok(ioctl.mapped as u64),
Err(err) => Err(err.into()),
Ok(_) => Ok(ioctl.mapped as u64),
}
}

/// Read an `Event` from the userfaultfd object.
///
/// If the `Uffd` object was created with `non_blocking` set to `false`, this will block until
Expand Down
2 changes: 2 additions & 0 deletions src/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ nix::ioctl_readwrite!(
_UFFDIO_WRITEPROTECT,
uffdio_writeprotect
);
#[cfg(feature = "linux5_13")]
nix::ioctl_readwrite!(r#continue, UFFDIO, _UFFDIO_CONTINUE, uffdio_continue);

// ioctls for /dev/userfaultfd

Expand Down
3 changes: 2 additions & 1 deletion userfaultfd-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ cc = "1.0"
[features]
default = []
linux4_14 = []
linux5_7 = []
linux5_7 = ["linux4_14"]
linux5_13 = ["linux5_7"]
20 changes: 20 additions & 0 deletions userfaultfd-sys/src/consts.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ const __u64 _const_UFFDIO_REGISTER_MODE_MISSING = UFFDIO_REGISTER_MODE_MISSING;
const __u64 _const_UFFDIO_REGISTER_MODE_WP = UFFDIO_REGISTER_MODE_WP;
#endif

#ifdef UFFDIO_REGISTER_MODE_MINOR
const __u64 _const_UFFDIO_REGISTER_MODE_MINOR = UFFDIO_REGISTER_MODE_MINOR;
#endif

#ifdef UFFDIO_COPY_MODE_DONTWAKE
const __u64 _const_UFFDIO_COPY_MODE_DONTWAKE = UFFDIO_COPY_MODE_DONTWAKE;
#endif
Expand All @@ -42,6 +46,18 @@ const __u64 _const_UFFDIO_COPY_MODE_WP = UFFDIO_COPY_MODE_WP;
const __u64 _const_UFFDIO_ZEROPAGE_MODE_DONTWAKE = UFFDIO_ZEROPAGE_MODE_DONTWAKE;
#endif

#ifdef UFFDIO_WRITEPROTECT_MODE_WP
const __u64 _const_UFFDIO_WRITEPROTECT_MODE_WP = UFFDIO_WRITEPROTECT_MODE_WP;
#endif

#ifdef UFFDIO_CONTINUE_MODE_DONTWAKE
const __u64 _const_UFFDIO_CONTINUE_MODE_DONTWAKE = UFFDIO_CONTINUE_MODE_DONTWAKE;
#endif

#ifdef UFFDIO_WRITEPROTECT_MODE_DONTWAKE
const __u64 _const_UFFDIO_WRITEPROTECT_MODE_DONTWAKE = UFFDIO_WRITEPROTECT_MODE_DONTWAKE;
#endif

#ifdef UFFDIO_API
const __u32 _const_UFFDIO_API = UFFDIO_API;
#endif
Expand Down Expand Up @@ -70,6 +86,10 @@ const __u32 _const_UFFDIO_ZEROPAGE = UFFDIO_ZEROPAGE;
const __u32 _const_UFFDIO_WRITEPROTECT = UFFDIO_WRITEPROTECT;
#endif

#ifdef UFFDIO_CONTINUE
const __u32 _const_UFFDIO_CONTINUE = UFFDIO_CONTINUE;
#endif

#ifdef USERFAULTFD_IOC
const __u32 _const_USERFAULTFD_IOC = USERFAULTFD_IOC;
#endif
51 changes: 45 additions & 6 deletions userfaultfd-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,57 @@

use cfg_if::cfg_if;

mod linux4_11;
#[cfg(feature = "linux4_14")]
mod linux4_14;
#[cfg(feature = "linux5_7")]
mod linux5_7;

#[cfg(feature = "linux5_13")]
mod linux5_13;

cfg_if! {
if #[cfg(feature = "linux5_7")] {
mod linux5_7;
if #[cfg(feature = "linux5_13")] {
pub use crate::linux5_13::*;
} else if #[cfg(feature = "linux5_7")] {
pub use crate::linux5_7::*;
}
else if #[cfg(feature = "linux4_14")] {
mod linux4_14;
} else if #[cfg(feature = "linux4_14")] {
pub use crate::linux4_14::*;
} else {
mod linux4_11;
pub use crate::linux4_11::*;
}
}

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

#[cfg(test)]
mod const_tests {
use super::*;

extern "C" {
static _const_UFFD_API_FEATURES: u64;
static _const_UFFD_API_RANGE_IOCTLS: u64;
static _const_UFFD_API_RANGE_IOCTLS_BASIC: u64;
}

#[test]
fn consts_correct() {
unsafe {
assert_eq!(
UFFD_API_FEATURES & _const_UFFD_API_FEATURES,
UFFD_API_FEATURES,
"UFFD_API_FEATURES"
);
assert_eq!(
UFFD_API_RANGE_IOCTLS & _const_UFFD_API_RANGE_IOCTLS,
UFFD_API_RANGE_IOCTLS,
"UFFD_API_RANGE_IOCTLS"
);
assert_eq!(
UFFD_API_RANGE_IOCTLS_BASIC & _const_UFFD_API_RANGE_IOCTLS_BASIC,
UFFD_API_RANGE_IOCTLS_BASIC,
"UFFD_API_RANGE_IOCTLS_BASIC"
);
}
}
}
16 changes: 1 addition & 15 deletions userfaultfd-sys/src/linux4_11.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub const UFFD_API_FEATURES: u64 = UFFD_FEATURE_EVENT_FORK
pub const UFFD_API_IOCTLS: u64 = 1 << _UFFDIO_REGISTER | 1 << _UFFDIO_UNREGISTER | 1 << _UFFDIO_API;
pub const UFFD_API_RANGE_IOCTLS: u64 =
1 << _UFFDIO_WAKE | 1 << _UFFDIO_COPY | 1 << _UFFDIO_ZEROPAGE;
pub const UFFD_API_RANGE_IOCTLS_BASIC: u64 = 0;

pub const UFFDIO_REGISTER_MODE_MISSING: u64 = 1 << 0;
pub const UFFDIO_REGISTER_MODE_WP: u64 = 1 << 1;
Expand All @@ -29,15 +30,13 @@ pub const UFFDIO_UNREGISTER: u32 = 0x8010aa01;
pub const UFFDIO_WAKE: u32 = 0x8010aa02;
pub const UFFDIO_COPY: u32 = 0xc028aa03;
pub const UFFDIO_ZEROPAGE: u32 = 0xc020aa04;
pub const UFFDIO_WRITEPROTECT: u32 = 0xc018aa06;

#[cfg(test)]
mod const_tests {
use super::*;

extern "C" {
static _const_UFFD_API: u64;
static _const_UFFD_API_FEATURES: u64;
static _const_UFFD_API_IOCTLS: u64;
static _const_UFFD_API_RANGE_IOCTLS: u64;
static _const_UFFDIO_REGISTER_MODE_MISSING: u64;
Expand All @@ -51,22 +50,13 @@ mod const_tests {
static _const_UFFDIO_WAKE: u32;
static _const_UFFDIO_COPY: u32;
static _const_UFFDIO_ZEROPAGE: u32;
static _const_UFFDIO_WRITEPROTECT: u32;
}

#[test]
fn consts_correct() {
unsafe {
assert_eq!(UFFD_API, _const_UFFD_API, "UFFD_API");
assert_eq!(
UFFD_API_FEATURES, _const_UFFD_API_FEATURES,
"UFFD_API_FEATURES"
);
assert_eq!(UFFD_API_IOCTLS, _const_UFFD_API_IOCTLS, "UFFD_API_IOCTLS");
assert_eq!(
UFFD_API_RANGE_IOCTLS, _const_UFFD_API_RANGE_IOCTLS,
"UFFD_API_RANGE_IOCTLS"
);
assert_eq!(
UFFDIO_REGISTER_MODE_MISSING, _const_UFFDIO_REGISTER_MODE_MISSING,
"UFFDIO_REGISTER_MODE_MISSING"
Expand Down Expand Up @@ -96,10 +86,6 @@ mod const_tests {
assert_eq!(UFFDIO_WAKE, _const_UFFDIO_WAKE, "UFFDIO_WAKE");
assert_eq!(UFFDIO_COPY, _const_UFFDIO_COPY, "UFFDIO_COPY");
assert_eq!(UFFDIO_ZEROPAGE, _const_UFFDIO_ZEROPAGE, "UFFDIO_ZEROPAGE");
assert_eq!(
UFFDIO_WRITEPROTECT, _const_UFFDIO_WRITEPROTECT,
"UFFDIO_WRITEPROTECT"
);
}
}
}
Loading
Loading