xref: /illumos-gate/usr/src/uts/common/io/eventfd.c (revision 3deeb0a9)
11767006bSBryan Cantrill /*
21767006bSBryan Cantrill  * This file and its contents are supplied under the terms of the
31767006bSBryan Cantrill  * Common Development and Distribution License ("CDDL"), version 1.0.
41767006bSBryan Cantrill  * You may only use this file in accordance with the terms of version
51767006bSBryan Cantrill  * 1.0 of the CDDL.
61767006bSBryan Cantrill  *
71767006bSBryan Cantrill  * A full copy of the text of the CDDL should have accompanied this
81767006bSBryan Cantrill  * source.  A copy of the CDDL is also available via the Internet at
91767006bSBryan Cantrill  * http://www.illumos.org/license/CDDL.
101767006bSBryan Cantrill  */
111767006bSBryan Cantrill 
121767006bSBryan Cantrill /*
1380d5689fSPatrick Mooney  * Copyright 2017 Joyent, Inc.
141767006bSBryan Cantrill  */
151767006bSBryan Cantrill 
161767006bSBryan Cantrill /*
171767006bSBryan Cantrill  * Support for the eventfd facility, a Linux-borne facility for user-generated
181767006bSBryan Cantrill  * file descriptor-based events.
191767006bSBryan Cantrill  */
201767006bSBryan Cantrill 
211767006bSBryan Cantrill #include <sys/ddi.h>
221767006bSBryan Cantrill #include <sys/sunddi.h>
231767006bSBryan Cantrill #include <sys/eventfd.h>
241767006bSBryan Cantrill #include <sys/conf.h>
251767006bSBryan Cantrill #include <sys/vmem.h>
261767006bSBryan Cantrill #include <sys/sysmacros.h>
271767006bSBryan Cantrill #include <sys/filio.h>
281767006bSBryan Cantrill #include <sys/stat.h>
291767006bSBryan Cantrill #include <sys/file.h>
301767006bSBryan Cantrill 
311767006bSBryan Cantrill struct eventfd_state;
321767006bSBryan Cantrill typedef struct eventfd_state eventfd_state_t;
331767006bSBryan Cantrill 
341767006bSBryan Cantrill struct eventfd_state {
351767006bSBryan Cantrill 	kmutex_t efd_lock;			/* lock protecting state */
361767006bSBryan Cantrill 	boolean_t efd_semaphore;		/* boolean: sema. semantics */
371767006bSBryan Cantrill 	kcondvar_t efd_cv;			/* condvar */
381767006bSBryan Cantrill 	pollhead_t efd_pollhd;			/* poll head */
391767006bSBryan Cantrill 	uint64_t efd_value;			/* value */
40860884ebSPatrick Mooney 	size_t efd_bwriters;			/* count of blocked writers */
411767006bSBryan Cantrill 	eventfd_state_t *efd_next;		/* next state on global list */
421767006bSBryan Cantrill };
431767006bSBryan Cantrill 
441767006bSBryan Cantrill /*
451767006bSBryan Cantrill  * Internal global variables.
461767006bSBryan Cantrill  */
471767006bSBryan Cantrill static kmutex_t		eventfd_lock;		/* lock protecting state */
481767006bSBryan Cantrill static dev_info_t	*eventfd_devi;		/* device info */
491767006bSBryan Cantrill static vmem_t		*eventfd_minor;		/* minor number arena */
501767006bSBryan Cantrill static void		*eventfd_softstate;	/* softstate pointer */
511767006bSBryan Cantrill static eventfd_state_t	*eventfd_state;		/* global list of state */
521767006bSBryan Cantrill 
531767006bSBryan Cantrill /*ARGSUSED*/
541767006bSBryan Cantrill static int
eventfd_open(dev_t * devp,int flag,int otyp,cred_t * cred_p)551767006bSBryan Cantrill eventfd_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
561767006bSBryan Cantrill {
571767006bSBryan Cantrill 	eventfd_state_t *state;
581767006bSBryan Cantrill 	major_t major = getemajor(*devp);
591767006bSBryan Cantrill 	minor_t minor = getminor(*devp);
601767006bSBryan Cantrill 
611767006bSBryan Cantrill 	if (minor != EVENTFDMNRN_EVENTFD)
621767006bSBryan Cantrill 		return (ENXIO);
631767006bSBryan Cantrill 
641767006bSBryan Cantrill 	mutex_enter(&eventfd_lock);
651767006bSBryan Cantrill 
661767006bSBryan Cantrill 	minor = (minor_t)(uintptr_t)vmem_alloc(eventfd_minor, 1,
671767006bSBryan Cantrill 	    VM_BESTFIT | VM_SLEEP);
681767006bSBryan Cantrill 
691767006bSBryan Cantrill 	if (ddi_soft_state_zalloc(eventfd_softstate, minor) != DDI_SUCCESS) {
701767006bSBryan Cantrill 		vmem_free(eventfd_minor, (void *)(uintptr_t)minor, 1);
711767006bSBryan Cantrill 		mutex_exit(&eventfd_lock);
72*3deeb0a9SToomas Soome 		return (ENXIO);
731767006bSBryan Cantrill 	}
741767006bSBryan Cantrill 
751767006bSBryan Cantrill 	state = ddi_get_soft_state(eventfd_softstate, minor);
761767006bSBryan Cantrill 	*devp = makedevice(major, minor);
771767006bSBryan Cantrill 
781767006bSBryan Cantrill 	state->efd_next = eventfd_state;
791767006bSBryan Cantrill 	eventfd_state = state;
801767006bSBryan Cantrill 
811767006bSBryan Cantrill 	mutex_exit(&eventfd_lock);
821767006bSBryan Cantrill 
831767006bSBryan Cantrill 	return (0);
841767006bSBryan Cantrill }
851767006bSBryan Cantrill 
861767006bSBryan Cantrill /*ARGSUSED*/
871767006bSBryan Cantrill static int
eventfd_read(dev_t dev,uio_t * uio,cred_t * cr)881767006bSBryan Cantrill eventfd_read(dev_t dev, uio_t *uio, cred_t *cr)
891767006bSBryan Cantrill {
901767006bSBryan Cantrill 	eventfd_state_t *state;
911767006bSBryan Cantrill 	minor_t minor = getminor(dev);
921767006bSBryan Cantrill 	uint64_t val, oval;
931767006bSBryan Cantrill 	int err;
941767006bSBryan Cantrill 
951767006bSBryan Cantrill 	if (uio->uio_resid < sizeof (val))
961767006bSBryan Cantrill 		return (EINVAL);
971767006bSBryan Cantrill 
981767006bSBryan Cantrill 	state = ddi_get_soft_state(eventfd_softstate, minor);
991767006bSBryan Cantrill 
1001767006bSBryan Cantrill 	mutex_enter(&state->efd_lock);
1011767006bSBryan Cantrill 
1021767006bSBryan Cantrill 	while (state->efd_value == 0) {
1031767006bSBryan Cantrill 		if (uio->uio_fmode & (FNDELAY|FNONBLOCK)) {
1041767006bSBryan Cantrill 			mutex_exit(&state->efd_lock);
1051767006bSBryan Cantrill 			return (EAGAIN);
1061767006bSBryan Cantrill 		}
1071767006bSBryan Cantrill 
1081767006bSBryan Cantrill 		if (!cv_wait_sig_swap(&state->efd_cv, &state->efd_lock)) {
1091767006bSBryan Cantrill 			mutex_exit(&state->efd_lock);
1101767006bSBryan Cantrill 			return (EINTR);
1111767006bSBryan Cantrill 		}
1121767006bSBryan Cantrill 	}
1131767006bSBryan Cantrill 
1141767006bSBryan Cantrill 	/*
1151767006bSBryan Cantrill 	 * We have a non-zero value and we own the lock; our behavior now
1161767006bSBryan Cantrill 	 * depends on whether or not EFD_SEMAPHORE was set when the eventfd
1171767006bSBryan Cantrill 	 * was created.
1181767006bSBryan Cantrill 	 */
1191767006bSBryan Cantrill 	val = oval = state->efd_value;
1201767006bSBryan Cantrill 
1211767006bSBryan Cantrill 	if (state->efd_semaphore) {
1221767006bSBryan Cantrill 		state->efd_value--;
1231767006bSBryan Cantrill 		val = 1;
1241767006bSBryan Cantrill 	} else {
1251767006bSBryan Cantrill 		state->efd_value = 0;
1261767006bSBryan Cantrill 	}
1271767006bSBryan Cantrill 
1281767006bSBryan Cantrill 	err = uiomove(&val, sizeof (val), UIO_READ, uio);
1291767006bSBryan Cantrill 
130860884ebSPatrick Mooney 	/*
131860884ebSPatrick Mooney 	 * Wake any writers blocked on this eventfd as this read operation may
132860884ebSPatrick Mooney 	 * have created adequate capacity for their values.
133860884ebSPatrick Mooney 	 */
134860884ebSPatrick Mooney 	if (state->efd_bwriters != 0) {
135860884ebSPatrick Mooney 		cv_broadcast(&state->efd_cv);
136860884ebSPatrick Mooney 	}
1371767006bSBryan Cantrill 	mutex_exit(&state->efd_lock);
1381767006bSBryan Cantrill 
139860884ebSPatrick Mooney 	/*
140860884ebSPatrick Mooney 	 * It is necessary to emit POLLOUT events only when the eventfd
141860884ebSPatrick Mooney 	 * transitions from EVENTFD_VALMAX to a lower value.  At all other
142860884ebSPatrick Mooney 	 * times, it is already considered writable by poll.
143860884ebSPatrick Mooney 	 */
1441767006bSBryan Cantrill 	if (oval == EVENTFD_VALMAX) {
1451767006bSBryan Cantrill 		pollwakeup(&state->efd_pollhd, POLLWRNORM | POLLOUT);
1461767006bSBryan Cantrill 	}
1471767006bSBryan Cantrill 
1481767006bSBryan Cantrill 	return (err);
1491767006bSBryan Cantrill }
1501767006bSBryan Cantrill 
1511767006bSBryan Cantrill /*ARGSUSED*/
1521767006bSBryan Cantrill static int
eventfd_write(dev_t dev,struct uio * uio,cred_t * credp)1531767006bSBryan Cantrill eventfd_write(dev_t dev, struct uio *uio, cred_t *credp)
1541767006bSBryan Cantrill {
1551767006bSBryan Cantrill 	eventfd_state_t *state;
1561767006bSBryan Cantrill 	minor_t minor = getminor(dev);
1571767006bSBryan Cantrill 	uint64_t val, oval;
1581767006bSBryan Cantrill 	int err;
1591767006bSBryan Cantrill 
1601767006bSBryan Cantrill 	if (uio->uio_resid < sizeof (val))
1611767006bSBryan Cantrill 		return (EINVAL);
1621767006bSBryan Cantrill 
1631767006bSBryan Cantrill 	if ((err = uiomove(&val, sizeof (val), UIO_WRITE, uio)) != 0)
1641767006bSBryan Cantrill 		return (err);
1651767006bSBryan Cantrill 
1661767006bSBryan Cantrill 	if (val > EVENTFD_VALMAX)
1671767006bSBryan Cantrill 		return (EINVAL);
1681767006bSBryan Cantrill 
1691767006bSBryan Cantrill 	state = ddi_get_soft_state(eventfd_softstate, minor);
1701767006bSBryan Cantrill 
1711767006bSBryan Cantrill 	mutex_enter(&state->efd_lock);
1721767006bSBryan Cantrill 
1731767006bSBryan Cantrill 	while (val > EVENTFD_VALMAX - state->efd_value) {
1741767006bSBryan Cantrill 		if (uio->uio_fmode & (FNDELAY|FNONBLOCK)) {
1751767006bSBryan Cantrill 			mutex_exit(&state->efd_lock);
1761767006bSBryan Cantrill 			return (EAGAIN);
1771767006bSBryan Cantrill 		}
1781767006bSBryan Cantrill 
179860884ebSPatrick Mooney 		state->efd_bwriters++;
1801767006bSBryan Cantrill 		if (!cv_wait_sig_swap(&state->efd_cv, &state->efd_lock)) {
181860884ebSPatrick Mooney 			state->efd_bwriters--;
1821767006bSBryan Cantrill 			mutex_exit(&state->efd_lock);
1831767006bSBryan Cantrill 			return (EINTR);
1841767006bSBryan Cantrill 		}
185860884ebSPatrick Mooney 		state->efd_bwriters--;
1861767006bSBryan Cantrill 	}
1871767006bSBryan Cantrill 
1881767006bSBryan Cantrill 	/*
1891767006bSBryan Cantrill 	 * We now know that we can add the value without overflowing.
1901767006bSBryan Cantrill 	 */
1911767006bSBryan Cantrill 	state->efd_value = (oval = state->efd_value) + val;
1921767006bSBryan Cantrill 
193860884ebSPatrick Mooney 	/*
194860884ebSPatrick Mooney 	 * If the value was previously "empty", notify blocked readers that
195860884ebSPatrick Mooney 	 * data is available.
196860884ebSPatrick Mooney 	 */
197860884ebSPatrick Mooney 	if (oval == 0) {
198860884ebSPatrick Mooney 		cv_broadcast(&state->efd_cv);
199860884ebSPatrick Mooney 	}
2001767006bSBryan Cantrill 	mutex_exit(&state->efd_lock);
2011767006bSBryan Cantrill 
202860884ebSPatrick Mooney 	/*
203860884ebSPatrick Mooney 	 * Notify pollers as well if the eventfd is now readable.
204860884ebSPatrick Mooney 	 */
2051767006bSBryan Cantrill 	if (oval == 0) {
2061767006bSBryan Cantrill 		pollwakeup(&state->efd_pollhd, POLLRDNORM | POLLIN);
2071767006bSBryan Cantrill 	}
2081767006bSBryan Cantrill 
2091767006bSBryan Cantrill 	return (0);
2101767006bSBryan Cantrill }
2111767006bSBryan Cantrill 
2121767006bSBryan Cantrill /*ARGSUSED*/
2131767006bSBryan Cantrill static int
eventfd_poll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)2141767006bSBryan Cantrill eventfd_poll(dev_t dev, short events, int anyyet, short *reventsp,
2151767006bSBryan Cantrill     struct pollhead **phpp)
2161767006bSBryan Cantrill {
2171767006bSBryan Cantrill 	eventfd_state_t *state;
2181767006bSBryan Cantrill 	minor_t minor = getminor(dev);
2191767006bSBryan Cantrill 	short revents = 0;
2201767006bSBryan Cantrill 
2211767006bSBryan Cantrill 	state = ddi_get_soft_state(eventfd_softstate, minor);
2221767006bSBryan Cantrill 
2231767006bSBryan Cantrill 	mutex_enter(&state->efd_lock);
2241767006bSBryan Cantrill 
2251767006bSBryan Cantrill 	if (state->efd_value > 0)
2261767006bSBryan Cantrill 		revents |= POLLRDNORM | POLLIN;
2271767006bSBryan Cantrill 
2281767006bSBryan Cantrill 	if (state->efd_value < EVENTFD_VALMAX)
2291767006bSBryan Cantrill 		revents |= POLLWRNORM | POLLOUT;
2301767006bSBryan Cantrill 
23180d5689fSPatrick Mooney 	*reventsp = revents & events;
23280d5689fSPatrick Mooney 	if ((*reventsp == 0 && !anyyet) || (events & POLLET)) {
2331767006bSBryan Cantrill 		*phpp = &state->efd_pollhd;
23480d5689fSPatrick Mooney 	}
2351767006bSBryan Cantrill 
2361767006bSBryan Cantrill 	mutex_exit(&state->efd_lock);
2371767006bSBryan Cantrill 
2381767006bSBryan Cantrill 	return (0);
2391767006bSBryan Cantrill }
2401767006bSBryan Cantrill 
2411767006bSBryan Cantrill /*ARGSUSED*/
2421767006bSBryan Cantrill static int
eventfd_ioctl(dev_t dev,int cmd,intptr_t arg,int md,cred_t * cr,int * rv)2431767006bSBryan Cantrill eventfd_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv)
2441767006bSBryan Cantrill {
2451767006bSBryan Cantrill 	eventfd_state_t *state;
2461767006bSBryan Cantrill 	minor_t minor = getminor(dev);
2471767006bSBryan Cantrill 
2481767006bSBryan Cantrill 	state = ddi_get_soft_state(eventfd_softstate, minor);
2491767006bSBryan Cantrill 
2501767006bSBryan Cantrill 	switch (cmd) {
2511767006bSBryan Cantrill 	case EVENTFDIOC_SEMAPHORE: {
2521767006bSBryan Cantrill 		mutex_enter(&state->efd_lock);
2531767006bSBryan Cantrill 		state->efd_semaphore ^= 1;
2541767006bSBryan Cantrill 		mutex_exit(&state->efd_lock);
2551767006bSBryan Cantrill 
2561767006bSBryan Cantrill 		return (0);
2571767006bSBryan Cantrill 	}
2581767006bSBryan Cantrill 
2591767006bSBryan Cantrill 	default:
2601767006bSBryan Cantrill 		break;
2611767006bSBryan Cantrill 	}
2621767006bSBryan Cantrill 
2631767006bSBryan Cantrill 	return (ENOTTY);
2641767006bSBryan Cantrill }
2651767006bSBryan Cantrill 
2661767006bSBryan Cantrill /*ARGSUSED*/
2671767006bSBryan Cantrill static int
eventfd_close(dev_t dev,int flag,int otyp,cred_t * cred_p)2681767006bSBryan Cantrill eventfd_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
2691767006bSBryan Cantrill {
2701767006bSBryan Cantrill 	eventfd_state_t *state, **sp;
2711767006bSBryan Cantrill 	minor_t minor = getminor(dev);
2721767006bSBryan Cantrill 
2731767006bSBryan Cantrill 	state = ddi_get_soft_state(eventfd_softstate, minor);
2741767006bSBryan Cantrill 
2751767006bSBryan Cantrill 	if (state->efd_pollhd.ph_list != NULL) {
2761767006bSBryan Cantrill 		pollwakeup(&state->efd_pollhd, POLLERR);
2771767006bSBryan Cantrill 		pollhead_clean(&state->efd_pollhd);
2781767006bSBryan Cantrill 	}
2791767006bSBryan Cantrill 
2801767006bSBryan Cantrill 	mutex_enter(&eventfd_lock);
2811767006bSBryan Cantrill 
2821767006bSBryan Cantrill 	/*
2831767006bSBryan Cantrill 	 * Remove our state from our global list.
2841767006bSBryan Cantrill 	 */
2851767006bSBryan Cantrill 	for (sp = &eventfd_state; *sp != state; sp = &((*sp)->efd_next))
2861767006bSBryan Cantrill 		VERIFY(*sp != NULL);
2871767006bSBryan Cantrill 
2881767006bSBryan Cantrill 	*sp = (*sp)->efd_next;
2891767006bSBryan Cantrill 
2901767006bSBryan Cantrill 	ddi_soft_state_free(eventfd_softstate, minor);
2911767006bSBryan Cantrill 	vmem_free(eventfd_minor, (void *)(uintptr_t)minor, 1);
2921767006bSBryan Cantrill 
2931767006bSBryan Cantrill 	mutex_exit(&eventfd_lock);
2941767006bSBryan Cantrill 
2951767006bSBryan Cantrill 	return (0);
2961767006bSBryan Cantrill }
2971767006bSBryan Cantrill 
2981767006bSBryan Cantrill static int
eventfd_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)2991767006bSBryan Cantrill eventfd_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
3001767006bSBryan Cantrill {
3011767006bSBryan Cantrill 	switch (cmd) {
3021767006bSBryan Cantrill 	case DDI_ATTACH:
3031767006bSBryan Cantrill 		break;
3041767006bSBryan Cantrill 
3051767006bSBryan Cantrill 	case DDI_RESUME:
3061767006bSBryan Cantrill 		return (DDI_SUCCESS);
3071767006bSBryan Cantrill 
3081767006bSBryan Cantrill 	default:
3091767006bSBryan Cantrill 		return (DDI_FAILURE);
3101767006bSBryan Cantrill 	}
3111767006bSBryan Cantrill 
3121767006bSBryan Cantrill 	mutex_enter(&eventfd_lock);
3131767006bSBryan Cantrill 
3141767006bSBryan Cantrill 	if (ddi_soft_state_init(&eventfd_softstate,
3151767006bSBryan Cantrill 	    sizeof (eventfd_state_t), 0) != 0) {
3161767006bSBryan Cantrill 		cmn_err(CE_NOTE, "/dev/eventfd failed to create soft state");
3171767006bSBryan Cantrill 		mutex_exit(&eventfd_lock);
3181767006bSBryan Cantrill 		return (DDI_FAILURE);
3191767006bSBryan Cantrill 	}
3201767006bSBryan Cantrill 
3211767006bSBryan Cantrill 	if (ddi_create_minor_node(devi, "eventfd", S_IFCHR,
322*3deeb0a9SToomas Soome 	    EVENTFDMNRN_EVENTFD, DDI_PSEUDO, 0) == DDI_FAILURE) {
3231767006bSBryan Cantrill 		cmn_err(CE_NOTE, "/dev/eventfd couldn't create minor node");
3241767006bSBryan Cantrill 		ddi_soft_state_fini(&eventfd_softstate);
3251767006bSBryan Cantrill 		mutex_exit(&eventfd_lock);
3261767006bSBryan Cantrill 		return (DDI_FAILURE);
3271767006bSBryan Cantrill 	}
3281767006bSBryan Cantrill 
3291767006bSBryan Cantrill 	ddi_report_dev(devi);
3301767006bSBryan Cantrill 	eventfd_devi = devi;
3311767006bSBryan Cantrill 
3321767006bSBryan Cantrill 	eventfd_minor = vmem_create("eventfd_minor", (void *)EVENTFDMNRN_CLONE,
3331767006bSBryan Cantrill 	    UINT32_MAX - EVENTFDMNRN_CLONE, 1, NULL, NULL, NULL, 0,
3341767006bSBryan Cantrill 	    VM_SLEEP | VMC_IDENTIFIER);
3351767006bSBryan Cantrill 
3361767006bSBryan Cantrill 	mutex_exit(&eventfd_lock);
3371767006bSBryan Cantrill 
3381767006bSBryan Cantrill 	return (DDI_SUCCESS);
3391767006bSBryan Cantrill }
3401767006bSBryan Cantrill 
3411767006bSBryan Cantrill /*ARGSUSED*/
3421767006bSBryan Cantrill static int
eventfd_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3431767006bSBryan Cantrill eventfd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3441767006bSBryan Cantrill {
3451767006bSBryan Cantrill 	switch (cmd) {
3461767006bSBryan Cantrill 	case DDI_DETACH:
3471767006bSBryan Cantrill 		break;
3481767006bSBryan Cantrill 
3491767006bSBryan Cantrill 	case DDI_SUSPEND:
3501767006bSBryan Cantrill 		return (DDI_SUCCESS);
3511767006bSBryan Cantrill 
3521767006bSBryan Cantrill 	default:
3531767006bSBryan Cantrill 		return (DDI_FAILURE);
3541767006bSBryan Cantrill 	}
3551767006bSBryan Cantrill 
3561767006bSBryan Cantrill 	mutex_enter(&eventfd_lock);
3571767006bSBryan Cantrill 	vmem_destroy(eventfd_minor);
3581767006bSBryan Cantrill 
3591767006bSBryan Cantrill 	ddi_remove_minor_node(eventfd_devi, NULL);
3601767006bSBryan Cantrill 	eventfd_devi = NULL;
3611767006bSBryan Cantrill 
3621767006bSBryan Cantrill 	ddi_soft_state_fini(&eventfd_softstate);
3631767006bSBryan Cantrill 	mutex_exit(&eventfd_lock);
3641767006bSBryan Cantrill 
3651767006bSBryan Cantrill 	return (DDI_SUCCESS);
3661767006bSBryan Cantrill }
3671767006bSBryan Cantrill 
3681767006bSBryan Cantrill /*ARGSUSED*/
3691767006bSBryan Cantrill static int
eventfd_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)3701767006bSBryan Cantrill eventfd_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
3711767006bSBryan Cantrill {
3721767006bSBryan Cantrill 	int error;
3731767006bSBryan Cantrill 
3741767006bSBryan Cantrill 	switch (infocmd) {
3751767006bSBryan Cantrill 	case DDI_INFO_DEVT2DEVINFO:
3761767006bSBryan Cantrill 		*result = (void *)eventfd_devi;
3771767006bSBryan Cantrill 		error = DDI_SUCCESS;
3781767006bSBryan Cantrill 		break;
3791767006bSBryan Cantrill 	case DDI_INFO_DEVT2INSTANCE:
3801767006bSBryan Cantrill 		*result = (void *)0;
3811767006bSBryan Cantrill 		error = DDI_SUCCESS;
3821767006bSBryan Cantrill 		break;
3831767006bSBryan Cantrill 	default:
3841767006bSBryan Cantrill 		error = DDI_FAILURE;
3851767006bSBryan Cantrill 	}
3861767006bSBryan Cantrill 	return (error);
3871767006bSBryan Cantrill }
3881767006bSBryan Cantrill 
3891767006bSBryan Cantrill static struct cb_ops eventfd_cb_ops = {
3901767006bSBryan Cantrill 	eventfd_open,		/* open */
3911767006bSBryan Cantrill 	eventfd_close,		/* close */
3921767006bSBryan Cantrill 	nulldev,		/* strategy */
3931767006bSBryan Cantrill 	nulldev,		/* print */
3941767006bSBryan Cantrill 	nodev,			/* dump */
3951767006bSBryan Cantrill 	eventfd_read,		/* read */
3961767006bSBryan Cantrill 	eventfd_write,		/* write */
3971767006bSBryan Cantrill 	eventfd_ioctl,		/* ioctl */
3981767006bSBryan Cantrill 	nodev,			/* devmap */
3991767006bSBryan Cantrill 	nodev,			/* mmap */
4001767006bSBryan Cantrill 	nodev,			/* segmap */
4011767006bSBryan Cantrill 	eventfd_poll,		/* poll */
4021767006bSBryan Cantrill 	ddi_prop_op,		/* cb_prop_op */
4031767006bSBryan Cantrill 	0,			/* streamtab  */
4041767006bSBryan Cantrill 	D_NEW | D_MP		/* Driver compatibility flag */
4051767006bSBryan Cantrill };
4061767006bSBryan Cantrill 
4071767006bSBryan Cantrill static struct dev_ops eventfd_ops = {
4081767006bSBryan Cantrill 	DEVO_REV,		/* devo_rev */
4091767006bSBryan Cantrill 	0,			/* refcnt */
4101767006bSBryan Cantrill 	eventfd_info,		/* get_dev_info */
4111767006bSBryan Cantrill 	nulldev,		/* identify */
4121767006bSBryan Cantrill 	nulldev,		/* probe */
4131767006bSBryan Cantrill 	eventfd_attach,		/* attach */
4141767006bSBryan Cantrill 	eventfd_detach,		/* detach */
4151767006bSBryan Cantrill 	nodev,			/* reset */
4161767006bSBryan Cantrill 	&eventfd_cb_ops,	/* driver operations */
4171767006bSBryan Cantrill 	NULL,			/* bus operations */
4181767006bSBryan Cantrill 	nodev,			/* dev power */
4191767006bSBryan Cantrill 	ddi_quiesce_not_needed,	/* quiesce */
4201767006bSBryan Cantrill };
4211767006bSBryan Cantrill 
4221767006bSBryan Cantrill static struct modldrv modldrv = {
4231767006bSBryan Cantrill 	&mod_driverops,		/* module type (this is a pseudo driver) */
4241767006bSBryan Cantrill 	"eventfd support",	/* name of module */
4251767006bSBryan Cantrill 	&eventfd_ops,		/* driver ops */
4261767006bSBryan Cantrill };
4271767006bSBryan Cantrill 
4281767006bSBryan Cantrill static struct modlinkage modlinkage = {
4291767006bSBryan Cantrill 	MODREV_1,
4301767006bSBryan Cantrill 	(void *)&modldrv,
4311767006bSBryan Cantrill 	NULL
4321767006bSBryan Cantrill };
4331767006bSBryan Cantrill 
4341767006bSBryan Cantrill int
_init(void)4351767006bSBryan Cantrill _init(void)
4361767006bSBryan Cantrill {
4371767006bSBryan Cantrill 	return (mod_install(&modlinkage));
4381767006bSBryan Cantrill }
4391767006bSBryan Cantrill 
4401767006bSBryan Cantrill int
_info(struct modinfo * modinfop)4411767006bSBryan Cantrill _info(struct modinfo *modinfop)
4421767006bSBryan Cantrill {
4431767006bSBryan Cantrill 	return (mod_info(&modlinkage, modinfop));
4441767006bSBryan Cantrill }
4451767006bSBryan Cantrill 
4461767006bSBryan Cantrill int
_fini(void)4471767006bSBryan Cantrill _fini(void)
4481767006bSBryan Cantrill {
4491767006bSBryan Cantrill 	return (mod_remove(&modlinkage));
4501767006bSBryan Cantrill }
451