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