fuchsia_storage_benchmarks/filesystems/
mod.rs1use async_trait::async_trait;
6use blob_writer::BlobWriter;
7use delivery_blob::{CompressionMode, DeliveryBlobType, Type1Blob, Type3Blob};
8use fidl::endpoints::ClientEnd;
9use fidl_fuchsia_fs_startup::{CreateOptions, MountOptions};
10use fidl_fuchsia_fxfs::{BlobCreatorProxy, BlobReaderProxy, CryptMarker};
11use fidl_fuchsia_io as fio;
12use fs_management::FSConfig;
13use fs_management::filesystem::{
14 Filesystem as FsManagementFilesystem, ServingMultiVolumeFilesystem,
15 ServingSingleVolumeFilesystem, ServingVolume,
16};
17use fuchsia_merkle::Hash;
18use std::path::Path;
19use std::sync::Arc;
20use storage_benchmarks::block_device::BlockDevice;
21use storage_benchmarks::{CacheClearableFilesystem, Filesystem};
22
23mod blobfs;
24mod f2fs;
25mod fxblob;
26pub mod fxfs;
27mod memfs;
28mod minfs;
29mod pkgdir;
30#[cfg(test)]
31mod testing;
32
33pub use blobfs::Blobfs;
34pub use f2fs::F2fs;
35pub use fxblob::{Fxblob, FxblobInstance};
36pub use fxfs::Fxfs;
37pub use memfs::Memfs;
38pub use minfs::Minfs;
39pub use pkgdir::{PkgDirInstance, PkgDirTest};
40
41const MOUNT_PATH: &str = "/benchmark";
42
43pub struct DeliveryBlob {
45 pub data: Vec<u8>,
46 pub name: Hash,
47}
48
49impl DeliveryBlob {
50 pub fn new(data: Vec<u8>, mode: CompressionMode) -> Self {
51 Self::new_with_type(DeliveryBlobType::Type1, data, mode)
52 }
53
54 pub fn new_with_type(
55 delivery_type: DeliveryBlobType,
56 data: Vec<u8>,
57 mode: CompressionMode,
58 ) -> Self {
59 let name = fuchsia_merkle::root_from_slice(&data);
60 let data = match delivery_type {
61 DeliveryBlobType::Type1 => Type1Blob::generate(&data, mode),
62 DeliveryBlobType::Type3 => Type3Blob::generate(&data, mode),
63 _ => panic!("Unsupported delivery blob type"),
64 };
65 Self { data, name }
66 }
67}
68
69#[async_trait]
71pub trait BlobFilesystem: CacheClearableFilesystem {
72 async fn write_blob(&self, blob: &DeliveryBlob) {
74 let writer_client_end = self
75 .blob_creator()
76 .create(&blob.name.into(), false)
77 .await
78 .expect("transport error on BlobCreator.Create")
79 .expect("failed to create blob");
80 let writer = writer_client_end.into_proxy();
81 let mut blob_writer = BlobWriter::create(writer, blob.data.len() as u64)
82 .await
83 .expect("failed to create BlobWriter");
84 blob_writer.write(&blob.data).await.unwrap();
85 }
86
87 async fn get_vmo(&self, name: &Hash) -> zx::Vmo {
89 self.blob_reader()
90 .get_vmo(&*name)
91 .await
92 .expect("transport error on BlobReader.GetVmo")
93 .expect("failed to get vmo")
94 }
95
96 async fn remove_blob(&self, name: &Hash) {
97 let root = fuchsia_fs::directory::open_directory(
98 self.exposed_dir(),
99 "root",
100 fio::PERM_READABLE | fio::PERM_WRITABLE,
101 )
102 .await
103 .expect("failed to open blob directory");
104 root.unlink(&name.to_string(), &fio::UnlinkOptions::default())
105 .await
106 .expect("transport error on Directory.Unlink")
107 .expect("failed to unlink blob");
108 root.sync()
109 .await
110 .expect("transport error on Directory.Sync")
111 .expect("failed to sync blob directory");
112 }
113
114 fn blob_creator(&self) -> &BlobCreatorProxy;
116
117 fn blob_reader(&self) -> &BlobReaderProxy;
119
120 fn exposed_dir(&self) -> &fio::DirectoryProxy;
122}
123
124enum FsType {
125 SingleVolume(ServingSingleVolumeFilesystem),
126 MultiVolume(ServingMultiVolumeFilesystem, ServingVolume),
127}
128
129pub type CryptClientFn = Arc<dyn Fn() -> ClientEnd<CryptMarker> + Send + Sync>;
130
131pub struct FsManagementFilesystemInstance {
132 config_creator: Box<dyn Fn() -> Box<dyn FSConfig> + Send + Sync>,
133 crypt_client_fn: Option<CryptClientFn>,
134 serving_filesystem: Option<FsType>,
135 as_blob: bool,
136 block_device: Box<dyn BlockDevice>,
138}
139
140impl FsManagementFilesystemInstance {
141 pub async fn new<FSC: FSConfig>(
142 config_creator: impl (Fn() -> FSC) + Send + Sync + 'static,
143 block_device: Box<dyn BlockDevice>,
144 crypt_client_fn: Option<CryptClientFn>,
145 as_blob: bool,
146 ) -> Self {
147 let config_creator = Box::new(move || Box::new(config_creator()) as Box<dyn FSConfig>);
148 let mut fs =
149 FsManagementFilesystem::from_boxed_config(block_device.connector(), config_creator());
150 fs.format().await.expect("Failed to format the filesystem");
151 let serving_filesystem = if fs.config().is_multi_volume() {
152 let serving_filesystem =
153 fs.serve_multi_volume().await.expect("Failed to start the filesystem");
154 let mut vol = serving_filesystem
155 .create_volume(
156 "default",
157 CreateOptions::default(),
158 MountOptions {
159 crypt: crypt_client_fn.as_ref().map(|f| f()),
160 as_blob: Some(as_blob),
161 ..MountOptions::default()
162 },
163 )
164 .await
165 .expect("Failed to create volume");
166 vol.bind_to_path(MOUNT_PATH).expect("Failed to bind the volume");
167 FsType::MultiVolume(serving_filesystem, vol)
168 } else {
169 let mut serving_filesystem = fs.serve().await.expect("Failed to start the filesystem");
170 serving_filesystem.bind_to_path(MOUNT_PATH).expect("Failed to bind the filesystem");
171 FsType::SingleVolume(serving_filesystem)
172 };
173 Self {
174 config_creator,
175 crypt_client_fn,
176 serving_filesystem: Some(serving_filesystem),
177 as_blob,
178 block_device,
179 }
180 }
181
182 fn exposed_dir(&self) -> &fio::DirectoryProxy {
183 let fs = self.serving_filesystem.as_ref().unwrap();
184 match fs {
185 FsType::SingleVolume(serving_filesystem) => serving_filesystem.exposed_dir(),
186 FsType::MultiVolume(_, serving_volume) => serving_volume.exposed_dir(),
187 }
188 }
189
190 pub(crate) fn exposed_services_dir(&self) -> &fio::DirectoryProxy {
193 let fs = self.serving_filesystem.as_ref().unwrap();
194 match fs {
195 FsType::SingleVolume(serving_filesystem) => serving_filesystem.exposed_dir(),
196 FsType::MultiVolume(serving_filesystem, _) => serving_filesystem.exposed_dir(),
197 }
198 }
199
200 fn fs(&self) -> FsManagementFilesystem {
201 FsManagementFilesystem::from_boxed_config(
202 self.block_device.connector(),
203 (self.config_creator)(),
204 )
205 }
206}
207
208#[async_trait]
209impl Filesystem for FsManagementFilesystemInstance {
210 async fn shutdown(mut self) {
211 if let Some(fs) = self.serving_filesystem.take() {
212 match fs {
213 FsType::SingleVolume(fs) => fs.shutdown().await.expect("Failed to stop filesystem"),
214 FsType::MultiVolume(fs, vol) => {
215 vol.shutdown().await.expect("Failed to stop volume");
216 fs.shutdown().await.expect("Failed to stop filesystem")
217 }
218 }
219 }
220 }
221
222 fn benchmark_dir(&self) -> &Path {
223 Path::new(MOUNT_PATH)
224 }
225}
226
227#[async_trait]
228impl CacheClearableFilesystem for FsManagementFilesystemInstance {
229 async fn clear_cache(&mut self) {
230 let serving_filesystem = self.serving_filesystem.take().unwrap();
232 let serving_filesystem = match serving_filesystem {
233 FsType::SingleVolume(serving_filesystem) => {
234 serving_filesystem.shutdown().await.expect("Failed to stop the filesystem");
235 let mut serving_filesystem =
236 self.fs().serve().await.expect("Failed to start the filesystem");
237 serving_filesystem.bind_to_path(MOUNT_PATH).expect("Failed to bind the filesystem");
238 FsType::SingleVolume(serving_filesystem)
239 }
240 FsType::MultiVolume(serving_filesystem, volume) => {
241 volume.shutdown().await.expect("Failed to stop the volume");
242 serving_filesystem.shutdown().await.expect("Failed to stop the filesystem");
243 let serving_filesystem =
244 self.fs().serve_multi_volume().await.expect("Failed to start the filesystem");
245 let mut vol = serving_filesystem
246 .open_volume(
247 "default",
248 MountOptions {
249 crypt: self.crypt_client_fn.as_ref().map(|f| f()),
250 as_blob: Some(self.as_blob),
251 ..MountOptions::default()
252 },
253 )
254 .await
255 .expect("Failed to create volume");
256 vol.bind_to_path(MOUNT_PATH).expect("Failed to bind the volume");
257 FsType::MultiVolume(serving_filesystem, vol)
258 }
259 };
260 self.serving_filesystem = Some(serving_filesystem);
261 }
262}