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/smp_lock.h>
# 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 )
{
if ( ( p = = current ) | |
( p - > flags & PF_NOFREEZE ) | |
( p - > exit_state = = EXIT_ZOMBIE ) | |
2006-12-13 00:34:28 -08:00
( p - > exit_state = = EXIT_DEAD ) )
2005-04-16 15:20:36 -07:00
return 0 ;
return 1 ;
}
/* 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 ;
save = current - > state ;
pr_debug ( " %s entered refrigerator \n " , current - > comm ) ;
2005-06-24 23:13:50 -07:00
frozen_process ( current ) ;
2005-04-16 15:20:36 -07:00
spin_lock_irq ( & current - > sighand - > siglock ) ;
recalc_sigpending ( ) ; /* We sent fake signal, clean it up */
spin_unlock_irq ( & current - > sighand - > siglock ) ;
2005-09-03 15:56:53 -07:00
while ( frozen ( current ) ) {
current - > state = TASK_UNINTERRUPTIBLE ;
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 ) ;
current - > state = save ;
}
2006-03-23 03:00:04 -08:00
static inline void freeze_process ( struct task_struct * p )
{
unsigned long flags ;
if ( ! freezing ( p ) ) {
2006-12-13 00:34:30 -08:00
rmb ( ) ;
if ( ! frozen ( p ) ) {
if ( p - > state = = TASK_STOPPED )
force_sig_specific ( SIGSTOP , p ) ;
2006-12-13 00:34:28 -08:00
2006-12-13 00:34:30 -08:00
freeze ( p ) ;
spin_lock_irqsave ( & p - > sighand - > siglock , flags ) ;
signal_wake_up ( p , p - > state = = TASK_STOPPED ) ;
spin_unlock_irqrestore ( & p - > sighand - > siglock , flags ) ;
}
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 ) ;
do_not_freeze ( p ) ;
spin_lock_irqsave ( & p - > sighand - > siglock , flags ) ;
recalc_sigpending_tsk ( p ) ;
spin_unlock_irqrestore ( & p - > sighand - > siglock , flags ) ;
}
}
2006-12-06 20:34:37 -08:00
static inline int is_user_space ( struct task_struct * p )
{
return p - > mm & & ! ( p - > flags & PF_BORROWED_MM ) ;
}
2006-12-06 20:34:40 -08:00
static unsigned 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 ) {
if ( ! freezeable ( p ) )
continue ;
2006-12-06 20:34:40 -08:00
2005-07-07 17:56:45 -07:00
if ( frozen ( p ) )
2005-04-16 15:20:36 -07:00
continue ;
2006-12-06 20:34:40 -08:00
2006-12-13 00:34:28 -08:00
if ( p - > state = = TASK_TRACED & & frozen ( p - > parent ) ) {
2006-08-05 12:13:42 -07:00
cancel_freezing ( p ) ;
continue ;
}
2006-12-06 20:34:37 -08:00
if ( is_user_space ( p ) ) {
2006-12-06 20:34:40 -08:00
if ( ! freeze_user_space )
continue ;
2006-12-06 20:34:37 -08:00
/* Freeze the task unless there is a vfork
* completion pending
2006-03-23 03:00:04 -08:00
*/
if ( ! p - > vfork_done )
freeze_process ( p ) ;
} else {
2006-12-06 20:34:40 -08:00
if ( freeze_user_space )
continue ;
freeze_process ( p ) ;
2006-03-23 03:00:04 -08:00
}
2006-12-06 20:34:40 -08:00
todo + + ;
2005-04-16 15:20:36 -07:00
} while_each_thread ( g , p ) ;
read_unlock ( & tasklist_lock ) ;
yield ( ) ; /* Yield is okay here */
2006-12-06 20:34:40 -08:00
if ( todo & & 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 " ) ;
2006-12-06 20:34:40 -08:00
printk ( KERN_ERR " Stopping %s timed out after %d seconds "
" (%d tasks refusing to freeze): \n " ,
freeze_user_space ? " user space processes " :
" kernel threads " ,
TIMEOUT / HZ , todo ) ;
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 ) {
2006-12-06 20:34:40 -08:00
if ( is_user_space ( p ) = = ! freeze_user_space )
continue ;
2006-03-23 03:00:04 -08:00
if ( freezeable ( p ) & & ! frozen ( p ) )
2006-12-06 20:34:26 -08:00
printk ( KERN_ERR " %s \n " , p - > comm ) ;
2006-12-06 20:34:40 -08:00
2006-08-05 12:13:42 -07:00
cancel_freezing ( 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 ) ;
}
2006-12-06 20:34:40 -08:00
return todo ;
}
/**
* freeze_processes - tell processes to enter the refrigerator
*
* Returns 0 on success , or the number of processes that didn ' t freeze ,
* although they were told to .
*/
int freeze_processes ( void )
{
unsigned int nr_unfrozen ;
printk ( " Stopping tasks ... " ) ;
nr_unfrozen = try_to_freeze_tasks ( FREEZER_USER_SPACE ) ;
if ( nr_unfrozen )
return nr_unfrozen ;
sys_sync ( ) ;
nr_unfrozen = try_to_freeze_tasks ( FREEZER_KERNEL_THREADS ) ;
if ( nr_unfrozen )
return nr_unfrozen ;
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
2006-12-06 20:34:37 -08:00
if ( is_user_space ( p ) = = ! thaw_user_space )
continue ;
2005-04-16 15:20:36 -07:00
2006-12-06 20:34:37 -08:00
if ( ! thaw_process ( p ) )
printk ( KERN_WARNING " Strange, %s not stopped \n " ,
p - > comm ) ;
} 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 ) ;