Skip to content

Commit

Permalink
numa_prealloc_threads: Runtime cpu-affinity handling
Browse files Browse the repository at this point in the history
Modifies existing numa preallocation threads case to
also cover the cpu-affinity handling. That means setting
new affinities, inside and outside from QEMU. Finally
it checks that the QEMU main thread remains untouched.

Signed-off-by: mcasquer <mcasquer@redhat.com>
  • Loading branch information
mcasquer committed Nov 30, 2023
1 parent 3b7cf22 commit 190ca62
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 22 deletions.
7 changes: 6 additions & 1 deletion qemu/tests/cfg/numa_prealloc_threads.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@
vm_thread_contexts = "tc1"
smp_fixed = 8
vcpu_maxcpus = ${smp_fixed}
vm_thread_context_cpu-affinity = "1-7"
not_preprocess = yes
first_cpu-affinity = "1-7"
variants:
- @default:
qemu_sandbox = on
qemu_sandbox_resourcecontrol = deny
sandbox_error_message = "Setting CPU affinity failed: Operation not permitted"
- sandbox_off:
qemu_sandbox = off
variants operation:
- @default:
second_cpu-affinity = "1-3"
- boot_cpu_affinity:
vm_thread_context_cpu-affinity = "1-7"
102 changes: 81 additions & 21 deletions qemu/tests/numa_prealloc_threads.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,44 @@
import re

from avocado.utils import cpu

from virttest import env_process
from virttest import error_context
from virttest.qemu_monitor import QMPCmdError
from avocado.utils import process


def check_affinity(affinity, cmd_taskset, stage, test):
"""
:param affinity: the cpu affinity
:param cmd_taskset: the taskset command
:param stage: the new or current affinity
:param test: QEMU test object
"""
output = process.getoutput(cmd_taskset)
actual_affinity = re.search("%s affinity list: (%s)" % (stage, affinity),
output).group(1)
if actual_affinity != affinity:
test.fail("Expect %s cpu affinity '%s', but get '%s'"
% (stage, affinity, actual_affinity))


def convert_affinity(affinity):
"""
convert the cpu affinitys between list (ex: [1, 2, 3])
and qemu-kvm command line style (ex: 1-3)
"""
if isinstance(affinity, str):
start, end = affinity.split('-')
output = list(range(int(start), int(end) + 1))
elif isinstance(affinity, list):
if len(affinity) == 1:
output = str(affinity[0])
else:
output = "%s-%s" % (affinity[0], affinity[-1])
return output


@error_context.context_aware
def run(test, params, env):
"""
Expand All @@ -16,25 +48,32 @@ def run(test, params, env):
3) Check the affinity obtained from QEMU is correct
4) With sandbox enabled, try to change the cpu-affinity
and handle the error
5) Set externally a new CPU affinity
6) Check QEMU main thread remains untouched
:param test: QEMU test object
:param params: Dictionary with the test parameters
:param env: Dictionary with test environment
"""

test.log.info("Check host CPUs number")
error_context.base_context("Check host CPUs number", test.log.info)
host_cpus = int(cpu.online_count())
smp_fixed = params.get_numeric("smp_fixed")
if host_cpus < smp_fixed:
test.cancel("The host only has %d CPUs, it needs at least %d!" % (host_cpus, smp_fixed))
test.cancel("The host only has %d CPUs, it needs at least %d!"
% (host_cpus, smp_fixed))

params['not_preprocess'] = "no"
first_cpu_affinity = params.get("first_cpu-affinity")
second_cpu_affinity = params.get("second_cpu-affinity")
operation_type = params.get("operation")
timeout = params.get_numeric("login_timeout", 1000)
env_process.preprocess(test, params, env)
vm = env.get_vm(params["main_vm"])
vm.verify_alive()
vm.wait_for_login(timeout=timeout)

error_context.context("Obtain the thread ID", test.log.info)
thread_context_device = vm.devices.get_by_params({"backend": "thread-context"})[0]
thread_context_device_id = thread_context_device.get_param("id")
error_msg = params.get("sandbox_error_message", "")
Expand All @@ -43,27 +82,48 @@ def run(test, params, env):
if not thread_id:
test.fail("No thread-id setted.")

expected_cpu_affinity = thread_context_device.get_param("cpu-affinity")
error_context.context("Check the CPU affinity", test.log.info)
qemu_cpu_affinity = thread_context_device.get_param("cpu-affinity", "0")
cpu_affinity = vm.monitor.qom_get(thread_context_device_id, "cpu-affinity")
affinity = str(cpu_affinity[0]) + "-" + str(cpu_affinity[-1])
test.log.debug("The affinity: %s and the expected_cpu_affinity: %s"
% (affinity, expected_cpu_affinity))
if expected_cpu_affinity != affinity:
affinity = convert_affinity(cpu_affinity)
test.log.debug("The affinity: %s and the qemu_cpu_affinity: %s"
% (affinity, qemu_cpu_affinity))
if qemu_cpu_affinity != affinity:
test.fail("Test and QEMU cpu-affinity does not match!")

cmd_taskset = "taskset -c -p " + str(thread_id)
output = process.getoutput(cmd_taskset)
if not re.search(affinity, output):
test.fail("The affinities %s and %s do not match!"
% (affinity, str(output)))
cmd_taskset = "taskset -c -p %s" % thread_id
check_affinity(affinity, cmd_taskset, "current", test)

sandbox = params.get("qemu_sandbox", "on")
if sandbox == "on":
try:
# The command is expected to fail
vm.monitor.qom_set(thread_context_device_id, "cpu-affinity", cpu_affinity)
except QMPCmdError as e:
test.log.debug("The expected error message: %s and the output: %s"
% (error_msg, e.data))
if not re.search(error_msg, str(e.data)):
test.fail("Can not get expected error message: %s" % error_msg)

error_context.base_context("Setting cpu-affinity: %s" % first_cpu_affinity,
test.log.info)
try:
vm.monitor.qom_set(thread_context_device_id,
"cpu-affinity",
convert_affinity(first_cpu_affinity))
except QMPCmdError as e:
if sandbox == "off":
test.fail("Set cpu-affinity '%s' failed as: %s"
% (first_cpu_affinity, str(e.data)))
if not re.search(error_msg, str(e.data)):
test.fail("Cannot get expected error message: %s" % error_msg)
test.log.debug("Get the expected error message: %s" % error_msg)
else:
if sandbox == "on":
test.fail("Set cpu-affinity should fail when sandbox=on")
affinity = first_cpu_affinity
check_affinity(affinity, cmd_taskset, "current", test)

if operation_type != "boot_cpu_affinity":
error_context.base_context("Set externally a new CPU affinity",
test.log.info)
cmd_taskset = "taskset -c -p %s %s" % (second_cpu_affinity,
str(thread_id))
error_context.context("Verify the new cpu-affinity", test.log.info)
check_affinity(second_cpu_affinity, cmd_taskset, "new", test)

error_context.context("Checking QEMU main thread remains untouched",
test.log.info)
cmd_taskset = "taskset -c -p %s" % vm.get_pid()
check_affinity(qemu_cpu_affinity, cmd_taskset, "current", test)

0 comments on commit 190ca62

Please sign in to comment.