xref: /illumos-gate/usr/src/lib/libc/port/sys/epoll.c (revision 8c69cc8fbe729fa7b091e901c4b50508ccc6bb33)
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 		flags ^= EPOLL_CLOEXEC;
101 	}
102 	/* Reject unrecognized flags */
103 	if (flags != 0) {
104 		errno = EINVAL;
105 		return (-1);
106 	}
107 
108 	if ((fd = open("/dev/poll", oflags)) == -1)
109 		return (-1);
110 
111 	if (ioctl(fd, DP_EPOLLCOMPAT, 0) == -1) {
112 		(void) close(fd);
113 		return (-1);
114 	}
115 
116 	return (fd);
117 }
118 
119 int
120 epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
121 {
122 	dvpoll_epollfd_t epoll[2];
123 	uint32_t events, ev = 0;
124 	int i = 0, res;
125 
126 	epoll[i].dpep_pollfd.fd = fd;
127 
128 	switch (op) {
129 	case EPOLL_CTL_DEL:
130 		ev = POLLREMOVE;
131 		break;
132 
133 	case EPOLL_CTL_MOD:
134 		/*
135 		 * In the modify case, we pass down two events:  one to
136 		 * remove the event and another to add it back.
137 		 */
138 		epoll[i++].dpep_pollfd.events = POLLREMOVE;
139 		epoll[i].dpep_pollfd.fd = fd;
140 		/* FALLTHROUGH */
141 
142 	case EPOLL_CTL_ADD:
143 		/*
144 		 * Mask off the events that we ignore, and then swizzle the
145 		 * events for which our values differ from their epoll(7)
146 		 * equivalents.
147 		 */
148 		events = event->events;
149 		ev = events & ~(EPOLLIGNORED | EPOLLSWIZZLED);
150 
151 		if (events & EPOLLRDHUP)
152 			ev |= POLLRDHUP;
153 
154 		if (events & EPOLLET)
155 			ev |= POLLET;
156 
157 		if (events & EPOLLONESHOT)
158 			ev |= POLLONESHOT;
159 
160 		if (events & EPOLLWRNORM)
161 			ev |= POLLWRNORM;
162 
163 		if (events & EPOLLWRBAND)
164 			ev |= POLLWRBAND;
165 
166 		epoll[i].dpep_data = event->data.u64;
167 		break;
168 
169 	default:
170 		errno = EOPNOTSUPP;
171 		return (-1);
172 	}
173 
174 	epoll[i].dpep_pollfd.events = ev;
175 retry:
176 	res = write(epfd, epoll, sizeof (epoll[0]) * (i + 1));
177 
178 	if (res == -1) {
179 		if (errno == EINTR) {
180 			/*
181 			 * Linux does not document EINTR as an allowed error
182 			 * for epoll_ctl.  The write must be retried if it is
183 			 * not done automatically via SA_RESTART.
184 			 */
185 			goto retry;
186 		}
187 		if (errno == ELOOP) {
188 			/*
189 			 * Convert the specific /dev/poll error about an fd
190 			 * loop into what is expected from the Linux epoll
191 			 * interface.
192 			 */
193 			errno = EINVAL;
194 		}
195 		return (-1);
196 	}
197 	return (0);
198 }
199 
200 int
201 epoll_wait(int epfd, struct epoll_event *events,
202     int maxevents, int timeout)
203 {
204 	struct dvpoll arg;
205 
206 	if (maxevents <= 0) {
207 		errno = EINVAL;
208 		return (-1);
209 	}
210 
211 	arg.dp_nfds = maxevents;
212 	arg.dp_timeout = timeout;
213 	arg.dp_fds = (pollfd_t *)events;
214 
215 	return (ioctl(epfd, DP_POLL, &arg));
216 }
217 
218 int
219 epoll_pwait(int epfd, struct epoll_event *events,
220     int maxevents, int timeout, const sigset_t *sigmask)
221 {
222 	struct dvpoll arg;
223 
224 	if (maxevents <= 0) {
225 		errno = EINVAL;
226 		return (-1);
227 	}
228 
229 	arg.dp_nfds = maxevents;
230 	arg.dp_timeout = timeout;
231 	arg.dp_fds = (pollfd_t *)events;
232 	arg.dp_setp = (sigset_t *)sigmask;
233 
234 	return (ioctl(epfd, DP_PPOLL, &arg));
235 }
236