mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 02:39:48 +00:00
Btrfs: pin buffers during write_dev_supers
write_dev_supers is called in sequence. First is it called with wait == 0, which starts IO on all of the super blocks for a given device. Then it is called with wait == 1 to make sure they all reach the disk. It doesn't currently pin the buffers between the two calls, and it also assumes the buffers won't go away between the two calls, leading to an oops if the VM manages to free the buffers in the middle of the sync. This fixes that assumption and updates the code to return an error if things are not up to date when the wait == 1 run is done. Signed-off-by: Hisashi Hifumi <hifumi.hisashi@oss.ntt.co.jp> Signed-off-by: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
parent
e5e9a5206a
commit
4eedeb75e7
1 changed files with 29 additions and 14 deletions
|
@ -2020,6 +2020,17 @@ struct buffer_head *btrfs_read_dev_super(struct block_device *bdev)
|
|||
return latest;
|
||||
}
|
||||
|
||||
/*
|
||||
* this should be called twice, once with wait == 0 and
|
||||
* once with wait == 1. When wait == 0 is done, all the buffer heads
|
||||
* we write are pinned.
|
||||
*
|
||||
* They are released when wait == 1 is done.
|
||||
* max_mirrors must be the same for both runs, and it indicates how
|
||||
* many supers on this one device should be written.
|
||||
*
|
||||
* max_mirrors == 0 means to write them all.
|
||||
*/
|
||||
static int write_dev_supers(struct btrfs_device *device,
|
||||
struct btrfs_super_block *sb,
|
||||
int do_barriers, int wait, int max_mirrors)
|
||||
|
@ -2055,12 +2066,16 @@ static int write_dev_supers(struct btrfs_device *device,
|
|||
bh = __find_get_block(device->bdev, bytenr / 4096,
|
||||
BTRFS_SUPER_INFO_SIZE);
|
||||
BUG_ON(!bh);
|
||||
brelse(bh);
|
||||
wait_on_buffer(bh);
|
||||
if (buffer_uptodate(bh)) {
|
||||
brelse(bh);
|
||||
continue;
|
||||
}
|
||||
if (!buffer_uptodate(bh))
|
||||
errors++;
|
||||
|
||||
/* drop our reference */
|
||||
brelse(bh);
|
||||
|
||||
/* drop the reference from the wait == 0 run */
|
||||
brelse(bh);
|
||||
continue;
|
||||
} else {
|
||||
btrfs_set_super_bytenr(sb, bytenr);
|
||||
|
||||
|
@ -2071,12 +2086,18 @@ static int write_dev_supers(struct btrfs_device *device,
|
|||
BTRFS_CSUM_SIZE);
|
||||
btrfs_csum_final(crc, sb->csum);
|
||||
|
||||
/*
|
||||
* one reference for us, and we leave it for the
|
||||
* caller
|
||||
*/
|
||||
bh = __getblk(device->bdev, bytenr / 4096,
|
||||
BTRFS_SUPER_INFO_SIZE);
|
||||
memcpy(bh->b_data, sb, BTRFS_SUPER_INFO_SIZE);
|
||||
|
||||
set_buffer_uptodate(bh);
|
||||
/* one reference for submit_bh */
|
||||
get_bh(bh);
|
||||
|
||||
set_buffer_uptodate(bh);
|
||||
lock_buffer(bh);
|
||||
bh->b_end_io = btrfs_end_buffer_write_sync;
|
||||
}
|
||||
|
@ -2088,6 +2109,7 @@ static int write_dev_supers(struct btrfs_device *device,
|
|||
device->name);
|
||||
set_buffer_uptodate(bh);
|
||||
device->barriers = 0;
|
||||
/* one reference for submit_bh */
|
||||
get_bh(bh);
|
||||
lock_buffer(bh);
|
||||
ret = submit_bh(WRITE_SYNC, bh);
|
||||
|
@ -2096,15 +2118,8 @@ static int write_dev_supers(struct btrfs_device *device,
|
|||
ret = submit_bh(WRITE_SYNC, bh);
|
||||
}
|
||||
|
||||
if (!ret && wait) {
|
||||
wait_on_buffer(bh);
|
||||
if (!buffer_uptodate(bh))
|
||||
errors++;
|
||||
} else if (ret) {
|
||||
if (ret)
|
||||
errors++;
|
||||
}
|
||||
if (wait)
|
||||
brelse(bh);
|
||||
}
|
||||
return errors < i ? 0 : -1;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue