diff options
Diffstat (limited to 'fs/select.c')
-rw-r--r-- | fs/select.c | 83 |
1 files changed, 51 insertions, 32 deletions
diff --git a/fs/select.c b/fs/select.c index a8109baa5e46..9c4f0f2604f1 100644 --- a/fs/select.c +++ b/fs/select.c | |||
@@ -546,37 +546,38 @@ struct poll_list { | |||
546 | 546 | ||
547 | #define POLLFD_PER_PAGE ((PAGE_SIZE-sizeof(struct poll_list)) / sizeof(struct pollfd)) | 547 | #define POLLFD_PER_PAGE ((PAGE_SIZE-sizeof(struct poll_list)) / sizeof(struct pollfd)) |
548 | 548 | ||
549 | static void do_pollfd(unsigned int num, struct pollfd * fdpage, | 549 | /* |
550 | poll_table ** pwait, int *count) | 550 | * Fish for pollable events on the pollfd->fd file descriptor. We're only |
551 | * interested in events matching the pollfd->events mask, and the result | ||
552 | * matching that mask is both recorded in pollfd->revents and returned. The | ||
553 | * pwait poll_table will be used by the fd-provided poll handler for waiting, | ||
554 | * if non-NULL. | ||
555 | */ | ||
556 | static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait) | ||
551 | { | 557 | { |
552 | int i; | 558 | unsigned int mask; |
553 | 559 | int fd; | |
554 | for (i = 0; i < num; i++) { | 560 | |
555 | int fd; | 561 | mask = 0; |
556 | unsigned int mask; | 562 | fd = pollfd->fd; |
557 | struct pollfd *fdp; | 563 | if (fd >= 0) { |
558 | 564 | int fput_needed; | |
559 | mask = 0; | 565 | struct file * file; |
560 | fdp = fdpage+i; | 566 | |
561 | fd = fdp->fd; | 567 | file = fget_light(fd, &fput_needed); |
562 | if (fd >= 0) { | 568 | mask = POLLNVAL; |
563 | int fput_needed; | 569 | if (file != NULL) { |
564 | struct file * file = fget_light(fd, &fput_needed); | 570 | mask = DEFAULT_POLLMASK; |
565 | mask = POLLNVAL; | 571 | if (file->f_op && file->f_op->poll) |
566 | if (file != NULL) { | 572 | mask = file->f_op->poll(file, pwait); |
567 | mask = DEFAULT_POLLMASK; | 573 | /* Mask out unneeded events. */ |
568 | if (file->f_op && file->f_op->poll) | 574 | mask &= pollfd->events | POLLERR | POLLHUP; |
569 | mask = file->f_op->poll(file, *pwait); | 575 | fput_light(file, fput_needed); |
570 | mask &= fdp->events | POLLERR | POLLHUP; | ||
571 | fput_light(file, fput_needed); | ||
572 | } | ||
573 | if (mask) { | ||
574 | *pwait = NULL; | ||
575 | (*count)++; | ||
576 | } | ||
577 | } | 576 | } |
578 | fdp->revents = mask; | ||
579 | } | 577 | } |
578 | pollfd->revents = mask; | ||
579 | |||
580 | return mask; | ||
580 | } | 581 | } |
581 | 582 | ||
582 | static int do_poll(unsigned int nfds, struct poll_list *list, | 583 | static int do_poll(unsigned int nfds, struct poll_list *list, |
@@ -594,11 +595,29 @@ static int do_poll(unsigned int nfds, struct poll_list *list, | |||
594 | long __timeout; | 595 | long __timeout; |
595 | 596 | ||
596 | set_current_state(TASK_INTERRUPTIBLE); | 597 | set_current_state(TASK_INTERRUPTIBLE); |
597 | walk = list; | 598 | for (walk = list; walk != NULL; walk = walk->next) { |
598 | while(walk != NULL) { | 599 | struct pollfd * pfd, * pfd_end; |
599 | do_pollfd( walk->len, walk->entries, &pt, &count); | 600 | |
600 | walk = walk->next; | 601 | pfd = walk->entries; |
602 | pfd_end = pfd + walk->len; | ||
603 | for (; pfd != pfd_end; pfd++) { | ||
604 | /* | ||
605 | * Fish for events. If we found one, record it | ||
606 | * and kill the poll_table, so we don't | ||
607 | * needlessly register any other waiters after | ||
608 | * this. They'll get immediately deregistered | ||
609 | * when we break out and return. | ||
610 | */ | ||
611 | if (do_pollfd(pfd, pt)) { | ||
612 | count++; | ||
613 | pt = NULL; | ||
614 | } | ||
615 | } | ||
601 | } | 616 | } |
617 | /* | ||
618 | * All waiters have already been registered, so don't provide | ||
619 | * a poll_table to them on the next loop iteration. | ||
620 | */ | ||
602 | pt = NULL; | 621 | pt = NULL; |
603 | if (count || !*timeout || signal_pending(current)) | 622 | if (count || !*timeout || signal_pending(current)) |
604 | break; | 623 | break; |