From: Manfred Spraul <manfred@colorfullife.com>

readdir on /proc has two problems: reading all entries is O(N^2), and
entries are overlooked if tasks die in the middle of readdir: the readdir
implementation remembers the offset into the task list, and if a task
(actually: process) that was returned by previous readdir calls exits, then
a random entry is dropped.

The attached patch fixes the O(N^2) by using f_version to store the pid of
the task that should be returned next.  This speeds up reading /proc to
O(N).  Additionally, it mitigates the effects of dying tasks: Tasks are
skipped only if the task whose pid is stored in f_version exits, all other
task deaths have no effect.  Unfortunately the code has a bad worst case
behavior: if the targeted task exits and a new task with the same pid is
created, then all entries in the task list between old and new position are
dropped.  This should be rare.



 fs/proc/base.c |   25 +++++++++++++++++++++----
 1 files changed, 21 insertions(+), 4 deletions(-)

diff -puN fs/proc/base.c~proc_pid_lookup-speedup fs/proc/base.c
--- 25/fs/proc/base.c~proc_pid_lookup-speedup	2004-01-06 09:20:15.000000000 -0800
+++ 25-akpm/fs/proc/base.c	2004-01-06 09:24:44.000000000 -0800
@@ -1669,14 +1669,26 @@ out:
  * tasklist lock while doing this, and we must release it before
  * we actually do the filldir itself, so we use a temp buffer..
  */
-static int get_tgid_list(int index, unsigned int *tgids)
+static int get_tgid_list(int index, unsigned long version, unsigned int *tgids)
 {
 	struct task_struct *p;
 	int nr_tgids = 0;
 
 	index--;
 	read_lock(&tasklist_lock);
-	for_each_process(p) {
+	p = NULL;
+	if (version) {
+		p = find_task_by_pid(version);
+		if (!thread_group_leader(p))
+			p = NULL;
+	}
+
+	if (p)
+		index = 0;
+	else
+		p = next_task(&init_task);
+
+	for ( ; p != &init_task; p = next_task(p)) {
 		int tgid = p->pid;
 		if (!pid_alive(p))
 			continue;
@@ -1739,7 +1751,10 @@ int proc_pid_readdir(struct file * filp,
 		nr++;
 	}
 
-	nr_tgids = get_tgid_list(nr, tgid_array);
+	/*
+	 * f_version caches the last tgid which was returned from readdir
+	 */
+	nr_tgids = get_tgid_list(nr, filp->f_version, tgid_array);
 
 	for (i = 0; i < nr_tgids; i++) {
 		int tgid = tgid_array[i];
@@ -1748,8 +1763,10 @@ int proc_pid_readdir(struct file * filp,
 
 		do buf[--j] = '0' + (tgid % 10); while (tgid/=10);
 
-		if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, ino, DT_DIR) < 0)
+		if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, ino, DT_DIR) < 0) {
+			filp->f_version = tgid;
 			break;
+		}
 		filp->f_pos++;
 	}
 	return 0;

_