1use crate::fuchsia::errors::map_to_status;
6use crate::fuchsia::node::{FxNode, OpenedNode};
7use crate::fuchsia::profile::Recorder;
8use anyhow::Error;
9use bitflags::bitflags;
10use fuchsia_async::epoch::{Epoch, EpochGuard};
11use fuchsia_async::{self as fasync};
12use fuchsia_sync::{Mutex, MutexGuard};
13use fxfs::future_with_guard::FutureWithGuard;
14use fxfs::log::*;
15use fxfs::range::RangeExt;
16use fxfs::round::{round_down, round_up};
17use std::future::Future;
18use std::marker::PhantomData;
19use std::mem::MaybeUninit;
20use std::ops::Range;
21use std::sync::atomic::{AtomicU64, Ordering};
22use std::sync::{Arc, Weak};
23use storage_device::buffer;
24use storage_units::page_size;
25use vfs::execution_scope::ExecutionScope;
26use zx::sys::zx_page_request_command_t::{ZX_PAGER_VMO_DIRTY, ZX_PAGER_VMO_READ};
27use zx::{PacketContents, PagerPacket, SignalPacket};
28
29pub static STRONG_FILE_REFS: AtomicU64 = AtomicU64::new(0);
30
31fn watch_for_zero_children(file: &impl PagerBacked) -> Result<(), zx::Status> {
32 file.vmo().wait_async(
33 file.pager().executor.port(),
34 file.pager_packet_receiver_registration().key(),
35 zx::Signals::VMO_ZERO_CHILDREN,
36 zx::WaitAsyncOpts::empty(),
37 )
38}
39
40pub type PagerPacketReceiverRegistration<T> = fasync::ReceiverRegistration<PagerPacketReceiver<T>>;
41
42pub struct PagerPacketReceiver<T> {
44 file: Mutex<FileHolder<T>>,
45}
46
47pub struct PagerPacketReceiverLock<'a, T> {
49 _guard: MutexGuard<'a, FileHolder<T>>,
50 strong: bool,
51}
52
53impl<T> PagerPacketReceiverLock<'_, T> {
54 pub fn is_strong(&self) -> bool {
56 self.strong
57 }
58}
59
60impl<T: PagerBacked> PagerPacketReceiver<T> {
61 pub fn stop_watching_for_zero_children(&self) {
65 let mut file = self.file.lock();
66 if let FileHolder::Strong(strong) = &*file {
67 let weak = FileHolder::Weak(Arc::downgrade(&strong));
68 let FileHolder::Strong(strong) = std::mem::replace(&mut *file, weak) else {
69 unreachable!();
70 };
71 STRONG_FILE_REFS.fetch_sub(1, Ordering::Relaxed);
72 strong.on_zero_children();
73 }
74 }
75
76 pub fn set_receiver(&self, new_receiver: &Arc<T>) -> PagerPacketReceiverLock<'_, T> {
79 let mut receiver_lock = self.file.lock();
80 let strong = match &mut *receiver_lock {
81 FileHolder::Strong(arc) => {
82 *arc = new_receiver.clone();
83 true
84 }
85 FileHolder::Weak(arc) => {
86 *arc = Arc::downgrade(new_receiver);
87 false
88 }
89 };
90 PagerPacketReceiverLock { _guard: receiver_lock, strong }
91 }
92
93 fn receive_pager_packet(&self, contents: PagerPacket) {
94 let command = contents.command();
95 if command != ZX_PAGER_VMO_READ && command != ZX_PAGER_VMO_DIRTY {
96 return;
97 }
98
99 let (opened_file, epoch_guard) = {
100 let file_lock = self.file.lock();
101 let file = match &*file_lock {
102 FileHolder::Strong(file) => file.clone(),
103 FileHolder::Weak(file) => {
104 if let Some(file) = file.upgrade() {
105 file
106 } else {
107 error!("Received a page request for a file that is closed {:?}", contents);
108 return;
109 }
110 }
111 };
112
113 let epoch_guard = match command {
119 ZX_PAGER_VMO_READ => Some(Epoch::global().guard()),
123 _ => None,
124 };
125
126 let opened_file = match file.try_keep_open() {
129 Ok(opened) => opened,
130 Err(bare) => {
131 bare.pager().report_failure(
132 bare.vmo(),
133 contents.range(),
134 zx::Status::BAD_STATE,
135 );
136 return;
137 }
138 };
139 (opened_file, epoch_guard)
140 };
141
142 let Some(_scope_guard) = opened_file.pager().scope.try_active_guard() else {
144 opened_file.pager().report_failure(
147 opened_file.vmo(),
148 contents.range(),
149 zx::Status::BAD_STATE,
150 );
151 return;
152 };
153 match command {
154 ZX_PAGER_VMO_READ => {
155 let file_arc = opened_file.clone();
156 file_arc.page_in(PageInRange::new(
157 contents.range(),
158 opened_file,
159 epoch_guard.unwrap(),
160 ))
161 }
162 ZX_PAGER_VMO_DIRTY => {
163 let file_arc = opened_file.clone();
164 file_arc.mark_dirty(MarkDirtyRange::new(contents.range(), opened_file))
165 }
166 _ => unreachable!("Unhandled commands are filtered above"),
167 }
168 }
169
170 fn receive_signal_packet(&self, signals: SignalPacket) {
171 assert!(signals.observed().contains(zx::Signals::VMO_ZERO_CHILDREN));
172
173 let mut file = self.file.lock();
178 if let FileHolder::Strong(strong) = &*file {
179 let Some(_guard) = strong.pager().scope.try_active_guard() else {
187 info!("Ignoring zero-children notification due to shutting down");
188 return;
189 };
190 match strong.vmo().info() {
191 Ok(info) => {
192 if info.num_children == 0 {
193 let weak = FileHolder::Weak(Arc::downgrade(&strong));
194 let FileHolder::Strong(strong) = std::mem::replace(&mut *file, weak) else {
195 unreachable!();
196 };
197 STRONG_FILE_REFS.fetch_sub(1, Ordering::Relaxed);
198 strong.on_zero_children();
199 } else {
200 watch_for_zero_children(strong.as_ref()).unwrap();
202 }
203 }
204 Err(e) => error!(error:? = e; "Vmo::info failed"),
205 }
206 }
207 }
208}
209
210impl<T: PagerBacked> fasync::PacketReceiver for PagerPacketReceiver<T> {
211 fn receive_packet(&self, packet: zx::Packet) {
212 match packet.contents() {
213 PacketContents::Pager(contents) => {
214 self.receive_pager_packet(contents);
215 }
216 PacketContents::SignalOne(signals) => {
217 self.receive_signal_packet(signals);
218 }
219 _ => unreachable!(), }
221 }
222}
223
224pub struct Pager {
225 pager: zx::Pager,
226 scope: ExecutionScope,
227 executor: fasync::EHandle,
228 recorder: Mutex<Option<Box<dyn Recorder>>>,
229}
230
231enum FileHolder<T> {
236 Strong(Arc<T>),
237 Weak(Weak<T>),
238}
239
240impl Pager {
242 pub fn new(scope: ExecutionScope) -> Result<Self, Error> {
244 Ok(Pager {
245 pager: zx::Pager::create(zx::PagerOptions::empty())?,
246 scope,
247 executor: fasync::EHandle::local(),
248 recorder: Mutex::new(None),
249 })
250 }
251
252 fn spawn(&self, task: impl Future<Output = ()> + Send + 'static) {
254 if let Some(guard) = self.scope.try_active_guard() {
255 self.executor.spawn_detached(FutureWithGuard::new(guard, task));
256 }
257 }
258
259 pub fn set_recorder(&self, recorder: Option<Box<dyn Recorder>>) {
261 let _old = std::mem::replace(&mut (*self.recorder.lock()), recorder);
263 }
264
265 pub fn recorder(&self) -> MutexGuard<'_, Option<Box<dyn Recorder>>> {
267 self.recorder.lock()
268 }
269
270 pub fn record_page_in<P: PagerBacked>(&self, node: Arc<P>, range: Range<u64>) {
272 let mut recorder_holder = self.recorder.lock();
273 if let Some(recorder) = &mut (*recorder_holder) {
274 if let Err(_) = recorder.record(node, range.start) {
276 *recorder_holder = None;
277 }
278 }
279 }
280
281 pub fn create_vmo<T: PagerBacked>(
283 &self,
284 file: Weak<T>,
285 initial_size: u64,
286 vmo_options: zx::VmoOptions,
287 ) -> Result<(zx::Vmo, PagerPacketReceiverRegistration<T>), Error> {
288 let registration = self
289 .executor
290 .register_receiver(PagerPacketReceiver { file: Mutex::new(FileHolder::Weak(file)) });
291 Ok((
292 self.pager.create_vmo(
293 vmo_options,
294 self.executor.port(),
295 registration.key(),
296 initial_size,
297 )?,
298 registration,
299 ))
300 }
301
302 pub fn watch_for_zero_children(&self, file: &impl PagerBacked) -> Result<bool, Error> {
306 let mut file = file.pager_packet_receiver_registration().file.lock();
307
308 match &*file {
309 FileHolder::Weak(weak) => {
310 let strong = weak.upgrade().unwrap();
312
313 watch_for_zero_children(strong.as_ref())?;
314
315 STRONG_FILE_REFS.fetch_add(1, Ordering::Relaxed);
316 *file = FileHolder::Strong(strong);
317 Ok(true)
318 }
319 FileHolder::Strong(_) => Ok(false),
320 }
321 }
322
323 fn supply_pages(
326 &self,
327 vmo: &zx::Vmo,
328 range: Range<u64>,
329 transfer_vmo: &zx::Vmo,
330 transfer_offset: u64,
331 ) {
332 if let Err(e) = self.pager.supply_pages(vmo, range, transfer_vmo, transfer_offset) {
333 error!(error:? = e; "supply_pages failed");
334 }
335 }
336
337 fn report_failure(&self, vmo: &zx::Vmo, range: Range<u64>, status: zx::Status) {
341 let pager_status = match status {
342 zx::Status::IO_DATA_INTEGRITY => zx::Status::IO_DATA_INTEGRITY,
343 zx::Status::NO_SPACE => zx::Status::NO_SPACE,
344 zx::Status::FILE_BIG => zx::Status::BUFFER_TOO_SMALL,
345 zx::Status::IO
346 | zx::Status::IO_DATA_LOSS
347 | zx::Status::IO_INVALID
348 | zx::Status::IO_MISSED_DEADLINE
349 | zx::Status::IO_NOT_PRESENT
350 | zx::Status::IO_OVERRUN
351 | zx::Status::IO_REFUSED
352 | zx::Status::PEER_CLOSED => zx::Status::IO,
353 _ => zx::Status::BAD_STATE,
354 };
355 if let Err(e) = self.pager.op_range(zx::PagerOp::Fail(pager_status), vmo, range) {
356 error!(error:? = e; "op_range failed");
357 }
358 }
359
360 fn dirty_pages(&self, vmo: &zx::Vmo, range: Range<u64>) -> Result<(), zx::Status> {
363 self.pager.op_range(zx::PagerOp::Dirty, vmo, range).inspect_err(|error| {
364 if *error != zx::Status::NOT_FOUND {
368 error!(error:?; "dirty_pages failed");
369 }
370 })
371 }
372
373 pub fn writeback_begin(
376 &self,
377 vmo: &zx::Vmo,
378 range: Range<u64>,
379 options: zx::PagerWritebackBeginOptions,
380 ) {
381 if let Err(e) = self.pager.op_range(zx::PagerOp::WritebackBegin(options), vmo, range) {
382 error!(error:? = e; "writeback_begin failed");
383 }
384 }
385
386 pub fn writeback_end(&self, vmo: &zx::Vmo, range: Range<u64>) {
389 if let Err(e) = self.pager.op_range(zx::PagerOp::WritebackEnd, vmo, range) {
390 error!(error:? = e; "writeback_end failed");
391 }
392 }
393
394 pub fn query_dirty_ranges(
399 &self,
400 vmo: &zx::Vmo,
401 range: Range<u64>,
402 buffer: &mut [VmoDirtyRange],
403 ) -> Result<(usize, usize), zx::Status> {
404 let mut actual = 0;
405 let mut avail = 0;
406 let status = unsafe {
407 zx::sys::zx_pager_query_dirty_ranges(
410 self.pager.raw_handle(),
411 vmo.raw_handle(),
412 range.start,
413 range.end - range.start,
414 buffer.as_mut_ptr() as *mut u8,
415 std::mem::size_of_val(buffer),
416 &mut actual as *mut usize,
417 &mut avail as *mut usize,
418 )
419 };
420 zx::ok(status).map(|_| (actual, avail - actual))
421 }
422
423 pub fn query_vmo_stats(
427 &self,
428 vmo: &zx::Vmo,
429 options: PagerVmoStatsOptions,
430 ) -> Result<PagerVmoStats, zx::Status> {
431 #[repr(C)]
432 #[derive(Default)]
433 struct zx_pager_vmo_stats {
434 pub modified: u32,
435 }
436 const ZX_PAGER_VMO_STATS_MODIFIED: u32 = 1;
437 let mut vmo_stats = MaybeUninit::<zx_pager_vmo_stats>::uninit();
438 let status = unsafe {
439 zx::sys::zx_pager_query_vmo_stats(
442 self.pager.raw_handle(),
443 vmo.raw_handle(),
444 options.bits(),
445 vmo_stats.as_mut_ptr() as *mut u8,
446 std::mem::size_of::<zx_pager_vmo_stats>(),
447 )
448 };
449 zx::ok(status)?;
450 let vmo_stats = unsafe { vmo_stats.assume_init() };
451 Ok(PagerVmoStats { was_vmo_modified: vmo_stats.modified == ZX_PAGER_VMO_STATS_MODIFIED })
452 }
453
454 pub async fn page_in_barrier() {
455 Epoch::global().barrier().await;
456 }
457}
458
459pub trait PagerBacked: FxNode + Sync + Send + Sized + 'static {
461 fn try_keep_open(self: Arc<Self>) -> Result<OpenedNode<Self>, Arc<Self>>;
463
464 fn pager(&self) -> &Pager;
466
467 fn pager_packet_receiver_registration(&self) -> &PagerPacketReceiverRegistration<Self>;
469
470 fn vmo(&self) -> &zx::Vmo;
473
474 fn page_in(self: Arc<Self>, range: PageInRange<Self>);
478
479 fn mark_dirty(self: Arc<Self>, range: MarkDirtyRange<Self>);
483
484 fn on_zero_children(self: Arc<Self>);
486
487 fn byte_size(&self) -> u64;
489
490 fn aligned_read(
497 &self,
498 aligned_byte_range: std::ops::Range<u64>,
499 ) -> impl Future<Output = Result<buffer::Buffer<'_>, Error>> + Send;
500}
501
502pub fn default_page_in<P: PagerBacked>(
504 this: Arc<P>,
505 pager_range: PageInRange<P>,
506 read_ahead_size: u64,
507) {
508 fxfs_trace::duration!(
509 "start-page-in",
510 "offset" => pager_range.start(),
511 "len" => pager_range.len()
512 );
513
514 const ZERO_VMO_SIZE: u64 = 1_048_576;
515 static ZERO_VMO: std::sync::LazyLock<zx::Vmo> =
516 std::sync::LazyLock::new(|| zx::Vmo::create(ZERO_VMO_SIZE).unwrap());
517
518 assert!(pager_range.end() < i64::MAX as u64);
519
520 let page_aligned_size = page_size().align_up(this.byte_size()).unwrap();
536
537 let (read_range, zero_range) = pager_range.split(page_aligned_size);
541 if let Some(zero_range) = zero_range {
542 for range in zero_range.chunks(ZERO_VMO_SIZE) {
543 range.supply_pages(&ZERO_VMO, 0);
544 }
545 }
546
547 if let Some(read_range) = read_range {
548 let expanded_range_for_readahead = round_down(read_range.start(), read_ahead_size)
549 ..std::cmp::min(
550 round_up(read_range.end(), read_ahead_size).unwrap(),
551 page_aligned_size,
552 );
553 let read_range = read_range.expand(expanded_range_for_readahead);
554 for range in read_range.chunks(read_ahead_size) {
555 this.pager().record_page_in(this.clone(), range.range.clone());
563
564 this.pager().spawn(page_in_chunk(this.clone(), range));
565 }
566 }
567}
568
569#[fxfs_trace::trace("offset" => read_range.start(), "len" => read_range.len())]
570async fn page_in_chunk<P: PagerBacked>(this: Arc<P>, read_range: PageInRange<P>) {
571 let buffer = match this.aligned_read(read_range.range()).await {
572 Ok(v) => v,
573 Err(error) => {
574 error!(range:? = read_range.range(), error:?; "Failed to load range");
575 read_range.report_failure(map_to_status(error));
576 return;
577 }
578 };
579 assert!(
580 buffer.len() as u64 >= read_range.len(),
581 "A buffer smaller than requested was returned. requested: {}, returned: {}",
582 read_range.len(),
583 buffer.len()
584 );
585 read_range.supply_pages(buffer.allocator().buffer_source().vmo(), buffer.range().start as u64);
586}
587
588#[repr(C)]
590#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
591pub struct VmoDirtyRange {
592 offset: u64,
593 length: u64,
594 options: u64,
595}
596
597impl VmoDirtyRange {
598 pub fn range(&self) -> Range<u64> {
600 self.offset..(self.offset + self.length)
601 }
602
603 pub fn is_zero_range(&self) -> bool {
605 self.options & zx::sys::ZX_VMO_DIRTY_RANGE_IS_ZERO != 0
606 }
607}
608
609bitflags! {
610 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
612 #[repr(transparent)]
613 pub struct PagerVmoStatsOptions: u32 {
614 const RESET_VMO_STATS = 1;
616 }
617}
618
619#[derive(Debug)]
621pub struct PagerVmoStats {
622 was_vmo_modified: bool,
623}
624
625impl PagerVmoStats {
626 pub fn was_vmo_modified(&self) -> bool {
628 self.was_vmo_modified
629 }
630}
631
632pub trait PagerRequestType {
634 fn request_type_name() -> &'static str;
636}
637
638pub struct PageInRequest;
640
641impl PagerRequestType for PageInRequest {
642 fn request_type_name() -> &'static str {
643 "PageInRequest"
644 }
645}
646
647pub type PageInRange<T> = PagerRange<T, PageInRequest>;
650
651impl<T: PagerBacked> PageInRange<T> {
652 pub fn new(range: Range<u64>, file: OpenedNode<T>, epoch_guard: EpochGuard<'static>) -> Self {
654 debug_assert!(page_size().is_aligned(&range), "{range:?} is not page aligned",);
655 Self {
656 range,
657 inner: Some(PagerRangeInner { file, _epoch_guard: Some(epoch_guard) }),
658 _request_type: PhantomData,
659 }
660 }
661
662 pub fn supply_pages(mut self, transfer_vmo: &zx::Vmo, transfer_offset: u64) {
665 let inner = self.inner.take().unwrap();
666 inner.file.pager().supply_pages(
667 inner.file.vmo(),
668 self.range.clone(),
669 transfer_vmo,
670 transfer_offset,
671 );
672 }
673}
674
675#[derive(Debug)]
677pub struct MarkDirtyRequest;
678
679impl PagerRequestType for MarkDirtyRequest {
680 fn request_type_name() -> &'static str {
681 "MarkDirtyRequest"
682 }
683}
684
685pub type MarkDirtyRange<T> = PagerRange<T, MarkDirtyRequest>;
688
689impl<T: PagerBacked> MarkDirtyRange<T> {
690 pub fn new(range: Range<u64>, file: OpenedNode<T>) -> Self {
692 debug_assert!(page_size().is_aligned(&range), "{range:?} is not page aligned");
693 Self {
694 range,
695 inner: Some(PagerRangeInner { file, _epoch_guard: None }),
696 _request_type: PhantomData,
697 }
698 }
699
700 pub fn dirty_pages(mut self) -> Result<(), zx::Status> {
703 let inner = self.inner.take().unwrap();
704 inner.file.pager().dirty_pages(inner.file.vmo(), self.range.clone())
705 }
706}
707
708struct PagerRangeInner<T: PagerBacked> {
709 file: OpenedNode<T>,
710
711 _epoch_guard: Option<EpochGuard<'static>>,
714}
715
716impl<T: PagerBacked> Clone for PagerRangeInner<T> {
717 fn clone(&self) -> Self {
718 Self { file: self.file.dup(), _epoch_guard: self._epoch_guard.clone() }
719 }
720}
721
722pub struct PagerRange<T: PagerBacked, U: PagerRequestType> {
725 range: Range<u64>,
726
727 inner: Option<PagerRangeInner<T>>,
729
730 _request_type: PhantomData<U>,
731}
732
733impl<T: PagerBacked, U: PagerRequestType> PagerRange<T, U> {
734 pub fn file(&self) -> &OpenedNode<T> {
735 &self.inner.as_ref().unwrap().file
736 }
737
738 pub fn split(mut self, split_point: u64) -> (Option<Self>, Option<Self>) {
742 let inner = self.inner.take().unwrap();
743 let (left, right) = self.range.clone().split(split_point);
744 let right = right.map(|range| Self {
745 range,
746 inner: Some(inner.clone()),
747 _request_type: PhantomData,
748 });
749 let left = left.map(|range| Self { range, inner: Some(inner), _request_type: PhantomData });
750 (left, right)
751 }
752
753 pub fn expand(mut self, new_range: Range<u64>) -> Self {
756 assert!(
757 self.range.start >= new_range.start && self.range.end <= new_range.end,
758 "{:?} is not a subset of {:?}",
759 self.range,
760 new_range
761 );
762 debug_assert!(page_size().is_aligned(&new_range), "{new_range:?} is not page aligned");
763 self.range = new_range;
764 self
765 }
766
767 pub fn chunks(mut self, chunk_size: u64) -> PagerRangeChunksIter<T, U> {
772 debug_assert!(
773 page_size().is_aligned(chunk_size),
774 "{chunk_size} is not a multiple of the page size"
775 );
776 PagerRangeChunksIter {
777 start: self.range.start,
778 end: self.range.end,
779 chunk_size: chunk_size,
780 inner: self.inner.take(),
781 _request_type: PhantomData,
782 }
783 }
784
785 #[inline]
786 pub fn start(&self) -> u64 {
787 self.range.start
788 }
789
790 #[inline]
791 pub fn end(&self) -> u64 {
792 self.range.end
793 }
794
795 #[inline]
796 pub fn len(&self) -> u64 {
797 self.range.end - self.range.start
798 }
799
800 #[inline]
801 pub fn range(&self) -> Range<u64> {
802 self.range.clone()
803 }
804
805 pub fn report_failure(mut self, status: zx::Status) {
808 let inner = self.inner.take().unwrap();
809 inner.file.pager().report_failure(inner.file.vmo(), self.range.clone(), status);
810 }
811
812 #[cfg(test)]
814 fn consume(mut self) {
815 self.inner.take().unwrap();
816 }
817}
818
819impl<T: PagerBacked, U: PagerRequestType> Drop for PagerRange<T, U> {
820 fn drop(&mut self) {
821 if let Some(inner) = &self.inner {
822 let request_type = U::request_type_name();
823 let range = self.range.clone();
824 let key = inner.file.pager_packet_receiver_registration().key();
825 if cfg!(debug_assertions) {
826 if !std::thread::panicking() {
830 panic!(
831 "PagerRange was dropped without sending a response, \
832 request_type={request_type}, range={range:?}, key={key}",
833 );
834 }
835 } else {
836 error!(
837 "PagerRange was dropped without sending a response, \
838 request_type={request_type}, range={range:?}, key={key}",
839 );
840 inner.file.pager().report_failure(inner.file.vmo(), range, zx::Status::BAD_STATE);
841 }
842 }
843 }
844}
845
846pub struct PagerRangeChunksIter<T: PagerBacked, U: PagerRequestType> {
849 start: u64,
850 end: u64,
851 chunk_size: u64,
852 inner: Option<PagerRangeInner<T>>,
854 _request_type: PhantomData<U>,
855}
856
857impl<T: PagerBacked, U: PagerRequestType> Iterator for PagerRangeChunksIter<T, U> {
858 type Item = PagerRange<T, U>;
859 fn next(&mut self) -> Option<Self::Item> {
860 if self.start == self.end {
861 None
862 } else if self.start + self.chunk_size >= self.end {
863 let next = Self::Item {
864 range: self.start..self.end,
865 inner: self.inner.take(),
866 _request_type: PhantomData,
867 };
868 self.start = self.end;
869 Some(next)
870 } else {
871 let next_end = self.start + self.chunk_size;
872 let next = Self::Item {
873 range: self.start..next_end,
874 inner: self.inner.clone(),
875 _request_type: PhantomData,
876 };
877 self.start = next_end;
878 Some(next)
879 }
880 }
881}
882
883impl<T: PagerBacked, U: PagerRequestType> Drop for PagerRangeChunksIter<T, U> {
884 fn drop(&mut self) {
885 if self.start != self.end {
886 let request_type = U::request_type_name();
887 let remaining = self.start..self.end;
888 let inner = self.inner.take().unwrap();
889 let key = inner.file.pager_packet_receiver_registration().key();
890 if cfg!(debug_assertions) {
891 if !std::thread::panicking() {
895 panic!(
896 "PagerRangeChunksIter was dropped without being fully consumed, \
897 request_type={request_type}, remaining={remaining:?}, key={key}",
898 );
899 }
900 } else {
901 error!(
902 "PagerRangeChunksIter was dropped without being fully consumed, \
903 request_type={request_type}, remaining={remaining:?}, key={key}",
904 );
905 inner.file.pager().report_failure(
906 inner.file.vmo(),
907 remaining,
908 zx::Status::BAD_STATE,
909 );
910 }
911 }
912 }
913}
914
915#[cfg(test)]
916mod tests {
917 use super::*;
918 use futures::StreamExt;
919 use futures::channel::mpsc;
920 use fxfs_macros::ToWeakNode;
921
922 fn page_size() -> u64 {
923 storage_units::page_size().get()
924 }
925
926 #[derive(Clone, Debug, PartialEq, Eq)]
927 enum PagerRequest {
928 PageIn(Range<u64>),
929 Dirty(Range<u64>),
930 }
931
932 #[derive(ToWeakNode)]
933 struct MockFile {
934 vmo: zx::Vmo,
935 pager_packet_receiver_registration: PagerPacketReceiverRegistration<Self>,
936 pager: Arc<Pager>,
937 pager_requests: Mutex<Vec<PagerRequest>>,
939 }
940
941 impl MockFile {
942 fn new(pager: Arc<Pager>) -> Arc<Self> {
943 Self::new_with_size_and_type(pager, page_size(), zx::VmoOptions::UNBOUNDED)
944 }
945
946 fn new_with_size_and_type(
947 pager: Arc<Pager>,
948 size: u64,
949 vmo_type: zx::VmoOptions,
950 ) -> Arc<Self> {
951 Arc::new_cyclic(|weak| {
952 let (vmo, pager_packet_receiver_registration) = pager
953 .create_vmo(weak.clone(), size, vmo_type | zx::VmoOptions::TRAP_DIRTY)
954 .unwrap();
955 Self {
956 pager,
957 vmo,
958 pager_packet_receiver_registration,
959 pager_requests: Default::default(),
960 }
961 })
962 }
963
964 fn pager_requests(&self, reset: bool) -> Vec<PagerRequest> {
966 if reset {
967 std::mem::take(&mut *self.pager_requests.lock())
968 } else {
969 self.pager_requests.lock().clone()
970 }
971 }
972 }
973
974 impl FxNode for MockFile {
975 fn object_id(&self) -> u64 {
976 unimplemented!();
977 }
978
979 fn parent(&self) -> Option<Arc<crate::directory::FxDirectory>> {
980 unimplemented!();
981 }
982
983 fn set_parent(&self, _parent: Arc<crate::directory::FxDirectory>) {
984 unimplemented!();
985 }
986
987 fn open_count_add_one(&self) {}
988
989 fn open_count_sub_one(self: Arc<Self>) {}
990
991 fn object_descriptor(&self) -> fxfs::object_store::ObjectDescriptor {
992 unimplemented!();
993 }
994 }
995
996 impl PagerBacked for MockFile {
997 fn try_keep_open(self: Arc<Self>) -> Result<OpenedNode<Self>, Arc<Self>> {
998 Ok(OpenedNode(self))
999 }
1000
1001 fn pager(&self) -> &Pager {
1002 &self.pager
1003 }
1004
1005 fn pager_packet_receiver_registration(&self) -> &PagerPacketReceiverRegistration<Self> {
1006 &self.pager_packet_receiver_registration
1007 }
1008
1009 fn vmo(&self) -> &zx::Vmo {
1010 &self.vmo
1011 }
1012
1013 fn page_in(self: Arc<Self>, range: PageInRange<Self>) {
1014 let aux_vmo = zx::Vmo::create(range.len()).unwrap();
1015 self.pager_requests.lock().push(PagerRequest::PageIn(range.range()));
1016 range.supply_pages(&aux_vmo, 0);
1017 }
1018
1019 fn mark_dirty(self: Arc<Self>, range: MarkDirtyRange<Self>) {
1020 self.pager_requests.lock().push(PagerRequest::Dirty(range.range()));
1021 let _ = range.dirty_pages();
1022 }
1023
1024 fn on_zero_children(self: Arc<Self>) {}
1025
1026 fn byte_size(&self) -> u64 {
1027 unimplemented!();
1028 }
1029 async fn aligned_read(
1030 &self,
1031 _aligned_byte_range: std::ops::Range<u64>,
1032 ) -> Result<buffer::Buffer<'_>, Error> {
1033 unimplemented!();
1034 }
1035 }
1036
1037 #[derive(ToWeakNode)]
1038 struct OnZeroChildrenFile {
1039 pager: Arc<Pager>,
1040 vmo: zx::Vmo,
1041 pager_packet_receiver_registration: PagerPacketReceiverRegistration<Self>,
1042 sender: Mutex<mpsc::UnboundedSender<()>>,
1043 }
1044
1045 impl OnZeroChildrenFile {
1046 fn new(pager: Arc<Pager>, sender: mpsc::UnboundedSender<()>) -> Arc<Self> {
1047 Arc::new_cyclic(|weak| {
1048 let (vmo, pager_packet_receiver_registration) =
1049 pager.create_vmo(weak.clone(), page_size(), zx::VmoOptions::empty()).unwrap();
1050 Self { pager, vmo, pager_packet_receiver_registration, sender: Mutex::new(sender) }
1051 })
1052 }
1053 }
1054
1055 impl FxNode for OnZeroChildrenFile {
1056 fn object_id(&self) -> u64 {
1057 unimplemented!();
1058 }
1059
1060 fn parent(&self) -> Option<Arc<crate::directory::FxDirectory>> {
1061 unimplemented!();
1062 }
1063
1064 fn set_parent(&self, _parent: Arc<crate::directory::FxDirectory>) {
1065 unimplemented!();
1066 }
1067
1068 fn open_count_add_one(&self) {}
1069
1070 fn open_count_sub_one(self: Arc<Self>) {}
1071
1072 fn object_descriptor(&self) -> fxfs::object_store::ObjectDescriptor {
1073 unimplemented!();
1074 }
1075 }
1076
1077 impl PagerBacked for OnZeroChildrenFile {
1078 fn try_keep_open(self: Arc<Self>) -> Result<OpenedNode<Self>, Arc<Self>> {
1079 Ok(OpenedNode(self))
1080 }
1081
1082 fn pager(&self) -> &Pager {
1083 &self.pager
1084 }
1085
1086 fn pager_packet_receiver_registration(&self) -> &PagerPacketReceiverRegistration<Self> {
1087 &self.pager_packet_receiver_registration
1088 }
1089
1090 fn vmo(&self) -> &zx::Vmo {
1091 &self.vmo
1092 }
1093
1094 fn page_in(self: Arc<Self>, _range: PageInRange<Self>) {
1095 unreachable!();
1096 }
1097
1098 fn mark_dirty(self: Arc<Self>, _range: MarkDirtyRange<Self>) {
1099 unreachable!();
1100 }
1101
1102 fn on_zero_children(self: Arc<Self>) {
1103 self.sender.lock().unbounded_send(()).unwrap();
1104 }
1105 fn byte_size(&self) -> u64 {
1106 unreachable!();
1107 }
1108 async fn aligned_read(
1109 &self,
1110 _aligned_byte_range: std::ops::Range<u64>,
1111 ) -> Result<buffer::Buffer<'_>, Error> {
1112 unreachable!();
1113 }
1114 }
1115
1116 #[fuchsia::test(threads = 2)]
1117 async fn test_watch_for_zero_children() {
1118 let (sender, mut receiver) = mpsc::unbounded();
1119 let scope = ExecutionScope::new();
1120 let pager = Arc::new(Pager::new(scope.clone()).unwrap());
1121 let file = OnZeroChildrenFile::new(pager.clone(), sender);
1122 {
1123 let _child_vmo = file
1124 .vmo()
1125 .create_child(
1126 zx::VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE,
1127 0,
1128 file.vmo().get_content_size().unwrap(),
1129 )
1130 .unwrap();
1131 assert!(pager.watch_for_zero_children(file.as_ref()).unwrap());
1132 }
1133 receiver.next().await.unwrap();
1135
1136 scope.wait().await;
1137 }
1138
1139 #[fuchsia::test(threads = 2)]
1140 async fn test_multiple_watch_for_zero_children_calls() {
1141 let (sender, mut receiver) = mpsc::unbounded();
1142 let scope = ExecutionScope::new();
1143 let pager = Arc::new(Pager::new(scope.clone()).unwrap());
1144 let file = OnZeroChildrenFile::new(pager.clone(), sender);
1145 {
1146 let _child_vmo = file
1147 .vmo()
1148 .create_child(
1149 zx::VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE,
1150 0,
1151 file.vmo().get_content_size().unwrap(),
1152 )
1153 .unwrap();
1154 assert!(pager.watch_for_zero_children(file.as_ref()).unwrap());
1155 assert!(!pager.watch_for_zero_children(file.as_ref()).unwrap());
1157 }
1158 receiver.next().await.unwrap();
1159
1160 assert!(pager.watch_for_zero_children(file.as_ref()).unwrap());
1164
1165 file.pager_packet_receiver_registration.stop_watching_for_zero_children();
1166
1167 scope.wait().await;
1168 }
1169
1170 #[fuchsia::test(threads = 2)]
1171 async fn test_status_code_mapping() {
1172 #[derive(ToWeakNode)]
1173 struct StatusCodeFile {
1174 vmo: zx::Vmo,
1175 pager: Arc<Pager>,
1176 status_code: Mutex<zx::Status>,
1177 pager_packet_receiver_registration: PagerPacketReceiverRegistration<Self>,
1178 }
1179
1180 impl FxNode for StatusCodeFile {
1181 fn object_id(&self) -> u64 {
1182 unimplemented!();
1183 }
1184
1185 fn parent(&self) -> Option<Arc<crate::directory::FxDirectory>> {
1186 unimplemented!();
1187 }
1188
1189 fn set_parent(&self, _parent: Arc<crate::directory::FxDirectory>) {
1190 unimplemented!();
1191 }
1192
1193 fn open_count_add_one(&self) {}
1194
1195 fn open_count_sub_one(self: Arc<Self>) {}
1196
1197 fn object_descriptor(&self) -> fxfs::object_store::ObjectDescriptor {
1198 unimplemented!();
1199 }
1200 }
1201
1202 impl PagerBacked for StatusCodeFile {
1203 fn try_keep_open(self: Arc<Self>) -> Result<OpenedNode<Self>, Arc<Self>> {
1204 Ok(OpenedNode(self))
1205 }
1206
1207 fn pager(&self) -> &Pager {
1208 &self.pager
1209 }
1210
1211 fn pager_packet_receiver_registration(&self) -> &PagerPacketReceiverRegistration<Self> {
1212 &self.pager_packet_receiver_registration
1213 }
1214
1215 fn vmo(&self) -> &zx::Vmo {
1216 &self.vmo
1217 }
1218
1219 fn page_in(self: Arc<Self>, range: PageInRange<Self>) {
1220 range.report_failure(*self.status_code.lock());
1221 }
1222
1223 fn mark_dirty(self: Arc<Self>, _range: MarkDirtyRange<Self>) {
1224 unreachable!();
1225 }
1226
1227 fn on_zero_children(self: Arc<Self>) {
1228 unreachable!();
1229 }
1230
1231 fn byte_size(&self) -> u64 {
1232 unreachable!();
1233 }
1234
1235 async fn aligned_read(
1236 &self,
1237 _aligned_byte_range: std::ops::Range<u64>,
1238 ) -> Result<buffer::Buffer<'_>, Error> {
1239 unreachable!();
1240 }
1241 }
1242
1243 let scope = ExecutionScope::new();
1244 let pager = Arc::new(Pager::new(scope.clone()).unwrap());
1245 let file = Arc::new_cyclic(|weak| {
1246 let (vmo, pager_packet_receiver_registration) =
1247 pager.create_vmo(weak.clone(), page_size(), zx::VmoOptions::empty()).unwrap();
1248 StatusCodeFile {
1249 vmo,
1250 pager: pager.clone(),
1251 status_code: Mutex::new(zx::Status::INTERNAL),
1252 pager_packet_receiver_registration,
1253 }
1254 });
1255
1256 fn check_mapping(
1257 file: &StatusCodeFile,
1258 failure_code: zx::Status,
1259 expected_code: zx::Status,
1260 ) {
1261 {
1262 *file.status_code.lock() = failure_code;
1263 }
1264 let mut buf = [0u8; 8];
1265 assert_eq!(file.vmo().read(&mut buf, 0).unwrap_err(), expected_code);
1266 }
1267 check_mapping(&file, zx::Status::IO_DATA_INTEGRITY, zx::Status::IO_DATA_INTEGRITY);
1268 check_mapping(&file, zx::Status::NO_SPACE, zx::Status::NO_SPACE);
1269 check_mapping(&file, zx::Status::FILE_BIG, zx::Status::BUFFER_TOO_SMALL);
1270 check_mapping(&file, zx::Status::IO, zx::Status::IO);
1271 check_mapping(&file, zx::Status::IO_DATA_LOSS, zx::Status::IO);
1272 check_mapping(&file, zx::Status::NOT_EMPTY, zx::Status::BAD_STATE);
1273 check_mapping(&file, zx::Status::BAD_STATE, zx::Status::BAD_STATE);
1274
1275 scope.wait().await;
1276 }
1277
1278 #[fuchsia::test(threads = 2)]
1279 async fn test_query_vmo_stats() {
1280 let scope = ExecutionScope::new();
1281 let pager = Arc::new(Pager::new(scope.clone()).unwrap());
1282 let file = MockFile::new(pager.clone());
1283
1284 let stats = pager.query_vmo_stats(file.vmo(), PagerVmoStatsOptions::empty()).unwrap();
1285 assert!(!stats.was_vmo_modified());
1287
1288 file.vmo().write(&[0, 1, 2, 3, 4], 0).unwrap();
1289 let stats = pager.query_vmo_stats(file.vmo(), PagerVmoStatsOptions::empty()).unwrap();
1290 assert!(stats.was_vmo_modified());
1291
1292 let stats =
1294 pager.query_vmo_stats(file.vmo(), PagerVmoStatsOptions::RESET_VMO_STATS).unwrap();
1295 assert!(stats.was_vmo_modified());
1297
1298 let stats = pager.query_vmo_stats(file.vmo(), PagerVmoStatsOptions::empty()).unwrap();
1299 assert!(!stats.was_vmo_modified());
1300
1301 scope.wait().await;
1302 }
1303
1304 #[fuchsia::test(threads = 2)]
1305 async fn test_query_dirty_ranges() {
1306 let scope = ExecutionScope::new();
1315 let pager = Arc::new(Pager::new(scope.clone()).unwrap());
1316 let file = MockFile::new_with_size_and_type(
1317 pager.clone(),
1318 page_size() + page_size() / 2,
1319 zx::VmoOptions::UNBOUNDED,
1320 );
1321 let mut buffer = vec![VmoDirtyRange::default(); 2];
1322
1323 let page_size = page_size();
1324 assert_eq!(file.vmo().get_content_size().unwrap(), page_size + page_size / 2);
1325
1326 let (actual, remaining) =
1327 pager.query_dirty_ranges(file.vmo(), 0..page_size * 100, &mut buffer).unwrap();
1328 assert_eq!(actual, 0);
1329 assert_eq!(remaining, 0);
1330
1331 file.vmo().set_stream_size(page_size * 7 + page_size / 2).unwrap();
1333
1334 let (actual, remaining) =
1335 pager.query_dirty_ranges(file.vmo(), 0..page_size * 100, &mut buffer).unwrap();
1336 assert_eq!(actual, 2);
1337 assert_eq!(remaining, 0);
1338 assert_eq!(buffer[0].range(), page_size..page_size * 2);
1340 assert!(!buffer[0].is_zero_range());
1341 assert_eq!(buffer[1].range(), page_size * 2..page_size * 8);
1343 assert!(buffer[1].is_zero_range());
1344
1345 assert_eq!(
1348 file.pager_requests(true),
1349 vec![
1350 PagerRequest::PageIn(page_size * 1..page_size * 2),
1351 PagerRequest::Dirty(page_size * 1..page_size * 2),
1352 ]
1353 );
1354
1355 file.vmo().write(&[1, 2, 3, 4], page_size).unwrap();
1357 file.vmo().write(&[1, 2, 3, 4], page_size * 2).unwrap();
1358 file.vmo().write(&[1, 2, 3, 4], page_size * 4).unwrap();
1359
1360 assert_eq!(
1363 file.pager_requests(true),
1364 vec![
1365 PagerRequest::Dirty(page_size * 2..page_size * 3),
1366 PagerRequest::Dirty(page_size * 4..page_size * 5)
1367 ]
1368 );
1369
1370 let (actual, remaining) =
1371 pager.query_dirty_ranges(file.vmo(), 0..page_size * 7, &mut buffer).unwrap();
1372 assert_eq!(actual, 2);
1373 assert_eq!(remaining, 2);
1374 assert_eq!(buffer[0].range(), page_size..(page_size * 3));
1376 assert!(!buffer[0].is_zero_range());
1377 assert_eq!(buffer[1].range(), (page_size * 3)..(page_size * 4));
1379 assert!(buffer[1].is_zero_range());
1380
1381 let (actual, remaining) = pager
1382 .query_dirty_ranges(file.vmo(), page_size * 4..page_size * 7, &mut buffer)
1383 .unwrap();
1384 assert_eq!(actual, 2);
1385 assert_eq!(remaining, 0);
1386 assert_eq!(buffer[0].range(), (page_size * 4)..(page_size * 5));
1388 assert!(!buffer[0].is_zero_range());
1389 assert_eq!(buffer[1].range(), (page_size * 5)..(page_size * 7));
1391 assert!(buffer[1].is_zero_range());
1392
1393 let mut read_buf = vec![0u8; page_size as usize];
1395 file.vmo().read(&mut read_buf, page_size * 3).expect("read");
1396 let expected = vec![0u8; page_size as usize];
1397 assert_eq!(read_buf, expected);
1398 assert_eq!(file.pager_requests(true), vec![]);
1399
1400 scope.wait().await;
1401 }
1402
1403 #[fuchsia::test(threads = 2)]
1404 async fn test_zero_grown_vmo() {
1405 let scope = ExecutionScope::new();
1407 let pager = Arc::new(Pager::new(scope.clone()).unwrap());
1408 let file = MockFile::new(pager.clone());
1409
1410 let write_buf = vec![0xff; page_size() as usize * 2];
1411 file.vmo().set_stream_size(page_size() * 2).expect("grow");
1412 file.vmo().write(&write_buf, 0).expect("write");
1413 let mut read_buf = vec![0u8; page_size() as usize * 2];
1414 file.vmo().read(&mut read_buf, 0).expect("read");
1416 assert_eq!(read_buf, write_buf);
1417
1418 file.vmo().set_stream_size(page_size() + 1).expect("shrink");
1420 file.vmo().write(&[0xff; 3], page_size() + 2).expect("write after shrink");
1421 file.vmo().set_stream_size(page_size() + 4).expect("grow again");
1423 let mut read_buf = vec![0u8; page_size() as usize];
1424 file.vmo().read(&mut read_buf, page_size()).expect("read");
1425 let mut expected = vec![0u8; page_size() as usize];
1426 expected[0] = 0xff;
1427 assert_eq!(read_buf, expected);
1428
1429 scope.wait().await;
1430 }
1431
1432 #[fuchsia::test]
1433 async fn test_pager_range_chunks_iter_chunks() {
1434 let scope = ExecutionScope::new();
1435 let pager = Arc::new(Pager::new(scope).unwrap());
1436 let file = MockFile::new(pager.clone());
1437
1438 let pager_range =
1439 PageInRange::new(0..page_size() * 5, OpenedNode::new(file), Epoch::global().guard());
1440 let ranges: Vec<Range<u64>> = pager_range
1441 .chunks(page_size() * 2)
1442 .map(|pager_range| {
1443 let range = pager_range.range();
1444 pager_range.consume();
1445 range
1446 })
1447 .collect();
1448 assert_eq!(
1449 ranges,
1450 [
1451 0..page_size() * 2,
1452 page_size() * 2..page_size() * 4,
1453 page_size() * 4..page_size() * 5
1454 ]
1455 );
1456 }
1457
1458 #[fuchsia::test]
1459 async fn test_pager_range_split() {
1460 let scope = ExecutionScope::new();
1461 let pager = Arc::new(Pager::new(scope).unwrap());
1462 let file = MockFile::new(pager.clone());
1463
1464 let pager_range =
1465 PageInRange::new(0..page_size() * 10, OpenedNode::new(file), Epoch::global().guard());
1466 let (left, right) = pager_range.split(page_size() * 5);
1467 let (left, right) = (left.unwrap(), right.unwrap());
1468 assert_eq!(left.range(), 0..page_size() * 5);
1469 assert_eq!(right.range(), page_size() * 5..page_size() * 10);
1470
1471 left.consume();
1472 right.consume();
1473 }
1474
1475 #[fuchsia::test]
1476 #[should_panic(expected = "0..8192 is not a subset of 0..4096")]
1477 async fn test_pager_range_bad_expand_panics() {
1478 let scope = ExecutionScope::new();
1479 let pager = Arc::new(Pager::new(scope).unwrap());
1480 let file = MockFile::new(pager.clone());
1481
1482 let pager_range =
1483 PageInRange::new(0..page_size() * 2, OpenedNode::new(file), Epoch::global().guard());
1484 pager_range.expand(0..page_size()).consume();
1485 }
1486
1487 #[derive(ToWeakNode)]
1488 struct PagerRangeTestFile {
1489 vmo: zx::Vmo,
1490 pager_packet_receiver_registration: PagerPacketReceiverRegistration<Self>,
1491 pager: Pager,
1492 page_in_fn: Box<dyn Fn(PageInRange<Self>) + Send + Sync + 'static>,
1493 mark_dirty_fn: Box<dyn Fn(MarkDirtyRange<Self>) + Send + Sync + 'static>,
1494 }
1495
1496 impl PagerRangeTestFile {
1497 fn new<
1498 F1: Fn(PageInRange<Self>) + Send + Sync + 'static,
1499 F2: Fn(MarkDirtyRange<Self>) + Send + Sync + 'static,
1500 >(
1501 page_in_fn: F1,
1502 mark_dirty_fn: F2,
1503 ) -> Arc<Self> {
1504 Arc::new_cyclic(|weak| {
1505 let pager = Pager::new(ExecutionScope::new()).unwrap();
1506 let (vmo, pager_packet_receiver_registration) = pager
1507 .create_vmo(weak.clone(), page_size() * 2, zx::VmoOptions::TRAP_DIRTY)
1508 .unwrap();
1509 Self {
1510 vmo,
1511 pager_packet_receiver_registration,
1512 pager,
1513 page_in_fn: Box::new(page_in_fn),
1514 mark_dirty_fn: Box::new(mark_dirty_fn),
1515 }
1516 })
1517 }
1518 }
1519
1520 impl FxNode for PagerRangeTestFile {
1521 fn object_id(&self) -> u64 {
1522 1
1523 }
1524
1525 fn parent(&self) -> Option<Arc<crate::directory::FxDirectory>> {
1526 unimplemented!()
1527 }
1528
1529 fn set_parent(&self, _parent: Arc<crate::directory::FxDirectory>) {
1530 unimplemented!()
1531 }
1532
1533 fn open_count_add_one(&self) {}
1534
1535 fn open_count_sub_one(self: Arc<Self>) {}
1536
1537 fn object_descriptor(&self) -> fxfs::object_store::ObjectDescriptor {
1538 unimplemented!()
1539 }
1540 }
1541
1542 impl PagerBacked for PagerRangeTestFile {
1543 fn try_keep_open(self: Arc<Self>) -> Result<OpenedNode<Self>, Arc<Self>> {
1544 Ok(OpenedNode(self))
1545 }
1546
1547 fn pager(&self) -> &Pager {
1548 &self.pager
1549 }
1550
1551 fn pager_packet_receiver_registration(&self) -> &PagerPacketReceiverRegistration<Self> {
1552 &self.pager_packet_receiver_registration
1553 }
1554
1555 fn vmo(&self) -> &zx::Vmo {
1556 &self.vmo
1557 }
1558
1559 fn page_in(self: Arc<Self>, range: PageInRange<Self>) {
1560 (self.page_in_fn)(range)
1561 }
1562
1563 fn mark_dirty(self: Arc<Self>, range: MarkDirtyRange<Self>) {
1564 (self.mark_dirty_fn)(range)
1565 }
1566
1567 fn on_zero_children(self: Arc<Self>) {}
1568
1569 fn byte_size(&self) -> u64 {
1570 unimplemented!();
1571 }
1572
1573 async fn aligned_read(
1574 &self,
1575 _range: std::ops::Range<u64>,
1576 ) -> Result<buffer::Buffer<'_>, Error> {
1577 unimplemented!();
1578 }
1579 }
1580
1581 fn real_supply_pages(range: PageInRange<PagerRangeTestFile>) {
1582 let aux_vmo = zx::Vmo::create(range.len()).unwrap();
1583 range.supply_pages(&aux_vmo, 0);
1584 }
1585
1586 fn real_mark_dirty(range: MarkDirtyRange<PagerRangeTestFile>) {
1587 let _ = range.dirty_pages();
1588 }
1589
1590 #[fuchsia::test(threads = 2)]
1591 async fn test_page_in_range_supply_pages() {
1592 let file = PagerRangeTestFile::new(real_supply_pages, real_mark_dirty);
1593
1594 let mut data = vec![0; 20];
1595 file.vmo.read(&mut data, 0).unwrap();
1596 }
1597
1598 #[fuchsia::test(threads = 2)]
1599 async fn test_page_in_range_report_failure() {
1600 let file = PagerRangeTestFile::new(
1601 |range| {
1602 range.report_failure(zx::Status::IO_DATA_INTEGRITY);
1603 },
1604 real_mark_dirty,
1605 );
1606
1607 let mut data = vec![0; 20];
1608 let err = file.vmo.read(&mut data, 0).unwrap_err();
1609 assert_eq!(err, zx::Status::IO_DATA_INTEGRITY);
1610 }
1611
1612 #[cfg(debug_assertions)]
1613 #[fuchsia::test(threads = 2)]
1614 #[should_panic(expected = "PagerRange was dropped without sending a response")]
1615 async fn test_page_in_range_dropped() {
1616 let file = PagerRangeTestFile::new(|_| {}, real_mark_dirty);
1617
1618 let mut data = vec![0; 20];
1619 file.vmo.read(&mut data, 0).unwrap_err();
1620 }
1621
1622 #[cfg(not(debug_assertions))]
1623 #[fuchsia::test(threads = 2)]
1624 async fn test_page_in_range_dropped() {
1625 let file = PagerRangeTestFile::new(|_| {}, real_mark_dirty);
1626
1627 let mut data = vec![0; 20];
1628 let err = file.vmo.read(&mut data, 0).unwrap_err();
1629 assert_eq!(err, zx::Status::BAD_STATE);
1630 }
1631
1632 #[fuchsia::test(threads = 2)]
1633 async fn test_mark_dirty_range_dirty_pages() {
1634 let file = PagerRangeTestFile::new(real_supply_pages, real_mark_dirty);
1635
1636 let data = vec![5; 20];
1637 file.vmo.write(&data, 0).unwrap();
1638 }
1639
1640 #[fuchsia::test(threads = 2)]
1641 async fn test_mark_dirty_range_report_failure() {
1642 let file = PagerRangeTestFile::new(real_supply_pages, |range| {
1643 range.report_failure(zx::Status::IO_DATA_INTEGRITY);
1644 });
1645
1646 let data = vec![5; 20];
1647 let err = file.vmo.write(&data, 0).unwrap_err();
1648 assert_eq!(err, zx::Status::IO_DATA_INTEGRITY);
1649 }
1650
1651 #[cfg(debug_assertions)]
1652 #[fuchsia::test(threads = 2)]
1653 #[should_panic(expected = "PagerRange was dropped without sending a response")]
1654 async fn test_mark_dirty_range_dropped() {
1655 let file = PagerRangeTestFile::new(real_supply_pages, |_| {});
1656
1657 let data = vec![5; 20];
1658 file.vmo.write(&data, 0).unwrap_err();
1659 }
1660
1661 #[cfg(not(debug_assertions))]
1662 #[fuchsia::test(threads = 2)]
1663 async fn test_mark_dirty_range_dropped() {
1664 let file = PagerRangeTestFile::new(real_supply_pages, |_| {});
1665
1666 let data = vec![5; 20];
1667 let err = file.vmo.write(&data, 0).unwrap_err();
1668 assert_eq!(err, zx::Status::BAD_STATE);
1669 }
1670
1671 #[fuchsia::test(threads = 2)]
1672 async fn test_pager_range_chunks_iter_consumed() {
1673 let file = PagerRangeTestFile::new(
1674 |range| {
1675 let aux_vmo = zx::Vmo::create(page_size()).unwrap();
1676 range.expand(0..page_size() * 2).chunks(page_size()).for_each(|range| {
1677 range.supply_pages(&aux_vmo, 0);
1678 });
1679 },
1680 real_mark_dirty,
1681 );
1682
1683 let mut data = vec![0; 20];
1684 file.vmo.read(&mut data, 0).unwrap();
1685 }
1686
1687 fn partial_supply_pages(range: PageInRange<PagerRangeTestFile>) {
1688 let aux_vmo = zx::Vmo::create(page_size()).unwrap();
1689 range.expand(0..page_size() * 2).chunks(page_size()).take(1).for_each(|range| {
1692 range.supply_pages(&aux_vmo, 0);
1693 });
1694 }
1695
1696 #[cfg(debug_assertions)]
1697 #[fuchsia::test(threads = 2)]
1698 #[should_panic(expected = "PagerRangeChunksIter was dropped without being fully consumed")]
1699 async fn test_pager_range_chunks_iter_dropped() {
1700 let file = PagerRangeTestFile::new(partial_supply_pages, real_mark_dirty);
1701
1702 let mut data = vec![0; 20];
1703 file.vmo.read(&mut data, page_size()).unwrap_err();
1706 }
1707
1708 #[cfg(not(debug_assertions))]
1709 #[fuchsia::test(threads = 2)]
1710 async fn test_pager_range_chunks_iter_dropped() {
1711 let file = PagerRangeTestFile::new(partial_supply_pages, real_mark_dirty);
1712
1713 let mut data = vec![0; 20];
1714 let err = file.vmo.read(&mut data, page_size()).unwrap_err();
1717 assert_eq!(err, zx::Status::BAD_STATE);
1718 }
1719
1720 #[fuchsia::test(threads = 2)]
1721 async fn test_grow_zeroes_new_bytes() {
1722 let scope = ExecutionScope::new();
1726 let pager = Arc::new(Pager::new(scope.clone()).unwrap());
1727 let page_size = page_size();
1728 let vmo_size: u64 = page_size * 2;
1729 let file_a =
1730 MockFile::new_with_size_and_type(pager.clone(), vmo_size, zx::VmoOptions::RESIZABLE);
1731 let file_b =
1732 MockFile::new_with_size_and_type(pager.clone(), vmo_size, zx::VmoOptions::UNBOUNDED);
1733 let mut buffer = vec![VmoDirtyRange::default(); 3];
1734
1735 assert_eq!(file_a.vmo().get_stream_size().unwrap(), page_size * 2);
1736 assert_eq!(file_b.vmo().get_stream_size().unwrap(), page_size * 2);
1737
1738 let mut read_buf = vec![0u8; page_size as usize];
1740 file_a.vmo().read(&mut read_buf, page_size).expect("read a");
1741 assert_eq!(
1742 file_a.pager_requests(true),
1743 vec![PagerRequest::PageIn(page_size..page_size * 2)]
1744 );
1745 file_b.vmo().read(&mut read_buf, page_size).expect("read b");
1746 assert_eq!(
1747 file_b.pager_requests(true),
1748 vec![PagerRequest::PageIn(page_size..page_size * 2)]
1749 );
1750
1751 let vmo_size = page_size * 8;
1753 file_a.vmo().set_size(vmo_size).unwrap();
1754 file_b.vmo().set_stream_size(vmo_size).unwrap();
1755
1756 assert_eq!(
1757 pager.query_dirty_ranges(file_a.vmo(), 0..vmo_size, &mut buffer).unwrap(),
1758 (1, 0)
1759 );
1760 assert_eq!(
1761 buffer[0],
1762 VmoDirtyRange { offset: page_size * 2, length: page_size * 6, options: 1 },
1763 );
1764 assert_eq!(
1765 pager.query_dirty_ranges(file_b.vmo(), 0..vmo_size, &mut buffer).unwrap(),
1766 (1, 0)
1767 );
1768 assert_eq!(
1769 buffer[0],
1770 VmoDirtyRange { offset: page_size * 2, length: page_size * 6, options: 1 },
1771 );
1772
1773 let mut read_buf = vec![0u8; page_size as usize * 6];
1775 file_a.vmo().read(&mut read_buf, page_size * 2).expect("read a");
1776 assert_eq!(file_a.pager_requests(true), vec![]);
1777 file_b.vmo().read(&mut read_buf, page_size * 2).expect("read b");
1778 assert_eq!(file_b.pager_requests(true), vec![]);
1779
1780 let vmo_size = page_size * 8;
1782 file_a.vmo().set_size(vmo_size).unwrap();
1783 file_b.vmo().set_stream_size(vmo_size).unwrap();
1784 assert_eq!(
1785 pager.query_dirty_ranges(file_a.vmo(), 0..vmo_size, &mut buffer).unwrap(),
1786 (1, 0)
1787 );
1788 assert_eq!(
1789 buffer[0],
1790 VmoDirtyRange { offset: page_size * 2, length: page_size * 6, options: 1 },
1791 );
1792 assert_eq!(
1793 pager.query_dirty_ranges(file_b.vmo(), 0..vmo_size, &mut buffer).unwrap(),
1794 (1, 0)
1795 );
1796 assert_eq!(
1797 buffer[0],
1798 VmoDirtyRange { offset: page_size * 2, length: page_size * 6, options: 1 },
1799 );
1800 assert_eq!(file_a.pager_requests(true), vec![],);
1802 assert_eq!(file_b.pager_requests(true), vec![],);
1803
1804 file_b.vmo().write(&[1; 10], page_size * 2).unwrap();
1806 assert_eq!(
1807 file_b.pager_requests(true),
1808 vec![PagerRequest::Dirty(page_size * 2..page_size * 3)],
1809 );
1810
1811 let vmo_size = page_size * 4;
1813 file_b.vmo().set_stream_size(vmo_size).unwrap();
1814 let stream =
1815 zx::Stream::create(zx::StreamOptions::MODE_WRITE, file_b.vmo(), page_size * 4).unwrap();
1816 stream.write(zx::StreamWriteOptions::empty(), &vec![10; page_size as usize]).unwrap();
1817 assert_eq!(
1818 file_b.pager_requests(true),
1819 vec![PagerRequest::Dirty(page_size * 4..page_size * 5)],
1820 );
1821
1822 let stream = zx::Stream::create(
1824 zx::StreamOptions::MODE_WRITE | zx::StreamOptions::MODE_APPEND,
1825 file_b.vmo(),
1826 page_size * 5,
1827 )
1828 .unwrap();
1829 stream.write(zx::StreamWriteOptions::empty(), &[10; 1024]).unwrap();
1830 assert_eq!(
1831 file_b.pager_requests(true),
1832 vec![PagerRequest::Dirty(page_size * 5..page_size * 6)],
1833 );
1834
1835 scope.wait().await;
1836 }
1837
1838 #[fuchsia::test(threads = 2)]
1839 async fn test_pathological_shrink_unbounded_vmo() {
1840 let scope = ExecutionScope::new();
1841 let pager = Arc::new(Pager::new(scope.clone()).unwrap());
1842 let page_size = page_size();
1843 let vmo_size: u64 = page_size * 25600; let file =
1845 MockFile::new_with_size_and_type(pager.clone(), vmo_size, zx::VmoOptions::UNBOUNDED);
1846 let mut buffer = vec![VmoDirtyRange::default(); 10];
1847
1848 assert_eq!(file.vmo().get_stream_size().unwrap(), vmo_size);
1849
1850 for i in 0..vmo_size / 256 {
1852 let data = vec![5; 20];
1853 file.vmo.write(&data, i * 256).expect("write failed");
1854 }
1855
1856 for i in (0..25600u64 / 1024).rev() {
1857 file.vmo().set_stream_size(i * 1024 + page_size / 2).unwrap();
1858 }
1859
1860 assert_eq!(pager.query_dirty_ranges(file.vmo(), 0..vmo_size, &mut buffer).unwrap(), (1, 0));
1861 assert_eq!(buffer[0..1], [VmoDirtyRange { offset: 0, length: page_size, options: 0 },]);
1862
1863 scope.wait().await;
1864 }
1865
1866 #[fuchsia::test(threads = 2)]
1867 async fn test_pathological_shrink_unbounded_vmo_with_gaps() {
1868 let scope = ExecutionScope::new();
1869 let pager = Arc::new(Pager::new(scope.clone()).unwrap());
1870 let page_size = page_size();
1871 let vmo_size: u64 = page_size * 25600; let file =
1873 MockFile::new_with_size_and_type(pager.clone(), vmo_size, zx::VmoOptions::UNBOUNDED);
1874 let mut buffer = vec![VmoDirtyRange::default(); 10];
1875
1876 assert_eq!(file.vmo().get_stream_size().unwrap(), vmo_size);
1877
1878 for offset in (0u64..vmo_size).step_by((page_size * 2) as usize) {
1880 let data = vec![5; 20];
1881 file.vmo.write(&data, offset).expect("write failed");
1882 }
1883 let (actual, remaining) =
1885 pager.query_dirty_ranges(file.vmo(), 0..vmo_size, &mut buffer).unwrap();
1886 assert_eq!(actual + remaining, 25600 / 2);
1887
1888 let mut offset = vmo_size.saturating_sub(5 * page_size - 2);
1890 'outer: loop {
1893 for delta in [5 * page_size, 4 * page_size] {
1894 file.vmo().set_stream_size(offset).unwrap();
1895 assert_eq!(
1896 pager.query_dirty_ranges(file.vmo(), offset..vmo_size, &mut buffer).unwrap(),
1897 (1, 0)
1898 );
1899 assert_eq!(
1901 buffer[0..1],
1902 [VmoDirtyRange {
1903 offset: round_down(offset, page_size),
1904 length: page_size,
1905 options: 0
1906 },]
1907 );
1908 offset = offset.saturating_sub(delta);
1909 if offset == 0 {
1910 break 'outer;
1911 }
1912 }
1913 }
1914
1915 scope.wait().await;
1916 }
1917
1918 #[fuchsia::test(threads = 2)]
1919 async fn test_grow_unbounded_vmo() {
1920 let scope = ExecutionScope::new();
1921 let pager = Arc::new(Pager::new(scope.clone()).unwrap());
1922 let file = MockFile::new_with_size_and_type(pager.clone(), 128, zx::VmoOptions::UNBOUNDED);
1923
1924 let data = vec![1; 128];
1925 file.vmo().write(&data, 128).expect("write failed");
1927 file.vmo().set_stream_size(256).unwrap();
1929 assert_eq!(file.vmo().get_stream_size().expect("get_stream_size"), 256);
1930
1931 let mut data = vec![0xff; 256];
1932 file.vmo().read(&mut data, 0).expect("read");
1933 let expected = vec![0; 256];
1934 assert_eq!(data, expected);
1935
1936 file.vmo().set_stream_size(page_size() * 3).unwrap();
1937 let mut buffer = vec![VmoDirtyRange::default(); 10];
1938 assert_eq!(
1939 pager.query_dirty_ranges(file.vmo(), 0..page_size() * 3, &mut buffer).unwrap(),
1940 (2, 0)
1941 );
1942 assert_eq!(
1944 buffer[0..2],
1945 [
1946 VmoDirtyRange { offset: 0, length: page_size(), options: 0 },
1947 VmoDirtyRange { offset: page_size(), length: page_size() * 2, options: 1 },
1948 ]
1949 );
1950
1951 scope.wait().await;
1952 }
1953}