[BACK]Return to kern_timeout.c CVS log [TXT][DIR] Up to [local] / sys / kern

Annotation of sys/kern/kern_timeout.c, Revision 1.1.1.1

1.1       nbrk        1: /*     $OpenBSD: kern_timeout.c,v 1.25 2006/07/19 20:25:08 miod Exp $  */
                      2: /*
                      3:  * Copyright (c) 2001 Thomas Nordin <nordin@openbsd.org>
                      4:  * Copyright (c) 2000-2001 Artur Grabowski <art@openbsd.org>
                      5:  * All rights reserved.
                      6:  *
                      7:  * Redistribution and use in source and binary forms, with or without
                      8:  * modification, are permitted provided that the following conditions
                      9:  * are met:
                     10:  *
                     11:  * 1. Redistributions of source code must retain the above copyright
                     12:  *    notice, this list of conditions and the following disclaimer.
                     13:  * 2. The name of the author may not be used to endorse or promote products
                     14:  *    derived from this software without specific prior written permission.
                     15:  *
                     16:  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
                     17:  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
                     18:  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
                     19:  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
                     20:  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
                     21:  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
                     22:  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
                     23:  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
                     24:  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
                     25:  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     26:  */
                     27:
                     28: #include <sys/param.h>
                     29: #include <sys/systm.h>
                     30: #include <sys/lock.h>
                     31: #include <sys/timeout.h>
                     32: #include <sys/mutex.h>
                     33: #include <sys/queue.h>
                     34:
                     35: #ifdef DDB
                     36: #include <machine/db_machdep.h>
                     37: #include <ddb/db_interface.h>
                     38: #include <ddb/db_access.h>
                     39: #include <ddb/db_sym.h>
                     40: #include <ddb/db_output.h>
                     41: #endif
                     42:
                     43: /*
                     44:  * Timeouts are kept in a hierarchical timing wheel. The to_time is the value
                     45:  * of the global variable "ticks" when the timeout should be called. There are
                     46:  * four levels with 256 buckets each. See 'Scheme 7' in
                     47:  * "Hashed and Hierarchical Timing Wheels: Efficient Data Structures for
                     48:  * Implementing a Timer Facility" by George Varghese and Tony Lauck.
                     49:  */
                     50: #define BUCKETS 1024
                     51: #define WHEELSIZE 256
                     52: #define WHEELMASK 255
                     53: #define WHEELBITS 8
                     54:
                     55: struct circq timeout_wheel[BUCKETS];   /* Queues of timeouts */
                     56: struct circq timeout_todo;             /* Worklist */
                     57:
                     58: #define MASKWHEEL(wheel, time) (((time) >> ((wheel)*WHEELBITS)) & WHEELMASK)
                     59:
                     60: #define BUCKET(rel, abs)                                               \
                     61:     (timeout_wheel[                                                    \
                     62:        ((rel) <= (1 << (2*WHEELBITS)))                         \
                     63:            ? ((rel) <= (1 << WHEELBITS))                               \
                     64:                ? MASKWHEEL(0, (abs))                                   \
                     65:                : MASKWHEEL(1, (abs)) + WHEELSIZE                       \
                     66:            : ((rel) <= (1 << (3*WHEELBITS)))                           \
                     67:                ? MASKWHEEL(2, (abs)) + 2*WHEELSIZE                     \
                     68:                : MASKWHEEL(3, (abs)) + 3*WHEELSIZE])
                     69:
                     70: #define MOVEBUCKET(wheel, time)                                                \
                     71:     CIRCQ_APPEND(&timeout_todo,                                                \
                     72:         &timeout_wheel[MASKWHEEL((wheel), (time)) + (wheel)*WHEELSIZE])
                     73:
                     74: /*
                     75:  * All wheels are locked with the same mutex.
                     76:  *
                     77:  * We need locking since the timeouts are manipulated from hardclock that's
                     78:  * not behind the big lock.
                     79:  */
                     80: struct mutex timeout_mutex = MUTEX_INITIALIZER(IPL_HIGH);
                     81:
                     82: /*
                     83:  * Circular queue definitions.
                     84:  */
                     85:
                     86: #define CIRCQ_INIT(elem) do {                   \
                     87:         (elem)->next = (elem);                  \
                     88:         (elem)->prev = (elem);                  \
                     89: } while (0)
                     90:
                     91: #define CIRCQ_INSERT(elem, list) do {           \
                     92:         (elem)->prev = (list)->prev;            \
                     93:         (elem)->next = (list);                  \
                     94:         (list)->prev->next = (elem);            \
                     95:         (list)->prev = (elem);                  \
                     96: } while (0)
                     97:
                     98: #define CIRCQ_APPEND(fst, snd) do {             \
                     99:         if (!CIRCQ_EMPTY(snd)) {                \
                    100:                 (fst)->prev->next = (snd)->next;\
                    101:                 (snd)->next->prev = (fst)->prev;\
                    102:                 (snd)->prev->next = (fst);      \
                    103:                 (fst)->prev = (snd)->prev;      \
                    104:                 CIRCQ_INIT(snd);                \
                    105:         }                                       \
                    106: } while (0)
                    107:
                    108: #define CIRCQ_REMOVE(elem) do {                 \
                    109:         (elem)->next->prev = (elem)->prev;      \
                    110:         (elem)->prev->next = (elem)->next;      \
                    111:        _Q_INVALIDATE((elem)->prev);            \
                    112:        _Q_INVALIDATE((elem)->next);            \
                    113: } while (0)
                    114:
                    115: #define CIRCQ_FIRST(elem) ((elem)->next)
                    116:
                    117: #define CIRCQ_EMPTY(elem) (CIRCQ_FIRST(elem) == (elem))
                    118:
                    119: /*
                    120:  * Some of the "math" in here is a bit tricky.
                    121:  *
                    122:  * We have to beware of wrapping ints.
                    123:  * We use the fact that any element added to the queue must be added with a
                    124:  * positive time. That means that any element `to' on the queue cannot be
                    125:  * scheduled to timeout further in time than INT_MAX, but to->to_time can
                    126:  * be positive or negative so comparing it with anything is dangerous.
                    127:  * The only way we can use the to->to_time value in any predictable way
                    128:  * is when we calculate how far in the future `to' will timeout -
                    129:  * "to->to_time - ticks". The result will always be positive for future
                    130:  * timeouts and 0 or negative for due timeouts.
                    131:  */
                    132: extern int ticks;              /* XXX - move to sys/X.h */
                    133:
                    134: void
                    135: timeout_startup(void)
                    136: {
                    137:        int b;
                    138:
                    139:        CIRCQ_INIT(&timeout_todo);
                    140:        for (b = 0; b < BUCKETS; b++)
                    141:                CIRCQ_INIT(&timeout_wheel[b]);
                    142: }
                    143:
                    144: void
                    145: timeout_set(struct timeout *new, void (*fn)(void *), void *arg)
                    146: {
                    147:        new->to_func = fn;
                    148:        new->to_arg = arg;
                    149:        new->to_flags = TIMEOUT_INITIALIZED;
                    150: }
                    151:
                    152:
                    153: void
                    154: timeout_add(struct timeout *new, int to_ticks)
                    155: {
                    156:        int old_time;
                    157:
                    158: #ifdef DIAGNOSTIC
                    159:        if (!(new->to_flags & TIMEOUT_INITIALIZED))
                    160:                panic("timeout_add: not initialized");
                    161:        if (to_ticks < 0)
                    162:                panic("timeout_add: to_ticks (%d) < 0", to_ticks);
                    163: #endif
                    164:
                    165:        mtx_enter(&timeout_mutex);
                    166:        /* Initialize the time here, it won't change. */
                    167:        old_time = new->to_time;
                    168:        new->to_time = to_ticks + ticks;
                    169:        new->to_flags &= ~TIMEOUT_TRIGGERED;
                    170:
                    171:        /*
                    172:         * If this timeout already is scheduled and now is moved
                    173:         * earlier, reschedule it now. Otherwise leave it in place
                    174:         * and let it be rescheduled later.
                    175:         */
                    176:        if (new->to_flags & TIMEOUT_ONQUEUE) {
                    177:                if (new->to_time - ticks < old_time - ticks) {
                    178:                        CIRCQ_REMOVE(&new->to_list);
                    179:                        CIRCQ_INSERT(&new->to_list, &timeout_todo);
                    180:                }
                    181:        } else {
                    182:                new->to_flags |= TIMEOUT_ONQUEUE;
                    183:                CIRCQ_INSERT(&new->to_list, &timeout_todo);
                    184:        }
                    185:        mtx_leave(&timeout_mutex);
                    186: }
                    187:
                    188: void
                    189: timeout_del(struct timeout *to)
                    190: {
                    191:        mtx_enter(&timeout_mutex);
                    192:        if (to->to_flags & TIMEOUT_ONQUEUE) {
                    193:                CIRCQ_REMOVE(&to->to_list);
                    194:                to->to_flags &= ~TIMEOUT_ONQUEUE;
                    195:        }
                    196:        to->to_flags &= ~TIMEOUT_TRIGGERED;
                    197:        mtx_leave(&timeout_mutex);
                    198: }
                    199:
                    200: /*
                    201:  * This is called from hardclock() once every tick.
                    202:  * We return !0 if we need to schedule a softclock.
                    203:  */
                    204: int
                    205: timeout_hardclock_update(void)
                    206: {
                    207:        int ret;
                    208:
                    209:        mtx_enter(&timeout_mutex);
                    210:
                    211:        ticks++;
                    212:
                    213:        MOVEBUCKET(0, ticks);
                    214:        if (MASKWHEEL(0, ticks) == 0) {
                    215:                MOVEBUCKET(1, ticks);
                    216:                if (MASKWHEEL(1, ticks) == 0) {
                    217:                        MOVEBUCKET(2, ticks);
                    218:                        if (MASKWHEEL(2, ticks) == 0)
                    219:                                MOVEBUCKET(3, ticks);
                    220:                }
                    221:        }
                    222:        ret = !CIRCQ_EMPTY(&timeout_todo);
                    223:        mtx_leave(&timeout_mutex);
                    224:
                    225:        return (ret);
                    226: }
                    227:
                    228: void
                    229: softclock(void)
                    230: {
                    231:        struct timeout *to;
                    232:        void (*fn)(void *);
                    233:        void *arg;
                    234:
                    235:        mtx_enter(&timeout_mutex);
                    236:        while (!CIRCQ_EMPTY(&timeout_todo)) {
                    237:
                    238:                to = (struct timeout *)CIRCQ_FIRST(&timeout_todo); /* XXX */
                    239:                CIRCQ_REMOVE(&to->to_list);
                    240:
                    241:                /* If due run it, otherwise insert it into the right bucket. */
                    242:                if (to->to_time - ticks > 0) {
                    243:                        CIRCQ_INSERT(&to->to_list,
                    244:                            &BUCKET((to->to_time - ticks), to->to_time));
                    245:                } else {
                    246: #ifdef DEBUG
                    247:                        if (to->to_time - ticks < 0)
                    248:                                printf("timeout delayed %d\n", to->to_time -
                    249:                                    ticks);
                    250: #endif
                    251:                        to->to_flags &= ~TIMEOUT_ONQUEUE;
                    252:                        to->to_flags |= TIMEOUT_TRIGGERED;
                    253:
                    254:                        fn = to->to_func;
                    255:                        arg = to->to_arg;
                    256:
                    257:                        mtx_leave(&timeout_mutex);
                    258:                        fn(arg);
                    259:                        mtx_enter(&timeout_mutex);
                    260:                }
                    261:        }
                    262:        mtx_leave(&timeout_mutex);
                    263: }
                    264:
                    265: #ifdef DDB
                    266: void db_show_callout_bucket(struct circq *);
                    267:
                    268: void
                    269: db_show_callout_bucket(struct circq *bucket)
                    270: {
                    271:        struct timeout *to;
                    272:        struct circq *p;
                    273:        db_expr_t offset;
                    274:        char *name;
                    275:
                    276:        for (p = CIRCQ_FIRST(bucket); p != bucket; p = CIRCQ_FIRST(p)) {
                    277:                to = (struct timeout *)p; /* XXX */
                    278:                db_find_sym_and_offset((db_addr_t)to->to_func, &name, &offset);
                    279:                name = name ? name : "?";
                    280:                db_printf("%9d %2d/%-4d %8x  %s\n", to->to_time - ticks,
                    281:                    (bucket - timeout_wheel) / WHEELSIZE,
                    282:                    bucket - timeout_wheel, to->to_arg, name);
                    283:        }
                    284: }
                    285:
                    286: void
                    287: db_show_callout(db_expr_t addr, int haddr, db_expr_t count, char *modif)
                    288: {
                    289:        int b;
                    290:
                    291:        db_printf("ticks now: %d\n", ticks);
                    292:        db_printf("    ticks  wheel       arg  func\n");
                    293:
                    294:        mtx_enter(&timeout_mutex);
                    295:        db_show_callout_bucket(&timeout_todo);
                    296:        for (b = 0; b < BUCKETS; b++)
                    297:                db_show_callout_bucket(&timeout_wheel[b]);
                    298:        mtx_leave(&timeout_mutex);
                    299: }
                    300: #endif

CVSweb