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:25 +04:00
/* total number of freezing conditions in effect */
atomic_t system_freezing_cnt = ATOMIC_INIT ( 0 ) ;
EXPORT_SYMBOL ( system_freezing_cnt ) ;
/* indicate whether PM freezing is in effect, protected by pm_mutex */
bool pm_freezing ;
bool pm_nosig_freezing ;
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
2011-11-22 00:32:25 +04:00
/**
* freezing_slow_path - slow path for testing whether a task needs to be frozen
* @ p : task to be tested
*
* This function is called by freezing ( ) if system_freezing_cnt isn ' t zero
* and tests whether @ p needs to enter and stay in frozen state . Can be
* called under any context . The freezers are responsible for ensuring the
* target tasks see the updated state .
*/
bool freezing_slow_path ( struct task_struct * p )
{
if ( p - > flags & PF_NOFREEZE )
return false ;
if ( pm_nosig_freezing | | cgroup_freezing ( p ) )
return true ;
if ( pm_freezing & & ! ( p - > flags & PF_FREEZER_NOSIG ) )
return true ;
return false ;
}
EXPORT_SYMBOL ( freezing_slow_path ) ;
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 ;
2011-11-22 00:32:26 +04:00
long save = current - > state ;
2008-10-19 07:27:19 +04:00
pr_debug ( " %s entered refrigerator \n " , current - > comm ) ;
for ( ; ; ) {
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
2011-11-22 00:32:26 +04:00
spin_lock_irq ( & freezer_lock ) ;
current - > flags | = PF_FROZEN ;
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 ( ) ) )
2011-11-22 00:32:26 +04:00
current - > flags & = ~ PF_FROZEN ;
spin_unlock_irq ( & freezer_lock ) ;
if ( ! ( current - > flags & PF_FROZEN ) )
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:26 +04:00
spin_lock_irq ( & current - > sighand - > siglock ) ;
recalc_sigpending ( ) ; /* We sent fake signal, clean it up */
spin_unlock_irq ( & current - > sighand - > siglock ) ;
2011-11-22 00:32:24 +04:00
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 ;
2011-11-22 00:32:26 +04:00
if ( lock_task_sighand ( p , & flags ) ) {
signal_wake_up ( p , 0 ) ;
unlock_task_sighand ( p , & flags ) ;
}
2008-10-19 07:27:19 +04:00
}
/**
* 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 ;
spin_lock_irqsave ( & freezer_lock , flags ) ;
2011-11-22 00:32:25 +04:00
if ( ! freezing ( p ) | | frozen ( p ) ) {
spin_unlock_irqrestore ( & freezer_lock , flags ) ;
return false ;
}
2008-10-19 07:27:19 +04:00
2011-11-22 00:32:25 +04:00
if ( ! ( p - > flags & PF_FREEZER_NOSIG ) ) {
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:25 +04:00
2011-11-22 00:32:24 +04:00
spin_unlock_irqrestore ( & freezer_lock , flags ) ;
2011-11-22 00:32:25 +04:00
return true ;
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
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
}
2011-11-22 00:32:25 +04:00
/**
* __set_freezable - make % current freezable
* @ with_signal : do we want % TIF_SIGPENDING for notification too ?
*
* Mark % current freezable and enter refrigerator if necessary .
*/
bool __set_freezable ( bool with_signal )
{
might_sleep ( ) ;
/*
* Modify flags while holding freezer_lock . This ensures the
* freezer notices that we aren ' t frozen yet or the freezing
* condition is visible to try_to_freeze ( ) below .
*/
spin_lock_irq ( & freezer_lock ) ;
current - > flags & = ~ PF_NOFREEZE ;
if ( with_signal )
current - > flags & = ~ PF_FREEZER_NOSIG ;
spin_unlock_irq ( & freezer_lock ) ;
return try_to_freeze ( ) ;
}
EXPORT_SYMBOL ( __set_freezable ) ;