patch-2.1.53 linux/fs/select.c

Next file: linux/include/asm-m68k/elf.h
Previous file: linux/fs/dcache.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.52/linux/fs/select.c linux/fs/select.c
@@ -141,25 +141,16 @@
 #define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
 #define POLLEX_SET (POLLPRI)
 
-static int do_select(int n, fd_set_buffer *fds)
+static int do_select(int n, fd_set_buffer *fds, poll_table *wait)
 {
 	int retval;
-	poll_table wait_table, *wait;
-	struct poll_table_entry *entry;
 	int i;
 
 	retval = max_select_fd(n, fds);
 	if (retval < 0)
 		goto out;
 	n = retval;
-	retval = -ENOMEM;
-	entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL);
-	if (!entry)
-		goto out;
 	retval = 0;
-	wait_table.nr = 0;
-	wait_table.entry = entry;
-	wait = &wait_table;
 	for (;;) {
 		struct file ** fd = current->files->fd;
 		current->state = TASK_INTERRUPTIBLE;
@@ -197,8 +188,6 @@
 			break;
 		schedule();
 	}
-	free_wait(&wait_table);
-	free_page((unsigned long) entry);
 	current->state = TASK_RUNNING;
 out:
 	return retval;
@@ -286,51 +275,60 @@
  */
 asmlinkage int sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp)
 {
-	int error = -EINVAL;
+	int error;
 	fd_set_buffer *fds;
 	unsigned long timeout;
+	poll_table wait_table, *wait;
 
 	lock_kernel();
-	fds = (fd_set_buffer *) __get_free_page(GFP_KERNEL);
-	if (!fds)
-		goto out;
-	if (n < 0)
-		goto out;
-	if (n > KFDS_NR)
-		n = KFDS_NR;
-	if ((error = get_fd_set(n, inp, &fds->in)) ||
-	    (error = get_fd_set(n, outp, &fds->out)) ||
-	    (error = get_fd_set(n, exp, &fds->ex))) goto out;
 	timeout = ~0UL;
 	if (tvp) {
-		error = verify_area(VERIFY_WRITE, tvp, sizeof(*tvp));
+		error = -EFAULT;
+		if (!access_ok(VERIFY_WRITE, tvp, sizeof(*tvp)))
+			goto out_nowait;
+		error = __get_user(timeout, &tvp->tv_usec);
 		if (error)
-			goto out;
-		__get_user(timeout, &tvp->tv_usec);
+			goto out_nowait;
 		timeout = ROUND_UP(timeout,(1000000/HZ));
 		{
 			unsigned long tmp;
-			__get_user(tmp, &tvp->tv_sec);
+			error = __get_user(tmp, &tvp->tv_sec);
+			if (error)
+				goto out_nowait;
 			timeout += tmp * (unsigned long) HZ;
 		}
 		if (timeout)
 			timeout += jiffies + 1;
 	}
+	error = -ENOMEM;
+	wait = NULL;
+	current->timeout = timeout;
+	if (timeout) {
+		struct poll_table_entry *entry;
+
+		entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL);
+		if (!entry)
+			goto out_nowait;
+		wait_table.nr = 0;
+		wait_table.entry = entry;
+		wait = &wait_table;
+	}
+	fds = (fd_set_buffer *) __get_free_page(GFP_KERNEL);
+	if (!fds)
+		goto out_nofds;
+	error = -EINVAL;
+	if (n < 0)
+		goto out;
+	if (n > KFDS_NR)
+		n = KFDS_NR;
+	if ((error = get_fd_set(n, inp, &fds->in)) ||
+	    (error = get_fd_set(n, outp, &fds->out)) ||
+	    (error = get_fd_set(n, exp, &fds->ex))) goto out;
 	zero_fd_set(n, &fds->res_in);
 	zero_fd_set(n, &fds->res_out);
 	zero_fd_set(n, &fds->res_ex);
-	current->timeout = timeout;
-	error = do_select(n, fds);
-	timeout = current->timeout - jiffies - 1;
+	error = do_select(n, fds, wait);
 	current->timeout = 0;
-	if ((long) timeout < 0)
-		timeout = 0;
-	if (tvp && !(current->personality & STICKY_TIMEOUTS)) {
-		__put_user(timeout/HZ, &tvp->tv_sec);
-		timeout %= HZ;
-		timeout *= (1000000/HZ);
-		__put_user(timeout, &tvp->tv_usec);
-	}
 	if (error < 0)
 		goto out;
 	if (!error) {
@@ -344,6 +342,12 @@
 	set_fd_set(n, exp, &fds->res_ex);
 out:
 	free_page((unsigned long) fds);
+out_nofds:
+	if (wait) {
+		free_wait(&wait_table);
+		free_page((unsigned long) wait->entry);
+	}
+out_nowait:
 	unlock_kernel();
 	return error;
 }
@@ -392,43 +396,43 @@
 {
 	int i, count, fdcount, err;
 	struct pollfd * fds, *fds1;
-	poll_table wait_table;
-	struct poll_table_entry *entry;
+	poll_table wait_table, *wait;
 
 	lock_kernel();
+	if (timeout < 0)
+		timeout = 0x7fffffff;
+	else if (timeout)
+		timeout = ((unsigned long)timeout*HZ+999)/1000+jiffies+1;
 	err = -ENOMEM;
-	entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL);
-	if (!entry)
-		goto out;
+
+	wait = NULL;
+	if (timeout) {
+		struct poll_table_entry *entry;
+		entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL);
+		if (!entry)
+			goto out;
+		wait_table.nr = 0;
+		wait_table.entry = entry;
+		wait = &wait_table;
+	}
 	fds = (struct pollfd *) kmalloc(nfds*sizeof(struct pollfd), GFP_KERNEL);
 	if (!fds) {
-		free_page((unsigned long) entry);
 		goto out;
 	}
 
 	err = -EFAULT;
 	if (copy_from_user(fds, ufds, nfds*sizeof(struct pollfd))) {
-		free_page((unsigned long)entry);
 		kfree(fds);
 		goto out;
 	}
 
-	if (timeout < 0)
-		timeout = 0x7fffffff;
-	else if (timeout)
-		timeout = ((unsigned long)timeout*HZ+999)/1000+jiffies+1;
 	current->timeout = timeout;
 
 	count = 0;
-	wait_table.nr = 0;
-	wait_table.entry = entry;
 
-	fdcount = do_poll(nfds, fds, timeout ? &wait_table : NULL);
+	fdcount = do_poll(nfds, fds, wait);
 	current->timeout = 0;
 
-	free_wait(&wait_table);
-	free_page((unsigned long) entry);
-
 	/* OK, now copy the revents fields back to user space. */
 	fds1 = fds;
 	for(i=0; i < (int)nfds; i++, ufds++, fds++) {
@@ -440,6 +444,10 @@
 	else
 		err = fdcount;
 out:
+	if (wait) {
+		free_wait(&wait_table);
+		free_page((unsigned long) wait->entry);
+	}
 	unlock_kernel();
 	return err;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov