diff --git a/fs/dcache.c b/fs/dcache.c
index 37c0f655166d..e705696ca57e 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1675,9 +1675,10 @@ EXPORT_SYMBOL(d_invalidate);
  * available. On a success the dentry is returned. The name passed in is
  * copied and the copy passed in may be reused after this call.
  */
- 
+
 static struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
 {
+	static struct lock_class_key __key;
 	struct dentry *dentry;
 	char *dname;
 	int err;
@@ -1735,6 +1736,8 @@ static struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
 	INIT_HLIST_NODE(&dentry->d_sib);
 	d_set_d_op(dentry, dentry->d_sb->s_d_op);
 
+	lockdep_init_map(&dentry->d_update_map, "DCACHE_PAR_UPDATE", &__key, 0);
+
 	if (dentry->d_op && dentry->d_op->d_init) {
 		err = dentry->d_op->d_init(dentry);
 		if (err) {
diff --git a/fs/internal.h b/fs/internal.h
index e7f02ae1e098..5cb9a34e26e8 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -225,6 +225,24 @@ extern struct dentry *__d_lookup_rcu(const struct dentry *parent,
 				const struct qstr *name, unsigned *seq);
 extern void d_genocide(struct dentry *);
 
+extern bool d_update_lock(struct dentry *dentry,
+			  struct dentry *base, const struct qstr *last,
+			  unsigned int subclass);
+
+extern bool d_update_trylock(struct dentry *dentry,
+			     struct dentry *base,
+			     const struct qstr *last);
+
+static inline void d_update_unlock(struct dentry *dentry)
+{
+	lock_map_release(&dentry->d_update_map);
+	spin_lock(&dentry->d_lock);
+	if (dentry->d_flags & DCACHE_PAR_WAITER)
+		wake_up_var_locked(&dentry->d_flags, &dentry->d_lock);
+	dentry->d_flags &= ~(DCACHE_PAR_UPDATE | DCACHE_PAR_WAITER);
+	spin_unlock(&dentry->d_lock);
+}
+
 /*
  * pipe.c
  */
diff --git a/fs/namei.c b/fs/namei.c
index eadde9de73bf..145ae07f9b8c 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1750,6 +1750,110 @@ struct dentry *lookup_one_qstr(const struct qstr *name,
 }
 EXPORT_SYMBOL(lookup_one_qstr);
 
+/*
+ * dentry locking for updates.
+ * When modifying a directory the target dentry will be locked by
+ * setting DCACHE_PAR_UPDATE under ->d_lock.  If it is already set,
+ * DCACHE_PAR_WAITER is set to ensure a wakeup is sent, and we wait
+ * using wait_var_event_spinlock().
+ * The DCACHE_PAR_UPDATE bit will only be set in a denty if it is
+ * NOT set in the parent.  This avoids commensing a new operation in
+ * a directory that is being asynchronously deleted using ->mkdir_async.
+ * Instead of holding ->d_lock on the parent while testing the flag, we
+ * use memory ordering to ensure correctness.  Locking a child
+ * retests the parent *after* setting the bit, and deleting a directory
+ * requires testing all children *after* setting the bit in the parent.
+ */
+
+static bool check_dentry_locked(struct dentry *de)
+{
+	if (de->d_flags & DCACHE_PAR_UPDATE) {
+		de->d_flags |= DCACHE_PAR_WAITER;
+		return true;
+	}
+	return false;
+}
+
+bool d_update_lock(struct dentry *dentry,
+		   struct dentry *base, const struct qstr *last,
+		   unsigned int subclass)
+{
+	lock_acquire_exclusive(&dentry->d_update_map, subclass, 0, NULL, _THIS_IP_);
+again:
+	spin_lock(&dentry->d_lock);
+	wait_var_event_spinlock(&dentry->d_flags,
+				!check_dentry_locked(dentry),
+				&dentry->d_lock);
+	if (d_is_positive(dentry)) {
+		rcu_read_lock(); /* needed for d_same_name() */
+		if (
+			/* Was unlinked while we waited ?*/
+			d_unhashed(dentry) ||
+			/* Or was dentry renamed ?? */
+			dentry->d_parent != base ||
+			dentry->d_name.hash != last->hash ||
+			!d_same_name(dentry, base, last)
+		) {
+			rcu_read_unlock();
+			spin_unlock(&dentry->d_lock);
+			lock_map_release(&dentry->d_update_map);
+			return false;
+		}
+		rcu_read_unlock();
+	}
+	/* Must ensure DCACHE_PAR_UPDATE in child is visible before reading
+	 * from parent
+	 */
+	smp_store_mb(dentry->d_flags, dentry->d_flags | DCACHE_PAR_UPDATE);
+	if (base->d_flags & DCACHE_PAR_UPDATE) {
+		/* We cannot grant DCACHE_PAR_UPDATE on a dentry while
+		 * it is held on the parent
+		 */
+		dentry->d_flags &= ~DCACHE_PAR_UPDATE;
+		spin_unlock(&dentry->d_lock);
+		spin_lock(&base->d_lock);
+		wait_var_event_spinlock(&base->d_flags,
+					!check_dentry_locked(base),
+					&base->d_lock);
+		spin_unlock(&base->d_lock);
+		goto again;
+	}
+	spin_unlock(&dentry->d_lock);
+	return true;
+}
+
+bool d_update_trylock(struct dentry *dentry,
+		      struct dentry *base,
+		      const struct qstr *last)
+{
+	int ret = false;
+
+	spin_lock(&dentry->d_lock);
+	rcu_read_lock(); /* needed for d_same_name() */
+	if (!(smp_load_acquire(&dentry->d_flags) & DCACHE_PAR_UPDATE) &&
+	    !(dentry->d_parent->d_flags & DCACHE_PAR_UPDATE)) {
+		if (!base || !(
+			/* Was unlinked before we got spinlock ?*/
+			d_unhashed(dentry) ||
+			/* Or was dentry renamed ?? */
+			dentry->d_parent != base ||
+			dentry->d_name.hash != last->hash ||
+			!d_same_name(dentry, base, last)
+		)) {
+			lock_map_acquire_try(&dentry->d_update_map);
+			smp_store_mb(dentry->d_flags,
+				     dentry->d_flags | DCACHE_PAR_UPDATE);
+			if (dentry->d_parent->d_flags & DCACHE_PAR_UPDATE)
+				dentry->d_flags &= ~DCACHE_PAR_UPDATE;
+			else
+				ret = true;
+		}
+	}
+	rcu_read_unlock();
+	spin_unlock(&dentry->d_lock);
+	return ret;
+}
+
 static struct dentry *lookup_and_lock_nested(const struct qstr *last,
 					     struct dentry *base,
 					     unsigned int lookup_flags,
@@ -1759,8 +1863,9 @@ static struct dentry *lookup_and_lock_nested(const struct qstr *last,
 
 	if (!(lookup_flags & LOOKUP_PARENT_LOCKED))
 		inode_lock_nested(base->d_inode, subclass);
-
-	dentry = lookup_one_qstr(last, base, lookup_flags);
+	do {
+		dentry = lookup_one_qstr(last, base, lookup_flags);
+	} while (!IS_ERR(dentry) && !d_update_lock(dentry, base, last, subclass));
 	if (IS_ERR(dentry) && !(lookup_flags & LOOKUP_PARENT_LOCKED)) {
 			inode_unlock(base->d_inode);
 	}
@@ -1779,6 +1884,7 @@ void done_lookup_and_lock(struct dentry *base, struct dentry *dentry,
 			  unsigned int lookup_flags)
 {
 	d_lookup_done(dentry);
+	d_update_unlock(dentry);
 	dput(dentry);
 	if (!(lookup_flags & LOOKUP_PARENT_LOCKED))
 		inode_unlock(base->d_inode);
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index d5816cf19538..f891fb1be63b 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -111,6 +111,8 @@ struct dentry {
 					 * possible!
 					 */
 
+	/* lockdep tracking of DCACHE_PAR_UPDATE locks */
+	struct lockdep_map		d_update_map;
 	union {
 		struct list_head d_lru;		/* LRU list */
 		wait_queue_head_t *d_wait;	/* in-lookup ones only */
@@ -232,6 +234,8 @@ struct dentry_operations {
 #define DCACHE_DENTRY_CURSOR		BIT(25)
 #define DCACHE_NORCU			BIT(26) /* No RCU delay for freeing */
 
+#define DCACHE_PAR_UPDATE		BIT(27) /* Locked for update */
+#define DCACHE_PAR_WAITER		BIT(28) /* someone is waiting for PAR_UPDATE */
 extern seqlock_t rename_lock;
 
 /*
