glusterfs
375 строк · 10.0 Кб
1/*
2* linux/kernel/timer.c
3*
4* Kernel internal timers
5*
6* Copyright (C) 1991, 1992 Linus Torvalds
7*
8*/
9/*
10This program is free software; you can redistribute it and/or modify
11it under the terms of the GNU General Public License as published by
12the Free Software Foundation; either version 2 of the License, or
13(at your option) any later version.
14
15This program is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License along
21with this program; if not, write to the Free Software Foundation, Inc.,
2251 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23*/
24
25#include <stdio.h>26#include <stdlib.h>27#include <sys/time.h>28#include <sys/types.h>29#include <unistd.h>30#include <sys/select.h>31#include <pthread.h>32
33#include "timer-wheel.h"34
35static inline void36__gf_tw_add_timer (struct tvec_base *base, struct gf_tw_timer_list *timer)37{
38int i;39unsigned long idx;40unsigned long expires;41struct list_head *vec;42
43expires = timer->expires;44
45idx = expires - base->timer_sec;46
47if (idx < TVR_SIZE) {48i = expires & TVR_MASK;49vec = base->tv1.vec + i;50} else if (idx < 1 << (TVR_BITS + TVN_BITS)) {51i = (expires >> TVR_BITS) & TVN_MASK;52vec = base->tv2.vec + i;53} else if (idx < 1 << (TVR_BITS + 2*TVN_BITS)) {54i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;55vec = base->tv3.vec + i;56} else if (idx < 1 << (TVR_BITS + 3*TVN_BITS)) {57i = (expires >> (TVR_BITS + 2*TVN_BITS)) & TVN_MASK;58vec = base->tv4.vec + i;59} else if (idx < 0) {60vec = base->tv1.vec + (base->timer_sec & TVR_MASK);61} else {62i = (expires >> (TVR_BITS + 3*TVN_BITS)) & TVN_MASK;63vec = base->tv5.vec + i;64}65
66list_add_tail (&timer->entry, vec);67}
68
69unsigned long gf_tw_find_last_bit(const unsigned long *, unsigned long);70
71#if defined(__GNUC__) || defined(__clang__)72static inline unsigned long gf_tw_fls (unsigned long word)73{
74return BITS_PER_LONG - __builtin_clzl(word);75}
76#else77extern unsigned long gf_tw_fls (unsigned long);78#endif79
80static inline unsigned long81apply_slack(struct tvec_base *base, struct gf_tw_timer_list *timer)82{
83long delta;84unsigned long mask, expires, expires_limit;85
86expires = timer->expires;87
88delta = expires - base->timer_sec;89if (delta < 256)90return expires;91
92expires_limit = expires + delta / 256;93mask = expires ^ expires_limit;94if (mask == 0)95return expires;96
97int bit = gf_tw_fls (mask);98mask = (1UL << bit) - 1;99
100expires_limit = expires_limit & ~(mask);101return expires_limit;102}
103
104static inline void105__gf_tw_detach_timer (struct gf_tw_timer_list *timer)106{
107struct list_head *entry = &timer->entry;108
109list_del (entry);110entry->next = NULL;111}
112
113static inline int114cascade (struct tvec_base *base, struct tvec *tv, int index)115{
116struct gf_tw_timer_list *timer, *tmp;117struct list_head tv_list;118
119list_replace_init (tv->vec + index, &tv_list);120
121list_for_each_entry_safe (timer, tmp, &tv_list, entry) {122__gf_tw_add_timer (base, timer);123}124
125return index;126}
127
128#define INDEX(N) ((base->timer_sec >> (TVR_BITS + N * TVN_BITS)) & TVN_MASK)129
130/**
131* run expired timers
132*/
133static inline void134run_timers (struct tvec_base *base)135{
136unsigned long index, call_time;137struct gf_tw_timer_list *timer;138
139struct list_head work_list;140struct list_head *head = &work_list;141
142pthread_spin_lock (&base->lock);143{144index = base->timer_sec & TVR_MASK;145
146if (!index &&147(!cascade (base, &base->tv2, INDEX(0))) &&148(!cascade (base, &base->tv3, INDEX(1))) &&149(!cascade (base, &base->tv4, INDEX(2))))150cascade (base, &base->tv5, INDEX(3));151
152call_time = base->timer_sec++;153list_replace_init (base->tv1.vec + index, head);154while (!list_empty(head)) {155void (*fn)(struct gf_tw_timer_list *, void *, unsigned long);156void *data;157
158timer = list_first_entry (head, struct gf_tw_timer_list, entry);159fn = timer->function;160data = timer->data;161
162__gf_tw_detach_timer (timer);163pthread_spin_unlock(&base->lock);164{165/* It is required to run the actual function outside166of the locked zone, so we don't bother about
167locked operations inside that function */
168fn(timer, data, call_time);169}170pthread_spin_lock(&base->lock);171}172}173pthread_spin_unlock (&base->lock);174
175}
176
177void *runner (void *arg)178{
179struct timeval tv = {0,};180struct tvec_base *base = arg;181
182while (1) {183run_timers (base);184
185tv.tv_sec = 1;186tv.tv_usec = 0;187(void) select (0, NULL, NULL, NULL, &tv);188}189
190return NULL;191
192}
193
194static inline int timer_pending (struct gf_tw_timer_list *timer)195{
196struct list_head *entry = &timer->entry;197
198return (entry->next != NULL);199}
200
201static inline int __detach_if_pending (struct gf_tw_timer_list *timer)202{
203if (!timer_pending (timer))204return 0;205
206__gf_tw_detach_timer (timer);207return 1;208}
209
210static inline int __mod_timer (struct tvec_base *base,211struct gf_tw_timer_list *timer, int pending_only)212{
213int ret = 0;214
215ret = __detach_if_pending (timer);216if (!ret && pending_only)217goto done;218
219ret = 1;220__gf_tw_add_timer (base, timer);221
222done:223return ret;224}
225
226/* interface */
227
228/**
229* Add a timer in the timer wheel
230*/
231void gf_tw_add_timer (struct tvec_base *base, struct gf_tw_timer_list *timer)232{
233pthread_spin_lock (&base->lock);234{235timer->expires += base->timer_sec;236timer->expires = apply_slack (base, timer);237__gf_tw_add_timer (base, timer);238}239pthread_spin_unlock (&base->lock);240}
241
242/**
243* Remove a timer from the timer wheel
244*/
245int gf_tw_del_timer (struct tvec_base *base, struct gf_tw_timer_list *timer)246{
247int ret = 0;248
249pthread_spin_lock (&base->lock);250{251if (timer_pending (timer)) {252ret = 1;253__gf_tw_detach_timer (timer);254}255}256pthread_spin_unlock (&base->lock);257
258return ret;259}
260
261int gf_tw_mod_timer_pending (struct tvec_base *base,262struct gf_tw_timer_list *timer,263unsigned long expires)264{
265int ret = 1;266
267pthread_spin_lock (&base->lock);268{269timer->expires = expires + base->timer_sec;270timer->expires = apply_slack (base, timer);271
272ret = __mod_timer (base, timer, 1);273}274pthread_spin_unlock (&base->lock);275
276return ret;277}
278
279int gf_tw_mod_timer (struct tvec_base *base,280struct gf_tw_timer_list *timer, unsigned long expires)281{
282int ret = 1;283
284pthread_spin_lock (&base->lock);285{286/* fast path optimization */287if (timer_pending (timer) && timer->expires == expires)288goto unblock;289
290timer->expires = expires + base->timer_sec;291timer->expires = apply_slack (base, timer);292
293ret = __mod_timer (base, timer, 0);294}295unblock:296pthread_spin_unlock (&base->lock);297
298return ret;299}
300
301int gf_tw_cleanup_timers (struct tvec_base *base)302{
303int ret = 0;304void *res = NULL;305
306/* terminate runner */307ret = pthread_cancel (base->runner);308if (ret != 0)309goto error_return;310ret = pthread_join (base->runner, &res);311if (ret != 0)312goto error_return;313if (res != PTHREAD_CANCELED)314goto error_return;315
316/* destroy lock */317ret = pthread_spin_destroy (&base->lock);318if (ret != 0)319goto error_return;320
321/* deallocated timer base */322free (base);323return 0;324
325error_return:326return -1;327}
328
329/**
330* Initialize various timer wheel lists and spawn a thread that
331* invokes run_timers()
332*/
333struct tvec_base *gf_tw_init_timers (void)334{
335int j = 0;336int ret = 0;337struct timeval tv = {0,};338struct tvec_base *base = NULL;339
340base = malloc (sizeof (*base));341if (!base)342goto error_return;343
344ret = pthread_spin_init (&base->lock, 0);345if (ret != 0)346goto error_dealloc;347
348for (j = 0; j < TVN_SIZE; j++) {349INIT_LIST_HEAD (base->tv5.vec + j);350INIT_LIST_HEAD (base->tv4.vec + j);351INIT_LIST_HEAD (base->tv3.vec + j);352INIT_LIST_HEAD (base->tv2.vec + j);353}354
355for (j = 0; j < TVR_SIZE; j++) {356INIT_LIST_HEAD (base->tv1.vec + j);357}358
359ret = gettimeofday (&tv, 0);360if (ret < 0)361goto destroy_lock;362base->timer_sec = tv.tv_sec;363
364ret = pthread_create (&base->runner, NULL, runner, base);365if (ret != 0)366goto destroy_lock;367return base;368
369destroy_lock:370(void) pthread_spin_destroy (&base->lock);371error_dealloc:372free (base);373error_return:374return NULL;375}
376