Skip to content

Commit 6902c27

Browse files
committed
Add support for UFFDIO_CONTINUE / MODE_MINOR
With Linux 5.13, uffd got support for minor page fault notifications (e.g. a page was already in the page cache, but not present in the userspace page tables). Notifications are enabled by registering with UFFDIO_REGISTER_MODE_MINOR, and resolved via the UFFDIO_CONTINUE ioctl. Add support for these in userfaultfd-sys, as well as utility wrappers in the main crate. Signed-off-by: Patrick Roy <[email protected]>
1 parent 43f1f6c commit 6902c27

File tree

8 files changed

+119
-12
lines changed

8 files changed

+119
-12
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ nix = { version = "0.27", features = ["poll", "mman", "feature"] }
2323
default = []
2424
linux4_14 = ["userfaultfd-sys/linux4_14", "nix/process"]
2525
linux5_7 = ["userfaultfd-sys/linux5_7"]
26+
linux5_13 = ["userfaultfd-sys/linux5_13"]

src/event.rs

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ pub enum FaultKind {
2121
/// The fault was a write on a write-protected page.
2222
#[cfg(feature = "linux5_7")]
2323
WriteProtected,
24+
// The fault was a minor page fault, meaning the page was present in the page cache,
25+
// but the userspace page table entry was missing.
26+
#[cfg(feature = "linux5_13")]
27+
Minor,
2428
}
2529

2630
/// Events from the userfaultfd object that are read by `Uffd::read_event()`.
@@ -82,17 +86,21 @@ impl Event {
8286
match msg.event {
8387
raw::UFFD_EVENT_PAGEFAULT => {
8488
let pagefault = unsafe { msg.arg.pagefault };
85-
cfg_if::cfg_if!(
86-
if #[cfg(feature = "linux5_7")] {
87-
let kind = if pagefault.flags & raw::UFFD_PAGEFAULT_FLAG_WP != 0 {
88-
FaultKind::WriteProtected
89-
} else {
90-
FaultKind::Missing
91-
};
92-
} else {
93-
let kind = FaultKind::Missing;
94-
}
95-
);
89+
90+
#[allow(unused_mut)]
91+
let mut kind = FaultKind::Missing;
92+
93+
// The below two flags are mutually exclusive (it does not make sense
94+
// to have a minor fault that is a write-protect fault at the same time.
95+
#[cfg(feature = "linux5_7")]
96+
if pagefault.flags & raw::UFFD_PAGEFAULT_FLAG_WP != 0 {
97+
kind = FaultKind::WriteProtected;
98+
}
99+
100+
#[cfg(feature = "linux5_13")]
101+
if pagefault.flags & raw::UFFD_PAGEFAULT_FLAG_MINOR != 0 {
102+
kind = FaultKind::Minor
103+
}
96104

97105
let rw = if pagefault.flags & raw::UFFD_PAGEFAULT_FLAG_WRITE == 0 {
98106
ReadWrite::Read

src/lib.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ bitflags! {
8787
/// Registers the range for write faults.
8888
#[cfg(feature = "linux5_7")]
8989
const WRITE_PROTECT = raw::UFFDIO_REGISTER_MODE_WP;
90+
// Registers the range for minor faults.
91+
#[cfg(feature = "linux5_13")]
92+
const MINOR = raw::UFFDIO_REGISTER_MODE_MINOR;
9093
}
9194
}
9295

@@ -269,6 +272,38 @@ impl Uffd {
269272
Ok(())
270273
}
271274

275+
/// Resolves minor faults for a range.
276+
///
277+
/// If `wake` is `true`, wake up the thread waiting for page fault resolution on the memory
278+
/// address range.
279+
///
280+
/// Returns the number of bytes actually mapped. If this differs from `len`, then the ioctl
281+
/// returned EAGAIN.
282+
#[cfg(feature = "linux5_13")]
283+
pub fn r#continue(&self, start: *mut c_void, len: usize, wake: bool) -> Result<u64> {
284+
let mut ioctl = raw::uffdio_continue {
285+
range: raw::uffdio_range {
286+
start: start as u64,
287+
len: len as u64,
288+
},
289+
mode: if wake {
290+
0
291+
} else {
292+
raw::UFFDIO_CONTINUE_MODE_DONTWAKE
293+
},
294+
mapped: 0,
295+
};
296+
297+
let r =
298+
unsafe { raw::r#continue(self.as_raw_fd(), &mut ioctl as *mut raw::uffdio_continue) };
299+
300+
match r {
301+
Err(Errno::EAGAIN) if ioctl.mapped > 0 => Ok(ioctl.mapped as u64),
302+
Err(err) => Err(err.into()),
303+
Ok(_) => Ok(ioctl.mapped as u64),
304+
}
305+
}
306+
272307
/// Read an `Event` from the userfaultfd object.
273308
///
274309
/// If the `Uffd` object was created with `non_blocking` set to `false`, this will block until

src/raw.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ nix::ioctl_readwrite!(
2323
_UFFDIO_WRITEPROTECT,
2424
uffdio_writeprotect
2525
);
26+
#[cfg(feature = "linux5_13")]
27+
nix::ioctl_readwrite!(r#continue, UFFDIO, _UFFDIO_CONTINUE, uffdio_continue);
2628

2729
// ioctls for /dev/userfaultfd
2830

userfaultfd-sys/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ cc = "1.0"
2020
default = []
2121
linux4_14 = []
2222
linux5_7 = ["linux4_14"]
23+
linux5_13 = ["linux5_7"]

userfaultfd-sys/src/consts.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ const __u64 _const_UFFDIO_REGISTER_MODE_MISSING = UFFDIO_REGISTER_MODE_MISSING;
3030
const __u64 _const_UFFDIO_REGISTER_MODE_WP = UFFDIO_REGISTER_MODE_WP;
3131
#endif
3232

33+
#ifdef UFFDIO_REGISTER_MODE_MINOR
34+
const __u64 _const_UFFDIO_REGISTER_MODE_MINOR = UFFDIO_REGISTER_MODE_MINOR;
35+
#endif
36+
3337
#ifdef UFFDIO_COPY_MODE_DONTWAKE
3438
const __u64 _const_UFFDIO_COPY_MODE_DONTWAKE = UFFDIO_COPY_MODE_DONTWAKE;
3539
#endif
@@ -46,6 +50,10 @@ const __u64 _const_UFFDIO_ZEROPAGE_MODE_DONTWAKE = UFFDIO_ZEROPAGE_MODE_DONTWAKE
4650
const __u64 _const_UFFDIO_WRITEPROTECT_MODE_WP = UFFDIO_WRITEPROTECT_MODE_WP;
4751
#endif
4852

53+
#ifdef UFFDIO_CONTINUE_MODE_DONTWAKE
54+
const __u64 _const_UFFDIO_CONTINUE_MODE_DONTWAKE = UFFDIO_CONTINUE_MODE_DONTWAKE;
55+
#endif
56+
4957
#ifdef UFFDIO_WRITEPROTECT_MODE_DONTWAKE
5058
const __u64 _const_UFFDIO_WRITEPROTECT_MODE_DONTWAKE = UFFDIO_WRITEPROTECT_MODE_DONTWAKE;
5159
#endif
@@ -78,6 +86,10 @@ const __u32 _const_UFFDIO_ZEROPAGE = UFFDIO_ZEROPAGE;
7886
const __u32 _const_UFFDIO_WRITEPROTECT = UFFDIO_WRITEPROTECT;
7987
#endif
8088

89+
#ifdef UFFDIO_CONTINUE
90+
const __u32 _const_UFFDIO_CONTINUE = UFFDIO_CONTINUE;
91+
#endif
92+
8193
#ifdef USERFAULTFD_IOC
8294
const __u32 _const_USERFAULTFD_IOC = USERFAULTFD_IOC;
8395
#endif

userfaultfd-sys/src/lib.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,13 @@ mod linux4_14;
1515
#[cfg(feature = "linux5_7")]
1616
mod linux5_7;
1717

18+
#[cfg(feature = "linux5_13")]
19+
mod linux5_13;
20+
1821
cfg_if! {
19-
if #[cfg(feature = "linux5_7")] {
22+
if #[cfg(feature = "linux5_13")] {
23+
pub use crate::linux5_13::*;
24+
} else if #[cfg(feature = "linux5_7")] {
2025
pub use crate::linux5_7::*;
2126
} else if #[cfg(feature = "linux4_14")] {
2227
pub use crate::linux4_14::*;

userfaultfd-sys/src/linux5_13.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use super::*;
2+
3+
pub use linux5_7::{
4+
UFFDIO_API, UFFDIO_COPY, UFFDIO_COPY_MODE_DONTWAKE, UFFDIO_COPY_MODE_WP, UFFDIO_REGISTER,
5+
UFFDIO_REGISTER_MODE_MISSING, UFFDIO_REGISTER_MODE_WP, UFFDIO_UNREGISTER, UFFDIO_WAKE,
6+
UFFDIO_WRITEPROTECT, UFFDIO_WRITEPROTECT_MODE_DONTWAKE, UFFDIO_WRITEPROTECT_MODE_WP,
7+
UFFDIO_ZEROPAGE, UFFDIO_ZEROPAGE_MODE_DONTWAKE, UFFD_API, UFFD_API_FEATURES, UFFD_API_IOCTLS,
8+
UFFD_API_RANGE_IOCTLS_BASIC,
9+
};
10+
11+
pub const UFFD_API_RANGE_IOCTLS: u64 = linux5_7::UFFD_API_RANGE_IOCTLS | 1 << _UFFDIO_CONTINUE;
12+
13+
pub const UFFDIO_REGISTER_MODE_MINOR: u64 = 1 << 2;
14+
15+
pub const UFFDIO_CONTINUE_MODE_DONTWAKE: u64 = 1 << 0;
16+
17+
pub const UFFDIO_CONTINUE: u32 = 0xc020aa07;
18+
19+
#[cfg(test)]
20+
mod const_tests {
21+
use super::*;
22+
23+
extern "C" {
24+
static _const_UFFDIO_REGISTER_MODE_MINOR: u64;
25+
static _const_UFFDIO_CONTINUE_MODE_DONTWAKE: u64;
26+
static _const_UFFDIO_CONTINUE: u32;
27+
}
28+
29+
#[test]
30+
fn consts_correct() {
31+
unsafe {
32+
assert_eq!(
33+
UFFDIO_REGISTER_MODE_MINOR, _const_UFFDIO_REGISTER_MODE_MINOR,
34+
"UFFDIO_REGISTER_MODE_MINOR"
35+
);
36+
assert_eq!(
37+
UFFDIO_CONTINUE_MODE_DONTWAKE, _const_UFFDIO_CONTINUE_MODE_DONTWAKE,
38+
"UFFDIO_CONTINUE_MODE_DONTWAKE"
39+
);
40+
assert_eq!(UFFDIO_CONTINUE, _const_UFFDIO_CONTINUE, "UFFDIO_CONTINUE");
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)