summaryrefslogtreecommitdiffstats
path: root/kernel/watchdog_perf.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/watchdog_perf.c')
-rw-r--r--kernel/watchdog_perf.c58
1 files changed, 53 insertions, 5 deletions
diff --git a/kernel/watchdog_perf.c b/kernel/watchdog_perf.c
index 8ea00c4a24..59c1d86a73 100644
--- a/kernel/watchdog_perf.c
+++ b/kernel/watchdog_perf.c
@@ -75,11 +75,15 @@ static bool watchdog_check_timestamp(void)
__this_cpu_write(last_timestamp, now);
return true;
}
-#else
-static inline bool watchdog_check_timestamp(void)
+
+static void watchdog_init_timestamp(void)
{
- return true;
+ __this_cpu_write(nmi_rearmed, 0);
+ __this_cpu_write(last_timestamp, ktime_get_mono_fast_ns());
}
+#else
+static inline bool watchdog_check_timestamp(void) { return true; }
+static inline void watchdog_init_timestamp(void) { }
#endif
static struct perf_event_attr wd_hw_attr = {
@@ -90,6 +94,14 @@ static struct perf_event_attr wd_hw_attr = {
.disabled = 1,
};
+static struct perf_event_attr fallback_wd_hw_attr = {
+ .type = PERF_TYPE_HARDWARE,
+ .config = PERF_COUNT_HW_CPU_CYCLES,
+ .size = sizeof(struct perf_event_attr),
+ .pinned = 1,
+ .disabled = 1,
+};
+
/* Callback function for perf event subsystem */
static void watchdog_overflow_callback(struct perf_event *event,
struct perf_sample_data *data,
@@ -123,6 +135,13 @@ static int hardlockup_detector_event_create(void)
evt = perf_event_create_kernel_counter(wd_attr, cpu, NULL,
watchdog_overflow_callback, NULL);
if (IS_ERR(evt)) {
+ wd_attr = &fallback_wd_hw_attr;
+ wd_attr->sample_period = hw_nmi_get_sample_period(watchdog_thresh);
+ evt = perf_event_create_kernel_counter(wd_attr, cpu, NULL,
+ watchdog_overflow_callback, NULL);
+ }
+
+ if (IS_ERR(evt)) {
pr_debug("Perf event create on CPU %d failed with %ld\n", cpu,
PTR_ERR(evt));
return PTR_ERR(evt);
@@ -133,7 +152,6 @@ static int hardlockup_detector_event_create(void)
/**
* watchdog_hardlockup_enable - Enable the local event
- *
* @cpu: The CPU to enable hard lockup on.
*/
void watchdog_hardlockup_enable(unsigned int cpu)
@@ -147,12 +165,12 @@ void watchdog_hardlockup_enable(unsigned int cpu)
if (!atomic_fetch_inc(&watchdog_cpus))
pr_info("Enabled. Permanently consumes one hw-PMU counter.\n");
+ watchdog_init_timestamp();
perf_event_enable(this_cpu_read(watchdog_ev));
}
/**
* watchdog_hardlockup_disable - Disable the local event
- *
* @cpu: The CPU to enable hard lockup on.
*/
void watchdog_hardlockup_disable(unsigned int cpu)
@@ -259,3 +277,33 @@ int __init watchdog_hardlockup_probe(void)
}
return ret;
}
+
+/**
+ * hardlockup_config_perf_event - Overwrite config of wd_hw_attr.
+ * @str: number which identifies the raw perf event to use
+ */
+void __init hardlockup_config_perf_event(const char *str)
+{
+ u64 config;
+ char buf[24];
+ char *comma = strchr(str, ',');
+
+ if (!comma) {
+ if (kstrtoull(str, 16, &config))
+ return;
+ } else {
+ unsigned int len = comma - str;
+
+ if (len >= sizeof(buf))
+ return;
+
+ if (strscpy(buf, str, sizeof(buf)) < 0)
+ return;
+ buf[len] = 0;
+ if (kstrtoull(buf, 16, &config))
+ return;
+ }
+
+ wd_hw_attr.type = PERF_TYPE_RAW;
+ wd_hw_attr.config = config;
+}