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