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.simple(),
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}