master
py 83 lines 2.4 KB
Raw
1 #!/usr/bin/env python3
2 #
3 # Functional test that check overcommit memlock options
4 #
5 # Copyright (c) Yandex Technologies LLC, 2025
6 #
7 # Author:
8 # Alexandr Moshkov <dtalexundeer@yandex-team.ru>
9 #
10 # SPDX-License-Identifier: GPL-2.0-or-later
11
12 import re
13
14 from typing import Dict
15
16 from qemu_test import QemuSystemTest
17 from qemu_test import skipLockedMemoryTest, skipUnlessOperatingSystem
18
19
20 STATUS_VALUE_PATTERN = re.compile(r'^(\w+):\s+(\d+) kB', re.MULTILINE)
21
22
23 @skipUnlessOperatingSystem('Linux')
24 @skipLockedMemoryTest(2_097_152) # 2GB
25 class MemlockTest(QemuSystemTest):
26 """
27 Runs a guest with memlock options.
28 Then verify, that this options is working correctly
29 by checking the status file of the QEMU process.
30 """
31
32 def common_vm_setup_with_memlock(self, memlock):
33 self.vm.add_args('-overcommit', f'mem-lock={memlock}')
34 self.vm.launch()
35
36 def test_memlock_off(self):
37 self.common_vm_setup_with_memlock('off')
38
39 status = self.get_process_status_values(self.vm.get_pid())
40
41 # libgcrypt may mlock a few pages
42 self.assertTrue(status['VmLck'] < 32)
43
44 def test_memlock_on(self):
45 self.common_vm_setup_with_memlock('on')
46
47 status = self.get_process_status_values(self.vm.get_pid())
48
49 # VmLck > 0 kB and almost all memory is resident
50 self.assertTrue(status['VmLck'] > 0)
51 self.assertTrue(status['VmRSS'] >= status['VmSize'] * 0.70)
52
53 def test_memlock_onfault(self):
54 self.common_vm_setup_with_memlock('on-fault')
55
56 status = self.get_process_status_values(self.vm.get_pid())
57
58 # VmLck > 0 kB and only few memory is resident
59 self.assertTrue(status['VmLck'] > 0)
60 self.assertTrue(status['VmRSS'] <= status['VmSize'] * 0.30)
61
62 def get_process_status_values(self, pid: int) -> Dict[str, int]:
63 result = {}
64 raw_status = self._get_raw_process_status(pid)
65
66 for line in raw_status.split('\n'):
67 if m := STATUS_VALUE_PATTERN.match(line):
68 result[m.group(1)] = int(m.group(2))
69
70 return result
71
72 def _get_raw_process_status(self, pid: int) -> str:
73 status = None
74 try:
75 with open(f'/proc/{pid}/status', 'r', encoding="ascii") as f:
76 status = f.read()
77 except FileNotFoundError:
78 self.skipTest("Can't open status file of the process")
79 return status
80
81
82 if __name__ == '__main__':
83 MemlockTest.main()