92 lines
2.6 KiB
Diff
92 lines
2.6 KiB
Diff
|
|
From 401e145800134d0310d613f48c4962a108b8ddda Mon Sep 17 00:00:00 2001
|
||
|
|
From: Keqian Zhu <zhukeqian1@huawei.com>
|
||
|
|
Date: Sun, 17 Mar 2024 16:37:03 +0800
|
||
|
|
Subject: [PATCH] system/cpus: Fix pause_all_vcpus() under concurrent
|
||
|
|
environment
|
||
|
|
|
||
|
|
Both main loop thread and vCPU thread are allowed to call
|
||
|
|
pause_all_vcpus(), and in general resume_all_vcpus() is called
|
||
|
|
after it. Two issues live in pause_all_vcpus():
|
||
|
|
|
||
|
|
1. There is possibility that during thread T1 waits on
|
||
|
|
qemu_pause_cond with bql unlocked, other thread has called
|
||
|
|
pause_all_vcpus() and resume_all_vcpus(), then thread T1 will
|
||
|
|
stuck, because the condition all_vcpus_paused() is always false.
|
||
|
|
|
||
|
|
2. After all_vcpus_paused() has been checked as true, we will
|
||
|
|
unlock bql to relock replay_mutex. During the bql was unlocked,
|
||
|
|
the vcpu's state may has been changed by other thread, so we
|
||
|
|
must retry.
|
||
|
|
|
||
|
|
Signed-off-by: Keqian Zhu <zhukeqian1@huawei.com>
|
||
|
|
---
|
||
|
|
system/cpus.c | 29 ++++++++++++++++++++++++-----
|
||
|
|
1 file changed, 24 insertions(+), 5 deletions(-)
|
||
|
|
|
||
|
|
diff --git a/system/cpus.c b/system/cpus.c
|
||
|
|
index a444a747f0..7c5369fa9c 100644
|
||
|
|
--- a/system/cpus.c
|
||
|
|
+++ b/system/cpus.c
|
||
|
|
@@ -551,12 +551,14 @@ static bool all_vcpus_paused(void)
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
-void pause_all_vcpus(void)
|
||
|
|
+static void request_pause_all_vcpus(void)
|
||
|
|
{
|
||
|
|
CPUState *cpu;
|
||
|
|
|
||
|
|
- qemu_clock_enable(QEMU_CLOCK_VIRTUAL, false);
|
||
|
|
CPU_FOREACH(cpu) {
|
||
|
|
+ if (cpu->stopped) {
|
||
|
|
+ continue;
|
||
|
|
+ }
|
||
|
|
if (qemu_cpu_is_self(cpu)) {
|
||
|
|
qemu_cpu_stop(cpu, true);
|
||
|
|
} else {
|
||
|
|
@@ -564,6 +566,14 @@ void pause_all_vcpus(void)
|
||
|
|
qemu_cpu_kick(cpu);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+void pause_all_vcpus(void)
|
||
|
|
+{
|
||
|
|
+ qemu_clock_enable(QEMU_CLOCK_VIRTUAL, false);
|
||
|
|
+
|
||
|
|
+retry:
|
||
|
|
+ request_pause_all_vcpus();
|
||
|
|
|
||
|
|
/* We need to drop the replay_lock so any vCPU threads woken up
|
||
|
|
* can finish their replay tasks
|
||
|
|
@@ -572,14 +582,23 @@ void pause_all_vcpus(void)
|
||
|
|
|
||
|
|
while (!all_vcpus_paused()) {
|
||
|
|
qemu_cond_wait(&qemu_pause_cond, &qemu_global_mutex);
|
||
|
|
- CPU_FOREACH(cpu) {
|
||
|
|
- qemu_cpu_kick(cpu);
|
||
|
|
- }
|
||
|
|
+ /* During we waited on qemu_pause_cond the bql was unlocked,
|
||
|
|
+ * the vcpu's state may has been changed by other thread, so
|
||
|
|
+ * we must request the pause state on all vcpus again.
|
||
|
|
+ */
|
||
|
|
+ request_pause_all_vcpus();
|
||
|
|
}
|
||
|
|
|
||
|
|
qemu_mutex_unlock_iothread();
|
||
|
|
replay_mutex_lock();
|
||
|
|
qemu_mutex_lock_iothread();
|
||
|
|
+
|
||
|
|
+ /* During the bql was unlocked, the vcpu's state may has been
|
||
|
|
+ * changed by other thread, so we must retry.
|
||
|
|
+ */
|
||
|
|
+ if (!all_vcpus_paused()) {
|
||
|
|
+ goto retry;
|
||
|
|
+ }
|
||
|
|
}
|
||
|
|
|
||
|
|
void cpu_resume(CPUState *cpu)
|
||
|
|
--
|
||
|
|
2.27.0
|
||
|
|
|