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

"Rui Qi" <[email protected]>
Newsgroups org.infradead.lists.linux-riscv,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
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 <[email protected]>
---
 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

_______________________________________________
linux-riscv mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-riscv
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.