diff options
Diffstat (limited to 'cpus-common.c')
-rw-r--r-- | cpus-common.c | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/cpus-common.c b/cpus-common.c index fda3848b7d..2005bfe41f 100644 --- a/cpus-common.c +++ b/cpus-common.c @@ -23,10 +23,12 @@ #include "sysemu/cpus.h" static QemuMutex qemu_cpu_list_lock; +static QemuCond qemu_work_cond; void qemu_init_cpu_list(void) { qemu_mutex_init(&qemu_cpu_list_lock); + qemu_cond_init(&qemu_work_cond); } void cpu_list_lock(void) @@ -81,3 +83,95 @@ void cpu_list_remove(CPUState *cpu) cpu->cpu_index = UNASSIGNED_CPU_INDEX; qemu_mutex_unlock(&qemu_cpu_list_lock); } + +struct qemu_work_item { + struct qemu_work_item *next; + run_on_cpu_func func; + void *data; + int done; + bool free; +}; + +static void queue_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi) +{ + qemu_mutex_lock(&cpu->work_mutex); + if (cpu->queued_work_first == NULL) { + cpu->queued_work_first = wi; + } else { + cpu->queued_work_last->next = wi; + } + cpu->queued_work_last = wi; + wi->next = NULL; + wi->done = false; + qemu_mutex_unlock(&cpu->work_mutex); + + qemu_cpu_kick(cpu); +} + +void do_run_on_cpu(CPUState *cpu, run_on_cpu_func func, void *data, + QemuMutex *mutex) +{ + struct qemu_work_item wi; + + if (qemu_cpu_is_self(cpu)) { + func(cpu, data); + return; + } + + wi.func = func; + wi.data = data; + wi.free = false; + + queue_work_on_cpu(cpu, &wi); + while (!atomic_mb_read(&wi.done)) { + CPUState *self_cpu = current_cpu; + + qemu_cond_wait(&qemu_work_cond, mutex); + current_cpu = self_cpu; + } +} + +void async_run_on_cpu(CPUState *cpu, run_on_cpu_func func, void *data) +{ + struct qemu_work_item *wi; + + if (qemu_cpu_is_self(cpu)) { + func(cpu, data); + return; + } + + wi = g_malloc0(sizeof(struct qemu_work_item)); + wi->func = func; + wi->data = data; + wi->free = true; + + queue_work_on_cpu(cpu, wi); +} + +void process_queued_cpu_work(CPUState *cpu) +{ + struct qemu_work_item *wi; + + if (cpu->queued_work_first == NULL) { + return; + } + + qemu_mutex_lock(&cpu->work_mutex); + while (cpu->queued_work_first != NULL) { + wi = cpu->queued_work_first; + cpu->queued_work_first = wi->next; + if (!cpu->queued_work_first) { + cpu->queued_work_last = NULL; + } + qemu_mutex_unlock(&cpu->work_mutex); + wi->func(cpu, wi->data); + qemu_mutex_lock(&cpu->work_mutex); + if (wi->free) { + g_free(wi); + } else { + atomic_mb_set(&wi->done, true); + } + } + qemu_mutex_unlock(&cpu->work_mutex); + qemu_cond_broadcast(&qemu_work_cond); +} |