mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-24 01:41:39 +00:00
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: David Sterba <dave@jikos.cz> Signed-off-by: Josef Bacik <jbacik@fusionio.com>
This commit is contained in:
parent
ff44c6e36d
commit
b5bae2612a
1 changed files with 20 additions and 5 deletions
|
@ -3256,20 +3256,35 @@ int btree_write_cache_pages(struct address_space *mapping,
|
|||
break;
|
||||
}
|
||||
|
||||
spin_lock(&mapping->private_lock);
|
||||
if (!PagePrivate(page)) {
|
||||
spin_unlock(&mapping->private_lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
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) {
|
||||
spin_unlock(&mapping->private_lock);
|
||||
WARN_ON(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (eb == prev_eb)
|
||||
continue;
|
||||
|
||||
if (!atomic_inc_not_zero(&eb->refs)) {
|
||||
WARN_ON(1);
|
||||
if (eb == prev_eb) {
|
||||
spin_unlock(&mapping->private_lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = atomic_inc_not_zero(&eb->refs);
|
||||
spin_unlock(&mapping->private_lock);
|
||||
if (!ret)
|
||||
continue;
|
||||
|
||||
prev_eb = eb;
|
||||
ret = lock_extent_buffer_for_io(eb, fs_info, &epd);
|
||||
if (!ret) {
|
||||
|
|
Loading…
Reference in a new issue