Btrfs: superblock duplication
This patch implements superblock duplication. Superblocks are stored at offset 16K, 64M and 256G on every devices. Spaces used by superblocks are preserved by the allocator, which uses a reverse mapping function to find the logical addresses that correspond to superblocks. Thank you, Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
This commit is contained in:
@@ -1595,8 +1595,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
|
||||
fs_info, BTRFS_ROOT_TREE_OBJECTID);
|
||||
|
||||
|
||||
bh = __bread(fs_devices->latest_bdev,
|
||||
BTRFS_SUPER_INFO_OFFSET / 4096, 4096);
|
||||
bh = btrfs_read_dev_super(fs_devices->latest_bdev);
|
||||
if (!bh)
|
||||
goto fail_iput;
|
||||
|
||||
@@ -1710,7 +1709,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
|
||||
}
|
||||
|
||||
mutex_lock(&fs_info->chunk_mutex);
|
||||
ret = btrfs_read_sys_array(tree_root);
|
||||
ret = btrfs_read_sys_array(tree_root, btrfs_super_bytenr(disk_super));
|
||||
mutex_unlock(&fs_info->chunk_mutex);
|
||||
if (ret) {
|
||||
printk("btrfs: failed to read the system array on %s\n",
|
||||
@@ -1905,19 +1904,147 @@ static void btrfs_end_buffer_write_sync(struct buffer_head *bh, int uptodate)
|
||||
put_bh(bh);
|
||||
}
|
||||
|
||||
static int write_all_supers(struct btrfs_root *root)
|
||||
struct buffer_head *btrfs_read_dev_super(struct block_device *bdev)
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
struct buffer_head *latest = NULL;
|
||||
struct btrfs_super_block *super;
|
||||
int i;
|
||||
u64 transid = 0;
|
||||
u64 bytenr;
|
||||
|
||||
/* we would like to check all the supers, but that would make
|
||||
* a btrfs mount succeed after a mkfs from a different FS.
|
||||
* So, we need to add a special mount option to scan for
|
||||
* later supers, using BTRFS_SUPER_MIRROR_MAX instead
|
||||
*/
|
||||
for (i = 0; i < 1; i++) {
|
||||
bytenr = btrfs_sb_offset(i);
|
||||
if (bytenr + 4096 >= i_size_read(bdev->bd_inode))
|
||||
break;
|
||||
bh = __bread(bdev, bytenr / 4096, 4096);
|
||||
if (!bh)
|
||||
continue;
|
||||
|
||||
super = (struct btrfs_super_block *)bh->b_data;
|
||||
if (btrfs_super_bytenr(super) != bytenr ||
|
||||
strncmp((char *)(&super->magic), BTRFS_MAGIC,
|
||||
sizeof(super->magic))) {
|
||||
brelse(bh);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!latest || btrfs_super_generation(super) > transid) {
|
||||
brelse(latest);
|
||||
latest = bh;
|
||||
transid = btrfs_super_generation(super);
|
||||
} else {
|
||||
brelse(bh);
|
||||
}
|
||||
}
|
||||
return latest;
|
||||
}
|
||||
|
||||
static int write_dev_supers(struct btrfs_device *device,
|
||||
struct btrfs_super_block *sb,
|
||||
int do_barriers, int wait, int max_mirrors)
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
int i;
|
||||
int ret;
|
||||
int errors = 0;
|
||||
u32 crc;
|
||||
u64 bytenr;
|
||||
int last_barrier = 0;
|
||||
|
||||
if (max_mirrors == 0)
|
||||
max_mirrors = BTRFS_SUPER_MIRROR_MAX;
|
||||
|
||||
/* make sure only the last submit_bh does a barrier */
|
||||
if (do_barriers) {
|
||||
for (i = 0; i < max_mirrors; i++) {
|
||||
bytenr = btrfs_sb_offset(i);
|
||||
if (bytenr + BTRFS_SUPER_INFO_SIZE >=
|
||||
device->total_bytes)
|
||||
break;
|
||||
last_barrier = i;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < max_mirrors; i++) {
|
||||
bytenr = btrfs_sb_offset(i);
|
||||
if (bytenr + BTRFS_SUPER_INFO_SIZE >= device->total_bytes)
|
||||
break;
|
||||
|
||||
if (wait) {
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
btrfs_set_super_bytenr(sb, bytenr);
|
||||
|
||||
crc = ~(u32)0;
|
||||
crc = btrfs_csum_data(NULL, (char *)sb +
|
||||
BTRFS_CSUM_SIZE, crc,
|
||||
BTRFS_SUPER_INFO_SIZE -
|
||||
BTRFS_CSUM_SIZE);
|
||||
btrfs_csum_final(crc, sb->csum);
|
||||
|
||||
bh = __getblk(device->bdev, bytenr / 4096,
|
||||
BTRFS_SUPER_INFO_SIZE);
|
||||
memcpy(bh->b_data, sb, BTRFS_SUPER_INFO_SIZE);
|
||||
|
||||
set_buffer_uptodate(bh);
|
||||
get_bh(bh);
|
||||
lock_buffer(bh);
|
||||
bh->b_end_io = btrfs_end_buffer_write_sync;
|
||||
}
|
||||
|
||||
if (i == last_barrier && do_barriers && device->barriers) {
|
||||
ret = submit_bh(WRITE_BARRIER, bh);
|
||||
if (ret == -EOPNOTSUPP) {
|
||||
printk("btrfs: disabling barriers on dev %s\n",
|
||||
device->name);
|
||||
set_buffer_uptodate(bh);
|
||||
device->barriers = 0;
|
||||
get_bh(bh);
|
||||
lock_buffer(bh);
|
||||
ret = submit_bh(WRITE, bh);
|
||||
}
|
||||
} else {
|
||||
ret = submit_bh(WRITE, bh);
|
||||
}
|
||||
|
||||
if (!ret && wait) {
|
||||
wait_on_buffer(bh);
|
||||
if (!buffer_uptodate(bh))
|
||||
errors++;
|
||||
} else if (ret) {
|
||||
errors++;
|
||||
}
|
||||
if (wait)
|
||||
brelse(bh);
|
||||
}
|
||||
return errors < i ? 0 : -1;
|
||||
}
|
||||
|
||||
int write_all_supers(struct btrfs_root *root, int max_mirrors)
|
||||
{
|
||||
struct list_head *cur;
|
||||
struct list_head *head = &root->fs_info->fs_devices->devices;
|
||||
struct btrfs_device *dev;
|
||||
struct btrfs_super_block *sb;
|
||||
struct btrfs_dev_item *dev_item;
|
||||
struct buffer_head *bh;
|
||||
int ret;
|
||||
int do_barriers;
|
||||
int max_errors;
|
||||
int total_errors = 0;
|
||||
u32 crc;
|
||||
u64 flags;
|
||||
|
||||
max_errors = btrfs_super_num_devices(&root->fs_info->super_copy) - 1;
|
||||
@@ -1944,40 +2071,11 @@ static int write_all_supers(struct btrfs_root *root)
|
||||
btrfs_set_stack_device_sector_size(dev_item, dev->sector_size);
|
||||
memcpy(dev_item->uuid, dev->uuid, BTRFS_UUID_SIZE);
|
||||
memcpy(dev_item->fsid, dev->fs_devices->fsid, BTRFS_UUID_SIZE);
|
||||
|
||||
flags = btrfs_super_flags(sb);
|
||||
btrfs_set_super_flags(sb, flags | BTRFS_HEADER_FLAG_WRITTEN);
|
||||
|
||||
|
||||
crc = ~(u32)0;
|
||||
crc = btrfs_csum_data(root, (char *)sb + BTRFS_CSUM_SIZE, crc,
|
||||
BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
|
||||
btrfs_csum_final(crc, sb->csum);
|
||||
|
||||
bh = __getblk(dev->bdev, BTRFS_SUPER_INFO_OFFSET / 4096,
|
||||
BTRFS_SUPER_INFO_SIZE);
|
||||
|
||||
memcpy(bh->b_data, sb, BTRFS_SUPER_INFO_SIZE);
|
||||
dev->pending_io = bh;
|
||||
|
||||
get_bh(bh);
|
||||
set_buffer_uptodate(bh);
|
||||
lock_buffer(bh);
|
||||
bh->b_end_io = btrfs_end_buffer_write_sync;
|
||||
|
||||
if (do_barriers && dev->barriers) {
|
||||
ret = submit_bh(WRITE_BARRIER, bh);
|
||||
if (ret == -EOPNOTSUPP) {
|
||||
printk("btrfs: disabling barriers on dev %s\n",
|
||||
dev->name);
|
||||
set_buffer_uptodate(bh);
|
||||
dev->barriers = 0;
|
||||
get_bh(bh);
|
||||
lock_buffer(bh);
|
||||
ret = submit_bh(WRITE, bh);
|
||||
}
|
||||
} else {
|
||||
ret = submit_bh(WRITE, bh);
|
||||
}
|
||||
ret = write_dev_supers(dev, sb, do_barriers, 0, max_mirrors);
|
||||
if (ret)
|
||||
total_errors++;
|
||||
}
|
||||
@@ -1985,8 +2083,8 @@ static int write_all_supers(struct btrfs_root *root)
|
||||
printk("btrfs: %d errors while writing supers\n", total_errors);
|
||||
BUG();
|
||||
}
|
||||
total_errors = 0;
|
||||
|
||||
total_errors = 0;
|
||||
list_for_each(cur, head) {
|
||||
dev = list_entry(cur, struct btrfs_device, dev_list);
|
||||
if (!dev->bdev)
|
||||
@@ -1994,29 +2092,9 @@ static int write_all_supers(struct btrfs_root *root)
|
||||
if (!dev->in_fs_metadata || !dev->writeable)
|
||||
continue;
|
||||
|
||||
BUG_ON(!dev->pending_io);
|
||||
bh = dev->pending_io;
|
||||
wait_on_buffer(bh);
|
||||
if (!buffer_uptodate(dev->pending_io)) {
|
||||
if (do_barriers && dev->barriers) {
|
||||
printk("btrfs: disabling barriers on dev %s\n",
|
||||
dev->name);
|
||||
set_buffer_uptodate(bh);
|
||||
get_bh(bh);
|
||||
lock_buffer(bh);
|
||||
dev->barriers = 0;
|
||||
ret = submit_bh(WRITE, bh);
|
||||
BUG_ON(ret);
|
||||
wait_on_buffer(bh);
|
||||
if (!buffer_uptodate(bh))
|
||||
total_errors++;
|
||||
} else {
|
||||
total_errors++;
|
||||
}
|
||||
|
||||
}
|
||||
dev->pending_io = NULL;
|
||||
brelse(bh);
|
||||
ret = write_dev_supers(dev, sb, do_barriers, 1, max_mirrors);
|
||||
if (ret)
|
||||
total_errors++;
|
||||
}
|
||||
if (total_errors > max_errors) {
|
||||
printk("btrfs: %d errors while writing supers\n", total_errors);
|
||||
@@ -2025,12 +2103,12 @@ static int write_all_supers(struct btrfs_root *root)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_ctree_super(struct btrfs_trans_handle *trans, struct btrfs_root
|
||||
*root)
|
||||
int write_ctree_super(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, int max_mirrors)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = write_all_supers(root);
|
||||
ret = write_all_supers(root, max_mirrors);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2116,7 +2194,7 @@ int btrfs_commit_super(struct btrfs_root *root)
|
||||
ret = btrfs_write_and_wait_transaction(NULL, root);
|
||||
BUG_ON(ret);
|
||||
|
||||
ret = write_ctree_super(NULL, root);
|
||||
ret = write_ctree_super(NULL, root, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user