Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1373,6 +1373,25 @@ dependencies = [
"vm_resource",
]

[[package]]
name = "disk_storvsc"
version = "0.0.0"
dependencies = [
"disk_backend",
"event-listener",
"futures",
"igvm_defs",
"inspect",
"scsi_buffers",
"scsi_defs",
"static_assertions",
"storvsc_driver",
"storvsp_protocol",
"tracing",
"vmbus_user_channel",
"zerocopy 0.8.24",
]

[[package]]
name = "disk_striped"
version = "0.0.0"
Expand Down Expand Up @@ -6926,10 +6945,14 @@ dependencies = [
name = "storvsc_driver"
version = "0.0.0"
dependencies = [
"anyhow",
"cvm_tracing",
"event-listener",
"futures",
"futures-concurrency",
"guestmem",
"inspect",
"mesh",
"mesh_channel",
"pal_async",
"scsi_buffers",
Expand All @@ -6941,6 +6964,7 @@ dependencies = [
"thiserror 2.0.12",
"tracing",
"tracing_helpers",
"user_driver",
"vmbus_async",
"vmbus_channel",
"vmbus_ring",
Expand Down Expand Up @@ -6996,6 +7020,7 @@ version = "0.0.0"
dependencies = [
"arbitrary",
"guid",
"inspect",
"open_enum",
"scsi_defs",
"zerocopy 0.8.24",
Expand Down Expand Up @@ -7777,6 +7802,7 @@ dependencies = [
"disk_blockdevice",
"disk_get_vmgs",
"disk_nvme",
"disk_storvsc",
"firmware_pcat",
"firmware_uefi",
"firmware_uefi_custom_vars",
Expand Down Expand Up @@ -7843,7 +7869,9 @@ dependencies = [
"sparse_mmap",
"state_unit",
"storage_string",
"storvsc_driver",
"storvsp",
"storvsp_protocol",
"storvsp_resources",
"tee_call",
"test_with_tracing",
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ disk_layered = { path = "vm/devices/storage/disk_layered" }
disk_nvme = { path = "vm/devices/storage/disk_nvme" }
disk_delay = { path = "vm/devices/storage/disk_delay" }
disk_prwrap = { path = "vm/devices/storage/disk_prwrap" }
disk_storvsc = { path = "vm/devices/storage/disk_storvsc" }
disk_striped = { path = "vm/devices/storage/disk_striped" }
disk_vhd1 = { path = "vm/devices/storage/disk_vhd1" }
disk_vhdmp = { path = "vm/devices/storage/disk_vhdmp" }
Expand Down
4 changes: 3 additions & 1 deletion openhcl/openhcl_boot/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,9 @@ fn build_kernel_command_line(
"rdinit=/underhill-init",
// Default to user-mode NVMe driver.
"OPENHCL_NVME_VFIO=1",
// The next three items reduce the memory overhead of the storvsc driver.
// Default to user-mode storvsc driver.
"OPENHCL_STORVSC_USERMODE=1",
// The next three items reduce the memory overhead of the kernel storvsc driver.
// Since it is only used for DVD, performance is not critical.
"hv_storvsc.storvsc_vcpus_per_sub_channel=2048",
// Fix number of hardware queues at 2.
Expand Down
2 changes: 1 addition & 1 deletion openhcl/rootfs.config
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ file /lib/modules/001/pci-hyperv.ko ${OPENHCL_KERNEL_PATH}/build/native/

# Storvsc is last because it sometimes takes a long time to load and should not
# block other device startup.
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
#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

# These nodes are needed for early logging before devfs is mounted.
nod /dev/null 0666 0 0 c 1 3
Expand Down
3 changes: 3 additions & 0 deletions openhcl/underhill_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ disk_backend_resources.workspace = true
disk_blockdevice.workspace = true
disk_get_vmgs.workspace = true
disk_nvme.workspace = true
disk_storvsc.workspace = true
firmware_uefi.workspace = true
firmware_uefi_custom_vars.workspace = true
hyperv_ic_guest.workspace = true
Expand Down Expand Up @@ -79,7 +80,9 @@ scsidisk.workspace = true
scsidisk_resources.workspace = true
serial_16550_resources.workspace = true
storage_string.workspace = true
storvsc_driver.workspace = true
storvsp.workspace = true
storvsp_protocol.workspace = true
storvsp_resources.workspace = true
tpm_resources.workspace = true
tpm = { workspace = true, features = ["tpm"] }
Expand Down
25 changes: 24 additions & 1 deletion openhcl/underhill_core/src/dispatch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ use crate::reference_time::ReferenceTime;
use crate::servicing;
use crate::servicing::NvmeSavedState;
use crate::servicing::ServicingState;
use crate::servicing::StorvscSavedState;
use crate::storvsc_manager::StorvscManager;
use crate::vmbus_relay_unit::VmbusRelayHandle;
use crate::worker::FirmwareType;
use crate::worker::NetworkSettingsError;
Expand Down Expand Up @@ -144,6 +146,7 @@ pub(crate) struct LoadedVm {
pub uevent_listener: Arc<UeventListener>,
pub resolver: ResourceResolver,
pub nvme_manager: Option<NvmeManager>,
pub storvsc_manager: Option<StorvscManager>,
pub emuplat_servicing: EmuplatServicing,
pub device_interfaces: Option<DeviceInterfaces>,
pub vmbus_client: Option<vmbus_client::VmbusClient>,
Expand Down Expand Up @@ -323,6 +326,7 @@ impl LoadedVm {
resp.field("vmgs", self.vmgs.as_ref().map(|x| &x.0));
resp.field("network", &self.network_settings);
resp.field("nvme", &self.nvme_manager);
resp.field("storvsc", &self.storvsc_manager);
resp.field("resolver", &self.resolver);
resp.field(
"vtl0_memory_map",
Expand Down Expand Up @@ -575,6 +579,13 @@ impl LoadedVm {
}
};

// Reset all user-mode storvsc devices.
let shutdown_storvsc = async {
if let Some(storvsc_manager) = self.storvsc_manager.take() {
storvsc_manager.shutdown().instrument(tracing::info_span!("shutdown_storvsc", CVM_ALLOWED, %correlation_id)).await;
}
};

// Unbind drivers from the PCI devices to prepare for a kernel
// restart.
let shutdown_pci = async {
Expand All @@ -583,7 +594,7 @@ impl LoadedVm {
.await
};

let (r, (), ()) = (shutdown_pci, shutdown_mana, shutdown_nvme).join().await;
let (r, (), (), ()) = (shutdown_pci, shutdown_mana, shutdown_nvme, shutdown_storvsc).join().await;
r?;

Ok(state)
Expand Down Expand Up @@ -731,6 +742,17 @@ impl LoadedVm {
None
};

let storvsc_state = if let Some(s) = &self.storvsc_manager {
s.save()
.instrument(tracing::info_span!("storvsc_manager_save", CVM_ALLOWED))
.await
.map(|state| StorvscSavedState {
storvsc_state: state,
})
} else {
None
};

let units = self.save_units().await.context("state unit save failed")?;
let vmgs = if let Some((vmgs_thin_client, vmgs_disk_metadata, _)) = self.vmgs.as_ref() {
Some((
Expand Down Expand Up @@ -770,6 +792,7 @@ impl LoadedVm {
nvme_state,
dma_manager_state,
vmbus_client,
storvsc_state,
},
units,
};
Expand Down
45 changes: 45 additions & 0 deletions openhcl/underhill_core/src/dispatch/vtl2_settings_worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

use super::LoadedVm;
use crate::nvme_manager::manager::NvmeDiskConfig;
use crate::storvsc_manager::StorvscDiskConfig;
use crate::worker::NicConfig;
use anyhow::Context;
use cvm_tracing::CVM_ALLOWED;
Expand Down Expand Up @@ -243,6 +244,7 @@ pub struct DeviceInterfaces {
scsi_dvds: HashMap<StorageDevicePath, mesh::Sender<SimpleScsiDvdRequest>>,
scsi_request: HashMap<Guid, mesh::Sender<ScsiControllerRequest>>,
use_nvme_vfio: bool,
use_storvsc_usermode: bool,
}

impl Vtl2SettingsWorker {
Expand Down Expand Up @@ -376,6 +378,7 @@ impl Vtl2SettingsWorker {
&StorageContext {
uevent_listener,
use_nvme_vfio: self.interfaces.use_nvme_vfio,
use_storvsc_usermode: self.interfaces.use_storvsc_usermode,
},
&disk,
false,
Expand All @@ -394,6 +397,7 @@ impl Vtl2SettingsWorker {
&StorageContext {
uevent_listener,
use_nvme_vfio: self.interfaces.use_nvme_vfio,
use_storvsc_usermode: self.interfaces.use_storvsc_usermode,
},
&disk,
false,
Expand Down Expand Up @@ -957,6 +961,7 @@ async fn make_disk_type_from_physical_devices(
struct StorageContext<'a> {
uevent_listener: &'a UeventListener,
use_nvme_vfio: bool,
use_storvsc_usermode: bool,
}

#[instrument(skip_all, fields(CVM_ALLOWED))]
Expand Down Expand Up @@ -1000,6 +1005,36 @@ async fn make_disk_type_from_physical_device(
}));
}

// Special case for VScsi when using usermode storvsc.
if storage_context.use_storvsc_usermode
&& matches!(
single_device.device_type,
underhill_config::DeviceType::VScsi
)
{
// Wait for the SCSI controller to arrive.
let devpath = uio_path(&controller_instance_id);
async {
ctx.until_cancelled(storage_context.uevent_listener.wait_for_devpath(&devpath))
.await??;
Ok(())
}
.await
.map_err(|err| Error::StorageCannotFindVtl2Device {
device_type: single_device.device_type,
instance_id: controller_instance_id,
sub_device_path,
source: err,
})?;

// Cannot validate device actually exists yet. Check this later
// TODO: Is this true?
return Ok(Resource::new(StorvscDiskConfig {
instance_guid: controller_instance_id,
lun: sub_device_path as u8,
}));
}

// Wait for the device to arrive.
let devname = async {
let devname = ctx
Expand Down Expand Up @@ -1428,6 +1463,7 @@ pub async fn create_storage_controllers_from_vtl2_settings(
ctx: &mut CancelContext,
uevent_listener: &UeventListener,
use_nvme_vfio: bool,
use_storvsc_usermode: bool,
settings: &Vtl2SettingsDynamic,
sub_channels: u16,
is_restoring: bool,
Expand All @@ -1443,6 +1479,7 @@ pub async fn create_storage_controllers_from_vtl2_settings(
let storage_context = StorageContext {
uevent_listener,
use_nvme_vfio,
use_storvsc_usermode,
};
let ide_controller =
make_ide_controller_config(ctx, &storage_context, settings, is_restoring).await?;
Expand Down Expand Up @@ -1643,6 +1680,11 @@ fn vpci_path(instance_id: &Guid) -> (String, PathBuf) {
(pci_id, devpath)
}

fn uio_path(instance_id: &Guid) -> PathBuf {
// TODO: Is this enough?
PathBuf::from(format!("/sys/bus/vmbus/devices/{instance_id}/uio"))
}

/// Waits for the PCI path to get populated. The PCI path is just a symlink to the actual
/// device path. This should be called once the device path is available.
pub async fn wait_for_pci_path(pci_id: &String) {
Expand Down Expand Up @@ -1782,6 +1824,7 @@ impl InitialControllers {
uevent_listener: &UeventListener,
dps: &DevicePlatformSettings,
use_nvme_vfio: bool,
use_storvsc_usermode: bool,
is_restoring: bool,
default_io_queue_depth: u32,
) -> anyhow::Result<Self> {
Expand All @@ -1801,6 +1844,7 @@ impl InitialControllers {
&mut context,
uevent_listener,
use_nvme_vfio,
use_storvsc_usermode,
dynamic,
fixed.scsi_sub_channels,
is_restoring,
Expand Down Expand Up @@ -1851,6 +1895,7 @@ impl InitialControllers {
scsi_dvds,
scsi_request,
use_nvme_vfio,
use_storvsc_usermode,
},
};

Expand Down
2 changes: 1 addition & 1 deletion openhcl/underhill_core/src/get_tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ pub fn init_tracing_backend(driver: impl 'static + SpawnDriver) -> anyhow::Resul
legacy_openvmm_env("OPENVMM_PERF_TRACE").unwrap_or_else(|_| "off".to_owned());

let pipe = vmbus_user_channel::open_uio_device(&GET_LOG_INTERFACE_GUID)
.and_then(|dev| vmbus_user_channel::message_pipe(&driver, dev))
.and_then(|dev| vmbus_user_channel::message_pipe(&driver, dev, None, None))
.map_err(|err| {
tracing::error!(
CVM_ALLOWED,
Expand Down
2 changes: 2 additions & 0 deletions openhcl/underhill_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ mod nvme_manager;
mod options;
mod reference_time;
mod servicing;
mod storvsc_manager;
mod threadpool_vm_task_backend;
mod vmbus_relay_unit;
mod vmgs_logger;
Expand Down Expand Up @@ -330,6 +331,7 @@ async fn launch_workers(
disable_uefi_frontpage: opt.disable_uefi_frontpage,
guest_state_encryption_policy: opt.guest_state_encryption_policy,
attempt_ak_cert_callback: opt.attempt_ak_cert_callback,
storvsc_usermode: opt.storvsc_usermode,
};

let (mut remote_console_cfg, framebuffer_access) =
Expand Down
6 changes: 6 additions & 0 deletions openhcl/underhill_core/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@ pub struct Options {
/// (HCL_ATTEMPT_AK_CERT_CALLBACK=1) Attempt to renew the AK cert.
/// If not specified, use the configuration in DPSv2 ManagementVtlFeatures.
pub attempt_ak_cert_callback: Option<bool>,

/// (OPENHCL_STORVSC_USERMODE=1)
/// Use the user-mode storvsc driver instead of the Linux driver.
pub storvsc_usermode: bool,
}

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

let mut args = std::env::args().chain(extra_args);
// Skip our own filename.
Expand Down Expand Up @@ -381,6 +386,7 @@ impl Options {
disable_uefi_frontpage,
guest_state_encryption_policy,
attempt_ak_cert_callback,
storvsc_usermode,
})
}

Expand Down
Loading