Skip to content

Commit

Permalink
Btrfs: superblock duplication
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Yan Zheng authored and Chris Mason committed Dec 8, 2008
1 parent d20f704 commit a512bbf
Show file tree
Hide file tree
Showing 8 changed files with 279 additions and 119 deletions.
208 changes: 143 additions & 65 deletions fs/btrfs/disk-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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;
Expand All @@ -1944,79 +2071,30 @@ 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++;
}
if (total_errors > max_errors) {
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)
continue;
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);
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
}

Expand Down
17 changes: 15 additions & 2 deletions fs/btrfs/disk-io.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,20 @@
#ifndef __DISKIO__
#define __DISKIO__

#define BTRFS_SUPER_INFO_OFFSET (16 * 1024)
#define BTRFS_SUPER_INFO_OFFSET (64 * 1024)
#define BTRFS_SUPER_INFO_SIZE 4096

#define BTRFS_SUPER_MIRROR_MAX 3
#define BTRFS_SUPER_MIRROR_SHIFT 12

static inline u64 btrfs_sb_offset(int mirror)
{
u64 start = 16 * 1024;
if (mirror)
return start << (BTRFS_SUPER_MIRROR_SHIFT * mirror);
return BTRFS_SUPER_INFO_OFFSET;
}

struct btrfs_device;
struct btrfs_fs_devices;

Expand All @@ -37,7 +49,8 @@ struct btrfs_root *open_ctree(struct super_block *sb,
char *options);
int close_ctree(struct btrfs_root *root);
int write_ctree_super(struct btrfs_trans_handle *trans,
struct btrfs_root *root);
struct btrfs_root *root, int max_mirrors);
struct buffer_head *btrfs_read_dev_super(struct block_device *bdev);
int btrfs_commit_super(struct btrfs_root *root);
struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root,
u64 bytenr, u32 blocksize);
Expand Down
Loading

0 comments on commit a512bbf

Please sign in to comment.