summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_clock.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/kern_clock.c')
-rw-r--r--sys/kern/kern_clock.c196
1 files changed, 115 insertions, 81 deletions
diff --git a/sys/kern/kern_clock.c b/sys/kern/kern_clock.c
index af5ffda..b14c552 100644
--- a/sys/kern/kern_clock.c
+++ b/sys/kern/kern_clock.c
@@ -36,7 +36,7 @@
* SUCH DAMAGE.
*
* @(#)kern_clock.c 8.5 (Berkeley) 1/21/94
- * $Id: kern_clock.c,v 1.39 1997/09/02 20:05:37 bde Exp $
+ * $Id: kern_clock.c,v 1.40 1997/09/07 05:25:43 bde Exp $
*/
/* Portions of this software are covered by the following: */
@@ -86,9 +86,11 @@ static void initclocks __P((void *dummy));
SYSINIT(clocks, SI_SUB_CLOCKS, SI_ORDER_FIRST, initclocks, NULL)
/* Exported to machdep.c. */
-struct callout *callfree, *callout;
+struct callout *callout;
+struct callout_list callfree;
+int callwheelsize, callwheelbits, callwheelmask;
+struct callout_tailq *callwheel;
-static struct callout calltodo;
/* Some of these don't belong here, but it's easiest to concentrate them. */
static long cp_time[CPUSTATES];
@@ -154,8 +156,10 @@ int stathz;
int profhz;
static int profprocs;
int ticks;
-static int psdiv, pscnt; /* prof => stat divider */
-int psratio; /* ratio: prof / stat */
+static int softticks; /* Like ticks, but for softclock(). */
+static struct callout *nextsoftcheck; /* Next callout to be checked. */
+static int psdiv, pscnt; /* prof => stat divider */
+int psratio; /* ratio: prof / stat */
volatile struct timeval time;
volatile struct timeval mono_time;
@@ -452,26 +456,6 @@ hardclock(frame)
{
register struct callout *p1;
register struct proc *p;
- register int needsoft;
-
- /*
- * Update real-time timeout queue.
- * At front of queue are some number of events which are ``due''.
- * The time to these is <= 0 and if negative represents the
- * number of ticks which have passed since it was supposed to happen.
- * The rest of the q elements (times > 0) are events yet to happen,
- * where the time for each is given as a delta from the previous.
- * Decrementing just the first of these serves to decrement the time
- * to all events.
- */
- needsoft = 0;
- for (p1 = calltodo.c_next; p1 != NULL; p1 = p1->c_next) {
- if (--p1->c_time > 0)
- break;
- needsoft = 1;
- if (p1->c_time == 0)
- break;
- }
p = curproc;
if (p) {
@@ -677,7 +661,7 @@ hardclock(frame)
* Process callouts at a very low cpu priority, so we don't keep the
* relatively high clock interrupt priority any longer than necessary.
*/
- if (needsoft) {
+ if (TAILQ_FIRST(&callwheel[ticks & callwheelmask]) != NULL) {
if (CLKF_BASEPRI(frame)) {
/*
* Save the overhead of a software interrupt;
@@ -687,10 +671,23 @@ hardclock(frame)
softclock();
} else
setsoftclock();
+ } else if (softticks + 1 == ticks) {
+ ++softticks;
}
}
/*
+ * The callout mechanism is based on the work of Adam M. Costello and
+ * George Varghese, published in a technical report entitled "Redesigning
+ * the BSD Callout and Timer Facilities" and modified slightly for inclusion
+ * in FreeBSD by Justin T. Gibbs. The original work on the data structures
+ * used in this implementation was published by G.Varghese and A. Lauck in
+ * the paper "Hashed and Hierarchical Timing Wheels: Data Structures for
+ * the Efficient Implementation of a Timer Facility" in the Proceedings of
+ * the 11th ACM Annual Symposium on Operating Systems Principles,
+ * Austin, Texas Nov 1987.
+ */
+/*
* Software (low priority) clock interrupt.
* Run periodic events from timeout queue.
*/
@@ -699,21 +696,52 @@ void
softclock()
{
register struct callout *c;
- register void *arg;
- register void (*func) __P((void *));
register int s;
+ register int steps; /*
+ * Number of steps taken since
+ * we last allowed interrupts.
+ */
+
+ #ifndef MAX_SOFTCLOCK_STEPS
+ #define MAX_SOFTCLOCK_STEPS 100 /* Maximum allowed value of steps. */
+ #endif /* MAX_SOFTCLOCK_STEPS */
+ steps = 0;
s = splhigh();
- while ((c = calltodo.c_next) != NULL && c->c_time <= 0) {
- func = c->c_func;
- arg = c->c_arg;
- calltodo.c_next = c->c_next;
- c->c_next = callfree;
- callfree = c;
- splx(s);
- (*func)(arg);
- (void) splhigh();
+ while (softticks != ticks) {
+ c = TAILQ_FIRST(&callwheel[++softticks & callwheelmask]);
+ while (c) {
+ if (c->c_time > 0) {
+ c->c_time--;
+ c = TAILQ_NEXT(c, c_links.tqe);
+ ++steps;
+ if (steps >= MAX_SOFTCLOCK_STEPS) {
+ nextsoftcheck = c;
+ splx(s);
+ /* Give hardclock() a chance. */
+ s = splhigh();
+ c = nextsoftcheck;
+ steps = 0;
+ }
+ } else {
+ void (*c_func)(void *);
+ void *c_arg;
+
+ nextsoftcheck = TAILQ_NEXT(c, c_links.tqe);
+ TAILQ_REMOVE(c->c_bucket, c, c_links.tqe);
+ c_func = c->c_func;
+ c_arg = c->c_arg;
+ c->c_func = NULL;
+ SLIST_INSERT_HEAD(&callfree, c, c_links.sle);
+ splx(s);
+ c_func(c_arg);
+ s = splhigh();
+ steps = 0;
+ c = nextsoftcheck;
+ }
+ }
}
+ nextsoftcheck = NULL;
splx(s);
}
@@ -724,81 +752,87 @@ softclock()
* untimeout --
* Cancel previous timeout function call.
*
+ * callout_handle_init --
+ * Initialize a handle so that using it with untimeout is benign.
+ *
* See AT&T BCI Driver Reference Manual for specification. This
- * implementation differs from that one in that no identification
- * value is returned from timeout, rather, the original arguments
- * to timeout are used to identify entries for untimeout.
+ * implementation differs from that one in that although an
+ * identification value is returned from timeout, the original
+ * arguments to timeout as well as the identifier are used to
+ * identify entries for untimeout.
*/
-void
-timeout(ftn, arg, ticks)
+struct callout_handle
+timeout(ftn, arg, to_ticks)
timeout_t ftn;
void *arg;
- register int ticks;
+ register int to_ticks;
{
- register struct callout *new, *p, *t;
- register int s;
+ int s;
+ struct callout *new;
+ struct callout_handle handle;
- if (ticks <= 0)
- ticks = 1;
+ if (to_ticks <= 0)
+ to_ticks = 1;
/* Lock out the clock. */
s = splhigh();
/* Fill in the next free callout structure. */
- if (callfree == NULL)
+ new = SLIST_FIRST(&callfree);
+ if (new == NULL)
+ /* XXX Attempt to malloc first */
panic("timeout table full");
- new = callfree;
- callfree = new->c_next;
+
+ SLIST_REMOVE_HEAD(&callfree, c_links.sle);
new->c_arg = arg;
new->c_func = ftn;
+ new->c_time = to_ticks >> callwheelbits;
+ new->c_bucket = &callwheel[(ticks + to_ticks) & callwheelmask];
+ TAILQ_INSERT_TAIL(new->c_bucket, new, c_links.tqe);
- /*
- * The time for each event is stored as a difference from the time
- * of the previous event on the queue. Walk the queue, correcting
- * the ticks argument for queue entries passed. Correct the ticks
- * value for the queue entry immediately after the insertion point
- * as well. Watch out for negative c_time values; these represent
- * overdue events.
- */
- for (p = &calltodo;
- (t = p->c_next) != NULL && ticks > t->c_time; p = t)
- if (t->c_time > 0)
- ticks -= t->c_time;
- new->c_time = ticks;
- if (t != NULL)
- t->c_time -= ticks;
-
- /* Insert the new entry into the queue. */
- p->c_next = new;
- new->c_next = t;
splx(s);
+ handle.callout = new;
+ return (handle);
}
void
-untimeout(ftn, arg)
+untimeout(ftn, arg, handle)
timeout_t ftn;
void *arg;
+ struct callout_handle handle;
{
register struct callout *p, *t;
register int s;
+ /*
+ * Check for a handle that was initialized
+ * by callout_handle_init, but never used
+ * for a real timeout.
+ */
+ if (handle.callout == NULL)
+ return;
+
s = splhigh();
- for (p = &calltodo; (t = p->c_next) != NULL; p = t)
- if (t->c_func == ftn && t->c_arg == arg) {
- /* Increment next entry's tick count. */
- if (t->c_next && t->c_time > 0)
- t->c_next->c_time += t->c_time;
-
- /* Move entry from callout queue to callfree queue. */
- p->c_next = t->c_next;
- t->c_next = callfree;
- callfree = t;
- break;
+ if ((handle.callout->c_func == ftn)
+ && (handle.callout->c_arg == arg)) {
+ if (nextsoftcheck == handle.callout) {
+ nextsoftcheck = TAILQ_NEXT(handle.callout, c_links.tqe);
}
+ TAILQ_REMOVE(handle.callout->c_bucket,
+ handle.callout, c_links.tqe);
+ handle.callout->c_func = NULL;
+ SLIST_INSERT_HEAD(&callfree, handle.callout, c_links.sle);
+ }
splx(s);
}
void
+callout_handle_init(struct callout_handle *handle)
+{
+ handle->callout = NULL;
+}
+
+void
gettime(struct timeval *tvp)
{
int s;
OpenPOWER on IntegriCloud