📄 smp.c
字号:
/* smp.c: Sparc64 SMP support. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) */#include <linux/kernel.h>#include <linux/sched.h>#include <linux/mm.h>#include <linux/pagemap.h>#include <linux/threads.h>#include <linux/smp.h>#include <linux/smp_lock.h>#include <linux/interrupt.h>#include <linux/kernel_stat.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/spinlock.h>#include <asm/head.h>#include <asm/ptrace.h>#include <asm/atomic.h>#include <asm/irq.h>#include <asm/page.h>#include <asm/pgtable.h>#include <asm/oplib.h>#include <asm/hardirq.h>#include <asm/softirq.h>#include <asm/uaccess.h>#include <asm/timer.h>#include <asm/starfire.h>#define __KERNEL_SYSCALLS__#include <linux/unistd.h>extern int linux_num_cpus;extern void calibrate_delay(void);extern unsigned prom_cpu_nodes[];struct cpuinfo_sparc cpu_data[NR_CPUS] __attribute__ ((aligned (64)));volatile int __cpu_number_map[NR_CPUS] __attribute__ ((aligned (64)));volatile int __cpu_logical_map[NR_CPUS] __attribute__ ((aligned (64)));/* Please don't make this stuff initdata!!! --DaveM */static unsigned char boot_cpu_id = 0;static int smp_activated = 0;/* Kernel spinlock */spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED;volatile int smp_processors_ready = 0;unsigned long cpu_present_map = 0;int smp_num_cpus = 1;int smp_threads_ready = 0;void __init smp_setup(char *str, int *ints){ /* XXX implement me XXX */}int smp_info(char *buf){ int len = 7, i; strcpy(buf, "State:\n"); for (i = 0; i < NR_CPUS; i++) if(cpu_present_map & (1UL << i)) len += sprintf(buf + len, "CPU%d:\t\tonline\n", i); return len;}int smp_bogo(char *buf){ int len = 0, i; for (i = 0; i < NR_CPUS; i++) if(cpu_present_map & (1UL << i)) len += sprintf(buf + len, "Cpu%dBogo\t: %lu.%02lu\n", i, cpu_data[i].udelay_val / (500000/HZ), (cpu_data[i].udelay_val / (5000/HZ)) % 100); return len;}void __init smp_store_cpu_info(int id){ int i; /* multiplier and counter set by smp_setup_percpu_timer() */ cpu_data[id].udelay_val = loops_per_jiffy; cpu_data[id].pgcache_size = 0; cpu_data[id].pte_cache[0] = NULL; cpu_data[id].pte_cache[1] = NULL; cpu_data[id].pgdcache_size = 0; cpu_data[id].pgd_cache = NULL; cpu_data[id].idle_volume = 1; for(i = 0; i < 16; i++) cpu_data[id].irq_worklists[i] = 0;}void __init smp_commence(void){}static void smp_setup_percpu_timer(void);static void smp_tune_scheduling(void);static volatile unsigned long callin_flag = 0;extern void inherit_locked_prom_mappings(int save_p);extern void cpu_probe(void);void __init smp_callin(void){ int cpuid = hard_smp_processor_id(); unsigned long pstate; inherit_locked_prom_mappings(0); __flush_cache_all(); __flush_tlb_all(); cpu_probe(); /* Guarentee that the following sequences execute * uninterrupted. */ __asm__ __volatile__("rdpr %%pstate, %0\n\t" "wrpr %0, %1, %%pstate" : "=r" (pstate) : "i" (PSTATE_IE)); /* Set things up so user can access tick register for profiling * purposes. Also workaround BB_ERRATA_1 by doing a dummy * read back of %tick after writing it. */ __asm__ __volatile__(" sethi %%hi(0x80000000), %%g1 ba,pt %%xcc, 1f sllx %%g1, 32, %%g1 .align 641: rd %%tick, %%g2 add %%g2, 6, %%g2 andn %%g2, %%g1, %%g2 wrpr %%g2, 0, %%tick rdpr %%tick, %%g0" : /* no outputs */ : /* no inputs */ : "g1", "g2"); /* Restore PSTATE_IE. */ __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : /* no outputs */ : "r" (pstate)); smp_setup_percpu_timer(); __sti(); calibrate_delay(); smp_store_cpu_info(cpuid); callin_flag = 1; __asm__ __volatile__("membar #Sync\n\t" "flush %%g6" : : : "memory"); /* Clear this or we will die instantly when we * schedule back to this idler... */ current->thread.flags &= ~(SPARC_FLAG_NEWCHILD); /* Attach to the address space of init_task. */ atomic_inc(&init_mm.mm_count); current->active_mm = &init_mm; while(!smp_processors_ready) membar("#LoadLoad");}extern int cpu_idle(void);extern void init_IRQ(void);void initialize_secondary(void){}int start_secondary(void *unused){ trap_init(); init_IRQ(); smp_callin(); return cpu_idle();}void cpu_panic(void){ printk("CPU[%d]: Returns from cpu_idle!\n", smp_processor_id()); panic("SMP bolixed\n");}extern struct prom_cpuinfo linux_cpus[64];extern unsigned long sparc64_cpu_startup;/* The OBP cpu startup callback truncates the 3rd arg cookie to * 32-bits (I think) so to be safe we have it read the pointer * contained here so we work on >4GB machines. -DaveM */static struct task_struct *cpu_new_task = NULL;void __init smp_boot_cpus(void){ int cpucount = 0, i; printk("Entering UltraSMPenguin Mode...\n"); __sti(); smp_store_cpu_info(boot_cpu_id); smp_tune_scheduling(); init_idle(); if(linux_num_cpus == 1) return; for(i = 0; i < NR_CPUS; i++) { if(i == boot_cpu_id) continue; if(cpu_present_map & (1UL << i)) { unsigned long entry = (unsigned long)(&sparc64_cpu_startup); unsigned long cookie = (unsigned long)(&cpu_new_task); struct task_struct *p; int timeout; int no; prom_printf("Starting CPU %d... ", i); kernel_thread(start_secondary, NULL, CLONE_PID); cpucount++; p = init_task.prev_task; init_tasks[cpucount] = p; p->processor = i; p->has_cpu = 1; /* we schedule the first task manually */ del_from_runqueue(p); unhash_process(p); callin_flag = 0; for (no = 0; no < linux_num_cpus; no++) if (linux_cpus[no].mid == i) break; cpu_new_task = p; prom_startcpu(linux_cpus[no].prom_node, entry, cookie); for(timeout = 0; timeout < 5000000; timeout++) { if(callin_flag) break; udelay(100); } if(callin_flag) { __cpu_number_map[i] = cpucount; __cpu_logical_map[cpucount] = i; prom_cpu_nodes[i] = linux_cpus[no].prom_node; prom_printf("OK\n"); } else { cpucount--; printk("Processor %d is stuck.\n", i); prom_printf("FAILED\n"); } } if(!callin_flag) { cpu_present_map &= ~(1UL << i); __cpu_number_map[i] = -1; } } cpu_new_task = NULL; if(cpucount == 0) { printk("Error: only one processor found.\n"); cpu_present_map = (1UL << smp_processor_id()); } else { unsigned long bogosum = 0; for(i = 0; i < NR_CPUS; i++) { if(cpu_present_map & (1UL << i)) bogosum += cpu_data[i].udelay_val; } printk("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; } smp_processors_ready = 1; membar("#StoreStore | #StoreLoad");}/* #define XCALL_DEBUG */static inline void xcall_deliver(u64 data0, u64 data1, u64 data2, u64 pstate, unsigned long cpu){ u64 result, target; int stuck, tmp; if (this_is_starfire) { /* map to real upaid */ cpu = (((cpu & 0x3c) << 1) | ((cpu & 0x40) >> 4) | (cpu & 0x3)); } target = (cpu << 14) | 0x70;#ifdef XCALL_DEBUG printk("CPU[%d]: xcall(data[%016lx:%016lx:%016lx],tgt[%016lx])\n", smp_processor_id(), data0, data1, data2, target);#endifagain: /* Ok, this is the real Spitfire Errata #54. * One must read back from a UDB internal register * after writes to the UDB interrupt dispatch, but * before the membar Sync for that write. * So we use the high UDB control register (ASI 0x7f, * ADDR 0x20) for the dummy read. -DaveM */ tmp = 0x40; __asm__ __volatile__(" wrpr %1, %2, %%pstate stxa %4, [%0] %3 stxa %5, [%0+%8] %3 add %0, %8, %0 stxa %6, [%0+%8] %3 membar #Sync stxa %%g0, [%7] %3 membar #Sync mov 0x20, %%g1 ldxa [%%g1] 0x7f, %%g0 membar #Sync" : "=r" (tmp) : "r" (pstate), "i" (PSTATE_IE), "i" (ASI_UDB_INTR_W), "r" (data0), "r" (data1), "r" (data2), "r" (target), "r" (0x10), "0" (tmp) : "g1"); /* NOTE: PSTATE_IE is still clear. */ stuck = 100000; do { __asm__ __volatile__("ldxa [%%g0] %1, %0" : "=r" (result) : "i" (ASI_INTR_DISPATCH_STAT)); if(result == 0) { __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate)); return; } stuck -= 1; if(stuck == 0) break; } while(result & 0x1); __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate)); if(stuck == 0) {#ifdef XCALL_DEBUG printk("CPU[%d]: mondo stuckage result[%016lx]\n", smp_processor_id(), result);#endif } else {#ifdef XCALL_DEBUG printk("CPU[%d]: Penguin %d NACK's master.\n", smp_processor_id(), cpu);#endif udelay(2); goto again; }}void smp_cross_call(unsigned long *func, u32 ctx, u64 data1, u64 data2){ if(smp_processors_ready) { unsigned long mask = (cpu_present_map & ~(1UL<<smp_processor_id())); u64 pstate, data0 = (((u64)ctx)<<32 | (((u64)func) & 0xffffffff)); int i, ncpus = smp_num_cpus - 1; __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); for(i = 0; i < NR_CPUS; i++) { if(mask & (1UL << i)) { xcall_deliver(data0, data1, data2, pstate, i); ncpus--; } if (!ncpus) break; } /* NOTE: Caller runs local copy on master. */ }}struct call_data_struct { void (*func) (void *info); void *info; atomic_t finished; int wait;};extern unsigned long xcall_call_function;int smp_call_function(void (*func)(void *info), void *info, int nonatomic, int wait){ struct call_data_struct data; int cpus = smp_num_cpus - 1; if (!cpus) return 0; data.func = func; data.info = info; atomic_set(&data.finished, 0); data.wait = wait; smp_cross_call(&xcall_call_function, 0, (u64) &data, 0); if (wait) { while (atomic_read(&data.finished) != cpus) barrier(); } return 0;}void smp_call_function_client(struct call_data_struct *call_data){ call_data->func(call_data->info); if (call_data->wait) atomic_inc(&call_data->finished);}extern unsigned long xcall_flush_tlb_page;extern unsigned long xcall_flush_tlb_mm;extern unsigned long xcall_flush_tlb_range;extern unsigned long xcall_flush_tlb_all;extern unsigned long xcall_tlbcachesync;extern unsigned long xcall_flush_cache_all;extern unsigned long xcall_report_regs;extern unsigned long xcall_receive_signal;void smp_receive_signal(int cpu){ if(smp_processors_ready && (cpu_present_map & (1UL<<cpu)) != 0) { u64 pstate, data0 = (((u64)&xcall_receive_signal) & 0xffffffff); __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); xcall_deliver(data0, 0, 0, pstate, cpu); }}void smp_report_regs(void){ smp_cross_call(&xcall_report_regs, 0, 0, 0);}void smp_flush_cache_all(void){ smp_cross_call(&xcall_flush_cache_all, 0, 0, 0); __flush_cache_all();}void smp_flush_tlb_all(void){ smp_cross_call(&xcall_flush_tlb_all, 0, 0, 0); __flush_tlb_all();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -