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