-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
xtimer: timer & target overflow, hang resolved. #9211
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
/** | ||
* Copyright (C) 2015 Kaspar Schleiser <[email protected]> | ||
* Copyright (C) 2016 Eistec AB | ||
* 2016 Eistec AB | ||
* 2018 Josua Arndt | ||
* | ||
* This file is subject to the terms and conditions of the GNU Lesser | ||
* General Public License v2.1. See the file LICENSE in the top level | ||
|
@@ -15,6 +16,7 @@ | |
* @brief xtimer core functionality | ||
* @author Kaspar Schleiser <[email protected]> | ||
* @author Joakim Nohlgård <[email protected]> | ||
* @author Josua Arndt <[email protected]> | ||
* @} | ||
*/ | ||
|
||
|
@@ -178,12 +180,27 @@ int _xtimer_set_absolute(xtimer_t *timer, uint32_t target) | |
uint32_t now = _xtimer_now(); | ||
jcarrano marked this conversation as resolved.
Show resolved
Hide resolved
|
||
int res = 0; | ||
|
||
DEBUG("timer_set_absolute(): now=%" PRIu32 " target=%" PRIu32 "\n", now, target); | ||
|
||
timer->next = NULL; | ||
if ((target >= now) && ((target - XTIMER_BACKOFF) < now)) { | ||
|
||
/* Ensure that offset is bigger than 'XTIMER_BACKOFF', | ||
* 'target - now' will allways be the offset no matter if target < or > now. | ||
* | ||
* This expects that target was not set too close to now and overrun now, so | ||
* from setting target up until the call of '_xtimer_now()' above now has not | ||
* become equal or bigger than target. | ||
* This is crucial when using low CPU frequencies so reaching the '_xtimer_now()' | ||
* call needs multiple xtimer ticks. | ||
* | ||
* '_xtimer_set()' and `_xtimer_periodic_wakeup()` ensure this by already | ||
* backing off for small values. */ | ||
uint32_t offset = (target - now); | ||
|
||
DEBUG("timer_set_absolute(): now=%" PRIu32 " target=%" PRIu32 " offset=%" PRIu32 "\n", | ||
now, target, offset); | ||
|
||
if (offset <= XTIMER_BACKOFF) { | ||
/* backoff */ | ||
xtimer_spin_until(target + XTIMER_BACKOFF); | ||
xtimer_spin_until(target); | ||
_shoot(timer); | ||
return 0; | ||
} | ||
|
@@ -195,6 +212,17 @@ int _xtimer_set_absolute(xtimer_t *timer, uint32_t target) | |
|
||
timer->target = target; | ||
timer->long_target = _long_cnt; | ||
|
||
/* Ensure timer is fired in right timer period. | ||
* Backoff condition above ensures that 'target - XTIMER_OVERHEAD` is later | ||
* than 'now', also for values when now will overflow and the value of target | ||
* is smaller then now. | ||
* If `target < XTIMER_OVERHEAD` the new target will be at the end of this | ||
* 32bit period, as `target - XTIMER_OVERHEAD` is a big number instead of a | ||
* small at the beginning of the next period. */ | ||
target = target - XTIMER_OVERHEAD; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens when target is small ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then the target underflows, it will be bigger than now and no timer overflow will happen, no long target increase needed.
I will have a deeper look into this tomorrow and also make some tests. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Testing for If the target for I added some coments to the code. |
||
|
||
/* 32 bit target overflow, target is in next 32bit period */ | ||
if (target < now) { | ||
timer->long_target++; | ||
} | ||
|
@@ -214,7 +242,7 @@ int _xtimer_set_absolute(xtimer_t *timer, uint32_t target) | |
|
||
if (timer_list_head == timer) { | ||
DEBUG("timer_set_absolute(): timer is new list head. updating lltimer.\n"); | ||
_lltimer_set(target - XTIMER_OVERHEAD); | ||
_lltimer_set(target); | ||
} | ||
} | ||
} | ||
|
@@ -493,10 +521,14 @@ static void _timer_callback(void) | |
* time to overflow. In that case we advance to | ||
* next timer period and check again for expired | ||
* timers.*/ | ||
if (reference > _xtimer_lltimer_now()) { | ||
/* check if the end of this period is very soon */ | ||
uint32_t now = _xtimer_lltimer_now() + XTIMER_ISR_BACKOFF; | ||
if (now < reference) { | ||
DEBUG("_timer_callback: overflowed while executing callbacks. %i\n", | ||
timer_list_head != NULL); | ||
_next_period(); | ||
/* wait till overflow */ | ||
while( reference < _xtimer_lltimer_now()){} | ||
reference = 0; | ||
goto overflow; | ||
} | ||
|
@@ -506,7 +538,7 @@ static void _timer_callback(void) | |
next_target = timer_list_head->target - XTIMER_OVERHEAD; | ||
|
||
/* make sure we're not setting a time in the past */ | ||
if (next_target < (_xtimer_lltimer_now() + XTIMER_ISR_BACKOFF)) { | ||
if (next_target < (_xtimer_now() + XTIMER_ISR_BACKOFF)) { | ||
goto overflow; | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is hard to guarantee I think. The problem is that the calling task may be de-scheduled for a few ticks and then you miss it anyway.
As it is a private function, is it not possible to update the API to pass it the
now
andoffset
value to handle it ?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really don't get this requirement. How can it be that for correct operation one needs to know the time it gets to get to a certain line? IMO this is very fragile.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To not pass
now
andoffset
, another solution could be to just assume (and ensure) thatoffset < UINT32_MAX - MAX_MARGIN
whereMAX_MARGIN
is the maximum time we allow to enter the function and readnow
again.This way, you are sure that a timer loop can be detected if you took less than
MAX_MARGIN
to go tonow
.A large enough solution could be to say it fits in an
int32_t
so uses31bits
.It is an easy thing to ensure over the code base.
With 1Mhz it would still allow working if you take 35 minutes between the first call and being in the function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is no problem to guarantee.
XTIMER_BACKOFF
has to be set to as much ticks as it needs to reach the backing-off case.I allready started an test which helps to extract the required values.
See: #8990
This requirement is based on the fact that the time is ticking while you try to set a timer. If you set your deadline too short you will miss it. Thats it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will look at #8990 as a base.
When you start calling
_xtimer_set_absolute
you are still in your context, and you could be de-scheduled, for some time (maybe more thanXTIMER_BACKOFF
), so you do not know that thetarget
is not elapsed, or not too close from your calling function.Of course if you call the function with something that ends up being in the past, it should _shoot it directly, but you cannot do better as it was asked too late.
All the calling functions of
_xtimer_set_absolute
already have anow
value (2 functions + 1 test).By getting
now
andoffset
you remove the issues with "calling offset was too short" issue and handle this in the function directly.However I see another issue also with the current implementation, it is that when
now
is read in the function, andoffset
is evaluated it is not with masked interrupts. So everything could also go wrong there too, be de-scheduled and get stuck withxtimer_spin_until
.The only hard limitation we cannot do anything about, is that between the first
now
and thenow
in the function, there should not be more thanUINT32_MAX / Frequency
ticks.I wanted to say 2min23 for 32Mhz timers, but it looks like there are only sub MHz timers.
Even if there is this comment about cc2538 using a 16Mhz timer and code to handle them.
RIOT/sys/include/xtimer/tick_conversion.h
Line 41 in 77dc923
Looking at all this really needs a lot of concentration D:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could propose something with my ideas and see if it would help.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you see #7630 regarding passing a reference time as argument to _xtimer_set_absolute?
It needs a rebase but it is related to this discussion I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did not, I just jumped in the xtimer issue to provide a test, but it means that I had a similar reasoning. I will provide a minimal test to show an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gebart I think your fix still misses something, from what I get, the function should be calling
xtimer_now
from within a masked interrupt context, so that the time between thisnow
and theperiph_timer_set
can be guaranteed to be fixed as you cannot be de-scheduled.If not the comparison with
XTIMER_BACKOFF
does not guarantee anything.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will try to show these with tests.