mp_machdep.c revision 74733
1/*- 2 * Copyright (c) 2000 Doug Rabson 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 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: head/sys/ia64/ia64/mp_machdep.c 74733 2001-03-24 06:22:57Z jhb $ 27 */ 28 29#include <sys/param.h> 30#include <sys/systm.h> 31#include <sys/ktr.h> 32#include <sys/proc.h> 33#include <sys/lock.h> 34#include <sys/malloc.h> 35#include <sys/mutex.h> 36#include <sys/kernel.h> 37#include <sys/sysctl.h> 38 39#include <vm/vm.h> 40#include <vm/pmap.h> 41#include <vm/vm_map.h> 42#include <sys/user.h> 43#include <sys/dkstat.h> 44 45#include <machine/smp.h> 46#include <machine/atomic.h> 47#include <machine/ipl.h> 48#include <machine/globaldata.h> 49#include <machine/pmap.h> 50#include <machine/clock.h> 51 52#define CHECKSTATE_USER 0 53#define CHECKSTATE_SYS 1 54#define CHECKSTATE_INTR 2 55 56volatile u_int stopped_cpus; 57volatile u_int started_cpus; 58volatile u_int checkstate_probed_cpus; 59volatile u_int checkstate_need_ast; 60volatile u_int checkstate_pending_ast; 61struct proc* checkstate_curproc[MAXCPU]; 62int checkstate_cpustate[MAXCPU]; 63u_long checkstate_pc[MAXCPU]; 64volatile u_int resched_cpus; 65void (*cpustop_restartfunc) __P((void)); 66int mp_ncpus; 67 68int smp_started; 69int boot_cpu_id; 70u_int32_t all_cpus; 71 72static struct globaldata *cpuid_to_globaldata[MAXCPU]; 73 74int smp_active = 0; /* are the APs allowed to run? */ 75SYSCTL_INT(_machdep, OID_AUTO, smp_active, CTLFLAG_RW, &smp_active, 0, ""); 76 77/* Is forwarding of a interrupt to the CPU holding the ISR lock enabled ? */ 78int forward_irq_enabled = 1; 79SYSCTL_INT(_machdep, OID_AUTO, forward_irq_enabled, CTLFLAG_RW, 80 &forward_irq_enabled, 0, ""); 81 82/* Enable forwarding of a signal to a process running on a different CPU */ 83static int forward_signal_enabled = 1; 84SYSCTL_INT(_machdep, OID_AUTO, forward_signal_enabled, CTLFLAG_RW, 85 &forward_signal_enabled, 0, ""); 86 87/* Enable forwarding of roundrobin to all other cpus */ 88static int forward_roundrobin_enabled = 1; 89SYSCTL_INT(_machdep, OID_AUTO, forward_roundrobin_enabled, CTLFLAG_RW, 90 &forward_roundrobin_enabled, 0, ""); 91 92/* 93 * Initialise a struct globaldata. 94 */ 95void 96globaldata_init(struct globaldata *globaldata, int cpuid, size_t sz) 97{ 98 bzero(globaldata, sz); 99 globaldata->gd_cpuid = cpuid; 100 globaldata->gd_other_cpus = all_cpus & ~(1 << cpuid); 101 cpuid_to_globaldata[cpuid] = globaldata; 102} 103 104struct globaldata * 105globaldata_find(int cpuid) 106{ 107 return cpuid_to_globaldata[cpuid]; 108} 109 110/* Other stuff */ 111 112/* lock around the MP rendezvous */ 113static struct mtx smp_rv_mtx; 114 115static void 116init_locks(void) 117{ 118 119 mtx_init(&smp_rv_mtx, "smp_rendezvous", MTX_SPIN); 120} 121 122void 123mp_start() 124{ 125 init_locks(); 126} 127 128void 129mp_announce() 130{ 131} 132 133void 134smp_invltlb() 135{ 136} 137 138#define GD_TO_INDEX(pc, prof) \ 139 ((int)(((u_quad_t)((pc) - (prof)->pr_off) * \ 140 (u_quad_t)((prof)->pr_scale)) >> 16) & ~1) 141 142static void 143addupc_intr_forwarded(struct proc *p, int id, int *astmap) 144{ 145 int i; 146 struct uprof *prof; 147 u_long pc; 148 149 pc = checkstate_pc[id]; 150 prof = &p->p_stats->p_prof; 151 if (pc >= prof->pr_off && 152 (i = GD_TO_INDEX(pc, prof)) < prof->pr_size) { 153 if ((p->p_sflag & PS_OWEUPC) == 0) { 154 prof->pr_addr = pc; 155 prof->pr_ticks = 1; 156 p->p_sflag |= PS_OWEUPC; 157 } 158 *astmap |= (1 << id); 159 } 160} 161 162static void 163forwarded_statclock(int id, int pscnt, int *astmap) 164{ 165 struct pstats *pstats; 166 long rss; 167 struct rusage *ru; 168 struct vmspace *vm; 169 int cpustate; 170 struct proc *p; 171#ifdef GPROF 172 register struct gmonparam *g; 173 int i; 174#endif 175 176 p = checkstate_curproc[id]; 177 cpustate = checkstate_cpustate[id]; 178 179 /* XXX */ 180 if (p->p_ithd) 181 cpustate = CHECKSTATE_INTR; 182 else if (p == cpuid_to_globaldata[id]->gd_idleproc) 183 cpustate = CHECKSTATE_SYS; 184 185 switch (cpustate) { 186 case CHECKSTATE_USER: 187 if (p->p_sflag & PS_PROFIL) 188 addupc_intr_forwarded(p, id, astmap); 189 if (pscnt > 1) 190 return; 191 p->p_uticks++; 192 if (p->p_nice > NZERO) 193 cp_time[CP_NICE]++; 194 else 195 cp_time[CP_USER]++; 196 break; 197 case CHECKSTATE_SYS: 198#ifdef GPROF 199 /* 200 * Kernel statistics are just like addupc_intr, only easier. 201 */ 202 g = &_gmonparam; 203 if (g->state == GMON_PROF_ON) { 204 i = checkstate_pc[id] - g->lowpc; 205 if (i < g->textsize) { 206 i /= HISTFRACTION * sizeof(*g->kcount); 207 g->kcount[i]++; 208 } 209 } 210#endif 211 if (pscnt > 1) 212 return; 213 214 if (p == cpuid_to_globaldata[id]->gd_idleproc) 215 cp_time[CP_IDLE]++; 216 else { 217 p->p_sticks++; 218 cp_time[CP_SYS]++; 219 } 220 break; 221 case CHECKSTATE_INTR: 222 default: 223#ifdef GPROF 224 /* 225 * Kernel statistics are just like addupc_intr, only easier. 226 */ 227 g = &_gmonparam; 228 if (g->state == GMON_PROF_ON) { 229 i = checkstate_pc[id] - g->lowpc; 230 if (i < g->textsize) { 231 i /= HISTFRACTION * sizeof(*g->kcount); 232 g->kcount[i]++; 233 } 234 } 235#endif 236 if (pscnt > 1) 237 return; 238 if (p) 239 p->p_iticks++; 240 cp_time[CP_INTR]++; 241 } 242 243 schedclock(p); 244 245 /* Update resource usage integrals and maximums. */ 246 if ((pstats = p->p_stats) != NULL && 247 (ru = &pstats->p_ru) != NULL && 248 (vm = p->p_vmspace) != NULL) { 249 ru->ru_ixrss += pgtok(vm->vm_tsize); 250 ru->ru_idrss += pgtok(vm->vm_dsize); 251 ru->ru_isrss += pgtok(vm->vm_ssize); 252 rss = pgtok(vmspace_resident_count(vm)); 253 if (ru->ru_maxrss < rss) 254 ru->ru_maxrss = rss; 255 } 256} 257 258#define BETTER_CLOCK_DIAGNOSTIC 259 260void 261forward_statclock(int pscnt) 262{ 263 int map; 264 int id; 265 int i; 266 267 /* Kludge. We don't yet have separate locks for the interrupts 268 * and the kernel. This means that we cannot let the other processors 269 * handle complex interrupts while inhibiting them from entering 270 * the kernel in a non-interrupt context. 271 * 272 * What we can do, without changing the locking mechanisms yet, 273 * is letting the other processors handle a very simple interrupt 274 * (wich determines the processor states), and do the main 275 * work ourself. 276 */ 277 278 CTR1(KTR_SMP, "forward_statclock(%d)", pscnt); 279 280 if (!smp_started || cold || panicstr) 281 return; 282 283 /* Step 1: Probe state (user, cpu, interrupt, spinlock, idle ) */ 284 285 map = PCPU_GET(other_cpus) & ~stopped_cpus ; 286 checkstate_probed_cpus = 0; 287 if (map != 0) 288 smp_ipi_selected(map, IPI_CHECKSTATE); 289 290 i = 0; 291 while (checkstate_probed_cpus != map) { 292 /* spin */ 293 i++; 294 if (i == 100000) { 295#ifdef BETTER_CLOCK_DIAGNOSTIC 296 printf("forward_statclock: checkstate %x\n", 297 checkstate_probed_cpus); 298#endif 299 break; 300 } 301 } 302 303 /* 304 * Step 2: walk through other processors processes, update ticks and 305 * profiling info. 306 */ 307 308 map = 0; 309 for (id = 0; id < mp_ncpus; id++) { 310 if (id == PCPU_GET(cpuid)) 311 continue; 312 if (((1 << id) & checkstate_probed_cpus) == 0) 313 continue; 314 forwarded_statclock(id, pscnt, &map); 315 } 316 if (map != 0) { 317 checkstate_need_ast |= map; 318 smp_ipi_selected(map, IPI_AST); 319 i = 0; 320 while ((checkstate_need_ast & map) != 0) { 321 /* spin */ 322 i++; 323 if (i > 100000) { 324#ifdef BETTER_CLOCK_DIAGNOSTIC 325 printf("forward_statclock: dropped ast 0x%x\n", 326 checkstate_need_ast & map); 327#endif 328 break; 329 } 330 } 331 } 332} 333 334void 335forward_hardclock(int pscnt) 336{ 337 int map; 338 int id; 339 struct proc *p; 340 struct pstats *pstats; 341 int i; 342 343 /* Kludge. We don't yet have separate locks for the interrupts 344 * and the kernel. This means that we cannot let the other processors 345 * handle complex interrupts while inhibiting them from entering 346 * the kernel in a non-interrupt context. 347 * 348 * What we can do, without changing the locking mechanisms yet, 349 * is letting the other processors handle a very simple interrupt 350 * (wich determines the processor states), and do the main 351 * work ourself. 352 */ 353 354 CTR1(KTR_SMP, "forward_hardclock(%d)", pscnt); 355 356 if (!smp_started || cold || panicstr) 357 return; 358 359 /* Step 1: Probe state (user, cpu, interrupt, spinlock, idle) */ 360 361 map = PCPU_GET(other_cpus) & ~stopped_cpus ; 362 checkstate_probed_cpus = 0; 363 if (map != 0) 364 smp_ipi_selected(map, IPI_CHECKSTATE); 365 366 i = 0; 367 while (checkstate_probed_cpus != map) { 368 /* spin */ 369 i++; 370 if (i == 100000) { 371#ifdef BETTER_CLOCK_DIAGNOSTIC 372 printf("forward_hardclock: checkstate %x\n", 373 checkstate_probed_cpus); 374#endif 375 breakpoint(); 376 break; 377 } 378 } 379 380 /* 381 * Step 2: walk through other processors processes, update virtual 382 * timer and profiling timer. If stathz == 0, also update ticks and 383 * profiling info. 384 */ 385 386 map = 0; 387 for (id = 0; id < mp_ncpus; id++) { 388 if (id == PCPU_GET(cpuid)) 389 continue; 390 if (((1 << id) & checkstate_probed_cpus) == 0) 391 continue; 392 p = checkstate_curproc[id]; 393 if (p) { 394 pstats = p->p_stats; 395 if (checkstate_cpustate[id] == CHECKSTATE_USER && 396 timevalisset(&pstats->p_timer[ITIMER_VIRTUAL].it_value) && 397 itimerdecr(&pstats->p_timer[ITIMER_VIRTUAL], tick) == 0) { 398 p->p_sflag |= PS_ALRMPEND; 399 map |= (1 << id); 400 } 401 if (timevalisset(&pstats->p_timer[ITIMER_PROF].it_value) && 402 itimerdecr(&pstats->p_timer[ITIMER_PROF], tick) == 0) { 403 p->p_sflag |= PS_PROFPEND; 404 map |= (1 << id); 405 } 406 } 407 if (stathz == 0) { 408 forwarded_statclock( id, pscnt, &map); 409 } 410 } 411 if (map != 0) { 412 checkstate_need_ast |= map; 413 smp_ipi_selected(map, IPI_AST); 414 i = 0; 415 while ((checkstate_need_ast & map) != 0) { 416 /* spin */ 417 i++; 418 if (i > 100000) { 419#ifdef BETTER_CLOCK_DIAGNOSTIC 420 printf("forward_hardclock: dropped ast 0x%x\n", 421 checkstate_need_ast & map); 422#endif 423 break; 424 } 425 } 426 } 427} 428 429void 430forward_signal(struct proc *p) 431{ 432 int map; 433 int id; 434 int i; 435 436 /* Kludge. We don't yet have separate locks for the interrupts 437 * and the kernel. This means that we cannot let the other processors 438 * handle complex interrupts while inhibiting them from entering 439 * the kernel in a non-interrupt context. 440 * 441 * What we can do, without changing the locking mechanisms yet, 442 * is letting the other processors handle a very simple interrupt 443 * (wich determines the processor states), and do the main 444 * work ourself. 445 */ 446 447 CTR1(KTR_SMP, "forward_signal(%p)", p); 448 449 if (!smp_started || cold || panicstr) 450 return; 451 if (!forward_signal_enabled) 452 return; 453 while (1) { 454 if (p->p_stat != SRUN) 455 return; 456 id = p->p_oncpu; 457 if (id == 0xff) 458 return; 459 map = (1<<id); 460 checkstate_need_ast |= map; 461 smp_ipi_selected(map, IPI_AST); 462 i = 0; 463 while ((checkstate_need_ast & map) != 0) { 464 /* spin */ 465 i++; 466 if (i > 100000) { 467#if 0 468 printf("forward_signal: dropped ast 0x%x\n", 469 checkstate_need_ast & map); 470#endif 471 break; 472 } 473 } 474 if (id == p->p_oncpu) 475 return; 476 } 477} 478 479void 480forward_roundrobin(void) 481{ 482 u_int map; 483 int i; 484 485 CTR0(KTR_SMP, "forward_roundrobin()"); 486 487 if (!smp_started || cold || panicstr) 488 return; 489 if (!forward_roundrobin_enabled) 490 return; 491 resched_cpus |= PCPU_GET(other_cpus); 492 map = PCPU_GET(other_cpus) & ~stopped_cpus ; 493 smp_ipi_selected(map, IPI_AST); 494 i = 0; 495 while ((checkstate_need_ast & map) != 0) { 496 /* spin */ 497 i++; 498 if (i > 100000) { 499#if 0 500 printf("forward_roundrobin: dropped ast 0x%x\n", 501 checkstate_need_ast & map); 502#endif 503 break; 504 } 505 } 506} 507 508/* 509 * When called the executing CPU will send an IPI to all other CPUs 510 * requesting that they halt execution. 511 * 512 * Usually (but not necessarily) called with 'other_cpus' as its arg. 513 * 514 * - Signals all CPUs in map to stop. 515 * - Waits for each to stop. 516 * 517 * Returns: 518 * -1: error 519 * 0: NA 520 * 1: ok 521 * 522 * XXX FIXME: this is not MP-safe, needs a lock to prevent multiple CPUs 523 * from executing at same time. 524 */ 525int 526stop_cpus(u_int map) 527{ 528 int i; 529 530 if (!smp_started) 531 return 0; 532 533 CTR1(KTR_SMP, "stop_cpus(%x)", map); 534 535 /* send the stop IPI to all CPUs in map */ 536 smp_ipi_selected(map, IPI_STOP); 537 538 i = 0; 539 while ((stopped_cpus & map) != map) { 540 /* spin */ 541 i++; 542 if (i == 100000) { 543 printf("timeout stopping cpus\n"); 544 break; 545 } 546 ia64_mf(); 547 } 548 549 printf("stopped_cpus=%x\n", stopped_cpus); 550 551 return 1; 552} 553 554 555/* 556 * Called by a CPU to restart stopped CPUs. 557 * 558 * Usually (but not necessarily) called with 'stopped_cpus' as its arg. 559 * 560 * - Signals all CPUs in map to restart. 561 * - Waits for each to restart. 562 * 563 * Returns: 564 * -1: error 565 * 0: NA 566 * 1: ok 567 */ 568int 569restart_cpus(u_int map) 570{ 571 if (!smp_started) 572 return 0; 573 574 CTR1(KTR_SMP, "restart_cpus(%x)", map); 575 576 started_cpus = map; /* signal other cpus to restart */ 577 ia64_mf(); 578 579 while ((stopped_cpus & map) != 0) /* wait for each to clear its bit */ 580 ia64_mf(); 581 582 return 1; 583} 584 585/* 586 * All-CPU rendezvous. CPUs are signalled, all execute the setup function 587 * (if specified), rendezvous, execute the action function (if specified), 588 * rendezvous again, execute the teardown function (if specified), and then 589 * resume. 590 * 591 * Note that the supplied external functions _must_ be reentrant and aware 592 * that they are running in parallel and in an unknown lock context. 593 */ 594static void (*smp_rv_setup_func)(void *arg); 595static void (*smp_rv_action_func)(void *arg); 596static void (*smp_rv_teardown_func)(void *arg); 597static void *smp_rv_func_arg; 598static volatile int smp_rv_waiters[2]; 599 600void 601smp_rendezvous_action(void) 602{ 603 /* setup function */ 604 if (smp_rv_setup_func != NULL) 605 smp_rv_setup_func(smp_rv_func_arg); 606 /* spin on entry rendezvous */ 607 atomic_add_int(&smp_rv_waiters[0], 1); 608 while (smp_rv_waiters[0] < mp_ncpus) 609 ; 610 /* action function */ 611 if (smp_rv_action_func != NULL) 612 smp_rv_action_func(smp_rv_func_arg); 613 /* spin on exit rendezvous */ 614 atomic_add_int(&smp_rv_waiters[1], 1); 615 while (smp_rv_waiters[1] < mp_ncpus) 616 ; 617 /* teardown function */ 618 if (smp_rv_teardown_func != NULL) 619 smp_rv_teardown_func(smp_rv_func_arg); 620} 621 622void 623smp_rendezvous(void (* setup_func)(void *), 624 void (* action_func)(void *), 625 void (* teardown_func)(void *), 626 void *arg) 627{ 628 629 /* obtain rendezvous lock */ 630 mtx_lock_spin(&smp_rv_mtx); 631 632 /* set static function pointers */ 633 smp_rv_setup_func = setup_func; 634 smp_rv_action_func = action_func; 635 smp_rv_teardown_func = teardown_func; 636 smp_rv_func_arg = arg; 637 smp_rv_waiters[0] = 0; 638 smp_rv_waiters[1] = 0; 639 640 /* signal other processors, which will enter the IPI with interrupts off */ 641 smp_ipi_all_but_self(IPI_RENDEZVOUS); 642 643 /* call executor function */ 644 smp_rendezvous_action(); 645 646 /* release lock */ 647 mtx_unlock_spin(&smp_rv_mtx); 648} 649 650/* 651 * send an IPI to a set of cpus. 652 */ 653void 654smp_ipi_selected(u_int32_t cpus, u_int64_t ipi) 655{ 656 struct globaldata *globaldata; 657 658 CTR2(KTR_SMP, "smp_ipi_selected: cpus: %x ipi: %lx", cpus, ipi); 659 ia64_mf(); 660 while (cpus) { 661 int cpuid = ffs(cpus) - 1; 662 cpus &= ~(1 << cpuid); 663 664 globaldata = cpuid_to_globaldata[cpuid]; 665 if (globaldata) { 666 atomic_set_64(&globaldata->gd_pending_ipis, ipi); 667 ia64_mf(); 668#if 0 669 CTR1(KTR_SMP, "calling alpha_pal_wripir(%d)", cpuid); 670 alpha_pal_wripir(cpuid); 671#endif 672 } 673 } 674} 675 676/* 677 * send an IPI INTerrupt containing 'vector' to all CPUs, including myself 678 */ 679void 680smp_ipi_all(u_int64_t ipi) 681{ 682 smp_ipi_selected(all_cpus, ipi); 683} 684 685/* 686 * send an IPI to all CPUs EXCEPT myself 687 */ 688void 689smp_ipi_all_but_self(u_int64_t ipi) 690{ 691 smp_ipi_selected(PCPU_GET(other_cpus), ipi); 692} 693 694/* 695 * send an IPI to myself 696 */ 697void 698smp_ipi_self(u_int64_t ipi) 699{ 700 smp_ipi_selected(1 << PCPU_GET(cpuid), ipi); 701} 702 703/* 704 * Handle an IPI sent to this processor. 705 */ 706void 707smp_handle_ipi(struct trapframe *frame) 708{ 709 u_int64_t ipis; 710 u_int64_t ipi; 711 int cpuid = PCPU_GET(cpuid); 712 713 do { 714 ipis = PCPU_GET(pending_ipis); 715 } while (atomic_cmpset_64(PCPU_PTR(pending_ipis), ipis, 0)); 716 717 CTR1(KTR_SMP, "smp_handle_ipi(), ipis=%lx", ipis); 718 while (ipis) { 719 /* 720 * Find the lowest set bit. 721 */ 722 ipi = ipis & ~(ipis - 1); 723 switch (ipi) { 724 case IPI_INVLTLB: 725 break; 726 727 case IPI_RENDEZVOUS: 728 CTR0(KTR_SMP, "IPI_RENDEZVOUS"); 729 smp_rendezvous_action(); 730 break; 731 732 case IPI_AST: 733 CTR0(KTR_SMP, "IPI_AST"); 734 atomic_clear_int(&checkstate_need_ast, 1<<cpuid); 735 atomic_set_int(&checkstate_pending_ast, 1<<cpuid); 736 if ((frame->tf_cr_ipsr & IA64_PSR_CPL) 737 == IA64_PSR_CPL_USER) 738 ast(frame); /* XXX */ 739 break; 740 741 case IPI_CHECKSTATE: 742 CTR0(KTR_SMP, "IPI_CHECKSTATE"); 743 if ((frame->tf_cr_ipsr & IA64_PSR_CPL) 744 == IA64_PSR_CPL_USER) 745 checkstate_cpustate[cpuid] = CHECKSTATE_USER; 746 else if (curproc->p_intr_nesting_level == 1) 747 checkstate_cpustate[cpuid] = CHECKSTATE_SYS; 748 else 749 checkstate_cpustate[cpuid] = CHECKSTATE_INTR; 750 checkstate_curproc[cpuid] = PCPU_GET(curproc); 751 atomic_set_int(&checkstate_probed_cpus, 1<<cpuid); 752 break; 753 754 case IPI_STOP: 755 CTR0(KTR_SMP, "IPI_STOP"); 756 atomic_set_int(&stopped_cpus, 1<<cpuid); 757 while ((started_cpus & (1<<cpuid)) == 0) 758 ia64_mf(); 759 atomic_clear_int(&started_cpus, 1<<cpuid); 760 atomic_clear_int(&stopped_cpus, 1<<cpuid); 761 break; 762 } 763 } 764} 765