diff options
Diffstat (limited to 'kernel/watchdog.c')
-rw-r--r-- | kernel/watchdog.c | 65 |
1 files changed, 32 insertions, 33 deletions
diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 613bc1f04610..6e3c41a4024c 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c | |||
@@ -43,8 +43,7 @@ static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts_saved); | |||
43 | static DEFINE_PER_CPU(struct perf_event *, watchdog_ev); | 43 | static DEFINE_PER_CPU(struct perf_event *, watchdog_ev); |
44 | #endif | 44 | #endif |
45 | 45 | ||
46 | static int __read_mostly did_panic; | 46 | static int no_watchdog; |
47 | static int __initdata no_watchdog; | ||
48 | 47 | ||
49 | 48 | ||
50 | /* boot commands */ | 49 | /* boot commands */ |
@@ -122,7 +121,7 @@ static void __touch_watchdog(void) | |||
122 | 121 | ||
123 | void touch_softlockup_watchdog(void) | 122 | void touch_softlockup_watchdog(void) |
124 | { | 123 | { |
125 | __get_cpu_var(watchdog_touch_ts) = 0; | 124 | __raw_get_cpu_var(watchdog_touch_ts) = 0; |
126 | } | 125 | } |
127 | EXPORT_SYMBOL(touch_softlockup_watchdog); | 126 | EXPORT_SYMBOL(touch_softlockup_watchdog); |
128 | 127 | ||
@@ -142,7 +141,14 @@ void touch_all_softlockup_watchdogs(void) | |||
142 | #ifdef CONFIG_HARDLOCKUP_DETECTOR | 141 | #ifdef CONFIG_HARDLOCKUP_DETECTOR |
143 | void touch_nmi_watchdog(void) | 142 | void touch_nmi_watchdog(void) |
144 | { | 143 | { |
145 | __get_cpu_var(watchdog_nmi_touch) = true; | 144 | if (watchdog_enabled) { |
145 | unsigned cpu; | ||
146 | |||
147 | for_each_present_cpu(cpu) { | ||
148 | if (per_cpu(watchdog_nmi_touch, cpu) != true) | ||
149 | per_cpu(watchdog_nmi_touch, cpu) = true; | ||
150 | } | ||
151 | } | ||
146 | touch_softlockup_watchdog(); | 152 | touch_softlockup_watchdog(); |
147 | } | 153 | } |
148 | EXPORT_SYMBOL(touch_nmi_watchdog); | 154 | EXPORT_SYMBOL(touch_nmi_watchdog); |
@@ -180,18 +186,6 @@ static int is_softlockup(unsigned long touch_ts) | |||
180 | return 0; | 186 | return 0; |
181 | } | 187 | } |
182 | 188 | ||
183 | static int | ||
184 | watchdog_panic(struct notifier_block *this, unsigned long event, void *ptr) | ||
185 | { | ||
186 | did_panic = 1; | ||
187 | |||
188 | return NOTIFY_DONE; | ||
189 | } | ||
190 | |||
191 | static struct notifier_block panic_block = { | ||
192 | .notifier_call = watchdog_panic, | ||
193 | }; | ||
194 | |||
195 | #ifdef CONFIG_HARDLOCKUP_DETECTOR | 189 | #ifdef CONFIG_HARDLOCKUP_DETECTOR |
196 | static struct perf_event_attr wd_hw_attr = { | 190 | static struct perf_event_attr wd_hw_attr = { |
197 | .type = PERF_TYPE_HARDWARE, | 191 | .type = PERF_TYPE_HARDWARE, |
@@ -202,10 +196,13 @@ static struct perf_event_attr wd_hw_attr = { | |||
202 | }; | 196 | }; |
203 | 197 | ||
204 | /* Callback function for perf event subsystem */ | 198 | /* Callback function for perf event subsystem */ |
205 | void watchdog_overflow_callback(struct perf_event *event, int nmi, | 199 | static void watchdog_overflow_callback(struct perf_event *event, int nmi, |
206 | struct perf_sample_data *data, | 200 | struct perf_sample_data *data, |
207 | struct pt_regs *regs) | 201 | struct pt_regs *regs) |
208 | { | 202 | { |
203 | /* Ensure the watchdog never gets throttled */ | ||
204 | event->hw.interrupts = 0; | ||
205 | |||
209 | if (__get_cpu_var(watchdog_nmi_touch) == true) { | 206 | if (__get_cpu_var(watchdog_nmi_touch) == true) { |
210 | __get_cpu_var(watchdog_nmi_touch) = false; | 207 | __get_cpu_var(watchdog_nmi_touch) = false; |
211 | return; | 208 | return; |
@@ -361,14 +358,14 @@ static int watchdog_nmi_enable(int cpu) | |||
361 | /* Try to register using hardware perf events */ | 358 | /* Try to register using hardware perf events */ |
362 | wd_attr = &wd_hw_attr; | 359 | wd_attr = &wd_hw_attr; |
363 | wd_attr->sample_period = hw_nmi_get_sample_period(); | 360 | wd_attr->sample_period = hw_nmi_get_sample_period(); |
364 | event = perf_event_create_kernel_counter(wd_attr, cpu, -1, watchdog_overflow_callback); | 361 | event = perf_event_create_kernel_counter(wd_attr, cpu, NULL, watchdog_overflow_callback); |
365 | if (!IS_ERR(event)) { | 362 | if (!IS_ERR(event)) { |
366 | printk(KERN_INFO "NMI watchdog enabled, takes one hw-pmu counter.\n"); | 363 | printk(KERN_INFO "NMI watchdog enabled, takes one hw-pmu counter.\n"); |
367 | goto out_save; | 364 | goto out_save; |
368 | } | 365 | } |
369 | 366 | ||
370 | printk(KERN_ERR "NMI watchdog failed to create perf event on cpu%i: %p\n", cpu, event); | 367 | printk(KERN_ERR "NMI watchdog failed to create perf event on cpu%i: %p\n", cpu, event); |
371 | return -1; | 368 | return PTR_ERR(event); |
372 | 369 | ||
373 | /* success path */ | 370 | /* success path */ |
374 | out_save: | 371 | out_save: |
@@ -412,17 +409,19 @@ static int watchdog_prepare_cpu(int cpu) | |||
412 | static int watchdog_enable(int cpu) | 409 | static int watchdog_enable(int cpu) |
413 | { | 410 | { |
414 | struct task_struct *p = per_cpu(softlockup_watchdog, cpu); | 411 | struct task_struct *p = per_cpu(softlockup_watchdog, cpu); |
412 | int err; | ||
415 | 413 | ||
416 | /* enable the perf event */ | 414 | /* enable the perf event */ |
417 | if (watchdog_nmi_enable(cpu) != 0) | 415 | err = watchdog_nmi_enable(cpu); |
418 | return -1; | 416 | if (err) |
417 | return err; | ||
419 | 418 | ||
420 | /* create the watchdog thread */ | 419 | /* create the watchdog thread */ |
421 | if (!p) { | 420 | if (!p) { |
422 | p = kthread_create(watchdog, (void *)(unsigned long)cpu, "watchdog/%d", cpu); | 421 | p = kthread_create(watchdog, (void *)(unsigned long)cpu, "watchdog/%d", cpu); |
423 | if (IS_ERR(p)) { | 422 | if (IS_ERR(p)) { |
424 | printk(KERN_ERR "softlockup watchdog for %i failed\n", cpu); | 423 | printk(KERN_ERR "softlockup watchdog for %i failed\n", cpu); |
425 | return -1; | 424 | return PTR_ERR(p); |
426 | } | 425 | } |
427 | kthread_bind(p, cpu); | 426 | kthread_bind(p, cpu); |
428 | per_cpu(watchdog_touch_ts, cpu) = 0; | 427 | per_cpu(watchdog_touch_ts, cpu) = 0; |
@@ -430,6 +429,9 @@ static int watchdog_enable(int cpu) | |||
430 | wake_up_process(p); | 429 | wake_up_process(p); |
431 | } | 430 | } |
432 | 431 | ||
432 | /* if any cpu succeeds, watchdog is considered enabled for the system */ | ||
433 | watchdog_enabled = 1; | ||
434 | |||
433 | return 0; | 435 | return 0; |
434 | } | 436 | } |
435 | 437 | ||
@@ -452,9 +454,6 @@ static void watchdog_disable(int cpu) | |||
452 | per_cpu(softlockup_watchdog, cpu) = NULL; | 454 | per_cpu(softlockup_watchdog, cpu) = NULL; |
453 | kthread_stop(p); | 455 | kthread_stop(p); |
454 | } | 456 | } |
455 | |||
456 | /* if any cpu succeeds, watchdog is considered enabled for the system */ | ||
457 | watchdog_enabled = 1; | ||
458 | } | 457 | } |
459 | 458 | ||
460 | static void watchdog_enable_all_cpus(void) | 459 | static void watchdog_enable_all_cpus(void) |
@@ -474,6 +473,9 @@ static void watchdog_disable_all_cpus(void) | |||
474 | { | 473 | { |
475 | int cpu; | 474 | int cpu; |
476 | 475 | ||
476 | if (no_watchdog) | ||
477 | return; | ||
478 | |||
477 | for_each_online_cpu(cpu) | 479 | for_each_online_cpu(cpu) |
478 | watchdog_disable(cpu); | 480 | watchdog_disable(cpu); |
479 | 481 | ||
@@ -516,17 +518,16 @@ static int __cpuinit | |||
516 | cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) | 518 | cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) |
517 | { | 519 | { |
518 | int hotcpu = (unsigned long)hcpu; | 520 | int hotcpu = (unsigned long)hcpu; |
521 | int err = 0; | ||
519 | 522 | ||
520 | switch (action) { | 523 | switch (action) { |
521 | case CPU_UP_PREPARE: | 524 | case CPU_UP_PREPARE: |
522 | case CPU_UP_PREPARE_FROZEN: | 525 | case CPU_UP_PREPARE_FROZEN: |
523 | if (watchdog_prepare_cpu(hotcpu)) | 526 | err = watchdog_prepare_cpu(hotcpu); |
524 | return NOTIFY_BAD; | ||
525 | break; | 527 | break; |
526 | case CPU_ONLINE: | 528 | case CPU_ONLINE: |
527 | case CPU_ONLINE_FROZEN: | 529 | case CPU_ONLINE_FROZEN: |
528 | if (watchdog_enable(hotcpu)) | 530 | err = watchdog_enable(hotcpu); |
529 | return NOTIFY_BAD; | ||
530 | break; | 531 | break; |
531 | #ifdef CONFIG_HOTPLUG_CPU | 532 | #ifdef CONFIG_HOTPLUG_CPU |
532 | case CPU_UP_CANCELED: | 533 | case CPU_UP_CANCELED: |
@@ -539,7 +540,7 @@ cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) | |||
539 | break; | 540 | break; |
540 | #endif /* CONFIG_HOTPLUG_CPU */ | 541 | #endif /* CONFIG_HOTPLUG_CPU */ |
541 | } | 542 | } |
542 | return NOTIFY_OK; | 543 | return notifier_from_errno(err); |
543 | } | 544 | } |
544 | 545 | ||
545 | static struct notifier_block __cpuinitdata cpu_nfb = { | 546 | static struct notifier_block __cpuinitdata cpu_nfb = { |
@@ -555,13 +556,11 @@ static int __init spawn_watchdog_task(void) | |||
555 | return 0; | 556 | return 0; |
556 | 557 | ||
557 | err = cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu); | 558 | err = cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu); |
558 | WARN_ON(err == NOTIFY_BAD); | 559 | WARN_ON(notifier_to_errno(err)); |
559 | 560 | ||
560 | cpu_callback(&cpu_nfb, CPU_ONLINE, cpu); | 561 | cpu_callback(&cpu_nfb, CPU_ONLINE, cpu); |
561 | register_cpu_notifier(&cpu_nfb); | 562 | register_cpu_notifier(&cpu_nfb); |
562 | 563 | ||
563 | atomic_notifier_chain_register(&panic_notifier_list, &panic_block); | ||
564 | |||
565 | return 0; | 564 | return 0; |
566 | } | 565 | } |
567 | early_initcall(spawn_watchdog_task); | 566 | early_initcall(spawn_watchdog_task); |