2008-07-28 21:16:28 +04:00
/* Copyright 2008, 2005 Rusty Russell rusty@rustcorp.com.au IBM Corporation.
2006-09-29 13:01:35 +04:00
* GPL v2 and any later version .
*/
2005-04-17 02:20:36 +04:00
# include <linux/cpu.h>
# include <linux/err.h>
2007-05-08 11:25:08 +04:00
# include <linux/kthread.h>
# include <linux/module.h>
# include <linux/sched.h>
# include <linux/stop_machine.h>
2005-04-17 02:20:36 +04:00
# include <linux/syscalls.h>
2007-05-11 09:22:47 +04:00
# include <linux/interrupt.h>
2005-04-17 02:20:36 +04:00
# include <asm/atomic.h>
# include <asm/uaccess.h>
2008-07-28 21:16:28 +04:00
/* This controls the threads on each CPU. */
2005-04-17 02:20:36 +04:00
enum stopmachine_state {
2008-07-28 21:16:28 +04:00
/* Dummy starting state for thread. */
STOPMACHINE_NONE ,
/* Awaiting everyone to be scheduled. */
2005-04-17 02:20:36 +04:00
STOPMACHINE_PREPARE ,
2008-07-28 21:16:28 +04:00
/* Disable interrupts. */
2005-04-17 02:20:36 +04:00
STOPMACHINE_DISABLE_IRQ ,
2008-07-28 21:16:28 +04:00
/* Run the function */
2008-02-28 19:33:03 +03:00
STOPMACHINE_RUN ,
2008-07-28 21:16:28 +04:00
/* Exit */
2005-04-17 02:20:36 +04:00
STOPMACHINE_EXIT ,
} ;
2008-07-28 21:16:28 +04:00
static enum stopmachine_state state ;
2005-04-17 02:20:36 +04:00
2008-02-28 19:33:03 +03:00
struct stop_machine_data {
int ( * fn ) ( void * ) ;
void * data ;
2008-07-28 21:16:28 +04:00
int fnret ;
} ;
2008-02-28 19:33:03 +03:00
2008-07-28 21:16:28 +04:00
/* Like num_online_cpus(), but hotplug cpu uses us, so we need this. */
static unsigned int num_threads ;
static atomic_t thread_ack ;
static struct completion finished ;
static DEFINE_MUTEX ( lock ) ;
2005-04-17 02:20:36 +04:00
2008-07-28 21:16:28 +04:00
static void set_state ( enum stopmachine_state newstate )
2005-04-17 02:20:36 +04:00
{
2008-07-28 21:16:28 +04:00
/* Reset ack counter. */
atomic_set ( & thread_ack , num_threads ) ;
smp_wmb ( ) ;
state = newstate ;
2005-04-17 02:20:36 +04:00
}
2008-07-28 21:16:28 +04:00
/* Last one to ack a state moves to the next state. */
static void ack_state ( void )
2005-04-17 02:20:36 +04:00
{
2008-07-28 21:16:28 +04:00
if ( atomic_dec_and_test ( & thread_ack ) ) {
/* If we're the last one to ack the EXIT, we're finished. */
if ( state = = STOPMACHINE_EXIT )
complete ( & finished ) ;
else
set_state ( state + 1 ) ;
}
2005-04-17 02:20:36 +04:00
}
2008-07-28 21:16:28 +04:00
/* This is the actual thread which stops the CPU. It exits by itself rather
* than waiting for kthread_stop ( ) , because it ' s easier for hotplug CPU . */
static int stop_cpu ( struct stop_machine_data * smdata )
2005-04-17 02:20:36 +04:00
{
2008-07-28 21:16:28 +04:00
enum stopmachine_state curstate = STOPMACHINE_NONE ;
int uninitialized_var ( ret ) ;
2005-04-17 02:20:36 +04:00
2008-07-28 21:16:28 +04:00
/* Simple state machine */
do {
/* Chill out and ensure we re-read stopmachine_state. */
2008-05-08 17:20:38 +04:00
cpu_relax ( ) ;
2008-07-28 21:16:28 +04:00
if ( state ! = curstate ) {
curstate = state ;
switch ( curstate ) {
case STOPMACHINE_DISABLE_IRQ :
local_irq_disable ( ) ;
hard_irq_disable ( ) ;
break ;
case STOPMACHINE_RUN :
/* |= allows error detection if functions on
* multiple CPUs . */
smdata - > fnret | = smdata - > fn ( smdata - > data ) ;
break ;
default :
break ;
}
ack_state ( ) ;
}
} while ( curstate ! = STOPMACHINE_EXIT ) ;
2005-04-17 02:20:36 +04:00
local_irq_enable ( ) ;
2008-07-28 21:16:28 +04:00
do_exit ( 0 ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-28 21:16:28 +04:00
/* Callback for CPUs which aren't supposed to do anything. */
static int chill ( void * unused )
2008-02-28 19:33:03 +03:00
{
2008-07-28 21:16:28 +04:00
return 0 ;
2008-02-28 19:33:03 +03:00
}
2005-04-17 02:20:36 +04:00
2008-07-28 21:16:28 +04:00
int __stop_machine_run ( int ( * fn ) ( void * ) , void * data , unsigned int cpu )
2005-04-17 02:20:36 +04:00
{
2008-07-28 21:16:28 +04:00
int i , err ;
struct stop_machine_data active , idle ;
struct task_struct * * threads ;
active . fn = fn ;
active . data = data ;
active . fnret = 0 ;
idle . fn = chill ;
idle . data = NULL ;
/* If they don't care which cpu fn runs on, just pick one. */
if ( cpu = = NR_CPUS )
cpu = any_online_cpu ( cpu_online_map ) ;
/* This could be too big for stack on large machines. */
threads = kcalloc ( NR_CPUS , sizeof ( threads [ 0 ] ) , GFP_KERNEL ) ;
if ( ! threads )
return - ENOMEM ;
/* Set up initial state. */
mutex_lock ( & lock ) ;
init_completion ( & finished ) ;
num_threads = num_online_cpus ( ) ;
set_state ( STOPMACHINE_PREPARE ) ;
2005-04-17 02:20:36 +04:00
2008-07-28 21:16:28 +04:00
for_each_online_cpu ( i ) {
struct stop_machine_data * smdata ;
struct sched_param param = { . sched_priority = MAX_RT_PRIO - 1 } ;
2005-04-17 02:20:36 +04:00
2008-07-28 21:16:28 +04:00
if ( cpu = = ALL_CPUS | | i = = cpu )
smdata = & active ;
else
smdata = & idle ;
threads [ i ] = kthread_create ( ( void * ) stop_cpu , smdata , " kstop%u " ,
i ) ;
if ( IS_ERR ( threads [ i ] ) ) {
err = PTR_ERR ( threads [ i ] ) ;
threads [ i ] = NULL ;
goto kill_threads ;
}
2005-04-17 02:20:36 +04:00
2008-07-28 21:16:28 +04:00
/* Place it onto correct cpu. */
kthread_bind ( threads [ i ] , i ) ;
2005-04-17 02:20:36 +04:00
2008-07-28 21:16:28 +04:00
/* Make it highest prio. */
if ( sched_setscheduler_nocheck ( threads [ i ] , SCHED_FIFO , & param ) )
BUG ( ) ;
}
2005-04-17 02:20:36 +04:00
2008-07-28 21:16:28 +04:00
/* We've created all the threads. Wake them all: hold this CPU so one
* doesn ' t hit this CPU until we ' re ready . */
cpu = get_cpu ( ) ;
for_each_online_cpu ( i )
wake_up_process ( threads [ i ] ) ;
2008-02-28 19:33:03 +03:00
2008-07-28 21:16:28 +04:00
/* This will release the thread on our CPU. */
put_cpu ( ) ;
wait_for_completion ( & finished ) ;
mutex_unlock ( & lock ) ;
2005-04-17 02:20:36 +04:00
2008-07-28 21:16:28 +04:00
kfree ( threads ) ;
2005-04-17 02:20:36 +04:00
2008-07-28 21:16:28 +04:00
return active . fnret ;
2005-04-17 02:20:36 +04:00
2008-07-28 21:16:28 +04:00
kill_threads :
for_each_online_cpu ( i )
if ( threads [ i ] )
kthread_stop ( threads [ i ] ) ;
mutex_unlock ( & lock ) ;
2007-07-16 10:39:47 +04:00
2008-07-28 21:16:28 +04:00
kfree ( threads ) ;
return err ;
2005-04-17 02:20:36 +04:00
}
int stop_machine_run ( int ( * fn ) ( void * ) , void * data , unsigned int cpu )
{
int ret ;
/* No CPUs can come up or down during this. */
2008-01-25 23:08:02 +03:00
get_online_cpus ( ) ;
2008-07-28 21:16:28 +04:00
ret = __stop_machine_run ( fn , data , cpu ) ;
2008-01-25 23:08:02 +03:00
put_online_cpus ( ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2007-05-08 11:25:08 +04:00
EXPORT_SYMBOL_GPL ( stop_machine_run ) ;