2005-04-16 15:20:36 -07:00
/*
* drivers / power / process . c - Functions for starting / stopping processes on
* suspend transitions .
*
* Originally from swsusp .
*/
# undef DEBUG
# include <linux/interrupt.h>
# include <linux/suspend.h>
# include <linux/module.h>
2006-03-23 03:00:04 -08:00
# include <linux/syscalls.h>
2006-12-06 20:34:23 -08:00
# include <linux/freezer.h>
2005-04-16 15:20:36 -07:00
/*
* Timeout for stopping processes
*/
2006-03-23 03:00:04 -08:00
# define TIMEOUT (20 * HZ)
2005-04-16 15:20:36 -07:00
2006-12-06 20:34:37 -08:00
# define FREEZER_KERNEL_THREADS 0
# define FREEZER_USER_SPACE 1
2005-04-16 15:20:36 -07:00
static inline int freezeable ( struct task_struct * p )
{
2007-05-08 00:24:01 -07:00
if ( ( p = = current ) | |
2005-04-16 15:20:36 -07:00
( p - > flags & PF_NOFREEZE ) | |
2007-05-08 00:24:01 -07:00
( p - > exit_state ! = 0 ) )
2005-04-16 15:20:36 -07:00
return 0 ;
return 1 ;
}
2007-05-23 13:57:29 -07:00
/*
* freezing is complete , mark current process as frozen
*/
static inline void frozen_process ( void )
{
if ( ! unlikely ( current - > flags & PF_NOFREEZE ) ) {
current - > flags | = PF_FROZEN ;
wmb ( ) ;
}
2007-07-19 01:47:33 -07:00
clear_freeze_flag ( current ) ;
2007-05-23 13:57:29 -07:00
}
2005-04-16 15:20:36 -07:00
/* Refrigerator is place where frozen processes are stored :-). */
2005-06-24 23:13:50 -07:00
void refrigerator ( void )
2005-04-16 15:20:36 -07:00
{
/* Hmm, should we be allowed to suspend when there are realtime
processes around ? */
long save ;
2007-05-23 13:57:24 -07:00
task_lock ( current ) ;
if ( freezing ( current ) ) {
2007-05-23 13:57:29 -07:00
frozen_process ( ) ;
2007-05-23 13:57:24 -07:00
task_unlock ( current ) ;
} else {
task_unlock ( current ) ;
return ;
}
2005-04-16 15:20:36 -07:00
save = current - > state ;
pr_debug ( " %s entered refrigerator \n " , current - > comm ) ;
spin_lock_irq ( & current - > sighand - > siglock ) ;
recalc_sigpending ( ) ; /* We sent fake signal, clean it up */
spin_unlock_irq ( & current - > sighand - > siglock ) ;
2007-05-06 14:50:40 -07:00
for ( ; ; ) {
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
if ( ! frozen ( current ) )
break ;
2005-04-16 15:20:36 -07:00
schedule ( ) ;
2005-09-03 15:56:53 -07:00
}
2005-04-16 15:20:36 -07:00
pr_debug ( " %s left refrigerator \n " , current - > comm ) ;
2007-07-19 01:47:33 -07:00
__set_current_state ( save ) ;
2005-04-16 15:20:36 -07:00
}
2007-10-18 03:04:46 -07:00
static void fake_signal_wake_up ( struct task_struct * p , int resume )
2006-03-23 03:00:04 -08:00
{
unsigned long flags ;
2007-10-18 03:04:46 -07:00
spin_lock_irqsave ( & p - > sighand - > siglock , flags ) ;
signal_wake_up ( p , resume ) ;
spin_unlock_irqrestore ( & p - > sighand - > siglock , flags ) ;
}
static void send_fake_signal ( struct task_struct * p )
{
if ( p - > state = = TASK_STOPPED )
force_sig_specific ( SIGSTOP , p ) ;
fake_signal_wake_up ( p , p - > state = = TASK_STOPPED ) ;
}
static int has_mm ( struct task_struct * p )
{
return ( p - > mm & & ! ( p - > flags & PF_BORROWED_MM ) ) ;
}
/**
* freeze_task - send a freeze request to given task
* @ p : task to send the request to
* @ with_mm_only : if set , the request will only be sent if the task has its
* own mm
* Return value : 0 , if @ with_mm_only is set and the task has no mm of its
* own or the task is frozen , 1 , otherwise
*
* The freeze request is sent by seting the tasks ' s TIF_FREEZE flag and
* either sending a fake signal to it or waking it up , depending on whether
* or not it has its own mm ( ie . it is a user land task ) . If @ with_mm_only
* is set and the task has no mm of its own ( ie . it is a kernel thread ) ,
* its TIF_FREEZE flag should not be set .
*
* The task_lock ( ) is necessary to prevent races with exit_mm ( ) or
* use_mm ( ) / unuse_mm ( ) from occuring .
*/
static int freeze_task ( struct task_struct * p , int with_mm_only )
{
int ret = 1 ;
task_lock ( p ) ;
if ( freezing ( p ) ) {
if ( has_mm ( p ) ) {
if ( ! signal_pending ( p ) )
fake_signal_wake_up ( p , 0 ) ;
} else {
if ( with_mm_only )
ret = 0 ;
else
wake_up_state ( p , TASK_INTERRUPTIBLE ) ;
}
} else {
2006-12-13 00:34:30 -08:00
rmb ( ) ;
2007-10-18 03:04:46 -07:00
if ( frozen ( p ) ) {
ret = 0 ;
} else {
if ( has_mm ( p ) ) {
set_freeze_flag ( p ) ;
send_fake_signal ( p ) ;
} else {
if ( with_mm_only ) {
ret = 0 ;
} else {
set_freeze_flag ( p ) ;
wake_up_state ( p , TASK_INTERRUPTIBLE ) ;
}
}
2006-12-13 00:34:30 -08:00
}
2006-03-23 03:00:04 -08:00
}
2007-10-18 03:04:46 -07:00
task_unlock ( p ) ;
return ret ;
2006-03-23 03:00:04 -08:00
}
2006-08-05 12:13:42 -07:00
static void cancel_freezing ( struct task_struct * p )
{
unsigned long flags ;
if ( freezing ( p ) ) {
pr_debug ( " clean up: %s \n " , p - > comm ) ;
2007-07-19 01:47:33 -07:00
clear_freeze_flag ( p ) ;
2006-08-05 12:13:42 -07:00
spin_lock_irqsave ( & p - > sighand - > siglock , flags ) ;
2007-05-23 13:57:44 -07:00
recalc_sigpending_and_wake ( p ) ;
2006-08-05 12:13:42 -07:00
spin_unlock_irqrestore ( & p - > sighand - > siglock , flags ) ;
}
}
2007-07-19 01:47:34 -07:00
static int try_to_freeze_tasks ( int freeze_user_space )
2005-04-16 15:20:36 -07:00
{
struct task_struct * g , * p ;
2006-12-06 20:34:40 -08:00
unsigned long end_time ;
unsigned int todo ;
2005-06-24 23:13:50 -07:00
2006-12-06 20:34:40 -08:00
end_time = jiffies + TIMEOUT ;
2005-04-16 15:20:36 -07:00
do {
2006-12-06 20:34:40 -08:00
todo = 0 ;
2005-04-16 15:20:36 -07:00
read_lock ( & tasklist_lock ) ;
do_each_thread ( g , p ) {
2007-07-19 01:47:33 -07:00
if ( frozen ( p ) | | ! freezeable ( p ) )
2005-04-16 15:20:36 -07:00
continue ;
2006-12-06 20:34:40 -08:00
2007-10-18 03:04:46 -07:00
if ( p - > state = = TASK_TRACED & & frozen ( p - > parent ) ) {
cancel_freezing ( p ) ;
continue ;
2006-08-05 12:13:42 -07:00
}
2007-10-18 03:04:46 -07:00
if ( ! freeze_task ( p , freeze_user_space ) )
continue ;
2007-05-23 13:57:25 -07:00
if ( ! freezer_should_skip ( p ) )
todo + + ;
2005-04-16 15:20:36 -07:00
} while_each_thread ( g , p ) ;
read_unlock ( & tasklist_lock ) ;
yield ( ) ; /* Yield is okay here */
2007-07-19 01:47:35 -07:00
if ( time_after ( jiffies , end_time ) )
2005-09-03 15:57:05 -07:00
break ;
2006-12-06 20:34:40 -08:00
} while ( todo ) ;
2005-06-24 23:13:50 -07:00
2005-09-03 15:57:05 -07:00
if ( todo ) {
2006-12-06 20:34:40 -08:00
/* This does not unfreeze processes that are already frozen
* ( we have slightly ugly calling convention in that respect ,
* and caller must call thaw_processes ( ) if something fails ) ,
* but it cleans up leftover PF_FREEZE requests .
*/
2006-12-06 20:34:26 -08:00
printk ( " \n " ) ;
2007-07-19 01:47:33 -07:00
printk ( KERN_ERR " Freezing of %s timed out after %d seconds "
2006-12-06 20:34:40 -08:00
" (%d tasks refusing to freeze): \n " ,
2007-07-19 01:47:33 -07:00
freeze_user_space ? " user space " : " tasks " ,
2006-12-06 20:34:40 -08:00
TIMEOUT / HZ , todo ) ;
2007-07-19 01:47:26 -07:00
show_state ( ) ;
2005-09-03 15:57:05 -07:00
read_lock ( & tasklist_lock ) ;
2006-03-23 03:00:04 -08:00
do_each_thread ( g , p ) {
2007-05-23 13:57:24 -07:00
task_lock ( p ) ;
2007-07-19 01:47:33 -07:00
if ( freezing ( p ) & & ! freezer_should_skip ( p ) )
2006-12-06 20:34:26 -08:00
printk ( KERN_ERR " %s \n " , p - > comm ) ;
2006-08-05 12:13:42 -07:00
cancel_freezing ( p ) ;
2007-05-23 13:57:24 -07:00
task_unlock ( p ) ;
2006-03-23 03:00:04 -08:00
} while_each_thread ( g , p ) ;
2005-09-03 15:57:05 -07:00
read_unlock ( & tasklist_lock ) ;
}
2007-07-19 01:47:34 -07:00
return todo ? - EBUSY : 0 ;
2006-12-06 20:34:40 -08:00
}
/**
* freeze_processes - tell processes to enter the refrigerator
*/
int freeze_processes ( void )
{
2007-07-19 01:47:34 -07:00
int error ;
2006-12-06 20:34:40 -08:00
printk ( " Stopping tasks ... " ) ;
2007-07-19 01:47:34 -07:00
error = try_to_freeze_tasks ( FREEZER_USER_SPACE ) ;
if ( error )
return error ;
2006-12-06 20:34:40 -08:00
2007-07-19 01:47:34 -07:00
error = try_to_freeze_tasks ( FREEZER_KERNEL_THREADS ) ;
if ( error )
return error ;
2006-12-06 20:34:40 -08:00
2006-12-06 20:34:26 -08:00
printk ( " done. \n " ) ;
2005-04-16 15:20:36 -07:00
BUG_ON ( in_atomic ( ) ) ;
return 0 ;
}
2006-12-06 20:34:37 -08:00
static void thaw_tasks ( int thaw_user_space )
2005-04-16 15:20:36 -07:00
{
struct task_struct * g , * p ;
read_lock ( & tasklist_lock ) ;
2006-12-06 20:34:37 -08:00
do_each_thread ( g , p ) {
if ( ! freezeable ( p ) )
continue ;
2006-12-06 20:34:28 -08:00
2007-07-19 01:47:33 -07:00
if ( ! p - > mm = = thaw_user_space )
2006-12-06 20:34:37 -08:00
continue ;
2005-04-16 15:20:36 -07:00
2007-05-23 13:57:25 -07:00
thaw_process ( p ) ;
2006-12-06 20:34:37 -08:00
} while_each_thread ( g , p ) ;
2005-04-16 15:20:36 -07:00
read_unlock ( & tasklist_lock ) ;
2006-12-06 20:34:37 -08:00
}
void thaw_processes ( void )
{
printk ( " Restarting tasks ... " ) ;
thaw_tasks ( FREEZER_KERNEL_THREADS ) ;
thaw_tasks ( FREEZER_USER_SPACE ) ;
2005-04-16 15:20:36 -07:00
schedule ( ) ;
2006-12-06 20:34:26 -08:00
printk ( " done. \n " ) ;
2005-04-16 15:20:36 -07:00
}
EXPORT_SYMBOL ( refrigerator ) ;