Refactor code to support multiple virtio transports at runtime

Currently, virtio transports for each device are determined with
architecture dependent build time conditionals. This isn't the ideal
solution, as virtio transports aren't exactly tied to the host's
architecture.

For example, aarch64 VMs do support both PCI and MMIO devices, and
after the recent introduction of the microvm machine type, that's also
the case for x86_64.

This patch extends each device that supports multiple transports with
a VirtioTransport field, so users of the library can manually specify
a transport for each device. To avoid breaking the compatibility, if
VirtioTransport is empty a behavior equivalent to the legacy one is
achieved by checking runtime.GOARCH and Config.Machine.Type.

Keeping support for isVirtioPCI/isVirtioCCW in qmp.go is a bit
tricky. Eventually, the hot-plug API should be extended so callers
must manually specify the transport for the device.

Signed-off-by: Sergio Lopez <slp@redhat.com>
This commit is contained in:
Sergio Lopez 2020-02-07 17:54:25 +01:00
parent cab4709376
commit 88a25a2d68
7 changed files with 566 additions and 383 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,112 +0,0 @@
// +build !s390x
/*
// Copyright contributors to the Virtual Machine Manager for Go project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
package qemu
const (
// Virtio9P is the 9pfs device driver.
Virtio9P DeviceDriver = "virtio-9p-pci"
// VirtioSerial is the serial device driver.
VirtioSerial DeviceDriver = "virtio-serial-pci"
// VirtioNet is the virt-io pci networking device driver.
VirtioNet DeviceDriver = VirtioNetPCI
// Vfio is the vfio driver
Vfio DeviceDriver = "vfio-pci"
// VirtioScsi is the virtio-scsi device
VirtioScsi DeviceDriver = "virtio-scsi-pci"
// VHostVSock is a generic Vsock vhost device
VHostVSock DeviceDriver = "vhost-vsock-pci"
)
// isVirtioPCI is a map indicating if a DeviceDriver is considered as a
// virtio PCI device, which is helpful to determine if the option "romfile"
// applies or not to this specific device.
var isVirtioPCI = map[DeviceDriver]bool{
NVDIMM: false,
Virtio9P: true,
VirtioNetPCI: true,
VirtioSerial: true,
VirtioBlock: true,
VirtioBlockPCI: true,
Console: false,
VirtioSerialPort: false,
VHostVSock: true,
VirtioRng: true,
VirtioBalloon: true,
VhostUserSCSI: true,
VhostUserBlk: true,
Vfio: true,
VirtioScsi: true,
PCIBridgeDriver: true,
PCIePCIBridgeDriver: true,
PCIeRootPort: true,
}
// isVirtioCCW is a dummy map to return always false on no-s390x arch
var isVirtioCCW = map[DeviceDriver]bool{}
// QemuNetdevParam converts to the QEMU -netdev parameter notation
func (n NetDeviceType) QemuNetdevParam() string {
switch n {
case TAP:
return "tap"
case MACVTAP:
return "tap"
case IPVTAP:
return "tap"
case VETHTAP:
return "tap" // -netdev type=tap -device virtio-net-pci
case VFIO:
return "" // -device vfio-pci (no netdev)
case VHOSTUSER:
return "vhost-user" // -netdev type=vhost-user (no device)
default:
return ""
}
}
// QemuDeviceParam converts to the QEMU -device parameter notation
func (n NetDeviceType) QemuDeviceParam() DeviceDriver {
switch n {
case TAP:
return "virtio-net-pci"
case MACVTAP:
return "virtio-net-pci"
case IPVTAP:
return "virtio-net-pci"
case VETHTAP:
return "virtio-net-pci" // -netdev type=tap -device virtio-net-pci
case VFIO:
return "vfio-pci" // -device vfio-pci (no netdev)
case VHOSTUSER:
return "" // -netdev type=vhost-user (no device)
default:
return ""
}
}
func isDimmSupported() bool {
return true
}

View File

@ -36,7 +36,7 @@ var (
deviceSCSIControllerBusAddrStr = "-device virtio-scsi-pci,id=foo,bus=pci.0,addr=00:04.0,disable-modern=true,iothread=iothread1,romfile=efi-virtio.rom"
deviceVhostUserSCSIString = "-chardev socket,id=char1,path=/tmp/nonexistentsocket.socket -device vhost-user-scsi-pci,id=scsi1,chardev=char1,romfile=efi-virtio.rom"
deviceVhostUserBlkString = "-chardev socket,id=char2,path=/tmp/nonexistentsocket.socket -device vhost-user-blk-pci,logical_block_size=4096,size=512M,chardev=char2,romfile=efi-virtio.rom"
deviceBlockString = "-device virtio-blk,disable-modern=true,drive=hd0,scsi=off,config-wce=off,romfile=efi-virtio.rom -drive id=hd0,file=/var/lib/vm.img,aio=threads,format=qcow2,if=none"
deviceBlockString = "-device virtio-blk-pci,disable-modern=true,drive=hd0,scsi=off,config-wce=off,romfile=efi-virtio.rom -drive id=hd0,file=/var/lib/vm.img,aio=threads,format=qcow2,if=none"
devicePCIBridgeString = "-device pci-bridge,bus=/pci-bus/pcie.0,id=mybridge,chassis_nr=5,shpc=on,addr=ff,romfile=efi-virtio.rom"
devicePCIEBridgeString = "-device pcie-pci-bridge,bus=/pci-bus/pcie.0,id=mybridge,addr=ff,romfile=efi-virtio.rom"
romfile = "efi-virtio.rom"
@ -81,7 +81,7 @@ func TestAppendVirtioBalloon(t *testing.T) {
ROMFile: romfile,
}
var deviceString = "-device " + string(VirtioBalloon)
var deviceString = "-device " + string(VirtioBalloon) + "-" + string(TransportPCI)
deviceString += ",id=" + balloonDevice.ID + ",romfile=" + balloonDevice.ROMFile
var OnDeflateOnOMM = ",deflate-on-oom=on"
@ -158,9 +158,6 @@ func TestAppendDeviceVFIOPCIe(t *testing.T) {
BDF: "02:00.0",
Bus: pcieRootPortID,
}
if isVirtioCCW[Vfio] {
vfioDevice.DevNo = DevNo
}
testAppend(vfioDevice, deviceVFIOPCIeSimpleString, t)
// full test
@ -172,8 +169,5 @@ func TestAppendDeviceVFIOPCIe(t *testing.T) {
VendorID: "0x10de",
DeviceID: "0x15f8",
}
if isVirtioCCW[Vfio] {
vfioDevice.DevNo = DevNo
}
testAppend(vfioDevice, deviceVFIOPCIeFullString, t)
}

View File

@ -1,140 +0,0 @@
// +build s390x
/*
// Copyright contributors to the Virtual Machine Manager for Go project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
package qemu
import "log"
// IBM Z uses CCW devices intead of PCI devices.
// See https://wiki.qemu.org/Documentation/Platforms/S390X
const (
// Virtio9P is the 9pfs device driver.
Virtio9P DeviceDriver = "virtio-9p-ccw"
// VirtioSerial is the serial device driver.
VirtioSerial DeviceDriver = "virtio-serial-ccw"
// VirtioNet is the virt-io ccw networking device driver.
VirtioNet DeviceDriver = VirtioNetCCW
// Vfio is the vfio driver
Vfio DeviceDriver = "vfio-ccw"
// VirtioScsi is the virtio-scsi device
VirtioScsi DeviceDriver = "virtio-scsi-ccw"
// VHostVSock is a generic Vsock Device
VHostVSock DeviceDriver = "vhost-vsock-ccw"
)
// isVirtioPCI is a fake map on s390x to always avoid the "romfile"
// option
var isVirtioPCI = map[DeviceDriver]bool{
NVDIMM: false,
Virtio9P: false,
VirtioNetCCW: false,
VirtioSerial: false,
VirtioBlock: false,
Console: false,
VirtioSerialPort: false,
VHostVSock: false,
VirtioRng: false,
VirtioBalloon: false,
VhostUserSCSI: false,
VhostUserBlk: false,
Vfio: false,
VirtioScsi: false,
PCIBridgeDriver: false,
PCIePCIBridgeDriver: false,
}
// isVirtioCCW returns if the device is a ccw device
var isVirtioCCW = map[DeviceDriver]bool{
NVDIMM: false,
Virtio9P: true,
VirtioNetCCW: true,
VirtioSerial: true,
VirtioBlock: true,
VirtioBlockCCW: true,
Console: false,
VirtioSerialPort: false,
VHostVSock: true,
VirtioRng: true,
VirtioBalloon: true,
VhostUserSCSI: false,
VhostUserBlk: false,
Vfio: true,
VirtioScsi: true,
PCIBridgeDriver: false,
PCIePCIBridgeDriver: false,
}
// QemuDeviceParam converts to the QEMU -device parameter notation
// This function has been reimplemented for the s390x architecture to deal
// with the VHOSTUSER case. Vhost user devices are not implemented on s390x
// architecture. For further details see issue
// https://github.com/kata-containers/runtime/issues/659
func (n NetDeviceType) QemuDeviceParam() string {
switch n {
case TAP:
return string(VirtioNet)
case MACVTAP:
return string(VirtioNet)
case IPVTAP:
return string(VirtioNet)
case VETHTAP:
return string(VirtioNet)
case VFIO:
return string(Vfio)
case VHOSTUSER:
log.Fatal("vhost-user devices are not supported on IBM Z")
return ""
default:
return ""
}
}
// QemuNetdevParam converts to the QEMU -netdev parameter notation
// This function has been reimplemented for the s390x architecture to deal
// with the VHOSTUSER case. Vhost user devices are not implemented on s390x
// architecture. For further details see issue
// https://github.com/kata-containers/runtime/issues/659
func (n NetDeviceType) QemuNetdevParam() string {
switch n {
case TAP:
return "tap"
case MACVTAP:
return "tap"
case IPVTAP:
return "tap"
case VETHTAP:
return "tap"
case VFIO:
return ""
case VHOSTUSER:
log.Fatal("vhost-user devices are not supported on IBM Z")
return ""
default:
return ""
}
}
func isDimmSupported() bool {
return false
}

View File

@ -31,7 +31,7 @@ var (
deviceVFIOString = "-device vfio-ccw,host=02:10.0,devno=" + DevNo
deviceSCSIControllerStr = "-device virtio-scsi-ccw,id=foo,devno=" + DevNo
deviceSCSIControllerBusAddrStr = "-device virtio-scsi-ccw,id=foo,bus=pci.0,addr=00:04.0,iothread=iothread1,devno=" + DevNo
deviceBlockString = "-device virtio-blk,drive=hd0,scsi=off,config-wce=off,devno=" + DevNo + " -drive id=hd0,file=/var/lib/vm.img,aio=threads,format=qcow2,if=none"
deviceBlockString = "-device virtio-blk-ccw,drive=hd0,scsi=off,config-wce=off,devno=" + DevNo + " -drive id=hd0,file=/var/lib/vm.img,aio=threads,format=qcow2,if=none"
devicePCIBridgeString = "-device pci-bridge,bus=/pci-bus/pcie.0,id=mybridge,chassis_nr=5,shpc=on,addr=ff"
devicePCIEBridgeString = "-device pcie-pci-bridge,bus=/pci-bus/pcie.0,id=mybridge,addr=ff"
romfile = ""
@ -42,7 +42,7 @@ func TestAppendVirtioBalloon(t *testing.T) {
ID: "balloon",
}
var deviceString = "-device " + string(VirtioBalloon)
var deviceString = "-device " + string(VirtioBalloon) + "-" + string(TransportCCW)
deviceString += ",id=" + balloonDevice.ID
balloonDevice.DevNo = DevNo
devnoOptios := ",devno=" + DevNo

View File

@ -143,7 +143,7 @@ func TestAppendDeviceFS(t *testing.T) {
ROMFile: "efi-virtio.rom",
}
if isVirtioCCW[fsdev.Driver] {
if fsdev.Transport.isVirtioCCW(nil) {
fsdev.DevNo = DevNo
}
@ -164,7 +164,7 @@ func TestAppendDeviceNetwork(t *testing.T) {
ROMFile: "efi-virtio.rom",
}
if isVirtioCCW[netdev.Driver] {
if netdev.Transport.isVirtioCCW(nil) {
netdev.DevNo = DevNo
}
@ -195,7 +195,7 @@ func TestAppendDeviceNetworkMq(t *testing.T) {
DisableModern: true,
ROMFile: "efi-virtio.rom",
}
if isVirtioCCW[netdev.Driver] {
if netdev.Transport.isVirtioCCW(nil) {
netdev.DevNo = DevNo
}
@ -219,7 +219,7 @@ func TestAppendDeviceNetworkPCI(t *testing.T) {
ROMFile: romfile,
}
if !isVirtioPCI[netdev.Driver] {
if !netdev.Transport.isVirtioPCI(nil) {
t.Skip("Test valid only for PCI devices")
}
@ -253,7 +253,7 @@ func TestAppendDeviceNetworkPCIMq(t *testing.T) {
ROMFile: romfile,
}
if !isVirtioPCI[netdev.Driver] {
if !netdev.Transport.isVirtioPCI(nil) {
t.Skip("Test valid only for PCI devices")
}
@ -267,7 +267,7 @@ func TestAppendDeviceSerial(t *testing.T) {
DisableModern: true,
ROMFile: romfile,
}
if isVirtioCCW[sdev.Driver] {
if sdev.Transport.isVirtioCCW(nil) {
sdev.DevNo = DevNo
}
@ -285,7 +285,7 @@ func TestAppendDeviceSerialPort(t *testing.T) {
Path: "/tmp/char.sock",
Name: "channel.0",
}
if isVirtioCCW[chardev.Driver] {
if chardev.Transport.isVirtioCCW(nil) {
chardev.DevNo = DevNo
}
testAppend(chardev, deviceSerialPortString, t)
@ -304,7 +304,7 @@ func TestAppendDeviceBlock(t *testing.T) {
DisableModern: true,
ROMFile: romfile,
}
if isVirtioCCW[blkdev.Driver] {
if blkdev.Transport.isVirtioCCW(nil) {
blkdev.DevNo = DevNo
}
testAppend(blkdev, deviceBlockString, t)
@ -318,7 +318,7 @@ func TestAppendDeviceVFIO(t *testing.T) {
DeviceID: "0x5678",
}
if isVirtioCCW[Vfio] {
if vfioDevice.Transport.isVirtioCCW(nil) {
vfioDevice.DevNo = DevNo
}
@ -334,7 +334,7 @@ func TestAppendVSOCK(t *testing.T) {
ROMFile: romfile,
}
if isVirtioCCW[VHostVSock] {
if vsockDevice.Transport.isVirtioCCW(nil) {
vsockDevice.DevNo = DevNo
}
@ -368,16 +368,19 @@ func TestVSOCKValid(t *testing.T) {
func TestAppendVirtioRng(t *testing.T) {
var objectString = "-object rng-random,id=rng0"
var deviceString = "-device " + string(VirtioRng) + ",rng=rng0"
if romfile != "" {
deviceString = deviceString + ",romfile=efi-virtio.rom"
}
var deviceString = "-device " + string(VirtioRng)
rngDevice := RngDevice{
ID: "rng0",
ROMFile: romfile,
}
if isVirtioCCW[VirtioRng] {
deviceString += "-" + string(rngDevice.Transport.getName(nil)) + ",rng=rng0"
if romfile != "" {
deviceString = deviceString + ",romfile=efi-virtio.rom"
}
if rngDevice.Transport.isVirtioCCW(nil) {
rngDevice.DevNo = DevNo
deviceString += ",devno=" + rngDevice.DevNo
}
@ -438,7 +441,7 @@ func TestAppendDeviceSCSIController(t *testing.T) {
ROMFile: romfile,
}
if isVirtioCCW[VirtioScsi] {
if scsiCon.Transport.isVirtioCCW(nil) {
scsiCon.DevNo = DevNo
}
@ -521,7 +524,7 @@ func TestAppendKnobsAllFalse(t *testing.T) {
}
func TestAppendMemoryHugePages(t *testing.T) {
if !isDimmSupported() {
if !isDimmSupported(nil) {
t.Skip("Dimm not supported")
}
@ -549,7 +552,7 @@ func TestAppendMemoryHugePages(t *testing.T) {
}
func TestAppendMemoryMemPrealloc(t *testing.T) {
if !isDimmSupported() {
if !isDimmSupported(nil) {
t.Skip("Dimm not supported")
}
@ -575,7 +578,7 @@ func TestAppendMemoryMemPrealloc(t *testing.T) {
}
func TestAppendMemoryMemShared(t *testing.T) {
if !isDimmSupported() {
if !isDimmSupported(nil) {
t.Skip("Dimm not supported")
}
@ -601,7 +604,7 @@ func TestAppendMemoryMemShared(t *testing.T) {
}
func TestAppendMemoryFileBackedMem(t *testing.T) {
if !isDimmSupported() {
if !isDimmSupported(nil) {
t.Skip("Dimm not supported")
}
@ -627,7 +630,7 @@ func TestAppendMemoryFileBackedMem(t *testing.T) {
}
func TestAppendMemoryFileBackedMemPrealloc(t *testing.T) {
if !isDimmSupported() {
if !isDimmSupported(nil) {
t.Skip("Dimm not supported")
}

View File

@ -846,7 +846,9 @@ func (q *QMP) ExecuteDeviceAdd(ctx context.Context, blockdevID, devID, driver, b
"drive": blockdevID,
}
if isVirtioCCW[DeviceDriver(driver)] {
var transport VirtioTransport
if transport.isVirtioCCW(nil) {
args["devno"] = bus
} else if bus != "" {
args["bus"] = bus
@ -855,7 +857,7 @@ func (q *QMP) ExecuteDeviceAdd(ctx context.Context, blockdevID, devID, driver, b
if shared && (q.version.Major > 2 || (q.version.Major == 2 && q.version.Minor >= 10)) {
args["share-rw"] = "on"
}
if isVirtioPCI[DeviceDriver(driver)] {
if transport.isVirtioPCI(nil) {
args["romfile"] = romfile
if disableModern {
@ -897,12 +899,7 @@ func (q *QMP) ExecuteSCSIDeviceAdd(ctx context.Context, blockdevID, devID, drive
"id": devID,
"driver": driver,
"drive": blockdevID,
}
if isVirtioCCW[DeviceDriver(driver)] {
args["devno"] = bus
} else {
args["bus"] = bus
"bus": bus,
}
if scsiID >= 0 {
@ -914,13 +911,6 @@ func (q *QMP) ExecuteSCSIDeviceAdd(ctx context.Context, blockdevID, devID, drive
if shared && (q.version.Major > 2 || (q.version.Major == 2 && q.version.Minor >= 10)) {
args["share-rw"] = "on"
}
if isVirtioPCI[DeviceDriver(driver)] {
args["romfile"] = romfile
if disableModern {
args["disable-modern"] = disableModern
}
}
return q.executeCommand(ctx, "device_add", args, nil)
}
@ -1124,7 +1114,10 @@ func (q *QMP) ExecutePCIDeviceAdd(ctx context.Context, blockdevID, devID, driver
if queues > 0 {
args["num-queues"] = strconv.Itoa(queues)
}
if isVirtioPCI[DeviceDriver(driver)] {
var transport VirtioTransport
if transport.isVirtioPCI(nil) {
args["romfile"] = romfile
if disableModern {
@ -1161,9 +1154,18 @@ func (q *QMP) ExecutePCIVhostUserDevAdd(ctx context.Context, driver, devID, char
// bdf is the PCI bus-device-function of the pci device.
// bus is optional. When hot plugging a PCIe device, the bus can be the ID of the pcie-root-port.
func (q *QMP) ExecuteVFIODeviceAdd(ctx context.Context, devID, bdf, bus, romfile string) error {
var driver string
var transport VirtioTransport
if transport.isVirtioCCW(nil) {
driver = string(VfioCCW)
} else {
driver = string(VfioPCI)
}
args := map[string]interface{}{
"id": devID,
"driver": Vfio,
"driver": driver,
"host": bdf,
"romfile": romfile,
}
@ -1181,7 +1183,7 @@ func (q *QMP) ExecuteVFIODeviceAdd(ctx context.Context, devID, bdf, bus, romfile
func (q *QMP) ExecutePCIVFIODeviceAdd(ctx context.Context, devID, bdf, addr, bus, romfile string) error {
args := map[string]interface{}{
"id": devID,
"driver": Vfio,
"driver": VfioPCI,
"host": bdf,
"addr": addr,
"romfile": romfile,
@ -1201,7 +1203,7 @@ func (q *QMP) ExecutePCIVFIODeviceAdd(ctx context.Context, devID, bdf, addr, bus
func (q *QMP) ExecutePCIVFIOMediatedDeviceAdd(ctx context.Context, devID, sysfsdev, addr, bus, romfile string) error {
args := map[string]interface{}{
"id": devID,
"driver": Vfio,
"driver": VfioPCI,
"sysfsdev": sysfsdev,
"romfile": romfile,
}
@ -1265,10 +1267,6 @@ func (q *QMP) ExecuteCPUDeviceAdd(ctx context.Context, driver, cpuID, socketID,
}
}
if isVirtioPCI[DeviceDriver(driver)] {
args["romfile"] = romfile
}
return q.executeCommand(ctx, "device_add", args, nil)
}
@ -1477,7 +1475,7 @@ func (q *QMP) ExecuteBalloon(ctx context.Context, bytes uint64) error {
// 1.0 in nested environments.
func (q *QMP) ExecutePCIVSockAdd(ctx context.Context, id, guestCID, vhostfd, addr, bus, romfile string, disableModern bool) error {
args := map[string]interface{}{
"driver": VHostVSock,
"driver": VHostVSockPCI,
"id": id,
"guest-cid": guestCID,
"vhostfd": vhostfd,