1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2016 Joyent, Inc.
  14  */
  15 
  16 #include <sys/types.h>
  17 #include <sys/epoll.h>
  18 #include <sys/devpoll.h>
  19 #include <unistd.h>
  20 #include <errno.h>
  21 #include <fcntl.h>
  22 #include <poll.h>
  23 
  24 /*
  25  * Events that match their epoll(7) equivalents.
  26  */
  27 #if EPOLLIN != POLLIN
  28 #error value of EPOLLIN does not match value of POLLIN
  29 #endif
  30 
  31 #if EPOLLPRI != POLLPRI
  32 #error value of EPOLLPRI does not match value of POLLPRI
  33 #endif
  34 
  35 #if EPOLLOUT != POLLOUT
  36 #error value of EPOLLOUT does not match value of POLLOUT
  37 #endif
  38 
  39 #if EPOLLRDNORM != POLLRDNORM
  40 #error value of EPOLLRDNORM does not match value of POLLRDNORM
  41 #endif
  42 
  43 #if EPOLLRDBAND != POLLRDBAND
  44 #error value of EPOLLRDBAND does not match value of POLLRDBAND
  45 #endif
  46 
  47 #if EPOLLERR != POLLERR
  48 #error value of EPOLLERR does not match value of POLLERR
  49 #endif
  50 
  51 #if EPOLLHUP != POLLHUP
  52 #error value of EPOLLHUP does not match value of POLLHUP
  53 #endif
  54 
  55 /*
  56  * Events that we ignore entirely.  They can be set in events, but they will
  57  * never be returned.
  58  */
  59 #define EPOLLIGNORED    (EPOLLMSG | EPOLLWAKEUP)
  60 
  61 /*
  62  * Events that we swizzle into other bit positions.
  63  */
  64 #define EPOLLSWIZZLED   \
  65         (EPOLLRDHUP | EPOLLONESHOT | EPOLLET | EPOLLWRBAND | EPOLLWRNORM)
  66 
  67 int
  68 epoll_create(int size)
  69 {
  70         int fd;
  71 
  72         /*
  73          * From the epoll_create() man page:  "Since Linux 2.6.8, the size
  74          * argument is ignored, but must be greater than zero."  You keep using
  75          * that word "ignored"...
  76          */
  77         if (size <= 0) {
  78                 errno = EINVAL;
  79                 return (-1);
  80         }
  81 
  82         if ((fd = open("/dev/poll", O_RDWR)) == -1)
  83                 return (-1);
  84 
  85         if (ioctl(fd, DP_EPOLLCOMPAT, 0) == -1) {
  86                 (void) close(fd);
  87                 return (-1);
  88         }
  89 
  90         return (fd);
  91 }
  92 
  93 int
  94 epoll_create1(int flags)
  95 {
  96         int fd, oflags = O_RDWR;
  97 
  98         if (flags & EPOLL_CLOEXEC)
  99                 oflags |= O_CLOEXEC;
 100 
 101         if ((fd = open("/dev/poll", oflags)) == -1)
 102                 return (-1);
 103 
 104         if (ioctl(fd, DP_EPOLLCOMPAT, 0) == -1) {
 105                 (void) close(fd);
 106                 return (-1);
 107         }
 108 
 109         return (fd);
 110 }
 111 
 112 int
 113 epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
 114 {
 115         dvpoll_epollfd_t epoll[2];
 116         uint32_t events, ev = 0;
 117         int i = 0, res;
 118 
 119         epoll[i].dpep_pollfd.fd = fd;
 120 
 121         switch (op) {
 122         case EPOLL_CTL_DEL:
 123                 ev = POLLREMOVE;
 124                 break;
 125 
 126         case EPOLL_CTL_MOD:
 127                 /*
 128                  * In the modify case, we pass down two events:  one to
 129                  * remove the event and another to add it back.
 130                  */
 131                 epoll[i++].dpep_pollfd.events = POLLREMOVE;
 132                 epoll[i].dpep_pollfd.fd = fd;
 133                 /* FALLTHROUGH */
 134 
 135         case EPOLL_CTL_ADD:
 136                 /*
 137                  * Mask off the events that we ignore, and then swizzle the
 138                  * events for which our values differ from their epoll(7)
 139                  * equivalents.
 140                  */
 141                 events = event->events;
 142                 ev = events & ~(EPOLLIGNORED | EPOLLSWIZZLED);
 143 
 144                 if (events & EPOLLRDHUP)
 145                         ev |= POLLRDHUP;
 146 
 147                 if (events & EPOLLET)
 148                         ev |= POLLET;
 149 
 150                 if (events & EPOLLONESHOT)
 151                         ev |= POLLONESHOT;
 152 
 153                 if (events & EPOLLWRNORM)
 154                         ev |= POLLWRNORM;
 155 
 156                 if (events & EPOLLWRBAND)
 157                         ev |= POLLWRBAND;
 158 
 159                 epoll[i].dpep_data = event->data.u64;
 160                 break;
 161 
 162         default:
 163                 errno = EOPNOTSUPP;
 164                 return (-1);
 165         }
 166 
 167         epoll[i].dpep_pollfd.events = ev;
 168 retry:
 169         res = write(epfd, epoll, sizeof (epoll[0]) * (i + 1));
 170 
 171         if (res == -1) {
 172                 if (errno == EINTR) {
 173                         /*
 174                          * Linux does not document EINTR as an allowed error
 175                          * for epoll_ctl.  The write must be retried if it is
 176                          * not done automatically via SA_RESTART.
 177                          */
 178                         goto retry;
 179                 }
 180                 if (errno == ELOOP) {
 181                         /*
 182                          * Convert the specific /dev/poll error about an fd
 183                          * loop into what is expected from the Linux epoll
 184                          * interface.
 185                          */
 186                         errno = EINVAL;
 187                 }
 188                 return (-1);
 189         }
 190         return (0);
 191 }
 192 
 193 int
 194 epoll_wait(int epfd, struct epoll_event *events,
 195     int maxevents, int timeout)
 196 {
 197         struct dvpoll arg;
 198 
 199         if (maxevents <= 0) {
 200                 errno = EINVAL;
 201                 return (-1);
 202         }
 203 
 204         arg.dp_nfds = maxevents;
 205         arg.dp_timeout = timeout;
 206         arg.dp_fds = (pollfd_t *)events;
 207 
 208         return (ioctl(epfd, DP_POLL, &arg));
 209 }
 210 
 211 int
 212 epoll_pwait(int epfd, struct epoll_event *events,
 213     int maxevents, int timeout, const sigset_t *sigmask)
 214 {
 215         struct dvpoll arg;
 216 
 217         if (maxevents <= 0) {
 218                 errno = EINVAL;
 219                 return (-1);
 220         }
 221 
 222         arg.dp_nfds = maxevents;
 223         arg.dp_timeout = timeout;
 224         arg.dp_fds = (pollfd_t *)events;
 225         arg.dp_setp = (sigset_t *)sigmask;
 226 
 227         return (ioctl(epfd, DP_PPOLL, &arg));
 228 }