summaryrefslogtreecommitdiff
path: root/fs/devfs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@athlon.transmeta.com>2002-02-05 00:13:33 -0800
committerLinus Torvalds <torvalds@athlon.transmeta.com>2002-02-05 00:13:33 -0800
commit5fb612aa91a08c183200312d943de6691f806ce6 (patch)
treeeeab4310853f2b2f3bed3a1931932d46b2bf39f9 /fs/devfs
parent18a933102825ea1e1b7e059234537d4d927e9216 (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.c150
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)