2009-01-15 22:08:40 +03:00
/*
* Detect Hung Task
*
* kernel / hung_task . c - kernel thread for detecting tasks stuck in D state
*
*/
# include <linux/mm.h>
# include <linux/cpu.h>
# include <linux/nmi.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/freezer.h>
# include <linux/kthread.h>
# include <linux/lockdep.h>
# include <linux/module.h>
# include <linux/sysctl.h>
/*
2009-02-05 07:35:48 +03:00
* The number of tasks checked :
2009-01-15 22:08:40 +03:00
*/
2009-02-05 07:35:48 +03:00
unsigned long __read_mostly sysctl_hung_task_check_count = PID_MAX_LIMIT ;
/*
* Limit number of tasks checked in a batch .
*
* This value controls the preemptibility of khungtaskd since preemption
* is disabled during the critical section . It also controls the size of
* the RCU grace period . So it needs to be upper - bound .
*/
# define HUNG_TASK_BATCHING 1024
2009-01-15 22:08:40 +03:00
/*
* Zero means infinite timeout - no checking done :
*/
unsigned long __read_mostly sysctl_hung_task_timeout_secs = 120 ;
unsigned long __read_mostly sysctl_hung_task_warnings = 10 ;
static int __read_mostly did_panic ;
static struct task_struct * watchdog_task ;
/*
* Should we panic ( and reboot , if panic_timeout = is set ) when a
* hung task is detected :
*/
unsigned int __read_mostly sysctl_hung_task_panic =
CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE ;
static int __init hung_task_panic_setup ( char * str )
{
sysctl_hung_task_panic = simple_strtoul ( str , NULL , 0 ) ;
return 1 ;
}
__setup ( " hung_task_panic= " , hung_task_panic_setup ) ;
static int
hung_task_panic ( struct notifier_block * this , unsigned long event , void * ptr )
{
did_panic = 1 ;
return NOTIFY_DONE ;
}
static struct notifier_block panic_block = {
. notifier_call = hung_task_panic ,
} ;
2009-02-07 02:37:47 +03:00
static void check_hung_task ( struct task_struct * t , unsigned long timeout )
2009-01-15 22:08:40 +03:00
{
unsigned long switch_count = t - > nvcsw + t - > nivcsw ;
2009-02-10 18:52:37 +03:00
/*
* Ensure the task is not frozen .
* Also , when a freshly created task is scheduled once , changes
* its state to TASK_UNINTERRUPTIBLE without having ever been
* switched out once , it musn ' t be checked .
*/
if ( unlikely ( t - > flags & PF_FROZEN | | ! switch_count ) )
2009-01-15 22:08:40 +03:00
return ;
2009-02-07 02:37:47 +03:00
if ( switch_count ! = t - > last_switch_count ) {
2009-01-15 22:08:40 +03:00
t - > last_switch_count = switch_count ;
return ;
}
if ( ! sysctl_hung_task_warnings )
return ;
sysctl_hung_task_warnings - - ;
/*
* Ok , the task did not get scheduled for more than 2 minutes ,
* complain :
*/
printk ( KERN_ERR " INFO: task %s:%d blocked for more than "
2009-01-17 21:31:48 +03:00
" %ld seconds. \n " , t - > comm , t - > pid , timeout ) ;
2009-01-15 22:08:40 +03:00
printk ( KERN_ERR " \" echo 0 > /proc/sys/kernel/hung_task_timeout_secs \" "
" disables this message. \n " ) ;
sched_show_task ( t ) ;
__debug_show_held_locks ( t ) ;
touch_nmi_watchdog ( ) ;
if ( sysctl_hung_task_panic )
panic ( " hung_task: blocked tasks " ) ;
}
2009-02-05 07:35:48 +03:00
/*
* To avoid extending the RCU grace period for an unbounded amount of time ,
* periodically exit the critical section and enter a new one .
*
* For preemptible RCU it is sufficient to call rcu_read_unlock in order
* exit the grace period . For classic RCU , a reschedule is required .
*/
static void rcu_lock_break ( struct task_struct * g , struct task_struct * t )
{
get_task_struct ( g ) ;
get_task_struct ( t ) ;
rcu_read_unlock ( ) ;
cond_resched ( ) ;
rcu_read_lock ( ) ;
put_task_struct ( t ) ;
put_task_struct ( g ) ;
}
2009-01-15 22:08:40 +03:00
/*
* Check whether a TASK_UNINTERRUPTIBLE does not get woken up for
* a really long time ( 120 seconds ) . If that happens , print out
* a warning .
*/
2009-01-17 21:31:48 +03:00
static void check_hung_uninterruptible_tasks ( unsigned long timeout )
2009-01-15 22:08:40 +03:00
{
int max_count = sysctl_hung_task_check_count ;
2009-02-05 07:35:48 +03:00
int batch_count = HUNG_TASK_BATCHING ;
2009-01-15 22:08:40 +03:00
struct task_struct * g , * t ;
/*
* If the system crashed already then all bets are off ,
* do not report extra hung tasks :
*/
if ( test_taint ( TAINT_DIE ) | | did_panic )
return ;
2009-02-05 20:56:08 +03:00
rcu_read_lock ( ) ;
2009-01-15 22:08:40 +03:00
do_each_thread ( g , t ) {
2009-11-27 05:28:20 +03:00
if ( ! max_count - - )
2009-01-15 22:08:40 +03:00
goto unlock ;
2009-02-05 07:35:48 +03:00
if ( ! - - batch_count ) {
batch_count = HUNG_TASK_BATCHING ;
rcu_lock_break ( g , t ) ;
/* Exit if t or g was unhashed during refresh. */
if ( t - > state = = TASK_DEAD | | g - > state = = TASK_DEAD )
goto unlock ;
}
2009-01-15 22:08:40 +03:00
/* use "==" to skip the TASK_KILLABLE tasks waiting on NFS */
if ( t - > state = = TASK_UNINTERRUPTIBLE )
2009-02-07 02:37:47 +03:00
check_hung_task ( t , timeout ) ;
2009-01-15 22:08:40 +03:00
} while_each_thread ( g , t ) ;
unlock :
2009-02-05 20:56:08 +03:00
rcu_read_unlock ( ) ;
2009-01-15 22:08:40 +03:00
}
2009-02-07 02:37:47 +03:00
static unsigned long timeout_jiffies ( unsigned long timeout )
2009-01-15 22:08:40 +03:00
{
/* timeout of 0 will disable the watchdog */
2009-02-07 02:37:47 +03:00
return timeout ? timeout * HZ : MAX_SCHEDULE_TIMEOUT ;
2009-01-15 22:08:40 +03:00
}
/*
* Process updating of timeout sysctl
*/
int proc_dohung_task_timeout_secs ( struct ctl_table * table , int write ,
2009-09-24 02:57:19 +04:00
void __user * buffer ,
2009-01-15 22:08:40 +03:00
size_t * lenp , loff_t * ppos )
{
int ret ;
2009-09-24 02:57:19 +04:00
ret = proc_doulongvec_minmax ( table , write , buffer , lenp , ppos ) ;
2009-01-15 22:08:40 +03:00
if ( ret | | ! write )
goto out ;
wake_up_process ( watchdog_task ) ;
out :
return ret ;
}
/*
* kthread which checks for tasks stuck in D state
*/
static int watchdog ( void * dummy )
{
set_user_nice ( current , 0 ) ;
for ( ; ; ) {
2009-02-07 02:37:47 +03:00
unsigned long timeout = sysctl_hung_task_timeout_secs ;
2009-01-17 21:31:48 +03:00
2009-02-07 02:37:47 +03:00
while ( schedule_timeout_interruptible ( timeout_jiffies ( timeout ) ) )
timeout = sysctl_hung_task_timeout_secs ;
2009-01-17 21:31:48 +03:00
2009-02-07 02:37:47 +03:00
check_hung_uninterruptible_tasks ( timeout ) ;
2009-01-15 22:08:40 +03:00
}
return 0 ;
}
static int __init hung_task_init ( void )
{
atomic_notifier_chain_register ( & panic_notifier_list , & panic_block ) ;
watchdog_task = kthread_run ( watchdog , NULL , " khungtaskd " ) ;
return 0 ;
}
module_init ( hung_task_init ) ;