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