aboutsummaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
authorGreg Kurz <groug@kaod.org>2021-11-09 19:35:22 +0100
committerPaolo Bonzini <pbonzini@redhat.com>2021-11-10 13:20:15 +0100
commitef149763a8fcce70b85dfda27cc1222ecf765750 (patch)
treeb63ade31a861723451db40bb192d9f060c5f3066 /util
parenta0b9c5f75c05c12d30328a305377383652e62e63 (diff)
rcu: Introduce force_rcu notifier
The drain_rcu_call() function can be blocked as long as an RCU reader stays in a read-side critical section. This is typically what happens when a TCG vCPU is executing a busy loop. It can deadlock the QEMU monitor as reported in https://gitlab.com/qemu-project/qemu/-/issues/650 . This can be avoided by allowing drain_rcu_call() to enforce an RCU grace period. Since each reader might need to do specific actions to end a read-side critical section, do it with notifiers. Prepare ground for this by adding a notifier list to the RCU reader struct and use it in wait_for_readers() if drain_rcu_call() is in progress. An API is added for readers to register their notifiers. This is largely based on a draft from Paolo Bonzini. Suggested-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Greg Kurz <groug@kaod.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20211109183523.47726-2-groug@kaod.org> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'util')
-rw-r--r--util/rcu.c19
1 files changed, 19 insertions, 0 deletions
diff --git a/util/rcu.c b/util/rcu.c
index 13ac0f75cb..c91da9f137 100644
--- a/util/rcu.c
+++ b/util/rcu.c
@@ -46,6 +46,7 @@
unsigned long rcu_gp_ctr = RCU_GP_LOCKED;
QemuEvent rcu_gp_event;
+static int in_drain_call_rcu;
static QemuMutex rcu_registry_lock;
static QemuMutex rcu_sync_lock;
@@ -107,6 +108,8 @@ static void wait_for_readers(void)
* get some extra futex wakeups.
*/
qatomic_set(&index->waiting, false);
+ } else if (qatomic_read(&in_drain_call_rcu)) {
+ notifier_list_notify(&index->force_rcu, NULL);
}
}
@@ -339,8 +342,10 @@ void drain_call_rcu(void)
* assumed.
*/
+ qatomic_inc(&in_drain_call_rcu);
call_rcu1(&rcu_drain.rcu, drain_rcu_callback);
qemu_event_wait(&rcu_drain.drain_complete_event);
+ qatomic_dec(&in_drain_call_rcu);
if (locked) {
qemu_mutex_lock_iothread();
@@ -363,6 +368,20 @@ void rcu_unregister_thread(void)
qemu_mutex_unlock(&rcu_registry_lock);
}
+void rcu_add_force_rcu_notifier(Notifier *n)
+{
+ qemu_mutex_lock(&rcu_registry_lock);
+ notifier_list_add(&rcu_reader.force_rcu, n);
+ qemu_mutex_unlock(&rcu_registry_lock);
+}
+
+void rcu_remove_force_rcu_notifier(Notifier *n)
+{
+ qemu_mutex_lock(&rcu_registry_lock);
+ notifier_remove(n);
+ qemu_mutex_unlock(&rcu_registry_lock);
+}
+
static void rcu_init_complete(void)
{
QemuThread thread;