]> Pileus Git - ~andy/linux/blobdiff - fs/btrfs/backref.c
Linux 3.14
[~andy/linux] / fs / btrfs / backref.c
index 826b98c211aead10735bfd2814b2317bf7e532aa..aded3ef3d3d4abfa6d61fa078a51e64e4b80be65 100644 (file)
@@ -66,6 +66,16 @@ static int check_extent_in_eb(struct btrfs_key *key, struct extent_buffer *eb,
        return 0;
 }
 
+static void free_inode_elem_list(struct extent_inode_elem *eie)
+{
+       struct extent_inode_elem *eie_next;
+
+       for (; eie; eie = eie_next) {
+               eie_next = eie->next;
+               kfree(eie);
+       }
+}
+
 static int find_extent_in_eb(struct extent_buffer *eb, u64 wanted_disk_byte,
                                u64 extent_item_pos,
                                struct extent_inode_elem **eie)
@@ -209,18 +219,19 @@ static int __add_prelim_ref(struct list_head *head, u64 root_id,
 }
 
 static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,
-                               struct ulist *parents, int level,
-                               struct btrfs_key *key_for_search, u64 time_seq,
-                               u64 wanted_disk_byte,
-                               const u64 *extent_item_pos)
+                          struct ulist *parents, struct __prelim_ref *ref,
+                          int level, u64 time_seq, const u64 *extent_item_pos)
 {
        int ret = 0;
        int slot;
        struct extent_buffer *eb;
        struct btrfs_key key;
+       struct btrfs_key *key_for_search = &ref->key_for_search;
        struct btrfs_file_extent_item *fi;
        struct extent_inode_elem *eie = NULL, *old = NULL;
        u64 disk_byte;
+       u64 wanted_disk_byte = ref->wanted_disk_byte;
+       u64 count = 0;
 
        if (level != 0) {
                eb = path->nodes[level];
@@ -238,7 +249,7 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,
        if (path->slots[0] >= btrfs_header_nritems(path->nodes[0]))
                ret = btrfs_next_old_leaf(root, path, time_seq);
 
-       while (!ret) {
+       while (!ret && count < ref->count) {
                eb = path->nodes[0];
                slot = path->slots[0];
 
@@ -254,6 +265,7 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,
                if (disk_byte == wanted_disk_byte) {
                        eie = NULL;
                        old = NULL;
+                       count++;
                        if (extent_item_pos) {
                                ret = check_extent_in_eb(&key, eb, fi,
                                                *extent_item_pos,
@@ -273,6 +285,7 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,
                                        old = old->next;
                                old->next = eie;
                        }
+                       eie = NULL;
                }
 next:
                ret = btrfs_next_old_item(root, path, time_seq);
@@ -280,6 +293,8 @@ next:
 
        if (ret > 0)
                ret = 0;
+       else if (ret < 0)
+               free_inode_elem_list(eie);
        return ret;
 }
 
@@ -299,23 +314,34 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
        int ret = 0;
        int root_level;
        int level = ref->level;
+       int index;
 
        root_key.objectid = ref->root_id;
        root_key.type = BTRFS_ROOT_ITEM_KEY;
        root_key.offset = (u64)-1;
+
+       index = srcu_read_lock(&fs_info->subvol_srcu);
+
        root = btrfs_read_fs_root_no_name(fs_info, &root_key);
        if (IS_ERR(root)) {
+               srcu_read_unlock(&fs_info->subvol_srcu, index);
                ret = PTR_ERR(root);
                goto out;
        }
 
        root_level = btrfs_old_root_level(root, time_seq);
 
-       if (root_level + 1 == level)
+       if (root_level + 1 == level) {
+               srcu_read_unlock(&fs_info->subvol_srcu, index);
                goto out;
+       }
 
        path->lowest_level = level;
        ret = btrfs_search_old_slot(root, &ref->key_for_search, path, time_seq);
+
+       /* root node has been locked, we can release @subvol_srcu safely here */
+       srcu_read_unlock(&fs_info->subvol_srcu, index);
+
        pr_debug("search slot in root %llu (level %d, ref count %d) returned "
                 "%d for key (%llu %u %llu)\n",
                 ref->root_id, level, ref->count, ret,
@@ -334,9 +360,8 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
                eb = path->nodes[level];
        }
 
-       ret = add_all_parents(root, path, parents, level, &ref->key_for_search,
-                               time_seq, ref->wanted_disk_byte,
-                               extent_item_pos);
+       ret = add_all_parents(root, path, parents, ref, level, time_seq,
+                             extent_item_pos);
 out:
        path->lowest_level = 0;
        btrfs_release_path(path);
@@ -376,10 +401,16 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
                        continue;
                err = __resolve_indirect_ref(fs_info, path, time_seq, ref,
                                             parents, extent_item_pos);
-               if (err == -ENOMEM)
-                       goto out;
-               if (err)
+               /*
+                * we can only tolerate ENOENT,otherwise,we should catch error
+                * and return directly.
+                */
+               if (err == -ENOENT) {
                        continue;
+               } else if (err) {
+                       ret = err;
+                       goto out;
+               }
 
                /* we put the first parent into the ref at hand */
                ULIST_ITER_INIT(&uiter);
@@ -538,14 +569,13 @@ static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq,
        if (extent_op && extent_op->update_key)
                btrfs_disk_key_to_cpu(&op_key, &extent_op->key);
 
-       while ((n = rb_prev(n))) {
+       spin_lock(&head->lock);
+       n = rb_first(&head->ref_root);
+       while (n) {
                struct btrfs_delayed_ref_node *node;
                node = rb_entry(n, struct btrfs_delayed_ref_node,
                                rb_node);
-               if (node->bytenr != head->node.bytenr)
-                       break;
-               WARN_ON(node->is_head);
-
+               n = rb_next(n);
                if (node->seq > seq)
                        continue;
 
@@ -612,10 +642,10 @@ static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq,
                        WARN_ON(1);
                }
                if (ret)
-                       return ret;
+                       break;
        }
-
-       return 0;
+       spin_unlock(&head->lock);
+       return ret;
 }
 
 /*
@@ -828,6 +858,7 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
        struct list_head prefs_delayed;
        struct list_head prefs;
        struct __prelim_ref *ref;
+       struct extent_inode_elem *eie = NULL;
 
        INIT_LIST_HEAD(&prefs);
        INIT_LIST_HEAD(&prefs_delayed);
@@ -882,15 +913,15 @@ again:
                                btrfs_put_delayed_ref(&head->node);
                                goto again;
                        }
+                       spin_unlock(&delayed_refs->lock);
                        ret = __add_delayed_refs(head, time_seq,
                                                 &prefs_delayed);
                        mutex_unlock(&head->mutex);
-                       if (ret) {
-                               spin_unlock(&delayed_refs->lock);
+                       if (ret)
                                goto out;
-                       }
+               } else {
+                       spin_unlock(&delayed_refs->lock);
                }
-               spin_unlock(&delayed_refs->lock);
        }
 
        if (path->slots[0]) {
@@ -941,7 +972,6 @@ again:
                                goto out;
                }
                if (ref->count && ref->parent) {
-                       struct extent_inode_elem *eie = NULL;
                        if (extent_item_pos && !ref->inode_list) {
                                u32 bsz;
                                struct extent_buffer *eb;
@@ -976,6 +1006,7 @@ again:
                                        eie = eie->next;
                                eie->next = ref->inode_list;
                        }
+                       eie = NULL;
                }
                list_del(&ref->list);
                kmem_cache_free(btrfs_prelim_ref_cache, ref);
@@ -994,7 +1025,8 @@ out:
                list_del(&ref->list);
                kmem_cache_free(btrfs_prelim_ref_cache, ref);
        }
-
+       if (ret < 0)
+               free_inode_elem_list(eie);
        return ret;
 }
 
@@ -1002,7 +1034,6 @@ static void free_leaf_list(struct ulist *blocks)
 {
        struct ulist_node *node = NULL;
        struct extent_inode_elem *eie;
-       struct extent_inode_elem *eie_next;
        struct ulist_iterator uiter;
 
        ULIST_ITER_INIT(&uiter);
@@ -1010,10 +1041,7 @@ static void free_leaf_list(struct ulist *blocks)
                if (!node->aux)
                        continue;
                eie = (struct extent_inode_elem *)(uintptr_t)node->aux;
-               for (; eie; eie = eie_next) {
-                       eie_next = eie->next;
-                       kfree(eie);
-               }
+               free_inode_elem_list(eie);
                node->aux = 0;
        }
 
@@ -1101,44 +1129,13 @@ int btrfs_find_all_roots(struct btrfs_trans_handle *trans,
                if (!node)
                        break;
                bytenr = node->val;
+               cond_resched();
        }
 
        ulist_free(tmp);
        return 0;
 }
 
-
-static int __inode_info(u64 inum, u64 ioff, u8 key_type,
-                       struct btrfs_root *fs_root, struct btrfs_path *path,
-                       struct btrfs_key *found_key)
-{
-       int ret;
-       struct btrfs_key key;
-       struct extent_buffer *eb;
-
-       key.type = key_type;
-       key.objectid = inum;
-       key.offset = ioff;
-
-       ret = btrfs_search_slot(NULL, fs_root, &key, path, 0, 0);
-       if (ret < 0)
-               return ret;
-
-       eb = path->nodes[0];
-       if (ret && path->slots[0] >= btrfs_header_nritems(eb)) {
-               ret = btrfs_next_leaf(fs_root, path);
-               if (ret)
-                       return ret;
-               eb = path->nodes[0];
-       }
-
-       btrfs_item_key_to_cpu(eb, found_key, path->slots[0]);
-       if (found_key->type != key.type || found_key->objectid != key.objectid)
-               return 1;
-
-       return 0;
-}
-
 /*
  * this makes the path point to (inum INODE_ITEM ioff)
  */
@@ -1146,16 +1143,16 @@ int inode_item_info(u64 inum, u64 ioff, struct btrfs_root *fs_root,
                        struct btrfs_path *path)
 {
        struct btrfs_key key;
-       return __inode_info(inum, ioff, BTRFS_INODE_ITEM_KEY, fs_root, path,
-                               &key);
+       return btrfs_find_item(fs_root, path, inum, ioff,
+                       BTRFS_INODE_ITEM_KEY, &key);
 }
 
 static int inode_ref_info(u64 inum, u64 ioff, struct btrfs_root *fs_root,
                                struct btrfs_path *path,
                                struct btrfs_key *found_key)
 {
-       return __inode_info(inum, ioff, BTRFS_INODE_REF_KEY, fs_root, path,
-                               found_key);
+       return btrfs_find_item(fs_root, path, inum, ioff,
+                       BTRFS_INODE_REF_KEY, found_key);
 }
 
 int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid,
@@ -1335,20 +1332,45 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
        ret = btrfs_search_slot(NULL, fs_info->extent_root, &key, path, 0, 0);
        if (ret < 0)
                return ret;
-       ret = btrfs_previous_item(fs_info->extent_root, path,
-                                       0, BTRFS_EXTENT_ITEM_KEY);
-       if (ret < 0)
-               return ret;
 
-       btrfs_item_key_to_cpu(path->nodes[0], found_key, path->slots[0]);
+       while (1) {
+               u32 nritems;
+               if (path->slots[0] == 0) {
+                       btrfs_set_path_blocking(path);
+                       ret = btrfs_prev_leaf(fs_info->extent_root, path);
+                       if (ret != 0) {
+                               if (ret > 0) {
+                                       pr_debug("logical %llu is not within "
+                                                "any extent\n", logical);
+                                       ret = -ENOENT;
+                               }
+                               return ret;
+                       }
+               } else {
+                       path->slots[0]--;
+               }
+               nritems = btrfs_header_nritems(path->nodes[0]);
+               if (nritems == 0) {
+                       pr_debug("logical %llu is not within any extent\n",
+                                logical);
+                       return -ENOENT;
+               }
+               if (path->slots[0] == nritems)
+                       path->slots[0]--;
+
+               btrfs_item_key_to_cpu(path->nodes[0], found_key,
+                                     path->slots[0]);
+               if (found_key->type == BTRFS_EXTENT_ITEM_KEY ||
+                   found_key->type == BTRFS_METADATA_ITEM_KEY)
+                       break;
+       }
+
        if (found_key->type == BTRFS_METADATA_ITEM_KEY)
                size = fs_info->extent_root->leafsize;
        else if (found_key->type == BTRFS_EXTENT_ITEM_KEY)
                size = found_key->offset;
 
-       if ((found_key->type != BTRFS_EXTENT_ITEM_KEY &&
-            found_key->type != BTRFS_METADATA_ITEM_KEY) ||
-           found_key->objectid > logical ||
+       if (found_key->objectid > logical ||
            found_key->objectid + size <= logical) {
                pr_debug("logical %llu is not within any extent\n", logical);
                return -ENOENT;
@@ -1601,7 +1623,6 @@ static int iterate_inode_refs(u64 inum, struct btrfs_root *fs_root,
        struct btrfs_key found_key;
 
        while (!ret) {
-               path->leave_spinning = 1;
                ret = inode_ref_info(inum, parent ? parent+1 : 0, fs_root, path,
                                     &found_key);
                if (ret < 0)
@@ -1614,9 +1635,12 @@ static int iterate_inode_refs(u64 inum, struct btrfs_root *fs_root,
 
                parent = found_key.offset;
                slot = path->slots[0];
-               eb = path->nodes[0];
-               /* make sure we can use eb after releasing the path */
-               atomic_inc(&eb->refs);
+               eb = btrfs_clone_extent_buffer(path->nodes[0]);
+               if (!eb) {
+                       ret = -ENOMEM;
+                       break;
+               }
+               extent_buffer_get(eb);
                btrfs_tree_read_lock(eb);
                btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK);
                btrfs_release_path(path);
@@ -1674,9 +1698,12 @@ static int iterate_inode_extrefs(u64 inum, struct btrfs_root *fs_root,
                ++found;
 
                slot = path->slots[0];
-               eb = path->nodes[0];
-               /* make sure we can use eb after releasing the path */
-               atomic_inc(&eb->refs);
+               eb = btrfs_clone_extent_buffer(path->nodes[0]);
+               if (!eb) {
+                       ret = -ENOMEM;
+                       break;
+               }
+               extent_buffer_get(eb);
 
                btrfs_tree_read_lock(eb);
                btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK);