summaryrefslogtreecommitdiff
path: root/kernel/watchdog_perf.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/watchdog_perf.c')
-rw-r--r--kernel/watchdog_perf.c47
1 files changed, 23 insertions, 24 deletions
diff --git a/kernel/watchdog_perf.c b/kernel/watchdog_perf.c
index 59c1d86a73a2..d3ca70e3c256 100644
--- a/kernel/watchdog_perf.c
+++ b/kernel/watchdog_perf.c
@@ -12,6 +12,7 @@
#define pr_fmt(fmt) "NMI watchdog: " fmt
+#include <linux/panic.h>
#include <linux/nmi.h>
#include <linux/atomic.h>
#include <linux/module.h>
@@ -21,8 +22,6 @@
#include <linux/perf_event.h>
static DEFINE_PER_CPU(struct perf_event *, watchdog_ev);
-static DEFINE_PER_CPU(struct perf_event *, dead_event);
-static struct cpumask dead_events_mask;
static atomic_t watchdog_cpus = ATOMIC_INIT(0);
@@ -110,6 +109,9 @@ static void watchdog_overflow_callback(struct perf_event *event,
/* Ensure the watchdog never gets throttled */
event->hw.interrupts = 0;
+ if (panic_in_progress())
+ return;
+
if (!watchdog_check_timestamp())
return;
@@ -146,6 +148,7 @@ static int hardlockup_detector_event_create(void)
PTR_ERR(evt));
return PTR_ERR(evt);
}
+ WARN_ONCE(this_cpu_read(watchdog_ev), "unexpected watchdog_ev leak");
this_cpu_write(watchdog_ev, evt);
return 0;
}
@@ -181,34 +184,32 @@ void watchdog_hardlockup_disable(unsigned int cpu)
if (event) {
perf_event_disable(event);
+ perf_event_release_kernel(event);
this_cpu_write(watchdog_ev, NULL);
- this_cpu_write(dead_event, event);
- cpumask_set_cpu(smp_processor_id(), &dead_events_mask);
atomic_dec(&watchdog_cpus);
}
}
/**
- * hardlockup_detector_perf_cleanup - Cleanup disabled events and destroy them
- *
- * Called from lockup_detector_cleanup(). Serialized by the caller.
+ * hardlockup_detector_perf_adjust_period - Adjust the event period due
+ * to current cpu frequency change
+ * @period: The target period to be set
*/
-void hardlockup_detector_perf_cleanup(void)
+void hardlockup_detector_perf_adjust_period(u64 period)
{
- int cpu;
+ struct perf_event *event = this_cpu_read(watchdog_ev);
- for_each_cpu(cpu, &dead_events_mask) {
- struct perf_event *event = per_cpu(dead_event, cpu);
+ if (!(watchdog_enabled & WATCHDOG_HARDLOCKUP_ENABLED))
+ return;
- /*
- * Required because for_each_cpu() reports unconditionally
- * CPU0 as set on UP kernels. Sigh.
- */
- if (event)
- perf_event_release_kernel(event);
- per_cpu(dead_event, cpu) = NULL;
- }
- cpumask_clear(&dead_events_mask);
+ if (!event)
+ return;
+
+ if (event->attr.sample_period == period)
+ return;
+
+ if (perf_event_period(event, period))
+ pr_err("failed to change period to %llu\n", period);
}
/**
@@ -294,12 +295,10 @@ void __init hardlockup_config_perf_event(const char *str)
} else {
unsigned int len = comma - str;
- if (len >= sizeof(buf))
+ if (len > sizeof(buf))
return;
- if (strscpy(buf, str, sizeof(buf)) < 0)
- return;
- buf[len] = 0;
+ strscpy(buf, str, len);
if (kstrtoull(buf, 16, &config))
return;
}