]> git.hungrycats.org Git - linux/commitdiff
[PATCH] Optimize proc_pid_lookup
authorAndrew Morton <akpm@osdl.org>
Mon, 19 Jan 2004 13:10:03 +0000 (05:10 -0800)
committerLinus Torvalds <torvalds@home.osdl.org>
Mon, 19 Jan 2004 13:10:03 +0000 (05:10 -0800)
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

index f16a555f7933980f8d8ab4a21bf7e0f0e73f73cc..363af5b73d24fd4ede37a02c5a280d8953237f57 100644 (file)
@@ -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, void * dirent, filldir_t filldir)
                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, void * dirent, filldir_t filldir)
 
                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;