Merge pull request #990 from alicefr/s390x

s390x: add support for s390x
This commit is contained in:
Julio Montes
2018-12-11 10:57:27 -06:00
committed by GitHub
13 changed files with 750 additions and 45 deletions

12
arch/s390x-options.mk Normal file
View File

@@ -0,0 +1,12 @@
# Copyright (c) 2018 IBM
#
# SPDX-License-Identifier: Apache-2.0
#
# s390x settings
MACHINETYPE := s390-ccw-virtio
KERNELPARAMS :=
MACHINEACCELERATORS :=
QEMUCMD := qemu-system-s390x

View File

@@ -0,0 +1,21 @@
// Copyright (c) 2018 IBM
//
// SPDX-License-Identifier: Apache-2.0
//
package main
const testCPUInfoTemplate = `
vendor_id : IBM/S390
# processors : 4
bogomips per cpu: 20325.00
max thread id : 0
features : esan3 zarch stfle msa ldisp eimm dfp edat etf3eh highgprs te vx sie
cache0 : level=1 type=Data scope=Private size=128K line_size=256 associativity=8
cache1 : level=1 type=Instruction scope=Private size=96K line_size=256 associativity=6
cache2 : level=2 type=Data scope=Private size=2048K line_size=256 associativity=8
cache3 : level=2 type=Instruction scope=Private size=2048K line_size=256 associativity=8
cache4 : level=3 type=Unified scope=Shared size=65536K line_size=256 associativity=16
cache5 : level=4 type=Unified scope=Shared size=491520K line_size=256 associativity=30
processor 0: version = FF, identification = FFFFFF, machine = 2964
`

124
cli/kata-check_s390x.go Normal file
View File

@@ -0,0 +1,124 @@
// Copyright (c) 2018 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"fmt"
"github.com/sirupsen/logrus"
"strings"
)
const (
cpuFlagsTag = genericCPUFlagsTag
archCPUVendorField = genericCPUVendorField
// On s390x the cpu model is indicated by the field machine.
// Example:
// processor 0: version = FF, identification = 3FEC87, machine = 2964
archCPUModelField = "machine"
)
// archRequiredCPUFlags maps a CPU flag value to search for and a
// human-readable description of that value.
var archRequiredCPUFlags = map[string]string{}
// archRequiredCPUAttribs maps a CPU (non-CPU flag) attribute value to search for
// and a human-readable description of that value.
var archRequiredCPUAttribs = map[string]string{}
// archRequiredKernelModules maps a required module name to a human-readable
// description of the modules functionality and an optional list of
// required module parameters.
var archRequiredKernelModules = map[string]kernelModule{
"kvm": {
desc: "Kernel-based Virtual Machine",
},
}
func setCPUtype() error {
return nil
}
// kvmIsUsable determines if it will be possible to create a full virtual machine
// by creating a minimal VM and then deleting it.
func kvmIsUsable() error {
return genericKvmIsUsable()
}
func archHostCanCreateVMContainer() error {
return kvmIsUsable()
}
// hostIsVMContainerCapable checks to see if the host is theoretically capable
// of creating a VM container.
func hostIsVMContainerCapable(details vmContainerCapableDetails) error {
_, err := getCPUInfo(details.cpuInfoFile)
if err != nil {
return err
}
count, err := checkKernelModules(details.requiredKernelModules, archKernelParamHandler)
if err != nil {
return err
}
if count == 0 {
return nil
}
return fmt.Errorf("ERROR: %s", failMessage)
}
func archKernelParamHandler(onVMM bool, fields logrus.Fields, msg string) bool {
return genericArchKernelParamHandler(onVMM, fields, msg)
}
// getS390xCPUDetails returns the cpu information
func getS390xCPUDetails() (vendor, model string, err error) {
prefixModel := "processor"
cpuinfo, err := getCPUInfo(procCPUInfo)
if err != nil {
return "", "", err
}
lines := strings.Split(cpuinfo, "\n")
for _, line := range lines {
if archCPUVendorField != "" {
if strings.HasPrefix(line, archCPUVendorField) {
fields := strings.Split(line, ":")
if len(fields) > 1 {
vendor = strings.TrimSpace(fields[1])
}
}
}
if archCPUModelField != "" {
if strings.HasPrefix(line, prefixModel) {
fields := strings.Split(strings.TrimSpace(line), ",")
cpuModel := strings.Split(fields[2], "=")
model = strings.TrimSpace(cpuModel[1])
}
}
}
if vendor == "" {
return "", "", fmt.Errorf("cannot find vendor field in file %v", procCPUInfo)
}
if model == "" {
return "", "", fmt.Errorf("Error in parsing cpu model from %v", procCPUInfo)
}
return vendor, model, nil
}
func getCPUDetails() (vendor, model string, err error) {
if vendor, model, err := genericGetCPUDetails(); err == nil {
return vendor, model, nil
}
return getS390xCPUDetails()
}

View File

@@ -0,0 +1,247 @@
// Copyright (c) 2018 IBM
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"testing"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli"
)
func setupCheckHostIsVMContainerCapable(assert *assert.Assertions, cpuInfoFile string, cpuData []testCPUData, moduleData []testModuleData) {
createModules(assert, cpuInfoFile, moduleData)
// all the modules files have now been created, so deal with the
// cpuinfo data.
for _, d := range cpuData {
err := makeCPUInfoFile(cpuInfoFile, d.vendorID, d.flags)
assert.NoError(err)
details := vmContainerCapableDetails{
cpuInfoFile: cpuInfoFile,
requiredCPUFlags: archRequiredCPUFlags,
requiredCPUAttribs: archRequiredCPUAttribs,
requiredKernelModules: archRequiredKernelModules,
}
err = hostIsVMContainerCapable(details)
if d.expectError {
assert.Error(err)
} else {
assert.NoError(err)
}
}
}
func TestCCCheckCLIFunction(t *testing.T) {
assert := assert.New(t)
dir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
savedSysModuleDir := sysModuleDir
savedProcCPUInfo := procCPUInfo
cpuInfoFile := filepath.Join(dir, "cpuinfo")
// XXX: override
sysModuleDir = filepath.Join(dir, "sys/module")
procCPUInfo = cpuInfoFile
defer func() {
sysModuleDir = savedSysModuleDir
procCPUInfo = savedProcCPUInfo
}()
err = os.MkdirAll(sysModuleDir, testDirMode)
if err != nil {
t.Fatal(err)
}
cpuData := []testCPUData{
{"", "", false},
}
moduleData := []testModuleData{
{filepath.Join(sysModuleDir, "kvm"), false, "Y"},
}
devNull, err := os.OpenFile(os.DevNull, os.O_WRONLY, 0666)
assert.NoError(err)
defer devNull.Close()
savedLogOutput := kataLog.Logger.Out
// discard normal output
kataLog.Logger.Out = devNull
defer func() {
kataLog.Logger.Out = savedLogOutput
}()
setupCheckHostIsVMContainerCapable(assert, cpuInfoFile, cpuData, moduleData)
ctx := createCLIContext(nil)
ctx.App.Name = "foo"
// create buffer to save logger output
buf := &bytes.Buffer{}
// capture output this time
kataLog.Logger.Out = buf
fn, ok := kataCheckCLICommand.Action.(func(context *cli.Context) error)
assert.True(ok)
err = fn(ctx)
assert.NoError(err)
output := buf.String()
for _, m := range moduleData {
name := path.Base(m.path)
assert.True(findAnchoredString(output, name))
}
}
func TestArchKernelParamHandler(t *testing.T) {
assert := assert.New(t)
type testData struct {
onVMM bool
fields logrus.Fields
msg string
expectIgnore bool
}
data := []testData{
{true, logrus.Fields{}, "", false},
{false, logrus.Fields{}, "", false},
{
false,
logrus.Fields{
// wrong type
"parameter": 123,
},
"foo",
false,
},
{
false,
logrus.Fields{
"parameter": "unrestricted_guest",
},
"",
false,
},
{
true,
logrus.Fields{
"parameter": "unrestricted_guest",
},
"",
true,
},
{
false,
logrus.Fields{
"parameter": "nested",
},
"",
true,
},
}
for i, d := range data {
result := archKernelParamHandler(d.onVMM, d.fields, d.msg)
if d.expectIgnore {
assert.True(result, "test %d (%+v)", i, d)
} else {
assert.False(result, "test %d (%+v)", i, d)
}
}
}
func TestKvmIsUsable(t *testing.T) {
assert := assert.New(t)
dir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
savedKvmDevice := kvmDevice
fakeKVMDevice := filepath.Join(dir, "kvm")
kvmDevice = fakeKVMDevice
defer func() {
kvmDevice = savedKvmDevice
}()
err = kvmIsUsable()
assert.Error(err)
err = createEmptyFile(fakeKVMDevice)
assert.NoError(err)
err = kvmIsUsable()
assert.Error(err)
}
type TestDataa struct {
contents string
expectedVendor string
expectedModel string
expectError bool
}
func TestGetCPUDetails(t *testing.T) {
type testData struct {
contents string
expectedVendor string
expectedModel string
expectError bool
}
const validVendorName = "a vendor"
validVendor := fmt.Sprintf(`%s : %s`, archCPUVendorField, validVendorName)
const validModelName = "some CPU model"
validModel := fmt.Sprintf(`processor 0: version = 00, identification = XXXXX, %s = %s`, archCPUModelField, validModelName)
validContents := fmt.Sprintf(`
a : b
%s
foo : bar
%s
`, validVendor, validModel)
data := []TestDataa{
{"", "", "", true},
{"invalid", "", "", true},
{archCPUVendorField, "", "", true},
{validVendor, "", "", true},
{validModel, "", "", true},
{validContents, validVendorName, validModelName, false},
}
genericTestGetCPUDetails(t, validVendor, validModel, validContents, data)
}

View File

@@ -0,0 +1,91 @@
// Copyright (c) 2018 IBM
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"fmt"
vcUtils "github.com/kata-containers/runtime/virtcontainers/utils"
"path/filepath"
goruntime "runtime"
)
func getExpectedHostDetails(tmpdir string) (HostInfo, error) {
type filesToCreate struct {
file string
contents string
}
const expectedKernelVersion = "99.1"
const expectedArch = goruntime.GOARCH
expectedDistro := DistroInfo{
Name: "Foo",
Version: "42",
}
expectedCPU := CPUInfo{
Vendor: "moi",
Model: "awesome XI",
}
expectedHostDetails := HostInfo{
Kernel: expectedKernelVersion,
Architecture: expectedArch,
Distro: expectedDistro,
CPU: expectedCPU,
VMContainerCapable: true,
SupportVSocks: vcUtils.SupportsVsocks(),
}
testProcCPUInfo := filepath.Join(tmpdir, "cpuinfo")
testOSRelease := filepath.Join(tmpdir, "os-release")
// XXX: This file is *NOT* created by this function on purpose
// (to ensure the only file checked by the tests is
// testOSRelease). osReleaseClr handling is tested in
// utils_test.go.
testOSReleaseClr := filepath.Join(tmpdir, "os-release-clr")
testProcVersion := filepath.Join(tmpdir, "proc-version")
// override
procVersion = testProcVersion
osRelease = testOSRelease
osReleaseClr = testOSReleaseClr
procCPUInfo = testProcCPUInfo
procVersionContents := fmt.Sprintf("Linux version %s a b c",
expectedKernelVersion)
osReleaseContents := fmt.Sprintf(`
NAME="%s"
VERSION_ID="%s"
`, expectedDistro.Name, expectedDistro.Version)
procCPUInfoContents := fmt.Sprintf(`
%s : %s
processor 0: version = 00, identification = 3929E7, %s = %s
`,
archCPUVendorField,
expectedCPU.Vendor,
archCPUModelField,
expectedCPU.Model)
data := []filesToCreate{
{procVersion, procVersionContents},
{osRelease, osReleaseContents},
{procCPUInfo, procCPUInfoContents},
}
for _, d := range data {
err := createFile(d.file, d.contents)
if err != nil {
return HostInfo{}, err
}
}
return expectedHostDetails, nil
}

10
cli/utils_s390x.go Normal file
View File

@@ -0,0 +1,10 @@
// Copyright (c) 2018 IBM
//
// SPDX-License-Identifier: Apache-2.0
//
package main
func archConvertStatFs(cgroupFsType int) uint32 {
return uint32(cgroupFsType)
}

View File

@@ -97,6 +97,9 @@ assets:
ppc64le: ppc64le:
name: "centos" name: "centos"
version: "latest" version: "latest"
s390x:
name: "ubuntu"
version: "latest"
x86_64: x86_64:
name: &default-image-name "clearlinux" name: &default-image-name "clearlinux"
version: "20640" version: "20640"
@@ -115,6 +118,9 @@ assets:
ppc64le: ppc64le:
name: *default-initrd-name name: *default-initrd-name
version: *default-initrd-version version: *default-initrd-version
s390x:
name: *default-initrd-name
version: *default-initrd-version
x86_64: x86_64:
name: *default-initrd-name name: *default-initrd-name
version: *default-initrd-version version: *default-initrd-version

View File

@@ -525,7 +525,7 @@ func getHostMemorySizeKb(memInfoPath string) (uint64, error) {
// RunningOnVMM checks if the system is running inside a VM. // RunningOnVMM checks if the system is running inside a VM.
func RunningOnVMM(cpuInfoPath string) (bool, error) { func RunningOnVMM(cpuInfoPath string) (bool, error) {
if runtime.GOARCH == "arm64" || runtime.GOARCH == "ppc64le" { if runtime.GOARCH == "arm64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x" {
virtLog.Info("Unable to know if the system is running inside a VM") virtLog.Info("Unable to know if the system is running inside a VM")
return false, nil return false, nil
} }

View File

@@ -872,8 +872,8 @@ func (q *qemu) hotplugNetDevice(endpoint Endpoint, op operation) error {
return err return err
} }
var tap TapInterface var tap TapInterface
devID := "virtio-" + tap.ID
if op == addDevice {
switch endpoint.Type() { switch endpoint.Type() {
case VethEndpointType: case VethEndpointType:
drive := endpoint.(*VethEndpoint) drive := endpoint.(*VethEndpoint)
@@ -885,6 +885,8 @@ func (q *qemu) hotplugNetDevice(endpoint Endpoint, op operation) error {
return fmt.Errorf("this endpoint is not supported") return fmt.Errorf("this endpoint is not supported")
} }
if op == addDevice {
if err = q.hotAddNetDevice(tap.Name, endpoint.HardwareAddr(), tap.VMFds, tap.VhostFds); err != nil { if err = q.hotAddNetDevice(tap.Name, endpoint.HardwareAddr(), tap.VMFds, tap.VhostFds); err != nil {
return err return err
} }
@@ -896,34 +898,28 @@ func (q *qemu) hotplugNetDevice(endpoint Endpoint, op operation) error {
pciAddr := fmt.Sprintf("%02x/%s", bridge.Addr, addr) pciAddr := fmt.Sprintf("%02x/%s", bridge.Addr, addr)
endpoint.SetPciAddr(pciAddr) endpoint.SetPciAddr(pciAddr)
devID := "virtio-" + tap.ID var machine govmmQemu.Machine
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 { machine, err = q.getQemuMachine()
if err != nil {
return err return err
} }
} else { if machine.Type == QemuCCWVirtio {
switch endpoint.Type() { return q.qmpMonitorCh.qmp.ExecuteNetCCWDeviceAdd(q.qmpMonitorCh.ctx, tap.Name, devID, endpoint.HardwareAddr(), addr, bridge.ID, int(q.config.NumVCPUs))
case VethEndpointType: }
drive := endpoint.(*VethEndpoint) return q.qmpMonitorCh.qmp.ExecuteNetPCIDeviceAdd(q.qmpMonitorCh.ctx, tap.Name, devID, endpoint.HardwareAddr(), addr, bridge.ID, romFile, int(q.config.NumVCPUs), q.arch.runNested())
tap = drive.NetPair.TapInterface
case TapEndpointType:
drive := endpoint.(*TapEndpoint)
tap = drive.TapInterface
default:
return fmt.Errorf("this endpoint is not supported")
} }
if err := q.removeDeviceFromBridge(tap.ID); err != nil { if err := q.removeDeviceFromBridge(tap.ID); err != nil {
return err return err
} }
devID := "virtio-" + tap.ID
if err := q.qmpMonitorCh.qmp.ExecuteDeviceDel(q.qmpMonitorCh.ctx, devID); err != nil { if err := q.qmpMonitorCh.qmp.ExecuteDeviceDel(q.qmpMonitorCh.ctx, devID); err != nil {
return err return err
} }
if err := q.qmpMonitorCh.qmp.ExecuteNetdevDel(q.qmpMonitorCh.ctx, tap.Name); err != nil { if err := q.qmpMonitorCh.qmp.ExecuteNetdevDel(q.qmpMonitorCh.ctx, tap.Name); err != nil {
return err return err
} }
}
return nil return nil
} }
@@ -1149,6 +1145,7 @@ func (q *qemu) resumeSandbox() error {
// addDevice will add extra devices to Qemu command line. // addDevice will add extra devices to Qemu command line.
func (q *qemu) addDevice(devInfo interface{}, devType deviceType) error { func (q *qemu) addDevice(devInfo interface{}, devType deviceType) error {
var err error
span, _ := q.trace("addDevice") span, _ := q.trace("addDevice")
defer span.Finish() defer span.Finish()
@@ -1165,14 +1162,14 @@ func (q *qemu) addDevice(devInfo interface{}, devType deviceType) error {
case config.BlockDrive: case config.BlockDrive:
q.qemuConfig.Devices = q.arch.appendBlockDevice(q.qemuConfig.Devices, v) q.qemuConfig.Devices = q.arch.appendBlockDevice(q.qemuConfig.Devices, v)
case config.VhostUserDeviceAttrs: case config.VhostUserDeviceAttrs:
q.qemuConfig.Devices = q.arch.appendVhostUserDevice(q.qemuConfig.Devices, v) q.qemuConfig.Devices, err = q.arch.appendVhostUserDevice(q.qemuConfig.Devices, v)
case config.VFIODev: case config.VFIODev:
q.qemuConfig.Devices = q.arch.appendVFIODevice(q.qemuConfig.Devices, v) q.qemuConfig.Devices = q.arch.appendVFIODevice(q.qemuConfig.Devices, v)
default: default:
break break
} }
return nil return err
} }
// getSandboxConsole builds the path of the console where we can read // getSandboxConsole builds the path of the console where we can read
@@ -1293,6 +1290,8 @@ func genericBridges(number uint32, machineType string) []Bridge {
bt = pcieBridge bt = pcieBridge
case QemuPseries: case QemuPseries:
bt = pciBridge bt = pciBridge
case QemuCCWVirtio:
bt = pciBridge
default: default:
return nil return nil
} }

View File

@@ -86,7 +86,7 @@ type qemuArch interface {
appendBlockDevice(devices []govmmQemu.Device, drive config.BlockDrive) []govmmQemu.Device appendBlockDevice(devices []govmmQemu.Device, drive config.BlockDrive) []govmmQemu.Device
// appendVhostUserDevice appends a vhost user device to devices // appendVhostUserDevice appends a vhost user device to devices
appendVhostUserDevice(devices []govmmQemu.Device, drive config.VhostUserDeviceAttrs) []govmmQemu.Device appendVhostUserDevice(devices []govmmQemu.Device, drive config.VhostUserDeviceAttrs) ([]govmmQemu.Device, error)
// appendVFIODevice appends a VFIO device to devices // appendVFIODevice appends a VFIO device to devices
appendVFIODevice(devices []govmmQemu.Device, vfioDevice config.VFIODev) []govmmQemu.Device appendVFIODevice(devices []govmmQemu.Device, vfioDevice config.VFIODev) []govmmQemu.Device
@@ -151,6 +151,9 @@ const (
// QemuPseries is a QEMU virt machine type for ppc64le // QemuPseries is a QEMU virt machine type for ppc64le
QemuPseries = "pseries" QemuPseries = "pseries"
// QemuCCWVirtio is a QEMU virt machine type for for s390x
QemuCCWVirtio = "s390-ccw-virtio"
) )
// kernelParamsNonDebug is a list of the default kernel // kernelParamsNonDebug is a list of the default kernel
@@ -456,7 +459,7 @@ func (q *qemuArchBase) appendNetwork(devices []govmmQemu.Device, endpoint Endpoi
devices = append(devices, devices = append(devices,
govmmQemu.NetDevice{ govmmQemu.NetDevice{
Type: networkModelToQemuType(netPair.NetInterworkingModel), Type: networkModelToQemuType(netPair.NetInterworkingModel),
Driver: govmmQemu.VirtioNetPCI, Driver: govmmQemu.VirtioNet,
ID: fmt.Sprintf("network-%d", q.networkIndex), ID: fmt.Sprintf("network-%d", q.networkIndex),
IFName: netPair.TAPIface.Name, IFName: netPair.TAPIface.Name,
MACAddress: netPair.TAPIface.HardAddr, MACAddress: netPair.TAPIface.HardAddr,
@@ -473,7 +476,7 @@ func (q *qemuArchBase) appendNetwork(devices []govmmQemu.Device, endpoint Endpoi
devices = append(devices, devices = append(devices,
govmmQemu.NetDevice{ govmmQemu.NetDevice{
Type: govmmQemu.MACVTAP, Type: govmmQemu.MACVTAP,
Driver: govmmQemu.VirtioNetPCI, Driver: govmmQemu.VirtioNet,
ID: fmt.Sprintf("network-%d", q.networkIndex), ID: fmt.Sprintf("network-%d", q.networkIndex),
IFName: ep.Name(), IFName: ep.Name(),
MACAddress: ep.HardwareAddr(), MACAddress: ep.HardwareAddr(),
@@ -516,7 +519,7 @@ func (q *qemuArchBase) appendBlockDevice(devices []govmmQemu.Device, drive confi
return devices return devices
} }
func (q *qemuArchBase) appendVhostUserDevice(devices []govmmQemu.Device, attr config.VhostUserDeviceAttrs) []govmmQemu.Device { func (q *qemuArchBase) appendVhostUserDevice(devices []govmmQemu.Device, attr config.VhostUserDeviceAttrs) ([]govmmQemu.Device, error) {
qemuVhostUserDevice := govmmQemu.VhostUserDevice{} qemuVhostUserDevice := govmmQemu.VhostUserDevice{}
switch attr.Type { switch attr.Type {
@@ -534,7 +537,7 @@ func (q *qemuArchBase) appendVhostUserDevice(devices []govmmQemu.Device, attr co
devices = append(devices, qemuVhostUserDevice) devices = append(devices, qemuVhostUserDevice)
return devices return devices, nil
} }
func (q *qemuArchBase) appendVFIODevice(devices []govmmQemu.Device, vfioDev config.VFIODev) []govmmQemu.Device { func (q *qemuArchBase) appendVFIODevice(devices []govmmQemu.Device, vfioDev config.VFIODev) []govmmQemu.Device {

View File

@@ -199,6 +199,7 @@ func TestQemuArchBaseMemoryTopology(t *testing.T) {
func testQemuArchBaseAppend(t *testing.T, structure interface{}, expected []govmmQemu.Device) { func testQemuArchBaseAppend(t *testing.T, structure interface{}, expected []govmmQemu.Device) {
var devices []govmmQemu.Device var devices []govmmQemu.Device
var err error
assert := assert.New(t) assert := assert.New(t)
qemuArchBase := newQemuArchBase() qemuArchBase := newQemuArchBase()
@@ -212,9 +213,10 @@ func testQemuArchBaseAppend(t *testing.T, structure interface{}, expected []govm
case config.VFIODev: case config.VFIODev:
devices = qemuArchBase.appendVFIODevice(devices, s) devices = qemuArchBase.appendVFIODevice(devices, s)
case config.VhostUserDeviceAttrs: case config.VhostUserDeviceAttrs:
devices = qemuArchBase.appendVhostUserDevice(devices, s) devices, err = qemuArchBase.appendVhostUserDevice(devices, s)
} }
assert.NoError(err)
assert.Equal(devices, expected) assert.Equal(devices, expected)
} }
@@ -471,7 +473,7 @@ func TestQemuArchBaseAppendNetwork(t *testing.T) {
expectedOut := []govmmQemu.Device{ expectedOut := []govmmQemu.Device{
govmmQemu.NetDevice{ govmmQemu.NetDevice{
Type: networkModelToQemuType(macvlanEp.NetPair.NetInterworkingModel), Type: networkModelToQemuType(macvlanEp.NetPair.NetInterworkingModel),
Driver: govmmQemu.VirtioNetPCI, Driver: govmmQemu.VirtioNet,
ID: fmt.Sprintf("network-%d", 0), ID: fmt.Sprintf("network-%d", 0),
IFName: macvlanEp.NetPair.TAPIface.Name, IFName: macvlanEp.NetPair.TAPIface.Name,
MACAddress: macvlanEp.NetPair.TAPIface.HardAddr, MACAddress: macvlanEp.NetPair.TAPIface.HardAddr,
@@ -482,7 +484,7 @@ func TestQemuArchBaseAppendNetwork(t *testing.T) {
}, },
govmmQemu.NetDevice{ govmmQemu.NetDevice{
Type: govmmQemu.MACVTAP, Type: govmmQemu.MACVTAP,
Driver: govmmQemu.VirtioNetPCI, Driver: govmmQemu.VirtioNet,
ID: fmt.Sprintf("network-%d", 1), ID: fmt.Sprintf("network-%d", 1),
IFName: macvtapEp.Name(), IFName: macvtapEp.Name(),
MACAddress: macvtapEp.HardwareAddr(), MACAddress: macvtapEp.HardwareAddr(),

View File

@@ -0,0 +1,122 @@
// Copyright (c) 2018 IBM
//
// SPDX-License-Identifier: Apache-2.0
//
package virtcontainers
import (
"fmt"
govmmQemu "github.com/intel/govmm/qemu"
"github.com/kata-containers/runtime/virtcontainers/device/config"
)
type qemuS390x struct {
// inherit from qemuArchBase, overwrite methods if needed
qemuArchBase
}
const defaultQemuPath = "/usr/bin/qemu-system-s390x"
const defaultQemuMachineType = QemuCCWVirtio
const defaultQemuMachineOptions = "accel=kvm"
const defaultPCBridgeBus = "pci.0"
const VirtioSerialCCW = "virtio-serial-ccw"
var qemuPaths = map[string]string{
QemuCCWVirtio: defaultQemuPath,
}
var kernelRootParams = []Param{}
// Verify needed parameters
var kernelParams = []Param{
{"console", "ttysclp0"},
}
var supportedQemuMachines = []govmmQemu.Machine{
{
Type: QemuCCWVirtio,
Options: defaultQemuMachineOptions,
},
}
// MaxQemuVCPUs returns the maximum number of vCPUs supported
func MaxQemuVCPUs() uint32 {
// Max number of virtual Cpu defined in qemu. See
// https://github.com/qemu/qemu/blob/80422b00196a7af4c6efb628fae0ad8b644e98af/target/s390x/cpu.h#L55
// #define S390_MAX_CPUS 248
return uint32(248)
}
func newQemuArch(config HypervisorConfig) qemuArch {
machineType := config.HypervisorMachineType
if machineType == "" {
machineType = defaultQemuMachineType
}
q := &qemuS390x{
qemuArchBase{
machineType: machineType,
qemuPaths: qemuPaths,
supportedQemuMachines: supportedQemuMachines,
kernelParamsNonDebug: kernelParamsNonDebug,
kernelParamsDebug: kernelParamsDebug,
kernelParams: kernelParams,
},
}
if config.ImagePath != "" {
q.kernelParams = append(q.kernelParams, kernelRootParams...)
q.kernelParamsNonDebug = append(q.kernelParamsNonDebug, kernelParamsSystemdNonDebug...)
q.kernelParamsDebug = append(q.kernelParamsDebug, kernelParamsSystemdDebug...)
}
return q
}
// appendBridges appends to devices the given bridges
func (q *qemuS390x) appendBridges(devices []govmmQemu.Device, bridges []Bridge) []govmmQemu.Device {
return genericAppendBridges(devices, bridges, q.machineType)
}
// appendConsole appends a console to devices.
// The function has been overwriten to correctly set the driver to the CCW device
func (q *qemuS390x) appendConsole(devices []govmmQemu.Device, path string) []govmmQemu.Device {
serial := govmmQemu.SerialDevice{
Driver: VirtioSerialCCW,
ID: "serial0",
DisableModern: q.nestedRun,
}
devices = append(devices, serial)
var console govmmQemu.CharDevice
console = govmmQemu.CharDevice{
Driver: govmmQemu.Console,
Backend: govmmQemu.Socket,
DeviceID: "console0",
ID: "charconsole0",
Path: path,
}
devices = append(devices, console)
return devices
}
// appendVhostUserDevice throws an error if vhost devices are tried to be used.
// See issue https://github.com/kata-containers/runtime/issues/659
func (q *qemuS390x) appendVhostUserDevice(devices []govmmQemu.Device, attr config.VhostUserDeviceAttrs) ([]govmmQemu.Device, error) {
return nil, fmt.Errorf("No vhost-user devices supported on s390x")
}
// supportGuestMemoryHotplug return false for s390x architecture. The pc-dimm backend device for s390x
// is not support. PC-DIMM is not listed in the devices supported by qemu-system-s390x -device help
func (q *qemuS390x) supportGuestMemoryHotplug() bool {
return false
}

View File

@@ -0,0 +1,68 @@
// Copyright (c) 2018 IBM
//
// SPDX-License-Identifier: Apache-2.0
//
package virtcontainers
import (
"fmt"
"testing"
govmmQemu "github.com/intel/govmm/qemu"
"github.com/kata-containers/runtime/virtcontainers/device/config"
"github.com/stretchr/testify/assert"
)
func newTestQemu(machineType string) qemuArch {
config := HypervisorConfig{
HypervisorMachineType: machineType,
}
return newQemuArch(config)
}
func TestQemuS390xCPUModel(t *testing.T) {
assert := assert.New(t)
s390x := newTestQemu(QemuCCWVirtio)
expectedOut := defaultCPUModel
model := s390x.cpuModel()
assert.Equal(expectedOut, model)
s390x.enableNestingChecks()
expectedOut = defaultCPUModel + ",pmu=off"
model = s390x.cpuModel()
assert.Equal(expectedOut, model)
}
func TestQemuS390xMemoryTopology(t *testing.T) {
assert := assert.New(t)
s390x := newTestQemu(QemuCCWVirtio)
memoryOffset := 1024
hostMem := uint64(1024)
mem := uint64(120)
slots := uint8(10)
expectedMemory := govmmQemu.Memory{
Size: fmt.Sprintf("%dM", mem),
Slots: slots,
MaxMem: fmt.Sprintf("%dM", hostMem+uint64(memoryOffset)),
}
m := s390x.memoryTopology(mem, hostMem, slots)
assert.Equal(expectedMemory, m)
}
func TestQemuS390xAppendVhostUserDevice(t *testing.T) {
macAddress := "00:11:22:33:44:55:66"
qemu := qemuS390x{}
assert := assert.New(t)
vhostUserDevice := config.VhostUserDeviceAttrs{
Type: config.VhostUserNet,
MacAddress: macAddress,
}
_, err := qemu.appendVhostUserDevice(nil, vhostUserDevice)
assert.Error(err)
}