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