Commit b5bae261 authored by Josef Bacik's avatar Josef Bacik Committed by Chris Mason

Btrfs: fix race when getting the eb out of page->private

We can race when checking wether PagePrivate is set on a page and we
actually have an eb saved in the pages private pointer.  We could have
easily written out this page and released it in the time that we did the
pagevec lookup and actually got around to looking at this page.  So use
mapping->private_lock to ensure we get a consistent view of the
page->private pointer.  This is inline with the alloc and releasepage paths
which use private_lock when manipulating page->private.  Thanks,
Reported-by: default avatarDavid Sterba <dave@jikos.cz>
Signed-off-by: default avatarJosef Bacik <jbacik@fusionio.com>
parent ff44c6e3
...@@ -3256,19 +3256,34 @@ int btree_write_cache_pages(struct address_space *mapping, ...@@ -3256,19 +3256,34 @@ int btree_write_cache_pages(struct address_space *mapping,
break; break;
} }
spin_lock(&mapping->private_lock);
if (!PagePrivate(page)) {
spin_unlock(&mapping->private_lock);
continue;
}
eb = (struct extent_buffer *)page->private; eb = (struct extent_buffer *)page->private;
/*
* Shouldn't happen and normally this would be a BUG_ON
* but no sense in crashing the users box for something
* we can survive anyway.
*/
if (!eb) { if (!eb) {
spin_unlock(&mapping->private_lock);
WARN_ON(1); WARN_ON(1);
continue; continue;
} }
if (eb == prev_eb) if (eb == prev_eb) {
spin_unlock(&mapping->private_lock);
continue; continue;
}
if (!atomic_inc_not_zero(&eb->refs)) { ret = atomic_inc_not_zero(&eb->refs);
WARN_ON(1); spin_unlock(&mapping->private_lock);
if (!ret)
continue; continue;
}
prev_eb = eb; prev_eb = eb;
ret = lock_extent_buffer_for_io(eb, fs_info, &epd); ret = lock_extent_buffer_for_io(eb, fs_info, &epd);
......
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