1
- //! Region backend for `lucet-runtime` backed by
2
- //! [`userfaultfd(2)`](http://man7.org/linux/man-pages/man2/userfaultfd.2.html) via the
3
- //! `userfaultfd-rs` crate.
4
-
5
1
use crate :: alloc:: { host_page_size, instance_heap_offset, AddrLocation , Alloc , Limits , Slot } ;
6
2
use crate :: embed_ctx:: CtxMap ;
7
3
use crate :: error:: Error ;
@@ -18,32 +14,32 @@ use std::sync::{Arc, Mutex, Weak};
18
14
use std:: thread:: { self , JoinHandle } ;
19
15
use userfaultfd:: { IoctlFlags , Uffd , UffdBuilder } ;
20
16
21
- /// A [`Region`](../ trait.Region.html) backed by `mmap` and managed by `userfaultfd`.
17
+ /// A [`Region`](trait.Region.html) backed by `mmap` and managed by `userfaultfd`.
22
18
///
23
- /// Much like [`MmapRegion`](../mmap/struct.MmapRegion.html) `UffdRegion` lays out virtual memory
24
- /// in a contiguous block. See [`MmapRegion`](../mmap/struct.MmapRegion.html) for details of the
25
- /// memory layout.
19
+ /// Much like [`MmapRegion`](struct.MmapRegion.html) `UffdRegion` lays out virtual memory in a
20
+ /// contiguous block. See [`MmapRegion`](struct.MmapRegion.html) for details of the memory layout.
26
21
///
27
- /// The difference is that `UffdRegion` is lazy. Only the minimum required physical memory is set
28
- /// up to back that virtual memory before an `Instance` begins running. The stack and the heap
29
- /// are both lazily allocated at runtime.
22
+ /// The difference is that `UffdRegion` is lazy. Only the minimum required physical memory is set up
23
+ /// to back that virtual memory before an `Instance` begins running. The stack and the heap are both
24
+ /// lazily allocated at runtime.
30
25
///
31
- /// That lazy allocation is handled by the [`userfaultfd`](http://man7.org/linux/man-pages/man2/ userfaultfd.2.html)
32
- /// system in recent Linux kernels. The entire Region is registered with `userfaultfd` handle.
33
- /// When page faults occur due to attempts by the `Guest` to access the lazy memory, the `Guest`
34
- /// thread is paused and a message is sent over the `userfaultfd` handle.
26
+ /// That lazy allocation is handled by the [`userfaultfd`][ userfaultfd] system, using extensions
27
+ /// available in Linux version 4.11 or newer. The entire ` Region` is registered with `userfaultfd`
28
+ /// handle. When page faults occur due to attempts by the guest to access the lazy memory, the
29
+ /// guest thread is paused and a message is sent over the `userfaultfd` handle.
35
30
///
36
- /// That message is picked up a separate thread which has the job of handling page faults. How
37
- /// it is handled is dependent on where the page fault occurred. In the case where it occurs in
38
- /// the stack, we just zero out the page. In the case it occurs in the heap, it is handled
39
- /// differently depending on whether the page should contain data defined in the WebAssembly
40
- /// module. In the case it should be blank we again just zero it out. In the case that it should
41
- /// contain data, we copy the data into the page. In any case we finish by reawakening the `Guest`
42
- /// thread.
31
+ /// That message is picked up a separate thread which has the job of handling page faults. How it is
32
+ /// handled is dependent on where the page fault occurred. In the case where it occurs in the stack,
33
+ /// we just zero out the page. In the case it occurs in the heap, it is handled differently
34
+ /// depending on whether the page should contain data defined in the WebAssembly module. In the case
35
+ /// it should be blank we again just zero it out. In the case that it should contain data, we copy
36
+ /// the data into the page. In any case we finish by reawakening the guest thread.
43
37
///
44
38
/// If the fault occurs in a guard page, we do nothing, and reawaken the thread without allocating
45
- /// the backing physical memory. This ends up causing the `Guest` thread to throw a SIGBUS. Which
46
- /// is caught and handled as normal.
39
+ /// the backing physical memory. This ends up causing the guest thread to raise a SIGBUS, which is
40
+ /// treated as a fatal error by the Lucet signal handler.
41
+ ///
42
+ /// [userfaultfd]: http://man7.org/linux/man-pages/man2/userfaultfd.2.html
47
43
pub struct UffdRegion {
48
44
uffd : Arc < Uffd > ,
49
45
start : * mut c_void ,
@@ -380,7 +376,7 @@ impl UffdRegion {
380
376
/// Create a new `UffdRegion` that can support a given number of instances, each subject to the
381
377
/// same runtime limits.
382
378
///
383
- /// The region is turned in an `Arc`, because any instances created from it carry a reference
379
+ /// The region is returned in an `Arc`, because any instances created from it carry a reference
384
380
/// back to the region.
385
381
///
386
382
/// This also creates and starts a separate thread that is responsible for handling page faults
@@ -406,7 +402,7 @@ impl UffdRegion {
406
402
if let Some ( sz) = instance_capacity. checked_mul ( limits. total_memory_size ( ) ) {
407
403
sz
408
404
} else {
409
- lucet_bail ! ( " region size overflowed" ) ;
405
+ return Err ( Error :: InvalidArgument ( "requested region size too large" ) ) ;
410
406
} ;
411
407
let start = unsafe {
412
408
mmap (
@@ -424,7 +420,7 @@ impl UffdRegion {
424
420
. register ( start, total_region_size)
425
421
. map_err ( |e| Error :: InternalError ( e. into ( ) ) ) ?;
426
422
if !ioctls. contains ( IoctlFlags :: WAKE | IoctlFlags :: COPY | IoctlFlags :: ZEROPAGE ) {
427
- lucet_bail ! ( "required uffd ioctls not supported; found: {:?}" , ioctls) ;
423
+ panic ! ( "required uffd ioctls not supported; found: {:?}" , ioctls) ;
428
424
}
429
425
430
426
let ( handler_pipe_recv, handler_pipe) = nix:: unistd:: pipe ( ) ?;
@@ -464,7 +460,7 @@ impl UffdRegion {
464
460
}
465
461
res
466
462
} )
467
- . map_err ( |e| lucet_format_err ! ( "error spawning uffd region handler: {}" , e ) ) ? ;
463
+ . expect ( "error spawning uffd region handler" ) ;
468
464
469
465
let region = Arc :: new ( UffdRegion {
470
466
uffd,
0 commit comments