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 (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            // 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
125            // This needs to be done while holding the page lock so that it cannot race with moving
126            // the open count on blob overwrite.
127            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        // The scope guard needs to be held and outlive the file Arc and the clones of it.
142        let Some(_scope_guard) = opened_file.pager().scope.try_active_guard() else {
143            // If an active guard can't be acquired then the filesystem must be shutting down. Fail
144            // the page request to avoid leaving the client hanging.
145            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        // Check to see if there really are no children (which is necessary to avoid races) and, if
173        // so, replace the strong reference with a weak one and call on_zero_children on the node.
174        // If the file does have children, this asks the kernel to send us the ON_ZERO_CHILDREN
175        // notification for the file.
176        let mut file = self.file.lock();
177        if let FileHolder::Strong(strong) = &*file {
178            // If the last strong reference to the Arc is dropped here, then FxVolume's shutdown
179            // won't wait for the inner node object to be dropped. Taking an active guard around
180            // dropping the strong reference forces the FxVolume to wait for the file to be dropped.
181            // If the scope has begun shutdown then we can't take an active guard, so instead we do
182            // nothing here and the strong reference in the FileHolder will be removed by calling
183            // `FxNode.terminate()` as part of `NodeCache.terminate()` in the FxVolume termination
184            // thread.
185            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                        // There's not much we can do here if this fails, so we panic.
200                        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!(), // We don't expect any other kinds of packets.
219        }
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
230// FileHolder is used to retain either a strong or a weak reference to a file.  If there are any
231// child VMOs that have been shared, then we will have a strong reference which is required to keep
232// the file alive.  When we detect that there are no more children, we can downgrade to a weak
233// reference which will allow the file to be cleaned up if there are no other uses.
234enum FileHolder<T> {
235    Strong(Arc<T>),
236    Weak(Weak<T>),
237}
238
239/// Pager handles page requests. It is a per-volume object.
240impl Pager {
241    /// Creates a new pager.
242    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    /// Spawns a short term task for the pager that includes a guard that will prevent termination.
252    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    /// Set the current profile recorder, or set to None to not record.
259    pub fn set_recorder(&self, recorder: Option<Box<dyn Recorder>>) {
260        // Drop the old one outside of the lock.
261        let _old = std::mem::replace(&mut (*self.recorder.lock()), recorder);
262    }
263
264    /// Borrow the profile recorder. Used to record file opens.
265    pub fn recorder(&self) -> MutexGuard<'_, Option<Box<dyn Recorder>>> {
266        self.recorder.lock()
267    }
268
269    /// Record a range into a profile if one is being recorded.
270    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 the message fails to send, so will all the rest.
274            if let Err(_) = recorder.record(node, range.start) {
275                *recorder_holder = None;
276            }
277        }
278    }
279
280    /// Creates a new VMO to be used with the pager.
281    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    /// Starts watching for the `VMO_ZERO_CHILDREN` signal on `file`'s vmo. Returns false if the
302    /// signal is already being watched for. When the pager receives the `VMO_ZERO_CHILDREN` signal
303    /// [`PagerBacked::on_zero_children`] will be called.
304    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                // Should never fail because watch_for_zero_children should be called from `file`.
310                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    /// Supplies pages in response to a `ZX_PAGER_VMO_READ` page request. See
323    /// `zx_pager_supply_pages` for more information.
324    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    /// Notifies the kernel that a page request for the given `range` has failed. Sent in response
337    /// to a `ZX_PAGER_VMO_READ` or `ZX_PAGER_VMO_DIRTY` page request. See `ZX_PAGER_OP_FAIL` for
338    /// more information.
339    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    /// Allows the kernel to dirty the `range` of pages. Sent in response to a `ZX_PAGER_VMO_DIRTY`
360    /// page request. See `ZX_PAGER_OP_DIRTY` for more information.
361    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            // It is possible for `ZX_ERR_NOT_FOUND` to be returned on a clean page that has
364            // been evicted.  In this case, the kernel will retry if necessary.  See
365            // https://fxbug.dev/42086069 for more information.
366            if *error != zx::Status::NOT_FOUND {
367                error!(error:?; "dirty_pages failed");
368            }
369        })
370    }
371
372    /// Notifies the kernel that the filesystem has started cleaning the `range` of pages. See
373    /// `ZX_PAGER_OP_WRITEBACK_BEGIN` for more information.
374    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    /// Notifies the kernel that the filesystem has finished cleaning the `range` of pages. See
386    /// `ZX_PAGER_OP_WRITEBACK_END` for more information.
387    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    /// Queries the `vmo` for ranges that are dirty within `range`. Returns `(num_returned,
394    /// num_remaining)` where `num_returned` is the number of objects populated in `buffer` and
395    /// `num_remaining` is the number of dirty ranges remaining in `range` that could not fit in
396    /// `buffer`. See `zx_pager_query_dirty_ranges` for more information.
397    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            // TODO(https://fxbug.dev/42142550) Move to src/lib/zircon/rust/src/pager.rs once
407            // query_dirty_ranges is part of the stable vDSO.
408            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    /// Queries the `vmo` for any pager related statistics. If
423    /// `PagerVmoStatsOptions::RESET_VMO_STATS` is passed then the stats will also be reset. See
424    /// `zx_pager_query_vmo_stats` for more information.
425    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            // TODO(https://fxbug.dev/42142550) Move to src/lib/zircon/rust/src/pager.rs once
439            // query_vmo_stats is part of the stable vDSO.
440            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
458/// This is a trait for objects (files/blobs) that expose a pager backed VMO.
459pub trait PagerBacked: FxNode + Sync + Send + Sized + 'static {
460    /// Create an OpenedNode if it is already open, if not, return the bare Arc.
461    fn try_keep_open(self: Arc<Self>) -> Result<OpenedNode<Self>, Arc<Self>>;
462
463    /// The pager backing this VMO.
464    fn pager(&self) -> &Pager;
465
466    /// The receiver registration returned from [`Pager::create_vmo`].
467    fn pager_packet_receiver_registration(&self) -> &PagerPacketReceiverRegistration<Self>;
468
469    /// The pager backed VMO that this object is handling packets for. The VMO must be created with
470    /// [`Pager::create_vmo`].
471    fn vmo(&self) -> &zx::Vmo;
472
473    /// Called by the pager when a `ZX_PAGER_VMO_READ` packet is received for the VMO. The
474    /// implementation must respond by calling either `PageInRange::supply_pages` or
475    /// `PageInRange::report_failure`.
476    fn page_in(self: Arc<Self>, range: PageInRange<Self>);
477
478    /// Called by the pager when a `ZX_PAGER_VMO_DIRTY` packet is received for the VMO. The
479    /// implementation must respond by calling either `MarkDirtyRange::dirty_pages` or
480    /// `MarkDirtyRange::report_failure`.
481    fn mark_dirty(self: Arc<Self>, range: MarkDirtyRange<Self>);
482
483    /// Called by the pager to indicate there are no more VMO children.
484    fn on_zero_children(self: Arc<Self>);
485
486    /// Total bytes readable. Anything reads over this will be zero padded in the VMO.
487    fn byte_size(&self) -> u64;
488
489    /// Reads one or more blocks into a buffer and returns it. This method is called by
490    /// `default_page_in` and `aligned_byte_range` will always be aligned to the `read_ahead_size`
491    /// past to `default_page_in` unless that would extend beyond `self.byte_size()`, in which case,
492    /// `aligned_byte_range` will end at `self.byte_size()`'s next page multiple. The returned
493    /// buffer must be at least as large as the requested range. Only the requested range will be
494    /// supplied to the pager.
495    fn aligned_read(
496        &self,
497        aligned_byte_range: std::ops::Range<u64>,
498    ) -> impl Future<Output = Result<buffer::Buffer<'_>, Error>> + Send;
499}
500
501/// A generic page_in implementation that supplies pages using block-aligned reads.
502pub 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    // Two important subtleties to consider in this space:
520    //
521    // `byte_size` is the official size of the object. VMOs are page-aligned so `page_aligned_size`
522    // is the "official" page length of the object. This may be smaller than Vmo::get_size because
523    // these two things are not updated atomically. The reverse is not true -- We do not currently
524    // ever shrink a VMO's size. We also do not update byte_size (self.handle.get_size()) if an
525    // independent handle is used to grow a file. This means the VMO's size should always be
526    // strictly equal or bigger than `byte_size`.
527    //
528    // It is valid to supply more pages than asked, but supplying pages outside of the VMO range
529    // will trigger OUT_OF_RANGE errors and the call will fail without supplying anything. We must
530    // supply the range requested under all circumstances to unblock any page misses but we should
531    // take care to never supply additional pages beyond `page_aligned_size` as there is a chance
532    // that we might serve a range outside of the VMO and fail to supply anything at all.
533
534    let page_aligned_size = round_up(this.byte_size(), page_size()).unwrap();
535
536    // Zero-pad the tail if the requested range exceeds the size of the thing we're reading. This
537    // can happen when we truncate and there are outstanding pager requests that the kernel was not
538    // able to cancel in time.
539    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            // Record the page in before spawning the task to handle the page-in. This is necessary
555            // so that we don't miss this page-in when replaying and recording a new profile.  The
556            // replay is considered finished once we've responded to the page request, so if we if
557            // we spawn the page request before recording the page-in, it's possible (albeit
558            // unlikely) that the profiler can think the replay has finished, but not know about the
559            // page request and so the next recording to be missing the page request.  With the
560            // order swapped, the `test_profile` test would have a rare flake.
561            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/// Represents a dirty range of page aligned bytes within a pager backed VMO.
588#[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    /// The page aligned byte range.
598    pub fn range(&self) -> Range<u64> {
599        self.offset..(self.offset + self.length)
600    }
601
602    /// Returns true if all of the bytes in the range are 0.
603    pub fn is_zero_range(&self) -> bool {
604        self.options & zx::sys::ZX_VMO_DIRTY_RANGE_IS_ZERO != 0
605    }
606}
607
608bitflags! {
609    /// Options for `Pager::query_vmo_stats`.
610    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
611    #[repr(transparent)]
612    pub struct PagerVmoStatsOptions: u32 {
613        /// Resets the stats at the of the `Pager::query_vmo_stats` call.
614        const RESET_VMO_STATS = 1;
615    }
616}
617
618/// Pager related statistic for a VMO.
619#[derive(Debug)]
620pub struct PagerVmoStats {
621    was_vmo_modified: bool,
622}
623
624impl PagerVmoStats {
625    /// Returns true if the VMO was modified since the last time the VMO stats were reset.
626    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
636/// A trait for specializing `PagerRange` for different request types.
637pub trait PagerRequestType {
638    /// Returns the name of the request type for logging purposes.
639    fn request_type_name() -> &'static str;
640}
641
642/// A request generated from a ZX_PAGER_VMO_READ packet.
643pub struct PageInRequest;
644
645impl PagerRequestType for PageInRequest {
646    fn request_type_name() -> &'static str {
647        "PageInRequest"
648    }
649}
650
651/// The requested range from a ZX_PAGER_VMO_READ packet. This object must not be dropped without
652/// calling either `supply_pages` or `report_failure`.
653pub type PageInRange<T> = PagerRange<T, PageInRequest>;
654
655impl<T: PagerBacked> PageInRange<T> {
656    /// Constructs a new `PageInRange<T>`. `range` must be page aligned.
657    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    /// Supplies pages to the kernel for this range. See `zx_pager_supply_pages` for more
671    /// information.
672    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/// A requested generated from a ZX_PAGER_VMO_DIRTY packet.
684#[derive(Debug)]
685pub struct MarkDirtyRequest;
686
687impl PagerRequestType for MarkDirtyRequest {
688    fn request_type_name() -> &'static str {
689        "MarkDirtyRequest"
690    }
691}
692
693/// The requested range from a ZX_PAGER_VMO_DIRTY packet. This object must not be dropped without
694/// calling either `mark_dirty` or `report_failure`.
695pub type MarkDirtyRange<T> = PagerRange<T, MarkDirtyRequest>;
696
697impl<T: PagerBacked> MarkDirtyRange<T> {
698    /// Constructs a new `MarkDirtyRange<T>`. `range` must be page aligned.
699    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    /// Allows the kernel to dirty this range of pages. See `ZX_PAGER_OP_DIRTY` for more
713    /// information.
714    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    /// Holds a reference to the current Epoch, so that in-flight read requests can be tracked. This
724    /// should be None for MarkDirty requests.
725    _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
734/// The requested range from a pager packet. This object ensures that all pager requests receive a
735/// response.
736pub struct PagerRange<T: PagerBacked, U: PagerRequestType> {
737    range: Range<u64>,
738
739    /// Contains the file and the ref guard. If this is None, then the request is complete.
740    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    /// Splits the underlying range allowing for different parts of the range to be handled and
751    /// responded to independently. See `RangeExt::split` for how splitting a range works.
752    /// `split_point` must be page aligned.
753    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    /// Increases the size of the range that will be responded to. Panics if the current range is
766    /// not a subset of `new_range`. `new_range` must be page aligned.
767    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    /// Returns an iterator that splits the range into ranges of `chunk_size`. If the length of the
784    /// range is not a multiple of `chunk_size` then the last chunk won't be of length `chunk_size`.
785    /// The returned iterator will panic if it's dropped without being fully consumed. `chunk_size`
786    /// must a multiple of the page size.
787    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    /// Notifies the kernel that the page request for this range has failed. See `ZX_PAGER_OP_FAIL`
823    /// for more information.
824    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    /// Test only method that will consume the PagerRange without having the send a response.
830    #[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 this object is being dropped as part of a panic then avoid panicking again.
844                // Dropping pager packets when fxfs is crashing is acceptable. Panicking again would
845                // only clutter the logs.
846                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
863/// An iterator similar to `std::slice::Chunks` which yields `PagerRange` objects.
864/// `PagerRangeChunksIter` will panic if it's dropped without being fully consumed.
865pub struct PagerRangeChunksIter<T: PagerBacked, U: PagerRequestType> {
866    start: u64,
867    end: u64,
868    chunk_size: u64,
869    /// The file and locks/references that need to survive the request.
870    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 this object is being dropped as part of a panic then avoid panicking again.
909                // Dropping pager packets when fxfs is crashing is acceptable. Panicking again would
910                // only clutter the logs.
911                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        /// page in requests get logged so we can compare actual calls to to expectations.
951        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        // Returns the page_in requests received for this file.
978        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        // Wait for `on_zero_children` to be called.
1147        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            // `watch_for_zero_children` will return false when it's already watching.
1169            assert!(!pager.watch_for_zero_children(file.as_ref()).unwrap());
1170        }
1171        receiver.next().await.unwrap();
1172
1173        // The pager stops listening for VMO_ZERO_CHILDREN once the signal fires. Calling
1174        // `watch_for_zero_children` afterwards should return true again because watching had
1175        // stopped.
1176        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        // The VMO hasn't been modified yet.
1299        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        // Reset the stats this time.
1306        let stats =
1307            pager.query_vmo_stats(file.vmo(), PagerVmoStatsOptions::RESET_VMO_STATS).unwrap();
1308        // The stats weren't reset last time so the stats are still showing that the vmo is modified.
1309        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        // Some notes on our paging implementation:
1320        //  * Fxfs uses UNBOUNDED VMO. These are maximally sized at creation time with
1321        //    stream size holding the content length.
1322        //  * Like regular VMO, all pages are initially in an unknown state. When a page
1323        //    is first accessed, the pager (Fxfs) will be asked to page in content.
1324        //  * Size can be set as a property, via set_content_size or via set_stream_size
1325        //    but only set_stream_size() should ever be used. This ensures that the tail
1326        //    is correctly zeroed.
1327        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        // Grow the VMO content size from 1.5 pages to 7.5 pages.
1345        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        // Second page must be assumed to contain data so tail is zeroed.
1352        assert_eq!(buffer[0].range(), page_size..page_size * 2);
1353        assert!(!buffer[0].is_zero_range());
1354        // All pages after that are marked as zero.
1355        assert_eq!(buffer[1].range(), page_size * 2..page_size * 8);
1356        assert!(buffer[1].is_zero_range());
1357
1358        // We expect the tail page to have been read as part of the zeroing when we grew the size.
1359        // It will then be marked dirty (modified)
1360        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        // Modify the 2nd, 3rd, and 5th pages.
1369        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        // Pages are known zero because we just grew the file.
1374        // We don't expect any page-in requests for them.
1375        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        // Second and third pages (non-zero)
1388        assert_eq!(buffer[0].range(), page_size..(page_size * 3));
1389        assert!(!buffer[0].is_zero_range());
1390        // Fourth page is zero.
1391        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        // Fifth page (non-zero)
1400        assert_eq!(buffer[0].range(), (page_size * 4)..(page_size * 5));
1401        assert!(!buffer[0].is_zero_range());
1402        // Rest of the VMO is zero.
1403        assert_eq!(buffer[1].range(), (page_size * 5)..(page_size * 7));
1404        assert!(buffer[1].is_zero_range());
1405
1406        // Read the 4th page.
1407        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        // When a VMO's content size is explicitly grown, check that new content is zeroed.
1419        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        // The tail beyond the content size will written.
1428        file.vmo().read(&mut read_buf, 0).expect("read");
1429        assert_eq!(read_buf, write_buf);
1430
1431        // The tail beyond the new content size should be zeroed.
1432        file.vmo().set_stream_size(page_size() + 1).expect("shrink");
1433        file.vmo().write(&[0xff; 3], page_size() + 2).expect("write after shrink");
1434        // To make sure the above content size change actually zeroed data, we grow again.
1435        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        // Expand the range to 2 pages and only supply the first page, dropping the iterator without
1703        // fully consuming it.
1704        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        // 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        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        // Ask for the 2nd page. The range will be expanded to the first 2 pages. The first page
1728        // will succeed and the second page will be dropped.
1729        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        // We expect that when we grow a file, the pages between the old and the new size
1736        // are zeroed. Reads and writes to these pages after growing a file should NOT
1737        // trigger any page-in requests.
1738        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        // Page in is expected.
1752        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        // Grow the VMO size and confirm intermediate pages (2..8) are zero.
1765        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        // The extra pages are all zero. We shouldn't see any page_in requests.
1787        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        // Grow again and check that pager gets notified.
1794        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        // No pager requests. All new pages are assumed zero.
1814        assert_eq!(file_a.pager_requests(true), vec![],);
1815        assert_eq!(file_b.pager_requests(true), vec![],);
1816
1817        // Modifying a page in this new region should trigger a dirty message to the pager.
1818        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        // Shrink again to 4 pages and then append a page via zx_stream_write (WRITE)
1825        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        // Append a page via zx_stream_write (APPEND)
1836        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; // 100MiB
1857        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        // Shrinking by a small step to check that last page truncation works as expected.
1864        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; // 100MiB
1885        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        // Write every second page.
1892        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        // Every second page should be dirty.
1897        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        // Avoid page-aligned sizes to ensure we test the partial page code paths.
1902        let mut offset = vmo_size.saturating_sub(5 * page_size - 2);
1903        // Shrink by 5 pages, then 4 pages. This covers all possible arrangements of
1904        // start/end being on zero and non-zero pages.
1905        '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                // We do not expect to see dirty pages beyond stream size.
1913                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        // Overwrite the 128 after the content size;
1939        file.vmo().write(&data, 128).expect("write failed");
1940        // Grow the VMO to include the newly written bytes.
1941        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        // We expect to see only zero pages beyond content size.
1956        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}