mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-07-01 09:42:45 +00:00
hypervisor/qemu: add memory hotplug support
So that we can add more memory to an existing guest. Fixes: #469 Signed-off-by: Peng Tao <bergwolf@gmail.com>
This commit is contained in:
parent
0646a39ff0
commit
66a3e812f2
@ -76,8 +76,16 @@ const (
|
||||
|
||||
// CPUDevice is CPU device type
|
||||
cpuDev
|
||||
|
||||
// memoryDevice is memory device type
|
||||
memoryDev
|
||||
)
|
||||
|
||||
type memoryDevice struct {
|
||||
slot int
|
||||
sizeMB int
|
||||
}
|
||||
|
||||
// Set sets an hypervisor type based on the input string.
|
||||
func (hType *HypervisorType) Set(value string) error {
|
||||
switch value {
|
||||
|
@ -7,6 +7,7 @@ package virtcontainers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -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
|
||||
@ -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)
|
||||
@ -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 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
|
||||
@ -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
|
||||
}
|
||||
@ -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, op)
|
||||
default:
|
||||
return nil, fmt.Errorf("cannot hotplug device: unsupported device type '%v'", devType)
|
||||
}
|
||||
@ -837,6 +852,63 @@ func (q *qemu) hotplugRemoveCPUs(amount uint32) (uint32, error) {
|
||||
return amount, q.sandbox.storage.storeHypervisorState(q.sandbox.id, q.state)
|
||||
}
|
||||
|
||||
func (q *qemu) hotplugMemory(memDev *memoryDevice, op operation) error {
|
||||
if memDev.sizeMB < 0 {
|
||||
return fmt.Errorf("cannot hotplug negative size (%d) memory", memDev.sizeMB)
|
||||
}
|
||||
|
||||
// We do not support memory hot unplug.
|
||||
if op == removeDevice {
|
||||
return errors.New("cannot hot unplug memory device")
|
||||
}
|
||||
|
||||
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+memDev.sizeMB > int(maxMem) {
|
||||
return fmt.Errorf("Unable to hotplug %d MiB memory, the SB has %d MiB and the maximum amount is %d MiB",
|
||||
memDev.sizeMB, currentMemory, q.config.DefaultMemSz)
|
||||
}
|
||||
|
||||
return q.hotplugAddMemory(memDev)
|
||||
}
|
||||
|
||||
func (q *qemu) hotplugAddMemory(memDev *memoryDevice) error {
|
||||
// setup qmp channel if necessary
|
||||
if q.qmpMonitorCh.qmp == nil {
|
||||
qmp, err := q.qmpSetup()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
q.qmpMonitorCh.qmp = qmp
|
||||
|
||||
defer func() {
|
||||
qmp.Shutdown()
|
||||
q.qmpMonitorCh.qmp = nil
|
||||
}()
|
||||
}
|
||||
|
||||
err := q.qmpMonitorCh.qmp.ExecHotplugMemory(q.qmpMonitorCh.ctx, "memory-backend-ram", "mem"+strconv.Itoa(memDev.slot), "", memDev.sizeMB)
|
||||
if err != nil {
|
||||
q.Logger().WithError(err).Error("hotplug memory")
|
||||
return err
|
||||
}
|
||||
|
||||
q.state.HotpluggedMemory += memDev.sizeMB
|
||||
return q.sandbox.storage.storeHypervisorState(q.sandbox.id, q.state)
|
||||
}
|
||||
|
||||
func (q *qemu) pauseSandbox() error {
|
||||
return q.togglePauseSandbox(true)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
@ -323,3 +319,29 @@ func TestQemuQemuPath(t *testing.T) {
|
||||
assert.Error(err)
|
||||
assert.Equal(path, "")
|
||||
}
|
||||
|
||||
func TestHotplugRemoveMemory(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
qemuConfig := newQemuConfig()
|
||||
q := &qemu{
|
||||
config: qemuConfig,
|
||||
}
|
||||
|
||||
_, err := q.hotplugRemoveDevice(&memoryDevice{0, 128}, memoryDev)
|
||||
assert.Error(err)
|
||||
}
|
||||
|
||||
func TestHotplugUnsupportedDeviceType(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
qemuConfig := newQemuConfig()
|
||||
q := &qemu{
|
||||
config: qemuConfig,
|
||||
}
|
||||
|
||||
_, err := q.hotplugAddDevice(&memoryDevice{0, 128}, fsDev)
|
||||
assert.Error(err)
|
||||
_, err = q.hotplugRemoveDevice(&memoryDevice{0, 128}, fsDev)
|
||||
assert.Error(err)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user