1use async_trait::async_trait;
6use fidl::HandleBased as _;
7use fidl::endpoints::{
8 DiscoverableProtocolMarker as _, Proxy, create_proxy, create_request_stream,
9};
10use fidl_fuchsia_device::ControllerMarker;
11use fidl_fuchsia_fs_startup::{CreateOptions, MountOptions};
12use fidl_fuchsia_io as fio;
13use fidl_fuchsia_storage_block::{
14 ALLOCATE_PARTITION_FLAG_INACTIVE, BlockMarker, BlockSynchronousProxy, VolumeManagerMarker,
15 VolumeManagerProxy,
16};
17use fs_management::filesystem::{
18 BlockConnector, DirBasedBlockConnector, ServingMultiVolumeFilesystem,
19};
20use fs_management::format::DiskFormat;
21use fs_management::format::constants::{BENCHMARK_FVM_TYPE_GUID, BENCHMARK_FVM_VOLUME_NAME};
22use fs_management::{BLOBFS_TYPE_GUID, Fvm};
23use fuchsia_component::client::{
24 Service, connect_to_named_protocol_at_dir_root, connect_to_protocol,
25 connect_to_protocol_at_dir_root, connect_to_protocol_at_path,
26};
27
28use std::path::PathBuf;
29use std::sync::Arc;
30use storage_benchmarks::block_device::BlockDevice;
31use storage_benchmarks::{BlockDeviceConfig, BlockDeviceFactory};
32use storage_isolated_driver_manager::{
33 BlockDeviceMatcher, Guid, create_random_guid, find_block_device, find_block_device_devfs, fvm,
34 into_guid, wait_for_block_device_devfs, zxcrypt,
35};
36use {
37 fidl_fuchsia_device as fdevice, fidl_fuchsia_storage_partitions as fpartitions,
38 fuchsia_async as fasync,
39};
40
41const BLOBFS_VOLUME_NAME: &str = "blobfs";
42
43const BENCHMARK_FVM_SIZE_BYTES: u64 = 160 * 1024 * 1024;
44const BENCHMARK_FVM_SLICE_SIZE_BYTES: usize = 8 * 1024 * 1024;
49
50const BENCHMARK_TYPE_GUID: &Guid = &[
54 0x67, 0x45, 0x23, 0x01, 0xab, 0x89, 0xef, 0xcd, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
55];
56const BENCHMARK_VOLUME_NAME: &str = "benchmark";
57
58pub async fn create_fvm_volume(
61 fvm: &fidl_fuchsia_fs_startup::VolumesProxy,
62 instance_guid: [u8; 16],
63 config: &BlockDeviceConfig,
64) -> (fio::DirectoryProxy, Option<fasync::Task<()>>) {
65 let (crypt, crypt_task) = if config.use_zxcrypt {
66 let (crypt, stream) = create_request_stream::<fidl_fuchsia_fxfs::CryptMarker>();
67 let task = fasync::Task::spawn(async {
68 if let Err(err) =
69 zxcrypt_crypt::run_crypt_service(crypt_policy::Policy::Null, stream).await
70 {
71 log::error!(err:?; "Crypt service failure");
72 }
73 });
74 (Some(crypt), Some(task))
75 } else {
76 (None, None)
77 };
78 let (volume_dir, server_end) = create_proxy::<fio::DirectoryMarker>();
79 fvm.create(
80 BENCHMARK_VOLUME_NAME,
81 server_end,
82 CreateOptions {
83 initial_size: config.volume_size,
84 type_guid: Some(BENCHMARK_TYPE_GUID.clone()),
85 guid: Some(instance_guid),
86 ..Default::default()
87 },
88 MountOptions { crypt, ..Default::default() },
89 )
90 .await
91 .expect("FIDL error")
92 .map_err(zx::Status::from_raw)
93 .expect("Failed to create volume");
94
95 (volume_dir, crypt_task)
96}
97
98pub enum SystemFvm {
99 Devfs(VolumeManagerProxy),
101 Component(
103 Box<dyn Send + Sync + Fn() -> fidl_fuchsia_fs_startup::VolumesProxy>,
104 Box<dyn Send + Sync + Fn() -> fio::DirectoryProxy>,
105 ),
106}
107
108pub enum SystemGpt {
109 Devfs(fdevice::ControllerProxy),
111 Component(Arc<fpartitions::PartitionServiceProxy>),
113}
114
115pub enum BenchmarkVolumeFactory {
119 SystemFvm(SystemFvm),
120 SystemGpt(SystemGpt),
121}
122
123struct RawBlockDeviceInDevfs(fdevice::ControllerProxy);
124
125impl BlockDevice for RawBlockDeviceInDevfs {
126 fn connector(&self) -> Box<dyn BlockConnector> {
127 Box::new(self.0.clone())
128 }
129}
130
131struct RawBlockDeviceInGpt(Arc<fpartitions::PartitionServiceProxy>);
132
133impl BlockDevice for RawBlockDeviceInGpt {
134 fn connector(&self) -> Box<dyn BlockConnector> {
135 Box::new(self.0.clone())
136 }
137}
138
139#[async_trait]
140impl BlockDeviceFactory for BenchmarkVolumeFactory {
141 async fn create_block_device(&self, config: &BlockDeviceConfig) -> Box<dyn BlockDevice> {
142 let instance_guid = create_random_guid();
143 match self {
144 Self::SystemFvm(SystemFvm::Devfs(volume_manager)) => Box::new(
145 Self::create_fvm_volume_in_devfs(volume_manager, instance_guid, config).await,
146 ),
147 Self::SystemFvm(SystemFvm::Component(volumes_connector, _)) => {
148 let volumes = volumes_connector();
149 Box::new(Self::create_fvm_volume(volumes, instance_guid, config).await)
150 }
151 Self::SystemGpt(SystemGpt::Devfs(controller)) => {
152 if config.requires_fvm {
153 Box::new(
154 Self::create_fvm_instance_and_volume_in_devfs(
155 controller,
156 instance_guid,
157 config,
158 )
159 .await,
160 )
161 } else {
162 Box::new(RawBlockDeviceInDevfs(controller.clone()))
163 }
164 }
165 Self::SystemGpt(SystemGpt::Component(partition_service)) => {
166 if config.requires_fvm {
167 Box::new(
168 Self::create_fvm_instance_and_volume(
169 partition_service.clone(),
170 instance_guid,
171 config,
172 )
173 .await,
174 )
175 } else {
176 Box::new(RawBlockDeviceInGpt(partition_service.clone()))
177 }
178 }
179 }
180 }
181}
182
183impl BenchmarkVolumeFactory {
184 pub async fn from_config(storage_host: bool, fxfs_blob: bool) -> BenchmarkVolumeFactory {
187 if storage_host {
188 let partitions = Service::open(fpartitions::PartitionServiceMarker).unwrap();
189 let manager = connect_to_protocol::<fpartitions::PartitionsManagerMarker>().unwrap();
190 if fxfs_blob {
191 let instance =
192 BenchmarkVolumeFactory::connect_to_test_partition(partitions, manager).await;
193 assert!(
194 instance.is_some(),
195 "Failed to open or create testing FVM in GPT. \
196 Perhaps the system doesn't have a GPT-formatted block device?"
197 );
198 instance.unwrap()
199 } else {
200 let volumes_connector = Box::new(move || {
201 connect_to_protocol::<fidl_fuchsia_fs_startup::VolumesMarker>().unwrap()
202 });
203 let volumes_dir_connector = {
204 Box::new(move || {
205 fuchsia_fs::directory::open_in_namespace("volumes", fio::PERM_READABLE)
206 .unwrap()
207 })
208 };
209 BenchmarkVolumeFactory::connect_to_system_fvm(
210 volumes_connector,
211 volumes_dir_connector,
212 )
213 .unwrap()
214 }
215 } else {
216 if fxfs_blob {
217 let instance = BenchmarkVolumeFactory::connect_to_test_partition_devfs().await;
218 assert!(
219 instance.is_some(),
220 "Failed to open or create testing volume in GPT. \
221 Perhaps the system doesn't have a GPT-formatted block device?"
222 );
223 instance.unwrap()
224 } else {
225 let instance = BenchmarkVolumeFactory::connect_to_system_fvm_devfs().await;
226 assert!(
227 instance.is_some(),
228 "Failed to open or create volume in FVM. \
229 Perhaps the system doesn't have an FVM-formatted block device?"
230 );
231 instance.unwrap()
232 }
233 }
234 }
235
236 pub fn connect_to_system_fvm(
238 volumes_connector: Box<dyn Send + Sync + Fn() -> fidl_fuchsia_fs_startup::VolumesProxy>,
239 volumes_dir_connector: Box<dyn Send + Sync + Fn() -> fio::DirectoryProxy>,
240 ) -> Option<BenchmarkVolumeFactory> {
241 Some(BenchmarkVolumeFactory::SystemFvm(SystemFvm::Component(
242 volumes_connector,
243 volumes_dir_connector,
244 )))
245 }
246
247 pub async fn connect_to_system_fvm_devfs() -> Option<BenchmarkVolumeFactory> {
249 let blobfs_dev_path = find_block_device_devfs(&[
252 BlockDeviceMatcher::Name(BLOBFS_VOLUME_NAME),
253 BlockDeviceMatcher::TypeGuid(&BLOBFS_TYPE_GUID),
254 ])
255 .await
256 .ok()?;
257
258 let controller_path = format!("{}/device_controller", blobfs_dev_path.to_str().unwrap());
259 let blobfs_controller = connect_to_protocol_at_path::<ControllerMarker>(&controller_path)
260 .unwrap_or_else(|_| panic!("Failed to connect to Controller at {:?}", controller_path));
261 let path = blobfs_controller
262 .get_topological_path()
263 .await
264 .expect("FIDL error")
265 .expect("get_topological_path failed");
266
267 let mut path = PathBuf::from(path);
268 if !path.pop() || !path.pop() {
269 panic!("Unexpected topological path for Blobfs {}", path.display());
270 }
271
272 match path.file_name() {
273 Some(p) => assert!(p == "fvm", "Unexpected FVM path: {}", path.display()),
274 None => panic!("Unexpected FVM path: {}", path.display()),
275 }
276 Some(BenchmarkVolumeFactory::SystemFvm(SystemFvm::Devfs(
277 connect_to_protocol_at_path::<VolumeManagerMarker>(path.to_str().unwrap())
278 .unwrap_or_else(|_| panic!("Failed to connect to VolumeManager at {:?}", path)),
279 )))
280 }
281
282 pub async fn connect_to_test_partition(
286 service: Service<fpartitions::PartitionServiceMarker>,
287 manager: fpartitions::PartitionsManagerProxy,
288 ) -> Option<BenchmarkVolumeFactory> {
289 let service_instances =
290 service.clone().enumerate().await.expect("Failed to enumerate partitions");
291 let connector = if let Some(connector) = find_block_device(
292 &[
293 BlockDeviceMatcher::Name(BENCHMARK_FVM_VOLUME_NAME),
294 BlockDeviceMatcher::TypeGuid(&BENCHMARK_FVM_TYPE_GUID),
295 ],
296 service_instances.into_iter(),
297 )
298 .await
299 .expect("Failed to find block device")
300 {
301 connector
303 } else {
304 let info =
306 manager.get_block_info().await.expect("FIDL error").expect("get_block_info failed");
307 let transaction = manager
308 .create_transaction()
309 .await
310 .expect("FIDL error")
311 .map_err(zx::Status::from_raw)
312 .expect("create_transaction failed");
313 let request = fpartitions::PartitionsManagerAddPartitionRequest {
314 transaction: Some(transaction.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
315 name: Some(BENCHMARK_FVM_VOLUME_NAME.to_string()),
316 type_guid: Some(fidl_fuchsia_storage_block::Guid {
317 value: BENCHMARK_FVM_TYPE_GUID.clone(),
318 }),
319 num_blocks: Some(BENCHMARK_FVM_SIZE_BYTES / info.1 as u64),
320 ..Default::default()
321 };
322 manager
323 .add_partition(request)
324 .await
325 .expect("FIDL error")
326 .map_err(zx::Status::from_raw)
327 .expect("add_partition failed");
328 manager
329 .commit_transaction(transaction)
330 .await
331 .expect("FIDL error")
332 .map_err(zx::Status::from_raw)
333 .expect("add_partition failed");
334 let service_instances =
335 service.enumerate().await.expect("Failed to enumerate partitions");
336 log::info!("len {}", service_instances.len());
337 find_block_device(
338 &[
339 BlockDeviceMatcher::Name(BENCHMARK_FVM_VOLUME_NAME),
340 BlockDeviceMatcher::TypeGuid(&BENCHMARK_FVM_TYPE_GUID),
341 ],
342 service_instances.into_iter(),
343 )
344 .await
345 .expect("Failed to find block device")?
346 };
347
348 Some(BenchmarkVolumeFactory::SystemGpt(SystemGpt::Component(Arc::new(connector))))
349 }
350
351 pub async fn connect_to_test_partition_devfs() -> Option<BenchmarkVolumeFactory> {
356 let mut fvm_path = if let Ok(path) = find_block_device_devfs(&[
357 BlockDeviceMatcher::Name(BENCHMARK_FVM_VOLUME_NAME),
358 BlockDeviceMatcher::TypeGuid(&BENCHMARK_FVM_TYPE_GUID),
359 ])
360 .await
361 {
362 path
364 } else {
365 let mut gpt_block_path =
367 find_block_device_devfs(&[BlockDeviceMatcher::ContentsMatch(DiskFormat::Gpt)])
368 .await
369 .ok()?;
370 gpt_block_path.push("device_controller");
371 let gpt_block_controller =
372 connect_to_protocol_at_path::<ControllerMarker>(gpt_block_path.to_str().unwrap())
373 .expect("Failed to connect to GPT controller");
374
375 let mut gpt_path = gpt_block_controller
376 .get_topological_path()
377 .await
378 .expect("FIDL error")
379 .expect("get_topological_path failed");
380 gpt_path.push_str("/gpt/device_controller");
381
382 let gpt_controller = connect_to_protocol_at_path::<ControllerMarker>(&gpt_path)
383 .expect("Failed to connect to GPT controller");
384
385 let (volume_manager, server) = create_proxy::<VolumeManagerMarker>();
386 gpt_controller
387 .connect_to_device_fidl(server.into_channel())
388 .expect("Failed to connect to device FIDL");
389 let slice_size = {
390 let (status, info) = volume_manager.get_info().await.expect("FIDL error");
391 zx::ok(status).expect("Failed to get VolumeManager info");
392 info.unwrap().slice_size
393 };
394 let slice_count = BENCHMARK_FVM_SIZE_BYTES / slice_size;
395 let instance_guid = into_guid(create_random_guid());
396 let status = volume_manager
397 .allocate_partition(
398 slice_count,
399 &into_guid(BENCHMARK_FVM_TYPE_GUID.clone()),
400 &instance_guid,
401 BENCHMARK_FVM_VOLUME_NAME,
402 0,
403 )
404 .await
405 .expect("FIDL error");
406 zx::ok(status).expect("Failed to allocate benchmark FVM");
407
408 wait_for_block_device_devfs(&[
409 BlockDeviceMatcher::Name(BENCHMARK_FVM_VOLUME_NAME),
410 BlockDeviceMatcher::TypeGuid(&BENCHMARK_FVM_TYPE_GUID),
411 ])
412 .await
413 .expect("Failed to wait for newly created benchmark FVM to appear")
414 };
415 fvm_path.push("device_controller");
416 let fvm_controller =
417 connect_to_protocol_at_path::<ControllerMarker>(fvm_path.to_str().unwrap())
418 .expect("failed to connect to controller");
419
420 Some(BenchmarkVolumeFactory::SystemGpt(SystemGpt::Devfs(fvm_controller)))
421 }
422
423 #[cfg(test)]
424 pub async fn contains_fvm_volume(&self, name: &str) -> bool {
425 match self {
426 Self::SystemFvm(SystemFvm::Devfs(_)) => {
427 find_block_device_devfs(&[BlockDeviceMatcher::Name(name)]).await.is_ok()
428 }
429 Self::SystemFvm(SystemFvm::Component(_, volumes_dir_connector)) => {
430 let dir = volumes_dir_connector();
431 fuchsia_fs::directory::dir_contains(&dir, name).await.unwrap()
432 }
433 _ => false,
436 }
437 }
438
439 async fn create_fvm_volume_in_devfs(
440 volume_manager: &VolumeManagerProxy,
441 instance_guid: [u8; 16],
442 config: &BlockDeviceConfig,
443 ) -> FvmVolume {
444 fvm::create_fvm_volume(
445 volume_manager,
446 BENCHMARK_VOLUME_NAME,
447 BENCHMARK_TYPE_GUID,
448 &instance_guid,
449 config.volume_size,
450 ALLOCATE_PARTITION_FLAG_INACTIVE,
451 )
452 .await
453 .expect("Failed to create FVM volume");
454
455 let device_path = wait_for_block_device_devfs(&[
456 BlockDeviceMatcher::TypeGuid(BENCHMARK_TYPE_GUID),
457 BlockDeviceMatcher::InstanceGuid(&instance_guid),
458 BlockDeviceMatcher::Name(BENCHMARK_VOLUME_NAME),
459 ])
460 .await
461 .expect("Failed to find the FVM volume");
462
463 let controller = connect_to_protocol_at_path::<ControllerMarker>(&format!(
468 "{}/device_controller",
469 device_path.to_str().unwrap()
470 ))
471 .expect("failed to connect to controller");
472 let topo_path = controller
473 .get_topological_path()
474 .await
475 .expect("transport error on get_topological_path")
476 .expect("get_topological_path failed");
477 let volume_dir =
478 fuchsia_fs::directory::open_in_namespace(&topo_path, fuchsia_fs::Flags::empty())
479 .expect("failed to open device");
480 let block = connect_to_named_protocol_at_dir_root::<BlockMarker>(&volume_dir, ".").unwrap();
485 let volume = BlockSynchronousProxy::new(block.into_channel().unwrap().into());
486 let volume_dir = if config.use_zxcrypt {
487 zxcrypt::set_up_insecure_zxcrypt(&volume_dir).await.expect("Failed to set up zxcrypt")
488 } else {
489 volume_dir
490 };
491
492 FvmVolume {
493 destroy_fn: Some(Box::new(move || {
494 zx::ok(volume.destroy(zx::MonotonicInstant::INFINITE).unwrap())
495 })),
496 fvm_instance: None,
497 volume_dir: Some(volume_dir),
498 block_path: ".".to_string(),
499 crypt_task: None,
500 }
501 }
502
503 async fn create_fvm_volume(
504 volumes: fidl_fuchsia_fs_startup::VolumesProxy,
505 instance_guid: [u8; 16],
506 config: &BlockDeviceConfig,
507 ) -> FvmVolume {
508 let (volume_dir, crypt_task) = create_fvm_volume(&volumes, instance_guid, config).await;
509 let volumes = volumes.into_client_end().unwrap().into_sync_proxy();
510 FvmVolume {
511 destroy_fn: Some(Box::new(move || {
512 volumes
513 .remove(BENCHMARK_VOLUME_NAME, zx::MonotonicInstant::INFINITE)
514 .unwrap()
515 .map_err(zx::Status::from_raw)
516 })),
517 volume_dir: Some(volume_dir),
518 fvm_instance: None,
519 block_path: format!("svc/{}", BlockMarker::PROTOCOL_NAME),
520 crypt_task,
521 }
522 }
523
524 async fn create_fvm_instance_and_volume(
525 partition: Arc<fpartitions::PartitionServiceProxy>,
526 instance_guid: [u8; 16],
527 config: &BlockDeviceConfig,
528 ) -> FvmVolume {
529 let block_device =
530 partition.connect_block().expect("Failed to connect to block").into_proxy();
531 fvm::format_for_fvm(&block_device, BENCHMARK_FVM_SLICE_SIZE_BYTES)
532 .expect("Failed to format FVM");
533
534 let fs = fs_management::filesystem::Filesystem::from_boxed_config(
535 Box::new(partition),
536 Box::new(Fvm::default()),
537 );
538 let fvm_instance = fs.serve_multi_volume().await.expect("Failed to serve FVM");
539 let volumes = connect_to_protocol_at_dir_root::<fidl_fuchsia_fs_startup::VolumesMarker>(
540 fvm_instance.exposed_dir(),
541 )
542 .unwrap();
543
544 let (volume_dir, crypt_task) = create_fvm_volume(&volumes, instance_guid, config).await;
545 FvmVolume {
546 destroy_fn: None,
547 volume_dir: Some(volume_dir),
548 fvm_instance: Some(fvm_instance),
549 block_path: format!("svc/{}", BlockMarker::PROTOCOL_NAME),
550 crypt_task,
551 }
552 }
553
554 async fn create_fvm_instance_and_volume_in_devfs(
555 fvm_controller: &fdevice::ControllerProxy,
556 instance_guid: [u8; 16],
557 config: &BlockDeviceConfig,
558 ) -> FvmVolume {
559 fvm_controller
561 .unbind_children()
562 .await
563 .expect("FIDL error")
564 .expect("failed to unbind children");
565
566 let (block_device, server_end) = create_proxy::<BlockMarker>();
567 fvm_controller.connect_to_device_fidl(server_end.into_channel()).unwrap();
568 fvm::format_for_fvm(&block_device, BENCHMARK_FVM_SLICE_SIZE_BYTES)
569 .expect("Failed to format FVM");
570
571 let topo_path = fvm_controller
572 .get_topological_path()
573 .await
574 .expect("transport error on get_topological_path")
575 .expect("get_topological_path failed");
576 let dir = fuchsia_fs::directory::open_in_namespace(&topo_path, fio::PERM_READABLE).unwrap();
577 let volume_manager =
578 fvm::start_fvm_driver(&fvm_controller, &dir).await.expect("Failed to start FVM");
579
580 Self::create_fvm_volume_in_devfs(&volume_manager, instance_guid, config).await
581 }
582}
583
584pub struct FvmVolume {
586 destroy_fn: Option<Box<dyn Send + Sync + FnOnce() -> Result<(), zx::Status>>>,
587 fvm_instance: Option<ServingMultiVolumeFilesystem>,
588 volume_dir: Option<fio::DirectoryProxy>,
589 crypt_task: Option<fasync::Task<()>>,
590 block_path: String,
592}
593
594impl BlockDevice for FvmVolume {
595 fn connector(&self) -> Box<dyn BlockConnector> {
596 let volume_dir = fuchsia_fs::directory::clone(self.volume_dir.as_ref().unwrap()).unwrap();
597 Box::new(DirBasedBlockConnector::new(volume_dir, self.block_path.clone()))
598 }
599}
600
601impl Drop for FvmVolume {
602 fn drop(&mut self) {
603 self.volume_dir = None;
604 self.fvm_instance = None;
605 self.crypt_task = None;
606 if let Some(destroy_fn) = self.destroy_fn.take() {
607 destroy_fn().expect("Failed to destroy FVM volume");
608 }
609 }
610}
611
612#[cfg(test)]
613mod tests {
614 use super::*;
615 use crate::testing::{RAMDISK_FVM_SLICE_SIZE, RamdiskFactory};
616 use block_client::RemoteBlockClient;
617 use fidl_fuchsia_fs_startup::VolumesMarker;
618 use fs_management::Gpt;
619 use ramdevice_client::{RamdiskClient, RamdiskClientBuilder};
620 use std::sync::Arc;
621 use vmo_backed_block_server::{VmoBackedServer, VmoBackedServerTestingExt as _};
622
623 const BLOCK_SIZE: u64 = 4 * 1024;
624 const BLOCK_COUNT: u64 = 1024;
625 const GPT_BLOCK_COUNT: u64 = 49152;
628
629 #[fuchsia::test]
630 async fn ramdisk_create_block_device_with_zxcrypt() {
631 let ramdisk_factory = RamdiskFactory::new(BLOCK_SIZE, BLOCK_COUNT);
632 let _ = ramdisk_factory
633 .create_block_device(&BlockDeviceConfig {
634 requires_fvm: true,
635 use_zxcrypt: true,
636 volume_size: None,
637 })
638 .await;
639 }
640
641 #[fuchsia::test]
642 async fn ramdisk_create_block_device_without_zxcrypt() {
643 let ramdisk_factory = RamdiskFactory::new(BLOCK_SIZE, BLOCK_COUNT);
644 let _ = ramdisk_factory
645 .create_block_device(&BlockDeviceConfig {
646 requires_fvm: true,
647 use_zxcrypt: false,
648 volume_size: None,
649 })
650 .await;
651 }
652
653 #[fuchsia::test]
654 async fn ramdisk_create_block_device_without_volume_size() {
655 let ramdisk_factory = RamdiskFactory::new(BLOCK_SIZE, BLOCK_COUNT);
656 let ramdisk = ramdisk_factory
657 .create_block_device(&BlockDeviceConfig {
658 requires_fvm: true,
659 use_zxcrypt: false,
660 volume_size: None,
661 })
662 .await;
663 let volume_info = ramdisk
664 .connector()
665 .connect_block()
666 .unwrap()
667 .into_proxy()
668 .get_volume_info()
669 .await
670 .unwrap();
671 zx::ok(volume_info.0).unwrap();
672 let volume_info = volume_info.2.unwrap();
673 assert_eq!(volume_info.partition_slice_count, 1);
674 }
675
676 #[fuchsia::test]
677 async fn ramdisk_create_block_device_with_volume_size() {
678 let ramdisk_factory = RamdiskFactory::new(BLOCK_SIZE, BLOCK_COUNT);
679 let ramdisk = ramdisk_factory
680 .create_block_device(&BlockDeviceConfig {
681 requires_fvm: false,
682 use_zxcrypt: false,
683 volume_size: Some(RAMDISK_FVM_SLICE_SIZE as u64 * 3),
684 })
685 .await;
686 let volume_info = ramdisk
687 .connector()
688 .connect_block()
689 .unwrap()
690 .into_proxy()
691 .get_volume_info()
692 .await
693 .unwrap();
694 zx::ok(volume_info.0).unwrap();
695 let volume_info = volume_info.2.unwrap();
696 assert_eq!(volume_info.partition_slice_count, 3);
697 }
698
699 async fn init_gpt(block_size: u32, block_count: u64) -> zx::Vmo {
700 let vmo = zx::Vmo::create(block_size as u64 * block_count).unwrap();
701 let server = Arc::new(VmoBackedServer::from_vmo(
702 block_size,
703 vmo.create_child(zx::VmoChildOptions::REFERENCE, 0, 0).unwrap(),
704 ));
705 let (client, server_end) =
706 fidl::endpoints::create_proxy::<fidl_fuchsia_storage_block::BlockMarker>();
707
708 let _task =
709 fasync::Task::spawn(async move { server.serve(server_end.into_stream()).await });
710 let client = Arc::new(RemoteBlockClient::new(client).await.unwrap());
711 gpt::Gpt::format(client.clone(), vec![gpt::PartitionInfo::nil(); 128])
712 .await
713 .expect("format failed");
714 vmo
715 }
716
717 struct FvmTestConfig {
718 fxblob_enabled: bool,
719 storage_host_enabled: bool,
720 }
721
722 enum TestState {
724 Devfs(#[allow(dead_code)] RamdiskClient),
726 StorageHost(
729 #[allow(dead_code)] RamdiskClient,
730 #[allow(dead_code)] ServingMultiVolumeFilesystem,
731 ),
732 }
733
734 async fn initialize(config: FvmTestConfig) -> (TestState, BenchmarkVolumeFactory) {
735 if config.fxblob_enabled {
736 let vmo = init_gpt(BLOCK_SIZE as u32, GPT_BLOCK_COUNT).await;
738 let ramdisk_builder = RamdiskClientBuilder::new_with_vmo(vmo, Some(BLOCK_SIZE));
739 let ramdisk = if config.storage_host_enabled {
740 ramdisk_builder.use_v2()
741 } else {
742 ramdisk_builder
743 }
744 .build()
745 .await
746 .expect("Failed to create ramdisk");
747
748 if config.storage_host_enabled {
749 let gpt = fs_management::filesystem::Filesystem::from_boxed_config(
750 ramdisk.connector().unwrap(),
751 Box::new(Gpt::dynamic_child()),
752 )
753 .serve_multi_volume()
754 .await
755 .expect("Failed to serve GPT");
756 let partitions =
757 Service::open_from_dir(gpt.exposed_dir(), fpartitions::PartitionServiceMarker)
758 .unwrap();
759 let manager =
760 connect_to_protocol_at_dir_root::<fpartitions::PartitionsManagerMarker>(
761 gpt.exposed_dir(),
762 )
763 .unwrap();
764 let fvm = BenchmarkVolumeFactory::connect_to_test_partition(partitions, manager)
765 .await
766 .expect("Failed to connect to FVM");
767 (TestState::StorageHost(ramdisk, gpt), fvm)
768 } else {
769 ramdisk
770 .as_controller()
771 .expect("invalid controller")
772 .bind("gpt.cm")
773 .await
774 .expect("FIDL error calling bind()")
775 .map_err(zx::Status::from_raw)
776 .expect("bind() returned non-Ok status");
777 wait_for_block_device_devfs(&[BlockDeviceMatcher::ContentsMatch(DiskFormat::Gpt)])
778 .await
779 .expect("Failed to wait for GPT to appear");
780 let fvm = BenchmarkVolumeFactory::connect_to_test_partition_devfs()
781 .await
782 .expect("Failed to connect to FVM");
783 (TestState::Devfs(ramdisk), fvm)
784 }
785 } else {
786 let ramdisk_builder = RamdiskClientBuilder::new(BLOCK_SIZE, BLOCK_COUNT);
788 let ramdisk = if config.storage_host_enabled {
789 ramdisk_builder.use_v2()
790 } else {
791 ramdisk_builder
792 }
793 .build()
794 .await
795 .expect("Failed to create ramdisk");
796 fvm::format_for_fvm(&ramdisk.open().unwrap().into_proxy(), RAMDISK_FVM_SLICE_SIZE)
797 .expect("Failed to format FVM");
798 if config.storage_host_enabled {
799 let fvm_component = match fs_management::filesystem::Filesystem::from_boxed_config(
800 ramdisk.connector().unwrap(),
801 Box::new(Fvm::dynamic_child()),
802 )
803 .serve_multi_volume()
804 .await
805 {
806 Ok(fvm_component) => fvm_component,
807 Err(_) => loop {},
808 };
809 let volumes_connector = {
810 let exposed_dir =
811 fuchsia_fs::directory::clone(fvm_component.exposed_dir()).unwrap();
812 Box::new(move || {
813 connect_to_protocol_at_dir_root::<VolumesMarker>(&exposed_dir).unwrap()
814 })
815 };
816 let volumes_dir_connector = {
817 let exposed_dir =
818 fuchsia_fs::directory::clone(fvm_component.exposed_dir()).unwrap();
819 Box::new(move || {
820 fuchsia_fs::directory::open_directory_async(
821 &exposed_dir,
822 "volumes",
823 fio::PERM_READABLE,
824 )
825 .unwrap()
826 })
827 };
828 let fvm = BenchmarkVolumeFactory::connect_to_system_fvm(
829 volumes_connector,
830 volumes_dir_connector,
831 );
832 (TestState::StorageHost(ramdisk, fvm_component), fvm.unwrap())
833 } else {
834 let block_controller = ramdisk.open_controller().unwrap();
836 let fvm_path = block_controller
837 .get_topological_path()
838 .await
839 .expect("FIDL error")
840 .expect("Failed to get topo path");
841 let dir = fuchsia_fs::directory::open_in_namespace(&fvm_path, fio::PERM_READABLE)
842 .unwrap();
843 let volume_manager = fvm::start_fvm_driver(&block_controller, &dir)
844 .await
845 .expect("Failed to start FVM");
846 let type_guid = fidl_fuchsia_storage_block::Guid { value: BLOBFS_TYPE_GUID };
847 let instance_guid =
848 fidl_fuchsia_storage_block::Guid { value: create_random_guid() };
849 zx::ok(
850 volume_manager
851 .allocate_partition(1, &type_guid, &instance_guid, BLOBFS_VOLUME_NAME, 0)
852 .await
853 .expect("FIDL error"),
854 )
855 .expect("failed to allocate blobfs partition");
856 wait_for_block_device_devfs(&[
857 BlockDeviceMatcher::Name(BLOBFS_VOLUME_NAME),
858 BlockDeviceMatcher::TypeGuid(&BLOBFS_TYPE_GUID),
859 ])
860 .await
861 .expect("Failed to wait for blobfs to appear");
862 let fvm = BenchmarkVolumeFactory::connect_to_system_fvm_devfs()
863 .await
864 .expect("Failed to connect to FVM");
865 (TestState::Devfs(ramdisk), fvm)
866 }
867 }
868 }
869
870 async fn benchmark_volume_factory_can_find_fvm_instance(config: FvmTestConfig) {
871 let (_state, volume_factory) = initialize(config).await;
872
873 volume_factory
875 .create_block_device(&BlockDeviceConfig {
876 requires_fvm: true,
877 use_zxcrypt: false,
878 volume_size: None,
879 })
880 .await;
881 }
882
883 #[fuchsia::test]
884 async fn benchmark_volume_factory_can_find_fvm_instance_fvm_non_storage_host() {
885 benchmark_volume_factory_can_find_fvm_instance(FvmTestConfig {
886 fxblob_enabled: false,
887 storage_host_enabled: false,
888 })
889 .await;
890 }
891
892 #[fuchsia::test]
893 async fn benchmark_volume_factory_can_find_fvm_instance_gpt_non_storage_host() {
894 benchmark_volume_factory_can_find_fvm_instance(FvmTestConfig {
895 fxblob_enabled: true,
896 storage_host_enabled: false,
897 })
898 .await;
899 }
900
901 #[fuchsia::test]
902 async fn benchmark_volume_factory_can_find_fvm_instance_fvm() {
903 benchmark_volume_factory_can_find_fvm_instance(FvmTestConfig {
904 fxblob_enabled: false,
905 storage_host_enabled: true,
906 })
907 .await;
908 }
909
910 #[fuchsia::test]
911 async fn benchmark_volume_factory_can_find_fvm_instance_gpt() {
912 benchmark_volume_factory_can_find_fvm_instance(FvmTestConfig {
913 fxblob_enabled: true,
914 storage_host_enabled: true,
915 })
916 .await;
917 }
918
919 async fn dropping_an_fvm_volume_removes_the_volume(config: FvmTestConfig) {
920 let (_state, volume_factory) = initialize(config).await;
921 {
922 let _volume = volume_factory
923 .create_block_device(&BlockDeviceConfig {
924 requires_fvm: true,
925 use_zxcrypt: false,
926 volume_size: None,
927 })
928 .await;
929 assert!(volume_factory.contains_fvm_volume(BENCHMARK_VOLUME_NAME).await);
930 };
931 assert!(!volume_factory.contains_fvm_volume(BENCHMARK_VOLUME_NAME).await);
932 }
933
934 #[fuchsia::test]
935 async fn dropping_an_fvm_volume_removes_the_volume_fvm_non_storage_host() {
936 dropping_an_fvm_volume_removes_the_volume(FvmTestConfig {
937 fxblob_enabled: false,
938 storage_host_enabled: false,
939 })
940 .await;
941 }
942
943 #[fuchsia::test]
944 async fn dropping_an_fvm_volume_removes_the_volume_fvm() {
945 dropping_an_fvm_volume_removes_the_volume(FvmTestConfig {
946 fxblob_enabled: false,
947 storage_host_enabled: true,
948 })
949 .await;
950 }
951
952 async fn benchmark_volume_factory_create_block_device_with_zxcrypt(config: FvmTestConfig) {
953 let (_state, volume_factory) = initialize(config).await;
954 let _ = volume_factory
955 .create_block_device(&BlockDeviceConfig {
956 requires_fvm: true,
957 use_zxcrypt: true,
958 volume_size: None,
959 })
960 .await;
961 }
962
963 #[fuchsia::test]
964 async fn benchmark_volume_factory_create_block_device_with_zxcrypt_fvm_non_storage_host() {
965 benchmark_volume_factory_create_block_device_with_zxcrypt(FvmTestConfig {
966 fxblob_enabled: false,
967 storage_host_enabled: false,
968 })
969 .await;
970 }
971
972 #[fuchsia::test]
973 async fn benchmark_volume_factory_create_block_device_with_zxcrypt_gpt_non_storage_host() {
974 benchmark_volume_factory_create_block_device_with_zxcrypt(FvmTestConfig {
975 fxblob_enabled: true,
976 storage_host_enabled: false,
977 })
978 .await;
979 }
980
981 #[fuchsia::test]
982 async fn benchmark_volume_factory_create_block_device_with_zxcrypt_fvm() {
983 benchmark_volume_factory_create_block_device_with_zxcrypt(FvmTestConfig {
984 fxblob_enabled: false,
985 storage_host_enabled: true,
986 })
987 .await;
988 }
989
990 #[fuchsia::test]
991 async fn benchmark_volume_factory_create_block_device_with_zxcrypt_gpt() {
992 benchmark_volume_factory_create_block_device_with_zxcrypt(FvmTestConfig {
993 fxblob_enabled: true,
994 storage_host_enabled: true,
995 })
996 .await;
997 }
998}