Commit 639eefc8 authored by Filipe David Borba Manana's avatar Filipe David Borba Manana Committed by Chris Mason

Btrfs: don't miss skinny extent items on delayed ref head contention

Currently extent-tree.c:btrfs_lookup_extent_info() can miss the lookup
of skinny extent items. This can happen when the execution flow is the
following:

* We do an extent tree lookup and fail to find a skinny extent item;

* As a result, we attempt to see if a non-skinny extent item exists,
  either by looking at previous item in the leaf or by doing another
  full extent tree search;

* We have a transaction and then we check for a matching delayed ref
  head in the transaction's delayed refs rbtree;

* We find such delayed ref head and then we try to lock it with a
  call to mutex_trylock();

* The lock was contended so we jump to the label "again", which repeats
  the extent tree search but for a non-skinny extent item, because we set
  previously metadata variable to 0 and the search key to look for a
  non-skinny extent-item;

* After the jump (and after releasing the transaction's delayed refs
  lock), a skinny extent item might have been added to the extent tree
  but we will miss it because metadata is set to 0 and the search key
  is set for a non-skinny extent-item.

The fix here is to not reset metadata to 0 and to jump to the initial search
key setup if the delayed ref head is contended, instead of jumping directly
to the extent tree search label ("again").

This issue was found while investigating the issue reported at Bugzilla 64961.

David Sterba suspected this function was missing extent items, and that
this could be caused by the last change to this function, which was made
in the following patch:

    [PATCH] Btrfs: optimize btrfs_lookup_extent_info()
    (commit 74be9510)

But in fact this issue already existed before, because after failing to find
a skinny extent item, the code set the search key for a non-skinny extent
item, and on contention of a matching delayed ref head it would not search
the extent tree for a skinny extent item anymore.
Signed-off-by: default avatarFilipe David Borba Manana <fdmanana@gmail.com>
Reviewed-by: default avatarLiu Bo <bo.li.liu@oracle.com>
Signed-off-by: default avatarChris Mason <clm@fb.com>
parent e43f998e
...@@ -767,20 +767,19 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans, ...@@ -767,20 +767,19 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
if (!path) if (!path)
return -ENOMEM; return -ENOMEM;
if (metadata) {
key.objectid = bytenr;
key.type = BTRFS_METADATA_ITEM_KEY;
key.offset = offset;
} else {
key.objectid = bytenr;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = offset;
}
if (!trans) { if (!trans) {
path->skip_locking = 1; path->skip_locking = 1;
path->search_commit_root = 1; path->search_commit_root = 1;
} }
search_again:
key.objectid = bytenr;
key.offset = offset;
if (metadata)
key.type = BTRFS_METADATA_ITEM_KEY;
else
key.type = BTRFS_EXTENT_ITEM_KEY;
again: again:
ret = btrfs_search_slot(trans, root->fs_info->extent_root, ret = btrfs_search_slot(trans, root->fs_info->extent_root,
&key, path, 0, 0); &key, path, 0, 0);
...@@ -788,7 +787,6 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans, ...@@ -788,7 +787,6 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
goto out_free; goto out_free;
if (ret > 0 && metadata && key.type == BTRFS_METADATA_ITEM_KEY) { if (ret > 0 && metadata && key.type == BTRFS_METADATA_ITEM_KEY) {
metadata = 0;
if (path->slots[0]) { if (path->slots[0]) {
path->slots[0]--; path->slots[0]--;
btrfs_item_key_to_cpu(path->nodes[0], &key, btrfs_item_key_to_cpu(path->nodes[0], &key,
...@@ -855,7 +853,7 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans, ...@@ -855,7 +853,7 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
mutex_lock(&head->mutex); mutex_lock(&head->mutex);
mutex_unlock(&head->mutex); mutex_unlock(&head->mutex);
btrfs_put_delayed_ref(&head->node); btrfs_put_delayed_ref(&head->node);
goto again; goto search_again;
} }
if (head->extent_op && head->extent_op->update_flags) if (head->extent_op && head->extent_op->update_flags)
extent_flags |= head->extent_op->flags_to_set; extent_flags |= head->extent_op->flags_to_set;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment