Whamcloud - gitweb
LU-1146 build: batch update copyright messages
[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  * Copyright (c) 2011, Whamcloud, Inc.
33  */
34 /*
35  * This file is part of Lustre, http://www.lustre.org/
36  * Lustre is a trademark of Sun Microsystems, Inc.
37  *
38  * lnet/selftest/timer.c
39  *
40  * Author: Isaac Huang <isaac@clusterfs.com>
41  */
42
43 #define DEBUG_SUBSYSTEM S_LNET
44
45 #include "selftest.h"
46
47
48 /*
49  * Timers are implemented as a sorted queue of expiry times. The queue 
50  * is slotted, with each slot holding timers which expire in a 
51  * 2**STTIMER_MINPOLL (8) second period. The timers in each slot are 
52  * sorted by increasing expiry time. The number of slots is 2**7 (128),
53  * to cover a time period of 1024 seconds into the future before wrapping.
54  */
55 #define STTIMER_MINPOLL        3   /* log2 min poll interval (8 s) */
56 #define STTIMER_SLOTTIME       (1 << STTIMER_MINPOLL)
57 #define STTIMER_SLOTTIMEMASK   (~(STTIMER_SLOTTIME - 1))
58 #define STTIMER_NSLOTS         (1 << 7)
59 #define STTIMER_SLOT(t)        (&stt_data.stt_hash[(((t) >> STTIMER_MINPOLL) & \
60                                                     (STTIMER_NSLOTS - 1))])
61
62 struct st_timer_data {
63         cfs_spinlock_t   stt_lock;
64         /* start time of the slot processed previously */
65         cfs_time_t       stt_prev_slot;
66         cfs_list_t       stt_hash[STTIMER_NSLOTS];
67         int              stt_shuttingdown;
68 #ifdef __KERNEL__
69         cfs_waitq_t      stt_waitq;
70         int              stt_nthreads;
71 #endif
72 } stt_data;
73
74 void
75 stt_add_timer (stt_timer_t *timer)
76 {
77         cfs_list_t *pos;
78
79         cfs_spin_lock(&stt_data.stt_lock);
80
81 #ifdef __KERNEL__
82         LASSERT (stt_data.stt_nthreads > 0);
83 #endif
84         LASSERT (!stt_data.stt_shuttingdown);
85         LASSERT (timer->stt_func != NULL);
86         LASSERT (cfs_list_empty(&timer->stt_list));
87         LASSERT (cfs_time_after(timer->stt_expires, cfs_time_current_sec()));
88
89         /* a simple insertion sort */
90         cfs_list_for_each_prev (pos, STTIMER_SLOT(timer->stt_expires)) {
91                 stt_timer_t *old = cfs_list_entry(pos, stt_timer_t, stt_list);
92
93                 if (cfs_time_aftereq(timer->stt_expires, old->stt_expires))
94                         break;
95         }
96         cfs_list_add(&timer->stt_list, pos);
97
98         cfs_spin_unlock(&stt_data.stt_lock);
99 }
100
101 /*
102  * The function returns whether it has deactivated a pending timer or not.
103  * (ie. del_timer() of an inactive timer returns 0, del_timer() of an
104  * active timer returns 1.)
105  *
106  * CAVEAT EMPTOR:
107  * When 0 is returned, it is possible that timer->stt_func _is_ running on
108  * another CPU.
109  */
110 int
111 stt_del_timer (stt_timer_t *timer)
112 {
113         int ret = 0;
114
115         cfs_spin_lock(&stt_data.stt_lock);
116
117 #ifdef __KERNEL__
118         LASSERT (stt_data.stt_nthreads > 0);
119 #endif
120         LASSERT (!stt_data.stt_shuttingdown);
121
122         if (!cfs_list_empty(&timer->stt_list)) {
123                 ret = 1;
124                 cfs_list_del_init(&timer->stt_list);
125         }
126
127         cfs_spin_unlock(&stt_data.stt_lock);
128         return ret;
129 }
130
131 /* called with stt_data.stt_lock held */
132 int
133 stt_expire_list (cfs_list_t *slot, cfs_time_t now)
134 {
135         int          expired = 0;
136         stt_timer_t *timer;
137
138         while (!cfs_list_empty(slot)) {
139                 timer = cfs_list_entry(slot->next, stt_timer_t, stt_list);
140
141                 if (cfs_time_after(timer->stt_expires, now))
142                         break;
143
144                 cfs_list_del_init(&timer->stt_list);
145                 cfs_spin_unlock(&stt_data.stt_lock);
146
147                 expired++;
148                 (*timer->stt_func) (timer->stt_data);
149                 
150                 cfs_spin_lock(&stt_data.stt_lock);
151         }
152
153         return expired;
154 }
155
156 int
157 stt_check_timers (cfs_time_t *last)
158 {
159         int        expired = 0;
160         cfs_time_t now;
161         cfs_time_t this_slot;
162
163         now = cfs_time_current_sec();
164         this_slot = now & STTIMER_SLOTTIMEMASK;
165
166         cfs_spin_lock(&stt_data.stt_lock);
167
168         while (cfs_time_aftereq(this_slot, *last)) {
169                 expired += stt_expire_list(STTIMER_SLOT(this_slot), now);
170                 this_slot = cfs_time_sub(this_slot, STTIMER_SLOTTIME);
171         }
172
173         *last = now & STTIMER_SLOTTIMEMASK;
174         cfs_spin_unlock(&stt_data.stt_lock);
175         return expired;
176 }
177
178 #ifdef __KERNEL__
179
180 int
181 stt_timer_main (void *arg)
182 {
183         int rc = 0;
184         UNUSED(arg);
185
186         SET_BUT_UNUSED(rc);
187
188         cfs_daemonize("st_timer");
189         cfs_block_allsigs();
190
191         while (!stt_data.stt_shuttingdown) {
192                 stt_check_timers(&stt_data.stt_prev_slot);
193
194                 cfs_waitq_wait_event_timeout(stt_data.stt_waitq,
195                                    stt_data.stt_shuttingdown,
196                                    cfs_time_seconds(STTIMER_SLOTTIME),
197                                    rc);
198         }
199
200         cfs_spin_lock(&stt_data.stt_lock);
201         stt_data.stt_nthreads--;
202         cfs_spin_unlock(&stt_data.stt_lock);
203         return 0;
204 }
205
206 int
207 stt_start_timer_thread (void)
208 {
209         long pid;
210
211         LASSERT (!stt_data.stt_shuttingdown);
212
213         pid = cfs_create_thread(stt_timer_main, NULL, 0);
214         if (pid < 0)
215                 return (int)pid;
216
217         cfs_spin_lock(&stt_data.stt_lock);
218         stt_data.stt_nthreads++;
219         cfs_spin_unlock(&stt_data.stt_lock);
220         return 0;
221 }
222
223 #else /* !__KERNEL__ */
224
225 int
226 stt_check_events (void)
227 {
228         return stt_check_timers(&stt_data.stt_prev_slot);
229 }
230
231 int
232 stt_poll_interval (void)
233 {
234         return STTIMER_SLOTTIME;
235 }
236
237 #endif
238
239 int
240 stt_startup (void)
241 {
242         int rc = 0;
243         int i;
244
245         stt_data.stt_shuttingdown = 0;
246         stt_data.stt_prev_slot = cfs_time_current_sec() & STTIMER_SLOTTIMEMASK;
247
248         cfs_spin_lock_init(&stt_data.stt_lock);
249         for (i = 0; i < STTIMER_NSLOTS; i++)
250                 CFS_INIT_LIST_HEAD(&stt_data.stt_hash[i]);
251
252 #ifdef __KERNEL__
253         stt_data.stt_nthreads = 0;
254         cfs_waitq_init(&stt_data.stt_waitq);
255         rc = stt_start_timer_thread();
256         if (rc != 0)
257                 CERROR ("Can't spawn timer thread: %d\n", rc);
258 #endif
259
260         return rc;
261 }
262
263 void
264 stt_shutdown (void)
265 {
266         int i;
267
268         cfs_spin_lock(&stt_data.stt_lock);
269
270         for (i = 0; i < STTIMER_NSLOTS; i++)
271                 LASSERT (cfs_list_empty(&stt_data.stt_hash[i]));
272
273         stt_data.stt_shuttingdown = 1;
274
275 #ifdef __KERNEL__
276         cfs_waitq_signal(&stt_data.stt_waitq);
277         lst_wait_until(stt_data.stt_nthreads == 0, stt_data.stt_lock,
278                        "waiting for %d threads to terminate\n",
279                        stt_data.stt_nthreads);
280 #endif
281
282         cfs_spin_unlock(&stt_data.stt_lock);
283         return;
284 }