From: James S. <jsi...@us...> - 2001-11-20 18:18:31
|
Update of /cvsroot/linux-mips/linux/arch/mips/kernel In directory usw-pr-cvs1:/tmp/cvs-serv12433/mips/kernel Modified Files: smp.c Log Message: Huge changes to SMP handling. Index: smp.c =================================================================== RCS file: /cvsroot/linux-mips/linux/arch/mips/kernel/smp.c,v retrieving revision 1.6 retrieving revision 1.7 diff -u -d -r1.6 -r1.7 --- smp.c 2001/11/08 17:11:07 1.6 +++ smp.c 2001/11/20 18:18:27 1.7 @@ -1,6 +1,4 @@ /* - * Copyright (C) 2000, 2001 Broadcom Corporation - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 @@ -14,8 +12,12 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) 2000, 2001 Kanoj Sarcar + * Copyright (C) 2000, 2001 Ralf Baechle + * Copyright (C) 2000, 2001 Silicon Graphics, Inc. + * Copyright (C) 2000, 2001 Broadcom Corporation */ - #include <linux/config.h> #include <linux/init.h> #include <linux/spinlock.h> @@ -27,6 +29,7 @@ #include <linux/interrupt.h> #include <asm/atomic.h> +#include <asm/cpu.h> #include <asm/processor.h> #include <asm/system.h> #include <asm/hardirq.h> @@ -35,39 +38,27 @@ #include <asm/delay.h> #include <asm/smp.h> -/* - * This was written with the BRCM12500 MP SOC in mind, but tries to - * be generic. It's modelled on the mips64 smp.c code, which is - * derived from Sparc, I'm guessing, which is derived from... - * - * It's probably horribly designed for very large ccNUMA systems - * as it doesn't take any node clustering into account. -*/ - - /* Ze Big Kernel Lock! */ spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; -int smp_threads_ready; /* Not used */ +int smp_threads_ready; int smp_num_cpus; /* Number that came online. */ cpumask_t cpu_online_map; /* Bitmask of currently online CPUs */ int global_irq_holder = NO_PROC_ID; spinlock_t global_irq_lock = SPIN_LOCK_UNLOCKED; struct cpuinfo_mips cpu_data[NR_CPUS]; - -struct smp_fn_call_struct smp_fn_call = -{ SPIN_LOCK_UNLOCKED, ATOMIC_INIT(0), NULL, NULL}; +void (*volatile smp_cpu0_finalize)(void) = NULL; static atomic_t cpus_booted = ATOMIC_INIT(0); /* These are defined by the board-specific code. */ -/* Cause the function described by smp_fn_call - to be executed on the passed cpu. When the function - has finished, increment the finished field of - smp_fn_call. */ - -void core_call_function(int cpu); +/* + * Cause the function described by call_data to be executed on the passed + * cpu. When the function has finished, increment the finished field of + * call_data. + */ +void core_send_ipi(int cpu, unsigned int action); /* * Clear all undefined state in the cpu, set up sp and gp to the passed @@ -89,11 +80,27 @@ the number of cpus in the system, including this one */ int prom_setup_smp(void); +/* + * Hook for doing final board-specific setup after the generic smp setup + * is done + */ int start_secondary(void *unused) { prom_init_secondary(); + + /* Do stuff that trap_init() did for the first processor */ + clear_cp0_status(ST0_BEV); + if (mips_cpu.options & MIPS_CPU_DIVEC) { + set_cp0_cause(CAUSEF_IV); + } + /* XXX parity protection should be folded in here when it's converted to + an option instead of something based on .cputype */ + write_32bit_cp0_register(CP0_CONTEXT, smp_processor_id()<<23); pgd_current[smp_processor_id()] = init_mm.pgd; + cpu_data[smp_processor_id()].udelay_val = loops_per_jiffy; + cpu_data[smp_processor_id()].asid_cache = ASID_FIRST_VERSION; + prom_smp_finish(); printk("Slave cpu booted successfully\n"); atomic_inc(&cpus_booted); cpu_idle(); @@ -107,6 +114,8 @@ smp_num_cpus = prom_setup_smp(); init_new_context(current, &init_mm); current->processor = 0; + cpu_data[0].udelay_val = loops_per_jiffy; + cpu_data[0].asid_cache = ASID_FIRST_VERSION; atomic_set(&cpus_booted, 1); /* Master CPU is already booted... */ init_idle(); for (i = 1; i < smp_num_cpus; i++) { @@ -164,6 +173,7 @@ /* Wait for everyone to come up */ while (atomic_read(&cpus_booted) != smp_num_cpus); + smp_threads_ready = 1; } void __init smp_commence(void) @@ -176,12 +186,15 @@ current->need_resched = 1; } -void FASTCALL(smp_send_reschedule(int cpu)) +void smp_send_reschedule(int cpu) { smp_call_function(reschedule_this_cpu, NULL, 0, 0); } +static spinlock_t call_lock = SPIN_LOCK_UNLOCKED; +struct call_data_struct *call_data; + /* * The caller of this wants the passed function to run on every cpu. If wait * is set, wait until all cpus have finished the function before returning. @@ -190,43 +203,61 @@ int smp_call_function (void (*func) (void *info), void *info, int retry, int wait) { + struct call_data_struct data; int cpus = smp_num_cpus - 1; + int cpu = smp_processor_id(); int i; - if (smp_num_cpus < 2) { + if (!cpus) return 0; - } - spin_lock_bh(&smp_fn_call.lock); - - atomic_set(&smp_fn_call.finished, 0); - smp_fn_call.fn = func; - smp_fn_call.data = info; + data.func = func; + data.info = info; + atomic_set(&data.started, 0); + data.wait = wait; + if (wait) + atomic_set(&data.finished, 0); + spin_lock_bh(&call_lock); + call_data = &data; for (i = 0; i < smp_num_cpus; i++) { - if (i != smp_processor_id()) { + if (i != cpu) { /* Call the board specific routine */ - core_call_function(i); + core_send_ipi(i, SMP_CALL_FUNCTION); } } - if (wait) { - while(atomic_read(&smp_fn_call.finished) != cpus) {} - } + /* Wait for response */ + while (atomic_read(&data.started) != cpus) + barrier(); - spin_unlock_bh(&smp_fn_call.lock); + if (wait) + while(atomic_read(&data.finished) != cpus) + barrier(); + spin_unlock_bh(&call_lock); + return 0; } void synchronize_irq(void) { - panic("synchronize_irq"); + if (irqs_running()) { + /* Stupid approach */ + cli(); + sti(); + } } static void stop_this_cpu(void *dummy) { - printk("Cpu stopping\n"); - for (;;); + int cpu = smp_processor_id(); + if (cpu) + for (;;); /* XXX Use halt like i386 */ + else { + /* XXXKW this isn't quite there yet */ + while (!smp_cpu0_finalize) ; + smp_cpu0_finalize(); + } } void smp_send_stop(void) @@ -394,6 +425,104 @@ default: printk("global_restore_flags: %08lx\n", flags); } +} + +static void flush_tlb_all_ipi(void *info) +{ + local_flush_tlb_all(); +} + +void flush_tlb_all(void) +{ + smp_call_function(flush_tlb_all_ipi, 0, 1, 1); + local_flush_tlb_all(); +} + +static void flush_tlb_mm_ipi(void *mm) +{ + local_flush_tlb_mm((struct mm_struct *)mm); +} + +/* + * The following tlb flush calls are invoked when old translations are + * being torn down, or pte attributes are changing. For single threaded + * address spaces, a new context is obtained on the current cpu, and tlb + * context on other cpus are invalidated to force a new context allocation + * at switch_mm time, should the mm ever be used on other cpus. For + * multithreaded address spaces, intercpu interrupts have to be sent. + * Another case where intercpu interrupts are required is when the target + * mm might be active on another cpu (eg debuggers doing the flushes on + * behalf of debugees, kswapd stealing pages from another process etc). + * Kanoj 07/00. + */ + +void flush_tlb_mm(struct mm_struct *mm) +{ + if ((atomic_read(&mm->mm_users) != 1) || (current->mm != mm)) { + smp_call_function(flush_tlb_mm_ipi, (void *)mm, 1, 1); + } else { + int i; + for (i = 0; i < smp_num_cpus; i++) + if (smp_processor_id() != i) + CPU_CONTEXT(i, mm) = 0; + } + local_flush_tlb_mm(mm); +} + +struct flush_tlb_data { + struct mm_struct *mm; + struct vm_area_struct *vma; + unsigned long addr1; + unsigned long addr2; +}; + +static void flush_tlb_range_ipi(void *info) +{ + struct flush_tlb_data *fd = (struct flush_tlb_data *)info; + + local_flush_tlb_range(fd->mm, fd->addr1, fd->addr2); +} + +void flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) +{ + if ((atomic_read(&mm->mm_users) != 1) || (current->mm != mm)) { + struct flush_tlb_data fd; + + fd.mm = mm; + fd.addr1 = start; + fd.addr2 = end; + smp_call_function(flush_tlb_range_ipi, (void *)&fd, 1, 1); + } else { + int i; + for (i = 0; i < smp_num_cpus; i++) + if (smp_processor_id() != i) + CPU_CONTEXT(i, mm) = 0; + } + local_flush_tlb_range(mm, start, end); +} + +static void flush_tlb_page_ipi(void *info) +{ + struct flush_tlb_data *fd = (struct flush_tlb_data *)info; + + local_flush_tlb_page(fd->vma, fd->addr1); +} + +void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) +{ + if ((atomic_read(&vma->vm_mm->mm_users) != 1) || (current->mm != vma->vm_mm)) { + struct flush_tlb_data fd; + + fd.vma = vma; + fd.addr1 = page; + smp_call_function(flush_tlb_page_ipi, (void *)&fd, 1, 1); + } else { + int i; + for (i = 0; i < smp_num_cpus; i++) + if (smp_processor_id() != i) + CPU_CONTEXT(i, vma->vm_mm) = 0; + } + local_flush_tlb_page(vma, page); } EXPORT_SYMBOL(flush_tlb_page); |