mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-07-02 18:23:12 +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
|
// CPUDevice is CPU device type
|
||||||
cpuDev
|
cpuDev
|
||||||
|
|
||||||
|
// memoryDevice is memory device type
|
||||||
|
memoryDev
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type memoryDevice struct {
|
||||||
|
slot int
|
||||||
|
sizeMB int
|
||||||
|
}
|
||||||
|
|
||||||
// Set sets an hypervisor type based on the input string.
|
// Set sets an hypervisor type based on the input string.
|
||||||
func (hType *HypervisorType) Set(value string) error {
|
func (hType *HypervisorType) Set(value string) error {
|
||||||
switch value {
|
switch value {
|
||||||
|
@ -7,6 +7,7 @@ package virtcontainers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -40,11 +41,14 @@ type QemuState struct {
|
|||||||
Bridges []Bridge
|
Bridges []Bridge
|
||||||
// HotpluggedCPUs is the list of CPUs that were hot-added
|
// HotpluggedCPUs is the list of CPUs that were hot-added
|
||||||
HotpluggedVCPUs []CPUDevice
|
HotpluggedVCPUs []CPUDevice
|
||||||
|
HotpluggedMemory int
|
||||||
UUID string
|
UUID string
|
||||||
}
|
}
|
||||||
|
|
||||||
// qemu is an Hypervisor interface implementation for the Linux qemu hypervisor.
|
// qemu is an Hypervisor interface implementation for the Linux qemu hypervisor.
|
||||||
type qemu struct {
|
type qemu struct {
|
||||||
|
vmConfig Resources
|
||||||
|
|
||||||
config HypervisorConfig
|
config HypervisorConfig
|
||||||
|
|
||||||
qmpMonitorCh qmpChannel
|
qmpMonitorCh qmpChannel
|
||||||
@ -169,6 +173,7 @@ func (q *qemu) init(sandbox *Sandbox) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
q.vmConfig = sandbox.config.VMConfig
|
||||||
q.config = sandbox.config.HypervisorConfig
|
q.config = sandbox.config.HypervisorConfig
|
||||||
q.sandbox = sandbox
|
q.sandbox = sandbox
|
||||||
q.arch = newQemuArch(q.config)
|
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)
|
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)
|
hostMemKb, err := getHostMemorySizeKb(procMemInfo)
|
||||||
if err != nil {
|
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 {
|
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)
|
memMb := uint64(q.config.DefaultMemSz)
|
||||||
if sandboxConfig.VMConfig.Memory > 0 {
|
if q.vmConfig.Memory > 0 {
|
||||||
memMb = uint64(sandboxConfig.VMConfig.Memory)
|
memMb = uint64(q.vmConfig.Memory)
|
||||||
}
|
}
|
||||||
|
|
||||||
return q.arch.memoryTopology(memMb, hostMemMb), nil
|
return q.arch.memoryTopology(memMb, hostMemMb), nil
|
||||||
@ -271,7 +283,7 @@ func (q *qemu) createSandbox(sandboxConfig SandboxConfig) error {
|
|||||||
|
|
||||||
smp := q.cpuTopology()
|
smp := q.cpuTopology()
|
||||||
|
|
||||||
memory, err := q.memoryTopology(sandboxConfig)
|
memory, err := q.memoryTopology()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
// TODO: find a way to remove dependency of deviceDrivers lib @weizhang555
|
||||||
device := devInfo.(deviceDrivers.VFIODevice)
|
device := devInfo.(deviceDrivers.VFIODevice)
|
||||||
return nil, q.hotplugVFIODevice(device, op)
|
return nil, q.hotplugVFIODevice(device, op)
|
||||||
|
case memoryDev:
|
||||||
|
memdev := devInfo.(*memoryDevice)
|
||||||
|
return nil, q.hotplugMemory(memdev, op)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("cannot hotplug device: unsupported device type '%v'", devType)
|
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)
|
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 {
|
func (q *qemu) pauseSandbox() error {
|
||||||
return q.togglePauseSandbox(true)
|
return q.togglePauseSandbox(true)
|
||||||
}
|
}
|
||||||
|
@ -167,15 +167,11 @@ func TestQemuMemoryTopology(t *testing.T) {
|
|||||||
MaxMem: memMax,
|
MaxMem: memMax,
|
||||||
}
|
}
|
||||||
|
|
||||||
vmConfig := Resources{
|
q.vmConfig = Resources{
|
||||||
Memory: uint(mem),
|
Memory: uint(mem),
|
||||||
}
|
}
|
||||||
|
|
||||||
sandboxConfig := SandboxConfig{
|
memory, err := q.memoryTopology()
|
||||||
VMConfig: vmConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
memory, err := q.memoryTopology(sandboxConfig)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -323,3 +319,29 @@ func TestQemuQemuPath(t *testing.T) {
|
|||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
assert.Equal(path, "")
|
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