Skip to content

Commit ce45ba1

Browse files
author
AKASHI Takahiro
committed
arm64: kdump: implement machine_crash_shutdown()
Primary kernel calls machine_crash_shutdown() to shut down non-boot cpus and save registers' status in per-cpu ELF notes before starting crash dump kernel. See kernel_kexec(). Even if not all secondary cpus have shut down, we do kdump anyway. As we don't have to make non-boot(crashed) cpus offline (to preserve correct status of cpus at crash dump) before shutting down, this patch also adds a variant of smp_send_stop(). Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org> Reviewed-by: James Morse <james.morse@arm.com> Acked-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
1 parent 76d3453 commit ce45ba1

5 files changed

Lines changed: 167 additions & 6 deletions

File tree

arch/arm64/include/asm/hardirq.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
#include <linux/threads.h>
2121
#include <asm/irq.h>
2222

23-
#define NR_IPI 6
23+
#define NR_IPI 7
2424

2525
typedef struct {
2626
unsigned int __softirq_pending;

arch/arm64/include/asm/kexec.h

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,47 @@
4040
static inline void crash_setup_regs(struct pt_regs *newregs,
4141
struct pt_regs *oldregs)
4242
{
43-
/* Empty routine needed to avoid build errors. */
43+
if (oldregs) {
44+
memcpy(newregs, oldregs, sizeof(*newregs));
45+
} else {
46+
u64 tmp1, tmp2;
47+
48+
__asm__ __volatile__ (
49+
"stp x0, x1, [%2, #16 * 0]\n"
50+
"stp x2, x3, [%2, #16 * 1]\n"
51+
"stp x4, x5, [%2, #16 * 2]\n"
52+
"stp x6, x7, [%2, #16 * 3]\n"
53+
"stp x8, x9, [%2, #16 * 4]\n"
54+
"stp x10, x11, [%2, #16 * 5]\n"
55+
"stp x12, x13, [%2, #16 * 6]\n"
56+
"stp x14, x15, [%2, #16 * 7]\n"
57+
"stp x16, x17, [%2, #16 * 8]\n"
58+
"stp x18, x19, [%2, #16 * 9]\n"
59+
"stp x20, x21, [%2, #16 * 10]\n"
60+
"stp x22, x23, [%2, #16 * 11]\n"
61+
"stp x24, x25, [%2, #16 * 12]\n"
62+
"stp x26, x27, [%2, #16 * 13]\n"
63+
"stp x28, x29, [%2, #16 * 14]\n"
64+
"mov %0, sp\n"
65+
"stp x30, %0, [%2, #16 * 15]\n"
66+
67+
"/* faked current PSTATE */\n"
68+
"mrs %0, CurrentEL\n"
69+
"mrs %1, SPSEL\n"
70+
"orr %0, %0, %1\n"
71+
"mrs %1, DAIF\n"
72+
"orr %0, %0, %1\n"
73+
"mrs %1, NZCV\n"
74+
"orr %0, %0, %1\n"
75+
/* pc */
76+
"adr %1, 1f\n"
77+
"1:\n"
78+
"stp %1, %0, [%2, #16 * 16]\n"
79+
: "=&r" (tmp1), "=&r" (tmp2)
80+
: "r" (newregs)
81+
: "memory"
82+
);
83+
}
4484
}
4585

4686
#if defined(CONFIG_KEXEC_CORE) && defined(CONFIG_HIBERNATION)

arch/arm64/include/asm/smp.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ static inline void update_cpu_boot_status(int val)
125125
*/
126126
bool cpus_are_stuck_in_kernel(void);
127127

128+
extern void smp_send_crash_stop(void);
129+
extern bool smp_crash_stop_failed(void);
130+
128131
#endif /* ifndef __ASSEMBLY__ */
129132

130133
#endif /* ifndef __ASM_SMP_H */

arch/arm64/kernel/machine_kexec.c

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
* published by the Free Software Foundation.
1010
*/
1111

12+
#include <linux/interrupt.h>
13+
#include <linux/irq.h>
14+
#include <linux/kernel.h>
1215
#include <linux/kexec.h>
1316
#include <linux/page-flags.h>
1417
#include <linux/smp.h>
@@ -143,11 +146,15 @@ void machine_kexec(struct kimage *kimage)
143146
{
144147
phys_addr_t reboot_code_buffer_phys;
145148
void *reboot_code_buffer;
149+
bool in_kexec_crash = (kimage == kexec_crash_image);
150+
bool stuck_cpus = cpus_are_stuck_in_kernel();
146151

147152
/*
148153
* New cpus may have become stuck_in_kernel after we loaded the image.
149154
*/
150-
BUG_ON(cpus_are_stuck_in_kernel() || (num_online_cpus() > 1));
155+
BUG_ON(!in_kexec_crash && (stuck_cpus || (num_online_cpus() > 1)));
156+
WARN(in_kexec_crash && (stuck_cpus || smp_crash_stop_failed()),
157+
"Some CPUs may be stale, kdump will be unreliable.\n");
151158

152159
reboot_code_buffer_phys = page_to_phys(kimage->control_code_page);
153160
reboot_code_buffer = phys_to_virt(reboot_code_buffer_phys);
@@ -199,15 +206,58 @@ void machine_kexec(struct kimage *kimage)
199206
* relocation is complete.
200207
*/
201208

202-
cpu_soft_restart(1, reboot_code_buffer_phys, kimage->head,
203-
kimage->start, 0);
209+
cpu_soft_restart(kimage != kexec_crash_image,
210+
reboot_code_buffer_phys, kimage->head, kimage->start, 0);
204211

205212
BUG(); /* Should never get here. */
206213
}
207214

215+
static void machine_kexec_mask_interrupts(void)
216+
{
217+
unsigned int i;
218+
struct irq_desc *desc;
219+
220+
for_each_irq_desc(i, desc) {
221+
struct irq_chip *chip;
222+
int ret;
223+
224+
chip = irq_desc_get_chip(desc);
225+
if (!chip)
226+
continue;
227+
228+
/*
229+
* First try to remove the active state. If this
230+
* fails, try to EOI the interrupt.
231+
*/
232+
ret = irq_set_irqchip_state(i, IRQCHIP_STATE_ACTIVE, false);
233+
234+
if (ret && irqd_irq_inprogress(&desc->irq_data) &&
235+
chip->irq_eoi)
236+
chip->irq_eoi(&desc->irq_data);
237+
238+
if (chip->irq_mask)
239+
chip->irq_mask(&desc->irq_data);
240+
241+
if (chip->irq_disable && !irqd_irq_disabled(&desc->irq_data))
242+
chip->irq_disable(&desc->irq_data);
243+
}
244+
}
245+
246+
/**
247+
* machine_crash_shutdown - shutdown non-crashing cpus and save registers
248+
*/
208249
void machine_crash_shutdown(struct pt_regs *regs)
209250
{
210-
/* Empty routine needed to avoid build errors. */
251+
local_irq_disable();
252+
253+
/* shutdown non-crashing cpus */
254+
smp_send_crash_stop();
255+
256+
/* for crashing cpu */
257+
crash_save_cpu(regs, smp_processor_id());
258+
machine_kexec_mask_interrupts();
259+
260+
pr_info("Starting crashdump kernel...\n");
211261
}
212262

213263
void arch_kexec_protect_crashkres(void)

arch/arm64/kernel/smp.c

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include <linux/completion.h>
3838
#include <linux/of.h>
3939
#include <linux/irq_work.h>
40+
#include <linux/kexec.h>
4041

4142
#include <asm/alternative.h>
4243
#include <asm/atomic.h>
@@ -70,6 +71,7 @@ enum ipi_msg_type {
7071
IPI_RESCHEDULE,
7172
IPI_CALL_FUNC,
7273
IPI_CPU_STOP,
74+
IPI_CPU_CRASH_STOP,
7375
IPI_TIMER,
7476
IPI_IRQ_WORK,
7577
IPI_WAKEUP
@@ -701,6 +703,7 @@ static const char *ipi_types[NR_IPI] __tracepoint_string = {
701703
S(IPI_RESCHEDULE, "Rescheduling interrupts"),
702704
S(IPI_CALL_FUNC, "Function call interrupts"),
703705
S(IPI_CPU_STOP, "CPU stop interrupts"),
706+
S(IPI_CPU_CRASH_STOP, "CPU stop (for crash dump) interrupts"),
704707
S(IPI_TIMER, "Timer broadcast interrupts"),
705708
S(IPI_IRQ_WORK, "IRQ work interrupts"),
706709
S(IPI_WAKEUP, "CPU wake-up interrupts"),
@@ -785,6 +788,29 @@ static void ipi_cpu_stop(unsigned int cpu)
785788
cpu_relax();
786789
}
787790

791+
#ifdef CONFIG_KEXEC_CORE
792+
static atomic_t waiting_for_crash_ipi = ATOMIC_INIT(0);
793+
#endif
794+
795+
static void ipi_cpu_crash_stop(unsigned int cpu, struct pt_regs *regs)
796+
{
797+
#ifdef CONFIG_KEXEC_CORE
798+
crash_save_cpu(regs, cpu);
799+
800+
atomic_dec(&waiting_for_crash_ipi);
801+
802+
local_irq_disable();
803+
804+
#ifdef CONFIG_HOTPLUG_CPU
805+
if (cpu_ops[cpu]->cpu_die)
806+
cpu_ops[cpu]->cpu_die(cpu);
807+
#endif
808+
809+
/* just in case */
810+
cpu_park_loop();
811+
#endif
812+
}
813+
788814
/*
789815
* Main handler for inter-processor interrupts
790816
*/
@@ -815,6 +841,15 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
815841
irq_exit();
816842
break;
817843

844+
case IPI_CPU_CRASH_STOP:
845+
if (IS_ENABLED(CONFIG_KEXEC_CORE)) {
846+
irq_enter();
847+
ipi_cpu_crash_stop(cpu, regs);
848+
849+
unreachable();
850+
}
851+
break;
852+
818853
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
819854
case IPI_TIMER:
820855
irq_enter();
@@ -883,6 +918,39 @@ void smp_send_stop(void)
883918
pr_warning("SMP: failed to stop secondary CPUs\n");
884919
}
885920

921+
#ifdef CONFIG_KEXEC_CORE
922+
void smp_send_crash_stop(void)
923+
{
924+
cpumask_t mask;
925+
unsigned long timeout;
926+
927+
if (num_online_cpus() == 1)
928+
return;
929+
930+
cpumask_copy(&mask, cpu_online_mask);
931+
cpumask_clear_cpu(smp_processor_id(), &mask);
932+
933+
atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);
934+
935+
pr_crit("SMP: stopping secondary CPUs\n");
936+
smp_cross_call(&mask, IPI_CPU_CRASH_STOP);
937+
938+
/* Wait up to one second for other CPUs to stop */
939+
timeout = USEC_PER_SEC;
940+
while ((atomic_read(&waiting_for_crash_ipi) > 0) && timeout--)
941+
udelay(1);
942+
943+
if (atomic_read(&waiting_for_crash_ipi) > 0)
944+
pr_warning("SMP: failed to stop secondary CPUs %*pbl\n",
945+
cpumask_pr_args(&mask));
946+
}
947+
948+
bool smp_crash_stop_failed(void)
949+
{
950+
return (atomic_read(&waiting_for_crash_ipi) > 0);
951+
}
952+
#endif
953+
886954
/*
887955
* not supported here
888956
*/

0 commit comments

Comments
 (0)