patch-2.3.43 linux/arch/ia64/kernel/smp.c

Next file: linux/arch/ia64/kernel/sys_ia64.c
Previous file: linux/arch/ia64/kernel/signal.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.42/linux/arch/ia64/kernel/smp.c linux/arch/ia64/kernel/smp.c
@@ -0,0 +1,777 @@
+/*
+ * SMP Support
+ *
+ * Copyright (C) 1999 Walt Drummond <drummond@valinux.com>
+ * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com>
+ * 
+ * Lots of stuff stolen from arch/alpha/kernel/smp.c
+ *
+ *  99/10/05 davidm	Update to bring it in sync with new command-line processing scheme.
+ */
+#define __KERNEL_SYSCALLS__
+
+#include <linux/config.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/smp.h>
+#include <linux/kernel_stat.h>
+#include <linux/mm.h>
+
+#include <asm/atomic.h>
+#include <asm/bitops.h>
+#include <asm/current.h>
+#include <asm/delay.h>
+
+#ifdef CONFIG_KDB
+#include <linux/kdb.h>
+void smp_kdb_interrupt (struct pt_regs* regs);
+void kdb_global(int cpuid);
+extern unsigned long smp_kdb_wait;
+extern int kdb_new_cpu;
+#endif
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/processor.h>
+#include <asm/ptrace.h>
+#include <asm/sal.h>
+#include <asm/system.h>
+#include <asm/unistd.h>
+
+extern int cpu_idle(void * unused);
+extern void _start(void);
+
+extern int cpu_now_booting;                          /* Used by head.S to find idle task */
+extern unsigned long cpu_initialized;                /* Bitmap of available cpu's */
+extern struct cpuinfo_ia64 cpu_data[NR_CPUS];        /* Duh... */
+
+spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED;
+
+#ifdef CONFIG_KDB
+unsigned long cpu_online_map = 1;
+#endif
+
+volatile int cpu_number_map[NR_CPUS] = { -1, };      /* SAPIC ID -> Logical ID */
+volatile int __cpu_logical_map[NR_CPUS] = { -1, };   /* logical ID -> SAPIC ID */
+int smp_num_cpus = 1;		
+int bootstrap_processor = -1;                        /* SAPIC ID of BSP */
+int smp_threads_ready = 0;	                     /* Set when the idlers are all forked */
+unsigned long ipi_base_addr = IPI_DEFAULT_BASE_ADDR; /* Base addr of IPI table */
+cycles_t cacheflush_time = 0;
+unsigned long ap_wakeup_vector = -1;                 /* External Int to use to wakeup AP's */
+static int max_cpus = -1;	                     /* Command line */
+static unsigned long ipi_op[NR_CPUS];
+struct smp_call_struct {
+	void (*func) (void *info);
+	void *info;
+	long wait;
+	atomic_t unstarted_count;
+	atomic_t unfinished_count;
+};
+static struct smp_call_struct *smp_call_function_data;
+
+#ifdef CONFIG_KDB
+unsigned long smp_kdb_wait = 0;                         /* Bitmask of waiters */
+#endif
+
+#ifdef	CONFIG_ITANIUM_ASTEP_SPECIFIC
+extern spinlock_t ivr_read_lock;
+#endif
+
+int use_xtp = 0;		/* XXX */
+
+#define IPI_RESCHEDULE	        0
+#define IPI_CALL_FUNC	        1
+#define IPI_CPU_STOP	        2
+#define IPI_KDB_INTERRUPT	4
+
+/*
+ *	Setup routine for controlling SMP activation
+ *
+ *	Command-line option of "nosmp" or "maxcpus=0" will disable SMP
+ *      activation entirely (the MPS table probe still happens, though).
+ *
+ *	Command-line option of "maxcpus=<NUM>", where <NUM> is an integer
+ *	greater than 0, limits the maximum number of CPUs activated in
+ *	SMP mode to <NUM>.
+ */
+
+static int __init nosmp(char *str)
+{
+	max_cpus = 0;
+	return 1;
+}
+
+__setup("nosmp", nosmp);
+
+static int __init maxcpus(char *str)
+{
+	get_option(&str, &max_cpus);
+	return 1;
+}
+
+__setup("maxcpus=", maxcpus);
+
+/*
+ * Yoink this CPU from the runnable list... 
+ */
+void
+halt_processor(void) 
+{
+        clear_bit(smp_processor_id(), &cpu_initialized);
+	max_xtp();
+	__cli();
+        for (;;)
+		;
+
+}
+
+void
+handle_IPI(int irq, void *dev_id, struct pt_regs *regs) 
+{
+	int this_cpu = smp_processor_id();
+	unsigned long *pending_ipis = &ipi_op[this_cpu];
+	unsigned long ops;
+
+	/* Count this now; we may make a call that never returns. */
+	cpu_data[this_cpu].ipi_count++;
+
+	mb();	/* Order interrupt and bit testing. */
+	while ((ops = xchg(pending_ipis, 0)) != 0) {
+	  mb();	/* Order bit clearing and data access. */
+	  do {
+		unsigned long which;
+
+		which = ffz(~ops);
+		ops &= ~(1 << which);
+		
+		switch (which) {
+		case IPI_RESCHEDULE:
+			/* 
+			 * Reschedule callback.  Everything to be done is done by the 
+			 * interrupt return path.  
+			 */
+			break;
+			
+		case IPI_CALL_FUNC: 
+			{
+				struct smp_call_struct *data;
+				void (*func)(void *info);
+				void *info;
+				int wait;
+
+				data = smp_call_function_data;
+				func = data->func;
+				info = data->info;
+				wait = data->wait;
+
+				mb();
+				atomic_dec (&data->unstarted_count);
+
+				/* At this point the structure may be gone unless wait is true.  */
+				(*func)(info);
+
+				/* Notify the sending CPU that the task is done.  */
+				mb();
+				if (wait) 
+					atomic_dec (&data->unfinished_count);
+			}
+			break;
+
+		case IPI_CPU_STOP:
+			halt_processor();
+			break;
+
+#ifdef CONFIG_KDB
+		case IPI_KDB_INTERRUPT:
+			smp_kdb_interrupt(regs);
+			break;
+#endif
+
+		default:
+			printk(KERN_CRIT "Unknown IPI on CPU %d: %lu\n", this_cpu, which);
+			break;
+		} /* Switch */
+	  } while (ops);
+
+	  mb();	/* Order data access and bit testing. */
+	}
+}
+
+static inline void
+send_IPI(int dest_cpu, unsigned char vector)
+{
+	unsigned long ipi_addr;
+	unsigned long ipi_data;
+#ifdef	CONFIG_ITANIUM_ASTEP_SPECIFIC
+	unsigned long flags;
+#endif
+
+	ipi_data = vector;
+	ipi_addr = ipi_base_addr | ((dest_cpu << 8) << 4); /* 16-bit SAPIC ID's; assume CPU bus 0 */
+	mb();
+
+#ifdef	CONFIG_ITANIUM_ASTEP_SPECIFIC
+	/*
+	 * Disable IVR reads
+	 */
+	spin_lock_irqsave(&ivr_read_lock, flags);
+	writeq(ipi_data, ipi_addr);
+	spin_unlock_irqrestore(&ivr_read_lock, flags);
+#else
+ 	writeq(ipi_data, ipi_addr);
+#endif	/* CONFIG_ITANIUM_ASTEP_SPECIFIC */
+
+}
+
+static inline void
+send_IPI_single(int dest_cpu, int op) 
+{
+	
+	if (dest_cpu == -1) 
+                return;
+        
+        ipi_op[dest_cpu] |= (1 << op);
+	send_IPI(dest_cpu, IPI_IRQ);
+}
+
+static inline void
+send_IPI_allbutself(int op)
+{
+	int i;
+	int cpu_id = 0;
+	
+	for (i = 0; i < smp_num_cpus; i++) {
+		cpu_id = __cpu_logical_map[i];
+		if (cpu_id != smp_processor_id())
+			send_IPI_single(cpu_id, op);
+	}
+}
+
+static inline void
+send_IPI_all(int op)
+{
+	int i;
+
+	for (i = 0; i < smp_num_cpus; i++)
+		send_IPI_single(__cpu_logical_map[i], op);
+}
+
+static inline void
+send_IPI_self(int op)
+{
+	send_IPI_single(smp_processor_id(), op);
+}
+
+void
+smp_send_reschedule(int cpu)
+{
+	send_IPI_single(cpu, IPI_RESCHEDULE);
+}
+
+void
+smp_send_stop(void)
+{
+	send_IPI_allbutself(IPI_CPU_STOP);
+}
+
+/*
+ * Run a function on all other CPUs.
+ *  <func>	The function to run. This must be fast and non-blocking.
+ *  <info>	An arbitrary pointer to pass to the function.
+ *  <retry>	If true, keep retrying until ready.
+ *  <wait>	If true, wait until function has completed on other CPUs.
+ *  [RETURNS]   0 on success, else a negative status code.
+ *
+ * Does not return until remote CPUs are nearly ready to execute <func>
+ * or are or have executed.
+ */
+
+int
+smp_call_function (void (*func) (void *info), void *info, int retry, int wait)
+{
+	struct smp_call_struct data;
+	long timeout;
+	static spinlock_t lock = SPIN_LOCK_UNLOCKED;
+	
+	data.func = func;
+	data.info = info;
+	data.wait = wait;
+	atomic_set(&data.unstarted_count, smp_num_cpus - 1);
+	atomic_set(&data.unfinished_count, smp_num_cpus - 1);
+
+	if (retry) {
+		while (1) {
+			if (smp_call_function_data) {
+				schedule ();  /*  Give a mate a go  */
+				continue;
+			}
+			spin_lock (&lock);
+			if (smp_call_function_data) {
+				spin_unlock (&lock);  /*  Bad luck  */
+				continue;
+			}
+			/*  Mine, all mine!  */
+			break;
+		}
+	}
+	else {
+		if (smp_call_function_data) 
+			return -EBUSY;
+		spin_lock (&lock);
+		if (smp_call_function_data) {
+			spin_unlock (&lock);
+			return -EBUSY;
+		}
+	}
+
+	smp_call_function_data = &data;
+	spin_unlock (&lock);
+	data.func = func;
+	data.info = info;
+	atomic_set (&data.unstarted_count, smp_num_cpus - 1);
+	data.wait = wait;
+	if (wait) 
+		atomic_set (&data.unfinished_count, smp_num_cpus - 1);
+	
+	/*  Send a message to all other CPUs and wait for them to respond  */
+	send_IPI_allbutself(IPI_CALL_FUNC);
+
+	/*  Wait for response  */
+	timeout = jiffies + HZ;
+	while ( (atomic_read (&data.unstarted_count) > 0) &&
+		time_before (jiffies, timeout) )
+		barrier ();
+	if (atomic_read (&data.unstarted_count) > 0) {
+		smp_call_function_data = NULL;
+		return -ETIMEDOUT;
+	}
+	if (wait)
+		while (atomic_read (&data.unfinished_count) > 0)
+			barrier ();
+	smp_call_function_data = NULL;
+	return 0;
+}
+
+/*
+ * Flush all other CPU's tlb and then mine.  Do this with smp_call_function() as we
+ * want to ensure all TLB's flushed before proceeding.
+ *
+ * XXX: Is it OK to use the same ptc.e info on all cpus?
+ */
+void
+smp_flush_tlb_all(void)
+{
+	smp_call_function((void (*)(void *))__flush_tlb_all, NULL, 1, 1);
+	__flush_tlb_all();
+}
+
+/*
+ * Ideally sets up per-cpu profiling hooks.  Doesn't do much now...
+ */
+static inline void __init
+smp_setup_percpu_timer(int cpuid)
+{
+        cpu_data[cpuid].prof_counter = 1;
+        cpu_data[cpuid].prof_multiplier = 1;
+}
+
+void 
+smp_do_timer(struct pt_regs *regs)
+{
+        int cpu = smp_processor_id();
+        int user = user_mode(regs);
+	struct cpuinfo_ia64 *data = &cpu_data[cpu];
+
+	extern void update_one_process(struct task_struct *, unsigned long, unsigned long, 
+				       unsigned long, int);
+        if (!--data->prof_counter) {
+		irq_enter(cpu, TIMER_IRQ);
+
+		update_one_process(current, 1, user, !user, cpu);
+		if (current->pid) { 
+			if (--current->counter < 0) {
+				current->counter = 0;
+				current->need_resched = 1;
+			}
+
+			if (user) {
+				if (current->priority < DEF_PRIORITY) {
+					kstat.cpu_nice++;
+					kstat.per_cpu_nice[cpu]++;
+				} else {
+					kstat.cpu_user++;
+					kstat.per_cpu_user[cpu]++;
+				}
+			} else {
+				kstat.cpu_system++;
+				kstat.per_cpu_system[cpu]++;
+			}
+		} 
+		
+		data->prof_counter = data->prof_multiplier;
+		irq_exit(cpu, TIMER_IRQ);
+	}
+}
+
+
+/*
+ * Called by both boot and secondaries to move global data into
+ * per-processor storage.
+ */
+static inline void __init
+smp_store_cpu_info(int cpuid)
+{
+	struct cpuinfo_ia64 *c = &cpu_data[cpuid];
+
+	identify_cpu(c);
+}
+
+/* 
+ * SAL shoves the AP's here when we start them.  Physical mode, no kernel TR, 
+ * no RRs set, better than even chance that psr is bogus.  Fix all that and 
+ * call _start.  In effect, pretend to be lilo.
+ *
+ * Stolen from lilo_start.c.  Thanks David! 
+ */
+void
+start_ap(void)
+{
+	unsigned long flags;
+
+	/*
+	 * Install a translation register that identity maps the
+	 * kernel's 256MB page(s).
+	 */
+	ia64_clear_ic(flags);
+	ia64_set_rr(          0, (0x1000 << 8) | (_PAGE_SIZE_1M << 2));
+	ia64_set_rr(PAGE_OFFSET, (ia64_rid(0, PAGE_OFFSET) << 8) | (_PAGE_SIZE_256M << 2));
+	ia64_itr(0x3, 1, PAGE_OFFSET,
+		 pte_val(mk_pte_phys(0, __pgprot(__DIRTY_BITS|_PAGE_PL_0|_PAGE_AR_RWX))),
+		 _PAGE_SIZE_256M);
+
+	flags = (IA64_PSR_IT | IA64_PSR_IC | IA64_PSR_DT | IA64_PSR_RT | IA64_PSR_DFH | 
+		 IA64_PSR_BN);
+	
+	asm volatile ("movl r8 = 1f\n"
+		      ";;\n"
+		      "mov cr.ipsr=%0\n"
+		      "mov cr.iip=r8\n" 
+		      "mov cr.ifs=r0\n"
+		      ";;\n"
+		      "rfi;;"
+		      "1:\n"
+		      "movl r1 = __gp" :: "r"(flags) : "r8");
+	_start();
+}
+
+
+/*
+ * AP's start using C here.
+ */
+void __init
+smp_callin(void) 
+{
+	extern void ia64_rid_init(void);
+	extern void ia64_init_itm(void);
+	extern void ia64_cpu_local_tick(void);
+
+	ia64_set_dcr(IA64_DCR_DR | IA64_DCR_DK | IA64_DCR_DX | IA64_DCR_PP);
+	ia64_set_fpu_owner(0);	       
+	ia64_rid_init();		/* initialize region ids */
+
+	cpu_init();
+	__flush_tlb_all();
+
+	smp_store_cpu_info(smp_processor_id());
+	smp_setup_percpu_timer(smp_processor_id());
+
+	while (!smp_threads_ready) 
+		mb();
+
+	normal_xtp();
+
+	/* setup the CPU local timer tick */
+	ia64_cpu_local_tick();
+
+	/* Disable all local interrupts */
+	ia64_set_lrr0(0, 1);	
+	ia64_set_lrr1(0, 1);	
+
+	__sti();		/* Interrupts have been off till now. */
+	cpu_idle(NULL);
+}
+
+/*
+ * Create the idle task for a new AP.  DO NOT use kernel_thread() because
+ * that could end up calling schedule() in the ia64_leave_kernel exit
+ * path in which case the new idle task could get scheduled before we
+ * had a chance to remove it from the run-queue...
+ */
+static int __init 
+fork_by_hand(void)
+{
+	/*
+	 * Don't care about the usp and regs settings since we'll never
+	 * reschedule the forked task.
+	 */
+	return do_fork(CLONE_VM|CLONE_PID, 0, 0);
+}
+
+/*
+ * Bring one cpu online.
+ *
+ * NB: cpuid is the CPU BUS-LOCAL ID, not the entire SAPIC ID.  See asm/smp.h.
+ */
+static int __init
+smp_boot_one_cpu(int cpuid, int cpunum)
+{
+	struct task_struct *idle;
+	long timeout;
+
+	/* 
+	 * Create an idle task for this CPU.  Note that the address we
+	 * give to kernel_thread is irrelevant -- it's going to start
+	 * where OS_BOOT_RENDEVZ vector in SAL says to start.  But
+	 * this gets all the other task-y sort of data structures set
+	 * up like we wish.   We need to pull the just created idle task 
+	 * off the run queue and stuff it into the init_tasks[] array.  
+	 * Sheesh . . .
+	 */
+	if (fork_by_hand() < 0) 
+		panic("failed fork for CPU %d", cpuid);
+	/*
+	 * We remove it from the pidhash and the runqueue
+	 * once we got the process:
+	 */
+	idle = init_task.prev_task;
+	if (!idle)
+		panic("No idle process for CPU %d", cpuid);
+	init_tasks[cpunum] = idle;
+	del_from_runqueue(idle);
+        unhash_process(idle);
+
+	/* Schedule the first task manually.  */
+	idle->processor = cpuid;
+	idle->has_cpu = 1;
+
+	/* Let _start know what logical CPU we're booting (offset into init_tasks[] */
+	cpu_now_booting = cpunum;
+	
+	/* Kick the AP in the butt */
+	send_IPI(cpuid, ap_wakeup_vector);
+	ia64_srlz_i();
+	mb();
+
+	/* 
+	 * OK, wait a bit for that CPU to finish staggering about.  smp_callin() will
+	 * call cpu_init() which will set a bit for this AP.  When that bit flips, the AP
+	 * is waiting for smp_threads_ready to be 1 and we can move on.
+	 */
+	for (timeout = 0; timeout < 100000; timeout++) {
+		if (test_bit(cpuid, &cpu_initialized))
+			goto alive;
+		udelay(10);
+		barrier();
+	}
+
+	printk(KERN_ERR "SMP: Processor %d is stuck.\n", cpuid);
+	return -1;
+
+alive:
+	/* Remember the AP data */
+	cpu_number_map[cpuid] = cpunum;
+#ifdef CONFIG_KDB
+        cpu_online_map |= (1<<cpunum);
+        printk ("DEBUGGER: cpu_online_map = 0x%08x\n", cpu_online_map);
+#endif
+	__cpu_logical_map[cpunum] = cpuid;
+	return 0;
+}
+
+
+
+/*
+ * Called by smp_init bring all the secondaries online and hold them.  
+ * XXX: this is ACPI specific; it uses "magic" variables exported from acpi.c 
+ *      to 'discover' the AP's.  Blech.
+ */
+void __init
+smp_boot_cpus(void)
+{
+	int i, cpu_count = 1;
+	unsigned long bogosum;
+	int sapic_id;
+	extern int acpi_cpus;
+	extern int acpi_apic_map[32];
+
+	/* Take care of some initial bookkeeping.  */
+	memset(&cpu_number_map, -1, sizeof(cpu_number_map));
+	memset(&__cpu_logical_map, -1, sizeof(__cpu_logical_map));
+	memset(&ipi_op, 0, sizeof(ipi_op));
+
+	/* Setup BSP mappings */
+	cpu_number_map[bootstrap_processor] = 0;
+	__cpu_logical_map[0] = bootstrap_processor;
+	current->processor = bootstrap_processor;
+
+	/* Mark BSP booted and get active_mm context */
+	cpu_init();
+
+	/* reset XTP for interrupt routing */
+	normal_xtp();
+
+	/* And generate an entry in cpu_data */
+	smp_store_cpu_info(bootstrap_processor);
+#if 0
+	smp_tune_scheduling();
+#endif
+ 	smp_setup_percpu_timer(bootstrap_processor);
+
+	init_idle();
+
+	/* Nothing to do when told not to.  */
+	if (max_cpus == 0) {
+	        printk(KERN_INFO "SMP mode deactivated.\n");
+		return;
+	}
+
+	if (acpi_cpus > 1) {
+		printk(KERN_INFO "SMP: starting up secondaries.\n");
+
+		for (i = 0; i < NR_CPUS; i++) {
+			if (acpi_apic_map[i] == -1 || 
+			    acpi_apic_map[i] == bootstrap_processor << 8) /* XXX Fix me Walt */
+				continue;
+
+			/*
+			 * IA64 SAPIC ID's are 16-bits.  See asm/smp.h for more info 
+			 */
+			sapic_id = acpi_apic_map[i] >> 8;
+			if (smp_boot_one_cpu(sapic_id, cpu_count))
+				continue;
+
+			cpu_count++; /* Count good CPUs only... */
+		}
+	}
+
+	if (cpu_count == 1) {
+		printk(KERN_ERR "SMP: Bootstrap processor only.\n");
+		return;
+	}
+
+	bogosum = 0;
+        for (i = 0; i < NR_CPUS; i++) {
+		if (cpu_initialized & (1L << i))
+			bogosum += cpu_data[i].loops_per_sec;
+        }
+
+	printk(KERN_INFO "SMP: Total of %d processors activated "
+	       "(%lu.%02lu BogoMIPS).\n",
+	       cpu_count, (bogosum + 2500) / 500000,
+	       ((bogosum + 2500) / 5000) % 100);
+
+	smp_num_cpus = cpu_count;
+}
+
+/* 
+ * Called from main.c by each AP.
+ */
+void __init 
+smp_commence(void)
+{
+	mb();
+}
+
+/*
+ * Not used; part of the i386 bringup
+ */
+void __init 
+initialize_secondary(void)
+{
+}
+
+int __init
+setup_profiling_timer(unsigned int multiplier)
+{
+        return -EINVAL;
+}
+
+/*
+ * Assume that CPU's have been discovered by some platform-dependant
+ * interface.  For SoftSDV/Lion, that would be ACPI.
+ *
+ * Setup of the IPI irq handler is done in irq.c:init_IRQ_SMP().
+ *
+ * So this just gets the BSP SAPIC ID and print's it out.  Dull, huh?
+ *
+ * Not anymore.  This also registers the AP OS_MC_REDVEZ address with SAL.
+ */
+void __init
+init_smp_config(void)
+{
+	struct fptr {
+		unsigned long fp;
+		unsigned long gp;
+	} *ap_startup;
+	long sal_ret;
+
+	/* Grab the BSP ID */
+	bootstrap_processor = hard_smp_processor_id();
+
+	/* Tell SAL where to drop the AP's.  */
+	ap_startup = (struct fptr *) start_ap;
+	sal_ret = ia64_sal_set_vectors(SAL_VECTOR_OS_BOOT_RENDEZ,
+				       __pa(ap_startup->fp), __pa(ap_startup->gp), 0, 
+				       0, 0, 0);
+	if (sal_ret < 0) {
+		printk("SMP: Can't set SAL AP Boot Rendezvous: %s\n", ia64_sal_strerror(sal_ret));
+		printk("     Forcing UP mode\n");
+		smp_num_cpus = 1; 
+	}
+
+}
+
+#ifdef CONFIG_KDB
+void smp_kdb_stop (int all, struct pt_regs* regs)
+{
+        if (all)
+      {
+              printk ("Sending IPI to all on CPU %i\n", smp_processor_id ());
+                smp_kdb_wait = 0xffffffff;
+                clear_bit (smp_processor_id(), &smp_kdb_wait);
+                send_IPI_allbutself (IPI_KDB_INTERRUPT);
+        }
+      else
+      {
+              printk ("Sending IPI to self on CPU %i\n",
+                      smp_processor_id ());
+                set_bit (smp_processor_id(), &smp_kdb_wait);
+              clear_bit (__cpu_logical_map[kdb_new_cpu], &smp_kdb_wait);
+                smp_kdb_interrupt (regs);
+        }
+}
+
+void smp_kdb_interrupt (struct pt_regs* regs)
+{
+        printk ("kdb: IPI on CPU %i with mask 0x%08x\n",
+              smp_processor_id (), smp_kdb_wait);
+
+      /* All CPUs spin here forever */
+        while (test_bit (smp_processor_id(), &smp_kdb_wait));
+
+      /* Enter KDB on CPU selected by KDB on the last CPU */
+        if (__cpu_logical_map[kdb_new_cpu] == smp_processor_id ())
+      {
+                kdb (KDB_REASON_SWITCH, 0, regs);
+        }
+}
+
+#endif
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)