2005-04-17 02:20:36 +04: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 14:00:04 +03:00
# include <linux/syscalls.h>
2006-12-07 07:34:23 +03:00
# include <linux/freezer.h>
2005-04-17 02:20:36 +04:00
/*
* Timeout for stopping processes
*/
2006-03-23 14:00:04 +03:00
# define TIMEOUT (20 * HZ)
2005-04-17 02:20:36 +04:00
static inline int freezeable ( struct task_struct * p )
{
if ( ( p = = current ) | |
( p - > flags & PF_NOFREEZE ) | |
( p - > exit_state = = EXIT_ZOMBIE ) | |
( p - > exit_state = = EXIT_DEAD ) | |
2006-03-31 14:30:06 +04:00
( p - > state = = TASK_STOPPED ) )
2005-04-17 02:20:36 +04:00
return 0 ;
return 1 ;
}
/* Refrigerator is place where frozen processes are stored :-). */
2005-06-25 10:13:50 +04:00
void refrigerator ( void )
2005-04-17 02:20:36 +04: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-25 10:13:50 +04:00
frozen_process ( current ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irq ( & current - > sighand - > siglock ) ;
recalc_sigpending ( ) ; /* We sent fake signal, clean it up */
spin_unlock_irq ( & current - > sighand - > siglock ) ;
2005-09-04 02:56:53 +04:00
while ( frozen ( current ) ) {
current - > state = TASK_UNINTERRUPTIBLE ;
2005-04-17 02:20:36 +04:00
schedule ( ) ;
2005-09-04 02:56:53 +04:00
}
2005-04-17 02:20:36 +04:00
pr_debug ( " %s left refrigerator \n " , current - > comm ) ;
current - > state = save ;
}
2006-03-23 14:00:04 +03:00
static inline void freeze_process ( struct task_struct * p )
{
unsigned long flags ;
if ( ! freezing ( p ) ) {
freeze ( p ) ;
spin_lock_irqsave ( & p - > sighand - > siglock , flags ) ;
signal_wake_up ( p , 0 ) ;
spin_unlock_irqrestore ( & p - > sighand - > siglock , flags ) ;
}
}
2006-08-05 23:13:42 +04: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 ) ;
}
}
2005-04-17 02:20:36 +04:00
/* 0 = success, else # of processes that we failed to stop */
int freeze_processes ( void )
{
2006-03-23 14:00:04 +03:00
int todo , nr_user , user_frozen ;
2005-06-25 10:13:50 +04:00
unsigned long start_time ;
2005-04-17 02:20:36 +04:00
struct task_struct * g , * p ;
2005-06-25 10:13:50 +04:00
2005-04-17 02:20:36 +04:00
printk ( " Stopping tasks: " ) ;
start_time = jiffies ;
2006-03-23 14:00:04 +03:00
user_frozen = 0 ;
2005-04-17 02:20:36 +04:00
do {
2006-03-23 14:00:04 +03:00
nr_user = todo = 0 ;
2005-04-17 02:20:36 +04:00
read_lock ( & tasklist_lock ) ;
do_each_thread ( g , p ) {
if ( ! freezeable ( p ) )
continue ;
2005-07-08 04:56:45 +04:00
if ( frozen ( p ) )
2005-04-17 02:20:36 +04:00
continue ;
2006-08-05 23:13:42 +04:00
if ( p - > state = = TASK_TRACED & & frozen ( p - > parent ) ) {
cancel_freezing ( p ) ;
continue ;
}
2006-03-23 14:00:04 +03:00
if ( p - > mm & & ! ( p - > flags & PF_BORROWED_MM ) ) {
/* The task is a user-space one.
* Freeze it unless there ' s a vfork completion
* pending
*/
if ( ! p - > vfork_done )
freeze_process ( p ) ;
nr_user + + ;
} else {
/* Freeze only if the user space is frozen */
if ( user_frozen )
freeze_process ( p ) ;
todo + + ;
}
2005-04-17 02:20:36 +04:00
} while_each_thread ( g , p ) ;
read_unlock ( & tasklist_lock ) ;
2006-03-23 14:00:04 +03:00
todo + = nr_user ;
if ( ! user_frozen & & ! nr_user ) {
sys_sync ( ) ;
start_time = jiffies ;
}
user_frozen = ! nr_user ;
2005-04-17 02:20:36 +04:00
yield ( ) ; /* Yield is okay here */
2006-03-23 14:00:04 +03:00
if ( todo & & time_after ( jiffies , start_time + TIMEOUT ) )
2005-09-04 02:57:05 +04:00
break ;
2005-04-17 02:20:36 +04:00
} while ( todo ) ;
2005-06-25 10:13:50 +04:00
2005-09-04 02:57:05 +04: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 .
*/
if ( todo ) {
2006-03-23 14:00:04 +03:00
printk ( " \n " ) ;
printk ( KERN_ERR " stopping tasks timed out "
" after %d seconds (%d tasks remaining): \n " ,
TIMEOUT / HZ , todo ) ;
2005-09-04 02:57:05 +04:00
read_lock ( & tasklist_lock ) ;
2006-03-23 14:00:04 +03:00
do_each_thread ( g , p ) {
if ( freezeable ( p ) & & ! frozen ( p ) )
printk ( KERN_ERR " %s \n " , p - > comm ) ;
2006-08-05 23:13:42 +04:00
cancel_freezing ( p ) ;
2006-03-23 14:00:04 +03:00
} while_each_thread ( g , p ) ;
2005-09-04 02:57:05 +04:00
read_unlock ( & tasklist_lock ) ;
return todo ;
}
2005-04-17 02:20:36 +04:00
printk ( " | \n " ) ;
BUG_ON ( in_atomic ( ) ) ;
return 0 ;
}
void thaw_processes ( void )
{
struct task_struct * g , * p ;
printk ( " Restarting tasks... " ) ;
read_lock ( & tasklist_lock ) ;
do_each_thread ( g , p ) {
if ( ! freezeable ( p ) )
continue ;
2005-06-25 10:13:50 +04:00
if ( ! thaw_process ( p ) )
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO " Strange, %s not stopped \n " , p - > comm ) ;
} while_each_thread ( g , p ) ;
read_unlock ( & tasklist_lock ) ;
schedule ( ) ;
printk ( " done \n " ) ;
}
EXPORT_SYMBOL ( refrigerator ) ;