Skip to main content

disk_delay/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! A disk device wrapper that provides configurable storage delay on Read/Write I/O operations to a disk.
5
6#![forbid(unsafe_code)]
7
8/// Provides a disk with a delay on every I/O operation.
9pub mod resolver;
10
11use disk_backend::Disk;
12use disk_backend::DiskError;
13use disk_backend::DiskIo;
14use disk_backend::UnmapBehavior;
15use inspect::Inspect;
16use mesh::Cell;
17use pal_async::timer::PolledTimer;
18use scsi_buffers::RequestBuffers;
19use std::time::Duration;
20use vmcore::vm_task::VmTaskDriver;
21use vmcore::vm_task::VmTaskDriverSource;
22
23/// A disk with delay on every I/O operation.
24#[derive(Inspect)]
25pub struct DelayDisk {
26    #[inspect(with = "|x| x.get()")]
27    delay: Cell<Duration>,
28    inner: Disk,
29    driver: VmTaskDriver,
30}
31
32impl DelayDisk {
33    /// Creates a new disk with a specified delay on I/O operations.
34    pub fn new(delay: Cell<Duration>, inner: Disk, driver_source: &VmTaskDriverSource) -> Self {
35        Self {
36            delay,
37            inner,
38            driver: driver_source.current(),
39        }
40    }
41}
42
43impl DiskIo for DelayDisk {
44    fn disk_type(&self) -> &str {
45        "delay"
46    }
47
48    /// Passthrough
49    fn sector_count(&self) -> u64 {
50        self.inner.sector_count()
51    }
52
53    /// Passthrough
54    fn sector_size(&self) -> u32 {
55        self.inner.sector_size()
56    }
57
58    /// Passthrough
59    fn disk_id(&self) -> Option<[u8; 16]> {
60        self.inner.disk_id()
61    }
62
63    /// Passthrough
64    fn physical_sector_size(&self) -> u32 {
65        self.inner.physical_sector_size()
66    }
67
68    /// Passthrough
69    fn is_fua_respected(&self) -> bool {
70        self.inner.is_fua_respected()
71    }
72
73    /// Passthrough
74    fn is_read_only(&self) -> bool {
75        self.inner.is_read_only()
76    }
77
78    /// Passthrough
79    fn pr(&self) -> Option<&dyn disk_backend::pr::PersistentReservation> {
80        self.inner.pr()
81    }
82
83    /// Delay and then Passthrough
84    async fn read_vectored(
85        &self,
86        buffers: &RequestBuffers<'_>,
87        sector: u64,
88    ) -> Result<(), DiskError> {
89        PolledTimer::new(&self.driver).sleep(self.delay.get()).await;
90        self.inner.read_vectored(buffers, sector).await
91    }
92
93    /// Delay and then Passthrough
94    async fn write_vectored(
95        &self,
96        buffers: &RequestBuffers<'_>,
97        sector: u64,
98        fua: bool,
99    ) -> Result<(), DiskError> {
100        PolledTimer::new(&self.driver).sleep(self.delay.get()).await;
101        self.inner.write_vectored(buffers, sector, fua).await
102    }
103
104    /// Passthrough
105    async fn sync_cache(&self) -> Result<(), DiskError> {
106        self.inner.sync_cache().await
107    }
108
109    /// Passthrough
110    async fn wait_resize(&self, sector_count: u64) -> u64 {
111        self.inner.wait_resize(sector_count).await
112    }
113
114    /// Passthrough
115    fn unmap(
116        &self,
117        sector: u64,
118        count: u64,
119        block_level_only: bool,
120    ) -> impl Future<Output = Result<(), DiskError>> + Send {
121        self.inner.unmap(sector, count, block_level_only)
122    }
123
124    /// Passthrough
125    fn unmap_behavior(&self) -> UnmapBehavior {
126        self.inner.unmap_behavior()
127    }
128
129    /// Passthrough
130    fn optimal_unmap_sectors(&self) -> u32 {
131        self.inner.optimal_unmap_sectors()
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use crate::DelayDisk;
138    use disk_backend::Disk;
139    use pal_async::DefaultDriver;
140    use pal_async::async_test;
141    use std::time::Duration;
142    use vmcore::vm_task::SingleDriverBackend;
143    use vmcore::vm_task::VmTaskDriverSource;
144
145    #[async_test]
146    async fn sector_range_conformance(driver: DefaultDriver) {
147        let source = VmTaskDriverSource::new(SingleDriverBackend::new(driver));
148        // A zero delay keeps the test fast. The delay is never updated, so the
149        // updater can be dropped immediately; the cell keeps its initial value.
150        let cell = mesh::CellUpdater::new(Duration::ZERO).cell();
151        let inner = disklayer_ram::ram_disk(1024 * 1024, false).unwrap();
152        let disk = Disk::new(DelayDisk::new(cell, inner, &source)).unwrap();
153        storage_tests::sector_range::test_disk_sector_range_conformance(&disk).await;
154    }
155}