xref: /kernel/liteos_a/fs/vfs/epoll/fs_epoll.c (revision 0d163575)
1/*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 *    conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 *    of conditions and the following disclaimer in the documentation and/or other materials
12 *    provided with the distribution.
13 *
14 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
15 *    to endorse or promote products derived from this software without specific prior written
16 *    permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
27 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "epoll.h"
32#include <stdint.h>
33#include <poll.h>
34#include <errno.h>
35#include <string.h>
36#include "pthread.h"
37
38/* 100, the number of fd one epollfd can control */
39#define EPOLL_DEFAULT_SIZE 100
40
41/* Internal data, used to manage each epoll fd */
42struct epoll_head {
43    int size;
44    int nodeCount;
45    struct epoll_event *evs;
46};
47
48STATIC pthread_mutex_t g_epollMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
49
50#ifndef MAX_EPOLL_FD
51#define MAX_EPOLL_FD CONFIG_EPOLL_DESCRIPTORS
52#endif
53
54/* Record the kernel fd of epoll  */
55STATIC fd_set g_epollFdSet;
56
57/* Record the private data of epoll  */
58STATIC struct epoll_head *g_epPrivBuf[MAX_EPOLL_FD];
59
60/**
61 * Alloc sysFd, storage epoll private data, set using bit.
62 *
63 * @param maxfdp: Maximum allowed application of sysFd.
64 * @param head: Private data.
65 * @return the index of the new fd; -1 on error
66 */
67static int EpollAllocSysFd(int maxfdp, struct epoll_head *head)
68{
69    int i;
70
71    fd_set *fdset = &g_epollFdSet;
72
73    for (i = 0; i < maxfdp; i++) {
74        if (fdset && !(FD_ISSET(i, fdset))) {
75            FD_SET(i, fdset);
76            if (!g_epPrivBuf[i]) {
77                g_epPrivBuf[i] = head;
78                return i + EPOLL_FD_OFFSET;
79            }
80        }
81    }
82
83    set_errno(EMFILE);
84    return -1;
85}
86
87/**
88 * free sysFd, delete epoll private data, clear using bit.
89 *
90 * @param fd: epoll fd.
91 * @return 0 or -1
92 */
93static int EpollFreeSysFd(int fd)
94{
95    int efd = fd - EPOLL_FD_OFFSET;
96
97    if ((efd < 0) || (efd >= MAX_EPOLL_FD)) {
98        set_errno(EMFILE);
99        return -1;
100    }
101
102    fd_set *fdset = &g_epollFdSet;
103    if (fdset && FD_ISSET(efd, fdset)) {
104        FD_CLR(efd, fdset);
105        g_epPrivBuf[efd] = NULL;
106    }
107
108    return 0;
109}
110
111/**
112 * get private data by epoll fd
113 *
114 * @param fd: epoll fd.
115 * @return point to epoll_head
116 */
117static struct epoll_head *EpollGetDataBuff(int fd)
118{
119    int id = fd - EPOLL_FD_OFFSET;
120
121    if ((id < 0) || (id >= MAX_EPOLL_FD)) {
122        return NULL;
123    }
124
125    return g_epPrivBuf[id];
126}
127
128/**
129 * when do EPOLL_CTL_ADD, need check if fd exist
130 *
131 * @param epHead: epoll control head, find by epoll id .
132 * @param fd: ctl add fd.
133 * @return 0 or -1
134 */
135static int CheckFdExist(struct epoll_head *epHead, int fd)
136{
137    int i;
138    for (i = 0; i < epHead->nodeCount; i++) {
139        if (epHead->evs[i].data.fd == fd) {
140            return -1;
141        }
142    }
143
144    return 0;
145}
146
147/**
148 * close epoll
149 *
150 * @param epHead: epoll control head.
151 * @return void
152 */
153static VOID DoEpollClose(struct epoll_head *epHead)
154{
155    if (epHead != NULL) {
156        if (epHead->evs != NULL) {
157            free(epHead->evs);
158        }
159
160        free(epHead);
161    }
162
163    return;
164}
165
166/**
167 * epoll_create unsupported api
168 *
169 * epoll_create is implemented by calling epoll_create1, it's parameter 'size' is useless.
170 *
171 * epoll_create1,
172 * The simple version of epoll does not use red-black trees,
173 * so when fd is normal value (greater than 0),
174 * actually allocated epoll can manage num of EPOLL_DEFAULT_SIZE
175 *
176 * @param flags: not actually used
177 * @return epoll fd
178 */
179int epoll_create1(int flags)
180{
181    (void)flags;
182    int fd = -1;
183
184    struct epoll_head *epHead = (struct epoll_head *)malloc(sizeof(struct epoll_head));
185    if (epHead == NULL) {
186        set_errno(ENOMEM);
187        return fd;
188    }
189
190    /* actually allocated epoll can manage num is EPOLL_DEFAULT_SIZE */
191    epHead->size = EPOLL_DEFAULT_SIZE;
192    epHead->nodeCount = 0;
193    epHead->evs = malloc(sizeof(struct epoll_event) * EPOLL_DEFAULT_SIZE);
194    if (epHead->evs == NULL) {
195        free(epHead);
196        set_errno(ENOMEM);
197        return fd;
198    }
199
200    /* fd set, get sysfd, for close */
201    (VOID)pthread_mutex_lock(&g_epollMutex);
202    fd = EpollAllocSysFd(MAX_EPOLL_FD, epHead);
203    if (fd == -1) {
204        (VOID)pthread_mutex_unlock(&g_epollMutex);
205        DoEpollClose(epHead);
206        set_errno(EMFILE);
207        return fd;
208    }
209    (VOID)pthread_mutex_unlock(&g_epollMutex);
210    return fd;
211}
212
213/**
214 * epoll_close,
215 * called by close
216 * @param epfd: epoll fd
217 * @return 0 or -1
218 */
219int epoll_close(int epfd)
220{
221    struct epoll_head *epHead = NULL;
222
223    (VOID)pthread_mutex_lock(&g_epollMutex);
224    epHead = EpollGetDataBuff(epfd);
225    if (epHead == NULL) {
226        (VOID)pthread_mutex_unlock(&g_epollMutex);
227        set_errno(EBADF);
228        return -1;
229    }
230
231    DoEpollClose(epHead);
232    int ret = EpollFreeSysFd(epfd);
233    (VOID)pthread_mutex_unlock(&g_epollMutex);
234    return ret;
235}
236
237int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev)
238{
239    struct epoll_head *epHead = NULL;
240    int i;
241    int ret = -1;
242
243    (VOID)pthread_mutex_lock(&g_epollMutex);
244    epHead = EpollGetDataBuff(epfd);
245    if (epHead == NULL) {
246        set_errno(EBADF);
247        goto OUT_RELEASE;
248    }
249
250    if (ev == NULL) {
251        set_errno(EINVAL);
252        goto OUT_RELEASE;
253    }
254
255    switch (op) {
256        case EPOLL_CTL_ADD:
257            ret = CheckFdExist(epHead, fd);
258            if (ret == -1) {
259                set_errno(EEXIST);
260                goto OUT_RELEASE;
261            }
262
263            if (epHead->nodeCount == EPOLL_DEFAULT_SIZE) {
264                set_errno(ENOMEM);
265                goto OUT_RELEASE;
266            }
267
268            epHead->evs[epHead->nodeCount].events = ev->events | POLLERR | POLLHUP;
269            epHead->evs[epHead->nodeCount].data.fd = fd;
270            epHead->nodeCount++;
271            ret = 0;
272            break;
273        case EPOLL_CTL_DEL:
274            for (i = 0; i < epHead->nodeCount; i++) {
275                if (epHead->evs[i].data.fd != fd) {
276                    continue;
277                }
278
279                if (i != epHead->nodeCount - 1) {
280                    memmove_s(&epHead->evs[i], epHead->nodeCount - i, &epHead->evs[i + 1],
281                              epHead->nodeCount - i);
282                }
283                epHead->nodeCount--;
284                ret = 0;
285                goto OUT_RELEASE;
286            }
287            set_errno(ENOENT);
288            break;
289        case EPOLL_CTL_MOD:
290            for (i = 0; i < epHead->nodeCount; i++) {
291                if (epHead->evs[i].data.fd == fd) {
292                    epHead->evs[i].events = ev->events | POLLERR | POLLHUP;
293                    ret = 0;
294                    goto OUT_RELEASE;
295                }
296            }
297            set_errno(ENOENT);
298            break;
299        default:
300            set_errno(EINVAL);
301            break;
302    }
303
304OUT_RELEASE:
305    (VOID)pthread_mutex_unlock(&g_epollMutex);
306    return ret;
307}
308
309int epoll_wait(int epfd, FAR struct epoll_event *evs, int maxevents, int timeout)
310{
311    struct epoll_head *epHead = NULL;
312    int ret;
313    int counter;
314    int i;
315    struct pollfd *pFd = NULL;
316    int pollSize;
317
318    epHead = EpollGetDataBuff(epfd);
319    if (epHead == NULL) {
320        set_errno(EBADF);
321        return -1;
322    }
323
324    if ((maxevents <= 0) || (evs == NULL)) {
325        set_errno(EINVAL);
326        return -1;
327    }
328
329    if (maxevents > epHead->nodeCount) {
330        pollSize = epHead->nodeCount;
331    } else {
332        pollSize = maxevents;
333    }
334
335    pFd = malloc(sizeof(struct pollfd) * pollSize);
336    if (pFd == NULL) {
337        set_errno(EINVAL);
338        return -1;
339    }
340
341    for (i = 0; i < pollSize; i++) {
342        pFd[i].fd = epHead->evs[i].data.fd;
343        pFd[i].events = (short)epHead->evs[i].events;
344    }
345
346
347    ret = poll(pFd, pollSize, timeout);
348    if (ret <= 0) {
349        free(pFd);
350        return 0;
351    }
352
353    for (i = 0, counter = 0; i < ret && counter < pollSize; counter++) {
354        if (pFd[counter].revents != 0) {
355            evs[i].data.fd = pFd[counter].fd;
356            evs[i].events  = pFd[counter].revents;
357            i++;
358        }
359    }
360
361    free(pFd);
362    return i;
363}
364
365