GNU Linux-libre 4.14.259-gnu1
[releases.git] / drivers / staging / lustre / 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, 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        BIT(STTIMER_MINPOLL)
50 #define STTIMER_SLOTTIMEMASK   (~(STTIMER_SLOTTIME - 1))
51 #define STTIMER_NSLOTS          BIT(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                                           */
60         struct list_head  stt_hash[STTIMER_NSLOTS];
61         int               stt_shuttingdown;
62         wait_queue_head_t stt_waitq;
63         int               stt_nthreads;
64 } stt_data;
65
66 void
67 stt_add_timer(struct stt_timer *timer)
68 {
69         struct list_head *pos;
70
71         spin_lock(&stt_data.stt_lock);
72
73         LASSERT(stt_data.stt_nthreads > 0);
74         LASSERT(!stt_data.stt_shuttingdown);
75         LASSERT(timer->stt_func);
76         LASSERT(list_empty(&timer->stt_list));
77         LASSERT(timer->stt_expires > ktime_get_real_seconds());
78
79         /* a simple insertion sort */
80         list_for_each_prev(pos, STTIMER_SLOT(timer->stt_expires)) {
81                 struct stt_timer *old = list_entry(pos, struct stt_timer,
82                                                    stt_list);
83
84                 if (timer->stt_expires >= old->stt_expires)
85                         break;
86         }
87         list_add(&timer->stt_list, pos);
88
89         spin_unlock(&stt_data.stt_lock);
90 }
91
92 /*
93  * The function returns whether it has deactivated a pending timer or not.
94  * (ie. del_timer() of an inactive timer returns 0, del_timer() of an
95  * active timer returns 1.)
96  *
97  * CAVEAT EMPTOR:
98  * When 0 is returned, it is possible that timer->stt_func _is_ running on
99  * another CPU.
100  */
101 int
102 stt_del_timer(struct stt_timer *timer)
103 {
104         int ret = 0;
105
106         spin_lock(&stt_data.stt_lock);
107
108         LASSERT(stt_data.stt_nthreads > 0);
109         LASSERT(!stt_data.stt_shuttingdown);
110
111         if (!list_empty(&timer->stt_list)) {
112                 ret = 1;
113                 list_del_init(&timer->stt_list);
114         }
115
116         spin_unlock(&stt_data.stt_lock);
117         return ret;
118 }
119
120 /* called with stt_data.stt_lock held */
121 static int
122 stt_expire_list(struct list_head *slot, time64_t now)
123 {
124         int expired = 0;
125         struct stt_timer *timer;
126
127         while (!list_empty(slot)) {
128                 timer = list_entry(slot->next, struct stt_timer, stt_list);
129
130                 if (timer->stt_expires > now)
131                         break;
132
133                 list_del_init(&timer->stt_list);
134                 spin_unlock(&stt_data.stt_lock);
135
136                 expired++;
137                 (*timer->stt_func) (timer->stt_data);
138
139                 spin_lock(&stt_data.stt_lock);
140         }
141
142         return expired;
143 }
144
145 static int
146 stt_check_timers(unsigned long *last)
147 {
148         int expired = 0;
149         time64_t now;
150         unsigned long this_slot;
151
152         now = ktime_get_real_seconds();
153         this_slot = now & STTIMER_SLOTTIMEMASK;
154
155         spin_lock(&stt_data.stt_lock);
156
157         while (cfs_time_aftereq(this_slot, *last)) {
158                 expired += stt_expire_list(STTIMER_SLOT(this_slot), now);
159                 this_slot = cfs_time_sub(this_slot, STTIMER_SLOTTIME);
160         }
161
162         *last = now & STTIMER_SLOTTIMEMASK;
163         spin_unlock(&stt_data.stt_lock);
164         return expired;
165 }
166
167 static int
168 stt_timer_main(void *arg)
169 {
170         int rc = 0;
171
172         cfs_block_allsigs();
173
174         while (!stt_data.stt_shuttingdown) {
175                 stt_check_timers(&stt_data.stt_prev_slot);
176
177                 rc = 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 rc;
186 }
187
188 static int
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
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)
222                 CERROR("Can't spawn timer thread: %d\n", rc);
223
224         return rc;
225 }
226
227 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, 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 }