Skip to content
Snippets Groups Projects
Commit 7d713866 authored by Florent Gluck's avatar Florent Gluck
Browse files

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.
parent 551a08df
Branches
No related tags found
No related merge requests found
package utils package utils
import ( 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()
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
}
// Returns: total RAM, shared RAM, free RAM and possibly an error. /*
// 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). // RAM amounts are given in megabytes (MB).
func GetRAM() (int, int, int, error) { func GetRAM() (int, int, int, int, error) {
in := &syscall.Sysinfo_t{} in := &syscall.Sysinfo_t{}
if err := syscall.Sysinfo(in); err != nil { if err := syscall.Sysinfo(in); err != nil {
return 0, 0, 0, err return 0, 0, 0, 0, err
} }
const MB = 1024*1024 const MB = 1024*1024
...@@ -19,6 +61,8 @@ func GetRAM() (int, int, int, error) { ...@@ -19,6 +61,8 @@ func GetRAM() (int, int, int, error) {
// We always convert to uint64 to match signature. // We always convert to uint64 to match signature.
totalRam := int(uint64(in.Totalram)*uint64(in.Unit)/MB) totalRam := int(uint64(in.Totalram)*uint64(in.Unit)/MB)
sharedRam := int(uint64(in.Sharedram)*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) 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
...@@ -2,6 +2,7 @@ package vms ...@@ -2,6 +2,7 @@ package vms
import ( import (
"os" "os"
"math"
"sort" "sort"
"sync" "sync"
"errors" "errors"
...@@ -29,7 +30,6 @@ type ( ...@@ -29,7 +30,6 @@ type (
var log = logger.GetInstance() var log = logger.GetInstance()
var vms *VMs var vms *VMs
var totalRAM int
// Returns a VMs "singleton". // Returns a VMs "singleton".
// IMPORTANT: the InitVMs function must have been previously called! // IMPORTANT: the InitVMs function must have been previously called!
...@@ -40,13 +40,6 @@ func GetVMsInstance() *VMs { ...@@ -40,13 +40,6 @@ func GetVMsInstance() *VMs {
// Creates all VMs from their files on disk. // Creates all VMs from their files on disk.
// NOTE: path is the root directory where VMs reside. // NOTE: path is the root directory where VMs reside.
func InitVMs() error { 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 vmsDir := paths.GetInstance().VMsDir
vms = &VMs { m: make(map[string]VM), dir: vmsDir, rwlock: new(sync.RWMutex), usedRAM: 0 } 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) { ...@@ -179,10 +172,17 @@ func (vms *VMs)StartVM(vmID uuid.UUID) (int, string, error) {
return 0, "", err 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. // 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 vms.usedRAM += vm.Ram
if totalRAM - vms.usedRAM < 1024 { if availRAM - int(math.Round(float64(vms.usedRAM)*0.7)) < 2048 {
vms.usedRAM -= vm.Ram vms.usedRAM -= vm.Ram
return -1, "", errors.New("Insufficient free RAM to start VM") return -1, "", errors.New("Insufficient free RAM to start VM")
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment