2006-03-27 01:14:30 -08:00
/*
2005-04-16 15:20:36 -07:00
* Copyright ( C ) 2000 - 2003 Jeff Dike ( jdike @ addtoit . com )
* Licensed under the GPL
*/
# include "linux/percpu.h"
# include "asm/pgalloc.h"
# include "asm/tlb.h"
/* For some reason, mmu_gathers are referenced when CONFIG_SMP is off. */
DEFINE_PER_CPU ( struct mmu_gather , mmu_gathers ) ;
# ifdef CONFIG_SMP
# include "linux/sched.h"
# include "linux/module.h"
# include "linux/threads.h"
# include "linux/interrupt.h"
# include "linux/err.h"
# include "linux/hardirq.h"
# include "asm/smp.h"
# include "asm/processor.h"
# include "asm/spinlock.h"
# include "user_util.h"
# include "kern_util.h"
# include "kern.h"
# include "irq_user.h"
# include "os.h"
/* CPU online map, set by smp_boot_cpus */
cpumask_t cpu_online_map = CPU_MASK_NONE ;
cpumask_t cpu_possible_map = CPU_MASK_NONE ;
EXPORT_SYMBOL ( cpu_online_map ) ;
EXPORT_SYMBOL ( cpu_possible_map ) ;
/* Per CPU bogomips and other parameters
* The only piece used here is the ipi pipe , which is set before SMP is
* started and never changed .
*/
struct cpuinfo_um cpu_data [ NR_CPUS ] ;
/* A statistic, can be a little off */
int num_reschedules_sent = 0 ;
/* Not changed after boot */
struct task_struct * idle_threads [ NR_CPUS ] ;
void smp_send_reschedule ( int cpu )
{
os_write_file ( cpu_data [ cpu ] . ipi_pipe [ 1 ] , " R " , 1 ) ;
num_reschedules_sent + + ;
}
void smp_send_stop ( void )
{
int i ;
printk ( KERN_INFO " Stopping all CPUs... " ) ;
for ( i = 0 ; i < num_online_cpus ( ) ; i + + ) {
if ( i = = current_thread - > cpu )
continue ;
os_write_file ( cpu_data [ i ] . ipi_pipe [ 1 ] , " S " , 1 ) ;
}
printk ( " done \n " ) ;
}
static cpumask_t smp_commenced_mask = CPU_MASK_NONE ;
static cpumask_t cpu_callin_map = CPU_MASK_NONE ;
static int idle_proc ( void * cpup )
{
int cpu = ( int ) cpup , err ;
err = os_pipe ( cpu_data [ cpu ] . ipi_pipe , 1 , 1 ) ;
if ( err < 0 )
panic ( " CPU#%d failed to create IPI pipe, err = %d " , cpu , - err ) ;
2006-03-27 01:14:30 -08:00
os_set_fd_async ( cpu_data [ cpu ] . ipi_pipe [ 0 ] ,
2005-04-16 15:20:36 -07:00
current - > thread . mode . tt . extern_pid ) ;
2006-03-27 01:14:30 -08:00
2005-04-16 15:20:36 -07:00
wmb ( ) ;
if ( cpu_test_and_set ( cpu , cpu_callin_map ) ) {
printk ( " huh, CPU#%d already present?? \n " , cpu ) ;
BUG ( ) ;
}
while ( ! cpu_isset ( cpu , smp_commenced_mask ) )
cpu_relax ( ) ;
cpu_set ( cpu , cpu_online_map ) ;
default_idle ( ) ;
return ( 0 ) ;
}
static struct task_struct * idle_thread ( int cpu )
{
struct task_struct * new_task ;
unsigned char c ;
current - > thread . request . u . thread . proc = idle_proc ;
current - > thread . request . u . thread . arg = ( void * ) cpu ;
new_task = fork_idle ( cpu ) ;
if ( IS_ERR ( new_task ) )
panic ( " copy_process failed in idle_thread, error = %ld " ,
PTR_ERR ( new_task ) ) ;
2006-03-27 01:14:30 -08:00
cpu_tasks [ cpu ] = ( ( struct cpu_task )
2005-04-16 15:20:36 -07:00
{ . pid = new_task - > thread . mode . tt . extern_pid ,
. task = new_task } ) ;
idle_threads [ cpu ] = new_task ;
CHOOSE_MODE ( os_write_file ( new_task - > thread . mode . tt . switch_pipe [ 1 ] , & c ,
sizeof ( c ) ) ,
( { panic ( " skas mode doesn't support SMP " ) ; } ) ) ;
return ( new_task ) ;
}
void smp_prepare_cpus ( unsigned int maxcpus )
{
struct task_struct * idle ;
unsigned long waittime ;
int err , cpu , me = smp_processor_id ( ) ;
int i ;
for ( i = 0 ; i < ncpus ; + + i )
cpu_set ( i , cpu_possible_map ) ;
cpu_clear ( me , cpu_online_map ) ;
cpu_set ( me , cpu_online_map ) ;
cpu_set ( me , cpu_callin_map ) ;
err = os_pipe ( cpu_data [ me ] . ipi_pipe , 1 , 1 ) ;
if ( err < 0 )
panic ( " CPU#0 failed to create IPI pipe, errno = %d " , - err ) ;
2006-03-27 01:14:30 -08:00
os_set_fd_async ( cpu_data [ me ] . ipi_pipe [ 0 ] ,
2005-04-16 15:20:36 -07:00
current - > thread . mode . tt . extern_pid ) ;
for ( cpu = 1 ; cpu < ncpus ; cpu + + ) {
printk ( " Booting processor %d... \n " , cpu ) ;
2006-03-27 01:14:30 -08:00
2005-04-16 15:20:36 -07:00
idle = idle_thread ( cpu ) ;
init_idle ( idle , cpu ) ;
waittime = 200000000 ;
while ( waittime - - & & ! cpu_isset ( cpu , cpu_callin_map ) )
cpu_relax ( ) ;
if ( cpu_isset ( cpu , cpu_callin_map ) )
printk ( " done \n " ) ;
else printk ( " failed \n " ) ;
}
}
void smp_prepare_boot_cpu ( void )
{
cpu_set ( smp_processor_id ( ) , cpu_online_map ) ;
}
int __cpu_up ( unsigned int cpu )
{
cpu_set ( cpu , smp_commenced_mask ) ;
while ( ! cpu_isset ( cpu , cpu_online_map ) )
mb ( ) ;
return ( 0 ) ;
}
int setup_profiling_timer ( unsigned int multiplier )
{
printk ( KERN_INFO " setup_profiling_timer \n " ) ;
return ( 0 ) ;
}
void smp_call_function_slave ( int cpu ) ;
void IPI_handler ( int cpu )
{
unsigned char c ;
int fd ;
fd = cpu_data [ cpu ] . ipi_pipe [ 0 ] ;
while ( os_read_file ( fd , & c , 1 ) = = 1 ) {
switch ( c ) {
case ' C ' :
smp_call_function_slave ( cpu ) ;
break ;
case ' R ' :
set_tsk_need_resched ( current ) ;
break ;
case ' S ' :
printk ( " CPU#%d stopping \n " , cpu ) ;
while ( 1 )
pause ( ) ;
break ;
default :
printk ( " CPU#%d received unknown IPI [%c]! \n " , cpu , c ) ;
break ;
}
}
}
int hard_smp_processor_id ( void )
{
return ( pid_to_processor_id ( os_getpid ( ) ) ) ;
}
static DEFINE_SPINLOCK ( call_lock ) ;
static atomic_t scf_started ;
static atomic_t scf_finished ;
static void ( * func ) ( void * info ) ;
static void * info ;
void smp_call_function_slave ( int cpu )
{
atomic_inc ( & scf_started ) ;
( * func ) ( info ) ;
atomic_inc ( & scf_finished ) ;
}
2006-03-27 01:14:30 -08:00
int smp_call_function ( void ( * _func ) ( void * info ) , void * _info , int nonatomic ,
2005-04-16 15:20:36 -07:00
int wait )
{
int cpus = num_online_cpus ( ) - 1 ;
int i ;
if ( ! cpus )
return 0 ;
/* Can deadlock when called with interrupts disabled */
WARN_ON ( irqs_disabled ( ) ) ;
spin_lock_bh ( & call_lock ) ;
atomic_set ( & scf_started , 0 ) ;
atomic_set ( & scf_finished , 0 ) ;
func = _func ;
info = _info ;
for_each_online_cpu ( i )
os_write_file ( cpu_data [ i ] . ipi_pipe [ 1 ] , " C " , 1 ) ;
while ( atomic_read ( & scf_started ) ! = cpus )
barrier ( ) ;
if ( wait )
while ( atomic_read ( & scf_finished ) ! = cpus )
barrier ( ) ;
spin_unlock_bh ( & call_lock ) ;
return 0 ;
}
# endif
/*
* Overrides for Emacs so that we follow Linus ' s tabbing style .
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only . This must remain at the end
* of the file .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Local variables :
* c - file - style : " linux "
* End :
*/