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