[PATCH] copperplate/timerobj: fix start/start, start/stop races

Jan Kiszka jan.kiszka at siemens.com
Fri Dec 11 07:50:42 CET 2020


On 04.12.20 16:31, Philippe Gerum wrote:
> From: Philippe Gerum <rpm at xenomai.org>
> 
> 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.html
> 
> Signed-off-by: Philippe Gerum <rpm at xenomai.org>
> ---
>  lib/copperplate/timerobj.c | 27 ++++++++++++++++++---------
>  1 file changed, 18 insertions(+), 9 deletions(-)
> 
> diff --git a/lib/copperplate/timerobj.c b/lib/copperplate/timerobj.c
> index cbfcda566..08cc0d3b9 100644
> --- a/lib/copperplate/timerobj.c
> +++ b/lib/copperplate/timerobj.c
> @@ -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;
> 

Thanks, applied.

Jan

-- 
Siemens AG, T RDA IOT
Corporate Competence Center Embedded Linux



More information about the Xenomai mailing list