diff --git a/crates/environ/src/module.rs b/crates/environ/src/module.rs index 79342f3546..b70748ec72 100644 --- a/crates/environ/src/module.rs +++ b/crates/environ/src/module.rs @@ -33,7 +33,7 @@ impl MemoryStyle { let maximum = std::cmp::min( memory.maximum.unwrap_or(WASM_MAX_PAGES), if tunables.static_memory_bound_is_maximum { - tunables.static_memory_bound + std::cmp::min(tunables.static_memory_bound, WASM_MAX_PAGES) } else { WASM_MAX_PAGES }, diff --git a/crates/runtime/src/instance/allocator/pooling.rs b/crates/runtime/src/instance/allocator/pooling.rs index 394b231c62..c759780ad8 100644 --- a/crates/runtime/src/instance/allocator/pooling.rs +++ b/crates/runtime/src/instance/allocator/pooling.rs @@ -60,7 +60,7 @@ pub struct ModuleLimits { /// The maximum number of imported tables for a module (default is 0). pub imported_tables: u32, - /// The maximum number of imported memories for a module (default is 0). + /// The maximum number of imported linear memories for a module (default is 0). pub imported_memories: u32, /// The maximum number of imported globals for a module (default is 0). @@ -75,7 +75,7 @@ pub struct ModuleLimits { /// The maximum number of defined tables for a module (default is 1). pub tables: u32, - /// The maximum number of defined memories for a module (default is 1). + /// The maximum number of defined linear memories for a module (default is 1). pub memories: u32, /// The maximum number of defined globals for a module (default is 10). @@ -90,7 +90,7 @@ pub struct ModuleLimits { /// the maximum will be `table_elements` for the purpose of any `table.grow` instruction. pub table_elements: u32, - /// The maximum number of pages for any memory defined in a module (default is 160). + /// The maximum number of pages for any linear memory defined in a module (default is 160). /// /// The default of 160 means at most 10 MiB of host memory may be committed for each instance. /// @@ -100,7 +100,7 @@ pub struct ModuleLimits { /// If a memory's maximum page limit is unbounded or greater than this value, /// the maximum will be `memory_pages` for the purpose of any `memory.grow` instruction. /// - /// This value cannot exceed any address space limits placed on instances. + /// This value cannot exceed any memory reservation size limits placed on instances. pub memory_pages: u32, } @@ -234,14 +234,14 @@ pub struct InstanceLimits { /// The maximum number of concurrent instances supported (default is 1000). pub count: u32, - /// The maximum reserved host address space size to use for each instance in bytes. + /// The maximum size, in bytes, of host address space to reserve for each linear memory of an instance. /// /// Note: this value has important performance ramifications. /// /// On 64-bit platforms, the default for this value will be 6 GiB. A value of less than 4 GiB will /// force runtime bounds checking for memory accesses and thus will negatively impact performance. /// Any value above 4 GiB will start eliding bounds checks provided the `offset` of the memory access is - /// less than (`address_space_size` - 4 GiB). A value of 8 GiB will completely elide *all* bounds + /// less than (`memory_reservation_size` - 4 GiB). A value of 8 GiB will completely elide *all* bounds /// checks; consequently, 8 GiB will be the maximum supported value. The default of 6 GiB reserves /// less host address space for each instance, but a memory access with an offet above 2 GiB will incur /// runtime bounds checks. @@ -251,7 +251,7 @@ pub struct InstanceLimits { /// for all memory accesses. For better runtime performance, a 64-bit host is recommended. /// /// This value will be rounded up by the WebAssembly page size (64 KiB). - pub address_space_size: u64, + pub memory_reservation_size: u64, } impl Default for InstanceLimits { @@ -260,9 +260,9 @@ impl Default for InstanceLimits { Self { count: 1000, #[cfg(target_pointer_width = "32")] - address_space_size: 0xA00000, + memory_reservation_size: 0xA00000, #[cfg(target_pointer_width = "64")] - address_space_size: 0x180000000, + memory_reservation_size: 0x180000000, } } } @@ -611,8 +611,8 @@ struct MemoryPool { impl MemoryPool { fn new(module_limits: &ModuleLimits, instance_limits: &InstanceLimits) -> Result { - let memory_size = usize::try_from(instance_limits.address_space_size) - .map_err(|_| "address space size exceeds addressable memory".to_string())?; + let memory_size = usize::try_from(instance_limits.memory_reservation_size) + .map_err(|_| "memory reservation size exceeds addressable memory".to_string())?; debug_assert!( memory_size % region::page::size() == 0, @@ -627,7 +627,7 @@ impl MemoryPool { .checked_mul(max_memories) .and_then(|c| c.checked_mul(max_instances)) .ok_or_else(|| { - "total size of instance address space exceeds addressable memory".to_string() + "total size of memory reservation exceeds addressable memory".to_string() })?; Ok(Self { @@ -877,15 +877,16 @@ impl PoolingInstanceAllocator { return Err("the instance count limit cannot be zero".into()); } - // Round the instance address space size to the nearest Wasm page size - instance_limits.address_space_size = u64::try_from(round_up_to_pow2( - usize::try_from(instance_limits.address_space_size).unwrap(), + // Round the memory reservation size to the nearest Wasm page size + instance_limits.memory_reservation_size = u64::try_from(round_up_to_pow2( + usize::try_from(instance_limits.memory_reservation_size).unwrap(), WASM_PAGE_SIZE as usize, )) .unwrap(); - // Cap the instance address space size to 8 GiB (maximum 4 GiB address space + 4 GiB of guard region) - instance_limits.address_space_size = min(instance_limits.address_space_size, 0x200000000); + // Cap the memory reservation size to 8 GiB (maximum 4 GiB accessible + 4 GiB of guard region) + instance_limits.memory_reservation_size = + min(instance_limits.memory_reservation_size, 0x200000000); // The maximum module memory page count cannot exceed 65536 pages if module_limits.memory_pages > 0x10000 { @@ -895,13 +896,14 @@ impl PoolingInstanceAllocator { )); } - // The maximum module memory page count cannot exceed the instance address space size - if (module_limits.memory_pages * WASM_PAGE_SIZE) as u64 > instance_limits.address_space_size + // The maximum module memory page count cannot exceed the memory reservation size + if (module_limits.memory_pages * WASM_PAGE_SIZE) as u64 + > instance_limits.memory_reservation_size { return Err(format!( - "module memory page limit of {} pages exeeds the instance address space size limit of {} bytes", + "module memory page limit of {} pages exeeds the memory reservation size limit of {} bytes", module_limits.memory_pages, - instance_limits.address_space_size + instance_limits.memory_reservation_size )); } @@ -940,15 +942,15 @@ unsafe impl InstanceAllocator for PoolingInstanceAllocator { } fn adjust_tunables(&self, tunables: &mut Tunables) { - let address_space_size = self.instance_limits.address_space_size; + let memory_reservation_size = self.instance_limits.memory_reservation_size; - // For address spaces larger than 4 GiB, use a guard region to elide - if address_space_size >= 0x100000000 { + // For reservation sizes larger than 4 GiB, use a guard region to elide bounds checks + if memory_reservation_size >= 0x100000000 { tunables.static_memory_bound = 0x10000; // in Wasm pages - tunables.static_memory_offset_guard_size = address_space_size - 0x100000000; + tunables.static_memory_offset_guard_size = memory_reservation_size - 0x100000000; } else { tunables.static_memory_bound = - u32::try_from(address_space_size).unwrap() / WASM_PAGE_SIZE; + u32::try_from(memory_reservation_size).unwrap() / WASM_PAGE_SIZE; tunables.static_memory_offset_guard_size = 0; } @@ -1349,7 +1351,7 @@ mod test { }; let instance_limits = InstanceLimits { count: 3, - address_space_size: 4096, + memory_reservation_size: 4096, }; let instances = InstancePool::new(&module_limits, &instance_limits)?; @@ -1471,7 +1473,7 @@ mod test { }, &InstanceLimits { count: 5, - address_space_size: WASM_PAGE_SIZE as u64, + memory_reservation_size: WASM_PAGE_SIZE as u64, }, )?; @@ -1517,7 +1519,7 @@ mod test { }, &InstanceLimits { count: 7, - address_space_size: WASM_PAGE_SIZE as u64, + memory_reservation_size: WASM_PAGE_SIZE as u64, }, )?; @@ -1551,7 +1553,7 @@ mod test { let pool = StackPool::new( &InstanceLimits { count: 10, - address_space_size: 0, + memory_reservation_size: 0, }, 1, )?; @@ -1638,7 +1640,7 @@ mod test { }, InstanceLimits { count: 1, - address_space_size: 1, + memory_reservation_size: 1, }, 4096 ) @@ -1648,7 +1650,7 @@ mod test { } #[test] - fn test_pooling_allocator_with_address_space_exeeded() { + fn test_pooling_allocator_with_reservation_size_exeeded() { assert_eq!( PoolingInstanceAllocator::new( PoolingAllocationStrategy::Random, @@ -1658,12 +1660,12 @@ mod test { }, InstanceLimits { count: 1, - address_space_size: 1, + memory_reservation_size: 1, }, 4096, ) .expect_err("expected a failure constructing instance allocator"), - "module memory page limit of 2 pages exeeds the instance address space size limit of 65536 bytes" + "module memory page limit of 2 pages exeeds the memory reservation size limit of 65536 bytes" ); } @@ -1686,7 +1688,7 @@ mod test { }, InstanceLimits { count: 1, - address_space_size: 1, + memory_reservation_size: 1, }, 4096, )?; diff --git a/crates/runtime/src/instance/allocator/pooling/uffd.rs b/crates/runtime/src/instance/allocator/pooling/uffd.rs index d18db383ef..793b63522f 100644 --- a/crates/runtime/src/instance/allocator/pooling/uffd.rs +++ b/crates/runtime/src/instance/allocator/pooling/uffd.rs @@ -1,12 +1,12 @@ -//! Implements user-mode page fault handling with the `userfaultfd` ("uffd") system call on Linux. +//! Implements user space page fault handling with the `userfaultfd` ("uffd") system call on Linux. //! //! Handling page faults for memory accesses in regions relating to WebAssembly instances -//! enables the implementation of guard pages in user space rather than kernel space. +//! enables the implementation of protecting guard pages in user space rather than kernel space. //! //! This reduces the number of system calls and kernel locks needed to provide correct //! WebAssembly memory semantics. //! -//! Additionally, linear memories and WebAssembly tables can be lazy-initialized upon access. +//! Additionally, linear memories can be lazy-initialized upon access. //! //! This feature requires a Linux kernel 4.11 or newer to use. @@ -49,7 +49,7 @@ pub fn create_memory_map(_accessible_size: usize, mapping_size: usize) -> Result // Allocate a single read-write region at once // As writable pages need to count towards commit charge, use MAP_NORESERVE to override. // This implies that the kernel is configured to allow overcommit or else - // this allocation will almost certainly fail without a plethora of physical memory to back the alloction. + // this allocation will almost certainly fail without a plethora of physical memory to back the allocation. // The consequence of not reserving is that our process may segfault on any write to a memory // page that cannot be backed (i.e. out of memory conditions). @@ -170,8 +170,8 @@ impl AddressLocator { } } - // This is super-duper unsafe as it is used from the handler thread - // to access instance data without any locking primitives. + /// This is super-duper unsafe as it is used from the handler thread + /// to access instance data without any locking primitives. /// /// It is assumed that the thread that owns the instance being accessed is /// currently suspended waiting on a fault to be handled. @@ -182,9 +182,9 @@ impl AddressLocator { /// /// If the assumption holds true, accessing the instance data from the handler thread /// should, in theory, be safe. - unsafe fn get_instance(&self, index: usize) -> &mut Instance { + unsafe fn get_instance(&self, index: usize) -> &Instance { debug_assert!(index < self.max_instances); - &mut *((self.instances_start + (index * self.instance_size)) as *mut Instance) + &*((self.instances_start + (index * self.instance_size)) as *const Instance) } unsafe fn get_location(&self, addr: usize) -> Option { @@ -475,6 +475,8 @@ fn handler_thread( } } + log::trace!("fault handler thread has successfully terminated"); + Ok(()) } @@ -591,7 +593,7 @@ mod test { }; let instance_limits = InstanceLimits { count: 3, - address_space_size: (WASM_PAGE_SIZE * 10) as u64, + memory_reservation_size: (WASM_PAGE_SIZE * 10) as u64, }; let instances = diff --git a/crates/wasmtime/src/lib.rs b/crates/wasmtime/src/lib.rs index 8b8a74c508..f0116362c9 100644 --- a/crates/wasmtime/src/lib.rs +++ b/crates/wasmtime/src/lib.rs @@ -173,7 +173,7 @@ //! supporting VTune profiling of JIT code. //! //! * `uffd` - Not enabled by default. This feature enables `userfaultfd` support -//! when using the pooling instance allocator. As handling page faults in userspace +//! when using the pooling instance allocator. As handling page faults in user space //! comes with a performance penalty, this feature should only be enabled when kernel //! lock contention is hampering multithreading throughput. This feature is only //! supported on Linux and requires a Linux kernel version 4.11 or higher. diff --git a/tests/all/async_functions.rs b/tests/all/async_functions.rs index eb3ea09611..6f90657bf4 100644 --- a/tests/all/async_functions.rs +++ b/tests/all/async_functions.rs @@ -378,7 +378,7 @@ fn async_with_pooling_stacks() { }, instance_limits: InstanceLimits { count: 1, - address_space_size: 1, + memory_reservation_size: 1, }, }) .expect("pooling allocator created"); diff --git a/tests/all/pooling_allocator.rs b/tests/all/pooling_allocator.rs index 846dac647e..d7b7101fee 100644 --- a/tests/all/pooling_allocator.rs +++ b/tests/all/pooling_allocator.rs @@ -13,7 +13,7 @@ fn successful_instantiation() -> Result<()> { }, instance_limits: InstanceLimits { count: 1, - address_space_size: 1, + memory_reservation_size: 1, }, })?; @@ -39,7 +39,7 @@ fn memory_limit() -> Result<()> { }, instance_limits: InstanceLimits { count: 1, - address_space_size: 196608, + memory_reservation_size: 196608, }, })?; @@ -191,7 +191,7 @@ fn memory_zeroed() -> Result<()> { }, instance_limits: InstanceLimits { count: 1, - address_space_size: 1, + memory_reservation_size: 1, }, })?; @@ -234,7 +234,7 @@ fn table_limit() -> Result<()> { }, instance_limits: InstanceLimits { count: 1, - address_space_size: 1, + memory_reservation_size: 1, }, })?; @@ -349,7 +349,7 @@ fn table_zeroed() -> Result<()> { }, instance_limits: InstanceLimits { count: 1, - address_space_size: 1, + memory_reservation_size: 1, }, })?; @@ -391,7 +391,7 @@ fn instantiation_limit() -> Result<()> { }, instance_limits: InstanceLimits { count: INSTANCE_LIMIT, - address_space_size: 1, + memory_reservation_size: 1, }, })?;