Task Hung

针对不同级别的进程、CPU上可能发生的"hang",CPU/内核提供了4种检测机制,记录下比较简单的 task hung。

?:msleep(1)后,线程A在120s没有得到调度,hung task检测到线程A hung了。

debug选项

kernel/Makefile: obj-$(CONFIG_DETECT_HUNG_TASK) += hung_task.o
unsigned long __read_mostly sysctl_hung_task_timeout_secs = CONFIG_DEFAULT_HUNG_TASK_TIMEOUT;


启动watchdog_task(4中机制都沾了“watchdog”,但是真的不一样)

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);

 

"watchdog"函数,每120s调用一次check_hung_uninterruptible_tasks(timeout).

/*
 * We can use __set_current_state() here because schedule_timeout() calls
 * schedule() unconditionally.
 */
signed long __sched schedule_timeout_interruptible(signed long timeout)
{
        __set_current_state(TASK_INTERRUPTIBLE);
        return schedule_timeout(timeout);
}
EXPORT_SYMBOL(schedule_timeout_interruptible);

/*      
 * kthread which checks for tasks stuck in D state
 */
static int watchdog(void *dummy)
{
    set_user_nice(current, 0);

    for ( ; ; ) {
        unsigned long timeout = sysctl_hung_task_timeout_secs;

        while (schedule_timeout_interruptible(timeout_jiffies(timeout)))                                                          
            timeout = sysctl_hung_task_timeout_secs;
        
//原来的reset_hung_task作为返回值,并把reset_hung_task置成0
if (atomic_xchg(&reset_hung_task, 0)) continue; check_hung_uninterruptible_tasks(timeout); } return 0; }


看下check_hung_uninterruptible_tasks的说明:两个条件都得满足

/*
 * Check whether a TASK_UNINTERRUPTIBLE does not get woken up for
 * a really long time (120 seconds). If that happens, print out
 * a warning.
 */

 

先看第一个条件,很简单(忽略RCU)

static void check_hung_uninterruptible_tasks(unsigned long timeout)
|-->//int sysctl_hung_task_check_count = PID_MAX_LIMIT;
|-->int max_count = sysctl_hung_task_check_count;
|-->struct task_struct *g, *t; 
|-->do_each_thread(g, t) {
      if (!max_count--)
         return; 
      if (t->state == TASK_UNINTERRUPTIBLE) //首先是TASK_UNINTERRUPTIBLE
         check_hung_task(t, timeout);
    } while_each_thread(g, t);
|

 

第二个条件,对于120s的检测

static void check_hung_task(struct task_struct *t, unsigned long timeout)
|-->unsigned long switch_count = t->nvcsw + t->nivcsw;
|-->不检测被frozen的task,对于刚被创建的task也需要特殊处理,除此而外:
|-->if (switch_count != t->last_switch_count) {//如果在120s内被调度了,则switch_count必然不等于la
       t->last_switch_count = switch_count;
       return;
    }
|-->//如果在120s内被调度了,则switch_count必然不等于last_switch_count,否则说明该task处于饥饿hung状态
|-->打印相关task信息,例如:
|-->sched_show_task(t);
|
|-->一种情形:一个task_A等待某个资源时处于TASK_UNINTERRUPTIBLE,这是由于task_B持有该资源时间太长而导致的,
|-->task_A 和 task_B可能处于不同的rq,处于不同的CPU,因此需要把其它CPU上当前运行的task也打印出来:
|-->if (sysctl_hung_task_panic) {
       trigger_all_cpu_backtrace(); //发送IPI到其它CPU,使其dump其上的运行task的信息
       panic("hung_task: blocked tasks");
    }
|

附:使用命令行选项控制是否需要其它CPU也dump相应信息
unsigned int  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);

很自然的,在主调度函数__schedule()中,如果task_A将被切换为task_B. 此时task_A的调度次数将会被加1(++*switch_count)(为什么不是task_B?不过也无妨),即task->nivcsw++或者task->nvcsw++。顺便问下:nivcsw和nvcsw的区别是什么?

问题还是没结啊,明天把120s缩短下,也许可以加速复现……

posted on 2014-12-01 23:21  阿加  阅读(1050)  评论(0)    收藏  举报

导航