[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