qmemman: add comments, make some identifiers more verbose
This commit is contained in:
parent
18e207cbc5
commit
6067be29df
@ -7,11 +7,11 @@ import os
|
|||||||
|
|
||||||
class DomainState:
|
class DomainState:
|
||||||
def __init__(self, id):
|
def __init__(self, id):
|
||||||
self.meminfo = None
|
self.meminfo = None #dictionary of memory info read from client
|
||||||
self.memory_actual = None
|
self.memory_actual = None #the current memory size
|
||||||
self.mem_used = None
|
self.mem_used = None #used memory, computed based on meminfo
|
||||||
self.id = id
|
self.id = id #domain id
|
||||||
self.last_target = 0
|
self.last_target = 0 #the last memset target
|
||||||
|
|
||||||
class SystemState:
|
class SystemState:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -36,6 +36,7 @@ class SystemState:
|
|||||||
# ret = host_metrics_record["memory_free"]
|
# ret = host_metrics_record["memory_free"]
|
||||||
# return long(ret)
|
# return long(ret)
|
||||||
|
|
||||||
|
#refresh information on memory assigned to all domains
|
||||||
def refresh_memactual(self):
|
def refresh_memactual(self):
|
||||||
for domain in self.xc.domain_getinfo():
|
for domain in self.xc.domain_getinfo():
|
||||||
id = str(domain['domid'])
|
id = str(domain['domid'])
|
||||||
@ -67,6 +68,7 @@ class SystemState:
|
|||||||
except XenAPI.Failure:
|
except XenAPI.Failure:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
#perform memory ballooning, across all domains, to add "memsize" to Xen free memory
|
||||||
def do_balloon(self, memsize):
|
def do_balloon(self, memsize):
|
||||||
MAX_TRIES = 20
|
MAX_TRIES = 20
|
||||||
niter = 0
|
niter = 0
|
||||||
@ -82,6 +84,7 @@ class SystemState:
|
|||||||
if prev_memory_actual is not None:
|
if prev_memory_actual is not None:
|
||||||
for i in prev_memory_actual.keys():
|
for i in prev_memory_actual.keys():
|
||||||
if prev_memory_actual[i] == self.domdict[i].memory_actual:
|
if prev_memory_actual[i] == self.domdict[i].memory_actual:
|
||||||
|
#domain not responding to memset requests, remove it from donors
|
||||||
self.domdict[i].no_progress = True
|
self.domdict[i].no_progress = True
|
||||||
print 'domain', i, 'stuck at', self.domdict[i].memory_actual
|
print 'domain', i, 'stuck at', self.domdict[i].memory_actual
|
||||||
memset_reqs = qmemman_algo.balloon(memsize + self.XEN_FREE_MEM_LEFT - xenfree, self.domdict)
|
memset_reqs = qmemman_algo.balloon(memsize + self.XEN_FREE_MEM_LEFT - xenfree, self.domdict)
|
||||||
@ -100,6 +103,8 @@ class SystemState:
|
|||||||
qmemman_algo.refresh_meminfo_for_domain(self.domdict[domid], untrusted_meminfo_key)
|
qmemman_algo.refresh_meminfo_for_domain(self.domdict[domid], untrusted_meminfo_key)
|
||||||
self.do_balance()
|
self.do_balance()
|
||||||
|
|
||||||
|
#is the computed balance request big enough ?
|
||||||
|
#so that we do not trash with small adjustments
|
||||||
def is_balance_req_significant(self, memset_reqs, xenfree):
|
def is_balance_req_significant(self, memset_reqs, xenfree):
|
||||||
total_memory_transfer = 0
|
total_memory_transfer = 0
|
||||||
MIN_TOTAL_MEMORY_TRANSFER = 150*1024*1024
|
MIN_TOTAL_MEMORY_TRANSFER = 150*1024*1024
|
||||||
|
@ -14,7 +14,7 @@ def parse_meminfo(untrusted_meminfo):
|
|||||||
|
|
||||||
return untrusted_dict
|
return untrusted_dict
|
||||||
|
|
||||||
def is_meminfo_suspicious(dom, untrusted_meminfo):
|
def is_meminfo_suspicious(domain, untrusted_meminfo):
|
||||||
ret = False
|
ret = False
|
||||||
|
|
||||||
#check whether the required keys exist and are not negative
|
#check whether the required keys exist and are not negative
|
||||||
@ -37,51 +37,54 @@ def is_meminfo_suspicious(dom, untrusted_meminfo):
|
|||||||
#it can be achieved with legal values, too, and it will not allow to
|
#it can be achieved with legal values, too, and it will not allow to
|
||||||
#starve existing domains, by design
|
#starve existing domains, by design
|
||||||
if ret:
|
if ret:
|
||||||
print 'suspicious meminfo for domain', dom.id, 'mem actual', dom.memory_actual, untrusted_meminfo
|
print 'suspicious meminfo for domain', domain.id, 'mem actual', domain.memory_actual, untrusted_meminfo
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def refresh_meminfo_for_domain(dom, untrusted_xenstore_key):
|
#called when a domain updates its 'meminfo' xenstore key
|
||||||
|
def refresh_meminfo_for_domain(domain, untrusted_xenstore_key):
|
||||||
untrusted_meminfo = parse_meminfo(untrusted_xenstore_key)
|
untrusted_meminfo = parse_meminfo(untrusted_xenstore_key)
|
||||||
if untrusted_meminfo is None:
|
if untrusted_meminfo is None:
|
||||||
dom.meminfo = None
|
domain.meminfo = None
|
||||||
return
|
return
|
||||||
#sanitize start
|
#sanitize start
|
||||||
if is_meminfo_suspicious(dom, untrusted_meminfo):
|
if is_meminfo_suspicious(domain, untrusted_meminfo):
|
||||||
#sanitize end
|
#sanitize end
|
||||||
dom.meminfo = None
|
domain.meminfo = None
|
||||||
dom.mem_used = None
|
domain.mem_used = None
|
||||||
else:
|
else:
|
||||||
#sanitized, can assign
|
#sanitized, can assign
|
||||||
dom.meminfo = untrusted_meminfo
|
domain.meminfo = untrusted_meminfo
|
||||||
dom.mem_used = dom.meminfo['MemTotal'] - dom.meminfo['MemFree'] - dom.meminfo['Cached'] - dom.meminfo['Buffers'] + dom.meminfo['SwapTotal'] - dom.meminfo['SwapFree']
|
domain.mem_used = domain.meminfo['MemTotal'] - domain.meminfo['MemFree'] - domain.meminfo['Cached'] - domain.meminfo['Buffers'] + domain.meminfo['SwapTotal'] - domain.meminfo['SwapFree']
|
||||||
|
|
||||||
def prefmem(dom):
|
def prefmem(domain):
|
||||||
CACHE_FACTOR = 1.3
|
CACHE_FACTOR = 1.3
|
||||||
#dom0 is special, as it must have large cache, for vbds. Thus, give it a special boost
|
#dom0 is special, as it must have large cache, for vbds. Thus, give it a special boost
|
||||||
if dom.id == '0':
|
if domain.id == '0':
|
||||||
return dom.mem_used*CACHE_FACTOR + 350*1024*1024
|
return domain.mem_used*CACHE_FACTOR + 350*1024*1024
|
||||||
return dom.mem_used*CACHE_FACTOR
|
return domain.mem_used*CACHE_FACTOR
|
||||||
|
|
||||||
def memneeded(dom):
|
def memory_needed(domain):
|
||||||
#do not change
|
#do not change
|
||||||
#in balance(), "distribute totalsum proportionally to mempref" relies on this exact formula
|
#in balance(), "distribute total_available_memory proportionally to mempref" relies on this exact formula
|
||||||
ret = prefmem(dom) - dom.memory_actual
|
ret = prefmem(domain) - domain.memory_actual
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
#prepare list of (domain, memory_target) pairs that need to be passed
|
||||||
def balloon(memsize, domdict):
|
#to "xm memset" equivalent in order to obtain "memsize" of memory
|
||||||
|
#return empty list when the request cannot be satisfied
|
||||||
|
def balloon(memsize, domain_dictionary):
|
||||||
REQ_SAFETY_NET_FACTOR = 1.05
|
REQ_SAFETY_NET_FACTOR = 1.05
|
||||||
donors = list()
|
donors = list()
|
||||||
request = list()
|
request = list()
|
||||||
available = 0
|
available = 0
|
||||||
for i in domdict.keys():
|
for i in domain_dictionary.keys():
|
||||||
if domdict[i].meminfo is None:
|
if domain_dictionary[i].meminfo is None:
|
||||||
continue
|
continue
|
||||||
if domdict[i].no_progress:
|
if domain_dictionary[i].no_progress:
|
||||||
continue
|
continue
|
||||||
need = memneeded(domdict[i])
|
need = memory_needed(domain_dictionary[i])
|
||||||
if need < 0:
|
if need < 0:
|
||||||
print 'balloon: dom' , i, 'has actual memory', domdict[i].memory_actual
|
print 'balloon: dom' , i, 'has actual memory', domain_dictionary[i].memory_actual
|
||||||
donors.append((i,-need))
|
donors.append((i,-need))
|
||||||
available-=need
|
available-=need
|
||||||
print 'req=', memsize, 'avail=', available, 'donors', donors
|
print 'req=', memsize, 'avail=', available, 'donors', donors
|
||||||
@ -92,78 +95,92 @@ def balloon(memsize, domdict):
|
|||||||
id, mem = donors_iter
|
id, mem = donors_iter
|
||||||
memborrowed = mem*scale*REQ_SAFETY_NET_FACTOR
|
memborrowed = mem*scale*REQ_SAFETY_NET_FACTOR
|
||||||
print 'borrow' , memborrowed, 'from', id
|
print 'borrow' , memborrowed, 'from', id
|
||||||
memtarget = int(domdict[id].memory_actual - memborrowed)
|
memtarget = int(domain_dictionary[id].memory_actual - memborrowed)
|
||||||
request.append((id, memtarget))
|
request.append((id, memtarget))
|
||||||
return request
|
return request
|
||||||
# REQ_SAFETY_NET_FACTOR is a bit greater that 1. So that if the domain yields a bit less than requested, due
|
# REQ_SAFETY_NET_FACTOR is a bit greater that 1. So that if the domain yields a bit less than requested, due
|
||||||
# to e.g. rounding errors, we will not get stuck. The surplus will return to the VM during "balance" call.
|
# to e.g. rounding errors, we will not get stuck. The surplus will return to the VM during "balance" call.
|
||||||
|
|
||||||
|
|
||||||
#redistribute positive "totalsum" of memory between domains, proportionally to prefmem
|
#redistribute positive "total_available_memory" of memory between domains, proportionally to prefmem
|
||||||
def balance_when_enough_memory(domdict, xenfree, total_mem_pref, totalsum):
|
def balance_when_enough_memory(domain_dictionary, xen_free_memory, total_mem_pref, total_available_memory):
|
||||||
donors_rq = list()
|
donors_rq = list()
|
||||||
acceptors_rq = list()
|
acceptors_rq = list()
|
||||||
for i in domdict.keys():
|
for i in domain_dictionary.keys():
|
||||||
if domdict[i].meminfo is None:
|
if domain_dictionary[i].meminfo is None:
|
||||||
continue
|
continue
|
||||||
#distribute totalsum proportionally to mempref
|
#distribute total_available_memory proportionally to mempref
|
||||||
scale = 1.0*prefmem(domdict[i])/total_mem_pref
|
scale = 1.0*prefmem(domain_dictionary[i])/total_mem_pref
|
||||||
target_nonint = prefmem(domdict[i]) + scale*totalsum
|
target_nonint = prefmem(domain_dictionary[i]) + scale*total_available_memory
|
||||||
#prevent rounding errors
|
#prevent rounding errors
|
||||||
target = int(0.999*target_nonint)
|
target = int(0.999*target_nonint)
|
||||||
if (target < domdict[i].memory_actual):
|
if (target < domain_dictionary[i].memory_actual):
|
||||||
donors_rq.append((i, target))
|
donors_rq.append((i, target))
|
||||||
else:
|
else:
|
||||||
acceptors_rq.append((i, target))
|
acceptors_rq.append((i, target))
|
||||||
# print 'balance(enough): xenfree=', xenfree, 'requests:', donors_rq + acceptors_rq
|
# print 'balance(enough): xen_free_memory=', xen_free_memory, 'requests:', donors_rq + acceptors_rq
|
||||||
return donors_rq + acceptors_rq
|
return donors_rq + acceptors_rq
|
||||||
|
|
||||||
#when not enough mem to make everyone be above prefmem, make donors be at prefmem, and
|
#when not enough mem to make everyone be above prefmem, make donors be at prefmem, and
|
||||||
#redistribute anything left between acceptors
|
#redistribute anything left between acceptors
|
||||||
def balance_when_low_on_memory(domdict, xenfree, total_mem_pref_acceptors, donors, acceptors):
|
def balance_when_low_on_memory(domain_dictionary, xen_free_memory, total_mem_pref_acceptors, donors, acceptors):
|
||||||
donors_rq = list()
|
donors_rq = list()
|
||||||
acceptors_rq = list()
|
acceptors_rq = list()
|
||||||
squeezed_mem = xenfree
|
squeezed_mem = xen_free_memory
|
||||||
for i in donors:
|
for i in donors:
|
||||||
avail = -memneeded(domdict[i])
|
avail = -memory_needed(domain_dictionary[i])
|
||||||
if avail < 10*1024*1024:
|
if avail < 10*1024*1024:
|
||||||
#probably we have already tried making it exactly at prefmem, give up
|
#probably we have already tried making it exactly at prefmem, give up
|
||||||
continue
|
continue
|
||||||
squeezed_mem -= avail
|
squeezed_mem -= avail
|
||||||
donors_rq.append((i, prefmem(domdict[i])))
|
donors_rq.append((i, prefmem(domain_dictionary[i])))
|
||||||
#the below can happen if initially xen free memory is below 50M
|
#the below can happen if initially xen free memory is below 50M
|
||||||
if squeezed_mem < 0:
|
if squeezed_mem < 0:
|
||||||
return donors_rq
|
return donors_rq
|
||||||
for i in acceptors:
|
for i in acceptors:
|
||||||
scale = 1.0*prefmem(domdict[i])/total_mem_pref_acceptors
|
scale = 1.0*prefmem(domain_dictionary[i])/total_mem_pref_acceptors
|
||||||
target_nonint = domdict[i].memory_actual + scale*squeezed_mem
|
target_nonint = domain_dictionary[i].memory_actual + scale*squeezed_mem
|
||||||
acceptors_rq.append((i, int(target_nonint)))
|
acceptors_rq.append((i, int(target_nonint)))
|
||||||
# print 'balance(low): xenfree=', xenfree, 'requests:', donors_rq + acceptors_rq
|
# print 'balance(low): xen_free_memory=', xen_free_memory, 'requests:', donors_rq + acceptors_rq
|
||||||
return donors_rq + acceptors_rq
|
return donors_rq + acceptors_rq
|
||||||
|
|
||||||
def balance(xenfree, domdict):
|
|
||||||
total_memneeded = 0
|
#redistribute memory across domains
|
||||||
|
#called when one of domains update its 'meminfo' xenstore key
|
||||||
|
#return the list of (domain, memory_target) pairs to be passed to
|
||||||
|
#"xm memset" equivalent
|
||||||
|
def balance(xen_free_memory, domain_dictionary):
|
||||||
|
|
||||||
|
#sum of all memory requirements - in other words, the difference between
|
||||||
|
#memory required to be added to domains (acceptors) to make them be at their
|
||||||
|
#preferred memory, and memory that can be taken from domains (donors) that
|
||||||
|
#can provide memory. So, it can be negative when plenty of memory.
|
||||||
|
total_memory_needed = 0
|
||||||
|
|
||||||
|
#sum of memory preferences of all domains
|
||||||
total_mem_pref = 0
|
total_mem_pref = 0
|
||||||
|
|
||||||
|
#sum of memory preferences of all domains that require more memory
|
||||||
total_mem_pref_acceptors = 0
|
total_mem_pref_acceptors = 0
|
||||||
|
|
||||||
donors = list()
|
donors = list() # domains that can yield memory
|
||||||
acceptors = list()
|
acceptors = list() # domains that require more memory
|
||||||
#pass 1: compute the above "total" values
|
#pass 1: compute the above "total" values
|
||||||
for i in domdict.keys():
|
for i in domain_dictionary.keys():
|
||||||
if domdict[i].meminfo is None:
|
if domain_dictionary[i].meminfo is None:
|
||||||
continue
|
continue
|
||||||
need = memneeded(domdict[i])
|
need = memory_needed(domain_dictionary[i])
|
||||||
# print 'domain' , i, 'act/pref', domdict[i].memory_actual, prefmem(domdict[i]), 'need=', need
|
# print 'domain' , i, 'act/pref', domain_dictionary[i].memory_actual, prefmem(domain_dictionary[i]), 'need=', need
|
||||||
if need < 0:
|
if need < 0:
|
||||||
donors.append(i)
|
donors.append(i)
|
||||||
else:
|
else:
|
||||||
acceptors.append(i)
|
acceptors.append(i)
|
||||||
total_mem_pref_acceptors += prefmem(domdict[i])
|
total_mem_pref_acceptors += prefmem(domain_dictionary[i])
|
||||||
total_memneeded += need
|
total_memory_needed += need
|
||||||
total_mem_pref += prefmem(domdict[i])
|
total_mem_pref += prefmem(domain_dictionary[i])
|
||||||
|
|
||||||
totalsum = xenfree - total_memneeded
|
total_available_memory = xen_free_memory - total_memory_needed
|
||||||
if totalsum > 0:
|
if total_available_memory > 0:
|
||||||
return balance_when_enough_memory(domdict, xenfree, total_mem_pref, totalsum)
|
return balance_when_enough_memory(domain_dictionary, xen_free_memory, total_mem_pref, total_available_memory)
|
||||||
else:
|
else:
|
||||||
return balance_when_low_on_memory(domdict, xenfree, total_mem_pref_acceptors, donors, acceptors)
|
return balance_when_low_on_memory(domain_dictionary, xen_free_memory, total_mem_pref_acceptors, donors, acceptors)
|
||||||
|
@ -17,7 +17,7 @@ def only_in_first_list(l1, l2):
|
|||||||
ret.append(i)
|
ret.append(i)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def get_req_node(domain_id):
|
def get_domain_meminfo_key(domain_id):
|
||||||
return '/local/domain/'+domain_id+'/memory/meminfo'
|
return '/local/domain/'+domain_id+'/memory/meminfo'
|
||||||
|
|
||||||
|
|
||||||
@ -29,27 +29,29 @@ class WatchType:
|
|||||||
class XS_Watcher:
|
class XS_Watcher:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.handle = xen.lowlevel.xs.xs()
|
self.handle = xen.lowlevel.xs.xs()
|
||||||
self.handle.watch('/vm', WatchType(XS_Watcher.dom_list_change, None))
|
self.handle.watch('/vm', WatchType(XS_Watcher.domain_list_changed, None))
|
||||||
self.watch_token_dict = {}
|
self.watch_token_dict = {}
|
||||||
|
|
||||||
def dom_list_change(self, param):
|
def domain_list_changed(self, param):
|
||||||
curr = self.handle.ls('', '/local/domain')
|
curr = self.handle.ls('', '/local/domain')
|
||||||
if curr == None:
|
if curr == None:
|
||||||
return
|
return
|
||||||
global_lock.acquire()
|
global_lock.acquire()
|
||||||
for i in only_in_first_list(curr, self.watch_token_dict.keys()):
|
for i in only_in_first_list(curr, self.watch_token_dict.keys()):
|
||||||
watch = WatchType(XS_Watcher.request, i)
|
#new domain has been created
|
||||||
|
watch = WatchType(XS_Watcher.meminfo_changed, i)
|
||||||
self.watch_token_dict[i] = watch
|
self.watch_token_dict[i] = watch
|
||||||
self.handle.watch(get_req_node(i), watch)
|
self.handle.watch(get_domain_meminfo_key(i), watch)
|
||||||
system_state.add_domain(i)
|
system_state.add_domain(i)
|
||||||
for i in only_in_first_list(self.watch_token_dict.keys(), curr):
|
for i in only_in_first_list(self.watch_token_dict.keys(), curr):
|
||||||
self.handle.unwatch(get_req_node(i), self.watch_token_dict[i])
|
#domain destroyed
|
||||||
|
self.handle.unwatch(get_domain_meminfo_key(i), self.watch_token_dict[i])
|
||||||
self.watch_token_dict.pop(i)
|
self.watch_token_dict.pop(i)
|
||||||
system_state.del_domain(i)
|
system_state.del_domain(i)
|
||||||
global_lock.release()
|
global_lock.release()
|
||||||
|
|
||||||
def request(self, domain_id):
|
def meminfo_changed(self, domain_id):
|
||||||
untrusted_meminfo_key = self.handle.read('', get_req_node(domain_id))
|
untrusted_meminfo_key = self.handle.read('', get_domain_meminfo_key(domain_id))
|
||||||
if untrusted_meminfo_key == None or untrusted_meminfo_key == '':
|
if untrusted_meminfo_key == None or untrusted_meminfo_key == '':
|
||||||
return
|
return
|
||||||
global_lock.acquire()
|
global_lock.acquire()
|
||||||
|
Loading…
Reference in New Issue
Block a user