devpoll.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /*
  2. * Copyright 2000-2009 Niels Provos <provos@citi.umich.edu>
  3. * Copyright 2009-2012 Niels Provos and Nick Mathewson
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. The name of the author may not be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  17. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  18. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  19. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  20. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  21. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  22. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  23. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  25. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. #include "event2/event-config.h"
  28. #include "evconfig-private.h"
  29. #ifdef EVENT__HAVE_DEVPOLL
  30. #include <sys/types.h>
  31. #include <sys/resource.h>
  32. #ifdef EVENT__HAVE_SYS_TIME_H
  33. #include <sys/time.h>
  34. #endif
  35. #include <sys/queue.h>
  36. #include <sys/devpoll.h>
  37. #include <signal.h>
  38. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <string.h>
  41. #include <unistd.h>
  42. #include <fcntl.h>
  43. #include <errno.h>
  44. #include "event2/event.h"
  45. #include "event2/event_struct.h"
  46. #include "event2/thread.h"
  47. #include "event-internal.h"
  48. #include "evsignal-internal.h"
  49. #include "log-internal.h"
  50. #include "evmap-internal.h"
  51. #include "evthread-internal.h"
  52. struct devpollop {
  53. struct pollfd *events;
  54. int nevents;
  55. int dpfd;
  56. struct pollfd *changes;
  57. int nchanges;
  58. };
  59. static void *devpoll_init(struct event_base *);
  60. static int devpoll_add(struct event_base *, int fd, short old, short events, void *);
  61. static int devpoll_del(struct event_base *, int fd, short old, short events, void *);
  62. static int devpoll_dispatch(struct event_base *, struct timeval *);
  63. static void devpoll_dealloc(struct event_base *);
  64. const struct eventop devpollops = {
  65. "devpoll",
  66. devpoll_init,
  67. devpoll_add,
  68. devpoll_del,
  69. devpoll_dispatch,
  70. devpoll_dealloc,
  71. 1, /* need reinit */
  72. EV_FEATURE_FDS|EV_FEATURE_O1,
  73. 0
  74. };
  75. #define NEVENT 32000
  76. static int
  77. devpoll_commit(struct devpollop *devpollop)
  78. {
  79. /*
  80. * Due to a bug in Solaris, we have to use pwrite with an offset of 0.
  81. * Write is limited to 2GB of data, until it will fail.
  82. */
  83. if (pwrite(devpollop->dpfd, devpollop->changes,
  84. sizeof(struct pollfd) * devpollop->nchanges, 0) == -1)
  85. return (-1);
  86. devpollop->nchanges = 0;
  87. return (0);
  88. }
  89. static int
  90. devpoll_queue(struct devpollop *devpollop, int fd, int events) {
  91. struct pollfd *pfd;
  92. if (devpollop->nchanges >= devpollop->nevents) {
  93. /*
  94. * Change buffer is full, must commit it to /dev/poll before
  95. * adding more
  96. */
  97. if (devpoll_commit(devpollop) != 0)
  98. return (-1);
  99. }
  100. pfd = &devpollop->changes[devpollop->nchanges++];
  101. pfd->fd = fd;
  102. pfd->events = events;
  103. pfd->revents = 0;
  104. return (0);
  105. }
  106. static void *
  107. devpoll_init(struct event_base *base)
  108. {
  109. int dpfd, nfiles = NEVENT;
  110. struct rlimit rl;
  111. struct devpollop *devpollop;
  112. if (!(devpollop = mm_calloc(1, sizeof(struct devpollop))))
  113. return (NULL);
  114. if (getrlimit(RLIMIT_NOFILE, &rl) == 0 &&
  115. rl.rlim_cur != RLIM_INFINITY)
  116. nfiles = rl.rlim_cur;
  117. /* Initialize the kernel queue */
  118. if ((dpfd = evutil_open_closeonexec_("/dev/poll", O_RDWR, 0)) == -1) {
  119. event_warn("open: /dev/poll");
  120. mm_free(devpollop);
  121. return (NULL);
  122. }
  123. devpollop->dpfd = dpfd;
  124. /* Initialize fields */
  125. /* FIXME: allocating 'nfiles' worth of space here can be
  126. * expensive and unnecessary. See how epoll.c does it instead. */
  127. devpollop->events = mm_calloc(nfiles, sizeof(struct pollfd));
  128. if (devpollop->events == NULL) {
  129. mm_free(devpollop);
  130. close(dpfd);
  131. return (NULL);
  132. }
  133. devpollop->nevents = nfiles;
  134. devpollop->changes = mm_calloc(nfiles, sizeof(struct pollfd));
  135. if (devpollop->changes == NULL) {
  136. mm_free(devpollop->events);
  137. mm_free(devpollop);
  138. close(dpfd);
  139. return (NULL);
  140. }
  141. evsig_init_(base);
  142. return (devpollop);
  143. }
  144. static int
  145. devpoll_dispatch(struct event_base *base, struct timeval *tv)
  146. {
  147. struct devpollop *devpollop = base->evbase;
  148. struct pollfd *events = devpollop->events;
  149. struct dvpoll dvp;
  150. int i, res, timeout = -1;
  151. if (devpollop->nchanges)
  152. devpoll_commit(devpollop);
  153. if (tv != NULL)
  154. timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
  155. dvp.dp_fds = devpollop->events;
  156. dvp.dp_nfds = devpollop->nevents;
  157. dvp.dp_timeout = timeout;
  158. EVBASE_RELEASE_LOCK(base, th_base_lock);
  159. res = ioctl(devpollop->dpfd, DP_POLL, &dvp);
  160. EVBASE_ACQUIRE_LOCK(base, th_base_lock);
  161. if (res == -1) {
  162. if (errno != EINTR) {
  163. event_warn("ioctl: DP_POLL");
  164. return (-1);
  165. }
  166. return (0);
  167. }
  168. event_debug(("%s: devpoll_wait reports %d", __func__, res));
  169. for (i = 0; i < res; i++) {
  170. int which = 0;
  171. int what = events[i].revents;
  172. if (what & POLLHUP)
  173. what |= POLLIN | POLLOUT;
  174. else if (what & POLLERR)
  175. what |= POLLIN | POLLOUT;
  176. if (what & POLLIN)
  177. which |= EV_READ;
  178. if (what & POLLOUT)
  179. which |= EV_WRITE;
  180. if (!which)
  181. continue;
  182. /* XXX(niels): not sure if this works for devpoll */
  183. evmap_io_active_(base, events[i].fd, which);
  184. }
  185. return (0);
  186. }
  187. static int
  188. devpoll_add(struct event_base *base, int fd, short old, short events, void *p)
  189. {
  190. struct devpollop *devpollop = base->evbase;
  191. int res;
  192. (void)p;
  193. /*
  194. * It's not necessary to OR the existing read/write events that we
  195. * are currently interested in with the new event we are adding.
  196. * The /dev/poll driver ORs any new events with the existing events
  197. * that it has cached for the fd.
  198. */
  199. res = 0;
  200. if (events & EV_READ)
  201. res |= POLLIN;
  202. if (events & EV_WRITE)
  203. res |= POLLOUT;
  204. if (devpoll_queue(devpollop, fd, res) != 0)
  205. return (-1);
  206. return (0);
  207. }
  208. static int
  209. devpoll_del(struct event_base *base, int fd, short old, short events, void *p)
  210. {
  211. struct devpollop *devpollop = base->evbase;
  212. int res;
  213. (void)p;
  214. res = 0;
  215. if (events & EV_READ)
  216. res |= POLLIN;
  217. if (events & EV_WRITE)
  218. res |= POLLOUT;
  219. /*
  220. * The only way to remove an fd from the /dev/poll monitored set is
  221. * to use POLLREMOVE by itself. This removes ALL events for the fd
  222. * provided so if we care about two events and are only removing one
  223. * we must re-add the other event after POLLREMOVE.
  224. */
  225. if (devpoll_queue(devpollop, fd, POLLREMOVE) != 0)
  226. return (-1);
  227. if ((res & (POLLIN|POLLOUT)) != (POLLIN|POLLOUT)) {
  228. /*
  229. * We're not deleting all events, so we must resubmit the
  230. * event that we are still interested in if one exists.
  231. */
  232. if ((res & POLLIN) && (old & EV_WRITE)) {
  233. /* Deleting read, still care about write */
  234. devpoll_queue(devpollop, fd, POLLOUT);
  235. } else if ((res & POLLOUT) && (old & EV_READ)) {
  236. /* Deleting write, still care about read */
  237. devpoll_queue(devpollop, fd, POLLIN);
  238. }
  239. }
  240. return (0);
  241. }
  242. static void
  243. devpoll_dealloc(struct event_base *base)
  244. {
  245. struct devpollop *devpollop = base->evbase;
  246. evsig_dealloc_(base);
  247. if (devpollop->events)
  248. mm_free(devpollop->events);
  249. if (devpollop->changes)
  250. mm_free(devpollop->changes);
  251. if (devpollop->dpfd >= 0)
  252. close(devpollop->dpfd);
  253. memset(devpollop, 0, sizeof(struct devpollop));
  254. mm_free(devpollop);
  255. }
  256. #endif /* EVENT__HAVE_DEVPOLL */