From: Andrey Borzenkov A while back Andrey fixed a devfs bug in which we were running remove_wait_queue() against a wait_queue_head which was on another process's stack, and which had gone out of scope. The patch reverts that fix and does it the same way as 2.4: just leave the waitqueue struct dangling on the waitqueue_head: there is no need to touch it at all. It adds a big comment explaining why we are doing this nasty thing. fs/devfs/base.c | 65 ++++++++++++-------------------------------------------- 1 files changed, 15 insertions(+), 50 deletions(-) diff -puN fs/devfs/base.c~devfs_lookup-revert-and-refix fs/devfs/base.c --- 25/fs/devfs/base.c~devfs_lookup-revert-and-refix 2003-07-26 10:25:55.000000000 -0700 +++ 25-akpm/fs/devfs/base.c 2003-07-26 10:25:55.000000000 -0700 @@ -2221,46 +2221,8 @@ struct devfs_lookup_struct { devfs_handle_t de; wait_queue_head_t wait_queue; - atomic_t count; }; -static struct devfs_lookup_struct * -new_devfs_lookup_struct(void) -{ - struct devfs_lookup_struct *p = kmalloc(sizeof(*p), GFP_KERNEL); - - if (!p) - return NULL; - - init_waitqueue_head (&p->wait_queue); - atomic_set(&p->count, 1); - return p; -} - -static void -get_devfs_lookup_struct(struct devfs_lookup_struct *info) -{ - if (info) - atomic_inc(&info->count); - else { - printk(KERN_ERR "null devfs_lookup_struct pointer\n"); - dump_stack(); - } -} - -static void -put_devfs_lookup_struct(struct devfs_lookup_struct *info) -{ - if (info) { - if (!atomic_dec_and_test(&info->count)) - return; - kfree(info); - } else { - printk(KERN_ERR "null devfs_lookup_struct pointer\n"); - dump_stack(); - } -} - /* XXX: this doesn't handle the case where we got a negative dentry but a devfs entry has been registered in the meanwhile */ static int devfs_d_revalidate_wait (struct dentry *dentry, struct nameidata *nd) @@ -2303,13 +2265,19 @@ static int devfs_d_revalidate_wait (stru read_lock (&parent->u.dir.lock); if (dentry->d_fsdata) { - get_devfs_lookup_struct(lookup_info); set_current_state (TASK_UNINTERRUPTIBLE); add_wait_queue (&lookup_info->wait_queue, &wait); read_unlock (&parent->u.dir.lock); schedule (); - remove_wait_queue (&lookup_info->wait_queue, &wait); - put_devfs_lookup_struct(lookup_info); + /* + * This does not need nor should remove wait from wait_queue. + * Wait queue head is never reused - nothing is ever added to it + * after all waiters have been waked up and head itself disappears + * very soon after it. Moreover it is local variable on stack that + * is likely to have already disappeared so any reference to it + * at this point is buggy. + */ + } else read_unlock (&parent->u.dir.lock); return 1; @@ -2321,7 +2289,7 @@ static int devfs_d_revalidate_wait (stru static struct dentry *devfs_lookup (struct inode *dir, struct dentry *dentry, struct nameidata *nd) { struct devfs_entry tmp; /* Must stay in scope until devfsd idle again */ - struct devfs_lookup_struct *lookup_info; + struct devfs_lookup_struct lookup_info; struct fs_info *fs_info = dir->i_sb->s_fs_info; struct devfs_entry *parent, *de; struct inode *inode; @@ -2338,10 +2306,9 @@ static struct dentry *devfs_lookup (stru read_lock (&parent->u.dir.lock); de = _devfs_search_dir (parent, dentry->d_name.name, dentry->d_name.len); read_unlock (&parent->u.dir.lock); - lookup_info = new_devfs_lookup_struct(); - if (!lookup_info) return ERR_PTR(-ENOMEM); - lookup_info->de = de; - dentry->d_fsdata = lookup_info; + lookup_info.de = de; + init_waitqueue_head (&lookup_info.wait_queue); + dentry->d_fsdata = &lookup_info; if (de == NULL) { /* Try with devfsd. For any kind of failure, leave a negative dentry so someone else can deal with it (in the case where the sysadmin @@ -2351,7 +2318,6 @@ static struct dentry *devfs_lookup (stru if (try_modload (parent, fs_info, dentry->d_name.name, dentry->d_name.len, &tmp) < 0) { /* Lookup event was not queued to devfsd */ - put_devfs_lookup_struct(lookup_info); d_add (dentry, NULL); return NULL; } @@ -2363,7 +2329,7 @@ static struct dentry *devfs_lookup (stru revalidation */ up (&dir->i_sem); wait_for_devfsd_finished (fs_info); /* If I'm not devfsd, must wait */ - de = lookup_info->de; + de = lookup_info.de; /* If someone else has been so kind as to make the inode, we go home early */ if (dentry->d_inode) goto out; @@ -2390,8 +2356,7 @@ out: write_lock (&parent->u.dir.lock); dentry->d_op = &devfs_dops; dentry->d_fsdata = NULL; - wake_up (&lookup_info->wait_queue); - put_devfs_lookup_struct(lookup_info); + wake_up (&lookup_info.wait_queue); write_unlock (&parent->u.dir.lock); down (&dir->i_sem); /* Grab it again because them's the rules */ devfs_put (de); _