|
@@ -4772,6 +4772,25 @@ struct extent_buffer *find_extent_buffer(struct btrfs_fs_info *fs_info,
|
|
|
start >> PAGE_CACHE_SHIFT);
|
|
|
if (eb && atomic_inc_not_zero(&eb->refs)) {
|
|
|
rcu_read_unlock();
|
|
|
+ /*
|
|
|
+ * Lock our eb's refs_lock to avoid races with
|
|
|
+ * free_extent_buffer. When we get our eb it might be flagged
|
|
|
+ * with EXTENT_BUFFER_STALE and another task running
|
|
|
+ * free_extent_buffer might have seen that flag set,
|
|
|
+ * eb->refs == 2, that the buffer isn't under IO (dirty and
|
|
|
+ * writeback flags not set) and it's still in the tree (flag
|
|
|
+ * EXTENT_BUFFER_TREE_REF set), therefore being in the process
|
|
|
+ * of decrementing the extent buffer's reference count twice.
|
|
|
+ * So here we could race and increment the eb's reference count,
|
|
|
+ * clear its stale flag, mark it as dirty and drop our reference
|
|
|
+ * before the other task finishes executing free_extent_buffer,
|
|
|
+ * which would later result in an attempt to free an extent
|
|
|
+ * buffer that is dirty.
|
|
|
+ */
|
|
|
+ if (test_bit(EXTENT_BUFFER_STALE, &eb->bflags)) {
|
|
|
+ spin_lock(&eb->refs_lock);
|
|
|
+ spin_unlock(&eb->refs_lock);
|
|
|
+ }
|
|
|
mark_extent_buffer_accessed(eb, NULL);
|
|
|
return eb;
|
|
|
}
|