Skip to content

Commit 73622c2

Browse files
committed
Annotate types
1 parent cf6ea36 commit 73622c2

File tree

4 files changed

+84
-68
lines changed

4 files changed

+84
-68
lines changed

qubes/qmemman/algo.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#
2020

2121
import logging
22+
from typing import Optional
2223

2324
# These defaults can be overridden by QMemmanServer with values from config
2425
# file.
@@ -33,7 +34,7 @@
3334
log = logging.getLogger("qmemman.daemon.algo")
3435

3536

36-
def sanitize_and_parse_meminfo(untrusted_meminfo):
37+
def sanitize_and_parse_meminfo(untrusted_meminfo) -> Optional[int]:
3738
# Untrusted meminfo size is read from xenstore, thus its size is limited
3839
# and splits do not require excessive memory.
3940
if not untrusted_meminfo:
@@ -43,14 +44,14 @@ def sanitize_and_parse_meminfo(untrusted_meminfo):
4344
return int(untrusted_meminfo) * 1024
4445

4546

46-
def refresh_meminfo_for_domain(domain, untrusted_xenstore_key):
47+
def refresh_meminfo_for_domain(domain, untrusted_xenstore_key) -> None:
4748
"""
4849
Called when a domain updates its 'meminfo' xenstore key.
4950
"""
5051
domain.mem_used = sanitize_and_parse_meminfo(untrusted_xenstore_key)
5152

5253

53-
def pref_mem(domain):
54+
def pref_mem(domain) -> int:
5455
# As dom0 must have large cache for vbds, give it a special boost.
5556
mem_used = domain.mem_used * CACHE_FACTOR
5657
if domain.domid == "0":
@@ -59,7 +60,7 @@ def pref_mem(domain):
5960
return int(max(min(mem_used, domain.mem_max), MIN_PREFMEM))
6061

6162

62-
def needed_mem(domain):
63+
def needed_mem(domain) -> int:
6364
# Do not change. In balance(), "distribute total_available_mem
6465
# proportionally to pref_mem" relies on this exact formula.
6566
ret = pref_mem(domain) - domain.mem_actual
@@ -69,7 +70,7 @@ def needed_mem(domain):
6970
# Prepare list of (domain, mem_target) pairs that need to be passed to "xm
7071
# memset" equivalent in order to obtain "mem_size".
7172
# Returns empty list when the request cannot be satisfied.
72-
def balloon(mem_size, dom_dict):
73+
def balloon(mem_size, dom_dict) -> list:
7374
log.debug(
7475
"balloon(mem_size={!r}, dom_dict={!r})".format(mem_size, dom_dict)
7576
)
@@ -212,7 +213,7 @@ def balance_when_low_on_mem(
212213
# Get memory information.
213214
# Called before and after domain balances.
214215
# Return a dictionary of various memory data points.
215-
def mem_info(xen_free_mem, dom_dict):
216+
def mem_info(xen_free_mem, dom_dict) -> dict:
216217
log.debug(
217218
"mem_info(xen_free_mem={!r}, dom_dict={!r})".format(
218219
xen_free_mem, dom_dict
@@ -264,7 +265,7 @@ def mem_info(xen_free_mem, dom_dict):
264265
# Called when one of domains update its 'meminfo' xenstore key.
265266
# Return the list of (domain, mem_target) pairs to be passed to "xm memset"
266267
# equivalent
267-
def balance(xen_free_mem, dom_dict):
268+
def balance(xen_free_mem, dom_dict) -> dict:
268269
log.debug(
269270
"balance(xen_free_mem={!r}, dom_dict={!r})".format(
270271
xen_free_mem, dom_dict

qubes/qmemman/client.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@
1919

2020
import socket
2121
import fcntl
22+
from typing import Optional
2223

2324

2425
class QMemmanClient:
25-
def __init__(self):
26-
self.sock = None
26+
def __init__(self) -> None:
27+
self.sock: Optional[socket.socket] = None
2728

28-
def request_mem(self, amount):
29+
def request_mem(self, amount) -> bool:
2930
self.sock = socket.socket(socket.AF_UNIX)
3031
flags = fcntl.fcntl(self.sock.fileno(), fcntl.F_GETFD)
3132
flags |= fcntl.FD_CLOEXEC
@@ -35,5 +36,6 @@ def request_mem(self, amount):
3536
received = self.sock.recv(1024).strip()
3637
return bool(received == b"OK")
3738

38-
def close(self):
39+
def close(self) -> None:
40+
assert isinstance(self.sock, socket.socket)
3941
self.sock.close()

qubes/qmemman/domainstate.py

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,29 @@
1818
# You should have received a copy of the GNU General Public
1919
# License along with this library; if not, see <https://www.gnu.org/licenses/>.
2020

21+
from typing import Optional
22+
2123

2224
class DomainState: # pylint: disable=too-few-public-methods
23-
def __init__(self, domid):
24-
self.mem_current = 0 # the current memory size
25-
self.mem_actual = None # the current memory allocation (what VM
26-
# is using or can use at any time)
27-
self.mem_max = None # the maximum memory size
28-
self.mem_used = None # used memory, computed based on meminfo
29-
self.domid = domid # domain id
30-
self.last_target = 0 # the last memset target
31-
self.use_hotplug = False # use memory hotplug for mem-set
32-
self.no_progress = False # no react to memset
33-
self.slow_memset_react = False # slow react to memset (after few
34-
# tries still above target)
25+
def __init__(self, domid) -> None:
26+
# Current memory size.
27+
self.mem_current: int = 0
28+
# Current memory allocation (what VM is using or can use at any time).
29+
self.mem_actual: Optional[int] = None
30+
# Maximum memory size.
31+
self.mem_max: Optional[int] = None
32+
# Used memory, computed based on meminfo.
33+
self.mem_used: Optional[int] = None
34+
# Domain ID.
35+
self.domid: str = domid
36+
# Last memset target.
37+
self.last_target: int = 0
38+
# Use memory hotplug for mem-set.
39+
self.use_hotplug: bool = False
40+
# No reaction to memset.
41+
self.no_progress: bool = False
42+
# Slow react to memset (after few tries still above target).
43+
self.slow_memset_react: bool = False
3544

36-
def __repr__(self):
45+
def __repr__(self) -> str:
3746
return self.__dict__.__repr__()

qubes/qmemman/systemstate.py

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import time
2525
import xen.lowlevel # pylint: disable=import-error
2626
from pathlib import Path
27+
from typing import Optional
2728

2829
import qubes.qmemman
2930
from qubes.qmemman.domainstate import DomainState
@@ -39,16 +40,16 @@
3940

4041

4142
class 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

Comments
 (0)