|
39 | 39 | #include <linux/completion.h> |
40 | 40 | #include <linux/of.h> |
41 | 41 | #include <linux/irq_work.h> |
| 42 | +#include <linux/kexec.h> |
42 | 43 |
|
43 | 44 | #include <asm/alternative.h> |
44 | 45 | #include <asm/atomic.h> |
@@ -76,6 +77,7 @@ enum ipi_msg_type { |
76 | 77 | IPI_RESCHEDULE, |
77 | 78 | IPI_CALL_FUNC, |
78 | 79 | IPI_CPU_STOP, |
| 80 | + IPI_CPU_CRASH_STOP, |
79 | 81 | IPI_TIMER, |
80 | 82 | IPI_IRQ_WORK, |
81 | 83 | IPI_WAKEUP |
@@ -756,6 +758,7 @@ static const char *ipi_types[NR_IPI] __tracepoint_string = { |
756 | 758 | S(IPI_RESCHEDULE, "Rescheduling interrupts"), |
757 | 759 | S(IPI_CALL_FUNC, "Function call interrupts"), |
758 | 760 | S(IPI_CPU_STOP, "CPU stop interrupts"), |
| 761 | + S(IPI_CPU_CRASH_STOP, "CPU stop (for crash dump) interrupts"), |
759 | 762 | S(IPI_TIMER, "Timer broadcast interrupts"), |
760 | 763 | S(IPI_IRQ_WORK, "IRQ work interrupts"), |
761 | 764 | S(IPI_WAKEUP, "CPU wake-up interrupts"), |
@@ -830,6 +833,29 @@ static void ipi_cpu_stop(unsigned int cpu) |
830 | 833 | cpu_relax(); |
831 | 834 | } |
832 | 835 |
|
| 836 | +#ifdef CONFIG_KEXEC_CORE |
| 837 | +static atomic_t waiting_for_crash_ipi = ATOMIC_INIT(0); |
| 838 | +#endif |
| 839 | + |
| 840 | +static void ipi_cpu_crash_stop(unsigned int cpu, struct pt_regs *regs) |
| 841 | +{ |
| 842 | +#ifdef CONFIG_KEXEC_CORE |
| 843 | + crash_save_cpu(regs, cpu); |
| 844 | + |
| 845 | + atomic_dec(&waiting_for_crash_ipi); |
| 846 | + |
| 847 | + local_irq_disable(); |
| 848 | + |
| 849 | +#ifdef CONFIG_HOTPLUG_CPU |
| 850 | + if (cpu_ops[cpu]->cpu_die) |
| 851 | + cpu_ops[cpu]->cpu_die(cpu); |
| 852 | +#endif |
| 853 | + |
| 854 | + /* just in case */ |
| 855 | + cpu_park_loop(); |
| 856 | +#endif |
| 857 | +} |
| 858 | + |
833 | 859 | /* |
834 | 860 | * Main handler for inter-processor interrupts |
835 | 861 | */ |
@@ -860,6 +886,15 @@ void handle_IPI(int ipinr, struct pt_regs *regs) |
860 | 886 | irq_exit(); |
861 | 887 | break; |
862 | 888 |
|
| 889 | + case IPI_CPU_CRASH_STOP: |
| 890 | + if (IS_ENABLED(CONFIG_KEXEC_CORE)) { |
| 891 | + irq_enter(); |
| 892 | + ipi_cpu_crash_stop(cpu, regs); |
| 893 | + |
| 894 | + unreachable(); |
| 895 | + } |
| 896 | + break; |
| 897 | + |
863 | 898 | #ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST |
864 | 899 | case IPI_TIMER: |
865 | 900 | irq_enter(); |
@@ -932,6 +967,39 @@ void smp_send_stop(void) |
932 | 967 | cpumask_pr_args(cpu_online_mask)); |
933 | 968 | } |
934 | 969 |
|
| 970 | +#ifdef CONFIG_KEXEC_CORE |
| 971 | +void smp_send_crash_stop(void) |
| 972 | +{ |
| 973 | + cpumask_t mask; |
| 974 | + unsigned long timeout; |
| 975 | + |
| 976 | + if (num_online_cpus() == 1) |
| 977 | + return; |
| 978 | + |
| 979 | + cpumask_copy(&mask, cpu_online_mask); |
| 980 | + cpumask_clear_cpu(smp_processor_id(), &mask); |
| 981 | + |
| 982 | + atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1); |
| 983 | + |
| 984 | + pr_crit("SMP: stopping secondary CPUs\n"); |
| 985 | + smp_cross_call(&mask, IPI_CPU_CRASH_STOP); |
| 986 | + |
| 987 | + /* Wait up to one second for other CPUs to stop */ |
| 988 | + timeout = USEC_PER_SEC; |
| 989 | + while ((atomic_read(&waiting_for_crash_ipi) > 0) && timeout--) |
| 990 | + udelay(1); |
| 991 | + |
| 992 | + if (atomic_read(&waiting_for_crash_ipi) > 0) |
| 993 | + pr_warning("SMP: failed to stop secondary CPUs %*pbl\n", |
| 994 | + cpumask_pr_args(&mask)); |
| 995 | +} |
| 996 | + |
| 997 | +bool smp_crash_stop_failed(void) |
| 998 | +{ |
| 999 | + return (atomic_read(&waiting_for_crash_ipi) > 0); |
| 1000 | +} |
| 1001 | +#endif |
| 1002 | + |
935 | 1003 | /* |
936 | 1004 | * not supported here |
937 | 1005 | */ |
|
0 commit comments