Emulate illumos interface cv_timedwait_hires()

Needed for Illumos #3582. This interface is supposed to support
a variable-resolution timeout with nanosecond granularity.  This
implementation rounds up to microsecond resolution, as nanosecond-
precision timing is rarely needed for real-world performance
tuning and may incur unnecessary busy-waiting.  usleep_range() is
used if available, otherwise udelay() or msleep() are used
depending on the length of the delay interval.

Add flags from sys/callo.h as these are used to control the behavior of
cv_timedwait_hires().  Specifically,

CALLOUT_FLAG_ABSOLUTE
    Normally, the expiration passed to the timeout API functions is
    an expiration interval. If this flag is specified, then it is
    interpreted as the expiration time itself.

CALLOUT_FLAG_ROUNDUP
    Roundup the expiration time to the next resolution boundary. If this
    flag is not specified, the expiration time is rounded down.

References:
    https://www.illumos.org/issues/3582
    illumos/illumos-gate@0689f76

Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #304
This commit is contained in:
Ned Bass
2013-11-01 13:37:58 -07:00
committed by Brian Behlendorf
parent 0f4b9a5806
commit 184c687387
8 changed files with 212 additions and 0 deletions
+81
View File
@@ -226,6 +226,87 @@ __cv_timedwait_interruptible(kcondvar_t *cvp, kmutex_t *mp, clock_t exp_time)
}
EXPORT_SYMBOL(__cv_timedwait_interruptible);
/*
*'expire_time' argument is an absolute clock time in nanoseconds.
* Return value is time left (expire_time - now) or -1 if timeout occurred.
*/
static clock_t
__cv_timedwait_hires(kcondvar_t *cvp, kmutex_t *mp,
hrtime_t expire_time, int state)
{
DEFINE_WAIT(wait);
hrtime_t time_left, now;
unsigned long time_left_us;
SENTRY;
ASSERT(cvp);
ASSERT(mp);
ASSERT(cvp->cv_magic == CV_MAGIC);
ASSERT(mutex_owned(mp));
atomic_inc(&cvp->cv_refs);
if (cvp->cv_mutex == NULL)
cvp->cv_mutex = mp;
/* Ensure the same mutex is used by all callers */
ASSERT(cvp->cv_mutex == mp);
now = gethrtime();
time_left = expire_time - now;
if (time_left <= 0) {
atomic_dec(&cvp->cv_refs);
SRETURN(-1);
}
time_left_us = time_left / NSEC_PER_USEC;
prepare_to_wait_exclusive(&cvp->cv_event, &wait, state);
atomic_inc(&cvp->cv_waiters);
/* Mutex should be dropped after prepare_to_wait() this
* ensures we're linked in to the waiters list and avoids the
* race where 'cvp->cv_waiters > 0' but the list is empty. */
mutex_exit(mp);
/* Allow a 100 us range to give kernel an opportunity to coalesce
* interrupts */
usleep_range(time_left_us, time_left_us + 100);
mutex_enter(mp);
/* No more waiters a different mutex could be used */
if (atomic_dec_and_test(&cvp->cv_waiters)) {
cvp->cv_mutex = NULL;
wake_up(&cvp->cv_destroy);
}
finish_wait(&cvp->cv_event, &wait);
atomic_dec(&cvp->cv_refs);
time_left = expire_time - gethrtime();
SRETURN(time_left > 0 ? time_left : -1);
}
/*
* Compatibility wrapper for the cv_timedwait_hires() Illumos interface.
*/
clock_t
cv_timedwait_hires(kcondvar_t *cvp, kmutex_t *mp, hrtime_t tim,
hrtime_t res, int flag)
{
if (res > 1) {
/*
* Align expiration to the specified resolution.
*/
if (flag & CALLOUT_FLAG_ROUNDUP)
tim += res - 1;
tim = (tim / res) * res;
}
if (!(flag & CALLOUT_FLAG_ABSOLUTE))
tim += gethrtime();
return __cv_timedwait_hires(cvp, mp, tim, TASK_UNINTERRUPTIBLE);
}
EXPORT_SYMBOL(cv_timedwait_hires);
void
__cv_signal(kcondvar_t *cvp)
{