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