1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25 /*
26 * Copyright (c) 2010, Intel Corporation.
27 * All rights reserved.
28 */
29
30 #include <sys/types.h>
31 #include <sys/param.h>
32 #include <sys/t_lock.h>
33 #include <sys/thread.h>
34 #include <sys/cpuvar.h>
35 #include <sys/x_call.h>
36 #include <sys/xc_levels.h>
37 #include <sys/cpu.h>
38 #include <sys/psw.h>
39 #include <sys/sunddi.h>
40 #include <sys/debug.h>
41 #include <sys/systm.h>
42 #include <sys/archsystm.h>
43 #include <sys/machsystm.h>
44 #include <sys/mutex_impl.h>
45 #include <sys/stack.h>
46 #include <sys/promif.h>
47 #include <sys/x86_archext.h>
48
49 /*
50 * Implementation for cross-processor calls via interprocessor interrupts
51 *
52 * This implementation uses a message passing architecture to allow multiple
53 * concurrent cross calls to be in flight at any given time. We use the cmpxchg
54 * instruction, aka casptr(), to implement simple efficient work queues for
55 * message passing between CPUs with almost no need for regular locking.
56 * See xc_extract() and xc_insert() below.
57 *
58 * The general idea is that initiating a cross call means putting a message
59 * on a target(s) CPU's work queue. Any synchronization is handled by passing
60 * the message back and forth between initiator and target(s).
61 *
62 * Every CPU has xc_work_cnt, which indicates it has messages to process.
63 * This value is incremented as message traffic is initiated and decremented
64 * with every message that finishes all processing.
65 *
66 * The code needs no mfence or other membar_*() calls. The uses of
67 * casptr(), cas32() and atomic_dec_32() for the message passing are
68 * implemented with LOCK prefix instructions which are equivalent to mfence.
69 *
70 * One interesting aspect of this implmentation is that it allows 2 or more
71 * CPUs to initiate cross calls to intersecting sets of CPUs at the same time.
72 * The cross call processing by the CPUs will happen in any order with only
73 * a guarantee, for xc_call() and xc_sync(), that an initiator won't return
74 * from cross calls before all slaves have invoked the function.
75 *
76 * The reason for this asynchronous approach is to allow for fast global
77 * TLB shootdowns. If all CPUs, say N, tried to do a global TLB invalidation
78 * on a different Virtual Address at the same time. The old code required
79 * N squared IPIs. With this method, depending on timing, it could happen
80 * with just N IPIs.
81 */
82
83 /*
84 * The default is to not enable collecting counts of IPI information, since
85 * the updating of shared cachelines could cause excess bus traffic.
86 */
87 uint_t xc_collect_enable = 0;
88 uint64_t xc_total_cnt = 0; /* total #IPIs sent for cross calls */
89 uint64_t xc_multi_cnt = 0; /* # times we piggy backed on another IPI */
90
91 /*
92 * Values for message states. Here are the normal transitions. A transition
93 * of "->" happens in the slave cpu and "=>" happens in the master cpu as
94 * the messages are passed back and forth.
95 *
96 * FREE => ASYNC -> DONE => FREE
97 * FREE => CALL -> DONE => FREE
98 * FREE => SYNC -> WAITING => RELEASED -> DONE => FREE
99 *
100 * The interesing one above is ASYNC. You might ask, why not go directly
101 * to FREE, instead of DONE. If it did that, it might be possible to exhaust
102 * the master's xc_free list if a master can generate ASYNC messages faster
103 * then the slave can process them. That could be handled with more complicated
104 * handling. However since nothing important uses ASYNC, I've not bothered.
105 */
106 #define XC_MSG_FREE (0) /* msg in xc_free queue */
107 #define XC_MSG_ASYNC (1) /* msg in slave xc_msgbox */
108 #define XC_MSG_CALL (2) /* msg in slave xc_msgbox */
109 #define XC_MSG_SYNC (3) /* msg in slave xc_msgbox */
110 #define XC_MSG_WAITING (4) /* msg in master xc_msgbox or xc_waiters */
111 #define XC_MSG_RELEASED (5) /* msg in slave xc_msgbox */
112 #define XC_MSG_DONE (6) /* msg in master xc_msgbox */
113
114 /*
115 * We allow for one high priority message at a time to happen in the system.
116 * This is used for panic, kmdb, etc., so no locking is done.
117 */
118 static volatile cpuset_t xc_priority_set_store;
119 static volatile ulong_t *xc_priority_set = CPUSET2BV(xc_priority_set_store);
120 static xc_data_t xc_priority_data;
121
122 /*
123 * Wrappers to avoid C compiler warnings due to volatile. The atomic bit
124 * operations don't accept volatile bit vectors - which is a bit silly.
125 */
126 #define XC_BT_SET(vector, b) BT_ATOMIC_SET((ulong_t *)(vector), (b))
127 #define XC_BT_CLEAR(vector, b) BT_ATOMIC_CLEAR((ulong_t *)(vector), (b))
128
129 /*
130 * Decrement a CPU's work count
131 */
132 static void
xc_decrement(struct machcpu * mcpu)133 xc_decrement(struct machcpu *mcpu)
134 {
135 atomic_dec_32(&mcpu->xc_work_cnt);
136 }
137
138 /*
139 * Increment a CPU's work count and return the old value
140 */
141 static int
xc_increment(struct machcpu * mcpu)142 xc_increment(struct machcpu *mcpu)
143 {
144 int old;
145 do {
146 old = mcpu->xc_work_cnt;
147 } while (cas32((uint32_t *)&mcpu->xc_work_cnt, old, old + 1) != old);
148 return (old);
149 }
150
151 /*
152 * Put a message into a queue. The insertion is atomic no matter
153 * how many different inserts/extracts to the same queue happen.
154 */
155 static void
xc_insert(void * queue,xc_msg_t * msg)156 xc_insert(void *queue, xc_msg_t *msg)
157 {
158 xc_msg_t *old_head;
159
160 /*
161 * FREE messages should only ever be getting inserted into
162 * the xc_master CPUs xc_free queue.
163 */
164 ASSERT(msg->xc_command != XC_MSG_FREE ||
165 cpu[msg->xc_master] == NULL || /* possible only during init */
166 queue == &cpu[msg->xc_master]->cpu_m.xc_free);
167
168 do {
169 old_head = (xc_msg_t *)*(volatile xc_msg_t **)queue;
170 msg->xc_next = old_head;
171 } while (casptr(queue, old_head, msg) != old_head);
172 }
173
174 /*
175 * Extract a message from a queue. The extraction is atomic only
176 * when just one thread does extractions from the queue.
177 * If the queue is empty, NULL is returned.
178 */
179 static xc_msg_t *
xc_extract(xc_msg_t ** queue)180 xc_extract(xc_msg_t **queue)
181 {
182 xc_msg_t *old_head;
183
184 do {
185 old_head = (xc_msg_t *)*(volatile xc_msg_t **)queue;
186 if (old_head == NULL)
187 return (old_head);
188 } while (casptr(queue, old_head, old_head->xc_next) != old_head);
189 old_head->xc_next = NULL;
190 return (old_head);
191 }
192
193 /*
194 * Initialize the machcpu fields used for cross calls
195 */
196 static uint_t xc_initialized = 0;
197
198 void
xc_init_cpu(struct cpu * cpup)199 xc_init_cpu(struct cpu *cpup)
200 {
201 xc_msg_t *msg;
202 int c;
203
204 /*
205 * Allocate message buffers for the new CPU.
206 */
207 for (c = 0; c < max_ncpus; ++c) {
208 if (plat_dr_support_cpu()) {
209 /*
210 * Allocate a message buffer for every CPU possible
211 * in system, including our own, and add them to our xc
212 * message queue.
213 */
214 msg = kmem_zalloc(sizeof (*msg), KM_SLEEP);
215 msg->xc_command = XC_MSG_FREE;
216 msg->xc_master = cpup->cpu_id;
217 xc_insert(&cpup->cpu_m.xc_free, msg);
218 } else if (cpu[c] != NULL && cpu[c] != cpup) {
219 /*
220 * Add a new message buffer to each existing CPU's free
221 * list, as well as one for my list for each of them.
222 * Note: cpu0 is statically inserted into cpu[] array,
223 * so need to check cpu[c] isn't cpup itself to avoid
224 * allocating extra message buffers for cpu0.
225 */
226 msg = kmem_zalloc(sizeof (*msg), KM_SLEEP);
227 msg->xc_command = XC_MSG_FREE;
228 msg->xc_master = c;
229 xc_insert(&cpu[c]->cpu_m.xc_free, msg);
230
231 msg = kmem_zalloc(sizeof (*msg), KM_SLEEP);
232 msg->xc_command = XC_MSG_FREE;
233 msg->xc_master = cpup->cpu_id;
234 xc_insert(&cpup->cpu_m.xc_free, msg);
235 }
236 }
237
238 if (!plat_dr_support_cpu()) {
239 /*
240 * Add one for self messages if CPU hotplug is disabled.
241 */
242 msg = kmem_zalloc(sizeof (*msg), KM_SLEEP);
243 msg->xc_command = XC_MSG_FREE;
244 msg->xc_master = cpup->cpu_id;
245 xc_insert(&cpup->cpu_m.xc_free, msg);
246 }
247
248 if (!xc_initialized)
249 xc_initialized = 1;
250 }
251
252 void
xc_fini_cpu(struct cpu * cpup)253 xc_fini_cpu(struct cpu *cpup)
254 {
255 xc_msg_t *msg;
256
257 ASSERT((cpup->cpu_flags & CPU_READY) == 0);
258 ASSERT(cpup->cpu_m.xc_msgbox == NULL);
259 ASSERT(cpup->cpu_m.xc_work_cnt == 0);
260
261 while ((msg = xc_extract(&cpup->cpu_m.xc_free)) != NULL) {
262 kmem_free(msg, sizeof (*msg));
263 }
264 }
265
266 #define XC_FLUSH_MAX_WAITS 1000
267
268 /* Flush inflight message buffers. */
269 int
xc_flush_cpu(struct cpu * cpup)270 xc_flush_cpu(struct cpu *cpup)
271 {
272 int i;
273
274 ASSERT((cpup->cpu_flags & CPU_READY) == 0);
275
276 /*
277 * Pause all working CPUs, which ensures that there's no CPU in
278 * function xc_common().
279 * This is used to work around a race condition window in xc_common()
280 * between checking CPU_READY flag and increasing working item count.
281 */
282 pause_cpus(cpup);
283 start_cpus();
284
285 for (i = 0; i < XC_FLUSH_MAX_WAITS; i++) {
286 if (cpup->cpu_m.xc_work_cnt == 0) {
287 break;
288 }
289 DELAY(1);
290 }
291 for (; i < XC_FLUSH_MAX_WAITS; i++) {
292 if (!BT_TEST(xc_priority_set, cpup->cpu_id)) {
293 break;
294 }
295 DELAY(1);
296 }
297
298 return (i >= XC_FLUSH_MAX_WAITS ? ETIME : 0);
299 }
300
301 /*
302 * X-call message processing routine. Note that this is used by both
303 * senders and recipients of messages.
304 *
305 * We're protected against changing CPUs by either being in a high-priority
306 * interrupt, having preemption disabled or by having a raised SPL.
307 */
308 /*ARGSUSED*/
309 uint_t
xc_serv(caddr_t arg1,caddr_t arg2)310 xc_serv(caddr_t arg1, caddr_t arg2)
311 {
312 struct machcpu *mcpup = &(CPU->cpu_m);
313 xc_msg_t *msg;
314 xc_data_t *data;
315 xc_msg_t *xc_waiters = NULL;
316 uint32_t num_waiting = 0;
317 xc_func_t func;
318 xc_arg_t a1;
319 xc_arg_t a2;
320 xc_arg_t a3;
321 uint_t rc = DDI_INTR_UNCLAIMED;
322
323 while (mcpup->xc_work_cnt != 0) {
324 rc = DDI_INTR_CLAIMED;
325
326 /*
327 * We may have to wait for a message to arrive.
328 */
329 for (msg = NULL; msg == NULL;
330 msg = xc_extract(&mcpup->xc_msgbox)) {
331
332 /*
333 * Alway check for and handle a priority message.
334 */
335 if (BT_TEST(xc_priority_set, CPU->cpu_id)) {
336 func = xc_priority_data.xc_func;
337 a1 = xc_priority_data.xc_a1;
338 a2 = xc_priority_data.xc_a2;
339 a3 = xc_priority_data.xc_a3;
340 XC_BT_CLEAR(xc_priority_set, CPU->cpu_id);
341 xc_decrement(mcpup);
342 func(a1, a2, a3);
343 if (mcpup->xc_work_cnt == 0)
344 return (rc);
345 }
346
347 /*
348 * wait for a message to arrive
349 */
350 SMT_PAUSE();
351 }
352
353
354 /*
355 * process the message
356 */
357 switch (msg->xc_command) {
358
359 /*
360 * ASYNC gives back the message immediately, then we do the
361 * function and return with no more waiting.
362 */
363 case XC_MSG_ASYNC:
364 data = &cpu[msg->xc_master]->cpu_m.xc_data;
365 func = data->xc_func;
366 a1 = data->xc_a1;
367 a2 = data->xc_a2;
368 a3 = data->xc_a3;
369 msg->xc_command = XC_MSG_DONE;
370 xc_insert(&cpu[msg->xc_master]->cpu_m.xc_msgbox, msg);
371 if (func != NULL)
372 (void) (*func)(a1, a2, a3);
373 xc_decrement(mcpup);
374 break;
375
376 /*
377 * SYNC messages do the call, then send it back to the master
378 * in WAITING mode
379 */
380 case XC_MSG_SYNC:
381 data = &cpu[msg->xc_master]->cpu_m.xc_data;
382 if (data->xc_func != NULL)
383 (void) (*data->xc_func)(data->xc_a1,
384 data->xc_a2, data->xc_a3);
385 msg->xc_command = XC_MSG_WAITING;
386 xc_insert(&cpu[msg->xc_master]->cpu_m.xc_msgbox, msg);
387 break;
388
389 /*
390 * WAITING messsages are collected by the master until all
391 * have arrived. Once all arrive, we release them back to
392 * the slaves
393 */
394 case XC_MSG_WAITING:
395 xc_insert(&xc_waiters, msg);
396 if (++num_waiting < mcpup->xc_wait_cnt)
397 break;
398 while ((msg = xc_extract(&xc_waiters)) != NULL) {
399 msg->xc_command = XC_MSG_RELEASED;
400 xc_insert(&cpu[msg->xc_slave]->cpu_m.xc_msgbox,
401 msg);
402 --num_waiting;
403 }
404 if (num_waiting != 0)
405 panic("wrong number waiting");
406 mcpup->xc_wait_cnt = 0;
407 break;
408
409 /*
410 * CALL messages do the function and then, like RELEASE,
411 * send the message is back to master as DONE.
412 */
413 case XC_MSG_CALL:
414 data = &cpu[msg->xc_master]->cpu_m.xc_data;
415 if (data->xc_func != NULL)
416 (void) (*data->xc_func)(data->xc_a1,
417 data->xc_a2, data->xc_a3);
418 /*FALLTHROUGH*/
419 case XC_MSG_RELEASED:
420 msg->xc_command = XC_MSG_DONE;
421 xc_insert(&cpu[msg->xc_master]->cpu_m.xc_msgbox, msg);
422 xc_decrement(mcpup);
423 break;
424
425 /*
426 * DONE means a slave has completely finished up.
427 * Once we collect all the DONE messages, we'll exit
428 * processing too.
429 */
430 case XC_MSG_DONE:
431 msg->xc_command = XC_MSG_FREE;
432 xc_insert(&mcpup->xc_free, msg);
433 xc_decrement(mcpup);
434 break;
435
436 case XC_MSG_FREE:
437 panic("free message 0x%p in msgbox", (void *)msg);
438 break;
439
440 default:
441 panic("bad message 0x%p in msgbox", (void *)msg);
442 break;
443 }
444 }
445 return (rc);
446 }
447
448 /*
449 * Initiate cross call processing.
450 */
451 static void
xc_common(xc_func_t func,xc_arg_t arg1,xc_arg_t arg2,xc_arg_t arg3,ulong_t * set,uint_t command)452 xc_common(
453 xc_func_t func,
454 xc_arg_t arg1,
455 xc_arg_t arg2,
456 xc_arg_t arg3,
457 ulong_t *set,
458 uint_t command)
459 {
460 int c;
461 struct cpu *cpup;
462 xc_msg_t *msg;
463 xc_data_t *data;
464 int cnt;
465 int save_spl;
466
467 if (!xc_initialized) {
468 if (BT_TEST(set, CPU->cpu_id) && (CPU->cpu_flags & CPU_READY) &&
469 func != NULL)
470 (void) (*func)(arg1, arg2, arg3);
471 return;
472 }
473
474 save_spl = splr(ipltospl(XC_HI_PIL));
475
476 /*
477 * fill in cross call data
478 */
479 data = &CPU->cpu_m.xc_data;
480 data->xc_func = func;
481 data->xc_a1 = arg1;
482 data->xc_a2 = arg2;
483 data->xc_a3 = arg3;
484
485 /*
486 * Post messages to all CPUs involved that are CPU_READY
487 */
488 CPU->cpu_m.xc_wait_cnt = 0;
489 for (c = 0; c < max_ncpus; ++c) {
490 if (!BT_TEST(set, c))
491 continue;
492 cpup = cpu[c];
493 if (cpup == NULL || !(cpup->cpu_flags & CPU_READY))
494 continue;
495
496 /*
497 * Fill out a new message.
498 */
499 msg = xc_extract(&CPU->cpu_m.xc_free);
500 if (msg == NULL)
501 panic("Ran out of free xc_msg_t's");
502 msg->xc_command = command;
503 if (msg->xc_master != CPU->cpu_id)
504 panic("msg %p has wrong xc_master", (void *)msg);
505 msg->xc_slave = c;
506
507 /*
508 * Increment my work count for all messages that I'll
509 * transition from DONE to FREE.
510 * Also remember how many XC_MSG_WAITINGs to look for
511 */
512 (void) xc_increment(&CPU->cpu_m);
513 if (command == XC_MSG_SYNC)
514 ++CPU->cpu_m.xc_wait_cnt;
515
516 /*
517 * Increment the target CPU work count then insert the message
518 * in the target msgbox. If I post the first bit of work
519 * for the target to do, send an IPI to the target CPU.
520 */
521 cnt = xc_increment(&cpup->cpu_m);
522 xc_insert(&cpup->cpu_m.xc_msgbox, msg);
523 if (cpup != CPU) {
524 if (cnt == 0) {
525 CPU_STATS_ADDQ(CPU, sys, xcalls, 1);
526 send_dirint(c, XC_HI_PIL);
527 if (xc_collect_enable)
528 ++xc_total_cnt;
529 } else if (xc_collect_enable) {
530 ++xc_multi_cnt;
531 }
532 }
533 }
534
535 /*
536 * Now drop into the message handler until all work is done
537 */
538 (void) xc_serv(NULL, NULL);
539 splx(save_spl);
540 }
541
542 /*
543 * Push out a priority cross call.
544 */
545 static void
xc_priority_common(xc_func_t func,xc_arg_t arg1,xc_arg_t arg2,xc_arg_t arg3,ulong_t * set)546 xc_priority_common(
547 xc_func_t func,
548 xc_arg_t arg1,
549 xc_arg_t arg2,
550 xc_arg_t arg3,
551 ulong_t *set)
552 {
553 int i;
554 int c;
555 struct cpu *cpup;
556
557 /*
558 * Wait briefly for any previous xc_priority to have finished.
559 */
560 for (c = 0; c < max_ncpus; ++c) {
561 cpup = cpu[c];
562 if (cpup == NULL || !(cpup->cpu_flags & CPU_READY))
563 continue;
564
565 /*
566 * The value of 40000 here is from old kernel code. It
567 * really should be changed to some time based value, since
568 * under a hypervisor, there's no guarantee a remote CPU
569 * is even scheduled.
570 */
571 for (i = 0; BT_TEST(xc_priority_set, c) && i < 40000; ++i)
572 SMT_PAUSE();
573
574 /*
575 * Some CPU did not respond to a previous priority request. It's
576 * probably deadlocked with interrupts blocked or some such
577 * problem. We'll just erase the previous request - which was
578 * most likely a kmdb_enter that has already expired - and plow
579 * ahead.
580 */
581 if (BT_TEST(xc_priority_set, c)) {
582 XC_BT_CLEAR(xc_priority_set, c);
583 if (cpup->cpu_m.xc_work_cnt > 0)
584 xc_decrement(&cpup->cpu_m);
585 }
586 }
587
588 /*
589 * fill in cross call data
590 */
591 xc_priority_data.xc_func = func;
592 xc_priority_data.xc_a1 = arg1;
593 xc_priority_data.xc_a2 = arg2;
594 xc_priority_data.xc_a3 = arg3;
595
596 /*
597 * Post messages to all CPUs involved that are CPU_READY
598 * We'll always IPI, plus bang on the xc_msgbox for i86_mwait()
599 */
600 for (c = 0; c < max_ncpus; ++c) {
601 if (!BT_TEST(set, c))
602 continue;
603 cpup = cpu[c];
604 if (cpup == NULL || !(cpup->cpu_flags & CPU_READY) ||
605 cpup == CPU)
606 continue;
607 (void) xc_increment(&cpup->cpu_m);
608 XC_BT_SET(xc_priority_set, c);
609 send_dirint(c, XC_HI_PIL);
610 for (i = 0; i < 10; ++i) {
611 (void) casptr(&cpup->cpu_m.xc_msgbox,
612 cpup->cpu_m.xc_msgbox, cpup->cpu_m.xc_msgbox);
613 }
614 }
615 }
616
617 /*
618 * Do cross call to all other CPUs with absolutely no waiting or handshaking.
619 * This should only be used for extraordinary operations, like panic(), which
620 * need to work, in some fashion, in a not completely functional system.
621 * All other uses that want minimal waiting should use xc_call_nowait().
622 */
623 void
xc_priority(xc_arg_t arg1,xc_arg_t arg2,xc_arg_t arg3,ulong_t * set,xc_func_t func)624 xc_priority(
625 xc_arg_t arg1,
626 xc_arg_t arg2,
627 xc_arg_t arg3,
628 ulong_t *set,
629 xc_func_t func)
630 {
631 extern int IGNORE_KERNEL_PREEMPTION;
632 int save_spl = splr(ipltospl(XC_HI_PIL));
633 int save_kernel_preemption = IGNORE_KERNEL_PREEMPTION;
634
635 IGNORE_KERNEL_PREEMPTION = 1;
636 xc_priority_common((xc_func_t)func, arg1, arg2, arg3, set);
637 IGNORE_KERNEL_PREEMPTION = save_kernel_preemption;
638 splx(save_spl);
639 }
640
641 /*
642 * Wrapper for kmdb to capture other CPUs, causing them to enter the debugger.
643 */
644 void
kdi_xc_others(int this_cpu,void (* func)(void))645 kdi_xc_others(int this_cpu, void (*func)(void))
646 {
647 extern int IGNORE_KERNEL_PREEMPTION;
648 int save_kernel_preemption;
649 cpuset_t set;
650
651 if (!xc_initialized)
652 return;
653
654 save_kernel_preemption = IGNORE_KERNEL_PREEMPTION;
655 IGNORE_KERNEL_PREEMPTION = 1;
656 CPUSET_ALL_BUT(set, this_cpu);
657 xc_priority_common((xc_func_t)func, 0, 0, 0, CPUSET2BV(set));
658 IGNORE_KERNEL_PREEMPTION = save_kernel_preemption;
659 }
660
661
662
663 /*
664 * Invoke function on specified processors. Remotes may continue after
665 * service with no waiting. xc_call_nowait() may return immediately too.
666 */
667 void
xc_call_nowait(xc_arg_t arg1,xc_arg_t arg2,xc_arg_t arg3,ulong_t * set,xc_func_t func)668 xc_call_nowait(
669 xc_arg_t arg1,
670 xc_arg_t arg2,
671 xc_arg_t arg3,
672 ulong_t *set,
673 xc_func_t func)
674 {
675 xc_common(func, arg1, arg2, arg3, set, XC_MSG_ASYNC);
676 }
677
678 /*
679 * Invoke function on specified processors. Remotes may continue after
680 * service with no waiting. xc_call() returns only after remotes have finished.
681 */
682 void
xc_call(xc_arg_t arg1,xc_arg_t arg2,xc_arg_t arg3,ulong_t * set,xc_func_t func)683 xc_call(
684 xc_arg_t arg1,
685 xc_arg_t arg2,
686 xc_arg_t arg3,
687 ulong_t *set,
688 xc_func_t func)
689 {
690 xc_common(func, arg1, arg2, arg3, set, XC_MSG_CALL);
691 }
692
693 /*
694 * Invoke function on specified processors. Remotes wait until all have
695 * finished. xc_sync() also waits until all remotes have finished.
696 */
697 void
xc_sync(xc_arg_t arg1,xc_arg_t arg2,xc_arg_t arg3,ulong_t * set,xc_func_t func)698 xc_sync(
699 xc_arg_t arg1,
700 xc_arg_t arg2,
701 xc_arg_t arg3,
702 ulong_t *set,
703 xc_func_t func)
704 {
705 xc_common(func, arg1, arg2, arg3, set, XC_MSG_SYNC);
706 }
707