Whamcloud - gitweb
- added LNET self test (landing b_self_test).
[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         int              stt_nthreads;
40 #endif
41 } stt_data;
42
43 void
44 stt_add_timer (stt_timer_t *timer)
45 {
46         struct list_head *pos;
47
48         spin_lock(&stt_data.stt_lock);
49
50 #ifdef __KERNEL__
51         LASSERT (stt_data.stt_nthreads > 0);
52 #endif
53         LASSERT (!stt_data.stt_shuttingdown);
54         LASSERT (timer->stt_func != NULL);
55         LASSERT (list_empty(&timer->stt_list));
56         LASSERT (cfs_time_after(timer->stt_expires, cfs_time_current_sec()));
57
58         /* a simple insertion sort */
59         list_for_each_prev (pos, STTIMER_SLOT(timer->stt_expires)) {
60                 stt_timer_t *old = list_entry(pos, stt_timer_t, stt_list);
61
62                 if (cfs_time_aftereq(timer->stt_expires, old->stt_expires))
63                         break;
64         }
65         list_add(&timer->stt_list, pos);
66
67         spin_unlock(&stt_data.stt_lock);
68 }
69
70 /*
71  * The function returns whether it has deactivated a pending timer or not.
72  * (ie. del_timer() of an inactive timer returns 0, del_timer() of an
73  * active timer returns 1.)
74  *
75  * CAVEAT EMPTOR:
76  * When 0 is returned, it is possible that timer->stt_func _is_ running on
77  * another CPU.
78  */
79 int
80 stt_del_timer (stt_timer_t *timer)
81 {
82         int ret = 0;
83
84         spin_lock(&stt_data.stt_lock);
85
86 #ifdef __KERNEL__
87         LASSERT (stt_data.stt_nthreads > 0);
88 #endif
89         LASSERT (!stt_data.stt_shuttingdown);
90
91         if (!list_empty(&timer->stt_list)) {
92                 ret = 1;
93                 list_del_init(&timer->stt_list);
94         }
95
96         spin_unlock(&stt_data.stt_lock);
97         return ret;
98 }
99
100 /* called with stt_data.stt_lock held */
101 int
102 stt_expire_list (struct list_head *slot, cfs_time_t now)
103 {
104         int          expired = 0;
105         stt_timer_t *timer;
106
107         while (!list_empty(slot)) {
108                 timer = list_entry(slot->next, stt_timer_t, stt_list);
109
110                 if (cfs_time_after(timer->stt_expires, now))
111                         break;
112
113                 list_del_init(&timer->stt_list);
114                 spin_unlock(&stt_data.stt_lock);
115
116                 expired++;
117                 (*timer->stt_func) (timer->stt_data);
118                 
119                 spin_lock(&stt_data.stt_lock);
120         }
121
122         return expired;
123 }
124
125 int
126 stt_check_timers (cfs_time_t *last)
127 {
128         int        expired = 0;
129         cfs_time_t now;
130         cfs_time_t this_slot;
131
132         now = cfs_time_current_sec();
133         this_slot = now & STTIMER_SLOTTIMEMASK;
134
135         spin_lock(&stt_data.stt_lock);
136
137         while (cfs_time_aftereq(this_slot, *last)) {
138                 expired += stt_expire_list(STTIMER_SLOT(this_slot), now);
139                 this_slot = cfs_time_sub(this_slot, STTIMER_SLOTTIME);
140         }
141
142         *last = now & STTIMER_SLOTTIMEMASK;
143         spin_unlock(&stt_data.stt_lock);
144         return expired;
145 }
146
147 #ifdef __KERNEL__
148
149 int
150 stt_timer_main (void *arg)
151 {
152         UNUSED(arg);
153
154         cfs_daemonize("st_timer");
155         cfs_block_allsigs();
156
157         while (!stt_data.stt_shuttingdown) {
158                 stt_check_timers(&stt_data.stt_prev_slot);
159
160                 set_current_state(CFS_TASK_INTERRUPTIBLE);
161                 cfs_schedule_timeout(CFS_TASK_INTERRUPTIBLE,
162                                      cfs_time_seconds(STTIMER_SLOTTIME));
163         }
164
165         spin_lock(&stt_data.stt_lock);
166         stt_data.stt_nthreads--;
167         spin_unlock(&stt_data.stt_lock);
168         return 0;
169 }
170
171 int
172 stt_start_timer_thread (void)
173 {
174         long pid;
175
176         LASSERT (!stt_data.stt_shuttingdown);
177
178         pid = cfs_kernel_thread(stt_timer_main, NULL, 0);
179         if (pid < 0)
180                 return (int)pid;
181
182         spin_lock(&stt_data.stt_lock);
183         stt_data.stt_nthreads++;
184         spin_unlock(&stt_data.stt_lock);
185         return 0;
186 }
187
188 #else /* !__KERNEL__ */
189
190 int
191 stt_check_events (void)
192 {
193         return stt_check_timers(&stt_data.stt_prev_slot);
194 }
195
196 int
197 stt_poll_interval (void)
198 {
199         return STTIMER_SLOTTIME;
200 }
201
202 #endif
203
204 int
205 stt_startup (void)
206 {
207         int rc = 0;
208         int i;
209
210         stt_data.stt_shuttingdown = 0;
211         stt_data.stt_prev_slot = cfs_time_current_sec() & STTIMER_SLOTTIMEMASK;
212
213         spin_lock_init(&stt_data.stt_lock);
214         for (i = 0; i < STTIMER_NSLOTS; i++)
215                 CFS_INIT_LIST_HEAD(&stt_data.stt_hash[i]);
216
217 #ifdef __KERNEL__
218         stt_data.stt_nthreads = 0;
219         rc = stt_start_timer_thread();
220         if (rc != 0)
221                 CERROR ("Can't spawn timer, stt_startup() has failed: %d\n", rc);
222 #endif
223
224         return rc;
225 }
226
227 void
228 stt_shutdown (void)
229 {
230         int i;
231
232         spin_lock(&stt_data.stt_lock);
233
234         for (i = 0; i < STTIMER_NSLOTS; i++)
235                 LASSERT (list_empty(&stt_data.stt_hash[i]));
236
237         stt_data.stt_shuttingdown = 1;
238
239 #ifdef __KERNEL__
240         lst_wait_until(stt_data.stt_nthreads == 0, stt_data.stt_lock,
241                        "waiting for %d threads to terminate\n",
242                        stt_data.stt_nthreads);
243 #endif
244
245         spin_unlock(&stt_data.stt_lock);
246         return;
247 }