Skip to content
This repository has been archived by the owner on Apr 3, 2018. It is now read-only.

[RFC] qemu: set maxmem to host memory size #336

Merged
merged 1 commit into from Aug 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
38 changes: 38 additions & 0 deletions hypervisor.go
Expand Up @@ -17,7 +17,10 @@
package virtcontainers

import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)

Expand All @@ -32,6 +35,8 @@ const (
MockHypervisor HypervisorType = "mock"
)

const procMemInfo = "/proc/meminfo"

const (
defaultVCPUs = 1
// 2 GiB
Expand Down Expand Up @@ -234,3 +239,36 @@ type hypervisor interface {
addDevice(devInfo interface{}, devType deviceType) error
getPodConsole(podID string) string
}

func getHostMemorySizeKb(memInfoPath string) (uint64, error) {
f, err := os.Open(memInfoPath)
if err != nil {
return 0, err
}
defer f.Close()

scanner := bufio.NewScanner(f)
for scanner.Scan() {
// Expected format: ["MemTotal:", "1234", "kB"]
parts := strings.Fields(scanner.Text())

// Sanity checks: Skip malformed entries.
if len(parts) < 3 || parts[0] != "MemTotal:" || parts[2] != "kB" {
continue
}

sizeKb, err := strconv.ParseUint(parts[1], 0, 64)
if err != nil {
continue
}

return sizeKb, nil
}

// Handle errors that may have occurred during the reading of the file.
if err := scanner.Err(); err != nil {
return 0, err
}

return 0, fmt.Errorf("unable get MemTotal from %s", memInfoPath)
}
62 changes: 62 additions & 0 deletions hypervisor_test.go
Expand Up @@ -18,6 +18,9 @@ package virtcontainers

import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"testing"
)
Expand Down Expand Up @@ -353,3 +356,62 @@ func TestAddKernelParamInvalid(t *testing.T) {
t.Fatal()
}
}

func TestGetHostMemorySizeKb(t *testing.T) {

type testData struct {
contents string
expectedResult int
expectError bool
}

data := []testData{
{
`
MemTotal: 1 kB
MemFree: 2 kB
SwapTotal: 3 kB
SwapFree: 4 kB
`,
1024,
false,
},
{
`
MemFree: 2 kB
SwapTotal: 3 kB
SwapFree: 4 kB
`,
0,
true,
},
}

dir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)

file := filepath.Join(dir, "meminfo")
if _, err := getHostMemorySizeKb(file); err == nil {
t.Fatalf("expected failure as file %q does not exist", file)
}

for _, d := range data {
if err := ioutil.WriteFile(file, []byte(d.contents), os.FileMode(0640)); err != nil {
t.Fatal(err)
}
defer os.Remove(file)

hostMemKb, err := getHostMemorySizeKb(file)

if (d.expectError && err == nil) || (!d.expectError && err != nil) {
t.Fatalf("got %d, input %v", hostMemKb, d)
}

if reflect.DeepEqual(hostMemKb, d.expectedResult) {
t.Fatalf("got %d, input %v", hostMemKb, d)
}
}
}
28 changes: 22 additions & 6 deletions qemu.go
Expand Up @@ -106,6 +106,12 @@ const (
maxDevIDSize = 31
)

const (
// NVDIMM device needs memory space 1024MB
// See https://github.com/clearcontainers/runtime/issues/380
maxMemoryOffset = 1024
)

type qmpLogger struct{}

func (l qmpLogger) V(level int32) bool {
Expand Down Expand Up @@ -468,13 +474,20 @@ func (q *qemu) setCPUResources(podConfig PodConfig) ciaoQemu.SMP {
return smp
}

func (q *qemu) setMemoryResources(podConfig PodConfig) ciaoQemu.Memory {
func (q *qemu) setMemoryResources(podConfig PodConfig) (ciaoQemu.Memory, error) {
hostMemKb, err := getHostMemorySizeKb(procMemInfo)
if err != nil {
return ciaoQemu.Memory{}, fmt.Errorf("Unable to read memory info: %s", err)
}
if hostMemKb == 0 {
return ciaoQemu.Memory{}, fmt.Errorf("Error host memory size 0")
}

// add 1G memory space for nvdimm device (vm guest image)
memMax := fmt.Sprintf("%dM", int(float64(hostMemKb)/1024)+maxMemoryOffset)
mem := fmt.Sprintf("%dM", q.config.DefaultMemSz)
memMax := fmt.Sprintf("%dM", int(float64(q.config.DefaultMemSz)*1.5))
if podConfig.VMConfig.Memory > 0 {
mem = fmt.Sprintf("%dM", podConfig.VMConfig.Memory)
intMemMax := int(float64(podConfig.VMConfig.Memory) * 1.5)
memMax = fmt.Sprintf("%dM", intMemMax)
}

memory := ciaoQemu.Memory{
Expand All @@ -483,7 +496,7 @@ func (q *qemu) setMemoryResources(podConfig PodConfig) ciaoQemu.Memory {
MaxMem: memMax,
}

return memory
return memory, nil
}

// createPod is the Hypervisor pod creation implementation for ciaoQemu.
Expand All @@ -502,7 +515,10 @@ func (q *qemu) createPod(podConfig PodConfig) error {

smp := q.setCPUResources(podConfig)

memory := q.setMemoryResources(podConfig)
memory, err := q.setMemoryResources(podConfig)
if err != nil {
return err
}

knobs := ciaoQemu.Knobs{
NoUserConfig: true,
Expand Down
13 changes: 11 additions & 2 deletions qemu_test.go
Expand Up @@ -395,10 +395,16 @@ func TestQemuSetMemoryResources(t *testing.T) {

q := &qemu{}

hostMemKb, err := getHostMemorySizeKb(procMemInfo)
if err != nil {
t.Fatal(err)
}
memMax := fmt.Sprintf("%dM", int(float64(hostMemKb)/1024)+maxMemoryOffset)

expectedOut := ciaoQemu.Memory{
Size: "1000M",
Slots: uint8(2),
MaxMem: "1500M",
MaxMem: memMax,
}

vmConfig := Resources{
Expand All @@ -409,7 +415,10 @@ func TestQemuSetMemoryResources(t *testing.T) {
VMConfig: vmConfig,
}

memory := q.setMemoryResources(podConfig)
memory, err := q.setMemoryResources(podConfig)
if err != nil {
t.Fatal(err)
}

if reflect.DeepEqual(memory, expectedOut) == false {
t.Fatalf("Got %v\nExpecting %v", memory, expectedOut)
Expand Down