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 eventfd facility, a Linux-borne facility for user-generated
18 * file descriptor-based events.
22 #include <sys/sunddi.h>
23 #include <sys/eventfd.h>
26 #include <sys/sysmacros.h>
27 #include <sys/filio.h>
32 typedef struct eventfd_state eventfd_state_t
;
34 struct eventfd_state
{
35 kmutex_t efd_lock
; /* lock protecting state */
36 boolean_t efd_semaphore
; /* boolean: sema. semantics */
37 kcondvar_t efd_cv
; /* condvar */
38 pollhead_t efd_pollhd
; /* poll head */
39 uint64_t efd_value
; /* value */
40 eventfd_state_t
*efd_next
; /* next state on global list */
44 * Internal global variables.
46 static kmutex_t eventfd_lock
; /* lock protecting state */
47 static dev_info_t
*eventfd_devi
; /* device info */
48 static vmem_t
*eventfd_minor
; /* minor number arena */
49 static void *eventfd_softstate
; /* softstate pointer */
50 static eventfd_state_t
*eventfd_state
; /* global list of state */
54 eventfd_open(dev_t
*devp
, int flag
, int otyp
, cred_t
*cred_p
)
56 eventfd_state_t
*state
;
57 major_t major
= getemajor(*devp
);
58 minor_t minor
= getminor(*devp
);
60 if (minor
!= EVENTFDMNRN_EVENTFD
)
63 mutex_enter(&eventfd_lock
);
65 minor
= (minor_t
)(uintptr_t)vmem_alloc(eventfd_minor
, 1,
66 VM_BESTFIT
| VM_SLEEP
);
68 if (ddi_soft_state_zalloc(eventfd_softstate
, minor
) != DDI_SUCCESS
) {
69 vmem_free(eventfd_minor
, (void *)(uintptr_t)minor
, 1);
70 mutex_exit(&eventfd_lock
);
74 state
= ddi_get_soft_state(eventfd_softstate
, minor
);
75 *devp
= makedevice(major
, minor
);
77 state
->efd_next
= eventfd_state
;
78 eventfd_state
= state
;
80 mutex_exit(&eventfd_lock
);
87 eventfd_read(dev_t dev
, uio_t
*uio
, cred_t
*cr
)
89 eventfd_state_t
*state
;
90 minor_t minor
= getminor(dev
);
94 if (uio
->uio_resid
< sizeof (val
))
97 state
= ddi_get_soft_state(eventfd_softstate
, minor
);
99 mutex_enter(&state
->efd_lock
);
101 while (state
->efd_value
== 0) {
102 if (uio
->uio_fmode
& (FNDELAY
|FNONBLOCK
)) {
103 mutex_exit(&state
->efd_lock
);
107 if (!cv_wait_sig_swap(&state
->efd_cv
, &state
->efd_lock
)) {
108 mutex_exit(&state
->efd_lock
);
114 * We have a non-zero value and we own the lock; our behavior now
115 * depends on whether or not EFD_SEMAPHORE was set when the eventfd
118 val
= oval
= state
->efd_value
;
120 if (state
->efd_semaphore
) {
124 state
->efd_value
= 0;
127 err
= uiomove(&val
, sizeof (val
), UIO_READ
, uio
);
129 mutex_exit(&state
->efd_lock
);
131 if (oval
== EVENTFD_VALMAX
) {
132 cv_broadcast(&state
->efd_cv
);
133 pollwakeup(&state
->efd_pollhd
, POLLWRNORM
| POLLOUT
);
141 eventfd_write(dev_t dev
, struct uio
*uio
, cred_t
*credp
)
143 eventfd_state_t
*state
;
144 minor_t minor
= getminor(dev
);
148 if (uio
->uio_resid
< sizeof (val
))
151 if ((err
= uiomove(&val
, sizeof (val
), UIO_WRITE
, uio
)) != 0)
154 if (val
> EVENTFD_VALMAX
)
157 state
= ddi_get_soft_state(eventfd_softstate
, minor
);
159 mutex_enter(&state
->efd_lock
);
161 while (val
> EVENTFD_VALMAX
- state
->efd_value
) {
162 if (uio
->uio_fmode
& (FNDELAY
|FNONBLOCK
)) {
163 mutex_exit(&state
->efd_lock
);
167 if (!cv_wait_sig_swap(&state
->efd_cv
, &state
->efd_lock
)) {
168 mutex_exit(&state
->efd_lock
);
174 * We now know that we can add the value without overflowing.
176 state
->efd_value
= (oval
= state
->efd_value
) + val
;
178 mutex_exit(&state
->efd_lock
);
181 cv_broadcast(&state
->efd_cv
);
182 pollwakeup(&state
->efd_pollhd
, POLLRDNORM
| POLLIN
);
190 eventfd_poll(dev_t dev
, short events
, int anyyet
, short *reventsp
,
191 struct pollhead
**phpp
)
193 eventfd_state_t
*state
;
194 minor_t minor
= getminor(dev
);
197 state
= ddi_get_soft_state(eventfd_softstate
, minor
);
199 mutex_enter(&state
->efd_lock
);
201 if (state
->efd_value
> 0)
202 revents
|= POLLRDNORM
| POLLIN
;
204 if (state
->efd_value
< EVENTFD_VALMAX
)
205 revents
|= POLLWRNORM
| POLLOUT
;
207 if (!(*reventsp
= revents
& events
) && !anyyet
)
208 *phpp
= &state
->efd_pollhd
;
210 mutex_exit(&state
->efd_lock
);
217 eventfd_ioctl(dev_t dev
, int cmd
, intptr_t arg
, int md
, cred_t
*cr
, int *rv
)
219 eventfd_state_t
*state
;
220 minor_t minor
= getminor(dev
);
222 state
= ddi_get_soft_state(eventfd_softstate
, minor
);
225 case EVENTFDIOC_SEMAPHORE
: {
226 mutex_enter(&state
->efd_lock
);
227 state
->efd_semaphore
^= 1;
228 mutex_exit(&state
->efd_lock
);
242 eventfd_close(dev_t dev
, int flag
, int otyp
, cred_t
*cred_p
)
244 eventfd_state_t
*state
, **sp
;
245 minor_t minor
= getminor(dev
);
247 state
= ddi_get_soft_state(eventfd_softstate
, minor
);
249 if (state
->efd_pollhd
.ph_list
!= NULL
) {
250 pollwakeup(&state
->efd_pollhd
, POLLERR
);
251 pollhead_clean(&state
->efd_pollhd
);
254 mutex_enter(&eventfd_lock
);
257 * Remove our state from our global list.
259 for (sp
= &eventfd_state
; *sp
!= state
; sp
= &((*sp
)->efd_next
))
262 *sp
= (*sp
)->efd_next
;
264 ddi_soft_state_free(eventfd_softstate
, minor
);
265 vmem_free(eventfd_minor
, (void *)(uintptr_t)minor
, 1);
267 mutex_exit(&eventfd_lock
);
273 eventfd_attach(dev_info_t
*devi
, ddi_attach_cmd_t cmd
)
280 return (DDI_SUCCESS
);
283 return (DDI_FAILURE
);
286 mutex_enter(&eventfd_lock
);
288 if (ddi_soft_state_init(&eventfd_softstate
,
289 sizeof (eventfd_state_t
), 0) != 0) {
290 cmn_err(CE_NOTE
, "/dev/eventfd failed to create soft state");
291 mutex_exit(&eventfd_lock
);
292 return (DDI_FAILURE
);
295 if (ddi_create_minor_node(devi
, "eventfd", S_IFCHR
,
296 EVENTFDMNRN_EVENTFD
, DDI_PSEUDO
, NULL
) == DDI_FAILURE
) {
297 cmn_err(CE_NOTE
, "/dev/eventfd couldn't create minor node");
298 ddi_soft_state_fini(&eventfd_softstate
);
299 mutex_exit(&eventfd_lock
);
300 return (DDI_FAILURE
);
303 ddi_report_dev(devi
);
306 eventfd_minor
= vmem_create("eventfd_minor", (void *)EVENTFDMNRN_CLONE
,
307 UINT32_MAX
- EVENTFDMNRN_CLONE
, 1, NULL
, NULL
, NULL
, 0,
308 VM_SLEEP
| VMC_IDENTIFIER
);
310 mutex_exit(&eventfd_lock
);
312 return (DDI_SUCCESS
);
317 eventfd_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
)
324 return (DDI_SUCCESS
);
327 return (DDI_FAILURE
);
330 mutex_enter(&eventfd_lock
);
331 vmem_destroy(eventfd_minor
);
333 ddi_remove_minor_node(eventfd_devi
, NULL
);
336 ddi_soft_state_fini(&eventfd_softstate
);
337 mutex_exit(&eventfd_lock
);
339 return (DDI_SUCCESS
);
344 eventfd_info(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
, void **result
)
349 case DDI_INFO_DEVT2DEVINFO
:
350 *result
= (void *)eventfd_devi
;
353 case DDI_INFO_DEVT2INSTANCE
:
363 static struct cb_ops eventfd_cb_ops
= {
364 eventfd_open
, /* open */
365 eventfd_close
, /* close */
366 nulldev
, /* strategy */
369 eventfd_read
, /* read */
370 eventfd_write
, /* write */
371 eventfd_ioctl
, /* ioctl */
375 eventfd_poll
, /* poll */
376 ddi_prop_op
, /* cb_prop_op */
378 D_NEW
| D_MP
/* Driver compatibility flag */
381 static struct dev_ops eventfd_ops
= {
382 DEVO_REV
, /* devo_rev */
384 eventfd_info
, /* get_dev_info */
385 nulldev
, /* identify */
387 eventfd_attach
, /* attach */
388 eventfd_detach
, /* detach */
390 &eventfd_cb_ops
, /* driver operations */
391 NULL
, /* bus operations */
392 nodev
, /* dev power */
393 ddi_quiesce_not_needed
, /* quiesce */
396 static struct modldrv modldrv
= {
397 &mod_driverops
, /* module type (this is a pseudo driver) */
398 "eventfd support", /* name of module */
399 &eventfd_ops
, /* driver ops */
402 static struct modlinkage modlinkage
= {
411 return (mod_install(&modlinkage
));
415 _info(struct modinfo
*modinfop
)
417 return (mod_info(&modlinkage
, modinfop
));
423 return (mod_remove(&modlinkage
));