Skip to main content

fxfs_platform_testing/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 storage_units::page_size;
25use vfs::execution_scope::ExecutionScope;
26use zx::sys::zx_page_request_command_t::{ZX_PAGER_VMO_DIRTY, ZX_PAGER_VMO_READ};
27use zx::{PacketContents, PagerPacket, SignalPacket};
28
29pub static STRONG_FILE_REFS: AtomicU64 = AtomicU64::new(0);
30
31fn watch_for_zero_children(file: &impl PagerBacked) -> Result<(), zx::Status> {
32    file.vmo().wait_async(
33        file.pager().executor.port(),
34        file.pager_packet_receiver_registration().key(),
35        zx::Signals::VMO_ZERO_CHILDREN,
36        zx::WaitAsyncOpts::empty(),
37    )
38}
39
40pub type PagerPacketReceiverRegistration<T> = fasync::ReceiverRegistration<PagerPacketReceiver<T>>;
41
42/// A `fuchsia_async::PacketReceiver` that handles pager packets and the `VMO_ZERO_CHILDREN` signal.
43pub struct PagerPacketReceiver<T> {
44    file: Mutex<FileHolder<T>>,
45}
46
47/// A returnable lock held on the receiver.
48pub struct PagerPacketReceiverLock<'a, T> {
49    _guard: MutexGuard<'a, FileHolder<T>>,
50    strong: bool,
51}
52
53impl<T> PagerPacketReceiverLock<'_, T> {
54    /// Returns true if the receiver was installed as a strong.
55    pub fn is_strong(&self) -> bool {
56        self.strong
57    }
58}
59
60impl<T: PagerBacked> PagerPacketReceiver<T> {
61    /// Drops the strong reference to the file that might be held if
62    /// `Pager::watch_for_zero_children` was called. This should only be used when forcibly dropping
63    /// the file object. Calls `on_zero_children` if the strong reference was held.
64    pub fn stop_watching_for_zero_children(&self) {
65        let mut file = self.file.lock();
66        if let FileHolder::Strong(strong) = &*file {
67            let weak = FileHolder::Weak(Arc::downgrade(&strong));
68            let FileHolder::Strong(strong) = std::mem::replace(&mut *file, weak) else {
69                unreachable!();
70            };
71            STRONG_FILE_REFS.fetch_sub(1, Ordering::Relaxed);
72            strong.on_zero_children();
73        }
74    }
75
76    /// Sets the current receiver and returns the lock guard so that it can be held after the value
77    /// is set. Currently this allows synchronizing open count adjustments.
78    pub fn set_receiver(&self, new_receiver: &Arc<T>) -> PagerPacketReceiverLock<'_, T> {
79        let mut receiver_lock = self.file.lock();
80        let strong = match &mut *receiver_lock {
81            FileHolder::Strong(arc) => {
82                *arc = new_receiver.clone();
83                true
84            }
85            FileHolder::Weak(arc) => {
86                *arc = Arc::downgrade(new_receiver);
87                false
88            }
89        };
90        PagerPacketReceiverLock { _guard: receiver_lock, strong }
91    }
92
93    fn receive_pager_packet(&self, contents: PagerPacket) {
94        let command = contents.command();
95        if command != ZX_PAGER_VMO_READ && command != ZX_PAGER_VMO_DIRTY {
96            return;
97        }
98
99        let (opened_file, epoch_guard) = {
100            let file_lock = self.file.lock();
101            let file = match &*file_lock {
102                FileHolder::Strong(file) => file.clone(),
103                FileHolder::Weak(file) => {
104                    if let Some(file) = file.upgrade() {
105                        file
106                    } else {
107                        error!("Received a page request for a file that is closed {:?}", contents);
108                        return;
109                    }
110                }
111            };
112
113            // Whenever a file is flushed, we must make sure existing page requests for a file are
114            // completed to eliminate the possibility of supplying stale data for a file.  We solve
115            // this by using a barrier when we flush to wait for outstanding page requests to
116            // finish.  Technically, we only need to wait for page requests for the specific file
117            // being flushed, but we should see if we need to for performance reasons first.
118            let epoch_guard = match command {
119                // Don't take refs for mark_dirty, it can block on flushes which block on the epoch
120                // creating a deadlock. The call for awaiting epochs is `page_in_barrier` which
121                // correctly implies that it should only wait on page in.
122                ZX_PAGER_VMO_READ => Some(Epoch::global().guard()),
123                _ => None,
124            };
125
126            // This needs to be done while holding the page lock so that it cannot race with moving
127            // the open count on blob overwrite.
128            let opened_file = match file.try_keep_open() {
129                Ok(opened) => opened,
130                Err(bare) => {
131                    bare.pager().report_failure(
132                        bare.vmo(),
133                        contents.range(),
134                        zx::Status::BAD_STATE,
135                    );
136                    return;
137                }
138            };
139            (opened_file, epoch_guard)
140        };
141
142        // The scope guard needs to be held and outlive the file Arc and the clones of it.
143        let Some(_scope_guard) = opened_file.pager().scope.try_active_guard() else {
144            // If an active guard can't be acquired then the filesystem must be shutting down. Fail
145            // the page request to avoid leaving the client hanging.
146            opened_file.pager().report_failure(
147                opened_file.vmo(),
148                contents.range(),
149                zx::Status::BAD_STATE,
150            );
151            return;
152        };
153        match command {
154            ZX_PAGER_VMO_READ => {
155                let file_arc = opened_file.clone();
156                file_arc.page_in(PageInRange::new(
157                    contents.range(),
158                    opened_file,
159                    epoch_guard.unwrap(),
160                ))
161            }
162            ZX_PAGER_VMO_DIRTY => {
163                let file_arc = opened_file.clone();
164                file_arc.mark_dirty(MarkDirtyRange::new(contents.range(), opened_file))
165            }
166            _ => unreachable!("Unhandled commands are filtered above"),
167        }
168    }
169
170    fn receive_signal_packet(&self, signals: SignalPacket) {
171        assert!(signals.observed().contains(zx::Signals::VMO_ZERO_CHILDREN));
172
173        // Check to see if there really are no children (which is necessary to avoid races) and, if
174        // so, replace the strong reference with a weak one and call on_zero_children on the node.
175        // If the file does have children, this asks the kernel to send us the ON_ZERO_CHILDREN
176        // notification for the file.
177        let mut file = self.file.lock();
178        if let FileHolder::Strong(strong) = &*file {
179            // If the last strong reference to the Arc is dropped here, then FxVolume's shutdown
180            // won't wait for the inner node object to be dropped. Taking an active guard around
181            // dropping the strong reference forces the FxVolume to wait for the file to be dropped.
182            // If the scope has begun shutdown then we can't take an active guard, so instead we do
183            // nothing here and the strong reference in the FileHolder will be removed by calling
184            // `FxNode.terminate()` as part of `NodeCache.terminate()` in the FxVolume termination
185            // thread.
186            let Some(_guard) = strong.pager().scope.try_active_guard() else {
187                info!("Ignoring zero-children notification due to shutting down");
188                return;
189            };
190            match strong.vmo().info() {
191                Ok(info) => {
192                    if info.num_children == 0 {
193                        let weak = FileHolder::Weak(Arc::downgrade(&strong));
194                        let FileHolder::Strong(strong) = std::mem::replace(&mut *file, weak) else {
195                            unreachable!();
196                        };
197                        STRONG_FILE_REFS.fetch_sub(1, Ordering::Relaxed);
198                        strong.on_zero_children();
199                    } else {
200                        // There's not much we can do here if this fails, so we panic.
201                        watch_for_zero_children(strong.as_ref()).unwrap();
202                    }
203                }
204                Err(e) => error!(error:? = e; "Vmo::info failed"),
205            }
206        }
207    }
208}
209
210impl<T: PagerBacked> fasync::PacketReceiver for PagerPacketReceiver<T> {
211    fn receive_packet(&self, packet: zx::Packet) {
212        match packet.contents() {
213            PacketContents::Pager(contents) => {
214                self.receive_pager_packet(contents);
215            }
216            PacketContents::SignalOne(signals) => {
217                self.receive_signal_packet(signals);
218            }
219            _ => unreachable!(), // We don't expect any other kinds of packets.
220        }
221    }
222}
223
224pub struct Pager {
225    pager: zx::Pager,
226    scope: ExecutionScope,
227    executor: fasync::EHandle,
228    recorder: Mutex<Option<Box<dyn Recorder>>>,
229}
230
231// FileHolder is used to retain either a strong or a weak reference to a file.  If there are any
232// child VMOs that have been shared, then we will have a strong reference which is required to keep
233// the file alive.  When we detect that there are no more children, we can downgrade to a weak
234// reference which will allow the file to be cleaned up if there are no other uses.
235enum FileHolder<T> {
236    Strong(Arc<T>),
237    Weak(Weak<T>),
238}
239
240/// Pager handles page requests. It is a per-volume object.
241impl Pager {
242    /// Creates a new pager.
243    pub fn new(scope: ExecutionScope) -> Result<Self, Error> {
244        Ok(Pager {
245            pager: zx::Pager::create(zx::PagerOptions::empty())?,
246            scope,
247            executor: fasync::EHandle::local(),
248            recorder: Mutex::new(None),
249        })
250    }
251
252    /// Spawns a short term task for the pager that includes a guard that will prevent termination.
253    fn spawn(&self, task: impl Future<Output = ()> + Send + 'static) {
254        if let Some(guard) = self.scope.try_active_guard() {
255            self.executor.spawn_detached(FutureWithGuard::new(guard, task));
256        }
257    }
258
259    /// Set the current profile recorder, or set to None to not record.
260    pub fn set_recorder(&self, recorder: Option<Box<dyn Recorder>>) {
261        // Drop the old one outside of the lock.
262        let _old = std::mem::replace(&mut (*self.recorder.lock()), recorder);
263    }
264
265    /// Borrow the profile recorder. Used to record file opens.
266    pub fn recorder(&self) -> MutexGuard<'_, Option<Box<dyn Recorder>>> {
267        self.recorder.lock()
268    }
269
270    /// Record a range into a profile if one is being recorded.
271    pub fn record_page_in<P: PagerBacked>(&self, node: Arc<P>, range: Range<u64>) {
272        let mut recorder_holder = self.recorder.lock();
273        if let Some(recorder) = &mut (*recorder_holder) {
274            // If the message fails to send, so will all the rest.
275            if let Err(_) = recorder.record(node, range.start) {
276                *recorder_holder = None;
277            }
278        }
279    }
280
281    /// Creates a new VMO to be used with the pager.
282    pub fn create_vmo<T: PagerBacked>(
283        &self,
284        file: Weak<T>,
285        initial_size: u64,
286        vmo_options: zx::VmoOptions,
287    ) -> Result<(zx::Vmo, PagerPacketReceiverRegistration<T>), Error> {
288        let registration = self
289            .executor
290            .register_receiver(PagerPacketReceiver { file: Mutex::new(FileHolder::Weak(file)) });
291        Ok((
292            self.pager.create_vmo(
293                vmo_options,
294                self.executor.port(),
295                registration.key(),
296                initial_size,
297            )?,
298            registration,
299        ))
300    }
301
302    /// Starts watching for the `VMO_ZERO_CHILDREN` signal on `file`'s vmo. Returns false if the
303    /// signal is already being watched for. When the pager receives the `VMO_ZERO_CHILDREN` signal
304    /// [`PagerBacked::on_zero_children`] will be called.
305    pub fn watch_for_zero_children(&self, file: &impl PagerBacked) -> Result<bool, Error> {
306        let mut file = file.pager_packet_receiver_registration().file.lock();
307
308        match &*file {
309            FileHolder::Weak(weak) => {
310                // Should never fail because watch_for_zero_children should be called from `file`.
311                let strong = weak.upgrade().unwrap();
312
313                watch_for_zero_children(strong.as_ref())?;
314
315                STRONG_FILE_REFS.fetch_add(1, Ordering::Relaxed);
316                *file = FileHolder::Strong(strong);
317                Ok(true)
318            }
319            FileHolder::Strong(_) => Ok(false),
320        }
321    }
322
323    /// Supplies pages in response to a `ZX_PAGER_VMO_READ` page request. See
324    /// `zx_pager_supply_pages` for more information.
325    fn supply_pages(
326        &self,
327        vmo: &zx::Vmo,
328        range: Range<u64>,
329        transfer_vmo: &zx::Vmo,
330        transfer_offset: u64,
331    ) {
332        if let Err(e) = self.pager.supply_pages(vmo, range, transfer_vmo, transfer_offset) {
333            error!(error:? = e; "supply_pages failed");
334        }
335    }
336
337    /// Notifies the kernel that a page request for the given `range` has failed. Sent in response
338    /// to a `ZX_PAGER_VMO_READ` or `ZX_PAGER_VMO_DIRTY` page request. See `ZX_PAGER_OP_FAIL` for
339    /// more information.
340    fn report_failure(&self, vmo: &zx::Vmo, range: Range<u64>, status: zx::Status) {
341        let pager_status = match status {
342            zx::Status::IO_DATA_INTEGRITY => zx::Status::IO_DATA_INTEGRITY,
343            zx::Status::NO_SPACE => zx::Status::NO_SPACE,
344            zx::Status::FILE_BIG => zx::Status::BUFFER_TOO_SMALL,
345            zx::Status::IO
346            | zx::Status::IO_DATA_LOSS
347            | zx::Status::IO_INVALID
348            | zx::Status::IO_MISSED_DEADLINE
349            | zx::Status::IO_NOT_PRESENT
350            | zx::Status::IO_OVERRUN
351            | zx::Status::IO_REFUSED
352            | zx::Status::PEER_CLOSED => zx::Status::IO,
353            _ => zx::Status::BAD_STATE,
354        };
355        if let Err(e) = self.pager.op_range(zx::PagerOp::Fail(pager_status), vmo, range) {
356            error!(error:? = e; "op_range failed");
357        }
358    }
359
360    /// Allows the kernel to dirty the `range` of pages. Sent in response to a `ZX_PAGER_VMO_DIRTY`
361    /// page request. See `ZX_PAGER_OP_DIRTY` for more information.
362    fn dirty_pages(&self, vmo: &zx::Vmo, range: Range<u64>) -> Result<(), zx::Status> {
363        self.pager.op_range(zx::PagerOp::Dirty, vmo, range).inspect_err(|error| {
364            // It is possible for `ZX_ERR_NOT_FOUND` to be returned on a clean page that has
365            // been evicted.  In this case, the kernel will retry if necessary.  See
366            // https://fxbug.dev/42086069 for more information.
367            if *error != zx::Status::NOT_FOUND {
368                error!(error:?; "dirty_pages failed");
369            }
370        })
371    }
372
373    /// Notifies the kernel that the filesystem has started cleaning the `range` of pages. See
374    /// `ZX_PAGER_OP_WRITEBACK_BEGIN` for more information.
375    pub fn writeback_begin(
376        &self,
377        vmo: &zx::Vmo,
378        range: Range<u64>,
379        options: zx::PagerWritebackBeginOptions,
380    ) {
381        if let Err(e) = self.pager.op_range(zx::PagerOp::WritebackBegin(options), vmo, range) {
382            error!(error:? = e; "writeback_begin failed");
383        }
384    }
385
386    /// Notifies the kernel that the filesystem has finished cleaning the `range` of pages. See
387    /// `ZX_PAGER_OP_WRITEBACK_END` for more information.
388    pub fn writeback_end(&self, vmo: &zx::Vmo, range: Range<u64>) {
389        if let Err(e) = self.pager.op_range(zx::PagerOp::WritebackEnd, vmo, range) {
390            error!(error:? = e; "writeback_end failed");
391        }
392    }
393
394    /// Queries the `vmo` for ranges that are dirty within `range`. Returns `(num_returned,
395    /// num_remaining)` where `num_returned` is the number of objects populated in `buffer` and
396    /// `num_remaining` is the number of dirty ranges remaining in `range` that could not fit in
397    /// `buffer`. See `zx_pager_query_dirty_ranges` for more information.
398    pub fn query_dirty_ranges(
399        &self,
400        vmo: &zx::Vmo,
401        range: Range<u64>,
402        buffer: &mut [VmoDirtyRange],
403    ) -> Result<(usize, usize), zx::Status> {
404        let mut actual = 0;
405        let mut avail = 0;
406        let status = unsafe {
407            // TODO(https://fxbug.dev/42142550) Move to src/lib/zircon/rust/src/pager.rs once
408            // query_dirty_ranges is part of the stable vDSO.
409            zx::sys::zx_pager_query_dirty_ranges(
410                self.pager.raw_handle(),
411                vmo.raw_handle(),
412                range.start,
413                range.end - range.start,
414                buffer.as_mut_ptr() as *mut u8,
415                std::mem::size_of_val(buffer),
416                &mut actual as *mut usize,
417                &mut avail as *mut usize,
418            )
419        };
420        zx::ok(status).map(|_| (actual, avail - actual))
421    }
422
423    /// Queries the `vmo` for any pager related statistics. If
424    /// `PagerVmoStatsOptions::RESET_VMO_STATS` is passed then the stats will also be reset. See
425    /// `zx_pager_query_vmo_stats` for more information.
426    pub fn query_vmo_stats(
427        &self,
428        vmo: &zx::Vmo,
429        options: PagerVmoStatsOptions,
430    ) -> Result<PagerVmoStats, zx::Status> {
431        #[repr(C)]
432        #[derive(Default)]
433        struct zx_pager_vmo_stats {
434            pub modified: u32,
435        }
436        const ZX_PAGER_VMO_STATS_MODIFIED: u32 = 1;
437        let mut vmo_stats = MaybeUninit::<zx_pager_vmo_stats>::uninit();
438        let status = unsafe {
439            // TODO(https://fxbug.dev/42142550) Move to src/lib/zircon/rust/src/pager.rs once
440            // query_vmo_stats is part of the stable vDSO.
441            zx::sys::zx_pager_query_vmo_stats(
442                self.pager.raw_handle(),
443                vmo.raw_handle(),
444                options.bits(),
445                vmo_stats.as_mut_ptr() as *mut u8,
446                std::mem::size_of::<zx_pager_vmo_stats>(),
447            )
448        };
449        zx::ok(status)?;
450        let vmo_stats = unsafe { vmo_stats.assume_init() };
451        Ok(PagerVmoStats { was_vmo_modified: vmo_stats.modified == ZX_PAGER_VMO_STATS_MODIFIED })
452    }
453
454    pub async fn page_in_barrier() {
455        Epoch::global().barrier().await;
456    }
457}
458
459/// This is a trait for objects (files/blobs) that expose a pager backed VMO.
460pub trait PagerBacked: FxNode + Sync + Send + Sized + 'static {
461    /// Create an OpenedNode if it is already open, if not, return the bare Arc.
462    fn try_keep_open(self: Arc<Self>) -> Result<OpenedNode<Self>, Arc<Self>>;
463
464    /// The pager backing this VMO.
465    fn pager(&self) -> &Pager;
466
467    /// The receiver registration returned from [`Pager::create_vmo`].
468    fn pager_packet_receiver_registration(&self) -> &PagerPacketReceiverRegistration<Self>;
469
470    /// The pager backed VMO that this object is handling packets for. The VMO must be created with
471    /// [`Pager::create_vmo`].
472    fn vmo(&self) -> &zx::Vmo;
473
474    /// Called by the pager when a `ZX_PAGER_VMO_READ` packet is received for the VMO. The
475    /// implementation must respond by calling either `PageInRange::supply_pages` or
476    /// `PageInRange::report_failure`.
477    fn page_in(self: Arc<Self>, range: PageInRange<Self>);
478
479    /// Called by the pager when a `ZX_PAGER_VMO_DIRTY` packet is received for the VMO. The
480    /// implementation must respond by calling either `MarkDirtyRange::dirty_pages` or
481    /// `MarkDirtyRange::report_failure`.
482    fn mark_dirty(self: Arc<Self>, range: MarkDirtyRange<Self>);
483
484    /// Called by the pager to indicate there are no more VMO children.
485    fn on_zero_children(self: Arc<Self>);
486
487    /// Total bytes readable. Anything reads over this will be zero padded in the VMO.
488    fn byte_size(&self) -> u64;
489
490    /// Reads one or more blocks into a buffer and returns it. This method is called by
491    /// `default_page_in` and `aligned_byte_range` will always be aligned to the `read_ahead_size`
492    /// past to `default_page_in` unless that would extend beyond `self.byte_size()`, in which case,
493    /// `aligned_byte_range` will end at `self.byte_size()`'s next page multiple. The returned
494    /// buffer must be at least as large as the requested range. Only the requested range will be
495    /// supplied to the pager.
496    fn aligned_read(
497        &self,
498        aligned_byte_range: std::ops::Range<u64>,
499    ) -> impl Future<Output = Result<buffer::Buffer<'_>, Error>> + Send;
500}
501
502/// A generic page_in implementation that supplies pages using block-aligned reads.
503pub fn default_page_in<P: PagerBacked>(
504    this: Arc<P>,
505    pager_range: PageInRange<P>,
506    read_ahead_size: u64,
507) {
508    fxfs_trace::duration!(
509        "start-page-in",
510        "offset" => pager_range.start(),
511        "len" => pager_range.len()
512    );
513
514    const ZERO_VMO_SIZE: u64 = 1_048_576;
515    static ZERO_VMO: std::sync::LazyLock<zx::Vmo> =
516        std::sync::LazyLock::new(|| zx::Vmo::create(ZERO_VMO_SIZE).unwrap());
517
518    assert!(pager_range.end() < i64::MAX as u64);
519
520    // Two important subtleties to consider in this space:
521    //
522    // `byte_size` is the official size of the object. VMOs are page-aligned so `page_aligned_size`
523    // is the "official" page length of the object. This may be smaller than Vmo::get_size because
524    // these two things are not updated atomically. The reverse is not true -- We do not currently
525    // ever shrink a VMO's size. We also do not update byte_size (self.handle.get_size()) if an
526    // independent handle is used to grow a file. This means the VMO's size should always be
527    // strictly equal or bigger than `byte_size`.
528    //
529    // It is valid to supply more pages than asked, but supplying pages outside of the VMO range
530    // will trigger OUT_OF_RANGE errors and the call will fail without supplying anything. We must
531    // supply the range requested under all circumstances to unblock any page misses but we should
532    // take care to never supply additional pages beyond `page_aligned_size` as there is a chance
533    // that we might serve a range outside of the VMO and fail to supply anything at all.
534
535    let page_aligned_size = page_size().align_up(this.byte_size()).unwrap();
536
537    // Zero-pad the tail if the requested range exceeds the size of the thing we're reading. This
538    // can happen when we truncate and there are outstanding pager requests that the kernel was not
539    // able to cancel in time.
540    let (read_range, zero_range) = pager_range.split(page_aligned_size);
541    if let Some(zero_range) = zero_range {
542        for range in zero_range.chunks(ZERO_VMO_SIZE) {
543            range.supply_pages(&ZERO_VMO, 0);
544        }
545    }
546
547    if let Some(read_range) = read_range {
548        let expanded_range_for_readahead = round_down(read_range.start(), read_ahead_size)
549            ..std::cmp::min(
550                round_up(read_range.end(), read_ahead_size).unwrap(),
551                page_aligned_size,
552            );
553        let read_range = read_range.expand(expanded_range_for_readahead);
554        for range in read_range.chunks(read_ahead_size) {
555            // Record the page in before spawning the task to handle the page-in. This is necessary
556            // so that we don't miss this page-in when replaying and recording a new profile.  The
557            // replay is considered finished once we've responded to the page request, so if we if
558            // we spawn the page request before recording the page-in, it's possible (albeit
559            // unlikely) that the profiler can think the replay has finished, but not know about the
560            // page request and so the next recording to be missing the page request.  With the
561            // order swapped, the `test_profile` test would have a rare flake.
562            this.pager().record_page_in(this.clone(), range.range.clone());
563
564            this.pager().spawn(page_in_chunk(this.clone(), range));
565        }
566    }
567}
568
569#[fxfs_trace::trace("offset" => read_range.start(), "len" => read_range.len())]
570async fn page_in_chunk<P: PagerBacked>(this: Arc<P>, read_range: PageInRange<P>) {
571    let buffer = match this.aligned_read(read_range.range()).await {
572        Ok(v) => v,
573        Err(error) => {
574            error!(range:? = read_range.range(), error:?; "Failed to load range");
575            read_range.report_failure(map_to_status(error));
576            return;
577        }
578    };
579    assert!(
580        buffer.len() as u64 >= read_range.len(),
581        "A buffer smaller than requested was returned. requested: {}, returned: {}",
582        read_range.len(),
583        buffer.len()
584    );
585    read_range.supply_pages(buffer.allocator().buffer_source().vmo(), buffer.range().start as u64);
586}
587
588/// Represents a dirty range of page aligned bytes within a pager backed VMO.
589#[repr(C)]
590#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
591pub struct VmoDirtyRange {
592    offset: u64,
593    length: u64,
594    options: u64,
595}
596
597impl VmoDirtyRange {
598    /// The page aligned byte range.
599    pub fn range(&self) -> Range<u64> {
600        self.offset..(self.offset + self.length)
601    }
602
603    /// Returns true if all of the bytes in the range are 0.
604    pub fn is_zero_range(&self) -> bool {
605        self.options & zx::sys::ZX_VMO_DIRTY_RANGE_IS_ZERO != 0
606    }
607}
608
609bitflags! {
610    /// Options for `Pager::query_vmo_stats`.
611    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
612    #[repr(transparent)]
613    pub struct PagerVmoStatsOptions: u32 {
614        /// Resets the stats at the of the `Pager::query_vmo_stats` call.
615        const RESET_VMO_STATS = 1;
616    }
617}
618
619/// Pager related statistic for a VMO.
620#[derive(Debug)]
621pub struct PagerVmoStats {
622    was_vmo_modified: bool,
623}
624
625impl PagerVmoStats {
626    /// Returns true if the VMO was modified since the last time the VMO stats were reset.
627    pub fn was_vmo_modified(&self) -> bool {
628        self.was_vmo_modified
629    }
630}
631
632/// A trait for specializing `PagerRange` for different request types.
633pub trait PagerRequestType {
634    /// Returns the name of the request type for logging purposes.
635    fn request_type_name() -> &'static str;
636}
637
638/// A request generated from a ZX_PAGER_VMO_READ packet.
639pub struct PageInRequest;
640
641impl PagerRequestType for PageInRequest {
642    fn request_type_name() -> &'static str {
643        "PageInRequest"
644    }
645}
646
647/// The requested range from a ZX_PAGER_VMO_READ packet. This object must not be dropped without
648/// calling either `supply_pages` or `report_failure`.
649pub type PageInRange<T> = PagerRange<T, PageInRequest>;
650
651impl<T: PagerBacked> PageInRange<T> {
652    /// Constructs a new `PageInRange<T>`. `range` must be page aligned.
653    pub fn new(range: Range<u64>, file: OpenedNode<T>, epoch_guard: EpochGuard<'static>) -> Self {
654        debug_assert!(page_size().is_aligned(&range), "{range:?} is not page aligned",);
655        Self {
656            range,
657            inner: Some(PagerRangeInner { file, _epoch_guard: Some(epoch_guard) }),
658            _request_type: PhantomData,
659        }
660    }
661
662    /// Supplies pages to the kernel for this range. See `zx_pager_supply_pages` for more
663    /// information.
664    pub fn supply_pages(mut self, transfer_vmo: &zx::Vmo, transfer_offset: u64) {
665        let inner = self.inner.take().unwrap();
666        inner.file.pager().supply_pages(
667            inner.file.vmo(),
668            self.range.clone(),
669            transfer_vmo,
670            transfer_offset,
671        );
672    }
673}
674
675/// A requested generated from a ZX_PAGER_VMO_DIRTY packet.
676#[derive(Debug)]
677pub struct MarkDirtyRequest;
678
679impl PagerRequestType for MarkDirtyRequest {
680    fn request_type_name() -> &'static str {
681        "MarkDirtyRequest"
682    }
683}
684
685/// The requested range from a ZX_PAGER_VMO_DIRTY packet. This object must not be dropped without
686/// calling either `mark_dirty` or `report_failure`.
687pub type MarkDirtyRange<T> = PagerRange<T, MarkDirtyRequest>;
688
689impl<T: PagerBacked> MarkDirtyRange<T> {
690    /// Constructs a new `MarkDirtyRange<T>`. `range` must be page aligned.
691    pub fn new(range: Range<u64>, file: OpenedNode<T>) -> Self {
692        debug_assert!(page_size().is_aligned(&range), "{range:?} is not page aligned");
693        Self {
694            range,
695            inner: Some(PagerRangeInner { file, _epoch_guard: None }),
696            _request_type: PhantomData,
697        }
698    }
699
700    /// Allows the kernel to dirty this range of pages. See `ZX_PAGER_OP_DIRTY` for more
701    /// information.
702    pub fn dirty_pages(mut self) -> Result<(), zx::Status> {
703        let inner = self.inner.take().unwrap();
704        inner.file.pager().dirty_pages(inner.file.vmo(), self.range.clone())
705    }
706}
707
708struct PagerRangeInner<T: PagerBacked> {
709    file: OpenedNode<T>,
710
711    /// Holds a reference to the current Epoch, so that in-flight read requests can be tracked. This
712    /// should be None for MarkDirty requests.
713    _epoch_guard: Option<EpochGuard<'static>>,
714}
715
716impl<T: PagerBacked> Clone for PagerRangeInner<T> {
717    fn clone(&self) -> Self {
718        Self { file: self.file.dup(), _epoch_guard: self._epoch_guard.clone() }
719    }
720}
721
722/// The requested range from a pager packet. This object ensures that all pager requests receive a
723/// response.
724pub struct PagerRange<T: PagerBacked, U: PagerRequestType> {
725    range: Range<u64>,
726
727    /// Contains the file and the ref guard. If this is None, then the request is complete.
728    inner: Option<PagerRangeInner<T>>,
729
730    _request_type: PhantomData<U>,
731}
732
733impl<T: PagerBacked, U: PagerRequestType> PagerRange<T, U> {
734    pub fn file(&self) -> &OpenedNode<T> {
735        &self.inner.as_ref().unwrap().file
736    }
737
738    /// Splits the underlying range allowing for different parts of the range to be handled and
739    /// responded to independently. See `RangeExt::split` for how splitting a range works.
740    /// `split_point` must be page aligned.
741    pub fn split(mut self, split_point: u64) -> (Option<Self>, Option<Self>) {
742        let inner = self.inner.take().unwrap();
743        let (left, right) = self.range.clone().split(split_point);
744        let right = right.map(|range| Self {
745            range,
746            inner: Some(inner.clone()),
747            _request_type: PhantomData,
748        });
749        let left = left.map(|range| Self { range, inner: Some(inner), _request_type: PhantomData });
750        (left, right)
751    }
752
753    /// Increases the size of the range that will be responded to. Panics if the current range is
754    /// not a subset of `new_range`. `new_range` must be page aligned.
755    pub fn expand(mut self, new_range: Range<u64>) -> Self {
756        assert!(
757            self.range.start >= new_range.start && self.range.end <= new_range.end,
758            "{:?} is not a subset of {:?}",
759            self.range,
760            new_range
761        );
762        debug_assert!(page_size().is_aligned(&new_range), "{new_range:?} is not page aligned");
763        self.range = new_range;
764        self
765    }
766
767    /// Returns an iterator that splits the range into ranges of `chunk_size`. If the length of the
768    /// range is not a multiple of `chunk_size` then the last chunk won't be of length `chunk_size`.
769    /// The returned iterator will panic if it's dropped without being fully consumed. `chunk_size`
770    /// must a multiple of the page size.
771    pub fn chunks(mut self, chunk_size: u64) -> PagerRangeChunksIter<T, U> {
772        debug_assert!(
773            page_size().is_aligned(chunk_size),
774            "{chunk_size} is not a multiple of the page size"
775        );
776        PagerRangeChunksIter {
777            start: self.range.start,
778            end: self.range.end,
779            chunk_size: chunk_size,
780            inner: self.inner.take(),
781            _request_type: PhantomData,
782        }
783    }
784
785    #[inline]
786    pub fn start(&self) -> u64 {
787        self.range.start
788    }
789
790    #[inline]
791    pub fn end(&self) -> u64 {
792        self.range.end
793    }
794
795    #[inline]
796    pub fn len(&self) -> u64 {
797        self.range.end - self.range.start
798    }
799
800    #[inline]
801    pub fn range(&self) -> Range<u64> {
802        self.range.clone()
803    }
804
805    /// Notifies the kernel that the page request for this range has failed. See `ZX_PAGER_OP_FAIL`
806    /// for more information.
807    pub fn report_failure(mut self, status: zx::Status) {
808        let inner = self.inner.take().unwrap();
809        inner.file.pager().report_failure(inner.file.vmo(), self.range.clone(), status);
810    }
811
812    /// Test only method that will consume the PagerRange without having the send a response.
813    #[cfg(test)]
814    fn consume(mut self) {
815        self.inner.take().unwrap();
816    }
817}
818
819impl<T: PagerBacked, U: PagerRequestType> Drop for PagerRange<T, U> {
820    fn drop(&mut self) {
821        if let Some(inner) = &self.inner {
822            let request_type = U::request_type_name();
823            let range = self.range.clone();
824            let key = inner.file.pager_packet_receiver_registration().key();
825            if cfg!(debug_assertions) {
826                // If this object is being dropped as part of a panic then avoid panicking again.
827                // Dropping pager packets when fxfs is crashing is acceptable. Panicking again would
828                // only clutter the logs.
829                if !std::thread::panicking() {
830                    panic!(
831                        "PagerRange was dropped without sending a response, \
832                        request_type={request_type}, range={range:?}, key={key}",
833                    );
834                }
835            } else {
836                error!(
837                    "PagerRange was dropped without sending a response, \
838                    request_type={request_type}, range={range:?}, key={key}",
839                );
840                inner.file.pager().report_failure(inner.file.vmo(), range, zx::Status::BAD_STATE);
841            }
842        }
843    }
844}
845
846/// An iterator similar to `std::slice::Chunks` which yields `PagerRange` objects.
847/// `PagerRangeChunksIter` will panic if it's dropped without being fully consumed.
848pub struct PagerRangeChunksIter<T: PagerBacked, U: PagerRequestType> {
849    start: u64,
850    end: u64,
851    chunk_size: u64,
852    /// The file and locks/references that need to survive the request.
853    inner: Option<PagerRangeInner<T>>,
854    _request_type: PhantomData<U>,
855}
856
857impl<T: PagerBacked, U: PagerRequestType> Iterator for PagerRangeChunksIter<T, U> {
858    type Item = PagerRange<T, U>;
859    fn next(&mut self) -> Option<Self::Item> {
860        if self.start == self.end {
861            None
862        } else if self.start + self.chunk_size >= self.end {
863            let next = Self::Item {
864                range: self.start..self.end,
865                inner: self.inner.take(),
866                _request_type: PhantomData,
867            };
868            self.start = self.end;
869            Some(next)
870        } else {
871            let next_end = self.start + self.chunk_size;
872            let next = Self::Item {
873                range: self.start..next_end,
874                inner: self.inner.clone(),
875                _request_type: PhantomData,
876            };
877            self.start = next_end;
878            Some(next)
879        }
880    }
881}
882
883impl<T: PagerBacked, U: PagerRequestType> Drop for PagerRangeChunksIter<T, U> {
884    fn drop(&mut self) {
885        if self.start != self.end {
886            let request_type = U::request_type_name();
887            let remaining = self.start..self.end;
888            let inner = self.inner.take().unwrap();
889            let key = inner.file.pager_packet_receiver_registration().key();
890            if cfg!(debug_assertions) {
891                // If this object is being dropped as part of a panic then avoid panicking again.
892                // Dropping pager packets when fxfs is crashing is acceptable. Panicking again would
893                // only clutter the logs.
894                if !std::thread::panicking() {
895                    panic!(
896                        "PagerRangeChunksIter was dropped without being fully consumed, \
897                    request_type={request_type}, remaining={remaining:?}, key={key}",
898                    );
899                }
900            } else {
901                error!(
902                    "PagerRangeChunksIter was dropped without being fully consumed, \
903                    request_type={request_type}, remaining={remaining:?}, key={key}",
904                );
905                inner.file.pager().report_failure(
906                    inner.file.vmo(),
907                    remaining,
908                    zx::Status::BAD_STATE,
909                );
910            }
911        }
912    }
913}
914
915#[cfg(test)]
916mod tests {
917    use super::*;
918    use futures::StreamExt;
919    use futures::channel::mpsc;
920    use fxfs_macros::ToWeakNode;
921
922    fn page_size() -> u64 {
923        storage_units::page_size().get()
924    }
925
926    #[derive(Clone, Debug, PartialEq, Eq)]
927    enum PagerRequest {
928        PageIn(Range<u64>),
929        Dirty(Range<u64>),
930    }
931
932    #[derive(ToWeakNode)]
933    struct MockFile {
934        vmo: zx::Vmo,
935        pager_packet_receiver_registration: PagerPacketReceiverRegistration<Self>,
936        pager: Arc<Pager>,
937        /// page in requests get logged so we can compare actual calls to to expectations.
938        pager_requests: Mutex<Vec<PagerRequest>>,
939    }
940
941    impl MockFile {
942        fn new(pager: Arc<Pager>) -> Arc<Self> {
943            Self::new_with_size_and_type(pager, page_size(), zx::VmoOptions::UNBOUNDED)
944        }
945
946        fn new_with_size_and_type(
947            pager: Arc<Pager>,
948            size: u64,
949            vmo_type: zx::VmoOptions,
950        ) -> Arc<Self> {
951            Arc::new_cyclic(|weak| {
952                let (vmo, pager_packet_receiver_registration) = pager
953                    .create_vmo(weak.clone(), size, vmo_type | zx::VmoOptions::TRAP_DIRTY)
954                    .unwrap();
955                Self {
956                    pager,
957                    vmo,
958                    pager_packet_receiver_registration,
959                    pager_requests: Default::default(),
960                }
961            })
962        }
963
964        // Returns the page_in requests received for this file.
965        fn pager_requests(&self, reset: bool) -> Vec<PagerRequest> {
966            if reset {
967                std::mem::take(&mut *self.pager_requests.lock())
968            } else {
969                self.pager_requests.lock().clone()
970            }
971        }
972    }
973
974    impl FxNode for MockFile {
975        fn object_id(&self) -> u64 {
976            unimplemented!();
977        }
978
979        fn parent(&self) -> Option<Arc<crate::directory::FxDirectory>> {
980            unimplemented!();
981        }
982
983        fn set_parent(&self, _parent: Arc<crate::directory::FxDirectory>) {
984            unimplemented!();
985        }
986
987        fn open_count_add_one(&self) {}
988
989        fn open_count_sub_one(self: Arc<Self>) {}
990
991        fn object_descriptor(&self) -> fxfs::object_store::ObjectDescriptor {
992            unimplemented!();
993        }
994    }
995
996    impl PagerBacked for MockFile {
997        fn try_keep_open(self: Arc<Self>) -> Result<OpenedNode<Self>, Arc<Self>> {
998            Ok(OpenedNode(self))
999        }
1000
1001        fn pager(&self) -> &Pager {
1002            &self.pager
1003        }
1004
1005        fn pager_packet_receiver_registration(&self) -> &PagerPacketReceiverRegistration<Self> {
1006            &self.pager_packet_receiver_registration
1007        }
1008
1009        fn vmo(&self) -> &zx::Vmo {
1010            &self.vmo
1011        }
1012
1013        fn page_in(self: Arc<Self>, range: PageInRange<Self>) {
1014            let aux_vmo = zx::Vmo::create(range.len()).unwrap();
1015            self.pager_requests.lock().push(PagerRequest::PageIn(range.range()));
1016            range.supply_pages(&aux_vmo, 0);
1017        }
1018
1019        fn mark_dirty(self: Arc<Self>, range: MarkDirtyRange<Self>) {
1020            self.pager_requests.lock().push(PagerRequest::Dirty(range.range()));
1021            let _ = range.dirty_pages();
1022        }
1023
1024        fn on_zero_children(self: Arc<Self>) {}
1025
1026        fn byte_size(&self) -> u64 {
1027            unimplemented!();
1028        }
1029        async fn aligned_read(
1030            &self,
1031            _aligned_byte_range: std::ops::Range<u64>,
1032        ) -> Result<buffer::Buffer<'_>, Error> {
1033            unimplemented!();
1034        }
1035    }
1036
1037    #[derive(ToWeakNode)]
1038    struct OnZeroChildrenFile {
1039        pager: Arc<Pager>,
1040        vmo: zx::Vmo,
1041        pager_packet_receiver_registration: PagerPacketReceiverRegistration<Self>,
1042        sender: Mutex<mpsc::UnboundedSender<()>>,
1043    }
1044
1045    impl OnZeroChildrenFile {
1046        fn new(pager: Arc<Pager>, sender: mpsc::UnboundedSender<()>) -> Arc<Self> {
1047            Arc::new_cyclic(|weak| {
1048                let (vmo, pager_packet_receiver_registration) =
1049                    pager.create_vmo(weak.clone(), page_size(), zx::VmoOptions::empty()).unwrap();
1050                Self { pager, vmo, pager_packet_receiver_registration, sender: Mutex::new(sender) }
1051            })
1052        }
1053    }
1054
1055    impl FxNode for OnZeroChildrenFile {
1056        fn object_id(&self) -> u64 {
1057            unimplemented!();
1058        }
1059
1060        fn parent(&self) -> Option<Arc<crate::directory::FxDirectory>> {
1061            unimplemented!();
1062        }
1063
1064        fn set_parent(&self, _parent: Arc<crate::directory::FxDirectory>) {
1065            unimplemented!();
1066        }
1067
1068        fn open_count_add_one(&self) {}
1069
1070        fn open_count_sub_one(self: Arc<Self>) {}
1071
1072        fn object_descriptor(&self) -> fxfs::object_store::ObjectDescriptor {
1073            unimplemented!();
1074        }
1075    }
1076
1077    impl PagerBacked for OnZeroChildrenFile {
1078        fn try_keep_open(self: Arc<Self>) -> Result<OpenedNode<Self>, Arc<Self>> {
1079            Ok(OpenedNode(self))
1080        }
1081
1082        fn pager(&self) -> &Pager {
1083            &self.pager
1084        }
1085
1086        fn pager_packet_receiver_registration(&self) -> &PagerPacketReceiverRegistration<Self> {
1087            &self.pager_packet_receiver_registration
1088        }
1089
1090        fn vmo(&self) -> &zx::Vmo {
1091            &self.vmo
1092        }
1093
1094        fn page_in(self: Arc<Self>, _range: PageInRange<Self>) {
1095            unreachable!();
1096        }
1097
1098        fn mark_dirty(self: Arc<Self>, _range: MarkDirtyRange<Self>) {
1099            unreachable!();
1100        }
1101
1102        fn on_zero_children(self: Arc<Self>) {
1103            self.sender.lock().unbounded_send(()).unwrap();
1104        }
1105        fn byte_size(&self) -> u64 {
1106            unreachable!();
1107        }
1108        async fn aligned_read(
1109            &self,
1110            _aligned_byte_range: std::ops::Range<u64>,
1111        ) -> Result<buffer::Buffer<'_>, Error> {
1112            unreachable!();
1113        }
1114    }
1115
1116    #[fuchsia::test(threads = 2)]
1117    async fn test_watch_for_zero_children() {
1118        let (sender, mut receiver) = mpsc::unbounded();
1119        let scope = ExecutionScope::new();
1120        let pager = Arc::new(Pager::new(scope.clone()).unwrap());
1121        let file = OnZeroChildrenFile::new(pager.clone(), sender);
1122        {
1123            let _child_vmo = file
1124                .vmo()
1125                .create_child(
1126                    zx::VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE,
1127                    0,
1128                    file.vmo().get_content_size().unwrap(),
1129                )
1130                .unwrap();
1131            assert!(pager.watch_for_zero_children(file.as_ref()).unwrap());
1132        }
1133        // Wait for `on_zero_children` to be called.
1134        receiver.next().await.unwrap();
1135
1136        scope.wait().await;
1137    }
1138
1139    #[fuchsia::test(threads = 2)]
1140    async fn test_multiple_watch_for_zero_children_calls() {
1141        let (sender, mut receiver) = mpsc::unbounded();
1142        let scope = ExecutionScope::new();
1143        let pager = Arc::new(Pager::new(scope.clone()).unwrap());
1144        let file = OnZeroChildrenFile::new(pager.clone(), sender);
1145        {
1146            let _child_vmo = file
1147                .vmo()
1148                .create_child(
1149                    zx::VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE,
1150                    0,
1151                    file.vmo().get_content_size().unwrap(),
1152                )
1153                .unwrap();
1154            assert!(pager.watch_for_zero_children(file.as_ref()).unwrap());
1155            // `watch_for_zero_children` will return false when it's already watching.
1156            assert!(!pager.watch_for_zero_children(file.as_ref()).unwrap());
1157        }
1158        receiver.next().await.unwrap();
1159
1160        // The pager stops listening for VMO_ZERO_CHILDREN once the signal fires. Calling
1161        // `watch_for_zero_children` afterwards should return true again because watching had
1162        // stopped.
1163        assert!(pager.watch_for_zero_children(file.as_ref()).unwrap());
1164
1165        file.pager_packet_receiver_registration.stop_watching_for_zero_children();
1166
1167        scope.wait().await;
1168    }
1169
1170    #[fuchsia::test(threads = 2)]
1171    async fn test_status_code_mapping() {
1172        #[derive(ToWeakNode)]
1173        struct StatusCodeFile {
1174            vmo: zx::Vmo,
1175            pager: Arc<Pager>,
1176            status_code: Mutex<zx::Status>,
1177            pager_packet_receiver_registration: PagerPacketReceiverRegistration<Self>,
1178        }
1179
1180        impl FxNode for StatusCodeFile {
1181            fn object_id(&self) -> u64 {
1182                unimplemented!();
1183            }
1184
1185            fn parent(&self) -> Option<Arc<crate::directory::FxDirectory>> {
1186                unimplemented!();
1187            }
1188
1189            fn set_parent(&self, _parent: Arc<crate::directory::FxDirectory>) {
1190                unimplemented!();
1191            }
1192
1193            fn open_count_add_one(&self) {}
1194
1195            fn open_count_sub_one(self: Arc<Self>) {}
1196
1197            fn object_descriptor(&self) -> fxfs::object_store::ObjectDescriptor {
1198                unimplemented!();
1199            }
1200        }
1201
1202        impl PagerBacked for StatusCodeFile {
1203            fn try_keep_open(self: Arc<Self>) -> Result<OpenedNode<Self>, Arc<Self>> {
1204                Ok(OpenedNode(self))
1205            }
1206
1207            fn pager(&self) -> &Pager {
1208                &self.pager
1209            }
1210
1211            fn pager_packet_receiver_registration(&self) -> &PagerPacketReceiverRegistration<Self> {
1212                &self.pager_packet_receiver_registration
1213            }
1214
1215            fn vmo(&self) -> &zx::Vmo {
1216                &self.vmo
1217            }
1218
1219            fn page_in(self: Arc<Self>, range: PageInRange<Self>) {
1220                range.report_failure(*self.status_code.lock());
1221            }
1222
1223            fn mark_dirty(self: Arc<Self>, _range: MarkDirtyRange<Self>) {
1224                unreachable!();
1225            }
1226
1227            fn on_zero_children(self: Arc<Self>) {
1228                unreachable!();
1229            }
1230
1231            fn byte_size(&self) -> u64 {
1232                unreachable!();
1233            }
1234
1235            async fn aligned_read(
1236                &self,
1237                _aligned_byte_range: std::ops::Range<u64>,
1238            ) -> Result<buffer::Buffer<'_>, Error> {
1239                unreachable!();
1240            }
1241        }
1242
1243        let scope = ExecutionScope::new();
1244        let pager = Arc::new(Pager::new(scope.clone()).unwrap());
1245        let file = Arc::new_cyclic(|weak| {
1246            let (vmo, pager_packet_receiver_registration) =
1247                pager.create_vmo(weak.clone(), page_size(), zx::VmoOptions::empty()).unwrap();
1248            StatusCodeFile {
1249                vmo,
1250                pager: pager.clone(),
1251                status_code: Mutex::new(zx::Status::INTERNAL),
1252                pager_packet_receiver_registration,
1253            }
1254        });
1255
1256        fn check_mapping(
1257            file: &StatusCodeFile,
1258            failure_code: zx::Status,
1259            expected_code: zx::Status,
1260        ) {
1261            {
1262                *file.status_code.lock() = failure_code;
1263            }
1264            let mut buf = [0u8; 8];
1265            assert_eq!(file.vmo().read(&mut buf, 0).unwrap_err(), expected_code);
1266        }
1267        check_mapping(&file, zx::Status::IO_DATA_INTEGRITY, zx::Status::IO_DATA_INTEGRITY);
1268        check_mapping(&file, zx::Status::NO_SPACE, zx::Status::NO_SPACE);
1269        check_mapping(&file, zx::Status::FILE_BIG, zx::Status::BUFFER_TOO_SMALL);
1270        check_mapping(&file, zx::Status::IO, zx::Status::IO);
1271        check_mapping(&file, zx::Status::IO_DATA_LOSS, zx::Status::IO);
1272        check_mapping(&file, zx::Status::NOT_EMPTY, zx::Status::BAD_STATE);
1273        check_mapping(&file, zx::Status::BAD_STATE, zx::Status::BAD_STATE);
1274
1275        scope.wait().await;
1276    }
1277
1278    #[fuchsia::test(threads = 2)]
1279    async fn test_query_vmo_stats() {
1280        let scope = ExecutionScope::new();
1281        let pager = Arc::new(Pager::new(scope.clone()).unwrap());
1282        let file = MockFile::new(pager.clone());
1283
1284        let stats = pager.query_vmo_stats(file.vmo(), PagerVmoStatsOptions::empty()).unwrap();
1285        // The VMO hasn't been modified yet.
1286        assert!(!stats.was_vmo_modified());
1287
1288        file.vmo().write(&[0, 1, 2, 3, 4], 0).unwrap();
1289        let stats = pager.query_vmo_stats(file.vmo(), PagerVmoStatsOptions::empty()).unwrap();
1290        assert!(stats.was_vmo_modified());
1291
1292        // Reset the stats this time.
1293        let stats =
1294            pager.query_vmo_stats(file.vmo(), PagerVmoStatsOptions::RESET_VMO_STATS).unwrap();
1295        // The stats weren't reset last time so the stats are still showing that the vmo is modified.
1296        assert!(stats.was_vmo_modified());
1297
1298        let stats = pager.query_vmo_stats(file.vmo(), PagerVmoStatsOptions::empty()).unwrap();
1299        assert!(!stats.was_vmo_modified());
1300
1301        scope.wait().await;
1302    }
1303
1304    #[fuchsia::test(threads = 2)]
1305    async fn test_query_dirty_ranges() {
1306        // Some notes on our paging implementation:
1307        //  * Fxfs uses UNBOUNDED VMO. These are maximally sized at creation time with
1308        //    stream size holding the content length.
1309        //  * Like regular VMO, all pages are initially in an unknown state. When a page
1310        //    is first accessed, the pager (Fxfs) will be asked to page in content.
1311        //  * Size can be set as a property, via set_content_size or via set_stream_size
1312        //    but only set_stream_size() should ever be used. This ensures that the tail
1313        //    is correctly zeroed.
1314        let scope = ExecutionScope::new();
1315        let pager = Arc::new(Pager::new(scope.clone()).unwrap());
1316        let file = MockFile::new_with_size_and_type(
1317            pager.clone(),
1318            page_size() + page_size() / 2,
1319            zx::VmoOptions::UNBOUNDED,
1320        );
1321        let mut buffer = vec![VmoDirtyRange::default(); 2];
1322
1323        let page_size = page_size();
1324        assert_eq!(file.vmo().get_content_size().unwrap(), page_size + page_size / 2);
1325
1326        let (actual, remaining) =
1327            pager.query_dirty_ranges(file.vmo(), 0..page_size * 100, &mut buffer).unwrap();
1328        assert_eq!(actual, 0);
1329        assert_eq!(remaining, 0);
1330
1331        // Grow the VMO content size from 1.5 pages to 7.5 pages.
1332        file.vmo().set_stream_size(page_size * 7 + page_size / 2).unwrap();
1333
1334        let (actual, remaining) =
1335            pager.query_dirty_ranges(file.vmo(), 0..page_size * 100, &mut buffer).unwrap();
1336        assert_eq!(actual, 2);
1337        assert_eq!(remaining, 0);
1338        // Second page must be assumed to contain data so tail is zeroed.
1339        assert_eq!(buffer[0].range(), page_size..page_size * 2);
1340        assert!(!buffer[0].is_zero_range());
1341        // All pages after that are marked as zero.
1342        assert_eq!(buffer[1].range(), page_size * 2..page_size * 8);
1343        assert!(buffer[1].is_zero_range());
1344
1345        // We expect the tail page to have been read as part of the zeroing when we grew the size.
1346        // It will then be marked dirty (modified)
1347        assert_eq!(
1348            file.pager_requests(true),
1349            vec![
1350                PagerRequest::PageIn(page_size * 1..page_size * 2),
1351                PagerRequest::Dirty(page_size * 1..page_size * 2),
1352            ]
1353        );
1354
1355        // Modify the 2nd, 3rd, and 5th pages.
1356        file.vmo().write(&[1, 2, 3, 4], page_size).unwrap();
1357        file.vmo().write(&[1, 2, 3, 4], page_size * 2).unwrap();
1358        file.vmo().write(&[1, 2, 3, 4], page_size * 4).unwrap();
1359
1360        // Pages are known zero because we just grew the file.
1361        // We don't expect any page-in requests for them.
1362        assert_eq!(
1363            file.pager_requests(true),
1364            vec![
1365                PagerRequest::Dirty(page_size * 2..page_size * 3),
1366                PagerRequest::Dirty(page_size * 4..page_size * 5)
1367            ]
1368        );
1369
1370        let (actual, remaining) =
1371            pager.query_dirty_ranges(file.vmo(), 0..page_size * 7, &mut buffer).unwrap();
1372        assert_eq!(actual, 2);
1373        assert_eq!(remaining, 2);
1374        // Second and third pages (non-zero)
1375        assert_eq!(buffer[0].range(), page_size..(page_size * 3));
1376        assert!(!buffer[0].is_zero_range());
1377        // Fourth page is zero.
1378        assert_eq!(buffer[1].range(), (page_size * 3)..(page_size * 4));
1379        assert!(buffer[1].is_zero_range());
1380
1381        let (actual, remaining) = pager
1382            .query_dirty_ranges(file.vmo(), page_size * 4..page_size * 7, &mut buffer)
1383            .unwrap();
1384        assert_eq!(actual, 2);
1385        assert_eq!(remaining, 0);
1386        // Fifth page (non-zero)
1387        assert_eq!(buffer[0].range(), (page_size * 4)..(page_size * 5));
1388        assert!(!buffer[0].is_zero_range());
1389        // Rest of the VMO is zero.
1390        assert_eq!(buffer[1].range(), (page_size * 5)..(page_size * 7));
1391        assert!(buffer[1].is_zero_range());
1392
1393        // Read the 4th page.
1394        let mut read_buf = vec![0u8; page_size as usize];
1395        file.vmo().read(&mut read_buf, page_size * 3).expect("read");
1396        let expected = vec![0u8; page_size as usize];
1397        assert_eq!(read_buf, expected);
1398        assert_eq!(file.pager_requests(true), vec![]);
1399
1400        scope.wait().await;
1401    }
1402
1403    #[fuchsia::test(threads = 2)]
1404    async fn test_zero_grown_vmo() {
1405        // When a VMO's content size is explicitly grown, check that new content is zeroed.
1406        let scope = ExecutionScope::new();
1407        let pager = Arc::new(Pager::new(scope.clone()).unwrap());
1408        let file = MockFile::new(pager.clone());
1409
1410        let write_buf = vec![0xff; page_size() as usize * 2];
1411        file.vmo().set_stream_size(page_size() * 2).expect("grow");
1412        file.vmo().write(&write_buf, 0).expect("write");
1413        let mut read_buf = vec![0u8; page_size() as usize * 2];
1414        // The tail beyond the content size will written.
1415        file.vmo().read(&mut read_buf, 0).expect("read");
1416        assert_eq!(read_buf, write_buf);
1417
1418        // The tail beyond the new content size should be zeroed.
1419        file.vmo().set_stream_size(page_size() + 1).expect("shrink");
1420        file.vmo().write(&[0xff; 3], page_size() + 2).expect("write after shrink");
1421        // To make sure the above content size change actually zeroed data, we grow again.
1422        file.vmo().set_stream_size(page_size() + 4).expect("grow again");
1423        let mut read_buf = vec![0u8; page_size() as usize];
1424        file.vmo().read(&mut read_buf, page_size()).expect("read");
1425        let mut expected = vec![0u8; page_size() as usize];
1426        expected[0] = 0xff;
1427        assert_eq!(read_buf, expected);
1428
1429        scope.wait().await;
1430    }
1431
1432    #[fuchsia::test]
1433    async fn test_pager_range_chunks_iter_chunks() {
1434        let scope = ExecutionScope::new();
1435        let pager = Arc::new(Pager::new(scope).unwrap());
1436        let file = MockFile::new(pager.clone());
1437
1438        let pager_range =
1439            PageInRange::new(0..page_size() * 5, OpenedNode::new(file), Epoch::global().guard());
1440        let ranges: Vec<Range<u64>> = pager_range
1441            .chunks(page_size() * 2)
1442            .map(|pager_range| {
1443                let range = pager_range.range();
1444                pager_range.consume();
1445                range
1446            })
1447            .collect();
1448        assert_eq!(
1449            ranges,
1450            [
1451                0..page_size() * 2,
1452                page_size() * 2..page_size() * 4,
1453                page_size() * 4..page_size() * 5
1454            ]
1455        );
1456    }
1457
1458    #[fuchsia::test]
1459    async fn test_pager_range_split() {
1460        let scope = ExecutionScope::new();
1461        let pager = Arc::new(Pager::new(scope).unwrap());
1462        let file = MockFile::new(pager.clone());
1463
1464        let pager_range =
1465            PageInRange::new(0..page_size() * 10, OpenedNode::new(file), Epoch::global().guard());
1466        let (left, right) = pager_range.split(page_size() * 5);
1467        let (left, right) = (left.unwrap(), right.unwrap());
1468        assert_eq!(left.range(), 0..page_size() * 5);
1469        assert_eq!(right.range(), page_size() * 5..page_size() * 10);
1470
1471        left.consume();
1472        right.consume();
1473    }
1474
1475    #[fuchsia::test]
1476    #[should_panic(expected = "0..8192 is not a subset of 0..4096")]
1477    async fn test_pager_range_bad_expand_panics() {
1478        let scope = ExecutionScope::new();
1479        let pager = Arc::new(Pager::new(scope).unwrap());
1480        let file = MockFile::new(pager.clone());
1481
1482        let pager_range =
1483            PageInRange::new(0..page_size() * 2, OpenedNode::new(file), Epoch::global().guard());
1484        pager_range.expand(0..page_size()).consume();
1485    }
1486
1487    #[derive(ToWeakNode)]
1488    struct PagerRangeTestFile {
1489        vmo: zx::Vmo,
1490        pager_packet_receiver_registration: PagerPacketReceiverRegistration<Self>,
1491        pager: Pager,
1492        page_in_fn: Box<dyn Fn(PageInRange<Self>) + Send + Sync + 'static>,
1493        mark_dirty_fn: Box<dyn Fn(MarkDirtyRange<Self>) + Send + Sync + 'static>,
1494    }
1495
1496    impl PagerRangeTestFile {
1497        fn new<
1498            F1: Fn(PageInRange<Self>) + Send + Sync + 'static,
1499            F2: Fn(MarkDirtyRange<Self>) + Send + Sync + 'static,
1500        >(
1501            page_in_fn: F1,
1502            mark_dirty_fn: F2,
1503        ) -> Arc<Self> {
1504            Arc::new_cyclic(|weak| {
1505                let pager = Pager::new(ExecutionScope::new()).unwrap();
1506                let (vmo, pager_packet_receiver_registration) = pager
1507                    .create_vmo(weak.clone(), page_size() * 2, zx::VmoOptions::TRAP_DIRTY)
1508                    .unwrap();
1509                Self {
1510                    vmo,
1511                    pager_packet_receiver_registration,
1512                    pager,
1513                    page_in_fn: Box::new(page_in_fn),
1514                    mark_dirty_fn: Box::new(mark_dirty_fn),
1515                }
1516            })
1517        }
1518    }
1519
1520    impl FxNode for PagerRangeTestFile {
1521        fn object_id(&self) -> u64 {
1522            1
1523        }
1524
1525        fn parent(&self) -> Option<Arc<crate::directory::FxDirectory>> {
1526            unimplemented!()
1527        }
1528
1529        fn set_parent(&self, _parent: Arc<crate::directory::FxDirectory>) {
1530            unimplemented!()
1531        }
1532
1533        fn open_count_add_one(&self) {}
1534
1535        fn open_count_sub_one(self: Arc<Self>) {}
1536
1537        fn object_descriptor(&self) -> fxfs::object_store::ObjectDescriptor {
1538            unimplemented!()
1539        }
1540    }
1541
1542    impl PagerBacked for PagerRangeTestFile {
1543        fn try_keep_open(self: Arc<Self>) -> Result<OpenedNode<Self>, Arc<Self>> {
1544            Ok(OpenedNode(self))
1545        }
1546
1547        fn pager(&self) -> &Pager {
1548            &self.pager
1549        }
1550
1551        fn pager_packet_receiver_registration(&self) -> &PagerPacketReceiverRegistration<Self> {
1552            &self.pager_packet_receiver_registration
1553        }
1554
1555        fn vmo(&self) -> &zx::Vmo {
1556            &self.vmo
1557        }
1558
1559        fn page_in(self: Arc<Self>, range: PageInRange<Self>) {
1560            (self.page_in_fn)(range)
1561        }
1562
1563        fn mark_dirty(self: Arc<Self>, range: MarkDirtyRange<Self>) {
1564            (self.mark_dirty_fn)(range)
1565        }
1566
1567        fn on_zero_children(self: Arc<Self>) {}
1568
1569        fn byte_size(&self) -> u64 {
1570            unimplemented!();
1571        }
1572
1573        async fn aligned_read(
1574            &self,
1575            _range: std::ops::Range<u64>,
1576        ) -> Result<buffer::Buffer<'_>, Error> {
1577            unimplemented!();
1578        }
1579    }
1580
1581    fn real_supply_pages(range: PageInRange<PagerRangeTestFile>) {
1582        let aux_vmo = zx::Vmo::create(range.len()).unwrap();
1583        range.supply_pages(&aux_vmo, 0);
1584    }
1585
1586    fn real_mark_dirty(range: MarkDirtyRange<PagerRangeTestFile>) {
1587        let _ = range.dirty_pages();
1588    }
1589
1590    #[fuchsia::test(threads = 2)]
1591    async fn test_page_in_range_supply_pages() {
1592        let file = PagerRangeTestFile::new(real_supply_pages, real_mark_dirty);
1593
1594        let mut data = vec![0; 20];
1595        file.vmo.read(&mut data, 0).unwrap();
1596    }
1597
1598    #[fuchsia::test(threads = 2)]
1599    async fn test_page_in_range_report_failure() {
1600        let file = PagerRangeTestFile::new(
1601            |range| {
1602                range.report_failure(zx::Status::IO_DATA_INTEGRITY);
1603            },
1604            real_mark_dirty,
1605        );
1606
1607        let mut data = vec![0; 20];
1608        let err = file.vmo.read(&mut data, 0).unwrap_err();
1609        assert_eq!(err, zx::Status::IO_DATA_INTEGRITY);
1610    }
1611
1612    #[cfg(debug_assertions)]
1613    #[fuchsia::test(threads = 2)]
1614    #[should_panic(expected = "PagerRange was dropped without sending a response")]
1615    async fn test_page_in_range_dropped() {
1616        let file = PagerRangeTestFile::new(|_| {}, real_mark_dirty);
1617
1618        let mut data = vec![0; 20];
1619        file.vmo.read(&mut data, 0).unwrap_err();
1620    }
1621
1622    #[cfg(not(debug_assertions))]
1623    #[fuchsia::test(threads = 2)]
1624    async fn test_page_in_range_dropped() {
1625        let file = PagerRangeTestFile::new(|_| {}, real_mark_dirty);
1626
1627        let mut data = vec![0; 20];
1628        let err = file.vmo.read(&mut data, 0).unwrap_err();
1629        assert_eq!(err, zx::Status::BAD_STATE);
1630    }
1631
1632    #[fuchsia::test(threads = 2)]
1633    async fn test_mark_dirty_range_dirty_pages() {
1634        let file = PagerRangeTestFile::new(real_supply_pages, real_mark_dirty);
1635
1636        let data = vec![5; 20];
1637        file.vmo.write(&data, 0).unwrap();
1638    }
1639
1640    #[fuchsia::test(threads = 2)]
1641    async fn test_mark_dirty_range_report_failure() {
1642        let file = PagerRangeTestFile::new(real_supply_pages, |range| {
1643            range.report_failure(zx::Status::IO_DATA_INTEGRITY);
1644        });
1645
1646        let data = vec![5; 20];
1647        let err = file.vmo.write(&data, 0).unwrap_err();
1648        assert_eq!(err, zx::Status::IO_DATA_INTEGRITY);
1649    }
1650
1651    #[cfg(debug_assertions)]
1652    #[fuchsia::test(threads = 2)]
1653    #[should_panic(expected = "PagerRange was dropped without sending a response")]
1654    async fn test_mark_dirty_range_dropped() {
1655        let file = PagerRangeTestFile::new(real_supply_pages, |_| {});
1656
1657        let data = vec![5; 20];
1658        file.vmo.write(&data, 0).unwrap_err();
1659    }
1660
1661    #[cfg(not(debug_assertions))]
1662    #[fuchsia::test(threads = 2)]
1663    async fn test_mark_dirty_range_dropped() {
1664        let file = PagerRangeTestFile::new(real_supply_pages, |_| {});
1665
1666        let data = vec![5; 20];
1667        let err = file.vmo.write(&data, 0).unwrap_err();
1668        assert_eq!(err, zx::Status::BAD_STATE);
1669    }
1670
1671    #[fuchsia::test(threads = 2)]
1672    async fn test_pager_range_chunks_iter_consumed() {
1673        let file = PagerRangeTestFile::new(
1674            |range| {
1675                let aux_vmo = zx::Vmo::create(page_size()).unwrap();
1676                range.expand(0..page_size() * 2).chunks(page_size()).for_each(|range| {
1677                    range.supply_pages(&aux_vmo, 0);
1678                });
1679            },
1680            real_mark_dirty,
1681        );
1682
1683        let mut data = vec![0; 20];
1684        file.vmo.read(&mut data, 0).unwrap();
1685    }
1686
1687    fn partial_supply_pages(range: PageInRange<PagerRangeTestFile>) {
1688        let aux_vmo = zx::Vmo::create(page_size()).unwrap();
1689        // Expand the range to 2 pages and only supply the first page, dropping the iterator without
1690        // fully consuming it.
1691        range.expand(0..page_size() * 2).chunks(page_size()).take(1).for_each(|range| {
1692            range.supply_pages(&aux_vmo, 0);
1693        });
1694    }
1695
1696    #[cfg(debug_assertions)]
1697    #[fuchsia::test(threads = 2)]
1698    #[should_panic(expected = "PagerRangeChunksIter was dropped without being fully consumed")]
1699    async fn test_pager_range_chunks_iter_dropped() {
1700        let file = PagerRangeTestFile::new(partial_supply_pages, real_mark_dirty);
1701
1702        let mut data = vec![0; 20];
1703        // Ask for the 2nd page. The range will be expanded to the first 2 pages. The first page
1704        // will succeed and the second page will be dropped.
1705        file.vmo.read(&mut data, page_size()).unwrap_err();
1706    }
1707
1708    #[cfg(not(debug_assertions))]
1709    #[fuchsia::test(threads = 2)]
1710    async fn test_pager_range_chunks_iter_dropped() {
1711        let file = PagerRangeTestFile::new(partial_supply_pages, real_mark_dirty);
1712
1713        let mut data = vec![0; 20];
1714        // Ask for the 2nd page. The range will be expanded to the first 2 pages. The first page
1715        // will succeed and the second page will be dropped.
1716        let err = file.vmo.read(&mut data, page_size()).unwrap_err();
1717        assert_eq!(err, zx::Status::BAD_STATE);
1718    }
1719
1720    #[fuchsia::test(threads = 2)]
1721    async fn test_grow_zeroes_new_bytes() {
1722        // We expect that when we grow a file, the pages between the old and the new size
1723        // are zeroed. Reads and writes to these pages after growing a file should NOT
1724        // trigger any page-in requests.
1725        let scope = ExecutionScope::new();
1726        let pager = Arc::new(Pager::new(scope.clone()).unwrap());
1727        let page_size = page_size();
1728        let vmo_size: u64 = page_size * 2;
1729        let file_a =
1730            MockFile::new_with_size_and_type(pager.clone(), vmo_size, zx::VmoOptions::RESIZABLE);
1731        let file_b =
1732            MockFile::new_with_size_and_type(pager.clone(), vmo_size, zx::VmoOptions::UNBOUNDED);
1733        let mut buffer = vec![VmoDirtyRange::default(); 3];
1734
1735        assert_eq!(file_a.vmo().get_stream_size().unwrap(), page_size * 2);
1736        assert_eq!(file_b.vmo().get_stream_size().unwrap(), page_size * 2);
1737
1738        // Page in is expected.
1739        let mut read_buf = vec![0u8; page_size as usize];
1740        file_a.vmo().read(&mut read_buf, page_size).expect("read a");
1741        assert_eq!(
1742            file_a.pager_requests(true),
1743            vec![PagerRequest::PageIn(page_size..page_size * 2)]
1744        );
1745        file_b.vmo().read(&mut read_buf, page_size).expect("read b");
1746        assert_eq!(
1747            file_b.pager_requests(true),
1748            vec![PagerRequest::PageIn(page_size..page_size * 2)]
1749        );
1750
1751        // Grow the VMO size and confirm intermediate pages (2..8) are zero.
1752        let vmo_size = page_size * 8;
1753        file_a.vmo().set_size(vmo_size).unwrap();
1754        file_b.vmo().set_stream_size(vmo_size).unwrap();
1755
1756        assert_eq!(
1757            pager.query_dirty_ranges(file_a.vmo(), 0..vmo_size, &mut buffer).unwrap(),
1758            (1, 0)
1759        );
1760        assert_eq!(
1761            buffer[0],
1762            VmoDirtyRange { offset: page_size * 2, length: page_size * 6, options: 1 },
1763        );
1764        assert_eq!(
1765            pager.query_dirty_ranges(file_b.vmo(), 0..vmo_size, &mut buffer).unwrap(),
1766            (1, 0)
1767        );
1768        assert_eq!(
1769            buffer[0],
1770            VmoDirtyRange { offset: page_size * 2, length: page_size * 6, options: 1 },
1771        );
1772
1773        // The extra pages are all zero. We shouldn't see any page_in requests.
1774        let mut read_buf = vec![0u8; page_size as usize * 6];
1775        file_a.vmo().read(&mut read_buf, page_size * 2).expect("read a");
1776        assert_eq!(file_a.pager_requests(true), vec![]);
1777        file_b.vmo().read(&mut read_buf, page_size * 2).expect("read b");
1778        assert_eq!(file_b.pager_requests(true), vec![]);
1779
1780        // Grow again and check that pager gets notified.
1781        let vmo_size = page_size * 8;
1782        file_a.vmo().set_size(vmo_size).unwrap();
1783        file_b.vmo().set_stream_size(vmo_size).unwrap();
1784        assert_eq!(
1785            pager.query_dirty_ranges(file_a.vmo(), 0..vmo_size, &mut buffer).unwrap(),
1786            (1, 0)
1787        );
1788        assert_eq!(
1789            buffer[0],
1790            VmoDirtyRange { offset: page_size * 2, length: page_size * 6, options: 1 },
1791        );
1792        assert_eq!(
1793            pager.query_dirty_ranges(file_b.vmo(), 0..vmo_size, &mut buffer).unwrap(),
1794            (1, 0)
1795        );
1796        assert_eq!(
1797            buffer[0],
1798            VmoDirtyRange { offset: page_size * 2, length: page_size * 6, options: 1 },
1799        );
1800        // No pager requests. All new pages are assumed zero.
1801        assert_eq!(file_a.pager_requests(true), vec![],);
1802        assert_eq!(file_b.pager_requests(true), vec![],);
1803
1804        // Modifying a page in this new region should trigger a dirty message to the pager.
1805        file_b.vmo().write(&[1; 10], page_size * 2).unwrap();
1806        assert_eq!(
1807            file_b.pager_requests(true),
1808            vec![PagerRequest::Dirty(page_size * 2..page_size * 3)],
1809        );
1810
1811        // Shrink again to 4 pages and then append a page via zx_stream_write (WRITE)
1812        let vmo_size = page_size * 4;
1813        file_b.vmo().set_stream_size(vmo_size).unwrap();
1814        let stream =
1815            zx::Stream::create(zx::StreamOptions::MODE_WRITE, file_b.vmo(), page_size * 4).unwrap();
1816        stream.write(zx::StreamWriteOptions::empty(), &vec![10; page_size as usize]).unwrap();
1817        assert_eq!(
1818            file_b.pager_requests(true),
1819            vec![PagerRequest::Dirty(page_size * 4..page_size * 5)],
1820        );
1821
1822        // Append a page via zx_stream_write (APPEND)
1823        let stream = zx::Stream::create(
1824            zx::StreamOptions::MODE_WRITE | zx::StreamOptions::MODE_APPEND,
1825            file_b.vmo(),
1826            page_size * 5,
1827        )
1828        .unwrap();
1829        stream.write(zx::StreamWriteOptions::empty(), &[10; 1024]).unwrap();
1830        assert_eq!(
1831            file_b.pager_requests(true),
1832            vec![PagerRequest::Dirty(page_size * 5..page_size * 6)],
1833        );
1834
1835        scope.wait().await;
1836    }
1837
1838    #[fuchsia::test(threads = 2)]
1839    async fn test_pathological_shrink_unbounded_vmo() {
1840        let scope = ExecutionScope::new();
1841        let pager = Arc::new(Pager::new(scope.clone()).unwrap());
1842        let page_size = page_size();
1843        let vmo_size: u64 = page_size * 25600; // 100MiB
1844        let file =
1845            MockFile::new_with_size_and_type(pager.clone(), vmo_size, zx::VmoOptions::UNBOUNDED);
1846        let mut buffer = vec![VmoDirtyRange::default(); 10];
1847
1848        assert_eq!(file.vmo().get_stream_size().unwrap(), vmo_size);
1849
1850        // Shrinking by a small step to check that last page truncation works as expected.
1851        for i in 0..vmo_size / 256 {
1852            let data = vec![5; 20];
1853            file.vmo.write(&data, i * 256).expect("write failed");
1854        }
1855
1856        for i in (0..25600u64 / 1024).rev() {
1857            file.vmo().set_stream_size(i * 1024 + page_size / 2).unwrap();
1858        }
1859
1860        assert_eq!(pager.query_dirty_ranges(file.vmo(), 0..vmo_size, &mut buffer).unwrap(), (1, 0));
1861        assert_eq!(buffer[0..1], [VmoDirtyRange { offset: 0, length: page_size, options: 0 },]);
1862
1863        scope.wait().await;
1864    }
1865
1866    #[fuchsia::test(threads = 2)]
1867    async fn test_pathological_shrink_unbounded_vmo_with_gaps() {
1868        let scope = ExecutionScope::new();
1869        let pager = Arc::new(Pager::new(scope.clone()).unwrap());
1870        let page_size = page_size();
1871        let vmo_size: u64 = page_size * 25600; // 100MiB
1872        let file =
1873            MockFile::new_with_size_and_type(pager.clone(), vmo_size, zx::VmoOptions::UNBOUNDED);
1874        let mut buffer = vec![VmoDirtyRange::default(); 10];
1875
1876        assert_eq!(file.vmo().get_stream_size().unwrap(), vmo_size);
1877
1878        // Write every second page.
1879        for offset in (0u64..vmo_size).step_by((page_size * 2) as usize) {
1880            let data = vec![5; 20];
1881            file.vmo.write(&data, offset).expect("write failed");
1882        }
1883        // Every second page should be dirty.
1884        let (actual, remaining) =
1885            pager.query_dirty_ranges(file.vmo(), 0..vmo_size, &mut buffer).unwrap();
1886        assert_eq!(actual + remaining, 25600 / 2);
1887
1888        // Avoid page-aligned sizes to ensure we test the partial page code paths.
1889        let mut offset = vmo_size.saturating_sub(5 * page_size - 2);
1890        // Shrink by 5 pages, then 4 pages. This covers all possible arrangements of
1891        // start/end being on zero and non-zero pages.
1892        'outer: loop {
1893            for delta in [5 * page_size, 4 * page_size] {
1894                file.vmo().set_stream_size(offset).unwrap();
1895                assert_eq!(
1896                    pager.query_dirty_ranges(file.vmo(), offset..vmo_size, &mut buffer).unwrap(),
1897                    (1, 0)
1898                );
1899                // We do not expect to see dirty pages beyond stream size.
1900                assert_eq!(
1901                    buffer[0..1],
1902                    [VmoDirtyRange {
1903                        offset: round_down(offset, page_size),
1904                        length: page_size,
1905                        options: 0
1906                    },]
1907                );
1908                offset = offset.saturating_sub(delta);
1909                if offset == 0 {
1910                    break 'outer;
1911                }
1912            }
1913        }
1914
1915        scope.wait().await;
1916    }
1917
1918    #[fuchsia::test(threads = 2)]
1919    async fn test_grow_unbounded_vmo() {
1920        let scope = ExecutionScope::new();
1921        let pager = Arc::new(Pager::new(scope.clone()).unwrap());
1922        let file = MockFile::new_with_size_and_type(pager.clone(), 128, zx::VmoOptions::UNBOUNDED);
1923
1924        let data = vec![1; 128];
1925        // Overwrite the 128 after the content size;
1926        file.vmo().write(&data, 128).expect("write failed");
1927        // Grow the VMO to include the newly written bytes.
1928        file.vmo().set_stream_size(256).unwrap();
1929        assert_eq!(file.vmo().get_stream_size().expect("get_stream_size"), 256);
1930
1931        let mut data = vec![0xff; 256];
1932        file.vmo().read(&mut data, 0).expect("read");
1933        let expected = vec![0; 256];
1934        assert_eq!(data, expected);
1935
1936        file.vmo().set_stream_size(page_size() * 3).unwrap();
1937        let mut buffer = vec![VmoDirtyRange::default(); 10];
1938        assert_eq!(
1939            pager.query_dirty_ranges(file.vmo(), 0..page_size() * 3, &mut buffer).unwrap(),
1940            (2, 0)
1941        );
1942        // We expect to see only zero pages beyond content size.
1943        assert_eq!(
1944            buffer[0..2],
1945            [
1946                VmoDirtyRange { offset: 0, length: page_size(), options: 0 },
1947                VmoDirtyRange { offset: page_size(), length: page_size() * 2, options: 1 },
1948            ]
1949        );
1950
1951        scope.wait().await;
1952    }
1953}