New callout implementation. This is based on callwheel implementation

done by Artur Grabowski and Thomas Nordin for OpenBSD, which is more
efficient in several ways than the callwheel implementation that it is
replacing.  It has been adapted to our pre-existing callout API, and
also provides the slightly more efficient (and much more intuitive)
API (adapted to the callout_*() naming scheme) that the OpenBSD version
provides.

Among other things, this shaves a bunch of cycles off rescheduling-in-
the-future a callout which is already scheduled, which the common case
for TCP timers (notably REXMT and KEEP).

The API has been simplified a bit, as well.  The (very confusing to
a good many people) "ACTIVE" state for callouts has gone away.  There
is now only "PENDING" (scheduled to fire in the future) and "EXPIRED"
(has fired, and the function called).

Kernel version bump not done; we'll ride the 1.6N bump that happened
with the malloc(9) change.
This commit is contained in:
thorpej 2003-02-04 01:21:03 +00:00
parent d30d72b4a5
commit 1b84adbe5f
10 changed files with 503 additions and 529 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: files,v 1.594 2003/02/03 23:02:42 matt Exp $
# $NetBSD: files,v 1.595 2003/02/04 01:21:03 thorpej Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@ -1073,6 +1073,7 @@ file kern/kern_subr.c
file kern/kern_synch.c
file kern/kern_sysctl.c
file kern/kern_time.c
file kern/kern_timeout.c
file kern/kern_xxx.c
file kern/kgdb_stub.c kgdb
file kern/subr_autoconf.c

View File

@ -1,4 +1,4 @@
/* $NetBSD: db_xxx.c,v 1.19 2003/01/23 12:41:33 pk Exp $ */
/* $NetBSD: db_xxx.c,v 1.20 2003/02/04 01:21:04 thorpej Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1991, 1993
@ -41,7 +41,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: db_xxx.c,v 1.19 2003/01/23 12:41:33 pk Exp $");
__KERNEL_RCSID(0, "$NetBSD: db_xxx.c,v 1.20 2003/02/04 01:21:04 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -215,37 +215,6 @@ db_show_all_procs(db_expr_t addr, int haddr, db_expr_t count, char *modif)
}
}
void
db_show_callout(db_expr_t addr, int haddr, db_expr_t count, char *modif)
{
uint64_t hint;
int i;
for (i = 0; i < callwheelsize; i++) {
struct callout_queue *bucket = &callwheel[i];
struct callout *c = TAILQ_FIRST(&bucket->cq_q);
if (c) {
db_printf("bucket %d (hint %llx):\n", i,
(long long) bucket->cq_hint);
hint = UQUAD_MAX;
while (c) {
if (c->c_time < hint)
hint = c->c_time;
db_printf("%p: time %llx arg %p flags %x "
"func %p: ", c, (long long) c->c_time,
c->c_arg, c->c_flags, c->c_func);
db_printsym((u_long)c->c_func, DB_STGY_PROC,
db_printf);
db_printf("\n");
c = TAILQ_NEXT(c, c_link);
}
if (bucket->cq_hint < hint)
printf("** HINT IS STALE\n");
}
}
}
void
db_dmesg(db_expr_t addr, int haddr, db_expr_t count, char *modif)
{

View File

@ -1,4 +1,4 @@
/* $NetBSD: kern_allocsys.c,v 1.22 2003/02/01 21:07:01 erh Exp $ */
/* $NetBSD: kern_allocsys.c,v 1.23 2003/02/04 01:21:04 thorpej Exp $ */
/*-
* Copyright (c) 1999 The NetBSD Foundation, Inc.
@ -71,16 +71,14 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_allocsys.c,v 1.22 2003/02/01 21:07:01 erh Exp $");
__KERNEL_RCSID(0, "$NetBSD: kern_allocsys.c,v 1.23 2003/02/04 01:21:04 thorpej Exp $");
#include "opt_bufcache.h"
#include "opt_callout.h"
#include "opt_sysv.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/callout.h>
#ifdef SYSVMSG
#include <sys/msg.h>
#endif
@ -130,14 +128,6 @@ caddr_t
allocsys(caddr_t v, caddr_t (*mdcallback)(caddr_t))
{
/* Calculate the number of callwheels if necessary. */
if (callwheelsize == 0)
callout_setsize();
ALLOCSYS(v, callwheel, struct callout_queue, callwheelsize);
#ifdef CALLWHEEL_STATS
ALLOCSYS(v, callwheel_sizes, int, callwheelsize);
#endif
#ifdef SYSVSHM
ALLOCSYS(v, shmsegs, struct shmid_ds, shminfo.shmmni);
#endif

View File

@ -1,4 +1,4 @@
/* $NetBSD: kern_clock.c,v 1.83 2003/01/27 22:38:24 pk Exp $ */
/* $NetBSD: kern_clock.c,v 1.84 2003/02/04 01:21:04 thorpej Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
@ -78,9 +78,8 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_clock.c,v 1.83 2003/01/27 22:38:24 pk Exp $");
__KERNEL_RCSID(0, "$NetBSD: kern_clock.c,v 1.84 2003/02/04 01:21:04 thorpej Exp $");
#include "opt_callout.h"
#include "opt_ntp.h"
#include "opt_perfctrs.h"
@ -96,9 +95,6 @@ __KERNEL_RCSID(0, "$NetBSD: kern_clock.c,v 1.83 2003/01/27 22:38:24 pk Exp $");
#include <sys/timex.h>
#include <sys/sched.h>
#include <sys/time.h>
#ifdef CALLWHEEL_STATS
#include <sys/device.h>
#endif
#include <machine/cpu.h>
#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
@ -325,7 +321,7 @@ int profhz;
int profsrc;
int schedhz;
int profprocs;
int softclock_running; /* 1 => softclock() is running */
int hardclock_ticks;
static int psdiv; /* prof => stat divider */
int psratio; /* ratio: prof / stat */
int tickfix, tickfixinterval; /* used if tick not really integral */
@ -344,74 +340,7 @@ int shifthz;
volatile struct timeval time __attribute__((__aligned__(__alignof__(quad_t))));
volatile struct timeval mono_time;
/*
* 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 Justin Gibbs's subsequent
* integration into FreeBSD, modified for NetBSD by Jason R. Thorpe.
*
* 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 System Principles, Austin, Texas,
* November 1987.
*/
struct callout_queue *callwheel;
int callwheelsize, callwheelbits, callwheelmask;
static struct callout *nextsoftcheck; /* next callout to be checked */
#ifdef CALLWHEEL_STATS
int *callwheel_sizes; /* per-bucket length count */
struct evcnt callwheel_collisions; /* number of hash collisions */
struct evcnt callwheel_maxlength; /* length of the longest hash chain */
struct evcnt callwheel_count; /* # callouts currently */
struct evcnt callwheel_established; /* # callouts established */
struct evcnt callwheel_fired; /* # callouts that fired */
struct evcnt callwheel_disestablished; /* # callouts disestablished */
struct evcnt callwheel_changed; /* # callouts changed */
struct evcnt callwheel_softclocks; /* # times softclock() called */
struct evcnt callwheel_softchecks; /* # checks per softclock() */
struct evcnt callwheel_softempty; /* # empty buckets seen */
struct evcnt callwheel_hintworked; /* # times hint saved scan */
#endif /* CALLWHEEL_STATS */
/*
* This value indicates the number of consecutive callouts that
* will be checked before we allow interrupts to have a chance
* again.
*/
#ifndef MAX_SOFTCLOCK_STEPS
#define MAX_SOFTCLOCK_STEPS 100
#endif
struct simplelock callwheel_slock;
#define CALLWHEEL_LOCK(s) \
do { \
s = splsched(); \
simple_lock(&callwheel_slock); \
} while (/*CONSTCOND*/ 0)
#define CALLWHEEL_UNLOCK(s) \
do { \
simple_unlock(&callwheel_slock); \
splx(s); \
} while (/*CONSTCOND*/ 0)
static void callout_stop_locked(struct callout *);
/*
* These are both protected by callwheel_lock.
* XXX SHOULD BE STATIC!!
*/
u_int64_t hardclock_ticks, softclock_ticks;
#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
void softclock(void *);
void *softclock_si;
#endif
/*
* Initialize clock frequencies and start both clocks running.
@ -527,7 +456,6 @@ hardclock(struct clockframe *frame)
extern long timedelta;
struct cpu_info *ci = curcpu();
struct ptimer *pt;
int s;
#ifdef NTP
int time_update;
int ltemp;
@ -574,6 +502,7 @@ hardclock(struct clockframe *frame)
* if we are still adjusting the time (see adjtime()),
* ``tickdelta'' may also be added in.
*/
hardclock_ticks++;
delta = tick;
#ifndef NTP
@ -888,21 +817,16 @@ hardclock(struct clockframe *frame)
#endif /* NTP */
/*
* Update real-time timeout queue.
* Process callouts at a very low cpu priority, so we don't keep the
* relatively high clock interrupt priority any longer than necessary.
*/
CALLWHEEL_LOCK(s);
hardclock_ticks++;
if (! TAILQ_EMPTY(&callwheel[hardclock_ticks & callwheelmask].cq_q)) {
CALLWHEEL_UNLOCK(s);
if (callout_hardclock()) {
if (CLKF_BASEPRI(frame)) {
/*
* Save the overhead of a software interrupt;
* it will happen as soon as we return, so do
* it now.
*
* NOTE: If we're at ``base priority'', softclock()
* was not already running.
*/
spllowersoftclock();
KERNEL_LOCK(LK_CANRECURSE|LK_EXCLUSIVE);
@ -915,317 +839,9 @@ hardclock(struct clockframe *frame)
setsoftclock();
#endif
}
return;
} else if (softclock_running == 0 &&
(softclock_ticks + 1) == hardclock_ticks) {
softclock_ticks++;
}
CALLWHEEL_UNLOCK(s);
}
/*
* Software (low priority) clock interrupt.
* Run periodic events from timeout queue.
*/
/*ARGSUSED*/
void
softclock(void *v)
{
struct callout_queue *bucket;
struct callout *c;
void (*func)(void *);
void *arg;
int s, idx;
int steps = 0;
CALLWHEEL_LOCK(s);
softclock_running = 1;
#ifdef CALLWHEEL_STATS
callwheel_softclocks.ev_count++;
#endif
while (softclock_ticks != hardclock_ticks) {
softclock_ticks++;
idx = (int)(softclock_ticks & callwheelmask);
bucket = &callwheel[idx];
c = TAILQ_FIRST(&bucket->cq_q);
if (c == NULL) {
#ifdef CALLWHEEL_STATS
callwheel_softempty.ev_count++;
#endif
continue;
}
if (softclock_ticks < bucket->cq_hint) {
#ifdef CALLWHEEL_STATS
callwheel_hintworked.ev_count++;
#endif
continue;
}
bucket->cq_hint = UQUAD_MAX;
while (c != NULL) {
#ifdef CALLWHEEL_STATS
callwheel_softchecks.ev_count++;
#endif
if (c->c_time != softclock_ticks) {
if (c->c_time < bucket->cq_hint)
bucket->cq_hint = c->c_time;
c = TAILQ_NEXT(c, c_link);
if (++steps >= MAX_SOFTCLOCK_STEPS) {
nextsoftcheck = c;
/* Give interrupts a chance. */
CALLWHEEL_UNLOCK(s);
CALLWHEEL_LOCK(s);
c = nextsoftcheck;
steps = 0;
}
} else {
nextsoftcheck = TAILQ_NEXT(c, c_link);
TAILQ_REMOVE(&bucket->cq_q, c, c_link);
#ifdef CALLWHEEL_STATS
callwheel_sizes[idx]--;
callwheel_fired.ev_count++;
callwheel_count.ev_count--;
#endif
func = c->c_func;
arg = c->c_arg;
c->c_func = NULL;
c->c_flags &= ~CALLOUT_PENDING;
CALLWHEEL_UNLOCK(s);
(*func)(arg);
CALLWHEEL_LOCK(s);
steps = 0;
c = nextsoftcheck;
}
}
if (TAILQ_EMPTY(&bucket->cq_q))
bucket->cq_hint = UQUAD_MAX;
}
nextsoftcheck = NULL;
softclock_running = 0;
CALLWHEEL_UNLOCK(s);
}
/*
* callout_setsize:
*
* Determine how many callwheels are necessary and
* set hash mask. Called from allocsys().
*/
void
callout_setsize(void)
{
for (callwheelsize = 1; callwheelsize < ncallout; callwheelsize <<= 1)
/* loop */ ;
callwheelmask = callwheelsize - 1;
}
/*
* callout_startup:
*
* Initialize the callwheel buckets.
*/
void
callout_startup(void)
{
int i;
for (i = 0; i < callwheelsize; i++) {
callwheel[i].cq_hint = UQUAD_MAX;
TAILQ_INIT(&callwheel[i].cq_q);
}
simple_lock_init(&callwheel_slock);
#ifdef CALLWHEEL_STATS
evcnt_attach_dynamic(&callwheel_collisions, EVCNT_TYPE_MISC,
NULL, "callwheel", "collisions");
evcnt_attach_dynamic(&callwheel_maxlength, EVCNT_TYPE_MISC,
NULL, "callwheel", "maxlength");
evcnt_attach_dynamic(&callwheel_count, EVCNT_TYPE_MISC,
NULL, "callwheel", "count");
evcnt_attach_dynamic(&callwheel_established, EVCNT_TYPE_MISC,
NULL, "callwheel", "established");
evcnt_attach_dynamic(&callwheel_fired, EVCNT_TYPE_MISC,
NULL, "callwheel", "fired");
evcnt_attach_dynamic(&callwheel_disestablished, EVCNT_TYPE_MISC,
NULL, "callwheel", "disestablished");
evcnt_attach_dynamic(&callwheel_changed, EVCNT_TYPE_MISC,
NULL, "callwheel", "changed");
evcnt_attach_dynamic(&callwheel_softclocks, EVCNT_TYPE_MISC,
NULL, "callwheel", "softclocks");
evcnt_attach_dynamic(&callwheel_softempty, EVCNT_TYPE_MISC,
NULL, "callwheel", "softempty");
evcnt_attach_dynamic(&callwheel_hintworked, EVCNT_TYPE_MISC,
NULL, "callwheel", "hintworked");
#endif /* CALLWHEEL_STATS */
}
/*
* callout_init:
*
* Initialize a callout structure so that it can be used
* by callout_reset() and callout_stop().
*/
void
callout_init(struct callout *c)
{
memset(c, 0, sizeof(*c));
}
/*
* callout_reset:
*
* Establish or change a timeout.
*/
void
callout_reset(struct callout *c, int ticks, void (*func)(void *), void *arg)
{
struct callout_queue *bucket;
int s;
if (ticks <= 0)
ticks = 1;
CALLWHEEL_LOCK(s);
/*
* If this callout's timer is already running, cancel it
* before we modify it.
*/
if (c->c_flags & CALLOUT_PENDING) {
callout_stop_locked(c); /* Already locked */
#ifdef CALLWHEEL_STATS
callwheel_changed.ev_count++;
#endif
}
c->c_arg = arg;
c->c_func = func;
c->c_flags = CALLOUT_ACTIVE | CALLOUT_PENDING;
c->c_time = hardclock_ticks + ticks;
bucket = &callwheel[c->c_time & callwheelmask];
#ifdef CALLWHEEL_STATS
if (! TAILQ_EMPTY(&bucket->cq_q))
callwheel_collisions.ev_count++;
#endif
TAILQ_INSERT_TAIL(&bucket->cq_q, c, c_link);
if (c->c_time < bucket->cq_hint)
bucket->cq_hint = c->c_time;
#ifdef CALLWHEEL_STATS
callwheel_count.ev_count++;
callwheel_established.ev_count++;
if (++callwheel_sizes[c->c_time & callwheelmask] >
callwheel_maxlength.ev_count)
callwheel_maxlength.ev_count =
callwheel_sizes[c->c_time & callwheelmask];
#endif
CALLWHEEL_UNLOCK(s);
}
/*
* callout_stop_locked:
*
* Disestablish a timeout. Callwheel is locked.
*/
static void
callout_stop_locked(struct callout *c)
{
struct callout_queue *bucket;
/*
* Don't attempt to delete a callout that's not on the queue.
*/
if ((c->c_flags & CALLOUT_PENDING) == 0) {
c->c_flags &= ~CALLOUT_ACTIVE;
return;
}
c->c_flags &= ~(CALLOUT_ACTIVE | CALLOUT_PENDING);
if (nextsoftcheck == c)
nextsoftcheck = TAILQ_NEXT(c, c_link);
bucket = &callwheel[c->c_time & callwheelmask];
TAILQ_REMOVE(&bucket->cq_q, c, c_link);
if (TAILQ_EMPTY(&bucket->cq_q))
bucket->cq_hint = UQUAD_MAX;
#ifdef CALLWHEEL_STATS
callwheel_count.ev_count--;
callwheel_disestablished.ev_count++;
callwheel_sizes[c->c_time & callwheelmask]--;
#endif
c->c_func = NULL;
}
/*
* callout_stop:
*
* Disestablish a timeout. Callwheel is unlocked. This is
* the standard entry point.
*/
void
callout_stop(struct callout *c)
{
int s;
CALLWHEEL_LOCK(s);
callout_stop_locked(c);
CALLWHEEL_UNLOCK(s);
}
#ifdef CALLWHEEL_STATS
/*
* callout_showstats:
*
* Display callout statistics. Call it from DDB.
*/
void
callout_showstats(void)
{
u_int64_t curticks;
int s;
s = splclock();
curticks = softclock_ticks;
splx(s);
printf("Callwheel statistics:\n");
printf("\tCallouts currently queued: %llu\n",
(long long) callwheel_count.ev_count);
printf("\tCallouts established: %llu\n",
(long long) callwheel_established.ev_count);
printf("\tCallouts disestablished: %llu\n",
(long long) callwheel_disestablished.ev_count);
if (callwheel_changed.ev_count != 0)
printf("\t\tOf those, %llu were changes\n",
(long long) callwheel_changed.ev_count);
printf("\tCallouts that fired: %llu\n",
(long long) callwheel_fired.ev_count);
printf("\tNumber of buckets: %d\n", callwheelsize);
printf("\tNumber of hash collisions: %llu\n",
(long long) callwheel_collisions.ev_count);
printf("\tMaximum hash chain length: %llu\n",
(long long) callwheel_maxlength.ev_count);
printf("\tSoftclocks: %llu, Softchecks: %llu\n",
(long long) callwheel_softclocks.ev_count,
(long long) callwheel_softchecks.ev_count);
printf("\t\tEmpty buckets seen: %llu\n",
(long long) callwheel_softempty.ev_count);
printf("\t\tTimes hint saved scan: %llu\n",
(long long) callwheel_hintworked.ev_count);
}
#endif
/*
* Compute number of hz until specified time. Used to compute second
* argument to callout_reset() from an absolute time.

444
sys/kern/kern_timeout.c Normal file
View File

@ -0,0 +1,444 @@
/* $NetBSD: kern_timeout.c,v 1.1 2003/02/04 01:21:05 thorpej Exp $ */
/*-
* Copyright (c) 2003 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Jason R. Thorpe.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 2001 Thomas Nordin <nordin@openbsd.org>
* Copyright (c) 2000-2001 Artur Grabowski <art@openbsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Adapted from OpenBSD: kern_timeout.c,v 1.15 2002/12/08 04:21:07 art Exp,
* modified to match NetBSD's pre-existing callout API.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/callout.h>
#ifdef DDB
#include <machine/db_machdep.h>
#include <ddb/db_interface.h>
#include <ddb/db_access.h>
#include <ddb/db_sym.h>
#include <ddb/db_output.h>
#endif
/*
* Timeouts are kept in a hierarchical timing wheel. The c_time is the value
* of the global variable "hardclock_ticks" when the timeout should be called.
* There are four levels with 256 buckets each. See 'Scheme 7' in
* "Hashed and Hierarchical Timing Wheels: Efficient Data Structures for
* Implementing a Timer Facility" by George Varghese and Tony Lauck.
*/
#define BUCKETS 1024
#define WHEELSIZE 256
#define WHEELMASK 255
#define WHEELBITS 8
static struct callout_circq timeout_wheel[BUCKETS]; /* Queues of timeouts */
static struct callout_circq timeout_todo; /* Worklist */
#define MASKWHEEL(wheel, time) (((time) >> ((wheel)*WHEELBITS)) & WHEELMASK)
#define BUCKET(rel, abs) \
(((rel) <= (1 << (2*WHEELBITS))) \
? ((rel) <= (1 << WHEELBITS)) \
? timeout_wheel[MASKWHEEL(0, (abs))] \
: timeout_wheel[MASKWHEEL(1, (abs)) + WHEELSIZE] \
: ((rel) <= (1 << (3*WHEELBITS))) \
? timeout_wheel[MASKWHEEL(2, (abs)) + 2*WHEELSIZE] \
: timeout_wheel[MASKWHEEL(3, (abs)) + 3*WHEELSIZE])
#define MOVEBUCKET(wheel, time) \
CIRCQ_APPEND(&timeout_todo, \
&timeout_wheel[MASKWHEEL((wheel), (time)) + (wheel)*WHEELSIZE])
/*
* All wheels are locked with the same lock (which must also block out all
* interrupts).
*/
static struct simplelock callout_slock;
#define CALLOUT_LOCK(s) \
do { \
s = splsched(); \
simple_lock(&callout_slock); \
} while (/*CONSTCOND*/0)
#define CALLOUT_UNLOCK(s) \
do { \
simple_unlock(&callout_slock); \
splx((s)); \
} while (/*CONSTCOND*/0)
/*
* Circular queue definitions.
*/
#define CIRCQ_INIT(elem) \
do { \
(elem)->cq_next = (elem); \
(elem)->cq_prev = (elem); \
} while (/*CONSTCOND*/0)
#define CIRCQ_INSERT(elem, list) \
do { \
(elem)->cq_prev = (list)->cq_prev; \
(elem)->cq_next = (list); \
(list)->cq_prev->cq_next = (elem); \
(list)->cq_prev = (elem); \
} while (/*CONSTCOND*/0)
#define CIRCQ_APPEND(fst, snd) \
do { \
if (!CIRCQ_EMPTY(snd)) { \
(fst)->cq_prev->cq_next = (snd)->cq_next; \
(snd)->cq_next->cq_prev = (fst)->cq_prev; \
(snd)->cq_prev->cq_next = (fst); \
(fst)->cq_prev = (snd)->cq_prev; \
CIRCQ_INIT(snd); \
} \
} while (/*CONSTCOND*/0)
#define CIRCQ_REMOVE(elem) \
do { \
(elem)->cq_next->cq_prev = (elem)->cq_prev; \
(elem)->cq_prev->cq_next = (elem)->cq_next; \
} while (/*CONSTCOND*/0)
#define CIRCQ_FIRST(elem) ((elem)->cq_next)
#define CIRCQ_EMPTY(elem) (CIRCQ_FIRST(elem) == (elem))
/*
* Some of the "math" in here is a bit tricky.
*
* We have to beware of wrapping ints.
* We use the fact that any element added to the queue must be added with a
* positive time. That means that any element `to' on the queue cannot be
* scheduled to timeout further in time than INT_MAX, but c->c_time can
* be positive or negative so comparing it with anything is dangerous.
* The only way we can use the c->c_time value in any predictable way
* is when we caluculate how far in the future `to' will timeout -
* "c->c_time - hardclock_ticks". The result will always be positive for
* future timeouts and 0 or negative for due timeouts.
*/
/*
* callout_startup:
*
* Initialize the callout facility, called at system startup time.
*/
void
callout_startup(void)
{
int b;
CIRCQ_INIT(&timeout_todo);
for (b = 0; b < BUCKETS; b++)
CIRCQ_INIT(&timeout_wheel[b]);
simple_lock_init(&callout_slock);
}
/*
* callout_init:
*
* Initialize a callout structure.
*/
void
callout_init(struct callout *c)
{
memset(c, 0, sizeof(*c));
}
/*
* callout_setfunc:
*
* Initialize a callout structure and set the function and
* argument.
*/
void
callout_setfunc(struct callout *c, void (*func)(void *), void *arg)
{
memset(c, 0, sizeof(*c));
c->c_func = func;
c->c_arg = arg;
}
/*
* callout_reset:
*
* Reset a callout structure with a new function and argument, and
* schedule it to run.
*/
void
callout_reset(struct callout *c, int to_ticks, void (*func)(void *), void *arg)
{
int s, old_time;
KASSERT(to_ticks >= 0);
CALLOUT_LOCK(s);
/* Initialize the time here, it won't change. */
old_time = c->c_time;
c->c_time = to_ticks + hardclock_ticks;
c->c_flags &= ~CALLOUT_FIRED;
c->c_func = func;
c->c_arg = arg;
/*
* If this timeout is already scheduled and now is moved
* earlier, reschedule it now. Otherwise leave it in place
* and let it be rescheduled later.
*/
if (callout_pending(c)) {
if (c->c_time < old_time) {
CIRCQ_REMOVE(&c->c_list);
CIRCQ_INSERT(&c->c_list, &timeout_todo);
}
} else {
c->c_flags |= CALLOUT_PENDING;
CIRCQ_INSERT(&c->c_list, &timeout_todo);
}
CALLOUT_UNLOCK(s);
}
/*
* callout_schedule:
*
* Schedule a callout to run. The function and argument must
* already be set in the callout structure.
*/
void
callout_schedule(struct callout *c, int to_ticks)
{
int s, old_time;
KASSERT(to_ticks >= 0);
CALLOUT_LOCK(s);
/* Initialize the time here, it won't change. */
old_time = c->c_time;
c->c_time = to_ticks + hardclock_ticks;
c->c_flags &= ~CALLOUT_FIRED;
/*
* If this timeout is already scheduled and now is moved
* earlier, reschedule it now. Otherwise leave it in place
* and let it be rescheduled later.
*/
if (callout_pending(c)) {
if (c->c_time < old_time) {
CIRCQ_REMOVE(&c->c_list);
CIRCQ_INSERT(&c->c_list, &timeout_todo);
}
} else {
c->c_flags |= CALLOUT_PENDING;
CIRCQ_INSERT(&c->c_list, &timeout_todo);
}
CALLOUT_UNLOCK(s);
}
/*
* callout_stop:
*
* Cancel a pending callout.
*/
void
callout_stop(struct callout *c)
{
int s;
CALLOUT_LOCK(s);
if (callout_pending(c))
CIRCQ_REMOVE(&c->c_list);
c->c_flags &= ~(CALLOUT_PENDING|CALLOUT_FIRED);
CALLOUT_UNLOCK(s);
}
/*
* This is called from hardclock() once every tick.
* We return !0 if we need to schedule a softclock.
*/
int
callout_hardclock(void)
{
int s;
CALLOUT_LOCK(s);
MOVEBUCKET(0, hardclock_ticks);
if (MASKWHEEL(0, hardclock_ticks) == 0) {
MOVEBUCKET(1, hardclock_ticks);
if (MASKWHEEL(1, hardclock_ticks) == 0) {
MOVEBUCKET(2, hardclock_ticks);
if (MASKWHEEL(2, hardclock_ticks) == 0)
MOVEBUCKET(3, hardclock_ticks);
}
}
CALLOUT_UNLOCK(s);
return (!CIRCQ_EMPTY(&timeout_todo));
}
/* ARGSUSED */
void
softclock(void *v)
{
struct callout *c;
void (*func)(void *);
void *arg;
int s;
CALLOUT_LOCK(s);
while (!CIRCQ_EMPTY(&timeout_todo)) {
c = (struct callout *)CIRCQ_FIRST(&timeout_todo); /* XXX */
CIRCQ_REMOVE(&c->c_list);
/* If due run it, otherwise insert it into the right bucket. */
if (c->c_time - hardclock_ticks > 0) {
CIRCQ_INSERT(&c->c_list,
&BUCKET((c->c_time - hardclock_ticks), c->c_time));
} else {
#ifdef DEBUG /* XXX evcnt */
if (c->c_time - hardclock_ticks < 0)
printf("timeout delayed %d\n", c->c_time -
hardclock_ticks);
#endif
c->c_flags = (c->c_flags & ~CALLOUT_PENDING) |
CALLOUT_FIRED;
func = c->c_func;
arg = c->c_arg;
CALLOUT_UNLOCK(s);
(*func)(arg);
CALLOUT_LOCK(s);
}
}
CALLOUT_UNLOCK(s);
}
#ifdef DDB
static void
db_show_callout_bucket(struct callout_circq *bucket)
{
struct callout *c;
struct callout_circq *p;
db_expr_t offset;
char *name;
for (p = CIRCQ_FIRST(bucket); p != bucket; p = CIRCQ_FIRST(p)) {
c = (struct callout *)p; /* XXX */
db_find_sym_and_offset((db_addr_t)c->c_func, &name, &offset);
name = name ? name : "?";
#ifdef _LP64
#define POINTER_WIDTH "%16lx"
#else
#define POINTER_WIDTH "%8lx"
#endif
db_printf("%9d %2d/%-4d " POINTER_WIDTH " %s\n",
c->c_time - hardclock_ticks,
(bucket - timeout_wheel) / WHEELSIZE,
bucket - timeout_wheel, (u_long) c->c_arg, name);
}
}
void
db_show_callout(db_expr_t addr, int haddr, db_expr_t count, char *modif)
{
int b;
db_printf("hardclock_ticks now: %d\n", hardclock_ticks);
#ifdef _LP64
db_printf(" ticks wheel arg func\n");
#else
db_printf(" ticks wheel arg func\n");
#endif
/*
* Don't lock the callwheel; all the other CPUs are paused
* anyhow, and we might be called in a circumstance where
* some other CPU was paused while holding the lock.
*/
db_show_callout_bucket(&timeout_todo);
for (b = 0; b < BUCKETS; b++)
db_show_callout_bucket(&timeout_wheel[b]);
}
#endif /* DDB */

View File

@ -1,4 +1,4 @@
/* $NetBSD: tp_pcb.h,v 1.13 2002/05/12 21:30:37 matt Exp $ */
/* $NetBSD: tp_pcb.h,v 1.14 2003/02/04 01:21:05 thorpej Exp $ */
/*-
* Copyright (c) 1991, 1993
@ -261,7 +261,7 @@ struct tp_pcb {
int tp_rtv; /* max round-trip time variance */
int tp_rtt; /* smoothed round-trip time */
SeqNum tp_rttseq; /* packet being timed */
u_int64_t tp_rttemit; /* when emitted, in ticks */
int tp_rttemit; /* when emitted, in ticks */
int tp_idle;/* last activity, in ticks */
short tp_rxtcur; /* current retransmit value */
short tp_rxtshift; /* log(2) of rexmt exp. backoff */

View File

@ -1,4 +1,4 @@
/* $NetBSD: tp_subr.c,v 1.14 2001/11/13 01:10:51 lukem Exp $ */
/* $NetBSD: tp_subr.c,v 1.15 2003/02/04 01:21:05 thorpej Exp $ */
/*-
* Copyright (c) 1991, 1993
@ -71,7 +71,7 @@ SOFTWARE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: tp_subr.c,v 1.14 2001/11/13 01:10:51 lukem Exp $");
__KERNEL_RCSID(0, "$NetBSD: tp_subr.c,v 1.15 2003/02/04 01:21:05 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -172,9 +172,7 @@ tp_rtt_rtv(tpcb)
int old = tpcb->tp_rtt;
int s, elapsed, delta = 0;
s = splclock();
elapsed = (int)(hardclock_ticks - tpcb->tp_rttemit);
splx(s);
elapsed = hardclock_ticks - tpcb->tp_rttemit;
if (tpcb->tp_rtt != 0) {
/*
@ -499,12 +497,8 @@ tp_send(tpcb)
SeqNum highseq, checkseq;
int s, idle, idleticks, off, cong_win;
#ifdef TP_PERF_MEAS
u_int64_t send_start_time;
int send_start_time = hardclock_ticks;
SeqNum oldnxt = tpcb->tp_sndnxt;
s = splclock();
send_start_time = hardclock_ticks;
splx(s);
#endif /* TP_PERF_MEAS */
idle = (tpcb->tp_snduna == tpcb->tp_sndnew);
@ -607,9 +601,7 @@ tp_send(tpcb)
* not currently timing anything.
*/
if (tpcb->tp_rttemit == 0) {
s = splclock();
tpcb->tp_rttemit = hardclock_ticks;
splx(s);
tpcb->tp_rttseq = tpcb->tp_sndnxt;
}
tpcb->tp_sndnxt = tpcb->tp_sndnew;
@ -637,9 +629,7 @@ tp_send(tpcb)
int s, elapsed, *t;
struct timeval now;
s = splclock();
elapsed = (int)(hardclock_ticks - send_start_time);
splx(s);
elapsed = hardclock_ticks - send_start_time;
npkts = SEQ_SUB(tpcb, tpcb->tp_sndnxt, oldnxt);

View File

@ -1,7 +1,7 @@
/* $NetBSD: callout.h,v 1.16 2001/09/11 04:32:19 thorpej Exp $ */
/* $NetBSD: callout.h,v 1.17 2003/02/04 01:21:06 thorpej Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* Copyright (c) 2000, 2003 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@ -37,91 +37,69 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
/*
* Copyright (c) 2000-2001 Artur Grabowski <art@openbsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)callout.h 8.2 (Berkeley) 1/21/94
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _SYS_CALLOUT_H_
#define _SYS_CALLOUT_H_
#include <sys/queue.h>
struct callout_queue {
uint64_t cq_hint; /* earliest callout in bkt */
TAILQ_HEAD(, callout) cq_q; /* callouts in this bucket */
struct callout_circq {
struct callout_circq *cq_next; /* next element */
struct callout_circq *cq_prev; /* previous element */
};
struct callout {
TAILQ_ENTRY(callout) c_link;
u_int64_t c_time; /* when callout fires */
void *c_arg; /* function argument */
void (*c_func)(void *); /* functiuon to call */
int c_flags; /* state of this entry */
struct callout_circq c_list; /* linkage on queue */
void (*c_func)(void *); /* function to call */
void *c_arg; /* function argument */
int c_time; /* when callout fires */
int c_flags; /* state of this entry */
};
#define CALLOUT_ACTIVE 0x0001 /* callout is active */
#define CALLOUT_PENDING 0x0002 /* callout time has not yet arrived */
#define CALLOUT_PENDING 0x0002 /* callout is on the queue */
#define CALLOUT_FIRED 0x0004 /* callout has fired */
#define CALLOUT_INITIALIZER { { NULL, NULL }, 0, NULL, NULL, 0 }
#define CALLOUT_INITIALIZER_SETFUNC(func, arg) \
{ { NULL, NULL }, func, arg, 0, 0 }
#define CALLOUT_INITIALIZER CALLOUT_INITIALIZER_SETFUNC(NULL, NULL)
#ifdef _KERNEL
extern struct callout_queue *callwheel;
extern int callwheelsize, callwheelbits, callwheelmask;
extern int ncallout;
#ifdef CALLWHEEL_STATS
extern int *callwheel_sizes; /* for allocsys() */
#endif
void callout_setsize(void);
void callout_startup(void);
void callout_init(struct callout *);
void callout_setfunc(struct callout *, void (*)(void *), void *);
void callout_reset(struct callout *, int, void (*)(void *), void *);
void callout_schedule(struct callout *, int);
void callout_stop(struct callout *);
#ifdef CALLWHEEL_STATS
void callout_showstats(void);
#endif
int callout_hardclock(void);
#define callout_active(c) ((c)->c_flags & CALLOUT_ACTIVE)
#define callout_pending(c) ((c)->c_flags & CALLOUT_PENDING)
#define callout_expired(c) (callout_pending((c)) == 0)
#define callout_deactivate(c) ((c)->c_flags &= ~CALLOUT_ACTIVE)
#define callout_expired(c) ((c)->c_flags & CALLOUT_FIRED)
#endif /* _KERNEL */
#endif /* !_SYS_CALLOUT_H_ */

View File

@ -1,4 +1,4 @@
/* $NetBSD: kernel.h,v 1.18 2003/01/21 13:56:53 kleink Exp $ */
/* $NetBSD: kernel.h,v 1.19 2003/02/04 01:21:06 thorpej Exp $ */
/*-
* Copyright (c) 1990, 1993
@ -59,6 +59,7 @@ extern int rtc_offset; /* offset of rtc from UTC in minutes */
extern int cold; /* still working on startup */
extern int tick; /* usec per tick (1000000 / hz) */
extern int hardclock_ticks; /* # of hardclock ticks */
extern int tickfix; /* periodic tick adj. tick not integral */
extern int tickfixinterval; /* interval at which to apply adjustment */
extern int tickadj; /* "standard" clock skew, us./tick */
@ -72,17 +73,4 @@ extern int profsrc; /* profiling source */
#define PROFSRC_CLOCK 0
#endif
/*
* These globals indicate the number of times hardlock() has ticked,
* and the successive attempt of softclock() to catch up with it. These
* are large unsigned numbers so that arithmetic can be performed on them
* with no reasonable worry about an overflow occurring (we get over 500
* million years with this).
*
* IMPORTANT NOTE: you *must* be at splclock() in order to read both
* hardclock_ticks and softclock_ticks. This is because the 64-bit
* quantities may not be readable in an atomic fashion on all CPU types.
*/
extern u_int64_t hardclock_ticks, softclock_ticks;
#endif /* _SYS_KERNEL_H_ */

View File

@ -1,4 +1,4 @@
/* $NetBSD: systm.h,v 1.158 2003/02/01 06:23:52 thorpej Exp $ */
/* $NetBSD: systm.h,v 1.159 2003/02/04 01:21:06 thorpej Exp $ */
/*-
* Copyright (c) 1982, 1988, 1991, 1993
@ -262,9 +262,7 @@ long fuiword __P((const void *));
int hzto __P((struct timeval *));
void hardclock __P((struct clockframe *));
#ifndef __HAVE_GENERIC_SOFT_INTERRUPTS
void softclock __P((void *));
#endif
void statclock __P((struct clockframe *));
#ifdef NTP
void hardupdate __P((long offset));