patch-2.4.17 linux/fs/devfs/base.c

Next file: linux/fs/devpts/root.c
Previous file: linux/fs/dcache.c
Back to the patch index
Back to the overall index

diff -Naur -X /home/marcelo/lib/dontdiff linux-2.4.16/fs/devfs/base.c linux/fs/devfs/base.c
@@ -427,7 +427,7 @@
 	       Work sponsored by SGI.
   v0.92
     20000306   Richard Gooch <rgooch@atnf.csiro.au>
-	       Added DEVFS_FL_NO_PERSISTENCE flag.
+	       Added DEVFS_ FL_NO_PERSISTENCE flag.
 	       Removed unnecessary call to <update_devfs_inode_from_entry> in
 	       <devfs_readdir>.
 	       Work sponsored by SGI.
@@ -545,21 +545,53 @@
     20010919   Richard Gooch <rgooch@atnf.csiro.au>
 	       Set inode->i_mapping->a_ops for block nodes in <get_vfs_inode>.
   v0.116
-    20010927   Richard Gooch <rgooch@atnf.csiro.au>
-	       Went back to global rwsem for symlinks (refcount scheme no good)
-  v0.117
     20011008   Richard Gooch <rgooch@atnf.csiro.au>
 	       Fixed overrun in <devfs_link> by removing function (not needed).
-  v0.118
     20011009   Richard Gooch <rgooch@atnf.csiro.au>
 	       Fixed buffer underrun in <try_modload>.
-	       Moved down_read() from <search_for_entry_in_dir> to <find_entry>
-  v0.119
     20011029   Richard Gooch <rgooch@atnf.csiro.au>
 	       Fixed race in <devfsd_ioctl> when setting event mask.
-    20011103   Richard Gooch <rgooch@atnf.csiro.au>
-	       Avoid deadlock in <devfs_follow_link> by using temporary buffer.
-  v0.120
+    20011114   Richard Gooch <rgooch@atnf.csiro.au>
+	       First release of new locking code.
+  v1.0
+    20011117   Richard Gooch <rgooch@atnf.csiro.au>
+	       Discard temporary buffer, now use "%s" for dentry names.
+    20011118   Richard Gooch <rgooch@atnf.csiro.au>
+	       Don't generate path in <try_modload>: use fake entry instead.
+	       Use "existing" directory in <_devfs_make_parent_for_leaf>.
+    20011122   Richard Gooch <rgooch@atnf.csiro.au>
+	       Use slab cache rather than fixed buffer for devfsd events.
+  v1.1
+    20011125   Richard Gooch <rgooch@atnf.csiro.au>
+	       Send DEVFSD_NOTIFY_REGISTERED events in <devfs_mk_dir>.
+    20011127   Richard Gooch <rgooch@atnf.csiro.au>
+	       Fixed locking bug in <devfs_d_revalidate_wait> due to typo.
+	       Do not send CREATE, CHANGE, ASYNC_OPEN or DELETE events from
+	       devfsd or children.
+  v1.2
+    20011202   Richard Gooch <rgooch@atnf.csiro.au>
+	       Fixed bug in <devfsd_read>: was dereferencing freed pointer.
+  v1.3
+    20011203   Richard Gooch <rgooch@atnf.csiro.au>
+	       Fixed bug in <devfsd_close>: was dereferencing freed pointer.
+	       Added process group check for devfsd privileges.
+  v1.4
+    20011204   Richard Gooch <rgooch@atnf.csiro.au>
+	       Use SLAB_ATOMIC in <devfsd_notify_de> from <devfs_d_delete>.
+  v1.5
+    20011211   Richard Gooch <rgooch@atnf.csiro.au>
+	       Return old entry in <devfs_mk_dir> for 2.4.x kernels.
+    20011212   Richard Gooch <rgooch@atnf.csiro.au>
+	       Increment refcount on module in <check_disc_changed>.
+    20011215   Richard Gooch <rgooch@atnf.csiro.au>
+	       Created <devfs_get_handle> and exported <devfs_put>.
+	       Increment refcount on module in <devfs_get_ops>.
+	       Created <devfs_put_ops>.
+  v1.6
+    20011216   Richard Gooch <rgooch@atnf.csiro.au>
+	       Added poisoning to <devfs_put>.
+	       Improved debugging messages.
+  v1.7
 */
 #include <linux/types.h>
 #include <linux/errno.h>
@@ -592,40 +624,45 @@
 #include <asm/bitops.h>
 #include <asm/atomic.h>
 
-#define DEVFS_VERSION            "0.120 (20011103)"
+#define DEVFS_VERSION            "1.7 (20011216)"
 
 #define DEVFS_NAME "devfs"
 
 #define FIRST_INODE 1
 
 #define STRING_LENGTH 256
+#define FAKE_BLOCK_SIZE 1024
+#define POISON_PTR ( *(void **) poison_array )
 
 #ifndef TRUE
 #  define TRUE 1
 #  define FALSE 0
 #endif
 
-#define IS_HIDDEN(de) (( ((de)->hide && !is_devfsd_or_child(fs_info)) || !(de)->registered))
+#define MODE_DIR (S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO)
 
-#define DEBUG_NONE         0x00000
-#define DEBUG_MODULE_LOAD  0x00001
-#define DEBUG_REGISTER     0x00002
-#define DEBUG_UNREGISTER   0x00004
-#define DEBUG_SET_FLAGS    0x00008
-#define DEBUG_S_PUT        0x00010
-#define DEBUG_I_LOOKUP     0x00020
-#define DEBUG_I_CREATE     0x00040
-#define DEBUG_I_GET        0x00080
-#define DEBUG_I_CHANGE     0x00100
-#define DEBUG_I_UNLINK     0x00200
-#define DEBUG_I_RLINK      0x00400
-#define DEBUG_I_FLINK      0x00800
-#define DEBUG_I_MKNOD      0x01000
-#define DEBUG_F_READDIR    0x02000
-#define DEBUG_D_DELETE     0x04000
-#define DEBUG_D_RELEASE    0x08000
-#define DEBUG_D_IPUT       0x10000
-#define DEBUG_ALL          0xfffff
+#define IS_HIDDEN(de) ( (de)->hide && !is_devfsd_or_child(fs_info) )
+
+#define DEBUG_NONE         0x0000000
+#define DEBUG_MODULE_LOAD  0x0000001
+#define DEBUG_REGISTER     0x0000002
+#define DEBUG_UNREGISTER   0x0000004
+#define DEBUG_FREE         0x0000008
+#define DEBUG_SET_FLAGS    0x0000010
+#define DEBUG_S_READ       0x0000100        /*  Break  */
+#define DEBUG_I_LOOKUP     0x0001000        /*  Break  */
+#define DEBUG_I_CREATE     0x0002000
+#define DEBUG_I_GET        0x0004000
+#define DEBUG_I_CHANGE     0x0008000
+#define DEBUG_I_UNLINK     0x0010000
+#define DEBUG_I_RLINK      0x0020000
+#define DEBUG_I_FLINK      0x0040000
+#define DEBUG_I_MKNOD      0x0080000
+#define DEBUG_F_READDIR    0x0100000        /*  Break  */
+#define DEBUG_D_DELETE     0x1000000        /*  Break  */
+#define DEBUG_D_RELEASE    0x2000000
+#define DEBUG_D_IPUT       0x4000000
+#define DEBUG_ALL          0xfffffff
 #define DEBUG_DISABLED     DEBUG_NONE
 
 #define OPTION_NONE             0x00
@@ -638,9 +675,11 @@
 
 struct directory_type
 {
+    rwlock_t lock;                   /*  Lock for searching(R)/updating(W)   */
     struct devfs_entry *first;
     struct devfs_entry *last;
-    unsigned int num_removable;
+    unsigned short num_removable;    /*  Lock for writing but not reading    */
+    unsigned char no_more_additions:1;
 };
 
 struct file_type
@@ -656,8 +695,6 @@
 
 struct fcb_type  /*  File, char, block type  */
 {
-    uid_t default_uid;
-    gid_t default_gid;
     void *ops;
     union 
     {
@@ -678,20 +715,13 @@
     char *linkname;              /*  This is NULL-terminated                 */
 };
 
-struct fifo_type
-{
-    uid_t uid;
-    gid_t gid;
-};
-
-struct devfs_inode  /*  This structure is for "persistent" inode storage  */
+struct devfs_inode     /*  This structure is for "persistent" inode storage  */
 {
+    struct dentry *dentry;
     time_t atime;
     time_t mtime;
     time_t ctime;
-    unsigned int ino;          /*  Inode number as seen in the VFS  */
-    struct dentry *dentry;
-    umode_t mode;
+    unsigned int ino;            /*  Inode number as seen in the VFS         */
     uid_t uid;
     gid_t gid;
 };
@@ -699,12 +729,13 @@
 struct devfs_entry
 {
     void *info;
+    atomic_t refcount;           /*  When this drops to zero, it's unused    */
     union 
     {
 	struct directory_type dir;
 	struct fcb_type fcb;
 	struct symlink_type symlink;
-	struct fifo_type fifo;
+	const char *name;        /*  Only used for (mode == 0)               */
     }
     u;
     struct devfs_entry *prev;    /*  Previous entry in the parent directory  */
@@ -713,12 +744,11 @@
     struct devfs_entry *slave;   /*  Another entry to unregister             */
     struct devfs_inode inode;
     umode_t mode;
-    unsigned short namelen;  /*  I think 64k+ filenames are a way off...  */
-    unsigned char registered:1;
+    unsigned short namelen;      /*  I think 64k+ filenames are a way off... */
     unsigned char hide:1;
-    unsigned char no_persistence:1;
-    char name[1];            /*  This is just a dummy: the allocated array is
-				 bigger. This is NULL-terminated  */
+    unsigned char vfs_created:1; /*  Whether created by driver or VFS        */
+    char name[1];                /*  This is just a dummy: the allocated array
+				     is bigger. This is NULL-terminated      */
 };
 
 /*  The root of the device tree  */
@@ -726,36 +756,42 @@
 
 struct devfsd_buf_entry
 {
-    void *data;
-    unsigned int type;
+    struct devfs_entry *de;      /*  The name is generated with this         */
+    unsigned short type;         /*  The type of event                       */
     umode_t mode;
     uid_t uid;
     gid_t gid;
+    struct devfsd_buf_entry *next;
 };
 
-struct fs_info      /*  This structure is for the mounted devfs  */
+struct fs_info                  /*  This structure is for the mounted devfs  */
 {
     struct super_block *sb;
-    volatile struct devfsd_buf_entry *devfsd_buffer;
-    spinlock_t devfsd_buffer_lock;
-    volatile unsigned int devfsd_buf_in;
-    volatile unsigned int devfsd_buf_out;
+    spinlock_t devfsd_buffer_lock;  /*  Lock when inserting/deleting events  */
+    struct devfsd_buf_entry *devfsd_first_event;
+    struct devfsd_buf_entry *devfsd_last_event;
     volatile int devfsd_sleeping;
     volatile struct task_struct *devfsd_task;
+    volatile pid_t devfsd_pgrp;
     volatile struct file *devfsd_file;
     struct devfsd_notify_struct *devfsd_info;
     volatile unsigned long devfsd_event_mask;
     atomic_t devfsd_overrun_count;
-    wait_queue_head_t devfsd_wait_queue;
-    wait_queue_head_t revalidate_wait_queue;
+    wait_queue_head_t devfsd_wait_queue;      /*  Wake devfsd on input       */
+    wait_queue_head_t revalidate_wait_queue;  /*  Wake when devfsd sleeps    */
 };
 
 static struct fs_info fs_info = {devfsd_buffer_lock: SPIN_LOCK_UNLOCKED};
-static const int devfsd_buf_size = PAGE_SIZE / sizeof(struct devfsd_buf_entry);
+static kmem_cache_t *devfsd_buf_cache;
 #ifdef CONFIG_DEVFS_DEBUG
 static unsigned int devfs_debug_init __initdata = DEBUG_NONE;
 static unsigned int devfs_debug = DEBUG_NONE;
+static spinlock_t stat_lock = SPIN_LOCK_UNLOCKED;
+static unsigned int stat_num_entries;
+static unsigned int stat_num_bytes;
 #endif
+static unsigned char poison_array[8] =
+    {0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a};
 
 #ifdef CONFIG_DEVFS_MOUNT
 static unsigned int boot_options = OPTION_MOUNT;
@@ -763,19 +799,23 @@
 static unsigned int boot_options = OPTION_NONE;
 #endif
 
-static DECLARE_RWSEM (symlink_rwsem);
-
 /*  Forward function declarations  */
-static struct devfs_entry *search_for_entry (struct devfs_entry *dir,
-					     const char *name,
-					     unsigned int namelen, int mkdir,
-					     int mkfile, int *is_new,
-					     int traverse_symlink);
+static devfs_handle_t _devfs_walk_path (struct devfs_entry *dir,
+					const char *name, int namelen,
+					int traverse_symlink);
 static ssize_t devfsd_read (struct file *file, char *buf, size_t len,
 			    loff_t *ppos);
 static int devfsd_ioctl (struct inode *inode, struct file *file,
 			 unsigned int cmd, unsigned long arg);
 static int devfsd_close (struct inode *inode, struct file *file);
+#ifdef CONFIG_DEVFS_DEBUG
+static int stat_read (struct file *file, char *buf, size_t len,
+			    loff_t *ppos);
+static struct file_operations stat_fops =
+{
+    read:    stat_read,
+};
+#endif
 
 
 /*  Devfs daemon file operations  */
@@ -791,46 +831,98 @@
 
 
 /**
- *	search_for_entry_in_dir - Search for a devfs entry inside another devfs entry.
- *	@parent:  The parent devfs entry.
- *	@name:  The name of the entry.
+ *	devfs_get - Get a reference to a devfs entry.
+ *	@de:  The devfs entry.
+ */
+
+static struct devfs_entry *devfs_get (struct devfs_entry *de)
+{
+    if (de) atomic_inc (&de->refcount);
+    return de;
+}   /*  End Function devfs_get  */
+
+/**
+ *	devfs_put - Put (release) a reference to a devfs entry.
+ *	@de:  The handle to the devfs entry.
+ */
+
+void devfs_put (devfs_handle_t de)
+{
+    if (!de) return;
+    if (de->info == POISON_PTR)
+	OOPS ("%s: devfs_put(%p): poisoned pointer\n", DEVFS_NAME, de);
+    if ( !atomic_dec_and_test (&de->refcount) ) return;
+    if (de == root_entry)
+	OOPS ("%s: devfs_put(): root entry being freed\n", DEVFS_NAME);
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_FREE)
+	printk ("%s: devfs_put(%s): de: %p, parent: %p \"%s\"\n",
+		DEVFS_NAME, de->name, de, de->parent,
+		de->parent ? de->parent->name : "no parent");
+#endif
+    if ( S_ISLNK (de->mode) ) kfree (de->u.symlink.linkname);
+    if ( ( S_ISCHR (de->mode) || S_ISBLK (de->mode) ) && de->u.fcb.autogen )
+    {
+	devfs_dealloc_devnum ( S_ISCHR (de->mode) ? DEVFS_SPECIAL_CHR :
+			       DEVFS_SPECIAL_BLK,
+			       MKDEV (de->u.fcb.u.device.major,
+				      de->u.fcb.u.device.minor) );
+    }
+#ifdef CONFIG_DEVFS_DEBUG
+    spin_lock (&stat_lock);
+    --stat_num_entries;
+    stat_num_bytes -= sizeof *de + de->namelen;
+    if ( S_ISLNK (de->mode) ) stat_num_bytes -= de->u.symlink.length + 1;
+    spin_unlock (&stat_lock);
+#endif
+    de->info = POISON_PTR;
+    kfree (de);
+}   /*  End Function devfs_put  */
+
+/**
+ *	_devfs_search_dir - Search for a devfs entry in a directory.
+ *	@dir:  The directory to search.
+ *	@name:  The name of the entry to search for.
  *	@namelen:  The number of characters in @name.
- *	@traverse_symlink:  If %TRUE then the entry is traversed if it is a symlink.
  *
- *  Search for a devfs entry inside another devfs entry and returns a pointer
- *   to the entry on success, else %NULL.
+ *  Search for a devfs entry in a directory and returns a pointer to the entry
+ *   on success, else %NULL. The directory must be locked already.
+ *   An implicit devfs_get() is performed on the returned entry.
  */
 
-static struct devfs_entry *search_for_entry_in_dir (struct devfs_entry *parent,
-						    const char *name,
-						    unsigned int namelen,
-						    int traverse_symlink)
+static struct devfs_entry *_devfs_search_dir (struct devfs_entry *dir,
+					      const char *name,
+					      unsigned int namelen)
 {
-    struct devfs_entry *curr, *retval;
+    struct devfs_entry *curr;
 
-    if ( !S_ISDIR (parent->mode) )
+    if ( !S_ISDIR (dir->mode) )
     {
-	printk ("%s: entry is not a directory\n", DEVFS_NAME);
+	printk ("%s: search_dir(%s): not a directory\n", DEVFS_NAME,dir->name);
 	return NULL;
     }
-    for (curr = parent->u.dir.first; curr != NULL; curr = curr->next)
+    for (curr = dir->u.dir.first; curr != NULL; curr = curr->next)
     {
 	if (curr->namelen != namelen) continue;
 	if (memcmp (curr->name, name, namelen) == 0) break;
 	/*  Not found: try the next one  */
     }
-    if (curr == NULL) return NULL;
-    if (!S_ISLNK (curr->mode) || !traverse_symlink) return curr;
-    /*  Need to follow the link: this is a stack chomper  */
-    retval = curr->registered ?
-	search_for_entry (parent, curr->u.symlink.linkname,
-			  curr->u.symlink.length, FALSE, FALSE, NULL,
-			  TRUE) : NULL;
-    return retval;
-}   /*  End Function search_for_entry_in_dir  */
+    return devfs_get (curr);
+}   /*  End Function _devfs_search_dir  */
+
+
+/**
+ *	_devfs_alloc_entry - Allocate a devfs entry.
+ *	@name:  The name of the entry.
+ *	@namelen:  The number of characters in @name.
+ *
+ *  Allocate a devfs entry and returns a pointer to the entry on success, else
+ *   %NULL.
+ */
 
-static struct devfs_entry *create_entry (struct devfs_entry *parent,
-					 const char *name,unsigned int namelen)
+static struct devfs_entry *_devfs_alloc_entry (const char *name,
+					       unsigned int namelen,
+					       umode_t mode)
 {
     struct devfs_entry *new;
     static unsigned long inode_counter = FIRST_INODE;
@@ -839,168 +931,270 @@
     if ( name && (namelen < 1) ) namelen = strlen (name);
     if ( ( new = kmalloc (sizeof *new + namelen, GFP_KERNEL) ) == NULL )
 	return NULL;
-    /*  Magic: this will set the ctime to zero, thus subsequent lookups will
-	trigger the call to <update_devfs_inode_from_entry>  */
-    memset (new, 0, sizeof *new + namelen);
+    memset (new, 0, sizeof *new + namelen);  /*  Will set '\0' on name  */
+    new->mode = mode;
+    if ( S_ISDIR (mode) ) rwlock_init (&new->u.dir.lock);
+    atomic_set (&new->refcount, 1);
     spin_lock (&counter_lock);
     new->inode.ino = inode_counter++;
     spin_unlock (&counter_lock);
-    new->parent = parent;
     if (name) memcpy (new->name, name, namelen);
     new->namelen = namelen;
-    if (parent == NULL) return new;
-    new->prev = parent->u.dir.last;
-    /*  Insert into the parent directory's list of children  */
-    if (parent->u.dir.first == NULL) parent->u.dir.first = new;
-    else parent->u.dir.last->next = new;
-    parent->u.dir.last = new;
+#ifdef CONFIG_DEVFS_DEBUG
+    spin_lock (&stat_lock);
+    ++stat_num_entries;
+    stat_num_bytes += sizeof *new + namelen;
+    spin_unlock (&stat_lock);
+#endif
     return new;
-}   /*  End Function create_entry  */
+}   /*  End Function _devfs_alloc_entry  */
+
+
+/**
+ *	_devfs_append_entry - Append a devfs entry to a directory's child list.
+ *	@dir:  The directory to add to.
+ *	@de:  The devfs entry to append.
+ *	@removable: If TRUE, increment the count of removable devices for %dir.
+ *	@old_de: If an existing entry exists, it will be written here. This may
+ *		 be %NULL. An implicit devfs_get() is performed on this entry.
+ *
+ *  Append a devfs entry to a directory's list of children, checking first to
+ *   see if an entry of the same name exists. The directory will be locked.
+ *   The value 0 is returned on success, else a negative error code.
+ *   On failure, an implicit devfs_put() is performed on %de.
+ */
 
-static void update_devfs_inode_from_entry (struct devfs_entry *de)
+static int _devfs_append_entry (devfs_handle_t dir, devfs_handle_t de,
+				int removable, devfs_handle_t *old_de)
 {
-    if (de == NULL) return;
-    if ( S_ISDIR (de->mode) )
-    {
-	de->inode.mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
-	de->inode.uid = 0;
-	de->inode.gid = 0;
-    }
-    else if ( S_ISLNK (de->mode) )
-    {
-	de->inode.mode = S_IFLNK | S_IRUGO | S_IXUGO;
-	de->inode.uid = 0;
-	de->inode.gid = 0;
-    }
-    else if ( S_ISFIFO (de->mode) )
+    int retval;
+
+    if (old_de) *old_de = NULL;
+    if ( !S_ISDIR (dir->mode) )
     {
-	de->inode.mode = de->mode;
-	de->inode.uid = de->u.fifo.uid;
-	de->inode.gid = de->u.fifo.gid;
+	printk ("%s: append_entry(%s): dir: \"%s\" is not a directory\n",
+		DEVFS_NAME, de->name, dir->name);
+	devfs_put (de);
+	return -ENOTDIR;
     }
+    write_lock (&dir->u.dir.lock);
+    if (dir->u.dir.no_more_additions) retval = -ENOENT;
     else
     {
-	if (de->u.fcb.auto_owner)
-	    de->inode.mode = (de->mode & ~S_IALLUGO) | S_IRUGO | S_IWUGO;
-	else de->inode.mode = de->mode;
-	de->inode.uid = de->u.fcb.default_uid;
-	de->inode.gid = de->u.fcb.default_gid;
+	struct devfs_entry *old;
+
+	old = _devfs_search_dir (dir, de->name, de->namelen);
+	if (old_de) *old_de = old;
+	else devfs_put (old);
+	if (old == NULL)
+	{
+	    de->parent = dir;
+	    de->prev = dir->u.dir.last;
+	    /*  Append to the directory's list of children  */
+	    if (dir->u.dir.first == NULL) dir->u.dir.first = de;
+	    else dir->u.dir.last->next = de;
+	    dir->u.dir.last = de;
+	    if (removable) ++dir->u.dir.num_removable;
+	    retval = 0;
+	}
+	else retval = -EEXIST;
     }
-}   /*  End Function update_devfs_inode_from_entry  */
+    write_unlock (&dir->u.dir.lock);
+    if (retval) devfs_put (de);
+    return retval;
+}   /*  End Function _devfs_append_entry  */
+
 
 /**
- *	get_root_entry - Get the root devfs entry.
+ *	_devfs_get_root_entry - Get the root devfs entry.
  *
  *	Returns the root devfs entry on success, else %NULL.
  */
 
-static struct devfs_entry *get_root_entry (void)
+static struct devfs_entry *_devfs_get_root_entry (void)
 {
     kdev_t devnum;
     struct devfs_entry *new;
+    static spinlock_t root_lock = SPIN_LOCK_UNLOCKED;
 
     /*  Always ensure the root is created  */
-    if (root_entry != NULL) return root_entry;
-    if ( ( root_entry = create_entry (NULL, NULL, 0) ) == NULL ) return NULL;
-    root_entry->mode = S_IFDIR;
-    /*  Force an inode update, because lookup() is never done for the root  */
-    update_devfs_inode_from_entry (root_entry);
-    root_entry->registered = TRUE;
+    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 = create_entry (root_entry, ".devfsd", 0) ) == NULL )
-	return NULL;
+    if ( ( new = _devfs_alloc_entry (".devfsd", 0, S_IFCHR |S_IRUSR |S_IWUSR) )
+	 == NULL ) return NULL;
     devnum = devfs_alloc_devnum (DEVFS_SPECIAL_CHR);
     new->u.fcb.u.device.major = MAJOR (devnum);
     new->u.fcb.u.device.minor = MINOR (devnum);
-    new->mode = S_IFCHR | S_IRUSR | S_IWUSR;
-    new->u.fcb.default_uid = 0;
-    new->u.fcb.default_gid = 0;
     new->u.fcb.ops = &devfsd_fops;
-    new->registered = TRUE;
+    _devfs_append_entry (root_entry, new, FALSE, NULL);
+#ifdef CONFIG_DEVFS_DEBUG
+    if ( ( new = _devfs_alloc_entry (".stat", 0, S_IFCHR | S_IRUGO | S_IWUGO) )
+	 == NULL ) return NULL;
+    devnum = devfs_alloc_devnum (DEVFS_SPECIAL_CHR);
+    new->u.fcb.u.device.major = MAJOR (devnum);
+    new->u.fcb.u.device.minor = MINOR (devnum);
+    new->u.fcb.ops = &stat_fops;
+    _devfs_append_entry (root_entry, new, FALSE, NULL);
+#endif
     return root_entry;
-}   /*  End Function get_root_entry  */
+}   /*  End Function _devfs_get_root_entry  */
 
 
 /**
- *	search_for_entry - Search for an entry in the devfs tree.
- *	@dir: The parent directory to search from. If this is %NULL the root is used
- *	@name: The name of the entry.
- *	@namelen: The number of characters in @name.
- *	@mkdir: If %TRUE intermediate directories are created as needed.
- *	@mkfile: If %TRUE the file entry is created if it doesn't exist.
- *	@is_new: If the returned entry was newly made, %TRUE is written here. If
- * 		this is %NULL nothing is written here.
- *	@traverse_symlink: If %TRUE then symbolic links are traversed.
+ *	_devfs_descend - Descend down a tree using the next component name.
+ *	@dir:  The directory to search.
+ *	@name:  The component name to search for.
+ *	@namelen:  The length of %name.
+ *	@next_pos:  The position of the next '/' or '\0' is written here.
  *
- *	If the entry is created, then it will be in the unregistered state.
- *	Returns a pointer to the entry on success, else %NULL.
+ *  Descend into a directory, searching for a component. This function forms
+ *   the core of a tree-walking algorithm. The directory will be locked.
+ *   The devfs entry corresponding to the component is returned. If there is
+ *   no matching entry, %NULL is returned.
+ *   An implicit devfs_get() is performed on the returned entry.
  */
 
-static struct devfs_entry *search_for_entry (struct devfs_entry *dir,
-					     const char *name,
-					     unsigned int namelen, int mkdir,
-					     int mkfile, int *is_new,
-					     int traverse_symlink)
+static struct devfs_entry *_devfs_descend (struct devfs_entry *dir,
+					   const char *name, int namelen,
+					   int *next_pos)
 {
-    int len;
-    const char *subname, *stop, *ptr;
+    const char *stop, *ptr;
     struct devfs_entry *entry;
 
-    if (is_new) *is_new = FALSE;
-    if (dir == NULL) dir = get_root_entry ();
-    if (dir == NULL) return NULL;
-    /*  Extract one filename component  */
-    subname = name;
+    if ( (namelen >= 3) && (strncmp (name, "../", 3) == 0) )
+    {   /*  Special-case going to parent directory  */
+	*next_pos = 3;
+	return devfs_get (dir->parent);
+    }
     stop = name + namelen;
-    while (subname < stop)
-    {
-	/*  Search for a possible '/'  */
-	for (ptr = subname; (ptr < stop) && (*ptr != '/'); ++ptr);
-	if (ptr >= stop)
-	{
-	    /*  Look for trailing component  */
-	    len = stop - subname;
-	    entry = search_for_entry_in_dir (dir, subname, len,
-					     traverse_symlink);
-	    if (entry != NULL) return entry;
-	    if (!mkfile) return NULL;
-	    entry = create_entry (dir, subname, len);
-	    if (entry && is_new) *is_new = TRUE;
-	    return entry;
+    /*  Search for a possible '/'  */
+    for (ptr = name; (ptr < stop) && (*ptr != '/'); ++ptr);
+    *next_pos = ptr - name;
+    read_lock (&dir->u.dir.lock);
+    entry = _devfs_search_dir (dir, name, *next_pos);
+    read_unlock (&dir->u.dir.lock);
+    return entry;
+}   /*  End Function _devfs_descend  */
+
+
+static devfs_handle_t _devfs_make_parent_for_leaf (struct devfs_entry *dir,
+						   const char *name,
+						   int namelen, int *leaf_pos)
+{
+    int next_pos = 0;
+
+    if (dir == NULL) dir = _devfs_get_root_entry ();
+    if (dir == NULL) return NULL;
+    devfs_get (dir);
+    /*  Search for possible trailing component and ignore it  */
+    for (--namelen; (namelen > 0) && (name[namelen] != '/'); --namelen);
+    *leaf_pos = (name[namelen] == '/') ? (namelen + 1) : 0;
+    for (; namelen > 0; name += next_pos, namelen -= next_pos)
+    {
+	struct devfs_entry *de, *old;
+
+	if ( ( de = _devfs_descend (dir, name, namelen, &next_pos) ) == NULL )
+	{
+	    de = _devfs_alloc_entry (name, next_pos, MODE_DIR);
+	    devfs_get (de);
+	    if ( !de || _devfs_append_entry (dir, de, FALSE, &old) )
+	    {
+		devfs_put (de);
+		if ( !old || !S_ISDIR (old->mode) )
+		{
+		    devfs_put (old);
+		    devfs_put (dir);
+		    return NULL;
+		}
+		de = old;  /*  Use the existing directory  */
+	    }
 	}
-	/*  Found '/': search for directory  */
-	if (strncmp (subname, "../", 3) == 0)
+	if (de == dir->parent)
 	{
-	    /*  Going up  */
-	    dir = dir->parent;
-	    if (dir == NULL) return NULL;  /*  Cannot escape from devfs  */
-	    subname += 3;
-	    continue;
-	}
-	len = ptr - subname;
-	entry = search_for_entry_in_dir (dir, subname, len, traverse_symlink);
-	if (!entry && !mkdir) return NULL;
-	if (entry == NULL)
-	{
-	    /*  Make it  */
-	    if ( ( entry = create_entry (dir, subname, len) ) == NULL )
-		return NULL;
-	    entry->mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
-	    if (is_new) *is_new = TRUE;
+	    devfs_put (dir);
+	    devfs_put (de);
+	    return NULL;
 	}
-	if ( !S_ISDIR (entry->mode) )
+	devfs_put (dir);
+	dir = de;
+	if (name[next_pos] == '/') ++next_pos;
+    }
+    return dir;
+}   /*  End Function _devfs_make_parent_for_leaf  */
+
+
+static devfs_handle_t _devfs_prepare_leaf (devfs_handle_t *dir,
+					   const char *name, umode_t mode)
+{
+    int namelen, leaf_pos;
+    struct devfs_entry *de;
+
+    namelen = strlen (name);
+    if ( ( *dir = _devfs_make_parent_for_leaf (*dir, name, namelen,
+					       &leaf_pos) ) == NULL )
+    {
+	printk ("%s: prepare_leaf(%s): could not create parent path\n",
+		DEVFS_NAME, name);
+	return NULL;
+    }
+    if ( ( de = _devfs_alloc_entry (name + leaf_pos, namelen - leaf_pos,mode) )
+	 == NULL )
+    {
+	printk ("%s: prepare_leaf(%s): could not allocate entry\n",
+		DEVFS_NAME, name);
+	devfs_put (*dir);
+	return NULL;
+    }
+    return de;
+}   /*  End Function _devfs_prepare_leaf  */
+
+
+static devfs_handle_t _devfs_walk_path (struct devfs_entry *dir,
+					const char *name, int namelen,
+					int traverse_symlink)
+{
+    int next_pos = 0;
+
+    if (dir == NULL) dir = _devfs_get_root_entry ();
+    if (dir == NULL) return NULL;
+    devfs_get (dir);
+    for (; namelen > 0; name += next_pos, namelen -= next_pos)
+    {
+	struct devfs_entry *de, *link;
+
+	if ( ( de = _devfs_descend (dir, name, namelen, &next_pos) ) == NULL )
 	{
-	    printk ("%s: existing non-directory entry\n", DEVFS_NAME);
+	    devfs_put (dir);
 	    return NULL;
 	}
-	/*  Ensure an unregistered entry is re-registered and visible  */
-	entry->hide = FALSE;
-	entry->registered = TRUE;
-	subname = ptr + 1;
-	dir = entry;
+	if (S_ISLNK (de->mode) && traverse_symlink)
+	{   /*  Need to follow the link: this is a stack chomper  */
+	    link = _devfs_walk_path (dir, de->u.symlink.linkname,
+				     de->u.symlink.length, TRUE);
+	    devfs_put (de);
+	    if (!link)
+	    {
+		devfs_put (dir);
+		return NULL;
+	    }
+	    de = link;
+	}
+	devfs_put (dir);
+	dir = de;
+	if (name[next_pos] == '/') ++next_pos;
     }
-    return NULL;
-}   /*  End Function search_for_entry  */
+    return dir;
+}   /*  End Function _devfs_walk_path  */
 
 
 /**
@@ -1011,7 +1205,8 @@
  *	@type: The type of special file to search for. This may be either
  *		%DEVFS_SPECIAL_CHR or %DEVFS_SPECIAL_BLK.
  *
- *	Returns the devfs_entry pointer on success, else %NULL.
+ *	Returns the devfs_entry pointer on success, else %NULL. An implicit
+ *	devfs_get() is performed.
  */
 
 static struct devfs_entry *find_by_dev (struct devfs_entry *dir,
@@ -1020,20 +1215,29 @@
 {
     struct devfs_entry *entry, *de;
 
+    devfs_get (dir);
     if (dir == NULL) return NULL;
     if ( !S_ISDIR (dir->mode) )
     {
 	printk ("%s: find_by_dev(): not a directory\n", DEVFS_NAME);
+	devfs_put (dir);
 	return NULL;
     }
     /*  First search files in this directory  */
+    read_lock (&dir->u.dir.lock);
     for (entry = dir->u.dir.first; entry != NULL; entry = entry->next)
     {
 	if ( !S_ISCHR (entry->mode) && !S_ISBLK (entry->mode) ) continue;
 	if ( S_ISCHR (entry->mode) && (type != DEVFS_SPECIAL_CHR) ) continue;
 	if ( S_ISBLK (entry->mode) && (type != DEVFS_SPECIAL_BLK) ) continue;
 	if ( (entry->u.fcb.u.device.major == major) &&
-	     (entry->u.fcb.u.device.minor == minor) ) return entry;
+	     (entry->u.fcb.u.device.minor == minor) )
+	{
+	    devfs_get (entry);
+	    read_unlock (&dir->u.dir.lock);
+	    devfs_put (dir);
+	    return entry;
+	}
 	/*  Not found: try the next one  */
     }
     /*  Now recursively search the subdirectories: this is a stack chomper  */
@@ -1041,8 +1245,15 @@
     {
 	if ( !S_ISDIR (entry->mode) ) continue;
 	de = find_by_dev (entry, major, minor, type);
-	if (de) return de;
+	if (de)
+	{
+	    read_unlock (&dir->u.dir.lock);
+	    devfs_put (dir);
+	    return de;
+	}
     }
+    read_unlock (&dir->u.dir.lock);
+    devfs_put (dir);
     return NULL;
 }   /*  End Function find_by_dev  */
 
@@ -1051,24 +1262,20 @@
  *	find_entry - Find a devfs entry.
  *	@dir: The handle to the parent devfs directory entry. If this is %NULL the
  *		name is relative to the root of the devfs.
- *	@name: The name of the entry. This is ignored if @handle is not %NULL.
- *	@namelen: The number of characters in @name, not including a %NULL
- *		terminator. If this is 0, then @name must be %NULL-terminated and the
- *		length is computed internally.
- *	@major: The major number. This is used if @handle and @name are %NULL.
- *	@minor: The minor number. This is used if @handle and @name are %NULL.
+ *	@name: The name of the entry. This may be %NULL.
+ *	@major: The major number. This is used if lookup by @name fails.
+ *	@minor: The minor number. This is used if lookup by @name fails.
  *		NOTE: If @major and @minor are both 0, searching by major and minor
  *		numbers is disabled.
  *	@type: The type of special file to search for. This may be either
  *		%DEVFS_SPECIAL_CHR or %DEVFS_SPECIAL_BLK.
  *	@traverse_symlink: If %TRUE then symbolic links are traversed.
  *
- *	FIXME: What the hell is @handle? - ch
- *	Returns the devfs_entry pointer on success, else %NULL.
+ *	Returns the devfs_entry pointer on success, else %NULL. An implicit
+ *	devfs_get() is performed.
  */
 
-static struct devfs_entry *find_entry (devfs_handle_t dir,
-				       const char *name, unsigned int namelen,
+static struct devfs_entry *find_entry (devfs_handle_t dir, const char *name,
 				       unsigned int major, unsigned int minor,
 				       char type, int traverse_symlink)
 {
@@ -1076,7 +1283,8 @@
 
     if (name != NULL)
     {
-	if (namelen < 1) namelen = strlen (name);
+	unsigned int namelen = strlen (name);
+
 	if (name[0] == '/')
 	{
 	    /*  Skip leading pathname component  */
@@ -1095,10 +1303,7 @@
 	    ++name;
 	    --namelen;
 	}
-	if (traverse_symlink) down_read (&symlink_rwsem);
-	entry = search_for_entry (dir, name, namelen, FALSE, FALSE, NULL,
-				  traverse_symlink);
-	if (traverse_symlink) up_read (&symlink_rwsem);
+	entry = _devfs_walk_path (dir, name, namelen, traverse_symlink);
 	if (entry != NULL) return entry;
     }
     /*  Have to search by major and minor: slow  */
@@ -1106,42 +1311,34 @@
     return find_by_dev (root_entry, major, minor, type);
 }   /*  End Function find_entry  */
 
-static struct devfs_entry *get_devfs_entry_from_vfs_inode (struct inode *inode,
-							   int do_check)
+static struct devfs_entry *get_devfs_entry_from_vfs_inode (struct inode *inode)
 {
-    struct devfs_entry *de;
-
     if (inode == NULL) return NULL;
-    de = inode->u.generic_ip;
-    if (!de) printk (__FUNCTION__ "(): NULL de for inode %ld\n", inode->i_ino);
-    if (do_check && de && !de->registered) de = NULL;
-    return de;
+    return inode->u.generic_ip;
 }   /*  End Function get_devfs_entry_from_vfs_inode  */
 
 
 /**
- *	free_dentries - Free the dentries for a device entry and invalidate inodes.
+ *	free_dentry - Free the dentry for a device entry and invalidate inode.
  *	@de: The entry.
+ *
+ *	This must only be called after the entry has been unhooked from it's
+ *	 parent directory.
  */
 
-static void free_dentries (struct devfs_entry *de)
+static void free_dentry (struct devfs_entry *de)
 {
-    struct dentry *dentry;
+    struct dentry *dentry = de->inode.dentry;
 
+    if (!dentry) return;
     spin_lock (&dcache_lock);
-    dentry = de->inode.dentry;
-    if (dentry != NULL)
-    {
-	dget_locked (dentry);
-	de->inode.dentry = NULL;
-	spin_unlock (&dcache_lock);
-	/*  Forcefully remove the inode  */
-	if (dentry->d_inode != NULL) dentry->d_inode->i_nlink = 0;
-	d_drop (dentry);
-	dput (dentry);
-    }
-    else spin_unlock (&dcache_lock);
-}   /*  End Function free_dentries  */
+    dget_locked (dentry);
+    spin_unlock (&dcache_lock);
+    /*  Forcefully remove the inode  */
+    if (dentry->d_inode != NULL) dentry->d_inode->i_nlink = 0;
+    d_drop (dentry);
+    dput (dentry);
+}   /*  End Function free_dentry  */
 
 
 /**
@@ -1155,10 +1352,14 @@
 {
     struct task_struct *p;
 
-    for (p = current; p != &init_task; p = p->p_opptr)
+    if (current == fs_info->devfsd_task) return (TRUE);
+    if (current->pgrp == fs_info->devfsd_pgrp) return (TRUE);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,1)
+    for (p = current->p_opptr; p != &init_task; p = p->p_opptr)
     {
 	if (p == fs_info->devfsd_task) return (TRUE);
     }
+#endif
     return (FALSE);
 }   /*  End Function is_devfsd_or_child  */
 
@@ -1172,7 +1373,7 @@
 
 static inline int devfsd_queue_empty (struct fs_info *fs_info)
 {
-    return (fs_info->devfsd_buf_out == fs_info->devfsd_buf_in) ? TRUE : FALSE;
+    return (fs_info->devfsd_last_event) ? FALSE : TRUE;
 }   /*  End Function devfsd_queue_empty  */
 
 
@@ -1201,8 +1402,9 @@
 
 
 /**
- *	devfsd_notify_one - Notify a single devfsd daemon of a change.
- *	@data: Data to be passed.
+ *	devfsd_notify_de - Notify the devfsd daemon of a change.
+ *	@de: The devfs entry that has changed. This and all parent entries will
+ *            have their reference counts incremented if the event was queued.
  *	@type: The type of change.
  *	@mode: The mode of the entry.
  *	@uid: The user ID.
@@ -1212,51 +1414,51 @@
  *	Returns %TRUE if an event was queued and devfsd woken up, else %FALSE.
  */
 
-static int devfsd_notify_one (void *data, unsigned int type, umode_t mode,
-			      uid_t uid, gid_t gid, struct fs_info *fs_info)
+static int devfsd_notify_de (struct devfs_entry *de,
+			     unsigned short type, umode_t mode,
+			     uid_t uid, gid_t gid, struct fs_info *fs_info,
+			     int atomic)
 {
-    unsigned int next_pos;
-    unsigned long flags;
     struct devfsd_buf_entry *entry;
+    struct devfs_entry *curr;
 
     if ( !( fs_info->devfsd_event_mask & (1 << type) ) ) return (FALSE);
-    next_pos = fs_info->devfsd_buf_in + 1;
-    if (next_pos >= devfsd_buf_size) next_pos = 0;
-    if (next_pos == fs_info->devfsd_buf_out)
+    if ( ( entry = kmem_cache_alloc (devfsd_buf_cache,
+				     atomic ? SLAB_ATOMIC : SLAB_KERNEL) )
+	 == NULL )
     {
-	/*  Running up the arse of the reader: drop it  */
 	atomic_inc (&fs_info->devfsd_overrun_count);
 	return (FALSE);
     }
-    spin_lock_irqsave (&fs_info->devfsd_buffer_lock, flags);
-    next_pos = fs_info->devfsd_buf_in + 1;
-    if (next_pos >= devfsd_buf_size) next_pos = 0;
-    entry = (struct devfsd_buf_entry *) fs_info->devfsd_buffer +
-	fs_info->devfsd_buf_in;
-    entry->data = data;
+    for (curr = de; curr != NULL; curr = curr->parent) devfs_get (curr);
+    entry->de = de;
     entry->type = type;
     entry->mode = mode;
     entry->uid = uid;
     entry->gid = gid;
-    fs_info->devfsd_buf_in = next_pos;
-    spin_unlock_irqrestore (&fs_info->devfsd_buffer_lock, flags);
+    entry->next = NULL;
+    spin_lock (&fs_info->devfsd_buffer_lock);
+    if (!fs_info->devfsd_first_event) fs_info->devfsd_first_event = entry;
+    if (fs_info->devfsd_last_event) fs_info->devfsd_last_event->next = entry;
+    fs_info->devfsd_last_event = entry;
+    spin_unlock (&fs_info->devfsd_buffer_lock);
     wake_up_interruptible (&fs_info->devfsd_wait_queue);
     return (TRUE);
-}   /*  End Function devfsd_notify_one  */
+}   /*  End Function devfsd_notify_de  */
 
 
 /**
- *	devfsd_notify - Notify all devfsd daemons of a change.
+ *	devfsd_notify - Notify the devfsd daemon of a change.
  *	@de: The devfs entry that has changed.
  *	@type: The type of change event.
- *	@wait: If TRUE, the functions waits for all daemons to finish processing
+ *	@wait: If TRUE, the function waits for the daemon to finish processing
  *		the event.
  */
 
-static void devfsd_notify (struct devfs_entry *de, unsigned int type, int wait)
+static void devfsd_notify (struct devfs_entry *de,unsigned short type,int wait)
 {
-    if (devfsd_notify_one (de, type, de->mode, current->euid,
-			   current->egid, &fs_info) && wait)
+    if (devfsd_notify_de (de, type, de->mode, current->euid,
+			  current->egid, &fs_info, 0) && wait)
 	wait_for_devfsd_finished (&fs_info);
 }   /*  End Function devfsd_notify  */
 
@@ -1287,7 +1489,7 @@
 			       umode_t mode, void *ops, void *info)
 {
     char devtype = S_ISCHR (mode) ? DEVFS_SPECIAL_CHR : DEVFS_SPECIAL_BLK;
-    int is_new;
+    int err;
     kdev_t devnum = NODEV;
     struct devfs_entry *de;
 
@@ -1332,146 +1534,128 @@
 	major = MAJOR (devnum);
 	minor = MINOR (devnum);
     }
-    de = search_for_entry (dir, name, strlen (name), TRUE, TRUE, &is_new,
-			   FALSE);
-    if (de == NULL)
+    if ( ( de = _devfs_prepare_leaf (&dir, name, mode) ) == NULL )
     {
-	printk ("%s: devfs_register(): could not create entry: \"%s\"\n",
+	printk ("%s: devfs_register(%s): could not prepare leaf\n",
 		DEVFS_NAME, name);
 	if (devnum != NODEV) devfs_dealloc_devnum (devtype, devnum);
 	return NULL;
     }
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_REGISTER)
-	printk ("%s: devfs_register(%s): de: %p %s\n",
-		DEVFS_NAME, name, de, is_new ? "new" : "existing");
-#endif
-    if (!is_new)
-    {
-	/*  Existing entry  */
-	if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) &&
-	     !S_ISREG (de->mode) )
-	{
-	    printk ("%s: devfs_register(): existing non-device/file entry: \"%s\"\n",
-		    DEVFS_NAME, name);
-	    if (devnum != NODEV) devfs_dealloc_devnum (devtype, devnum);
-	    return NULL;
-	}
-	if (de->registered)
-	{
-	    printk("%s: devfs_register(): device already registered: \"%s\"\n",
-		   DEVFS_NAME, name);
-	    if (devnum != NODEV) devfs_dealloc_devnum (devtype, devnum);
-	    return NULL;
-	}
-    }
-    de->u.fcb.autogen = FALSE;
     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 = (devnum == NODEV) ? FALSE : TRUE;
     }
-    else if ( S_ISREG (mode) ) de->u.fcb.u.file.size = 0;
-    else
+    else if ( !S_ISREG (mode) )
     {
-	printk ("%s: devfs_register(): illegal mode: %x\n",
-		DEVFS_NAME, mode);
+	printk ("%s: devfs_register(%s): illegal mode: %x\n",
+		DEVFS_NAME, name, mode);
+	devfs_put (de);
+	devfs_put (dir);
 	return (NULL);
     }
     de->info = info;
-    de->mode = mode;
     if (flags & DEVFS_FL_CURRENT_OWNER)
     {
-	de->u.fcb.default_uid = current->uid;
-	de->u.fcb.default_gid = current->gid;
+	de->inode.uid = current->uid;
+	de->inode.gid = current->gid;
     }
     else
     {
-	de->u.fcb.default_uid = 0;
-	de->u.fcb.default_gid = 0;
+	de->inode.uid = 0;
+	de->inode.gid = 0;
     }
     de->u.fcb.ops = ops;
     de->u.fcb.auto_owner = (flags & DEVFS_FL_AUTO_OWNER) ? TRUE : FALSE;
     de->u.fcb.aopen_notify = (flags & DEVFS_FL_AOPEN_NOTIFY) ? TRUE : FALSE;
-    if (flags & DEVFS_FL_REMOVABLE)
-    {
-	de->u.fcb.removable = TRUE;
-	++de->parent->u.dir.num_removable;
-    }
-    de->u.fcb.open = FALSE;
     de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE;
-    de->no_persistence = (flags & DEVFS_FL_NO_PERSISTENCE) ? TRUE : FALSE;
-    de->registered = TRUE;
+    if (flags & DEVFS_FL_REMOVABLE) de->u.fcb.removable = TRUE;
+    if ( ( err = _devfs_append_entry (dir, de, de->u.fcb.removable, NULL) )
+	 != 0 )
+    {
+	printk("%s: devfs_register(%s): could not append to parent, err: %d\n",
+	       DEVFS_NAME, name, err);
+	devfs_put (dir);
+	if (devnum != NODEV) devfs_dealloc_devnum (devtype, devnum);
+	return NULL;
+    }
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_REGISTER)
+	printk ("%s: devfs_register(%s): de: %p dir: %p \"%s\"  pp: %p\n",
+		DEVFS_NAME, name, de, dir, dir->name, dir->parent);
+#endif
     devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, flags & DEVFS_FL_WAIT);
+    devfs_put (dir);
     return de;
 }   /*  End Function devfs_register  */
 
 
 /**
- *	unregister - Unregister a device entry.
+ *	_devfs_unhook - Unhook a device entry from its parents list
+ *	@de: The entry to unhook.
+ *
+ *	Returns %TRUE if the entry was unhooked, else %FALSE if it was
+ *		previously unhooked.
+ *	The caller must have a write lock on the parent directory.
+ */
+
+static int _devfs_unhook (struct devfs_entry *de)
+{
+    struct devfs_entry *parent;
+
+    if ( !de || (de->prev == de) ) return FALSE;
+    parent = de->parent;
+    if (de->prev == NULL) parent->u.dir.first = de->next;
+    else de->prev->next = de->next;
+    if (de->next == NULL) parent->u.dir.last = de->prev;
+    else de->next->prev = de->prev;
+    de->prev = de;          /*  Indicate we're unhooked                      */
+    de->next = NULL;        /*  Force early termination for <devfs_readdir>  */
+    if ( ( S_ISREG (de->mode) || S_ISCHR (de->mode) || S_ISBLK (de->mode) ) &&
+	 de->u.fcb.removable )
+	--parent->u.dir.num_removable;
+    return TRUE;
+}   /*  End Function _devfs_unhook  */
+
+
+/**
+ *	unregister - Unregister a device entry from it's parent.
+ *	@dir: The parent directory.
  *	@de: The entry to unregister.
+ *
+ *	The caller must have a write lock on the parent directory, which is
+ *	unlocked by this function.
  */
 
-static void unregister (struct devfs_entry *de)
+static void unregister (struct devfs_entry *dir, struct devfs_entry *de)
 {
-    struct devfs_entry *child;
+    int unhooked = _devfs_unhook (de);
 
-    if ( (child = de->slave) != NULL )
-    {
-	de->slave = NULL;  /* Unhook first in case slave is parent directory */
-	unregister (child);
-    }
-    if (de->registered)
-    {
-	devfsd_notify (de, DEVFSD_NOTIFY_UNREGISTERED, 0);
-	free_dentries (de);
-    }
-    de->info = NULL;
-    if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) )
-    {
-	de->registered = FALSE;
-	de->u.fcb.ops = NULL;
-	if (!S_ISREG (de->mode) && de->u.fcb.autogen)
-	{
-	    devfs_dealloc_devnum ( S_ISCHR (de->mode) ? DEVFS_SPECIAL_CHR :
-				   DEVFS_SPECIAL_BLK,
-				   MKDEV (de->u.fcb.u.device.major,
-					  de->u.fcb.u.device.minor) );
-	}
-	de->u.fcb.autogen = FALSE;
-	return;
-    }
-    if (S_ISLNK (de->mode) && de->registered)
-    {
-	de->registered = FALSE;
-	down_write (&symlink_rwsem);
-	if (de->u.symlink.linkname) kfree (de->u.symlink.linkname);
-	de->u.symlink.linkname = NULL;
-	up_write (&symlink_rwsem);
-	return;
-    }
-    if ( S_ISFIFO (de->mode) )
-    {
-	de->registered = FALSE;
-	return;
-    }
-    if (!de->registered) return;
-    if ( !S_ISDIR (de->mode) )
-    {
-	printk ("%s: unregister(): unsupported type\n", DEVFS_NAME);
-	return;
-    }
-    de->registered = FALSE;
-    /*  Now recursively search the subdirectories: this is a stack chomper  */
-    for (child = de->u.dir.first; child != NULL; child = child->next)
-    {
+    write_unlock (&dir->u.dir.lock);
+    if (!unhooked) return;
+    devfs_get (dir);
+    devfs_unregister (de->slave);  /*  Let it handle the locking  */
+    devfsd_notify (de, DEVFSD_NOTIFY_UNREGISTERED, 0);
+    free_dentry (de);
+    devfs_put (dir);
+    if ( !S_ISDIR (de->mode) ) return;
+    while (TRUE)  /*  Recursively unregister: this is a stack chomper  */
+    {
+	struct devfs_entry *child;
+
+	write_lock (&de->u.dir.lock);
+	de->u.dir.no_more_additions = TRUE;
+	child = de->u.dir.first;
+	unregister (de, child);
+	if (!child) break;
 #ifdef CONFIG_DEVFS_DEBUG
 	if (devfs_debug & DEBUG_UNREGISTER)
-	    printk ("%s: unregister(): child->name: \"%s\" child: %p\n",
-		    DEVFS_NAME, child->name, child);
+	    printk ( "%s: unregister(%s): child: %p  refcount: %d\n",
+		     DEVFS_NAME, child->name, child,
+		     atomic_read (&de->refcount) );
 #endif
-	unregister (child);
+	devfs_put (child);
     }
 }   /*  End Function unregister  */
 
@@ -1479,25 +1663,27 @@
 /**
  *	devfs_unregister - Unregister a device entry.
  *	@de: A handle previously created by devfs_register() or returned from
- *		devfs_find_handle(). If this is %NULL the routine does nothing.
+ *		devfs_get_handle(). If this is %NULL the routine does nothing.
  */
 
 void devfs_unregister (devfs_handle_t de)
 {
-    if (de == NULL) return;
+    if ( (de == NULL) || (de->parent == NULL) ) return;
 #ifdef CONFIG_DEVFS_DEBUG
     if (devfs_debug & DEBUG_UNREGISTER)
-	printk ("%s: devfs_unregister(): de->name: \"%s\" de: %p\n",
-		DEVFS_NAME, de->name, de);
+	printk ( "%s: devfs_unregister(%s): de: %p  refcount: %d\n",
+		 DEVFS_NAME, de->name, de, atomic_read (&de->refcount) );
 #endif
-    unregister (de);
+    write_lock (&de->parent->u.dir.lock);
+    unregister (de->parent, de);
+    devfs_put (de);
 }   /*  End Function devfs_unregister  */
 
 static int devfs_do_symlink (devfs_handle_t dir, const char *name,
 			     unsigned int flags, const char *link,
 			     devfs_handle_t *handle, void *info)
 {
-    int is_new;
+    int err;
     unsigned int linklength;
     char *newlink;
     struct devfs_entry *de;
@@ -1522,28 +1708,31 @@
 	return -ENOMEM;
     memcpy (newlink, link, linklength);
     newlink[linklength] = '\0';
-    if ( ( de = search_for_entry (dir, name, strlen (name), TRUE, TRUE,
-				  &is_new, FALSE) ) == NULL )
+    if ( ( de = _devfs_prepare_leaf (&dir, name, S_IFLNK | S_IRUGO | S_IXUGO) )
+	 == NULL )
     {
-	kfree (newlink);
-	return -ENOMEM;
-    }
-    down_write (&symlink_rwsem);
-    if (de->registered)
-    {
-	up_write (&symlink_rwsem);
-	kfree (newlink);
-	printk ("%s: devfs_do_symlink(%s): entry already exists\n",
+	printk ("%s: devfs_do_symlink(%s): could not prepare leaf\n",
 		DEVFS_NAME, name);
-	return -EEXIST;
+	kfree (newlink);
+	return -ENOTDIR;
     }
-    de->mode = S_IFLNK | S_IRUGO | S_IXUGO;
     de->info = info;
     de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE;
     de->u.symlink.linkname = newlink;
     de->u.symlink.length = linklength;
-    de->registered = TRUE;
-    up_write (&symlink_rwsem);
+    if ( ( err = _devfs_append_entry (dir, de, FALSE, NULL) ) != 0 )
+    {
+	printk ("%s: devfs_do_symlink(%s): could not append to parent, err: %d\n",
+		DEVFS_NAME, name, err);
+	devfs_put (dir);
+	return err;
+    }
+    devfs_put (dir);
+#ifdef CONFIG_DEVFS_DEBUG
+    spin_lock (&stat_lock);
+    stat_num_bytes += linklength + 1;
+    spin_unlock (&stat_lock);
+#endif
     if (handle != NULL) *handle = de;
     return 0;
 }   /*  End Function devfs_do_symlink  */
@@ -1593,50 +1782,52 @@
 
 devfs_handle_t devfs_mk_dir (devfs_handle_t dir, const char *name, void *info)
 {
-    int is_new;
-    struct devfs_entry *de;
+    int err;
+    struct devfs_entry *de, *old;
 
     if (name == NULL)
     {
 	printk ("%s: devfs_mk_dir(): NULL name pointer\n", DEVFS_NAME);
 	return NULL;
     }
-    de = search_for_entry (dir, name, strlen (name), TRUE, TRUE, &is_new,
-			   FALSE);
-    if (de == NULL)
+    if ( ( de = _devfs_prepare_leaf (&dir, name, MODE_DIR) ) == NULL )
     {
-	printk ("%s: devfs_mk_dir(): could not create entry: \"%s\"\n",
+	printk ("%s: devfs_mk_dir(%s): could not prepare leaf\n",
 		DEVFS_NAME, name);
 	return NULL;
     }
-    if (!S_ISDIR (de->mode) && de->registered)
+    de->info = info;
+    if ( ( err = _devfs_append_entry (dir, de, FALSE, &old) ) != 0 )
     {
-	printk ("%s: devfs_mk_dir(): existing non-directory entry: \"%s\"\n",
-		DEVFS_NAME, name);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,1)
+	if (old)
+	{
+	    printk("%s: devfs_mk_dir(%s): using old entry in dir: %p \"%s\"\n",
+		   DEVFS_NAME, name, dir, dir->name);
+	    old->vfs_created = FALSE;
+	    devfs_put (dir);
+	    return old;
+	}
+#endif
+	printk ("%s: devfs_mk_dir(%s): could not append to dir: %p \"%s\", err: %d\n",
+		DEVFS_NAME, name, dir, dir->name, err);
+	devfs_put (old);
+	devfs_put (dir);
 	return NULL;
     }
 #ifdef CONFIG_DEVFS_DEBUG
     if (devfs_debug & DEBUG_REGISTER)
-	printk ("%s: devfs_mk_dir(%s): de: %p %s\n",
-		DEVFS_NAME, name, de, is_new ? "new" : "existing");
+	printk ("%s: devfs_mk_dir(%s): de: %p dir: %p \"%s\"\n",
+		DEVFS_NAME, name, de, dir, dir->name);
 #endif
-    if (!S_ISDIR (de->mode) && !is_new)
-    {
-	/*  Transmogrifying an old entry  */
-	de->u.dir.first = NULL;
-	de->u.dir.last = NULL;
-    }
-    de->mode = S_IFDIR | S_IRUGO | S_IXUGO;
-    de->info = info;
-    if (!de->registered) de->u.dir.num_removable = 0;
-    de->hide = FALSE;
-    de->registered = TRUE;
+    devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, 0);
+    devfs_put (dir);
     return de;
 }   /*  End Function devfs_mk_dir  */
 
 
 /**
- *	devfs_find_handle - Find the handle of a devfs entry.
+ *	devfs_get_handle - Find the handle of a devfs entry.
  *	@dir: The handle to the parent devfs directory entry. If this is %NULL the
  *		name is relative to the root of the devfs.
  *	@name: The name of the entry.
@@ -1648,20 +1839,31 @@
  *		traversed. Symlinks pointing out of the devfs namespace will cause a
  *		failure. Symlink traversal consumes stack space.
  *
- *	Returns a handle which may later be used in a call to devfs_unregister(),
- *	devfs_get_flags(), or devfs_set_flags(). On failure %NULL is returned.
+ *	Returns a handle which may later be used in a call to
+ *	devfs_unregister(), devfs_get_flags(), or devfs_set_flags(). A
+ *	subsequent devfs_put() is required to decrement the refcount.
+ *	On failure %NULL is returned.
  */
 
+devfs_handle_t devfs_get_handle (devfs_handle_t dir, const char *name,
+				 unsigned int major, unsigned int minor,
+				 char type, int traverse_symlinks)
+{
+    if ( (name != NULL) && (name[0] == '\0') ) name = NULL;
+    return find_entry (dir, name, major, minor, type, traverse_symlinks);
+}   /*  End Function devfs_get_handle  */
+
+
+/*  Compatibility function. Will be removed in sometime in 2.5  */
+
 devfs_handle_t devfs_find_handle (devfs_handle_t dir, const char *name,
 				  unsigned int major, unsigned int minor,
 				  char type, int traverse_symlinks)
 {
     devfs_handle_t de;
 
-    if ( (name != NULL) && (name[0] == '\0') ) name = NULL;
-    de = find_entry (dir, name, 0, major, minor, type, traverse_symlinks);
-    if (de == NULL) return NULL;
-    if (!de->registered) return NULL;
+    de = devfs_get_handle (dir, name, major, minor, type, traverse_symlinks);
+    devfs_put (de);
     return de;
 }   /*  End Function devfs_find_handle  */
 
@@ -1679,7 +1881,6 @@
     unsigned int fl = 0;
 
     if (de == NULL) return -EINVAL;
-    if (!de->registered) return -ENODEV;
     if (de->hide) fl |= DEVFS_FL_HIDE;
     if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) )
     {
@@ -1703,7 +1904,6 @@
 int devfs_set_flags (devfs_handle_t de, unsigned int flags)
 {
     if (de == NULL) return -EINVAL;
-    if (!de->registered) return -ENODEV;
 #ifdef CONFIG_DEVFS_DEBUG
     if (devfs_debug & DEBUG_SET_FLAGS)
 	printk ("%s: devfs_set_flags(): de->name: \"%s\"\n",
@@ -1714,16 +1914,6 @@
     {
 	de->u.fcb.auto_owner = (flags & DEVFS_FL_AUTO_OWNER) ? TRUE : FALSE;
 	de->u.fcb.aopen_notify = (flags & DEVFS_FL_AOPEN_NOTIFY) ? TRUE:FALSE;
-	if ( de->u.fcb.removable && !(flags & DEVFS_FL_REMOVABLE) )
-	{
-	    de->u.fcb.removable = FALSE;
-	    --de->parent->u.dir.num_removable;
-	}
-	else if ( !de->u.fcb.removable && (flags & DEVFS_FL_REMOVABLE) )
-	{
-	    de->u.fcb.removable = TRUE;
-	    ++de->parent->u.dir.num_removable;
-	}
     }
     return 0;
 }   /*  End Function devfs_set_flags  */
@@ -1742,7 +1932,6 @@
 		       unsigned int *minor)
 {
     if (de == NULL) return -EINVAL;
-    if (!de->registered) return -ENODEV;
     if ( S_ISDIR (de->mode) ) return -EISDIR;
     if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) ) return -EINVAL;
     if (major != NULL) *major = de->u.fcb.u.device.major;
@@ -1762,7 +1951,7 @@
 {
     if (!inode || !inode->i_sb) return NULL;
     if (inode->i_sb->s_magic != DEVFS_SUPER_MAGIC) return NULL;
-    return get_devfs_entry_from_vfs_inode (inode, TRUE);
+    return get_devfs_entry_from_vfs_inode (inode);
 }   /*  End Function devfs_get_handle_from_inode  */
 
 
@@ -1780,19 +1969,20 @@
 int devfs_generate_path (devfs_handle_t de, char *path, int buflen)
 {
     int pos;
+#define NAMEOF(de) ( (de)->mode ? (de)->name : (de)->u.name )
 
     if (de == NULL) return -EINVAL;
     if (de->namelen >= buflen) return -ENAMETOOLONG; /*  Must be first       */
     path[buflen - 1] = '\0';
     if (de->parent == NULL) return buflen - 1;       /*  Don't prepend root  */
     pos = buflen - de->namelen - 1;
-    memcpy (path + pos, de->name, de->namelen);
+    memcpy (path + pos, NAMEOF (de), de->namelen);
     for (de = de->parent; de->parent != NULL; de = de->parent)
     {
 	if (pos - de->namelen - 1 < 0) return -ENAMETOOLONG;
 	path[--pos] = '/';
 	pos -= de->namelen;
-	memcpy (path + pos, de->name, de->namelen);
+	memcpy (path + pos, NAMEOF (de), de->namelen);
     }
     return pos;
 }   /*  End Function devfs_generate_path  */
@@ -1803,19 +1993,55 @@
  *	@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.
  */
 
 void *devfs_get_ops (devfs_handle_t de)
 {
+    struct module *owner;
+
     if (de == NULL) return NULL;
-    if (!de->registered) return NULL;
-    if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) )
-	return de->u.fcb.ops;
-    return NULL;
+    if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) && !S_ISREG (de->mode) )
+	return NULL;
+    if (de->u.fcb.ops == NULL) return NULL;
+    read_lock (&de->parent->u.dir.lock);  /*  Prevent module from unloading  */
+    if (de->next == de) owner = NULL;     /*  Ops pointer is already stale   */
+    else if ( S_ISCHR (de->mode) || S_ISREG (de->mode) )
+	owner = ( (struct file_operations *) de->u.fcb.ops )->owner;
+    else owner = ( (struct block_device_operations *) de->u.fcb.ops )->owner;
+    if ( (de->next == de) || !try_inc_mod_count (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 de->u.fcb.ops;
 }   /*  End Function devfs_get_ops  */
 
 
 /**
+ *	devfs_put_ops - Put the device operations for a devfs entry.
+ *	@de: The handle to the device entry.
+ *
+ *	The use count for the module owning the operations will be decremented.
+ */
+
+void devfs_put_ops (devfs_handle_t de)
+{
+    struct module *owner;
+
+    if (de == NULL) return;
+    if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) && !S_ISREG (de->mode) )
+	return;
+    if (de->u.fcb.ops == NULL) return;
+    if ( S_ISCHR (de->mode) || S_ISREG (de->mode) )
+	owner = ( (struct file_operations *) de->u.fcb.ops )->owner;
+    else owner = ( (struct block_device_operations *) de->u.fcb.ops )->owner;
+    if (owner) __MOD_DEC_USE_COUNT (owner);
+}   /*  End Function devfs_put_ops  */
+
+
+/**
  *	devfs_set_file_size - Set the file size for a devfs regular file.
  *	@de: The handle to the device entry.
  *	@size: The new file size.
@@ -1826,7 +2052,6 @@
 int devfs_set_file_size (devfs_handle_t de, unsigned long size)
 {
     if (de == NULL) return -EINVAL;
-    if (!de->registered) return -EINVAL;
     if ( !S_ISREG (de->mode) ) return -EINVAL;
     if (de->u.fcb.u.file.size == size) return 0;
     de->u.fcb.u.file.size = size;
@@ -1846,7 +2071,6 @@
 void *devfs_get_info (devfs_handle_t de)
 {
     if (de == NULL) return NULL;
-    if (!de->registered) return NULL;
     return de->info;
 }   /*  End Function devfs_get_info  */
 
@@ -1861,7 +2085,6 @@
 int devfs_set_info (devfs_handle_t de, void *info)
 {
     if (de == NULL) return -EINVAL;
-    if (!de->registered) return -EINVAL;
     de->info = info;
     return 0;
 }   /*  End Function devfs_set_info  */
@@ -1876,7 +2099,6 @@
 devfs_handle_t devfs_get_parent (devfs_handle_t de)
 {
     if (de == NULL) return NULL;
-    if (!de->registered) return NULL;
     return de->parent;
 }   /*  End Function devfs_get_parent  */
 
@@ -1891,7 +2113,6 @@
 devfs_handle_t devfs_get_first_child (devfs_handle_t de)
 {
     if (de == NULL) return NULL;
-    if (!de->registered) return NULL;
     if ( !S_ISDIR (de->mode) ) return NULL;
     return de->u.dir.first;
 }   /*  End Function devfs_get_first_child  */
@@ -1907,7 +2128,6 @@
 devfs_handle_t devfs_get_next_sibling (devfs_handle_t de)
 {
     if (de == NULL) return NULL;
-    if (!de->registered) return NULL;
     return de->next;
 }   /*  End Function devfs_get_next_sibling  */
 
@@ -1961,7 +2181,6 @@
 const char *devfs_get_name (devfs_handle_t de, unsigned int *namelen)
 {
     if (de == NULL) return NULL;
-    if (!de->registered) return NULL;
     if (namelen != NULL) *namelen = de->namelen;
     return de->name;
 }   /*  End Function devfs_get_name  */
@@ -2057,8 +2276,10 @@
 	{"dmod",      DEBUG_MODULE_LOAD,  &devfs_debug_init},
 	{"dreg",      DEBUG_REGISTER,     &devfs_debug_init},
 	{"dunreg",    DEBUG_UNREGISTER,   &devfs_debug_init},
+	{"dfree",     DEBUG_FREE,         &devfs_debug_init},
 	{"diget",     DEBUG_I_GET,        &devfs_debug_init},
 	{"dchange",   DEBUG_SET_FLAGS,    &devfs_debug_init},
+	{"dsread",    DEBUG_S_READ,       &devfs_debug_init},
 	{"dichange",  DEBUG_I_CHANGE,     &devfs_debug_init},
 	{"dimknod",   DEBUG_I_MKNOD,      &devfs_debug_init},
 	{"dilookup",  DEBUG_I_LOOKUP,     &devfs_debug_init},
@@ -2102,10 +2323,12 @@
 
 __setup("devfs=", devfs_setup);
 
+EXPORT_SYMBOL(devfs_put);
 EXPORT_SYMBOL(devfs_register);
 EXPORT_SYMBOL(devfs_unregister);
 EXPORT_SYMBOL(devfs_mk_symlink);
 EXPORT_SYMBOL(devfs_mk_dir);
+EXPORT_SYMBOL(devfs_get_handle);
 EXPORT_SYMBOL(devfs_find_handle);
 EXPORT_SYMBOL(devfs_get_flags);
 EXPORT_SYMBOL(devfs_set_flags);
@@ -2129,34 +2352,31 @@
 
 
 /**
- *	try_modload - Notify devfsd of an inode lookup.
+ *	try_modload - Notify devfsd of an inode lookup by a non-devfsd process.
  *	@parent: The parent devfs entry.
  *	@fs_info: The filesystem info.
  *	@name: The device name.
  *	@namelen: The number of characters in @name.
- *	@buf: A working area that will be used. This must not go out of scope until
- *		devfsd is idle again.
+ *	@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.
  */
 
 static int try_modload (struct devfs_entry *parent, struct fs_info *fs_info,
 			const char *name, unsigned namelen,
-			char buf[STRING_LENGTH])
+			struct devfs_entry *buf)
 {
-    int pos = STRING_LENGTH - namelen - 1;
-
     if ( !( fs_info->devfsd_event_mask & (1 << DEVFSD_NOTIFY_LOOKUP) ) )
 	return -ENOENT;
     if ( is_devfsd_or_child (fs_info) ) return -ENOENT;
-    if (namelen >= STRING_LENGTH - 1) return -ENAMETOOLONG;
-    memcpy (buf + pos, name, namelen);
-    buf[STRING_LENGTH - 1] = '\0';
-    if (parent->parent != NULL) pos = devfs_generate_path (parent, buf, pos);
-    if (pos < 0) return pos;
-    buf[STRING_LENGTH - namelen - 2] = '/';
-    if ( !devfsd_notify_one (buf + pos, DEVFSD_NOTIFY_LOOKUP, 0,
-			     current->euid, current->egid, fs_info) )
+    memset (buf, 0, sizeof *buf);
+    atomic_set (&buf->refcount, 1);
+    buf->parent = parent;
+    buf->namelen = namelen;
+    buf->u.name = name;
+    if ( !devfsd_notify_de (buf, DEVFSD_NOTIFY_LOOKUP, 0,
+			    current->euid, current->egid, fs_info, 0) )
 	return -ENOENT;
     /*  Possible success  */
     return 0;
@@ -2173,14 +2393,17 @@
 static int check_disc_changed (struct devfs_entry *de)
 {
     int tmp;
+    int retval = 0;
     kdev_t dev = MKDEV (de->u.fcb.u.device.major, de->u.fcb.u.device.minor);
-    struct block_device_operations *bdops = de->u.fcb.ops;
+    struct block_device_operations *bdops;
     extern int warn_no_part;
 
     if ( !S_ISBLK (de->mode) ) return 0;
-    if (bdops == NULL) return 0;
-    if (bdops->check_media_change == NULL) return 0;
-    if ( !bdops->check_media_change (dev) ) return 0;
+    bdops = devfs_get_ops (de);
+    if (!bdops) return 0;
+    if (bdops->check_media_change == NULL) goto out;
+    if ( !bdops->check_media_change (dev) ) goto out;
+    retval = 1;
     printk ( KERN_DEBUG "VFS: Disk change detected on device %s\n",
 	     kdevname (dev) );
     if (invalidate_device(dev, 0))
@@ -2190,7 +2413,9 @@
     warn_no_part = 0;
     if (bdops->revalidate) bdops->revalidate (dev);
     warn_no_part = tmp;
-    return 1;
+out:
+    devfs_put_ops (de);
+    return retval;
 }   /*  End Function check_disc_changed  */
 
 
@@ -2206,7 +2431,6 @@
     if (dir->u.dir.num_removable < 1) return;
     for (de = dir->u.dir.first; de != NULL; de = de->next)
     {
-	if (!de->registered) continue;
 	if ( !S_ISBLK (de->mode) ) continue;
 	if (!de->u.fcb.removable) continue;
 	check_disc_changed (de);
@@ -2229,7 +2453,6 @@
 
     for (de = dir->u.dir.first; de != NULL; de = de->next)
     {
-	if (!de->registered) continue;
 	if ( !S_ISBLK (de->mode) ) continue;
 	if (!de->u.fcb.removable) continue;
 	if (strcmp (de->name, "disc") == 0) return check_disc_changed (de);
@@ -2258,7 +2481,7 @@
     struct inode *inode = dentry->d_inode;
     struct fs_info *fs_info = inode->i_sb->u.generic_sbp;
 
-    de = get_devfs_entry_from_vfs_inode (inode, TRUE);
+    de = get_devfs_entry_from_vfs_inode (inode);
     if (de == NULL) return -ENODEV;
     retval = inode_change_ok (inode, iattr);
     if (retval != 0) return retval;
@@ -2276,22 +2499,27 @@
 #endif
     /*  Inode is not on hash chains, thus must save permissions here rather
 	than in a write_inode() method  */
-    de->inode.mode = inode->i_mode;
-    de->inode.uid = inode->i_uid;
-    de->inode.gid = inode->i_gid;
+    if ( ( !S_ISREG (inode->i_mode) && !S_ISCHR (inode->i_mode) &&
+	   !S_ISBLK (inode->i_mode) ) || !de->u.fcb.auto_owner )
+    {
+	de->mode = inode->i_mode;
+	de->inode.uid = inode->i_uid;
+	de->inode.gid = inode->i_gid;
+    }
     de->inode.atime = inode->i_atime;
     de->inode.mtime = inode->i_mtime;
     de->inode.ctime = inode->i_ctime;
-    if ( iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID) )
-	devfsd_notify_one (de, DEVFSD_NOTIFY_CHANGE, inode->i_mode,
-			   inode->i_uid, inode->i_gid, fs_info);
+    if ( ( iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID) ) &&
+	 !is_devfsd_or_child (fs_info) )
+	devfsd_notify_de (de, DEVFSD_NOTIFY_CHANGE, inode->i_mode,
+			  inode->i_uid, inode->i_gid, fs_info, 0);
     return 0;
 }   /*  End Function devfs_notify_change  */
 
 static int devfs_statfs (struct super_block *sb, struct statfs *buf)
 {
     buf->f_type = DEVFS_SUPER_MAGIC;
-    buf->f_bsize = PAGE_SIZE / sizeof (long);
+    buf->f_bsize = FAKE_BLOCK_SIZE;
     buf->f_bfree = 0;
     buf->f_bavail = 0;
     buf->f_ffree = 0;
@@ -2299,11 +2527,10 @@
     return 0;
 }   /*  End Function devfs_statfs  */
 
-static void devfs_clear_inode(struct inode *inode)
+static void devfs_clear_inode (struct inode *inode)
 {
-	if (S_ISBLK(inode->i_mode))
-		bdput(inode->i_bdev);
-}
+    if ( S_ISBLK (inode->i_mode) ) bdput (inode->i_bdev);
+}   /*  End Function devfs_clear_inode  */
 
 static struct super_operations devfs_sops =
 { 
@@ -2319,32 +2546,37 @@
  *	@de: The devfs inode.
  *	@dentry: The dentry to register with the devfs inode.
  *
- *	Returns the inode on success, else %NULL.
+ *	Returns the inode on success, else %NULL. An implicit devfs_get() is
+ *       performed if the inode is created.
  */
 
 static struct inode *get_vfs_inode (struct super_block *sb,
 				    struct devfs_entry *de,
 				    struct dentry *dentry)
 {
+    int is_fcb = FALSE;
     struct inode *inode;
 
-    if (de->inode.dentry != NULL)
-    {
-	printk ("%s: get_vfs_inode(%u): old de->inode.dentry: %p \"%s\"  new dentry: %p \"%s\"\n",
-		DEVFS_NAME, de->inode.ino,
-		de->inode.dentry, de->inode.dentry->d_name.name,
-		dentry, dentry->d_name.name);
-	printk ("  old inode: %p\n", de->inode.dentry->d_inode);
-	return NULL;
-    }
+    if (de->prev == de) return NULL;  /*  Quick check to see if unhooked  */
     if ( ( inode = new_inode (sb) ) == NULL )
     {
 	printk ("%s: get_vfs_inode(%s): new_inode() failed, de: %p\n",
 		DEVFS_NAME, de->name, de);
 	return NULL;
     }
-    de->inode.dentry = dentry;
-    inode->u.generic_ip = de;
+    if (de->parent)
+    {
+	read_lock (&de->parent->u.dir.lock);
+	if (de->prev != de) de->inode.dentry = dentry; /*      Not unhooked  */
+	read_unlock (&de->parent->u.dir.lock);
+    }
+    else de->inode.dentry = dentry;             /*  Root: no locking needed  */
+    if (de->inode.dentry != dentry)
+    {   /*  Must have been unhooked  */
+	iput (inode);
+	return NULL;
+    }
+    inode->u.generic_ip = devfs_get (de);
     inode->i_ino = de->inode.ino;
 #ifdef CONFIG_DEVFS_DEBUG
     if (devfs_debug & DEBUG_I_GET)
@@ -2352,41 +2584,49 @@
 		DEVFS_NAME, (int) inode->i_ino, inode, de);
 #endif
     inode->i_blocks = 0;
-    inode->i_blksize = 1024;
+    inode->i_blksize = FAKE_BLOCK_SIZE;
     inode->i_op = &devfs_iops;
     inode->i_fop = &devfs_fops;
     inode->i_rdev = NODEV;
-    if ( S_ISCHR (de->inode.mode) )
+    if ( S_ISCHR (de->mode) )
     {
 	inode->i_rdev = MKDEV (de->u.fcb.u.device.major,
 			       de->u.fcb.u.device.minor);
-	inode->i_cdev = cdget (kdev_t_to_nr(inode->i_rdev));
+	inode->i_cdev = cdget ( kdev_t_to_nr (inode->i_rdev) );
+	is_fcb = TRUE;
     }
-    else if ( S_ISBLK (de->inode.mode) )
+    else if ( S_ISBLK (de->mode) )
     {
 	inode->i_rdev = MKDEV (de->u.fcb.u.device.major,
 			       de->u.fcb.u.device.minor);
-	if (bd_acquire(inode) == 0)
+	if (bd_acquire (inode) == 0)
 	{
 	    if (!inode->i_bdev->bd_op && de->u.fcb.ops)
 		inode->i_bdev->bd_op = de->u.fcb.ops;
 	}
 	else printk ("%s: get_vfs_inode(%d): no block device from bdget()\n",
 		     DEVFS_NAME, (int) inode->i_ino);
+	is_fcb = TRUE;
     }
-    else if ( S_ISFIFO (de->inode.mode) ) inode->i_fop = &def_fifo_fops;
-    else if ( S_ISREG (de->inode.mode) ) inode->i_size = de->u.fcb.u.file.size;
-    else if ( S_ISDIR (de->inode.mode) )
+    else if ( S_ISFIFO (de->mode) ) inode->i_fop = &def_fifo_fops;
+    else if ( S_ISREG (de->mode) )
+    {
+	inode->i_size = de->u.fcb.u.file.size;
+	is_fcb = TRUE;
+    }
+    else if ( S_ISDIR (de->mode) )
     {
 	inode->i_op = &devfs_dir_iops;
     	inode->i_fop = &devfs_dir_fops;
     }
-    else if ( S_ISLNK (de->inode.mode) )
+    else if ( S_ISLNK (de->mode) )
     {
 	inode->i_op = &devfs_symlink_iops;
 	inode->i_size = de->u.symlink.length;
     }
-    inode->i_mode = de->inode.mode;
+    if (is_fcb && de->u.fcb.auto_owner)
+	inode->i_mode = (de->mode & S_IFMT) | S_IRUGO | S_IWUGO;
+    else inode->i_mode = de->mode;
     inode->i_uid = de->inode.uid;
     inode->i_gid = de->inode.gid;
     inode->i_atime = de->inode.atime;
@@ -2409,11 +2649,11 @@
     int err, count;
     int stored = 0;
     struct fs_info *fs_info;
-    struct devfs_entry *parent, *de;
+    struct devfs_entry *parent, *de, *next = NULL;
     struct inode *inode = file->f_dentry->d_inode;
 
     fs_info = inode->i_sb->u.generic_sbp;
-    parent = get_devfs_entry_from_vfs_inode (file->f_dentry->d_inode, TRUE);
+    parent = get_devfs_entry_from_vfs_inode (file->f_dentry->d_inode);
     if ( (long) file->f_pos < 0 ) return -EINVAL;
 #ifdef CONFIG_DEVFS_DEBUG
     if (devfs_debug & DEBUG_F_READDIR)
@@ -2441,19 +2681,32 @@
       default:
 	/*  Skip entries  */
 	count = file->f_pos - 2;
-	for (de = parent->u.dir.first; (de != NULL) && (count > 0);
-	     de = de->next)
+	read_lock (&parent->u.dir.lock);
+	for (de = parent->u.dir.first; de && (count > 0); de = de->next)
 	    if ( !IS_HIDDEN (de) ) --count;
+	devfs_get (de);
+	read_unlock (&parent->u.dir.lock);
 	/*  Now add all remaining entries  */
-	for (; de != NULL; de = de->next)
+	while (de)
 	{
-	    if ( IS_HIDDEN (de) ) continue;
-	    err = (*filldir) (dirent, de->name, de->namelen,
-			      file->f_pos, de->inode.ino, de->mode >> 12);
+	    if ( IS_HIDDEN (de) ) err = 0;
+	    else
+	    {
+		err = (*filldir) (dirent, de->name, de->namelen,
+				  file->f_pos, de->inode.ino, de->mode >> 12);
+		if (err >= 0)
+		{
+		    file->f_pos++;
+		    ++stored;
+		}
+	    }
+	    read_lock (&parent->u.dir.lock);
+	    next = devfs_get (de->next);
+	    read_unlock (&parent->u.dir.lock);
+	    devfs_put (de);
+	    de = next;
 	    if (err == -EINVAL) break;
 	    if (err < 0) return err;
-	    file->f_pos++;
-	    ++stored;
 	}
 	break;
     }
@@ -2467,14 +2720,9 @@
     struct devfs_entry *de;
     struct fs_info *fs_info = inode->i_sb->u.generic_sbp;
 
-    lock_kernel ();
-    de = get_devfs_entry_from_vfs_inode (inode, TRUE);
-    err = -ENODEV;
-    if (de == NULL)
-	goto out;
-    err = 0;
-    if ( S_ISDIR (de->mode) )
-	goto out;
+    de = get_devfs_entry_from_vfs_inode (inode);
+    if (de == NULL) return -ENODEV;
+    if ( S_ISDIR (de->mode) ) return 0;
     df = &de->u.fcb;
     file->private_data = de->info;
     if ( S_ISBLK (inode->i_mode) )
@@ -2482,7 +2730,7 @@
 	file->f_op = &def_blk_fops;
 	if (df->ops) inode->i_bdev->bd_op = df->ops;
     }
-    else file->f_op = fops_get ( (struct file_operations*) df->ops );
+    else file->f_op = fops_get ( (struct file_operations *) df->ops );
     if (file->f_op)
 	err = file->f_op->open ? (*file->f_op->open) (inode, file) : 0;
     else
@@ -2491,39 +2739,33 @@
 	if ( S_ISCHR (inode->i_mode) ) err = chrdev_open (inode, file);
 	else err = -ENODEV;
     }
-    if (err < 0) goto out;
+    if (err < 0) return err;
     /*  Open was successful  */
-    err = 0;
-    if (df->open) goto out;
+    if (df->open) return 0;
     df->open = TRUE;  /*  This is the first open  */
     if (df->auto_owner)
     {
-	/*  Change the ownership/protection  */
-	de->inode.mode = (de->inode.mode & ~S_IALLUGO) |(de->mode & S_IRWXUGO);
-	de->inode.uid = current->euid;
-	de->inode.gid = current->egid;
-	inode->i_mode = de->inode.mode;
-	inode->i_uid = de->inode.uid;
-	inode->i_gid = de->inode.gid;
-    }
-    if (df->aopen_notify)
-	devfsd_notify_one (de, DEVFSD_NOTIFY_ASYNC_OPEN, inode->i_mode,
-			   current->euid, current->egid, fs_info);
-out:
-    unlock_kernel ();
-    return err;
+	/*  Change the ownership/protection to what driver specified  */
+	inode->i_mode = de->mode;
+	inode->i_uid = current->euid;
+	inode->i_gid = current->egid;
+    }
+    if ( df->aopen_notify && !is_devfsd_or_child (fs_info) )
+	devfsd_notify_de (de, DEVFSD_NOTIFY_ASYNC_OPEN, inode->i_mode,
+			  current->euid, current->egid, fs_info, 0);
+    return 0;
 }   /*  End Function devfs_open  */
 
 static struct file_operations devfs_fops =
 {
-    open: devfs_open,
+    open:    devfs_open,
 };
 
 static struct file_operations devfs_dir_fops =
 {
-    read: generic_read_dir,
+    read:    generic_read_dir,
     readdir: devfs_readdir,
-    open: devfs_open,
+    open:    devfs_open,
 };
 
 
@@ -2556,16 +2798,18 @@
 {
     struct devfs_entry *de;
 
-    lock_kernel ();
-    de = get_devfs_entry_from_vfs_inode (inode, FALSE);
+    de = get_devfs_entry_from_vfs_inode (inode);
 #ifdef CONFIG_DEVFS_DEBUG
     if (devfs_debug & DEBUG_D_IPUT)
 	printk ("%s: d_iput(): dentry: %p inode: %p de: %p  de->dentry: %p\n",
 		DEVFS_NAME, dentry, inode, de, de->inode.dentry);
 #endif
-    if (de->inode.dentry == dentry) de->inode.dentry = NULL;
-    unlock_kernel ();
+    if ( de->inode.dentry && (de->inode.dentry != dentry) )
+	OOPS ("%s: d_iput(%s): de: %p dentry: %p de->dentry: %p\n",
+	      DEVFS_NAME, de->name, de, dentry, de->inode.dentry);
+    de->inode.dentry = NULL;
     iput (inode);
+    devfs_put (de);
 }   /*  End Function devfs_d_iput  */
 
 static int devfs_d_delete (struct dentry *dentry);
@@ -2610,7 +2854,7 @@
 	return 1;
     }
     fs_info = inode->i_sb->u.generic_sbp;
-    de = get_devfs_entry_from_vfs_inode (inode, TRUE);
+    de = get_devfs_entry_from_vfs_inode (inode);
 #ifdef CONFIG_DEVFS_DEBUG
     if (devfs_debug & DEBUG_D_DELETE)
 	printk ("%s: d_delete(): dentry: %p  inode: %p  devfs_entry: %p\n",
@@ -2622,14 +2866,11 @@
     if (!de->u.fcb.open) return 0;
     de->u.fcb.open = FALSE;
     if (de->u.fcb.aopen_notify)
-	devfsd_notify_one (de, DEVFSD_NOTIFY_CLOSE, inode->i_mode,
-			   current->euid, current->egid, fs_info);
+	devfsd_notify_de (de, DEVFSD_NOTIFY_CLOSE, inode->i_mode,
+			  current->euid, current->egid, fs_info, 1);
     if (!de->u.fcb.auto_owner) return 0;
     /*  Change the ownership/protection back  */
-    de->inode.mode = (de->inode.mode & ~S_IALLUGO) | S_IRUGO | S_IWUGO;
-    de->inode.uid = de->u.fcb.default_uid;
-    de->inode.gid = de->u.fcb.default_gid;
-    inode->i_mode = de->inode.mode;
+    inode->i_mode = (de->mode & S_IFMT) | S_IRUGO | S_IWUGO;
     inode->i_uid = de->inode.uid;
     inode->i_gid = de->inode.gid;
     return 0;
@@ -2637,59 +2878,38 @@
 
 static int devfs_d_revalidate_wait (struct dentry *dentry, int flags)
 {
-    devfs_handle_t de = dentry->d_fsdata;
-    struct inode *dir;
-    struct fs_info *fs_info;
+    struct inode *dir = dentry->d_parent->d_inode;
+    struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
 
-    lock_kernel ();
-    dir = dentry->d_parent->d_inode;
-    fs_info = dir->i_sb->u.generic_sbp;
-    if (!de || de->registered)
+    if ( !dentry->d_inode && is_devfsd_or_child (fs_info) )
     {
-	if ( !dentry->d_inode && is_devfsd_or_child (fs_info) )
-	{
-	    struct inode *inode;
-
-#ifdef CONFIG_DEVFS_DEBUG
-	    char txt[STRING_LENGTH];
-
-	    memset (txt, 0, STRING_LENGTH);
-	    memcpy (txt, dentry->d_name.name,
-		    (dentry->d_name.len >= STRING_LENGTH) ?
-		    (STRING_LENGTH - 1) : dentry->d_name.len);
-	    if (devfs_debug & DEBUG_I_LOOKUP)
-		printk ("%s: d_revalidate(): dentry: %p name: \"%s\" by: \"%s\"\n",
-			DEVFS_NAME, dentry, txt, current->comm);
-#endif
-	    if (de == NULL)
-	    {
-		devfs_handle_t parent;
-
-		parent = get_devfs_entry_from_vfs_inode (dir, TRUE);
-		de = search_for_entry_in_dir (parent, dentry->d_name.name,
-					      dentry->d_name.len, FALSE);
-	    }
-	    if (de == NULL) goto out;
-	    /*  Create an inode, now that the driver information is available
-	     */
-	    if (de->no_persistence) update_devfs_inode_from_entry (de);
-	    else if (de->inode.ctime == 0) update_devfs_inode_from_entry (de);
-	    else de->inode.mode =
-		     (de->mode & ~S_IALLUGO) | (de->inode.mode & S_IALLUGO);
-	    if ( ( inode = get_vfs_inode (dir->i_sb, de, dentry) ) == NULL )
-		goto out;
-#ifdef CONFIG_DEVFS_DEBUG
-	    if (devfs_debug & DEBUG_I_LOOKUP)
-		printk ("%s: d_revalidate(): new VFS inode(%u): %p  devfs_entry: %p\n",
-			DEVFS_NAME, de->inode.ino, inode, de);
+	devfs_handle_t de;
+	devfs_handle_t parent = get_devfs_entry_from_vfs_inode (dir);
+	struct inode *inode;
+
+#ifdef CONFIG_DEVFS_DEBUG
+	if (devfs_debug & DEBUG_I_LOOKUP)
+	    printk ("%s: d_revalidate(%s): dentry: %p by: \"%s\"\n",
+		    DEVFS_NAME, dentry->d_name.name, dentry, current->comm);
+#endif
+	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;
+	/*  Create an inode, now that the driver information is available  */
+	inode = get_vfs_inode (dir->i_sb, de, dentry);
+	devfs_put (de);
+	if (!inode) return 1;
+#ifdef CONFIG_DEVFS_DEBUG
+	if (devfs_debug & DEBUG_I_LOOKUP)
+	    printk ("%s: d_revalidate(): new VFS inode(%u): %p  devfs_entry: %p\n",
+		    DEVFS_NAME, de->inode.ino, inode, de);
 #endif
-	    d_instantiate (dentry, inode);
-	    goto out;
-	}
+	d_instantiate (dentry, inode);
+	return 1;
     }
     if ( wait_for_devfsd_finished (fs_info) ) dentry->d_op = &devfs_dops;
-out:
-    unlock_kernel ();
     return 1;
 }   /*  End Function devfs_d_revalidate_wait  */
 
@@ -2698,70 +2918,63 @@
 
 static struct dentry *devfs_lookup (struct inode *dir, struct dentry *dentry)
 {
-    struct fs_info *fs_info;
+    struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
     struct devfs_entry *parent, *de;
     struct inode *inode;
-    char txt[STRING_LENGTH];
 
     /*  Set up the dentry operations before anything else, to ensure cleaning
 	up on any error  */
     dentry->d_op = &devfs_dops;
-    memset (txt, 0, STRING_LENGTH);
-    memcpy (txt, dentry->d_name.name,
-	    (dentry->d_name.len >= STRING_LENGTH) ?
-	    (STRING_LENGTH - 1) : dentry->d_name.len);
-    fs_info = dir->i_sb->u.generic_sbp;
     /*  First try to get the devfs entry for this directory  */
-    parent = get_devfs_entry_from_vfs_inode (dir, TRUE);
+    parent = get_devfs_entry_from_vfs_inode (dir);
 #ifdef CONFIG_DEVFS_DEBUG
     if (devfs_debug & DEBUG_I_LOOKUP)
 	printk ("%s: lookup(%s): dentry: %p parent: %p by: \"%s\"\n",
-		DEVFS_NAME, txt, dentry, parent, current->comm);
+		DEVFS_NAME, dentry->d_name.name, dentry, parent,current->comm);
 #endif
     if (parent == NULL) return ERR_PTR (-ENOENT);
-    /*  Try to reclaim an existing devfs entry  */
-    de = search_for_entry_in_dir (parent,
-				  dentry->d_name.name, dentry->d_name.len,
-				  FALSE);
-    if ( ( (de == NULL) || !de->registered ) &&
-	 (parent->u.dir.num_removable > 0) &&
+    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) && (parent->u.dir.num_removable > 0) &&
 	 get_removable_partition (parent, dentry->d_name.name,
 				  dentry->d_name.len) )
     {
-	if (de == NULL)
-	    de = search_for_entry_in_dir (parent, dentry->d_name.name,
-					  dentry->d_name.len, FALSE);
+	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) || !de->registered )
-    {
-	/*  Try with devfsd. For any kind of failure, leave a negative dentry
+    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, txt) < 0)
+			 dentry->d_name.name, dentry->d_name.len, &tmp) < 0)
 	{
 	    d_add (dentry, NULL);
 	    return NULL;
 	}
 	/*  devfsd claimed success  */
 	dentry->d_op = &devfs_wait_dops;
-	dentry->d_fsdata = de;
 	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  */
+	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;
-	if (de && !de->registered) return NULL;
-	if (de == NULL)
-	    de = search_for_entry_in_dir (parent, dentry->d_name.name,
-					  dentry->d_name.len, FALSE);
+	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;
 	/*  OK, there's an entry now, but no VFS inode yet  */
     }
@@ -2771,58 +2984,49 @@
 	d_add (dentry, NULL);  /*  Open the floodgates  */
     }
     /*  Create an inode, now that the driver information is available  */
-    if (de->no_persistence) update_devfs_inode_from_entry (de);
-    else if (de->inode.ctime == 0) update_devfs_inode_from_entry (de);
-    else de->inode.mode =
-	     (de->mode & ~S_IALLUGO) | (de->inode.mode & S_IALLUGO);
-    if ( ( inode = get_vfs_inode (dir->i_sb, de, dentry) ) == NULL )
-	return ERR_PTR (-ENOMEM);
+    inode = get_vfs_inode (dir->i_sb, de, dentry);
+    devfs_put (de);
+    if (!inode) return ERR_PTR (-ENOMEM);
 #ifdef CONFIG_DEVFS_DEBUG
     if (devfs_debug & DEBUG_I_LOOKUP)
 	printk ("%s: lookup(): new VFS inode(%u): %p  devfs_entry: %p\n",
 		DEVFS_NAME, de->inode.ino, inode, de);
 #endif
     d_instantiate (dentry, inode);
-    /*  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);
     if (dentry->d_op == &devfs_wait_dops)
-	devfs_d_revalidate_wait (dentry, 0);  /*  I might have to wait too  */
-    down (&dir->i_sem);          /*  Grab it again because them's the rules  */
+    {   /*  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  */
+    }
     return NULL;
 }   /*  End Function devfs_lookup  */
 
 static int devfs_unlink (struct inode *dir, struct dentry *dentry)
 {
+    int unhooked;
     struct devfs_entry *de;
     struct inode *inode = dentry->d_inode;
+    struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
 
 #ifdef CONFIG_DEVFS_DEBUG
-    char txt[STRING_LENGTH];
-
     if (devfs_debug & DEBUG_I_UNLINK)
-    {
-	memset (txt, 0, STRING_LENGTH);
-	memcpy (txt, dentry->d_name.name, dentry->d_name.len);
-	txt[STRING_LENGTH - 1] = '\0';
-	printk ("%s: unlink(%s)\n", DEVFS_NAME, txt);
-    }
+	printk ("%s: unlink(%s)\n", DEVFS_NAME, dentry->d_name.name);
 #endif
-
-    de = get_devfs_entry_from_vfs_inode (dentry->d_inode, TRUE);
+    de = get_devfs_entry_from_vfs_inode (inode);
     if (de == NULL) return -ENOENT;
-    devfsd_notify_one (de, DEVFSD_NOTIFY_DELETE, inode->i_mode,
-		       inode->i_uid, inode->i_gid, dir->i_sb->u.generic_sbp);
-    de->registered = FALSE;
-    de->hide = TRUE;
-    if ( S_ISLNK (de->mode) )
-    {
-	down_write (&symlink_rwsem);
-	if (de->u.symlink.linkname) kfree (de->u.symlink.linkname);
-	de->u.symlink.linkname = NULL;
-	up_write (&symlink_rwsem);
-    }
-    free_dentries (de);
+    if (!de->vfs_created) return -EPERM;
+    write_lock (&de->parent->u.dir.lock);
+    unhooked = _devfs_unhook (de);
+    write_unlock (&de->parent->u.dir.lock);
+    if (!unhooked) return -ENOENT;
+    if ( !is_devfsd_or_child (fs_info) )
+	devfsd_notify_de (de, DEVFSD_NOTIFY_DELETE, inode->i_mode,
+			  inode->i_uid, inode->i_gid, fs_info, 0);
+    free_dentry (de);
+    devfs_put (de);
     return 0;
 }   /*  End Function devfs_unlink  */
 
@@ -2830,13 +3034,12 @@
 			  const char *symname)
 {
     int err;
-    struct fs_info *fs_info;
+    struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
     struct devfs_entry *parent, *de;
     struct inode *inode;
 
-    fs_info = dir->i_sb->u.generic_sbp;
     /*  First try to get the devfs entry for this directory  */
-    parent = get_devfs_entry_from_vfs_inode (dir, TRUE);
+    parent = get_devfs_entry_from_vfs_inode (dir);
     if (parent == NULL) return -ENOENT;
     err = devfs_do_symlink (parent, dentry->d_name.name, DEVFS_FL_NONE,
 			    symname, &de, NULL);
@@ -2846,7 +3049,9 @@
 		DEVFS_NAME, err);
 #endif
     if (err < 0) return err;
-    de->inode.mode = de->mode;
+    de->vfs_created = TRUE;
+    de->inode.uid = current->euid;
+    de->inode.gid = current->egid;
     de->inode.atime = CURRENT_TIME;
     de->inode.mtime = CURRENT_TIME;
     de->inode.ctime = CURRENT_TIME;
@@ -2857,50 +3062,33 @@
 	printk ("%s: symlink(): new VFS inode(%u): %p  dentry: %p\n",
 		DEVFS_NAME, de->inode.ino, inode, dentry);
 #endif
-    de->hide = FALSE;
     d_instantiate (dentry, inode);
-    devfsd_notify_one (de, DEVFSD_NOTIFY_CREATE, inode->i_mode,
-		       inode->i_uid, inode->i_gid, fs_info);
+    if ( !is_devfsd_or_child (fs_info) )
+	devfsd_notify_de (de, DEVFSD_NOTIFY_CREATE, inode->i_mode,
+			  inode->i_uid, inode->i_gid, fs_info, 0);
     return 0;
 }   /*  End Function devfs_symlink  */
 
 static int devfs_mkdir (struct inode *dir, struct dentry *dentry, int mode)
 {
-    int is_new;
-    struct fs_info *fs_info;
+    int err;
+    struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
     struct devfs_entry *parent, *de;
     struct inode *inode;
 
-    mode = (mode & ~S_IFMT) | S_IFDIR;
-    fs_info = dir->i_sb->u.generic_sbp;
-    /*  First try to get the devfs entry for this directory  */
-    parent = get_devfs_entry_from_vfs_inode (dir, TRUE);
+    mode = (mode & ~S_IFMT) | S_IFDIR;  /*  VFS doesn't pass S_IFMT part  */
+    parent = get_devfs_entry_from_vfs_inode (dir);
     if (parent == NULL) return -ENOENT;
-    /*  Try to reclaim an existing devfs entry, create if there isn't one  */
-    de = search_for_entry (parent, dentry->d_name.name, dentry->d_name.len,
-			   FALSE, TRUE, &is_new, FALSE);
-    if (de == NULL) return -ENOMEM;
-    if (de->registered)
-    {
-	printk ("%s: mkdir(): existing entry\n", DEVFS_NAME);
-	return -EEXIST;
-    }
-    de->hide = FALSE;
-    if (!S_ISDIR (de->mode) && !is_new)
-    {
-	/*  Transmogrifying an old entry  */
-	de->u.dir.first = NULL;
-	de->u.dir.last = NULL;
-    }
-    de->mode = mode;
-    de->u.dir.num_removable = 0;
-    de->inode.mode = mode;
+    de = _devfs_alloc_entry (dentry->d_name.name, dentry->d_name.len, mode);
+    if (!de) return -ENOMEM;
+    de->vfs_created = TRUE;
+    if ( ( err = _devfs_append_entry (parent, de, FALSE, NULL) ) != 0 )
+	return err;
     de->inode.uid = current->euid;
     de->inode.gid = current->egid;
     de->inode.atime = CURRENT_TIME;
     de->inode.mtime = CURRENT_TIME;
     de->inode.ctime = CURRENT_TIME;
-    de->registered = TRUE;
     if ( ( inode = get_vfs_inode (dir->i_sb, de, dentry) ) == NULL )
 	return -ENOMEM;
 #ifdef CONFIG_DEVFS_DEBUG
@@ -2909,100 +3097,73 @@
 		DEVFS_NAME, de->inode.ino, inode, dentry);
 #endif
     d_instantiate (dentry, inode);
-    devfsd_notify_one (de, DEVFSD_NOTIFY_CREATE, inode->i_mode,
-		       inode->i_uid, inode->i_gid, fs_info);
+    if ( !is_devfsd_or_child (fs_info) )
+	devfsd_notify_de (de, DEVFSD_NOTIFY_CREATE, inode->i_mode,
+			  inode->i_uid, inode->i_gid, fs_info, 0);
     return 0;
 }   /*  End Function devfs_mkdir  */
 
 static int devfs_rmdir (struct inode *dir, struct dentry *dentry)
 {
-    int has_children = FALSE;
-    struct fs_info *fs_info;
-    struct devfs_entry *de, *child;
+    int err = 0;
+    struct devfs_entry *de;
+    struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
     struct inode *inode = dentry->d_inode;
 
     if (dir->i_sb->u.generic_sbp != inode->i_sb->u.generic_sbp) return -EINVAL;
-    fs_info = dir->i_sb->u.generic_sbp;
-    de = get_devfs_entry_from_vfs_inode (inode, TRUE);
+    de = get_devfs_entry_from_vfs_inode (inode);
     if (de == NULL) return -ENOENT;
     if ( !S_ISDIR (de->mode) ) return -ENOTDIR;
-    for (child = de->u.dir.first; child != NULL; child = child->next)
-    {
-	if (child->registered)
-	{
-	    has_children = TRUE;
-	    break;
-	}
-    }
-    if (has_children) return -ENOTEMPTY;
-    devfsd_notify_one (de, DEVFSD_NOTIFY_DELETE, inode->i_mode,
-		       inode->i_uid, inode->i_gid, fs_info);
-    de->hide = TRUE;
-    de->registered = FALSE;
-    free_dentries (de);
+    if (!de->vfs_created) return -EPERM;
+    /*  First ensure the directory is empty and will stay thay way  */
+    write_lock (&de->u.dir.lock);
+    de->u.dir.no_more_additions = TRUE;
+    if (de->u.dir.first) err = -ENOTEMPTY;
+    write_unlock (&de->u.dir.lock);
+    if (err) return err;
+    /*  Now unhook the directory from it's parent  */
+    write_lock (&de->parent->u.dir.lock);
+    if ( !_devfs_unhook (de) ) err = -ENOENT;
+    write_unlock (&de->parent->u.dir.lock);
+    if (err) return err;
+    if ( !is_devfsd_or_child (fs_info) )
+	devfsd_notify_de (de, DEVFSD_NOTIFY_DELETE, inode->i_mode,
+			  inode->i_uid, inode->i_gid, fs_info, 0);
+    free_dentry (de);
+    devfs_put (de);
     return 0;
 }   /*  End Function devfs_rmdir  */
 
 static int devfs_mknod (struct inode *dir, struct dentry *dentry, int mode,
 			int rdev)
 {
-    int is_new;
-    struct fs_info *fs_info;
+    int err;
+    struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
     struct devfs_entry *parent, *de;
     struct inode *inode;
 
 #ifdef CONFIG_DEVFS_DEBUG
-    char txt[STRING_LENGTH];
-
     if (devfs_debug & DEBUG_I_MKNOD)
-    {
-	memset (txt, 0, STRING_LENGTH);
-	memcpy (txt, dentry->d_name.name, dentry->d_name.len);
-	txt[STRING_LENGTH - 1] = '\0';
 	printk ("%s: mknod(%s): mode: 0%o  dev: %d\n",
-		DEVFS_NAME, txt, mode, rdev);
-    }
+		DEVFS_NAME, dentry->d_name.name, mode, rdev);
 #endif
-
-    fs_info = dir->i_sb->u.generic_sbp;
-    /*  First try to get the devfs entry for this directory  */
-    parent = get_devfs_entry_from_vfs_inode (dir, TRUE);
+    parent = get_devfs_entry_from_vfs_inode (dir);
     if (parent == NULL) return -ENOENT;
-    /*  Try to reclaim an existing devfs entry, create if there isn't one  */
-    de = search_for_entry (parent, dentry->d_name.name, dentry->d_name.len,
-			   FALSE, TRUE, &is_new, FALSE);
-    if (de == NULL) return -ENOMEM;
-    if (de->registered)
-    {
-	printk ("%s: mknod(): existing entry\n", DEVFS_NAME);
-	return -EEXIST;
-    }
-    de->info = NULL;
-    de->mode = mode;
+    de = _devfs_alloc_entry (dentry->d_name.name, dentry->d_name.len, mode);
+    if (!de) return -ENOMEM;
+    de->vfs_created = TRUE;
     if ( S_ISBLK (mode) || S_ISCHR (mode) )
     {
 	de->u.fcb.u.device.major = MAJOR (rdev);
 	de->u.fcb.u.device.minor = MINOR (rdev);
-	de->u.fcb.default_uid = current->euid;
-	de->u.fcb.default_gid = current->egid;
-	de->u.fcb.ops = NULL;
-	de->u.fcb.auto_owner = FALSE;
-	de->u.fcb.aopen_notify = FALSE;
-	de->u.fcb.open = FALSE;
-    }
-    else if ( S_ISFIFO (mode) )
-    {
-	de->u.fifo.uid = current->euid;
-	de->u.fifo.gid = current->egid;
     }
-    de->hide = FALSE;
-    de->inode.mode = mode;
+    if ( ( err = _devfs_append_entry (parent, de, FALSE, NULL) ) != 0 )
+	return err;
     de->inode.uid = current->euid;
     de->inode.gid = current->egid;
     de->inode.atime = CURRENT_TIME;
     de->inode.mtime = CURRENT_TIME;
     de->inode.ctime = CURRENT_TIME;
-    de->registered = TRUE;
     if ( ( inode = get_vfs_inode (dir->i_sb, de, dentry) ) == NULL )
 	return -ENOMEM;
 #ifdef CONFIG_DEVFS_DEBUG
@@ -3011,8 +3172,9 @@
 		DEVFS_NAME, de->inode.ino, inode, dentry);
 #endif
     d_instantiate (dentry, inode);
-    devfsd_notify_one (de, DEVFSD_NOTIFY_CREATE, inode->i_mode,
-		       inode->i_uid, inode->i_gid, fs_info);
+    if ( !is_devfsd_or_child (fs_info) )
+	devfsd_notify_de (de, DEVFSD_NOTIFY_CREATE, inode->i_mode,
+			  inode->i_uid, inode->i_gid, fs_info, 0);
     return 0;
 }   /*  End Function devfs_mknod  */
 
@@ -3021,12 +3183,9 @@
     int err;
     struct devfs_entry *de;
 
-    de = get_devfs_entry_from_vfs_inode (dentry->d_inode, TRUE);
+    de = get_devfs_entry_from_vfs_inode (dentry->d_inode);
     if (!de) return -ENODEV;
-    down_read (&symlink_rwsem);
-    err = de->registered ? vfs_readlink (dentry, buffer, buflen,
-					 de->u.symlink.linkname) : -ENODEV;
-    up_read (&symlink_rwsem);
+    err = vfs_readlink (dentry, buffer, buflen, de->u.symlink.linkname);
     return err;
 }   /*  End Function devfs_readlink  */
 
@@ -3034,25 +3193,10 @@
 {
     int err;
     struct devfs_entry *de;
-    char *copy;
 
-    de = get_devfs_entry_from_vfs_inode (dentry->d_inode, TRUE);
+    de = get_devfs_entry_from_vfs_inode (dentry->d_inode);
     if (!de) return -ENODEV;
-    down_read (&symlink_rwsem);
-    if (!de->registered)
-    {
-	up_read (&symlink_rwsem);
-	return -ENODEV;
-    }
-    copy = kmalloc (de->u.symlink.length + 1, GFP_KERNEL);
-    if (copy) memcpy (copy, de->u.symlink.linkname, de->u.symlink.length + 1);
-    up_read (&symlink_rwsem);
-    if (copy)
-    {
-	err = vfs_follow_link (nd, copy);
-	kfree (copy);
-    }
-    else err = -ENOMEM;
+    err = vfs_follow_link (nd, de->u.symlink.linkname);
     return err;
 }   /*  End Function devfs_follow_link  */
 
@@ -3084,7 +3228,7 @@
 {
     struct inode *root_inode = NULL;
 
-    if (get_root_entry () == NULL) goto out_no_root;
+    if (_devfs_get_root_entry () == NULL) goto out_no_root;
     atomic_set (&fs_info.devfsd_overrun_count, 0);
     init_waitqueue_head (&fs_info.devfsd_wait_queue);
     init_waitqueue_head (&fs_info.revalidate_wait_queue);
@@ -3099,7 +3243,7 @@
     sb->s_root = d_alloc_root (root_inode);
     if (!sb->s_root) goto out_no_root;
 #ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_DISABLED)
+    if (devfs_debug & DEBUG_S_READ)
 	printk ("%s: read super, made devfs ptr: %p\n",
 		DEVFS_NAME, sb->u.generic_sbp);
 #endif
@@ -3123,6 +3267,7 @@
     int done = FALSE;
     int ival;
     loff_t pos, devname_offset, tlen, rpos;
+    devfs_handle_t de;
     struct devfsd_buf_entry *entry;
     struct fs_info *fs_info = file->f_dentry->d_inode->i_sb->u.generic_sbp;
     struct devfsd_notify_struct *info = fs_info->devfsd_info;
@@ -3149,40 +3294,28 @@
 	    current->state = TASK_RUNNING;
 	    return -EINTR;
 	}
-	set_current_state(TASK_INTERRUPTIBLE);
+	set_current_state (TASK_INTERRUPTIBLE);
     }
     remove_wait_queue (&fs_info->devfsd_wait_queue, &wait);
     current->state = TASK_RUNNING;
     /*  Now play with the data  */
     ival = atomic_read (&fs_info->devfsd_overrun_count);
-    if (ival > 0) atomic_sub (ival, &fs_info->devfsd_overrun_count);
     info->overrun_count = ival;
-    entry = (struct devfsd_buf_entry *) fs_info->devfsd_buffer +
-	fs_info->devfsd_buf_out;
+    entry = fs_info->devfsd_first_event;
     info->type = entry->type;
     info->mode = entry->mode;
     info->uid = entry->uid;
     info->gid = entry->gid;
-    if (entry->type == DEVFSD_NOTIFY_LOOKUP)
+    de = entry->de;
+    if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) )
     {
-	info->namelen = strlen (entry->data);
-	pos = 0;
-	memcpy (info->devname, entry->data, info->namelen + 1);
-    }
-    else
-    {
-	devfs_handle_t de = entry->data;
-
-	if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) )
-	{
-	    info->major = de->u.fcb.u.device.major;
-	    info->minor = de->u.fcb.u.device.minor;
-	}
-	pos = devfs_generate_path (de, info->devname, DEVFS_PATHLEN);
-	if (pos < 0) return pos;
-	info->namelen = DEVFS_PATHLEN - pos - 1;
-	if (info->mode == 0) info->mode = de->mode;
+	info->major = de->u.fcb.u.device.major;
+	info->minor = de->u.fcb.u.device.minor;
     }
+    pos = devfs_generate_path (de, info->devname, DEVFS_PATHLEN);
+    if (pos < 0) return pos;
+    info->namelen = DEVFS_PATHLEN - pos - 1;
+    if (info->mode == 0) info->mode = de->mode;
     devname_offset = info->devname - (char *) info;
     rpos = *ppos;
     if (rpos < devname_offset)
@@ -3214,10 +3347,19 @@
     tlen = rpos - *ppos;
     if (done)
     {
-	unsigned int next_pos = fs_info->devfsd_buf_out + 1;
+	devfs_handle_t parent;
 
-	if (next_pos >= devfsd_buf_size) next_pos = 0;
-	fs_info->devfsd_buf_out = next_pos;
+	spin_lock (&fs_info->devfsd_buffer_lock);
+	fs_info->devfsd_first_event = entry->next;
+	if (entry->next == NULL) fs_info->devfsd_last_event = NULL;
+	spin_unlock (&fs_info->devfsd_buffer_lock);
+	for (; de != NULL; de = parent)
+	{
+	    parent = de->parent;
+	    devfs_put (de);
+	}
+	kmem_cache_free (devfsd_buf_cache, entry);
+	if (ival > 0) atomic_sub (ival, &fs_info->devfsd_overrun_count);
 	*ppos = 0;
     }
     else *ppos = rpos;
@@ -3252,16 +3394,16 @@
 	    }
 	    fs_info->devfsd_task = current;
 	    spin_unlock (&lock);
+	    fs_info->devfsd_pgrp = (current->pgrp == current->pid) ?
+		current->pgrp : 0;
 	    fs_info->devfsd_file = file;
-	    fs_info->devfsd_buffer = (void *) __get_free_page (GFP_KERNEL);
 	    fs_info->devfsd_info = kmalloc (sizeof *fs_info->devfsd_info,
 					    GFP_KERNEL);
-	    if (!fs_info->devfsd_buffer || !fs_info->devfsd_info)
+	    if (!fs_info->devfsd_info)
 	    {
 		devfsd_close (inode, file);
 		return -ENOMEM;
 	    }
-	    fs_info->devfsd_buf_out = fs_info->devfsd_buf_in;
 	}
 	else if (fs_info->devfsd_task != current) return -EBUSY;
 	fs_info->devfsd_event_mask = arg;  /*  Let the masses come forth  */
@@ -3284,29 +3426,52 @@
 
 static int devfsd_close (struct inode *inode, struct file *file)
 {
-    unsigned long flags;
+    struct devfsd_buf_entry *entry, *next;
     struct fs_info *fs_info = inode->i_sb->u.generic_sbp;
 
     if (fs_info->devfsd_file != file) return 0;
     fs_info->devfsd_event_mask = 0;
     fs_info->devfsd_file = NULL;
-    spin_lock_irqsave (&fs_info->devfsd_buffer_lock, flags);
-    if (fs_info->devfsd_buffer)
-    {
-	free_page ( (unsigned long) fs_info->devfsd_buffer );
-	fs_info->devfsd_buffer = NULL;
-    }
+    spin_lock (&fs_info->devfsd_buffer_lock);
+    entry = fs_info->devfsd_first_event;
+    fs_info->devfsd_first_event = NULL;
+    fs_info->devfsd_last_event = NULL;
     if (fs_info->devfsd_info)
     {
 	kfree (fs_info->devfsd_info);
 	fs_info->devfsd_info = NULL;
     }
-    spin_unlock_irqrestore (&fs_info->devfsd_buffer_lock, flags);
+    spin_unlock (&fs_info->devfsd_buffer_lock);
+    fs_info->devfsd_pgrp = 0;
     fs_info->devfsd_task = NULL;
     wake_up (&fs_info->revalidate_wait_queue);
+    for (; entry; entry = next)
+    {
+	next = entry->next;
+	kmem_cache_free (devfsd_buf_cache, entry);
+    }
     return 0;
 }   /*  End Function devfsd_close  */
 
+#ifdef CONFIG_DEVFS_DEBUG
+static ssize_t stat_read (struct file *file, char *buf, size_t len,
+			  loff_t *ppos)
+{
+    ssize_t num;
+    char txt[80];
+
+    num = sprintf (txt, "Number of entries: %u  number of bytes: %u\n",
+		   stat_num_entries, stat_num_bytes) + 1;
+    /*  Can't seek (pread) on this device  */
+    if (ppos != &file->f_pos) return -ESPIPE;
+    if (*ppos >= num) return 0;
+    if (*ppos + len > num) len = num - *ppos;
+    if ( copy_to_user (buf, txt + *ppos, len) ) return -EFAULT;
+    *ppos += len;
+    return len;
+}   /*  End Function stat_read  */
+#endif
+
 
 static int __init init_devfs_fs (void)
 {
@@ -3333,6 +3498,9 @@
 {
     int err;
 
+    devfsd_buf_cache = kmem_cache_create ("devfsd_event",
+					  sizeof (struct devfsd_buf_entry),
+					  0, 0, NULL, NULL);
     if ( !(boot_options & OPTION_MOUNT) ) return;
     err = do_mount ("none", "/dev", "devfs", 0, "");
     if (err == 0) printk ("Mounted devfs on /dev\n");

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)