1// SPDX-License-Identifier: GPL-2.0 2// 3// waiter-epoll.c - Waiter for event notification by epoll(7). 4// 5// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp> 6// 7// Licensed under the terms of the GNU General Public License, version 2. 8 9#include "waiter.h" 10#include "misc.h" 11 12#include <stdlib.h> 13#include <unistd.h> 14#include <string.h> 15#include <errno.h> 16#include <sys/types.h> 17#include <sys/epoll.h> 18 19struct epoll_state { 20 int epfd; 21 struct epoll_event *events; 22 unsigned int ev_count; 23}; 24 25static int epoll_prepare(struct waiter_context *waiter) 26{ 27 struct epoll_state *state = waiter->private_data; 28 int i; 29 30 state->ev_count = waiter->pfd_count; 31 state->events = calloc(state->ev_count, sizeof(*state->events)); 32 if (state->events == NULL) 33 return -ENOMEM; 34 35 state->epfd = epoll_create(1); 36 if (state->epfd < 0) 37 return -errno; 38 39 for (i = 0; i < (int)waiter->pfd_count; ++i) { 40 struct epoll_event ev = { 41 .data.fd = waiter->pfds[i].fd, 42 .events = waiter->pfds[i].events, 43 }; 44 if (epoll_ctl(state->epfd, EPOLL_CTL_ADD, ev.data.fd, &ev) < 0) 45 return -errno; 46 } 47 48 return 0; 49} 50 51static int epoll_wait_event(struct waiter_context *waiter, int timeout_msec) 52{ 53 struct epoll_state *state = waiter->private_data; 54 unsigned int ev_count; 55 int i, j; 56 int err; 57 58 memset(state->events, 0, state->ev_count * sizeof(*state->events)); 59 err = epoll_wait(state->epfd, state->events, state->ev_count, 60 timeout_msec); 61 if (err < 0) 62 return -errno; 63 ev_count = (unsigned int)err; 64 65 if (ev_count > 0) { 66 // Reconstruct data of pollfd structure. 67 for (i = 0; i < (int)ev_count; ++i) { 68 struct epoll_event *ev = &state->events[i]; 69 for (j = 0; j < (int)waiter->pfd_count; ++j) { 70 if (waiter->pfds[i].fd == ev->data.fd) { 71 waiter->pfds[i].revents = ev->events; 72 break; 73 } 74 } 75 } 76 } 77 78 return ev_count; 79} 80 81static void epoll_release(struct waiter_context *waiter) 82{ 83 struct epoll_state *state = waiter->private_data; 84 int i; 85 86 for (i = 0; i < (int)waiter->pfd_count; ++i) { 87 int fd = waiter->pfds[i].fd; 88 epoll_ctl(state->epfd, EPOLL_CTL_DEL, fd, NULL); 89 } 90 91 free(state->events); 92 state->events = NULL; 93 94 close(state->epfd); 95 96 state->ev_count = 0; 97 state->epfd = 0; 98} 99 100const struct waiter_data waiter_epoll = { 101 .ops = { 102 .prepare = epoll_prepare, 103 .wait_event = epoll_wait_event, 104 .release = epoll_release, 105 }, 106 .private_size = sizeof(struct epoll_state), 107}; 108