patch-2.1.34 linux/arch/sparc/kernel/smp.c
Next file: linux/arch/sparc/kernel/sparc_ksyms.c
Previous file: linux/arch/sparc/kernel/setup.c
Back to the patch index
Back to the overall index
- Lines: 582
- Date:
Mon Apr 14 09:31:09 1997
- Orig file:
v2.1.33/linux/arch/sparc/kernel/smp.c
- Orig date:
Sun Jan 26 02:07:07 1997
diff -u --recursive --new-file v2.1.33/linux/arch/sparc/kernel/smp.c linux/arch/sparc/kernel/smp.c
@@ -5,6 +5,7 @@
#include <asm/head.h>
#include <asm/ptrace.h>
+#include <asm/atomic.h>
#include <linux/kernel.h>
#include <linux/tasks.h>
@@ -18,38 +19,31 @@
#include <asm/pgtable.h>
#include <asm/oplib.h>
#include <asm/atops.h>
+#include <asm/spinlock.h>
+#include <asm/hardirq.h>
+#include <asm/softirq.h>
+
+#define IRQ_RESCHEDULE 13
+#define IRQ_STOP_CPU 14
+#define IRQ_CROSS_CALL 15
extern ctxd_t *srmmu_ctx_table_phys;
extern int linux_num_cpus;
-struct tlog {
- unsigned long pc;
- unsigned long psr;
-};
-
-struct tlog trap_log[4][256];
-unsigned long trap_log_ent[4] = { 0, 0, 0, 0, };
-
extern void calibrate_delay(void);
-volatile unsigned long stuck_pc = 0;
volatile int smp_processors_ready = 0;
-int smp_found_config = 0;
unsigned long cpu_present_map = 0;
int smp_num_cpus = 1;
int smp_threads_ready=0;
unsigned char mid_xlate[NR_CPUS] = { 0, 0, 0, 0, };
volatile unsigned long cpu_callin_map[NR_CPUS] = {0,};
-volatile unsigned long smp_invalidate_needed[NR_CPUS] = { 0, };
volatile unsigned long smp_spinning[NR_CPUS] = { 0, };
+unsigned long smp_proc_in_lock[NR_CPUS] = { 0, };
struct cpuinfo_sparc cpu_data[NR_CPUS];
-unsigned char boot_cpu_id = 0;
+static unsigned char boot_cpu_id = 0;
static int smp_activated = 0;
-static volatile unsigned char smp_cpu_in_msg[NR_CPUS];
-static volatile unsigned long smp_msg_data;
-static volatile int smp_src_cpu;
-static volatile int smp_msg_id;
volatile int cpu_number_map[NR_CPUS];
volatile int cpu_logical_map[NR_CPUS];
@@ -60,7 +54,7 @@
* compared to the Alpha and the intel no? Most Sparcs have 'swap'
* instruction which is much better...
*/
-struct klock_info klock_info = { KLOCK_CLEAR, 0, 0 };
+struct klock_info klock_info = { KLOCK_CLEAR, 0 };
volatile unsigned long ipi_count;
#ifdef __SMP_PROF__
@@ -74,7 +68,6 @@
volatile unsigned long smp_idle_map=0;
#endif
-volatile unsigned long smp_proc_in_lock[NR_CPUS] = {0,};
volatile int smp_process_available=0;
/*#define SMP_DEBUG*/
@@ -180,7 +173,7 @@
local_flush_cache_all();
local_flush_tlb_all();
- sti();
+ __sti();
}
void cpu_panic(void)
@@ -200,14 +193,15 @@
{
int cpucount = 0;
int i = 0;
+ int first, prev;
printk("Entering SMP Mode...\n");
penguin_ctable.which_io = 0;
- penguin_ctable.phys_addr = (char *) srmmu_ctx_table_phys;
+ penguin_ctable.phys_addr = (unsigned int) srmmu_ctx_table_phys;
penguin_ctable.reg_size = 0;
- sti();
+ __sti();
cpu_present_map = 0;
for(i=0; i < linux_num_cpus; i++)
cpu_present_map |= (1<<i);
@@ -218,7 +212,7 @@
mid_xlate[boot_cpu_id] = (linux_cpus[boot_cpu_id].mid & ~8);
cpu_number_map[boot_cpu_id] = 0;
cpu_logical_map[0] = boot_cpu_id;
- klock_info.akp = klock_info.irq_udt = boot_cpu_id;
+ klock_info.akp = boot_cpu_id;
smp_store_cpu_info(boot_cpu_id);
set_irq_udt(mid_xlate[boot_cpu_id]);
local_flush_cache_all();
@@ -280,139 +274,75 @@
smp_activated = 1;
smp_num_cpus = cpucount + 1;
}
+
+ /* Setup CPU list for IRQ distribution scheme. */
+ first = prev = -1;
+ for(i = 0; i < NR_CPUS; i++) {
+ if(cpu_present_map & (1 << i)) {
+ if(first == -1)
+ first = i;
+ if(prev != -1)
+ cpu_data[i].next = i;
+ cpu_data[i].mid = mid_xlate[i];
+ prev = i;
+ }
+ }
+ cpu_data[prev].next = first;
+
+ /* Ok, they are spinning and ready to go. */
smp_processors_ready = 1;
}
-static inline void send_ipi(unsigned long target_map, int irq)
+/* At each hardware IRQ, we get this called to forward IRQ reception
+ * to the next processor. The caller must disable the IRQ level being
+ * serviced globally so that there are no double interrupts received.
+ */
+void smp_irq_rotate(int cpu)
{
- int i;
-
- for(i = 0; i < 4; i++) {
- if((1<<i) & target_map)
- set_cpu_int(mid_xlate[i], irq);
- }
+ if(smp_processors_ready)
+ set_irq_udt(cpu_data[cpu_data[cpu].next].mid);
}
-/*
- * A non wait message cannot pass data or cpu source info. This current
- * setup is only safe because the kernel lock owner is the only person
- * who can send a message.
- *
- * Wrapping this whole block in a spinlock is not the safe answer either.
- * A processor may get stuck with irq's off waiting to send a message and
- * thus not replying to the person spinning for a reply....
- *
- * On the Sparc we use NMI's for all messages except reschedule.
+/* Cross calls, in order to work efficiently and atomically do all
+ * the message passing work themselves, only stopcpu and reschedule
+ * messages come through here.
*/
-
-static volatile int message_cpu = NO_PROC_ID;
-
void smp_message_pass(int target, int msg, unsigned long data, int wait)
{
- unsigned long target_map;
- int p = smp_processor_id();
- int irq = 15;
- int i;
-
- /* Before processors have been placed into their initial
- * patterns do not send messages.
- */
- if(!smp_processors_ready)
- return;
+ static unsigned long smp_cpu_in_msg[NR_CPUS];
+ unsigned long mask;
+ int me = smp_processor_id();
+ int irq, i;
- /* Skip the reschedule if we are waiting to clear a
- * message at this time. The reschedule cannot wait
- * but is not critical.
- */
if(msg == MSG_RESCHEDULE) {
- if(!smp_commenced)
- return;
- irq = 13;
- if(smp_cpu_in_msg[p])
- return;
- }
-
- /* Sanity check we don't re-enter this across CPU's. Only the kernel
- * lock holder may send messages. For a STOP_CPU we are bringing the
- * entire box to the fastest halt we can.. A reschedule carries
- * no data and can occur during a flush.. guess what panic
- * I got to notice this bug...
- */
- if(message_cpu != NO_PROC_ID && msg != MSG_STOP_CPU && msg != MSG_RESCHEDULE) {
- printk("CPU #%d: Message pass %d but pass in progress by %d of %d\n",
- smp_processor_id(),msg,message_cpu, smp_msg_id);
-
- /* I don't know how to gracefully die so that debugging
- * this doesn't completely eat up my filesystems...
- * let's try this...
- */
- smp_cpu_in_msg[p] = 0; /* In case we come back here... */
- intr_count = 0; /* and so panic don't barf... */
- smp_swap(&message_cpu, NO_PROC_ID); /* push the store buffer */
- sti();
- printk("spinning, please L1-A, type ctrace and send output to davem\n");
- while(1)
- barrier();
- }
- smp_swap(&message_cpu, smp_processor_id()); /* store buffers... */
+ irq = IRQ_RESCHEDULE;
- /* We are busy. */
- smp_cpu_in_msg[p]++;
-
- /* Reschedule is currently special. */
- if(msg != MSG_RESCHEDULE) {
- smp_src_cpu = p;
- smp_msg_id = msg;
- smp_msg_data = data;
+ if(smp_cpu_in_msg[me])
+ return;
+ } else if(msg == MSG_STOP_CPU) {
+ irq = IRQ_STOP_CPU;
+ } else {
+ goto barf;
}
-#if 0
- printk("SMP message pass from cpu %d to cpu %d msg %d\n", p, target, msg);
-#endif
-
- /* Set the target requirement. */
- for(i = 0; i < smp_num_cpus; i++)
- swap((unsigned long *) &cpu_callin_map[i], 0);
- if(target == MSG_ALL_BUT_SELF) {
- target_map = (cpu_present_map & ~(1<<p));
- swap((unsigned long *) &cpu_callin_map[p], 1);
- } else if(target == MSG_ALL) {
- target_map = cpu_present_map;
+ smp_cpu_in_msg[me]++;
+ if(target == MSG_ALL_BUT_SELF || target == MSG_ALL) {
+ mask = cpu_present_map;
+ if(target == MSG_ALL_BUT_SELF)
+ mask &= ~(1 << me);
+ for(i = 0; i < 4; i++) {
+ if(mask & (1 << i))
+ set_cpu_int(mid_xlate[i], irq);
+ }
} else {
- for(i = 0; i < smp_num_cpus; i++)
- if(i != target)
- swap((unsigned long *) &cpu_callin_map[i], 1);
- target_map = (1<<target);
+ set_cpu_int(mid_xlate[target], irq);
}
+ smp_cpu_in_msg[me]--;
- /* Fire it off. */
- send_ipi(target_map, irq);
-
- switch(wait) {
- case 1:
- for(i = 0; i < smp_num_cpus; i++)
- while(!cpu_callin_map[i])
- barrier();
- break;
- case 2:
- for(i = 0; i < smp_num_cpus; i++)
- while(smp_invalidate_needed[i])
- barrier();
- break;
- case 3:
- /* For cross calls we hold message_cpu and smp_cpu_in_msg[]
- * until all processors disperse. Else we have _big_ problems.
- */
- return;
- case 4:
- /* For captures we hold smp_cpu_in_msg[] until all processors
- * released. Else we have _big_ problems.
- */
- message_cpu = NO_PROC_ID;
- return;
- }
- smp_cpu_in_msg[p]--;
- message_cpu = NO_PROC_ID;
+ return;
+barf:
+ printk("Yeeee, trying to send SMP msg(%d) on cpu %d\n", msg, me);
+ panic("Bogon SMP message pass.");
}
struct smp_funcall {
@@ -432,29 +362,20 @@
#define CCALL_TIMEOUT 5000000 /* enough for initial testing */
-/* #define DEBUG_CCALL */
+static spinlock_t cross_call_lock = SPIN_LOCK_UNLOCKED;
-/* Some nice day when we really thread the kernel I'd like to synchronize
- * this with either a broadcast conditional variable, a resource adaptive
- * generic mutex, or a convoy semaphore scheme of some sort. No reason
- * we can't let multiple processors in here if the appropriate locking
- * is done. Note that such a scheme assumes we will have a
- * prioritized ipi scheme using different software level irq's.
- */
+/* Cross calls must be serialized, at least currently. */
void smp_cross_call(smpfunc_t func, unsigned long arg1, unsigned long arg2,
unsigned long arg3, unsigned long arg4, unsigned long arg5)
{
unsigned long me = smp_processor_id();
- unsigned long flags;
+ unsigned long flags, mask;
int i, timeout;
-#ifdef DEBUG_CCALL
- printk("xc%d<", me);
-#endif
if(smp_processors_ready) {
- save_and_cli(flags);
- if(me != klock_info.akp)
- goto cross_call_not_master;
+ __save_flags(flags);
+ __cli();
+ spin_lock(&cross_call_lock);
/* Init function glue. */
ccall_info.func = func;
@@ -473,7 +394,11 @@
ccall_info.processors_out[me] = 1;
/* Fire it off. */
- smp_message_pass(MSG_ALL_BUT_SELF, MSG_CROSS_CALL, 0, 3);
+ mask = (cpu_present_map & ~(1 << me));
+ for(i = 0; i < 4; i++) {
+ if(mask & (1 << i))
+ set_cpu_int(mid_xlate[i], IRQ_CROSS_CALL);
+ }
/* For debugging purposes right now we can timeout
* on both callin and callexit.
@@ -485,9 +410,6 @@
if(!ccall_info.processors_in[i])
goto procs_time_out;
}
-#ifdef DEBUG_CCALL
- printk("I");
-#endif
/* Run local copy. */
func(arg1, arg2, arg3, arg4, arg5);
@@ -500,33 +422,19 @@
if(!ccall_info.processors_out[i])
goto procs_time_out;
}
-#ifdef DEBUG_CCALL
- printk("O>");
-#endif
- /* See wait case 3 in smp_message_pass()... */
- smp_cpu_in_msg[me]--;
- message_cpu = NO_PROC_ID;
- restore_flags(flags);
+ spin_unlock(&cross_call_lock);
+ __restore_flags(flags);
return; /* made it... */
procs_time_out:
printk("smp: Wheee, penguin drops off the bus\n");
- smp_cpu_in_msg[me]--;
- message_cpu = NO_PROC_ID;
- restore_flags(flags);
+ spin_unlock(&cross_call_lock);
+ __restore_flags(flags);
return; /* why me... why me... */
}
/* Just need to run local copy. */
func(arg1, arg2, arg3, arg4, arg5);
- return;
-
-cross_call_not_master:
- printk("Cross call initiated by non master cpu from [%p]\n",
- __builtin_return_address(0));
- printk("akp=%x me=%08lx\n", klock_info.akp, me);
- restore_flags(flags);
- panic("penguin cross call");
}
void smp_flush_cache_all(void)
@@ -578,171 +486,24 @@
/* Reschedule call back. */
void smp_reschedule_irq(void)
{
- lock_kernel();
- intr_count++;
- if(smp_processor_id() != klock_info.akp) {
- panic("SMP Reschedule on CPU #%d, but #%d is active.\n",
- smp_processor_id(), klock_info.akp);
- }
need_resched=1;
- intr_count--;
- unlock_kernel();
}
-#undef DEBUG_CAPTURE
-
-static struct capture_struct {
- int capture_level;
- volatile unsigned long processors_in[NR_CPUS];
- volatile unsigned long processors_out[NR_CPUS];
-} cap_info = { 0, };
-
-void smp_capture(void)
+/* Running cross calls. */
+void smp_cross_call_irq(void)
{
- unsigned long flags;
- int me = smp_processor_id();
- int cpu;
-
- if(!smp_processors_ready)
- return;
-
- if (me != klock_info.akp)
- panic("SMP Capture on CPU #%d, but #%d is active.\n",
- me, klock_info.akp);
-
-#ifdef DEBUG_CAPTURE
- printk("C%d(%d)<", smp_processor_id(), cap_info.capture_level);
-#endif
- save_and_cli(flags);
-
- if (! cap_info.capture_level++) {
- int timeout;
-
- /* Initialize Capture Info. */
- for (cpu = 0; cpu < smp_num_cpus; cpu++) {
- cap_info.processors_in[cpu] = 0;
- cap_info.processors_out[cpu] = 0;
- }
- cap_info.processors_in[me] = 1;
- cap_info.processors_out[me] = 1;
-
- smp_message_pass(MSG_ALL_BUT_SELF, MSG_CAPTURE, 0, 4);
-
- timeout = CCALL_TIMEOUT;
- for (cpu = 0; cpu < smp_num_cpus; cpu++) {
- while (!cap_info.processors_in[cpu] && timeout-- > 0)
- barrier();
- if (!cap_info.processors_in[cpu])
- goto procs_time_out;
-#ifdef DEBUG_CAPTURE
- printk("%d", cpu);
-#endif
- }
- }
-#ifdef DEBUG_CAPTURE
- printk(">");
-#endif
- restore_flags(flags);
- return;
-
-procs_time_out:
- printk("smp_capture (%d): Wheee, penguin drops off the bus\n",
- smp_processor_id());
- printk("smp_capture: map: %ld %ld %ld %ld\n",
- cap_info.processors_in[0], cap_info.processors_in[1],
- cap_info.processors_in[2], cap_info.processors_in[3]);
- smp_cpu_in_msg[me]--;
- message_cpu = NO_PROC_ID;
- restore_flags(flags);
-}
-
-void smp_release(void)
-{
- unsigned long flags;
- int me = smp_processor_id();
- int cpu;
-
- if(!smp_processors_ready)
- return;
-#ifdef DEBUG_CAPTURE
- printk("R%d(%d)<", smp_processor_id(), cap_info.capture_level);
-#endif
-
- if (me != klock_info.akp)
- panic("SMP Release on CPU #%d, but #%d is active.\n",
- me, klock_info.akp);
-
- save_and_cli(flags);
-
- if (! --cap_info.capture_level) {
- int timeout;
-
- for (cpu = 0; cpu < smp_num_cpus; cpu++)
- cap_info.processors_in[cpu] = 0;
-
- timeout = CCALL_TIMEOUT;
- for (cpu = 0; cpu < smp_num_cpus; cpu++) {
- while (!cap_info.processors_out[cpu] && timeout-- > 0)
- barrier();
- if (!cap_info.processors_out[cpu])
- goto procs_time_out;
-#ifdef DEBUG_CAPTURE
- printk("%d", cpu);
-#endif
- }
-
- /* See wait case 4 in smp_message_pass()... */
- smp_cpu_in_msg[me]--;
- }
-#ifdef DEBUG_CAPTURE
- printk(">");
-#endif
- restore_flags(flags);
- return;
+ int i = smp_processor_id();
-procs_time_out:
- printk("smp_release (%d): Wheee, penguin drops off the bus\n",
- smp_processor_id());
- printk("smp_release: map: %ld %ld %ld %ld\n",
- cap_info.processors_out[0], cap_info.processors_out[1],
- cap_info.processors_out[2], cap_info.processors_out[3]);
- smp_cpu_in_msg[me]--;
- restore_flags(flags);
+ ccall_info.processors_in[i] = 1;
+ ccall_info.func(ccall_info.arg1, ccall_info.arg2, ccall_info.arg3,
+ ccall_info.arg4, ccall_info.arg5);
+ ccall_info.processors_out[i] = 1;
}
-/* Message call back. */
-void smp_message_irq(void)
+/* Stopping processors. */
+void smp_stop_cpu_irq(void)
{
- int i=smp_processor_id();
-
- switch(smp_msg_id) {
- case MSG_CROSS_CALL:
- /* Do it to it. */
- ccall_info.processors_in[i] = 1;
- ccall_info.func(ccall_info.arg1, ccall_info.arg2, ccall_info.arg3,
- ccall_info.arg4, ccall_info.arg5);
- ccall_info.processors_out[i] = 1;
- break;
-
- case MSG_CAPTURE:
- flush_user_windows();
- cap_info.processors_in[i] = 1;
- while (cap_info.processors_in[i])
- barrier();
- cap_info.processors_out[i] = 1;
- break;
-
- /*
- * Halt other CPU's for a panic or reboot
- */
- case MSG_STOP_CPU:
- sti();
- while(1)
- barrier();
-
- default:
- printk("CPU #%d sent invalid cross CPU message to CPU #%d: %X(%lX).\n",
- smp_src_cpu,smp_processor_id(),smp_msg_id,smp_msg_data);
- break;
- }
+ __sti();
+ while(1)
+ barrier();
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov