X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fcommon%2Fcompat%2Fcompat-epoll.c;h=9055f51291475ae31b9508f7197d3eb581d2cc89;hp=008dba7028dff9d0756914d50c4a1baf7effdbe7;hb=4fb28dfc10106c4095ca3b1155d4da9a04467fd1;hpb=4c462e790c62ed5f6c5d61b3a182762fe02f7e9a diff --git a/src/common/compat/compat-epoll.c b/src/common/compat/compat-epoll.c index 008dba702..9055f5129 100644 --- a/src/common/compat/compat-epoll.c +++ b/src/common/compat/compat-epoll.c @@ -1,39 +1,74 @@ /* - * Copyright (C) 2011 - David Goulet + * Copyright (C) 2011 David Goulet * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. + * SPDX-License-Identifier: GPL-2.0-only * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. */ -#define _GNU_SOURCE +#define _LGPL_SOURCE +#include #include #include #include #include #include #include -#include +#include #include #include +#include +#include #include "poll.h" -unsigned int poll_max_size; +/* + * Maximum number of fd we can monitor. + * + * For epoll(7), /proc/sys/fs/epoll/max_user_watches (since Linux 2.6.28) will + * be used for the maximum size of the poll set. If this interface is not + * available, according to the manpage, the max_user_watches value is 1/25 (4%) + * of the available low memory divided by the registration cost in bytes which + * is 90 bytes on a 32-bit kernel and 160 bytes on a 64-bit kernel. + * + */ +static unsigned int poll_max_size; + +/* + * Resize the epoll events structure of the new size. + * + * Return 0 on success or else -1 with the current events pointer untouched. + */ +static int resize_poll_event(struct lttng_poll_event *events, + uint32_t new_size) +{ + struct epoll_event *ptr; + + assert(events); + + ptr = realloc(events->events, new_size * sizeof(*ptr)); + if (ptr == NULL) { + PERROR("realloc epoll add"); + goto error; + } + if (new_size > events->alloc_size) { + /* Zero newly allocated memory */ + memset(ptr + events->alloc_size, 0, + (new_size - events->alloc_size) * sizeof(*ptr)); + } + events->events = ptr; + events->alloc_size = new_size; + + return 0; + +error: + return -1; +} /* * Create epoll set and allocate returned events structure. */ +LTTNG_HIDDEN int compat_epoll_create(struct lttng_poll_event *events, int size, int flags) { int ret; @@ -42,12 +77,18 @@ int compat_epoll_create(struct lttng_poll_event *events, int size, int flags) goto error; } + if (!poll_max_size) { + if (lttng_poll_set_max_size()) { + goto error; + } + } + /* Don't bust the limit here */ if (size > poll_max_size) { size = poll_max_size; } - ret = epoll_create1(flags); + ret = compat_glibc_epoll_create(size, flags); if (ret < 0) { /* At this point, every error is fatal */ PERROR("epoll_create1"); @@ -63,7 +104,7 @@ int compat_epoll_create(struct lttng_poll_event *events, int size, int flags) goto error_close; } - events->events_size = size; + events->alloc_size = events->init_size = size; events->nb_fd = 0; return 0; @@ -80,16 +121,22 @@ error: /* * Add a fd to the epoll set with requesting events. */ +LTTNG_HIDDEN int compat_epoll_add(struct lttng_poll_event *events, int fd, uint32_t req_events) { - int ret, new_size; - struct epoll_event ev, *ptr; + int ret; + struct epoll_event ev; if (events == NULL || events->events == NULL || fd < 0) { ERR("Bad compat epoll add arguments"); goto error; } + /* + * Zero struct epoll_event to ensure all representations of its + * union are zeroed. + */ + memset(&ev, 0, sizeof(ev)); ev.events = req_events; ev.data.fd = fd; @@ -112,17 +159,6 @@ int compat_epoll_add(struct lttng_poll_event *events, int fd, uint32_t req_event events->nb_fd++; - if (events->nb_fd >= events->events_size) { - new_size = 2 * events->events_size; - ptr = realloc(events->events, new_size * sizeof(struct epoll_event)); - if (ptr == NULL) { - PERROR("realloc epoll add"); - goto error; - } - events->events = ptr; - events->events_size = new_size; - } - end: return 0; @@ -133,11 +169,12 @@ error: /* * Remove a fd from the epoll set. */ +LTTNG_HIDDEN int compat_epoll_del(struct lttng_poll_event *events, int fd) { int ret; - if (events == NULL || fd < 0) { + if (events == NULL || fd < 0 || events->nb_fd == 0) { goto error; } @@ -153,8 +190,6 @@ int compat_epoll_del(struct lttng_poll_event *events, int fd) PERROR("epoll_ctl DEL fatal"); goto error; } - PERROR("epoll_ctl del"); - goto error; } events->nb_fd--; @@ -166,28 +201,97 @@ error: return -1; } +/* + * Set an fd's events. + */ +LTTNG_HIDDEN +int compat_epoll_mod(struct lttng_poll_event *events, int fd, uint32_t req_events) +{ + int ret; + struct epoll_event ev; + + if (events == NULL || fd < 0 || events->nb_fd == 0) { + goto error; + } + + /* + * Zero struct epoll_event to ensure all representations of its + * union are zeroed. + */ + memset(&ev, 0, sizeof(ev)); + ev.events = req_events; + ev.data.fd = fd; + + ret = epoll_ctl(events->epfd, EPOLL_CTL_MOD, fd, &ev); + if (ret < 0) { + switch (errno) { + case ENOENT: + case EPERM: + /* Print PERROR and goto end not failing. Show must go on. */ + PERROR("epoll_ctl MOD"); + goto end; + default: + PERROR("epoll_ctl MOD fatal"); + goto error; + } + } + +end: + return 0; + +error: + return -1; +} + /* * Wait on epoll set. This is a blocking call of timeout value. */ -int compat_epoll_wait(struct lttng_poll_event *events, int timeout) +LTTNG_HIDDEN +int compat_epoll_wait(struct lttng_poll_event *events, int timeout, + bool interruptible) { int ret; + uint32_t new_size; - if (events == NULL || events->events == NULL || - events->events_size < events->nb_fd) { + if (events == NULL || events->events == NULL) { ERR("Wrong arguments in compat_epoll_wait"); goto error; } + if (events->nb_fd == 0) { + errno = EINVAL; + return -1; + } + + /* + * Resize if needed before waiting. We could either expand the array or + * shrink it down. It's important to note that after this step, we are + * ensured that the events argument of the epoll_wait call will be large + * enough to hold every possible returned events. + */ + new_size = 1U << utils_get_count_order_u32(events->nb_fd); + if (new_size != events->alloc_size && new_size >= events->init_size) { + ret = resize_poll_event(events, new_size); + if (ret < 0) { + /* ENOMEM problem at this point. */ + goto error; + } + } + do { ret = epoll_wait(events->epfd, events->events, events->nb_fd, timeout); - } while (ret == -1 && errno == EINTR); + } while (!interruptible && ret == -1 && errno == EINTR); if (ret < 0) { - /* At this point, every error is fatal */ - PERROR("epoll_wait"); + if (errno != EINTR) { + PERROR("epoll_wait"); + } goto error; } + /* + * Since the returned events are set sequentially in the "events" structure + * we only need to return the epoll_wait value and iterate over it. + */ return ret; error: @@ -197,35 +301,48 @@ error: /* * Setup poll set maximum size. */ -void compat_epoll_set_max_size(void) +LTTNG_HIDDEN +int compat_epoll_set_max_size(void) { - int ret, fd; + int ret, fd, retval = 0; + ssize_t size_ret; char buf[64]; - poll_max_size = DEFAULT_POLL_SIZE; - fd = open(COMPAT_EPOLL_PROC_PATH, O_RDONLY); if (fd < 0) { - return; + /* + * Failing on opening [1] is not an error per see. [1] was + * introduced in Linux 2.6.28 but epoll is available since + * 2.5.44. Hence, goto end and set a default value without + * setting an error return value. + * + * [1] /proc/sys/fs/epoll/max_user_watches + */ + retval = 0; + goto end; } - ret = read(fd, buf, sizeof(buf)); - if (ret < 0) { + size_ret = lttng_read(fd, buf, sizeof(buf)); + /* + * Allow reading a file smaller than buf, but keep space for + * final \0. + */ + if (size_ret < 0 || size_ret >= sizeof(buf)) { PERROR("read set max size"); - goto error; + retval = -1; + goto end_read; } - + buf[size_ret] = '\0'; poll_max_size = atoi(buf); - if (poll_max_size <= 0) { - /* Extra precaution */ - poll_max_size = DEFAULT_POLL_SIZE; - } - - DBG("epoll set max size is %d", poll_max_size); - -error: +end_read: ret = close(fd); if (ret) { PERROR("close"); } +end: + if (!poll_max_size) { + poll_max_size = DEFAULT_POLL_SIZE; + } + DBG("epoll set max size is %d", poll_max_size); + return retval; }