天天看點

hung_task_timeout_secs 簡單學習

[原文連結]

http://hi.baidu.com/stealth_space/item/2007d93fe93ca28fb711dbac

接上篇 簡單學習了解下 hung_task_timeout_secs 相關知識.

os: 2.6.18-194.32.1.el5 x86_64

# 先從系統中看下 hung_task 相關的參數及其參數值

[sina@localhost ~]$ sudo sysctl -a | grep hung

kernel.hung_task_warnings = 0

kernel.hung_task_timeout_secs = 120

kernel.hung_task_check_count = 4194304

kernel.hung_task_panic = 0

[sina@localhost ~]$

# 源碼在此.... (v2.6.37.1)   & 結合看下kernel/sysctl.c更好

# kernel/hung_task.c

/*

 * 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>

 * the number of tasks checked:

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

 * 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,

};

static void check_hung_task(struct task_struct *t, unsigned long timeout)

    unsigned long switch_count = t->nvcsw + t->nivcsw;

    /*

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

        return;

    if (switch_count != t->last_switch_count) {

        t->last_switch_count = switch_count;

    }

    if (!sysctl_hung_task_warnings)

    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 "

            "%ld seconds.\n", t->comm, t->pid, timeout);

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

 * 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

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

 * check whether a task_uninterruptible does not get woken up for

 * a really long time (120 seconds). if that happens, print out

 * a warning.

static void check_hung_uninterruptible_tasks(unsigned long timeout)

    int max_count = sysctl_hung_task_check_count;

    int batch_count = hung_task_batching;

    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)

    do_each_thread(g, t) {

        if (!max_count--)

            goto unlock;

        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;

        }

        /* use "==" to skip the task_killable tasks waiting on nfs */

        if (t->state == task_uninterruptible)

            check_hung_task(t, timeout);

    } while_each_thread(g, t);

 unlock:

static unsigned long timeout_jiffies(unsigned long timeout)

    /* timeout of 0 will disable the watchdog */

    return timeout ? timeout * hz : max_schedule_timeout;

 * process updating of timeout sysctl

int proc_dohung_task_timeout_secs(struct ctl_table *table, int write,

                  void __user *buffer,

                  size_t *lenp, loff_t *ppos)

    int ret;

    ret = proc_doulongvec_minmax(table, write, buffer, lenp, ppos);

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

        unsigned long timeout = sysctl_hung_task_timeout_secs;

        while (schedule_timeout_interruptible(timeout_jiffies(timeout)))

            timeout = sysctl_hung_task_timeout_secs;

        check_hung_uninterruptible_tasks(timeout);

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

module_init(hung_task_init);

[ -eof- ]

繼續閱讀