[PATCH v1 03/18] KVM: selftests/kvm_util: helper functions for vcpus and threads

From: Wei Wang
Date: Mon Oct 24 2022 - 07:38:07 EST


Add a vcpu thread field to the kvm_vcpu struct, so that each user
doesn't need to define an array of such threads on their own. The
private_data pointer is added and optionally used to hold user
specific data, and type casting to the user's data type will be
performed in the user vcpu thread's start_routine.

A couple of the helper functions are added to support vcpu related
operations:

pthread_create_with_name is provided to create general threads with
user specified name.

vcpu_thread_create is provided to create a vcpu thread with name in
"vcpu##id" format, vm_vcpu_threads_create is provided to create vcpu
threads for the vcpus that have been created for a vm. The thread
naming facilitates debugging, performance tuning, runtime pining etc.
An example is shown below reported from "top". With naming the vcpu
threads, the per-vcpu info becomes more noticeable:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4464 root 20 0 4248684 4.0g 1628 R 99.9 26.2 0:50.97 dirty_log_perf_
4467 root 20 0 4248684 4.0g 1628 R 99.9 26.2 0:50.93 vcpu0
4469 root 20 0 4248684 4.0g 1628 R 99.9 26.2 0:50.93 vcpu2
4470 root 20 0 4248684 4.0g 1628 R 99.9 26.2 0:50.94 vcpu3
4468 root 20 0 4248684 4.0g 1628 R 99.7 26.2 0:50.93 vcpu1

vm_vcpu_threads_join is provided to join all the vcpu threads.

vm_vcpu_threads_private_data_alloc is provided to allocate memory used
for user specific private data to each vcpu that have been created to
the vm.

Signed-off-by: Wei Wang <wei.w.wang@xxxxxxxxx>
---
.../testing/selftests/kvm/include/kvm_util.h | 20 ++
.../selftests/kvm/include/kvm_util_base.h | 2 +
tools/testing/selftests/kvm/lib/kvm_util.c | 172 ++++++++++++++++++
3 files changed, 194 insertions(+)

diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
index 5d5c8968fb06..036ed05e72e6 100644
--- a/tools/testing/selftests/kvm/include/kvm_util.h
+++ b/tools/testing/selftests/kvm/include/kvm_util.h
@@ -6,6 +6,7 @@
*/
#ifndef SELFTEST_KVM_UTIL_H
#define SELFTEST_KVM_UTIL_H
+#include <pthread.h>

#include "kvm_util_base.h"
#include "ucall_common.h"
@@ -14,4 +15,23 @@
for (i = 0, vcpu = vm->vcpus[0]; \
vcpu && i < KVM_MAX_VCPUS; vcpu = vm->vcpus[++i])

+void __pthread_create_with_name(pthread_t *thread, const pthread_attr_t *attr,
+ void *(*start_routine)(void *), void *arg, char *name);
+
+void pthread_create_with_name(pthread_t *thread,
+ void *(*start_routine)(void *), void *arg, char *name);
+
+void __vcpu_thread_create(struct kvm_vcpu *vcpu, const pthread_attr_t *attr,
+ void *(*start_routine)(void *), uint32_t private_data_size);
+
+void vcpu_thread_create(struct kvm_vcpu *vcpu, void *(*start_routine)(void *),
+ uint32_t private_data_size);
+
+void vm_vcpu_threads_create(struct kvm_vm *vm,
+ void *(*start_routine)(void *), uint32_t private_data_size);
+
+void vm_vcpu_threads_join(struct kvm_vm *vm);
+
+void vm_vcpu_threads_private_data_alloc(struct kvm_vm *vm, uint32_t data_size);
+
#endif /* SELFTEST_KVM_UTIL_H */
diff --git a/tools/testing/selftests/kvm/include/kvm_util_base.h b/tools/testing/selftests/kvm/include/kvm_util_base.h
index c90a9609b853..d0d6aaec0098 100644
--- a/tools/testing/selftests/kvm/include/kvm_util_base.h
+++ b/tools/testing/selftests/kvm/include/kvm_util_base.h
@@ -55,6 +55,8 @@ struct kvm_vcpu {
struct kvm_dirty_gfn *dirty_gfns;
uint32_t fetch_index;
uint32_t dirty_gfns_count;
+ pthread_t thread;
+ void *private_data;
};

struct userspace_mem_regions {
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index 1f69f5ca8356..ba3e774087fb 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -2006,3 +2006,175 @@ void __vm_get_stat(struct kvm_vm *vm, const char *stat_name, uint64_t *data,
break;
}
}
+
+/*
+ * Create a named thread with user's attribute
+ *
+ * Input Args:
+ * attr - the attribute of the thread to create
+ * start_routine - the routine to run in the thread context
+ * arg - the argument passed to start_routine
+ * name - the name of the thread
+ *
+ * Output Args:
+ * thread - the thread to be created
+ *
+ * Create a thread with a user specified name.
+ */
+void __pthread_create_with_name(pthread_t *thread, const pthread_attr_t *attr,
+ void *(*start_routine)(void *), void *arg, char *name)
+{
+ int r;
+
+ r = pthread_create(thread, NULL, start_routine, arg);
+ TEST_ASSERT(!r, "thread(%s) creation failed, r = %d", name, r);
+ r = pthread_setname_np(*thread, name);
+ TEST_ASSERT(!r, "thread(%s) setting name failed, r = %d", name, r);
+}
+
+/*
+ * Create a named thread with the default thread attribute
+ *
+ * Input Args:
+ * start_routine - the routine to run in the thread context
+ * arg - the argument passed to start_routine
+ * name - the name of the thread
+ *
+ * Output Args:
+ * thread - the thread to be created
+ *
+ * Create a thread with a user specified name and default thread attribute.
+ */
+void pthread_create_with_name(pthread_t *thread,
+ void *(*start_routine)(void *), void *arg, char *name)
+{
+ __pthread_create_with_name(thread, NULL, start_routine, arg, name);
+}
+
+/*
+ * Create a vcpu thread with user's attribute
+ *
+ * Input Args:
+ * vcpu - the vcpu for which the thread is created
+ * attr - the attribute of the vcpu thread
+ * start_routine - the routine to run in the thread context
+ * private_data_size - the size of the user's per-vcpu private_data
+ *
+ * Output Args:
+ * None
+ *
+ * Create a vcpu thread with user provided attribute and the name in
+ * "vcpu-##id" format.
+ */
+void __vcpu_thread_create(struct kvm_vcpu *vcpu, const pthread_attr_t *attr,
+ void *(*start_routine)(void *), uint32_t private_data_size)
+{
+ char vcpu_name[16];
+
+ if (private_data_size) {
+ vcpu->private_data = calloc(1, private_data_size);
+ TEST_ASSERT(vcpu->private_data, "%s: failed", __func__);
+ }
+
+ sprintf(vcpu_name, "vcpu-%d", vcpu->id);
+ __pthread_create_with_name(&vcpu->thread, attr,
+ start_routine, (void *)vcpu, vcpu_name);
+}
+
+/*
+ * Create a vcpu thread with the default thread attribute
+ *
+ * Input Args:
+ * vcpu - the vcpu for which the thread is created
+ * start_routine - the routine to run in the thread context
+ * private_data_size - the size of the user's per-vcpu private_data
+ *
+ * Output Args:
+ * None
+ *
+ * Create a vcpu thread with the default thread attribute and the name in
+ * "vcpu-##id" format, and allocate memory to be used as the vcpu thread's
+ * private data if private_data_size isn't 0.
+ */
+void vcpu_thread_create(struct kvm_vcpu *vcpu, void *(*start_routine)(void *),
+ uint32_t private_data_size)
+{
+ __vcpu_thread_create(vcpu, NULL, start_routine, private_data_size);
+}
+
+/*
+ * Create vcpu threads for all the vcpus that have been created for a VM
+ *
+ * Input Args:
+ * vm - the VM for which the vcpu threads are created
+ * start_routine - the routine to run in the thread context
+ * private_data_size - the size of the user's per-vcpu private_data
+ *
+ * Output Args:
+ * None
+ *
+ * Create vcpu threads for all the vcpus that have been created for the VM,
+ * and the thread name in "vcpu-##id" format. Allocate memory to each vcpu
+ * thread to be used for its private data if private_data_size isn't 0.
+ */
+void vm_vcpu_threads_create(struct kvm_vm *vm,
+ void *(*start_routine)(void *), uint32_t private_data_size)
+{
+ struct kvm_vcpu *vcpu;
+ uint32_t i;
+
+ vm_iterate_over_vcpus(vm, vcpu, i)
+ vcpu_thread_create(vcpu, start_routine, private_data_size);
+
+}
+
+/*
+ * Join the VM's vcpu threads
+ *
+ * Input Args:
+ * vm - the VM for which its vcpu threads should join
+ *
+ * Output Args:
+ * None
+ *
+ * Iterate over all the vcpus and join the threads.
+ */
+void vm_vcpu_threads_join(struct kvm_vm *vm)
+{
+ struct kvm_vcpu *vcpu;
+ void *one_failure;
+ unsigned long failures = 0;
+ int r, i;
+
+ vm_iterate_over_vcpus(vm, vcpu, i) {
+ r = pthread_join(vcpu->thread, &one_failure);
+ TEST_ASSERT(r == 0, "failed to join vcpu %d thread", i);
+ failures += (unsigned long)one_failure;
+ }
+
+ TEST_ASSERT(!failures, "%s: failed", __func__);
+}
+
+/*
+ * Allocate memory used for private data of the vm's vcpus
+ *
+ * Input Args:
+ * vm - the VM for which its vcpus will be assigned the allocated memory
+ * data_size - the size of the memory to allocate
+ *
+ * Output Args:
+ * None
+ *
+ * Allocate memory to be used for private data of each vcpu that has been
+ * created for vm.
+ */
+void vm_vcpu_threads_private_data_alloc(struct kvm_vm *vm, uint32_t data_size)
+{
+ struct kvm_vcpu *vcpu;
+ int i;
+
+ vm_iterate_over_vcpus(vm, vcpu, i) {
+ vcpu->private_data = calloc(1, data_size);
+ TEST_ASSERT(vcpu->private_data, "%s: failed", __func__);
+ }
+}
--
2.27.0