Whamcloud - gitweb
LU-14487 lnet: remove references to Sun Trademark.
[fs/lustre-release.git] / lnet / selftest / timer.c
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, 2013, Intel Corporation.
27  */
28 /*
29  * This file is part of Lustre, http://www.lustre.org/
30  *
31  * lnet/selftest/timer.c
32  *
33  * Author: Isaac Huang <isaac@clusterfs.com>
34  */
35
36 #define DEBUG_SUBSYSTEM S_LNET
37
38 #include "selftest.h"
39
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         /* start time of the slot processed previously */
58         time64_t                stt_prev_slot;
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
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 != NULL);
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
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
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
145 stt_check_timers(time64_t *last)
146 {
147         int expired = 0;
148         time64_t now;
149         time64_t 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 (this_slot >= *last) {
157                 expired += stt_expire_list(STTIMER_SLOT(this_slot), now);
158                 this_slot = this_slot - STTIMER_SLOTTIME;
159         }
160
161         *last = now & STTIMER_SLOTTIMEMASK;
162         spin_unlock(&stt_data.stt_lock);
163         return expired;
164 }
165
166
167 static int
168 stt_timer_main (void *arg)
169 {
170         int rc = 0;
171
172         while (!stt_data.stt_shuttingdown) {
173                 stt_check_timers(&stt_data.stt_prev_slot);
174
175                 rc = wait_event_timeout(stt_data.stt_waitq,
176                                         stt_data.stt_shuttingdown,
177                                         cfs_time_seconds(STTIMER_SLOTTIME));
178         }
179
180         spin_lock(&stt_data.stt_lock);
181         stt_data.stt_nthreads--;
182         spin_unlock(&stt_data.stt_lock);
183         return rc;
184 }
185
186 static int
187 stt_start_timer_thread (void)
188 {
189         struct task_struct *task;
190
191         LASSERT(!stt_data.stt_shuttingdown);
192
193         task = kthread_run(stt_timer_main, NULL, "st_timer");
194         if (IS_ERR(task))
195                 return PTR_ERR(task);
196
197         spin_lock(&stt_data.stt_lock);
198         stt_data.stt_nthreads++;
199         spin_unlock(&stt_data.stt_lock);
200         return 0;
201 }
202
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 = 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 != 0)
221                 CERROR ("Can't spawn timer thread: %d\n", rc);
222
223         return rc;
224 }
225
226 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 == 0, 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 }