Commit d556a9ae authored by Palmer Dabbelt's avatar Palmer Dabbelt

Merge patch series "riscv: kexec: Fxiup crash_save percpu and machine_kexec_mask_interrupts"

guoren@kernel.org <guoren@kernel.org> says:

From: Guo Ren <guoren@linux.alibaba.com>

Current riscv kexec can't crash_save percpu states and disable
interrupts properly. The patch series fix them, make kexec work correct.

* b4-shazam-merge:
  riscv: kexec: Fixup crash_smp_send_stop without multi cores
  riscv: kexec: Fixup irq controller broken in kexec crash path

Link: https://lore.kernel.org/r/20221020141603.2856206-1-guoren@kernel.orgSigned-off-by: default avatarPalmer Dabbelt <palmer@rivosinc.com>
parents 6fdd5d2f 9b932aad
...@@ -50,6 +50,9 @@ void riscv_set_ipi_ops(const struct riscv_ipi_ops *ops); ...@@ -50,6 +50,9 @@ void riscv_set_ipi_ops(const struct riscv_ipi_ops *ops);
/* Clear IPI for current CPU */ /* Clear IPI for current CPU */
void riscv_clear_ipi(void); void riscv_clear_ipi(void);
/* Check other CPUs stop or not */
bool smp_crash_stop_failed(void);
/* Secondary hart entry */ /* Secondary hart entry */
asmlinkage void smp_callin(void); asmlinkage void smp_callin(void);
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
#include <linux/compiler.h> /* For unreachable() */ #include <linux/compiler.h> /* For unreachable() */
#include <linux/cpu.h> /* For cpu_down() */ #include <linux/cpu.h> /* For cpu_down() */
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
/* /*
* kexec_image_info - Print received image details * kexec_image_info - Print received image details
...@@ -138,20 +140,35 @@ void machine_shutdown(void) ...@@ -138,20 +140,35 @@ void machine_shutdown(void)
#endif #endif
} }
/* Override the weak function in kernel/panic.c */ static void machine_kexec_mask_interrupts(void)
void crash_smp_send_stop(void)
{ {
static int cpus_stopped; unsigned int i;
struct irq_desc *desc;
/* for_each_irq_desc(i, desc) {
* This function can be called twice in panic path, but obviously struct irq_chip *chip;
* we execute this only once. int ret;
*/
if (cpus_stopped) chip = irq_desc_get_chip(desc);
return; if (!chip)
continue;
/*
* First try to remove the active state. If this
* fails, try to EOI the interrupt.
*/
ret = irq_set_irqchip_state(i, IRQCHIP_STATE_ACTIVE, false);
if (ret && irqd_irq_inprogress(&desc->irq_data) &&
chip->irq_eoi)
chip->irq_eoi(&desc->irq_data);
smp_send_stop(); if (chip->irq_mask)
cpus_stopped = 1; chip->irq_mask(&desc->irq_data);
if (chip->irq_disable && !irqd_irq_disabled(&desc->irq_data))
chip->irq_disable(&desc->irq_data);
}
} }
/* /*
...@@ -169,6 +186,8 @@ machine_crash_shutdown(struct pt_regs *regs) ...@@ -169,6 +186,8 @@ machine_crash_shutdown(struct pt_regs *regs)
crash_smp_send_stop(); crash_smp_send_stop();
crash_save_cpu(regs, smp_processor_id()); crash_save_cpu(regs, smp_processor_id());
machine_kexec_mask_interrupts();
pr_info("Starting crashdump kernel...\n"); pr_info("Starting crashdump kernel...\n");
} }
...@@ -195,6 +214,11 @@ machine_kexec(struct kimage *image) ...@@ -195,6 +214,11 @@ machine_kexec(struct kimage *image)
void *control_code_buffer = page_address(image->control_code_page); void *control_code_buffer = page_address(image->control_code_page);
riscv_kexec_method kexec_method = NULL; riscv_kexec_method kexec_method = NULL;
#ifdef CONFIG_SMP
WARN(smp_crash_stop_failed(),
"Some CPUs may be stale, kdump will be unreliable.\n");
#endif
if (image->type != KEXEC_TYPE_CRASH) if (image->type != KEXEC_TYPE_CRASH)
kexec_method = control_code_buffer; kexec_method = control_code_buffer;
else else
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/clockchips.h> #include <linux/clockchips.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kexec.h>
#include <linux/profile.h> #include <linux/profile.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -22,11 +23,13 @@ ...@@ -22,11 +23,13 @@
#include <asm/sbi.h> #include <asm/sbi.h>
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/cpu_ops.h>
enum ipi_message_type { enum ipi_message_type {
IPI_RESCHEDULE, IPI_RESCHEDULE,
IPI_CALL_FUNC, IPI_CALL_FUNC,
IPI_CPU_STOP, IPI_CPU_STOP,
IPI_CPU_CRASH_STOP,
IPI_IRQ_WORK, IPI_IRQ_WORK,
IPI_TIMER, IPI_TIMER,
IPI_MAX IPI_MAX
...@@ -71,6 +74,32 @@ static void ipi_stop(void) ...@@ -71,6 +74,32 @@ static void ipi_stop(void)
wait_for_interrupt(); wait_for_interrupt();
} }
#ifdef CONFIG_KEXEC_CORE
static atomic_t waiting_for_crash_ipi = ATOMIC_INIT(0);
static inline void ipi_cpu_crash_stop(unsigned int cpu, struct pt_regs *regs)
{
crash_save_cpu(regs, cpu);
atomic_dec(&waiting_for_crash_ipi);
local_irq_disable();
#ifdef CONFIG_HOTPLUG_CPU
if (cpu_has_hotplug(cpu))
cpu_ops[cpu]->cpu_stop();
#endif
for(;;)
wait_for_interrupt();
}
#else
static inline void ipi_cpu_crash_stop(unsigned int cpu, struct pt_regs *regs)
{
unreachable();
}
#endif
static const struct riscv_ipi_ops *ipi_ops __ro_after_init; static const struct riscv_ipi_ops *ipi_ops __ro_after_init;
void riscv_set_ipi_ops(const struct riscv_ipi_ops *ops) void riscv_set_ipi_ops(const struct riscv_ipi_ops *ops)
...@@ -124,8 +153,9 @@ void arch_irq_work_raise(void) ...@@ -124,8 +153,9 @@ void arch_irq_work_raise(void)
void handle_IPI(struct pt_regs *regs) void handle_IPI(struct pt_regs *regs)
{ {
unsigned long *pending_ipis = &ipi_data[smp_processor_id()].bits; unsigned int cpu = smp_processor_id();
unsigned long *stats = ipi_data[smp_processor_id()].stats; unsigned long *pending_ipis = &ipi_data[cpu].bits;
unsigned long *stats = ipi_data[cpu].stats;
riscv_clear_ipi(); riscv_clear_ipi();
...@@ -154,6 +184,10 @@ void handle_IPI(struct pt_regs *regs) ...@@ -154,6 +184,10 @@ void handle_IPI(struct pt_regs *regs)
ipi_stop(); ipi_stop();
} }
if (ops & (1 << IPI_CPU_CRASH_STOP)) {
ipi_cpu_crash_stop(cpu, get_irq_regs());
}
if (ops & (1 << IPI_IRQ_WORK)) { if (ops & (1 << IPI_IRQ_WORK)) {
stats[IPI_IRQ_WORK]++; stats[IPI_IRQ_WORK]++;
irq_work_run(); irq_work_run();
...@@ -176,6 +210,7 @@ static const char * const ipi_names[] = { ...@@ -176,6 +210,7 @@ static const char * const ipi_names[] = {
[IPI_RESCHEDULE] = "Rescheduling interrupts", [IPI_RESCHEDULE] = "Rescheduling interrupts",
[IPI_CALL_FUNC] = "Function call interrupts", [IPI_CALL_FUNC] = "Function call interrupts",
[IPI_CPU_STOP] = "CPU stop interrupts", [IPI_CPU_STOP] = "CPU stop interrupts",
[IPI_CPU_CRASH_STOP] = "CPU stop (for crash dump) interrupts",
[IPI_IRQ_WORK] = "IRQ work interrupts", [IPI_IRQ_WORK] = "IRQ work interrupts",
[IPI_TIMER] = "Timer broadcast interrupts", [IPI_TIMER] = "Timer broadcast interrupts",
}; };
...@@ -235,6 +270,64 @@ void smp_send_stop(void) ...@@ -235,6 +270,64 @@ void smp_send_stop(void)
cpumask_pr_args(cpu_online_mask)); cpumask_pr_args(cpu_online_mask));
} }
#ifdef CONFIG_KEXEC_CORE
/*
* The number of CPUs online, not counting this CPU (which may not be
* fully online and so not counted in num_online_cpus()).
*/
static inline unsigned int num_other_online_cpus(void)
{
unsigned int this_cpu_online = cpu_online(smp_processor_id());
return num_online_cpus() - this_cpu_online;
}
void crash_smp_send_stop(void)
{
static int cpus_stopped;
cpumask_t mask;
unsigned long timeout;
/*
* This function can be called twice in panic path, but obviously
* we execute this only once.
*/
if (cpus_stopped)
return;
cpus_stopped = 1;
/*
* If this cpu is the only one alive at this point in time, online or
* not, there are no stop messages to be sent around, so just back out.
*/
if (num_other_online_cpus() == 0)
return;
cpumask_copy(&mask, cpu_online_mask);
cpumask_clear_cpu(smp_processor_id(), &mask);
atomic_set(&waiting_for_crash_ipi, num_other_online_cpus());
pr_crit("SMP: stopping secondary CPUs\n");
send_ipi_mask(&mask, IPI_CPU_CRASH_STOP);
/* Wait up to one second for other CPUs to stop */
timeout = USEC_PER_SEC;
while ((atomic_read(&waiting_for_crash_ipi) > 0) && timeout--)
udelay(1);
if (atomic_read(&waiting_for_crash_ipi) > 0)
pr_warn("SMP: failed to stop secondary CPUs %*pbl\n",
cpumask_pr_args(&mask));
}
bool smp_crash_stop_failed(void)
{
return (atomic_read(&waiting_for_crash_ipi) > 0);
}
#endif
void smp_send_reschedule(int cpu) void smp_send_reschedule(int cpu)
{ {
send_ipi_single(cpu, IPI_RESCHEDULE); send_ipi_single(cpu, IPI_RESCHEDULE);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment