aboutsummaryrefslogtreecommitdiff
path: root/common/timer.c
blob: f0088519047a27eddf3af168c5aa8ca0c894d087 (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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/**
 * @file timer.c
 * Timer handling implementation. Currently we only expect an architecture to
 * support a single timer per core.
 *
 * By keeping all timers in a binary search
 * tree ordered by time, we can just set the single timer to interrupt us when
 * the next timer is due and with thread info call the thread that set the
 * timer. From what I can tell, this is largely what Linux does.
 *
 * \todo Figure out if there are any advantages to having multiple concurrent
 * timers.
 */

#include <apos/sp_tree.h>
#include <apos/string.h>
#include <apos/nodes.h>
#include <apos/utils.h>
#include <apos/timer.h>
#include <apos/debug.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);
	info("ticks_per_sec: %" PRIu64 "\n", ticks_per_sec);
	info("current ticks: %" PRIu64 "\n", current_ticks());
	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 = LEFT;
	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;
}

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

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

	return OK;
}

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

/* call to this function from exception handlers */
void update_timers()
{
	struct timer *t = newest_timer();
	remove_timer(t);

	/** \todo handle timer thread ID */
}