]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/sys_timerfd.c
Merge llvm-project main llvmorg-18-init-16864-g3b3ee1f53424
[FreeBSD/FreeBSD.git] / sys / kern / sys_timerfd.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2014 Dmitry Chagin <dchagin@FreeBSD.org>
5  * Copyright (c) 2023 Jake Freeland <jfree@FreeBSD.org>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/callout.h>
32 #include <sys/fcntl.h>
33 #include <sys/file.h>
34 #include <sys/filedesc.h>
35 #include <sys/filio.h>
36 #include <sys/kernel.h>
37 #include <sys/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/mount.h>
40 #include <sys/mutex.h>
41 #include <sys/poll.h>
42 #include <sys/proc.h>
43 #include <sys/queue.h>
44 #include <sys/selinfo.h>
45 #include <sys/stat.h>
46 #include <sys/sx.h>
47 #include <sys/syscallsubr.h>
48 #include <sys/sysctl.h>
49 #include <sys/sysent.h>
50 #include <sys/sysproto.h>
51 #include <sys/timerfd.h>
52 #include <sys/timespec.h>
53 #include <sys/uio.h>
54 #include <sys/user.h>
55
56 #include <security/audit/audit.h>
57
58 static MALLOC_DEFINE(M_TIMERFD, "timerfd", "timerfd structures");
59
60 static struct mtx timerfd_list_lock;
61 static LIST_HEAD(, timerfd) timerfd_list;
62 MTX_SYSINIT(timerfd, &timerfd_list_lock, "timerfd_list_lock", MTX_DEF);
63
64 static struct unrhdr64 tfdino_unr;
65
66 #define TFD_NOJUMP      0       /* Realtime clock has not jumped. */
67 #define TFD_READ        1       /* Jumped, tfd has been read since. */
68 #define TFD_ZREAD       2       /* Jumped backwards, CANCEL_ON_SET=false. */
69 #define TFD_CANCELED    4       /* Jumped, CANCEL_ON_SET=true. */
70 #define TFD_JUMPED      (TFD_ZREAD | TFD_CANCELED)
71
72 /*
73  * One structure allocated per timerfd descriptor.
74  *
75  * Locking semantics:
76  * (t)  locked by tfd_lock mtx
77  * (l)  locked by timerfd_list_lock sx
78  * (c)  const until freeing
79  */
80 struct timerfd {
81         /* User specified. */
82         struct itimerspec tfd_time;     /* (t) tfd timer */
83         clockid_t       tfd_clockid;    /* (c) timing base */
84         int             tfd_flags;      /* (c) creation flags */
85         int             tfd_timflags;   /* (t) timer flags */
86
87         /* Used internally. */
88         timerfd_t       tfd_count;      /* (t) expiration count since read */
89         bool            tfd_expired;    /* (t) true upon initial expiration */
90         struct mtx      tfd_lock;       /* tfd mtx lock */
91         struct callout  tfd_callout;    /* (t) expiration notification */
92         struct selinfo  tfd_sel;        /* (t) I/O alerts */
93         struct timespec tfd_boottim;    /* (t) cached boottime */
94         int             tfd_jumped;     /* (t) timer jump status */
95         LIST_ENTRY(timerfd) entry;      /* (l) entry in list */
96
97         /* For stat(2). */
98         ino_t           tfd_ino;        /* (c) inode number */
99         struct timespec tfd_atim;       /* (t) time of last read */
100         struct timespec tfd_mtim;       /* (t) time of last settime */
101         struct timespec tfd_birthtim;   /* (c) creation time */
102 };
103
104 static void
105 timerfd_init(void *data)
106 {
107         new_unrhdr64(&tfdino_unr, 1);
108 }
109
110 SYSINIT(timerfd, SI_SUB_VFS, SI_ORDER_ANY, timerfd_init, NULL);
111
112 static inline void
113 timerfd_getboottime(struct timespec *ts)
114 {
115         struct timeval tv;
116
117         getboottime(&tv);
118         TIMEVAL_TO_TIMESPEC(&tv, ts);
119 }
120
121 /*
122  * Call when a discontinuous jump has occured in CLOCK_REALTIME and
123  * update timerfd's cached boottime. A jump can be triggered using
124  * functions like clock_settime(2) or settimeofday(2).
125  *
126  * Timer is marked TFD_CANCELED if TFD_TIMER_CANCEL_ON_SET is set
127  * and the realtime clock jumps.
128  * Timer is marked TFD_ZREAD if TFD_TIMER_CANCEL_ON_SET is not set,
129  * but the realtime clock jumps backwards.
130  */
131 void
132 timerfd_jumped(void)
133 {
134         struct timerfd *tfd;
135         struct timespec boottime, diff;
136
137         if (LIST_EMPTY(&timerfd_list))
138                 return;
139
140         timerfd_getboottime(&boottime);
141         mtx_lock(&timerfd_list_lock);
142         LIST_FOREACH(tfd, &timerfd_list, entry) {
143                 mtx_lock(&tfd->tfd_lock);
144                 if (tfd->tfd_clockid != CLOCK_REALTIME ||
145                     (tfd->tfd_timflags & TFD_TIMER_ABSTIME) == 0 ||
146                     timespeccmp(&boottime, &tfd->tfd_boottim, ==)) {
147                         mtx_unlock(&tfd->tfd_lock);
148                         continue;
149                 }
150
151                 if (callout_active(&tfd->tfd_callout)) {
152                         if ((tfd->tfd_timflags & TFD_TIMER_CANCEL_ON_SET) != 0)
153                                 tfd->tfd_jumped = TFD_CANCELED;
154                         else if (timespeccmp(&boottime, &tfd->tfd_boottim, <))
155                                 tfd->tfd_jumped = TFD_ZREAD;
156
157                         /*
158                          * Do not reschedule callout when
159                          * inside interval time loop.
160                          */
161                         if (!tfd->tfd_expired) {
162                                 timespecsub(&boottime,
163                                     &tfd->tfd_boottim, &diff);
164                                 timespecsub(&tfd->tfd_time.it_value,
165                                     &diff, &tfd->tfd_time.it_value);
166                                 if (callout_stop(&tfd->tfd_callout) == 1) {
167                                         callout_schedule_sbt(&tfd->tfd_callout,
168                                             tstosbt(tfd->tfd_time.it_value),
169                                             0, C_ABSOLUTE);
170                                 }
171                         }
172                 }
173
174                 tfd->tfd_boottim = boottime;
175                 mtx_unlock(&tfd->tfd_lock);
176         }
177         mtx_unlock(&timerfd_list_lock);
178 }
179
180 static int
181 timerfd_read(struct file *fp, struct uio *uio, struct ucred *active_cred,
182     int flags, struct thread *td)
183 {
184         struct timerfd *tfd = fp->f_data;
185         timerfd_t count;
186         int error = 0;
187
188         if (uio->uio_resid < sizeof(timerfd_t))
189                 return (EINVAL);
190
191         mtx_lock(&tfd->tfd_lock);
192 retry:
193         getnanotime(&tfd->tfd_atim);
194         if ((tfd->tfd_jumped & TFD_JUMPED) != 0) {
195                 if (tfd->tfd_jumped == TFD_CANCELED)
196                         error = ECANCELED;
197                 tfd->tfd_jumped = TFD_READ;
198                 tfd->tfd_count = 0;
199                 mtx_unlock(&tfd->tfd_lock);
200                 return (error);
201         } else {
202                 tfd->tfd_jumped = TFD_NOJUMP;
203         }
204         if (tfd->tfd_count == 0) {
205                 if ((fp->f_flag & FNONBLOCK) != 0) {
206                         mtx_unlock(&tfd->tfd_lock);
207                         return (EAGAIN);
208                 }
209                 td->td_rtcgen = atomic_load_acq_int(&rtc_generation);
210                 error = mtx_sleep(&tfd->tfd_count, &tfd->tfd_lock,
211                     PCATCH, "tfdrd", 0);
212                 if (error == 0) {
213                         goto retry;
214                 } else {
215                         mtx_unlock(&tfd->tfd_lock);
216                         return (error);
217                 }
218         }
219
220         count = tfd->tfd_count;
221         tfd->tfd_count = 0;
222         mtx_unlock(&tfd->tfd_lock);
223         error = uiomove(&count, sizeof(timerfd_t), uio);
224
225         return (error);
226 }
227
228 static int
229 timerfd_ioctl(struct file *fp, u_long cmd, void *data,
230     struct ucred *active_cred, struct thread *td)
231 {
232         switch (cmd) {
233         case FIOASYNC:
234                 if (*(int *)data != 0)
235                         atomic_set_int(&fp->f_flag, FASYNC);
236                 else
237                         atomic_clear_int(&fp->f_flag, FASYNC);
238                 return (0);
239         case FIONBIO:
240                 if (*(int *)data != 0)
241                         atomic_set_int(&fp->f_flag, FNONBLOCK);
242                 else
243                         atomic_clear_int(&fp->f_flag, FNONBLOCK);
244                 return (0);
245         }
246         return (ENOTTY);
247 }
248
249 static int
250 timerfd_poll(struct file *fp, int events, struct ucred *active_cred,
251     struct thread *td)
252 {
253         struct timerfd *tfd = fp->f_data;
254         int revents = 0;
255
256         mtx_lock(&tfd->tfd_lock);
257         if ((events & (POLLIN | POLLRDNORM)) != 0 &&
258             tfd->tfd_count > 0 && tfd->tfd_jumped != TFD_READ)
259                 revents |= events & (POLLIN | POLLRDNORM);
260         if (revents == 0)
261                 selrecord(td, &tfd->tfd_sel);
262         mtx_unlock(&tfd->tfd_lock);
263
264         return (revents);
265 }
266
267 static void
268 filt_timerfddetach(struct knote *kn)
269 {
270         struct timerfd *tfd = kn->kn_hook;
271
272         mtx_lock(&tfd->tfd_lock);
273         knlist_remove(&tfd->tfd_sel.si_note, kn, 1);
274         mtx_unlock(&tfd->tfd_lock);
275 }
276
277 static int
278 filt_timerfdread(struct knote *kn, long hint)
279 {
280         struct timerfd *tfd = kn->kn_hook;
281
282         mtx_assert(&tfd->tfd_lock, MA_OWNED);
283         kn->kn_data = (int64_t)tfd->tfd_count;
284         return (tfd->tfd_count > 0);
285 }
286
287 static struct filterops timerfd_rfiltops = {
288         .f_isfd = 1,
289         .f_detach = filt_timerfddetach,
290         .f_event = filt_timerfdread,
291 };
292
293 static int
294 timerfd_kqfilter(struct file *fp, struct knote *kn)
295 {
296         struct timerfd *tfd = fp->f_data;
297
298         if (kn->kn_filter != EVFILT_READ)
299                 return (EINVAL);
300
301         kn->kn_fop = &timerfd_rfiltops;
302         kn->kn_hook = tfd;
303         knlist_add(&tfd->tfd_sel.si_note, kn, 0);
304
305         return (0);
306 }
307
308 static int
309 timerfd_stat(struct file *fp, struct stat *sb, struct ucred *active_cred)
310 {
311         struct timerfd *tfd = fp->f_data;
312
313         bzero(sb, sizeof(*sb));
314         sb->st_nlink = fp->f_count - 1;
315         sb->st_uid = fp->f_cred->cr_uid;
316         sb->st_gid = fp->f_cred->cr_gid;
317         sb->st_blksize = PAGE_SIZE;
318         mtx_lock(&tfd->tfd_lock);
319         sb->st_atim = tfd->tfd_atim;
320         sb->st_mtim = tfd->tfd_mtim;
321         mtx_unlock(&tfd->tfd_lock);
322         sb->st_ctim = sb->st_mtim;
323         sb->st_ino = tfd->tfd_ino;
324         sb->st_birthtim = tfd->tfd_birthtim;
325
326         return (0);
327 }
328
329 static int
330 timerfd_close(struct file *fp, struct thread *td)
331 {
332         struct timerfd *tfd = fp->f_data;
333
334         mtx_lock(&timerfd_list_lock);
335         LIST_REMOVE(tfd, entry);
336         mtx_unlock(&timerfd_list_lock);
337
338         callout_drain(&tfd->tfd_callout);
339         seldrain(&tfd->tfd_sel);
340         knlist_destroy(&tfd->tfd_sel.si_note);
341         mtx_destroy(&tfd->tfd_lock);
342         free(tfd, M_TIMERFD);
343         fp->f_ops = &badfileops;
344
345         return (0);
346 }
347
348 static int
349 timerfd_fill_kinfo(struct file *fp, struct kinfo_file *kif,
350     struct filedesc *fdp)
351 {
352         struct timerfd *tfd = fp->f_data;
353
354         kif->kf_type = KF_TYPE_TIMERFD;
355         kif->kf_un.kf_timerfd.kf_timerfd_clockid = tfd->tfd_clockid;
356         kif->kf_un.kf_timerfd.kf_timerfd_flags = tfd->tfd_flags;
357         kif->kf_un.kf_timerfd.kf_timerfd_addr = (uintptr_t)tfd;
358
359         return (0);
360 }
361
362 static struct fileops timerfdops = {
363         .fo_read = timerfd_read,
364         .fo_write = invfo_rdwr,
365         .fo_truncate = invfo_truncate,
366         .fo_ioctl = timerfd_ioctl,
367         .fo_poll = timerfd_poll,
368         .fo_kqfilter = timerfd_kqfilter,
369         .fo_stat = timerfd_stat,
370         .fo_close = timerfd_close,
371         .fo_chmod = invfo_chmod,
372         .fo_chown = invfo_chown,
373         .fo_sendfile = invfo_sendfile,
374         .fo_fill_kinfo = timerfd_fill_kinfo,
375         .fo_cmp = file_kcmp_generic,
376         .fo_flags = DFLAG_PASSABLE,
377 };
378
379 static void
380 timerfd_curval(struct timerfd *tfd, struct itimerspec *old_value)
381 {
382         struct timespec curr_value;
383
384         mtx_assert(&tfd->tfd_lock, MA_OWNED);
385         *old_value = tfd->tfd_time;
386         if (timespecisset(&tfd->tfd_time.it_value)) {
387                 nanouptime(&curr_value);
388                 timespecsub(&tfd->tfd_time.it_value, &curr_value,
389                     &old_value->it_value);
390         }
391 }
392
393 static void
394 timerfd_expire(void *arg)
395 {
396         struct timerfd *tfd = (struct timerfd *)arg;
397         struct timespec uptime;
398
399         ++tfd->tfd_count;
400         tfd->tfd_expired = true;
401         if (timespecisset(&tfd->tfd_time.it_interval)) {
402                 /* Count missed events. */
403                 nanouptime(&uptime);
404                 if (timespeccmp(&uptime, &tfd->tfd_time.it_value, >)) {
405                         timespecsub(&uptime, &tfd->tfd_time.it_value, &uptime);
406                         tfd->tfd_count += tstosbt(uptime) /
407                             tstosbt(tfd->tfd_time.it_interval);
408                 }
409                 timespecadd(&tfd->tfd_time.it_value,
410                     &tfd->tfd_time.it_interval, &tfd->tfd_time.it_value);
411                 callout_schedule_sbt(&tfd->tfd_callout,
412                     tstosbt(tfd->tfd_time.it_value),
413                     0, C_ABSOLUTE);
414         } else {
415                 /* Single shot timer. */
416                 callout_deactivate(&tfd->tfd_callout);
417                 timespecclear(&tfd->tfd_time.it_value);
418         }
419
420         wakeup(&tfd->tfd_count);
421         selwakeup(&tfd->tfd_sel);
422         KNOTE_LOCKED(&tfd->tfd_sel.si_note, 0);
423 }
424
425 int
426 kern_timerfd_create(struct thread *td, int clockid, int flags)
427 {
428         struct file *fp;
429         struct timerfd *tfd;
430         int error, fd, fflags;
431
432         AUDIT_ARG_VALUE(clockid);
433         AUDIT_ARG_FFLAGS(flags);
434
435         switch (clockid) {
436         case CLOCK_REALTIME:
437                 /* FALLTHROUGH */
438         case CLOCK_MONOTONIC:
439                 /* FALLTHROUGH */
440         case CLOCK_UPTIME:
441                 /*
442                  * CLOCK_BOOTTIME should be added once different from
443                  * CLOCK_UPTIME
444                  */
445                 break;
446         default:
447                 return (EINVAL);
448         }
449         if ((flags & ~(TFD_CLOEXEC | TFD_NONBLOCK)) != 0)
450                 return (EINVAL);
451
452         fflags = FREAD;
453         if ((flags & TFD_CLOEXEC) != 0)
454                 fflags |= O_CLOEXEC;
455         if ((flags & TFD_NONBLOCK) != 0)
456                 fflags |= FNONBLOCK;
457
458         error = falloc(td, &fp, &fd, fflags);
459         if (error != 0)
460                 return (error);
461
462         tfd = malloc(sizeof(*tfd), M_TIMERFD, M_WAITOK | M_ZERO);
463         tfd->tfd_clockid = (clockid_t)clockid;
464         tfd->tfd_flags = flags;
465         tfd->tfd_ino = alloc_unr64(&tfdino_unr);
466         mtx_init(&tfd->tfd_lock, "timerfd", NULL, MTX_DEF);
467         callout_init_mtx(&tfd->tfd_callout, &tfd->tfd_lock, 0);
468         knlist_init_mtx(&tfd->tfd_sel.si_note, &tfd->tfd_lock);
469         timerfd_getboottime(&tfd->tfd_boottim);
470         getnanotime(&tfd->tfd_birthtim);
471         mtx_lock(&timerfd_list_lock);
472         LIST_INSERT_HEAD(&timerfd_list, tfd, entry);
473         mtx_unlock(&timerfd_list_lock);
474
475         finit(fp, fflags, DTYPE_TIMERFD, tfd, &timerfdops);
476
477         fdrop(fp, td);
478
479         td->td_retval[0] = fd;
480         return (0);
481 }
482
483 int
484 kern_timerfd_gettime(struct thread *td, int fd, struct itimerspec *curr_value)
485 {
486         struct file *fp;
487         struct timerfd *tfd;
488         int error;
489
490         error = fget(td, fd, &cap_write_rights, &fp);
491         if (error != 0)
492                 return (error);
493         if (fp->f_type != DTYPE_TIMERFD) {
494                 fdrop(fp, td);
495                 return (EINVAL);
496         }
497         tfd = fp->f_data;
498
499         mtx_lock(&tfd->tfd_lock);
500         timerfd_curval(tfd, curr_value);
501         mtx_unlock(&tfd->tfd_lock);
502
503         fdrop(fp, td);
504         return (0);
505 }
506
507 int
508 kern_timerfd_settime(struct thread *td, int fd, int flags,
509     const struct itimerspec *new_value, struct itimerspec *old_value)
510 {
511         struct file *fp;
512         struct timerfd *tfd;
513         struct timespec ts;
514         int error = 0;
515
516         if ((flags & ~(TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)) != 0)
517                 return (EINVAL);
518         if (!timespecvalid_interval(&new_value->it_value) ||
519             !timespecvalid_interval(&new_value->it_interval))
520                 return (EINVAL);
521
522         error = fget(td, fd, &cap_write_rights, &fp);
523         if (error != 0)
524                 return (error);
525         if (fp->f_type != DTYPE_TIMERFD) {
526                 fdrop(fp, td);
527                 return (EINVAL);
528         }
529         tfd = fp->f_data;
530
531         mtx_lock(&tfd->tfd_lock);
532         getnanotime(&tfd->tfd_mtim);
533         tfd->tfd_timflags = flags;
534
535         /* Store old itimerspec, if applicable. */
536         if (old_value != NULL)
537                 timerfd_curval(tfd, old_value);
538
539         /* Set new expiration. */
540         tfd->tfd_time = *new_value;
541         if (timespecisset(&tfd->tfd_time.it_value)) {
542                 if ((flags & TFD_TIMER_ABSTIME) == 0) {
543                         nanouptime(&ts);
544                         timespecadd(&tfd->tfd_time.it_value, &ts,
545                             &tfd->tfd_time.it_value);
546                 } else if (tfd->tfd_clockid == CLOCK_REALTIME) {
547                         /* ECANCELED if unread jump is pending. */
548                         if (tfd->tfd_jumped == TFD_CANCELED)
549                                 error = ECANCELED;
550                         /* Convert from CLOCK_REALTIME to CLOCK_BOOTTIME. */
551                         timespecsub(&tfd->tfd_time.it_value, &tfd->tfd_boottim,
552                             &tfd->tfd_time.it_value);
553                 }
554                 callout_reset_sbt(&tfd->tfd_callout,
555                     tstosbt(tfd->tfd_time.it_value),
556                     0, timerfd_expire, tfd, C_ABSOLUTE);
557         } else {
558                 callout_stop(&tfd->tfd_callout);
559         }
560         tfd->tfd_count = 0;
561         tfd->tfd_expired = false;
562         tfd->tfd_jumped = TFD_NOJUMP;
563         mtx_unlock(&tfd->tfd_lock);
564
565         fdrop(fp, td);
566         return (error);
567 }
568
569 int
570 sys_timerfd_create(struct thread *td, struct timerfd_create_args *uap)
571 {
572         return (kern_timerfd_create(td, uap->clockid, uap->flags));
573 }
574
575 int
576 sys_timerfd_gettime(struct thread *td, struct timerfd_gettime_args *uap)
577 {
578         struct itimerspec curr_value;
579         int error;
580
581         error = kern_timerfd_gettime(td, uap->fd, &curr_value);
582         if (error == 0)
583                 error = copyout(&curr_value, uap->curr_value,
584                     sizeof(curr_value));
585
586         return (error);
587 }
588
589 int
590 sys_timerfd_settime(struct thread *td, struct timerfd_settime_args *uap)
591 {
592         struct itimerspec new_value, old_value;
593         int error;
594
595         error = copyin(uap->new_value, &new_value, sizeof(new_value));
596         if (error != 0)
597                 return (error);
598         if (uap->old_value == NULL) {
599                 error = kern_timerfd_settime(td, uap->fd, uap->flags,
600                     &new_value, NULL);
601         } else {
602                 error = kern_timerfd_settime(td, uap->fd, uap->flags,
603                     &new_value, &old_value);
604                 if (error == 0)
605                         error = copyout(&old_value, uap->old_value,
606                             sizeof(old_value));
607         }
608         return (error);
609 }