Btrfs: Invalidate dcache entry after creating snapshot and

We need to invalidate an existing dcache entry after creating a new
snapshot or subvolume, because a negative dache entry will stop us from
accessing the new snapshot or subvolume.

---
  ctree.h       |   23 +++++++++++++++++++++++
  inode.c       |    4 ++++
  transaction.c |    4 ++++
  3 files changed, 31 insertions(+)

Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 7b73a9c..ad4eacc 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -1544,6 +1544,9 @@
 			struct btrfs_root *root, struct btrfs_path *path,
 			u64 isize);
 /* inode.c */
+void btrfs_invalidate_dcache_root(struct btrfs_root *root, char *name,
+				  int namelen);
+
 int btrfs_merge_bio_hook(struct page *page, unsigned long offset,
 			 size_t size, struct bio *bio);
 
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 1569fb8..31aa4ba 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -2642,6 +2642,31 @@
 	btrfs_throttle(root);
 }
 
+/*
+ * Invalidate a single dcache entry at the root of the filesystem.
+ * Needed after creation of snapshot or subvolume.
+ */
+void btrfs_invalidate_dcache_root(struct btrfs_root *root, char *name,
+				  int namelen)
+{
+	struct dentry *alias, *entry;
+	struct qstr qstr;
+
+	alias = d_find_alias(root->fs_info->sb->s_root->d_inode);
+	if (alias) {
+		qstr.name = name;
+		qstr.len = namelen;
+		/* change me if btrfs ever gets a d_hash operation */
+		qstr.hash = full_name_hash(qstr.name, qstr.len);
+		entry = d_lookup(alias, &qstr);
+		dput(alias);
+		if (entry) {
+			d_invalidate(entry);
+			dput(entry);
+		}
+	}
+}
+
 static int noinline create_subvol(struct btrfs_root *root, char *name,
 				  int namelen)
 {
@@ -2761,6 +2786,10 @@
 	ret = btrfs_update_inode(trans, new_root, inode);
 	if (ret)
 		goto fail;
+
+	/* Invalidate existing dcache entry for new subvolume. */
+	btrfs_invalidate_dcache_root(root, name, namelen);
+
 fail:
 	nr = trans->blocks_used;
 	err = btrfs_commit_transaction(trans, new_root);
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 1cb084e..f04684f 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -560,6 +560,7 @@
 	struct btrfs_root *root = pending->root;
 	struct extent_buffer *tmp;
 	int ret;
+	int namelen;
 	u64 objectid;
 
 	new_root_item = kmalloc(sizeof(*new_root_item), GFP_NOFS);
@@ -595,8 +596,9 @@
 	 * insert the directory item
 	 */
 	key.offset = (u64)-1;
+	namelen = strlen(pending->name);
 	ret = btrfs_insert_dir_item(trans, root->fs_info->tree_root,
-				    pending->name, strlen(pending->name),
+				    pending->name, namelen,
 				    root->fs_info->sb->s_root->d_inode->i_ino,
 				    &key, BTRFS_FT_DIR);
 
@@ -606,6 +608,10 @@
 	ret = btrfs_insert_inode_ref(trans, root->fs_info->tree_root,
 			     pending->name, strlen(pending->name), objectid,
 			     root->fs_info->sb->s_root->d_inode->i_ino);
+
+	/* Invalidate existing dcache entry for new snapshot. */
+	btrfs_invalidate_dcache_root(root, pending->name, namelen);
+
 fail:
 	kfree(new_root_item);
 	return ret;