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