clh,qemu: Adapt to using default_maxmemory

Let's adapt Cloud Hypervisor's and QEMU's code to properly behave to the
newly added `default_maxmemory` config.

While implementing this, a change of behaviour (or a bug fix, depending
on how you see it) has been introduced as if a pod requests more memory
than the amount avaiable in the host, instead of failing to start the
pod, we simply hotplug the maximum amount of memory available, mimicing
better the runc behaviour.

Fixes: #4516

Signed-off-by: Fabiano Fidêncio <fabiano.fidencio@intel.com>
This commit is contained in:
Fabiano Fidêncio 2022-06-27 22:09:21 +02:00
parent afdc960424
commit 58ff2bd5c9
3 changed files with 20 additions and 32 deletions

View File

@ -480,12 +480,9 @@ func (clh *cloudHypervisor) CreateVM(ctx context.Context, id string, network Net
// Enable hugepages if needed // Enable hugepages if needed
clh.vmconfig.Memory.Hugepages = func(b bool) *bool { return &b }(clh.config.HugePages) clh.vmconfig.Memory.Hugepages = func(b bool) *bool { return &b }(clh.config.HugePages)
if !clh.config.ConfidentialGuest { if !clh.config.ConfidentialGuest {
hostMemKb, err := GetHostMemorySizeKb(procMemInfo) hotplugSize := clh.config.DefaultMaxMemorySize
if err != nil {
return nil
}
// OpenAPI only supports int64 values // OpenAPI only supports int64 values
clh.vmconfig.Memory.HotplugSize = func(i int64) *int64 { return &i }(int64((utils.MemUnit(hostMemKb) * utils.KiB).ToBytes())) clh.vmconfig.Memory.HotplugSize = func(i int64) *int64 { return &i }(int64((utils.MemUnit(hotplugSize) * utils.MiB).ToBytes()))
} }
// Set initial amount of cpu's for the virtual machine // Set initial amount of cpu's for the virtual machine
clh.vmconfig.Cpus = chclient.NewCpusConfig(int32(clh.config.NumVCPUs), int32(clh.config.DefaultMaxVCPUs)) clh.vmconfig.Cpus = chclient.NewCpusConfig(int32(clh.config.NumVCPUs), int32(clh.config.DefaultMaxVCPUs))
@ -882,6 +879,11 @@ func (clh *cloudHypervisor) ResizeMemory(ctx context.Context, reqMemMB uint32, m
return 0, MemoryDevice{}, err return 0, MemoryDevice{}, err
} }
maxHotplugSize := utils.MemUnit(*info.Config.Memory.HotplugSize) * utils.Byte
if reqMemMB > uint32(maxHotplugSize.ToMiB()) {
reqMemMB = uint32(maxHotplugSize.ToMiB())
}
currentMem := utils.MemUnit(info.Config.Memory.Size) * utils.Byte currentMem := utils.MemUnit(info.Config.Memory.Size) * utils.Byte
newMem := utils.MemUnit(reqMemMB) * utils.MiB newMem := utils.MemUnit(reqMemMB) * utils.MiB

View File

@ -315,11 +315,7 @@ func (q *qemu) hostMemMB() (uint64, error) {
} }
func (q *qemu) memoryTopology() (govmmQemu.Memory, error) { func (q *qemu) memoryTopology() (govmmQemu.Memory, error) {
hostMemMb, err := q.hostMemMB() hostMemMb := q.config.DefaultMaxMemorySize
if err != nil {
return govmmQemu.Memory{}, err
}
memMb := uint64(q.config.MemorySize) memMb := uint64(q.config.MemorySize)
return q.arch.memoryTopology(memMb, hostMemMb, uint8(q.config.MemSlots)), nil return q.arch.memoryTopology(memMb, hostMemMb, uint8(q.config.MemSlots)), nil
@ -779,12 +775,8 @@ func (q *qemu) getMemArgs() (bool, string, string, error) {
} }
func (q *qemu) setupVirtioMem(ctx context.Context) error { func (q *qemu) setupVirtioMem(ctx context.Context) error {
maxMem, err := q.hostMemMB()
if err != nil {
return err
}
// backend memory size must be multiple of 4Mib // backend memory size must be multiple of 4Mib
sizeMB := (int(maxMem) - int(q.config.MemorySize)) >> 2 << 2 sizeMB := (int(q.config.DefaultMaxMemorySize) - int(q.config.MemorySize)) >> 2 << 2
share, target, memoryBack, err := q.getMemArgs() share, target, memoryBack, err := q.getMemArgs()
if err != nil { if err != nil {
@ -1970,8 +1962,6 @@ func (q *qemu) hotplugMemory(memDev *MemoryDevice, op Operation) (int, error) {
return 0, err return 0, err
} }
currentMemory := int(q.config.MemorySize) + q.state.HotpluggedMemory
if memDev.SizeMB == 0 { if memDev.SizeMB == 0 {
memLog.Debug("hotplug is not required") memLog.Debug("hotplug is not required")
return 0, nil return 0, nil
@ -1985,17 +1975,7 @@ func (q *qemu) hotplugMemory(memDev *MemoryDevice, op Operation) (int, error) {
return 0, nil return 0, nil
case AddDevice: case AddDevice:
memLog.WithField("operation", "add").Debugf("Requested to add memory: %d MB", memDev.SizeMB) memLog.WithField("operation", "add").Debugf("Requested to add memory: %d MB", memDev.SizeMB)
maxMem, err := q.hostMemMB()
if err != nil {
return 0, err
}
// Don't exceed the maximum amount of memory
if currentMemory+memDev.SizeMB > int(maxMem) {
// Fixme: return a typed error
return 0, fmt.Errorf("Unable to hotplug %d MiB memory, the SB has %d MiB and the maximum amount is %d MiB",
memDev.SizeMB, currentMemory, maxMem)
}
memoryAdded, err := q.hotplugAddMemory(memDev) memoryAdded, err := q.hotplugAddMemory(memDev)
if err != nil { if err != nil {
return memoryAdded, err return memoryAdded, err
@ -2231,6 +2211,11 @@ func (q *qemu) ResizeMemory(ctx context.Context, reqMemMB uint32, memoryBlockSiz
case currentMemory < reqMemMB: case currentMemory < reqMemMB:
//hotplug //hotplug
addMemMB := reqMemMB - currentMemory addMemMB := reqMemMB - currentMemory
if currentMemory+addMemMB > uint32(q.config.DefaultMaxMemorySize) {
addMemMB = uint32(q.config.DefaultMaxMemorySize) - currentMemory
}
memHotplugMB, err := calcHotplugMemMiBSize(addMemMB, memoryBlockSizeMB) memHotplugMB, err := calcHotplugMemMiBSize(addMemMB, memoryBlockSizeMB)
if err != nil { if err != nil {
return currentMemory, MemoryDevice{}, err return currentMemory, MemoryDevice{}, err

View File

@ -21,6 +21,7 @@ import (
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/utils" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/utils"
"github.com/pbnjay/memory"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -172,6 +173,7 @@ func TestQemuCPUTopology(t *testing.T) {
func TestQemuMemoryTopology(t *testing.T) { func TestQemuMemoryTopology(t *testing.T) {
mem := uint32(1000) mem := uint32(1000)
maxMem := memory.TotalMemory() / 1024 / 1024 //MiB
slots := uint32(8) slots := uint32(8)
assert := assert.New(t) assert := assert.New(t)
@ -180,12 +182,11 @@ func TestQemuMemoryTopology(t *testing.T) {
config: HypervisorConfig{ config: HypervisorConfig{
MemorySize: mem, MemorySize: mem,
MemSlots: slots, MemSlots: slots,
DefaultMaxMemorySize: maxMem,
}, },
} }
hostMemKb, err := GetHostMemorySizeKb(procMemInfo) memMax := fmt.Sprintf("%dM", int(maxMem))
assert.NoError(err)
memMax := fmt.Sprintf("%dM", int(float64(hostMemKb)/1024))
expectedOut := govmmQemu.Memory{ expectedOut := govmmQemu.Memory{
Size: fmt.Sprintf("%dM", mem), Size: fmt.Sprintf("%dM", mem),