Skip to content
This repository has been archived by the owner on May 12, 2021. It is now read-only.

Commit

Permalink
hypervisor/qemu: add memory hotplug support
Browse files Browse the repository at this point in the history
So that we can add more memory to an existing guest.

Fixes: #469

Signed-off-by: Peng Tao <bergwolf@gmail.com>
  • Loading branch information
bergwolf committed Jul 5, 2018
1 parent 0646a39 commit 0844887
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 15 deletions.
8 changes: 8 additions & 0 deletions virtcontainers/hypervisor.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,16 @@ const (

// CPUDevice is CPU device type
cpuDev

// memoryDevice is memory device type
memoryDev
)

type memoryDevice struct {
slot uint32
sizeMB int
}

// Set sets an hypervisor type based on the input string.
func (hType *HypervisorType) Set(value string) error {
switch value {
Expand Down
88 changes: 79 additions & 9 deletions virtcontainers/qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package virtcontainers

import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
Expand Down Expand Up @@ -39,12 +40,15 @@ type CPUDevice struct {
type QemuState struct {
Bridges []Bridge
// HotpluggedCPUs is the list of CPUs that were hot-added
HotpluggedVCPUs []CPUDevice
UUID string
HotpluggedVCPUs []CPUDevice
HotpluggedMemory int
UUID string
}

// qemu is an Hypervisor interface implementation for the Linux qemu hypervisor.
type qemu struct {
vmConfig Resources

config HypervisorConfig

qmpMonitorCh qmpChannel
Expand Down Expand Up @@ -169,6 +173,7 @@ func (q *qemu) init(sandbox *Sandbox) error {
return err
}

q.vmConfig = sandbox.config.VMConfig
q.config = sandbox.config.HypervisorConfig
q.sandbox = sandbox
q.arch = newQemuArch(q.config)
Expand Down Expand Up @@ -204,20 +209,27 @@ func (q *qemu) cpuTopology() govmmQemu.SMP {
return q.arch.cpuTopology(q.config.DefaultVCPUs, q.config.DefaultMaxVCPUs)
}

func (q *qemu) memoryTopology(sandboxConfig SandboxConfig) (govmmQemu.Memory, error) {
func (q *qemu) hostMemMB() (uint64, error) {
hostMemKb, err := getHostMemorySizeKb(procMemInfo)
if err != nil {
return govmmQemu.Memory{}, fmt.Errorf("Unable to read memory info: %s", err)
return 0, fmt.Errorf("Unable to read memory info: %s", err)
}
if hostMemKb == 0 {
return govmmQemu.Memory{}, fmt.Errorf("Error host memory size 0")
return 0, fmt.Errorf("Error host memory size 0")
}

hostMemMb := uint64(float64(hostMemKb / 1024))
return uint64(float64(hostMemKb / 1024)), nil
}

func (q *qemu) memoryTopology() (govmmQemu.Memory, error) {
hostMemMb, err := q.hostMemMB()
if err != nil {
return govmmQemu.Memory{}, err
}

memMb := uint64(q.config.DefaultMemSz)
if sandboxConfig.VMConfig.Memory > 0 {
memMb = uint64(sandboxConfig.VMConfig.Memory)
if q.vmConfig.Memory > 0 {
memMb = uint64(q.vmConfig.Memory)
}

return q.arch.memoryTopology(memMb, hostMemMb), nil
Expand Down Expand Up @@ -271,7 +283,7 @@ func (q *qemu) createSandbox(sandboxConfig SandboxConfig) error {

smp := q.cpuTopology()

memory, err := q.memoryTopology(sandboxConfig)
memory, err := q.memoryTopology()
if err != nil {
return err
}
Expand Down Expand Up @@ -705,6 +717,9 @@ func (q *qemu) hotplugDevice(devInfo interface{}, devType deviceType, op operati
// TODO: find a way to remove dependency of deviceDrivers lib @weizhang555
device := devInfo.(deviceDrivers.VFIODevice)
return nil, q.hotplugVFIODevice(device, op)
case memoryDev:
memdev := devInfo.(*memoryDevice)
return nil, q.hotplugMemory(memdev.slot, memdev.sizeMB, op)
default:
return nil, fmt.Errorf("cannot hotplug device: unsupported device type '%v'", devType)
}
Expand Down Expand Up @@ -837,6 +852,61 @@ func (q *qemu) hotplugRemoveCPUs(amount uint32) (uint32, error) {
return amount, q.sandbox.storage.storeHypervisorState(q.sandbox.id, q.state)
}

func (q *qemu) hotplugMemory(slot uint32, memMB int, op operation) error {
if memMB <= 0 {
return errors.New("cannot hotplug zero or negative MB memory")
}

// We do not support memory hot unplug.
if op == removeDevice {
return errors.New("cannot hot unplug memory device")
}

defer func(qemu *qemu) {
if q.qmpMonitorCh.qmp != nil {
q.qmpMonitorCh.qmp.Shutdown()
}
}(q)

qmp, err := q.qmpSetup()
if err != nil {
return err
}

q.qmpMonitorCh.qmp = qmp

return q.hotplugAddMemory(slot, memMB)
}

func (q *qemu) hotplugAddMemory(slot uint32, amount int) error {
maxMem, err := q.hostMemMB()
if err != nil {
return err
}

// calculate current memory
currentMemory := int(q.config.DefaultMemSz)
if q.vmConfig.Memory > 0 {
currentMemory = int(q.vmConfig.Memory)
}
currentMemory += q.state.HotpluggedMemory

// Don't exceed the maximum amount of memory
if currentMemory+amount > int(maxMem) {
return fmt.Errorf("Unable to hotplug %d MB memory, currently this SB has %d memory and the maximum amount is %d",
amount, currentMemory, q.config.DefaultMemSz)
}

err = q.qmpMonitorCh.qmp.ExecHotplugMemory(q.qmpMonitorCh.ctx, "memory-backend-ram", "mem"+strconv.Itoa(int(slot)), "", amount)
if err != nil {
q.Logger().WithError(err).Error("hotplug memory")
return err
}

q.state.HotpluggedMemory += amount
return q.sandbox.storage.storeHypervisorState(q.sandbox.id, q.state)
}

func (q *qemu) pauseSandbox() error {
return q.togglePauseSandbox(true)
}
Expand Down
8 changes: 2 additions & 6 deletions virtcontainers/qemu_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,15 +167,11 @@ func TestQemuMemoryTopology(t *testing.T) {
MaxMem: memMax,
}

vmConfig := Resources{
q.vmConfig = Resources{
Memory: uint(mem),
}

sandboxConfig := SandboxConfig{
VMConfig: vmConfig,
}

memory, err := q.memoryTopology(sandboxConfig)
memory, err := q.memoryTopology()
if err != nil {
t.Fatal(err)
}
Expand Down

0 comments on commit 0844887

Please sign in to comment.