kconfig: use pkg-config for ncurses detection
[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 static uint64_t timespec2tsc(const struct timespec *ts)
61 {
62         return nsec2tsc(ts->tv_sec * 1000000000ULL + ts->tv_nsec);
63 }
64
65 static void tsc2timespec(uint64_t tsc, struct timespec *ts)
66 {
67         uint64_t nsec = tsc2nsec(tsc);
68
69         ts->tv_sec = nsec / 1000000000;
70         ts->tv_nsec = nsec % 1000000000;
71 }
72
73 static int __timerfd_gettime(int timerfd, int periodfd,
74                              struct itimerspec *curr_value)
75 {
76         char buf[20];
77         uint64_t timer_tsc, now_tsc, period_tsc;
78
79         if (read(periodfd, buf, sizeof(buf) <= 0))
80                 return -1;
81         period_tsc = strtoul(buf, 0, 0);
82         tsc2timespec(period_tsc, &curr_value->it_interval);
83         if (read(timerfd, buf, sizeof(buf) <= 0))
84                 return -1;
85         timer_tsc = strtoul(buf, 0, 0);
86         /* If 0 (disabled), we'll return 0 for 'it_value'.  o/w we need to
87          * return the relative time. */
88         if (timer_tsc) {
89                 now_tsc = read_tsc();
90                 if (timer_tsc > now_tsc) {
91                         timer_tsc -= now_tsc;
92                 } else {
93                         /* it's possible that timer_tsc is in the past, and that
94                          * we lost the race.  The alarm fired since we looked at
95                          * it, and it might be disabled.  It might have fired
96                          * multiple times too. */
97                         if (!period_tsc) {
98                                 /* if there was no period and the alarm fired,
99                                  * then it should be disabled.  This is racy, if
100                                  * there are other people setting the timer. */
101                                 timer_tsc = 0;
102                         } else {
103                                 while (timer_tsc < now_tsc)
104                                         timer_tsc += period_tsc;
105                         }
106                 }
107         }
108         tsc2timespec(timer_tsc, &curr_value->it_value);
109         return 0;
110 }
111
112 int timerfd_settime(int fd, int flags,
113                     const struct itimerspec *new_value,
114                     struct itimerspec *old_value)
115 {
116         int timerfd, periodfd;
117         int ret;
118         uint64_t period;
119         struct timespec now_timespec = {0};
120         struct timespec rel_timespec;
121
122         timerfd = get_sibling_fd(fd, "timer");
123         if (timerfd < 0)
124                 return -1;
125         periodfd = get_sibling_fd(fd, "period");
126         if (periodfd < 0) {
127                 close(timerfd);
128                 return -1;
129         }
130         if (old_value) {
131                 if (__timerfd_gettime(timerfd, periodfd, old_value)) {
132                         ret = -1;
133                         goto out;
134                 }
135         }
136         if (!new_value->it_value.tv_sec && !new_value->it_value.tv_nsec) {
137                 ret = set_timer(timerfd, 0);
138                 goto out;
139         }
140         period = timespec2tsc(&new_value->it_interval);
141         ret = set_period(periodfd, period);
142         if (ret < 0)
143                 goto out;
144         /* So the caller is asking for timespecs in wall-clock time (depending
145          * on the clock, actually, (TODO)), and the kernel expects TSC ticks
146          * from boot.  If !ABSTIME, then it's just relative to now.  If it is
147          * ABSTIME, then they are asking in terms of real-world time, which
148          * means ABS - NOW to get the rel time, then convert to tsc ticks. */
149         if (flags & TFD_TIMER_ABSTIME) {
150                 ret = clock_gettime(CLOCK_MONOTONIC, &now_timespec);
151                 if (ret < 0)
152                         goto out;
153                 subtract_timespecs(&rel_timespec, &new_value->it_value,
154                                    &now_timespec);
155         } else {
156                 rel_timespec = new_value->it_value;
157         }
158         ret = set_timer(timerfd, timespec2tsc(&rel_timespec) + read_tsc());
159         /* fall-through */
160 out:
161         close(timerfd);
162         close(periodfd);
163         return ret;
164 }
165
166 int timerfd_gettime(int fd, struct itimerspec *curr_value)
167 {
168         int timerfd, periodfd;
169         int ret;
170
171         timerfd = get_sibling_fd(fd, "timer");
172         if (timerfd < 0)
173                 return -1;
174         periodfd = get_sibling_fd(fd, "period");
175         if (periodfd < 0) {
176                 close(timerfd);
177                 return -1;
178         }
179         ret = __timerfd_gettime(timerfd, periodfd, curr_value);
180         close(timerfd);
181         close(periodfd);
182         return ret;
183 }