Commit 9c02379c authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Optimize proc_pid_lookup

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.
parent b8ad9f1f
......@@ -1669,14 +1669,26 @@ static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry
* 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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment