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 <[email protected]>
  • Loading branch information
mcasquer committed Nov 16, 2023
1 parent 9ce78ff commit 4b6071d
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 18 deletions.
6 changes: 5 additions & 1 deletion qemu/tests/cfg/numa_prealloc_threads.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
vm_thread_contexts = "tc1"
smp_fixed = 8
vcpu_maxcpus = ${smp_fixed}
vm_thread_context_cpu-affinity = "1-7"
not_preprocess = yes
variants:
- @default:
Expand All @@ -18,3 +17,8 @@
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"
75 changes: 58 additions & 17 deletions qemu/tests/numa_prealloc_threads.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@
from avocado.utils import process


def check_affinity(affinity, cmd_taskset, test):
"""
:param affinity: the cpu affinity
:param cmd_taskset: the taskset command
:param test: QEMU test object
"""
output = process.getoutput(cmd_taskset)
if not re.search(affinity, output):
test.fail("The affinities %s and %s do not match!"
% (affinity, str(output)))


@error_context.context_aware
def run(test, params, env):
"""
Expand All @@ -16,25 +28,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 = list(range(1, 8))
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 +62,49 @@ 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)
expected_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])
if cpu_affinity[0] != 0:
affinity = str(cpu_affinity[0]) + "-" + str(cpu_affinity[-1])
else:
affinity = "0"
test.log.debug("The affinity: %s and the expected_cpu_affinity: %s"
% (affinity, expected_cpu_affinity))
if expected_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, 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 a new affinity with sandbox enabled",
test.log.info)
try:
# The command is expected to fail when sandbox=on
vm.monitor.qom_set(thread_context_device_id,
"cpu-affinity",
first_cpu_affinity)
# Updates affinity with the latest value
affinity = str(first_cpu_affinity[0]) + "-" + str(first_cpu_affinity[-1])
except QMPCmdError as e:
error_context.context("The expected error message: %s and the output: %s"
% (error_msg, e.data), test.log.debug)
if not re.search(error_msg, str(e.data)):
test.fail("Cannot get expected error message: %s" % error_msg)

if operation_type != "boot_cpu_affinity":
check_affinity(affinity, cmd_taskset, test)
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))
output = process.getoutput(cmd_taskset)
if not re.search("new affinity list: %s" % second_cpu_affinity, output):
test.fail("The new affinity %s is not correct!" % second_cpu_affinity)

error_context.context("Checking QEMU main thread", test.log.info)
cmd_taskset = "taskset -c -p $(pgrep qemu)"
output = process.getoutput(cmd_taskset)
# QEMU main thread value should prevail as 0
if not re.search("current affinity list: 0", output):
test.fail("The QEMU main thread affinity has been modified!")

0 comments on commit 4b6071d

Please sign in to comment.