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