byteor_transport_shm/
mapping.rs

1//! File-backed mapping helpers.
2
3use std::path::Path;
4
5use memmap2::{MmapMut, MmapOptions};
6
7use crate::util::{ensure_parent_dir, hugepage_size_bytes, is_hugetlbfs};
8use crate::{Error, OpenOptions};
9
10/// Describes the OS-facing properties of a mapped backing region.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct MappingInfo {
13    /// Total mapped bytes for the region.
14    pub mapped_bytes: usize,
15    /// Whether the mapping was prefaulted by touching each mapped page after `mmap`.
16    pub prefaulted: bool,
17    /// Whether the backing path resides on `hugetlbfs`.
18    pub hugetlbfs_backing: bool,
19    /// Hugepage size used by the backing filesystem when known.
20    pub backing_hugepage_bytes: Option<usize>,
21}
22
23pub(crate) struct MappedRegion {
24    pub(crate) mmap: MmapMut,
25    pub(crate) info: MappingInfo,
26}
27
28pub(crate) fn open_file_backed(
29    path: &Path,
30    bytes: usize,
31    options: &OpenOptions,
32) -> Result<MappedRegion, Error> {
33    use std::fs::OpenOptions;
34
35    ensure_parent_dir(path).map_err(|e| Error::IoAt {
36        op: "create_dir_all",
37        path: path.to_path_buf(),
38        source: e,
39    })?;
40
41    let mut opts = OpenOptions::new();
42    opts.read(true).write(true).create(true).truncate(false);
43
44    #[cfg(unix)]
45    {
46        use std::os::unix::fs::OpenOptionsExt;
47        opts.mode(0o600);
48    }
49
50    let file = opts.open(path).map_err(|e| Error::IoAt {
51        op: "open",
52        path: path.to_path_buf(),
53        source: e,
54    })?;
55
56    let meta = file.metadata().map_err(|e| Error::IoAt {
57        op: "metadata",
58        path: path.to_path_buf(),
59        source: e,
60    })?;
61    let mut len = meta.len() as usize;
62
63    let hugetlbfs_backing = is_hugetlbfs(path);
64
65    if len == 0 {
66        #[cfg(unix)]
67        {
68            use std::os::unix::fs::PermissionsExt;
69            let _ = file.set_permissions(std::fs::Permissions::from_mode(0o600));
70        }
71
72        let mut target = bytes;
73        if hugetlbfs_backing {
74            let hp = hugepage_size_bytes();
75            if hp > 0 {
76                target = target.div_ceil(hp).saturating_mul(hp);
77            }
78        }
79
80        file.set_len(target as u64).map_err(|e| Error::IoAt {
81            op: "set_len",
82            path: path.to_path_buf(),
83            source: e,
84        })?;
85        len = target;
86    }
87
88    if len < bytes {
89        return Err(Error::RegionTooSmall {
90            needed: bytes,
91            found: len,
92        });
93    }
94
95    // Map the whole file length so all participants observe identical mapping bytes.
96    let mmap = unsafe {
97        MmapOptions::new()
98            .len(len)
99            .map_mut(&file)
100            .map_err(|e| Error::IoAt {
101                op: "mmap",
102                path: path.to_path_buf(),
103                source: e,
104            })?
105    };
106
107    if options.prefault {
108        prefault_mapping(&mmap);
109    }
110
111    let info = mapping_info_for_path(len, options.prefault, hugetlbfs_backing);
112
113    Ok(MappedRegion { mmap, info })
114}
115
116pub(crate) fn mapping_info_for_path(
117    mapped_bytes: usize,
118    prefaulted: bool,
119    hugetlbfs_backing: bool,
120) -> MappingInfo {
121    MappingInfo {
122        mapped_bytes,
123        prefaulted,
124        hugetlbfs_backing,
125        backing_hugepage_bytes: hugetlbfs_backing.then(hugepage_size_bytes),
126    }
127}
128
129fn prefault_mapping(mmap: &MmapMut) {
130    let len = mmap.len();
131    if len == 0 {
132        return;
133    }
134
135    let page_size = page_size_bytes();
136    let base = mmap.as_ptr();
137    let mut touched = 0u8;
138
139    let mut offset = 0usize;
140    while offset < len {
141        touched ^= unsafe { std::ptr::read_volatile(base.add(offset)) };
142        offset = offset.saturating_add(page_size);
143    }
144
145    let last = len - 1;
146    if !last.is_multiple_of(page_size) {
147        touched ^= unsafe { std::ptr::read_volatile(base.add(last)) };
148    }
149
150    std::hint::black_box(touched);
151}
152
153fn page_size_bytes() -> usize {
154    #[cfg(target_os = "linux")]
155    {
156        let rc = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
157        if rc > 0 {
158            return (rc as usize).max(4096);
159        }
160    }
161
162    4096
163}