1#![warn(missing_docs)]
15
16use std::ptr::null_mut;
17
18use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
19use itertools::max;
20use log::{debug, log_enabled, warn};
21use swage_core::allocator::ConsecAllocator;
22use swage_core::memory::{
23 ConsecBlocks, GetConsecPfns, PfnResolver, TimerError, construct_memory_tuple_timer,
24};
25use swage_core::util::Size::MB;
26use swage_core::util::{NamedProgress, Size};
27use swage_core::{memory::Memory, util::PAGE_SIZE};
28use thiserror::Error;
29
30pub struct THP {
32 conflict_threshold: u64,
33 progress: Option<MultiProgress>,
34}
35
36impl THP {
37 pub fn new(conflict_threshold: u64, progress: Option<MultiProgress>) -> Self {
39 THP {
40 conflict_threshold,
41 progress,
42 }
43 }
44}
45
46const ALIGN_SIZE: Size = MB(2);
47
48impl THP {
49 fn allocate_2m_aligned(size: Size) -> Result<Memory, std::io::Error> {
51 let aligned = unsafe {
52 libc::mmap(
53 null_mut(),
54 size.bytes(),
55 libc::PROT_READ | libc::PROT_WRITE,
56 libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
57 -1,
58 0,
59 )
60 };
61 if aligned == libc::MAP_FAILED {
62 return Err(std::io::Error::last_os_error());
63 }
64 unsafe { libc::memset(aligned, 0, size.bytes()) };
65 if unsafe { libc::madvise(aligned, size.bytes(), libc::MADV_COLLAPSE) } != 0 {
66 return Err(std::io::Error::last_os_error());
67 }
68 unsafe { libc::mlock(aligned, PAGE_SIZE) };
69 if log_enabled!(log::Level::Debug)
70 && let Ok(consecs) = (aligned, size.bytes()).consec_pfns()
71 {
72 debug!("Aligned PFNs: {:?}", consecs);
73 }
74 assert_eq!(aligned as usize & (ALIGN_SIZE.bytes() - 1), 0);
75 assert_eq!(
76 aligned.pfn().unwrap_or_default().as_usize() & (ALIGN_SIZE.bytes() - 1),
77 0
78 );
79 Ok(Memory::new(aligned as *mut u8, size.bytes()))
80 }
81}
82
83#[derive(Debug, Error)]
85#[allow(missing_docs)]
86pub enum Error {
87 #[error(transparent)]
88 IoError(#[from] std::io::Error),
89 #[error(transparent)]
90 TimerError(#[from] TimerError),
91 #[error("Size must be a multiple of {0}")]
92 SizeError(Size),
93}
94
95impl ConsecAllocator for THP {
96 type Error = Error;
97
98 fn block_size(&self) -> swage_core::util::Size {
99 Size::GB(1)
100 }
101
102 fn alloc_consec_blocks(
103 &mut self,
104 size: swage_core::util::Size,
105 ) -> Result<swage_core::memory::ConsecBlocks, Self::Error> {
106 if size.bytes() == 0 || !size.bytes().is_multiple_of(ALIGN_SIZE.bytes()) {
107 return Err(Error::SizeError(ALIGN_SIZE));
108 }
109 let mut blocks: Vec<Memory> = vec![];
110 let required_blocks =
111 max([size.bytes() / self.block_size().bytes(), 1]).expect("empty iter");
112 let timer = construct_memory_tuple_timer()?;
113 let p = self.progress.as_ref().map(|p| {
114 p.add(
115 ProgressBar::new(required_blocks as u64)
116 .with_style(ProgressStyle::named_bar("Allocating blocks")),
117 )
118 });
119 let mut garbage = vec![];
120 while blocks.len() < required_blocks {
121 let block = Self::allocate_2m_aligned(size)?;
122
123 if let Some(last_block) = blocks.last() {
125 let timing = unsafe {
126 timer.time_subsequent_access_from_ram(block.ptr, last_block.ptr, 10000)
127 };
128 let same_bank = timing >= self.conflict_threshold;
129 if !same_bank {
130 warn!(
131 "Bank check failed: {} < {} for blocks {:?} and {:?}",
132 timing, self.conflict_threshold, block, last_block
133 );
134 block.log_pfns(log::Level::Warn);
135 last_block.log_pfns(log::Level::Warn);
136 garbage.push(block);
137 continue;
138 }
139 }
140 if let Some(p) = &p {
141 p.inc(1);
142 }
143 blocks.push(block);
144 }
145 for block in garbage {
146 block.dealloc();
147 }
148 Ok(ConsecBlocks::new(blocks))
149 }
150}