[PATCH 2/2] riscv: Poll for SBI_HSM_STATE_STOPPED in sbi_cpu_is_stopped()

Rui Qi qirui.001 at bytedance.com
Thu Aug 13 06:03:57 PDT 2026


When offlining a CPU, the dying CPU (AP) and the controlling CPU (BP)
have a race:

  AP: cpuhp_ap_report_dead() -> cpu_ops->cpu_stop()
                                    ^^^
  BP: sees DEAD -> cpu_is_stopped() -> sbi_hsm_hart_get_status()

The AP reports itself dead before it actually invokes cpu_stop(), so
the BP can read the SBI HSM state while the AP is still in
STARTED state, not yet STOPPED.

This race was previously masked: the old sbi_cpu_is_stopped() returned
the raw SBI status code, and SBI_HSM_STATE_STARTED happens to be 0,
which the caller treated as success. The recent bool conversion made
the check strict (rc != STOPPED is failure), exposing the race as a
spurious warning.

Fix by replacing the single sbi_hsm_hart_get_status() call with
read_poll_timeout(), giving the AP time to complete the transition to
STOPPED. This follows the same approach as arm64's cpu_psci_cpu_kill(),
which polls psci_ops.affinity_info() for the same reason.

The first SBI query is issued immediately (sleep_before_read=false),
so the common case where the CPU has already stopped incurs no extra
delay. If the state is not yet STOPPED, the code retries every 100us
with usleep_range() for up to 100ms.

Signed-off-by: Rui Qi <qirui.001 at bytedance.com>
---
 arch/riscv/kernel/cpu_ops_sbi.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/arch/riscv/kernel/cpu_ops_sbi.c b/arch/riscv/kernel/cpu_ops_sbi.c
index ee6e4b5cc39e..607d386db5f7 100644
--- a/arch/riscv/kernel/cpu_ops_sbi.c
+++ b/arch/riscv/kernel/cpu_ops_sbi.c
@@ -6,6 +6,7 @@
  */
 
 #include <linux/init.h>
+#include <linux/iopoll.h>
 #include <linux/mm.h>
 #include <linux/sched/task_stack.h>
 #include <asm/cpu_ops.h>
@@ -13,6 +14,9 @@
 #include <asm/sbi.h>
 #include <asm/smp.h>
 
+#define SBI_HSM_STOP_POLL_US		100
+#define SBI_HSM_STOP_TIMEOUT_US		(100 * USEC_PER_MSEC)
+
 extern char secondary_start_sbi[];
 const struct cpu_operations cpu_ops_sbi;
 
@@ -85,12 +89,15 @@ static void sbi_cpu_stop(void)
 
 static bool sbi_cpu_is_stopped(unsigned int cpuid)
 {
-	int rc;
+	int rc, ret;
 	unsigned long hartid = cpuid_to_hartid_map(cpuid);
 
-	rc = sbi_hsm_hart_get_status(hartid);
+	ret = read_poll_timeout(sbi_hsm_hart_get_status, rc,
+				rc < 0 || rc == SBI_HSM_STATE_STOPPED,
+				SBI_HSM_STOP_POLL_US, SBI_HSM_STOP_TIMEOUT_US,
+				false, hartid);
 
-	if (rc != SBI_HSM_STATE_STOPPED) {
+	if (ret || rc != SBI_HSM_STATE_STOPPED) {
 		pr_warn("HART%lu isn't stopped; status %d\n", hartid, rc);
 		return false;
 	}
-- 
2.20.1



More information about the linux-riscv mailing list