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

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

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • 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.