• 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.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Copyright (c) 2011, 2012, Intel Corporation.
27  */
28 /*
29  * This file is part of Lustre, http://www.lustre.org/
30  * Lustre is a trademark of Sun Microsystems, Inc.
31  *
32  * lnet/selftest/timer.c
33  *
34  * Author: Isaac Huang <isaac@clusterfs.com>
35  */
36 
37 #define DEBUG_SUBSYSTEM S_LNET
38 
39 #include "selftest.h"
40 
41 /*
42  * Timers are implemented as a sorted queue of expiry times. The queue
43  * is slotted, with each slot holding timers which expire in a
44  * 2**STTIMER_MINPOLL (8) second period. The timers in each slot are
45  * sorted by increasing expiry time. The number of slots is 2**7 (128),
46  * to cover a time period of 1024 seconds into the future before wrapping.
47  */
48 #define STTIMER_MINPOLL        3	/* log2 min poll interval (8 s) */
49 #define STTIMER_SLOTTIME       (1 << STTIMER_MINPOLL)
50 #define STTIMER_SLOTTIMEMASK   (~(STTIMER_SLOTTIME - 1))
51 #define STTIMER_NSLOTS	       (1 << 7)
52 #define STTIMER_SLOT(t)	       (&stt_data.stt_hash[(((t) >> STTIMER_MINPOLL) & \
53 						    (STTIMER_NSLOTS - 1))])
54 
55 static struct st_timer_data {
56 	spinlock_t	  stt_lock;
57 	unsigned long	  stt_prev_slot; /* start time of the slot processed
58 					  * previously */
59 	struct list_head  stt_hash[STTIMER_NSLOTS];
60 	int		  stt_shuttingdown;
61 	wait_queue_head_t stt_waitq;
62 	int		  stt_nthreads;
63 } stt_data;
64 
65 void
stt_add_timer(struct stt_timer * timer)66 stt_add_timer(struct stt_timer *timer)
67 {
68 	struct list_head *pos;
69 
70 	spin_lock(&stt_data.stt_lock);
71 
72 	LASSERT(stt_data.stt_nthreads > 0);
73 	LASSERT(!stt_data.stt_shuttingdown);
74 	LASSERT(timer->stt_func);
75 	LASSERT(list_empty(&timer->stt_list));
76 	LASSERT(timer->stt_expires > ktime_get_real_seconds());
77 
78 	/* a simple insertion sort */
79 	list_for_each_prev(pos, STTIMER_SLOT(timer->stt_expires)) {
80 		struct stt_timer *old = list_entry(pos, struct stt_timer,
81 						   stt_list);
82 
83 		if (timer->stt_expires >= old->stt_expires)
84 			break;
85 	}
86 	list_add(&timer->stt_list, pos);
87 
88 	spin_unlock(&stt_data.stt_lock);
89 }
90 
91 /*
92  * The function returns whether it has deactivated a pending timer or not.
93  * (ie. del_timer() of an inactive timer returns 0, del_timer() of an
94  * active timer returns 1.)
95  *
96  * CAVEAT EMPTOR:
97  * When 0 is returned, it is possible that timer->stt_func _is_ running on
98  * another CPU.
99  */
100 int
stt_del_timer(struct stt_timer * timer)101 stt_del_timer(struct stt_timer *timer)
102 {
103 	int ret = 0;
104 
105 	spin_lock(&stt_data.stt_lock);
106 
107 	LASSERT(stt_data.stt_nthreads > 0);
108 	LASSERT(!stt_data.stt_shuttingdown);
109 
110 	if (!list_empty(&timer->stt_list)) {
111 		ret = 1;
112 		list_del_init(&timer->stt_list);
113 	}
114 
115 	spin_unlock(&stt_data.stt_lock);
116 	return ret;
117 }
118 
119 /* called with stt_data.stt_lock held */
120 static int
stt_expire_list(struct list_head * slot,time64_t now)121 stt_expire_list(struct list_head *slot, time64_t now)
122 {
123 	int expired = 0;
124 	struct stt_timer *timer;
125 
126 	while (!list_empty(slot)) {
127 		timer = list_entry(slot->next, struct stt_timer, stt_list);
128 
129 		if (timer->stt_expires > now)
130 			break;
131 
132 		list_del_init(&timer->stt_list);
133 		spin_unlock(&stt_data.stt_lock);
134 
135 		expired++;
136 		(*timer->stt_func) (timer->stt_data);
137 
138 		spin_lock(&stt_data.stt_lock);
139 	}
140 
141 	return expired;
142 }
143 
144 static int
stt_check_timers(unsigned long * last)145 stt_check_timers(unsigned long *last)
146 {
147 	int expired = 0;
148 	time64_t now;
149 	unsigned long this_slot;
150 
151 	now = ktime_get_real_seconds();
152 	this_slot = now & STTIMER_SLOTTIMEMASK;
153 
154 	spin_lock(&stt_data.stt_lock);
155 
156 	while (cfs_time_aftereq(this_slot, *last)) {
157 		expired += stt_expire_list(STTIMER_SLOT(this_slot), now);
158 		this_slot = cfs_time_sub(this_slot, STTIMER_SLOTTIME);
159 	}
160 
161 	*last = now & STTIMER_SLOTTIMEMASK;
162 	spin_unlock(&stt_data.stt_lock);
163 	return expired;
164 }
165 
166 static int
stt_timer_main(void * arg)167 stt_timer_main(void *arg)
168 {
169 	int rc = 0;
170 
171 	cfs_block_allsigs();
172 
173 	while (!stt_data.stt_shuttingdown) {
174 		stt_check_timers(&stt_data.stt_prev_slot);
175 
176 		rc = wait_event_timeout(stt_data.stt_waitq,
177 					stt_data.stt_shuttingdown,
178 					cfs_time_seconds(STTIMER_SLOTTIME));
179 	}
180 
181 	spin_lock(&stt_data.stt_lock);
182 	stt_data.stt_nthreads--;
183 	spin_unlock(&stt_data.stt_lock);
184 	return rc;
185 }
186 
187 static int
stt_start_timer_thread(void)188 stt_start_timer_thread(void)
189 {
190 	struct task_struct *task;
191 
192 	LASSERT(!stt_data.stt_shuttingdown);
193 
194 	task = kthread_run(stt_timer_main, NULL, "st_timer");
195 	if (IS_ERR(task))
196 		return PTR_ERR(task);
197 
198 	spin_lock(&stt_data.stt_lock);
199 	stt_data.stt_nthreads++;
200 	spin_unlock(&stt_data.stt_lock);
201 	return 0;
202 }
203 
204 int
stt_startup(void)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 = ktime_get_real_seconds() & STTIMER_SLOTTIMEMASK;
212 
213 	spin_lock_init(&stt_data.stt_lock);
214 	for (i = 0; i < STTIMER_NSLOTS; i++)
215 		INIT_LIST_HEAD(&stt_data.stt_hash[i]);
216 
217 	stt_data.stt_nthreads = 0;
218 	init_waitqueue_head(&stt_data.stt_waitq);
219 	rc = stt_start_timer_thread();
220 	if (rc)
221 		CERROR("Can't spawn timer thread: %d\n", rc);
222 
223 	return rc;
224 }
225 
226 void
stt_shutdown(void)227 stt_shutdown(void)
228 {
229 	int i;
230 
231 	spin_lock(&stt_data.stt_lock);
232 
233 	for (i = 0; i < STTIMER_NSLOTS; i++)
234 		LASSERT(list_empty(&stt_data.stt_hash[i]));
235 
236 	stt_data.stt_shuttingdown = 1;
237 
238 	wake_up(&stt_data.stt_waitq);
239 	lst_wait_until(!stt_data.stt_nthreads, stt_data.stt_lock,
240 		       "waiting for %d threads to terminate\n",
241 		       stt_data.stt_nthreads);
242 
243 	spin_unlock(&stt_data.stt_lock);
244 }
245