2008-10-19 07:27:19 +04:00
/*
* kernel / freezer . c - Function to freeze a process
*
* Originally from kernel / power / process . c
*/
# include <linux/interrupt.h>
# include <linux/suspend.h>
2011-05-23 22:51:41 +04:00
# include <linux/export.h>
2008-10-19 07:27:19 +04:00
# include <linux/syscalls.h>
# include <linux/freezer.h>
2011-11-22 00:32:23 +04:00
# include <linux/kthread.h>
2008-10-19 07:27:19 +04:00
2011-11-22 00:32:24 +04:00
/* protects freezing and frozen transitions */
static DEFINE_SPINLOCK ( freezer_lock ) ;
2008-10-19 07:27:19 +04:00
/* Refrigerator is place where frozen processes are stored :-). */
2011-11-22 00:32:23 +04:00
bool __refrigerator ( bool check_kthr_stop )
2008-10-19 07:27:19 +04:00
{
/* Hmm, should we be allowed to suspend when there are realtime
processes around ? */
2011-11-22 00:32:22 +04:00
bool was_frozen = false ;
2008-10-19 07:27:19 +04:00
long save ;
2011-11-22 00:32:24 +04:00
/*
* Enter FROZEN . If NOFREEZE , schedule immediate thawing by
* clearing freezing .
*/
2011-11-22 00:32:24 +04:00
spin_lock_irq ( & freezer_lock ) ;
2011-11-22 00:32:24 +04:00
repeat :
2011-11-22 00:32:24 +04:00
if ( ! freezing ( current ) ) {
spin_unlock_irq ( & freezer_lock ) ;
2011-11-22 00:32:22 +04:00
return was_frozen ;
2008-10-19 07:27:19 +04:00
}
2011-11-22 00:32:24 +04:00
if ( current - > flags & PF_NOFREEZE )
clear_freeze_flag ( current ) ;
current - > flags | = PF_FROZEN ;
2011-11-22 00:32:24 +04:00
spin_unlock_irq ( & freezer_lock ) ;
2008-10-19 07:27:19 +04: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 ) ;
for ( ; ; ) {
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
2011-11-22 00:32:24 +04:00
if ( ! freezing ( current ) | |
2011-11-22 00:32:23 +04:00
( check_kthr_stop & & kthread_should_stop ( ) ) )
2008-10-19 07:27:19 +04:00
break ;
2011-11-22 00:32:22 +04:00
was_frozen = true ;
2008-10-19 07:27:19 +04:00
schedule ( ) ;
}
2009-07-17 16:15:47 +04:00
2011-11-22 00:32:24 +04:00
/* leave FROZEN */
spin_lock_irq ( & freezer_lock ) ;
if ( freezing ( current ) )
goto repeat ;
current - > flags & = ~ PF_FROZEN ;
spin_unlock_irq ( & freezer_lock ) ;
2008-10-19 07:27:19 +04:00
pr_debug ( " %s left refrigerator \n " , current - > comm ) ;
2011-11-22 00:32:22 +04:00
/*
* Restore saved task state before returning . The mb ' d version
* needs to be used ; otherwise , it might silently break
* synchronization which depends on ordered task state change .
*/
set_current_state ( save ) ;
2011-11-22 00:32:22 +04:00
return was_frozen ;
2008-10-19 07:27:19 +04:00
}
2011-11-22 00:32:22 +04:00
EXPORT_SYMBOL ( __refrigerator ) ;
2008-10-19 07:27:19 +04:00
static void fake_signal_wake_up ( struct task_struct * p )
{
unsigned long flags ;
spin_lock_irqsave ( & p - > sighand - > siglock , flags ) ;
2011-11-04 04:04:52 +04:00
signal_wake_up ( p , 0 ) ;
2008-10-19 07:27:19 +04:00
spin_unlock_irqrestore ( & p - > sighand - > siglock , flags ) ;
}
/**
* freeze_task - send a freeze request to given task
* @ p : task to send the request to
* @ sig_only : if set , the request will only be sent if the task has the
* PF_FREEZER_NOSIG flag unset
* Return value : ' false ' , if @ sig_only is set and the task has
* PF_FREEZER_NOSIG set or the task is frozen , ' true ' , otherwise
*
* The freeze request is sent by setting 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 PF_FREEZER_NOSIG set . If @ sig_only is set and the task
* has PF_FREEZER_NOSIG set ( ie . it is a typical kernel thread ) , its
* TIF_FREEZE flag will not be set .
*/
bool freeze_task ( struct task_struct * p , bool sig_only )
{
2011-11-22 00:32:24 +04:00
unsigned long flags ;
bool ret = false ;
spin_lock_irqsave ( & freezer_lock , flags ) ;
2011-11-22 00:32:24 +04:00
if ( ( p - > flags & PF_NOFREEZE ) | |
( sig_only & & ! should_send_signal ( p ) ) )
2011-11-22 00:32:24 +04:00
goto out_unlock ;
if ( frozen ( p ) )
goto out_unlock ;
set_freeze_flag ( p ) ;
2008-10-19 07:27:19 +04:00
if ( should_send_signal ( p ) ) {
2010-11-27 01:07:27 +03:00
fake_signal_wake_up ( p ) ;
/*
* fake_signal_wake_up ( ) goes through p ' s scheduler
* lock and guarantees that TASK_STOPPED / TRACED - >
* TASK_RUNNING transition can ' t race with task state
* testing in try_to_freeze_tasks ( ) .
*/
2008-10-19 07:27:19 +04:00
} else {
wake_up_state ( p , TASK_INTERRUPTIBLE ) ;
}
2011-11-22 00:32:24 +04:00
ret = true ;
out_unlock :
spin_unlock_irqrestore ( & freezer_lock , flags ) ;
return ret ;
2008-10-19 07:27:19 +04:00
}
2011-11-22 00:32:23 +04:00
void __thaw_task ( struct task_struct * p )
2008-10-19 07:27:21 +04:00
{
2011-11-22 00:32:24 +04:00
unsigned long flags ;
2011-11-22 00:32:23 +04:00
2011-11-22 00:32:24 +04:00
/*
* Clear freezing and kick @ p if FROZEN . Clearing is guaranteed to
* be visible to @ p as waking up implies wmb . Waking up inside
* freezer_lock also prevents wakeups from leaking outside
* refrigerator .
2011-11-22 00:32:24 +04:00
*
* If ! FROZEN , @ p hasn ' t reached refrigerator , recalc sigpending to
* avoid leaving dangling TIF_SIGPENDING behind .
2011-11-22 00:32:24 +04:00
*/
2011-11-22 00:32:24 +04:00
spin_lock_irqsave ( & freezer_lock , flags ) ;
2011-11-22 00:32:24 +04:00
clear_freeze_flag ( p ) ;
2011-11-22 00:32:24 +04:00
if ( frozen ( p ) ) {
2011-11-22 00:32:23 +04:00
wake_up_process ( p ) ;
2011-11-22 00:32:24 +04:00
} else {
spin_lock ( & p - > sighand - > siglock ) ;
recalc_sigpending_and_wake ( p ) ;
spin_unlock ( & p - > sighand - > siglock ) ;
}
2011-11-22 00:32:24 +04:00
spin_unlock_irqrestore ( & freezer_lock , flags ) ;
2008-10-19 07:27:21 +04:00
}