2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
13 * Copyright (c) 2015 Joyent, Inc. All rights reserved.
17 * Support for the timerfd facility, a Linux-borne facility that allows
18 * POSIX.1b timers to be created and manipulated via a file descriptor
23 #include <sys/sunddi.h>
24 #include <sys/timerfd.h>
27 #include <sys/sysmacros.h>
28 #include <sys/filio.h>
31 #include <sys/timer.h>
34 typedef struct timerfd_state timerfd_state_t
;
36 struct timerfd_state
{
37 kmutex_t tfd_lock
; /* lock protecting state */
38 kcondvar_t tfd_cv
; /* condvar */
39 pollhead_t tfd_pollhd
; /* poll head */
40 uint64_t tfd_fired
; /* # of times fired */
41 itimer_t tfd_itimer
; /* underlying itimer */
42 timerfd_state_t
*tfd_next
; /* next state on global list */
46 * Internal global variables.
48 static kmutex_t timerfd_lock
; /* lock protecting state */
49 static dev_info_t
*timerfd_devi
; /* device info */
50 static vmem_t
*timerfd_minor
; /* minor number arena */
51 static void *timerfd_softstate
; /* softstate pointer */
52 static timerfd_state_t
*timerfd_state
; /* global list of state */
55 timerfd_itimer_lock(timerfd_state_t
*state
)
57 itimer_t
*it
= &state
->tfd_itimer
;
59 mutex_enter(&state
->tfd_lock
);
61 while (it
->it_lock
& ITLK_LOCKED
) {
63 cv_wait(&it
->it_cv
, &state
->tfd_lock
);
67 it
->it_lock
|= ITLK_LOCKED
;
69 mutex_exit(&state
->tfd_lock
);
75 timerfd_itimer_unlock(timerfd_state_t
*state
, itimer_t
*it
)
77 VERIFY(it
== &state
->tfd_itimer
);
78 VERIFY(it
->it_lock
& ITLK_LOCKED
);
80 mutex_enter(&state
->tfd_lock
);
82 it
->it_lock
&= ~ITLK_LOCKED
;
85 cv_signal(&it
->it_cv
);
87 mutex_exit(&state
->tfd_lock
);
91 timerfd_fire(itimer_t
*it
)
93 timerfd_state_t
*state
= it
->it_frontend
;
96 mutex_enter(&state
->tfd_lock
);
97 oval
= state
->tfd_fired
++;
98 mutex_exit(&state
->tfd_lock
);
101 cv_broadcast(&state
->tfd_cv
);
102 pollwakeup(&state
->tfd_pollhd
, POLLRDNORM
| POLLIN
);
108 timerfd_open(dev_t
*devp
, int flag
, int otyp
, cred_t
*cred_p
)
110 timerfd_state_t
*state
;
111 major_t major
= getemajor(*devp
);
112 minor_t minor
= getminor(*devp
);
114 if (minor
!= TIMERFDMNRN_TIMERFD
)
117 mutex_enter(&timerfd_lock
);
119 minor
= (minor_t
)(uintptr_t)vmem_alloc(timerfd_minor
, 1,
120 VM_BESTFIT
| VM_SLEEP
);
122 if (ddi_soft_state_zalloc(timerfd_softstate
, minor
) != DDI_SUCCESS
) {
123 vmem_free(timerfd_minor
, (void *)(uintptr_t)minor
, 1);
124 mutex_exit(&timerfd_lock
);
128 state
= ddi_get_soft_state(timerfd_softstate
, minor
);
129 *devp
= makedevice(major
, minor
);
131 state
->tfd_next
= timerfd_state
;
132 timerfd_state
= state
;
134 mutex_exit(&timerfd_lock
);
141 timerfd_read(dev_t dev
, uio_t
*uio
, cred_t
*cr
)
143 timerfd_state_t
*state
;
144 minor_t minor
= getminor(dev
);
148 if (uio
->uio_resid
< sizeof (val
))
151 state
= ddi_get_soft_state(timerfd_softstate
, minor
);
153 mutex_enter(&state
->tfd_lock
);
155 while (state
->tfd_fired
== 0) {
156 if (uio
->uio_fmode
& (FNDELAY
|FNONBLOCK
)) {
157 mutex_exit(&state
->tfd_lock
);
161 if (!cv_wait_sig_swap(&state
->tfd_cv
, &state
->tfd_lock
)) {
162 mutex_exit(&state
->tfd_lock
);
168 * Our tfd_fired is non-zero; slurp its value and then clear it.
170 val
= state
->tfd_fired
;
171 state
->tfd_fired
= 0;
172 mutex_exit(&state
->tfd_lock
);
174 err
= uiomove(&val
, sizeof (val
), UIO_READ
, uio
);
181 timerfd_poll(dev_t dev
, short events
, int anyyet
, short *reventsp
,
182 struct pollhead
**phpp
)
184 timerfd_state_t
*state
;
185 minor_t minor
= getminor(dev
);
188 state
= ddi_get_soft_state(timerfd_softstate
, minor
);
190 mutex_enter(&state
->tfd_lock
);
192 if (state
->tfd_fired
> 0)
193 revents
|= POLLRDNORM
| POLLIN
;
195 if (!(*reventsp
= revents
& events
) && !anyyet
)
196 *phpp
= &state
->tfd_pollhd
;
198 mutex_exit(&state
->tfd_lock
);
204 timerfd_copyin(uintptr_t addr
, itimerspec_t
*dest
)
206 if (get_udatamodel() == DATAMODEL_NATIVE
) {
207 if (copyin((void *)addr
, dest
, sizeof (itimerspec_t
)) != 0)
210 itimerspec32_t dest32
;
212 if (copyin((void *)addr
, &dest32
, sizeof (itimerspec32_t
)) != 0)
215 ITIMERSPEC32_TO_ITIMERSPEC(dest
, &dest32
);
218 if (itimerspecfix(&dest
->it_value
) ||
219 (itimerspecfix(&dest
->it_interval
) &&
220 timerspecisset(&dest
->it_value
))) {
228 timerfd_copyout(itimerspec_t
*src
, uintptr_t addr
)
230 if (get_udatamodel() == DATAMODEL_NATIVE
) {
231 if (copyout(src
, (void *)addr
, sizeof (itimerspec_t
)) != 0)
234 itimerspec32_t src32
;
236 if (ITIMERSPEC_OVERFLOW(src
))
239 ITIMERSPEC_TO_ITIMERSPEC32(&src32
, src
);
241 if (copyout(&src32
, (void *)addr
, sizeof (itimerspec32_t
)) != 0)
250 timerfd_ioctl(dev_t dev
, int cmd
, intptr_t arg
, int md
, cred_t
*cr
, int *rv
)
252 itimerspec_t when
, oval
;
253 timerfd_state_t
*state
;
254 minor_t minor
= getminor(dev
);
258 state
= ddi_get_soft_state(timerfd_softstate
, minor
);
261 case TIMERFDIOC_CREATE
: {
262 if (arg
== TIMERFD_MONOTONIC
)
263 arg
= CLOCK_MONOTONIC
;
265 it
= timerfd_itimer_lock(state
);
267 if (it
->it_backend
!= NULL
) {
268 timerfd_itimer_unlock(state
, it
);
272 if ((it
->it_backend
= clock_get_backend(arg
)) == NULL
) {
273 timerfd_itimer_unlock(state
, it
);
278 * We need to provide a proc structure only for purposes
279 * of locking CLOCK_REALTIME-based timers -- it is safe to
284 err
= it
->it_backend
->clk_timer_create(it
, timerfd_fire
);
287 it
->it_backend
= NULL
;
288 timerfd_itimer_unlock(state
, it
);
292 it
->it_frontend
= state
;
293 timerfd_itimer_unlock(state
, it
);
298 case TIMERFDIOC_GETTIME
: {
299 it
= timerfd_itimer_lock(state
);
301 if (it
->it_backend
== NULL
) {
302 timerfd_itimer_unlock(state
, it
);
306 err
= it
->it_backend
->clk_timer_gettime(it
, &when
);
307 timerfd_itimer_unlock(state
, it
);
312 if ((err
= timerfd_copyout(&when
, arg
)) != 0)
318 case TIMERFDIOC_SETTIME
: {
319 timerfd_settime_t st
;
321 if (copyin((void *)arg
, &st
, sizeof (st
)) != 0)
324 if ((err
= timerfd_copyin(st
.tfd_settime_value
, &when
)) != 0)
327 it
= timerfd_itimer_lock(state
);
329 if (it
->it_backend
== NULL
) {
330 timerfd_itimer_unlock(state
, it
);
334 if (st
.tfd_settime_ovalue
!= NULL
) {
335 err
= it
->it_backend
->clk_timer_gettime(it
, &oval
);
338 timerfd_itimer_unlock(state
, it
);
344 * Before we set the time, we're going to clear tfd_fired.
345 * This can potentially race with the (old) timer firing, but
346 * the window is deceptively difficult to close: if we were
347 * to simply clear tfd_fired after the call to the backend
348 * returned, we would run the risk of plowing a firing of the
349 * new timer. Ultimately, the race can only be resolved by
350 * the backend, which would likely need to be extended with a
351 * function to call back into when the timer is between states
352 * (that is, after the timer can no longer fire with the old
353 * timer value, but before it can fire with the new one).
354 * This is straightforward enough for backends that set a
355 * timer's value by deleting the old one and adding the new
356 * one, but for those that modify the timer value in place
357 * (e.g., cyclics), the required serialization is necessarily
358 * delicate: the function would have to be callable from
359 * arbitrary interrupt context. While implementing all of
360 * this is possible, it does not (for the moment) seem worth
361 * it: if the timer is firing at essentially the same moment
362 * that it's being reprogrammed, there is a higher-level race
363 * with respect to timerfd usage that the progam itself will
364 * have to properly resolve -- and it seems reasonable to
365 * simply allow the program to resolve it in this case.
367 mutex_enter(&state
->tfd_lock
);
368 state
->tfd_fired
= 0;
369 mutex_exit(&state
->tfd_lock
);
371 err
= it
->it_backend
->clk_timer_settime(it
,
372 st
.tfd_settime_flags
& TFD_TIMER_ABSTIME
?
373 TIMER_ABSTIME
: TIMER_RELTIME
, &when
);
374 timerfd_itimer_unlock(state
, it
);
376 if (err
!= 0 || st
.tfd_settime_ovalue
== NULL
)
379 if ((err
= timerfd_copyout(&oval
, st
.tfd_settime_ovalue
)) != 0)
394 timerfd_close(dev_t dev
, int flag
, int otyp
, cred_t
*cred_p
)
396 timerfd_state_t
*state
, **sp
;
398 minor_t minor
= getminor(dev
);
400 state
= ddi_get_soft_state(timerfd_softstate
, minor
);
402 if (state
->tfd_pollhd
.ph_list
!= NULL
) {
403 pollwakeup(&state
->tfd_pollhd
, POLLERR
);
404 pollhead_clean(&state
->tfd_pollhd
);
408 * No one can get to this timer; we don't need to lock it -- we can
409 * just call on the backend to delete it.
411 it
= &state
->tfd_itimer
;
413 if (it
->it_backend
!= NULL
)
414 it
->it_backend
->clk_timer_delete(it
);
416 mutex_enter(&timerfd_lock
);
419 * Remove our state from our global list.
421 for (sp
= &timerfd_state
; *sp
!= state
; sp
= &((*sp
)->tfd_next
))
424 *sp
= (*sp
)->tfd_next
;
426 ddi_soft_state_free(timerfd_softstate
, minor
);
427 vmem_free(timerfd_minor
, (void *)(uintptr_t)minor
, 1);
429 mutex_exit(&timerfd_lock
);
435 timerfd_attach(dev_info_t
*devi
, ddi_attach_cmd_t cmd
)
442 return (DDI_SUCCESS
);
445 return (DDI_FAILURE
);
448 mutex_enter(&timerfd_lock
);
450 if (ddi_soft_state_init(&timerfd_softstate
,
451 sizeof (timerfd_state_t
), 0) != 0) {
452 cmn_err(CE_NOTE
, "/dev/timerfd failed to create soft state");
453 mutex_exit(&timerfd_lock
);
454 return (DDI_FAILURE
);
457 if (ddi_create_minor_node(devi
, "timerfd", S_IFCHR
,
458 TIMERFDMNRN_TIMERFD
, DDI_PSEUDO
, NULL
) == DDI_FAILURE
) {
459 cmn_err(CE_NOTE
, "/dev/timerfd couldn't create minor node");
460 ddi_soft_state_fini(&timerfd_softstate
);
461 mutex_exit(&timerfd_lock
);
462 return (DDI_FAILURE
);
465 ddi_report_dev(devi
);
468 timerfd_minor
= vmem_create("timerfd_minor", (void *)TIMERFDMNRN_CLONE
,
469 UINT32_MAX
- TIMERFDMNRN_CLONE
, 1, NULL
, NULL
, NULL
, 0,
470 VM_SLEEP
| VMC_IDENTIFIER
);
472 mutex_exit(&timerfd_lock
);
474 return (DDI_SUCCESS
);
479 timerfd_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
)
486 return (DDI_SUCCESS
);
489 return (DDI_FAILURE
);
492 mutex_enter(&timerfd_lock
);
493 vmem_destroy(timerfd_minor
);
495 ddi_remove_minor_node(timerfd_devi
, NULL
);
498 ddi_soft_state_fini(&timerfd_softstate
);
499 mutex_exit(&timerfd_lock
);
501 return (DDI_SUCCESS
);
506 timerfd_info(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
, void **result
)
511 case DDI_INFO_DEVT2DEVINFO
:
512 *result
= (void *)timerfd_devi
;
515 case DDI_INFO_DEVT2INSTANCE
:
525 static struct cb_ops timerfd_cb_ops
= {
526 timerfd_open
, /* open */
527 timerfd_close
, /* close */
528 nulldev
, /* strategy */
531 timerfd_read
, /* read */
533 timerfd_ioctl
, /* ioctl */
537 timerfd_poll
, /* poll */
538 ddi_prop_op
, /* cb_prop_op */
540 D_NEW
| D_MP
/* Driver compatibility flag */
543 static struct dev_ops timerfd_ops
= {
544 DEVO_REV
, /* devo_rev */
546 timerfd_info
, /* get_dev_info */
547 nulldev
, /* identify */
549 timerfd_attach
, /* attach */
550 timerfd_detach
, /* detach */
552 &timerfd_cb_ops
, /* driver operations */
553 NULL
, /* bus operations */
554 nodev
, /* dev power */
555 ddi_quiesce_not_needed
, /* quiesce */
558 static struct modldrv modldrv
= {
559 &mod_driverops
, /* module type (this is a pseudo driver) */
560 "timerfd support", /* name of module */
561 &timerfd_ops
, /* driver ops */
564 static struct modlinkage modlinkage
= {
573 return (mod_install(&modlinkage
));
577 _info(struct modinfo
*modinfop
)
579 return (mod_info(&modlinkage
, modinfop
));
585 return (mod_remove(&modlinkage
));