epoll/poll compat: expose interruptible API
[lttng-tools.git] / src / common / compat / compat-epoll.c
1 /*
2 * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2 only,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17
18 #define _LGPL_SOURCE
19 #include <assert.h>
20 #include <fcntl.h>
21 #include <limits.h>
22 #include <stdlib.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <stdbool.h>
27
28 #include <common/error.h>
29 #include <common/defaults.h>
30 #include <common/macros.h>
31 #include <common/utils.h>
32
33 #include "poll.h"
34
35 unsigned int poll_max_size;
36
37 /*
38 * Resize the epoll events structure of the new size.
39 *
40 * Return 0 on success or else -1 with the current events pointer untouched.
41 */
42 static int resize_poll_event(struct lttng_poll_event *events,
43 uint32_t new_size)
44 {
45 struct epoll_event *ptr;
46
47 assert(events);
48
49 ptr = realloc(events->events, new_size * sizeof(*ptr));
50 if (ptr == NULL) {
51 PERROR("realloc epoll add");
52 goto error;
53 }
54 if (new_size > events->alloc_size) {
55 /* Zero newly allocated memory */
56 memset(ptr + events->alloc_size, 0,
57 (new_size - events->alloc_size) * sizeof(*ptr));
58 }
59 events->events = ptr;
60 events->alloc_size = new_size;
61
62 return 0;
63
64 error:
65 return -1;
66 }
67
68 /*
69 * Create epoll set and allocate returned events structure.
70 */
71 int compat_epoll_create(struct lttng_poll_event *events, int size, int flags)
72 {
73 int ret;
74
75 if (events == NULL || size <= 0) {
76 goto error;
77 }
78
79 if (!poll_max_size) {
80 if (lttng_poll_set_max_size()) {
81 goto error;
82 }
83 }
84
85 /* Don't bust the limit here */
86 if (size > poll_max_size) {
87 size = poll_max_size;
88 }
89
90 ret = compat_glibc_epoll_create(size, flags);
91 if (ret < 0) {
92 /* At this point, every error is fatal */
93 PERROR("epoll_create1");
94 goto error;
95 }
96
97 events->epfd = ret;
98
99 /* This *must* be freed by using lttng_poll_free() */
100 events->events = zmalloc(size * sizeof(struct epoll_event));
101 if (events->events == NULL) {
102 PERROR("zmalloc epoll set");
103 goto error_close;
104 }
105
106 events->alloc_size = events->init_size = size;
107 events->nb_fd = 0;
108
109 return 0;
110
111 error_close:
112 ret = close(events->epfd);
113 if (ret) {
114 PERROR("close");
115 }
116 error:
117 return -1;
118 }
119
120 /*
121 * Add a fd to the epoll set with requesting events.
122 */
123 int compat_epoll_add(struct lttng_poll_event *events, int fd, uint32_t req_events)
124 {
125 int ret;
126 struct epoll_event ev;
127
128 if (events == NULL || events->events == NULL || fd < 0) {
129 ERR("Bad compat epoll add arguments");
130 goto error;
131 }
132
133 /*
134 * Zero struct epoll_event to ensure all representations of its
135 * union are zeroed.
136 */
137 memset(&ev, 0, sizeof(ev));
138 ev.events = req_events;
139 ev.data.fd = fd;
140
141 ret = epoll_ctl(events->epfd, EPOLL_CTL_ADD, fd, &ev);
142 if (ret < 0) {
143 switch (errno) {
144 case EEXIST:
145 /* If exist, it's OK. */
146 goto end;
147 case ENOSPC:
148 case EPERM:
149 /* Print PERROR and goto end not failing. Show must go on. */
150 PERROR("epoll_ctl ADD");
151 goto end;
152 default:
153 PERROR("epoll_ctl ADD fatal");
154 goto error;
155 }
156 }
157
158 events->nb_fd++;
159
160 end:
161 return 0;
162
163 error:
164 return -1;
165 }
166
167 /*
168 * Remove a fd from the epoll set.
169 */
170 int compat_epoll_del(struct lttng_poll_event *events, int fd)
171 {
172 int ret;
173
174 if (events == NULL || fd < 0 || events->nb_fd == 0) {
175 goto error;
176 }
177
178 ret = epoll_ctl(events->epfd, EPOLL_CTL_DEL, fd, NULL);
179 if (ret < 0) {
180 switch (errno) {
181 case ENOENT:
182 case EPERM:
183 /* Print PERROR and goto end not failing. Show must go on. */
184 PERROR("epoll_ctl DEL");
185 goto end;
186 default:
187 PERROR("epoll_ctl DEL fatal");
188 goto error;
189 }
190 }
191
192 events->nb_fd--;
193
194 end:
195 return 0;
196
197 error:
198 return -1;
199 }
200
201 /*
202 * Set an fd's events.
203 */
204 int compat_epoll_mod(struct lttng_poll_event *events, int fd, uint32_t req_events)
205 {
206 int ret;
207 struct epoll_event ev;
208
209 if (events == NULL || fd < 0 || events->nb_fd == 0) {
210 goto error;
211 }
212
213 /*
214 * Zero struct epoll_event to ensure all representations of its
215 * union are zeroed.
216 */
217 memset(&ev, 0, sizeof(ev));
218 ev.events = req_events;
219 ev.data.fd = fd;
220
221 ret = epoll_ctl(events->epfd, EPOLL_CTL_MOD, fd, &ev);
222 if (ret < 0) {
223 switch (errno) {
224 case ENOENT:
225 case EPERM:
226 /* Print PERROR and goto end not failing. Show must go on. */
227 PERROR("epoll_ctl MOD");
228 goto end;
229 default:
230 PERROR("epoll_ctl MOD fatal");
231 goto error;
232 }
233 }
234
235 end:
236 return 0;
237
238 error:
239 return -1;
240 }
241
242 /*
243 * Wait on epoll set. This is a blocking call of timeout value.
244 */
245 int compat_epoll_wait(struct lttng_poll_event *events, int timeout,
246 bool interruptible)
247 {
248 int ret;
249 uint32_t new_size;
250
251 if (events == NULL || events->events == NULL) {
252 ERR("Wrong arguments in compat_epoll_wait");
253 goto error;
254 }
255
256 if (events->nb_fd == 0) {
257 errno = EINVAL;
258 return -1;
259 }
260
261 /*
262 * Resize if needed before waiting. We could either expand the array or
263 * shrink it down. It's important to note that after this step, we are
264 * ensured that the events argument of the epoll_wait call will be large
265 * enough to hold every possible returned events.
266 */
267 new_size = 1U << utils_get_count_order_u32(events->nb_fd);
268 if (new_size != events->alloc_size && new_size >= events->init_size) {
269 ret = resize_poll_event(events, new_size);
270 if (ret < 0) {
271 /* ENOMEM problem at this point. */
272 goto error;
273 }
274 }
275
276 do {
277 ret = epoll_wait(events->epfd, events->events, events->nb_fd, timeout);
278 } while (!interruptible && ret == -1 && errno == EINTR);
279 if (ret < 0) {
280 if (errno != EINTR) {
281 PERROR("epoll_wait");
282 }
283 goto error;
284 }
285
286 /*
287 * Since the returned events are set sequentially in the "events" structure
288 * we only need to return the epoll_wait value and iterate over it.
289 */
290 return ret;
291
292 error:
293 return -1;
294 }
295
296 /*
297 * Setup poll set maximum size.
298 */
299 int compat_epoll_set_max_size(void)
300 {
301 int ret, fd, retval = 0;
302 ssize_t size_ret;
303 char buf[64];
304
305 fd = open(COMPAT_EPOLL_PROC_PATH, O_RDONLY);
306 if (fd < 0) {
307 /*
308 * Failing on opening [1] is not an error per see. [1] was
309 * introduced in Linux 2.6.28 but epoll is available since
310 * 2.5.44. Hence, goto end and set a default value without
311 * setting an error return value.
312 *
313 * [1] /proc/sys/fs/epoll/max_user_watches
314 */
315 retval = 0;
316 goto end;
317 }
318
319 size_ret = lttng_read(fd, buf, sizeof(buf));
320 /*
321 * Allow reading a file smaller than buf, but keep space for
322 * final \0.
323 */
324 if (size_ret < 0 || size_ret >= sizeof(buf)) {
325 PERROR("read set max size");
326 retval = -1;
327 goto end_read;
328 }
329 buf[size_ret] = '\0';
330 poll_max_size = atoi(buf);
331 end_read:
332 ret = close(fd);
333 if (ret) {
334 PERROR("close");
335 }
336 end:
337 if (!poll_max_size) {
338 poll_max_size = DEFAULT_POLL_SIZE;
339 }
340 DBG("epoll set max size is %d", poll_max_size);
341 return retval;
342 }
This page took 0.036481 seconds and 5 git commands to generate.