Commit 63f77059 authored by Rusty Russell's avatar Rusty Russell

timer: brute force corruption fix.

Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 59846988
This diff is collapsed.
#define CCAN_TIMER_DEBUG
#include <ccan/timer/timer.h> #include <ccan/timer/timer.h>
/* Include the C files directly. */ /* Include the C files directly. */
#include <ccan/timer/timer.c> #include <ccan/timer/timer.c>
......
...@@ -148,73 +148,50 @@ static const struct timer *find_first(const struct list_head *list, ...@@ -148,73 +148,50 @@ static const struct timer *find_first(const struct list_head *list,
return prev; return prev;
} }
static const struct timer *get_first(const struct timers *timers) /* FIXME: Suboptimal */
static const struct timer *brute_force_first(const struct timers *timers)
{ {
unsigned int level, i, off; unsigned int l, i;
bool need_next;
uint64_t base;
const struct timer *found = NULL; const struct timer *found = NULL;
struct list_head *h;
if (timers->first < timers->base) { for (l = 0; l < ARRAY_SIZE(timers->level) && timers->level[l]; l++) {
base = timers->base; for (i = 0; i < PER_LEVEL; i++)
level = 0; found = find_first(&timers->level[l]->list[i], l,
} else { found);
/* May not be accurate, due to timer_del / expiry. */
level = level_of(timers, timers->first);
base = timers->first >> (TIMER_LEVEL_BITS * level);
} }
next: found = find_first(&timers->far, -1U, found);
if (!timers->level[level]) return found;
return find_first(&timers->far, -1U, NULL); }
need_next = false;
off = base % PER_LEVEL;
for (i = 0; i < PER_LEVEL; i++) {
h = &timers->level[level]->list[(i+off) % PER_LEVEL];
static const struct timer *get_first(const struct timers *timers)
{
uint64_t time;
/* Where can we start from? */
if (timers->first < timers->base)
time = timers->base;
else
time = timers->first;
/* We can have just far timers, for example. */
if (timers->level[0]) {
/* First search rest of lower buckets; we've already spilled
* so if we find one there we don't need to search further. */
unsigned int i, off = time % PER_LEVEL;
for (i = off; i < PER_LEVEL; i++) {
struct list_head *h = &timers->level[0]->list[i];
if (!list_empty(h)) if (!list_empty(h))
break; return find_first(h, 0, NULL);
/* We haven't cascaded yet, so if we wrap, we'll need to
* check next level, too. */
if (i + off == PER_LEVEL)
need_next = true;
} }
if (i == PER_LEVEL) {
level++;
base >>= TIMER_LEVEL_BITS;
if (off != 0)
/* We need *next* bucket: we've started reusing the
* one above */
base++;
goto next;
} }
/* Level 0 is exact, so they're all the same. */ /* From here on, we're searching non-normalized parts of the
found = find_first(h, level, NULL); * data structure, which is much subtler.
*
while (need_next) { * So we brute force. */
need_next = false; return brute_force_first(timers);
if (!timers->level[level+1]) {
found = find_first(&timers->far, -1U, found);
} else {
/* Current upper bucket has emptied into this
* bucket; we want *next* one. */
base >>= TIMER_LEVEL_BITS;
base++;
off = base % PER_LEVEL;
if (off == 0) {
need_next = true;
} else {
h = &timers->level[level+1]->list[off];
found = find_first(h, level+1, found);
}
}
}
return found;
} }
static bool update_first(struct timers *timers) static bool update_first(struct timers *timers)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment