linux/drivers/staging/lustre/lnet/selftest/timer.c
<<
>>
Prefs
   1/*
   2 * GPL HEADER START
   3 *
   4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 only,
   8 * as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful, but
  11 * WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13 * General Public License version 2 for more details (a copy is included
  14 * in the LICENSE file that accompanied this code).
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * version 2 along with this program; If not, see
  18 * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
  19 *
  20 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  21 * CA 95054 USA or visit www.sun.com if you need additional information or
  22 * have any questions.
  23 *
  24 * GPL HEADER END
  25 */
  26/*
  27 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  28 * Use is subject to license terms.
  29 *
  30 * Copyright (c) 2011, 2012, Intel Corporation.
  31 */
  32/*
  33 * This file is part of Lustre, http://www.lustre.org/
  34 * Lustre is a trademark of Sun Microsystems, Inc.
  35 *
  36 * lnet/selftest/timer.c
  37 *
  38 * Author: Isaac Huang <isaac@clusterfs.com>
  39 */
  40
  41#define DEBUG_SUBSYSTEM S_LNET
  42
  43#include "selftest.h"
  44
  45
  46/*
  47 * Timers are implemented as a sorted queue of expiry times. The queue
  48 * is slotted, with each slot holding timers which expire in a
  49 * 2**STTIMER_MINPOLL (8) second period. The timers in each slot are
  50 * sorted by increasing expiry time. The number of slots is 2**7 (128),
  51 * to cover a time period of 1024 seconds into the future before wrapping.
  52 */
  53#define STTIMER_MINPOLL 3   /* log2 min poll interval (8 s) */
  54#define STTIMER_SLOTTIME       (1 << STTIMER_MINPOLL)
  55#define STTIMER_SLOTTIMEMASK   (~(STTIMER_SLOTTIME - 1))
  56#define STTIMER_NSLOTS         (1 << 7)
  57#define STTIMER_SLOT(t)        (&stt_data.stt_hash[(((t) >> STTIMER_MINPOLL) & \
  58                                                    (STTIMER_NSLOTS - 1))])
  59
  60struct st_timer_data {
  61        spinlock_t       stt_lock;
  62        /* start time of the slot processed previously */
  63        cfs_time_t       stt_prev_slot;
  64        struct list_head       stt_hash[STTIMER_NSLOTS];
  65        int           stt_shuttingdown;
  66        wait_queue_head_t      stt_waitq;
  67        int           stt_nthreads;
  68} stt_data;
  69
  70void
  71stt_add_timer(stt_timer_t *timer)
  72{
  73        struct list_head *pos;
  74
  75        spin_lock(&stt_data.stt_lock);
  76
  77        LASSERT (stt_data.stt_nthreads > 0);
  78        LASSERT (!stt_data.stt_shuttingdown);
  79        LASSERT (timer->stt_func != NULL);
  80        LASSERT (list_empty(&timer->stt_list));
  81        LASSERT (cfs_time_after(timer->stt_expires, cfs_time_current_sec()));
  82
  83        /* a simple insertion sort */
  84        list_for_each_prev (pos, STTIMER_SLOT(timer->stt_expires)) {
  85                stt_timer_t *old = list_entry(pos, stt_timer_t, stt_list);
  86
  87                if (cfs_time_aftereq(timer->stt_expires, old->stt_expires))
  88                        break;
  89        }
  90        list_add(&timer->stt_list, pos);
  91
  92        spin_unlock(&stt_data.stt_lock);
  93}
  94
  95/*
  96 * The function returns whether it has deactivated a pending timer or not.
  97 * (ie. del_timer() of an inactive timer returns 0, del_timer() of an
  98 * active timer returns 1.)
  99 *
 100 * CAVEAT EMPTOR:
 101 * When 0 is returned, it is possible that timer->stt_func _is_ running on
 102 * another CPU.
 103 */
 104int
 105stt_del_timer (stt_timer_t *timer)
 106{
 107        int ret = 0;
 108
 109        spin_lock(&stt_data.stt_lock);
 110
 111        LASSERT (stt_data.stt_nthreads > 0);
 112        LASSERT (!stt_data.stt_shuttingdown);
 113
 114        if (!list_empty(&timer->stt_list)) {
 115                ret = 1;
 116                list_del_init(&timer->stt_list);
 117        }
 118
 119        spin_unlock(&stt_data.stt_lock);
 120        return ret;
 121}
 122
 123/* called with stt_data.stt_lock held */
 124int
 125stt_expire_list (struct list_head *slot, cfs_time_t now)
 126{
 127        int       expired = 0;
 128        stt_timer_t *timer;
 129
 130        while (!list_empty(slot)) {
 131                timer = list_entry(slot->next, stt_timer_t, stt_list);
 132
 133                if (cfs_time_after(timer->stt_expires, now))
 134                        break;
 135
 136                list_del_init(&timer->stt_list);
 137                spin_unlock(&stt_data.stt_lock);
 138
 139                expired++;
 140                (*timer->stt_func) (timer->stt_data);
 141
 142                spin_lock(&stt_data.stt_lock);
 143        }
 144
 145        return expired;
 146}
 147
 148int
 149stt_check_timers (cfs_time_t *last)
 150{
 151        int     expired = 0;
 152        cfs_time_t now;
 153        cfs_time_t this_slot;
 154
 155        now = cfs_time_current_sec();
 156        this_slot = now & STTIMER_SLOTTIMEMASK;
 157
 158        spin_lock(&stt_data.stt_lock);
 159
 160        while (cfs_time_aftereq(this_slot, *last)) {
 161                expired += stt_expire_list(STTIMER_SLOT(this_slot), now);
 162                this_slot = cfs_time_sub(this_slot, STTIMER_SLOTTIME);
 163        }
 164
 165        *last = now & STTIMER_SLOTTIMEMASK;
 166        spin_unlock(&stt_data.stt_lock);
 167        return expired;
 168}
 169
 170
 171int
 172stt_timer_main (void *arg)
 173{
 174        int rc = 0;
 175        UNUSED(arg);
 176
 177        SET_BUT_UNUSED(rc);
 178
 179        cfs_block_allsigs();
 180
 181        while (!stt_data.stt_shuttingdown) {
 182                stt_check_timers(&stt_data.stt_prev_slot);
 183
 184                rc = wait_event_timeout(stt_data.stt_waitq,
 185                                        stt_data.stt_shuttingdown,
 186                                        cfs_time_seconds(STTIMER_SLOTTIME));
 187        }
 188
 189        spin_lock(&stt_data.stt_lock);
 190        stt_data.stt_nthreads--;
 191        spin_unlock(&stt_data.stt_lock);
 192        return 0;
 193}
 194
 195int
 196stt_start_timer_thread (void)
 197{
 198        struct task_struct *task;
 199
 200        LASSERT(!stt_data.stt_shuttingdown);
 201
 202        task = kthread_run(stt_timer_main, NULL, "st_timer");
 203        if (IS_ERR(task))
 204                return PTR_ERR(task);
 205
 206        spin_lock(&stt_data.stt_lock);
 207        stt_data.stt_nthreads++;
 208        spin_unlock(&stt_data.stt_lock);
 209        return 0;
 210}
 211
 212
 213int
 214stt_startup (void)
 215{
 216        int rc = 0;
 217        int i;
 218
 219        stt_data.stt_shuttingdown = 0;
 220        stt_data.stt_prev_slot = cfs_time_current_sec() & STTIMER_SLOTTIMEMASK;
 221
 222        spin_lock_init(&stt_data.stt_lock);
 223        for (i = 0; i < STTIMER_NSLOTS; i++)
 224                INIT_LIST_HEAD(&stt_data.stt_hash[i]);
 225
 226        stt_data.stt_nthreads = 0;
 227        init_waitqueue_head(&stt_data.stt_waitq);
 228        rc = stt_start_timer_thread();
 229        if (rc != 0)
 230                CERROR ("Can't spawn timer thread: %d\n", rc);
 231
 232        return rc;
 233}
 234
 235void
 236stt_shutdown (void)
 237{
 238        int i;
 239
 240        spin_lock(&stt_data.stt_lock);
 241
 242        for (i = 0; i < STTIMER_NSLOTS; i++)
 243                LASSERT (list_empty(&stt_data.stt_hash[i]));
 244
 245        stt_data.stt_shuttingdown = 1;
 246
 247        wake_up(&stt_data.stt_waitq);
 248        lst_wait_until(stt_data.stt_nthreads == 0, stt_data.stt_lock,
 249                       "waiting for %d threads to terminate\n",
 250                       stt_data.stt_nthreads);
 251
 252        spin_unlock(&stt_data.stt_lock);
 253}
 254