diff -ru linux-2.9.13/fs/devfs/base.c linux/fs/devfs/base.c --- linux-2.9.13/fs/devfs/base.c Thu Oct 11 08:23:24 2001 +++ linux/fs/devfs/base.c Thu Oct 25 00:02:34 2001 @@ -669,6 +669,8 @@ struct symlink_type { + atomic_t refcount; /* When this drops to zero, it's unused */ + rwlock_t lock; /* Lock around the registered flag */ unsigned int length; /* Not including the NULL-termimator */ char *linkname; /* This is NULL-terminated */ }; @@ -758,8 +760,6 @@ 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, @@ -817,10 +817,19 @@ 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; + read_lock (&curr->u.symlink.lock); + if (!curr->registered) + { + read_unlock (&curr->u.symlink.lock); + return NULL; + } + atomic_inc (&curr->u.symlink.refcount); + read_unlock (&curr->u.symlink.lock); + retval = search_for_entry (parent, curr->u.symlink.linkname, + curr->u.symlink.length, FALSE, FALSE, NULL, + TRUE); + if ( atomic_dec_and_test (&curr->u.symlink.refcount) ) + kfree (curr->u.symlink.linkname); return retval; } /* End Function search_for_entry_in_dir */ @@ -1090,10 +1099,8 @@ ++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); if (entry != NULL) return entry; } /* Have to search by major and minor: slow */ @@ -1439,11 +1446,11 @@ } 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); + write_lock (&de->u.symlink.lock); + de->registered = FALSE; + write_unlock (&de->u.symlink.lock); + if ( atomic_dec_and_test (&de->u.symlink.refcount) ) + kfree (de->u.symlink.linkname); return; } if ( S_ISFIFO (de->mode) ) @@ -1523,10 +1530,8 @@ 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", DEVFS_NAME, name); @@ -1537,8 +1542,9 @@ de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE; de->u.symlink.linkname = newlink; de->u.symlink.length = linklength; + atomic_set (&de->u.symlink.refcount, 1); + rwlock_init (&de->u.symlink.lock); de->registered = TRUE; - up_write (&symlink_rwsem); if (handle != NULL) *handle = de; return 0; } /* End Function devfs_do_symlink */ @@ -2808,15 +2814,16 @@ 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); + write_lock (&de->u.symlink.lock); + de->registered = FALSE; + write_unlock (&de->u.symlink.lock); + if ( atomic_dec_and_test (&de->u.symlink.refcount) ) + kfree (de->u.symlink.linkname); } + else de->registered = FALSE; + de->hide = TRUE; free_dentries (de); return 0; } /* End Function devfs_unlink */ @@ -3018,10 +3025,17 @@ de = get_devfs_entry_from_vfs_inode (dentry->d_inode, TRUE); 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); + read_lock (&de->u.symlink.lock); + if (!de->registered) + { + read_unlock (&de->u.symlink.lock); + return -ENODEV; + } + atomic_inc (&de->u.symlink.refcount); + read_unlock (&de->u.symlink.lock); + err = vfs_readlink (dentry, buffer, buflen, de->u.symlink.linkname); + if ( atomic_dec_and_test (&de->u.symlink.refcount) ) + kfree (de->u.symlink.linkname); return err; } /* End Function devfs_readlink */ @@ -3032,10 +3046,17 @@ de = get_devfs_entry_from_vfs_inode (dentry->d_inode, TRUE); if (!de) return -ENODEV; - down_read (&symlink_rwsem); - err = de->registered ? vfs_follow_link (nd, - de->u.symlink.linkname) : -ENODEV; - up_read (&symlink_rwsem); + read_lock (&de->u.symlink.lock); + if (!de->registered) + { + read_unlock (&de->u.symlink.lock); + return -ENODEV; + } + atomic_inc (&de->u.symlink.refcount); + read_unlock (&de->u.symlink.lock); + err = vfs_follow_link (nd, de->u.symlink.linkname); + if ( atomic_dec_and_test (&de->u.symlink.refcount) ) + kfree (de->u.symlink.linkname); return err; } /* End Function devfs_follow_link */