Commit 1de59c7c authored by Philippe Gerum's avatar Philippe Gerum Committed by Jan Kiszka

copperplate/timerobj: fix start/start, start/stop races

Concurrent timer start vs start, or start vs stop operations may end
up confusing the logic, like:

- a periodic timer being reinserted into the dispatch queue while
  disabled.

- dirty reads of inconsistent ( date, interval, handler ) triplets by
  the dispatch loop, as the timer properties are being updated
  concurrently.

Fix this by moving all updates to the timer properties under the
protection of the dispatch lock (svlock), likewise for loads.

Issue reported by Ronny Meeus, with a preliminary fix:
https://xenomai.org/pipermail/xenomai/2020-December/043907.htmlSigned-off-by: Philippe Gerum's avatarPhilippe Gerum <rpm@xenomai.org>
Signed-off-by: Jan Kiszka's avatarJan Kiszka <jan.kiszka@siemens.com>
parent 24592c28
Pipeline #5603 failed with stage
in 15 minutes and 58 seconds
......@@ -98,6 +98,7 @@ static int server_prologue(void *arg)
static void *timerobj_server(void *arg)
{
void (*handler)(struct timerobj *tmobj);
struct timespec now, value, interval;
struct timerobj *tmobj, *tmp;
sigset_t set;
......@@ -120,17 +121,18 @@ static void *timerobj_server(void *arg)
pvlist_for_each_entry_safe(tmobj, tmp, &svtimers, next) {
value = tmobj->itspec.it_value;
interval = tmobj->itspec.it_interval;
handler = tmobj->handler;
if (timespec_after(&value, &now))
break;
pvlist_remove_init(&tmobj->next);
interval = tmobj->itspec.it_interval;
if (interval.tv_sec > 0 || interval.tv_nsec > 0) {
timespec_add(&tmobj->itspec.it_value,
&value, &interval);
timerobj_enqueue(tmobj);
}
write_unlock(&svlock);
tmobj->handler(tmobj);
handler(tmobj);
write_lock_nocancel(&svlock);
}
......@@ -218,8 +220,7 @@ int timerobj_start(struct timerobj *tmobj,
void (*handler)(struct timerobj *tmobj),
struct itimerspec *it) /* lock held, dropped */
{
tmobj->handler = handler;
tmobj->itspec = *it;
int ret = 0;
/*
* We hold the queue lock long enough to prevent the timer
......@@ -232,14 +233,23 @@ int timerobj_start(struct timerobj *tmobj,
*/
write_lock_nocancel(&svlock);
if (__RT(timer_settime(tmobj->timer, TIMER_ABSTIME, it, NULL)))
return __bt(-errno);
if (pvholder_linked(&tmobj->next))
pvlist_remove_init(&tmobj->next);
tmobj->handler = handler;
tmobj->itspec = *it;
if (__RT(timer_settime(tmobj->timer, TIMER_ABSTIME, it, NULL))) {
ret = __bt(-errno);
goto fail;
}
timerobj_enqueue(tmobj);
fail:
write_unlock(&svlock);
timerobj_unlock(tmobj);
return 0;
return ret;
}
int timerobj_stop(struct timerobj *tmobj) /* lock held, dropped */
......@@ -251,10 +261,9 @@ int timerobj_stop(struct timerobj *tmobj) /* lock held, dropped */
if (pvholder_linked(&tmobj->next))
pvlist_remove_init(&tmobj->next);
write_unlock(&svlock);
__RT(timer_settime(tmobj->timer, 0, &itimer_stop, NULL));
tmobj->handler = NULL;
write_unlock(&svlock);
timerobj_unlock(tmobj);
return 0;
......
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