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