aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDengcheng Zhu <dzhu@wavecomp.com>2018-09-11 17:49:21 -0400
committerPaul Burton <paul.burton@mips.com>2018-09-22 13:31:50 -0400
commit62cac480f33f8f9413d609cb1601b0ee521a86b8 (patch)
tree92d3e1e0e580cbc175cd27b77b0ddf7c931ac338
parentdc57aaf95a516f70e2d527d8287a0332c481a226 (diff)
MIPS: kexec: Make a framework for both jumping and halting on nonboot CPUs
The existing implementation lets machine_kexec() CPU jump to reboot code buffer, whereas other CPUs to relocated_kexec_smp_wait. The natural way to bring up an SMP new kernel would be to let CPU0 do it while others being halted. For those failing to do so, fall back to the jumping method. Signed-off-by: Dengcheng Zhu <dzhu@wavecomp.com> [paul.burton@mips.com: Guard kexec_nonboot_cpu_jump with CONFIG_SMP] Signed-off-by: Paul Burton <paul.burton@mips.com> Patchwork: https://patchwork.linux-mips.org/patch/20570/ Cc: pburton@wavecomp.com Cc: ralf@linux-mips.org Cc: linux-mips@linux-mips.org Cc: rachel.mozes@intel.com
-rw-r--r--arch/mips/cavium-octeon/smp.c7
-rw-r--r--arch/mips/include/asm/kexec.h5
-rw-r--r--arch/mips/include/asm/smp-ops.h3
-rw-r--r--arch/mips/include/asm/smp.h16
-rw-r--r--arch/mips/kernel/crash.c4
-rw-r--r--arch/mips/kernel/machine_kexec.c90
-rw-r--r--arch/mips/kernel/smp-bmips.c7
-rw-r--r--arch/mips/loongson64/loongson-3/smp.c4
8 files changed, 126 insertions, 10 deletions
diff --git a/arch/mips/cavium-octeon/smp.c b/arch/mips/cavium-octeon/smp.c
index 75e7c8625659..39f2a2ec1286 100644
--- a/arch/mips/cavium-octeon/smp.c
+++ b/arch/mips/cavium-octeon/smp.c
@@ -15,6 +15,7 @@
15#include <linux/sched/task_stack.h> 15#include <linux/sched/task_stack.h>
16#include <linux/init.h> 16#include <linux/init.h>
17#include <linux/export.h> 17#include <linux/export.h>
18#include <linux/kexec.h>
18 19
19#include <asm/mmu_context.h> 20#include <asm/mmu_context.h>
20#include <asm/time.h> 21#include <asm/time.h>
@@ -424,6 +425,9 @@ const struct plat_smp_ops octeon_smp_ops = {
424 .cpu_disable = octeon_cpu_disable, 425 .cpu_disable = octeon_cpu_disable,
425 .cpu_die = octeon_cpu_die, 426 .cpu_die = octeon_cpu_die,
426#endif 427#endif
428#ifdef CONFIG_KEXEC
429 .kexec_nonboot_cpu = kexec_nonboot_cpu_jump,
430#endif
427}; 431};
428 432
429static irqreturn_t octeon_78xx_reched_interrupt(int irq, void *dev_id) 433static irqreturn_t octeon_78xx_reched_interrupt(int irq, void *dev_id)
@@ -501,6 +505,9 @@ static const struct plat_smp_ops octeon_78xx_smp_ops = {
501 .cpu_disable = octeon_cpu_disable, 505 .cpu_disable = octeon_cpu_disable,
502 .cpu_die = octeon_cpu_die, 506 .cpu_die = octeon_cpu_die,
503#endif 507#endif
508#ifdef CONFIG_KEXEC
509 .kexec_nonboot_cpu = kexec_nonboot_cpu_jump,
510#endif
504}; 511};
505 512
506void __init octeon_setup_smp(void) 513void __init octeon_setup_smp(void)
diff --git a/arch/mips/include/asm/kexec.h b/arch/mips/include/asm/kexec.h
index 493a3cc7c39a..5eeb648c4e3a 100644
--- a/arch/mips/include/asm/kexec.h
+++ b/arch/mips/include/asm/kexec.h
@@ -39,11 +39,12 @@ extern unsigned long kexec_args[4];
39extern int (*_machine_kexec_prepare)(struct kimage *); 39extern int (*_machine_kexec_prepare)(struct kimage *);
40extern void (*_machine_kexec_shutdown)(void); 40extern void (*_machine_kexec_shutdown)(void);
41extern void (*_machine_crash_shutdown)(struct pt_regs *regs); 41extern void (*_machine_crash_shutdown)(struct pt_regs *regs);
42extern void default_machine_crash_shutdown(struct pt_regs *regs); 42void default_machine_crash_shutdown(struct pt_regs *regs);
43void kexec_nonboot_cpu_jump(void);
44void kexec_reboot(void);
43#ifdef CONFIG_SMP 45#ifdef CONFIG_SMP
44extern const unsigned char kexec_smp_wait[]; 46extern const unsigned char kexec_smp_wait[];
45extern unsigned long secondary_kexec_args[4]; 47extern unsigned long secondary_kexec_args[4];
46extern void (*relocated_kexec_smp_wait) (void *);
47extern atomic_t kexec_ready_to_reboot; 48extern atomic_t kexec_ready_to_reboot;
48extern void (*_crash_smp_send_stop)(void); 49extern void (*_crash_smp_send_stop)(void);
49#endif 50#endif
diff --git a/arch/mips/include/asm/smp-ops.h b/arch/mips/include/asm/smp-ops.h
index 53b2cb8e5966..b7123f9c0785 100644
--- a/arch/mips/include/asm/smp-ops.h
+++ b/arch/mips/include/asm/smp-ops.h
@@ -33,6 +33,9 @@ struct plat_smp_ops {
33 int (*cpu_disable)(void); 33 int (*cpu_disable)(void);
34 void (*cpu_die)(unsigned int cpu); 34 void (*cpu_die)(unsigned int cpu);
35#endif 35#endif
36#ifdef CONFIG_KEXEC
37 void (*kexec_nonboot_cpu)(void);
38#endif
36}; 39};
37 40
38extern void register_smp_ops(const struct plat_smp_ops *ops); 41extern void register_smp_ops(const struct plat_smp_ops *ops);
diff --git a/arch/mips/include/asm/smp.h b/arch/mips/include/asm/smp.h
index 056a6bf13491..7990c1c70471 100644
--- a/arch/mips/include/asm/smp.h
+++ b/arch/mips/include/asm/smp.h
@@ -91,6 +91,22 @@ static inline void __cpu_die(unsigned int cpu)
91extern void play_dead(void); 91extern void play_dead(void);
92#endif 92#endif
93 93
94#ifdef CONFIG_KEXEC
95static inline void kexec_nonboot_cpu(void)
96{
97 extern const struct plat_smp_ops *mp_ops; /* private */
98
99 return mp_ops->kexec_nonboot_cpu();
100}
101
102static inline void *kexec_nonboot_cpu_func(void)
103{
104 extern const struct plat_smp_ops *mp_ops; /* private */
105
106 return mp_ops->kexec_nonboot_cpu;
107}
108#endif
109
94/* 110/*
95 * This function will set up the necessary IPIs for Linux to communicate 111 * This function will set up the necessary IPIs for Linux to communicate
96 * with the CPUs in mask. 112 * with the CPUs in mask.
diff --git a/arch/mips/kernel/crash.c b/arch/mips/kernel/crash.c
index 4c07a43a3242..2c7288041a99 100644
--- a/arch/mips/kernel/crash.c
+++ b/arch/mips/kernel/crash.c
@@ -46,7 +46,9 @@ static void crash_shutdown_secondary(void *passed_regs)
46 46
47 while (!atomic_read(&kexec_ready_to_reboot)) 47 while (!atomic_read(&kexec_ready_to_reboot))
48 cpu_relax(); 48 cpu_relax();
49 relocated_kexec_smp_wait(NULL); 49
50 kexec_reboot();
51
50 /* NOTREACHED */ 52 /* NOTREACHED */
51} 53}
52 54
diff --git a/arch/mips/kernel/machine_kexec.c b/arch/mips/kernel/machine_kexec.c
index 4b3726e4fe3a..14ced77c5ef6 100644
--- a/arch/mips/kernel/machine_kexec.c
+++ b/arch/mips/kernel/machine_kexec.c
@@ -19,15 +19,19 @@ extern const size_t relocate_new_kernel_size;
19extern unsigned long kexec_start_address; 19extern unsigned long kexec_start_address;
20extern unsigned long kexec_indirection_page; 20extern unsigned long kexec_indirection_page;
21 21
22int (*_machine_kexec_prepare)(struct kimage *) = NULL; 22static unsigned long reboot_code_buffer;
23void (*_machine_kexec_shutdown)(void) = NULL; 23
24void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL;
25#ifdef CONFIG_SMP 24#ifdef CONFIG_SMP
26void (*relocated_kexec_smp_wait) (void *); 25static void (*relocated_kexec_smp_wait)(void *);
26
27atomic_t kexec_ready_to_reboot = ATOMIC_INIT(0); 27atomic_t kexec_ready_to_reboot = ATOMIC_INIT(0);
28void (*_crash_smp_send_stop)(void) = NULL; 28void (*_crash_smp_send_stop)(void) = NULL;
29#endif 29#endif
30 30
31int (*_machine_kexec_prepare)(struct kimage *) = NULL;
32void (*_machine_kexec_shutdown)(void) = NULL;
33void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL;
34
31static void kexec_image_info(const struct kimage *kimage) 35static void kexec_image_info(const struct kimage *kimage)
32{ 36{
33 unsigned long i; 37 unsigned long i;
@@ -51,10 +55,16 @@ static void kexec_image_info(const struct kimage *kimage)
51int 55int
52machine_kexec_prepare(struct kimage *kimage) 56machine_kexec_prepare(struct kimage *kimage)
53{ 57{
58#ifdef CONFIG_SMP
59 if (!kexec_nonboot_cpu_func())
60 return -EINVAL;
61#endif
62
54 kexec_image_info(kimage); 63 kexec_image_info(kimage);
55 64
56 if (_machine_kexec_prepare) 65 if (_machine_kexec_prepare)
57 return _machine_kexec_prepare(kimage); 66 return _machine_kexec_prepare(kimage);
67
58 return 0; 68 return 0;
59} 69}
60 70
@@ -63,11 +73,41 @@ machine_kexec_cleanup(struct kimage *kimage)
63{ 73{
64} 74}
65 75
76#ifdef CONFIG_SMP
77static void kexec_shutdown_secondary(void *param)
78{
79 int cpu = smp_processor_id();
80
81 if (!cpu_online(cpu))
82 return;
83
84 /* We won't be sent IPIs any more. */
85 set_cpu_online(cpu, false);
86
87 local_irq_disable();
88 while (!atomic_read(&kexec_ready_to_reboot))
89 cpu_relax();
90
91 kexec_reboot();
92
93 /* NOTREACHED */
94}
95#endif
96
66void 97void
67machine_shutdown(void) 98machine_shutdown(void)
68{ 99{
69 if (_machine_kexec_shutdown) 100 if (_machine_kexec_shutdown)
70 _machine_kexec_shutdown(); 101 _machine_kexec_shutdown();
102
103#ifdef CONFIG_SMP
104 smp_call_function(kexec_shutdown_secondary, NULL, 0);
105
106 while (num_online_cpus() > 1) {
107 cpu_relax();
108 mdelay(1);
109 }
110#endif
71} 111}
72 112
73void 113void
@@ -79,12 +119,47 @@ machine_crash_shutdown(struct pt_regs *regs)
79 default_machine_crash_shutdown(regs); 119 default_machine_crash_shutdown(regs);
80} 120}
81 121
82typedef void (*noretfun_t)(void) __noreturn; 122#ifdef CONFIG_SMP
123void kexec_nonboot_cpu_jump(void)
124{
125 local_flush_icache_range((unsigned long)relocated_kexec_smp_wait,
126 reboot_code_buffer + relocate_new_kernel_size);
127
128 relocated_kexec_smp_wait(NULL);
129}
130#endif
131
132void kexec_reboot(void)
133{
134 void (*do_kexec)(void) __noreturn;
135
136#ifdef CONFIG_SMP
137 if (smp_processor_id() > 0) {
138 /*
139 * Instead of cpu_relax() or wait, this is needed for kexec
140 * smp reboot. Kdump usually doesn't require an smp new
141 * kernel, but kexec may do.
142 */
143 kexec_nonboot_cpu();
144
145 /* NOTREACHED */
146 }
147#endif
148
149 /*
150 * Make sure we get correct instructions written by the
151 * machine_kexec() CPU.
152 */
153 local_flush_icache_range(reboot_code_buffer,
154 reboot_code_buffer + relocate_new_kernel_size);
155
156 do_kexec = (void *)reboot_code_buffer;
157 do_kexec();
158}
83 159
84void 160void
85machine_kexec(struct kimage *image) 161machine_kexec(struct kimage *image)
86{ 162{
87 unsigned long reboot_code_buffer;
88 unsigned long entry; 163 unsigned long entry;
89 unsigned long *ptr; 164 unsigned long *ptr;
90 165
@@ -128,6 +203,7 @@ machine_kexec(struct kimage *image)
128 203
129 printk("Will call new kernel at %08lx\n", image->start); 204 printk("Will call new kernel at %08lx\n", image->start);
130 printk("Bye ...\n"); 205 printk("Bye ...\n");
206 /* Make reboot code buffer available to the boot CPU. */
131 __flush_cache_all(); 207 __flush_cache_all();
132#ifdef CONFIG_SMP 208#ifdef CONFIG_SMP
133 /* All secondary cpus now may jump to kexec_wait cycle */ 209 /* All secondary cpus now may jump to kexec_wait cycle */
@@ -136,5 +212,5 @@ machine_kexec(struct kimage *image)
136 smp_wmb(); 212 smp_wmb();
137 atomic_set(&kexec_ready_to_reboot, 1); 213 atomic_set(&kexec_ready_to_reboot, 1);
138#endif 214#endif
139 ((noretfun_t) reboot_code_buffer)(); 215 kexec_reboot();
140} 216}
diff --git a/arch/mips/kernel/smp-bmips.c b/arch/mips/kernel/smp-bmips.c
index 159e83add4bb..76fae9b79f13 100644
--- a/arch/mips/kernel/smp-bmips.c
+++ b/arch/mips/kernel/smp-bmips.c
@@ -25,6 +25,7 @@
25#include <linux/linkage.h> 25#include <linux/linkage.h>
26#include <linux/bug.h> 26#include <linux/bug.h>
27#include <linux/kernel.h> 27#include <linux/kernel.h>
28#include <linux/kexec.h>
28 29
29#include <asm/time.h> 30#include <asm/time.h>
30#include <asm/pgtable.h> 31#include <asm/pgtable.h>
@@ -423,6 +424,9 @@ const struct plat_smp_ops bmips43xx_smp_ops = {
423 .cpu_disable = bmips_cpu_disable, 424 .cpu_disable = bmips_cpu_disable,
424 .cpu_die = bmips_cpu_die, 425 .cpu_die = bmips_cpu_die,
425#endif 426#endif
427#ifdef CONFIG_KEXEC
428 .kexec_nonboot_cpu = kexec_nonboot_cpu_jump,
429#endif
426}; 430};
427 431
428const struct plat_smp_ops bmips5000_smp_ops = { 432const struct plat_smp_ops bmips5000_smp_ops = {
@@ -437,6 +441,9 @@ const struct plat_smp_ops bmips5000_smp_ops = {
437 .cpu_disable = bmips_cpu_disable, 441 .cpu_disable = bmips_cpu_disable,
438 .cpu_die = bmips_cpu_die, 442 .cpu_die = bmips_cpu_die,
439#endif 443#endif
444#ifdef CONFIG_KEXEC
445 .kexec_nonboot_cpu = kexec_nonboot_cpu_jump,
446#endif
440}; 447};
441 448
442#endif /* CONFIG_SMP */ 449#endif /* CONFIG_SMP */
diff --git a/arch/mips/loongson64/loongson-3/smp.c b/arch/mips/loongson64/loongson-3/smp.c
index 361239bd6b78..b5c1e0aa955e 100644
--- a/arch/mips/loongson64/loongson-3/smp.c
+++ b/arch/mips/loongson64/loongson-3/smp.c
@@ -21,6 +21,7 @@
21#include <linux/sched/task_stack.h> 21#include <linux/sched/task_stack.h>
22#include <linux/smp.h> 22#include <linux/smp.h>
23#include <linux/cpufreq.h> 23#include <linux/cpufreq.h>
24#include <linux/kexec.h>
24#include <asm/processor.h> 25#include <asm/processor.h>
25#include <asm/time.h> 26#include <asm/time.h>
26#include <asm/clock.h> 27#include <asm/clock.h>
@@ -749,4 +750,7 @@ const struct plat_smp_ops loongson3_smp_ops = {
749 .cpu_disable = loongson3_cpu_disable, 750 .cpu_disable = loongson3_cpu_disable,
750 .cpu_die = loongson3_cpu_die, 751 .cpu_die = loongson3_cpu_die,
751#endif 752#endif
753#ifdef CONFIG_KEXEC
754 .kexec_nonboot_cpu = kexec_nonboot_cpu_jump,
755#endif
752}; 756};