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

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

smp_call: initial unicast version for ia32, amd64.

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