From eefd082c20711115fe4a57c814ebe97af4dbb5c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Mon, 9 Oct 2023 18:15:17 +0300 Subject: [PATCH] Add `mlockall` and `munlockall` --- src/backend/libc/mm/syscalls.rs | 42 +++++++++++++++++ src/backend/libc/mm/types.rs | 27 +++++++++++ src/backend/linux_raw/mm/syscalls.rs | 45 +++++++++++++++++- src/backend/linux_raw/mm/types.rs | 36 ++++++++++++++ src/mm/mmap.rs | 70 ++++++++++++++++++++++++++++ 5 files changed, 218 insertions(+), 2 deletions(-) diff --git a/src/backend/libc/mm/syscalls.rs b/src/backend/libc/mm/syscalls.rs index 4b23a58b7..468760408 100644 --- a/src/backend/libc/mm/syscalls.rs +++ b/src/backend/libc/mm/syscalls.rs @@ -2,6 +2,15 @@ #[cfg(not(target_os = "redox"))] use super::types::Advice; +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + target_os = "illumos", +))] +use super::types::MlockFlags; #[cfg(any(target_os = "emscripten", target_os = "linux"))] use super::types::MremapFlags; use super::types::{MapFlags, MprotectFlags, MsyncFlags, ProtFlags}; @@ -220,3 +229,36 @@ pub(crate) unsafe fn userfaultfd(flags: UserfaultfdFlags) -> io::Result } ret_owned_fd(userfaultfd(bitflags_bits!(flags))) } + +/// Locks all pages mapped into the address space of the calling process. +/// +/// This includes the pages of the code, data and stack segment, as well as shared libraries, +/// user space kernel data, shared memory, and memory-mapped files. All mapped pages are +/// guaranteed to be resident in RAM when the call returns successfully; +/// the pages are guaranteed to stay in RAM until later unlocked. +#[inline] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + target_os = "illumos", +))] +pub(crate) fn mlockall(flags: MlockFlags) -> io::Result<()> { + unsafe { ret(c::mlockall(bitflags_bits!(flags))) } +} + +/// Unlocks all pages mapped into the address space of the calling process. +#[inline] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + target_os = "illumos", +))] +pub(crate) fn munlockall() -> io::Result<()> { + unsafe { ret(c::munlockall()) } +} diff --git a/src/backend/libc/mm/types.rs b/src/backend/libc/mm/types.rs index f0b4ad593..2f0cd9c43 100644 --- a/src/backend/libc/mm/types.rs +++ b/src/backend/libc/mm/types.rs @@ -442,3 +442,30 @@ bitflags! { const _ = !0; } } + +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + target_os = "illumos", +))] +bitflags! { + /// `MCL_*` flags for use with [`mlockall`]. + /// + /// [`mlockall`]: crate::mm::mlockall + pub struct MlockallFlags: i32 { + // libc doesn't define `MCL_ONFAULT` yet. + // const ONFAULT = libc::MCL_ONFAULT; + /// Lock all pages which will become mapped into the address + /// space of the process in the future. These could be, for + /// instance, new pages required by a growing heap and stack + /// as well as new memory-mapped files or shared memory + /// regions. + const FUTURE = libc::MCL_FUTURE; + /// Lock all pages which are currently mapped into the address + /// space of the process. + const CURRENT = libc::MCL_CURRENT; + } +} diff --git a/src/backend/linux_raw/mm/syscalls.rs b/src/backend/linux_raw/mm/syscalls.rs index b51f826a9..2fac874de 100644 --- a/src/backend/linux_raw/mm/syscalls.rs +++ b/src/backend/linux_raw/mm/syscalls.rs @@ -6,9 +6,17 @@ #![allow(unsafe_code)] #![allow(clippy::undocumented_unsafe_blocks)] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + target_os = "illumos", +))] +use super::types::MlockFlags; use super::types::{ - Advice, MapFlags, MlockFlags, MprotectFlags, MremapFlags, MsyncFlags, ProtFlags, - UserfaultfdFlags, + Advice, MapFlags, MprotectFlags, MremapFlags, MsyncFlags, ProtFlags, UserfaultfdFlags, }; use crate::backend::c; #[cfg(target_pointer_width = "64")] @@ -210,3 +218,36 @@ pub(crate) unsafe fn munlock(addr: *mut c::c_void, length: usize) -> io::Result< pub(crate) unsafe fn userfaultfd(flags: UserfaultfdFlags) -> io::Result { ret_owned_fd(syscall_readonly!(__NR_userfaultfd, flags)) } + +/// Locks all pages mapped into the address space of the calling process. +/// +/// This includes the pages of the code, data and stack segment, as well as shared libraries, +/// user space kernel data, shared memory, and memory-mapped files. All mapped pages are +/// guaranteed to be resident in RAM when the call returns successfully; +/// the pages are guaranteed to stay in RAM until later unlocked. +#[inline] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + target_os = "illumos", +))] +pub(crate) fn mlockall(flags: MlockFlags) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_mlockall, flags)) } +} + +/// Unlocks all pages mapped into the address space of the calling process. +#[inline] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + target_os = "illumos", +))] +pub(crate) fn munlockall() -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_munlockall)) } +} diff --git a/src/backend/linux_raw/mm/types.rs b/src/backend/linux_raw/mm/types.rs index 0dfb41050..88d55aec3 100644 --- a/src/backend/linux_raw/mm/types.rs +++ b/src/backend/linux_raw/mm/types.rs @@ -262,3 +262,39 @@ bitflags! { const _ = !0; } } + +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + target_os = "illumos", +))] +bitflags! { + /// `MCL_*` flags for use with [`mlockall`]. + /// + /// [`mlockall`]: crate::mm::mlockall + pub struct MlockallFlags: u32 { + /// Used together with `MCL_CURRENT`, `MCL_FUTURE`, or both. Mark + /// all current (with `MCL_CURRENT`) or future (with `MCL_FUTURE`) + /// mappings to lock pages when they are faulted in. When + /// used with `MCL_CURRENT`, all present pages are locked, but + /// `mlockall()` will not fault in non-present pages. When used + /// with `MCL_FUTURE`, all future mappings will be marked to + /// lock pages when they are faulted in, but they will not be + /// populated by the lock when the mapping is created. + /// `MCL_ONFAULT` must be used with either `MCL_CURRENT` or + /// `MCL_FUTURE` or both. + const ONFAULT = linux_raw_sys::general::MCL_ONFAULT; + /// Lock all pages which will become mapped into the address + /// space of the process in the future. These could be, for + /// instance, new pages required by a growing heap and stack + /// as well as new memory-mapped files or shared memory + /// regions. + const FUTURE = linux_raw_sys::general::MCL_FUTURE; + /// Lock all pages which are currently mapped into the address + /// space of the process. + const CURRENT = linux_raw_sys::general::MCL_CURRENT; + } +} diff --git a/src/mm/mmap.rs b/src/mm/mmap.rs index f68a02b72..5b1d6f9a2 100644 --- a/src/mm/mmap.rs +++ b/src/mm/mmap.rs @@ -340,3 +340,73 @@ pub unsafe fn mlock_with(ptr: *mut c_void, len: usize, flags: MlockFlags) -> io: pub unsafe fn munlock(ptr: *mut c_void, len: usize) -> io::Result<()> { backend::mm::syscalls::munlock(ptr, len) } + +/// Locks all pages mapped into the address space of the calling process. +/// +/// This includes the pages of the code, data and stack segment, as well as shared libraries, +/// user space kernel data, shared memory, and memory-mapped files. All mapped pages are +/// guaranteed to be resident in RAM when the call returns successfully; +/// the pages are guaranteed to stay in RAM until later unlocked. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [FreeBSD] +/// - [NetBSD] +/// - [OpenBSD] +/// - [DragonFly BSD] +/// - [illumos] +/// - [glibc] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/mlockall.html +/// [Linux]: https://man7.org/linux/man-pages/man2/mlockall.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=mlockall&sektion=2 +/// [NetBSD]: https://man.netbsd.org/mlockall.2 +/// [OpenBSD]: https://man.openbsd.org/mlockall.2 +/// [DragonFly BSD]: https://man.dragonflybsd.org/?command=mlockall§ion=2 +/// [illumos]: https://illumos.org/man/3C/mlockall +/// [glibc]: https://www.gnu.org/software/libc/manual/html_node/Page-Lock-Functions.html#index-mlockall +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + target_os = "illumos", +))] +pub fn mlockall(flags: MlockFlags) -> io::Result<()> { + backend::mm::syscalls::mlockall(flags) +} + +/// Unlocks all pages mapped into the address space of the calling process. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [FreeBSD] +/// - [NetBSD] +/// - [OpenBSD] +/// - [DragonFly BSD] +/// - [illumos] +/// - [glibc] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/munlockall.html +/// [Linux]: https://man7.org/linux/man-pages/man2/munlockall.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=munlockall&sektion=2 +/// [NetBSD]: https://man.netbsd.org/munlockall.2 +/// [OpenBSD]: https://man.openbsd.org/munlockall.2 +/// [DragonFly BSD]: https://man.dragonflybsd.org/?command=munlockall§ion=2 +/// [illumos]: https://illumos.org/man/3C/munlockall +/// [glibc]: https://www.gnu.org/software/libc/manual/html_node/Page-Lock-Functions.html#index-munlockall +#[inline] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + target_os = "illumos", +))] +pub fn munlockall() -> io::Result<()> { + backend::mm::syscalls::munlockall() +}