diff --git a/arm/include/arm-common/kvm-cpu-arch.h b/arm/include/arm-common/kvm-cpu-arch.h
index 329979a..8a6a6e7 100644
--- a/arm/include/arm-common/kvm-cpu-arch.h
+++ b/arm/include/arm-common/kvm-cpu-arch.h
@@ -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;
diff --git a/include/kvm/kvm-cpu.h b/include/kvm/kvm-cpu.h
index aa0cb54..4481096 100644
--- a/include/kvm/kvm-cpu.h
+++ b/include/kvm/kvm-cpu.h
@@ -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 */
diff --git a/include/kvm/kvm.h b/include/kvm/kvm.h
index 37155db..731abee 100644
--- a/include/kvm/kvm.h
+++ b/include/kvm/kvm.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")
diff --git a/kvm-cpu.c b/kvm-cpu.c
index ad4441b..4b202f8 100644
--- a/kvm-cpu.c
+++ b/kvm-cpu.c
@@ -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);
diff --git a/mips/include/kvm/kvm-cpu-arch.h b/mips/include/kvm/kvm-cpu-arch.h
index 3db7f2b..45e69f6 100644
--- a/mips/include/kvm/kvm-cpu-arch.h
+++ b/mips/include/kvm/kvm-cpu-arch.h
@@ -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;
 
diff --git a/powerpc/include/kvm/kvm-cpu-arch.h b/powerpc/include/kvm/kvm-cpu-arch.h
index 01eafdf..f2bfbb5 100644
--- a/powerpc/include/kvm/kvm-cpu-arch.h
+++ b/powerpc/include/kvm/kvm-cpu-arch.h
@@ -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;
diff --git a/x86/include/kvm/kvm-cpu-arch.h b/x86/include/kvm/kvm-cpu-arch.h
index 89c8059..05e5bb6 100644
--- a/x86/include/kvm/kvm-cpu-arch.h
+++ b/x86/include/kvm/kvm-cpu-arch.h
@@ -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;
