|
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