diff options
| -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 | } |
