• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  * Timers are implemented as a sorted queue of expiry times. The queue
47  * is slotted, with each slot holding timers which expire in a
48  * 2**STTIMER_MINPOLL (8) second period. The timers in each slot are
49  * sorted by increasing expiry time. The number of slots is 2**7 (128),
50  * to cover a time period of 1024 seconds into the future before wrapping.
51  */
52 #define STTIMER_MINPOLL        3   /* log2 min poll interval (8 s) */
53 #define STTIMER_SLOTTIME       (1 << STTIMER_MINPOLL)
54 #define STTIMER_SLOTTIMEMASK   (~(STTIMER_SLOTTIME - 1))
55 #define STTIMER_NSLOTS	       (1 << 7)
56 #define STTIMER_SLOT(t)	       (&stt_data.stt_hash[(((t) >> STTIMER_MINPOLL) & \
57 						    (STTIMER_NSLOTS - 1))])
58 
59 static struct st_timer_data {
60 	spinlock_t        stt_lock;
61 	unsigned long     stt_prev_slot; /* start time of the slot processed
62 					  * previously */
63 	struct list_head  stt_hash[STTIMER_NSLOTS];
64 	int               stt_shuttingdown;
65 	wait_queue_head_t stt_waitq;
66 	int               stt_nthreads;
67 } stt_data;
68 
69 void
stt_add_timer(stt_timer_t * timer)70 stt_add_timer(stt_timer_t *timer)
71 {
72 	struct list_head *pos;
73 
74 	spin_lock(&stt_data.stt_lock);
75 
76 	LASSERT(stt_data.stt_nthreads > 0);
77 	LASSERT(!stt_data.stt_shuttingdown);
78 	LASSERT(timer->stt_func != NULL);
79 	LASSERT(list_empty(&timer->stt_list));
80 	LASSERT(timer->stt_expires > ktime_get_real_seconds());
81 
82 	/* a simple insertion sort */
83 	list_for_each_prev(pos, STTIMER_SLOT(timer->stt_expires)) {
84 		stt_timer_t *old = list_entry(pos, stt_timer_t, stt_list);
85 
86 		if (timer->stt_expires >= old->stt_expires)
87 			break;
88 	}
89 	list_add(&timer->stt_list, pos);
90 
91 	spin_unlock(&stt_data.stt_lock);
92 }
93 
94 /*
95  * The function returns whether it has deactivated a pending timer or not.
96  * (ie. del_timer() of an inactive timer returns 0, del_timer() of an
97  * active timer returns 1.)
98  *
99  * CAVEAT EMPTOR:
100  * When 0 is returned, it is possible that timer->stt_func _is_ running on
101  * another CPU.
102  */
103 int
stt_del_timer(stt_timer_t * timer)104 stt_del_timer(stt_timer_t *timer)
105 {
106 	int ret = 0;
107 
108 	spin_lock(&stt_data.stt_lock);
109 
110 	LASSERT(stt_data.stt_nthreads > 0);
111 	LASSERT(!stt_data.stt_shuttingdown);
112 
113 	if (!list_empty(&timer->stt_list)) {
114 		ret = 1;
115 		list_del_init(&timer->stt_list);
116 	}
117 
118 	spin_unlock(&stt_data.stt_lock);
119 	return ret;
120 }
121 
122 /* called with stt_data.stt_lock held */
123 static int
stt_expire_list(struct list_head * slot,time64_t now)124 stt_expire_list(struct list_head *slot, time64_t now)
125 {
126 	int expired = 0;
127 	stt_timer_t *timer;
128 
129 	while (!list_empty(slot)) {
130 		timer = list_entry(slot->next, stt_timer_t, stt_list);
131 
132 		if (timer->stt_expires > now)
133 			break;
134 
135 		list_del_init(&timer->stt_list);
136 		spin_unlock(&stt_data.stt_lock);
137 
138 		expired++;
139 		(*timer->stt_func) (timer->stt_data);
140 
141 		spin_lock(&stt_data.stt_lock);
142 	}
143 
144 	return expired;
145 }
146 
147 static int
stt_check_timers(unsigned long * last)148 stt_check_timers(unsigned long *last)
149 {
150 	int expired = 0;
151 	time64_t now;
152 	unsigned long this_slot;
153 
154 	now = ktime_get_real_seconds();
155 	this_slot = now & STTIMER_SLOTTIMEMASK;
156 
157 	spin_lock(&stt_data.stt_lock);
158 
159 	while (cfs_time_aftereq(this_slot, *last)) {
160 		expired += stt_expire_list(STTIMER_SLOT(this_slot), now);
161 		this_slot = cfs_time_sub(this_slot, STTIMER_SLOTTIME);
162 	}
163 
164 	*last = now & STTIMER_SLOTTIMEMASK;
165 	spin_unlock(&stt_data.stt_lock);
166 	return expired;
167 }
168 
169 static int
stt_timer_main(void * arg)170 stt_timer_main(void *arg)
171 {
172 	cfs_block_allsigs();
173 
174 	while (!stt_data.stt_shuttingdown) {
175 		stt_check_timers(&stt_data.stt_prev_slot);
176 
177 		wait_event_timeout(stt_data.stt_waitq,
178 				   stt_data.stt_shuttingdown,
179 				   cfs_time_seconds(STTIMER_SLOTTIME));
180 	}
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 static int
stt_start_timer_thread(void)189 stt_start_timer_thread(void)
190 {
191 	struct task_struct *task;
192 
193 	LASSERT(!stt_data.stt_shuttingdown);
194 
195 	task = kthread_run(stt_timer_main, NULL, "st_timer");
196 	if (IS_ERR(task))
197 		return PTR_ERR(task);
198 
199 	spin_lock(&stt_data.stt_lock);
200 	stt_data.stt_nthreads++;
201 	spin_unlock(&stt_data.stt_lock);
202 	return 0;
203 }
204 
205 int
stt_startup(void)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 = ktime_get_real_seconds() & STTIMER_SLOTTIMEMASK;
213 
214 	spin_lock_init(&stt_data.stt_lock);
215 	for (i = 0; i < STTIMER_NSLOTS; i++)
216 		INIT_LIST_HEAD(&stt_data.stt_hash[i]);
217 
218 	stt_data.stt_nthreads = 0;
219 	init_waitqueue_head(&stt_data.stt_waitq);
220 	rc = stt_start_timer_thread();
221 	if (rc != 0)
222 		CERROR("Can't spawn timer thread: %d\n", rc);
223 
224 	return rc;
225 }
226 
227 void
stt_shutdown(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 	wake_up(&stt_data.stt_waitq);
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 
244 	spin_unlock(&stt_data.stt_lock);
245 }
246