From: Alexandr Moshkov <dtalexund...@yandex-team.ru> Add new tests to check the correctness of the `-overcommit memlock` option (possible values: off, on, on-fault) by using `/proc/{qemu_pid}/status` file to check in VmSize, VmRSS and VmLck values:
* if `memlock=off`, then VmLck = 0; * if `memlock=on`, then VmLck > 0 and almost all memory is resident; * if `memlock=on-fault`, then VmLck > 0 and only few memory is resident. Signed-off-by: Alexandr Moshkov <dtalexund...@yandex-team.ru> Message-ID: <20250525070737.54267-3-dtalexund...@yandex-team.ru> [thuth: improved the text in a comment] Signed-off-by: Thomas Huth <th...@redhat.com> --- tests/functional/meson.build | 1 + tests/functional/test_memlock.py | 79 ++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100755 tests/functional/test_memlock.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 52b4706cfe8..13079f58b66 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -68,6 +68,7 @@ tests_generic_system = [ 'empty_cpu_model', 'info_usernet', 'version', + 'memlock', ] tests_generic_linuxuser = [ diff --git a/tests/functional/test_memlock.py b/tests/functional/test_memlock.py new file mode 100755 index 00000000000..8910cb6da33 --- /dev/null +++ b/tests/functional/test_memlock.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# +# Functional test that check overcommit memlock options +# +# Copyright (c) Yandex Technologies LLC, 2025 +# +# Author: +# Alexandr Moshkov <dtalexund...@yandex-team.ru> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import re + +from typing import Dict + +from qemu_test import QemuSystemTest +from qemu_test import skipLockedMemoryTest + + +STATUS_VALUE_PATTERN = re.compile(r'^(\w+):\s+(\d+) kB', re.MULTILINE) + + +@skipLockedMemoryTest(2_097_152) # 2GB +class MemlockTest(QemuSystemTest): + """ + Runs a guest with memlock options. + Then verify, that this options is working correctly + by checking the smaps of the QEMU process. + """ + + def common_vm_setup_with_memlock(self, memlock): + self.vm.add_args('-overcommit', f'mem-lock={memlock}') + self.vm.launch() + + def test_memlock_off(self): + self.common_vm_setup_with_memlock('off') + + status = self.get_process_status_values(self.vm.get_pid()) + + self.assertTrue(status['VmLck'] == 0) + + def test_memlock_on(self): + self.common_vm_setup_with_memlock('on') + + status = self.get_process_status_values(self.vm.get_pid()) + + # VmLck > 0 kB and almost all memory is resident + self.assertTrue(status['VmLck'] > 0) + self.assertTrue(status['VmRSS'] >= status['VmSize'] * 0.70) + + def test_memlock_onfault(self): + self.common_vm_setup_with_memlock('on-fault') + + status = self.get_process_status_values(self.vm.get_pid()) + + # VmLck > 0 kB and only few memory is resident + self.assertTrue(status['VmLck'] > 0) + self.assertTrue(status['VmRSS'] <= status['VmSize'] * 0.30) + + def get_process_status_values(self, pid: int) -> Dict[str, int]: + result = {} + raw_status = self._get_raw_process_status(pid) + + for line in raw_status.split('\n'): + if m := STATUS_VALUE_PATTERN.match(line): + result[m.group(1)] = int(m.group(2)) + + return result + + def _get_raw_process_status(self, pid: int) -> str: + try: + with open(f'/proc/{pid}/status', 'r') as f: + return f.read() + except FileNotFoundError: + self.skipTest("Can't open status file of the process") + + +if __name__ == '__main__': + MemlockTest.main() -- 2.49.0