1/* 2 * Copyright © 2016 Keith Packard 3 * 4 * Permission to use, copy, modify, distribute, and sell this software and its 5 * documentation for any purpose is hereby granted without fee, provided that 6 * the above copyright notice appear in all copies and that both that copyright 7 * notice and this permission notice appear in supporting documentation, and 8 * that the name of the copyright holders not be used in advertising or 9 * publicity pertaining to distribution of the software without specific, 10 * written prior permission. The copyright holders make no representations 11 * about the suitability of this software for any purpose. It is provided "as 12 * is" without express or implied warranty. 13 * 14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 20 * OF THIS SOFTWARE. 21 */ 22 23#ifdef HAVE_DIX_CONFIG_H 24#include <dix-config.h> 25#endif 26 27#include <X11/X.h> 28#include <X11/Xproto.h> 29#include <stdlib.h> 30#include <unistd.h> 31#include "misc.h" /* for typedef of pointer */ 32#include "ospoll.h" 33#include "list.h" 34 35#if !HAVE_OSPOLL && defined(HAVE_POLLSET_CREATE) 36#include <sys/pollset.h> 37#define POLLSET 1 38#define HAVE_OSPOLL 1 39#endif 40 41#if !HAVE_OSPOLL && defined(HAVE_PORT_CREATE) 42#include <port.h> 43#include <poll.h> 44#define PORT 1 45#define HAVE_OSPOLL 1 46#endif 47 48#if !HAVE_OSPOLL && defined(HAVE_EPOLL_CREATE1) 49#include <sys/epoll.h> 50#define EPOLL 1 51#define HAVE_OSPOLL 1 52#endif 53 54#if !HAVE_OSPOLL 55#include "xserver_poll.h" 56#define POLL 1 57#define HAVE_OSPOLL 1 58#endif 59 60#if POLLSET 61 62// pollset-based implementation (as seen on AIX) 63struct ospollfd { 64 int fd; 65 int xevents; 66 short revents; 67 enum ospoll_trigger trigger; 68 void (*callback)(int fd, int xevents, void *data); 69 void *data; 70}; 71 72struct ospoll { 73 pollset_t ps; 74 struct ospollfd *fds; 75 int num; 76 int size; 77}; 78 79#endif 80 81#if EPOLL || PORT 82 83/* epoll-based implementation */ 84struct ospollfd { 85 int fd; 86 int xevents; 87 enum ospoll_trigger trigger; 88 void (*callback)(int fd, int xevents, void *data); 89 void *data; 90 struct xorg_list deleted; 91}; 92 93struct ospoll { 94 int epoll_fd; 95 struct ospollfd **fds; 96 int num; 97 int size; 98 struct xorg_list deleted; 99}; 100 101#endif 102 103#if POLL 104 105/* poll-based implementation */ 106struct ospollfd { 107 short revents; 108 enum ospoll_trigger trigger; 109 void (*callback)(int fd, int revents, void *data); 110 void *data; 111}; 112 113struct ospoll { 114 struct pollfd *fds; 115 struct ospollfd *osfds; 116 int num; 117 int size; 118 Bool changed; 119}; 120 121#endif 122 123/* Binary search for the specified file descriptor 124 * 125 * Returns position if found 126 * Returns -position - 1 if not found 127 */ 128 129static int 130ospoll_find(struct ospoll *ospoll, int fd) 131{ 132 int lo = 0; 133 int hi = ospoll->num - 1; 134 135 while (lo <= hi) { 136 int m = (lo + hi) >> 1; 137#if EPOLL || PORT 138 int t = ospoll->fds[m]->fd; 139#endif 140#if POLL || POLLSET 141 int t = ospoll->fds[m].fd; 142#endif 143 144 if (t < fd) 145 lo = m + 1; 146 else if (t > fd) 147 hi = m - 1; 148 else 149 return m; 150 } 151 return -(lo + 1); 152} 153 154#if EPOLL || PORT 155static void 156ospoll_clean_deleted(struct ospoll *ospoll) 157{ 158 struct ospollfd *osfd, *tmp; 159 160 xorg_list_for_each_entry_safe(osfd, tmp, &ospoll->deleted, deleted) { 161 xorg_list_del(&osfd->deleted); 162 free(osfd); 163 } 164} 165#endif 166 167/* Insert an element into an array 168 * 169 * base: base address of array 170 * num: number of elements in the array before the insert 171 * size: size of each element 172 * pos: position to insert at 173 */ 174static inline void 175array_insert(void *base, size_t num, size_t size, size_t pos) 176{ 177 char *b = base; 178 179 memmove(b + (pos+1) * size, 180 b + pos * size, 181 (num - pos) * size); 182} 183 184/* Delete an element from an array 185 * 186 * base: base address of array 187 * num: number of elements in the array before the delete 188 * size: size of each element 189 * pos: position to delete from 190 */ 191static inline void 192array_delete(void *base, size_t num, size_t size, size_t pos) 193{ 194 char *b = base; 195 196 memmove(b + pos * size, b + (pos + 1) * size, 197 (num - pos - 1) * size); 198} 199 200 201struct ospoll * 202ospoll_create(void) 203{ 204#if POLLSET 205 struct ospoll *ospoll = calloc(1, sizeof (struct ospoll)); 206 207 ospoll->ps = pollset_create(-1); 208 if (ospoll->ps < 0) { 209 free (ospoll); 210 return NULL; 211 } 212 return ospoll; 213#endif 214#if PORT 215 struct ospoll *ospoll = calloc(1, sizeof (struct ospoll)); 216 217 ospoll->epoll_fd = port_create(); 218 if (ospoll->epoll_fd < 0) { 219 free (ospoll); 220 return NULL; 221 } 222 xorg_list_init(&ospoll->deleted); 223 return ospoll; 224#endif 225#if EPOLL 226 struct ospoll *ospoll = calloc(1, sizeof (struct ospoll)); 227 228 ospoll->epoll_fd = epoll_create1(EPOLL_CLOEXEC); 229 if (ospoll->epoll_fd < 0) { 230 free (ospoll); 231 return NULL; 232 } 233 xorg_list_init(&ospoll->deleted); 234 return ospoll; 235#endif 236#if POLL 237 return calloc(1, sizeof (struct ospoll)); 238#endif 239} 240 241void 242ospoll_destroy(struct ospoll *ospoll) 243{ 244#if POLLSET 245 if (ospoll) { 246 assert (ospoll->num == 0); 247 pollset_destroy(ospoll->ps); 248 free(ospoll->fds); 249 free(ospoll); 250 } 251#endif 252#if EPOLL || PORT 253 if (ospoll) { 254 assert (ospoll->num == 0); 255 close(ospoll->epoll_fd); 256 ospoll_clean_deleted(ospoll); 257 free(ospoll->fds); 258 free(ospoll); 259 } 260#endif 261#if POLL 262 if (ospoll) { 263 assert (ospoll->num == 0); 264 free (ospoll->fds); 265 free (ospoll->osfds); 266 free (ospoll); 267 } 268#endif 269} 270 271Bool 272ospoll_add(struct ospoll *ospoll, int fd, 273 enum ospoll_trigger trigger, 274 void (*callback)(int fd, int xevents, void *data), 275 void *data) 276{ 277 int pos = ospoll_find(ospoll, fd); 278#if POLLSET 279 if (pos < 0) { 280 if (ospoll->num == ospoll->size) { 281 struct ospollfd *new_fds; 282 int new_size = ospoll->size ? ospoll->size * 2 : MAXCLIENTS * 2; 283 284 new_fds = reallocarray(ospoll->fds, new_size, sizeof (ospoll->fds[0])); 285 if (!new_fds) 286 return FALSE; 287 ospoll->fds = new_fds; 288 ospoll->size = new_size; 289 } 290 pos = -pos - 1; 291 array_insert(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos); 292 ospoll->num++; 293 294 ospoll->fds[pos].fd = fd; 295 ospoll->fds[pos].xevents = 0; 296 ospoll->fds[pos].revents = 0; 297 } 298 ospoll->fds[pos].trigger = trigger; 299 ospoll->fds[pos].callback = callback; 300 ospoll->fds[pos].data = data; 301#endif 302#if PORT 303 struct ospollfd *osfd; 304 305 if (pos < 0) { 306 osfd = calloc(1, sizeof (struct ospollfd)); 307 if (!osfd) 308 return FALSE; 309 310 if (ospoll->num >= ospoll->size) { 311 struct ospollfd **new_fds; 312 int new_size = ospoll->size ? ospoll->size * 2 : MAXCLIENTS * 2; 313 314 new_fds = reallocarray(ospoll->fds, new_size, sizeof (ospoll->fds[0])); 315 if (!new_fds) { 316 free (osfd); 317 return FALSE; 318 } 319 ospoll->fds = new_fds; 320 ospoll->size = new_size; 321 } 322 323 osfd->fd = fd; 324 osfd->xevents = 0; 325 326 pos = -pos - 1; 327 array_insert(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos); 328 ospoll->fds[pos] = osfd; 329 ospoll->num++; 330 } else { 331 osfd = ospoll->fds[pos]; 332 } 333 osfd->data = data; 334 osfd->callback = callback; 335 osfd->trigger = trigger; 336#endif 337#if EPOLL 338 struct ospollfd *osfd; 339 340 if (pos < 0) { 341 342 struct epoll_event ev; 343 344 osfd = calloc(1, sizeof (struct ospollfd)); 345 if (!osfd) 346 return FALSE; 347 348 if (ospoll->num >= ospoll->size) { 349 struct ospollfd **new_fds; 350 int new_size = ospoll->size ? ospoll->size * 2 : MAXCLIENTS * 2; 351 352 new_fds = reallocarray(ospoll->fds, new_size, sizeof (ospoll->fds[0])); 353 if (!new_fds) { 354 free (osfd); 355 return FALSE; 356 } 357 ospoll->fds = new_fds; 358 ospoll->size = new_size; 359 } 360 361 ev.events = 0; 362 ev.data.ptr = osfd; 363 if (trigger == ospoll_trigger_edge) 364 ev.events |= EPOLLET; 365 if (epoll_ctl(ospoll->epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) { 366 free(osfd); 367 return FALSE; 368 } 369 osfd->fd = fd; 370 osfd->xevents = 0; 371 372 pos = -pos - 1; 373 array_insert(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos); 374 ospoll->fds[pos] = osfd; 375 ospoll->num++; 376 } else { 377 osfd = ospoll->fds[pos]; 378 } 379 osfd->data = data; 380 osfd->callback = callback; 381 osfd->trigger = trigger; 382#endif 383#if POLL 384 if (pos < 0) { 385 if (ospoll->num == ospoll->size) { 386 struct pollfd *new_fds; 387 struct ospollfd *new_osfds; 388 int new_size = ospoll->size ? ospoll->size * 2 : MAXCLIENTS * 2; 389 390 new_fds = reallocarray(ospoll->fds, new_size, sizeof (ospoll->fds[0])); 391 if (!new_fds) 392 return FALSE; 393 ospoll->fds = new_fds; 394 new_osfds = reallocarray(ospoll->osfds, new_size, sizeof (ospoll->osfds[0])); 395 if (!new_osfds) 396 return FALSE; 397 ospoll->osfds = new_osfds; 398 ospoll->size = new_size; 399 } 400 pos = -pos - 1; 401 array_insert(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos); 402 array_insert(ospoll->osfds, ospoll->num, sizeof (ospoll->osfds[0]), pos); 403 ospoll->num++; 404 ospoll->changed = TRUE; 405 406 ospoll->fds[pos].fd = fd; 407 ospoll->fds[pos].events = 0; 408 ospoll->fds[pos].revents = 0; 409 ospoll->osfds[pos].revents = 0; 410 } 411 ospoll->osfds[pos].trigger = trigger; 412 ospoll->osfds[pos].callback = callback; 413 ospoll->osfds[pos].data = data; 414#endif 415 return TRUE; 416} 417 418void 419ospoll_remove(struct ospoll *ospoll, int fd) 420{ 421 int pos = ospoll_find(ospoll, fd); 422 423 pos = ospoll_find(ospoll, fd); 424 if (pos >= 0) { 425#if POLLSET 426 struct ospollfd *osfd = &ospoll->fds[pos]; 427 struct poll_ctl ctl = { .cmd = PS_DELETE, .fd = fd }; 428 pollset_ctl(ospoll->ps, &ctl, 1); 429 430 array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos); 431 ospoll->num--; 432#endif 433#if PORT 434 struct ospollfd *osfd = ospoll->fds[pos]; 435 port_dissociate(ospoll->epoll_fd, PORT_SOURCE_FD, fd); 436 437 array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos); 438 ospoll->num--; 439 osfd->callback = NULL; 440 osfd->data = NULL; 441 xorg_list_add(&osfd->deleted, &ospoll->deleted); 442#endif 443#if EPOLL 444 struct ospollfd *osfd = ospoll->fds[pos]; 445 struct epoll_event ev; 446 ev.events = 0; 447 ev.data.ptr = osfd; 448 (void) epoll_ctl(ospoll->epoll_fd, EPOLL_CTL_DEL, fd, &ev); 449 450 array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos); 451 ospoll->num--; 452 osfd->callback = NULL; 453 osfd->data = NULL; 454 xorg_list_add(&osfd->deleted, &ospoll->deleted); 455#endif 456#if POLL 457 array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos); 458 array_delete(ospoll->osfds, ospoll->num, sizeof (ospoll->osfds[0]), pos); 459 ospoll->num--; 460 ospoll->changed = TRUE; 461#endif 462 } 463} 464 465#if PORT 466static void 467epoll_mod(struct ospoll *ospoll, struct ospollfd *osfd) 468{ 469 int events = 0; 470 if (osfd->xevents & X_NOTIFY_READ) 471 events |= POLLIN; 472 if (osfd->xevents & X_NOTIFY_WRITE) 473 events |= POLLOUT; 474 port_associate(ospoll->epoll_fd, PORT_SOURCE_FD, osfd->fd, events, osfd); 475} 476#endif 477 478#if EPOLL 479static void 480epoll_mod(struct ospoll *ospoll, struct ospollfd *osfd) 481{ 482 struct epoll_event ev; 483 ev.events = 0; 484 if (osfd->xevents & X_NOTIFY_READ) 485 ev.events |= EPOLLIN; 486 if (osfd->xevents & X_NOTIFY_WRITE) 487 ev.events |= EPOLLOUT; 488 if (osfd->trigger == ospoll_trigger_edge) 489 ev.events |= EPOLLET; 490 ev.data.ptr = osfd; 491 (void) epoll_ctl(ospoll->epoll_fd, EPOLL_CTL_MOD, osfd->fd, &ev); 492} 493#endif 494 495void 496ospoll_listen(struct ospoll *ospoll, int fd, int xevents) 497{ 498 int pos = ospoll_find(ospoll, fd); 499 500 if (pos >= 0) { 501#if POLLSET 502 struct poll_ctl ctl = { .cmd = PS_MOD, .fd = fd }; 503 if (xevents & X_NOTIFY_READ) { 504 ctl.events |= POLLIN; 505 ospoll->fds[pos].revents &= ~POLLIN; 506 } 507 if (xevents & X_NOTIFY_WRITE) { 508 ctl.events |= POLLOUT; 509 ospoll->fds[pos].revents &= ~POLLOUT; 510 } 511 pollset_ctl(ospoll->ps, &ctl, 1); 512 ospoll->fds[pos].xevents |= xevents; 513#endif 514#if EPOLL || PORT 515 struct ospollfd *osfd = ospoll->fds[pos]; 516 osfd->xevents |= xevents; 517 epoll_mod(ospoll, osfd); 518#endif 519#if POLL 520 if (xevents & X_NOTIFY_READ) { 521 ospoll->fds[pos].events |= POLLIN; 522 ospoll->osfds[pos].revents &= ~POLLIN; 523 } 524 if (xevents & X_NOTIFY_WRITE) { 525 ospoll->fds[pos].events |= POLLOUT; 526 ospoll->osfds[pos].revents &= ~POLLOUT; 527 } 528#endif 529 } 530} 531 532void 533ospoll_mute(struct ospoll *ospoll, int fd, int xevents) 534{ 535 int pos = ospoll_find(ospoll, fd); 536 537 if (pos >= 0) { 538#if POLLSET 539 struct ospollfd *osfd = &ospoll->fds[pos]; 540 osfd->xevents &= ~xevents; 541 struct poll_ctl ctl = { .cmd = PS_DELETE, .fd = fd }; 542 pollset_ctl(ospoll->ps, &ctl, 1); 543 if (osfd->xevents) { 544 ctl.cmd = PS_ADD; 545 if (osfd->xevents & X_NOTIFY_READ) { 546 ctl.events |= POLLIN; 547 } 548 if (osfd->xevents & X_NOTIFY_WRITE) { 549 ctl.events |= POLLOUT; 550 } 551 pollset_ctl(ospoll->ps, &ctl, 1); 552 } 553#endif 554#if EPOLL || PORT 555 struct ospollfd *osfd = ospoll->fds[pos]; 556 osfd->xevents &= ~xevents; 557 epoll_mod(ospoll, osfd); 558#endif 559#if POLL 560 if (xevents & X_NOTIFY_READ) 561 ospoll->fds[pos].events &= ~POLLIN; 562 if (xevents & X_NOTIFY_WRITE) 563 ospoll->fds[pos].events &= ~POLLOUT; 564#endif 565 } 566} 567 568 569int 570ospoll_wait(struct ospoll *ospoll, int timeout) 571{ 572 int nready; 573#if POLLSET 574#define MAX_EVENTS 256 575 struct pollfd events[MAX_EVENTS]; 576 577 nready = pollset_poll(ospoll->ps, events, MAX_EVENTS, timeout); 578 for (int i = 0; i < nready; i++) { 579 struct pollfd *ev = &events[i]; 580 int pos = ospoll_find(ospoll, ev->fd); 581 struct ospollfd *osfd = &ospoll->fds[pos]; 582 short revents = ev->revents; 583 short oldevents = osfd->revents; 584 585 osfd->revents = (revents & (POLLIN|POLLOUT)); 586 if (osfd->trigger == ospoll_trigger_edge) 587 revents &= ~oldevents; 588 if (revents) { 589 int xevents = 0; 590 if (revents & POLLIN) 591 xevents |= X_NOTIFY_READ; 592 if (revents & POLLOUT) 593 xevents |= X_NOTIFY_WRITE; 594 if (revents & (~(POLLIN|POLLOUT))) 595 xevents |= X_NOTIFY_ERROR; 596 osfd->callback(osfd->fd, xevents, osfd->data); 597 } 598 } 599#endif 600#if PORT 601#define MAX_EVENTS 256 602 port_event_t events[MAX_EVENTS]; 603 uint_t nget = 1; 604 timespec_t port_timeout = { 605 .tv_sec = timeout / 1000, 606 .tv_nsec = (timeout % 1000) * 1000000 607 }; 608 609 nready = 0; 610 if (port_getn(ospoll->epoll_fd, events, MAX_EVENTS, &nget, &port_timeout) 611 == 0) { 612 nready = nget; 613 } 614 for (int i = 0; i < nready; i++) { 615 port_event_t *ev = &events[i]; 616 struct ospollfd *osfd = ev->portev_user; 617 uint32_t revents = ev->portev_events; 618 int xevents = 0; 619 620 if (revents & POLLIN) 621 xevents |= X_NOTIFY_READ; 622 if (revents & POLLOUT) 623 xevents |= X_NOTIFY_WRITE; 624 if (revents & (~(POLLIN|POLLOUT))) 625 xevents |= X_NOTIFY_ERROR; 626 627 if (osfd->callback) 628 osfd->callback(osfd->fd, xevents, osfd->data); 629 630 if (osfd->trigger == ospoll_trigger_level && 631 !xorg_list_is_empty(&osfd->deleted)) { 632 epoll_mod(ospoll, osfd); 633 } 634 } 635 ospoll_clean_deleted(ospoll); 636#endif 637#if EPOLL 638#define MAX_EVENTS 256 639 struct epoll_event events[MAX_EVENTS]; 640 int i; 641 642 nready = epoll_wait(ospoll->epoll_fd, events, MAX_EVENTS, timeout); 643 for (i = 0; i < nready; i++) { 644 struct epoll_event *ev = &events[i]; 645 struct ospollfd *osfd = ev->data.ptr; 646 uint32_t revents = ev->events; 647 int xevents = 0; 648 649 if (revents & EPOLLIN) 650 xevents |= X_NOTIFY_READ; 651 if (revents & EPOLLOUT) 652 xevents |= X_NOTIFY_WRITE; 653 if (revents & (~(EPOLLIN|EPOLLOUT))) 654 xevents |= X_NOTIFY_ERROR; 655 656 if (osfd->callback) 657 osfd->callback(osfd->fd, xevents, osfd->data); 658 } 659 ospoll_clean_deleted(ospoll); 660#endif 661#if POLL 662 nready = xserver_poll(ospoll->fds, ospoll->num, timeout); 663 ospoll->changed = FALSE; 664 if (nready > 0) { 665 int f; 666 for (f = 0; f < ospoll->num; f++) { 667 short revents = ospoll->fds[f].revents; 668 short oldevents = ospoll->osfds[f].revents; 669 670 ospoll->osfds[f].revents = (revents & (POLLIN|POLLOUT)); 671 if (ospoll->osfds[f].trigger == ospoll_trigger_edge) 672 revents &= ~oldevents; 673 if (revents) { 674 int xevents = 0; 675 if (revents & POLLIN) 676 xevents |= X_NOTIFY_READ; 677 if (revents & POLLOUT) 678 xevents |= X_NOTIFY_WRITE; 679 if (revents & (~(POLLIN|POLLOUT))) 680 xevents |= X_NOTIFY_ERROR; 681 ospoll->osfds[f].callback(ospoll->fds[f].fd, xevents, 682 ospoll->osfds[f].data); 683 684 /* Check to see if the arrays have changed, and just go back 685 * around again 686 */ 687 if (ospoll->changed) 688 break; 689 } 690 } 691 } 692#endif 693 return nready; 694} 695 696void 697ospoll_reset_events(struct ospoll *ospoll, int fd) 698{ 699#if POLLSET 700 int pos = ospoll_find(ospoll, fd); 701 702 if (pos < 0) 703 return; 704 705 ospoll->fds[pos].revents = 0; 706#endif 707#if PORT 708 int pos = ospoll_find(ospoll, fd); 709 710 if (pos < 0) 711 return; 712 713 epoll_mod(ospoll, ospoll->fds[pos]); 714#endif 715#if POLL 716 int pos = ospoll_find(ospoll, fd); 717 718 if (pos < 0) 719 return; 720 721 ospoll->osfds[pos].revents = 0; 722#endif 723} 724 725void * 726ospoll_data(struct ospoll *ospoll, int fd) 727{ 728 int pos = ospoll_find(ospoll, fd); 729 730 if (pos < 0) 731 return NULL; 732#if POLLSET 733 return ospoll->fds[pos].data; 734#endif 735#if EPOLL || PORT 736 return ospoll->fds[pos]->data; 737#endif 738#if POLL 739 return ospoll->osfds[pos].data; 740#endif 741} 742