diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 13:25:08 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 13:25:08 +0000 |
commit | 59223edaa18759982db0a8aced0e77457d10c68e (patch) | |
tree | 89354903b01fa0a447bffeefe00df3044495db2e /arch/alpha | |
parent | db7d4daea91e105e3859cf461d7e53b9b77454b2 (diff) |
Merge with Linux 2.3.6. Sorry, this isn't tested on silicon, I don't
have a MIPS box at hand.
Diffstat (limited to 'arch/alpha')
-rw-r--r-- | arch/alpha/kernel/alpha_ksyms.c | 1 | ||||
-rw-r--r-- | arch/alpha/kernel/fpreg.c | 6 | ||||
-rw-r--r-- | arch/alpha/kernel/head.S | 36 | ||||
-rw-r--r-- | arch/alpha/kernel/irq.c | 10 | ||||
-rw-r--r-- | arch/alpha/kernel/process.c | 49 | ||||
-rw-r--r-- | arch/alpha/kernel/proto.h | 4 | ||||
-rw-r--r-- | arch/alpha/kernel/ptrace.c | 23 | ||||
-rw-r--r-- | arch/alpha/kernel/signal.c | 3 | ||||
-rw-r--r-- | arch/alpha/kernel/smp.c | 1239 | ||||
-rw-r--r-- | arch/alpha/kernel/time.c | 90 | ||||
-rw-r--r-- | arch/alpha/kernel/traps.c | 21 | ||||
-rw-r--r-- | arch/alpha/lib/memcpy.c | 88 | ||||
-rw-r--r-- | arch/alpha/mm/init.c | 20 |
13 files changed, 861 insertions, 729 deletions
diff --git a/arch/alpha/kernel/alpha_ksyms.c b/arch/alpha/kernel/alpha_ksyms.c index 65975c168..ce949d722 100644 --- a/arch/alpha/kernel/alpha_ksyms.c +++ b/arch/alpha/kernel/alpha_ksyms.c @@ -52,6 +52,7 @@ EXPORT_SYMBOL(local_bh_count); EXPORT_SYMBOL(local_irq_count); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); +EXPORT_SYMBOL(disable_irq_nosync); EXPORT_SYMBOL(screen_info); EXPORT_SYMBOL(perf_irq); diff --git a/arch/alpha/kernel/fpreg.c b/arch/alpha/kernel/fpreg.c index 6da94c0cb..0bd8b9f67 100644 --- a/arch/alpha/kernel/fpreg.c +++ b/arch/alpha/kernel/fpreg.c @@ -1,10 +1,10 @@ /* - * kernel/fpreg.c + * arch/alpha/kernel/fpreg.c * * (C) Copyright 1998 Linus Torvalds */ -#ifdef __alpha_cix__ +#if defined(__alpha_cix__) || defined(__alpha_fix__) #define STT(reg,val) asm volatile ("ftoit $f"#reg",%0" : "=r"(val)); #else #define STT(reg,val) asm volatile ("stt $f"#reg",%0" : "=m"(val)); @@ -52,7 +52,7 @@ alpha_read_fp_reg (unsigned long reg) return val; } -#ifdef __alpha_cix__ +#if defined(__alpha_cix__) || defined(__alpha_fix__) #define LDT(reg,val) asm volatile ("itoft %0,$f"#reg : : "r"(val)); #else #define LDT(reg,val) asm volatile ("ldt $f"#reg",%0" : : "m"(val)); diff --git a/arch/alpha/kernel/head.S b/arch/alpha/kernel/head.S index 8ba50462b..3fcbdbcda 100644 --- a/arch/alpha/kernel/head.S +++ b/arch/alpha/kernel/head.S @@ -32,24 +32,26 @@ __start: #ifdef __SMP__ .align 3 - .globl __start_cpu - .ent __start_cpu - /* On entry here from SRM console, the HWPCB of this processor - has been loaded, and $27 contains the task pointer */ -__start_cpu: - .prologue 0 - /* First order of business, load the GP */ - br $26,1f -1: ldgp $29,0($26) - /* We need to get current loaded up with our first task... */ - mov $27,$8 - /* Set FEN */ - lda $16,1($31) - call_pal PAL_wrfen - /* ... and then we can start the processor. */ - jsr $26,start_secondary + .globl __smp_callin + .ent __smp_callin + /* On entry here from SRM console, the HWPCB of the per-cpu + slot for this processor has been loaded. We've arranged + for the UNIQUE value for this process to contain the PCBB + of the target idle task. */ +__smp_callin: + .prologue 1 + ldgp $29,0($27) # First order of business, load the GP. + + call_pal PAL_rduniq # Grab the target PCBB. + mov $0,$16 # Install it. + call_pal PAL_swpctx + + lda $8,0x3fff # Find "current". + bic $30,$8,$8 + + jsr $26,smp_callin call_pal PAL_halt - .end __start_cpu + .end __smp_callin #endif /* __SMP__ */ .align 3 diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c index e1a53ab57..d53f7ffea 100644 --- a/arch/alpha/kernel/irq.c +++ b/arch/alpha/kernel/irq.c @@ -192,7 +192,7 @@ unmask_irq(unsigned long irq) } void -disable_irq(unsigned int irq_nr) +disable_irq_nosync(unsigned int irq_nr) { unsigned long flags; @@ -202,6 +202,14 @@ disable_irq(unsigned int irq_nr) } void +disable_irq(unsigned int irq_nr) +{ + /* This works non-SMP, and SMP until we write code to distribute + interrupts to more that cpu 0. */ + disable_irq_nosync(irq_nr); +} + +void enable_irq(unsigned int irq_nr) { unsigned long flags; diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index 93d8db602..1993ed3b4 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -58,10 +58,10 @@ static struct fs_struct init_fs = INIT_FS; static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; -struct mm_struct init_mm = INIT_MM; +struct mm_struct init_mm = INIT_MM(init_mm); union task_union init_task_union __attribute__((section("init_task"))) - = { task: INIT_TASK }; + = { task: INIT_TASK(init_task_union.task) }; /* * No need to acquire the kernel lock, we're entirely local.. @@ -75,33 +75,46 @@ sys_sethae(unsigned long hae, unsigned long a1, unsigned long a2, return 0; } -static void __attribute__((noreturn)) -do_cpu_idle(void) +#ifdef __SMP__ +void +cpu_idle(void *unused) { /* An endless idle loop with no priority at all. */ current->priority = 0; + current->counter = -100; + while (1) { - check_pgt_cache(); - run_task_queue(&tq_scheduler); - current->counter = 0; - schedule(); + /* FIXME -- EV6 and LCA45 know how to power down + the CPU. */ + + /* Although we are an idle CPU, we do not want to + get into the scheduler unnecessarily. */ + if (current->need_resched) { + schedule(); + check_pgt_cache(); + } } } - -#ifdef __SMP__ -void -cpu_idle(void *unused) -{ - do_cpu_idle(); -} #endif asmlinkage int sys_idle(void) { - if (current->pid == 0) - do_cpu_idle(); - return -EPERM; + if (current->pid != 0) + return -EPERM; + + /* An endless idle loop with no priority at all. */ + current->priority = 0; + current->counter = -100; + init_idle(); + + while (1) { + /* FIXME -- EV6 and LCA45 know how to power down + the CPU. */ + + schedule(); + check_pgt_cache(); + } } void diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h index 8a0efe52d..f7e54a982 100644 --- a/arch/alpha/kernel/proto.h +++ b/arch/alpha/kernel/proto.h @@ -151,6 +151,8 @@ extern unsigned long srm_hae; extern void setup_smp(void); extern int smp_info(char *buffer); extern void handle_ipi(struct pt_regs *); +extern void smp_percpu_timer_interrupt(struct pt_regs *); +extern int smp_boot_cpuid; /* bios32.c */ extern void reset_for_srm(void); @@ -178,7 +180,7 @@ extern unsigned long alpha_read_fp_reg (unsigned long reg); extern void wrmces(unsigned long mces); extern void cserve_ena(unsigned long); extern void cserve_dis(unsigned long); -extern void __start_cpu(unsigned long); +extern void __smp_callin(void); /* entry.S */ extern void entArith(void); diff --git a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c index 18c9a8b13..3e20e7091 100644 --- a/arch/alpha/kernel/ptrace.c +++ b/arch/alpha/kernel/ptrace.c @@ -246,26 +246,6 @@ put_long(struct task_struct * tsk, struct vm_area_struct * vma, flush_tlb(); } -static struct vm_area_struct * -find_extend_vma(struct task_struct * tsk, unsigned long addr) -{ - struct vm_area_struct * vma; - - addr &= PAGE_MASK; - vma = find_vma(tsk->mm,addr); - if (!vma) - return NULL; - if (vma->vm_start <= addr) - return vma; - if (!(vma->vm_flags & VM_GROWSDOWN)) - return NULL; - if (vma->vm_end - addr > tsk->rlim[RLIMIT_STACK].rlim_cur) - return NULL; - vma->vm_offset -= vma->vm_start - addr; - vma->vm_start = addr; - return vma; -} - /* * This routine checks the page boundaries, and that the offset is * within the task area. It then calls get_long() to read a long. @@ -506,7 +486,8 @@ sys_ptrace(long request, long pid, long addr, long data, (current->uid != child->uid) || (current->gid != child->egid) || (current->gid != child->sgid) || - (current->gid != child->gid)) + (current->gid != child->gid) || + (!cap_issubset(child->cap_permitted, current->cap_permitted))) && !capable(CAP_SYS_PTRACE)) goto out; /* the same process cannot be attached many times */ diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c index 99bd36785..a477c2344 100644 --- a/arch/alpha/kernel/signal.c +++ b/arch/alpha/kernel/signal.c @@ -24,6 +24,9 @@ #include <asm/sigcontext.h> #include <asm/ucontext.h> +#include "proto.h" + + #define DEBUG_SIG 0 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index aa1eaf363..f01c0e55d 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c @@ -18,6 +18,7 @@ #include <asm/ptrace.h> #include <asm/atomic.h> +#include <asm/io.h> #include <asm/irq.h> #include <asm/bitops.h> #include <asm/pgtable.h> @@ -29,6 +30,8 @@ #include <asm/unistd.h> #include "proto.h" +#include "irq.h" + #define DEBUG_SMP 0 #if DEBUG_SMP @@ -37,62 +40,44 @@ #define DBGS(args) #endif -struct ipi_msg_flush_tb_struct { - volatile unsigned int flush_tb_mask; - union { - struct mm_struct * flush_mm; - struct vm_area_struct * flush_vma; - } p; - unsigned long flush_addr; - unsigned long flush_end; -}; - -static struct ipi_msg_flush_tb_struct ipi_msg_flush_tb __cacheline_aligned; -static spinlock_t flush_tb_lock = SPIN_LOCK_UNLOCKED; - +/* A collection of per-processor data. */ struct cpuinfo_alpha cpu_data[NR_CPUS]; -spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED; -spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; - -unsigned int boot_cpu_id = 0; -static int smp_activated = 0; +/* A collection of single bit ipi messages. */ +static struct { + unsigned long bits __cacheline_aligned; +} ipi_data[NR_CPUS]; -int smp_found_config = 0; /* Have we found an SMP box */ -static int max_cpus = -1; +enum ipi_message_type { + IPI_RESCHEDULE, + IPI_CALL_FUNC, + IPI_CPU_STOP, +}; -unsigned int cpu_present_map = 0; +spinlock_t kernel_flag __cacheline_aligned = SPIN_LOCK_UNLOCKED; -int smp_num_cpus = 1; -int smp_num_probed = 0; /* Internal processor count */ +/* Set to a secondary's cpuid when it comes online. */ +static unsigned long smp_secondary_alive; -int smp_threads_ready = 0; -volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; -volatile unsigned long smp_spinning[NR_CPUS] = { 0, }; +unsigned long cpu_present_mask; /* Which cpus ids came online. */ +static int max_cpus = -1; /* Command-line limitation. */ +int smp_boot_cpuid; /* Which processor we booted from. */ +int smp_num_probed; /* Internal processor count */ +int smp_num_cpus = 1; /* Number that came online. */ +int smp_threads_ready; /* True once the per process idle is forked. */ cycles_t cacheflush_time; -unsigned int prof_multiplier[NR_CPUS]; -unsigned int prof_counter[NR_CPUS]; - -volatile int ipi_bits[NR_CPUS] __cacheline_aligned; - -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]; +int cpu_number_map[NR_CPUS]; +int __cpu_logical_map[NR_CPUS]; extern void calibrate_delay(void); -extern struct thread_struct * original_pcb_ptr; +extern asmlinkage void entInt(void); -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=" */ + +/* + * Process bootcommand SMP options, like "nosmp" and "maxcpus=". + */ void __init smp_setup(char *str, int *ints) { @@ -102,100 +87,87 @@ smp_setup(char *str, int *ints) max_cpus = 0; } -static void __init -smp_store_cpu_info(int id) +/* + * Called by both boot and secondaries to move global data into + * per-processor storage. + */ +static inline void __init +smp_store_cpu_info(int cpuid) { - /* This is it on Alpha, so far. */ - cpu_data[id].loops_per_sec = loops_per_sec; + cpu_data[cpuid].loops_per_sec = loops_per_sec; } -void __init -smp_commence(void) +/* + * Ideally sets up per-cpu profiling hooks. Doesn't do much now... + */ +static inline void __init +smp_setup_percpu_timer(int cpuid) { - /* Lets the callin's below out of their loop. */ - mb(); - smp_commenced = 1; + cpu_data[cpuid].prof_counter = 1; + cpu_data[cpuid].prof_multiplier = 1; + +#ifdef NOT_YET_PROFILING + load_profile_irq(mid_xlate[cpu], lvl14_resolution); + if (cpu == smp_boot_cpuid) + enable_pil_irq(14); +#endif } +/* + * Where secondaries begin a life of C. + */ void __init smp_callin(void) { int cpuid = hard_smp_processor_id(); DBGS(("CALLIN %d state 0x%lx\n", cpuid, current->state)); -#ifdef HUH - local_flush_cache_all(); - local_flush_tlb_all(); -#endif -#if 0 - set_irq_udt(mid_xlate[boot_cpu_id]); -#endif + + /* Turn on machine checks. */ + wrmces(7); + + /* Set trap vectors. */ + trap_init(); + + /* Set interrupt vector. */ + wrent(entInt, 0); + + /* Setup the scheduler for this processor. */ + init_idle(); /* Get our local ticker going. */ - smp_setup_percpu_timer(); + smp_setup_percpu_timer(cpuid); -#if 0 + /* Must have completely accurate bogos. */ + __sti(); 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 + wmb(); + smp_secondary_alive = cpuid; -#ifdef HUH - local_flush_cache_all(); - local_flush_tlb_all(); -#endif -#if 0 - __sti(); -#endif -} + /* Wait for the go code. */ + while (!smp_threads_ready) + barrier(); -asmlinkage int __init -start_secondary(void *unused) -{ - extern asmlinkage void entInt(void); - extern void paging_init_secondary(void); + printk(KERN_INFO "SMP: commencing CPU %d current %p\n", + cpuid, current); - 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 + /* Do nothing. */ cpu_idle(NULL); } + +/* + * Rough estimation for SMP scheduling, this is the number of cycles it + * takes for a fully memory-limited process to flush the SMP-local cache. + * + * We are not told how much cache there is, so we have to guess. + */ static void __init smp_tune_scheduling (void) { - /* - * Rough estimation for SMP scheduling, this is the number of - * cycles it takes for a fully memory-limited process to flush - * the SMP-local cache. - * - * We are not told how much cache there is, so we have to guess. - */ - struct percpu_struct *cpu; unsigned long on_chip_cache; unsigned long freq; @@ -231,259 +203,159 @@ smp_tune_scheduling (void) cacheflush_time = freq / 1024 * on_chip_cache / 5000; } - /* - * Cycle through the processors sending START msgs to boot each. + * Send a message to a secondary's console. "START" is one such + * interesting message. ;-) */ -void __init -smp_boot_cpus(void) +static void +send_secondary_console_msg(char *str, int cpuid) { - int cpucount = 0; - int i, first, prev; - - printk("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; /* ??? */ - - smp_store_cpu_info(boot_cpu_id); - smp_tune_scheduling(); -#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 - 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 - - for (i = 0; i < NR_CPUS; i++) { + struct percpu_struct *cpu; + register char *cp1, *cp2; + unsigned long cpumask; + size_t len; + long timeout; - if (i == boot_cpu_id) - continue; + cpu = (struct percpu_struct *) + ((char*)hwrpb + + hwrpb->processor_offset + + cpuid * hwrpb->processor_size); - if (cpu_present_map & (1 << i)) { - struct task_struct *idle; - int timeout; + cpumask = (1L << cpuid); + if (hwrpb->txrdy & cpumask) + goto delay1; + ready1: - /* 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; + cp2 = str; + len = strlen(cp2); + *(unsigned int *)&cpu->ipc_buffer[0] = len; + cp1 = (char *) &cpu->ipc_buffer[1]; + memcpy(cp1, cp2, len); - DBGS(("smp_boot_cpus: CPU %d state 0x%lx flags 0x%lx\n", - i, idle->state, idle->flags)); + /* atomic test and set */ + wmb(); + set_bit(cpuid, &hwrpb->rxrdy); - /* whirrr, whirrr, whirrrrrrrrr... */ -#ifdef HUH - local_flush_cache_all(); -#endif - secondary_cpu_start(i, idle); + if (hwrpb->txrdy & cpumask) + goto delay2; + ready2: + return; - /* 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; +delay1: + /* Wait one second. Note that jiffies aren't ticking yet. */ + for (timeout = 100000; timeout > 0; --timeout) { + if (!(hwrpb->txrdy & cpumask)) + goto ready1; + udelay(10); + barrier(); } + goto timeout; - /* 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; - } +delay2: + /* Wait one second. */ + for (timeout = 100000; timeout > 0; --timeout) { + if (!(hwrpb->txrdy & cpumask)) + goto ready2; + udelay(10); + barrier(); } - cpu_data[prev].next = first; + goto timeout; - /* Ok, they are spinning and ready to go. */ - smp_processors_ready = 1; +timeout: + printk("Processor %x not ready\n", cpuid); + return; } -static void __init -smp_setup_percpu_timer(void) +/* + * A secondary console wants to send a message. Receive it. + */ +static void +recv_secondary_console_msg(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); + int mycpu, i, cnt; + unsigned long txrdy = hwrpb->txrdy; + char *cp1, *cp2, buf[80]; + struct percpu_struct *cpu; -void -smp_percpu_timer_interrupt(struct pt_regs *regs) -{ - int cpu = smp_processor_id(); + DBGS(("recv_secondary_console_msg: TXRDY 0x%lx.\n", txrdy)); -#ifdef NOT_YET - clear_profile_irq(mid_xlate[cpu]); - if(!user_mode(regs)) - alpha_do_profile(regs->pc); -#endif + mycpu = hard_smp_processor_id(); - if (!--prof_counter[cpu]) { - int user = user_mode(regs); - if (current->pid) { - update_one_process(current, 1, user, !user, cpu); + for (i = 0; i < NR_CPUS; i++) { + if (!(txrdy & (1L << i))) + continue; - if (--current->counter < 0) { - current->counter = 0; - current->need_resched = 1; - } + DBGS(("recv_secondary_console_msg: " + "TXRDY contains CPU %d.\n", i)); - 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]; - } -} + cpu = (struct percpu_struct *) + ((char*)hwrpb + + hwrpb->processor_offset + + i * hwrpb->processor_size); -int __init -setup_profiling_timer(unsigned int multiplier) -{ -#ifdef NOT_YET - int i; - unsigned long flags; + printk(KERN_INFO "recv_secondary_console_msg: on %d from %d" + " HALT_REASON 0x%lx FLAGS 0x%lx\n", + mycpu, i, cpu->halt_reason, cpu->flags); - /* Prevent level14 ticker IRQ flooding. */ - if((!multiplier) || (lvl14_resolution / multiplier) < 500) - return -EINVAL; + cnt = cpu->ipc_buffer[0] >> 32; + if (cnt <= 0 || cnt >= 80) + strcpy(buf, "<<< BOGUS MSG >>>"); + else { + cp1 = (char *) &cpu->ipc_buffer[11]; + cp2 = buf; + strcpy(cp2, cp1); + + while ((cp2 = strchr(cp2, '\r')) != 0) { + *cp2 = ' '; + if (cp2[1] == '\n') + cp2[1] = ' '; + } + } - 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; - } + printk(KERN_INFO "recv_secondary_console_msg: on %d " + "message is '%s'\n", mycpu, buf); } - restore_flags(flags); - - return 0; - -#endif - return -EINVAL; -} - -/* Only broken Intel needs this, thus it should not even be - referenced globally. */ -void __init -initialize_secondary(void) -{ + hwrpb->txrdy = 0; } -static void __init +/* + * Convince the console to have a secondary cpu begin execution. + */ +static int __init secondary_cpu_start(int cpuid, struct task_struct *idle) { struct percpu_struct *cpu; - int timeout; + struct pcb_struct *hwpcb; + long 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 ??? */ - - DBGS(("KSP 0x%lx PTBR 0x%lx VPTBR 0x%lx\n", - cpu->hwpcb[0], cpu->hwpcb[2], hwrpb->vptb)); + hwpcb = (struct pcb_struct *) cpu->hwpcb; + + /* Initialize the CPU's HWPCB to something just good enough for + us to get started. Immediately after starting, we'll swpctx + to the target idle task's tss. Reuse the stack in the mean + time. Precalculate the target PCBB. */ + hwpcb->ksp = (unsigned long) idle + sizeof(union task_union) - 16; + hwpcb->usp = 0; + hwpcb->ptbr = idle->tss.ptbr; + hwpcb->pcc = 0; + hwpcb->asn = 0; + hwpcb->unique = virt_to_phys(&idle->tss); + hwpcb->flags = idle->tss.pal_flags; + hwpcb->res1 = hwpcb->res2 = 0; + + DBGS(("KSP 0x%lx PTBR 0x%lx VPTBR 0x%lx UNIQUE 0x%lx\n", + hwpcb->ksp, hwpcb->ptbr, hwrpb->vptb, hwcpb->unique)); DBGS(("Starting secondary cpu %d: state 0x%lx pal_flags 0x%lx\n", cpuid, idle->state, idle->tss.pal_flags)); /* Setup HWRPB fields that SRM uses to activate secondary CPU */ - hwrpb->CPU_restart = __start_cpu; - hwrpb->CPU_restart_data = (unsigned long) idle; + hwrpb->CPU_restart = __smp_callin; + hwrpb->CPU_restart_data = (unsigned long) __smp_callin; /* Recalculate and update the HWRPB checksum */ hwrpb_update_checksum(hwrpb); @@ -495,99 +367,97 @@ secondary_cpu_start(int cpuid, struct task_struct *idle) /* 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(); + wmb(); - send_cpu_msg("START\r\n", cpuid); + send_secondary_console_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; - } - mdelay(1); + /* Wait 1 second for an ACK from the console. Note that jiffies + aren't ticking yet. */ + for (timeout = 100000; timeout > 0; timeout--) { + if (cpu->flags & 1) + goto started; + udelay(10); barrier(); } + printk(KERN_ERR "SMP: Processor %d failed to start.\n", cpuid); + return -1; + +started: DBGS(("secondary_cpu_start: SUCCESS for CPU %d!!!\n", cpuid)); + return 0; } -static void -send_cpu_msg(char *str, int cpuid) +/* + * Bring one cpu online. + */ +static int __init +smp_boot_one_cpu(int cpuid, int cpunum) { - struct percpu_struct *cpu; - register char *cp1, *cp2; - unsigned long cpumask; - size_t len; - int timeout; - - cpu = (struct percpu_struct *) - ((char*)hwrpb - + hwrpb->processor_offset - + cpuid * hwrpb->processor_size); - - cpumask = (1L << cpuid); - if (hwrpb->txrdy & cpumask) - goto delay1; - ready1: - - cp2 = str; - len = strlen(cp2); - *(unsigned int *)&cpu->ipc_buffer[0] = len; - cp1 = (char *) &cpu->ipc_buffer[1]; - memcpy(cp1, cp2, len); - - /* atomic test and set */ - set_bit(cpuid, &hwrpb->rxrdy); - - if (hwrpb->txrdy & cpumask) - goto delay2; - ready2: - return; - -delay1: - for (timeout = 10000; timeout > 0; --timeout) { - if (!(hwrpb->txrdy & cpumask)) - goto ready1; - udelay(100); + struct task_struct *idle; + long timeout; + + /* Cook up an idler for this guy. Note that the address we give + to kernel_thread is irrelevant -- it's going to start where + HWRPB.CPU_restart says to start. But this gets all the other + task-y sort of data structures set up like we wish. */ + kernel_thread((void *)__smp_callin, NULL, CLONE_PID|CLONE_VM); + idle = task[cpunum]; + if (!idle) + panic("No idle process for CPU %d", cpuid); + idle->processor = cpuid; + + /* Schedule the first task manually. */ + /* ??? Ingo, what is this? */ + idle->has_cpu = 1; + + DBGS(("smp_boot_one_cpu: CPU %d state 0x%lx flags 0x%lx\n", + cpuid, idle->state, idle->flags)); + + /* The secondary will change this once it is happy. Note that + secondary_cpu_start contains the necessary memory barrier. */ + smp_secondary_alive = -1; + + /* Whirrr, whirrr, whirrrrrrrrr... */ + if (secondary_cpu_start(cpuid, idle)) + return -1; + + /* We've been acked by the console; wait one second for the task + to start up for real. Note that jiffies aren't ticking yet. */ + for (timeout = 0; timeout < 100000; timeout++) { + if (smp_secondary_alive != -1) + goto alive; + udelay(10); barrier(); } - goto timeout; -delay2: - for (timeout = 10000; timeout > 0; --timeout) { - if (!(hwrpb->txrdy & cpumask)) - goto ready2; - udelay(100); - barrier(); - } - goto timeout; + printk(KERN_ERR "SMP: Processor %d is stuck.\n", cpuid); + return -1; -timeout: - printk("Processor %x not ready\n", cpuid); - return; +alive: + /* Another "Red Snapper". */ + cpu_number_map[cpuid] = cpunum; + __cpu_logical_map[cpunum] = cpuid; + return 0; } /* - * setup_smp() - * - * called from arch/alpha/kernel/setup.c:setup_arch() when __SMP__ defined + * Called from setup_arch. Detect an SMP system and which processors + * are present. */ void __init 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); + + smp_boot_cpuid = hard_smp_processor_id(); + if (smp_boot_cpuid != 0) { + printk(KERN_WARNING "SMP: Booting off cpu %d instead of 0?\n", + smp_boot_cpuid); } if (hwrpb->nr_processors > 1) { + int boot_cpu_palrev; DBGS(("setup_smp: nr_processors %ld\n", hwrpb->nr_processors)); @@ -601,10 +471,9 @@ setup_smp(void) ((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; + /* Assume here that "whami" == index */ + cpu_present_mask |= (1L << i); + cpu->pal_revision = boot_cpu_palrev; } DBGS(("setup_smp: CPU %d: flags 0x%lx type 0x%lx\n", @@ -614,76 +483,249 @@ setup_smp(void) } } else { smp_num_probed = 1; - cpu_present_map = (1 << boot_cpu_id); + cpu_present_mask = (1L << smp_boot_cpuid); } - 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); + + printk(KERN_INFO "SMP: %d CPUs probed -- cpu_present_mask = %lx\n", + smp_num_probed, cpu_present_mask); } -static void -secondary_console_message(void) +/* + * Called by smp_init bring all the secondaries online and hold them. + */ +void __init +smp_boot_cpus(void) { - int mycpu, i, cnt; - unsigned long txrdy = hwrpb->txrdy; - char *cp1, *cp2, buf[80]; - struct percpu_struct *cpu; + int cpu_count, i; + unsigned long bogosum; - DBGS(("secondary_console_message: TXRDY 0x%lx.\n", txrdy)); + /* 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_data, 0, sizeof(ipi_data)); - mycpu = hard_smp_processor_id(); + cpu_number_map[smp_boot_cpuid] = 0; + __cpu_logical_map[0] = smp_boot_cpuid; + current->processor = smp_boot_cpuid; + + smp_store_cpu_info(smp_boot_cpuid); + smp_tune_scheduling(); + smp_setup_percpu_timer(smp_boot_cpuid); + + init_idle(); + /* Nothing to do on a UP box, or when told not to. */ + if (smp_num_probed == 1 || max_cpus == 0) { + printk(KERN_INFO "SMP mode deactivated.\n"); + return; + } + + printk(KERN_INFO "SMP starting up secondaries.\n"); + + cpu_count = 1; for (i = 0; i < NR_CPUS; i++) { - if (!(txrdy & (1L << i))) + if (i == smp_boot_cpuid) continue; - DBGS(("secondary_console_message: " - "TXRDY contains CPU %d.\n", i)); + if (((cpu_present_mask >> i) & 1) == 0) + continue; - cpu = (struct percpu_struct *) - ((char*)hwrpb - + hwrpb->processor_offset - + i * hwrpb->processor_size); + if (smp_boot_one_cpu(i, cpu_count)) + continue; - printk("secondary_console_message: on %d from %d" - " HALT_REASON 0x%lx FLAGS 0x%lx\n", - mycpu, i, cpu->halt_reason, cpu->flags); + cpu_count++; + } - cnt = cpu->ipc_buffer[0] >> 32; - if (cnt <= 0 || cnt >= 80) - strcpy(buf, "<<< BOGUS MSG >>>"); - else { - cp1 = (char *) &cpu->ipc_buffer[11]; - cp2 = buf; - strcpy(cp2, cp1); - - while ((cp2 = strchr(cp2, '\r')) != 0) { - *cp2 = ' '; - if (cp2[1] == '\n') - cp2[1] = ' '; - } - } + if (cpu_count == 1) { + printk(KERN_ERR "SMP: Only one lonely processor alive.\n"); + return; + } + + bogosum = 0; + for (i = 0; i < NR_CPUS; i++) { + if (cpu_present_mask & (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 by smp_init to release the blocking online cpus once they + * are all started. + */ +void __init +smp_commence(void) +{ + /* smp_init sets smp_threads_ready -- that's enough. */ + mb(); +} + +/* + * Only broken Intel needs this, thus it should not even be + * referenced globally. + */ + +void __init +initialize_secondary(void) +{ +} + + +extern void update_one_process(struct task_struct *p, unsigned long ticks, + unsigned long user, unsigned long system, + int cpu); - printk("secondary_console_message: on %d message is '%s'\n", - mycpu, buf); +void +smp_percpu_timer_interrupt(struct pt_regs *regs) +{ + int cpu = smp_processor_id(); + int user = user_mode(regs); + struct cpuinfo_alpha *data = &cpu_data[cpu]; + +#ifdef NOT_YET_PROFILING + clear_profile_irq(mid_xlate[cpu]); + if (!user) + alpha_do_profile(regs->pc); +#endif + + if (!--data->prof_counter) { + /* We need to make like a normal interrupt -- otherwise + timer interrupts ignore the global interrupt lock, + which would be a Bad Thing. */ + 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); } +} - hwrpb->txrdy = 0; +int __init +setup_profiling_timer(unsigned int multiplier) +{ +#ifdef NOT_YET_PROFILING + 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_mask & (1L << i)) { + load_profile_irq(mid_xlate[i], + lvl14_resolution / multiplier); + prof_multiplier[i] = multiplier; + } + } + restore_flags(flags); + + return 0; +#else + return -EINVAL; +#endif } -enum ipi_message_type { - IPI_TLB_ALL, - IPI_TLB_MM, - IPI_TLB_PAGE, - IPI_RESCHEDULE, - IPI_CPU_STOP + +static void +send_ipi_message(unsigned long to_whom, enum ipi_message_type operation) +{ + long i, j; + + /* Reduce the number of memory barriers by doing two loops, + one to set the bits, one to invoke the interrupts. */ + + mb(); /* Order out-of-band data and bit setting. */ + + for (i = 0, j = 1; i < NR_CPUS; ++i, j <<= 1) { + if (to_whom & j) + set_bit(operation, &ipi_data[i].bits); + } + + mb(); /* Order bit setting and interrupt. */ + + for (i = 0, j = 1; i < NR_CPUS; ++i, j <<= 1) { + if (to_whom & j) + wripir(i); + } +} + +/* Structure and data for smp_call_function. This is designed to + minimize static memory requirements. Plus it looks cleaner. */ + +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; + +/* Atomicly drop data into a shared pointer. The pointer is free if + it is initially locked. If retry, spin until free. */ + +static inline int +pointer_lock (void *lock, void *data, int retry) +{ + void *old, *tmp; + + mb(); +again: + /* Compare and swap with zero. */ + asm volatile ( + "1: ldq_l %0,%1\n" + " mov %3,%2\n" + " bne %0,2f\n" + " stq_c %2,%1\n" + " beq %2,1b\n" + "2:" + : "=&r"(old), "=m"(*(void **)lock), "=&r"(tmp) + : "r"(data) + : "memory"); + + if (old == 0) + return 0; + if (! retry) + return -EBUSY; + + while (*(void **)lock) + schedule(); + goto again; +} + void handle_ipi(struct pt_regs *regs) { int this_cpu = smp_processor_id(); - volatile int * pending_ipis = &ipi_bits[this_cpu]; + unsigned long *pending_ipis = &ipi_data[this_cpu].bits; unsigned long ops; DBGS(("handle_ipi: on CPU %d ops 0x%x PC 0x%lx\n", @@ -699,190 +741,189 @@ handle_ipi(struct pt_regs *regs) ops &= ~which; which = ffz(~which); - if (which < IPI_RESCHEDULE) { - if (which == IPI_TLB_ALL) - tbia(); - else if (which == IPI_TLB_MM) { - struct mm_struct * mm; - mm = ipi_msg_flush_tb.p.flush_mm; - if (mm == current->mm) - flush_tlb_current(mm); - } - else /* IPI_TLB_PAGE */ { - struct vm_area_struct * vma; - struct mm_struct * mm; - unsigned long addr; - - vma = ipi_msg_flush_tb.p.flush_vma; - mm = vma->vm_mm; - addr = ipi_msg_flush_tb.flush_addr; - - if (mm == current->mm) - flush_tlb_current_page(mm, vma, addr); - } - clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask); - } - else if (which == IPI_RESCHEDULE) { + if (which == IPI_RESCHEDULE) { /* Reschedule callback. Everything to be done is done by the interrupt return path. */ } + else if (which == 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; + + /* Notify the sending CPU that the data has been + received, and execution is about to begin. */ + 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); + } else if (which == IPI_CPU_STOP) { halt(); } else { - printk(KERN_CRIT "unknown_ipi() on CPU %d: %lu\n", + printk(KERN_CRIT "Unknown IPI on CPU %d: %lu\n", this_cpu, which); } } while (ops); + mb(); /* Order data access and bit testing. */ } cpu_data[this_cpu].ipi_count++; if (hwrpb->txrdy) - secondary_console_message(); + recv_secondary_console_msg(); } -static void -send_ipi_message(unsigned long to_whom, enum ipi_message_type operation) +void +smp_send_reschedule(int cpu) { - long i, j; - - /* Reduce the number of memory barriers by doing two loops, - one to set the bits, one to invoke the interrupts. */ - - mb(); /* Order out-of-band data and bit setting. */ - - for (i = 0, j = 1; i < NR_CPUS; ++i, j <<= 1) { - if (to_whom & j) - set_bit(operation, &ipi_bits[i]); - } - - mb(); /* Order bit setting and interrupt. */ + send_ipi_message(1L << cpu, IPI_RESCHEDULE); +} - for (i = 0, j = 1; i < NR_CPUS; ++i, j <<= 1) { - if (to_whom & j) - wripir(i); - } +void +smp_send_stop(void) +{ + unsigned long to_whom = cpu_present_mask ^ (1L << smp_processor_id()); + send_ipi_message(to_whom, 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_info(char *buffer) +smp_call_function (void (*func) (void *info), void *info, int retry, int wait) { - long i; - unsigned long sum = 0; - for (i = 0; i < NR_CPUS; i++) - sum += cpu_data[i].ipi_count; + unsigned long to_whom = cpu_present_mask ^ (1L << smp_processor_id()); + struct smp_call_struct data; + long timeout; + + 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); + + /* Aquire the smp_call_function_data mutex. */ + if (pointer_lock(&smp_call_function_data, &data, retry)) + return -EBUSY; + + /* Send a message to all other CPUs. */ + send_ipi_message(to_whom, IPI_CALL_FUNC); + + /* Wait for a minimal response. */ + timeout = jiffies + HZ; + while (atomic_read (&data.unstarted_count) > 0 + && time_before (jiffies, timeout)) + barrier(); - return sprintf(buffer, "CPUs probed %d active %d map 0x%x IPIs %ld\n", - smp_num_probed, smp_num_cpus, cpu_present_map, sum); -} + /* We either got one or timed out -- clear the lock. */ + mb(); + smp_call_function_data = 0; + if (atomic_read (&data.unstarted_count) > 0) + return -ETIMEDOUT; + + /* Wait for a complete response, if needed. */ + if (wait) { + while (atomic_read (&data.unfinished_count) > 0) + barrier(); + } -void -smp_send_reschedule(int cpu) -{ - send_ipi_message(1 << cpu, IPI_RESCHEDULE); + return 0; } -void -smp_send_stop(void) +static void +ipi_flush_tlb_all(void *ignored) { - unsigned long to_whom = cpu_present_map ^ (1 << smp_processor_id()); - send_ipi_message(to_whom, IPI_CPU_STOP); + tbia(); } void flush_tlb_all(void) { - unsigned long to_whom = cpu_present_map ^ (1 << smp_processor_id()); - long timeout = 1000000; - - spin_lock(&flush_tb_lock); - - ipi_msg_flush_tb.flush_tb_mask = to_whom; - send_ipi_message(to_whom, IPI_TLB_ALL); tbia(); - while (ipi_msg_flush_tb.flush_tb_mask && --timeout) { - udelay(1); - barrier(); - } - - 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; + /* Although we don't have any data to pass, we do want to + synchronize with the other processors. */ + if (smp_call_function(ipi_flush_tlb_all, NULL, 1, 1)) { + printk(KERN_CRIT "flush_tlb_all: timed out\n"); } +} - spin_unlock(&flush_tb_lock); +static void +ipi_flush_tlb_mm(void *x) +{ + struct mm_struct *mm = (struct mm_struct *) x; + if (mm == current->mm) + flush_tlb_current(mm); } void flush_tlb_mm(struct mm_struct *mm) { - unsigned long to_whom = cpu_present_map ^ (1 << smp_processor_id()); - long timeout = 1000000; - - spin_lock(&flush_tb_lock); - - ipi_msg_flush_tb.flush_tb_mask = to_whom; - ipi_msg_flush_tb.p.flush_mm = mm; - send_ipi_message(to_whom, IPI_TLB_MM); - - if (mm != current->mm) - flush_tlb_other(mm); - else + if (mm == current->mm) flush_tlb_current(mm); + else + flush_tlb_other(mm); - while (ipi_msg_flush_tb.flush_tb_mask && --timeout) { - udelay(1); - barrier(); + if (smp_call_function(ipi_flush_tlb_mm, mm, 1, 1)) { + printk(KERN_CRIT "flush_tlb_mm: timed out\n"); } +} - 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; - } +struct flush_tlb_page_struct { + struct vm_area_struct *vma; + struct mm_struct *mm; + unsigned long addr; +}; - spin_unlock(&flush_tb_lock); +static void +ipi_flush_tlb_page(void *x) +{ + struct flush_tlb_page_struct *data = (struct flush_tlb_page_struct *)x; + if (data->mm == current->mm) + flush_tlb_current_page(data->mm, data->vma, data->addr); } void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) { - int cpu = smp_processor_id(); - unsigned long to_whom = cpu_present_map ^ (1 << cpu); - struct mm_struct * mm = vma->vm_mm; - int timeout = 1000000; - - spin_lock(&flush_tb_lock); + struct flush_tlb_page_struct data; + struct mm_struct *mm = vma->vm_mm; - ipi_msg_flush_tb.flush_tb_mask = to_whom; - ipi_msg_flush_tb.p.flush_vma = vma; - ipi_msg_flush_tb.flush_addr = addr; - send_ipi_message(to_whom, IPI_TLB_PAGE); + data.vma = vma; + data.mm = mm; + data.addr = addr; - if (mm != current->mm) - flush_tlb_other(mm); - else + if (mm == current->mm) flush_tlb_current_page(mm, vma, addr); - - while (ipi_msg_flush_tb.flush_tb_mask && --timeout) { - udelay(1); - barrier(); - } - - if (timeout == 0) { - printk("flush_tlb_page: 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; + else + flush_tlb_other(mm); + + if (smp_call_function(ipi_flush_tlb_page, &data, 1, 1)) { + printk(KERN_CRIT "flush_tlb_page: timed out\n"); } - - spin_unlock(&flush_tb_lock); } void @@ -892,6 +933,20 @@ flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) flush_tlb_mm(mm); } + +int +smp_info(char *buffer) +{ + long i; + unsigned long sum = 0; + for (i = 0; i < NR_CPUS; i++) + sum += cpu_data[i].ipi_count; + + return sprintf(buffer, "CPUs probed %d active %d map 0x%lx IPIs %ld\n", + smp_num_probed, smp_num_cpus, cpu_present_mask, sum); +} + + #if DEBUG_SPINLOCK #ifdef MANAGE_SPINLOCK_IPL @@ -932,17 +987,16 @@ void spin_lock(spinlock_t * lock) { long tmp; - long stuck = 1<<27; + long stuck; void *inline_pc = __builtin_return_address(0); unsigned long started = jiffies; int printed = 0; int cpu = smp_processor_id(); long old_ipl = spinlock_raise_ipl(lock); + stuck = 1L << 28; 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. */ @@ -961,19 +1015,16 @@ spin_lock(spinlock_t * lock) " blbs %0,2b\n" " br 1b\n" ".previous" - : "=r" (tmp), - "=m" (__dummy_lock(lock)), - "=r" (stuck) - : "2" (stuck)); + : "=r" (tmp), "=m" (__dummy_lock(lock)), "=r" (stuck) + : "1" (__dummy_lock(lock)), "2" (stuck)); if (stuck < 0) { - if (!printed) { - printk("spinlock stuck at %p(%d) owner %s at %p\n", - inline_pc, cpu, lock->task->comm, - lock->previous); - printed = 1; - } - stuck = 1<<30; + printk(KERN_WARNING + "spinlock stuck at %p(%d) owner %s at %p(%d) st %ld\n", + inline_pc, cpu, lock->task->comm, lock->previous, + lock->task->processor, lock->task->state); + stuck = 1L << 36; + printed = 1; goto try_again; } @@ -984,7 +1035,7 @@ spin_lock(spinlock_t * lock) lock->task = current; if (printed) { - printk("spinlock grabbed at %p(%d) %ld ticks\n", + printk(KERN_WARNING "spinlock grabbed at %p(%d) %ld ticks\n", inline_pc, cpu, jiffies - started); } } @@ -1006,7 +1057,7 @@ spin_trylock(spinlock_t * lock) return ret; } #endif /* DEBUG_SPINLOCK */ - + #if DEBUG_RWLOCK void write_lock(rwlock_t * lock) { @@ -1038,18 +1089,17 @@ void write_lock(rwlock_t * lock) " blt %1,8b\n" " br 1b\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) - ); + : "=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 %p\n", inline_pc); + printk(KERN_WARNING "write_lock stuck at %p\n", inline_pc); goto try_again; } if (stuck_reader < 0) { - printk("write_lock stuck on readers at %p\n", inline_pc); + printk(KERN_WARNING "write_lock stuck on readers at %p\n", + inline_pc); goto try_again; } } @@ -1079,11 +1129,10 @@ void read_lock(rwlock_t * lock) " br 1b\n" ".previous" : "=m" (__dummy_lock(lock)), "=&r" (regx), "=&r" (stuck_lock) - : "0" (__dummy_lock(lock)), "2" (stuck_lock) - ); + : "0" (__dummy_lock(lock)), "2" (stuck_lock)); if (stuck_lock < 0) { - printk("read_lock stuck at %p\n", inline_pc); + printk(KERN_WARNING "read_lock stuck at %p\n", inline_pc); goto try_again; } } diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c index a84378926..927ca201f 100644 --- a/arch/alpha/kernel/time.c +++ b/arch/alpha/kernel/time.c @@ -42,6 +42,9 @@ #include "proto.h" #include "irq.h" +extern rwlock_t xtime_lock; +extern volatile unsigned long lost_ticks; /*kernel/sched.c*/ + static int set_rtc_mmss(unsigned long); @@ -86,15 +89,15 @@ void timer_interrupt(int irq, void *dev, struct pt_regs * regs) long nticks; #ifdef __SMP__ - extern void smp_percpu_timer_interrupt(struct pt_regs *); - extern unsigned int boot_cpu_id; - /* when SMP, do this for *all* CPUs, - but only do the rest for the boot CPU */ + /* When SMP, do this for *all* CPUs, but only do the rest for + the boot CPU. */ smp_percpu_timer_interrupt(regs); - if (smp_processor_id() != boot_cpu_id) - return; + if (smp_processor_id() != smp_boot_cpuid) + return; #endif + write_lock(&xtime_lock); + /* * Calculate how many ticks have passed since the last update, * including any previous partial leftover. Save any resulting @@ -124,6 +127,8 @@ void timer_interrupt(int irq, void *dev, struct pt_regs * regs) int tmp = set_rtc_mmss(xtime.tv_sec); state.last_rtc_update = xtime.tv_sec - (tmp ? 600 : 0); } + + write_unlock(&xtime_lock); } /* @@ -226,7 +231,8 @@ time_init(void) { void (*irq_handler)(int, void *, struct pt_regs *); unsigned int year, mon, day, hour, min, sec, cc1, cc2; - unsigned long cycle_freq, diff, one_percent; + unsigned long cycle_freq, one_percent; + long diff; /* * The Linux interpretation of the CMOS clock register contents: @@ -242,7 +248,7 @@ time_init(void) if (!est_cycle_freq) { /* Sometimes the hwrpb->cycle_freq value is bogus. - Go another round to check up on it and see. */ + Go another round to check up on it and see. */ do { } while (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)); do { } while (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP); cc2 = rpcc(); @@ -279,8 +285,7 @@ time_init(void) mon = CMOS_READ(RTC_MONTH); year = CMOS_READ(RTC_YEAR); - if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) - { + if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { BCD_TO_BIN(sec); BCD_TO_BIN(min); BCD_TO_BIN(hour); @@ -328,18 +333,24 @@ time_init(void) void do_gettimeofday(struct timeval *tv) { - unsigned long flags, delta_cycles, delta_usec; - unsigned long sec, usec; - __u32 now; - extern volatile unsigned long lost_ticks; /*kernel/sched.c*/ + unsigned long sec, usec, lost, flags; + unsigned long delta_cycles, delta_usec, partial_tick; - now = rpcc(); - save_and_cli(flags); + read_lock_irqsave(&xtime_lock, flags); + + delta_cycles = rpcc() - state.last_time; sec = xtime.tv_sec; usec = xtime.tv_usec; - delta_cycles = now - state.last_time; - restore_flags(flags); + partial_tick = state.partial_tick; + lost = lost_ticks; + + read_unlock_irqrestore(&xtime_lock, flags); +#ifdef __SMP__ + /* Until and unless we figure out how to get cpu cycle counters + in sync and keep them there, we can't use the rpcc tricks. */ + delta_usec = lost * (1000000 / HZ); +#else /* * usec = cycles * ticks_per_cycle * 2**48 * 1e6 / (2**48 * ticks) * = cycles * (s_t_p_c) * 1e6 / (2**48 * ticks) @@ -354,13 +365,10 @@ do_gettimeofday(struct timeval *tv) */ delta_usec = (delta_cycles * state.scaled_ticks_per_cycle - + state.partial_tick - + (lost_ticks << FIX_SHIFT) ) * 15625; + + partial_tick + + (lost << FIX_SHIFT)) * 15625; delta_usec = ((delta_usec / ((1UL << (FIX_SHIFT-6-1)) * HZ)) + 1) / 2; - - /* the 'lost_tics' term above implements this: - * delta_usec += lost_ticks * (1000000 / HZ); - */ +#endif usec += delta_usec; if (usec >= 1000000) { @@ -375,13 +383,41 @@ do_gettimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv) { - cli(); - xtime = *tv; + unsigned long delta_usec; + long sec, usec; + + write_lock_irq(&xtime_lock); + + /* The offset that is added into time in do_gettimeofday above + must be subtracted out here to keep a coherent view of the + time. Without this, a full-tick error is possible. */ + +#ifdef __SMP__ + delta_usec = lost_ticks * (1000000 / HZ); +#else + delta_usec = rpcc() - state.last_time; + delta_usec = (delta_usec * state.scaled_ticks_per_cycle + + state.partial_tick + + (lost_ticks << FIX_SHIFT)) * 15625; + delta_usec = ((delta_usec / ((1UL << (FIX_SHIFT-6-1)) * HZ)) + 1) / 2; +#endif + + sec = tv->tv_sec; + usec = tv->tv_usec; + usec -= delta_usec; + if (usec < 0) { + usec += 1000000; + sec -= 1; + } + + xtime.tv_sec = sec; + xtime.tv_usec = usec; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; - sti(); + + write_unlock_irq(&xtime_lock); } diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c index 2548f0914..1b490b9c1 100644 --- a/arch/alpha/kernel/traps.c +++ b/arch/alpha/kernel/traps.c @@ -1,5 +1,5 @@ /* - * kernel/traps.c + * arch/alpha/kernel/traps.c * * (C) Copyright 1994 Linus Torvalds */ @@ -95,6 +95,9 @@ die_if_kernel(char * str, struct pt_regs *regs, long err, unsigned long *r9_15) { if (regs->ps & 8) return; +#ifdef __SMP__ + printk("CPU %d ", hard_smp_processor_id()); +#endif printk("%s(%d): %s %ld\n", current->comm, current->pid, str, err); dik_show_regs(regs, r9_15); dik_show_code((unsigned int *)regs->pc); @@ -128,8 +131,8 @@ do_entArith(unsigned long summary, unsigned long write_mask, if (summary & 1) { /* Software-completion summary bit is set, so try to emulate the instruction. */ - if (implver() == IMPLVER_EV6) { - /* Whee! EV6 has precice exceptions. */ + if (!amask(AMASK_PRECISE_TRAP)) { + /* 21264 (except pass 1) has precise exceptions. */ if (alpha_fp_emul(regs.pc - 4)) return; } else { @@ -138,14 +141,12 @@ do_entArith(unsigned long summary, unsigned long write_mask, } } - lock_kernel(); #if 0 printk("%s: arithmetic trap at %016lx: %02lx %016lx\n", current->comm, regs.pc, summary, write_mask); #endif die_if_kernel("Arithmetic fault", ®s, 0, 0); send_sig(SIGFPE, current, 1); - unlock_kernel(); } asmlinkage void @@ -235,10 +236,8 @@ do_entDbg(unsigned long type, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, struct pt_regs regs) { - lock_kernel(); die_if_kernel("Instruction fault", ®s, type, 0); force_sig(SIGILL, current); - unlock_kernel(); } @@ -453,10 +452,8 @@ got_exception: unsigned long newpc; newpc = fixup_exception(una_reg, fixup, pc); - lock_kernel(); printk("Forwarding unaligned exception at %lx (%lx)\n", pc, newpc); - unlock_kernel(); (®s)->pc = newpc; return; @@ -610,11 +607,9 @@ do_entUnaUser(void * va, unsigned long opcode, cnt = 0; } if (++cnt < 5) { - lock_kernel(); printk("%s(%d): unaligned trap at %016lx: %p %lx %ld\n", current->comm, current->pid, regs->pc - 4, va, opcode, reg); - unlock_kernel(); } last_time = jiffies; } @@ -868,16 +863,12 @@ do_entUnaUser(void * va, unsigned long opcode, give_sigsegv: regs->pc -= 4; /* make pc point to faulting insn */ - lock_kernel(); send_sig(SIGSEGV, current, 1); - unlock_kernel(); return; give_sigbus: regs->pc -= 4; - lock_kernel(); send_sig(SIGBUS, current, 1); - unlock_kernel(); return; } diff --git a/arch/alpha/lib/memcpy.c b/arch/alpha/lib/memcpy.c index dc708c73e..d715f0219 100644 --- a/arch/alpha/lib/memcpy.c +++ b/arch/alpha/lib/memcpy.c @@ -21,30 +21,44 @@ * This should be done in one go with ldq_u*2/mask/stq_u. Do it * with a macro so that we can fix it up later.. */ -#define ALIGN_DEST_TO8(d,s,n) \ +#define ALIGN_DEST_TO8_UP(d,s,n) \ while (d & 7) { \ if (n <= 0) return; \ n--; \ *(char *) d = *(char *) s; \ d++; s++; \ } +#define ALIGN_DEST_TO8_DN(d,s,n) \ + while (d & 7) { \ + if (n <= 0) return; \ + n--; \ + d--; s--; \ + *(char *) d = *(char *) s; \ + } /* * This should similarly be done with ldq_u*2/mask/stq. The destination * is aligned, but we don't fill in a full quad-word */ -#define DO_REST(d,s,n) \ +#define DO_REST_UP(d,s,n) \ while (n > 0) { \ n--; \ *(char *) d = *(char *) s; \ d++; s++; \ } +#define DO_REST_DN(d,s,n) \ + while (n > 0) { \ + n--; \ + d--; s--; \ + *(char *) d = *(char *) s; \ + } /* * This should be done with ldq/mask/stq. The source and destination are * aligned, but we don't fill in a full quad-word */ -#define DO_REST_ALIGNED(d,s,n) DO_REST(d,s,n) +#define DO_REST_ALIGNED_UP(d,s,n) DO_REST_UP(d,s,n) +#define DO_REST_ALIGNED_DN(d,s,n) DO_REST_DN(d,s,n) /* * This does unaligned memory copies. We want to avoid storing to @@ -53,9 +67,10 @@ * * Note the ordering to try to avoid load (and address generation) latencies. */ -static inline void __memcpy_unaligned(unsigned long d, unsigned long s, long n) +static inline void __memcpy_unaligned_up (unsigned long d, unsigned long s, + long n) { - ALIGN_DEST_TO8(d,s,n); + ALIGN_DEST_TO8_UP(d,s,n); n -= 8; /* to avoid compare against 8 in the loop */ if (n >= 0) { unsigned long low_word, high_word; @@ -77,7 +92,17 @@ static inline void __memcpy_unaligned(unsigned long d, unsigned long s, long n) } while (n >= 0); } n += 8; - DO_REST(d,s,n); + DO_REST_UP(d,s,n); +} + +static inline void __memcpy_unaligned_dn (unsigned long d, unsigned long s, + long n) +{ + /* I don't understand AXP assembler well enough for this. -Tim */ + s += n; + d += n; + while (n--) + * (char *) --d = * (char *) --s; } /* @@ -88,9 +113,10 @@ static inline void __memcpy_unaligned(unsigned long d, unsigned long s, long n) * * Note the ordering to try to avoid load (and address generation) latencies. */ -static inline void __memcpy_aligned(unsigned long d, unsigned long s, long n) +static inline void __memcpy_aligned_up (unsigned long d, unsigned long s, + long n) { - ALIGN_DEST_TO8(d,s,n); + ALIGN_DEST_TO8_UP(d,s,n); n -= 8; while (n >= 0) { unsigned long tmp; @@ -101,18 +127,58 @@ static inline void __memcpy_aligned(unsigned long d, unsigned long s, long n) d += 8; } n += 8; - DO_REST_ALIGNED(d,s,n); + DO_REST_ALIGNED_UP(d,s,n); +} +static inline void __memcpy_aligned_dn (unsigned long d, unsigned long s, + long n) +{ + s += n; + d += n; + ALIGN_DEST_TO8_DN(d,s,n); + n -= 8; + while (n >= 0) { + unsigned long tmp; + s -= 8; + __asm__("ldq %0,%1":"=r" (tmp):"m" (*(unsigned long *) s)); + n -= 8; + d -= 8; + *(unsigned long *) d = tmp; + } + n += 8; + DO_REST_ALIGNED_DN(d,s,n); } void * memcpy(void * dest, const void *src, size_t n) { if (!(((unsigned long) dest ^ (unsigned long) src) & 7)) { - __memcpy_aligned((unsigned long) dest, (unsigned long) src, n); + __memcpy_aligned_up ((unsigned long) dest, (unsigned long) src, + n); return dest; } - __memcpy_unaligned((unsigned long) dest, (unsigned long) src, n); + __memcpy_unaligned_up ((unsigned long) dest, (unsigned long) src, n); return dest; } /* For backward modules compatibility, define __memcpy. */ asm("__memcpy = memcpy; .globl __memcpy"); + +void *memmove (void *dest, const void *src, size_t n) +{ + if (dest <= src) { + if (!(((unsigned long) dest ^ (unsigned long) src) & 7)) + __memcpy_aligned_up ((unsigned long) dest, + (unsigned long) src, n); + else + __memcpy_unaligned_up ((unsigned long) dest, + (unsigned long) src, n); + } + else { + if (!(((unsigned long) dest ^ (unsigned long) src) & 7)) + __memcpy_aligned_dn ((unsigned long) dest, + (unsigned long) src, n); + else + __memcpy_unaligned_dn ((unsigned long) dest, + (unsigned long) src, n); + } + return dest; +} diff --git a/arch/alpha/mm/init.c b/arch/alpha/mm/init.c index 2e67deb8a..fc5a964bb 100644 --- a/arch/alpha/mm/init.c +++ b/arch/alpha/mm/init.c @@ -256,26 +256,6 @@ paging_init(unsigned long start_mem, unsigned long end_mem) return start_mem; } -#ifdef __SMP__ -/* - * paging_init_secondary(), called ONLY by secondary CPUs, - * sets up current->tss contents appropriately and does a load_PCB. - * note that current should be pointing at the idle thread task struct - * for this CPU. - */ -void -paging_init_secondary(void) -{ - current->tss.ptbr = init_task.tss.ptbr; - current->tss.pal_flags = 1; - current->tss.flags = 0; - load_PCB(¤t->tss); - tbia(); - - return; -} -#endif /* __SMP__ */ - #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_SRM) void srm_paging_stop (void) |