📄 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 <linux/fs.h>#include <linux/seq_file.h>#include <linux/cache.h>#include <linux/timer.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[];cpuinfo_sparc cpu_data[NR_CPUS];volatile int __cpu_number_map[NR_CPUS] __attribute__ ((aligned (SMP_CACHE_BYTES)));volatile int __cpu_logical_map[NR_CPUS] __attribute__ ((aligned (SMP_CACHE_BYTES)));/* Please don't make this stuff initdata!!! --DaveM */static unsigned char boot_cpu_id;static int smp_activated;/* Kernel spinlock */spinlock_t kernel_flag __cacheline_aligned_in_smp = 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 */}static int max_cpus = NR_CPUS;static int __init maxcpus(char *str){ get_option(&str, &max_cpus); return 1;}__setup("maxcpus=", maxcpus);void smp_info(struct seq_file *m){ int i; seq_printf(m, "State:\n"); for (i = 0; i < NR_CPUS; i++) { if (cpu_present_map & (1UL << i)) seq_printf(m, "CPU%d:\t\tonline\n", i); }}void smp_bogo(struct seq_file *m){ int i; for (i = 0; i < NR_CPUS; i++) if (cpu_present_map & (1UL << i)) seq_printf(m, "Cpu%dBogo\t: %lu.%02lu\n" "Cpu%dClkTck\t: %016lx\n", i, cpu_data[i].udelay_val / (500000/HZ), (cpu_data[i].udelay_val / (5000/HZ)) % 100, i, cpu_data[i].clock_tick);}void __init smp_store_cpu_info(int id){ int i, no; /* multiplier and counter set by smp_setup_percpu_timer() */ cpu_data[id].udelay_val = loops_per_jiffy; for (no = 0; no < linux_num_cpus; no++) if (linux_cpus[no].mid == id) break; cpu_data[id].clock_tick = prom_getintdefault(linux_cpus[no].prom_node, "clock-frequency", 0); 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 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(); extern int bigkernel; extern unsigned long kern_locked_tte_data; if (bigkernel) { prom_dtlb_load(sparc64_highest_locked_tlbent()-1, kern_locked_tte_data + 0x400000, KERNBASE + 0x400000); prom_itlb_load(sparc64_highest_locked_tlbent()-1, kern_locked_tte_data + 0x400000, KERNBASE + 0x400000); } inherit_locked_prom_mappings(0); __flush_cache_all(); __flush_tlb_all(); cpu_probe(); 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_threads_ready) membar("#LoadLoad");}extern int cpu_idle(void);extern void init_IRQ(void);int start_secondary(void *unused){ trap_init(); init_IRQ(); return cpu_idle();}void cpu_panic(void){ printk("CPU[%d]: Returns from cpu_idle!\n", smp_processor_id()); panic("SMP bolixed\n");}static unsigned long current_tick_offset;/* This tick register synchronization scheme is taken entirely from * the ia64 port, see arch/ia64/kernel/smpboot.c for details and credit. * * The only change I've made is to rework it so that the master * initiates the synchonization instead of the slave. -DaveM */#define MASTER 0#define SLAVE (SMP_CACHE_BYTES/sizeof(unsigned long))#define NUM_ROUNDS 64 /* magic value */#define NUM_ITERS 5 /* likewise */static spinlock_t itc_sync_lock = SPIN_LOCK_UNLOCKED;static unsigned long go[SLAVE + 1];#define DEBUG_TICK_SYNC 0static inline long get_delta (long *rt, long *master){ unsigned long best_t0 = 0, best_t1 = ~0UL, best_tm = 0; unsigned long tcenter, t0, t1, tm; unsigned long i; for (i = 0; i < NUM_ITERS; i++) { t0 = tick_ops->get_tick(); go[MASTER] = 1; membar("#StoreLoad"); while (!(tm = go[SLAVE])) membar("#LoadLoad"); go[SLAVE] = 0; membar("#StoreStore"); t1 = tick_ops->get_tick(); if (t1 - t0 < best_t1 - best_t0) best_t0 = t0, best_t1 = t1, best_tm = tm; } *rt = best_t1 - best_t0; *master = best_tm - best_t0; /* average best_t0 and best_t1 without overflow: */ tcenter = (best_t0/2 + best_t1/2); if (best_t0 % 2 + best_t1 % 2 == 2) tcenter++; return tcenter - best_tm;}void smp_synchronize_tick_client(void){ long i, delta, adj, adjust_latency = 0, done = 0; unsigned long flags, rt, master_time_stamp, bound;#if DEBUG_TICK_SYNC struct { long rt; /* roundtrip time */ long master; /* master's timestamp */ long diff; /* difference between midpoint and master's timestamp */ long lat; /* estimate of itc adjustment latency */ } t[NUM_ROUNDS];#endif go[MASTER] = 1; while (go[MASTER]) membar("#LoadLoad"); local_irq_save(flags); { for (i = 0; i < NUM_ROUNDS; i++) { delta = get_delta(&rt, &master_time_stamp); if (delta == 0) { done = 1; /* let's lock on to this... */ bound = rt; } if (!done) { if (i > 0) { adjust_latency += -delta; adj = -delta + adjust_latency/4; } else adj = -delta; tick_ops->add_tick(adj, current_tick_offset); }#if DEBUG_TICK_SYNC t[i].rt = rt; t[i].master = master_time_stamp; t[i].diff = delta; t[i].lat = adjust_latency/4;#endif } } local_irq_restore(flags);#if DEBUG_TICK_SYNC for (i = 0; i < NUM_ROUNDS; i++) printk("rt=%5ld master=%5ld diff=%5ld adjlat=%5ld\n", t[i].rt, t[i].master, t[i].diff, t[i].lat);#endif printk(KERN_INFO "CPU %d: synchronized TICK with master CPU (last diff %ld cycles," "maxerr %lu cycles)\n", smp_processor_id(), delta, rt);}static void smp_start_sync_tick_client(int cpu);static void smp_synchronize_one_tick(int cpu){ unsigned long flags, i; go[MASTER] = 0; smp_start_sync_tick_client(cpu); /* wait for client to be ready */ while (!go[MASTER]) membar("#LoadLoad"); /* now let the client proceed into his loop */ go[MASTER] = 0; membar("#StoreLoad"); spin_lock_irqsave(&itc_sync_lock, flags); { for (i = 0; i < NUM_ROUNDS*NUM_ITERS; i++) { while (!go[MASTER]) membar("#LoadLoad"); go[MASTER] = 0; membar("#StoreStore"); go[SLAVE] = tick_ops->get_tick(); membar("#StoreLoad"); } } spin_unlock_irqrestore(&itc_sync_lock, flags);}static void smp_synchronize_tick(void){ int cpu = smp_processor_id(); int i; for (i = 0; i < NR_CPUS; i++) { if (cpu_present_map & (1UL << i)) { if (i == cpu) continue; smp_synchronize_one_tick(i); } }}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); init_idle(); if (linux_num_cpus == 1) return; for (i = 0; i < NR_CPUS; i++) { if (i == boot_cpu_id) continue; if ((cpucount + 1) == max_cpus) goto ignorecpu; 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->cpus_runnable = 1UL << i; /* 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");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -