timer.c revision 68b636b68ebb58ef7a400886249be6479bdce96d
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