aboutsummaryrefslogtreecommitdiff
path: root/common/timer.c
blob: f35c983225298d387902b9cf2e21510e677269e0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include <apos/sp_tree.h>
#include <apos/string.h>
#include <apos/nodes.h>
#include <apos/utils.h>
#include <apos/timer.h>
#include <arch/timer.h>
#include <arch/cpu.h>

static ticks_t ticks_per_sec = 0;
static struct sp_root cpu_timers[MAX_CPUS] = {0};
static struct node_root node_root;

struct timer_node {
	struct sp_node sp_n;
	struct timer timer;
};

#define timer_container(ptr)\
	container_of(ptr, struct timer_node, sp_n)

#define timer_node_container(ptr)\
	container_of(ptr, struct timer_node, timer)

static struct sp_root *__cpu_timers()
{
	return &cpu_timers[cpu_id()];
}

void init_timer(const void *fdt)
{
	ticks_per_sec = stat_timer(fdt);
	init_nodes(&node_root, sizeof(struct timer_node));
}

static id_t __insert_timer(struct timer_node *ti)
{
	struct sp_root *root = __cpu_timers();
	struct sp_node *n = sp_root(root), *p = NULL;
	enum sp_dir d;
	while (n) {
		struct timer_node *t = container_of(n, struct timer_node, sp_n);
		if (ti->timer.cid == t->timer.cid) {
			/* if there's an identical ID, we'll just increment our
			 * ID until we get and ID that doesn't exist yet. There
			 * is a very small possibility that this will set a
			 * timer that's very slightly ahead of some other timer
			 * to be handled after the one that's very close, but
			 * the timescales that we're dealing with are probably
			 * tiny enough that this won't matter, even if it
			 * occurs. */
			ti->timer.cid++;
		}

		p = n;

		if (ti->timer.cid < t->timer.cid) {
			n = sp_left(n);
			d = LEFT;
		} else {
			n = sp_right(n);
			d = RIGHT;
		}
	}
	
	if (sp_root(root))
		sp_insert(&sp_root(root), p, &ti->sp_n, d);
	else
		sp_root(root) = &ti->sp_n;

	return ti->timer.cid;
}

/* ticks is absolute */
static id_t __new_timer(id_t tid, ticks_t ticks)
{
	struct timer_node *ti = (struct timer_node *)get_node(&node_root);
	ti->timer.ticks = ticks;
	/* preliminary ID, may change after actual insertion */
	ti->timer.cid = ticks;
	ti->timer.tid = tid;
	return __insert_timer(ti);
}

/* these are likely not perfectly accurate timers due to some random delay from
 * function calls etc, but probably good enough. */
id_t new_rel_timer(id_t tid, ticks_t ticks)
{
	return new_abs_timer(tid, ticks + current_ticks());
}

id_t new_abs_timer(id_t tid, ticks_t ticks)
{
	id_t id = __new_timer(tid, ticks);
	set_timer(ticks);
	return id;
}

struct timer *newest_timer()
{
	struct sp_node *t = sp_first(sp_root(__cpu_timers()));
	return &timer_container(t)->timer;
}

struct timer *find_timer(id_t cid)
{
	struct sp_node *n = sp_root(__cpu_timers());
	while (n) {
		struct timer_node *t = timer_container(n);
		if (t->timer.cid == cid)
			return &t->timer;

		if (t->timer.cid < cid)
			n = sp_left(n);
		else
			n = sp_right(n);
	}

	return 0;
}

void remove_timer(struct timer *t)
{
	if (!t)
		return;

	struct sp_node *n = &timer_node_container(t)->sp_n;
	sp_remove(&sp_root(__cpu_timers()), n);
}

ticks_t nsecs_to_ticks(tunit_t nsecs)
{
	ticks_t t = (nsecs * ticks_per_sec) / 1000000000;
	return t == 0 ? 1 : t;
}