2424import time
2525import xen .lowlevel # pylint: disable=import-error
2626from pathlib import Path
27+ from typing import Optional
2728
2829import qubes .qmemman
2930from qubes .qmemman .domainstate import DomainState
3940
4041
4142class SystemState :
42- def __init__ (self ):
43+ def __init__ (self ) -> None :
4344 self .log = logging .getLogger ("qmemman.systemstate" )
4445 self .log .debug ("SystemState()" )
4546
46- self .dom_dict = {}
47- self .xc = None
48- self .xs = None
49- self .all_phys_mem = 0
47+ self .dom_dict : dict [ str , DomainState ] = {}
48+ self .xc : xen . lowlevel . xc . xc = None
49+ self .xs : xen . lowlevel . xs . xs = None
50+ self .all_phys_mem : int = 0
5051
51- def init (self ):
52+ def init (self ) -> None :
5253 self .xc = xen .lowlevel .xc .xc ()
5354 self .xs = xen .lowlevel .xs .xs ()
5455 # Overhead of per-page Xen structures, taken from OpenStack
@@ -63,28 +64,28 @@ def init(self):
6364 except xen .lowlevel .xc .Error :
6465 pass
6566
66- def xs_wrapper (self , key , value = None ):
67+ def xs_wrapper (self , key , value = None ) -> Optional [ str ] :
6768 if value :
6869 self .xs .write ("" , key , value )
6970 return None
7071 return self .xs .read ("" , key )
7172
72- def get_xs_path (self , domid , key ):
73+ def get_xs_path (self , domid , key ) -> str :
7374 return "/local/domain/" + str (domid ) + "/memory/" + key
7475
75- def add_domain (self , domid ):
76+ def add_domain (self , domid ) -> None :
7677 self .log .debug ("add_domain(domid={!r})" .format (domid ))
7778 self .dom_dict [domid ] = DomainState (domid )
7879 # TODO: move to DomainState.__init__
7980 target_str = self .xs_wrapper (self .get_xs_path (domid , "target" ))
8081 if target_str :
8182 self .dom_dict [domid ].last_target = int (target_str ) * 1024
8283
83- def del_domain (self , domid ):
84+ def del_domain (self , domid ) -> None :
8485 self .log .debug ("del_domain(domid={!r})" .format (domid ))
8586 self .dom_dict .pop (domid )
8687
87- def get_free_xen_mem (self ):
88+ def get_free_xen_mem (self ) -> int :
8889 xen_free = int (
8990 self .xc .physinfo ()["free_memory" ] * 1024 * MEM_OVERHEAD_FACTOR
9091 )
@@ -116,7 +117,9 @@ def get_free_xen_mem(self):
116117 return xen_free - assigned_but_unused
117118
118119 # Refresh information on memory assigned to all domains
119- def refresh_mem_actual (self ):
120+ def refresh_mem_actual (self ) -> None :
121+ if self .xc is None :
122+ raise AttributeError ("self.xc has no methods" )
120123 for domain in self .xc .domain_getinfo ():
121124 domid = str (domain ["domid" ])
122125 if domid in self .dom_dict :
@@ -149,7 +152,7 @@ def refresh_mem_actual(self):
149152 # the only possible case of nonexisting memory/static-max
150153 # is dom0, see #307
151154
152- def clear_outdated_error_markers (self ):
155+ def clear_outdated_error_markers (self ) -> None :
153156 # Clear outdated errors.
154157 for _ , dom in self .dom_dict .items ():
155158 if dom .mem_used is None :
@@ -159,6 +162,7 @@ def clear_outdated_error_markers(self):
159162 # - VM request more memory than it has assigned
160163 # The second condition avoids starving a VM, even when there is
161164 # some free memory available.
165+ assert isinstance (dom .mem_actual , int )
162166 if (
163167 dom .mem_actual <= dom .last_target + XEN_FREE_MEM_LEFT / 2
164168 or dom .mem_actual < qubes .qmemman .algo .pref_mem (dom )
@@ -168,7 +172,7 @@ def clear_outdated_error_markers(self):
168172
169173 # The below works (and is fast), but then 'xm list' shows unchanged memory
170174 # value.
171- def mem_set (self , domid , val ):
175+ def mem_set (self , domid , val ) -> None :
172176 self .log .info ("mem-set domain {} to {}" .format (domid , val ))
173177 dom = self .dom_dict [domid ]
174178 dom .last_target = val
@@ -196,7 +200,7 @@ def mem_set(self, domid, val):
196200
197201 # This is called at the end of ballooning, when we have Xen free mem
198202 # already, make sure that past mem_set will not decrease Xen free mem.
199- def inhibit_balloon_up (self ):
203+ def inhibit_balloon_up (self ) -> None :
200204 self .log .debug ("inhibit_balloon_up()" )
201205 for domid , dom in self .dom_dict .items ():
202206 if (
@@ -210,10 +214,10 @@ def inhibit_balloon_up(self):
210214
211215 # Perform memory ballooning, across all domains, to add "mem_size" to Xen
212216 # free memory
213- def do_balloon (self , mem_size ):
217+ def do_balloon (self , mem_size ) -> bool :
214218 self .log .info ("do_balloon(mem_size={!r})" .format (mem_size ))
215219 niter = 0
216- prev_mem_actual = None
220+ prev_mem_actual : dict [ str , Optional [ int ]] = {}
217221
218222 for _ , dom in self .dom_dict .items ():
219223 dom .no_progress = False
@@ -244,33 +248,30 @@ def do_balloon(self, mem_size):
244248 ):
245249 return False
246250 xenfree_ring [ring_slot ] = xenfree
247- if prev_mem_actual is not None :
248- for domid , dom in prev_mem_actual .items ():
249- if prev_mem_actual [domid ] == dom .mem_actual :
250- # domain not responding to memset requests, remove it
251- # from donors
252- dom .no_progress = True
253- self .log .info (
254- "domain {} stuck at {}" .format (
255- domid , dom .mem_actual
256- )
257- )
251+ for domid , prev_mem in prev_mem_actual .items ():
252+ dom = self .dom_dict [domid ]
253+ if prev_mem == dom .mem_actual :
254+ # domain not responding to memset requests, remove it
255+ # from donors
256+ dom .no_progress = True
257+ self .log .info (
258+ "domain {} stuck at {}" .format (domid , dom .mem_actual )
259+ )
258260 memset_reqs = qubes .qmemman .algo .balloon (
259261 mem_size + XEN_FREE_MEM_LEFT - xenfree , self .dom_dict
260262 )
261263 self .log .info ("memset_reqs={!r}" .format (memset_reqs ))
262264 if len (memset_reqs ) == 0 :
263265 return False
264266 prev_mem_actual = {}
265- for req in memset_reqs :
266- dom , mem = req
267- self .mem_set (dom , mem )
268- prev_mem_actual [dom ] = self .dom_dict [dom ].mem_actual
267+ for domid , memset in memset_reqs :
268+ self .mem_set (domid , memset )
269+ prev_mem_actual [domid ] = self .dom_dict [domid ].mem_actual
269270 self .log .debug ("sleeping for {} s" .format (BALOON_DELAY ))
270271 time .sleep (BALOON_DELAY )
271272 niter = niter + 1
272273
273- def refresh_meminfo (self , domid , untrusted_meminfo_key ):
274+ def refresh_meminfo (self , domid , untrusted_meminfo_key ) -> None :
274275 self .log .debug (
275276 "refresh_meminfo(domid={}, untrusted_meminfo_key={!r})" .format (
276277 domid , untrusted_meminfo_key
@@ -284,7 +285,7 @@ def refresh_meminfo(self, domid, untrusted_meminfo_key):
284285
285286 # Is the computed balance request big enough so that we do not trash with
286287 # small adjustments.
287- def is_balance_req_significant (self , memset_reqs , xenfree ):
288+ def is_balance_req_significant (self , memset_reqs , xenfree ) -> bool :
288289 self .log .debug (
289290 "is_balance_req_significant(memset_reqs={}, xenfree={})" .format (
290291 memset_reqs , xenfree
@@ -298,19 +299,18 @@ def is_balance_req_significant(self, memset_reqs, xenfree):
298299 self .log .debug ("xenfree is too low, returning" )
299300 return True
300301
301- for req in memset_reqs :
302- dom , mem = req
303- last_target = self .dom_dict [dom ].last_target
304- mem_change = mem - last_target
302+ for domid , memset in memset_reqs :
303+ last_target = self .dom_dict [domid ].last_target
304+ mem_change = memset - last_target
305305 total_mem_transfer += abs (mem_change )
306- pref = qubes .qmemman .algo .pref_mem (self .dom_dict [dom ])
306+ pref = qubes .qmemman .algo .pref_mem (self .dom_dict [domid ])
307307
308308 if (
309309 0 < last_target < pref
310310 and mem_change > MIN_MEM_CHANGE_WHEN_UNDER_PREF
311311 ):
312312 self .log .info (
313- "dom {} is below pref, allowing balance" .format (dom )
313+ "dom {} is below pref, allowing balance" .format (domid )
314314 )
315315 return True
316316
@@ -321,7 +321,7 @@ def is_balance_req_significant(self, memset_reqs, xenfree):
321321 self .log .debug ("is_balance_req_significant return {}" .format (ret ))
322322 return ret
323323
324- def print_stats (self , xenfree , memset_reqs ):
324+ def print_stats (self , xenfree , memset_reqs ) -> None :
325325 for domid , dom in self .dom_dict .items ():
326326 if dom .mem_used is not None :
327327 self .log .info (
@@ -340,14 +340,17 @@ def print_stats(self, xenfree, memset_reqs):
340340 "stat: xenfree={} memset_reqs={}" .format (xenfree , memset_reqs )
341341 )
342342
343- def debug_stuck_balance (self , stuck_domid , memset_reqs , prev_mem_actual ):
343+ def debug_stuck_balance (
344+ self , stuck_domid , memset_reqs , prev_mem_actual
345+ ) -> None :
344346 for req in memset_reqs :
345347 domid , mem = req
346348 if domid == stuck_domid :
347349 # All donors have been processed.
348350 break
349351 dom = self .dom_dict [domid ]
350352 # Allow some small margin.
353+ assert isinstance (dom .mem_actual , int )
351354 if dom .mem_actual > dom .last_target + XEN_FREE_MEM_LEFT / 4 :
352355 # VM didn't react to memory request at all, remove from donors.
353356 if prev_mem_actual [domid ] == dom .mem_actual :
@@ -371,7 +374,7 @@ def debug_stuck_balance(self, stuck_domid, memset_reqs, prev_mem_actual):
371374 )
372375 dom .slow_memset_react = True
373376
374- def do_balance (self ):
377+ def do_balance (self ) -> None :
375378 self .log .debug ("do_balance()" )
376379 if os .path .isfile ("/var/run/qubes/do-not-membalance" ):
377380 self .log .debug ("do-not-membalance file present, returning" )
@@ -388,7 +391,7 @@ def do_balance(self):
388391
389392 self .print_stats (xenfree , memset_reqs )
390393
391- prev_mem_actual = {}
394+ prev_mem_actual : dict [ str , Optional [ int ]] = {}
392395 for domid , dom in self .dom_dict .items ():
393396 prev_mem_actual [domid ] = dom .mem_actual
394397 for req in memset_reqs :
@@ -416,6 +419,7 @@ def do_balance(self):
416419 self .debug_stuck_balance (
417420 domid , memset_reqs , prev_mem_actual
418421 )
422+ assert isinstance (dom .mem_actual , int )
419423 self .mem_set (
420424 domid ,
421425 self .get_free_xen_mem ()
0 commit comments