Skip to main content

fxfs_platform/fuchsia/
pager.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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
41/// A `fuchsia_async::PacketReceiver` that handles pager packets and the `VMO_ZERO_CHILDREN` signal.
42pub struct PagerPacketReceiver<T> {
43    file: Mutex<FileHolder<T>>,
44}
45
46/// A returnable lock held on the receiver.
47pub struct PagerPacketReceiverLock<'a, T> {
48    _guard: MutexGuard<'a, FileHolder<T>>,
49    strong: bool,
50}
51
52impl<T> PagerPacketReceiverLock<'_, T> {
53    /// Returns true if the receiver was installed as a strong.
54    pub fn is_strong(&self) -> bool {
55        self.strong
56    }
57}
58
59impl<T: PagerBacked> PagerPacketReceiver<T> {
60    /// Drops the strong reference to the file that might be held if
61    /// `Pager::watch_for_zero_children` was called. This should only be used when forcibly dropping
62    /// the file object. Calls `on_zero_children` if the strong reference was held.
63    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    /// Sets the current receiver and returns the lock guard so that it can be held after the value
76    /// is set. Currently this allows synchronizing open count adjustments.
77    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            // Whenever a file is flushed, we must make sure existing page requests for a file are
113            // completed to eliminate the possibility of supplying stale data for a file.  We solve
114            // this by using a barrier when we flush to wait for outstanding page requests to
115            // finish.  Technically, we only need to wait for page requests for the specific file
116            // being flushed, but we should see if we need to for performance reasons first.
117            let epoch_guard = match command {
118                // Don't take refs for mark_dirty, it can block on flushes which block on the epoch
119                // creating a deadlock. The call for awaiting epochs is `page_in_barrier` which
120                // correctly implies that it should only wait on page in.
121                ZX_PAGER_VMO_READ => Some(Epoch::global().guard()),
122                _ => None,
123            };
124            (file, epoch_guard)
125        };
126
127        // The scope guard needs to be held and outlive the file Arc and the clones of it.
128        let Some(_scope_guard) = file.pager().scope.try_active_guard() else {
129            // If an active guard can't be acquired then the filesystem must be shutting down. Fail
130            // the page request to avoid leaving the client hanging.
131            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        // Check to see if there really are no children (which is necessary to avoid races) and, if
162        // so, replace the strong reference with a weak one and call on_zero_children on the node.
163        // If the file does have children, this asks the kernel to send us the ON_ZERO_CHILDREN
164        // notification for the file.
165        let mut file = self.file.lock();
166        if let FileHolder::Strong(strong) = &*file {
167            // If the last strong reference to the Arc is dropped here, then FxVolume's shutdown
168            // won't wait for the inner node object to be dropped. Taking an active guard around
169            // dropping the strong reference forces the FxVolume to wait for the file to be dropped.
170            // If the scope has begun shutdown then we can't take an active guard, so instead we do
171            // nothing here and the strong reference in the FileHolder will be removed by calling
172            // `FxNode.terminate()` as part of `NodeCache.terminate()` in the FxVolume termination
173            // thread.
174            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                        // There's not much we can do here if this fails, so we panic.
189                        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!(), // We don't expect any other kinds of packets.
208        }
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
219// FileHolder is used to retain either a strong or a weak reference to a file.  If there are any
220// child VMOs that have been shared, then we will have a strong reference which is required to keep
221// the file alive.  When we detect that there are no more children, we can downgrade to a weak
222// reference which will allow the file to be cleaned up if there are no other uses.
223enum FileHolder<T> {
224    Strong(Arc<T>),
225    Weak(Weak<T>),
226}
227
228/// Pager handles page requests. It is a per-volume object.
229impl Pager {
230    /// Creates a new pager.
231    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    /// Spawns a short term task for the pager that includes a guard that will prevent termination.
241    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    /// Set the current profile recorder, or set to None to not record.
248    pub fn set_recorder(&self, recorder: Option<Box<dyn Recorder>>) {
249        // Drop the old one outside of the lock.
250        let _old = std::mem::replace(&mut (*self.recorder.lock()), recorder);
251    }
252
253    /// Borrow the profile recorder. Used to record file opens.
254    pub fn recorder(&self) -> MutexGuard<'_, Option<Box<dyn Recorder>>> {
255        self.recorder.lock()
256    }
257
258    /// Record a range into a profile if one is being recorded.
259    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 the message fails to send, so will all the rest.
263            if let Err(_) = recorder.record(node, range.start) {
264                *recorder_holder = None;
265            }
266        }
267    }
268
269    /// Creates a new VMO to be used with the pager.
270    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    /// Starts watching for the `VMO_ZERO_CHILDREN` signal on `file`'s vmo. Returns false if the
291    /// signal is already being watched for. When the pager receives the `VMO_ZERO_CHILDREN` signal
292    /// [`PagerBacked::on_zero_children`] will be called.
293    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                // Should never fail because watch_for_zero_children should be called from `file`.
299                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    /// Supplies pages in response to a `ZX_PAGER_VMO_READ` page request. See
312    /// `zx_pager_supply_pages` for more information.
313    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    /// Notifies the kernel that a page request for the given `range` has failed. Sent in response
326    /// to a `ZX_PAGER_VMO_READ` or `ZX_PAGER_VMO_DIRTY` page request. See `ZX_PAGER_OP_FAIL` for
327    /// more information.
328    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    /// Allows the kernel to dirty the `range` of pages. Sent in response to a `ZX_PAGER_VMO_DIRTY`
349    /// page request. See `ZX_PAGER_OP_DIRTY` for more information.
350    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            // It is possible for `ZX_ERR_NOT_FOUND` to be returned on a clean page that has
353            // been evicted.  In this case, the kernel will retry if necessary.  See
354            // https://fxbug.dev/42086069 for more information.
355            if *error != zx::Status::NOT_FOUND {
356                error!(error:?; "dirty_pages failed");
357            }
358        })
359    }
360
361    /// Notifies the kernel that the filesystem has started cleaning the `range` of pages. See
362    /// `ZX_PAGER_OP_WRITEBACK_BEGIN` for more information.
363    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    /// Notifies the kernel that the filesystem has finished cleaning the `range` of pages. See
375    /// `ZX_PAGER_OP_WRITEBACK_END` for more information.
376    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    /// Queries the `vmo` for ranges that are dirty within `range`. Returns `(num_returned,
383    /// num_remaining)` where `num_returned` is the number of objects populated in `buffer` and
384    /// `num_remaining` is the number of dirty ranges remaining in `range` that could not fit in
385    /// `buffer`. See `zx_pager_query_dirty_ranges` for more information.
386    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            // TODO(https://fxbug.dev/42142550) Move to src/lib/zircon/rust/src/pager.rs once
396            // query_dirty_ranges is part of the stable vDSO.
397            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    /// Queries the `vmo` for any pager related statistics. If
412    /// `PagerVmoStatsOptions::RESET_VMO_STATS` is passed then the stats will also be reset. See
413    /// `zx_pager_query_vmo_stats` for more information.
414    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            // TODO(https://fxbug.dev/42142550) Move to src/lib/zircon/rust/src/pager.rs once
428            // query_vmo_stats is part of the stable vDSO.
429            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
447/// This is a trait for objects (files/blobs) that expose a pager backed VMO.
448pub trait PagerBacked: FxNode + Sync + Send + Sized + 'static {
449    /// Create an OpenedNode if it is already open, if not, return the bare Arc.
450    fn try_keep_open(self: Arc<Self>) -> Result<OpenedNode<Self>, Arc<Self>>;
451
452    /// The pager backing this VMO.
453    fn pager(&self) -> &Pager;
454
455    /// The receiver registration returned from [`Pager::create_vmo`].
456    fn pager_packet_receiver_registration(&self) -> &PagerPacketReceiverRegistration<Self>;
457
458    /// The pager backed VMO that this object is handling packets for. The VMO must be created with
459    /// [`Pager::create_vmo`].
460    fn vmo(&self) -> &zx::Vmo;
461
462    /// Called by the pager when a `ZX_PAGER_VMO_READ` packet is received for the VMO. The
463    /// implementation must respond by calling either `PageInRange::supply_pages` or
464    /// `PageInRange::report_failure`.
465    fn page_in(self: Arc<Self>, range: PageInRange<Self>);
466
467    /// Called by the pager when a `ZX_PAGER_VMO_DIRTY` packet is received for the VMO. The
468    /// implementation must respond by calling either `MarkDirtyRange::dirty_pages` or
469    /// `MarkDirtyRange::report_failure`.
470    fn mark_dirty(self: Arc<Self>, range: MarkDirtyRange<Self>);
471
472    /// Called by the pager to indicate there are no more VMO children.
473    fn on_zero_children(self: Arc<Self>);
474
475    /// Total bytes readable. Anything reads over this will be zero padded in the VMO.
476    fn byte_size(&self) -> u64;
477
478    /// Reads one or more blocks into a buffer and returns it. This method is called by
479    /// `default_page_in` and `aligned_byte_range` will always be aligned to the `read_ahead_size`
480    /// past to `default_page_in` unless that would extend beyond `self.byte_size()`, in which case,
481    /// `aligned_byte_range` will end at `self.byte_size()`'s next page multiple. The returned
482    /// buffer must be at least as large as the requested range. Only the requested range will be
483    /// supplied to the pager.
484    fn aligned_read(
485        &self,
486        aligned_byte_range: std::ops::Range<u64>,
487    ) -> impl Future<Output = Result<buffer::Buffer<'_>, Error>> + Send;
488}
489
490/// A generic page_in implementation that supplies pages using block-aligned reads.
491pub 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    // Two important subtleties to consider in this space:
509    //
510    // `byte_size` is the official size of the object. VMOs are page-aligned so `page_aligned_size`
511    // is the "official" page length of the object. This may be smaller than Vmo::get_size because
512    // these two things are not updated atomically. The reverse is not true -- We do not currently
513    // ever shrink a VMO's size. We also do not update byte_size (self.handle.get_size()) if an
514    // independent handle is used to grow a file. This means the VMO's size should always be
515    // strictly equal or bigger than `byte_size`.
516    //
517    // It is valid to supply more pages than asked, but supplying pages outside of the VMO range
518    // will trigger OUT_OF_RANGE errors and the call will fail without supplying anything. We must
519    // supply the range requested under all circumstances to unblock any page misses but we should
520    // take care to never supply additional pages beyond `page_aligned_size` as there is a chance
521    // that we might serve a range outside of the VMO and fail to supply anything at all.
522
523    let page_aligned_size = round_up(this.byte_size(), page_size()).unwrap();
524
525    // Zero-pad the tail if the requested range exceeds the size of the thing we're reading. This
526    // can happen when we truncate and there are outstanding pager requests that the kernel was not
527    // able to cancel in time.
528    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            // Record the page in before spawning the task to handle the page-in. This is necessary
544            // so that we don't miss this page-in when replaying and recording a new profile.  The
545            // replay is considered finished once we've responded to the page request, so if we if
546            // we spawn the page request before recording the page-in, it's possible (albeit
547            // unlikely) that the profiler can think the replay has finished, but not know about the
548            // page request and so the next recording to be missing the page request.  With the
549            // order swapped, the `test_profile` test would have a rare flake.
550            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/// Represents a dirty range of page aligned bytes within a pager backed VMO.
577#[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    /// The page aligned byte range.
587    pub fn range(&self) -> Range<u64> {
588        self.offset..(self.offset + self.length)
589    }
590
591    /// Returns true if all of the bytes in the range are 0.
592    pub fn is_zero_range(&self) -> bool {
593        self.options & zx::sys::ZX_VMO_DIRTY_RANGE_IS_ZERO != 0
594    }
595}
596
597bitflags! {
598    /// Options for `Pager::query_vmo_stats`.
599    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
600    #[repr(transparent)]
601    pub struct PagerVmoStatsOptions: u32 {
602        /// Resets the stats at the of the `Pager::query_vmo_stats` call.
603        const RESET_VMO_STATS = 1;
604    }
605}
606
607/// Pager related statistic for a VMO.
608#[derive(Debug)]
609pub struct PagerVmoStats {
610    was_vmo_modified: bool,
611}
612
613impl PagerVmoStats {
614    /// Returns true if the VMO was modified since the last time the VMO stats were reset.
615    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
625/// A trait for specializing `PagerRange` for different request types.
626pub trait PagerRequestType {
627    /// Returns the name of the request type for logging purposes.
628    fn request_type_name() -> &'static str;
629}
630
631/// A request generated from a ZX_PAGER_VMO_READ packet.
632pub struct PageInRequest;
633
634impl PagerRequestType for PageInRequest {
635    fn request_type_name() -> &'static str {
636        "PageInRequest"
637    }
638}
639
640/// The requested range from a ZX_PAGER_VMO_READ packet. This object must not be dropped without
641/// calling either `supply_pages` or `report_failure`.
642pub type PageInRange<T> = PagerRange<T, PageInRequest>;
643
644impl<T: PagerBacked> PageInRange<T> {
645    /// Constructs a new `PageInRange<T>`. `range` must be page aligned.
646    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    /// Supplies pages to the kernel for this range. See `zx_pager_supply_pages` for more
660    /// information.
661    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/// A requested generated from a ZX_PAGER_VMO_DIRTY packet.
673#[derive(Debug)]
674pub struct MarkDirtyRequest;
675
676impl PagerRequestType for MarkDirtyRequest {
677    fn request_type_name() -> &'static str {
678        "MarkDirtyRequest"
679    }
680}
681
682/// The requested range from a ZX_PAGER_VMO_DIRTY packet. This object must not be dropped without
683/// calling either `mark_dirty` or `report_failure`.
684pub type MarkDirtyRange<T> = PagerRange<T, MarkDirtyRequest>;
685
686impl<T: PagerBacked> MarkDirtyRange<T> {
687    /// Constructs a new `MarkDirtyRange<T>`. `range` must be page aligned.
688    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    /// Allows the kernel to dirty this range of pages. See `ZX_PAGER_OP_DIRTY` for more
702    /// information.
703    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    /// Holds a reference to the current Epoch, so that in-flight read requests can be tracked. This
713    /// should be None for MarkDirty requests.
714    _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
723/// The requested range from a pager packet. This object ensures that all pager requests receive a
724/// response.
725pub struct PagerRange<T: PagerBacked, U: PagerRequestType> {
726    range: Range<u64>,
727
728    /// Contains the file and the ref guard. If this is None, then the request is complete.
729    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    /// Splits the underlying range allowing for different parts of the range to be handled and
740    /// responded to independently. See `RangeExt::split` for how splitting a range works.
741    /// `split_point` must be page aligned.
742    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    /// Increases the size of the range that will be responded to. Panics if the current range is
755    /// not a subset of `new_range`. `new_range` must be page aligned.
756    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    /// Returns an iterator that splits the range into ranges of `chunk_size`. If the length of the
773    /// range is not a multiple of `chunk_size` then the last chunk won't be of length `chunk_size`.
774    /// The returned iterator will panic if it's dropped without being fully consumed. `chunk_size`
775    /// must a multiple of the page size.
776    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    /// Notifies the kernel that the page request for this range has failed. See `ZX_PAGER_OP_FAIL`
812    /// for more information.
813    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    /// Test only method that will consume the PagerRange without having the send a response.
819    #[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 this object is being dropped as part of a panic then avoid panicking again.
833                // Dropping pager packets when fxfs is crashing is acceptable. Panicking again would
834                // only clutter the logs.
835                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
852/// An iterator similar to `std::slice::Chunks` which yields `PagerRange` objects.
853/// `PagerRangeChunksIter` will panic if it's dropped without being fully consumed.
854pub struct PagerRangeChunksIter<T: PagerBacked, U: PagerRequestType> {
855    start: u64,
856    end: u64,
857    chunk_size: u64,
858    /// The file and locks/references that need to survive the request.
859    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 this object is being dropped as part of a panic then avoid panicking again.
898                // Dropping pager packets when fxfs is crashing is acceptable. Panicking again would
899                // only clutter the logs.
900                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        /// page in requests get logged so we can compare actual calls to to expectations.
940        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        // Returns the page_in requests received for this file.
967        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        // Wait for `on_zero_children` to be called.
1136        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            // `watch_for_zero_children` will return false when it's already watching.
1158            assert!(!pager.watch_for_zero_children(file.as_ref()).unwrap());
1159        }
1160        receiver.next().await.unwrap();
1161
1162        // The pager stops listening for VMO_ZERO_CHILDREN once the signal fires. Calling
1163        // `watch_for_zero_children` afterwards should return true again because watching had
1164        // stopped.
1165        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        // The VMO hasn't been modified yet.
1288        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        // Reset the stats this time.
1295        let stats =
1296            pager.query_vmo_stats(file.vmo(), PagerVmoStatsOptions::RESET_VMO_STATS).unwrap();
1297        // The stats weren't reset last time so the stats are still showing that the vmo is modified.
1298        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        // Some notes on our paging implementation:
1309        //  * Fxfs uses UNBOUNDED VMO. These are maximally sized at creation time with
1310        //    stream size holding the content length.
1311        //  * Like regular VMO, all pages are initially in an unknown state. When a page
1312        //    is first accessed, the pager (Fxfs) will be asked to page in content.
1313        //  * Size can be set as a property, via set_content_size or via set_stream_size
1314        //    but only set_stream_size() should ever be used. This ensures that the tail
1315        //    is correctly zeroed.
1316        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        // Grow the VMO content size from 1.5 pages to 7.5 pages.
1334        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        // Second page must be assumed to contain data so tail is zeroed.
1341        assert_eq!(buffer[0].range(), page_size..page_size * 2);
1342        assert!(!buffer[0].is_zero_range());
1343        // All pages after that are marked as zero.
1344        assert_eq!(buffer[1].range(), page_size * 2..page_size * 8);
1345        assert!(buffer[1].is_zero_range());
1346
1347        // We expect the tail page to have been read as part of the zeroing when we grew the size.
1348        // It will then be marked dirty (modified)
1349        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        // Modify the 2nd, 3rd, and 5th pages.
1358        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        // Pages are known zero because we just grew the file.
1363        // We don't expect any page-in requests for them.
1364        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        // Second and third pages (non-zero)
1377        assert_eq!(buffer[0].range(), page_size..(page_size * 3));
1378        assert!(!buffer[0].is_zero_range());
1379        // Fourth page is zero.
1380        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        // Fifth page (non-zero)
1389        assert_eq!(buffer[0].range(), (page_size * 4)..(page_size * 5));
1390        assert!(!buffer[0].is_zero_range());
1391        // Rest of the VMO is zero.
1392        assert_eq!(buffer[1].range(), (page_size * 5)..(page_size * 7));
1393        assert!(buffer[1].is_zero_range());
1394
1395        // Read the 4th page.
1396        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        // When a VMO's content size is explicitly grown, check that new content is zeroed.
1408        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        // The tail beyond the content size will written.
1417        file.vmo().read(&mut read_buf, 0).expect("read");
1418        assert_eq!(read_buf, write_buf);
1419
1420        // The tail beyond the new content size should be zeroed.
1421        file.vmo().set_stream_size(page_size() + 1).expect("shrink");
1422        file.vmo().write(&[0xff; 3], page_size() + 2).expect("write after shrink");
1423        // To make sure the above content size change actually zeroed data, we grow again.
1424        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        // Expand the range to 2 pages and only supply the first page, dropping the iterator without
1692        // fully consuming it.
1693        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        // Ask for the 2nd page. The range will be expanded to the first 2 pages. The first page
1706        // will succeed and the second page will be dropped.
1707        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        // Ask for the 2nd page. The range will be expanded to the first 2 pages. The first page
1717        // will succeed and the second page will be dropped.
1718        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        // We expect that when we grow a file, the pages between the old and the new size
1725        // are zeroed. Reads and writes to these pages after growing a file should NOT
1726        // trigger any page-in requests.
1727        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        // Page in is expected.
1741        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        // Grow the VMO size and confirm intermediate pages (2..8) are zero.
1754        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        // The extra pages are all zero. We shouldn't see any page_in requests.
1776        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        // Grow again and check that pager gets notified.
1783        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        // No pager requests. All new pages are assumed zero.
1803        assert_eq!(file_a.pager_requests(true), vec![],);
1804        assert_eq!(file_b.pager_requests(true), vec![],);
1805
1806        // Modifying a page in this new region should trigger a dirty message to the pager.
1807        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        // Shrink again to 4 pages and then append a page via zx_stream_write (WRITE)
1814        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        // Append a page via zx_stream_write (APPEND)
1825        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; // 100MiB
1846        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        // Shrinking by a small step to check that last page truncation works as expected.
1853        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; // 100MiB
1874        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        // Write every second page.
1881        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        // Every second page should be dirty.
1886        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        // Avoid page-aligned sizes to ensure we test the partial page code paths.
1891        let mut offset = vmo_size.saturating_sub(5 * page_size - 2);
1892        // Shrink by 5 pages, then 4 pages. This covers all possible arrangements of
1893        // start/end being on zero and non-zero pages.
1894        '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                // We do not expect to see dirty pages beyond stream size.
1902                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        // Overwrite the 128 after the content size;
1928        file.vmo().write(&data, 128).expect("write failed");
1929        // Grow the VMO to include the newly written bytes.
1930        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        // We expect to see only zero pages beyond content size.
1945        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}