diff --git a/config/config.go b/config/config.go index a57d9c46..7e6114ff 100644 --- a/config/config.go +++ b/config/config.go @@ -788,6 +788,44 @@ func UseOpenat2() bool { } } +// IsKVMAvailable checks if KVM is available on the host system by checking +// if /dev/kvm exists and is accessible. +func IsKVMAvailable() bool { + // Check if /dev/kvm exists + if _, err := os.Stat("/dev/kvm"); err != nil { + if os.IsNotExist(err) { + log.Debug("/dev/kvm not found: KVM is not available on this system") + return false + } + // Other errors from Stat (e.g., permission issues checking the file) + log.WithError(err).Warn("/dev/kvm stat failed: unexpected error, assuming KVM not available") + return false + } + + // Try to open the device to verify it's actually accessible + file, err := os.Open("/dev/kvm") + if err != nil { + if os.IsPermission(err) { + // KVM device exists but we don't have permission to access it + // Return true since KVM is present, just not accessible to this process + log.Info("/dev/kvm permission denied: KVM is present but not accessible to this process") + return true + } + if os.IsNotExist(err) { + // Shouldn't happen if Stat succeeded, but handle it anyway + log.Debug("/dev/kvm not found: KVM is not available on this system") + return false + } + // Other unexpected errors + log.WithError(err).Warn("/dev/kvm open failed: unexpected error, assuming KVM not available") + return false + } + defer file.Close() + + log.Debug("/dev/kvm is available and accessible") + return true +} + // Expand expands an input string by calling [os.ExpandEnv] to expand all // environment variables, then checks if the value is prefixed with `file://` // to support reading the value from a file. diff --git a/config/config_docker.go b/config/config_docker.go index f3e846b0..99036279 100644 --- a/config/config_docker.go +++ b/config/config_docker.go @@ -96,6 +96,10 @@ type DockerConfiguration struct { Type string `default:"local" json:"type" yaml:"type"` Config map[string]string `default:"{\"max-size\":\"5m\",\"max-file\":\"1\",\"compress\":\"false\",\"mode\":\"non-blocking\"}" json:"config" yaml:"config"` } `json:"log_config" yaml:"log_config"` + + // EnableNativeKVM enables native KVM support for containers. This allows containers + // to access /dev/kvm for hardware-accelerated virtualization. Defaults to false. + EnableNativeKVM bool `default:"false" json:"enable_native_kvm" yaml:"enable_native_kvm"` } func (c DockerConfiguration) ContainerLogConfig() container.LogConfig { diff --git a/environment/settings.go b/environment/settings.go index 596da6fc..0567109f 100644 --- a/environment/settings.go +++ b/environment/settings.go @@ -133,6 +133,17 @@ func (l Limits) AsContainerResources() container.Resources { resources.CpusetCpus = l.Threads } + // Add KVM device mapping if native KVM support is enabled + if config.Get().Docker.EnableNativeKVM { + kvmDevice := container.DeviceMapping{ + PathOnHost: "/dev/kvm", + PathInContainer: "/dev/kvm", + CgroupPermissions: "rwm", + } + // Append to existing devices slice (append handles nil slices safely) + resources.Devices = append(resources.Devices, kvmDevice) + } + return resources }