Fix: poll and epoll fd set reallocation
[lttng-tools.git] / src / common / compat / compat-epoll.c
... / ...
CommitLineData
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 _GNU_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 <config.h>
27
28#include <common/error.h>
29#include <common/defaults.h>
30
31#include "poll.h"
32
33unsigned int poll_max_size;
34
35/*
36 * Resize the epoll events structure of the new size.
37 *
38 * Return 0 on success or else -1 with the current events pointer untouched.
39 */
40static int resize_poll_event(struct lttng_poll_event *events,
41 uint32_t new_size)
42{
43 struct epoll_event *ptr;
44
45 assert(events);
46
47 ptr = realloc(events->events, new_size * sizeof(*ptr));
48 if (ptr == NULL) {
49 PERROR("realloc epoll add");
50 goto error;
51 }
52 events->events = ptr;
53 events->alloc_size = new_size;
54
55 return 0;
56
57error:
58 return -1;
59}
60
61/*
62 * Create epoll set and allocate returned events structure.
63 */
64int compat_epoll_create(struct lttng_poll_event *events, int size, int flags)
65{
66 int ret;
67
68 if (events == NULL || size <= 0) {
69 goto error;
70 }
71
72 /* Don't bust the limit here */
73 if (size > poll_max_size && poll_max_size != 0) {
74 size = poll_max_size;
75 }
76
77 ret = epoll_create1(flags);
78 if (ret < 0) {
79 /* At this point, every error is fatal */
80 PERROR("epoll_create1");
81 goto error;
82 }
83
84 events->epfd = ret;
85
86 /* This *must* be freed by using lttng_poll_free() */
87 events->events = zmalloc(size * sizeof(struct epoll_event));
88 if (events->events == NULL) {
89 PERROR("zmalloc epoll set");
90 goto error_close;
91 }
92
93 events->alloc_size = events->init_size = size;
94 events->nb_fd = 0;
95
96 return 0;
97
98error_close:
99 ret = close(events->epfd);
100 if (ret) {
101 PERROR("close");
102 }
103error:
104 return -1;
105}
106
107/*
108 * Add a fd to the epoll set with requesting events.
109 */
110int compat_epoll_add(struct lttng_poll_event *events, int fd, uint32_t req_events)
111{
112 int ret;
113 struct epoll_event ev;
114
115 if (events == NULL || events->events == NULL || fd < 0) {
116 ERR("Bad compat epoll add arguments");
117 goto error;
118 }
119
120 ev.events = req_events;
121 ev.data.fd = fd;
122
123 ret = epoll_ctl(events->epfd, EPOLL_CTL_ADD, fd, &ev);
124 if (ret < 0) {
125 switch (errno) {
126 case EEXIST:
127 /* If exist, it's OK. */
128 goto end;
129 case ENOSPC:
130 case EPERM:
131 /* Print PERROR and goto end not failing. Show must go on. */
132 PERROR("epoll_ctl ADD");
133 goto end;
134 default:
135 PERROR("epoll_ctl ADD fatal");
136 goto error;
137 }
138 }
139
140 events->nb_fd++;
141
142end:
143 return 0;
144
145error:
146 return -1;
147}
148
149/*
150 * Remove a fd from the epoll set.
151 */
152int compat_epoll_del(struct lttng_poll_event *events, int fd)
153{
154 int ret;
155
156 if (events == NULL || fd < 0) {
157 goto error;
158 }
159
160 ret = epoll_ctl(events->epfd, EPOLL_CTL_DEL, fd, NULL);
161 if (ret < 0) {
162 switch (errno) {
163 case ENOENT:
164 case EPERM:
165 /* Print PERROR and goto end not failing. Show must go on. */
166 PERROR("epoll_ctl DEL");
167 goto end;
168 default:
169 PERROR("epoll_ctl DEL fatal");
170 goto error;
171 }
172 PERROR("epoll_ctl del");
173 goto error;
174 }
175
176 events->nb_fd--;
177
178end:
179 return 0;
180
181error:
182 return -1;
183}
184
185/*
186 * Wait on epoll set. This is a blocking call of timeout value.
187 */
188int compat_epoll_wait(struct lttng_poll_event *events, int timeout)
189{
190 int ret;
191 uint32_t new_size;
192
193 if (events == NULL || events->events == NULL) {
194 ERR("Wrong arguments in compat_epoll_wait");
195 goto error;
196 }
197
198 /*
199 * Resize if needed before waiting. We could either expand the array or
200 * shrink it down. It's important to note that after this step, we are
201 * ensured that the events argument of the epoll_wait call will be large
202 * enough to hold every possible returned events.
203 */
204 if (events->nb_fd > events->alloc_size) {
205 /* Expand if the nb_fd is higher than the actual size. */
206 new_size = events->alloc_size << 1UL;
207 } else if ((events->nb_fd << 1UL) <= events->alloc_size &&
208 events->nb_fd >= events->init_size) {
209 /* Shrink if nb_fd multiplied by two is <= than the actual size. */
210 new_size = events->alloc_size >> 1UL;
211 } else {
212 /* Indicate that we don't want to resize. */
213 new_size = 0;
214 }
215
216 if (new_size) {
217 ret = resize_poll_event(events, new_size);
218 if (ret < 0) {
219 /* ENOMEM problem at this point. */
220 goto error;
221 }
222 }
223
224 do {
225 ret = epoll_wait(events->epfd, events->events, events->nb_fd, timeout);
226 } while (ret == -1 && errno == EINTR);
227 if (ret < 0) {
228 /* At this point, every error is fatal */
229 PERROR("epoll_wait");
230 goto error;
231 }
232
233 /*
234 * Since the returned events are set sequentially in the "events" structure
235 * we only need to return the epoll_wait value and iterate over it.
236 */
237 return ret;
238
239error:
240 return -1;
241}
242
243/*
244 * Setup poll set maximum size.
245 */
246void compat_epoll_set_max_size(void)
247{
248 int ret, fd;
249 char buf[64];
250
251 poll_max_size = DEFAULT_POLL_SIZE;
252
253 fd = open(COMPAT_EPOLL_PROC_PATH, O_RDONLY);
254 if (fd < 0) {
255 return;
256 }
257
258 ret = read(fd, buf, sizeof(buf));
259 if (ret < 0) {
260 PERROR("read set max size");
261 goto error;
262 }
263
264 poll_max_size = atoi(buf);
265 if (poll_max_size == 0) {
266 /* Extra precaution */
267 poll_max_size = DEFAULT_POLL_SIZE;
268 }
269
270 DBG("epoll set max size is %d", poll_max_size);
271
272error:
273 ret = close(fd);
274 if (ret) {
275 PERROR("close");
276 }
277}
This page took 0.024182 seconds and 4 git commands to generate.