📄 smp.c
字号:
/* * $Id: smp.c,v 1.68 1999/09/17 19:38:05 cort Exp $ * * Smp support for ppc. * * Written by Cort Dougan (cort@cs.nmt.edu) borrowing a great * deal of code from the sparc and intel versions. * * Copyright (C) 1999 Cort Dougan <cort@cs.nmt.edu> * * Support for PReP (Motorola MTX/MVME) SMP by Troy Benjegerdes * (troy@microux.com, hozer@drgw.net) */#include <linux/config.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/smp.h>#include <linux/smp_lock.h>#include <linux/interrupt.h>#include <linux/kernel_stat.h>#include <linux/delay.h>#define __KERNEL_SYSCALLS__#include <linux/unistd.h>#include <linux/init.h>#include <linux/openpic.h>#include <linux/spinlock.h>#include <asm/ptrace.h>#include <asm/atomic.h>#include <asm/irq.h>#include <asm/page.h>#include <asm/pgtable.h>#include <asm/hardirq.h>#include <asm/softirq.h>#include <asm/init.h>#include <asm/io.h>#include <asm/prom.h>#include <asm/smp.h>#include <asm/gemini.h>#include <asm/time.h>#include "open_pic.h"int smp_threads_ready;volatile int smp_commenced;int smp_num_cpus = 1;struct cpuinfo_PPC cpu_data[NR_CPUS];struct klock_info_struct klock_info = { KLOCK_CLEAR, 0 };volatile unsigned char active_kernel_processor = NO_PROC_ID; /* Processor holding kernel spinlock */volatile unsigned long ipi_count;spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED;unsigned int prof_multiplier[NR_CPUS];unsigned int prof_counter[NR_CPUS];cycles_t cacheflush_time;/* this has to go in the data section because it is accessed from prom_init */int smp_hw_index[NR_CPUS];/* all cpu mappings are 1-1 -- Cort */volatile unsigned long cpu_callin_map[NR_CPUS];int start_secondary(void *);extern int cpu_idle(void *unused);u_int openpic_read(volatile u_int *addr);void smp_call_function_interrupt(void);void smp_message_pass(int target, int msg, unsigned long data, int wait);/* register for interrupting the primary processor on the powersurge *//* N.B. this is actually the ethernet ROM! */#define PSURGE_PRI_INTR 0xf3019000/* register for interrupting the secondary processor on the powersurge */#define PSURGE_SEC_INTR 0xf80000c0/* register for storing the start address for the secondary processor */#define PSURGE_START 0xf2800000/* virtual addresses for the above */volatile u32 *psurge_pri_intr;volatile u32 *psurge_sec_intr;volatile u32 *psurge_start;/* Since OpenPIC has only 4 IPIs, we use slightly different message numbers. */#define PPC_MSG_CALL_FUNCTION 0#define PPC_MSG_RESCHEDULE 1#define PPC_MSG_INVALIDATE_TLB 2#define PPC_MSG_XMON_BREAK 3static inline void set_tb(unsigned int upper, unsigned int lower){ mtspr(SPRN_TBWU, upper); mtspr(SPRN_TBWL, lower);}void smp_local_timer_interrupt(struct pt_regs * regs){ int cpu = smp_processor_id(); if (!--prof_counter[cpu]) { update_process_times(user_mode(regs)); prof_counter[cpu]=prof_multiplier[cpu]; }}void smp_message_recv(int msg, struct pt_regs *regs){ ipi_count++; switch( msg ) { case PPC_MSG_CALL_FUNCTION: smp_call_function_interrupt(); break; case PPC_MSG_RESCHEDULE: current->need_resched = 1; break; case PPC_MSG_INVALIDATE_TLB: _tlbia(); break;#ifdef CONFIG_XMON case PPC_MSG_XMON_BREAK: xmon(regs); break;#endif /* CONFIG_XMON */ default: printk("SMP %d: smp_message_recv(): unknown msg %d\n", smp_processor_id(), msg); break; }}/* * As it is now, if we're sending two message at the same time * we have race conditions on Pmac. The PowerSurge doesn't easily * allow us to send IPI messages so we put the messages in * smp_message[]. * * This is because don't have several IPI's on the PowerSurge even though * we do on the chrp. It would be nice to use actual IPI's such as with * openpic rather than this. * -- Cort */int pmac_smp_message[NR_CPUS];void pmac_smp_message_recv(struct pt_regs *regs){ int cpu = smp_processor_id(); int msg; /* clear interrupt */ if (cpu == 1) out_be32(psurge_sec_intr, ~0); if (smp_num_cpus < 2) return; /* make sure there is a message there */ msg = pmac_smp_message[cpu]; if (msg == 0) return; /* reset message */ pmac_smp_message[cpu] = 0; smp_message_recv(msg - 1, regs);}voidpmac_primary_intr(int irq, void *d, struct pt_regs *regs){ pmac_smp_message_recv(regs);}/* * 750's don't broadcast tlb invalidates so * we have to emulate that behavior. * -- Cort */void smp_send_tlb_invalidate(int cpu){ if ( (_get_PVR()>>16) == 8 ) smp_message_pass(MSG_ALL_BUT_SELF, PPC_MSG_INVALIDATE_TLB, 0, 0);}void smp_send_reschedule(int cpu){ /* * This is only used if `cpu' is running an idle task, * so it will reschedule itself anyway... * * This isn't the case anymore since the other CPU could be * sleeping and won't reschedule until the next interrupt (such * as the timer). * -- Cort */ /* This is only used if `cpu' is running an idle task, so it will reschedule itself anyway... */ smp_message_pass(cpu, PPC_MSG_RESCHEDULE, 0, 0);}#ifdef CONFIG_XMONvoid smp_send_xmon_break(int cpu){ smp_message_pass(cpu, PPC_MSG_XMON_BREAK, 0, 0);}#endif /* CONFIG_XMON */static void stop_this_cpu(void *dummy){ __cli(); while (1) ;}void smp_send_stop(void){ smp_call_function(stop_this_cpu, NULL, 1, 0); smp_num_cpus = 1;}/* * Structure and data for smp_call_function(). This is designed to minimise * static memory requirements. It also looks cleaner. * Stolen from the i386 version. */static spinlock_t call_lock = SPIN_LOCK_UNLOCKED;static volatile struct call_data_struct { void (*func) (void *info); void *info; atomic_t started; atomic_t finished; int wait;} *call_data;/* * this function sends a 'generic call function' IPI to all other CPUs * in the system. */int smp_call_function (void (*func) (void *info), void *info, int nonatomic, int wait)/* * [SUMMARY] 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. * <nonatomic> currently unused. * <wait> If true, wait (atomically) 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. * * You must not call this function with disabled interrupts or from a * hardware interrupt handler, you may call it from a bottom half handler. */{ struct call_data_struct data; int ret = -1, cpus = smp_num_cpus-1; int timeout; if (!cpus) return 0; 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; /* Send a message to all other CPUs and wait for them to respond */ smp_message_pass(MSG_ALL_BUT_SELF, PPC_MSG_CALL_FUNCTION, 0, 0); /* Wait for response */ timeout = 1000000; while (atomic_read(&data.started) != cpus) { if (--timeout == 0) { printk("smp_call_function on cpu %d: other cpus not responding (%d)\n", smp_processor_id(), atomic_read(&data.started)); goto out; } barrier(); udelay(1); } if (wait) { timeout = 1000000; while (atomic_read(&data.finished) != cpus) { if (--timeout == 0) { printk("smp_call_function on cpu %d: other cpus not finishing (%d/%d)\n", smp_processor_id(), atomic_read(&data.finished), atomic_read(&data.started)); goto out; } barrier(); udelay(1); } } ret = 0; out: spin_unlock_bh(&call_lock); return ret;}void smp_call_function_interrupt(void){ void (*func) (void *info) = call_data->func; void *info = call_data->info; int wait = call_data->wait; /* * Notify initiating CPU that I've grabbed the data and am * about to execute the function */ atomic_inc(&call_data->started); /* * At this point the info structure may be out of scope unless wait==1 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -