diff options
Diffstat (limited to 'fs/btrfs/extent_io.c')
-rw-r--r-- | fs/btrfs/extent_io.c | 19 |
1 files changed, 19 insertions, 0 deletions
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 43af5a61ad25..c32d226bfecc 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c | |||
@@ -4772,6 +4772,25 @@ struct extent_buffer *find_extent_buffer(struct btrfs_fs_info *fs_info, | |||
4772 | start >> PAGE_CACHE_SHIFT); | 4772 | start >> PAGE_CACHE_SHIFT); |
4773 | if (eb && atomic_inc_not_zero(&eb->refs)) { | 4773 | if (eb && atomic_inc_not_zero(&eb->refs)) { |
4774 | rcu_read_unlock(); | 4774 | rcu_read_unlock(); |
4775 | /* | ||
4776 | * Lock our eb's refs_lock to avoid races with | ||
4777 | * free_extent_buffer. When we get our eb it might be flagged | ||
4778 | * with EXTENT_BUFFER_STALE and another task running | ||
4779 | * free_extent_buffer might have seen that flag set, | ||
4780 | * eb->refs == 2, that the buffer isn't under IO (dirty and | ||
4781 | * writeback flags not set) and it's still in the tree (flag | ||
4782 | * EXTENT_BUFFER_TREE_REF set), therefore being in the process | ||
4783 | * of decrementing the extent buffer's reference count twice. | ||
4784 | * So here we could race and increment the eb's reference count, | ||
4785 | * clear its stale flag, mark it as dirty and drop our reference | ||
4786 | * before the other task finishes executing free_extent_buffer, | ||
4787 | * which would later result in an attempt to free an extent | ||
4788 | * buffer that is dirty. | ||
4789 | */ | ||
4790 | if (test_bit(EXTENT_BUFFER_STALE, &eb->bflags)) { | ||
4791 | spin_lock(&eb->refs_lock); | ||
4792 | spin_unlock(&eb->refs_lock); | ||
4793 | } | ||
4775 | mark_extent_buffer_accessed(eb, NULL); | 4794 | mark_extent_buffer_accessed(eb, NULL); |
4776 | return eb; | 4795 | return eb; |
4777 | } | 4796 | } |