This is the initial implementation of a hardlockup detector driven by an
HPET timer. This initial implementation includes functions to control
the timer via its registers. It also requests such timer, installs
a minimal interrupt handler and performs the initial configuration of
the timer.
The detector is not functional at this stage. Subsequent changesets will
populate the NMI watchdog operations and register it with the lockup
detector.
This detector depends on HPET_TIMER since platform code performs the
initialization of the timer and maps its registers to memory. It depends
on HPET to compute the ticks per second of the timer.
Cc: Ashok Raj
Cc: Andi Kleen
Cc: Tony Luck
Cc: Borislav Petkov
Cc: Jacob Pan
Cc: "Rafael J. Wysocki"
Cc: Don Zickus
Cc: Nicholas Piggin
Cc: Michael Ellerman
Cc: Frederic Weisbecker
Cc: Alexei Starovoitov
Cc: Babu Moger
Cc: Mathieu Desnoyers
Cc: Masami Hiramatsu
Cc: Peter Zijlstra
Cc: Andrew Morton
Cc: Philippe Ombredanne
Cc: Colin Ian King
Cc: Byungchul Park
Cc: "Paul E. McKenney"
Cc: "Luis R. Rodriguez"
Cc: Waiman Long
Cc: Josh Poimboeuf
Cc: Randy Dunlap
Cc: Davidlohr Bueso
Cc: Christoffer Dall
Cc: Marc Zyngier
Cc: Kai-Heng Feng
Cc: Konrad Rzeszutek Wilk
Cc: David Rientjes
Cc: "Ravi V. Shankar"
Cc: x...@kernel.org
Cc: iommu@lists.linux-foundation.org
Signed-off-by: Ricardo Neri
---
kernel/Makefile| 1 +
kernel/watchdog_hld_hpet.c | 334 +
lib/Kconfig.debug | 10 ++
3 files changed, 345 insertions(+)
create mode 100644 kernel/watchdog_hld_hpet.c
diff --git a/kernel/Makefile b/kernel/Makefile
index 0a0d86d..73c79b2 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -86,6 +86,7 @@ obj-$(CONFIG_KGDB) += debug/
obj-$(CONFIG_DETECT_HUNG_TASK) += hung_task.o
obj-$(CONFIG_LOCKUP_DETECTOR) += watchdog.o
obj-$(CONFIG_HARDLOCKUP_DETECTOR_PERF) += watchdog_hld.o watchdog_hld_perf.o
+obj-$(CONFIG_HARDLOCKUP_DETECTOR_HPET) += watchdog_hld.o watchdog_hld_hpet.o
obj-$(CONFIG_SECCOMP) += seccomp.o
obj-$(CONFIG_RELAY) += relay.o
obj-$(CONFIG_SYSCTL) += utsname_sysctl.o
diff --git a/kernel/watchdog_hld_hpet.c b/kernel/watchdog_hld_hpet.c
new file mode 100644
index 000..8fa4e55
--- /dev/null
+++ b/kernel/watchdog_hld_hpet.c
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * A hardlockup detector driven by an HPET timer.
+ *
+ * Copyright (C) Intel Corporation 2018
+ */
+
+#define pr_fmt(fmt) "NMI hpet watchdog: " fmt
+
+#include
+#include
+#include
+
+#undef pr_fmt
+#define pr_fmt(fmt) "NMI hpet watchdog: " fmt
+
+static struct hpet_hld_data *hld_data;
+
+/**
+ * get_count() - Get the current count of the HPET timer
+ *
+ * Returns:
+ *
+ * Value of the main counter of the HPET timer
+ */
+static inline unsigned long get_count(void)
+{
+ return hpet_readq(HPET_COUNTER);
+}
+
+/**
+ * set_comparator() - Update the comparator in an HPET timer instance
+ * @hdata: A data structure with the timer instance to update
+ * @cmp: The value to write in the in the comparator registere
+ *
+ * Returns:
+ *
+ * None
+ */
+static inline void set_comparator(struct hpet_hld_data *hdata,
+ unsigned long cmp)
+{
+ hpet_writeq(cmp, HPET_Tn_CMP(hdata->num));
+}
+
+/**
+ * kick_timer() - Reprogram timer to expire in the future
+ * @hdata: A data structure with the timer instance to update
+ *
+ * Reprogram the timer to expire within watchdog_thresh seconds in the future.
+ *
+ * Returns:
+ *
+ * None
+ */
+static void kick_timer(struct hpet_hld_data *hdata)
+{
+ unsigned long new_compare, count;
+
+ /*
+* Update the comparator in increments of watch_thresh seconds relative
+* to the current count. Since watch_thresh is given in seconds, we
+* are able to update the comparator before the counter reaches such new
+* value.
+*
+* Let it wrap around if needed.
+*/
+ count = get_count();
+
+ new_compare = count + watchdog_thresh * hdata->ticks_per_second;
+
+ set_comparator(hdata, new_compare);
+}
+
+/**
+ * disable() - Disable an HPET timer instance
+ * @hdata: A data structure with the timer instance to disable
+ *
+ * Returns:
+ *
+ * None
+ */
+static void disable(struct hpet_hld_data *hdata)
+{
+ unsigned int v;
+
+ v = hpet_readl(HPET_Tn_CFG(hdata->num));
+ v &= ~HPET_TN_ENABLE;
+ hpet_writel(v, HPET_Tn_CFG(hdata->num));
+}
+
+/**
+ * enable() - Enable an HPET timer instance
+ * @hdata: A data structure with the timer instance to enable
+ *
+ * Returns:
+ *
+ * None
+ */
+static void enable(struct hpet_hld_data *hdata)
+{
+ unsigned long v;
+
+ /* Clear any previously active interrupt. */
+ hpet_writel(BIT(hdata->num), HPET_STATUS);
+
+ v = hpet_readl(HPET_Tn_CFG(hdata->num));
+ v |= HPET_TN_ENABLE;
+ hpet_writel(v, HPET_Tn_CFG(hdata->num));
+}
+
+/**
+ * set_periodic() - Set an