Re: [Qemu-devel] [RFC 7/8] cpu-exec-common: Introduce async_safe_run_on_cpu()
On 01/07/16 19:29, Alvise Rigo wrote: > Hi Sergey, > > On Mon, Jun 20, 2016 at 12:28 AM, Sergey Fedorov >wrote: >> diff --git a/cpu-exec-common.c b/cpu-exec-common.c >> index 8184e0662cbd..3056324738f8 100644 >> --- a/cpu-exec-common.c >> +++ b/cpu-exec-common.c >> @@ -25,6 +25,7 @@ >> >> bool exit_request; >> CPUState *tcg_current_cpu; >> +int tcg_pending_cpus; >> >> /* exit the current TB, but without causing any exception to be raised */ >> void cpu_loop_exit_noexc(CPUState *cpu) >> @@ -78,6 +79,15 @@ void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) >> siglongjmp(cpu->jmp_env, 1); >> } >> >> +static int safe_work_pending; >> + >> +void wait_safe_cpu_work(void) >> +{ >> +while (atomic_mb_read(_work_pending) > 0) { >> +wait_cpu_work(); >> +} >> +} >> + > Is this piece of code deadlock-safe once we are in mttcg mode? It is supposed to be deadlock-safe. > What happens when two threads call simultaneously async_safe_run_on_cpu? > In this case each thread will roughly: - exit its execution loop; - take BQL; - decrement 'tcg_pending_cpus', signal 'qemu_work_cond' if zero; - start processing its work queue; - encountering safe work wait on 'qemu_work_cond' for 'tcg_pending_cpus' to become zero; - reacquire BQL; - process the safe work; - decrement 'safe_work_pending', signal 'qemu_work_cond' if zero; - when finished processing work, wait on 'qemu_work_cond' for 'safe_work_pending' to become zero; - reacquire BQL; - continue execution (releasing BQL). Hope this will help. Kind regards, Sergey.
Re: [Qemu-devel] [RFC 7/8] cpu-exec-common: Introduce async_safe_run_on_cpu()
Hi Sergey, On Mon, Jun 20, 2016 at 12:28 AM, Sergey Fedorovwrote: > > From: Sergey Fedorov > > This patch is based on the ideas found in work of KONRAD Frederic [1], > Alex Bennée [2], and Alvise Rigo [3]. > > This mechanism allows to perform an operation safely in a quiescent > state. Quiescent state means: (1) no vCPU is running and (2) BQL in > system-mode or 'exclusive_lock' in user-mode emulation is held while > performing the operation. This functionality is required e.g. for > performing translation buffer flush safely in multi-threaded user-mode > emulation. > > The existing CPU work queue is used to schedule such safe operations. A > new 'safe' flag is added into struct qemu_work_item to designate the > special requirements of the safe work. An operation in a quiescent sate > can be scheduled by using async_safe_run_on_cpu() function which is > actually the same as sync_run_on_cpu() except that it marks the queued > work item with the 'safe' flag set to true. Given this flag set > queue_work_on_cpu() atomically increments 'safe_work_pending' global > counter and kicks all the CPUs instead of just the target CPU as in case > of normal CPU work. This allows to force other CPUs to exit their > execution loops and wait in wait_safe_cpu_work() function for the safe > work to finish. When a CPU drains its work queue, if it encounters a > work item marked as safe, it first waits for other CPUs to exit their > execution loops, then called the work item function, and finally > decrements 'safe_work_pending' counter with signalling other CPUs to let > them continue execution as soon as all pending safe work items have been > processed. The 'tcg_pending_cpus' protected by 'exclusive_lock' in > user-mode or by 'qemu_global_mutex' in system-mode emulation is used to > determine if there is any CPU run and wait for it to exit the execution > loop. The fairness of all the CPU work queues is ensured by draining all > the pending safe work items before any CPU can run. > > [1] http://lists.nongnu.org/archive/html/qemu-devel/2015-08/msg01128.html > [2] http://lists.nongnu.org/archive/html/qemu-devel/2016-04/msg02531.html > [3] http://lists.nongnu.org/archive/html/qemu-devel/2016-05/msg04792.html > > Signed-off-by: Sergey Fedorov > Signed-off-by: Sergey Fedorov > --- > cpu-exec-common.c | 45 - > cpus.c | 16 > include/exec/exec-all.h | 2 ++ > include/qom/cpu.h | 14 ++ > linux-user/main.c | 2 +- > 5 files changed, 77 insertions(+), 2 deletions(-) > > diff --git a/cpu-exec-common.c b/cpu-exec-common.c > index 8184e0662cbd..3056324738f8 100644 > --- a/cpu-exec-common.c > +++ b/cpu-exec-common.c > @@ -25,6 +25,7 @@ > > bool exit_request; > CPUState *tcg_current_cpu; > +int tcg_pending_cpus; > > /* exit the current TB, but without causing any exception to be raised */ > void cpu_loop_exit_noexc(CPUState *cpu) > @@ -78,6 +79,15 @@ void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) > siglongjmp(cpu->jmp_env, 1); > } > > +static int safe_work_pending; > + > +void wait_safe_cpu_work(void) > +{ > +while (atomic_mb_read(_work_pending) > 0) { > +wait_cpu_work(); > +} > +} > + Is this piece of code deadlock-safe once we are in mttcg mode? What happens when two threads call simultaneously async_safe_run_on_cpu? Thank you, alvise > > static void queue_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi) > { > qemu_mutex_lock(>work_mutex); > @@ -89,9 +99,18 @@ static void queue_work_on_cpu(CPUState *cpu, struct > qemu_work_item *wi) > cpu->queued_work_last = wi; > wi->next = NULL; > wi->done = false; > +if (wi->safe) { > +atomic_inc(_work_pending); > +} > qemu_mutex_unlock(>work_mutex); > > -qemu_cpu_kick(cpu); > +if (!wi->safe) { > +qemu_cpu_kick(cpu); > +} else { > +CPU_FOREACH(cpu) { > +qemu_cpu_kick(cpu); > +} > +} > } > > void run_on_cpu(CPUState *cpu, run_on_cpu_func func, void *data) > @@ -106,6 +125,7 @@ void run_on_cpu(CPUState *cpu, run_on_cpu_func func, void > *data) > wi.func = func; > wi.data = data; > wi.free = false; > +wi.safe = false; > > queue_work_on_cpu(cpu, ); > while (!atomic_mb_read()) { > @@ -129,6 +149,20 @@ void async_run_on_cpu(CPUState *cpu, run_on_cpu_func > func, void *data) > wi->func = func; > wi->data = data; > wi->free = true; > +wi->safe = false; > + > +queue_work_on_cpu(cpu, wi); > +} > + > +void async_safe_run_on_cpu(CPUState *cpu, run_on_cpu_func func, void *data) > +{ > +struct qemu_work_item *wi; > + > +wi = g_malloc0(sizeof(struct qemu_work_item)); > +wi->func = func; > +wi->data = data; > +wi->free = true; > +wi->safe = true; > > queue_work_on_cpu(cpu, wi); > }
Re: [Qemu-devel] [RFC 7/8] cpu-exec-common: Introduce async_safe_run_on_cpu()
Sergey Fedorovwrites: > On 27/06/16 12:36, Alex Bennée wrote: >> Sergey Fedorov writes: >> >>> From: Sergey Fedorov >>> > (snip) >>> diff --git a/cpus.c b/cpus.c >>> index 98f60f6f98f5..bb6bd8615cfc 100644 >>> --- a/cpus.c >>> +++ b/cpus.c >>> @@ -932,6 +932,18 @@ static void qemu_tcg_destroy_vcpu(CPUState *cpu) >>> { >>> } >>> >>> +static void tcg_cpu_exec_start(CPUState *cpu) >>> +{ >>> +tcg_pending_cpus++; >>> +} >>> + >>> +static void tcg_cpu_exec_end(CPUState *cpu) >>> +{ >>> +if (--tcg_pending_cpus) { >>> +signal_cpu_work(); >>> +} >>> +} >> Don't these need to be atomic? > > 'tcg_pending_cpus' is protected by BQL. A quick comment above the function would help then. > >> >>> + >>> static void qemu_wait_io_event_common(CPUState *cpu) >>> { >>> if (cpu->stop) { >>> > (snip) > > Thanks, > Sergey -- Alex Bennée
Re: [Qemu-devel] [RFC 7/8] cpu-exec-common: Introduce async_safe_run_on_cpu()
On 27/06/16 12:36, Alex Bennée wrote: > Sergey Fedorovwrites: > >> From: Sergey Fedorov >> (snip) >> diff --git a/cpus.c b/cpus.c >> index 98f60f6f98f5..bb6bd8615cfc 100644 >> --- a/cpus.c >> +++ b/cpus.c >> @@ -932,6 +932,18 @@ static void qemu_tcg_destroy_vcpu(CPUState *cpu) >> { >> } >> >> +static void tcg_cpu_exec_start(CPUState *cpu) >> +{ >> +tcg_pending_cpus++; >> +} >> + >> +static void tcg_cpu_exec_end(CPUState *cpu) >> +{ >> +if (--tcg_pending_cpus) { >> +signal_cpu_work(); >> +} >> +} > Don't these need to be atomic? 'tcg_pending_cpus' is protected by BQL. > >> + >> static void qemu_wait_io_event_common(CPUState *cpu) >> { >> if (cpu->stop) { >> (snip) Thanks, Sergey
Re: [Qemu-devel] [RFC 7/8] cpu-exec-common: Introduce async_safe_run_on_cpu()
Sergey Fedorovwrites: > From: Sergey Fedorov > > This patch is based on the ideas found in work of KONRAD Frederic [1], > Alex Bennée [2], and Alvise Rigo [3]. > > This mechanism allows to perform an operation safely in a quiescent > state. Quiescent state means: (1) no vCPU is running and (2) BQL in > system-mode or 'exclusive_lock' in user-mode emulation is held while > performing the operation. This functionality is required e.g. for > performing translation buffer flush safely in multi-threaded user-mode > emulation. > > The existing CPU work queue is used to schedule such safe operations. A > new 'safe' flag is added into struct qemu_work_item to designate the > special requirements of the safe work. An operation in a quiescent sate > can be scheduled by using async_safe_run_on_cpu() function which is > actually the same as sync_run_on_cpu() except that it marks the queued > work item with the 'safe' flag set to true. Given this flag set > queue_work_on_cpu() atomically increments 'safe_work_pending' global > counter and kicks all the CPUs instead of just the target CPU as in case > of normal CPU work. This allows to force other CPUs to exit their > execution loops and wait in wait_safe_cpu_work() function for the safe > work to finish. When a CPU drains its work queue, if it encounters a > work item marked as safe, it first waits for other CPUs to exit their > execution loops, then called the work item function, and finally > decrements 'safe_work_pending' counter with signalling other CPUs to let > them continue execution as soon as all pending safe work items have been > processed. The 'tcg_pending_cpus' protected by 'exclusive_lock' in > user-mode or by 'qemu_global_mutex' in system-mode emulation is used to > determine if there is any CPU run and wait for it to exit the execution > loop. The fairness of all the CPU work queues is ensured by draining all > the pending safe work items before any CPU can run. > > [1] http://lists.nongnu.org/archive/html/qemu-devel/2015-08/msg01128.html > [2] http://lists.nongnu.org/archive/html/qemu-devel/2016-04/msg02531.html > [3] http://lists.nongnu.org/archive/html/qemu-devel/2016-05/msg04792.html > > Signed-off-by: Sergey Fedorov > Signed-off-by: Sergey Fedorov > --- > cpu-exec-common.c | 45 - > cpus.c | 16 > include/exec/exec-all.h | 2 ++ > include/qom/cpu.h | 14 ++ > linux-user/main.c | 2 +- > 5 files changed, 77 insertions(+), 2 deletions(-) > > diff --git a/cpu-exec-common.c b/cpu-exec-common.c > index 8184e0662cbd..3056324738f8 100644 > --- a/cpu-exec-common.c > +++ b/cpu-exec-common.c > @@ -25,6 +25,7 @@ > > bool exit_request; > CPUState *tcg_current_cpu; > +int tcg_pending_cpus; > > /* exit the current TB, but without causing any exception to be raised */ > void cpu_loop_exit_noexc(CPUState *cpu) > @@ -78,6 +79,15 @@ void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) > siglongjmp(cpu->jmp_env, 1); > } > > +static int safe_work_pending; > + > +void wait_safe_cpu_work(void) > +{ > +while (atomic_mb_read(_work_pending) > 0) { > +wait_cpu_work(); > +} > +} > + > static void queue_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi) > { > qemu_mutex_lock(>work_mutex); > @@ -89,9 +99,18 @@ static void queue_work_on_cpu(CPUState *cpu, struct > qemu_work_item *wi) > cpu->queued_work_last = wi; > wi->next = NULL; > wi->done = false; > +if (wi->safe) { > +atomic_inc(_work_pending); > +} > qemu_mutex_unlock(>work_mutex); > > -qemu_cpu_kick(cpu); > +if (!wi->safe) { > +qemu_cpu_kick(cpu); > +} else { > +CPU_FOREACH(cpu) { > +qemu_cpu_kick(cpu); > +} > +} > } > > void run_on_cpu(CPUState *cpu, run_on_cpu_func func, void *data) > @@ -106,6 +125,7 @@ void run_on_cpu(CPUState *cpu, run_on_cpu_func func, void > *data) > wi.func = func; > wi.data = data; > wi.free = false; > +wi.safe = false; > > queue_work_on_cpu(cpu, ); > while (!atomic_mb_read()) { > @@ -129,6 +149,20 @@ void async_run_on_cpu(CPUState *cpu, run_on_cpu_func > func, void *data) > wi->func = func; > wi->data = data; > wi->free = true; > +wi->safe = false; > + > +queue_work_on_cpu(cpu, wi); > +} > + > +void async_safe_run_on_cpu(CPUState *cpu, run_on_cpu_func func, void *data) > +{ > +struct qemu_work_item *wi; > + > +wi = g_malloc0(sizeof(struct qemu_work_item)); > +wi->func = func; > +wi->data = data; > +wi->free = true; > +wi->safe = true; > > queue_work_on_cpu(cpu, wi); > } > @@ -148,9 +182,18 @@ void flush_queued_work(CPUState *cpu) > if (!cpu->queued_work_first) { > cpu->queued_work_last = NULL; > } > +if (wi->safe) { > +
[Qemu-devel] [RFC 7/8] cpu-exec-common: Introduce async_safe_run_on_cpu()
From: Sergey FedorovThis patch is based on the ideas found in work of KONRAD Frederic [1], Alex Bennée [2], and Alvise Rigo [3]. This mechanism allows to perform an operation safely in a quiescent state. Quiescent state means: (1) no vCPU is running and (2) BQL in system-mode or 'exclusive_lock' in user-mode emulation is held while performing the operation. This functionality is required e.g. for performing translation buffer flush safely in multi-threaded user-mode emulation. The existing CPU work queue is used to schedule such safe operations. A new 'safe' flag is added into struct qemu_work_item to designate the special requirements of the safe work. An operation in a quiescent sate can be scheduled by using async_safe_run_on_cpu() function which is actually the same as sync_run_on_cpu() except that it marks the queued work item with the 'safe' flag set to true. Given this flag set queue_work_on_cpu() atomically increments 'safe_work_pending' global counter and kicks all the CPUs instead of just the target CPU as in case of normal CPU work. This allows to force other CPUs to exit their execution loops and wait in wait_safe_cpu_work() function for the safe work to finish. When a CPU drains its work queue, if it encounters a work item marked as safe, it first waits for other CPUs to exit their execution loops, then called the work item function, and finally decrements 'safe_work_pending' counter with signalling other CPUs to let them continue execution as soon as all pending safe work items have been processed. The 'tcg_pending_cpus' protected by 'exclusive_lock' in user-mode or by 'qemu_global_mutex' in system-mode emulation is used to determine if there is any CPU run and wait for it to exit the execution loop. The fairness of all the CPU work queues is ensured by draining all the pending safe work items before any CPU can run. [1] http://lists.nongnu.org/archive/html/qemu-devel/2015-08/msg01128.html [2] http://lists.nongnu.org/archive/html/qemu-devel/2016-04/msg02531.html [3] http://lists.nongnu.org/archive/html/qemu-devel/2016-05/msg04792.html Signed-off-by: Sergey Fedorov Signed-off-by: Sergey Fedorov --- cpu-exec-common.c | 45 - cpus.c | 16 include/exec/exec-all.h | 2 ++ include/qom/cpu.h | 14 ++ linux-user/main.c | 2 +- 5 files changed, 77 insertions(+), 2 deletions(-) diff --git a/cpu-exec-common.c b/cpu-exec-common.c index 8184e0662cbd..3056324738f8 100644 --- a/cpu-exec-common.c +++ b/cpu-exec-common.c @@ -25,6 +25,7 @@ bool exit_request; CPUState *tcg_current_cpu; +int tcg_pending_cpus; /* exit the current TB, but without causing any exception to be raised */ void cpu_loop_exit_noexc(CPUState *cpu) @@ -78,6 +79,15 @@ void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) siglongjmp(cpu->jmp_env, 1); } +static int safe_work_pending; + +void wait_safe_cpu_work(void) +{ +while (atomic_mb_read(_work_pending) > 0) { +wait_cpu_work(); +} +} + static void queue_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi) { qemu_mutex_lock(>work_mutex); @@ -89,9 +99,18 @@ static void queue_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi) cpu->queued_work_last = wi; wi->next = NULL; wi->done = false; +if (wi->safe) { +atomic_inc(_work_pending); +} qemu_mutex_unlock(>work_mutex); -qemu_cpu_kick(cpu); +if (!wi->safe) { +qemu_cpu_kick(cpu); +} else { +CPU_FOREACH(cpu) { +qemu_cpu_kick(cpu); +} +} } void run_on_cpu(CPUState *cpu, run_on_cpu_func func, void *data) @@ -106,6 +125,7 @@ void run_on_cpu(CPUState *cpu, run_on_cpu_func func, void *data) wi.func = func; wi.data = data; wi.free = false; +wi.safe = false; queue_work_on_cpu(cpu, ); while (!atomic_mb_read()) { @@ -129,6 +149,20 @@ void async_run_on_cpu(CPUState *cpu, run_on_cpu_func func, void *data) wi->func = func; wi->data = data; wi->free = true; +wi->safe = false; + +queue_work_on_cpu(cpu, wi); +} + +void async_safe_run_on_cpu(CPUState *cpu, run_on_cpu_func func, void *data) +{ +struct qemu_work_item *wi; + +wi = g_malloc0(sizeof(struct qemu_work_item)); +wi->func = func; +wi->data = data; +wi->free = true; +wi->safe = true; queue_work_on_cpu(cpu, wi); } @@ -148,9 +182,18 @@ void flush_queued_work(CPUState *cpu) if (!cpu->queued_work_first) { cpu->queued_work_last = NULL; } +if (wi->safe) { +while (tcg_pending_cpus) { +wait_cpu_work(); +} +} qemu_mutex_unlock(>work_mutex); wi->func(cpu, wi->data); qemu_mutex_lock(>work_mutex); +if (wi->safe) { +atomic_dec(_work_pending); +signal_cpu_work(); +