From 108754005d86c17b6247e83e2b7aed788d93a8c9 Mon Sep 17 00:00:00 2001 From: Randall Spangler Date: Thu, 29 Nov 2012 10:44:30 -0800 Subject: Fix potential deadlock in udelay() If interrupts are disabled and the deadline is across a 32-bit timer boundary from the current time, udelay() can lock up. The fix is to do 32-bit math directly in udelay(). BUG=chrome-os-partner:16472 BRANCH=link TEST=manual waitms 1 -> prompt returns almost instantly waitms 500 -> prompt returns after 0.5 sec waitms 1000 -> watchdog error printed, then prompt returns waitms 2000 -> watchdog reboot Change-Id: Ib8ca06cee414d48900c0142e629daa68aa0993c9 Signed-off-by: Randall Spangler Reviewed-on: https://gerrit.chromium.org/gerrit/38924 Reviewed-by: Yung-Chieh Lo Reviewed-by: Vincent Palatin --- core/cortex-m/timer.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/core/cortex-m/timer.c b/core/cortex-m/timer.c index de4310a88e..0256d673bc 100644 --- a/core/cortex-m/timer.c +++ b/core/cortex-m/timer.c @@ -96,10 +96,20 @@ void process_timers(int overflow) void udelay(unsigned us) { - timestamp_t deadline = get_time(); + unsigned t0 = __hw_clock_source_read(); - deadline.val += us; - while (get_time().val < deadline.val) {} + /* + * udelay() may be called with interrupts disabled, so we can't rely on + * process_timers() updating the top 32 bits. So handle wraparound + * ourselves rather than calling get_time() and comparing with a + * deadline. + * + * This may fail for delays close to 2^32 us (~4000 sec), because the + * subtraction below can overflow. That's acceptable, because the + * watchdog timer would have tripped long before that anyway. + */ + while (__hw_clock_source_read() - t0 < us) + ; } int timer_arm(timestamp_t tstamp, task_id_t tskid) -- cgit v1.2.1