diff options
author | Ned Bass <[email protected]> | 2013-11-01 13:37:58 -0700 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2013-11-04 09:49:24 -0800 |
commit | 184c6873874c350bfb0b74f9e08ec8d89750d603 (patch) | |
tree | 504643ad1332fce93a920b7e6bbb1b30cd216dbd /module/spl/spl-condvar.c | |
parent | 0f4b9a58063d95b4da26c64dab4054d6272e0973 (diff) |
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 <[email protected]>
Closes #304
Diffstat (limited to 'module/spl/spl-condvar.c')
-rw-r--r-- | module/spl/spl-condvar.c | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/module/spl/spl-condvar.c b/module/spl/spl-condvar.c index 60cf726f9..283648aac 100644 --- a/module/spl/spl-condvar.c +++ b/module/spl/spl-condvar.c @@ -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) { |