zram: remove zram stats code duplication
[firefly-linux-kernel-4.4.55.git] / fs / dcache.c
index b3cb521809e9850707716c400f7345898ee485e9..efa4602e064f75d47f064ab74084f8b81f5cc56b 100644 (file)
@@ -364,9 +364,9 @@ static struct dentry *d_kill(struct dentry *dentry, struct dentry *parent)
        __releases(parent->d_lock)
        __releases(dentry->d_inode->i_lock)
 {
-       list_del(&dentry->d_child);
+       __list_del_entry(&dentry->d_child);
        /*
-        * Inform try_to_ascend() that we are no longer attached to the
+        * Inform ascending readers that we are no longer attached to the
         * dentry tree
         */
        dentry->d_flags |= DCACHE_DENTRY_KILLED;
@@ -987,35 +987,6 @@ void shrink_dcache_for_umount(struct super_block *sb)
        }
 }
 
-/*
- * This tries to ascend one level of parenthood, but
- * we can race with renaming, so we need to re-check
- * the parenthood after dropping the lock and check
- * that the sequence number still matches.
- */
-static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq)
-{
-       struct dentry *new = old->d_parent;
-
-       rcu_read_lock();
-       spin_unlock(&old->d_lock);
-       spin_lock(&new->d_lock);
-
-       /*
-        * might go back up the wrong parent if we have had a rename
-        * or deletion
-        */
-       if (new != old->d_parent ||
-                (old->d_flags & DCACHE_DENTRY_KILLED) ||
-                (!locked && read_seqretry(&rename_lock, seq))) {
-               spin_unlock(&new->d_lock);
-               new = NULL;
-       }
-       rcu_read_unlock();
-       return new;
-}
-
-
 /*
  * Search for at least 1 mount point in the dentry's subdirs.
  * We descend to the next level whenever the d_subdirs
@@ -1070,30 +1041,48 @@ resume:
        /*
         * All done at this level ... ascend and resume the search.
         */
+       rcu_read_lock();
+ascend:
        if (this_parent != parent) {
                struct dentry *child = this_parent;
-               this_parent = try_to_ascend(this_parent, locked, seq);
-               if (!this_parent)
+               this_parent = child->d_parent;
+
+               spin_unlock(&child->d_lock);
+               spin_lock(&this_parent->d_lock);
+
+               /* might go back up the wrong parent if we have had a rename. */
+               if (!locked && read_seqretry(&rename_lock, seq))
                        goto rename_retry;
                next = child->d_child.next;
+               while (unlikely(child->d_flags & DCACHE_DENTRY_KILLED)) {
+                       if (next == &this_parent->d_subdirs)
+                               goto ascend;
+                       child = list_entry(next, struct dentry, d_child);
+                       next = next->next;
+               }
+               rcu_read_unlock();
                goto resume;
        }
-       spin_unlock(&this_parent->d_lock);
        if (!locked && read_seqretry(&rename_lock, seq))
                goto rename_retry;
+       spin_unlock(&this_parent->d_lock);
+       rcu_read_unlock();
        if (locked)
                write_sequnlock(&rename_lock);
        return 0; /* No mount points found in tree */
 positive:
        if (!locked && read_seqretry(&rename_lock, seq))
-               goto rename_retry;
+               goto rename_retry_unlocked;
        if (locked)
                write_sequnlock(&rename_lock);
        return 1;
 
 rename_retry:
+       spin_unlock(&this_parent->d_lock);
+       rcu_read_unlock();
        if (locked)
                goto again;
+rename_retry_unlocked:
        locked = 1;
        write_seqlock(&rename_lock);
        goto again;
@@ -1158,6 +1147,7 @@ resume:
                 */
                if (found && need_resched()) {
                        spin_unlock(&dentry->d_lock);
+                       rcu_read_lock();
                        goto out;
                }
 
@@ -1177,23 +1167,40 @@ resume:
        /*
         * All done at this level ... ascend and resume the search.
         */
+       rcu_read_lock();
+ascend:
        if (this_parent != parent) {
                struct dentry *child = this_parent;
-               this_parent = try_to_ascend(this_parent, locked, seq);
-               if (!this_parent)
+               this_parent = child->d_parent;
+
+               spin_unlock(&child->d_lock);
+               spin_lock(&this_parent->d_lock);
+
+               /* might go back up the wrong parent if we have had a rename. */
+               if (!locked && read_seqretry(&rename_lock, seq))
                        goto rename_retry;
                next = child->d_child.next;
+               while (unlikely(child->d_flags & DCACHE_DENTRY_KILLED)) {
+                       if (next == &this_parent->d_subdirs)
+                               goto ascend;
+                       child = list_entry(next, struct dentry, d_child);
+                       next = next->next;
+               }
+               rcu_read_unlock();
                goto resume;
        }
 out:
-       spin_unlock(&this_parent->d_lock);
        if (!locked && read_seqretry(&rename_lock, seq))
                goto rename_retry;
+       spin_unlock(&this_parent->d_lock);
+       rcu_read_unlock();
        if (locked)
                write_sequnlock(&rename_lock);
        return found;
 
 rename_retry:
+       spin_unlock(&this_parent->d_lock);
+       rcu_read_unlock();
        if (found)
                return found;
        if (locked)
@@ -2954,26 +2961,43 @@ resume:
                }
                spin_unlock(&dentry->d_lock);
        }
+       rcu_read_lock();
+ascend:
        if (this_parent != root) {
                struct dentry *child = this_parent;
                if (!(this_parent->d_flags & DCACHE_GENOCIDE)) {
                        this_parent->d_flags |= DCACHE_GENOCIDE;
                        this_parent->d_count--;
                }
-               this_parent = try_to_ascend(this_parent, locked, seq);
-               if (!this_parent)
+               this_parent = child->d_parent;
+
+               spin_unlock(&child->d_lock);
+               spin_lock(&this_parent->d_lock);
+
+               /* might go back up the wrong parent if we have had a rename. */
+               if (!locked && read_seqretry(&rename_lock, seq))
                        goto rename_retry;
                next = child->d_child.next;
+               while (unlikely(child->d_flags & DCACHE_DENTRY_KILLED)) {
+                       if (next == &this_parent->d_subdirs)
+                               goto ascend;
+                       child = list_entry(next, struct dentry, d_child);
+                       next = next->next;
+               }
+               rcu_read_unlock();
                goto resume;
        }
-       spin_unlock(&this_parent->d_lock);
        if (!locked && read_seqretry(&rename_lock, seq))
                goto rename_retry;
+       spin_unlock(&this_parent->d_lock);
+       rcu_read_unlock();
        if (locked)
                write_sequnlock(&rename_lock);
        return;
 
 rename_retry:
+       spin_unlock(&this_parent->d_lock);
+       rcu_read_unlock();
        if (locked)
                goto again;
        locked = 1;