|
37 | 37 | #include <linux/completion.h> |
38 | 38 | #include <linux/of.h> |
39 | 39 | #include <linux/irq_work.h> |
| 40 | +#include <linux/kexec.h> |
40 | 41 |
|
41 | 42 | #include <asm/alternative.h> |
42 | 43 | #include <asm/atomic.h> |
@@ -70,6 +71,7 @@ enum ipi_msg_type { |
70 | 71 | IPI_RESCHEDULE, |
71 | 72 | IPI_CALL_FUNC, |
72 | 73 | IPI_CPU_STOP, |
| 74 | + IPI_CPU_CRASH_STOP, |
73 | 75 | IPI_TIMER, |
74 | 76 | IPI_IRQ_WORK, |
75 | 77 | IPI_WAKEUP |
@@ -701,6 +703,7 @@ static const char *ipi_types[NR_IPI] __tracepoint_string = { |
701 | 703 | S(IPI_RESCHEDULE, "Rescheduling interrupts"), |
702 | 704 | S(IPI_CALL_FUNC, "Function call interrupts"), |
703 | 705 | S(IPI_CPU_STOP, "CPU stop interrupts"), |
| 706 | + S(IPI_CPU_CRASH_STOP, "CPU stop (for crash dump) interrupts"), |
704 | 707 | S(IPI_TIMER, "Timer broadcast interrupts"), |
705 | 708 | S(IPI_IRQ_WORK, "IRQ work interrupts"), |
706 | 709 | S(IPI_WAKEUP, "CPU wake-up interrupts"), |
@@ -785,6 +788,29 @@ static void ipi_cpu_stop(unsigned int cpu) |
785 | 788 | cpu_relax(); |
786 | 789 | } |
787 | 790 |
|
| 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 | + |
788 | 814 | /* |
789 | 815 | * Main handler for inter-processor interrupts |
790 | 816 | */ |
@@ -815,6 +841,15 @@ void handle_IPI(int ipinr, struct pt_regs *regs) |
815 | 841 | irq_exit(); |
816 | 842 | break; |
817 | 843 |
|
| 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 | + |
818 | 853 | #ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST |
819 | 854 | case IPI_TIMER: |
820 | 855 | irq_enter(); |
@@ -883,6 +918,39 @@ void smp_send_stop(void) |
883 | 918 | pr_warning("SMP: failed to stop secondary CPUs\n"); |
884 | 919 | } |
885 | 920 |
|
| 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 | + |
886 | 954 | /* |
887 | 955 | * not supported here |
888 | 956 | */ |
|
0 commit comments