]> git.hungrycats.org Git - linux/commitdiff
[PATCH] devfs: race fixes and cleanup
authorAndrew Morton <akpm@osdl.org>
Wed, 18 Feb 2004 12:49:43 +0000 (04:49 -0800)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Wed, 18 Feb 2004 12:49:43 +0000 (04:49 -0800)
From: Andrey Borzenkov <arvidjaar@mail.ru>

- use struct nameidata in devfs_d_revalidate_wait to detect when it is
  called without i_sem hold; take i_sem on parent in this case.  This
  prevents both deadlock with devfs_lookup by allowing it to drop i_sem
  consistently and oops in d_instantiate by ensuring that it always runs
  protected

- remove dead code that deals with major number allocation.  The only
  remaining user was devfs itself and patch changes it to

- use register_chardev to get device number for internal /dev/.devfsd and
  /dev/.statd.

- remove dead auto allocation flag as well

- remove code that does module get on dev open - it is handled by fops_get.
   Use init_special_inode consistently

- get rid of struct cdev_type and bdev_type - both have just single dev_t
  now

fs/devfs/base.c
fs/devfs/internal.h [deleted file]
fs/devfs/util.c

index d31e67d98a148c84c6009a68da1465fbba57c1c5..952031e7dd99fb5fd306ec1c967cf1be64fb79e7 100644 (file)
 #include <linux/smp.h>
 #include <linux/rwsem.h>
 #include <linux/sched.h>
+#include <linux/namei.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/bitops.h>
 #include <asm/atomic.h>
 
-#include "internal.h"
-
-#define DEVFS_VERSION            "1.22 (20021013)"
+#define DEVFS_VERSION            "2004-01-31"
 
 #define DEVFS_NAME "devfs"
 
@@ -762,18 +761,6 @@ struct directory_type
     unsigned char no_more_additions:1;
 };
 
-struct bdev_type
-{
-    dev_t dev;
-};
-
-struct cdev_type
-{
-    struct file_operations *ops;
-    dev_t dev;
-    unsigned char autogen:1;
-};
-
 struct symlink_type
 {
     unsigned int length;         /*  Not including the NULL-termimator       */
@@ -801,8 +788,7 @@ struct devfs_entry
     union 
     {
        struct directory_type dir;
-       struct bdev_type bdev;
-       struct cdev_type cdev;
+       dev_t dev;
        struct symlink_type symlink;
        const char *name;        /*  Only used for (mode == 0)               */
     }
@@ -813,7 +799,7 @@ struct devfs_entry
     struct devfs_inode inode;
     umode_t mode;
     unsigned short namelen;      /*  I think 64k+ filenames are a way off... */
-    unsigned char vfs_deletable:1;/*  Whether the VFS may delete the entry   */
+    unsigned char vfs:1;/*  Whether the VFS may delete the entry   */
     char name[1];                /*  This is just a dummy: the allocated array
                                     is bigger. This is NULL-terminated      */
 };
@@ -925,8 +911,6 @@ static void devfs_put (devfs_handle_t de)
             de->name, de, de->parent,
             de->parent ? de->parent->name : "no parent");
     if ( S_ISLNK (de->mode) ) kfree (de->u.symlink.linkname);
-    if ( S_ISCHR (de->mode) && de->u.cdev.autogen )
-       devfs_dealloc_devnum (de->mode, de->u.cdev.dev);
     WRITE_ENTRY_MAGIC (de, 0);
 #ifdef CONFIG_DEVFS_DEBUG
     spin_lock (&stat_lock);
@@ -1063,46 +1047,40 @@ static int _devfs_append_entry (devfs_handle_t dir, devfs_handle_t de,
     return retval;
 }   /*  End Function _devfs_append_entry  */
 
-
 /**
  *     _devfs_get_root_entry - Get the root devfs entry.
  *
  *     Returns the root devfs entry on success, else %NULL.
+ *
+ *     TODO it must be called asynchronously due to the fact
+ *     that devfs is initialized relatively late. Proper way
+ *     is to remove module_init from init_devfs_fs and manually
+ *     call it early enough during system init
  */
 
-static struct devfs_entry *_devfs_get_root_entry (void)
+static struct devfs_entry *_devfs_get_root_entry(void)
 {
-    struct devfs_entry *new;
-    static spinlock_t root_lock = SPIN_LOCK_UNLOCKED;
+       struct devfs_entry *new;
+       static spinlock_t root_lock = SPIN_LOCK_UNLOCKED;
 
-    /*  Always ensure the root is created  */
-    if (root_entry) return root_entry;
-    if ( ( new = _devfs_alloc_entry (NULL, 0,MODE_DIR) ) == NULL ) return NULL;
-    spin_lock (&root_lock);
-    if (root_entry)
-    {
-       spin_unlock (&root_lock);
-       devfs_put (new);
-       return (root_entry);
-    }
-    root_entry = new;
-    spin_unlock (&root_lock);
-    /*  And create the entry for ".devfsd"  */
-    if ( ( new = _devfs_alloc_entry (".devfsd", 0, S_IFCHR |S_IRUSR |S_IWUSR) )
-        == NULL ) return NULL;
-    new->u.cdev.dev = devfs_alloc_devnum (S_IFCHR |S_IRUSR |S_IWUSR);
-    new->u.cdev.ops = &devfsd_fops;
-    _devfs_append_entry (root_entry, new, NULL);
-#ifdef CONFIG_DEVFS_DEBUG
-    if ( ( new = _devfs_alloc_entry (".stat", 0, S_IFCHR | S_IRUGO | S_IWUGO) )
-        == NULL ) return NULL;
-    new->u.cdev.dev = devfs_alloc_devnum (S_IFCHR | S_IRUGO | S_IWUGO);
-    new->u.cdev.ops = &stat_fops;
-    _devfs_append_entry (root_entry, new, NULL);
-#endif
-    return root_entry;
-}   /*  End Function _devfs_get_root_entry  */
+       if (root_entry)
+               return root_entry;
+
+       new = _devfs_alloc_entry(NULL, 0, MODE_DIR);
+       if (new == NULL )
+               return NULL;
 
+       spin_lock(&root_lock);
+       if (root_entry) {
+               spin_unlock(&root_lock);
+               devfs_put(new);
+               return root_entry;
+       }
+       root_entry = new;
+       spin_unlock(&root_lock);
+
+       return root_entry;
+}   /*  End Function _devfs_get_root_entry  */
 
 /**
  *     _devfs_descend - Descend down a tree using the next component name.
@@ -1237,6 +1215,7 @@ static devfs_handle_t _devfs_walk_path (struct devfs_entry *dir,
        }
        if (S_ISLNK (de->mode) && traverse_symlink)
        {   /*  Need to follow the link: this is a stack chomper  */
+               /* FIXME what if it puts outside of mounted tree? */
            link = _devfs_walk_path (dir, de->u.symlink.linkname,
                                     de->u.symlink.length, TRUE);
            devfs_put (de);
@@ -1444,27 +1423,19 @@ static void devfsd_notify (struct devfs_entry *de,unsigned short type)
                         current->egid, &fs_info);
 } 
 
-int devfs_mk_bdev(dev_t dev, umode_t mode, const char *fmt, ...)
+static int devfs_mk_dev(dev_t dev, umode_t mode, const char *fmt, va_list args)
 {
        struct devfs_entry *dir = NULL, *de;
        char buf[64];
-       va_list args;
        int error, n;
 
-       va_start(args, fmt);
-       n = vsnprintf(buf, 64, fmt, args);
-       if (n >= 64 || !buf[0]) {
-               printk(KERN_WARNING "%s: invalid format string\n",
-                               __FUNCTION__);
+       n = vsnprintf(buf, sizeof(buf), fmt, args);
+       if (n >= sizeof(buf) || !buf[0]) {
+               printk(KERN_WARNING "%s: invalid format string %s\n",
+                               __FUNCTION__, fmt);
                return -EINVAL;
        }
        
-       if (!S_ISBLK(mode)) {
-               printk(KERN_WARNING "%s: invalide mode (%u) for %s\n",
-                               __FUNCTION__, mode, buf);
-               return -EINVAL;
-       }
-
        de = _devfs_prepare_leaf(&dir, buf, mode);
        if (!de) {
                printk(KERN_WARNING "%s: could not prepare leaf for %s\n",
@@ -1472,7 +1443,7 @@ int devfs_mk_bdev(dev_t dev, umode_t mode, const char *fmt, ...)
                return -ENOMEM;         /* could be more accurate... */
        }
 
-       de->u.bdev.dev = dev;
+       de->u.dev = dev;
 
        error = _devfs_append_entry(dir, de, NULL);
        if (error) {
@@ -1487,50 +1458,35 @@ int devfs_mk_bdev(dev_t dev, umode_t mode, const char *fmt, ...)
        return error;
 }
 
+int devfs_mk_bdev(dev_t dev, umode_t mode, const char *fmt, ...)
+{
+       va_list args;
+
+       if (!S_ISBLK(mode)) {
+               printk(KERN_WARNING "%s: invalide mode (%u) for %s\n",
+                               __FUNCTION__, mode, fmt);
+               return -EINVAL;
+       }
+
+       va_start(args, fmt);
+       return devfs_mk_dev(dev, mode, fmt, args);
+}
+
 EXPORT_SYMBOL(devfs_mk_bdev);
 
 
 int devfs_mk_cdev(dev_t dev, umode_t mode, const char *fmt, ...)
 {
-       struct devfs_entry *dir = NULL, *de;
-       char buf[64];
        va_list args;
-       int error, n;
-
-       va_start(args, fmt);
-       n = vsnprintf(buf, 64, fmt, args);
-       if (n >= 64 || !buf[0]) {
-               printk(KERN_WARNING "%s: invalid format string\n",
-                               __FUNCTION__);
-               return -EINVAL;
-       }
 
        if (!S_ISCHR(mode)) {
                printk(KERN_WARNING "%s: invalide mode (%u) for %s\n",
-                               __FUNCTION__, mode, buf);
+                               __FUNCTION__, mode, fmt);
                return -EINVAL;
        }
 
-       de = _devfs_prepare_leaf(&dir, buf, mode);
-       if (!de) {
-               printk(KERN_WARNING "%s: could not prepare leaf for %s\n",
-                               __FUNCTION__, buf);
-               return -ENOMEM;         /* could be more accurate... */
-       }
-
-       de->u.cdev.dev = dev;
-
-       error = _devfs_append_entry(dir, de, NULL);
-       if (error) {
-               printk(KERN_WARNING "%s: could not append to parent for %s\n",
-                               __FUNCTION__, buf);
-               goto out;
-       }
-
-       devfsd_notify(de, DEVFSD_NOTIFY_REGISTERED);
- out:
-       devfs_put(dir);
-       return error;
+       va_start(args, fmt);
+       return devfs_mk_dev(dev, mode, fmt, args);
 }
 
 EXPORT_SYMBOL(devfs_mk_cdev);
@@ -1663,7 +1619,7 @@ int devfs_mk_symlink(const char *from, const char *to)
 
        err = devfs_do_symlink(NULL, from, to, &de);
        if (!err) {
-               de->vfs_deletable = TRUE;
+               de->vfs = TRUE;
                devfsd_notify(de, DEVFSD_NOTIFY_REGISTERED);
        }
 
@@ -1732,8 +1688,8 @@ void devfs_remove(const char *fmt, ...)
        int n;
 
        va_start(args, fmt);
-       n = vsnprintf(buf, 64, fmt, args);
-       if (n < 64 && buf[0]) {
+       n = vsnprintf(buf, sizeof(buf), fmt, args);
+       if (n < sizeof(buf) && buf[0]) {
                devfs_handle_t de = _devfs_find_entry(NULL, buf, 0);
 
                if (!de) {
@@ -1784,33 +1740,6 @@ static int devfs_generate_path (devfs_handle_t de, char *path, int buflen)
     return pos;
 }   /*  End Function devfs_generate_path  */
 
-
-/**
- *     devfs_get_ops - Get the device operations for a devfs entry.
- *     @de: The handle to the device entry.
- *
- *     Returns a pointer to the device operations on success, else NULL.
- *     The use count for the module owning the operations will be incremented.
- */
-
-static struct file_operations *devfs_get_ops (devfs_handle_t de)
-{
-    struct file_operations *ops = de->u.cdev.ops;
-    struct module *owner;
-
-    if (!ops)
-       return NULL;
-    owner = ops->owner;
-    read_lock (&de->parent->u.dir.lock);  /*  Prevent module from unloading  */
-    if ( (de->next == de) || !try_module_get (owner) )
-    {   /*  Entry is already unhooked or module is unloading  */
-       read_unlock (&de->parent->u.dir.lock);
-       return NULL;
-    }
-    read_unlock (&de->parent->u.dir.lock);  /*  Module can continue unloading*/
-    return ops;
-}   /*  End Function devfs_get_ops  */
-
 /**
  *     devfs_setup - Process kernel boot options.
  *     @str: The boot options after the "devfs=".
@@ -1876,7 +1805,6 @@ static int __init devfs_setup (char *str)
 
 __setup("devfs=", devfs_setup);
 
-EXPORT_SYMBOL(devfs_put);
 EXPORT_SYMBOL(devfs_mk_symlink);
 EXPORT_SYMBOL(devfs_mk_dir);
 EXPORT_SYMBOL(devfs_remove);
@@ -1996,6 +1924,7 @@ static struct inode *_devfs_get_vfs_inode (struct super_block *sb,
        iput (inode);
        return NULL;
     }
+    /* FIXME where is devfs_put? */
     inode->u.generic_ip = devfs_get (de);
     inode->i_ino = de->inode.ino;
     DPRINTK (DEBUG_I_GET, "(%d): VFS inode: %p  devfs_entry: %p\n",
@@ -2003,26 +1932,25 @@ static struct inode *_devfs_get_vfs_inode (struct super_block *sb,
     inode->i_blocks = 0;
     inode->i_blksize = FAKE_BLOCK_SIZE;
     inode->i_op = &devfs_iops;
-    inode->i_fop = &devfs_fops;
-    if ( S_ISCHR (de->mode) )
-    {
-       inode->i_rdev = de->u.cdev.dev;
-    }
-    else if ( S_ISBLK (de->mode) )
-       init_special_inode(inode, de->mode, de->u.bdev.dev);
-    else if ( S_ISFIFO (de->mode) )
-       inode->i_fop = &def_fifo_fops;
-    else if ( S_ISDIR (de->mode) )
-    {
-       inode->i_op = &devfs_dir_iops;
-       inode->i_fop = &devfs_dir_fops;
-    }
-    else if ( S_ISLNK (de->mode) )
-    {
-       inode->i_op = &devfs_symlink_iops;
-       inode->i_size = de->u.symlink.length;
-    }
     inode->i_mode = de->mode;
+       if (S_ISDIR(de->mode)) {
+               inode->i_op = &devfs_dir_iops;
+               inode->i_fop = &devfs_dir_fops;
+       } else if (S_ISLNK(de->mode)) {
+               inode->i_op = &devfs_symlink_iops;
+               inode->i_size = de->u.symlink.length;
+       } else if (S_ISCHR(de->mode) || S_ISBLK(de->mode)) {
+               init_special_inode(inode, de->mode, de->u.dev);
+       } else if (S_ISFIFO(de->mode) || S_ISSOCK(de->mode)) {
+               init_special_inode(inode, de->mode, 0);
+       } else {
+               PRINTK("(%s): unknown mode %o de: %p\n",
+                       de->name, de->mode, de);
+               iput(inode);
+               devfs_put(de);
+               return NULL;
+       }
+
     inode->i_uid = de->inode.uid;
     inode->i_gid = de->inode.gid;
     inode->i_atime = de->inode.atime;
@@ -2098,29 +2026,37 @@ static int devfs_readdir (struct file *file, void *dirent, filldir_t filldir)
     return stored;
 }   /*  End Function devfs_readdir  */
 
+/* Open devfs specific special files */
 static int devfs_open (struct inode *inode, struct file *file)
 {
-    int err = -ENODEV;
-    struct devfs_entry *de;
-    struct file_operations *ops;
+       int err;
+       int minor = MINOR(inode->i_rdev);
+       struct file_operations *old_fops, *new_fops;
 
-    de = get_devfs_entry_from_vfs_inode (inode);
-    if (de == NULL) return -ENODEV;
-    if ( S_ISDIR (de->mode) ) return 0;
-    file->private_data = de->info;
-    if (S_ISCHR(inode->i_mode)) {
-       ops = devfs_get_ops (de);  /*  Now have module refcount  */
-       file->f_op = ops;
-       if (file->f_op)
-       {
-           lock_kernel ();
-           err = file->f_op->open ? (*file->f_op->open) (inode, file) : 0;
-           unlock_kernel ();
+       switch (minor) {
+       case 0: /* /dev/.devfsd */
+               new_fops = fops_get(&devfsd_fops);
+               break;
+#ifdef CONFIG_DEVFS_DEBUG
+       case 1: /* /dev/.stat */
+               new_fops = fops_get(&stat_fops);
+               break;
+#endif
+       default:
+               return -ENODEV;
        }
-       else
-           err = chrdev_open (inode, file);
-    }
-    return err;
+
+       if (new_fops == NULL)
+               return -ENODEV;
+       old_fops = file->f_op;
+       file->f_op = new_fops;
+       err = new_fops->open ? new_fops->open(inode, file) : 0;
+       if (err) {
+               file->f_op = old_fops;
+               fops_put(new_fops);
+       } else
+               fops_put(old_fops);
+       return err;
 }   /*  End Function devfs_open  */
 
 static struct file_operations devfs_fops =
@@ -2132,7 +2068,6 @@ static struct file_operations devfs_dir_fops =
 {
     .read    = generic_read_dir,
     .readdir = devfs_readdir,
-    .open    = devfs_open,
 };
 
 
@@ -2223,6 +2158,34 @@ static int devfs_d_revalidate_wait (struct dentry *dentry, struct nameidata *nd)
     devfs_handle_t parent = get_devfs_entry_from_vfs_inode (dir);
     struct devfs_lookup_struct *lookup_info = dentry->d_fsdata;
     DECLARE_WAITQUEUE (wait, current);
+    int need_lock;
+
+    /*
+     * FIXME HACK
+     *
+     * make sure that
+     *   d_instantiate always runs under lock
+     *   we release i_sem lock before going to sleep
+     *
+     * unfortunately sometimes d_revalidate is called with
+     * and sometimes without i_sem lock held. The following checks
+     * attempt to deduce when we need to add (and drop resp.) lock
+     * here. This relies on current (2.6.2) calling coventions:
+     *
+     *   lookup_hash is always run under i_sem and is passing NULL
+     *   as nd
+     *
+     *   open(...,O_CREATE,...) calls _lookup_hash under i_sem
+     *   and sets flags to LOOKUP_OPEN|LOOKUP_CREATE
+     *
+     *   all other invocations of ->d_revalidate seem to happen
+     *   outside of i_sem
+     */
+    need_lock = nd &&
+               (!(nd->flags & LOOKUP_CREATE) || (nd->flags & LOOKUP_PARENT));
+
+    if (need_lock)
+       down(&dir->i_sem);
 
     if ( is_devfsd_or_child (fs_info) )
     {
@@ -2233,33 +2196,40 @@ static int devfs_d_revalidate_wait (struct dentry *dentry, struct nameidata *nd)
                 "(%s): dentry: %p inode: %p de: %p by: \"%s\"\n",
                 dentry->d_name.name, dentry, dentry->d_inode, de,
                 current->comm);
-       if (dentry->d_inode) return 1;
+       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 1;
+           if (de == NULL)
+               goto out;
            lookup_info->de = de;
        }
        /*  Create an inode, now that the driver information is available  */
        inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry);
-       if (!inode) return 1;
+       if (!inode)
+           goto out;
        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;
+       goto out;
     }
-    if (lookup_info == NULL) return 1;  /*  Early termination  */
+    if (lookup_info == NULL)
+       goto out;  /*  Early termination  */
     read_lock (&parent->u.dir.lock);
     if (dentry->d_fsdata)
     {
        set_current_state (TASK_UNINTERRUPTIBLE);
        add_wait_queue (&lookup_info->wait_queue, &wait);
        read_unlock (&parent->u.dir.lock);
+       /* at this point it is always (hopefully) locked */
+       up(&dir->i_sem);
        schedule ();
+       down(&dir->i_sem);
        /*
         * This does not need nor should remove wait from wait_queue.
         * Wait queue head is never reused - nothing is ever added to it
@@ -2271,6 +2241,10 @@ static int devfs_d_revalidate_wait (struct dentry *dentry, struct nameidata *nd)
 
     }
     else read_unlock (&parent->u.dir.lock);
+
+out:
+    if (need_lock)
+       up(&dir->i_sem);
     return 1;
 }   /*  End Function devfs_d_revalidate_wait  */
 
@@ -2320,6 +2294,7 @@ static struct dentry *devfs_lookup (struct inode *dir, struct dentry *dentry, st
        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  */
@@ -2349,7 +2324,6 @@ out:
     dentry->d_fsdata = NULL;
     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);
     return retval;
 }   /*  End Function devfs_lookup  */
@@ -2364,7 +2338,7 @@ static int devfs_unlink (struct inode *dir, struct dentry *dentry)
     de = get_devfs_entry_from_vfs_inode (inode);
     DPRINTK (DEBUG_I_UNLINK, "(%s): de: %p\n", dentry->d_name.name, de);
     if (de == NULL) return -ENOENT;
-    if (!de->vfs_deletable) return -EPERM;
+    if (!de->vfs) return -EPERM;
     write_lock (&de->parent->u.dir.lock);
     unhooked = _devfs_unhook (de);
     write_unlock (&de->parent->u.dir.lock);
@@ -2392,7 +2366,7 @@ static int devfs_symlink (struct inode *dir, struct dentry *dentry,
     DPRINTK (DEBUG_DISABLED, "(%s): errcode from <devfs_do_symlink>: %d\n",
             dentry->d_name.name, err);
     if (err < 0) return err;
-    de->vfs_deletable = TRUE;
+    de->vfs = TRUE;
     de->inode.uid = current->euid;
     de->inode.gid = current->egid;
     de->inode.atime = CURRENT_TIME;
@@ -2421,7 +2395,7 @@ static int devfs_mkdir (struct inode *dir, struct dentry *dentry, int mode)
     if (parent == NULL) return -ENOENT;
     de = _devfs_alloc_entry (dentry->d_name.name, dentry->d_name.len, mode);
     if (!de) return -ENOMEM;
-    de->vfs_deletable = TRUE;
+    de->vfs = TRUE;
     if ( ( err = _devfs_append_entry (parent, de, NULL) ) != 0 )
        return err;
     de->inode.uid = current->euid;
@@ -2451,7 +2425,7 @@ static int devfs_rmdir (struct inode *dir, struct dentry *dentry)
     de = get_devfs_entry_from_vfs_inode (inode);
     if (de == NULL) return -ENOENT;
     if ( !S_ISDIR (de->mode) ) return -ENOTDIR;
-    if (!de->vfs_deletable) return -EPERM;
+    if (!de->vfs) return -EPERM;
     /*  First ensure the directory is empty and will stay that way  */
     write_lock (&de->u.dir.lock);
     if (de->u.dir.first) err = -ENOTEMPTY;
@@ -2485,11 +2459,9 @@ static int devfs_mknod (struct inode *dir, struct dentry *dentry, int mode,
     if (parent == NULL) return -ENOENT;
     de = _devfs_alloc_entry (dentry->d_name.name, dentry->d_name.len, mode);
     if (!de) return -ENOMEM;
-    de->vfs_deletable = TRUE;
-    if (S_ISCHR (mode))
-       de->u.cdev.dev = rdev;
-    else if (S_ISBLK (mode))
-       de->u.bdev.dev = rdev;
+    de->vfs = TRUE;
+    if (S_ISCHR(mode) || S_ISBLK(mode))
+       de->u.dev = rdev;
     if ( ( err = _devfs_append_entry (parent, de, NULL) ) != 0 )
        return err;
     de->inode.uid = current->euid;
@@ -2642,12 +2614,9 @@ static ssize_t devfsd_read (struct file *file, char *buf, size_t len,
     info->uid = entry->uid;
     info->gid = entry->gid;
     de = entry->de;
-    if (S_ISCHR(de->mode)) {
-       info->major = MAJOR(de->u.cdev.dev);
-       info->minor = MINOR(de->u.cdev.dev);
-    } else if (S_ISBLK (de->mode)) {
-       info->major = MAJOR(de->u.bdev.dev);
-       info->minor = MINOR(de->u.bdev.dev);
+    if (S_ISCHR(de->mode) || S_ISBLK(de->mode)) {
+       info->major = MAJOR(de->u.dev);
+       info->minor = MINOR(de->u.dev);
     }
     pos = devfs_generate_path (de, info->devname, DEVFS_PATHLEN);
     if (pos < 0) return pos;
@@ -2809,30 +2778,53 @@ static ssize_t stat_read (struct file *file, char *buf, size_t len,
 }   /*  End Function stat_read  */
 #endif
 
-
-static int __init init_devfs_fs (void)
+static int __init init_devfs_fs(void)
 {
-    int err;
+       int err;
+       int major;
+       struct devfs_entry *devfsd;
+#ifdef CONFIG_DEVFS_DEBUG
+       struct devfs_entry *stat;
+#endif
+
+       if (_devfs_get_root_entry() == NULL)
+               return -ENOMEM;
 
-    printk (KERN_INFO "%s: v%s Richard Gooch (rgooch@atnf.csiro.au)\n",
-           DEVFS_NAME, DEVFS_VERSION);
-    devfsd_buf_cache = kmem_cache_create ("devfsd_event",
+       printk(KERN_INFO "%s: %s Richard Gooch (rgooch@atnf.csiro.au)\n",
+              DEVFS_NAME, DEVFS_VERSION);
+       devfsd_buf_cache = kmem_cache_create("devfsd_event",
                                          sizeof (struct devfsd_buf_entry),
                                          0, 0, NULL, NULL);
-    if (!devfsd_buf_cache) OOPS ("(): unable to allocate event slab\n");
+       if (!devfsd_buf_cache)
+               OOPS("(): unable to allocate event slab\n");
 #ifdef CONFIG_DEVFS_DEBUG
-    devfs_debug = devfs_debug_init;
-    printk (KERN_INFO "%s: devfs_debug: 0x%0x\n", DEVFS_NAME, devfs_debug);
+       devfs_debug = devfs_debug_init;
+       printk(KERN_INFO "%s: devfs_debug: 0x%0x\n", DEVFS_NAME, devfs_debug);
 #endif
-    printk (KERN_INFO "%s: boot_options: 0x%0x\n", DEVFS_NAME, boot_options);
-    err = register_filesystem (&devfs_fs_type);
-    if (!err)
-    {
-       struct vfsmount *devfs_mnt = kern_mount (&devfs_fs_type);
-       err = PTR_ERR (devfs_mnt);
-       if ( !IS_ERR (devfs_mnt) ) err = 0;
-    }
-    return err;
+       printk(KERN_INFO "%s: boot_options: 0x%0x\n", DEVFS_NAME, boot_options);
+
+       /* register special device for devfsd communication */
+       major = register_chrdev(0, "devfs", &devfs_fops);
+       if (major < 0)
+               return major;
+
+       /*  And create the entry for ".devfsd"  */
+       devfsd = _devfs_alloc_entry(".devfsd", 0, S_IFCHR|S_IRUSR|S_IWUSR);
+       if (devfsd == NULL )
+               return -ENOMEM;
+       devfsd->u.dev = MKDEV(major, 0);
+       _devfs_append_entry(root_entry, devfsd, NULL);
+
+#ifdef CONFIG_DEVFS_DEBUG
+       stat = _devfs_alloc_entry(".stat", 0, S_IFCHR|S_IRUGO);
+       if (stat == NULL )
+               return -ENOMEM;
+       stat->u.dev = MKDEV(major, 1);
+       _devfs_append_entry (root_entry, stat, NULL);
+#endif
+
+       err = register_filesystem(&devfs_fs_type);
+       return err;
 }   /*  End Function init_devfs_fs  */
 
 void __init mount_devfs_fs (void)
diff --git a/fs/devfs/internal.h b/fs/devfs/internal.h
deleted file mode 100644 (file)
index 23c302f..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-
-extern dev_t devfs_alloc_devnum(umode_t mode);
-extern void devfs_dealloc_devnum(umode_t mode, dev_t devnum);
index eb8f4bf9d4b58b171242089418905a99cc291387..a6ecc014b471f793afb479c2a55c88d20e669554 100644 (file)
@@ -72,7 +72,6 @@
 #include <linux/vmalloc.h>
 #include <linux/genhd.h>
 #include <asm/bitops.h>
-#include "internal.h"
 
 
 int devfs_register_tape(const char *name)
@@ -96,161 +95,3 @@ void devfs_unregister_tape(int num)
 }
 
 EXPORT_SYMBOL(devfs_unregister_tape);
-
-struct major_list
-{
-    spinlock_t lock;
-    unsigned long bits[256 / BITS_PER_LONG];
-};
-#if BITS_PER_LONG == 32
-#  define INITIALISER64(low,high) (low), (high)
-#else
-#  define INITIALISER64(low,high) ( (unsigned long) (high) << 32 | (low) )
-#endif
-
-/*  Block majors already assigned:
-    0-3, 7-9, 11-63, 65-99, 101-113, 120-127, 199, 201, 240-255
-    Total free: 122
-*/
-static struct major_list block_major_list =
-{SPIN_LOCK_UNLOCKED,
-    {INITIALISER64 (0xfffffb8f, 0xffffffff),  /*  Majors 0-31,    32-63    */
-     INITIALISER64 (0xfffffffe, 0xff03ffef),  /*  Majors 64-95,   96-127   */
-     INITIALISER64 (0x00000000, 0x00000000),  /*  Majors 128-159, 160-191  */
-     INITIALISER64 (0x00000280, 0xffff0000),  /*  Majors 192-223, 224-255  */
-    }
-};
-
-/*  Char majors already assigned:
-    0-7, 9-151, 154-158, 160-211, 216-221, 224-230, 240-255
-    Total free: 19
-*/
-static struct major_list char_major_list =
-{SPIN_LOCK_UNLOCKED,
-    {INITIALISER64 (0xfffffeff, 0xffffffff),  /*  Majors 0-31,    32-63    */
-     INITIALISER64 (0xffffffff, 0xffffffff),  /*  Majors 64-95,   96-127   */
-     INITIALISER64 (0x7cffffff, 0xffffffff),  /*  Majors 128-159, 160-191  */
-     INITIALISER64 (0x3f0fffff, 0xffff007f),  /*  Majors 192-223, 224-255  */
-    }
-};
-
-
-/**
- *     devfs_alloc_major - Allocate a major number.
- *     @mode: The file mode (must be block device or character device).
- *     Returns the allocated major, else -1 if none are available.
- *     This routine is thread safe and does not block.
- */
-
-
-struct minor_list
-{
-    int major;
-    unsigned long bits[256 / BITS_PER_LONG];
-    struct minor_list *next;
-};
-
-static struct device_list {
-       struct minor_list       *first;
-       struct minor_list       *last;
-       int                     none_free;
-} block_list, char_list;
-
-static DECLARE_MUTEX(device_list_mutex);
-
-
-/**
- *     devfs_alloc_devnum - Allocate a device number.
- *     @mode: The file mode (must be block device or character device).
- *
- *     Returns the allocated device number, else NODEV if none are available.
- *     This routine is thread safe and may block.
- */
-
-dev_t devfs_alloc_devnum(umode_t mode)
-{
-       struct device_list *list;
-       struct major_list *major_list;
-       struct minor_list *entry;
-       int minor;
-
-       if (S_ISCHR(mode)) {
-               major_list = &char_major_list;
-               list = &char_list;
-       } else {
-               major_list = &block_major_list;
-               list = &block_list;
-       }
-
-       down(&device_list_mutex);
-       if (list->none_free)
-               goto out_unlock;
-
-       for (entry = list->first; entry; entry = entry->next) {
-               minor = find_first_zero_bit (entry->bits, 256);
-               if (minor >= 256)
-                       continue;
-               goto out_done;
-       }
-       
-       /*  Need to allocate a new major  */
-       entry = kmalloc (sizeof *entry, GFP_KERNEL);
-       if (!entry)
-               goto out_full;
-       memset(entry, 0, sizeof *entry);
-
-       spin_lock(&major_list->lock);
-       entry->major = find_first_zero_bit(major_list->bits, 256);
-       if (entry->major >= 256) {
-               spin_unlock(&major_list->lock);
-               kfree(entry);
-               goto out_full;
-       }
-       __set_bit(entry->major, major_list->bits);
-       spin_unlock(&major_list->lock);
-
-       if (!list->first)
-               list->first = entry;
-       else
-               list->last->next = entry;
-       list->last = entry;
-
-       minor = 0;
- out_done:
-       __set_bit(minor, entry->bits);
-       up(&device_list_mutex);
-       return MKDEV(entry->major, minor);
- out_full:
-       list->none_free = 1;
- out_unlock:
-       up(&device_list_mutex);
-       return 0;
-}
-
-
-/**
- *     devfs_dealloc_devnum - Dellocate a device number.
- *     @mode: The file mode (must be block device or character device).
- *     @devnum: The device number.
- *
- *     This routine is thread safe and may block.
- */
-
-void devfs_dealloc_devnum(umode_t mode, dev_t devnum)
-{
-       struct device_list *list = S_ISCHR(mode) ? &char_list : &block_list;
-       struct minor_list *entry;
-
-       if (!devnum)
-               return;
-
-       down(&device_list_mutex);
-       for (entry = list->first; entry; entry = entry->next) {
-               if (entry->major == MAJOR(devnum)) {
-                       if (__test_and_clear_bit(MINOR(devnum), entry->bits))
-                               list->none_free = 0;
-                       break;
-               }
-       }
-       up(&device_list_mutex);
-}