patch-2.4.0-test4 linux/fs/select.c

Next file: linux/fs/smbfs/file.c
Previous file: linux/fs/pipe.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test3/linux/fs/select.c linux/fs/select.c
@@ -31,57 +31,19 @@
  * understand what I'm doing here, then you understand how the linux
  * sleep/wakeup mechanism works.
  *
- * Two very simple procedures, poll_wait() and free_wait() make all the
+ * Two very simple procedures, poll_wait() and poll_freewait() make all the
  * work.  poll_wait() is an inline-function defined in <linux/poll.h>,
  * as all select/poll functions have to call it to add an entry to the
  * poll table.
  */
 
-/*
- * I rewrote this again to make the poll_table size variable, take some
- * more shortcuts, improve responsiveness, and remove another race that
- * Linus noticed.  -- jrs
- */
-
-static poll_table* alloc_wait(int nfds)
-{
-	poll_table* out;
-	poll_table* walk;
-
-	out = (poll_table *) __get_free_page(GFP_KERNEL);
-	if(out==NULL)
-		return NULL;
-	out->nr = 0;
-	out->entry = (struct poll_table_entry *)(out + 1);
-	out->next = NULL;
-	nfds -=__MAX_POLL_TABLE_ENTRIES;
-	walk = out;
-	while(nfds > 0) {
-		poll_table *tmp = (poll_table *) __get_free_page(GFP_KERNEL);
-		if (!tmp) {
-			while(out != NULL) {
-				tmp = out->next;
-				free_page((unsigned long)out);
-				out = tmp;
-			}
-			return NULL;
-		}
-		tmp->nr = 0;
-		tmp->entry = (struct poll_table_entry *)(tmp + 1);
-		tmp->next = NULL;
-		walk->next = tmp;
-		walk = tmp;
-		nfds -=__MAX_POLL_TABLE_ENTRIES;
-	}
-	return out;
-}
-
-static void free_wait(poll_table * p)
+void poll_freewait(poll_table* pt)
 {
-	struct poll_table_entry * entry;
-	poll_table *old;
-
+	struct poll_table_page * p = pt->table;
 	while (p) {
+		struct poll_table_entry * entry;
+		struct poll_table_page *old;
+
 		entry = p->entry + p->nr;
 		while (p->nr > 0) {
 			p->nr--;
@@ -97,19 +59,34 @@
 
 void __pollwait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
 {
-	for (;;) {
-		if (p->nr < __MAX_POLL_TABLE_ENTRIES) {
-			struct poll_table_entry * entry;
-		 	entry = p->entry + p->nr;
-		 	get_file(filp);
-		 	entry->filp = filp;
-			entry->wait_address = wait_address;
-			init_waitqueue_entry(&entry->wait, current);
-			add_wait_queue(wait_address,&entry->wait);
-			p->nr++;
+	struct poll_table_page *table = p->table;
+
+	if (!table || table->nr >= __MAX_POLL_TABLE_ENTRIES) {
+		struct poll_table_page *new_table;
+
+		new_table = (struct poll_table_page *) __get_free_page(GFP_KERNEL);
+		if (!new_table) {
+			p->error = -ENOMEM;
+			__set_current_state(TASK_RUNNING);
 			return;
 		}
-		p = p->next;
+		new_table->nr = 0;
+		new_table->entry = (struct poll_table_entry *)(new_table + 1);
+		new_table->next = table;
+		p->table = new_table;
+		table = new_table;
+	}
+
+	/* Add a new entry */
+	{
+		struct poll_table_entry * entry;
+	 	entry = table->entry + table->nr;
+		table->nr++;
+	 	get_file(filp);
+	 	entry->filp = filp;
+		entry->wait_address = wait_address;
+		init_waitqueue_entry(&entry->wait, current);
+		add_wait_queue(wait_address,&entry->wait);
 	}
 }
 
@@ -173,12 +150,10 @@
 
 int do_select(int n, fd_set_bits *fds, long *timeout)
 {
-	poll_table *wait, *orig_wait;
+	poll_table table, *wait;
 	int retval, i, off;
 	long __timeout = *timeout;
 
-	orig_wait = wait = NULL;
-
  	read_lock(&current->files->file_lock);
 	retval = max_select_fd(n, fds);
 	read_unlock(&current->files->file_lock);
@@ -186,11 +161,11 @@
 	if (retval < 0)
 		return retval;
 	n = retval;
-	if (__timeout) {
- 		orig_wait = wait = alloc_wait(n);
- 		if (!wait)
-			return -ENOMEM;
- 	}
+
+	poll_initwait(&table);
+	wait = &table;
+	if (!__timeout)
+		wait = NULL;
 	retval = 0;
 	for (;;) {
 		set_current_state(TASK_INTERRUPTIBLE);
@@ -229,11 +204,15 @@
 		wait = NULL;
 		if (retval || !__timeout || signal_pending(current))
 			break;
+		if(table.error) {
+			retval = table.error;
+			break;
+		}
 		__timeout = schedule_timeout(__timeout);
 	}
 	current->state = TASK_RUNNING;
 
-	free_wait(orig_wait);
+	poll_freewait(&table);
 
 	/*
 	 * Up-to-date the caller timeout.
@@ -382,18 +361,22 @@
 	struct pollfd *fds[], poll_table *wait, long timeout)
 {
 	int count = 0;
+	poll_table* pt = wait;
 
 	for (;;) {
 		unsigned int i;
 
 		set_current_state(TASK_INTERRUPTIBLE);
 		for (i=0; i < nchunks; i++)
-			do_pollfd(POLLFD_PER_PAGE, fds[i], &wait, &count);
+			do_pollfd(POLLFD_PER_PAGE, fds[i], &pt, &count);
 		if (nleft)
-			do_pollfd(nleft, fds[nchunks], &wait, &count);
-		wait = NULL;
+			do_pollfd(nleft, fds[nchunks], &pt, &count);
+		pt = NULL;
 		if (count || !timeout || signal_pending(current))
 			break;
+		if(wait->error) {
+			return wait->error;
+		}
 		timeout = schedule_timeout(timeout);
 	}
 	current->state = TASK_RUNNING;
@@ -404,7 +387,7 @@
 {
 	int i, j, fdcount, err;
 	struct pollfd **fds;
-	poll_table *wait = NULL;
+	poll_table table, *wait;
 	int nchunks, nleft;
 
 	/* Do a sanity check on nfds ... */
@@ -419,13 +402,12 @@
 			timeout = MAX_SCHEDULE_TIMEOUT;
 	}
 
-	if (timeout) {
-		wait = alloc_wait(nfds);
-		if (!wait)
-			return -ENOMEM;
-	}
-	err = -ENOMEM;
+	poll_initwait(&table);
+	wait = &table;
+	if (!timeout)
+		wait = NULL;
 
+	err = -ENOMEM;
 	fds = NULL;
 	if (nfds != 0) {
 		fds = (struct pollfd **)kmalloc(
@@ -483,6 +465,6 @@
 	if (nfds != 0)
 		kfree(fds);
 out:
-	free_wait(wait);
+	poll_freewait(&table);
 	return err;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)