linux/kernel/softlockup.c
<<
>>
Prefs
   1/*
   2 * Detect Soft Lockups
   3 *
   4 * started by Ingo Molnar, Copyright (C) 2005, 2006 Red Hat, Inc.
   5 *
   6 * this code detects soft lockups: incidents in where on a CPU
   7 * the kernel does not reschedule for 10 seconds or more.
   8 */
   9#include <linux/mm.h>
  10#include <linux/cpu.h>
  11#include <linux/init.h>
  12#include <linux/delay.h>
  13#include <linux/kthread.h>
  14#include <linux/notifier.h>
  15#include <linux/module.h>
  16
  17static DEFINE_SPINLOCK(print_lock);
  18
  19static DEFINE_PER_CPU(unsigned long, touch_timestamp);
  20static DEFINE_PER_CPU(unsigned long, print_timestamp);
  21static DEFINE_PER_CPU(struct task_struct *, watchdog_task);
  22
  23static int did_panic = 0;
  24
  25static int
  26softlock_panic(struct notifier_block *this, unsigned long event, void *ptr)
  27{
  28        did_panic = 1;
  29
  30        return NOTIFY_DONE;
  31}
  32
  33static struct notifier_block panic_block = {
  34        .notifier_call = softlock_panic,
  35};
  36
  37void touch_softlockup_watchdog(void)
  38{
  39        __raw_get_cpu_var(touch_timestamp) = jiffies;
  40}
  41EXPORT_SYMBOL(touch_softlockup_watchdog);
  42
  43/*
  44 * This callback runs from the timer interrupt, and checks
  45 * whether the watchdog thread has hung or not:
  46 */
  47void softlockup_tick(void)
  48{
  49        int this_cpu = smp_processor_id();
  50        unsigned long touch_timestamp = per_cpu(touch_timestamp, this_cpu);
  51
  52        /* prevent double reports: */
  53        if (per_cpu(print_timestamp, this_cpu) == touch_timestamp ||
  54                did_panic ||
  55                        !per_cpu(watchdog_task, this_cpu))
  56                return;
  57
  58        /* do not print during early bootup: */
  59        if (unlikely(system_state != SYSTEM_RUNNING)) {
  60                touch_softlockup_watchdog();
  61                return;
  62        }
  63
  64        /* Wake up the high-prio watchdog task every second: */
  65        if (time_after(jiffies, touch_timestamp + HZ))
  66                wake_up_process(per_cpu(watchdog_task, this_cpu));
  67
  68        /* Warn about unreasonable 10+ seconds delays: */
  69        if (time_after(jiffies, touch_timestamp + 10*HZ)) {
  70                per_cpu(print_timestamp, this_cpu) = touch_timestamp;
  71
  72                spin_lock(&print_lock);
  73                printk(KERN_ERR "BUG: soft lockup detected on CPU#%d!\n",
  74                        this_cpu);
  75                dump_stack();
  76                spin_unlock(&print_lock);
  77        }
  78}
  79
  80/*
  81 * The watchdog thread - runs every second and touches the timestamp.
  82 */
  83static int watchdog(void * __bind_cpu)
  84{
  85        struct sched_param param = { .sched_priority = 99 };
  86
  87        sched_setscheduler(current, SCHED_FIFO, &param);
  88        current->flags |= PF_NOFREEZE;
  89
  90        /*
  91         * Run briefly once per second to reset the softlockup timestamp.
  92         * If this gets delayed for more than 10 seconds then the
  93         * debug-printout triggers in softlockup_tick().
  94         */
  95        while (!kthread_should_stop()) {
  96                set_current_state(TASK_INTERRUPTIBLE);
  97                touch_softlockup_watchdog();
  98                schedule();
  99        }
 100
 101        return 0;
 102}
 103
 104/*
 105 * Create/destroy watchdog threads as CPUs come and go:
 106 */
 107static int __cpuinit
 108cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
 109{
 110        int hotcpu = (unsigned long)hcpu;
 111        struct task_struct *p;
 112
 113        switch (action) {
 114        case CPU_UP_PREPARE:
 115                BUG_ON(per_cpu(watchdog_task, hotcpu));
 116                p = kthread_create(watchdog, hcpu, "watchdog/%d", hotcpu);
 117                if (IS_ERR(p)) {
 118                        printk("watchdog for %i failed\n", hotcpu);
 119                        return NOTIFY_BAD;
 120                }
 121                per_cpu(touch_timestamp, hotcpu) = jiffies;
 122                per_cpu(watchdog_task, hotcpu) = p;
 123                kthread_bind(p, hotcpu);
 124                break;
 125        case CPU_ONLINE:
 126                wake_up_process(per_cpu(watchdog_task, hotcpu));
 127                break;
 128#ifdef CONFIG_HOTPLUG_CPU
 129        case CPU_UP_CANCELED:
 130                if (!per_cpu(watchdog_task, hotcpu))
 131                        break;
 132                /* Unbind so it can run.  Fall thru. */
 133                kthread_bind(per_cpu(watchdog_task, hotcpu),
 134                             any_online_cpu(cpu_online_map));
 135        case CPU_DEAD:
 136                p = per_cpu(watchdog_task, hotcpu);
 137                per_cpu(watchdog_task, hotcpu) = NULL;
 138                kthread_stop(p);
 139                break;
 140#endif /* CONFIG_HOTPLUG_CPU */
 141        }
 142        return NOTIFY_OK;
 143}
 144
 145static struct notifier_block __cpuinitdata cpu_nfb = {
 146        .notifier_call = cpu_callback
 147};
 148
 149__init void spawn_softlockup_task(void)
 150{
 151        void *cpu = (void *)(long)smp_processor_id();
 152        int err = cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu);
 153
 154        BUG_ON(err == NOTIFY_BAD);
 155        cpu_callback(&cpu_nfb, CPU_ONLINE, cpu);
 156        register_cpu_notifier(&cpu_nfb);
 157
 158        atomic_notifier_chain_register(&panic_notifier_list, &panic_block);
 159}
 160