1 /*
2 * GPL HEADER START
3 *
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 only,
8 * as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License version 2 for more details (a copy is included
14 * in the LICENSE file that accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License
17 * version 2 along with this program; If not, see
18 * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
19 *
20 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21 * CA 95054 USA or visit www.sun.com if you need additional information or
22 * have any questions.
23 *
24 * GPL HEADER END
25 */
26 /*
27 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
28 * Use is subject to license terms.
29 *
30 * Copyright (c) 2011, 2012, Intel Corporation.
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 spinlock_t stt_lock;
62 /* start time of the slot processed previously */
63 unsigned long stt_prev_slot;
64 struct list_head stt_hash[STTIMER_NSLOTS];
65 int stt_shuttingdown;
66 wait_queue_head_t stt_waitq;
67 int stt_nthreads;
68 } stt_data;
69
70 void
stt_add_timer(stt_timer_t * timer)71 stt_add_timer(stt_timer_t *timer)
72 {
73 struct list_head *pos;
74
75 spin_lock(&stt_data.stt_lock);
76
77 LASSERT(stt_data.stt_nthreads > 0);
78 LASSERT(!stt_data.stt_shuttingdown);
79 LASSERT(timer->stt_func != NULL);
80 LASSERT(list_empty(&timer->stt_list));
81 LASSERT(cfs_time_after(timer->stt_expires, get_seconds()));
82
83 /* a simple insertion sort */
84 list_for_each_prev(pos, STTIMER_SLOT(timer->stt_expires)) {
85 stt_timer_t *old = list_entry(pos, stt_timer_t, stt_list);
86
87 if (cfs_time_aftereq(timer->stt_expires, old->stt_expires))
88 break;
89 }
90 list_add(&timer->stt_list, pos);
91
92 spin_unlock(&stt_data.stt_lock);
93 }
94
95 /*
96 * The function returns whether it has deactivated a pending timer or not.
97 * (ie. del_timer() of an inactive timer returns 0, del_timer() of an
98 * active timer returns 1.)
99 *
100 * CAVEAT EMPTOR:
101 * When 0 is returned, it is possible that timer->stt_func _is_ running on
102 * another CPU.
103 */
104 int
stt_del_timer(stt_timer_t * timer)105 stt_del_timer(stt_timer_t *timer)
106 {
107 int ret = 0;
108
109 spin_lock(&stt_data.stt_lock);
110
111 LASSERT(stt_data.stt_nthreads > 0);
112 LASSERT(!stt_data.stt_shuttingdown);
113
114 if (!list_empty(&timer->stt_list)) {
115 ret = 1;
116 list_del_init(&timer->stt_list);
117 }
118
119 spin_unlock(&stt_data.stt_lock);
120 return ret;
121 }
122
123 /* called with stt_data.stt_lock held */
124 int
stt_expire_list(struct list_head * slot,unsigned long now)125 stt_expire_list(struct list_head *slot, unsigned long now)
126 {
127 int expired = 0;
128 stt_timer_t *timer;
129
130 while (!list_empty(slot)) {
131 timer = list_entry(slot->next, stt_timer_t, stt_list);
132
133 if (cfs_time_after(timer->stt_expires, now))
134 break;
135
136 list_del_init(&timer->stt_list);
137 spin_unlock(&stt_data.stt_lock);
138
139 expired++;
140 (*timer->stt_func) (timer->stt_data);
141
142 spin_lock(&stt_data.stt_lock);
143 }
144
145 return expired;
146 }
147
148 int
stt_check_timers(unsigned long * last)149 stt_check_timers(unsigned long *last)
150 {
151 int expired = 0;
152 unsigned long now;
153 unsigned long this_slot;
154
155 now = get_seconds();
156 this_slot = now & STTIMER_SLOTTIMEMASK;
157
158 spin_lock(&stt_data.stt_lock);
159
160 while (cfs_time_aftereq(this_slot, *last)) {
161 expired += stt_expire_list(STTIMER_SLOT(this_slot), now);
162 this_slot = cfs_time_sub(this_slot, STTIMER_SLOTTIME);
163 }
164
165 *last = now & STTIMER_SLOTTIMEMASK;
166 spin_unlock(&stt_data.stt_lock);
167 return expired;
168 }
169
170
171 int
stt_timer_main(void * arg)172 stt_timer_main(void *arg)
173 {
174 cfs_block_allsigs();
175
176 while (!stt_data.stt_shuttingdown) {
177 stt_check_timers(&stt_data.stt_prev_slot);
178
179 wait_event_timeout(stt_data.stt_waitq,
180 stt_data.stt_shuttingdown,
181 cfs_time_seconds(STTIMER_SLOTTIME));
182 }
183
184 spin_lock(&stt_data.stt_lock);
185 stt_data.stt_nthreads--;
186 spin_unlock(&stt_data.stt_lock);
187 return 0;
188 }
189
190 int
stt_start_timer_thread(void)191 stt_start_timer_thread(void)
192 {
193 struct task_struct *task;
194
195 LASSERT(!stt_data.stt_shuttingdown);
196
197 task = kthread_run(stt_timer_main, NULL, "st_timer");
198 if (IS_ERR(task))
199 return PTR_ERR(task);
200
201 spin_lock(&stt_data.stt_lock);
202 stt_data.stt_nthreads++;
203 spin_unlock(&stt_data.stt_lock);
204 return 0;
205 }
206
207
208 int
stt_startup(void)209 stt_startup(void)
210 {
211 int rc = 0;
212 int i;
213
214 stt_data.stt_shuttingdown = 0;
215 stt_data.stt_prev_slot = get_seconds() & STTIMER_SLOTTIMEMASK;
216
217 spin_lock_init(&stt_data.stt_lock);
218 for (i = 0; i < STTIMER_NSLOTS; i++)
219 INIT_LIST_HEAD(&stt_data.stt_hash[i]);
220
221 stt_data.stt_nthreads = 0;
222 init_waitqueue_head(&stt_data.stt_waitq);
223 rc = stt_start_timer_thread();
224 if (rc != 0)
225 CERROR("Can't spawn timer thread: %d\n", rc);
226
227 return rc;
228 }
229
230 void
stt_shutdown(void)231 stt_shutdown(void)
232 {
233 int i;
234
235 spin_lock(&stt_data.stt_lock);
236
237 for (i = 0; i < STTIMER_NSLOTS; i++)
238 LASSERT(list_empty(&stt_data.stt_hash[i]));
239
240 stt_data.stt_shuttingdown = 1;
241
242 wake_up(&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
247 spin_unlock(&stt_data.stt_lock);
248 }
249