Whamcloud - gitweb
96240bbe370a8111af601157989767b0628f2c13
[fs/lustre-release.git] / lnet / selftest / timer.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * GPL HEADER START
5  *
6  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 only,
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License version 2 for more details (a copy is included
16  * in the LICENSE file that accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License
19  * version 2 along with this program; If not, see
20  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
21  *
22  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
23  * CA 95054 USA or visit www.sun.com if you need additional information or
24  * have any questions.
25  *
26  * GPL HEADER END
27  */
28 /*
29  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
30  * Use is subject to license terms.
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
60 struct st_timer_data {
61         cfs_spinlock_t   stt_lock;
62         /* start time of the slot processed previously */
63         cfs_time_t       stt_prev_slot;
64         cfs_list_t       stt_hash[STTIMER_NSLOTS];
65         int              stt_shuttingdown;
66 #ifdef __KERNEL__
67         cfs_waitq_t      stt_waitq;
68         int              stt_nthreads;
69 #endif
70 } stt_data;
71
72 void
73 stt_add_timer (stt_timer_t *timer)
74 {
75         cfs_list_t *pos;
76
77         cfs_spin_lock(&stt_data.stt_lock);
78
79 #ifdef __KERNEL__
80         LASSERT (stt_data.stt_nthreads > 0);
81 #endif
82         LASSERT (!stt_data.stt_shuttingdown);
83         LASSERT (timer->stt_func != NULL);
84         LASSERT (cfs_list_empty(&timer->stt_list));
85         LASSERT (cfs_time_after(timer->stt_expires, cfs_time_current_sec()));
86
87         /* a simple insertion sort */
88         cfs_list_for_each_prev (pos, STTIMER_SLOT(timer->stt_expires)) {
89                 stt_timer_t *old = cfs_list_entry(pos, stt_timer_t, stt_list);
90
91                 if (cfs_time_aftereq(timer->stt_expires, old->stt_expires))
92                         break;
93         }
94         cfs_list_add(&timer->stt_list, pos);
95
96         cfs_spin_unlock(&stt_data.stt_lock);
97 }
98
99 /*
100  * The function returns whether it has deactivated a pending timer or not.
101  * (ie. del_timer() of an inactive timer returns 0, del_timer() of an
102  * active timer returns 1.)
103  *
104  * CAVEAT EMPTOR:
105  * When 0 is returned, it is possible that timer->stt_func _is_ running on
106  * another CPU.
107  */
108 int
109 stt_del_timer (stt_timer_t *timer)
110 {
111         int ret = 0;
112
113         cfs_spin_lock(&stt_data.stt_lock);
114
115 #ifdef __KERNEL__
116         LASSERT (stt_data.stt_nthreads > 0);
117 #endif
118         LASSERT (!stt_data.stt_shuttingdown);
119
120         if (!cfs_list_empty(&timer->stt_list)) {
121                 ret = 1;
122                 cfs_list_del_init(&timer->stt_list);
123         }
124
125         cfs_spin_unlock(&stt_data.stt_lock);
126         return ret;
127 }
128
129 /* called with stt_data.stt_lock held */
130 int
131 stt_expire_list (cfs_list_t *slot, cfs_time_t now)
132 {
133         int          expired = 0;
134         stt_timer_t *timer;
135
136         while (!cfs_list_empty(slot)) {
137                 timer = cfs_list_entry(slot->next, stt_timer_t, stt_list);
138
139                 if (cfs_time_after(timer->stt_expires, now))
140                         break;
141
142                 cfs_list_del_init(&timer->stt_list);
143                 cfs_spin_unlock(&stt_data.stt_lock);
144
145                 expired++;
146                 (*timer->stt_func) (timer->stt_data);
147                 
148                 cfs_spin_lock(&stt_data.stt_lock);
149         }
150
151         return expired;
152 }
153
154 int
155 stt_check_timers (cfs_time_t *last)
156 {
157         int        expired = 0;
158         cfs_time_t now;
159         cfs_time_t this_slot;
160
161         now = cfs_time_current_sec();
162         this_slot = now & STTIMER_SLOTTIMEMASK;
163
164         cfs_spin_lock(&stt_data.stt_lock);
165
166         while (cfs_time_aftereq(this_slot, *last)) {
167                 expired += stt_expire_list(STTIMER_SLOT(this_slot), now);
168                 this_slot = cfs_time_sub(this_slot, STTIMER_SLOTTIME);
169         }
170
171         *last = now & STTIMER_SLOTTIMEMASK;
172         cfs_spin_unlock(&stt_data.stt_lock);
173         return expired;
174 }
175
176 #ifdef __KERNEL__
177
178 int
179 stt_timer_main (void *arg)
180 {
181         int rc = 0;
182         UNUSED(arg);
183
184         cfs_daemonize("st_timer");
185         cfs_block_allsigs();
186
187         while (!stt_data.stt_shuttingdown) {
188                 stt_check_timers(&stt_data.stt_prev_slot);
189
190                 cfs_waitq_wait_event_timeout(stt_data.stt_waitq,
191                                    stt_data.stt_shuttingdown,
192                                    cfs_time_seconds(STTIMER_SLOTTIME),
193                                    rc);
194         }
195
196         cfs_spin_lock(&stt_data.stt_lock);
197         stt_data.stt_nthreads--;
198         cfs_spin_unlock(&stt_data.stt_lock);
199         return 0;
200 }
201
202 int
203 stt_start_timer_thread (void)
204 {
205         long pid;
206
207         LASSERT (!stt_data.stt_shuttingdown);
208
209         pid = cfs_create_thread(stt_timer_main, NULL, 0);
210         if (pid < 0)
211                 return (int)pid;
212
213         cfs_spin_lock(&stt_data.stt_lock);
214         stt_data.stt_nthreads++;
215         cfs_spin_unlock(&stt_data.stt_lock);
216         return 0;
217 }
218
219 #else /* !__KERNEL__ */
220
221 int
222 stt_check_events (void)
223 {
224         return stt_check_timers(&stt_data.stt_prev_slot);
225 }
226
227 int
228 stt_poll_interval (void)
229 {
230         return STTIMER_SLOTTIME;
231 }
232
233 #endif
234
235 int
236 stt_startup (void)
237 {
238         int rc = 0;
239         int i;
240
241         stt_data.stt_shuttingdown = 0;
242         stt_data.stt_prev_slot = cfs_time_current_sec() & STTIMER_SLOTTIMEMASK;
243
244         cfs_spin_lock_init(&stt_data.stt_lock);
245         for (i = 0; i < STTIMER_NSLOTS; i++)
246                 CFS_INIT_LIST_HEAD(&stt_data.stt_hash[i]);
247
248 #ifdef __KERNEL__
249         stt_data.stt_nthreads = 0;
250         cfs_waitq_init(&stt_data.stt_waitq);
251         rc = stt_start_timer_thread();
252         if (rc != 0)
253                 CERROR ("Can't spawn timer thread: %d\n", rc);
254 #endif
255
256         return rc;
257 }
258
259 void
260 stt_shutdown (void)
261 {
262         int i;
263
264         cfs_spin_lock(&stt_data.stt_lock);
265
266         for (i = 0; i < STTIMER_NSLOTS; i++)
267                 LASSERT (cfs_list_empty(&stt_data.stt_hash[i]));
268
269         stt_data.stt_shuttingdown = 1;
270
271 #ifdef __KERNEL__
272         cfs_waitq_signal(&stt_data.stt_waitq);
273         lst_wait_until(stt_data.stt_nthreads == 0, stt_data.stt_lock,
274                        "waiting for %d threads to terminate\n",
275                        stt_data.stt_nthreads);
276 #endif
277
278         cfs_spin_unlock(&stt_data.stt_lock);
279         return;
280 }