Btrfs: add inodes before dropping the extent lock in find_all_leafs

We must build up the inode list with the extent lock held after following
indirect refs.

This also requires an extension to ulists, which allows to modify the stored
aux value in case a key already exists in the list.

Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net>
diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
index 0ac47f2..3f75895 100644
--- a/fs/btrfs/backref.c
+++ b/fs/btrfs/backref.c
@@ -106,6 +106,7 @@
 	struct btrfs_key key_for_search;
 	int level;
 	int count;
+	struct extent_inode_elem *inode_list;
 	u64 parent;
 	u64 wanted_disk_byte;
 };
@@ -166,6 +167,7 @@
 	else
 		memset(&ref->key_for_search, 0, sizeof(ref->key_for_search));
 
+	ref->inode_list = NULL;
 	ref->level = level;
 	ref->count = count;
 	ref->parent = parent;
@@ -181,14 +183,21 @@
 				const u64 *extent_item_pos)
 {
 	int ret;
-	int slot;
+	int slot = path->slots[level];
 	struct extent_buffer *eb = path->nodes[level];
 	struct btrfs_file_extent_item *fi;
+	struct extent_inode_elem *eie = NULL;
 	u64 disk_byte;
 	u64 wanted_objectid = key->objectid;
 
 add_parent:
-	ret = ulist_add(parents, eb->start, 0, GFP_NOFS);
+	if (level == 0 && extent_item_pos) {
+		fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
+		ret = check_extent_in_eb(key, eb, fi, *extent_item_pos, &eie);
+		if (ret < 0)
+			return ret;
+	}
+	ret = ulist_add(parents, eb->start, (unsigned long)eie, GFP_NOFS);
 	if (ret < 0)
 		return ret;
 
@@ -202,6 +211,7 @@
 	 * repeat this until we don't find any additional EXTENT_DATA items.
 	 */
 	while (1) {
+		eie = NULL;
 		ret = btrfs_next_leaf(root, path);
 		if (ret < 0)
 			return ret;
@@ -346,6 +356,8 @@
 		ULIST_ITER_INIT(&uiter);
 		node = ulist_next(parents, &uiter);
 		ref->parent = node ? node->val : 0;
+		ref->inode_list =
+			node ? (struct extent_inode_elem *)node->aux : 0;
 
 		/* additional parents require new refs being added here */
 		while ((node = ulist_next(parents, &uiter))) {
@@ -356,6 +368,8 @@
 			}
 			memcpy(new_ref, ref, sizeof(*ref));
 			new_ref->parent = node->val;
+			new_ref->inode_list =
+					(struct extent_inode_elem *)node->aux;
 			list_add(&new_ref->list, &ref->list);
 		}
 		ulist_reinit(parents);
@@ -879,7 +893,7 @@
 		}
 		if (ref->count && ref->parent) {
 			struct extent_inode_elem *eie = NULL;
-			if (extent_item_pos) {
+			if (extent_item_pos && !ref->inode_list) {
 				u32 bsz;
 				struct extent_buffer *eb;
 				bsz = btrfs_level_size(fs_info->extent_root,
@@ -889,10 +903,22 @@
 				BUG_ON(!eb);
 				ret = find_extent_in_eb(eb, bytenr,
 							*extent_item_pos, &eie);
+				ref->inode_list = eie;
 				free_extent_buffer(eb);
 			}
-			ret = ulist_add(refs, ref->parent,
-					(unsigned long)eie, GFP_NOFS);
+			ret = ulist_add_merge(refs, ref->parent,
+					      (unsigned long)ref->inode_list,
+					      (unsigned long *)&eie, GFP_NOFS);
+			if (!ret && extent_item_pos) {
+				/*
+				 * we've recorded that parent, so we must extend
+				 * its inode list here
+				 */
+				BUG_ON(!eie);
+				while (eie->next)
+					eie = eie->next;
+				eie->next = ref->inode_list;
+			}
 			BUG_ON(ret < 0);
 		}
 		kfree(ref);
diff --git a/fs/btrfs/ulist.c b/fs/btrfs/ulist.c
index 17e68bd..2ef5940 100644
--- a/fs/btrfs/ulist.c
+++ b/fs/btrfs/ulist.c
@@ -146,11 +146,20 @@
 int ulist_add(struct ulist *ulist, u64 val, unsigned long aux,
 	      unsigned long gfp_mask)
 {
+	return ulist_add_merge(ulist, val, aux, NULL, gfp_mask);
+}
+
+int ulist_add_merge(struct ulist *ulist, u64 val, unsigned long aux,
+		    unsigned long *old_aux, unsigned long gfp_mask)
+{
 	int i;
 
 	for (i = 0; i < ulist->nnodes; ++i) {
-		if (ulist->nodes[i].val == val)
+		if (ulist->nodes[i].val == val) {
+			if (old_aux)
+				*old_aux = ulist->nodes[i].aux;
 			return 0;
+		}
 	}
 
 	if (ulist->nnodes >= ulist->nodes_alloced) {
diff --git a/fs/btrfs/ulist.h b/fs/btrfs/ulist.h
index 62d2574..f1b1bf0 100644
--- a/fs/btrfs/ulist.h
+++ b/fs/btrfs/ulist.h
@@ -67,6 +67,8 @@
 void ulist_free(struct ulist *ulist);
 int ulist_add(struct ulist *ulist, u64 val, unsigned long aux,
 	      unsigned long gfp_mask);
+int ulist_add_merge(struct ulist *ulist, u64 val, unsigned long aux,
+		    unsigned long *old_aux, unsigned long gfp_mask);
 struct ulist_node *ulist_next(struct ulist *ulist,
 			      struct ulist_iterator *uiter);