Sync ax_have_epoll.m4 with autoconf-archive
[lttng-tools.git] / src / common / compat / compat-poll.c
CommitLineData
5eb91c98
DG
1/*
2 * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
a1de8fcc 3 * Copyright (C) 2019 - Yannick Lamarre <ylamarre@efficios.com>
5eb91c98 4 *
d14d33bf
AM
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License, version 2 only,
7 * as published by the Free Software Foundation.
5eb91c98
DG
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
d14d33bf
AM
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
5eb91c98
DG
17 */
18
6c1c0768 19#define _LGPL_SOURCE
d21b0d71 20#include <assert.h>
5eb91c98
DG
21#include <stdlib.h>
22#include <sys/resource.h>
23#include <sys/time.h>
f057dfc3 24#include <stdbool.h>
5eb91c98 25
990570ed 26#include <common/defaults.h>
db758600 27#include <common/error.h>
cfa9a5a2
DG
28#include <common/macros.h>
29#include <common/utils.h>
5eb91c98
DG
30
31#include "poll.h"
32
cc0acbd1
JG
33
34/*
35 * Maximum number of fd we can monitor.
36 *
37 * For poll(2), the max fds must not exceed RLIMIT_NOFILE given by
38 * getrlimit(2).
39 */
40static unsigned int poll_max_size;
5eb91c98 41
d21b0d71
DG
42/*
43 * Resize the epoll events structure of the new size.
44 *
45 * Return 0 on success or else -1 with the current events pointer untouched.
46 */
47static int resize_poll_event(struct compat_poll_event_array *array,
48 uint32_t new_size)
49{
50 struct pollfd *ptr;
51
52 assert(array);
53
ac018a8b
DG
54 /* Refuse to resize the array more than the max size. */
55 if (new_size > poll_max_size) {
56 goto error;
57 }
58
d21b0d71
DG
59 ptr = realloc(array->events, new_size * sizeof(*ptr));
60 if (ptr == NULL) {
61 PERROR("realloc epoll add");
62 goto error;
63 }
53efb85a
MD
64 if (new_size > array->alloc_size) {
65 /* Zero newly allocated memory */
66 memset(ptr + array->alloc_size, 0,
67 (new_size - array->alloc_size) * sizeof(*ptr));
68 }
d21b0d71
DG
69 array->events = ptr;
70 array->alloc_size = new_size;
71
72 return 0;
73
74error:
75 return -1;
76}
77
78/*
79 * Update events with the current events object.
80 */
81static int update_current_events(struct lttng_poll_event *events)
82{
83 int ret;
84 struct compat_poll_event_array *current, *wait;
85
86 assert(events);
87
88 current = &events->current;
89 wait = &events->wait;
90
91 wait->nb_fd = current->nb_fd;
dbe23f45 92 if (current->alloc_size != wait->alloc_size) {
d21b0d71
DG
93 ret = resize_poll_event(wait, current->alloc_size);
94 if (ret < 0) {
95 goto error;
96 }
97 }
98 memcpy(wait->events, current->events,
99 current->nb_fd * sizeof(*current->events));
100
dbe23f45 101 /* Update is done. */
d21b0d71 102 events->need_update = 0;
d21b0d71
DG
103
104 return 0;
105
106error:
107 return -1;
108}
109
5eb91c98
DG
110/*
111 * Create pollfd data structure.
112 */
cc0acbd1 113LTTNG_HIDDEN
5eb91c98
DG
114int compat_poll_create(struct lttng_poll_event *events, int size)
115{
d21b0d71
DG
116 struct compat_poll_event_array *current, *wait;
117
5eb91c98
DG
118 if (events == NULL || size <= 0) {
119 ERR("Wrong arguments for poll create");
120 goto error;
121 }
122
dbe23f45 123 if (!poll_max_size) {
c607fe03
MJ
124 if (lttng_poll_set_max_size()) {
125 goto error;
126 }
dbe23f45
MD
127 }
128
5eb91c98
DG
129 /* Don't bust the limit here */
130 if (size > poll_max_size) {
131 size = poll_max_size;
132 }
133
d21b0d71
DG
134 /* Reset everything before begining the allocation. */
135 memset(events, 0, sizeof(struct lttng_poll_event));
136
d21b0d71
DG
137 current = &events->current;
138 wait = &events->wait;
139
5eb91c98 140 /* This *must* be freed by using lttng_poll_free() */
d21b0d71
DG
141 wait->events = zmalloc(size * sizeof(struct pollfd));
142 if (wait->events == NULL) {
6f04ed72 143 PERROR("zmalloc struct pollfd");
5eb91c98
DG
144 goto error;
145 }
146
d21b0d71
DG
147 wait->alloc_size = wait->init_size = size;
148
149 current->events = zmalloc(size * sizeof(struct pollfd));
150 if (current->events == NULL) {
6f04ed72 151 PERROR("zmalloc struct current pollfd");
d21b0d71
DG
152 goto error;
153 }
154
155 current->alloc_size = current->init_size = size;
5eb91c98
DG
156
157 return 0;
158
159error:
160 return -1;
161}
162
163/*
164 * Add fd to pollfd data structure with requested events.
165 */
cc0acbd1 166LTTNG_HIDDEN
5eb91c98
DG
167int compat_poll_add(struct lttng_poll_event *events, int fd,
168 uint32_t req_events)
169{
d21b0d71
DG
170 int new_size, ret, i;
171 struct compat_poll_event_array *current;
5eb91c98 172
d21b0d71 173 if (events == NULL || events->current.events == NULL || fd < 0) {
5eb91c98
DG
174 ERR("Bad compat poll add arguments");
175 goto error;
176 }
177
d21b0d71
DG
178 current = &events->current;
179
180 /* Check if fd we are trying to add is already there. */
181 for (i = 0; i < current->nb_fd; i++) {
d21b0d71
DG
182 if (current->events[i].fd == fd) {
183 errno = EEXIST;
5eb91c98
DG
184 goto error;
185 }
5eb91c98
DG
186 }
187
dbe23f45
MD
188 /* Resize array if needed. */
189 new_size = 1U << utils_get_count_order_u32(current->nb_fd + 1);
190 if (new_size != current->alloc_size && new_size >= current->init_size) {
d21b0d71
DG
191 ret = resize_poll_event(current, new_size);
192 if (ret < 0) {
193 goto error;
194 }
d21b0d71 195 }
5eb91c98 196
d21b0d71
DG
197 current->events[current->nb_fd].fd = fd;
198 current->events[current->nb_fd].events = req_events;
199 current->nb_fd++;
200 events->need_update = 1;
201
202 DBG("fd %d of %d added to pollfd", fd, current->nb_fd);
5eb91c98
DG
203
204 return 0;
205
206error:
207 return -1;
208}
209
f057dfc3
JG
210/*
211 * Modify an fd's events..
212 */
cc0acbd1 213LTTNG_HIDDEN
f057dfc3
JG
214int compat_poll_mod(struct lttng_poll_event *events, int fd,
215 uint32_t req_events)
216{
8a282751 217 int i;
f057dfc3
JG
218 struct compat_poll_event_array *current;
219
a1de8fcc
YL
220 if (events == NULL || events->current.nb_fd == 0 ||
221 events->current.events == NULL || fd < 0) {
f057dfc3
JG
222 ERR("Bad compat poll mod arguments");
223 goto error;
224 }
225
226 current = &events->current;
227
228 for (i = 0; i < current->nb_fd; i++) {
229 if (current->events[i].fd == fd) {
f057dfc3
JG
230 current->events[i].events = req_events;
231 events->need_update = 1;
232 break;
233 }
234 }
235
a1de8fcc
YL
236 /*
237 * The epoll flavor doesn't flag modifying a non-included FD as an
238 * error.
239 */
f057dfc3
JG
240
241 return 0;
242
243error:
244 return -1;
245}
246
5eb91c98
DG
247/*
248 * Remove a fd from the pollfd structure.
249 */
cc0acbd1 250LTTNG_HIDDEN
5eb91c98
DG
251int compat_poll_del(struct lttng_poll_event *events, int fd)
252{
a1de8fcc
YL
253 int i, count = 0, ret;
254 uint32_t new_size;
d21b0d71 255 struct compat_poll_event_array *current;
5eb91c98 256
a1de8fcc
YL
257 if (events == NULL || events->current.nb_fd == 0 ||
258 events->current.events == NULL || fd < 0) {
5eb91c98
DG
259 goto error;
260 }
261
d21b0d71
DG
262 /* Ease our life a bit. */
263 current = &events->current;
5eb91c98 264
d21b0d71 265 for (i = 0; i < current->nb_fd; i++) {
5eb91c98 266 /* Don't put back the fd we want to delete */
d21b0d71
DG
267 if (current->events[i].fd != fd) {
268 current->events[count].fd = current->events[i].fd;
269 current->events[count].events = current->events[i].events;
5eb91c98
DG
270 count++;
271 }
272 }
a1de8fcc
YL
273
274 /* The fd was not in our set, return no error as with epoll. */
275 if (current->nb_fd == count) {
276 goto end;
277 }
278
dbe23f45
MD
279 /* No fd duplicate should be ever added into array. */
280 assert(current->nb_fd - 1 == count);
281 current->nb_fd = count;
282
283 /* Resize array if needed. */
284 new_size = 1U << utils_get_count_order_u32(current->nb_fd);
a1de8fcc
YL
285 if (new_size != current->alloc_size && new_size >= current->init_size
286 && current->nb_fd != 0) {
dbe23f45
MD
287 ret = resize_poll_event(current, new_size);
288 if (ret < 0) {
289 goto error;
290 }
291 }
5eb91c98 292
d21b0d71 293 events->need_update = 1;
5eb91c98 294
a1de8fcc 295end:
5eb91c98
DG
296 return 0;
297
298error:
299 return -1;
300}
301
302/*
303 * Wait on poll() with timeout. Blocking call.
304 */
cc0acbd1 305LTTNG_HIDDEN
9f32e9bf
MD
306int compat_poll_wait(struct lttng_poll_event *events, int timeout,
307 bool interruptible)
5eb91c98 308{
22a73671
YL
309 int ret, active_fd_count;
310 int idle_pfd_index = 0;
311 size_t i;
5eb91c98 312
d21b0d71 313 if (events == NULL || events->current.events == NULL) {
5eb91c98
DG
314 ERR("poll wait arguments error");
315 goto error;
316 }
317
d21b0d71
DG
318 if (events->current.nb_fd == 0) {
319 /* Return an invalid error to be consistent with epoll. */
320 errno = EINVAL;
dbe23f45 321 events->wait.nb_fd = 0;
d21b0d71
DG
322 goto error;
323 }
324
325 if (events->need_update) {
326 ret = update_current_events(events);
327 if (ret < 0) {
328 errno = ENOMEM;
329 goto error;
330 }
331 }
332
a9b0dbc2
JG
333 do {
334 ret = poll(events->wait.events, events->wait.nb_fd, timeout);
9f32e9bf 335 } while (!interruptible && ret == -1 && errno == EINTR);
5eb91c98 336 if (ret < 0) {
9f32e9bf
MD
337 if (errno != EINTR) {
338 PERROR("poll wait");
339 }
5eb91c98
DG
340 goto error;
341 }
342
22a73671
YL
343 active_fd_count = ret;
344
9ddba525 345 /*
22a73671
YL
346 * Swap all active pollfd structs to the beginning of the
347 * array to emulate compat-epoll behaviour. This algorithm takes
348 * advantage of poll's returned value and the burst nature of active
349 * events on the file descriptors. The while loop guarantees that
350 * idle_pfd will always point to an idle fd.
9ddba525 351 */
22a73671
YL
352 if (active_fd_count == events->wait.nb_fd) {
353 goto end;
354 }
355 while (idle_pfd_index < active_fd_count &&
356 events->wait.events[idle_pfd_index].revents != 0) {
357 idle_pfd_index++;
358 }
359
360 for (i = idle_pfd_index + 1; idle_pfd_index < active_fd_count;
361 i++) {
362 struct pollfd swap_pfd;
363 struct pollfd *idle_pfd = &events->wait.events[idle_pfd_index];
364 struct pollfd *current_pfd = &events->wait.events[i];
365
fbccfe84 366 if (idle_pfd->revents != 0) {
22a73671
YL
367 swap_pfd = *current_pfd;
368 *current_pfd = *idle_pfd;
369 *idle_pfd = swap_pfd;
370 idle_pfd_index++;
371 }
372 }
373
374end:
375 return ret;
5eb91c98
DG
376
377error:
378 return -1;
379}
380
381/*
382 * Setup poll set maximum size.
383 */
cc0acbd1 384LTTNG_HIDDEN
dbe23f45 385int compat_poll_set_max_size(void)
5eb91c98 386{
dbe23f45 387 int ret, retval = 0;
5eb91c98
DG
388 struct rlimit lim;
389
5eb91c98
DG
390 ret = getrlimit(RLIMIT_NOFILE, &lim);
391 if (ret < 0) {
6f04ed72 392 PERROR("getrlimit poll RLIMIT_NOFILE");
dbe23f45
MD
393 retval = -1;
394 goto end;
5eb91c98
DG
395 }
396
397 poll_max_size = lim.rlim_cur;
dbe23f45 398end:
d21b0d71 399 if (poll_max_size == 0) {
990570ed 400 poll_max_size = DEFAULT_POLL_SIZE;
5eb91c98 401 }
5eb91c98 402 DBG("poll set max size set to %u", poll_max_size);
dbe23f45 403 return retval;
5eb91c98 404}
This page took 0.093272 seconds and 4 git commands to generate.