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
clh.vmconfig.Memory.Hugepages = func(b bool) *bool { return &b }(clh.config.HugePages)
if !clh.config.ConfidentialGuest {
hostMemKb, err := GetHostMemorySizeKb(procMemInfo)
if err != nil {
return nil
}
hotplugSize := clh.config.DefaultMaxMemorySize
// 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
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
}
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
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) {
hostMemMb, err := q.hostMemMB()
if err != nil {
return govmmQemu.Memory{}, err
}
hostMemMb := q.config.DefaultMaxMemorySize
memMb := uint64(q.config.MemorySize)
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 {
maxMem, err := q.hostMemMB()
if err != nil {
return err
}
// 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()
if err != nil {
@ -1970,8 +1962,6 @@ func (q *qemu) hotplugMemory(memDev *MemoryDevice, op Operation) (int, error) {
return 0, err
}
currentMemory := int(q.config.MemorySize) + q.state.HotpluggedMemory
if memDev.SizeMB == 0 {
memLog.Debug("hotplug is not required")
return 0, nil
@ -1985,17 +1975,7 @@ func (q *qemu) hotplugMemory(memDev *MemoryDevice, op Operation) (int, error) {
return 0, nil
case AddDevice:
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)
if err != nil {
return memoryAdded, err
@ -2231,6 +2211,11 @@ func (q *qemu) ResizeMemory(ctx context.Context, reqMemMB uint32, memoryBlockSiz
case currentMemory < reqMemMB:
//hotplug
addMemMB := reqMemMB - currentMemory
if currentMemory+addMemMB > uint32(q.config.DefaultMaxMemorySize) {
addMemMB = uint32(q.config.DefaultMaxMemorySize) - currentMemory
}
memHotplugMB, err := calcHotplugMemMiBSize(addMemMB, memoryBlockSizeMB)
if err != nil {
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/types"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/utils"
"github.com/pbnjay/memory"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
@ -172,6 +173,7 @@ func TestQemuCPUTopology(t *testing.T) {
func TestQemuMemoryTopology(t *testing.T) {
mem := uint32(1000)
maxMem := memory.TotalMemory() / 1024 / 1024 //MiB
slots := uint32(8)
assert := assert.New(t)
@ -180,12 +182,11 @@ func TestQemuMemoryTopology(t *testing.T) {
config: HypervisorConfig{
MemorySize: mem,
MemSlots: slots,
DefaultMaxMemorySize: maxMem,
},
}
hostMemKb, err := GetHostMemorySizeKb(procMemInfo)
assert.NoError(err)
memMax := fmt.Sprintf("%dM", int(float64(hostMemKb)/1024))
memMax := fmt.Sprintf("%dM", int(maxMem))
expectedOut := govmmQemu.Memory{
Size: fmt.Sprintf("%dM", mem),