From 7d713866c5912646cd7eebbfa423673a2670e1ac Mon Sep 17 00:00:00 2001 From: Florent Gluck <florent.gluck@hesge.ch> Date: Wed, 7 Sep 2022 16:49:42 +0200 Subject: [PATCH] Takes into account real RAM usage when estimating how much RAM is left. This is used when a VM is stated in order to know whether there is enough RAM left. We assume that KSM allows us to save ~30% or RAM. --- src/utils/memory.go | 56 ++++++++++++++++++++++++++++++++++++++++----- src/vms/vms.go | 20 ++++++++-------- 2 files changed, 60 insertions(+), 16 deletions(-) diff --git a/src/utils/memory.go b/src/utils/memory.go index 1a6e9ab..a75f0b2 100644 --- a/src/utils/memory.go +++ b/src/utils/memory.go @@ -1,17 +1,59 @@ package utils import ( - "syscall" + "os" + "bufio" + "strconv" + "strings" ) -var totalRAM int +// This function returns info about the system's RAM +// by reading /proc/meminfo. +// Beware: it is Linux SPECIFIC! +// Returns total and avaiable memory in MB. +func GetRAM() (int, int, error) { + f, err := os.Open("/proc/meminfo") + if err != nil { + return 0, 0, err + } + defer f.Close() -// Returns: total RAM, shared RAM, free RAM and possibly an error. + scan := bufio.NewScanner(f) + scan.Split(bufio.ScanLines) + + var memTotal int = -1 + var memAvail int = -1 + + const units = 1024 + + for scan.Scan() { + s := scan.Text() + fields := strings.Split(s, ":") + attr := fields[0] + vals := strings.Split(strings.TrimSpace(fields[1]), " ") + val := strings.TrimSpace(vals[0]) + attr = strings.TrimSpace(strings.ToLower(attr)) + switch attr { + case "memtotal": + memTotal, _ = strconv.Atoi(val) + memTotal = memTotal/units + case "memavailable": + memAvail, _ = strconv.Atoi(val) + memAvail = memAvail/units + } + } + + return memTotal, memAvail, nil +} + +/* +// This function is not used because it DOES NOT return the available RAM! +// Returns: total RAM, shared RAM, buffered RAM, free RAM and possibly an error. // RAM amounts are given in megabytes (MB). -func GetRAM() (int, int, int, error) { +func GetRAM() (int, int, int, int, error) { in := &syscall.Sysinfo_t{} if err := syscall.Sysinfo(in); err != nil { - return 0, 0, 0, err + return 0, 0, 0, 0, err } const MB = 1024*1024 @@ -19,6 +61,8 @@ func GetRAM() (int, int, int, error) { // We always convert to uint64 to match signature. totalRam := int(uint64(in.Totalram)*uint64(in.Unit)/MB) sharedRam := int(uint64(in.Sharedram)*uint64(in.Unit)/MB) + bufferedRam := int(uint64(in.Bufferram)*uint64(in.Unit)/MB) freeRam := int(uint64(in.Freeram)*uint64(in.Unit)/MB) - return totalRam, sharedRam, freeRam, nil + return totalRam, sharedRam, bufferedRam, freeRam, nil } +*/ \ No newline at end of file diff --git a/src/vms/vms.go b/src/vms/vms.go index 0e5277e..3112bc5 100644 --- a/src/vms/vms.go +++ b/src/vms/vms.go @@ -2,6 +2,7 @@ package vms import ( "os" + "math" "sort" "sync" "errors" @@ -29,7 +30,6 @@ type ( var log = logger.GetInstance() var vms *VMs -var totalRAM int // Returns a VMs "singleton". // IMPORTANT: the InitVMs function must have been previously called! @@ -40,13 +40,6 @@ func GetVMsInstance() *VMs { // Creates all VMs from their files on disk. // NOTE: path is the root directory where VMs reside. func InitVMs() error { - // Retrieves the total amount of RAM (in MB). - ram, _, _, err := utils.GetRAM() - if err != nil { - return errors.New("Failed obtaining memory info: "+err.Error()) - } - totalRAM = ram - vmsDir := paths.GetInstance().VMsDir vms = &VMs { m: make(map[string]VM), dir: vmsDir, rwlock: new(sync.RWMutex), usedRAM: 0 } @@ -179,10 +172,17 @@ func (vms *VMs)StartVM(vmID uuid.UUID) (int, string, error) { return 0, "", err } - // Check there will be at least 1GB of RAM left after the VM has started, + // Check there will be at least 2GB of RAM left after the VM has started, // otherwise, we prevent the execution of the VM to avoid RAM saturation. + // Furthermore, given Linux's KSVM technology allows page sharing, we + // assume it's able to save 30% of RAM (should be a conservative estimate). + _, availRAM, err := utils.GetRAM() + if err != nil { + return -1, "", errors.New("Failed obtaining memory info: "+err.Error()) + } + vms.usedRAM += vm.Ram - if totalRAM - vms.usedRAM < 1024 { + if availRAM - int(math.Round(float64(vms.usedRAM)*0.7)) < 2048 { vms.usedRAM -= vm.Ram return -1, "", errors.New("Insufficient free RAM to start VM") } -- GitLab