summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/os/callout.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/os/callout.c')
-rw-r--r--usr/src/uts/common/os/callout.c145
1 files changed, 131 insertions, 14 deletions
diff --git a/usr/src/uts/common/os/callout.c b/usr/src/uts/common/os/callout.c
index 1d13f7d179..92b52e27af 100644
--- a/usr/src/uts/common/os/callout.c
+++ b/usr/src/uts/common/os/callout.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -79,6 +78,49 @@ static callout_table_t *callout_table[CALLOUT_TABLES];
CALLOUT_HASH_##INSDEL(ct->ct_lbhash[CALLOUT_LBHASH(runtime)], \
cp, c_lbnext, c_lbprev)
+#define CALLOUT_HRES_INSERT(ct, cp, cnext, cprev, hresms) \
+{ \
+ callout_t *nextp = ct->ct_hresq; \
+ callout_t *prevp; \
+ \
+ if (nextp == NULL || hresms <= nextp->c_hresms) { \
+ cp->cnext = ct->ct_hresq; \
+ ct->ct_hresq = cp; \
+ cp->cprev = NULL; \
+ if (cp->cnext != NULL) \
+ cp->cnext->cprev = cp; \
+ } else { \
+ do { \
+ prevp = nextp; \
+ nextp = nextp->cnext; \
+ } while (nextp != NULL && hresms > nextp->c_hresms); \
+ prevp->cnext = cp; \
+ cp->cprev = prevp; \
+ cp->cnext = nextp; \
+ if (nextp != NULL) \
+ nextp->cprev = cp; \
+ } \
+}
+
+#define CALLOUT_HRES_DELETE(ct, cp, cnext, cprev, hresms) \
+{ \
+ if (cp == ct->ct_hresq) { \
+ ct->ct_hresq = cp->cnext; \
+ if (cp->cnext != NULL) \
+ cp->cnext->cprev = NULL; \
+ } else { \
+ cp->cprev->cnext = cp->cnext; \
+ if (cp->cnext != NULL) \
+ cp->cnext->cprev = cp->cprev; \
+ } \
+}
+
+#define CALLOUT_HRES_UPDATE(INSDEL, ct, cp, id, hresms) \
+ ASSERT(MUTEX_HELD(&ct->ct_lock)); \
+ ASSERT(cp->c_xid == id); \
+ CALLOUT_HRES_##INSDEL(ct, cp, c_hrnext, \
+ c_hrprev, hresms)
+
/*
* Allocate a callout structure. We try quite hard because we
* can't sleep, and if we can't do the allocation, we're toast.
@@ -106,9 +148,13 @@ static timeout_id_t
timeout_common(void (*func)(void *), void *arg, clock_t delta,
callout_table_t *ct)
{
- callout_t *cp;
- callout_id_t id;
- clock_t runtime;
+ callout_t *cp;
+ callout_id_t id;
+ clock_t runtime;
+ timestruc_t now;
+ int64_t hresms;
+
+ gethrestime(&now);
mutex_enter(&ct->ct_lock);
@@ -127,6 +173,11 @@ timeout_common(void (*func)(void *), void *arg, clock_t delta,
delta = 1;
cp->c_runtime = runtime = lbolt + delta;
+ /* Calculate the future time in milli-second */
+ hresms = now.tv_sec * MILLISEC + now.tv_nsec / MICROSEC +
+ TICK_TO_MSEC(delta);
+ cp->c_hresms = hresms;
+
/*
* Assign an ID to this callout
*/
@@ -140,6 +191,7 @@ timeout_common(void (*func)(void *), void *arg, clock_t delta,
cp->c_xid = id;
CALLOUT_HASH_UPDATE(INSERT, ct, cp, id, runtime);
+ CALLOUT_HRES_UPDATE(INSERT, ct, cp, id, hresms);
mutex_exit(&ct->ct_lock);
@@ -184,6 +236,7 @@ untimeout(timeout_id_t id_arg)
clock_t time_left = runtime - lbolt;
CALLOUT_HASH_UPDATE(DELETE, ct, cp, id, runtime);
+ CALLOUT_HRES_UPDATE(DELETE, ct, cp, id, 0);
cp->c_idnext = ct->ct_freelist;
ct->ct_freelist = cp;
mutex_exit(&ct->ct_lock);
@@ -245,9 +298,11 @@ untimeout(timeout_id_t id_arg)
static void
callout_execute(callout_table_t *ct)
{
- callout_t *cp;
- callout_id_t xid;
- clock_t runtime;
+ callout_t *cp;
+ callout_id_t xid;
+ clock_t runtime;
+ timestruc_t now;
+ int64_t hresms;
mutex_enter(&ct->ct_lock);
@@ -267,14 +322,16 @@ callout_execute(callout_table_t *ct)
mutex_enter(&ct->ct_lock);
/*
- * Delete callout from hash tables, return to freelist,
- * and tell anyone who cares that we're done.
+ * Delete callout from both the hash tables and the
+ * hres queue, return it to freelist, and tell anyone
+ * who cares that we're done.
* Even though we dropped and reacquired ct->ct_lock,
* it's OK to pick up where we left off because only
* newly-created timeouts can precede cp on ct_lbhash,
* and those timeouts cannot be due on this tick.
*/
CALLOUT_HASH_UPDATE(DELETE, ct, cp, xid, runtime);
+ CALLOUT_HRES_UPDATE(DELETE, ct, cp, xid, hresms);
cp->c_idnext = ct->ct_freelist;
ct->ct_freelist = cp;
cp->c_xid = 0; /* Indicate completion for c_done */
@@ -289,6 +346,44 @@ callout_execute(callout_table_t *ct)
if (ct->ct_runtime == runtime)
ct->ct_runtime = runtime + 1;
}
+
+ gethrestime(&now);
+
+ /* Calculate the current time in milli-second */
+ hresms = now.tv_sec * MILLISEC + now.tv_nsec / MICROSEC;
+
+ cp = ct->ct_hresq;
+ while (cp != NULL && hresms >= cp->c_hresms) {
+ xid = cp->c_xid;
+ if (xid & CALLOUT_EXECUTING) {
+ cp = cp->c_hrnext;
+ continue;
+ }
+ cp->c_executor = curthread;
+ cp->c_xid = xid |= CALLOUT_EXECUTING;
+ runtime = cp->c_runtime;
+ mutex_exit(&ct->ct_lock);
+ DTRACE_PROBE1(callout__start, callout_t *, cp);
+ (*cp->c_func)(cp->c_arg);
+ DTRACE_PROBE1(callout__end, callout_t *, cp);
+ mutex_enter(&ct->ct_lock);
+
+ /*
+ * See comments above.
+ */
+ CALLOUT_HASH_UPDATE(DELETE, ct, cp, xid, runtime);
+ CALLOUT_HRES_UPDATE(DELETE, ct, cp, xid, hresms);
+ cp->c_idnext = ct->ct_freelist;
+ ct->ct_freelist = cp;
+ cp->c_xid = 0; /* Indicate completion for c_done */
+ cv_broadcast(&cp->c_done);
+
+ /*
+ * Start over from the head of the list, see if
+ * any timeout bearing an earlier hres time.
+ */
+ cp = ct->ct_hresq;
+ }
mutex_exit(&ct->ct_lock);
}
@@ -298,8 +393,10 @@ callout_execute(callout_table_t *ct)
static void
callout_schedule_1(callout_table_t *ct)
{
- callout_t *cp;
- clock_t curtime, runtime;
+ callout_t *cp;
+ clock_t curtime, runtime;
+ timestruc_t now;
+ int64_t hresms;
mutex_enter(&ct->ct_lock);
ct->ct_curtime = curtime = lbolt;
@@ -320,6 +417,26 @@ callout_schedule_1(callout_table_t *ct)
}
ct->ct_runtime++;
}
+
+ gethrestime(&now);
+
+ /* Calculate the current time in milli-second */
+ hresms = now.tv_sec * MILLISEC + now.tv_nsec / MICROSEC;
+
+ cp = ct->ct_hresq;
+ while (cp != NULL && hresms >= cp->c_hresms) {
+ if (cp->c_xid & CALLOUT_EXECUTING) {
+ cp = cp->c_hrnext;
+ continue;
+ }
+ mutex_exit(&ct->ct_lock);
+ if (ct->ct_taskq == NULL)
+ softcall((void (*)(void *))callout_execute, ct);
+ else
+ (void) taskq_dispatch(ct->ct_taskq,
+ (task_func_t *)callout_execute, ct, KM_NOSLEEP);
+ return;
+ }
mutex_exit(&ct->ct_lock);
}