Skip to content

Commit a6cd013

Browse files
committed
storage: Add storvsc_manager and disk_storvsc
1 parent ad54732 commit a6cd013

File tree

22 files changed

+1382
-36
lines changed

22 files changed

+1382
-36
lines changed

Cargo.lock

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,6 +1373,22 @@ dependencies = [
13731373
"vm_resource",
13741374
]
13751375

1376+
[[package]]
1377+
name = "disk_storvsc"
1378+
version = "0.0.0"
1379+
dependencies = [
1380+
"disk_backend",
1381+
"futures",
1382+
"inspect",
1383+
"scsi_buffers",
1384+
"scsi_defs",
1385+
"storvsc_driver",
1386+
"storvsp_protocol",
1387+
"tracing",
1388+
"vmbus_user_channel",
1389+
"zerocopy 0.8.24",
1390+
]
1391+
13761392
[[package]]
13771393
name = "disk_striped"
13781394
version = "0.0.0"
@@ -6926,10 +6942,12 @@ dependencies = [
69266942
name = "storvsc_driver"
69276943
version = "0.0.0"
69286944
dependencies = [
6945+
"anyhow",
69296946
"futures",
69306947
"futures-concurrency",
69316948
"guestmem",
69326949
"inspect",
6950+
"mesh",
69336951
"mesh_channel",
69346952
"pal_async",
69356953
"scsi_buffers",
@@ -6941,6 +6959,7 @@ dependencies = [
69416959
"thiserror 2.0.12",
69426960
"tracing",
69436961
"tracing_helpers",
6962+
"user_driver",
69446963
"vmbus_async",
69456964
"vmbus_channel",
69466965
"vmbus_ring",
@@ -6996,6 +7015,7 @@ version = "0.0.0"
69967015
dependencies = [
69977016
"arbitrary",
69987017
"guid",
7018+
"inspect",
69997019
"open_enum",
70007020
"scsi_defs",
70017021
"zerocopy 0.8.24",
@@ -7777,6 +7797,7 @@ dependencies = [
77777797
"disk_blockdevice",
77787798
"disk_get_vmgs",
77797799
"disk_nvme",
7800+
"disk_storvsc",
77807801
"firmware_pcat",
77817802
"firmware_uefi",
77827803
"firmware_uefi_custom_vars",
@@ -7843,7 +7864,9 @@ dependencies = [
78437864
"sparse_mmap",
78447865
"state_unit",
78457866
"storage_string",
7867+
"storvsc_driver",
78467868
"storvsp",
7869+
"storvsp_protocol",
78477870
"storvsp_resources",
78487871
"tee_call",
78497872
"test_with_tracing",

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ disk_layered = { path = "vm/devices/storage/disk_layered" }
268268
disk_nvme = { path = "vm/devices/storage/disk_nvme" }
269269
disk_delay = { path = "vm/devices/storage/disk_delay" }
270270
disk_prwrap = { path = "vm/devices/storage/disk_prwrap" }
271+
disk_storvsc = { path = "vm/devices/storage/disk_storvsc" }
271272
disk_striped = { path = "vm/devices/storage/disk_striped" }
272273
disk_vhd1 = { path = "vm/devices/storage/disk_vhd1" }
273274
disk_vhdmp = { path = "vm/devices/storage/disk_vhdmp" }

openhcl/openhcl_boot/src/main.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,9 @@ fn build_kernel_command_line(
158158
"rdinit=/underhill-init",
159159
// Default to user-mode NVMe driver.
160160
"OPENHCL_NVME_VFIO=1",
161-
// The next three items reduce the memory overhead of the storvsc driver.
161+
// Default to user-mode storvsc driver.
162+
"OPENHCL_STORVSC_USERMODE=1",
163+
// The next three items reduce the memory overhead of the kernel storvsc driver.
162164
// Since it is only used for DVD, performance is not critical.
163165
"hv_storvsc.storvsc_vcpus_per_sub_channel=2048",
164166
// Fix number of hardware queues at 2.

openhcl/rootfs.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ file /lib/modules/001/pci-hyperv.ko ${OPENHCL_KERNEL_PATH}/build/native/
3535

3636
# Storvsc is last because it sometimes takes a long time to load and should not
3737
# block other device startup.
38-
file /lib/modules/999/hv_storvsc.ko ${OPENHCL_KERNEL_PATH}/build/native/bin/${OPENHCL_KERNEL_ARCH}/modules/kernel/drivers/scsi/hv_storvsc.ko 0644 0 0
38+
#file /lib/modules/999/hv_storvsc.ko ${OPENHCL_KERNEL_PATH}/build/native/bin/${OPENHCL_KERNEL_ARCH}/modules/kernel/drivers/scsi/hv_storvsc.ko 0644 0 0
3939

4040
# These nodes are needed for early logging before devfs is mounted.
4141
nod /dev/null 0666 0 0 c 1 3

openhcl/underhill_core/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ disk_backend_resources.workspace = true
4848
disk_blockdevice.workspace = true
4949
disk_get_vmgs.workspace = true
5050
disk_nvme.workspace = true
51+
disk_storvsc.workspace = true
5152
firmware_uefi.workspace = true
5253
firmware_uefi_custom_vars.workspace = true
5354
hyperv_ic_guest.workspace = true
@@ -79,7 +80,9 @@ scsidisk.workspace = true
7980
scsidisk_resources.workspace = true
8081
serial_16550_resources.workspace = true
8182
storage_string.workspace = true
83+
storvsc_driver.workspace = true
8284
storvsp.workspace = true
85+
storvsp_protocol.workspace = true
8386
storvsp_resources.workspace = true
8487
tpm_resources.workspace = true
8588
tpm = { workspace = true, features = ["tpm"] }

openhcl/underhill_core/src/dispatch/mod.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ use crate::reference_time::ReferenceTime;
1616
use crate::servicing;
1717
use crate::servicing::NvmeSavedState;
1818
use crate::servicing::ServicingState;
19+
use crate::servicing::StorvscSavedState;
20+
use crate::storvsc_manager::StorvscManager;
1921
use crate::vmbus_relay_unit::VmbusRelayHandle;
2022
use crate::worker::FirmwareType;
2123
use crate::worker::NetworkSettingsError;
@@ -144,6 +146,7 @@ pub(crate) struct LoadedVm {
144146
pub uevent_listener: Arc<UeventListener>,
145147
pub resolver: ResourceResolver,
146148
pub nvme_manager: Option<NvmeManager>,
149+
pub storvsc_manager: Option<StorvscManager>,
147150
pub emuplat_servicing: EmuplatServicing,
148151
pub device_interfaces: Option<DeviceInterfaces>,
149152
pub vmbus_client: Option<vmbus_client::VmbusClient>,
@@ -323,6 +326,7 @@ impl LoadedVm {
323326
resp.field("vmgs", self.vmgs.as_ref().map(|x| &x.0));
324327
resp.field("network", &self.network_settings);
325328
resp.field("nvme", &self.nvme_manager);
329+
resp.field("storvsc", &self.storvsc_manager);
326330
resp.field("resolver", &self.resolver);
327331
resp.field(
328332
"vtl0_memory_map",
@@ -575,6 +579,13 @@ impl LoadedVm {
575579
}
576580
};
577581

582+
// Reset all user-mode storvsc devices.
583+
let shutdown_storvsc = async {
584+
if let Some(storvsc_manager) = self.storvsc_manager.take() {
585+
storvsc_manager.shutdown().instrument(tracing::info_span!("shutdown_storvsc", CVM_ALLOWED, %correlation_id)).await;
586+
}
587+
};
588+
578589
// Unbind drivers from the PCI devices to prepare for a kernel
579590
// restart.
580591
let shutdown_pci = async {
@@ -583,7 +594,7 @@ impl LoadedVm {
583594
.await
584595
};
585596

586-
let (r, (), ()) = (shutdown_pci, shutdown_mana, shutdown_nvme).join().await;
597+
let (r, (), (), ()) = (shutdown_pci, shutdown_mana, shutdown_nvme, shutdown_storvsc).join().await;
587598
r?;
588599

589600
Ok(state)
@@ -731,6 +742,17 @@ impl LoadedVm {
731742
None
732743
};
733744

745+
let storvsc_state = if let Some(s) = &self.storvsc_manager {
746+
s.save()
747+
.instrument(tracing::info_span!("storvsc_manager_save", CVM_ALLOWED))
748+
.await
749+
.map(|state| StorvscSavedState {
750+
storvsc_state: state,
751+
})
752+
} else {
753+
None
754+
};
755+
734756
let units = self.save_units().await.context("state unit save failed")?;
735757
let vmgs = if let Some((vmgs_thin_client, vmgs_disk_metadata, _)) = self.vmgs.as_ref() {
736758
Some((
@@ -770,6 +792,7 @@ impl LoadedVm {
770792
nvme_state,
771793
dma_manager_state,
772794
vmbus_client,
795+
storvsc_state,
773796
},
774797
units,
775798
};

openhcl/underhill_core/src/dispatch/vtl2_settings_worker.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
66
use super::LoadedVm;
77
use crate::nvme_manager::manager::NvmeDiskConfig;
8+
use crate::storvsc_manager::StorvscDiskConfig;
89
use crate::worker::NicConfig;
910
use anyhow::Context;
1011
use cvm_tracing::CVM_ALLOWED;
@@ -243,6 +244,7 @@ pub struct DeviceInterfaces {
243244
scsi_dvds: HashMap<StorageDevicePath, mesh::Sender<SimpleScsiDvdRequest>>,
244245
scsi_request: HashMap<Guid, mesh::Sender<ScsiControllerRequest>>,
245246
use_nvme_vfio: bool,
247+
use_storvsc_usermode: bool,
246248
}
247249

248250
impl Vtl2SettingsWorker {
@@ -376,6 +378,7 @@ impl Vtl2SettingsWorker {
376378
&StorageContext {
377379
uevent_listener,
378380
use_nvme_vfio: self.interfaces.use_nvme_vfio,
381+
use_storvsc_usermode: self.interfaces.use_storvsc_usermode,
379382
},
380383
&disk,
381384
false,
@@ -394,6 +397,7 @@ impl Vtl2SettingsWorker {
394397
&StorageContext {
395398
uevent_listener,
396399
use_nvme_vfio: self.interfaces.use_nvme_vfio,
400+
use_storvsc_usermode: self.interfaces.use_storvsc_usermode,
397401
},
398402
&disk,
399403
false,
@@ -957,6 +961,7 @@ async fn make_disk_type_from_physical_devices(
957961
struct StorageContext<'a> {
958962
uevent_listener: &'a UeventListener,
959963
use_nvme_vfio: bool,
964+
use_storvsc_usermode: bool,
960965
}
961966

962967
#[instrument(skip_all, fields(CVM_ALLOWED))]
@@ -1000,6 +1005,36 @@ async fn make_disk_type_from_physical_device(
10001005
}));
10011006
}
10021007

1008+
// Special case for VScsi when using usermode storvsc.
1009+
if storage_context.use_storvsc_usermode
1010+
&& matches!(
1011+
single_device.device_type,
1012+
underhill_config::DeviceType::VScsi
1013+
)
1014+
{
1015+
// Wait for the SCSI controller to arrive.
1016+
let devpath = uio_path(&controller_instance_id);
1017+
async {
1018+
ctx.until_cancelled(storage_context.uevent_listener.wait_for_devpath(&devpath))
1019+
.await??;
1020+
Ok(())
1021+
}
1022+
.await
1023+
.map_err(|err| Error::StorageCannotFindVtl2Device {
1024+
device_type: single_device.device_type,
1025+
instance_id: controller_instance_id,
1026+
sub_device_path,
1027+
source: err,
1028+
})?;
1029+
1030+
// Cannot validate device actually exists yet. Check this later
1031+
// TODO: Is this true?
1032+
return Ok(Resource::new(StorvscDiskConfig {
1033+
instance_guid: controller_instance_id,
1034+
lun: sub_device_path as u8,
1035+
}));
1036+
}
1037+
10031038
// Wait for the device to arrive.
10041039
let devname = async {
10051040
let devname = ctx
@@ -1428,6 +1463,7 @@ pub async fn create_storage_controllers_from_vtl2_settings(
14281463
ctx: &mut CancelContext,
14291464
uevent_listener: &UeventListener,
14301465
use_nvme_vfio: bool,
1466+
use_storvsc_usermode: bool,
14311467
settings: &Vtl2SettingsDynamic,
14321468
sub_channels: u16,
14331469
is_restoring: bool,
@@ -1443,6 +1479,7 @@ pub async fn create_storage_controllers_from_vtl2_settings(
14431479
let storage_context = StorageContext {
14441480
uevent_listener,
14451481
use_nvme_vfio,
1482+
use_storvsc_usermode,
14461483
};
14471484
let ide_controller =
14481485
make_ide_controller_config(ctx, &storage_context, settings, is_restoring).await?;
@@ -1643,6 +1680,11 @@ fn vpci_path(instance_id: &Guid) -> (String, PathBuf) {
16431680
(pci_id, devpath)
16441681
}
16451682

1683+
fn uio_path(instance_id: &Guid) -> PathBuf {
1684+
// TODO: Is this enough?
1685+
PathBuf::from(format!("/sys/bus/vmbus/devices/{instance_id}/uio"))
1686+
}
1687+
16461688
/// Waits for the PCI path to get populated. The PCI path is just a symlink to the actual
16471689
/// device path. This should be called once the device path is available.
16481690
pub async fn wait_for_pci_path(pci_id: &String) {
@@ -1782,6 +1824,7 @@ impl InitialControllers {
17821824
uevent_listener: &UeventListener,
17831825
dps: &DevicePlatformSettings,
17841826
use_nvme_vfio: bool,
1827+
use_storvsc_usermode: bool,
17851828
is_restoring: bool,
17861829
default_io_queue_depth: u32,
17871830
) -> anyhow::Result<Self> {
@@ -1801,6 +1844,7 @@ impl InitialControllers {
18011844
&mut context,
18021845
uevent_listener,
18031846
use_nvme_vfio,
1847+
use_storvsc_usermode,
18041848
dynamic,
18051849
fixed.scsi_sub_channels,
18061850
is_restoring,
@@ -1851,6 +1895,7 @@ impl InitialControllers {
18511895
scsi_dvds,
18521896
scsi_request,
18531897
use_nvme_vfio,
1898+
use_storvsc_usermode,
18541899
},
18551900
};
18561901

openhcl/underhill_core/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ mod nvme_manager;
2020
mod options;
2121
mod reference_time;
2222
mod servicing;
23+
mod storvsc_manager;
2324
mod threadpool_vm_task_backend;
2425
mod vmbus_relay_unit;
2526
mod vmgs_logger;
@@ -330,6 +331,7 @@ async fn launch_workers(
330331
disable_uefi_frontpage: opt.disable_uefi_frontpage,
331332
guest_state_encryption_policy: opt.guest_state_encryption_policy,
332333
attempt_ak_cert_callback: opt.attempt_ak_cert_callback,
334+
storvsc_usermode: opt.storvsc_usermode,
333335
};
334336

335337
let (mut remote_console_cfg, framebuffer_access) =

openhcl/underhill_core/src/options.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ pub struct Options {
187187
/// (HCL_ATTEMPT_AK_CERT_CALLBACK=1) Attempt to renew the AK cert.
188188
/// If not specified, use the configuration in DPSv2 ManagementVtlFeatures.
189189
pub attempt_ak_cert_callback: Option<bool>,
190+
191+
/// (OPENHCL_STORVSC_USERMODE=1)
192+
/// Use the user-mode storvsc driver instead of the Linux driver.
193+
pub storvsc_usermode: bool,
190194
}
191195

192196
impl Options {
@@ -322,6 +326,7 @@ impl Options {
322326
.map_err(|e| tracing::warn!("failed to parse HCL_ATTEMPT_AK_CERT_CALLBACK: {:#}", e))
323327
.ok()
324328
.flatten();
329+
let storvsc_usermode = parse_env_bool("OPENHCL_STORVSC_USERMODE");
325330

326331
let mut args = std::env::args().chain(extra_args);
327332
// Skip our own filename.
@@ -381,6 +386,7 @@ impl Options {
381386
disable_uefi_frontpage,
382387
guest_state_encryption_policy,
383388
attempt_ak_cert_callback,
389+
storvsc_usermode,
384390
})
385391
}
386392

0 commit comments

Comments
 (0)