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.gnu.org/licenses/gpl-2.0.html
  19 *
  20 * GPL HEADER END
  21 */
  22/*
  23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  24 * Use is subject to license terms.
  25 *
  26 * Copyright (c) 2011, 2012, Intel Corporation.
  27 */
  28/*
  29 * This file is part of Lustre, http://www.lustre.org/
  30 * Lustre is a trademark of Sun Microsystems, Inc.
  31 *
  32 * lnet/selftest/timer.c
  33 *
  34 * Author: Isaac Huang <isaac@clusterfs.com>
  35 */
  36
  37#define DEBUG_SUBSYSTEM S_LNET
  38
  39#include "selftest.h"
  40
  41/*
  42 * Timers are implemented as a sorted queue of expiry times. The queue
  43 * is slotted, with each slot holding timers which expire in a
  44 * 2**STTIMER_MINPOLL (8) second period. The timers in each slot are
  45 * sorted by increasing expiry time. The number of slots is 2**7 (128),
  46 * to cover a time period of 1024 seconds into the future before wrapping.
  47 */
  48#define STTIMER_MINPOLL        3        /* log2 min poll interval (8 s) */
  49#define STTIMER_SLOTTIME        BIT(STTIMER_MINPOLL)
  50#define STTIMER_SLOTTIMEMASK   (~(STTIMER_SLOTTIME - 1))
  51#define STTIMER_NSLOTS          BIT(7)
  52#define STTIMER_SLOT(t)        (&stt_data.stt_hash[(((t) >> STTIMER_MINPOLL) & \
  53                                                    (STTIMER_NSLOTS - 1))])
  54
  55static struct st_timer_data {
  56        spinlock_t        stt_lock;
  57        unsigned long     stt_prev_slot; /* start time of the slot processed
  58                                          * previously
  59                                          */
  60        struct list_head  stt_hash[STTIMER_NSLOTS];
  61        int               stt_shuttingdown;
  62        wait_queue_head_t stt_waitq;
  63        int               stt_nthreads;
  64} stt_data;
  65
  66void
  67stt_add_timer(struct stt_timer *timer)
  68{
  69        struct list_head *pos;
  70
  71        spin_lock(&stt_data.stt_lock);
  72
  73        LASSERT(stt_data.stt_nthreads > 0);
  74        LASSERT(!stt_data.stt_shuttingdown);
  75        LASSERT(timer->stt_func);
  76        LASSERT(list_empty(&timer->stt_list));
  77        LASSERT(timer->stt_expires > ktime_get_real_seconds());
  78
  79        /* a simple insertion sort */
  80        list_for_each_prev(pos, STTIMER_SLOT(timer->stt_expires)) {
  81                struct stt_timer *old = list_entry(pos, struct stt_timer,
  82                                                   stt_list);
  83
  84                if (timer->stt_expires >= old->stt_expires)
  85                        break;
  86        }
  87        list_add(&timer->stt_list, pos);
  88
  89        spin_unlock(&stt_data.stt_lock);
  90}
  91
  92/*
  93 * The function returns whether it has deactivated a pending timer or not.
  94 * (ie. del_timer() of an inactive timer returns 0, del_timer() of an
  95 * active timer returns 1.)
  96 *
  97 * CAVEAT EMPTOR:
  98 * When 0 is returned, it is possible that timer->stt_func _is_ running on
  99 * another CPU.
 100 */
 101int
 102stt_del_timer(struct stt_timer *timer)
 103{
 104        int ret = 0;
 105
 106        spin_lock(&stt_data.stt_lock);
 107
 108        LASSERT(stt_data.stt_nthreads > 0);
 109        LASSERT(!stt_data.stt_shuttingdown);
 110
 111        if (!list_empty(&timer->stt_list)) {
 112                ret = 1;
 113                list_del_init(&timer->stt_list);
 114        }
 115
 116        spin_unlock(&stt_data.stt_lock);
 117        return ret;
 118}
 119
 120/* called with stt_data.stt_lock held */
 121static int
 122stt_expire_list(struct list_head *slot, time64_t now)
 123{
 124        int expired = 0;
 125        struct stt_timer *timer;
 126
 127        while (!list_empty(slot)) {
 128                timer = list_entry(slot->next, struct stt_timer, stt_list);
 129
 130                if (timer->stt_expires > now)
 131                        break;
 132
 133                list_del_init(&timer->stt_list);
 134                spin_unlock(&stt_data.stt_lock);
 135
 136                expired++;
 137                (*timer->stt_func) (timer->stt_data);
 138
 139                spin_lock(&stt_data.stt_lock);
 140        }
 141
 142        return expired;
 143}
 144
 145static int
 146stt_check_timers(unsigned long *last)
 147{
 148        int expired = 0;
 149        time64_t now;
 150        unsigned long this_slot;
 151
 152        now = ktime_get_real_seconds();
 153        this_slot = now & STTIMER_SLOTTIMEMASK;
 154
 155        spin_lock(&stt_data.stt_lock);
 156
 157        while (cfs_time_aftereq(this_slot, *last)) {
 158                expired += stt_expire_list(STTIMER_SLOT(this_slot), now);
 159                this_slot = cfs_time_sub(this_slot, STTIMER_SLOTTIME);
 160        }
 161
 162        *last = now & STTIMER_SLOTTIMEMASK;
 163        spin_unlock(&stt_data.stt_lock);
 164        return expired;
 165}
 166
 167static int
 168stt_timer_main(void *arg)
 169{
 170        int rc = 0;
 171
 172        cfs_block_allsigs();
 173
 174        while (!stt_data.stt_shuttingdown) {
 175                stt_check_timers(&stt_data.stt_prev_slot);
 176
 177                rc = wait_event_timeout(stt_data.stt_waitq,
 178                                        stt_data.stt_shuttingdown,
 179                                        cfs_time_seconds(STTIMER_SLOTTIME));
 180        }
 181
 182        spin_lock(&stt_data.stt_lock);
 183        stt_data.stt_nthreads--;
 184        spin_unlock(&stt_data.stt_lock);
 185        return rc;
 186}
 187
 188static int
 189stt_start_timer_thread(void)
 190{
 191        struct task_struct *task;
 192
 193        LASSERT(!stt_data.stt_shuttingdown);
 194
 195        task = kthread_run(stt_timer_main, NULL, "st_timer");
 196        if (IS_ERR(task))
 197                return PTR_ERR(task);
 198
 199        spin_lock(&stt_data.stt_lock);
 200        stt_data.stt_nthreads++;
 201        spin_unlock(&stt_data.stt_lock);
 202        return 0;
 203}
 204
 205int
 206stt_startup(void)
 207{
 208        int rc = 0;
 209        int i;
 210
 211        stt_data.stt_shuttingdown = 0;
 212        stt_data.stt_prev_slot = ktime_get_real_seconds() & STTIMER_SLOTTIMEMASK;
 213
 214        spin_lock_init(&stt_data.stt_lock);
 215        for (i = 0; i < STTIMER_NSLOTS; i++)
 216                INIT_LIST_HEAD(&stt_data.stt_hash[i]);
 217
 218        stt_data.stt_nthreads = 0;
 219        init_waitqueue_head(&stt_data.stt_waitq);
 220        rc = stt_start_timer_thread();
 221        if (rc)
 222                CERROR("Can't spawn timer thread: %d\n", rc);
 223
 224        return rc;
 225}
 226
 227void
 228stt_shutdown(void)
 229{
 230        int i;
 231
 232        spin_lock(&stt_data.stt_lock);
 233
 234        for (i = 0; i < STTIMER_NSLOTS; i++)
 235                LASSERT(list_empty(&stt_data.stt_hash[i]));
 236
 237        stt_data.stt_shuttingdown = 1;
 238
 239        wake_up(&stt_data.stt_waitq);
 240        lst_wait_until(!stt_data.stt_nthreads, stt_data.stt_lock,
 241                       "waiting for %d threads to terminate\n",
 242                       stt_data.stt_nthreads);
 243
 244        spin_unlock(&stt_data.stt_lock);
 245}
 246