ramips: mt7621: fix reset hang

These patches stop secondary CPUs before restart and wait for them
to go offline, fixing the mt7621 reboot deadlock without needing to
disable CONFIG_LIST_HARDENED.

Signed-off-by: Rany Hany <rany_hany@riseup.net>
Link: https://github.com/openwrt/openwrt/pull/22724
(cherry picked from commit 3166710fc9)
Link: https://github.com/openwrt/openwrt/pull/22673
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
This commit is contained in:
Rany Hany 2026-03-30 13:37:03 +00:00 committed by Hauke Mehrtens
parent d7fe2c7562
commit fdeb6d6a9f
3 changed files with 207 additions and 0 deletions

View File

@ -0,0 +1,67 @@
From 641e36faf86bcd9b6dc2cd4a6d6e3fc604ee6397 Mon Sep 17 00:00:00 2001
From: Rany Hany <rany_hany@riseup.net>
Date: Tue, 31 Mar 2026 17:46:35 +0300
Subject: [PATCH 1/3] MIPS: Stop secondary CPUs before platform
restart/halt/poweroff
smp_send_stop() was placed after the platform restart, halt, and
power-off callbacks. These callbacks never return, so smp_send_stop()
was dead code and secondary CPUs were never stopped before the system
reset. Move smp_send_stop() before the callbacks.
Signed-off-by: Rany Hany <rany_hany@riseup.net>
---
arch/mips/kernel/reset.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
--- a/arch/mips/kernel/reset.c
+++ b/arch/mips/kernel/reset.c
@@ -87,13 +87,14 @@ static void machine_hang(void)
void machine_restart(char *command)
{
- if (_machine_restart)
- _machine_restart(command);
-
#ifdef CONFIG_SMP
preempt_disable();
smp_send_stop();
#endif
+
+ if (_machine_restart)
+ _machine_restart(command);
+
do_kernel_restart(command);
mdelay(1000);
pr_emerg("Reboot failed -- System halted\n");
@@ -102,23 +103,25 @@ void machine_restart(char *command)
void machine_halt(void)
{
- if (_machine_halt)
- _machine_halt();
-
#ifdef CONFIG_SMP
preempt_disable();
smp_send_stop();
#endif
+
+ if (_machine_halt)
+ _machine_halt();
+
machine_hang();
}
void machine_power_off(void)
{
- do_kernel_power_off();
-
#ifdef CONFIG_SMP
preempt_disable();
smp_send_stop();
#endif
+
+ do_kernel_power_off();
+
machine_hang();
}

View File

@ -0,0 +1,36 @@
From 251cd7607a3dcc578b249d2a685db99d7da73975 Mon Sep 17 00:00:00 2001
From: Rany Hany <rany_hany@riseup.net>
Date: Tue, 31 Mar 2026 18:07:36 +0300
Subject: [PATCH 2/3] MIPS: SMP: Wait for secondary CPUs to stop in
smp_send_stop()
smp_send_stop() returns immediately without waiting.
Wait up to one second for secondary CPUs to mark themselves offline
before returning, similar to what ARM and ARM64 already do.
Signed-off-by: Rany Hany <rany_hany@riseup.net>
---
arch/mips/kernel/smp.c | 10 ++++++++++
1 file changed, 10 insertions(+)
--- a/arch/mips/kernel/smp.c
+++ b/arch/mips/kernel/smp.c
@@ -416,7 +416,17 @@ static void stop_this_cpu(void *dummy)
void smp_send_stop(void)
{
+ unsigned long timeout;
+
smp_call_function(stop_this_cpu, NULL, 0);
+
+ /* Wait up to 1s for other CPUs to stop */
+ timeout = USEC_PER_SEC;
+ while (num_online_cpus() > 1 && timeout--)
+ udelay(1);
+
+ if (num_online_cpus() > 1)
+ pr_warn("SMP: failed to stop secondary CPUs\n");
}
void __init smp_cpus_done(unsigned int max_cpus)

View File

@ -0,0 +1,104 @@
From de332bdaf33c09be03c05c0681beae1276ccbcc2 Mon Sep 17 00:00:00 2001
From: Rany Hany <rany_hany@riseup.net>
Date: Tue, 31 Mar 2026 20:29:53 +0300
Subject: [PATCH 3/3] MIPS: SMP: Properly stop secondary CPUs for restart
MT7621 deadlocks in the platform restart callback unless the other
CPUs are stopped by taking other cores out of coherence and clocking
them off, while halting sibling TCs on the restarting core.
This fixes restart deadlocks on MT7621, although the exact reason
why this is required is still unclear.
Signed-off-by: Rany Hany <rany_hany@riseup.net>
---
arch/mips/kernel/smp.c | 54 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 53 insertions(+), 1 deletion(-)
--- a/arch/mips/kernel/smp.c
+++ b/arch/mips/kernel/smp.c
@@ -32,6 +32,7 @@
#include <asm/processor.h>
#include <asm/idle.h>
#include <asm/r4k-timer.h>
+#include <asm/r4kcache.h>
#include <asm/mips-cps.h>
#include <asm/mmu_context.h>
#include <asm/time.h>
@@ -402,6 +403,8 @@ asmlinkage void start_secondary(void)
cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
}
+static atomic_t core_stop_count[NR_CPUS];
+
static void stop_this_cpu(void *dummy)
{
/*
@@ -411,13 +414,66 @@ static void stop_this_cpu(void *dummy)
set_cpu_online(smp_processor_id(), false);
calculate_cpu_foreign_map();
local_irq_disable();
- while (1);
+
+ if (mips_cm_present() && r4k_blast_dcache) {
+ unsigned int core = cpu_core(&current_cpu_data);
+
+ if (atomic_dec_and_test(&core_stop_count[core])) {
+ /* Flush data cache */
+ r4k_blast_dcache();
+ __sync();
+
+ if (mips_cm_revision() < CM_REV_CM3) {
+ /* Restrict coherence to own core first */
+ write_gcr_cl_coherence(1 << core);
+ read_gcr_cl_coherence();
+ __sync();
+ }
+
+ /* Disable coherence */
+ write_gcr_cl_coherence(0);
+ read_gcr_cl_coherence();
+
+ /* Gate the core clock */
+ if (mips_cpc_present())
+ write_cpc_cl_cmd(CPC_Cx_CMD_CLOCKOFF);
+ }
+ }
+
+ if (cpu_has_mipsmt) {
+ /* The last active VPE on the core will gate the core clock
+ * and all other remaining VPEs will halt this TC instead.
+ *
+ * Note that on systems without CPC, this will be the
+ * only way to shutdown the CPU.
+ */
+ write_c0_tchalt(TCHALT_H);
+ instruction_hazard();
+ }
+
+ while (1)
+ cpu_relax();
}
void smp_send_stop(void)
{
+ static unsigned long stop_in_progress;
unsigned long timeout;
+ if (test_and_set_bit(0, &stop_in_progress))
+ return;
+
+ if (mips_cm_present()) {
+ unsigned int cpu;
+
+ for_each_online_cpu(cpu) {
+ unsigned int core;
+
+ core = cpu_core(&cpu_data[cpu]);
+ atomic_inc(&core_stop_count[core]);
+ }
+ }
+
smp_call_function(stop_this_cpu, NULL, 0);
/* Wait up to 1s for other CPUs to stop */