2010-05-06 20:49:20 +04:00
/*
* kernel / stop_machine . c
*
* Copyright ( C ) 2008 , 2005 IBM Corporation .
* Copyright ( C ) 2008 , 2005 Rusty Russell rusty @ rustcorp . com . au
* Copyright ( C ) 2010 SUSE Linux Products GmbH
* Copyright ( C ) 2010 Tejun Heo < tj @ kernel . org >
*
* This file is released under the GPLv2 and any later version .
2006-09-29 13:01:35 +04:00
*/
2010-05-06 20:49:20 +04:00
# include <linux/completion.h>
2005-04-17 02:20:36 +04:00
# include <linux/cpu.h>
2010-05-06 20:49:20 +04:00
# include <linux/init.h>
2007-05-08 11:25:08 +04:00
# include <linux/kthread.h>
2011-05-23 22:51:41 +04:00
# include <linux/export.h>
2010-05-06 20:49:20 +04:00
# include <linux/percpu.h>
2007-05-08 11:25:08 +04:00
# include <linux/sched.h>
# include <linux/stop_machine.h>
2007-05-11 09:22:47 +04:00
# include <linux/interrupt.h>
2010-05-06 20:49:20 +04:00
# include <linux/kallsyms.h>
2013-01-31 16:11:14 +04:00
# include <linux/smpboot.h>
2011-07-27 03:09:06 +04:00
# include <linux/atomic.h>
2013-11-01 18:41:46 +04:00
# include <linux/lglock.h>
2010-05-06 20:49:20 +04:00
/*
* Structure to determine completion condition and record errors . May
* be shared by works on different cpus .
*/
struct cpu_stop_done {
atomic_t nr_todo ; /* nr left to execute */
bool executed ; /* actually executed? */
int ret ; /* collected return value */
struct completion completion ; /* fired if nr_todo reaches 0 */
} ;
/* the actual stopper, one per every possible cpu, enabled on online cpus */
struct cpu_stopper {
spinlock_t lock ;
2010-08-10 04:20:34 +04:00
bool enabled ; /* is this stopper enabled? */
2010-05-06 20:49:20 +04:00
struct list_head works ; /* list of pending works */
} ;
static DEFINE_PER_CPU ( struct cpu_stopper , cpu_stopper ) ;
2013-01-31 16:11:13 +04:00
static DEFINE_PER_CPU ( struct task_struct * , cpu_stopper_task ) ;
2011-11-01 04:11:15 +04:00
static bool stop_machine_initialized = false ;
2010-05-06 20:49:20 +04:00
2013-11-01 18:41:46 +04:00
/*
* Avoids a race between stop_two_cpus and global stop_cpus , where
* the stoppers could get queued up in reverse order , leading to
* system deadlock . Using an lglock means stop_two_cpus remains
* relatively cheap .
*/
DEFINE_STATIC_LGLOCK ( stop_cpus_lock ) ;
2010-05-06 20:49:20 +04:00
static void cpu_stop_init_done ( struct cpu_stop_done * done , unsigned int nr_todo )
{
memset ( done , 0 , sizeof ( * done ) ) ;
atomic_set ( & done - > nr_todo , nr_todo ) ;
init_completion ( & done - > completion ) ;
}
/* signal completion unless @done is NULL */
static void cpu_stop_signal_done ( struct cpu_stop_done * done , bool executed )
{
if ( done ) {
if ( executed )
done - > executed = true ;
if ( atomic_dec_and_test ( & done - > nr_todo ) )
complete ( & done - > completion ) ;
}
}
/* queue @work to @stopper. if offline, @work is completed immediately */
2013-01-31 16:11:13 +04:00
static void cpu_stop_queue_work ( unsigned int cpu , struct cpu_stop_work * work )
2010-05-06 20:49:20 +04:00
{
2013-01-31 16:11:13 +04:00
struct cpu_stopper * stopper = & per_cpu ( cpu_stopper , cpu ) ;
struct task_struct * p = per_cpu ( cpu_stopper_task , cpu ) ;
2010-05-06 20:49:20 +04:00
unsigned long flags ;
spin_lock_irqsave ( & stopper - > lock , flags ) ;
if ( stopper - > enabled ) {
list_add_tail ( & work - > list , & stopper - > works ) ;
2013-01-31 16:11:13 +04:00
wake_up_process ( p ) ;
2010-05-06 20:49:20 +04:00
} else
cpu_stop_signal_done ( work - > done , false ) ;
spin_unlock_irqrestore ( & stopper - > lock , flags ) ;
}
/**
* stop_one_cpu - stop a cpu
* @ cpu : cpu to stop
* @ fn : function to execute
* @ arg : argument to @ fn
*
* Execute @ fn ( @ arg ) on @ cpu . @ fn is run in a process context with
* the highest priority preempting any task on the cpu and
* monopolizing it . This function returns after the execution is
* complete .
*
* This function doesn ' t guarantee @ cpu stays online till @ fn
* completes . If @ cpu goes down in the middle , execution may happen
* partially or fully on different cpus . @ fn should either be ready
* for that or the caller should ensure that @ cpu stays online until
* this function completes .
*
* CONTEXT :
* Might sleep .
*
* RETURNS :
* - ENOENT if @ fn ( @ arg ) was not executed because @ cpu was offline ;
* otherwise , the return value of @ fn .
*/
int stop_one_cpu ( unsigned int cpu , cpu_stop_fn_t fn , void * arg )
{
struct cpu_stop_done done ;
struct cpu_stop_work work = { . fn = fn , . arg = arg , . done = & done } ;
cpu_stop_init_done ( & done , 1 ) ;
2013-01-31 16:11:13 +04:00
cpu_stop_queue_work ( cpu , & work ) ;
2010-05-06 20:49:20 +04:00
wait_for_completion ( & done . completion ) ;
return done . executed ? done . ret : - ENOENT ;
}
2013-10-07 14:29:15 +04:00
/* This controls the threads on each CPU. */
enum multi_stop_state {
/* Dummy starting state for thread. */
MULTI_STOP_NONE ,
/* Awaiting everyone to be scheduled. */
MULTI_STOP_PREPARE ,
/* Disable interrupts. */
MULTI_STOP_DISABLE_IRQ ,
/* Run the function */
MULTI_STOP_RUN ,
/* Exit */
MULTI_STOP_EXIT ,
} ;
struct multi_stop_data {
int ( * fn ) ( void * ) ;
void * data ;
/* Like num_online_cpus(), but hotplug cpu uses us, so we need this. */
unsigned int num_threads ;
const struct cpumask * active_cpus ;
enum multi_stop_state state ;
atomic_t thread_ack ;
} ;
static void set_state ( struct multi_stop_data * msdata ,
enum multi_stop_state newstate )
{
/* Reset ack counter. */
atomic_set ( & msdata - > thread_ack , msdata - > num_threads ) ;
smp_wmb ( ) ;
msdata - > state = newstate ;
}
/* Last one to ack a state moves to the next state. */
static void ack_state ( struct multi_stop_data * msdata )
{
if ( atomic_dec_and_test ( & msdata - > thread_ack ) )
set_state ( msdata , msdata - > state + 1 ) ;
}
/* This is the cpu_stop function which stops the CPU. */
static int multi_cpu_stop ( void * data )
{
struct multi_stop_data * msdata = data ;
enum multi_stop_state curstate = MULTI_STOP_NONE ;
int cpu = smp_processor_id ( ) , err = 0 ;
unsigned long flags ;
bool is_active ;
/*
* When called from stop_machine_from_inactive_cpu ( ) , irq might
* already be disabled . Save the state and restore it on exit .
*/
local_save_flags ( flags ) ;
if ( ! msdata - > active_cpus )
is_active = cpu = = cpumask_first ( cpu_online_mask ) ;
else
is_active = cpumask_test_cpu ( cpu , msdata - > active_cpus ) ;
/* Simple state machine */
do {
/* Chill out and ensure we re-read multi_stop_state. */
cpu_relax ( ) ;
if ( msdata - > state ! = curstate ) {
curstate = msdata - > state ;
switch ( curstate ) {
case MULTI_STOP_DISABLE_IRQ :
local_irq_disable ( ) ;
hard_irq_disable ( ) ;
break ;
case MULTI_STOP_RUN :
if ( is_active )
err = msdata - > fn ( msdata - > data ) ;
break ;
default :
break ;
}
ack_state ( msdata ) ;
}
} while ( curstate ! = MULTI_STOP_EXIT ) ;
local_irq_restore ( flags ) ;
return err ;
}
/**
* stop_two_cpus - stops two cpus
* @ cpu1 : the cpu to stop
* @ cpu2 : the other cpu to stop
* @ fn : function to execute
* @ arg : argument to @ fn
*
* Stops both the current and specified CPU and runs @ fn on one of them .
*
* returns when both are completed .
*/
int stop_two_cpus ( unsigned int cpu1 , unsigned int cpu2 , cpu_stop_fn_t fn , void * arg )
{
struct cpu_stop_done done ;
struct cpu_stop_work work1 , work2 ;
2013-10-11 16:38:20 +04:00
struct multi_stop_data msdata ;
preempt_disable ( ) ;
msdata = ( struct multi_stop_data ) {
2013-10-07 14:29:15 +04:00
. fn = fn ,
. data = arg ,
. num_threads = 2 ,
. active_cpus = cpumask_of ( cpu1 ) ,
} ;
work1 = work2 = ( struct cpu_stop_work ) {
. fn = multi_cpu_stop ,
. arg = & msdata ,
. done = & done
} ;
cpu_stop_init_done ( & done , 2 ) ;
set_state ( & msdata , MULTI_STOP_PREPARE ) ;
2013-10-11 16:38:20 +04:00
/*
* If we observe both CPUs active we know _cpu_down ( ) cannot yet have
* queued its stop_machine works and therefore ours will get executed
* first . Or its not either one of our CPUs that ' s getting unplugged ,
* in which case we don ' t care .
*
* This relies on the stopper workqueues to be FIFO .
*/
if ( ! cpu_active ( cpu1 ) | | ! cpu_active ( cpu2 ) ) {
preempt_enable ( ) ;
return - ENOENT ;
}
sched/stop_machine: Fix deadlock between multiple stop_two_cpus()
Jiri reported a machine stuck in multi_cpu_stop() with
migrate_swap_stop() as function and with the following src,dst cpu
pairs: {11, 4} {13, 11} { 4, 13}
4 11 13
cpuM: queue(4 ,13)
*Ma
cpuN: queue(13,11)
*N Na
*M Mb
cpuO: queue(11, 4)
*O Oa
*Nb
*Ob
Where *X denotes the cpu running the queueing of cpu-X and X[ab] denotes
the first/second queued work.
You'll observe the top of the workqueue for each cpu: 4,11,13 to be work
from cpus: M, O, N resp. IOW. deadlock.
Do away with the queueing trickery and introduce lg_double_lock() to
lock both CPUs and fully serialize the stop_two_cpus() callers instead
of the partial (and buggy) serialization we have now.
Reported-by: Jiri Olsa <jolsa@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Rik van Riel <riel@redhat.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/20150605153023.GH19282@twins.programming.kicks-ass.net
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2015-06-05 18:30:23 +03:00
lg_double_lock ( & stop_cpus_lock , cpu1 , cpu2 ) ;
cpu_stop_queue_work ( cpu1 , & work1 ) ;
cpu_stop_queue_work ( cpu2 , & work2 ) ;
lg_double_unlock ( & stop_cpus_lock , cpu1 , cpu2 ) ;
2013-10-11 16:38:20 +04:00
preempt_enable ( ) ;
2013-10-07 14:29:15 +04:00
wait_for_completion ( & done . completion ) ;
2013-10-11 16:38:20 +04:00
2013-10-07 14:29:15 +04:00
return done . executed ? done . ret : - ENOENT ;
}
2010-05-06 20:49:20 +04:00
/**
* stop_one_cpu_nowait - stop a cpu but don ' t wait for completion
* @ cpu : cpu to stop
* @ fn : function to execute
* @ arg : argument to @ fn
2014-06-05 03:11:22 +04:00
* @ work_buf : pointer to cpu_stop_work structure
2010-05-06 20:49:20 +04:00
*
* Similar to stop_one_cpu ( ) but doesn ' t wait for completion . The
* caller is responsible for ensuring @ work_buf is currently unused
* and will remain untouched until stopper starts executing @ fn .
*
* CONTEXT :
* Don ' t care .
*/
void stop_one_cpu_nowait ( unsigned int cpu , cpu_stop_fn_t fn , void * arg ,
struct cpu_stop_work * work_buf )
{
* work_buf = ( struct cpu_stop_work ) { . fn = fn , . arg = arg , } ;
2013-01-31 16:11:13 +04:00
cpu_stop_queue_work ( cpu , work_buf ) ;
2010-05-06 20:49:20 +04:00
}
/* static data for stop_cpus */
2011-06-23 22:19:29 +04:00
static DEFINE_MUTEX ( stop_cpus_mutex ) ;
2010-05-06 20:49:20 +04:00
static DEFINE_PER_CPU ( struct cpu_stop_work , stop_cpus_work ) ;
2011-06-23 22:19:27 +04:00
static void queue_stop_cpus_work ( const struct cpumask * cpumask ,
cpu_stop_fn_t fn , void * arg ,
struct cpu_stop_done * done )
2010-05-06 20:49:20 +04:00
{
struct cpu_stop_work * work ;
unsigned int cpu ;
/* initialize works and done */
for_each_cpu ( cpu , cpumask ) {
work = & per_cpu ( stop_cpus_work , cpu ) ;
work - > fn = fn ;
work - > arg = arg ;
2011-06-23 22:19:27 +04:00
work - > done = done ;
2010-05-06 20:49:20 +04:00
}
/*
* Disable preemption while queueing to avoid getting
* preempted by a stopper which might wait for other stoppers
* to enter @ fn which can lead to deadlock .
*/
2013-11-01 18:41:46 +04:00
lg_global_lock ( & stop_cpus_lock ) ;
2010-05-06 20:49:20 +04:00
for_each_cpu ( cpu , cpumask )
2013-01-31 16:11:13 +04:00
cpu_stop_queue_work ( cpu , & per_cpu ( stop_cpus_work , cpu ) ) ;
2013-11-01 18:41:46 +04:00
lg_global_unlock ( & stop_cpus_lock ) ;
2011-06-23 22:19:27 +04:00
}
2010-05-06 20:49:20 +04:00
2011-06-23 22:19:27 +04:00
static int __stop_cpus ( const struct cpumask * cpumask ,
cpu_stop_fn_t fn , void * arg )
{
struct cpu_stop_done done ;
cpu_stop_init_done ( & done , cpumask_weight ( cpumask ) ) ;
queue_stop_cpus_work ( cpumask , fn , arg , & done ) ;
2010-05-06 20:49:20 +04:00
wait_for_completion ( & done . completion ) ;
return done . executed ? done . ret : - ENOENT ;
}
/**
* stop_cpus - stop multiple cpus
* @ cpumask : cpus to stop
* @ fn : function to execute
* @ arg : argument to @ fn
*
* Execute @ fn ( @ arg ) on online cpus in @ cpumask . On each target cpu ,
* @ fn is run in a process context with the highest priority
* preempting any task on the cpu and monopolizing it . This function
* returns after all executions are complete .
*
* This function doesn ' t guarantee the cpus in @ cpumask stay online
* till @ fn completes . If some cpus go down in the middle , execution
* on the cpu may happen partially or fully on different cpus . @ fn
* should either be ready for that or the caller should ensure that
* the cpus stay online until this function completes .
*
* All stop_cpus ( ) calls are serialized making it safe for @ fn to wait
* for all cpus to start executing it .
*
* CONTEXT :
* Might sleep .
*
* RETURNS :
* - ENOENT if @ fn ( @ arg ) was not executed at all because all cpus in
* @ cpumask were offline ; otherwise , 0 if all executions of @ fn
* returned 0 , any non zero return value if any returned non zero .
*/
int stop_cpus ( const struct cpumask * cpumask , cpu_stop_fn_t fn , void * arg )
{
int ret ;
/* static works are used, process one request at a time */
mutex_lock ( & stop_cpus_mutex ) ;
ret = __stop_cpus ( cpumask , fn , arg ) ;
mutex_unlock ( & stop_cpus_mutex ) ;
return ret ;
}
/**
* try_stop_cpus - try to stop multiple cpus
* @ cpumask : cpus to stop
* @ fn : function to execute
* @ arg : argument to @ fn
*
* Identical to stop_cpus ( ) except that it fails with - EAGAIN if
* someone else is already using the facility .
*
* CONTEXT :
* Might sleep .
*
* RETURNS :
* - EAGAIN if someone else is already stopping cpus , - ENOENT if
* @ fn ( @ arg ) was not executed at all because all cpus in @ cpumask were
* offline ; otherwise , 0 if all executions of @ fn returned 0 , any non
* zero return value if any returned non zero .
*/
int try_stop_cpus ( const struct cpumask * cpumask , cpu_stop_fn_t fn , void * arg )
{
int ret ;
/* static works are used, process one request at a time */
if ( ! mutex_trylock ( & stop_cpus_mutex ) )
return - EAGAIN ;
ret = __stop_cpus ( cpumask , fn , arg ) ;
mutex_unlock ( & stop_cpus_mutex ) ;
return ret ;
}
2013-01-31 16:11:14 +04:00
static int cpu_stop_should_run ( unsigned int cpu )
{
struct cpu_stopper * stopper = & per_cpu ( cpu_stopper , cpu ) ;
unsigned long flags ;
int run ;
spin_lock_irqsave ( & stopper - > lock , flags ) ;
run = ! list_empty ( & stopper - > works ) ;
spin_unlock_irqrestore ( & stopper - > lock , flags ) ;
return run ;
}
static void cpu_stopper_thread ( unsigned int cpu )
2010-05-06 20:49:20 +04:00
{
2013-01-31 16:11:14 +04:00
struct cpu_stopper * stopper = & per_cpu ( cpu_stopper , cpu ) ;
2010-05-06 20:49:20 +04:00
struct cpu_stop_work * work ;
int ret ;
repeat :
work = NULL ;
spin_lock_irq ( & stopper - > lock ) ;
if ( ! list_empty ( & stopper - > works ) ) {
work = list_first_entry ( & stopper - > works ,
struct cpu_stop_work , list ) ;
list_del_init ( & work - > list ) ;
}
spin_unlock_irq ( & stopper - > lock ) ;
if ( work ) {
cpu_stop_fn_t fn = work - > fn ;
void * arg = work - > arg ;
struct cpu_stop_done * done = work - > done ;
2010-10-27 01:22:44 +04:00
char ksym_buf [ KSYM_NAME_LEN ] __maybe_unused ;
2010-05-06 20:49:20 +04:00
/* cpu stop callbacks are not allowed to sleep */
preempt_disable ( ) ;
ret = fn ( arg ) ;
if ( ret )
done - > ret = ret ;
/* restore preemption and check it's still balanced */
preempt_enable ( ) ;
WARN_ONCE ( preempt_count ( ) ,
" cpu_stop: %s(%p) leaked preempt count \n " ,
kallsyms_lookup ( ( unsigned long ) fn , NULL , NULL , NULL ,
ksym_buf ) , arg ) ;
cpu_stop_signal_done ( done , true ) ;
2013-01-31 16:11:14 +04:00
goto repeat ;
}
2010-05-06 20:49:20 +04:00
}
2010-09-22 15:53:15 +04:00
extern void sched_set_stop_task ( int cpu , struct task_struct * stop ) ;
2013-01-31 16:11:14 +04:00
static void cpu_stop_create ( unsigned int cpu )
{
sched_set_stop_task ( cpu , per_cpu ( cpu_stopper_task , cpu ) ) ;
}
static void cpu_stop_park ( unsigned int cpu )
2010-05-06 20:49:20 +04:00
{
struct cpu_stopper * stopper = & per_cpu ( cpu_stopper , cpu ) ;
2013-01-31 16:11:14 +04:00
struct cpu_stop_work * work ;
unsigned long flags ;
2010-05-06 20:49:20 +04:00
2013-01-31 16:11:14 +04:00
/* drain remaining works */
spin_lock_irqsave ( & stopper - > lock , flags ) ;
list_for_each_entry ( work , & stopper - > works , list )
cpu_stop_signal_done ( work - > done , false ) ;
stopper - > enabled = false ;
spin_unlock_irqrestore ( & stopper - > lock , flags ) ;
}
2010-05-06 20:49:20 +04:00
2013-01-31 16:11:14 +04:00
static void cpu_stop_unpark ( unsigned int cpu )
{
struct cpu_stopper * stopper = & per_cpu ( cpu_stopper , cpu ) ;
spin_lock_irq ( & stopper - > lock ) ;
stopper - > enabled = true ;
spin_unlock_irq ( & stopper - > lock ) ;
2010-05-06 20:49:20 +04:00
}
2013-01-31 16:11:14 +04:00
static struct smp_hotplug_thread cpu_stop_threads = {
. store = & cpu_stopper_task ,
. thread_should_run = cpu_stop_should_run ,
. thread_fn = cpu_stopper_thread ,
. thread_comm = " migration/%u " ,
. create = cpu_stop_create ,
. setup = cpu_stop_unpark ,
. park = cpu_stop_park ,
2013-02-26 21:44:33 +04:00
. pre_unpark = cpu_stop_unpark ,
2013-01-31 16:11:14 +04:00
. selfparking = true ,
2010-05-06 20:49:20 +04:00
} ;
static int __init cpu_stop_init ( void )
{
unsigned int cpu ;
for_each_possible_cpu ( cpu ) {
struct cpu_stopper * stopper = & per_cpu ( cpu_stopper , cpu ) ;
spin_lock_init ( & stopper - > lock ) ;
INIT_LIST_HEAD ( & stopper - > works ) ;
}
2013-01-31 16:11:14 +04:00
BUG_ON ( smpboot_register_percpu_thread ( & cpu_stop_threads ) ) ;
2011-11-01 04:11:15 +04:00
stop_machine_initialized = true ;
2010-05-06 20:49:20 +04:00
return 0 ;
}
early_initcall ( cpu_stop_init ) ;
2005-04-17 02:20:36 +04:00
2010-05-08 18:20:53 +04:00
# ifdef CONFIG_STOP_MACHINE
2009-01-01 02:42:28 +03:00
int __stop_machine ( int ( * fn ) ( void * ) , void * data , const struct cpumask * cpus )
2005-04-17 02:20:36 +04:00
{
2013-10-07 14:29:15 +04:00
struct multi_stop_data msdata = {
. fn = fn ,
. data = data ,
. num_threads = num_online_cpus ( ) ,
. active_cpus = cpus ,
} ;
2010-05-06 20:49:20 +04:00
2011-11-01 04:11:15 +04:00
if ( ! stop_machine_initialized ) {
/*
* Handle the case where stop_machine ( ) is called
* early in boot before stop_machine ( ) has been
* initialized .
*/
unsigned long flags ;
int ret ;
2013-10-07 14:29:15 +04:00
WARN_ON_ONCE ( msdata . num_threads ! = 1 ) ;
2011-11-01 04:11:15 +04:00
local_irq_save ( flags ) ;
hard_irq_disable ( ) ;
ret = ( * fn ) ( data ) ;
local_irq_restore ( flags ) ;
return ret ;
}
2010-05-06 20:49:20 +04:00
/* Set the initial state and stop all online cpus. */
2013-10-07 14:29:15 +04:00
set_state ( & msdata , MULTI_STOP_PREPARE ) ;
return stop_cpus ( cpu_online_mask , multi_cpu_stop , & msdata ) ;
2005-04-17 02:20:36 +04:00
}
2009-01-01 02:42:28 +03:00
int stop_machine ( int ( * fn ) ( void * ) , void * data , const struct cpumask * cpus )
2005-04-17 02:20:36 +04:00
{
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:30 +04:00
ret = __stop_machine ( fn , data , cpus ) ;
2008-01-25 23:08:02 +03:00
put_online_cpus ( ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2008-07-28 21:16:30 +04:00
EXPORT_SYMBOL_GPL ( stop_machine ) ;
2010-05-08 18:20:53 +04:00
2011-06-23 22:19:28 +04:00
/**
* stop_machine_from_inactive_cpu - stop_machine ( ) from inactive CPU
* @ fn : the function to run
* @ data : the data ptr for the @ fn ( )
* @ cpus : the cpus to run the @ fn ( ) on ( NULL = any online cpu )
*
* This is identical to stop_machine ( ) but can be called from a CPU which
* is not active . The local CPU is in the process of hotplug ( so no other
* CPU hotplug can start ) and not marked active and doesn ' t have enough
* context to sleep .
*
* This function provides stop_machine ( ) functionality for such state by
* using busy - wait for synchronization and executing @ fn directly for local
* CPU .
*
* CONTEXT :
* Local CPU is inactive . Temporarily stops all active CPUs .
*
* RETURNS :
* 0 if all executions of @ fn returned 0 , any non zero return value if any
* returned non zero .
*/
int stop_machine_from_inactive_cpu ( int ( * fn ) ( void * ) , void * data ,
const struct cpumask * cpus )
{
2013-10-07 14:29:15 +04:00
struct multi_stop_data msdata = { . fn = fn , . data = data ,
2011-06-23 22:19:28 +04:00
. active_cpus = cpus } ;
struct cpu_stop_done done ;
int ret ;
/* Local CPU must be inactive and CPU hotplug in progress. */
BUG_ON ( cpu_active ( raw_smp_processor_id ( ) ) ) ;
2013-10-07 14:29:15 +04:00
msdata . num_threads = num_active_cpus ( ) + 1 ; /* +1 for local */
2011-06-23 22:19:28 +04:00
/* No proper task established and can't sleep - busy wait for lock. */
while ( ! mutex_trylock ( & stop_cpus_mutex ) )
cpu_relax ( ) ;
/* Schedule work on other CPUs and execute directly for local CPU */
2013-10-07 14:29:15 +04:00
set_state ( & msdata , MULTI_STOP_PREPARE ) ;
2011-06-23 22:19:28 +04:00
cpu_stop_init_done ( & done , num_active_cpus ( ) ) ;
2013-10-07 14:29:15 +04:00
queue_stop_cpus_work ( cpu_active_mask , multi_cpu_stop , & msdata ,
2011-06-23 22:19:28 +04:00
& done ) ;
2013-10-07 14:29:15 +04:00
ret = multi_cpu_stop ( & msdata ) ;
2011-06-23 22:19:28 +04:00
/* Busy wait for completion. */
while ( ! completion_done ( & done . completion ) )
cpu_relax ( ) ;
mutex_unlock ( & stop_cpus_mutex ) ;
return ret ? : done . ret ;
}
2010-05-08 18:20:53 +04:00
# endif /* CONFIG_STOP_MACHINE */