[lxc-devel] [lxd/master] VM: Adds support for live memory growth back to boot time memory size

tomponline on Github lxc-bot at linuxcontainers.org
Thu Oct 1 11:30:36 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 391 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20201001/a78f8a4b/attachment-0001.bin>
-------------- next part --------------
From 124aad5233ee89720edd6c137500d8c81a300508 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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
 		}


More information about the lxc-devel mailing list