@@ -18,6 +18,7 @@ use mesh::rpc::RpcSend;
1818use nvme_resources:: NamespaceDefinition ;
1919use nvme_resources:: NvmeControllerHandle ;
2020use petri:: PetriVmBuilder ;
21+ #[ cfg( windows) ]
2122use petri:: hyperv:: HyperVPetriBackend ;
2223use petri:: openvmm:: OpenVmmPetriBackend ;
2324use petri:: pipette:: PipetteClient ;
@@ -37,10 +38,11 @@ use storvsp_resources::ScsiControllerHandle;
3738use storvsp_resources:: ScsiDeviceAndPath ;
3839use storvsp_resources:: ScsiPath ;
3940use vm_resource:: IntoResource ;
41+ #[ cfg( windows) ]
4042use vmm_test_macros:: hyperv_test;
4143use vmm_test_macros:: openvmm_test;
44+ #[ cfg( windows) ]
4245use vtl2_settings_proto:: Vtl2Settings ;
43- use vtl2_settings_proto:: Vtl2SettingsFixed ;
4446
4547/// Create a VPCI device config for an NVMe controller assigned to VTL2, with a single namespace.
4648/// The namespace will be backed by either a file or a ramdisk, depending on whether
@@ -624,12 +626,16 @@ async fn openhcl_linux_storvsp_dvd_nvme(
624626 Ok ( ( ) )
625627}
626628
629+ #[ cfg( windows) ]
627630#[ hyperv_test( openhcl_uefi_x64( vhd( ubuntu_2504_server_x64) ) ) ]
628631pub async fn add_openhcl_nvme_storage (
629632 config : PetriVmBuilder < HyperVPetriBackend > ,
630633) -> anyhow:: Result < ( ) > {
631- let instance_id = Guid :: new_random ( ) ;
634+ let vtl0_instance_id = Guid :: new_random ( ) ;
635+ let vtl2_instance_id = Guid :: new_random ( ) ;
636+ const VHD_SIZE : u64 = 200 * 1024 * 1024 ; // 200 MiB
632637
638+ // todo: generate a better temp file name
633639 let temp_dir = tempfile:: tempdir ( ) ?;
634640 let vhd_path = temp_dir. path ( ) . join ( "test.vhd" ) ;
635641 let file = File :: options ( )
@@ -641,43 +647,78 @@ pub async fn add_openhcl_nvme_storage(
641647 . open ( & vhd_path)
642648 . context ( "create file" ) ?;
643649
644- file. set_len ( 32 * 1024 * 1024 ) . context ( "set file length" ) ?;
650+ file. set_len ( VHD_SIZE ) . context ( "set file length" ) ?;
645651 disk_vhd1:: Vhd1Disk :: make_fixed ( & file) . context ( "make fixed" ) ?;
646652
647- let ( mut vm, agent) = config. run ( ) . await ?;
648- vm. backend ( )
649- . add_openhcl_nvme_storage ( Some ( & instance_id) , & [ vhd_path. to_str ( ) . unwrap ( ) ] )
650- . await ?;
651-
652- let mut vtl2_settings = Vtl2Settings {
653+ let initial_vtl2_settings = Vtl2Settings {
653654 version : vtl2_settings_proto:: vtl2_settings_base:: Version :: V1 . into ( ) ,
654- dynamic : Some ( Default :: default ( ) ) ,
655- fixed : Some ( Default :: default ( ) ) ,
655+ dynamic : Some ( vtl2_settings_proto:: Vtl2SettingsDynamic {
656+ storage_controllers : vec ! [
657+ Vtl2StorageControllerBuilder :: scsi( )
658+ . with_instance_id( vtl0_instance_id)
659+ . build( ) ,
660+ ] ,
661+ ..Default :: default ( )
662+ } ) ,
663+ fixed : None ,
656664 namespace_settings : Default :: default ( ) ,
657665 } ;
658- vtl2_settings
659- . dynamic
660- . as_mut ( )
661- . unwrap ( )
662- . storage_controllers
666+
667+ // This is a bit ugly; ideally the HyperV backend would munge and set any
668+ // VTL2 settings to the modify call (just like happens for OpenVMM).
669+ // For now, just take the expedient path to get this test stood up.
670+ let mut vtl2_settings = initial_vtl2_settings. clone ( ) ;
671+
672+ let ( mut vm, agent) = config
673+ . with_vmbus_redirect ( true )
674+ . modify_backend ( move |b| {
675+ assert ! ( b. initial_vtl2_settings. is_none( ) ) ;
676+ petri:: hyperv:: HyperVPetriConfig {
677+ initial_vtl2_settings : Some ( initial_vtl2_settings) ,
678+ }
679+ } )
680+ . run ( )
681+ . await ?;
682+
683+ // Runtime add the NVMe device and update the VTL2 settings to
684+ // include it.
685+ //
686+ // This _could_ happen before the VM is started, but do it at runtime
687+ // for test simplicity.
688+
689+ vm. backend ( )
690+ . add_openhcl_nvme_storage ( Some ( & vtl2_instance_id) , & [ vhd_path. to_str ( ) . unwrap ( ) ] )
691+ . await ?;
692+
693+ vtl2_settings. dynamic . as_mut ( ) . unwrap ( ) . storage_controllers [ 0 ]
694+ . luns
663695 . push (
664- Vtl2StorageControllerBuilder :: scsi ( )
665- . with_instance_id ( Guid :: new_random ( ) )
666- . add_lun (
667- Vtl2LunBuilder :: disk ( )
668- . with_location ( 0 )
669- . with_physical_device ( Vtl2StorageBackingDeviceBuilder :: new (
670- ControllerType :: Nvme ,
671- instance_id,
672- 1 ,
673- ) ) ,
674- )
696+ Vtl2LunBuilder :: disk ( )
697+ . with_location ( 0 )
698+ . with_physical_device ( Vtl2StorageBackingDeviceBuilder :: new (
699+ ControllerType :: Nvme ,
700+ vtl2_instance_id,
701+ 1 ,
702+ ) )
675703 . build ( ) ,
676704 ) ;
677705 vm. backend ( ) . set_base_vtl2_settings ( & vtl2_settings) . await ?;
678706
679- // todo: configure vtl2 settings
680- // todo: make sure the disk shows up in the VTL0 guest
707+ match vm. inspect_openhcl ( "vm/nvme/devices" , None , None ) . await {
708+ Err ( e) => tracing:: error!( ?e, "Failed to inspect NVMe devices" ) ,
709+ Ok ( devices) => tracing:: info!( devices = %devices. json( ) , "NVMe devices" ) ,
710+ }
711+
712+ test_storage_linux (
713+ & agent,
714+ vec ! [ ExpectedGuestDevice {
715+ controller_guid: vtl0_instance_id,
716+ lun: 0 ,
717+ disk_size_sectors: ( VHD_SIZE / 512 ) as usize ,
718+ friendly_name: "nvme" . to_string( ) ,
719+ } ] ,
720+ )
721+ . await ?;
681722
682723 agent. power_off ( ) . await ?;
683724 vm. wait_for_clean_teardown ( ) . await ?;
0 commit comments