diff options
| author | Linus Torvalds <torvalds@athlon.transmeta.com> | 2002-02-05 00:13:33 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@athlon.transmeta.com> | 2002-02-05 00:13:33 -0800 |
| commit | 5fb612aa91a08c183200312d943de6691f806ce6 (patch) | |
| tree | eeab4310853f2b2f3bed3a1931932d46b2bf39f9 /fs/devfs | |
| parent | 18a933102825ea1e1b7e059234537d4d927e9216 (diff) | |
v2.5.1.11 -> v2.5.2
- Matt Domsch: combine common crc32 library
- Pete Zaitcev: ymfpci update
- Davide Libenzi: scheduler improvements
- Al Viro: almost there: "struct block_device *" everywhere
- Richard Gooch: devfs cpqarray update, race fix
- Rusty Russell: PATH_MAX should include the final '0' count
- David Miller: various random updates (mainly net and sparc)
Diffstat (limited to 'fs/devfs')
| -rw-r--r-- | fs/devfs/base.c | 150 |
1 files changed, 89 insertions, 61 deletions
diff --git a/fs/devfs/base.c b/fs/devfs/base.c index 639ae7464c6c..19c4066007e3 100644 --- a/fs/devfs/base.c +++ b/fs/devfs/base.c @@ -601,6 +601,9 @@ Only return old entry in <devfs_mk_dir> if a directory. Defined macros for error and debug messages. v1.8 + 20020113 Richard Gooch <rgooch@atnf.csiro.au> + Fixed (rare, old) race in <devfs_lookup>. + v1.9 */ #include <linux/types.h> #include <linux/errno.h> @@ -633,7 +636,7 @@ #include <asm/bitops.h> #include <asm/atomic.h> -#define DEVFS_VERSION "1.8 (20011226)" +#define DEVFS_VERSION "1.9 (20020113)" #define DEVFS_NAME "devfs" @@ -894,8 +897,8 @@ void devfs_put (devfs_handle_t de) { devfs_dealloc_devnum ( S_ISCHR (de->mode) ? DEVFS_SPECIAL_CHR : DEVFS_SPECIAL_BLK, - mk_kdev(de->u.fcb.u.device.major, - de->u.fcb.u.device.minor) ); + mk_kdev (de->u.fcb.u.device.major, + de->u.fcb.u.device.minor) ); } WRITE_ENTRY_MAGIC (de, 0); #ifdef CONFIG_DEVFS_DEBUG @@ -1552,7 +1555,7 @@ devfs_handle_t devfs_register (devfs_handle_t dir, const char *name, if ( ( S_ISCHR (mode) || S_ISBLK (mode) ) && (flags & DEVFS_FL_AUTO_DEVNUM) ) { - if ( kdev_none( devnum = devfs_alloc_devnum (devtype) ) ) + if ( kdev_none ( devnum = devfs_alloc_devnum (devtype) ) ) { PRINTK ("(%s): exhausted %s device numbers\n", name, S_ISCHR (mode) ? "char" : "block"); @@ -1564,14 +1567,14 @@ devfs_handle_t devfs_register (devfs_handle_t dir, const char *name, if ( ( de = _devfs_prepare_leaf (&dir, name, mode) ) == NULL ) { PRINTK ("(%s): could not prepare leaf\n", name); - if (!kdev_none(devnum)) devfs_dealloc_devnum (devtype, devnum); + if ( !kdev_none (devnum) ) devfs_dealloc_devnum (devtype, devnum); return NULL; } if ( S_ISCHR (mode) || S_ISBLK (mode) ) { de->u.fcb.u.device.major = major; de->u.fcb.u.device.minor = minor; - de->u.fcb.autogen = kdev_none(devnum) ? FALSE : TRUE; + de->u.fcb.autogen = kdev_none (devnum) ? FALSE : TRUE; } else if ( !S_ISREG (mode) ) { @@ -1601,7 +1604,7 @@ devfs_handle_t devfs_register (devfs_handle_t dir, const char *name, { PRINTK ("(%s): could not append to parent, err: %d\n", name, err); devfs_put (dir); - if (!kdev_none(devnum)) devfs_dealloc_devnum (devtype, devnum); + if ( !kdev_none (devnum) ) devfs_dealloc_devnum (devtype, devnum); return NULL; } DPRINTK (DEBUG_REGISTER, "(%s): de: %p dir: %p \"%s\" pp: %p\n", @@ -2378,7 +2381,7 @@ EXPORT_SYMBOL(devfs_unregister_blkdev); * @buf: A working area that will be used. This must not go out of scope * until devfsd is idle again. * - * Returns 0 on success, else a negative error code. + * Returns 0 on success (event was queued), else a negative error code. */ static int try_modload (struct devfs_entry *parent, struct fs_info *fs_info, @@ -2397,7 +2400,7 @@ static int try_modload (struct devfs_entry *parent, struct fs_info *fs_info, if ( !devfsd_notify_de (buf, DEVFSD_NOTIFY_LOOKUP, 0, current->euid, current->egid, fs_info, 0) ) return -ENOENT; - /* Possible success */ + /* Possible success: event has been queued */ return 0; } /* End Function try_modload */ @@ -2413,7 +2416,7 @@ static int check_disc_changed (struct devfs_entry *de) { int tmp; int retval = 0; - kdev_t dev = mk_kdev(de->u.fcb.u.device.major, de->u.fcb.u.device.minor); + kdev_t dev = mk_kdev (de->u.fcb.u.device.major, de->u.fcb.u.device.minor); struct block_device_operations *bdops; extern int warn_no_part; @@ -2599,15 +2602,15 @@ static struct inode *_devfs_get_vfs_inode (struct super_block *sb, inode->i_rdev = NODEV; if ( S_ISCHR (de->mode) ) { - inode->i_rdev = mk_kdev(de->u.fcb.u.device.major, - de->u.fcb.u.device.minor); + inode->i_rdev = mk_kdev (de->u.fcb.u.device.major, + de->u.fcb.u.device.minor); inode->i_cdev = cdget ( kdev_t_to_nr (inode->i_rdev) ); is_fcb = TRUE; } else if ( S_ISBLK (de->mode) ) { - inode->i_rdev = mk_kdev(de->u.fcb.u.device.major, - de->u.fcb.u.device.minor); + inode->i_rdev = mk_kdev (de->u.fcb.u.device.major, + de->u.fcb.u.device.minor); if (bd_acquire (inode) == 0) { if (!inode->i_bdev->bd_op && de->u.fcb.ops) @@ -2861,34 +2864,55 @@ static int devfs_d_delete (struct dentry *dentry) return 0; } /* End Function devfs_d_delete */ +struct devfs_lookup_struct +{ + devfs_handle_t de; + wait_queue_head_t wait_queue; +}; + static int devfs_d_revalidate_wait (struct dentry *dentry, int flags) { struct inode *dir = dentry->d_parent->d_inode; struct fs_info *fs_info = dir->i_sb->u.generic_sbp; + devfs_handle_t parent = get_devfs_entry_from_vfs_inode (dir); + struct devfs_lookup_struct *lookup_info = dentry->d_fsdata; + DECLARE_WAITQUEUE (wait, current); if ( !dentry->d_inode && is_devfsd_or_child (fs_info) ) { - devfs_handle_t de; - devfs_handle_t parent = get_devfs_entry_from_vfs_inode (dir); + devfs_handle_t de = lookup_info->de; struct inode *inode; - DPRINTK (DEBUG_I_LOOKUP, "(%s): dentry: %p by: \"%s\"\n", - dentry->d_name.name, dentry, current->comm); - 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); - if (de == NULL) return 1; + DPRINTK (DEBUG_I_LOOKUP, "(%s): dentry: %p de: %p by: \"%s\"\n", + dentry->d_name.name, dentry, de, current->comm); + if (de == NULL) + { + 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); + if (de == NULL) return 1; + lookup_info->de = de; + } /* Create an inode, now that the driver information is available */ inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry); - devfs_put (de); if (!inode) return 1; - DPRINTK (DEBUG_I_LOOKUP, "(%s): new VFS inode(%u): %p de: %p\n", - de->name, de->inode.ino, inode, de); + DPRINTK (DEBUG_I_LOOKUP, + "(%s): new VFS inode(%u): %p de: %p by: \"%s\"\n", + de->name, de->inode.ino, inode, de, current->comm); d_instantiate (dentry, inode); return 1; } - if ( wait_for_devfsd_finished (fs_info) ) dentry->d_op = &devfs_dops; + if (lookup_info == NULL) return 1; /* Early termination */ + read_lock (&parent->u.dir.lock); + if (dentry->d_fsdata) + { + add_wait_queue (&lookup_info->wait_queue, &wait); + current->state = TASK_UNINTERRUPTIBLE; + read_unlock (&parent->u.dir.lock); + schedule (); + } + else read_unlock (&parent->u.dir.lock); return 1; } /* End Function devfs_d_revalidate_wait */ @@ -2897,9 +2921,12 @@ static int devfs_d_revalidate_wait (struct dentry *dentry, int flags) static struct dentry *devfs_lookup (struct inode *dir, struct dentry *dentry) { + struct devfs_entry tmp; /* Must stay in scope until devfsd idle again */ + struct devfs_lookup_struct lookup_info; struct fs_info *fs_info = dir->i_sb->u.generic_sbp; struct devfs_entry *parent, *de; struct inode *inode; + struct dentry *retval = NULL; /* Set up the dentry operations before anything else, to ensure cleaning up on any error */ @@ -2921,60 +2948,61 @@ static struct dentry *devfs_lookup (struct inode *dir, struct dentry *dentry) dentry->d_name.len); read_unlock (&parent->u.dir.lock); } + 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 does a mknod()). It's important to do this before hashing the dentry, so that the devfsd queue is filled before revalidates can start */ - struct devfs_entry tmp; - if (try_modload (parent, fs_info, dentry->d_name.name, dentry->d_name.len, &tmp) < 0) - { + { /* Lookup event was not queued to devfsd */ d_add (dentry, NULL); return NULL; } - /* devfsd claimed success */ - dentry->d_op = &devfs_wait_dops; - d_add (dentry, NULL); /* Open the floodgates */ - /* Unlock directory semaphore, which will release any waiters. They - will get the hashed dentry, and may be forced to wait for - revalidation */ - up (&dir->i_sem); - devfs_d_revalidate_wait (dentry, 0); /* I might have to wait too */ - down (&dir->i_sem); /* Grab it again because them's the rules */ - /* If someone else has been so kind as to make the inode, we go home - early */ - if (dentry->d_inode) return NULL; + } + dentry->d_op = &devfs_wait_dops; + d_add (dentry, NULL); /* Open the floodgates */ + /* Unlock directory semaphore, which will release any waiters. They + will get the hashed dentry, and may be forced to wait for + revalidation */ + up (&dir->i_sem); + wait_for_devfsd_finished (fs_info); /* If I'm not devfsd, must wait */ + down (&dir->i_sem); /* Grab it again because them's the rules */ + 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; + if (de == NULL) + { 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); - if (de == NULL) return NULL; + if (de == NULL) goto out; /* OK, there's an entry now, but no VFS inode yet */ } - else - { - dentry->d_op = &devfs_wait_dops; - d_add (dentry, NULL); /* Open the floodgates */ - } /* Create an inode, now that the driver information is available */ inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry); - devfs_put (de); - if (!inode) return ERR_PTR (-ENOMEM); - DPRINTK (DEBUG_I_LOOKUP, "(%s): new VFS inode(%u): %p de: %p\n", - de->name, de->inode.ino, inode, de); - d_instantiate (dentry, inode); - if (dentry->d_op == &devfs_wait_dops) - { /* Unlock directory semaphore, which will release any waiters. They - will get the hashed dentry, and may be forced to wait for - revalidation */ - up (&dir->i_sem); - devfs_d_revalidate_wait (dentry, 0); /* I might have to wait too */ - down (&dir->i_sem); /* Grab it again because them's the rules */ + if (!inode) + { + retval = ERR_PTR (-ENOMEM); + goto out; } - return NULL; + DPRINTK (DEBUG_I_LOOKUP, "(%s): new VFS inode(%u): %p de: %p by: \"%s\"\n", + de->name, de->inode.ino, inode, de, current->comm); + d_instantiate (dentry, inode); +out: + dentry->d_op = &devfs_dops; + dentry->d_fsdata = NULL; + write_lock (&parent->u.dir.lock); + wake_up (&lookup_info.wait_queue); + write_unlock (&parent->u.dir.lock); + devfs_put (de); + return retval; } /* End Function devfs_lookup */ static int devfs_unlink (struct inode *dir, struct dentry *dentry) |
