source: mainline/kernel/generic/src/smp/smp_call.c@ 3061bc1

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3061bc1 was 1b20da0, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on non-empty lines, in certain file types.

Command used: tools/srepl '\([^[:space:]]\)\s\+$' '\1' -- *.c *.h *.py *.sh *.s *.S *.ag

  • Property mode set to 100644
File size: 8.4 KB
Line 
1/*
2 * Copyright (c) 2012 Adam Hraska
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup generic
30 * @{
31 */
32
33/**
34 * @file
35 * @brief Facility to invoke functions on other cpus via IPIs.
36 */
37
38#include <smp/smp_call.h>
39#include <arch/barrier.h>
40#include <arch/asm.h> /* interrupt_disable */
41#include <arch.h>
42#include <assert.h>
43#include <config.h>
44#include <preemption.h>
45#include <cpu.h>
46
47static void call_start(smp_call_t *call_info, smp_call_func_t func, void *arg);
48static void call_done(smp_call_t *call_info);
49static void call_wait(smp_call_t *call_info);
50
51
52/** Init smp_call() on the local cpu. */
53void smp_call_init(void)
54{
55 assert(CPU);
56 assert(PREEMPTION_DISABLED || interrupts_disabled());
57
58 spinlock_initialize(&CPU->smp_calls_lock, "cpu[].smp_calls_lock");
59 list_initialize(&CPU->smp_pending_calls);
60}
61
62/** Invokes a function on a specific cpu and waits for it to complete.
63 *
64 * Calls @a func on the CPU denoted by its logical id @cpu_id .
65 * The function will execute with interrupts disabled. It should
66 * be a quick and simple function and must never block.
67 *
68 * If @a cpu_id is the local CPU, the function will be invoked
69 * directly.
70 *
71 * All memory accesses of prior to smp_call() will be visible
72 * to @a func on cpu @a cpu_id. Similarly, any changes @a func
73 * makes on cpu @a cpu_id will be visible on this cpu once
74 * smp_call() returns.
75 *
76 * Invoking @a func on the destination cpu acts as a memory barrier
77 * on that cpu.
78 *
79 * @param cpu_id Destination CPU's logical id (eg CPU->id)
80 * @param func Function to call.
81 * @param arg Argument to pass to the user supplied function @a func.
82 */
83void smp_call(unsigned int cpu_id, smp_call_func_t func, void *arg)
84{
85 smp_call_t call_info;
86 smp_call_async(cpu_id, func, arg, &call_info);
87 smp_call_wait(&call_info);
88}
89
90/** Invokes a function on a specific cpu asynchronously.
91 *
92 * Calls @a func on the CPU denoted by its logical id @cpu_id .
93 * The function will execute with interrupts disabled. It should
94 * be a quick and simple function and must never block.
95 *
96 * Pass @a call_info to smp_call_wait() in order to wait for
97 * @a func to complete.
98 *
99 * @a call_info must be valid until/after @a func returns. Use
100 * smp_call_wait() to wait until it is safe to free @a call_info.
101 *
102 * If @a cpu_id is the local CPU, the function will be invoked
103 * directly. If the destination cpu id @a cpu_id is invalid
104 * or denotes an inactive cpu, the call is discarded immediately.
105 *
106 * All memory accesses of the caller prior to smp_call_async()
107 * will be made visible to @a func on the other cpu. Similarly,
108 * any changes @a func makes on cpu @a cpu_id will be visible
109 * to this cpu when smp_call_wait() returns.
110 *
111 * Invoking @a func on the destination cpu acts as a memory barrier
112 * on that cpu.
113 *
114 * Interrupts must be enabled. Otherwise you run the risk
115 * of a deadlock.
116 *
117 * @param cpu_id Destination CPU's logical id (eg CPU->id).
118 * @param func Function to call.
119 * @param arg Argument to pass to the user supplied function @a func.
120 * @param call_info Use it to wait for the function to complete. Must
121 * be valid until the function completes.
122 */
123void smp_call_async(unsigned int cpu_id, smp_call_func_t func, void *arg,
124 smp_call_t *call_info)
125{
126 /*
127 * Interrupts must not be disabled or you run the risk of a deadlock
128 * if both the destination and source cpus try to send an IPI to each
129 * other with interrupts disabled. Because the interrupts are disabled
130 * the IPIs cannot be delivered and both cpus will forever busy wait
131 * for an acknowledgment of the IPI from the other cpu.
132 */
133 assert(!interrupts_disabled());
134 assert(call_info != NULL);
135
136 /* Discard invalid calls. */
137 if (config.cpu_count <= cpu_id || !cpus[cpu_id].active) {
138 call_start(call_info, func, arg);
139 call_done(call_info);
140 return;
141 }
142
143 /* Protect cpu->id against migration. */
144 preemption_disable();
145
146 call_start(call_info, func, arg);
147
148 if (cpu_id != CPU->id) {
149#ifdef CONFIG_SMP
150 spinlock_lock(&cpus[cpu_id].smp_calls_lock);
151 list_append(&call_info->calls_link, &cpus[cpu_id].smp_pending_calls);
152 spinlock_unlock(&cpus[cpu_id].smp_calls_lock);
153
154 /*
155 * If a platform supports SMP it must implement arch_smp_call_ipi().
156 * It should issue an IPI on cpu_id and invoke smp_call_ipi_recv()
157 * on cpu_id in turn.
158 *
159 * Do not implement as just an empty dummy function. Instead
160 * consider providing a full implementation or at least a version
161 * that panics if invoked. Note that smp_call_async() never
162 * calls arch_smp_call_ipi() on uniprocessors even if CONFIG_SMP.
163 */
164 arch_smp_call_ipi(cpu_id);
165#endif
166 } else {
167 /* Invoke local smp calls in place. */
168 ipl_t ipl = interrupts_disable();
169 func(arg);
170 interrupts_restore(ipl);
171
172 call_done(call_info);
173 }
174
175 preemption_enable();
176}
177
178/** Waits for a function invoked on another CPU asynchronously to complete.
179 *
180 * Does not sleep but rather spins.
181 *
182 * Example usage:
183 * @code
184 * void hello(void *p) {
185 * puts((char*)p);
186 * }
187 *
188 * smp_call_t call_info;
189 * smp_call_async(cpus[2].id, hello, "hi!\n", &call_info);
190 * // Do some work. In the meantime, hello() is executed on cpu2.
191 * smp_call_wait(&call_info);
192 * @endcode
193 *
194 * @param call_info Initialized by smp_call_async().
195 */
196void smp_call_wait(smp_call_t *call_info)
197{
198 call_wait(call_info);
199}
200
201#ifdef CONFIG_SMP
202
203/** Architecture independent smp call IPI handler.
204 *
205 * Interrupts must be disabled. Tolerates spurious calls.
206 */
207void smp_call_ipi_recv(void)
208{
209 assert(interrupts_disabled());
210 assert(CPU);
211
212 list_t calls_list;
213 list_initialize(&calls_list);
214
215 /*
216 * Acts as a load memory barrier. Any changes made by the cpu that
217 * added the smp_call to calls_list will be made visible to this cpu.
218 */
219 spinlock_lock(&CPU->smp_calls_lock);
220 list_concat(&calls_list, &CPU->smp_pending_calls);
221 spinlock_unlock(&CPU->smp_calls_lock);
222
223 /* Walk the list manually, so that we can safely remove list items. */
224 for (link_t *cur = calls_list.head.next, *next = cur->next;
225 !list_empty(&calls_list); cur = next, next = cur->next) {
226
227 smp_call_t *call_info = list_get_instance(cur, smp_call_t, calls_link);
228 list_remove(cur);
229
230 call_info->func(call_info->arg);
231 call_done(call_info);
232 }
233}
234
235#endif /* CONFIG_SMP */
236
237static void call_start(smp_call_t *call_info, smp_call_func_t func, void *arg)
238{
239 link_initialize(&call_info->calls_link);
240 call_info->func = func;
241 call_info->arg = arg;
242
243 /*
244 * We can't use standard spinlocks here because we want to lock
245 * the structure on one cpu and unlock it on another (without
246 * messing up the preemption count).
247 */
248 atomic_set(&call_info->pending, 1);
249
250 /* Let initialization complete before continuing. */
251 memory_barrier();
252}
253
254static void call_done(smp_call_t *call_info)
255{
256 /*
257 * Separate memory accesses of the called function from the
258 * announcement of its completion.
259 */
260 memory_barrier();
261 atomic_set(&call_info->pending, 0);
262}
263
264static void call_wait(smp_call_t *call_info)
265{
266 do {
267 /*
268 * Ensure memory accesses following call_wait() are ordered
269 * after completion of the called function on another cpu.
270 * Also, speed up loading of call_info->pending.
271 */
272 memory_barrier();
273 } while (atomic_get(&call_info->pending));
274}
275
276
277/** @}
278 */
Note: See TracBrowser for help on using the repository browser.