Module Name: src Committed By: riastradh Date: Mon Jul 3 02:12:47 UTC 2017
Modified Files: src/sys/kern: kern_condvar.c Log Message: Add cv_timedwaitbt, cv_timedwaitbt_sig. Takes struct bintime maximum delay, and decrements it in place so that you can use it in a loop in case of spurious wakeup. Discussed on tech-kern a couple years ago: https://mail-index.netbsd.org/tech-kern/2015/03/23/msg018557.html Added a parameter for expressing desired precision -- not currently interpreted, but intended for a future tickless kernel with a choice of high-resolution timers. To generate a diff of this commit: cvs rdiff -u -r1.36 -r1.37 src/sys/kern/kern_condvar.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/kern/kern_condvar.c diff -u src/sys/kern/kern_condvar.c:1.36 src/sys/kern/kern_condvar.c:1.37 --- src/sys/kern/kern_condvar.c:1.36 Thu Jun 8 01:09:52 2017 +++ src/sys/kern/kern_condvar.c Mon Jul 3 02:12:47 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_condvar.c,v 1.36 2017/06/08 01:09:52 chs Exp $ */ +/* $NetBSD: kern_condvar.c,v 1.37 2017/07/03 02:12:47 riastradh Exp $ */ /*- * Copyright (c) 2006, 2007, 2008 The NetBSD Foundation, Inc. @@ -34,7 +34,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: kern_condvar.c,v 1.36 2017/06/08 01:09:52 chs Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_condvar.c,v 1.37 2017/07/03 02:12:47 riastradh Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -43,6 +43,7 @@ __KERNEL_RCSID(0, "$NetBSD: kern_condvar #include <sys/sleepq.h> #include <sys/lockdebug.h> #include <sys/cpu.h> +#include <sys/kernel.h> /* * Accessors for the private contents of the kcondvar_t data type. @@ -319,6 +320,148 @@ cv_timedwait_sig(kcondvar_t *cv, kmutex_ } /* + * Given a number of seconds, sec, and 2^64ths of a second, frac, we + * want a number of ticks for a timeout: + * + * timo = hz*(sec + frac/2^64) + * = hz*sec + hz*frac/2^64 + * = hz*sec + hz*(frachi*2^32 + fraclo)/2^64 + * = hz*sec + hz*frachi/2^32 + hz*fraclo/2^64, + * + * where frachi is the high 32 bits of frac and fraclo is the + * low 32 bits. + * + * We assume hz < INT_MAX/2 < UINT32_MAX, so + * + * hz*fraclo/2^64 < fraclo*2^32/2^64 <= 1, + * + * since fraclo < 2^32. + * + * We clamp the result at INT_MAX/2 for a timeout in ticks, since we + * can't represent timeouts higher than INT_MAX in cv_timedwait, and + * spurious wakeup is OK. Moreover, we don't want to wrap around, + * because we compute end - start in ticks in order to compute the + * remaining timeout, and that difference cannot wrap around, so we use + * a timeout less than INT_MAX. Using INT_MAX/2 provides plenty of + * margin for paranoia and will exceed most waits in practice by far. + */ +static unsigned +bintime2timo(const struct bintime *bt) +{ + + KASSERT(hz < INT_MAX/2); + CTASSERT(INT_MAX/2 < UINT32_MAX); + if (bt->sec > ((INT_MAX/2)/hz)) + return INT_MAX/2; + if ((hz*(bt->frac >> 32) >> 32) > (INT_MAX/2 - hz*bt->sec)) + return INT_MAX/2; + + return hz*bt->sec + (hz*(bt->frac >> 32) >> 32); +} + +/* + * timo is in units of ticks. We want units of seconds and 2^64ths of + * a second. We know hz = 1 sec/tick, and 2^64 = 1 sec/(2^64th of a + * second), from which we can conclude 2^64 / hz = 1 (2^64th of a + * second)/tick. So for the fractional part, we compute + * + * frac = rem * 2^64 / hz + * = ((rem * 2^32) / hz) * 2^32 + * + * Using truncating integer division instead of real division will + * leave us with only about 32 bits of precision, which means about + * 1/4-nanosecond resolution, which is good enough for our purposes. + */ +static struct bintime +timo2bintime(unsigned timo) +{ + + return (struct bintime) { + .sec = timo / hz, + .frac = (((uint64_t)(timo % hz) << 32)/hz << 32), + }; +} + +/* + * cv_timedwaitbt: + * + * Wait on a condition variable until awoken or the specified + * timeout expires. Returns zero if awoken normally or + * EWOULDBLOCK if the timeout expires. + * + * On entry, bt is a timeout in bintime. cv_timedwaitbt subtracts + * the time slept, so on exit, bt is the time remaining after + * sleeping. No infinite timeout; use cv_wait_sig instead. + * + * epsilon is a requested maximum error in timeout (excluding + * spurious wakeups). Currently not used, will be used in the + * future to choose between low- and high-resolution timers. + */ +int +cv_timedwaitbt(kcondvar_t *cv, kmutex_t *mtx, struct bintime *bt, + const struct bintime *epsilon __unused) +{ + struct bintime slept; + unsigned start, end; + int error; + + /* + * hardclock_ticks is technically int, but nothing special + * happens instead of overflow, so we assume two's-complement + * wraparound and just treat it as unsigned. + */ + start = hardclock_ticks; + error = cv_timedwait(cv, mtx, bintime2timo(bt)); + end = hardclock_ticks; + + slept = timo2bintime(end - start); + /* bt := bt - slept */ + bintime_sub(bt, &slept); + + return error; +} + +/* + * cv_timedwaitbt_sig: + * + * Wait on a condition variable until awoken, the specified + * timeout expires, or interrupted by a signal. Returns zero if + * awoken normally, EWOULDBLOCK if the timeout expires, or + * EINTR/ERESTART if interrupted by a signal. + * + * On entry, bt is a timeout in bintime. cv_timedwaitbt_sig + * subtracts the time slept, so on exit, bt is the time remaining + * after sleeping. No infinite timeout; use cv_wait instead. + * + * epsilon is a requested maximum error in timeout (excluding + * spurious wakeups). Currently not used, will be used in the + * future to choose between low- and high-resolution timers. + */ +int +cv_timedwaitbt_sig(kcondvar_t *cv, kmutex_t *mtx, struct bintime *bt, + const struct bintime *epsilon __unused) +{ + struct bintime slept; + unsigned start, end; + int error; + + /* + * hardclock_ticks is technically int, but nothing special + * happens instead of overflow, so we assume two's-complement + * wraparound and just treat it as unsigned. + */ + start = hardclock_ticks; + error = cv_timedwait_sig(cv, mtx, bintime2timo(bt)); + end = hardclock_ticks; + + slept = timo2bintime(end - start); + /* bt := bt - slept */ + bintime_sub(bt, &slept); + + return error; +} + +/* * cv_signal: * * Wake the highest priority LWP waiting on a condition variable.