return false;
 }
 
-static inline bool retain_dentry(struct dentry *dentry)
+/*
+ * Decide if dentry is worth retaining.  Usually this is called with dentry
+ * locked; if not locked, we are more limited and might not be able to tell
+ * without a lock.  False in this case means "punt to locked path and recheck".
+ *
+ * In case we aren't locked, these predicates are not "stable". However, it is
+ * sufficient that at some point after we dropped the reference the dentry was
+ * hashed and the flags had the proper value. Other dentry users may have
+ * re-gotten a reference to the dentry and change that, but our work is done -
+ * we can leave the dentry around with a zero refcount.
+ */
+static inline bool retain_dentry(struct dentry *dentry, bool locked)
 {
-       WARN_ON(d_in_lookup(dentry));
+       unsigned int d_flags;
 
-       /* Unreachable? Get rid of it */
+       smp_rmb();
+       d_flags = READ_ONCE(dentry->d_flags);
+
+       // Unreachable? Nobody would be able to look it up, no point retaining
        if (unlikely(d_unhashed(dentry)))
                return false;
 
-       if (unlikely(dentry->d_flags & DCACHE_DISCONNECTED))
+       // Same if it's disconnected
+       if (unlikely(d_flags & DCACHE_DISCONNECTED))
                return false;
 
-       if (unlikely(dentry->d_flags & DCACHE_OP_DELETE)) {
-               if (dentry->d_op->d_delete(dentry))
+       // ->d_delete() might tell us not to bother, but that requires
+       // ->d_lock; can't decide without it
+       if (unlikely(d_flags & DCACHE_OP_DELETE)) {
+               if (!locked || dentry->d_op->d_delete(dentry))
                        return false;
        }
 
-       if (unlikely(dentry->d_flags & DCACHE_DONTCACHE))
+       // Explicitly told not to bother
+       if (unlikely(d_flags & DCACHE_DONTCACHE))
                return false;
 
-       /* retain; LRU fodder */
-       if (unlikely(!(dentry->d_flags & DCACHE_LRU_LIST)))
+       // At this point it looks like we ought to keep it.  We also might
+       // need to do something - put it on LRU if it wasn't there already
+       // and mark it referenced if it was on LRU, but not marked yet.
+       // Unfortunately, both actions require ->d_lock, so in lockless
+       // case we'd have to punt rather than doing those.
+       if (unlikely(!(d_flags & DCACHE_LRU_LIST))) {
+               if (!locked)
+                       return false;
                d_lru_add(dentry);
-       else if (unlikely(!(dentry->d_flags & DCACHE_REFERENCED)))
+       } else if (unlikely(!(d_flags & DCACHE_REFERENCED))) {
+               if (!locked)
+                       return false;
                dentry->d_flags |= DCACHE_REFERENCED;
+       }
        return true;
 }
 
 static inline bool fast_dput(struct dentry *dentry)
 {
        int ret;
-       unsigned int d_flags;
 
        /*
         * try to decrement the lockref optimistically.
                return true;
 
        /*
-        * Careful, careful. The reference count went down
-        * to zero, but we don't hold the dentry lock, so
-        * somebody else could get it again, and do another
-        * dput(), and we need to not race with that.
-        *
-        * However, there is a very special and common case
-        * where we don't care, because there is nothing to
-        * do: the dentry is still hashed, it does not have
-        * a 'delete' op, and it's referenced and already on
-        * the LRU list.
-        *
-        * NOTE! Since we aren't locked, these values are
-        * not "stable". However, it is sufficient that at
-        * some point after we dropped the reference the
-        * dentry was hashed and the flags had the proper
-        * value. Other dentry users may have re-gotten
-        * a reference to the dentry and change that, but
-        * our work is done - we can leave the dentry
-        * around with a zero refcount.
-        *
-        * Nevertheless, there are two cases that we should kill
-        * the dentry anyway.
-        * 1. free disconnected dentries as soon as their refcount
-        *    reached zero.
-        * 2. free dentries if they should not be cached.
+        * Can we decide that decrement of refcount is all we needed without
+        * taking the lock?  There's a very common case when it's all we need -
+        * dentry looks like it ought to be retained and there's nothing else
+        * to do.
         */
-       smp_rmb();
-       d_flags = READ_ONCE(dentry->d_flags);
-       d_flags &= DCACHE_REFERENCED | DCACHE_LRU_LIST | DCACHE_OP_DELETE |
-                       DCACHE_DISCONNECTED | DCACHE_DONTCACHE;
-
-       /* Nothing to do? Dropping the reference was all we needed? */
-       if (d_flags == (DCACHE_REFERENCED | DCACHE_LRU_LIST) && !d_unhashed(dentry))
+       if (retain_dentry(dentry, false))
                return true;
 
        /*
-        * Not the fast normal case? Get the lock. We've already decremented
-        * the refcount, but we'll need to re-check the situation after
-        * getting the lock.
+        * Either not worth retaining or we can't tell without the lock.
+        * Get the lock, then.  We've already decremented the refcount to 0,
+        * but we'll need to re-check the situation after getting the lock.
         */
        spin_lock(&dentry->d_lock);
 
         * don't need to do anything else.
         */
 locked:
-       if (dentry->d_lockref.count || retain_dentry(dentry)) {
+       if (dentry->d_lockref.count || retain_dentry(dentry, true)) {
                spin_unlock(&dentry->d_lock);
                return true;
        }
                dentry = __dentry_kill(dentry);
                if (!dentry)
                        return;
-               if (retain_dentry(dentry)) {
+               if (retain_dentry(dentry, true)) {
                        spin_unlock(&dentry->d_lock);
                        return;
                }