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