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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d71331b was d71331b, checked in by Adam Hraska <adam.hraska+hos@…>, 13 years ago

smp_call: Moved content of arch header to generic header.

  • Property mode set to 100644
File size: 5.1 KB
Line 
1#include <smp/smp_call.h>
2#include <arch.h>
3#include <config.h>
4#include <preemption.h>
5#include <compiler/barrier.h>
6#include <arch/barrier.h>
7#include <arch/asm.h> /* interrupt_disable */
8
9
10static void call_start(smp_call_t *call_info, smp_call_func_t func, void *arg);
11static void call_done(smp_call_t *call_info);
12static void call_wait(smp_call_t *call_info);
13
14
15void smp_call_init(void)
16{
17 ASSERT(CPU);
18 ASSERT(PREEMPTION_DISABLED || interrupts_disabled());
19
20 spinlock_initialize(&CPU->smp_calls_lock, "cpu[].smp_calls_lock");
21 list_initialize(&CPU->smp_pending_calls);
22}
23
24/** Invokes a function on a specific cpu and waits for it to complete.
25 *
26 * Calls @a func on the CPU denoted by its logical id @cpu_id .
27 * The function will execute with interrupts disabled. It should
28 * be a quick and simple function and must never block.
29 *
30 * If @a cpu_id is the local CPU, the function will be invoked
31 * directly.
32 *
33 * @param cpu_id Destination CPU's logical id (eg CPU->id)
34 * @param func Function to call.
35 * @param arg Argument to pass to the user supplied function @a func.
36 */
37void smp_call(unsigned int cpu_id, smp_call_func_t func, void *arg)
38{
39 smp_call_t call_info;
40 smp_call_async(cpu_id, func, arg, &call_info);
41 smp_call_wait(&call_info);
42}
43
44/** Invokes a function on a specific cpu asynchronously.
45 *
46 * Calls @a func on the CPU denoted by its logical id @cpu_id .
47 * The function will execute with interrupts disabled. It should
48 * be a quick and simple function and must never block.
49 *
50 * Pass @a call_info to smp_call_wait() in order to wait for
51 * @a func to complete.
52 *
53 * @a call_info must be valid until @a func returns.
54 *
55 * If @a cpu_id is the local CPU, the function will be invoked
56 * directly.
57 *
58 * Interrupts must be enabled. Otherwise you run the risk
59 * of a deadlock.
60 *
61 * @param cpu_id Destination CPU's logical id (eg CPU->id)
62 * @param func Function to call.
63 * @param arg Argument to pass to the user supplied function @a func.
64 * @param call_info Use it to wait for the function to complete. Must
65 * be valid until the function completes.
66 */
67void smp_call_async(unsigned int cpu_id, smp_call_func_t func, void *arg,
68 smp_call_t *call_info)
69{
70 /* todo: doc deadlock */
71 ASSERT(!interrupts_disabled());
72 ASSERT(call_info != NULL);
73
74 /* Discard invalid calls. */
75 if (config.cpu_count <= cpu_id) {
76 call_start(call_info, func, arg);
77 call_done(call_info);
78 return;
79 }
80
81 /* Protect cpu->id against migration. */
82 preemption_disable();
83
84 call_start(call_info, func, arg);
85
86 if (cpu_id != CPU->id) {
87#ifdef CONFIG_SMP
88 spinlock_lock(&cpus[cpu_id].smp_calls_lock);
89 list_append(&call_info->calls_link, &cpus[cpu_id].smp_pending_calls);
90 spinlock_unlock(&cpus[cpu_id].smp_calls_lock);
91
92 arch_smp_call_ipi(cpu_id);
93#endif
94 } else {
95 /* Invoke local smp calls in place. */
96 ipl_t ipl = interrupts_disable();
97 func(arg);
98 interrupts_restore(ipl);
99
100 call_done(call_info);
101 }
102
103 preemption_enable();
104}
105
106/** Waits for a function invoked on another CPU asynchronously to complete.
107 *
108 * Does not sleep but rather spins.
109 *
110 * Example usage:
111 * @code
112 * void hello(void *p) {
113 * puts((char*)p);
114 * }
115 *
116 * smp_call_t call_info;
117 * smp_call_async(cpus[2].id, hello, "hi!\n", &call_info);
118 * // Do some work. In the meantime, hello() is executed on cpu2.
119 * smp_call_wait(&call_info);
120 * @endcode
121 *
122 * @param call_info Initialized by smp_call_async().
123 */
124void smp_call_wait(smp_call_t *call_info)
125{
126 call_wait(call_info);
127}
128
129#ifdef CONFIG_SMP
130
131/** Architecture independent smp call IPI handler.
132 *
133 * Interrupts must be disabled.
134 */
135void smp_call_ipi_recv(void)
136{
137 ASSERT(interrupts_disabled());
138 ASSERT(CPU);
139
140 list_t calls_list;
141 list_initialize(&calls_list);
142
143 spinlock_lock(&CPU->smp_calls_lock);
144 list_splice(&CPU->smp_pending_calls, &calls_list.head);
145 spinlock_unlock(&CPU->smp_calls_lock);
146
147 /* Walk the list manually, so that we can safely remove list items. */
148 for (link_t *cur = calls_list.head.next, *next = cur->next;
149 !list_empty(&calls_list); cur = next, next = cur->next) {
150
151 smp_call_t *call_info = list_get_instance(cur, smp_call_t, calls_link);
152 list_remove(cur);
153
154 call_info->func(call_info->arg);
155 call_done(call_info);
156 }
157}
158
159#endif /* CONFIG_SMP */
160
161static void call_start(smp_call_t *call_info, smp_call_func_t func, void *arg)
162{
163 link_initialize(&call_info->calls_link);
164 call_info->func = func;
165 call_info->arg = arg;
166
167 /*
168 * We can't use standard spinlocks here because we want to lock
169 * the structure on one cpu and unlock it on another (without
170 * messing up the preemption count).
171 */
172 atomic_set(&call_info->pending, 1);
173
174 /* Let initialization complete before continuing. */
175 memory_barrier();
176}
177
178static void call_done(smp_call_t *call_info)
179{
180 /*
181 * Separate memory accesses of the called function from the
182 * announcement of its completion.
183 */
184 memory_barrier();
185 atomic_set(&call_info->pending, 0);
186}
187
188static void call_wait(smp_call_t *call_info)
189{
190 do {
191 /*
192 * Ensure memory accesses following call_wait() are ordered
193 * after completion of the called function on another cpu.
194 * Also, speed up loading of call_info->pending.
195 */
196 memory_barrier();
197 } while (atomic_get(&call_info->pending));
198}
199
200
201
Note: See TracBrowser for help on using the repository browser.