]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/dcache.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[karo-tx-linux.git] / fs / dcache.c
index 83cfb834db0364b1ffed30d58815f826aaaf55dd..5aa53bc056bada2af9c3c6040498abcb9a3a3856 100644 (file)
@@ -229,7 +229,7 @@ static void __d_free(struct rcu_head *head)
  */
 static void d_free(struct dentry *dentry)
 {
-       BUG_ON(dentry->d_count);
+       BUG_ON(dentry->d_lockref.count);
        this_cpu_dec(nr_dentry);
        if (dentry->d_op && dentry->d_op->d_release)
                dentry->d_op->d_release(dentry);
@@ -467,12 +467,12 @@ relock:
        }
 
        if (ref)
-               dentry->d_count--;
+               dentry->d_lockref.count--;
        /*
         * inform the fs via d_prune that this dentry is about to be
         * unhashed and destroyed.
         */
-       if (dentry->d_flags & DCACHE_OP_PRUNE)
+       if ((dentry->d_flags & DCACHE_OP_PRUNE) && !d_unhashed(dentry))
                dentry->d_op->d_prune(dentry);
 
        dentry_lru_del(dentry);
@@ -513,15 +513,10 @@ void dput(struct dentry *dentry)
                return;
 
 repeat:
-       if (dentry->d_count == 1)
+       if (dentry->d_lockref.count == 1)
                might_sleep();
-       spin_lock(&dentry->d_lock);
-       BUG_ON(!dentry->d_count);
-       if (dentry->d_count > 1) {
-               dentry->d_count--;
-               spin_unlock(&dentry->d_lock);
+       if (lockref_put_or_lock(&dentry->d_lockref))
                return;
-       }
 
        if (dentry->d_flags & DCACHE_OP_DELETE) {
                if (dentry->d_op->d_delete(dentry))
@@ -535,7 +530,7 @@ repeat:
        dentry->d_flags |= DCACHE_REFERENCED;
        dentry_lru_add(dentry);
 
-       dentry->d_count--;
+       dentry->d_lockref.count--;
        spin_unlock(&dentry->d_lock);
        return;
 
@@ -590,7 +585,7 @@ int d_invalidate(struct dentry * dentry)
         * We also need to leave mountpoints alone,
         * directory or not.
         */
-       if (dentry->d_count > 1 && dentry->d_inode) {
+       if (dentry->d_lockref.count > 1 && dentry->d_inode) {
                if (S_ISDIR(dentry->d_inode->i_mode) || d_mountpoint(dentry)) {
                        spin_unlock(&dentry->d_lock);
                        return -EBUSY;
@@ -606,20 +601,33 @@ EXPORT_SYMBOL(d_invalidate);
 /* This must be called with d_lock held */
 static inline void __dget_dlock(struct dentry *dentry)
 {
-       dentry->d_count++;
+       dentry->d_lockref.count++;
 }
 
 static inline void __dget(struct dentry *dentry)
 {
-       spin_lock(&dentry->d_lock);
-       __dget_dlock(dentry);
-       spin_unlock(&dentry->d_lock);
+       lockref_get(&dentry->d_lockref);
 }
 
 struct dentry *dget_parent(struct dentry *dentry)
 {
+       int gotref;
        struct dentry *ret;
 
+       /*
+        * Do optimistic parent lookup without any
+        * locking.
+        */
+       rcu_read_lock();
+       ret = ACCESS_ONCE(dentry->d_parent);
+       gotref = lockref_get_not_zero(&ret->d_lockref);
+       rcu_read_unlock();
+       if (likely(gotref)) {
+               if (likely(ret == ACCESS_ONCE(dentry->d_parent)))
+                       return ret;
+               dput(ret);
+       }
+
 repeat:
        /*
         * Don't need rcu_dereference because we re-check it was correct under
@@ -634,8 +642,8 @@ repeat:
                goto repeat;
        }
        rcu_read_unlock();
-       BUG_ON(!ret->d_count);
-       ret->d_count++;
+       BUG_ON(!ret->d_lockref.count);
+       ret->d_lockref.count++;
        spin_unlock(&ret->d_lock);
        return ret;
 }
@@ -718,7 +726,15 @@ restart:
        spin_lock(&inode->i_lock);
        hlist_for_each_entry(dentry, &inode->i_dentry, d_alias) {
                spin_lock(&dentry->d_lock);
-               if (!dentry->d_count) {
+               if (!dentry->d_lockref.count) {
+                       /*
+                        * inform the fs via d_prune that this dentry
+                        * is about to be unhashed and destroyed.
+                        */
+                       if ((dentry->d_flags & DCACHE_OP_PRUNE) &&
+                           !d_unhashed(dentry))
+                               dentry->d_op->d_prune(dentry);
+
                        __dget_dlock(dentry);
                        __d_drop(dentry);
                        spin_unlock(&dentry->d_lock);
@@ -763,12 +779,8 @@ static void try_prune_one_dentry(struct dentry *dentry)
        /* Prune ancestors. */
        dentry = parent;
        while (dentry) {
-               spin_lock(&dentry->d_lock);
-               if (dentry->d_count > 1) {
-                       dentry->d_count--;
-                       spin_unlock(&dentry->d_lock);
+               if (lockref_put_or_lock(&dentry->d_lockref))
                        return;
-               }
                dentry = dentry_kill(dentry, 1);
        }
 }
@@ -793,7 +805,7 @@ static void shrink_dentry_list(struct list_head *list)
                 * the LRU because of laziness during lookup.  Do not free
                 * it - just keep it off the LRU list.
                 */
-               if (dentry->d_count) {
+               if (dentry->d_lockref.count) {
                        dentry_lru_del(dentry);
                        spin_unlock(&dentry->d_lock);
                        continue;
@@ -907,13 +919,14 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
                         * inform the fs that this dentry is about to be
                         * unhashed and destroyed.
                         */
-                       if (dentry->d_flags & DCACHE_OP_PRUNE)
+                       if ((dentry->d_flags & DCACHE_OP_PRUNE) &&
+                           !d_unhashed(dentry))
                                dentry->d_op->d_prune(dentry);
 
                        dentry_lru_del(dentry);
                        __d_shrink(dentry);
 
-                       if (dentry->d_count != 0) {
+                       if (dentry->d_lockref.count != 0) {
                                printk(KERN_ERR
                                       "BUG: Dentry %p{i=%lx,n=%s}"
                                       " still in use (%d)"
@@ -922,7 +935,7 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
                                       dentry->d_inode ?
                                       dentry->d_inode->i_ino : 0UL,
                                       dentry->d_name.name,
-                                      dentry->d_count,
+                                      dentry->d_lockref.count,
                                       dentry->d_sb->s_type->name,
                                       dentry->d_sb->s_id);
                                BUG();
@@ -933,7 +946,7 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
                                list_del(&dentry->d_u.d_child);
                        } else {
                                parent = dentry->d_parent;
-                               parent->d_count--;
+                               parent->d_lockref.count--;
                                list_del(&dentry->d_u.d_child);
                        }
 
@@ -981,7 +994,7 @@ void shrink_dcache_for_umount(struct super_block *sb)
 
        dentry = sb->s_root;
        sb->s_root = NULL;
-       dentry->d_count--;
+       dentry->d_lockref.count--;
        shrink_dcache_for_umount_subtree(dentry);
 
        while (!hlist_bl_empty(&sb->s_anon)) {
@@ -1147,7 +1160,7 @@ resume:
                 * loop in shrink_dcache_parent() might not make any progress
                 * and loop forever.
                 */
-               if (dentry->d_count) {
+               if (dentry->d_lockref.count) {
                        dentry_lru_del(dentry);
                } else if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) {
                        dentry_lru_move_list(dentry, dispose);
@@ -1269,7 +1282,7 @@ struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
        smp_wmb();
        dentry->d_name.name = dname;
 
-       dentry->d_count = 1;
+       dentry->d_lockref.count = 1;
        dentry->d_flags = 0;
        spin_lock_init(&dentry->d_lock);
        seqcount_init(&dentry->d_seq);
@@ -1782,7 +1795,7 @@ static noinline enum slow_d_compare slow_dentry_cmp(
  * without taking d_lock and checking d_seq sequence count against @seq
  * returned here.
  *
- * A refcount may be taken on the found dentry with the __d_rcu_to_refcount
+ * A refcount may be taken on the found dentry with the d_rcu_to_refcount
  * function.
  *
  * Alternatively, __d_lookup_rcu may be called again to look up the child of
@@ -1970,7 +1983,7 @@ struct dentry *__d_lookup(const struct dentry *parent, const struct qstr *name)
                                goto next;
                }
 
-               dentry->d_count++;
+               dentry->d_lockref.count++;
                found = dentry;
                spin_unlock(&dentry->d_lock);
                break;
@@ -2069,7 +2082,7 @@ again:
        spin_lock(&dentry->d_lock);
        inode = dentry->d_inode;
        isdir = S_ISDIR(inode->i_mode);
-       if (dentry->d_count == 1) {
+       if (dentry->d_lockref.count == 1) {
                if (!spin_trylock(&inode->i_lock)) {
                        spin_unlock(&dentry->d_lock);
                        cpu_relax();
@@ -2948,7 +2961,7 @@ resume:
                }
                if (!(dentry->d_flags & DCACHE_GENOCIDE)) {
                        dentry->d_flags |= DCACHE_GENOCIDE;
-                       dentry->d_count--;
+                       dentry->d_lockref.count--;
                }
                spin_unlock(&dentry->d_lock);
        }
@@ -2956,7 +2969,7 @@ resume:
                struct dentry *child = this_parent;
                if (!(this_parent->d_flags & DCACHE_GENOCIDE)) {
                        this_parent->d_flags |= DCACHE_GENOCIDE;
-                       this_parent->d_count--;
+                       this_parent->d_lockref.count--;
                }
                this_parent = try_to_ascend(this_parent, locked, seq);
                if (!this_parent)