patch-2.1.92 linux/arch/alpha/kernel/smp.c
Next file: linux/arch/alpha/kernel/t2.c
Previous file: linux/arch/alpha/kernel/setup.c
Back to the patch index
Back to the overall index
- Lines: 1097
- Date:
Mon Mar 30 00:21:39 1998
- Orig file:
v2.1.91/linux/arch/alpha/kernel/smp.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/kernel/smp.c linux/arch/alpha/kernel/smp.c
@@ -0,0 +1,1096 @@
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/tasks.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+
+#include <asm/hwrpb.h>
+#include <asm/ptrace.h>
+#include <asm/atomic.h>
+
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+#include <asm/pgtable.h>
+#include <asm/spinlock.h>
+#include <asm/hardirq.h>
+#include <asm/softirq.h>
+
+#define __KERNEL_SYSCALLS__
+#include <asm/unistd.h>
+
+struct ipi_msg_flush_tb_struct ipi_msg_flush_tb;
+
+struct cpuinfo_alpha cpu_data[NR_CPUS];
+
+/* Processor holding kernel spinlock */
+klock_info_t klock_info = { KLOCK_CLEAR, 0 };
+
+spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED;
+
+unsigned int boot_cpu_id = 0;
+static int smp_activated = 0;
+
+int smp_found_config = 0; /* Have we found an SMP box */
+static int max_cpus = -1;
+
+unsigned int cpu_present_map = 0;
+
+int smp_num_cpus = 1;
+int smp_num_probed = 0; /* Internal processor count */
+
+int smp_threads_ready = 0;
+volatile unsigned long cpu_callin_map[NR_CPUS] = {0,};
+volatile unsigned long smp_spinning[NR_CPUS] = { 0, };
+
+unsigned int prof_multiplier[NR_CPUS];
+unsigned int prof_counter[NR_CPUS];
+
+volatile int ipi_bits[NR_CPUS];
+
+unsigned long boot_cpu_palrev;
+
+volatile int smp_commenced = 0;
+volatile int smp_processors_ready = 0;
+
+volatile int cpu_number_map[NR_CPUS];
+volatile int cpu_logical_map[NR_CPUS];
+
+extern int cpu_idle(void *unused);
+extern void calibrate_delay(void);
+extern struct hwrpb_struct *hwrpb;
+extern struct thread_struct * original_pcb_ptr;
+extern void __start_cpu(unsigned long);
+
+static void smp_setup_percpu_timer(void);
+static void secondary_cpu_start(int, struct task_struct *);
+static void send_cpu_msg(char *, int);
+
+/* process bootcommand SMP options, like "nosmp" and "maxcpus=" */
+__initfunc(void smp_setup(char *str, int *ints))
+{
+ if (ints && ints[0] > 0)
+ max_cpus = ints[1];
+ else
+ max_cpus = 0;
+}
+
+void smp_store_cpu_info(int id)
+{
+ /* This is it on Alpha, so far. */
+ cpu_data[id].loops_per_sec = loops_per_sec;
+}
+
+void smp_commence(void)
+{
+ /* Lets the callin's below out of their loop. */
+ mb();
+ smp_commenced = 1;
+}
+
+void smp_callin(void)
+{
+ int cpuid = hard_smp_processor_id();
+
+#if 0
+ printk("CALLIN %d state 0x%lx\n", cpuid, current->state);
+#endif
+#ifdef HUH
+ local_flush_cache_all();
+ local_flush_tlb_all();
+#endif
+#if 0
+ set_irq_udt(mid_xlate[boot_cpu_id]);
+#endif
+
+ /* Get our local ticker going. */
+ smp_setup_percpu_timer();
+
+#if 0
+ calibrate_delay();
+#endif
+ smp_store_cpu_info(cpuid);
+#ifdef HUH
+ local_flush_cache_all();
+ local_flush_tlb_all();
+#endif
+
+ /* Allow master to continue. */
+ set_bit(cpuid, (unsigned long *)&cpu_callin_map[cpuid]);
+#ifdef HUH
+ local_flush_cache_all();
+ local_flush_tlb_all();
+#endif
+
+#ifdef NOT_YET
+ while(!task[cpuid] || current_set[cpuid] != task[cpuid])
+ barrier();
+#endif /* NOT_YET */
+
+#if 0
+ /* Fix idle thread fields. */
+ __asm__ __volatile__("ld [%0], %%g6\n\t"
+ : : "r" (¤t_set[cpuid])
+ : "memory" /* paranoid */);
+ current->mm->mmap->vm_page_prot = PAGE_SHARED;
+ current->mm->mmap->vm_start = PAGE_OFFSET;
+ current->mm->mmap->vm_end = init_task.mm->mmap->vm_end;
+#endif
+
+#ifdef HUH
+ local_flush_cache_all();
+ local_flush_tlb_all();
+#endif
+#if 0
+ __sti();
+#endif
+}
+
+asmlinkage int start_secondary(void *unused)
+{
+ extern asmlinkage void entInt(void);
+ extern void paging_init_secondary(void);
+
+ wrmces(7);
+ paging_init_secondary();
+ trap_init();
+ wrent(entInt, 0);
+
+ smp_callin();
+ while (!smp_commenced)
+ barrier();
+#if 1
+printk("start_secondary: commencing CPU %d current %p\n",
+ hard_smp_processor_id(), current);
+#endif
+ return cpu_idle(NULL);
+}
+
+/*
+ * Cycle through the processors sending START msgs to boot each.
+ */
+void smp_boot_cpus(void)
+{
+ int cpucount = 0;
+ int i, first, prev;
+
+ printk("smp_boot_cpus: Entering SMP Mode...\n");
+
+#if 0
+ __sti();
+#endif
+
+ for(i=0; i < NR_CPUS; i++) {
+ cpu_number_map[i] = -1;
+ cpu_logical_map[i] = -1;
+ prof_counter[i] = 1;
+ prof_multiplier[i] = 1;
+ ipi_bits[i] = 0;
+ }
+
+ cpu_number_map[boot_cpu_id] = 0;
+ cpu_logical_map[0] = boot_cpu_id;
+ current->processor = boot_cpu_id; /* ??? */
+ klock_info.akp = boot_cpu_id;
+
+ smp_store_cpu_info(boot_cpu_id);
+#ifdef NOT_YET
+ printk("CPU%d: ", boot_cpu_id);
+ print_cpu_info(&cpu_data[boot_cpu_id]);
+ set_irq_udt(mid_xlate[boot_cpu_id]);
+#endif /* NOT_YET */
+ smp_setup_percpu_timer();
+#ifdef HUH
+ local_flush_cache_all();
+#endif
+ if (smp_num_probed == 1)
+ return; /* Not an MP box. */
+
+#if NOT_YET
+ /*
+ * If SMP should be disabled, then really disable it!
+ */
+ if (!max_cpus)
+ {
+ smp_found_config = 0;
+ printk(KERN_INFO "SMP mode deactivated.\n");
+ }
+#endif /* NOT_YET */
+
+ for (i = 0; i < NR_CPUS; i++) {
+
+ if (i == boot_cpu_id)
+ continue;
+
+ if (cpu_present_map & (1 << i)) {
+ struct task_struct *idle;
+ int timeout;
+
+ /* Cook up an idler for this guy. */
+ kernel_thread(start_secondary, NULL, CLONE_PID);
+ idle = task[++cpucount];
+ if (!idle)
+ panic("No idle process for CPU %d", i);
+ idle->processor = i;
+
+#if 0
+printk("smp_boot_cpus: CPU %d state 0x%lx flags 0x%lx\n",
+ i, idle->state, idle->flags);
+#endif
+
+ /* whirrr, whirrr, whirrrrrrrrr... */
+#ifdef HUH
+ local_flush_cache_all();
+#endif
+ secondary_cpu_start(i, idle);
+
+ /* wheee... it's going... wait for 5 secs...*/
+ for (timeout = 0; timeout < 50000; timeout++) {
+ if (cpu_callin_map[i])
+ break;
+ udelay(100);
+ }
+ if (cpu_callin_map[i]) {
+ /* Another "Red Snapper". */
+ cpu_number_map[i] = cpucount;
+ cpu_logical_map[cpucount] = i;
+ } else {
+ cpucount--;
+ printk("smp_boot_cpus: Processor %d"
+ " is stuck 0x%lx.\n", i, idle->flags);
+ }
+ }
+ if (!(cpu_callin_map[i])) {
+ cpu_present_map &= ~(1 << i);
+ cpu_number_map[i] = -1;
+ }
+ }
+#ifdef HUH
+ local_flush_cache_all();
+#endif
+ if (cpucount == 0) {
+ printk("smp_boot_cpus: ERROR - only one Processor found.\n");
+ cpu_present_map = (1 << smp_processor_id());
+ } else {
+ unsigned long bogosum = 0;
+ for (i = 0; i < NR_CPUS; i++) {
+ if (cpu_present_map & (1 << i))
+ bogosum += cpu_data[i].loops_per_sec;
+ }
+ printk("smp_boot_cpus: Total of %d Processors activated"
+ " (%lu.%02lu BogoMIPS).\n",
+ cpucount + 1,
+ (bogosum + 2500)/500000,
+ ((bogosum + 2500)/5000)%100);
+ 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;
+ prev = i;
+ }
+ }
+ cpu_data[prev].next = first;
+
+ /* Ok, they are spinning and ready to go. */
+ smp_processors_ready = 1;
+}
+
+__initfunc(void ioapic_pirq_setup(char *str, int *ints))
+{
+ /* this is prolly INTEL-specific */
+}
+
+static void smp_setup_percpu_timer(void)
+{
+ int cpu = smp_processor_id();
+
+ prof_counter[cpu] = prof_multiplier[cpu] = 1;
+#ifdef NOT_YET
+ load_profile_irq(mid_xlate[cpu], lvl14_resolution);
+ if (cpu == boot_cpu_id)
+ enable_pil_irq(14);
+#endif
+}
+
+extern void update_one_process(struct task_struct *p, unsigned long ticks,
+ unsigned long user, unsigned long system,
+ int cpu);
+
+void smp_percpu_timer_interrupt(struct pt_regs *regs)
+{
+ int cpu = smp_processor_id();
+
+#ifdef NOT_YET
+ clear_profile_irq(mid_xlate[cpu]);
+#ifdef CONFIG_PROFILE
+ if(!user_mode(regs))
+ sparc_do_profile(regs->pc);
+#endif
+#endif
+
+ if (!--prof_counter[cpu]) {
+ int user = user_mode(regs);
+ if (current->pid) {
+ update_one_process(current, 1, user, !user, cpu);
+
+ if (--current->counter < 0) {
+ current->counter = 0;
+ need_resched = 1;
+ }
+
+ spin_lock(&ticker_lock);
+ 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]++;
+ }
+ spin_unlock(&ticker_lock);
+ }
+ prof_counter[cpu] = prof_multiplier[cpu];
+ }
+}
+
+int setup_profiling_timer(unsigned int multiplier)
+{
+#ifdef NOT_YET
+ int i;
+ unsigned long flags;
+
+ /* Prevent level14 ticker IRQ flooding. */
+ if((!multiplier) || (lvl14_resolution / multiplier) < 500)
+ return -EINVAL;
+
+ save_and_cli(flags);
+ for(i = 0; i < NR_CPUS; i++) {
+ if(cpu_present_map & (1 << i)) {
+ load_profile_irq(mid_xlate[i], lvl14_resolution / multip
+lier);
+ prof_multiplier[i] = multiplier;
+ }
+ }
+ restore_flags(flags);
+
+ return 0;
+
+#endif
+ return -EINVAL;
+}
+
+/* Only broken Intel needs this, thus it should not even be referenced
+ * globally...
+ */
+__initfunc(void initialize_secondary(void))
+{
+ printk("initialize_secondary: entry\n");
+}
+
+static void
+secondary_cpu_start(int cpuid, struct task_struct *idle)
+{
+ struct percpu_struct *cpu;
+ int timeout;
+
+ cpu = (struct percpu_struct *)
+ ((char*)hwrpb
+ + hwrpb->processor_offset
+ + cpuid * hwrpb->processor_size);
+
+ /* set context to idle thread this CPU will use when running */
+ /* assumption is that the idle thread is all set to go... ??? */
+ memcpy(&cpu->hwpcb[0], &idle->tss, sizeof(struct pcb_struct));
+ cpu->hwpcb[4] = cpu->hwpcb[0]; /* UNIQUE set to KSP ??? */
+#if 0
+printk("KSP 0x%lx PTBR 0x%lx VPTBR 0x%lx\n",
+ cpu->hwpcb[0], cpu->hwpcb[2], hwrpb->vptb);
+printk("Starting secondary cpu %d: state 0x%lx pal_flags 0x%lx\n",
+ cpuid, idle->state, idle->tss.pal_flags);
+#endif
+
+ /* setup HWRPB fields that SRM uses to activate secondary CPU */
+ hwrpb->CPU_restart = __start_cpu;
+ hwrpb->CPU_restart_data = (unsigned long) idle;
+
+ /* recalculate and update the HWRPB checksum */
+ {
+ unsigned long sum, *lp1, *lp2;
+ sum = 0;
+ lp1 = (unsigned long *)hwrpb;
+ lp2 = &hwrpb->chksum;
+ while (lp1 < lp2)
+ sum += *lp1++;
+ *lp2 = sum;
+ }
+
+ /*
+ * Send a "start" command to the specified processor.
+ */
+
+ /* SRM III 3.4.1.3 */
+ cpu->flags |= 0x22; /* turn on Context Valid and Restart Capable */
+ cpu->flags &= ~1;/* turn off Bootstrap In Progress */
+ mb();
+
+ send_cpu_msg("START\r\n", cpuid);
+
+ /* now, we wait... */
+ for (timeout = 10000; !(cpu->flags & 1); timeout--) {
+ if (timeout <= 0) {
+ printk("Processor %d failed to start\n", cpuid);
+ /* needed for pset_info to work */
+#if 0
+ ipc_processor_enable(cpu_to_processor(cpunum));
+#endif
+ return;
+ }
+ udelay(1000);
+ }
+#if 0
+ printk("secondary_cpu_start: SUCCESS for CPU %d!!!\n", cpuid);
+#endif
+}
+
+static void
+send_cpu_msg(char *str, int cpuid)
+{
+ struct percpu_struct *cpu;
+ register char *cp1, *cp2;
+ unsigned long cpumask;
+ int timeout;
+
+
+ cpu = (struct percpu_struct *)
+ ((char*)hwrpb
+ + hwrpb->processor_offset
+ + cpuid * hwrpb->processor_size);
+
+ cpumask = (1L << cpuid);
+ for (timeout = 10000; (hwrpb->txrdy & cpumask); timeout--) {
+ if (timeout <= 0) {
+ printk("Processor %x not ready\n", cpuid);
+ return;
+ }
+ udelay(1000);
+ }
+
+ cp1 = (char *) &cpu->ipc_buffer[1];
+ cp2 = str;
+ while (*cp2) *cp1++ = *cp2++;
+ *(unsigned int *)&cpu->ipc_buffer[0] = cp2 - str; /* hack */
+
+ /* atomic test and set */
+ set_bit(cpuid, &hwrpb->rxrdy);
+
+ for (timeout = 10000; (hwrpb->txrdy & cpumask); timeout--) {
+ if (timeout <= 0) {
+ printk("Processor %x not ready\n", cpuid);
+ return;
+ }
+ udelay(1000);
+ }
+}
+
+/*
+ * setup_smp()
+ *
+ * called from arch/alpha/kernel/setup.c:setup_arch() when __SMP__ defined
+ */
+__initfunc(void setup_smp(void))
+{
+ struct percpu_struct *cpubase, *cpu;
+ int i;
+
+ boot_cpu_id = hard_smp_processor_id();
+ if (boot_cpu_id != 0) {
+ printk("setup_smp: boot_cpu_id != 0 (%d).\n", boot_cpu_id);
+ }
+
+ if (hwrpb->nr_processors > 1) {
+#if 0
+printk("setup_smp: nr_processors 0x%lx\n",
+ hwrpb->nr_processors);
+#endif
+ cpubase = (struct percpu_struct *)
+ ((char*)hwrpb + hwrpb->processor_offset);
+ boot_cpu_palrev = cpubase->pal_revision;
+
+ for (i = 0; i < hwrpb->nr_processors; i++ ) {
+ cpu = (struct percpu_struct *)
+ ((char *)cpubase + i*hwrpb->processor_size);
+ if ((cpu->flags & 0x1cc) == 0x1cc) {
+ smp_num_probed++;
+ /* assume here that "whami" == index */
+ cpu_present_map |= (1 << i);
+ if (i != boot_cpu_id)
+ cpu->pal_revision = boot_cpu_palrev;
+ }
+#if 0
+printk("setup_smp: CPU %d: flags 0x%lx type 0x%lx\n",
+ i, cpu->flags, cpu->type);
+ printk("setup_smp: CPU %d: PAL rev 0x%lx\n",
+ i, cpu->pal_revision);
+#endif
+ }
+ } else {
+ smp_num_probed = 1;
+ cpu_present_map = (1 << boot_cpu_id);
+ }
+ printk("setup_smp: %d CPUs probed, cpu_present_map 0x%x,"
+ " boot_cpu_id %d\n",
+ smp_num_probed, cpu_present_map, boot_cpu_id);
+}
+
+static void
+secondary_console_message(void)
+{
+ int mycpu, i, cnt;
+ unsigned long txrdy = hwrpb->txrdy;
+ char *cp1, *cp2, buf[80];
+ struct percpu_struct *cpu;
+
+ mycpu = hard_smp_processor_id();
+
+#if 0
+printk("secondary_console_message: TXRDY 0x%lx.\n", txrdy);
+#endif
+ for (i = 0; i < NR_CPUS; i++) {
+ if (txrdy & (1L << i)) {
+#if 0
+printk("secondary_console_message: TXRDY contains CPU %d.\n", i);
+#endif
+ cpu = (struct percpu_struct *)
+ ((char*)hwrpb
+ + hwrpb->processor_offset
+ + i * hwrpb->processor_size);
+#if 1
+ printk("secondary_console_message: on %d from %d"
+ " HALT_REASON 0x%lx FLAGS 0x%lx\n",
+ mycpu, i, cpu->halt_reason, cpu->flags);
+#endif
+ cnt = cpu->ipc_buffer[0] >> 32;
+ if (cnt <= 0 || cnt >= 80)
+ strcpy(buf,"<<< BOGUS MSG >>>");
+ else {
+ cp1 = (char *) &cpu->ipc_buffer[11];
+ cp2 = buf;
+ while (cnt--) {
+ if (*cp1 == '\r' || *cp1 == '\n') {
+ *cp2++ = ' '; cp1++;
+ } else
+ *cp2++ = *cp1++;
+ }
+ *cp2 = 0;
+ }
+#if 1
+ printk("secondary_console_message: on %d message is '%s'\n",
+ mycpu, buf);
+#endif
+ }
+ }
+ hwrpb->txrdy = 0;
+ return;
+}
+
+static int
+halt_on_panic(unsigned int this_cpu)
+{
+ halt();
+ return 0;
+}
+
+static int
+local_flush_tlb_all(unsigned int this_cpu)
+{
+ tbia();
+ clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask);
+ mb();
+ return 0;
+}
+
+static int
+local_flush_tlb_mm(unsigned int this_cpu)
+{
+ struct mm_struct * mm = ipi_msg_flush_tb.p.flush_mm;
+ if (mm != current->mm)
+ flush_tlb_other(mm);
+ else
+ flush_tlb_current(mm);
+ clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask);
+ mb();
+ return 0;
+}
+
+static int
+local_flush_tlb_page(unsigned int this_cpu)
+{
+ struct vm_area_struct * vma = ipi_msg_flush_tb.p.flush_vma;
+ struct mm_struct * mm = vma->vm_mm;
+
+ if (mm != current->mm)
+ flush_tlb_other(mm);
+ else
+ flush_tlb_current_page(mm, vma, ipi_msg_flush_tb.flush_addr);
+ clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask);
+ mb();
+ return 0;
+}
+
+static int
+wrapper_local_flush_tlb_page(unsigned int this_cpu)
+{
+#if 0
+ int cpu = smp_processor_id();
+
+ if (cpu) {
+ printk("wrapper: ipi_msg_flush_tb.flush_addr 0x%lx [%d]\n",
+ ipi_msg_flush_tb.flush_addr, atomic_read(&global_irq_count));
+ }
+#endif
+ local_flush_tlb_page(this_cpu);
+ return 0;
+}
+
+static int
+unknown_ipi(unsigned int this_cpu)
+{
+ printk("unknown_ipi() on cpu %d: ", this_cpu);
+ return 1;
+}
+
+enum ipi_message_type {
+ CPU_STOP,
+ TLB_ALL,
+ TLB_MM,
+ TLB_PAGE,
+ TLB_RANGE
+};
+
+static int (* ipi_func[32])(unsigned int) = {
+ halt_on_panic,
+ local_flush_tlb_all,
+ local_flush_tlb_mm,
+ wrapper_local_flush_tlb_page,
+ local_flush_tlb_mm, /* a.k.a. local_flush_tlb_range */
+ unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi,
+ unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi,
+ unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi,
+ unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi,
+ unknown_ipi, unknown_ipi, unknown_ipi
+};
+
+void
+handle_ipi(struct pt_regs *regs)
+{
+ int this_cpu = smp_processor_id();
+ volatile int * pending_ipis = &ipi_bits[this_cpu];
+ int ops;
+
+ mb();
+#if 0
+ printk("handle_ipi: on CPU %d ops 0x%x PC 0x%lx\n",
+ this_cpu, *pending_ipis, regs->pc);
+#endif
+ while ((ops = *pending_ipis)) {
+ int first;
+ for (first = 0; (ops & 1) == 0; ++first, ops >>= 1)
+ ; /* look for the first thing to do */
+ clear_bit(first, pending_ipis);
+ mb();
+ if ((*ipi_func[first])(this_cpu))
+ printk("%d\n", first);
+ mb();
+ }
+ if (hwrpb->txrdy)
+ secondary_console_message();
+}
+
+void
+send_ipi_message(long to_whom, enum ipi_message_type operation)
+{
+ int i;
+ unsigned int j;
+
+ for (i = 0, j = 1; i < NR_CPUS; ++i, j += j) {
+ if ((to_whom & j) == 0)
+ continue;
+ set_bit(operation, &ipi_bits[i]);
+ mb();
+ wripir(i);
+ }
+}
+
+static char smp_buf[256];
+
+char *smp_info(void)
+{
+ sprintf(smp_buf, "CPUs probed %d active %d map 0x%x AKP %d\n",
+ smp_num_probed, smp_num_cpus, cpu_present_map,
+ klock_info.akp);
+
+ return smp_buf;
+}
+
+/* wrapper for call from panic() */
+void
+smp_message_pass(int target, int msg, unsigned long data, int wait)
+{
+ int me = smp_processor_id();
+
+ if (msg != MSG_STOP_CPU)
+ goto barf;
+
+ send_ipi_message(CPU_STOP, cpu_present_map ^ (1 << me));
+ return;
+barf:
+ printk("Yeeee, trying to send SMP msg(%d) on cpu %d\n", msg, me);
+ panic("Bogon SMP message pass.");
+}
+
+void
+flush_tlb_all(void)
+{
+ unsigned int to_whom = cpu_present_map ^ (1 << smp_processor_id());
+ int timeout = 10000;
+
+#if 1
+ if (!kernel_lock_held()) {
+ printk("flush_tlb_all: kernel_flag %d (cpu %d akp %d)!\n",
+ klock_info.kernel_flag, smp_processor_id(), klock_info.akp);
+ }
+#endif
+ ipi_msg_flush_tb.flush_tb_mask = to_whom;
+ send_ipi_message(to_whom, TLB_ALL);
+ tbia();
+
+ while (ipi_msg_flush_tb.flush_tb_mask) {
+ if (--timeout < 0) {
+ printk("flush_tlb_all: STUCK on CPU %d mask 0x%x\n",
+ smp_processor_id(), ipi_msg_flush_tb.flush_tb_mask);
+ ipi_msg_flush_tb.flush_tb_mask = 0;
+ break;
+ }
+ udelay(100);
+ ; /* Wait for all clear from other CPUs. */
+ }
+}
+
+void
+flush_tlb_mm(struct mm_struct *mm)
+{
+ unsigned int to_whom = cpu_present_map ^ (1 << smp_processor_id());
+ int timeout = 10000;
+
+#if 1
+ if (!kernel_lock_held()) {
+ printk("flush_tlb_mm: kernel_flag %d (cpu %d akp %d)!\n",
+ klock_info.kernel_flag, smp_processor_id(), klock_info.akp);
+ }
+#endif
+ ipi_msg_flush_tb.p.flush_mm = mm;
+ ipi_msg_flush_tb.flush_tb_mask = to_whom;
+ send_ipi_message(to_whom, TLB_MM);
+
+ if (mm != current->mm)
+ flush_tlb_other(mm);
+ else
+ flush_tlb_current(mm);
+
+ while (ipi_msg_flush_tb.flush_tb_mask) {
+ if (--timeout < 0) {
+ printk("flush_tlb_mm: STUCK on CPU %d mask 0x%x\n",
+ smp_processor_id(), ipi_msg_flush_tb.flush_tb_mask);
+ ipi_msg_flush_tb.flush_tb_mask = 0;
+ break;
+ }
+ udelay(100);
+ ; /* Wait for all clear from other CPUs. */
+ }
+}
+
+void
+flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
+{
+ int cpu = smp_processor_id();
+ unsigned int to_whom = cpu_present_map ^ (1 << cpu);
+ struct mm_struct * mm = vma->vm_mm;
+ int timeout = 10000;
+
+#if 1
+ if (!kernel_lock_held()) {
+ printk("flush_tlb_page: kernel_flag %d (cpu %d akp %d)!\n",
+ klock_info.kernel_flag, cpu, klock_info.akp);
+ }
+#endif
+ ipi_msg_flush_tb.p.flush_vma = vma;
+ ipi_msg_flush_tb.flush_addr = addr;
+ ipi_msg_flush_tb.flush_tb_mask = to_whom;
+ send_ipi_message(to_whom, TLB_PAGE);
+
+ if (mm != current->mm)
+ flush_tlb_other(mm);
+ else
+ flush_tlb_current_page(mm, vma, addr);
+
+ while (ipi_msg_flush_tb.flush_tb_mask) {
+ if (--timeout < 0) {
+ printk("flush_tlb_page: STUCK on CPU %d [0x%x,0x%lx,%d,%d]\n",
+ cpu, ipi_msg_flush_tb.flush_tb_mask, addr,
+ klock_info.akp, global_irq_holder);
+ ipi_msg_flush_tb.flush_tb_mask = 0;
+ break;
+ }
+ udelay(100);
+ ; /* Wait for all clear from other CPUs. */
+ }
+}
+
+void
+flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+#if 0
+ flush_tlb_mm(mm);
+#else
+ unsigned int to_whom;
+ int timeout;
+ unsigned long where;
+
+ __asm__("mov $26, %0" : "=r" (where));
+
+ timeout = 10000;
+ to_whom = cpu_present_map ^ (1 << smp_processor_id());
+
+#if 1
+ if (!kernel_lock_held()) {
+ printk("flush_tlb_range: kernel_flag %d (cpu %d akp %d) @ 0x%lx\n",
+ klock_info.kernel_flag, smp_processor_id(), klock_info.akp,
+ where);
+ }
+#endif
+ ipi_msg_flush_tb.p.flush_mm = mm;
+ ipi_msg_flush_tb.flush_tb_mask = to_whom;
+ send_ipi_message(to_whom, TLB_MM);
+
+ if (mm != current->mm)
+ flush_tlb_other(mm);
+ else
+ flush_tlb_current(mm);
+
+ while (ipi_msg_flush_tb.flush_tb_mask) {
+ if (--timeout < 0) {
+ printk("flush_tlb_range: STUCK on CPU %d mask 0x%x\n",
+ smp_processor_id(), ipi_msg_flush_tb.flush_tb_mask);
+ ipi_msg_flush_tb.flush_tb_mask = 0;
+ break;
+ }
+ udelay(100);
+ ; /* Wait for all clear from other CPUs. */
+ }
+#endif
+}
+
+#ifdef DEBUG_KERNEL_LOCK
+void ___lock_kernel(klock_info_t *klip, int cpu, long ipl)
+{
+ long regx;
+ int stuck_lock;
+ unsigned long inline_pc;
+
+ __asm__("mov $26, %0" : "=r" (inline_pc));
+
+ try_again:
+
+ stuck_lock = 1<<26;
+
+ __asm__ __volatile__(
+ "1: ldl_l %1,%0;"
+ " blbs %1,6f;"
+ " or %1,1,%1;"
+ " stl_c %1,%0;"
+ " beq %1,6f;"
+ "4: mb\n"
+ ".section .text2,\"ax\"\n"
+ "6: mov %5,$16;"
+ " call_pal %4;"
+ "7: ldl %1,%0;"
+ " blt %2,4b # debug\n"
+ " subl %2,1,%2 # debug\n"
+ " blbs %1,7b;"
+ " bis $31,7,$16;"
+ " call_pal %4;"
+ " br 1b\n"
+ ".previous"
+ : "=m,=m" (__dummy_lock(klip)), "=&r,=&r" (regx),
+ "=&r,=&r" (stuck_lock)
+ : "0,0" (__dummy_lock(klip)), "i,i" (PAL_swpipl),
+ "i,r" (ipl), "2,2" (stuck_lock)
+ : "$0", "$1", "$16", "$22", "$23", "$24", "$25", "memory");
+
+ if (stuck_lock < 0) {
+ printk("___kernel_lock stuck at %lx(%d) held %lx(%d)\n",
+ inline_pc, cpu, klip->pc, klip->cpu);
+ goto try_again;
+ } else {
+ klip->pc = inline_pc;
+ klip->cpu = cpu;
+ }
+}
+#endif
+
+#ifdef DEBUG_SPINLOCK
+void spin_lock(spinlock_t * lock)
+{
+ long tmp;
+ long stuck;
+ unsigned long inline_pc;
+
+ __asm__("mov $26, %0" : "=r" (inline_pc));
+
+ try_again:
+
+ stuck = 0x10000000; /* was 4G, now 256M */
+
+ /* Use sub-sections to put the actual loop at the end
+ of this object file's text section so as to perfect
+ branch prediction. */
+ __asm__ __volatile__(
+ "1: ldq_l %0,%1\n"
+ " subq %2,1,%2\n"
+ " blbs %0,2f\n"
+ " or %0,1,%0\n"
+ " stq_c %0,%1\n"
+ " beq %0,3f\n"
+ "4: mb\n"
+ ".section .text2,\"ax\"\n"
+ "2: ldq %0,%1\n"
+ " subq %2,1,%2\n"
+ "3: blt %2,4b\n"
+ " blbs %0,2b\n"
+ " br 1b\n"
+ ".previous"
+ : "=r" (tmp),
+ "=m" (__dummy_lock(lock)),
+ "=r" (stuck)
+ : "2" (stuck));
+
+ if (stuck < 0) {
+ printk("spinlock stuck at %lx (cur=%lx, own=%lx)\n",
+ inline_pc,
+#if 0
+ lock->previous, lock->task
+#else
+ (unsigned long) current, lock->task
+#endif
+ );
+ goto try_again;
+ } else {
+ lock->previous = (unsigned long) inline_pc;
+ lock->task = (unsigned long) current;
+ }
+}
+#endif /* DEBUG_SPINLOCK */
+
+#ifdef DEBUG_RWLOCK
+void write_lock(rwlock_t * lock)
+{
+ long regx, regy;
+ int stuck_lock, stuck_reader;
+ unsigned long inline_pc;
+
+ __asm__("mov $26, %0" : "=r" (inline_pc));
+
+ try_again:
+
+ stuck_lock = 1<<26;
+ stuck_reader = 1<<26;
+
+ __asm__ __volatile__(
+ "1: ldl_l %1,%0;"
+ " blbs %1,6f;"
+ " or %1,1,%2;"
+ " stl_c %2,%0;"
+ " beq %2,6f;"
+ " blt %1,8f;"
+ "4: mb\n"
+ ".section .text2,\"ax\"\n"
+ "6: ldl %1,%0;"
+ " blt %3,4b # debug\n"
+ " subl %3,1,%3 # debug\n"
+ " blbs %1,6b;"
+ " br 1b;"
+ "8: ldl %1,%0;"
+ " blt %4,4b # debug\n"
+ " subl %4,1,%4 # debug\n"
+ " blt %1,8b;"
+ "9: br 4b\n"
+ ".previous"
+ : "=m" (__dummy_lock(lock)), "=&r" (regx), "=&r" (regy)
+ , "=&r" (stuck_lock), "=&r" (stuck_reader)
+ : "0" (__dummy_lock(lock))
+ , "3" (stuck_lock), "4" (stuck_reader)
+ );
+
+ if (stuck_lock < 0) {
+ printk("write_lock stuck at %lx\n", inline_pc);
+ goto try_again;
+ }
+ if (stuck_reader < 0) {
+ printk("write_lock stuck on readers at %lx\n", inline_pc);
+ goto try_again;
+ }
+}
+
+void _read_lock(rwlock_t * lock)
+{
+ long regx;
+ int stuck_lock;
+ unsigned long inline_pc;
+
+ __asm__("mov $26, %0" : "=r" (inline_pc));
+
+ try_again:
+
+ stuck_lock = 1<<26;
+
+ __asm__ __volatile__(
+ "1: ldl_l %1,%0;"
+ " blbs %1,6f;"
+ " subl %1,2,%1;"
+ " stl_c %1,%0;"
+ " beq %1,6f;"
+ "4: mb\n"
+ ".section .text2,\"ax\"\n"
+ "6: ldl %1,%0;"
+ " blt %2,4b # debug\n"
+ " subl %2,1,%2 # debug\n"
+ " blbs %1,6b;"
+ " br 1b\n"
+ ".previous"
+ : "=m" (__dummy_lock(lock)), "=&r" (regx), "=&r" (stuck_lock)
+ : "0" (__dummy_lock(lock)), "2" (stuck_lock)
+ );
+
+ if (stuck_lock < 0) {
+ printk("_read_lock stuck at %lx\n", inline_pc);
+ goto try_again;
+ }
+}
+#endif /* DEBUG_RWLOCK */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov