@@ -17,6 +17,7 @@ struct kvm_cpu {
struct kvm *kvm;
int vcpu_fd;
struct kvm_run *kvm_run;
+ struct kvm_cpu_task *task;
u8 is_running;
u8 paused;
@@ -4,6 +4,11 @@
#include "kvm/kvm-cpu-arch.h"
#include <stdbool.h>
+struct kvm_cpu_task {
+ void (*func)(struct kvm_cpu *vcpu, void *data);
+ void *data;
+};
+
int kvm_cpu__init(struct kvm *kvm);
int kvm_cpu__exit(struct kvm *kvm);
struct kvm_cpu *kvm_cpu__arch_init(struct kvm *kvm, unsigned long cpu_id);
@@ -23,5 +28,6 @@ void kvm_cpu__show_code(struct kvm_cpu *vcpu);
void kvm_cpu__show_registers(struct kvm_cpu *vcpu);
void kvm_cpu__show_page_tables(struct kvm_cpu *vcpu);
void kvm_cpu__arch_nmi(struct kvm_cpu *cpu);
+void kvm_cpu__run_on_all_cpus(struct kvm *kvm, struct kvm_cpu_task *task);
#endif /* KVM__KVM_CPU_H */
@@ -15,6 +15,7 @@
#define SIGKVMEXIT (SIGRTMIN + 0)
#define SIGKVMPAUSE (SIGRTMIN + 1)
+#define SIGKVMTASK (SIGRTMIN + 2)
#define KVM_PID_FILE_PATH "/.lkvm/"
#define HOME_DIR getenv("HOME")
@@ -4,9 +4,12 @@
#include "kvm/util.h"
#include "kvm/kvm.h"
#include "kvm/virtio.h"
+#include "kvm/mutex.h"
+#include "kvm/barrier.h"
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include <sys/eventfd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
@@ -52,6 +55,8 @@ static void kvm_cpu_signal_handler(int signum)
} else if (signum == SIGKVMPAUSE) {
current_kvm_cpu->paused = 1;
}
+
+ /* For SIGKVMTASK cpu->task is already set */
}
static void kvm_cpu__handle_coalesced_mmio(struct kvm_cpu *cpu)
@@ -83,6 +88,62 @@ void kvm_cpu__reboot(struct kvm *kvm)
}
}
+static DEFINE_MUTEX(task_lock);
+static int task_eventfd;
+
+static void kvm_cpu__run_task(struct kvm_cpu *cpu)
+{
+ u64 inc = 1;
+
+ pr_debug("Running task %p on cpu %lu", cpu->task, cpu->cpu_id);
+
+ /* Make sure we see the store to cpu->task */
+ rmb();
+ cpu->task->func(cpu, cpu->task->data);
+
+ /* Clear task before we signal completion */
+ cpu->task = NULL;
+ wmb();
+
+ if (write(task_eventfd, &inc, sizeof(inc)) < 0)
+ die("Failed notifying of completed task.");
+}
+
+void kvm_cpu__run_on_all_cpus(struct kvm *kvm, struct kvm_cpu_task *task)
+{
+ int i, done = 0;
+
+ pr_debug("Running task %p on all cpus", task);
+
+ mutex_lock(&task_lock);
+
+ for (i = 0; i < kvm->nrcpus; i++) {
+ if (kvm->cpus[i]->task) {
+ /* Should never happen */
+ die("CPU %d already has a task pending!", i);
+ }
+
+ kvm->cpus[i]->task = task;
+ wmb();
+
+ if (kvm->cpus[i] == current_kvm_cpu)
+ kvm_cpu__run_task(current_kvm_cpu);
+ else
+ pthread_kill(kvm->cpus[i]->thread, SIGKVMTASK);
+ }
+
+ while (done < kvm->nrcpus) {
+ u64 count;
+
+ if (read(task_eventfd, &count, sizeof(count)) < 0)
+ die("Failed reading task eventfd");
+
+ done += count;
+ }
+
+ mutex_unlock(&task_lock);
+}
+
int kvm_cpu__start(struct kvm_cpu *cpu)
{
sigset_t sigset;
@@ -94,6 +155,7 @@ int kvm_cpu__start(struct kvm_cpu *cpu)
signal(SIGKVMEXIT, kvm_cpu_signal_handler);
signal(SIGKVMPAUSE, kvm_cpu_signal_handler);
+ signal(SIGKVMTASK, kvm_cpu_signal_handler);
kvm_cpu__reset_vcpu(cpu);
@@ -111,6 +173,9 @@ int kvm_cpu__start(struct kvm_cpu *cpu)
cpu->needs_nmi = 0;
}
+ if (cpu->task)
+ kvm_cpu__run_task(cpu);
+
kvm_cpu__run(cpu);
switch (cpu->kvm_run->exit_reason) {
@@ -217,6 +282,12 @@ int kvm_cpu__init(struct kvm *kvm)
kvm->nrcpus = kvm->cfg.nrcpus;
+ task_eventfd = eventfd(0, 0);
+ if (task_eventfd < 0) {
+ pr_warning("Couldn't create task_eventfd");
+ return task_eventfd;
+ }
+
/* Alloc one pointer too many, so array ends up 0-terminated */
kvm->cpus = calloc(kvm->nrcpus + 1, sizeof(void *));
if (!kvm->cpus) {
@@ -264,6 +335,8 @@ int kvm_cpu__exit(struct kvm *kvm)
kvm->nrcpus = 0;
+ close(task_eventfd);
+
return r;
}
core_exit(kvm_cpu__exit);
@@ -15,6 +15,7 @@ struct kvm_cpu {
struct kvm *kvm; /* parent KVM */
int vcpu_fd; /* For VCPU ioctls() */
struct kvm_run *kvm_run;
+ struct kvm_cpu_task *task;
struct kvm_regs regs;
@@ -48,6 +48,7 @@ struct kvm_cpu {
struct kvm *kvm; /* parent KVM */
int vcpu_fd; /* For VCPU ioctls() */
struct kvm_run *kvm_run;
+ struct kvm_cpu_task *task;
struct kvm_regs regs;
struct kvm_sregs sregs;
@@ -18,6 +18,7 @@ struct kvm_cpu {
struct kvm *kvm; /* parent KVM */
int vcpu_fd; /* For VCPU ioctls() */
struct kvm_run *kvm_run;
+ struct kvm_cpu_task *task;
struct kvm_regs regs;
struct kvm_sregs sregs;