1/* 2 * (C) 2006-2008 by Pablo Neira Ayuso <pablo@netfilter.org> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 */ 18 19#include "alarm.h" 20#include "date.h" 21#include <stdlib.h> 22#include <limits.h> 23 24static struct rb_root alarm_root = RB_ROOT; 25 26void init_alarm(struct alarm_block *t, 27 void *data, 28 void (*fcn)(struct alarm_block *a, void *data)) 29{ 30 /* initialize the head to check whether a node is inserted */ 31 RB_CLEAR_NODE(&t->node); 32 timerclear(&t->tv); 33 t->data = data; 34 t->function = fcn; 35} 36 37static void __add_alarm(struct alarm_block *alarm) 38{ 39 struct rb_node **new = &(alarm_root.rb_node); 40 struct rb_node *parent = NULL; 41 42 while (*new) { 43 struct alarm_block *this; 44 45 this = container_of(*new, struct alarm_block, node); 46 47 parent = *new; 48 if (timercmp(&alarm->tv, &this->tv, <)) 49 new = &((*new)->rb_left); 50 else 51 new = &((*new)->rb_right); 52 } 53 54 rb_link_node(&alarm->node, parent, new); 55 rb_insert_color(&alarm->node, &alarm_root); 56} 57 58void add_alarm(struct alarm_block *alarm, unsigned long sc, unsigned long usc) 59{ 60 struct timeval tv; 61 62 del_alarm(alarm); 63 alarm->tv.tv_sec = sc; 64 alarm->tv.tv_usec = usc; 65 gettimeofday_cached(&tv); 66 timeradd(&alarm->tv, &tv, &alarm->tv); 67 __add_alarm(alarm); 68} 69 70void del_alarm(struct alarm_block *alarm) 71{ 72 /* don't remove a non-inserted node */ 73 if (!RB_EMPTY_NODE(&alarm->node)) { 74 rb_erase(&alarm->node, &alarm_root); 75 RB_CLEAR_NODE(&alarm->node); 76 } 77} 78 79int alarm_pending(struct alarm_block *alarm) 80{ 81 if (RB_EMPTY_NODE(&alarm->node)) 82 return 0; 83 84 return 1; 85} 86 87static struct timeval * 88calculate_next_run(struct timeval *cand, 89 struct timeval *tv, 90 struct timeval *next_run) 91{ 92 if (cand->tv_sec != LONG_MAX) { 93 if (timercmp(cand, tv, >)) 94 timersub(cand, tv, next_run); 95 else { 96 /* loop again inmediately */ 97 next_run->tv_sec = 0; 98 next_run->tv_usec = 0; 99 } 100 return next_run; 101 } 102 return NULL; 103} 104 105struct timeval * 106get_next_alarm_run(struct timeval *next_run) 107{ 108 struct rb_node *node; 109 struct timeval tv; 110 111 gettimeofday_cached(&tv); 112 113 node = rb_first(&alarm_root); 114 if (node) { 115 struct alarm_block *this; 116 this = container_of(node, struct alarm_block, node); 117 return calculate_next_run(&this->tv, &tv, next_run); 118 } 119 return NULL; 120} 121 122struct timeval * 123do_alarm_run(struct timeval *next_run) 124{ 125 struct list_head alarm_run_queue; 126 struct rb_node *node; 127 struct alarm_block *this, *tmp; 128 struct timeval tv; 129 130 gettimeofday_cached(&tv); 131 132 INIT_LIST_HEAD(&alarm_run_queue); 133 for (node = rb_first(&alarm_root); node; node = rb_next(node)) { 134 this = container_of(node, struct alarm_block, node); 135 136 if (timercmp(&this->tv, &tv, >)) 137 break; 138 139 list_add(&this->list, &alarm_run_queue); 140 } 141 142 /* must be safe as entries can vanish from the callback */ 143 list_for_each_entry_safe(this, tmp, &alarm_run_queue, list) { 144 rb_erase(&this->node, &alarm_root); 145 RB_CLEAR_NODE(&this->node); 146 this->function(this, this->data); 147 } 148 149 return get_next_alarm_run(next_run); 150} 151