diff --git a/cli/kata-check.go b/cli/kata-check.go index 28c23b6dd7..068c743738 100644 --- a/cli/kata-check.go +++ b/cli/kata-check.go @@ -16,6 +16,7 @@ const int ioctl_KVM_CHECK_EXTENSION = KVM_CHECK_EXTENSION; import "C" import ( + "errors" "fmt" "os" "os/exec" @@ -26,6 +27,7 @@ import ( "github.com/kata-containers/runtime/pkg/katautils" vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" "github.com/sirupsen/logrus" "github.com/urfave/cli" ) @@ -311,7 +313,12 @@ var kataCheckCLICommand = cli.Command{ span, _ := katautils.Trace(ctx, "kata-check") defer span.Finish() - err = setCPUtype() + runtimeConfig, ok := context.App.Metadata["runtimeConfig"].(oci.RuntimeConfig) + if !ok { + return errors.New("kata-check: cannot determine runtime config") + } + + err = setCPUtype(runtimeConfig.HypervisorType) if err != nil { return err } @@ -332,7 +339,7 @@ var kataCheckCLICommand = cli.Command{ kataLog.Info(successMessageCapable) if os.Geteuid() == 0 { - err = archHostCanCreateVMContainer() + err = archHostCanCreateVMContainer(runtimeConfig.HypervisorType) if err != nil { return err } diff --git a/cli/kata-check_amd64.go b/cli/kata-check_amd64.go index 21e210e891..a36a3ec16a 100644 --- a/cli/kata-check_amd64.go +++ b/cli/kata-check_amd64.go @@ -7,11 +7,13 @@ package main import ( "fmt" - "github.com/sirupsen/logrus" "io/ioutil" "strings" + "syscall" + "unsafe" vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/sirupsen/logrus" ) const ( @@ -24,6 +26,17 @@ const ( msgKernelVirtio = "Host kernel accelerator for virtio" msgKernelVirtioNet = "Host kernel accelerator for virtio network" msgKernelVirtioVhostVsock = "Host Support for Linux VM Sockets" + cpuFlagVMX = "vmx" + cpuFlagLM = "lm" + cpuFlagSVM = "svm" + cpuFlagSSE4_1 = "sse4_1" + kernelModvhm = "vhm_dev" + kernelModvhost = "vhost" + kernelModvhostnet = "vhost_net" + kernelModvhostvsock = "vhost_vsock" + kernelModkvm = "kvm" + kernelModkvmintel = "kvm_intel" + kernelModkvmamd = "kvm_amd" ) // CPU types @@ -33,6 +46,28 @@ const ( cpuTypeUnknown = -1 ) +const acrnDevice = "/dev/acrn_vhm" + +// ioctl_ACRN_CREATE_VM is the IOCTL to create VM in ACRN. +// Current Linux mainstream kernel doesn't have support for ACRN. +// Due to this several macros are not defined in Linux headers. +// Until the support is available, directly use the value instead +// of macros. +//https://github.com/kata-containers/runtime/issues/1784 +const ioctl_ACRN_CREATE_VM = 0x43000010 //nolint +const ioctl_ACRN_DESTROY_VM = 0x43000011 //nolint + +type acrn_create_vm struct { //nolint + vmid uint16 //nolint + reserved0 uint16 //nolint + vcpu_num uint16 //nolint + reserved1 uint16 //nolint + uuid [16]uint8 + vm_flag uint64 //nolint + req_buf uint64 //nolint + reserved2 [16]uint8 //nolint +} + // cpuType save the CPU type var cpuType int @@ -49,7 +84,7 @@ var archRequiredCPUAttribs map[string]string // required module parameters. var archRequiredKernelModules map[string]kernelModule -func setCPUtype() error { +func setCPUtype(hypervisorType vc.HypervisorType) error { cpuType = getCPUtype() if cpuType == cpuTypeUnknown { @@ -66,64 +101,88 @@ func setCPUtype() error { "unrestricted_guest": "Y", } } - archRequiredCPUFlags = map[string]string{ - "vmx": "Virtualization support", - "lm": "64Bit CPU", - "sse4_1": "SSE4.1", - } - archRequiredCPUAttribs = map[string]string{ - archGenuineIntel: "Intel Architecture CPU", - } - archRequiredKernelModules = map[string]kernelModule{ - "kvm": { - desc: msgKernelVM, - required: true, - }, - "kvm_intel": { - desc: "Intel KVM", - parameters: kvmIntelParams, - required: true, - }, - "vhost": { - desc: msgKernelVirtio, - required: true, - }, - "vhost_net": { - desc: msgKernelVirtioNet, - required: true, - }, - "vhost_vsock": { - desc: msgKernelVirtioVhostVsock, - required: false, - }, + + switch hypervisorType { + case "firecracker": + fallthrough + case "qemu": + archRequiredCPUFlags = map[string]string{ + cpuFlagVMX: "Virtualization support", + cpuFlagLM: "64Bit CPU", + cpuFlagSSE4_1: "SSE4.1", + } + archRequiredCPUAttribs = map[string]string{ + archGenuineIntel: "Intel Architecture CPU", + } + archRequiredKernelModules = map[string]kernelModule{ + kernelModkvm: { + desc: msgKernelVM, + }, + kernelModkvmintel: { + desc: "Intel KVM", + parameters: kvmIntelParams, + }, + kernelModvhost: { + desc: msgKernelVirtio, + }, + kernelModvhostnet: { + desc: msgKernelVirtioNet, + }, + kernelModvhostvsock: { + desc: msgKernelVirtioVhostVsock, + required: false, + }, + } + case "acrn": + archRequiredCPUFlags = map[string]string{ + cpuFlagLM: "64Bit CPU", + cpuFlagSSE4_1: "SSE4.1", + } + archRequiredCPUAttribs = map[string]string{ + archGenuineIntel: "Intel Architecture CPU", + } + archRequiredKernelModules = map[string]kernelModule{ + kernelModvhm: { + desc: "Intel ACRN", + }, + kernelModvhost: { + desc: msgKernelVirtio, + }, + kernelModvhostnet: { + desc: msgKernelVirtioNet, + }, + } + default: + return fmt.Errorf("setCPUtype: Unknown hypervisor type %s", hypervisorType) } + } else if cpuType == cpuTypeAMD { archRequiredCPUFlags = map[string]string{ - "svm": "Virtualization support", - "lm": "64Bit CPU", - "sse4_1": "SSE4.1", + cpuFlagSVM: "Virtualization support", + cpuFlagLM: "64Bit CPU", + cpuFlagSSE4_1: "SSE4.1", } archRequiredCPUAttribs = map[string]string{ archAuthenticAMD: "AMD Architecture CPU", } archRequiredKernelModules = map[string]kernelModule{ - "kvm": { + kernelModkvm: { desc: msgKernelVM, required: true, }, - "kvm_amd": { + kernelModkvmamd: { desc: "AMD KVM", required: true, }, - "vhost": { + kernelModvhost: { desc: msgKernelVirtio, required: true, }, - "vhost_net": { + kernelModvhostnet: { desc: msgKernelVirtioNet, required: true, }, - "vhost_vsock": { + kernelModvhostvsock: { desc: msgKernelVirtioVhostVsock, required: false, }, @@ -155,8 +214,72 @@ func kvmIsUsable() error { return genericKvmIsUsable() } -func archHostCanCreateVMContainer() error { - return kvmIsUsable() +// acrnIsUsable determines if it will be possible to create a full virtual machine +// by creating a minimal VM and then deleting it. +func acrnIsUsable() error { + flags := syscall.O_RDWR | syscall.O_CLOEXEC + + f, err := syscall.Open(acrnDevice, flags, 0) + if err != nil { + return err + } + defer syscall.Close(f) + + fieldLogger := kataLog.WithField("check-type", "full") + + fieldLogger.WithField("device", acrnDevice).Info("device available") + + createVM := acrn_create_vm{ + uuid: [16]uint8{ + 0xd2, 0x79, 0x54, 0x38, 0x25, 0xd6, 0x11, 0xe8, + 0x86, 0x4e, 0xcb, 0x7a, 0x18, 0xb3, 0x46, 0x43, + }, + } + + ret, _, errno := syscall.Syscall(syscall.SYS_IOCTL, + uintptr(f), + uintptr(ioctl_ACRN_CREATE_VM), + uintptr(unsafe.Pointer(&createVM))) + if ret != 0 || errno != 0 { + if errno == syscall.EBUSY { + fieldLogger.WithField("reason", "another hypervisor running").Error("cannot create VM") + } + fieldLogger.WithFields(logrus.Fields{ + "ret": ret, + "errno": errno, + }).Info("Create VM Error") + return errno + } + + ret, _, errno = syscall.Syscall(syscall.SYS_IOCTL, + uintptr(f), + uintptr(ioctl_ACRN_DESTROY_VM), + 0) + if ret != 0 || errno != 0 { + fieldLogger.WithFields(logrus.Fields{ + "ret": ret, + "errno": errno, + }).Info("Destroy VM Error") + return errno + } + + fieldLogger.WithField("feature", "create-vm").Info("feature available") + + return nil +} + +func archHostCanCreateVMContainer(hypervisorType vc.HypervisorType) error { + + switch hypervisorType { + case "qemu": + fallthrough + case "firecracker": + return kvmIsUsable() + case "acrn": + return acrnIsUsable() + default: + return fmt.Errorf("archHostCanCreateVMContainer: Unknown hypervisor type %s", hypervisorType) + } } // hostIsVMContainerCapable checks to see if the host is theoretically capable diff --git a/cli/kata-check_amd64_test.go b/cli/kata-check_amd64_test.go index bf2bbedd02..2fcba9ae29 100644 --- a/cli/kata-check_amd64_test.go +++ b/cli/kata-check_amd64_test.go @@ -55,6 +55,9 @@ func TestCCCheckCLIFunction(t *testing.T) { } defer os.RemoveAll(dir) + _, config, err := makeRuntimeConfig(dir) + assert.NoError(err) + savedSysModuleDir := sysModuleDir savedProcCPUInfo := procCPUInfo @@ -108,6 +111,7 @@ func TestCCCheckCLIFunction(t *testing.T) { ctx := createCLIContext(nil) ctx.App.Name = "foo" + ctx.App.Metadata["runtimeConfig"] = config // create buffer to save logger output buf := &bytes.Buffer{} @@ -514,6 +518,10 @@ foo : bar func TestSetCPUtype(t *testing.T) { assert := assert.New(t) + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + savedArchRequiredCPUFlags := archRequiredCPUFlags savedArchRequiredCPUAttribs := archRequiredCPUAttribs savedArchRequiredKernelModules := archRequiredKernelModules @@ -528,7 +536,10 @@ func TestSetCPUtype(t *testing.T) { archRequiredCPUAttribs = map[string]string{} archRequiredKernelModules = map[string]kernelModule{} - setCPUtype() + _, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(err) + + setCPUtype(config.HypervisorType) assert.NotEmpty(archRequiredCPUFlags) assert.NotEmpty(archRequiredCPUAttribs) diff --git a/cli/kata-check_arm64.go b/cli/kata-check_arm64.go index 5f835fcaba..959f312a45 100644 --- a/cli/kata-check_arm64.go +++ b/cli/kata-check_arm64.go @@ -8,6 +8,7 @@ package main import ( "fmt" + vc "github.com/kata-containers/runtime/virtcontainers" "github.com/sirupsen/logrus" ) @@ -56,7 +57,7 @@ var archRequiredKVMExtensions = map[string]kvmExtension{ }, } -func setCPUtype() error { +func setCPUtype(hypervisorType vc.HypervisorType) error { return nil } @@ -84,7 +85,7 @@ func checkKVMExtensions() error { return nil } -func archHostCanCreateVMContainer() error { +func archHostCanCreateVMContainer(hypervisorType vc.HypervisorType) error { if err := kvmIsUsable(); err != nil { return err } diff --git a/cli/kata-check_arm64_test.go b/cli/kata-check_arm64_test.go index 433c0996fd..3c0588f29e 100644 --- a/cli/kata-check_arm64_test.go +++ b/cli/kata-check_arm64_test.go @@ -36,6 +36,9 @@ func TestCCCheckCLIFunction(t *testing.T) { } defer os.RemoveAll(dir) + _, config, err := makeRuntimeConfig(dir) + assert.NoError(err) + savedSysModuleDir := sysModuleDir savedProcCPUInfo := procCPUInfo @@ -78,6 +81,7 @@ func TestCCCheckCLIFunction(t *testing.T) { ctx := createCLIContext(nil) ctx.App.Name = "foo" + ctx.App.Metadata["runtimeConfig"] = config // create buffer to save logger output buf := &bytes.Buffer{} diff --git a/cli/kata-check_generic_test.go b/cli/kata-check_generic_test.go index 5332d4e892..1441268308 100644 --- a/cli/kata-check_generic_test.go +++ b/cli/kata-check_generic_test.go @@ -8,6 +8,8 @@ package main import ( + "io/ioutil" + "os" "testing" "github.com/stretchr/testify/assert" @@ -16,6 +18,10 @@ import ( func testSetCPUTypeGeneric(t *testing.T) { assert := assert.New(t) + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + savedArchRequiredCPUFlags := archRequiredCPUFlags savedArchRequiredCPUAttribs := archRequiredCPUAttribs savedArchRequiredKernelModules := archRequiredKernelModules @@ -30,7 +36,10 @@ func testSetCPUTypeGeneric(t *testing.T) { assert.Empty(archRequiredCPUAttribs) assert.NotEmpty(archRequiredKernelModules) - setCPUtype() + _, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(err) + + setCPUtype(config.HypervisorType) assert.Equal(archRequiredCPUFlags, savedArchRequiredCPUFlags) assert.Equal(archRequiredCPUAttribs, savedArchRequiredCPUAttribs) diff --git a/cli/kata-check_ppc64le.go b/cli/kata-check_ppc64le.go index 4d7ec3494b..b57c971ced 100644 --- a/cli/kata-check_ppc64le.go +++ b/cli/kata-check_ppc64le.go @@ -8,12 +8,13 @@ package main import ( "fmt" "os/exec" + "regexp" + "strconv" "strings" "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" "github.com/sirupsen/logrus" - "regexp" - "strconv" ) const ( @@ -55,11 +56,11 @@ var archRequiredKernelModules = map[string]kernelModule{ }, } -func setCPUtype() error { +func setCPUtype(hypervisorType vc.HypervisorType) error { return nil } -func archHostCanCreateVMContainer() error { +func archHostCanCreateVMContainer(hypervisorType vc.HypervisorType) error { return kvmIsUsable() } diff --git a/cli/kata-check_ppc64le_test.go b/cli/kata-check_ppc64le_test.go index 29859c30cb..d49ec445f5 100644 --- a/cli/kata-check_ppc64le_test.go +++ b/cli/kata-check_ppc64le_test.go @@ -53,6 +53,9 @@ func TestCCCheckCLIFunction(t *testing.T) { } defer os.RemoveAll(dir) + _, config, err := makeRuntimeConfig(dir) + assert.NoError(err) + savedSysModuleDir := sysModuleDir savedProcCPUInfo := procCPUInfo @@ -98,6 +101,7 @@ func TestCCCheckCLIFunction(t *testing.T) { ctx := createCLIContext(nil) ctx.App.Name = "foo" + ctx.App.Metadata["runtimeConfig"] = config // create buffer to save logger output buf := &bytes.Buffer{} diff --git a/cli/kata-check_s390x.go b/cli/kata-check_s390x.go index 1e5f42d503..0c67f1520a 100644 --- a/cli/kata-check_s390x.go +++ b/cli/kata-check_s390x.go @@ -7,8 +7,10 @@ package main import ( "fmt" - "github.com/sirupsen/logrus" "strings" + + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/sirupsen/logrus" ) const ( @@ -42,7 +44,7 @@ var archRequiredKernelModules = map[string]kernelModule{ }, } -func setCPUtype() error { +func setCPUtype(hypervisorType vc.HypervisorType) error { return nil } @@ -52,7 +54,7 @@ func kvmIsUsable() error { return genericKvmIsUsable() } -func archHostCanCreateVMContainer() error { +func archHostCanCreateVMContainer(hypervisorType vc.HypervisorType) error { return kvmIsUsable() } diff --git a/cli/kata-check_s390x_test.go b/cli/kata-check_s390x_test.go index c8bc129ea4..108d6e1e8e 100644 --- a/cli/kata-check_s390x_test.go +++ b/cli/kata-check_s390x_test.go @@ -53,6 +53,9 @@ func TestCCCheckCLIFunction(t *testing.T) { } defer os.RemoveAll(dir) + _, config, err := makeRuntimeConfig(dir) + assert.NoError(err) + savedSysModuleDir := sysModuleDir savedProcCPUInfo := procCPUInfo @@ -97,6 +100,7 @@ func TestCCCheckCLIFunction(t *testing.T) { ctx := createCLIContext(nil) ctx.App.Name = "foo" + ctx.App.Metadata["runtimeConfig"] = config // create buffer to save logger output buf := &bytes.Buffer{} diff --git a/cli/kata-check_test.go b/cli/kata-check_test.go index 29767a9260..87605ef123 100644 --- a/cli/kata-check_test.go +++ b/cli/kata-check_test.go @@ -659,6 +659,9 @@ func TestCheckCLIFunctionFail(t *testing.T) { } defer os.RemoveAll(dir) + _, config, err := makeRuntimeConfig(dir) + assert.NoError(err) + oldProcCPUInfo := procCPUInfo // doesn't exist @@ -670,6 +673,7 @@ func TestCheckCLIFunctionFail(t *testing.T) { ctx := createCLIContext(nil) ctx.App.Name = "foo" + ctx.App.Metadata["runtimeConfig"] = config fn, ok := kataCheckCLICommand.Action.(func(context *cli.Context) error) assert.True(ok) diff --git a/cli/kata-env.go b/cli/kata-env.go index c8610daa4c..f9c1e9d808 100644 --- a/cli/kata-env.go +++ b/cli/kata-env.go @@ -9,9 +9,8 @@ import ( "encoding/json" "errors" "os" - "strings" - runtim "runtime" + "strings" "github.com/BurntSushi/toml" "github.com/kata-containers/runtime/pkg/katautils" @@ -358,7 +357,7 @@ func getHypervisorInfo(config oci.RuntimeConfig) HypervisorInfo { } func getEnvInfo(configFile string, config oci.RuntimeConfig) (env EnvInfo, err error) { - err = setCPUtype() + err = setCPUtype(config.HypervisorType) if err != nil { return EnvInfo{}, err } diff --git a/cli/main.go b/cli/main.go index a021a0a775..72c9165e7d 100644 --- a/cli/main.go +++ b/cli/main.go @@ -254,54 +254,54 @@ func beforeSubcommands(c *cli.Context) error { handleShowConfig(c) - if userWantsUsage(c) || (c.NArg() == 1 && (c.Args()[0] == checkCmd)) { + if userWantsUsage(c) { // No setup required if the user just - // wants to see the usage statement or are - // running a command that does not manipulate - // containers. + // wants to see the usage statement. return nil } - if path := c.GlobalString("log"); path != "" { - f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0640) - if err != nil { - return err - } - kataLog.Logger.Out = f - } - - switch c.GlobalString("log-format") { - case "text": - // retain logrus's default. - case "json": - kataLog.Logger.Formatter = new(logrus.JSONFormatter) - default: - return fmt.Errorf("unknown log-format %q", c.GlobalString("log-format")) - } - + ignoreLogging := false var traceRootSpan string - // Add the name of the sub-command to each log entry for easier - // debugging. - cmdName := c.Args().First() - if c.App.Command(cmdName) != nil { - kataLog = kataLog.WithField("command", cmdName) + subCmdIsCheckCmd := (c.NArg() == 1 && (c.Args()[0] == checkCmd)) + if !subCmdIsCheckCmd { + if path := c.GlobalString("log"); path != "" { + f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0640) + if err != nil { + return err + } + kataLog.Logger.Out = f + } - // Name for the root span (used for tracing) now the - // sub-command name is known. - traceRootSpan = name + " " + cmdName - } + switch c.GlobalString("log-format") { + case "text": + // retain logrus's default. + case "json": + kataLog.Logger.Formatter = new(logrus.JSONFormatter) + default: + return fmt.Errorf("unknown log-format %q", c.GlobalString("log-format")) + } - // Since a context is required, pass a new (throw-away) one - we - // cannot use the main context as tracing hasn't been enabled yet - // (meaning any spans created at this point will be silently ignored). - setExternalLoggers(context.Background(), kataLog) + // Add the name of the sub-command to each log entry for easier + // debugging. + cmdName := c.Args().First() + if c.App.Command(cmdName) != nil { + kataLog = kataLog.WithField("command", cmdName) - ignoreLogging := false + // Name for the root span (used for tracing) now the + // sub-command name is known. + traceRootSpan = name + " " + cmdName + } - if c.NArg() == 1 && c.Args()[0] == envCmd { - // simply report the logging setup - ignoreLogging = true + // Since a context is required, pass a new (throw-away) one - we + // cannot use the main context as tracing hasn't been enabled yet + // (meaning any spans created at this point will be silently ignored). + setExternalLoggers(context.Background(), kataLog) + + if c.NArg() == 1 && c.Args()[0] == envCmd { + // simply report the logging setup + ignoreLogging = true + } } configFile, runtimeConfig, err = katautils.LoadConfiguration(c.GlobalString(configFilePathOption), ignoreLogging, false) @@ -309,19 +309,21 @@ func beforeSubcommands(c *cli.Context) error { fatal(err) } - debug = runtimeConfig.Debug - crashOnError = runtimeConfig.Debug - - if traceRootSpan != "" { - // Create the tracer. - // - // Note: no spans are created until the command-line has been parsed. - // This delays collection of trace data slightly but benefits the user by - // ensuring the first span is the name of the sub-command being - // invoked from the command-line. - err = setupTracing(c, traceRootSpan) - if err != nil { - return err + if !subCmdIsCheckCmd { + debug = runtimeConfig.Debug + crashOnError = runtimeConfig.Debug + + if traceRootSpan != "" { + // Create the tracer. + // + // Note: no spans are created until the command-line has been parsed. + // This delays collection of trace data slightly but benefits the user by + // ensuring the first span is the name of the sub-command being + // invoked from the command-line. + err = setupTracing(c, traceRootSpan) + if err != nil { + return err + } } }