[PATCH 0/3] watchdog: honour hw_margin_ms property

Rasmus Villemoes rasmus.villemoes at prevas.dk
Thu Jun 4 10:28:45 CEST 2020


On 02/06/2020 17.38, Rasmus Villemoes wrote:
> On 02/06/2020 16.53, Stefan Roese wrote:
>> On 02.06.20 15:29, Rasmus Villemoes wrote:
>>> On 16/03/2020 16.52, Rasmus Villemoes wrote:
>>>> On 14/03/2020 13.04, Stefan Roese wrote:
>>>>> On 13.03.20 17:04, Rasmus Villemoes wrote:
>>>>

>>> But, as I suspected, I do have a problem when loading a compressed
>>> kernel image - what I write above "so even in U-Boot proper, time as
>>> measured by get_timer() ceases to pass after that point, so all the
>>> WATCHDOG_RESET() calls from the inflate code effectively get ignored."
>>> is indeed the case.
>>>
>>> So, what's the best way to proceed? Should there be a hook disabling the
>>>   rate-limiting logic that bootm_disable_interrupts() can call? Or must
>>> get_timer() always return a sensible result even with interrupts
>>> disabled?
>>
>> Wouldn't it make sense to move the bootm_disable_interrupts() call to
>> after loading and uncompressing the OS image? To right before jumping
>> to the OS?
> 
> No, because the point of disabling interrupts is that we may start
> writing to physical address 0 (e.g. if that's the load= address in the
> FIT image), which is also where the interrupt vectors reside - i.e.,
> we're about to overwrite 0x900 (the decrementer interrupt vector), so if
> we don't disable interrupts, we'll crash on the very next decrementer
> interrupt (i.e., within one millisecond).

FWIW, the below very hacky patch makes get_timer() return sensible
results on ppc even when interrupts are disabled, and hence ensures that
the watchdog does get petted. It's obviously not meant for inclusion as
is (it's prepared for being a proper config option, but for toying
around it's easier to have it all in one file - also, I don't really
like the name of the config knob). But it's also kind of expensive to do
that do_div(), so I'm not sure I think this is even the right approach.

You previously rejected allowing the board to provide an override for
the rate-limiting, and at least the "hw_margin_ms" parsing now solves
part of what I wanted to use that for. What about implementing the
rate-limiting instead in terms of get_ticks() (the hw_margin_ms can
trivially be translated to a number of ticks at init time - there's
already a usec_to_tick helper)? Are there any boards where get_ticks()
doesn't return something sensible?

Rasmus

_Not_ for inclusion:

diff --git a/arch/powerpc/lib/interrupts.c b/arch/powerpc/lib/interrupts.c
index 23ac5bca1e..e6b6a967ae 100644
--- a/arch/powerpc/lib/interrupts.c
+++ b/arch/powerpc/lib/interrupts.c
@@ -10,11 +10,14 @@
 #include <common.h>
 #include <irq_func.h>
 #include <asm/processor.h>
+#include <div64.h>
 #include <watchdog.h>
 #ifdef CONFIG_LED_STATUS
 #include <status_led.h>
 #endif

+#define CONFIG_GET_TIMER_IRQ 1
+
 #ifndef CONFIG_MPC83XX_TIMER
 #ifndef CONFIG_SYS_WATCHDOG_FREQ
 #define CONFIG_SYS_WATCHDOG_FREQ (CONFIG_SYS_HZ / 2)
@@ -39,8 +42,33 @@ static __inline__ void set_dec (unsigned long val)
 }
 #endif /* !CONFIG_MPC83XX_TIMER */

+static u64 irq_off_ticks = 0;
+static int interrupts_enabled = 0;
+static volatile ulong timestamp = 0;
+
+static u32 irq_off_msecs(void)
+{
+	u64 t;
+	u32 d = get_tbclk();
+
+	if (!d)
+		return 0;
+	t = get_ticks() - irq_off_ticks;
+	t *= 1000;
+	do_div(t, d);
+	return t;
+}
+
 void enable_interrupts(void)
 {
+	ulong msr = get_msr ();
+
+	if (IS_ENABLED(CONFIG_GET_TIMER_IRQ) && !(msr & MSR_EE)) {
+		/* Account for the time interrupts were off. */
+		timestamp += irq_off_msecs();
+		interrupts_enabled = 1;
+	}
+
 	set_msr (get_msr () | MSR_EE);
 }

@@ -50,6 +78,13 @@ int disable_interrupts(void)
 	ulong msr = get_msr ();

 	set_msr (msr & ~MSR_EE);
+
+	if (IS_ENABLED(CONFIG_GET_TIMER_IRQ) && (msr & MSR_EE)) {
+		/* Record when interrupts were disabled. */
+		irq_off_ticks = get_ticks();
+		interrupts_enabled = 0;
+	}
+
 	return ((msr & MSR_EE) != 0);
 }

@@ -61,13 +96,11 @@ int interrupt_init(void)

 	set_dec (decrementer_count);

-	set_msr (get_msr () | MSR_EE);
+	enable_interrupts();

 	return (0);
 }

-static volatile ulong timestamp = 0;
-
 void timer_interrupt(struct pt_regs *regs)
 {
 	/* call cpu specific function from $(CPU)/interrupts.c */
@@ -90,6 +123,12 @@ void timer_interrupt(struct pt_regs *regs)

 ulong get_timer (ulong base)
 {
-	return (timestamp - base);
+	ulong ts = timestamp;
+
+	/* If called within an irq-off section, account for the time since
irqs were turned off. */
+	if (IS_ENABLED(CONFIG_GET_TIMER_IRQ) && !interrupts_enabled)
+		ts += irq_off_msecs();
+
+	return (ts - base);
 }
 #endif /* !CONFIG_MPC83XX_TIMER */



-- 
Rasmus Villemoes
Software Developer
Prevas A/S
Hedeager 3
DK-8200 Aarhus N
+45 51210274
rasmus.villemoes at prevas.dk
www.prevas.dk


More information about the U-Boot mailing list