timer.c revision 68b636b68ebb58ef7a400886249be6479bdce96d
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
60struct st_timer_data {
61	spinlock_t	 stt_lock;
62	/* start time of the slot processed previously */
63	cfs_time_t       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
70void
71stt_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, cfs_time_current_sec()));
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 */
104int
105stt_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 */
124int
125stt_expire_list (struct list_head *slot, cfs_time_t 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
148int
149stt_check_timers (cfs_time_t *last)
150{
151	int	expired = 0;
152	cfs_time_t now;
153	cfs_time_t this_slot;
154
155	now = cfs_time_current_sec();
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
171int
172stt_timer_main (void *arg)
173{
174	int rc = 0;
175	UNUSED(arg);
176
177	SET_BUT_UNUSED(rc);
178
179	cfs_block_allsigs();
180
181	while (!stt_data.stt_shuttingdown) {
182		stt_check_timers(&stt_data.stt_prev_slot);
183
184		rc = wait_event_timeout(stt_data.stt_waitq,
185					stt_data.stt_shuttingdown,
186					cfs_time_seconds(STTIMER_SLOTTIME));
187	}
188
189	spin_lock(&stt_data.stt_lock);
190	stt_data.stt_nthreads--;
191	spin_unlock(&stt_data.stt_lock);
192	return 0;
193}
194
195int
196stt_start_timer_thread (void)
197{
198	struct task_struct *task;
199
200	LASSERT(!stt_data.stt_shuttingdown);
201
202	task = kthread_run(stt_timer_main, NULL, "st_timer");
203	if (IS_ERR(task))
204		return PTR_ERR(task);
205
206	spin_lock(&stt_data.stt_lock);
207	stt_data.stt_nthreads++;
208	spin_unlock(&stt_data.stt_lock);
209	return 0;
210}
211
212
213int
214stt_startup (void)
215{
216	int rc = 0;
217	int i;
218
219	stt_data.stt_shuttingdown = 0;
220	stt_data.stt_prev_slot = cfs_time_current_sec() & STTIMER_SLOTTIMEMASK;
221
222	spin_lock_init(&stt_data.stt_lock);
223	for (i = 0; i < STTIMER_NSLOTS; i++)
224		INIT_LIST_HEAD(&stt_data.stt_hash[i]);
225
226	stt_data.stt_nthreads = 0;
227	init_waitqueue_head(&stt_data.stt_waitq);
228	rc = stt_start_timer_thread();
229	if (rc != 0)
230		CERROR ("Can't spawn timer thread: %d\n", rc);
231
232	return rc;
233}
234
235void
236stt_shutdown (void)
237{
238	int i;
239
240	spin_lock(&stt_data.stt_lock);
241
242	for (i = 0; i < STTIMER_NSLOTS; i++)
243		LASSERT (list_empty(&stt_data.stt_hash[i]));
244
245	stt_data.stt_shuttingdown = 1;
246
247	wake_up(&stt_data.stt_waitq);
248	lst_wait_until(stt_data.stt_nthreads == 0, stt_data.stt_lock,
249		       "waiting for %d threads to terminate\n",
250		       stt_data.stt_nthreads);
251
252	spin_unlock(&stt_data.stt_lock);
253}
254