lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20251009155752.773732-2-sashal@kernel.org>
Date: Thu,  9 Oct 2025 11:54:28 -0400
From: Sasha Levin <sashal@...nel.org>
To: patches@...ts.linux.dev,
	stable@...r.kernel.org
Cc: Markus Stockhausen <markus.stockhausen@....de>,
	Daniel Lezcano <daniel.lezcano@...aro.org>,
	Stephen Howell <howels@...thatwemight.be>,
	Bjørn Mork <bjorn@...k.no>,
	Sasha Levin <sashal@...nel.org>,
	tglx@...utronix.de,
	linux-kernel@...r.kernel.org
Subject: [PATCH AUTOSEL 6.17-6.12] clocksource/drivers/timer-rtl-otto: Work around dying timers

From: Markus Stockhausen <markus.stockhausen@....de>

[ Upstream commit e7a25106335041aeca4fdf50a84804c90142c886 ]

The OpenWrt distribution has switched from kernel longterm 6.6 to
6.12. Reports show that devices with the Realtek Otto switch platform
die during operation and are rebooted by the watchdog. Sorting out
other possible reasons the Otto timer is to blame. The platform
currently consists of 4 targets with different hardware revisions.
It is not 100% clear which devices and revisions are affected.

Analysis shows:

A more aggressive sched/deadline handling leads to more timer starts
with small intervals. This increases the bug chances. See
https://marc.info/?l=linux-kernel&m=175276556023276&w=2

Focusing on the real issue a hardware limitation on some devices was
found. There is a minimal chance that a timer ends without firing an
interrupt if it is reprogrammed within the 5us before its expiration
time. Work around this issue by introducing a bounce() function. It
restarts the timer directly before the normal restart functions as
follows:

- Stop timer
- Restart timer with a slow frequency.
- Target time will be >5us
- The subsequent normal restart is outside the critical window

Downstream has already tested and confirmed a patch. See
https://github.com/openwrt/openwrt/pull/19468
https://forum.openwrt.org/t/support-for-rtl838x-based-managed-switches/57875/3788

Signed-off-by: Markus Stockhausen <markus.stockhausen@....de>
Signed-off-by: Daniel Lezcano <daniel.lezcano@...aro.org>
Tested-by: Stephen Howell <howels@...thatwemight.be>
Tested-by: Bjørn Mork <bjorn@...k.no>
Link: https://lore.kernel.org/r/20250804080328.2609287-2-markus.stockhausen@gmx.de
Signed-off-by: Sasha Levin <sashal@...nel.org>
---

LLM Generated explanations, may be completely bogus:

YES

- Fixes a real user-visible bug: The commit addresses a hardware timing
  erratum on some Realtek Otto SoCs where reprogramming a running timer
  within ~5 µs of its expiration can cause the next interrupt to be
  lost, leading to hangs and watchdog resets. This is confirmed by field
  reports (OpenWrt) and multiple Tested-by tags in the commit message.
  It is an important reliability fix, not a feature.

- Small, contained change in one driver: The patch is limited to
  `drivers/clocksource/timer-rtl-otto.c`. It introduces a minimal helper
  and three call sites; no API or architectural changes.

- Core idea and code changes:
  - Adds `RTTM_MAX_DIVISOR` to select the slowest prescaler for a brief
    “bounce” restart to safely move the timer away from the <5 µs danger
    window (define added near the other timer constants).
  - Introduces `rttm_bounce_timer()` which disables and immediately re-
    enables the timer with the slowest divisor, preserving the current
    period so the immediate follow-up reprogramming happens well outside
    the critical window:
    - New helper is placed after the IRQ handler and before the
      stop/start helpers.
  - Wires the bounce into all clockevent reprogram paths by calling it
    just before the existing stop/program/start sequence:
    - `rttm_next_event()` adds the bounce before `rttm_stop_timer()`
      (see current function start at drivers/clocksource/timer-rtl-
      otto.c:127).
    - `rttm_state_oneshot()` adds the bounce before `rttm_stop_timer()`
      (drivers/clocksource/timer-rtl-otto.c:139).
    - `rttm_state_periodic()` adds the bounce before `rttm_stop_timer()`
      (drivers/clocksource/timer-rtl-otto.c:151).
  - The clocksource path remains untouched (e.g.,
    `rttm_enable_clocksource()` at drivers/clocksource/timer-rtl-
    otto.c:204), which is appropriate since the bug is triggered by
    frequent reprogramming of the clockevent timers, not the continuous
    clocksource.

- Rationale for safety and effectiveness:
  - The bounce sequence is purely local to the Otto timer MMIO block and
    uses existing primitives (`rttm_disable_timer()`,
    `rttm_enable_timer()`), preserving established semantics while
    creating a safe temporal margin before the normal reprogramming.
  - Using `RTTM_MAX_DIVISOR` ensures the effective tick frequency drops
    to ~kHz, making the “time to end marker” well beyond 5 µs even with
    the minimal period (`>= RTTM_MIN_DELTA`, 8 ticks), eliminating the
    observed race window.
  - The stop/ack/program/start logic remains identical aside from the
    pre-amble bounce; ack of pending IRQs is still done in
    `rttm_stop_timer()`, as before, so the change does not introduce new
    interrupt handling semantics.

- Scope and regression risk:
  - Limited to Realtek Otto timer driver; no impact on other platforms
    or subsystems.
  - No ABI/DT/Kconfig changes; no scheduling or generic timekeeping
    changes.
  - Minimal runtime overhead (a couple of MMIO writes per reprogram) is
    acceptable versus preventing system hangs.

- Stable backport suitability:
  - The driver is present in stable trees starting with v6.11 (verified:
    file exists in v6.11 and v6.12; drivers/clocksource/timer-rtl-
    otto.c). The bug has real-world impact with OpenWrt on 6.12; hence
    backporting to 6.11.y, 6.12.y, and newer stable series that include
    this driver is appropriate.
  - The patch is self-contained and does not depend on recent framework
    changes.

Conclusion: This is a targeted, low-risk workaround for a serious
hardware erratum affecting deployed systems. It cleanly fits stable
criteria and should be backported to all stable series that contain
`drivers/clocksource/timer-rtl-otto.c`.

 drivers/clocksource/timer-rtl-otto.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/drivers/clocksource/timer-rtl-otto.c b/drivers/clocksource/timer-rtl-otto.c
index 8a3068b36e752..8be45a11fb8b6 100644
--- a/drivers/clocksource/timer-rtl-otto.c
+++ b/drivers/clocksource/timer-rtl-otto.c
@@ -38,6 +38,7 @@
 #define RTTM_BIT_COUNT		28
 #define RTTM_MIN_DELTA		8
 #define RTTM_MAX_DELTA		CLOCKSOURCE_MASK(28)
+#define RTTM_MAX_DIVISOR	GENMASK(15, 0)
 
 /*
  * Timers are derived from the LXB clock frequency. Usually this is a fixed
@@ -112,6 +113,22 @@ static irqreturn_t rttm_timer_interrupt(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static void rttm_bounce_timer(void __iomem *base, u32 mode)
+{
+	/*
+	 * When a running timer has less than ~5us left, a stop/start sequence
+	 * might fail. While the details are unknown the most evident effect is
+	 * that the subsequent interrupt will not be fired.
+	 *
+	 * As a workaround issue an intermediate restart with a very slow
+	 * frequency of ~3kHz keeping the target counter (>=8). So the follow
+	 * up restart will always be issued outside the critical window.
+	 */
+
+	rttm_disable_timer(base);
+	rttm_enable_timer(base, mode, RTTM_MAX_DIVISOR);
+}
+
 static void rttm_stop_timer(void __iomem *base)
 {
 	rttm_disable_timer(base);
@@ -129,6 +146,7 @@ static int rttm_next_event(unsigned long delta, struct clock_event_device *clkev
 	struct timer_of *to = to_timer_of(clkevt);
 
 	RTTM_DEBUG(to->of_base.base);
+	rttm_bounce_timer(to->of_base.base, RTTM_CTRL_COUNTER);
 	rttm_stop_timer(to->of_base.base);
 	rttm_set_period(to->of_base.base, delta);
 	rttm_start_timer(to, RTTM_CTRL_COUNTER);
@@ -141,6 +159,7 @@ static int rttm_state_oneshot(struct clock_event_device *clkevt)
 	struct timer_of *to = to_timer_of(clkevt);
 
 	RTTM_DEBUG(to->of_base.base);
+	rttm_bounce_timer(to->of_base.base, RTTM_CTRL_COUNTER);
 	rttm_stop_timer(to->of_base.base);
 	rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ);
 	rttm_start_timer(to, RTTM_CTRL_COUNTER);
@@ -153,6 +172,7 @@ static int rttm_state_periodic(struct clock_event_device *clkevt)
 	struct timer_of *to = to_timer_of(clkevt);
 
 	RTTM_DEBUG(to->of_base.base);
+	rttm_bounce_timer(to->of_base.base, RTTM_CTRL_TIMER);
 	rttm_stop_timer(to->of_base.base);
 	rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ);
 	rttm_start_timer(to, RTTM_CTRL_TIMER);
-- 
2.51.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ