Skip to content

Commit

Permalink
Merge pull request #796 from AkihiroSuda/reduce-ram
Browse files Browse the repository at this point in the history
[Test wanted] qemu: adjust the memory size for workarounding M1 panic
  • Loading branch information
AkihiroSuda authored Apr 22, 2022
2 parents eabffb0 + 130b300 commit 9b26026
Showing 1 changed file with 74 additions and 10 deletions.
84 changes: 74 additions & 10 deletions pkg/qemu/qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"strconv"
"strings"

"github.com/coreos/go-semver/semver"
"github.com/docker/go-units"
"github.com/lima-vm/lima/pkg/downloader"
"github.com/lima-vm/lima/pkg/iso9660util"
Expand Down Expand Up @@ -155,6 +156,9 @@ type features struct {
// e.g. "Supported machines are:\nakita...\n...virt-6.2...\n...virt-7.0...\n...\n"
// Not machine-readable, but checking strings.Contains() should be fine.
MachineHelp []byte

// VersionGEQ7 is true when the QEMU version seems v7.0.0 or later
VersionGEQ7 bool
}

func inspectFeatures(exe string) (*features, error) {
Expand Down Expand Up @@ -198,6 +202,7 @@ func inspectFeatures(exe string) (*features, error) {
f.MachineHelp = stderr.Bytes()
}
}
f.VersionGEQ7 = strings.Contains(string(f.MachineHelp), "-7.0")

return &f, nil
}
Expand All @@ -218,8 +223,7 @@ func showDarwinARM64HVFQEMU620Warning(exe, accel string, features *features) {
if accel != "hvf" {
return
}
if strings.Contains(string(features.MachineHelp), "virt-7.0") {
// QEMU 7.0.0 or later
if features.VersionGEQ7 {
return
}
if exeFull, err := exec.LookPath(exe); err == nil {
Expand All @@ -243,6 +247,63 @@ func showDarwinARM64HVFQEMU620Warning(exe, accel string, features *features) {
logrus.Warn(w)
}

func getMacOSProductVersion() (*semver.Version, error) {
cmd := exec.Command("sw_vers", "-productVersion")
// output is like "12.3.1\n"
b, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("failed to execute %v: %w", cmd.Args, err)
}
verTrimmed := strings.TrimSpace(string(b))
verSem, err := semver.NewVersion(verTrimmed)
if err != nil {
return nil, fmt.Errorf("failed to parse macOS version %q: %w", verTrimmed, err)
}
return verSem, nil
}

// adjustMemBytesDarwinARM64HVF adjusts the memory to be <= 3 GiB, only when the following conditions are met:
//
// - Host OS < macOS 12.4
// - Host Arch == arm64
// - Accel == hvf
// - QEMU >= 7.0
//
// This adjustment is required for avoiding host kernel panic. The issue was fixed in macOS 12.4 Beta 1.
// See https://github.com/lima-vm/lima/issues/795 https://gitlab.com/qemu-project/qemu/-/issues/903#note_911000975
func adjustMemBytesDarwinARM64HVF(memBytes int64, accel string, features *features) int64 {
const safeSize = 3 * 1024 * 1024 * 1024 // 3 GiB
if memBytes <= safeSize {
return memBytes
}
if runtime.GOOS != "darwin" {
return memBytes
}
if runtime.GOARCH != "arm64" {
return memBytes
}
if accel != "hvf" {
return memBytes
}
if !features.VersionGEQ7 {
return memBytes
}
macOSProductVersion, err := getMacOSProductVersion()
if err != nil {
logrus.Warn(err)
return memBytes
}
if !macOSProductVersion.LessThan(*semver.New("12.4.0")) {
return memBytes
}
logrus.Warnf("Reducing the guest memory from %s to %s, to avoid host kernel panic on macOS <= 12.3 with QEMU >= 7.0; "+
"Please update macOS to 12.4 or later, or downgrade QEMU to 6.2; "+
"See https://github.com/lima-vm/lima/issues/795 for the further background.",
units.BytesSize(float64(memBytes)), units.BytesSize(float64(safeSize)))
memBytes = safeSize
return memBytes
}

func Cmdline(cfg Config) (string, []string, error) {
y := cfg.LimaYAML
exe, args, err := getExe(*y.Arch)
Expand All @@ -262,6 +323,15 @@ func Cmdline(cfg Config) (string, []string, error) {
}
showDarwinARM64HVFQEMU620Warning(exe, accel, features)

// Memory
memBytes, err := units.RAMInBytes(*y.Memory)
if err != nil {
return "", nil, err
}
memBytes = adjustMemBytesDarwinARM64HVF(memBytes, accel, features)
args = appendArgsIfNoConflict(args, "-m", strconv.Itoa(int(memBytes>>20)))

// CPU
cpu := y.CPUType[*y.Arch]
args = appendArgsIfNoConflict(args, "-cpu", cpu)
switch *y.Arch {
Expand All @@ -285,7 +355,8 @@ func Cmdline(cfg Config) (string, []string, error) {
// QEMU < 7.0 requires highmem=off to be set, otherwise fails with "VCPU supports less PA bits (36) than requested by the memory map (40)"
// https://github.com/lima-vm/lima/issues/680
// https://github.com/lima-vm/lima/pull/24
if !strings.Contains(string(features.MachineHelp), "virt-7.0") {
// But when the memory size is <= 3 GiB, we can always set highmem=off.
if !features.VersionGEQ7 || memBytes <= 3*1024*1024*1024 {
machine += ",highmem=off"
}
args = appendArgsIfNoConflict(args, "-machine", machine)
Expand All @@ -295,13 +366,6 @@ func Cmdline(cfg Config) (string, []string, error) {
args = appendArgsIfNoConflict(args, "-smp",
fmt.Sprintf("%d,sockets=1,cores=%d,threads=1", *y.CPUs, *y.CPUs))

// Memory
memBytes, err := units.RAMInBytes(*y.Memory)
if err != nil {
return "", nil, err
}
args = appendArgsIfNoConflict(args, "-m", strconv.Itoa(int(memBytes>>20)))

// Firmware
legacyBIOS := *y.Firmware.LegacyBIOS
if legacyBIOS && *y.Arch != limayaml.X8664 {
Expand Down

0 comments on commit 9b26026

Please sign in to comment.