1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author        Notes
8  * 2023-07-29     zmq810150896  first version
9  * 2024-03-26     TroyMitchelle Add comments for all functions, members within structure members and fix incorrect naming of triggered
10  * 2023-12-14     Shell         When poll goes to sleep before the waitqueue has added a
11  *                              record and finished enumerating all the fd's, it may be
12  *                              incorrectly woken up. This is basically because the poll
13  *                              mechanism wakeup algorithm does not correctly distinguish
14  *                              the current wait state.
15  */
16 
17 #include <rtthread.h>
18 #include <fcntl.h>
19 #include <stdint.h>
20 #include <unistd.h>
21 #include <dfs_file.h>
22 #include "sys/epoll.h"
23 #include "poll.h"
24 #include <lwp_signal.h>
25 
26 #define EPOLL_MUTEX_NAME "EVENTEPOLL"
27 
28 #define EFD_SHARED_EPOLL_TYPE (EPOLL_CTL_ADD | EPOLL_CTL_DEL | EPOLL_CTL_MOD)
29 #define EPOLLINOUT_BITS (EPOLLIN | EPOLLOUT | EPOLLRDNORM | EPOLLWRNORM)
30 #define EPOLLEXCLUSIVE_BITS (EPOLLINOUT_BITS | EPOLLERR | EPOLLHUP | \
31                 EPOLLET | EPOLLEXCLUSIVE)
32 
33 struct rt_eventpoll;
34 
35 enum rt_epoll_status {
36     RT_EPOLL_STAT_INIT,
37     RT_EPOLL_STAT_TRIG,
38     RT_EPOLL_STAT_WAITING,
39 };
40 
41 /* Monitor queue */
42 struct rt_fd_list
43 {
44     rt_uint32_t revents;        /**< Monitored events */
45     struct epoll_event epev;    /**< Epoll event structure */
46     rt_pollreq_t req;           /**< Poll request structure */
47     struct rt_eventpoll *ep;    /**< Pointer to the associated event poll */
48     struct rt_wqueue_node wqn;  /**< Wait queue node */
49     int exclusive;              /**< Indicates if the event is exclusive */
50     rt_bool_t is_rdl_node;      /**< Indicates if the node is in the ready list */
51     int fd;                     /**< File descriptor */
52     struct rt_fd_list *next;    /**< Pointer to the next file descriptor list */
53     rt_slist_t rdl_node;        /**< Ready list node */
54 };
55 
56 struct rt_eventpoll
57 {
58     rt_wqueue_t epoll_read;      /**< Epoll read queue */
59     rt_thread_t polling_thread;  /**< Polling thread */
60     struct rt_mutex lock;        /**< Mutex lock */
61     struct rt_fd_list *fdlist;   /**< Monitor list */
62     int eventpoll_num;           /**< Number of ready lists */
63     rt_pollreq_t req;            /**< Poll request structure */
64     struct rt_spinlock spinlock; /**< Spin lock */
65     rt_slist_t rdl_head;         /**< Ready list head */
66     enum rt_epoll_status status; /* the waited thread whether triggered */
67 };
68 
69 static int epoll_close(struct dfs_file *file);
70 static int epoll_poll(struct dfs_file *file, struct rt_pollreq *req);
71 static int epoll_get_event(struct rt_fd_list *fl, rt_pollreq_t *req);
72 static int epoll_do_ctl(int epfd, int op, int fd, struct epoll_event *event);
73 
74 static const struct dfs_file_ops epoll_fops =
75 {
76     .close      = epoll_close,
77     .poll       = epoll_poll,
78 };
79 
80 /**
81  * @brief   Closes the file descriptor list associated with epoll.
82  *
83  * This function closes the file descriptor list associated with epoll and frees the allocated memory.
84  *
85  * @param   fdlist  Pointer to the file descriptor list.
86  *
87  * @return  Returns 0 on success.
88  */
epoll_close_fdlist(struct rt_fd_list * fdlist)89 static int epoll_close_fdlist(struct rt_fd_list *fdlist)
90 {
91     struct rt_fd_list *fre_node, *list;
92 
93     if (fdlist != RT_NULL)
94     {
95         list = fdlist;
96         while (list->next != RT_NULL)
97         {
98             fre_node = list->next;
99             rt_wqueue_remove(&fre_node->wqn);
100 
101             list->next = fre_node->next;
102             rt_free(fre_node);
103         }
104 
105         rt_free(fdlist);
106     }
107 
108     return 0;
109 }
110 
111 /**
112  * @brief   Closes the epoll file descriptor.
113  *
114  * This function closes the epoll file descriptor and cleans up associated resources.
115  *
116  * @param   file    Pointer to the file structure.
117  *
118  * @return  Returns 0 on success.
119  */
epoll_close(struct dfs_file * file)120 static int epoll_close(struct dfs_file *file)
121 {
122     struct rt_eventpoll *ep;
123 
124     if (file->vnode->ref_count != 1)
125         return 0;
126 
127     if (file->vnode)
128     {
129         if (file->vnode->data)
130         {
131             ep = file->vnode->data;
132             if (ep)
133             {
134                 rt_mutex_take(&ep->lock, RT_WAITING_FOREVER);
135                 if (ep->fdlist)
136                 {
137                     epoll_close_fdlist(ep->fdlist);
138                 }
139 
140                 rt_mutex_release(&ep->lock);
141                 rt_mutex_detach(&ep->lock);
142                 rt_free(ep);
143             }
144         }
145     }
146 
147     return 0;
148 }
149 
150 /**
151  * @brief   Polls the epoll file descriptor for events.
152  *
153  * This function polls the epoll file descriptor for events and updates the poll request accordingly.
154  *
155  * @param   file    Pointer to the file structure.
156  * @param   req     Pointer to the poll request structure.
157  *
158  * @return  Returns the events occurred on success.
159  */
epoll_poll(struct dfs_file * file,struct rt_pollreq * req)160 static int epoll_poll(struct dfs_file *file, struct rt_pollreq *req)
161 {
162     struct rt_eventpoll *ep;
163     int events = 0;
164     rt_base_t level;
165 
166     if (file->vnode->data)
167     {
168         ep = file->vnode->data;
169         ep->req._key = req->_key;
170 
171         rt_mutex_take(&ep->lock, RT_WAITING_FOREVER);
172         rt_poll_add(&ep->epoll_read, req);
173 
174         level = rt_spin_lock_irqsave(&ep->spinlock);
175 
176         if (!rt_slist_isempty(&ep->rdl_head))
177             events |= POLLIN | EPOLLRDNORM | POLLOUT;
178 
179         rt_spin_unlock_irqrestore(&ep->spinlock, level);
180         rt_mutex_release(&ep->lock);
181     }
182 
183     return events;
184 }
185 
186 /**
187  * @brief   Callback function for the wait queue.
188  *
189  * This function is called when the file descriptor is ready for polling.
190  *
191  * @param   wait    Pointer to the wait queue node.
192  * @param   key     Key associated with the wait queue node.
193  *
194  * @return  Returns 0 on success.
195  */
epoll_wqueue_callback(struct rt_wqueue_node * wait,void * key)196 static int epoll_wqueue_callback(struct rt_wqueue_node *wait, void *key)
197 {
198     struct rt_fd_list *fdlist;
199     struct rt_eventpoll *ep;
200     rt_base_t level;
201     int is_waiting = 0;
202 
203     if (key && !((rt_ubase_t)key & wait->key))
204         return -1;
205 
206     fdlist = rt_container_of(wait, struct rt_fd_list, wqn);
207     ep = fdlist->ep;
208 
209     if (ep)
210     {
211         level = rt_spin_lock_irqsave(&ep->spinlock);
212         if (fdlist->is_rdl_node == RT_FALSE)
213         {
214             rt_slist_append(&ep->rdl_head, &fdlist->rdl_node);
215             fdlist->exclusive = 0;
216             fdlist->is_rdl_node = RT_TRUE;
217             ep->eventpoll_num++;
218             is_waiting = (ep->status == RT_EPOLL_STAT_WAITING);
219             ep->status = RT_EPOLL_STAT_TRIG;
220             rt_wqueue_wakeup(&ep->epoll_read, (void *)POLLIN);
221         }
222         rt_spin_unlock_irqrestore(&ep->spinlock, level);
223     }
224 
225     if (is_waiting)
226     {
227         return __wqueue_default_wake(wait, key);
228     }
229 
230     return -1;
231 }
232 
233 /**
234  * @brief   Adds a callback function to the wait queue associated with epoll.
235  *
236  * This function adds a callback function to the wait queue associated with epoll.
237  *
238  * @param   wq      Pointer to the wait queue.
239  * @param   req     Pointer to the poll request structure.
240  */
epoll_wqueue_add_callback(rt_wqueue_t * wq,rt_pollreq_t * req)241 static void epoll_wqueue_add_callback(rt_wqueue_t *wq, rt_pollreq_t *req)
242 {
243     struct rt_fd_list *fdlist;
244     struct rt_eventpoll *ep;
245 
246     fdlist = rt_container_of(req, struct rt_fd_list, req);
247 
248     ep = fdlist->ep;
249     fdlist->wqn.key = req->_key;
250 
251     rt_list_init(&(fdlist->wqn.list));
252 
253     fdlist->wqn.polling_thread = ep->polling_thread;
254     fdlist->wqn.wakeup = epoll_wqueue_callback;
255     rt_wqueue_add(wq, &fdlist->wqn);
256 }
257 
258 /**
259  * @brief   Installs a file descriptor list into the epoll control structure.
260  *
261  * This function installs a file descriptor list into the epoll control structure.
262  *
263  * @param   fdlist  Pointer to the file descriptor list.
264  * @param   ep      Pointer to the epoll control structure.
265  */
epoll_ctl_install(struct rt_fd_list * fdlist,struct rt_eventpoll * ep)266 static void epoll_ctl_install(struct rt_fd_list *fdlist, struct rt_eventpoll *ep)
267 {
268     rt_uint32_t mask = 0;
269     rt_base_t level;
270 
271     fdlist->req._key = fdlist->revents;
272 
273     mask = epoll_get_event(fdlist, &fdlist->req);
274 
275     if (mask & fdlist->revents)
276     {
277         if (ep)
278         {
279             rt_mutex_take(&ep->lock, RT_WAITING_FOREVER);
280             level = rt_spin_lock_irqsave(&ep->spinlock);
281             rt_slist_append(&ep->rdl_head, &fdlist->rdl_node);
282             fdlist->exclusive = 0;
283             fdlist->is_rdl_node = RT_TRUE;
284             ep->status = RT_EPOLL_STAT_TRIG;
285             ep->eventpoll_num ++;
286             rt_spin_unlock_irqrestore(&ep->spinlock, level);
287             rt_mutex_release(&ep->lock);
288         }
289     }
290 }
291 
292 /**
293  * @brief   Initializes the epoll control structure.
294  *
295  * This function initializes the epoll control structure.
296  *
297  * @param   ep      Pointer to the epoll control structure.
298  */
epoll_member_init(struct rt_eventpoll * ep)299 static void epoll_member_init(struct rt_eventpoll *ep)
300 {
301     ep->status = RT_EPOLL_STAT_INIT;
302     ep->eventpoll_num = 0;
303     ep->polling_thread = rt_thread_self();
304     ep->fdlist = RT_NULL;
305     ep->req._key = 0;
306     rt_slist_init(&(ep->rdl_head));
307     rt_wqueue_init(&ep->epoll_read);
308     rt_mutex_init(&ep->lock, EPOLL_MUTEX_NAME, RT_IPC_FLAG_FIFO);
309     rt_spin_lock_init(&ep->spinlock);
310 }
311 
312 /**
313  * @brief   Initializes the epoll file descriptor.
314  *
315  * This function initializes the epoll file descriptor.
316  *
317  * @param   fd      File descriptor.
318  *
319  * @return  Returns 0 on success.
320  */
epoll_epf_init(int fd)321 static int epoll_epf_init(int fd)
322 {
323     struct dfs_file *df;
324     struct rt_eventpoll *ep;
325     rt_err_t ret = 0;
326 
327     df = fd_get(fd);
328 
329     if (df)
330     {
331         ep = (struct rt_eventpoll *)rt_malloc(sizeof(struct rt_eventpoll));
332         if (ep)
333         {
334             epoll_member_init(ep);
335 
336             #ifdef RT_USING_DFS_V2
337             df->fops = &epoll_fops;
338             #endif
339 
340             df->vnode = (struct dfs_vnode *)rt_malloc(sizeof(struct dfs_vnode));
341             if (df->vnode)
342             {
343                 ep->fdlist = (struct rt_fd_list *)rt_malloc(sizeof(struct rt_fd_list));
344                 if (ep->fdlist)
345                 {
346                     ep->fdlist->next = RT_NULL;
347                     ep->fdlist->fd = fd;
348                     ep->fdlist->ep = ep;
349                     ep->fdlist->exclusive = 0;
350                     ep->fdlist->is_rdl_node = RT_FALSE;
351                     dfs_vnode_init(df->vnode, FT_REGULAR, &epoll_fops);
352                     df->vnode->data = ep;
353                     rt_slist_init(&ep->fdlist->rdl_node);
354                 }
355                 else
356                 {
357                     ret = -ENOMEM;
358                     rt_free(df->vnode);
359                     rt_free(ep);
360                 }
361             }
362             else
363             {
364                 ret = -ENOMEM;
365                 rt_free(ep);
366             }
367         }
368         else
369         {
370             ret = -ENOMEM;
371         }
372     }
373 
374     return ret;
375 }
376 
377 /**
378  * @brief   Creates an epoll file descriptor.
379  *
380  * This function creates an epoll file descriptor.
381  *
382  * @param   size    Size of the epoll instance.
383  *
384  * @return  Returns the file descriptor on success, or -1 on failure.
385  */
epoll_do_create(int size)386 static int epoll_do_create(int size)
387 {
388     rt_err_t ret = -1;
389     int status;
390     int fd;
391 
392     if (size < 0)
393     {
394         rt_set_errno(EINVAL);
395     }
396     else
397     {
398         fd = fd_new();
399         if (fd >= 0)
400         {
401             ret = fd;
402             status = epoll_epf_init(fd);
403             if (status < 0)
404             {
405                 fd_release(fd);
406                 rt_set_errno(-status);
407             }
408         }
409         else
410         {
411             rt_set_errno(-fd);
412         }
413     }
414 
415     return ret;
416 }
417 
418 /**
419  * @brief   Adds a file descriptor to the epoll instance.
420  *
421  * This function adds a file descriptor to the epoll instance.
422  *
423  * @param   df      Pointer to the file structure.
424  * @param   fd      File descriptor to add.
425  * @param   event   Pointer to the epoll event structure.
426  *
427  * @return  Returns 0 on success, or an error code on failure.
428  */
epoll_ctl_add(struct dfs_file * df,int fd,struct epoll_event * event)429 static int epoll_ctl_add(struct dfs_file *df, int fd, struct epoll_event *event)
430 {
431     struct rt_fd_list *fdlist;
432     struct rt_eventpoll *ep;
433     rt_err_t ret = -EINVAL;
434 
435     if (df->vnode->data)
436     {
437         ep = df->vnode->data;
438         fdlist = ep->fdlist;
439         ret = 0;
440 
441         rt_mutex_take(&ep->lock, RT_WAITING_FOREVER);
442         while (fdlist->next != RT_NULL)
443         {
444             if (fdlist->next->fd == fd)
445             {
446                 rt_mutex_release(&ep->lock);
447                 return 0;
448             }
449             fdlist = fdlist->next;
450         }
451         rt_mutex_release(&ep->lock);
452 
453         fdlist = (struct rt_fd_list *)rt_malloc(sizeof(struct rt_fd_list));
454         if (fdlist)
455         {
456             fdlist->fd = fd;
457             memcpy(&fdlist->epev.data, &event->data, sizeof(event->data));
458             fdlist->epev.events = 0;
459             fdlist->ep = ep;
460             fdlist->exclusive = 0;
461             fdlist->is_rdl_node = RT_FALSE;
462             fdlist->req._proc = epoll_wqueue_add_callback;
463             fdlist->revents = event->events;
464             rt_mutex_take(&ep->lock, RT_WAITING_FOREVER);
465             fdlist->next = ep->fdlist->next;
466             ep->fdlist->next = fdlist;
467             rt_mutex_release(&ep->lock);
468             rt_slist_init(&fdlist->rdl_node);
469 
470             epoll_ctl_install(fdlist, ep);
471         }
472         else
473         {
474             ret = -ENOMEM;
475         }
476     }
477 
478     return ret;
479 }
480 
481 /**
482  * @brief   Removes a file descriptor from the epoll instance.
483  *
484  * This function removes a file descriptor from the epoll instance.
485  *
486  * @param   df      Pointer to the file structure.
487  * @param   fd      File descriptor to remove.
488  *
489  * @return  Returns 0 on success, or an error code on failure.
490  */
epoll_ctl_del(struct dfs_file * df,int fd)491 static int epoll_ctl_del(struct dfs_file *df, int fd)
492 {
493     struct rt_fd_list *fdlist, *fre_fd, *rdlist;
494     struct rt_eventpoll *ep = RT_NULL;
495     rt_slist_t *node = RT_NULL;
496     rt_err_t ret = -EINVAL;
497     rt_base_t level;
498 
499     if (df->vnode->data)
500     {
501         ep = df->vnode->data;
502 
503         if (ep)
504         {
505             rt_mutex_take(&ep->lock, RT_WAITING_FOREVER);
506             level = rt_spin_lock_irqsave(&ep->spinlock);
507             rt_slist_for_each(node, &ep->rdl_head)
508             {
509                 rdlist = rt_slist_entry(node, struct rt_fd_list, rdl_node);
510                 if (rdlist->fd == fd)
511                     rt_slist_remove(&ep->rdl_head, node);
512             }
513             rt_spin_unlock_irqrestore(&ep->spinlock, level);
514 
515             fdlist = ep->fdlist;
516             while (fdlist->next != RT_NULL)
517             {
518                 if (fdlist->next->fd == fd)
519                 {
520                     fre_fd = fdlist->next;
521                     fdlist->next = fdlist->next->next;
522 
523                     if (fre_fd->wqn.wqueue)
524                         rt_wqueue_remove(&fre_fd->wqn);
525 
526                     rt_free(fre_fd);
527                     break;
528                 }
529                 else
530                 {
531                     fdlist = fdlist->next;
532                 }
533             }
534 
535             rt_mutex_release(&ep->lock);
536         }
537 
538         ret = 0;
539     }
540 
541     return ret;
542 }
543 
544 /**
545  * @brief   Modifies the events associated with a file descriptor in the epoll instance.
546  *
547  * This function modifies the events associated with a file descriptor in the epoll instance.
548  *
549  * @param   df      Pointer to the file structure.
550  * @param   fd      File descriptor to modify.
551  * @param   event   Pointer to the epoll event structure.
552  *
553  * @return  Returns 0 on success, or an error code on failure.
554  */
epoll_ctl_mod(struct dfs_file * df,int fd,struct epoll_event * event)555 static int epoll_ctl_mod(struct dfs_file *df, int fd, struct epoll_event *event)
556 {
557     struct rt_fd_list *fdlist;
558     struct rt_eventpoll *ep = RT_NULL;
559     rt_err_t ret = -EINVAL;
560 
561     if (df->vnode->data)
562     {
563         ep = df->vnode->data;
564 
565         fdlist = ep->fdlist;
566         while (fdlist->next != RT_NULL)
567         {
568             if (fdlist->next->fd == fd)
569             {
570                 rt_mutex_take(&ep->lock, RT_WAITING_FOREVER);
571                 memcpy(&fdlist->next->epev.data, &event->data, sizeof(event->data));
572                 fdlist->next->revents = event->events;
573                 if (fdlist->next->wqn.wqueue)
574                     rt_wqueue_remove(&fdlist->next->wqn);
575 
576                 rt_mutex_release(&ep->lock);
577                 epoll_ctl_install(fdlist->next, ep);
578                 break;
579             }
580 
581             fdlist = fdlist->next;
582         }
583 
584         ret = 0;
585     }
586 
587     return ret;
588 }
589 
590 /**
591  * @brief   Controls an epoll instance.
592  *
593  * This function controls an epoll instance, performing operations such as adding,
594  * modifying, or removing file descriptors associated with the epoll instance.
595  *
596  * @param   epfd    File descriptor of the epoll instance.
597  * @param   op      Operation to perform (EPOLL_CTL_ADD, EPOLL_CTL_DEL, or EPOLL_CTL_MOD).
598  * @param   fd      File descriptor to add, modify, or remove.
599  * @param   event   Pointer to the epoll event structure.
600  *
601  * @return  Returns 0 on success, or -1 on failure with errno set appropriately.
602  */
epoll_do_ctl(int epfd,int op,int fd,struct epoll_event * event)603 static int epoll_do_ctl(int epfd, int op, int fd, struct epoll_event *event)
604 {
605     struct dfs_file *epdf;
606     struct rt_eventpoll *ep;
607     rt_err_t ret = 0;
608 
609     if (op & ~EFD_SHARED_EPOLL_TYPE)
610     {
611         rt_set_errno(EINVAL);
612         return -1;
613     }
614 
615     if ((epfd == fd) || (epfd < 0))
616     {
617         rt_set_errno(EINVAL);
618         return -1;
619     }
620 
621     if (!(op & EPOLL_CTL_DEL))
622     {
623         if (!(event->events & EPOLLEXCLUSIVE_BITS))
624         {
625             rt_set_errno(EINVAL);
626             return -1;
627         }
628         event->events |= EPOLLERR | EPOLLHUP;
629     }
630 
631     if (!fd_get(fd))
632     {
633         rt_set_errno(EBADF);
634         return -1;
635     }
636 
637     epdf = fd_get(epfd);
638 
639     if (epdf->vnode->data)
640     {
641         ep = epdf->vnode->data;
642 
643         switch (op)
644         {
645         case EPOLL_CTL_ADD:
646             ret = epoll_ctl_add(epdf, fd, event);
647             break;
648         case EPOLL_CTL_DEL:
649             ret = epoll_ctl_del(epdf, fd);
650             break;
651         case EPOLL_CTL_MOD:
652             ret = epoll_ctl_mod(epdf, fd, event);
653             break;
654         default:
655             rt_set_errno(EINVAL);
656             break;
657         }
658 
659         if (ret < 0)
660         {
661             rt_set_errno(-ret);
662             ret = -1;
663         }
664         else
665         {
666             ep->polling_thread = rt_thread_self();
667         }
668     }
669 
670     return ret;
671 }
672 
673 
674 /**
675  * @brief   Waits for events on an epoll instance with a specified timeout.
676  *
677  * This function waits for events on the specified epoll instance within the given timeout.
678  *
679  * @param   ep      Pointer to the epoll instance.
680  * @param   msec    Timeout in milliseconds.
681  *
682  * @return  Returns 0 if no events occurred within the timeout, or 1 if events were triggered.
683  */
epoll_wait_timeout(struct rt_eventpoll * ep,int msec)684 static int epoll_wait_timeout(struct rt_eventpoll *ep, int msec)
685 {
686     rt_int32_t timeout;
687     struct rt_thread *thread;
688     rt_base_t level;
689     int ret = 0;
690 
691     thread = ep->polling_thread;
692 
693     timeout = rt_tick_from_millisecond(msec);
694 
695     level = rt_spin_lock_irqsave(&ep->spinlock);
696 
697     if (timeout != 0 && ep->status != RT_EPOLL_STAT_TRIG)
698     {
699         if (rt_thread_suspend_with_flag(thread, RT_KILLABLE) == RT_EOK)
700         {
701             if (timeout > 0)
702             {
703                 rt_timer_control(&(thread->thread_timer),
704                         RT_TIMER_CTRL_SET_TIME,
705                         &timeout);
706                 rt_timer_start(&(thread->thread_timer));
707             }
708 
709             ep->status = RT_EPOLL_STAT_WAITING;
710 
711             rt_spin_unlock_irqrestore(&ep->spinlock, level);
712 
713             rt_schedule();
714 
715             level = rt_spin_lock_irqsave(&ep->spinlock);
716             if (ep->status == RT_EPOLL_STAT_WAITING)
717                 ep->status = RT_EPOLL_STAT_INIT;
718         }
719     }
720 
721     ret = !(ep->status == RT_EPOLL_STAT_TRIG);
722     rt_spin_unlock_irqrestore(&ep->spinlock, level);
723 
724     return ret;
725 }
726 
727 /**
728  * @brief   Gets events associated with a file descriptor in the epoll instance.
729  *
730  * This function gets events associated with a file descriptor in the epoll instance.
731  *
732  * @param   fl      Pointer to the file descriptor list structure.
733  * @param   req     Pointer to the poll request structure.
734  *
735  * @return  Returns the bitmask of events associated with the file descriptor.
736  */
epoll_get_event(struct rt_fd_list * fl,rt_pollreq_t * req)737 static int epoll_get_event(struct rt_fd_list *fl, rt_pollreq_t *req)
738 {
739     struct dfs_file *df;
740     int mask = 0;
741     int fd = 0;
742 
743     fd = fl->fd;
744     if (fd >= 0)
745     {
746         df = fd_get(fd);
747         if (df)
748         {
749             if (df->vnode->fops->poll)
750             {
751                 req->_key = fl->revents | POLLERR | POLLHUP;
752                 mask = df->vnode->fops->poll(df, req);
753                 if (mask < 0)
754                     return mask;
755             }
756 
757             mask &= fl->revents | EPOLLOUT | POLLERR;
758         }
759     }
760 
761     return mask;
762 }
763 
764 /**
765  * @brief   Performs epoll operation to get triggered events.
766  *
767  * This function performs epoll operation to get triggered events.
768  *
769  * @param   ep          Pointer to the epoll instance.
770  * @param   events      Pointer to the array to store triggered events.
771  * @param   maxevents   Maximum number of events to store in the array.
772  * @param   timeout     Timeout value in milliseconds.
773  *
774  * @return  Returns the number of triggered events.
775  */
epoll_do(struct rt_eventpoll * ep,struct epoll_event * events,int maxevents,int timeout)776 static int epoll_do(struct rt_eventpoll *ep, struct epoll_event *events, int maxevents, int timeout)
777 {
778     struct rt_fd_list *rdlist;
779     rt_slist_t *node = RT_NULL;
780     int event_num = 0;
781     int istimeout = 0;
782     int isn_add = 0;
783     int isfree = 0;
784     int mask = 0;
785     rt_base_t level;
786 
787     while (1)
788     {
789         rt_mutex_take(&ep->lock, RT_WAITING_FOREVER);
790         level = rt_spin_lock_irqsave(&ep->spinlock);
791         if (ep->eventpoll_num > 0)
792         {
793             rt_slist_for_each(node,&ep->rdl_head)
794             {
795                 rdlist = rt_slist_entry(node, struct rt_fd_list, rdl_node);
796                 ep->eventpoll_num --;
797                 rt_slist_remove(&ep->rdl_head, &rdlist->rdl_node);
798                 rdlist->is_rdl_node = RT_FALSE;
799 
800                 rt_spin_unlock_irqrestore(&ep->spinlock, level);
801 
802                 isfree = 0;
803                 isn_add = 0;
804                 if (event_num < maxevents)
805                 {
806                     if (rdlist->wqn.wqueue)
807                     {
808                         rt_wqueue_remove(&rdlist->wqn);
809                     }
810 
811                     mask = epoll_get_event(rdlist, &rdlist->req);
812 
813                     if (mask & rdlist->revents)
814                     {
815                         rdlist->epev.events = mask & rdlist->revents;
816                     }
817                     else
818                     {
819                         isfree = 1;
820                         isn_add = 1;
821                     }
822 
823                     if (rdlist->revents & EPOLLONESHOT)
824                     {
825                         rdlist->revents = 0;
826                         isfree = 1;
827                         if (rdlist->wqn.wqueue)
828                             rt_wqueue_remove(&rdlist->wqn);
829                     }
830                     else
831                     {
832                         if (rdlist->revents & EPOLLET)
833                         {
834                             isfree = 1;
835                         }
836                         else
837                         {
838                             level = rt_spin_lock_irqsave(&ep->spinlock);
839                             if (rdlist->exclusive != 1)
840                             {
841                                 rdlist->exclusive = 1;
842                             }
843                             rt_spin_unlock_irqrestore(&ep->spinlock, level);
844                         }
845                     }
846 
847                     if (!isn_add)
848                     {
849                         memcpy(&events[event_num], &rdlist->epev, sizeof(rdlist->epev));
850                         event_num ++;
851                     }
852 
853                     if (!isfree)
854                     {
855                         if (rdlist->is_rdl_node == RT_FALSE)
856                         {
857                             level = rt_spin_lock_irqsave(&ep->spinlock);
858                             ep->eventpoll_num ++;
859                             rt_slist_append(&ep->rdl_head, &rdlist->rdl_node);
860                             rdlist->is_rdl_node = RT_TRUE;
861                             rt_spin_unlock_irqrestore(&ep->spinlock, level);
862                         }
863                         else
864                         {
865                             level = rt_spin_lock_irqsave(&ep->spinlock);
866                             if (!rdlist->wqn.wqueue)
867                             {
868                                 epoll_get_event(rdlist, &rdlist->req);
869                             }
870                             rt_spin_unlock_irqrestore(&ep->spinlock, level);
871                         }
872                     }
873                 }
874                 else
875                 {
876                     level = rt_spin_lock_irqsave(&ep->spinlock);
877                     break;
878                 }
879 
880                 level = rt_spin_lock_irqsave(&ep->spinlock);
881             }
882         }
883 
884         rt_spin_unlock_irqrestore(&ep->spinlock, level);
885         rt_mutex_release(&ep->lock);
886 
887         if (event_num || istimeout)
888         {
889             level = rt_spin_lock_irqsave(&ep->spinlock);
890             ep->status = RT_EPOLL_STAT_INIT;
891             rt_spin_unlock_irqrestore(&ep->spinlock, level);
892             if ((timeout >= 0) || (event_num > 0))
893                 break;
894         }
895 
896         if (epoll_wait_timeout(ep, timeout))
897         {
898             istimeout = 1;
899         }
900     }
901 
902     return event_num;
903 }
904 
905 /**
906  * @brief   Waits for events on an epoll instance with specified parameters.
907  *
908  * This function waits for events on the specified epoll instance within the given timeout, optionally blocking signals based on the provided signal set.
909  *
910  * @param   epfd        File descriptor referring to the epoll instance.
911  * @param   events      Pointer to the array to store triggered events.
912  * @param   maxevents   Maximum number of events to store in the array.
913  * @param   timeout     Timeout value in milliseconds.
914  * @param   ss          Pointer to the signal set indicating signals to block during the wait operation. Pass NULL if no signals need to be blocked.
915  *
916  * @return  Returns the number of triggered events on success, or -1 on failure.
917  */
epoll_do_wait(int epfd,struct epoll_event * events,int maxevents,int timeout,const sigset_t * ss)918 static int epoll_do_wait(int epfd, struct epoll_event *events, int maxevents, int timeout, const sigset_t *ss)
919 {
920     struct rt_eventpoll *ep;
921     struct dfs_file *df;
922     lwp_sigset_t old_sig, new_sig;
923     rt_err_t ret = 0;
924 
925     if (ss)
926     {
927         memcpy(&new_sig, ss, sizeof(lwp_sigset_t));
928         lwp_thread_signal_mask(rt_thread_self(), LWP_SIG_MASK_CMD_BLOCK, &new_sig, &old_sig);
929     }
930 
931     if ((maxevents > 0) && (epfd >= 0))
932     {
933         df = fd_get(epfd);
934         if (df && df->vnode)
935         {
936             ep = (struct rt_eventpoll *)df->vnode->data;
937             if (ep)
938             {
939                 ret = epoll_do(ep, events, maxevents, timeout);
940             }
941         }
942     }
943 
944     if (ss)
945     {
946         lwp_thread_signal_mask(rt_thread_self(), LWP_SIG_MASK_CMD_SET_MASK, &old_sig, RT_NULL);
947     }
948 
949     if (ret < 0)
950     {
951         rt_set_errno(-ret);
952         ret = -1;
953     }
954 
955     return ret;
956 }
957 
958 /**
959  * @brief   Creates an epoll instance.
960  *
961  * This function creates an epoll instance with the specified size.
962  *
963  * @param   size    Size of the epoll instance.
964  *
965  * @return  Returns the file descriptor referring to the created epoll instance on success, or -1 on failure.
966  */
epoll_create(int size)967 int epoll_create(int size)
968 {
969     return epoll_do_create(size);
970 }
971 
972 /**
973  * @brief   Modifies an epoll instance.
974  *
975  * This function modifies the epoll instance referred to by 'epfd' according to the specified operation 'op', associated with the file descriptor 'fd', and the event structure 'event'.
976  *
977  * @param   epfd    File descriptor referring to the epoll instance.
978  * @param   op      Operation to perform (EPOLL_CTL_ADD, EPOLL_CTL_DEL, or EPOLL_CTL_MOD).
979  * @param   fd      File descriptor to associate with the epoll instance or remove from it.
980  * @param   event   Pointer to the event structure containing the events to modify.
981  *
982  * @return  Returns 0 on success, or -1 on failure.
983  */
epoll_ctl(int epfd,int op,int fd,struct epoll_event * event)984 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
985 {
986     return epoll_do_ctl(epfd, op, fd, event);
987 }
988 
989 /**
990  * @brief   Waits for events on an epoll instance.
991  *
992  * This function waits for events on the epoll instance referred to by 'epfd' within the given timeout.
993  *
994  * @param   epfd        File descriptor referring to the epoll instance.
995  * @param   events      Pointer to the array to store triggered events.
996  * @param   maxevents   Maximum number of events to store in the array.
997  * @param   timeout     Timeout value in milliseconds.
998  *
999  * @return  Returns the number of triggered events on success, or -1 on failure.
1000  */
epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)1001 int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
1002 {
1003     return epoll_do_wait(epfd, events, maxevents, timeout, RT_NULL);
1004 }
1005 
1006 /**
1007  * @brief   Waits for events on an epoll instance, blocking signals.
1008  *
1009  * This function waits for events on the epoll instance referred to by 'epfd' within the given timeout, blocking signals based on the provided signal set 'ss'.
1010  *
1011  * @param   epfd        File descriptor referring to the epoll instance.
1012  * @param   events      Pointer to the array to store triggered events.
1013  * @param   maxevents   Maximum number of events to store in the array.
1014  * @param   timeout     Timeout value in milliseconds.
1015  * @param   ss          Pointer to the signal set indicating signals to block during the wait operation.
1016  *
1017  * @return  Returns the number of triggered events on success, or -1 on failure.
1018  */
epoll_pwait(int epfd,struct epoll_event * events,int maxevents,int timeout,const sigset_t * ss)1019 int epoll_pwait(int epfd, struct epoll_event *events, int maxevents, int timeout, const sigset_t *ss)
1020 {
1021     return epoll_do_wait(epfd, events, maxevents, timeout, ss);
1022 }
1023 
1024 /**
1025  * @brief   Waits for events on an epoll instance, blocking signals.
1026  *
1027  * This function waits for events on the epoll instance referred to by 'epfd' within the given timeout, blocking signals based on the provided signal set 'ss'.
1028  *
1029  * @param   epfd        File descriptor referring to the epoll instance.
1030  * @param   events      Pointer to the array to store triggered events.
1031  * @param   maxevents   Maximum number of events to store in the array.
1032  * @param   timeout     Timeout value in milliseconds.
1033  * @param   ss          Pointer to the signal set indicating signals to block during the wait operation.
1034  *
1035  * @return  Returns the number of triggered events on success, or -1 on failure.
1036  */
epoll_pwait2(int epfd,struct epoll_event * events,int maxevents,int timeout,const sigset_t * ss)1037 int epoll_pwait2(int epfd, struct epoll_event *events, int maxevents, int timeout, const sigset_t *ss)
1038 {
1039     return epoll_do_wait(epfd, events, maxevents, timeout, ss);
1040 }
1041 
1042