fs: dcache scale subdirs
[linux-flexiantxendom0-natty.git] / fs / autofs4 / expire.c
index a796c94..968c143 100644 (file)
@@ -91,24 +91,64 @@ done:
 }
 
 /*
- * Calculate next entry in top down tree traversal.
- * From next_mnt in namespace.c - elegant.
+ * Calculate and dget next entry in top down tree traversal.
  */
-static struct dentry *next_dentry(struct dentry *p, struct dentry *root)
+static struct dentry *get_next_positive_dentry(struct dentry *prev,
+                                               struct dentry *root)
 {
-       struct list_head *next = p->d_subdirs.next;
+       struct list_head *next;
+       struct dentry *p, *ret;
+
+       if (prev == NULL)
+               return dget(prev);
 
+       spin_lock(&dcache_lock);
+relock:
+       p = prev;
+       spin_lock(&p->d_lock);
+again:
+       next = p->d_subdirs.next;
        if (next == &p->d_subdirs) {
                while (1) {
-                       if (p == root)
+                       struct dentry *parent;
+
+                       if (p == root) {
+                               spin_unlock(&p->d_lock);
+                               spin_unlock(&dcache_lock);
+                               dput(prev);
                                return NULL;
+                       }
+
+                       parent = p->d_parent;
+                       if (!spin_trylock(&parent->d_lock)) {
+                               spin_unlock(&p->d_lock);
+                               cpu_relax();
+                               goto relock;
+                       }
+                       spin_unlock(&p->d_lock);
                        next = p->d_u.d_child.next;
-                       if (next != &p->d_parent->d_subdirs)
+                       p = parent;
+                       if (next != &parent->d_subdirs)
                                break;
-                       p = p->d_parent;
                }
        }
-       return list_entry(next, struct dentry, d_u.d_child);
+       ret = list_entry(next, struct dentry, d_u.d_child);
+
+       spin_lock_nested(&ret->d_lock, DENTRY_D_LOCK_NESTED);
+       /* Negative dentry - try next */
+       if (!simple_positive(ret)) {
+               spin_unlock(&ret->d_lock);
+               p = ret;
+               goto again;
+       }
+       dget_dlock(ret);
+       spin_unlock(&ret->d_lock);
+       spin_unlock(&p->d_lock);
+       spin_unlock(&dcache_lock);
+
+       dput(prev);
+
+       return ret;
 }
 
 /*
@@ -158,18 +198,11 @@ static int autofs4_tree_busy(struct vfsmount *mnt,
        if (!simple_positive(top))
                return 1;
 
-       spin_lock(&dcache_lock);
-       for (p = top; p; p = next_dentry(p, top)) {
-               /* Negative dentry - give up */
-               if (!simple_positive(p))
-                       continue;
-
+       p = NULL;
+       while ((p = get_next_positive_dentry(p, top))) {
                DPRINTK("dentry %p %.*s",
                        p, (int) p->d_name.len, p->d_name.name);
 
-               p = dget(p);
-               spin_unlock(&dcache_lock);
-
                /*
                 * Is someone visiting anywhere in the subtree ?
                 * If there's no mount we need to check the usage
@@ -198,16 +231,13 @@ static int autofs4_tree_busy(struct vfsmount *mnt,
                        else
                                ino_count++;
 
-                       if (atomic_read(&p->d_count) > ino_count) {
+                       if (p->d_count > ino_count) {
                                top_ino->last_used = jiffies;
                                dput(p);
                                return 1;
                        }
                }
-               dput(p);
-               spin_lock(&dcache_lock);
        }
-       spin_unlock(&dcache_lock);
 
        /* Timeout of a tree mount is ultimately determined by its top dentry */
        if (!autofs4_can_expire(top, timeout, do_now))
@@ -226,32 +256,21 @@ static struct dentry *autofs4_check_leaves(struct vfsmount *mnt,
        DPRINTK("parent %p %.*s",
                parent, (int)parent->d_name.len, parent->d_name.name);
 
-       spin_lock(&dcache_lock);
-       for (p = parent; p; p = next_dentry(p, parent)) {
-               /* Negative dentry - give up */
-               if (!simple_positive(p))
-                       continue;
-
+       p = NULL;
+       while ((p = get_next_positive_dentry(p, parent))) {
                DPRINTK("dentry %p %.*s",
                        p, (int) p->d_name.len, p->d_name.name);
 
-               p = dget(p);
-               spin_unlock(&dcache_lock);
-
                if (d_mountpoint(p)) {
                        /* Can we umount this guy */
                        if (autofs4_mount_busy(mnt, p))
-                               goto cont;
+                               continue;
 
                        /* Can we expire this guy */
                        if (autofs4_can_expire(p, timeout, do_now))
                                return p;
                }
-cont:
-               dput(p);
-               spin_lock(&dcache_lock);
        }
-       spin_unlock(&dcache_lock);
        return NULL;
 }
 
@@ -302,8 +321,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
 {
        unsigned long timeout;
        struct dentry *root = sb->s_root;
+       struct dentry *dentry;
        struct dentry *expired = NULL;
-       struct list_head *next;
        int do_now = how & AUTOFS_EXP_IMMEDIATE;
        int exp_leaves = how & AUTOFS_EXP_LEAVES;
        struct autofs_info *ino;
@@ -315,23 +334,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
        now = jiffies;
        timeout = sbi->exp_timeout;
 
-       spin_lock(&dcache_lock);
-       next = root->d_subdirs.next;
-
-       /* On exit from the loop expire is set to a dgot dentry
-        * to expire or it's NULL */
-       while ( next != &root->d_subdirs ) {
-               struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child);
-
-               /* Negative dentry - give up */
-               if (!simple_positive(dentry)) {
-                       next = next->next;
-                       continue;
-               }
-
-               dentry = dget(dentry);
-               spin_unlock(&dcache_lock);
-
+       dentry = NULL;
+       while ((dentry = get_next_positive_dentry(dentry, root))) {
                spin_lock(&sbi->fs_lock);
                ino = autofs4_dentry_ino(dentry);
 
@@ -347,7 +351,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
 
                        /* Path walk currently on this dentry? */
                        ino_count = atomic_read(&ino->count) + 2;
-                       if (atomic_read(&dentry->d_count) > ino_count)
+                       if (dentry->d_count > ino_count)
                                goto next;
 
                        /* Can we umount this guy */
@@ -369,7 +373,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
                if (!exp_leaves) {
                        /* Path walk currently on this dentry? */
                        ino_count = atomic_read(&ino->count) + 1;
-                       if (atomic_read(&dentry->d_count) > ino_count)
+                       if (dentry->d_count > ino_count)
                                goto next;
 
                        if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) {
@@ -383,7 +387,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
                } else {
                        /* Path walk currently on this dentry? */
                        ino_count = atomic_read(&ino->count) + 1;
-                       if (atomic_read(&dentry->d_count) > ino_count)
+                       if (dentry->d_count > ino_count)
                                goto next;
 
                        expired = autofs4_check_leaves(mnt, dentry, timeout, do_now);
@@ -394,11 +398,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
                }
 next:
                spin_unlock(&sbi->fs_lock);
-               dput(dentry);
-               spin_lock(&dcache_lock);
-               next = next->next;
        }
-       spin_unlock(&dcache_lock);
        return NULL;
 
 found:
@@ -409,7 +409,11 @@ found:
        init_completion(&ino->expire_complete);
        spin_unlock(&sbi->fs_lock);
        spin_lock(&dcache_lock);
+       spin_lock(&expired->d_parent->d_lock);
+       spin_lock_nested(&expired->d_lock, DENTRY_D_LOCK_NESTED);
        list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child);
+       spin_unlock(&expired->d_lock);
+       spin_unlock(&expired->d_parent->d_lock);
        spin_unlock(&dcache_lock);
        return expired;
 }