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