Commit | Line | Data |
---|---|---|
63ae2a94 | 1 | /* |
4c9e1385 | 2 | * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) |
1da177e4 LT |
3 | * Licensed under the GPL |
4 | */ | |
5 | ||
37185b33 AV |
6 | #include <linux/percpu.h> |
7 | #include <asm/pgalloc.h> | |
8 | #include <asm/tlb.h> | |
1da177e4 | 9 | |
1da177e4 LT |
10 | #ifdef CONFIG_SMP |
11 | ||
37185b33 AV |
12 | #include <linux/sched.h> |
13 | #include <linux/module.h> | |
14 | #include <linux/threads.h> | |
15 | #include <linux/interrupt.h> | |
16 | #include <linux/err.h> | |
17 | #include <linux/hardirq.h> | |
18 | #include <asm/smp.h> | |
19 | #include <asm/processor.h> | |
20 | #include <asm/spinlock.h> | |
21 | #include <kern.h> | |
22 | #include <irq_user.h> | |
23 | #include <os.h> | |
1da177e4 | 24 | |
1da177e4 LT |
25 | /* Per CPU bogomips and other parameters |
26 | * The only piece used here is the ipi pipe, which is set before SMP is | |
27 | * started and never changed. | |
28 | */ | |
29 | struct cpuinfo_um cpu_data[NR_CPUS]; | |
30 | ||
31 | /* A statistic, can be a little off */ | |
32 | int num_reschedules_sent = 0; | |
33 | ||
34 | /* Not changed after boot */ | |
35 | struct task_struct *idle_threads[NR_CPUS]; | |
36 | ||
37 | void smp_send_reschedule(int cpu) | |
38 | { | |
a6ea4cce | 39 | os_write_file(cpu_data[cpu].ipi_pipe[1], "R", 1); |
1da177e4 LT |
40 | num_reschedules_sent++; |
41 | } | |
42 | ||
43 | void smp_send_stop(void) | |
44 | { | |
45 | int i; | |
46 | ||
47 | printk(KERN_INFO "Stopping all CPUs..."); | |
4c9e1385 JD |
48 | for (i = 0; i < num_online_cpus(); i++) { |
49 | if (i == current_thread->cpu) | |
1da177e4 | 50 | continue; |
a6ea4cce | 51 | os_write_file(cpu_data[i].ipi_pipe[1], "S", 1); |
1da177e4 | 52 | } |
c5d4bb17 | 53 | printk(KERN_CONT "done\n"); |
1da177e4 LT |
54 | } |
55 | ||
56 | static cpumask_t smp_commenced_mask = CPU_MASK_NONE; | |
57 | static cpumask_t cpu_callin_map = CPU_MASK_NONE; | |
58 | ||
59 | static int idle_proc(void *cpup) | |
60 | { | |
61 | int cpu = (int) cpup, err; | |
62 | ||
63 | err = os_pipe(cpu_data[cpu].ipi_pipe, 1, 1); | |
4c9e1385 | 64 | if (err < 0) |
1da177e4 LT |
65 | panic("CPU#%d failed to create IPI pipe, err = %d", cpu, -err); |
66 | ||
bf8fde78 | 67 | os_set_fd_async(cpu_data[cpu].ipi_pipe[0]); |
63ae2a94 | 68 | |
1da177e4 LT |
69 | wmb(); |
70 | if (cpu_test_and_set(cpu, cpu_callin_map)) { | |
4c9e1385 | 71 | printk(KERN_ERR "huh, CPU#%d already present??\n", cpu); |
1da177e4 LT |
72 | BUG(); |
73 | } | |
74 | ||
75 | while (!cpu_isset(cpu, smp_commenced_mask)) | |
76 | cpu_relax(); | |
77 | ||
e545a614 | 78 | notify_cpu_starting(cpu); |
0b5f9c00 | 79 | set_cpu_online(cpu, true); |
1da177e4 | 80 | default_idle(); |
dc764e50 | 81 | return 0; |
1da177e4 LT |
82 | } |
83 | ||
84 | static struct task_struct *idle_thread(int cpu) | |
85 | { | |
86 | struct task_struct *new_task; | |
1da177e4 | 87 | |
dc764e50 JD |
88 | current->thread.request.u.thread.proc = idle_proc; |
89 | current->thread.request.u.thread.arg = (void *) cpu; | |
1da177e4 | 90 | new_task = fork_idle(cpu); |
4c9e1385 | 91 | if (IS_ERR(new_task)) |
1da177e4 LT |
92 | panic("copy_process failed in idle_thread, error = %ld", |
93 | PTR_ERR(new_task)); | |
94 | ||
63ae2a94 | 95 | cpu_tasks[cpu] = ((struct cpu_task) |
1da177e4 LT |
96 | { .pid = new_task->thread.mode.tt.extern_pid, |
97 | .task = new_task } ); | |
98 | idle_threads[cpu] = new_task; | |
42fda663 | 99 | panic("skas mode doesn't support SMP"); |
dc764e50 | 100 | return new_task; |
1da177e4 LT |
101 | } |
102 | ||
103 | void smp_prepare_cpus(unsigned int maxcpus) | |
104 | { | |
105 | struct task_struct *idle; | |
106 | unsigned long waittime; | |
107 | int err, cpu, me = smp_processor_id(); | |
108 | int i; | |
109 | ||
110 | for (i = 0; i < ncpus; ++i) | |
a6a01063 | 111 | set_cpu_possible(i, true); |
1da177e4 | 112 | |
0b5f9c00 | 113 | set_cpu_online(me, true); |
1da177e4 LT |
114 | cpu_set(me, cpu_callin_map); |
115 | ||
116 | err = os_pipe(cpu_data[me].ipi_pipe, 1, 1); | |
4c9e1385 | 117 | if (err < 0) |
1da177e4 LT |
118 | panic("CPU#0 failed to create IPI pipe, errno = %d", -err); |
119 | ||
bf8fde78 | 120 | os_set_fd_async(cpu_data[me].ipi_pipe[0]); |
1da177e4 | 121 | |
4c9e1385 JD |
122 | for (cpu = 1; cpu < ncpus; cpu++) { |
123 | printk(KERN_INFO "Booting processor %d...\n", cpu); | |
63ae2a94 | 124 | |
1da177e4 LT |
125 | idle = idle_thread(cpu); |
126 | ||
127 | init_idle(idle, cpu); | |
1da177e4 LT |
128 | |
129 | waittime = 200000000; | |
130 | while (waittime-- && !cpu_isset(cpu, cpu_callin_map)) | |
131 | cpu_relax(); | |
132 | ||
c5d4bb17 JD |
133 | printk(KERN_INFO "%s\n", |
134 | cpu_isset(cpu, cpu_calling_map) ? "done" : "failed"); | |
1da177e4 LT |
135 | } |
136 | } | |
137 | ||
138 | void smp_prepare_boot_cpu(void) | |
139 | { | |
0b5f9c00 | 140 | set_cpu_online(smp_processor_id(), true); |
1da177e4 LT |
141 | } |
142 | ||
8239c25f | 143 | int __cpu_up(unsigned int cpu, struct task_struct *tidle) |
1da177e4 LT |
144 | { |
145 | cpu_set(cpu, smp_commenced_mask); | |
0b5f9c00 | 146 | while (!cpu_online(cpu)) |
1da177e4 | 147 | mb(); |
dc764e50 | 148 | return 0; |
1da177e4 LT |
149 | } |
150 | ||
151 | int setup_profiling_timer(unsigned int multiplier) | |
152 | { | |
153 | printk(KERN_INFO "setup_profiling_timer\n"); | |
dc764e50 | 154 | return 0; |
1da177e4 LT |
155 | } |
156 | ||
157 | void smp_call_function_slave(int cpu); | |
158 | ||
159 | void IPI_handler(int cpu) | |
160 | { | |
161 | unsigned char c; | |
162 | int fd; | |
163 | ||
164 | fd = cpu_data[cpu].ipi_pipe[0]; | |
a6ea4cce | 165 | while (os_read_file(fd, &c, 1) == 1) { |
1da177e4 LT |
166 | switch (c) { |
167 | case 'C': | |
168 | smp_call_function_slave(cpu); | |
169 | break; | |
170 | ||
171 | case 'R': | |
184748cc | 172 | scheduler_ipi(); |
1da177e4 LT |
173 | break; |
174 | ||
175 | case 'S': | |
4c9e1385 JD |
176 | printk(KERN_INFO "CPU#%d stopping\n", cpu); |
177 | while (1) | |
1da177e4 LT |
178 | pause(); |
179 | break; | |
180 | ||
181 | default: | |
4c9e1385 JD |
182 | printk(KERN_ERR "CPU#%d received unknown IPI [%c]!\n", |
183 | cpu, c); | |
1da177e4 LT |
184 | break; |
185 | } | |
186 | } | |
187 | } | |
188 | ||
189 | int hard_smp_processor_id(void) | |
190 | { | |
dc764e50 | 191 | return pid_to_processor_id(os_getpid()); |
1da177e4 LT |
192 | } |
193 | ||
194 | static DEFINE_SPINLOCK(call_lock); | |
195 | static atomic_t scf_started; | |
196 | static atomic_t scf_finished; | |
197 | static void (*func)(void *info); | |
198 | static void *info; | |
199 | ||
200 | void smp_call_function_slave(int cpu) | |
201 | { | |
202 | atomic_inc(&scf_started); | |
203 | (*func)(info); | |
204 | atomic_inc(&scf_finished); | |
205 | } | |
206 | ||
8691e5a8 | 207 | int smp_call_function(void (*_func)(void *info), void *_info, int wait) |
1da177e4 LT |
208 | { |
209 | int cpus = num_online_cpus() - 1; | |
210 | int i; | |
211 | ||
212 | if (!cpus) | |
213 | return 0; | |
214 | ||
215 | /* Can deadlock when called with interrupts disabled */ | |
216 | WARN_ON(irqs_disabled()); | |
217 | ||
218 | spin_lock_bh(&call_lock); | |
219 | atomic_set(&scf_started, 0); | |
220 | atomic_set(&scf_finished, 0); | |
221 | func = _func; | |
222 | info = _info; | |
223 | ||
224 | for_each_online_cpu(i) | |
a6ea4cce | 225 | os_write_file(cpu_data[i].ipi_pipe[1], "C", 1); |
1da177e4 LT |
226 | |
227 | while (atomic_read(&scf_started) != cpus) | |
228 | barrier(); | |
229 | ||
230 | if (wait) | |
231 | while (atomic_read(&scf_finished) != cpus) | |
232 | barrier(); | |
233 | ||
234 | spin_unlock_bh(&call_lock); | |
235 | return 0; | |
236 | } | |
237 | ||
238 | #endif |