xref: /aosp_15_r20/external/crosvm/swap/src/file.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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