• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 Institute of Parallel And Distributed Systems (IPADS), Shanghai Jiao Tong University (SJTU)
3  * Licensed under the Mulan PSL v2.
4  * You can use this software according to the terms and conditions of the Mulan PSL v2.
5  * You may obtain a copy of Mulan PSL v2 at:
6  *     http://license.coscl.org.cn/MulanPSL2
7  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
8  * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
9  * PURPOSE.
10  * See the Mulan PSL v2 for more details.
11  */
12 
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <pthread.h>
16 #include <stdbool.h>
17 #include <stdio.h>
18 #include <sys/time.h>
19 #include <sys/timerfd.h>
20 #include <syscall_arch.h>
21 
22 #include "fd.h"
23 
24 #include <chcore/bug.h>
25 #include <chcore/syscall.h>
26 
27 struct timer_file {
28     struct itimerspec it;
29     struct timeval start_time;
30     int volatile timer_lock;
31 };
32 
33 #define NS_IN_S (1000000000ULL)
timespec_to_ns(struct timespec * time)34 static uint64_t timespec_to_ns(struct timespec *time)
35 {
36     return (uint64_t)(NS_IN_S * (uint64_t)time->tv_sec
37                       + (uint64_t)time->tv_nsec);
38 }
39 
ns_to_timespec(uint64_t ns)40 static struct timespec ns_to_timespec(uint64_t ns)
41 {
42     struct timespec time;
43     time.tv_sec = (time_t)(ns / NS_IN_S);
44     time.tv_nsec = (long)(ns % NS_IN_S);
45     return time;
46 }
47 
chcore_timerfd_create(int clockid,int flags)48 int chcore_timerfd_create(int clockid, int flags)
49 {
50     int timerfd;
51     struct fd_desc *timerfd_desc;
52     struct timer_file *timer_file;
53     int ret = 0;
54 
55     if (clockid != CLOCK_MONOTONIC) {
56         WARN("timerfd_create: clockid not support. use CLOCK_MONOTONIC "
57              "instead\n");
58         clockid = CLOCK_MONOTONIC;
59     }
60 
61     if (flags & TFD_CLOEXEC) {
62         WARN("timerfd_create: flag TFD_CLOEXEC is omitted\n");
63         flags &= ~TFD_CLOEXEC;
64     }
65 
66     if ((timer_file = malloc(sizeof(*timer_file))) == NULL)
67         return -ENOMEM;
68 
69     timer_file->it = (struct itimerspec){0};
70     timer_file->timer_lock = 0;
71 
72     timerfd = alloc_fd();
73     if (timerfd < 0) { /* failed */
74         ret = timerfd;
75         goto fail_out;
76     }
77     timerfd_desc = fd_dic[timerfd];
78     timerfd_desc->flags = flags;
79     timerfd_desc->type = FD_TYPE_TIMER;
80     timerfd_desc->private_data = timer_file;
81     timerfd_desc->fd_op = &timer_op;
82 out:
83     return timerfd;
84 fail_out:
85     if (timerfd > 0)
86         free_fd(timerfd);
87     if (timer_file > 0)
88         free(timer_file);
89     return ret;
90 }
91 
chcore_timerfd_settime(int fd,int flags,struct itimerspec * new_value,struct itimerspec * old_value)92 int chcore_timerfd_settime(int fd, int flags, struct itimerspec *new_value,
93                            struct itimerspec *old_value)
94 {
95     struct timer_file *timer_file;
96     struct timespec cur_time;
97     uint64_t cur_ns, val_ns;
98 
99     /* EBADF  fd is not a valid file descriptor. */
100     if (fd < 0 || fd >= MAX_FD || fd_dic[fd] == NULL)
101         return -EBADF;
102 
103     /* EINVAL fd is not a valid timerfd file descriptor. */
104     if (fd_dic[fd]->type != FD_TYPE_TIMER || fd_dic[fd]->private_data == NULL)
105         return -EINVAL;
106 
107     /* EFAULT new_value, old_value, or curr_value is not valid a pointer. */
108     if (new_value == NULL)
109         return -EFAULT;
110 
111     /* EINVAL new_value is not properly initialized (one of the tv_nsec
112      * falls outside the range zero to 999,999,999). */
113     if (new_value->it_value.tv_nsec < 0
114         || new_value->it_value.tv_nsec > 999999999
115         || new_value->it_interval.tv_nsec < 0
116         || new_value->it_interval.tv_nsec > 999999999)
117         return -EINVAL;
118 
119     if (flags & TFD_TIMER_CANCEL_ON_SET) {
120         WARN("timerfd_settime: flag TFD_TIMER_CANCEL_ON_SET omitted\n");
121         flags &= ~TFD_TIMER_CANCEL_ON_SET;
122     }
123 
124     timer_file = fd_dic[fd]->private_data;
125     chcore_spin_lock(&timer_file->timer_lock);
126     /* Passing the old_value */
127     if (old_value) {
128         old_value->it_interval = timer_file->it.it_interval;
129         old_value->it_value = timer_file->it.it_value;
130     }
131     /* Set the time to expire. */
132     timer_file->it = *new_value;
133     if (new_value->it_value.tv_sec != 0 || new_value->it_value.tv_sec != 0) {
134         val_ns = timespec_to_ns(&new_value->it_value);
135         clock_gettime(CLOCK_MONOTONIC, &cur_time);
136         cur_ns = timespec_to_ns(&cur_time);
137         if (flags & TFD_TIMER_ABSTIME)
138             timer_file->it.it_value =
139                 ns_to_timespec(cur_ns > val_ns ? cur_ns : val_ns);
140         else
141             timer_file->it.it_value = ns_to_timespec(cur_ns + val_ns);
142     }
143     chcore_spin_unlock(&timer_file->timer_lock);
144     return 0;
145 }
146 
chcore_timerfd_gettime(int fd,struct itimerspec * curr_value)147 int chcore_timerfd_gettime(int fd, struct itimerspec *curr_value)
148 {
149     uint64_t cur_ns, val_ns, interval_ns, remain_ns;
150     struct timespec cur_time;
151     struct timer_file *timer_file;
152 
153     /* EBADF  fd is not a valid file descriptor. */
154     if (fd < 0 || fd >= MAX_FD || fd_dic[fd] == NULL)
155         return -EBADF;
156 
157     /* EINVAL fd is not a valid timerfd file descriptor. */
158     if (fd_dic[fd]->type != FD_TYPE_TIMER || fd_dic[fd]->private_data == NULL)
159         return -EINVAL;
160 
161     /* EFAULT new_value, old_value, or curr_value is not valid a pointer. */
162     if (curr_value == NULL)
163         return -EFAULT;
164 
165     timer_file = fd_dic[fd]->private_data;
166 
167     chcore_spin_lock(&timer_file->timer_lock);
168     curr_value->it_interval = timer_file->it.it_interval;
169     if (timer_file->it.it_value.tv_sec == 0
170         && timer_file->it.it_value.tv_nsec == 0) {
171         curr_value->it_value = timer_file->it.it_value;
172     } else {
173         clock_gettime(CLOCK_MONOTONIC, &cur_time);
174         cur_ns = timespec_to_ns(&cur_time);
175         val_ns = timespec_to_ns(&timer_file->it.it_value);
176         interval_ns = timespec_to_ns(&timer_file->it.it_interval);
177 
178         /* The it_value field returns the amount of time until the timer
179          * will next expire.  If both fields of this structure are zero,
180          * then the timer is currently disarmed.  This field always
181          * contains a relative value, regardless of whether the
182          * TFD_TIMER_ABSTIME flag was specified
183          * when setting the timer. */
184         if (cur_ns < val_ns)
185             remain_ns = val_ns - cur_ns;
186         else
187             remain_ns = interval_ns - (cur_ns - val_ns) % interval_ns;
188         curr_value->it_value = ns_to_timespec(remain_ns);
189     }
190     chcore_spin_unlock(&timer_file->timer_lock);
191     return 0;
192 }
193 
chcore_timerfd_read(int fd,void * buf,size_t count)194 static ssize_t chcore_timerfd_read(int fd, void *buf, size_t count)
195 {
196     uint64_t cur_ns, val_ns, interval_ns, next_expire_ns, tick;
197     struct timespec cur_time, sleep_time;
198     struct timer_file *timer_file;
199 
200     /* EBADF  fd is not a valid file descriptor. */
201     if (fd < 0 || fd >= MAX_FD || fd_dic[fd] == NULL)
202         return -EBADF;
203 
204     /* EINVAL fd is not a valid timerfd file descriptor. */
205     if (fd_dic[fd]->type != FD_TYPE_TIMER || fd_dic[fd]->private_data == NULL)
206         return -EINVAL;
207 
208     if (count < sizeof(long))
209         return -EINVAL;
210 
211     if (buf == NULL)
212         return -EINVAL;
213 
214     timer_file = fd_dic[fd]->private_data;
215     chcore_spin_lock(&timer_file->timer_lock);
216     val_ns = timespec_to_ns(&timer_file->it.it_value);
217     interval_ns = timespec_to_ns(&timer_file->it.it_interval);
218     /* Check whether the timer has been disarmed */
219     if (interval_ns == 0 && val_ns == 0) {
220         chcore_spin_unlock(&timer_file->timer_lock);
221         *(uint64_t *)buf = 0;
222         return sizeof(tick);
223     }
224     clock_gettime(CLOCK_MONOTONIC, &cur_time);
225     cur_ns = timespec_to_ns(&cur_time);
226     /* Non-blocking mode */
227     if (cur_ns < val_ns && (fd_dic[fd]->flags & TFD_NONBLOCK)) {
228         chcore_spin_unlock(&timer_file->timer_lock);
229         return -EAGAIN;
230     }
231     if (cur_ns < val_ns) {
232         sleep_time = ns_to_timespec(val_ns - cur_ns);
233         nanosleep(&sleep_time, NULL);
234         clock_gettime(CLOCK_MONOTONIC, &cur_time);
235         cur_ns = timespec_to_ns(&cur_time);
236         val_ns = timespec_to_ns(&timer_file->it.it_value);
237     }
238     /* If both fields of new_value.it_interval are zero, the timer expires
239      * just once, at the time specified by new_value.it_value. */
240     if (interval_ns > 0) {
241         tick = (cur_ns - val_ns) / interval_ns + 1;
242         next_expire_ns = cur_ns + interval_ns - (cur_ns - val_ns) % interval_ns;
243     } else {
244         tick = 1;
245         next_expire_ns = 0;
246     }
247     *(uint64_t *)buf = tick;
248     /* update timer for the next read */
249     timer_file->it.it_value = ns_to_timespec(next_expire_ns);
250     chcore_spin_unlock(&timer_file->timer_lock);
251     return sizeof(tick);
252 }
253 
chcore_timerfd_close(int fd)254 static int chcore_timerfd_close(int fd)
255 {
256     BUG_ON(!fd_dic[fd]->private_data);
257 
258     /* maybe we should lock the file */
259     free(fd_dic[fd]->private_data);
260     free_fd(fd);
261     return 0;
262 }
263 
chcore_timerfd_write(int fd,void * buf,size_t count)264 static ssize_t chcore_timerfd_write(int fd, void *buf, size_t count)
265 {
266     return -EINVAL;
267 }
268 
chcore_timerfd_poll(int fd,struct pollarg * arg)269 static int chcore_timerfd_poll(int fd, struct pollarg *arg)
270 {
271     uint64_t cur_ns, val_ns;
272     struct timespec cur_time;
273     struct timer_file *timer_file;
274     int mask = 0;
275 
276     /* Check fd */
277     if (fd < 0 || fd >= MAX_FD || fd_dic[fd] == NULL
278         || fd_dic[fd]->type != FD_TYPE_TIMER
279         || fd_dic[fd]->private_data == NULL)
280         return -EINVAL;
281 
282     if (arg->events & POLLIN || arg->events & POLLRDNORM) {
283         timer_file = fd_dic[fd]->private_data;
284         /* no need to lock, only check the value of it_value */
285         val_ns = timespec_to_ns(&timer_file->it.it_value);
286         if (val_ns != 0) {
287             clock_gettime(CLOCK_MONOTONIC, &cur_time);
288             cur_ns = timespec_to_ns(&cur_time);
289             mask = cur_ns >= val_ns ? POLLIN | POLLRDNORM : 0;
290         }
291     }
292 
293     return mask;
294 }
295 
296 struct timerfd_poll_arg {
297     uint64_t wait_ns;
298     cap_t notifc_cap;
299 };
300 
timerfd_poll_thread_routine(void * arg)301 static void *timerfd_poll_thread_routine(void *arg)
302 {
303     struct timerfd_poll_arg *tpa = (struct timerfd_poll_arg *)arg;
304     struct timespec wait_time;
305 
306     wait_time = ns_to_timespec(tpa->wait_ns);
307     nanosleep(&wait_time, NULL);
308     // chcore_syscall1(CHCORE_SYS_notify, tpa->notifc_cap);
309     usys_notify(tpa->notifc_cap);
310     free(arg);
311     return NULL;
312 }
313 
chcore_timerfd_async_poll(struct pollfd fds[],nfds_t nfds,int timeout,cap_t notifc_cap)314 int chcore_timerfd_async_poll(struct pollfd fds[], nfds_t nfds, int timeout,
315                               cap_t notifc_cap)
316 {
317     int ret = 0, i = 0, count = 0, fd = 0;
318     pthread_t tid;
319     struct timer_file *timer_file;
320     struct timespec cur_time;
321     uint64_t wait_ns = UINT64_MAX;
322     uint64_t val_ns = 0, cur_ns = 0;
323     struct timerfd_poll_arg *tpa;
324 
325     /* if timeout == 0, we have check in fd_op->poll function directly
326      * return */
327     if (timeout == 0)
328         return 0;
329     /* Find timeout */
330     clock_gettime(CLOCK_MONOTONIC, &cur_time);
331     cur_ns = timespec_to_ns(&cur_time);
332     for (i = 0; i < nfds; i++) {
333         fd = fds[i].fd;
334         if (fd < 0 || fd >= MAX_FD || fd_dic[fd] == NULL
335             || fd_dic[fd]->type != FD_TYPE_TIMER
336             || fd_dic[fd]->private_data == NULL)
337             continue; /* not a valid timerfd */
338         /* get timeout */
339         timer_file = fd_dic[fd]->private_data;
340         val_ns = timespec_to_ns(&timer_file->it.it_value);
341         if (val_ns == 0) { /* timer is disarmed */
342             continue;
343         }
344         if (cur_ns > val_ns) { /* ready */
345             fds[i].revents = fds[i].events & POLLIN
346                                      || fds[i].events & POLLRDNORM ?
347                                  POLLIN | POLLRDNORM :
348                                  0;
349             count += 1;
350         } else { /* not ready */
351             wait_ns = (val_ns - cur_ns) < wait_ns ? (val_ns - cur_ns) : wait_ns;
352         }
353     }
354 
355     /* directly return if ready */
356     if (count > 0)
357         return count;
358 
359     /* OPT: directly return when timeout is closer */
360     if (timeout > 0 && wait_ns > (uint64_t)timeout * 1000000)
361         return 0;
362 
363     tpa = malloc(sizeof(struct timerfd_poll_arg));
364     if (tpa == NULL) {
365         return -ENOMEM;
366     }
367     tpa->notifc_cap = notifc_cap;
368     tpa->wait_ns = wait_ns;
369     ret = pthread_create(&tid, NULL, timerfd_poll_thread_routine, (void *)tpa);
370     return ret;
371 }
372 
373 /* TIMERFD */
374 struct fd_ops timer_op = {
375     .read = chcore_timerfd_read,
376     .write = chcore_timerfd_write,
377     .close = chcore_timerfd_close,
378     .poll = chcore_timerfd_poll,
379     .ioctl = NULL,
380     .fcntl = NULL,
381 };
382