| From b62c3081aeb94c9264405a51df86c24583454490 Mon Sep 17 00:00:00 2001 |
| From: Junaid Shahid <junaids@google.com> |
| Date: Mon, 4 Nov 2019 12:22:02 +0100 |
| Subject: [PATCH] kvm: Add helper function for creating VM worker threads |
| |
| commit c57c80467f90e5504c8df9ad3555d2c78800bf94 upstream. |
| |
| Add a function to create a kernel thread associated with a given VM. In |
| particular, it ensures that the worker thread inherits the priority and |
| cgroups of the calling thread. |
| |
| Signed-off-by: Junaid Shahid <junaids@google.com> |
| Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h |
| index 09c27a7a7762..5fe864abf541 100644 |
| --- a/include/linux/kvm_host.h |
| +++ b/include/linux/kvm_host.h |
| @@ -1379,4 +1379,10 @@ static inline int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu) |
| } |
| #endif /* CONFIG_HAVE_KVM_VCPU_RUN_PID_CHANGE */ |
| |
| +typedef int (*kvm_vm_thread_fn_t)(struct kvm *kvm, uintptr_t data); |
| + |
| +int kvm_vm_create_worker_thread(struct kvm *kvm, kvm_vm_thread_fn_t thread_fn, |
| + uintptr_t data, const char *name, |
| + struct task_struct **thread_ptr); |
| + |
| #endif |
| diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c |
| index 598083ca5009..e9c1aa052bf5 100644 |
| --- a/virt/kvm/kvm_main.c |
| +++ b/virt/kvm/kvm_main.c |
| @@ -50,6 +50,7 @@ |
| #include <linux/bsearch.h> |
| #include <linux/io.h> |
| #include <linux/lockdep.h> |
| +#include <linux/kthread.h> |
| |
| #include <asm/processor.h> |
| #include <asm/ioctl.h> |
| @@ -4370,3 +4371,86 @@ void kvm_exit(void) |
| kvm_vfio_ops_exit(); |
| } |
| EXPORT_SYMBOL_GPL(kvm_exit); |
| + |
| +struct kvm_vm_worker_thread_context { |
| + struct kvm *kvm; |
| + struct task_struct *parent; |
| + struct completion init_done; |
| + kvm_vm_thread_fn_t thread_fn; |
| + uintptr_t data; |
| + int err; |
| +}; |
| + |
| +static int kvm_vm_worker_thread(void *context) |
| +{ |
| + /* |
| + * The init_context is allocated on the stack of the parent thread, so |
| + * we have to locally copy anything that is needed beyond initialization |
| + */ |
| + struct kvm_vm_worker_thread_context *init_context = context; |
| + struct kvm *kvm = init_context->kvm; |
| + kvm_vm_thread_fn_t thread_fn = init_context->thread_fn; |
| + uintptr_t data = init_context->data; |
| + int err; |
| + |
| + err = kthread_park(current); |
| + /* kthread_park(current) is never supposed to return an error */ |
| + WARN_ON(err != 0); |
| + if (err) |
| + goto init_complete; |
| + |
| + err = cgroup_attach_task_all(init_context->parent, current); |
| + if (err) { |
| + kvm_err("%s: cgroup_attach_task_all failed with err %d\n", |
| + __func__, err); |
| + goto init_complete; |
| + } |
| + |
| + set_user_nice(current, task_nice(init_context->parent)); |
| + |
| +init_complete: |
| + init_context->err = err; |
| + complete(&init_context->init_done); |
| + init_context = NULL; |
| + |
| + if (err) |
| + return err; |
| + |
| + /* Wait to be woken up by the spawner before proceeding. */ |
| + kthread_parkme(); |
| + |
| + if (!kthread_should_stop()) |
| + err = thread_fn(kvm, data); |
| + |
| + return err; |
| +} |
| + |
| +int kvm_vm_create_worker_thread(struct kvm *kvm, kvm_vm_thread_fn_t thread_fn, |
| + uintptr_t data, const char *name, |
| + struct task_struct **thread_ptr) |
| +{ |
| + struct kvm_vm_worker_thread_context init_context = {}; |
| + struct task_struct *thread; |
| + |
| + *thread_ptr = NULL; |
| + init_context.kvm = kvm; |
| + init_context.parent = current; |
| + init_context.thread_fn = thread_fn; |
| + init_context.data = data; |
| + init_completion(&init_context.init_done); |
| + |
| + thread = kthread_run(kvm_vm_worker_thread, &init_context, |
| + "%s-%d", name, task_pid_nr(current)); |
| + if (IS_ERR(thread)) |
| + return PTR_ERR(thread); |
| + |
| + /* kthread_run is never supposed to return NULL */ |
| + WARN_ON(thread == NULL); |
| + |
| + wait_for_completion(&init_context.init_done); |
| + |
| + if (!init_context.err) |
| + *thread_ptr = thread; |
| + |
| + return init_context.err; |
| +} |
| -- |
| 2.7.4 |
| |