iio: imu: nvi v.336 Add DMP AUX support
[linux-3.10.git] / fs / dcache.c
index 93d9f4e..17222fa 100644 (file)
@@ -43,7 +43,7 @@
 /*
  * Usage:
  * dcache->d_inode->i_lock protects:
- *   - i_dentry, d_alias, d_inode of aliases
+ *   - i_dentry, d_u.d_alias, d_inode of aliases
  * dcache_hash_bucket lock protects:
  *   - the dcache hash table
  * s_anon bl list spinlock protects:
@@ -58,7 +58,7 @@
  *   - d_unhashed()
  *   - d_parent and d_subdirs
  *   - childrens' d_child and d_parent
- *   - d_alias, d_inode
+ *   - d_u.d_alias, d_inode
  *
  * Ordering:
  * dentry->d_inode->i_lock
@@ -96,8 +96,6 @@ static struct kmem_cache *dentry_cache __read_mostly;
  * This hash-function tries to avoid losing too many bits of hash
  * information, yet avoid using a prime hash-size or similar.
  */
-#define D_HASHBITS     d_hash_shift
-#define D_HASHMASK     d_hash_mask
 
 static unsigned int d_hash_mask __read_mostly;
 static unsigned int d_hash_shift __read_mostly;
@@ -108,8 +106,7 @@ static inline struct hlist_bl_head *d_hash(const struct dentry *parent,
                                        unsigned int hash)
 {
        hash += (unsigned long) parent / L1_CACHE_BYTES;
-       hash = hash + (hash >> D_HASHBITS);
-       return dentry_hashtable + (hash & D_HASHMASK);
+       return dentry_hashtable + hash_32(hash, d_hash_shift);
 }
 
 /* Statistics gathering. */
@@ -218,7 +215,6 @@ static void __d_free(struct rcu_head *head)
 {
        struct dentry *dentry = container_of(head, struct dentry, d_u.d_rcu);
 
-       WARN_ON(!hlist_unhashed(&dentry->d_alias));
        if (dname_external(dentry))
                kfree(dentry->d_name.name);
        kmem_cache_free(dentry_cache, dentry); 
@@ -229,6 +225,7 @@ static void __d_free(struct rcu_head *head)
  */
 static void d_free(struct dentry *dentry)
 {
+       WARN_ON(!hlist_unhashed(&dentry->d_u.d_alias));
        BUG_ON(dentry->d_count);
        this_cpu_dec(nr_dentry);
        if (dentry->d_op && dentry->d_op->d_release)
@@ -267,7 +264,7 @@ static void dentry_iput(struct dentry * dentry)
        struct inode *inode = dentry->d_inode;
        if (inode) {
                dentry->d_inode = NULL;
-               hlist_del_init(&dentry->d_alias);
+               hlist_del_init(&dentry->d_u.d_alias);
                spin_unlock(&dentry->d_lock);
                spin_unlock(&inode->i_lock);
                if (!inode->i_nlink)
@@ -291,7 +288,7 @@ static void dentry_unlink_inode(struct dentry * dentry)
 {
        struct inode *inode = dentry->d_inode;
        dentry->d_inode = NULL;
-       hlist_del_init(&dentry->d_alias);
+       hlist_del_init(&dentry->d_u.d_alias);
        dentry_rcuwalk_barrier(dentry);
        spin_unlock(&dentry->d_lock);
        spin_unlock(&inode->i_lock);
@@ -337,23 +334,6 @@ static void dentry_lru_del(struct dentry *dentry)
        }
 }
 
-/*
- * Remove a dentry that is unreferenced and about to be pruned
- * (unhashed and destroyed) from the LRU, and inform the file system.
- * This wrapper should be called _prior_ to unhashing a victim dentry.
- */
-static void dentry_lru_prune(struct dentry *dentry)
-{
-       if (!list_empty(&dentry->d_lru)) {
-               if (dentry->d_flags & DCACHE_OP_PRUNE)
-                       dentry->d_op->d_prune(dentry);
-
-               spin_lock(&dcache_lru_lock);
-               __dentry_lru_del(dentry);
-               spin_unlock(&dcache_lru_lock);
-       }
-}
-
 static void dentry_lru_move_list(struct dentry *dentry, struct list_head *list)
 {
        spin_lock(&dcache_lru_lock);
@@ -384,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_u.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;
@@ -486,11 +466,13 @@ relock:
        if (ref)
                dentry->d_count--;
        /*
-        * if dentry was on the d_lru list delete it from there.
         * inform the fs via d_prune that this dentry is about to be
         * unhashed and destroyed.
         */
-       dentry_lru_prune(dentry);
+       if (dentry->d_flags & DCACHE_OP_PRUNE)
+               dentry->d_op->d_prune(dentry);
+
+       dentry_lru_del(dentry);
        /* if it was on the hash then remove it */
        __d_drop(dentry);
        return d_kill(dentry, parent);
@@ -538,6 +520,9 @@ repeat:
                return;
        }
 
+       if (unlikely(dentry->d_flags & DCACHE_DISCONNECTED))
+               goto kill_it;
+
        if (dentry->d_flags & DCACHE_OP_DELETE) {
                if (dentry->d_op->d_delete(dentry))
                        goto kill_it;
@@ -675,11 +660,10 @@ EXPORT_SYMBOL(dget_parent);
 static struct dentry *__d_find_alias(struct inode *inode, int want_discon)
 {
        struct dentry *alias, *discon_alias;
-       struct hlist_node *p;
 
 again:
        discon_alias = NULL;
-       hlist_for_each_entry(alias, p, &inode->i_dentry, d_alias) {
+       hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) {
                spin_lock(&alias->d_lock);
                if (S_ISDIR(inode->i_mode) || !d_unhashed(alias)) {
                        if (IS_ROOT(alias) &&
@@ -730,10 +714,9 @@ EXPORT_SYMBOL(d_find_alias);
 void d_prune_aliases(struct inode *inode)
 {
        struct dentry *dentry;
-       struct hlist_node *p;
 restart:
        spin_lock(&inode->i_lock);
-       hlist_for_each_entry(dentry, p, &inode->i_dentry, d_alias) {
+       hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) {
                spin_lock(&dentry->d_lock);
                if (!dentry->d_count) {
                        __dget_dlock(dentry);
@@ -913,7 +896,7 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
                /* descend to the first leaf in the current subtree */
                while (!list_empty(&dentry->d_subdirs))
                        dentry = list_entry(dentry->d_subdirs.next,
-                                           struct dentry, d_u.d_child);
+                                           struct dentry, d_child);
 
                /* consume the dentries from this leaf up through its parents
                 * until we find one with children or run out altogether */
@@ -921,11 +904,13 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
                        struct inode *inode;
 
                        /*
-                        * remove the dentry from the lru, and inform
-                        * the fs that this dentry is about to be
+                        * inform the fs that this dentry is about to be
                         * unhashed and destroyed.
                         */
-                       dentry_lru_prune(dentry);
+                       if (dentry->d_flags & DCACHE_OP_PRUNE)
+                               dentry->d_op->d_prune(dentry);
+
+                       dentry_lru_del(dentry);
                        __d_shrink(dentry);
 
                        if (dentry->d_count != 0) {
@@ -945,17 +930,17 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
 
                        if (IS_ROOT(dentry)) {
                                parent = NULL;
-                               list_del(&dentry->d_u.d_child);
+                               list_del(&dentry->d_child);
                        } else {
                                parent = dentry->d_parent;
                                parent->d_count--;
-                               list_del(&dentry->d_u.d_child);
+                               list_del(&dentry->d_child);
                        }
 
                        inode = dentry->d_inode;
                        if (inode) {
                                dentry->d_inode = NULL;
-                               hlist_del_init(&dentry->d_alias);
+                               hlist_del_init(&dentry->d_u.d_alias);
                                if (dentry->d_op && dentry->d_op->d_iput)
                                        dentry->d_op->d_iput(dentry, inode);
                                else
@@ -973,7 +958,7 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
                } while (list_empty(&dentry->d_subdirs));
 
                dentry = list_entry(dentry->d_subdirs.next,
-                                   struct dentry, d_u.d_child);
+                                   struct dentry, d_child);
        }
 }
 
@@ -1006,35 +991,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
  * list is non-empty and continue searching.
@@ -1066,7 +1022,7 @@ repeat:
 resume:
        while (next != &this_parent->d_subdirs) {
                struct list_head *tmp = next;
-               struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child);
+               struct dentry *dentry = list_entry(tmp, struct dentry, d_child);
                next = tmp->next;
 
                spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
@@ -1088,30 +1044,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_u.d_child.next;
+               /* go into the first sibling still alive */
+               do {
+                       next = child->d_child.next;
+                       if (next == &this_parent->d_subdirs)
+                               goto ascend;
+                       child = list_entry(next, struct dentry, d_child);
+               } while (unlikely(child->d_flags & DCACHE_DENTRY_KILLED));
+               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;
@@ -1149,7 +1123,7 @@ repeat:
 resume:
        while (next != &this_parent->d_subdirs) {
                struct list_head *tmp = next;
-               struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child);
+               struct dentry *dentry = list_entry(tmp, struct dentry, d_child);
                next = tmp->next;
 
                spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
@@ -1176,6 +1150,7 @@ resume:
                 */
                if (found && need_resched()) {
                        spin_unlock(&dentry->d_lock);
+                       rcu_read_lock();
                        goto out;
                }
 
@@ -1195,23 +1170,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_u.d_child.next;
+               /* go into the first sibling still alive */
+               do {
+                       next = child->d_child.next;
+                       if (next == &this_parent->d_subdirs)
+                               goto ascend;
+                       child = list_entry(next, struct dentry, d_child);
+               } while (unlikely(child->d_flags & DCACHE_DENTRY_KILLED));
+               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)
@@ -1232,8 +1224,10 @@ void shrink_dcache_parent(struct dentry * parent)
        LIST_HEAD(dispose);
        int found;
 
-       while ((found = select_parent(parent, &dispose)) != 0)
+       while ((found = select_parent(parent, &dispose)) != 0) {
                shrink_dentry_list(&dispose);
+               cond_resched();
+       }
 }
 EXPORT_SYMBOL(shrink_dcache_parent);
 
@@ -1294,8 +1288,8 @@ struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
        INIT_HLIST_BL_NODE(&dentry->d_hash);
        INIT_LIST_HEAD(&dentry->d_lru);
        INIT_LIST_HEAD(&dentry->d_subdirs);
-       INIT_HLIST_NODE(&dentry->d_alias);
-       INIT_LIST_HEAD(&dentry->d_u.d_child);
+       INIT_HLIST_NODE(&dentry->d_u.d_alias);
+       INIT_LIST_HEAD(&dentry->d_child);
        d_set_d_op(dentry, dentry->d_sb->s_d_op);
 
        this_cpu_inc(nr_dentry);
@@ -1325,7 +1319,7 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
         */
        __dget_dlock(parent);
        dentry->d_parent = parent;
-       list_add(&dentry->d_u.d_child, &parent->d_subdirs);
+       list_add(&dentry->d_child, &parent->d_subdirs);
        spin_unlock(&parent->d_lock);
 
        return dentry;
@@ -1358,6 +1352,7 @@ void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op)
        WARN_ON_ONCE(dentry->d_flags & (DCACHE_OP_HASH  |
                                DCACHE_OP_COMPARE       |
                                DCACHE_OP_REVALIDATE    |
+                               DCACHE_OP_WEAK_REVALIDATE       |
                                DCACHE_OP_DELETE ));
        dentry->d_op = op;
        if (!op)
@@ -1368,6 +1363,8 @@ void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op)
                dentry->d_flags |= DCACHE_OP_COMPARE;
        if (op->d_revalidate)
                dentry->d_flags |= DCACHE_OP_REVALIDATE;
+       if (op->d_weak_revalidate)
+               dentry->d_flags |= DCACHE_OP_WEAK_REVALIDATE;
        if (op->d_delete)
                dentry->d_flags |= DCACHE_OP_DELETE;
        if (op->d_prune)
@@ -1382,7 +1379,7 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
        if (inode) {
                if (unlikely(IS_AUTOMOUNT(inode)))
                        dentry->d_flags |= DCACHE_NEED_AUTOMOUNT;
-               hlist_add_head(&dentry->d_alias, &inode->i_dentry);
+               hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry);
        }
        dentry->d_inode = inode;
        dentry_rcuwalk_barrier(dentry);
@@ -1407,7 +1404,7 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
  
 void d_instantiate(struct dentry *entry, struct inode * inode)
 {
-       BUG_ON(!hlist_unhashed(&entry->d_alias));
+       BUG_ON(!hlist_unhashed(&entry->d_u.d_alias));
        if (inode)
                spin_lock(&inode->i_lock);
        __d_instantiate(entry, inode);
@@ -1440,14 +1437,13 @@ static struct dentry *__d_instantiate_unique(struct dentry *entry,
        int len = entry->d_name.len;
        const char *name = entry->d_name.name;
        unsigned int hash = entry->d_name.hash;
-       struct hlist_node *p;
 
        if (!inode) {
                __d_instantiate(entry, NULL);
                return NULL;
        }
 
-       hlist_for_each_entry(alias, p, &inode->i_dentry, d_alias) {
+       hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) {
                /*
                 * Don't need alias->d_lock here, because aliases with
                 * d_parent == entry->d_parent are not subject to name or
@@ -1473,7 +1469,7 @@ struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode)
 {
        struct dentry *result;
 
-       BUG_ON(!hlist_unhashed(&entry->d_alias));
+       BUG_ON(!hlist_unhashed(&entry->d_u.d_alias));
 
        if (inode)
                spin_lock(&inode->i_lock);
@@ -1516,7 +1512,7 @@ static struct dentry * __d_find_any_alias(struct inode *inode)
 
        if (hlist_empty(&inode->i_dentry))
                return NULL;
-       alias = hlist_entry(inode->i_dentry.first, struct dentry, d_alias);
+       alias = hlist_entry(inode->i_dentry.first, struct dentry, d_u.d_alias);
        __dget(alias);
        return alias;
 }
@@ -1590,7 +1586,7 @@ struct dentry *d_obtain_alias(struct inode *inode)
        spin_lock(&tmp->d_lock);
        tmp->d_inode = inode;
        tmp->d_flags |= DCACHE_DISCONNECTED;
-       hlist_add_head(&tmp->d_alias, &inode->i_dentry);
+       hlist_add_head(&tmp->d_u.d_alias, &inode->i_dentry);
        hlist_bl_lock(&tmp->d_sb->s_anon);
        hlist_bl_add_head(&tmp->d_hash, &tmp->d_sb->s_anon);
        hlist_bl_unlock(&tmp->d_sb->s_anon);
@@ -1672,7 +1668,6 @@ EXPORT_SYMBOL(d_splice_alias);
 struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode,
                        struct qstr *name)
 {
-       int error;
        struct dentry *found;
        struct dentry *new;
 
@@ -1681,10 +1676,12 @@ struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode,
         * if not go ahead and create it now.
         */
        found = d_hash_and_lookup(dentry->d_parent, name);
+       if (unlikely(IS_ERR(found)))
+               goto err_out;
        if (!found) {
                new = d_alloc(dentry->d_parent, name);
                if (!new) {
-                       error = -ENOMEM;
+                       found = ERR_PTR(-ENOMEM);
                        goto err_out;
                }
 
@@ -1725,7 +1722,7 @@ struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode,
 
 err_out:
        iput(inode);
-       return ERR_PTR(error);
+       return found;
 }
 EXPORT_SYMBOL(d_add_ci);
 
@@ -1889,7 +1886,7 @@ seqretry:
  * dentry is returned. The caller must use dput to free the entry when it has
  * finished using it. %NULL is returned if the dentry does not exist.
  */
-struct dentry *d_lookup(struct dentry *parent, struct qstr *name)
+struct dentry *d_lookup(const struct dentry *parent, const struct qstr *name)
 {
        struct dentry *dentry;
        unsigned seq;
@@ -1997,12 +1994,10 @@ next:
  * @dir: Directory to search in
  * @name: qstr of name we wish to find
  *
- * On hash failure or on lookup failure NULL is returned.
+ * On lookup failure NULL is returned; on bad name - ERR_PTR(-error)
  */
 struct dentry *d_hash_and_lookup(struct dentry *dir, struct qstr *name)
 {
-       struct dentry *dentry = NULL;
-
        /*
         * Check for a fs-specific hash function. Note that we must
         * calculate the standard hash first, as the d_op->d_hash()
@@ -2010,13 +2005,13 @@ struct dentry *d_hash_and_lookup(struct dentry *dir, struct qstr *name)
         */
        name->hash = full_name_hash(name->name, name->len);
        if (dir->d_flags & DCACHE_OP_HASH) {
-               if (dir->d_op->d_hash(dir, dir->d_inode, name) < 0)
-                       goto out;
+               int err = dir->d_op->d_hash(dir, dir->d_inode, name);
+               if (unlikely(err < 0))
+                       return ERR_PTR(err);
        }
-       dentry = d_lookup(dir, name);
-out:
-       return dentry;
+       return d_lookup(dir, name);
 }
+EXPORT_SYMBOL(d_hash_and_lookup);
 
 /**
  * d_validate - verify dentry provided from insecure source (deprecated)
@@ -2034,7 +2029,7 @@ int d_validate(struct dentry *dentry, struct dentry *dparent)
        struct dentry *child;
 
        spin_lock(&dparent->d_lock);
-       list_for_each_entry(child, &dparent->d_subdirs, d_u.d_child) {
+       list_for_each_entry(child, &dparent->d_subdirs, d_child) {
                if (dentry == child) {
                        spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
                        __dget_dlock(dentry);
@@ -2281,8 +2276,8 @@ static void __d_move(struct dentry * dentry, struct dentry * target)
        /* Unhash the target: dput() will then get rid of it */
        __d_drop(target);
 
-       list_del(&dentry->d_u.d_child);
-       list_del(&target->d_u.d_child);
+       list_del(&dentry->d_child);
+       list_del(&target->d_child);
 
        /* Switch the names.. */
        switch_names(dentry, target);
@@ -2292,15 +2287,15 @@ static void __d_move(struct dentry * dentry, struct dentry * target)
        if (IS_ROOT(dentry)) {
                dentry->d_parent = target->d_parent;
                target->d_parent = target;
-               INIT_LIST_HEAD(&target->d_u.d_child);
+               INIT_LIST_HEAD(&target->d_child);
        } else {
                swap(dentry->d_parent, target->d_parent);
 
                /* And add them back to the (new) parent lists */
-               list_add(&target->d_u.d_child, &target->d_parent->d_subdirs);
+               list_add(&target->d_child, &target->d_parent->d_subdirs);
        }
 
-       list_add(&dentry->d_u.d_child, &dentry->d_parent->d_subdirs);
+       list_add(&dentry->d_child, &dentry->d_parent->d_subdirs);
 
        write_seqcount_end(&target->d_seq);
        write_seqcount_end(&dentry->d_seq);
@@ -2394,7 +2389,7 @@ out_err:
  */
 static void __d_materialise_dentry(struct dentry *dentry, struct dentry *anon)
 {
-       struct dentry *dparent, *aparent;
+       struct dentry *dparent;
 
        dentry_lock_for_move(anon, dentry);
 
@@ -2402,24 +2397,14 @@ static void __d_materialise_dentry(struct dentry *dentry, struct dentry *anon)
        write_seqcount_begin(&anon->d_seq);
 
        dparent = dentry->d_parent;
-       aparent = anon->d_parent;
 
        switch_names(dentry, anon);
        swap(dentry->d_name.hash, anon->d_name.hash);
 
-       dentry->d_parent = (aparent == anon) ? dentry : aparent;
-       list_del(&dentry->d_u.d_child);
-       if (!IS_ROOT(dentry))
-               list_add(&dentry->d_u.d_child, &dentry->d_parent->d_subdirs);
-       else
-               INIT_LIST_HEAD(&dentry->d_u.d_child);
-
-       anon->d_parent = (dparent == dentry) ? anon : dparent;
-       list_del(&anon->d_u.d_child);
-       if (!IS_ROOT(anon))
-               list_add(&anon->d_u.d_child, &anon->d_parent->d_subdirs);
-       else
-               INIT_LIST_HEAD(&anon->d_u.d_child);
+       dentry->d_parent = dentry;
+       list_del_init(&dentry->d_child);
+       anon->d_parent = dparent;
+       list_move(&anon->d_child, &dparent->d_subdirs);
 
        write_seqcount_end(&dentry->d_seq);
        write_seqcount_end(&anon->d_seq);
@@ -2549,14 +2534,23 @@ static int prepend_path(const struct path *path,
        struct dentry *dentry = path->dentry;
        struct vfsmount *vfsmnt = path->mnt;
        struct mount *mnt = real_mount(vfsmnt);
+       char *orig_buffer = *buffer;
+       int orig_len = *buflen;
        bool slash = false;
        int error = 0;
 
-       br_read_lock(&vfsmount_lock);
        while (dentry != root->dentry || vfsmnt != root->mnt) {
                struct dentry * parent;
 
                if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
+                       /* Escaped? */
+                       if (dentry != vfsmnt->mnt_root) {
+                               *buffer = orig_buffer;
+                               *buflen = orig_len;
+                               slash = false;
+                               error = 3;
+                               goto global_root;
+                       }
                        /* Global root? */
                        if (!mnt_has_parent(mnt))
                                goto global_root;
@@ -2582,25 +2576,14 @@ static int prepend_path(const struct path *path,
        if (!error && !slash)
                error = prepend(buffer, buflen, "/", 1);
 
-out:
-       br_read_unlock(&vfsmount_lock);
        return error;
 
 global_root:
-       /*
-        * Filesystems needing to implement special "root names"
-        * should do so with ->d_dname()
-        */
-       if (IS_ROOT(dentry) &&
-           (dentry->d_name.len != 1 || dentry->d_name.name[0] != '/')) {
-               WARN(1, "Root dentry has weird name <%.*s>\n",
-                    (int) dentry->d_name.len, dentry->d_name.name);
-       }
        if (!slash)
                error = prepend(buffer, buflen, "/", 1);
        if (!error)
                error = is_mounted(vfsmnt) ? 1 : 2;
-       goto out;
+       return error;
 }
 
 /**
@@ -2627,9 +2610,11 @@ char *__d_path(const struct path *path,
        int error;
 
        prepend(&res, &buflen, "\0", 1);
+       br_read_lock(&vfsmount_lock);
        write_seqlock(&rename_lock);
        error = prepend_path(path, root, &res, &buflen);
        write_sequnlock(&rename_lock);
+       br_read_unlock(&vfsmount_lock);
 
        if (error < 0)
                return ERR_PTR(error);
@@ -2646,9 +2631,11 @@ char *d_absolute_path(const struct path *path,
        int error;
 
        prepend(&res, &buflen, "\0", 1);
+       br_read_lock(&vfsmount_lock);
        write_seqlock(&rename_lock);
        error = prepend_path(path, &root, &res, &buflen);
        write_sequnlock(&rename_lock);
+       br_read_unlock(&vfsmount_lock);
 
        if (error > 1)
                error = -EINVAL;
@@ -2707,16 +2694,23 @@ char *d_path(const struct path *path, char *buf, int buflen)
         * thus don't need to be hashed.  They also don't need a name until a
         * user wants to identify the object in /proc/pid/fd/.  The little hack
         * below allows us to generate a name for these objects on demand:
+        *
+        * Some pseudo inodes are mountable.  When they are mounted
+        * path->dentry == path->mnt->mnt_root.  In that case don't call d_dname
+        * and instead have d_path return the mounted path.
         */
-       if (path->dentry->d_op && path->dentry->d_op->d_dname)
+       if (path->dentry->d_op && path->dentry->d_op->d_dname &&
+           (!IS_ROOT(path->dentry) || path->dentry != path->mnt->mnt_root))
                return path->dentry->d_op->d_dname(path->dentry, buf, buflen);
 
        get_fs_root(current->fs, &root);
+       br_read_lock(&vfsmount_lock);
        write_seqlock(&rename_lock);
        error = path_with_deleted(path, &root, &res, &buflen);
+       write_sequnlock(&rename_lock);
+       br_read_unlock(&vfsmount_lock);
        if (error < 0)
                res = ERR_PTR(error);
-       write_sequnlock(&rename_lock);
        path_put(&root);
        return res;
 }
@@ -2743,6 +2737,17 @@ char *dynamic_dname(struct dentry *dentry, char *buffer, int buflen,
        return memcpy(buffer, temp, sz);
 }
 
+char *simple_dname(struct dentry *dentry, char *buffer, int buflen)
+{
+       char *end = buffer + buflen;
+       /* these dentries are never renamed, so d_lock is not needed */
+       if (prepend(&end, &buflen, " (deleted)", 11) ||
+           prepend_name(&end, &buflen, &dentry->d_name) ||
+           prepend(&end, &buflen, "/", 1))
+               end = ERR_PTR(-ENAMETOOLONG);
+       return end;
+}
+
 /*
  * Write full pathname from the root of the filesystem into the buffer.
  */
@@ -2840,6 +2845,7 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
        get_fs_root_and_pwd(current->fs, &root, &pwd);
 
        error = -ENOENT;
+       br_read_lock(&vfsmount_lock);
        write_seqlock(&rename_lock);
        if (!d_unlinked(pwd.dentry)) {
                unsigned long len;
@@ -2849,6 +2855,7 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
                prepend(&cwd, &buflen, "\0", 1);
                error = prepend_path(&pwd, &root, &cwd, &buflen);
                write_sequnlock(&rename_lock);
+               br_read_unlock(&vfsmount_lock);
 
                if (error < 0)
                        goto out;
@@ -2869,6 +2876,7 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
                }
        } else {
                write_sequnlock(&rename_lock);
+               br_read_unlock(&vfsmount_lock);
        }
 
 out:
@@ -2936,7 +2944,7 @@ repeat:
 resume:
        while (next != &this_parent->d_subdirs) {
                struct list_head *tmp = next;
-               struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child);
+               struct dentry *dentry = list_entry(tmp, struct dentry, d_child);
                next = tmp->next;
 
                spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
@@ -2957,26 +2965,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_u.d_child.next;
+               /* go into the first sibling still alive */
+               do {
+                       next = child->d_child.next;
+                       if (next == &this_parent->d_subdirs)
+                               goto ascend;
+                       child = list_entry(next, struct dentry, d_child);
+               } while (unlikely(child->d_flags & DCACHE_DENTRY_KILLED));
+               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;
@@ -3004,7 +3029,7 @@ ino_t find_inode_number(struct dentry *dir, struct qstr *name)
        ino_t ino = 0;
 
        dentry = d_hash_and_lookup(dir, name);
-       if (dentry) {
+       if (!IS_ERR_OR_NULL(dentry)) {
                if (dentry->d_inode)
                        ino = dentry->d_inode->i_ino;
                dput(dentry);