📄 smp.c
字号:
/* * * * 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> * * PowerPC-64 Support added by Dave Engebretsen, Peter Bergner, and * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com * * 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 of the License, or (at your option) any later version. */#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 <linux/cache.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/naca.h>#include <asm/paca.h>#include <asm/iSeries/LparData.h>#include <asm/iSeries/HvCall.h>#include <asm/iSeries/HvCallCfg.h>#include <asm/time.h>#include <asm/ppcdebug.h>#include "open_pic.h"#include <asm/machdep.h>int smp_threads_ready = 0;volatile int smp_commenced = 0;int smp_num_cpus = 1;int smp_tb_synchronized = 0;extern atomic_t ipi_recv;extern atomic_t ipi_sent;spinlock_t kernel_flag __cacheline_aligned = SPIN_LOCK_UNLOCKED;cycles_t cacheflush_time;static int max_cpus __initdata = NR_CPUS;unsigned long cpu_online_map;volatile unsigned long cpu_callin_map[NR_CPUS] = {0,};#define TB_SYNC_PASSES 4volatile unsigned long __initdata tb_sync_flag = 0;volatile unsigned long __initdata tb_offset = 0;extern unsigned char stab_array[];int start_secondary(void *);extern int cpu_idle(void *unused);void smp_call_function_interrupt(void);void smp_message_pass(int target, int msg, unsigned long data, int wait);static unsigned long iSeries_smp_message[NR_CPUS];void xics_setup_cpu(void);void xics_cause_IPI(int cpu);/* * XICS only has a single IPI, so encode the messages per CPU */volatile unsigned long xics_ipi_message[NR_CPUS] = {0};#define smp_message_pass(t,m,d,w) \ do { atomic_inc(&ipi_sent); \ ppc_md.smp_message_pass((t),(m),(d),(w)); \ } while(0)#ifdef CONFIG_KDBvoid smp_kdb_stop(void){}#endifstatic inline void set_tb(unsigned int upper, unsigned int lower){ mtspr(SPRN_TBWL, 0); mtspr(SPRN_TBWU, upper); mtspr(SPRN_TBWL, lower);}void iSeries_smp_message_recv( struct pt_regs * regs ){ int cpu = smp_processor_id(); int msg; if ( smp_num_cpus < 2 ) return; for ( msg = 0; msg < 4; ++msg ) if ( test_and_clear_bit( msg, &iSeries_smp_message[cpu] ) ) smp_message_recv( msg, regs );}static void smp_iSeries_message_pass(int target, int msg, unsigned long data, int wait){ int i; for (i = 0; i < smp_num_cpus; ++i) { if ( (target == MSG_ALL) || (target == i) || ((target == MSG_ALL_BUT_SELF) && (i != smp_processor_id())) ) { set_bit( msg, &iSeries_smp_message[i] ); HvCall_sendIPI(&(paca[i])); } }}static int smp_iSeries_numProcs(void){ unsigned np, i; struct ItLpPaca * lpPaca; np = 0; for (i=0; i < MAX_PACAS; ++i) { lpPaca = paca[i].xLpPacaPtr; if ( lpPaca->xDynProcStatus < 2 ) { ++np; } } return np;}static int smp_iSeries_probe(void){ unsigned i; unsigned np; struct ItLpPaca * lpPaca; np = 0; for (i=0; i < MAX_PACAS; ++i) { lpPaca = paca[i].xLpPacaPtr; if ( lpPaca->xDynProcStatus < 2 ) { ++np; paca[i].next_jiffy_update_tb = paca[0].next_jiffy_update_tb; } } smp_tb_synchronized = 1; return np;}static void smp_iSeries_kick_cpu(int nr){ struct ItLpPaca * lpPaca; /* Verify we have a Paca for processor nr */ if ( ( nr <= 0 ) || ( nr >= MAX_PACAS ) ) return; /* Verify that our partition has a processor nr */ lpPaca = paca[nr].xLpPacaPtr; if ( lpPaca->xDynProcStatus >= 2 ) return; /* The information for processor bringup must * be written out to main store before we release * the processor. */ mb(); /* The processor is currently spinning, waiting * for the xProcStart field to become non-zero * After we set xProcStart, the processor will * continue on to secondary_start in iSeries_head.S */ paca[nr].xProcStart = 1;}static void smp_iSeries_setup_cpu(int nr){}/* This is called very early. */void smp_init_iSeries(void){ ppc_md.smp_message_pass = smp_iSeries_message_pass; ppc_md.smp_probe = smp_iSeries_probe; ppc_md.smp_kick_cpu = smp_iSeries_kick_cpu; ppc_md.smp_setup_cpu = smp_iSeries_setup_cpu; naca->processorCount = smp_iSeries_numProcs();}static voidsmp_openpic_message_pass(int target, int msg, unsigned long data, int wait){ /* make sure we're sending something that translates to an IPI */ if ( msg > 0x3 ){ printk("SMP %d: smp_message_pass: unknown msg %d\n", smp_processor_id(), msg); return; } switch ( target ) { case MSG_ALL: openpic_cause_IPI(msg, 0xffffffff); break; case MSG_ALL_BUT_SELF: openpic_cause_IPI(msg, 0xffffffff & ~(1 << smp_processor_id())); break; default: openpic_cause_IPI(msg, 1<<target); break; }}static intsmp_chrp_probe(void){ if (naca->processorCount > 1) openpic_request_IPIs(); return naca->processorCount;}static voidsmp_kick_cpu(int nr){ /* Verify we have a Paca for processor nr */ if ( ( nr <= 0 ) || ( nr >= MAX_PACAS ) ) return; /* The information for processor bringup must * be written out to main store before we release * the processor. */ mb(); /* The processor is currently spinning, waiting * for the xProcStart field to become non-zero * After we set xProcStart, the processor will * continue on to secondary_start in iSeries_head.S */ paca[nr].xProcStart = 1;}extern struct gettimeofday_struct do_gtod;static void smp_space_timers( unsigned nr ){ unsigned long offset, i; offset = tb_ticks_per_jiffy / nr; for ( i=1; i<nr; ++i ) { paca[i].next_jiffy_update_tb = paca[i-1].next_jiffy_update_tb + offset; }}static voidsmp_chrp_setup_cpu(int cpu_nr){ static atomic_t ready = ATOMIC_INIT(1); static volatile int frozen = 0; if (naca->platform == PLATFORM_PSERIES_LPAR) { /* timebases already synced under the hypervisor. */ paca[cpu_nr].next_jiffy_update_tb = tb_last_stamp = get_tb(); if (cpu_nr == 0) { do_gtod.tb_orig_stamp = tb_last_stamp; /* Should update do_gtod.stamp_xsec. * For now we leave it which means the time can be some * number of msecs off until someone does a settimeofday() */ } smp_tb_synchronized = 1; } else { if (cpu_nr == 0) { /* wait for all the others */ while (atomic_read(&ready) < smp_num_cpus) barrier(); atomic_set(&ready, 1); /* freeze the timebase */ rtas_call(rtas_token("freeze-time-base"), 0, 1, NULL); mb(); frozen = 1; set_tb(0, 0); paca[0].next_jiffy_update_tb = 0; smp_space_timers(smp_num_cpus); while (atomic_read(&ready) < smp_num_cpus) barrier(); /* thaw the timebase again */ rtas_call(rtas_token("thaw-time-base"), 0, 1, NULL); mb(); frozen = 0; tb_last_stamp = get_tb(); do_gtod.tb_orig_stamp = tb_last_stamp; smp_tb_synchronized = 1; } else { atomic_inc(&ready); while (!frozen) barrier(); set_tb(0, 0); mb(); atomic_inc(&ready); while (frozen) barrier(); } } if (OpenPIC_Addr) { do_openpic_setup_cpu(); } else { if (cpu_nr > 0) xics_setup_cpu(); }}static voidsmp_xics_message_pass(int target, int msg, unsigned long data, int wait){ int i; for (i = 0; i < smp_num_cpus; ++i) { if (target == MSG_ALL || target == i || (target == MSG_ALL_BUT_SELF && i != smp_processor_id())) { set_bit(msg, &xics_ipi_message[i]); mb(); xics_cause_IPI(i); } }}static intsmp_xics_probe(void){ return naca->processorCount;}/* This is called very early */void smp_init_pSeries(void){ if(naca->interrupt_controller == IC_OPEN_PIC) { ppc_md.smp_message_pass = smp_openpic_message_pass; ppc_md.smp_probe = smp_chrp_probe; ppc_md.smp_kick_cpu = smp_kick_cpu; ppc_md.smp_setup_cpu = smp_chrp_setup_cpu; } else { ppc_md.smp_message_pass = smp_xics_message_pass; ppc_md.smp_probe = smp_xics_probe; ppc_md.smp_kick_cpu = smp_kick_cpu; ppc_md.smp_setup_cpu = smp_chrp_setup_cpu; }}void smp_local_timer_interrupt(struct pt_regs * regs){ if (!--(get_paca()->prof_counter)) { update_process_times(user_mode(regs)); (get_paca()->prof_counter)=get_paca()->prof_multiplier; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -