Skip to content

Commit 9b26026

Browse files
authored
Merge pull request #796 from AkihiroSuda/reduce-ram
[Test wanted] qemu: adjust the memory size for workarounding M1 panic
2 parents eabffb0 + 130b300 commit 9b26026

File tree

1 file changed

+74
-10
lines changed

1 file changed

+74
-10
lines changed

pkg/qemu/qemu.go

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"strconv"
1313
"strings"
1414

15+
"github.com/coreos/go-semver/semver"
1516
"github.com/docker/go-units"
1617
"github.com/lima-vm/lima/pkg/downloader"
1718
"github.com/lima-vm/lima/pkg/iso9660util"
@@ -155,6 +156,9 @@ type features struct {
155156
// e.g. "Supported machines are:\nakita...\n...virt-6.2...\n...virt-7.0...\n...\n"
156157
// Not machine-readable, but checking strings.Contains() should be fine.
157158
MachineHelp []byte
159+
160+
// VersionGEQ7 is true when the QEMU version seems v7.0.0 or later
161+
VersionGEQ7 bool
158162
}
159163

160164
func inspectFeatures(exe string) (*features, error) {
@@ -198,6 +202,7 @@ func inspectFeatures(exe string) (*features, error) {
198202
f.MachineHelp = stderr.Bytes()
199203
}
200204
}
205+
f.VersionGEQ7 = strings.Contains(string(f.MachineHelp), "-7.0")
201206

202207
return &f, nil
203208
}
@@ -218,8 +223,7 @@ func showDarwinARM64HVFQEMU620Warning(exe, accel string, features *features) {
218223
if accel != "hvf" {
219224
return
220225
}
221-
if strings.Contains(string(features.MachineHelp), "virt-7.0") {
222-
// QEMU 7.0.0 or later
226+
if features.VersionGEQ7 {
223227
return
224228
}
225229
if exeFull, err := exec.LookPath(exe); err == nil {
@@ -243,6 +247,63 @@ func showDarwinARM64HVFQEMU620Warning(exe, accel string, features *features) {
243247
logrus.Warn(w)
244248
}
245249

250+
func getMacOSProductVersion() (*semver.Version, error) {
251+
cmd := exec.Command("sw_vers", "-productVersion")
252+
// output is like "12.3.1\n"
253+
b, err := cmd.Output()
254+
if err != nil {
255+
return nil, fmt.Errorf("failed to execute %v: %w", cmd.Args, err)
256+
}
257+
verTrimmed := strings.TrimSpace(string(b))
258+
verSem, err := semver.NewVersion(verTrimmed)
259+
if err != nil {
260+
return nil, fmt.Errorf("failed to parse macOS version %q: %w", verTrimmed, err)
261+
}
262+
return verSem, nil
263+
}
264+
265+
// adjustMemBytesDarwinARM64HVF adjusts the memory to be <= 3 GiB, only when the following conditions are met:
266+
//
267+
// - Host OS < macOS 12.4
268+
// - Host Arch == arm64
269+
// - Accel == hvf
270+
// - QEMU >= 7.0
271+
//
272+
// This adjustment is required for avoiding host kernel panic. The issue was fixed in macOS 12.4 Beta 1.
273+
// See https://github.com/lima-vm/lima/issues/795 https://gitlab.com/qemu-project/qemu/-/issues/903#note_911000975
274+
func adjustMemBytesDarwinARM64HVF(memBytes int64, accel string, features *features) int64 {
275+
const safeSize = 3 * 1024 * 1024 * 1024 // 3 GiB
276+
if memBytes <= safeSize {
277+
return memBytes
278+
}
279+
if runtime.GOOS != "darwin" {
280+
return memBytes
281+
}
282+
if runtime.GOARCH != "arm64" {
283+
return memBytes
284+
}
285+
if accel != "hvf" {
286+
return memBytes
287+
}
288+
if !features.VersionGEQ7 {
289+
return memBytes
290+
}
291+
macOSProductVersion, err := getMacOSProductVersion()
292+
if err != nil {
293+
logrus.Warn(err)
294+
return memBytes
295+
}
296+
if !macOSProductVersion.LessThan(*semver.New("12.4.0")) {
297+
return memBytes
298+
}
299+
logrus.Warnf("Reducing the guest memory from %s to %s, to avoid host kernel panic on macOS <= 12.3 with QEMU >= 7.0; "+
300+
"Please update macOS to 12.4 or later, or downgrade QEMU to 6.2; "+
301+
"See https://github.com/lima-vm/lima/issues/795 for the further background.",
302+
units.BytesSize(float64(memBytes)), units.BytesSize(float64(safeSize)))
303+
memBytes = safeSize
304+
return memBytes
305+
}
306+
246307
func Cmdline(cfg Config) (string, []string, error) {
247308
y := cfg.LimaYAML
248309
exe, args, err := getExe(*y.Arch)
@@ -262,6 +323,15 @@ func Cmdline(cfg Config) (string, []string, error) {
262323
}
263324
showDarwinARM64HVFQEMU620Warning(exe, accel, features)
264325

326+
// Memory
327+
memBytes, err := units.RAMInBytes(*y.Memory)
328+
if err != nil {
329+
return "", nil, err
330+
}
331+
memBytes = adjustMemBytesDarwinARM64HVF(memBytes, accel, features)
332+
args = appendArgsIfNoConflict(args, "-m", strconv.Itoa(int(memBytes>>20)))
333+
334+
// CPU
265335
cpu := y.CPUType[*y.Arch]
266336
args = appendArgsIfNoConflict(args, "-cpu", cpu)
267337
switch *y.Arch {
@@ -285,7 +355,8 @@ func Cmdline(cfg Config) (string, []string, error) {
285355
// 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)"
286356
// https://github.com/lima-vm/lima/issues/680
287357
// https://github.com/lima-vm/lima/pull/24
288-
if !strings.Contains(string(features.MachineHelp), "virt-7.0") {
358+
// But when the memory size is <= 3 GiB, we can always set highmem=off.
359+
if !features.VersionGEQ7 || memBytes <= 3*1024*1024*1024 {
289360
machine += ",highmem=off"
290361
}
291362
args = appendArgsIfNoConflict(args, "-machine", machine)
@@ -295,13 +366,6 @@ func Cmdline(cfg Config) (string, []string, error) {
295366
args = appendArgsIfNoConflict(args, "-smp",
296367
fmt.Sprintf("%d,sockets=1,cores=%d,threads=1", *y.CPUs, *y.CPUs))
297368

298-
// Memory
299-
memBytes, err := units.RAMInBytes(*y.Memory)
300-
if err != nil {
301-
return "", nil, err
302-
}
303-
args = appendArgsIfNoConflict(args, "-m", strconv.Itoa(int(memBytes>>20)))
304-
305369
// Firmware
306370
legacyBIOS := *y.Firmware.LegacyBIOS
307371
if legacyBIOS && *y.Arch != limayaml.X8664 {

0 commit comments

Comments
 (0)