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