diff --git a/Gopkg.lock b/Gopkg.lock index 1f6eaa743..9827044b2 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -238,11 +238,11 @@ revision = "3520598351bb3500a49ae9563f5539666ae0a27c" [[projects]] - digest = "1:6cfbc8646c1b7746e366eacb17a6f6f97f28beabd87eebffe961447419eb60bf" + digest = "1:40227c1b7841f35c5965b955b21cc84f3990b9b972d3224e5e31ba20a3dc1f37" name = "github.com/intel/govmm" packages = ["qemu"] pruneopts = "NUT" - revision = "e82e8498c5a214b24ac75e0a05ace556bf91a9ab" + revision = "32f64a0630c7602d536be2a9c83bc3eee160359b" [[projects]] digest = "1:e96806ae1b041a36386249b22ef9eaf5af1788c0f86f686c6296e5a9caf53df8" diff --git a/Gopkg.toml b/Gopkg.toml index 10115f9a5..2a91d0c54 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -52,7 +52,7 @@ [[constraint]] name = "github.com/intel/govmm" - revision = "e82e8498c5a214b24ac75e0a05ace556bf91a9ab" + revision = "32f64a0630c7602d536be2a9c83bc3eee160359b" [[constraint]] name = "github.com/kata-containers/agent" diff --git a/vendor/github.com/intel/govmm/CONTRIBUTORS.md b/vendor/github.com/intel/govmm/CONTRIBUTORS.md new file mode 100644 index 000000000..c159b9a1f --- /dev/null +++ b/vendor/github.com/intel/govmm/CONTRIBUTORS.md @@ -0,0 +1,21 @@ +This file is a partial list of contributors to the Virtual Machine +Manager for Go project. To see the full list of contributors, see the +revision history in source control. + +Contributors who wish to be recognized in this file should add +themselves (or their employer, as appropriate). + +- afrosi@de.ibm.com +- archana.m.shinde@intel.com +- caoruidong@huawei.com +- clare.chenhui@huawei.com +- eric.ernst@intel.com +- james.o.hunt@intel.com +- jose.carlos.venegas.munoz@intel.com +- julio.montes@intel.com +- manohar.r.castelino@intel.com +- mark.d.ryan@intel.com +- robert.bradford@intel.com +- sameo@linux.intel.com +- sebastien.boeuf@intel.com +- xinda.zhao@intel.com diff --git a/vendor/github.com/intel/govmm/qemu/image.go b/vendor/github.com/intel/govmm/qemu/image.go index b2f906f7c..de12333c4 100644 --- a/vendor/github.com/intel/govmm/qemu/image.go +++ b/vendor/github.com/intel/govmm/qemu/image.go @@ -1,5 +1,5 @@ /* -// Copyright (c) 2017 Intel Corporation +// 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. diff --git a/vendor/github.com/intel/govmm/qemu/qemu.go b/vendor/github.com/intel/govmm/qemu/qemu.go index f26f09392..8bd6afbef 100644 --- a/vendor/github.com/intel/govmm/qemu/qemu.go +++ b/vendor/github.com/intel/govmm/qemu/qemu.go @@ -1,5 +1,5 @@ /* -// Copyright (c) 2016 Intel Corporation +// 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. @@ -62,17 +62,11 @@ const ( // NVDIMM is the Non Volatile DIMM device driver. NVDIMM DeviceDriver = "nvdimm" - // Virtio9P is the 9pfs device driver. - Virtio9P DeviceDriver = "virtio-9p-pci" - - // VirtioNet is the virt-io networking device driver. - VirtioNet DeviceDriver = "virtio-net" - // VirtioNetPCI is the virt-io pci networking device driver. VirtioNetPCI DeviceDriver = "virtio-net-pci" - // VirtioSerial is the serial device driver. - VirtioSerial DeviceDriver = "virtio-serial-pci" + // VirtioNetCCW is the virt-io ccw networking device driver. + VirtioNetCCW DeviceDriver = "virtio-net-ccw" // VirtioBlock is the block device driver. VirtioBlock DeviceDriver = "virtio-blk" @@ -83,9 +77,6 @@ const ( // VirtioSerialPort is the serial port device driver. VirtioSerialPort DeviceDriver = "virtserialport" - // VHostVSockPCI is the vhost vsock pci driver. - VHostVSockPCI DeviceDriver = "vhost-vsock-pci" - // VirtioRng is the paravirtualized RNG device driver. VirtioRng DeviceDriver = "virtio-rng" @@ -101,12 +92,6 @@ const ( //VhostUserBlk represents a block vhostuser device type. VhostUserBlk DeviceDriver = "vhost-user-blk-pci" - // VfioPCI represent a VFIO device type. - VfioPCI DeviceDriver = "vfio-pci" - - // VirtioScsiPCI represents a SCSI device type. - VirtioScsiPCI DeviceDriver = "virtio-scsi-pci" - // PCIBridgeDriver represents a PCI bridge device type. PCIBridgeDriver DeviceDriver = "pci-bridge" @@ -114,27 +99,19 @@ const ( PCIePCIBridgeDriver DeviceDriver = "pcie-pci-bridge" ) -// 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, - VirtioNet: true, - VirtioNetPCI: true, - VirtioSerial: true, - VirtioBlock: true, - Console: false, - VirtioSerialPort: false, - VHostVSockPCI: true, - VirtioRng: true, - VirtioBalloon: true, - VhostUserSCSI: true, - VhostUserBlk: true, - VfioPCI: true, - VirtioScsiPCI: true, - PCIBridgeDriver: true, - PCIePCIBridgeDriver: true, +// disableModern returns the parameters with the disable-modern option. +// In case the device driver is not a PCI device and it doesn't have the option +// an empty string is returned. +func (driver DeviceDriver) disableModern(disable bool) string { + if !isVirtioPCI[driver] { + return "" + } + + if disable { + return "disable-modern=true" + } + + return "disable-modern=false" } // ObjectType is a string representing a qemu object type. @@ -284,8 +261,8 @@ func (fsdev FSDevice) QemuParams(config *Config) []string { var qemuParams []string deviceParams = append(deviceParams, fmt.Sprintf("%s", fsdev.Driver)) - if fsdev.DisableModern { - deviceParams = append(deviceParams, ",disable-modern=true") + if s := fsdev.Driver.disableModern(fsdev.DisableModern); s != "" { + deviceParams = append(deviceParams, fmt.Sprintf(",%s", s)) } deviceParams = append(deviceParams, fmt.Sprintf(",fsdev=%s", fsdev.ID)) deviceParams = append(deviceParams, fmt.Sprintf(",mount_tag=%s", fsdev.MountTag)) @@ -370,8 +347,8 @@ func (cdev CharDevice) QemuParams(config *Config) []string { var qemuParams []string deviceParams = append(deviceParams, fmt.Sprintf("%s", cdev.Driver)) - if cdev.DisableModern { - deviceParams = append(deviceParams, ",disable-modern=true") + if s := cdev.Driver.disableModern(cdev.DisableModern); s != "" { + deviceParams = append(deviceParams, fmt.Sprintf(",%s", s)) } if cdev.Bus != "" { deviceParams = append(deviceParams, fmt.Sprintf(",bus=%s", cdev.Bus)) @@ -425,48 +402,6 @@ const ( VHOSTUSER NetDeviceType = "vhostuser" ) -// 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 "" - - } -} - // NetDevice represents a guest networking device type NetDevice struct { // Type is the netdev type (e.g. tap). @@ -527,6 +462,29 @@ func (netdev NetDevice) Valid() bool { } } +// mqParameter returns the parameters for multi-queue driver. If the driver is a PCI device then the +// vector flag is required. If the driver is a CCW type than the vector flag is not implemented and only +// multi-queue option mq needs to be activated. See comment in libvirt code at +// https://github.com/libvirt/libvirt/blob/6e7e965dcd3d885739129b1454ce19e819b54c25/src/qemu/qemu_command.c#L3633 +func (netdev NetDevice) mqParameter() string { + p := []string{",mq=on"} + + if isVirtioPCI[netdev.Driver] { + // https://www.linux-kvm.org/page/Multiqueue + // -netdev tap,vhost=on,queues=N + // enable mq and specify msix vectors in qemu cmdline + // (2N+2 vectors, N for tx queues, N for rx queues, 1 for config, and one for possible control vq) + // -device virtio-net-pci,mq=on,vectors=2N+2... + // enable mq in guest by 'ethtool -L eth0 combined $queue_num' + // Clearlinux automatically sets up the queues properly + // The agent implementation should do this to ensure that it is + // always set + vectors := len(netdev.FDs)*2 + 2 + p = append(p, fmt.Sprintf(",vectors=%d", vectors)) + } + return strings.Join(p, "") +} + // QemuDeviceParams returns the -device parameters for this network device func (netdev NetDevice) QemuDeviceParams(config *Config) []string { var deviceParams []string @@ -549,26 +507,13 @@ func (netdev NetDevice) QemuDeviceParams(config *Config) []string { deviceParams = append(deviceParams, fmt.Sprintf(",addr=%x", addr)) } } - - if netdev.DisableModern { - deviceParams = append(deviceParams, ",disable-modern=true") + if s := netdev.Driver.disableModern(netdev.DisableModern); s != "" { + deviceParams = append(deviceParams, fmt.Sprintf(",%s", s)) } if len(netdev.FDs) > 0 { - // https://www.linux-kvm.org/page/Multiqueue - // -netdev tap,vhost=on,queues=N - // enable mq and specify msix vectors in qemu cmdline - // (2N+2 vectors, N for tx queues, N for rx queues, 1 for config, and one for possible control vq) - // -device virtio-net-pci,mq=on,vectors=2N+2... - // enable mq in guest by 'ethtool -L eth0 combined $queue_num' - // Clearlinux automatically sets up the queues properly - // The agent implementation should do this to ensure that it is - // always set - vectors := len(netdev.FDs)*2 + 2 - // Note: We are appending to the device params here - deviceParams = append(deviceParams, ",mq=on") - deviceParams = append(deviceParams, fmt.Sprintf(",vectors=%d", vectors)) + deviceParams = append(deviceParams, netdev.mqParameter()) } if isVirtioPCI[netdev.Driver] { @@ -683,8 +628,8 @@ func (dev SerialDevice) QemuParams(config *Config) []string { var qemuParams []string deviceParams = append(deviceParams, fmt.Sprintf("%s", dev.Driver)) - if dev.DisableModern { - deviceParams = append(deviceParams, ",disable-modern=true") + if s := dev.Driver.disableModern(dev.DisableModern); s != "" { + deviceParams = append(deviceParams, fmt.Sprintf(",%s", s)) } deviceParams = append(deviceParams, fmt.Sprintf(",id=%s", dev.ID)) if isVirtioPCI[dev.Driver] { @@ -761,8 +706,8 @@ func (blkdev BlockDevice) QemuParams(config *Config) []string { var qemuParams []string deviceParams = append(deviceParams, fmt.Sprintf("%s", blkdev.Driver)) - if blkdev.DisableModern { - deviceParams = append(deviceParams, ",disable-modern=true") + if s := blkdev.Driver.disableModern(blkdev.DisableModern); s != "" { + deviceParams = append(deviceParams, fmt.Sprintf(",%s", s)) } deviceParams = append(deviceParams, fmt.Sprintf(",drive=%s", blkdev.ID)) if blkdev.SCSI == false { @@ -909,7 +854,7 @@ func (vfioDev VFIODevice) QemuParams(config *Config) []string { var qemuParams []string var deviceParams []string - driver := VfioPCI + driver := Vfio deviceParams = append(deviceParams, fmt.Sprintf("%s,host=%s", driver, vfioDev.BDF)) if isVirtioPCI[driver] { @@ -956,7 +901,7 @@ func (scsiCon SCSIController) QemuParams(config *Config) []string { var qemuParams []string var devParams []string - driver := VirtioScsiPCI + driver := VirtioScsi devParams = append(devParams, fmt.Sprintf("%s,id=%s", driver, scsiCon.ID)) if scsiCon.Bus != "" { devParams = append(devParams, fmt.Sprintf("bus=%s", scsiCon.Bus)) @@ -964,8 +909,8 @@ func (scsiCon SCSIController) QemuParams(config *Config) []string { if scsiCon.Addr != "" { devParams = append(devParams, fmt.Sprintf("addr=%s", scsiCon.Addr)) } - if scsiCon.DisableModern { - devParams = append(devParams, fmt.Sprintf("disable-modern=true")) + if s := driver.disableModern(scsiCon.DisableModern); s != "" { + devParams = append(devParams, fmt.Sprintf("%s", s)) } if scsiCon.IOThread != "" { devParams = append(devParams, fmt.Sprintf("iothread=%s", scsiCon.IOThread)) @@ -1072,7 +1017,7 @@ func (bridgeDev BridgeDevice) QemuParams(config *Config) []string { type VSOCKDevice struct { ID string - ContextID uint32 + ContextID uint64 // VHostFD vhost file descriptor that holds the ContextID VHostFD *os.File @@ -1086,15 +1031,20 @@ type VSOCKDevice struct { const ( // MinimalGuestCID is the smallest valid context ID for a guest. - MinimalGuestCID uint32 = 3 + MinimalGuestCID uint64 = 3 + // MaxGuestCID is the largest valid context ID for a guest. + MaxGuestCID uint64 = 1<<32 - 1 +) + +const ( // VSOCKGuestCID is the VSOCK guest CID parameter. VSOCKGuestCID = "guest-cid" ) // Valid returns true if the VSOCKDevice structure is valid and complete. func (vsock VSOCKDevice) Valid() bool { - if vsock.ID == "" || vsock.ContextID < MinimalGuestCID { + if vsock.ID == "" || vsock.ContextID < MinimalGuestCID || vsock.ContextID > MaxGuestCID { return false } @@ -1106,10 +1056,10 @@ func (vsock VSOCKDevice) QemuParams(config *Config) []string { var deviceParams []string var qemuParams []string - driver := VHostVSockPCI + driver := VHostVSock deviceParams = append(deviceParams, fmt.Sprintf("%s", driver)) - if vsock.DisableModern { - deviceParams = append(deviceParams, ",disable-modern=true") + if s := driver.disableModern(vsock.DisableModern); s != "" { + deviceParams = append(deviceParams, fmt.Sprintf(",%s", s)) } if vsock.VHostFD != nil { qemuFDs := config.appendFDs([]*os.File{vsock.VHostFD}) @@ -1223,13 +1173,9 @@ func (b BalloonDevice) QemuParams(_ *Config) []string { } else { deviceParams = append(deviceParams, "deflate-on-oom=off") } - - if b.DisableModern { - deviceParams = append(deviceParams, "disable-modern=on") - } else { - deviceParams = append(deviceParams, "disable-modern=off") + if s := driver.disableModern(b.DisableModern); s != "" { + deviceParams = append(deviceParams, fmt.Sprintf("%s", s)) } - qemuParams = append(qemuParams, "-device") qemuParams = append(qemuParams, strings.Join(deviceParams, ",")) @@ -1516,6 +1462,9 @@ type Config struct { IOThreads []IOThread + // PidFile is the -pidfile parameter + PidFile string + qemuParams []string } @@ -1830,6 +1779,13 @@ func (config *Config) appendIncoming() { config.qemuParams = append(config.qemuParams, "-S", "-incoming", uri) } +func (config *Config) appendPidFile() { + if config.PidFile != "" { + config.qemuParams = append(config.qemuParams, "-pidfile") + config.qemuParams = append(config.qemuParams, config.PidFile) + } +} + // LaunchQemu can be used to launch a new qemu instance. // // The Config parameter contains a set of qemu parameters and settings. @@ -1855,6 +1811,7 @@ func LaunchQemu(config Config, logger QMPLog) (string, error) { config.appendBios() config.appendIOThreads() config.appendIncoming() + config.appendPidFile() if err := config.appendCPUs(); err != nil { return "", err diff --git a/vendor/github.com/intel/govmm/qemu/qemu_arch_base.go b/vendor/github.com/intel/govmm/qemu/qemu_arch_base.go new file mode 100644 index 000000000..521741fa6 --- /dev/null +++ b/vendor/github.com/intel/govmm/qemu/qemu_arch_base.go @@ -0,0 +1,103 @@ +// +build !s390x,!s390x_test + +/* +// 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, + Console: false, + VirtioSerialPort: false, + VHostVSock: true, + VirtioRng: true, + VirtioBalloon: true, + VhostUserSCSI: true, + VhostUserBlk: true, + Vfio: true, + VirtioScsi: true, + PCIBridgeDriver: true, + PCIePCIBridgeDriver: true, +} + +// 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 "" + + } +} diff --git a/vendor/github.com/intel/govmm/qemu/qemus390x.go b/vendor/github.com/intel/govmm/qemu/qemus390x.go new file mode 100644 index 000000000..7c45c3edd --- /dev/null +++ b/vendor/github.com/intel/govmm/qemu/qemus390x.go @@ -0,0 +1,115 @@ +// +build s390x s390x_test + +/* +// 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, +} + +// 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 "" + + } +} diff --git a/vendor/github.com/intel/govmm/qemu/qmp.go b/vendor/github.com/intel/govmm/qemu/qmp.go index 43daed369..65557b7fa 100644 --- a/vendor/github.com/intel/govmm/qemu/qmp.go +++ b/vendor/github.com/intel/govmm/qemu/qmp.go @@ -1,5 +1,5 @@ /* -// Copyright (c) 2016 Intel Corporation +// 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. @@ -313,7 +313,7 @@ func (q *QMP) finaliseCommandWithResponse(cmdEl *list.Element, cmdQueue *list.Li if succeeded { cmd.res <- qmpResult{response: response} } else { - cmd.res <- qmpResult{err: fmt.Errorf("QMP command failed")} + cmd.res <- qmpResult{err: fmt.Errorf("QMP command failed: %v", response)} } } if cmdQueue.Len() > 0 { @@ -325,6 +325,23 @@ func (q *QMP) finaliseCommand(cmdEl *list.Element, cmdQueue *list.List, succeede q.finaliseCommandWithResponse(cmdEl, cmdQueue, succeeded, nil) } +func (q *QMP) errorDesc(errorData interface{}) (string, error) { + // convert error to json + data, err := json.Marshal(errorData) + if err != nil { + return "", fmt.Errorf("Unable to extract error information: %v", err) + } + + // see: https://github.com/qemu/qemu/blob/stable-2.12/qapi/qmp-dispatch.c#L125 + var qmpErr map[string]string + // convert json to qmpError + if err = json.Unmarshal(data, &qmpErr); err != nil { + return "", fmt.Errorf("Unable to convert json to qmpError: %v", err) + } + + return qmpErr["desc"], nil +} + func (q *QMP) processQMPInput(line []byte, cmdQueue *list.List) { var vmData map[string]interface{} err := json.Unmarshal(line, &vmData) @@ -339,7 +356,7 @@ func (q *QMP) processQMPInput(line []byte, cmdQueue *list.List) { } response, succeeded := vmData["return"] - _, failed := vmData["error"] + errData, failed := vmData["error"] if !succeeded && !failed { return @@ -353,6 +370,14 @@ func (q *QMP) processQMPInput(line []byte, cmdQueue *list.List) { } cmd := cmdEl.Value.(*qmpCommand) if failed || cmd.filter == nil { + if errData != nil { + desc, err := q.errorDesc(errData) + if err != nil { + q.cfg.Logger.Infof("Get error description failed: %v", err) + } else { + response = desc + } + } q.finaliseCommandWithResponse(cmdEl, cmdQueue, succeeded, response) } else { cmd.resultReceived = true @@ -722,11 +747,7 @@ func (q *QMP) ExecuteQuit(ctx context.Context) error { return q.executeCommand(ctx, "quit", nil, nil) } -// ExecuteBlockdevAdd sends a blockdev-add to the QEMU instance. device is the -// path of the device to add, e.g., /dev/rdb0, and blockdevID is an identifier -// used to name the device. As this identifier will be passed directly to QMP, -// it must obey QMP's naming rules, e,g., it must start with a letter. -func (q *QMP) ExecuteBlockdevAdd(ctx context.Context, device, blockdevID string) error { +func (q *QMP) blockdevAddBaseArgs(device, blockdevID string) (map[string]interface{}, map[string]interface{}) { var args map[string]interface{} blockdevArgs := map[string]interface{}{ @@ -747,6 +768,39 @@ func (q *QMP) ExecuteBlockdevAdd(ctx context.Context, device, blockdevID string) } } + return args, blockdevArgs +} + +// ExecuteBlockdevAdd sends a blockdev-add to the QEMU instance. device is the +// path of the device to add, e.g., /dev/rdb0, and blockdevID is an identifier +// used to name the device. As this identifier will be passed directly to QMP, +// it must obey QMP's naming rules, e,g., it must start with a letter. +func (q *QMP) ExecuteBlockdevAdd(ctx context.Context, device, blockdevID string) error { + args, _ := q.blockdevAddBaseArgs(device, blockdevID) + + return q.executeCommand(ctx, "blockdev-add", args, nil) +} + +// ExecuteBlockdevAddWithCache has two more parameters direct and noFlush +// than ExecuteBlockdevAdd. +// They are cache-related options for block devices that are described in +// https://github.com/qemu/qemu/blob/master/qapi/block-core.json. +// direct denotes whether use of O_DIRECT (bypass the host page cache) +// is enabled. noFlush denotes whether flush requests for the device are +// ignored. +func (q *QMP) ExecuteBlockdevAddWithCache(ctx context.Context, device, blockdevID string, direct, noFlush bool) error { + args, blockdevArgs := q.blockdevAddBaseArgs(device, blockdevID) + + if q.version.Major < 2 || (q.version.Major == 2 && q.version.Minor < 9) { + return fmt.Errorf("versions of qemu (%d.%d) older than 2.9 do not support set cache-related options for block devices", + q.version.Major, q.version.Minor) + } + + blockdevArgs["cache"] = map[string]interface{}{ + "direct": direct, + "no-flush": noFlush, + } + return q.executeCommand(ctx, "blockdev-add", args, nil) } @@ -756,7 +810,10 @@ func (q *QMP) ExecuteBlockdevAdd(ctx context.Context, device, blockdevID string) // add. Both strings must be valid QMP identifiers. driver is the name of the // driver,e.g., virtio-blk-pci, and bus is the name of the bus. bus is optional. // shared denotes if the drive can be shared allowing it to be passed more than once. -func (q *QMP) ExecuteDeviceAdd(ctx context.Context, blockdevID, devID, driver, bus, romfile string, shared bool) error { +// disableModern indicates if virtio version 1.0 should be replaced by the +// former version 0.9, as there is a KVM bug that occurs when using virtio +// 1.0 in nested environments. +func (q *QMP) ExecuteDeviceAdd(ctx context.Context, blockdevID, devID, driver, bus, romfile string, shared, disableModern bool) error { args := map[string]interface{}{ "id": devID, "driver": driver, @@ -770,6 +827,10 @@ func (q *QMP) ExecuteDeviceAdd(ctx context.Context, blockdevID, devID, driver, b } if isVirtioPCI[DeviceDriver(driver)] { args["romfile"] = romfile + + if disableModern { + args["disable-modern"] = disableModern + } } return q.executeCommand(ctx, "device_add", args, nil) @@ -783,7 +844,10 @@ func (q *QMP) ExecuteDeviceAdd(ctx context.Context, blockdevID, devID, driver, b // scsiID is the SCSI id, lun is logical unit number. scsiID and lun are optional, a negative value // for scsiID and lun is ignored. shared denotes if the drive can be shared allowing it // to be passed more than once. -func (q *QMP) ExecuteSCSIDeviceAdd(ctx context.Context, blockdevID, devID, driver, bus, romfile string, scsiID, lun int, shared bool) error { +// disableModern indicates if virtio version 1.0 should be replaced by the +// former version 0.9, as there is a KVM bug that occurs when using virtio +// 1.0 in nested environments. +func (q *QMP) ExecuteSCSIDeviceAdd(ctx context.Context, blockdevID, devID, driver, bus, romfile string, scsiID, lun int, shared, disableModern bool) error { // TBD: Add drivers for scsi passthrough like scsi-generic and scsi-block drivers := []string{"scsi-hd", "scsi-cd", "scsi-disk"} @@ -816,6 +880,10 @@ func (q *QMP) ExecuteSCSIDeviceAdd(ctx context.Context, blockdevID, devID, drive } if isVirtioPCI[DeviceDriver(driver)] { args["romfile"] = romfile + + if disableModern { + args["disable-modern"] = disableModern + } } return q.executeCommand(ctx, "device_add", args, nil) @@ -908,7 +976,10 @@ func (q *QMP) ExecuteNetdevDel(ctx context.Context, netdevID string) error { // using the device_add command. devID is the id of the device to add. // Must be valid QMP identifier. netdevID is the id of nic added by previous netdev_add. // queues is the number of queues of a nic. -func (q *QMP) ExecuteNetPCIDeviceAdd(ctx context.Context, netdevID, devID, macAddr, addr, bus, romfile string, queues int) error { +// disableModern indicates if virtio version 1.0 should be replaced by the +// former version 0.9, as there is a KVM bug that occurs when using virtio +// 1.0 in nested environments. +func (q *QMP) ExecuteNetPCIDeviceAdd(ctx context.Context, netdevID, devID, macAddr, addr, bus, romfile string, queues int, disableModern bool) error { args := map[string]interface{}{ "id": devID, "driver": VirtioNetPCI, @@ -927,6 +998,9 @@ func (q *QMP) ExecuteNetPCIDeviceAdd(ctx context.Context, netdevID, devID, macAd if netdevID != "" { args["netdev"] = netdevID } + if disableModern { + args["disable-modern"] = disableModern + } if queues > 0 { // (2N+2 vectors, N for tx queues, N for rx queues, 1 for config, and one for possible control vq) @@ -942,6 +1016,26 @@ func (q *QMP) ExecuteNetPCIDeviceAdd(ctx context.Context, netdevID, devID, macAd return q.executeCommand(ctx, "device_add", args, nil) } +// ExecuteNetCCWDeviceAdd adds a Net CCW device to a QEMU instance +// using the device_add command. devID is the id of the device to add. +// Must be valid QMP identifier. netdevID is the id of nic added by previous netdev_add. +// queues is the number of queues of a nic. +func (q *QMP) ExecuteNetCCWDeviceAdd(ctx context.Context, netdevID, devID, macAddr, addr, bus string, queues int) error { + args := map[string]interface{}{ + "id": devID, + "driver": VirtioNetCCW, + "netdev": netdevID, + "mac": macAddr, + "addr": addr, + } + + if queues > 0 { + args["mq"] = "on" + } + + return q.executeCommand(ctx, "device_add", args, nil) +} + // ExecuteDeviceDel deletes guest portion of a QEMU device by sending a // device_del command. devId is the identifier of the device to delete. // Typically it would match the devID parameter passed to an earlier call @@ -964,7 +1058,10 @@ func (q *QMP) ExecuteDeviceDel(ctx context.Context, devID string) error { // to hot plug PCI devices on PCI(E) bridges, unlike ExecuteDeviceAdd this function receive the // device address on its parent bus. bus is optional. shared denotes if the drive can be shared // allowing it to be passed more than once. -func (q *QMP) ExecutePCIDeviceAdd(ctx context.Context, blockdevID, devID, driver, addr, bus, romfile string, shared bool) error { +// disableModern indicates if virtio version 1.0 should be replaced by the +// former version 0.9, as there is a KVM bug that occurs when using virtio +// 1.0 in nested environments. +func (q *QMP) ExecutePCIDeviceAdd(ctx context.Context, blockdevID, devID, driver, addr, bus, romfile string, shared, disableModern bool) error { args := map[string]interface{}{ "id": devID, "driver": driver, @@ -979,6 +1076,10 @@ func (q *QMP) ExecutePCIDeviceAdd(ctx context.Context, blockdevID, devID, driver } if isVirtioPCI[DeviceDriver(driver)] { args["romfile"] = romfile + + if disableModern { + args["disable-modern"] = disableModern + } } return q.executeCommand(ctx, "device_add", args, nil) @@ -991,7 +1092,7 @@ func (q *QMP) ExecutePCIDeviceAdd(ctx context.Context, blockdevID, devID, driver func (q *QMP) ExecuteVFIODeviceAdd(ctx context.Context, devID, bdf, romfile string) error { args := map[string]interface{}{ "id": devID, - "driver": "vfio-pci", + "driver": Vfio, "host": bdf, "romfile": romfile, } @@ -1006,11 +1107,12 @@ func (q *QMP) ExecuteVFIODeviceAdd(ctx context.Context, devID, bdf, romfile stri func (q *QMP) ExecutePCIVFIODeviceAdd(ctx context.Context, devID, bdf, addr, bus, romfile string) error { args := map[string]interface{}{ "id": devID, - "driver": "vfio-pci", + "driver": Vfio, "host": bdf, "addr": addr, "romfile": romfile, } + if bus != "" { args["bus"] = bus } @@ -1025,10 +1127,11 @@ 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-pci", + "driver": Vfio, "sysfsdev": sysfsdev, "romfile": romfile, } + if bus != "" { args["bus"] = bus } @@ -1170,13 +1273,14 @@ func (q *QMP) ExecQueryCpusFast(ctx context.Context) ([]CPUInfoFast, error) { // ExecHotplugMemory adds size of MiB memory to the guest func (q *QMP) ExecHotplugMemory(ctx context.Context, qomtype, id, mempath string, size int) error { + props := map[string]interface{}{"size": uint64(size) << 20} args := map[string]interface{}{ "qom-type": qomtype, "id": id, - "props": map[string]interface{}{"size": uint64(size) << 20}, + "props": props, } if mempath != "" { - args["mem-path"] = mempath + props["mem-path"] = mempath } err := q.executeCommand(ctx, "object-add", args, nil) if err != nil { @@ -1213,9 +1317,12 @@ func (q *QMP) ExecuteBalloon(ctx context.Context, bytes uint64) error { } // ExecutePCIVSockAdd adds a vhost-vsock-pci bus +// disableModern indicates if virtio version 1.0 should be replaced by the +// former version 0.9, as there is a KVM bug that occurs when using virtio +// 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": VHostVSockPCI, + "driver": VHostVSock, "id": id, "guest-cid": guestCID, "vhostfd": vhostfd, diff --git a/virtcontainers/qemu.go b/virtcontainers/qemu.go index e73e93699..25b29e6ac 100644 --- a/virtcontainers/qemu.go +++ b/virtcontainers/qemu.go @@ -750,7 +750,7 @@ func (q *qemu) hotplugBlockDevice(drive *config.BlockDrive, op operation) error // PCI address is in the format bridge-addr/device-addr eg. "03/02" drive.PCIAddr = fmt.Sprintf("%02x", bridge.Addr) + "/" + addr - if err = q.qmpMonitorCh.qmp.ExecutePCIDeviceAdd(q.qmpMonitorCh.ctx, drive.ID, devID, driver, addr, bridge.ID, romFile, true); err != nil { + if err = q.qmpMonitorCh.qmp.ExecutePCIDeviceAdd(q.qmpMonitorCh.ctx, drive.ID, devID, driver, addr, bridge.ID, romFile, true, q.arch.runNested()); err != nil { return err } } else { @@ -765,7 +765,7 @@ func (q *qemu) hotplugBlockDevice(drive *config.BlockDrive, op operation) error return err } - if err = q.qmpMonitorCh.qmp.ExecuteSCSIDeviceAdd(q.qmpMonitorCh.ctx, drive.ID, devID, driver, bus, romFile, scsiID, lun, true); err != nil { + if err = q.qmpMonitorCh.qmp.ExecuteSCSIDeviceAdd(q.qmpMonitorCh.ctx, drive.ID, devID, driver, bus, romFile, scsiID, lun, true, q.arch.runNested()); err != nil { return err } } @@ -892,7 +892,7 @@ func (q *qemu) hotplugNetDevice(endpoint Endpoint, op operation) error { endpoint.SetPciAddr(pciAddr) devID := "virtio-" + tap.ID - if err = q.qmpMonitorCh.qmp.ExecuteNetPCIDeviceAdd(q.qmpMonitorCh.ctx, tap.Name, devID, endpoint.HardwareAddr(), addr, bridge.ID, romFile, int(q.config.NumVCPUs)); err != nil { + if err = q.qmpMonitorCh.qmp.ExecuteNetPCIDeviceAdd(q.qmpMonitorCh.ctx, tap.Name, devID, endpoint.HardwareAddr(), addr, bridge.ID, romFile, int(q.config.NumVCPUs), q.arch.runNested()); err != nil { return err } } else { diff --git a/virtcontainers/qemu_arch_base.go b/virtcontainers/qemu_arch_base.go index a0f8f84e7..03e9251b5 100644 --- a/virtcontainers/qemu_arch_base.go +++ b/virtcontainers/qemu_arch_base.go @@ -24,6 +24,9 @@ type qemuArch interface { // disableNestingChecks nesting checks will be ignored disableNestingChecks() + // runNested indicates if the hypervisor runs in a nested environment + runNested() bool + // enableVhostNet vhost will be enabled enableVhostNet() @@ -185,6 +188,10 @@ func (q *qemuArchBase) disableNestingChecks() { q.nestedRun = false } +func (q *qemuArchBase) runNested() bool { + return q.nestedRun +} + func (q *qemuArchBase) enableVhostNet() { q.vhost = true } @@ -415,7 +422,7 @@ func (q *qemuArchBase) appendVSockPCI(devices []govmmQemu.Device, vsock kataVSOC devices = append(devices, govmmQemu.VSOCKDevice{ ID: fmt.Sprintf("vsock-%d", vsock.contextID), - ContextID: vsock.contextID, + ContextID: uint64(vsock.contextID), VHostFD: vsock.vhostFd, DisableModern: q.nestedRun, }, diff --git a/virtcontainers/qemu_test.go b/virtcontainers/qemu_test.go index 656d88490..9ac81823d 100644 --- a/virtcontainers/qemu_test.go +++ b/virtcontainers/qemu_test.go @@ -256,7 +256,7 @@ func TestQemuAddDeviceKataVSOCK(t *testing.T) { expectedOut := []govmmQemu.Device{ govmmQemu.VSOCKDevice{ ID: fmt.Sprintf("vsock-%d", contextID), - ContextID: contextID, + ContextID: uint64(contextID), VHostFD: vHostFD, }, }