OpenVPN
schedule.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single TCP/UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2002-2026 OpenVPN Inc <sales@openvpn.net>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, see <https://www.gnu.org/licenses/>.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "syshead.h"
28
29#include "buffer.h"
30#include "misc.h"
31#include "crypto.h"
32#include "schedule.h"
33
34#include "memdbg.h"
35
36#ifdef ENABLE_DEBUG
37static void
38schedule_entry_debug_info(const char *caller, const struct schedule_entry *e)
39{
40 struct gc_arena gc = gc_new();
41 if (e)
42 {
43 dmsg(D_SCHEDULER, "SCHEDULE: %s wakeup=[%s] pri=%u", caller, tv_string_abs(&e->tv, &gc),
44 e->pri);
45 }
46 else
47 {
48 dmsg(D_SCHEDULER, "SCHEDULE: %s NULL", caller);
49 }
50 gc_free(&gc);
51}
52#endif
53
54static inline void
56{
57 e->pri = (unsigned int)random();
58 if (e->pri < 1)
59 {
60 e->pri = 1;
61 }
62}
63
64int
65schedule_entry_compare(const struct schedule_entry *e1, const struct schedule_entry *e2)
66{
67 if (e1->tv.tv_sec < e2->tv.tv_sec)
68 {
69 return -1;
70 }
71 else if (e1->tv.tv_sec > e2->tv.tv_sec)
72 {
73 return 1;
74 }
75 else
76 {
77 if (e1->tv.tv_usec < e2->tv.tv_usec)
78 {
79 return -1;
80 }
81 else if (e1->tv.tv_usec > e2->tv.tv_usec)
82 {
83 return 1;
84 }
85 else
86 {
87 if (e1->pri < e2->pri)
88 {
89 return -1;
90 }
91 else if (e1->pri > e2->pri)
92 {
93 return 1;
94 }
95 else
96 {
97 return 0;
98 }
99 }
100 }
101}
102
103/*
104 * Detach a btree node from its parent
105 */
106static inline void
108{
109 if (e)
110 {
111 if (e->parent)
112 {
113 if (e->parent->lt == e)
114 {
115 e->parent->lt = NULL;
116 }
117 else if (e->parent->gt == e)
118 {
119 e->parent->gt = NULL;
120 }
121 else
122 {
123 /* parent <-> child linkage is corrupted */
124 ASSERT(0);
125 }
126 e->parent = NULL;
127 }
128 else
129 {
130 if (s->root == e) /* last element deleted, tree is empty */
131 {
132 s->root = NULL;
133 }
134 }
135 }
136}
137
138/*
139 *
140 * Given a binary search tree, move a node toward the root
141 * while still maintaining the correct ordering relationships
142 * within the tree. This function is the workhorse
143 * of the tree balancer.
144 *
145 * This code will break on key collisions, which shouldn't
146 * happen because the treap priority is considered part of the key
147 * and is guaranteed to be unique.
148 */
149static void
151{
152 if (e && e->parent)
153 {
154 struct schedule_entry *lt = e->lt;
155 struct schedule_entry *gt = e->gt;
156 struct schedule_entry *p = e->parent;
157 struct schedule_entry *gp = p->parent;
158
159 if (gp) /* if grandparent exists, modify its child link */
160 {
161 if (gp->gt == p)
162 {
163 gp->gt = e;
164 }
165 else if (gp->lt == p)
166 {
167 gp->lt = e;
168 }
169 else
170 {
171 ASSERT(0);
172 }
173 }
174 else /* no grandparent, now we are the root */
175 {
176 s->root = e;
177 }
178
179 /* grandparent is now our parent */
180 e->parent = gp;
181
182 /* parent is now our child */
183 p->parent = e;
184
185 /* reorient former parent's links
186 * to reflect new position in the tree */
187 if (p->gt == e)
188 {
189 e->lt = p;
190 p->gt = lt;
191 if (lt)
192 {
193 lt->parent = p;
194 }
195 }
196 else if (p->lt == e)
197 {
198 e->gt = p;
199 p->lt = gt;
200 if (gt)
201 {
202 gt->parent = p;
203 }
204 }
205 else
206 {
207 /* parent <-> child linkage is corrupted */
208 ASSERT(0);
209 }
210 }
211}
212
213/*
214 * This is the treap deletion algorithm:
215 *
216 * Rotate lesser-priority children up in the tree
217 * until we are childless. Then delete.
218 */
219void
221{
222 while (e->lt || e->gt)
223 {
224 if (e->lt)
225 {
226 if (e->gt)
227 {
228 if (e->lt->pri < e->gt->pri)
229 {
230 schedule_rotate_up(s, e->lt);
231 }
232 else
233 {
234 schedule_rotate_up(s, e->gt);
235 }
236 }
237 else
238 {
239 schedule_rotate_up(s, e->lt);
240 }
241 }
242 else if (e->gt)
243 {
244 schedule_rotate_up(s, e->gt);
245 }
246 }
247
249 e->pri = 0;
250}
251
252/*
253 * Trivially add a node to a binary search tree without
254 * regard for balance.
255 */
256static void
258{
259 struct schedule_entry *c = s->root;
260 while (true)
261 {
262 const int comp = schedule_entry_compare(e, c);
263
264 if (comp == -1)
265 {
266 if (c->lt)
267 {
268 c = c->lt;
269 continue;
270 }
271 else
272 {
273 c->lt = e;
274 e->parent = c;
275 break;
276 }
277 }
278 else if (comp == 1)
279 {
280 if (c->gt)
281 {
282 c = c->gt;
283 continue;
284 }
285 else
286 {
287 c->gt = e;
288 e->parent = c;
289 break;
290 }
291 }
292 else
293 {
294 /* rare key/priority collision -- no big deal,
295 * just choose another priority and retry */
297 /* msg (M_INFO, "PRI COLLISION pri=%u", e->pri); */
298 c = s->root;
299 continue;
300 }
301 }
302}
303
304/*
305 * Given an element, remove it from the btree if it's already
306 * there and re-insert it based on its current key.
307 */
308void
310{
311#ifdef ENABLE_DEBUG
313 {
314 schedule_entry_debug_info("schedule_add_modify", e);
315 }
316#endif
317
318 /* already in tree, remove */
319 if (IN_TREE(e))
320 {
322 }
323
324 /* set random priority */
326
327 if (s->root)
328 {
329 schedule_insert(s, e); /* trivial insert into tree */
330 }
331 else
332 {
333 s->root = e; /* tree was empty, we are the first element */
334 }
335 /* This is the magic of the randomized treap algorithm which
336 * keeps the tree balanced. Move the node up the tree until
337 * its own priority is greater than that of its parent */
338 while (e->parent && e->parent->pri > e->pri)
339 {
340 schedule_rotate_up(s, e);
341 }
342}
343
344/*
345 * Find the earliest event to be scheduled
346 */
347struct schedule_entry *
349{
350 if (e)
351 {
352 while (e->lt)
353 {
354 e = e->lt;
355 }
356 }
357
358#ifdef ENABLE_DEBUG
360 {
361 schedule_entry_debug_info("schedule_find_least", e);
362 }
363#endif
364
365 return e;
366}
367
368/*
369 * Public functions below this point
370 */
371
372struct schedule *
374{
375 struct schedule *s;
376
377 ALLOC_OBJ_CLEAR(s, struct schedule);
378 return s;
379}
380
381void
383{
384 free(s);
385}
386
387void
389{
390 s->earliest_wakeup = NULL; /* invalidate cache */
392}
Buffer management functions and garbage collection.
static void gc_free(struct gc_arena *a)
Free all allocations in a garbage collection arena.
Definition buffer.h:1909
#define ALLOC_OBJ_CLEAR(dptr, type)
Allocate and zero-initialise memory for a single object of the given type.
Definition buffer.h:1971
static struct gc_arena gc_new(void)
Allocate and return a new, empty garbage collection arena.
Definition buffer.h:1893
Data Channel Cryptography Module.
#define D_SCHEDULER
Definition errlevel.h:158
static bool check_debug_level(msglvl_t level)
Definition error.h:251
#define dmsg(flags,...)
Definition error.h:172
#define ASSERT(x)
Definition error.h:219
const char * tv_string_abs(const struct timeval *tv, struct gc_arena *gc)
Definition otime.c:96
static void schedule_set_pri(struct schedule_entry *e)
Definition schedule.c:55
static void schedule_rotate_up(struct schedule *s, struct schedule_entry *e)
Definition schedule.c:150
void schedule_remove_entry(struct schedule *s, struct schedule_entry *e)
Definition schedule.c:388
struct schedule * schedule_init(void)
Definition schedule.c:373
void schedule_remove_node(struct schedule *s, struct schedule_entry *e)
Definition schedule.c:220
struct schedule_entry * schedule_find_least(struct schedule_entry *e)
Definition schedule.c:348
int schedule_entry_compare(const struct schedule_entry *e1, const struct schedule_entry *e2)
This method compares two schedule entries and return which one is earlier,later or equal.
Definition schedule.c:65
void schedule_add_modify(struct schedule *s, struct schedule_entry *e)
Definition schedule.c:309
static void schedule_insert(struct schedule *s, struct schedule_entry *e)
Definition schedule.c:257
void schedule_free(struct schedule *s)
Definition schedule.c:382
static void schedule_detach_parent(struct schedule *s, struct schedule_entry *e)
Definition schedule.c:107
#define IN_TREE(e)
Definition schedule.h:66
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:127
Definition schedule.h:41
unsigned int pri
Definition schedule.h:43
struct timeval tv
Definition schedule.h:42
struct schedule_entry * lt
Definition schedule.h:45
struct schedule_entry * parent
Definition schedule.h:44
struct schedule_entry * gt
Definition schedule.h:46
struct schedule_entry * earliest_wakeup
Definition schedule.h:51
struct schedule_entry * root
Definition schedule.h:52
#define random
Definition syshead.h:43
struct gc_arena gc
Definition test_ssl.c:135