1use fidl_fuchsia_starnix_runner as fstarnixrunner;
10use futures::TryStreamExt;
11use refaults_vmo::{AtomicBitVec, PageRefaultCounter};
12use starnix_core::mm::PAGE_SIZE;
13use starnix_logging::{log_debug, log_error, log_warn, with_zx_name};
14use starnix_sync::{LockDepMutex, PagerFilesByInodeLock, PagerFilesystemsLock};
15use starnix_uapi::errors::Errno;
16use starnix_uapi::{errno, error};
17use std::collections::HashMap;
18use std::collections::hash_map::Entry;
19use std::ops::Range;
20use std::sync::Arc;
21use std::sync::atomic::{AtomicU32, Ordering};
22use zx::sys::zx_page_request_command_t::{ZX_PAGER_VMO_COMPLETE, ZX_PAGER_VMO_READ};
23
24const PAGER_THREADS: usize = 1;
27const TRANSFER_VMO_SIZE: u64 = 1 * 1024 * 1024;
28const ZERO_VMO_SIZE: u64 = 1 * 1024 * 1024;
29const READAHEAD_ALIGNMENT: u64 = 128 * 1024;
30
31const CATEGORY_STARNIX_PAGER: &'static str = "starnix:pager";
33
34pub async fn run_pager(
35 pager_request: fstarnixrunner::ManagerCreatePagerRequest,
36 pager: Arc<Pager>,
37) {
38 let fstarnixrunner::ManagerCreatePagerRequest {
39 backing_vmo: Some(backing_vmo),
40 block_size: Some(block_size),
41 pager: Some(pager_server),
42 ..
43 } = pager_request
44 else {
45 log_error!("Invalid create pager request");
46 return;
47 };
48
49 let filesystem = Arc::new(match Filesystem::new(pager.clone(), backing_vmo, block_size) {
50 Ok(filesystem) => filesystem,
51 Err(error) => {
52 log_error!("Unable to register filesystem {error}");
53 return;
54 }
55 });
56
57 pager.add_filesystem(filesystem.clone());
58
59 let mut stream = pager_server.into_stream();
60 'outer: while let Ok(Some(event)) = stream.try_next().await {
61 match event {
62 fstarnixrunner::PagerRequest::RegisterFile {
63 payload:
64 fstarnixrunner::PagerRegisterFileRequest {
65 name: Some(name),
66 inode_num: Some(inode_num),
67 size: Some(size),
68 extents: Some(extents),
69 ..
70 },
71 responder,
72 ..
73 } => {
74 fuchsia_trace::instant!(
75 CATEGORY_STARNIX_PAGER,
76 "file_register",
77 fuchsia_trace::Scope::Thread
78 );
79 match filesystem.register(
80 &name,
81 inode_num,
82 size,
83 extents
84 .iter()
85 .map(|e| PagerExtent {
86 logical: e.logical_start..e.logical_end,
87 physical_block: e.physical_block,
88 })
89 .collect(),
90 ) {
91 Ok(vmo) => {
92 match responder.send(Ok(fstarnixrunner::PagerRegisterFileResponse {
93 vmo: Some(vmo),
94 ..Default::default()
95 })) {
96 Ok(_) => {}
97 Err(e) => {
98 log_error!("Error sending pager response {:?}", e);
99 break 'outer;
100 }
101 }
102 }
103 Err(e) => match responder.send(Err(e.into_raw())) {
104 Ok(_) => {}
105 Err(e) => {
106 log_error!("Error sending pager error response {:?}", e);
107 break 'outer;
108 }
109 },
110 };
111 }
112 fstarnixrunner::PagerRequest::RegisterFile { .. } => {
113 log_error!("Invalid RegisterFile request");
114 break 'outer;
115 }
116 _ => {}
117 }
118 }
119 pager.remove_filesystem(&*filesystem);
120}
121
122pub struct Pager {
124 pager: zx::Pager,
125 port: zx::Port,
126 zero_vmo: zx::Vmo,
127 next_filesystem_id: AtomicU32,
128 filesystems: LockDepMutex<HashMap<u32, Arc<Filesystem>>, PagerFilesystemsLock>,
129 page_refault_counter: PageRefaultCounter,
130}
131
132impl Pager {
133 pub fn new() -> Result<Self, Errno> {
134 Ok(Self {
135 pager: zx::Pager::create(zx::PagerOptions::empty()).map_err(|error| {
136 log_error!(error:?; "Pager::create failed");
137 errno!(EINVAL)
138 })?,
139 port: zx::Port::create(),
140 zero_vmo: with_zx_name(
141 zx::Vmo::create(ZERO_VMO_SIZE).map_err(|_| errno!(EINVAL))?,
142 b"starnix:ext4",
143 ),
144 next_filesystem_id: AtomicU32::new(1),
145 filesystems: Default::default(),
146 page_refault_counter: PageRefaultCounter::new().map_err(|error| {
147 log_error!(error:?; "PageRefaultCounter::new failed");
148 errno!(EINVAL)
149 })?,
150 })
151 }
152
153 pub fn page_refault_counter(&self) -> &PageRefaultCounter {
154 &self.page_refault_counter
155 }
156
157 pub fn start_threads(self: &Arc<Self>) {
159 for i in 0..PAGER_THREADS {
160 let this = self.clone();
161 let _ = std::thread::Builder::new().name(format!("pager-{}", i)).spawn(move || {
162 this.run_pager_thread();
163 });
164 }
165 }
166
167 pub fn run_pager_thread(&self) {
170 let transfer_vmo = with_zx_name(
171 zx::Vmo::create(TRANSFER_VMO_SIZE).expect("unable to create transfer vmo"),
172 b"starnix:ext4",
173 );
174 let transfer_vmo_addr = fuchsia_runtime::vmar_root_self()
175 .map(
176 0,
177 &transfer_vmo,
178 0,
179 TRANSFER_VMO_SIZE as usize,
180 zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE | zx::VmarFlags::ALLOW_FAULTS,
181 )
182 .expect("unable to map transfer vmo");
183 scopeguard::defer!({
184 let _ = unsafe {
186 fuchsia_runtime::vmar_root_self()
187 .unmap(transfer_vmo_addr, TRANSFER_VMO_SIZE as usize)
188 };
189 });
190 let split_key = |key: u64| -> (u32, u32) { ((key >> 32) as u32, key as u32) };
191 loop {
192 match self.port.wait(zx::MonotonicInstant::INFINITE) {
193 Ok(packet) => {
194 match packet.contents() {
195 zx::PacketContents::Pager(contents)
196 if contents.command() == ZX_PAGER_VMO_READ =>
197 {
198 fuchsia_trace::duration!(CATEGORY_STARNIX_PAGER, "vmo_read");
199 let (filesystem_num, inode_num) = split_key(packet.key());
200 let filesystem = self
201 .filesystems
202 .lock()
203 .get(&filesystem_num)
204 .cloned()
205 .expect("Unexpected packet key");
206 filesystem.receive_pager_packet(
207 inode_num,
208 contents,
209 &transfer_vmo,
210 transfer_vmo_addr,
211 );
212 }
213 zx::PacketContents::Pager(contents)
214 if contents.command() == ZX_PAGER_VMO_COMPLETE =>
215 {
216 fuchsia_trace::duration!(CATEGORY_STARNIX_PAGER, "vmo_complete");
217 }
220 zx::PacketContents::SignalOne(signals)
221 if signals.observed().contains(zx::Signals::VMO_ZERO_CHILDREN) =>
222 {
223 fuchsia_trace::duration!(
224 CATEGORY_STARNIX_PAGER,
225 "signal_zero_children"
226 );
227 let (filesystem_num, inode_num) = split_key(packet.key());
228 let filesystem = self.filesystems.lock().get(&filesystem_num).cloned();
232 if let Some(filesystem) = filesystem {
233 filesystem.on_zero_children(inode_num).expect("on_zero_children");
234 }
235 }
236 zx::PacketContents::User(_) => break,
237 _ => log_error!("Unexpected port packet: {:?}", packet.contents()),
238 }
239 }
240 Err(error) => log_error!(error:?; "Port::wait failed"),
241 }
242 }
243 log_debug!("Pager thread terminating");
244 }
245
246 fn create_pager_vmo(&self, key: u64, size: u64) -> Result<zx::Vmo, zx::Status> {
247 self.pager.create_vmo(zx::VmoOptions::empty(), &self.port, key, size)
248 }
249
250 fn allocate_filesystem_id(&self) -> u32 {
251 self.next_filesystem_id.fetch_add(1, Ordering::Relaxed)
252 }
253
254 fn add_filesystem(&self, filesystem: Arc<Filesystem>) {
255 self.filesystems.lock().insert(filesystem.id(), filesystem);
256 }
257
258 fn remove_filesystem(&self, filesystem: &Filesystem) {
259 self.filesystems.lock().remove(&filesystem.id());
260 }
261
262 pub fn terminate(&self) {
264 let up = zx::UserPacket::from_u8_array([0; 32]);
265 let packet = zx::Packet::from_user_packet(0, 0, up);
266 for _ in 0..PAGER_THREADS {
267 self.port.queue(&packet).unwrap();
268 }
269 }
270}
271
272pub struct Filesystem {
274 pager: Arc<Pager>,
275 backing_vmo: zx::Vmo,
276 block_size: u64,
277 files_by_inode: LockDepMutex<HashMap<u32, Arc<PagedFile>>, PagerFilesByInodeLock>,
278 id: u32,
279}
280
281impl Filesystem {
282 pub fn new(pager: Arc<Pager>, backing_vmo: zx::Vmo, block_size: u64) -> Result<Self, Errno> {
285 if block_size > 1024 * 1024 || !block_size.is_power_of_two() {
286 return error!(EINVAL, "Bad block size {block_size}");
287 }
288 let id = pager.allocate_filesystem_id();
289 Ok(Self { pager, backing_vmo, block_size, files_by_inode: Default::default(), id })
290 }
291
292 pub fn register(
294 &self,
295 name: &str,
296 inode_num: u32,
297 size: u64,
298 extents: Box<[PagerExtent]>,
299 ) -> Result<zx::Vmo, zx::Status> {
300 let (file, did_create) = {
301 match self.files_by_inode.lock().entry(inode_num) {
302 Entry::Occupied(o) => (o.get().clone(), false),
303 Entry::Vacant(v) => {
304 let num_chunks = size.div_ceil(READAHEAD_ALIGNMENT);
305 let chunks_supplied = AtomicBitVec::new(num_chunks);
306 (
307 v.insert(Arc::new(PagedFile {
308 vmo: self
309 .pager
310 .create_pager_vmo(self.port_key_for_inode(inode_num), size)?,
311 extents,
312 chunks_supplied,
313 }))
314 .clone(),
315 true,
316 )
317 }
318 }
319 };
320 let child_vmo = file.vmo.create_child(zx::VmoChildOptions::REFERENCE, 0, 0);
321 if did_create {
322 let set_up_vmo = |vmo| -> Result<(), zx::Status> {
323 self.watch_for_zero_children(vmo, inode_num)?;
324 vmo.set_name(&zx::Name::new_lossy(&format!("ext4!{}", name)))?;
325 Ok(())
326 };
327
328 if let Err(e) = set_up_vmo(&file.vmo) {
329 self.files_by_inode.lock().remove(&inode_num);
330 return Err(e);
331 }
332 }
333 child_vmo
334 }
335
336 fn receive_pager_packet(
337 &self,
338 inode_num: u32,
339 contents: zx::PagerPacket,
340 transfer_vmo: &zx::Vmo,
341 transfer_vmo_addr: usize,
342 ) {
343 let Some(file) = self.files_by_inode.lock().get(&inode_num).cloned() else {
344 return;
345 };
346
347 let requested_range = contents.range();
348
349 let readahead_start = (requested_range.start / READAHEAD_ALIGNMENT) * READAHEAD_ALIGNMENT;
351 let mut readahead_end = requested_range.end.next_multiple_of(READAHEAD_ALIGNMENT);
352
353 let vmo_size = match file.vmo.get_size() {
355 Ok(size) => size,
356 Err(status) => {
357 log_error!("Failed to get VMO size: {:?}", status);
358 let _ = self.pager.pager.op_range(
359 zx::PagerOp::Fail(zx::Status::IO),
360 &file.vmo,
361 contents.range(),
362 );
363 return;
364 }
365 };
366 readahead_end = std::cmp::min(readahead_end, vmo_size);
367
368 if readahead_end <= readahead_start {
369 return;
370 }
371
372 let supplied_end = self.supply_range(
373 &file,
374 readahead_start..readahead_end,
375 transfer_vmo,
376 transfer_vmo_addr,
377 );
378
379 self.record_refaults(&file, readahead_start..supplied_end);
380 }
381
382 fn supply_range(
386 &self,
387 file: &PagedFile,
388 range: Range<u64>,
389 transfer_vmo: &zx::Vmo,
390 transfer_vmo_addr: usize,
391 ) -> u64 {
392 let start_block = (range.start / self.block_size) as u32;
393 let mut ix = file.extents.partition_point(|e| e.logical.end <= start_block);
394
395 let buf = unsafe {
398 std::slice::from_raw_parts_mut(transfer_vmo_addr as *mut u8, TRANSFER_VMO_SIZE as usize)
399 };
400
401 let mut current_offset = range.start;
402 let mut supply_helper =
403 SupplyHelper::new(transfer_vmo, buf, &file.vmo, current_offset, &*self.pager);
404
405 while ix < file.extents.len() && current_offset < range.end {
406 let extent = &file.extents[ix];
407
408 let logical_start = extent.logical.start as u64 * self.block_size;
409
410 if current_offset < logical_start {
412 if let Err(e) = supply_helper.zero(logical_start - current_offset) {
413 let supplied_end = supply_helper.supplied_end();
414 supply_helper.fail_to(range.end, e);
415 return supplied_end;
416 }
417 current_offset = logical_start;
418 }
419
420 let end = std::cmp::min(extent.logical.end as u64 * self.block_size, range.end);
421
422 while current_offset < end {
423 let phys_offset =
424 extent.physical_block * self.block_size + current_offset - logical_start;
425
426 match supply_helper.fill_buf(|buf| {
427 let amount = std::cmp::min(buf.len() as u64, end - current_offset) as usize;
428 self.backing_vmo.read(&mut buf[..amount], phys_offset)?;
429 Ok(amount)
430 }) {
431 Ok(amount) => {
432 let _ = self.backing_vmo.op_range(
435 zx::VmoOp::DONT_NEED,
436 phys_offset,
437 amount as u64,
438 );
439 current_offset += amount as u64;
440 }
441 Err(e) => {
442 let supplied_end = supply_helper.supplied_end();
443 supply_helper.fail_to(range.end, e);
444 return supplied_end;
445 }
446 }
447 }
448
449 ix += 1;
450 }
451
452 if let Err(e) = supply_helper.finish(range.end) {
453 let supplied_end = supply_helper.supplied_end();
454 supply_helper.fail_to(range.end, e);
455 return supplied_end;
456 }
457
458 supply_helper.supplied_end()
459 }
460
461 fn record_refaults(&self, file: &PagedFile, supplied: Range<u64>) {
464 let page_size = *PAGE_SIZE;
465 let pages_per_chunk = READAHEAD_ALIGNMENT / page_size;
466
467 let first_chunk = supplied.start / READAHEAD_ALIGNMENT;
468 let full_end_chunk = supplied.end / READAHEAD_ALIGNMENT;
469
470 let mut refault_pages =
471 file.chunks_supplied.test_and_set_range(first_chunk, full_end_chunk) * pages_per_chunk;
472
473 let trailing_pages = (supplied.end % READAHEAD_ALIGNMENT) / page_size;
476 if trailing_pages > 0 {
477 refault_pages +=
478 file.chunks_supplied.test_and_set_range(full_end_chunk, full_end_chunk + 1)
479 * trailing_pages;
480 }
481
482 if refault_pages > 0 {
483 self.pager.page_refault_counter.increment(refault_pages, Ordering::Relaxed);
484 }
485 }
486
487 fn watch_for_zero_children(&self, vmo: &zx::Vmo, inode_num: u32) -> Result<(), zx::Status> {
488 vmo.wait_async(
489 &self.pager.port,
490 self.port_key_for_inode(inode_num),
491 zx::Signals::VMO_ZERO_CHILDREN,
492 zx::WaitAsyncOpts::empty(),
493 )
494 }
495
496 fn on_zero_children(&self, inode_num: u32) -> Result<(), Errno> {
497 let mut files = self.files_by_inode.lock();
498 let file = files.entry(inode_num);
499 if let Entry::Occupied(o) = file {
500 let vmo = &o.get().vmo;
501 match vmo.info() {
502 Ok(info) => {
503 if info.num_children == 0 {
504 o.remove();
506 } else {
507 if let Err(error) = self.watch_for_zero_children(vmo, inode_num) {
510 log_error!(
511 error:?;
512 "watch_for_zero_children failed"
513 );
514 }
515 }
516 }
517 Err(error) => log_error!(error:?; "Vmo::info failed"),
518 }
519 }
520 Ok(())
521 }
522
523 fn port_key_for_inode(&self, inode_num: u32) -> u64 {
524 (self.id as u64) << 32 | inode_num as u64
525 }
526
527 fn id(&self) -> u32 {
528 self.id
529 }
530}
531
532struct PagedFile {
534 vmo: zx::Vmo,
536
537 extents: Box<[PagerExtent]>,
540
541 chunks_supplied: AtomicBitVec,
544}
545
546pub struct PagerExtent {
548 pub logical: Range<u32>,
549 pub physical_block: u64,
550}
551
552struct SupplyHelper<'a> {
554 transfer_vmo: &'a zx::Vmo,
555 buffer: &'a mut [u8],
556 target_vmo: &'a zx::Vmo,
557 offset: u64,
558 pager: &'a Pager,
559 page_size: u64,
560 buf_len: usize,
561}
562
563impl<'a> SupplyHelper<'a> {
564 fn new(
565 transfer_vmo: &'a zx::Vmo,
566 buffer: &'a mut [u8],
567 target_vmo: &'a zx::Vmo,
568 offset: u64,
569 pager: &'a Pager,
570 ) -> Self {
571 Self { transfer_vmo, buffer, target_vmo, offset, pager, page_size: *PAGE_SIZE, buf_len: 0 }
572 }
573
574 fn supplied_end(&self) -> u64 {
576 self.offset
577 }
578
579 fn zero(&mut self, mut len: u64) -> Result<(), zx::Status> {
581 let unaligned = self.buf_len as u64 % self.page_size;
582 if unaligned > 0 {
583 let amount = std::cmp::min(self.page_size - unaligned, len);
584 self.buffer[self.buf_len..self.buf_len + amount as usize].fill(0);
585 self.buf_len += amount as usize;
586 len -= amount;
587 self.supply_pages()?;
588 }
589 while len >= self.page_size {
591 let amount =
592 if len >= ZERO_VMO_SIZE { ZERO_VMO_SIZE } else { len - len % self.page_size };
593 self.pager.pager.supply_pages(
594 self.target_vmo,
595 self.offset..self.offset + amount,
596 &self.pager.zero_vmo,
597 0,
598 )?;
599 self.offset += amount;
600 len -= amount;
601 }
602 self.buffer[self.buf_len..self.buf_len + len as usize].fill(0);
604 self.buf_len += len as usize;
605 Ok(())
606 }
607
608 fn supply_pages(&mut self) -> Result<(), zx::Status> {
610 if self.buf_len as u64 >= self.page_size {
611 let len = self.buf_len - self.buf_len % self.page_size as usize;
612 self.pager.pager.supply_pages(
613 self.target_vmo,
614 self.offset..self.offset + len as u64,
615 self.transfer_vmo,
616 0,
617 )?;
618 self.buffer.copy_within(len..self.buf_len, 0);
620 self.buf_len -= len;
621 self.offset += len as u64;
622 }
623 Ok(())
624 }
625
626 fn fill_buf(
628 &mut self,
629 f: impl FnOnce(&mut [u8]) -> Result<usize, zx::Status>,
630 ) -> Result<usize, zx::Status> {
631 let amount = f(&mut self.buffer[self.buf_len..])?;
632 self.buf_len += amount;
633 self.supply_pages()?;
634 Ok(amount)
635 }
636
637 fn finish(&mut self, mut end: u64) -> Result<(), zx::Status> {
639 let byte_offset = self.offset + self.buf_len as u64;
640 end = std::cmp::max(end, byte_offset);
641 end = end + self.page_size - 1;
642 end -= end % self.page_size;
643 self.zero(end - byte_offset)
644 }
645
646 fn fail_to(&mut self, end: u64, error: zx::Status) {
648 if self.offset < end {
649 log_warn!(error:?; "Failing page-in, range: {:?}", self.offset..end);
650 match self.pager.pager.op_range(
652 zx::PagerOp::Fail(zx::Status::IO),
653 self.target_vmo,
654 self.offset..end,
655 ) {
656 Ok(()) => {}
657 Err(error) => log_error!(error:?; "Failed to report error"),
658 }
659 self.offset = end;
660 self.buf_len = 0;
661 }
662 }
663}
664
665#[cfg(test)]
666mod tests {
667 use super::{Filesystem, Pager, PagerExtent, READAHEAD_ALIGNMENT};
668
669 use starnix_core::mm::PAGE_SIZE;
670 use std::sync::Arc;
671 use std::sync::atomic::Ordering;
672 use std::time::Duration;
673
674 fn sync_with_pager(filesystem: &Filesystem, inode: u32) {
679 let sync_vmo =
680 filesystem.register("sync", inode, 1, Box::new([])).expect("register failed");
681 let mut byte = [0u8; 1];
682 sync_vmo.read(&mut byte, 0).expect("sync read failed");
683 }
684
685 #[::fuchsia::test]
686 async fn test_pager() {
687 let backing_vmo = zx::Vmo::create(1 * 1024 * 1024).expect("Vmo::craete failed");
688 let backing_vmo_clone =
689 backing_vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("failed handle dup");
690
691 let pager = Arc::new(Pager::new().expect("Pager::new failed"));
692 let filesystem = Arc::new(
693 Filesystem::new(pager.clone(), backing_vmo_clone, 1024)
694 .expect("Filesystem::new failed"),
695 );
696
697 pager.add_filesystem(filesystem.clone());
698
699 {
700 pager.start_threads();
701
702 let vmo = filesystem.register("a".into(), 1, 5, Box::new([])).expect("register failed");
704
705 let mut buf = vec![1; 5];
706 vmo.read(&mut buf, 0).expect("read failed");
707
708 assert_eq!(&buf, &[0; 5]);
709
710 let vmo = filesystem
712 .register(
713 "b".into(),
714 2,
715 5,
716 Box::new([PagerExtent { logical: 0..1, physical_block: 0 }]),
717 )
718 .expect("register failed");
719 backing_vmo.write(b"hello", 0).expect("write failed");
720 vmo.read(&mut buf, 0).expect("read failed");
721
722 assert_eq!(&buf, b"hello");
723
724 let file_size = (6 + 1 + 5 + 4) * 1024 + 100;
727 let vmo = filesystem
728 .register(
729 "c".into(),
730 3,
731 file_size,
732 Box::new([
733 PagerExtent { logical: 6..7, physical_block: 0 },
734 PagerExtent { logical: 12..13, physical_block: 1 },
735 ]),
736 )
737 .expect("register failed");
738 backing_vmo.write(b"there", 1024).expect("write failed");
739 let mut buf = vec![1; file_size as usize];
740 vmo.read(&mut buf, 0).expect("read failed");
741
742 let mut expected = vec![0; file_size as usize];
743 expected[6 * 1024..6 * 1024 + 5].copy_from_slice(b"hello");
744 expected[12 * 1024..12 * 1024 + 5].copy_from_slice(b"there");
745 assert_eq!(&buf, &expected);
746
747 let vmo = filesystem
749 .register(
750 "d".into(),
751 4,
752 file_size,
753 Box::new([
754 PagerExtent { logical: 6..7, physical_block: 0 },
755 PagerExtent { logical: 12..13, physical_block: 1 },
756 ]),
757 )
758 .expect("register failed");
759
760 let offset = 9000;
761 let mut buf = vec![1; (file_size - offset) as usize];
762 vmo.read(&mut buf, offset).expect("read failed");
763
764 assert_eq!(&buf, &expected[offset as usize..]);
765
766 sync_with_pager(&filesystem, 100);
767
768 assert_eq!(pager.page_refault_counter().read(Ordering::Relaxed), 0);
770
771 let file = filesystem.files_by_inode.lock().get(&4).unwrap().clone();
772 let num_pages = file_size.div_ceil(*PAGE_SIZE);
773
774 filesystem.record_refaults(&file, 0..num_pages * *PAGE_SIZE);
777 assert_eq!(pager.page_refault_counter().read(Ordering::Relaxed), num_pages);
778
779 filesystem.record_refaults(&file, 0..0);
781 assert_eq!(pager.page_refault_counter().read(Ordering::Relaxed), num_pages);
782
783 filesystem.record_refaults(&file, 0..*PAGE_SIZE);
785 assert_eq!(pager.page_refault_counter().read(Ordering::Relaxed), num_pages + 1);
786
787 let pages_per_chunk = READAHEAD_ALIGNMENT / *PAGE_SIZE;
789 let multi_chunk_size = 2 * READAHEAD_ALIGNMENT + 3 * *PAGE_SIZE;
790 let _vmo = filesystem
791 .register("e".into(), 5, multi_chunk_size, Box::new([]))
792 .expect("register failed");
793 let multi_chunk_file = filesystem.files_by_inode.lock().get(&5).unwrap().clone();
794
795 filesystem.record_refaults(&multi_chunk_file, 0..multi_chunk_size);
796 assert_eq!(pager.page_refault_counter().read(Ordering::Relaxed), num_pages + 1);
797
798 filesystem.record_refaults(&multi_chunk_file, READAHEAD_ALIGNMENT..multi_chunk_size);
799 assert_eq!(
800 pager.page_refault_counter().read(Ordering::Relaxed),
801 num_pages + 1 + pages_per_chunk + 3
802 );
803 }
804
805 loop {
807 if filesystem.files_by_inode.lock().is_empty() {
808 break;
809 }
810 std::thread::sleep(Duration::from_millis(10));
812 }
813
814 let file_size = (6 + 1 + 5 + 4) * 1024 + 100;
817 let vmo = filesystem
818 .register(
819 "d".into(),
820 4,
821 file_size,
822 Box::new([
823 PagerExtent { logical: 6..7, physical_block: 0 },
824 PagerExtent { logical: 12..13, physical_block: 1 },
825 ]),
826 )
827 .expect("register failed");
828 let refaults_before = pager.page_refault_counter().read(Ordering::Relaxed);
829 let mut buf = vec![1; 100];
830 vmo.read(&mut buf, 0).expect("read failed");
831 sync_with_pager(&filesystem, 101);
832 assert_eq!(pager.page_refault_counter().read(Ordering::Relaxed), refaults_before);
833 }
834}