xref: /illumos-gate/usr/src/lib/libc/port/sys/epoll.c (revision 3bfdbb4947361851ca9626e6f9c967cdfa67a24e)
1 /*
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
5  * 1.0 of the CDDL.
6  *
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.
10  */
11 
12 /*
13  * Copyright 2016 Joyent, Inc.
14  */
15 
16 #include <sys/types.h>
17 #include <sys/epoll.h>
18 #include <sys/devpoll.h>
19 #include <unistd.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <poll.h>
23 
24 /*
25  * Events that match their epoll(7) equivalents.
26  */
27 #if EPOLLIN != POLLIN
28 #error value of EPOLLIN does not match value of POLLIN
29 #endif
30 
31 #if EPOLLPRI != POLLPRI
32 #error value of EPOLLPRI does not match value of POLLPRI
33 #endif
34 
35 #if EPOLLOUT != POLLOUT
36 #error value of EPOLLOUT does not match value of POLLOUT
37 #endif
38 
39 #if EPOLLRDNORM != POLLRDNORM
40 #error value of EPOLLRDNORM does not match value of POLLRDNORM
41 #endif
42 
43 #if EPOLLRDBAND != POLLRDBAND
44 #error value of EPOLLRDBAND does not match value of POLLRDBAND
45 #endif
46 
47 #if EPOLLERR != POLLERR
48 #error value of EPOLLERR does not match value of POLLERR
49 #endif
50 
51 #if EPOLLHUP != POLLHUP
52 #error value of EPOLLHUP does not match value of POLLHUP
53 #endif
54 
55 /*
56  * Events that we ignore entirely.  They can be set in events, but they will
57  * never be returned.
58  */
59 #define	EPOLLIGNORED 	(EPOLLMSG | EPOLLWAKEUP)
60 
61 /*
62  * Events that we swizzle into other bit positions.
63  */
64 #define	EPOLLSWIZZLED	\
65 	(EPOLLRDHUP | EPOLLONESHOT | EPOLLET | EPOLLWRBAND | EPOLLWRNORM)
66 
67 int
68 epoll_create(int size)
69 {
70 	int fd;
71 
72 	/*
73 	 * From the epoll_create() man page:  "Since Linux 2.6.8, the size
74 	 * argument is ignored, but must be greater than zero."  You keep using
75 	 * that word "ignored"...
76 	 */
77 	if (size <= 0) {
78 		errno = EINVAL;
79 		return (-1);
80 	}
81 
82 	if ((fd = open("/dev/poll", O_RDWR)) == -1)
83 		return (-1);
84 
85 	if (ioctl(fd, DP_EPOLLCOMPAT, 0) == -1) {
86 		(void) close(fd);
87 		return (-1);
88 	}
89 
90 	return (fd);
91 }
92 
93 int
94 epoll_create1(int flags)
95 {
96 	int fd, oflags = O_RDWR;
97 
98 	if (flags & EPOLL_CLOEXEC)
99 		oflags |= O_CLOEXEC;
100 
101 	if ((fd = open("/dev/poll", oflags)) == -1)
102 		return (-1);
103 
104 	if (ioctl(fd, DP_EPOLLCOMPAT, 0) == -1) {
105 		(void) close(fd);
106 		return (-1);
107 	}
108 
109 	return (fd);
110 }
111 
112 int
113 epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
114 {
115 	dvpoll_epollfd_t epoll[2];
116 	uint32_t events, ev = 0;
117 	int i = 0, res;
118 
119 	epoll[i].dpep_pollfd.fd = fd;
120 
121 	switch (op) {
122 	case EPOLL_CTL_DEL:
123 		ev = POLLREMOVE;
124 		break;
125 
126 	case EPOLL_CTL_MOD:
127 		/*
128 		 * In the modify case, we pass down two events:  one to
129 		 * remove the event and another to add it back.
130 		 */
131 		epoll[i++].dpep_pollfd.events = POLLREMOVE;
132 		epoll[i].dpep_pollfd.fd = fd;
133 		/* FALLTHROUGH */
134 
135 	case EPOLL_CTL_ADD:
136 		/*
137 		 * Mask off the events that we ignore, and then swizzle the
138 		 * events for which our values differ from their epoll(7)
139 		 * equivalents.
140 		 */
141 		events = event->events;
142 		ev = events & ~(EPOLLIGNORED | EPOLLSWIZZLED);
143 
144 		if (events & EPOLLRDHUP)
145 			ev |= POLLRDHUP;
146 
147 		if (events & EPOLLET)
148 			ev |= POLLET;
149 
150 		if (events & EPOLLONESHOT)
151 			ev |= POLLONESHOT;
152 
153 		if (events & EPOLLWRNORM)
154 			ev |= POLLWRNORM;
155 
156 		if (events & EPOLLWRBAND)
157 			ev |= POLLWRBAND;
158 
159 		epoll[i].dpep_data = event->data.u64;
160 		break;
161 
162 	default:
163 		errno = EOPNOTSUPP;
164 		return (-1);
165 	}
166 
167 	epoll[i].dpep_pollfd.events = ev;
168 retry:
169 	res = write(epfd, epoll, sizeof (epoll[0]) * (i + 1));
170 
171 	if (res == -1) {
172 		if (errno == EINTR) {
173 			/*
174 			 * Linux does not document EINTR as an allowed error
175 			 * for epoll_ctl.  The write must be retried if it is
176 			 * not done automatically via SA_RESTART.
177 			 */
178 			goto retry;
179 		}
180 		if (errno == ELOOP) {
181 			/*
182 			 * Convert the specific /dev/poll error about an fd
183 			 * loop into what is expected from the Linux epoll
184 			 * interface.
185 			 */
186 			errno = EINVAL;
187 		}
188 		return (-1);
189 	}
190 	return (0);
191 }
192 
193 int
194 epoll_wait(int epfd, struct epoll_event *events,
195     int maxevents, int timeout)
196 {
197 	struct dvpoll arg;
198 
199 	if (maxevents <= 0) {
200 		errno = EINVAL;
201 		return (-1);
202 	}
203 
204 	arg.dp_nfds = maxevents;
205 	arg.dp_timeout = timeout;
206 	arg.dp_fds = (pollfd_t *)events;
207 
208 	return (ioctl(epfd, DP_POLL, &arg));
209 }
210 
211 int
212 epoll_pwait(int epfd, struct epoll_event *events,
213     int maxevents, int timeout, const sigset_t *sigmask)
214 {
215 	struct dvpoll arg;
216 
217 	if (maxevents <= 0) {
218 		errno = EINVAL;
219 		return (-1);
220 	}
221 
222 	arg.dp_nfds = maxevents;
223 	arg.dp_timeout = timeout;
224 	arg.dp_fds = (pollfd_t *)events;
225 	arg.dp_setp = (sigset_t *)sigmask;
226 
227 	return (ioctl(epfd, DP_PPOLL, &arg));
228 }
229