aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/select.c92
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,
651int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout) 651int 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;
725out_fds: 706out_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