Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=dbde0fcf9f8f6d477af3c32d9979e789ee680cde
Commit:     dbde0fcf9f8f6d477af3c32d9979e789ee680cde
Parent:     198a2a847015805c6f57d8cc732bdaaccb494007
Author:     Tejun Heo <[EMAIL PROTECTED]>
AuthorDate: Thu Jun 14 03:45:16 2007 +0900
Committer:  Greg Kroah-Hartman <[EMAIL PROTECTED]>
CommitDate: Wed Jul 11 16:09:06 2007 -0700

    sysfs: reimplement sysfs_drop_dentry()
    
    This patch reimplements sysfs_drop_dentry() such that remove_dir() can
    use it to drop dentry instead of using a separate mechanism.  With
    this change, making directories reclaimable is much easier.
    
    This patch used to contain fixes for two race conditions around
    sd->s_dentry but that part has been separated out and included into
    mainline early as commit 6aa054aadfea613a437ad0b15d38eca2b963fc0a and
    dd14cbc994709a1c5a64ed3621f583c49a27e521.
    
    Signed-off-by: Tejun Heo <[EMAIL PROTECTED]>
    Signed-off-by: Greg Kroah-Hartman <[EMAIL PROTECTED]>
---
 fs/sysfs/dir.c   |   18 +++--------
 fs/sysfs/inode.c |   82 ++++++++++++++++++++++++++++++++++++++++--------------
 fs/sysfs/sysfs.h |    2 +-
 3 files changed, 67 insertions(+), 35 deletions(-)

diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index 01eeb4b..bc11a26 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -372,22 +372,19 @@ const struct inode_operations sysfs_dir_inode_operations 
= {
 
 static void remove_dir(struct dentry * d)
 {
-       struct dentry * parent = dget(d->d_parent);
-       struct sysfs_dirent * sd;
+       struct dentry *parent = d->d_parent;
+       struct sysfs_dirent *sd = d->d_fsdata;
 
        mutex_lock(&parent->d_inode->i_mutex);
-       d_delete(d);
-       sd = d->d_fsdata;
+
        list_del_init(&sd->s_sibling);
-       if (d->d_inode)
-               simple_rmdir(parent->d_inode,d);
 
        pr_debug(" o %s removing done (%d)\n",d->d_name.name,
                 atomic_read(&d->d_count));
 
        mutex_unlock(&parent->d_inode->i_mutex);
-       dput(parent);
 
+       sysfs_drop_dentry(sd);
        sysfs_deactivate(sd);
        sysfs_put(sd);
 }
@@ -404,7 +401,6 @@ static void __sysfs_remove_dir(struct dentry *dentry)
        struct sysfs_dirent * parent_sd;
        struct sysfs_dirent * sd, * tmp;
 
-       dget(dentry);
        if (!dentry)
                return;
 
@@ -415,21 +411,17 @@ static void __sysfs_remove_dir(struct dentry *dentry)
                if (!sd->s_type || !(sd->s_type & SYSFS_NOT_PINNED))
                        continue;
                list_move(&sd->s_sibling, &removed);
-               sysfs_drop_dentry(sd, dentry);
        }
        mutex_unlock(&dentry->d_inode->i_mutex);
 
        list_for_each_entry_safe(sd, tmp, &removed, s_sibling) {
                list_del_init(&sd->s_sibling);
+               sysfs_drop_dentry(sd);
                sysfs_deactivate(sd);
                sysfs_put(sd);
        }
 
        remove_dir(dentry);
-       /**
-        * Drop reference from dget() on entrance.
-        */
-       dput(dentry);
 }
 
 /**
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index 88857a3..6ad47c1 100644
--- a/fs/sysfs/inode.c
+++ b/fs/sysfs/inode.c
@@ -191,13 +191,25 @@ int sysfs_create(struct sysfs_dirent *sd, struct dentry 
*dentry, int mode,
        return error;
 }
 
-/*
- * Unhashes the dentry corresponding to given sysfs_dirent
- * Called with parent inode's i_mutex held.
+/**
+ *     sysfs_drop_dentry - drop dentry for the specified sysfs_dirent
+ *     @sd: target sysfs_dirent
+ *
+ *     Drop dentry for @sd.  @sd must have been unlinked from its
+ *     parent on entry to this function such that it can't be looked
+ *     up anymore.
+ *
+ *     @sd->s_dentry which is protected with sysfs_lock points to the
+ *     currently associated dentry but we're not holding a reference
+ *     to it and racing with dput().  Grab dcache_lock and verify
+ *     dentry before dropping it.  If @sd->s_dentry is NULL or dput()
+ *     beats us, no need to bother.
  */
-void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent)
+void sysfs_drop_dentry(struct sysfs_dirent *sd)
 {
-       struct dentry *dentry = NULL;
+       struct dentry *dentry = NULL, *parent = NULL;
+       struct inode *dir;
+       struct timespec curtime;
 
        /* We're not holding a reference to ->s_dentry dentry but the
         * field will stay valid as long as sysfs_lock is held.
@@ -205,30 +217,57 @@ void sysfs_drop_dentry(struct sysfs_dirent * sd, struct 
dentry * parent)
        spin_lock(&sysfs_lock);
        spin_lock(&dcache_lock);
 
-       /* dget dentry if it's still alive */
-       if (sd->s_dentry && sd->s_dentry->d_inode)
+       if (sd->s_dentry && sd->s_dentry->d_inode) {
+               /* get dentry if it's there and dput() didn't kill it yet */
                dentry = dget_locked(sd->s_dentry);
+               parent = dentry->d_parent;
+       } else if (sd->s_parent->s_dentry->d_inode) {
+               /* We need to update the parent even if dentry for the
+                * victim itself doesn't exist.
+                */
+               parent = dget_locked(sd->s_parent->s_dentry);
+       }
+
+       /* drop */
+       if (dentry) {
+               spin_lock(&dentry->d_lock);
+               __d_drop(dentry);
+               spin_unlock(&dentry->d_lock);
+       }
 
        spin_unlock(&dcache_lock);
        spin_unlock(&sysfs_lock);
 
-       /* drop dentry */
+       /* nothing to do if the parent isn't in dcache */
+       if (!parent)
+               return;
+
+       /* adjust nlink and update timestamp */
+       dir = parent->d_inode;
+       mutex_lock(&dir->i_mutex);
+
+       curtime = CURRENT_TIME;
+
+       dir->i_ctime = dir->i_mtime = curtime;
+
        if (dentry) {
-               spin_lock(&dcache_lock);
-               spin_lock(&dentry->d_lock);
-               if (!d_unhashed(dentry) && dentry->d_inode) {
-                       dget_locked(dentry);
-                       __d_drop(dentry);
-                       spin_unlock(&dentry->d_lock);
-                       spin_unlock(&dcache_lock);
-                       simple_unlink(parent->d_inode, dentry);
-               } else {
-                       spin_unlock(&dentry->d_lock);
-                       spin_unlock(&dcache_lock);
+               dentry->d_inode->i_ctime = curtime;
+               drop_nlink(dentry->d_inode);
+               if (sd->s_type & SYSFS_DIR) {
+                       drop_nlink(dentry->d_inode);
+                       drop_nlink(dir);
+                       /* XXX: unpin if directory, this will go away soon */
+                       dput(dentry);
                }
+       }
+
+       mutex_unlock(&dir->i_mutex);
 
+       /* bye bye */
+       if (dentry)
                dput(dentry);
-       }
+       else
+               dput(parent);
 }
 
 int sysfs_hash_and_remove(struct dentry * dir, const char * name)
@@ -251,7 +290,6 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * 
name)
                        continue;
                if (!strcmp(sd->s_name, name)) {
                        list_del_init(&sd->s_sibling);
-                       sysfs_drop_dentry(sd, dir);
                        found = 1;
                        break;
                }
@@ -261,7 +299,9 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * 
name)
        if (!found)
                return -ENOENT;
 
+       sysfs_drop_dentry(sd);
        sysfs_deactivate(sd);
        sysfs_put(sd);
+
        return 0;
 }
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index 9fa77d6..fc6aa86 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -76,7 +76,7 @@ extern struct sysfs_dirent *sysfs_find(struct sysfs_dirent 
*dir, const char * na
 extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry 
**);
 extern void sysfs_remove_subdir(struct dentry *);
 
-extern void sysfs_drop_dentry(struct sysfs_dirent *sd, struct dentry *parent);
+extern void sysfs_drop_dentry(struct sysfs_dirent *sd);
 extern int sysfs_setattr(struct dentry *dentry, struct iattr *iattr);
 
 extern spinlock_t sysfs_lock;
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to