diff options
-rw-r--r-- | fs/select.c | 92 |
1 files changed, 36 insertions, 56 deletions
diff --git a/fs/select.c b/fs/select.c index 46dca31c607a..41c3571e64ed 100644 --- a/fs/select.c +++ b/fs/select.c | |||
@@ -651,86 +651,66 @@ static int do_poll(unsigned int nfds, struct poll_list *list, | |||
651 | int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout) | 651 | int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout) |
652 | { | 652 | { |
653 | struct poll_wqueues table; | 653 | struct poll_wqueues table; |
654 | int fdcount, err; | 654 | int err = -EFAULT, fdcount, len, size; |
655 | unsigned int i; | ||
656 | struct poll_list *head; | ||
657 | struct poll_list *walk; | ||
658 | /* Allocate small arguments on the stack to save memory and be | 655 | /* Allocate small arguments on the stack to save memory and be |
659 | faster - use long to make sure the buffer is aligned properly | 656 | faster - use long to make sure the buffer is aligned properly |
660 | on 64 bit archs to avoid unaligned access */ | 657 | on 64 bit archs to avoid unaligned access */ |
661 | long stack_pps[POLL_STACK_ALLOC/sizeof(long)]; | 658 | long stack_pps[POLL_STACK_ALLOC/sizeof(long)]; |
662 | struct poll_list *stack_pp = NULL; | 659 | struct poll_list *const head = (struct poll_list *)stack_pps; |
660 | struct poll_list *walk = head; | ||
661 | unsigned long todo = nfds; | ||
663 | 662 | ||
664 | /* Do a sanity check on nfds ... */ | ||
665 | if (nfds > current->signal->rlim[RLIMIT_NOFILE].rlim_cur) | 663 | if (nfds > current->signal->rlim[RLIMIT_NOFILE].rlim_cur) |
666 | return -EINVAL; | 664 | return -EINVAL; |
667 | 665 | ||
668 | poll_initwait(&table); | 666 | len = min_t(unsigned int, nfds, N_STACK_PPS); |
667 | for (;;) { | ||
668 | walk->next = NULL; | ||
669 | walk->len = len; | ||
670 | if (!len) | ||
671 | break; | ||
669 | 672 | ||
670 | head = NULL; | 673 | if (copy_from_user(walk->entries, ufds + nfds-todo, |
671 | walk = NULL; | 674 | sizeof(struct pollfd) * walk->len)) |
672 | i = nfds; | 675 | goto out_fds; |
673 | err = -ENOMEM; | 676 | |
674 | while(i!=0) { | 677 | todo -= walk->len; |
675 | struct poll_list *pp; | 678 | if (!todo) |
676 | int num, size; | 679 | break; |
677 | if (stack_pp == NULL) | ||
678 | num = N_STACK_PPS; | ||
679 | else | ||
680 | num = POLLFD_PER_PAGE; | ||
681 | if (num > i) | ||
682 | num = i; | ||
683 | size = sizeof(struct poll_list) + sizeof(struct pollfd)*num; | ||
684 | if (!stack_pp) | ||
685 | stack_pp = pp = (struct poll_list *)stack_pps; | ||
686 | else { | ||
687 | pp = kmalloc(size, GFP_KERNEL); | ||
688 | if (!pp) | ||
689 | goto out_fds; | ||
690 | } | ||
691 | pp->next=NULL; | ||
692 | pp->len = num; | ||
693 | if (head == NULL) | ||
694 | head = pp; | ||
695 | else | ||
696 | walk->next = pp; | ||
697 | 680 | ||
698 | walk = pp; | 681 | len = min(todo, POLLFD_PER_PAGE); |
699 | if (copy_from_user(pp->entries, ufds + nfds-i, | 682 | size = sizeof(struct poll_list) + sizeof(struct pollfd) * len; |
700 | sizeof(struct pollfd)*num)) { | 683 | walk = walk->next = kmalloc(size, GFP_KERNEL); |
701 | err = -EFAULT; | 684 | if (!walk) { |
685 | err = -ENOMEM; | ||
702 | goto out_fds; | 686 | goto out_fds; |
703 | } | 687 | } |
704 | i -= pp->len; | ||
705 | } | 688 | } |
706 | 689 | ||
690 | poll_initwait(&table); | ||
707 | fdcount = do_poll(nfds, head, &table, timeout); | 691 | fdcount = do_poll(nfds, head, &table, timeout); |
692 | if (!fdcount && signal_pending(current)) | ||
693 | fdcount = -EINTR; | ||
694 | poll_freewait(&table); | ||
708 | 695 | ||
709 | /* OK, now copy the revents fields back to user space. */ | 696 | for (walk = head; walk; walk = walk->next) { |
710 | walk = head; | ||
711 | err = -EFAULT; | ||
712 | while(walk != NULL) { | ||
713 | struct pollfd *fds = walk->entries; | 697 | struct pollfd *fds = walk->entries; |
714 | int j; | 698 | int j; |
715 | 699 | ||
716 | for (j=0; j < walk->len; j++, ufds++) { | 700 | for (j = 0; j < walk->len; j++, ufds++) |
717 | if(__put_user(fds[j].revents, &ufds->revents)) | 701 | if (__put_user(fds[j].revents, &ufds->revents)) |
718 | goto out_fds; | 702 | goto out_fds; |
719 | } | ||
720 | walk = walk->next; | ||
721 | } | 703 | } |
704 | |||
722 | err = fdcount; | 705 | err = fdcount; |
723 | if (!fdcount && signal_pending(current)) | ||
724 | err = -EINTR; | ||
725 | out_fds: | 706 | out_fds: |
726 | walk = head; | 707 | walk = head->next; |
727 | while(walk!=NULL) { | 708 | while (walk) { |
728 | struct poll_list *pp = walk->next; | 709 | struct poll_list *pos = walk; |
729 | if (walk != stack_pp) | 710 | walk = walk->next; |
730 | kfree(walk); | 711 | kfree(pos); |
731 | walk = pp; | ||
732 | } | 712 | } |
733 | poll_freewait(&table); | 713 | |
734 | return err; | 714 | return err; |
735 | } | 715 | } |
736 | 716 | ||