Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/snapshots.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ A snapshot contains the full VM state:

`snapshot save` and `vm hibernate` are refused while a vhost-user-fs share, a VFIO device or a hot-attached disk is present; see [Devices](devices.md).

On Cloud Hypervisor, `snapshot save` and `vm hibernate` first let a running guest finish inflating its balloon, waiting up to a minute and moving on once inflation stalls for 2 s (the wait reads the live target, so it covers a target raised by `vm balloon`), so a `vm restore` or hibernate wake comes back with a settled balloon instead of inflating it again at a high CPU cost. A clone moves to the create-time balloon target after restore: it deflates from a raised snapshot target, so a golden saved at the default target keeps the settled balloon.
On Cloud Hypervisor, `snapshot save` and `vm hibernate` first let a running guest finish inflating its balloon, waiting up to a minute and moving on once inflation frees less than 1% of guest memory in 2 s (the wait reads the live target, so it covers a target raised by `vm balloon`), so a `vm restore` or hibernate wake comes back with a settled balloon instead of inflating it again at a high CPU cost. A clone moves to the create-time balloon target after restore: it deflates from a raised snapshot target, so a golden saved at the default target keeps the settled balloon.

### Clone Constraints

Expand Down
10 changes: 6 additions & 4 deletions hypervisor/cloudhypervisor/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
const (
balloonPollInterval = 100 * time.Millisecond
balloonStallWindow = 2 * time.Second
balloonStallPercent = 1
balloonSettleTimeout = time.Minute

ejectRenotifyInterval = 5 * time.Second
Expand Down Expand Up @@ -182,8 +183,9 @@ func waitBalloonSettled(ctx context.Context, hc *http.Client, vmID string, info
return nil
}
target := info.Config.Memory.Size - balloon.Size
step := info.Config.Memory.Size * balloonStallPercent / 100
start := time.Now()
progressAt := start
progressAt, progressFrom := start, info.MemoryActualSize
for actual := info.MemoryActualSize; actual > target; {
if time.Since(progressAt) >= balloonStallWindow || time.Since(start) >= balloonSettleTimeout {
log.WithFunc("cloudhypervisor.waitBalloonSettled").Warnf(ctx, "snapshot VM %s with balloon at %d of %d bytes", vmID, info.Config.Memory.Size-actual, balloon.Size)
Expand All @@ -198,10 +200,10 @@ func waitBalloonSettled(ctx context.Context, hc *http.Client, vmID string, info
if err != nil {
return err
}
if cur.MemoryActualSize < actual {
progressAt = time.Now()
}
actual = cur.MemoryActualSize
if progressFrom-actual >= step {
progressAt, progressFrom = time.Now(), actual
}
}
return nil
}
3 changes: 2 additions & 1 deletion hypervisor/cloudhypervisor/snapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,8 @@ func TestSnapshotPauseWaitsForBalloonToSettle(t *testing.T) {
{"paused guest cannot inflate", chStatePaused, &chBalloon{Size: balloon}, func(int) int64 { return mem }, 0},
{"inflating reaches target", chStateRunning, &chBalloon{Size: balloon}, func(polls int) int64 { return mem - min(int64(polls)*(512<<20), balloon) }, 4 * balloonPollInterval},
{"stalled under guest memory pressure", chStateRunning, &chBalloon{Size: balloon}, func(int) int64 { return mem - (512 << 20) }, balloonStallWindow},
{"slow inflation stops at the cap", chStateRunning, &chBalloon{Size: balloon}, func(polls int) int64 { return mem - int64(polls)<<12 }, balloonSettleTimeout},
{"trickling inflation counts as stalled", chStateRunning, &chBalloon{Size: balloon}, func(polls int) int64 { return mem - int64(polls)<<12 }, balloonStallWindow},
{"slow inflation stops at the cap", chStateRunning, &chBalloon{Size: mem * 3 / 4}, func(polls int) int64 { return mem - int64(polls)*(mem/1000) }, balloonSettleTimeout},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
Loading