The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/7965
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === Allows you to shrink memory and then increase it back to boot time size without reboot VM.
From 124aad5233ee89720edd6c137500d8c81a300508 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 1 Oct 2020 12:25:37 +0100 Subject: [PATCH 1/5] lxd/instance/drivers/qmp/monitor: Renames GetMemoryBalloonSizeBytes And makes comments more accurate. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/instance/drivers/qmp/monitor.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/instance/drivers/qmp/monitor.go b/lxd/instance/drivers/qmp/monitor.go index 11c011da38..9d5eb789ff 100644 --- a/lxd/instance/drivers/qmp/monitor.go +++ b/lxd/instance/drivers/qmp/monitor.go @@ -323,8 +323,8 @@ func (m *Monitor) GetCPUs() ([]int, error) { return pids, nil } -// GetBalloonSizeBytes returns the current size of the memory balloon in bytes. -func (m *Monitor) GetBalloonSizeBytes() (int64, error) { +// GetMemoryBalloonSizeBytes returns effective size of the memory in bytes (considering the current balloon size). +func (m *Monitor) GetMemoryBalloonSizeBytes() (int64, error) { respRaw, err := m.qmp.Run([]byte("{'execute': 'query-balloon'}")) if err != nil { m.Disconnect() From 6f9663cc9d33bd27376c28155830eca3707b3072 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 1 Oct 2020 12:26:00 +0100 Subject: [PATCH 2/5] lxd/instance/drivers/qmp/monitor: Renames SetMemoryBalloonSizeBytes And makes comments more accurate. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/instance/drivers/qmp/monitor.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/instance/drivers/qmp/monitor.go b/lxd/instance/drivers/qmp/monitor.go index 9d5eb789ff..da917d78af 100644 --- a/lxd/instance/drivers/qmp/monitor.go +++ b/lxd/instance/drivers/qmp/monitor.go @@ -346,8 +346,8 @@ func (m *Monitor) GetMemoryBalloonSizeBytes() (int64, error) { return respDecoded.Return.Actual, nil } -// SetBalloonSizeBytes sets the size of the memory balloon in bytes. -func (m *Monitor) SetBalloonSizeBytes(sizeBytes int64) error { +// SetMemoryBalloonSizeBytes sets the size of the memory in bytes (which will resize the balloon as needed). +func (m *Monitor) SetMemoryBalloonSizeBytes(sizeBytes int64) error { respRaw, err := m.qmp.Run([]byte(fmt.Sprintf("{'execute': 'balloon', 'arguments': {'value': %d}}", sizeBytes))) if err != nil { m.Disconnect() From 6ced837edcae82aee518f31af32c13fd06d36d88 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 1 Oct 2020 12:26:24 +0100 Subject: [PATCH 3/5] lxd/instance/drivers/qmp/monitor: Adds GetMemorySizeBytes function Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/instance/drivers/qmp/monitor.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lxd/instance/drivers/qmp/monitor.go b/lxd/instance/drivers/qmp/monitor.go index da917d78af..e02bc96590 100644 --- a/lxd/instance/drivers/qmp/monitor.go +++ b/lxd/instance/drivers/qmp/monitor.go @@ -323,6 +323,29 @@ func (m *Monitor) GetCPUs() ([]int, error) { return pids, nil } +// GetMemorySizeBytes returns the current size of the base memory in bytes. +func (m *Monitor) GetMemorySizeBytes() (int64, error) { + respRaw, err := m.qmp.Run([]byte("{'execute': 'query-memory-size-summary'}")) + if err != nil { + m.Disconnect() + return -1, ErrMonitorDisconnect + } + + // Process the response. + var respDecoded struct { + Return struct { + BaseMemory int64 `json:"base-memory"` + } `json:"return"` + } + + err = json.Unmarshal(respRaw, &respDecoded) + if err != nil { + return -1, ErrMonitorBadReturn + } + + return respDecoded.Return.BaseMemory, nil +} + // GetMemoryBalloonSizeBytes returns effective size of the memory in bytes (considering the current balloon size). func (m *Monitor) GetMemoryBalloonSizeBytes() (int64, error) { respRaw, err := m.qmp.Run([]byte("{'execute': 'query-balloon'}")) From 6c6cd366eeb1f5e0c3a6106f15975f8de6408f14 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 1 Oct 2020 12:26:49 +0100 Subject: [PATCH 4/5] lxd/instance/drivers/driver/qemu: Adds qemuDefaultMemSize constant Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/instance/drivers/driver_qemu.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 3fed78efcb..af18d1dfe7 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -69,6 +69,9 @@ const qemuUnsafeIO = "unsafeio" // qemuSerialChardevName is used to communicate state via qmp between Qemu and LXD. const qemuSerialChardevName = "qemu_serial-chardev" +// qemuDefaultMemSize is the default memory size for VMs if not limit specified. +const qemuDefaultMemSize = "1GiB" + var errQemuAgentOffline = fmt.Errorf("LXD VM agent isn't currently running") var vmConsole = map[int]bool{} @@ -1924,7 +1927,7 @@ func (vm *qemu) addCPUMemoryConfig(sb *strings.Builder) error { // Configure memory limit. memSize := vm.expandedConfig["limits.memory"] if memSize == "" { - memSize = "1GiB" // Default to 1GiB if no memory limit specified. + memSize = qemuDefaultMemSize // Default if no memory limit specified. } memSizeBytes, err := units.ParseByteSizeString(memSize) From c71a3f8ec288b9c29eb8908db316614c1d0998ef Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 1 Oct 2020 12:27:24 +0100 Subject: [PATCH 5/5] lxd/instance/drivers/driver/qemu: Updates updateMemoryLimit to allow memory resize back to boot time size - Fixes bug where unsetting limits.memory didn't try and resize memory back to default size. - Allows live memory growth back to original boot time size. - Makes comments more accurate base on qemu's actual balloon size commands. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/instance/drivers/driver_qemu.go | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index af18d1dfe7..547e5fb088 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -2975,9 +2975,12 @@ func (vm *qemu) Update(args db.InstanceArgs, userRequested bool) error { return nil } -// updateMemoryLimit live updates the VM's memory limit by shrinking the balloon device. -// Only memory shrinking is supported at this time. +// updateMemoryLimit live updates the VM's memory limit by reszing the balloon device. func (vm *qemu) updateMemoryLimit(newLimit string) error { + if newLimit == "" { + newLimit = qemuDefaultMemSize + } + if shared.IsTrue(vm.expandedConfig["limits.memory.hugepages"]) { return fmt.Errorf("Cannot live update memory limit when using huge pages") } @@ -2994,27 +2997,32 @@ func (vm *qemu) updateMemoryLimit(newLimit string) error { return err // The VM isn't running as no monitor socket available. } - curSizeBytes, err := monitor.GetBalloonSizeBytes() + baseSizeBytes, err := monitor.GetMemorySizeBytes() + if err != nil { + return err + } + + curSizeBytes, err := monitor.GetMemoryBalloonSizeBytes() if err != nil { return err } if curSizeBytes == newSizeBytes { return nil - } else if curSizeBytes < newSizeBytes { - return fmt.Errorf("Cannot increase memory size when VM is running") + } else if baseSizeBytes < newSizeBytes { + return fmt.Errorf("Cannot increase memory size beyond boot time size when VM is running") } - // Shrink balloon device. - err = monitor.SetBalloonSizeBytes(newSizeBytes) + // Set effective memory size. + err = monitor.SetMemoryBalloonSizeBytes(newSizeBytes) if err != nil { return err } - // Shrinking the balloon can take time, so poll the actual balloon size to check it has shrunk within 1% + // Changing the memory balloon can take time, so poll the effectice size to check it has shrunk within 1% // of the target size, which we then take as success (it may still continue to shrink closer to target). for i := 0; i < 5; i++ { - curSizeBytes, err = monitor.GetBalloonSizeBytes() + curSizeBytes, err = monitor.GetMemoryBalloonSizeBytes() if err != nil { return err }
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel