Implement timerfd on top of #alarm (XCC)
[akaros.git] / tools / compilers / gcc-glibc / glibc-2.19-akaros / sysdeps / akaros / timerfd.c
1 /* Copyright (c) 2016 Google Inc.
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * Implementation of glibc's timerfd interface on top of #alarm.
6  *
7  * Like sockets, timerfd is really an alarm directory under the hood, but the
8  * user gets a single FD that they will read and epoll on.  This FD will be for
9  * the 'count' file.  Other operations will require opening other FDs, given the
10  * 'count' fd.  This is basically the Rock lookup problem. */
11
12 #include <sys/timerfd.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <syscall.h>
20 #include <string.h>
21 #include <parlib/timing.h>
22 #include <time.h>
23 #include <sys/plan9_helpers.h>
24
25 int timerfd_create(int clockid, int flags)
26 {
27         int ctlfd, countfd, ret;
28         char id[20];
29         char path[MAX_PATH_LEN];
30         int count_oflags = O_RDWR;
31
32         ctlfd = open("#alarm/clone", O_RDWR);
33         if (ctlfd < 0)
34                 return -1;
35         /* TODO: if we want to support clocks like CLOCK_REALTIME, do it here,
36          * either at attach time (with a .spec), or with a ctl message. */
37         /* fd2path doesn't work on cloned files, so open the count manually. */
38         ret = read(ctlfd, id, sizeof(id) - 1);
39         if (ret <= 0)
40                 return -1;
41         id[ret] = 0;
42         snprintf(path, sizeof(path), "#alarm/a%s/count", id);
43         count_oflags |= (flags & TFD_NONBLOCK ? O_NONBLOCK : 0);
44         count_oflags |= (flags & TFD_CLOEXEC ? O_CLOEXEC : 0);
45         countfd = open(path, count_oflags);
46         close(ctlfd);
47         return countfd;
48 }
49
50 static int set_period(int periodfd, uint64_t period)
51 {
52         return write_hex_to_fd(periodfd, period);
53 }
54
55 static int set_timer(int timerfd, uint64_t abs_ticks)
56 {
57         return write_hex_to_fd(timerfd, abs_ticks);
58 }
59
60 /* Helper: adds normal timespecs */
61 static void add_timespecs(struct timespec *sum, const struct timespec *x,
62                           const struct timespec *y)
63 {
64         bool plus_one = FALSE;
65
66         sum->tv_nsec = x->tv_nsec + y->tv_nsec;
67         /* Overflow detection */
68         if (sum->tv_nsec / 1000000000) {
69                 sum->tv_nsec -= 1000000000;
70                 plus_one = TRUE;
71         }
72         sum->tv_sec = x->tv_sec + y->tv_sec + (plus_one ? 1 : 0);
73 }
74
75 /* Helper: subtracts normal timespecs */
76 static void sub_timespecs(struct timespec *diff, const struct timespec *minuend,
77                           const struct timespec *subtrahend)
78 {
79         unsigned long borrow_amt = 0;
80
81         if (minuend->tv_nsec < subtrahend->tv_nsec)
82                 borrow_amt = 1000000000;
83         diff->tv_nsec = borrow_amt + minuend->tv_nsec - subtrahend->tv_nsec;
84         diff->tv_sec = minuend->tv_sec - subtrahend->tv_sec - (borrow_amt ? 1 : 0);
85 }
86
87 static uint64_t timespec2tsc(const struct timespec *ts)
88 {
89         return nsec2tsc(ts->tv_sec * 1000000000ULL + ts->tv_nsec);
90 }
91
92 static void tsc2timespec(uint64_t tsc, struct timespec *ts)
93 {
94         uint64_t nsec = tsc2nsec(tsc);
95
96         ts->tv_sec = nsec / 1000000000;
97         ts->tv_nsec = nsec % 1000000000;
98 }
99
100 static int __timerfd_gettime(int timerfd, int periodfd,
101                              struct itimerspec *curr_value)
102 {
103         char buf[20];
104         uint64_t timer_tsc, now_tsc, period_tsc;
105
106         if (read(periodfd, buf, sizeof(buf) <= 0))
107                 return -1;
108         period_tsc = strtoul(buf, 0, 0);
109         tsc2timespec(period_tsc, &curr_value->it_interval);
110         if (read(timerfd, buf, sizeof(buf) <= 0))
111                 return -1;
112         timer_tsc = strtoul(buf, 0, 0);
113         /* If 0 (disabled), we'll return 0 for 'it_value'.  o/w we need to return
114          * the relative time. */
115         if (timer_tsc) {
116                 now_tsc = read_tsc();
117                 if (timer_tsc > now_tsc) {
118                         timer_tsc -= now_tsc;
119                 } else {
120                         /* it's possible that timer_tsc is in the past, and that we lost the
121                          * race.  The alarm fired since we looked at it, and it might be
122                          * disabled.  It might have fired multiple times too. */
123                         if (!period_tsc) {
124                                 /* if there was no period and the alarm fired, then it should be
125                                  * disabled.  This is racy, if there are other people setting
126                                  * the timer. */
127                                 timer_tsc = 0;
128                         } else {
129                                 while (timer_tsc < now_tsc)
130                                         timer_tsc += period_tsc;
131                         }
132                 }
133         }
134         tsc2timespec(timer_tsc, &curr_value->it_value);
135         return 0;
136 }
137
138 int timerfd_settime(int fd, int flags,
139                     const struct itimerspec *new_value,
140                     struct itimerspec *old_value)
141 {
142         int timerfd, periodfd;
143         int ret;
144         uint64_t period;
145         struct timespec now_timespec = {0};
146         struct timespec rel_timespec;
147
148         timerfd = get_sibling_fd(fd, "timer");
149         if (timerfd < 0)
150                 return -1;
151         periodfd = get_sibling_fd(fd, "period");
152         if (periodfd < 0) {
153                 close(timerfd);
154                 return -1;
155         }
156         if (old_value) {
157                 if (__timerfd_gettime(timerfd, periodfd, old_value)) {
158                         ret = -1;
159                         goto out;
160                 }
161         }
162         if (!new_value->it_value.tv_sec && !new_value->it_value.tv_nsec) {
163                 ret = set_timer(timerfd, 0);
164                 goto out;
165         }
166         period = timespec2tsc(&new_value->it_interval);
167         ret = set_period(periodfd, period);
168         if (ret < 0)
169                 goto out;
170         /* So the caller is asking for timespecs in wall-clock time (depending on
171          * the clock, actually, (TODO)), and the kernel expects TSC ticks from boot.
172          * If !ABSTIME, then it's just relative to now.  If it is ABSTIME, then they
173          * are asking in terms of real-world time, which means ABS - NOW to get the
174          * rel time, then convert to tsc ticks. */
175         if (flags & TFD_TIMER_ABSTIME) {
176                 ret = clock_gettime(CLOCK_MONOTONIC, &now_timespec);
177                 if (ret < 0)
178                         goto out;
179                 sub_timespecs(&rel_timespec, &new_value->it_value, &now_timespec);
180         } else {
181                 rel_timespec = new_value->it_value;
182         }
183         ret = set_timer(timerfd, timespec2tsc(&rel_timespec) + read_tsc());
184         /* fall-through */
185 out:
186         close(timerfd);
187         close(periodfd);
188         return ret;
189 }
190
191 int timerfd_gettime(int fd, struct itimerspec *curr_value)
192 {
193         int timerfd, periodfd;
194         int ret;
195
196         timerfd = get_sibling_fd(fd, "timer");
197         if (timerfd < 0)
198                 return -1;
199         periodfd = get_sibling_fd(fd, "period");
200         if (periodfd < 0) {
201                 close(timerfd);
202                 return -1;
203         }
204         ret = __timerfd_gettime(timerfd, periodfd, curr_value);
205         close(timerfd);
206         close(periodfd);
207         return ret;
208 }