|
50 | 50 | #include <linux/bsearch.h>
|
51 | 51 | #include <linux/io.h>
|
52 | 52 | #include <linux/lockdep.h>
|
| 53 | +#include <linux/kthread.h> |
53 | 54 |
|
54 | 55 | #include <asm/processor.h>
|
55 | 56 | #include <asm/ioctl.h>
|
@@ -4371,3 +4372,86 @@ void kvm_exit(void)
|
4371 | 4372 | kvm_vfio_ops_exit();
|
4372 | 4373 | }
|
4373 | 4374 | EXPORT_SYMBOL_GPL(kvm_exit);
|
| 4375 | + |
| 4376 | +struct kvm_vm_worker_thread_context { |
| 4377 | + struct kvm *kvm; |
| 4378 | + struct task_struct *parent; |
| 4379 | + struct completion init_done; |
| 4380 | + kvm_vm_thread_fn_t thread_fn; |
| 4381 | + uintptr_t data; |
| 4382 | + int err; |
| 4383 | +}; |
| 4384 | + |
| 4385 | +static int kvm_vm_worker_thread(void *context) |
| 4386 | +{ |
| 4387 | + /* |
| 4388 | + * The init_context is allocated on the stack of the parent thread, so |
| 4389 | + * we have to locally copy anything that is needed beyond initialization |
| 4390 | + */ |
| 4391 | + struct kvm_vm_worker_thread_context *init_context = context; |
| 4392 | + struct kvm *kvm = init_context->kvm; |
| 4393 | + kvm_vm_thread_fn_t thread_fn = init_context->thread_fn; |
| 4394 | + uintptr_t data = init_context->data; |
| 4395 | + int err; |
| 4396 | + |
| 4397 | + err = kthread_park(current); |
| 4398 | + /* kthread_park(current) is never supposed to return an error */ |
| 4399 | + WARN_ON(err != 0); |
| 4400 | + if (err) |
| 4401 | + goto init_complete; |
| 4402 | + |
| 4403 | + err = cgroup_attach_task_all(init_context->parent, current); |
| 4404 | + if (err) { |
| 4405 | + kvm_err("%s: cgroup_attach_task_all failed with err %d\n", |
| 4406 | + __func__, err); |
| 4407 | + goto init_complete; |
| 4408 | + } |
| 4409 | + |
| 4410 | + set_user_nice(current, task_nice(init_context->parent)); |
| 4411 | + |
| 4412 | +init_complete: |
| 4413 | + init_context->err = err; |
| 4414 | + complete(&init_context->init_done); |
| 4415 | + init_context = NULL; |
| 4416 | + |
| 4417 | + if (err) |
| 4418 | + return err; |
| 4419 | + |
| 4420 | + /* Wait to be woken up by the spawner before proceeding. */ |
| 4421 | + kthread_parkme(); |
| 4422 | + |
| 4423 | + if (!kthread_should_stop()) |
| 4424 | + err = thread_fn(kvm, data); |
| 4425 | + |
| 4426 | + return err; |
| 4427 | +} |
| 4428 | + |
| 4429 | +int kvm_vm_create_worker_thread(struct kvm *kvm, kvm_vm_thread_fn_t thread_fn, |
| 4430 | + uintptr_t data, const char *name, |
| 4431 | + struct task_struct **thread_ptr) |
| 4432 | +{ |
| 4433 | + struct kvm_vm_worker_thread_context init_context = {}; |
| 4434 | + struct task_struct *thread; |
| 4435 | + |
| 4436 | + *thread_ptr = NULL; |
| 4437 | + init_context.kvm = kvm; |
| 4438 | + init_context.parent = current; |
| 4439 | + init_context.thread_fn = thread_fn; |
| 4440 | + init_context.data = data; |
| 4441 | + init_completion(&init_context.init_done); |
| 4442 | + |
| 4443 | + thread = kthread_run(kvm_vm_worker_thread, &init_context, |
| 4444 | + "%s-%d", name, task_pid_nr(current)); |
| 4445 | + if (IS_ERR(thread)) |
| 4446 | + return PTR_ERR(thread); |
| 4447 | + |
| 4448 | + /* kthread_run is never supposed to return NULL */ |
| 4449 | + WARN_ON(thread == NULL); |
| 4450 | + |
| 4451 | + wait_for_completion(&init_context.init_done); |
| 4452 | + |
| 4453 | + if (!init_context.err) |
| 4454 | + *thread_ptr = thread; |
| 4455 | + |
| 4456 | + return init_context.err; |
| 4457 | +} |
0 commit comments