From 15b00ace1399d51507b2884ba7c29dd0b1f84938 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 7 Jan 2024 10:10:29 -0500 Subject: [PATCH] Add support for nvme devices This also seems to avoid the disk corruption that we see with virtio-blk; it reportedly has a small performance hit for raw speed, but I think avoiding the double caching (guest and host) is much better from a performance perspective. Signed-off-by: Colin Walters --- pkg/config/json.go | 16 ++++++++++++++++ pkg/config/virtio.go | 29 +++++++++++++++++++++++++++++ pkg/config/virtio_test.go | 10 ++++++++++ pkg/vf/virtio.go | 28 ++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+) diff --git a/pkg/config/json.go b/pkg/config/json.go index 8d7be706..312c5f3f 100644 --- a/pkg/config/json.go +++ b/pkg/config/json.go @@ -25,6 +25,7 @@ const ( vfGpu vmComponentKind = "virtiogpu" vfInput vmComponentKind = "virtioinput" usbMassStorage vmComponentKind = "usbmassstorage" + nvme vmComponentKind = "nvme" rosetta vmComponentKind = "rosetta" ) @@ -109,6 +110,10 @@ func unmarshalDevice(rawMsg json.RawMessage) (VirtioDevice, error) { var newDevice VirtioBlk err = json.Unmarshal(rawMsg, &newDevice) dev = &newDevice + case nvme: + var newDevice NVMExpressController + err = json.Unmarshal(rawMsg, &newDevice) + dev = &newDevice case vfFs: var newDevice VirtioFs err = json.Unmarshal(rawMsg, &newDevice) @@ -258,6 +263,17 @@ func (dev *VirtioFs) MarshalJSON() ([]byte, error) { }) } +func (dev *NVMExpressController) MarshalJSON() ([]byte, error) { + type devWithKind struct { + jsonKind + NVMExpressController + } + return json.Marshal(devWithKind{ + jsonKind: kind(nvme), + NVMExpressController: *dev, + }) +} + func (dev *RosettaShare) MarshalJSON() ([]byte, error) { type devWithKind struct { jsonKind diff --git a/pkg/config/virtio.go b/pkg/config/virtio.go index 318e08d7..dd1b1cc1 100644 --- a/pkg/config/virtio.go +++ b/pkg/config/virtio.go @@ -77,6 +77,11 @@ type RosettaShare struct { InstallRosetta bool } +// NVMExpressController configures a NVMe controller in the guest +type NVMExpressController struct { + StorageConfig +} + // virtioRng configures a random number generator (RNG) device. type VirtioRng struct { } @@ -143,6 +148,8 @@ func deviceFromCmdLine(deviceOpts string) (VirtioDevice, error) { switch opts[0] { case "rosetta": dev = &RosettaShare{} + case "nvme": + dev = nvmExpressControllerNewEmpty() case "virtio-blk": dev = virtioBlkNewEmpty() case "virtio-fs": @@ -452,6 +459,28 @@ func (dev *VirtioRng) FromOptions(options []option) error { return nil } +func nvmExpressControllerNewEmpty() *NVMExpressController { + return &NVMExpressController{ + StorageConfig: StorageConfig{ + DevName: "nvme", + }, + } +} + +func NVMExpressControllerNew(imagePath string) (*NVMExpressController, error) { + r := nvmExpressControllerNewEmpty() + r.ImagePath = imagePath + return r, nil +} + +func (dev *NVMExpressController) FromOptions(options []option) error { + return dev.StorageConfig.FromOptions(options) +} + +func (dev *NVMExpressController) ToCmdLine() ([]string, error) { + return dev.StorageConfig.ToCmdLine() +} + func virtioBlkNewEmpty() *VirtioBlk { return &VirtioBlk{ StorageConfig: StorageConfig{ diff --git a/pkg/config/virtio_test.go b/pkg/config/virtio_test.go index 0b947a18..371c1875 100644 --- a/pkg/config/virtio_test.go +++ b/pkg/config/virtio_test.go @@ -45,6 +45,16 @@ var virtioDevTests = map[string]virtioDevTest{ expectedCmdLine: []string{"--device", "virtio-blk,path=/foo/bar,deviceId=test"}, alternateCmdLine: []string{"--device", "virtio-blk,deviceId=test,path=/foo/bar"}, }, + "NewNVMe": { + newDev: func() (VirtioDevice, error) { return NVMExpressControllerNew("/foo/bar") }, + expectedDev: &NVMExpressController{ + StorageConfig: StorageConfig{ + DevName: "nvme", + ImagePath: "/foo/bar", + }, + }, + expectedCmdLine: []string{"--device", "nvme,path=/foo/bar"}, + }, "NewVirtioFs": { newDev: func() (VirtioDevice, error) { return VirtioFsNew("/foo/bar", "") }, expectedDev: &VirtioFs{ diff --git a/pkg/vf/virtio.go b/pkg/vf/virtio.go index c6cdd250..0262b6d6 100644 --- a/pkg/vf/virtio.go +++ b/pkg/vf/virtio.go @@ -14,6 +14,7 @@ import ( ) type RosettaShare config.RosettaShare +type NVMExpressController config.NVMExpressController type VirtioBlk config.VirtioBlk type VirtioFs config.VirtioFs type VirtioRng config.VirtioRng @@ -22,6 +23,31 @@ type VirtioVsock config.VirtioVsock type VirtioInput config.VirtioInput type VirtioGPU config.VirtioGPU +func (dev *NVMExpressController) toVz() (vz.StorageDeviceConfiguration, error) { + var storageConfig StorageConfig = StorageConfig(dev.StorageConfig) + attachment, err := storageConfig.toVz() + if err != nil { + return nil, err + } + devConfig, err := vz.NewNVMExpressControllerDeviceConfiguration(attachment) + if err != nil { + return nil, err + } + + return devConfig, nil +} + +func (dev *NVMExpressController) AddToVirtualMachineConfig(vmConfig *vzVirtualMachineConfiguration) error { + storageDeviceConfig, err := dev.toVz() + if err != nil { + return err + } + log.Infof("Adding nvme device (imagePath: %s)", dev.ImagePath) + vmConfig.storageDevicesConfiguration = append(vmConfig.storageDevicesConfiguration, storageDeviceConfig) + + return nil +} + func (dev *VirtioBlk) toVz() (vz.StorageDeviceConfiguration, error) { var storageConfig StorageConfig = StorageConfig(dev.StorageConfig) attachment, err := storageConfig.toVz() @@ -251,6 +277,8 @@ func AddToVirtualMachineConfig(dev config.VirtioDevice, vmConfig *vzVirtualMachi return (*VirtioBlk)(d).AddToVirtualMachineConfig(vmConfig) case *config.RosettaShare: return (*RosettaShare)(d).AddToVirtualMachineConfig(vmConfig) + case *config.NVMExpressController: + return (*NVMExpressController)(d).AddToVirtualMachineConfig(vmConfig) case *config.VirtioFs: return (*VirtioFs)(d).AddToVirtualMachineConfig(vmConfig) case *config.VirtioNet: