1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2022 The ChromiumOS Authors 2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be 3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file. 4*bb4ee6a4SAndroid Build Coastguard Worker 5*bb4ee6a4SAndroid Build Coastguard Worker #![deny(missing_docs)] 6*bb4ee6a4SAndroid Build Coastguard Worker 7*bb4ee6a4SAndroid Build Coastguard Worker use std::fs::File; 8*bb4ee6a4SAndroid Build Coastguard Worker use std::ops::Range; 9*bb4ee6a4SAndroid Build Coastguard Worker use std::os::unix::fs::FileExt; 10*bb4ee6a4SAndroid Build Coastguard Worker 11*bb4ee6a4SAndroid Build Coastguard Worker use base::error; 12*bb4ee6a4SAndroid Build Coastguard Worker use base::linux::MemoryMappingUnix; 13*bb4ee6a4SAndroid Build Coastguard Worker use base::MemoryMapping; 14*bb4ee6a4SAndroid Build Coastguard Worker use base::MemoryMappingBuilder; 15*bb4ee6a4SAndroid Build Coastguard Worker use base::MmapError; 16*bb4ee6a4SAndroid Build Coastguard Worker use base::Protection; 17*bb4ee6a4SAndroid Build Coastguard Worker use base::VolatileMemory; 18*bb4ee6a4SAndroid Build Coastguard Worker use base::VolatileMemoryError; 19*bb4ee6a4SAndroid Build Coastguard Worker use base::VolatileSlice; 20*bb4ee6a4SAndroid Build Coastguard Worker use thiserror::Error as ThisError; 21*bb4ee6a4SAndroid Build Coastguard Worker 22*bb4ee6a4SAndroid Build Coastguard Worker use crate::pagesize::bytes_to_pages; 23*bb4ee6a4SAndroid Build Coastguard Worker use crate::pagesize::is_page_aligned; 24*bb4ee6a4SAndroid Build Coastguard Worker use crate::pagesize::pages_to_bytes; 25*bb4ee6a4SAndroid Build Coastguard Worker 26*bb4ee6a4SAndroid Build Coastguard Worker pub type Result<T> = std::result::Result<T, Error>; 27*bb4ee6a4SAndroid Build Coastguard Worker 28*bb4ee6a4SAndroid Build Coastguard Worker // On 4KB page size system, guest memory must be less than 8 TiB which is reasonable assumption. 29*bb4ee6a4SAndroid Build Coastguard Worker const MAX_PAGE_IDX: usize = (1 << 31) - 2; 30*bb4ee6a4SAndroid Build Coastguard Worker 31*bb4ee6a4SAndroid Build Coastguard Worker #[derive(ThisError, Debug)] 32*bb4ee6a4SAndroid Build Coastguard Worker pub enum Error { 33*bb4ee6a4SAndroid Build Coastguard Worker #[error("failed to io: {0}")] 34*bb4ee6a4SAndroid Build Coastguard Worker Io(#[from] std::io::Error), 35*bb4ee6a4SAndroid Build Coastguard Worker #[error("failed to mmap operation ({0}): {1}")] 36*bb4ee6a4SAndroid Build Coastguard Worker Mmap(&'static str, MmapError), 37*bb4ee6a4SAndroid Build Coastguard Worker #[error("failed to volatile memory operation: {0}")] 38*bb4ee6a4SAndroid Build Coastguard Worker VolatileMemory(#[from] VolatileMemoryError), 39*bb4ee6a4SAndroid Build Coastguard Worker #[error("index is out of range")] 40*bb4ee6a4SAndroid Build Coastguard Worker OutOfRange, 41*bb4ee6a4SAndroid Build Coastguard Worker #[error("data size is invalid")] 42*bb4ee6a4SAndroid Build Coastguard Worker InvalidSize, 43*bb4ee6a4SAndroid Build Coastguard Worker #[error("index is invalid")] 44*bb4ee6a4SAndroid Build Coastguard Worker InvalidIndex, 45*bb4ee6a4SAndroid Build Coastguard Worker } 46*bb4ee6a4SAndroid Build Coastguard Worker 47*bb4ee6a4SAndroid Build Coastguard Worker /// u32 to pack the state of a page on the file. 48*bb4ee6a4SAndroid Build Coastguard Worker /// 49*bb4ee6a4SAndroid Build Coastguard Worker /// * MSB: Whether the page on file is freed. (1: freed, 2: allocated) 50*bb4ee6a4SAndroid Build Coastguard Worker /// * lower 31 bits: 51*bb4ee6a4SAndroid Build Coastguard Worker /// * The corresponding page index if the file page is allocated. 52*bb4ee6a4SAndroid Build Coastguard Worker /// * The file page index + 1 of next freed file page if the file page is freed. Zero means it is 53*bb4ee6a4SAndroid Build Coastguard Worker /// the last page in the free list. 54*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug)] 55*bb4ee6a4SAndroid Build Coastguard Worker struct FilePageState(u32); 56*bb4ee6a4SAndroid Build Coastguard Worker 57*bb4ee6a4SAndroid Build Coastguard Worker impl FilePageState { 58*bb4ee6a4SAndroid Build Coastguard Worker const FREED_BIT_MASK: u32 = 1 << 31; 59*bb4ee6a4SAndroid Build Coastguard Worker freed_state(first_freed_page: Option<usize>) -> Self60*bb4ee6a4SAndroid Build Coastguard Worker fn freed_state(first_freed_page: Option<usize>) -> Self { 61*bb4ee6a4SAndroid Build Coastguard Worker Self( 62*bb4ee6a4SAndroid Build Coastguard Worker Self::FREED_BIT_MASK 63*bb4ee6a4SAndroid Build Coastguard Worker | first_freed_page 64*bb4ee6a4SAndroid Build Coastguard Worker .map(|idx_file| idx_file as u32 + 1) 65*bb4ee6a4SAndroid Build Coastguard Worker .unwrap_or(0), 66*bb4ee6a4SAndroid Build Coastguard Worker ) 67*bb4ee6a4SAndroid Build Coastguard Worker } 68*bb4ee6a4SAndroid Build Coastguard Worker allocated_state(idx_page: usize) -> Option<Self>69*bb4ee6a4SAndroid Build Coastguard Worker fn allocated_state(idx_page: usize) -> Option<Self> { 70*bb4ee6a4SAndroid Build Coastguard Worker if idx_page <= MAX_PAGE_IDX { 71*bb4ee6a4SAndroid Build Coastguard Worker Some(Self(idx_page as u32)) 72*bb4ee6a4SAndroid Build Coastguard Worker } else { 73*bb4ee6a4SAndroid Build Coastguard Worker // idx_page is invalid. 74*bb4ee6a4SAndroid Build Coastguard Worker None 75*bb4ee6a4SAndroid Build Coastguard Worker } 76*bb4ee6a4SAndroid Build Coastguard Worker } 77*bb4ee6a4SAndroid Build Coastguard Worker is_freed(&self) -> bool78*bb4ee6a4SAndroid Build Coastguard Worker fn is_freed(&self) -> bool { 79*bb4ee6a4SAndroid Build Coastguard Worker self.0 & Self::FREED_BIT_MASK != 0 80*bb4ee6a4SAndroid Build Coastguard Worker } 81*bb4ee6a4SAndroid Build Coastguard Worker 82*bb4ee6a4SAndroid Build Coastguard Worker /// This is valid only if the page is freed. next_file_freed_idx(&self) -> Option<Option<usize>>83*bb4ee6a4SAndroid Build Coastguard Worker fn next_file_freed_idx(&self) -> Option<Option<usize>> { 84*bb4ee6a4SAndroid Build Coastguard Worker if self.is_freed() { 85*bb4ee6a4SAndroid Build Coastguard Worker let next_idx_file = !Self::FREED_BIT_MASK & self.0; 86*bb4ee6a4SAndroid Build Coastguard Worker if next_idx_file == 0 { 87*bb4ee6a4SAndroid Build Coastguard Worker Some(None) 88*bb4ee6a4SAndroid Build Coastguard Worker } else { 89*bb4ee6a4SAndroid Build Coastguard Worker Some(Some(next_idx_file as usize - 1)) 90*bb4ee6a4SAndroid Build Coastguard Worker } 91*bb4ee6a4SAndroid Build Coastguard Worker } else { 92*bb4ee6a4SAndroid Build Coastguard Worker None 93*bb4ee6a4SAndroid Build Coastguard Worker } 94*bb4ee6a4SAndroid Build Coastguard Worker } 95*bb4ee6a4SAndroid Build Coastguard Worker 96*bb4ee6a4SAndroid Build Coastguard Worker /// This is valid only if the page is allocated. idx_page(&self) -> Option<usize>97*bb4ee6a4SAndroid Build Coastguard Worker fn idx_page(&self) -> Option<usize> { 98*bb4ee6a4SAndroid Build Coastguard Worker if self.is_freed() { 99*bb4ee6a4SAndroid Build Coastguard Worker // The file page is freed. 100*bb4ee6a4SAndroid Build Coastguard Worker None 101*bb4ee6a4SAndroid Build Coastguard Worker } else { 102*bb4ee6a4SAndroid Build Coastguard Worker Some(self.0 as usize) 103*bb4ee6a4SAndroid Build Coastguard Worker } 104*bb4ee6a4SAndroid Build Coastguard Worker } 105*bb4ee6a4SAndroid Build Coastguard Worker } 106*bb4ee6a4SAndroid Build Coastguard Worker 107*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug)] 108*bb4ee6a4SAndroid Build Coastguard Worker struct FilePageStates { 109*bb4ee6a4SAndroid Build Coastguard Worker /// Freed pages on the swap file are managed in a free list. `first_freed_idx_file` points to 110*bb4ee6a4SAndroid Build Coastguard Worker /// the first page index in the list. 111*bb4ee6a4SAndroid Build Coastguard Worker first_idx_file_freed: Option<usize>, 112*bb4ee6a4SAndroid Build Coastguard Worker states: Vec<FilePageState>, 113*bb4ee6a4SAndroid Build Coastguard Worker } 114*bb4ee6a4SAndroid Build Coastguard Worker 115*bb4ee6a4SAndroid Build Coastguard Worker impl FilePageStates { new(capacity: usize) -> Self116*bb4ee6a4SAndroid Build Coastguard Worker fn new(capacity: usize) -> Self { 117*bb4ee6a4SAndroid Build Coastguard Worker FilePageStates { 118*bb4ee6a4SAndroid Build Coastguard Worker first_idx_file_freed: None, 119*bb4ee6a4SAndroid Build Coastguard Worker states: Vec::with_capacity(capacity), 120*bb4ee6a4SAndroid Build Coastguard Worker } 121*bb4ee6a4SAndroid Build Coastguard Worker } 122*bb4ee6a4SAndroid Build Coastguard Worker len(&self) -> usize123*bb4ee6a4SAndroid Build Coastguard Worker fn len(&self) -> usize { 124*bb4ee6a4SAndroid Build Coastguard Worker self.states.len() 125*bb4ee6a4SAndroid Build Coastguard Worker } 126*bb4ee6a4SAndroid Build Coastguard Worker 127*bb4ee6a4SAndroid Build Coastguard Worker /// Free a page on swap file. free(&mut self, idx_file: usize)128*bb4ee6a4SAndroid Build Coastguard Worker fn free(&mut self, idx_file: usize) { 129*bb4ee6a4SAndroid Build Coastguard Worker self.states[idx_file] = FilePageState::freed_state(self.first_idx_file_freed); 130*bb4ee6a4SAndroid Build Coastguard Worker self.first_idx_file_freed = Some(idx_file); 131*bb4ee6a4SAndroid Build Coastguard Worker } 132*bb4ee6a4SAndroid Build Coastguard Worker 133*bb4ee6a4SAndroid Build Coastguard Worker /// Allocates a file page on the swap file. 134*bb4ee6a4SAndroid Build Coastguard Worker /// 135*bb4ee6a4SAndroid Build Coastguard Worker /// This returns the index of the allocated file page. 136*bb4ee6a4SAndroid Build Coastguard Worker /// 137*bb4ee6a4SAndroid Build Coastguard Worker /// This reuses freed file pages first. If the free list is empty, this allocates new pages in 138*bb4ee6a4SAndroid Build Coastguard Worker /// the file. allocate(&mut self, idx_page: usize) -> usize139*bb4ee6a4SAndroid Build Coastguard Worker fn allocate(&mut self, idx_page: usize) -> usize { 140*bb4ee6a4SAndroid Build Coastguard Worker if let Some(idx_file_freed) = self.first_idx_file_freed { 141*bb4ee6a4SAndroid Build Coastguard Worker // TODO(kawasin): Collect consecutive freed pages in the free list to reduce number of 142*bb4ee6a4SAndroid Build Coastguard Worker // writes. 143*bb4ee6a4SAndroid Build Coastguard Worker let Some(next_idx_file_freed) = self.states[idx_file_freed].next_file_freed_idx() 144*bb4ee6a4SAndroid Build Coastguard Worker else { 145*bb4ee6a4SAndroid Build Coastguard Worker unreachable!("pages in free list must be freed pages") 146*bb4ee6a4SAndroid Build Coastguard Worker }; 147*bb4ee6a4SAndroid Build Coastguard Worker let Some(state) = FilePageState::allocated_state(idx_page) else { 148*bb4ee6a4SAndroid Build Coastguard Worker unreachable!("idx_page must be less than MAX_PAGE_IDX"); 149*bb4ee6a4SAndroid Build Coastguard Worker }; 150*bb4ee6a4SAndroid Build Coastguard Worker self.states[idx_file_freed] = state; 151*bb4ee6a4SAndroid Build Coastguard Worker self.first_idx_file_freed = next_idx_file_freed; 152*bb4ee6a4SAndroid Build Coastguard Worker 153*bb4ee6a4SAndroid Build Coastguard Worker idx_file_freed 154*bb4ee6a4SAndroid Build Coastguard Worker } else { 155*bb4ee6a4SAndroid Build Coastguard Worker // The free list is empty. Allocate new pages. 156*bb4ee6a4SAndroid Build Coastguard Worker let head_idx_file = self.states.len(); 157*bb4ee6a4SAndroid Build Coastguard Worker let Some(state) = FilePageState::allocated_state(idx_page) else { 158*bb4ee6a4SAndroid Build Coastguard Worker unreachable!("idx must be less than MAX_PAGE_IDX"); 159*bb4ee6a4SAndroid Build Coastguard Worker }; 160*bb4ee6a4SAndroid Build Coastguard Worker self.states.push(state); 161*bb4ee6a4SAndroid Build Coastguard Worker head_idx_file 162*bb4ee6a4SAndroid Build Coastguard Worker } 163*bb4ee6a4SAndroid Build Coastguard Worker } 164*bb4ee6a4SAndroid Build Coastguard Worker 165*bb4ee6a4SAndroid Build Coastguard Worker /// Find the index range of file pages that are all present. 166*bb4ee6a4SAndroid Build Coastguard Worker /// 167*bb4ee6a4SAndroid Build Coastguard Worker /// This returns the pair of range of file page indexes and the index of the corresponding first 168*bb4ee6a4SAndroid Build Coastguard Worker /// page. 169*bb4ee6a4SAndroid Build Coastguard Worker /// 170*bb4ee6a4SAndroid Build Coastguard Worker /// Returns `None` if no pages after `idx_file` are present. 171*bb4ee6a4SAndroid Build Coastguard Worker /// 172*bb4ee6a4SAndroid Build Coastguard Worker /// # Arguments 173*bb4ee6a4SAndroid Build Coastguard Worker /// 174*bb4ee6a4SAndroid Build Coastguard Worker /// * `idx_file` - The first index to start searching from. 175*bb4ee6a4SAndroid Build Coastguard Worker /// * `page_states` - The page states 176*bb4ee6a4SAndroid Build Coastguard Worker /// * `max_pages` - The maximum number of pages to search. 177*bb4ee6a4SAndroid Build Coastguard Worker /// * `consecutive` - If true, the pages must have consecutive idx_page values. find_present_pages_range( &self, idx_file: usize, page_states: &[PageState], max_pages: usize, consecutive: bool, ) -> Option<(Range<usize>, usize)>178*bb4ee6a4SAndroid Build Coastguard Worker fn find_present_pages_range( 179*bb4ee6a4SAndroid Build Coastguard Worker &self, 180*bb4ee6a4SAndroid Build Coastguard Worker idx_file: usize, 181*bb4ee6a4SAndroid Build Coastguard Worker page_states: &[PageState], 182*bb4ee6a4SAndroid Build Coastguard Worker max_pages: usize, 183*bb4ee6a4SAndroid Build Coastguard Worker consecutive: bool, 184*bb4ee6a4SAndroid Build Coastguard Worker ) -> Option<(Range<usize>, usize)> { 185*bb4ee6a4SAndroid Build Coastguard Worker let next_head_idx_offset = self.states[idx_file..].iter().position(|state| { 186*bb4ee6a4SAndroid Build Coastguard Worker !state.is_freed() 187*bb4ee6a4SAndroid Build Coastguard Worker && page_states[state 188*bb4ee6a4SAndroid Build Coastguard Worker .idx_page() 189*bb4ee6a4SAndroid Build Coastguard Worker .unwrap_or_else(|| unreachable!("the page is not freed"))] 190*bb4ee6a4SAndroid Build Coastguard Worker .is_present() 191*bb4ee6a4SAndroid Build Coastguard Worker })?; 192*bb4ee6a4SAndroid Build Coastguard Worker let idx_file = idx_file + next_head_idx_offset; 193*bb4ee6a4SAndroid Build Coastguard Worker 194*bb4ee6a4SAndroid Build Coastguard Worker let Some(head_idx_page) = self.states[idx_file].idx_page() else { 195*bb4ee6a4SAndroid Build Coastguard Worker unreachable!("the file page must not be freed"); 196*bb4ee6a4SAndroid Build Coastguard Worker }; 197*bb4ee6a4SAndroid Build Coastguard Worker 198*bb4ee6a4SAndroid Build Coastguard Worker let mut pages = 1; 199*bb4ee6a4SAndroid Build Coastguard Worker 200*bb4ee6a4SAndroid Build Coastguard Worker if max_pages > 1 { 201*bb4ee6a4SAndroid Build Coastguard Worker for state in self.states[idx_file + 1..].iter() { 202*bb4ee6a4SAndroid Build Coastguard Worker if state.is_freed() { 203*bb4ee6a4SAndroid Build Coastguard Worker break; 204*bb4ee6a4SAndroid Build Coastguard Worker } else { 205*bb4ee6a4SAndroid Build Coastguard Worker let Some(idx_page) = state.idx_page() else { 206*bb4ee6a4SAndroid Build Coastguard Worker unreachable!("allocated page must have idx_page"); 207*bb4ee6a4SAndroid Build Coastguard Worker }; 208*bb4ee6a4SAndroid Build Coastguard Worker if !page_states[idx_page].is_present() 209*bb4ee6a4SAndroid Build Coastguard Worker || (consecutive && idx_page != head_idx_page + pages) 210*bb4ee6a4SAndroid Build Coastguard Worker { 211*bb4ee6a4SAndroid Build Coastguard Worker break; 212*bb4ee6a4SAndroid Build Coastguard Worker } 213*bb4ee6a4SAndroid Build Coastguard Worker } 214*bb4ee6a4SAndroid Build Coastguard Worker 215*bb4ee6a4SAndroid Build Coastguard Worker pages += 1; 216*bb4ee6a4SAndroid Build Coastguard Worker if pages >= max_pages { 217*bb4ee6a4SAndroid Build Coastguard Worker break; 218*bb4ee6a4SAndroid Build Coastguard Worker } 219*bb4ee6a4SAndroid Build Coastguard Worker } 220*bb4ee6a4SAndroid Build Coastguard Worker } 221*bb4ee6a4SAndroid Build Coastguard Worker 222*bb4ee6a4SAndroid Build Coastguard Worker Some((idx_file..idx_file + pages, head_idx_page)) 223*bb4ee6a4SAndroid Build Coastguard Worker } 224*bb4ee6a4SAndroid Build Coastguard Worker } 225*bb4ee6a4SAndroid Build Coastguard Worker 226*bb4ee6a4SAndroid Build Coastguard Worker /// u32 to pack the state of a guest memory page. 227*bb4ee6a4SAndroid Build Coastguard Worker /// 228*bb4ee6a4SAndroid Build Coastguard Worker /// * If the page is not on the swap file, the value is zero. 229*bb4ee6a4SAndroid Build Coastguard Worker /// * MSB: Whether the page is stale or not. (0: stale, 1: present). 230*bb4ee6a4SAndroid Build Coastguard Worker /// * lower 31 bits: The corresponding file page index + 1. Never be zero. 231*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Clone, Debug)] 232*bb4ee6a4SAndroid Build Coastguard Worker struct PageState(u32); 233*bb4ee6a4SAndroid Build Coastguard Worker 234*bb4ee6a4SAndroid Build Coastguard Worker impl PageState { 235*bb4ee6a4SAndroid Build Coastguard Worker const IDX_FILE_MASK: u32 = (1 << 31) - 1; 236*bb4ee6a4SAndroid Build Coastguard Worker const PRESENT_BIT_MASK: u32 = 1 << 31; 237*bb4ee6a4SAndroid Build Coastguard Worker is_none(&self) -> bool238*bb4ee6a4SAndroid Build Coastguard Worker fn is_none(&self) -> bool { 239*bb4ee6a4SAndroid Build Coastguard Worker self.0 == 0 240*bb4ee6a4SAndroid Build Coastguard Worker } 241*bb4ee6a4SAndroid Build Coastguard Worker idx_file(&self) -> Option<usize>242*bb4ee6a4SAndroid Build Coastguard Worker fn idx_file(&self) -> Option<usize> { 243*bb4ee6a4SAndroid Build Coastguard Worker if self.0 != 0 { 244*bb4ee6a4SAndroid Build Coastguard Worker Some((self.0 & Self::IDX_FILE_MASK) as usize - 1) 245*bb4ee6a4SAndroid Build Coastguard Worker } else { 246*bb4ee6a4SAndroid Build Coastguard Worker None 247*bb4ee6a4SAndroid Build Coastguard Worker } 248*bb4ee6a4SAndroid Build Coastguard Worker } 249*bb4ee6a4SAndroid Build Coastguard Worker is_present(&self) -> bool250*bb4ee6a4SAndroid Build Coastguard Worker fn is_present(&self) -> bool { 251*bb4ee6a4SAndroid Build Coastguard Worker self.0 & Self::PRESENT_BIT_MASK != 0 252*bb4ee6a4SAndroid Build Coastguard Worker } 253*bb4ee6a4SAndroid Build Coastguard Worker update(&mut self, idx_file: usize)254*bb4ee6a4SAndroid Build Coastguard Worker fn update(&mut self, idx_file: usize) { 255*bb4ee6a4SAndroid Build Coastguard Worker self.0 = (idx_file as u32 + 1) | Self::PRESENT_BIT_MASK; 256*bb4ee6a4SAndroid Build Coastguard Worker } 257*bb4ee6a4SAndroid Build Coastguard Worker mark_as_present(&mut self)258*bb4ee6a4SAndroid Build Coastguard Worker fn mark_as_present(&mut self) { 259*bb4ee6a4SAndroid Build Coastguard Worker self.0 |= Self::PRESENT_BIT_MASK; 260*bb4ee6a4SAndroid Build Coastguard Worker } 261*bb4ee6a4SAndroid Build Coastguard Worker clear(&mut self)262*bb4ee6a4SAndroid Build Coastguard Worker fn clear(&mut self) { 263*bb4ee6a4SAndroid Build Coastguard Worker self.0 &= !Self::PRESENT_BIT_MASK; 264*bb4ee6a4SAndroid Build Coastguard Worker } 265*bb4ee6a4SAndroid Build Coastguard Worker free(&mut self)266*bb4ee6a4SAndroid Build Coastguard Worker fn free(&mut self) { 267*bb4ee6a4SAndroid Build Coastguard Worker self.0 = 0; 268*bb4ee6a4SAndroid Build Coastguard Worker } 269*bb4ee6a4SAndroid Build Coastguard Worker } 270*bb4ee6a4SAndroid Build Coastguard Worker 271*bb4ee6a4SAndroid Build Coastguard Worker /// [SwapFile] stores active pages in a memory region. 272*bb4ee6a4SAndroid Build Coastguard Worker /// 273*bb4ee6a4SAndroid Build Coastguard Worker /// This shares the swap file with other regions and creates mmap corresponding range in the file. 274*bb4ee6a4SAndroid Build Coastguard Worker /// 275*bb4ee6a4SAndroid Build Coastguard Worker /// TODO(kawasin): The file structure is straightforward and is not optimized yet. 276*bb4ee6a4SAndroid Build Coastguard Worker /// Each page in the file corresponds to the page in the memory region. 277*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug)] 278*bb4ee6a4SAndroid Build Coastguard Worker pub struct SwapFile<'a> { 279*bb4ee6a4SAndroid Build Coastguard Worker file: &'a File, 280*bb4ee6a4SAndroid Build Coastguard Worker file_mmap: MemoryMapping, 281*bb4ee6a4SAndroid Build Coastguard Worker page_states: Vec<PageState>, 282*bb4ee6a4SAndroid Build Coastguard Worker file_states: FilePageStates, 283*bb4ee6a4SAndroid Build Coastguard Worker // All the data pages before this index are mlock(2)ed. 284*bb4ee6a4SAndroid Build Coastguard Worker cursor_mlock: usize, 285*bb4ee6a4SAndroid Build Coastguard Worker min_possible_present_idx_file: usize, 286*bb4ee6a4SAndroid Build Coastguard Worker } 287*bb4ee6a4SAndroid Build Coastguard Worker 288*bb4ee6a4SAndroid Build Coastguard Worker impl<'a> SwapFile<'a> { 289*bb4ee6a4SAndroid Build Coastguard Worker /// Creates an initialized [SwapFile] for a memory region. 290*bb4ee6a4SAndroid Build Coastguard Worker /// 291*bb4ee6a4SAndroid Build Coastguard Worker /// The all pages are marked as empty at first time. 292*bb4ee6a4SAndroid Build Coastguard Worker /// 293*bb4ee6a4SAndroid Build Coastguard Worker /// # Arguments 294*bb4ee6a4SAndroid Build Coastguard Worker /// 295*bb4ee6a4SAndroid Build Coastguard Worker /// * `file` - The swap file. 296*bb4ee6a4SAndroid Build Coastguard Worker /// * `num_of_pages` - The number of pages in the region. new(file: &'a File, num_of_pages: usize) -> Result<Self>297*bb4ee6a4SAndroid Build Coastguard Worker pub fn new(file: &'a File, num_of_pages: usize) -> Result<Self> { 298*bb4ee6a4SAndroid Build Coastguard Worker if num_of_pages > MAX_PAGE_IDX { 299*bb4ee6a4SAndroid Build Coastguard Worker return Err(Error::InvalidSize); 300*bb4ee6a4SAndroid Build Coastguard Worker } 301*bb4ee6a4SAndroid Build Coastguard Worker let file_mmap = MemoryMappingBuilder::new(pages_to_bytes(num_of_pages)) 302*bb4ee6a4SAndroid Build Coastguard Worker .from_file(file) 303*bb4ee6a4SAndroid Build Coastguard Worker .protection(Protection::read()) 304*bb4ee6a4SAndroid Build Coastguard Worker .build() 305*bb4ee6a4SAndroid Build Coastguard Worker .map_err(|e| Error::Mmap("create", e))?; 306*bb4ee6a4SAndroid Build Coastguard Worker Ok(Self { 307*bb4ee6a4SAndroid Build Coastguard Worker file, 308*bb4ee6a4SAndroid Build Coastguard Worker file_mmap, 309*bb4ee6a4SAndroid Build Coastguard Worker page_states: vec![PageState(0); num_of_pages], 310*bb4ee6a4SAndroid Build Coastguard Worker file_states: FilePageStates::new(num_of_pages), 311*bb4ee6a4SAndroid Build Coastguard Worker cursor_mlock: 0, 312*bb4ee6a4SAndroid Build Coastguard Worker min_possible_present_idx_file: 0, 313*bb4ee6a4SAndroid Build Coastguard Worker }) 314*bb4ee6a4SAndroid Build Coastguard Worker } 315*bb4ee6a4SAndroid Build Coastguard Worker 316*bb4ee6a4SAndroid Build Coastguard Worker /// Returns a content of the page corresponding to the index if it is present. 317*bb4ee6a4SAndroid Build Coastguard Worker /// 318*bb4ee6a4SAndroid Build Coastguard Worker /// Returns [Option::None] if no content in the file. 319*bb4ee6a4SAndroid Build Coastguard Worker /// 320*bb4ee6a4SAndroid Build Coastguard Worker /// Returns [Error::OutOfRange] if the `idx` is out of range. 321*bb4ee6a4SAndroid Build Coastguard Worker /// 322*bb4ee6a4SAndroid Build Coastguard Worker /// # Arguments 323*bb4ee6a4SAndroid Build Coastguard Worker /// 324*bb4ee6a4SAndroid Build Coastguard Worker /// * `idx_page` - the index of the page from the head of the pages. page_content( &self, idx_page: usize, allow_cleared: bool, ) -> Result<Option<VolatileSlice>>325*bb4ee6a4SAndroid Build Coastguard Worker pub fn page_content( 326*bb4ee6a4SAndroid Build Coastguard Worker &self, 327*bb4ee6a4SAndroid Build Coastguard Worker idx_page: usize, 328*bb4ee6a4SAndroid Build Coastguard Worker allow_cleared: bool, 329*bb4ee6a4SAndroid Build Coastguard Worker ) -> Result<Option<VolatileSlice>> { 330*bb4ee6a4SAndroid Build Coastguard Worker let state = self.page_states.get(idx_page).ok_or(Error::OutOfRange)?; 331*bb4ee6a4SAndroid Build Coastguard Worker if !state.is_none() && (allow_cleared || state.is_present()) { 332*bb4ee6a4SAndroid Build Coastguard Worker let Some(idx_file) = state.idx_file() else { 333*bb4ee6a4SAndroid Build Coastguard Worker unreachable!("the page is not none"); 334*bb4ee6a4SAndroid Build Coastguard Worker }; 335*bb4ee6a4SAndroid Build Coastguard Worker return match self 336*bb4ee6a4SAndroid Build Coastguard Worker .file_mmap 337*bb4ee6a4SAndroid Build Coastguard Worker .get_slice(pages_to_bytes(idx_file), pages_to_bytes(1)) 338*bb4ee6a4SAndroid Build Coastguard Worker { 339*bb4ee6a4SAndroid Build Coastguard Worker Ok(slice) => Ok(Some(slice)), 340*bb4ee6a4SAndroid Build Coastguard Worker Err(VolatileMemoryError::OutOfBounds { .. }) => Err(Error::OutOfRange), 341*bb4ee6a4SAndroid Build Coastguard Worker Err(e) => Err(e.into()), 342*bb4ee6a4SAndroid Build Coastguard Worker }; 343*bb4ee6a4SAndroid Build Coastguard Worker } 344*bb4ee6a4SAndroid Build Coastguard Worker Ok(None) 345*bb4ee6a4SAndroid Build Coastguard Worker } 346*bb4ee6a4SAndroid Build Coastguard Worker 347*bb4ee6a4SAndroid Build Coastguard Worker /// Start readahead the swap file into the page cache from the head. 348*bb4ee6a4SAndroid Build Coastguard Worker /// 349*bb4ee6a4SAndroid Build Coastguard Worker /// This also `mlock2(2)` the pages not to be dropped again after populated. This does not block 350*bb4ee6a4SAndroid Build Coastguard Worker /// the caller thread by I/O wait because: 351*bb4ee6a4SAndroid Build Coastguard Worker /// 352*bb4ee6a4SAndroid Build Coastguard Worker /// * `mlock2(2)` is executed with `MLOCK_ONFAULT`. 353*bb4ee6a4SAndroid Build Coastguard Worker /// * `MADV_WILLNEED` is the same as `readahead(2)` which triggers the readahead background. 354*bb4ee6a4SAndroid Build Coastguard Worker /// * However Linux has a bug that `readahead(2)` (and also `MADV_WILLNEED`) may block due to 355*bb4ee6a4SAndroid Build Coastguard Worker /// reading the filesystem metadata. 356*bb4ee6a4SAndroid Build Coastguard Worker /// 357*bb4ee6a4SAndroid Build Coastguard Worker /// This returns the number of consecutive pages which are newly mlock(2)ed. Returning `0` means 358*bb4ee6a4SAndroid Build Coastguard Worker /// that there is no more data to be mlock(2)ed in this file. 359*bb4ee6a4SAndroid Build Coastguard Worker /// 360*bb4ee6a4SAndroid Build Coastguard Worker /// The caller must track the number of pages mlock(2)ed not to mlock(2) more pages than 361*bb4ee6a4SAndroid Build Coastguard Worker /// `RLIMIT_MEMLOCK` if it does not have `CAP_IPC_LOCK`. 362*bb4ee6a4SAndroid Build Coastguard Worker /// 363*bb4ee6a4SAndroid Build Coastguard Worker /// # Arguments 364*bb4ee6a4SAndroid Build Coastguard Worker /// 365*bb4ee6a4SAndroid Build Coastguard Worker /// * `max_pages` - The maximum number of pages to be mlock(2)ed at once. lock_and_async_prefetch(&mut self, max_pages: usize) -> Result<usize>366*bb4ee6a4SAndroid Build Coastguard Worker pub fn lock_and_async_prefetch(&mut self, max_pages: usize) -> Result<usize> { 367*bb4ee6a4SAndroid Build Coastguard Worker if let Some((idx_file_range, _)) = self.file_states.find_present_pages_range( 368*bb4ee6a4SAndroid Build Coastguard Worker self.cursor_mlock, 369*bb4ee6a4SAndroid Build Coastguard Worker &self.page_states, 370*bb4ee6a4SAndroid Build Coastguard Worker max_pages, 371*bb4ee6a4SAndroid Build Coastguard Worker false, 372*bb4ee6a4SAndroid Build Coastguard Worker ) { 373*bb4ee6a4SAndroid Build Coastguard Worker let pages = idx_file_range.end - idx_file_range.start; 374*bb4ee6a4SAndroid Build Coastguard Worker let mem_offset = pages_to_bytes(idx_file_range.start); 375*bb4ee6a4SAndroid Build Coastguard Worker let size_in_bytes = pages_to_bytes(pages); 376*bb4ee6a4SAndroid Build Coastguard Worker self.file_mmap 377*bb4ee6a4SAndroid Build Coastguard Worker .lock_on_fault(mem_offset, size_in_bytes) 378*bb4ee6a4SAndroid Build Coastguard Worker .map_err(|e| Error::Mmap("mlock", e))?; 379*bb4ee6a4SAndroid Build Coastguard Worker self.file_mmap 380*bb4ee6a4SAndroid Build Coastguard Worker .async_prefetch(mem_offset, size_in_bytes) 381*bb4ee6a4SAndroid Build Coastguard Worker .map_err(|e| Error::Mmap("madvise willneed", e))?; 382*bb4ee6a4SAndroid Build Coastguard Worker self.cursor_mlock = idx_file_range.end; 383*bb4ee6a4SAndroid Build Coastguard Worker Ok(pages) 384*bb4ee6a4SAndroid Build Coastguard Worker } else { 385*bb4ee6a4SAndroid Build Coastguard Worker self.cursor_mlock = self.file_states.len(); 386*bb4ee6a4SAndroid Build Coastguard Worker Ok(0) 387*bb4ee6a4SAndroid Build Coastguard Worker } 388*bb4ee6a4SAndroid Build Coastguard Worker } 389*bb4ee6a4SAndroid Build Coastguard Worker 390*bb4ee6a4SAndroid Build Coastguard Worker /// Mark the pages in the file corresponding to the index as cleared. 391*bb4ee6a4SAndroid Build Coastguard Worker /// 392*bb4ee6a4SAndroid Build Coastguard Worker /// The contents on the swap file are preserved and will be reused by 393*bb4ee6a4SAndroid Build Coastguard Worker /// `SwapFile::mark_as_present()` and reduce disk I/O. 394*bb4ee6a4SAndroid Build Coastguard Worker /// 395*bb4ee6a4SAndroid Build Coastguard Worker /// If the pages are mlock(2)ed, unlock them before MADV_DONTNEED. This returns the number of 396*bb4ee6a4SAndroid Build Coastguard Worker /// pages munlock(2)ed. 397*bb4ee6a4SAndroid Build Coastguard Worker /// 398*bb4ee6a4SAndroid Build Coastguard Worker /// # Arguments 399*bb4ee6a4SAndroid Build Coastguard Worker /// 400*bb4ee6a4SAndroid Build Coastguard Worker /// * `idx_page_range` - The indices of consecutive pages to be cleared. All the pages must be 401*bb4ee6a4SAndroid Build Coastguard Worker /// present and consecutive in the compacted file. clear_range(&mut self, idx_page_range: Range<usize>) -> Result<usize>402*bb4ee6a4SAndroid Build Coastguard Worker pub fn clear_range(&mut self, idx_page_range: Range<usize>) -> Result<usize> { 403*bb4ee6a4SAndroid Build Coastguard Worker let idx_file_range = self.convert_idx_page_range_to_idx_file(idx_page_range.clone())?; 404*bb4ee6a4SAndroid Build Coastguard Worker 405*bb4ee6a4SAndroid Build Coastguard Worker for state in &mut self.page_states[idx_page_range] { 406*bb4ee6a4SAndroid Build Coastguard Worker state.clear(); 407*bb4ee6a4SAndroid Build Coastguard Worker } 408*bb4ee6a4SAndroid Build Coastguard Worker 409*bb4ee6a4SAndroid Build Coastguard Worker let offset = pages_to_bytes(idx_file_range.start); 410*bb4ee6a4SAndroid Build Coastguard Worker let munlocked_size = if idx_file_range.start < self.cursor_mlock { 411*bb4ee6a4SAndroid Build Coastguard Worker // idx_page_range is validated at clear_range() and self.cursor_mlock is within the 412*bb4ee6a4SAndroid Build Coastguard Worker // mmap. 413*bb4ee6a4SAndroid Build Coastguard Worker let pages = idx_file_range.end.min(self.cursor_mlock) - idx_file_range.start; 414*bb4ee6a4SAndroid Build Coastguard Worker // munlock(2) first because MADV_DONTNEED fails for mlock(2)ed pages. 415*bb4ee6a4SAndroid Build Coastguard Worker self.file_mmap 416*bb4ee6a4SAndroid Build Coastguard Worker .unlock(offset, pages_to_bytes(pages)) 417*bb4ee6a4SAndroid Build Coastguard Worker .map_err(|e| Error::Mmap("munlock", e))?; 418*bb4ee6a4SAndroid Build Coastguard Worker pages 419*bb4ee6a4SAndroid Build Coastguard Worker } else { 420*bb4ee6a4SAndroid Build Coastguard Worker 0 421*bb4ee6a4SAndroid Build Coastguard Worker }; 422*bb4ee6a4SAndroid Build Coastguard Worker // offset and size are validated at clear_range(). 423*bb4ee6a4SAndroid Build Coastguard Worker let size = pages_to_bytes(idx_file_range.end - idx_file_range.start); 424*bb4ee6a4SAndroid Build Coastguard Worker // The page cache is cleared without writing pages back to file even if they are dirty. 425*bb4ee6a4SAndroid Build Coastguard Worker // The disk contents which may not be the latest are kept for later trim optimization. 426*bb4ee6a4SAndroid Build Coastguard Worker self.file_mmap 427*bb4ee6a4SAndroid Build Coastguard Worker .drop_page_cache(offset, size) 428*bb4ee6a4SAndroid Build Coastguard Worker .map_err(|e| Error::Mmap("madvise dontneed", e))?; 429*bb4ee6a4SAndroid Build Coastguard Worker Ok(munlocked_size) 430*bb4ee6a4SAndroid Build Coastguard Worker } 431*bb4ee6a4SAndroid Build Coastguard Worker 432*bb4ee6a4SAndroid Build Coastguard Worker /// Free the pages corresponding to the given range in the file. 433*bb4ee6a4SAndroid Build Coastguard Worker /// 434*bb4ee6a4SAndroid Build Coastguard Worker /// If the pages are mlock(2)ed, unlock them. This returns the number of pages munlock(2)ed. 435*bb4ee6a4SAndroid Build Coastguard Worker /// 436*bb4ee6a4SAndroid Build Coastguard Worker /// # Arguments 437*bb4ee6a4SAndroid Build Coastguard Worker /// 438*bb4ee6a4SAndroid Build Coastguard Worker /// * `idx_page_range` - The indices of consecutive pages to be freed. This may contains 439*bb4ee6a4SAndroid Build Coastguard Worker /// non-present pages. free_range(&mut self, idx_page_range: Range<usize>) -> Result<usize>440*bb4ee6a4SAndroid Build Coastguard Worker pub fn free_range(&mut self, idx_page_range: Range<usize>) -> Result<usize> { 441*bb4ee6a4SAndroid Build Coastguard Worker if idx_page_range.end > self.page_states.len() { 442*bb4ee6a4SAndroid Build Coastguard Worker return Err(Error::OutOfRange); 443*bb4ee6a4SAndroid Build Coastguard Worker } 444*bb4ee6a4SAndroid Build Coastguard Worker let mut mlocked_pages = 0; 445*bb4ee6a4SAndroid Build Coastguard Worker let mut mlock_range: Option<Range<usize>> = None; 446*bb4ee6a4SAndroid Build Coastguard Worker for state in &mut self.page_states[idx_page_range] { 447*bb4ee6a4SAndroid Build Coastguard Worker if !state.is_none() { 448*bb4ee6a4SAndroid Build Coastguard Worker let Some(idx_file) = state.idx_file() else { 449*bb4ee6a4SAndroid Build Coastguard Worker unreachable!("the page is not none."); 450*bb4ee6a4SAndroid Build Coastguard Worker }; 451*bb4ee6a4SAndroid Build Coastguard Worker self.file_states.free(idx_file); 452*bb4ee6a4SAndroid Build Coastguard Worker 453*bb4ee6a4SAndroid Build Coastguard Worker if idx_file < self.cursor_mlock && state.is_present() { 454*bb4ee6a4SAndroid Build Coastguard Worker mlocked_pages += 1; 455*bb4ee6a4SAndroid Build Coastguard Worker if let Some(range) = mlock_range.as_mut() { 456*bb4ee6a4SAndroid Build Coastguard Worker if idx_file + 1 == range.start { 457*bb4ee6a4SAndroid Build Coastguard Worker range.start = idx_file; 458*bb4ee6a4SAndroid Build Coastguard Worker } else if idx_file == range.end { 459*bb4ee6a4SAndroid Build Coastguard Worker range.end += 1; 460*bb4ee6a4SAndroid Build Coastguard Worker } else { 461*bb4ee6a4SAndroid Build Coastguard Worker self.file_mmap 462*bb4ee6a4SAndroid Build Coastguard Worker .unlock( 463*bb4ee6a4SAndroid Build Coastguard Worker pages_to_bytes(range.start), 464*bb4ee6a4SAndroid Build Coastguard Worker pages_to_bytes(range.end - range.start), 465*bb4ee6a4SAndroid Build Coastguard Worker ) 466*bb4ee6a4SAndroid Build Coastguard Worker .map_err(|e| Error::Mmap("munlock", e))?; 467*bb4ee6a4SAndroid Build Coastguard Worker mlock_range = Some(idx_file..idx_file + 1); 468*bb4ee6a4SAndroid Build Coastguard Worker } 469*bb4ee6a4SAndroid Build Coastguard Worker } else { 470*bb4ee6a4SAndroid Build Coastguard Worker mlock_range = Some(idx_file..idx_file + 1); 471*bb4ee6a4SAndroid Build Coastguard Worker } 472*bb4ee6a4SAndroid Build Coastguard Worker } 473*bb4ee6a4SAndroid Build Coastguard Worker } 474*bb4ee6a4SAndroid Build Coastguard Worker state.free(); 475*bb4ee6a4SAndroid Build Coastguard Worker } 476*bb4ee6a4SAndroid Build Coastguard Worker if let Some(mlock_range) = mlock_range { 477*bb4ee6a4SAndroid Build Coastguard Worker self.file_mmap 478*bb4ee6a4SAndroid Build Coastguard Worker .unlock( 479*bb4ee6a4SAndroid Build Coastguard Worker pages_to_bytes(mlock_range.start), 480*bb4ee6a4SAndroid Build Coastguard Worker pages_to_bytes(mlock_range.end - mlock_range.start), 481*bb4ee6a4SAndroid Build Coastguard Worker ) 482*bb4ee6a4SAndroid Build Coastguard Worker .map_err(|e| Error::Mmap("munlock", e))?; 483*bb4ee6a4SAndroid Build Coastguard Worker } 484*bb4ee6a4SAndroid Build Coastguard Worker 485*bb4ee6a4SAndroid Build Coastguard Worker Ok(mlocked_pages) 486*bb4ee6a4SAndroid Build Coastguard Worker } 487*bb4ee6a4SAndroid Build Coastguard Worker 488*bb4ee6a4SAndroid Build Coastguard Worker /// munlock(2) pages if there are mlock(2)ed pages in the mmap and reset the internal cursor for 489*bb4ee6a4SAndroid Build Coastguard Worker /// mlock(2) tracking. clear_mlock(&mut self) -> Result<()>490*bb4ee6a4SAndroid Build Coastguard Worker pub fn clear_mlock(&mut self) -> Result<()> { 491*bb4ee6a4SAndroid Build Coastguard Worker if self.cursor_mlock > 0 { 492*bb4ee6a4SAndroid Build Coastguard Worker // cursor_mlock is not `0` only when disabling vmm-swap is aborted by overriding 493*bb4ee6a4SAndroid Build Coastguard Worker // vmm-swap enable. munlock(2)ing the whole possible pages is not a problem because this 494*bb4ee6a4SAndroid Build Coastguard Worker // is not a hot path. 495*bb4ee6a4SAndroid Build Coastguard Worker self.file_mmap 496*bb4ee6a4SAndroid Build Coastguard Worker .unlock(0, pages_to_bytes(self.cursor_mlock)) 497*bb4ee6a4SAndroid Build Coastguard Worker .map_err(|e| Error::Mmap("munlock", e))?; 498*bb4ee6a4SAndroid Build Coastguard Worker } 499*bb4ee6a4SAndroid Build Coastguard Worker self.cursor_mlock = 0; 500*bb4ee6a4SAndroid Build Coastguard Worker Ok(()) 501*bb4ee6a4SAndroid Build Coastguard Worker } 502*bb4ee6a4SAndroid Build Coastguard Worker 503*bb4ee6a4SAndroid Build Coastguard Worker /// Mark the page as present on the file. 504*bb4ee6a4SAndroid Build Coastguard Worker /// 505*bb4ee6a4SAndroid Build Coastguard Worker /// The content on the swap file on previous `SwapFile::write_to_file()` is reused. 506*bb4ee6a4SAndroid Build Coastguard Worker /// 507*bb4ee6a4SAndroid Build Coastguard Worker /// # Arguments 508*bb4ee6a4SAndroid Build Coastguard Worker /// 509*bb4ee6a4SAndroid Build Coastguard Worker /// * `idx` - the index of the page from the head of the pages. mark_as_present(&mut self, idx_page: usize) -> Result<()>510*bb4ee6a4SAndroid Build Coastguard Worker pub fn mark_as_present(&mut self, idx_page: usize) -> Result<()> { 511*bb4ee6a4SAndroid Build Coastguard Worker let state = self 512*bb4ee6a4SAndroid Build Coastguard Worker .page_states 513*bb4ee6a4SAndroid Build Coastguard Worker .get_mut(idx_page) 514*bb4ee6a4SAndroid Build Coastguard Worker .ok_or(Error::OutOfRange)?; 515*bb4ee6a4SAndroid Build Coastguard Worker if !state.is_none() && !state.is_present() { 516*bb4ee6a4SAndroid Build Coastguard Worker state.mark_as_present(); 517*bb4ee6a4SAndroid Build Coastguard Worker let Some(idx_file) = state.idx_file() else { 518*bb4ee6a4SAndroid Build Coastguard Worker unreachable!("the page is not none."); 519*bb4ee6a4SAndroid Build Coastguard Worker }; 520*bb4ee6a4SAndroid Build Coastguard Worker self.min_possible_present_idx_file = 521*bb4ee6a4SAndroid Build Coastguard Worker std::cmp::min(idx_file, self.min_possible_present_idx_file); 522*bb4ee6a4SAndroid Build Coastguard Worker Ok(()) 523*bb4ee6a4SAndroid Build Coastguard Worker } else { 524*bb4ee6a4SAndroid Build Coastguard Worker Err(Error::InvalidIndex) 525*bb4ee6a4SAndroid Build Coastguard Worker } 526*bb4ee6a4SAndroid Build Coastguard Worker } 527*bb4ee6a4SAndroid Build Coastguard Worker 528*bb4ee6a4SAndroid Build Coastguard Worker /// Writes the contents to the swap file. 529*bb4ee6a4SAndroid Build Coastguard Worker /// 530*bb4ee6a4SAndroid Build Coastguard Worker /// # Arguments 531*bb4ee6a4SAndroid Build Coastguard Worker /// 532*bb4ee6a4SAndroid Build Coastguard Worker /// * `idx_page` - the index of the head page of the content from the head of the pages. 533*bb4ee6a4SAndroid Build Coastguard Worker /// * `mem_slice` - the page content(s). this can be more than 1 page. the size must align with 534*bb4ee6a4SAndroid Build Coastguard Worker /// the pagesize. write_to_file(&mut self, idx_page: usize, mem_slice: &[u8]) -> Result<()>535*bb4ee6a4SAndroid Build Coastguard Worker pub fn write_to_file(&mut self, idx_page: usize, mem_slice: &[u8]) -> Result<()> { 536*bb4ee6a4SAndroid Build Coastguard Worker // validate 537*bb4ee6a4SAndroid Build Coastguard Worker if !is_page_aligned(mem_slice.len()) { 538*bb4ee6a4SAndroid Build Coastguard Worker // mem_slice size must align with page size. 539*bb4ee6a4SAndroid Build Coastguard Worker return Err(Error::InvalidSize); 540*bb4ee6a4SAndroid Build Coastguard Worker } 541*bb4ee6a4SAndroid Build Coastguard Worker let num_pages = bytes_to_pages(mem_slice.len()); 542*bb4ee6a4SAndroid Build Coastguard Worker if idx_page + num_pages > self.page_states.len() { 543*bb4ee6a4SAndroid Build Coastguard Worker return Err(Error::OutOfRange); 544*bb4ee6a4SAndroid Build Coastguard Worker } 545*bb4ee6a4SAndroid Build Coastguard Worker 546*bb4ee6a4SAndroid Build Coastguard Worker // Setting 0 is faster than setting exact index by complex conditions. 547*bb4ee6a4SAndroid Build Coastguard Worker self.min_possible_present_idx_file = 0; 548*bb4ee6a4SAndroid Build Coastguard Worker 549*bb4ee6a4SAndroid Build Coastguard Worker for cur in idx_page..idx_page + num_pages { 550*bb4ee6a4SAndroid Build Coastguard Worker let state = &mut self.page_states[cur]; 551*bb4ee6a4SAndroid Build Coastguard Worker if state.is_none() { 552*bb4ee6a4SAndroid Build Coastguard Worker let idx_file = self.file_states.allocate(cur); 553*bb4ee6a4SAndroid Build Coastguard Worker state.update(idx_file); 554*bb4ee6a4SAndroid Build Coastguard Worker } else { 555*bb4ee6a4SAndroid Build Coastguard Worker state.mark_as_present(); 556*bb4ee6a4SAndroid Build Coastguard Worker } 557*bb4ee6a4SAndroid Build Coastguard Worker } 558*bb4ee6a4SAndroid Build Coastguard Worker 559*bb4ee6a4SAndroid Build Coastguard Worker let mut pending_idx_file = None; 560*bb4ee6a4SAndroid Build Coastguard Worker let mut pending_pages = 0; 561*bb4ee6a4SAndroid Build Coastguard Worker let mut mem_slice = mem_slice; 562*bb4ee6a4SAndroid Build Coastguard Worker for state in self.page_states[idx_page..idx_page + num_pages].iter() { 563*bb4ee6a4SAndroid Build Coastguard Worker let Some(idx_file) = state.idx_file() else { 564*bb4ee6a4SAndroid Build Coastguard Worker unreachable!("pages must be allocated"); 565*bb4ee6a4SAndroid Build Coastguard Worker }; 566*bb4ee6a4SAndroid Build Coastguard Worker if let Some(pending_idx_file) = pending_idx_file { 567*bb4ee6a4SAndroid Build Coastguard Worker if idx_file == pending_idx_file + pending_pages { 568*bb4ee6a4SAndroid Build Coastguard Worker pending_pages += 1; 569*bb4ee6a4SAndroid Build Coastguard Worker continue; 570*bb4ee6a4SAndroid Build Coastguard Worker } 571*bb4ee6a4SAndroid Build Coastguard Worker let size = pages_to_bytes(pending_pages); 572*bb4ee6a4SAndroid Build Coastguard Worker // Write with pwrite(2) syscall instead of copying contents to mmap because write 573*bb4ee6a4SAndroid Build Coastguard Worker // syscall is more explicit for kernel how many pages are going to be written while 574*bb4ee6a4SAndroid Build Coastguard Worker // mmap only knows each page to be written on a page fault basis. 575*bb4ee6a4SAndroid Build Coastguard Worker self.file 576*bb4ee6a4SAndroid Build Coastguard Worker .write_all_at(&mem_slice[..size], pages_to_bytes(pending_idx_file) as u64)?; 577*bb4ee6a4SAndroid Build Coastguard Worker mem_slice = &mem_slice[size..]; 578*bb4ee6a4SAndroid Build Coastguard Worker } 579*bb4ee6a4SAndroid Build Coastguard Worker pending_idx_file = Some(idx_file); 580*bb4ee6a4SAndroid Build Coastguard Worker pending_pages = 1; 581*bb4ee6a4SAndroid Build Coastguard Worker } 582*bb4ee6a4SAndroid Build Coastguard Worker if let Some(pending_idx_file) = pending_idx_file { 583*bb4ee6a4SAndroid Build Coastguard Worker let size = pages_to_bytes(pending_pages); 584*bb4ee6a4SAndroid Build Coastguard Worker self.file 585*bb4ee6a4SAndroid Build Coastguard Worker .write_all_at(&mem_slice[..size], pages_to_bytes(pending_idx_file) as u64)?; 586*bb4ee6a4SAndroid Build Coastguard Worker mem_slice = &mem_slice[size..]; 587*bb4ee6a4SAndroid Build Coastguard Worker } 588*bb4ee6a4SAndroid Build Coastguard Worker if !mem_slice.is_empty() { 589*bb4ee6a4SAndroid Build Coastguard Worker unreachable!("mem_slice must be all consumed"); 590*bb4ee6a4SAndroid Build Coastguard Worker } 591*bb4ee6a4SAndroid Build Coastguard Worker 592*bb4ee6a4SAndroid Build Coastguard Worker Ok(()) 593*bb4ee6a4SAndroid Build Coastguard Worker } 594*bb4ee6a4SAndroid Build Coastguard Worker 595*bb4ee6a4SAndroid Build Coastguard Worker /// Returns the first range of indices of consecutive pages present in the swap file. 596*bb4ee6a4SAndroid Build Coastguard Worker /// 597*bb4ee6a4SAndroid Build Coastguard Worker /// # Arguments 598*bb4ee6a4SAndroid Build Coastguard Worker /// 599*bb4ee6a4SAndroid Build Coastguard Worker /// * `max_pages` - the max size of the returned chunk even if the chunk of consecutive present 600*bb4ee6a4SAndroid Build Coastguard Worker /// pages is longer than this. first_data_range(&mut self, max_pages: usize) -> Option<Range<usize>>601*bb4ee6a4SAndroid Build Coastguard Worker pub fn first_data_range(&mut self, max_pages: usize) -> Option<Range<usize>> { 602*bb4ee6a4SAndroid Build Coastguard Worker if let Some((idx_file_range, head_idx_page)) = self.file_states.find_present_pages_range( 603*bb4ee6a4SAndroid Build Coastguard Worker self.min_possible_present_idx_file, 604*bb4ee6a4SAndroid Build Coastguard Worker &self.page_states, 605*bb4ee6a4SAndroid Build Coastguard Worker max_pages, 606*bb4ee6a4SAndroid Build Coastguard Worker true, 607*bb4ee6a4SAndroid Build Coastguard Worker ) { 608*bb4ee6a4SAndroid Build Coastguard Worker self.min_possible_present_idx_file = idx_file_range.start; 609*bb4ee6a4SAndroid Build Coastguard Worker let idx_page_range = 610*bb4ee6a4SAndroid Build Coastguard Worker head_idx_page..head_idx_page + idx_file_range.end - idx_file_range.start; 611*bb4ee6a4SAndroid Build Coastguard Worker Some(idx_page_range) 612*bb4ee6a4SAndroid Build Coastguard Worker } else { 613*bb4ee6a4SAndroid Build Coastguard Worker self.min_possible_present_idx_file = self.file_states.len(); 614*bb4ee6a4SAndroid Build Coastguard Worker None 615*bb4ee6a4SAndroid Build Coastguard Worker } 616*bb4ee6a4SAndroid Build Coastguard Worker } 617*bb4ee6a4SAndroid Build Coastguard Worker 618*bb4ee6a4SAndroid Build Coastguard Worker /// Returns the [VolatileSlice] corresponding to the indices regardless of whether the pages are 619*bb4ee6a4SAndroid Build Coastguard Worker /// present or not. 620*bb4ee6a4SAndroid Build Coastguard Worker /// 621*bb4ee6a4SAndroid Build Coastguard Worker /// If the range is out of the region, this returns [Error::OutOfRange]. 622*bb4ee6a4SAndroid Build Coastguard Worker /// 623*bb4ee6a4SAndroid Build Coastguard Worker /// # Arguments 624*bb4ee6a4SAndroid Build Coastguard Worker /// 625*bb4ee6a4SAndroid Build Coastguard Worker /// * `idx_page_range` - the indices of the pages. All the pages must be present and consecutive 626*bb4ee6a4SAndroid Build Coastguard Worker /// in the compacted file. get_slice(&self, idx_page_range: Range<usize>) -> Result<VolatileSlice>627*bb4ee6a4SAndroid Build Coastguard Worker pub fn get_slice(&self, idx_page_range: Range<usize>) -> Result<VolatileSlice> { 628*bb4ee6a4SAndroid Build Coastguard Worker let idx_file_range = self.convert_idx_page_range_to_idx_file(idx_page_range)?; 629*bb4ee6a4SAndroid Build Coastguard Worker match self.file_mmap.get_slice( 630*bb4ee6a4SAndroid Build Coastguard Worker pages_to_bytes(idx_file_range.start), 631*bb4ee6a4SAndroid Build Coastguard Worker pages_to_bytes(idx_file_range.end - idx_file_range.start), 632*bb4ee6a4SAndroid Build Coastguard Worker ) { 633*bb4ee6a4SAndroid Build Coastguard Worker Ok(slice) => Ok(slice), 634*bb4ee6a4SAndroid Build Coastguard Worker Err(VolatileMemoryError::OutOfBounds { .. }) => Err(Error::OutOfRange), 635*bb4ee6a4SAndroid Build Coastguard Worker Err(e) => Err(e.into()), 636*bb4ee6a4SAndroid Build Coastguard Worker } 637*bb4ee6a4SAndroid Build Coastguard Worker } 638*bb4ee6a4SAndroid Build Coastguard Worker 639*bb4ee6a4SAndroid Build Coastguard Worker /// Returns the count of present pages in the swap file. present_pages(&self) -> usize640*bb4ee6a4SAndroid Build Coastguard Worker pub fn present_pages(&self) -> usize { 641*bb4ee6a4SAndroid Build Coastguard Worker self.page_states 642*bb4ee6a4SAndroid Build Coastguard Worker .iter() 643*bb4ee6a4SAndroid Build Coastguard Worker .map(|state| state.is_present() as usize) 644*bb4ee6a4SAndroid Build Coastguard Worker .sum() 645*bb4ee6a4SAndroid Build Coastguard Worker } 646*bb4ee6a4SAndroid Build Coastguard Worker 647*bb4ee6a4SAndroid Build Coastguard Worker /// Convert the index range to corresponding index range of compacted file. 648*bb4ee6a4SAndroid Build Coastguard Worker /// 649*bb4ee6a4SAndroid Build Coastguard Worker /// This validates that the `idx_page_range` satisfy: 650*bb4ee6a4SAndroid Build Coastguard Worker /// 651*bb4ee6a4SAndroid Build Coastguard Worker /// * `idx_page_range` has corresponding page in the file. 652*bb4ee6a4SAndroid Build Coastguard Worker /// * corresponding index range in the file is consecutive. convert_idx_page_range_to_idx_file( &self, idx_page_range: Range<usize>, ) -> Result<Range<usize>>653*bb4ee6a4SAndroid Build Coastguard Worker fn convert_idx_page_range_to_idx_file( 654*bb4ee6a4SAndroid Build Coastguard Worker &self, 655*bb4ee6a4SAndroid Build Coastguard Worker idx_page_range: Range<usize>, 656*bb4ee6a4SAndroid Build Coastguard Worker ) -> Result<Range<usize>> { 657*bb4ee6a4SAndroid Build Coastguard Worker // Validate that the idx_range is for cosecutive present file pages. 658*bb4ee6a4SAndroid Build Coastguard Worker let state = self 659*bb4ee6a4SAndroid Build Coastguard Worker .page_states 660*bb4ee6a4SAndroid Build Coastguard Worker .get(idx_page_range.start) 661*bb4ee6a4SAndroid Build Coastguard Worker .ok_or(Error::OutOfRange)?; 662*bb4ee6a4SAndroid Build Coastguard Worker if state.is_none() || !state.is_present() { 663*bb4ee6a4SAndroid Build Coastguard Worker return Err(Error::InvalidIndex); 664*bb4ee6a4SAndroid Build Coastguard Worker } 665*bb4ee6a4SAndroid Build Coastguard Worker let Some(head_idx_file) = state.idx_file() else { 666*bb4ee6a4SAndroid Build Coastguard Worker unreachable!("the page is not none."); 667*bb4ee6a4SAndroid Build Coastguard Worker }; 668*bb4ee6a4SAndroid Build Coastguard Worker let mut idx_file = head_idx_file; 669*bb4ee6a4SAndroid Build Coastguard Worker for idx in idx_page_range.start + 1..idx_page_range.end { 670*bb4ee6a4SAndroid Build Coastguard Worker let state = self.page_states.get(idx).ok_or(Error::OutOfRange)?; 671*bb4ee6a4SAndroid Build Coastguard Worker idx_file += 1; 672*bb4ee6a4SAndroid Build Coastguard Worker if state.is_none() 673*bb4ee6a4SAndroid Build Coastguard Worker || !state.is_present() 674*bb4ee6a4SAndroid Build Coastguard Worker || state 675*bb4ee6a4SAndroid Build Coastguard Worker .idx_file() 676*bb4ee6a4SAndroid Build Coastguard Worker .unwrap_or_else(|| unreachable!("the page is not none.")) 677*bb4ee6a4SAndroid Build Coastguard Worker != idx_file 678*bb4ee6a4SAndroid Build Coastguard Worker { 679*bb4ee6a4SAndroid Build Coastguard Worker return Err(Error::InvalidIndex); 680*bb4ee6a4SAndroid Build Coastguard Worker } 681*bb4ee6a4SAndroid Build Coastguard Worker } 682*bb4ee6a4SAndroid Build Coastguard Worker let idx_file_range = 683*bb4ee6a4SAndroid Build Coastguard Worker head_idx_file..head_idx_file + idx_page_range.end - idx_page_range.start; 684*bb4ee6a4SAndroid Build Coastguard Worker Ok(idx_file_range) 685*bb4ee6a4SAndroid Build Coastguard Worker } 686*bb4ee6a4SAndroid Build Coastguard Worker } 687*bb4ee6a4SAndroid Build Coastguard Worker 688*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)] 689*bb4ee6a4SAndroid Build Coastguard Worker mod tests { 690*bb4ee6a4SAndroid Build Coastguard Worker use std::slice; 691*bb4ee6a4SAndroid Build Coastguard Worker 692*bb4ee6a4SAndroid Build Coastguard Worker use base::pagesize; 693*bb4ee6a4SAndroid Build Coastguard Worker use base::sys::FileDataIterator; 694*bb4ee6a4SAndroid Build Coastguard Worker 695*bb4ee6a4SAndroid Build Coastguard Worker use super::*; 696*bb4ee6a4SAndroid Build Coastguard Worker 697*bb4ee6a4SAndroid Build Coastguard Worker #[test] new_success()698*bb4ee6a4SAndroid Build Coastguard Worker fn new_success() { 699*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 700*bb4ee6a4SAndroid Build Coastguard Worker 701*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(SwapFile::new(&file, 200).is_ok(), true); 702*bb4ee6a4SAndroid Build Coastguard Worker } 703*bb4ee6a4SAndroid Build Coastguard Worker 704*bb4ee6a4SAndroid Build Coastguard Worker #[test] len()705*bb4ee6a4SAndroid Build Coastguard Worker fn len() { 706*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 707*bb4ee6a4SAndroid Build Coastguard Worker let swap_file = SwapFile::new(&file, 200).unwrap(); 708*bb4ee6a4SAndroid Build Coastguard Worker 709*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.page_states.len(), 200); 710*bb4ee6a4SAndroid Build Coastguard Worker } 711*bb4ee6a4SAndroid Build Coastguard Worker 712*bb4ee6a4SAndroid Build Coastguard Worker #[test] page_content_default_is_none()713*bb4ee6a4SAndroid Build Coastguard Worker fn page_content_default_is_none() { 714*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 715*bb4ee6a4SAndroid Build Coastguard Worker let swap_file = SwapFile::new(&file, 200).unwrap(); 716*bb4ee6a4SAndroid Build Coastguard Worker 717*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.page_content(0, false).unwrap().is_none(), true); 718*bb4ee6a4SAndroid Build Coastguard Worker } 719*bb4ee6a4SAndroid Build Coastguard Worker 720*bb4ee6a4SAndroid Build Coastguard Worker #[test] page_content_returns_content()721*bb4ee6a4SAndroid Build Coastguard Worker fn page_content_returns_content() { 722*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 723*bb4ee6a4SAndroid Build Coastguard Worker let mut swap_file = SwapFile::new(&file, 200).unwrap(); 724*bb4ee6a4SAndroid Build Coastguard Worker 725*bb4ee6a4SAndroid Build Coastguard Worker let data = &vec![1; pagesize()]; 726*bb4ee6a4SAndroid Build Coastguard Worker swap_file.write_to_file(0, data).unwrap(); 727*bb4ee6a4SAndroid Build Coastguard Worker 728*bb4ee6a4SAndroid Build Coastguard Worker let page = swap_file.page_content(0, false).unwrap().unwrap(); 729*bb4ee6a4SAndroid Build Coastguard Worker // TODO(b/315998194): Add safety comment 730*bb4ee6a4SAndroid Build Coastguard Worker #[allow(clippy::undocumented_unsafe_blocks)] 731*bb4ee6a4SAndroid Build Coastguard Worker let result = unsafe { slice::from_raw_parts(page.as_ptr(), pagesize()) }; 732*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(result, data); 733*bb4ee6a4SAndroid Build Coastguard Worker } 734*bb4ee6a4SAndroid Build Coastguard Worker 735*bb4ee6a4SAndroid Build Coastguard Worker #[test] page_content_out_of_range()736*bb4ee6a4SAndroid Build Coastguard Worker fn page_content_out_of_range() { 737*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 738*bb4ee6a4SAndroid Build Coastguard Worker let swap_file = SwapFile::new(&file, 200).unwrap(); 739*bb4ee6a4SAndroid Build Coastguard Worker 740*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.page_content(199, false).is_ok(), true); 741*bb4ee6a4SAndroid Build Coastguard Worker match swap_file.page_content(200, false) { 742*bb4ee6a4SAndroid Build Coastguard Worker Err(Error::OutOfRange) => {} 743*bb4ee6a4SAndroid Build Coastguard Worker _ => unreachable!("not out of range"), 744*bb4ee6a4SAndroid Build Coastguard Worker } 745*bb4ee6a4SAndroid Build Coastguard Worker } 746*bb4ee6a4SAndroid Build Coastguard Worker assert_page_content(swap_file: &SwapFile, idx: usize, data: &[u8])747*bb4ee6a4SAndroid Build Coastguard Worker fn assert_page_content(swap_file: &SwapFile, idx: usize, data: &[u8]) { 748*bb4ee6a4SAndroid Build Coastguard Worker let page = swap_file.page_content(idx, false).unwrap().unwrap(); 749*bb4ee6a4SAndroid Build Coastguard Worker // TODO(b/315998194): Add safety comment 750*bb4ee6a4SAndroid Build Coastguard Worker #[allow(clippy::undocumented_unsafe_blocks)] 751*bb4ee6a4SAndroid Build Coastguard Worker let result = unsafe { slice::from_raw_parts(page.as_ptr(), pagesize()) }; 752*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(result, data); 753*bb4ee6a4SAndroid Build Coastguard Worker } 754*bb4ee6a4SAndroid Build Coastguard Worker 755*bb4ee6a4SAndroid Build Coastguard Worker #[test] write_to_file_swap_file()756*bb4ee6a4SAndroid Build Coastguard Worker fn write_to_file_swap_file() { 757*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 758*bb4ee6a4SAndroid Build Coastguard Worker let mut swap_file = SwapFile::new(&file, 200).unwrap(); 759*bb4ee6a4SAndroid Build Coastguard Worker 760*bb4ee6a4SAndroid Build Coastguard Worker let buf1 = &vec![1; pagesize()]; 761*bb4ee6a4SAndroid Build Coastguard Worker let buf2 = &vec![2; 2 * pagesize()]; 762*bb4ee6a4SAndroid Build Coastguard Worker swap_file.write_to_file(0, buf1).unwrap(); 763*bb4ee6a4SAndroid Build Coastguard Worker swap_file.write_to_file(2, buf2).unwrap(); 764*bb4ee6a4SAndroid Build Coastguard Worker 765*bb4ee6a4SAndroid Build Coastguard Worker // page_content() 766*bb4ee6a4SAndroid Build Coastguard Worker assert_page_content(&swap_file, 0, buf1); 767*bb4ee6a4SAndroid Build Coastguard Worker assert_page_content(&swap_file, 2, &buf2[0..pagesize()]); 768*bb4ee6a4SAndroid Build Coastguard Worker assert_page_content(&swap_file, 3, &buf2[pagesize()..2 * pagesize()]); 769*bb4ee6a4SAndroid Build Coastguard Worker } 770*bb4ee6a4SAndroid Build Coastguard Worker 771*bb4ee6a4SAndroid Build Coastguard Worker #[test] write_to_file_invalid_size()772*bb4ee6a4SAndroid Build Coastguard Worker fn write_to_file_invalid_size() { 773*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 774*bb4ee6a4SAndroid Build Coastguard Worker let mut swap_file = SwapFile::new(&file, 200).unwrap(); 775*bb4ee6a4SAndroid Build Coastguard Worker 776*bb4ee6a4SAndroid Build Coastguard Worker let buf = &vec![1; pagesize() + 1]; 777*bb4ee6a4SAndroid Build Coastguard Worker match swap_file.write_to_file(0, buf) { 778*bb4ee6a4SAndroid Build Coastguard Worker Err(Error::InvalidSize) => {} 779*bb4ee6a4SAndroid Build Coastguard Worker _ => unreachable!("not invalid size"), 780*bb4ee6a4SAndroid Build Coastguard Worker }; 781*bb4ee6a4SAndroid Build Coastguard Worker } 782*bb4ee6a4SAndroid Build Coastguard Worker 783*bb4ee6a4SAndroid Build Coastguard Worker #[test] write_to_file_out_of_range()784*bb4ee6a4SAndroid Build Coastguard Worker fn write_to_file_out_of_range() { 785*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 786*bb4ee6a4SAndroid Build Coastguard Worker let mut swap_file = SwapFile::new(&file, 200).unwrap(); 787*bb4ee6a4SAndroid Build Coastguard Worker 788*bb4ee6a4SAndroid Build Coastguard Worker let buf1 = &vec![1; pagesize()]; 789*bb4ee6a4SAndroid Build Coastguard Worker let buf2 = &vec![2; 2 * pagesize()]; 790*bb4ee6a4SAndroid Build Coastguard Worker match swap_file.write_to_file(200, buf1) { 791*bb4ee6a4SAndroid Build Coastguard Worker Err(Error::OutOfRange) => {} 792*bb4ee6a4SAndroid Build Coastguard Worker _ => unreachable!("not out of range"), 793*bb4ee6a4SAndroid Build Coastguard Worker }; 794*bb4ee6a4SAndroid Build Coastguard Worker match swap_file.write_to_file(199, buf2) { 795*bb4ee6a4SAndroid Build Coastguard Worker Err(Error::OutOfRange) => {} 796*bb4ee6a4SAndroid Build Coastguard Worker _ => unreachable!("not out of range"), 797*bb4ee6a4SAndroid Build Coastguard Worker }; 798*bb4ee6a4SAndroid Build Coastguard Worker } 799*bb4ee6a4SAndroid Build Coastguard Worker 800*bb4ee6a4SAndroid Build Coastguard Worker #[test] write_to_file_overwrite()801*bb4ee6a4SAndroid Build Coastguard Worker fn write_to_file_overwrite() { 802*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 803*bb4ee6a4SAndroid Build Coastguard Worker let mut swap_file = SwapFile::new(&file, 200).unwrap(); 804*bb4ee6a4SAndroid Build Coastguard Worker 805*bb4ee6a4SAndroid Build Coastguard Worker swap_file.write_to_file(0, &vec![1; pagesize()]).unwrap(); 806*bb4ee6a4SAndroid Build Coastguard Worker swap_file 807*bb4ee6a4SAndroid Build Coastguard Worker .write_to_file(2, &vec![2; 2 * pagesize()]) 808*bb4ee6a4SAndroid Build Coastguard Worker .unwrap(); 809*bb4ee6a4SAndroid Build Coastguard Worker 810*bb4ee6a4SAndroid Build Coastguard Worker let mut buf = vec![0; 3 * pagesize()]; 811*bb4ee6a4SAndroid Build Coastguard Worker buf[..pagesize()].fill(3); 812*bb4ee6a4SAndroid Build Coastguard Worker buf[pagesize()..2 * pagesize()].fill(4); 813*bb4ee6a4SAndroid Build Coastguard Worker buf[2 * pagesize()..3 * pagesize()].fill(5); 814*bb4ee6a4SAndroid Build Coastguard Worker swap_file.write_to_file(0, &buf).unwrap(); 815*bb4ee6a4SAndroid Build Coastguard Worker 816*bb4ee6a4SAndroid Build Coastguard Worker assert_page_content(&swap_file, 0, &vec![3; pagesize()]); 817*bb4ee6a4SAndroid Build Coastguard Worker assert_page_content(&swap_file, 1, &vec![4; pagesize()]); 818*bb4ee6a4SAndroid Build Coastguard Worker assert_page_content(&swap_file, 2, &vec![5; pagesize()]); 819*bb4ee6a4SAndroid Build Coastguard Worker assert_page_content(&swap_file, 3, &vec![2; pagesize()]); 820*bb4ee6a4SAndroid Build Coastguard Worker assert!(swap_file.page_content(4, false).unwrap().is_none()); 821*bb4ee6a4SAndroid Build Coastguard Worker 822*bb4ee6a4SAndroid Build Coastguard Worker let data = FileDataIterator::new(&file, 0, file.metadata().unwrap().len()) 823*bb4ee6a4SAndroid Build Coastguard Worker .collect::<std::result::Result<Vec<_>, _>>(); 824*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(data, Ok(vec![0..4 * pagesize() as u64])); 825*bb4ee6a4SAndroid Build Coastguard Worker 826*bb4ee6a4SAndroid Build Coastguard Worker buf[..pagesize()].fill(6); 827*bb4ee6a4SAndroid Build Coastguard Worker buf[pagesize()..2 * pagesize()].fill(7); 828*bb4ee6a4SAndroid Build Coastguard Worker buf[2 * pagesize()..3 * pagesize()].fill(8); 829*bb4ee6a4SAndroid Build Coastguard Worker swap_file.write_to_file(2, &buf).unwrap(); 830*bb4ee6a4SAndroid Build Coastguard Worker assert_page_content(&swap_file, 0, &vec![3; pagesize()]); 831*bb4ee6a4SAndroid Build Coastguard Worker assert_page_content(&swap_file, 1, &vec![4; pagesize()]); 832*bb4ee6a4SAndroid Build Coastguard Worker assert_page_content(&swap_file, 2, &vec![6; pagesize()]); 833*bb4ee6a4SAndroid Build Coastguard Worker assert_page_content(&swap_file, 3, &vec![7; pagesize()]); 834*bb4ee6a4SAndroid Build Coastguard Worker assert_page_content(&swap_file, 4, &vec![8; pagesize()]); 835*bb4ee6a4SAndroid Build Coastguard Worker assert!(swap_file.page_content(5, false).unwrap().is_none()); 836*bb4ee6a4SAndroid Build Coastguard Worker 837*bb4ee6a4SAndroid Build Coastguard Worker let data = FileDataIterator::new(&file, 0, file.metadata().unwrap().len()) 838*bb4ee6a4SAndroid Build Coastguard Worker .collect::<std::result::Result<Vec<_>, _>>(); 839*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(data, Ok(vec![0..5 * pagesize() as u64])); 840*bb4ee6a4SAndroid Build Coastguard Worker } 841*bb4ee6a4SAndroid Build Coastguard Worker 842*bb4ee6a4SAndroid Build Coastguard Worker #[test] 843*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")] // TODO(b/272612118): unit test infra (qemu-user) support lock_and_start_populate()844*bb4ee6a4SAndroid Build Coastguard Worker fn lock_and_start_populate() { 845*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 846*bb4ee6a4SAndroid Build Coastguard Worker let mut swap_file = SwapFile::new(&file, 200).unwrap(); 847*bb4ee6a4SAndroid Build Coastguard Worker 848*bb4ee6a4SAndroid Build Coastguard Worker swap_file.write_to_file(1, &vec![1; pagesize()]).unwrap(); 849*bb4ee6a4SAndroid Build Coastguard Worker swap_file 850*bb4ee6a4SAndroid Build Coastguard Worker .write_to_file(3, &vec![1; 5 * pagesize()]) 851*bb4ee6a4SAndroid Build Coastguard Worker .unwrap(); 852*bb4ee6a4SAndroid Build Coastguard Worker swap_file.write_to_file(10, &vec![1; pagesize()]).unwrap(); 853*bb4ee6a4SAndroid Build Coastguard Worker 854*bb4ee6a4SAndroid Build Coastguard Worker let mut locked_pages = 0; 855*bb4ee6a4SAndroid Build Coastguard Worker loop { 856*bb4ee6a4SAndroid Build Coastguard Worker let pages = swap_file.lock_and_async_prefetch(2).unwrap(); 857*bb4ee6a4SAndroid Build Coastguard Worker if pages == 0 { 858*bb4ee6a4SAndroid Build Coastguard Worker break; 859*bb4ee6a4SAndroid Build Coastguard Worker } 860*bb4ee6a4SAndroid Build Coastguard Worker assert!(pages <= 2); 861*bb4ee6a4SAndroid Build Coastguard Worker locked_pages += pages; 862*bb4ee6a4SAndroid Build Coastguard Worker } 863*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(locked_pages, 7); 864*bb4ee6a4SAndroid Build Coastguard Worker } 865*bb4ee6a4SAndroid Build Coastguard Worker 866*bb4ee6a4SAndroid Build Coastguard Worker #[test] clear_range()867*bb4ee6a4SAndroid Build Coastguard Worker fn clear_range() { 868*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 869*bb4ee6a4SAndroid Build Coastguard Worker let mut swap_file = SwapFile::new(&file, 200).unwrap(); 870*bb4ee6a4SAndroid Build Coastguard Worker 871*bb4ee6a4SAndroid Build Coastguard Worker let data = &vec![1; pagesize()]; 872*bb4ee6a4SAndroid Build Coastguard Worker swap_file.write_to_file(0, data).unwrap(); 873*bb4ee6a4SAndroid Build Coastguard Worker swap_file.clear_range(0..1).unwrap(); 874*bb4ee6a4SAndroid Build Coastguard Worker 875*bb4ee6a4SAndroid Build Coastguard Worker assert!(swap_file.page_content(0, false).unwrap().is_none()); 876*bb4ee6a4SAndroid Build Coastguard Worker } 877*bb4ee6a4SAndroid Build Coastguard Worker 878*bb4ee6a4SAndroid Build Coastguard Worker #[test] 879*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")] // TODO(b/272612118): unit test infra (qemu-user) support clear_range_unlocked_pages()880*bb4ee6a4SAndroid Build Coastguard Worker fn clear_range_unlocked_pages() { 881*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 882*bb4ee6a4SAndroid Build Coastguard Worker let mut swap_file = SwapFile::new(&file, 200).unwrap(); 883*bb4ee6a4SAndroid Build Coastguard Worker 884*bb4ee6a4SAndroid Build Coastguard Worker swap_file 885*bb4ee6a4SAndroid Build Coastguard Worker .write_to_file(1, &vec![1; 10 * pagesize()]) 886*bb4ee6a4SAndroid Build Coastguard Worker .unwrap(); 887*bb4ee6a4SAndroid Build Coastguard Worker // 1..6 is locked, 6..11 is not locked. 888*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.lock_and_async_prefetch(5).unwrap(), 5); 889*bb4ee6a4SAndroid Build Coastguard Worker 890*bb4ee6a4SAndroid Build Coastguard Worker // locked pages only 891*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.clear_range(1..4).unwrap(), 3); 892*bb4ee6a4SAndroid Build Coastguard Worker // locked pages + non-locked pages 893*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.clear_range(4..7).unwrap(), 2); 894*bb4ee6a4SAndroid Build Coastguard Worker // non-locked pages 895*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.clear_range(10..11).unwrap(), 0); 896*bb4ee6a4SAndroid Build Coastguard Worker } 897*bb4ee6a4SAndroid Build Coastguard Worker 898*bb4ee6a4SAndroid Build Coastguard Worker #[test] clear_range_keep_on_disk()899*bb4ee6a4SAndroid Build Coastguard Worker fn clear_range_keep_on_disk() { 900*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 901*bb4ee6a4SAndroid Build Coastguard Worker let mut swap_file = SwapFile::new(&file, 200).unwrap(); 902*bb4ee6a4SAndroid Build Coastguard Worker 903*bb4ee6a4SAndroid Build Coastguard Worker let data = &vec![1; pagesize()]; 904*bb4ee6a4SAndroid Build Coastguard Worker swap_file.write_to_file(0, data).unwrap(); 905*bb4ee6a4SAndroid Build Coastguard Worker swap_file.clear_range(0..1).unwrap(); 906*bb4ee6a4SAndroid Build Coastguard Worker 907*bb4ee6a4SAndroid Build Coastguard Worker let slice = swap_file.page_content(0, true).unwrap().unwrap(); 908*bb4ee6a4SAndroid Build Coastguard Worker // TODO(b/315998194): Add safety comment 909*bb4ee6a4SAndroid Build Coastguard Worker #[allow(clippy::undocumented_unsafe_blocks)] 910*bb4ee6a4SAndroid Build Coastguard Worker let slice = unsafe { slice::from_raw_parts(slice.as_ptr(), slice.size()) }; 911*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(slice, data); 912*bb4ee6a4SAndroid Build Coastguard Worker } 913*bb4ee6a4SAndroid Build Coastguard Worker 914*bb4ee6a4SAndroid Build Coastguard Worker #[test] clear_range_out_of_range()915*bb4ee6a4SAndroid Build Coastguard Worker fn clear_range_out_of_range() { 916*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 917*bb4ee6a4SAndroid Build Coastguard Worker let mut swap_file = SwapFile::new(&file, 200).unwrap(); 918*bb4ee6a4SAndroid Build Coastguard Worker swap_file.write_to_file(199, &vec![0; pagesize()]).unwrap(); 919*bb4ee6a4SAndroid Build Coastguard Worker 920*bb4ee6a4SAndroid Build Coastguard Worker match swap_file.clear_range(199..201) { 921*bb4ee6a4SAndroid Build Coastguard Worker Err(Error::OutOfRange) => {} 922*bb4ee6a4SAndroid Build Coastguard Worker _ => unreachable!("not out of range"), 923*bb4ee6a4SAndroid Build Coastguard Worker }; 924*bb4ee6a4SAndroid Build Coastguard Worker assert!(swap_file.clear_range(199..200).is_ok()); 925*bb4ee6a4SAndroid Build Coastguard Worker match swap_file.clear_range(200..201) { 926*bb4ee6a4SAndroid Build Coastguard Worker Err(Error::OutOfRange) => {} 927*bb4ee6a4SAndroid Build Coastguard Worker _ => unreachable!("not out of range"), 928*bb4ee6a4SAndroid Build Coastguard Worker }; 929*bb4ee6a4SAndroid Build Coastguard Worker } 930*bb4ee6a4SAndroid Build Coastguard Worker 931*bb4ee6a4SAndroid Build Coastguard Worker #[test] free_range()932*bb4ee6a4SAndroid Build Coastguard Worker fn free_range() { 933*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 934*bb4ee6a4SAndroid Build Coastguard Worker let mut swap_file = SwapFile::new(&file, 200).unwrap(); 935*bb4ee6a4SAndroid Build Coastguard Worker 936*bb4ee6a4SAndroid Build Coastguard Worker let data = &vec![1; pagesize()]; 937*bb4ee6a4SAndroid Build Coastguard Worker swap_file.write_to_file(0, data).unwrap(); 938*bb4ee6a4SAndroid Build Coastguard Worker swap_file.free_range(0..1).unwrap(); 939*bb4ee6a4SAndroid Build Coastguard Worker 940*bb4ee6a4SAndroid Build Coastguard Worker assert!(swap_file.page_content(0, false).unwrap().is_none()); 941*bb4ee6a4SAndroid Build Coastguard Worker assert!(swap_file.page_content(0, true).unwrap().is_none()); 942*bb4ee6a4SAndroid Build Coastguard Worker } 943*bb4ee6a4SAndroid Build Coastguard Worker 944*bb4ee6a4SAndroid Build Coastguard Worker #[test] 945*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")] // TODO(b/272612118): unit test infra (qemu-user) support free_range_unlocked_pages()946*bb4ee6a4SAndroid Build Coastguard Worker fn free_range_unlocked_pages() { 947*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 948*bb4ee6a4SAndroid Build Coastguard Worker let mut swap_file = SwapFile::new(&file, 200).unwrap(); 949*bb4ee6a4SAndroid Build Coastguard Worker 950*bb4ee6a4SAndroid Build Coastguard Worker swap_file 951*bb4ee6a4SAndroid Build Coastguard Worker .write_to_file(1, &vec![1; 10 * pagesize()]) 952*bb4ee6a4SAndroid Build Coastguard Worker .unwrap(); 953*bb4ee6a4SAndroid Build Coastguard Worker // 1..6 is locked, 6..11 is not locked. 954*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.lock_and_async_prefetch(5).unwrap(), 5); 955*bb4ee6a4SAndroid Build Coastguard Worker 956*bb4ee6a4SAndroid Build Coastguard Worker // empty pages 957*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.free_range(0..1).unwrap(), 0); 958*bb4ee6a4SAndroid Build Coastguard Worker // empty pages + locked pages 959*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.free_range(0..2).unwrap(), 1); 960*bb4ee6a4SAndroid Build Coastguard Worker // locked pages only 961*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.free_range(2..4).unwrap(), 2); 962*bb4ee6a4SAndroid Build Coastguard Worker // empty pages + locked pages + non-locked pages 963*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.free_range(3..7).unwrap(), 2); 964*bb4ee6a4SAndroid Build Coastguard Worker // non-locked pages 965*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.free_range(10..11).unwrap(), 0); 966*bb4ee6a4SAndroid Build Coastguard Worker } 967*bb4ee6a4SAndroid Build Coastguard Worker 968*bb4ee6a4SAndroid Build Coastguard Worker #[test] free_range_out_of_range()969*bb4ee6a4SAndroid Build Coastguard Worker fn free_range_out_of_range() { 970*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 971*bb4ee6a4SAndroid Build Coastguard Worker let mut swap_file = SwapFile::new(&file, 200).unwrap(); 972*bb4ee6a4SAndroid Build Coastguard Worker 973*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.free_range(199..200).is_ok(), true); 974*bb4ee6a4SAndroid Build Coastguard Worker match swap_file.free_range(200..201) { 975*bb4ee6a4SAndroid Build Coastguard Worker Err(Error::OutOfRange) => {} 976*bb4ee6a4SAndroid Build Coastguard Worker _ => unreachable!("not out of range"), 977*bb4ee6a4SAndroid Build Coastguard Worker }; 978*bb4ee6a4SAndroid Build Coastguard Worker match swap_file.free_range(199..201) { 979*bb4ee6a4SAndroid Build Coastguard Worker Err(Error::OutOfRange) => {} 980*bb4ee6a4SAndroid Build Coastguard Worker _ => unreachable!("not out of range"), 981*bb4ee6a4SAndroid Build Coastguard Worker }; 982*bb4ee6a4SAndroid Build Coastguard Worker } 983*bb4ee6a4SAndroid Build Coastguard Worker 984*bb4ee6a4SAndroid Build Coastguard Worker #[test] free_range_and_write()985*bb4ee6a4SAndroid Build Coastguard Worker fn free_range_and_write() { 986*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 987*bb4ee6a4SAndroid Build Coastguard Worker let mut swap_file = SwapFile::new(&file, 200).unwrap(); 988*bb4ee6a4SAndroid Build Coastguard Worker 989*bb4ee6a4SAndroid Build Coastguard Worker let data = &vec![1; 5 * pagesize()]; 990*bb4ee6a4SAndroid Build Coastguard Worker swap_file.write_to_file(0, data).unwrap(); 991*bb4ee6a4SAndroid Build Coastguard Worker swap_file.free_range(0..5).unwrap(); 992*bb4ee6a4SAndroid Build Coastguard Worker 993*bb4ee6a4SAndroid Build Coastguard Worker swap_file 994*bb4ee6a4SAndroid Build Coastguard Worker .write_to_file(0, &vec![2; 2 * pagesize()]) 995*bb4ee6a4SAndroid Build Coastguard Worker .unwrap(); 996*bb4ee6a4SAndroid Build Coastguard Worker swap_file 997*bb4ee6a4SAndroid Build Coastguard Worker .write_to_file(5, &vec![3; 4 * pagesize()]) 998*bb4ee6a4SAndroid Build Coastguard Worker .unwrap(); 999*bb4ee6a4SAndroid Build Coastguard Worker 1000*bb4ee6a4SAndroid Build Coastguard Worker assert_page_content(&swap_file, 0, &vec![2; pagesize()]); 1001*bb4ee6a4SAndroid Build Coastguard Worker assert_page_content(&swap_file, 1, &vec![2; pagesize()]); 1002*bb4ee6a4SAndroid Build Coastguard Worker assert!(swap_file.page_content(2, true).unwrap().is_none()); 1003*bb4ee6a4SAndroid Build Coastguard Worker assert!(swap_file.page_content(3, true).unwrap().is_none()); 1004*bb4ee6a4SAndroid Build Coastguard Worker assert!(swap_file.page_content(4, true).unwrap().is_none()); 1005*bb4ee6a4SAndroid Build Coastguard Worker assert_page_content(&swap_file, 5, &vec![3; pagesize()]); 1006*bb4ee6a4SAndroid Build Coastguard Worker assert_page_content(&swap_file, 6, &vec![3; pagesize()]); 1007*bb4ee6a4SAndroid Build Coastguard Worker assert_page_content(&swap_file, 7, &vec![3; pagesize()]); 1008*bb4ee6a4SAndroid Build Coastguard Worker assert_page_content(&swap_file, 8, &vec![3; pagesize()]); 1009*bb4ee6a4SAndroid Build Coastguard Worker assert!(swap_file.page_content(9, true).unwrap().is_none()); 1010*bb4ee6a4SAndroid Build Coastguard Worker 1011*bb4ee6a4SAndroid Build Coastguard Worker let data = FileDataIterator::new(&file, 0, file.metadata().unwrap().len()) 1012*bb4ee6a4SAndroid Build Coastguard Worker .collect::<std::result::Result<Vec<_>, _>>(); 1013*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(data, Ok(vec![0..6 * pagesize() as u64])); 1014*bb4ee6a4SAndroid Build Coastguard Worker } 1015*bb4ee6a4SAndroid Build Coastguard Worker 1016*bb4ee6a4SAndroid Build Coastguard Worker #[test] 1017*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")] // TODO(b/272612118): unit test infra (qemu-user) support clear_mlock()1018*bb4ee6a4SAndroid Build Coastguard Worker fn clear_mlock() { 1019*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 1020*bb4ee6a4SAndroid Build Coastguard Worker let mut swap_file = SwapFile::new(&file, 200).unwrap(); 1021*bb4ee6a4SAndroid Build Coastguard Worker 1022*bb4ee6a4SAndroid Build Coastguard Worker swap_file 1023*bb4ee6a4SAndroid Build Coastguard Worker .write_to_file(1, &vec![1; 10 * pagesize()]) 1024*bb4ee6a4SAndroid Build Coastguard Worker .unwrap(); 1025*bb4ee6a4SAndroid Build Coastguard Worker // success if there is no mlock. 1026*bb4ee6a4SAndroid Build Coastguard Worker assert!(swap_file.clear_mlock().is_ok()); 1027*bb4ee6a4SAndroid Build Coastguard Worker 1028*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.lock_and_async_prefetch(11).unwrap(), 10); 1029*bb4ee6a4SAndroid Build Coastguard Worker // success if there is mlocked area. 1030*bb4ee6a4SAndroid Build Coastguard Worker assert!(swap_file.clear_mlock().is_ok()); 1031*bb4ee6a4SAndroid Build Coastguard Worker 1032*bb4ee6a4SAndroid Build Coastguard Worker // mlock area is cleared. 1033*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.lock_and_async_prefetch(11).unwrap(), 10); 1034*bb4ee6a4SAndroid Build Coastguard Worker } 1035*bb4ee6a4SAndroid Build Coastguard Worker 1036*bb4ee6a4SAndroid Build Coastguard Worker #[test] first_data_range()1037*bb4ee6a4SAndroid Build Coastguard Worker fn first_data_range() { 1038*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 1039*bb4ee6a4SAndroid Build Coastguard Worker let mut swap_file = SwapFile::new(&file, 200).unwrap(); 1040*bb4ee6a4SAndroid Build Coastguard Worker 1041*bb4ee6a4SAndroid Build Coastguard Worker swap_file 1042*bb4ee6a4SAndroid Build Coastguard Worker .write_to_file(1, &vec![1; 2 * pagesize()]) 1043*bb4ee6a4SAndroid Build Coastguard Worker .unwrap(); 1044*bb4ee6a4SAndroid Build Coastguard Worker swap_file.write_to_file(3, &vec![2; pagesize()]).unwrap(); 1045*bb4ee6a4SAndroid Build Coastguard Worker 1046*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.first_data_range(200).unwrap(), 1..4); 1047*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.first_data_range(2).unwrap(), 1..3); 1048*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.first_data_range(1).unwrap(), 1..2); 1049*bb4ee6a4SAndroid Build Coastguard Worker swap_file.clear_range(1..3).unwrap(); 1050*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.first_data_range(2).unwrap(), 3..4); 1051*bb4ee6a4SAndroid Build Coastguard Worker swap_file.clear_range(3..4).unwrap(); 1052*bb4ee6a4SAndroid Build Coastguard Worker assert!(swap_file.first_data_range(2).is_none()); 1053*bb4ee6a4SAndroid Build Coastguard Worker } 1054*bb4ee6a4SAndroid Build Coastguard Worker 1055*bb4ee6a4SAndroid Build Coastguard Worker #[test] get_slice()1056*bb4ee6a4SAndroid Build Coastguard Worker fn get_slice() { 1057*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 1058*bb4ee6a4SAndroid Build Coastguard Worker let mut swap_file = SwapFile::new(&file, 200).unwrap(); 1059*bb4ee6a4SAndroid Build Coastguard Worker 1060*bb4ee6a4SAndroid Build Coastguard Worker swap_file.write_to_file(1, &vec![1; pagesize()]).unwrap(); 1061*bb4ee6a4SAndroid Build Coastguard Worker swap_file.write_to_file(2, &vec![2; pagesize()]).unwrap(); 1062*bb4ee6a4SAndroid Build Coastguard Worker 1063*bb4ee6a4SAndroid Build Coastguard Worker let slice = swap_file.get_slice(1..3).unwrap(); 1064*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(slice.size(), 2 * pagesize()); 1065*bb4ee6a4SAndroid Build Coastguard Worker let mut buf = vec![0u8; pagesize()]; 1066*bb4ee6a4SAndroid Build Coastguard Worker slice.get_slice(0, pagesize()).unwrap().copy_to(&mut buf); 1067*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(buf, vec![1; pagesize()]); 1068*bb4ee6a4SAndroid Build Coastguard Worker 1069*bb4ee6a4SAndroid Build Coastguard Worker let mut buf = vec![0u8; pagesize()]; 1070*bb4ee6a4SAndroid Build Coastguard Worker slice 1071*bb4ee6a4SAndroid Build Coastguard Worker .get_slice(pagesize(), pagesize()) 1072*bb4ee6a4SAndroid Build Coastguard Worker .unwrap() 1073*bb4ee6a4SAndroid Build Coastguard Worker .copy_to(&mut buf); 1074*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(buf, vec![2; pagesize()]); 1075*bb4ee6a4SAndroid Build Coastguard Worker } 1076*bb4ee6a4SAndroid Build Coastguard Worker 1077*bb4ee6a4SAndroid Build Coastguard Worker #[test] get_slice_out_of_range()1078*bb4ee6a4SAndroid Build Coastguard Worker fn get_slice_out_of_range() { 1079*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 1080*bb4ee6a4SAndroid Build Coastguard Worker let swap_file = SwapFile::new(&file, 200).unwrap(); 1081*bb4ee6a4SAndroid Build Coastguard Worker 1082*bb4ee6a4SAndroid Build Coastguard Worker match swap_file.get_slice(200..201) { 1083*bb4ee6a4SAndroid Build Coastguard Worker Err(Error::OutOfRange) => {} 1084*bb4ee6a4SAndroid Build Coastguard Worker other => { 1085*bb4ee6a4SAndroid Build Coastguard Worker unreachable!("unexpected result {:?}", other); 1086*bb4ee6a4SAndroid Build Coastguard Worker } 1087*bb4ee6a4SAndroid Build Coastguard Worker } 1088*bb4ee6a4SAndroid Build Coastguard Worker } 1089*bb4ee6a4SAndroid Build Coastguard Worker 1090*bb4ee6a4SAndroid Build Coastguard Worker #[test] present_pages()1091*bb4ee6a4SAndroid Build Coastguard Worker fn present_pages() { 1092*bb4ee6a4SAndroid Build Coastguard Worker let file = tempfile::tempfile().unwrap(); 1093*bb4ee6a4SAndroid Build Coastguard Worker let mut swap_file = SwapFile::new(&file, 200).unwrap(); 1094*bb4ee6a4SAndroid Build Coastguard Worker 1095*bb4ee6a4SAndroid Build Coastguard Worker swap_file.write_to_file(1, &vec![1; pagesize()]).unwrap(); 1096*bb4ee6a4SAndroid Build Coastguard Worker swap_file.write_to_file(2, &vec![2; pagesize()]).unwrap(); 1097*bb4ee6a4SAndroid Build Coastguard Worker 1098*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(swap_file.present_pages(), 2); 1099*bb4ee6a4SAndroid Build Coastguard Worker } 1100*bb4ee6a4SAndroid Build Coastguard Worker } 1101