Merge branch 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik...
authorLinus Torvalds <torvalds@woody.linux-foundation.org>
Wed, 13 Jun 2007 03:35:10 +0000 (20:35 -0700)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Wed, 13 Jun 2007 03:35:10 +0000 (20:35 -0700)
* 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6: (89 commits)
  myri10ge: update driver version
  myri10ge: report when the link partner is running in Myrinet mode
  myri10ge: limit the number of recoveries
  NetXen: Fix link status messages
  Revert "[netdrvr e100] experiment with doing RX in a similar manner to eepro100"
  [PATCH] libertas: convert libertas_mpp into anycast_mask
  [PATCH] libertas: actually send mesh frames to mesh netdev
  [PATCH] libertas: deauthenticate from AP in channel switch
  [PATCH] libertas: pull current channel from firmware on mesh autostart
  [PATCH] libertas: reduce SSID and BSSID mixed-case abuse
  [PATCH] libertas: remove WPA_SUPPLICANT structure
  [PATCH] libertas: remove structure WLAN_802_11_SSID and libertas_escape_essid
  [PATCH] libertas: tweak association debug output
  [PATCH] libertas: fix big-endian associate command.
  [PATCH] libertas: don't byte-swap firmware version number. It's a byte array.
  [PATCH] libertas: more endianness fixes, in tx.c this time
  [PATCH] libertas: More endianness fixes.
  [PATCH] libertas: first pass at fixing up endianness issues
  [PATCH] libertas: sparse fixes
  [PATCH] libertas: fix character set in README
  ...

fs/sysfs/dir.c
fs/sysfs/inode.c
fs/sysfs/mount.c
fs/sysfs/sysfs.h

index 85a668680f82af4e550b3b381cf7340419eb7d86..c4342a0199727b19fe832c4eaff371c0c8e1b9ee 100644 (file)
 #include "sysfs.h"
 
 DECLARE_RWSEM(sysfs_rename_sem);
+spinlock_t sysfs_lock = SPIN_LOCK_UNLOCKED;
 
 static void sysfs_d_iput(struct dentry * dentry, struct inode * inode)
 {
        struct sysfs_dirent * sd = dentry->d_fsdata;
 
        if (sd) {
-               BUG_ON(sd->s_dentry != dentry);
-               sd->s_dentry = NULL;
+               /* sd->s_dentry is protected with sysfs_lock.  This
+                * allows sysfs_drop_dentry() to dereference it.
+                */
+               spin_lock(&sysfs_lock);
+
+               /* The dentry might have been deleted or another
+                * lookup could have happened updating sd->s_dentry to
+                * point the new dentry.  Ignore if it isn't pointing
+                * to this dentry.
+                */
+               if (sd->s_dentry == dentry)
+                       sd->s_dentry = NULL;
+               spin_unlock(&sysfs_lock);
                sysfs_put(sd);
        }
        iput(inode);
@@ -30,6 +42,14 @@ static struct dentry_operations sysfs_dentry_ops = {
        .d_iput         = sysfs_d_iput,
 };
 
+static unsigned int sysfs_inode_counter;
+ino_t sysfs_get_inum(void)
+{
+       if (unlikely(sysfs_inode_counter < 3))
+               sysfs_inode_counter = 3;
+       return sysfs_inode_counter++;
+}
+
 /*
  * Allocates a new sysfs_dirent and links it to the parent sysfs_dirent
  */
@@ -41,6 +61,7 @@ static struct sysfs_dirent * __sysfs_new_dirent(void * element)
        if (!sd)
                return NULL;
 
+       sd->s_ino = sysfs_get_inum();
        atomic_set(&sd->s_count, 1);
        atomic_set(&sd->s_event, 1);
        INIT_LIST_HEAD(&sd->s_children);
@@ -238,7 +259,10 @@ static int sysfs_attach_attr(struct sysfs_dirent * sd, struct dentry * dentry)
         }
 
        dentry->d_fsdata = sysfs_get(sd);
+       /* protect sd->s_dentry against sysfs_d_iput */
+       spin_lock(&sysfs_lock);
        sd->s_dentry = dentry;
+       spin_unlock(&sysfs_lock);
        error = sysfs_create(dentry, (attr->mode & S_IALLUGO) | S_IFREG, init);
        if (error) {
                sysfs_put(sd);
@@ -260,7 +284,10 @@ static int sysfs_attach_link(struct sysfs_dirent * sd, struct dentry * dentry)
        int err = 0;
 
        dentry->d_fsdata = sysfs_get(sd);
+       /* protect sd->s_dentry against sysfs_d_iput */
+       spin_lock(&sysfs_lock);
        sd->s_dentry = dentry;
+       spin_unlock(&sysfs_lock);
        err = sysfs_create(dentry, S_IFLNK|S_IRWXUGO, init_symlink);
        if (!err) {
                dentry->d_op = &sysfs_dentry_ops;
@@ -509,7 +536,7 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
 
        switch (i) {
                case 0:
-                       ino = dentry->d_inode->i_ino;
+                       ino = parent_sd->s_ino;
                        if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
                                break;
                        filp->f_pos++;
@@ -538,10 +565,7 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
 
                                name = sysfs_get_name(next);
                                len = strlen(name);
-                               if (next->s_dentry)
-                                       ino = next->s_dentry->d_inode->i_ino;
-                               else
-                                       ino = iunique(sysfs_sb, 2);
+                               ino = next->s_ino;
 
                                if (filldir(dirent, name, len, filp->f_pos, ino,
                                                 dt_type(next)) < 0)
index bdd30e74de6b00c16685e98745d2fe96325c483b..5266eec15f6e35766c84939f1c7663690d6c3930 100644 (file)
@@ -141,6 +141,7 @@ struct inode * sysfs_new_inode(mode_t mode, struct sysfs_dirent * sd)
                inode->i_mapping->a_ops = &sysfs_aops;
                inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;
                inode->i_op = &sysfs_inode_operations;
+               inode->i_ino = sd->s_ino;
                lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key);
 
                if (sd->s_iattr) {
@@ -245,13 +246,27 @@ static inline void orphan_all_buffers(struct inode *node)
  */
 void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent)
 {
-       struct dentry * dentry = sd->s_dentry;
+       struct dentry *dentry = NULL;
        struct inode *inode;
 
+       /* We're not holding a reference to ->s_dentry dentry but the
+        * field will stay valid as long as sysfs_lock is held.
+        */
+       spin_lock(&sysfs_lock);
+       spin_lock(&dcache_lock);
+
+       /* dget dentry if it's still alive */
+       if (sd->s_dentry && sd->s_dentry->d_inode)
+               dentry = dget_locked(sd->s_dentry);
+
+       spin_unlock(&dcache_lock);
+       spin_unlock(&sysfs_lock);
+
+       /* drop dentry */
        if (dentry) {
                spin_lock(&dcache_lock);
                spin_lock(&dentry->d_lock);
-               if (!(d_unhashed(dentry) && dentry->d_inode)) {
+               if (!d_unhashed(dentry) && dentry->d_inode) {
                        inode = dentry->d_inode;
                        spin_lock(&inode->i_lock);
                        __iget(inode);
@@ -267,6 +282,8 @@ void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent)
                        spin_unlock(&dentry->d_lock);
                        spin_unlock(&dcache_lock);
                }
+
+               dput(dentry);
        }
 }
 
index 23a48a38e6af01455575083f8242ef4538f76cac..00ab9125d398984efa12496fa3b48ff405f62bad 100644 (file)
@@ -33,6 +33,7 @@ static struct sysfs_dirent sysfs_root = {
        .s_element      = NULL,
        .s_type         = SYSFS_ROOT,
        .s_iattr        = NULL,
+       .s_ino          = 1,
 };
 
 static void sysfs_clear_inode(struct inode *inode)
index a77c57e5a6d5d983116203656559911142251c87..502c949c402d9dd3de49ac922d9f1e1db2543e2a 100644 (file)
@@ -5,6 +5,7 @@ struct sysfs_dirent {
        void                    * s_element;
        int                     s_type;
        umode_t                 s_mode;
+       ino_t                   s_ino;
        struct dentry           * s_dentry;
        struct iattr            * s_iattr;
        atomic_t                s_event;
@@ -32,6 +33,7 @@ extern const unsigned char * sysfs_get_name(struct sysfs_dirent *sd);
 extern void sysfs_drop_dentry(struct sysfs_dirent *sd, struct dentry *parent);
 extern int sysfs_setattr(struct dentry *dentry, struct iattr *iattr);
 
+extern spinlock_t sysfs_lock;
 extern struct rw_semaphore sysfs_rename_sem;
 extern struct super_block * sysfs_sb;
 extern const struct file_operations sysfs_dir_operations;